From 8e21d528cab98447fd072718a649906551de78d9 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 16 Sep 2021 20:49:10 -0500 Subject: Geometry Nodes: Add a toggle to use attributes as input values This adds a toggle to node group inputs exposed in the modifier to use an attribute instead of a single value. When the toggle is pressed, the button switches to a text button to choose an attribute name. Attribute search isn't implemented here yet. One confusing thing is that some values can't be driven by attributes at all, like the size of a primitive node. In that case, we should have a node warning, but that will be separate since it's more general. We can also have an option to turn off this toggle in node group input settings. The two new properties for each input are stored with the same name as the value, but with `"_use_attribute"` and `"_attribute_name"`` suffixes. The properties are not added for socket types that don't support attribute input, like object sockets. Differential Revision: https://developer.blender.org/D12504 --- source/blender/modifiers/intern/MOD_nodes.cc | 106 +++++++++++++++++++++++++-- 1 file changed, 99 insertions(+), 7 deletions(-) (limited to 'source/blender/modifiers') diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index 3b952e1e649..011890f4d43 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -68,6 +68,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "WM_types.h" + #include "RNA_access.h" #include "RNA_enum_types.h" @@ -291,6 +293,17 @@ static bool logging_enabled(const ModifierEvalContext *ctx) return true; } +static const std::string use_attribute_suffix = "_use_attribute"; +static const std::string attribute_name_suffix = "_attribute_name"; + +/** + * \return Whether using an attribute to input values of this type is supported. + */ +static bool socket_type_has_attribute_toggle(const bNodeSocket &socket) +{ + return ELEM(socket.type, SOCK_FLOAT, SOCK_VECTOR, SOCK_BOOLEAN, SOCK_RGBA, SOCK_INT); +} + static IDProperty *id_property_create_from_socket(const bNodeSocket &socket) { switch (socket.type) { @@ -546,6 +559,32 @@ void MOD_nodes_update_interface(Object *object, NodesModifierData *nmd) new_prop->ui_data = ui_data; } } + + if (socket_type_has_attribute_toggle(*socket)) { + const std::string use_attribute_id = socket->identifier + use_attribute_suffix; + const std::string attribute_name_id = socket->identifier + attribute_name_suffix; + + IDPropertyTemplate idprop = {0}; + IDProperty *use_attribute_prop = IDP_New(IDP_INT, &idprop, use_attribute_id.c_str()); + IDP_AddToGroup(nmd->settings.properties, use_attribute_prop); + + IDProperty *attribute_prop = IDP_New(IDP_STRING, &idprop, attribute_name_id.c_str()); + IDP_AddToGroup(nmd->settings.properties, attribute_prop); + + if (old_properties != nullptr) { + IDProperty *old_prop_use_attribute = IDP_GetPropertyFromGroup(old_properties, + use_attribute_id.c_str()); + if (old_prop_use_attribute != nullptr) { + IDP_CopyPropertyContent(use_attribute_prop, old_prop_use_attribute); + } + + IDProperty *old_attribute_name_prop = IDP_GetPropertyFromGroup(old_properties, + attribute_name_id.c_str()); + if (old_attribute_name_prop != nullptr) { + IDP_CopyPropertyContent(attribute_prop, old_attribute_name_prop); + } + } + } } if (old_properties != nullptr) { @@ -601,8 +640,31 @@ static void initialize_group_input(NodesModifierData &nmd, return; } - init_socket_cpp_value_from_property( - *property, static_cast(socket.type), r_value); + if (!socket_type_has_attribute_toggle(socket)) { + init_socket_cpp_value_from_property( + *property, static_cast(socket.type), r_value); + } + + const IDProperty *property_use_attribute = IDP_GetPropertyFromGroup( + nmd.settings.properties, (socket.identifier + use_attribute_suffix).c_str()); + const IDProperty *property_attribute_name = IDP_GetPropertyFromGroup( + nmd.settings.properties, (socket.identifier + attribute_name_suffix).c_str()); + if (property_use_attribute == nullptr || property_attribute_name == nullptr) { + socket.typeinfo->get_geometry_nodes_cpp_value(socket, r_value); + return; + } + + const bool use_attribute = IDP_Int(property_use_attribute) != 0; + if (use_attribute) { + const StringRef attribute_name{IDP_String(property_attribute_name)}; + auto attribute_input = std::make_shared( + attribute_name, *socket.typeinfo->get_base_cpp_type()); + new (r_value) blender::fn::GField(std::move(attribute_input), 0); + } + else { + init_socket_cpp_value_from_property( + *property, static_cast(socket.type), r_value); + } } static Vector find_spreadsheet_editors(Main *bmain) @@ -912,13 +974,13 @@ static void modifyGeometrySet(ModifierData *md, * the node socket identifier for the property names, since they are unique, but also having * the correct label displayed in the UI. */ static void draw_property_for_socket(uiLayout *layout, + NodesModifierData *nmd, PointerRNA *bmain_ptr, PointerRNA *md_ptr, - const IDProperty *modifier_props, const bNodeSocket &socket) { /* The property should be created in #MOD_nodes_update_interface with the correct type. */ - IDProperty *property = IDP_GetPropertyFromGroup(modifier_props, socket.identifier); + IDProperty *property = IDP_GetPropertyFromGroup(nmd->settings.properties, socket.identifier); /* IDProperties can be removed with python, so there could be a situation where * there isn't a property for a socket or it doesn't have the correct type. */ @@ -959,8 +1021,38 @@ static void draw_property_for_socket(uiLayout *layout, uiItemPointerR(layout, md_ptr, rna_path, bmain_ptr, "textures", socket.name, ICON_TEXTURE); break; } - default: - uiItemR(layout, md_ptr, rna_path, 0, socket.name, ICON_NONE); + default: { + if (socket_type_has_attribute_toggle(socket) && + USER_EXPERIMENTAL_TEST(&U, use_geometry_nodes_fields)) { + const std::string rna_path_use_attribute = "[\"" + std::string(socket_id_esc) + + use_attribute_suffix + "\"]"; + const std::string rna_path_attribute_name = "[\"" + std::string(socket_id_esc) + + attribute_name_suffix + "\"]"; + + uiLayout *row = uiLayoutRow(layout, true); + const int use_attribute = RNA_int_get(md_ptr, rna_path_use_attribute.c_str()) != 0; + if (use_attribute) { + uiItemR(row, md_ptr, rna_path_attribute_name.c_str(), 0, socket.name, ICON_NONE); + } + else { + uiItemR(row, md_ptr, rna_path, 0, socket.name, ICON_NONE); + } + PointerRNA props; + uiItemFullO(row, + "object.geometry_nodes_input_attribute_toggle", + "", + ICON_SPREADSHEET, + nullptr, + WM_OP_INVOKE_DEFAULT, + 0, + &props); + RNA_string_set(&props, "modifier_name", nmd->modifier.name); + RNA_string_set(&props, "prop_path", rna_path_use_attribute.c_str()); + } + else { + uiItemR(layout, md_ptr, rna_path, 0, socket.name, ICON_NONE); + } + } } } @@ -991,7 +1083,7 @@ static void panel_draw(const bContext *C, Panel *panel) RNA_main_pointer_create(bmain, &bmain_ptr); LISTBASE_FOREACH (bNodeSocket *, socket, &nmd->node_group->inputs) { - draw_property_for_socket(layout, &bmain_ptr, ptr, nmd->settings.properties, *socket); + draw_property_for_socket(layout, nmd, &bmain_ptr, ptr, *socket); } } -- cgit v1.2.3