From 0d3a33e45eb3a69b665429cd24694d2d2ca0cbd4 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 16 Nov 2022 17:27:28 -0600 Subject: [PATCH] Geometry Nodes: Add "Exists" output to Named Attribute input node As described in T100004, add an output socket that returns true if the attribute accessed by the node was already present in that context. Initial patch by Edward (@edward88). Differential Revision: https://developer.blender.org/D16316 --- .../node_geometry_attribute_search.cc | 10 ++-- .../nodes/node_geo_input_named_attribute.cc | 51 ++++++++++++++++--- 2 files changed, 50 insertions(+), 11 deletions(-) diff --git a/source/blender/editors/space_node/node_geometry_attribute_search.cc b/source/blender/editors/space_node/node_geometry_attribute_search.cc index a1a8fd0dfdc..002faffd0fb 100644 --- a/source/blender/editors/space_node/node_geometry_attribute_search.cc +++ b/source/blender/editors/space_node/node_geometry_attribute_search.cc @@ -197,10 +197,14 @@ static void attribute_search_exec_fn(bContext *C, void *data_v, void *item_v) /* Relink all node links to the newly active output socket. */ bNodeSocket *output_socket = bke::node_find_enabled_output_socket(*node, "Attribute"); LISTBASE_FOREACH (bNodeLink *, link, &node_tree->links) { - if (link->fromnode == node) { - link->fromsock = output_socket; - BKE_ntree_update_tag_link_changed(node_tree); + if (link->fromnode != node) { + continue; } + if (!STREQ(link->fromsock->name, "Attribute")) { + continue; + } + link->fromsock = output_socket; + BKE_ntree_update_tag_link_changed(node_tree); } } BKE_ntree_update_tag_node_property(node_tree, node); 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 9d1f90ba0f3..90e7f58c07e 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 @@ -20,6 +20,8 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output(N_("Attribute"), "Attribute_Color").field_source(); b.add_output(N_("Attribute"), "Attribute_Bool").field_source(); b.add_output(N_("Attribute"), "Attribute_Int").field_source(); + + b.add_output(N_("Exists")).field_source(); } static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) @@ -57,20 +59,52 @@ static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) const NodeDeclaration &declaration = *params.node_type().fixed_declaration; search_link_ops_for_declarations(params, declaration.inputs()); + const bNodeType &node_type = params.node_type(); if (params.in_out() == SOCK_OUT) { const std::optional 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("GeometryNodeInputNamedAttribute"); + params.add_item(IFACE_("Attribute"), [node_type, type](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node(node_type); node_storage(node).data_type = *type; params.update_and_connect_available_socket(node, "Attribute"); }); + params.add_item(IFACE_("Exists"), [node_type](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node(node_type); + params.update_and_connect_available_socket(node, "Exists"); + }); } } } +class AttributeExistsFieldInput final : public bke::GeometryFieldInput { + private: + std::string name_; + + public: + AttributeExistsFieldInput(std::string name, const CPPType &type) + : GeometryFieldInput(type, name), name_(std::move(name)) + { + category_ = Category::Generated; + } + + static Field Create(std::string name) + { + const CPPType &type = CPPType::get(); + auto field_input = std::make_shared(std::move(name), type); + return Field(field_input); + } + + GVArray get_varray_for_context(const bke::GeometryFieldContext &context, + const IndexMask /*mask*/) const final + { + const bool exists = context.attributes()->contains(name_); + const int domain_size = context.attributes()->domain_size(context.domain()); + return VArray::ForSingle(exists, domain_size); + } +}; + static void node_geo_exec(GeoNodeExecParams params) { const NodeGeometryInputNamedAttribute &storage = node_storage(params.node()); @@ -92,24 +126,25 @@ static void node_geo_exec(GeoNodeExecParams params) switch (data_type) { case CD_PROP_FLOAT: - params.set_output("Attribute_Float", AttributeFieldInput::Create(std::move(name))); + params.set_output("Attribute_Float", AttributeFieldInput::Create(name)); break; case CD_PROP_FLOAT3: - params.set_output("Attribute_Vector", AttributeFieldInput::Create(std::move(name))); + params.set_output("Attribute_Vector", AttributeFieldInput::Create(name)); break; case CD_PROP_COLOR: - params.set_output("Attribute_Color", - AttributeFieldInput::Create(std::move(name))); + params.set_output("Attribute_Color", AttributeFieldInput::Create(name)); break; case CD_PROP_BOOL: - params.set_output("Attribute_Bool", AttributeFieldInput::Create(std::move(name))); + params.set_output("Attribute_Bool", AttributeFieldInput::Create(name)); break; case CD_PROP_INT32: - params.set_output("Attribute_Int", AttributeFieldInput::Create(std::move(name))); + params.set_output("Attribute_Int", AttributeFieldInput::Create(name)); break; default: break; } + + params.set_output("Exists", AttributeExistsFieldInput::Create(std::move(name))); } } // namespace blender::nodes::node_geo_input_named_attribute_cc