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
This commit is contained in:
Hans Goudey 2022-03-14 11:48:11 -05:00
parent a5578351c3
commit d4e46c13cc
Notes: blender-bot 2023-02-13 17:35:32 +01:00
Referenced by issue #91742, Named Attribute Nodes
16 changed files with 506 additions and 13 deletions

View File

@ -2262,6 +2262,7 @@ class USERPREF_PT_experimental_new_features(ExperimentalPanel, Panel):
({"property": "use_sculpt_tools_tilt"}, "T82877"),
({"property": "use_extended_asset_browser"}, ("project/view/130/", "Project Page")),
({"property": "use_override_templates"}, ("T73318", "Milestone 4")),
({"property": "use_named_attribute_nodes"}, ("T91742")),
),
)

View File

@ -201,6 +201,8 @@ def geometry_input_node_items(context):
yield NodeItem("ShaderNodeValue")
yield NodeItem("FunctionNodeInputVector")
yield NodeItemCustom(draw=lambda self, layout, context: layout.separator())
if named_attribute_poll(context):
yield NodeItem("GeometryNodeInputNamedAttribute")
yield NodeItem("GeometryNodeInputID")
yield NodeItem("GeometryNodeInputIndex")
yield NodeItem("GeometryNodeInputNormal")
@ -358,6 +360,10 @@ def geometry_nodes_legacy_poll(context):
return context.preferences.experimental.use_geometry_nodes_legacy
def named_attribute_poll(context):
return context.preferences.experimental.use_named_attribute_nodes
# All standard node categories currently used in nodes.
shader_node_categories = [
@ -683,6 +689,8 @@ geometry_node_categories = [
NodeItem("GeometryNodeAttributeDomainSize"),
NodeItem("GeometryNodeAttributeStatistic"),
NodeItem("GeometryNodeAttributeTransfer"),
NodeItem("GeometryNodeStoreNamedAttribute", poll=named_attribute_poll),
NodeItem("GeometryNodeRemoveAttribute", poll=named_attribute_poll),
]),
GeometryNodeCategory("GEO_COLOR", "Color", items=[
NodeItem("ShaderNodeMixRGB"),

View File

@ -1392,7 +1392,7 @@ struct TexResult;
#define GEO_NODE_LEGACY_ATTRIBUTE_COMBINE_XYZ 1027
#define GEO_NODE_LEGACY_ATTRIBUTE_SEPARATE_XYZ 1028
#define GEO_NODE_SUBDIVIDE_MESH 1029
#define GEO_NODE_ATTRIBUTE_REMOVE 1030
#define GEO_NODE_LEGACY_ATTRIBUTE_REMOVE 1030
#define GEO_NODE_LEGACY_ATTRIBUTE_CONVERT 1031
#define GEO_NODE_MESH_PRIMITIVE_CUBE 1032
#define GEO_NODE_MESH_PRIMITIVE_CIRCLE 1033
@ -1517,6 +1517,9 @@ struct TexResult;
#define GEO_NODE_MERGE_BY_DISTANCE 1153
#define GEO_NODE_DUPLICATE_ELEMENTS 1154
#define GEO_NODE_INPUT_MESH_FACE_IS_PLANAR 1155
#define GEO_NODE_STORE_NAMED_ATTRIBUTE 1156
#define GEO_NODE_INPUT_NAMED_ATTRIBUTE 1157
#define GEO_NODE_REMOVE_ATTRIBUTE 1158
/** \} */

View File

@ -4694,6 +4694,7 @@ static void registerGeometryNodes()
register_node_type_geo_legacy_attribute_proximity();
register_node_type_geo_legacy_attribute_randomize();
register_node_type_geo_legacy_attribute_remove();
register_node_type_geo_legacy_attribute_transfer();
register_node_type_geo_legacy_curve_endpoints();
register_node_type_geo_legacy_curve_reverse();
@ -4726,7 +4727,6 @@ static void registerGeometryNodes()
register_node_type_geo_attribute_map_range();
register_node_type_geo_attribute_math();
register_node_type_geo_attribute_mix();
register_node_type_geo_attribute_remove();
register_node_type_geo_attribute_separate_xyz();
register_node_type_geo_attribute_statistic();
register_node_type_geo_attribute_vector_math();
@ -4768,6 +4768,7 @@ static void registerGeometryNodes()
register_node_type_geo_flip_faces();
register_node_type_geo_geometry_to_instance();
register_node_type_geo_image_texture();
register_node_type_geo_input_named_attribute();
register_node_type_geo_input_curve_handles();
register_node_type_geo_input_curve_tilt();
register_node_type_geo_input_id();
@ -4821,6 +4822,7 @@ static void registerGeometryNodes()
register_node_type_geo_proximity();
register_node_type_geo_raycast();
register_node_type_geo_realize_instances();
register_node_type_geo_remove_attribute();
register_node_type_geo_rotate_instances();
register_node_type_geo_sample_texture();
register_node_type_geo_scale_elements();
@ -4838,6 +4840,7 @@ static void registerGeometryNodes()
register_node_type_geo_set_shade_smooth();
register_node_type_geo_set_spline_cyclic();
register_node_type_geo_set_spline_resolution();
register_node_type_geo_store_named_attribute();
register_node_type_geo_string_join();
register_node_type_geo_string_to_curves();
register_node_type_geo_subdivision_surface();

View File

@ -527,7 +527,7 @@ static void version_geometry_nodes_add_realize_instance_nodes(bNodeTree *ntree)
GEO_NODE_TRIM_CURVE,
GEO_NODE_REPLACE_MATERIAL,
GEO_NODE_SUBDIVIDE_MESH,
GEO_NODE_ATTRIBUTE_REMOVE,
GEO_NODE_LEGACY_ATTRIBUTE_REMOVE,
GEO_NODE_TRIANGULATE)) {
bNodeSocket *geometry_socket = node->inputs.first;
add_realize_instances_before_socket(ntree, node, geometry_socket);
@ -999,7 +999,7 @@ static bool geometry_node_is_293_legacy(const short node_type)
/* Maybe legacy: Might need special attribute handling, depending on design. */
case GEO_NODE_SWITCH:
case GEO_NODE_JOIN_GEOMETRY:
case GEO_NODE_ATTRIBUTE_REMOVE:
case GEO_NODE_LEGACY_ATTRIBUTE_REMOVE:
case GEO_NODE_OBJECT_INFO:
case GEO_NODE_COLLECTION_INFO:
return false;
@ -2637,5 +2637,14 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
*/
{
/* Keep this block, even when empty. */
/* Deprecate the attribute remove node. It was hidden and is replaced by a version without a
* multi-input socket. */
LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) {
if (ntree->type == NTREE_GEOMETRY) {
version_node_id(
ntree, GEO_NODE_LEGACY_ATTRIBUTE_REMOVE, "GeometryNodeLegacyAttributeRemove");
}
}
}
}

View File

@ -1592,6 +1592,18 @@ typedef struct NodeGeometryAttributeCapture {
int8_t domain;
} NodeGeometryAttributeCapture;
typedef struct NodeGeometryStoreNamedAttribute {
/* CustomDataType. */
int8_t data_type;
/* AttributeDomain. */
int8_t domain;
} NodeGeometryStoreNamedAttribute;
typedef struct NodeGeometryInputNamedAttribute {
/* CustomDataType. */
int8_t data_type;
} NodeGeometryInputNamedAttribute;
typedef struct NodeGeometryStringToCurves {
/* GeometryNodeStringToCurvesOverflowMode */
uint8_t overflow;

View File

@ -650,7 +650,8 @@ typedef struct UserDef_Experimental {
char use_sculpt_tools_tilt;
char use_extended_asset_browser;
char use_override_templates;
char _pad[2];
char use_named_attribute_nodes;
char _pad[1];
/** `makesdna` does not allow empty structs. */
} UserDef_Experimental;

View File

@ -11281,6 +11281,40 @@ static void def_geo_curve_fill(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
static void def_geo_store_named_attribute(StructRNA *srna)
{
PropertyRNA *prop;
RNA_def_struct_sdna_from(srna, "NodeGeometryStoreNamedAttribute", "storage");
prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, rna_enum_attribute_type_items);
RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeFill_type_itemf");
RNA_def_property_enum_default(prop, CD_PROP_FLOAT);
RNA_def_property_ui_text(prop, "Data Type", "Type of data stored in attribute");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update");
prop = RNA_def_property(srna, "domain", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, rna_enum_attribute_domain_items);
RNA_def_property_enum_default(prop, ATTR_DOMAIN_POINT);
RNA_def_property_ui_text(prop, "Domain", "Which domain to store the data in");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
static void def_geo_input_named_attribute(StructRNA *srna)
{
PropertyRNA *prop;
RNA_def_struct_sdna_from(srna, "NodeGeometryInputNamedAttribute", "storage");
prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, rna_enum_attribute_type_items);
RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeFill_type_itemf");
RNA_def_property_enum_default(prop, CD_PROP_FLOAT);
RNA_def_property_ui_text(prop, "Data Type", "The data type used to read the attribute values");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update");
}
static void def_geo_attribute_capture(StructRNA *srna)
{
PropertyRNA *prop;

View File

@ -6441,6 +6441,12 @@ static void rna_def_userdef_experimental(BlenderRNA *brna)
RNA_def_property_boolean_sdna(prop, NULL, "use_geometry_nodes_legacy", 1);
RNA_def_property_ui_text(
prop, "Geometry Nodes Legacy", "Enable legacy geometry nodes in the menu");
prop = RNA_def_property(srna, "use_named_attribute_nodes", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "use_named_attribute_nodes", 1);
RNA_def_property_ui_text(prop,
"Named Attribute Nodes",
"Enable named attribute nodes in the geometry nodes add menu");
}
static void rna_def_userdef_addon_collection(BlenderRNA *brna, PropertyRNA *cprop)

View File

@ -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);

View File

@ -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", "")

View File

@ -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

View File

@ -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);

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}