Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHans Goudey <h.goudey@me.com>2022-03-14 19:48:11 +0300
committerHans Goudey <h.goudey@me.com>2022-03-14 19:48:11 +0300
commitd4e46c13cc92e01489a031a6afe6bafb5af5ca18 (patch)
tree5c6294d006044d4100500f9e92bce8fea36c8841 /source/blender/nodes
parenta5578351c38e2b2bb45d940a2fc57354e5fe3a5e (diff)
Geometry Nodes: Add named attribute nodes behind experimental flag
This commit adds three nodes: - `Remove Attribute`: Removes an attribute with the given name - `Named Attribute`: A field input node - `Store Named Attribute`: Puts results of a field in a named attribute They are added behind a new experimental feature flag, because further development of attribute search and name dependency visualization will happen as separate steps. Ref T91742 Differential Revision: https://developer.blender.org/D12685
Diffstat (limited to 'source/blender/nodes')
-rw-r--r--source/blender/nodes/NOD_geometry.h5
-rw-r--r--source/blender/nodes/NOD_static_types.h5
-rw-r--r--source/blender/nodes/geometry/CMakeLists.txt5
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_remove.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc)11
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_named_attribute.cc131
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_remove_attribute.cc87
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc188
7 files changed, 424 insertions, 8 deletions
diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h
index 8aea54f8ac5..be21dd4b88f 100644
--- a/source/blender/nodes/NOD_geometry.h
+++ b/source/blender/nodes/NOD_geometry.h
@@ -49,7 +49,7 @@ void register_node_type_geo_attribute_fill(void);
void register_node_type_geo_attribute_map_range(void);
void register_node_type_geo_attribute_math(void);
void register_node_type_geo_attribute_mix(void);
-void register_node_type_geo_attribute_remove(void);
+void register_node_type_geo_legacy_attribute_remove(void);
void register_node_type_geo_attribute_separate_xyz(void);
void register_node_type_geo_attribute_statistic(void);
void register_node_type_geo_attribute_vector_math(void);
@@ -91,6 +91,7 @@ void register_node_type_geo_field_at_index(void);
void register_node_type_geo_flip_faces(void);
void register_node_type_geo_geometry_to_instance(void);
void register_node_type_geo_image_texture(void);
+void register_node_type_geo_input_named_attribute(void);
void register_node_type_geo_input_curve_handles(void);
void register_node_type_geo_input_curve_tilt(void);
void register_node_type_geo_input_id(void);
@@ -144,6 +145,7 @@ void register_node_type_geo_points_to_volume(void);
void register_node_type_geo_proximity(void);
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_texture(void);
void register_node_type_geo_scale_elements(void);
@@ -162,6 +164,7 @@ void register_node_type_geo_set_position(void);
void register_node_type_geo_set_shade_smooth(void);
void register_node_type_geo_set_spline_cyclic(void);
void register_node_type_geo_set_spline_resolution(void);
+void register_node_type_geo_store_named_attribute(void);
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);
diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h
index 700c3a83b70..97a6b8a6e63 100644
--- a/source/blender/nodes/NOD_static_types.h
+++ b/source/blender/nodes/NOD_static_types.h
@@ -286,6 +286,7 @@ DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_MATH, def_geo_attribute_math, "L
DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_MIX, def_geo_attribute_mix, "LEGACY_ATTRIBUTE_MIX", LegacyAttributeMix, "Attribute Mix", "")
DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_PROXIMITY, def_geo_legacy_attribute_proximity, "LEGACY_ATTRIBUTE_PROXIMITY", LegacyAttributeProximity, "Attribute Proximity", "")
DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_RANDOMIZE, def_geo_attribute_randomize, "LEGACY_ATTRIBUTE_RANDOMIZE", LegacyAttributeRandomize, "Attribute Randomize", "")
+DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_REMOVE, 0, "LEGACY_ATTRIBUTE_REMOVE", LegacyAttributeRemove, "Attribute Remove", "")
DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_SAMPLE_TEXTURE, 0, "LEGACY_ATTRIBUTE_SAMPLE_TEXTURE", LegacyAttributeSampleTexture, "Attribute Sample Texture", "")
DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_SEPARATE_XYZ, def_geo_attribute_separate_xyz, "LEGACY_ATTRIBUTE_SEPARATE_XYZ", LegacyAttributeSeparateXYZ, "Attribute Separate XYZ", "")
DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_TRANSFER, def_geo_attribute_transfer, "LEGACY_ATTRIBUTE_TRANSFER", LegacyAttributeTransfer, "Attribute Transfer", "")
@@ -315,7 +316,6 @@ DefNode(GeometryNode, GEO_NODE_LEGACY_SUBDIVISION_SURFACE, def_geo_subdivision_s
DefNode(GeometryNode, GEO_NODE_LEGACY_VOLUME_TO_MESH, def_geo_volume_to_mesh, "LEGACY_VOLUME_TO_MESH", LegacyVolumeToMesh, "Volume to Mesh", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_DOMAIN_SIZE, def_geo_attribute_domain_size, "ATTRIBUTE_DOMAIN_SIZE", AttributeDomainSize, "Domain Size", "")
-DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_REMOVE, 0, "ATTRIBUTE_REMOVE", AttributeRemove, "Attribute Remove", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_STATISTIC, def_geo_attribute_statistic, "ATTRIBUTE_STATISTIC", AttributeStatistic, "Attribute Statistic", "")
DefNode(GeometryNode, GEO_NODE_BOUNDING_BOX, 0, "BOUNDING_BOX", BoundBox, "Bounding Box", "")
DefNode(GeometryNode, GEO_NODE_CAPTURE_ATTRIBUTE, def_geo_attribute_capture, "CAPTURE_ATTRIBUTE", CaptureAttribute, "Capture Attribute", "")
@@ -349,6 +349,7 @@ DefNode(GeometryNode, GEO_NODE_FILLET_CURVE, def_geo_curve_fillet, "FILLET_CURVE
DefNode(GeometryNode, GEO_NODE_FLIP_FACES, 0, "FLIP_FACES", FlipFaces, "Flip Faces", "")
DefNode(GeometryNode, GEO_NODE_GEOMETRY_TO_INSTANCE, 0, "GEOMETRY_TO_INSTANCE", GeometryToInstance, "Geometry to Instance", "")
DefNode(GeometryNode, GEO_NODE_IMAGE_TEXTURE, def_geo_image_texture, "IMAGE_TEXTURE", ImageTexture, "Image Texture", "")
+DefNode(GeometryNode, GEO_NODE_INPUT_NAMED_ATTRIBUTE, def_geo_input_named_attribute, "INPUT_ATTRIBUTE", InputNamedAttribute, "Named Attribute", "")
DefNode(GeometryNode, GEO_NODE_INPUT_CURVE_HANDLES, 0, "INPUT_CURVE_HANDLES", InputCurveHandlePositions, "Curve Handle Positions", "")
DefNode(GeometryNode, GEO_NODE_INPUT_CURVE_TILT, 0, "INPUT_CURVE_TILT", InputCurveTilt, "Curve Tilt", "")
DefNode(GeometryNode, GEO_NODE_INPUT_ID, 0, "INPUT_ID", InputID, "ID", "")
@@ -394,6 +395,7 @@ DefNode(GeometryNode, GEO_NODE_POINTS_TO_VERTICES, 0, "POINTS_TO_VERTICES", Poin
DefNode(GeometryNode, GEO_NODE_POINTS_TO_VOLUME, def_geo_points_to_volume, "POINTS_TO_VOLUME", PointsToVolume, "Points to Volume", "")
DefNode(GeometryNode, GEO_NODE_PROXIMITY, def_geo_proximity, "PROXIMITY", Proximity, "Geometry Proximity", "")
DefNode(GeometryNode, GEO_NODE_RAYCAST, def_geo_raycast, "RAYCAST", Raycast, "Raycast", "")
+DefNode(GeometryNode, GEO_NODE_REMOVE_ATTRIBUTE, 0, "REMOVE_ATTRIBUTE", RemoveAttribute, "Remove Attribute", "")
DefNode(GeometryNode, GEO_NODE_REALIZE_INSTANCES, def_geo_realize_instances, "REALIZE_INSTANCES", RealizeInstances, "Realize Instances", "")
DefNode(GeometryNode, GEO_NODE_REPLACE_MATERIAL, 0, "REPLACE_MATERIAL", ReplaceMaterial, "Replace Material", "")
DefNode(GeometryNode, GEO_NODE_RESAMPLE_CURVE, def_geo_curve_resample, "RESAMPLE_CURVE", ResampleCurve, "Resample Curve", "")
@@ -416,6 +418,7 @@ DefNode(GeometryNode, GEO_NODE_SET_SHADE_SMOOTH, 0, "SET_SHADE_SMOOTH", SetShade
DefNode(GeometryNode, GEO_NODE_SET_SPLINE_CYCLIC, 0, "SET_SPLINE_CYCLIC", SetSplineCyclic, "Set Spline Cyclic", "")
DefNode(GeometryNode, GEO_NODE_SET_SPLINE_RESOLUTION, 0, "SET_SPLINE_RESOLUTION", SetSplineResolution, "Set Spline Resolution", "")
DefNode(GeometryNode, GEO_NODE_SPLIT_EDGES, 0, "SPLIT_EDGES", SplitEdges, "Split Edges", "")
+DefNode(GeometryNode, GEO_NODE_STORE_NAMED_ATTRIBUTE, def_geo_store_named_attribute, "STORE_NAMED_ATTRIBUTE", StoreNamedAttribute, "Store Named Attribute", "")
DefNode(GeometryNode, GEO_NODE_STRING_JOIN, 0, "STRING_JOIN", StringJoin, "Join Strings", "")
DefNode(GeometryNode, GEO_NODE_STRING_TO_CURVES, def_geo_string_to_curves, "STRING_TO_CURVES", StringToCurves, "String to Curves", "")
DefNode(GeometryNode, GEO_NODE_SUBDIVIDE_CURVE, 0, "SUBDIVIDE_CURVE", SubdivideCurve, "Subdivide Curve", "")
diff --git a/source/blender/nodes/geometry/CMakeLists.txt b/source/blender/nodes/geometry/CMakeLists.txt
index 4d39240fed4..0e99d8ad646 100644
--- a/source/blender/nodes/geometry/CMakeLists.txt
+++ b/source/blender/nodes/geometry/CMakeLists.txt
@@ -38,6 +38,7 @@ set(SRC
nodes/legacy/node_geo_legacy_attribute_mix.cc
nodes/legacy/node_geo_legacy_attribute_proximity.cc
nodes/legacy/node_geo_legacy_attribute_randomize.cc
+ nodes/legacy/node_geo_legacy_attribute_remove.cc
nodes/legacy/node_geo_legacy_attribute_sample_texture.cc
nodes/legacy/node_geo_legacy_attribute_separate_xyz.cc
nodes/legacy/node_geo_legacy_attribute_transfer.cc
@@ -69,7 +70,6 @@ set(SRC
nodes/node_geo_accumulate_field.cc
nodes/node_geo_attribute_capture.cc
nodes/node_geo_attribute_domain_size.cc
- nodes/node_geo_attribute_remove.cc
nodes/node_geo_attribute_statistic.cc
nodes/node_geo_boolean.cc
nodes/node_geo_bounding_box.cc
@@ -123,6 +123,7 @@ set(SRC
nodes/node_geo_input_mesh_face_neighbors.cc
nodes/node_geo_input_mesh_island.cc
nodes/node_geo_input_mesh_vertex_neighbors.cc
+ nodes/node_geo_input_named_attribute.cc
nodes/node_geo_input_normal.cc
nodes/node_geo_input_position.cc
nodes/node_geo_input_radius.cc
@@ -156,6 +157,7 @@ set(SRC
nodes/node_geo_proximity.cc
nodes/node_geo_raycast.cc
nodes/node_geo_realize_instances.cc
+ nodes/node_geo_remove_attribute.cc
nodes/node_geo_rotate_instances.cc
nodes/node_geo_scale_elements.cc
nodes/node_geo_scale_instances.cc
@@ -172,6 +174,7 @@ set(SRC
nodes/node_geo_set_shade_smooth.cc
nodes/node_geo_set_spline_cyclic.cc
nodes/node_geo_set_spline_resolution.cc
+ nodes/node_geo_store_named_attribute.cc
nodes/node_geo_string_join.cc
nodes/node_geo_string_to_curves.cc
nodes/node_geo_subdivision_surface.cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_remove.cc
index cb7132d5ea2..cc7118fd305 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_remove.cc
@@ -2,7 +2,7 @@
#include "node_geometry_util.hh"
-namespace blender::nodes::node_geo_attribute_remove_cc {
+namespace blender::nodes::node_geo_legacy_attribute_remove_cc {
static void node_declare(NodeDeclarationBuilder &b)
{
@@ -45,15 +45,16 @@ static void node_geo_exec(GeoNodeExecParams params)
params.set_output("Geometry", geometry_set);
}
-} // namespace blender::nodes::node_geo_attribute_remove_cc
+} // namespace blender::nodes::node_geo_legacy_attribute_remove_cc
-void register_node_type_geo_attribute_remove()
+void register_node_type_geo_legacy_attribute_remove()
{
- namespace file_ns = blender::nodes::node_geo_attribute_remove_cc;
+ namespace file_ns = blender::nodes::node_geo_legacy_attribute_remove_cc;
static bNodeType ntype;
- geo_node_type_base(&ntype, GEO_NODE_ATTRIBUTE_REMOVE, "Attribute Remove", NODE_CLASS_ATTRIBUTE);
+ geo_node_type_base(
+ &ntype, GEO_NODE_LEGACY_ATTRIBUTE_REMOVE, "Attribute Remove", NODE_CLASS_ATTRIBUTE);
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_input_named_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_input_named_attribute.cc
new file mode 100644
index 00000000000..f6e2be9119c
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_named_attribute.cc
@@ -0,0 +1,131 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "NOD_socket_search_link.hh"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_input_named_attribute_cc {
+
+NODE_STORAGE_FUNCS(NodeGeometryInputNamedAttribute)
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::String>(N_("Name")).is_attribute_name();
+
+ b.add_output<decl::Vector>(N_("Attribute"), "Attribute_Vector").field_source();
+ b.add_output<decl::Float>(N_("Attribute"), "Attribute_Float").field_source();
+ b.add_output<decl::Color>(N_("Attribute"), "Attribute_Color").field_source();
+ b.add_output<decl::Bool>(N_("Attribute"), "Attribute_Bool").field_source();
+ b.add_output<decl::Int>(N_("Attribute"), "Attribute_Int").field_source();
+}
+
+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)
+{
+ NodeGeometryInputNamedAttribute *data = MEM_cnew<NodeGeometryInputNamedAttribute>(__func__);
+ data->data_type = CD_PROP_FLOAT;
+ node->storage = data;
+}
+
+static void node_update(bNodeTree *ntree, bNode *node)
+{
+ const NodeGeometryInputNamedAttribute &storage = node_storage(*node);
+ const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type);
+
+ bNodeSocket *socket_vector = (bNodeSocket *)node->outputs.first;
+ bNodeSocket *socket_float = socket_vector->next;
+ bNodeSocket *socket_color4f = socket_float->next;
+ bNodeSocket *socket_boolean = socket_color4f->next;
+ bNodeSocket *socket_int32 = socket_boolean->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);
+}
+
+static void node_gather_link_searches(GatherLinkSearchOpParams &params)
+{
+ if (U.experimental.use_named_attribute_nodes == 0) {
+ return;
+ }
+ const NodeDeclaration &declaration = *params.node_type().fixed_declaration;
+ search_link_ops_for_declarations(params, declaration.inputs());
+
+ if (params.in_out() == SOCK_OUT) {
+ const std::optional<CustomDataType> type = node_data_type_to_custom_data_type(
+ static_cast<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 &params) {
+ bNode &node = params.add_node("GeometryNodeInputNamedAttribute");
+ node_storage(node).data_type = *type;
+ params.update_and_connect_available_socket(node, "Attribute");
+ });
+ }
+ }
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ const NodeGeometryInputNamedAttribute &storage = node_storage(params.node());
+ const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type);
+
+ const std::string name = params.extract_input<std::string>("Name");
+
+ if (!U.experimental.use_named_attribute_nodes) {
+ params.set_default_remaining_outputs();
+ return;
+ }
+
+ switch (data_type) {
+ case CD_PROP_FLOAT:
+ params.set_output("Attribute_Float", AttributeFieldInput::Create<float>(std::move(name)));
+ break;
+ case CD_PROP_FLOAT3:
+ params.set_output("Attribute_Vector", AttributeFieldInput::Create<float3>(std::move(name)));
+ break;
+ case CD_PROP_COLOR:
+ params.set_output("Attribute_Color",
+ AttributeFieldInput::Create<ColorGeometry4f>(std::move(name)));
+ break;
+ case CD_PROP_BOOL:
+ params.set_output("Attribute_Bool", AttributeFieldInput::Create<bool>(std::move(name)));
+ break;
+ case CD_PROP_INT32:
+ params.set_output("Attribute_Int", AttributeFieldInput::Create<int>(std::move(name)));
+ break;
+ default:
+ break;
+ }
+}
+
+} // namespace blender::nodes::node_geo_input_named_attribute_cc
+
+void register_node_type_geo_input_named_attribute()
+{
+ namespace file_ns = blender::nodes::node_geo_input_named_attribute_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_INPUT_NAMED_ATTRIBUTE, "Named Attribute", NODE_CLASS_INPUT);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.declare = file_ns::node_declare;
+ ntype.draw_buttons = file_ns::node_layout;
+ ntype.gather_link_search_ops = file_ns::node_gather_link_searches;
+ ntype.updatefunc = file_ns::node_update;
+ node_type_init(&ntype, file_ns::node_init);
+ node_type_storage(&ntype,
+ "NodeGeometryInputNamedAttribute",
+ node_free_standard_storage,
+ node_copy_standard_storage);
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_remove_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_remove_attribute.cc
new file mode 100644
index 00000000000..202241affeb
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_remove_attribute.cc
@@ -0,0 +1,87 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "node_geometry_util.hh"
+
+#include "NOD_socket_search_link.hh"
+
+namespace blender::nodes::node_geo_remove_attribute_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::String>(N_("Name")).is_attribute_name();
+ b.add_output<decl::Geometry>(N_("Geometry"));
+}
+
+static void node_gather_link_searches(GatherLinkSearchOpParams &params)
+{
+ if (U.experimental.use_named_attribute_nodes == 0) {
+ return;
+ }
+ const NodeDeclaration &declaration = *params.node_type().fixed_declaration;
+ search_link_ops_for_declarations(params, declaration.inputs());
+ search_link_ops_for_declarations(params, declaration.outputs());
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ const std::string name = params.extract_input<std::string>("Name");
+ if (name.empty() || !U.experimental.use_named_attribute_nodes) {
+ params.set_output("Geometry", std::move(geometry_set));
+ return;
+ }
+
+ std::atomic<bool> attribute_exists = false;
+ std::atomic<bool> cannot_delete = false;
+
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ for (const GeometryComponentType type : {GEO_COMPONENT_TYPE_MESH,
+ GEO_COMPONENT_TYPE_POINT_CLOUD,
+ GEO_COMPONENT_TYPE_CURVE,
+ GEO_COMPONENT_TYPE_INSTANCES}) {
+ if (geometry_set.has(type)) {
+ /* First check if the attribute exists before getting write access,
+ * to avoid potentially expensive unnecessary copies. */
+ const GeometryComponent &read_only_component = *geometry_set.get_component_for_read(type);
+ if (read_only_component.attribute_exists(name)) {
+ attribute_exists = true;
+ }
+ else {
+ continue;
+ }
+
+ GeometryComponent &component = geometry_set.get_component_for_write(type);
+ if (!component.attribute_try_delete(name)) {
+ cannot_delete = true;
+ }
+ }
+ }
+ });
+
+ if (!attribute_exists) {
+ params.error_message_add(NodeWarningType::Info,
+ TIP_("Attribute does not exist: \"") + name + "\"");
+ }
+ if (cannot_delete) {
+ params.error_message_add(NodeWarningType::Info,
+ TIP_("Cannot delete attribute with name \"") + name + "\"");
+ }
+
+ params.set_output("Geometry", std::move(geometry_set));
+}
+
+} // namespace blender::nodes::node_geo_remove_attribute_cc
+
+void register_node_type_geo_remove_attribute()
+{
+ namespace file_ns = blender::nodes::node_geo_remove_attribute_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_REMOVE_ATTRIBUTE, "Remove Attribute", NODE_CLASS_ATTRIBUTE);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.gather_link_search_ops = file_ns::node_gather_link_searches;
+ nodeRegisterType(&ntype);
+}
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
new file mode 100644
index 00000000000..5b0816fa3e3
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc
@@ -0,0 +1,188 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "NOD_socket_search_link.hh"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_store_named_attribute_cc {
+
+NODE_STORAGE_FUNCS(NodeGeometryStoreNamedAttribute)
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::String>(N_("Name")).is_attribute_name();
+ b.add_input<decl::Vector>(N_("Value"), "Value_Vector").supports_field();
+ b.add_input<decl::Float>(N_("Value"), "Value_Float").supports_field();
+ b.add_input<decl::Color>(N_("Value"), "Value_Color").supports_field();
+ b.add_input<decl::Bool>(N_("Value"), "Value_Bool").supports_field();
+ b.add_input<decl::Int>(N_("Value"), "Value_Int").supports_field();
+
+ b.add_output<decl::Geometry>(N_("Geometry"));
+}
+
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
+ uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE);
+ uiItemR(layout, ptr, "domain", 0, "", ICON_NONE);
+}
+
+static void node_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometryStoreNamedAttribute *data = MEM_cnew<NodeGeometryStoreNamedAttribute>(__func__);
+ data->data_type = CD_PROP_FLOAT;
+ data->domain = ATTR_DOMAIN_POINT;
+ node->storage = data;
+}
+
+static void node_update(bNodeTree *ntree, bNode *node)
+{
+ const NodeGeometryStoreNamedAttribute &storage = node_storage(*node);
+ const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type);
+
+ bNodeSocket *socket_geometry = (bNodeSocket *)node->inputs.first;
+ bNodeSocket *socket_name = socket_geometry->next;
+ bNodeSocket *socket_vector = socket_name->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;
+
+ 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);
+}
+
+static void node_gather_link_searches(GatherLinkSearchOpParams &params)
+{
+ if (U.experimental.use_named_attribute_nodes == 0) {
+ return;
+ }
+ const NodeDeclaration &declaration = *params.node_type().fixed_declaration;
+ search_link_ops_for_declarations(params, declaration.inputs().take_front(2));
+
+ if (params.in_out() == SOCK_OUT) {
+ const std::optional<CustomDataType> type = node_data_type_to_custom_data_type(
+ static_cast<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 &params) {
+ bNode &node = params.add_node("GeometryNodeStoreNamedAttribute");
+ node_storage(node).data_type = *type;
+ params.update_and_connect_available_socket(node, "Value");
+ });
+ }
+ }
+}
+
+static void try_capture_field_on_geometry(GeometryComponent &component,
+ const StringRef name,
+ const AttributeDomain domain,
+ const GField &field)
+{
+ GeometryComponentFieldContext field_context{component, domain};
+ const int domain_size = component.attribute_domain_size(domain);
+ const IndexMask mask{IndexMask(domain_size)};
+
+ const CustomDataType data_type = bke::cpp_type_to_custom_data_type(field.cpp_type());
+
+ /* Don't use #add_with_destination because the field might depend on an attribute
+ * with that name, and changing it as part of evaluation might affect the result. */
+ fn::FieldEvaluator evaluator{field_context, &mask};
+ evaluator.add(field);
+ evaluator.evaluate();
+ const GVArray &result = evaluator.get_evaluated(0);
+ OutputAttribute attribute = component.attribute_try_get_for_output_only(name, domain, data_type);
+ if (attribute) {
+ result.materialize(attribute.as_span().data());
+ attribute.save();
+ }
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ std::string name = params.extract_input<std::string>("Name");
+
+ if (!U.experimental.use_named_attribute_nodes) {
+ params.set_output("Geometry", std::move(geometry_set));
+ return;
+ }
+
+ const NodeGeometryStoreNamedAttribute &storage = node_storage(params.node());
+ const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type);
+ const AttributeDomain domain = static_cast<AttributeDomain>(storage.domain);
+
+ GField field;
+ switch (data_type) {
+ case CD_PROP_FLOAT:
+ field = params.get_input<Field<float>>("Value_Float");
+ break;
+ case CD_PROP_FLOAT3:
+ field = params.get_input<Field<float3>>("Value_Vector");
+ break;
+ case CD_PROP_COLOR:
+ field = params.get_input<Field<ColorGeometry4f>>("Value_Color");
+ break;
+ case CD_PROP_BOOL:
+ field = params.get_input<Field<bool>>("Value_Bool");
+ break;
+ case CD_PROP_INT32:
+ field = params.get_input<Field<int>>("Value_Int");
+ break;
+ default:
+ break;
+ }
+
+ /* Run on the instances component separately to only affect the top level of instances. */
+ if (domain == ATTR_DOMAIN_INSTANCE) {
+ if (geometry_set.has_instances()) {
+ GeometryComponent &component = geometry_set.get_component_for_write(
+ GEO_COMPONENT_TYPE_INSTANCES);
+ try_capture_field_on_geometry(component, name, domain, field);
+ }
+ }
+ else {
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ for (const GeometryComponentType type :
+ {GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE}) {
+ if (geometry_set.has(type)) {
+ GeometryComponent &component = geometry_set.get_component_for_write(type);
+ try_capture_field_on_geometry(component, name, domain, field);
+ }
+ }
+ });
+ }
+
+ params.set_output("Geometry", std::move(geometry_set));
+}
+
+} // namespace blender::nodes::node_geo_store_named_attribute_cc
+
+void register_node_type_geo_store_named_attribute()
+{
+ namespace file_ns = blender::nodes::node_geo_store_named_attribute_cc;
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_STORE_NAMED_ATTRIBUTE, "Store Named Attribute", NODE_CLASS_ATTRIBUTE);
+ node_type_storage(&ntype,
+ "NodeGeometryStoreNamedAttribute",
+ node_free_standard_storage,
+ node_copy_standard_storage);
+ node_type_size(&ntype, 140, 100, 700);
+ node_type_init(&ntype, file_ns::node_init);
+ ntype.updatefunc = file_ns::node_update;
+ ntype.declare = file_ns::node_declare;
+ ntype.gather_link_search_ops = file_ns::node_gather_link_searches;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.draw_buttons = file_ns::node_layout;
+ nodeRegisterType(&ntype);
+}