diff options
Diffstat (limited to 'source/blender/nodes')
191 files changed, 8080 insertions, 6237 deletions
diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 386e5fe14c9..e9c0f2be368 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -29,7 +29,6 @@ set(INC ../makesrna ../render ../windowmanager - ../../../intern/glew-mx ../../../intern/guardedalloc # dna_type_offsets.h @@ -41,7 +40,8 @@ set(INC set(SRC intern/derived_node_tree.cc - intern/geometry_nodes_eval_log.cc + intern/geometry_nodes_lazy_function.cc + intern/geometry_nodes_log.cc intern/math_functions.cc intern/node_common.cc intern/node_declaration.cc @@ -50,7 +50,6 @@ set(SRC intern/node_multi_function.cc intern/node_socket.cc intern/node_socket_declarations.cc - intern/node_tree_ref.cc intern/node_util.c intern/socket_search_link.cc @@ -60,11 +59,10 @@ set(SRC NOD_function.h NOD_geometry.h NOD_geometry_exec.hh - NOD_geometry_nodes_eval_log.hh + NOD_geometry_nodes_lazy_function.hh NOD_math_functions.hh NOD_multi_function.hh NOD_node_declaration.hh - NOD_node_tree_ref.hh NOD_shader.h NOD_socket.h NOD_socket_declarations.hh @@ -104,20 +102,6 @@ if(WITH_BULLET) add_definitions(-DWITH_BULLET) endif() -if(WITH_PYTHON) - list(APPEND INC - ../python - ) - list(APPEND INC_SYS - ${PYTHON_INCLUDE_DIRS} - ) - list(APPEND LIB - ${PYTHON_LINKFLAGS} - ${PYTHON_LIBRARIES} - ) - add_definitions(-DWITH_PYTHON) -endif() - if(WITH_TBB) list(APPEND INC_SYS ${TBB_INCLUDE_DIRS} diff --git a/source/blender/nodes/NOD_derived_node_tree.hh b/source/blender/nodes/NOD_derived_node_tree.hh index b0799d90dcd..b3775e729da 100644 --- a/source/blender/nodes/NOD_derived_node_tree.hh +++ b/source/blender/nodes/NOD_derived_node_tree.hh @@ -5,16 +5,17 @@ /** \file * \ingroup nodes * - * DerivedNodeTree builds on top of NodeTreeRef and makes working with (nested) node groups more - * convenient and safe. It does so by pairing nodes and sockets with a context. The context - * contains information about the current "instance" of the node or socket. A node might be - * "instanced" multiple times when it is in a node group that is used multiple times. + * DerivedNodeTree makes working with (nested) node groups more convenient and safe. It does so by + * pairing nodes and sockets with a context. The context contains information about the current + * "instance" of the node or socket. A node might be "instanced" multiple times when it is in a + * node group that is used multiple times. */ #include "BLI_function_ref.hh" +#include "BLI_linear_allocator.hh" #include "BLI_vector_set.hh" -#include "NOD_node_tree_ref.hh" +#include "BKE_node_runtime.hh" namespace blender::nodes { @@ -40,20 +41,20 @@ class DTreeContext { DTreeContext *parent_context_; /* Null when this context is for the root node group. Otherwise it points to the group node in * the parent node group that contains this context. */ - const NodeRef *parent_node_; + const bNode *parent_node_; /* The current node tree. */ - const NodeTreeRef *tree_; + const bNodeTree *btree_; /* All the children contexts of this context. */ - Map<const NodeRef *, DTreeContext *> children_; + Map<const bNode *, DTreeContext *> children_; DerivedNodeTree *derived_tree_; friend DerivedNodeTree; public: - const NodeTreeRef &tree() const; + const bNodeTree &btree() const; const DTreeContext *parent_context() const; - const NodeRef *parent_node() const; - const DTreeContext *child_context(const NodeRef &node) const; + const bNode *parent_node() const; + const DTreeContext *child_context(const bNode &node) const; const DerivedNodeTree &derived_tree() const; bool is_root() const; }; @@ -65,15 +66,16 @@ class DTreeContext { class DNode { private: const DTreeContext *context_ = nullptr; - const NodeRef *node_ref_ = nullptr; + const bNode *bnode_ = nullptr; public: DNode() = default; - DNode(const DTreeContext *context, const NodeRef *node); + DNode(const DTreeContext *context, const bNode *node); const DTreeContext *context() const; - const NodeRef *node_ref() const; - const NodeRef *operator->() const; + const bNode *bnode() const; + const bNode *operator->() const; + const bNode &operator*() const; friend bool operator==(const DNode &a, const DNode &b); friend bool operator!=(const DNode &a, const DNode &b); @@ -98,17 +100,18 @@ class DNode { class DSocket { protected: const DTreeContext *context_ = nullptr; - const SocketRef *socket_ref_ = nullptr; + const bNodeSocket *bsocket_ = nullptr; public: DSocket() = default; - DSocket(const DTreeContext *context, const SocketRef *socket); + DSocket(const DTreeContext *context, const bNodeSocket *socket); DSocket(const DInputSocket &input_socket); DSocket(const DOutputSocket &output_socket); const DTreeContext *context() const; - const SocketRef *socket_ref() const; - const SocketRef *operator->() const; + const bNodeSocket *bsocket() const; + const bNodeSocket *operator->() const; + const bNodeSocket &operator*() const; friend bool operator==(const DSocket &a, const DSocket &b); friend bool operator!=(const DSocket &a, const DSocket &b); @@ -123,12 +126,9 @@ class DSocket { class DInputSocket : public DSocket { public: DInputSocket() = default; - DInputSocket(const DTreeContext *context, const InputSocketRef *socket); + DInputSocket(const DTreeContext *context, const bNodeSocket *socket); explicit DInputSocket(const DSocket &base_socket); - const InputSocketRef *socket_ref() const; - const InputSocketRef *operator->() const; - DOutputSocket get_corresponding_group_node_output() const; Vector<DOutputSocket, 4> get_corresponding_group_input_sockets() const; @@ -144,12 +144,9 @@ class DInputSocket : public DSocket { class DOutputSocket : public DSocket { public: DOutputSocket() = default; - DOutputSocket(const DTreeContext *context, const OutputSocketRef *socket); + DOutputSocket(const DTreeContext *context, const bNodeSocket *socket); explicit DOutputSocket(const DSocket &base_socket); - const OutputSocketRef *socket_ref() const; - const OutputSocketRef *operator->() const; - DInputSocket get_corresponding_group_node_input() const; DInputSocket get_active_corresponding_group_output_socket() const; @@ -177,7 +174,7 @@ class DerivedNodeTree { private: LinearAllocator<> allocator_; DTreeContext *root_context_; - VectorSet<const NodeTreeRef *> used_node_tree_refs_; + VectorSet<const bNodeTree *> used_btrees_; public: /** @@ -186,11 +183,11 @@ class DerivedNodeTree { * has to make sure that the node tree refs added to #node_tree_refs live at least as long as the * derived node tree. */ - DerivedNodeTree(bNodeTree &btree, NodeTreeRefMap &node_tree_refs); + DerivedNodeTree(const bNodeTree &btree); ~DerivedNodeTree(); const DTreeContext &root_context() const; - Span<const NodeTreeRef *> used_node_tree_refs() const; + Span<const bNodeTree *> used_btrees() const; /** * \return True when there is a link cycle. Unavailable sockets are ignored. @@ -205,9 +202,8 @@ class DerivedNodeTree { private: DTreeContext &construct_context_recursively(DTreeContext *parent_context, - const NodeRef *parent_node, - bNodeTree &btree, - NodeTreeRefMap &node_tree_refs); + const bNode *parent_node, + const bNodeTree &btree); void destruct_context_recursively(DTreeContext *context); void foreach_node_in_context_recursive(const DTreeContext &context, @@ -215,7 +211,6 @@ class DerivedNodeTree { }; namespace derived_node_tree_types { -using namespace node_tree_ref_types; using nodes::DerivedNodeTree; using nodes::DInputSocket; using nodes::DNode; @@ -228,9 +223,9 @@ using nodes::DTreeContext; /** \name #DTreeContext Inline Methods * \{ */ -inline const NodeTreeRef &DTreeContext::tree() const +inline const bNodeTree &DTreeContext::btree() const { - return *tree_; + return *btree_; } inline const DTreeContext *DTreeContext::parent_context() const @@ -238,12 +233,12 @@ inline const DTreeContext *DTreeContext::parent_context() const return parent_context_; } -inline const NodeRef *DTreeContext::parent_node() const +inline const bNode *DTreeContext::parent_node() const { return parent_node_; } -inline const DTreeContext *DTreeContext::child_context(const NodeRef &node) const +inline const DTreeContext *DTreeContext::child_context(const bNode &node) const { return children_.lookup_default(&node, nullptr); } @@ -264,10 +259,10 @@ inline bool DTreeContext::is_root() const /** \name #DNode Inline Methods * \{ */ -inline DNode::DNode(const DTreeContext *context, const NodeRef *node_ref) - : context_(context), node_ref_(node_ref) +inline DNode::DNode(const DTreeContext *context, const bNode *bnode) + : context_(context), bnode_(bnode) { - BLI_assert(node_ref == nullptr || &node_ref->tree() == &context->tree()); + BLI_assert(bnode == nullptr || bnode->runtime->owner_tree == &context->btree()); } inline const DTreeContext *DNode::context() const @@ -275,14 +270,14 @@ inline const DTreeContext *DNode::context() const return context_; } -inline const NodeRef *DNode::node_ref() const +inline const bNode *DNode::bnode() const { - return node_ref_; + return bnode_; } inline bool operator==(const DNode &a, const DNode &b) { - return a.context_ == b.context_ && a.node_ref_ == b.node_ref_; + return a.context_ == b.context_ && a.bnode_ == b.bnode_; } inline bool operator!=(const DNode &a, const DNode &b) @@ -292,37 +287,43 @@ inline bool operator!=(const DNode &a, const DNode &b) inline DNode::operator bool() const { - return node_ref_ != nullptr; + return bnode_ != nullptr; +} + +inline const bNode *DNode::operator->() const +{ + return bnode_; } -inline const NodeRef *DNode::operator->() const +inline const bNode &DNode::operator*() const { - return node_ref_; + BLI_assert(bnode_ != nullptr); + return *bnode_; } inline uint64_t DNode::hash() const { - return get_default_hash_2(context_, node_ref_); + return get_default_hash_2(context_, bnode_); } inline DInputSocket DNode::input(int index) const { - return {context_, &node_ref_->input(index)}; + return {context_, &bnode_->input_socket(index)}; } inline DOutputSocket DNode::output(int index) const { - return {context_, &node_ref_->output(index)}; + return {context_, &bnode_->output_socket(index)}; } inline DInputSocket DNode::input_by_identifier(StringRef identifier) const { - return {context_, &node_ref_->input_by_identifier(identifier)}; + return {context_, &bnode_->input_by_identifier(identifier)}; } inline DOutputSocket DNode::output_by_identifier(StringRef identifier) const { - return {context_, &node_ref_->output_by_identifier(identifier)}; + return {context_, &bnode_->output_by_identifier(identifier)}; } /** \} */ @@ -331,19 +332,20 @@ inline DOutputSocket DNode::output_by_identifier(StringRef identifier) const /** \name #DSocket Inline Methods * \{ */ -inline DSocket::DSocket(const DTreeContext *context, const SocketRef *socket_ref) - : context_(context), socket_ref_(socket_ref) +inline DSocket::DSocket(const DTreeContext *context, const bNodeSocket *bsocket) + : context_(context), bsocket_(bsocket) { - BLI_assert(socket_ref == nullptr || &socket_ref->tree() == &context->tree()); + BLI_assert(bsocket == nullptr || + bsocket->runtime->owner_node->runtime->owner_tree == &context->btree()); } inline DSocket::DSocket(const DInputSocket &input_socket) - : DSocket(input_socket.context_, input_socket.socket_ref_) + : DSocket(input_socket.context_, input_socket.bsocket_) { } inline DSocket::DSocket(const DOutputSocket &output_socket) - : DSocket(output_socket.context_, output_socket.socket_ref_) + : DSocket(output_socket.context_, output_socket.bsocket_) { } @@ -352,14 +354,14 @@ inline const DTreeContext *DSocket::context() const return context_; } -inline const SocketRef *DSocket::socket_ref() const +inline const bNodeSocket *DSocket::bsocket() const { - return socket_ref_; + return bsocket_; } inline bool operator==(const DSocket &a, const DSocket &b) { - return a.context_ == b.context_ && a.socket_ref_ == b.socket_ref_; + return a.context_ == b.context_ && a.bsocket_ == b.bsocket_; } inline bool operator!=(const DSocket &a, const DSocket &b) @@ -369,23 +371,29 @@ inline bool operator!=(const DSocket &a, const DSocket &b) inline DSocket::operator bool() const { - return socket_ref_ != nullptr; + return bsocket_ != nullptr; } -inline const SocketRef *DSocket::operator->() const +inline const bNodeSocket *DSocket::operator->() const { - return socket_ref_; + return bsocket_; +} + +inline const bNodeSocket &DSocket::operator*() const +{ + BLI_assert(bsocket_ != nullptr); + return *bsocket_; } inline uint64_t DSocket::hash() const { - return get_default_hash_2(context_, socket_ref_); + return get_default_hash_2(context_, bsocket_); } inline DNode DSocket::node() const { - BLI_assert(socket_ref_ != nullptr); - return {context_, &socket_ref_->node()}; + BLI_assert(bsocket_ != nullptr); + return {context_, bsocket_->runtime->owner_node}; } /** \} */ @@ -394,8 +402,8 @@ inline DNode DSocket::node() const /** \name #DInputSocket Inline Methods * \{ */ -inline DInputSocket::DInputSocket(const DTreeContext *context, const InputSocketRef *socket_ref) - : DSocket(context, socket_ref) +inline DInputSocket::DInputSocket(const DTreeContext *context, const bNodeSocket *bsocket) + : DSocket(context, bsocket) { } @@ -404,24 +412,14 @@ inline DInputSocket::DInputSocket(const DSocket &base_socket) : DSocket(base_soc BLI_assert(base_socket->is_input()); } -inline const InputSocketRef *DInputSocket::socket_ref() const -{ - return (const InputSocketRef *)socket_ref_; -} - -inline const InputSocketRef *DInputSocket::operator->() const -{ - return (const InputSocketRef *)socket_ref_; -} - /** \} */ /* -------------------------------------------------------------------- */ /** \name #DOutputSocket Inline Methods * \{ */ -inline DOutputSocket::DOutputSocket(const DTreeContext *context, const OutputSocketRef *socket_ref) - : DSocket(context, socket_ref) +inline DOutputSocket::DOutputSocket(const DTreeContext *context, const bNodeSocket *bsocket) + : DSocket(context, bsocket) { } @@ -430,16 +428,6 @@ inline DOutputSocket::DOutputSocket(const DSocket &base_socket) : DSocket(base_s BLI_assert(base_socket->is_output()); } -inline const OutputSocketRef *DOutputSocket::socket_ref() const -{ - return (const OutputSocketRef *)socket_ref_; -} - -inline const OutputSocketRef *DOutputSocket::operator->() const -{ - return (const OutputSocketRef *)socket_ref_; -} - /** \} */ /* -------------------------------------------------------------------- */ @@ -451,9 +439,9 @@ inline const DTreeContext &DerivedNodeTree::root_context() const return *root_context_; } -inline Span<const NodeTreeRef *> DerivedNodeTree::used_node_tree_refs() const +inline Span<const bNodeTree *> DerivedNodeTree::used_btrees() const { - return used_node_tree_refs_; + return used_btrees_; } /** \} */ diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index e16cd7a253f..d02bbeb67f7 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -49,6 +49,7 @@ void register_node_type_geo_curve_to_points(void); void register_node_type_geo_curve_trim(void); void register_node_type_geo_deform_curves_on_surface(void); void register_node_type_geo_delete_geometry(void); +void register_node_type_geo_distribute_points_in_volume(void); void register_node_type_geo_distribute_points_on_faces(void); void register_node_type_geo_dual_mesh(void); void register_node_type_geo_duplicate_elements(void); @@ -95,6 +96,7 @@ void register_node_type_geo_join_geometry(void); void register_node_type_geo_material_replace(void); void register_node_type_geo_material_selection(void); void register_node_type_geo_merge_by_distance(void); +void register_node_type_geo_mesh_face_set_boundaries(void); void register_node_type_geo_mesh_primitive_circle(void); void register_node_type_geo_mesh_primitive_cone(void); void register_node_type_geo_mesh_primitive_cube(void); @@ -116,11 +118,15 @@ void register_node_type_geo_raycast(void); void register_node_type_geo_realize_instances(void); void register_node_type_geo_remove_attribute(void); void register_node_type_geo_rotate_instances(void); +void register_node_type_geo_sample_index(void); +void register_node_type_geo_sample_nearest_surface(void); +void register_node_type_geo_sample_nearest(void); void register_node_type_geo_scale_elements(void); void register_node_type_geo_scale_instances(void); void register_node_type_geo_select_by_handle_type(void); void register_node_type_geo_separate_components(void); void register_node_type_geo_separate_geometry(void); +void register_node_type_geo_self_object(void); void register_node_type_geo_set_curve_handles(void); void register_node_type_geo_set_curve_radius(void); void register_node_type_geo_set_curve_tilt(void); @@ -137,7 +143,6 @@ void register_node_type_geo_string_join(void); void register_node_type_geo_string_to_curves(void); void register_node_type_geo_subdivision_surface(void); void register_node_type_geo_switch(void); -void register_node_type_geo_transfer_attribute(void); void register_node_type_geo_transform(void); void register_node_type_geo_translate_instances(void); void register_node_type_geo_triangulate(void); diff --git a/source/blender/nodes/NOD_geometry_exec.hh b/source/blender/nodes/NOD_geometry_exec.hh index c5bc42b059d..73e82f741ab 100644 --- a/source/blender/nodes/NOD_geometry_exec.hh +++ b/source/blender/nodes/NOD_geometry_exec.hh @@ -3,6 +3,7 @@ #pragma once #include "FN_field.hh" +#include "FN_lazy_function.hh" #include "FN_multi_function_builder.hh" #include "BKE_geometry_fields.hh" @@ -11,9 +12,8 @@ #include "DNA_node_types.h" #include "NOD_derived_node_tree.hh" -#include "NOD_geometry_nodes_eval_log.hh" +#include "NOD_geometry_nodes_lazy_function.hh" -struct Depsgraph; struct ModifierData; namespace blender::nodes { @@ -28,8 +28,6 @@ using bke::AttributeReader; using bke::AttributeWriter; using bke::GAttributeReader; using bke::GAttributeWriter; -using bke::GeometryComponentFieldContext; -using bke::GeometryFieldInput; using bke::GSpanAttributeWriter; using bke::MutableAttributeAccessor; using bke::SpanAttributeWriter; @@ -42,75 +40,18 @@ using fn::FieldInput; using fn::FieldOperation; using fn::GField; using fn::ValueOrField; -using geometry_nodes_eval_log::eNamedAttrUsage; -using geometry_nodes_eval_log::NodeWarningType; - -/** - * This class exists to separate the memory management details of the geometry nodes evaluator - * from the node execution functions and related utilities. - */ -class GeoNodeExecParamsProvider { - public: - DNode dnode; - const Object *self_object = nullptr; - const ModifierData *modifier = nullptr; - Depsgraph *depsgraph = nullptr; - geometry_nodes_eval_log::GeoLogger *logger = nullptr; - - /** - * Returns true when the node is allowed to get/extract the input value. The identifier is - * expected to be valid. This may return false if the input value has been consumed already. - */ - virtual bool can_get_input(StringRef identifier) const = 0; - - /** - * Returns true when the node is allowed to set the output value. The identifier is expected to - * be valid. This may return false if the output value has been set already. - */ - virtual bool can_set_output(StringRef identifier) const = 0; - - /** - * Take ownership of an input value. The caller is responsible for destructing the value. It does - * not have to be freed, because the memory is managed by the geometry nodes evaluator. - */ - virtual GMutablePointer extract_input(StringRef identifier) = 0; - - /** - * Similar to #extract_input, but has to be used for multi-input sockets. - */ - virtual Vector<GMutablePointer> extract_multi_input(StringRef identifier) = 0; - - /** - * Get the input value for the identifier without taking ownership of it. - */ - virtual GPointer get_input(StringRef identifier) const = 0; - - /** - * Prepare a memory buffer for an output value of the node. The returned memory has to be - * initialized by the caller. The identifier and type are expected to be correct. - */ - virtual GMutablePointer alloc_output_value(const CPPType &type) = 0; - - /** - * The value has been allocated with #alloc_output_value. - */ - virtual void set_output(StringRef identifier, GMutablePointer value) = 0; - - /* A description for these methods is provided in GeoNodeExecParams. */ - virtual void set_input_unused(StringRef identifier) = 0; - virtual bool output_is_required(StringRef identifier) const = 0; - virtual bool lazy_require_input(StringRef identifier) = 0; - virtual bool lazy_output_is_required(StringRef identifier) const = 0; - - virtual void set_default_remaining_outputs() = 0; -}; +using geo_eval_log::NamedAttributeUsage; +using geo_eval_log::NodeWarningType; class GeoNodeExecParams { private: - GeoNodeExecParamsProvider *provider_; + const bNode &node_; + lf::Params ¶ms_; + const lf::Context &lf_context_; public: - GeoNodeExecParams(GeoNodeExecParamsProvider &provider) : provider_(&provider) + GeoNodeExecParams(const bNode &node, lf::Params ¶ms, const lf::Context &lf_context) + : node_(node), params_(params), lf_context_(lf_context) { } @@ -121,20 +62,6 @@ class GeoNodeExecParams { /** * Get the input value for the input socket with the given identifier. * - * The node calling becomes responsible for destructing the value before it is done - * executing. This method can only be called once for each identifier. - */ - GMutablePointer extract_input(StringRef identifier) - { -#ifdef DEBUG - this->check_input_access(identifier); -#endif - return provider_->extract_input(identifier); - } - - /** - * Get the input value for the input socket with the given identifier. - * * This method can only be called once for each identifier. */ template<typename T> T extract_input(StringRef identifier) @@ -153,8 +80,8 @@ class GeoNodeExecParams { #ifdef DEBUG this->check_input_access(identifier, &CPPType::get<T>()); #endif - GMutablePointer gvalue = this->extract_input(identifier); - T value = gvalue.relocate_out<T>(); + const int index = this->get_input_index(identifier); + T value = params_.extract_input<T>(index); if constexpr (std::is_same_v<T, GeometrySet>) { this->check_input_geometry_set(identifier, value); } @@ -166,27 +93,6 @@ class GeoNodeExecParams { void check_output_geometry_set(const GeometrySet &geometry_set) const; /** - * Get input as vector for multi input socket with the given identifier. - * - * This method can only be called once for each identifier. - */ - template<typename T> Vector<T> extract_multi_input(StringRef identifier) - { - Vector<GMutablePointer> gvalues = provider_->extract_multi_input(identifier); - Vector<T> values; - for (GMutablePointer gvalue : gvalues) { - if constexpr (is_field_base_type_v<T>) { - const ValueOrField<T> value_or_field = gvalue.relocate_out<ValueOrField<T>>(); - values.append(value_or_field.as_value()); - } - else { - values.append(gvalue.relocate_out<T>()); - } - } - return values; - } - - /** * Get the input value for the input socket with the given identifier. */ template<typename T> T get_input(StringRef identifier) const @@ -204,9 +110,8 @@ class GeoNodeExecParams { #ifdef DEBUG this->check_input_access(identifier, &CPPType::get<T>()); #endif - GPointer gvalue = provider_->get_input(identifier); - BLI_assert(gvalue.is_type<T>()); - const T &value = *(const T *)gvalue.get(); + const int index = this->get_input_index(identifier); + const T &value = params_.get_input<T>(index); if constexpr (std::is_same_v<T, GeometrySet>) { this->check_input_geometry_set(identifier, value); } @@ -228,17 +133,28 @@ class GeoNodeExecParams { this->set_output(identifier, ValueOrField<BaseType>(std::forward<T>(value))); } else { - const CPPType &type = CPPType::get<StoredT>(); #ifdef DEBUG + const CPPType &type = CPPType::get<StoredT>(); this->check_output_access(identifier, type); #endif if constexpr (std::is_same_v<StoredT, GeometrySet>) { this->check_output_geometry_set(value); } - GMutablePointer gvalue = provider_->alloc_output_value(type); - new (gvalue.get()) StoredT(std::forward<T>(value)); - provider_->set_output(identifier, gvalue); + const int index = this->get_output_index(identifier); + params_.set_output(index, std::forward<T>(value)); + } + } + + geo_eval_log::GeoTreeLogger *get_local_tree_logger() const + { + GeoNodesLFUserData *user_data = this->user_data(); + BLI_assert(user_data != nullptr); + const ComputeContext *compute_context = user_data->compute_context; + BLI_assert(compute_context != nullptr); + if (user_data->modifier_data->eval_log == nullptr) { + return nullptr; } + return &user_data->modifier_data->eval_log->get_local_tree_logger(*compute_context); } /** @@ -246,7 +162,8 @@ class GeoNodeExecParams { */ void set_input_unused(StringRef identifier) { - provider_->set_input_unused(identifier); + const int index = this->get_input_index(identifier); + params_.set_input_unused(index); } /** @@ -256,7 +173,8 @@ class GeoNodeExecParams { */ bool output_is_required(StringRef identifier) const { - return provider_->output_is_required(identifier); + const int index = this->get_output_index(identifier); + return params_.get_output_usage(index) != lf::ValueUsage::Unused; } /** @@ -267,7 +185,8 @@ class GeoNodeExecParams { */ bool lazy_require_input(StringRef identifier) { - return provider_->lazy_require_input(identifier); + const int index = this->get_input_index(identifier); + return params_.try_get_input_data_ptr_or_request(index) == nullptr; } /** @@ -277,7 +196,8 @@ class GeoNodeExecParams { */ bool lazy_output_is_required(StringRef identifier) { - return provider_->lazy_output_is_required(identifier); + const int index = this->get_output_index(identifier); + return params_.get_output_usage(index) == lf::ValueUsage::Used; } /** @@ -285,30 +205,45 @@ class GeoNodeExecParams { */ const bNode &node() const { - return *provider_->dnode->bnode(); + return node_; } const Object *self_object() const { - return provider_->self_object; + if (const auto *data = this->user_data()) { + if (data->modifier_data) { + return data->modifier_data->self_object; + } + } + return nullptr; } Depsgraph *depsgraph() const { - return provider_->depsgraph; + if (const auto *data = this->user_data()) { + if (data->modifier_data) { + return data->modifier_data->depsgraph; + } + } + return nullptr; + } + + GeoNodesLFUserData *user_data() const + { + return dynamic_cast<GeoNodesLFUserData *>(lf_context_.user_data); } /** * Add an error message displayed at the top of the node when displaying the node tree, * and potentially elsewhere in Blender. */ - void error_message_add(const NodeWarningType type, std::string message) const; + void error_message_add(const NodeWarningType type, StringRef message) const; std::string attribute_producer_name() const; void set_default_remaining_outputs(); - void used_named_attribute(std::string attribute_name, eNamedAttrUsage usage); + void used_named_attribute(StringRef attribute_name, NamedAttributeUsage usage); private: /* Utilities for detecting common errors at when using this class. */ @@ -317,6 +252,38 @@ class GeoNodeExecParams { /* Find the active socket with the input name (not the identifier). */ const bNodeSocket *find_available_socket(const StringRef name) const; + + int get_input_index(const StringRef identifier) const + { + int counter = 0; + for (const bNodeSocket *socket : node_.input_sockets()) { + if (!socket->is_available()) { + continue; + } + if (socket->identifier == identifier) { + return counter; + } + counter++; + } + BLI_assert_unreachable(); + return -1; + } + + int get_output_index(const StringRef identifier) const + { + int counter = 0; + for (const bNodeSocket *socket : node_.output_sockets()) { + if (!socket->is_available()) { + continue; + } + if (socket->identifier == identifier) { + return counter; + } + counter++; + } + BLI_assert_unreachable(); + return -1; + } }; } // namespace blender::nodes diff --git a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh b/source/blender/nodes/NOD_geometry_nodes_eval_log.hh deleted file mode 100644 index 46ba72d14d8..00000000000 --- a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh +++ /dev/null @@ -1,411 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#pragma once - -/** - * Many geometry nodes related UI features need access to data produced during evaluation. Not only - * is the final output required but also the intermediate results. Those features include - * attribute search, node warnings, socket inspection and the viewer node. - * - * This file provides the framework for logging data during evaluation and accessing the data after - * evaluation. - * - * During logging every thread gets its own local logger to avoid too much locking (logging - * generally happens for every socket). After geometry nodes evaluation is done, the thread-local - * logging information is combined and post-processed to make it easier for the UI to lookup. - * necessary information. - */ - -#include "BLI_enumerable_thread_specific.hh" -#include "BLI_function_ref.hh" -#include "BLI_generic_pointer.hh" -#include "BLI_linear_allocator.hh" -#include "BLI_map.hh" - -#include "BKE_geometry_set.hh" - -#include "NOD_derived_node_tree.hh" - -#include "FN_field.hh" - -#include <chrono> - -struct SpaceNode; -struct SpaceSpreadsheet; - -namespace blender::nodes::geometry_nodes_eval_log { - -/** Contains information about a value that has been computed during geometry nodes evaluation. */ -class ValueLog { - public: - virtual ~ValueLog() = default; -}; - -/** Contains an owned copy of a value of a generic type. */ -class GenericValueLog : public ValueLog { - private: - GMutablePointer data_; - - public: - GenericValueLog(GMutablePointer data) : data_(data) - { - } - - ~GenericValueLog() - { - data_.destruct(); - } - - GPointer value() const - { - return data_; - } -}; - -class GFieldValueLog : public ValueLog { - private: - fn::GField field_; - const CPPType &type_; - Vector<std::string> input_tooltips_; - - public: - GFieldValueLog(fn::GField field, bool log_full_field); - - const fn::GField &field() const - { - return field_; - } - - Span<std::string> input_tooltips() const - { - return input_tooltips_; - } - - const CPPType &type() const - { - return type_; - } -}; - -struct GeometryAttributeInfo { - std::string name; - /** Can be empty when #name does not actually exist on a geometry yet. */ - std::optional<eAttrDomain> domain; - std::optional<eCustomDataType> data_type; -}; - -/** Contains information about a geometry set. In most cases this does not store the entire - * geometry set as this would require too much memory. */ -class GeometryValueLog : public ValueLog { - private: - Vector<GeometryAttributeInfo> attributes_; - Vector<GeometryComponentType> component_types_; - std::unique_ptr<GeometrySet> full_geometry_; - - public: - struct MeshInfo { - int verts_num, edges_num, faces_num; - }; - struct CurveInfo { - int splines_num; - }; - struct PointCloudInfo { - int points_num; - }; - struct InstancesInfo { - int instances_num; - }; - struct EditDataInfo { - bool has_deformed_positions; - bool has_deform_matrices; - }; - - std::optional<MeshInfo> mesh_info; - std::optional<CurveInfo> curve_info; - std::optional<PointCloudInfo> pointcloud_info; - std::optional<InstancesInfo> instances_info; - std::optional<EditDataInfo> edit_data_info; - - GeometryValueLog(const GeometrySet &geometry_set, bool log_full_geometry = false); - - Span<GeometryAttributeInfo> attributes() const - { - return attributes_; - } - - Span<GeometryComponentType> component_types() const - { - return component_types_; - } - - const GeometrySet *full_geometry() const - { - return full_geometry_.get(); - } -}; - -enum class NodeWarningType { - Error, - Warning, - Info, -}; - -struct NodeWarning { - NodeWarningType type; - std::string message; -}; - -struct NodeWithWarning { - DNode node; - NodeWarning warning; -}; - -struct NodeWithExecutionTime { - DNode node; - std::chrono::microseconds exec_time; -}; - -struct NodeWithDebugMessage { - DNode node; - std::string message; -}; - -/** The same value can be referenced by multiple sockets when they are linked. */ -struct ValueOfSockets { - Span<DSocket> sockets; - destruct_ptr<ValueLog> value; -}; - -enum class eNamedAttrUsage { - None = 0, - Read = 1 << 0, - Write = 1 << 1, - Remove = 1 << 2, -}; -ENUM_OPERATORS(eNamedAttrUsage, eNamedAttrUsage::Remove); - -struct UsedNamedAttribute { - std::string name; - eNamedAttrUsage usage; -}; - -struct NodeWithUsedNamedAttribute { - DNode node; - UsedNamedAttribute attribute; -}; - -class GeoLogger; -class ModifierLog; - -/** Every thread has its own local logger to avoid having to communicate between threads during - * evaluation. After evaluation the individual logs are combined. */ -class LocalGeoLogger { - private: - /* Back pointer to the owner of this local logger. */ - GeoLogger *main_logger_; - /* Allocator for the many small allocations during logging. This is in a `unique_ptr` so that - * ownership can be transferred later on. */ - std::unique_ptr<LinearAllocator<>> allocator_; - Vector<ValueOfSockets> values_; - Vector<NodeWithWarning> node_warnings_; - Vector<NodeWithExecutionTime> node_exec_times_; - Vector<NodeWithDebugMessage> node_debug_messages_; - Vector<NodeWithUsedNamedAttribute> used_named_attributes_; - - friend ModifierLog; - - public: - LocalGeoLogger(GeoLogger &main_logger) : main_logger_(&main_logger) - { - this->allocator_ = std::make_unique<LinearAllocator<>>(); - } - - void log_value_for_sockets(Span<DSocket> sockets, GPointer value); - void log_multi_value_socket(DSocket socket, Span<GPointer> values); - void log_node_warning(DNode node, NodeWarningType type, std::string message); - void log_execution_time(DNode node, std::chrono::microseconds exec_time); - void log_used_named_attribute(DNode node, std::string attribute_name, eNamedAttrUsage usage); - /** - * Log a message that will be displayed in the node editor next to the node. - * This should only be used for debugging purposes and not to display information to users. - */ - void log_debug_message(DNode node, std::string message); -}; - -/** The root logger class. */ -class GeoLogger { - private: - /** - * Log the entire value for these sockets, because they may be inspected afterwards. - * We don't log everything, because that would take up too much memory and cause significant - * slowdowns. - */ - Set<DSocket> log_full_sockets_; - threading::EnumerableThreadSpecific<LocalGeoLogger> threadlocals_; - - /* These are only optional since they don't have a default constructor. */ - std::unique_ptr<GeometryValueLog> input_geometry_log_; - std::unique_ptr<GeometryValueLog> output_geometry_log_; - - friend LocalGeoLogger; - friend ModifierLog; - - public: - GeoLogger(Set<DSocket> log_full_sockets) - : log_full_sockets_(std::move(log_full_sockets)), - threadlocals_([this]() { return LocalGeoLogger(*this); }) - { - } - - void log_input_geometry(const GeometrySet &geometry) - { - input_geometry_log_ = std::make_unique<GeometryValueLog>(geometry); - } - - void log_output_geometry(const GeometrySet &geometry) - { - output_geometry_log_ = std::make_unique<GeometryValueLog>(geometry); - } - - LocalGeoLogger &local() - { - return threadlocals_.local(); - } - - auto begin() - { - return threadlocals_.begin(); - } - - auto end() - { - return threadlocals_.end(); - } -}; - -/** Contains information that has been logged for one specific socket. */ -class SocketLog { - private: - ValueLog *value_ = nullptr; - - friend ModifierLog; - - public: - const ValueLog *value() const - { - return value_; - } -}; - -/** Contains information that has been logged for one specific node. */ -class NodeLog { - private: - Vector<SocketLog> input_logs_; - Vector<SocketLog> output_logs_; - Vector<NodeWarning, 0> warnings_; - Vector<std::string, 0> debug_messages_; - Vector<UsedNamedAttribute, 0> used_named_attributes_; - std::chrono::microseconds exec_time_; - - friend ModifierLog; - - public: - const SocketLog *lookup_socket_log(eNodeSocketInOut in_out, int index) const; - const SocketLog *lookup_socket_log(const bNode &node, const bNodeSocket &socket) const; - void execution_time(std::chrono::microseconds exec_time); - - Span<SocketLog> input_logs() const - { - return input_logs_; - } - - Span<SocketLog> output_logs() const - { - return output_logs_; - } - - Span<NodeWarning> warnings() const - { - return warnings_; - } - - Span<std::string> debug_messages() const - { - return debug_messages_; - } - - Span<UsedNamedAttribute> used_named_attributes() const - { - return used_named_attributes_; - } - - std::chrono::microseconds execution_time() const - { - return exec_time_; - } - - Vector<const GeometryAttributeInfo *> lookup_available_attributes() const; -}; - -/** Contains information that has been logged for one specific tree. */ -class TreeLog { - private: - Map<std::string, destruct_ptr<NodeLog>> node_logs_; - Map<std::string, destruct_ptr<TreeLog>> child_logs_; - - friend ModifierLog; - - public: - const NodeLog *lookup_node_log(StringRef node_name) const; - const NodeLog *lookup_node_log(const bNode &node) const; - const TreeLog *lookup_child_log(StringRef node_name) const; - void foreach_node_log(FunctionRef<void(const NodeLog &)> fn) const; -}; - -/** Contains information about an entire geometry nodes evaluation. */ -class ModifierLog { - private: - LinearAllocator<> allocator_; - /* Allocators of the individual loggers. */ - Vector<std::unique_ptr<LinearAllocator<>>> logger_allocators_; - destruct_ptr<TreeLog> root_tree_logs_; - Vector<destruct_ptr<ValueLog>> logged_values_; - - std::unique_ptr<GeometryValueLog> input_geometry_log_; - std::unique_ptr<GeometryValueLog> output_geometry_log_; - - public: - ModifierLog(GeoLogger &logger); - - const TreeLog &root_tree() const - { - return *root_tree_logs_; - } - - /* Utilities to find logged information for a specific context. */ - static const ModifierLog *find_root_by_node_editor_context(const SpaceNode &snode); - static const TreeLog *find_tree_by_node_editor_context(const SpaceNode &snode); - static const NodeLog *find_node_by_node_editor_context(const SpaceNode &snode, - const bNode &node); - static const NodeLog *find_node_by_node_editor_context(const SpaceNode &snode, - const StringRef node_name); - static const SocketLog *find_socket_by_node_editor_context(const SpaceNode &snode, - const bNode &node, - const bNodeSocket &socket); - static const NodeLog *find_node_by_spreadsheet_editor_context( - const SpaceSpreadsheet &sspreadsheet); - void foreach_node_log(FunctionRef<void(const NodeLog &)> fn) const; - - const GeometryValueLog *input_geometry_log() const; - const GeometryValueLog *output_geometry_log() const; - - private: - using LogByTreeContext = Map<const DTreeContext *, TreeLog *>; - - TreeLog &lookup_or_add_tree_log(LogByTreeContext &log_by_tree_context, - const DTreeContext &tree_context); - NodeLog &lookup_or_add_node_log(LogByTreeContext &log_by_tree_context, DNode node); - SocketLog &lookup_or_add_socket_log(LogByTreeContext &log_by_tree_context, DSocket socket); -}; - -} // namespace blender::nodes::geometry_nodes_eval_log diff --git a/source/blender/nodes/NOD_geometry_nodes_lazy_function.hh b/source/blender/nodes/NOD_geometry_nodes_lazy_function.hh new file mode 100644 index 00000000000..240a0115f68 --- /dev/null +++ b/source/blender/nodes/NOD_geometry_nodes_lazy_function.hh @@ -0,0 +1,181 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +/** + * For evaluation, geometry node groups are converted to a lazy-function graph. The generated graph + * is cached per node group, so it only has to be generated once after a change. + * + * Node groups are *not* inlined into the lazy-function graph. This could be added in the future as + * it might improve performance in some cases, but generally does not seem necessary. Inlining node + * groups also has disadvantages like making per-node-group caches less useful, resulting in more + * overhead. + * + * Instead, group nodes are just like all other nodes in the lazy-function graph. What makes them + * special is that they reference the lazy-function graph of the group they reference. + * + * During lazy-function graph generation, a mapping between the #bNodeTree and + * #lazy_function::Graph is build that can be used when evaluating the graph (e.g. for logging). + */ + +#include "FN_lazy_function_graph.hh" +#include "FN_lazy_function_graph_executor.hh" + +#include "NOD_geometry_nodes_log.hh" +#include "NOD_multi_function.hh" + +#include "BLI_compute_context.hh" + +struct Object; +struct Depsgraph; + +namespace blender::nodes { + +namespace lf = fn::lazy_function; +using lf::LazyFunction; + +/** + * Data that is passed into geometry nodes evaluation from the modifier. + */ +struct GeoNodesModifierData { + /** Object that is currently evaluated. */ + const Object *self_object = nullptr; + /** Depsgraph that is evaluating the modifier. */ + Depsgraph *depsgraph = nullptr; + /** Optional logger. */ + geo_eval_log::GeoModifierLog *eval_log = nullptr; + /** + * Some nodes should be executed even when their output is not used (e.g. active viewer nodes and + * the node groups they are contained in). + */ + const MultiValueMap<ComputeContextHash, const lf::FunctionNode *> *side_effect_nodes; +}; + +/** + * Custom user data that is passed to every geometry nodes related lazy-function evaluation. + */ +struct GeoNodesLFUserData : public lf::UserData { + /** + * Data from the modifier that is being evaluated. + */ + GeoNodesModifierData *modifier_data = nullptr; + /** + * Current compute context. This is different depending in the (nested) node group that is being + * evaluated. + */ + const ComputeContext *compute_context = nullptr; +}; + +/** + * Contains the mapping between the #bNodeTree and the corresponding lazy-function graph. + * This is *not* a one-to-one mapping. + */ +struct GeometryNodeLazyFunctionGraphMapping { + /** + * Contains mapping of sockets for special nodes like group input and group output. + */ + Map<const bNodeSocket *, lf::Socket *> dummy_socket_map; + /** + * 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; + /** + * A mapping used for logging intermediate values. + */ + MultiValueMap<const lf::Socket *, const bNodeSocket *> bsockets_by_lf_socket_map; + /** + * Mappings for some special node types. Generally, this mapping does not exist for all node + * types, so better have more specialized mappings for now. + */ + Map<const bNode *, const lf::FunctionNode *> group_node_map; + Map<const bNode *, const lf::FunctionNode *> viewer_node_map; +}; + +/** + * Data that is cached for every #bNodeTree. + */ +struct GeometryNodesLazyFunctionGraphInfo { + /** + * Allocator used for many things contained in this struct. + */ + LinearAllocator<> allocator; + /** + * Many nodes are implemented as multi-functions. So this contains a mapping from nodes to their + * corresponding multi-functions. + */ + std::unique_ptr<NodeMultiFunctions> node_multi_functions; + /** + * Many lazy-functions are build for the lazy-function graph. Since the graph does not own them, + * we have to keep track of them separately. + */ + Vector<std::unique_ptr<LazyFunction>> functions; + /** + * Many sockets have default values. Since those are not owned by the lazy-function graph, we + * have to keep track of them separately. This only owns the values, the memory is owned by the + * allocator above. + */ + Vector<GMutablePointer> values_to_destruct; + /** + * The actual lazy-function graph. + */ + lf::Graph graph; + /** + * Mappings between the lazy-function graph and the #bNodeTree. + */ + GeometryNodeLazyFunctionGraphMapping mapping; + /** + * Approximate number of nodes in the graph if all sub-graphs were inlined. + * This can be used as a simple heuristic for the complexity of the node group. + */ + int num_inline_nodes_approximate = 0; + + GeometryNodesLazyFunctionGraphInfo(); + ~GeometryNodesLazyFunctionGraphInfo(); +}; + +/** + * Logs intermediate values from the lazy-function graph evaluation into #GeoModifierLog based on + * the mapping between the lazy-function graph and the corresponding #bNodeTree. + */ +class GeometryNodesLazyFunctionLogger : public fn::lazy_function::GraphExecutor::Logger { + private: + const GeometryNodesLazyFunctionGraphInfo &lf_graph_info_; + + public: + GeometryNodesLazyFunctionLogger(const GeometryNodesLazyFunctionGraphInfo &lf_graph_info); + void log_socket_value(const fn::lazy_function::Socket &lf_socket, + GPointer value, + const fn::lazy_function::Context &context) const override; + void dump_when_outputs_are_missing(const lf::FunctionNode &node, + Span<const lf::OutputSocket *> missing_sockets, + const lf::Context &context) const override; + void dump_when_input_is_set_twice(const lf::InputSocket &target_socket, + const lf::OutputSocket &from_socket, + const lf::Context &context) const override; + void log_before_node_execute(const lf::FunctionNode &node, + const lf::Params ¶ms, + const lf::Context &context) const override; +}; + +/** + * Tells the lazy-function graph evaluator which nodes have side effects based on the current + * context. For example, the same viewer node can have side effects in one context, but not in + * another (depending on e.g. which tree path is currently viewed in the node editor). + */ +class GeometryNodesLazyFunctionSideEffectProvider + : public fn::lazy_function::GraphExecutor::SideEffectProvider { + public: + Vector<const lf::FunctionNode *> get_nodes_with_side_effects( + const lf::Context &context) const override; +}; + +/** + * Main function that converts a #bNodeTree into a lazy-function graph. If the graph has been + * generated already, nothing is done. Under some circumstances a valid graph cannot be created. In + * those cases null is returned. + */ +const GeometryNodesLazyFunctionGraphInfo *ensure_geometry_nodes_lazy_function_graph( + const bNodeTree &btree); + +} // namespace blender::nodes diff --git a/source/blender/nodes/NOD_geometry_nodes_log.hh b/source/blender/nodes/NOD_geometry_nodes_log.hh new file mode 100644 index 00000000000..cf59c99bc79 --- /dev/null +++ b/source/blender/nodes/NOD_geometry_nodes_log.hh @@ -0,0 +1,340 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +/** + * Many geometry nodes related UI features need access to data produced during evaluation. Not only + * is the final output required but also the intermediate results. Those features include attribute + * search, node warnings, socket inspection and the viewer node. + * + * This file provides the system for logging data during evaluation and accessing the data after + * evaluation. Geometry nodes is executed by a modifier, therefore the "root" of logging is + * #GeoModifierLog which will contain all data generated in a modifier. + * + * The system makes a distinction between "loggers" and the "log": + * - Logger (#GeoTreeLogger): Is used during geometry nodes evaluation. Each thread logs data + * independently to avoid communication between threads. Logging should generally be fast. + * Generally, the logged data is just dumped into simple containers. Any processing of the data + * happens later if necessary. This is important for performance, because in practice, most of + * the logged data is never used again. So any processing of the data is likely to be a waste of + * resources. + * - Log (#GeoTreeLog, #GeoNodeLog): Those are used when accessing logged data in UI code. They + * contain and cache preprocessed data produced during logging. The log combines data from all + * thread-local loggers to provide simple access. Importantly, the (preprocessed) log is only + * created when it is actually used by UI code. + */ + +#include <chrono> + +#include "BLI_compute_context.hh" +#include "BLI_enumerable_thread_specific.hh" +#include "BLI_generic_pointer.hh" +#include "BLI_multi_value_map.hh" + +#include "BKE_attribute.h" +#include "BKE_geometry_set.hh" + +#include "FN_field.hh" + +#include "DNA_node_types.h" + +struct SpaceNode; +struct SpaceSpreadsheet; +struct NodesModifierData; + +namespace blender::nodes::geo_eval_log { + +using fn::GField; + +enum class NodeWarningType { + Error, + Warning, + Info, +}; + +struct NodeWarning { + NodeWarningType type; + std::string message; +}; + +enum class NamedAttributeUsage { + None = 0, + Read = 1 << 0, + Write = 1 << 1, + Remove = 1 << 2, +}; +ENUM_OPERATORS(NamedAttributeUsage, NamedAttributeUsage::Remove); + +/** + * Values of different types are logged differently. This is necessary because some types are so + * simple that we can log them entirely (e.g. `int`), while we don't want to log all intermediate + * geometries in their entirety. + * + * #ValueLog is a base class for the different ways we log values. + */ +class ValueLog { + public: + virtual ~ValueLog() = default; +}; + +/** + * Simplest logger. It just stores a copy of the entire value. This is used for most simple types + * like `int`. + */ +class GenericValueLog : public ValueLog { + public: + /** + * This is owning the value, but not the memory. + */ + GMutablePointer value; + + GenericValueLog(const GMutablePointer value) : value(value) + { + } + + ~GenericValueLog(); +}; + +/** + * Fields are not logged entirely, because they might contain arbitrarily large data (e.g. + * geometries that are sampled). Instead, only the data needed for UI features is logged. + */ +class FieldInfoLog : public ValueLog { + public: + const CPPType &type; + Vector<std::string> input_tooltips; + + FieldInfoLog(const GField &field); +}; + +struct GeometryAttributeInfo { + std::string name; + /** Can be empty when #name does not actually exist on a geometry yet. */ + std::optional<eAttrDomain> domain; + std::optional<eCustomDataType> data_type; +}; + +/** + * Geometries are not logged entirely, because that would result in a lot of time and memory + * overhead. Instead, only the data needed for UI features is logged. + */ +class GeometryInfoLog : public ValueLog { + public: + Vector<GeometryAttributeInfo> attributes; + Vector<GeometryComponentType> component_types; + + struct MeshInfo { + int verts_num, edges_num, faces_num; + }; + struct CurveInfo { + int splines_num; + }; + struct PointCloudInfo { + int points_num; + }; + struct InstancesInfo { + int instances_num; + }; + struct EditDataInfo { + bool has_deformed_positions; + bool has_deform_matrices; + }; + + std::optional<MeshInfo> mesh_info; + std::optional<CurveInfo> curve_info; + std::optional<PointCloudInfo> pointcloud_info; + std::optional<InstancesInfo> instances_info; + std::optional<EditDataInfo> edit_data_info; + + GeometryInfoLog(const GeometrySet &geometry_set); +}; + +/** + * Data logged by a viewer node when it is executed. In this case, we do want to log the entire + * geometry. + */ +class ViewerNodeLog { + public: + GeometrySet geometry; + GField field; +}; + +using Clock = std::chrono::steady_clock; +using TimePoint = Clock::time_point; + +/** + * Logs all data for a specific geometry node tree in a specific context. When the same node group + * is used in multiple times each instantiation will have a separate logger. + */ +class GeoTreeLogger { + public: + std::optional<ComputeContextHash> parent_hash; + std::optional<StringRefNull> group_node_name; + Vector<ComputeContextHash> children_hashes; + + LinearAllocator<> *allocator = nullptr; + + struct WarningWithNode { + StringRefNull node_name; + NodeWarning warning; + }; + struct SocketValueLog { + StringRefNull node_name; + StringRefNull socket_identifier; + destruct_ptr<ValueLog> value; + }; + struct NodeExecutionTime { + StringRefNull node_name; + TimePoint start; + TimePoint end; + }; + struct ViewerNodeLogWithNode { + StringRefNull node_name; + destruct_ptr<ViewerNodeLog> viewer_log; + }; + struct AttributeUsageWithNode { + StringRefNull node_name; + StringRefNull attribute_name; + NamedAttributeUsage usage; + }; + struct DebugMessage { + StringRefNull node_name; + StringRefNull message; + }; + + Vector<WarningWithNode> node_warnings; + Vector<SocketValueLog> input_socket_values; + Vector<SocketValueLog> output_socket_values; + Vector<NodeExecutionTime> node_execution_times; + Vector<ViewerNodeLogWithNode, 0> viewer_node_logs; + Vector<AttributeUsageWithNode, 0> used_named_attributes; + Vector<DebugMessage, 0> debug_messages; + + GeoTreeLogger(); + ~GeoTreeLogger(); + + void log_value(const bNode &node, const bNodeSocket &socket, GPointer value); + void log_viewer_node(const bNode &viewer_node, const GeometrySet &geometry, const GField &field); +}; + +/** + * Contains data that has been logged for a specific node in a context. So when the node is in a + * node group that is used multiple times, there will be a different #GeoNodeLog for every + * instance. + * + * By default, not all of the info below is valid. A #GeoTreeLog::ensure_* method has to be called + * first. + */ +class GeoNodeLog { + public: + /** Warnings generated for that node. */ + Vector<NodeWarning> warnings; + /** + * Time spend in that node. For node groups this is the sum of the run times of the nodes + * inside. + */ + std::chrono::nanoseconds run_time{0}; + /** Maps from socket identifiers to their values. */ + Map<StringRefNull, ValueLog *> input_values_; + Map<StringRefNull, ValueLog *> output_values_; + /** Maps from attribute name to their usage flags. */ + Map<StringRefNull, NamedAttributeUsage> used_named_attributes; + /** Messages that are used for debugging purposes during development. */ + Vector<StringRefNull> debug_messages; + + GeoNodeLog(); + ~GeoNodeLog(); +}; + +class GeoModifierLog; + +/** + * Contains data that has been logged for a specific node group in a context. If the same node + * group is used multiple times, there will be a different #GeoTreeLog for every instance. + * + * This contains lazily evaluated data. Call the corresponding `ensure_*` methods before accessing + * data. + */ +class GeoTreeLog { + private: + GeoModifierLog *modifier_log_; + Vector<GeoTreeLogger *> tree_loggers_; + VectorSet<ComputeContextHash> children_hashes_; + bool reduced_node_warnings_ = false; + bool reduced_node_run_times_ = false; + bool reduced_socket_values_ = false; + bool reduced_viewer_node_logs_ = false; + bool reduced_existing_attributes_ = false; + bool reduced_used_named_attributes_ = false; + bool reduced_debug_messages_ = false; + + public: + Map<StringRefNull, GeoNodeLog> nodes; + Map<StringRefNull, ViewerNodeLog *, 0> viewer_node_logs; + Vector<NodeWarning> all_warnings; + std::chrono::nanoseconds run_time_sum{0}; + Vector<const GeometryAttributeInfo *> existing_attributes; + Map<StringRefNull, NamedAttributeUsage> used_named_attributes; + + GeoTreeLog(GeoModifierLog *modifier_log, Vector<GeoTreeLogger *> tree_loggers); + ~GeoTreeLog(); + + void ensure_node_warnings(); + void ensure_node_run_time(); + void ensure_socket_values(); + void ensure_viewer_node_logs(); + void ensure_existing_attributes(); + void ensure_used_named_attributes(); + void ensure_debug_messages(); + + ValueLog *find_socket_value_log(const bNodeSocket &query_socket); +}; + +/** + * There is one #GeoModifierLog for every modifier that evaluates geometry nodes. It contains all + * the loggers that are used during evaluation as well as the preprocessed logs that are used by UI + * code. + */ +class GeoModifierLog { + private: + /** Data that is stored for each thread. */ + struct LocalData { + /** Each thread has its own allocator. */ + LinearAllocator<> allocator; + /** + * Store a separate #GeoTreeLogger for each instance of the corresponding node group (e.g. + * when the same node group is used multiple times). + */ + Map<ComputeContextHash, destruct_ptr<GeoTreeLogger>> tree_logger_by_context; + }; + + /** Container for all thread-local data. */ + threading::EnumerableThreadSpecific<LocalData> data_per_thread_; + /** + * A #GeoTreeLog for every compute context. Those are created lazily when requested by UI code. + */ + Map<ComputeContextHash, std::unique_ptr<GeoTreeLog>> tree_logs_; + + public: + GeoModifierLog(); + ~GeoModifierLog(); + + /** + * Get a thread-local logger for the current node tree. + */ + GeoTreeLogger &get_local_tree_logger(const ComputeContext &compute_context); + + /** + * Get a log a specific node tree instance. + */ + GeoTreeLog &get_tree_log(const ComputeContextHash &compute_context_hash); + + /** + * Utility accessor to logged data. + */ + static GeoTreeLog *get_tree_log_for_node_editor(const SpaceNode &snode); + static const ViewerNodeLog *find_viewer_node_log_for_spreadsheet( + const SpaceSpreadsheet &sspreadsheet); +}; + +} // namespace blender::nodes::geo_eval_log diff --git a/source/blender/nodes/NOD_multi_function.hh b/source/blender/nodes/NOD_multi_function.hh index b6d51578b1c..676bf03927e 100644 --- a/source/blender/nodes/NOD_multi_function.hh +++ b/source/blender/nodes/NOD_multi_function.hh @@ -6,8 +6,6 @@ #include "DNA_node_types.h" -#include "NOD_derived_node_tree.hh" - namespace blender::nodes { using namespace fn::multi_function_types; @@ -19,15 +17,15 @@ class NodeMultiFunctions; */ class NodeMultiFunctionBuilder : NonCopyable, NonMovable { private: - bNode &node_; - bNodeTree &tree_; + const bNode &node_; + const bNodeTree &tree_; std::shared_ptr<MultiFunction> owned_built_fn_; const MultiFunction *built_fn_ = nullptr; friend NodeMultiFunctions; public: - NodeMultiFunctionBuilder(bNode &node, bNodeTree &tree); + NodeMultiFunctionBuilder(const bNode &node, const bNodeTree &tree); /** * Assign a multi-function for the current node. The input and output parameters of the function @@ -42,8 +40,8 @@ class NodeMultiFunctionBuilder : NonCopyable, NonMovable { */ template<typename T, typename... Args> void construct_and_set_matching_fn(Args &&...args); - bNode &node(); - bNodeTree &tree(); + const bNode &node(); + const bNodeTree &tree(); }; /** @@ -60,26 +58,26 @@ class NodeMultiFunctions { Map<const bNode *, Item> map_; public: - NodeMultiFunctions(const DerivedNodeTree &tree); + NodeMultiFunctions(const bNodeTree &tree); - const Item &try_get(const DNode &node) const; + const Item &try_get(const bNode &node) const; }; /* -------------------------------------------------------------------- */ /** \name #NodeMultiFunctionBuilder Inline Methods * \{ */ -inline NodeMultiFunctionBuilder::NodeMultiFunctionBuilder(bNode &node, bNodeTree &tree) +inline NodeMultiFunctionBuilder::NodeMultiFunctionBuilder(const bNode &node, const bNodeTree &tree) : node_(node), tree_(tree) { } -inline bNode &NodeMultiFunctionBuilder::node() +inline const bNode &NodeMultiFunctionBuilder::node() { return node_; } -inline bNodeTree &NodeMultiFunctionBuilder::tree() +inline const bNodeTree &NodeMultiFunctionBuilder::tree() { return tree_; } @@ -107,10 +105,10 @@ inline void NodeMultiFunctionBuilder::construct_and_set_matching_fn(Args &&...ar /** \name #NodeMultiFunctions Inline Methods * \{ */ -inline const NodeMultiFunctions::Item &NodeMultiFunctions::try_get(const DNode &node) const +inline const NodeMultiFunctions::Item &NodeMultiFunctions::try_get(const bNode &node) const { static Item empty_item; - const Item *item = map_.lookup_ptr(node->bnode()); + const Item *item = map_.lookup_ptr(&node); if (item == nullptr) { return empty_item; } diff --git a/source/blender/nodes/NOD_node_declaration.hh b/source/blender/nodes/NOD_node_declaration.hh index d8b8c354230..42755b2e8dd 100644 --- a/source/blender/nodes/NOD_node_declaration.hh +++ b/source/blender/nodes/NOD_node_declaration.hh @@ -92,6 +92,10 @@ class SocketDeclaration { * realtime_compositor::InputDescriptor for more information. */ int compositor_domain_priority_ = 0; + /** This input shouldn't be realized on the operation domain of the node. See + * realtime_compositor::InputDescriptor for more information. */ + bool compositor_skip_realization_ = false; + /** This input expects a single value and can't operate on non-single values. See * realtime_compositor::InputDescriptor for more information. */ bool compositor_expects_single_value_ = false; @@ -133,6 +137,7 @@ class SocketDeclaration { const OutputFieldDependency &output_field_dependency() const; int compositor_domain_priority() const; + bool compositor_skip_realization() const; bool compositor_expects_single_value() const; protected: @@ -257,6 +262,14 @@ class SocketDeclarationBuilder : public BaseSocketDeclarationBuilder { return *(Self *)this; } + /** This input shouldn't be realized on the operation domain of the node. See + * realtime_compositor::InputDescriptor for more information. */ + Self &compositor_skip_realization(bool value = true) + { + decl_->compositor_skip_realization_ = value; + return *(Self *)this; + } + /** This input expects a single value and can't operate on non-single values. See * realtime_compositor::InputDescriptor for more information. */ Self &compositor_expects_single_value(bool value = true) @@ -460,6 +473,11 @@ inline int SocketDeclaration::compositor_domain_priority() const return compositor_domain_priority_; } +inline bool SocketDeclaration::compositor_skip_realization() const +{ + return compositor_skip_realization_; +} + inline bool SocketDeclaration::compositor_expects_single_value() const { return compositor_expects_single_value_; diff --git a/source/blender/nodes/NOD_node_tree_ref.hh b/source/blender/nodes/NOD_node_tree_ref.hh deleted file mode 100644 index 257aa5f4110..00000000000 --- a/source/blender/nodes/NOD_node_tree_ref.hh +++ /dev/null @@ -1,760 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#pragma once - -/** \file - * \ingroup nodes - * - * NodeTreeRef makes querying information about a bNodeTree more efficient. It is an immutable data - * structure. It should not be used after anymore, after the underlying node tree changed. - * - * The following queries are supported efficiently: - * - socket -> index of socket - * - socket -> directly linked sockets - * - socket -> directly linked links - * - socket -> linked sockets when skipping reroutes - * - socket -> node - * - socket/node -> rna pointer - * - node -> inputs/outputs - * - node -> tree - * - tree -> all nodes - * - tree -> all (input/output) sockets - * - idname -> nodes - * - * Every socket has an id. The id-space is shared between input and output sockets. - * When storing data per socket, it is often better to use the id as index into an array, instead - * of a hash table. - * - * Every node has an id as well. The same rule regarding hash tables applies. - * - * There is an utility to export this data structure as graph in dot format. - */ - -#include "BLI_array.hh" -#include "BLI_function_ref.hh" -#include "BLI_linear_allocator.hh" -#include "BLI_map.hh" -#include "BLI_multi_value_map.hh" -#include "BLI_string_ref.hh" -#include "BLI_timeit.hh" -#include "BLI_utility_mixins.hh" -#include "BLI_vector.hh" - -#include "BKE_node.h" -#include "BKE_node_runtime.hh" - -#include "DNA_node_types.h" - -#include "RNA_access.h" - -namespace blender::nodes { - -class SocketRef; -class InputSocketRef; -class OutputSocketRef; -class NodeRef; -class NodeTreeRef; -class LinkRef; -class InternalLinkRef; - -using SocketIndexByIdentifierMap = Map<std::string, int>; - -class SocketRef : NonCopyable, NonMovable { - protected: - NodeRef *node_; - bNodeSocket *bsocket_; - bool is_input_; - int id_; - int index_; - Vector<LinkRef *> directly_linked_links_; - - /* These sockets are linked directly, i.e. with a single link in between. */ - MutableSpan<const SocketRef *> directly_linked_sockets_; - /* These sockets are linked when reroutes, muted links and muted nodes have been taken into - * account. */ - MutableSpan<const SocketRef *> logically_linked_sockets_; - /* These are the sockets that have been skipped when searching for logically linked sockets. - * That includes for example the input and output socket of an intermediate reroute node. */ - MutableSpan<const SocketRef *> logically_linked_skipped_sockets_; - - friend NodeTreeRef; - - public: - Span<const SocketRef *> logically_linked_sockets() const; - Span<const SocketRef *> logically_linked_skipped_sockets() const; - Span<const SocketRef *> directly_linked_sockets() const; - Span<const LinkRef *> directly_linked_links() const; - - bool is_directly_linked() const; - bool is_logically_linked() const; - - const NodeRef &node() const; - const NodeTreeRef &tree() const; - - int id() const; - int index() const; - - bool is_input() const; - bool is_output() const; - - const SocketRef &as_base() const; - const InputSocketRef &as_input() const; - const OutputSocketRef &as_output() const; - - PointerRNA rna() const; - - StringRefNull idname() const; - StringRefNull name() const; - StringRefNull identifier() const; - bNodeSocketType *typeinfo() const; - - bNodeSocket *bsocket() const; - bNode *bnode() const; - bNodeTree *btree() const; - - bool is_available() const; - bool is_undefined() const; - - void *default_value() const; - template<typename T> T *default_value() const; -}; - -class InputSocketRef final : public SocketRef { - public: - friend NodeTreeRef; - - Span<const OutputSocketRef *> logically_linked_sockets() const; - Span<const OutputSocketRef *> directly_linked_sockets() const; - - bool is_multi_input_socket() const; - - private: - void foreach_logical_origin(FunctionRef<void(const OutputSocketRef &)> origin_fn, - FunctionRef<void(const SocketRef &)> skipped_fn, - bool only_follow_first_input_link, - Vector<const InputSocketRef *> &seen_sockets_stack) const; -}; - -class OutputSocketRef final : public SocketRef { - public: - friend NodeTreeRef; - - Span<const InputSocketRef *> logically_linked_sockets() const; - Span<const InputSocketRef *> directly_linked_sockets() const; - - private: - void foreach_logical_target(FunctionRef<void(const InputSocketRef &)> target_fn, - FunctionRef<void(const SocketRef &)> skipped_fn, - Vector<const OutputSocketRef *> &seen_sockets_stack) const; -}; - -class NodeRef : NonCopyable, NonMovable { - private: - NodeTreeRef *tree_; - bNode *bnode_; - int id_; - Vector<InputSocketRef *> inputs_; - Vector<OutputSocketRef *> outputs_; - Vector<InternalLinkRef *> internal_links_; - SocketIndexByIdentifierMap *input_index_by_identifier_; - SocketIndexByIdentifierMap *output_index_by_identifier_; - - friend NodeTreeRef; - - public: - const NodeTreeRef &tree() const; - - Span<const InputSocketRef *> inputs() const; - Span<const OutputSocketRef *> outputs() const; - Span<const InternalLinkRef *> internal_links() const; - Span<const SocketRef *> sockets(eNodeSocketInOut in_out) const; - - const InputSocketRef &input(int index) const; - const OutputSocketRef &output(int index) const; - - const InputSocketRef &input_by_identifier(StringRef identifier) const; - const OutputSocketRef &output_by_identifier(StringRef identifier) const; - - bool any_input_is_directly_linked() const; - bool any_output_is_directly_linked() const; - bool any_socket_is_directly_linked(eNodeSocketInOut in_out) const; - - bNode *bnode() const; - bNodeTree *btree() const; - - PointerRNA rna() const; - StringRefNull idname() const; - StringRefNull name() const; - StringRefNull label() const; - StringRefNull label_or_name() const; - bNodeType *typeinfo() const; - const NodeDeclaration *declaration() const; - - int id() const; - - bool is_reroute_node() const; - bool is_group_node() const; - bool is_group_input_node() const; - bool is_group_output_node() const; - bool is_muted() const; - bool is_frame() const; - bool is_undefined() const; - - void *storage() const; - template<typename T> T *storage() const; -}; - -class LinkRef : NonCopyable, NonMovable { - private: - OutputSocketRef *from_; - InputSocketRef *to_; - bNodeLink *blink_; - - friend NodeTreeRef; - - public: - const OutputSocketRef &from() const; - const InputSocketRef &to() const; - - bNodeLink *blink() const; - - bool is_muted() const; -}; - -class InternalLinkRef : NonCopyable, NonMovable { - private: - InputSocketRef *from_; - OutputSocketRef *to_; - bNodeLink *blink_; - - friend NodeTreeRef; - - public: - const InputSocketRef &from() const; - const OutputSocketRef &to() const; - - bNodeLink *blink() const; -}; - -class NodeTreeRef : NonCopyable, NonMovable { - private: - LinearAllocator<> allocator_; - bNodeTree *btree_; - Vector<NodeRef *> nodes_by_id_; - Vector<SocketRef *> sockets_by_id_; - Vector<InputSocketRef *> input_sockets_; - Vector<OutputSocketRef *> output_sockets_; - Vector<LinkRef *> links_; - MultiValueMap<const bNodeType *, NodeRef *> nodes_by_type_; - Vector<std::unique_ptr<SocketIndexByIdentifierMap>> owned_identifier_maps_; - const NodeRef *group_output_node_ = nullptr; - - public: - NodeTreeRef(bNodeTree *btree); - ~NodeTreeRef(); - - Span<const NodeRef *> nodes() const; - Span<const NodeRef *> nodes_by_type(StringRefNull idname) const; - Span<const NodeRef *> nodes_by_type(const bNodeType *nodetype) const; - - Span<const SocketRef *> sockets() const; - Span<const InputSocketRef *> input_sockets() const; - Span<const OutputSocketRef *> output_sockets() const; - - Span<const LinkRef *> links() const; - - const NodeRef *find_node(const bNode &bnode) const; - - /** - * This is the active group output node if there are multiple. - */ - const NodeRef *group_output_node() const; - - /** - * \return True when there is a link cycle. Unavailable sockets are ignored. - */ - bool has_link_cycles() const; - bool has_undefined_nodes_or_sockets() const; - - enum class ToposortDirection { - LeftToRight, - RightToLeft, - }; - - struct ToposortResult { - Vector<const NodeRef *> sorted_nodes; - /** - * There can't be a correct topological sort of the nodes when there is a cycle. The nodes will - * still be sorted to some degree. The caller has to decide whether it can handle non-perfect - * sorts or not. - */ - bool has_cycle = false; - }; - - /** - * Sort nodes topologically from left to right or right to left. - * In the future the result if this could be cached on #NodeTreeRef. - */ - ToposortResult toposort(ToposortDirection direction) const; - - bNodeTree *btree() const; - StringRefNull name() const; - - std::string to_dot() const; - - private: - /* Utility functions used during construction. */ - InputSocketRef &find_input_socket(Map<bNode *, NodeRef *> &node_mapping, - bNode *bnode, - bNodeSocket *bsocket); - OutputSocketRef &find_output_socket(Map<bNode *, NodeRef *> &node_mapping, - bNode *bnode, - bNodeSocket *bsocket); - - void create_linked_socket_caches(); - void create_socket_identifier_maps(); -}; - -using NodeTreeRefMap = Map<bNodeTree *, std::unique_ptr<const NodeTreeRef>>; - -const NodeTreeRef &get_tree_ref_from_map(NodeTreeRefMap &node_tree_refs, bNodeTree &btree); - -namespace node_tree_ref_types { -using nodes::InputSocketRef; -using nodes::NodeRef; -using nodes::NodeTreeRef; -using nodes::NodeTreeRefMap; -using nodes::OutputSocketRef; -using nodes::SocketRef; -} // namespace node_tree_ref_types - -/* -------------------------------------------------------------------- */ -/** \name #SocketRef Inline Methods - * \{ */ - -inline Span<const SocketRef *> SocketRef::logically_linked_sockets() const -{ - return logically_linked_sockets_; -} - -inline Span<const SocketRef *> SocketRef::logically_linked_skipped_sockets() const -{ - return logically_linked_skipped_sockets_; -} - -inline Span<const SocketRef *> SocketRef::directly_linked_sockets() const -{ - return directly_linked_sockets_; -} - -inline Span<const LinkRef *> SocketRef::directly_linked_links() const -{ - return directly_linked_links_; -} - -inline bool SocketRef::is_directly_linked() const -{ - return directly_linked_sockets_.size() > 0; -} - -inline bool SocketRef::is_logically_linked() const -{ - return logically_linked_sockets_.size() > 0; -} - -inline const NodeRef &SocketRef::node() const -{ - return *node_; -} - -inline const NodeTreeRef &SocketRef::tree() const -{ - return node_->tree(); -} - -inline int SocketRef::id() const -{ - return id_; -} - -inline int SocketRef::index() const -{ - return index_; -} - -inline bool SocketRef::is_input() const -{ - return is_input_; -} - -inline bool SocketRef::is_output() const -{ - return !is_input_; -} - -inline const SocketRef &SocketRef::as_base() const -{ - return *this; -} - -inline const InputSocketRef &SocketRef::as_input() const -{ - BLI_assert(this->is_input()); - return static_cast<const InputSocketRef &>(*this); -} - -inline const OutputSocketRef &SocketRef::as_output() const -{ - BLI_assert(this->is_output()); - return static_cast<const OutputSocketRef &>(*this); -} - -inline StringRefNull SocketRef::idname() const -{ - return bsocket_->idname; -} - -inline StringRefNull SocketRef::name() const -{ - return bsocket_->name; -} - -inline StringRefNull SocketRef::identifier() const -{ - return bsocket_->identifier; -} - -inline bNodeSocketType *SocketRef::typeinfo() const -{ - return bsocket_->typeinfo; -} - -inline bNodeSocket *SocketRef::bsocket() const -{ - return bsocket_; -} - -inline bNode *SocketRef::bnode() const -{ - return node_->bnode(); -} - -inline bNodeTree *SocketRef::btree() const -{ - return node_->btree(); -} - -inline bool SocketRef::is_available() const -{ - return (bsocket_->flag & SOCK_UNAVAIL) == 0; -} - -inline bool SocketRef::is_undefined() const -{ - return bsocket_->typeinfo == &NodeSocketTypeUndefined; -} - -inline void *SocketRef::default_value() const -{ - return bsocket_->default_value; -} - -template<typename T> inline T *SocketRef::default_value() const -{ - return (T *)bsocket_->default_value; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name #InputSocketRef Inline Methods - * \{ */ - -inline Span<const OutputSocketRef *> InputSocketRef::logically_linked_sockets() const -{ - return logically_linked_sockets_.as_span().cast<const OutputSocketRef *>(); -} - -inline Span<const OutputSocketRef *> InputSocketRef::directly_linked_sockets() const -{ - return directly_linked_sockets_.cast<const OutputSocketRef *>(); -} - -inline bool InputSocketRef::is_multi_input_socket() const -{ - return bsocket_->flag & SOCK_MULTI_INPUT; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name #OutputSocketRef Inline Methods - * \{ */ - -inline Span<const InputSocketRef *> OutputSocketRef::logically_linked_sockets() const -{ - return logically_linked_sockets_.as_span().cast<const InputSocketRef *>(); -} - -inline Span<const InputSocketRef *> OutputSocketRef::directly_linked_sockets() const -{ - return directly_linked_sockets_.cast<const InputSocketRef *>(); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name #NodeRef Inline Methods - * \{ */ - -inline const NodeTreeRef &NodeRef::tree() const -{ - return *tree_; -} - -inline Span<const InputSocketRef *> NodeRef::inputs() const -{ - return inputs_; -} - -inline Span<const OutputSocketRef *> NodeRef::outputs() const -{ - return outputs_; -} - -inline Span<const SocketRef *> NodeRef::sockets(const eNodeSocketInOut in_out) const -{ - return in_out == SOCK_IN ? inputs_.as_span().cast<const SocketRef *>() : - outputs_.as_span().cast<const SocketRef *>(); -} - -inline Span<const InternalLinkRef *> NodeRef::internal_links() const -{ - return internal_links_; -} - -inline const InputSocketRef &NodeRef::input(int index) const -{ - return *inputs_[index]; -} - -inline const OutputSocketRef &NodeRef::output(int index) const -{ - return *outputs_[index]; -} - -inline const InputSocketRef &NodeRef::input_by_identifier(StringRef identifier) const -{ - const int index = input_index_by_identifier_->lookup_as(identifier); - return this->input(index); -} - -inline const OutputSocketRef &NodeRef::output_by_identifier(StringRef identifier) const -{ - const int index = output_index_by_identifier_->lookup_as(identifier); - return this->output(index); -} - -inline bNode *NodeRef::bnode() const -{ - return bnode_; -} - -inline bNodeTree *NodeRef::btree() const -{ - return tree_->btree(); -} - -inline StringRefNull NodeRef::idname() const -{ - return bnode_->idname; -} - -inline StringRefNull NodeRef::name() const -{ - return bnode_->name; -} - -inline StringRefNull NodeRef::label() const -{ - return bnode_->label; -} - -inline StringRefNull NodeRef::label_or_name() const -{ - const StringRefNull label = this->label(); - if (!label.is_empty()) { - return label; - } - return this->name(); -} - -inline bNodeType *NodeRef::typeinfo() const -{ - return bnode_->typeinfo; -} - -/* Returns a pointer because not all nodes have declarations currently. */ -inline const NodeDeclaration *NodeRef::declaration() const -{ - nodeDeclarationEnsure(this->tree().btree(), bnode_); - return bnode_->runtime->declaration; -} - -inline int NodeRef::id() const -{ - return id_; -} - -inline bool NodeRef::is_reroute_node() const -{ - return bnode_->type == NODE_REROUTE; -} - -inline bool NodeRef::is_group_node() const -{ - return bnode_->type == NODE_GROUP || bnode_->type == NODE_CUSTOM_GROUP; -} - -inline bool NodeRef::is_group_input_node() const -{ - return bnode_->type == NODE_GROUP_INPUT; -} - -inline bool NodeRef::is_group_output_node() const -{ - return bnode_->type == NODE_GROUP_OUTPUT; -} - -inline bool NodeRef::is_frame() const -{ - return bnode_->type == NODE_FRAME; -} - -inline bool NodeRef::is_undefined() const -{ - return bnode_->typeinfo == &NodeTypeUndefined; -} - -inline bool NodeRef::is_muted() const -{ - return (bnode_->flag & NODE_MUTED) != 0; -} - -inline void *NodeRef::storage() const -{ - return bnode_->storage; -} - -template<typename T> inline T *NodeRef::storage() const -{ - return (T *)bnode_->storage; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name #LinkRef Inline Methods - * \{ */ - -inline const OutputSocketRef &LinkRef::from() const -{ - return *from_; -} - -inline const InputSocketRef &LinkRef::to() const -{ - return *to_; -} - -inline bNodeLink *LinkRef::blink() const -{ - return blink_; -} - -inline bool LinkRef::is_muted() const -{ - return blink_->flag & NODE_LINK_MUTED; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name #InternalLinkRef Inline Methods - * \{ */ - -inline const InputSocketRef &InternalLinkRef::from() const -{ - return *from_; -} - -inline const OutputSocketRef &InternalLinkRef::to() const -{ - return *to_; -} - -inline bNodeLink *InternalLinkRef::blink() const -{ - return blink_; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name #NodeTreeRef Inline Methods - * \{ */ - -inline Span<const NodeRef *> NodeTreeRef::nodes() const -{ - return nodes_by_id_; -} - -inline Span<const NodeRef *> NodeTreeRef::nodes_by_type(StringRefNull idname) const -{ - const bNodeType *nodetype = nodeTypeFind(idname.c_str()); - return this->nodes_by_type(nodetype); -} - -inline Span<const NodeRef *> NodeTreeRef::nodes_by_type(const bNodeType *nodetype) const -{ - return nodes_by_type_.lookup(nodetype); -} - -inline Span<const SocketRef *> NodeTreeRef::sockets() const -{ - return sockets_by_id_; -} - -inline Span<const InputSocketRef *> NodeTreeRef::input_sockets() const -{ - return input_sockets_; -} - -inline Span<const OutputSocketRef *> NodeTreeRef::output_sockets() const -{ - return output_sockets_; -} - -inline Span<const LinkRef *> NodeTreeRef::links() const -{ - return links_; -} - -inline const NodeRef *NodeTreeRef::group_output_node() const -{ - return group_output_node_; -} - -inline bNodeTree *NodeTreeRef::btree() const -{ - return btree_; -} - -inline StringRefNull NodeTreeRef::name() const -{ - return btree_->id.name + 2; -} - -/** \} */ - -} // namespace blender::nodes diff --git a/source/blender/nodes/NOD_shader.h b/source/blender/nodes/NOD_shader.h index 1d1310360b8..8fe77bffaad 100644 --- a/source/blender/nodes/NOD_shader.h +++ b/source/blender/nodes/NOD_shader.h @@ -26,6 +26,7 @@ void register_node_type_sh_camera(void); void register_node_type_sh_value(void); void register_node_type_sh_rgb(void); void register_node_type_sh_mix_rgb(void); +void register_node_type_sh_mix(void); void register_node_type_sh_valtorgb(void); void register_node_type_sh_rgbtobw(void); void register_node_type_sh_shadertorgb(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 786ce88152e..8f2a4adcf9a 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -25,7 +25,7 @@ DefNode(Node, NODE_REROUTE, 0, "REROUT DefNode(ShaderNode, SH_NODE_RGB, 0, "RGB", RGB, "RGB", "A color picker") DefNode(ShaderNode, SH_NODE_VALUE, 0, "VALUE", Value, "Value", "Used to Input numerical values to other nodes in the tree") -DefNode(ShaderNode, SH_NODE_MIX_RGB, def_mix_rgb, "MIX_RGB", MixRGB, "MixRGB", "Mix two input colors") +DefNode(ShaderNode, SH_NODE_MIX_RGB_LEGACY, def_mix_rgb, "MIX_RGB", MixRGB, "MixRGB", "Mix two input colors") DefNode(ShaderNode, SH_NODE_VALTORGB, def_colorramp, "VALTORGB", ValToRGB, "ColorRamp", "Map values to colors with the use of a gradient") DefNode(ShaderNode, SH_NODE_RGBTOBW, 0, "RGBTOBW", RGBToBW, "RGB to BW", "Convert a color's luminance to a grayscale value") DefNode(ShaderNode, SH_NODE_SHADERTORGB, 0, "SHADERTORGB", ShaderToRGB, "Shader to RGB", "Convert rendering effect (such as light and shadow) to color. Typically used for non-photorealistic rendering, to apply additional effects on the output of BSDFs.\nNote: only supported for Eevee") @@ -122,6 +122,7 @@ DefNode(ShaderNode, SH_NODE_OUTPUT_AOV, def_sh_output_aov, "OUT DefNode(ShaderNode, SH_NODE_CURVE_FLOAT, def_float_curve, "CURVE_FLOAT", FloatCurve, "Float Curve", "Map an input float to a curve and outputs a float value") DefNode(ShaderNode, SH_NODE_COMBINE_COLOR, def_sh_combsep_color, "COMBINE_COLOR", CombineColor, "Combine Color", "Create a color from individual components using multiple models") DefNode(ShaderNode, SH_NODE_SEPARATE_COLOR, def_sh_combsep_color, "SEPARATE_COLOR", SeparateColor, "Separate Color", "Split a color into its individual components using multiple models") +DefNode(ShaderNode, SH_NODE_MIX, def_sh_mix, "MIX", Mix, "Mix", "Mix values by a factor") DefNode(CompositorNode, CMP_NODE_VIEWER, def_cmp_viewer, "VIEWER", Viewer, "Viewer", "" ) DefNode(CompositorNode, CMP_NODE_RGB, 0, "RGB", RGB, "RGB", "" ) @@ -304,6 +305,7 @@ DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, " DefNode(GeometryNode, GEO_NODE_CURVE_TO_POINTS, def_geo_curve_to_points, "CURVE_TO_POINTS", CurveToPoints, "Curve to Points", "Generate a point cloud by sampling positions along curves") DefNode(GeometryNode, GEO_NODE_DEFORM_CURVES_ON_SURFACE, 0, "DEFORM_CURVES_ON_SURFACE", DeformCurvesOnSurface, "Deform Curves on Surface", "Translate and rotate curves based on changes between the object's original and evaluated surface mesh") DefNode(GeometryNode, GEO_NODE_DELETE_GEOMETRY, def_geo_delete_geometry, "DELETE_GEOMETRY", DeleteGeometry, "Delete Geometry", "Remove selected elements of a geometry") +DefNode(GeometryNode, GEO_NODE_DISTRIBUTE_POINTS_IN_VOLUME, def_geo_distribute_points_in_volume, "DISTRIBUTE_POINTS_IN_VOLUME", DistributePointsInVolume, "Distribute Points In Volume", "Generate points inside a volume") DefNode(GeometryNode, GEO_NODE_DISTRIBUTE_POINTS_ON_FACES, def_geo_distribute_points_on_faces, "DISTRIBUTE_POINTS_ON_FACES", DistributePointsOnFaces, "Distribute Points on Faces", "Generate points spread out on the surface of a mesh") DefNode(GeometryNode, GEO_NODE_DUAL_MESH, 0, "DUAL_MESH", DualMesh, "Dual Mesh", "Convert Faces into vertices and vertices into faces") DefNode(GeometryNode, GEO_NODE_DUPLICATE_ELEMENTS, def_geo_duplicate_elements, "DUPLICATE_ELEMENTS", DuplicateElements, "Duplicate Elements", "Generate an arbitrary number copies of each selected input element") @@ -328,7 +330,7 @@ DefNode(GeometryNode, GEO_NODE_INPUT_MESH_EDGE_ANGLE, 0, "MESH_EDGE_ANGLE", Inpu DefNode(GeometryNode, GEO_NODE_INPUT_MESH_EDGE_NEIGHBORS, 0, "MESH_EDGE_NEIGHBORS",InputMeshEdgeNeighbors, "Edge Neighbors", "Retrieve the number of faces that use each edge as one of their sides") DefNode(GeometryNode, GEO_NODE_INPUT_MESH_EDGE_VERTICES, 0, "MESH_EDGE_VERTICES", InputMeshEdgeVertices, "Edge Vertices", "Retrieve topology information relating to each edge of a mesh") DefNode(GeometryNode, GEO_NODE_INPUT_MESH_FACE_AREA, 0, "MESH_FACE_AREA", InputMeshFaceArea, "Face Area", "Calculate the surface area of a mesh's faces") -DefNode(GeometryNode, GEO_NODE_INPUT_MESH_FACE_IS_PLANAR, 0, "MESH_FACE_IS_PLANAR",InputMeshFaceIsPlanar, "Face is Planar", "Retrieve whether all triangles in a face are on the same plane, i.e. whether have the same normal") +DefNode(GeometryNode, GEO_NODE_INPUT_MESH_FACE_IS_PLANAR, 0, "MESH_FACE_IS_PLANAR",InputMeshFaceIsPlanar, "Is Face Planar", "Retrieve whether all triangles in a face are on the same plane, i.e. whether have the same normal") DefNode(GeometryNode, GEO_NODE_INPUT_MESH_FACE_NEIGHBORS, 0, "MESH_FACE_NEIGHBORS",InputMeshFaceNeighbors, "Face Neighbors", "Retrieve topology information relating to each face of a mesh") DefNode(GeometryNode, GEO_NODE_INPUT_MESH_ISLAND, 0, "MESH_ISLAND", InputMeshIsland, "Mesh Island", "Retrieve information about separate connected regions in a mesh") DefNode(GeometryNode, GEO_NODE_INPUT_MESH_VERTEX_NEIGHBORS, 0, "MESH_VERTEX_NEIGHBORS", InputMeshVertexNeighbors, "Vertex Neighbors", "Retrieve topology information relating to each vertex of a mesh") @@ -351,6 +353,7 @@ DefNode(GeometryNode, GEO_NODE_JOIN_GEOMETRY, 0, "JOIN_GEOMETRY", JoinGeometry, DefNode(GeometryNode, GEO_NODE_MATERIAL_SELECTION, 0, "MATERIAL_SELECTION", MaterialSelection, "Material Selection", "Provide a selection of faces that use the specified material") DefNode(GeometryNode, GEO_NODE_MERGE_BY_DISTANCE, def_geo_merge_by_distance,"MERGE_BY_DISTANCE", MergeByDistance, "Merge by Distance", "Merge vertices or points within a given distance") DefNode(GeometryNode, GEO_NODE_MESH_BOOLEAN, def_geo_boolean, "MESH_BOOLEAN", MeshBoolean, "Mesh Boolean", "Cut, subtract, or join multiple mesh inputs") +DefNode(GeometryNode, GEO_NODE_MESH_FACE_SET_BOUNDARIES, 0, "MESH_FACE_SET_BOUNDARIES", MeshFaceSetBoundaries, "Face Set Boundaries", "Find edges on the boundaries between face sets") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CIRCLE, def_geo_mesh_circle, "MESH_PRIMITIVE_CIRCLE", MeshCircle, "Mesh Circle", "Generate a circular ring of edges") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CONE, def_geo_mesh_cone, "MESH_PRIMITIVE_CONE",MeshCone, "Cone", "Generate a cone mesh") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CUBE, 0, "MESH_PRIMITIVE_CUBE",MeshCube, "Cube", "Generate a cuboid mesh with variable side lengths and subdivisions") @@ -361,7 +364,7 @@ DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_LINE, def_geo_mesh_line, "MESH_PRI DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, 0, "MESH_PRIMITIVE_UV_SPHERE", MeshUVSphere, "UV Sphere", "Generate a spherical mesh with quads, except for triangles at the top and bottom") DefNode(GeometryNode, GEO_NODE_MESH_TO_CURVE, 0, "MESH_TO_CURVE", MeshToCurve, "Mesh to Curve", "Generate a curve from a mesh") DefNode(GeometryNode, GEO_NODE_MESH_TO_POINTS, def_geo_mesh_to_points, "MESH_TO_POINTS", MeshToPoints, "Mesh to Points", "Generate a point cloud from a mesh's vertices") -DefNode(GeometryNode, GEO_NODE_MESH_TO_VOLUME, def_geo_mesh_to_volume, "MESH_TO_VOLUME", MeshToVolume, "Mesh To Volume", "Create a fog volume with the shape of the input mesh's surface") +DefNode(GeometryNode, GEO_NODE_MESH_TO_VOLUME, def_geo_mesh_to_volume, "MESH_TO_VOLUME", MeshToVolume, "Mesh to Volume", "Create a fog volume with the shape of the input mesh's surface") DefNode(GeometryNode, GEO_NODE_OBJECT_INFO, def_geo_object_info, "OBJECT_INFO", ObjectInfo, "Object Info", "Retrieve information from an object") DefNode(GeometryNode, GEO_NODE_POINTS_TO_VERTICES, 0, "POINTS_TO_VERTICES", PointsToVertices, "Points to Vertices", "Generate a mesh vertex for each point cloud point") DefNode(GeometryNode, GEO_NODE_POINTS_TO_VOLUME, def_geo_points_to_volume, "POINTS_TO_VOLUME", PointsToVolume, "Points to Volume", "Generate a fog volume sphere around every point") @@ -375,10 +378,14 @@ DefNode(GeometryNode, GEO_NODE_RESAMPLE_CURVE, def_geo_curve_resample, "RESAMPLE DefNode(GeometryNode, GEO_NODE_REVERSE_CURVE, 0, "REVERSE_CURVE", ReverseCurve, "Reverse Curve", "Swap the start and end of splines") DefNode(GeometryNode, GEO_NODE_ROTATE_INSTANCES, 0, "ROTATE_INSTANCES", RotateInstances, "Rotate Instances", "Rotate geometry instances in local or global space") DefNode(GeometryNode, GEO_NODE_SAMPLE_CURVE, def_geo_curve_sample, "SAMPLE_CURVE", SampleCurve, "Sample Curve", "Retrieve data from a point on a curve at a certain distance from its start") +DefNode(GeometryNode, GEO_NODE_SAMPLE_INDEX, def_geo_sample_index, "SAMPLE_INDEX", SampleIndex, "Sample Index", "Retrieve values from specific geometry elements") +DefNode(GeometryNode, GEO_NODE_SAMPLE_NEAREST_SURFACE, def_geo_sample_nearest_surface, "sample_nearest_surface", SampleNearestSurface, "Sample Nearest Surface", "Calculate the interpolated value of a mesh attribute on the closest point of its surface") +DefNode(GeometryNode, GEO_NODE_SAMPLE_NEAREST, def_geo_sample_nearest, "SAMPLE_NEAREST", SampleNearest, "Sample Nearest", "Find the element of a geometry closest to a position") DefNode(GeometryNode, GEO_NODE_SCALE_ELEMENTS, def_geo_scale_elements, "SCALE_ELEMENTS", ScaleElements, "Scale Elements", "Scale groups of connected edges and faces") DefNode(GeometryNode, GEO_NODE_SCALE_INSTANCES, 0, "SCALE_INSTANCES", ScaleInstances, "Scale Instances", "Scale geometry instances in local or global space") DefNode(GeometryNode, GEO_NODE_SEPARATE_COMPONENTS, 0, "SEPARATE_COMPONENTS",SeparateComponents, "Separate Components","Split a geometry into a separate output for each type of data in the geometry") DefNode(GeometryNode, GEO_NODE_SEPARATE_GEOMETRY, def_geo_separate_geometry,"SEPARATE_GEOMETRY", SeparateGeometry, "Separate Geometry", "Split a geometry into two geometry outputs based on a selection") +DefNode(GeometryNode, GEO_NODE_SELF_OBJECT, 0, "SELF_OBJECT", SelfObject, "Self Object", "Retrieve the object that contains the geometry nodes modifier currently being executed") DefNode(GeometryNode, GEO_NODE_SET_CURVE_HANDLES, def_geo_curve_set_handle_positions, "SET_CURVE_HANDLES", SetCurveHandlePositions, "Set Handle Positions", "Set the positions for the handles of Bézier curves") DefNode(GeometryNode, GEO_NODE_SET_CURVE_RADIUS, 0, "SET_CURVE_RADIUS", SetCurveRadius, "Set Curve Radius", "Set the radius of the curve at each control point") DefNode(GeometryNode, GEO_NODE_SET_CURVE_TILT, 0, "SET_CURVE_TILT", SetCurveTilt, "Set Curve Tilt", "Set the tilt angle at each curve control point") @@ -398,7 +405,6 @@ DefNode(GeometryNode, GEO_NODE_SUBDIVIDE_CURVE, 0, "SUBDIVIDE_CURVE", SubdivideC DefNode(GeometryNode, GEO_NODE_SUBDIVIDE_MESH, 0, "SUBDIVIDE_MESH", SubdivideMesh, "Subdivide Mesh", "Divide mesh faces into smaller ones without changing the shape or volume, using linear interpolation to place the new vertices") DefNode(GeometryNode, GEO_NODE_SUBDIVISION_SURFACE, def_geo_subdivision_surface, "SUBDIVISION_SURFACE",SubdivisionSurface, "Subdivision Surface","Divide mesh faces to form a smooth surface, using the Catmull-Clark subdivision method") DefNode(GeometryNode, GEO_NODE_SWITCH, def_geo_switch, "SWITCH", Switch, "Switch", "Switch between two inputs") -DefNode(GeometryNode, GEO_NODE_TRANSFER_ATTRIBUTE, def_geo_transfer_attribute, "ATTRIBUTE_TRANSFER", AttributeTransfer, "Transfer Attribute", "Retrieve values from a source geometry and provides them as a field by interpolating them with the context geometry") DefNode(GeometryNode, GEO_NODE_TRANSFORM, 0, "TRANSFORM", Transform, "Transform", "Translate, rotate or scale the geometry") DefNode(GeometryNode, GEO_NODE_TRANSLATE_INSTANCES, 0, "TRANSLATE_INSTANCES",TranslateInstances, "Translate Instances","Move top-level geometry instances in local or global space") DefNode(GeometryNode, GEO_NODE_TRIANGULATE, def_geo_triangulate, "TRIANGULATE", Triangulate, "Triangulate", "Convert all faces in a mesh to triangular faces") diff --git a/source/blender/nodes/composite/nodes/node_composite_alpha_over.cc b/source/blender/nodes/composite/nodes/node_composite_alpha_over.cc index 64c59eb24e3..12f81da3d1c 100644 --- a/source/blender/nodes/composite/nodes/node_composite_alpha_over.cc +++ b/source/blender/nodes/composite/nodes/node_composite_alpha_over.cc @@ -18,6 +18,8 @@ namespace blender::nodes::node_composite_alpha_over_cc { +NODE_STORAGE_FUNCS(NodeTwoFloats) + static void cmp_node_alphaover_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Float>(N_("Fac")) @@ -86,7 +88,7 @@ class AlphaOverShaderNode : public ShaderNode { float get_premultiply_factor() { - return ((NodeTwoFloats *)bnode().storage)->x; + return node_storage(bnode()).x; } }; diff --git a/source/blender/nodes/composite/nodes/node_composite_bilateralblur.cc b/source/blender/nodes/composite/nodes/node_composite_bilateralblur.cc index 66a321eb088..ac9a6c89aa4 100644 --- a/source/blender/nodes/composite/nodes/node_composite_bilateralblur.cc +++ b/source/blender/nodes/composite/nodes/node_composite_bilateralblur.cc @@ -5,10 +5,15 @@ * \ingroup cmpnodes */ +#include "BLI_math_base.hh" + #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_shader.h" + #include "COM_node_operation.hh" +#include "COM_utilities.hh" #include "node_composite_util.hh" @@ -16,10 +21,16 @@ namespace blender::nodes::node_composite_bilateralblur_cc { +NODE_STORAGE_FUNCS(NodeBilateralBlurData) + static void cmp_node_bilateralblur_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input<decl::Color>(N_("Determinator")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); + b.add_input<decl::Color>(N_("Determinator")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(1); b.add_output<decl::Color>(N_("Image")); } @@ -52,7 +63,45 @@ class BilateralBlurOperation : public NodeOperation { void execute() override { - get_input("Image").pass_through(get_result("Image")); + const Result &input_image = get_input("Image"); + /* Single value inputs can't be blurred and are returned as is. */ + if (input_image.is_single_value()) { + get_input("Image").pass_through(get_result("Image")); + return; + } + + GPUShader *shader = shader_manager().get("compositor_bilateral_blur"); + GPU_shader_bind(shader); + + GPU_shader_uniform_1i(shader, "radius", get_blur_radius()); + GPU_shader_uniform_1f(shader, "threshold", get_threshold()); + + input_image.bind_as_texture(shader, "input_tx"); + + const Result &determinator_image = get_input("Determinator"); + determinator_image.bind_as_texture(shader, "determinator_tx"); + + const Domain domain = compute_domain(); + Result &output_image = get_result("Image"); + output_image.allocate_texture(domain); + output_image.bind_as_image(shader, "output_img"); + + compute_dispatch_threads_at_least(shader, domain.size); + + GPU_shader_unbind(); + output_image.unbind_as_image(); + input_image.unbind_as_texture(); + determinator_image.unbind_as_texture(); + } + + int get_blur_radius() + { + return math::ceil(node_storage(bnode()).iter + node_storage(bnode()).sigma_space); + } + + float get_threshold() + { + return node_storage(bnode()).sigma_color; } }; diff --git a/source/blender/nodes/composite/nodes/node_composite_blur.cc b/source/blender/nodes/composite/nodes/node_composite_blur.cc index cb1d93fe10b..630f18361e3 100644 --- a/source/blender/nodes/composite/nodes/node_composite_blur.cc +++ b/source/blender/nodes/composite/nodes/node_composite_blur.cc @@ -5,12 +5,27 @@ * \ingroup cmpnodes */ +#include <cstdint> + +#include "BLI_array.hh" +#include "BLI_assert.h" +#include "BLI_index_range.hh" +#include "BLI_math_base.hh" +#include "BLI_math_vec_types.hh" +#include "BLI_math_vector.hh" + #include "RNA_access.h" #include "UI_interface.h" #include "UI_resources.h" +#include "RE_pipeline.h" + +#include "GPU_state.h" +#include "GPU_texture.h" + #include "COM_node_operation.hh" +#include "COM_utilities.hh" #include "node_composite_util.hh" @@ -18,6 +33,8 @@ namespace blender::nodes::node_composite_blur_cc { +NODE_STORAGE_FUNCS(NodeBlurData) + static void cmp_node_blur_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); @@ -75,13 +92,395 @@ static void node_composit_buts_blur(uiLayout *layout, bContext *UNUSED(C), Point using namespace blender::realtime_compositor; +/* A helper class that computes and caches a 1D GPU texture containing the weights of the separable + * filter of the given type and radius. The filter is assumed to be symmetric, because the filter + * functions are all even functions. Consequently, only the positive half of the filter is computed + * and the shader takes that into consideration. */ +class SymmetricSeparableBlurWeights { + private: + float radius_ = 1.0f; + int type_ = R_FILTER_GAUSS; + GPUTexture *texture_ = nullptr; + + public: + ~SymmetricSeparableBlurWeights() + { + if (texture_) { + GPU_texture_free(texture_); + } + } + + /* Check if a texture containing the weights was already computed for the given filter type and + * radius. If such texture exists, do nothing, otherwise, free the already computed texture and + * recompute it with the given filter type and radius. */ + void update(float radius, int type) + { + if (texture_ && type == type_ && radius == radius_) { + return; + } + + if (texture_) { + GPU_texture_free(texture_); + } + + /* The size of filter is double the radius plus 1, but since the filter is symmetric, we only + * compute half of it and no doubling happens. We add 1 to make sure the filter size is always + * odd and there is a center weight. */ + const int size = math::ceil(radius) + 1; + Array<float> weights(size); + + float sum = 0.0f; + + /* First, compute the center weight. */ + const float center_weight = RE_filter_value(type, 0.0f); + weights[0] = center_weight; + sum += center_weight; + + /* Second, compute the other weights in the positive direction, making sure to add double the + * weight to the sum of weights because the filter is symmetric and we only loop over half of + * it. Skip the center weight already computed by dropping the front index. */ + const float scale = radius > 0.0f ? 1.0f / radius : 0.0f; + for (const int i : weights.index_range().drop_front(1)) { + const float weight = RE_filter_value(type, i * scale); + weights[i] = weight; + sum += weight * 2.0f; + } + + /* Finally, normalize the weights. */ + for (const int i : weights.index_range()) { + weights[i] /= sum; + } + + texture_ = GPU_texture_create_1d("Weights", size, 1, GPU_R16F, weights.data()); + + type_ = type; + radius_ = radius; + } + + void bind_as_texture(GPUShader *shader, const char *texture_name) + { + const int texture_image_unit = GPU_shader_get_texture_binding(shader, texture_name); + GPU_texture_bind(texture_, texture_image_unit); + } + + void unbind_as_texture() + { + GPU_texture_unbind(texture_); + } +}; + +/* A helper class that computes and caches a 2D GPU texture containing the weights of the filter of + * the given type and radius. The filter is assumed to be symmetric, because the filter functions + * are evaluated on the normalized distance to the center. Consequently, only the upper right + * quadrant are computed and the shader takes that into consideration. */ +class SymmetricBlurWeights { + private: + int type_ = R_FILTER_GAUSS; + float2 radius_ = float2(1.0f); + GPUTexture *texture_ = nullptr; + + public: + ~SymmetricBlurWeights() + { + if (texture_) { + GPU_texture_free(texture_); + } + } + + /* Check if a texture containing the weights was already computed for the given filter type and + * radius. If such texture exists, do nothing, otherwise, free the already computed texture and + * recompute it with the given filter type and radius. */ + void update(float2 radius, int type) + { + if (texture_ && type == type_ && radius == radius_) { + return; + } + + if (texture_) { + GPU_texture_free(texture_); + } + + /* The full size of filter is double the radius plus 1, but since the filter is symmetric, we + * only compute a single quadrant of it and so no doubling happens. We add 1 to make sure the + * filter size is always odd and there is a center weight. */ + const float2 scale = math::safe_divide(float2(1.0f), radius); + const int2 size = int2(math::ceil(radius)) + int2(1); + Array<float> weights(size.x * size.y); + + float sum = 0.0f; + + /* First, compute the center weight. */ + const float center_weight = RE_filter_value(type, 0.0f); + weights[0] = center_weight; + sum += center_weight; + + /* Then, compute the weights along the positive x axis, making sure to add double the weight to + * the sum of weights because the filter is symmetric and we only loop over the positive half + * of the x axis. Skip the center weight already computed by dropping the front index. */ + for (const int x : IndexRange(size.x).drop_front(1)) { + const float weight = RE_filter_value(type, x * scale.x); + weights[x] = weight; + sum += weight * 2.0f; + } + + /* Then, compute the weights along the positive y axis, making sure to add double the weight to + * the sum of weights because the filter is symmetric and we only loop over the positive half + * of the y axis. Skip the center weight already computed by dropping the front index. */ + for (const int y : IndexRange(size.y).drop_front(1)) { + const float weight = RE_filter_value(type, y * scale.y); + weights[size.x * y] = weight; + sum += weight * 2.0f; + } + + /* Then, compute the other weights in the upper right quadrant, making sure to add quadruple + * the weight to the sum of weights because the filter is symmetric and we only loop over one + * quadrant of it. Skip the weights along the y and x axis already computed by dropping the + * front index. */ + for (const int y : IndexRange(size.y).drop_front(1)) { + for (const int x : IndexRange(size.x).drop_front(1)) { + const float weight = RE_filter_value(type, math::length(float2(x, y) * scale)); + weights[size.x * y + x] = weight; + sum += weight * 4.0f; + } + } + + /* Finally, normalize the weights. */ + for (const int y : IndexRange(size.y)) { + for (const int x : IndexRange(size.x)) { + weights[size.x * y + x] /= sum; + } + } + + texture_ = GPU_texture_create_2d("Weights", size.x, size.y, 1, GPU_R16F, weights.data()); + + type_ = type; + radius_ = radius; + } + + void bind_as_texture(GPUShader *shader, const char *texture_name) + { + const int texture_image_unit = GPU_shader_get_texture_binding(shader, texture_name); + GPU_texture_bind(texture_, texture_image_unit); + } + + void unbind_as_texture() + { + GPU_texture_unbind(texture_); + } +}; + class BlurOperation : public NodeOperation { + private: + /* Cached symmetric blur weights. */ + SymmetricBlurWeights blur_weights_; + /* Cached symmetric blur weights for the separable horizontal pass. */ + SymmetricSeparableBlurWeights blur_horizontal_weights_; + /* Cached symmetric blur weights for the separable vertical pass. */ + SymmetricSeparableBlurWeights blur_vertical_weights_; + public: using NodeOperation::NodeOperation; void execute() override { - get_input("Image").pass_through(get_result("Image")); + if (is_identity()) { + get_input("Image").pass_through(get_result("Image")); + return; + } + + if (use_separable_filter()) { + GPUTexture *horizontal_pass_result = execute_separable_blur_horizontal_pass(); + execute_separable_blur_vertical_pass(horizontal_pass_result); + } + else { + execute_blur(); + } + } + + void execute_blur() + { + GPUShader *shader = shader_manager().get("compositor_symmetric_blur"); + GPU_shader_bind(shader); + + GPU_shader_uniform_1b(shader, "extend_bounds", get_extend_bounds()); + GPU_shader_uniform_1b(shader, "gamma_correct", node_storage(bnode()).gamma); + + const Result &input_image = get_input("Image"); + input_image.bind_as_texture(shader, "input_tx"); + + blur_weights_.update(compute_blur_radius(), node_storage(bnode()).filtertype); + blur_weights_.bind_as_texture(shader, "weights_tx"); + + Domain domain = compute_domain(); + if (get_extend_bounds()) { + /* Add a radius amount of pixels in both sides of the image, hence the multiply by 2. */ + domain.size += int2(math::ceil(compute_blur_radius())) * 2; + } + + Result &output_image = get_result("Image"); + output_image.allocate_texture(domain); + output_image.bind_as_image(shader, "output_img"); + + compute_dispatch_threads_at_least(shader, domain.size); + + GPU_shader_unbind(); + output_image.unbind_as_image(); + input_image.unbind_as_texture(); + blur_weights_.unbind_as_texture(); + } + + GPUTexture *execute_separable_blur_horizontal_pass() + { + GPUShader *shader = shader_manager().get("compositor_symmetric_separable_blur"); + GPU_shader_bind(shader); + + GPU_shader_uniform_1b(shader, "extend_bounds", get_extend_bounds()); + GPU_shader_uniform_1b(shader, "gamma_correct_input", node_storage(bnode()).gamma); + GPU_shader_uniform_1b(shader, "gamma_uncorrect_output", false); + + const Result &input_image = get_input("Image"); + input_image.bind_as_texture(shader, "input_tx"); + + blur_horizontal_weights_.update(compute_blur_radius().x, node_storage(bnode()).filtertype); + blur_horizontal_weights_.bind_as_texture(shader, "weights_tx"); + + Domain domain = compute_domain(); + if (get_extend_bounds()) { + domain.size.x += static_cast<int>(math::ceil(compute_blur_radius().x)) * 2; + } + + /* We allocate an output image of a transposed size, that is, with a height equivalent to the + * width of the input and vice versa. This is done as a performance optimization. The shader + * will blur the image horizontally and write it to the intermediate output transposed. Then + * the vertical pass will execute the same horizontal blur shader, but since its input is + * transposed, it will effectively do a vertical blur and write to the output transposed, + * effectively undoing the transposition in the horizontal pass. This is done to improve + * spatial cache locality in the shader and to avoid having two separate shaders for each blur + * pass. */ + const int2 transposed_domain = int2(domain.size.y, domain.size.x); + + GPUTexture *horizontal_pass_result = texture_pool().acquire_color(transposed_domain); + const int image_unit = GPU_shader_get_texture_binding(shader, "output_img"); + GPU_texture_image_bind(horizontal_pass_result, image_unit); + + compute_dispatch_threads_at_least(shader, domain.size); + + GPU_shader_unbind(); + input_image.unbind_as_texture(); + blur_horizontal_weights_.unbind_as_texture(); + GPU_texture_image_unbind(horizontal_pass_result); + + return horizontal_pass_result; + } + + void execute_separable_blur_vertical_pass(GPUTexture *horizontal_pass_result) + { + GPUShader *shader = shader_manager().get("compositor_symmetric_separable_blur"); + GPU_shader_bind(shader); + + GPU_shader_uniform_1b(shader, "extend_bounds", get_extend_bounds()); + GPU_shader_uniform_1b(shader, "gamma_correct_input", false); + GPU_shader_uniform_1b(shader, "gamma_uncorrect_output", node_storage(bnode()).gamma); + + GPU_memory_barrier(GPU_BARRIER_TEXTURE_FETCH); + const int texture_image_unit = GPU_shader_get_texture_binding(shader, "input_tx"); + GPU_texture_bind(horizontal_pass_result, texture_image_unit); + + blur_vertical_weights_.update(compute_blur_radius().y, node_storage(bnode()).filtertype); + blur_vertical_weights_.bind_as_texture(shader, "weights_tx"); + + Domain domain = compute_domain(); + if (get_extend_bounds()) { + /* Add a radius amount of pixels in both sides of the image, hence the multiply by 2. */ + domain.size += int2(math::ceil(compute_blur_radius())) * 2; + } + + Result &output_image = get_result("Image"); + output_image.allocate_texture(domain); + output_image.bind_as_image(shader, "output_img"); + + /* Notice that the domain is transposed, see the note on the horizontal pass method for more + * information on the reasoning behind this. */ + compute_dispatch_threads_at_least(shader, int2(domain.size.y, domain.size.x)); + + GPU_shader_unbind(); + output_image.unbind_as_image(); + blur_vertical_weights_.unbind_as_texture(); + GPU_texture_unbind(horizontal_pass_result); + } + + float2 compute_blur_radius() + { + const float size = math::clamp(get_input("Size").get_float_value_default(1.0f), 0.0f, 1.0f); + + if (!node_storage(bnode()).relative) { + return float2(node_storage(bnode()).sizex, node_storage(bnode()).sizey) * size; + } + + int2 image_size = get_input("Image").domain().size; + switch (node_storage(bnode()).aspect) { + case CMP_NODE_BLUR_ASPECT_Y: + image_size.y = image_size.x; + break; + case CMP_NODE_BLUR_ASPECT_X: + image_size.x = image_size.y; + break; + default: + BLI_assert(node_storage(bnode()).aspect == CMP_NODE_BLUR_ASPECT_NONE); + break; + } + + return float2(image_size) * get_size_factor() * size; + } + + /* Returns true if the operation does nothing and the input can be passed through. */ + bool is_identity() + { + const Result &input = get_input("Image"); + /* Single value inputs can't be blurred and are returned as is. */ + if (input.is_single_value()) { + return true; + } + + /* Zero blur radius. The operation does nothing and the input can be passed through. */ + if (compute_blur_radius() == float2(0.0)) { + return true; + } + + return false; + } + + /* The blur node can operate with different filter types, evaluated on the normalized distance to + * the center of the filter. Some of those filters are separable and can be computed as such. If + * the bokeh member is disabled in the node, then the filter is always computed as separable even + * if it is not in fact separable, in which case, the used filter is a cheaper approximation to + * the actual filter. If the bokeh member is enabled, then the filter is computed as separable if + * it is in fact separable and as a normal 2D filter otherwise. */ + bool use_separable_filter() + { + if (!node_storage(bnode()).bokeh) { + return true; + } + + /* Both Box and Gaussian filters are separable. The rest is not. */ + switch (node_storage(bnode()).filtertype) { + case R_FILTER_BOX: + case R_FILTER_GAUSS: + case R_FILTER_FAST_GAUSS: + return true; + default: + return false; + } + } + + float2 get_size_factor() + { + return float2(node_storage(bnode()).percentx, node_storage(bnode()).percenty) / 100.0f; + } + + bool get_extend_bounds() + { + return bnode().custom1 & CMP_NODEFLAG_BLUR_EXTEND_BOUNDS; } }; diff --git a/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc b/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc index 538f00af12d..9c0617ee8c3 100644 --- a/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc +++ b/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc @@ -5,10 +5,16 @@ * \ingroup cmpnodes */ +#include "BLI_math_base.hh" +#include "BLI_math_vec_types.hh" + #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_texture.h" + #include "COM_node_operation.hh" +#include "COM_utilities.hh" #include "node_composite_util.hh" @@ -18,10 +24,22 @@ namespace blender::nodes::node_composite_bokehblur_cc { static void cmp_node_bokehblur_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image")).default_value({0.8f, 0.8f, 0.8f, 1.0f}); - b.add_input<decl::Color>(N_("Bokeh")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input<decl::Float>(N_("Size")).default_value(1.0f).min(0.0f).max(10.0f); - b.add_input<decl::Float>(N_("Bounding box")).default_value(1.0f).min(0.0f).max(1.0f); + b.add_input<decl::Color>(N_("Image")) + .default_value({0.8f, 0.8f, 0.8f, 1.0f}) + .compositor_domain_priority(0); + b.add_input<decl::Color>(N_("Bokeh")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_skip_realization(); + b.add_input<decl::Float>(N_("Size")) + .default_value(1.0f) + .min(0.0f) + .max(10.0f) + .compositor_domain_priority(1); + b.add_input<decl::Float>(N_("Bounding box")) + .default_value(1.0f) + .min(0.0f) + .max(1.0f) + .compositor_domain_priority(2); b.add_output<decl::Color>(N_("Image")); } @@ -47,7 +65,82 @@ class BokehBlurOperation : public NodeOperation { void execute() override { - get_input("Image").pass_through(get_result("Image")); + if (is_identity()) { + get_input("Image").pass_through(get_result("Image")); + return; + } + + GPUShader *shader = shader_manager().get("compositor_blur"); + GPU_shader_bind(shader); + + GPU_shader_uniform_1i(shader, "radius", compute_blur_radius()); + GPU_shader_uniform_1b(shader, "extend_bounds", get_extend_bounds()); + + const Result &input_image = get_input("Image"); + input_image.bind_as_texture(shader, "input_tx"); + + const Result &input_weights = get_input("Bokeh"); + input_weights.bind_as_texture(shader, "weights_tx"); + + const Result &input_mask = get_input("Bounding box"); + input_mask.bind_as_texture(shader, "mask_tx"); + + Domain domain = compute_domain(); + if (get_extend_bounds()) { + /* Add a radius amount of pixels in both sides of the image, hence the multiply by 2. */ + domain.size += int2(compute_blur_radius() * 2); + } + + Result &output_image = get_result("Image"); + output_image.allocate_texture(domain); + output_image.bind_as_image(shader, "output_img"); + + compute_dispatch_threads_at_least(shader, domain.size); + + GPU_shader_unbind(); + output_image.unbind_as_image(); + input_image.unbind_as_texture(); + input_weights.unbind_as_texture(); + input_mask.unbind_as_texture(); + } + + int compute_blur_radius() + { + const int2 image_size = get_input("Image").domain().size; + const int max_size = math::max(image_size.x, image_size.y); + + /* The [0, 10] range of the size is arbitrary and is merely in place to avoid very long + * computations of the bokeh blur. */ + const float size = math::clamp(get_input("Size").get_float_value_default(1.0f), 0.0f, 10.0f); + + /* The 100 divisor is arbitrary and was chosen using visual judgment. */ + return size * (max_size / 100.0f); + } + + bool is_identity() + { + const Result &input = get_input("Image"); + if (input.is_single_value()) { + return true; + } + + if (compute_blur_radius() == 0) { + return true; + } + + /* This input is, in fact, a boolean mask. If it is zero, no blurring will take place. + * Otherwise, the blurring will take place ignoring the value of the input entirely. */ + const Result &bounding_box = get_input("Bounding box"); + if (bounding_box.is_single_value() && bounding_box.get_float_value() == 0.0) { + return true; + } + + return false; + } + + bool get_extend_bounds() + { + return bnode().custom1 & CMP_NODEFLAG_BLUR_EXTEND_BOUNDS; } }; diff --git a/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc b/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc index a11cba37191..81cc8990d35 100644 --- a/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc +++ b/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc @@ -5,10 +5,16 @@ * \ingroup cmpnodes */ +#include "BLI_math_base.h" +#include "BLI_math_vec_types.hh" + #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_shader.h" + #include "COM_node_operation.hh" +#include "COM_utilities.hh" #include "node_composite_util.hh" @@ -16,6 +22,8 @@ namespace blender::nodes::node_composite_bokehimage_cc { +NODE_STORAGE_FUNCS(NodeBokehImage) + static void cmp_node_bokehimage_declare(NodeDeclarationBuilder &b) { b.add_output<decl::Color>(N_("Image")); @@ -55,7 +63,45 @@ class BokehImageOperation : public NodeOperation { void execute() override { - get_result("Image").allocate_invalid(); + GPUShader *shader = shader_manager().get("compositor_bokeh_image"); + GPU_shader_bind(shader); + + GPU_shader_uniform_1f(shader, "exterior_angle", get_exterior_angle()); + GPU_shader_uniform_1f(shader, "rotation", get_rotation()); + GPU_shader_uniform_1f(shader, "roundness", node_storage(bnode()).rounding); + GPU_shader_uniform_1f(shader, "catadioptric", node_storage(bnode()).catadioptric); + GPU_shader_uniform_1f(shader, "lens_shift", node_storage(bnode()).lensshift); + + Result &output = get_result("Image"); + const Domain domain = compute_domain(); + output.allocate_texture(domain); + output.bind_as_image(shader, "output_img"); + + compute_dispatch_threads_at_least(shader, domain.size); + + output.unbind_as_image(); + GPU_shader_unbind(); + } + + Domain compute_domain() override + { + return Domain(int2(512)); + } + + /* The exterior angle is the angle between each two consecutive vertices of the regular polygon + * from its center. */ + float get_exterior_angle() + { + return (M_PI * 2.0f) / node_storage(bnode()).flaps; + } + + float get_rotation() + { + /* Offset the rotation such that the second vertex of the regular polygon lies on the positive + * y axis, which is 90 degrees minus the angle that it makes with the positive x axis assuming + * the first vertex lies on the positive x axis. */ + const float offset = M_PI_2 - get_exterior_angle(); + return node_storage(bnode()).angle - offset; } }; diff --git a/source/blender/nodes/composite/nodes/node_composite_boxmask.cc b/source/blender/nodes/composite/nodes/node_composite_boxmask.cc index 9c7bb6432cb..3cf0932e1b3 100644 --- a/source/blender/nodes/composite/nodes/node_composite_boxmask.cc +++ b/source/blender/nodes/composite/nodes/node_composite_boxmask.cc @@ -23,6 +23,8 @@ namespace blender::nodes::node_composite_boxmask_cc { +NODE_STORAGE_FUNCS(NodeBoxMask) + static void cmp_node_boxmask_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Float>(N_("Mask")).default_value(0.0f).min(0.0f).max(1.0f); @@ -123,24 +125,19 @@ class BoxMaskOperation : public NodeOperation { } } - NodeBoxMask &get_node_box_mask() - { - return *static_cast<NodeBoxMask *>(bnode().storage); - } - float2 get_location() { - return float2(get_node_box_mask().x, get_node_box_mask().y); + return float2(node_storage(bnode()).x, node_storage(bnode()).y); } float2 get_size() { - return float2(get_node_box_mask().width, get_node_box_mask().height); + return float2(node_storage(bnode()).width, node_storage(bnode()).height); } float get_angle() { - return get_node_box_mask().rotation; + return node_storage(bnode()).rotation; } }; diff --git a/source/blender/nodes/composite/nodes/node_composite_channel_matte.cc b/source/blender/nodes/composite/nodes/node_composite_channel_matte.cc index 018632f776c..3b825017da8 100644 --- a/source/blender/nodes/composite/nodes/node_composite_channel_matte.cc +++ b/source/blender/nodes/composite/nodes/node_composite_channel_matte.cc @@ -20,6 +20,8 @@ namespace blender::nodes::node_composite_channel_matte_cc { +NODE_STORAGE_FUNCS(NodeChroma) + static void cmp_node_channel_matte_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Color>(N_("Image")) @@ -130,15 +132,10 @@ class ChannelMatteShaderNode : public ShaderNode { return bnode().custom2 - 1; } - NodeChroma *get_node_chroma() - { - return static_cast<NodeChroma *>(bnode().storage); - } - /* Get the index of the channel used to compute the limit value. */ int get_limit_channel() { - return get_node_chroma()->channel - 1; + return node_storage(bnode()).channel - 1; } /* Get the indices of the channels used to compute the limit value. We always assume the limit @@ -146,7 +143,7 @@ class ChannelMatteShaderNode : public ShaderNode { * the maximum of two identical values is the same value. */ void get_limit_channels(float limit_channels[2]) { - if (get_node_chroma()->algorithm == CMP_NODE_CHANNEL_MATTE_LIMIT_ALGORITHM_MAX) { + if (node_storage(bnode()).algorithm == CMP_NODE_CHANNEL_MATTE_LIMIT_ALGORITHM_MAX) { /* If the algorithm is Max, store the indices of the other two channels other than the matte * channel. */ limit_channels[0] = (get_matte_channel() + 1) % 3; @@ -161,12 +158,12 @@ class ChannelMatteShaderNode : public ShaderNode { float get_max_limit() { - return get_node_chroma()->t1; + return node_storage(bnode()).t1; } float get_min_limit() { - return get_node_chroma()->t2; + return node_storage(bnode()).t2; } }; diff --git a/source/blender/nodes/composite/nodes/node_composite_chroma_matte.cc b/source/blender/nodes/composite/nodes/node_composite_chroma_matte.cc index cb3648c5680..e5ce87169d4 100644 --- a/source/blender/nodes/composite/nodes/node_composite_chroma_matte.cc +++ b/source/blender/nodes/composite/nodes/node_composite_chroma_matte.cc @@ -22,6 +22,8 @@ namespace blender::nodes::node_composite_chroma_matte_cc { +NODE_STORAGE_FUNCS(NodeChroma) + static void cmp_node_chroma_matte_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Color>(N_("Image")) @@ -86,24 +88,19 @@ class ChromaMatteShaderNode : public ShaderNode { GPU_uniform(&falloff)); } - NodeChroma *get_node_chroma() - { - return static_cast<NodeChroma *>(bnode().storage); - } - float get_acceptance() { - return std::tan(get_node_chroma()->t1) / 2.0f; + return std::tan(node_storage(bnode()).t1) / 2.0f; } float get_cutoff() { - return get_node_chroma()->t2; + return node_storage(bnode()).t2; } float get_falloff() { - return get_node_chroma()->fstrength; + return node_storage(bnode()).fstrength; } }; diff --git a/source/blender/nodes/composite/nodes/node_composite_color_matte.cc b/source/blender/nodes/composite/nodes/node_composite_color_matte.cc index 5e3aaf512e6..08329601f14 100644 --- a/source/blender/nodes/composite/nodes/node_composite_color_matte.cc +++ b/source/blender/nodes/composite/nodes/node_composite_color_matte.cc @@ -18,6 +18,8 @@ namespace blender::nodes::node_composite_color_matte_cc { +NODE_STORAGE_FUNCS(NodeChroma) + static void cmp_node_color_matte_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Color>(N_("Image")) @@ -83,25 +85,20 @@ class ColorMatteShaderNode : public ShaderNode { GPU_uniform(&value_epsilon)); } - NodeChroma *get_node_chroma() - { - return static_cast<NodeChroma *>(bnode().storage); - } - float get_hue_epsilon() { /* Divide by 2 because the hue wraps around. */ - return get_node_chroma()->t1 / 2.0f; + return node_storage(bnode()).t1 / 2.0f; } float get_saturation_epsilon() { - return get_node_chroma()->t2; + return node_storage(bnode()).t2; } float get_value_epsilon() { - return get_node_chroma()->t3; + return node_storage(bnode()).t3; } }; diff --git a/source/blender/nodes/composite/nodes/node_composite_color_spill.cc b/source/blender/nodes/composite/nodes/node_composite_color_spill.cc index 9744c01a256..29401d7b20f 100644 --- a/source/blender/nodes/composite/nodes/node_composite_color_spill.cc +++ b/source/blender/nodes/composite/nodes/node_composite_color_spill.cc @@ -20,6 +20,8 @@ namespace blender::nodes::node_composite_color_spill_cc { +NODE_STORAGE_FUNCS(NodeColorspill) + static void cmp_node_color_spill_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Color>(N_("Image")) @@ -131,18 +133,13 @@ class ColorSpillShaderNode : public ShaderNode { return (CMPNodeColorSpillLimitAlgorithm)bnode().custom2; } - NodeColorspill *get_node_color_spill() - { - return static_cast<NodeColorspill *>(bnode().storage); - } - void get_spill_scale(float spill_scale[3]) { - const NodeColorspill *node_color_spill = get_node_color_spill(); - if (node_color_spill->unspill) { - spill_scale[0] = node_color_spill->uspillr; - spill_scale[1] = node_color_spill->uspillg; - spill_scale[2] = node_color_spill->uspillb; + const NodeColorspill &node_color_spill = node_storage(bnode()); + if (node_color_spill.unspill) { + spill_scale[0] = node_color_spill.uspillr; + spill_scale[1] = node_color_spill.uspillg; + spill_scale[2] = node_color_spill.uspillb; spill_scale[get_spill_channel()] *= -1.0f; } else { @@ -156,7 +153,7 @@ class ColorSpillShaderNode : public ShaderNode { /* Get the index of the channel used for limiting. */ int get_limit_channel() { - return get_node_color_spill()->limchan; + return node_storage(bnode()).limchan; } /* Get the indices of the channels used to compute the limit value. We always assume the limit @@ -179,7 +176,7 @@ class ColorSpillShaderNode : public ShaderNode { float get_limit_scale() { - return get_node_color_spill()->limscale; + return node_storage(bnode()).limscale; } }; diff --git a/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc b/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc index 95675169c76..e05fbf00a25 100644 --- a/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc +++ b/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc @@ -48,6 +48,8 @@ void ntreeCompositColorBalanceSyncFromCDL(bNodeTree *UNUSED(ntree), bNode *node) namespace blender::nodes::node_composite_colorbalance_cc { +NODE_STORAGE_FUNCS(NodeColorBalance) + static void cmp_node_colorbalance_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Float>(N_("Fac")) @@ -161,7 +163,7 @@ class ColorBalanceShaderNode : public ShaderNode { GPUNodeStack *inputs = get_inputs_array(); GPUNodeStack *outputs = get_outputs_array(); - const NodeColorBalance *node_color_balance = get_node_color_balance(); + const NodeColorBalance &node_color_balance = node_storage(bnode()); if (get_color_balance_method() == CMP_NODE_COLOR_BALANCE_LGG) { GPU_stack_link(material, @@ -169,9 +171,9 @@ class ColorBalanceShaderNode : public ShaderNode { "node_composite_color_balance_lgg", inputs, outputs, - GPU_uniform(node_color_balance->lift), - GPU_uniform(node_color_balance->gamma), - GPU_uniform(node_color_balance->gain)); + GPU_uniform(node_color_balance.lift), + GPU_uniform(node_color_balance.gamma), + GPU_uniform(node_color_balance.gain)); return; } @@ -180,21 +182,16 @@ class ColorBalanceShaderNode : public ShaderNode { "node_composite_color_balance_asc_cdl", inputs, outputs, - GPU_uniform(node_color_balance->offset), - GPU_uniform(node_color_balance->power), - GPU_uniform(node_color_balance->slope), - GPU_uniform(&node_color_balance->offset_basis)); + GPU_uniform(node_color_balance.offset), + GPU_uniform(node_color_balance.power), + GPU_uniform(node_color_balance.slope), + GPU_uniform(&node_color_balance.offset_basis)); } CMPNodeColorBalanceMethod get_color_balance_method() { return (CMPNodeColorBalanceMethod)bnode().custom1; } - - NodeColorBalance *get_node_color_balance() - { - return static_cast<NodeColorBalance *>(bnode().storage); - } }; static ShaderNode *get_compositor_shader_node(DNode node) diff --git a/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc b/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc index 36e6672ce1c..92b10fc1877 100644 --- a/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc +++ b/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc @@ -20,6 +20,8 @@ namespace blender::nodes::node_composite_colorcorrection_cc { +NODE_STORAGE_FUNCS(NodeColorCorrection) + static void cmp_node_colorcorrection_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Color>(N_("Image")) @@ -294,7 +296,7 @@ class ColorCorrectionShaderNode : public ShaderNode { float luminance_coefficients[3]; IMB_colormanagement_get_luminance_coefficients(luminance_coefficients); - const NodeColorCorrection *node_color_correction = get_node_color_correction(); + const NodeColorCorrection &node_color_correction = node_storage(bnode()); GPU_stack_link(material, &bnode(), @@ -302,28 +304,28 @@ class ColorCorrectionShaderNode : public ShaderNode { inputs, outputs, GPU_constant(enabled_channels), - GPU_uniform(&node_color_correction->startmidtones), - GPU_uniform(&node_color_correction->endmidtones), - GPU_uniform(&node_color_correction->master.saturation), - GPU_uniform(&node_color_correction->master.contrast), - GPU_uniform(&node_color_correction->master.gamma), - GPU_uniform(&node_color_correction->master.gain), - GPU_uniform(&node_color_correction->master.lift), - GPU_uniform(&node_color_correction->shadows.saturation), - GPU_uniform(&node_color_correction->shadows.contrast), - GPU_uniform(&node_color_correction->shadows.gamma), - GPU_uniform(&node_color_correction->shadows.gain), - GPU_uniform(&node_color_correction->shadows.lift), - GPU_uniform(&node_color_correction->midtones.saturation), - GPU_uniform(&node_color_correction->midtones.contrast), - GPU_uniform(&node_color_correction->midtones.gamma), - GPU_uniform(&node_color_correction->midtones.gain), - GPU_uniform(&node_color_correction->midtones.lift), - GPU_uniform(&node_color_correction->highlights.saturation), - GPU_uniform(&node_color_correction->highlights.contrast), - GPU_uniform(&node_color_correction->highlights.gamma), - GPU_uniform(&node_color_correction->highlights.gain), - GPU_uniform(&node_color_correction->highlights.lift), + GPU_uniform(&node_color_correction.startmidtones), + GPU_uniform(&node_color_correction.endmidtones), + GPU_uniform(&node_color_correction.master.saturation), + GPU_uniform(&node_color_correction.master.contrast), + GPU_uniform(&node_color_correction.master.gamma), + GPU_uniform(&node_color_correction.master.gain), + GPU_uniform(&node_color_correction.master.lift), + GPU_uniform(&node_color_correction.shadows.saturation), + GPU_uniform(&node_color_correction.shadows.contrast), + GPU_uniform(&node_color_correction.shadows.gamma), + GPU_uniform(&node_color_correction.shadows.gain), + GPU_uniform(&node_color_correction.shadows.lift), + GPU_uniform(&node_color_correction.midtones.saturation), + GPU_uniform(&node_color_correction.midtones.contrast), + GPU_uniform(&node_color_correction.midtones.gamma), + GPU_uniform(&node_color_correction.midtones.gain), + GPU_uniform(&node_color_correction.midtones.lift), + GPU_uniform(&node_color_correction.highlights.saturation), + GPU_uniform(&node_color_correction.highlights.contrast), + GPU_uniform(&node_color_correction.highlights.gamma), + GPU_uniform(&node_color_correction.highlights.gain), + GPU_uniform(&node_color_correction.highlights.lift), GPU_constant(luminance_coefficients)); } @@ -333,11 +335,6 @@ class ColorCorrectionShaderNode : public ShaderNode { enabled_channels[i] = (bnode().custom1 & (1 << i)) ? 1.0f : 0.0f; } } - - NodeColorCorrection *get_node_color_correction() - { - return static_cast<NodeColorCorrection *>(bnode().storage); - } }; static ShaderNode *get_compositor_shader_node(DNode node) diff --git a/source/blender/nodes/composite/nodes/node_composite_crop.cc b/source/blender/nodes/composite/nodes/node_composite_crop.cc index d7331732fc7..13d02a707be 100644 --- a/source/blender/nodes/composite/nodes/node_composite_crop.cc +++ b/source/blender/nodes/composite/nodes/node_composite_crop.cc @@ -27,6 +27,8 @@ namespace blender::nodes::node_composite_crop_cc { +NODE_STORAGE_FUNCS(NodeTwoXYs) + static void cmp_node_crop_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Color>(N_("Image")) @@ -163,11 +165,6 @@ class CropOperation : public NodeOperation { return bnode().custom2; } - NodeTwoXYs &get_node_two_xys() - { - return *static_cast<NodeTwoXYs *>(bnode().storage); - } - /* Returns true if the operation does nothing and the input can be passed through. */ bool is_identity() { @@ -190,7 +187,7 @@ class CropOperation : public NodeOperation { void compute_cropping_bounds(int2 &lower_bound, int2 &upper_bound) { - const NodeTwoXYs &node_two_xys = get_node_two_xys(); + const NodeTwoXYs &node_two_xys = node_storage(bnode()); const int2 input_size = get_input("Image").domain().size; if (get_is_relative()) { diff --git a/source/blender/nodes/composite/nodes/node_composite_curves.cc b/source/blender/nodes/composite/nodes/node_composite_curves.cc index c5d303c576a..bf45e219730 100644 --- a/source/blender/nodes/composite/nodes/node_composite_curves.cc +++ b/source/blender/nodes/composite/nodes/node_composite_curves.cc @@ -47,15 +47,15 @@ class TimeCurveOperation : public NodeOperation { Result &result = get_result("Fac"); result.allocate_single_value(); - CurveMapping *curve_mapping = get_curve_mapping(); + CurveMapping *curve_mapping = const_cast<CurveMapping *>(get_curve_mapping()); BKE_curvemapping_init(curve_mapping); const float time = BKE_curvemapping_evaluateF(curve_mapping, 0, compute_normalized_time()); result.set_float_value(clamp_f(time, 0.0f, 1.0f)); } - CurveMapping *get_curve_mapping() + const CurveMapping *get_curve_mapping() { - return static_cast<CurveMapping *>(bnode().storage); + return static_cast<const CurveMapping *>(bnode().storage); } int get_start_time() @@ -143,7 +143,7 @@ class VectorCurvesShaderNode : public ShaderNode { GPUNodeStack *inputs = get_inputs_array(); GPUNodeStack *outputs = get_outputs_array(); - CurveMapping *curve_mapping = get_curve_mapping(); + CurveMapping *curve_mapping = const_cast<CurveMapping *>(get_curve_mapping()); BKE_curvemapping_init(curve_mapping); float *band_values; @@ -173,9 +173,9 @@ class VectorCurvesShaderNode : public ShaderNode { GPU_uniform(end_slopes)); } - CurveMapping *get_curve_mapping() + const CurveMapping *get_curve_mapping() { - return static_cast<CurveMapping *>(bnode().storage); + return static_cast<const CurveMapping *>(bnode().storage); } }; @@ -239,7 +239,7 @@ class RGBCurvesShaderNode : public ShaderNode { GPUNodeStack *inputs = get_inputs_array(); GPUNodeStack *outputs = get_outputs_array(); - CurveMapping *curve_mapping = get_curve_mapping(); + CurveMapping *curve_mapping = const_cast<CurveMapping *>(get_curve_mapping()); BKE_curvemapping_init(curve_mapping); float *band_values; @@ -311,9 +311,9 @@ class RGBCurvesShaderNode : public ShaderNode { GPU_uniform(end_slopes)); } - CurveMapping *get_curve_mapping() + const CurveMapping *get_curve_mapping() { - return static_cast<CurveMapping *>(bnode().storage); + return static_cast<const CurveMapping *>(bnode().storage); } }; diff --git a/source/blender/nodes/composite/nodes/node_composite_despeckle.cc b/source/blender/nodes/composite/nodes/node_composite_despeckle.cc index 0b9f9c8f76d..aa6725b8750 100644 --- a/source/blender/nodes/composite/nodes/node_composite_despeckle.cc +++ b/source/blender/nodes/composite/nodes/node_composite_despeckle.cc @@ -8,7 +8,10 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_shader.h" + #include "COM_node_operation.hh" +#include "COM_utilities.hh" #include "node_composite_util.hh" @@ -18,8 +21,15 @@ namespace blender::nodes::node_composite_despeckle_cc { static void cmp_node_despeckle_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Float>(N_("Fac")) + .default_value(1.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR) + .compositor_domain_priority(1); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); b.add_output<decl::Color>(N_("Image")); } @@ -46,7 +56,45 @@ class DespeckleOperation : public NodeOperation { void execute() override { - get_input("Image").pass_through(get_result("Image")); + const Result &input_image = get_input("Image"); + /* Single value inputs can't be despeckled and are returned as is. */ + if (input_image.is_single_value()) { + get_input("Image").pass_through(get_result("Image")); + return; + } + + GPUShader *shader = shader_manager().get("compositor_despeckle"); + GPU_shader_bind(shader); + + GPU_shader_uniform_1f(shader, "threshold", get_threshold()); + GPU_shader_uniform_1f(shader, "neighbor_threshold", get_neighbor_threshold()); + + input_image.bind_as_texture(shader, "input_tx"); + + const Result &factor_image = get_input("Fac"); + factor_image.bind_as_texture(shader, "factor_tx"); + + const Domain domain = compute_domain(); + Result &output_image = get_result("Image"); + output_image.allocate_texture(domain); + output_image.bind_as_image(shader, "output_img"); + + compute_dispatch_threads_at_least(shader, domain.size); + + GPU_shader_unbind(); + output_image.unbind_as_image(); + input_image.unbind_as_texture(); + factor_image.unbind_as_texture(); + } + + float get_threshold() + { + return bnode().custom3; + } + + float get_neighbor_threshold() + { + return bnode().custom4; } }; diff --git a/source/blender/nodes/composite/nodes/node_composite_diff_matte.cc b/source/blender/nodes/composite/nodes/node_composite_diff_matte.cc index e129dcaa6ef..8912d00a9be 100644 --- a/source/blender/nodes/composite/nodes/node_composite_diff_matte.cc +++ b/source/blender/nodes/composite/nodes/node_composite_diff_matte.cc @@ -18,6 +18,8 @@ namespace blender::nodes::node_composite_diff_matte_cc { +NODE_STORAGE_FUNCS(NodeChroma) + static void cmp_node_diff_matte_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Color>(N_("Image 1")) @@ -71,19 +73,14 @@ class DifferenceMatteShaderNode : public ShaderNode { GPU_uniform(&falloff)); } - NodeChroma *get_node_chroma() - { - return static_cast<NodeChroma *>(bnode().storage); - } - float get_tolerance() { - return get_node_chroma()->t1; + return node_storage(bnode()).t1; } float get_falloff() { - return get_node_chroma()->t2; + return node_storage(bnode()).t2; } }; diff --git a/source/blender/nodes/composite/nodes/node_composite_dilate.cc b/source/blender/nodes/composite/nodes/node_composite_dilate.cc index 46199d3ff04..551dfacb276 100644 --- a/source/blender/nodes/composite/nodes/node_composite_dilate.cc +++ b/source/blender/nodes/composite/nodes/node_composite_dilate.cc @@ -5,12 +5,27 @@ * \ingroup cmpnodes */ +#include <cmath> + +#include "BLI_array.hh" +#include "BLI_assert.h" +#include "BLI_math_base.hh" + +#include "DNA_scene_types.h" + #include "RNA_access.h" #include "UI_interface.h" #include "UI_resources.h" +#include "RE_pipeline.h" + +#include "GPU_shader.h" +#include "GPU_state.h" +#include "GPU_texture.h" + #include "COM_node_operation.hh" +#include "COM_utilities.hh" #include "node_composite_util.hh" @@ -18,6 +33,8 @@ namespace blender::nodes::node_composite_dilate_cc { +NODE_STORAGE_FUNCS(NodeDilateErode) + static void cmp_node_dilate_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Float>(N_("Mask")).default_value(0.0f).min(0.0f).max(1.0f); @@ -36,10 +53,10 @@ static void node_composit_buts_dilateerode(uiLayout *layout, bContext *UNUSED(C) uiItemR(layout, ptr, "mode", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); uiItemR(layout, ptr, "distance", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); switch (RNA_enum_get(ptr, "mode")) { - case CMP_NODE_DILATEERODE_DISTANCE_THRESH: + case CMP_NODE_DILATE_ERODE_DISTANCE_THRESHOLD: uiItemR(layout, ptr, "edge", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); break; - case CMP_NODE_DILATEERODE_DISTANCE_FEATHER: + case CMP_NODE_DILATE_ERODE_DISTANCE_FEATHER: uiItemR(layout, ptr, "falloff", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); break; } @@ -47,13 +64,458 @@ static void node_composit_buts_dilateerode(uiLayout *layout, bContext *UNUSED(C) using namespace blender::realtime_compositor; +/* Computes a falloff that is equal to 1 at an input of zero and decrease to zero at an input of 1, + * with the rate of decrease depending on the falloff type. */ +static float compute_distance_falloff(float x, int falloff_type) +{ + x = 1.0f - x; + + switch (falloff_type) { + case PROP_SMOOTH: + return 3.0f * x * x - 2.0f * x * x * x; + case PROP_SPHERE: + return std::sqrt(2.0f * x - x * x); + case PROP_ROOT: + return std::sqrt(x); + case PROP_SHARP: + return x * x; + case PROP_INVSQUARE: + return x * (2.0f - x); + case PROP_LIN: + return x; + default: + BLI_assert_unreachable(); + return x; + } +} + +/* A helper class that computes and caches 1D GPU textures containing the weights of the separable + * Gaussian filter of the given radius as well as an inverse distance falloff of the given type and + * radius. The weights and falloffs are symmetric, because the Gaussian and falloff functions are + * all even functions. Consequently, only the positive half of the filter is computed and the + * shader takes that into consideration. */ +class SymmetricSeparableMorphologicalDistanceFeatherWeights { + private: + int radius_ = 1; + int falloff_type_ = PROP_SMOOTH; + GPUTexture *weights_texture_ = nullptr; + GPUTexture *distance_falloffs_texture_ = nullptr; + + public: + ~SymmetricSeparableMorphologicalDistanceFeatherWeights() + { + if (weights_texture_) { + GPU_texture_free(weights_texture_); + } + + if (distance_falloffs_texture_) { + GPU_texture_free(distance_falloffs_texture_); + } + } + + /* Check if textures containing the weights and distance falloffs were already computed for the + * given distance falloff type and radius. If such textures exists, do nothing, otherwise, free + * the already computed textures and recompute it with the given distance falloff type and + * radius. */ + void update(int radius, int falloff_type) + { + if (weights_texture_ && distance_falloffs_texture_ && falloff_type == falloff_type_ && + radius == radius_) { + return; + } + + radius_ = radius; + falloff_type_ = falloff_type; + + compute_weights(); + compute_distance_falloffs(); + } + + void compute_weights() + { + if (weights_texture_) { + GPU_texture_free(weights_texture_); + } + + /* The size of filter is double the radius plus 1, but since the filter is symmetric, we only + * compute half of it and no doubling happens. We add 1 to make sure the filter size is always + * odd and there is a center weight. */ + const int size = radius_ + 1; + Array<float> weights(size); + + float sum = 0.0f; + + /* First, compute the center weight. */ + const float center_weight = RE_filter_value(R_FILTER_GAUSS, 0.0f); + weights[0] = center_weight; + sum += center_weight; + + /* Second, compute the other weights in the positive direction, making sure to add double the + * weight to the sum of weights because the filter is symmetric and we only loop over half of + * it. Skip the center weight already computed by dropping the front index. */ + const float scale = radius_ > 0.0f ? 1.0f / radius_ : 0.0f; + for (const int i : weights.index_range().drop_front(1)) { + const float weight = RE_filter_value(R_FILTER_GAUSS, i * scale); + weights[i] = weight; + sum += weight * 2.0f; + } + + /* Finally, normalize the weights. */ + for (const int i : weights.index_range()) { + weights[i] /= sum; + } + + weights_texture_ = GPU_texture_create_1d("Weights", size, 1, GPU_R16F, weights.data()); + } + + void compute_distance_falloffs() + { + if (distance_falloffs_texture_) { + GPU_texture_free(distance_falloffs_texture_); + } + + /* The size of the distance falloffs is double the radius plus 1, but since the falloffs are + * symmetric, we only compute half of them and no doubling happens. We add 1 to make sure the + * falloffs size is always odd and there is a center falloff. */ + const int size = radius_ + 1; + Array<float> falloffs(size); + + /* Compute the distance falloffs in the positive direction only, because the falloffs are + * symmetric. */ + const float scale = radius_ > 0.0f ? 1.0f / radius_ : 0.0f; + for (const int i : falloffs.index_range()) { + falloffs[i] = compute_distance_falloff(i * scale, falloff_type_); + } + + distance_falloffs_texture_ = GPU_texture_create_1d( + "Distance Factors", size, 1, GPU_R16F, falloffs.data()); + } + + void bind_weights_as_texture(GPUShader *shader, const char *texture_name) + { + const int texture_image_unit = GPU_shader_get_texture_binding(shader, texture_name); + GPU_texture_bind(weights_texture_, texture_image_unit); + } + + void unbind_weights_as_texture() + { + GPU_texture_unbind(weights_texture_); + } + + void bind_distance_falloffs_as_texture(GPUShader *shader, const char *texture_name) + { + const int texture_image_unit = GPU_shader_get_texture_binding(shader, texture_name); + GPU_texture_bind(distance_falloffs_texture_, texture_image_unit); + } + + void unbind_distance_falloffs_as_texture() + { + GPU_texture_unbind(distance_falloffs_texture_); + } +}; + class DilateErodeOperation : public NodeOperation { + private: + /* Cached symmetric blur weights and distance falloffs for the distance feature method. */ + SymmetricSeparableMorphologicalDistanceFeatherWeights distance_feather_weights_; + public: using NodeOperation::NodeOperation; void execute() override { - get_input("Mask").pass_through(get_result("Mask")); + if (is_identity()) { + get_input("Mask").pass_through(get_result("Mask")); + return; + } + + switch (get_method()) { + case CMP_NODE_DILATE_ERODE_STEP: + execute_step(); + return; + case CMP_NODE_DILATE_ERODE_DISTANCE: + execute_distance(); + return; + case CMP_NODE_DILATE_ERODE_DISTANCE_THRESHOLD: + execute_distance_threshold(); + return; + case CMP_NODE_DILATE_ERODE_DISTANCE_FEATHER: + execute_distance_feather(); + return; + default: + BLI_assert_unreachable(); + return; + } + } + + /* ---------------------------- + * Step Morphological Operator. + * ---------------------------- */ + + void execute_step() + { + GPUTexture *horizontal_pass_result = execute_step_horizontal_pass(); + execute_step_vertical_pass(horizontal_pass_result); + } + + GPUTexture *execute_step_horizontal_pass() + { + GPUShader *shader = shader_manager().get(get_morphological_step_shader_name()); + GPU_shader_bind(shader); + + /* Pass the absolute value of the distance. We have specialized shaders for each sign. */ + GPU_shader_uniform_1i(shader, "radius", math::abs(get_distance())); + + const Result &input_mask = get_input("Mask"); + input_mask.bind_as_texture(shader, "input_tx"); + + /* We allocate an output image of a transposed size, that is, with a height equivalent to the + * width of the input and vice versa. This is done as a performance optimization. The shader + * will process the image horizontally and write it to the intermediate output transposed. Then + * the vertical pass will execute the same horizontal pass shader, but since its input is + * transposed, it will effectively do a vertical pass and write to the output transposed, + * effectively undoing the transposition in the horizontal pass. This is done to improve + * spatial cache locality in the shader and to avoid having two separate shaders for each of + * the passes. */ + const Domain domain = compute_domain(); + const int2 transposed_domain = int2(domain.size.y, domain.size.x); + + GPUTexture *horizontal_pass_result = texture_pool().acquire_color(transposed_domain); + const int image_unit = GPU_shader_get_texture_binding(shader, "output_img"); + GPU_texture_image_bind(horizontal_pass_result, image_unit); + + compute_dispatch_threads_at_least(shader, domain.size); + + GPU_shader_unbind(); + input_mask.unbind_as_texture(); + GPU_texture_image_unbind(horizontal_pass_result); + + return horizontal_pass_result; + } + + void execute_step_vertical_pass(GPUTexture *horizontal_pass_result) + { + GPUShader *shader = shader_manager().get(get_morphological_step_shader_name()); + GPU_shader_bind(shader); + + /* Pass the absolute value of the distance. We have specialized shaders for each sign. */ + GPU_shader_uniform_1i(shader, "radius", math::abs(get_distance())); + + GPU_memory_barrier(GPU_BARRIER_TEXTURE_FETCH); + const int texture_image_unit = GPU_shader_get_texture_binding(shader, "input_tx"); + GPU_texture_bind(horizontal_pass_result, texture_image_unit); + + const Domain domain = compute_domain(); + Result &output_mask = get_result("Mask"); + output_mask.allocate_texture(domain); + output_mask.bind_as_image(shader, "output_img"); + + /* Notice that the domain is transposed, see the note on the horizontal pass method for more + * information on the reasoning behind this. */ + compute_dispatch_threads_at_least(shader, int2(domain.size.y, domain.size.x)); + + GPU_shader_unbind(); + output_mask.unbind_as_image(); + GPU_texture_unbind(horizontal_pass_result); + } + + const char *get_morphological_step_shader_name() + { + if (get_distance() > 0) { + return "compositor_morphological_step_dilate"; + } + return "compositor_morphological_step_erode"; + } + + /* -------------------------------- + * Distance Morphological Operator. + * -------------------------------- */ + + void execute_distance() + { + GPUShader *shader = shader_manager().get(get_morphological_distance_shader_name()); + GPU_shader_bind(shader); + + /* Pass the absolute value of the distance. We have specialized shaders for each sign. */ + GPU_shader_uniform_1i(shader, "radius", math::abs(get_distance())); + + const Result &input_mask = get_input("Mask"); + input_mask.bind_as_texture(shader, "input_tx"); + + const Domain domain = compute_domain(); + Result &output_mask = get_result("Mask"); + output_mask.allocate_texture(domain); + output_mask.bind_as_image(shader, "output_img"); + + compute_dispatch_threads_at_least(shader, domain.size); + + GPU_shader_unbind(); + output_mask.unbind_as_image(); + input_mask.unbind_as_texture(); + } + + const char *get_morphological_distance_shader_name() + { + if (get_distance() > 0) { + return "compositor_morphological_distance_dilate"; + } + return "compositor_morphological_distance_erode"; + } + + /* ------------------------------------------ + * Distance Threshold Morphological Operator. + * ------------------------------------------ */ + + void execute_distance_threshold() + { + GPUShader *shader = shader_manager().get("compositor_morphological_distance_threshold"); + GPU_shader_bind(shader); + + GPU_shader_uniform_1f(shader, "inset", get_inset()); + GPU_shader_uniform_1i(shader, "radius", get_morphological_distance_threshold_radius()); + GPU_shader_uniform_1i(shader, "distance", get_distance()); + + const Result &input_mask = get_input("Mask"); + input_mask.bind_as_texture(shader, "input_tx"); + + const Domain domain = compute_domain(); + Result &output_mask = get_result("Mask"); + output_mask.allocate_texture(domain); + output_mask.bind_as_image(shader, "output_img"); + + compute_dispatch_threads_at_least(shader, domain.size); + + GPU_shader_unbind(); + output_mask.unbind_as_image(); + input_mask.unbind_as_texture(); + } + + /* See the discussion in the implementation for more information. */ + int get_morphological_distance_threshold_radius() + { + return static_cast<int>(math::ceil(get_inset())) + math::abs(get_distance()); + } + + /* ---------------------------------------- + * Distance Feather Morphological Operator. + * ---------------------------------------- */ + + void execute_distance_feather() + { + GPUTexture *horizontal_pass_result = execute_distance_feather_horizontal_pass(); + execute_distance_feather_vertical_pass(horizontal_pass_result); + } + + GPUTexture *execute_distance_feather_horizontal_pass() + { + GPUShader *shader = shader_manager().get(get_morphological_distance_feather_shader_name()); + GPU_shader_bind(shader); + + const Result &input_image = get_input("Mask"); + input_image.bind_as_texture(shader, "input_tx"); + + distance_feather_weights_.update(math::abs(get_distance()), node_storage(bnode()).falloff); + distance_feather_weights_.bind_weights_as_texture(shader, "weights_tx"); + distance_feather_weights_.bind_distance_falloffs_as_texture(shader, "falloffs_tx"); + + /* We allocate an output image of a transposed size, that is, with a height equivalent to the + * width of the input and vice versa. This is done as a performance optimization. The shader + * will process the image horizontally and write it to the intermediate output transposed. Then + * the vertical pass will execute the same horizontal pass shader, but since its input is + * transposed, it will effectively do a vertical pass and write to the output transposed, + * effectively undoing the transposition in the horizontal pass. This is done to improve + * spatial cache locality in the shader and to avoid having two separate shaders for each of + * the passes. */ + const Domain domain = compute_domain(); + const int2 transposed_domain = int2(domain.size.y, domain.size.x); + + GPUTexture *horizontal_pass_result = texture_pool().acquire_color(transposed_domain); + const int image_unit = GPU_shader_get_texture_binding(shader, "output_img"); + GPU_texture_image_bind(horizontal_pass_result, image_unit); + + compute_dispatch_threads_at_least(shader, domain.size); + + GPU_shader_unbind(); + input_image.unbind_as_texture(); + distance_feather_weights_.unbind_weights_as_texture(); + distance_feather_weights_.unbind_distance_falloffs_as_texture(); + GPU_texture_image_unbind(horizontal_pass_result); + + return horizontal_pass_result; + } + + void execute_distance_feather_vertical_pass(GPUTexture *horizontal_pass_result) + { + GPUShader *shader = shader_manager().get(get_morphological_distance_feather_shader_name()); + GPU_shader_bind(shader); + + GPU_memory_barrier(GPU_BARRIER_TEXTURE_FETCH); + const int texture_image_unit = GPU_shader_get_texture_binding(shader, "input_tx"); + GPU_texture_bind(horizontal_pass_result, texture_image_unit); + + distance_feather_weights_.update(math::abs(get_distance()), node_storage(bnode()).falloff); + distance_feather_weights_.bind_weights_as_texture(shader, "weights_tx"); + distance_feather_weights_.bind_distance_falloffs_as_texture(shader, "falloffs_tx"); + + const Domain domain = compute_domain(); + Result &output_image = get_result("Mask"); + output_image.allocate_texture(domain); + output_image.bind_as_image(shader, "output_img"); + + /* Notice that the domain is transposed, see the note on the horizontal pass method for more + * information on the reasoning behind this. */ + compute_dispatch_threads_at_least(shader, int2(domain.size.y, domain.size.x)); + + GPU_shader_unbind(); + output_image.unbind_as_image(); + distance_feather_weights_.unbind_weights_as_texture(); + distance_feather_weights_.unbind_distance_falloffs_as_texture(); + GPU_texture_unbind(horizontal_pass_result); + } + + const char *get_morphological_distance_feather_shader_name() + { + if (get_distance() > 0) { + return "compositor_morphological_distance_feather_dilate"; + } + return "compositor_morphological_distance_feather_erode"; + } + + /* --------------- + * Common Methods. + * --------------- */ + + bool is_identity() + { + const Result &input = get_input("Mask"); + if (input.is_single_value()) { + return true; + } + + if (get_method() == CMP_NODE_DILATE_ERODE_DISTANCE_THRESHOLD && get_inset() != 0.0f) { + return false; + } + + if (get_distance() == 0) { + return true; + } + + return false; + } + + int get_distance() + { + return bnode().custom2; + } + + float get_inset() + { + return bnode().custom3; + } + + CMPNodeDilateErodeMethod get_method() + { + return (CMPNodeDilateErodeMethod)bnode().custom1; } }; diff --git a/source/blender/nodes/composite/nodes/node_composite_directionalblur.cc b/source/blender/nodes/composite/nodes/node_composite_directionalblur.cc index eacba5ad12d..6e6bec70283 100644 --- a/source/blender/nodes/composite/nodes/node_composite_directionalblur.cc +++ b/source/blender/nodes/composite/nodes/node_composite_directionalblur.cc @@ -5,18 +5,30 @@ * \ingroup cmpnodes */ +#include "BLI_float3x3.hh" +#include "BLI_math_base.hh" +#include "BLI_math_vec_types.hh" +#include "BLI_math_vector.hh" + #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_shader.h" + #include "COM_node_operation.hh" +#include "COM_utilities.hh" #include "node_composite_util.hh" namespace blender::nodes::node_composite_directionalblur_cc { +NODE_STORAGE_FUNCS(NodeDBlurData) + static void cmp_node_directional_blur_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); b.add_output<decl::Color>(N_("Image")); } @@ -61,7 +73,113 @@ class DirectionalBlurOperation : public NodeOperation { void execute() override { - get_input("Image").pass_through(get_result("Image")); + if (is_identity()) { + get_input("Image").pass_through(get_result("Image")); + return; + } + + GPUShader *shader = shader_manager().get("compositor_directional_blur"); + GPU_shader_bind(shader); + + /* The number of iterations does not cover the original image, that is, the image with no + * transformation. So add an extra iteration for the original image and put that into + * consideration in the shader. */ + GPU_shader_uniform_1i(shader, "iterations", get_iterations() + 1); + GPU_shader_uniform_mat3_as_mat4(shader, "inverse_transformation", get_transformation().ptr()); + + const Result &input_image = get_input("Image"); + input_image.bind_as_texture(shader, "input_tx"); + + GPU_texture_filter_mode(input_image.texture(), true); + GPU_texture_wrap_mode(input_image.texture(), false, false); + + const Domain domain = compute_domain(); + Result &output_image = get_result("Image"); + output_image.allocate_texture(domain); + output_image.bind_as_image(shader, "output_img"); + + compute_dispatch_threads_at_least(shader, domain.size); + + GPU_shader_unbind(); + output_image.unbind_as_image(); + input_image.unbind_as_texture(); + } + + /* Get the amount of translation that will be applied on each iteration. The translation is in + * the negative x direction rotated in the clock-wise direction, hence the negative sign for the + * rotation and translation vector. */ + float2 get_translation() + { + const float diagonal_length = math::length(float2(get_input("Image").domain().size)); + const float translation_amount = diagonal_length * node_storage(bnode()).distance; + const float3x3 rotation = float3x3::from_rotation(-node_storage(bnode()).angle); + return rotation * float2(-translation_amount / get_iterations(), 0.0f); + } + + /* Get the amount of rotation that will be applied on each iteration. */ + float get_rotation() + { + return node_storage(bnode()).spin / get_iterations(); + } + + /* Get the amount of scale that will be applied on each iteration. The scale is identity when the + * user supplies 0, so we add 1. */ + float2 get_scale() + { + return float2(1.0f + node_storage(bnode()).zoom / get_iterations()); + } + + float2 get_origin() + { + const float2 center = float2(node_storage(bnode()).center_x, node_storage(bnode()).center_y); + return float2(get_input("Image").domain().size) * center; + } + + float3x3 get_transformation() + { + /* Construct the transformation that will be applied on each iteration. */ + const float3x3 transformation = float3x3::from_translation_rotation_scale( + get_translation(), get_rotation(), get_scale()); + /* Change the origin of the transformation to the user-specified origin. */ + const float3x3 origin_transformation = float3x3::from_origin_transformation(transformation, + get_origin()); + /* The shader will transform the coordinates, not the image itself, so take the inverse. */ + return origin_transformation.inverted(); + } + + /* The actual number of iterations is 2 to the power of the user supplied iterations. The power + * is implemented using a bit shift. But also make sure it doesn't exceed the upper limit which + * is the number of diagonal pixels. */ + int get_iterations() + { + const int iterations = 2 << (node_storage(bnode()).iter - 1); + const int upper_limit = math::ceil(math::length(float2(get_input("Image").domain().size))); + return math::min(iterations, upper_limit); + } + + /* Returns true if the operation does nothing and the input can be passed through. */ + bool is_identity() + { + const Result &input = get_input("Image"); + /* Single value inputs can't be blurred and are returned as is. */ + if (input.is_single_value()) { + return true; + } + + /* If any of the following options are non-zero, then the operation is not an identity. */ + if (node_storage(bnode()).distance != 0.0f) { + return false; + } + + if (node_storage(bnode()).spin != 0.0f) { + return false; + } + + if (node_storage(bnode()).zoom != 0.0f) { + return false; + } + + return true; } }; diff --git a/source/blender/nodes/composite/nodes/node_composite_distance_matte.cc b/source/blender/nodes/composite/nodes/node_composite_distance_matte.cc index 9d910b3f409..6a786571f43 100644 --- a/source/blender/nodes/composite/nodes/node_composite_distance_matte.cc +++ b/source/blender/nodes/composite/nodes/node_composite_distance_matte.cc @@ -18,6 +18,8 @@ namespace blender::nodes::node_composite_distance_matte_cc { +NODE_STORAGE_FUNCS(NodeChroma) + static void cmp_node_distance_matte_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Color>(N_("Image")) @@ -90,24 +92,19 @@ class DistanceMatteShaderNode : public ShaderNode { GPU_uniform(&falloff)); } - NodeChroma *get_node_chroma() - { - return static_cast<NodeChroma *>(bnode().storage); - } - CMPNodeDistanceMatteColorSpace get_color_space() { - return (CMPNodeDistanceMatteColorSpace)get_node_chroma()->channel; + return (CMPNodeDistanceMatteColorSpace)node_storage(bnode()).channel; } float get_tolerance() { - return get_node_chroma()->t1; + return node_storage(bnode()).t1; } float get_falloff() { - return get_node_chroma()->t2; + return node_storage(bnode()).t2; } }; diff --git a/source/blender/nodes/composite/nodes/node_composite_ellipsemask.cc b/source/blender/nodes/composite/nodes/node_composite_ellipsemask.cc index 54dfa00eadd..7c031b354e5 100644 --- a/source/blender/nodes/composite/nodes/node_composite_ellipsemask.cc +++ b/source/blender/nodes/composite/nodes/node_composite_ellipsemask.cc @@ -23,6 +23,8 @@ namespace blender::nodes::node_composite_ellipsemask_cc { +NODE_STORAGE_FUNCS(NodeEllipseMask) + static void cmp_node_ellipsemask_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Float>(N_("Mask")).default_value(0.0f).min(0.0f).max(1.0f); @@ -121,24 +123,19 @@ class EllipseMaskOperation : public NodeOperation { } } - NodeEllipseMask &get_node_ellipse_mask() - { - return *static_cast<NodeEllipseMask *>(bnode().storage); - } - float2 get_location() { - return float2(get_node_ellipse_mask().x, get_node_ellipse_mask().y); + return float2(node_storage(bnode()).x, node_storage(bnode()).y); } float2 get_size() { - return float2(get_node_ellipse_mask().width, get_node_ellipse_mask().height); + return float2(node_storage(bnode()).width, node_storage(bnode()).height); } float get_angle() { - return get_node_ellipse_mask().rotation; + return node_storage(bnode()).rotation; } }; diff --git a/source/blender/nodes/composite/nodes/node_composite_filter.cc b/source/blender/nodes/composite/nodes/node_composite_filter.cc index 854cf684806..bd7b443e17e 100644 --- a/source/blender/nodes/composite/nodes/node_composite_filter.cc +++ b/source/blender/nodes/composite/nodes/node_composite_filter.cc @@ -5,10 +5,13 @@ * \ingroup cmpnodes */ +#include "BLI_float3x3.hh" + #include "UI_interface.h" #include "UI_resources.h" #include "COM_node_operation.hh" +#include "COM_utilities.hh" #include "node_composite_util.hh" @@ -18,8 +21,15 @@ namespace blender::nodes::node_composite_filter_cc { static void cmp_node_filter_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Float>(N_("Fac")) + .default_value(1.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR) + .compositor_domain_priority(1); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); b.add_output<decl::Color>(N_("Image")); } @@ -36,7 +46,103 @@ class FilterOperation : public NodeOperation { void execute() override { - get_input("Image").pass_through(get_result("Image")); + GPUShader *shader = shader_manager().get(get_shader_name()); + GPU_shader_bind(shader); + + GPU_shader_uniform_mat3_as_mat4(shader, "kernel", get_filter_kernel().ptr()); + + const Result &input_image = get_input("Image"); + input_image.bind_as_texture(shader, "input_tx"); + + const Result &factor = get_input("Fac"); + factor.bind_as_texture(shader, "factor_tx"); + + const Domain domain = compute_domain(); + + Result &output_image = get_result("Image"); + output_image.allocate_texture(domain); + output_image.bind_as_image(shader, "output_img"); + + compute_dispatch_threads_at_least(shader, domain.size); + + input_image.unbind_as_texture(); + factor.unbind_as_texture(); + output_image.unbind_as_image(); + GPU_shader_unbind(); + } + + CMPNodeFilterMethod get_filter_method() + { + return (CMPNodeFilterMethod)bnode().custom1; + } + + float3x3 get_filter_kernel() + { + /* Initialize the kernels as arrays of rows with the top row first. Edge detection kernels + * return the kernel in the X direction, while the kernel in the Y direction will be computed + * inside the shader by transposing the kernel in the X direction. */ + switch (get_filter_method()) { + case CMP_NODE_FILTER_SOFT: { + const float kernel[3][3] = {{1.0f / 16.0f, 2.0f / 16.0f, 1.0f / 16.0f}, + {2.0f / 16.0f, 4.0f / 16.0f, 2.0f / 16.0f}, + {1.0f / 16.0f, 2.0f / 16.0f, 1.0f / 16.0f}}; + return float3x3(kernel); + } + case CMP_NODE_FILTER_SHARP_BOX: { + const float kernel[3][3] = { + {-1.0f, -1.0f, -1.0f}, {-1.0f, 9.0f, -1.0f}, {-1.0f, -1.0f, -1.0f}}; + return float3x3(kernel); + } + case CMP_NODE_FILTER_LAPLACE: { + const float kernel[3][3] = {{-1.0f / 8.0f, -1.0f / 8.0f, -1.0f / 8.0f}, + {-1.0f / 8.0f, 1.0f, -1.0f / 8.0f}, + {-1.0f / 8.0f, -1.0f / 8.0f, -1.0f / 8.0f}}; + return float3x3(kernel); + } + case CMP_NODE_FILTER_SOBEL: { + const float kernel[3][3] = {{1.0f, 0.0f, -1.0f}, {2.0f, 0.0f, -2.0f}, {1.0f, 0.0f, -1.0f}}; + return float3x3(kernel); + } + case CMP_NODE_FILTER_PREWITT: { + const float kernel[3][3] = {{1.0f, 0.0f, -1.0f}, {1.0f, 0.0f, -1.0f}, {1.0f, 0.0f, -1.0f}}; + return float3x3(kernel); + } + case CMP_NODE_FILTER_KIRSCH: { + const float kernel[3][3] = { + {5.0f, -3.0f, -2.0f}, {5.0f, -3.0f, -2.0f}, {5.0f, -3.0f, -2.0f}}; + return float3x3(kernel); + } + case CMP_NODE_FILTER_SHADOW: { + const float kernel[3][3] = {{1.0f, 2.0f, 1.0f}, {0.0f, 1.0f, 0.0f}, {-1.0f, -2.0f, -1.0f}}; + return float3x3(kernel); + } + case CMP_NODE_FILTER_SHARP_DIAMOND: { + const float kernel[3][3] = { + {0.0f, -1.0f, 0.0f}, {-1.0f, 5.0f, -1.0f}, {0.0f, -1.0f, 0.0f}}; + return float3x3(kernel); + } + default: { + const float kernel[3][3] = {{0.0f, 0.0f, 0.0f}, {0.0f, 1.0f, 0.0f}, {0.0f, 0.0f, 0.0f}}; + return float3x3(kernel); + } + } + } + + const char *get_shader_name() + { + switch (get_filter_method()) { + case CMP_NODE_FILTER_LAPLACE: + case CMP_NODE_FILTER_SOBEL: + case CMP_NODE_FILTER_PREWITT: + case CMP_NODE_FILTER_KIRSCH: + return "compositor_edge_filter"; + case CMP_NODE_FILTER_SOFT: + case CMP_NODE_FILTER_SHARP_BOX: + case CMP_NODE_FILTER_SHADOW: + case CMP_NODE_FILTER_SHARP_DIAMOND: + default: + return "compositor_filter"; + } } }; diff --git a/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc b/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc index a84420231aa..6333860a19b 100644 --- a/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc +++ b/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc @@ -59,7 +59,7 @@ class HueCorrectShaderNode : public ShaderNode { GPUNodeStack *inputs = get_inputs_array(); GPUNodeStack *outputs = get_outputs_array(); - CurveMapping *curve_mapping = get_curve_mapping(); + CurveMapping *curve_mapping = const_cast<CurveMapping *>(get_curve_mapping()); BKE_curvemapping_init(curve_mapping); float *band_values; @@ -84,9 +84,9 @@ class HueCorrectShaderNode : public ShaderNode { GPU_uniform(range_dividers)); } - CurveMapping *get_curve_mapping() + const CurveMapping *get_curve_mapping() { - return static_cast<CurveMapping *>(bnode().storage); + return static_cast<const CurveMapping *>(bnode().storage); } }; diff --git a/source/blender/nodes/composite/nodes/node_composite_image.cc b/source/blender/nodes/composite/nodes/node_composite_image.cc index d8852e9333f..4d1eff0b940 100644 --- a/source/blender/nodes/composite/nodes/node_composite_image.cc +++ b/source/blender/nodes/composite/nodes/node_composite_image.cc @@ -457,8 +457,8 @@ class ImageOperation : public NodeOperation { update_image_frame_number(); - for (const OutputSocketRef *output : node()->outputs()) { - compute_output(output->identifier()); + for (const bNodeSocket *output : this->node()->output_sockets()) { + compute_output(output->identifier); } } @@ -488,12 +488,12 @@ class ImageOperation : public NodeOperation { /* Allocate all needed outputs as invalid. This should be called when is_valid returns false. */ void allocate_invalid() { - for (const OutputSocketRef *output : node()->outputs()) { - if (!should_compute_output(output->identifier())) { + for (const bNodeSocket *output : this->node()->output_sockets()) { + if (!should_compute_output(output->identifier)) { continue; } - Result &result = get_result(output->identifier()); + Result &result = get_result(output->identifier); result.allocate_invalid(); } } @@ -535,7 +535,7 @@ class ImageOperation : public NodeOperation { /* Get a copy of the image user that is appropriate to retrieve the image buffer for the output * with the given identifier. This essentially sets the appropriate pass and view indices that - * corresponds to the output. */ + * corresponds to the output. */ ImageUser compute_image_user_for_output(StringRef identifier) { ImageUser image_user = *get_image_user(); @@ -594,7 +594,7 @@ class ImageOperation : public NodeOperation { const char *get_pass_name(StringRef identifier) { DOutputSocket output = node().output_by_identifier(identifier); - return static_cast<NodeImageLayer *>(output->bsocket()->storage)->pass_name; + return static_cast<NodeImageLayer *>(output->storage)->pass_name; } /* Get the index of the pass with the given name in the selected render layer's passes list @@ -850,9 +850,9 @@ class RenderLayerOperation : public NodeOperation { alpha_result.unbind_as_image(); /* Other output passes are not supported for now, so allocate them as invalid. */ - for (const OutputSocketRef *output : node()->outputs()) { - if (output->identifier() != "Image" && output->identifier() != "Alpha") { - get_result(output->identifier()).allocate_invalid(); + for (const bNodeSocket *output : this->node()->output_sockets()) { + if (!STREQ(output->identifier, "Image") && !STREQ(output->identifier, "Alpha")) { + get_result(output->identifier).allocate_invalid(); } } } diff --git a/source/blender/nodes/composite/nodes/node_composite_lensdist.cc b/source/blender/nodes/composite/nodes/node_composite_lensdist.cc index 2d4c0afcda7..260fccf66d0 100644 --- a/source/blender/nodes/composite/nodes/node_composite_lensdist.cc +++ b/source/blender/nodes/composite/nodes/node_composite_lensdist.cc @@ -32,6 +32,8 @@ namespace blender::nodes::node_composite_lensdist_cc { +NODE_STORAGE_FUNCS(NodeLensDist) + static void cmp_node_lensdist_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Color>(N_("Image")) @@ -197,22 +199,17 @@ class LensDistortionOperation : public NodeOperation { bool get_is_projector() { - return get_node_lens_distortion().proj; + return node_storage(bnode()).proj; } bool get_is_jitter() { - return get_node_lens_distortion().jit; + return node_storage(bnode()).jit; } bool get_is_fit() { - return get_node_lens_distortion().fit; - } - - NodeLensDist &get_node_lens_distortion() - { - return *static_cast<NodeLensDist *>(bnode().storage); + return node_storage(bnode()).fit; } /* Returns true if the operation does nothing and the input can be passed through. */ diff --git a/source/blender/nodes/composite/nodes/node_composite_luma_matte.cc b/source/blender/nodes/composite/nodes/node_composite_luma_matte.cc index 092a12a7ea4..59ae62ec411 100644 --- a/source/blender/nodes/composite/nodes/node_composite_luma_matte.cc +++ b/source/blender/nodes/composite/nodes/node_composite_luma_matte.cc @@ -20,6 +20,8 @@ namespace blender::nodes::node_composite_luma_matte_cc { +NODE_STORAGE_FUNCS(NodeChroma) + static void cmp_node_luma_matte_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Color>(N_("Image")) @@ -74,19 +76,14 @@ class LuminanceMatteShaderNode : public ShaderNode { GPU_constant(luminance_coefficients)); } - NodeChroma *get_node_chroma() - { - return static_cast<NodeChroma *>(bnode().storage); - } - float get_high() { - return get_node_chroma()->t1; + return node_storage(bnode()).t1; } float get_low() { - return get_node_chroma()->t2; + return node_storage(bnode()).t2; } }; diff --git a/source/blender/nodes/composite/nodes/node_composite_map_value.cc b/source/blender/nodes/composite/nodes/node_composite_map_value.cc index ec9b2d56636..e30de39605d 100644 --- a/source/blender/nodes/composite/nodes/node_composite_map_value.cc +++ b/source/blender/nodes/composite/nodes/node_composite_map_value.cc @@ -22,6 +22,8 @@ namespace blender::nodes::node_composite_map_value_cc { +NODE_STORAGE_FUNCS(TexMapping) + static void cmp_node_map_value_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Float>(N_("Value")) @@ -69,7 +71,7 @@ class MapValueShaderNode : public ShaderNode { GPUNodeStack *inputs = get_inputs_array(); GPUNodeStack *outputs = get_outputs_array(); - const TexMapping *texture_mapping = get_texture_mapping(); + const TexMapping &texture_mapping = node_storage(bnode()); const float use_min = get_use_min(); const float use_max = get_use_max(); @@ -79,27 +81,22 @@ class MapValueShaderNode : public ShaderNode { "node_composite_map_value", inputs, outputs, - GPU_uniform(texture_mapping->loc), - GPU_uniform(texture_mapping->size), + GPU_uniform(texture_mapping.loc), + GPU_uniform(texture_mapping.size), GPU_constant(&use_min), - GPU_uniform(texture_mapping->min), + GPU_uniform(texture_mapping.min), GPU_constant(&use_max), - GPU_uniform(texture_mapping->max)); - } - - TexMapping *get_texture_mapping() - { - return static_cast<TexMapping *>(bnode().storage); + GPU_uniform(texture_mapping.max)); } bool get_use_min() { - return get_texture_mapping()->flag & TEXMAP_CLIP_MIN; + return node_storage(bnode()).flag & TEXMAP_CLIP_MIN; } bool get_use_max() { - return get_texture_mapping()->flag & TEXMAP_CLIP_MAX; + return node_storage(bnode()).flag & TEXMAP_CLIP_MAX; } }; diff --git a/source/blender/nodes/composite/nodes/node_composite_movieclip.cc b/source/blender/nodes/composite/nodes/node_composite_movieclip.cc index ec95de3da18..b9d9620a214 100644 --- a/source/blender/nodes/composite/nodes/node_composite_movieclip.cc +++ b/source/blender/nodes/composite/nodes/node_composite_movieclip.cc @@ -239,7 +239,7 @@ class MovieClipOperation : public NodeOperation { GPUTexture *get_movie_clip_texture() { MovieClip *movie_clip = get_movie_clip(); - MovieClipUser *movie_clip_user = static_cast<MovieClipUser *>(bnode().storage); + MovieClipUser *movie_clip_user = get_movie_clip_user(); BKE_movieclip_user_set_frame(movie_clip_user, context().get_frame_number()); return BKE_movieclip_get_gpu_texture(movie_clip, movie_clip_user); } @@ -247,13 +247,20 @@ class MovieClipOperation : public NodeOperation { void free_movie_clip_texture() { MovieClip *movie_clip = get_movie_clip(); - return BKE_movieclip_free_gputexture(movie_clip); + if (movie_clip) { + BKE_movieclip_free_gputexture(movie_clip); + } } MovieClip *get_movie_clip() { return (MovieClip *)bnode().id; } + + MovieClipUser *get_movie_clip_user() + { + return static_cast<MovieClipUser *>(bnode().storage); + } }; static NodeOperation *get_compositor_operation(Context &context, DNode node) diff --git a/source/blender/nodes/composite/nodes/node_composite_normal.cc b/source/blender/nodes/composite/nodes/node_composite_normal.cc index f61ace01cfd..a1a6303e21b 100644 --- a/source/blender/nodes/composite/nodes/node_composite_normal.cc +++ b/source/blender/nodes/composite/nodes/node_composite_normal.cc @@ -51,9 +51,12 @@ class NormalShaderNode : public ShaderNode { } /* The vector value is stored in the default value of the output socket. */ - float *get_vector_value() + const float *get_vector_value() { - return node().output_by_identifier("Normal")->default_value<bNodeSocketValueVector>()->value; + return node() + .output_by_identifier("Normal") + ->default_value_typed<bNodeSocketValueVector>() + ->value; } }; diff --git a/source/blender/nodes/composite/nodes/node_composite_pixelate.cc b/source/blender/nodes/composite/nodes/node_composite_pixelate.cc index 4567464a547..c4e42f8247d 100644 --- a/source/blender/nodes/composite/nodes/node_composite_pixelate.cc +++ b/source/blender/nodes/composite/nodes/node_composite_pixelate.cc @@ -5,6 +5,9 @@ * \ingroup cmpnodes */ +#include "BLI_math_vec_types.hh" +#include "BLI_math_vector.hh" + #include "COM_node_operation.hh" #include "node_composite_util.hh" @@ -27,8 +30,34 @@ class PixelateOperation : public NodeOperation { void execute() override { + /* It might seems strange that the input is passed through without any processing, but note + * that the actual processing happens inside the domain realization input processor of the + * input. Indeed, the pixelate node merely realizes its input on a smaller-sized domain that + * matches its apparent size, that is, its size after the domain transformation. The pixelate + * node has no effect if the input is scaled-up. See the compute_domain method for more + * information. */ get_input("Color").pass_through(get_result("Color")); } + + /* Compute a smaller-sized domain that matches the apparent size of the input while having a unit + * scale transformation, see the execute method for more information. */ + Domain compute_domain() override + { + Domain domain = get_input("Color").domain(); + + /* Get the scaling component of the domain transformation, but make sure it doesn't exceed 1, + * because pixelation should only happen if the input is scaled down. */ + const float2 scale = math::min(float2(1.0f), domain.transformation.scale_2d()); + + /* Multiply the size of the domain by its scale to match its apparent size, but make sure it is + * at least 1 pixel in both axis. */ + domain.size = math::max(int2(float2(domain.size) * scale), int2(1)); + + /* Reset the scale of the transformation by transforming it with the inverse of the scale. */ + domain.transformation *= float3x3::from_scale(math::safe_divide(float2(1.0f), scale)); + + return domain; + } }; static NodeOperation *get_compositor_operation(Context &context, DNode node) diff --git a/source/blender/nodes/composite/nodes/node_composite_rgb.cc b/source/blender/nodes/composite/nodes/node_composite_rgb.cc index 6f3a00af7e3..f107961f301 100644 --- a/source/blender/nodes/composite/nodes/node_composite_rgb.cc +++ b/source/blender/nodes/composite/nodes/node_composite_rgb.cc @@ -33,8 +33,8 @@ class RGBOperation : public NodeOperation { Result &result = get_result("RGBA"); result.allocate_single_value(); - const bNodeSocket *socket = static_cast<bNodeSocket *>(bnode().outputs.first); - float4 color = float4(static_cast<bNodeSocketValueRGBA *>(socket->default_value)->value); + const bNodeSocket *socket = static_cast<const bNodeSocket *>(bnode().outputs.first); + float4 color = float4(static_cast<const bNodeSocketValueRGBA *>(socket->default_value)->value); result.set_color_value(color); } diff --git a/source/blender/nodes/composite/nodes/node_composite_scale.cc b/source/blender/nodes/composite/nodes/node_composite_scale.cc index 8b43ae8c9ca..eb2d7162c69 100644 --- a/source/blender/nodes/composite/nodes/node_composite_scale.cc +++ b/source/blender/nodes/composite/nodes/node_composite_scale.cc @@ -5,6 +5,11 @@ * \ingroup cmpnodes */ +#include "BLI_assert.h" +#include "BLI_float3x3.hh" +#include "BLI_math_base.hh" +#include "BLI_math_vec_types.hh" + #include "RNA_access.h" #include "UI_interface.h" @@ -20,16 +25,26 @@ namespace blender::nodes::node_composite_scale_cc { static void cmp_node_scale_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input<decl::Float>(N_("X")).default_value(1.0f).min(0.0001f).max(CMP_SCALE_MAX); - b.add_input<decl::Float>(N_("Y")).default_value(1.0f).min(0.0001f).max(CMP_SCALE_MAX); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); + b.add_input<decl::Float>(N_("X")) + .default_value(1.0f) + .min(0.0001f) + .max(CMP_SCALE_MAX) + .compositor_expects_single_value(); + b.add_input<decl::Float>(N_("Y")) + .default_value(1.0f) + .min(0.0001f) + .max(CMP_SCALE_MAX) + .compositor_expects_single_value(); b.add_output<decl::Color>(N_("Image")); } static void node_composite_update_scale(bNodeTree *ntree, bNode *node) { bNodeSocket *sock; - bool use_xy_scale = ELEM(node->custom1, CMP_SCALE_RELATIVE, CMP_SCALE_ABSOLUTE); + bool use_xy_scale = ELEM(node->custom1, CMP_NODE_SCALE_RELATIVE, CMP_NODE_SCALE_ABSOLUTE); /* Only show X/Y scale factor inputs for modes using them! */ for (sock = (bNodeSocket *)node->inputs.first; sock; sock = sock->next) { @@ -43,7 +58,7 @@ static void node_composit_buts_scale(uiLayout *layout, bContext *UNUSED(C), Poin { uiItemR(layout, ptr, "space", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); - if (RNA_enum_get(ptr, "space") == CMP_SCALE_RENDERPERCENT) { + if (RNA_enum_get(ptr, "space") == CMP_NODE_SCALE_RENDER_SIZE) { uiLayout *row; uiItemR(layout, ptr, @@ -65,7 +80,129 @@ class ScaleOperation : public NodeOperation { void execute() override { - get_input("Image").pass_through(get_result("Image")); + Result &input = get_input("Image"); + Result &result = get_result("Image"); + input.pass_through(result); + + const float3x3 transformation = float3x3::from_translation_rotation_scale( + get_translation(), 0.0f, get_scale()); + + result.transform(transformation); + result.get_realization_options().interpolation = Interpolation::Bilinear; + } + + float2 get_scale() + { + switch (get_scale_method()) { + case CMP_NODE_SCALE_RELATIVE: + return get_scale_relative(); + case CMP_NODE_SCALE_ABSOLUTE: + return get_scale_absolute(); + case CMP_NODE_SCALE_RENDER_PERCENT: + return get_scale_render_percent(); + case CMP_NODE_SCALE_RENDER_SIZE: + return get_scale_render_size(); + default: + BLI_assert_unreachable(); + return float2(1.0f); + } + } + + /* Scale by the input factors. */ + float2 get_scale_relative() + { + return float2(get_input("X").get_float_value_default(1.0f), + get_input("Y").get_float_value_default(1.0f)); + } + + /* Scale such that the new size matches the input absolute size. */ + float2 get_scale_absolute() + { + const float2 input_size = float2(get_input("Image").domain().size); + const float2 absolute_size = float2(get_input("X").get_float_value_default(1.0f), + get_input("Y").get_float_value_default(1.0f)); + return absolute_size / input_size; + } + + /* Scale by the render resolution percentage. */ + float2 get_scale_render_percent() + { + return float2(context().get_scene()->r.size / 100.0f); + } + + float2 get_scale_render_size() + { + switch (get_scale_render_size_method()) { + case CMP_NODE_SCALE_RENDER_SIZE_STRETCH: + return get_scale_render_size_stretch(); + case CMP_NODE_SCALE_RENDER_SIZE_FIT: + return get_scale_render_size_fit(); + case CMP_NODE_SCALE_RENDER_SIZE_CROP: + return get_scale_render_size_crop(); + default: + BLI_assert_unreachable(); + return float2(1.0f); + } + } + + /* Scale such that the new size matches the render size. Since the input is freely scaled, it is + * potentially stretched, hence the name. */ + float2 get_scale_render_size_stretch() + { + const float2 input_size = float2(get_input("Image").domain().size); + const float2 render_size = float2(context().get_output_size()); + return render_size / input_size; + } + + /* Scale such that the dimension with the smaller scaling factor matches that of the render size + * while maintaining the input's aspect ratio. Since the other dimension is guaranteed not to + * exceed the render size region due to its larger scaling factor, the image is said to be fit + * inside that region, hence the name. */ + float2 get_scale_render_size_fit() + { + const float2 input_size = float2(get_input("Image").domain().size); + const float2 render_size = float2(context().get_output_size()); + const float2 scale = render_size / input_size; + return float2(math::min(scale.x, scale.y)); + } + + /* Scale such that the dimension with the larger scaling factor matches that of the render size + * while maintaining the input's aspect ratio. Since the other dimension is guaranteed to exceed + * the render size region due to its lower scaling factor, the image will be cropped inside that + * region, hence the name. */ + float2 get_scale_render_size_crop() + { + const float2 input_size = float2(get_input("Image").domain().size); + const float2 render_size = float2(context().get_output_size()); + const float2 scale = render_size / input_size; + return float2(math::max(scale.x, scale.y)); + } + + float2 get_translation() + { + /* Only the render size option supports offset translation. */ + if (get_scale_method() != CMP_NODE_SCALE_RENDER_SIZE) { + return float2(0.0f); + } + + /* Translate by the offset factor relative to the new size. */ + const float2 input_size = float2(get_input("Image").domain().size); + return get_offset() * input_size * get_scale(); + } + + CMPNodeScaleMethod get_scale_method() + { + return (CMPNodeScaleMethod)bnode().custom1; + } + + CMPNodeScaleRenderSizeMethod get_scale_render_size_method() + { + return (CMPNodeScaleRenderSizeMethod)bnode().custom2; + } + + float2 get_offset() + { + return float2(bnode().custom3, bnode().custom4); } }; diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_color.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_color.cc index d1f0b7977f8..f6792d7ce61 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_color.cc +++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_color.cc @@ -62,6 +62,8 @@ static void node_cmp_combsep_color_label(const ListBase *sockets, CMPNodeCombSep namespace blender::nodes::node_composite_separate_color_cc { +NODE_STORAGE_FUNCS(NodeCMPCombSepColor) + static void cmp_node_separate_color_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Color>(N_("Image")) @@ -93,14 +95,9 @@ class SeparateColorShaderNode : public ShaderNode { GPU_stack_link(material, &bnode(), get_shader_function_name(), inputs, outputs); } - NodeCMPCombSepColor *get_node_combine_separate_color() - { - return static_cast<NodeCMPCombSepColor *>(bnode().storage); - } - const char *get_shader_function_name() { - switch (get_node_combine_separate_color()->mode) { + switch (node_storage(bnode()).mode) { case CMP_NODE_COMBSEP_COLOR_RGB: return "node_composite_separate_rgba"; case CMP_NODE_COMBSEP_COLOR_HSV: @@ -110,7 +107,7 @@ class SeparateColorShaderNode : public ShaderNode { case CMP_NODE_COMBSEP_COLOR_YUV: return "node_composite_separate_yuva_itu_709"; case CMP_NODE_COMBSEP_COLOR_YCC: - switch (get_node_combine_separate_color()->ycc_mode) { + switch (node_storage(bnode()).ycc_mode) { case BLI_YCC_ITU_BT601: return "node_composite_separate_ycca_itu_601"; case BLI_YCC_ITU_BT709: @@ -153,6 +150,8 @@ void register_node_type_cmp_separate_color() namespace blender::nodes::node_composite_combine_color_cc { +NODE_STORAGE_FUNCS(NodeCMPCombSepColor) + static void cmp_node_combine_color_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Float>(N_("Red")) @@ -202,14 +201,9 @@ class CombineColorShaderNode : public ShaderNode { GPU_stack_link(material, &bnode(), get_shader_function_name(), inputs, outputs); } - NodeCMPCombSepColor *get_node_combine_separate_color() - { - return static_cast<NodeCMPCombSepColor *>(bnode().storage); - } - const char *get_shader_function_name() { - switch (get_node_combine_separate_color()->mode) { + switch (node_storage(bnode()).mode) { case CMP_NODE_COMBSEP_COLOR_RGB: return "node_composite_combine_rgba"; case CMP_NODE_COMBSEP_COLOR_HSV: @@ -219,7 +213,7 @@ class CombineColorShaderNode : public ShaderNode { case CMP_NODE_COMBSEP_COLOR_YUV: return "node_composite_combine_yuva_itu_709"; case CMP_NODE_COMBSEP_COLOR_YCC: - switch (get_node_combine_separate_color()->ycc_mode) { + switch (node_storage(bnode()).ycc_mode) { case BLI_YCC_ITU_BT601: return "node_composite_combine_ycca_itu_601"; case BLI_YCC_ITU_BT709: diff --git a/source/blender/nodes/composite/nodes/node_composite_setalpha.cc b/source/blender/nodes/composite/nodes/node_composite_setalpha.cc index 9930125aa70..df3aca2c665 100644 --- a/source/blender/nodes/composite/nodes/node_composite_setalpha.cc +++ b/source/blender/nodes/composite/nodes/node_composite_setalpha.cc @@ -18,6 +18,8 @@ namespace blender::nodes::node_composite_setalpha_cc { +NODE_STORAGE_FUNCS(NodeSetAlpha) + static void cmp_node_setalpha_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Color>(N_("Image")) @@ -54,18 +56,13 @@ class SetAlphaShaderNode : public ShaderNode { GPUNodeStack *inputs = get_inputs_array(); GPUNodeStack *outputs = get_outputs_array(); - if (get_node_set_alpha()->mode == CMP_NODE_SETALPHA_MODE_APPLY) { + if (node_storage(bnode()).mode == CMP_NODE_SETALPHA_MODE_APPLY) { GPU_stack_link(material, &bnode(), "node_composite_set_alpha_apply", inputs, outputs); return; } GPU_stack_link(material, &bnode(), "node_composite_set_alpha_replace", inputs, outputs); } - - NodeSetAlpha *get_node_set_alpha() - { - return static_cast<NodeSetAlpha *>(bnode().storage); - } }; static ShaderNode *get_compositor_shader_node(DNode node) diff --git a/source/blender/nodes/composite/nodes/node_composite_translate.cc b/source/blender/nodes/composite/nodes/node_composite_translate.cc index fbd53b8310f..e0f87ff604a 100644 --- a/source/blender/nodes/composite/nodes/node_composite_translate.cc +++ b/source/blender/nodes/composite/nodes/node_composite_translate.cc @@ -19,6 +19,8 @@ namespace blender::nodes::node_composite_translate_cc { +NODE_STORAGE_FUNCS(NodeTranslateData) + static void cmp_node_translate_declare(NodeDeclarationBuilder &b) { b.add_input<decl::Color>(N_("Image")) @@ -76,24 +78,19 @@ class TranslateOperation : public NodeOperation { result.get_realization_options().repeat_y = get_repeat_y(); } - NodeTranslateData &get_node_translate() - { - return *static_cast<NodeTranslateData *>(bnode().storage); - } - bool get_use_relative() { - return get_node_translate().relative; + return node_storage(bnode()).relative; } bool get_repeat_x() { - return ELEM(get_node_translate().wrap_axis, CMP_NODE_WRAP_X, CMP_NODE_WRAP_XY); + return ELEM(node_storage(bnode()).wrap_axis, CMP_NODE_WRAP_X, CMP_NODE_WRAP_XY); } bool get_repeat_y() { - return ELEM(get_node_translate().wrap_axis, CMP_NODE_WRAP_Y, CMP_NODE_WRAP_XY); + return ELEM(node_storage(bnode()).wrap_axis, CMP_NODE_WRAP_Y, CMP_NODE_WRAP_XY); } }; diff --git a/source/blender/nodes/function/node_function_util.hh b/source/blender/nodes/function/node_function_util.hh index fd0b6c31b1d..059b2f9bc17 100644 --- a/source/blender/nodes/function/node_function_util.hh +++ b/source/blender/nodes/function/node_function_util.hh @@ -23,4 +23,6 @@ #include "FN_multi_function_builder.hh" +#include "RNA_access.h" + void fn_node_type_base(struct bNodeType *ntype, int type, const char *name, short nclass); diff --git a/source/blender/nodes/function/nodes/node_fn_align_euler_to_vector.cc b/source/blender/nodes/function/nodes/node_fn_align_euler_to_vector.cc index 7d08d57c503..e5c89567d44 100644 --- a/source/blender/nodes/function/nodes/node_fn_align_euler_to_vector.cc +++ b/source/blender/nodes/function/nodes/node_fn_align_euler_to_vector.cc @@ -190,7 +190,7 @@ class MF_AlignEulerToVector : public fn::MultiFunction { static void fn_node_align_euler_to_vector_build_multi_function(NodeMultiFunctionBuilder &builder) { - bNode &node = builder.node(); + const bNode &node = builder.node(); builder.construct_and_set_matching_fn<MF_AlignEulerToVector>(node.custom1, node.custom2); } diff --git a/source/blender/nodes/function/nodes/node_fn_boolean_math.cc b/source/blender/nodes/function/nodes/node_fn_boolean_math.cc index b6d7e6c9a5f..5fc28509a49 100644 --- a/source/blender/nodes/function/nodes/node_fn_boolean_math.cc +++ b/source/blender/nodes/function/nodes/node_fn_boolean_math.cc @@ -68,7 +68,7 @@ static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) } } -static const fn::MultiFunction *get_multi_function(bNode &bnode) +static const fn::MultiFunction *get_multi_function(const bNode &bnode) { static auto exec_preset = fn::CustomMF_presets::AllSpanOrSingle(); static fn::CustomMF_SI_SI_SO<bool, bool, bool> and_fn{ diff --git a/source/blender/nodes/function/nodes/node_fn_combine_color.cc b/source/blender/nodes/function/nodes/node_fn_combine_color.cc index c5fd3ce38a1..450cd166e78 100644 --- a/source/blender/nodes/function/nodes/node_fn_combine_color.cc +++ b/source/blender/nodes/function/nodes/node_fn_combine_color.cc @@ -49,7 +49,7 @@ static void fn_node_combine_color_init(bNodeTree *UNUSED(tree), bNode *node) node->storage = data; } -static const fn::MultiFunction *get_multi_function(bNode &bnode) +static const fn::MultiFunction *get_multi_function(const bNode &bnode) { const NodeCombSepColor &storage = node_storage(bnode); diff --git a/source/blender/nodes/function/nodes/node_fn_compare.cc b/source/blender/nodes/function/nodes/node_fn_compare.cc index e3f13dc7d6b..122d1a3c93e 100644 --- a/source/blender/nodes/function/nodes/node_fn_compare.cc +++ b/source/blender/nodes/function/nodes/node_fn_compare.cc @@ -167,7 +167,7 @@ static float component_average(float3 a) return (a.x + a.y + a.z) / 3.0f; } -static const fn::MultiFunction *get_multi_function(bNode &node) +static const fn::MultiFunction *get_multi_function(const bNode &node) { const NodeFunctionCompare *data = (NodeFunctionCompare *)node.storage; diff --git a/source/blender/nodes/function/nodes/node_fn_float_to_int.cc b/source/blender/nodes/function/nodes/node_fn_float_to_int.cc index 9c9d8620a7e..aad2f532d20 100644 --- a/source/blender/nodes/function/nodes/node_fn_float_to_int.cc +++ b/source/blender/nodes/function/nodes/node_fn_float_to_int.cc @@ -39,7 +39,7 @@ static void node_float_to_int_label(const bNodeTree *UNUSED(ntree), BLI_strncpy(label, IFACE_(name), maxlen); } -static const fn::MultiFunction *get_multi_function(bNode &bnode) +static const fn::MultiFunction *get_multi_function(const bNode &bnode) { static auto exec_preset = fn::CustomMF_presets::AllSpanOrSingle(); static fn::CustomMF_SI_SO<float, int> round_fn{ diff --git a/source/blender/nodes/function/nodes/node_fn_input_bool.cc b/source/blender/nodes/function/nodes/node_fn_input_bool.cc index 5ced719627f..717f4d1ac6b 100644 --- a/source/blender/nodes/function/nodes/node_fn_input_bool.cc +++ b/source/blender/nodes/function/nodes/node_fn_input_bool.cc @@ -22,7 +22,7 @@ static void fn_node_input_bool_layout(uiLayout *layout, bContext *UNUSED(C), Poi static void fn_node_input_bool_build_multi_function(NodeMultiFunctionBuilder &builder) { - bNode &bnode = builder.node(); + const bNode &bnode = builder.node(); NodeInputBool *node_storage = static_cast<NodeInputBool *>(bnode.storage); builder.construct_and_set_matching_fn<fn::CustomMF_Constant<bool>>(node_storage->boolean); } diff --git a/source/blender/nodes/function/nodes/node_fn_input_color.cc b/source/blender/nodes/function/nodes/node_fn_input_color.cc index 46787f7575d..cdad1542c66 100644 --- a/source/blender/nodes/function/nodes/node_fn_input_color.cc +++ b/source/blender/nodes/function/nodes/node_fn_input_color.cc @@ -23,7 +23,7 @@ static void fn_node_input_color_layout(uiLayout *layout, bContext *UNUSED(C), Po static void fn_node_input_color_build_multi_function( blender::nodes::NodeMultiFunctionBuilder &builder) { - bNode &bnode = builder.node(); + const bNode &bnode = builder.node(); NodeInputColor *node_storage = static_cast<NodeInputColor *>(bnode.storage); blender::ColorGeometry4f color = (ColorGeometry4f)node_storage->color; builder.construct_and_set_matching_fn<blender::fn::CustomMF_Constant<ColorGeometry4f>>(color); diff --git a/source/blender/nodes/function/nodes/node_fn_input_int.cc b/source/blender/nodes/function/nodes/node_fn_input_int.cc index 1e5dcd5ae7a..16506b5f9b8 100644 --- a/source/blender/nodes/function/nodes/node_fn_input_int.cc +++ b/source/blender/nodes/function/nodes/node_fn_input_int.cc @@ -22,7 +22,7 @@ static void fn_node_input_int_layout(uiLayout *layout, bContext *UNUSED(C), Poin static void fn_node_input_int_build_multi_function(NodeMultiFunctionBuilder &builder) { - bNode &bnode = builder.node(); + const bNode &bnode = builder.node(); NodeInputInt *node_storage = static_cast<NodeInputInt *>(bnode.storage); builder.construct_and_set_matching_fn<fn::CustomMF_Constant<int>>(node_storage->integer); } diff --git a/source/blender/nodes/function/nodes/node_fn_input_string.cc b/source/blender/nodes/function/nodes/node_fn_input_string.cc index 124a8572f78..129d19f4f04 100644 --- a/source/blender/nodes/function/nodes/node_fn_input_string.cc +++ b/source/blender/nodes/function/nodes/node_fn_input_string.cc @@ -20,7 +20,7 @@ static void fn_node_input_string_layout(uiLayout *layout, bContext *UNUSED(C), P static void fn_node_input_string_build_multi_function(NodeMultiFunctionBuilder &builder) { - bNode &bnode = builder.node(); + const bNode &bnode = builder.node(); NodeInputString *node_storage = static_cast<NodeInputString *>(bnode.storage); std::string string = std::string((node_storage->string) ? node_storage->string : ""); builder.construct_and_set_matching_fn<fn::CustomMF_Constant<std::string>>(std::move(string)); diff --git a/source/blender/nodes/function/nodes/node_fn_input_vector.cc b/source/blender/nodes/function/nodes/node_fn_input_vector.cc index 898c19e92f0..de894a4038d 100644 --- a/source/blender/nodes/function/nodes/node_fn_input_vector.cc +++ b/source/blender/nodes/function/nodes/node_fn_input_vector.cc @@ -22,7 +22,7 @@ static void fn_node_input_vector_layout(uiLayout *layout, bContext *UNUSED(C), P static void fn_node_input_vector_build_multi_function(NodeMultiFunctionBuilder &builder) { - bNode &bnode = builder.node(); + const bNode &bnode = builder.node(); NodeInputVector *node_storage = static_cast<NodeInputVector *>(bnode.storage); float3 vector(node_storage->vector); builder.construct_and_set_matching_fn<fn::CustomMF_Constant<float3>>(vector); diff --git a/source/blender/nodes/function/nodes/node_fn_rotate_euler.cc b/source/blender/nodes/function/nodes/node_fn_rotate_euler.cc index a4fc1a6bfd1..299c0f7a932 100644 --- a/source/blender/nodes/function/nodes/node_fn_rotate_euler.cc +++ b/source/blender/nodes/function/nodes/node_fn_rotate_euler.cc @@ -52,7 +52,7 @@ static void fn_node_rotate_euler_layout(uiLayout *layout, bContext *UNUSED(C), P uiItemR(layout, ptr, "space", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); } -static const fn::MultiFunction *get_multi_function(bNode &bnode) +static const fn::MultiFunction *get_multi_function(const bNode &bnode) { static fn::CustomMF_SI_SI_SO<float3, float3, float3> obj_euler_rot{ "Rotate Euler by Euler/Object", [](const float3 &input, const float3 &rotation) { diff --git a/source/blender/nodes/geometry/CMakeLists.txt b/source/blender/nodes/geometry/CMakeLists.txt index 31c00cc6b82..19a325eca89 100644 --- a/source/blender/nodes/geometry/CMakeLists.txt +++ b/source/blender/nodes/geometry/CMakeLists.txt @@ -59,6 +59,7 @@ set(SRC nodes/node_geo_curve_trim.cc nodes/node_geo_deform_curves_on_surface.cc nodes/node_geo_delete_geometry.cc + nodes/node_geo_distribute_points_in_volume.cc nodes/node_geo_distribute_points_on_faces.cc nodes/node_geo_dual_mesh.cc nodes/node_geo_duplicate_elements.cc @@ -105,6 +106,7 @@ set(SRC nodes/node_geo_material_replace.cc nodes/node_geo_material_selection.cc nodes/node_geo_merge_by_distance.cc + nodes/node_geo_mesh_face_set_boundaries.cc nodes/node_geo_mesh_primitive_circle.cc nodes/node_geo_mesh_primitive_cone.cc nodes/node_geo_mesh_primitive_cube.cc @@ -126,10 +128,14 @@ set(SRC nodes/node_geo_realize_instances.cc nodes/node_geo_remove_attribute.cc nodes/node_geo_rotate_instances.cc + nodes/node_geo_sample_index.cc + nodes/node_geo_sample_nearest_surface.cc + nodes/node_geo_sample_nearest.cc nodes/node_geo_scale_elements.cc nodes/node_geo_scale_instances.cc nodes/node_geo_separate_components.cc nodes/node_geo_separate_geometry.cc + nodes/node_geo_self_object.cc nodes/node_geo_set_curve_handles.cc nodes/node_geo_set_curve_radius.cc nodes/node_geo_set_curve_tilt.cc @@ -146,7 +152,6 @@ set(SRC nodes/node_geo_string_to_curves.cc nodes/node_geo_subdivision_surface.cc nodes/node_geo_switch.cc - nodes/node_geo_transfer_attribute.cc nodes/node_geo_transform.cc nodes/node_geo_translate_instances.cc nodes/node_geo_triangulate.cc @@ -187,20 +192,6 @@ if(WITH_BULLET) add_definitions(-DWITH_BULLET) endif() -if(WITH_PYTHON) - list(APPEND INC - ../../python - ) - list(APPEND INC_SYS - ${PYTHON_INCLUDE_DIRS} - ) - list(APPEND LIB - ${PYTHON_LINKFLAGS} - ${PYTHON_LIBRARIES} - ) - add_definitions(-DWITH_PYTHON) -endif() - if(WITH_TBB) list(APPEND INC_SYS ${TBB_INCLUDE_DIRS} diff --git a/source/blender/nodes/geometry/node_geometry_exec.cc b/source/blender/nodes/geometry/node_geometry_exec.cc index 58ded7aadd2..ef4daf94bbe 100644 --- a/source/blender/nodes/geometry/node_geometry_exec.cc +++ b/source/blender/nodes/geometry/node_geometry_exec.cc @@ -4,3 +4,4 @@ #include "NOD_geometry_exec.hh" BLI_CPP_TYPE_MAKE(GeometrySet, GeometrySet, CPPTypeFlags::Printable); +BLI_CPP_TYPE_MAKE(GeometrySetVector, blender::Vector<GeometrySet>, CPPTypeFlags::None); diff --git a/source/blender/nodes/geometry/node_geometry_tree.cc b/source/blender/nodes/geometry/node_geometry_tree.cc index e3998322741..1a12d2c49e6 100644 --- a/source/blender/nodes/geometry/node_geometry_tree.cc +++ b/source/blender/nodes/geometry/node_geometry_tree.cc @@ -7,6 +7,7 @@ #include "NOD_geometry.h" #include "BKE_context.h" +#include "BKE_layer.h" #include "BKE_node.h" #include "BKE_object.h" @@ -31,8 +32,10 @@ static void geometry_node_tree_get_from_context(const bContext *C, ID **r_id, ID **r_from) { + const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); - Object *ob = OBACT(view_layer); + BKE_view_layer_synced_ensure(scene, view_layer); + Object *ob = BKE_view_layer_active_object_get(view_layer); if (ob == nullptr) { return; diff --git a/source/blender/nodes/geometry/node_geometry_util.hh b/source/blender/nodes/geometry/node_geometry_util.hh index efb7efaf1cc..5aeb68b3fdc 100644 --- a/source/blender/nodes/geometry/node_geometry_util.hh +++ b/source/blender/nodes/geometry/node_geometry_util.hh @@ -20,8 +20,12 @@ #include "NOD_socket_declarations.hh" #include "NOD_socket_declarations_geometry.hh" +#include "RNA_access.h" + #include "node_util.h" +struct BVHTreeFromMesh; + void geo_node_type_base(struct bNodeType *ntype, int type, const char *name, short nclass); bool geo_node_poll_default(struct bNodeType *ntype, struct bNodeTree *ntree, @@ -34,7 +38,8 @@ void transform_mesh(Mesh &mesh, const float3 rotation, const float3 scale); -void transform_geometry_set(GeometrySet &geometry, +void transform_geometry_set(GeoNodeExecParams ¶ms, + GeometrySet &geometry, const float4x4 &transform, const Depsgraph &depsgraph); @@ -75,6 +80,13 @@ void separate_geometry(GeometrySet &geometry_set, const Field<bool> &selection_field, bool &r_is_error); +void get_closest_in_bvhtree(BVHTreeFromMesh &tree_data, + const VArray<float3> &positions, + const IndexMask mask, + const MutableSpan<int> r_indices, + const MutableSpan<float> r_distances_sq, + const MutableSpan<float3> r_positions); + std::optional<eCustomDataType> node_data_type_to_custom_data_type(eNodeSocketDatatype type); std::optional<eCustomDataType> node_socket_to_custom_data_type(const bNodeSocket &socket); diff --git a/source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc b/source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc index 58fbfb5a000..13a9cdc8600 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc @@ -192,7 +192,7 @@ static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) } } -template<typename T> class AccumulateFieldInput final : public GeometryFieldInput { +template<typename T> class AccumulateFieldInput final : public bke::GeometryFieldInput { private: Field<T> input_; Field<int> group_index_; @@ -204,7 +204,7 @@ template<typename T> class AccumulateFieldInput final : public GeometryFieldInpu Field<T> input, Field<int> group_index, AccumulationMode accumulation_mode) - : GeometryFieldInput(CPPType::get<T>(), "Accumulation"), + : bke::GeometryFieldInput(CPPType::get<T>(), "Accumulation"), input_(input), group_index_(group_index), source_domain_(source_domain), @@ -212,18 +212,18 @@ template<typename T> class AccumulateFieldInput final : public GeometryFieldInpu { } - GVArray get_varray_for_context(const GeometryComponent &component, - const eAttrDomain domain, - IndexMask UNUSED(mask)) const final + GVArray get_varray_for_context(const bke::GeometryFieldContext &context, + const IndexMask /*mask*/) const final { - const GeometryComponentFieldContext field_context{component, source_domain_}; - const int domain_size = component.attribute_domain_size(field_context.domain()); + const AttributeAccessor attributes = *context.attributes(); + const int domain_size = attributes.domain_size(source_domain_); if (domain_size == 0) { return {}; } - const AttributeAccessor attributes = *component.attributes(); - fn::FieldEvaluator evaluator{field_context, domain_size}; + const bke::GeometryFieldContext source_context{ + context.geometry(), context.type(), source_domain_}; + fn::FieldEvaluator evaluator{source_context, domain_size}; evaluator.add(input_); evaluator.add(group_index_); evaluator.evaluate(); @@ -266,7 +266,7 @@ template<typename T> class AccumulateFieldInput final : public GeometryFieldInpu } return attributes.adapt_domain<T>( - VArray<T>::ForContainer(std::move(accumulations_out)), source_domain_, domain); + VArray<T>::ForContainer(std::move(accumulations_out)), source_domain_, context.domain()); } uint64_t hash() const override @@ -287,7 +287,7 @@ template<typename T> class AccumulateFieldInput final : public GeometryFieldInpu } }; -template<typename T> class TotalFieldInput final : public GeometryFieldInput { +template<typename T> class TotalFieldInput final : public bke::GeometryFieldInput { private: Field<T> input_; Field<int> group_index_; @@ -295,25 +295,25 @@ template<typename T> class TotalFieldInput final : public GeometryFieldInput { public: TotalFieldInput(const eAttrDomain source_domain, Field<T> input, Field<int> group_index) - : GeometryFieldInput(CPPType::get<T>(), "Total Value"), + : bke::GeometryFieldInput(CPPType::get<T>(), "Total Value"), input_(input), group_index_(group_index), source_domain_(source_domain) { } - GVArray get_varray_for_context(const GeometryComponent &component, - const eAttrDomain domain, - IndexMask UNUSED(mask)) const final + GVArray get_varray_for_context(const bke::GeometryFieldContext &context, + IndexMask /*mask*/) const final { - const GeometryComponentFieldContext field_context{component, source_domain_}; - const int domain_size = component.attribute_domain_size(field_context.domain()); + const AttributeAccessor attributes = *context.attributes(); + const int domain_size = attributes.domain_size(source_domain_); if (domain_size == 0) { return {}; } - const AttributeAccessor attributes = *component.attributes(); - fn::FieldEvaluator evaluator{field_context, domain_size}; + const bke::GeometryFieldContext source_context{ + context.geometry(), context.type(), source_domain_}; + fn::FieldEvaluator evaluator{source_context, domain_size}; evaluator.add(input_); evaluator.add(group_index_); evaluator.evaluate(); @@ -339,7 +339,7 @@ template<typename T> class TotalFieldInput final : public GeometryFieldInput { } return attributes.adapt_domain<T>( - VArray<T>::ForContainer(std::move(accumulations_out)), source_domain_, domain); + VArray<T>::ForContainer(std::move(accumulations_out)), source_domain_, context.domain()); } uint64_t hash() const override diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc index 9f317075bb5..8c11288efdd 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc @@ -115,7 +115,7 @@ static void try_capture_field_on_geometry(GeometryComponent &component, if (domain_size == 0) { return; } - GeometryComponentFieldContext field_context{component, domain}; + bke::GeometryFieldContext field_context{component, domain}; MutableAttributeAccessor attributes = *component.attributes_for_write(); const IndexMask mask{IndexMask(domain_size)}; diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc index 34e28e50c81..af0007c2fa4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc @@ -200,7 +200,7 @@ static void node_geo_exec(GeoNodeExecParams params) continue; } if (attributes->domain_supported(domain)) { - GeometryComponentFieldContext field_context{*component, domain}; + bke::GeometryFieldContext field_context{*component, domain}; const int domain_num = attributes->domain_size(domain); fn::FieldEvaluator data_evaluator{field_context, domain_num}; @@ -282,7 +282,7 @@ static void node_geo_exec(GeoNodeExecParams params) continue; } if (attributes->domain_supported(domain)) { - GeometryComponentFieldContext field_context{*component, domain}; + bke::GeometryFieldContext field_context{*component, domain}; const int domain_num = attributes->domain_size(domain); fn::FieldEvaluator data_evaluator{field_context, domain_num}; diff --git a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc index 69938f3e447..c8c58945bce 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc @@ -93,7 +93,7 @@ static void node_geo_exec(GeoNodeExecParams params) /* The instance transform matrices are owned by the instance group, so we have to * keep all of them around for use during the boolean operation. */ Vector<bke::GeometryInstanceGroup> set_groups; - Vector<GeometrySet> geometry_sets = params.extract_multi_input<GeometrySet>("Mesh 2"); + Vector<GeometrySet> geometry_sets = params.extract_input<Vector<GeometrySet>>("Mesh 2"); for (const GeometrySet &geometry_set : geometry_sets) { bke::geometry_set_gather_instances(geometry_set, set_groups); } @@ -154,7 +154,7 @@ static void node_geo_exec(GeoNodeExecParams params) /* Store intersecting edges in attribute. */ if (attribute_outputs.intersecting_edges_id) { - MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*result); + MutableAttributeAccessor attributes = result->attributes_for_write(); SpanAttributeWriter<bool> selection = attributes.lookup_or_add_for_write_only_span<bool>( attribute_outputs.intersecting_edges_id.get(), ATTR_DOMAIN_EDGE); diff --git a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc index 03892501c89..c8b692651e9 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc @@ -46,6 +46,7 @@ static Mesh *hull_from_bullet(const Mesh *mesh, Span<float3> coords) } /* Copy vertices. */ + MutableSpan<MVert> dst_verts = result->verts_for_write(); for (const int i : IndexRange(verts_num)) { float co[3]; int original_index; @@ -59,7 +60,7 @@ static Mesh *hull_from_bullet(const Mesh *mesh, Span<float3> coords) } # endif /* Copy the position of the original point. */ - copy_v3_v3(result->mvert[i].co, co); + copy_v3_v3(dst_verts[i].co, co); } else { BLI_assert_msg(0, "Unexpected new vertex in hull output"); @@ -73,6 +74,8 @@ static Mesh *hull_from_bullet(const Mesh *mesh, Span<float3> coords) * to a loop and edges need to be created from that. */ Array<MLoop> mloop_src(loops_num); uint edge_index = 0; + MutableSpan<MEdge> edges = result->edges_for_write(); + for (const int i : IndexRange(loops_num)) { int v_from; int v_to; @@ -81,7 +84,7 @@ static Mesh *hull_from_bullet(const Mesh *mesh, Span<float3> coords) mloop_src[i].v = (uint)v_from; /* Add edges for ascending order loops only. */ if (v_from < v_to) { - MEdge &edge = result->medge[edge_index]; + MEdge &edge = edges[edge_index]; edge.v1 = v_from; edge.v2 = v_to; edge.flag = ME_EDGEDRAW | ME_EDGERENDER; @@ -95,7 +98,7 @@ static Mesh *hull_from_bullet(const Mesh *mesh, Span<float3> coords) } if (edges_num == 1) { /* In this case there are no loops. */ - MEdge &edge = result->medge[0]; + MEdge &edge = edges[0]; edge.v1 = 0; edge.v2 = 1; edge.flag |= ME_EDGEDRAW | ME_EDGERENDER | ME_LOOSEEDGE; @@ -106,7 +109,10 @@ static Mesh *hull_from_bullet(const Mesh *mesh, Span<float3> coords) /* Copy faces. */ Array<int> loops; int j = 0; - MLoop *loop = result->mloop; + MutableSpan<MPoly> polys = result->polys_for_write(); + MutableSpan<MLoop> mesh_loops = result->loops_for_write(); + MLoop *loop = mesh_loops.data(); + for (const int i : IndexRange(faces_num)) { const int len = plConvexHullGetFaceSize(hull, i); @@ -116,7 +122,7 @@ static Mesh *hull_from_bullet(const Mesh *mesh, Span<float3> coords) loops.reinitialize(len); plConvexHullGetFaceLoops(hull, i, loops.data()); - MPoly &face = result->mpoly[i]; + MPoly &face = polys[i]; face.loopstart = j; face.totloop = len; for (const int k : IndexRange(len)) { diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc index db3f108aad5..28d979facac 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc @@ -27,39 +27,31 @@ static void node_declare(NodeDeclarationBuilder &b) N_("The selection from the start and end of the splines based on the input sizes")); } -class EndpointFieldInput final : public GeometryFieldInput { +class EndpointFieldInput final : public bke::CurvesFieldInput { Field<int> start_size_; Field<int> end_size_; public: EndpointFieldInput(Field<int> start_size, Field<int> end_size) - : GeometryFieldInput(CPPType::get<bool>(), "Endpoint Selection node"), + : bke::CurvesFieldInput(CPPType::get<bool>(), "Endpoint Selection node"), start_size_(start_size), end_size_(end_size) { category_ = Category::Generated; } - GVArray get_varray_for_context(const GeometryComponent &component, + GVArray get_varray_for_context(const bke::CurvesGeometry &curves, const eAttrDomain domain, IndexMask UNUSED(mask)) const final { - if (component.type() != GEO_COMPONENT_TYPE_CURVE || domain != ATTR_DOMAIN_POINT) { - return nullptr; + if (domain != ATTR_DOMAIN_POINT) { + return {}; } - - const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); - if (!curve_component.has_curves()) { - return nullptr; - } - - const Curves &curves_id = *curve_component.get_for_read(); - const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); if (curves.points_num() == 0) { - return nullptr; + return {}; } - GeometryComponentFieldContext size_context{curve_component, ATTR_DOMAIN_CURVE}; + bke::CurvesFieldContext size_context{curves, ATTR_DOMAIN_CURVE}; fn::FieldEvaluator evaluator{size_context, curves.curves_num()}; evaluator.add(start_size_); evaluator.add(end_size_); @@ -72,12 +64,12 @@ class EndpointFieldInput final : public GeometryFieldInput { devirtualize_varray2(start_size, end_size, [&](const auto &start_size, const auto &end_size) { threading::parallel_for(curves.curves_range(), 1024, [&](IndexRange curves_range) { for (const int i : curves_range) { - const IndexRange range = curves.points_for_curve(i); + const IndexRange points = curves.points_for_curve(i); const int start = std::max(start_size[i], 0); const int end = std::max(end_size[i], 0); - selection_span.slice(range.take_front(start)).fill(true); - selection_span.slice(range.take_back(end)).fill(true); + selection_span.slice(points.take_front(start)).fill(true); + selection_span.slice(points.take_back(end)).fill(true); } }); }); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc index c9a8dba55b2..c675ee472cd 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc @@ -79,10 +79,10 @@ static Mesh *cdt_to_mesh(const meshintersect::CDT_result<double> &result) } Mesh *mesh = BKE_mesh_new_nomain(vert_len, edge_len, 0, loop_len, poly_len); - MutableSpan<MVert> verts{mesh->mvert, mesh->totvert}; - MutableSpan<MEdge> edges{mesh->medge, mesh->totedge}; - MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop}; - MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly}; + MutableSpan<MVert> verts = mesh->verts_for_write(); + MutableSpan<MEdge> edges = mesh->edges_for_write(); + MutableSpan<MPoly> polys = mesh->polys_for_write(); + MutableSpan<MLoop> loops = mesh->loops_for_write(); for (const int i : IndexRange(result.vert.size())) { copy_v3_v3(verts[i].co, float3((float)result.vert[i].x, (float)result.vert[i].y, 0.0f)); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc index ab1f8269c39..4586bb24464 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc @@ -72,10 +72,9 @@ static void node_geo_exec(GeoNodeExecParams params) return; } - const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>(); - const Curves &curves_id = *component.get_for_read(); + const Curves &curves_id = *geometry_set.get_curves_for_read(); const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); - GeometryComponentFieldContext context{component, ATTR_DOMAIN_POINT}; + bke::CurvesFieldContext context{curves, ATTR_DOMAIN_POINT}; fn::FieldEvaluator evaluator{context, curves.points_num()}; evaluator.add(radius_field); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc index 5ef20f03f28..b34b22e995d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc @@ -70,35 +70,28 @@ static void select_by_handle_type(const bke::CurvesGeometry &curves, } } -class HandleTypeFieldInput final : public GeometryFieldInput { +class HandleTypeFieldInput final : public bke::CurvesFieldInput { HandleType type_; GeometryNodeCurveHandleMode mode_; public: HandleTypeFieldInput(HandleType type, GeometryNodeCurveHandleMode mode) - : GeometryFieldInput(CPPType::get<bool>(), "Handle Type Selection node"), + : bke::CurvesFieldInput(CPPType::get<bool>(), "Handle Type Selection node"), type_(type), mode_(mode) { category_ = Category::Generated; } - GVArray get_varray_for_context(const GeometryComponent &component, + GVArray get_varray_for_context(const bke::CurvesGeometry &curves, const eAttrDomain domain, IndexMask mask) const final { - if (component.type() != GEO_COMPONENT_TYPE_CURVE || domain != ATTR_DOMAIN_POINT) { + if (domain != ATTR_DOMAIN_POINT) { return {}; } - - const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); - const Curves *curves_id = curve_component.get_for_read(); - if (curves_id == nullptr) { - return {}; - } - Array<bool> selection(mask.min_array_size()); - select_by_handle_type(bke::CurvesGeometry::wrap(curves_id->geometry), type_, mode_, selection); + select_by_handle_type(curves, type_, mode_, selection); return VArray<bool>::ForContainer(std::move(selection)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc index 37fc6823b9a..41eafe2a741 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc @@ -66,12 +66,14 @@ static void node_geo_exec(GeoNodeExecParams params) case GEO_NODE_CURVE_RESAMPLE_COUNT: { Field<int> count = params.extract_input<Field<int>>("Count"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry) { - if (const CurveComponent *component = geometry.get_component_for_read<CurveComponent>()) { - if (const Curves *src_curves = component->get_for_read()) { - Curves *dst_curves = geometry::resample_to_count(*component, selection, count); - bke::curves_copy_parameters(*src_curves, *dst_curves); - geometry.replace_curves(dst_curves); - } + if (const Curves *src_curves_id = geometry.get_curves_for_read()) { + const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap( + src_curves_id->geometry); + bke::CurvesGeometry dst_curves = geometry::resample_to_count( + src_curves, selection, count); + Curves *dst_curves_id = bke::curves_new_nomain(std::move(dst_curves)); + bke::curves_copy_parameters(*src_curves_id, *dst_curves_id); + geometry.replace_curves(dst_curves_id); } }); break; @@ -79,24 +81,27 @@ static void node_geo_exec(GeoNodeExecParams params) case GEO_NODE_CURVE_RESAMPLE_LENGTH: { Field<float> length = params.extract_input<Field<float>>("Length"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry) { - if (const CurveComponent *component = geometry.get_component_for_read<CurveComponent>()) { - if (const Curves *src_curves = component->get_for_read()) { - Curves *dst_curves = geometry::resample_to_length(*component, selection, length); - bke::curves_copy_parameters(*src_curves, *dst_curves); - geometry.replace_curves(dst_curves); - } + if (const Curves *src_curves_id = geometry.get_curves_for_read()) { + const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap( + src_curves_id->geometry); + bke::CurvesGeometry dst_curves = geometry::resample_to_length( + src_curves, selection, length); + Curves *dst_curves_id = bke::curves_new_nomain(std::move(dst_curves)); + bke::curves_copy_parameters(*src_curves_id, *dst_curves_id); + geometry.replace_curves(dst_curves_id); } }); break; } case GEO_NODE_CURVE_RESAMPLE_EVALUATED: geometry_set.modify_geometry_sets([&](GeometrySet &geometry) { - if (const CurveComponent *component = geometry.get_component_for_read<CurveComponent>()) { - if (const Curves *src_curves = component->get_for_read()) { - Curves *dst_curves = geometry::resample_to_evaluated(*component, selection); - bke::curves_copy_parameters(*src_curves, *dst_curves); - geometry.replace_curves(dst_curves); - } + if (const Curves *src_curves_id = geometry.get_curves_for_read()) { + const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap( + src_curves_id->geometry); + bke::CurvesGeometry dst_curves = geometry::resample_to_evaluated(src_curves, selection); + Curves *dst_curves_id = bke::curves_new_nomain(std::move(dst_curves)); + bke::curves_copy_parameters(*src_curves_id, *dst_curves_id); + geometry.replace_curves(dst_curves_id); } }); break; diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc index de29735bd2d..0169ead5bd2 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc @@ -23,14 +23,12 @@ static void node_geo_exec(GeoNodeExecParams params) if (!geometry_set.has_curves()) { return; } + const Curves &src_curves_id = *geometry_set.get_curves_for_read(); + const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(src_curves_id.geometry); - Field<bool> selection_field = params.get_input<Field<bool>>("Selection"); - const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>(); - GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE); - - fn::FieldEvaluator selection_evaluator{field_context, domain_size}; - selection_evaluator.add(selection_field); + bke::CurvesFieldContext field_context{src_curves, ATTR_DOMAIN_CURVE}; + fn::FieldEvaluator selection_evaluator{field_context, src_curves.curves_num()}; + selection_evaluator.add(params.get_input<Field<bool>>("Selection")); selection_evaluator.evaluate(); const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); if (selection.is_empty()) { diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_set_handle_type.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_set_handle_type.cc index f7ba724c377..8bb24821064 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_set_handle_type.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_set_handle_type.cc @@ -51,15 +51,12 @@ static HandleType handle_type_from_input_type(GeometryNodeCurveHandleType type) return BEZIER_HANDLE_AUTO; } -static void set_type_in_component(CurveComponent &component, - const GeometryNodeCurveHandleMode mode, - const HandleType new_handle_type, - const Field<bool> &selection_field) +static void set_handle_type(bke::CurvesGeometry &curves, + const GeometryNodeCurveHandleMode mode, + const HandleType new_handle_type, + const Field<bool> &selection_field) { - Curves &curves_id = *component.get_for_write(); - bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); - - GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; + bke::CurvesFieldContext field_context{curves, ATTR_DOMAIN_POINT}; fn::FieldEvaluator evaluator{field_context, curves.points_num()}; evaluator.set_selection(selection_field); evaluator.evaluate(); @@ -93,21 +90,17 @@ static void node_geo_exec(GeoNodeExecParams params) std::atomic<bool> has_bezier = false; geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (!geometry_set.has_curves()) { - return; - } - has_curves = true; - const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>(); - const AttributeAccessor attributes = *component.attributes(); - if (!attributes.contains("handle_type_left") || !attributes.contains("handle_type_right")) { - return; + if (Curves *curves_id = geometry_set.get_curves_for_write()) { + bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry); + has_curves = true; + const AttributeAccessor attributes = curves.attributes(); + if (!attributes.contains("handle_type_left") || !attributes.contains("handle_type_right")) { + return; + } + has_bezier = true; + + set_handle_type(curves, mode, new_handle_type, selection_field); } - has_bezier = true; - - set_type_in_component(geometry_set.get_component_for_write<CurveComponent>(), - mode, - new_handle_type, - selection_field); }); if (has_curves && !has_bezier) { diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc index b98541e3446..b5d8d1f020a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc @@ -143,8 +143,8 @@ static VArray<float> construct_curve_parameter_varray(const bke::CurvesGeometry Array<float> lengths = accumulated_lengths_curve_domain(curves); const int last_index = curves.curves_num() - 1; - const int total_length = lengths.last() + curves.evaluated_length_total_for_curve( - last_index, cyclic[last_index]); + const float total_length = lengths.last() + curves.evaluated_length_total_for_curve( + last_index, cyclic[last_index]); if (total_length > 0.0f) { const float factor = 1.0f / total_length; for (float &value : lengths) { @@ -203,26 +203,18 @@ static VArray<int> construct_index_on_spline_varray(const bke::CurvesGeometry &c return {}; } -class CurveParameterFieldInput final : public GeometryFieldInput { +class CurveParameterFieldInput final : public bke::CurvesFieldInput { public: - CurveParameterFieldInput() : GeometryFieldInput(CPPType::get<float>(), "Curve Parameter node") + CurveParameterFieldInput() : bke::CurvesFieldInput(CPPType::get<float>(), "Curve Parameter node") { category_ = Category::Generated; } - GVArray get_varray_for_context(const GeometryComponent &component, + GVArray get_varray_for_context(const bke::CurvesGeometry &curves, const eAttrDomain domain, IndexMask mask) const final { - if (component.type() == GEO_COMPONENT_TYPE_CURVE) { - const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); - if (curve_component.has_curves()) { - const Curves &curves_id = *curve_component.get_for_read(); - const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); - return construct_curve_parameter_varray(curves, mask, domain); - } - } - return {}; + return construct_curve_parameter_varray(curves, mask, domain); } uint64_t hash() const override @@ -237,26 +229,19 @@ class CurveParameterFieldInput final : public GeometryFieldInput { } }; -class CurveLengthParameterFieldInput final : public GeometryFieldInput { +class CurveLengthParameterFieldInput final : public bke::CurvesFieldInput { public: - CurveLengthParameterFieldInput() : GeometryFieldInput(CPPType::get<float>(), "Curve Length node") + CurveLengthParameterFieldInput() + : bke::CurvesFieldInput(CPPType::get<float>(), "Curve Length node") { category_ = Category::Generated; } - GVArray get_varray_for_context(const GeometryComponent &component, + GVArray get_varray_for_context(const bke::CurvesGeometry &curves, const eAttrDomain domain, IndexMask mask) const final { - if (component.type() == GEO_COMPONENT_TYPE_CURVE) { - const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); - if (curve_component.has_curves()) { - const Curves &curves_id = *curve_component.get_for_read(); - const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); - return construct_curve_length_parameter_varray(curves, mask, domain); - } - } - return {}; + return construct_curve_length_parameter_varray(curves, mask, domain); } uint64_t hash() const override @@ -271,26 +256,18 @@ class CurveLengthParameterFieldInput final : public GeometryFieldInput { } }; -class IndexOnSplineFieldInput final : public GeometryFieldInput { +class IndexOnSplineFieldInput final : public bke::CurvesFieldInput { public: - IndexOnSplineFieldInput() : GeometryFieldInput(CPPType::get<int>(), "Spline Index") + IndexOnSplineFieldInput() : bke::CurvesFieldInput(CPPType::get<int>(), "Spline Index") { category_ = Category::Generated; } - GVArray get_varray_for_context(const GeometryComponent &component, + GVArray get_varray_for_context(const bke::CurvesGeometry &curves, const eAttrDomain domain, IndexMask mask) const final { - if (component.type() == GEO_COMPONENT_TYPE_CURVE) { - const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); - if (curve_component.has_curves()) { - const Curves &curves_id = *curve_component.get_for_read(); - const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); - return construct_index_on_spline_varray(curves, mask, domain); - } - } - return {}; + return construct_index_on_spline_varray(curves, mask, domain); } uint64_t hash() const override diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc index a92479bc5f1..4d7e5f13969 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc @@ -45,14 +45,13 @@ static void node_geo_exec(GeoNodeExecParams params) if (!geometry_set.has_curves()) { return; } - const CurveComponent &src_component = *geometry_set.get_component_for_read<CurveComponent>(); - const Curves &src_curves_id = *src_component.get_for_read(); + const Curves &src_curves_id = *geometry_set.get_curves_for_read(); const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(src_curves_id.geometry); if (src_curves.is_single_type(dst_type)) { return; } - GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_CURVE}; + bke::CurvesFieldContext field_context{src_curves, ATTR_DOMAIN_CURVE}; fn::FieldEvaluator evaluator{field_context, src_curves.curves_num()}; evaluator.set_selection(selection_field); evaluator.evaluate(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc index bd44adb35a2..919d0056bca 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc @@ -34,11 +34,10 @@ static void node_geo_exec(GeoNodeExecParams params) return; } - const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>(); - const Curves &src_curves_id = *component.get_for_read(); + const Curves &src_curves_id = *geometry_set.get_curves_for_read(); const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(src_curves_id.geometry); - GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; + bke::CurvesFieldContext field_context{src_curves, ATTR_DOMAIN_POINT}; fn::FieldEvaluator evaluator{field_context, src_curves.points_num()}; evaluator.add(cuts_field); evaluator.evaluate(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc index 0ddf06dc8c7..799a65ec3d1 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc @@ -4,8 +4,11 @@ #include "BLI_task.hh" #include "BLI_timeit.hh" +#include "DNA_pointcloud_types.h" + #include "BKE_pointcloud.h" -#include "BKE_spline.hh" + +#include "GEO_resample_curves.hh" #include "UI_interface.h" #include "UI_resources.h" @@ -62,9 +65,9 @@ static void node_update(bNodeTree *ntree, bNode *node) nodeSetSocketAvailability(ntree, length_socket, mode == GEO_NODE_CURVE_RESAMPLE_LENGTH); } -static void curve_create_default_rotation_attribute(Span<float3> tangents, - Span<float3> normals, - MutableSpan<float3> rotations) +static void fill_rotation_attribute(const Span<float3> tangents, + const Span<float3> normals, + MutableSpan<float3> rotations) { threading::parallel_for(IndexRange(rotations.size()), 512, [&](IndexRange range) { for (const int i : range) { @@ -74,239 +77,30 @@ static void curve_create_default_rotation_attribute(Span<float3> tangents, }); } -static Array<int> calculate_spline_point_offsets(GeoNodeExecParams ¶ms, - const GeometryNodeCurveResampleMode mode, - const CurveEval &curve, - const Span<SplinePtr> splines) -{ - const int size = curve.splines().size(); - switch (mode) { - case GEO_NODE_CURVE_RESAMPLE_COUNT: { - const int count = params.get_input<int>("Count"); - if (count < 1) { - return {0}; - } - Array<int> offsets(size + 1); - int offset = 0; - for (const int i : IndexRange(size)) { - offsets[i] = offset; - if (splines[i]->evaluated_points_num() > 0) { - offset += count; - } - } - offsets.last() = offset; - return offsets; - } - case GEO_NODE_CURVE_RESAMPLE_LENGTH: { - /* Don't allow asymptotic count increase for low resolution values. */ - const float resolution = std::max(params.get_input<float>("Length"), 0.0001f); - Array<int> offsets(size + 1); - int offset = 0; - for (const int i : IndexRange(size)) { - offsets[i] = offset; - if (splines[i]->evaluated_points_num() > 0) { - offset += splines[i]->length() / resolution + 1; - } - } - offsets.last() = offset; - return offsets; - } - case GEO_NODE_CURVE_RESAMPLE_EVALUATED: { - return curve.evaluated_point_offsets(); - } - } - BLI_assert_unreachable(); - return {0}; -} - -/** - * \note Relies on the fact that all attributes on point clouds are stored contiguously. - */ -static GMutableSpan ensure_point_attribute(PointCloudComponent &points, - const AttributeIDRef &attribute_id, - const eCustomDataType data_type) -{ - GAttributeWriter attribute = points.attributes_for_write()->lookup_or_add_for_write( - attribute_id, ATTR_DOMAIN_POINT, data_type); - GMutableSpan span = attribute.varray.get_internal_span(); - attribute.finish(); - return span; -} - -template<typename T> -static MutableSpan<T> ensure_point_attribute(PointCloudComponent &points, - const AttributeIDRef &attribute_id) -{ - AttributeWriter<T> attribute = points.attributes_for_write()->lookup_or_add_for_write<T>( - attribute_id, ATTR_DOMAIN_POINT); - MutableSpan<T> span = attribute.varray.get_internal_span(); - attribute.finish(); - return span; -} - -namespace { -struct AnonymousAttributeIDs { - StrongAnonymousAttributeID tangent_id; - StrongAnonymousAttributeID normal_id; - StrongAnonymousAttributeID rotation_id; -}; - -struct ResultAttributes { - MutableSpan<float3> positions; - MutableSpan<float> radii; - - Map<AttributeIDRef, GMutableSpan> point_attributes; - - MutableSpan<float3> tangents; - MutableSpan<float3> normals; - MutableSpan<float3> rotations; -}; -} // namespace - -static ResultAttributes create_attributes_for_transfer(PointCloudComponent &points, - const CurveEval &curve, - const AnonymousAttributeIDs &attributes) +static PointCloud *pointcloud_from_curves(bke::CurvesGeometry curves, + const AttributeIDRef &tangent_id, + const AttributeIDRef &normal_id, + const AttributeIDRef &rotation_id) { - ResultAttributes outputs; - - outputs.positions = ensure_point_attribute<float3>(points, "position"); - outputs.radii = ensure_point_attribute<float>(points, "radius"); - - if (attributes.tangent_id) { - outputs.tangents = ensure_point_attribute<float3>(points, attributes.tangent_id.get()); - } - if (attributes.normal_id) { - outputs.normals = ensure_point_attribute<float3>(points, attributes.normal_id.get()); - } - if (attributes.rotation_id) { - outputs.rotations = ensure_point_attribute<float3>(points, attributes.rotation_id.get()); + PointCloud *pointcloud = BKE_pointcloud_new_nomain(0); + pointcloud->totpoint = curves.points_num(); + + if (rotation_id) { + MutableAttributeAccessor attributes = curves.attributes_for_write(); + const VArraySpan<float3> tangents = attributes.lookup<float3>(tangent_id, ATTR_DOMAIN_POINT); + const VArraySpan<float3> normals = attributes.lookup<float3>(normal_id, ATTR_DOMAIN_POINT); + SpanAttributeWriter<float3> rotations = attributes.lookup_or_add_for_write_only_span<float3>( + rotation_id, ATTR_DOMAIN_POINT); + fill_rotation_attribute(tangents, normals, rotations.span); + rotations.finish(); } - /* Because of the invariants of the curve component, we use the attributes of the first spline - * as a representative for the attribute meta data all splines. Attributes from the spline domain - * are handled separately. */ - curve.splines().first()->attributes.foreach_attribute( - [&](const AttributeIDRef &id, const AttributeMetaData &meta_data) { - if (id.should_be_kept()) { - outputs.point_attributes.add_new( - id, ensure_point_attribute(points, id, meta_data.data_type)); - } - return true; - }, - ATTR_DOMAIN_POINT); - - return outputs; -} - -/** - * TODO: For non-poly splines, this has double copies that could be avoided as part - * of a general look at optimizing uses of #Spline::interpolate_to_evaluated. - */ -static void copy_evaluated_point_attributes(const Span<SplinePtr> splines, - const Span<int> offsets, - ResultAttributes &data) -{ - threading::parallel_for(splines.index_range(), 64, [&](IndexRange range) { - for (const int i : range) { - const Spline &spline = *splines[i]; - const int offset = offsets[i]; - const int size = offsets[i + 1] - offsets[i]; - - data.positions.slice(offset, size).copy_from(spline.evaluated_positions()); - spline.interpolate_to_evaluated(spline.radii()).materialize(data.radii.slice(offset, size)); + /* Move the curve point custom data to the pointcloud, to avoid any copying. */ + CustomData_free(&pointcloud->pdata, pointcloud->totpoint); + pointcloud->pdata = curves.point_data; + CustomData_reset(&curves.point_data); - for (const Map<AttributeIDRef, GMutableSpan>::Item item : data.point_attributes.items()) { - const AttributeIDRef attribute_id = item.key; - const GMutableSpan dst = item.value; - - BLI_assert(spline.attributes.get_for_read(attribute_id)); - GSpan spline_span = *spline.attributes.get_for_read(attribute_id); - - spline.interpolate_to_evaluated(spline_span).materialize(dst.slice(offset, size).data()); - } - - if (!data.tangents.is_empty()) { - data.tangents.slice(offset, size).copy_from(spline.evaluated_tangents()); - } - if (!data.normals.is_empty()) { - data.normals.slice(offset, size).copy_from(spline.evaluated_normals()); - } - } - }); -} - -static void copy_uniform_sample_point_attributes(const Span<SplinePtr> splines, - const Span<int> offsets, - ResultAttributes &data) -{ - threading::parallel_for(splines.index_range(), 64, [&](IndexRange range) { - for (const int i : range) { - const Spline &spline = *splines[i]; - const int offset = offsets[i]; - const int num = offsets[i + 1] - offsets[i]; - if (num == 0) { - continue; - } - - const Array<float> uniform_samples = spline.sample_uniform_index_factors(num); - - spline.sample_with_index_factors<float3>( - spline.evaluated_positions(), uniform_samples, data.positions.slice(offset, num)); - spline.sample_with_index_factors<float>(spline.interpolate_to_evaluated(spline.radii()), - uniform_samples, - data.radii.slice(offset, num)); - - for (const Map<AttributeIDRef, GMutableSpan>::Item item : data.point_attributes.items()) { - const AttributeIDRef attribute_id = item.key; - const GMutableSpan dst = item.value; - - BLI_assert(spline.attributes.get_for_read(attribute_id)); - GSpan spline_span = *spline.attributes.get_for_read(attribute_id); - - spline.sample_with_index_factors( - spline.interpolate_to_evaluated(spline_span), uniform_samples, dst.slice(offset, num)); - } - - if (!data.tangents.is_empty()) { - Span<float3> src_tangents = spline.evaluated_tangents(); - MutableSpan<float3> sampled_tangents = data.tangents.slice(offset, num); - spline.sample_with_index_factors<float3>(src_tangents, uniform_samples, sampled_tangents); - for (float3 &vector : sampled_tangents) { - vector = math::normalize(vector); - } - } - - if (!data.normals.is_empty()) { - Span<float3> src_normals = spline.evaluated_normals(); - MutableSpan<float3> sampled_normals = data.normals.slice(offset, num); - spline.sample_with_index_factors<float3>(src_normals, uniform_samples, sampled_normals); - for (float3 &vector : sampled_normals) { - vector = math::normalize(vector); - } - } - } - }); -} - -static void copy_spline_domain_attributes(const CurveEval &curve, - const Span<int> offsets, - PointCloudComponent &points) -{ - curve.attributes.foreach_attribute( - [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { - const GSpan curve_attribute = *curve.attributes.get_for_read(attribute_id); - const CPPType &type = curve_attribute.type(); - const GMutableSpan dst = ensure_point_attribute(points, attribute_id, meta_data.data_type); - - for (const int i : curve.splines().index_range()) { - const int offset = offsets[i]; - const int num = offsets[i + 1] - offsets[i]; - type.fill_assign_n(curve_attribute[i], dst[offset], num); - } - - return true; - }, - ATTR_DOMAIN_CURVE); + return pointcloud; } static void node_geo_exec(GeoNodeExecParams params) @@ -315,73 +109,96 @@ static void node_geo_exec(GeoNodeExecParams params) const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)storage.mode; GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); - AnonymousAttributeIDs attribute_outputs; - attribute_outputs.tangent_id = StrongAnonymousAttributeID("Tangent"); - attribute_outputs.normal_id = StrongAnonymousAttributeID("Normal"); - attribute_outputs.rotation_id = StrongAnonymousAttributeID("Rotation"); - GeometryComponentEditData::remember_deformed_curve_positions_if_necessary(geometry_set); - geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (!geometry_set.has_curves()) { - geometry_set.remove_geometry_during_modify(); - return; - } - const std::unique_ptr<CurveEval> curve = curves_to_curve_eval( - *geometry_set.get_curves_for_read()); - const Span<SplinePtr> splines = curve->splines(); - curve->assert_valid_point_attributes(); - - const Array<int> offsets = calculate_spline_point_offsets(params, mode, *curve, splines); - const int total_num = offsets.last(); - if (total_num == 0) { - geometry_set.remove_geometry_during_modify(); - return; - } + 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"); + } - geometry_set.replace_pointcloud(BKE_pointcloud_new_nomain(total_num)); - PointCloudComponent &points = geometry_set.get_component_for_write<PointCloudComponent>(); - ResultAttributes point_attributes = create_attributes_for_transfer( - points, *curve, attribute_outputs); + geometry::ResampleCurvesOutputAttributeIDs resample_attributes; + resample_attributes.tangent_id = tangent_anonymous_id.get(); + resample_attributes.normal_id = normal_anonymous_id.get(); - switch (mode) { - case GEO_NODE_CURVE_RESAMPLE_COUNT: - case GEO_NODE_CURVE_RESAMPLE_LENGTH: - copy_uniform_sample_point_attributes(splines, offsets, point_attributes); - break; - case GEO_NODE_CURVE_RESAMPLE_EVALUATED: - copy_evaluated_point_attributes(splines, offsets, point_attributes); - break; + switch (mode) { + case GEO_NODE_CURVE_RESAMPLE_COUNT: { + Field<int> count = params.extract_input<Field<int>>("Count"); + geometry_set.modify_geometry_sets([&](GeometrySet &geometry) { + if (const Curves *src_curves_id = geometry.get_curves_for_read()) { + const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap( + src_curves_id->geometry); + bke::CurvesGeometry dst_curves = geometry::resample_to_count( + src_curves, fn::make_constant_field<bool>(true), count, resample_attributes); + PointCloud *pointcloud = pointcloud_from_curves(std::move(dst_curves), + resample_attributes.tangent_id, + resample_attributes.normal_id, + rotation_anonymous_id.get()); + geometry.remove_geometry_during_modify(); + geometry.replace_pointcloud(pointcloud); + } + }); + break; } - - copy_spline_domain_attributes(*curve, offsets, points); - - if (!point_attributes.rotations.is_empty()) { - curve_create_default_rotation_attribute( - point_attributes.tangents, point_attributes.normals, point_attributes.rotations); + case GEO_NODE_CURVE_RESAMPLE_LENGTH: { + Field<float> length = params.extract_input<Field<float>>("Length"); + geometry_set.modify_geometry_sets([&](GeometrySet &geometry) { + if (const Curves *src_curves_id = geometry.get_curves_for_read()) { + const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap( + src_curves_id->geometry); + bke::CurvesGeometry dst_curves = geometry::resample_to_length( + src_curves, fn::make_constant_field<bool>(true), length, resample_attributes); + PointCloud *pointcloud = pointcloud_from_curves(std::move(dst_curves), + resample_attributes.tangent_id, + resample_attributes.normal_id, + rotation_anonymous_id.get()); + geometry.remove_geometry_during_modify(); + geometry.replace_pointcloud(pointcloud); + } + }); + break; } - - geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_POINT_CLOUD}); - }); + case GEO_NODE_CURVE_RESAMPLE_EVALUATED: + geometry_set.modify_geometry_sets([&](GeometrySet &geometry) { + if (const Curves *src_curves_id = geometry.get_curves_for_read()) { + const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap( + src_curves_id->geometry); + bke::CurvesGeometry dst_curves = geometry::resample_to_evaluated( + src_curves, fn::make_constant_field<bool>(true), resample_attributes); + PointCloud *pointcloud = pointcloud_from_curves(std::move(dst_curves), + resample_attributes.tangent_id, + resample_attributes.normal_id, + rotation_anonymous_id.get()); + geometry.remove_geometry_during_modify(); + geometry.replace_pointcloud(pointcloud); + } + }); + break; + } params.set_output("Points", std::move(geometry_set)); - if (attribute_outputs.tangent_id) { - params.set_output( - "Tangent", - AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.tangent_id), - params.attribute_producer_name())); + if (tangent_anonymous_id) { + params.set_output("Tangent", + AnonymousAttributeFieldInput::Create<float3>( + std::move(tangent_anonymous_id), params.attribute_producer_name())); } - if (attribute_outputs.normal_id) { - params.set_output( - "Normal", - AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.normal_id), - params.attribute_producer_name())); + if (normal_anonymous_id) { + params.set_output("Normal", + AnonymousAttributeFieldInput::Create<float3>( + std::move(normal_anonymous_id), params.attribute_producer_name())); } - if (attribute_outputs.rotation_id) { - params.set_output( - "Rotation", - AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.rotation_id), - params.attribute_producer_name())); + if (rotation_anonymous_id) { + params.set_output("Rotation", + AnonymousAttributeFieldInput::Create<float3>( + std::move(rotation_anonymous_id), params.attribute_producer_name())); } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc index 0932de237a9..1576b573058 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc @@ -1,7 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ #include "BKE_curves.hh" -#include "BKE_spline.hh" #include "BLI_task.hh" #include "UI_interface.h" @@ -9,12 +8,12 @@ #include "NOD_socket_search_link.hh" +#include "GEO_trim_curves.hh" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_curve_trim_cc { -using blender::attribute_math::mix2; - NODE_STORAGE_FUNCS(NodeGeometryCurveTrim) static void node_declare(NodeDeclarationBuilder &b) @@ -108,394 +107,6 @@ static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) } } -struct TrimLocation { - /* Control point index at the start side of the trim location. */ - int left_index; - /* Control point index at the end of the trim location's segment. */ - int right_index; - /* The factor between the left and right indices. */ - float factor; -}; - -template<typename T> -static void shift_slice_to_start(MutableSpan<T> data, const int start_index, const int num) -{ - BLI_assert(start_index + num - 1 <= data.size()); - memmove(data.data(), &data[start_index], sizeof(T) * num); -} - -/* Shift slice to start of span and modifies start and end data. */ -template<typename T> -static void linear_trim_data(const TrimLocation &start, - const TrimLocation &end, - MutableSpan<T> data) -{ - const int num = end.right_index - start.left_index + 1; - - if (start.left_index > 0) { - shift_slice_to_start<T>(data, start.left_index, num); - } - - const T start_data = mix2<T>(start.factor, data.first(), data[1]); - const T end_data = mix2<T>(end.factor, data[num - 2], data[num - 1]); - - data.first() = start_data; - data[num - 1] = end_data; -} - -/** - * Identical operation as #linear_trim_data, but copy data to a new #MutableSpan rather than - * modifying the original data. - */ -template<typename T> -static void linear_trim_to_output_data(const TrimLocation &start, - const TrimLocation &end, - Span<T> src, - MutableSpan<T> dst) -{ - const int num = end.right_index - start.left_index + 1; - - const T start_data = mix2<T>(start.factor, src[start.left_index], src[start.right_index]); - const T end_data = mix2<T>(end.factor, src[end.left_index], src[end.right_index]); - - dst.copy_from(src.slice(start.left_index, num)); - dst.first() = start_data; - dst.last() = end_data; -} - -/* Look up the control points to the left and right of factor, and get the factor between them. */ -static TrimLocation lookup_control_point_position(const Spline::LookupResult &lookup, - const BezierSpline &spline) -{ - Span<int> offsets = spline.control_point_offsets(); - - const int *offset = std::lower_bound(offsets.begin(), offsets.end(), lookup.evaluated_index); - const int index = offset - offsets.begin(); - - const int left = offsets[index] > lookup.evaluated_index ? index - 1 : index; - const int right = left == (spline.size() - 1) ? 0 : left + 1; - - const float offset_in_segment = lookup.evaluated_index + lookup.factor - offsets[left]; - const int segment_eval_num = offsets[left + 1] - offsets[left]; - const float factor = std::clamp(offset_in_segment / segment_eval_num, 0.0f, 1.0f); - - return {left, right, factor}; -} - -static void trim_poly_spline(Spline &spline, - const Spline::LookupResult &start_lookup, - const Spline::LookupResult &end_lookup) -{ - /* Poly splines have a 1 to 1 mapping between control points and evaluated points. */ - const TrimLocation start = { - start_lookup.evaluated_index, start_lookup.next_evaluated_index, start_lookup.factor}; - const TrimLocation end = { - end_lookup.evaluated_index, end_lookup.next_evaluated_index, end_lookup.factor}; - - const int num = end.right_index - start.left_index + 1; - - linear_trim_data<float3>(start, end, spline.positions()); - linear_trim_data<float>(start, end, spline.radii()); - linear_trim_data<float>(start, end, spline.tilts()); - - spline.attributes.foreach_attribute( - [&](const AttributeIDRef &attribute_id, const AttributeMetaData &UNUSED(meta_data)) { - std::optional<GMutableSpan> src = spline.attributes.get_for_write(attribute_id); - BLI_assert(src); - attribute_math::convert_to_static_type(src->type(), [&](auto dummy) { - using T = decltype(dummy); - linear_trim_data<T>(start, end, src->typed<T>()); - }); - return true; - }, - ATTR_DOMAIN_POINT); - - spline.resize(num); -} - -/** - * Trim NURB splines by converting to a poly spline. - */ -static PolySpline trim_nurbs_spline(const Spline &spline, - const Spline::LookupResult &start_lookup, - const Spline::LookupResult &end_lookup) -{ - /* Since this outputs a poly spline, the evaluated indices are the control point indices. */ - const TrimLocation start = { - start_lookup.evaluated_index, start_lookup.next_evaluated_index, start_lookup.factor}; - const TrimLocation end = { - end_lookup.evaluated_index, end_lookup.next_evaluated_index, end_lookup.factor}; - - const int num = end.right_index - start.left_index + 1; - - /* Create poly spline and copy trimmed data to it. */ - PolySpline new_spline; - new_spline.resize(num); - - /* Copy generic attribute data. */ - spline.attributes.foreach_attribute( - [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { - std::optional<GSpan> src = spline.attributes.get_for_read(attribute_id); - BLI_assert(src); - if (!new_spline.attributes.create(attribute_id, meta_data.data_type)) { - BLI_assert_unreachable(); - return false; - } - std::optional<GMutableSpan> dst = new_spline.attributes.get_for_write(attribute_id); - BLI_assert(dst); - - attribute_math::convert_to_static_type(src->type(), [&](auto dummy) { - using T = decltype(dummy); - VArray<T> eval_data = spline.interpolate_to_evaluated<T>(src->typed<T>()); - linear_trim_to_output_data<T>( - start, end, eval_data.get_internal_span(), dst->typed<T>()); - }); - return true; - }, - ATTR_DOMAIN_POINT); - - linear_trim_to_output_data<float3>( - start, end, spline.evaluated_positions(), new_spline.positions()); - - VArray<float> evaluated_radii = spline.interpolate_to_evaluated(spline.radii()); - linear_trim_to_output_data<float>( - start, end, evaluated_radii.get_internal_span(), new_spline.radii()); - - VArray<float> evaluated_tilts = spline.interpolate_to_evaluated(spline.tilts()); - linear_trim_to_output_data<float>( - start, end, evaluated_tilts.get_internal_span(), new_spline.tilts()); - - return new_spline; -} - -/** - * Trim Bezier splines by adjusting the first and last handles - * and control points to maintain the original shape. - */ -static void trim_bezier_spline(Spline &spline, - const Spline::LookupResult &start_lookup, - const Spline::LookupResult &end_lookup) -{ - BezierSpline &bezier_spline = static_cast<BezierSpline &>(spline); - - const TrimLocation start = lookup_control_point_position(start_lookup, bezier_spline); - TrimLocation end = lookup_control_point_position(end_lookup, bezier_spline); - - const Span<int> control_offsets = bezier_spline.control_point_offsets(); - - /* The number of control points in the resulting spline. */ - const int num = end.right_index - start.left_index + 1; - - /* Trim the spline attributes. Done before end.factor recalculation as it needs - * the original end.factor value. */ - linear_trim_data<float>(start, end, bezier_spline.radii()); - linear_trim_data<float>(start, end, bezier_spline.tilts()); - spline.attributes.foreach_attribute( - [&](const AttributeIDRef &attribute_id, const AttributeMetaData &UNUSED(meta_data)) { - std::optional<GMutableSpan> src = spline.attributes.get_for_write(attribute_id); - BLI_assert(src); - attribute_math::convert_to_static_type(src->type(), [&](auto dummy) { - using T = decltype(dummy); - linear_trim_data<T>(start, end, src->typed<T>()); - }); - return true; - }, - ATTR_DOMAIN_POINT); - - /* Recalculate end.factor if the `num` is two, because the adjustment in the - * position of the control point of the spline to the left of the new end point will change the - * factor between them. */ - if (num == 2) { - if (start_lookup.factor == 1.0f) { - end.factor = 0.0f; - } - else { - end.factor = (end_lookup.evaluated_index + end_lookup.factor - - (start_lookup.evaluated_index + start_lookup.factor)) / - (control_offsets[end.right_index] - - (start_lookup.evaluated_index + start_lookup.factor)); - end.factor = std::clamp(end.factor, 0.0f, 1.0f); - } - } - - BezierSpline::InsertResult start_point = bezier_spline.calculate_segment_insertion( - start.left_index, start.right_index, start.factor); - - /* Update the start control point parameters so they are used calculating the new end point. */ - bezier_spline.positions()[start.left_index] = start_point.position; - bezier_spline.handle_positions_right()[start.left_index] = start_point.right_handle; - bezier_spline.handle_positions_left()[start.right_index] = start_point.handle_next; - - const BezierSpline::InsertResult end_point = bezier_spline.calculate_segment_insertion( - end.left_index, end.right_index, end.factor); - - /* If `num` is two, then the start point right handle needs to change to reflect the end point - * previous handle update. */ - if (num == 2) { - start_point.right_handle = end_point.handle_prev; - } - - /* Shift control point position data to start at beginning of array. */ - if (start.left_index > 0) { - shift_slice_to_start(bezier_spline.positions(), start.left_index, num); - shift_slice_to_start(bezier_spline.handle_positions_left(), start.left_index, num); - shift_slice_to_start(bezier_spline.handle_positions_right(), start.left_index, num); - } - - bezier_spline.positions().first() = start_point.position; - bezier_spline.positions()[num - 1] = end_point.position; - - bezier_spline.handle_positions_left().first() = start_point.left_handle; - bezier_spline.handle_positions_left()[num - 1] = end_point.left_handle; - - bezier_spline.handle_positions_right().first() = start_point.right_handle; - bezier_spline.handle_positions_right()[num - 1] = end_point.right_handle; - - /* If there is at least one control point between the endpoints, update the control - * point handle to the right of the start point and to the left of the end point. */ - if (num > 2) { - bezier_spline.handle_positions_left()[start.right_index - start.left_index] = - start_point.handle_next; - bezier_spline.handle_positions_right()[end.left_index - start.left_index] = - end_point.handle_prev; - } - - bezier_spline.resize(num); -} - -static void trim_spline(SplinePtr &spline, - const Spline::LookupResult start, - const Spline::LookupResult end) -{ - switch (spline->type()) { - case CURVE_TYPE_BEZIER: - trim_bezier_spline(*spline, start, end); - break; - case CURVE_TYPE_POLY: - trim_poly_spline(*spline, start, end); - break; - case CURVE_TYPE_NURBS: - spline = std::make_unique<PolySpline>(trim_nurbs_spline(*spline, start, end)); - break; - case CURVE_TYPE_CATMULL_ROM: - BLI_assert_unreachable(); - spline = {}; - } - spline->mark_cache_invalid(); -} - -template<typename T> -static void to_single_point_data(const TrimLocation &trim, MutableSpan<T> data) -{ - data.first() = mix2<T>(trim.factor, data[trim.left_index], data[trim.right_index]); -} -template<typename T> -static void to_single_point_data(const TrimLocation &trim, Span<T> src, MutableSpan<T> dst) -{ - dst.first() = mix2<T>(trim.factor, src[trim.left_index], src[trim.right_index]); -} - -static void to_single_point_bezier(Spline &spline, const Spline::LookupResult &lookup) -{ - BezierSpline &bezier = static_cast<BezierSpline &>(spline); - - const TrimLocation trim = lookup_control_point_position(lookup, bezier); - - const BezierSpline::InsertResult new_point = bezier.calculate_segment_insertion( - trim.left_index, trim.right_index, trim.factor); - bezier.positions().first() = new_point.position; - bezier.handle_types_left().first() = BEZIER_HANDLE_FREE; - bezier.handle_types_right().first() = BEZIER_HANDLE_FREE; - bezier.handle_positions_left().first() = new_point.left_handle; - bezier.handle_positions_right().first() = new_point.right_handle; - - to_single_point_data<float>(trim, bezier.radii()); - to_single_point_data<float>(trim, bezier.tilts()); - spline.attributes.foreach_attribute( - [&](const AttributeIDRef &attribute_id, const AttributeMetaData &UNUSED(meta_data)) { - std::optional<GMutableSpan> data = spline.attributes.get_for_write(attribute_id); - attribute_math::convert_to_static_type(data->type(), [&](auto dummy) { - using T = decltype(dummy); - to_single_point_data<T>(trim, data->typed<T>()); - }); - return true; - }, - ATTR_DOMAIN_POINT); - spline.resize(1); -} - -static void to_single_point_poly(Spline &spline, const Spline::LookupResult &lookup) -{ - const TrimLocation trim{lookup.evaluated_index, lookup.next_evaluated_index, lookup.factor}; - - to_single_point_data<float3>(trim, spline.positions()); - to_single_point_data<float>(trim, spline.radii()); - to_single_point_data<float>(trim, spline.tilts()); - spline.attributes.foreach_attribute( - [&](const AttributeIDRef &attribute_id, const AttributeMetaData &UNUSED(meta_data)) { - std::optional<GMutableSpan> data = spline.attributes.get_for_write(attribute_id); - attribute_math::convert_to_static_type(data->type(), [&](auto dummy) { - using T = decltype(dummy); - to_single_point_data<T>(trim, data->typed<T>()); - }); - return true; - }, - ATTR_DOMAIN_POINT); - spline.resize(1); -} - -static PolySpline to_single_point_nurbs(const Spline &spline, const Spline::LookupResult &lookup) -{ - /* Since this outputs a poly spline, the evaluated indices are the control point indices. */ - const TrimLocation trim{lookup.evaluated_index, lookup.next_evaluated_index, lookup.factor}; - - /* Create poly spline and copy trimmed data to it. */ - PolySpline new_spline; - new_spline.resize(1); - - spline.attributes.foreach_attribute( - [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { - new_spline.attributes.create(attribute_id, meta_data.data_type); - std::optional<GSpan> src = spline.attributes.get_for_read(attribute_id); - std::optional<GMutableSpan> dst = new_spline.attributes.get_for_write(attribute_id); - attribute_math::convert_to_static_type(src->type(), [&](auto dummy) { - using T = decltype(dummy); - VArray<T> eval_data = spline.interpolate_to_evaluated<T>(src->typed<T>()); - to_single_point_data<T>(trim, eval_data.get_internal_span(), dst->typed<T>()); - }); - return true; - }, - ATTR_DOMAIN_POINT); - - to_single_point_data<float3>(trim, spline.evaluated_positions(), new_spline.positions()); - - VArray<float> evaluated_radii = spline.interpolate_to_evaluated(spline.radii()); - to_single_point_data<float>(trim, evaluated_radii.get_internal_span(), new_spline.radii()); - - VArray<float> evaluated_tilts = spline.interpolate_to_evaluated(spline.tilts()); - to_single_point_data<float>(trim, evaluated_tilts.get_internal_span(), new_spline.tilts()); - - return new_spline; -} - -static void to_single_point_spline(SplinePtr &spline, const Spline::LookupResult &lookup) -{ - switch (spline->type()) { - case CURVE_TYPE_BEZIER: - to_single_point_bezier(*spline, lookup); - break; - case CURVE_TYPE_POLY: - to_single_point_poly(*spline, lookup); - break; - case CURVE_TYPE_NURBS: - spline = std::make_unique<PolySpline>(to_single_point_nurbs(*spline, lookup)); - break; - case CURVE_TYPE_CATMULL_ROM: - BLI_assert_unreachable(); - spline = {}; - } -} - static void geometry_set_curve_trim(GeometrySet &geometry_set, const GeometryNodeCurveSampleMode mode, Field<float> &start_field, @@ -504,71 +115,50 @@ static void geometry_set_curve_trim(GeometrySet &geometry_set, if (!geometry_set.has_curves()) { return; } + const Curves &src_curves_id = *geometry_set.get_curves_for_read(); + const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(src_curves_id.geometry); + if (src_curves.curves_num() == 0) { + return; + } - CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>(); - GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE); - - fn::FieldEvaluator evaluator{field_context, domain_size}; + bke::CurvesFieldContext field_context{src_curves, ATTR_DOMAIN_CURVE}; + fn::FieldEvaluator evaluator{field_context, src_curves.curves_num()}; evaluator.add(start_field); evaluator.add(end_field); evaluator.evaluate(); const VArray<float> starts = evaluator.get_evaluated<float>(0); const VArray<float> ends = evaluator.get_evaluated<float>(1); - const Curves &src_curves_id = *geometry_set.get_curves_for_read(); - std::unique_ptr<CurveEval> curve = curves_to_curve_eval(src_curves_id); - MutableSpan<SplinePtr> splines = curve->splines(); - - threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) { - for (const int i : range) { - SplinePtr &spline = splines[i]; - - /* Currently trimming cyclic splines is not supported. It could be in the future though. */ - if (spline->is_cyclic()) { - continue; - } - - if (spline->evaluated_edges_num() == 0) { - continue; - } - - const float length = spline->length(); - if (length == 0.0f) { - continue; - } - - const float start = starts[i]; - const float end = ends[i]; - - /* When the start and end samples are reversed, instead of implicitly reversing the spline - * or switching the parameters, create a single point spline with the end sample point. */ - if (end <= start) { - if (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) { - to_single_point_spline(spline, - spline->lookup_evaluated_length(std::clamp(start, 0.0f, length))); - } - else { - to_single_point_spline(spline, - spline->lookup_evaluated_factor(std::clamp(start, 0.0f, 1.0f))); - } - continue; - } - - if (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) { - trim_spline(spline, - spline->lookup_evaluated_length(std::clamp(start, 0.0f, length)), - spline->lookup_evaluated_length(std::clamp(end, 0.0f, length))); - } - else { - trim_spline(spline, - spline->lookup_evaluated_factor(std::clamp(start, 0.0f, 1.0f)), - spline->lookup_evaluated_factor(std::clamp(end, 0.0f, 1.0f))); - } + const VArray<bool> cyclic = src_curves.cyclic(); + + /* If node length input is on form [0, 1] instead of [0, length]*/ + const bool normalized_length_lookup = mode == GEO_NODE_CURVE_SAMPLE_FACTOR; + + /* Stack start + end field. */ + Vector<float> length_factors(src_curves.curves_num() * 2); + Vector<int64_t> lookup_indices(src_curves.curves_num() * 2); + threading::parallel_for(src_curves.curves_range(), 512, [&](IndexRange curve_range) { + for (const int64_t curve_i : curve_range) { + const bool negative_trim = !cyclic[curve_i] && starts[curve_i] > ends[curve_i]; + length_factors[curve_i] = starts[curve_i]; + length_factors[curve_i + src_curves.curves_num()] = negative_trim ? starts[curve_i] : + ends[curve_i]; + lookup_indices[curve_i] = curve_i; + lookup_indices[curve_i + src_curves.curves_num()] = curve_i; } }); - Curves *dst_curves_id = curve_eval_to_curves(*curve); + /* Create curve trim lookup table. */ + Array<bke::curves::CurvePoint, 12> point_lookups = geometry::lookup_curve_points( + src_curves, length_factors, lookup_indices, normalized_length_lookup); + + bke::CurvesGeometry dst_curves = geometry::trim_curves( + src_curves, + src_curves.curves_range().as_span(), + point_lookups.as_span().slice(0, src_curves.curves_num()), + point_lookups.as_span().slice(src_curves.curves_num(), src_curves.curves_num())); + + 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); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc index 5a40ededa96..3f6155460ed 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc @@ -66,6 +66,12 @@ static void deform_curves(const CurvesGeometry &curves, const float4x4 curves_to_surface = surface_to_curves.inverted(); + const Span<MVert> surface_verts_old = surface_mesh_old.verts(); + const Span<MLoop> surface_loops_old = surface_mesh_old.loops(); + + const Span<MVert> surface_verts_new = surface_mesh_new.verts(); + const Span<MLoop> surface_loops_new = surface_mesh_new.loops(); + threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange range) { for (const int curve_i : range) { const ReverseUVSampler::Result &surface_sample_old = surface_samples_old[curve_i]; @@ -92,13 +98,13 @@ static void deform_curves(const CurvesGeometry &curves, const int corner_1_new = looptri_new.tri[1]; const int corner_2_new = looptri_new.tri[2]; - const int vert_0_old = surface_mesh_old.mloop[corner_0_old].v; - const int vert_1_old = surface_mesh_old.mloop[corner_1_old].v; - const int vert_2_old = surface_mesh_old.mloop[corner_2_old].v; + const int vert_0_old = surface_loops_old[corner_0_old].v; + const int vert_1_old = surface_loops_old[corner_1_old].v; + const int vert_2_old = surface_loops_old[corner_2_old].v; - const int vert_0_new = surface_mesh_new.mloop[corner_0_new].v; - const int vert_1_new = surface_mesh_new.mloop[corner_1_new].v; - const int vert_2_new = surface_mesh_new.mloop[corner_2_new].v; + const int vert_0_new = surface_loops_new[corner_0_new].v; + const int vert_1_new = surface_loops_new[corner_1_new].v; + const int vert_2_new = surface_loops_new[corner_2_new].v; const float3 &normal_0_old = corner_normals_old[corner_0_old]; const float3 &normal_1_old = corner_normals_old[corner_1_old]; @@ -112,14 +118,14 @@ static void deform_curves(const CurvesGeometry &curves, const float3 normal_new = math::normalize( mix3(bary_weights_new, normal_0_new, normal_1_new, normal_2_new)); - const float3 &pos_0_old = surface_mesh_old.mvert[vert_0_old].co; - const float3 &pos_1_old = surface_mesh_old.mvert[vert_1_old].co; - const float3 &pos_2_old = surface_mesh_old.mvert[vert_2_old].co; + const float3 &pos_0_old = surface_verts_old[vert_0_old].co; + const float3 &pos_1_old = surface_verts_old[vert_1_old].co; + const float3 &pos_2_old = surface_verts_old[vert_2_old].co; const float3 pos_old = mix3(bary_weights_old, pos_0_old, pos_1_old, pos_2_old); - const float3 &pos_0_new = surface_mesh_new.mvert[vert_0_new].co; - const float3 &pos_1_new = surface_mesh_new.mvert[vert_1_new].co; - const float3 &pos_2_new = surface_mesh_new.mvert[vert_2_new].co; + const float3 &pos_0_new = surface_verts_new[vert_0_new].co; + const float3 &pos_1_new = surface_verts_new[vert_1_new].co; + const float3 &pos_2_new = surface_verts_new[vert_2_new].co; const float3 pos_new = mix3(bary_weights_new, pos_0_new, pos_1_new, pos_2_new); /* The translation is just the difference between the old and new position on the surface. */ @@ -249,7 +255,7 @@ static void node_geo_exec(GeoNodeExecParams params) Mesh &surface_object_data = *static_cast<Mesh *>(surface_ob_orig->data); if (BMEditMesh *em = surface_object_data.edit_mesh) { - surface_mesh_orig = BKE_mesh_from_bmesh_for_eval_nomain(em->bm, NULL, &surface_object_data); + surface_mesh_orig = BKE_mesh_from_bmesh_for_eval_nomain(em->bm, nullptr, &surface_object_data); free_suface_mesh_orig = true; } else { @@ -264,8 +270,8 @@ static void node_geo_exec(GeoNodeExecParams params) BKE_mesh_wrapper_ensure_mdata(surface_mesh_eval); - const AttributeAccessor mesh_attributes_eval = bke::mesh_attributes(*surface_mesh_eval); - const AttributeAccessor mesh_attributes_orig = bke::mesh_attributes(*surface_mesh_orig); + const AttributeAccessor mesh_attributes_eval = surface_mesh_eval->attributes(); + const AttributeAccessor mesh_attributes_orig = surface_mesh_orig->attributes(); Curves &curves_id = *curves_geometry.get_curves_for_write(); CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry); diff --git a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc index b74b4e45199..851ca622d6b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc @@ -7,6 +7,7 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "DNA_pointcloud_types.h" #include "BKE_attribute_math.hh" #include "BKE_curves.hh" @@ -160,10 +161,11 @@ static void copy_face_corner_attributes(const Map<AttributeIDRef, AttributeKind> const Span<int> selected_poly_indices, const Mesh &mesh_in) { + const Span<MPoly> polys = mesh_in.polys(); Vector<int64_t> indices; indices.reserve(selected_loops_num); for (const int src_poly_index : selected_poly_indices) { - const MPoly &src_poly = mesh_in.mpoly[src_poly_index]; + const MPoly &src_poly = polys[src_poly_index]; const int src_loop_start = src_poly.loopstart; const int tot_loop = src_poly.totloop; for (const int i : IndexRange(tot_loop)) { @@ -174,39 +176,35 @@ static void copy_face_corner_attributes(const Map<AttributeIDRef, AttributeKind> attributes, src_attributes, dst_attributes, ATTR_DOMAIN_CORNER, IndexMask(indices)); } -static void copy_masked_vertices_to_new_mesh(const Mesh &src_mesh, - Mesh &dst_mesh, - Span<int> vertex_map) +static void copy_masked_verts_to_new_mesh(const Mesh &src_mesh, + Mesh &dst_mesh, + Span<int> vertex_map) { BLI_assert(src_mesh.totvert == vertex_map.size()); + const Span<MVert> src_verts = src_mesh.verts(); + MutableSpan<MVert> dst_verts = dst_mesh.verts_for_write(); + for (const int i_src : vertex_map.index_range()) { const int i_dst = vertex_map[i_src]; if (i_dst == -1) { continue; } - - const MVert &v_src = src_mesh.mvert[i_src]; - MVert &v_dst = dst_mesh.mvert[i_dst]; - - v_dst = v_src; + dst_verts[i_dst] = src_verts[i_src]; } } static void copy_masked_edges_to_new_mesh(const Mesh &src_mesh, Mesh &dst_mesh, Span<int> edge_map) { BLI_assert(src_mesh.totedge == edge_map.size()); + const Span<MEdge> src_edges = src_mesh.edges(); + MutableSpan<MEdge> dst_edges = dst_mesh.edges_for_write(); + for (const int i_src : IndexRange(src_mesh.totedge)) { const int i_dst = edge_map[i_src]; if (ELEM(i_dst, -1, -2)) { continue; } - - const MEdge &e_src = src_mesh.medge[i_src]; - MEdge &e_dst = dst_mesh.medge[i_dst]; - - e_dst = e_src; - e_dst.v1 = e_src.v1; - e_dst.v2 = e_src.v2; + dst_edges[i_dst] = src_edges[i_src]; } } @@ -217,14 +215,16 @@ static void copy_masked_edges_to_new_mesh(const Mesh &src_mesh, { BLI_assert(src_mesh.totvert == vertex_map.size()); BLI_assert(src_mesh.totedge == edge_map.size()); + const Span<MEdge> src_edges = src_mesh.edges(); + MutableSpan<MEdge> dst_edges = dst_mesh.edges_for_write(); + for (const int i_src : IndexRange(src_mesh.totedge)) { const int i_dst = edge_map[i_src]; if (i_dst == -1) { continue; } - - const MEdge &e_src = src_mesh.medge[i_src]; - MEdge &e_dst = dst_mesh.medge[i_dst]; + const MEdge &e_src = src_edges[i_src]; + MEdge &e_dst = dst_edges[i_dst]; e_dst = e_src; e_dst.v1 = vertex_map[e_src.v1]; @@ -239,16 +239,21 @@ static void copy_masked_polys_to_new_mesh(const Mesh &src_mesh, Span<int> masked_poly_indices, Span<int> new_loop_starts) { + const Span<MPoly> src_polys = src_mesh.polys(); + const Span<MLoop> src_loops = src_mesh.loops(); + MutableSpan<MPoly> dst_polys = dst_mesh.polys_for_write(); + MutableSpan<MLoop> dst_loops = dst_mesh.loops_for_write(); + for (const int i_dst : masked_poly_indices.index_range()) { const int i_src = masked_poly_indices[i_dst]; - const MPoly &mp_src = src_mesh.mpoly[i_src]; - MPoly &mp_dst = dst_mesh.mpoly[i_dst]; + const MPoly &mp_src = src_polys[i_src]; + MPoly &mp_dst = dst_polys[i_dst]; const int i_ml_src = mp_src.loopstart; const int i_ml_dst = new_loop_starts[i_dst]; - const MLoop *ml_src = src_mesh.mloop + i_ml_src; - MLoop *ml_dst = dst_mesh.mloop + i_ml_dst; + const MLoop *ml_src = &src_loops[i_ml_src]; + MLoop *ml_dst = &dst_loops[i_ml_dst]; mp_dst = mp_src; mp_dst.loopstart = i_ml_dst; @@ -265,16 +270,21 @@ static void copy_masked_polys_to_new_mesh(const Mesh &src_mesh, Span<int> masked_poly_indices, Span<int> new_loop_starts) { + const Span<MPoly> src_polys = src_mesh.polys(); + const Span<MLoop> src_loops = src_mesh.loops(); + MutableSpan<MPoly> dst_polys = dst_mesh.polys_for_write(); + MutableSpan<MLoop> dst_loops = dst_mesh.loops_for_write(); + for (const int i_dst : masked_poly_indices.index_range()) { const int i_src = masked_poly_indices[i_dst]; - const MPoly &mp_src = src_mesh.mpoly[i_src]; - MPoly &mp_dst = dst_mesh.mpoly[i_dst]; + const MPoly &mp_src = src_polys[i_src]; + MPoly &mp_dst = dst_polys[i_dst]; const int i_ml_src = mp_src.loopstart; const int i_ml_dst = new_loop_starts[i_dst]; - const MLoop *ml_src = src_mesh.mloop + i_ml_src; - MLoop *ml_dst = dst_mesh.mloop + i_ml_dst; + const MLoop *ml_src = &src_loops[i_ml_src]; + MLoop *ml_dst = &dst_loops[i_ml_dst]; mp_dst = mp_src; mp_dst.loopstart = i_ml_dst; @@ -292,16 +302,21 @@ static void copy_masked_polys_to_new_mesh(const Mesh &src_mesh, Span<int> masked_poly_indices, Span<int> new_loop_starts) { + const Span<MPoly> src_polys = src_mesh.polys(); + const Span<MLoop> src_loops = src_mesh.loops(); + MutableSpan<MPoly> dst_polys = dst_mesh.polys_for_write(); + MutableSpan<MLoop> dst_loops = dst_mesh.loops_for_write(); + for (const int i_dst : masked_poly_indices.index_range()) { const int i_src = masked_poly_indices[i_dst]; - const MPoly &mp_src = src_mesh.mpoly[i_src]; - MPoly &mp_dst = dst_mesh.mpoly[i_dst]; + const MPoly &mp_src = src_polys[i_src]; + MPoly &mp_dst = dst_polys[i_dst]; const int i_ml_src = mp_src.loopstart; const int i_ml_dst = new_loop_starts[i_dst]; - const MLoop *ml_src = src_mesh.mloop + i_ml_src; - MLoop *ml_dst = dst_mesh.mloop + i_ml_dst; + const MLoop *ml_src = &src_loops[i_ml_src]; + MLoop *ml_dst = &dst_loops[i_ml_dst]; mp_dst = mp_src; mp_dst.loopstart = i_ml_dst; @@ -316,18 +331,19 @@ static void delete_curves_selection(GeometrySet &geometry_set, const Field<bool> &selection_field, const eAttrDomain selection_domain) { - const CurveComponent &src_component = *geometry_set.get_component_for_read<CurveComponent>(); - GeometryComponentFieldContext field_context{src_component, selection_domain}; + const Curves &src_curves_id = *geometry_set.get_curves_for_read(); + const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(src_curves_id.geometry); - const int domain_num = src_component.attribute_domain_size(selection_domain); - fn::FieldEvaluator evaluator{field_context, domain_num}; + const int domain_size = src_curves.attributes().domain_size(selection_domain); + bke::CurvesFieldContext field_context{src_curves, selection_domain}; + fn::FieldEvaluator evaluator{field_context, domain_size}; evaluator.set_selection(selection_field); evaluator.evaluate(); const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); if (selection.is_empty()) { return; } - if (selection.size() == domain_num) { + if (selection.size() == domain_size) { geometry_set.remove<CurveComponent>(); return; } @@ -347,11 +363,10 @@ static void delete_curves_selection(GeometrySet &geometry_set, static void separate_point_cloud_selection(GeometrySet &geometry_set, const Field<bool> &selection_field) { - const PointCloudComponent &src_points = - *geometry_set.get_component_for_read<PointCloudComponent>(); - GeometryComponentFieldContext field_context{src_points, ATTR_DOMAIN_POINT}; + const PointCloud &src_pointcloud = *geometry_set.get_pointcloud_for_read(); - fn::FieldEvaluator evaluator{field_context, src_points.attribute_domain_size(ATTR_DOMAIN_POINT)}; + bke::PointCloudFieldContext field_context{src_pointcloud}; + fn::FieldEvaluator evaluator{field_context, src_pointcloud.totpoint}; evaluator.set_selection(selection_field); evaluator.evaluate(); const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); @@ -367,8 +382,8 @@ static void separate_point_cloud_selection(GeometrySet &geometry_set, {GEO_COMPONENT_TYPE_POINT_CLOUD}, GEO_COMPONENT_TYPE_POINT_CLOUD, false, attributes); copy_attributes_based_on_mask(attributes, - bke::pointcloud_attributes(*src_points.get_for_read()), - bke::pointcloud_attributes_for_write(*pointcloud), + src_pointcloud.attributes(), + pointcloud->attributes_for_write(), ATTR_DOMAIN_POINT, selection); geometry_set.replace_pointcloud(pointcloud); @@ -378,7 +393,7 @@ static void delete_selected_instances(GeometrySet &geometry_set, const Field<bool> &selection_field) { InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>(); - GeometryComponentFieldContext field_context{instances, ATTR_DOMAIN_INSTANCE}; + bke::GeometryFieldContext field_context{instances, ATTR_DOMAIN_INSTANCE}; fn::FieldEvaluator evaluator{field_context, instances.instances_num()}; evaluator.set_selection(selection_field); @@ -392,9 +407,9 @@ static void delete_selected_instances(GeometrySet &geometry_set, instances.remove_instances(selection); } -static void compute_selected_vertices_from_vertex_selection(const Span<bool> vertex_selection, - MutableSpan<int> r_vertex_map, - int *r_selected_vertices_num) +static void compute_selected_verts_from_vertex_selection(const Span<bool> vertex_selection, + MutableSpan<int> r_vertex_map, + int *r_selected_verts_num) { BLI_assert(vertex_selection.size() == r_vertex_map.size()); @@ -409,7 +424,7 @@ static void compute_selected_vertices_from_vertex_selection(const Span<bool> ver } } - *r_selected_vertices_num = selected_verts_num; + *r_selected_verts_num = selected_verts_num; } static void compute_selected_edges_from_vertex_selection(const Mesh &mesh, @@ -418,10 +433,11 @@ static void compute_selected_edges_from_vertex_selection(const Mesh &mesh, int *r_selected_edges_num) { BLI_assert(mesh.totedge == r_edge_map.size()); + const Span<MEdge> edges = mesh.edges(); int selected_edges_num = 0; for (const int i : IndexRange(mesh.totedge)) { - const MEdge &edge = mesh.medge[i]; + const MEdge &edge = edges[i]; /* Only add the edge if both vertices will be in the new mesh. */ if (vertex_selection[edge.v1] && vertex_selection[edge.v2]) { @@ -436,25 +452,27 @@ static void compute_selected_edges_from_vertex_selection(const Mesh &mesh, *r_selected_edges_num = selected_edges_num; } -static void compute_selected_polygons_from_vertex_selection(const Mesh &mesh, - const Span<bool> vertex_selection, - Vector<int> &r_selected_poly_indices, - Vector<int> &r_loop_starts, - int *r_selected_polys_num, - int *r_selected_loops_num) +static void compute_selected_polys_from_vertex_selection(const Mesh &mesh, + const Span<bool> vertex_selection, + Vector<int> &r_selected_poly_indices, + Vector<int> &r_loop_starts, + int *r_selected_polys_num, + int *r_selected_loops_num) { BLI_assert(mesh.totvert == vertex_selection.size()); + const Span<MPoly> polys = mesh.polys(); + const Span<MLoop> loops = mesh.loops(); r_selected_poly_indices.reserve(mesh.totpoly); r_loop_starts.reserve(mesh.totloop); int selected_loops_num = 0; - for (const int i : IndexRange(mesh.totpoly)) { - const MPoly &poly_src = mesh.mpoly[i]; + for (const int i : polys.index_range()) { + const MPoly &poly_src = polys[i]; bool all_verts_in_selection = true; - Span<MLoop> loops_src(&mesh.mloop[poly_src.loopstart], poly_src.totloop); - for (const MLoop &loop : loops_src) { + const Span<MLoop> poly_loops = loops.slice(poly_src.loopstart, poly_src.totloop); + for (const MLoop &loop : poly_loops) { if (!vertex_selection[loop.v]) { all_verts_in_selection = false; break; @@ -476,20 +494,20 @@ static void compute_selected_polygons_from_vertex_selection(const Mesh &mesh, * Checks for every edge if it is in `edge_selection`. If it is, then the two vertices of the * edge are kept along with the edge. */ -static void compute_selected_vertices_and_edges_from_edge_selection( - const Mesh &mesh, - const Span<bool> edge_selection, - MutableSpan<int> r_vertex_map, - MutableSpan<int> r_edge_map, - int *r_selected_vertices_num, - int *r_selected_edges_num) +static void compute_selected_verts_and_edges_from_edge_selection(const Mesh &mesh, + const Span<bool> edge_selection, + MutableSpan<int> r_vertex_map, + MutableSpan<int> r_edge_map, + int *r_selected_verts_num, + int *r_selected_edges_num) { BLI_assert(mesh.totedge == edge_selection.size()); + const Span<MEdge> edges = mesh.edges(); int selected_edges_num = 0; int selected_verts_num = 0; for (const int i : IndexRange(mesh.totedge)) { - const MEdge &edge = mesh.medge[i]; + const MEdge &edge = edges[i]; if (edge_selection[i]) { r_edge_map[i] = selected_edges_num; selected_edges_num++; @@ -507,7 +525,7 @@ static void compute_selected_vertices_and_edges_from_edge_selection( } } - *r_selected_vertices_num = selected_verts_num; + *r_selected_verts_num = selected_verts_num; *r_selected_edges_num = selected_edges_num; } @@ -539,23 +557,26 @@ static void compute_selected_edges_from_edge_selection(const Mesh &mesh, * Checks for every polygon if all the edges are in `edge_selection`. If they are, then that * polygon is kept. */ -static void compute_selected_polygons_from_edge_selection(const Mesh &mesh, - const Span<bool> edge_selection, - Vector<int> &r_selected_poly_indices, - Vector<int> &r_loop_starts, - int *r_selected_polys_num, - int *r_selected_loops_num) +static void compute_selected_polys_from_edge_selection(const Mesh &mesh, + const Span<bool> edge_selection, + Vector<int> &r_selected_poly_indices, + Vector<int> &r_loop_starts, + int *r_selected_polys_num, + int *r_selected_loops_num) { + const Span<MPoly> polys = mesh.polys(); + const Span<MLoop> loops = mesh.loops(); + r_selected_poly_indices.reserve(mesh.totpoly); r_loop_starts.reserve(mesh.totloop); int selected_loops_num = 0; - for (const int i : IndexRange(mesh.totpoly)) { - const MPoly &poly_src = mesh.mpoly[i]; + for (const int i : polys.index_range()) { + const MPoly &poly_src = polys[i]; bool all_edges_in_selection = true; - Span<MLoop> loops_src(&mesh.mloop[poly_src.loopstart], poly_src.totloop); - for (const MLoop &loop : loops_src) { + const Span<MLoop> poly_loops = loops.slice(poly_src.loopstart, poly_src.totloop); + for (const MLoop &loop : poly_loops) { if (!edge_selection[loop.e]) { all_edges_in_selection = false; break; @@ -590,12 +611,12 @@ static void compute_selected_mesh_data_from_vertex_selection_edge_face( compute_selected_edges_from_vertex_selection( mesh, vertex_selection, r_edge_map, r_selected_edges_num); - compute_selected_polygons_from_vertex_selection(mesh, - vertex_selection, - r_selected_poly_indices, - r_loop_starts, - r_selected_polys_num, - r_selected_loops_num); + compute_selected_polys_from_vertex_selection(mesh, + vertex_selection, + r_selected_poly_indices, + r_loop_starts, + r_selected_polys_num, + r_selected_loops_num); } /** @@ -608,23 +629,23 @@ static void compute_selected_mesh_data_from_vertex_selection(const Mesh &mesh, MutableSpan<int> r_edge_map, Vector<int> &r_selected_poly_indices, Vector<int> &r_loop_starts, - int *r_selected_vertices_num, + int *r_selected_verts_num, int *r_selected_edges_num, int *r_selected_polys_num, int *r_selected_loops_num) { - compute_selected_vertices_from_vertex_selection( - vertex_selection, r_vertex_map, r_selected_vertices_num); + compute_selected_verts_from_vertex_selection( + vertex_selection, r_vertex_map, r_selected_verts_num); compute_selected_edges_from_vertex_selection( mesh, vertex_selection, r_edge_map, r_selected_edges_num); - compute_selected_polygons_from_vertex_selection(mesh, - vertex_selection, - r_selected_poly_indices, - r_loop_starts, - r_selected_polys_num, - r_selected_loops_num); + compute_selected_polys_from_vertex_selection(mesh, + vertex_selection, + r_selected_poly_indices, + r_loop_starts, + r_selected_polys_num, + r_selected_loops_num); } /** @@ -643,17 +664,17 @@ static void compute_selected_mesh_data_from_edge_selection_edge_face( { compute_selected_edges_from_edge_selection( mesh, edge_selection, r_edge_map, r_selected_edges_num); - compute_selected_polygons_from_edge_selection(mesh, - edge_selection, - r_selected_poly_indices, - r_loop_starts, - r_selected_polys_num, - r_selected_loops_num); + compute_selected_polys_from_edge_selection(mesh, + edge_selection, + r_selected_poly_indices, + r_loop_starts, + r_selected_polys_num, + r_selected_loops_num); } /** * Checks for every edge if it is in `edge_selection`. If it is, the vertices belonging to - * that edge are kept as well. The polygons are kept if all edges are in the selection. + * that edge are kept as well. The polys are kept if all edges are in the selection. */ static void compute_selected_mesh_data_from_edge_selection(const Mesh &mesh, const Span<bool> edge_selection, @@ -661,44 +682,41 @@ static void compute_selected_mesh_data_from_edge_selection(const Mesh &mesh, MutableSpan<int> r_edge_map, Vector<int> &r_selected_poly_indices, Vector<int> &r_loop_starts, - int *r_selected_vertices_num, + int *r_selected_verts_num, int *r_selected_edges_num, int *r_selected_polys_num, int *r_selected_loops_num) { r_vertex_map.fill(-1); - compute_selected_vertices_and_edges_from_edge_selection(mesh, - edge_selection, - r_vertex_map, - r_edge_map, - r_selected_vertices_num, - r_selected_edges_num); - compute_selected_polygons_from_edge_selection(mesh, - edge_selection, - r_selected_poly_indices, - r_loop_starts, - r_selected_polys_num, - r_selected_loops_num); + compute_selected_verts_and_edges_from_edge_selection( + mesh, edge_selection, r_vertex_map, r_edge_map, r_selected_verts_num, r_selected_edges_num); + compute_selected_polys_from_edge_selection(mesh, + edge_selection, + r_selected_poly_indices, + r_loop_starts, + r_selected_polys_num, + r_selected_loops_num); } /** * Checks for every polygon if it is in `poly_selection`. */ -static void compute_selected_polygons_from_poly_selection(const Mesh &mesh, - const Span<bool> poly_selection, - Vector<int> &r_selected_poly_indices, - Vector<int> &r_loop_starts, - int *r_selected_polys_num, - int *r_selected_loops_num) +static void compute_selected_polys_from_poly_selection(const Mesh &mesh, + const Span<bool> poly_selection, + Vector<int> &r_selected_poly_indices, + Vector<int> &r_loop_starts, + int *r_selected_polys_num, + int *r_selected_loops_num) { BLI_assert(mesh.totpoly == poly_selection.size()); + const Span<MPoly> polys = mesh.polys(); r_selected_poly_indices.reserve(mesh.totpoly); r_loop_starts.reserve(mesh.totloop); int selected_loops_num = 0; - for (const int i : IndexRange(mesh.totpoly)) { - const MPoly &poly_src = mesh.mpoly[i]; + for (const int i : polys.index_range()) { + const MPoly &poly_src = polys[i]; /* We keep this one. */ if (poly_selection[i]) { r_selected_poly_indices.append_unchecked(i); @@ -725,6 +743,9 @@ static void compute_selected_mesh_data_from_poly_selection_edge_face( { BLI_assert(mesh.totpoly == poly_selection.size()); BLI_assert(mesh.totedge == r_edge_map.size()); + const Span<MPoly> polys = mesh.polys(); + const Span<MLoop> loops = mesh.loops(); + r_edge_map.fill(-1); r_selected_poly_indices.reserve(mesh.totpoly); @@ -732,8 +753,8 @@ static void compute_selected_mesh_data_from_poly_selection_edge_face( int selected_loops_num = 0; int selected_edges_num = 0; - for (const int i : IndexRange(mesh.totpoly)) { - const MPoly &poly_src = mesh.mpoly[i]; + for (const int i : polys.index_range()) { + const MPoly &poly_src = polys[i]; /* We keep this one. */ if (poly_selection[i]) { r_selected_poly_indices.append_unchecked(i); @@ -741,8 +762,8 @@ static void compute_selected_mesh_data_from_poly_selection_edge_face( selected_loops_num += poly_src.totloop; /* Add the vertices and the edges. */ - Span<MLoop> loops_src(&mesh.mloop[poly_src.loopstart], poly_src.totloop); - for (const MLoop &loop : loops_src) { + const Span<MLoop> poly_loops = loops.slice(poly_src.loopstart, poly_src.totloop); + for (const MLoop &loop : poly_loops) { /* Check first if it has not yet been added. */ if (r_edge_map[loop.e] == -1) { r_edge_map[loop.e] = selected_edges_num; @@ -766,13 +787,16 @@ static void compute_selected_mesh_data_from_poly_selection(const Mesh &mesh, MutableSpan<int> r_edge_map, Vector<int> &r_selected_poly_indices, Vector<int> &r_loop_starts, - int *r_selected_vertices_num, + int *r_selected_verts_num, int *r_selected_edges_num, int *r_selected_polys_num, int *r_selected_loops_num) { BLI_assert(mesh.totpoly == poly_selection.size()); BLI_assert(mesh.totedge == r_edge_map.size()); + const Span<MPoly> polys = mesh.polys(); + const Span<MLoop> loops = mesh.loops(); + r_vertex_map.fill(-1); r_edge_map.fill(-1); @@ -782,8 +806,8 @@ static void compute_selected_mesh_data_from_poly_selection(const Mesh &mesh, int selected_loops_num = 0; int selected_verts_num = 0; int selected_edges_num = 0; - for (const int i : IndexRange(mesh.totpoly)) { - const MPoly &poly_src = mesh.mpoly[i]; + for (const int i : polys.index_range()) { + const MPoly &poly_src = polys[i]; /* We keep this one. */ if (poly_selection[i]) { r_selected_poly_indices.append_unchecked(i); @@ -791,8 +815,8 @@ static void compute_selected_mesh_data_from_poly_selection(const Mesh &mesh, selected_loops_num += poly_src.totloop; /* Add the vertices and the edges. */ - Span<MLoop> loops_src(&mesh.mloop[poly_src.loopstart], poly_src.totloop); - for (const MLoop &loop : loops_src) { + const Span<MLoop> poly_loops = loops.slice(poly_src.loopstart, poly_src.totloop); + for (const MLoop &loop : poly_loops) { /* Check first if it has not yet been added. */ if (r_vertex_map[loop.v] == -1) { r_vertex_map[loop.v] = selected_verts_num; @@ -805,7 +829,7 @@ static void compute_selected_mesh_data_from_poly_selection(const Mesh &mesh, } } } - *r_selected_vertices_num = selected_verts_num; + *r_selected_verts_num = selected_verts_num; *r_selected_edges_num = selected_edges_num; *r_selected_polys_num = r_selected_poly_indices.size(); *r_selected_loops_num = selected_loops_num; @@ -890,30 +914,30 @@ static void do_mesh_separation(GeometrySet &geometry_set, selected_polys_num); /* Copy the selected parts of the mesh over to the new mesh. */ - copy_masked_vertices_to_new_mesh(mesh_in, *mesh_out, vertex_map); + copy_masked_verts_to_new_mesh(mesh_in, *mesh_out, vertex_map); copy_masked_edges_to_new_mesh(mesh_in, *mesh_out, vertex_map, edge_map); copy_masked_polys_to_new_mesh( mesh_in, *mesh_out, vertex_map, edge_map, selected_poly_indices, new_loop_starts); /* Copy attributes. */ copy_attributes_based_on_map(attributes, - bke::mesh_attributes(mesh_in), - bke::mesh_attributes_for_write(*mesh_out), + mesh_in.attributes(), + mesh_out->attributes_for_write(), ATTR_DOMAIN_POINT, vertex_map); copy_attributes_based_on_map(attributes, - bke::mesh_attributes(mesh_in), - bke::mesh_attributes_for_write(*mesh_out), + mesh_in.attributes(), + mesh_out->attributes_for_write(), ATTR_DOMAIN_EDGE, edge_map); copy_attributes_based_on_mask(attributes, - bke::mesh_attributes(mesh_in), - bke::mesh_attributes_for_write(*mesh_out), + mesh_in.attributes(), + mesh_out->attributes_for_write(), ATTR_DOMAIN_FACE, IndexMask(Vector<int64_t>(selected_poly_indices.as_span()))); copy_face_corner_attributes(attributes, - bke::mesh_attributes(mesh_in), - bke::mesh_attributes_for_write(*mesh_out), + mesh_in.attributes(), + mesh_out->attributes_for_write(), selected_loops_num, selected_poly_indices, mesh_in); @@ -967,29 +991,27 @@ static void do_mesh_separation(GeometrySet &geometry_set, selected_polys_num); /* Copy the selected parts of the mesh over to the new mesh. */ - memcpy(mesh_out->mvert, mesh_in.mvert, mesh_in.totvert * sizeof(MVert)); + mesh_out->verts_for_write().copy_from(mesh_in.verts()); copy_masked_edges_to_new_mesh(mesh_in, *mesh_out, edge_map); copy_masked_polys_to_new_mesh( mesh_in, *mesh_out, edge_map, selected_poly_indices, new_loop_starts); /* Copy attributes. */ - copy_attributes(attributes, - bke::mesh_attributes(mesh_in), - bke::mesh_attributes_for_write(*mesh_out), - {ATTR_DOMAIN_POINT}); + copy_attributes( + attributes, mesh_in.attributes(), mesh_out->attributes_for_write(), {ATTR_DOMAIN_POINT}); copy_attributes_based_on_map(attributes, - bke::mesh_attributes(mesh_in), - bke::mesh_attributes_for_write(*mesh_out), + mesh_in.attributes(), + mesh_out->attributes_for_write(), ATTR_DOMAIN_EDGE, edge_map); copy_attributes_based_on_mask(attributes, - bke::mesh_attributes(mesh_in), - bke::mesh_attributes_for_write(*mesh_out), + mesh_in.attributes(), + mesh_out->attributes_for_write(), ATTR_DOMAIN_FACE, IndexMask(Vector<int64_t>(selected_poly_indices.as_span()))); copy_face_corner_attributes(attributes, - bke::mesh_attributes(mesh_in), - bke::mesh_attributes_for_write(*mesh_out), + mesh_in.attributes(), + mesh_out->attributes_for_write(), selected_loops_num, selected_poly_indices, mesh_in); @@ -999,28 +1021,28 @@ static void do_mesh_separation(GeometrySet &geometry_set, /* Fill all the maps based on the selection. */ switch (domain) { case ATTR_DOMAIN_POINT: - compute_selected_polygons_from_vertex_selection(mesh_in, - selection, - selected_poly_indices, - new_loop_starts, - &selected_polys_num, - &selected_loops_num); + compute_selected_polys_from_vertex_selection(mesh_in, + selection, + selected_poly_indices, + new_loop_starts, + &selected_polys_num, + &selected_loops_num); break; case ATTR_DOMAIN_EDGE: - compute_selected_polygons_from_edge_selection(mesh_in, - selection, - selected_poly_indices, - new_loop_starts, - &selected_polys_num, - &selected_loops_num); + compute_selected_polys_from_edge_selection(mesh_in, + selection, + selected_poly_indices, + new_loop_starts, + &selected_polys_num, + &selected_loops_num); break; case ATTR_DOMAIN_FACE: - compute_selected_polygons_from_poly_selection(mesh_in, - selection, - selected_poly_indices, - new_loop_starts, - &selected_polys_num, - &selected_loops_num); + compute_selected_polys_from_poly_selection(mesh_in, + selection, + selected_poly_indices, + new_loop_starts, + &selected_polys_num, + &selected_loops_num); break; default: BLI_assert_unreachable(); @@ -1030,23 +1052,23 @@ static void do_mesh_separation(GeometrySet &geometry_set, &mesh_in, mesh_in.totvert, mesh_in.totedge, 0, selected_loops_num, selected_polys_num); /* Copy the selected parts of the mesh over to the new mesh. */ - memcpy(mesh_out->mvert, mesh_in.mvert, mesh_in.totvert * sizeof(MVert)); - memcpy(mesh_out->medge, mesh_in.medge, mesh_in.totedge * sizeof(MEdge)); + mesh_out->verts_for_write().copy_from(mesh_in.verts()); + mesh_out->edges_for_write().copy_from(mesh_in.edges()); copy_masked_polys_to_new_mesh(mesh_in, *mesh_out, selected_poly_indices, new_loop_starts); /* Copy attributes. */ copy_attributes(attributes, - bke::mesh_attributes(mesh_in), - bke::mesh_attributes_for_write(*mesh_out), + mesh_in.attributes(), + mesh_out->attributes_for_write(), {ATTR_DOMAIN_POINT, ATTR_DOMAIN_EDGE}); copy_attributes_based_on_mask(attributes, - bke::mesh_attributes(mesh_in), - bke::mesh_attributes_for_write(*mesh_out), + mesh_in.attributes(), + mesh_out->attributes_for_write(), ATTR_DOMAIN_FACE, IndexMask(Vector<int64_t>(selected_poly_indices.as_span()))); copy_face_corner_attributes(attributes, - bke::mesh_attributes(mesh_in), - bke::mesh_attributes_for_write(*mesh_out), + mesh_in.attributes(), + mesh_out->attributes_for_write(), selected_loops_num, selected_poly_indices, mesh_in); @@ -1063,11 +1085,9 @@ static void separate_mesh_selection(GeometrySet &geometry_set, const eAttrDomain selection_domain, const GeometryNodeDeleteGeometryMode mode) { - const MeshComponent &src_component = *geometry_set.get_component_for_read<MeshComponent>(); - GeometryComponentFieldContext field_context{src_component, selection_domain}; - - fn::FieldEvaluator evaluator{field_context, - src_component.attribute_domain_size(selection_domain)}; + const Mesh &src_mesh = *geometry_set.get_mesh_for_read(); + bke::MeshFieldContext field_context{src_mesh, selection_domain}; + fn::FieldEvaluator evaluator{field_context, src_mesh.attributes().domain_size(selection_domain)}; evaluator.add(selection_field); evaluator.evaluate(); const VArray<bool> selection = evaluator.get_evaluated<bool>(0); @@ -1078,8 +1098,7 @@ static void separate_mesh_selection(GeometrySet &geometry_set, const VArraySpan<bool> selection_span{selection}; - do_mesh_separation( - geometry_set, *src_component.get_for_read(), selection_span, selection_domain, mode); + do_mesh_separation(geometry_set, src_mesh, selection_span, selection_domain, mode); } } // namespace blender::nodes::node_geo_delete_geometry_cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_in_volume.cc b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_in_volume.cc new file mode 100644 index 00000000000..a0bd28218cc --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_in_volume.cc @@ -0,0 +1,285 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifdef WITH_OPENVDB +# include <openvdb/openvdb.h> +# include <openvdb/tools/Interpolation.h> +# include <openvdb/tools/PointScatter.h> +#endif + +#include "DNA_node_types.h" +#include "DNA_pointcloud_types.h" + +#include "BKE_pointcloud.h" +#include "BKE_volume.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +NODE_STORAGE_FUNCS(NodeGeometryDistributePointsInVolume) + +static void geo_node_distribute_points_in_volume_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>(N_("Volume")).supported_type(GEO_COMPONENT_TYPE_VOLUME); + b.add_input<decl::Float>(N_("Density")) + .default_value(1.0f) + .min(0.0f) + .max(100000.0f) + .subtype(PROP_NONE) + .description(N_("Number of points to sample per unit volume")); + b.add_input<decl::Int>(N_("Seed")) + .min(-10000) + .max(10000) + .description(N_("Seed used by the random number generator to generate random points")); + b.add_input<decl::Vector>(N_("Spacing")) + .default_value({0.3, 0.3, 0.3}) + .min(0.0001f) + .subtype(PROP_XYZ) + .description(N_("Spacing between grid points")); + b.add_input<decl::Float>(N_("Threshold")) + .default_value(0.1f) + .min(0.0f) + .max(FLT_MAX) + .description(N_("Minimum density of a volume cell to contain a grid point")); + b.add_output<decl::Geometry>(N_("Points")); +} + +static void geo_node_distribute_points_in_volume_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiItemR(layout, ptr, "mode", 0, "", ICON_NONE); +} + +static void node_distribute_points_in_volume_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryDistributePointsInVolume *data = MEM_cnew<NodeGeometryDistributePointsInVolume>( + __func__); + data->mode = GEO_NODE_DISTRIBUTE_POINTS_IN_VOLUME_DENSITY_RANDOM; + node->storage = data; +} + +static void node_distribute_points_in_volume_update(bNodeTree *ntree, bNode *node) +{ + const NodeGeometryDistributePointsInVolume &storage = node_storage(*node); + GeometryNodeDistributePointsInVolumeMode mode = + static_cast<GeometryNodeDistributePointsInVolumeMode>(storage.mode); + + bNodeSocket *sock_density = ((bNodeSocket *)(node->inputs.first))->next; + bNodeSocket *sock_seed = sock_density->next; + bNodeSocket *sock_spacing = sock_seed->next; + bNodeSocket *sock_threshold = sock_spacing->next; + + nodeSetSocketAvailability( + ntree, sock_density, mode == GEO_NODE_DISTRIBUTE_POINTS_IN_VOLUME_DENSITY_RANDOM); + nodeSetSocketAvailability( + ntree, sock_seed, mode == GEO_NODE_DISTRIBUTE_POINTS_IN_VOLUME_DENSITY_RANDOM); + nodeSetSocketAvailability( + ntree, sock_spacing, mode == GEO_NODE_DISTRIBUTE_POINTS_IN_VOLUME_DENSITY_GRID); + nodeSetSocketAvailability( + ntree, sock_threshold, mode == GEO_NODE_DISTRIBUTE_POINTS_IN_VOLUME_DENSITY_GRID); +} + +#ifdef WITH_OPENVDB +/* Implements the interface required by #openvdb::tools::NonUniformPointScatter. */ +class PositionsVDBWrapper { + private: + float3 offset_fix_; + Vector<float3> &vector_; + + public: + PositionsVDBWrapper(Vector<float3> &vector, const float3 offset_fix) + : offset_fix_(offset_fix), vector_(vector) + { + } + PositionsVDBWrapper(const PositionsVDBWrapper &wrapper) = default; + + void add(const openvdb::Vec3R &pos) + { + vector_.append((float3((float)pos[0], (float)pos[1], (float)pos[2]) + offset_fix_)); + } +}; + +/* Use #std::mt19937 as a random number generator, + * it has a very long period and thus there should be no visible patterns in the generated points. + */ +using RNGType = std::mt19937; +/* Non-uniform scatter allows the amount of points to be scaled with the volume's density. */ +using NonUniformPointScatterVDB = + openvdb::tools::NonUniformPointScatter<PositionsVDBWrapper, RNGType>; + +static void point_scatter_density_random(const openvdb::FloatGrid &grid, + const float density, + const int seed, + Vector<float3> &r_positions) +{ + /* Offset points by half a voxel so that grid points are aligned with world grid points. */ + const float3 offset_fix = {0.5f * (float)grid.voxelSize().x(), + 0.5f * (float)grid.voxelSize().y(), + 0.5f * (float)grid.voxelSize().z()}; + /* Setup and call into OpenVDB's point scatter API. */ + PositionsVDBWrapper vdb_position_wrapper = PositionsVDBWrapper(r_positions, offset_fix); + RNGType random_generator(seed); + NonUniformPointScatterVDB point_scatter(vdb_position_wrapper, density, random_generator); + point_scatter(grid); +} + +static void point_scatter_density_grid(const openvdb::FloatGrid &grid, + const float3 spacing, + const float threshold, + Vector<float3> &r_positions) +{ + const openvdb::Vec3d half_voxel(0.5, 0.5, 0.5); + const openvdb::Vec3d voxel_spacing((double)spacing.x / grid.voxelSize().x(), + (double)spacing.y / grid.voxelSize().y(), + (double)spacing.z / grid.voxelSize().z()); + + /* Abort if spacing is zero. */ + const double min_spacing = std::min(voxel_spacing.x(), + std::min(voxel_spacing.y(), voxel_spacing.z())); + if (std::abs(min_spacing) < 0.0001) { + return; + } + + /* Iterate through tiles and voxels on the grid. */ + for (openvdb::FloatGrid::ValueOnCIter cell = grid.cbeginValueOn(); cell; ++cell) { + /* Check if the cell's value meets the minimum threshold. */ + if (cell.getValue() < threshold) { + continue; + } + /* Compute the bounding box of each tile/voxel. */ + const openvdb::CoordBBox bbox = cell.getBoundingBox(); + const openvdb::Vec3d box_min = bbox.min().asVec3d() - half_voxel; + const openvdb::Vec3d box_max = bbox.max().asVec3d() + half_voxel; + + /* Pick a starting point rounded up to the nearest possible point. */ + double abs_spacing_x = std::abs(voxel_spacing.x()); + double abs_spacing_y = std::abs(voxel_spacing.y()); + double abs_spacing_z = std::abs(voxel_spacing.z()); + const openvdb::Vec3d start(ceil(box_min.x() / abs_spacing_x) * abs_spacing_x, + ceil(box_min.y() / abs_spacing_y) * abs_spacing_y, + ceil(box_min.z() / abs_spacing_z) * abs_spacing_z); + + /* Iterate through all possible points in box. */ + for (double x = start.x(); x < box_max.x(); x += abs_spacing_x) { + for (double y = start.y(); y < box_max.y(); y += abs_spacing_y) { + for (double z = start.z(); z < box_max.z(); z += abs_spacing_z) { + /* Transform with grid matrix and add point. */ + const openvdb::Vec3d idx_pos(x, y, z); + const openvdb::Vec3d local_pos = grid.indexToWorld(idx_pos + half_voxel); + r_positions.append({(float)local_pos.x(), (float)local_pos.y(), (float)local_pos.z()}); + } + } + } + } +} + +#endif /* WITH_OPENVDB */ + +static void geo_node_distribute_points_in_volume_exec(GeoNodeExecParams params) +{ +#ifdef WITH_OPENVDB + GeometrySet geometry_set = params.extract_input<GeometrySet>("Volume"); + + const NodeGeometryDistributePointsInVolume &storage = node_storage(params.node()); + const GeometryNodeDistributePointsInVolumeMode mode = + static_cast<GeometryNodeDistributePointsInVolumeMode>(storage.mode); + + float density; + int seed; + float3 spacing{0, 0, 0}; + float threshold; + if (mode == GEO_NODE_DISTRIBUTE_POINTS_IN_VOLUME_DENSITY_RANDOM) { + density = params.extract_input<float>("Density"); + seed = params.extract_input<int>("Seed"); + } + else if (mode == GEO_NODE_DISTRIBUTE_POINTS_IN_VOLUME_DENSITY_GRID) { + spacing = params.extract_input<float3>("Spacing"); + threshold = params.extract_input<float>("Threshold"); + } + + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (!geometry_set.has_volume()) { + geometry_set.keep_only({GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_INSTANCES}); + return; + } + const VolumeComponent *component = geometry_set.get_component_for_read<VolumeComponent>(); + const Volume *volume = component->get_for_read(); + + Vector<float3> positions; + + for (const int i : IndexRange(BKE_volume_num_grids(volume))) { + const VolumeGrid *volume_grid = BKE_volume_grid_get_for_read(volume, i); + if (volume_grid == nullptr) { + continue; + } + + openvdb::GridBase::ConstPtr base_grid = BKE_volume_grid_openvdb_for_read(volume, + volume_grid); + if (!base_grid) { + continue; + } + + if (!base_grid->isType<openvdb::FloatGrid>()) { + continue; + } + + const openvdb::FloatGrid::ConstPtr grid = openvdb::gridConstPtrCast<openvdb::FloatGrid>( + base_grid); + + if (mode == GEO_NODE_DISTRIBUTE_POINTS_IN_VOLUME_DENSITY_RANDOM) { + point_scatter_density_random(*grid, density, seed, positions); + } + else if (mode == GEO_NODE_DISTRIBUTE_POINTS_IN_VOLUME_DENSITY_GRID) { + point_scatter_density_grid(*grid, spacing, threshold, positions); + } + } + + PointCloud *pointcloud = BKE_pointcloud_new_nomain(positions.size()); + bke::MutableAttributeAccessor point_attributes = pointcloud->attributes_for_write(); + bke::SpanAttributeWriter<float3> point_positions = + point_attributes.lookup_or_add_for_write_only_span<float3>("position", ATTR_DOMAIN_POINT); + bke::SpanAttributeWriter<float> point_radii = + point_attributes.lookup_or_add_for_write_only_span<float>("radius", ATTR_DOMAIN_POINT); + + point_positions.span.copy_from(positions); + point_radii.span.fill(0.05f); + point_positions.finish(); + point_radii.finish(); + + geometry_set.replace_pointcloud(pointcloud); + geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_POINT_CLOUD}); + }); + + params.set_output("Points", std::move(geometry_set)); + +#else + params.set_default_remaining_outputs(); + params.error_message_add(NodeWarningType::Error, + TIP_("Disabled, Blender was compiled without OpenVDB")); +#endif +} +} // namespace blender::nodes + +void register_node_type_geo_distribute_points_in_volume() +{ + static bNodeType ntype; + geo_node_type_base(&ntype, + GEO_NODE_DISTRIBUTE_POINTS_IN_VOLUME, + "Distribute Points in Volume", + NODE_CLASS_GEOMETRY); + node_type_storage(&ntype, + "NodeGeometryDistributePointsInVolume", + node_free_standard_storage, + node_copy_standard_storage); + node_type_init(&ntype, blender::nodes::node_distribute_points_in_volume_init); + node_type_update(&ntype, blender::nodes::node_distribute_points_in_volume_update); + node_type_size(&ntype, 170, 100, 320); + ntype.declare = blender::nodes::geo_node_distribute_points_in_volume_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_distribute_points_in_volume_exec; + ntype.draw_buttons = blender::nodes::geo_node_distribute_points_in_volume_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc index a6c67cac916..cdcb16985ac 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc @@ -105,6 +105,8 @@ static void sample_mesh_surface(const Mesh &mesh, Vector<float3> &r_bary_coords, Vector<int> &r_looptri_indices) { + const Span<MVert> verts = mesh.verts(); + const Span<MLoop> loops = mesh.loops(); const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&mesh), BKE_mesh_runtime_looptri_len(&mesh)}; @@ -113,12 +115,12 @@ static void sample_mesh_surface(const Mesh &mesh, const int v0_loop = looptri.tri[0]; const int v1_loop = looptri.tri[1]; const int v2_loop = looptri.tri[2]; - const int v0_index = mesh.mloop[v0_loop].v; - const int v1_index = mesh.mloop[v1_loop].v; - const int v2_index = mesh.mloop[v2_loop].v; - const float3 v0_pos = float3(mesh.mvert[v0_index].co); - const float3 v1_pos = float3(mesh.mvert[v1_index].co); - const float3 v2_pos = float3(mesh.mvert[v2_index].co); + const int v0_index = loops[v0_loop].v; + const int v1_index = loops[v1_loop].v; + const int v2_index = loops[v2_loop].v; + const float3 v0_pos = verts[v0_index].co; + const float3 v1_pos = verts[v1_index].co; + const float3 v2_pos = verts[v2_index].co; float looptri_density_factor = 1.0f; if (!density_factors.is_empty()) { @@ -283,15 +285,14 @@ BLI_NOINLINE static void interpolate_attribute(const Mesh &mesh, } BLI_NOINLINE static void propagate_existing_attributes( - const MeshComponent &mesh_component, + const Mesh &mesh, const Map<AttributeIDRef, AttributeKind> &attributes, - GeometryComponent &point_component, + PointCloud &points, const Span<float3> bary_coords, const Span<int> looptri_indices) { - const Mesh &mesh = *mesh_component.get_for_read(); - const AttributeAccessor mesh_attributes = *mesh_component.attributes(); - MutableAttributeAccessor point_attributes = *point_component.attributes_for_write(); + const AttributeAccessor mesh_attributes = mesh.attributes(); + MutableAttributeAccessor point_attributes = points.attributes_for_write(); for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) { const AttributeIDRef attribute_id = entry.key; @@ -326,30 +327,31 @@ struct AttributeOutputs { }; } // namespace -BLI_NOINLINE static void compute_attribute_outputs(const MeshComponent &mesh_component, - PointCloudComponent &point_component, +BLI_NOINLINE static void compute_attribute_outputs(const Mesh &mesh, + PointCloud &points, const Span<float3> bary_coords, const Span<int> looptri_indices, const AttributeOutputs &attribute_outputs) { - MutableAttributeAccessor pointcloud_attributes = *point_component.attributes_for_write(); + MutableAttributeAccessor point_attributes = points.attributes_for_write(); - SpanAttributeWriter<int> ids = pointcloud_attributes.lookup_or_add_for_write_only_span<int>( + SpanAttributeWriter<int> ids = point_attributes.lookup_or_add_for_write_only_span<int>( "id", ATTR_DOMAIN_POINT); SpanAttributeWriter<float3> normals; SpanAttributeWriter<float3> rotations; if (attribute_outputs.normal_id) { - normals = pointcloud_attributes.lookup_or_add_for_write_only_span<float3>( + normals = point_attributes.lookup_or_add_for_write_only_span<float3>( attribute_outputs.normal_id.get(), ATTR_DOMAIN_POINT); } if (attribute_outputs.rotation_id) { - rotations = pointcloud_attributes.lookup_or_add_for_write_only_span<float3>( + rotations = point_attributes.lookup_or_add_for_write_only_span<float3>( attribute_outputs.rotation_id.get(), ATTR_DOMAIN_POINT); } - const Mesh &mesh = *mesh_component.get_for_read(); + const Span<MVert> verts = mesh.verts(); + const Span<MLoop> loops = mesh.loops(); const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&mesh), BKE_mesh_runtime_looptri_len(&mesh)}; @@ -358,12 +360,12 @@ BLI_NOINLINE static void compute_attribute_outputs(const MeshComponent &mesh_com const MLoopTri &looptri = looptris[looptri_index]; const float3 &bary_coord = bary_coords[i]; - const int v0_index = mesh.mloop[looptri.tri[0]].v; - const int v1_index = mesh.mloop[looptri.tri[1]].v; - const int v2_index = mesh.mloop[looptri.tri[2]].v; - const float3 v0_pos = float3(mesh.mvert[v0_index].co); - const float3 v1_pos = float3(mesh.mvert[v1_index].co); - const float3 v2_pos = float3(mesh.mvert[v2_index].co); + const int v0_index = loops[looptri.tri[0]].v; + const int v1_index = loops[looptri.tri[1]].v; + const int v2_index = loops[looptri.tri[2]].v; + const float3 v0_pos = verts[v0_index].co; + const float3 v1_pos = verts[v1_index].co; + const float3 v2_pos = verts[v2_index].co; ids.span[i] = noise::hash(noise::hash_float(bary_coord), looptri_index); @@ -380,25 +382,19 @@ BLI_NOINLINE static void compute_attribute_outputs(const MeshComponent &mesh_com } ids.finish(); - - if (normals) { - normals.finish(); - } - if (rotations) { - rotations.finish(); - } + normals.finish(); + rotations.finish(); } -static Array<float> calc_full_density_factors_with_selection(const MeshComponent &component, +static Array<float> calc_full_density_factors_with_selection(const Mesh &mesh, const Field<float> &density_field, const Field<bool> &selection_field) { - const eAttrDomain attribute_domain = ATTR_DOMAIN_CORNER; - GeometryComponentFieldContext field_context{component, attribute_domain}; - const int domain_size = component.attribute_domain_size(attribute_domain); - + const eAttrDomain domain = ATTR_DOMAIN_CORNER; + const int domain_size = mesh.attributes().domain_size(domain); Array<float> densities(domain_size, 0.0f); + bke::MeshFieldContext field_context{mesh, domain}; fn::FieldEvaluator evaluator{field_context, domain_size}; evaluator.set_selection(selection_field); evaluator.add_with_destination(density_field, densities.as_mutable_span()); @@ -406,7 +402,7 @@ static Array<float> calc_full_density_factors_with_selection(const MeshComponent return densities; } -static void distribute_points_random(const MeshComponent &component, +static void distribute_points_random(const Mesh &mesh, const Field<float> &density_field, const Field<bool> &selection_field, const int seed, @@ -415,12 +411,11 @@ static void distribute_points_random(const MeshComponent &component, Vector<int> &looptri_indices) { const Array<float> densities = calc_full_density_factors_with_selection( - component, density_field, selection_field); - const Mesh &mesh = *component.get_for_read(); + mesh, density_field, selection_field); sample_mesh_surface(mesh, 1.0f, densities, seed, positions, bary_coords, looptri_indices); } -static void distribute_points_poisson_disk(const MeshComponent &mesh_component, +static void distribute_points_poisson_disk(const Mesh &mesh, const float minimum_distance, const float max_density, const Field<float> &density_factor_field, @@ -430,14 +425,13 @@ static void distribute_points_poisson_disk(const MeshComponent &mesh_component, Vector<float3> &bary_coords, Vector<int> &looptri_indices) { - const Mesh &mesh = *mesh_component.get_for_read(); sample_mesh_surface(mesh, max_density, {}, seed, positions, bary_coords, looptri_indices); Array<bool> elimination_mask(positions.size(), false); update_elimination_mask_for_close_points(positions, minimum_distance, elimination_mask); const Array<float> density_factors = calc_full_density_factors_with_selection( - mesh_component, density_factor_field, selection_field); + mesh, density_factor_field, selection_field); update_elimination_mask_based_on_density_factors( mesh, density_factors, bary_coords, looptri_indices, elimination_mask.as_mutable_span()); @@ -457,7 +451,7 @@ static void point_distribution_calculate(GeometrySet &geometry_set, return; } - const MeshComponent &mesh_component = *geometry_set.get_component_for_read<MeshComponent>(); + const Mesh &mesh = *geometry_set.get_mesh_for_read(); Vector<float3> positions; Vector<float3> bary_coords; @@ -466,20 +460,15 @@ static void point_distribution_calculate(GeometrySet &geometry_set, switch (method) { case GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_RANDOM: { const Field<float> density_field = params.get_input<Field<float>>("Density"); - distribute_points_random(mesh_component, - density_field, - selection_field, - seed, - positions, - bary_coords, - looptri_indices); + distribute_points_random( + mesh, density_field, selection_field, seed, positions, bary_coords, looptri_indices); break; } case GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_POISSON: { const float minimum_distance = params.get_input<float>("Distance Min"); const float density_max = params.get_input<float>("Density Max"); const Field<float> density_factors_field = params.get_input<Field<float>>("Density Factor"); - distribute_points_poisson_disk(mesh_component, + distribute_points_poisson_disk(mesh, minimum_distance, density_max, density_factors_field, @@ -497,8 +486,7 @@ static void point_distribution_calculate(GeometrySet &geometry_set, } PointCloud *pointcloud = BKE_pointcloud_new_nomain(positions.size()); - bke::MutableAttributeAccessor point_attributes = bke::pointcloud_attributes_for_write( - *pointcloud); + bke::MutableAttributeAccessor point_attributes = pointcloud->attributes_for_write(); bke::SpanAttributeWriter<float3> point_positions = point_attributes.lookup_or_add_for_write_only_span<float3>("position", ATTR_DOMAIN_POINT); bke::SpanAttributeWriter<float> point_radii = @@ -510,9 +498,6 @@ static void point_distribution_calculate(GeometrySet &geometry_set, geometry_set.replace_pointcloud(pointcloud); - PointCloudComponent &point_component = - geometry_set.get_component_for_write<PointCloudComponent>(); - Map<AttributeIDRef, AttributeKind> attributes; geometry_set.gather_attributes_for_propagation( {GEO_COMPONENT_TYPE_MESH}, GEO_COMPONENT_TYPE_POINT_CLOUD, false, attributes); @@ -520,11 +505,9 @@ static void point_distribution_calculate(GeometrySet &geometry_set, /* Position is set separately. */ attributes.remove("position"); - propagate_existing_attributes( - mesh_component, attributes, point_component, bary_coords, looptri_indices); + propagate_existing_attributes(mesh, attributes, *pointcloud, bary_coords, looptri_indices); - compute_attribute_outputs( - mesh_component, point_component, bary_coords, looptri_indices, attribute_outputs); + compute_attribute_outputs(mesh, *pointcloud, bary_coords, looptri_indices, attribute_outputs); } static void node_geo_exec(GeoNodeExecParams params) @@ -545,6 +528,8 @@ static void node_geo_exec(GeoNodeExecParams params) attribute_outputs.rotation_id = StrongAnonymousAttributeID("Rotation"); } + lazy_threading::send_hint(); + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { point_distribution_calculate( geometry_set, selection_field, method, seed, attribute_outputs, params); diff --git a/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc index 76eeee95239..84e63845b84 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc @@ -209,13 +209,18 @@ static void calc_boundaries(const Mesh &mesh, { BLI_assert(r_vertex_types.size() == mesh.totvert); BLI_assert(r_edge_types.size() == mesh.totedge); + const Span<MEdge> edges = mesh.edges(); + const Span<MPoly> polys = mesh.polys(); + const Span<MLoop> loops = mesh.loops(); + r_vertex_types.fill(VertexType::Loose); r_edge_types.fill(EdgeType::Loose); /* Add up the number of polys connected to each edge. */ for (const int i : IndexRange(mesh.totpoly)) { - const MPoly &poly = mesh.mpoly[i]; - for (const MLoop &loop : Span<MLoop>(&mesh.mloop[poly.loopstart], poly.totloop)) { + const MPoly &poly = polys[i]; + const Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop); + for (const MLoop &loop : poly_loops) { r_edge_types[loop.e] = get_edge_type_with_added_neighbor(r_edge_types[loop.e]); } } @@ -226,7 +231,7 @@ static void calc_boundaries(const Mesh &mesh, if (edge_type == EdgeType::Loose) { continue; } - const MEdge &edge = mesh.medge[i]; + const MEdge &edge = edges[i]; if (edge_type == EdgeType::Boundary) { r_vertex_types[edge.v1] = get_vertex_type_with_added_neighbor(r_vertex_types[edge.v1]); r_vertex_types[edge.v2] = get_vertex_type_with_added_neighbor(r_vertex_types[edge.v2]); @@ -241,7 +246,7 @@ static void calc_boundaries(const Mesh &mesh, for (const int i : IndexRange(mesh.totedge)) { const EdgeType edge_type = r_edge_types[i]; if (edge_type == EdgeType::Normal) { - const MEdge &edge = mesh.medge[i]; + const MEdge &edge = edges[i]; if (r_vertex_types[edge.v1] == VertexType::Loose) { r_vertex_types[edge.v1] = VertexType::Normal; } @@ -258,9 +263,12 @@ static void calc_boundaries(const Mesh &mesh, static void create_vertex_poly_map(const Mesh &mesh, MutableSpan<Vector<int>> r_vertex_poly_indices) { - for (const int i : IndexRange(mesh.totpoly)) { - const MPoly &poly = mesh.mpoly[i]; - for (const MLoop &loop : Span<MLoop>(&mesh.mloop[poly.loopstart], poly.totloop)) { + const Span<MPoly> polys = mesh.polys(); + const Span<MLoop> loops = mesh.loops(); + for (const int i : polys.index_range()) { + const MPoly &poly = polys[i]; + const Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop); + for (const MLoop &loop : poly_loops) { r_vertex_poly_indices[loop.v].append(i); } } @@ -321,26 +329,28 @@ static void create_vertex_poly_map(const Mesh &mesh, * - Finally if we are in the normal case we also need to add the last "shared edge" to close the * loop. */ -static bool sort_vertex_polys(const Mesh &mesh, +static bool sort_vertex_polys(const Span<MEdge> edges, + const Span<MPoly> polys, + const Span<MLoop> loops, const int vertex_index, const bool boundary_vertex, const Span<EdgeType> edge_types, - MutableSpan<int> connected_polygons, + MutableSpan<int> connected_polys, MutableSpan<int> r_shared_edges, MutableSpan<int> r_sorted_corners) { - if (connected_polygons.size() <= 2 && (!boundary_vertex || connected_polygons.size() == 0)) { + if (connected_polys.size() <= 2 && (!boundary_vertex || connected_polys.size() == 0)) { return true; } /* For each polygon store the two corners whose edge contains the vertex. */ - Array<std::pair<int, int>> poly_vertex_corners(connected_polygons.size()); - for (const int i : connected_polygons.index_range()) { - const MPoly &poly = mesh.mpoly[connected_polygons[i]]; + Array<std::pair<int, int>> poly_vertex_corners(connected_polys.size()); + for (const int i : connected_polys.index_range()) { + const MPoly &poly = polys[connected_polys[i]]; bool first_edge_done = false; for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { - const MLoop &loop = mesh.mloop[loop_index]; - if (mesh.medge[loop.e].v1 == vertex_index || mesh.medge[loop.e].v2 == vertex_index) { + const MLoop &loop = loops[loop_index]; + if (edges[loop.e].v1 == vertex_index || edges[loop.e].v2 == vertex_index) { if (!first_edge_done) { poly_vertex_corners[i].first = loop_index; first_edge_done = true; @@ -359,20 +369,20 @@ static bool sort_vertex_polys(const Mesh &mesh, * the loop to determine the 'average' orientation. */ if (boundary_vertex) { /* Our first polygon needs to be one which has a boundary edge. */ - for (const int i : connected_polygons.index_range()) { - const MLoop &first_loop = mesh.mloop[poly_vertex_corners[i].first]; - const MLoop &second_loop = mesh.mloop[poly_vertex_corners[i].second]; + for (const int i : connected_polys.index_range()) { + const MLoop &first_loop = loops[poly_vertex_corners[i].first]; + const MLoop &second_loop = loops[poly_vertex_corners[i].second]; if (edge_types[first_loop.e] == EdgeType::Boundary && first_loop.v == vertex_index) { shared_edge_i = second_loop.e; r_sorted_corners[0] = poly_vertex_corners[i].first; - std::swap(connected_polygons[i], connected_polygons[0]); + std::swap(connected_polys[i], connected_polys[0]); std::swap(poly_vertex_corners[i], poly_vertex_corners[0]); break; } if (edge_types[second_loop.e] == EdgeType::Boundary && second_loop.v == vertex_index) { shared_edge_i = first_loop.e; r_sorted_corners[0] = poly_vertex_corners[i].second; - std::swap(connected_polygons[i], connected_polygons[0]); + std::swap(connected_polys[i], connected_polys[0]); std::swap(poly_vertex_corners[i], poly_vertex_corners[0]); break; } @@ -380,20 +390,20 @@ static bool sort_vertex_polys(const Mesh &mesh, if (shared_edge_i == -1) { /* The rotation is inconsistent between the two polygons on the boundary. Just choose one * of the polygon's orientation. */ - for (const int i : connected_polygons.index_range()) { - const MLoop &first_loop = mesh.mloop[poly_vertex_corners[i].first]; - const MLoop &second_loop = mesh.mloop[poly_vertex_corners[i].second]; + for (const int i : connected_polys.index_range()) { + const MLoop &first_loop = loops[poly_vertex_corners[i].first]; + const MLoop &second_loop = loops[poly_vertex_corners[i].second]; if (edge_types[first_loop.e] == EdgeType::Boundary) { shared_edge_i = second_loop.e; r_sorted_corners[0] = poly_vertex_corners[i].first; - std::swap(connected_polygons[i], connected_polygons[0]); + std::swap(connected_polys[i], connected_polys[0]); std::swap(poly_vertex_corners[i], poly_vertex_corners[0]); break; } if (edge_types[second_loop.e] == EdgeType::Boundary) { shared_edge_i = first_loop.e; r_sorted_corners[0] = poly_vertex_corners[i].second; - std::swap(connected_polygons[i], connected_polygons[0]); + std::swap(connected_polys[i], connected_polys[0]); std::swap(poly_vertex_corners[i], poly_vertex_corners[0]); break; } @@ -402,8 +412,8 @@ static bool sort_vertex_polys(const Mesh &mesh, } else { /* Any polygon can be the first. Just need to check the orientation. */ - const MLoop &first_loop = mesh.mloop[poly_vertex_corners[0].first]; - const MLoop &second_loop = mesh.mloop[poly_vertex_corners[0].second]; + const MLoop &first_loop = loops[poly_vertex_corners[0].first]; + const MLoop &second_loop = loops[poly_vertex_corners[0].second]; if (first_loop.v == vertex_index) { shared_edge_i = second_loop.e; r_sorted_corners[0] = poly_vertex_corners[0].first; @@ -415,14 +425,14 @@ static bool sort_vertex_polys(const Mesh &mesh, } BLI_assert(shared_edge_i != -1); - for (const int i : IndexRange(connected_polygons.size() - 1)) { + for (const int i : IndexRange(connected_polys.size() - 1)) { r_shared_edges[i] = shared_edge_i; /* Look at the other polys to see if it has this shared edge. */ int j = i + 1; - for (; j < connected_polygons.size(); ++j) { - const MLoop &first_loop = mesh.mloop[poly_vertex_corners[j].first]; - const MLoop &second_loop = mesh.mloop[poly_vertex_corners[j].second]; + for (; j < connected_polys.size(); ++j) { + const MLoop &first_loop = loops[poly_vertex_corners[j].first]; + const MLoop &second_loop = loops[poly_vertex_corners[j].second]; if (first_loop.e == shared_edge_i) { r_sorted_corners[i + 1] = poly_vertex_corners[j].first; shared_edge_i = second_loop.e; @@ -434,13 +444,13 @@ static bool sort_vertex_polys(const Mesh &mesh, break; } } - if (j == connected_polygons.size()) { + if (j == connected_polys.size()) { /* The vertex is not manifold because the polygons around the vertex don't form a loop, and * hence can't be sorted. */ return false; } - std::swap(connected_polygons[i + 1], connected_polygons[j]); + std::swap(connected_polys[i + 1], connected_polys[j]); std::swap(poly_vertex_corners[i + 1], poly_vertex_corners[j]); } @@ -455,14 +465,16 @@ static bool sort_vertex_polys(const Mesh &mesh, * Get the edge on the poly that contains the given vertex and is a boundary edge. */ static void boundary_edge_on_poly(const MPoly &poly, - const Mesh &mesh, + const Span<MEdge> edges, + const Span<MLoop> loops, const int vertex_index, const Span<EdgeType> edge_types, int &r_edge) { - for (const MLoop &loop : Span<MLoop>(&mesh.mloop[poly.loopstart], poly.totloop)) { + const Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop); + for (const MLoop &loop : poly_loops) { if (edge_types[loop.e] == EdgeType::Boundary) { - const MEdge &edge = mesh.medge[loop.e]; + const MEdge &edge = edges[loop.e]; if (edge.v1 == vertex_index || edge.v2 == vertex_index) { r_edge = loop.e; return; @@ -476,7 +488,8 @@ static void boundary_edge_on_poly(const MPoly &poly, * orientation of the poly is taken into account. */ static void boundary_edges_on_poly(const MPoly &poly, - const Mesh &mesh, + const Span<MEdge> edges, + const Span<MLoop> loops, const int vertex_index, const Span<EdgeType> edge_types, int &r_edge1, @@ -486,9 +499,10 @@ static void boundary_edges_on_poly(const MPoly &poly, /* This is set to true if the order in which we encounter the two edges is inconsistent with the * orientation of the polygon. */ bool needs_swap = false; - for (const MLoop &loop : Span<MLoop>(&mesh.mloop[poly.loopstart], poly.totloop)) { + const Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop); + for (const MLoop &loop : poly_loops) { if (edge_types[loop.e] == EdgeType::Boundary) { - const MEdge &edge = mesh.medge[loop.e]; + const MEdge &edge = edges[loop.e]; if (edge.v1 == vertex_index || edge.v2 == vertex_index) { if (edge1_done) { if (needs_swap) { @@ -510,7 +524,7 @@ static void boundary_edges_on_poly(const MPoly &poly, } } -static void add_edge(const Mesh &mesh, +static void add_edge(const Span<MEdge> src_edges, const int old_edge_i, const int v1, const int v2, @@ -518,7 +532,7 @@ static void add_edge(const Mesh &mesh, Vector<MEdge> &new_edges, Vector<int> &loop_edges) { - MEdge new_edge = MEdge(mesh.medge[old_edge_i]); + MEdge new_edge = src_edges[old_edge_i]; new_edge.v1 = v1; new_edge.v2 = v2; const int new_edge_i = new_edges.size(); @@ -549,14 +563,17 @@ static bool vertex_needs_dissolving(const int vertex, * edges in the input mesh which contain such a vertex are marked as 'done' to prevent duplicate * edges being created. (See T94144) */ -static void dissolve_redundant_verts(const Mesh &mesh, +static void dissolve_redundant_verts(const Span<MEdge> edges, + const Span<MPoly> polys, + const Span<MLoop> loops, const Span<Vector<int>> vertex_poly_indices, MutableSpan<VertexType> vertex_types, MutableSpan<int> old_to_new_edges_map, Vector<MEdge> &new_edges, Vector<int> &new_to_old_edges_map) { - for (const int vert_i : IndexRange(mesh.totvert)) { + const int vertex_num = vertex_types.size(); + for (const int vert_i : IndexRange(vertex_num)) { if (vertex_poly_indices[vert_i].size() != 2 || vertex_types[vert_i] != VertexType::Normal) { continue; } @@ -564,9 +581,10 @@ static void dissolve_redundant_verts(const Mesh &mesh, const int second_poly_index = vertex_poly_indices[vert_i][1]; const int new_edge_index = new_edges.size(); bool edge_created = false; - const MPoly &poly = mesh.mpoly[first_poly_index]; - for (const MLoop &loop : Span<MLoop>(&mesh.mloop[poly.loopstart], poly.totloop)) { - const MEdge &edge = mesh.medge[loop.e]; + const MPoly &poly = polys[first_poly_index]; + const Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop); + for (const MLoop &loop : poly_loops) { + const MEdge &edge = edges[loop.e]; const int v1 = edge.v1; const int v2 = edge.v2; bool mark_edge = false; @@ -617,6 +635,10 @@ static void calc_dual_mesh(GeometrySet &geometry_set, const bool keep_boundaries) { const Mesh &mesh_in = *in_component.get_for_read(); + const Span<MVert> src_verts = mesh_in.verts(); + const Span<MEdge> src_edges = mesh_in.edges(); + const Span<MPoly> src_polys = mesh_in.polys(); + const Span<MLoop> src_loops = mesh_in.loops(); Map<AttributeIDRef, AttributeKind> attributes; geometry_set.gather_attributes_for_propagation( @@ -644,14 +666,28 @@ static void calc_dual_mesh(GeometrySet &geometry_set, bool vertex_ok = true; if (vertex_types[i] == VertexType::Normal) { Array<int> shared_edges(loop_indices.size()); - vertex_ok = sort_vertex_polys( - mesh_in, i, false, edge_types, loop_indices, shared_edges, sorted_corners); + vertex_ok = sort_vertex_polys(src_edges, + src_polys, + src_loops, + i, + false, + edge_types, + loop_indices, + shared_edges, + sorted_corners); vertex_shared_edges[i] = std::move(shared_edges); } else { Array<int> shared_edges(loop_indices.size() - 1); - vertex_ok = sort_vertex_polys( - mesh_in, i, true, edge_types, loop_indices, shared_edges, sorted_corners); + vertex_ok = sort_vertex_polys(src_edges, + src_polys, + src_loops, + i, + true, + edge_types, + loop_indices, + shared_edges, + sorted_corners); vertex_shared_edges[i] = std::move(shared_edges); } if (!vertex_ok) { @@ -666,9 +702,9 @@ static void calc_dual_mesh(GeometrySet &geometry_set, Vector<float3> vertex_positions(mesh_in.totpoly); for (const int i : IndexRange(mesh_in.totpoly)) { - const MPoly poly = mesh_in.mpoly[i]; + const MPoly &poly = src_polys[i]; BKE_mesh_calc_poly_center( - &poly, &mesh_in.mloop[poly.loopstart], mesh_in.mvert, vertex_positions[i]); + &poly, &src_loops[poly.loopstart], src_verts.data(), vertex_positions[i]); } Array<int> boundary_edge_midpoint_index; @@ -679,8 +715,8 @@ static void calc_dual_mesh(GeometrySet &geometry_set, for (const int i : IndexRange(mesh_in.totedge)) { if (edge_types[i] == EdgeType::Boundary) { float3 mid; - const MEdge &edge = mesh_in.medge[i]; - mid_v3_v3v3(mid, mesh_in.mvert[edge.v1].co, mesh_in.mvert[edge.v2].co); + const MEdge &edge = src_edges[i]; + mid_v3_v3v3(mid, src_verts[edge.v1].co, src_verts[edge.v2].co); boundary_edge_midpoint_index[i] = vertex_positions.size(); vertex_positions.append(mid); } @@ -706,7 +742,9 @@ static void calc_dual_mesh(GeometrySet &geometry_set, /* This is necessary to prevent duplicate edges from being created, but will likely not do * anything for most meshes. */ - dissolve_redundant_verts(mesh_in, + dissolve_redundant_verts(src_edges, + src_polys, + src_loops, vertex_poly_indices, vertex_types, old_to_new_edges_map, @@ -734,7 +772,7 @@ static void calc_dual_mesh(GeometrySet &geometry_set, const int old_edge_i = shared_edges[i]; if (old_to_new_edges_map[old_edge_i] == -1) { /* This edge has not been created yet. */ - MEdge new_edge = MEdge(mesh_in.medge[old_edge_i]); + MEdge new_edge = src_edges[old_edge_i]; new_edge.v1 = loop_indices[i]; new_edge.v2 = loop_indices[(i + 1) % loop_indices.size()]; new_to_old_edges_map.append(old_edge_i); @@ -776,7 +814,7 @@ static void calc_dual_mesh(GeometrySet &geometry_set, const int old_edge_i = shared_edges[i]; if (old_to_new_edges_map[old_edge_i] == -1) { /* This edge has not been created yet. */ - MEdge new_edge = MEdge(mesh_in.medge[old_edge_i]); + MEdge new_edge = src_edges[old_edge_i]; new_edge.v1 = loop_indices[i]; new_edge.v2 = loop_indices[i + 1]; new_to_old_edges_map.append(old_edge_i); @@ -795,13 +833,15 @@ static void calc_dual_mesh(GeometrySet &geometry_set, int edge2; if (loop_indices.size() >= 2) { /* The first boundary edge is at the end of the chain of polygons. */ - boundary_edge_on_poly(mesh_in.mpoly[loop_indices.last()], mesh_in, i, edge_types, edge1); - boundary_edge_on_poly(mesh_in.mpoly[loop_indices.first()], mesh_in, i, edge_types, edge2); + boundary_edge_on_poly( + src_polys[loop_indices.last()], src_edges, src_loops, i, edge_types, edge1); + boundary_edge_on_poly( + src_polys[loop_indices.first()], src_edges, src_loops, i, edge_types, edge2); } else { /* If there is only one polygon both edges are in that polygon. */ boundary_edges_on_poly( - mesh_in.mpoly[loop_indices[0]], mesh_in, i, edge_types, edge1, edge2); + src_polys[loop_indices[0]], src_edges, src_loops, i, edge_types, edge1, edge2); } const int last_face_center = loop_indices.last(); @@ -809,7 +849,7 @@ static void calc_dual_mesh(GeometrySet &geometry_set, new_to_old_face_corners_map.append(sorted_corners.last()); const int first_midpoint = loop_indices.last(); if (old_to_new_edges_map[edge1] == -1) { - add_edge(mesh_in, + add_edge(src_edges, edge1, last_face_center, first_midpoint, @@ -827,9 +867,9 @@ static void calc_dual_mesh(GeometrySet &geometry_set, new_to_old_face_corners_map.append(sorted_corners.first()); boundary_vertex_to_relevant_face_map.append( std::pair(loop_indices.last(), last_face_center)); - vertex_positions.append(mesh_in.mvert[i].co); + vertex_positions.append(src_verts[i].co); const int boundary_vertex = loop_indices.last(); - add_edge(mesh_in, + add_edge(src_edges, edge1, first_midpoint, boundary_vertex, @@ -840,7 +880,7 @@ static void calc_dual_mesh(GeometrySet &geometry_set, loop_indices.append(boundary_edge_midpoint_index[edge2]); new_to_old_face_corners_map.append(sorted_corners.first()); const int second_midpoint = loop_indices.last(); - add_edge(mesh_in, + add_edge(src_edges, edge2, boundary_vertex, second_midpoint, @@ -850,7 +890,7 @@ static void calc_dual_mesh(GeometrySet &geometry_set, if (old_to_new_edges_map[edge2] == -1) { const int first_face_center = loop_indices.first(); - add_edge(mesh_in, + add_edge(src_edges, edge2, second_midpoint, first_face_center, @@ -878,23 +918,28 @@ static void calc_dual_mesh(GeometrySet &geometry_set, new_to_old_edges_map, new_to_old_face_corners_map, boundary_vertex_to_relevant_face_map, - bke::mesh_attributes(mesh_in), - bke::mesh_attributes_for_write(*mesh_out)); + mesh_in.attributes(), + mesh_out->attributes_for_write()); + + MutableSpan<MVert> dst_verts = mesh_out->verts_for_write(); + MutableSpan<MEdge> dst_edges = mesh_out->edges_for_write(); + MutableSpan<MPoly> dst_polys = mesh_out->polys_for_write(); + MutableSpan<MLoop> dst_loops = mesh_out->loops_for_write(); int loop_start = 0; for (const int i : IndexRange(mesh_out->totpoly)) { - mesh_out->mpoly[i].loopstart = loop_start; - mesh_out->mpoly[i].totloop = loop_lengths[i]; + dst_polys[i].loopstart = loop_start; + dst_polys[i].totloop = loop_lengths[i]; loop_start += loop_lengths[i]; } for (const int i : IndexRange(mesh_out->totloop)) { - mesh_out->mloop[i].v = loops[i]; - mesh_out->mloop[i].e = loop_edges[i]; + dst_loops[i].v = loops[i]; + dst_loops[i].e = loop_edges[i]; } for (const int i : IndexRange(mesh_out->totvert)) { - copy_v3_v3(mesh_out->mvert[i].co, vertex_positions[i]); + copy_v3_v3(dst_verts[i].co, vertex_positions[i]); } - memcpy(mesh_out->medge, new_edges.data(), sizeof(MEdge) * new_edges.size()); + dst_edges.copy_from(new_edges); geometry_set.replace_mesh(mesh_out); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc b/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc index c6b0fb4c068..d2a3c339301 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc @@ -334,11 +334,10 @@ static void duplicate_curves(GeometrySet &geometry_set, geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_CURVE}); GeometryComponentEditData::remember_deformed_curve_positions_if_necessary(geometry_set); - const CurveComponent &src_component = *geometry_set.get_component_for_read<CurveComponent>(); - const Curves &curves_id = *src_component.get_for_read(); + const Curves &curves_id = *geometry_set.get_curves_for_read(); const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); - GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_CURVE}; + bke::CurvesFieldContext field_context{curves, ATTR_DOMAIN_CURVE}; FieldEvaluator evaluator{field_context, curves.curves_num()}; evaluator.add(count_field); evaluator.set_selection(selection_field); @@ -487,7 +486,7 @@ static void copy_stable_id_faces(const Mesh &mesh, VArraySpan<int> src{src_attribute.varray.typed<int>()}; MutableSpan<int> dst = dst_attribute.span.typed<int>(); - Span<MPoly> polys(mesh.mpoly, mesh.totpoly); + const Span<MPoly> polys = mesh.polys(); int loop_index = 0; for (const int i_poly : selection.index_range()) { const IndexRange range = range_for_offsets_index(poly_offsets, i_poly); @@ -522,14 +521,13 @@ static void duplicate_faces(GeometrySet &geometry_set, } geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_MESH}); - const MeshComponent &src_component = *geometry_set.get_component_for_read<MeshComponent>(); - const Mesh &mesh = *src_component.get_for_read(); - Span<MVert> verts(mesh.mvert, mesh.totvert); - Span<MEdge> edges(mesh.medge, mesh.totedge); - Span<MPoly> polys(mesh.mpoly, mesh.totpoly); - Span<MLoop> loops(mesh.mloop, mesh.totloop); + const Mesh &mesh = *geometry_set.get_mesh_for_read(); + const Span<MVert> verts = mesh.verts(); + const Span<MEdge> edges = mesh.edges(); + const Span<MPoly> polys = mesh.polys(); + const Span<MLoop> loops = mesh.loops(); - GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_FACE}; + bke::MeshFieldContext field_context{mesh, ATTR_DOMAIN_FACE}; FieldEvaluator evaluator(field_context, polys.size()); evaluator.add(count_field); evaluator.set_selection(selection_field); @@ -549,10 +547,10 @@ static void duplicate_faces(GeometrySet &geometry_set, offsets[selection.size()] = total_polys; Mesh *new_mesh = BKE_mesh_new_nomain(total_loops, total_loops, 0, total_loops, total_polys); - MutableSpan<MVert> new_verts(new_mesh->mvert, new_mesh->totvert); - MutableSpan<MEdge> new_edges(new_mesh->medge, new_mesh->totedge); - MutableSpan<MLoop> new_loops(new_mesh->mloop, new_mesh->totloop); - MutableSpan<MPoly> new_poly(new_mesh->mpoly, new_mesh->totpoly); + MutableSpan<MVert> new_verts = new_mesh->verts_for_write(); + MutableSpan<MEdge> new_edges = new_mesh->edges_for_write(); + MutableSpan<MPoly> new_polys = new_mesh->polys_for_write(); + MutableSpan<MLoop> new_loops = new_mesh->loops_for_write(); Array<int> vert_mapping(new_verts.size()); Array<int> edge_mapping(new_edges.size()); @@ -565,8 +563,8 @@ static void duplicate_faces(GeometrySet &geometry_set, const MPoly &source = polys[selection[i_selection]]; for ([[maybe_unused]] const int i_duplicate : IndexRange(poly_range.size())) { - new_poly[poly_index] = source; - new_poly[poly_index].loopstart = loop_index; + new_polys[poly_index] = source; + new_polys[poly_index].loopstart = loop_index; for (const int i_loops : IndexRange(source.totloop)) { const MLoop ¤t_loop = loops[source.loopstart + i_loops]; loop_mapping[loop_index] = source.loopstart + i_loops; @@ -579,7 +577,7 @@ static void duplicate_faces(GeometrySet &geometry_set, new_edges[loop_index].v2 = loop_index + 1; } else { - new_edges[loop_index].v2 = new_poly[poly_index].loopstart; + new_edges[loop_index].v2 = new_polys[poly_index].loopstart; } new_loops[loop_index].v = loop_index; new_loops[loop_index].e = loop_index; @@ -595,22 +593,15 @@ static void duplicate_faces(GeometrySet &geometry_set, loop_mapping, offsets, selection, - bke::mesh_attributes(mesh), - bke::mesh_attributes_for_write(*new_mesh)); + mesh.attributes(), + new_mesh->attributes_for_write()); - copy_stable_id_faces(mesh, - selection, - offsets, - vert_mapping, - bke::mesh_attributes(mesh), - bke::mesh_attributes_for_write(*new_mesh)); + copy_stable_id_faces( + mesh, selection, offsets, vert_mapping, mesh.attributes(), new_mesh->attributes_for_write()); if (attribute_outputs.duplicate_index) { - create_duplicate_index_attribute(bke::mesh_attributes_for_write(*new_mesh), - ATTR_DOMAIN_FACE, - selection, - attribute_outputs, - offsets); + create_duplicate_index_attribute( + new_mesh->attributes_for_write(), ATTR_DOMAIN_FACE, selection, attribute_outputs, offsets); } geometry_set.replace_mesh(new_mesh); @@ -691,7 +682,7 @@ static void copy_stable_id_edges(const Mesh &mesh, return; } - Span<MEdge> edges(mesh.medge, mesh.totedge); + const Span<MEdge> edges = mesh.edges(); VArraySpan<int> src{src_attribute.varray.typed<int>()}; MutableSpan<int> dst = dst_attribute.span.typed<int>(); @@ -724,12 +715,10 @@ static void duplicate_edges(GeometrySet &geometry_set, geometry_set.remove_geometry_during_modify(); return; }; - const MeshComponent &src_component = *geometry_set.get_component_for_read<MeshComponent>(); - const Mesh &mesh = *src_component.get_for_read(); - Span<MVert> verts(mesh.mvert, mesh.totvert); - Span<MEdge> edges(mesh.medge, mesh.totedge); + const Mesh &mesh = *geometry_set.get_mesh_for_read(); + const Span<MEdge> edges = mesh.edges(); - GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_EDGE}; + bke::MeshFieldContext field_context{mesh, ATTR_DOMAIN_EDGE}; FieldEvaluator evaluator{field_context, edges.size()}; evaluator.add(count_field); evaluator.set_selection(selection_field); @@ -740,8 +729,7 @@ static void duplicate_edges(GeometrySet &geometry_set, Array<int> edge_offsets = accumulate_counts_to_offsets(selection, counts); Mesh *new_mesh = BKE_mesh_new_nomain(edge_offsets.last() * 2, edge_offsets.last(), 0, 0, 0); - MutableSpan<MVert> new_verts(new_mesh->mvert, new_mesh->totvert); - MutableSpan<MEdge> new_edges(new_mesh->medge, new_mesh->totedge); + MutableSpan<MEdge> new_edges = new_mesh->edges_for_write(); Array<int> vert_orig_indices(edge_offsets.last() * 2); threading::parallel_for(selection.index_range(), 1024, [&](IndexRange range) { @@ -774,17 +762,14 @@ static void duplicate_edges(GeometrySet &geometry_set, vert_orig_indices, edge_offsets, selection, - bke::mesh_attributes(mesh), - bke::mesh_attributes_for_write(*new_mesh)); + mesh.attributes(), + new_mesh->attributes_for_write()); - copy_stable_id_edges(mesh, - selection, - edge_offsets, - bke::mesh_attributes(mesh), - bke::mesh_attributes_for_write(*new_mesh)); + copy_stable_id_edges( + mesh, selection, edge_offsets, mesh.attributes(), new_mesh->attributes_for_write()); if (attribute_outputs.duplicate_index) { - create_duplicate_index_attribute(bke::mesh_attributes_for_write(*new_mesh), + create_duplicate_index_attribute(new_mesh->attributes_for_write(), ATTR_DOMAIN_EDGE, selection, attribute_outputs, @@ -805,14 +790,13 @@ static void duplicate_points_curve(GeometrySet &geometry_set, const Field<bool> &selection_field, const IndexAttributes &attribute_outputs) { - const CurveComponent &src_component = *geometry_set.get_component_for_read<CurveComponent>(); - const Curves &src_curves_id = *src_component.get_for_read(); + const Curves &src_curves_id = *geometry_set.get_curves_for_read(); const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(src_curves_id.geometry); if (src_curves.points_num() == 0) { return; } - GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_POINT}; + bke::CurvesFieldContext field_context{src_curves, ATTR_DOMAIN_POINT}; FieldEvaluator evaluator{field_context, src_curves.points_num()}; evaluator.add(count_field); evaluator.set_selection(selection_field); @@ -845,7 +829,7 @@ static void duplicate_points_curve(GeometrySet &geometry_set, for (const Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) { const AttributeIDRef attribute_id = entry.key; - GAttributeReader src_attribute = src_component.attributes()->lookup(attribute_id); + GAttributeReader src_attribute = src_curves.attributes().lookup(attribute_id); if (!src_attribute) { continue; } @@ -909,11 +893,10 @@ static void duplicate_points_mesh(GeometrySet &geometry_set, const Field<bool> &selection_field, const IndexAttributes &attribute_outputs) { - const MeshComponent &src_component = *geometry_set.get_component_for_read<MeshComponent>(); const Mesh &mesh = *geometry_set.get_mesh_for_read(); - Span<MVert> src_verts(mesh.mvert, mesh.totvert); + const Span<MVert> src_verts = mesh.verts(); - GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_POINT}; + bke::MeshFieldContext field_context{mesh, ATTR_DOMAIN_POINT}; FieldEvaluator evaluator{field_context, src_verts.size()}; evaluator.add(count_field); evaluator.set_selection(selection_field); @@ -924,7 +907,7 @@ static void duplicate_points_mesh(GeometrySet &geometry_set, Array<int> offsets = accumulate_counts_to_offsets(selection, counts); Mesh *new_mesh = BKE_mesh_new_nomain(offsets.last(), 0, 0, 0, 0); - MutableSpan<MVert> dst_verts(new_mesh->mvert, new_mesh->totvert); + MutableSpan<MVert> dst_verts = new_mesh->verts_for_write(); threaded_slice_fill(offsets.as_span(), selection, src_verts, dst_verts); @@ -933,14 +916,13 @@ static void duplicate_points_mesh(GeometrySet &geometry_set, ATTR_DOMAIN_POINT, offsets, selection, - bke::mesh_attributes(mesh), - bke::mesh_attributes_for_write(*new_mesh)); + mesh.attributes(), + new_mesh->attributes_for_write()); - copy_stable_id_point( - offsets, bke::mesh_attributes(mesh), bke::mesh_attributes_for_write(*new_mesh)); + copy_stable_id_point(offsets, mesh.attributes(), new_mesh->attributes_for_write()); if (attribute_outputs.duplicate_index) { - create_duplicate_index_attribute(bke::mesh_attributes_for_write(*new_mesh), + create_duplicate_index_attribute(new_mesh->attributes_for_write(), ATTR_DOMAIN_POINT, selection, attribute_outputs, @@ -961,12 +943,10 @@ static void duplicate_points_pointcloud(GeometrySet &geometry_set, const Field<bool> &selection_field, const IndexAttributes &attribute_outputs) { - const PointCloudComponent &src_points = - *geometry_set.get_component_for_read<PointCloudComponent>(); - const int point_num = src_points.attribute_domain_size(ATTR_DOMAIN_POINT); + const PointCloud &src_points = *geometry_set.get_pointcloud_for_read(); - GeometryComponentFieldContext field_context{src_points, ATTR_DOMAIN_POINT}; - FieldEvaluator evaluator{field_context, point_num}; + bke::PointCloudFieldContext field_context{src_points}; + FieldEvaluator evaluator{field_context, src_points.totpoint}; evaluator.add(count_field); evaluator.set_selection(selection_field); evaluator.evaluate(); @@ -982,14 +962,13 @@ static void duplicate_points_pointcloud(GeometrySet &geometry_set, ATTR_DOMAIN_POINT, offsets, selection, - *src_points.attributes(), - bke::pointcloud_attributes_for_write(*pointcloud)); + src_points.attributes(), + pointcloud->attributes_for_write()); - copy_stable_id_point( - offsets, *src_points.attributes(), bke::pointcloud_attributes_for_write(*pointcloud)); + copy_stable_id_point(offsets, src_points.attributes(), pointcloud->attributes_for_write()); if (attribute_outputs.duplicate_index) { - create_duplicate_index_attribute(bke::pointcloud_attributes_for_write(*pointcloud), + create_duplicate_index_attribute(pointcloud->attributes_for_write(), ATTR_DOMAIN_POINT, selection, attribute_outputs, @@ -1055,7 +1034,7 @@ static void duplicate_instances(GeometrySet &geometry_set, const InstancesComponent &src_instances = *geometry_set.get_component_for_read<InstancesComponent>(); - GeometryComponentFieldContext field_context{src_instances, ATTR_DOMAIN_INSTANCE}; + bke::GeometryFieldContext field_context{src_instances, ATTR_DOMAIN_INSTANCE}; FieldEvaluator evaluator{field_context, src_instances.instances_num()}; evaluator.add(count_field); evaluator.set_selection(selection_field); diff --git a/source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_curves.cc b/source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_curves.cc index 89abfa0aa88..ba09acf0bf0 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_curves.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_curves.cc @@ -3,7 +3,6 @@ #include "BKE_curves.hh" #include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" #include "GEO_mesh_to_curve.hh" @@ -23,7 +22,6 @@ static Curves *edge_paths_to_curves_convert(const Mesh &mesh, const IndexMask start_verts_mask, const Span<int> next_indices) { - const Span<MVert> mvert{mesh.mvert, mesh.totvert}; Vector<int> vert_indices; Vector<int> curve_offsets; Array<bool> visited(mesh.totvert, false); @@ -70,14 +68,14 @@ static void node_geo_exec(GeoNodeExecParams params) GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (!geometry_set.has_mesh()) { + const Mesh *mesh = geometry_set.get_mesh_for_read(); + if (mesh == nullptr) { geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); return; } - const MeshComponent &component = *geometry_set.get_component_for_read<MeshComponent>(); - GeometryComponentFieldContext context{component, ATTR_DOMAIN_POINT}; - fn::FieldEvaluator evaluator{context, component.attribute_domain_size(ATTR_DOMAIN_POINT)}; + bke::MeshFieldContext context{*mesh, ATTR_DOMAIN_POINT}; + fn::FieldEvaluator evaluator{context, mesh->totvert}; evaluator.add(params.get_input<Field<int>>("Next Vertex Index")); evaluator.add(params.get_input<Field<bool>>("Start Vertices")); evaluator.evaluate(); @@ -89,8 +87,7 @@ static void node_geo_exec(GeoNodeExecParams params) return; } - const Mesh &mesh = *component.get_for_read(); - 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)); geometry_set.keep_only({GEO_COMPONENT_TYPE_CURVE, GEO_COMPONENT_TYPE_INSTANCES}); }); diff --git a/source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_selection.cc index 53cbd691fdb..9ef9ee8ad6e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_selection.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_selection.cc @@ -7,9 +7,6 @@ #include "BLI_set.hh" #include "BLI_task.hh" -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" - #include "node_geometry_util.hh" #include <set> @@ -28,6 +25,8 @@ static void edge_paths_to_selection(const Mesh &src_mesh, const Span<int> next_indices, MutableSpan<bool> r_selection) { + const Span<MEdge> edges = src_mesh.edges(); + Array<bool> selection(src_mesh.totvert, false); for (const int start_vert : start_selection) { @@ -45,8 +44,8 @@ static void edge_paths_to_selection(const Mesh &src_mesh, } } - for (const int i : IndexRange(src_mesh.totedge)) { - const MEdge &edge = src_mesh.medge[i]; + for (const int i : edges.index_range()) { + const MEdge &edge = edges[i]; if ((selection[edge.v1] && selection[edge.v2]) && (edge.v1 == next_indices[edge.v2] || edge.v2 == next_indices[edge.v1])) { r_selection[i] = true; @@ -54,36 +53,26 @@ static void edge_paths_to_selection(const Mesh &src_mesh, } } -class PathToEdgeSelectionFieldInput final : public GeometryFieldInput { +class PathToEdgeSelectionFieldInput final : public bke::MeshFieldInput { private: Field<bool> start_vertices_; Field<int> next_vertex_; public: - PathToEdgeSelectionFieldInput(Field<bool> start_vertices, Field<int> next_vertex) - : GeometryFieldInput(CPPType::get<bool>(), "Edge Selection"), - start_vertices_(start_vertices), + PathToEdgeSelectionFieldInput(Field<bool> start_verts, Field<int> next_vertex) + : bke::MeshFieldInput(CPPType::get<bool>(), "Edge Selection"), + start_vertices_(start_verts), next_vertex_(next_vertex) { category_ = Category::Generated; } - GVArray get_varray_for_context(const GeometryComponent &component, + GVArray get_varray_for_context(const Mesh &mesh, const eAttrDomain domain, - [[maybe_unused]] IndexMask mask) const final + const IndexMask /*mask*/) const final { - if (component.type() != GEO_COMPONENT_TYPE_MESH) { - return {}; - } - - const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); - const Mesh *mesh = mesh_component.get_for_read(); - if (mesh == nullptr) { - return {}; - } - - GeometryComponentFieldContext context{mesh_component, ATTR_DOMAIN_POINT}; - fn::FieldEvaluator evaluator{context, mesh_component.attribute_domain_size(ATTR_DOMAIN_POINT)}; + bke::MeshFieldContext context{mesh, ATTR_DOMAIN_POINT}; + fn::FieldEvaluator evaluator{context, mesh.totvert}; evaluator.add(next_vertex_); evaluator.add(start_vertices_); evaluator.evaluate(); @@ -94,12 +83,12 @@ class PathToEdgeSelectionFieldInput final : public GeometryFieldInput { return {}; } - Array<bool> selection(mesh->totedge, false); + Array<bool> selection(mesh.totedge, false); MutableSpan<bool> selection_span = selection.as_mutable_span(); - edge_paths_to_selection(*mesh, start_verts, next_vert, selection_span); + edge_paths_to_selection(mesh, start_verts, next_vert, selection_span); - return mesh_component.attributes()->adapt_domain<bool>( + return mesh.attributes().adapt_domain<bool>( VArray<bool>::ForContainer(std::move(selection)), ATTR_DOMAIN_EDGE, domain); } @@ -121,10 +110,10 @@ class PathToEdgeSelectionFieldInput final : public GeometryFieldInput { static void node_geo_exec(GeoNodeExecParams params) { - Field<bool> start_vertices = params.extract_input<Field<bool>>("Start Vertices"); + Field<bool> start_verts = params.extract_input<Field<bool>>("Start Vertices"); Field<int> next_vertex = params.extract_input<Field<int>>("Next Vertex Index"); Field<bool> selection_field{ - std::make_shared<PathToEdgeSelectionFieldInput>(start_vertices, next_vertex)}; + std::make_shared<PathToEdgeSelectionFieldInput>(start_verts, next_vertex)}; params.set_output("Selection", std::move(selection_field)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc index 84acab47661..0b4d5bd53f3 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "DNA_mesh_types.h" + #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" @@ -51,19 +53,18 @@ static void node_geo_exec(GeoNodeExecParams params) const Field<bool> selection_field = params.extract_input<Field<bool>>("Selection"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (!geometry_set.has_mesh()) { - return; - } + if (const Mesh *mesh = geometry_set.get_mesh_for_write()) { - const MeshComponent &mesh_component = *geometry_set.get_component_for_read<MeshComponent>(); - GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_EDGE}; - const int domain_size = mesh_component.attribute_domain_size(ATTR_DOMAIN_EDGE); - fn::FieldEvaluator selection_evaluator{field_context, domain_size}; - selection_evaluator.add(selection_field); - selection_evaluator.evaluate(); - const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); + bke::MeshFieldContext field_context{*mesh, ATTR_DOMAIN_EDGE}; + fn::FieldEvaluator selection_evaluator{field_context, mesh->totedge}; + selection_evaluator.add(selection_field); + selection_evaluator.evaluate(); + const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); - geometry_set.replace_mesh(mesh_edge_split(*mesh_component.get_for_read(), selection)); + Mesh *result = mesh_edge_split(*mesh, selection); + + geometry_set.replace_mesh(result); + } }); params.set_output("Mesh", std::move(geometry_set)); diff --git a/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc index 024dbd1c852..5614d5b66dc 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc @@ -61,15 +61,15 @@ struct AttributeOutputs { StrongAnonymousAttributeID side_id; }; -static void save_selection_as_attribute(MeshComponent &component, +static void save_selection_as_attribute(Mesh &mesh, const AnonymousAttributeID *id, const eAttrDomain domain, const IndexMask selection) { - BLI_assert(!component.attributes()->contains(id)); + MutableAttributeAccessor attributes = mesh.attributes_for_write(); + BLI_assert(!attributes.contains(id)); - SpanAttributeWriter<bool> attribute = - component.attributes_for_write()->lookup_or_add_for_write_span<bool>(id, domain); + SpanAttributeWriter<bool> attribute = attributes.lookup_or_add_for_write_span<bool>(id, domain); /* Rely on the new attribute being zeroed by default. */ BLI_assert(!attribute.span.as_span().contains(true)); @@ -83,31 +83,6 @@ static void save_selection_as_attribute(MeshComponent &component, attribute.finish(); } -static MutableSpan<MVert> mesh_verts(Mesh &mesh) -{ - return {mesh.mvert, mesh.totvert}; -} -static MutableSpan<MEdge> mesh_edges(Mesh &mesh) -{ - return {mesh.medge, mesh.totedge}; -} -static Span<MPoly> mesh_polys(const Mesh &mesh) -{ - return {mesh.mpoly, mesh.totpoly}; -} -static MutableSpan<MPoly> mesh_polys(Mesh &mesh) -{ - return {mesh.mpoly, mesh.totpoly}; -} -static Span<MLoop> mesh_loops(const Mesh &mesh) -{ - return {mesh.mloop, mesh.totloop}; -} -static MutableSpan<MLoop> mesh_loops(Mesh &mesh) -{ - return {mesh.mloop, mesh.totloop}; -} - /** * \note Some areas in this file rely on the new sections of attributes from #CustomData_realloc * to be zeroed. @@ -119,30 +94,25 @@ static void expand_mesh(Mesh &mesh, const int loop_expand) { if (vert_expand != 0) { - CustomData_duplicate_referenced_layers(&mesh.vdata, mesh.totvert); + const int old_verts_num = mesh.totvert; mesh.totvert += vert_expand; - CustomData_realloc(&mesh.vdata, mesh.totvert); - } - else { - /* Even when the number of vertices is not changed, the mesh can still be deformed. */ - CustomData_duplicate_referenced_layer(&mesh.vdata, CD_MVERT, mesh.totvert); + CustomData_realloc(&mesh.vdata, old_verts_num, mesh.totvert); } if (edge_expand != 0) { - CustomData_duplicate_referenced_layers(&mesh.edata, mesh.totedge); + const int old_edges_num = mesh.totedge; mesh.totedge += edge_expand; - CustomData_realloc(&mesh.edata, mesh.totedge); + CustomData_realloc(&mesh.edata, old_edges_num, mesh.totedge); } if (poly_expand != 0) { - CustomData_duplicate_referenced_layers(&mesh.pdata, mesh.totpoly); + const int old_polys_num = mesh.totpoly; mesh.totpoly += poly_expand; - CustomData_realloc(&mesh.pdata, mesh.totpoly); + CustomData_realloc(&mesh.pdata, old_polys_num, mesh.totpoly); } if (loop_expand != 0) { - CustomData_duplicate_referenced_layers(&mesh.ldata, mesh.totloop); + const int old_loops_num = mesh.totloop; mesh.totloop += loop_expand; - CustomData_realloc(&mesh.ldata, mesh.totloop); + CustomData_realloc(&mesh.ldata, old_loops_num, mesh.totloop); } - BKE_mesh_update_customdata_pointers(&mesh, false); } static CustomData &get_customdata(Mesh &mesh, const eAttrDomain domain) @@ -162,9 +132,12 @@ static CustomData &get_customdata(Mesh &mesh, const eAttrDomain domain) } } +/** + * \note The result may be an empty span. + */ static MutableSpan<int> get_orig_index_layer(Mesh &mesh, const eAttrDomain domain) { - const bke::AttributeAccessor attributes = bke::mesh_attributes(mesh); + const bke::AttributeAccessor attributes = mesh.attributes(); CustomData &custom_data = get_customdata(mesh, domain); if (int *orig_indices = static_cast<int *>(CustomData_get_layer(&custom_data, CD_ORIGINDEX))) { return {orig_indices, attributes.domain_size(domain)}; @@ -247,16 +220,15 @@ static Array<Vector<int>> create_vert_to_edge_map(const int vert_size, return vert_to_edge_map; } -static void extrude_mesh_vertices(MeshComponent &component, +static void extrude_mesh_vertices(Mesh &mesh, const Field<bool> &selection_field, const Field<float3> &offset_field, const AttributeOutputs &attribute_outputs) { - Mesh &mesh = *component.get_for_write(); const int orig_vert_size = mesh.totvert; const int orig_edge_size = mesh.totedge; - GeometryComponentFieldContext context{component, ATTR_DOMAIN_POINT}; + bke::MeshFieldContext context{mesh, ATTR_DOMAIN_POINT}; FieldEvaluator evaluator{context, mesh.totvert}; evaluator.add(offset_field); evaluator.set_selection(selection_field); @@ -265,21 +237,21 @@ static void extrude_mesh_vertices(MeshComponent &component, const VArray<float3> offsets = evaluator.get_evaluated<float3>(0); /* This allows parallelizing attribute mixing for new edges. */ - Array<Vector<int>> vert_to_edge_map = create_vert_to_edge_map(orig_vert_size, mesh_edges(mesh)); + Array<Vector<int>> vert_to_edge_map = create_vert_to_edge_map(orig_vert_size, mesh.edges()); expand_mesh(mesh, selection.size(), selection.size(), 0, 0); const IndexRange new_vert_range{orig_vert_size, selection.size()}; const IndexRange new_edge_range{orig_edge_size, selection.size()}; - MutableSpan<MVert> new_verts = mesh_verts(mesh).slice(new_vert_range); - MutableSpan<MEdge> new_edges = mesh_edges(mesh).slice(new_edge_range); + MutableSpan<MVert> new_verts = mesh.verts_for_write().slice(new_vert_range); + MutableSpan<MEdge> new_edges = mesh.edges_for_write().slice(new_edge_range); for (const int i_selection : selection.index_range()) { new_edges[i_selection] = new_loose_edge(selection[i_selection], new_vert_range[i_selection]); } - MutableAttributeAccessor attributes = *component.attributes_for_write(); + MutableAttributeAccessor attributes = mesh.attributes_for_write(); attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) { if (!ELEM(meta_data.domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_EDGE)) { @@ -324,13 +296,16 @@ static void extrude_mesh_vertices(MeshComponent &component, MutableSpan<int> vert_orig_indices = get_orig_index_layer(mesh, ATTR_DOMAIN_POINT); vert_orig_indices.slice(new_vert_range).fill(ORIGINDEX_NONE); + MutableSpan<int> new_edge_orig_indices = get_orig_index_layer(mesh, ATTR_DOMAIN_EDGE); + new_edge_orig_indices.slice(new_edge_range).fill(ORIGINDEX_NONE); + if (attribute_outputs.top_id) { save_selection_as_attribute( - component, attribute_outputs.top_id.get(), ATTR_DOMAIN_POINT, new_vert_range); + mesh, attribute_outputs.top_id.get(), ATTR_DOMAIN_POINT, new_vert_range); } if (attribute_outputs.side_id) { save_selection_as_attribute( - component, attribute_outputs.side_id.get(), ATTR_DOMAIN_EDGE, new_edge_range); + mesh, attribute_outputs.side_id.get(), ATTR_DOMAIN_EDGE, new_edge_range); } BKE_mesh_runtime_clear_cache(&mesh); @@ -338,8 +313,8 @@ static void extrude_mesh_vertices(MeshComponent &component, static Array<Vector<int, 2>> mesh_calculate_polys_of_edge(const Mesh &mesh) { - Span<MPoly> polys = mesh_polys(mesh); - Span<MLoop> loops = mesh_loops(mesh); + Span<MPoly> polys = mesh.polys(); + Span<MLoop> loops = mesh.loops(); Array<Vector<int, 2>> polys_of_edge(mesh.totedge); for (const int i_poly : polys.index_range()) { @@ -397,29 +372,29 @@ template<typename T> static VectorSet<int> vert_indices_from_edges(const Mesh &mesh, const Span<T> edge_indices) { static_assert(is_same_any_v<T, int, int64_t>); + const Span<MEdge> edges = mesh.edges(); VectorSet<int> vert_indices; vert_indices.reserve(edge_indices.size()); for (const T i_edge : edge_indices) { - const MEdge &edge = mesh.medge[i_edge]; + const MEdge &edge = edges[i_edge]; vert_indices.add(edge.v1); vert_indices.add(edge.v2); } return vert_indices; } -static void extrude_mesh_edges(MeshComponent &component, +static void extrude_mesh_edges(Mesh &mesh, const Field<bool> &selection_field, const Field<float3> &offset_field, const AttributeOutputs &attribute_outputs) { - Mesh &mesh = *component.get_for_write(); const int orig_vert_size = mesh.totvert; - Span<MEdge> orig_edges = mesh_edges(mesh); - Span<MPoly> orig_polys = mesh_polys(mesh); + Span<MEdge> orig_edges = mesh.edges(); + Span<MPoly> orig_polys = mesh.polys(); const int orig_loop_size = mesh.totloop; - GeometryComponentFieldContext edge_context{component, ATTR_DOMAIN_EDGE}; + bke::MeshFieldContext edge_context{mesh, ATTR_DOMAIN_EDGE}; FieldEvaluator edge_evaluator{edge_context, mesh.totedge}; edge_evaluator.set_selection(selection_field); edge_evaluator.add(offset_field); @@ -465,12 +440,12 @@ static void extrude_mesh_edges(MeshComponent &component, new_poly_range.size(), new_loop_range.size()); - MutableSpan<MVert> new_verts = mesh_verts(mesh).slice(new_vert_range); - MutableSpan<MEdge> connect_edges = mesh_edges(mesh).slice(connect_edge_range); - MutableSpan<MEdge> duplicate_edges = mesh_edges(mesh).slice(duplicate_edge_range); - MutableSpan<MPoly> polys = mesh_polys(mesh); + MutableSpan<MEdge> edges = mesh.edges_for_write(); + MutableSpan<MEdge> connect_edges = edges.slice(connect_edge_range); + MutableSpan<MEdge> duplicate_edges = edges.slice(duplicate_edge_range); + MutableSpan<MPoly> polys = mesh.polys_for_write(); MutableSpan<MPoly> new_polys = polys.slice(new_poly_range); - MutableSpan<MLoop> loops = mesh_loops(mesh); + MutableSpan<MLoop> loops = mesh.loops_for_write(); MutableSpan<MLoop> new_loops = loops.slice(new_loop_range); for (const int i : connect_edges.index_range()) { @@ -478,7 +453,7 @@ static void extrude_mesh_edges(MeshComponent &component, } for (const int i : duplicate_edges.index_range()) { - const MEdge &orig_edge = mesh.medge[edge_selection[i]]; + const MEdge &orig_edge = edges[edge_selection[i]]; const int i_new_vert_1 = new_vert_indices.index_of(orig_edge.v1); const int i_new_vert_2 = new_vert_indices.index_of(orig_edge.v2); duplicate_edges[i] = new_edge(new_vert_range[i_new_vert_1], new_vert_range[i_new_vert_2]); @@ -525,7 +500,7 @@ static void extrude_mesh_edges(MeshComponent &component, const Array<Vector<int>> new_vert_to_duplicate_edge_map = create_vert_to_edge_map( new_vert_range.size(), duplicate_edges, orig_vert_size); - MutableAttributeAccessor attributes = *component.attributes_for_write(); + MutableAttributeAccessor attributes = mesh.attributes_for_write(); attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) { GSpanAttributeWriter attribute = attributes.lookup_or_add_for_write_span( @@ -633,6 +608,7 @@ static void extrude_mesh_edges(MeshComponent &component, return true; }); + MutableSpan<MVert> new_verts = mesh.verts_for_write().slice(new_vert_range); if (edge_offsets.is_single()) { const float3 offset = edge_offsets.get_internal_single(); threading::parallel_for(new_verts.index_range(), 1024, [&](const IndexRange range) { @@ -656,13 +632,16 @@ static void extrude_mesh_edges(MeshComponent &component, edge_orig_indices.slice(connect_edge_range).fill(ORIGINDEX_NONE); edge_orig_indices.slice(duplicate_edge_range).fill(ORIGINDEX_NONE); + MutableSpan<int> poly_orig_indices = get_orig_index_layer(mesh, ATTR_DOMAIN_FACE); + poly_orig_indices.slice(new_poly_range).fill(ORIGINDEX_NONE); + if (attribute_outputs.top_id) { save_selection_as_attribute( - component, attribute_outputs.top_id.get(), ATTR_DOMAIN_EDGE, duplicate_edge_range); + mesh, attribute_outputs.top_id.get(), ATTR_DOMAIN_EDGE, duplicate_edge_range); } if (attribute_outputs.side_id) { save_selection_as_attribute( - component, attribute_outputs.side_id.get(), ATTR_DOMAIN_FACE, new_poly_range); + mesh, attribute_outputs.side_id.get(), ATTR_DOMAIN_FACE, new_poly_range); } BKE_mesh_runtime_clear_cache(&mesh); @@ -672,18 +651,17 @@ static void extrude_mesh_edges(MeshComponent &component, * Edges connected to one selected face are on the boundary of a region and will be duplicated into * a "side face". Edges inside a region will be duplicated to leave any original faces unchanged. */ -static void extrude_mesh_face_regions(MeshComponent &component, +static void extrude_mesh_face_regions(Mesh &mesh, const Field<bool> &selection_field, const Field<float3> &offset_field, const AttributeOutputs &attribute_outputs) { - Mesh &mesh = *component.get_for_write(); const int orig_vert_size = mesh.totvert; - Span<MEdge> orig_edges = mesh_edges(mesh); - Span<MPoly> orig_polys = mesh_polys(mesh); - Span<MLoop> orig_loops = mesh_loops(mesh); + Span<MEdge> orig_edges = mesh.edges(); + Span<MPoly> orig_polys = mesh.polys(); + Span<MLoop> orig_loops = mesh.loops(); - GeometryComponentFieldContext poly_context{component, ATTR_DOMAIN_FACE}; + bke::MeshFieldContext poly_context{mesh, ATTR_DOMAIN_FACE}; FieldEvaluator poly_evaluator{poly_context, mesh.totpoly}; poly_evaluator.set_selection(selection_field); poly_evaluator.add(offset_field); @@ -784,7 +762,7 @@ static void extrude_mesh_face_regions(MeshComponent &component, /* The vertices attached to duplicate inner edges also have to be duplicated. */ for (const int i_edge : new_inner_edge_indices) { - const MEdge &edge = mesh.medge[i_edge]; + const MEdge &edge = orig_edges[i_edge]; new_vert_indices.add(edge.v1); new_vert_indices.add(edge.v2); } @@ -808,13 +786,13 @@ static void extrude_mesh_face_regions(MeshComponent &component, side_poly_range.size(), side_loop_range.size()); - MutableSpan<MEdge> edges = mesh_edges(mesh); + MutableSpan<MEdge> edges = mesh.edges_for_write(); MutableSpan<MEdge> connect_edges = edges.slice(connect_edge_range); MutableSpan<MEdge> boundary_edges = edges.slice(boundary_edge_range); MutableSpan<MEdge> new_inner_edges = edges.slice(new_inner_edge_range); - MutableSpan<MPoly> polys = mesh_polys(mesh); + MutableSpan<MPoly> polys = mesh.polys_for_write(); MutableSpan<MPoly> new_polys = polys.slice(side_poly_range); - MutableSpan<MLoop> loops = mesh_loops(mesh); + MutableSpan<MLoop> loops = mesh.loops_for_write(); MutableSpan<MLoop> new_loops = loops.slice(side_loop_range); /* Initialize the edges that form the sides of the extrusion. */ @@ -905,7 +883,7 @@ static void extrude_mesh_face_regions(MeshComponent &component, const Array<Vector<int>> new_vert_to_duplicate_edge_map = create_vert_to_edge_map( new_vert_range.size(), boundary_edges, orig_vert_size); - MutableAttributeAccessor attributes = *component.attributes_for_write(); + MutableAttributeAccessor attributes = mesh.attributes_for_write(); attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) { GSpanAttributeWriter attribute = attributes.lookup_or_add_for_write_span( @@ -1003,13 +981,14 @@ static void extrude_mesh_face_regions(MeshComponent &component, /* Translate vertices based on the offset. If the vertex is used by a selected edge, it will * have been duplicated and only the new vertex should use the offset. Otherwise the vertex might * still need an offset, but it was reused on the inside of a region of extruded faces. */ + MutableSpan<MVert> verts = mesh.verts_for_write(); if (poly_offsets.is_single()) { const float3 offset = poly_offsets.get_internal_single(); threading::parallel_for( IndexRange(all_selected_verts.size()), 1024, [&](const IndexRange range) { for (const int i_orig : all_selected_verts.as_span().slice(range)) { const int i_new = new_vert_indices.index_of_try(i_orig); - MVert &vert = mesh_verts(mesh)[(i_new == -1) ? i_orig : new_vert_range[i_new]]; + MVert &vert = verts[(i_new == -1) ? i_orig : new_vert_range[i_new]]; add_v3_v3(vert.co, offset); } }); @@ -1020,7 +999,7 @@ static void extrude_mesh_face_regions(MeshComponent &component, for (const int i_orig : all_selected_verts.as_span().slice(range)) { const int i_new = new_vert_indices.index_of_try(i_orig); const float3 offset = vert_offsets[i_orig]; - MVert &vert = mesh_verts(mesh)[(i_new == -1) ? i_orig : new_vert_range[i_new]]; + MVert &vert = verts[(i_new == -1) ? i_orig : new_vert_range[i_new]]; add_v3_v3(vert.co, offset); } }); @@ -1039,11 +1018,11 @@ static void extrude_mesh_face_regions(MeshComponent &component, if (attribute_outputs.top_id) { save_selection_as_attribute( - component, attribute_outputs.top_id.get(), ATTR_DOMAIN_FACE, poly_selection); + mesh, attribute_outputs.top_id.get(), ATTR_DOMAIN_FACE, poly_selection); } if (attribute_outputs.side_id) { save_selection_as_attribute( - component, attribute_outputs.side_id.get(), ATTR_DOMAIN_FACE, side_poly_range); + mesh, attribute_outputs.side_id.get(), ATTR_DOMAIN_FACE, side_poly_range); } BKE_mesh_runtime_clear_cache(&mesh); @@ -1057,21 +1036,20 @@ static IndexRange selected_corner_range(Span<int> offsets, const int index) return IndexRange(offset, next_offset - offset); } -static void extrude_individual_mesh_faces(MeshComponent &component, +static void extrude_individual_mesh_faces(Mesh &mesh, const Field<bool> &selection_field, const Field<float3> &offset_field, const AttributeOutputs &attribute_outputs) { - Mesh &mesh = *component.get_for_write(); const int orig_vert_size = mesh.totvert; const int orig_edge_size = mesh.totedge; - Span<MPoly> orig_polys = mesh_polys(mesh); - Span<MLoop> orig_loops = mesh_loops(mesh); + Span<MPoly> orig_polys = mesh.polys(); + Span<MLoop> orig_loops = mesh.loops(); /* Use a mesh for the result of the evaluation because the mesh is reallocated before * the vertices are moved, and the evaluated result might reference an attribute. */ Array<float3> poly_offset(orig_polys.size()); - GeometryComponentFieldContext poly_context{component, ATTR_DOMAIN_FACE}; + bke::MeshFieldContext poly_context{mesh, ATTR_DOMAIN_FACE}; FieldEvaluator poly_evaluator{poly_context, mesh.totpoly}; poly_evaluator.set_selection(selection_field); poly_evaluator.add_with_destination(offset_field, poly_offset.as_mutable_span()); @@ -1105,13 +1083,13 @@ static void extrude_individual_mesh_faces(MeshComponent &component, side_poly_range.size(), side_loop_range.size()); - MutableSpan<MVert> new_verts = mesh_verts(mesh).slice(new_vert_range); - MutableSpan<MEdge> edges{mesh.medge, mesh.totedge}; + MutableSpan<MVert> new_verts = mesh.verts_for_write().slice(new_vert_range); + MutableSpan<MEdge> edges = mesh.edges_for_write(); MutableSpan<MEdge> connect_edges = edges.slice(connect_edge_range); MutableSpan<MEdge> duplicate_edges = edges.slice(duplicate_edge_range); - MutableSpan<MPoly> polys{mesh.mpoly, mesh.totpoly}; + MutableSpan<MPoly> polys = mesh.polys_for_write(); MutableSpan<MPoly> new_polys = polys.slice(side_poly_range); - MutableSpan<MLoop> loops{mesh.mloop, mesh.totloop}; + MutableSpan<MLoop> loops = mesh.loops_for_write(); /* For every selected polygon, build the faces that form the sides of the extrusion. Filling some * of this data like the new edges or polygons could be easily split into separate loops, which @@ -1159,7 +1137,7 @@ static void extrude_individual_mesh_faces(MeshComponent &component, } }); - MutableAttributeAccessor attributes = *component.attributes_for_write(); + MutableAttributeAccessor attributes = mesh.attributes_for_write(); attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) { GSpanAttributeWriter attribute = attributes.lookup_or_add_for_write_span( @@ -1318,11 +1296,11 @@ static void extrude_individual_mesh_faces(MeshComponent &component, if (attribute_outputs.top_id) { save_selection_as_attribute( - component, attribute_outputs.top_id.get(), ATTR_DOMAIN_FACE, poly_selection); + mesh, attribute_outputs.top_id.get(), ATTR_DOMAIN_FACE, poly_selection); } if (attribute_outputs.side_id) { save_selection_as_attribute( - component, attribute_outputs.side_id.get(), ATTR_DOMAIN_FACE, side_poly_range); + mesh, attribute_outputs.side_id.get(), ATTR_DOMAIN_FACE, side_poly_range); } BKE_mesh_runtime_clear_cache(&mesh); @@ -1359,27 +1337,26 @@ static void node_geo_exec(GeoNodeExecParams params) params.extract_input<bool>("Individual"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (geometry_set.has_mesh()) { - MeshComponent &component = geometry_set.get_component_for_write<MeshComponent>(); + if (Mesh *mesh = geometry_set.get_mesh_for_write()) { switch (mode) { case GEO_NODE_EXTRUDE_MESH_VERTICES: - extrude_mesh_vertices(component, selection, final_offset, attribute_outputs); + extrude_mesh_vertices(*mesh, selection, final_offset, attribute_outputs); break; case GEO_NODE_EXTRUDE_MESH_EDGES: - extrude_mesh_edges(component, selection, final_offset, attribute_outputs); + extrude_mesh_edges(*mesh, selection, final_offset, attribute_outputs); break; case GEO_NODE_EXTRUDE_MESH_FACES: { if (extrude_individual) { - extrude_individual_mesh_faces(component, selection, final_offset, attribute_outputs); + extrude_individual_mesh_faces(*mesh, selection, final_offset, attribute_outputs); } else { - extrude_mesh_face_regions(component, selection, final_offset, attribute_outputs); + extrude_mesh_face_regions(*mesh, selection, final_offset, attribute_outputs); } break; } } - BLI_assert(BKE_mesh_is_valid(component.get_for_write())); + BLI_assert(BKE_mesh_is_valid(mesh)); } }); diff --git a/source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc b/source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc index bde4af12d84..3c9889b664b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc @@ -89,7 +89,7 @@ static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) } } -class FieldAtIndex final : public GeometryFieldInput { +class FieldAtIndex final : public bke::GeometryFieldInput { private: Field<int> index_field_; GField value_field_; @@ -97,26 +97,25 @@ class FieldAtIndex final : public GeometryFieldInput { public: FieldAtIndex(Field<int> index_field, GField value_field, eAttrDomain value_field_domain) - : GeometryFieldInput(value_field.cpp_type(), "Field at Index"), + : bke::GeometryFieldInput(value_field.cpp_type(), "Field at Index"), index_field_(std::move(index_field)), value_field_(std::move(value_field)), value_field_domain_(value_field_domain) { } - GVArray get_varray_for_context(const GeometryComponent &component, - const eAttrDomain domain, - IndexMask mask) const final + GVArray get_varray_for_context(const bke::GeometryFieldContext &context, + const IndexMask mask) const final { - const GeometryComponentFieldContext value_field_context{component, value_field_domain_}; + const bke::GeometryFieldContext value_field_context{ + context.geometry(), context.type(), value_field_domain_}; FieldEvaluator value_evaluator{value_field_context, - component.attribute_domain_size(value_field_domain_)}; + context.attributes()->domain_size(value_field_domain_)}; value_evaluator.add(value_field_); value_evaluator.evaluate(); const GVArray &values = value_evaluator.get_evaluated(0); - const GeometryComponentFieldContext index_field_context{component, domain}; - FieldEvaluator index_evaluator{index_field_context, &mask}; + FieldEvaluator index_evaluator{context, &mask}; index_evaluator.add(index_field_); index_evaluator.evaluate(); const VArray<int> indices = index_evaluator.get_evaluated<int>(0); @@ -129,7 +128,7 @@ class FieldAtIndex final : public GeometryFieldInput { threading::parallel_for(mask.index_range(), 1024, [&](const IndexRange range) { for (const int i : mask.slice(range)) { const int index = indices[i]; - if (index >= 0 && index < src_values.size()) { + if (src_values.index_range().contains(index)) { dst_array[i] = src_values[index]; } else { diff --git a/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc index 15b2822805a..613425716d4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc @@ -19,24 +19,19 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Geometry>(N_("Mesh")); } -static void mesh_flip_faces(MeshComponent &component, const Field<bool> &selection_field) +static void mesh_flip_faces(Mesh &mesh, const Field<bool> &selection_field) { - GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_FACE}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE); - if (domain_size == 0) { + if (mesh.totpoly == 0) { return; } - fn::FieldEvaluator evaluator{field_context, domain_size}; + bke::MeshFieldContext field_context{mesh, ATTR_DOMAIN_FACE}; + fn::FieldEvaluator evaluator{field_context, mesh.totpoly}; evaluator.add(selection_field); evaluator.evaluate(); const IndexMask selection = evaluator.get_evaluated_as_mask(0); - Mesh *mesh = component.get_for_write(); - - mesh->mloop = (MLoop *)CustomData_duplicate_referenced_layer( - &mesh->ldata, CD_MLOOP, mesh->totloop); - Span<MPoly> polys{mesh->mpoly, mesh->totpoly}; - MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop}; + const Span<MPoly> polys = mesh.polys(); + MutableSpan<MLoop> loops = mesh.loops_for_write(); for (const int i : selection.index_range()) { const MPoly &poly = polys[selection[i]]; @@ -49,7 +44,7 @@ static void mesh_flip_faces(MeshComponent &component, const Field<bool> &selecti } } - MutableAttributeAccessor attributes = *component.attributes_for_write(); + MutableAttributeAccessor attributes = mesh.attributes_for_write(); attributes.for_all( [&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { if (meta_data.domain == ATTR_DOMAIN_CORNER) { @@ -76,11 +71,9 @@ static void node_geo_exec(GeoNodeExecParams params) const Field<bool> selection_field = params.extract_input<Field<bool>>("Selection"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (!geometry_set.has_mesh()) { - return; + if (Mesh *mesh = geometry_set.get_mesh_for_write()) { + mesh_flip_faces(*mesh, selection_field); } - MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>(); - mesh_flip_faces(mesh_component, selection_field); }); params.set_output("Mesh", std::move(geometry_set)); diff --git a/source/blender/nodes/geometry/nodes/node_geo_geometry_to_instance.cc b/source/blender/nodes/geometry/nodes/node_geo_geometry_to_instance.cc index 1f84f8f288d..8e64209a418 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_geometry_to_instance.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_geometry_to_instance.cc @@ -12,7 +12,7 @@ static void node_declare(NodeDeclarationBuilder &b) static void node_geo_exec(GeoNodeExecParams params) { - Vector<GeometrySet> geometries = params.extract_multi_input<GeometrySet>("Geometry"); + Vector<GeometrySet> geometries = params.extract_input<Vector<GeometrySet>>("Geometry"); GeometrySet instances_geometry; InstancesComponent &instances_component = instances_geometry.get_component_for_write<InstancesComponent>(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc index bc1b9e940a1..bff2e7831c6 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "BKE_curves.hh" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_input_curve_handles_cc { @@ -15,31 +17,27 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Vector>(N_("Right")).field_source(); } -class HandlePositionFieldInput final : public GeometryFieldInput { +class HandlePositionFieldInput final : public bke::CurvesFieldInput { Field<bool> relative_; bool left_; public: HandlePositionFieldInput(Field<bool> relative, bool left) - : GeometryFieldInput(CPPType::get<float3>(), "Handle"), relative_(relative), left_(left) + : bke::CurvesFieldInput(CPPType::get<float3>(), "Handle"), relative_(relative), left_(left) { } - GVArray get_varray_for_context(const GeometryComponent &component, + GVArray get_varray_for_context(const bke::CurvesGeometry &curves, const eAttrDomain domain, - IndexMask mask) const final + const IndexMask mask) const final { - if (component.type() != GEO_COMPONENT_TYPE_CURVE) { - return {}; - } - - GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; + bke::CurvesFieldContext field_context{curves, ATTR_DOMAIN_POINT}; fn::FieldEvaluator evaluator(field_context, &mask); evaluator.add(relative_); evaluator.evaluate(); const VArray<bool> relative = evaluator.get_evaluated<bool>(0); - const AttributeAccessor attributes = *component.attributes(); + const AttributeAccessor attributes = curves.attributes(); VArray<float3> positions = attributes.lookup_or_default<float3>( "position", ATTR_DOMAIN_POINT, {0, 0, 0}); @@ -69,7 +67,7 @@ class HandlePositionFieldInput final : public GeometryFieldInput { output[i] = handles[i]; } } - return component.attributes()->adapt_domain<float3>( + return attributes.adapt_domain<float3>( VArray<float3>::ForContainer(std::move(output)), ATTR_DOMAIN_POINT, domain); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_instance_rotation.cc b/source/blender/nodes/geometry/nodes/node_geo_input_instance_rotation.cc index 4c7a148a797..8c5a92904ab 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_instance_rotation.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_instance_rotation.cc @@ -9,28 +9,20 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Vector>(N_("Rotation")).field_source(); } -class VectorFieldInput final : public GeometryFieldInput { +class InstanceRotationFieldInput final : public bke::InstancesFieldInput { public: - VectorFieldInput() : GeometryFieldInput(CPPType::get<float3>(), "Rotation") + InstanceRotationFieldInput() : bke::InstancesFieldInput(CPPType::get<float3>(), "Rotation") { } - GVArray get_varray_for_context(const GeometryComponent &component, - const eAttrDomain UNUSED(domain), + GVArray get_varray_for_context(const InstancesComponent &instances, IndexMask UNUSED(mask)) const final { - if (component.type() != GEO_COMPONENT_TYPE_INSTANCES) { - return {}; - } - - const InstancesComponent &instance_component = static_cast<const InstancesComponent &>( - component); - auto rotation_fn = [&](const int i) -> float3 { - return instance_component.instance_transforms()[i].to_euler(); + return instances.instance_transforms()[i].to_euler(); }; - return VArray<float3>::ForFunc(instance_component.instances_num(), rotation_fn); + return VArray<float3>::ForFunc(instances.instances_num(), rotation_fn); } uint64_t hash() const override @@ -40,13 +32,13 @@ class VectorFieldInput final : public GeometryFieldInput { bool is_equal_to(const fn::FieldNode &other) const override { - return dynamic_cast<const VectorFieldInput *>(&other) != nullptr; + return dynamic_cast<const InstanceRotationFieldInput *>(&other) != nullptr; } }; static void node_geo_exec(GeoNodeExecParams params) { - Field<float3> rotation{std::make_shared<VectorFieldInput>()}; + Field<float3> rotation{std::make_shared<InstanceRotationFieldInput>()}; params.set_output("Rotation", std::move(rotation)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_instance_scale.cc b/source/blender/nodes/geometry/nodes/node_geo_input_instance_scale.cc index b3a362fbf3e..b79e73915b7 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_instance_scale.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_instance_scale.cc @@ -9,28 +9,20 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Vector>(N_("Scale")).field_source(); } -class VectorFieldInput final : public GeometryFieldInput { +class InstanceScaleFieldInput final : public bke::InstancesFieldInput { public: - VectorFieldInput() : GeometryFieldInput(CPPType::get<float3>(), "Scale") + InstanceScaleFieldInput() : bke::InstancesFieldInput(CPPType::get<float3>(), "Scale") { } - GVArray get_varray_for_context(const GeometryComponent &component, - const eAttrDomain UNUSED(domain), + GVArray get_varray_for_context(const InstancesComponent &instances, IndexMask UNUSED(mask)) const final { - if (component.type() != GEO_COMPONENT_TYPE_INSTANCES) { - return {}; - } - - const InstancesComponent &instance_component = static_cast<const InstancesComponent &>( - component); - auto scale_fn = [&](const int i) -> float3 { - return instance_component.instance_transforms()[i].scale(); + return instances.instance_transforms()[i].scale(); }; - return VArray<float3>::ForFunc(instance_component.instances_num(), scale_fn); + return VArray<float3>::ForFunc(instances.instances_num(), scale_fn); } uint64_t hash() const override @@ -40,13 +32,13 @@ class VectorFieldInput final : public GeometryFieldInput { bool is_equal_to(const fn::FieldNode &other) const override { - return dynamic_cast<const VectorFieldInput *>(&other) != nullptr; + return dynamic_cast<const InstanceScaleFieldInput *>(&other) != nullptr; } }; static void node_geo_exec(GeoNodeExecParams params) { - Field<float3> scale{std::make_shared<VectorFieldInput>()}; + Field<float3> scale{std::make_shared<InstanceScaleFieldInput>()}; params.set_output("Scale", std::move(scale)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_angle.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_angle.cc index b009aaa5291..f2e7379b3a2 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_angle.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_angle.cc @@ -53,46 +53,36 @@ static Array<EdgeMapEntry> create_edge_map(const Span<MPoly> polys, return edge_map; } -class AngleFieldInput final : public GeometryFieldInput { +class AngleFieldInput final : public bke::MeshFieldInput { public: - AngleFieldInput() : GeometryFieldInput(CPPType::get<float>(), "Unsigned Angle Field") + AngleFieldInput() : bke::MeshFieldInput(CPPType::get<float>(), "Unsigned Angle Field") { category_ = Category::Generated; } - GVArray get_varray_for_context(const GeometryComponent &component, + GVArray get_varray_for_context(const Mesh &mesh, const eAttrDomain domain, IndexMask UNUSED(mask)) const final { - if (component.type() != GEO_COMPONENT_TYPE_MESH) { - return {}; - } - - const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); - const Mesh *mesh = mesh_component.get_for_read(); - if (mesh == nullptr) { - return {}; - } + const Span<MVert> verts = mesh.verts(); + const Span<MPoly> polys = mesh.polys(); + const Span<MLoop> loops = mesh.loops(); + Array<EdgeMapEntry> edge_map = create_edge_map(polys, loops, mesh.totedge); - Span<MPoly> polys{mesh->mpoly, mesh->totpoly}; - Span<MLoop> loops{mesh->mloop, mesh->totloop}; - Array<EdgeMapEntry> edge_map = create_edge_map(polys, loops, mesh->totedge); - - auto angle_fn = [edge_map, polys, loops, mesh](const int i) -> float { + auto angle_fn = [edge_map = std::move(edge_map), verts, polys, loops](const int i) -> float { if (edge_map[i].face_count != 2) { return 0.0f; } const MPoly &mpoly_1 = polys[edge_map[i].face_index_1]; const MPoly &mpoly_2 = polys[edge_map[i].face_index_2]; float3 normal_1, normal_2; - BKE_mesh_calc_poly_normal(&mpoly_1, &loops[mpoly_1.loopstart], mesh->mvert, normal_1); - BKE_mesh_calc_poly_normal(&mpoly_2, &loops[mpoly_2.loopstart], mesh->mvert, normal_2); + BKE_mesh_calc_poly_normal(&mpoly_1, &loops[mpoly_1.loopstart], verts.data(), normal_1); + BKE_mesh_calc_poly_normal(&mpoly_2, &loops[mpoly_2.loopstart], verts.data(), normal_2); return angle_normalized_v3v3(normal_1, normal_2); }; - VArray<float> angles = VArray<float>::ForFunc(mesh->totedge, angle_fn); - return component.attributes()->adapt_domain<float>( - std::move(angles), ATTR_DOMAIN_EDGE, domain); + VArray<float> angles = VArray<float>::ForFunc(mesh.totedge, angle_fn); + return mesh.attributes().adapt_domain<float>(std::move(angles), ATTR_DOMAIN_EDGE, domain); } uint64_t hash() const override @@ -107,32 +97,25 @@ class AngleFieldInput final : public GeometryFieldInput { } }; -class SignedAngleFieldInput final : public GeometryFieldInput { +class SignedAngleFieldInput final : public bke::MeshFieldInput { public: - SignedAngleFieldInput() : GeometryFieldInput(CPPType::get<float>(), "Signed Angle Field") + SignedAngleFieldInput() : bke::MeshFieldInput(CPPType::get<float>(), "Signed Angle Field") { category_ = Category::Generated; } - GVArray get_varray_for_context(const GeometryComponent &component, + GVArray get_varray_for_context(const Mesh &mesh, const eAttrDomain domain, IndexMask UNUSED(mask)) const final { - if (component.type() != GEO_COMPONENT_TYPE_MESH) { - return {}; - } - - const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); - const Mesh *mesh = mesh_component.get_for_read(); - if (mesh == nullptr) { - return {}; - } - - Span<MPoly> polys{mesh->mpoly, mesh->totpoly}; - Span<MLoop> loops{mesh->mloop, mesh->totloop}; - Array<EdgeMapEntry> edge_map = create_edge_map(polys, loops, mesh->totedge); - - auto angle_fn = [edge_map, polys, loops, mesh](const int i) -> float { + const Span<MVert> verts = mesh.verts(); + const Span<MEdge> edges = mesh.edges(); + const Span<MPoly> polys = mesh.polys(); + const Span<MLoop> loops = mesh.loops(); + Array<EdgeMapEntry> edge_map = create_edge_map(polys, loops, mesh.totedge); + + auto angle_fn = + [edge_map = std::move(edge_map), verts, edges, polys, loops](const int i) -> float { if (edge_map[i].face_count != 2) { return 0.0f; } @@ -141,18 +124,18 @@ class SignedAngleFieldInput final : public GeometryFieldInput { /* Find the normals of the 2 polys. */ float3 poly_1_normal, poly_2_normal; - BKE_mesh_calc_poly_normal(&mpoly_1, &loops[mpoly_1.loopstart], mesh->mvert, poly_1_normal); - BKE_mesh_calc_poly_normal(&mpoly_2, &loops[mpoly_2.loopstart], mesh->mvert, poly_2_normal); + BKE_mesh_calc_poly_normal(&mpoly_1, &loops[mpoly_1.loopstart], verts.data(), poly_1_normal); + BKE_mesh_calc_poly_normal(&mpoly_2, &loops[mpoly_2.loopstart], verts.data(), poly_2_normal); /* Find the centerpoint of the axis edge */ - const float3 edge_centerpoint = (float3(mesh->mvert[mesh->medge[i].v1].co) + - float3(mesh->mvert[mesh->medge[i].v2].co)) * + const float3 edge_centerpoint = (float3(verts[edges[i].v1].co) + + float3(verts[edges[i].v2].co)) * 0.5f; /* Get the centerpoint of poly 2 and subtract the edge centerpoint to get a tangent * normal for poly 2. */ float3 poly_center_2; - BKE_mesh_calc_poly_center(&mpoly_2, &loops[mpoly_2.loopstart], mesh->mvert, poly_center_2); + BKE_mesh_calc_poly_center(&mpoly_2, &loops[mpoly_2.loopstart], verts.data(), poly_center_2); const float3 poly_2_tangent = math::normalize(poly_center_2 - edge_centerpoint); const float concavity = math::dot(poly_1_normal, poly_2_tangent); @@ -165,9 +148,8 @@ class SignedAngleFieldInput final : public GeometryFieldInput { return -angle; }; - VArray<float> angles = VArray<float>::ForFunc(mesh->totedge, angle_fn); - return component.attributes()->adapt_domain<float>( - std::move(angles), ATTR_DOMAIN_EDGE, domain); + VArray<float> angles = VArray<float>::ForFunc(mesh.totedge, angle_fn); + return mesh.attributes().adapt_domain<float>(std::move(angles), ATTR_DOMAIN_EDGE, domain); } uint64_t hash() const override diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_neighbors.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_neighbors.cc index 50d6998bb27..bfe8753c039 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_neighbors.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_neighbors.cc @@ -16,34 +16,26 @@ static void node_declare(NodeDeclarationBuilder &b) .description(N_("The number of faces that use each edge as one of their sides")); } -class EdgeNeighborCountFieldInput final : public GeometryFieldInput { +class EdgeNeighborCountFieldInput final : public bke::MeshFieldInput { public: EdgeNeighborCountFieldInput() - : GeometryFieldInput(CPPType::get<int>(), "Edge Neighbor Count Field") + : bke::MeshFieldInput(CPPType::get<int>(), "Edge Neighbor Count Field") { category_ = Category::Generated; } - GVArray get_varray_for_context(const GeometryComponent &component, + GVArray get_varray_for_context(const Mesh &mesh, const eAttrDomain domain, IndexMask UNUSED(mask)) const final { - if (component.type() == GEO_COMPONENT_TYPE_MESH) { - const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); - const Mesh *mesh = mesh_component.get_for_read(); - if (mesh == nullptr) { - return {}; - } - - Array<int> face_count(mesh->totedge, 0); - for (const int i : IndexRange(mesh->totloop)) { - face_count[mesh->mloop[i].e]++; - } - - return mesh_component.attributes()->adapt_domain<int>( - VArray<int>::ForContainer(std::move(face_count)), ATTR_DOMAIN_EDGE, domain); + const Span<MLoop> loops = mesh.loops(); + Array<int> face_count(mesh.totedge, 0); + for (const MLoop &loop : loops) { + face_count[loop.e]++; } - return {}; + + return mesh.attributes().adapt_domain<int>( + VArray<int>::ForContainer(std::move(face_count)), ATTR_DOMAIN_EDGE, domain); } uint64_t hash() const override diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_vertices.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_vertices.cc index 83e511f45c2..c8ceae239a4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_vertices.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_vertices.cc @@ -27,45 +27,37 @@ static void node_declare(NodeDeclarationBuilder &b) enum VertexNumber { VERTEX_ONE, VERTEX_TWO }; -static VArray<int> construct_edge_vertices_gvarray(const MeshComponent &component, - const VertexNumber vertex, - const eAttrDomain domain) +static VArray<int> construct_edge_verts_gvarray(const Mesh &mesh, + const VertexNumber vertex, + const eAttrDomain domain) { - const Mesh *mesh = component.get_for_read(); - if (mesh == nullptr) { - return {}; - } + const Span<MEdge> edges = mesh.edges(); if (domain == ATTR_DOMAIN_EDGE) { if (vertex == VERTEX_ONE) { - return VArray<int>::ForFunc(mesh->totedge, - [mesh](const int i) -> int { return mesh->medge[i].v1; }); + return VArray<int>::ForFunc(edges.size(), + [edges](const int i) -> int { return edges[i].v1; }); } - return VArray<int>::ForFunc(mesh->totedge, - [mesh](const int i) -> int { return mesh->medge[i].v2; }); + return VArray<int>::ForFunc(edges.size(), [edges](const int i) -> int { return edges[i].v2; }); } return {}; } -class EdgeVerticesFieldInput final : public GeometryFieldInput { +class EdgeVerticesFieldInput final : public bke::MeshFieldInput { private: VertexNumber vertex_; public: EdgeVerticesFieldInput(VertexNumber vertex) - : GeometryFieldInput(CPPType::get<int>(), "Edge Vertices Field"), vertex_(vertex) + : bke::MeshFieldInput(CPPType::get<int>(), "Edge Vertices Field"), vertex_(vertex) { category_ = Category::Generated; } - GVArray get_varray_for_context(const GeometryComponent &component, + GVArray get_varray_for_context(const Mesh &mesh, const eAttrDomain domain, IndexMask UNUSED(mask)) const final { - if (component.type() == GEO_COMPONENT_TYPE_MESH) { - const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); - return construct_edge_vertices_gvarray(mesh_component, vertex_, domain); - } - return {}; + return construct_edge_verts_gvarray(mesh, vertex_, domain); } uint64_t hash() const override @@ -83,51 +75,43 @@ class EdgeVerticesFieldInput final : public GeometryFieldInput { } }; -static VArray<float3> construct_edge_positions_gvarray(const MeshComponent &component, +static VArray<float3> construct_edge_positions_gvarray(const Mesh &mesh, const VertexNumber vertex, const eAttrDomain domain) { - const Mesh *mesh = component.get_for_read(); - if (mesh == nullptr) { - return {}; - } + const Span<MVert> verts = mesh.verts(); + const Span<MEdge> edges = mesh.edges(); if (vertex == VERTEX_ONE) { - return component.attributes()->adapt_domain<float3>( - VArray<float3>::ForFunc( - mesh->totedge, - [mesh](const int i) { return float3(mesh->mvert[mesh->medge[i].v1].co); }), + return mesh.attributes().adapt_domain<float3>( + VArray<float3>::ForFunc(edges.size(), + [verts, edges](const int i) { return verts[edges[i].v1].co; }), ATTR_DOMAIN_EDGE, domain); } - return component.attributes()->adapt_domain<float3>( - VArray<float3>::ForFunc( - mesh->totedge, - [mesh](const int i) { return float3(mesh->mvert[mesh->medge[i].v2].co); }), + return mesh.attributes().adapt_domain<float3>( + VArray<float3>::ForFunc(edges.size(), + [verts, edges](const int i) { return verts[edges[i].v2].co; }), ATTR_DOMAIN_EDGE, domain); } -class EdgePositionFieldInput final : public GeometryFieldInput { +class EdgePositionFieldInput final : public bke::MeshFieldInput { private: VertexNumber vertex_; public: EdgePositionFieldInput(VertexNumber vertex) - : GeometryFieldInput(CPPType::get<float3>(), "Edge Position Field"), vertex_(vertex) + : bke::MeshFieldInput(CPPType::get<float3>(), "Edge Position Field"), vertex_(vertex) { category_ = Category::Generated; } - GVArray get_varray_for_context(const GeometryComponent &component, + GVArray get_varray_for_context(const Mesh &mesh, const eAttrDomain domain, IndexMask UNUSED(mask)) const final { - if (component.type() == GEO_COMPONENT_TYPE_MESH) { - const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); - return construct_edge_positions_gvarray(mesh_component, vertex_, domain); - } - return {}; + return construct_edge_positions_gvarray(mesh, vertex_, domain); } uint64_t hash() const override diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_area.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_area.cc index 4d21bf9443a..be921c1f1c5 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_area.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_area.cc @@ -16,39 +16,33 @@ static void node_declare(NodeDeclarationBuilder &b) .description(N_("The surface area of each of the mesh's faces")); } -static VArray<float> construct_face_area_gvarray(const MeshComponent &component, - const eAttrDomain domain) +static VArray<float> construct_face_area_varray(const Mesh &mesh, const eAttrDomain domain) { - const Mesh *mesh = component.get_for_read(); - if (mesh == nullptr) { - return {}; - } + const Span<MVert> verts = mesh.verts(); + const Span<MPoly> polys = mesh.polys(); + const Span<MLoop> loops = mesh.loops(); - auto area_fn = [mesh](const int i) -> float { - const MPoly *mp = &mesh->mpoly[i]; - return BKE_mesh_calc_poly_area(mp, &mesh->mloop[mp->loopstart], mesh->mvert); + auto area_fn = [verts, polys, loops](const int i) -> float { + const MPoly &poly = polys[i]; + return BKE_mesh_calc_poly_area(&poly, &loops[poly.loopstart], verts.data()); }; - return component.attributes()->adapt_domain<float>( - VArray<float>::ForFunc(mesh->totpoly, area_fn), ATTR_DOMAIN_FACE, domain); + return mesh.attributes().adapt_domain<float>( + VArray<float>::ForFunc(polys.size(), area_fn), ATTR_DOMAIN_FACE, domain); } -class FaceAreaFieldInput final : public GeometryFieldInput { +class FaceAreaFieldInput final : public bke::MeshFieldInput { public: - FaceAreaFieldInput() : GeometryFieldInput(CPPType::get<float>(), "Face Area Field") + FaceAreaFieldInput() : bke::MeshFieldInput(CPPType::get<float>(), "Face Area Field") { category_ = Category::Generated; } - GVArray get_varray_for_context(const GeometryComponent &component, + GVArray get_varray_for_context(const Mesh &mesh, const eAttrDomain domain, IndexMask UNUSED(mask)) const final { - if (component.type() == GEO_COMPONENT_TYPE_MESH) { - const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); - return construct_face_area_gvarray(mesh_component, domain); - } - return {}; + return construct_face_area_varray(mesh, domain); } uint64_t hash() const override diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_is_planar.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_is_planar.cc index 6b04ff08d9e..72c45de7b0f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_is_planar.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_is_planar.cc @@ -22,53 +22,45 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Bool>("Planar").field_source(); } -class PlanarFieldInput final : public GeometryFieldInput { +class PlanarFieldInput final : public bke::MeshFieldInput { private: Field<float> threshold_; public: PlanarFieldInput(Field<float> threshold) - : GeometryFieldInput(CPPType::get<bool>(), "Planar"), threshold_(threshold) + : bke::MeshFieldInput(CPPType::get<bool>(), "Planar"), threshold_(threshold) { category_ = Category::Generated; } - GVArray get_varray_for_context(const GeometryComponent &component, + GVArray get_varray_for_context(const Mesh &mesh, const eAttrDomain domain, - [[maybe_unused]] IndexMask mask) const final + IndexMask /*mask*/) const final { - if (component.type() != GEO_COMPONENT_TYPE_MESH) { - return {}; - } - - const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); - const Mesh *mesh = mesh_component.get_for_read(); - if (mesh == nullptr) { - return {}; - } - - GeometryComponentFieldContext context{mesh_component, ATTR_DOMAIN_FACE}; - fn::FieldEvaluator evaluator{context, mesh->totpoly}; + const Span<MVert> verts = mesh.verts(); + const Span<MPoly> polys = mesh.polys(); + const Span<MLoop> loops = mesh.loops(); + const Span<float3> poly_normals{(float3 *)BKE_mesh_poly_normals_ensure(&mesh), mesh.totpoly}; + + bke::MeshFieldContext context{mesh, ATTR_DOMAIN_FACE}; + fn::FieldEvaluator evaluator{context, polys.size()}; evaluator.add(threshold_); evaluator.evaluate(); const VArray<float> thresholds = evaluator.get_evaluated<float>(0); - Span<float3> poly_normals{(float3 *)BKE_mesh_poly_normals_ensure(mesh), mesh->totpoly}; - - auto planar_fn = [mesh, thresholds, poly_normals](const int i_poly) -> bool { - if (mesh->mpoly[i_poly].totloop <= 3) { + auto planar_fn = [verts, polys, loops, thresholds, poly_normals](const int i) -> bool { + const MPoly &poly = polys[i]; + if (poly.totloop <= 3) { return true; } - const int loopstart = mesh->mpoly[i_poly].loopstart; - const int loops = mesh->mpoly[i_poly].totloop; - Span<MLoop> poly_loops(&mesh->mloop[loopstart], loops); - float3 reference_normal = poly_normals[i_poly]; + const Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop); + const float3 &reference_normal = poly_normals[i]; float min = FLT_MAX; float max = -FLT_MAX; for (const int i_loop : poly_loops.index_range()) { - const float3 vert = mesh->mvert[poly_loops[i_loop].v].co; + const float3 vert = verts[poly_loops[i_loop].v].co; float dot = math::dot(reference_normal, vert); if (dot > max) { max = dot; @@ -77,11 +69,11 @@ class PlanarFieldInput final : public GeometryFieldInput { min = dot; } } - return max - min < thresholds[i_poly] / 2.0f; + return max - min < thresholds[i] / 2.0f; }; - return component.attributes()->adapt_domain<bool>( - VArray<bool>::ForFunc(mesh->totpoly, planar_fn), ATTR_DOMAIN_FACE, domain); + return mesh.attributes().adapt_domain<bool>( + VArray<bool>::ForFunc(polys.size(), planar_fn), ATTR_DOMAIN_FACE, domain); } uint64_t hash() const override diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_neighbors.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_neighbors.cc index a225ce61b14..9e85eae3a31 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_neighbors.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_neighbors.cc @@ -19,48 +19,41 @@ static void node_declare(NodeDeclarationBuilder &b) .description(N_("Number of faces which share an edge with the face")); } -static VArray<int> construct_neighbor_count_gvarray(const MeshComponent &component, - const eAttrDomain domain) +static VArray<int> construct_neighbor_count_varray(const Mesh &mesh, const eAttrDomain domain) { - const Mesh *mesh = component.get_for_read(); - if (mesh == nullptr) { - return {}; - } + const Span<MPoly> polys = mesh.polys(); + const Span<MLoop> loops = mesh.loops(); - Array<int> edge_count(mesh->totedge, 0); - for (const int i : IndexRange(mesh->totloop)) { - edge_count[mesh->mloop[i].e]++; + Array<int> edge_count(mesh.totedge, 0); + for (const MLoop &loop : loops) { + edge_count[loop.e]++; } - Array<int> poly_count(mesh->totpoly, 0); - for (const int poly_num : IndexRange(mesh->totpoly)) { - MPoly &poly = mesh->mpoly[poly_num]; - for (const int loop_num : IndexRange(poly.loopstart, poly.totloop)) { - poly_count[poly_num] += edge_count[mesh->mloop[loop_num].e] - 1; + Array<int> poly_count(polys.size(), 0); + for (const int poly_index : polys.index_range()) { + const MPoly &poly = polys[poly_index]; + for (const MLoop &loop : loops.slice(poly.loopstart, poly.totloop)) { + poly_count[poly_index] += edge_count[loop.e] - 1; } } - return component.attributes()->adapt_domain<int>( + return mesh.attributes().adapt_domain<int>( VArray<int>::ForContainer(std::move(poly_count)), ATTR_DOMAIN_FACE, domain); } -class FaceNeighborCountFieldInput final : public GeometryFieldInput { +class FaceNeighborCountFieldInput final : public bke::MeshFieldInput { public: FaceNeighborCountFieldInput() - : GeometryFieldInput(CPPType::get<int>(), "Face Neighbor Count Field") + : bke::MeshFieldInput(CPPType::get<int>(), "Face Neighbor Count Field") { category_ = Category::Generated; } - GVArray get_varray_for_context(const GeometryComponent &component, + GVArray get_varray_for_context(const Mesh &mesh, const eAttrDomain domain, IndexMask UNUSED(mask)) const final { - if (component.type() == GEO_COMPONENT_TYPE_MESH) { - const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); - return construct_neighbor_count_gvarray(mesh_component, domain); - } - return {}; + return construct_neighbor_count_varray(mesh, domain); } uint64_t hash() const override @@ -75,37 +68,28 @@ class FaceNeighborCountFieldInput final : public GeometryFieldInput { } }; -static VArray<int> construct_vertex_count_gvarray(const MeshComponent &component, - const eAttrDomain domain) +static VArray<int> construct_vertex_count_varray(const Mesh &mesh, const eAttrDomain domain) { - const Mesh *mesh = component.get_for_read(); - if (mesh == nullptr) { - return {}; - } - - return component.attributes()->adapt_domain<int>( - VArray<int>::ForFunc(mesh->totpoly, - [mesh](const int i) -> float { return mesh->mpoly[i].totloop; }), + const Span<MPoly> polys = mesh.polys(); + return mesh.attributes().adapt_domain<int>( + VArray<int>::ForFunc(polys.size(), + [polys](const int i) -> float { return polys[i].totloop; }), ATTR_DOMAIN_FACE, domain); } -class FaceVertexCountFieldInput final : public GeometryFieldInput { +class FaceVertexCountFieldInput final : public bke::MeshFieldInput { public: - FaceVertexCountFieldInput() : GeometryFieldInput(CPPType::get<int>(), "Vertex Count Field") + FaceVertexCountFieldInput() : bke::MeshFieldInput(CPPType::get<int>(), "Vertex Count Field") { category_ = Category::Generated; } - GVArray get_varray_for_context(const GeometryComponent &component, + GVArray get_varray_for_context(const Mesh &mesh, const eAttrDomain domain, IndexMask UNUSED(mask)) const final { - if (component.type() == GEO_COMPONENT_TYPE_MESH) { - const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); - return construct_vertex_count_gvarray(mesh_component, domain); - } - return {}; + return construct_vertex_count_varray(mesh, domain); } uint64_t hash() const override diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc index 2c7eef5665f..9d7735e707d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc @@ -22,39 +22,32 @@ static void node_declare(NodeDeclarationBuilder &b) .description(N_("The total number of mesh islands")); } -class IslandFieldInput final : public GeometryFieldInput { +class IslandFieldInput final : public bke::MeshFieldInput { public: - IslandFieldInput() : GeometryFieldInput(CPPType::get<int>(), "Island Index") + IslandFieldInput() : bke::MeshFieldInput(CPPType::get<int>(), "Island Index") { category_ = Category::Generated; } - GVArray get_varray_for_context(const GeometryComponent &component, + GVArray get_varray_for_context(const Mesh &mesh, const eAttrDomain domain, IndexMask UNUSED(mask)) const final { - if (component.type() != GEO_COMPONENT_TYPE_MESH) { - return {}; - } - const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); - const Mesh *mesh = mesh_component.get_for_read(); - if (mesh == nullptr) { - return {}; - } + const Span<MEdge> edges = mesh.edges(); - DisjointSet islands(mesh->totvert); - for (const int i : IndexRange(mesh->totedge)) { - islands.join(mesh->medge[i].v1, mesh->medge[i].v2); + DisjointSet islands(mesh.totvert); + for (const int i : edges.index_range()) { + islands.join(edges[i].v1, edges[i].v2); } - Array<int> output(mesh->totvert); + Array<int> output(mesh.totvert); VectorSet<int> ordered_roots; - for (const int i : IndexRange(mesh->totvert)) { + for (const int i : IndexRange(mesh.totvert)) { const int64_t root = islands.find_root(i); output[i] = ordered_roots.index_of_or_add(root); } - return mesh_component.attributes()->adapt_domain<int>( + return mesh.attributes().adapt_domain<int>( VArray<int>::ForContainer(std::move(output)), ATTR_DOMAIN_POINT, domain); } @@ -70,39 +63,31 @@ class IslandFieldInput final : public GeometryFieldInput { } }; -class IslandCountFieldInput final : public GeometryFieldInput { +class IslandCountFieldInput final : public bke::MeshFieldInput { public: - IslandCountFieldInput() : GeometryFieldInput(CPPType::get<int>(), "Island Count") + IslandCountFieldInput() : bke::MeshFieldInput(CPPType::get<int>(), "Island Count") { category_ = Category::Generated; } - GVArray get_varray_for_context(const GeometryComponent &component, + GVArray get_varray_for_context(const Mesh &mesh, const eAttrDomain domain, IndexMask UNUSED(mask)) const final { - if (component.type() != GEO_COMPONENT_TYPE_MESH) { - return {}; - } - const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); - const Mesh *mesh = mesh_component.get_for_read(); - if (mesh == nullptr) { - return {}; - } + const Span<MEdge> edges = mesh.edges(); - DisjointSet islands(mesh->totvert); - for (const int i : IndexRange(mesh->totedge)) { - islands.join(mesh->medge[i].v1, mesh->medge[i].v2); + DisjointSet islands(mesh.totvert); + for (const int i : edges.index_range()) { + islands.join(edges[i].v1, edges[i].v2); } Set<int> island_list; - for (const int i_vert : IndexRange(mesh->totvert)) { + for (const int i_vert : IndexRange(mesh.totvert)) { const int64_t root = islands.find_root(i_vert); island_list.add(root); } - return VArray<int>::ForSingle(island_list.size(), - mesh_component.attribute_domain_size(domain)); + return VArray<int>::ForSingle(island_list.size(), mesh.attributes().domain_size(domain)); } uint64_t hash() const override diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_vertex_neighbors.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_vertex_neighbors.cc index 62b3f9d0e92..ab44a6c8515 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_vertex_neighbors.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_vertex_neighbors.cc @@ -20,41 +20,32 @@ static void node_declare(NodeDeclarationBuilder &b) .description(N_("Number of faces that contain the vertex")); } -static VArray<int> construct_vertex_count_gvarray(const MeshComponent &component, - const eAttrDomain domain) +static VArray<int> construct_vertex_count_gvarray(const Mesh &mesh, const eAttrDomain domain) { - const Mesh *mesh = component.get_for_read(); - if (mesh == nullptr) { - return {}; - } - + const Span<MEdge> edges = mesh.edges(); if (domain == ATTR_DOMAIN_POINT) { - Array<int> vertices(mesh->totvert, 0); - for (const int i : IndexRange(mesh->totedge)) { - vertices[mesh->medge[i].v1]++; - vertices[mesh->medge[i].v2]++; + Array<int> counts(mesh.totvert, 0); + for (const int i : edges.index_range()) { + counts[edges[i].v1]++; + counts[edges[i].v2]++; } - return VArray<int>::ForContainer(std::move(vertices)); + return VArray<int>::ForContainer(std::move(counts)); } return {}; } -class VertexCountFieldInput final : public GeometryFieldInput { +class VertexCountFieldInput final : public bke::MeshFieldInput { public: - VertexCountFieldInput() : GeometryFieldInput(CPPType::get<int>(), "Vertex Count Field") + VertexCountFieldInput() : bke::MeshFieldInput(CPPType::get<int>(), "Vertex Count Field") { category_ = Category::Generated; } - GVArray get_varray_for_context(const GeometryComponent &component, + GVArray get_varray_for_context(const Mesh &mesh, const eAttrDomain domain, IndexMask UNUSED(mask)) const final { - if (component.type() == GEO_COMPONENT_TYPE_MESH) { - const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); - return construct_vertex_count_gvarray(mesh_component, domain); - } - return {}; + return construct_vertex_count_gvarray(mesh, domain); } uint64_t hash() const override @@ -69,18 +60,13 @@ class VertexCountFieldInput final : public GeometryFieldInput { } }; -static VArray<int> construct_face_count_gvarray(const MeshComponent &component, - const eAttrDomain domain) +static VArray<int> construct_face_count_gvarray(const Mesh &mesh, const eAttrDomain domain) { - const Mesh *mesh = component.get_for_read(); - if (mesh == nullptr) { - return {}; - } - + const Span<MLoop> loops = mesh.loops(); if (domain == ATTR_DOMAIN_POINT) { - Array<int> vertices(mesh->totvert, 0); - for (const int i : IndexRange(mesh->totloop)) { - int vertex = mesh->mloop[i].v; + Array<int> vertices(mesh.totvert, 0); + for (const int i : loops.index_range()) { + int vertex = loops[i].v; vertices[vertex]++; } return VArray<int>::ForContainer(std::move(vertices)); @@ -88,22 +74,18 @@ static VArray<int> construct_face_count_gvarray(const MeshComponent &component, return {}; } -class VertexFaceCountFieldInput final : public GeometryFieldInput { +class VertexFaceCountFieldInput final : public bke::MeshFieldInput { public: - VertexFaceCountFieldInput() : GeometryFieldInput(CPPType::get<int>(), "Vertex Face Count Field") + VertexFaceCountFieldInput() : bke::MeshFieldInput(CPPType::get<int>(), "Vertex Face Count Field") { category_ = Category::Generated; } - GVArray get_varray_for_context(const GeometryComponent &component, + GVArray get_varray_for_context(const Mesh &mesh, const eAttrDomain domain, IndexMask UNUSED(mask)) const final { - if (component.type() == GEO_COMPONENT_TYPE_MESH) { - const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); - return construct_face_count_gvarray(mesh_component, domain); - } - return {}; + return construct_face_count_gvarray(mesh, domain); } uint64_t hash() const override diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_named_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_input_named_attribute.cc index 122c7b352c7..da09d3650e3 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_named_attribute.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_named_attribute.cc @@ -88,7 +88,7 @@ static void node_geo_exec(GeoNodeExecParams params) return; } - params.used_named_attribute(name, eNamedAttrUsage::Read); + params.used_named_attribute(name, NamedAttributeUsage::Read); switch (data_type) { case CD_PROP_FLOAT: diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_shortest_edge_paths.cc b/source/blender/nodes/geometry/nodes/node_geo_input_shortest_edge_paths.cc index ca6406d2810..a54daabde3b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_shortest_edge_paths.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_shortest_edge_paths.cc @@ -2,14 +2,12 @@ #include <queue> -#include "BKE_curves.hh" - #include "BLI_map.hh" #include "BLI_math_vec_types.hh" #include "BLI_set.hh" +#include "BLI_task.hh" -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" +#include "BKE_mesh.h" #include "node_geometry_util.hh" @@ -28,10 +26,10 @@ typedef std::pair<float, int> VertPriority; struct EdgeVertMap { Array<Vector<int>> edges_by_vertex_map; - EdgeVertMap(const Mesh *mesh) + EdgeVertMap(const Mesh &mesh) { - const Span<MEdge> edges{mesh->medge, mesh->totedge}; - edges_by_vertex_map.reinitialize(mesh->totvert); + const Span<MEdge> edges = mesh.edges(); + edges_by_vertex_map.reinitialize(mesh.totvert); for (const int edge_i : edges.index_range()) { const MEdge &edge = edges[edge_i]; edges_by_vertex_map[edge.v1].append(edge_i); @@ -40,16 +38,15 @@ struct EdgeVertMap { } }; -static void shortest_paths(const Mesh *mesh, +static void shortest_paths(const Mesh &mesh, EdgeVertMap &maps, const IndexMask end_selection, const VArray<float> &input_cost, MutableSpan<int> r_next_index, MutableSpan<float> r_cost) { - const Span<MVert> verts{mesh->mvert, mesh->totvert}; - const Span<MEdge> edges{mesh->medge, mesh->totedge}; - Array<bool> visited(mesh->totvert, false); + const Span<MEdge> edges = mesh.edges(); + Array<bool> visited(mesh.totvert, false); std::priority_queue<VertPriority, std::vector<VertPriority>, std::greater<VertPriority>> queue; @@ -84,46 +81,38 @@ static void shortest_paths(const Mesh *mesh, } } -class ShortestEdgePathsNextVertFieldInput final : public GeometryFieldInput { +class ShortestEdgePathsNextVertFieldInput final : public bke::MeshFieldInput { private: Field<bool> end_selection_; Field<float> cost_; public: ShortestEdgePathsNextVertFieldInput(Field<bool> end_selection, Field<float> cost) - : GeometryFieldInput(CPPType::get<int>(), "Shortest Edge Paths Next Vertex Field"), + : bke::MeshFieldInput(CPPType::get<int>(), "Shortest Edge Paths Next Vertex Field"), end_selection_(end_selection), cost_(cost) { category_ = Category::Generated; } - GVArray get_varray_for_context(const GeometryComponent &component, + GVArray get_varray_for_context(const Mesh &mesh, const eAttrDomain domain, - [[maybe_unused]] IndexMask mask) const final + const IndexMask /*mask*/) const final { - if (component.type() != GEO_COMPONENT_TYPE_MESH) { - return {}; - } - const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); - const Mesh *mesh = mesh_component.get_for_read(); - if (mesh == nullptr) { - return {}; - } - GeometryComponentFieldContext edge_context{component, ATTR_DOMAIN_EDGE}; - fn::FieldEvaluator edge_evaluator{edge_context, mesh->totedge}; + bke::MeshFieldContext edge_context{mesh, ATTR_DOMAIN_EDGE}; + fn::FieldEvaluator edge_evaluator{edge_context, mesh.totedge}; edge_evaluator.add(cost_); edge_evaluator.evaluate(); const VArray<float> input_cost = edge_evaluator.get_evaluated<float>(0); - GeometryComponentFieldContext point_context{component, ATTR_DOMAIN_POINT}; - fn::FieldEvaluator point_evaluator{point_context, mesh->totvert}; + bke::MeshFieldContext point_context{mesh, ATTR_DOMAIN_POINT}; + fn::FieldEvaluator point_evaluator{point_context, mesh.totvert}; point_evaluator.add(end_selection_); point_evaluator.evaluate(); const IndexMask end_selection = point_evaluator.get_evaluated_as_mask(0); - Array<int> next_index(mesh->totvert, -1); - Array<float> cost(mesh->totvert, FLT_MAX); + Array<int> next_index(mesh.totvert, -1); + Array<float> cost(mesh.totvert, FLT_MAX); if (!end_selection.is_empty()) { EdgeVertMap maps(mesh); @@ -136,7 +125,7 @@ class ShortestEdgePathsNextVertFieldInput final : public GeometryFieldInput { } } }); - return component.attributes()->adapt_domain<int>( + return mesh.attributes().adapt_domain<int>( VArray<int>::ForContainer(std::move(next_index)), ATTR_DOMAIN_POINT, domain); } @@ -156,46 +145,38 @@ class ShortestEdgePathsNextVertFieldInput final : public GeometryFieldInput { } }; -class ShortestEdgePathsCostFieldInput final : public GeometryFieldInput { +class ShortestEdgePathsCostFieldInput final : public bke::MeshFieldInput { private: Field<bool> end_selection_; Field<float> cost_; public: ShortestEdgePathsCostFieldInput(Field<bool> end_selection, Field<float> cost) - : GeometryFieldInput(CPPType::get<float>(), "Shortest Edge Paths Cost Field"), + : bke::MeshFieldInput(CPPType::get<float>(), "Shortest Edge Paths Cost Field"), end_selection_(end_selection), cost_(cost) { category_ = Category::Generated; } - GVArray get_varray_for_context(const GeometryComponent &component, + GVArray get_varray_for_context(const Mesh &mesh, const eAttrDomain domain, - [[maybe_unused]] IndexMask mask) const final + const IndexMask /*mask*/) const final { - if (component.type() != GEO_COMPONENT_TYPE_MESH) { - return {}; - } - const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); - const Mesh *mesh = mesh_component.get_for_read(); - if (mesh == nullptr) { - return {}; - } - GeometryComponentFieldContext edge_context{component, ATTR_DOMAIN_EDGE}; - fn::FieldEvaluator edge_evaluator{edge_context, mesh->totedge}; + bke::MeshFieldContext edge_context{mesh, ATTR_DOMAIN_EDGE}; + fn::FieldEvaluator edge_evaluator{edge_context, mesh.totedge}; edge_evaluator.add(cost_); edge_evaluator.evaluate(); const VArray<float> input_cost = edge_evaluator.get_evaluated<float>(0); - GeometryComponentFieldContext point_context{component, ATTR_DOMAIN_POINT}; - fn::FieldEvaluator point_evaluator{point_context, mesh->totvert}; + bke::MeshFieldContext point_context{mesh, ATTR_DOMAIN_POINT}; + fn::FieldEvaluator point_evaluator{point_context, mesh.totvert}; point_evaluator.add(end_selection_); point_evaluator.evaluate(); const IndexMask end_selection = point_evaluator.get_evaluated_as_mask(0); - Array<int> next_index(mesh->totvert, -1); - Array<float> cost(mesh->totvert, FLT_MAX); + Array<int> next_index(mesh.totvert, -1); + Array<float> cost(mesh.totvert, FLT_MAX); if (!end_selection.is_empty()) { EdgeVertMap maps(mesh); @@ -208,7 +189,7 @@ class ShortestEdgePathsCostFieldInput final : public GeometryFieldInput { } } }); - return component.attributes()->adapt_domain<float>( + return mesh.attributes().adapt_domain<float>( VArray<float>::ForContainer(std::move(cost)), ATTR_DOMAIN_POINT, domain); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc b/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc index 267ba44cc00..07dc158ff48 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc @@ -16,15 +16,9 @@ static void node_declare(NodeDeclarationBuilder &b) * Spline Count */ -static VArray<int> construct_curve_point_count_gvarray(const CurveComponent &component, +static VArray<int> construct_curve_point_count_gvarray(const bke::CurvesGeometry &curves, const eAttrDomain domain) { - if (!component.has_curves()) { - return {}; - } - const Curves &curves_id = *component.get_for_read(); - const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); - auto count_fn = [curves](int64_t i) { return curves.points_for_curve(i).size(); }; if (domain == ATTR_DOMAIN_CURVE) { @@ -32,29 +26,24 @@ static VArray<int> construct_curve_point_count_gvarray(const CurveComponent &com } if (domain == ATTR_DOMAIN_POINT) { VArray<int> count = VArray<int>::ForFunc(curves.curves_num(), count_fn); - return component.attributes()->adapt_domain<int>( - std::move(count), ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT); + return curves.adapt_domain<int>(std::move(count), ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT); } return {}; } -class SplineCountFieldInput final : public GeometryFieldInput { +class SplineCountFieldInput final : public bke::CurvesFieldInput { public: - SplineCountFieldInput() : GeometryFieldInput(CPPType::get<int>(), "Spline Point Count") + SplineCountFieldInput() : bke::CurvesFieldInput(CPPType::get<int>(), "Spline Point Count") { category_ = Category::Generated; } - GVArray get_varray_for_context(const GeometryComponent &component, + GVArray get_varray_for_context(const bke::CurvesGeometry &curves, const eAttrDomain domain, IndexMask UNUSED(mask)) const final { - if (component.type() == GEO_COMPONENT_TYPE_CURVE) { - const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); - return construct_curve_point_count_gvarray(curve_component, domain); - } - return {}; + return construct_curve_point_count_gvarray(curves, domain); } uint64_t hash() const override diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc b/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc index a2aab5464aa..ea3d060f03c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc @@ -63,19 +63,12 @@ static Array<float3> curve_tangent_point_domain(const bke::CurvesGeometry &curve return results; } -static VArray<float3> construct_curve_tangent_gvarray(const CurveComponent &component, +static VArray<float3> construct_curve_tangent_gvarray(const bke::CurvesGeometry &curves, const eAttrDomain domain) { - if (!component.has_curves()) { - return {}; - } - - const Curves &curves_id = *component.get_for_read(); - const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); - const VArray<int8_t> types = curves.curve_types(); if (curves.is_single_type(CURVE_TYPE_POLY)) { - return component.attributes()->adapt_domain<float3>( + return curves.adapt_domain<float3>( VArray<float3>::ForSpan(curves.evaluated_tangents()), ATTR_DOMAIN_POINT, domain); } @@ -86,29 +79,25 @@ static VArray<float3> construct_curve_tangent_gvarray(const CurveComponent &comp } if (domain == ATTR_DOMAIN_CURVE) { - return component.attributes()->adapt_domain<float3>( + return curves.adapt_domain<float3>( VArray<float3>::ForContainer(std::move(tangents)), ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE); } return nullptr; } -class TangentFieldInput final : public GeometryFieldInput { +class TangentFieldInput final : public bke::CurvesFieldInput { public: - TangentFieldInput() : GeometryFieldInput(CPPType::get<float3>(), "Tangent node") + TangentFieldInput() : bke::CurvesFieldInput(CPPType::get<float3>(), "Tangent node") { category_ = Category::Generated; } - GVArray get_varray_for_context(const GeometryComponent &component, + GVArray get_varray_for_context(const bke::CurvesGeometry &curves, const eAttrDomain domain, IndexMask UNUSED(mask)) const final { - if (component.type() == GEO_COMPONENT_TYPE_CURVE) { - const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); - return construct_curve_tangent_gvarray(curve_component, domain); - } - return {}; + return construct_curve_tangent_gvarray(curves, domain); } uint64_t hash() const override diff --git a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc index 37f9917f39d..d54d082311f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc @@ -57,7 +57,7 @@ static void add_instances_from_component( VArray<float3> rotations; VArray<float3> scales; - GeometryComponentFieldContext field_context{src_component, domain}; + bke::GeometryFieldContext field_context{src_component, domain}; const Field<bool> selection_field = params.get_input<Field<bool>>("Selection"); fn::FieldEvaluator evaluator{field_context, domain_num}; evaluator.set_selection(selection_field); diff --git a/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc index 5e0789e557b..ec2f1b00e6c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc @@ -29,10 +29,8 @@ static void convert_instances_to_points(GeometrySet &geometry_set, { const InstancesComponent &instances = *geometry_set.get_component_for_read<InstancesComponent>(); - GeometryComponentFieldContext field_context{instances, ATTR_DOMAIN_INSTANCE}; - const int domain_size = instances.instances_num(); - - fn::FieldEvaluator evaluator{field_context, domain_size}; + const bke::InstancesFieldContext context{instances}; + fn::FieldEvaluator evaluator{context, instances.instances_num()}; evaluator.set_selection(std::move(selection_field)); evaluator.add(std::move(position_field)); evaluator.add(std::move(radius_field)); @@ -47,8 +45,7 @@ static void convert_instances_to_points(GeometrySet &geometry_set, PointCloud *pointcloud = BKE_pointcloud_new_nomain(selection.size()); geometry_set.replace_pointcloud(pointcloud); - bke::MutableAttributeAccessor point_attributes = bke::pointcloud_attributes_for_write( - *pointcloud); + bke::MutableAttributeAccessor point_attributes = pointcloud->attributes_for_write(); bke::SpanAttributeWriter<float3> point_positions = point_attributes.lookup_or_add_for_write_only_span<float3>("position", ATTR_DOMAIN_POINT); diff --git a/source/blender/nodes/geometry/nodes/node_geo_interpolate_domain.cc b/source/blender/nodes/geometry/nodes/node_geo_interpolate_domain.cc index 93203988552..8e38ef14aba 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_interpolate_domain.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_interpolate_domain.cc @@ -83,31 +83,33 @@ static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) } } -class InterpolateDomain final : public GeometryFieldInput { +class InterpolateDomain final : public bke::GeometryFieldInput { private: GField src_field_; eAttrDomain src_domain_; public: InterpolateDomain(GField field, eAttrDomain domain) - : GeometryFieldInput(field.cpp_type(), "Interpolate Domain"), + : bke::GeometryFieldInput(field.cpp_type(), "Interpolate Domain"), src_field_(std::move(field)), src_domain_(domain) { } - GVArray get_varray_for_context(const GeometryComponent &component, - const eAttrDomain domain, - IndexMask /* mask */) const final + GVArray get_varray_for_context(const bke::GeometryFieldContext &context, + IndexMask /*mask*/) const final { - const GeometryComponentFieldContext context{component, src_domain_}; - const int64_t src_domain_size = component.attribute_domain_size(src_domain_); + const bke::AttributeAccessor attributes = *context.attributes(); + + const bke::GeometryFieldContext other_domain_context{ + context.geometry(), context.type(), src_domain_}; + const int64_t src_domain_size = attributes.domain_size(src_domain_); GArray values(src_field_.cpp_type(), src_domain_size); - FieldEvaluator value_evaluator{context, src_domain_size}; + FieldEvaluator value_evaluator{other_domain_context, src_domain_size}; value_evaluator.add_with_destination(src_field_, values.as_mutable_span()); value_evaluator.evaluate(); - return component.attributes()->adapt_domain( - GVArray::ForGArray(std::move(values)), src_domain_, domain); + return attributes.adapt_domain( + GVArray::ForGArray(std::move(values)), src_domain_, context.domain()); } }; diff --git a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc index 023d7a32a61..9fdf7fe7d31 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc @@ -177,7 +177,7 @@ static void join_component_type(Span<GeometrySet> src_geometry_sets, GeometrySet static void node_geo_exec(GeoNodeExecParams params) { - Vector<GeometrySet> geometry_sets = params.extract_multi_input<GeometrySet>("Geometry"); + Vector<GeometrySet> geometry_sets = params.extract_input<Vector<GeometrySet>>("Geometry"); GeometrySet geometry_set_result; join_component_type<MeshComponent>(geometry_sets, geometry_set_result); diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc index ca613ae009b..628688f3b47 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc @@ -23,46 +23,56 @@ static void node_declare(NodeDeclarationBuilder &b) static void select_mesh_by_material(const Mesh &mesh, const Material *material, const IndexMask mask, - const MutableSpan<bool> r_selection) + MutableSpan<bool> r_selection) { BLI_assert(mesh.totpoly >= r_selection.size()); - Vector<int> material_indices; + Vector<int> slots; for (const int i : IndexRange(mesh.totcol)) { if (mesh.mat[i] == material) { - material_indices.append(i); + slots.append(i); } } + const AttributeAccessor attributes = mesh.attributes(); + const VArray<int> material_indices = attributes.lookup_or_default<int>( + "material_index", ATTR_DOMAIN_FACE, 0); + if (material != nullptr && material_indices.is_single() && + material_indices.get_internal_single() == 0) { + r_selection.fill_indices(mask, false); + return; + } + + const VArraySpan<int> material_indices_span(material_indices); + threading::parallel_for(mask.index_range(), 1024, [&](IndexRange range) { for (const int i : range) { const int face_index = mask[i]; - r_selection[i] = material_indices.contains(mesh.mpoly[face_index].mat_nr); + r_selection[i] = slots.contains(material_indices_span[face_index]); } }); } -class MaterialSelectionFieldInput final : public GeometryFieldInput { +class MaterialSelectionFieldInput final : public bke::GeometryFieldInput { Material *material_; public: MaterialSelectionFieldInput(Material *material) - : GeometryFieldInput(CPPType::get<bool>(), "Material Selection node"), material_(material) + : bke::GeometryFieldInput(CPPType::get<bool>(), "Material Selection node"), + material_(material) { category_ = Category::Generated; } - GVArray get_varray_for_context(const GeometryComponent &component, - const eAttrDomain domain, - IndexMask mask) const final + GVArray get_varray_for_context(const bke::GeometryFieldContext &context, + const IndexMask mask) const final { - if (component.type() != GEO_COMPONENT_TYPE_MESH) { + if (context.type() != GEO_COMPONENT_TYPE_MESH) { return {}; } - const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); - const Mesh *mesh = mesh_component.get_for_read(); + const Mesh *mesh = context.mesh(); if (mesh == nullptr) { return {}; } - + const eAttrDomain domain = context.domain(); if (domain == ATTR_DOMAIN_FACE) { Array<bool> selection(mask.min_array_size()); select_mesh_by_material(*mesh, material_, mask, selection); @@ -71,7 +81,7 @@ class MaterialSelectionFieldInput final : public GeometryFieldInput { Array<bool> selection(mesh->totpoly); select_mesh_by_material(*mesh, material_, IndexMask(mesh->totpoly), selection); - return mesh_component.attributes()->adapt_domain<bool>( + return mesh->attributes().adapt_domain<bool>( VArray<bool>::ForContainer(std::move(selection)), ATTR_DOMAIN_FACE, domain); return nullptr; diff --git a/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc b/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc index a4fb79bef7a..f64f997810e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc @@ -1,5 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "DNA_mesh_types.h" +#include "DNA_pointcloud_types.h" + #include "GEO_mesh_merge_by_distance.hh" #include "GEO_point_merge_by_distance.hh" @@ -35,13 +38,12 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node) node->storage = data; } -static PointCloud *pointcloud_merge_by_distance(const PointCloudComponent &src_points, +static PointCloud *pointcloud_merge_by_distance(const PointCloud &src_points, const float merge_distance, const Field<bool> &selection_field) { - const int src_num = src_points.attribute_domain_size(ATTR_DOMAIN_POINT); - GeometryComponentFieldContext context{src_points, ATTR_DOMAIN_POINT}; - FieldEvaluator evaluator{context, src_num}; + bke::PointCloudFieldContext context{src_points}; + FieldEvaluator evaluator{context, src_points.totpoint}; evaluator.add(selection_field); evaluator.evaluate(); @@ -50,31 +52,28 @@ static PointCloud *pointcloud_merge_by_distance(const PointCloudComponent &src_p return nullptr; } - return geometry::point_merge_by_distance(*src_points.get_for_read(), merge_distance, selection); + return geometry::point_merge_by_distance(src_points, merge_distance, selection); } -static std::optional<Mesh *> mesh_merge_by_distance_connected(const MeshComponent &mesh_component, +static std::optional<Mesh *> mesh_merge_by_distance_connected(const Mesh &mesh, const float merge_distance, const Field<bool> &selection_field) { - const int src_num = mesh_component.attribute_domain_size(ATTR_DOMAIN_POINT); - Array<bool> selection(src_num); - GeometryComponentFieldContext context{mesh_component, ATTR_DOMAIN_POINT}; - FieldEvaluator evaluator{context, src_num}; + Array<bool> selection(mesh.totvert); + bke::MeshFieldContext context{mesh, ATTR_DOMAIN_POINT}; + FieldEvaluator evaluator{context, mesh.totvert}; evaluator.add_with_destination(selection_field, selection.as_mutable_span()); evaluator.evaluate(); - const Mesh &mesh = *mesh_component.get_for_read(); return geometry::mesh_merge_by_distance_connected(mesh, selection, merge_distance, false); } -static std::optional<Mesh *> mesh_merge_by_distance_all(const MeshComponent &mesh_component, +static std::optional<Mesh *> mesh_merge_by_distance_all(const Mesh &mesh, const float merge_distance, const Field<bool> &selection_field) { - const int src_num = mesh_component.attribute_domain_size(ATTR_DOMAIN_POINT); - GeometryComponentFieldContext context{mesh_component, ATTR_DOMAIN_POINT}; - FieldEvaluator evaluator{context, src_num}; + bke::MeshFieldContext context{mesh, ATTR_DOMAIN_POINT}; + FieldEvaluator evaluator{context, mesh.totvert}; evaluator.add(selection_field); evaluator.evaluate(); @@ -83,7 +82,6 @@ static std::optional<Mesh *> mesh_merge_by_distance_all(const MeshComponent &mes return std::nullopt; } - const Mesh &mesh = *mesh_component.get_for_read(); return geometry::mesh_merge_by_distance_all(mesh, selection, merge_distance); } @@ -98,22 +96,20 @@ static void node_geo_exec(GeoNodeExecParams params) const float merge_distance = params.extract_input<float>("Distance"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (geometry_set.has_pointcloud()) { - PointCloud *result = pointcloud_merge_by_distance( - *geometry_set.get_component_for_read<PointCloudComponent>(), merge_distance, selection); + if (const PointCloud *pointcloud = geometry_set.get_pointcloud_for_read()) { + PointCloud *result = pointcloud_merge_by_distance(*pointcloud, merge_distance, selection); if (result) { geometry_set.replace_pointcloud(result); } } - if (geometry_set.has_mesh()) { - const MeshComponent &component = *geometry_set.get_component_for_read<MeshComponent>(); + if (const Mesh *mesh = geometry_set.get_mesh_for_read()) { std::optional<Mesh *> result; switch (mode) { case GEO_NODE_MERGE_BY_DISTANCE_MODE_ALL: - result = mesh_merge_by_distance_all(component, merge_distance, selection); + result = mesh_merge_by_distance_all(*mesh, merge_distance, selection); break; case GEO_NODE_MERGE_BY_DISTANCE_MODE_CONNECTED: - result = mesh_merge_by_distance_connected(component, merge_distance, selection); + result = mesh_merge_by_distance_connected(*mesh, merge_distance, selection); break; default: BLI_assert_unreachable(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_face_set_boundaries.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_face_set_boundaries.cc new file mode 100644 index 00000000000..88cccfb2f94 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_face_set_boundaries.cc @@ -0,0 +1,89 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_mesh.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_mesh_face_set_boundaries_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Int>(N_("Face Set")) + .default_value(0) + .hide_value() + .supports_field() + .description(N_("An identifier for the group of each face. All contiguous faces with the " + "same value are in the same region")); + b.add_output<decl::Bool>(N_("Boundary Edges")) + .field_source() + .description(N_("The edges that lie on the boundaries between the different face sets")); +} + +class BoundaryFieldInput final : public bke::MeshFieldInput { + private: + const Field<int> face_set; + + public: + BoundaryFieldInput(const Field<int> face_set) + : bke::MeshFieldInput(CPPType::get<bool>(), "Boundary Field"), face_set(face_set) + { + category_ = Category::Generated; + } + + GVArray get_varray_for_context(const Mesh &mesh, + const eAttrDomain domain, + const IndexMask /*mask*/) const final + { + const bke::MeshFieldContext face_context{mesh, ATTR_DOMAIN_FACE}; + FieldEvaluator face_evaluator{face_context, mesh.totpoly}; + face_evaluator.add(face_set); + face_evaluator.evaluate(); + const VArray<int> face_set = face_evaluator.get_evaluated<int>(0); + + Array<bool> boundary(mesh.totedge, false); + Array<bool> edge_visited(mesh.totedge, false); + Array<int> edge_face_set(mesh.totedge, 0); + const Span<MPoly> polys = mesh.polys(); + const Span<MLoop> loops = mesh.loops(); + for (const int i : polys.index_range()) { + const MPoly &poly = polys[i]; + for (const MLoop &loop : loops.slice(poly.loopstart, poly.totloop)) { + const int edge = loop.e; + if (edge_visited[edge]) { + if (edge_face_set[edge] != face_set[i]) { + /* This edge is connected to two faces on different face sets. */ + boundary[edge] = true; + } + } + edge_visited[edge] = true; + edge_face_set[edge] = face_set[i]; + } + } + return mesh.attributes().adapt_domain<bool>( + VArray<bool>::ForContainer(std::move(boundary)), ATTR_DOMAIN_EDGE, domain); + } +}; + +static void node_geo_exec(GeoNodeExecParams params) +{ + const Field<int> face_set_field = params.extract_input<Field<int>>("Face Set"); + Field<bool> face_set_boundaries{std::make_shared<BoundaryFieldInput>(face_set_field)}; + params.set_output("Boundary Edges", std::move(face_set_boundaries)); +} + +} // namespace blender::nodes::node_geo_mesh_face_set_boundaries_cc + +void register_node_type_geo_mesh_face_set_boundaries() +{ + namespace file_ns = blender::nodes::node_geo_mesh_face_set_boundaries_cc; + + static bNodeType ntype; + geo_node_type_base( + &ntype, GEO_NODE_MESH_FACE_SET_BOUNDARIES, "Face Set Boundaries", NODE_CLASS_INPUT); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc index 9e85547315c..801b3c78060 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc @@ -109,10 +109,10 @@ static Mesh *create_circle_mesh(const float radius, circle_corner_total(fill_type, verts_num), circle_face_total(fill_type, verts_num)); BKE_id_material_eval_ensure_default_slot(&mesh->id); - MutableSpan<MVert> verts{mesh->mvert, mesh->totvert}; - MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop}; - MutableSpan<MEdge> edges{mesh->medge, mesh->totedge}; - MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly}; + MutableSpan<MVert> verts = mesh->verts_for_write(); + MutableSpan<MEdge> edges = mesh->edges_for_write(); + MutableSpan<MPoly> polys = mesh->polys_for_write(); + MutableSpan<MLoop> loops = mesh->loops_for_write(); /* Assign vertex coordinates. */ const float angle_delta = 2.0f * (M_PI / static_cast<float>(verts_num)); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc index cb79ef93de9..1f9ad9f6ea2 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc @@ -255,7 +255,7 @@ int ConeConfig::calculate_total_corners() return corner_total; } -static void calculate_cone_vertices(const MutableSpan<MVert> &verts, const ConeConfig &config) +static void calculate_cone_verts(const MutableSpan<MVert> &verts, const ConeConfig &config) { Array<float2> circle(config.circle_segments); const float angle_delta = 2.0f * (M_PI / static_cast<float>(config.circle_segments)); @@ -480,12 +480,12 @@ static void calculate_selection_outputs(Mesh *mesh, const ConeConfig &config, ConeAttributeOutputs &attribute_outputs) { - MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*mesh); + MutableAttributeAccessor attributes = mesh->attributes_for_write(); /* Populate "Top" selection output. */ if (attribute_outputs.top_id) { const bool face = !config.top_is_point && config.fill_type != GEO_NODE_MESH_CIRCLE_FILL_NONE; - SpanAttributeWriter<bool> selection = attributes.lookup_or_add_for_write_only_span<bool>( + SpanAttributeWriter<bool> selection = attributes.lookup_or_add_for_write_span<bool>( attribute_outputs.top_id.get(), face ? ATTR_DOMAIN_FACE : ATTR_DOMAIN_POINT); if (config.top_is_point) { @@ -501,7 +501,7 @@ static void calculate_selection_outputs(Mesh *mesh, if (attribute_outputs.bottom_id) { const bool face = !config.bottom_is_point && config.fill_type != GEO_NODE_MESH_CIRCLE_FILL_NONE; - SpanAttributeWriter<bool> selection = attributes.lookup_or_add_for_write_only_span<bool>( + SpanAttributeWriter<bool> selection = attributes.lookup_or_add_for_write_span<bool>( attribute_outputs.bottom_id.get(), face ? ATTR_DOMAIN_FACE : ATTR_DOMAIN_POINT); if (config.bottom_is_point) { @@ -518,7 +518,7 @@ static void calculate_selection_outputs(Mesh *mesh, /* Populate "Side" selection output. */ if (attribute_outputs.side_id) { - SpanAttributeWriter<bool> selection = attributes.lookup_or_add_for_write_only_span<bool>( + SpanAttributeWriter<bool> selection = attributes.lookup_or_add_for_write_span<bool>( attribute_outputs.side_id.get(), ATTR_DOMAIN_FACE); selection.span.slice(config.side_faces_start, config.side_faces_len).fill(true); @@ -536,7 +536,7 @@ static void calculate_selection_outputs(Mesh *mesh, */ static void calculate_cone_uvs(Mesh *mesh, const ConeConfig &config) { - MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*mesh); + MutableAttributeAccessor attributes = mesh->attributes_for_write(); SpanAttributeWriter<float2> uv_attribute = attributes.lookup_or_add_for_write_only_span<float2>( "uv_map", ATTR_DOMAIN_CORNER); @@ -657,7 +657,7 @@ static Mesh *create_vertex_mesh() { /* Returns a mesh with a single vertex at the origin. */ Mesh *mesh = BKE_mesh_new_nomain(1, 0, 0, 0, 0); - copy_v3_fl3(mesh->mvert[0].co, 0.0f, 0.0f, 0.0f); + copy_v3_fl3(mesh->verts_for_write().first().co, 0.0f, 0.0f, 0.0f); return mesh; } @@ -689,12 +689,12 @@ Mesh *create_cylinder_or_cone_mesh(const float radius_top, config.tot_verts, config.tot_edges, 0, config.tot_corners, config.tot_faces); BKE_id_material_eval_ensure_default_slot(&mesh->id); - MutableSpan<MVert> verts{mesh->mvert, mesh->totvert}; - MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop}; - MutableSpan<MEdge> edges{mesh->medge, mesh->totedge}; - MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly}; + MutableSpan<MVert> verts = mesh->verts_for_write(); + MutableSpan<MEdge> edges = mesh->edges_for_write(); + MutableSpan<MPoly> polys = mesh->polys_for_write(); + MutableSpan<MLoop> loops = mesh->loops_for_write(); - calculate_cone_vertices(verts, config); + calculate_cone_verts(verts, config); calculate_cone_edges(edges, config); calculate_cone_faces(loops, polys, config); calculate_cone_uvs(mesh, config); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc index 9baf0b3171e..6f0b8283b72 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc @@ -18,7 +18,7 @@ namespace blender::nodes { static void calculate_uvs( Mesh *mesh, Span<MVert> verts, Span<MLoop> loops, const float size_x, const float size_y) { - MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*mesh); + MutableAttributeAccessor attributes = mesh->attributes_for_write(); SpanAttributeWriter<float2> uv_attribute = attributes.lookup_or_add_for_write_only_span<float2>( "uv_map", ATTR_DOMAIN_CORNER); @@ -49,10 +49,10 @@ Mesh *create_grid_mesh(const int verts_x, 0, edges_x * edges_y * 4, edges_x * edges_y); - MutableSpan<MVert> verts{mesh->mvert, mesh->totvert}; - MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop}; - MutableSpan<MEdge> edges{mesh->medge, mesh->totedge}; - MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly}; + MutableSpan<MVert> verts = mesh->verts_for_write(); + MutableSpan<MEdge> edges = mesh->edges_for_write(); + MutableSpan<MPoly> polys = mesh->polys_for_write(); + MutableSpan<MLoop> loops = mesh->loops_for_write(); { const float dx = edges_x == 0 ? 0.0f : size_x / edges_x; diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc index a5edc6c4b3f..4fd6399f4eb 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc @@ -179,8 +179,8 @@ Mesh *create_line_mesh(const float3 start, const float3 delta, const int count) Mesh *mesh = BKE_mesh_new_nomain(count, count - 1, 0, 0, 0); BKE_id_material_eval_ensure_default_slot(&mesh->id); - MutableSpan<MVert> verts{mesh->mvert, mesh->totvert}; - MutableSpan<MEdge> edges{mesh->medge, mesh->totedge}; + MutableSpan<MVert> verts = mesh->verts_for_write(); + MutableSpan<MEdge> edges = mesh->edges_for_write(); threading::parallel_invoke( 1024 < count, diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc index 85facf1e758..d39e72b7f0a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc @@ -254,7 +254,7 @@ BLI_NOINLINE static void calculate_sphere_corners(MutableSpan<MLoop> loops, BLI_NOINLINE static void calculate_sphere_uvs(Mesh *mesh, const float segments, const float rings) { - MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*mesh); + MutableAttributeAccessor attributes = mesh->attributes_for_write(); SpanAttributeWriter<float2> uv_attribute = attributes.lookup_or_add_for_write_only_span<float2>( "uv_map", ATTR_DOMAIN_CORNER); @@ -301,10 +301,10 @@ static Mesh *create_uv_sphere_mesh(const float radius, const int segments, const sphere_corner_total(segments, rings), sphere_face_total(segments, rings)); BKE_id_material_eval_ensure_default_slot(&mesh->id); - MutableSpan<MVert> verts{mesh->mvert, mesh->totvert}; - MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop}; - MutableSpan<MEdge> edges{mesh->medge, mesh->totedge}; - MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly}; + MutableSpan<MVert> verts = mesh->verts_for_write(); + MutableSpan<MEdge> edges = mesh->edges_for_write(); + MutableSpan<MPoly> polys = mesh->polys_for_write(); + MutableSpan<MLoop> loops = mesh->loops_for_write(); threading::parallel_invoke( 1024 < segments * rings, diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc index 40169def51e..4d08fa40a29 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "DNA_mesh_types.h" + #include "GEO_mesh_to_curve.hh" #include "node_geometry_util.hh" @@ -24,9 +26,8 @@ static void node_geo_exec(GeoNodeExecParams params) return; } - const MeshComponent &component = *geometry_set.get_component_for_read<MeshComponent>(); - GeometryComponentFieldContext context{component, ATTR_DOMAIN_EDGE}; - fn::FieldEvaluator evaluator{context, component.attribute_domain_size(ATTR_DOMAIN_EDGE)}; + bke::MeshFieldContext context{*mesh, ATTR_DOMAIN_EDGE}; + fn::FieldEvaluator evaluator{context, mesh->totedge}; evaluator.add(params.get_input<Field<bool>>("Selection")); evaluator.evaluate(); const IndexMask selection = evaluator.get_evaluated_as_mask(0); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc index d3d1312be6d..ce06ccbda75 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc @@ -1,7 +1,9 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "BLI_array_utils.hh" #include "BLI_task.hh" +#include "DNA_mesh_types.h" #include "DNA_pointcloud_types.h" #include "BKE_attribute_math.hh" @@ -43,35 +45,24 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node) node->storage = data; } -static void materialize_compressed_to_uninitialized_threaded(const GVArray &src, - const IndexMask mask, - GMutableSpan dst) -{ - BLI_assert(src.type() == dst.type()); - BLI_assert(mask.size() == dst.size()); - threading::parallel_for(mask.index_range(), 4096, [&](IndexRange range) { - src.materialize_compressed_to_uninitialized(mask.slice(range), dst.slice(range).data()); - }); -} - 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 MeshComponent *mesh_component = geometry_set.get_component_for_read<MeshComponent>(); - if (mesh_component == nullptr) { + const Mesh *mesh = geometry_set.get_mesh_for_read(); + if (mesh == nullptr) { geometry_set.remove_geometry_during_modify(); return; } - GeometryComponentFieldContext field_context{*mesh_component, domain}; - const int domain_num = mesh_component->attribute_domain_size(domain); - if (domain_num == 0) { + const int domain_size = mesh->attributes().domain_size(domain); + if (domain_size == 0) { geometry_set.remove_geometry_during_modify(); return; } - fn::FieldEvaluator evaluator{field_context, domain_num}; + bke::MeshFieldContext field_context{*mesh, domain}; + fn::FieldEvaluator evaluator{field_context, domain_size}; evaluator.set_selection(selection_field); /* Evaluating directly into the point cloud doesn't work because we are not using the full * "min_array_size" array but compressing the selected elements into the final array with no @@ -83,19 +74,16 @@ static void geometry_set_mesh_to_points(GeometrySet &geometry_set, PointCloud *pointcloud = BKE_pointcloud_new_nomain(selection.size()); geometry_set.replace_pointcloud(pointcloud); - MutableAttributeAccessor pointcloud_attributes = bke::pointcloud_attributes_for_write( - *pointcloud); + MutableAttributeAccessor dst_attributes = pointcloud->attributes_for_write(); - GSpanAttributeWriter position = pointcloud_attributes.lookup_or_add_for_write_only_span( + GSpanAttributeWriter position = dst_attributes.lookup_or_add_for_write_only_span( "position", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3); - materialize_compressed_to_uninitialized_threaded( - evaluator.get_evaluated(0), selection, position.span); + array_utils::gather(evaluator.get_evaluated(0), selection, position.span); position.finish(); - GSpanAttributeWriter radius = pointcloud_attributes.lookup_or_add_for_write_only_span( + GSpanAttributeWriter radius = dst_attributes.lookup_or_add_for_write_only_span( "radius", ATTR_DOMAIN_POINT, CD_PROP_FLOAT); - materialize_compressed_to_uninitialized_threaded( - evaluator.get_evaluated(1), selection, radius.span); + array_utils::gather(evaluator.get_evaluated(1), selection, radius.span); radius.finish(); Map<AttributeIDRef, AttributeKind> attributes; @@ -103,14 +91,16 @@ static void geometry_set_mesh_to_points(GeometrySet &geometry_set, {GEO_COMPONENT_TYPE_MESH}, GEO_COMPONENT_TYPE_POINT_CLOUD, false, attributes); attributes.remove("position"); + const AttributeAccessor src_attributes = mesh->attributes(); + for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) { const AttributeIDRef attribute_id = entry.key; const eCustomDataType data_type = entry.value.data_type; - GVArray src = mesh_component->attributes()->lookup_or_default(attribute_id, domain, data_type); - GSpanAttributeWriter dst = pointcloud_attributes.lookup_or_add_for_write_only_span( + GVArray src = src_attributes.lookup_or_default(attribute_id, domain, data_type); + GSpanAttributeWriter dst = dst_attributes.lookup_or_add_for_write_only_span( attribute_id, ATTR_DOMAIN_POINT, data_type); if (dst && src) { - materialize_compressed_to_uninitialized_threaded(src, selection, dst.span); + array_utils::gather(src, selection, dst.span); dst.finish(); } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_volume.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_volume.cc index 92814a8bc5e..f6fa5c99013 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_volume.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_volume.cc @@ -149,7 +149,6 @@ static void node_geo_exec(GeoNodeExecParams params) { #ifdef WITH_OPENVDB GeometrySet geometry_set(params.extract_input<GeometrySet>("Mesh")); - geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { if (geometry_set.has_mesh()) { Volume *volume = create_volume_from_mesh(*geometry_set.get_mesh_for_read(), params); @@ -159,9 +158,9 @@ static void node_geo_exec(GeoNodeExecParams params) }); params.set_output("Volume", std::move(geometry_set)); #else + params.set_default_remaining_outputs(); params.error_message_add(NodeWarningType::Error, TIP_("Disabled, Blender was compiled without OpenVDB")); - params.set_default_remaining_outputs(); return; #endif } diff --git a/source/blender/nodes/geometry/nodes/node_geo_object_info.cc b/source/blender/nodes/geometry/nodes/node_geo_object_info.cc index 0b2159364f1..691988249df 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_object_info.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_object_info.cc @@ -80,7 +80,7 @@ static void node_geo_exec(GeoNodeExecParams params) else { geometry_set = bke::object_get_evaluated_geometry_set(*object); if (transform_space_relative) { - transform_geometry_set(geometry_set, transform, *params.depsgraph()); + transform_geometry_set(params, geometry_set, transform, *params.depsgraph()); } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_points.cc b/source/blender/nodes/geometry/nodes/node_geo_points.cc index dd32e6714f4..4a294076834 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_points.cc @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ #include "BKE_pointcloud.h" +#include "DNA_pointcloud_types.h" #include "BLI_task.hh" @@ -69,10 +70,8 @@ static void node_geo_exec(GeoNodeExecParams params) Field<float3> position_field = params.extract_input<Field<float3>>("Position"); Field<float> radius_field = params.extract_input<Field<float>>("Radius"); - PointCloud *new_point_cloud = BKE_pointcloud_new_nomain(count); - GeometrySet geometry_set = GeometrySet::create_with_pointcloud(new_point_cloud); - PointCloudComponent &points = geometry_set.get_component_for_write<PointCloudComponent>(); - MutableAttributeAccessor attributes = *points.attributes_for_write(); + PointCloud *points = BKE_pointcloud_new_nomain(count); + MutableAttributeAccessor attributes = points->attributes_for_write(); AttributeWriter<float3> output_position = attributes.lookup_or_add_for_write<float3>( "position", ATTR_DOMAIN_POINT); AttributeWriter<float> output_radii = attributes.lookup_or_add_for_write<float>( @@ -86,7 +85,7 @@ static void node_geo_exec(GeoNodeExecParams params) output_position.finish(); output_radii.finish(); - params.set_output("Geometry", std::move(geometry_set)); + params.set_output("Geometry", GeometrySet::create_with_pointcloud(points)); } } // namespace blender::nodes::node_geo_points_cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc index ed7ef9b7c71..4ac3bf712f7 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc @@ -2,6 +2,8 @@ #include "BLI_task.hh" +#include "DNA_pointcloud_types.h" + #include "BKE_attribute_math.hh" #include "BKE_mesh.h" @@ -22,21 +24,18 @@ static void node_declare(NodeDeclarationBuilder &b) static void geometry_set_points_to_vertices(GeometrySet &geometry_set, Field<bool> &selection_field) { - const PointCloudComponent *point_component = - geometry_set.get_component_for_read<PointCloudComponent>(); - if (point_component == nullptr) { + const PointCloud *points = geometry_set.get_pointcloud_for_read(); + if (points == nullptr) { geometry_set.remove_geometry_during_modify(); return; } - - GeometryComponentFieldContext field_context{*point_component, ATTR_DOMAIN_POINT}; - const int domain_num = point_component->attribute_domain_size(ATTR_DOMAIN_POINT); - if (domain_num == 0) { + if (points->totpoint == 0) { geometry_set.remove_geometry_during_modify(); return; } - fn::FieldEvaluator selection_evaluator{field_context, domain_num}; + bke::PointCloudFieldContext field_context{*points}; + fn::FieldEvaluator selection_evaluator{field_context, points->totpoint}; selection_evaluator.add(selection_field); selection_evaluator.evaluate(); const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); @@ -47,16 +46,16 @@ static void geometry_set_points_to_vertices(GeometrySet &geometry_set, Mesh *mesh = BKE_mesh_new_nomain(selection.size(), 0, 0, 0, 0); geometry_set.replace_mesh(mesh); - MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>(); + + const AttributeAccessor src_attributes = points->attributes(); + MutableAttributeAccessor dst_attributes = mesh->attributes_for_write(); for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) { const AttributeIDRef attribute_id = entry.key; const eCustomDataType data_type = entry.value.data_type; - GVArray src = point_component->attributes()->lookup_or_default( + GVArray src = src_attributes.lookup_or_default(attribute_id, ATTR_DOMAIN_POINT, data_type); + GSpanAttributeWriter dst = dst_attributes.lookup_or_add_for_write_only_span( attribute_id, ATTR_DOMAIN_POINT, data_type); - GSpanAttributeWriter dst = - mesh_component.attributes_for_write()->lookup_or_add_for_write_only_span( - attribute_id, ATTR_DOMAIN_POINT, data_type); if (dst && src) { src.materialize_compressed_to_uninitialized(selection, dst.span.data()); dst.finish(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc index 4a3048e5f4a..0990eebb903 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc @@ -170,7 +170,7 @@ static void gather_point_data_from_component(GeoNodeExecParams ¶ms, "position", ATTR_DOMAIN_POINT, {0, 0, 0}); Field<float> radius_field = params.get_input<Field<float>>("Radius"); - GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; + bke::GeometryFieldContext field_context{component, ATTR_DOMAIN_POINT}; const int domain_num = component.attribute_domain_size(ATTR_DOMAIN_POINT); r_positions.resize(r_positions.size() + domain_num); @@ -231,17 +231,16 @@ static void initialize_volume_component_from_points(GeoNodeExecParams ¶ms, static void node_geo_exec(GeoNodeExecParams params) { - GeometrySet geometry_set = params.extract_input<GeometrySet>("Points"); - #ifdef WITH_OPENVDB + GeometrySet geometry_set = params.extract_input<GeometrySet>("Points"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { initialize_volume_component_from_points(params, geometry_set); }); params.set_output("Volume", std::move(geometry_set)); #else + params.set_default_remaining_outputs(); params.error_message_add(NodeWarningType::Error, TIP_("Disabled, Blender was compiled without OpenVDB")); - params.set_default_remaining_outputs(); #endif } diff --git a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc index f81748da587..f657b128c51 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc @@ -208,7 +208,7 @@ class RaycastFunction : public fn::MultiFunction { GeometryNodeRaycastMapMode mapping_; /** The field for data evaluated on the target geometry. */ - std::optional<GeometryComponentFieldContext> target_context_; + std::optional<bke::MeshFieldContext> target_context_; std::unique_ptr<FieldEvaluator> target_evaluator_; const GVArray *target_data_ = nullptr; @@ -310,9 +310,9 @@ class RaycastFunction : public fn::MultiFunction { if (!src_field) { return; } - const MeshComponent &mesh_component = *target_.get_component_for_read<MeshComponent>(); - target_context_.emplace(GeometryComponentFieldContext{mesh_component, domain_}); - const int domain_size = mesh_component.attribute_domain_size(domain_); + const Mesh &mesh = *target_.get_mesh_for_read(); + target_context_.emplace(bke::MeshFieldContext{mesh, domain_}); + const int domain_size = mesh.attributes().domain_size(domain_); target_evaluator_ = std::make_unique<FieldEvaluator>(*target_context_, domain_size); target_evaluator_->add(std::move(src_field)); target_evaluator_->evaluate(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_remove_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_remove_attribute.cc index ee279ba58f9..1b398f63691 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_remove_attribute.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_remove_attribute.cc @@ -55,7 +55,7 @@ static void node_geo_exec(GeoNodeExecParams params) }); if (attribute_exists && !cannot_delete) { - params.used_named_attribute(name, eNamedAttrUsage::Remove); + params.used_named_attribute(name, NamedAttributeUsage::Remove); } if (!attribute_exists) { diff --git a/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc index d414bb1fa1d..4ed94e67e74 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc @@ -18,10 +18,8 @@ static void node_declare(NodeDeclarationBuilder &b) static void rotate_instances(GeoNodeExecParams ¶ms, InstancesComponent &instances_component) { - GeometryComponentFieldContext field_context{instances_component, ATTR_DOMAIN_INSTANCE}; - const int domain_num = instances_component.instances_num(); - - fn::FieldEvaluator evaluator{field_context, domain_num}; + const bke::InstancesFieldContext context{instances_component}; + fn::FieldEvaluator evaluator{context, instances_component.instances_num()}; evaluator.set_selection(params.extract_input<Field<bool>>("Selection")); evaluator.add(params.extract_input<Field<float3>>("Rotation")); evaluator.add(params.extract_input<Field<float3>>("Pivot Point")); diff --git a/source/blender/nodes/geometry/nodes/node_geo_sample_index.cc b/source/blender/nodes/geometry/nodes/node_geo_sample_index.cc new file mode 100644 index 00000000000..bfaf9b70f13 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_sample_index.cc @@ -0,0 +1,337 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_task.hh" + +#include "BKE_attribute_math.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "NOD_socket_search_link.hh" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_sample_index_cc { + +NODE_STORAGE_FUNCS(NodeGeometrySampleIndex); + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>(N_("Geometry")) + .supported_type({GEO_COMPONENT_TYPE_MESH, + GEO_COMPONENT_TYPE_POINT_CLOUD, + GEO_COMPONENT_TYPE_CURVE, + GEO_COMPONENT_TYPE_INSTANCES}); + + b.add_input<decl::Float>(N_("Value"), "Value_Float").hide_value().supports_field(); + b.add_input<decl::Int>(N_("Value"), "Value_Int").hide_value().supports_field(); + b.add_input<decl::Vector>(N_("Value"), "Value_Vector").hide_value().supports_field(); + b.add_input<decl::Color>(N_("Value"), "Value_Color").hide_value().supports_field(); + b.add_input<decl::Bool>(N_("Value"), "Value_Bool").hide_value().supports_field(); + b.add_input<decl::Int>(N_("Index")) + .supports_field() + .description(N_("Which element to retrieve a value from on the geometry")); + + b.add_output<decl::Float>(N_("Value"), "Value_Float").dependent_field({6}); + b.add_output<decl::Int>(N_("Value"), "Value_Int").dependent_field({6}); + b.add_output<decl::Vector>(N_("Value"), "Value_Vector").dependent_field({6}); + b.add_output<decl::Color>(N_("Value"), "Value_Color").dependent_field({6}); + b.add_output<decl::Bool>(N_("Value"), "Value_Bool").dependent_field({6}); +} + +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); + uiItemR(layout, ptr, "domain", 0, "", ICON_NONE); + uiItemR(layout, ptr, "clamp", 0, nullptr, ICON_NONE); +} + +static void node_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometrySampleIndex *data = MEM_cnew<NodeGeometrySampleIndex>(__func__); + data->data_type = CD_PROP_FLOAT; + data->domain = ATTR_DOMAIN_POINT; + data->clamp = 0; + node->storage = data; +} + +static void node_update(bNodeTree *ntree, bNode *node) +{ + const eCustomDataType data_type = eCustomDataType(node_storage(*node).data_type); + + bNodeSocket *in_socket_geometry = static_cast<bNodeSocket *>(node->inputs.first); + bNodeSocket *in_socket_float = in_socket_geometry->next; + bNodeSocket *in_socket_int32 = in_socket_float->next; + bNodeSocket *in_socket_vector = in_socket_int32->next; + bNodeSocket *in_socket_color4f = in_socket_vector->next; + bNodeSocket *in_socket_bool = in_socket_color4f->next; + + nodeSetSocketAvailability(ntree, in_socket_vector, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(ntree, in_socket_float, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(ntree, in_socket_color4f, data_type == CD_PROP_COLOR); + nodeSetSocketAvailability(ntree, in_socket_bool, data_type == CD_PROP_BOOL); + nodeSetSocketAvailability(ntree, in_socket_int32, data_type == CD_PROP_INT32); + + bNodeSocket *out_socket_float = static_cast<bNodeSocket *>(node->outputs.first); + bNodeSocket *out_socket_int32 = out_socket_float->next; + bNodeSocket *out_socket_vector = out_socket_int32->next; + bNodeSocket *out_socket_color4f = out_socket_vector->next; + bNodeSocket *out_socket_bool = out_socket_color4f->next; + + nodeSetSocketAvailability(ntree, out_socket_vector, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(ntree, out_socket_float, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(ntree, out_socket_color4f, data_type == CD_PROP_COLOR); + nodeSetSocketAvailability(ntree, out_socket_bool, data_type == CD_PROP_BOOL); + nodeSetSocketAvailability(ntree, out_socket_int32, data_type == CD_PROP_INT32); +} + +static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) +{ + const NodeDeclaration &declaration = *params.node_type().fixed_declaration; + search_link_ops_for_declarations(params, declaration.inputs().take_back(1)); + search_link_ops_for_declarations(params, declaration.inputs().take_front(1)); + + const std::optional<eCustomDataType> type = node_data_type_to_custom_data_type( + (eNodeSocketDatatype)params.other_socket().type); + if (type && *type != CD_PROP_STRING) { + /* The input and output sockets have the same name. */ + params.add_item(IFACE_("Value"), [type](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node("GeometryNodeSampleIndex"); + node_storage(node).data_type = *type; + params.update_and_connect_available_socket(node, "Value"); + }); + } +} + +static bool component_is_available(const GeometrySet &geometry, + const GeometryComponentType type, + const eAttrDomain domain) +{ + if (!geometry.has(type)) { + return false; + } + const GeometryComponent &component = *geometry.get_component_for_read(type); + if (component.is_empty()) { + return false; + } + return component.attribute_domain_size(domain) != 0; +} + +static const GeometryComponent *find_source_component(const GeometrySet &geometry, + const eAttrDomain domain) +{ + /* Choose the other component based on a consistent order, rather than some more complicated + * heuristic. This is the same order visible in the spreadsheet and used in the ray-cast node. */ + static const Array<GeometryComponentType> supported_types = {GEO_COMPONENT_TYPE_MESH, + GEO_COMPONENT_TYPE_POINT_CLOUD, + GEO_COMPONENT_TYPE_CURVE, + GEO_COMPONENT_TYPE_INSTANCES}; + for (const GeometryComponentType src_type : supported_types) { + if (component_is_available(geometry, src_type, domain)) { + return geometry.get_component_for_read(src_type); + } + } + + return nullptr; +} + +template<typename T> +void copy_with_indices(const VArray<T> &src, + const VArray<int> &indices, + const IndexMask mask, + MutableSpan<T> dst) +{ + const IndexRange src_range = src.index_range(); + devirtualize_varray2(src, indices, [&](const auto src, const auto indices) { + threading::parallel_for(mask.index_range(), 4096, [&](IndexRange range) { + for (const int i : mask.slice(range)) { + const int index = indices[i]; + if (src_range.contains(index)) { + dst[i] = src[index]; + } + else { + dst[i] = {}; + } + } + }); + }); +} + +template<typename T> +void copy_with_clamped_indices(const VArray<T> &src, + const VArray<int> &indices, + const IndexMask mask, + MutableSpan<T> dst) +{ + const int last_index = src.index_range().last(); + devirtualize_varray2(src, indices, [&](const auto src, const auto indices) { + threading::parallel_for(mask.index_range(), 4096, [&](IndexRange range) { + for (const int i : mask.slice(range)) { + const int index = indices[i]; + dst[i] = src[std::clamp(index, 0, last_index)]; + } + }); + }); +} + +/** + * The index-based transfer theoretically does not need realized data when there is only one + * instance geometry set in the source. A future optimization could be removing that limitation + * internally. + */ +class SampleIndexFunction : public fn::MultiFunction { + GeometrySet src_geometry_; + GField src_field_; + eAttrDomain domain_; + bool clamp_; + + fn::MFSignature signature_; + + std::optional<bke::GeometryFieldContext> geometry_context_; + std::unique_ptr<FieldEvaluator> evaluator_; + const GVArray *src_data_ = nullptr; + + public: + SampleIndexFunction(GeometrySet geometry, + GField src_field, + const eAttrDomain domain, + const bool clamp) + : src_geometry_(std::move(geometry)), + src_field_(std::move(src_field)), + domain_(domain), + clamp_(clamp) + { + src_geometry_.ensure_owns_direct_data(); + + signature_ = this->create_signature(); + this->set_signature(&signature_); + + this->evaluate_field(); + } + + fn::MFSignature create_signature() + { + fn::MFSignatureBuilder signature{"Sample Index"}; + signature.single_input<int>("Index"); + signature.single_output("Value", src_field_.cpp_type()); + return signature.build(); + } + + void evaluate_field() + { + const GeometryComponent *component = find_source_component(src_geometry_, domain_); + if (component == nullptr) { + return; + } + const int domain_num = component->attribute_domain_size(domain_); + geometry_context_.emplace(bke::GeometryFieldContext(*component, domain_)); + evaluator_ = std::make_unique<FieldEvaluator>(*geometry_context_, domain_num); + evaluator_->add(src_field_); + evaluator_->evaluate(); + src_data_ = &evaluator_->get_evaluated(0); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + const VArray<int> &indices = params.readonly_single_input<int>(0, "Index"); + GMutableSpan dst = params.uninitialized_single_output(1, "Value"); + + const CPPType &type = dst.type(); + if (src_data_ == nullptr) { + type.value_initialize_indices(dst.data(), mask); + return; + } + + attribute_math::convert_to_static_type(type, [&](auto dummy) { + using T = decltype(dummy); + if (clamp_) { + copy_with_clamped_indices(src_data_->typed<T>(), indices, mask, dst.typed<T>()); + } + else { + copy_with_indices(src_data_->typed<T>(), indices, mask, dst.typed<T>()); + } + }); + } +}; + +static GField get_input_attribute_field(GeoNodeExecParams ¶ms, const eCustomDataType data_type) +{ + switch (data_type) { + case CD_PROP_FLOAT: + return params.extract_input<Field<float>>("Value_Float"); + case CD_PROP_FLOAT3: + return params.extract_input<Field<float3>>("Value_Vector"); + case CD_PROP_COLOR: + return params.extract_input<Field<ColorGeometry4f>>("Value_Color"); + case CD_PROP_BOOL: + return params.extract_input<Field<bool>>("Value_Bool"); + case CD_PROP_INT32: + return params.extract_input<Field<int>>("Value_Int"); + default: + BLI_assert_unreachable(); + } + return {}; +} + +static void output_attribute_field(GeoNodeExecParams ¶ms, GField field) +{ + switch (bke::cpp_type_to_custom_data_type(field.cpp_type())) { + case CD_PROP_FLOAT: { + params.set_output("Value_Float", Field<float>(field)); + break; + } + case CD_PROP_FLOAT3: { + params.set_output("Value_Vector", Field<float3>(field)); + break; + } + case CD_PROP_COLOR: { + params.set_output("Value_Color", Field<ColorGeometry4f>(field)); + break; + } + case CD_PROP_BOOL: { + params.set_output("Value_Bool", Field<bool>(field)); + break; + } + case CD_PROP_INT32: { + params.set_output("Value_Int", Field<int>(field)); + break; + } + default: + break; + } +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + GeometrySet geometry = params.extract_input<GeometrySet>("Geometry"); + const NodeGeometrySampleIndex &storage = node_storage(params.node()); + const eCustomDataType data_type = eCustomDataType(storage.data_type); + const eAttrDomain domain = eAttrDomain(storage.domain); + + auto fn = std::make_shared<SampleIndexFunction>(std::move(geometry), + get_input_attribute_field(params, data_type), + domain, + bool(storage.clamp)); + auto op = FieldOperation::Create(std::move(fn), {params.extract_input<Field<int>>("Index")}); + output_attribute_field(params, GField(std::move(op))); +} + +} // namespace blender::nodes::node_geo_sample_index_cc + +void register_node_type_geo_sample_index() +{ + namespace file_ns = blender::nodes::node_geo_sample_index_cc; + + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_SAMPLE_INDEX, "Sample Index", NODE_CLASS_GEOMETRY); + node_type_init(&ntype, file_ns::node_init); + node_type_update(&ntype, file_ns::node_update); + ntype.declare = file_ns::node_declare; + node_type_storage( + &ntype, "NodeGeometrySampleIndex", node_free_standard_storage, node_copy_standard_storage); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.draw_buttons = file_ns::node_layout; + ntype.gather_link_search_ops = file_ns::node_gather_link_searches; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_sample_nearest.cc b/source/blender/nodes/geometry/nodes/node_geo_sample_nearest.cc new file mode 100644 index 00000000000..691e3101d3d --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_sample_nearest.cc @@ -0,0 +1,345 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_task.hh" + +#include "DNA_pointcloud_types.h" + +#include "BKE_bvhutils.h" +#include "BKE_mesh.h" +#include "BKE_mesh_runtime.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +void get_closest_in_bvhtree(BVHTreeFromMesh &tree_data, + const VArray<float3> &positions, + const IndexMask mask, + const MutableSpan<int> r_indices, + const MutableSpan<float> r_distances_sq, + const MutableSpan<float3> r_positions) +{ + BLI_assert(positions.size() >= r_indices.size()); + BLI_assert(positions.size() >= r_distances_sq.size()); + BLI_assert(positions.size() >= r_positions.size()); + + for (const int i : mask) { + BVHTreeNearest nearest; + nearest.dist_sq = FLT_MAX; + const float3 position = positions[i]; + BLI_bvhtree_find_nearest( + tree_data.tree, position, &nearest, tree_data.nearest_callback, &tree_data); + if (!r_indices.is_empty()) { + r_indices[i] = nearest.index; + } + if (!r_distances_sq.is_empty()) { + r_distances_sq[i] = nearest.dist_sq; + } + if (!r_positions.is_empty()) { + r_positions[i] = nearest.co; + } + } +} + +} // namespace blender::nodes + +namespace blender::nodes::node_geo_sample_nearest_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>(N_("Geometry")) + .supported_type({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD}); + b.add_input<decl::Vector>(N_("Sample Position")).implicit_field(); + b.add_output<decl::Int>(N_("Index")).dependent_field({1}); +} + +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "domain", 0, "", ICON_NONE); +} + +static void node_init(bNodeTree *UNUSED(tree), bNode *node) +{ + node->custom1 = CD_PROP_FLOAT; + node->custom2 = ATTR_DOMAIN_POINT; +} + +static void get_closest_pointcloud_points(const PointCloud &pointcloud, + const VArray<float3> &positions, + const IndexMask mask, + const MutableSpan<int> r_indices, + const MutableSpan<float> r_distances_sq) +{ + BLI_assert(positions.size() >= r_indices.size()); + BLI_assert(pointcloud.totpoint > 0); + + BVHTreeFromPointCloud tree_data; + BKE_bvhtree_from_pointcloud_get(&tree_data, &pointcloud, 2); + + for (const int i : mask) { + BVHTreeNearest nearest; + nearest.dist_sq = FLT_MAX; + const float3 position = positions[i]; + BLI_bvhtree_find_nearest( + tree_data.tree, position, &nearest, tree_data.nearest_callback, &tree_data); + r_indices[i] = nearest.index; + if (!r_distances_sq.is_empty()) { + r_distances_sq[i] = nearest.dist_sq; + } + } + + free_bvhtree_from_pointcloud(&tree_data); +} + +static void get_closest_mesh_points(const Mesh &mesh, + const VArray<float3> &positions, + const IndexMask mask, + const MutableSpan<int> r_point_indices, + const MutableSpan<float> r_distances_sq, + const MutableSpan<float3> r_positions) +{ + BLI_assert(mesh.totvert > 0); + BVHTreeFromMesh tree_data; + BKE_bvhtree_from_mesh_get(&tree_data, &mesh, BVHTREE_FROM_VERTS, 2); + get_closest_in_bvhtree(tree_data, positions, mask, r_point_indices, r_distances_sq, r_positions); + free_bvhtree_from_mesh(&tree_data); +} + +static void get_closest_mesh_edges(const Mesh &mesh, + const VArray<float3> &positions, + const IndexMask mask, + const MutableSpan<int> r_edge_indices, + const MutableSpan<float> r_distances_sq, + const MutableSpan<float3> r_positions) +{ + BLI_assert(mesh.totedge > 0); + BVHTreeFromMesh tree_data; + BKE_bvhtree_from_mesh_get(&tree_data, &mesh, BVHTREE_FROM_EDGES, 2); + get_closest_in_bvhtree(tree_data, positions, mask, r_edge_indices, r_distances_sq, r_positions); + free_bvhtree_from_mesh(&tree_data); +} + +static void get_closest_mesh_looptris(const Mesh &mesh, + const VArray<float3> &positions, + const IndexMask mask, + const MutableSpan<int> r_looptri_indices, + const MutableSpan<float> r_distances_sq, + const MutableSpan<float3> r_positions) +{ + BLI_assert(mesh.totpoly > 0); + BVHTreeFromMesh tree_data; + BKE_bvhtree_from_mesh_get(&tree_data, &mesh, BVHTREE_FROM_LOOPTRI, 2); + get_closest_in_bvhtree( + tree_data, positions, mask, r_looptri_indices, r_distances_sq, r_positions); + free_bvhtree_from_mesh(&tree_data); +} + +static void get_closest_mesh_polys(const Mesh &mesh, + const VArray<float3> &positions, + const IndexMask mask, + const MutableSpan<int> r_poly_indices, + const MutableSpan<float> r_distances_sq, + const MutableSpan<float3> r_positions) +{ + BLI_assert(mesh.totpoly > 0); + + Array<int> looptri_indices(positions.size()); + get_closest_mesh_looptris(mesh, positions, mask, looptri_indices, r_distances_sq, r_positions); + + const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&mesh), + BKE_mesh_runtime_looptri_len(&mesh)}; + + for (const int i : mask) { + const MLoopTri &looptri = looptris[looptri_indices[i]]; + r_poly_indices[i] = looptri.poly; + } +} + +/* The closest corner is defined to be the closest corner on the closest face. */ +static void get_closest_mesh_corners(const Mesh &mesh, + const VArray<float3> &positions, + const IndexMask mask, + const MutableSpan<int> r_corner_indices, + const MutableSpan<float> r_distances_sq, + const MutableSpan<float3> r_positions) +{ + const Span<MVert> verts = mesh.verts(); + const Span<MPoly> polys = mesh.polys(); + const Span<MLoop> loops = mesh.loops(); + + BLI_assert(mesh.totloop > 0); + Array<int> poly_indices(positions.size()); + get_closest_mesh_polys(mesh, positions, mask, poly_indices, {}, {}); + + for (const int i : mask) { + const float3 position = positions[i]; + const int poly_index = poly_indices[i]; + const MPoly &poly = polys[poly_index]; + + /* Find the closest vertex in the polygon. */ + float min_distance_sq = FLT_MAX; + const MVert *closest_mvert; + int closest_loop_index = 0; + for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { + const MLoop &loop = loops[loop_index]; + const int vertex_index = loop.v; + const MVert &mvert = verts[vertex_index]; + const float distance_sq = math::distance_squared(position, float3(mvert.co)); + if (distance_sq < min_distance_sq) { + min_distance_sq = distance_sq; + closest_loop_index = loop_index; + closest_mvert = &mvert; + } + } + if (!r_corner_indices.is_empty()) { + r_corner_indices[i] = closest_loop_index; + } + if (!r_positions.is_empty()) { + r_positions[i] = closest_mvert->co; + } + if (!r_distances_sq.is_empty()) { + r_distances_sq[i] = min_distance_sq; + } + } +} + +static bool component_is_available(const GeometrySet &geometry, + const GeometryComponentType type, + const eAttrDomain domain) +{ + if (!geometry.has(type)) { + return false; + } + const GeometryComponent &component = *geometry.get_component_for_read(type); + if (component.is_empty()) { + return false; + } + return component.attribute_domain_size(domain) != 0; +} + +static const GeometryComponent *find_source_component(const GeometrySet &geometry, + const eAttrDomain domain) +{ + /* Choose the other component based on a consistent order, rather than some more complicated + * heuristic. This is the same order visible in the spreadsheet and used in the ray-cast node. */ + static const Array<GeometryComponentType> supported_types = {GEO_COMPONENT_TYPE_MESH, + GEO_COMPONENT_TYPE_POINT_CLOUD, + GEO_COMPONENT_TYPE_CURVE, + GEO_COMPONENT_TYPE_INSTANCES}; + for (const GeometryComponentType src_type : supported_types) { + if (component_is_available(geometry, src_type, domain)) { + return geometry.get_component_for_read(src_type); + } + } + + return nullptr; +} + +class SampleNearestFunction : public fn::MultiFunction { + GeometrySet source_; + eAttrDomain domain_; + + const GeometryComponent *src_component_; + + fn::MFSignature signature_; + + public: + SampleNearestFunction(GeometrySet geometry, eAttrDomain domain) + : source_(std::move(geometry)), domain_(domain) + { + source_.ensure_owns_direct_data(); + signature_ = this->create_signature(); + this->set_signature(&signature_); + + this->src_component_ = find_source_component(source_, domain_); + } + + fn::MFSignature create_signature() + { + blender::fn::MFSignatureBuilder signature{"Sample Nearest"}; + signature.single_input<float3>("Position"); + signature.single_output<int>("Index"); + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + const VArray<float3> &positions = params.readonly_single_input<float3>(0, "Position"); + MutableSpan<int> indices = params.uninitialized_single_output<int>(1, "Index"); + if (!src_component_) { + indices.fill_indices(mask, 0); + return; + } + + switch (src_component_->type()) { + case GEO_COMPONENT_TYPE_MESH: { + const MeshComponent &component = *static_cast<const MeshComponent *>(src_component_); + const Mesh &mesh = *component.get_for_read(); + Array<float> distances(mask.min_array_size()); + switch (domain_) { + case ATTR_DOMAIN_POINT: + get_closest_mesh_points(mesh, positions, mask, indices, distances, {}); + break; + case ATTR_DOMAIN_EDGE: + get_closest_mesh_edges(mesh, positions, mask, indices, distances, {}); + break; + case ATTR_DOMAIN_FACE: + get_closest_mesh_polys(mesh, positions, mask, indices, distances, {}); + break; + case ATTR_DOMAIN_CORNER: + get_closest_mesh_corners(mesh, positions, mask, indices, distances, {}); + break; + default: + break; + } + break; + } + case GEO_COMPONENT_TYPE_POINT_CLOUD: { + const PointCloudComponent &component = *static_cast<const PointCloudComponent *>( + src_component_); + const PointCloud &points = *component.get_for_read(); + Array<float> distances(mask.min_array_size()); + get_closest_pointcloud_points(points, positions, mask, indices, distances); + break; + } + default: + break; + } + } +}; + +static void node_geo_exec(GeoNodeExecParams params) +{ + GeometrySet geometry = params.extract_input<GeometrySet>("Geometry"); + const eAttrDomain domain = eAttrDomain(params.node().custom2); + if (geometry.has_curves() && !geometry.has_mesh() && !geometry.has_pointcloud()) { + params.error_message_add(NodeWarningType::Error, + TIP_("The source geometry must contain a mesh or a point cloud")); + params.set_default_remaining_outputs(); + return; + } + + Field<float3> positions = params.extract_input<Field<float3>>("Sample Position"); + auto fn = std::make_shared<SampleNearestFunction>(std::move(geometry), domain); + auto op = FieldOperation::Create(std::move(fn), {std::move(positions)}); + params.set_output<Field<int>>("Index", Field<int>(std::move(op))); +} + +} // namespace blender::nodes::node_geo_sample_nearest_cc + +void register_node_type_geo_sample_nearest() +{ + namespace file_ns = blender::nodes::node_geo_sample_nearest_cc; + + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_SAMPLE_NEAREST, "Sample Nearest", NODE_CLASS_GEOMETRY); + node_type_init(&ntype, file_ns::node_init); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.draw_buttons = file_ns::node_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_sample_nearest_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_sample_nearest_surface.cc new file mode 100644 index 00000000000..9290185aef0 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_sample_nearest_surface.cc @@ -0,0 +1,280 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_generic_array.hh" +#include "BLI_kdopbvh.h" +#include "BLI_task.hh" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_pointcloud_types.h" + +#include "BKE_attribute_math.hh" +#include "BKE_bvhutils.h" +#include "BKE_mesh.h" +#include "BKE_mesh_runtime.h" +#include "BKE_mesh_sample.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "NOD_socket_search_link.hh" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_sample_nearest_surface_cc { + +using namespace blender::bke::mesh_surface_sample; + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>(N_("Mesh")).supported_type({GEO_COMPONENT_TYPE_MESH}); + + b.add_input<decl::Float>(N_("Value"), "Value_Float").hide_value().supports_field(); + b.add_input<decl::Int>(N_("Value"), "Value_Int").hide_value().supports_field(); + b.add_input<decl::Vector>(N_("Value"), "Value_Vector").hide_value().supports_field(); + b.add_input<decl::Color>(N_("Value"), "Value_Color").hide_value().supports_field(); + b.add_input<decl::Bool>(N_("Value"), "Value_Bool").hide_value().supports_field(); + + b.add_input<decl::Vector>(N_("Sample Position")).implicit_field(); + + b.add_output<decl::Float>(N_("Value"), "Value_Float").dependent_field({6}); + b.add_output<decl::Int>(N_("Value"), "Value_Int").dependent_field({6}); + b.add_output<decl::Vector>(N_("Value"), "Value_Vector").dependent_field({6}); + b.add_output<decl::Color>(N_("Value"), "Value_Color").dependent_field({6}); + b.add_output<decl::Bool>(N_("Value"), "Value_Bool").dependent_field({6}); +} + +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); +} + +static void node_init(bNodeTree *UNUSED(tree), bNode *node) +{ + node->custom1 = CD_PROP_FLOAT; +} + +static void node_update(bNodeTree *ntree, bNode *node) +{ + const eCustomDataType data_type = eCustomDataType(node->custom1); + + bNodeSocket *in_socket_mesh = static_cast<bNodeSocket *>(node->inputs.first); + bNodeSocket *in_socket_float = in_socket_mesh->next; + bNodeSocket *in_socket_int32 = in_socket_float->next; + bNodeSocket *in_socket_vector = in_socket_int32->next; + bNodeSocket *in_socket_color4f = in_socket_vector->next; + bNodeSocket *in_socket_bool = in_socket_color4f->next; + + nodeSetSocketAvailability(ntree, in_socket_vector, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(ntree, in_socket_float, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(ntree, in_socket_color4f, data_type == CD_PROP_COLOR); + nodeSetSocketAvailability(ntree, in_socket_bool, data_type == CD_PROP_BOOL); + nodeSetSocketAvailability(ntree, in_socket_int32, data_type == CD_PROP_INT32); + + bNodeSocket *out_socket_float = static_cast<bNodeSocket *>(node->outputs.first); + bNodeSocket *out_socket_int32 = out_socket_float->next; + bNodeSocket *out_socket_vector = out_socket_int32->next; + bNodeSocket *out_socket_color4f = out_socket_vector->next; + bNodeSocket *out_socket_bool = out_socket_color4f->next; + + nodeSetSocketAvailability(ntree, out_socket_vector, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(ntree, out_socket_float, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(ntree, out_socket_color4f, data_type == CD_PROP_COLOR); + nodeSetSocketAvailability(ntree, out_socket_bool, data_type == CD_PROP_BOOL); + nodeSetSocketAvailability(ntree, out_socket_int32, data_type == CD_PROP_INT32); +} + +static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) +{ + const NodeDeclaration &declaration = *params.node_type().fixed_declaration; + search_link_ops_for_declarations(params, declaration.inputs().take_back(2)); + search_link_ops_for_declarations(params, declaration.inputs().take_front(1)); + + const std::optional<eCustomDataType> type = node_data_type_to_custom_data_type( + (eNodeSocketDatatype)params.other_socket().type); + if (type && *type != CD_PROP_STRING) { + /* The input and output sockets have the same name. */ + params.add_item(IFACE_("Value"), [type](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node("GeometryNodeSampleNearestSurface"); + node.custom1 = *type; + params.update_and_connect_available_socket(node, "Value"); + }); + } +} + +static void get_closest_mesh_looptris(const Mesh &mesh, + const VArray<float3> &positions, + const IndexMask mask, + const MutableSpan<int> r_looptri_indices, + const MutableSpan<float> r_distances_sq, + const MutableSpan<float3> r_positions) +{ + BLI_assert(mesh.totpoly > 0); + BVHTreeFromMesh tree_data; + BKE_bvhtree_from_mesh_get(&tree_data, &mesh, BVHTREE_FROM_LOOPTRI, 2); + get_closest_in_bvhtree( + tree_data, positions, mask, r_looptri_indices, r_distances_sq, r_positions); + free_bvhtree_from_mesh(&tree_data); +} + +/** + * \note Multi-threading for this function is provided by the field evaluator. Since the #call + * function could be called many times, calculate the data from the source geometry once and store + * it for later. + */ +class SampleNearestSurfaceFunction : public fn::MultiFunction { + GeometrySet source_; + GField src_field_; + + /** + * This function is meant to sample the surface of a mesh rather than take the value from + * individual elements, so use the most complex domain, ensuring no information is lost. In the + * future, it should be possible to use the most complex domain required by the field inputs, to + * simplify sampling and avoid domain conversions. + */ + eAttrDomain domain_ = ATTR_DOMAIN_CORNER; + + fn::MFSignature signature_; + + std::optional<bke::MeshFieldContext> source_context_; + std::unique_ptr<FieldEvaluator> source_evaluator_; + const GVArray *source_data_; + + public: + SampleNearestSurfaceFunction(GeometrySet geometry, GField src_field) + : source_(std::move(geometry)), src_field_(std::move(src_field)) + { + source_.ensure_owns_direct_data(); + signature_ = this->create_signature(); + this->set_signature(&signature_); + this->evaluate_source_field(); + } + + fn::MFSignature create_signature() + { + blender::fn::MFSignatureBuilder signature{"Sample Nearest Surface"}; + signature.single_input<float3>("Position"); + signature.single_output("Value", src_field_.cpp_type()); + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + const VArray<float3> &positions = params.readonly_single_input<float3>(0, "Position"); + GMutableSpan dst = params.uninitialized_single_output_if_required(1, "Value"); + + const MeshComponent &mesh_component = *source_.get_component_for_read<MeshComponent>(); + BLI_assert(mesh_component.has_mesh()); + const Mesh &mesh = *mesh_component.get_for_read(); + BLI_assert(mesh.totpoly > 0); + + /* Find closest points on the mesh surface. */ + Array<int> looptri_indices(mask.min_array_size()); + Array<float3> sampled_positions(mask.min_array_size()); + get_closest_mesh_looptris(mesh, positions, mask, looptri_indices, {}, sampled_positions); + + MeshAttributeInterpolator interp(&mesh, mask, sampled_positions, looptri_indices); + interp.sample_data(*source_data_, domain_, eAttributeMapMode::INTERPOLATED, dst); + } + + private: + void evaluate_source_field() + { + const Mesh &mesh = *source_.get_mesh_for_read(); + source_context_.emplace(bke::MeshFieldContext{mesh, domain_}); + const int domain_size = mesh.attributes().domain_size(domain_); + source_evaluator_ = std::make_unique<FieldEvaluator>(*source_context_, domain_size); + source_evaluator_->add(src_field_); + source_evaluator_->evaluate(); + source_data_ = &source_evaluator_->get_evaluated(0); + } +}; + +static GField get_input_attribute_field(GeoNodeExecParams ¶ms, const eCustomDataType data_type) +{ + switch (data_type) { + case CD_PROP_FLOAT: + return params.extract_input<Field<float>>("Value_Float"); + case CD_PROP_FLOAT3: + return params.extract_input<Field<float3>>("Value_Vector"); + case CD_PROP_COLOR: + return params.extract_input<Field<ColorGeometry4f>>("Value_Color"); + case CD_PROP_BOOL: + return params.extract_input<Field<bool>>("Value_Bool"); + case CD_PROP_INT32: + return params.extract_input<Field<int>>("Value_Int"); + default: + BLI_assert_unreachable(); + } + return {}; +} + +static void output_attribute_field(GeoNodeExecParams ¶ms, GField field) +{ + switch (bke::cpp_type_to_custom_data_type(field.cpp_type())) { + case CD_PROP_FLOAT: { + params.set_output("Value_Float", Field<float>(field)); + break; + } + case CD_PROP_FLOAT3: { + params.set_output("Value_Vector", Field<float3>(field)); + break; + } + case CD_PROP_COLOR: { + params.set_output("Value_Color", Field<ColorGeometry4f>(field)); + break; + } + case CD_PROP_BOOL: { + params.set_output("Value_Bool", Field<bool>(field)); + break; + } + case CD_PROP_INT32: { + params.set_output("Value_Int", Field<int>(field)); + break; + } + default: + break; + } +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + GeometrySet geometry = params.extract_input<GeometrySet>("Mesh"); + const eCustomDataType data_type = eCustomDataType(params.node().custom1); + const Mesh *mesh = geometry.get_mesh_for_read(); + if (mesh == nullptr) { + params.set_default_remaining_outputs(); + return; + } + if (mesh->totpoly == 0 && mesh->totvert != 0) { + params.error_message_add(NodeWarningType::Error, TIP_("The source mesh must have faces")); + params.set_default_remaining_outputs(); + return; + } + + Field<float3> positions = params.extract_input<Field<float3>>("Sample Position"); + GField field = get_input_attribute_field(params, data_type); + auto fn = std::make_shared<SampleNearestSurfaceFunction>(std::move(geometry), std::move(field)); + auto op = FieldOperation::Create(std::move(fn), {std::move(positions)}); + output_attribute_field(params, GField(std::move(op))); +} + +} // namespace blender::nodes::node_geo_sample_nearest_surface_cc + +void register_node_type_geo_sample_nearest_surface() +{ + namespace file_ns = blender::nodes::node_geo_sample_nearest_surface_cc; + + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_SAMPLE_NEAREST_SURFACE, "Sample Nearest Surface", NODE_CLASS_GEOMETRY); + node_type_init(&ntype, file_ns::node_init); + node_type_update(&ntype, file_ns::node_update); + ntype.declare = file_ns::node_declare; + node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.draw_buttons = file_ns::node_layout; + ntype.gather_link_search_ops = file_ns::node_gather_link_searches; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_scale_elements.cc b/source/blender/nodes/geometry/nodes/node_geo_scale_elements.cc index d674f611c9f..2ebbf88b8ad 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_scale_elements.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_scale_elements.cc @@ -147,14 +147,22 @@ static float4x4 create_single_axis_transform(const float3 ¢er, return transform; } -using GetVertexIndicesFn = - FunctionRef<void(const Mesh &mesh, int element_index, VectorSet<int> &r_vertex_indices)>; +using GetVertexIndicesFn = FunctionRef<void(Span<MEdge> edges, + Span<MPoly> polys, + Span<MLoop> loops, + int element_index, + VectorSet<int> &r_vertex_indices)>; static void scale_vertex_islands_uniformly(Mesh &mesh, const Span<ElementIsland> islands, const UniformScaleParams ¶ms, const GetVertexIndicesFn get_vertex_indices) { + MutableSpan<MVert> verts = mesh.verts_for_write(); + const Span<MEdge> edges = mesh.edges(); + const Span<MPoly> polys = mesh.polys(); + const Span<MLoop> loops = mesh.loops(); + threading::parallel_for(islands.index_range(), 256, [&](const IndexRange range) { for (const int island_index : range) { const ElementIsland &island = islands[island_index]; @@ -164,7 +172,7 @@ static void scale_vertex_islands_uniformly(Mesh &mesh, VectorSet<int> vertex_indices; for (const int poly_index : island.element_indices) { - get_vertex_indices(mesh, poly_index, vertex_indices); + get_vertex_indices(edges, polys, loops, poly_index, vertex_indices); center += params.centers[poly_index]; scale += params.scales[poly_index]; } @@ -175,7 +183,7 @@ static void scale_vertex_islands_uniformly(Mesh &mesh, center *= f; for (const int vert_index : vertex_indices) { - MVert &vert = mesh.mvert[vert_index]; + MVert &vert = verts[vert_index]; const float3 old_position = vert.co; const float3 new_position = transform_with_uniform_scale(old_position, center, scale); copy_v3_v3(vert.co, new_position); @@ -191,6 +199,11 @@ static void scale_vertex_islands_on_axis(Mesh &mesh, const AxisScaleParams ¶ms, const GetVertexIndicesFn get_vertex_indices) { + MutableSpan<MVert> verts = mesh.verts_for_write(); + const Span<MEdge> edges = mesh.edges(); + const Span<MPoly> polys = mesh.polys(); + const Span<MLoop> loops = mesh.loops(); + threading::parallel_for(islands.index_range(), 256, [&](const IndexRange range) { for (const int island_index : range) { const ElementIsland &island = islands[island_index]; @@ -201,7 +214,7 @@ static void scale_vertex_islands_on_axis(Mesh &mesh, VectorSet<int> vertex_indices; for (const int poly_index : island.element_indices) { - get_vertex_indices(mesh, poly_index, vertex_indices); + get_vertex_indices(edges, polys, loops, poly_index, vertex_indices); center += params.centers[poly_index]; scale += params.scales[poly_index]; axis += params.axis_vectors[poly_index]; @@ -219,7 +232,7 @@ static void scale_vertex_islands_on_axis(Mesh &mesh, const float4x4 transform = create_single_axis_transform(center, axis, scale); for (const int vert_index : vertex_indices) { - MVert &vert = mesh.mvert[vert_index]; + MVert &vert = verts[vert_index]; const float3 old_position = vert.co; const float3 new_position = transform * old_position; copy_v3_v3(vert.co, new_position); @@ -232,11 +245,14 @@ static void scale_vertex_islands_on_axis(Mesh &mesh, static Vector<ElementIsland> prepare_face_islands(const Mesh &mesh, const IndexMask face_selection) { + const Span<MPoly> polys = mesh.polys(); + const Span<MLoop> loops = mesh.loops(); + /* Use the disjoint set data structure to determine which vertices have to be scaled together. */ DisjointSet disjoint_set(mesh.totvert); for (const int poly_index : face_selection) { - const MPoly &poly = mesh.mpoly[poly_index]; - const Span<MLoop> poly_loops{mesh.mloop + poly.loopstart, poly.totloop}; + const MPoly &poly = polys[poly_index]; + const Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop); for (const int loop_index : IndexRange(poly.totloop - 1)) { const int v1 = poly_loops[loop_index].v; const int v2 = poly_loops[loop_index + 1].v; @@ -252,8 +268,8 @@ static Vector<ElementIsland> prepare_face_islands(const Mesh &mesh, const IndexM /* Gather all of the face indices in each island into separate vectors. */ for (const int poly_index : face_selection) { - const MPoly &poly = mesh.mpoly[poly_index]; - const Span<MLoop> poly_loops{mesh.mloop + poly.loopstart, poly.totloop}; + const MPoly &poly = polys[poly_index]; + const Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop); const int island_id = disjoint_set.find_root(poly_loops[0].v); const int island_index = island_ids.index_of_or_add(island_id); if (island_index == islands.size()) { @@ -266,10 +282,14 @@ static Vector<ElementIsland> prepare_face_islands(const Mesh &mesh, const IndexM return islands; } -static void get_face_vertices(const Mesh &mesh, int face_index, VectorSet<int> &r_vertex_indices) +static void get_face_verts(const Span<MEdge> /*edges*/, + const Span<MPoly> polys, + const Span<MLoop> loops, + int face_index, + VectorSet<int> &r_vertex_indices) { - const MPoly &poly = mesh.mpoly[face_index]; - const Span<MLoop> poly_loops{mesh.mloop + poly.loopstart, poly.totloop}; + const MPoly &poly = polys[face_index]; + const Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop); for (const MLoop &loop : poly_loops) { r_vertex_indices.add(loop.v); } @@ -288,18 +308,14 @@ static AxisScaleParams evaluate_axis_scale_fields(FieldEvaluator &evaluator, return out; } -static void scale_faces_on_axis(MeshComponent &mesh_component, const AxisScaleFields &fields) +static void scale_faces_on_axis(Mesh &mesh, const AxisScaleFields &fields) { - Mesh &mesh = *mesh_component.get_for_write(); - mesh.mvert = static_cast<MVert *>( - CustomData_duplicate_referenced_layer(&mesh.vdata, CD_MVERT, mesh.totvert)); - - GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_FACE}; + bke::MeshFieldContext field_context{mesh, ATTR_DOMAIN_FACE}; FieldEvaluator evaluator{field_context, mesh.totpoly}; AxisScaleParams params = evaluate_axis_scale_fields(evaluator, fields); Vector<ElementIsland> island = prepare_face_islands(mesh, params.selection); - scale_vertex_islands_on_axis(mesh, island, params, get_face_vertices); + scale_vertex_islands_on_axis(mesh, island, params, get_face_verts); } static UniformScaleParams evaluate_uniform_scale_fields(FieldEvaluator &evaluator, @@ -314,26 +330,24 @@ static UniformScaleParams evaluate_uniform_scale_fields(FieldEvaluator &evaluato return out; } -static void scale_faces_uniformly(MeshComponent &mesh_component, const UniformScaleFields &fields) +static void scale_faces_uniformly(Mesh &mesh, const UniformScaleFields &fields) { - Mesh &mesh = *mesh_component.get_for_write(); - mesh.mvert = static_cast<MVert *>( - CustomData_duplicate_referenced_layer(&mesh.vdata, CD_MVERT, mesh.totvert)); - - GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_FACE}; + bke::MeshFieldContext field_context{mesh, ATTR_DOMAIN_FACE}; FieldEvaluator evaluator{field_context, mesh.totpoly}; UniformScaleParams params = evaluate_uniform_scale_fields(evaluator, fields); Vector<ElementIsland> island = prepare_face_islands(mesh, params.selection); - scale_vertex_islands_uniformly(mesh, island, params, get_face_vertices); + scale_vertex_islands_uniformly(mesh, island, params, get_face_verts); } static Vector<ElementIsland> prepare_edge_islands(const Mesh &mesh, const IndexMask edge_selection) { + const Span<MEdge> edges = mesh.edges(); + /* Use the disjoint set data structure to determine which vertices have to be scaled together. */ DisjointSet disjoint_set(mesh.totvert); for (const int edge_index : edge_selection) { - const MEdge &edge = mesh.medge[edge_index]; + const MEdge &edge = edges[edge_index]; disjoint_set.join(edge.v1, edge.v2); } @@ -344,7 +358,7 @@ static Vector<ElementIsland> prepare_edge_islands(const Mesh &mesh, const IndexM /* Gather all of the edge indices in each island into separate vectors. */ for (const int edge_index : edge_selection) { - const MEdge &edge = mesh.medge[edge_index]; + const MEdge &edge = edges[edge_index]; const int island_id = disjoint_set.find_root(edge.v1); const int island_index = island_ids.index_of_or_add(island_id); if (island_index == islands.size()) { @@ -357,39 +371,35 @@ static Vector<ElementIsland> prepare_edge_islands(const Mesh &mesh, const IndexM return islands; } -static void get_edge_vertices(const Mesh &mesh, int edge_index, VectorSet<int> &r_vertex_indices) +static void get_edge_verts(const Span<MEdge> edges, + const Span<MPoly> /*polys*/, + const Span<MLoop> /*loops*/, + int edge_index, + VectorSet<int> &r_vertex_indices) { - const MEdge &edge = mesh.medge[edge_index]; + const MEdge &edge = edges[edge_index]; r_vertex_indices.add(edge.v1); r_vertex_indices.add(edge.v2); } -static void scale_edges_uniformly(MeshComponent &mesh_component, const UniformScaleFields &fields) +static void scale_edges_uniformly(Mesh &mesh, const UniformScaleFields &fields) { - Mesh &mesh = *mesh_component.get_for_write(); - mesh.mvert = static_cast<MVert *>( - CustomData_duplicate_referenced_layer(&mesh.vdata, CD_MVERT, mesh.totvert)); - - GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_EDGE}; + bke::MeshFieldContext field_context{mesh, ATTR_DOMAIN_EDGE}; FieldEvaluator evaluator{field_context, mesh.totedge}; UniformScaleParams params = evaluate_uniform_scale_fields(evaluator, fields); Vector<ElementIsland> island = prepare_edge_islands(mesh, params.selection); - scale_vertex_islands_uniformly(mesh, island, params, get_edge_vertices); + scale_vertex_islands_uniformly(mesh, island, params, get_edge_verts); } -static void scale_edges_on_axis(MeshComponent &mesh_component, const AxisScaleFields &fields) +static void scale_edges_on_axis(Mesh &mesh, const AxisScaleFields &fields) { - Mesh &mesh = *mesh_component.get_for_write(); - mesh.mvert = static_cast<MVert *>( - CustomData_duplicate_referenced_layer(&mesh.vdata, CD_MVERT, mesh.totvert)); - - GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_EDGE}; + bke::MeshFieldContext field_context{mesh, ATTR_DOMAIN_EDGE}; FieldEvaluator evaluator{field_context, mesh.totedge}; AxisScaleParams params = evaluate_axis_scale_fields(evaluator, fields); Vector<ElementIsland> island = prepare_edge_islands(mesh, params.selection); - scale_vertex_islands_on_axis(mesh, island, params, get_edge_vertices); + scale_vertex_islands_on_axis(mesh, island, params, get_edge_verts); } static void node_geo_exec(GeoNodeExecParams params) @@ -410,42 +420,38 @@ static void node_geo_exec(GeoNodeExecParams params) } geometry.modify_geometry_sets([&](GeometrySet &geometry) { - if (!geometry.has_mesh()) { - return; - } - MeshComponent &mesh_component = geometry.get_component_for_write<MeshComponent>(); - switch (domain) { - case ATTR_DOMAIN_FACE: { - switch (scale_mode) { - case GEO_NODE_SCALE_ELEMENTS_UNIFORM: { - scale_faces_uniformly(mesh_component, {selection_field, scale_field, center_field}); - break; - } - case GEO_NODE_SCALE_ELEMENTS_SINGLE_AXIS: { - scale_faces_on_axis(mesh_component, - {selection_field, scale_field, center_field, axis_field}); - break; + if (Mesh *mesh = geometry.get_mesh_for_write()) { + switch (domain) { + case ATTR_DOMAIN_FACE: { + switch (scale_mode) { + case GEO_NODE_SCALE_ELEMENTS_UNIFORM: { + scale_faces_uniformly(*mesh, {selection_field, scale_field, center_field}); + break; + } + case GEO_NODE_SCALE_ELEMENTS_SINGLE_AXIS: { + scale_faces_on_axis(*mesh, {selection_field, scale_field, center_field, axis_field}); + break; + } } + break; } - break; - } - case ATTR_DOMAIN_EDGE: { - switch (scale_mode) { - case GEO_NODE_SCALE_ELEMENTS_UNIFORM: { - scale_edges_uniformly(mesh_component, {selection_field, scale_field, center_field}); - break; - } - case GEO_NODE_SCALE_ELEMENTS_SINGLE_AXIS: { - scale_edges_on_axis(mesh_component, - {selection_field, scale_field, center_field, axis_field}); - break; + case ATTR_DOMAIN_EDGE: { + switch (scale_mode) { + case GEO_NODE_SCALE_ELEMENTS_UNIFORM: { + scale_edges_uniformly(*mesh, {selection_field, scale_field, center_field}); + break; + } + case GEO_NODE_SCALE_ELEMENTS_SINGLE_AXIS: { + scale_edges_on_axis(*mesh, {selection_field, scale_field, center_field, axis_field}); + break; + } } + break; } - break; + default: + BLI_assert_unreachable(); + break; } - default: - BLI_assert_unreachable(); - break; } }); diff --git a/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc index 7156feb37d7..21fe724e194 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc @@ -21,9 +21,8 @@ static void node_declare(NodeDeclarationBuilder &b) static void scale_instances(GeoNodeExecParams ¶ms, InstancesComponent &instances_component) { - GeometryComponentFieldContext field_context{instances_component, ATTR_DOMAIN_INSTANCE}; - - fn::FieldEvaluator evaluator{field_context, instances_component.instances_num()}; + const bke::InstancesFieldContext context{instances_component}; + fn::FieldEvaluator evaluator{context, instances_component.instances_num()}; evaluator.set_selection(params.extract_input<Field<bool>>("Selection")); evaluator.add(params.extract_input<Field<float3>>("Scale")); evaluator.add(params.extract_input<Field<float3>>("Center")); diff --git a/source/blender/nodes/geometry/nodes/node_geo_self_object.cc b/source/blender/nodes/geometry/nodes/node_geo_self_object.cc new file mode 100644 index 00000000000..7b33afdb4a0 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_self_object.cc @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_self_object_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_output<decl::Object>(N_("Self Object")); +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + params.set_output("Self Object", const_cast<Object *>(params.self_object())); +} + +} // namespace blender::nodes::node_geo_self_object_cc + +void register_node_type_geo_self_object() +{ + namespace file_ns = blender::nodes::node_geo_self_object_cc; + + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_SELF_OBJECT, "Self Object", NODE_CLASS_INPUT); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.declare = file_ns::node_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc index fc3cb7006bb..e529ddddabe 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc @@ -68,19 +68,18 @@ static void update_handle_types_for_movement(int8_t &type, int8_t &other) } } -static void set_position_in_component(CurveComponent &component, +static void set_position_in_component(bke::CurvesGeometry &curves, const GeometryNodeCurveHandleMode mode, const Field<bool> &selection_field, const Field<float3> &position_field, const Field<float3> &offset_field) { - GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); - if (domain_size == 0) { + if (curves.points_num() == 0) { return; } - fn::FieldEvaluator evaluator{field_context, domain_size}; + bke::CurvesFieldContext field_context{curves, ATTR_DOMAIN_POINT}; + fn::FieldEvaluator evaluator{field_context, curves.points_num()}; evaluator.set_selection(selection_field); evaluator.add(position_field); evaluator.add(offset_field); @@ -89,9 +88,6 @@ static void set_position_in_component(CurveComponent &component, const VArray<float3> new_positions = evaluator.get_evaluated<float3>(0); const VArray<float3> new_offsets = evaluator.get_evaluated<float3>(1); - Curves &curves_id = *component.get_for_write(); - bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); - Span<float3> positions = curves.positions(); const bool use_left = mode == GEO_NODE_CURVE_HANDLE_LEFT; @@ -141,22 +137,17 @@ static void node_geo_exec(GeoNodeExecParams params) std::atomic<bool> has_bezier = false; geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (!geometry_set.has_curves()) { - return; - } - has_curves = true; - const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>(); - const AttributeAccessor attributes = *component.attributes(); - if (!attributes.contains("handle_left") || !attributes.contains("handle_right")) { - return; - } - has_bezier = true; + if (Curves *curves_id = geometry_set.get_curves_for_write()) { + bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry); + has_curves = true; + const AttributeAccessor attributes = curves.attributes(); + if (!attributes.contains("handle_left") || !attributes.contains("handle_right")) { + return; + } + has_bezier = true; - set_position_in_component(geometry_set.get_component_for_write<CurveComponent>(), - mode, - selection_field, - position_field, - offset_field); + set_position_in_component(curves, mode, selection_field, position_field, offset_field); + } }); if (has_curves && !has_bezier) { diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc index e4fae95b5a5..0d361090068 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "BKE_curves.hh" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_set_curve_radius_cc { @@ -16,21 +18,19 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Geometry>(N_("Curve")); } -static void set_radius_in_component(GeometryComponent &component, - const Field<bool> &selection_field, - const Field<float> &radius_field) +static void set_radius(bke::CurvesGeometry &curves, + const Field<bool> &selection_field, + const Field<float> &radius_field) { - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); - if (domain_size == 0) { + if (curves.points_num() == 0) { return; } - MutableAttributeAccessor attributes = *component.attributes_for_write(); - GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; - + MutableAttributeAccessor attributes = curves.attributes_for_write(); AttributeWriter<float> radii = attributes.lookup_or_add_for_write<float>("radius", ATTR_DOMAIN_POINT); - fn::FieldEvaluator evaluator{field_context, domain_size}; + bke::CurvesFieldContext field_context{curves, ATTR_DOMAIN_POINT}; + fn::FieldEvaluator evaluator{field_context, curves.points_num()}; evaluator.set_selection(selection_field); evaluator.add_with_destination(radius_field, radii.varray); evaluator.evaluate(); @@ -45,9 +45,8 @@ static void node_geo_exec(GeoNodeExecParams params) Field<float> radii_field = params.extract_input<Field<float>>("Radius"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (geometry_set.has_curves()) { - set_radius_in_component( - geometry_set.get_component_for_write<CurveComponent>(), selection_field, radii_field); + if (Curves *curves_id = geometry_set.get_curves_for_write()) { + set_radius(bke::CurvesGeometry::wrap(curves_id->geometry), selection_field, radii_field); } }); diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc index 2211ac62727..8c1fb883f46 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "BKE_curves.hh" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_set_curve_tilt_cc { @@ -12,22 +14,19 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Geometry>(N_("Curve")); } -static void set_tilt_in_component(GeometryComponent &component, - const Field<bool> &selection_field, - const Field<float> &tilt_field) +static void set_tilt(bke::CurvesGeometry &curves, + const Field<bool> &selection_field, + const Field<float> &tilt_field) { - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); - if (domain_size == 0) { + if (curves.points_num() == 0) { return; } - MutableAttributeAccessor attributes = *component.attributes_for_write(); - - GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; - + MutableAttributeAccessor attributes = curves.attributes_for_write(); AttributeWriter<float> tilts = attributes.lookup_or_add_for_write<float>("tilt", ATTR_DOMAIN_POINT); - fn::FieldEvaluator evaluator{field_context, domain_size}; + bke::CurvesFieldContext field_context{curves, ATTR_DOMAIN_POINT}; + fn::FieldEvaluator evaluator{field_context, curves.points_num()}; evaluator.set_selection(selection_field); evaluator.add_with_destination(tilt_field, tilts.varray); evaluator.evaluate(); @@ -42,9 +41,8 @@ static void node_geo_exec(GeoNodeExecParams params) Field<float> tilt_field = params.extract_input<Field<float>>("Tilt"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (geometry_set.has_curves()) { - set_tilt_in_component( - geometry_set.get_component_for_write<CurveComponent>(), selection_field, tilt_field); + if (Curves *curves_id = geometry_set.get_curves_for_write()) { + set_tilt(bke::CurvesGeometry::wrap(curves_id->geometry), selection_field, tilt_field); } }); diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_id.cc b/source/blender/nodes/geometry/nodes/node_geo_set_id.cc index fbb2ecbb799..5864401223b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_id.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_id.cc @@ -24,7 +24,7 @@ static void set_id_in_component(GeometryComponent &component, return; } MutableAttributeAccessor attributes = *component.attributes_for_write(); - GeometryComponentFieldContext field_context{component, domain}; + bke::GeometryFieldContext field_context{component, domain}; fn::FieldEvaluator evaluator{field_context, domain_size}; evaluator.set_selection(selection_field); diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_material.cc b/source/blender/nodes/geometry/nodes/node_geo_set_material.cc index 507c6e81b1f..8d00d82664b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_material.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_material.cc @@ -12,6 +12,7 @@ #include "DNA_volume_types.h" #include "BKE_material.h" +#include "BKE_mesh.h" namespace blender::nodes::node_geo_set_material_cc { @@ -49,11 +50,11 @@ static void assign_material_to_faces(Mesh &mesh, const IndexMask selection, Mate BKE_id_material_eval_assign(&mesh.id, new_material_index + 1, material); } - mesh.mpoly = (MPoly *)CustomData_duplicate_referenced_layer(&mesh.pdata, CD_MPOLY, mesh.totpoly); - for (const int i : selection) { - MPoly &poly = mesh.mpoly[i]; - poly.mat_nr = new_material_index; - } + MutableAttributeAccessor attributes = mesh.attributes_for_write(); + SpanAttributeWriter<int> material_indices = attributes.lookup_or_add_for_write_span<int>( + "material_index", ATTR_DOMAIN_FACE); + material_indices.span.fill_indices(selection, new_material_index); + material_indices.finish(); } static void node_geo_exec(GeoNodeExecParams params) @@ -72,8 +73,8 @@ static void node_geo_exec(GeoNodeExecParams params) if (geometry_set.has_mesh()) { MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>(); Mesh &mesh = *mesh_component.get_for_write(); - GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_FACE}; + bke::MeshFieldContext field_context{mesh, ATTR_DOMAIN_FACE}; fn::FieldEvaluator selection_evaluator{field_context, mesh.totpoly}; selection_evaluator.add(selection_field); selection_evaluator.evaluate(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc b/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc index 0dc89bb7ef4..bb9ac9b5d4c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc @@ -14,21 +14,23 @@ static void node_declare(NodeDeclarationBuilder &b) static void set_material_index_in_component(GeometryComponent &component, const Field<bool> &selection_field, - const Field<int> &index_field) + const Field<int> &index_field, + const eAttrDomain domain) { - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE); + const int domain_size = component.attribute_domain_size(domain); if (domain_size == 0) { return; } MutableAttributeAccessor attributes = *component.attributes_for_write(); - GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_FACE}; + bke::GeometryFieldContext field_context{component, domain}; - AttributeWriter<int> indices = attributes.lookup_or_add_for_write<int>("material_index", - ATTR_DOMAIN_FACE); + const bke::AttributeValidator validator = attributes.lookup_validator("material_index"); + AttributeWriter<int> indices = attributes.lookup_or_add_for_write<int>("material_index", domain); fn::FieldEvaluator evaluator{field_context, domain_size}; evaluator.set_selection(selection_field); - evaluator.add_with_destination(index_field, indices.varray); + evaluator.add_with_destination(validator.validate_field_if_necessary(index_field), + indices.varray); evaluator.evaluate(); indices.finish(); } @@ -41,8 +43,10 @@ static void node_geo_exec(GeoNodeExecParams params) geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { if (geometry_set.has_mesh()) { - set_material_index_in_component( - geometry_set.get_component_for_write<MeshComponent>(), selection_field, index_field); + set_material_index_in_component(geometry_set.get_component_for_write<MeshComponent>(), + selection_field, + index_field, + ATTR_DOMAIN_FACE); } }); params.set_output("Geometry", std::move(geometry_set)); diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc b/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc index da7977a4fb4..28d07b31218 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "DNA_pointcloud_types.h" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_set_point_radius_cc { @@ -16,21 +18,19 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Geometry>(N_("Points")); } -static void set_radius_in_component(GeometryComponent &component, +static void set_radius_in_component(PointCloud &pointcloud, const Field<bool> &selection_field, const Field<float> &radius_field) { - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); - if (domain_size == 0) { + if (pointcloud.totpoint == 0) { return; } - MutableAttributeAccessor attributes = *component.attributes_for_write(); - GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; - + MutableAttributeAccessor attributes = pointcloud.attributes_for_write(); AttributeWriter<float> radii = attributes.lookup_or_add_for_write<float>("radius", ATTR_DOMAIN_POINT); - fn::FieldEvaluator evaluator{field_context, domain_size}; + bke::PointCloudFieldContext field_context{pointcloud}; + fn::FieldEvaluator evaluator{field_context, pointcloud.totpoint}; evaluator.set_selection(selection_field); evaluator.add_with_destination(radius_field, radii.varray); evaluator.evaluate(); @@ -45,10 +45,8 @@ static void node_geo_exec(GeoNodeExecParams params) Field<float> radii_field = params.extract_input<Field<float>>("Radius"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (geometry_set.has_pointcloud()) { - set_radius_in_component(geometry_set.get_component_for_write<PointCloudComponent>(), - selection_field, - radii_field); + if (PointCloud *pointcloud = geometry_set.get_pointcloud_for_write()) { + set_radius_in_component(*pointcloud, selection_field, radii_field); } }); diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc index 880252de4fa..613f140ff0a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc @@ -8,6 +8,7 @@ #include "DNA_meshdata_types.h" #include "BKE_curves.hh" +#include "BKE_mesh.h" #include "node_geometry_util.hh" @@ -35,14 +36,14 @@ static void set_computed_position_and_offset(GeometryComponent &component, switch (component.type()) { case GEO_COMPONENT_TYPE_MESH: { Mesh *mesh = static_cast<MeshComponent &>(component).get_for_write(); - MutableSpan<MVert> mverts{mesh->mvert, mesh->totvert}; + MutableSpan<MVert> verts = mesh->verts_for_write(); if (in_positions.is_same(positions.varray)) { devirtualize_varray(in_offsets, [&](const auto in_offsets) { threading::parallel_for( selection.index_range(), grain_size, [&](const IndexRange range) { for (const int i : selection.slice(range)) { const float3 offset = in_offsets[i]; - add_v3_v3(mverts[i].co, offset); + add_v3_v3(verts[i].co, offset); } }); }); @@ -54,7 +55,7 @@ static void set_computed_position_and_offset(GeometryComponent &component, selection.index_range(), grain_size, [&](const IndexRange range) { for (const int i : selection.slice(range)) { const float3 new_position = in_positions[i] + in_offsets[i]; - copy_v3_v3(mverts[i].co, new_position); + copy_v3_v3(verts[i].co, new_position); } }); }); @@ -136,7 +137,7 @@ static void set_position_in_component(GeometryComponent &component, { eAttrDomain domain = component.type() == GEO_COMPONENT_TYPE_INSTANCES ? ATTR_DOMAIN_INSTANCE : ATTR_DOMAIN_POINT; - GeometryComponentFieldContext field_context{component, domain}; + bke::GeometryFieldContext field_context{component, domain}; const int domain_size = component.attribute_domain_size(domain); if (domain_size == 0) { return; diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc b/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc index e0cf0f98d58..0df51e49827 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "DNA_mesh_types.h" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_set_shade_smooth_cc { @@ -12,27 +14,25 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Geometry>(N_("Geometry")); } -static void set_smooth_in_component(GeometryComponent &component, - const Field<bool> &selection_field, - const Field<bool> &shade_field) +static void set_smooth(Mesh &mesh, + const Field<bool> &selection_field, + const Field<bool> &shade_field) { - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE); - if (domain_size == 0) { + if (mesh.totpoly == 0) { return; } - MutableAttributeAccessor attributes = *component.attributes_for_write(); - - GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_FACE}; - AttributeWriter<bool> shades = attributes.lookup_or_add_for_write<bool>("shade_smooth", + MutableAttributeAccessor attributes = mesh.attributes_for_write(); + AttributeWriter<bool> smooth = attributes.lookup_or_add_for_write<bool>("shade_smooth", ATTR_DOMAIN_FACE); - fn::FieldEvaluator evaluator{field_context, domain_size}; + bke::MeshFieldContext field_context{mesh, ATTR_DOMAIN_FACE}; + fn::FieldEvaluator evaluator{field_context, mesh.totpoly}; evaluator.set_selection(selection_field); - evaluator.add_with_destination(shade_field, shades.varray); + evaluator.add_with_destination(shade_field, smooth.varray); evaluator.evaluate(); - shades.finish(); + smooth.finish(); } static void node_geo_exec(GeoNodeExecParams params) @@ -42,9 +42,8 @@ static void node_geo_exec(GeoNodeExecParams params) Field<bool> shade_field = params.extract_input<Field<bool>>("Shade Smooth"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (geometry_set.has_mesh()) { - set_smooth_in_component( - geometry_set.get_component_for_write<MeshComponent>(), selection_field, shade_field); + if (Mesh *mesh = geometry_set.get_mesh_for_write()) { + set_smooth(*mesh, selection_field, shade_field); } }); params.set_output("Geometry", std::move(geometry_set)); diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc b/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc index a35d8d66558..d8faa154477 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "BKE_curves.hh" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_set_spline_cyclic_cc { @@ -12,22 +14,19 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Geometry>(N_("Geometry")); } -static void set_cyclic_in_component(GeometryComponent &component, - const Field<bool> &selection_field, - const Field<bool> &cyclic_field) +static void set_cyclic(bke::CurvesGeometry &curves, + const Field<bool> &selection_field, + const Field<bool> &cyclic_field) { - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE); - if (domain_size == 0) { + if (curves.curves_num() == 0) { return; } - MutableAttributeAccessor attributes = *component.attributes_for_write(); - - GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE}; - + MutableAttributeAccessor attributes = curves.attributes_for_write(); AttributeWriter<bool> cyclics = attributes.lookup_or_add_for_write<bool>("cyclic", ATTR_DOMAIN_CURVE); - fn::FieldEvaluator evaluator{field_context, domain_size}; + bke::CurvesFieldContext field_context{curves, ATTR_DOMAIN_CURVE}; + fn::FieldEvaluator evaluator{field_context, curves.curves_num()}; evaluator.set_selection(selection_field); evaluator.add_with_destination(cyclic_field, cyclics.varray); evaluator.evaluate(); @@ -42,9 +41,8 @@ static void node_geo_exec(GeoNodeExecParams params) Field<bool> cyclic_field = params.extract_input<Field<bool>>("Cyclic"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (geometry_set.has_curves()) { - set_cyclic_in_component( - geometry_set.get_component_for_write<CurveComponent>(), selection_field, cyclic_field); + if (Curves *curves_id = geometry_set.get_curves_for_write()) { + set_cyclic(bke::CurvesGeometry::wrap(curves_id->geometry), selection_field, cyclic_field); } }); diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc b/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc index fcebc1116d7..d46ceac92ba 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "BKE_curves.hh" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_set_spline_resolution_cc { @@ -12,22 +14,19 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Geometry>(N_("Geometry")); } -static void set_resolution_in_component(GeometryComponent &component, - const Field<bool> &selection_field, - const Field<int> &resolution_field) +static void set_resolution(bke::CurvesGeometry &curves, + const Field<bool> &selection_field, + const Field<int> &resolution_field) { - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE); - if (domain_size == 0) { + if (curves.curves_num() == 0) { return; } - MutableAttributeAccessor attributes = *component.attributes_for_write(); - - GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE}; - + MutableAttributeAccessor attributes = curves.attributes_for_write(); AttributeWriter<int> resolutions = attributes.lookup_or_add_for_write<int>("resolution", ATTR_DOMAIN_CURVE); - fn::FieldEvaluator evaluator{field_context, domain_size}; + bke::CurvesFieldContext field_context{curves, ATTR_DOMAIN_CURVE}; + fn::FieldEvaluator evaluator{field_context, curves.curves_num()}; evaluator.set_selection(selection_field); evaluator.add_with_destination(resolution_field, resolutions.varray); evaluator.evaluate(); @@ -38,12 +37,13 @@ static void set_resolution_in_component(GeometryComponent &component, static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - Field<bool> selection_field = params.extract_input<Field<bool>>("Selection"); - Field<int> resolution_field = params.extract_input<Field<int>>("Resolution"); + Field<bool> selection = params.extract_input<Field<bool>>("Selection"); + Field<int> resolution = params.extract_input<Field<int>>("Resolution"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - set_resolution_in_component( - geometry_set.get_component_for_write<CurveComponent>(), selection_field, resolution_field); + if (Curves *curves_id = geometry_set.get_curves_for_write()) { + set_resolution(bke::CurvesGeometry::wrap(curves_id->geometry), selection, resolution); + } }); params.set_output("Geometry", std::move(geometry_set)); diff --git a/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc index 70c33ad6a96..37e55602fb0 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc @@ -71,6 +71,7 @@ static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) { const NodeDeclaration &declaration = *params.node_type().fixed_declaration; search_link_ops_for_declarations(params, declaration.inputs().take_front(2)); + search_link_ops_for_declarations(params, declaration.outputs().take_front(1)); if (params.in_out() == SOCK_IN) { const std::optional<eCustomDataType> type = node_data_type_to_custom_data_type( @@ -98,11 +99,12 @@ static void try_capture_field_on_geometry(GeometryComponent &component, return; } - GeometryComponentFieldContext field_context{component, domain}; + bke::GeometryFieldContext field_context{component, domain}; const IndexMask mask{IndexMask(domain_size)}; const CPPType &type = field.cpp_type(); const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(type); + const bke::AttributeValidator validator = attributes.lookup_validator(name); /* Could avoid allocating a new buffer if: * - We are writing to an attribute that exists already with the correct domain and type. @@ -110,7 +112,8 @@ static void try_capture_field_on_geometry(GeometryComponent &component, void *buffer = MEM_mallocN(type.size() * domain_size, __func__); fn::FieldEvaluator evaluator{field_context, &mask}; - evaluator.add_with_destination(field, GMutableSpan{type, buffer, domain_size}); + evaluator.add_with_destination(validator.validate_field_if_necessary(field), + GMutableSpan{type, buffer, domain_size}); evaluator.evaluate(); if (GAttributeWriter attribute = attributes.lookup_for_write(name)) { @@ -123,7 +126,7 @@ static void try_capture_field_on_geometry(GeometryComponent &component, } } attributes.remove(name); - if (attributes.add(name, domain, data_type, bke::AttributeInitMove{buffer})) { + if (attributes.add(name, domain, data_type, bke::AttributeInitMoveArray{buffer})) { return; } @@ -149,7 +152,7 @@ static void node_geo_exec(GeoNodeExecParams params) return; } - params.used_named_attribute(name, eNamedAttrUsage::Write); + params.used_named_attribute(name, NamedAttributeUsage::Write); const NodeGeometryStoreNamedAttribute &storage = node_storage(params.node()); const eCustomDataType data_type = static_cast<eCustomDataType>(storage.data_type); diff --git a/source/blender/nodes/geometry/nodes/node_geo_string_join.cc b/source/blender/nodes/geometry/nodes/node_geo_string_join.cc index bb33430a02f..09c01b8c627 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_string_join.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_string_join.cc @@ -13,12 +13,13 @@ static void node_declare(NodeDeclarationBuilder &b) static void node_geo_exec(GeoNodeExecParams params) { - Vector<std::string> strings = params.extract_multi_input<std::string>("Strings"); + Vector<fn::ValueOrField<std::string>> strings = + params.extract_input<Vector<fn::ValueOrField<std::string>>>("Strings"); const std::string delim = params.extract_input<std::string>("Delimiter"); std::string output; for (const int i : strings.index_range()) { - output += strings[i]; + output += strings[i].as_value(); if (i < (strings.size() - 1)) { output += delim; } diff --git a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc index eda6a51d412..60c8a89a6bf 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc @@ -73,7 +73,7 @@ static void write_vertex_creases(Mesh &mesh, const VArray<float> &crease_varray) } else { crease = static_cast<float *>( - CustomData_add_layer(&mesh.vdata, CD_CREASE, CD_DEFAULT, nullptr, mesh.totvert)); + CustomData_add_layer(&mesh.vdata, CD_CREASE, CD_CONSTRUCT, nullptr, mesh.totvert)); } materialize_and_clamp_creases(crease_varray, {crease, mesh.totvert}); } @@ -119,21 +119,19 @@ static void node_geo_exec(GeoNodeExecParams params) return; } - const MeshComponent &mesh_component = *geometry_set.get_component_for_read<MeshComponent>(); - const int verts_num = mesh_component.attribute_domain_size(ATTR_DOMAIN_POINT); - const int edges_num = mesh_component.attribute_domain_size(ATTR_DOMAIN_EDGE); - if (verts_num == 0 || edges_num == 0) { + const Mesh &mesh = *geometry_set.get_mesh_for_read(); + if (mesh.totvert == 0 || mesh.totedge == 0) { return; } - GeometryComponentFieldContext point_context{mesh_component, ATTR_DOMAIN_POINT}; - FieldEvaluator point_evaluator(point_context, verts_num); + bke::MeshFieldContext point_context{mesh, ATTR_DOMAIN_POINT}; + FieldEvaluator point_evaluator(point_context, mesh.totvert); point_evaluator.add(vertex_crease_field); point_evaluator.evaluate(); const VArray<float> vertex_creases = point_evaluator.get_evaluated<float>(0); - GeometryComponentFieldContext edge_context{mesh_component, ATTR_DOMAIN_EDGE}; - FieldEvaluator edge_evaluator(edge_context, edges_num); + bke::MeshFieldContext edge_context{mesh, ATTR_DOMAIN_EDGE}; + FieldEvaluator edge_evaluator(edge_context, mesh.totedge); edge_evaluator.add(edge_crease_field); edge_evaluator.evaluate(); const VArray<float> edge_creases = edge_evaluator.get_evaluated<float>(0); @@ -162,17 +160,15 @@ static void node_geo_exec(GeoNodeExecParams params) subdiv_settings.fvar_linear_interpolation = BKE_subdiv_fvar_interpolation_from_uv_smooth( uv_smooth); - const Mesh &mesh_in = *geometry_set.get_mesh_for_read(); - /* Apply subdivision to mesh. */ - Subdiv *subdiv = BKE_subdiv_update_from_mesh(nullptr, &subdiv_settings, &mesh_in); + Subdiv *subdiv = BKE_subdiv_update_from_mesh(nullptr, &subdiv_settings, &mesh); /* In case of bad topology, skip to input mesh. */ if (subdiv == nullptr) { return; } - Mesh *mesh_out = BKE_subdiv_to_mesh(subdiv, &mesh_settings, &mesh_in); + Mesh *mesh_out = BKE_subdiv_to_mesh(subdiv, &mesh_settings, &mesh); geometry_set.replace_mesh(mesh_out); diff --git a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc deleted file mode 100644 index cd75822f665..00000000000 --- a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc +++ /dev/null @@ -1,826 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "BLI_generic_array.hh" -#include "BLI_kdopbvh.h" -#include "BLI_task.hh" - -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" -#include "DNA_pointcloud_types.h" - -#include "BKE_attribute_math.hh" -#include "BKE_bvhutils.h" -#include "BKE_mesh_runtime.h" -#include "BKE_mesh_sample.hh" - -#include "UI_interface.h" -#include "UI_resources.h" - -#include "NOD_socket_search_link.hh" - -#include "node_geometry_util.hh" - -namespace blender::nodes::node_geo_transfer_attribute_cc { - -using namespace blender::bke::mesh_surface_sample; - -NODE_STORAGE_FUNCS(NodeGeometryTransferAttribute) - -static void node_declare(NodeDeclarationBuilder &b) -{ - b.add_input<decl::Geometry>(N_("Source")) - .supported_type({GEO_COMPONENT_TYPE_MESH, - GEO_COMPONENT_TYPE_POINT_CLOUD, - GEO_COMPONENT_TYPE_CURVE, - GEO_COMPONENT_TYPE_INSTANCES}); - - b.add_input<decl::Vector>(N_("Attribute")).hide_value().supports_field(); - b.add_input<decl::Float>(N_("Attribute"), "Attribute_001").hide_value().supports_field(); - b.add_input<decl::Color>(N_("Attribute"), "Attribute_002").hide_value().supports_field(); - b.add_input<decl::Bool>(N_("Attribute"), "Attribute_003").hide_value().supports_field(); - b.add_input<decl::Int>(N_("Attribute"), "Attribute_004").hide_value().supports_field(); - - b.add_input<decl::Vector>(N_("Source Position")) - .implicit_field() - .make_available([](bNode &node) { - node_storage(node).mode = GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED; - }); - b.add_input<decl::Int>(N_("Index")).implicit_field().make_available([](bNode &node) { - node_storage(node).mode = GEO_NODE_ATTRIBUTE_TRANSFER_INDEX; - }); - - b.add_output<decl::Vector>(N_("Attribute")).dependent_field({6, 7}); - b.add_output<decl::Float>(N_("Attribute"), "Attribute_001").dependent_field({6, 7}); - b.add_output<decl::Color>(N_("Attribute"), "Attribute_002").dependent_field({6, 7}); - b.add_output<decl::Bool>(N_("Attribute"), "Attribute_003").dependent_field({6, 7}); - b.add_output<decl::Int>(N_("Attribute"), "Attribute_004").dependent_field({6, 7}); -} - -static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - const bNode &node = *static_cast<const bNode *>(ptr->data); - const NodeGeometryTransferAttribute &storage = node_storage(node); - const GeometryNodeAttributeTransferMode mapping = (GeometryNodeAttributeTransferMode) - storage.mode; - - uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); - uiItemR(layout, ptr, "mapping", 0, "", ICON_NONE); - if (mapping != GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED) { - uiItemR(layout, ptr, "domain", 0, "", ICON_NONE); - } -} - -static void node_init(bNodeTree *UNUSED(tree), bNode *node) -{ - NodeGeometryTransferAttribute *data = MEM_cnew<NodeGeometryTransferAttribute>(__func__); - data->data_type = CD_PROP_FLOAT; - data->mode = GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED; - node->storage = data; -} - -static void node_update(bNodeTree *ntree, bNode *node) -{ - const NodeGeometryTransferAttribute &storage = node_storage(*node); - const eCustomDataType data_type = static_cast<eCustomDataType>(storage.data_type); - const GeometryNodeAttributeTransferMode mapping = (GeometryNodeAttributeTransferMode) - storage.mode; - - bNodeSocket *socket_geometry = (bNodeSocket *)node->inputs.first; - bNodeSocket *socket_vector = socket_geometry->next; - bNodeSocket *socket_float = socket_vector->next; - bNodeSocket *socket_color4f = socket_float->next; - bNodeSocket *socket_boolean = socket_color4f->next; - bNodeSocket *socket_int32 = socket_boolean->next; - - bNodeSocket *socket_positions = socket_int32->next; - bNodeSocket *socket_indices = socket_positions->next; - - nodeSetSocketAvailability(ntree, socket_vector, data_type == CD_PROP_FLOAT3); - nodeSetSocketAvailability(ntree, socket_float, data_type == CD_PROP_FLOAT); - nodeSetSocketAvailability(ntree, socket_color4f, data_type == CD_PROP_COLOR); - nodeSetSocketAvailability(ntree, socket_boolean, data_type == CD_PROP_BOOL); - nodeSetSocketAvailability(ntree, socket_int32, data_type == CD_PROP_INT32); - - nodeSetSocketAvailability(ntree, socket_positions, mapping != GEO_NODE_ATTRIBUTE_TRANSFER_INDEX); - nodeSetSocketAvailability(ntree, socket_indices, mapping == GEO_NODE_ATTRIBUTE_TRANSFER_INDEX); - - bNodeSocket *out_socket_vector = (bNodeSocket *)node->outputs.first; - bNodeSocket *out_socket_float = out_socket_vector->next; - bNodeSocket *out_socket_color4f = out_socket_float->next; - bNodeSocket *out_socket_boolean = out_socket_color4f->next; - bNodeSocket *out_socket_int32 = out_socket_boolean->next; - - nodeSetSocketAvailability(ntree, out_socket_vector, data_type == CD_PROP_FLOAT3); - nodeSetSocketAvailability(ntree, out_socket_float, data_type == CD_PROP_FLOAT); - nodeSetSocketAvailability(ntree, out_socket_color4f, data_type == CD_PROP_COLOR); - nodeSetSocketAvailability(ntree, out_socket_boolean, data_type == CD_PROP_BOOL); - nodeSetSocketAvailability(ntree, out_socket_int32, data_type == CD_PROP_INT32); -} - -static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) -{ - const NodeDeclaration &declaration = *params.node_type().fixed_declaration; - search_link_ops_for_declarations(params, declaration.inputs().take_back(2)); - search_link_ops_for_declarations(params, declaration.inputs().take_front(1)); - - const std::optional<eCustomDataType> type = node_data_type_to_custom_data_type( - (eNodeSocketDatatype)params.other_socket().type); - if (type && *type != CD_PROP_STRING) { - /* The input and output sockets have the same name. */ - params.add_item(IFACE_("Attribute"), [type](LinkSearchOpParams ¶ms) { - bNode &node = params.add_node("GeometryNodeAttributeTransfer"); - node_storage(node).data_type = *type; - params.update_and_connect_available_socket(node, "Attribute"); - }); - } -} - -static void get_closest_in_bvhtree(BVHTreeFromMesh &tree_data, - const VArray<float3> &positions, - const IndexMask mask, - const MutableSpan<int> r_indices, - const MutableSpan<float> r_distances_sq, - const MutableSpan<float3> r_positions) -{ - BLI_assert(positions.size() >= r_indices.size()); - BLI_assert(positions.size() >= r_distances_sq.size()); - BLI_assert(positions.size() >= r_positions.size()); - - for (const int i : mask) { - BVHTreeNearest nearest; - nearest.dist_sq = FLT_MAX; - const float3 position = positions[i]; - BLI_bvhtree_find_nearest( - tree_data.tree, position, &nearest, tree_data.nearest_callback, &tree_data); - if (!r_indices.is_empty()) { - r_indices[i] = nearest.index; - } - if (!r_distances_sq.is_empty()) { - r_distances_sq[i] = nearest.dist_sq; - } - if (!r_positions.is_empty()) { - r_positions[i] = nearest.co; - } - } -} - -static void get_closest_pointcloud_points(const PointCloud &pointcloud, - const VArray<float3> &positions, - const IndexMask mask, - const MutableSpan<int> r_indices, - const MutableSpan<float> r_distances_sq) -{ - BLI_assert(positions.size() >= r_indices.size()); - BLI_assert(pointcloud.totpoint > 0); - - BVHTreeFromPointCloud tree_data; - BKE_bvhtree_from_pointcloud_get(&tree_data, &pointcloud, 2); - - for (const int i : mask) { - BVHTreeNearest nearest; - nearest.dist_sq = FLT_MAX; - const float3 position = positions[i]; - BLI_bvhtree_find_nearest( - tree_data.tree, position, &nearest, tree_data.nearest_callback, &tree_data); - r_indices[i] = nearest.index; - if (!r_distances_sq.is_empty()) { - r_distances_sq[i] = nearest.dist_sq; - } - } - - free_bvhtree_from_pointcloud(&tree_data); -} - -static void get_closest_mesh_points(const Mesh &mesh, - const VArray<float3> &positions, - const IndexMask mask, - const MutableSpan<int> r_point_indices, - const MutableSpan<float> r_distances_sq, - const MutableSpan<float3> r_positions) -{ - BLI_assert(mesh.totvert > 0); - BVHTreeFromMesh tree_data; - BKE_bvhtree_from_mesh_get(&tree_data, &mesh, BVHTREE_FROM_VERTS, 2); - get_closest_in_bvhtree(tree_data, positions, mask, r_point_indices, r_distances_sq, r_positions); - free_bvhtree_from_mesh(&tree_data); -} - -static void get_closest_mesh_edges(const Mesh &mesh, - const VArray<float3> &positions, - const IndexMask mask, - const MutableSpan<int> r_edge_indices, - const MutableSpan<float> r_distances_sq, - const MutableSpan<float3> r_positions) -{ - BLI_assert(mesh.totedge > 0); - BVHTreeFromMesh tree_data; - BKE_bvhtree_from_mesh_get(&tree_data, &mesh, BVHTREE_FROM_EDGES, 2); - get_closest_in_bvhtree(tree_data, positions, mask, r_edge_indices, r_distances_sq, r_positions); - free_bvhtree_from_mesh(&tree_data); -} - -static void get_closest_mesh_looptris(const Mesh &mesh, - const VArray<float3> &positions, - const IndexMask mask, - const MutableSpan<int> r_looptri_indices, - const MutableSpan<float> r_distances_sq, - const MutableSpan<float3> r_positions) -{ - BLI_assert(mesh.totpoly > 0); - BVHTreeFromMesh tree_data; - BKE_bvhtree_from_mesh_get(&tree_data, &mesh, BVHTREE_FROM_LOOPTRI, 2); - get_closest_in_bvhtree( - tree_data, positions, mask, r_looptri_indices, r_distances_sq, r_positions); - free_bvhtree_from_mesh(&tree_data); -} - -static void get_closest_mesh_polygons(const Mesh &mesh, - const VArray<float3> &positions, - const IndexMask mask, - const MutableSpan<int> r_poly_indices, - const MutableSpan<float> r_distances_sq, - const MutableSpan<float3> r_positions) -{ - BLI_assert(mesh.totpoly > 0); - - Array<int> looptri_indices(positions.size()); - get_closest_mesh_looptris(mesh, positions, mask, looptri_indices, r_distances_sq, r_positions); - - const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&mesh), - BKE_mesh_runtime_looptri_len(&mesh)}; - - for (const int i : mask) { - const MLoopTri &looptri = looptris[looptri_indices[i]]; - r_poly_indices[i] = looptri.poly; - } -} - -/* The closest corner is defined to be the closest corner on the closest face. */ -static void get_closest_mesh_corners(const Mesh &mesh, - const VArray<float3> &positions, - const IndexMask mask, - const MutableSpan<int> r_corner_indices, - const MutableSpan<float> r_distances_sq, - const MutableSpan<float3> r_positions) -{ - BLI_assert(mesh.totloop > 0); - Array<int> poly_indices(positions.size()); - get_closest_mesh_polygons(mesh, positions, mask, poly_indices, {}, {}); - - for (const int i : mask) { - const float3 position = positions[i]; - const int poly_index = poly_indices[i]; - const MPoly &poly = mesh.mpoly[poly_index]; - - /* Find the closest vertex in the polygon. */ - float min_distance_sq = FLT_MAX; - const MVert *closest_mvert; - int closest_loop_index = 0; - for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { - const MLoop &loop = mesh.mloop[loop_index]; - const int vertex_index = loop.v; - const MVert &mvert = mesh.mvert[vertex_index]; - const float distance_sq = math::distance_squared(position, float3(mvert.co)); - if (distance_sq < min_distance_sq) { - min_distance_sq = distance_sq; - closest_loop_index = loop_index; - closest_mvert = &mvert; - } - } - if (!r_corner_indices.is_empty()) { - r_corner_indices[i] = closest_loop_index; - } - if (!r_positions.is_empty()) { - r_positions[i] = closest_mvert->co; - } - if (!r_distances_sq.is_empty()) { - r_distances_sq[i] = min_distance_sq; - } - } -} - -template<typename T> -void copy_with_indices(const VArray<T> &src, - const IndexMask mask, - const Span<int> indices, - const MutableSpan<T> dst) -{ - if (src.is_empty()) { - return; - } - for (const int i : mask) { - dst[i] = src[indices[i]]; - } -} - -template<typename T> -void copy_with_indices_clamped(const VArray<T> &src, - const IndexMask mask, - const VArray<int> &indices, - const MutableSpan<T> dst) -{ - if (src.is_empty()) { - return; - } - const int max_index = src.size() - 1; - threading::parallel_for(mask.index_range(), 4096, [&](IndexRange range) { - for (const int i : range) { - const int index = mask[i]; - dst[index] = src[std::clamp(indices[index], 0, max_index)]; - } - }); -} - -template<typename T> -void copy_with_indices_and_comparison(const VArray<T> &src_1, - const VArray<T> &src_2, - const Span<float> distances_1, - const Span<float> distances_2, - const IndexMask mask, - const Span<int> indices_1, - const Span<int> indices_2, - const MutableSpan<T> dst) -{ - if (src_1.is_empty() || src_2.is_empty()) { - return; - } - for (const int i : mask) { - if (distances_1[i] < distances_2[i]) { - dst[i] = src_1[indices_1[i]]; - } - else { - dst[i] = src_2[indices_2[i]]; - } - } -} - -static bool component_is_available(const GeometrySet &geometry, - const GeometryComponentType type, - const eAttrDomain domain) -{ - if (!geometry.has(type)) { - return false; - } - const GeometryComponent &component = *geometry.get_component_for_read(type); - if (component.is_empty()) { - return false; - } - return component.attribute_domain_size(domain) != 0; -} - -/** - * \note Multi-threading for this function is provided by the field evaluator. Since the #call - * function could be called many times, calculate the data from the source geometry once and store - * it for later. - */ -class NearestInterpolatedTransferFunction : public fn::MultiFunction { - GeometrySet source_; - GField src_field_; - - /** - * This function is meant to sample the surface of a mesh rather than take the value from - * individual elements, so use the most complex domain, ensuring no information is lost. In the - * future, it should be possible to use the most complex domain required by the field inputs, to - * simplify sampling and avoid domain conversions. - */ - eAttrDomain domain_ = ATTR_DOMAIN_CORNER; - - fn::MFSignature signature_; - - std::optional<GeometryComponentFieldContext> source_context_; - std::unique_ptr<FieldEvaluator> source_evaluator_; - const GVArray *source_data_; - - public: - NearestInterpolatedTransferFunction(GeometrySet geometry, GField src_field) - : source_(std::move(geometry)), src_field_(std::move(src_field)) - { - source_.ensure_owns_direct_data(); - signature_ = this->create_signature(); - this->set_signature(&signature_); - this->evaluate_source_field(); - } - - fn::MFSignature create_signature() - { - blender::fn::MFSignatureBuilder signature{"Attribute Transfer Nearest Interpolated"}; - signature.single_input<float3>("Position"); - signature.single_output("Attribute", src_field_.cpp_type()); - return signature.build(); - } - - void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override - { - const VArray<float3> &positions = params.readonly_single_input<float3>(0, "Position"); - GMutableSpan dst = params.uninitialized_single_output_if_required(1, "Attribute"); - - const MeshComponent &mesh_component = *source_.get_component_for_read<MeshComponent>(); - BLI_assert(mesh_component.has_mesh()); - const Mesh &mesh = *mesh_component.get_for_read(); - BLI_assert(mesh.totpoly > 0); - - /* Find closest points on the mesh surface. */ - Array<int> looptri_indices(mask.min_array_size()); - Array<float3> sampled_positions(mask.min_array_size()); - get_closest_mesh_looptris(mesh, positions, mask, looptri_indices, {}, sampled_positions); - - MeshAttributeInterpolator interp(&mesh, mask, sampled_positions, looptri_indices); - interp.sample_data(*source_data_, domain_, eAttributeMapMode::INTERPOLATED, dst); - } - - private: - void evaluate_source_field() - { - const MeshComponent &mesh_component = *source_.get_component_for_read<MeshComponent>(); - source_context_.emplace(GeometryComponentFieldContext{mesh_component, domain_}); - const int domain_num = mesh_component.attribute_domain_size(domain_); - source_evaluator_ = std::make_unique<FieldEvaluator>(*source_context_, domain_num); - source_evaluator_->add(src_field_); - source_evaluator_->evaluate(); - source_data_ = &source_evaluator_->get_evaluated(0); - } -}; - -/** - * \note Multi-threading for this function is provided by the field evaluator. Since the #call - * function could be called many times, calculate the data from the source geometry once and store - * it for later. - */ -class NearestTransferFunction : public fn::MultiFunction { - GeometrySet source_; - GField src_field_; - eAttrDomain domain_; - - fn::MFSignature signature_; - - bool use_mesh_; - bool use_points_; - - /* Store data from the source as a virtual array, since we may only access a few indices. */ - std::optional<GeometryComponentFieldContext> mesh_context_; - std::unique_ptr<FieldEvaluator> mesh_evaluator_; - const GVArray *mesh_data_; - - std::optional<GeometryComponentFieldContext> point_context_; - std::unique_ptr<FieldEvaluator> point_evaluator_; - const GVArray *point_data_; - - public: - NearestTransferFunction(GeometrySet geometry, GField src_field, eAttrDomain domain) - : source_(std::move(geometry)), src_field_(std::move(src_field)), domain_(domain) - { - source_.ensure_owns_direct_data(); - signature_ = this->create_signature(); - this->set_signature(&signature_); - - this->use_mesh_ = component_is_available(source_, GEO_COMPONENT_TYPE_MESH, domain_); - this->use_points_ = component_is_available(source_, GEO_COMPONENT_TYPE_POINT_CLOUD, domain_); - - this->evaluate_source_field(); - } - - fn::MFSignature create_signature() - { - blender::fn::MFSignatureBuilder signature{"Attribute Transfer Nearest"}; - signature.single_input<float3>("Position"); - signature.single_output("Attribute", src_field_.cpp_type()); - return signature.build(); - } - - void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override - { - const VArray<float3> &positions = params.readonly_single_input<float3>(0, "Position"); - GMutableSpan dst = params.uninitialized_single_output_if_required(1, "Attribute"); - - if (!use_mesh_ && !use_points_) { - dst.type().value_initialize_indices(dst.data(), mask); - return; - } - - const Mesh *mesh = use_mesh_ ? source_.get_mesh_for_read() : nullptr; - const PointCloud *pointcloud = use_points_ ? source_.get_pointcloud_for_read() : nullptr; - - const int tot_samples = mask.min_array_size(); - - Array<int> point_indices; - Array<float> point_distances; - - /* Depending on where what domain the source attribute lives, these indices are either vertex, - * corner, edge or polygon indices. */ - Array<int> mesh_indices; - Array<float> mesh_distances; - - /* If there is a point cloud, find the closest points. */ - if (use_points_) { - point_indices.reinitialize(tot_samples); - if (use_mesh_) { - point_distances.reinitialize(tot_samples); - } - get_closest_pointcloud_points(*pointcloud, positions, mask, point_indices, point_distances); - } - - /* If there is a mesh, find the closest mesh elements. */ - if (use_mesh_) { - mesh_indices.reinitialize(tot_samples); - if (use_points_) { - mesh_distances.reinitialize(tot_samples); - } - switch (domain_) { - case ATTR_DOMAIN_POINT: { - get_closest_mesh_points(*mesh, positions, mask, mesh_indices, mesh_distances, {}); - break; - } - case ATTR_DOMAIN_EDGE: { - get_closest_mesh_edges(*mesh, positions, mask, mesh_indices, mesh_distances, {}); - break; - } - case ATTR_DOMAIN_FACE: { - get_closest_mesh_polygons(*mesh, positions, mask, mesh_indices, mesh_distances, {}); - break; - } - case ATTR_DOMAIN_CORNER: { - get_closest_mesh_corners(*mesh, positions, mask, mesh_indices, mesh_distances, {}); - break; - } - default: { - break; - } - } - } - - attribute_math::convert_to_static_type(dst.type(), [&](auto dummy) { - using T = decltype(dummy); - if (use_mesh_ && use_points_) { - VArray<T> src_mesh = mesh_data_->typed<T>(); - VArray<T> src_point = point_data_->typed<T>(); - copy_with_indices_and_comparison(src_mesh, - src_point, - mesh_distances, - point_distances, - mask, - mesh_indices, - point_indices, - dst.typed<T>()); - } - else if (use_points_) { - VArray<T> src_point = point_data_->typed<T>(); - copy_with_indices(src_point, mask, point_indices, dst.typed<T>()); - } - else if (use_mesh_) { - VArray<T> src_mesh = mesh_data_->typed<T>(); - copy_with_indices(src_mesh, mask, mesh_indices, dst.typed<T>()); - } - }); - } - - private: - void evaluate_source_field() - { - if (use_mesh_) { - const MeshComponent &mesh = *source_.get_component_for_read<MeshComponent>(); - const int domain_num = mesh.attribute_domain_size(domain_); - mesh_context_.emplace(GeometryComponentFieldContext(mesh, domain_)); - mesh_evaluator_ = std::make_unique<FieldEvaluator>(*mesh_context_, domain_num); - mesh_evaluator_->add(src_field_); - mesh_evaluator_->evaluate(); - mesh_data_ = &mesh_evaluator_->get_evaluated(0); - } - - if (use_points_) { - const PointCloudComponent &points = *source_.get_component_for_read<PointCloudComponent>(); - const int domain_num = points.attribute_domain_size(domain_); - point_context_.emplace(GeometryComponentFieldContext(points, domain_)); - point_evaluator_ = std::make_unique<FieldEvaluator>(*point_context_, domain_num); - point_evaluator_->add(src_field_); - point_evaluator_->evaluate(); - point_data_ = &point_evaluator_->get_evaluated(0); - } - } -}; - -static const GeometryComponent *find_source_component(const GeometrySet &geometry, - const eAttrDomain domain) -{ - /* Choose the other component based on a consistent order, rather than some more complicated - * heuristic. This is the same order visible in the spreadsheet and used in the ray-cast node. */ - static const Array<GeometryComponentType> supported_types = {GEO_COMPONENT_TYPE_MESH, - GEO_COMPONENT_TYPE_POINT_CLOUD, - GEO_COMPONENT_TYPE_CURVE, - GEO_COMPONENT_TYPE_INSTANCES}; - for (const GeometryComponentType src_type : supported_types) { - if (component_is_available(geometry, src_type, domain)) { - return geometry.get_component_for_read(src_type); - } - } - - return nullptr; -} - -/** - * The index-based transfer theoretically does not need realized data when there is only one - * instance geometry set in the source. A future optimization could be removing that limitation - * internally. - */ -class IndexTransferFunction : public fn::MultiFunction { - GeometrySet src_geometry_; - GField src_field_; - eAttrDomain domain_; - - fn::MFSignature signature_; - - std::optional<GeometryComponentFieldContext> geometry_context_; - std::unique_ptr<FieldEvaluator> evaluator_; - const GVArray *src_data_ = nullptr; - - public: - IndexTransferFunction(GeometrySet geometry, GField src_field, const eAttrDomain domain) - : src_geometry_(std::move(geometry)), src_field_(std::move(src_field)), domain_(domain) - { - src_geometry_.ensure_owns_direct_data(); - - signature_ = this->create_signature(); - this->set_signature(&signature_); - - this->evaluate_field(); - } - - fn::MFSignature create_signature() - { - fn::MFSignatureBuilder signature{"Attribute Transfer Index"}; - signature.single_input<int>("Index"); - signature.single_output("Attribute", src_field_.cpp_type()); - return signature.build(); - } - - void evaluate_field() - { - const GeometryComponent *component = find_source_component(src_geometry_, domain_); - if (component == nullptr) { - return; - } - const int domain_num = component->attribute_domain_size(domain_); - geometry_context_.emplace(GeometryComponentFieldContext(*component, domain_)); - evaluator_ = std::make_unique<FieldEvaluator>(*geometry_context_, domain_num); - evaluator_->add(src_field_); - evaluator_->evaluate(); - src_data_ = &evaluator_->get_evaluated(0); - } - - void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override - { - const VArray<int> &indices = params.readonly_single_input<int>(0, "Index"); - GMutableSpan dst = params.uninitialized_single_output(1, "Attribute"); - - const CPPType &type = dst.type(); - if (src_data_ == nullptr) { - type.value_initialize_indices(dst.data(), mask); - return; - } - - attribute_math::convert_to_static_type(type, [&](auto dummy) { - using T = decltype(dummy); - copy_with_indices_clamped(src_data_->typed<T>(), mask, indices, dst.typed<T>()); - }); - } -}; - -static GField get_input_attribute_field(GeoNodeExecParams ¶ms, const eCustomDataType data_type) -{ - switch (data_type) { - case CD_PROP_FLOAT: - return params.extract_input<Field<float>>("Attribute_001"); - case CD_PROP_FLOAT3: - return params.extract_input<Field<float3>>("Attribute"); - case CD_PROP_COLOR: - return params.extract_input<Field<ColorGeometry4f>>("Attribute_002"); - case CD_PROP_BOOL: - return params.extract_input<Field<bool>>("Attribute_003"); - case CD_PROP_INT32: - return params.extract_input<Field<int>>("Attribute_004"); - default: - BLI_assert_unreachable(); - } - return {}; -} - -static void output_attribute_field(GeoNodeExecParams ¶ms, GField field) -{ - switch (bke::cpp_type_to_custom_data_type(field.cpp_type())) { - case CD_PROP_FLOAT: { - params.set_output("Attribute_001", Field<float>(field)); - break; - } - case CD_PROP_FLOAT3: { - params.set_output("Attribute", Field<float3>(field)); - break; - } - case CD_PROP_COLOR: { - params.set_output("Attribute_002", Field<ColorGeometry4f>(field)); - break; - } - case CD_PROP_BOOL: { - params.set_output("Attribute_003", Field<bool>(field)); - break; - } - case CD_PROP_INT32: { - params.set_output("Attribute_004", Field<int>(field)); - break; - } - default: - break; - } -} - -static void node_geo_exec(GeoNodeExecParams params) -{ - GeometrySet geometry = params.extract_input<GeometrySet>("Source"); - const NodeGeometryTransferAttribute &storage = node_storage(params.node()); - const GeometryNodeAttributeTransferMode mapping = (GeometryNodeAttributeTransferMode) - storage.mode; - const eCustomDataType data_type = static_cast<eCustomDataType>(storage.data_type); - const eAttrDomain domain = static_cast<eAttrDomain>(storage.domain); - - GField field = get_input_attribute_field(params, data_type); - - auto return_default = [&]() { - attribute_math::convert_to_static_type(data_type, [&](auto dummy) { - using T = decltype(dummy); - output_attribute_field(params, fn::make_constant_field<T>(T())); - }); - }; - - GField output_field; - switch (mapping) { - case GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED: { - const Mesh *mesh = geometry.get_mesh_for_read(); - if (mesh == nullptr) { - if (!geometry.is_empty()) { - params.error_message_add(NodeWarningType::Error, - TIP_("The source geometry must contain a mesh")); - } - return return_default(); - } - if (mesh->totpoly == 0) { - /* Don't add a warning for empty meshes. */ - if (mesh->totvert != 0) { - params.error_message_add(NodeWarningType::Error, - TIP_("The source mesh must have faces")); - } - return return_default(); - } - auto fn = std::make_unique<NearestInterpolatedTransferFunction>(std::move(geometry), - std::move(field)); - auto op = std::make_shared<FieldOperation>( - FieldOperation(std::move(fn), {params.extract_input<Field<float3>>("Source Position")})); - output_field = GField(std::move(op)); - break; - } - case GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST: { - if (geometry.has_curves() && !geometry.has_mesh() && !geometry.has_pointcloud()) { - params.error_message_add(NodeWarningType::Error, - TIP_("The source geometry must contain a mesh or a point cloud")); - return return_default(); - } - auto fn = std::make_unique<NearestTransferFunction>( - std::move(geometry), std::move(field), domain); - auto op = std::make_shared<FieldOperation>( - FieldOperation(std::move(fn), {params.extract_input<Field<float3>>("Source Position")})); - output_field = GField(std::move(op)); - break; - } - case GEO_NODE_ATTRIBUTE_TRANSFER_INDEX: { - Field<int> indices = params.extract_input<Field<int>>("Index"); - auto fn = std::make_unique<IndexTransferFunction>( - std::move(geometry), std::move(field), domain); - auto op = std::make_shared<FieldOperation>( - FieldOperation(std::move(fn), {std::move(indices)})); - output_field = GField(std::move(op)); - break; - } - } - - output_attribute_field(params, std::move(output_field)); -} - -} // namespace blender::nodes::node_geo_transfer_attribute_cc - -void register_node_type_geo_transfer_attribute() -{ - namespace file_ns = blender::nodes::node_geo_transfer_attribute_cc; - - static bNodeType ntype; - - geo_node_type_base( - &ntype, GEO_NODE_TRANSFER_ATTRIBUTE, "Transfer Attribute", NODE_CLASS_ATTRIBUTE); - node_type_init(&ntype, file_ns::node_init); - node_type_update(&ntype, file_ns::node_update); - node_type_storage(&ntype, - "NodeGeometryTransferAttribute", - node_free_standard_storage, - node_copy_standard_storage); - ntype.declare = file_ns::node_declare; - ntype.geometry_node_execute = file_ns::node_geo_exec; - ntype.draw_buttons = file_ns::node_layout; - ntype.gather_link_search_ops = file_ns::node_gather_link_searches; - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/geometry/nodes/node_geo_transform.cc b/source/blender/nodes/geometry/nodes/node_geo_transform.cc index 945d5fbdcac..4130cad3bda 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_transform.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_transform.cc @@ -47,7 +47,7 @@ static void transform_mesh(Mesh &mesh, const float4x4 &transform) static void translate_pointcloud(PointCloud &pointcloud, const float3 translation) { - MutableAttributeAccessor attributes = bke::pointcloud_attributes_for_write(pointcloud); + MutableAttributeAccessor attributes = pointcloud.attributes_for_write(); SpanAttributeWriter position = attributes.lookup_or_add_for_write_span<float3>( "position", ATTR_DOMAIN_POINT); for (const int i : position.span.index_range()) { @@ -58,7 +58,7 @@ static void translate_pointcloud(PointCloud &pointcloud, const float3 translatio static void transform_pointcloud(PointCloud &pointcloud, const float4x4 &transform) { - MutableAttributeAccessor attributes = bke::pointcloud_attributes_for_write(pointcloud); + MutableAttributeAccessor attributes = pointcloud.attributes_for_write(); SpanAttributeWriter position = attributes.lookup_or_add_for_write_span<float3>( "position", ATTR_DOMAIN_POINT); for (const int i : position.span.index_range()) { @@ -83,44 +83,61 @@ static void transform_instances(InstancesComponent &instances, const float4x4 &t } } -static void transform_volume(Volume &volume, const float4x4 &transform, const Depsgraph &depsgraph) +static void transform_volume(GeoNodeExecParams ¶ms, + Volume &volume, + const float4x4 &transform, + const Depsgraph &depsgraph) { #ifdef WITH_OPENVDB - /* Scaling an axis to zero is not supported for volumes. */ - const float3 translation = transform.translation(); - const float3 rotation = transform.to_euler(); - const float3 scale = transform.scale(); - const float3 limited_scale = { - (scale.x == 0.0f) ? FLT_EPSILON : scale.x, - (scale.y == 0.0f) ? FLT_EPSILON : scale.y, - (scale.z == 0.0f) ? FLT_EPSILON : scale.z, - }; - const float4x4 scale_limited_transform = float4x4::from_loc_eul_scale( - translation, rotation, limited_scale); - const Main *bmain = DEG_get_bmain(&depsgraph); BKE_volume_load(&volume, bmain); openvdb::Mat4s vdb_matrix; - memcpy(vdb_matrix.asPointer(), &scale_limited_transform, sizeof(float[4][4])); + memcpy(vdb_matrix.asPointer(), &transform, sizeof(float[4][4])); openvdb::Mat4d vdb_matrix_d{vdb_matrix}; + bool found_too_small_scale = false; const int grids_num = BKE_volume_num_grids(&volume); for (const int i : IndexRange(grids_num)) { VolumeGrid *volume_grid = BKE_volume_grid_get_for_write(&volume, i); - - openvdb::GridBase::Ptr grid = BKE_volume_grid_openvdb_for_write(&volume, volume_grid, false); - openvdb::math::Transform &grid_transform = grid->transform(); - grid_transform.postMult(vdb_matrix_d); + float4x4 grid_matrix; + BKE_volume_grid_transform_matrix(volume_grid, grid_matrix.values); + mul_m4_m4_pre(grid_matrix.values, transform.values); + const float determinant = determinant_m4(grid_matrix.values); + if (!BKE_volume_grid_determinant_valid(determinant)) { + found_too_small_scale = true; + /* Clear the tree because it is too small. */ + BKE_volume_grid_clear_tree(volume, *volume_grid); + if (determinant == 0) { + /* Reset rotation and scale. */ + copy_v3_fl3(grid_matrix.values[0], 1, 0, 0); + copy_v3_fl3(grid_matrix.values[1], 0, 1, 0); + copy_v3_fl3(grid_matrix.values[2], 0, 0, 1); + } + else { + /* Keep rotation but reset scale. */ + normalize_v3(grid_matrix.values[0]); + normalize_v3(grid_matrix.values[1]); + normalize_v3(grid_matrix.values[2]); + } + } + BKE_volume_grid_transform_matrix_set(volume_grid, grid_matrix.values); + } + if (found_too_small_scale) { + params.error_message_add(NodeWarningType::Warning, + TIP_("Volume scale is lower than permitted by OpenVDB")); } #else - UNUSED_VARS(volume, transform, depsgraph); + UNUSED_VARS(params, volume, transform, depsgraph); #endif } -static void translate_volume(Volume &volume, const float3 translation, const Depsgraph &depsgraph) +static void translate_volume(GeoNodeExecParams ¶ms, + Volume &volume, + const float3 translation, + const Depsgraph &depsgraph) { - transform_volume(volume, float4x4::from_location(translation), depsgraph); + transform_volume(params, volume, float4x4::from_location(translation), depsgraph); } static void transform_curve_edit_hints(bke::CurvesEditHints &edit_hints, const float4x4 &transform) @@ -151,7 +168,8 @@ static void translate_curve_edit_hints(bke::CurvesEditHints &edit_hints, const f } } -static void translate_geometry_set(GeometrySet &geometry, +static void translate_geometry_set(GeoNodeExecParams ¶ms, + GeometrySet &geometry, const float3 translation, const Depsgraph &depsgraph) { @@ -165,7 +183,7 @@ static void translate_geometry_set(GeometrySet &geometry, translate_pointcloud(*pointcloud, translation); } if (Volume *volume = geometry.get_volume_for_write()) { - translate_volume(*volume, translation, depsgraph); + translate_volume(params, *volume, translation, depsgraph); } if (geometry.has_instances()) { translate_instances(geometry.get_component_for_write<InstancesComponent>(), translation); @@ -175,7 +193,8 @@ static void translate_geometry_set(GeometrySet &geometry, } } -void transform_geometry_set(GeometrySet &geometry, +void transform_geometry_set(GeoNodeExecParams ¶ms, + GeometrySet &geometry, const float4x4 &transform, const Depsgraph &depsgraph) { @@ -189,7 +208,7 @@ void transform_geometry_set(GeometrySet &geometry, transform_pointcloud(*pointcloud, transform); } if (Volume *volume = geometry.get_volume_for_write()) { - transform_volume(*volume, transform, depsgraph); + transform_volume(params, *volume, transform, depsgraph); } if (geometry.has_instances()) { transform_instances(geometry.get_component_for_write<InstancesComponent>(), transform); @@ -230,10 +249,11 @@ static void node_geo_exec(GeoNodeExecParams params) /* Use only translation if rotation and scale don't apply. */ if (use_translate(rotation, scale)) { - translate_geometry_set(geometry_set, translation, *params.depsgraph()); + translate_geometry_set(params, geometry_set, translation, *params.depsgraph()); } else { - transform_geometry_set(geometry_set, + transform_geometry_set(params, + geometry_set, float4x4::from_loc_eul_scale(translation, rotation, scale), *params.depsgraph()); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc index ae538072e65..3e9fe99adb0 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc @@ -17,9 +17,8 @@ static void node_declare(NodeDeclarationBuilder &b) static void translate_instances(GeoNodeExecParams ¶ms, InstancesComponent &instances_component) { - GeometryComponentFieldContext field_context{instances_component, ATTR_DOMAIN_INSTANCE}; - - fn::FieldEvaluator evaluator{field_context, instances_component.instances_num()}; + const bke::InstancesFieldContext context{instances_component}; + fn::FieldEvaluator evaluator{context, instances_component.instances_num()}; evaluator.set_selection(params.extract_input<Field<bool>>("Selection")); evaluator.add(params.extract_input<Field<float3>>("Translation")); evaluator.add(params.extract_input<Field<bool>>("Local Space")); diff --git a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc index 992470e8279..57487059437 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc @@ -47,9 +47,6 @@ static Mesh *triangulate_mesh_selection(const Mesh &mesh, BMeshFromMeshParams from_mesh_params{}; from_mesh_params.calc_face_normal = true; from_mesh_params.calc_vert_normal = true; - from_mesh_params.add_key_index = true; - from_mesh_params.use_shapekey = true; - from_mesh_params.active_shapekey = 1; from_mesh_params.cd_mask_extra = cd_mask_extra; BMesh *bm = BKE_mesh_to_bmesh_ex(&mesh, &create_params, &from_mesh_params); @@ -80,12 +77,10 @@ static void node_geo_exec(GeoNodeExecParams params) if (!geometry_set.has_mesh()) { return; } - GeometryComponent &component = geometry_set.get_component_for_write<MeshComponent>(); const Mesh &mesh_in = *geometry_set.get_mesh_for_read(); - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE); - GeometryComponentFieldContext context{component, ATTR_DOMAIN_FACE}; - FieldEvaluator evaluator{context, domain_size}; + bke::MeshFieldContext context{mesh_in, ATTR_DOMAIN_FACE}; + FieldEvaluator evaluator{context, mesh_in.totpoly}; evaluator.add(selection_field); evaluator.evaluate(); const IndexMask selection = evaluator.get_evaluated_as_mask(0); diff --git a/source/blender/nodes/geometry/nodes/node_geo_uv_pack_islands.cc b/source/blender/nodes/geometry/nodes/node_geo_uv_pack_islands.cc index 17413e64f7d..ccb489f6e29 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_uv_pack_islands.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_uv_pack_islands.cc @@ -5,6 +5,8 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "BKE_mesh.h" + #include "node_geometry_util.hh" namespace blender::nodes::node_geo_uv_pack_islands_cc { @@ -28,21 +30,19 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Vector>(N_("UV")).field_source(); } -static VArray<float3> construct_uv_gvarray(const MeshComponent &component, +static VArray<float3> construct_uv_gvarray(const Mesh &mesh, const Field<bool> selection_field, const Field<float3> uv_field, const bool rotate, const float margin, const eAttrDomain domain) { - const Mesh *mesh = component.get_for_read(); - if (mesh == nullptr) { - return {}; - } + const Span<MVert> verts = mesh.verts(); + const Span<MPoly> polys = mesh.polys(); + const Span<MLoop> loops = mesh.loops(); - const int face_num = component.attribute_domain_size(ATTR_DOMAIN_FACE); - GeometryComponentFieldContext face_context{component, ATTR_DOMAIN_FACE}; - FieldEvaluator face_evaluator{face_context, face_num}; + bke::MeshFieldContext face_context{mesh, ATTR_DOMAIN_FACE}; + FieldEvaluator face_evaluator{face_context, polys.size()}; face_evaluator.add(selection_field); face_evaluator.evaluate(); const IndexMask selection = face_evaluator.get_evaluated_as_mask(0); @@ -50,25 +50,24 @@ static VArray<float3> construct_uv_gvarray(const MeshComponent &component, return {}; } - const int corner_num = component.attribute_domain_size(ATTR_DOMAIN_CORNER); - GeometryComponentFieldContext corner_context{component, ATTR_DOMAIN_CORNER}; - FieldEvaluator evaluator{corner_context, corner_num}; - Array<float3> uv(corner_num); + bke::MeshFieldContext corner_context{mesh, ATTR_DOMAIN_CORNER}; + FieldEvaluator evaluator{corner_context, mesh.totloop}; + Array<float3> uv(mesh.totloop); evaluator.add_with_destination(uv_field, uv.as_mutable_span()); evaluator.evaluate(); ParamHandle *handle = GEO_uv_parametrizer_construct_begin(); for (const int mp_index : selection) { - const MPoly &mp = mesh->mpoly[mp_index]; + const MPoly &mp = polys[mp_index]; Array<ParamKey, 16> mp_vkeys(mp.totloop); Array<bool, 16> mp_pin(mp.totloop); Array<bool, 16> mp_select(mp.totloop); Array<const float *, 16> mp_co(mp.totloop); Array<float *, 16> mp_uv(mp.totloop); for (const int i : IndexRange(mp.totloop)) { - const MLoop &ml = mesh->mloop[mp.loopstart + i]; + const MLoop &ml = loops[mp.loopstart + i]; mp_vkeys[i] = ml.v; - mp_co[i] = mesh->mvert[ml.v].co; + mp_co[i] = verts[ml.v].co; mp_uv[i] = uv[mp.loopstart + i]; mp_pin[i] = false; mp_select[i] = false; @@ -88,11 +87,11 @@ static VArray<float3> construct_uv_gvarray(const MeshComponent &component, GEO_uv_parametrizer_flush(handle); GEO_uv_parametrizer_delete(handle); - return component.attributes()->adapt_domain<float3>( + return mesh.attributes().adapt_domain<float3>( VArray<float3>::ForContainer(std::move(uv)), ATTR_DOMAIN_CORNER, domain); } -class PackIslandsFieldInput final : public GeometryFieldInput { +class PackIslandsFieldInput final : public bke::MeshFieldInput { private: const Field<bool> selection_field; const Field<float3> uv_field; @@ -104,7 +103,7 @@ class PackIslandsFieldInput final : public GeometryFieldInput { const Field<float3> uv_field, const bool rotate, const float margin) - : GeometryFieldInput(CPPType::get<float3>(), "Pack UV Islands Field"), + : bke::MeshFieldInput(CPPType::get<float3>(), "Pack UV Islands Field"), selection_field(selection_field), uv_field(uv_field), rotate(rotate), @@ -113,16 +112,11 @@ class PackIslandsFieldInput final : public GeometryFieldInput { category_ = Category::Generated; } - GVArray get_varray_for_context(const GeometryComponent &component, + GVArray get_varray_for_context(const Mesh &mesh, const eAttrDomain domain, IndexMask UNUSED(mask)) const final { - if (component.type() == GEO_COMPONENT_TYPE_MESH) { - const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); - return construct_uv_gvarray( - mesh_component, selection_field, uv_field, rotate, margin, domain); - } - return {}; + return construct_uv_gvarray(mesh, selection_field, uv_field, rotate, margin, domain); } }; diff --git a/source/blender/nodes/geometry/nodes/node_geo_uv_unwrap.cc b/source/blender/nodes/geometry/nodes/node_geo_uv_unwrap.cc index 03657f3e016..801bc3f4642 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_uv_unwrap.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_uv_unwrap.cc @@ -5,6 +5,8 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "BKE_mesh.h" + #include "UI_interface.h" #include "UI_resources.h" @@ -52,7 +54,7 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node) node->storage = data; } -static VArray<float3> construct_uv_gvarray(const MeshComponent &component, +static VArray<float3> construct_uv_gvarray(const Mesh &mesh, const Field<bool> selection_field, const Field<bool> seam_field, const bool fill_holes, @@ -60,14 +62,13 @@ static VArray<float3> construct_uv_gvarray(const MeshComponent &component, const GeometryNodeUVUnwrapMethod method, const eAttrDomain domain) { - const Mesh *mesh = component.get_for_read(); - if (mesh == nullptr) { - return {}; - } + const Span<MVert> verts = mesh.verts(); + const Span<MEdge> edges = mesh.edges(); + const Span<MPoly> polys = mesh.polys(); + const Span<MLoop> loops = mesh.loops(); - const int face_num = component.attribute_domain_size(ATTR_DOMAIN_FACE); - GeometryComponentFieldContext face_context{component, ATTR_DOMAIN_FACE}; - FieldEvaluator face_evaluator{face_context, face_num}; + bke::MeshFieldContext face_context{mesh, ATTR_DOMAIN_FACE}; + FieldEvaluator face_evaluator{face_context, polys.size()}; face_evaluator.add(selection_field); face_evaluator.evaluate(); const IndexMask selection = face_evaluator.get_evaluated_as_mask(0); @@ -75,27 +76,26 @@ static VArray<float3> construct_uv_gvarray(const MeshComponent &component, return {}; } - const int edge_num = component.attribute_domain_size(ATTR_DOMAIN_EDGE); - GeometryComponentFieldContext edge_context{component, ATTR_DOMAIN_EDGE}; - FieldEvaluator edge_evaluator{edge_context, edge_num}; + bke::MeshFieldContext edge_context{mesh, ATTR_DOMAIN_EDGE}; + FieldEvaluator edge_evaluator{edge_context, edges.size()}; edge_evaluator.add(seam_field); edge_evaluator.evaluate(); const IndexMask seam = edge_evaluator.get_evaluated_as_mask(0); - Array<float3> uv(mesh->totloop, float3(0)); + Array<float3> uv(loops.size(), float3(0)); ParamHandle *handle = GEO_uv_parametrizer_construct_begin(); for (const int mp_index : selection) { - const MPoly &mp = mesh->mpoly[mp_index]; + const MPoly &mp = polys[mp_index]; Array<ParamKey, 16> mp_vkeys(mp.totloop); Array<bool, 16> mp_pin(mp.totloop); Array<bool, 16> mp_select(mp.totloop); Array<const float *, 16> mp_co(mp.totloop); Array<float *, 16> mp_uv(mp.totloop); for (const int i : IndexRange(mp.totloop)) { - const MLoop &ml = mesh->mloop[mp.loopstart + i]; + const MLoop &ml = loops[mp.loopstart + i]; mp_vkeys[i] = ml.v; - mp_co[i] = mesh->mvert[ml.v].co; + mp_co[i] = verts[ml.v].co; mp_uv[i] = uv[mp.loopstart + i]; mp_pin[i] = false; mp_select[i] = false; @@ -110,7 +110,7 @@ static VArray<float3> construct_uv_gvarray(const MeshComponent &component, mp_select.data()); } for (const int i : seam) { - const MEdge &edge = mesh->medge[i]; + const MEdge &edge = edges[i]; ParamKey vkeys[2]{edge.v1, edge.v2}; GEO_uv_parametrizer_edge_set_seam(handle, vkeys); } @@ -126,11 +126,11 @@ static VArray<float3> construct_uv_gvarray(const MeshComponent &component, GEO_uv_parametrizer_flush(handle); GEO_uv_parametrizer_delete(handle); - return component.attributes()->adapt_domain<float3>( + return mesh.attributes().adapt_domain<float3>( VArray<float3>::ForContainer(std::move(uv)), ATTR_DOMAIN_CORNER, domain); } -class UnwrapFieldInput final : public GeometryFieldInput { +class UnwrapFieldInput final : public bke::MeshFieldInput { private: const Field<bool> selection; const Field<bool> seam; @@ -144,7 +144,7 @@ class UnwrapFieldInput final : public GeometryFieldInput { const bool fill_holes, const float margin, const GeometryNodeUVUnwrapMethod method) - : GeometryFieldInput(CPPType::get<float3>(), "UV Unwrap Field"), + : bke::MeshFieldInput(CPPType::get<float3>(), "UV Unwrap Field"), selection(selection), seam(seam), fill_holes(fill_holes), @@ -154,16 +154,11 @@ class UnwrapFieldInput final : public GeometryFieldInput { category_ = Category::Generated; } - GVArray get_varray_for_context(const GeometryComponent &component, + GVArray get_varray_for_context(const Mesh &mesh, const eAttrDomain domain, IndexMask UNUSED(mask)) const final { - if (component.type() == GEO_COMPONENT_TYPE_MESH) { - const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); - return construct_uv_gvarray( - mesh_component, selection, seam, fill_holes, margin, method, domain); - } - return {}; + return construct_uv_gvarray(mesh, selection, seam, fill_holes, margin, method, domain); } }; diff --git a/source/blender/nodes/geometry/nodes/node_geo_volume_cube.cc b/source/blender/nodes/geometry/nodes/node_geo_volume_cube.cc index d7e9e38ea0d..c102b91acb1 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_volume_cube.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_volume_cube.cc @@ -113,9 +113,9 @@ class Grid3DFieldContext : public FieldContext { } }; -#ifdef WITH_OPENVDB static void node_geo_exec(GeoNodeExecParams params) { +#ifdef WITH_OPENVDB const float3 bounds_min = params.extract_input<float3>("Min"); const float3 bounds_max = params.extract_input<float3>("Max"); @@ -137,6 +137,14 @@ static void node_geo_exec(GeoNodeExecParams params) return; } + const double3 scale_fac = double3(bounds_max - bounds_min) / double3(resolution - 1); + if (!BKE_volume_grid_determinant_valid(scale_fac.x * scale_fac.y * scale_fac.z)) { + params.error_message_add(NodeWarningType::Warning, + TIP_("Volume scale is lower than permitted by OpenVDB")); + params.set_default_remaining_outputs(); + return; + } + Field<float> input_field = params.extract_input<Field<float>>("Density"); /* Evaluate input field on a 3D grid. */ @@ -157,8 +165,7 @@ static void node_geo_exec(GeoNodeExecParams params) openvdb::tools::copyFromDense(dense_grid, *grid, 0.0f); grid->transform().preTranslate(openvdb::math::Vec3<float>(-0.5f)); - const float3 scale_fac = (bounds_max - bounds_min) / float3(resolution - 1); - grid->transform().postScale(openvdb::math::Vec3<float>(scale_fac.x, scale_fac.y, scale_fac.z)); + grid->transform().postScale(openvdb::math::Vec3<double>(scale_fac.x, scale_fac.y, scale_fac.z)); grid->transform().postTranslate( openvdb::math::Vec3<float>(bounds_min.x, bounds_min.y, bounds_min.z)); @@ -170,16 +177,12 @@ static void node_geo_exec(GeoNodeExecParams params) GeometrySet r_geometry_set; r_geometry_set.replace_volume(volume); params.set_output("Volume", r_geometry_set); -} - #else -static void node_geo_exec(GeoNodeExecParams params) -{ + params.set_default_remaining_outputs(); params.error_message_add(NodeWarningType::Error, TIP_("Disabled, Blender was compiled without OpenVDB")); - params.set_default_remaining_outputs(); +#endif } -#endif /* WITH_OPENVDB */ } // namespace blender::nodes::node_geo_volume_cube_cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc index 91429560ac8..763e207b388 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc @@ -123,9 +123,9 @@ static Mesh *create_mesh_from_volume_grids(Span<openvdb::GridBase::ConstPtr> gri Mesh *mesh = BKE_mesh_new_nomain(vert_offset, 0, 0, loop_offset, poly_offset); BKE_id_material_eval_ensure_default_slot(&mesh->id); - MutableSpan<MVert> verts{mesh->mvert, mesh->totvert}; - MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop}; - MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly}; + MutableSpan<MVert> verts = mesh->verts_for_write(); + MutableSpan<MPoly> polys = mesh->polys_for_write(); + MutableSpan<MLoop> loops = mesh->loops_for_write(); for (const int i : grids.index_range()) { const bke::OpenVDBMeshData &data = mesh_data[i]; @@ -187,20 +187,19 @@ static Mesh *create_mesh_from_volume(GeometrySet &geometry_set, GeoNodeExecParam static void node_geo_exec(GeoNodeExecParams params) { - GeometrySet geometry_set = params.extract_input<GeometrySet>("Volume"); - #ifdef WITH_OPENVDB + GeometrySet geometry_set = params.extract_input<GeometrySet>("Volume"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { Mesh *mesh = create_mesh_from_volume(geometry_set, params); geometry_set.replace_mesh(mesh); geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_MESH}); }); + params.set_output("Mesh", std::move(geometry_set)); #else + params.set_default_remaining_outputs(); params.error_message_add(NodeWarningType::Error, TIP_("Disabled, Blender was compiled without OpenVDB")); #endif - - params.set_output("Mesh", std::move(geometry_set)); } } // namespace blender::nodes::node_geo_volume_to_mesh_cc diff --git a/source/blender/nodes/intern/derived_node_tree.cc b/source/blender/nodes/intern/derived_node_tree.cc index e589da09b16..2ea80008af8 100644 --- a/source/blender/nodes/intern/derived_node_tree.cc +++ b/source/blender/nodes/intern/derived_node_tree.cc @@ -2,38 +2,38 @@ #include "NOD_derived_node_tree.hh" +#include "BKE_node.h" + #include "BLI_dot_export.hh" namespace blender::nodes { -DerivedNodeTree::DerivedNodeTree(bNodeTree &btree, NodeTreeRefMap &node_tree_refs) +DerivedNodeTree::DerivedNodeTree(const bNodeTree &btree) { /* Construct all possible contexts immediately. This is significantly cheaper than inlining all * node groups. If it still becomes a performance issue in the future, contexts could be * constructed lazily when they are needed. */ - root_context_ = &this->construct_context_recursively(nullptr, nullptr, btree, node_tree_refs); + root_context_ = &this->construct_context_recursively(nullptr, nullptr, btree); } DTreeContext &DerivedNodeTree::construct_context_recursively(DTreeContext *parent_context, - const NodeRef *parent_node, - bNodeTree &btree, - NodeTreeRefMap &node_tree_refs) + const bNode *parent_node, + const bNodeTree &btree) { + btree.ensure_topology_cache(); DTreeContext &context = *allocator_.construct<DTreeContext>().release(); context.parent_context_ = parent_context; context.parent_node_ = parent_node; context.derived_tree_ = this; - context.tree_ = &get_tree_ref_from_map(node_tree_refs, btree); - used_node_tree_refs_.add(context.tree_); + context.btree_ = &btree; + used_btrees_.add(context.btree_); - for (const NodeRef *node : context.tree_->nodes()) { - if (node->is_group_node()) { - bNode *bnode = node->bnode(); + for (const bNode *bnode : context.btree_->all_nodes()) { + if (bnode->is_group()) { bNodeTree *child_btree = reinterpret_cast<bNodeTree *>(bnode->id); if (child_btree != nullptr) { - DTreeContext &child = this->construct_context_recursively( - &context, node, *child_btree, node_tree_refs); - context.children_.add_new(node, &child); + DTreeContext &child = this->construct_context_recursively(&context, bnode, *child_btree); + context.children_.add_new(bnode, &child); } } } @@ -57,8 +57,8 @@ void DerivedNodeTree::destruct_context_recursively(DTreeContext *context) bool DerivedNodeTree::has_link_cycles() const { - for (const NodeTreeRef *tree_ref : used_node_tree_refs_) { - if (tree_ref->has_link_cycles()) { + for (const bNodeTree *btree : used_btrees_) { + if (btree->has_available_link_cycle()) { return true; } } @@ -67,8 +67,8 @@ bool DerivedNodeTree::has_link_cycles() const bool DerivedNodeTree::has_undefined_nodes_or_sockets() const { - for (const NodeTreeRef *tree_ref : used_node_tree_refs_) { - if (tree_ref->has_undefined_nodes_or_sockets()) { + for (const bNodeTree *btree : used_btrees_) { + if (btree->has_undefined_nodes_or_sockets()) { return true; } } @@ -83,8 +83,8 @@ void DerivedNodeTree::foreach_node(FunctionRef<void(DNode)> callback) const void DerivedNodeTree::foreach_node_in_context_recursive(const DTreeContext &context, FunctionRef<void(DNode)> callback) const { - for (const NodeRef *node_ref : context.tree_->nodes()) { - callback(DNode(&context, node_ref)); + for (const bNode *bnode : context.btree_->all_nodes()) { + callback(DNode(&context, bnode)); } for (const DTreeContext *child_context : context.children_.values()) { this->foreach_node_in_context_recursive(*child_context, callback); @@ -94,32 +94,32 @@ void DerivedNodeTree::foreach_node_in_context_recursive(const DTreeContext &cont DOutputSocket DInputSocket::get_corresponding_group_node_output() const { BLI_assert(*this); - BLI_assert(socket_ref_->node().is_group_output_node()); - BLI_assert(socket_ref_->index() < socket_ref_->node().inputs().size() - 1); + BLI_assert(bsocket_->owner_node().is_group_output()); + BLI_assert(bsocket_->index() < bsocket_->owner_node().input_sockets().size() - 1); const DTreeContext *parent_context = context_->parent_context(); - const NodeRef *parent_node = context_->parent_node(); + const bNode *parent_node = context_->parent_node(); BLI_assert(parent_context != nullptr); BLI_assert(parent_node != nullptr); - const int socket_index = socket_ref_->index(); - return {parent_context, &parent_node->output(socket_index)}; + const int socket_index = bsocket_->index(); + return {parent_context, &parent_node->output_socket(socket_index)}; } Vector<DOutputSocket> DInputSocket::get_corresponding_group_input_sockets() const { BLI_assert(*this); - BLI_assert(socket_ref_->node().is_group_node()); + BLI_assert(bsocket_->owner_node().is_group()); - const DTreeContext *child_context = context_->child_context(socket_ref_->node()); + const DTreeContext *child_context = context_->child_context(bsocket_->owner_node()); BLI_assert(child_context != nullptr); - const NodeTreeRef &child_tree = child_context->tree(); - Span<const NodeRef *> group_input_nodes = child_tree.nodes_by_type("NodeGroupInput"); - const int socket_index = socket_ref_->index(); + const bNodeTree &child_tree = child_context->btree(); + Span<const bNode *> group_input_nodes = child_tree.nodes_by_type("NodeGroupInput"); + const int socket_index = bsocket_->index(); Vector<DOutputSocket> sockets; - for (const NodeRef *group_input_node : group_input_nodes) { - sockets.append(DOutputSocket(child_context, &group_input_node->output(socket_index))); + for (const bNode *group_input_node : group_input_nodes) { + sockets.append(DOutputSocket(child_context, &group_input_node->output_socket(socket_index))); } return sockets; } @@ -127,36 +127,36 @@ Vector<DOutputSocket> DInputSocket::get_corresponding_group_input_sockets() cons DInputSocket DOutputSocket::get_corresponding_group_node_input() const { BLI_assert(*this); - BLI_assert(socket_ref_->node().is_group_input_node()); - BLI_assert(socket_ref_->index() < socket_ref_->node().outputs().size() - 1); + BLI_assert(bsocket_->owner_node().is_group_input()); + BLI_assert(bsocket_->index() < bsocket_->owner_node().output_sockets().size() - 1); const DTreeContext *parent_context = context_->parent_context(); - const NodeRef *parent_node = context_->parent_node(); + const bNode *parent_node = context_->parent_node(); BLI_assert(parent_context != nullptr); BLI_assert(parent_node != nullptr); - const int socket_index = socket_ref_->index(); - return {parent_context, &parent_node->input(socket_index)}; + const int socket_index = bsocket_->index(); + return {parent_context, &parent_node->input_socket(socket_index)}; } DInputSocket DOutputSocket::get_active_corresponding_group_output_socket() const { BLI_assert(*this); - BLI_assert(socket_ref_->node().is_group_node()); + BLI_assert(bsocket_->owner_node().is_group()); - const DTreeContext *child_context = context_->child_context(socket_ref_->node()); + const DTreeContext *child_context = context_->child_context(bsocket_->owner_node()); if (child_context == nullptr) { /* Can happen when the group node references a non-existent group (e.g. when the group is * linked but the original file is not found). */ return {}; } - const NodeTreeRef &child_tree = child_context->tree(); - Span<const NodeRef *> group_output_nodes = child_tree.nodes_by_type("NodeGroupOutput"); - const int socket_index = socket_ref_->index(); - for (const NodeRef *group_output_node : group_output_nodes) { - if (group_output_node->bnode()->flag & NODE_DO_OUTPUT || group_output_nodes.size() == 1) { - return {child_context, &group_output_node->input(socket_index)}; + const bNodeTree &child_tree = child_context->btree(); + Span<const bNode *> group_output_nodes = child_tree.nodes_by_type("NodeGroupOutput"); + const int socket_index = bsocket_->index(); + for (const bNode *group_output_node : group_output_nodes) { + if (group_output_node->flag & NODE_DO_OUTPUT || group_output_nodes.size() == 1) { + return {child_context, &group_output_node->input_socket(socket_index)}; } } return {}; @@ -165,11 +165,11 @@ DInputSocket DOutputSocket::get_active_corresponding_group_output_socket() const void DInputSocket::foreach_origin_socket(FunctionRef<void(DSocket)> origin_fn) const { BLI_assert(*this); - for (const OutputSocketRef *linked_socket : socket_ref_->as_input().logically_linked_sockets()) { - const NodeRef &linked_node = linked_socket->node(); + for (const bNodeSocket *linked_socket : bsocket_->logically_linked_sockets()) { + const bNode &linked_node = linked_socket->owner_node(); DOutputSocket linked_dsocket{context_, linked_socket}; - if (linked_node.is_group_input_node()) { + if (linked_node.is_group_input()) { if (context_->is_root()) { /* This is a group input in the root node group. */ origin_fn(linked_dsocket); @@ -187,7 +187,7 @@ void DInputSocket::foreach_origin_socket(FunctionRef<void(DSocket)> origin_fn) c } } } - else if (linked_node.is_group_node()) { + else if (linked_node.is_group()) { DInputSocket socket_in_group = linked_dsocket.get_active_corresponding_group_output_socket(); if (socket_in_group) { if (socket_in_group->is_logically_linked()) { @@ -217,16 +217,16 @@ void DOutputSocket::foreach_target_socket(ForeachTargetSocketFn target_fn) const void DOutputSocket::foreach_target_socket(ForeachTargetSocketFn target_fn, TargetSocketPathInfo &path_info) const { - for (const LinkRef *link : socket_ref_->as_output().directly_linked_links()) { + for (const bNodeLink *link : bsocket_->directly_linked_links()) { if (link->is_muted()) { continue; } - const DInputSocket &linked_socket{context_, &link->to()}; + const DInputSocket &linked_socket{context_, link->tosock}; if (!linked_socket->is_available()) { continue; } const DNode linked_node = linked_socket.node(); - if (linked_node->is_reroute_node()) { + if (linked_node->is_reroute()) { const DInputSocket reroute_input = linked_socket; const DOutputSocket reroute_output = linked_node.output(0); path_info.sockets.append(reroute_input); @@ -236,18 +236,18 @@ void DOutputSocket::foreach_target_socket(ForeachTargetSocketFn target_fn, path_info.sockets.pop_last(); } else if (linked_node->is_muted()) { - for (const InternalLinkRef *internal_link : linked_node->internal_links()) { - if (&internal_link->from() != linked_socket.socket_ref()) { + for (const bNodeLink *internal_link : linked_node->internal_links_span()) { + if (internal_link->fromsock != linked_socket.bsocket()) { continue; } /* The internal link only forwards the first incoming link. */ - if (linked_socket->is_multi_input_socket()) { + if (linked_socket->is_multi_input()) { if (linked_socket->directly_linked_links()[0] != link) { continue; } } const DInputSocket mute_input = linked_socket; - const DOutputSocket mute_output{context_, &internal_link->to()}; + const DOutputSocket mute_output{context_, internal_link->tosock}; path_info.sockets.append(mute_input); path_info.sockets.append(mute_output); mute_output.foreach_target_socket(target_fn, path_info); @@ -255,8 +255,8 @@ void DOutputSocket::foreach_target_socket(ForeachTargetSocketFn target_fn, path_info.sockets.pop_last(); } } - else if (linked_node->is_group_output_node()) { - if (linked_node.node_ref() != context_->tree().group_output_node()) { + else if (linked_node->is_group_output()) { + if (linked_node.bnode() != context_->btree().group_output_node()) { continue; } if (context_->is_root()) { @@ -276,7 +276,7 @@ void DOutputSocket::foreach_target_socket(ForeachTargetSocketFn target_fn, path_info.sockets.pop_last(); } } - else if (linked_node->is_group_node()) { + else if (linked_node->is_group()) { /* Follow the links within the nested node group. */ path_info.sockets.append(linked_socket); const Vector<DOutputSocket> sockets_in_group = @@ -310,7 +310,8 @@ static dot::Cluster *get_dot_cluster_for_context( } dot::Cluster *parent_cluster = get_dot_cluster_for_context( digraph, parent_context, dot_clusters); - std::string cluster_name = context->tree().name() + " / " + context->parent_node()->name(); + std::string cluster_name = StringRef(context->btree().id.name + 2) + " / " + + context->parent_node()->name; dot::Cluster &cluster = digraph.new_cluster(cluster_name); cluster.set_parent_cluster(parent_cluster); return &cluster; @@ -328,11 +329,11 @@ std::string DerivedNodeTree::to_dot() const this->foreach_node([&](DNode node) { /* Ignore nodes that should not show up in the final output. */ - if (node->is_muted() || node->is_group_node() || node->is_reroute_node() || node->is_frame()) { + if (node->is_muted() || node->is_group() || node->is_reroute() || node->is_frame()) { return; } if (!node.context()->is_root()) { - if (node->is_group_input_node() || node->is_group_output_node()) { + if (node->is_group_input() || node->is_group_output()) { return; } } @@ -345,22 +346,22 @@ std::string DerivedNodeTree::to_dot() const Vector<std::string> input_names; Vector<std::string> output_names; - for (const InputSocketRef *socket : node->inputs()) { + for (const bNodeSocket *socket : node->input_sockets()) { if (socket->is_available()) { - input_names.append(socket->name()); + input_names.append(socket->name); } } - for (const OutputSocketRef *socket : node->outputs()) { + for (const bNodeSocket *socket : node->output_sockets()) { if (socket->is_available()) { - output_names.append(socket->name()); + output_names.append(socket->name); } } dot::NodeWithSocketsRef dot_node_with_sockets = dot::NodeWithSocketsRef( - dot_node, node->name(), input_names, output_names); + dot_node, node->name, input_names, output_names); int input_index = 0; - for (const InputSocketRef *socket : node->inputs()) { + for (const bNodeSocket *socket : node->input_sockets()) { if (socket->is_available()) { dot_input_sockets.add_new(DInputSocket{node.context(), socket}, dot_node_with_sockets.input(input_index)); @@ -368,7 +369,7 @@ std::string DerivedNodeTree::to_dot() const } } int output_index = 0; - for (const OutputSocketRef *socket : node->outputs()) { + for (const bNodeSocket *socket : node->output_sockets()) { if (socket->is_available()) { dot_output_sockets.add_new(DOutputSocket{node.context(), socket}, dot_node_with_sockets.output(output_index)); @@ -392,7 +393,7 @@ std::string DerivedNodeTree::to_dot() const } } dot::Node &dot_node = *dot_floating_inputs.lookup_or_add_cb(from_socket, [&]() { - dot::Node &dot_node = digraph.new_node(from_socket->name()); + dot::Node &dot_node = digraph.new_node(from_socket->name); dot_node.set_background_color("white"); dot_node.set_shape(dot::Attr_shape::Ellipse); dot_node.set_parent_cluster( diff --git a/source/blender/nodes/intern/geometry_nodes_eval_log.cc b/source/blender/nodes/intern/geometry_nodes_eval_log.cc deleted file mode 100644 index 55930dcb1ee..00000000000 --- a/source/blender/nodes/intern/geometry_nodes_eval_log.cc +++ /dev/null @@ -1,520 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "NOD_geometry_nodes_eval_log.hh" - -#include "BKE_curves.hh" -#include "BKE_geometry_set_instances.hh" - -#include "DNA_modifier_types.h" -#include "DNA_space_types.h" - -#include "FN_field_cpp_type.hh" - -#include "BLT_translation.h" - -#include <chrono> - -namespace blender::nodes::geometry_nodes_eval_log { - -using fn::FieldCPPType; -using fn::FieldInput; -using fn::GField; -using fn::ValueOrFieldCPPType; - -ModifierLog::ModifierLog(GeoLogger &logger) - : input_geometry_log_(std::move(logger.input_geometry_log_)), - output_geometry_log_(std::move(logger.output_geometry_log_)) -{ - root_tree_logs_ = allocator_.construct<TreeLog>(); - - LogByTreeContext log_by_tree_context; - - /* Combine all the local loggers that have been used by separate threads. */ - for (LocalGeoLogger &local_logger : logger) { - /* Take ownership of the allocator. */ - logger_allocators_.append(std::move(local_logger.allocator_)); - - for (ValueOfSockets &value_of_sockets : local_logger.values_) { - ValueLog *value_log = value_of_sockets.value.get(); - - /* Take centralized ownership of the logged value. It might be referenced by multiple - * sockets. */ - logged_values_.append(std::move(value_of_sockets.value)); - - for (const DSocket &socket : value_of_sockets.sockets) { - SocketLog &socket_log = this->lookup_or_add_socket_log(log_by_tree_context, socket); - socket_log.value_ = value_log; - } - } - - for (NodeWithWarning &node_with_warning : local_logger.node_warnings_) { - NodeLog &node_log = this->lookup_or_add_node_log(log_by_tree_context, - node_with_warning.node); - node_log.warnings_.append(node_with_warning.warning); - } - - for (NodeWithExecutionTime &node_with_exec_time : local_logger.node_exec_times_) { - NodeLog &node_log = this->lookup_or_add_node_log(log_by_tree_context, - node_with_exec_time.node); - node_log.exec_time_ = node_with_exec_time.exec_time; - } - - for (NodeWithDebugMessage &debug_message : local_logger.node_debug_messages_) { - NodeLog &node_log = this->lookup_or_add_node_log(log_by_tree_context, debug_message.node); - node_log.debug_messages_.append(debug_message.message); - } - - for (NodeWithUsedNamedAttribute &node_with_attribute_name : - local_logger.used_named_attributes_) { - NodeLog &node_log = this->lookup_or_add_node_log(log_by_tree_context, - node_with_attribute_name.node); - node_log.used_named_attributes_.append(std::move(node_with_attribute_name.attribute)); - } - } -} - -TreeLog &ModifierLog::lookup_or_add_tree_log(LogByTreeContext &log_by_tree_context, - const DTreeContext &tree_context) -{ - TreeLog *tree_log = log_by_tree_context.lookup_default(&tree_context, nullptr); - if (tree_log != nullptr) { - return *tree_log; - } - - const DTreeContext *parent_context = tree_context.parent_context(); - if (parent_context == nullptr) { - return *root_tree_logs_.get(); - } - TreeLog &parent_log = this->lookup_or_add_tree_log(log_by_tree_context, *parent_context); - destruct_ptr<TreeLog> owned_tree_log = allocator_.construct<TreeLog>(); - tree_log = owned_tree_log.get(); - log_by_tree_context.add_new(&tree_context, tree_log); - parent_log.child_logs_.add_new(tree_context.parent_node()->name(), std::move(owned_tree_log)); - return *tree_log; -} - -NodeLog &ModifierLog::lookup_or_add_node_log(LogByTreeContext &log_by_tree_context, DNode node) -{ - TreeLog &tree_log = this->lookup_or_add_tree_log(log_by_tree_context, *node.context()); - NodeLog &node_log = *tree_log.node_logs_.lookup_or_add_cb(node->name(), [&]() { - destruct_ptr<NodeLog> node_log = allocator_.construct<NodeLog>(); - node_log->input_logs_.resize(node->inputs().size()); - node_log->output_logs_.resize(node->outputs().size()); - return node_log; - }); - return node_log; -} - -SocketLog &ModifierLog::lookup_or_add_socket_log(LogByTreeContext &log_by_tree_context, - DSocket socket) -{ - NodeLog &node_log = this->lookup_or_add_node_log(log_by_tree_context, socket.node()); - MutableSpan<SocketLog> socket_logs = socket->is_input() ? node_log.input_logs_ : - node_log.output_logs_; - SocketLog &socket_log = socket_logs[socket->index()]; - return socket_log; -} - -void ModifierLog::foreach_node_log(FunctionRef<void(const NodeLog &)> fn) const -{ - if (root_tree_logs_) { - root_tree_logs_->foreach_node_log(fn); - } -} - -const GeometryValueLog *ModifierLog::input_geometry_log() const -{ - return input_geometry_log_.get(); -} -const GeometryValueLog *ModifierLog::output_geometry_log() const -{ - return output_geometry_log_.get(); -} - -const NodeLog *TreeLog::lookup_node_log(StringRef node_name) const -{ - const destruct_ptr<NodeLog> *node_log = node_logs_.lookup_ptr_as(node_name); - if (node_log == nullptr) { - return nullptr; - } - return node_log->get(); -} - -const NodeLog *TreeLog::lookup_node_log(const bNode &node) const -{ - return this->lookup_node_log(node.name); -} - -const TreeLog *TreeLog::lookup_child_log(StringRef node_name) const -{ - const destruct_ptr<TreeLog> *tree_log = child_logs_.lookup_ptr_as(node_name); - if (tree_log == nullptr) { - return nullptr; - } - return tree_log->get(); -} - -void TreeLog::foreach_node_log(FunctionRef<void(const NodeLog &)> fn) const -{ - for (auto node_log : node_logs_.items()) { - fn(*node_log.value); - } - - for (auto child : child_logs_.items()) { - child.value->foreach_node_log(fn); - } -} - -const SocketLog *NodeLog::lookup_socket_log(eNodeSocketInOut in_out, int index) const -{ - BLI_assert(index >= 0); - Span<SocketLog> socket_logs = (in_out == SOCK_IN) ? input_logs_ : output_logs_; - if (index >= socket_logs.size()) { - return nullptr; - } - return &socket_logs[index]; -} - -const SocketLog *NodeLog::lookup_socket_log(const bNode &node, const bNodeSocket &socket) const -{ - ListBase sockets = socket.in_out == SOCK_IN ? node.inputs : node.outputs; - int index = BLI_findindex(&sockets, &socket); - return this->lookup_socket_log((eNodeSocketInOut)socket.in_out, index); -} - -GFieldValueLog::GFieldValueLog(fn::GField field, bool log_full_field) : type_(field.cpp_type()) -{ - const std::shared_ptr<const fn::FieldInputs> &field_input_nodes = field.node().field_inputs(); - - /* Put the deduplicated field inputs into a vector so that they can be sorted below. */ - Vector<std::reference_wrapper<const FieldInput>> field_inputs; - if (field_input_nodes) { - field_inputs.extend(field_input_nodes->deduplicated_nodes.begin(), - field_input_nodes->deduplicated_nodes.end()); - } - - std::sort( - field_inputs.begin(), field_inputs.end(), [](const FieldInput &a, const FieldInput &b) { - const int index_a = (int)a.category(); - const int index_b = (int)b.category(); - if (index_a == index_b) { - return a.socket_inspection_name().size() < b.socket_inspection_name().size(); - } - return index_a < index_b; - }); - - for (const FieldInput &field_input : field_inputs) { - input_tooltips_.append(field_input.socket_inspection_name()); - } - - if (log_full_field) { - field_ = std::move(field); - } -} - -GeometryValueLog::GeometryValueLog(const GeometrySet &geometry_set, bool log_full_geometry) -{ - static std::array all_component_types = {GEO_COMPONENT_TYPE_CURVE, - GEO_COMPONENT_TYPE_INSTANCES, - GEO_COMPONENT_TYPE_MESH, - GEO_COMPONENT_TYPE_POINT_CLOUD, - GEO_COMPONENT_TYPE_VOLUME}; - - /* Keep track handled attribute names to make sure that we do not return the same name twice. - * Currently #GeometrySet::attribute_foreach does not do that. Note that this will merge - * attributes with the same name but different domains or data types on separate components. */ - Set<StringRef> names; - - geometry_set.attribute_foreach( - all_component_types, - true, - [&](const bke::AttributeIDRef &attribute_id, - const bke::AttributeMetaData &meta_data, - const GeometryComponent &UNUSED(component)) { - if (attribute_id.is_named() && names.add(attribute_id.name())) { - this->attributes_.append({attribute_id.name(), meta_data.domain, meta_data.data_type}); - } - }); - - for (const GeometryComponent *component : geometry_set.get_components_for_read()) { - component_types_.append(component->type()); - switch (component->type()) { - case GEO_COMPONENT_TYPE_MESH: { - const MeshComponent &mesh_component = *(const MeshComponent *)component; - MeshInfo &info = this->mesh_info.emplace(); - info.verts_num = mesh_component.attribute_domain_size(ATTR_DOMAIN_POINT); - info.edges_num = mesh_component.attribute_domain_size(ATTR_DOMAIN_EDGE); - info.faces_num = mesh_component.attribute_domain_size(ATTR_DOMAIN_FACE); - break; - } - case GEO_COMPONENT_TYPE_CURVE: { - const CurveComponent &curve_component = *(const CurveComponent *)component; - CurveInfo &info = this->curve_info.emplace(); - info.splines_num = curve_component.attribute_domain_size(ATTR_DOMAIN_CURVE); - break; - } - case GEO_COMPONENT_TYPE_POINT_CLOUD: { - const PointCloudComponent &pointcloud_component = *(const PointCloudComponent *)component; - PointCloudInfo &info = this->pointcloud_info.emplace(); - info.points_num = pointcloud_component.attribute_domain_size(ATTR_DOMAIN_POINT); - break; - } - case GEO_COMPONENT_TYPE_INSTANCES: { - const InstancesComponent &instances_component = *(const InstancesComponent *)component; - InstancesInfo &info = this->instances_info.emplace(); - info.instances_num = instances_component.instances_num(); - break; - } - case GEO_COMPONENT_TYPE_EDIT: { - const GeometryComponentEditData &edit_component = *( - const GeometryComponentEditData *)component; - if (const bke::CurvesEditHints *curve_edit_hints = - edit_component.curves_edit_hints_.get()) { - EditDataInfo &info = this->edit_data_info.emplace(); - info.has_deform_matrices = curve_edit_hints->deform_mats.has_value(); - info.has_deformed_positions = curve_edit_hints->positions.has_value(); - } - break; - } - case GEO_COMPONENT_TYPE_VOLUME: { - break; - } - } - } - if (log_full_geometry) { - full_geometry_ = std::make_unique<GeometrySet>(geometry_set); - full_geometry_->ensure_owns_direct_data(); - } -} - -Vector<const GeometryAttributeInfo *> NodeLog::lookup_available_attributes() const -{ - Vector<const GeometryAttributeInfo *> attributes; - Set<StringRef> names; - for (const SocketLog &socket_log : input_logs_) { - const ValueLog *value_log = socket_log.value(); - if (const GeometryValueLog *geo_value_log = dynamic_cast<const GeometryValueLog *>( - value_log)) { - for (const GeometryAttributeInfo &attribute : geo_value_log->attributes()) { - if (names.add(attribute.name)) { - attributes.append(&attribute); - } - } - } - } - return attributes; -} - -const ModifierLog *ModifierLog::find_root_by_node_editor_context(const SpaceNode &snode) -{ - if (snode.id == nullptr) { - return nullptr; - } - if (GS(snode.id->name) != ID_OB) { - return nullptr; - } - Object *object = (Object *)snode.id; - LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) { - if (md->type == eModifierType_Nodes) { - NodesModifierData *nmd = (NodesModifierData *)md; - if (nmd->node_group == snode.nodetree) { - return (ModifierLog *)nmd->runtime_eval_log; - } - } - } - return nullptr; -} - -const TreeLog *ModifierLog::find_tree_by_node_editor_context(const SpaceNode &snode) -{ - const ModifierLog *eval_log = ModifierLog::find_root_by_node_editor_context(snode); - if (eval_log == nullptr) { - return nullptr; - } - Vector<bNodeTreePath *> tree_path_vec = snode.treepath; - if (tree_path_vec.is_empty()) { - return nullptr; - } - TreeLog *current = eval_log->root_tree_logs_.get(); - for (bNodeTreePath *path : tree_path_vec.as_span().drop_front(1)) { - destruct_ptr<TreeLog> *tree_log = current->child_logs_.lookup_ptr_as(path->node_name); - if (tree_log == nullptr) { - return nullptr; - } - current = tree_log->get(); - } - return current; -} - -const NodeLog *ModifierLog::find_node_by_node_editor_context(const SpaceNode &snode, - const bNode &node) -{ - const TreeLog *tree_log = ModifierLog::find_tree_by_node_editor_context(snode); - if (tree_log == nullptr) { - return nullptr; - } - return tree_log->lookup_node_log(node); -} - -const NodeLog *ModifierLog::find_node_by_node_editor_context(const SpaceNode &snode, - const StringRef node_name) -{ - const TreeLog *tree_log = ModifierLog::find_tree_by_node_editor_context(snode); - if (tree_log == nullptr) { - return nullptr; - } - return tree_log->lookup_node_log(node_name); -} - -const SocketLog *ModifierLog::find_socket_by_node_editor_context(const SpaceNode &snode, - const bNode &node, - const bNodeSocket &socket) -{ - const NodeLog *node_log = ModifierLog::find_node_by_node_editor_context(snode, node); - if (node_log == nullptr) { - return nullptr; - } - return node_log->lookup_socket_log(node, socket); -} - -const NodeLog *ModifierLog::find_node_by_spreadsheet_editor_context( - const SpaceSpreadsheet &sspreadsheet) -{ - Vector<SpreadsheetContext *> context_path = sspreadsheet.context_path; - if (context_path.size() <= 2) { - return nullptr; - } - if (context_path[0]->type != SPREADSHEET_CONTEXT_OBJECT) { - return nullptr; - } - if (context_path[1]->type != SPREADSHEET_CONTEXT_MODIFIER) { - return nullptr; - } - for (SpreadsheetContext *context : context_path.as_span().drop_front(2)) { - if (context->type != SPREADSHEET_CONTEXT_NODE) { - return nullptr; - } - } - Span<SpreadsheetContextNode *> node_contexts = - context_path.as_span().drop_front(2).cast<SpreadsheetContextNode *>(); - - Object *object = ((SpreadsheetContextObject *)context_path[0])->object; - StringRefNull modifier_name = ((SpreadsheetContextModifier *)context_path[1])->modifier_name; - if (object == nullptr) { - return nullptr; - } - - const ModifierLog *eval_log = nullptr; - LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) { - if (md->type == eModifierType_Nodes) { - if (md->name == modifier_name) { - NodesModifierData *nmd = (NodesModifierData *)md; - eval_log = (const ModifierLog *)nmd->runtime_eval_log; - break; - } - } - } - if (eval_log == nullptr) { - return nullptr; - } - - const TreeLog *tree_log = &eval_log->root_tree(); - for (SpreadsheetContextNode *context : node_contexts.drop_back(1)) { - tree_log = tree_log->lookup_child_log(context->node_name); - if (tree_log == nullptr) { - return nullptr; - } - } - const NodeLog *node_log = tree_log->lookup_node_log(node_contexts.last()->node_name); - return node_log; -} - -void LocalGeoLogger::log_value_for_sockets(Span<DSocket> sockets, GPointer value) -{ - const CPPType &type = *value.type(); - Span<DSocket> copied_sockets = allocator_->construct_array_copy(sockets); - if (type.is<GeometrySet>()) { - bool log_full_geometry = false; - for (const DSocket &socket : sockets) { - if (main_logger_->log_full_sockets_.contains(socket)) { - log_full_geometry = true; - break; - } - } - - const GeometrySet &geometry_set = *value.get<GeometrySet>(); - destruct_ptr<GeometryValueLog> value_log = allocator_->construct<GeometryValueLog>( - geometry_set, log_full_geometry); - values_.append({copied_sockets, std::move(value_log)}); - } - else if (const ValueOrFieldCPPType *value_or_field_type = - dynamic_cast<const ValueOrFieldCPPType *>(&type)) { - const void *value_or_field = value.get(); - if (value_or_field_type->is_field(value_or_field)) { - GField field = *value_or_field_type->get_field_ptr(value_or_field); - bool log_full_field = false; - if (!field.node().depends_on_input()) { - /* Always log constant fields so that their value can be shown in socket inspection. - * In the future we can also evaluate the field here and only store the value. */ - log_full_field = true; - } - if (!log_full_field) { - for (const DSocket &socket : sockets) { - if (main_logger_->log_full_sockets_.contains(socket)) { - log_full_field = true; - break; - } - } - } - destruct_ptr<GFieldValueLog> value_log = allocator_->construct<GFieldValueLog>( - std::move(field), log_full_field); - values_.append({copied_sockets, std::move(value_log)}); - } - else { - const CPPType &base_type = value_or_field_type->base_type(); - const void *value = value_or_field_type->get_value_ptr(value_or_field); - void *buffer = allocator_->allocate(base_type.size(), base_type.alignment()); - base_type.copy_construct(value, buffer); - destruct_ptr<GenericValueLog> value_log = allocator_->construct<GenericValueLog>( - GMutablePointer{base_type, buffer}); - values_.append({copied_sockets, std::move(value_log)}); - } - } - else { - void *buffer = allocator_->allocate(type.size(), type.alignment()); - type.copy_construct(value.get(), buffer); - destruct_ptr<GenericValueLog> value_log = allocator_->construct<GenericValueLog>( - GMutablePointer{type, buffer}); - values_.append({copied_sockets, std::move(value_log)}); - } -} - -void LocalGeoLogger::log_multi_value_socket(DSocket socket, Span<GPointer> values) -{ - /* Doesn't have to be logged currently. */ - UNUSED_VARS(socket, values); -} - -void LocalGeoLogger::log_node_warning(DNode node, NodeWarningType type, std::string message) -{ - node_warnings_.append({node, {type, std::move(message)}}); -} - -void LocalGeoLogger::log_execution_time(DNode node, std::chrono::microseconds exec_time) -{ - node_exec_times_.append({node, exec_time}); -} - -void LocalGeoLogger::log_used_named_attribute(DNode node, - std::string attribute_name, - eNamedAttrUsage usage) -{ - used_named_attributes_.append({node, {std::move(attribute_name), usage}}); -} - -void LocalGeoLogger::log_debug_message(DNode node, std::string message) -{ - node_debug_messages_.append({node, std::move(message)}); -} - -} // namespace blender::nodes::geometry_nodes_eval_log diff --git a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc new file mode 100644 index 00000000000..cb296cdd93f --- /dev/null +++ b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc @@ -0,0 +1,1423 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** + * This file mainly converts a #bNodeTree into a lazy-function graph. This generally works by + * creating a lazy-function for every node, which is then put into the lazy-function graph. Then + * the nodes in the new graph are linked based on links in the original #bNodeTree. Some additional + * nodes are inserted for things like type conversions and multi-input sockets. + * + * Currently, lazy-functions are even created for nodes that don't strictly require it, like + * reroutes or muted nodes. In the future we could avoid that at the cost of additional code + * complexity. So far, this does not seem to be a performance issue. + */ + +#include "NOD_geometry_exec.hh" +#include "NOD_geometry_nodes_lazy_function.hh" +#include "NOD_multi_function.hh" +#include "NOD_node_declaration.hh" + +#include "BLI_lazy_threading.hh" +#include "BLI_map.hh" + +#include "DNA_ID.h" + +#include "BKE_compute_contexts.hh" +#include "BKE_geometry_set.hh" +#include "BKE_type_conversions.hh" + +#include "FN_field_cpp_type.hh" +#include "FN_lazy_function_graph_executor.hh" + +namespace blender::nodes { + +using fn::ValueOrField; +using fn::ValueOrFieldCPPType; +using namespace fn::multi_function_types; + +static const CPPType *get_socket_cpp_type(const bNodeSocketType &typeinfo) +{ + const CPPType *type = typeinfo.geometry_nodes_cpp_type; + if (type == nullptr) { + return nullptr; + } + BLI_assert(type->has_special_member_functions()); + return type; +} + +static const CPPType *get_socket_cpp_type(const bNodeSocket &socket) +{ + return get_socket_cpp_type(*socket.typeinfo); +} + +static const CPPType *get_vector_type(const CPPType &type) +{ + /* This could be generalized in the future. For now we only support a small set of vectors. */ + if (type.is<GeometrySet>()) { + return &CPPType::get<Vector<GeometrySet>>(); + } + if (type.is<ValueOrField<std::string>>()) { + return &CPPType::get<Vector<ValueOrField<std::string>>>(); + } + return nullptr; +} + +/** + * Checks which sockets of the node are available and creates corresponding inputs/outputs on the + * lazy-function. + */ +static void lazy_function_interface_from_node(const bNode &node, + Vector<const bNodeSocket *> &r_used_inputs, + Vector<const bNodeSocket *> &r_used_outputs, + Vector<lf::Input> &r_inputs, + Vector<lf::Output> &r_outputs) +{ + const bool is_muted = node.is_muted(); + const bool supports_laziness = node.typeinfo->geometry_node_execute_supports_laziness || + node.is_group(); + const lf::ValueUsage input_usage = supports_laziness ? lf::ValueUsage::Maybe : + lf::ValueUsage::Used; + for (const bNodeSocket *socket : node.input_sockets()) { + if (!socket->is_available()) { + continue; + } + const CPPType *type = get_socket_cpp_type(*socket); + if (type == nullptr) { + continue; + } + if (socket->is_multi_input() && !is_muted) { + type = get_vector_type(*type); + } + r_inputs.append({socket->identifier, *type, input_usage}); + r_used_inputs.append(socket); + } + for (const bNodeSocket *socket : node.output_sockets()) { + if (!socket->is_available()) { + continue; + } + const CPPType *type = get_socket_cpp_type(*socket); + if (type == nullptr) { + continue; + } + r_outputs.append({socket->identifier, *type}); + r_used_outputs.append(socket); + } +} + +/** + * Used for most normal geometry nodes like Subdivision Surface and Set Position. + */ +class LazyFunctionForGeometryNode : public LazyFunction { + private: + const bNode &node_; + + public: + LazyFunctionForGeometryNode(const bNode &node, + Vector<const bNodeSocket *> &r_used_inputs, + Vector<const bNodeSocket *> &r_used_outputs) + : node_(node) + { + BLI_assert(node.typeinfo->geometry_node_execute != nullptr); + debug_name_ = node.name; + lazy_function_interface_from_node(node, r_used_inputs, r_used_outputs, inputs_, outputs_); + } + + void execute_impl(lf::Params ¶ms, const lf::Context &context) const override + { + GeoNodesLFUserData *user_data = dynamic_cast<GeoNodesLFUserData *>(context.user_data); + BLI_assert(user_data != nullptr); + + GeoNodeExecParams geo_params{node_, params, context}; + + geo_eval_log::TimePoint start_time = geo_eval_log::Clock::now(); + node_.typeinfo->geometry_node_execute(geo_params); + geo_eval_log::TimePoint end_time = geo_eval_log::Clock::now(); + + if (geo_eval_log::GeoModifierLog *modifier_log = user_data->modifier_data->eval_log) { + geo_eval_log::GeoTreeLogger &tree_logger = modifier_log->get_local_tree_logger( + *user_data->compute_context); + tree_logger.node_execution_times.append( + {tree_logger.allocator->copy_string(node_.name), start_time, end_time}); + } + } +}; + +/** + * Used to gather all inputs of a multi-input socket. A separate node is necessary because + * multi-inputs are not supported in lazy-function graphs. + */ +class LazyFunctionForMultiInput : public LazyFunction { + private: + const CPPType *base_type_; + + public: + LazyFunctionForMultiInput(const bNodeSocket &socket) + { + debug_name_ = "Multi Input"; + base_type_ = get_socket_cpp_type(socket); + BLI_assert(base_type_ != nullptr); + BLI_assert(socket.is_multi_input()); + for (const bNodeLink *link : socket.directly_linked_links()) { + if (!link->is_muted()) { + inputs_.append({"Input", *base_type_}); + } + } + const CPPType *vector_type = get_vector_type(*base_type_); + BLI_assert(vector_type != nullptr); + outputs_.append({"Output", *vector_type}); + } + + void execute_impl(lf::Params ¶ms, const lf::Context &UNUSED(context)) const override + { + /* Currently we only have multi-inputs for geometry and string sockets. This could be + * generalized in the future. */ + base_type_->to_static_type_tag<GeometrySet, ValueOrField<std::string>>([&](auto type_tag) { + using T = typename decltype(type_tag)::type; + if constexpr (std::is_void_v<T>) { + /* This type is not supported in this node for now. */ + BLI_assert_unreachable(); + } + else { + void *output_ptr = params.get_output_data_ptr(0); + Vector<T> &values = *new (output_ptr) Vector<T>(); + for (const int i : inputs_.index_range()) { + values.append(params.extract_input<T>(i)); + } + params.output_set(0); + } + }); + } +}; + +/** + * Simple lazy-function that just forwards the input. + */ +class LazyFunctionForRerouteNode : public LazyFunction { + public: + LazyFunctionForRerouteNode(const CPPType &type) + { + debug_name_ = "Reroute"; + inputs_.append({"Input", type}); + outputs_.append({"Output", type}); + } + + void execute_impl(lf::Params ¶ms, const lf::Context &UNUSED(context)) const override + { + void *input_value = params.try_get_input_data_ptr(0); + void *output_value = params.get_output_data_ptr(0); + BLI_assert(input_value != nullptr); + BLI_assert(output_value != nullptr); + const CPPType &type = *inputs_[0].type; + type.move_construct(input_value, output_value); + params.output_set(0); + } +}; + +/** + * Lazy functions for nodes whose type cannot be found. An undefined function just outputs default + * values. It's useful to have so other parts of the conversion don't have to care about undefined + * nodes. + */ +class LazyFunctionForUndefinedNode : public LazyFunction { + public: + LazyFunctionForUndefinedNode(const bNode &node, Vector<const bNodeSocket *> &r_used_outputs) + { + debug_name_ = "Undefined"; + Vector<const bNodeSocket *> dummy_used_inputs; + Vector<lf::Input> dummy_inputs; + lazy_function_interface_from_node( + node, dummy_used_inputs, r_used_outputs, dummy_inputs, outputs_); + } + + void execute_impl(lf::Params ¶ms, const lf::Context &UNUSED(context)) const override + { + params.set_default_remaining_outputs(); + } +}; + +/** + * Executes a multi-function. If all inputs are single values, the results will also be single + * values. If any input is a field, the outputs will also be fields. + */ +static void execute_multi_function_on_value_or_field( + const MultiFunction &fn, + const std::shared_ptr<MultiFunction> &owned_fn, + const Span<const ValueOrFieldCPPType *> input_types, + const Span<const ValueOrFieldCPPType *> output_types, + const Span<const void *> input_values, + const Span<void *> output_values) +{ + BLI_assert(fn.param_amount() == input_types.size() + output_types.size()); + BLI_assert(input_types.size() == input_values.size()); + BLI_assert(output_types.size() == output_values.size()); + + /* Check if any input is a field. */ + bool any_input_is_field = false; + for (const int i : input_types.index_range()) { + const ValueOrFieldCPPType &type = *input_types[i]; + const void *value_or_field = input_values[i]; + if (type.is_field(value_or_field)) { + any_input_is_field = true; + break; + } + } + + if (any_input_is_field) { + /* Convert all inputs into fields, so that they can be used as input in the new field. */ + Vector<GField> input_fields; + for (const int i : input_types.index_range()) { + const ValueOrFieldCPPType &type = *input_types[i]; + const void *value_or_field = input_values[i]; + input_fields.append(type.as_field(value_or_field)); + } + + /* Construct the new field node. */ + std::shared_ptr<fn::FieldOperation> operation; + if (owned_fn) { + operation = std::make_shared<fn::FieldOperation>(owned_fn, std::move(input_fields)); + } + else { + operation = std::make_shared<fn::FieldOperation>(fn, std::move(input_fields)); + } + + /* Store the new fields in the output. */ + for (const int i : output_types.index_range()) { + const ValueOrFieldCPPType &type = *output_types[i]; + void *value_or_field = output_values[i]; + type.construct_from_field(value_or_field, GField{operation, i}); + } + } + else { + /* In this case, the multi-function is evaluated directly. */ + MFParamsBuilder params{fn, 1}; + MFContextBuilder context; + + for (const int i : input_types.index_range()) { + const ValueOrFieldCPPType &type = *input_types[i]; + const CPPType &base_type = type.base_type(); + const void *value_or_field = input_values[i]; + const void *value = type.get_value_ptr(value_or_field); + params.add_readonly_single_input(GVArray::ForSingleRef(base_type, 1, value)); + } + for (const int i : output_types.index_range()) { + const ValueOrFieldCPPType &type = *output_types[i]; + const CPPType &base_type = type.base_type(); + void *value_or_field = output_values[i]; + type.default_construct(value_or_field); + void *value = type.get_value_ptr(value_or_field); + base_type.destruct(value); + params.add_uninitialized_single_output(GMutableSpan{base_type, value, 1}); + } + fn.call(IndexRange(1), params, context); + } +} + +/** + * Behavior of muted nodes: + * - Some inputs are forwarded to outputs without changes. + * - Some inputs are converted to a different type which becomes the output. + * - Some outputs are value initialized because they don't have a corresponding input. + */ +class LazyFunctionForMutedNode : public LazyFunction { + private: + Array<int> input_by_output_index_; + + public: + LazyFunctionForMutedNode(const bNode &node, + Vector<const bNodeSocket *> &r_used_inputs, + Vector<const bNodeSocket *> &r_used_outputs) + { + debug_name_ = "Muted"; + lazy_function_interface_from_node(node, r_used_inputs, r_used_outputs, inputs_, outputs_); + for (lf::Input &fn_input : inputs_) { + fn_input.usage = lf::ValueUsage::Maybe; + } + + for (lf::Input &fn_input : inputs_) { + fn_input.usage = lf::ValueUsage::Unused; + } + + input_by_output_index_.reinitialize(outputs_.size()); + input_by_output_index_.fill(-1); + for (const bNodeLink *internal_link : node.internal_links_span()) { + const int input_i = r_used_inputs.first_index_of_try(internal_link->fromsock); + const int output_i = r_used_outputs.first_index_of_try(internal_link->tosock); + if (ELEM(-1, input_i, output_i)) { + continue; + } + input_by_output_index_[output_i] = input_i; + inputs_[input_i].usage = lf::ValueUsage::Maybe; + } + } + + void execute_impl(lf::Params ¶ms, const lf::Context &UNUSED(context)) const override + { + for (const int output_i : outputs_.index_range()) { + if (params.output_was_set(output_i)) { + continue; + } + const CPPType &output_type = *outputs_[output_i].type; + void *output_value = params.get_output_data_ptr(output_i); + const int input_i = input_by_output_index_[output_i]; + if (input_i == -1) { + /* The output does not have a corresponding input. */ + output_type.value_initialize(output_value); + params.output_set(output_i); + continue; + } + const void *input_value = params.try_get_input_data_ptr_or_request(input_i); + if (input_value == nullptr) { + continue; + } + const CPPType &input_type = *inputs_[input_i].type; + if (input_type == output_type) { + /* Forward the value as is. */ + input_type.copy_construct(input_value, output_value); + params.output_set(output_i); + continue; + } + /* Perform a type conversion and then format the value. */ + const bke::DataTypeConversions &conversions = bke::get_implicit_type_conversions(); + const auto *from_field_type = dynamic_cast<const ValueOrFieldCPPType *>(&input_type); + const auto *to_field_type = dynamic_cast<const ValueOrFieldCPPType *>(&output_type); + if (from_field_type != nullptr && to_field_type != nullptr) { + const CPPType &from_base_type = from_field_type->base_type(); + const CPPType &to_base_type = to_field_type->base_type(); + if (conversions.is_convertible(from_base_type, to_base_type)) { + const MultiFunction &multi_fn = *conversions.get_conversion_multi_function( + MFDataType::ForSingle(from_base_type), MFDataType::ForSingle(to_base_type)); + execute_multi_function_on_value_or_field( + multi_fn, {}, {from_field_type}, {to_field_type}, {input_value}, {output_value}); + } + params.output_set(output_i); + continue; + } + /* Use a value initialization if the conversion does not work. */ + output_type.value_initialize(output_value); + params.output_set(output_i); + } + } +}; + +/** + * Type conversions are generally implemented as multi-functions. This node checks if the input is + * a field or single value and outputs a field or single value respectively. + */ +class LazyFunctionForMultiFunctionConversion : public LazyFunction { + private: + const MultiFunction &fn_; + const ValueOrFieldCPPType &from_type_; + const ValueOrFieldCPPType &to_type_; + const Vector<const bNodeSocket *> target_sockets_; + + public: + LazyFunctionForMultiFunctionConversion(const MultiFunction &fn, + const ValueOrFieldCPPType &from, + const ValueOrFieldCPPType &to, + Vector<const bNodeSocket *> &&target_sockets) + : fn_(fn), from_type_(from), to_type_(to), target_sockets_(std::move(target_sockets)) + { + debug_name_ = "Convert"; + inputs_.append({"From", from}); + outputs_.append({"To", to}); + } + + void execute_impl(lf::Params ¶ms, const lf::Context &UNUSED(context)) const override + { + const void *from_value = params.try_get_input_data_ptr(0); + void *to_value = params.get_output_data_ptr(0); + BLI_assert(from_value != nullptr); + BLI_assert(to_value != nullptr); + + execute_multi_function_on_value_or_field( + fn_, {}, {&from_type_}, {&to_type_}, {from_value}, {to_value}); + + params.output_set(0); + } +}; + +/** + * This lazy-function wraps nodes that are implemented as multi-function (mostly math nodes). + */ +class LazyFunctionForMultiFunctionNode : public LazyFunction { + private: + const NodeMultiFunctions::Item fn_item_; + Vector<const ValueOrFieldCPPType *> input_types_; + Vector<const ValueOrFieldCPPType *> output_types_; + + public: + LazyFunctionForMultiFunctionNode(const bNode &node, + NodeMultiFunctions::Item fn_item, + Vector<const bNodeSocket *> &r_used_inputs, + Vector<const bNodeSocket *> &r_used_outputs) + : fn_item_(std::move(fn_item)) + { + BLI_assert(fn_item_.fn != nullptr); + debug_name_ = node.name; + lazy_function_interface_from_node(node, r_used_inputs, r_used_outputs, inputs_, outputs_); + for (const lf::Input &fn_input : inputs_) { + input_types_.append(dynamic_cast<const ValueOrFieldCPPType *>(fn_input.type)); + } + for (const lf::Output &fn_output : outputs_) { + output_types_.append(dynamic_cast<const ValueOrFieldCPPType *>(fn_output.type)); + } + } + + void execute_impl(lf::Params ¶ms, const lf::Context &UNUSED(context)) const override + { + Vector<const void *> input_values(inputs_.size()); + Vector<void *> output_values(outputs_.size()); + for (const int i : inputs_.index_range()) { + input_values[i] = params.try_get_input_data_ptr(i); + } + for (const int i : outputs_.index_range()) { + output_values[i] = params.get_output_data_ptr(i); + } + execute_multi_function_on_value_or_field( + *fn_item_.fn, fn_item_.owned_fn, input_types_, output_types_, input_values, output_values); + for (const int i : outputs_.index_range()) { + params.output_set(i); + } + } +}; + +/** + * Some sockets have non-trivial implicit inputs (e.g. the Position input of the Set Position + * node). Those are implemented as a separate node that outputs the value. + */ +class LazyFunctionForImplicitInput : public LazyFunction { + private: + /** + * The function that generates the implicit input. The passed in memory is uninitialized. + */ + std::function<void(void *)> init_fn_; + + public: + LazyFunctionForImplicitInput(const CPPType &type, std::function<void(void *)> init_fn) + : init_fn_(std::move(init_fn)) + { + debug_name_ = "Input"; + outputs_.append({"Output", type}); + } + + void execute_impl(lf::Params ¶ms, const lf::Context &UNUSED(context)) const override + { + void *value = params.get_output_data_ptr(0); + init_fn_(value); + params.output_set(0); + } +}; + +/** + * The viewer node does not have outputs. Instead it is executed because the executor knows that it + * has side effects. The side effect is that the inputs to the viewer are logged. + */ +class LazyFunctionForViewerNode : public LazyFunction { + private: + const bNode &bnode_; + /** The field is only logged when it is linked. */ + bool use_field_input_ = true; + + public: + LazyFunctionForViewerNode(const bNode &bnode, Vector<const bNodeSocket *> &r_used_inputs) + : bnode_(bnode) + { + debug_name_ = "Viewer"; + Vector<const bNodeSocket *> dummy_used_outputs; + lazy_function_interface_from_node(bnode, r_used_inputs, dummy_used_outputs, inputs_, outputs_); + if (!r_used_inputs[1]->is_directly_linked()) { + use_field_input_ = false; + r_used_inputs.pop_last(); + inputs_.pop_last(); + } + } + + void execute_impl(lf::Params ¶ms, const lf::Context &context) const override + { + GeoNodesLFUserData *user_data = dynamic_cast<GeoNodesLFUserData *>(context.user_data); + BLI_assert(user_data != nullptr); + + GeometrySet geometry = params.extract_input<GeometrySet>(0); + + GField field; + if (use_field_input_) { + const void *value_or_field = params.try_get_input_data_ptr(1); + BLI_assert(value_or_field != nullptr); + const ValueOrFieldCPPType &value_or_field_type = static_cast<const ValueOrFieldCPPType &>( + *inputs_[1].type); + field = value_or_field_type.as_field(value_or_field); + } + + geo_eval_log::GeoTreeLogger &tree_logger = + user_data->modifier_data->eval_log->get_local_tree_logger(*user_data->compute_context); + tree_logger.log_viewer_node(bnode_, geometry, field); + } +}; + +/** + * This lazy-function wraps a group node. Internally it just executes the lazy-function graph of + * the referenced group. + */ +class LazyFunctionForGroupNode : public LazyFunction { + private: + const bNode &group_node_; + bool has_many_nodes_ = false; + std::optional<GeometryNodesLazyFunctionLogger> lf_logger_; + std::optional<GeometryNodesLazyFunctionSideEffectProvider> lf_side_effect_provider_; + std::optional<lf::GraphExecutor> graph_executor_; + + public: + LazyFunctionForGroupNode(const bNode &group_node, + const GeometryNodesLazyFunctionGraphInfo &lf_graph_info, + Vector<const bNodeSocket *> &r_used_inputs, + Vector<const bNodeSocket *> &r_used_outputs) + : group_node_(group_node) + { + debug_name_ = group_node.name; + lazy_function_interface_from_node( + group_node, r_used_inputs, r_used_outputs, inputs_, outputs_); + + bNodeTree *group_btree = reinterpret_cast<bNodeTree *>(group_node_.id); + BLI_assert(group_btree != nullptr); + + has_many_nodes_ = lf_graph_info.num_inline_nodes_approximate > 1000; + + Vector<const lf::OutputSocket *> graph_inputs; + for (const lf::OutputSocket *socket : lf_graph_info.mapping.group_input_sockets) { + if (socket != nullptr) { + graph_inputs.append(socket); + } + } + Vector<const lf::InputSocket *> graph_outputs; + if (const bNode *group_output_bnode = group_btree->group_output_node()) { + for (const bNodeSocket *bsocket : group_output_bnode->input_sockets().drop_back(1)) { + const lf::Socket *socket = lf_graph_info.mapping.dummy_socket_map.lookup_default(bsocket, + nullptr); + if (socket != nullptr) { + graph_outputs.append(&socket->as_input()); + } + } + } + + lf_logger_.emplace(lf_graph_info); + lf_side_effect_provider_.emplace(); + graph_executor_.emplace(lf_graph_info.graph, + std::move(graph_inputs), + std::move(graph_outputs), + &*lf_logger_, + &*lf_side_effect_provider_); + } + + void execute_impl(lf::Params ¶ms, const lf::Context &context) const override + { + GeoNodesLFUserData *user_data = dynamic_cast<GeoNodesLFUserData *>(context.user_data); + BLI_assert(user_data != nullptr); + + if (has_many_nodes_) { + /* If the called node group has many nodes, it's likely that executing it takes a while even + * if every individual node is very small. */ + lazy_threading::send_hint(); + } + + /* The compute context changes when entering a node group. */ + bke::NodeGroupComputeContext compute_context{user_data->compute_context, group_node_.name}; + GeoNodesLFUserData group_user_data = *user_data; + group_user_data.compute_context = &compute_context; + + lf::Context group_context = context; + group_context.user_data = &group_user_data; + + graph_executor_->execute(params, group_context); + } + + void *init_storage(LinearAllocator<> &allocator) const override + { + return graph_executor_->init_storage(allocator); + } + + void destruct_storage(void *storage) const override + { + graph_executor_->destruct_storage(storage); + } +}; + +static GMutablePointer get_socket_default_value(LinearAllocator<> &allocator, + const bNodeSocket &bsocket) +{ + const bNodeSocketType &typeinfo = *bsocket.typeinfo; + const CPPType *type = get_socket_cpp_type(typeinfo); + if (type == nullptr) { + return {}; + } + void *buffer = allocator.allocate(type->size(), type->alignment()); + typeinfo.get_geometry_nodes_cpp_value(bsocket, buffer); + return {type, buffer}; +} + +/** + * Utility class to build a lazy-function graph based on a geometry nodes tree. + * This is mainly a separate class because it makes it easier to have variables that can be + * accessed by many functions. + */ +struct GeometryNodesLazyFunctionGraphBuilder { + private: + const bNodeTree &btree_; + GeometryNodesLazyFunctionGraphInfo *lf_graph_info_; + lf::Graph *lf_graph_; + GeometryNodeLazyFunctionGraphMapping *mapping_; + MultiValueMap<const bNodeSocket *, lf::InputSocket *> input_socket_map_; + Map<const bNodeSocket *, lf::OutputSocket *> output_socket_map_; + Map<const bNodeSocket *, lf::Node *> multi_input_socket_nodes_; + const bke::DataTypeConversions *conversions_; + + /** + * All group input nodes are combined into one dummy node in the lazy-function graph. + * If some input has an invalid type, it is ignored in the new graph. In this case null and -1 is + * used in the vectors below. + */ + Vector<const CPPType *> group_input_types_; + Vector<int> group_input_indices_; + lf::DummyNode *group_input_lf_node_; + + /** + * The output types or null if an output is invalid. Each group output node gets a separate + * corresponding dummy node in the new graph. + */ + Vector<const CPPType *> group_output_types_; + Vector<int> group_output_indices_; + + public: + GeometryNodesLazyFunctionGraphBuilder(const bNodeTree &btree, + GeometryNodesLazyFunctionGraphInfo &lf_graph_info) + : btree_(btree), lf_graph_info_(&lf_graph_info) + { + } + + void build() + { + btree_.ensure_topology_cache(); + + lf_graph_ = &lf_graph_info_->graph; + mapping_ = &lf_graph_info_->mapping; + conversions_ = &bke::get_implicit_type_conversions(); + + this->prepare_node_multi_functions(); + this->prepare_group_inputs(); + this->prepare_group_outputs(); + this->build_group_input_node(); + this->handle_nodes(); + this->handle_links(); + this->add_default_inputs(); + + lf_graph_->update_node_indices(); + lf_graph_info_->num_inline_nodes_approximate += lf_graph_->nodes().size(); + } + + private: + void prepare_node_multi_functions() + { + lf_graph_info_->node_multi_functions = std::make_unique<NodeMultiFunctions>(btree_); + } + + void prepare_group_inputs() + { + LISTBASE_FOREACH (const bNodeSocket *, interface_bsocket, &btree_.inputs) { + const CPPType *type = get_socket_cpp_type(*interface_bsocket->typeinfo); + if (type != nullptr) { + const int index = group_input_types_.append_and_get_index(type); + group_input_indices_.append(index); + } + else { + group_input_indices_.append(-1); + } + } + } + + void prepare_group_outputs() + { + LISTBASE_FOREACH (const bNodeSocket *, interface_bsocket, &btree_.outputs) { + const CPPType *type = get_socket_cpp_type(*interface_bsocket->typeinfo); + if (type != nullptr) { + const int index = group_output_types_.append_and_get_index(type); + group_output_indices_.append(index); + } + else { + group_output_indices_.append(-1); + } + } + } + + void build_group_input_node() + { + /* Create a dummy node for the group inputs. */ + group_input_lf_node_ = &lf_graph_->add_dummy({}, group_input_types_); + for (const int group_input_index : group_input_indices_) { + if (group_input_index == -1) { + mapping_->group_input_sockets.append(nullptr); + } + else { + mapping_->group_input_sockets.append(&group_input_lf_node_->output(group_input_index)); + } + } + } + + void handle_nodes() + { + /* Insert all nodes into the lazy function graph. */ + for (const bNode *bnode : btree_.all_nodes()) { + const bNodeType *node_type = bnode->typeinfo; + if (node_type == nullptr) { + continue; + } + if (bnode->is_muted()) { + this->handle_muted_node(*bnode); + continue; + } + switch (node_type->type) { + case NODE_FRAME: { + /* Ignored. */ + break; + } + case NODE_REROUTE: { + this->handle_reroute_node(*bnode); + break; + } + case NODE_GROUP_INPUT: { + this->handle_group_input_node(*bnode); + break; + } + case NODE_GROUP_OUTPUT: { + this->handle_group_output_node(*bnode); + break; + } + case NODE_CUSTOM_GROUP: + case NODE_GROUP: { + this->handle_group_node(*bnode); + break; + } + case GEO_NODE_VIEWER: { + this->handle_viewer_node(*bnode); + break; + } + default: { + if (node_type->geometry_node_execute) { + this->handle_geometry_node(*bnode); + break; + } + const NodeMultiFunctions::Item &fn_item = lf_graph_info_->node_multi_functions->try_get( + *bnode); + if (fn_item.fn != nullptr) { + this->handle_multi_function_node(*bnode, fn_item); + break; + } + if (node_type == &NodeTypeUndefined) { + this->handle_undefined_node(*bnode); + break; + } + /* Nodes that don't match any of the criteria above are just ignored. */ + break; + } + } + } + } + + void handle_muted_node(const bNode &bnode) + { + Vector<const bNodeSocket *> used_inputs; + Vector<const bNodeSocket *> used_outputs; + auto lazy_function = std::make_unique<LazyFunctionForMutedNode>( + bnode, used_inputs, used_outputs); + lf::Node &lf_node = lf_graph_->add_function(*lazy_function); + lf_graph_info_->functions.append(std::move(lazy_function)); + for (const int i : used_inputs.index_range()) { + const bNodeSocket &bsocket = *used_inputs[i]; + lf::InputSocket &lf_socket = lf_node.input(i); + input_socket_map_.add(&bsocket, &lf_socket); + mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket); + } + for (const int i : used_outputs.index_range()) { + const bNodeSocket &bsocket = *used_outputs[i]; + lf::OutputSocket &lf_socket = lf_node.output(i); + output_socket_map_.add_new(&bsocket, &lf_socket); + mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket); + } + } + + void handle_reroute_node(const bNode &bnode) + { + const bNodeSocket &input_bsocket = bnode.input_socket(0); + const bNodeSocket &output_bsocket = bnode.output_socket(0); + const CPPType *type = get_socket_cpp_type(input_bsocket); + if (type == nullptr) { + return; + } + + auto lazy_function = std::make_unique<LazyFunctionForRerouteNode>(*type); + lf::Node &lf_node = lf_graph_->add_function(*lazy_function); + lf_graph_info_->functions.append(std::move(lazy_function)); + + lf::InputSocket &lf_input = lf_node.input(0); + lf::OutputSocket &lf_output = lf_node.output(0); + input_socket_map_.add(&input_bsocket, &lf_input); + output_socket_map_.add_new(&output_bsocket, &lf_output); + mapping_->bsockets_by_lf_socket_map.add(&lf_input, &input_bsocket); + mapping_->bsockets_by_lf_socket_map.add(&lf_output, &output_bsocket); + } + + void handle_group_input_node(const bNode &bnode) + { + for (const int btree_index : group_input_indices_.index_range()) { + const int lf_index = group_input_indices_[btree_index]; + if (lf_index == -1) { + continue; + } + const bNodeSocket &bsocket = bnode.output_socket(btree_index); + lf::OutputSocket &lf_socket = group_input_lf_node_->output(lf_index); + output_socket_map_.add_new(&bsocket, &lf_socket); + mapping_->dummy_socket_map.add_new(&bsocket, &lf_socket); + mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket); + } + } + + void handle_group_output_node(const bNode &bnode) + { + lf::DummyNode &group_output_lf_node = lf_graph_->add_dummy(group_output_types_, {}); + for (const int btree_index : group_output_indices_.index_range()) { + const int lf_index = group_output_indices_[btree_index]; + if (lf_index == -1) { + continue; + } + const bNodeSocket &bsocket = bnode.input_socket(btree_index); + lf::InputSocket &lf_socket = group_output_lf_node.input(lf_index); + input_socket_map_.add(&bsocket, &lf_socket); + mapping_->dummy_socket_map.add(&bsocket, &lf_socket); + mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket); + } + } + + void handle_group_node(const bNode &bnode) + { + const bNodeTree *group_btree = reinterpret_cast<bNodeTree *>(bnode.id); + if (group_btree == nullptr) { + return; + } + const GeometryNodesLazyFunctionGraphInfo *group_lf_graph_info = + ensure_geometry_nodes_lazy_function_graph(*group_btree); + if (group_lf_graph_info == nullptr) { + return; + } + + Vector<const bNodeSocket *> used_inputs; + Vector<const bNodeSocket *> used_outputs; + auto lazy_function = std::make_unique<LazyFunctionForGroupNode>( + bnode, *group_lf_graph_info, used_inputs, used_outputs); + lf::FunctionNode &lf_node = lf_graph_->add_function(*lazy_function); + lf_graph_info_->functions.append(std::move(lazy_function)); + for (const int i : used_inputs.index_range()) { + const bNodeSocket &bsocket = *used_inputs[i]; + BLI_assert(!bsocket.is_multi_input()); + lf::InputSocket &lf_socket = lf_node.input(i); + input_socket_map_.add(&bsocket, &lf_socket); + mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket); + } + for (const int i : used_outputs.index_range()) { + const bNodeSocket &bsocket = *used_outputs[i]; + lf::OutputSocket &lf_socket = lf_node.output(i); + output_socket_map_.add_new(&bsocket, &lf_socket); + mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket); + } + mapping_->group_node_map.add(&bnode, &lf_node); + lf_graph_info_->num_inline_nodes_approximate += + group_lf_graph_info->num_inline_nodes_approximate; + } + + void handle_geometry_node(const bNode &bnode) + { + Vector<const bNodeSocket *> used_inputs; + Vector<const bNodeSocket *> used_outputs; + auto lazy_function = std::make_unique<LazyFunctionForGeometryNode>( + bnode, used_inputs, used_outputs); + lf::Node &lf_node = lf_graph_->add_function(*lazy_function); + lf_graph_info_->functions.append(std::move(lazy_function)); + + for (const int i : used_inputs.index_range()) { + const bNodeSocket &bsocket = *used_inputs[i]; + lf::InputSocket &lf_socket = lf_node.input(i); + + if (bsocket.is_multi_input()) { + auto multi_input_lazy_function = std::make_unique<LazyFunctionForMultiInput>(bsocket); + lf::Node &lf_multi_input_node = lf_graph_->add_function(*multi_input_lazy_function); + lf_graph_info_->functions.append(std::move(multi_input_lazy_function)); + lf_graph_->add_link(lf_multi_input_node.output(0), lf_socket); + multi_input_socket_nodes_.add_new(&bsocket, &lf_multi_input_node); + for (lf::InputSocket *lf_multi_input_socket : lf_multi_input_node.inputs()) { + mapping_->bsockets_by_lf_socket_map.add(lf_multi_input_socket, &bsocket); + } + } + else { + input_socket_map_.add(&bsocket, &lf_socket); + mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket); + } + } + for (const int i : used_outputs.index_range()) { + const bNodeSocket &bsocket = *used_outputs[i]; + lf::OutputSocket &lf_socket = lf_node.output(i); + output_socket_map_.add_new(&bsocket, &lf_socket); + mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket); + } + } + + void handle_multi_function_node(const bNode &bnode, const NodeMultiFunctions::Item &fn_item) + { + Vector<const bNodeSocket *> used_inputs; + Vector<const bNodeSocket *> used_outputs; + auto lazy_function = std::make_unique<LazyFunctionForMultiFunctionNode>( + bnode, fn_item, used_inputs, used_outputs); + lf::Node &lf_node = lf_graph_->add_function(*lazy_function); + lf_graph_info_->functions.append(std::move(lazy_function)); + + for (const int i : used_inputs.index_range()) { + const bNodeSocket &bsocket = *used_inputs[i]; + BLI_assert(!bsocket.is_multi_input()); + lf::InputSocket &lf_socket = lf_node.input(i); + input_socket_map_.add(&bsocket, &lf_socket); + mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket); + } + for (const int i : used_outputs.index_range()) { + const bNodeSocket &bsocket = *used_outputs[i]; + lf::OutputSocket &lf_socket = lf_node.output(i); + output_socket_map_.add(&bsocket, &lf_socket); + mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket); + } + } + + void handle_viewer_node(const bNode &bnode) + { + Vector<const bNodeSocket *> used_inputs; + auto lazy_function = std::make_unique<LazyFunctionForViewerNode>(bnode, used_inputs); + lf::FunctionNode &lf_node = lf_graph_->add_function(*lazy_function); + lf_graph_info_->functions.append(std::move(lazy_function)); + + for (const int i : used_inputs.index_range()) { + const bNodeSocket &bsocket = *used_inputs[i]; + lf::InputSocket &lf_socket = lf_node.input(i); + input_socket_map_.add(&bsocket, &lf_socket); + mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket); + } + + mapping_->viewer_node_map.add(&bnode, &lf_node); + } + + void handle_undefined_node(const bNode &bnode) + { + Vector<const bNodeSocket *> used_outputs; + auto lazy_function = std::make_unique<LazyFunctionForUndefinedNode>(bnode, used_outputs); + lf::FunctionNode &lf_node = lf_graph_->add_function(*lazy_function); + lf_graph_info_->functions.append(std::move(lazy_function)); + + for (const int i : used_outputs.index_range()) { + const bNodeSocket &bsocket = *used_outputs[i]; + lf::OutputSocket &lf_socket = lf_node.output(i); + output_socket_map_.add(&bsocket, &lf_socket); + mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket); + } + } + + void handle_links() + { + for (const auto item : output_socket_map_.items()) { + this->insert_links_from_socket(*item.key, *item.value); + } + } + + void insert_links_from_socket(const bNodeSocket &from_bsocket, lf::OutputSocket &from_lf_socket) + { + const Span<const bNodeLink *> links_from_bsocket = from_bsocket.directly_linked_links(); + + struct TypeWithLinks { + const CPPType *type; + Vector<const bNodeLink *> links; + }; + + /* Group available target sockets by type so that they can be handled together. */ + Vector<TypeWithLinks> types_with_links; + for (const bNodeLink *link : links_from_bsocket) { + if (link->is_muted()) { + continue; + } + if (!link->is_available()) { + continue; + } + const bNodeSocket &to_bsocket = *link->tosock; + const CPPType *to_type = get_socket_cpp_type(to_bsocket); + if (to_type == nullptr) { + continue; + } + bool inserted = false; + for (TypeWithLinks &types_with_links : types_with_links) { + if (types_with_links.type == to_type) { + types_with_links.links.append(link); + inserted = true; + break; + } + } + if (inserted) { + continue; + } + types_with_links.append({to_type, {link}}); + } + + for (const TypeWithLinks &type_with_links : types_with_links) { + const CPPType &to_type = *type_with_links.type; + const Span<const bNodeLink *> links = type_with_links.links; + + Vector<const bNodeSocket *> target_bsockets; + for (const bNodeLink *link : links) { + target_bsockets.append(link->tosock); + } + + lf::OutputSocket *converted_from_lf_socket = this->insert_type_conversion_if_necessary( + from_lf_socket, to_type, std::move(target_bsockets)); + + auto make_input_link_or_set_default = [&](lf::InputSocket &to_lf_socket) { + if (converted_from_lf_socket == nullptr) { + const void *default_value = to_type.default_value(); + to_lf_socket.set_default_value(default_value); + } + else { + lf_graph_->add_link(*converted_from_lf_socket, to_lf_socket); + } + }; + + for (const bNodeLink *link : links) { + const bNodeSocket &to_bsocket = *link->tosock; + if (to_bsocket.is_multi_input()) { + /* TODO: Cache this index on the link. */ + int link_index = 0; + for (const bNodeLink *multi_input_link : to_bsocket.directly_linked_links()) { + if (multi_input_link == link) { + break; + } + if (!multi_input_link->is_muted()) { + link_index++; + } + } + if (to_bsocket.owner_node().is_muted()) { + if (link_index == 0) { + for (lf::InputSocket *to_lf_socket : input_socket_map_.lookup(&to_bsocket)) { + make_input_link_or_set_default(*to_lf_socket); + } + } + } + else { + lf::Node *multi_input_lf_node = multi_input_socket_nodes_.lookup_default(&to_bsocket, + nullptr); + if (multi_input_lf_node == nullptr) { + continue; + } + make_input_link_or_set_default(multi_input_lf_node->input(link_index)); + } + } + else { + for (lf::InputSocket *to_lf_socket : input_socket_map_.lookup(&to_bsocket)) { + make_input_link_or_set_default(*to_lf_socket); + } + } + } + } + } + + lf::OutputSocket *insert_type_conversion_if_necessary( + lf::OutputSocket &from_socket, + const CPPType &to_type, + Vector<const bNodeSocket *> &&target_sockets) + { + const CPPType &from_type = from_socket.type(); + if (from_type == to_type) { + return &from_socket; + } + const auto *from_field_type = dynamic_cast<const ValueOrFieldCPPType *>(&from_type); + const auto *to_field_type = dynamic_cast<const ValueOrFieldCPPType *>(&to_type); + if (from_field_type != nullptr && to_field_type != nullptr) { + const CPPType &from_base_type = from_field_type->base_type(); + const CPPType &to_base_type = to_field_type->base_type(); + if (conversions_->is_convertible(from_base_type, to_base_type)) { + const MultiFunction &multi_fn = *conversions_->get_conversion_multi_function( + MFDataType::ForSingle(from_base_type), MFDataType::ForSingle(to_base_type)); + auto fn = std::make_unique<LazyFunctionForMultiFunctionConversion>( + multi_fn, *from_field_type, *to_field_type, std::move(target_sockets)); + lf::Node &conversion_node = lf_graph_->add_function(*fn); + lf_graph_info_->functions.append(std::move(fn)); + lf_graph_->add_link(from_socket, conversion_node.input(0)); + return &conversion_node.output(0); + } + } + return nullptr; + } + + void add_default_inputs() + { + for (auto item : input_socket_map_.items()) { + const bNodeSocket &bsocket = *item.key; + const Span<lf::InputSocket *> lf_sockets = item.value; + for (lf::InputSocket *lf_socket : lf_sockets) { + if (lf_socket->origin() != nullptr) { + /* Is linked already. */ + continue; + } + this->add_default_input(bsocket, *lf_socket); + } + } + } + + void add_default_input(const bNodeSocket &input_bsocket, lf::InputSocket &input_lf_socket) + { + if (this->try_add_implicit_input(input_bsocket, input_lf_socket)) { + return; + } + GMutablePointer value = get_socket_default_value(lf_graph_info_->allocator, input_bsocket); + if (value.get() == nullptr) { + /* Not possible to add a default value. */ + return; + } + input_lf_socket.set_default_value(value.get()); + if (!value.type()->is_trivially_destructible()) { + lf_graph_info_->values_to_destruct.append(value); + } + } + + bool try_add_implicit_input(const bNodeSocket &input_bsocket, lf::InputSocket &input_lf_socket) + { + const bNode &bnode = input_bsocket.owner_node(); + const NodeDeclaration *node_declaration = bnode.declaration(); + if (node_declaration == nullptr) { + return false; + } + const SocketDeclaration &socket_declaration = + *node_declaration->inputs()[input_bsocket.index()]; + if (socket_declaration.input_field_type() != InputSocketFieldType::Implicit) { + return false; + } + const CPPType &type = input_lf_socket.type(); + std::function<void(void *)> init_fn = this->get_implicit_input_init_function(bnode, + input_bsocket); + if (!init_fn) { + return false; + } + + auto lazy_function = std::make_unique<LazyFunctionForImplicitInput>(type, std::move(init_fn)); + lf::Node &lf_node = lf_graph_->add_function(*lazy_function); + lf_graph_info_->functions.append(std::move(lazy_function)); + lf_graph_->add_link(lf_node.output(0), input_lf_socket); + return true; + } + + std::function<void(void *)> get_implicit_input_init_function(const bNode &bnode, + const bNodeSocket &bsocket) + { + const bNodeSocketType &socket_type = *bsocket.typeinfo; + if (socket_type.type == SOCK_VECTOR) { + if (bnode.type == GEO_NODE_SET_CURVE_HANDLES) { + StringRef side = ((NodeGeometrySetCurveHandlePositions *)bnode.storage)->mode == + GEO_NODE_CURVE_HANDLE_LEFT ? + "handle_left" : + "handle_right"; + return [side](void *r_value) { + new (r_value) ValueOrField<float3>(bke::AttributeFieldInput::Create<float3>(side)); + }; + } + else if (bnode.type == GEO_NODE_EXTRUDE_MESH) { + return [](void *r_value) { + new (r_value) + ValueOrField<float3>(Field<float3>(std::make_shared<bke::NormalFieldInput>())); + }; + } + else { + return [](void *r_value) { + new (r_value) ValueOrField<float3>(bke::AttributeFieldInput::Create<float3>("position")); + }; + } + } + else if (socket_type.type == SOCK_INT) { + if (ELEM(bnode.type, FN_NODE_RANDOM_VALUE, GEO_NODE_INSTANCE_ON_POINTS)) { + return [](void *r_value) { + new (r_value) + ValueOrField<int>(Field<int>(std::make_shared<bke::IDAttributeFieldInput>())); + }; + } + else { + return [](void *r_value) { + new (r_value) ValueOrField<int>(Field<int>(std::make_shared<fn::IndexFieldInput>())); + }; + } + } + return {}; + } +}; + +const GeometryNodesLazyFunctionGraphInfo *ensure_geometry_nodes_lazy_function_graph( + const bNodeTree &btree) +{ + btree.ensure_topology_cache(); + if (btree.has_available_link_cycle()) { + return nullptr; + } + + std::unique_ptr<GeometryNodesLazyFunctionGraphInfo> &lf_graph_info_ptr = + btree.runtime->geometry_nodes_lazy_function_graph_info; + + if (lf_graph_info_ptr) { + return lf_graph_info_ptr.get(); + } + std::lock_guard lock{btree.runtime->geometry_nodes_lazy_function_graph_info_mutex}; + if (lf_graph_info_ptr) { + return lf_graph_info_ptr.get(); + } + + auto lf_graph_info = std::make_unique<GeometryNodesLazyFunctionGraphInfo>(); + GeometryNodesLazyFunctionGraphBuilder builder{btree, *lf_graph_info}; + builder.build(); + + lf_graph_info_ptr = std::move(lf_graph_info); + return lf_graph_info_ptr.get(); +} + +GeometryNodesLazyFunctionLogger::GeometryNodesLazyFunctionLogger( + const GeometryNodesLazyFunctionGraphInfo &lf_graph_info) + : lf_graph_info_(lf_graph_info) +{ +} + +void GeometryNodesLazyFunctionLogger::log_socket_value( + const fn::lazy_function::Socket &lf_socket, + const GPointer value, + const fn::lazy_function::Context &context) const +{ + const Span<const bNodeSocket *> bsockets = + lf_graph_info_.mapping.bsockets_by_lf_socket_map.lookup(&lf_socket); + if (bsockets.is_empty()) { + return; + } + + GeoNodesLFUserData *user_data = dynamic_cast<GeoNodesLFUserData *>(context.user_data); + BLI_assert(user_data != nullptr); + if (user_data->modifier_data->eval_log == nullptr) { + return; + } + geo_eval_log::GeoTreeLogger &tree_logger = + user_data->modifier_data->eval_log->get_local_tree_logger(*user_data->compute_context); + for (const bNodeSocket *bsocket : bsockets) { + /* Avoid logging to some sockets when the same value will also be logged to a linked socket. + * This reduces the number of logged values without losing information. */ + if (bsocket->is_input() && bsocket->is_directly_linked()) { + continue; + } + const bNode &bnode = bsocket->owner_node(); + if (bnode.is_reroute()) { + continue; + } + tree_logger.log_value(bsocket->owner_node(), *bsocket, value); + } +} + +static std::mutex dump_error_context_mutex; + +void GeometryNodesLazyFunctionLogger::dump_when_outputs_are_missing( + const lf::FunctionNode &node, + Span<const lf::OutputSocket *> missing_sockets, + const lf::Context &context) const +{ + std::lock_guard lock{dump_error_context_mutex}; + + GeoNodesLFUserData *user_data = dynamic_cast<GeoNodesLFUserData *>(context.user_data); + BLI_assert(user_data != nullptr); + user_data->compute_context->print_stack(std::cout, node.name()); + std::cout << "Missing outputs:\n"; + for (const lf::OutputSocket *socket : missing_sockets) { + std::cout << " " << socket->name() << "\n"; + } +} + +void GeometryNodesLazyFunctionLogger::dump_when_input_is_set_twice( + const lf::InputSocket &target_socket, + const lf::OutputSocket &from_socket, + const lf::Context &context) const +{ + std::lock_guard lock{dump_error_context_mutex}; + + std::stringstream ss; + ss << from_socket.node().name() << ":" << from_socket.name() << " -> " + << target_socket.node().name() << ":" << target_socket.name(); + + GeoNodesLFUserData *user_data = dynamic_cast<GeoNodesLFUserData *>(context.user_data); + BLI_assert(user_data != nullptr); + user_data->compute_context->print_stack(std::cout, ss.str()); +} + +Vector<const lf::FunctionNode *> GeometryNodesLazyFunctionSideEffectProvider:: + get_nodes_with_side_effects(const lf::Context &context) const +{ + GeoNodesLFUserData *user_data = dynamic_cast<GeoNodesLFUserData *>(context.user_data); + BLI_assert(user_data != nullptr); + const ComputeContextHash &context_hash = user_data->compute_context->hash(); + const GeoNodesModifierData &modifier_data = *user_data->modifier_data; + return modifier_data.side_effect_nodes->lookup(context_hash); +} + +GeometryNodesLazyFunctionGraphInfo::GeometryNodesLazyFunctionGraphInfo() = default; +GeometryNodesLazyFunctionGraphInfo::~GeometryNodesLazyFunctionGraphInfo() +{ + for (GMutablePointer &p : this->values_to_destruct) { + p.destruct(); + } +} + +[[maybe_unused]] static void add_thread_id_debug_message( + const GeometryNodesLazyFunctionGraphInfo &lf_graph_info, + const lf::FunctionNode &node, + const lf::Context &context) +{ + static std::atomic<int> thread_id_source = 0; + static thread_local const int thread_id = thread_id_source.fetch_add(1); + static thread_local const std::string thread_id_str = "Thread: " + std::to_string(thread_id); + + GeoNodesLFUserData *user_data = dynamic_cast<GeoNodesLFUserData *>(context.user_data); + BLI_assert(user_data != nullptr); + if (user_data->modifier_data->eval_log == nullptr) { + return; + } + geo_eval_log::GeoTreeLogger &tree_logger = + user_data->modifier_data->eval_log->get_local_tree_logger(*user_data->compute_context); + + /* Find corresponding node based on the socket mapping. */ + auto check_sockets = [&](const Span<const lf::Socket *> lf_sockets) { + for (const lf::Socket *lf_socket : lf_sockets) { + const Span<const bNodeSocket *> bsockets = + lf_graph_info.mapping.bsockets_by_lf_socket_map.lookup(lf_socket); + if (!bsockets.is_empty()) { + const bNodeSocket &bsocket = *bsockets[0]; + const bNode &bnode = bsocket.owner_node(); + tree_logger.debug_messages.append( + {tree_logger.allocator->copy_string(bnode.name), thread_id_str}); + return true; + } + } + return false; + }; + + if (check_sockets(node.inputs().cast<const lf::Socket *>())) { + return; + } + check_sockets(node.outputs().cast<const lf::Socket *>()); +} + +void GeometryNodesLazyFunctionLogger::log_before_node_execute(const lf::FunctionNode &node, + const lf::Params &UNUSED(params), + const lf::Context &context) const +{ + /* Enable this to see the threads that invoked a node. */ + if constexpr (false) { + add_thread_id_debug_message(lf_graph_info_, node, context); + } +} + +} // namespace blender::nodes diff --git a/source/blender/nodes/intern/geometry_nodes_log.cc b/source/blender/nodes/intern/geometry_nodes_log.cc new file mode 100644 index 00000000000..110573c9119 --- /dev/null +++ b/source/blender/nodes/intern/geometry_nodes_log.cc @@ -0,0 +1,610 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "NOD_geometry_nodes_lazy_function.hh" +#include "NOD_geometry_nodes_log.hh" + +#include "BKE_compute_contexts.hh" +#include "BKE_curves.hh" +#include "BKE_node_runtime.hh" + +#include "FN_field_cpp_type.hh" + +#include "DNA_modifier_types.h" +#include "DNA_space_types.h" + +namespace blender::nodes::geo_eval_log { + +using fn::FieldInput; +using fn::FieldInputs; + +GenericValueLog::~GenericValueLog() +{ + this->value.destruct(); +} + +FieldInfoLog::FieldInfoLog(const GField &field) : type(field.cpp_type()) +{ + const std::shared_ptr<const fn::FieldInputs> &field_input_nodes = field.node().field_inputs(); + + /* Put the deduplicated field inputs into a vector so that they can be sorted below. */ + Vector<std::reference_wrapper<const FieldInput>> field_inputs; + if (field_input_nodes) { + field_inputs.extend(field_input_nodes->deduplicated_nodes.begin(), + field_input_nodes->deduplicated_nodes.end()); + } + + std::sort( + field_inputs.begin(), field_inputs.end(), [](const FieldInput &a, const FieldInput &b) { + const int index_a = (int)a.category(); + const int index_b = (int)b.category(); + if (index_a == index_b) { + return a.socket_inspection_name().size() < b.socket_inspection_name().size(); + } + return index_a < index_b; + }); + + for (const FieldInput &field_input : field_inputs) { + this->input_tooltips.append(field_input.socket_inspection_name()); + } +} + +GeometryInfoLog::GeometryInfoLog(const GeometrySet &geometry_set) +{ + static std::array all_component_types = {GEO_COMPONENT_TYPE_CURVE, + GEO_COMPONENT_TYPE_INSTANCES, + GEO_COMPONENT_TYPE_MESH, + GEO_COMPONENT_TYPE_POINT_CLOUD, + GEO_COMPONENT_TYPE_VOLUME}; + + /* Keep track handled attribute names to make sure that we do not return the same name twice. + * Currently #GeometrySet::attribute_foreach does not do that. Note that this will merge + * attributes with the same name but different domains or data types on separate components. */ + Set<StringRef> names; + + geometry_set.attribute_foreach( + all_component_types, + true, + [&](const bke::AttributeIDRef &attribute_id, + const bke::AttributeMetaData &meta_data, + const GeometryComponent &UNUSED(component)) { + if (attribute_id.is_named() && names.add(attribute_id.name())) { + this->attributes.append({attribute_id.name(), meta_data.domain, meta_data.data_type}); + } + }); + + for (const GeometryComponent *component : geometry_set.get_components_for_read()) { + this->component_types.append(component->type()); + switch (component->type()) { + case GEO_COMPONENT_TYPE_MESH: { + const MeshComponent &mesh_component = *(const MeshComponent *)component; + MeshInfo &info = this->mesh_info.emplace(); + info.verts_num = mesh_component.attribute_domain_size(ATTR_DOMAIN_POINT); + info.edges_num = mesh_component.attribute_domain_size(ATTR_DOMAIN_EDGE); + info.faces_num = mesh_component.attribute_domain_size(ATTR_DOMAIN_FACE); + break; + } + case GEO_COMPONENT_TYPE_CURVE: { + const CurveComponent &curve_component = *(const CurveComponent *)component; + CurveInfo &info = this->curve_info.emplace(); + info.splines_num = curve_component.attribute_domain_size(ATTR_DOMAIN_CURVE); + break; + } + case GEO_COMPONENT_TYPE_POINT_CLOUD: { + const PointCloudComponent &pointcloud_component = *(const PointCloudComponent *)component; + PointCloudInfo &info = this->pointcloud_info.emplace(); + info.points_num = pointcloud_component.attribute_domain_size(ATTR_DOMAIN_POINT); + break; + } + case GEO_COMPONENT_TYPE_INSTANCES: { + const InstancesComponent &instances_component = *(const InstancesComponent *)component; + InstancesInfo &info = this->instances_info.emplace(); + info.instances_num = instances_component.instances_num(); + break; + } + case GEO_COMPONENT_TYPE_EDIT: { + const GeometryComponentEditData &edit_component = *( + const GeometryComponentEditData *)component; + if (const bke::CurvesEditHints *curve_edit_hints = + edit_component.curves_edit_hints_.get()) { + EditDataInfo &info = this->edit_data_info.emplace(); + info.has_deform_matrices = curve_edit_hints->deform_mats.has_value(); + info.has_deformed_positions = curve_edit_hints->positions.has_value(); + } + break; + } + case GEO_COMPONENT_TYPE_VOLUME: { + break; + } + } + } +} + +/* Avoid generating these in every translation unit. */ +GeoModifierLog::GeoModifierLog() = default; +GeoModifierLog::~GeoModifierLog() = default; + +GeoTreeLogger::GeoTreeLogger() = default; +GeoTreeLogger::~GeoTreeLogger() = default; + +GeoNodeLog::GeoNodeLog() = default; +GeoNodeLog::~GeoNodeLog() = default; + +GeoTreeLog::GeoTreeLog(GeoModifierLog *modifier_log, Vector<GeoTreeLogger *> tree_loggers) + : modifier_log_(modifier_log), tree_loggers_(std::move(tree_loggers)) +{ + for (GeoTreeLogger *tree_logger : tree_loggers_) { + for (const ComputeContextHash &hash : tree_logger->children_hashes) { + children_hashes_.add(hash); + } + } +} + +GeoTreeLog::~GeoTreeLog() = default; + +void GeoTreeLogger::log_value(const bNode &node, const bNodeSocket &socket, const GPointer value) +{ + const CPPType &type = *value.type(); + + auto store_logged_value = [&](destruct_ptr<ValueLog> value_log) { + auto &socket_values = socket.in_out == SOCK_IN ? this->input_socket_values : + this->output_socket_values; + socket_values.append({this->allocator->copy_string(node.name), + this->allocator->copy_string(socket.identifier), + std::move(value_log)}); + }; + + auto log_generic_value = [&](const CPPType &type, const void *value) { + void *buffer = this->allocator->allocate(type.size(), type.alignment()); + type.copy_construct(value, buffer); + store_logged_value(this->allocator->construct<GenericValueLog>(GMutablePointer{type, buffer})); + }; + + if (type.is<GeometrySet>()) { + const GeometrySet &geometry = *value.get<GeometrySet>(); + store_logged_value(this->allocator->construct<GeometryInfoLog>(geometry)); + } + else if (const auto *value_or_field_type = dynamic_cast<const fn::ValueOrFieldCPPType *>( + &type)) { + const void *value_or_field = value.get(); + const CPPType &base_type = value_or_field_type->base_type(); + if (value_or_field_type->is_field(value_or_field)) { + const GField *field = value_or_field_type->get_field_ptr(value_or_field); + if (field->node().depends_on_input()) { + store_logged_value(this->allocator->construct<FieldInfoLog>(*field)); + } + else { + BUFFER_FOR_CPP_TYPE_VALUE(base_type, value); + fn::evaluate_constant_field(*field, value); + log_generic_value(base_type, value); + } + } + else { + const void *value = value_or_field_type->get_value_ptr(value_or_field); + log_generic_value(base_type, value); + } + } + else { + log_generic_value(type, value.get()); + } +} + +void GeoTreeLogger::log_viewer_node(const bNode &viewer_node, + const GeometrySet &geometry, + const GField &field) +{ + destruct_ptr<ViewerNodeLog> log = this->allocator->construct<ViewerNodeLog>(); + log->geometry = geometry; + log->field = field; + log->geometry.ensure_owns_direct_data(); + this->viewer_node_logs.append({this->allocator->copy_string(viewer_node.name), std::move(log)}); +} + +void GeoTreeLog::ensure_node_warnings() +{ + if (reduced_node_warnings_) { + return; + } + for (GeoTreeLogger *tree_logger : tree_loggers_) { + for (const GeoTreeLogger::WarningWithNode &warnings : tree_logger->node_warnings) { + this->nodes.lookup_or_add_default(warnings.node_name).warnings.append(warnings.warning); + this->all_warnings.append(warnings.warning); + } + } + for (const ComputeContextHash &child_hash : children_hashes_) { + GeoTreeLog &child_log = modifier_log_->get_tree_log(child_hash); + child_log.ensure_node_warnings(); + const std::optional<std::string> &group_node_name = + child_log.tree_loggers_[0]->group_node_name; + if (group_node_name.has_value()) { + this->nodes.lookup_or_add_default(*group_node_name).warnings.extend(child_log.all_warnings); + } + this->all_warnings.extend(child_log.all_warnings); + } + reduced_node_warnings_ = true; +} + +void GeoTreeLog::ensure_node_run_time() +{ + if (reduced_node_run_times_) { + return; + } + for (GeoTreeLogger *tree_logger : tree_loggers_) { + for (const GeoTreeLogger::NodeExecutionTime &timings : tree_logger->node_execution_times) { + const std::chrono::nanoseconds duration = timings.end - timings.start; + this->nodes.lookup_or_add_default_as(timings.node_name).run_time += duration; + this->run_time_sum += duration; + } + } + for (const ComputeContextHash &child_hash : children_hashes_) { + GeoTreeLog &child_log = modifier_log_->get_tree_log(child_hash); + child_log.ensure_node_run_time(); + const std::optional<std::string> &group_node_name = + child_log.tree_loggers_[0]->group_node_name; + if (group_node_name.has_value()) { + this->nodes.lookup_or_add_default(*group_node_name).run_time += child_log.run_time_sum; + } + this->run_time_sum += child_log.run_time_sum; + } + reduced_node_run_times_ = true; +} + +void GeoTreeLog::ensure_socket_values() +{ + if (reduced_socket_values_) { + return; + } + for (GeoTreeLogger *tree_logger : tree_loggers_) { + for (const GeoTreeLogger::SocketValueLog &value_log_data : tree_logger->input_socket_values) { + this->nodes.lookup_or_add_as(value_log_data.node_name) + .input_values_.add(value_log_data.socket_identifier, value_log_data.value.get()); + } + for (const GeoTreeLogger::SocketValueLog &value_log_data : tree_logger->output_socket_values) { + this->nodes.lookup_or_add_as(value_log_data.node_name) + .output_values_.add(value_log_data.socket_identifier, value_log_data.value.get()); + } + } + reduced_socket_values_ = true; +} + +void GeoTreeLog::ensure_viewer_node_logs() +{ + if (reduced_viewer_node_logs_) { + return; + } + for (GeoTreeLogger *tree_logger : tree_loggers_) { + for (const GeoTreeLogger::ViewerNodeLogWithNode &viewer_log : tree_logger->viewer_node_logs) { + this->viewer_node_logs.add(viewer_log.node_name, viewer_log.viewer_log.get()); + } + } + reduced_viewer_node_logs_ = true; +} + +void GeoTreeLog::ensure_existing_attributes() +{ + if (reduced_existing_attributes_) { + return; + } + this->ensure_socket_values(); + + Set<StringRef> names; + + auto handle_value_log = [&](const ValueLog &value_log) { + const GeometryInfoLog *geo_log = dynamic_cast<const GeometryInfoLog *>(&value_log); + if (geo_log == nullptr) { + return; + } + for (const GeometryAttributeInfo &attribute : geo_log->attributes) { + if (names.add(attribute.name)) { + this->existing_attributes.append(&attribute); + } + } + }; + + for (const GeoNodeLog &node_log : this->nodes.values()) { + for (const ValueLog *value_log : node_log.input_values_.values()) { + handle_value_log(*value_log); + } + for (const ValueLog *value_log : node_log.output_values_.values()) { + handle_value_log(*value_log); + } + } + reduced_existing_attributes_ = true; +} + +void GeoTreeLog::ensure_used_named_attributes() +{ + if (reduced_used_named_attributes_) { + return; + } + + auto add_attribute = [&](const StringRefNull node_name, + const StringRefNull attribute_name, + const NamedAttributeUsage &usage) { + this->nodes.lookup_or_add_default(node_name).used_named_attributes.lookup_or_add( + attribute_name, usage) |= usage; + this->used_named_attributes.lookup_or_add_as(attribute_name, usage) |= usage; + }; + + for (GeoTreeLogger *tree_logger : tree_loggers_) { + for (const GeoTreeLogger::AttributeUsageWithNode &item : tree_logger->used_named_attributes) { + add_attribute(item.node_name, item.attribute_name, item.usage); + } + } + for (const ComputeContextHash &child_hash : children_hashes_) { + GeoTreeLog &child_log = modifier_log_->get_tree_log(child_hash); + child_log.ensure_used_named_attributes(); + if (const std::optional<std::string> &group_node_name = + child_log.tree_loggers_[0]->group_node_name) { + for (const auto &item : child_log.used_named_attributes.items()) { + add_attribute(*group_node_name, item.key, item.value); + } + } + } + reduced_used_named_attributes_ = true; +} + +void GeoTreeLog::ensure_debug_messages() +{ + if (reduced_debug_messages_) { + return; + } + for (GeoTreeLogger *tree_logger : tree_loggers_) { + for (const GeoTreeLogger::DebugMessage &debug_message : tree_logger->debug_messages) { + this->nodes.lookup_or_add_as(debug_message.node_name) + .debug_messages.append(debug_message.message); + } + } + reduced_debug_messages_ = true; +} + +ValueLog *GeoTreeLog::find_socket_value_log(const bNodeSocket &query_socket) +{ + /** + * Geometry nodes does not log values for every socket. That would produce a lot of redundant + * data,because often many linked sockets have the same value. To find the logged value for a + * socket one might have to look at linked sockets as well. + */ + + BLI_assert(reduced_socket_values_); + if (query_socket.is_multi_input()) { + /* Not supported currently. */ + return nullptr; + } + + Set<const bNodeSocket *> added_sockets; + Stack<const bNodeSocket *> sockets_to_check; + sockets_to_check.push(&query_socket); + added_sockets.add(&query_socket); + + while (!sockets_to_check.is_empty()) { + const bNodeSocket &socket = *sockets_to_check.pop(); + const bNode &node = socket.owner_node(); + if (GeoNodeLog *node_log = this->nodes.lookup_ptr(node.name)) { + ValueLog *value_log = socket.is_input() ? + node_log->input_values_.lookup_default(socket.identifier, + nullptr) : + node_log->output_values_.lookup_default(socket.identifier, + nullptr); + if (value_log != nullptr) { + return value_log; + } + } + + if (socket.is_input()) { + const Span<const bNodeLink *> links = socket.directly_linked_links(); + for (const bNodeLink *link : links) { + const bNodeSocket &from_socket = *link->fromsock; + if (added_sockets.add(&from_socket)) { + sockets_to_check.push(&from_socket); + } + } + } + else { + if (node.is_reroute()) { + const bNodeSocket &input_socket = node.input_socket(0); + if (added_sockets.add(&input_socket)) { + sockets_to_check.push(&input_socket); + } + const Span<const bNodeLink *> links = input_socket.directly_linked_links(); + for (const bNodeLink *link : links) { + const bNodeSocket &from_socket = *link->fromsock; + if (added_sockets.add(&from_socket)) { + sockets_to_check.push(&from_socket); + } + } + } + else if (node.is_muted()) { + if (const bNodeSocket *input_socket = socket.internal_link_input()) { + if (added_sockets.add(input_socket)) { + sockets_to_check.push(input_socket); + } + const Span<const bNodeLink *> links = input_socket->directly_linked_links(); + for (const bNodeLink *link : links) { + const bNodeSocket &from_socket = *link->fromsock; + if (added_sockets.add(&from_socket)) { + sockets_to_check.push(&from_socket); + } + } + } + } + } + } + + return nullptr; +} + +GeoTreeLogger &GeoModifierLog::get_local_tree_logger(const ComputeContext &compute_context) +{ + LocalData &local_data = data_per_thread_.local(); + Map<ComputeContextHash, destruct_ptr<GeoTreeLogger>> &local_tree_loggers = + local_data.tree_logger_by_context; + destruct_ptr<GeoTreeLogger> &tree_logger_ptr = local_tree_loggers.lookup_or_add_default( + compute_context.hash()); + if (tree_logger_ptr) { + return *tree_logger_ptr; + } + tree_logger_ptr = local_data.allocator.construct<GeoTreeLogger>(); + GeoTreeLogger &tree_logger = *tree_logger_ptr; + tree_logger.allocator = &local_data.allocator; + const ComputeContext *parent_compute_context = compute_context.parent(); + if (parent_compute_context != nullptr) { + tree_logger.parent_hash = parent_compute_context->hash(); + GeoTreeLogger &parent_logger = this->get_local_tree_logger(*parent_compute_context); + parent_logger.children_hashes.append(compute_context.hash()); + } + if (const bke::NodeGroupComputeContext *node_group_compute_context = + dynamic_cast<const bke::NodeGroupComputeContext *>(&compute_context)) { + tree_logger.group_node_name.emplace(node_group_compute_context->node_name()); + } + return tree_logger; +} + +GeoTreeLog &GeoModifierLog::get_tree_log(const ComputeContextHash &compute_context_hash) +{ + GeoTreeLog &reduced_tree_log = *tree_logs_.lookup_or_add_cb(compute_context_hash, [&]() { + Vector<GeoTreeLogger *> tree_logs; + for (LocalData &local_data : data_per_thread_) { + destruct_ptr<GeoTreeLogger> *tree_log = local_data.tree_logger_by_context.lookup_ptr( + compute_context_hash); + if (tree_log != nullptr) { + tree_logs.append(tree_log->get()); + } + } + return std::make_unique<GeoTreeLog>(this, std::move(tree_logs)); + }); + return reduced_tree_log; +} + +struct ObjectAndModifier { + const Object *object; + const NodesModifierData *nmd; +}; + +static std::optional<ObjectAndModifier> get_modifier_for_node_editor(const SpaceNode &snode) +{ + if (snode.id == nullptr) { + return std::nullopt; + } + if (GS(snode.id->name) != ID_OB) { + return std::nullopt; + } + const Object *object = reinterpret_cast<Object *>(snode.id); + const NodesModifierData *used_modifier = nullptr; + if (snode.flag & SNODE_PIN) { + LISTBASE_FOREACH (const ModifierData *, md, &object->modifiers) { + if (md->type == eModifierType_Nodes) { + const NodesModifierData *nmd = reinterpret_cast<const NodesModifierData *>(md); + /* Would be good to store the name of the pinned modifier in the node editor. */ + if (nmd->node_group == snode.nodetree) { + used_modifier = nmd; + break; + } + } + } + } + else { + LISTBASE_FOREACH (const ModifierData *, md, &object->modifiers) { + if (md->type == eModifierType_Nodes) { + const NodesModifierData *nmd = reinterpret_cast<const NodesModifierData *>(md); + if (nmd->node_group == snode.nodetree) { + if (md->flag & eModifierFlag_Active) { + used_modifier = nmd; + break; + } + } + } + } + } + if (used_modifier == nullptr) { + return std::nullopt; + } + return ObjectAndModifier{object, used_modifier}; +} + +GeoTreeLog *GeoModifierLog::get_tree_log_for_node_editor(const SpaceNode &snode) +{ + std::optional<ObjectAndModifier> object_and_modifier = get_modifier_for_node_editor(snode); + if (!object_and_modifier) { + return nullptr; + } + GeoModifierLog *modifier_log = static_cast<GeoModifierLog *>( + object_and_modifier->nmd->runtime_eval_log); + if (modifier_log == nullptr) { + return nullptr; + } + Vector<const bNodeTreePath *> tree_path = snode.treepath; + if (tree_path.is_empty()) { + return nullptr; + } + ComputeContextBuilder compute_context_builder; + compute_context_builder.push<bke::ModifierComputeContext>( + object_and_modifier->nmd->modifier.name); + for (const bNodeTreePath *path_item : tree_path.as_span().drop_front(1)) { + compute_context_builder.push<bke::NodeGroupComputeContext>(path_item->node_name); + } + return &modifier_log->get_tree_log(compute_context_builder.hash()); +} + +const ViewerNodeLog *GeoModifierLog::find_viewer_node_log_for_spreadsheet( + const SpaceSpreadsheet &sspreadsheet) +{ + Vector<const SpreadsheetContext *> context_path = sspreadsheet.context_path; + if (context_path.size() < 3) { + return nullptr; + } + if (context_path[0]->type != SPREADSHEET_CONTEXT_OBJECT) { + return nullptr; + } + if (context_path[1]->type != SPREADSHEET_CONTEXT_MODIFIER) { + return nullptr; + } + const SpreadsheetContextObject *object_context = + reinterpret_cast<const SpreadsheetContextObject *>(context_path[0]); + const SpreadsheetContextModifier *modifier_context = + reinterpret_cast<const SpreadsheetContextModifier *>(context_path[1]); + if (object_context->object == nullptr) { + return nullptr; + } + NodesModifierData *nmd = nullptr; + LISTBASE_FOREACH (ModifierData *, md, &object_context->object->modifiers) { + if (STREQ(md->name, modifier_context->modifier_name)) { + if (md->type == eModifierType_Nodes) { + nmd = reinterpret_cast<NodesModifierData *>(md); + } + } + } + if (nmd == nullptr) { + return nullptr; + } + if (nmd->runtime_eval_log == nullptr) { + return nullptr; + } + nodes::geo_eval_log::GeoModifierLog *modifier_log = + static_cast<nodes::geo_eval_log::GeoModifierLog *>(nmd->runtime_eval_log); + + ComputeContextBuilder compute_context_builder; + compute_context_builder.push<bke::ModifierComputeContext>(modifier_context->modifier_name); + for (const SpreadsheetContext *context : context_path.as_span().drop_front(2).drop_back(1)) { + if (context->type != SPREADSHEET_CONTEXT_NODE) { + return nullptr; + } + const SpreadsheetContextNode &node_context = *reinterpret_cast<const SpreadsheetContextNode *>( + context); + compute_context_builder.push<bke::NodeGroupComputeContext>(node_context.node_name); + } + const ComputeContextHash context_hash = compute_context_builder.hash(); + nodes::geo_eval_log::GeoTreeLog &tree_log = modifier_log->get_tree_log(context_hash); + tree_log.ensure_viewer_node_logs(); + + const SpreadsheetContext *last_context = context_path.last(); + if (last_context->type != SPREADSHEET_CONTEXT_NODE) { + return nullptr; + } + const SpreadsheetContextNode &last_node_context = + *reinterpret_cast<const SpreadsheetContextNode *>(last_context); + const ViewerNodeLog *viewer_log = tree_log.viewer_node_logs.lookup_default( + last_node_context.node_name, nullptr); + return viewer_log; +} + +} // namespace blender::nodes::geo_eval_log diff --git a/source/blender/nodes/intern/node_common.cc b/source/blender/nodes/intern/node_common.cc index 6402ec3f3d6..1f085375329 100644 --- a/source/blender/nodes/intern/node_common.cc +++ b/source/blender/nodes/intern/node_common.cc @@ -82,7 +82,9 @@ bool node_group_poll_instance(bNode *node, bNodeTree *nodetree, const char **dis return false; } -bool nodeGroupPoll(bNodeTree *nodetree, bNodeTree *grouptree, const char **r_disabled_hint) +bool nodeGroupPoll(const bNodeTree *nodetree, + const bNodeTree *grouptree, + const char **r_disabled_hint) { bool valid = true; @@ -94,13 +96,16 @@ bool nodeGroupPoll(bNodeTree *nodetree, bNodeTree *grouptree, const char **r_dis } if (nodetree == grouptree) { - *r_disabled_hint = TIP_("Nesting a node group inside of itself is not allowed"); + if (r_disabled_hint) { + *r_disabled_hint = TIP_("Nesting a node group inside of itself is not allowed"); + } return false; } - LISTBASE_FOREACH (bNode *, node, &grouptree->nodes) { + LISTBASE_FOREACH (const bNode *, node, &grouptree->nodes) { if (node->typeinfo->poll_instance && - !node->typeinfo->poll_instance(node, nodetree, r_disabled_hint)) { + !node->typeinfo->poll_instance( + const_cast<bNode *>(node), const_cast<bNodeTree *>(nodetree), r_disabled_hint)) { valid = false; break; } diff --git a/source/blender/nodes/intern/node_geometry_exec.cc b/source/blender/nodes/intern/node_geometry_exec.cc index c6ebc22c43c..1de92fa8409 100644 --- a/source/blender/nodes/intern/node_geometry_exec.cc +++ b/source/blender/nodes/intern/node_geometry_exec.cc @@ -11,34 +11,31 @@ #include "node_geometry_util.hh" -using blender::nodes::geometry_nodes_eval_log::LocalGeoLogger; - namespace blender::nodes { -void GeoNodeExecParams::error_message_add(const NodeWarningType type, std::string message) const +void GeoNodeExecParams::error_message_add(const NodeWarningType type, + const StringRef message) const { - if (provider_->logger == nullptr) { - return; + if (geo_eval_log::GeoTreeLogger *tree_logger = this->get_local_tree_logger()) { + tree_logger->node_warnings.append({tree_logger->allocator->copy_string(node_.name), + {type, tree_logger->allocator->copy_string(message)}}); } - LocalGeoLogger &local_logger = provider_->logger->local(); - local_logger.log_node_warning(provider_->dnode, type, std::move(message)); } -void GeoNodeExecParams::used_named_attribute(std::string attribute_name, - const eNamedAttrUsage usage) +void GeoNodeExecParams::used_named_attribute(const StringRef attribute_name, + const NamedAttributeUsage usage) { - if (provider_->logger == nullptr) { - return; + if (geo_eval_log::GeoTreeLogger *tree_logger = this->get_local_tree_logger()) { + tree_logger->used_named_attributes.append({tree_logger->allocator->copy_string(node_.name), + tree_logger->allocator->copy_string(attribute_name), + usage}); } - LocalGeoLogger &local_logger = provider_->logger->local(); - local_logger.log_used_named_attribute(provider_->dnode, std::move(attribute_name), usage); } void GeoNodeExecParams::check_input_geometry_set(StringRef identifier, const GeometrySet &geometry_set) const { - const SocketDeclaration &decl = - *provider_->dnode->input_by_identifier(identifier).bsocket()->runtime->declaration; + const SocketDeclaration &decl = *node_.input_by_identifier(identifier).runtime->declaration; const decl::Geometry *geo_decl = dynamic_cast<const decl::Geometry *>(&decl); if (geo_decl == nullptr) { return; @@ -118,9 +115,9 @@ void GeoNodeExecParams::check_output_geometry_set(const GeometrySet &geometry_se const bNodeSocket *GeoNodeExecParams::find_available_socket(const StringRef name) const { - for (const InputSocketRef *socket : provider_->dnode->inputs()) { - if (socket->is_available() && socket->name() == name) { - return socket->bsocket(); + for (const bNodeSocket *socket : node_.input_sockets()) { + if (socket->is_available() && socket->name == name) { + return socket; } } @@ -129,21 +126,21 @@ const bNodeSocket *GeoNodeExecParams::find_available_socket(const StringRef name std::string GeoNodeExecParams::attribute_producer_name() const { - return provider_->dnode->label_or_name() + TIP_(" node"); + return node_.label_or_name() + TIP_(" node"); } void GeoNodeExecParams::set_default_remaining_outputs() { - provider_->set_default_remaining_outputs(); + params_.set_default_remaining_outputs(); } void GeoNodeExecParams::check_input_access(StringRef identifier, const CPPType *requested_type) const { - bNodeSocket *found_socket = nullptr; - for (const InputSocketRef *socket : provider_->dnode->inputs()) { - if (socket->identifier() == identifier) { - found_socket = socket->bsocket(); + const bNodeSocket *found_socket = nullptr; + for (const bNodeSocket *socket : node_.input_sockets()) { + if (socket->identifier == identifier) { + found_socket = socket; break; } } @@ -151,9 +148,9 @@ void GeoNodeExecParams::check_input_access(StringRef identifier, if (found_socket == nullptr) { std::cout << "Did not find an input socket with the identifier '" << identifier << "'.\n"; std::cout << "Possible identifiers are: "; - for (const InputSocketRef *socket : provider_->dnode->inputs()) { + for (const bNodeSocket *socket : node_.input_sockets()) { if (socket->is_available()) { - std::cout << "'" << socket->identifier() << "', "; + std::cout << "'" << socket->identifier << "', "; } } std::cout << "\n"; @@ -164,13 +161,7 @@ void GeoNodeExecParams::check_input_access(StringRef identifier, << "' is disabled.\n"; BLI_assert_unreachable(); } - else if (!provider_->can_get_input(identifier)) { - std::cout << "The identifier '" << identifier - << "' is valid, but there is no value for it anymore.\n"; - std::cout << "Most likely it has been extracted before.\n"; - BLI_assert_unreachable(); - } - else if (requested_type != nullptr) { + else if (requested_type != nullptr && (found_socket->flag & SOCK_MULTI_INPUT) == 0) { const CPPType &expected_type = *found_socket->typeinfo->geometry_nodes_cpp_type; if (*requested_type != expected_type) { std::cout << "The requested type '" << requested_type->name() << "' is incorrect. Expected '" @@ -182,10 +173,10 @@ void GeoNodeExecParams::check_input_access(StringRef identifier, void GeoNodeExecParams::check_output_access(StringRef identifier, const CPPType &value_type) const { - bNodeSocket *found_socket = nullptr; - for (const OutputSocketRef *socket : provider_->dnode->outputs()) { - if (socket->identifier() == identifier) { - found_socket = socket->bsocket(); + const bNodeSocket *found_socket = nullptr; + for (const bNodeSocket *socket : node_.output_sockets()) { + if (socket->identifier == identifier) { + found_socket = socket; break; } } @@ -193,9 +184,9 @@ void GeoNodeExecParams::check_output_access(StringRef identifier, const CPPType if (found_socket == nullptr) { std::cout << "Did not find an output socket with the identifier '" << identifier << "'.\n"; std::cout << "Possible identifiers are: "; - for (const OutputSocketRef *socket : provider_->dnode->outputs()) { + for (const bNodeSocket *socket : node_.output_sockets()) { if (socket->is_available()) { - std::cout << "'" << socket->identifier() << "', "; + std::cout << "'" << socket->identifier << "', "; } } std::cout << "\n"; @@ -206,7 +197,7 @@ void GeoNodeExecParams::check_output_access(StringRef identifier, const CPPType << "' is disabled.\n"; BLI_assert_unreachable(); } - else if (!provider_->can_set_output(identifier)) { + else if (params_.output_was_set(this->get_output_index(identifier))) { std::cout << "The identifier '" << identifier << "' has been set already.\n"; BLI_assert_unreachable(); } diff --git a/source/blender/nodes/intern/node_multi_function.cc b/source/blender/nodes/intern/node_multi_function.cc index 13bfdfbfac1..d731fe8f877 100644 --- a/source/blender/nodes/intern/node_multi_function.cc +++ b/source/blender/nodes/intern/node_multi_function.cc @@ -2,22 +2,22 @@ #include "NOD_multi_function.hh" +#include "BKE_node.h" +#include "BKE_node_runtime.hh" + namespace blender::nodes { -NodeMultiFunctions::NodeMultiFunctions(const DerivedNodeTree &tree) +NodeMultiFunctions::NodeMultiFunctions(const bNodeTree &tree) { - for (const NodeTreeRef *tree_ref : tree.used_node_tree_refs()) { - bNodeTree *btree = tree_ref->btree(); - for (const NodeRef *node : tree_ref->nodes()) { - bNode *bnode = node->bnode(); - if (bnode->typeinfo->build_multi_function == nullptr) { - continue; - } - NodeMultiFunctionBuilder builder{*bnode, *btree}; - bnode->typeinfo->build_multi_function(builder); - if (builder.built_fn_ != nullptr) { - map_.add_new(bnode, {builder.built_fn_, std::move(builder.owned_built_fn_)}); - } + tree.ensure_topology_cache(); + for (const bNode *bnode : tree.all_nodes()) { + if (bnode->typeinfo->build_multi_function == nullptr) { + continue; + } + NodeMultiFunctionBuilder builder{*bnode, tree}; + bnode->typeinfo->build_multi_function(builder); + if (builder.built_fn_ != nullptr) { + map_.add_new(bnode, {builder.built_fn_, std::move(builder.owned_built_fn_)}); } } } diff --git a/source/blender/nodes/intern/node_tree_ref.cc b/source/blender/nodes/intern/node_tree_ref.cc deleted file mode 100644 index 05e7fe33776..00000000000 --- a/source/blender/nodes/intern/node_tree_ref.cc +++ /dev/null @@ -1,679 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include <mutex> - -#include "NOD_node_tree_ref.hh" - -#include "BLI_dot_export.hh" -#include "BLI_stack.hh" - -#include "RNA_prototypes.h" - -namespace blender::nodes { - -NodeTreeRef::NodeTreeRef(bNodeTree *btree) : btree_(btree) -{ - Map<bNode *, NodeRef *> node_mapping; - - LISTBASE_FOREACH (bNode *, bnode, &btree->nodes) { - NodeRef &node = *allocator_.construct<NodeRef>().release(); - - node.tree_ = this; - node.bnode_ = bnode; - node.id_ = nodes_by_id_.append_and_get_index(&node); - - LISTBASE_FOREACH (bNodeSocket *, bsocket, &bnode->inputs) { - InputSocketRef &socket = *allocator_.construct<InputSocketRef>().release(); - socket.node_ = &node; - socket.index_ = node.inputs_.append_and_get_index(&socket); - socket.is_input_ = true; - socket.bsocket_ = bsocket; - socket.id_ = sockets_by_id_.append_and_get_index(&socket); - } - - LISTBASE_FOREACH (bNodeSocket *, bsocket, &bnode->outputs) { - OutputSocketRef &socket = *allocator_.construct<OutputSocketRef>().release(); - socket.node_ = &node; - socket.index_ = node.outputs_.append_and_get_index(&socket); - socket.is_input_ = false; - socket.bsocket_ = bsocket; - socket.id_ = sockets_by_id_.append_and_get_index(&socket); - } - - LISTBASE_FOREACH (bNodeLink *, blink, &bnode->internal_links) { - InternalLinkRef &internal_link = *allocator_.construct<InternalLinkRef>().release(); - internal_link.blink_ = blink; - for (InputSocketRef *socket_ref : node.inputs_) { - if (socket_ref->bsocket_ == blink->fromsock) { - internal_link.from_ = socket_ref; - break; - } - } - for (OutputSocketRef *socket_ref : node.outputs_) { - if (socket_ref->bsocket_ == blink->tosock) { - internal_link.to_ = socket_ref; - break; - } - } - BLI_assert(internal_link.from_ != nullptr); - BLI_assert(internal_link.to_ != nullptr); - node.internal_links_.append(&internal_link); - } - - input_sockets_.extend(node.inputs_.as_span()); - output_sockets_.extend(node.outputs_.as_span()); - - node_mapping.add_new(bnode, &node); - } - - LISTBASE_FOREACH (bNodeLink *, blink, &btree->links) { - OutputSocketRef &from_socket = this->find_output_socket( - node_mapping, blink->fromnode, blink->fromsock); - InputSocketRef &to_socket = this->find_input_socket( - node_mapping, blink->tonode, blink->tosock); - - LinkRef &link = *allocator_.construct<LinkRef>().release(); - link.from_ = &from_socket; - link.to_ = &to_socket; - link.blink_ = blink; - - links_.append(&link); - - from_socket.directly_linked_links_.append(&link); - to_socket.directly_linked_links_.append(&link); - } - - for (InputSocketRef *input_socket : input_sockets_) { - if (input_socket->is_multi_input_socket()) { - std::sort(input_socket->directly_linked_links_.begin(), - input_socket->directly_linked_links_.end(), - [&](const LinkRef *a, const LinkRef *b) -> bool { - int index_a = a->blink()->multi_input_socket_index; - int index_b = b->blink()->multi_input_socket_index; - return index_a > index_b; - }); - } - } - - this->create_socket_identifier_maps(); - this->create_linked_socket_caches(); - - for (NodeRef *node : nodes_by_id_) { - const bNodeType *nodetype = node->bnode_->typeinfo; - nodes_by_type_.add(nodetype, node); - } - - const Span<const NodeRef *> group_output_nodes = this->nodes_by_type("NodeGroupOutput"); - if (group_output_nodes.is_empty()) { - group_output_node_ = nullptr; - } - else if (group_output_nodes.size() == 1) { - group_output_node_ = group_output_nodes.first(); - } - else { - for (const NodeRef *group_output : group_output_nodes) { - if (group_output->bnode_->flag & NODE_DO_OUTPUT) { - group_output_node_ = group_output; - break; - } - } - } -} - -NodeTreeRef::~NodeTreeRef() -{ - /* The destructor has to be called manually, because these types are allocated in a linear - * allocator. */ - for (NodeRef *node : nodes_by_id_) { - node->~NodeRef(); - } - for (InputSocketRef *socket : input_sockets_) { - socket->~InputSocketRef(); - } - for (OutputSocketRef *socket : output_sockets_) { - socket->~OutputSocketRef(); - } - for (LinkRef *link : links_) { - link->~LinkRef(); - } -} - -InputSocketRef &NodeTreeRef::find_input_socket(Map<bNode *, NodeRef *> &node_mapping, - bNode *bnode, - bNodeSocket *bsocket) -{ - NodeRef *node = node_mapping.lookup(bnode); - for (InputSocketRef *socket : node->inputs_) { - if (socket->bsocket_ == bsocket) { - return *socket; - } - } - BLI_assert_unreachable(); - return *node->inputs_[0]; -} - -OutputSocketRef &NodeTreeRef::find_output_socket(Map<bNode *, NodeRef *> &node_mapping, - bNode *bnode, - bNodeSocket *bsocket) -{ - NodeRef *node = node_mapping.lookup(bnode); - for (OutputSocketRef *socket : node->outputs_) { - if (socket->bsocket_ == bsocket) { - return *socket; - } - } - BLI_assert_unreachable(); - return *node->outputs_[0]; -} - -void NodeTreeRef::create_linked_socket_caches() -{ - for (InputSocketRef *socket : input_sockets_) { - /* Find directly linked socket based on incident links. */ - Vector<const SocketRef *> directly_linked_sockets; - for (LinkRef *link : socket->directly_linked_links_) { - directly_linked_sockets.append(link->from_); - } - socket->directly_linked_sockets_ = allocator_.construct_array_copy( - directly_linked_sockets.as_span()); - - /* Find logically linked sockets. */ - Vector<const SocketRef *> logically_linked_sockets; - Vector<const SocketRef *> logically_linked_skipped_sockets; - Vector<const InputSocketRef *> seen_sockets_stack; - socket->foreach_logical_origin( - [&](const OutputSocketRef &origin) { logically_linked_sockets.append(&origin); }, - [&](const SocketRef &socket) { logically_linked_skipped_sockets.append(&socket); }, - false, - seen_sockets_stack); - if (logically_linked_sockets == directly_linked_sockets) { - socket->logically_linked_sockets_ = socket->directly_linked_sockets_; - } - else { - socket->logically_linked_sockets_ = allocator_.construct_array_copy( - logically_linked_sockets.as_span()); - } - socket->logically_linked_skipped_sockets_ = allocator_.construct_array_copy( - logically_linked_skipped_sockets.as_span()); - } - - for (OutputSocketRef *socket : output_sockets_) { - /* Find directly linked socket based on incident links. */ - Vector<const SocketRef *> directly_linked_sockets; - for (LinkRef *link : socket->directly_linked_links_) { - directly_linked_sockets.append(link->to_); - } - socket->directly_linked_sockets_ = allocator_.construct_array_copy( - directly_linked_sockets.as_span()); - - /* Find logically linked sockets. */ - Vector<const SocketRef *> logically_linked_sockets; - Vector<const SocketRef *> logically_linked_skipped_sockets; - Vector<const OutputSocketRef *> handled_sockets; - socket->foreach_logical_target( - [&](const InputSocketRef &target) { logically_linked_sockets.append(&target); }, - [&](const SocketRef &socket) { logically_linked_skipped_sockets.append(&socket); }, - handled_sockets); - if (logically_linked_sockets == directly_linked_sockets) { - socket->logically_linked_sockets_ = socket->directly_linked_sockets_; - } - else { - socket->logically_linked_sockets_ = allocator_.construct_array_copy( - logically_linked_sockets.as_span()); - } - socket->logically_linked_skipped_sockets_ = allocator_.construct_array_copy( - logically_linked_skipped_sockets.as_span()); - } -} - -void InputSocketRef::foreach_logical_origin( - FunctionRef<void(const OutputSocketRef &)> origin_fn, - FunctionRef<void(const SocketRef &)> skipped_fn, - bool only_follow_first_input_link, - Vector<const InputSocketRef *> &seen_sockets_stack) const -{ - /* Protect against loops. */ - if (seen_sockets_stack.contains(this)) { - return; - } - seen_sockets_stack.append(this); - - Span<const LinkRef *> links_to_check = this->directly_linked_links(); - if (only_follow_first_input_link) { - links_to_check = links_to_check.take_front(1); - } - for (const LinkRef *link : links_to_check) { - if (link->is_muted()) { - continue; - } - const OutputSocketRef &origin = link->from(); - const NodeRef &origin_node = origin.node(); - if (!origin.is_available()) { - /* Non available sockets are ignored. */ - } - else if (origin_node.is_reroute_node()) { - const InputSocketRef &reroute_input = origin_node.input(0); - const OutputSocketRef &reroute_output = origin_node.output(0); - skipped_fn.call_safe(reroute_input); - skipped_fn.call_safe(reroute_output); - reroute_input.foreach_logical_origin(origin_fn, skipped_fn, false, seen_sockets_stack); - } - else if (origin_node.is_muted()) { - for (const InternalLinkRef *internal_link : origin_node.internal_links()) { - if (&internal_link->to() == &origin) { - const InputSocketRef &mute_input = internal_link->from(); - skipped_fn.call_safe(origin); - skipped_fn.call_safe(mute_input); - mute_input.foreach_logical_origin(origin_fn, skipped_fn, true, seen_sockets_stack); - } - } - } - else { - origin_fn(origin); - } - } - - seen_sockets_stack.pop_last(); -} - -void OutputSocketRef::foreach_logical_target( - FunctionRef<void(const InputSocketRef &)> target_fn, - FunctionRef<void(const SocketRef &)> skipped_fn, - Vector<const OutputSocketRef *> &seen_sockets_stack) const -{ - /* Protect against loops. */ - if (seen_sockets_stack.contains(this)) { - return; - } - seen_sockets_stack.append(this); - - for (const LinkRef *link : this->directly_linked_links()) { - if (link->is_muted()) { - continue; - } - const InputSocketRef &target = link->to(); - const NodeRef &target_node = target.node(); - if (!target.is_available()) { - /* Non available sockets are ignored. */ - } - else if (target_node.is_reroute_node()) { - const OutputSocketRef &reroute_output = target_node.output(0); - skipped_fn.call_safe(target); - skipped_fn.call_safe(reroute_output); - reroute_output.foreach_logical_target(target_fn, skipped_fn, seen_sockets_stack); - } - else if (target_node.is_muted()) { - skipped_fn.call_safe(target); - for (const InternalLinkRef *internal_link : target_node.internal_links()) { - if (&internal_link->from() == &target) { - /* The internal link only forwards the first incoming link. */ - if (target.is_multi_input_socket()) { - if (target.directly_linked_links()[0] != link) { - continue; - } - } - const OutputSocketRef &mute_output = internal_link->to(); - skipped_fn.call_safe(target); - skipped_fn.call_safe(mute_output); - mute_output.foreach_logical_target(target_fn, skipped_fn, seen_sockets_stack); - } - } - } - else { - target_fn(target); - } - } - - seen_sockets_stack.pop_last(); -} - -namespace { -struct SocketByIdentifierMap { - SocketIndexByIdentifierMap *map = nullptr; - std::unique_ptr<SocketIndexByIdentifierMap> owned_map; -}; -} // namespace - -static std::unique_ptr<SocketIndexByIdentifierMap> create_identifier_map(const ListBase &sockets) -{ - std::unique_ptr<SocketIndexByIdentifierMap> map = std::make_unique<SocketIndexByIdentifierMap>(); - int index; - LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, &sockets, index) { - map->add_new(socket->identifier, index); - } - return map; -} - -/* This function is not threadsafe. */ -static SocketByIdentifierMap get_or_create_identifier_map( - const bNode &node, const ListBase &sockets, const bNodeSocketTemplate *sockets_template) -{ - SocketByIdentifierMap map; - if (sockets_template == nullptr) { - if (BLI_listbase_is_empty(&sockets)) { - static SocketIndexByIdentifierMap empty_map; - map.map = &empty_map; - } - else if (node.type == NODE_REROUTE) { - if (&node.inputs == &sockets) { - static SocketIndexByIdentifierMap reroute_input_map = [] { - SocketIndexByIdentifierMap map; - map.add_new("Input", 0); - return map; - }(); - map.map = &reroute_input_map; - } - else { - static SocketIndexByIdentifierMap reroute_output_map = [] { - SocketIndexByIdentifierMap map; - map.add_new("Output", 0); - return map; - }(); - map.map = &reroute_output_map; - } - } - else { - /* The node has a dynamic amount of sockets. Therefore we need to create a new map. */ - map.owned_map = create_identifier_map(sockets); - map.map = &*map.owned_map; - } - } - else { - /* Cache only one map for nodes that have the same sockets. */ - static Map<const bNodeSocketTemplate *, std::unique_ptr<SocketIndexByIdentifierMap>> maps; - map.map = &*maps.lookup_or_add_cb(sockets_template, - [&]() { return create_identifier_map(sockets); }); - } - return map; -} - -void NodeTreeRef::create_socket_identifier_maps() -{ - /* `get_or_create_identifier_map` is not threadsafe, therefore we have to hold a lock here. */ - static std::mutex mutex; - std::lock_guard lock{mutex}; - - for (NodeRef *node : nodes_by_id_) { - bNode &bnode = *node->bnode_; - SocketByIdentifierMap inputs_map = get_or_create_identifier_map( - bnode, bnode.inputs, bnode.typeinfo->inputs); - SocketByIdentifierMap outputs_map = get_or_create_identifier_map( - bnode, bnode.outputs, bnode.typeinfo->outputs); - node->input_index_by_identifier_ = inputs_map.map; - node->output_index_by_identifier_ = outputs_map.map; - if (inputs_map.owned_map) { - owned_identifier_maps_.append(std::move(inputs_map.owned_map)); - } - if (outputs_map.owned_map) { - owned_identifier_maps_.append(std::move(outputs_map.owned_map)); - } - } -} - -static bool has_link_cycles_recursive(const NodeRef &node, - MutableSpan<bool> visited, - MutableSpan<bool> is_in_stack) -{ - const int node_id = node.id(); - if (is_in_stack[node_id]) { - return true; - } - if (visited[node_id]) { - return false; - } - - visited[node_id] = true; - is_in_stack[node_id] = true; - - for (const OutputSocketRef *from_socket : node.outputs()) { - if (!from_socket->is_available()) { - continue; - } - for (const InputSocketRef *to_socket : from_socket->directly_linked_sockets()) { - if (!to_socket->is_available()) { - continue; - } - const NodeRef &to_node = to_socket->node(); - if (has_link_cycles_recursive(to_node, visited, is_in_stack)) { - return true; - } - } - } - - is_in_stack[node_id] = false; - return false; -} - -bool NodeTreeRef::has_link_cycles() const -{ - const int node_amount = nodes_by_id_.size(); - Array<bool> visited(node_amount, false); - Array<bool> is_in_stack(node_amount, false); - - for (const NodeRef *node : nodes_by_id_) { - if (has_link_cycles_recursive(*node, visited, is_in_stack)) { - return true; - } - } - return false; -} - -bool NodeTreeRef::has_undefined_nodes_or_sockets() const -{ - for (const NodeRef *node : nodes_by_id_) { - if (node->is_undefined()) { - return true; - } - } - for (const SocketRef *socket : sockets_by_id_) { - if (socket->is_undefined()) { - return true; - } - } - return false; -} - -bool NodeRef::any_input_is_directly_linked() const -{ - for (const SocketRef *socket : inputs_) { - if (!socket->directly_linked_sockets().is_empty()) { - return true; - } - } - return false; -} - -bool NodeRef::any_output_is_directly_linked() const -{ - for (const SocketRef *socket : outputs_) { - if (!socket->directly_linked_sockets().is_empty()) { - return true; - } - } - return false; -} - -bool NodeRef::any_socket_is_directly_linked(eNodeSocketInOut in_out) const -{ - if (in_out == SOCK_IN) { - return this->any_input_is_directly_linked(); - } - return this->any_output_is_directly_linked(); -} - -struct ToposortNodeState { - bool is_done = false; - bool is_in_stack = false; -}; - -static void toposort_from_start_node(const NodeTreeRef::ToposortDirection direction, - const NodeRef &start_node, - MutableSpan<ToposortNodeState> node_states, - NodeTreeRef::ToposortResult &result) -{ - struct Item { - const NodeRef *node; - /* Index of the next socket that is checked in the depth-first search. */ - int socket_index = 0; - /* Link index in the next socket that is checked in the depth-first search. */ - int link_index = 0; - }; - - /* Do a depth-first search to sort nodes topologically. */ - Stack<Item, 64> nodes_to_check; - nodes_to_check.push({&start_node}); - node_states[start_node.id()].is_in_stack = true; - while (!nodes_to_check.is_empty()) { - Item &item = nodes_to_check.peek(); - const NodeRef &node = *item.node; - const Span<const SocketRef *> sockets = node.sockets( - direction == NodeTreeRef::ToposortDirection::LeftToRight ? SOCK_IN : SOCK_OUT); - - while (true) { - if (item.socket_index == sockets.size()) { - /* All sockets have already been visited. */ - break; - } - const SocketRef &socket = *sockets[item.socket_index]; - const Span<const SocketRef *> linked_sockets = socket.directly_linked_sockets(); - if (item.link_index == linked_sockets.size()) { - /* All links connected to this socket have already been visited. */ - item.socket_index++; - item.link_index = 0; - continue; - } - const SocketRef &linked_socket = *linked_sockets[item.link_index]; - const NodeRef &linked_node = linked_socket.node(); - ToposortNodeState &linked_node_state = node_states[linked_node.id()]; - if (linked_node_state.is_done) { - /* The linked node has already been visited. */ - item.link_index++; - continue; - } - if (linked_node_state.is_in_stack) { - result.has_cycle = true; - } - else { - nodes_to_check.push({&linked_node}); - linked_node_state.is_in_stack = true; - } - break; - } - - /* If no other element has been pushed, the current node can be pushed to the sorted list. */ - if (&item == &nodes_to_check.peek()) { - ToposortNodeState &node_state = node_states[node.id()]; - node_state.is_done = true; - node_state.is_in_stack = false; - result.sorted_nodes.append(&node); - nodes_to_check.pop(); - } - } -} - -NodeTreeRef::ToposortResult NodeTreeRef::toposort(const ToposortDirection direction) const -{ - ToposortResult result; - result.sorted_nodes.reserve(nodes_by_id_.size()); - - Array<ToposortNodeState> node_states(nodes_by_id_.size()); - - for (const NodeRef *node : nodes_by_id_) { - if (node_states[node->id()].is_done) { - /* Ignore nodes that are done already. */ - continue; - } - if (node->any_socket_is_directly_linked( - direction == ToposortDirection::LeftToRight ? SOCK_OUT : SOCK_IN)) { - /* Ignore non-start nodes. */ - continue; - } - - toposort_from_start_node(direction, *node, node_states, result); - } - - /* Check if the loop above forgot some nodes because there is a cycle. */ - if (result.sorted_nodes.size() < nodes_by_id_.size()) { - result.has_cycle = true; - for (const NodeRef *node : nodes_by_id_) { - if (node_states[node->id()].is_done) { - /* Ignore nodes that are done already. */ - continue; - } - /* Start toposort at this node which is somewhere in the middle of a loop. */ - toposort_from_start_node(direction, *node, node_states, result); - } - } - - BLI_assert(result.sorted_nodes.size() == nodes_by_id_.size()); - return result; -} - -const NodeRef *NodeTreeRef::find_node(const bNode &bnode) const -{ - for (const NodeRef *node : this->nodes_by_type(bnode.typeinfo)) { - if (node->bnode_ == &bnode) { - return node; - } - } - return nullptr; -} - -std::string NodeTreeRef::to_dot() const -{ - dot::DirectedGraph digraph; - digraph.set_rankdir(dot::Attr_rankdir::LeftToRight); - - Map<const NodeRef *, dot::NodeWithSocketsRef> dot_nodes; - - for (const NodeRef *node : nodes_by_id_) { - dot::Node &dot_node = digraph.new_node(""); - dot_node.set_background_color("white"); - - Vector<std::string> input_names; - Vector<std::string> output_names; - for (const InputSocketRef *socket : node->inputs()) { - input_names.append(socket->name()); - } - for (const OutputSocketRef *socket : node->outputs()) { - output_names.append(socket->name()); - } - - dot_nodes.add_new(node, - dot::NodeWithSocketsRef(dot_node, node->name(), input_names, output_names)); - } - - for (const OutputSocketRef *from_socket : output_sockets_) { - for (const InputSocketRef *to_socket : from_socket->directly_linked_sockets()) { - dot::NodeWithSocketsRef &from_dot_node = dot_nodes.lookup(&from_socket->node()); - dot::NodeWithSocketsRef &to_dot_node = dot_nodes.lookup(&to_socket->node()); - - digraph.new_edge(from_dot_node.output(from_socket->index()), - to_dot_node.input(to_socket->index())); - } - } - - return digraph.to_dot_string(); -} - -const NodeTreeRef &get_tree_ref_from_map(NodeTreeRefMap &node_tree_refs, bNodeTree &btree) -{ - return *node_tree_refs.lookup_or_add_cb(&btree, - [&]() { return std::make_unique<NodeTreeRef>(&btree); }); -} - -PointerRNA NodeRef::rna() const -{ - PointerRNA rna; - RNA_pointer_create(&tree_->btree()->id, &RNA_Node, bnode_, &rna); - return rna; -} - -PointerRNA SocketRef::rna() const -{ - PointerRNA rna; - RNA_pointer_create(&this->tree().btree()->id, &RNA_NodeSocket, bsocket_, &rna); - return rna; -} - -} // namespace blender::nodes diff --git a/source/blender/nodes/intern/node_util.c b/source/blender/nodes/intern/node_util.c index e8be093c606..ddab455509d 100644 --- a/source/blender/nodes/intern/node_util.c +++ b/source/blender/nodes/intern/node_util.c @@ -200,7 +200,7 @@ void node_math_label(const bNodeTree *UNUSED(ntree), const bNode *node, char *la if (!enum_label) { name = "Unknown"; } - BLI_strncpy(label, IFACE_(name), maxlen); + BLI_strncpy(label, CTX_IFACE_(BLT_I18NCONTEXT_ID_NODETREE, name), maxlen); } void node_vector_math_label(const bNodeTree *UNUSED(ntree), diff --git a/source/blender/nodes/shader/CMakeLists.txt b/source/blender/nodes/shader/CMakeLists.txt index 3e90f185168..e6f90449843 100644 --- a/source/blender/nodes/shader/CMakeLists.txt +++ b/source/blender/nodes/shader/CMakeLists.txt @@ -67,6 +67,7 @@ set(SRC nodes/node_shader_map_range.cc nodes/node_shader_mapping.cc nodes/node_shader_math.cc + nodes/node_shader_mix.cc nodes/node_shader_mix_rgb.cc nodes/node_shader_mix_shader.cc nodes/node_shader_normal.cc @@ -131,22 +132,24 @@ set(LIB bf_nodes ) -if(WITH_PYTHON) - list(APPEND INC - ../../python - ) +if(WITH_FREESTYLE) + add_definitions(-DWITH_FREESTYLE) +endif() + +if(WITH_TBB) + add_definitions(-DWITH_TBB) + if(WIN32) + # TBB includes Windows.h which will define min/max macros + # that will collide with the stl versions. + add_definitions(-DNOMINMAX) + endif() list(APPEND INC_SYS - ${PYTHON_INCLUDE_DIRS} + ${TBB_INCLUDE_DIRS} ) + list(APPEND LIB - ${PYTHON_LINKFLAGS} - ${PYTHON_LIBRARIES} + ${TBB_LIBRARIES} ) - add_definitions(-DWITH_PYTHON) -endif() - -if(WITH_FREESTYLE) - add_definitions(-DWITH_FREESTYLE) endif() blender_add_lib(bf_nodes_shader "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/nodes/shader/node_shader_tree.cc b/source/blender/nodes/shader/node_shader_tree.cc index 02ac54f4b2b..a6d2e954a0c 100644 --- a/source/blender/nodes/shader/node_shader_tree.cc +++ b/source/blender/nodes/shader/node_shader_tree.cc @@ -26,6 +26,7 @@ #include "BLT_translation.h" #include "BKE_context.h" +#include "BKE_layer.h" #include "BKE_lib_id.h" #include "BKE_linestyle.h" #include "BKE_node.h" @@ -71,7 +72,8 @@ static void shader_get_from_context(const bContext *C, SpaceNode *snode = CTX_wm_space_node(C); Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); - Object *ob = OBACT(view_layer); + BKE_view_layer_synced_ensure(scene, view_layer); + Object *ob = BKE_view_layer_active_object_get(view_layer); if (snode->shaderfrom == SNODE_SHADER_OBJECT) { if (ob) { @@ -617,7 +619,7 @@ static bool ntree_shader_implicit_closure_cast(bNodeTree *ntree) bool modified = false; LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree->links) { if ((link->fromsock->type != SOCK_SHADER) && (link->tosock->type == SOCK_SHADER)) { - bNode *emission_node = nodeAddStaticNode(NULL, ntree, SH_NODE_EMISSION); + bNode *emission_node = nodeAddStaticNode(nullptr, ntree, SH_NODE_EMISSION); bNodeSocket *in_sock = ntree_shader_node_find_input(emission_node, "Color"); bNodeSocket *out_sock = ntree_shader_node_find_output(emission_node, "Emission"); nodeAddLink(ntree, link->fromnode, link->fromsock, emission_node, in_sock); @@ -645,7 +647,7 @@ static void ntree_weight_tree_merge_weight(bNodeTree *ntree, bNode **tonode, bNodeSocket **tosock) { - bNode *addnode = nodeAddStaticNode(NULL, ntree, SH_NODE_MATH); + bNode *addnode = nodeAddStaticNode(nullptr, ntree, SH_NODE_MATH); addnode->custom1 = NODE_MATH_ADD; addnode->tmp_flag = -2; /* Copy */ bNodeSocket *addsock_out = ntree_shader_node_output_get(addnode, 0); @@ -683,19 +685,19 @@ static bool ntree_weight_tree_tag_nodes(bNode *fromnode, bNode *tonode, void *us * with their respective weights. */ static void ntree_shader_weight_tree_invert(bNodeTree *ntree, bNode *output_node) { - bNodeLink *displace_link = NULL; + bNodeLink *displace_link = nullptr; bNodeSocket *displace_output = ntree_shader_node_find_input(output_node, "Displacement"); if (displace_output && displace_output->link) { /* Remove any displacement link to avoid tagging it later on. */ displace_link = displace_output->link; - displace_output->link = NULL; + displace_output->link = nullptr; } - bNodeLink *thickness_link = NULL; + bNodeLink *thickness_link = nullptr; bNodeSocket *thickness_output = ntree_shader_node_find_input(output_node, "Thickness"); if (thickness_output && thickness_output->link) { /* Remove any thickness link to avoid tagging it later on. */ thickness_link = thickness_output->link; - thickness_output->link = NULL; + thickness_output->link = nullptr; } /* Init tmp flag. */ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { @@ -717,7 +719,7 @@ static void ntree_shader_weight_tree_invert(bNodeTree *ntree, bNode *output_node case SH_NODE_OUTPUT_WORLD: case SH_NODE_OUTPUT_MATERIAL: { /* Start the tree with full weight. */ - nodes_copy[id] = nodeAddStaticNode(NULL, ntree, SH_NODE_VALUE); + nodes_copy[id] = nodeAddStaticNode(nullptr, ntree, SH_NODE_VALUE); nodes_copy[id]->tmp_flag = -2; /* Copy */ ((bNodeSocketValueFloat *)ntree_shader_node_output_get(nodes_copy[id], 0)->default_value) ->value = 1.0f; @@ -726,7 +728,7 @@ static void ntree_shader_weight_tree_invert(bNodeTree *ntree, bNode *output_node case SH_NODE_ADD_SHADER: { /* Simple passthrough node. Each original inputs will get the same weight. */ /* TODO(fclem): Better use some kind of reroute node? */ - nodes_copy[id] = nodeAddStaticNode(NULL, ntree, SH_NODE_MATH); + nodes_copy[id] = nodeAddStaticNode(nullptr, ntree, SH_NODE_MATH); nodes_copy[id]->custom1 = NODE_MATH_ADD; nodes_copy[id]->tmp_flag = -2; /* Copy */ ((bNodeSocketValueFloat *)ntree_shader_node_input_get(nodes_copy[id], 0)->default_value) @@ -739,17 +741,17 @@ static void ntree_shader_weight_tree_invert(bNodeTree *ntree, bNode *output_node bNodeSocket *fromsock, *tosock; int id_start = id; /* output = (factor * input_weight) */ - nodes_copy[id] = nodeAddStaticNode(NULL, ntree, SH_NODE_MATH); + nodes_copy[id] = nodeAddStaticNode(nullptr, ntree, SH_NODE_MATH); nodes_copy[id]->custom1 = NODE_MATH_MULTIPLY; nodes_copy[id]->tmp_flag = -2; /* Copy */ id++; /* output = ((1.0 - factor) * input_weight) <=> (input_weight - factor * input_weight) */ - nodes_copy[id] = nodeAddStaticNode(NULL, ntree, SH_NODE_MATH); + nodes_copy[id] = nodeAddStaticNode(nullptr, ntree, SH_NODE_MATH); nodes_copy[id]->custom1 = NODE_MATH_SUBTRACT; nodes_copy[id]->tmp_flag = -2; /* Copy */ id++; /* Node sanitizes the input mix factor by clamping it. */ - nodes_copy[id] = nodeAddStaticNode(NULL, ntree, SH_NODE_MATH); + nodes_copy[id] = nodeAddStaticNode(nullptr, ntree, SH_NODE_MATH); nodes_copy[id]->custom1 = NODE_MATH_ADD; nodes_copy[id]->custom2 = SHD_MATH_CLAMP; nodes_copy[id]->tmp_flag = -2; /* Copy */ @@ -765,7 +767,7 @@ static void ntree_shader_weight_tree_invert(bNodeTree *ntree, bNode *output_node id++; /* Reroute the weight input to the 3 processing nodes. Simplify linking later-on. */ /* TODO(fclem): Better use some kind of reroute node? */ - nodes_copy[id] = nodeAddStaticNode(NULL, ntree, SH_NODE_MATH); + nodes_copy[id] = nodeAddStaticNode(nullptr, ntree, SH_NODE_MATH); nodes_copy[id]->custom1 = NODE_MATH_ADD; nodes_copy[id]->tmp_flag = -2; /* Copy */ ((bNodeSocketValueFloat *)ntree_shader_node_input_get(nodes_copy[id], 0)->default_value) @@ -1040,7 +1042,7 @@ void ntreeGPUMaterialNodes(bNodeTree *localtree, GPUMaterial *mat) /* Tree is valid if it contains no undefined implicit socket type cast. */ bool valid_tree = ntree_shader_implicit_closure_cast(localtree); - if (valid_tree && output != NULL) { + if (valid_tree && output != nullptr) { ntree_shader_pruned_unused(localtree, output); ntree_shader_shader_to_rgba_branch(localtree, output); ntree_shader_weight_tree_invert(localtree, output); diff --git a/source/blender/nodes/shader/node_shader_util.hh b/source/blender/nodes/shader/node_shader_util.hh index d5f54d9cac9..38220634695 100644 --- a/source/blender/nodes/shader/node_shader_util.hh +++ b/source/blender/nodes/shader/node_shader_util.hh @@ -59,6 +59,8 @@ #include "RE_pipeline.h" #include "RE_texture.h" +#include "RNA_access.h" + bool sh_node_poll_default(struct bNodeType *ntype, struct bNodeTree *ntree, const char **r_disabled_hint); diff --git a/source/blender/nodes/shader/nodes/node_shader_attribute.cc b/source/blender/nodes/shader/nodes/node_shader_attribute.cc index d01271c15d3..65d053e6379 100644 --- a/source/blender/nodes/shader/nodes/node_shader_attribute.cc +++ b/source/blender/nodes/shader/nodes/node_shader_attribute.cc @@ -36,6 +36,7 @@ static int node_shader_gpu_attribute(GPUMaterial *mat, { NodeShaderAttribute *attr = static_cast<NodeShaderAttribute *>(node->storage); bool is_varying = attr->type == SHD_ATTRIBUTE_GEOMETRY; + float attr_hash = 0.0f; GPUNodeLink *cd_attr; @@ -43,7 +44,12 @@ static int node_shader_gpu_attribute(GPUMaterial *mat, cd_attr = GPU_attribute(mat, CD_AUTO_FROM_NAME, attr->name); } else { - cd_attr = GPU_uniform_attribute(mat, attr->name, attr->type == SHD_ATTRIBUTE_INSTANCER); + cd_attr = GPU_uniform_attribute(mat, + attr->name, + attr->type == SHD_ATTRIBUTE_INSTANCER, + reinterpret_cast<uint32_t *>(&attr_hash)); + + GPU_link(mat, "node_attribute_uniform", cd_attr, GPU_constant(&attr_hash), &cd_attr); } if (STREQ(attr->name, "color")) { @@ -55,9 +61,11 @@ static int node_shader_gpu_attribute(GPUMaterial *mat, GPU_stack_link(mat, node, "node_attribute", in, out, cd_attr); - int i; - LISTBASE_FOREACH_INDEX (bNodeSocket *, sock, &node->outputs, i) { - node_shader_gpu_bump_tex_coord(mat, node, &out[i].link); + if (is_varying) { + int i; + LISTBASE_FOREACH_INDEX (bNodeSocket *, sock, &node->outputs, i) { + node_shader_gpu_bump_tex_coord(mat, node, &out[i].link); + } } return 1; diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc index a63c7aede04..7b72d4b9be4 100644 --- a/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc +++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc @@ -167,6 +167,27 @@ static int node_shader_gpu_bsdf_principled(GPUMaterial *mat, if (use_transparency) { flag |= GPU_MATFLAG_TRANSPARENT; } + if (use_clear) { + flag |= GPU_MATFLAG_CLEARCOAT; + } + + /* Ref. T98190: Defines are optimizations for old compilers. + * Might become unnecessary with EEVEE-Next. */ + if (use_diffuse == false && use_refract == false && use_clear == true) { + flag |= GPU_MATFLAG_PRINCIPLED_CLEARCOAT; + } + else if (use_diffuse == false && use_refract == false && use_clear == false) { + flag |= GPU_MATFLAG_PRINCIPLED_METALLIC; + } + else if (use_diffuse == true && use_refract == false && use_clear == false) { + flag |= GPU_MATFLAG_PRINCIPLED_DIELECTRIC; + } + else if (use_diffuse == false && use_refract == true && use_clear == false) { + flag |= GPU_MATFLAG_PRINCIPLED_GLASS; + } + else { + flag |= GPU_MATFLAG_PRINCIPLED_ANY; + } if (use_subsurf) { bNodeSocket *socket = (bNodeSocket *)BLI_findlink(&node->original->inputs, 2); diff --git a/source/blender/nodes/shader/nodes/node_shader_color_ramp.cc b/source/blender/nodes/shader/nodes/node_shader_color_ramp.cc index 3723480ffa3..d73ffd89288 100644 --- a/source/blender/nodes/shader/nodes/node_shader_color_ramp.cc +++ b/source/blender/nodes/shader/nodes/node_shader_color_ramp.cc @@ -125,7 +125,7 @@ class ColorBandFunction : public fn::MultiFunction { static void sh_node_valtorgb_build_multi_function(nodes::NodeMultiFunctionBuilder &builder) { - bNode &bnode = builder.node(); + const bNode &bnode = builder.node(); const ColorBand *color_band = (const ColorBand *)bnode.storage; builder.construct_and_set_matching_fn<ColorBandFunction>(*color_band); } diff --git a/source/blender/nodes/shader/nodes/node_shader_curves.cc b/source/blender/nodes/shader/nodes/node_shader_curves.cc index eb47059063d..4725aef5991 100644 --- a/source/blender/nodes/shader/nodes/node_shader_curves.cc +++ b/source/blender/nodes/shader/nodes/node_shader_curves.cc @@ -95,7 +95,7 @@ class CurveVecFunction : public fn::MultiFunction { static void sh_node_curve_vec_build_multi_function(NodeMultiFunctionBuilder &builder) { - bNode &bnode = builder.node(); + const bNode &bnode = builder.node(); CurveMapping *cumap = (CurveMapping *)bnode.storage; BKE_curvemapping_init(cumap); builder.construct_and_set_matching_fn<CurveVecFunction>(*cumap); @@ -237,7 +237,7 @@ class CurveRGBFunction : public fn::MultiFunction { static void sh_node_curve_rgb_build_multi_function(NodeMultiFunctionBuilder &builder) { - bNode &bnode = builder.node(); + const bNode &bnode = builder.node(); CurveMapping *cumap = (CurveMapping *)bnode.storage; BKE_curvemapping_init(cumap); builder.construct_and_set_matching_fn<CurveRGBFunction>(*cumap); @@ -356,7 +356,7 @@ class CurveFloatFunction : public fn::MultiFunction { static void sh_node_curve_float_build_multi_function(NodeMultiFunctionBuilder &builder) { - bNode &bnode = builder.node(); + const bNode &bnode = builder.node(); CurveMapping *cumap = (CurveMapping *)bnode.storage; BKE_curvemapping_init(cumap); builder.construct_and_set_matching_fn<CurveFloatFunction>(*cumap); diff --git a/source/blender/nodes/shader/nodes/node_shader_math.cc b/source/blender/nodes/shader/nodes/node_shader_math.cc index 8a2b18d7d76..bd83f8dac37 100644 --- a/source/blender/nodes/shader/nodes/node_shader_math.cc +++ b/source/blender/nodes/shader/nodes/node_shader_math.cc @@ -61,8 +61,9 @@ static void sh_node_math_gather_link_searches(GatherLinkSearchOpParams ¶ms) ELEM(item->value, NODE_MATH_COMPARE, NODE_MATH_GREATER_THAN, NODE_MATH_LESS_THAN)) ? -1 : weight; - params.add_item( - IFACE_(item->name), SocketSearchOp{"Value", (NodeMathOperation)item->value}, gn_weight); + params.add_item(CTX_IFACE_(BLT_I18NCONTEXT_ID_NODETREE, item->name), + SocketSearchOp{"Value", (NodeMathOperation)item->value}, + gn_weight); } } } @@ -101,7 +102,7 @@ static int gpu_shader_math(GPUMaterial *mat, return 0; } -static const fn::MultiFunction *get_base_multi_function(bNode &node) +static const fn::MultiFunction *get_base_multi_function(const bNode &node) { const int mode = node.custom1; const fn::MultiFunction *base_fn = nullptr; diff --git a/source/blender/nodes/shader/nodes/node_shader_mix.cc b/source/blender/nodes/shader/nodes/node_shader_mix.cc new file mode 100644 index 00000000000..f785e32832e --- /dev/null +++ b/source/blender/nodes/shader/nodes/node_shader_mix.cc @@ -0,0 +1,443 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2005 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup shdnodes + */ + +#include <algorithm> + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_shader_util.hh" + +#include "NOD_socket_search_link.hh" +#include "RNA_enum_types.h" + +namespace blender::nodes::node_sh_mix_cc { + +NODE_STORAGE_FUNCS(NodeShaderMix) + +static void sh_node_mix_declare(NodeDeclarationBuilder &b) +{ + b.is_function_node(); + b.add_input<decl::Float>(N_("Factor"), "Factor_Float") + .default_value(0.5f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Vector>(N_("Factor"), "Factor_Vector") + .default_value(float3(0.5f)) + .subtype(PROP_FACTOR); + + b.add_input<decl::Float>(N_("A"), "A_Float") + .min(-10000.0f) + .max(10000.0f) + .is_default_link_socket(); + b.add_input<decl::Float>(N_("B"), "B_Float").min(-10000.0f).max(10000.0f); + + b.add_input<decl::Vector>(N_("A"), "A_Vector").is_default_link_socket(); + b.add_input<decl::Vector>(N_("B"), "B_Vector"); + + b.add_input<decl::Color>(N_("A"), "A_Color") + .default_value({0.5f, 0.5f, 0.5f, 1.0f}) + .is_default_link_socket(); + b.add_input<decl::Color>(N_("B"), "B_Color").default_value({0.5f, 0.5f, 0.5f, 1.0f}); + + b.add_output<decl::Float>(N_("Result"), "Result_Float"); + b.add_output<decl::Vector>(N_("Result"), "Result_Vector"); + b.add_output<decl::Color>(N_("Result"), "Result_Color"); +}; + +static void sh_node_mix_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + const NodeShaderMix &data = node_storage(*static_cast<const bNode *>(ptr->data)); + uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); + if (data.data_type == SOCK_VECTOR) { + uiItemR(layout, ptr, "factor_mode", 0, "", ICON_NONE); + } + if (data.data_type == SOCK_RGBA) { + uiItemR(layout, ptr, "blend_type", 0, "", ICON_NONE); + uiItemR(layout, ptr, "clamp_result", 0, nullptr, ICON_NONE); + uiItemR(layout, ptr, "clamp_factor", 0, nullptr, ICON_NONE); + } + else { + uiItemR(layout, ptr, "clamp_factor", 0, nullptr, ICON_NONE); + } +} + +static void sh_node_mix_label(const bNodeTree *UNUSED(ntree), + const bNode *node, + char *label, + int maxlen) +{ + const NodeShaderMix &storage = node_storage(*node); + if (storage.data_type == SOCK_RGBA) { + const char *name; + bool enum_label = RNA_enum_name(rna_enum_ramp_blend_items, storage.blend_type, &name); + if (!enum_label) { + name = "Unknown"; + } + BLI_strncpy(label, IFACE_(name), maxlen); + } +} + +static int sh_node_mix_ui_class(const bNode *node) +{ + const NodeShaderMix &storage = node_storage(*node); + const eNodeSocketDatatype data_type = static_cast<eNodeSocketDatatype>(storage.data_type); + + switch (data_type) { + case SOCK_VECTOR: + return NODE_CLASS_OP_VECTOR; + case SOCK_RGBA: + return NODE_CLASS_OP_COLOR; + default: + return NODE_CLASS_CONVERTER; + } +} + +static void sh_node_mix_update(bNodeTree *ntree, bNode *node) +{ + const NodeShaderMix &storage = node_storage(*node); + const eNodeSocketDatatype data_type = static_cast<eNodeSocketDatatype>(storage.data_type); + + LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) { + nodeSetSocketAvailability(ntree, socket, socket->type == data_type); + } + + LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) { + nodeSetSocketAvailability(ntree, socket, socket->type == data_type); + } + + bool use_vector_factor = data_type == SOCK_VECTOR && + storage.factor_mode != NODE_MIX_MODE_UNIFORM; + + bNodeSocket *sock_factor = (bNodeSocket *)BLI_findlink(&node->inputs, 0); + nodeSetSocketAvailability(ntree, sock_factor, !use_vector_factor); + + bNodeSocket *sock_factor_vec = (bNodeSocket *)BLI_findlink(&node->inputs, 1); + nodeSetSocketAvailability(ntree, sock_factor_vec, use_vector_factor); +} + +static void node_mix_gather_link_searches(GatherLinkSearchOpParams ¶ms) +{ + const eNodeSocketDatatype sock_type = static_cast<eNodeSocketDatatype>( + params.other_socket().type); + + if (ELEM(sock_type, SOCK_BOOLEAN, SOCK_FLOAT, SOCK_RGBA, SOCK_VECTOR, SOCK_INT)) { + const eNodeSocketDatatype type = ELEM(sock_type, SOCK_BOOLEAN, SOCK_INT) ? SOCK_FLOAT : + sock_type; + + if (params.in_out() == SOCK_OUT) { + params.add_item(IFACE_("Result"), [type](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node("ShaderNodeMix"); + node_storage(node).data_type = type; + params.update_and_connect_available_socket(node, "Result"); + }); + } + else { + if (ELEM(sock_type, SOCK_VECTOR, SOCK_RGBA)) { + params.add_item(IFACE_("Factor (Non-Uniform)"), [](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node("ShaderNodeMix"); + node_storage(node).data_type = SOCK_VECTOR; + node_storage(node).factor_mode = NODE_MIX_MODE_NON_UNIFORM; + params.update_and_connect_available_socket(node, "Factor"); + }); + } + params.add_item(IFACE_("Factor"), [type](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node("ShaderNodeMix"); + node_storage(node).data_type = type; + params.update_and_connect_available_socket(node, "Factor"); + }); + params.add_item(IFACE_("A"), [type](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node("ShaderNodeMix"); + node_storage(node).data_type = type; + params.update_and_connect_available_socket(node, "A"); + }); + params.add_item(IFACE_("B"), [type](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node("ShaderNodeMix"); + node_storage(node).data_type = type; + params.update_and_connect_available_socket(node, "B"); + }); + } + } +} + +static void node_mix_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeShaderMix *data = MEM_cnew<NodeShaderMix>(__func__); + data->data_type = SOCK_FLOAT; + data->factor_mode = NODE_MIX_MODE_UNIFORM; + data->clamp_factor = 1; + data->clamp_result = 0; + data->blend_type = MA_RAMP_BLEND; + node->storage = data; +} + +static const char *gpu_shader_get_name(eNodeSocketDatatype data_type, + const bool non_uniform, + const int blend_type) +{ + switch (data_type) { + case SOCK_FLOAT: + return "node_mix_float"; + case SOCK_VECTOR: + return (non_uniform) ? "node_mix_vector_non_uniform" : "node_mix_vector"; + case SOCK_RGBA: + switch (blend_type) { + case MA_RAMP_BLEND: + return "node_mix_blend"; + case MA_RAMP_ADD: + return "node_mix_add"; + case MA_RAMP_MULT: + return "node_mix_mult"; + case MA_RAMP_SUB: + return "node_mix_sub"; + case MA_RAMP_SCREEN: + return "node_mix_screen"; + case MA_RAMP_DIV: + return "node_mix_div_fallback"; + case MA_RAMP_DIFF: + return "node_mix_diff"; + case MA_RAMP_DARK: + return "node_mix_dark"; + case MA_RAMP_LIGHT: + return "node_mix_light"; + case MA_RAMP_OVERLAY: + return "node_mix_overlay"; + case MA_RAMP_DODGE: + return "node_mix_dodge"; + case MA_RAMP_BURN: + return "node_mix_burn"; + case MA_RAMP_HUE: + return "node_mix_hue"; + case MA_RAMP_SAT: + return "node_mix_sat"; + case MA_RAMP_VAL: + return "node_mix_val"; + case MA_RAMP_COLOR: + return "node_mix_color"; + case MA_RAMP_SOFT: + return "node_mix_soft"; + case MA_RAMP_LINEAR: + return "node_mix_linear"; + default: + BLI_assert_unreachable(); + return nullptr; + } + default: + BLI_assert_unreachable(); + return nullptr; + } +} + +static int gpu_shader_mix(GPUMaterial *mat, + bNode *node, + bNodeExecData *UNUSED(execdata), + GPUNodeStack *in, + GPUNodeStack *out) +{ + const NodeShaderMix &storage = node_storage(*node); + const bool is_non_uniform = storage.factor_mode == NODE_MIX_MODE_NON_UNIFORM; + const bool is_color_mode = storage.data_type == SOCK_RGBA; + const bool is_vector_mode = storage.data_type == SOCK_VECTOR; + const int blend_type = storage.blend_type; + const char *name = gpu_shader_get_name( + (eNodeSocketDatatype)storage.data_type, is_non_uniform, blend_type); + + if (name == nullptr) { + return 0; + } + + if (storage.clamp_factor) { + if (is_non_uniform && is_vector_mode) { + const float min[3] = {0.0f, 0.0f, 0.0f}; + const float max[3] = {1.0f, 1.0f, 1.0f}; + const GPUNodeLink *factor_link = in[1].link ? in[1].link : GPU_uniform(in[1].vec); + GPU_link(mat, + "node_mix_clamp_vector", + factor_link, + GPU_constant(min), + GPU_constant(max), + &in[1].link); + } + else { + const float min = 0.0f; + const float max = 1.0f; + const GPUNodeLink *factor_link = in[0].link ? in[0].link : GPU_uniform(in[0].vec); + GPU_link(mat, + "node_mix_clamp_value", + factor_link, + GPU_constant(&min), + GPU_constant(&max), + &in[0].link); + } + } + + int ret = GPU_stack_link(mat, node, name, in, out); + + if (ret && is_color_mode && storage.clamp_result) { + const float min[3] = {0.0f, 0.0f, 0.0f}; + const float max[3] = {1.0f, 1.0f, 1.0f}; + GPU_link(mat, + "node_mix_clamp_vector", + out[2].link, + GPU_constant(min), + GPU_constant(max), + &out[2].link); + } + return ret; +} + +class MixColorFunction : public fn::MultiFunction { + private: + const bool clamp_factor_; + const bool clamp_result_; + const int blend_type_; + + public: + MixColorFunction(const bool clamp_factor, const bool clamp_result, const int blend_type) + : clamp_factor_(clamp_factor), clamp_result_(clamp_result), blend_type_(blend_type) + { + static fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static fn::MFSignature create_signature() + { + fn::MFSignatureBuilder signature{"MixColor"}; + signature.single_input<float>("Factor"); + signature.single_input<ColorGeometry4f>("A"); + signature.single_input<ColorGeometry4f>("B"); + signature.single_output<ColorGeometry4f>("Result"); + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + const VArray<float> &fac = params.readonly_single_input<float>(0, "Factor"); + const VArray<ColorGeometry4f> &col1 = params.readonly_single_input<ColorGeometry4f>(1, "A"); + const VArray<ColorGeometry4f> &col2 = params.readonly_single_input<ColorGeometry4f>(2, "B"); + MutableSpan<ColorGeometry4f> results = params.uninitialized_single_output<ColorGeometry4f>( + 3, "Result"); + + if (clamp_factor_) { + for (int64_t i : mask) { + results[i] = col1[i]; + ramp_blend(blend_type_, results[i], std::clamp(fac[i], 0.0f, 1.0f), col2[i]); + } + } + else { + for (int64_t i : mask) { + results[i] = col1[i]; + ramp_blend(blend_type_, results[i], fac[i], col2[i]); + } + } + + if (clamp_result_) { + for (int64_t i : mask) { + clamp_v3(results[i], 0.0f, 1.0f); + } + } + } +}; + +static const fn::MultiFunction *get_multi_function(const bNode &node) +{ + const NodeShaderMix *data = (NodeShaderMix *)node.storage; + bool uniform_factor = data->factor_mode == NODE_MIX_MODE_UNIFORM; + const bool clamp_factor = data->clamp_factor; + switch (data->data_type) { + case SOCK_FLOAT: { + if (clamp_factor) { + static fn::CustomMF_SI_SI_SI_SO<float, float, float, float> fn{ + "Clamp Mix Float", [](float t, const float a, const float b) { + return math::interpolate(a, b, std::clamp(t, 0.0f, 1.0f)); + }}; + return &fn; + } + else { + static fn::CustomMF_SI_SI_SI_SO<float, float, float, float> fn{ + "Mix Float", [](const float t, const float a, const float b) { + return math::interpolate(a, b, t); + }}; + return &fn; + } + } + case SOCK_VECTOR: { + if (clamp_factor) { + if (uniform_factor) { + static fn::CustomMF_SI_SI_SI_SO<float, float3, float3, float3> fn{ + "Clamp Mix Vector", [](const float t, const float3 a, const float3 b) { + return math::interpolate(a, b, std::clamp(t, 0.0f, 1.0f)); + }}; + return &fn; + } + else { + static fn::CustomMF_SI_SI_SI_SO<float3, float3, float3, float3> fn{ + "Clamp Mix Vector Non Uniform", [](float3 t, const float3 a, const float3 b) { + t = math::clamp(t, 0.0f, 1.0f); + return a * (float3(1.0f) - t) + b * t; + }}; + return &fn; + } + } + else { + if (uniform_factor) { + static fn::CustomMF_SI_SI_SI_SO<float, float3, float3, float3> fn{ + "Mix Vector", [](const float t, const float3 a, const float3 b) { + return math::interpolate(a, b, t); + }}; + return &fn; + } + else { + static fn::CustomMF_SI_SI_SI_SO<float3, float3, float3, float3> fn{ + "Mix Vector Non Uniform", [](const float3 t, const float3 a, const float3 b) { + return a * (float3(1.0f) - t) + b * t; + }}; + return &fn; + } + } + } + } + BLI_assert_unreachable(); + return nullptr; +} + +static void sh_node_mix_build_multi_function(NodeMultiFunctionBuilder &builder) +{ + const NodeShaderMix &storage = node_storage(builder.node()); + + if (storage.data_type == SOCK_RGBA) { + builder.construct_and_set_matching_fn<MixColorFunction>( + storage.clamp_factor, storage.clamp_result, storage.blend_type); + } + else { + const fn::MultiFunction *fn = get_multi_function(builder.node()); + builder.set_matching_fn(fn); + } +} + +} // namespace blender::nodes::node_sh_mix_cc + +void register_node_type_sh_mix() +{ + namespace file_ns = blender::nodes::node_sh_mix_cc; + + static bNodeType ntype; + sh_fn_node_type_base(&ntype, SH_NODE_MIX, "Mix", NODE_CLASS_CONVERTER); + ntype.declare = file_ns::sh_node_mix_declare; + ntype.ui_class = file_ns::sh_node_mix_ui_class; + node_type_gpu(&ntype, file_ns::gpu_shader_mix); + node_type_update(&ntype, file_ns::sh_node_mix_update); + node_type_init(&ntype, file_ns::node_mix_init); + node_type_storage( + &ntype, "NodeShaderMix", node_free_standard_storage, node_copy_standard_storage); + ntype.build_multi_function = file_ns::sh_node_mix_build_multi_function; + ntype.draw_buttons = file_ns::sh_node_mix_layout; + ntype.labelfunc = file_ns::sh_node_mix_label; + ntype.gather_link_search_ops = file_ns::node_mix_gather_link_searches; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/shader/nodes/node_shader_mix_rgb.cc b/source/blender/nodes/shader/nodes/node_shader_mix_rgb.cc index 478a6812c36..46ac8f05803 100644 --- a/source/blender/nodes/shader/nodes/node_shader_mix_rgb.cc +++ b/source/blender/nodes/shader/nodes/node_shader_mix_rgb.cc @@ -136,7 +136,7 @@ class MixRGBFunction : public fn::MultiFunction { static void sh_node_mix_rgb_build_multi_function(NodeMultiFunctionBuilder &builder) { - bNode &node = builder.node(); + const bNode &node = builder.node(); bool clamp = node.custom2 & SHD_MIXRGB_CLAMP; int mix_type = node.custom1; builder.construct_and_set_matching_fn<MixRGBFunction>(clamp, mix_type); @@ -150,11 +150,11 @@ void register_node_type_sh_mix_rgb() static bNodeType ntype; - sh_fn_node_type_base(&ntype, SH_NODE_MIX_RGB, "Mix", NODE_CLASS_OP_COLOR); + sh_fn_node_type_base(&ntype, SH_NODE_MIX_RGB_LEGACY, "Mix", NODE_CLASS_OP_COLOR); ntype.declare = file_ns::sh_node_mix_rgb_declare; ntype.labelfunc = node_blend_label; node_type_gpu(&ntype, file_ns::gpu_shader_mix_rgb); ntype.build_multi_function = file_ns::sh_node_mix_rgb_build_multi_function; - + ntype.gather_link_search_ops = nullptr; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_brick.cc b/source/blender/nodes/shader/nodes/node_shader_tex_brick.cc index cad9e1b33f2..a1c51a440d0 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_brick.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_brick.cc @@ -261,7 +261,7 @@ class BrickFunction : public fn::MultiFunction { static void sh_node_brick_build_multi_function(NodeMultiFunctionBuilder &builder) { - bNode &node = builder.node(); + const bNode &node = builder.node(); NodeTexBrick *tex = (NodeTexBrick *)node.storage; builder.construct_and_set_matching_fn<BrickFunction>( diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_coord.cc b/source/blender/nodes/shader/nodes/node_shader_tex_coord.cc index 0a28b34902e..9727a6089a6 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_coord.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_coord.cc @@ -38,8 +38,8 @@ static int node_shader_gpu_tex_coord(GPUMaterial *mat, /* Use special matrix to let the shader branch to using the render object's matrix. */ float dummy_matrix[4][4]; dummy_matrix[3][3] = 0.0f; - GPUNodeLink *inv_obmat = (ob != NULL) ? GPU_uniform(&ob->imat[0][0]) : - GPU_uniform(&dummy_matrix[0][0]); + GPUNodeLink *inv_obmat = (ob != nullptr) ? GPU_uniform(&ob->imat[0][0]) : + GPU_uniform(&dummy_matrix[0][0]); /* Optimization: don't request orco if not needed. */ float4 zero(0.0f); diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_environment.cc b/source/blender/nodes/shader/nodes/node_shader_tex_environment.cc index df5fbac3ab5..2739a75ed21 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_environment.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_environment.cc @@ -50,7 +50,11 @@ static int node_shader_gpu_tex_environment(GPUMaterial *mat, return GPU_stack_link(mat, node, "node_tex_environment_empty", in, out); } - node_shader_gpu_default_tex_coord(mat, node, &in[0].link); + if (!in[0].link) { + GPU_link(mat, "node_tex_coord_position", &in[0].link); + node_shader_gpu_bump_tex_coord(mat, node, &in[0].link); + } + node_shader_gpu_tex_mapping(mat, node, in, out); /* Compute texture coordinate. */ diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_gradient.cc b/source/blender/nodes/shader/nodes/node_shader_tex_gradient.cc index 8478cbd406b..37c72ec1f17 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_gradient.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_gradient.cc @@ -139,7 +139,7 @@ class GradientFunction : public fn::MultiFunction { static void sh_node_gradient_tex_build_multi_function(NodeMultiFunctionBuilder &builder) { - bNode &node = builder.node(); + const bNode &node = builder.node(); NodeTexGradient *tex = (NodeTexGradient *)node.storage; builder.construct_and_set_matching_fn<GradientFunction>(tex->gradient_type); } diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_magic.cc b/source/blender/nodes/shader/nodes/node_shader_tex_magic.cc index 95c4a8b8e46..205d3b89016 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_magic.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_magic.cc @@ -161,7 +161,7 @@ class MagicFunction : public fn::MultiFunction { static void sh_node_magic_tex_build_multi_function(NodeMultiFunctionBuilder &builder) { - bNode &node = builder.node(); + const bNode &node = builder.node(); NodeTexMagic *tex = (NodeTexMagic *)node.storage; builder.construct_and_set_matching_fn<MagicFunction>(tex->depth); } diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc b/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc index c13ce3c3df3..a2241c2327f 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc @@ -516,7 +516,7 @@ class MusgraveFunction : public fn::MultiFunction { static void sh_node_musgrave_build_multi_function(NodeMultiFunctionBuilder &builder) { - bNode &node = builder.node(); + const bNode &node = builder.node(); NodeTexMusgrave *tex = (NodeTexMusgrave *)node.storage; builder.construct_and_set_matching_fn<MusgraveFunction>(tex->dimensions, tex->musgrave_type); } diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_sky.cc b/source/blender/nodes/shader/nodes/node_shader_tex_sky.cc index f5a4d087dbd..97b7f2616ae 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_sky.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_sky.cc @@ -4,6 +4,8 @@ #include "node_shader_util.hh" #include "sky_model.h" +#include "BLI_task.hh" + #include "BKE_context.h" #include "BKE_scene.h" @@ -36,7 +38,7 @@ static void node_shader_buts_tex_sky(uiLayout *layout, bContext *C, PointerRNA * if (RNA_enum_get(ptr, "sky_type") == SHD_SKY_NISHITA) { Scene *scene = CTX_data_scene(C); if (BKE_scene_uses_blender_eevee(scene)) { - uiItemL(layout, TIP_("Nishita not available in Eevee"), ICON_ERROR); + uiItemL(layout, TIP_("Sun disc not available in Eevee"), ICON_ERROR); } uiItemR(layout, ptr, "sun_disc", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, 0); @@ -179,7 +181,7 @@ static int node_shader_gpu_tex_sky(GPUMaterial *mat, GPU_uniform(xyz_to_rgb.g), GPU_uniform(xyz_to_rgb.b)); } - if (tex->sky_model == 1) { + else if (tex->sky_model == 1) { /* Hosek / Wilkie */ sun_angles[0] = fmin(M_PI_2, sun_angles[0]); /* clamp to horizon */ SKY_ArHosekSkyModelState *sky_state = SKY_arhosek_xyz_skymodelstate_alloc_init( @@ -219,8 +221,52 @@ static int node_shader_gpu_tex_sky(GPUMaterial *mat, GPU_uniform(xyz_to_rgb.g), GPU_uniform(xyz_to_rgb.b)); } + else { + /* Nishita */ + + Array<float> pixels(4 * GPU_SKY_WIDTH * GPU_SKY_HEIGHT); + + threading::parallel_for(IndexRange(GPU_SKY_HEIGHT), 2, [&](IndexRange range) { + SKY_nishita_skymodel_precompute_texture(pixels.data(), + 4, + range.first(), + range.one_after_last(), + GPU_SKY_WIDTH, + GPU_SKY_HEIGHT, + tex->sun_elevation, + tex->altitude, + tex->air_density, + tex->dust_density, + tex->ozone_density); + }); + + float sun_rotation = fmodf(tex->sun_rotation, 2.0f * M_PI); + if (sun_rotation < 0.0f) { + sun_rotation += 2.0f * M_PI; + } + sun_rotation = 2.0f * M_PI - sun_rotation; + + XYZ_to_RGB xyz_to_rgb; + get_XYZ_to_RGB_for_gpu(&xyz_to_rgb); - return GPU_stack_link(mat, node, "node_tex_sky_nishita", in, out); + eGPUSamplerState sampler = GPU_SAMPLER_REPEAT | GPU_SAMPLER_FILTER; + /* To fix pole issue we clamp the v coordinate. */ + sampler &= ~GPU_SAMPLER_REPEAT_T; + float layer; + GPUNodeLink *sky_texture = GPU_image_sky( + mat, GPU_SKY_WIDTH, GPU_SKY_HEIGHT, pixels.data(), &layer, sampler); + return GPU_stack_link(mat, + node, + "node_tex_sky_nishita", + in, + out, + GPU_constant(&sun_rotation), + GPU_uniform(xyz_to_rgb.r), + GPU_uniform(xyz_to_rgb.g), + GPU_uniform(xyz_to_rgb.b), + sky_texture, + GPU_constant(&layer)); + } } static void node_shader_update_sky(bNodeTree *ntree, bNode *node) diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_wave.cc b/source/blender/nodes/shader/nodes/node_shader_tex_wave.cc index ad24224dc7f..8475101dbaf 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_wave.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_wave.cc @@ -206,7 +206,7 @@ class WaveFunction : public fn::MultiFunction { static void sh_node_wave_tex_build_multi_function(NodeMultiFunctionBuilder &builder) { - bNode &node = builder.node(); + const bNode &node = builder.node(); NodeTexWave *tex = (NodeTexWave *)node.storage; builder.construct_and_set_matching_fn<WaveFunction>( tex->wave_type, tex->bands_direction, tex->rings_direction, tex->wave_profile); diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc b/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc index 6d4c491046b..64075a903ab 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc @@ -176,7 +176,7 @@ class WhiteNoiseFunction : public fn::MultiFunction { static void sh_node_noise_build_multi_function(NodeMultiFunctionBuilder &builder) { - bNode &node = builder.node(); + const bNode &node = builder.node(); builder.construct_and_set_matching_fn<WhiteNoiseFunction>((int)node.custom1); } diff --git a/source/blender/nodes/shader/nodes/node_shader_vector_math.cc b/source/blender/nodes/shader/nodes/node_shader_vector_math.cc index 21f5c44c640..d01e03f9d71 100644 --- a/source/blender/nodes/shader/nodes/node_shader_vector_math.cc +++ b/source/blender/nodes/shader/nodes/node_shader_vector_math.cc @@ -225,7 +225,7 @@ static void node_shader_update_vector_math(bNodeTree *ntree, bNode *node) } } -static const fn::MultiFunction *get_multi_function(bNode &node) +static const fn::MultiFunction *get_multi_function(const bNode &node) { NodeVectorMathOperation operation = NodeVectorMathOperation(node.custom1); diff --git a/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc b/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc index b35f686e331..a036fc52d84 100644 --- a/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc +++ b/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc @@ -96,7 +96,7 @@ static float3 sh_node_vector_rotate_euler(const float3 &vector, return result + center; } -static const fn::MultiFunction *get_multi_function(bNode &node) +static const fn::MultiFunction *get_multi_function(const bNode &node) { bool invert = node.custom2; const int mode = node.custom1; diff --git a/source/blender/nodes/shader/nodes/node_shader_vector_transform.cc b/source/blender/nodes/shader/nodes/node_shader_vector_transform.cc index de588f9005f..159bd238212 100644 --- a/source/blender/nodes/shader/nodes/node_shader_vector_transform.cc +++ b/source/blender/nodes/shader/nodes/node_shader_vector_transform.cc @@ -83,7 +83,7 @@ static const char *get_gpufn_name_from_to(short from, short to, bool is_directio } break; } - return NULL; + return nullptr; } static int gpu_shader_vect_transform(GPUMaterial *mat, diff --git a/source/blender/nodes/shader/nodes/node_shader_volume_info.cc b/source/blender/nodes/shader/nodes/node_shader_volume_info.cc index a202312f8d8..1f31e9c605f 100644 --- a/source/blender/nodes/shader/nodes/node_shader_volume_info.cc +++ b/source/blender/nodes/shader/nodes/node_shader_volume_info.cc @@ -25,9 +25,11 @@ static int node_shader_gpu_volume_info(GPUMaterial *mat, } if (out[1].hasoutput) { out[1].link = GPU_attribute(mat, CD_AUTO_FROM_NAME, "density"); + GPU_link(mat, "node_attribute_density", out[1].link, &out[1].link); } if (out[2].hasoutput) { out[2].link = GPU_attribute(mat, CD_AUTO_FROM_NAME, "flame"); + GPU_link(mat, "node_attribute_flame", out[2].link, &out[2].link); } if (out[3].hasoutput) { out[3].link = GPU_attribute(mat, CD_AUTO_FROM_NAME, "temperature"); diff --git a/source/blender/nodes/texture/CMakeLists.txt b/source/blender/nodes/texture/CMakeLists.txt index 2ccdf4c0bc9..77db71d4b1a 100644 --- a/source/blender/nodes/texture/CMakeLists.txt +++ b/source/blender/nodes/texture/CMakeLists.txt @@ -57,21 +57,6 @@ set(LIB bf_nodes ) -if(WITH_PYTHON) - list(APPEND INC - ../../python - ) - list(APPEND INC_SYS - ${PYTHON_INCLUDE_DIRS} - ) - list(APPEND LIB - ${PYTHON_LINKFLAGS} - ${PYTHON_LIBRARIES} - ) - add_definitions(-DWITH_PYTHON) -endif() - - blender_add_lib(bf_nodes_texture "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") # RNA_prototypes.h diff --git a/source/blender/nodes/texture/node_texture_tree.c b/source/blender/nodes/texture/node_texture_tree.c index ac105b5bcb9..81d0b0fbc84 100644 --- a/source/blender/nodes/texture/node_texture_tree.c +++ b/source/blender/nodes/texture/node_texture_tree.c @@ -18,6 +18,7 @@ #include "BLT_translation.h" #include "BKE_context.h" +#include "BKE_layer.h" #include "BKE_linestyle.h" #include "BKE_node.h" #include "BKE_paint.h" @@ -46,7 +47,8 @@ static void texture_get_from_context(const bContext *C, SpaceNode *snode = CTX_wm_space_node(C); Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); - Object *ob = OBACT(view_layer); + BKE_view_layer_synced_ensure(scene, view_layer); + Object *ob = BKE_view_layer_active_object_get(view_layer); Tex *tx = NULL; if (snode->texfrom == SNODE_TEX_BRUSH) { @@ -249,7 +251,7 @@ bNodeTreeExec *ntreeTexBeginExecTree(bNodeTree *ntree) exec = ntreeTexBeginExecTree_internal(&context, ntree, NODE_INSTANCE_KEY_BASE); - /* XXX this should not be necessary, but is still used for cmp/sha/tex nodes, + /* XXX this should not be necessary, but is still used for compositor/shading/texture nodes, * which only store the ntree pointer. Should be fixed at some point! */ ntree->execdata = exec; |