diff --git a/source/blender/editors/include/UI_interface.hh b/source/blender/editors/include/UI_interface.hh index 4a583d0225e..5edccfa8c88 100644 --- a/source/blender/editors/include/UI_interface.hh +++ b/source/blender/editors/include/UI_interface.hh @@ -24,10 +24,22 @@ #include "BLI_string_ref.hh" +namespace blender::nodes::geometry_nodes_eval_log { +struct GeometryAttributeInfo; +} + struct uiBlock; namespace blender::ui { class AbstractTreeView; -} + +void attribute_search_add_items( + StringRefNull str, + const bool is_output, + Span infos, + uiSearchItems *items, + const bool is_first); + +} // namespace blender::ui blender::ui::AbstractTreeView *UI_block_add_view( uiBlock &block, diff --git a/source/blender/editors/interface/CMakeLists.txt b/source/blender/editors/interface/CMakeLists.txt index 8fcc704a301..b2659f5ed52 100644 --- a/source/blender/editors/interface/CMakeLists.txt +++ b/source/blender/editors/interface/CMakeLists.txt @@ -25,9 +25,11 @@ set(INC ../../depsgraph ../../draw ../../gpu + ../../functions ../../imbuf ../../makesdna ../../makesrna + ../../nodes ../../python ../../render ../../windowmanager @@ -69,6 +71,7 @@ set(SRC interface_style.c interface_template_asset_view.cc interface_template_list.cc + interface_template_attribute_search.cc interface_template_search_menu.c interface_template_search_operator.c interface_templates.c diff --git a/source/blender/editors/interface/interface_region_search.c b/source/blender/editors/interface/interface_region_search.c index 1cd3ef89ed3..5bea03dee63 100644 --- a/source/blender/editors/interface/interface_region_search.c +++ b/source/blender/editors/interface/interface_region_search.c @@ -316,7 +316,11 @@ bool ui_searchbox_apply(uiBut *but, ARegion *region) const char *name_sep = data->use_shortcut_sep ? strrchr(name, UI_SEP_CHAR) : NULL; - BLI_strncpy(but->editstr, name, name_sep ? (name_sep - name) + 1 : data->items.maxstrlen); + /* Search button with dynamic string properties may have their own method of applying + * the search results, so only copy the result if there is a proper space for it. */ + if (but->hardmax != 0) { + BLI_strncpy(but->editstr, name, name_sep ? (name_sep - name) + 1 : data->items.maxstrlen); + } search_but->item_active = data->items.pointers[data->active]; @@ -878,7 +882,8 @@ static ARegion *ui_searchbox_create_generic_ex(bContext *C, else { data->items.maxitem = SEARCH_ITEMS; } - data->items.maxstrlen = but->hardmax; + /* In case the button's string is dynamic, make sure there are buffers available. */ + data->items.maxstrlen = but->hardmax == 0 ? UI_MAX_NAME_STR : but->hardmax; data->items.totitem = 0; data->items.names = MEM_callocN(data->items.maxitem * sizeof(void *), "search names"); data->items.pointers = MEM_callocN(data->items.maxitem * sizeof(void *), "search pointers"); @@ -886,7 +891,7 @@ static ARegion *ui_searchbox_create_generic_ex(bContext *C, data->items.states = MEM_callocN(data->items.maxitem * sizeof(int), "search flags"); data->items.name_prefix_offsets = NULL; /* Lazy initialized as needed. */ for (int i = 0; i < data->items.maxitem; i++) { - data->items.names[i] = MEM_callocN(but->hardmax + 1, "search pointers"); + data->items.names[i] = MEM_callocN(data->items.maxstrlen + 1, "search pointers"); } return region; diff --git a/source/blender/editors/interface/interface_template_attribute_search.cc b/source/blender/editors/interface/interface_template_attribute_search.cc new file mode 100644 index 00000000000..0157d0b66a3 --- /dev/null +++ b/source/blender/editors/interface/interface_template_attribute_search.cc @@ -0,0 +1,125 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup edinterface + */ + +#include "BLI_string_ref.hh" +#include "BLI_string_search.h" + +#include "DNA_customdata_types.h" + +#include "RNA_access.h" +#include "RNA_enum_types.h" + +#include "BLT_translation.h" + +#include "NOD_geometry_nodes_eval_log.hh" + +#include "UI_interface.h" +#include "UI_interface.hh" +#include "UI_resources.h" + +using blender::nodes::geometry_nodes_eval_log::GeometryAttributeInfo; + +namespace blender::ui { + +static StringRef attribute_data_type_string(const CustomDataType type) +{ + const char *name = nullptr; + RNA_enum_name_from_value(rna_enum_attribute_type_items, type, &name); + return StringRef(IFACE_(name)); +} + +static StringRef attribute_domain_string(const AttributeDomain domain) +{ + const char *name = nullptr; + RNA_enum_name_from_value(rna_enum_attribute_domain_items, domain, &name); + return StringRef(IFACE_(name)); +} + +static bool attribute_search_item_add(uiSearchItems *items, const GeometryAttributeInfo &item) +{ + const StringRef data_type_name = attribute_data_type_string(item.data_type); + const StringRef domain_name = attribute_domain_string(item.domain); + std::string search_item_text = domain_name + " " + UI_MENU_ARROW_SEP + item.name + UI_SEP_CHAR + + data_type_name; + + return UI_search_item_add( + items, search_item_text.c_str(), (void *)&item, ICON_NONE, UI_BUT_HAS_SEP_CHAR, 0); +} + +void attribute_search_add_items(StringRefNull str, + const bool is_output, + Span infos, + uiSearchItems *seach_items, + const bool is_first) +{ + static GeometryAttributeInfo dummy_info; + + /* Any string may be valid, so add the current search string along with the hints. */ + if (str[0] != '\0') { + bool contained = false; + for (const GeometryAttributeInfo *attribute_info : infos) { + if (attribute_info->name == str) { + contained = true; + break; + } + } + if (!contained && is_output) { + dummy_info.name = str; + UI_search_item_add(seach_items, str.c_str(), &dummy_info, ICON_ADD, 0, 0); + } + } + + if (str[0] == '\0' && !is_first) { + /* Allow clearing the text field when the string is empty, but not on the first pass, + * or opening an attribute field for the first time would show this search item. */ + dummy_info.name = str; + UI_search_item_add(seach_items, str.c_str(), &dummy_info, ICON_X, 0, 0); + } + + /* Don't filter when the menu is first opened, but still run the search + * so the items are in the same order they will appear in while searching. */ + const char *string = is_first ? "" : str.c_str(); + + StringSearch *search = BLI_string_search_new(); + for (const GeometryAttributeInfo *item : infos) { + + /* Don't show the legacy "normal" attribute. */ + if (item->name == "normal" && item->domain == ATTR_DOMAIN_FACE) { + continue; + } + + BLI_string_search_add(search, item->name.c_str(), (void *)item); + } + + GeometryAttributeInfo **filtered_items; + const int filtered_amount = BLI_string_search_query(search, string, (void ***)&filtered_items); + + for (const int i : IndexRange(filtered_amount)) { + const GeometryAttributeInfo *item = filtered_items[i]; + if (!attribute_search_item_add(seach_items, *item)) { + break; + } + } + + MEM_freeN(filtered_items); + BLI_string_search_free(search); +} + +} // namespace blender::ui \ No newline at end of file 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 a69109db69c..d0ccbb03107 100644 --- a/source/blender/editors/space_node/node_geometry_attribute_search.cc +++ b/source/blender/editors/space_node/node_geometry_attribute_search.cc @@ -38,6 +38,7 @@ #include "BLT_translation.h" #include "UI_interface.h" +#include "UI_interface.hh" #include "UI_resources.h" #include "NOD_geometry_nodes_eval_log.hh" @@ -60,37 +61,6 @@ struct AttributeSearchData { /* This class must not have a destructor, since it is used by buttons and freed with #MEM_freeN. */ BLI_STATIC_ASSERT(std::is_trivially_destructible_v, ""); -static StringRef attribute_data_type_string(const CustomDataType type) -{ - const char *name = nullptr; - RNA_enum_name_from_value(rna_enum_attribute_type_items, type, &name); - return StringRef(IFACE_(name)); -} - -static StringRef attribute_domain_string(const AttributeDomain domain) -{ - const char *name = nullptr; - RNA_enum_name_from_value(rna_enum_attribute_domain_items, domain, &name); - return StringRef(IFACE_(name)); -} - -static bool attribute_search_item_add(uiSearchItems *items, const GeometryAttributeInfo &item) -{ - const StringRef data_type_name = attribute_data_type_string(item.data_type); - const StringRef domain_name = attribute_domain_string(item.domain); - std::string search_item_text = domain_name + " " + UI_MENU_ARROW_SEP + item.name + UI_SEP_CHAR + - data_type_name; - - return UI_search_item_add( - items, search_item_text.c_str(), (void *)&item, ICON_NONE, UI_BUT_HAS_SEP_CHAR, 0); -} - -static GeometryAttributeInfo &get_dummy_item_info() -{ - static GeometryAttributeInfo info; - return info; -} - static void attribute_search_update_fn( const bContext *C, void *arg, const char *str, uiSearchItems *items, const bool is_first) { @@ -104,51 +74,7 @@ static void attribute_search_update_fn( } blender::Vector infos = node_log->lookup_available_attributes(); - GeometryAttributeInfo &dummy_info = get_dummy_item_info(); - - /* Any string may be valid, so add the current search string along with the hints. */ - if (str[0] != '\0') { - bool contained = false; - for (const GeometryAttributeInfo *attribute_info : infos) { - if (attribute_info->name == str) { - contained = true; - break; - } - } - if (!contained) { - dummy_info.name = str; - UI_search_item_add(items, str, &dummy_info, ICON_ADD, 0, 0); - } - } - - if (str[0] == '\0' && !is_first) { - /* Allow clearing the text field when the string is empty, but not on the first pass, - * or opening an attribute field for the first time would show this search item. */ - dummy_info.name = str; - UI_search_item_add(items, str, &dummy_info, ICON_X, 0, 0); - } - - /* Don't filter when the menu is first opened, but still run the search - * so the items are in the same order they will appear in while searching. */ - const char *string = is_first ? "" : str; - - StringSearch *search = BLI_string_search_new(); - for (const GeometryAttributeInfo *item : infos) { - BLI_string_search_add(search, item->name.c_str(), (void *)item); - } - - GeometryAttributeInfo **filtered_items; - const int filtered_amount = BLI_string_search_query(search, string, (void ***)&filtered_items); - - for (const int i : IndexRange(filtered_amount)) { - const GeometryAttributeInfo *item = filtered_items[i]; - if (!attribute_search_item_add(items, *item)) { - break; - } - } - - MEM_freeN(filtered_items); - BLI_string_search_free(search); + blender::ui::attribute_search_add_items(str, true, infos, items, is_first); } static void attribute_search_exec_fn(bContext *C, void *data_v, void *item_v) diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index 562589e2610..e6cc7663c58 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -27,11 +27,13 @@ #include "MEM_guardedalloc.h" +#include "BLI_array.hh" #include "BLI_float3.hh" #include "BLI_listbase.h" #include "BLI_multi_value_map.hh" #include "BLI_set.hh" #include "BLI_string.h" +#include "BLI_string_search.h" #include "BLI_utildefines.h" #include "DNA_collection_types.h" @@ -66,6 +68,7 @@ #include "BLO_read_write.h" #include "UI_interface.h" +#include "UI_interface.hh" #include "UI_resources.h" #include "BLT_translation.h" @@ -84,6 +87,7 @@ #include "MOD_ui_common.h" #include "ED_spreadsheet.h" +#include "ED_undo.h" #include "NOD_derived_node_tree.hh" #include "NOD_geometry.h" @@ -93,6 +97,7 @@ #include "FN_field.hh" #include "FN_multi_function.hh" +using blender::Array; using blender::ColorGeometry4f; using blender::destruct_ptr; using blender::float3; @@ -114,6 +119,7 @@ using blender::nodes::InputSocketFieldType; using blender::threading::EnumerableThreadSpecific; using namespace blender::fn::multi_function_types; using namespace blender::nodes::derived_node_tree_types; +using geo_log::GeometryAttributeInfo; static void initData(ModifierData *md) { @@ -957,9 +963,6 @@ static GeometrySet compute_geometry(const DerivedNodeTree &tree, } } - /* Don't keep a reference to the input geometry components to avoid copies during evaluation. */ - input_geometry_set.clear(); - Vector group_outputs; for (const InputSocketRef *socket_ref : output_node.inputs().drop_back(1)) { group_outputs.append({root_context, socket_ref}); @@ -974,8 +977,13 @@ static GeometrySet compute_geometry(const DerivedNodeTree &tree, find_sockets_to_preview(nmd, ctx, tree, preview_sockets); eval_params.force_compute_sockets.extend(preview_sockets.begin(), preview_sockets.end()); geo_logger.emplace(std::move(preview_sockets)); + + geo_logger->log_input_geometry(input_geometry_set); } + /* Don't keep a reference to the input geometry components to avoid copies during evaluation. */ + input_geometry_set.clear(); + eval_params.input_values = group_inputs; eval_params.output_sockets = group_outputs; eval_params.mf_by_node = &mf_by_node; @@ -985,14 +993,15 @@ static GeometrySet compute_geometry(const DerivedNodeTree &tree, eval_params.geo_logger = geo_logger.has_value() ? &*geo_logger : nullptr; blender::modifiers::geometry_nodes::evaluate_geometry_nodes(eval_params); + GeometrySet output_geometry_set = eval_params.r_output_values[0].relocate_out(); + if (geo_logger.has_value()) { + geo_logger->log_output_geometry(output_geometry_set); NodesModifierData *nmd_orig = (NodesModifierData *)BKE_modifier_get_original(&nmd->modifier); clear_runtime_data(nmd_orig); nmd_orig->runtime_eval_log = new geo_log::ModifierLog(*geo_logger); } - GeometrySet output_geometry_set = eval_params.r_output_values[0].relocate_out(); - for (const InputSocketRef *socket : output_node.inputs().drop_front(1).drop_back(1)) { GMutablePointer socket_value = eval_params.r_output_values[socket->index()]; store_output_value_in_geometry(output_geometry_set, nmd, *socket, socket_value); @@ -1108,6 +1117,154 @@ static void modifyGeometrySet(ModifierData *md, modifyGeometry(md, ctx, *geometry_set); } +struct AttributeSearchData { + const geo_log::ModifierLog &modifier_log; + IDProperty &name_property; + bool is_output; +}; + +/* This class must not have a destructor, since it is used by buttons and freed with #MEM_freeN. */ +BLI_STATIC_ASSERT(std::is_trivially_destructible_v, ""); + +static void attribute_search_update_fn(const bContext *UNUSED(C), + void *arg, + const char *str, + uiSearchItems *items, + const bool is_first) +{ + AttributeSearchData *data = static_cast(arg); + + const geo_log::GeometryValueLog *geometry_log = data->is_output ? + data->modifier_log.output_geometry_log() : + data->modifier_log.input_geometry_log(); + if (geometry_log == nullptr) { + return; + } + + Span infos = geometry_log->attributes(); + + /* The shared attribute search code expects a span of pointers, so convert to that. */ + Array info_ptrs(infos.size()); + for (const int i : infos.index_range()) { + info_ptrs[i] = &infos[i]; + } + blender::ui::attribute_search_add_items( + str, data->is_output, info_ptrs.as_span(), items, is_first); +} + +static void attribute_search_exec_fn(bContext *C, void *data_v, void *item_v) +{ + if (item_v == nullptr) { + return; + } + AttributeSearchData &data = *static_cast(data_v); + const GeometryAttributeInfo &item = *static_cast(item_v); + + IDProperty &name_property = data.name_property; + BLI_assert(name_property.type == IDP_STRING); + IDP_AssignString(&name_property, item.name.c_str(), 0); + + ED_undo_push(C, "Assign Attribute Name"); +} + +static void add_attribute_search_button(uiLayout *layout, + const NodesModifierData &nmd, + PointerRNA *md_ptr, + const StringRefNull rna_path_attribute_name, + const bNodeSocket &socket, + const bool is_output) +{ + const geo_log::ModifierLog *log = static_cast(nmd.runtime_eval_log); + if (log == nullptr) { + uiItemR(layout, md_ptr, rna_path_attribute_name.c_str(), 0, "", ICON_NONE); + return; + } + + uiBlock *block = uiLayoutGetBlock(layout); + uiBut *but = uiDefIconTextButR(block, + UI_BTYPE_SEARCH_MENU, + 0, + ICON_NONE, + "", + 0, + 0, + 10 * UI_UNIT_X, /* Dummy value, replaced by layout system. */ + UI_UNIT_Y, + md_ptr, + rna_path_attribute_name.c_str(), + 0, + 0.0f, + 0.0f, + 0.0f, + 0.0f, + ""); + + const std::string use_attribute_prop_name = socket.identifier + attribute_name_suffix; + IDProperty *property = IDP_GetPropertyFromGroup(nmd.settings.properties, + use_attribute_prop_name.c_str()); + BLI_assert(property != nullptr); + if (property == nullptr) { + return; + } + + AttributeSearchData *data = OBJECT_GUARDED_NEW(AttributeSearchData, + {*log, *property, is_output}); + + UI_but_func_search_set_results_are_suggestions(but, true); + UI_but_func_search_set_sep_string(but, UI_MENU_ARROW_SEP); + UI_but_func_search_set(but, + nullptr, + attribute_search_update_fn, + static_cast(data), + true, + nullptr, + attribute_search_exec_fn, + nullptr); +} + +static void add_attribute_search_or_value_buttons(uiLayout *layout, + const NodesModifierData &nmd, + PointerRNA *md_ptr, + const bNodeSocket &socket) +{ + char socket_id_esc[sizeof(socket.identifier) * 2]; + BLI_str_escape(socket_id_esc, socket.identifier, sizeof(socket_id_esc)); + const std::string rna_path = "[\"" + std::string(socket_id_esc) + "\"]"; + 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 *split = uiLayoutSplit(layout, 0.4f, false); + uiLayout *name_row = uiLayoutRow(split, false); + uiLayoutSetAlignment(name_row, UI_LAYOUT_ALIGN_RIGHT); + uiItemL(name_row, socket.name, ICON_NONE); + + uiLayout *row = uiLayoutRow(split, true); + + 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()); + + const int use_attribute = RNA_int_get(md_ptr, rna_path_use_attribute.c_str()) != 0; + if (use_attribute) { + add_attribute_search_button(row, nmd, md_ptr, rna_path_attribute_name, socket, false); + uiItemL(row, "", ICON_BLANK1); + } + else { + uiItemR(row, md_ptr, rna_path.c_str(), 0, "", ICON_NONE); + uiItemDecoratorR(row, md_ptr, rna_path.c_str(), 0); + } +} + /* Drawing the properties manually with #uiItemR instead of #uiDefAutoButsRNA allows using * the node socket identifier for the property names, since they are unique, but also having * the correct label displayed in the UI. */ @@ -1166,39 +1323,19 @@ static void draw_property_for_socket(uiLayout *layout, } default: { if (input_has_attribute_toggle(*nmd->node_group, socket_index)) { - 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()); + add_attribute_search_or_value_buttons(layout, *nmd, md_ptr, socket); } else { - uiItemR(layout, md_ptr, rna_path, 0, socket.name, ICON_NONE); + uiLayout *row = uiLayoutRow(layout, false); + uiItemR(row, md_ptr, rna_path, 0, socket.name, ICON_NONE); + uiItemDecoratorR(row, md_ptr, rna_path, 0); } } } } static void draw_property_for_output_socket(uiLayout *layout, + const NodesModifierData &nmd, PointerRNA *md_ptr, const bNodeSocket &socket) { @@ -1207,7 +1344,13 @@ static void draw_property_for_output_socket(uiLayout *layout, const std::string rna_path_attribute_name = "[\"" + StringRef(socket_id_esc) + attribute_name_suffix + "\"]"; - uiItemR(layout, md_ptr, rna_path_attribute_name.c_str(), 0, socket.name, ICON_NONE); + uiLayout *split = uiLayoutSplit(layout, 0.4f, false); + uiLayout *name_row = uiLayoutRow(split, false); + uiLayoutSetAlignment(name_row, UI_LAYOUT_ALIGN_RIGHT); + uiItemL(name_row, socket.name, ICON_NONE); + + uiLayout *row = uiLayoutRow(split, true); + add_attribute_search_button(row, nmd, md_ptr, rna_path_attribute_name, socket, true); } static void panel_draw(const bContext *C, Panel *panel) @@ -1219,7 +1362,9 @@ static void panel_draw(const bContext *C, Panel *panel) NodesModifierData *nmd = static_cast(ptr->data); uiLayoutSetPropSep(layout, true); - uiLayoutSetPropDecorate(layout, true); + /* Decorators are added manually for supported properties because the + * attribute/value toggle requires a manually built layout anyway. */ + uiLayoutSetPropDecorate(layout, false); uiTemplateID(layout, C, @@ -1282,7 +1427,7 @@ static void output_attribute_panel_draw(const bContext *UNUSED(C), Panel *panel) if (nmd->node_group != nullptr && nmd->settings.properties != nullptr) { LISTBASE_FOREACH (bNodeSocket *, socket, &nmd->node_group->outputs) { if (socket_type_has_attribute_toggle(*socket)) { - draw_property_for_output_socket(layout, ptr, *socket); + draw_property_for_output_socket(layout, *nmd, ptr, *socket); } } } diff --git a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh b/source/blender/nodes/NOD_geometry_nodes_eval_log.hh index ffe2a0d63c3..830a7d4070c 100644 --- a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh +++ b/source/blender/nodes/NOD_geometry_nodes_eval_log.hh @@ -109,7 +109,7 @@ class GeometryValueLog : public ValueLog { std::optional pointcloud_info; std::optional instances_info; - GeometryValueLog(const GeometrySet &geometry_set, bool log_full_geometry); + GeometryValueLog(const GeometrySet &geometry_set, bool log_full_geometry = false); Span attributes() const { @@ -189,7 +189,12 @@ class GeoLogger { Set log_full_sockets_; threading::EnumerableThreadSpecific threadlocals_; + /* These are only optional since they don't have a default constructor. */ + std::unique_ptr input_geometry_log_; + std::unique_ptr output_geometry_log_; + friend LocalGeoLogger; + friend ModifierLog; public: GeoLogger(Set log_full_sockets) @@ -198,6 +203,16 @@ class GeoLogger { { } + void log_input_geometry(const GeometrySet &geometry) + { + input_geometry_log_ = std::make_unique(geometry); + } + + void log_output_geometry(const GeometrySet &geometry) + { + output_geometry_log_ = std::make_unique(geometry); + } + LocalGeoLogger &local() { return threadlocals_.local(); @@ -283,6 +298,9 @@ class ModifierLog { destruct_ptr root_tree_logs_; Vector> logged_values_; + std::unique_ptr input_geometry_log_; + std::unique_ptr output_geometry_log_; + public: ModifierLog(GeoLogger &logger); @@ -303,6 +321,9 @@ class ModifierLog { const SpaceSpreadsheet &sspreadsheet); void foreach_node_log(FunctionRef fn) const; + const GeometryValueLog *input_geometry_log() const; + const GeometryValueLog *output_geometry_log() const; + private: using LogByTreeContext = Map; diff --git a/source/blender/nodes/intern/geometry_nodes_eval_log.cc b/source/blender/nodes/intern/geometry_nodes_eval_log.cc index d39f8367686..852f52f38cd 100644 --- a/source/blender/nodes/intern/geometry_nodes_eval_log.cc +++ b/source/blender/nodes/intern/geometry_nodes_eval_log.cc @@ -26,6 +26,8 @@ namespace blender::nodes::geometry_nodes_eval_log { using fn::CPPType; ModifierLog::ModifierLog(GeoLogger &logger) + : input_geometry_log_(std::move(logger.input_geometry_log_)), + output_geometry_log_(std::move(logger.output_geometry_log_)) { root_tree_logs_ = allocator_.construct(); @@ -106,6 +108,15 @@ void ModifierLog::foreach_node_log(FunctionRef fn) const } } +const GeometryValueLog *ModifierLog::input_geometry_log() const +{ + return input_geometry_log_.get(); +} +const GeometryValueLog *ModifierLog::output_geometry_log() const +{ + return output_geometry_log_.get(); +} + const NodeLog *TreeLog::lookup_node_log(StringRef node_name) const { const destruct_ptr *node_log = node_logs_.lookup_ptr_as(node_name);