mirror of
https://github.com/blender/blender
synced 2026-09-29 04:37:17 +03:00
Geometry Nodes: Get Attribute Names node
This PR adds the `Get Attribute Names` node to retrieve attribute names as a list of strings. The attributes can be filtered by domain and data type. Filters for both can be disabled. Co-authored-by: Jacques Lucke <jacques@blender.org> Pull Request: https://projects.blender.org/blender/blender/pulls/134760
This commit is contained in:
parent
14d549b16e
commit
eae8e8569d
10 changed files with 222 additions and 0 deletions
|
|
@ -16,6 +16,7 @@ class NODE_MT_gn_attribute_base(node_add_menu.NodeMenu):
|
|||
layout = self.layout
|
||||
self.node_operator(layout, "GeometryNodeAttributeStatistic")
|
||||
self.node_operator(layout, "GeometryNodeAttributeDomainSize")
|
||||
self.node_operator(layout, "GeometryNodeGetAttributeNames")
|
||||
layout.separator()
|
||||
self.node_operator(layout, "GeometryNodeBlurAttribute")
|
||||
self.node_operator(layout, "GeometryNodeCaptureAttribute")
|
||||
|
|
|
|||
|
|
@ -226,6 +226,7 @@ DEF_ENUM(rna_enum_context_mode_items)
|
|||
DEF_ENUM(rna_enum_preference_section_items)
|
||||
|
||||
DEF_ENUM(rna_enum_attribute_type_items)
|
||||
DEF_ENUM(rna_enum_attrtype_items)
|
||||
DEF_ENUM(rna_enum_attr_storage_type_items)
|
||||
DEF_ENUM(rna_enum_color_attribute_type_items)
|
||||
DEF_ENUM(rna_enum_attribute_type_with_auto_items)
|
||||
|
|
|
|||
|
|
@ -51,6 +51,52 @@ const EnumPropertyItem rna_enum_attribute_type_items[] = {
|
|||
{0, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
|
||||
const EnumPropertyItem rna_enum_attrtype_items[] = {
|
||||
{int(bke::AttrType::Float), "FLOAT", 0, "Float", "Floating-point value"},
|
||||
{int(bke::AttrType::Int32), "INT", 0, "Integer", "32-bit integer"},
|
||||
{int(bke::AttrType::Bool), "BOOLEAN", 0, "Boolean", "True or false"},
|
||||
{int(bke::AttrType::Float3),
|
||||
"FLOAT_VECTOR",
|
||||
0,
|
||||
"Vector",
|
||||
"3D vector with floating-point values"},
|
||||
{int(bke::AttrType::ColorFloat),
|
||||
"FLOAT_COLOR",
|
||||
0,
|
||||
"Color",
|
||||
"RGBA color with 32-bit floating-point values"},
|
||||
{int(bke::AttrType::Quaternion),
|
||||
"QUATERNION",
|
||||
0,
|
||||
"Quaternion",
|
||||
"Floating point quaternion rotation"},
|
||||
{int(bke::AttrType::Float4x4), "FLOAT4X4", 0, "4x4 Matrix", "Floating point matrix"},
|
||||
{int(bke::AttrType::String), "STRING", 0, "String", "Text string"},
|
||||
{int(bke::AttrType::Int8),
|
||||
"INT8",
|
||||
0,
|
||||
"8-Bit Integer",
|
||||
"Smaller integer with a range from -128 to 127"},
|
||||
{int(bke::AttrType::Int16_2D),
|
||||
"INT16_2D",
|
||||
0,
|
||||
"2D 16-Bit Integer Vector",
|
||||
"16-bit signed integer vector"},
|
||||
{int(bke::AttrType::Int32_2D),
|
||||
"INT32_2D",
|
||||
0,
|
||||
"2D Integer Vector",
|
||||
"32-bit signed integer vector"},
|
||||
{int(bke::AttrType::Float2), "FLOAT2", 0, "2D Vector", "2D vector with floating-point values"},
|
||||
{int(bke::AttrType::Float4), "FLOAT4", 0, "4D Vector", "4D vector with floating-point values"},
|
||||
{int(bke::AttrType::ColorByte),
|
||||
"BYTE_COLOR",
|
||||
0,
|
||||
"Byte Color",
|
||||
"RGBA color with 8-bit positive integer values"},
|
||||
{0, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
|
||||
const EnumPropertyItem rna_enum_color_attribute_type_items[] = {
|
||||
{CD_PROP_COLOR, "FLOAT_COLOR", 0, "Color", "RGBA color 32-bit floating-point values"},
|
||||
{CD_PROP_BYTE_COLOR,
|
||||
|
|
|
|||
|
|
@ -10524,6 +10524,7 @@ static void rna_def_nodes(BlenderRNA *brna)
|
|||
define("GeometryNode", "GeometryNodeForeachGeometryElementInput", def_geo_foreach_geometry_element_input);
|
||||
define("GeometryNode", "GeometryNodeForeachGeometryElementOutput", def_geo_foreach_geometry_element_output);
|
||||
define("GeometryNode", "GeometryNodeGeometryToInstance");
|
||||
define("GeometryNode", "GeometryNodeGetAttributeNames");
|
||||
define("GeometryNode", "GeometryNodeGetGeometryBundle");
|
||||
define("GeometryNode", "GeometryNodeGetNamedGrid");
|
||||
define("GeometryNode", "GeometryNodeGizmoDial");
|
||||
|
|
|
|||
|
|
@ -468,6 +468,17 @@ class BaseSocketDeclarationBuilder {
|
|||
BaseSocketDeclarationBuilder &usage_by_menu(const UString menu_input_identifier,
|
||||
const Array<int> menu_values);
|
||||
|
||||
/**
|
||||
* Utility method for the case when this socket is only used when the given boolean input matches
|
||||
* the specified value.
|
||||
*/
|
||||
BaseSocketDeclarationBuilder &usage_by_bool(UString bool_input_identifier, bool value);
|
||||
|
||||
/**
|
||||
* The socket is only used if the parent panel toggle is checked.
|
||||
*/
|
||||
BaseSocketDeclarationBuilder &usage_by_panel_toggle();
|
||||
|
||||
/**
|
||||
* Puts this socket on the same row as the previous socket. This only works when one of them is
|
||||
* an input and the other is an output.
|
||||
|
|
|
|||
|
|
@ -86,6 +86,7 @@ class SocketUsageParams {
|
|||
* Utility for the case when the socket depends on a specific menu input to have a certain value.
|
||||
*/
|
||||
bool menu_input_may_be(UString identifier, int enum_value) const;
|
||||
bool bool_input_may_be(UString identifier, bool bool_value) const;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -97,6 +97,7 @@ set(SRC
|
|||
nodes/node_geo_flip_faces.cc
|
||||
nodes/node_geo_foreach_geometry_element.cc
|
||||
nodes/node_geo_geometry_to_instance.cc
|
||||
nodes/node_geo_get_attribute_names.cc
|
||||
nodes/node_geo_get_bundle_item.cc
|
||||
nodes/node_geo_get_geometry_bundle.cc
|
||||
nodes/node_geo_get_named_grid.cc
|
||||
|
|
|
|||
|
|
@ -0,0 +1,99 @@
|
|||
/* SPDX-FileCopyrightText: 2026 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "BKE_attribute.hh"
|
||||
|
||||
#include "BLI_sort.hh"
|
||||
|
||||
#include "RNA_enum_types.hh"
|
||||
|
||||
#include "node_geometry_util.hh"
|
||||
|
||||
namespace blender::nodes::node_geo_get_attribute_names_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.use_custom_socket_order();
|
||||
b.allow_any_socket_order();
|
||||
|
||||
b.add_output<decl::String>("Names"_ustr).structure_type(StructureType::List);
|
||||
|
||||
b.add_input<decl::Geometry>("Geometry"_ustr);
|
||||
|
||||
{
|
||||
auto &p = b.add_panel("Filter Data Type"_ustr);
|
||||
p.add_input<decl::Bool>("Filter Data Type"_ustr).panel_toggle();
|
||||
p.add_input<decl::Menu>("Data Type"_ustr)
|
||||
.static_items(rna_enum_attrtype_items)
|
||||
.default_value(bke::AttrType::Float)
|
||||
.optional_label()
|
||||
.usage_by_panel_toggle();
|
||||
}
|
||||
{
|
||||
auto &p = b.add_panel("Filter Domain"_ustr);
|
||||
p.add_input<decl::Bool>("Filter Domain"_ustr).panel_toggle();
|
||||
p.add_input<decl::Menu>("Domain"_ustr)
|
||||
.static_items(rna_enum_attribute_domain_items)
|
||||
.default_value(AttrDomain::Point)
|
||||
.optional_label()
|
||||
.usage_by_panel_toggle();
|
||||
}
|
||||
}
|
||||
|
||||
static void node_geo_exec(GeoNodeExecParams params)
|
||||
{
|
||||
const GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"_ustr);
|
||||
const bool filter_data_type = params.extract_input<bool>("Filter Data Type"_ustr);
|
||||
const bool filter_domain = params.extract_input<bool>("Filter Domain"_ustr);
|
||||
|
||||
const bke::AttrType data_type = params.extract_input<bke::AttrType>("Data Type"_ustr);
|
||||
const AttrDomain domain = params.extract_input<AttrDomain>("Domain"_ustr);
|
||||
|
||||
VectorSet<std::string> names_set;
|
||||
|
||||
for (const GeometryComponent *component : geometry_set.get_components()) {
|
||||
const std::optional<AttributeAccessor> attributes = component->attributes();
|
||||
if (!attributes) {
|
||||
continue;
|
||||
}
|
||||
attributes->foreach_attribute([&](const AttributeIter &iter) {
|
||||
if (filter_data_type) {
|
||||
if (iter.data_type != data_type) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (filter_domain) {
|
||||
if (iter.domain != domain) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (iter.name.startswith(".")) {
|
||||
return;
|
||||
}
|
||||
names_set.add(iter.name);
|
||||
});
|
||||
}
|
||||
|
||||
Vector<std::string> names = names_set.extract_vector();
|
||||
parallel_sort(
|
||||
names.begin(), names.end(), [](const StringRef a, const StringRef b) { return a < b; });
|
||||
params.set_output("Names"_ustr, GList::from_container(names));
|
||||
}
|
||||
|
||||
static void node_register()
|
||||
{
|
||||
static blender::bke::bNodeType ntype;
|
||||
|
||||
geo_node_type_base(&ntype, "GeometryNodeGetAttributeNames"_ustr);
|
||||
ntype.ui_name = "Get Attribute Names";
|
||||
ntype.ui_description = "Retrieves attribute names as a list of strings";
|
||||
ntype.nclass = NODE_CLASS_ATTRIBUTE;
|
||||
ntype.geometry_node_execute = node_geo_exec;
|
||||
ntype.declare = node_declare;
|
||||
bke::node_type_size(ntype, 160, 100, NODE_DEFAULT_MAX_WIDTH);
|
||||
blender::bke::node_register_type(ntype);
|
||||
}
|
||||
NOD_REGISTER_NODE(node_register)
|
||||
|
||||
} // namespace blender::nodes::node_geo_get_attribute_names_cc
|
||||
|
|
@ -920,6 +920,56 @@ BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::usage_by_menu(
|
|||
return *this;
|
||||
}
|
||||
|
||||
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::usage_by_bool(
|
||||
const UString bool_input_identifier, const bool value)
|
||||
{
|
||||
this->make_available([bool_input_identifier, value](bNode &node) {
|
||||
bNodeSocket &bool_socket = *bke::node_find_socket(node, SOCK_IN, bool_input_identifier);
|
||||
const SocketDeclaration &socket_declaration = *bool_socket.runtime->declaration;
|
||||
socket_declaration.make_available(node);
|
||||
bool_socket.default_value_typed<bNodeSocketValueBoolean>()->value = value;
|
||||
});
|
||||
this->usage_inference(
|
||||
[bool_input_identifier,
|
||||
value](const socket_usage_inference::SocketUsageParams ¶ms) -> std::optional<bool> {
|
||||
if (params.socket.is_input()) {
|
||||
if (std::optional<bool> any_output_used = params.any_output_is_used()) {
|
||||
if (!*any_output_used) {
|
||||
/* If no output is used, none of the inputs is used either. */
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* It's not known if any output is used yet. This function will be called again once
|
||||
* new information about output usages is available. */
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
const bool might_be_value = params.bool_input_may_be(bool_input_identifier, value);
|
||||
const bNodeSocket &bool_socket = *bke::node_find_socket(
|
||||
params.node, SOCK_IN, bool_input_identifier);
|
||||
const SocketDeclaration &bool_socket_declaration = *bool_socket.runtime->declaration;
|
||||
if (!bool_socket_declaration.usage_inference_fn) {
|
||||
return might_be_value;
|
||||
}
|
||||
const std::optional<bool> bool_might_be_used =
|
||||
(*bool_socket_declaration.usage_inference_fn)(params);
|
||||
if (!bool_might_be_used.has_value()) {
|
||||
return might_be_value;
|
||||
}
|
||||
return *bool_might_be_used && might_be_value;
|
||||
});
|
||||
return *this;
|
||||
}
|
||||
|
||||
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::usage_by_panel_toggle()
|
||||
{
|
||||
if (const SocketDeclaration *panel_toggle_decl = decl_base_->parent->panel_input_decl()) {
|
||||
this->usage_by_bool(panel_toggle_decl->identifier, true);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::align_with_previous(const bool value)
|
||||
{
|
||||
decl_base_->align_with_previous_socket = value;
|
||||
|
|
|
|||
|
|
@ -1097,6 +1097,17 @@ bool SocketUsageParams::menu_input_may_be(const UString identifier, const int en
|
|||
return value.get_primitive<MenuValue>().value == enum_value;
|
||||
}
|
||||
|
||||
bool SocketUsageParams::bool_input_may_be(const UString identifier, const bool bool_value) const
|
||||
{
|
||||
BLI_assert(this->node.input_by_identifier(identifier)->type == SOCK_BOOLEAN);
|
||||
const InferenceValue value = this->get_input(identifier);
|
||||
if (!value.is_primitive_value()) {
|
||||
/* The value is unknown, so it may be the requested enum value. */
|
||||
return true;
|
||||
}
|
||||
return value.get_primitive<bool>() == bool_value;
|
||||
}
|
||||
|
||||
void SocketUsageInferencer::mark_top_level_node_outputs_as_used()
|
||||
{
|
||||
impl_.mark_top_level_node_outputs_as_used();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue