Geometry Nodes: Attribute search in the modifier

This adds attribute search to the geometry nodes modifier
for the input and output attributes. The "New" search item
is only shown for the output attributes.

Some of the attribute search code is extracted to a new file
in the interface code, to avoid some code duplication.

The UI code required two fixes so that the search would work
for dynamic length strings (IDProperties do not have a fixed size).

Since this does changes to the UI layout of the modifier, I also
addressed T91485 here.

Differential Revisiion: https://developer.blender.org/D12788
This commit is contained in:
Hans Goudey 2021-10-21 13:54:48 -05:00
parent 65490e6270
commit 1d96a48267
8 changed files with 362 additions and 114 deletions

View File

@ -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<const nodes::geometry_nodes_eval_log::GeometryAttributeInfo *> infos,
uiSearchItems *items,
const bool is_first);
} // namespace blender::ui
blender::ui::AbstractTreeView *UI_block_add_view(
uiBlock &block,

View File

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

View File

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

View File

@ -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<const GeometryAttributeInfo *> 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

View File

@ -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<AttributeSearchData>, "");
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<const GeometryAttributeInfo *> 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)

View File

@ -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<DInputSocket> 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<GeometrySet>();
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<GeometrySet>();
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<AttributeSearchData>, "");
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<AttributeSearchData *>(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<GeometryAttributeInfo> infos = geometry_log->attributes();
/* The shared attribute search code expects a span of pointers, so convert to that. */
Array<const GeometryAttributeInfo *> 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<AttributeSearchData *>(data_v);
const GeometryAttributeInfo &item = *static_cast<const GeometryAttributeInfo *>(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<geo_log::ModifierLog *>(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<void *>(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<NodesModifierData *>(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);
}
}
}

View File

@ -109,7 +109,7 @@ class GeometryValueLog : public ValueLog {
std::optional<PointCloudInfo> pointcloud_info;
std::optional<InstancesInfo> instances_info;
GeometryValueLog(const GeometrySet &geometry_set, bool log_full_geometry);
GeometryValueLog(const GeometrySet &geometry_set, bool log_full_geometry = false);
Span<GeometryAttributeInfo> attributes() const
{
@ -189,7 +189,12 @@ class GeoLogger {
Set<DSocket> log_full_sockets_;
threading::EnumerableThreadSpecific<LocalGeoLogger> threadlocals_;
/* These are only optional since they don't have a default constructor. */
std::unique_ptr<GeometryValueLog> input_geometry_log_;
std::unique_ptr<GeometryValueLog> output_geometry_log_;
friend LocalGeoLogger;
friend ModifierLog;
public:
GeoLogger(Set<DSocket> log_full_sockets)
@ -198,6 +203,16 @@ class GeoLogger {
{
}
void log_input_geometry(const GeometrySet &geometry)
{
input_geometry_log_ = std::make_unique<GeometryValueLog>(geometry);
}
void log_output_geometry(const GeometrySet &geometry)
{
output_geometry_log_ = std::make_unique<GeometryValueLog>(geometry);
}
LocalGeoLogger &local()
{
return threadlocals_.local();
@ -283,6 +298,9 @@ class ModifierLog {
destruct_ptr<TreeLog> root_tree_logs_;
Vector<destruct_ptr<ValueLog>> logged_values_;
std::unique_ptr<GeometryValueLog> input_geometry_log_;
std::unique_ptr<GeometryValueLog> output_geometry_log_;
public:
ModifierLog(GeoLogger &logger);
@ -303,6 +321,9 @@ class ModifierLog {
const SpaceSpreadsheet &sspreadsheet);
void foreach_node_log(FunctionRef<void(const NodeLog &)> fn) const;
const GeometryValueLog *input_geometry_log() const;
const GeometryValueLog *output_geometry_log() const;
private:
using LogByTreeContext = Map<const DTreeContext *, TreeLog *>;

View File

@ -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<TreeLog>();
@ -106,6 +108,15 @@ void ModifierLog::foreach_node_log(FunctionRef<void(const NodeLog &)> 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<NodeLog> *node_log = node_logs_.lookup_ptr_as(node_name);