Geometry Nodes: support viewing non-geometry data with viewer node

As discussed in the last geometry nodes workshop, the viewer node now
needs the flexibility to handle new features: bundles, closures, and
lists. This PR takes the opportunity to add support for an arbitrary
number of items. Values are displayed directly in the node are all
displayed in the spreadsheet, where a new tree view allows selecting
which data to view, including nested bundles. Lists, single values,
bundle items, and closure signatures are all visualized in the spreadsheet.

We also prioritize the existing viewer behavior that views a geometry
together with a field, so various special cases are added in the viewer
activation to handle this.

Bundle hierarchies are displayed in the new tree view in the spreadsheet
sidebar. The spreadsheet itself just displays bundle identifiers, types,
and the contained values. Design wise, there might be more integrated
ways to present that hierarchy, but doing it in the tree view is a very
simple starting place.

Interactively added viewer node inputs are now removed automatically
if the link is removed. There is a new "Auto Remove" flag for each input
controlling this behavior. It can't be enabled for all inputs all the time
because then one couldn't e.g. setup the viewer node properly using
a script (which might add a few inputs first and then creates links).
Also when viewer items are added with the plus icon in the sidebar,
they are not automatically removed immediately.

https://code.blender.org/2025/07/geometry-nodes-workshop-july-2025/#view-any-data

Co-authored-by: Hans Goudey <hans@blender.org>
Pull Request: https://projects.blender.org/blender/blender/pulls/144050
This commit is contained in:
Jacques Lucke
2025-09-22 18:08:45 +02:00
committed by Hans Goudey
parent 849aba1ccf
commit 5ffc5df4f6
33 changed files with 1635 additions and 310 deletions

View File

@@ -27,7 +27,7 @@
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
#define BLENDER_FILE_SUBVERSION 85
#define BLENDER_FILE_SUBVERSION 86
/* Minimum Blender version that supports reading file written with the current
* version. Older Blender versions will test this and cancel loading the file, showing a warning to

View File

@@ -45,6 +45,7 @@ BLI_CPP_TYPE_MAKE(blender::bke::SocketValueVariant, CPPTypeFlags::Printable);
BLI_VECTOR_CPP_TYPE_MAKE(blender::bke::SocketValueVariant);
BLI_CPP_TYPE_MAKE(blender::nodes::GeoNodesMultiInput<blender::bke::SocketValueVariant>,
CPPTypeFlags::None);
BLI_CPP_TYPE_MAKE(blender::nodes::BundleItemValue, CPPTypeFlags::None);
void BKE_cpp_types_init()
{
@@ -71,4 +72,5 @@ void BKE_cpp_types_init()
BLI_CPP_TYPE_REGISTER(blender::bke::SocketValueVariant);
BLI_VECTOR_CPP_TYPE_REGISTER(blender::bke::SocketValueVariant);
BLI_CPP_TYPE_REGISTER(blender::nodes::GeoNodesMultiInput<blender::bke::SocketValueVariant>);
BLI_CPP_TYPE_REGISTER(blender::nodes::BundleItemValue);
}

View File

@@ -1073,6 +1073,26 @@ static void node_blend_write_storage(BlendWriter *writer, bNodeTree *ntree, bNod
}
}
}
else if (node->type_legacy == GEO_NODE_VIEWER) {
/* Forward compatibility for older Blender versionins where the viewer node only had a geometry
* and field input. */
auto &storage = *static_cast<NodeGeometryViewer *>(node->storage);
for (const NodeGeometryViewerItem &item : Span{storage.items, storage.items_num}) {
if (ELEM(item.socket_type,
SOCK_FLOAT,
SOCK_INT,
SOCK_VECTOR,
SOCK_RGBA,
SOCK_BOOLEAN,
SOCK_ROTATION,
SOCK_MATRIX))
{
storage.data_type_legacy = *socket_type_to_custom_data_type(
eNodeSocketDatatype(item.socket_type));
break;
}
}
}
const bNodeType *ntype = node->typeinfo;
if (!ntype->storagename.empty()) {

View File

@@ -33,6 +33,7 @@
#include "MOD_nodes.hh"
#include "NOD_geo_viewer.hh"
#include "NOD_geometry_nodes_dependencies.hh"
#include "NOD_geometry_nodes_gizmos.hh"
#include "NOD_geometry_nodes_lazy_function.hh"
@@ -622,6 +623,9 @@ class NodeTreeMainUpdater {
bke::node_declaration_ensure(ntree, *node);
if (this->should_update_individual_node(ntree, *node)) {
bke::bNodeType &ntype = *node->typeinfo;
if (ntype.type_legacy == GEO_NODE_VIEWER) {
this->remove_unused_geometry_nodes_viewer_sockets(ntree, *node);
}
if (ntype.declare) {
/* Should have been created when the node was registered. */
BLI_assert(ntype.static_declaration != nullptr);
@@ -677,6 +681,35 @@ class NodeTreeMainUpdater {
return false;
}
void remove_unused_geometry_nodes_viewer_sockets(bNodeTree &ntree, bNode &viewer_node)
{
ntree.ensure_topology_cache();
Vector<int> item_indices_to_remove;
auto &storage = *static_cast<NodeGeometryViewer *>(viewer_node.storage);
for (const int i : IndexRange(storage.items_num)) {
const NodeGeometryViewerItem &item = storage.items[i];
if (!(item.flag & NODE_GEO_VIEWER_ITEM_FLAG_AUTO_REMOVE)) {
continue;
}
const std::string identifier_str = GeoViewerItemsAccessor::socket_identifier_for_item(item);
const bNodeSocket *socket = viewer_node.input_by_identifier(identifier_str.c_str());
if (!socket) {
continue;
}
if (!socket->is_directly_linked()) {
item_indices_to_remove.append(i);
}
}
std::reverse(item_indices_to_remove.begin(), item_indices_to_remove.end());
for (const int i : item_indices_to_remove) {
dna::array::remove_index(&storage.items,
&storage.items_num,
&storage.active_index,
i,
GeoViewerItemsAccessor::destruct_item);
}
}
struct InternalLink {
bNodeSocket *from;
bNodeSocket *to;

View File

@@ -1861,12 +1861,14 @@ void object_duplilist_preview(Depsgraph *depsgraph,
if (const geo_log::ViewerNodeLog *viewer_log =
geo_log::GeoNodesLog::find_viewer_node_log_for_path(*viewer_path))
{
ctx.preview_base_geometry = &viewer_log->geometry;
make_duplis_geometry_set_impl(&ctx,
viewer_log->geometry,
ob_eval->object_to_world().ptr(),
true,
ob_eval->type == OB_CURVES);
if (std::optional<blender::bke::GeometrySet> viewer_geometry = viewer_log->main_geometry()) {
ctx.preview_base_geometry = &*viewer_geometry;
make_duplis_geometry_set_impl(&ctx,
*viewer_geometry,
ob_eval->object_to_world().ptr(),
true,
ob_eval->type == OB_CURVES);
}
}
}
}

View File

@@ -2977,7 +2977,7 @@ void blo_do_versions_300(FileData *fd, Library * /*lib*/, Main *bmain)
if (node->type_legacy == GEO_NODE_VIEWER) {
if (node->storage == nullptr) {
NodeGeometryViewer *data = MEM_callocN<NodeGeometryViewer>(__func__);
data->data_type = CD_PROP_FLOAT;
data->data_type_legacy = CD_PROP_FLOAT;
node->storage = data;
}
}

View File

@@ -2253,6 +2253,34 @@ static void do_version_double_edge_mask_options_to_inputs(bNodeTree &ntree, bNod
node.custom1);
}
static void version_dynamic_viewer_node_items(bNodeTree &ntree)
{
LISTBASE_FOREACH (bNode *, node, &ntree.nodes) {
if (node->type_legacy != GEO_NODE_VIEWER) {
continue;
}
NodeGeometryViewer *storage = static_cast<NodeGeometryViewer *>(node->storage);
const int input_sockets_num = BLI_listbase_count(&node->inputs);
if (input_sockets_num == storage->items_num + 1) {
/* Make versioning idempotent. */
continue;
}
storage->items_num = 2;
storage->items = MEM_calloc_arrayN<NodeGeometryViewerItem>(2, __func__);
NodeGeometryViewerItem &geometry_item = storage->items[0];
geometry_item.name = BLI_strdup("Geometry");
geometry_item.socket_type = SOCK_GEOMETRY;
geometry_item.identifier = 0;
NodeGeometryViewerItem &value_item = storage->items[1];
value_item.name = BLI_strdup("Value");
value_item.socket_type = blender::bke::custom_data_type_to_socket_type(
eCustomDataType(storage->data_type_legacy))
.value_or(SOCK_FLOAT);
value_item.identifier = 1;
storage->next_identifier = 2;
}
}
void do_versions_after_linking_500(FileData *fd, Main *bmain)
{
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 9)) {
@@ -3365,6 +3393,16 @@ void blo_do_versions_500(FileData *fd, Library * /*lib*/, Main *bmain)
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 86)) {
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
if (ntree->type != NTREE_GEOMETRY) {
continue;
}
version_dynamic_viewer_node_items(*ntree);
}
FOREACH_NODETREE_END;
}
/**
* Always bump subversion in BKE_blender_version.h when adding versioning
* code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check.

View File

@@ -26,7 +26,10 @@ namespace blender::ed::viewer_path {
* Activates the given node in the context provided by the editor. This indirectly updates all
* non-pinned viewer paths in other editors (spreadsheet and 3d view).
*/
void activate_geometry_node(Main &bmain, SpaceNode &snode, bNode &node);
void activate_geometry_node(Main &bmain,
SpaceNode &snode,
bNode &node,
std::optional<int> item_identifier = std::nullopt);
/**
* Returns the object referenced by the viewer path. This only returns something if the viewer path

View File

@@ -8,6 +8,7 @@
#include "MEM_guardedalloc.h"
#include "DNA_array_utils.hh"
#include "DNA_node_types.h"
#include "BLI_easing.h"
@@ -40,8 +41,10 @@
#include "UI_resources.hh"
#include "UI_view2d.hh"
#include "NOD_geo_viewer.hh"
#include "NOD_node_declaration.hh"
#include "NOD_socket.hh"
#include "NOD_socket_items.hh"
#include "node_intern.hh" /* own include */
@@ -455,21 +458,93 @@ static bool socket_can_be_viewed(const bNodeSocket &socket)
if (STREQ(socket.idname, "NodeSocketVirtual")) {
return false;
}
if (socket.owner_tree().type != NTREE_GEOMETRY) {
return true;
}
return ELEM(socket.typeinfo->type,
SOCK_GEOMETRY,
SOCK_FLOAT,
SOCK_VECTOR,
SOCK_INT,
SOCK_BOOLEAN,
SOCK_ROTATION,
SOCK_MATRIX,
SOCK_RGBA,
SOCK_MENU);
return true;
}
static void ensure_geometry_nodes_viewer_starts_with_geometry_socket(bNodeTree &tree,
bNode &viewer_node)
{
const auto &storage = *static_cast<const NodeGeometryViewer *>(viewer_node.storage);
if (storage.items_num >= 1) {
const NodeGeometryViewerItem &first_item = storage.items[0];
if (first_item.socket_type == SOCK_GEOMETRY) {
return;
}
}
std::optional<int> existing_geometry_index;
for (const int i : IndexRange(storage.items_num)) {
const NodeGeometryViewerItem &item = storage.items[i];
if (item.socket_type == SOCK_GEOMETRY) {
existing_geometry_index = i;
break;
}
}
if (existing_geometry_index) {
BLI_assert(*existing_geometry_index >= 1);
dna::array::move_index(storage.items, storage.items_num, *existing_geometry_index, 0);
return;
}
nodes::socket_items::add_item_with_socket_type_and_name<nodes::GeoViewerItemsAccessor>(
tree, viewer_node, SOCK_GEOMETRY, "Geometry");
dna::array::move_index(storage.items, storage.items_num, storage.items_num - 1, 0);
}
static int ensure_geometry_nodes_viewer_has_non_geometry_socket(
bNodeTree &ntree, bNode &viewer_node, const eNodeSocketDatatype socket_type)
{
const auto &storage = *static_cast<const NodeGeometryViewer *>(viewer_node.storage);
if (storage.items_num == 0) {
nodes::socket_items::add_item_with_socket_type_and_name<nodes::GeoViewerItemsAccessor>(
ntree, viewer_node, socket_type, IFACE_("Value"));
return 0;
}
if (storage.items_num == 1 && storage.items[0].socket_type != SOCK_GEOMETRY) {
storage.items[0].socket_type = socket_type;
return 0;
}
if (storage.items_num == 1 && storage.items[0].socket_type == SOCK_GEOMETRY) {
nodes::socket_items::add_item_with_socket_type_and_name<nodes::GeoViewerItemsAccessor>(
ntree, viewer_node, socket_type, IFACE_("Value"));
return 1;
}
if (storage.items_num == 2 && storage.items[0].socket_type == SOCK_GEOMETRY &&
storage.items[1].socket_type != SOCK_GEOMETRY)
{
storage.items[1].socket_type = socket_type;
return 1;
}
std::optional<int> existing_geometry_index;
for (const int i : IndexRange(storage.items_num)) {
if (storage.items[i].socket_type == SOCK_GEOMETRY) {
existing_geometry_index = i;
break;
}
}
if (existing_geometry_index) {
dna::array::move_index(storage.items, storage.items_num, *existing_geometry_index, 0);
storage.items[1].socket_type = socket_type;
MEM_SAFE_FREE(storage.items[1].name);
storage.items[1].name = BLI_strdup(IFACE_("Value"));
return 1;
}
storage.items[0].socket_type = socket_type;
MEM_SAFE_FREE(storage.items[0].name);
storage.items[0].name = BLI_strdup(IFACE_("Value"));
return 0;
}
static std::string get_viewer_source_name(const bNodeSocket &socket)
{
const bNode &node = socket.owner_node();
if (node.is_reroute()) {
const bNodeSocket &reroute_input = node.input_socket(0);
if (!reroute_input.is_logically_linked()) {
return IFACE_(socket.typeinfo->label);
}
return reroute_input.logically_linked_sockets()[0]->name;
}
return socket.name;
}
/**
* Find the socket to link to in a viewer node.
*/
@@ -483,23 +558,25 @@ static bNodeSocket *node_link_viewer_get_socket(bNodeTree &ntree,
}
/* For the geometry nodes viewer, find the socket with the correct type. */
const std::string name = get_viewer_source_name(src_socket);
int item_index;
if (src_socket.type == SOCK_GEOMETRY) {
return static_cast<bNodeSocket *>(viewer_node.inputs.first);
ensure_geometry_nodes_viewer_starts_with_geometry_socket(ntree, viewer_node);
item_index = 0;
}
else {
item_index = ensure_geometry_nodes_viewer_has_non_geometry_socket(
ntree, viewer_node, src_socket.typeinfo->type);
}
ntree.ensure_topology_cache();
if (!socket_can_be_viewed(src_socket)) {
return nullptr;
}
NodeGeometryViewer &storage = *static_cast<NodeGeometryViewer *>(viewer_node.storage);
const eCustomDataType data_type = *bke::socket_type_to_custom_data_type(
eNodeSocketDatatype(src_socket.type));
BLI_assert(data_type != CD_AUTO_FROM_NAME);
storage.data_type = data_type;
auto &storage = *static_cast<NodeGeometryViewer *>(viewer_node.storage);
NodeGeometryViewerItem &item = storage.items[item_index];
nodes::socket_items::set_item_name_and_make_unique<nodes::GeoViewerItemsAccessor>(
viewer_node, item, name.c_str());
item.flag |= NODE_GEO_VIEWER_ITEM_FLAG_AUTO_REMOVE;
nodes::update_node_declaration_and_sockets(ntree, viewer_node);
return static_cast<bNodeSocket *>(viewer_node.inputs.last);
return static_cast<bNodeSocket *>(BLI_findlink(&viewer_node.inputs, item_index));
}
static bool is_viewer_node(const bNode &node)
@@ -631,7 +708,17 @@ static void finalize_viewer_link(const bContext &C,
viewer_node.flag |= NODE_DO_OUTPUT;
if (snode.edittree->type == NTREE_GEOMETRY) {
viewer_path::activate_geometry_node(*bmain, snode, viewer_node);
std::optional<int> item_identifier;
const NodeGeometryViewerItem *item =
nodes::socket_items::find_item_by_identifier<nodes::GeoViewerItemsAccessor>(
viewer_node, viewer_link.tosock->identifier);
BLI_assert(item);
if (item) {
item_identifier = item->identifier;
}
viewer_path::activate_geometry_node(*bmain, snode, viewer_node, item_identifier);
}
else if (snode.edittree->type == NTREE_COMPOSIT) {
for (bNode *node : snode.nodetree->all_nodes()) {

View File

@@ -19,6 +19,8 @@
#include "BKE_geometry_set.hh"
#include "BKE_instances.hh"
#include "NOD_geometry_nodes_bundle.hh"
#include "spreadsheet_column.hh"
#include "spreadsheet_column_values.hh"
@@ -65,6 +67,9 @@ eSpreadsheetColumnValueType cpp_type_to_column_type(const CPPType &type)
if (type.is<float4x4>()) {
return SPREADSHEET_VALUE_TYPE_FLOAT4X4;
}
if (type.is<nodes::BundleItemValue>()) {
return SPREADSHEET_VALUE_TYPE_BUNDLE_ITEM;
}
return SPREADSHEET_VALUE_TYPE_UNKNOWN;
}

View File

@@ -2,6 +2,8 @@
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include <fmt/format.h>
#include "BLI_listbase.h"
#include "BLI_math_matrix.hh"
#include "BLI_virtual_array.hh"
@@ -32,6 +34,9 @@
#include "ED_curves.hh"
#include "ED_outliner.hh"
#include "NOD_geometry_nodes_bundle.hh"
#include "NOD_geometry_nodes_closure.hh"
#include "NOD_geometry_nodes_list.hh"
#include "NOD_geometry_nodes_log.hh"
#include "BLT_translation.hh"
@@ -46,8 +51,6 @@
#include "spreadsheet_data_source_geometry.hh"
#include "spreadsheet_intern.hh"
using blender::nodes::geo_eval_log::ViewerNodeLog;
uint64_t SpreadsheetInstanceID::hash() const
{
return blender::get_default_hash(this->reference_index);
@@ -63,6 +66,16 @@ bool operator!=(const SpreadsheetInstanceID &a, const SpreadsheetInstanceID &b)
return !(a == b);
}
bool operator==(const SpreadsheetBundlePathElem &a, const SpreadsheetBundlePathElem &b)
{
return STREQ(a.identifier, b.identifier);
}
bool operator!=(const SpreadsheetBundlePathElem &a, const SpreadsheetBundlePathElem &b)
{
return !(a == b);
}
namespace blender::ed::spreadsheet {
static void add_mesh_debug_column_names(
@@ -608,6 +621,262 @@ int VolumeDataSource::tot_rows() const
return BKE_volume_num_grids(volume);
}
ListDataSource::ListDataSource(nodes::ListPtr list) : list_(std::move(list)) {}
void ListDataSource::foreach_default_column_ids(
FunctionRef<void(const SpreadsheetColumnID &, bool is_extra)> fn) const
{
if (list_->size() == 0) {
return;
}
for (const char *name : {"Value"}) {
SpreadsheetColumnID column_id{(char *)name};
fn(column_id, false);
}
}
std::unique_ptr<ColumnValues> ListDataSource::get_column_values(
const SpreadsheetColumnID &column_id) const
{
if (STREQ(column_id.name, "Value")) {
return std::make_unique<ColumnValues>(IFACE_("Value"), list_->varray());
}
return {};
}
int ListDataSource::tot_rows() const
{
return list_->size();
}
BundleDataSource::BundleDataSource(nodes::BundlePtr bundle) : bundle_(std::move(bundle))
{
this->collect_flat_items(*bundle_, "");
}
void BundleDataSource::foreach_default_column_ids(
FunctionRef<void(const SpreadsheetColumnID &, bool is_extra)> fn) const
{
if (bundle_->is_empty()) {
return;
}
for (const char *name : {"Identifier", "Type", "Value"}) {
SpreadsheetColumnID column_id{(char *)name};
fn(column_id, false);
}
}
std::unique_ptr<ColumnValues> BundleDataSource::get_column_values(
const SpreadsheetColumnID &column_id) const
{
if (STREQ(column_id.name, "Identifier")) {
return std::make_unique<ColumnValues>(IFACE_("Identifier"),
VArray<std::string>::from_span(flat_item_keys_));
}
if (STREQ(column_id.name, "Type")) {
return std::make_unique<ColumnValues>(
IFACE_("Type"),
VArray<std::string>::from_func(
flat_items_.size(), [items = flat_items_](int64_t index) -> std::string {
const nodes::BundleItemValue &value = *items[index];
if (const auto *socket_value = std::get_if<nodes::BundleItemSocketValue>(
&value.value)) {
const bke::SocketValueVariant &value_variant = socket_value->value;
const StringRef type_name = IFACE_(socket_value->type->label);
if (value_variant.is_single()) {
return type_name;
}
if (value_variant.is_context_dependent_field()) {
return fmt::format("{} {}", type_name, IFACE_("Field"));
}
if (value_variant.is_volume_grid()) {
return fmt::format("{} {}", type_name, IFACE_("Grid"));
}
if (value_variant.is_list()) {
return fmt::format("{} {}", type_name, IFACE_("List"));
}
return type_name;
}
if (const auto *internal_value = std::get_if<nodes::BundleItemInternalValue>(
&value.value)) {
return internal_value->value->type_name();
}
return "";
}));
}
if (STREQ(column_id.name, "Value")) {
return std::make_unique<ColumnValues>(
IFACE_("Value"),
VArray<nodes::BundleItemValue>::from_func(
flat_items_.size(),
[items = flat_items_](const int64_t index) -> nodes::BundleItemValue {
return *items[index];
}));
}
return {};
}
int BundleDataSource::tot_rows() const
{
return flat_item_keys_.size();
}
void BundleDataSource::collect_flat_items(const nodes::Bundle &bundle, const StringRef parent_path)
{
const Span<nodes::Bundle::StoredItem> items = bundle.items();
for (const nodes::Bundle::StoredItem &item : items) {
const std::string path = parent_path.is_empty() ?
item.key :
nodes::Bundle::combine_path({parent_path, item.key});
flat_item_keys_.append(path);
flat_items_.append(&item.value);
if (const auto *value = std::get_if<nodes::BundleItemSocketValue>(&item.value.value)) {
if (value->value.is_single()) {
const GPointer ptr = value->value.get_single_ptr();
if (ptr.is_type<nodes::BundlePtr>()) {
const nodes::BundlePtr child_bundle = *ptr.get<nodes::BundlePtr>();
if (child_bundle) {
this->collect_flat_items(*child_bundle, path);
}
}
}
}
}
}
ClosureSignatureDataSource::ClosureSignatureDataSource(nodes::ClosurePtr closure,
const SpreadsheetClosureInputOutput in_out)
: closure_(std::move(closure)), in_out_(in_out)
{
}
void ClosureSignatureDataSource::foreach_default_column_ids(
FunctionRef<void(const SpreadsheetColumnID &, bool is_extra)> fn) const
{
Vector<StringRefNull> columns_names;
if (in_out_ == SPREADSHEET_CLOSURE_NONE) {
columns_names.append("Interface");
}
columns_names.extend({"Identifier", "Type"});
for (const StringRefNull name : columns_names) {
SpreadsheetColumnID column_id{(char *)name.c_str()};
fn(column_id, false);
}
}
std::unique_ptr<ColumnValues> ClosureSignatureDataSource::get_column_values(
const SpreadsheetColumnID &column_id) const
{
const Span<nodes::ClosureSignature::Item> input_items = closure_->signature().inputs;
const Span<nodes::ClosureSignature::Item> output_items = closure_->signature().outputs;
switch (in_out_) {
case SPREADSHEET_CLOSURE_NONE: {
const int items_sum = input_items.size() + output_items.size();
if (STREQ(column_id.name, "Identifier")) {
return std::make_unique<ColumnValues>(
IFACE_("Identifier"),
VArray<std::string>::from_func(items_sum,
[input_items, output_items](const int64_t index) {
if (index < input_items.size()) {
return input_items[index].key;
}
return output_items[index - input_items.size()].key;
}));
}
if (STREQ(column_id.name, "Type")) {
return std::make_unique<ColumnValues>(
IFACE_("Type"),
VArray<std::string>::from_func(
items_sum, [input_items, output_items](const int64_t index) {
if (index < input_items.size()) {
return input_items[index].type->label;
}
return output_items[index - input_items.size()].type->label;
}));
}
if (STREQ(column_id.name, "Interface")) {
return std::make_unique<ColumnValues>(
IFACE_("Interface"),
VArray<std::string>::from_func(items_sum,
[inputs_num = input_items.size()](const int64_t index) {
if (index < inputs_num) {
return IFACE_("Input");
}
return IFACE_("Output");
}));
}
break;
}
case SPREADSHEET_CLOSURE_INPUT:
case SPREADSHEET_CLOSURE_OUTPUT: {
const Span<nodes::ClosureSignature::Item> items = in_out_ == SPREADSHEET_CLOSURE_INPUT ?
input_items :
output_items;
if (STREQ(column_id.name, "Identifier")) {
return std::make_unique<ColumnValues>(
IFACE_("Identifier"),
VArray<std::string>::from_func(items.size(),
[items](int64_t index) { return items[index].key; }));
}
if (STREQ(column_id.name, "Type")) {
return std::make_unique<ColumnValues>(
IFACE_("Type"), VArray<std::string>::from_func(items.size(), [items](int64_t index) {
return items[index].type->label;
}));
}
break;
}
}
return {};
}
int ClosureSignatureDataSource::tot_rows() const
{
const int inputs_num = closure_->signature().inputs.size();
const int outputs_num = closure_->signature().outputs.size();
switch (in_out_) {
case SPREADSHEET_CLOSURE_NONE:
return inputs_num + outputs_num;
case SPREADSHEET_CLOSURE_INPUT:
return inputs_num;
case SPREADSHEET_CLOSURE_OUTPUT:
return outputs_num;
}
return 0;
}
SingleValueDataSource::SingleValueDataSource(const GPointer value)
: value_gvarray_(GVArray::from_single(*value.type(), 1, value.get()))
{
}
void SingleValueDataSource::foreach_default_column_ids(
FunctionRef<void(const SpreadsheetColumnID &, bool is_extra)> fn) const
{
for (const char *name : {"Value"}) {
SpreadsheetColumnID column_id{(char *)name};
fn(column_id, false);
}
}
std::unique_ptr<ColumnValues> SingleValueDataSource::get_column_values(
const SpreadsheetColumnID &column_id) const
{
if (STREQ(column_id.name, "Value")) {
return std::make_unique<ColumnValues>(IFACE_("Value"), value_gvarray_);
}
return {};
}
int SingleValueDataSource::tot_rows() const
{
return 1;
}
int get_instance_reference_icon(const bke::InstanceReference &reference)
{
switch (reference.type()) {
@@ -628,10 +897,16 @@ int get_instance_reference_icon(const bke::InstanceReference &reference)
return ICON_NONE;
}
bke::GeometrySet spreadsheet_get_display_geometry_set(const SpaceSpreadsheet *sspreadsheet,
Object *object_eval)
const nodes::geo_eval_log::ViewerNodeLog *viewer_node_log_lookup(
const SpaceSpreadsheet &sspreadsheet)
{
return nodes::geo_eval_log::GeoNodesLog::find_viewer_node_log_for_path(
sspreadsheet.geometry_id.viewer_path);
}
bke::SocketValueVariant geometry_display_data_get(const SpaceSpreadsheet *sspreadsheet,
Object *object_eval)
{
bke::GeometrySet geometry_set;
if (sspreadsheet->geometry_id.object_eval_state == SPREADSHEET_OBJECT_EVAL_STATE_ORIGINAL) {
const Object *object_orig = DEG_get_original(object_eval);
if (object_orig->type == OB_MESH) {
@@ -639,47 +914,109 @@ bke::GeometrySet spreadsheet_get_display_geometry_set(const SpaceSpreadsheet *ss
if (object_orig->mode == OB_MODE_EDIT) {
if (const BMEditMesh *em = mesh->runtime->edit_mesh.get()) {
Mesh *new_mesh = BKE_id_new_nomain<Mesh>(nullptr);
/* This is a potentially heavy operation to do on every redraw. The best solution here is
* to display the data directly from the bmesh without a conversion, which can be
/* This is a potentially heavy operation to do on every redraw. The best solution here
* is to display the data directly from the bmesh without a conversion, which can be
* implemented a bit later. */
BM_mesh_bm_to_me_for_eval(*em->bm, *new_mesh, nullptr);
geometry_set.replace_mesh(new_mesh, bke::GeometryOwnershipType::Owned);
return bke::SocketValueVariant::From(bke::GeometrySet::from_mesh(new_mesh));
}
}
else {
geometry_set.replace_mesh(const_cast<Mesh *>(mesh), bke::GeometryOwnershipType::ReadOnly);
return bke::SocketValueVariant::From(bke::GeometrySet::from_mesh(
const_cast<Mesh *>(mesh), bke::GeometryOwnershipType::ReadOnly));
}
}
else if (object_orig->type == OB_POINTCLOUD) {
const PointCloud *pointcloud = static_cast<const PointCloud *>(object_orig->data);
geometry_set.replace_pointcloud(const_cast<PointCloud *>(pointcloud),
bke::GeometryOwnershipType::ReadOnly);
return bke::SocketValueVariant::From(bke::GeometrySet::from_pointcloud(
const_cast<PointCloud *>(pointcloud), bke::GeometryOwnershipType::ReadOnly));
}
else if (object_orig->type == OB_CURVES) {
const Curves &curves_id = *static_cast<const Curves *>(object_orig->data);
geometry_set.replace_curves(&const_cast<Curves &>(curves_id),
bke::GeometryOwnershipType::ReadOnly);
return bke::SocketValueVariant::From(bke::GeometrySet::from_curves(
&const_cast<Curves &>(curves_id), bke::GeometryOwnershipType::ReadOnly));
}
else if (object_orig->type == OB_GREASE_PENCIL) {
const GreasePencil &grease_pencil = *static_cast<const GreasePencil *>(object_orig->data);
geometry_set.replace_grease_pencil(&const_cast<GreasePencil &>(grease_pencil),
bke::GeometryOwnershipType::ReadOnly);
return bke::SocketValueVariant::From(bke::GeometrySet::from_grease_pencil(
&const_cast<GreasePencil &>(grease_pencil), bke::GeometryOwnershipType::ReadOnly));
}
return {};
}
else {
if (BLI_listbase_is_single(&sspreadsheet->geometry_id.viewer_path.path)) {
geometry_set = bke::object_get_evaluated_geometry_set(*object_eval);
}
else {
if (const ViewerNodeLog *viewer_log =
nodes::geo_eval_log::GeoNodesLog::find_viewer_node_log_for_path(
sspreadsheet->geometry_id.viewer_path))
{
geometry_set = viewer_log->geometry;
if (BLI_listbase_is_single(&sspreadsheet->geometry_id.viewer_path.path)) {
return bke::SocketValueVariant::From(bke::object_get_evaluated_geometry_set(*object_eval));
}
const nodes::geo_eval_log::ViewerNodeLog *viewer_log =
nodes::geo_eval_log::GeoNodesLog::find_viewer_node_log_for_path(
sspreadsheet->geometry_id.viewer_path);
if (!viewer_log) {
return {};
}
const SpreadsheetTableIDGeometry &table_id = sspreadsheet->geometry_id;
const int item_index = viewer_log->items.index_of_try_as(table_id.viewer_item_identifier);
if (item_index == -1) {
return {};
}
bke::SocketValueVariant value = viewer_log->items[item_index].value;
/* Try to display the previous geometry instead of the value is a field (it will have been
* evaluated on that geometry). */
if (value.is_context_dependent_field()) {
for (int i = item_index - 1; i >= 0; i--) {
const bke::SocketValueVariant &prev_value = viewer_log->items[i].value;
if (!prev_value.is_single()) {
continue;
}
const GPointer ptr = prev_value.get_single_ptr();
if (!ptr.is_type<bke::GeometrySet>()) {
continue;
}
return prev_value;
}
return {};
}
return geometry_set;
for (const SpreadsheetBundlePathElem &bundle_path_elem :
Span(table_id.bundle_path, table_id.bundle_path_num))
{
if (!value.is_single()) {
return {};
}
const GPointer ptr = value.get_single_ptr();
if (!ptr.is_type<nodes::BundlePtr>()) {
return {};
}
const nodes::BundlePtr &bundle = *ptr.get<nodes::BundlePtr>();
const nodes::BundleItemValue *item = bundle->lookup(bundle_path_elem.identifier);
if (!item) {
return {};
}
const auto *stored_value = std::get_if<nodes::BundleItemSocketValue>(&item->value);
if (!stored_value) {
return {};
}
value = stored_value->value;
}
return value;
}
std::optional<bke::GeometrySet> root_geometry_set_get(const SpaceSpreadsheet *sspreadsheet,
Object *object_eval)
{
bke::SocketValueVariant display_data = geometry_display_data_get(sspreadsheet, object_eval);
if (!display_data.is_single()) {
return std::nullopt;
}
const GPointer ptr = display_data.get_single_ptr();
if (!ptr.is_type<bke::GeometrySet>()) {
return std::nullopt;
}
return display_data.extract<bke::GeometrySet>();
}
bke::GeometrySet get_geometry_set_for_instance_ids(const bke::GeometrySet &root_geometry,
@@ -709,33 +1046,69 @@ std::unique_ptr<DataSource> data_source_from_geometry(const bContext *C, Object
{
SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
const bke::GeometrySet root_geometry_set = spreadsheet_get_display_geometry_set(sspreadsheet,
object_eval);
const bke::GeometrySet geometry_set = get_geometry_set_for_instance_ids(
root_geometry_set,
Span{sspreadsheet->geometry_id.instance_ids, sspreadsheet->geometry_id.instance_ids_num});
const bke::AttrDomain domain = (bke::AttrDomain)sspreadsheet->geometry_id.attribute_domain;
const auto component_type = bke::GeometryComponent::Type(
sspreadsheet->geometry_id.geometry_component_type);
const int layer_index = sspreadsheet->geometry_id.layer_index;
if (!geometry_set.has(component_type)) {
bke::SocketValueVariant display_data = geometry_display_data_get(sspreadsheet, object_eval);
if (display_data.is_context_dependent_field()) {
return {};
}
if (component_type == bke::GeometryComponent::Type::Volume) {
return std::make_unique<VolumeDataSource>(std::move(geometry_set));
if (display_data.is_volume_grid()) {
return {};
}
Object *object_orig = sspreadsheet->geometry_id.instance_ids_num == 0 ?
DEG_get_original(object_eval) :
nullptr;
return std::make_unique<GeometryDataSource>(object_orig,
std::move(geometry_set),
component_type,
domain,
sspreadsheet->flag &
SPREADSHEET_FLAG_SHOW_INTERNAL_ATTRIBUTES,
layer_index);
if (display_data.is_list()) {
return std::make_unique<ListDataSource>(display_data.extract<nodes::ListPtr>());
}
if (!display_data.is_single()) {
return {};
}
const GPointer ptr = display_data.get_single_ptr();
if (ptr.is_type<bke::GeometrySet>()) {
const bke::GeometrySet root_geometry_set = display_data.extract<bke::GeometrySet>();
const bke::GeometrySet geometry_set = get_geometry_set_for_instance_ids(
root_geometry_set,
Span{sspreadsheet->geometry_id.instance_ids, sspreadsheet->geometry_id.instance_ids_num});
const bke::AttrDomain domain = (bke::AttrDomain)sspreadsheet->geometry_id.attribute_domain;
const auto component_type = bke::GeometryComponent::Type(
sspreadsheet->geometry_id.geometry_component_type);
const int layer_index = sspreadsheet->geometry_id.layer_index;
if (!geometry_set.has(component_type)) {
return {};
}
if (component_type == bke::GeometryComponent::Type::Volume) {
return std::make_unique<VolumeDataSource>(std::move(geometry_set));
}
Object *object_orig = sspreadsheet->geometry_id.instance_ids_num == 0 ?
DEG_get_original(object_eval) :
nullptr;
return std::make_unique<GeometryDataSource>(object_orig,
std::move(geometry_set),
component_type,
domain,
sspreadsheet->flag &
SPREADSHEET_FLAG_SHOW_INTERNAL_ATTRIBUTES,
layer_index);
}
if (ptr.is_type<nodes::BundlePtr>()) {
const nodes::BundlePtr bundle_ptr = display_data.extract<nodes::BundlePtr>();
if (bundle_ptr) {
return std::make_unique<BundleDataSource>(bundle_ptr);
}
return {};
}
if (ptr.is_type<nodes::ClosurePtr>()) {
const auto in_out = SpreadsheetClosureInputOutput(
sspreadsheet->geometry_id.closure_input_output);
const nodes::ClosurePtr closure_ptr = display_data.extract<nodes::ClosurePtr>();
if (closure_ptr) {
return std::make_unique<ClosureSignatureDataSource>(closure_ptr, in_out);
}
return {};
}
const eSpreadsheetColumnValueType column_type = cpp_type_to_column_type(*ptr.type());
if (column_type == SPREADSHEET_VALUE_TYPE_UNKNOWN) {
return {};
}
return std::make_unique<SingleValueDataSource>(ptr);
}
} // namespace blender::ed::spreadsheet

View File

@@ -11,6 +11,10 @@
#include "BKE_geometry_set.hh"
#include "BKE_instances.hh"
#include "NOD_geometry_nodes_bundle_fwd.hh"
#include "NOD_geometry_nodes_closure_fwd.hh"
#include "NOD_geometry_nodes_list_fwd.hh"
#include "spreadsheet_data_source.hh"
struct bContext;
@@ -89,6 +93,72 @@ class VolumeDataSource : public DataSource {
int tot_rows() const override;
};
class ListDataSource : public DataSource {
nodes::ListPtr list_;
public:
ListDataSource(nodes::ListPtr list);
void foreach_default_column_ids(
FunctionRef<void(const SpreadsheetColumnID &, bool is_extra)> fn) const override;
std::unique_ptr<ColumnValues> get_column_values(
const SpreadsheetColumnID &column_id) const override;
int tot_rows() const override;
};
class BundleDataSource : public DataSource {
nodes::BundlePtr bundle_;
Vector<std::string> flat_item_keys_;
Vector<const nodes::BundleItemValue *> flat_items_;
public:
BundleDataSource(nodes::BundlePtr bundle);
void foreach_default_column_ids(
FunctionRef<void(const SpreadsheetColumnID &, bool is_extra)> fn) const override;
std::unique_ptr<ColumnValues> get_column_values(
const SpreadsheetColumnID &column_id) const override;
int tot_rows() const override;
private:
void collect_flat_items(const nodes::Bundle &bundle, StringRef parent_path);
};
class ClosureSignatureDataSource : public DataSource {
nodes::ClosurePtr closure_;
SpreadsheetClosureInputOutput in_out_;
public:
ClosureSignatureDataSource(nodes::ClosurePtr closure, SpreadsheetClosureInputOutput in_out);
void foreach_default_column_ids(
FunctionRef<void(const SpreadsheetColumnID &, bool is_extra)> fn) const override;
std::unique_ptr<ColumnValues> get_column_values(
const SpreadsheetColumnID &column_id) const override;
int tot_rows() const override;
};
class SingleValueDataSource : public DataSource {
GVArray value_gvarray_;
public:
SingleValueDataSource(const GPointer value);
void foreach_default_column_ids(
FunctionRef<void(const SpreadsheetColumnID &, bool is_extra)> fn) const override;
std::unique_ptr<ColumnValues> get_column_values(
const SpreadsheetColumnID &column_id) const override;
int tot_rows() const override;
};
int get_instance_reference_icon(const bke::InstanceReference &reference);
std::unique_ptr<DataSource> data_source_from_geometry(const bContext *C, Object *object_eval);

View File

@@ -34,6 +34,10 @@
#include "ED_outliner.hh"
#include "ED_spreadsheet.hh"
#include "NOD_geometry_nodes_bundle.hh"
#include "NOD_geometry_nodes_closure.hh"
#include "NOD_geometry_nodes_log.hh"
#include "spreadsheet_data_source_geometry.hh"
#include "spreadsheet_dataset_draw.hh"
#include "spreadsheet_intern.hh"
@@ -997,6 +1001,207 @@ std::optional<bool> ViewerPathTreeViewItem::should_be_active() const
return false;
}
class ViewerDataTreeItem : public ui::AbstractTreeViewItem {
public:
Vector<const ViewerDataTreeItem *> items_path() const
{
Vector<const ViewerDataTreeItem *> items;
this->foreach_parent([&](const ui::AbstractTreeViewItem &parent) {
items.append(dynamic_cast<const ViewerDataTreeItem *>(&parent));
});
items.as_mutable_span().reverse();
items.append(this);
return items;
}
void on_activate(bContext &C) override;
std::optional<bool> should_be_active() const override;
};
struct ViewerDataPath {
int viewer_item;
Vector<StringRef> bundles;
SpreadsheetClosureInputOutput closure_input_output = SPREADSHEET_CLOSURE_NONE;
BLI_STRUCT_EQUALITY_OPERATORS_3(ViewerDataPath, viewer_item, bundles, closure_input_output);
ViewerDataPath() = default;
explicit ViewerDataPath(const SpreadsheetTableIDGeometry &table_id)
: viewer_item(table_id.viewer_item_identifier)
{
for (const auto &elem : Span(table_id.bundle_path, table_id.bundle_path_num)) {
this->bundles.append(elem.identifier);
}
this->closure_input_output = SpreadsheetClosureInputOutput(table_id.closure_input_output);
}
explicit ViewerDataPath(const Span<const ViewerDataTreeItem *> tree_items);
void store(SpreadsheetTableIDGeometry &table_id)
{
table_id.viewer_item_identifier = this->viewer_item;
if (table_id.bundle_path) {
for (const int i : IndexRange(table_id.bundle_path_num)) {
MEM_freeN(table_id.bundle_path[i].identifier);
}
MEM_freeN(table_id.bundle_path);
}
table_id.bundle_path = MEM_calloc_arrayN<SpreadsheetBundlePathElem>(this->bundles.size(),
__func__);
table_id.bundle_path_num = this->bundles.size();
for (const int i : this->bundles.index_range()) {
table_id.bundle_path[i].identifier = BLI_strdupn(this->bundles[i].data(),
this->bundles[i].size());
}
table_id.closure_input_output = int8_t(this->closure_input_output);
}
};
class ViewerNodeItem : public ViewerDataTreeItem {
private:
const nodes::geo_eval_log::ViewerNodeLog::Item &item_;
friend ViewerDataPath;
public:
ViewerNodeItem(const nodes::geo_eval_log::ViewerNodeLog::Item &item) : item_(item)
{
label_ = std::to_string(item.identifier);
}
void build_row(uiLayout &row) override
{
row.label(item_.name, ICON_NONE);
}
};
class BundleItem : public ViewerDataTreeItem {
friend ViewerDataPath;
public:
BundleItem(const StringRef key)
{
label_ = key;
}
void build_row(uiLayout &row) override
{
row.label(label_, ICON_NONE);
}
};
class ClosureInputOutputItem : public ViewerDataTreeItem {
private:
SpreadsheetClosureInputOutput in_out_;
friend ViewerDataPath;
public:
ClosureInputOutputItem(const SpreadsheetClosureInputOutput in_out) : in_out_(in_out)
{
label_ = in_out_ == SPREADSHEET_CLOSURE_INPUT ? IFACE_("Inputs") : IFACE_("Outputs");
}
void build_row(uiLayout &row) override
{
row.label(label_, ICON_NONE);
}
};
ViewerDataPath::ViewerDataPath(const Span<const ViewerDataTreeItem *> tree_items)
{
for (const ViewerDataTreeItem *item : tree_items) {
if (const auto *viewer_node_item = dynamic_cast<const ViewerNodeItem *>(item)) {
this->viewer_item = viewer_node_item->item_.identifier;
}
else if (const auto *bundle_item = dynamic_cast<const BundleItem *>(item)) {
this->bundles.append(bundle_item->label_);
}
else if (const auto *bundle_item = dynamic_cast<const ClosureInputOutputItem *>(item)) {
this->closure_input_output = bundle_item->in_out_;
}
}
}
class ViewerDataTreeView : public ui::AbstractTreeView {
private:
SpaceSpreadsheet &sspreadsheet_;
friend ViewerDataTreeItem;
public:
ViewerDataTreeView(const bContext &C) : sspreadsheet_(*CTX_wm_space_spreadsheet(&C)) {}
void build_tree() override
{
const nodes::geo_eval_log::ViewerNodeLog *log = viewer_node_log_lookup(sspreadsheet_);
for (const nodes::geo_eval_log::ViewerNodeLog::Item &item : log->items) {
const bke::SocketValueVariant &value = item.value;
auto &child_item = this->add_tree_item<ViewerNodeItem>(item);
this->build_value(child_item, value);
}
}
void build_value(ui::AbstractTreeViewItem &parent, const bke::SocketValueVariant &value)
{
if (!value.is_single()) {
return;
}
const GPointer single_value = value.get_single_ptr();
if (single_value.is_type<nodes::BundlePtr>()) {
const nodes::BundlePtr &bundle_ptr = *single_value.get<nodes::BundlePtr>();
if (bundle_ptr) {
this->build_bundle_children(parent, *bundle_ptr);
}
}
if (single_value.is_type<nodes::ClosurePtr>()) {
const nodes::ClosurePtr &closure_ptr = *single_value.get<nodes::ClosurePtr>();
if (closure_ptr) {
this->build_closure_children(parent, closure_ptr);
}
}
}
void build_bundle_children(ui::AbstractTreeViewItem &parent, const nodes::Bundle &bundle)
{
for (const nodes::Bundle::StoredItem &item : bundle.items()) {
auto &child_item = parent.add_tree_item<BundleItem>(item.key);
const auto *stored_value = std::get_if<nodes::BundleItemSocketValue>(&item.value.value);
if (!stored_value) {
continue;
}
this->build_value(child_item, stored_value->value);
}
}
void build_closure_children(ui::AbstractTreeViewItem &parent, const nodes::ClosurePtr &closure)
{
const nodes::ClosureSignature &signature = closure->signature();
if (!signature.inputs.is_empty()) {
parent.add_tree_item<ClosureInputOutputItem>(SPREADSHEET_CLOSURE_INPUT);
}
if (!signature.outputs.is_empty()) {
parent.add_tree_item<ClosureInputOutputItem>(SPREADSHEET_CLOSURE_OUTPUT);
}
}
};
void ViewerDataTreeItem::on_activate(bContext & /*C*/)
{
const auto &tree = static_cast<const ViewerDataTreeView &>(this->get_tree_view());
SpaceSpreadsheet &sspreadsheet = tree.sspreadsheet_;
SpreadsheetTableIDGeometry &table_id = sspreadsheet.geometry_id;
ViewerDataPath(this->items_path()).store(table_id);
WM_main_add_notifier(NC_SPACE | ND_SPACE_SPREADSHEET, nullptr);
}
std::optional<bool> ViewerDataTreeItem::should_be_active() const
{
const auto &tree = static_cast<const ViewerDataTreeView &>(this->get_tree_view());
const SpaceSpreadsheet &sspreadsheet = tree.sspreadsheet_;
const SpreadsheetTableIDGeometry &table_id = sspreadsheet.geometry_id;
return ViewerDataPath(table_id) == ViewerDataPath(this->items_path());
}
static void draw_context_panel_without_context(uiLayout &layout)
{
layout.label(IFACE_("No Active Context"), ICON_NONE);
@@ -1020,6 +1225,15 @@ static void draw_viewer_path_panel(const bContext &C, uiLayout &layout)
ui::TreeViewBuilder::build_tree_view(C, *tree_view, layout, {}, true);
}
static void draw_viewer_data_panel(const bContext &C, uiLayout &layout)
{
uiBlock *block = layout.block();
ui::AbstractTreeView *tree_view = UI_block_add_view(
*block, "Viewer Data", std::make_unique<ViewerDataTreeView>(C));
tree_view->set_context_menu_title("Viewer Data");
ui::TreeViewBuilder::build_tree_view(C, *tree_view, layout, {}, false);
}
static void draw_context_panel_content(const bContext &C, uiLayout &layout)
{
bScreen &screen = *CTX_wm_screen(&C);
@@ -1047,6 +1261,9 @@ static void draw_context_panel_content(const bContext &C, uiLayout &layout)
if (uiLayout *panel = layout.panel(&C, "viewer path", true, IFACE_("Viewer Path"))) {
draw_viewer_path_panel(C, *panel);
}
if (uiLayout *panel = layout.panel(&C, "viewer data", true, IFACE_("Viewer Data"))) {
draw_viewer_data_panel(C, *panel);
}
}
}
@@ -1100,27 +1317,28 @@ void spreadsheet_data_set_panel_draw(const bContext *C, Panel *panel)
return;
}
const bke::GeometrySet root_geometry = spreadsheet_get_display_geometry_set(sspreadsheet,
object);
if (uiLayout *panel = layout->panel(C, "instance tree", false, IFACE_("Geometry"))) {
ui::AbstractTreeView *tree_view = UI_block_add_view(
*block,
"Instances Tree View",
std::make_unique<GeometryInstancesTreeView>(root_geometry, *C));
tree_view->set_context_menu_title("Instance");
ui::TreeViewBuilder::build_tree_view(*C, *tree_view, *panel, {}, false);
}
if (uiLayout *panel = layout->panel(C, "geometry_domain_tree_view", false, IFACE_("Domain"))) {
bke::GeometrySet instance_geometry = get_geometry_set_for_instance_ids(
root_geometry,
{sspreadsheet->geometry_id.instance_ids, sspreadsheet->geometry_id.instance_ids_num});
ui::AbstractTreeView *tree_view = UI_block_add_view(
*block,
"Data Set Tree View",
std::make_unique<GeometryDataSetTreeView>(std::move(instance_geometry), *C));
tree_view->set_context_menu_title("Domain");
ui::TreeViewBuilder::build_tree_view(*C, *tree_view, *panel, {}, false);
if (const std::optional<bke::GeometrySet> root_geometry = root_geometry_set_get(sspreadsheet,
object))
{
if (uiLayout *panel = layout->panel(C, "instance tree", false, IFACE_("Geometry"))) {
ui::AbstractTreeView *tree_view = UI_block_add_view(
*block,
"Instances Tree View",
std::make_unique<GeometryInstancesTreeView>(*root_geometry, *C));
tree_view->set_context_menu_title("Instance");
ui::TreeViewBuilder::build_tree_view(*C, *tree_view, *panel, {}, false);
}
if (uiLayout *panel = layout->panel(C, "geometry_domain_tree_view", false, IFACE_("Domain"))) {
bke::GeometrySet instance_geometry = get_geometry_set_for_instance_ids(
*root_geometry,
{sspreadsheet->geometry_id.instance_ids, sspreadsheet->geometry_id.instance_ids_num});
ui::AbstractTreeView *tree_view = UI_block_add_view(
*block,
"Data Set Tree View",
std::make_unique<GeometryDataSetTreeView>(std::move(instance_geometry), *C));
tree_view->set_context_menu_title("Domain");
ui::TreeViewBuilder::build_tree_view(*C, *tree_view, *panel, {}, false);
}
}
}

View File

@@ -6,6 +6,7 @@
#include "BKE_geometry_set.hh"
#include "BKE_node_socket_value.hh"
#include "DNA_space_types.h"
struct ARegionType;
@@ -15,6 +16,12 @@ struct SpaceSpreadsheet;
struct ARegion;
struct SpreadsheetColumn;
struct bContext;
namespace blender::nodes {
class Bundle;
}
namespace blender::nodes::geo_eval_log {
class ViewerNodeLog;
}
#define SPREADSHEET_EDGE_ACTION_ZONE (UI_UNIT_X * 0.3f)
@@ -50,7 +57,12 @@ void spreadsheet_operatortypes();
Object *spreadsheet_get_object_eval(const SpaceSpreadsheet *sspreadsheet,
const Depsgraph *depsgraph);
bke::GeometrySet spreadsheet_get_display_geometry_set(const SpaceSpreadsheet *sspreadsheet,
const nodes::geo_eval_log::ViewerNodeLog *viewer_node_log_lookup(
const SpaceSpreadsheet &sspreadsheet);
bke::SocketValueVariant geometry_display_data_get(const SpaceSpreadsheet *sspreadsheet,
Object *object_eval);
std::optional<bke::GeometrySet> root_geometry_set_get(const SpaceSpreadsheet *sspreadsheet,
Object *object_eval);
void spreadsheet_data_set_region_panels_register(ARegionType &region_type);

View File

@@ -16,6 +16,8 @@
#include "BKE_instances.hh"
#include "NOD_geometry_nodes_bundle.hh"
#include "spreadsheet_column_values.hh"
#include "spreadsheet_data_source_geometry.hh"
#include "spreadsheet_layout.hh"
@@ -27,6 +29,10 @@
#include "BLT_translation.hh"
/* Need to do our own padding in some cases because we use low-level ui code to draw the
* spreadsheet. */
#define CELL_PADDING_X (0.15f * SPREADSHEET_WIDTH_UNIT)
namespace blender::ed::spreadsheet {
class SpreadsheetLayoutDrawer : public SpreadsheetDrawer {
@@ -259,9 +265,9 @@ class SpreadsheetLayoutDrawer : public SpreadsheetDrawer {
0,
ICON_NONE,
*value_ptr.get<std::string>(),
params.xmin,
params.xmin + CELL_PADDING_X,
params.ymin,
params.width,
params.width - 2.0f * CELL_PADDING_X,
params.height,
nullptr,
std::nullopt);
@@ -275,9 +281,9 @@ class SpreadsheetLayoutDrawer : public SpreadsheetDrawer {
0,
ICON_NONE,
StringRef(prop->s, prop->s_len),
params.xmin,
params.xmin + CELL_PADDING_X,
params.ymin,
params.width,
params.width - 2.0f * CELL_PADDING_X,
params.height,
nullptr,
std::nullopt);
@@ -292,6 +298,22 @@ class SpreadsheetLayoutDrawer : public SpreadsheetDrawer {
MEM_freeN);
return;
}
if (type.is<nodes::BundleItemValue>()) {
const nodes::BundleItemValue &value = *value_ptr.get<nodes::BundleItemValue>();
if (const nodes::BundleItemSocketValue *socket_value =
std::get_if<nodes::BundleItemSocketValue>(&value.value))
{
const bke::SocketValueVariant &value_variant = socket_value->value;
if (value_variant.is_single()) {
const GPointer single_value_ptr = value_variant.get_single_ptr();
this->draw_content_cell_value(single_value_ptr, params);
return;
}
}
this->draw_undrawable(params);
return;
}
this->draw_undrawable(params);
}
void draw_float_vector(const CellDrawParams &params, const Span<float> values) const
@@ -405,17 +427,7 @@ class SpreadsheetLayoutDrawer : public SpreadsheetDrawer {
void draw_float4x4(const CellDrawParams &params, const float4x4 &value) const
{
uiBut *but = uiDefIconTextBut(params.block,
ButType::Label,
0,
ICON_NONE,
"...",
params.xmin,
params.ymin,
params.width,
params.height,
nullptr,
std::nullopt);
uiBut *but = this->draw_undrawable(params);
/* Center alignment. */
UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT);
UI_but_func_tooltip_set(
@@ -434,6 +446,24 @@ class SpreadsheetLayoutDrawer : public SpreadsheetDrawer {
MEM_freeN);
}
uiBut *draw_undrawable(const CellDrawParams &params) const
{
uiBut *but = uiDefIconTextBut(params.block,
ButType::Label,
0,
ICON_NONE,
"...",
params.xmin,
params.ymin,
params.width,
params.height,
nullptr,
std::nullopt);
/* Center alignment. */
UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT);
return but;
}
int column_width(int column_index) const final
{
return spreadsheet_layout_.columns[column_index].width;
@@ -591,6 +621,9 @@ float ColumnValues::fit_column_values_width_px(const std::optional<int64_t> &max
}
break;
}
case SPREADSHEET_VALUE_TYPE_BUNDLE_ITEM: {
return 12 * SPREADSHEET_WIDTH_UNIT;
}
case SPREADSHEET_VALUE_TYPE_UNKNOWN: {
break;
}

View File

@@ -111,6 +111,7 @@ static std::string value_string(const SpreadsheetRowFilter &row_filter,
return row_filter.value_string;
case SPREADSHEET_VALUE_TYPE_QUATERNION:
case SPREADSHEET_VALUE_TYPE_FLOAT4X4:
case SPREADSHEET_VALUE_TYPE_BUNDLE_ITEM:
case SPREADSHEET_VALUE_TYPE_UNKNOWN:
return "";
}
@@ -260,6 +261,7 @@ static void spreadsheet_filter_panel_draw(const bContext *C, Panel *panel)
case SPREADSHEET_VALUE_TYPE_UNKNOWN:
case SPREADSHEET_VALUE_TYPE_QUATERNION:
case SPREADSHEET_VALUE_TYPE_FLOAT4X4:
case SPREADSHEET_VALUE_TYPE_BUNDLE_ITEM:
layout->label(IFACE_("Unsupported column type"), ICON_ERROR);
break;
}

View File

@@ -6,6 +6,7 @@
#include "BLI_hash.hh"
#include "BLI_listbase.h"
#include "BLI_string.h"
#include "DNA_array_utils.hh"
@@ -33,6 +34,11 @@ void spreadsheet_table_id_copy_content_geometry(SpreadsheetTableIDGeometry &dst,
dst.layer_index = src.layer_index;
dst.instance_ids = static_cast<SpreadsheetInstanceID *>(MEM_dupallocN(src.instance_ids));
dst.instance_ids_num = src.instance_ids_num;
dst.bundle_path = MEM_calloc_arrayN<SpreadsheetBundlePathElem>(src.bundle_path_num, __func__);
for (const int i : IndexRange(src.bundle_path_num)) {
dst.bundle_path[i].identifier = BLI_strdup_null(src.bundle_path[i].identifier);
}
dst.bundle_path_num = src.bundle_path_num;
}
SpreadsheetTableID *spreadsheet_table_id_copy(const SpreadsheetTableID &src_table_id)
@@ -55,6 +61,10 @@ void spreadsheet_table_id_free_content(SpreadsheetTableID *table_id)
auto *table_id_ = reinterpret_cast<SpreadsheetTableIDGeometry *>(table_id);
BKE_viewer_path_clear(&table_id_->viewer_path);
MEM_SAFE_FREE(table_id_->instance_ids);
for (const int i : IndexRange(table_id_->bundle_path_num)) {
MEM_SAFE_FREE(table_id_->bundle_path[i].identifier);
}
MEM_SAFE_FREE(table_id_->bundle_path);
break;
}
}
@@ -72,6 +82,11 @@ void spreadsheet_table_id_blend_write_content_geometry(BlendWriter *writer,
BKE_viewer_path_blend_write(writer, &table_id->viewer_path);
BLO_write_struct_array(
writer, SpreadsheetInstanceID, table_id->instance_ids_num, table_id->instance_ids);
BLO_write_struct_array(
writer, SpreadsheetBundlePathElem, table_id->bundle_path_num, table_id->bundle_path);
for (const int i : IndexRange(table_id->bundle_path_num)) {
BLO_write_string(writer, table_id->bundle_path[i].identifier);
}
}
void spreadsheet_table_id_blend_write(BlendWriter *writer, const SpreadsheetTableID *table_id)
@@ -94,6 +109,11 @@ void spreadsheet_table_id_blend_read(BlendDataReader *reader, SpreadsheetTableID
BKE_viewer_path_blend_read_data(reader, &table_id_->viewer_path);
BLO_read_struct_array(
reader, SpreadsheetInstanceID, table_id_->instance_ids_num, &table_id_->instance_ids);
BLO_read_struct_array(
reader, SpreadsheetBundlePathElem, table_id_->bundle_path_num, &table_id_->bundle_path);
for (const int i : IndexRange(table_id_->bundle_path_num)) {
BLO_read_string(reader, &table_id_->bundle_path[i].identifier);
}
break;
}
}
@@ -138,7 +158,9 @@ bool spreadsheet_table_id_match(const SpreadsheetTableID &a, const SpreadsheetTa
a_.attribute_domain == b_.attribute_domain &&
a_.object_eval_state == b_.object_eval_state && a_.layer_index == b_.layer_index &&
blender::Span(a_.instance_ids, a_.instance_ids_num) ==
blender::Span(b_.instance_ids, b_.instance_ids_num);
blender::Span(b_.instance_ids, b_.instance_ids_num) &&
blender::Span(a_.bundle_path, a_.bundle_path_num) ==
blender::Span(b_.bundle_path, b_.bundle_path_num);
}
}
return true;

View File

@@ -888,7 +888,9 @@ static bke::GeometrySet find_geometry_for_gizmo(const Object &object_eval,
if (const geo_eval_log::ViewerNodeLog *viewer_log =
nmd_orig.runtime->eval_log->find_viewer_node_log_for_path(viewer_path))
{
return viewer_log->geometry;
if (std::optional<bke::GeometrySet> viewer_geometry = viewer_log->main_geometry()) {
return *viewer_geometry;
}
}
}
return bke::object_get_evaluated_geometry_set(object_eval);

View File

@@ -132,7 +132,10 @@ static void viewer_path_for_geometry_node(const SpaceNode &snode,
BLI_addtail(&r_dst.path, viewer_node_elem);
}
void activate_geometry_node(Main &bmain, SpaceNode &snode, bNode &node)
void activate_geometry_node(Main &bmain,
SpaceNode &snode,
bNode &node,
std::optional<int> item_identifier)
{
wmWindowManager *wm = (wmWindowManager *)bmain.wm.first;
if (wm == nullptr) {
@@ -159,7 +162,14 @@ void activate_geometry_node(Main &bmain, SpaceNode &snode, bNode &node)
if (sl->spacetype == SPACE_SPREADSHEET) {
SpaceSpreadsheet &sspreadsheet = *reinterpret_cast<SpaceSpreadsheet *>(sl);
if (!(sspreadsheet.flag & SPREADSHEET_FLAG_PINNED)) {
sspreadsheet.geometry_id.object_eval_state = SPREADSHEET_OBJECT_EVAL_STATE_VIEWER_NODE;
SpreadsheetTableIDGeometry &table_id = sspreadsheet.geometry_id;
table_id.object_eval_state = SPREADSHEET_OBJECT_EVAL_STATE_VIEWER_NODE;
if (item_identifier) {
table_id.viewer_item_identifier = *item_identifier;
}
MEM_SAFE_FREE(table_id.bundle_path);
table_id.bundle_path_num = 0;
table_id.closure_input_output = SPREADSHEET_CLOSURE_NONE;
}
}
else if (sl->spacetype == SPACE_VIEW3D) {

View File

@@ -2104,11 +2104,43 @@ typedef struct NodeGeometryImageTexture {
int8_t extension;
} NodeGeometryImageTexture;
typedef enum NodeGeometryViewerItemFlag {
/**
* Automatically remove the viewer item when there is no link connected to it. This simplifies
* working with viewers when one adds and removes values to view all the time.
*
* This is a flag instead of always being used, because sometimes the user or some script sets up
* multiple inputs which shouldn't be deleted immediately. This flag is automatically set when
* viewer items are added interactively in the node editor.
*/
NODE_GEO_VIEWER_ITEM_FLAG_AUTO_REMOVE = (1 << 0),
} NodeGeometryViewerItemFlag;
typedef struct NodeGeometryViewerItem {
char *name;
/** #eNodeSocketDatatype. */
short socket_type;
uint8_t flag;
char _pad[1];
/**
* Generated unique identifier for sockets which stays the same even when the item order or
* names change.
*/
int identifier;
} NodeGeometryViewerItem;
typedef struct NodeGeometryViewer {
NodeGeometryViewerItem *items;
int items_num;
int active_index;
int next_identifier;
/** #eCustomDataType. */
int8_t data_type;
int8_t data_type_legacy;
/** #AttrDomain. */
int8_t domain;
char _pad[2];
} NodeGeometryViewer;
typedef struct NodeGeometryUVUnwrap {

View File

@@ -1025,6 +1025,7 @@ typedef enum eSpreadsheetColumnValueType {
SPREADSHEET_VALUE_TYPE_INT32_2D = 10,
SPREADSHEET_VALUE_TYPE_QUATERNION = 11,
SPREADSHEET_VALUE_TYPE_FLOAT4X4 = 12,
SPREADSHEET_VALUE_TYPE_BUNDLE_ITEM = 13,
} eSpreadsheetColumnValueType;
typedef enum eSpreadsheetColumnFlag {

View File

@@ -1137,6 +1137,20 @@ typedef struct SpreadsheetTableID {
int type;
} SpreadsheetTableID;
typedef struct SpreadsheetBundlePathElem {
char *identifier;
#ifdef __cplusplus
friend bool operator==(const SpreadsheetBundlePathElem &a, const SpreadsheetBundlePathElem &b);
friend bool operator!=(const SpreadsheetBundlePathElem &a, const SpreadsheetBundlePathElem &b);
#endif
} SpreadsheetBundlePathElem;
typedef enum SpreadsheetClosureInputOutput {
SPREADSHEET_CLOSURE_NONE = 0,
SPREADSHEET_CLOSURE_INPUT = 1,
SPREADSHEET_CLOSURE_OUTPUT = 2,
} SpreadsheetClosureInputOutput;
typedef struct SpreadsheetTableIDGeometry {
SpreadsheetTableID base;
char _pad0[4];
@@ -1146,6 +1160,17 @@ typedef struct SpreadsheetTableIDGeometry {
* can be pinned so that it stays constant even when the active node changes.
*/
ViewerPath viewer_path;
int viewer_item_identifier;
int bundle_path_num;
SpreadsheetBundlePathElem *bundle_path;
/** #SpreadsheetClosureInputOutput. */
int8_t closure_input_output;
char _pad3[7];
/**
* The "path" to the currently active instance reference. This is needed when viewing nested
* instances.

View File

@@ -176,6 +176,7 @@ DNA_STRUCT_RENAME_MEMBER(NodeCryptomatte, num_inputs, inputs_num)
DNA_STRUCT_RENAME_MEMBER(NodeCompositorFileOutput, base_path, directory)
DNA_STRUCT_RENAME_MEMBER(NodeCompositorFileOutput, active_input, active_item_index)
DNA_STRUCT_RENAME_MEMBER(NodeGeometryAttributeCapture, data_type, data_type_legacy)
DNA_STRUCT_RENAME_MEMBER(NodeGeometryViewer, data_type, data_type_legacy)
DNA_STRUCT_RENAME_MEMBER(NodeTexSky, dust_density, aerosol_density)
DNA_STRUCT_RENAME_MEMBER(NodesModifierData, simulation_bake_directory, bake_directory)
DNA_STRUCT_RENAME_MEMBER(Object, col, color)

View File

@@ -656,6 +656,7 @@ static const EnumPropertyItem node_cryptomatte_layer_name_items[] = {
# include "NOD_geo_menu_switch.hh"
# include "NOD_geo_repeat.hh"
# include "NOD_geo_simulation.hh"
# include "NOD_geo_viewer.hh"
# include "NOD_geometry.hh"
# include "NOD_geometry_nodes_lazy_function.hh"
# include "NOD_shader.h"
@@ -687,6 +688,7 @@ using blender::nodes::ForeachGeometryElementGenerationItemsAccessor;
using blender::nodes::ForeachGeometryElementInputItemsAccessor;
using blender::nodes::ForeachGeometryElementMainItemsAccessor;
using blender::nodes::FormatStringItemsAccessor;
using blender::nodes::GeoViewerItemsAccessor;
using blender::nodes::IndexSwitchItemsAccessor;
using blender::nodes::MenuSwitchItemsAccessor;
using blender::nodes::RepeatItemsAccessor;
@@ -1401,12 +1403,6 @@ void rna_Node_Viewer_shortcut_node_set(PointerRNA *ptr, PropertyRNA * /*prop*/,
node_viewer_set_shortcut_fn(curr_node, ntree, value);
}
int rna_Node_Viewer_shortcut_node_get(PointerRNA *ptr, PropertyRNA * /*prop*/)
{
bNode *curr_node = ptr->data_as<bNode>();
return curr_node->custom1;
}
void rna_Node_Viewer_shortcut_node_set(PointerRNA *ptr, int value)
{
bNode *curr_node = ptr->data_as<bNode>();
@@ -7280,6 +7276,84 @@ static void def_geo_repeat_output(BlenderRNA *brna, StructRNA *srna)
RNA_def_property_update(prop, NC_NODE, "rna_Node_update");
}
static void rna_def_geo_viewer_item(BlenderRNA *brna)
{
PropertyRNA *prop;
StructRNA *srna = RNA_def_struct(brna, "NodeGeometryViewerItem", nullptr);
RNA_def_struct_ui_text(srna, "Viewer Item", "");
RNA_def_struct_sdna(srna, "NodeGeometryViewerItem");
rna_def_node_item_array_socket_item_common(srna, "GeoViewerItemsAccessor", true);
prop = RNA_def_property(srna, "auto_remove", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "flag", NODE_GEO_VIEWER_ITEM_FLAG_AUTO_REMOVE);
RNA_def_property_flag(prop, PROP_NO_DEG_UPDATE);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_ui_text(
prop, "Auto Remove", "Remove the item automatically when it is unlinked");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
static void rna_def_geo_viewer_items(BlenderRNA *brna)
{
StructRNA *srna = RNA_def_struct(brna, "NodeGeometryViewerItems", nullptr);
RNA_def_struct_sdna(srna, "bNode");
RNA_def_struct_ui_text(srna, "Items", "Collection of viewer items");
rna_def_node_item_array_new_with_socket_and_name(
srna, "NodeGeometryViewerItem", "GeoViewerItemsAccessor");
rna_def_node_item_array_common_functions(
srna, "NodeGeometryViewerItem", "GeoViewerItemsAccessor");
}
static void rna_def_geo_viewer(BlenderRNA *brna, StructRNA *srna)
{
PropertyRNA *prop;
rna_def_geo_viewer_item(brna);
rna_def_geo_viewer_items(brna);
prop = RNA_def_property(srna, "ui_shortcut", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, nullptr, "custom1");
RNA_def_property_int_funcs(prop, nullptr, "rna_Node_Viewer_shortcut_node_set", nullptr);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_override_flag(prop, PROPOVERRIDE_IGNORE);
RNA_def_property_int_default(prop, NODE_VIEWER_SHORTCUT_NONE);
RNA_def_property_update(prop, NC_NODE | ND_DISPLAY, nullptr);
RNA_def_struct_sdna_from(srna, "NodeGeometryViewer", "storage");
prop = RNA_def_property(srna, "viewer_items", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, nullptr, "items", "items_num");
RNA_def_property_struct_type(prop, "NodeGeometryViewerItem");
RNA_def_property_ui_text(prop, "Viewer Items", "");
RNA_def_property_srna(prop, "NodeGeometryViewerItems");
prop = RNA_def_property(srna, "active_index", PROP_INT, PROP_UNSIGNED);
RNA_def_property_int_sdna(prop, nullptr, "active_index");
RNA_def_property_ui_text(prop, "Active Item Index", "Index of the active item");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_flag(prop, PROP_NO_DEG_UPDATE);
RNA_def_property_update(prop, NC_NODE, nullptr);
prop = RNA_def_property(srna, "active_item", PROP_POINTER, PROP_NONE);
RNA_def_property_struct_type(prop, "NodeGeometryViewerItem");
RNA_def_property_ui_text(prop, "Active Item", "");
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_NO_DEG_UPDATE);
RNA_def_property_pointer_funcs(prop,
"rna_Node_ItemArray_active_get<GeoViewerItemsAccessor>",
"rna_Node_ItemArray_active_set<GeoViewerItemsAccessor>",
nullptr,
nullptr);
prop = RNA_def_property(srna, "domain", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, rna_enum_attribute_domain_with_auto_items);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_ui_text(prop, "Domain", "Domain to evaluate fields on");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
static void rna_def_geo_foreach_geometry_element_input_item(BlenderRNA *brna)
{
StructRNA *srna = RNA_def_struct(brna, "ForeachGeometryElementInputItem", nullptr);
@@ -10033,7 +10107,7 @@ static void rna_def_nodes(BlenderRNA *brna)
define("GeometryNode", "GeometryNodeUVPackIslands");
define("GeometryNode", "GeometryNodeUVUnwrap");
define("GeometryNode", "GeometryNodeVertexOfCorner");
define("GeometryNode", "GeometryNodeViewer");
define("GeometryNode", "GeometryNodeViewer", rna_def_geo_viewer);
define("GeometryNode", "GeometryNodeViewportTransform");
define("GeometryNode", "GeometryNodeVolumeCube");
define("GeometryNode", "GeometryNodeVolumeToMesh");

View File

@@ -10,5 +10,6 @@ namespace blender::nodes {
class Bundle;
using BundlePtr = ImplicitSharingPtr<Bundle>;
struct BundleItemValue;
} // namespace blender::nodes

View File

@@ -38,6 +38,7 @@
#include "BKE_compute_context_cache_fwd.hh"
#include "BKE_geometry_set.hh"
#include "BKE_node.hh"
#include "BKE_node_socket_value.hh"
#include "BKE_node_tree_zones.hh"
#include "BKE_volume_grid_fwd.hh"
@@ -235,12 +236,26 @@ class ListInfoLog : public ValueLog {
};
/**
* Data logged by a viewer node when it is executed. In this case, we do want to log the entire
* geometry.
* Data logged by a viewer node when it is executed.
*/
class ViewerNodeLog {
public:
bke::GeometrySet geometry;
struct Item {
int identifier;
std::string name;
bke::SocketValueVariant value;
};
struct ItemIdentifierGetter {
int operator()(const Item &item) const
{
return item.identifier;
}
};
CustomIDVectorSet<Item, ItemIdentifierGetter> items;
std::optional<bke::GeometrySet> main_geometry() const;
};
using Clock = std::chrono::steady_clock;
@@ -310,7 +325,6 @@ class GeoTreeLogger {
~GeoTreeLogger();
void log_value(const bNode &node, const bNodeSocket &socket, GPointer value);
void log_viewer_node(const bNode &viewer_node, bke::GeometrySet geometry);
};
/**

View File

@@ -16,7 +16,6 @@ void rna_Node_update(Main *bmain, Scene *scene, PointerRNA *ptr);
void rna_Node_socket_update(Main *bmain, Scene *scene, PointerRNA *ptr);
void rna_Node_update_relations(Main *bmain, Scene *scne, PointerRNA *ptr);
void rna_Node_Viewer_shortcut_node_set(PointerRNA *ptr, PropertyRNA *prop, int value);
int rna_Node_Viewer_shortcut_node_get(PointerRNA *ptr, PropertyRNA *prop);
namespace blender::nodes {

View File

@@ -67,6 +67,21 @@ inline bNode *find_node_by_item(bNodeTree &ntree, const typename Accessor::ItemT
return nullptr;
}
/** Find the item with the given identifier. */
template<typename Accessor>
inline typename Accessor::ItemT *find_item_by_identifier(bNode &node, const StringRef identifier)
{
SocketItemsRef array = Accessor::get_items_from_node(node);
for (const int i : IndexRange(*array.items_num)) {
typename Accessor::ItemT &item = (*array.items)[i];
if (Accessor::socket_identifier_for_item(item) == identifier) {
return &item;
}
}
return nullptr;
}
/**
* Destruct all the items and the free the array itself.
*/
@@ -267,11 +282,13 @@ inline std::string get_socket_identifier(const typename Accessor::ItemT &item,
* \return False if the link should be removed.
*/
template<typename Accessor>
[[nodiscard]] inline bool try_add_item_via_extend_socket(bNodeTree &ntree,
bNode &extend_node,
bNodeSocket &extend_socket,
bNode &storage_node,
bNodeLink &link)
[[nodiscard]] inline bool try_add_item_via_extend_socket(
bNodeTree &ntree,
bNode &extend_node,
bNodeSocket &extend_socket,
bNode &storage_node,
bNodeLink &link,
typename Accessor::ItemT **r_new_item = nullptr)
{
using ItemT = typename Accessor::ItemT;
bNodeSocket *src_socket = nullptr;
@@ -285,7 +302,7 @@ template<typename Accessor>
return false;
}
const ItemT *item = nullptr;
ItemT *item = nullptr;
if constexpr (Accessor::has_name && Accessor::has_type) {
const eNodeSocketDatatype socket_type = eNodeSocketDatatype(src_socket->type);
if (!Accessor::supports_socket_type(socket_type, ntree.type)) {
@@ -311,6 +328,9 @@ template<typename Accessor>
if (item == nullptr) {
return false;
}
if (r_new_item) {
*r_new_item = item;
}
update_node_declaration_and_sockets(ntree, extend_node);
if (extend_socket.is_input()) {
@@ -338,7 +358,8 @@ template<typename Accessor>
bNode &extend_node,
bNode &storage_node,
bNodeLink &link,
const std::optional<StringRef> socket_identifier = std::nullopt)
const std::optional<StringRef> socket_identifier = std::nullopt,
typename Accessor::ItemT **r_new_item = nullptr)
{
bNodeSocket *possible_extend_socket = nullptr;
if (link.fromnode == &extend_node) {
@@ -359,7 +380,7 @@ template<typename Accessor>
}
}
return try_add_item_via_extend_socket<Accessor>(
ntree, extend_node, *possible_extend_socket, storage_node, link);
ntree, extend_node, *possible_extend_socket, storage_node, link, r_new_item);
}
} // namespace blender::nodes::socket_items

View File

@@ -257,6 +257,7 @@ set(SRC
include/NOD_geo_menu_switch.hh
include/NOD_geo_repeat.hh
include/NOD_geo_simulation.hh
include/NOD_geo_viewer.hh
node_geometry_tree.cc
node_geometry_util.cc

View File

@@ -0,0 +1,117 @@
/* SPDX-FileCopyrightText: 2025 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "DNA_node_types.h"
#include "NOD_geometry_nodes_log.hh"
#include "NOD_socket_items.hh"
namespace blender::nodes {
/**
* Makes it possible to use various functions (e.g. the ones in `NOD_socket_items.hh`) for viewer
* node items.
*/
struct GeoViewerItemsAccessor : public socket_items::SocketItemsAccessorDefaults {
using ItemT = NodeGeometryViewerItem;
static StructRNA *item_srna;
static int node_type;
static constexpr StringRefNull node_idname = "GeometryNodeViewer";
static constexpr bool has_type = true;
static constexpr bool has_name = true;
struct operator_idnames {
static constexpr StringRefNull add_item = "NODE_OT_geometry_nodes_viewer_item_add";
static constexpr StringRefNull remove_item = "NODE_OT_geometry_nodes_viewer_item_remove";
static constexpr StringRefNull move_item = "NODE_OT_geometry_nodes_viewer_item_move";
};
struct ui_idnames {
static constexpr StringRefNull list = "NODE_UL_geometry_nodes_viewer_items";
};
struct rna_names {
static constexpr StringRefNull items = "viewer_items";
static constexpr StringRefNull active_index = "active_index";
};
static socket_items::SocketItemsRef<NodeGeometryViewerItem> get_items_from_node(bNode &node)
{
auto *storage = static_cast<NodeGeometryViewer *>(node.storage);
return {&storage->items, &storage->items_num, &storage->active_index};
}
static void copy_item(const NodeGeometryViewerItem &src, NodeGeometryViewerItem &dst)
{
dst = src;
dst.name = BLI_strdup_null(dst.name);
}
static void destruct_item(NodeGeometryViewerItem *item)
{
MEM_SAFE_FREE(item->name);
}
static void blend_write_item(BlendWriter *writer, const ItemT &item);
static void blend_read_data_item(BlendDataReader *reader, ItemT &item);
static eNodeSocketDatatype get_socket_type(const NodeGeometryViewerItem &item)
{
return eNodeSocketDatatype(item.socket_type);
}
static char **get_name(NodeGeometryViewerItem &item)
{
return &item.name;
}
static void init_with_socket_type_and_name(bNode &node,
NodeGeometryViewerItem &item,
const eNodeSocketDatatype socket_type,
const char *name)
{
auto *storage = static_cast<NodeGeometryViewer *>(node.storage);
item.socket_type = socket_type;
item.identifier = storage->next_identifier++;
socket_items::set_item_name_and_make_unique<GeoViewerItemsAccessor>(node, item, name);
}
static bool supports_socket_type(const eNodeSocketDatatype socket_type, const int /*ntree_type*/)
{
return ELEM(socket_type,
SOCK_FLOAT,
SOCK_VECTOR,
SOCK_RGBA,
SOCK_BOOLEAN,
SOCK_ROTATION,
SOCK_MATRIX,
SOCK_INT,
SOCK_STRING,
SOCK_GEOMETRY,
SOCK_OBJECT,
SOCK_MATERIAL,
SOCK_IMAGE,
SOCK_COLLECTION,
SOCK_BUNDLE,
SOCK_CLOSURE);
}
static std::string socket_identifier_for_item(const NodeGeometryViewerItem &item)
{
/* These special cases exist for compatibility with older Blender versions when the viewer did
* not have a dynamic number of inputs yet. */
if (item.identifier == 0 && item.socket_type == SOCK_GEOMETRY) {
return "Geometry";
}
if (item.identifier == 1 && item.socket_type != SOCK_GEOMETRY) {
return "Value";
}
return "Item_" + std::to_string(item.identifier);
}
};
void geo_viewer_node_log(const bNode &node,
const Span<bke::SocketValueVariant *> input_values,
geo_eval_log::ViewerNodeLog &r_log);
} // namespace blender::nodes

View File

@@ -2,12 +2,17 @@
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_listbase.h"
#include <fmt/format.h>
#include "BKE_context.hh"
#include "BLO_read_write.hh"
#include "NOD_geo_viewer.hh"
#include "NOD_node_extra_info.hh"
#include "NOD_rna_define.hh"
#include "NOD_socket_items_blend.hh"
#include "NOD_socket_items_ops.hh"
#include "NOD_socket_items_ui.hh"
#include "NOD_socket_search_link.hh"
#include "UI_interface_layout.hh"
@@ -17,6 +22,9 @@
#include "ED_viewer_path.hh"
#include "RNA_enum_types.hh"
#include "RNA_prototypes.hh"
#include "GEO_foreach_geometry.hh"
#include "node_geometry_util.hh"
@@ -26,90 +34,201 @@ NODE_STORAGE_FUNCS(NodeGeometryViewer)
static void node_declare(NodeDeclarationBuilder &b)
{
const bNode *node = b.node_or_null();
b.use_custom_socket_order();
b.allow_any_socket_order();
b.add_input<decl::Geometry>("Geometry");
if (node != nullptr) {
const eCustomDataType data_type = eCustomDataType(node_storage(*node).data_type);
b.add_input(data_type, "Value").field_on_all().hide_value();
const bNode *node = b.node_or_null();
const bNodeTree *tree = b.tree_or_null();
if (!node || !tree) {
return;
}
b.add_default_layout();
const NodeGeometryViewer &storage = node_storage(*node);
for (const int i : IndexRange(storage.items_num)) {
const NodeGeometryViewerItem &item = storage.items[i];
const eNodeSocketDatatype socket_type = eNodeSocketDatatype(item.socket_type);
const StringRef name = item.name ? item.name : "";
const std::string identifier = GeoViewerItemsAccessor::socket_identifier_for_item(item);
auto &input_decl = b.add_input(socket_type, name, identifier)
.socket_name_ptr(
&tree->id, GeoViewerItemsAccessor::item_srna, &item, "name");
if (socket_type_supports_fields(socket_type)) {
input_decl.field_on_all();
}
input_decl.structure_type(StructureType::Dynamic);
}
b.add_input<decl::Extend>("", "__extend__").structure_type(StructureType::Dynamic);
}
static void node_init(bNodeTree * /*tree*/, bNode *node)
{
NodeGeometryViewer *data = MEM_callocN<NodeGeometryViewer>(__func__);
data->data_type = CD_PROP_FLOAT;
data->data_type_legacy = CD_PROP_FLOAT;
data->domain = int8_t(AttrDomain::Auto);
node->storage = data;
}
static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
{
layout->prop(ptr, "domain", UI_ITEM_NONE, "", ICON_NONE);
const bNode &node = *ptr->data_as<bNode>();
const NodeGeometryViewer &storage = node_storage(node);
bool has_geometry_input = false;
bool has_potential_field_input = false;
for (const int i : IndexRange(storage.items_num)) {
const NodeGeometryViewerItem &item = storage.items[i];
const eNodeSocketDatatype socket_type = eNodeSocketDatatype(item.socket_type);
if (socket_type == SOCK_GEOMETRY) {
has_geometry_input = true;
}
else if (socket_type_supports_fields(socket_type)) {
has_potential_field_input = true;
}
}
if (has_geometry_input && has_potential_field_input) {
layout->prop(ptr, "domain", UI_ITEM_NONE, "", ICON_NONE);
}
}
static void node_layout_ex(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
static void node_layout_ex(uiLayout *layout, bContext *C, PointerRNA *ptr)
{
layout->prop(ptr, "data_type", UI_ITEM_NONE, "", ICON_NONE);
bNode &node = *ptr->data_as<bNode>();
bNodeTree &ntree = *reinterpret_cast<bNodeTree *>(ptr->owner_id);
if (uiLayout *panel = layout->panel(C, "viewer_items", false, IFACE_("Viewer Items"))) {
socket_items::ui::draw_items_list_with_operators<GeoViewerItemsAccessor>(
C, panel, ntree, node);
socket_items::ui::draw_active_item_props<GeoViewerItemsAccessor>(
ntree, node, [&](PointerRNA *item_ptr) {
panel->use_property_split_set(true);
panel->use_property_decorate_set(false);
panel->prop(item_ptr, "socket_type", UI_ITEM_NONE, std::nullopt, ICON_NONE);
panel->prop(item_ptr, "auto_remove", UI_ITEM_NONE, std::nullopt, ICON_NONE);
});
}
}
static void node_gather_link_searches(GatherLinkSearchOpParams &params)
{
auto set_active_fn = [](LinkSearchOpParams &params, bNode &viewer_node) {
/* Set this new viewer node active in spreadsheet editors. */
SpaceNode *snode = CTX_wm_space_node(&params.C);
Main *bmain = CTX_data_main(&params.C);
ED_node_set_active(bmain, snode, &params.node_tree, &viewer_node, nullptr);
ed::viewer_path::activate_geometry_node(*bmain, *snode, viewer_node);
};
const eNodeSocketDatatype socket_type = eNodeSocketDatatype(params.other_socket().type);
const std::optional<eCustomDataType> type = bke::socket_type_to_custom_data_type(socket_type);
if (params.in_out() == SOCK_OUT) {
/* The viewer node only has inputs. */
const bNodeSocket &other_socket = params.other_socket();
if (other_socket.in_out == SOCK_OUT) {
params.add_item("Value", [](LinkSearchOpParams &params) {
bNode &node = params.add_node("GeometryNodeViewer");
const auto *item = socket_items::add_item_with_socket_type_and_name<GeoViewerItemsAccessor>(
params.node_tree, node, params.socket.typeinfo->type, params.socket.name);
params.update_and_connect_available_socket(node, item->name);
SpaceNode *snode = CTX_wm_space_node(&params.C);
Main *bmain = CTX_data_main(&params.C);
ed::viewer_path::activate_geometry_node(*bmain, *snode, node);
});
return;
}
if (params.other_socket().type == SOCK_GEOMETRY) {
params.add_item(IFACE_("Geometry"), [set_active_fn](LinkSearchOpParams &params) {
bNode &node = params.add_node("GeometryNodeViewer");
params.connect_available_socket(node, "Geometry");
set_active_fn(params, node);
});
}
if (type && ELEM(*type,
CD_PROP_FLOAT,
CD_PROP_BOOL,
CD_PROP_INT32,
CD_PROP_FLOAT3,
CD_PROP_COLOR,
CD_PROP_QUATERNION,
CD_PROP_FLOAT4X4))
{
params.add_item(IFACE_("Value"), [type, set_active_fn](LinkSearchOpParams &params) {
bNode &node = params.add_node("GeometryNodeViewer");
node_storage(node).data_type = *type;
params.update_and_connect_available_socket(node, "Value");
}
/* If the source node has a geometry socket, connect it to the new viewer node as well. */
LISTBASE_FOREACH (bNodeSocket *, socket, &params.node.outputs) {
if (socket->type == SOCK_GEOMETRY && socket->is_visible()) {
bke::node_add_link(params.node_tree,
params.node,
*socket,
node,
*static_cast<bNodeSocket *>(node.inputs.first));
break;
}
/**
* Evaluates the first field after for each geometry as ".viewer" attribute. This attribute is used
* by drawing code.
*/
static void log_viewer_attribute(const bNode &node, geo_eval_log::ViewerNodeLog &r_log)
{
const auto &storage = *static_cast<NodeGeometryViewer *>(node.storage);
const StringRef viewer_attribute_name = ".viewer";
std::optional<int> last_geometry_identifier;
for (const int i : IndexRange(storage.items_num)) {
const bNodeSocket &bsocket = node.input_socket(i);
const NodeGeometryViewerItem &item = storage.items[i];
const bke::bNodeSocketType &type = *bsocket.typeinfo;
if (type.type == SOCK_GEOMETRY) {
last_geometry_identifier = item.identifier;
continue;
}
if (!last_geometry_identifier) {
continue;
}
if (!socket_type_supports_fields(type.type)) {
continue;
}
/* Changing the `value` field doesn't change the hash or equality of the item. */
GMutablePointer geometry_ptr = const_cast<bke::SocketValueVariant &>(
r_log.items.lookup_key_as(*last_geometry_identifier).value)
.get_single_ptr();
GeometrySet &geometry = *geometry_ptr.get<GeometrySet>();
const bke::SocketValueVariant &value = r_log.items.lookup_key_as(item.identifier).value;
if (!(value.is_single() || value.is_context_dependent_field())) {
continue;
}
const GField field = value.get<GField>();
const AttrDomain domain_or_auto = AttrDomain(storage.domain);
if (domain_or_auto == AttrDomain::Instance) {
if (geometry.has_instances()) {
bke::GeometryComponent &component =
geometry.get_component_for_write<bke::InstancesComponent>();
bke::try_capture_field_on_geometry(
component, viewer_attribute_name, AttrDomain::Instance, field);
}
set_active_fn(params, node);
});
}
else {
geometry::foreach_real_geometry(geometry, [&](GeometrySet &geometry) {
for (const bke::GeometryComponent::Type type :
{bke::GeometryComponent::Type::Mesh,
bke::GeometryComponent::Type::PointCloud,
bke::GeometryComponent::Type::Curve,
bke::GeometryComponent::Type::GreasePencil})
{
if (!geometry.has(type)) {
continue;
}
bke::GeometryComponent &component = geometry.get_component_for_write(type);
AttrDomain used_domain = domain_or_auto;
if (domain_or_auto == AttrDomain::Auto) {
if (const std::optional<AttrDomain> domain = bke::try_detect_field_domain(component,
field))
{
used_domain = *domain;
}
else {
used_domain = AttrDomain::Point;
}
}
bke::try_capture_field_on_geometry(component, viewer_attribute_name, used_domain, field);
}
});
}
/* Avoid overriding the viewer attribute with other fields.*/
last_geometry_identifier.reset();
}
}
static void geo_viewer_node_log_impl(const bNode &node,
const Span<bke::SocketValueVariant *> input_values,
geo_eval_log::ViewerNodeLog &r_log)
{
const auto &storage = *static_cast<NodeGeometryViewer *>(node.storage);
for (const int i : IndexRange(storage.items_num)) {
void *src_value = input_values[i];
if (!src_value) {
continue;
}
const NodeGeometryViewerItem &item = storage.items[i];
bke::SocketValueVariant &value = *input_values[i];
if (value.is_single() && value.get_single_ptr().is_type<bke::GeometrySet>()) {
value.get_single_ptr().get<bke::GeometrySet>()->ensure_owns_direct_data();
}
r_log.items.add_new({item.identifier, item.name, std::move(value)});
}
log_viewer_attribute(node, r_log);
}
static void node_extra_info(NodeExtraInfoParams &params)
{
const auto data_type = eCustomDataType(node_storage(params.node).data_type);
const auto data_type = eCustomDataType(node_storage(params.node).data_type_legacy);
if (ELEM(data_type, CD_PROP_QUATERNION, CD_PROP_FLOAT4X4)) {
NodeExtraInfoRow row;
row.icon = ICON_INFO;
@@ -120,37 +239,46 @@ static void node_extra_info(NodeExtraInfoParams &params)
}
}
static void node_rna(StructRNA *srna)
static void node_operators()
{
RNA_def_node_enum(srna,
"data_type",
"Data Type",
"",
rna_enum_attribute_type_items,
NOD_storage_enum_accessors(data_type),
CD_PROP_FLOAT,
enums::attribute_type_type_with_socket_fn);
socket_items::ops::make_common_operators<GeoViewerItemsAccessor>();
}
RNA_def_node_enum(srna,
"domain",
"Domain",
"Domain to evaluate the field on",
rna_enum_attribute_domain_with_auto_items,
NOD_storage_enum_accessors(domain),
int(AttrDomain::Point));
static void node_free_storage(bNode *node)
{
socket_items::destruct_array<GeoViewerItemsAccessor>(*node);
MEM_freeN(node->storage);
}
PropertyRNA *prop;
prop = RNA_def_property(srna, "ui_shortcut", PROP_INT, PROP_NONE);
RNA_def_property_int_funcs_runtime(prop,
rna_Node_Viewer_shortcut_node_get,
rna_Node_Viewer_shortcut_node_set,
nullptr,
nullptr,
nullptr);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_override_flag(prop, PROPOVERRIDE_IGNORE);
RNA_def_property_int_default(prop, NODE_VIEWER_SHORTCUT_NONE);
RNA_def_property_update_notifier(prop, NC_NODE | ND_DISPLAY);
static void node_copy_storage(bNodeTree * /*dst_tree*/, bNode *dst_node, const bNode *src_node)
{
const NodeGeometryViewer &src_storage = node_storage(*src_node);
auto *dst_storage = MEM_mallocN<NodeGeometryViewer>(__func__);
*dst_storage = src_storage;
dst_node->storage = dst_storage;
socket_items::copy_array<GeoViewerItemsAccessor>(*src_node, *dst_node);
}
static bool node_insert_link(bke::NodeInsertLinkParams &params)
{
NodeGeometryViewerItem *new_item = nullptr;
const bool keep_link = socket_items::try_add_item_via_any_extend_socket<GeoViewerItemsAccessor>(
params.ntree, params.node, params.node, params.link, std::nullopt, &new_item);
if (new_item) {
new_item->flag |= NODE_GEO_VIEWER_ITEM_FLAG_AUTO_REMOVE;
}
return keep_link;
}
static void node_blend_write(const bNodeTree & /*tree*/, const bNode &node, BlendWriter &writer)
{
socket_items::blend_write<GeoViewerItemsAccessor>(&writer, node);
}
static void node_blend_read(bNodeTree & /*tree*/, bNode &node, BlendDataReader &reader)
{
socket_items::blend_read_data<GeoViewerItemsAccessor>(&reader, node);
}
static void node_register()
@@ -163,18 +291,45 @@ static void node_register()
ntype.enum_name_legacy = "VIEWER";
ntype.nclass = NODE_CLASS_OUTPUT;
blender::bke::node_type_storage(
ntype, "NodeGeometryViewer", node_free_standard_storage, node_copy_standard_storage);
ntype, "NodeGeometryViewer", node_free_storage, node_copy_storage);
ntype.declare = node_declare;
ntype.initfunc = node_init;
ntype.draw_buttons = node_layout;
ntype.draw_buttons_ex = node_layout_ex;
ntype.insert_link = node_insert_link;
ntype.gather_link_search_ops = node_gather_link_searches;
ntype.no_muting = true;
ntype.register_operators = node_operators;
ntype.get_extra_info = node_extra_info;
ntype.blend_write_storage_content = node_blend_write;
ntype.blend_data_read_storage_content = node_blend_read;
blender::bke::node_register_type(ntype);
node_rna(ntype.rna_ext.srna);
}
NOD_REGISTER_NODE(node_register)
} // namespace blender::nodes::node_geo_viewer_cc
namespace blender::nodes {
StructRNA *GeoViewerItemsAccessor::item_srna = &RNA_NodeGeometryViewerItem;
void GeoViewerItemsAccessor::blend_write_item(BlendWriter *writer,
const NodeGeometryViewerItem &item)
{
BLO_write_string(writer, item.name);
}
void GeoViewerItemsAccessor::blend_read_data_item(BlendDataReader *reader,
NodeGeometryViewerItem &item)
{
BLO_read_string(reader, &item.name);
}
void geo_viewer_node_log(const bNode &node,
const Span<bke::SocketValueVariant *> input_values,
geo_eval_log::ViewerNodeLog &r_log)
{
node_geo_viewer_cc::geo_viewer_node_log_impl(node, input_values, r_log);
}
} // namespace blender::nodes

View File

@@ -20,6 +20,7 @@
* complexity. So far, this does not seem to be a performance issue.
*/
#include "NOD_geo_viewer.hh"
#include "NOD_geometry_exec.hh"
#include "NOD_geometry_nodes_lazy_function.hh"
#include "NOD_geometry_nodes_list.hh"
@@ -811,28 +812,14 @@ class LazyFunctionForImplicitInput : public LazyFunction {
class LazyFunctionForViewerNode : public LazyFunction {
private:
const bNode &bnode_;
/** The field is only logged when it is linked. */
bool use_field_input_ = true;
Span<int> lf_index_by_bsocket_;
public:
LazyFunctionForViewerNode(const bNode &bnode, MutableSpan<int> r_lf_index_by_bsocket)
: bnode_(bnode)
: bnode_(bnode), lf_index_by_bsocket_(r_lf_index_by_bsocket)
{
debug_name_ = "Viewer";
lazy_function_interface_from_node(bnode, inputs_, outputs_, r_lf_index_by_bsocket);
/* Remove field input if it is not used. */
for (const bNodeSocket *bsocket : bnode.input_sockets().drop_front(1)) {
if (!bsocket->is_available()) {
continue;
}
const Span<const bNodeLink *> links = bsocket->directly_linked_links();
if (links.is_empty() || links.first()->fromnode->is_dangling_reroute()) {
use_field_input_ = false;
inputs_.pop_last();
r_lf_index_by_bsocket[bsocket->index_in_tree()] = -1;
}
}
}
void execute_impl(lf::Params &params, const lf::Context &context) const override
@@ -844,53 +831,21 @@ class LazyFunctionForViewerNode : public LazyFunction {
return;
}
GeometrySet geometry = params.extract_input<SocketValueVariant>(0).extract<GeometrySet>();
const NodeGeometryViewer *storage = static_cast<NodeGeometryViewer *>(bnode_.storage);
LinearAllocator<> &allocator = *tree_logger->allocator;
if (use_field_input_) {
SocketValueVariant *value_variant = params.try_get_input_data_ptr<SocketValueVariant>(1);
BLI_assert(value_variant != nullptr);
GField field = value_variant->extract<GField>();
const AttrDomain domain = AttrDomain(storage->domain);
const StringRefNull viewer_attribute_name = ".viewer";
if (domain == AttrDomain::Instance) {
if (geometry.has_instances()) {
GeometryComponent &component = geometry.get_component_for_write(
bke::GeometryComponent::Type::Instance);
bke::try_capture_field_on_geometry(
component, viewer_attribute_name, AttrDomain::Instance, field);
}
}
else {
geometry::foreach_real_geometry(geometry, [&](GeometrySet &geometry) {
for (const bke::GeometryComponent::Type type :
{bke::GeometryComponent::Type::Mesh,
bke::GeometryComponent::Type::PointCloud,
bke::GeometryComponent::Type::Curve,
bke::GeometryComponent::Type::GreasePencil})
{
if (geometry.has(type)) {
GeometryComponent &component = geometry.get_component_for_write(type);
AttrDomain used_domain = domain;
if (used_domain == AttrDomain::Auto) {
if (const std::optional<AttrDomain> detected_domain = bke::try_detect_field_domain(
component, field))
{
used_domain = *detected_domain;
}
else {
used_domain = AttrDomain::Point;
}
}
bke::try_capture_field_on_geometry(
component, viewer_attribute_name, used_domain, field);
}
}
});
}
const NodeGeometryViewer &storage = *static_cast<const NodeGeometryViewer *>(bnode_.storage);
Vector<bke::SocketValueVariant *> values(storage.items_num, nullptr);
for (const int i : IndexRange(storage.items_num)) {
const bNodeSocket &bsocket = bnode_.input_socket(i);
const int param_index = lf_index_by_bsocket_[bsocket.index_in_tree()];
values[i] = params.try_get_input_data_ptr<bke::SocketValueVariant>(param_index);
}
tree_logger->log_viewer_node(bnode_, std::move(geometry));
auto log = allocator.construct<geo_eval_log::ViewerNodeLog>();
geo_viewer_node_log(bnode_, values, *log);
tree_logger->viewer_node_logs.append(allocator, {bnode_.identifier, std::move(log)});
}
};
@@ -3287,11 +3242,8 @@ struct GeometryNodesLazyFunctionBuilder {
bnode, mapping_->lf_index_by_bsocket);
lf::FunctionNode &lf_viewer_node = graph_params.lf_graph.add_function(lazy_function);
for (const bNodeSocket *bsocket : bnode.input_sockets()) {
for (const bNodeSocket *bsocket : bnode.input_sockets().drop_back(1)) {
const int lf_index = mapping_->lf_index_by_bsocket[bsocket->index_in_tree()];
if (lf_index == -1) {
continue;
}
lf::InputSocket &lf_socket = lf_viewer_node.input(lf_index);
graph_params.lf_inputs_by_bsocket.add(bsocket, &lf_socket);
mapping_->bsockets_by_lf_socket_map.add(&lf_socket, bsocket);
@@ -3304,10 +3256,8 @@ struct GeometryNodesLazyFunctionBuilder {
lf_viewer_node);
lf::FunctionNode &lf_usage_node = graph_params.lf_graph.add_function(usage_lazy_function);
for (const bNodeSocket *bsocket : bnode.input_sockets()) {
if (bsocket->is_available()) {
graph_params.usage_by_bsocket.add(bsocket, &lf_usage_node.output(0));
}
for (const bNodeSocket *bsocket : bnode.input_sockets().drop_back(1)) {
graph_params.usage_by_bsocket.add(bsocket, &lf_usage_node.output(0));
}
}
}

View File

@@ -381,12 +381,14 @@ void GeoTreeLogger::log_value(const bNode &node, const bNodeSocket &socket, cons
}
}
void GeoTreeLogger::log_viewer_node(const bNode &viewer_node, bke::GeometrySet geometry)
std::optional<bke::GeometrySet> ViewerNodeLog::main_geometry() const
{
destruct_ptr<ViewerNodeLog> log = this->allocator->construct<ViewerNodeLog>();
log->geometry = std::move(geometry);
log->geometry.ensure_owns_direct_data();
this->viewer_node_logs.append(*this->allocator, {viewer_node.identifier, std::move(log)});
for (const Item &item : this->items) {
if (item.value.is_single() && item.value.get_single_ptr().is_type<bke::GeometrySet>()) {
return *item.value.get_single_ptr().get<bke::GeometrySet>();
}
}
return std::nullopt;
}
static bool warning_is_propagated(const NodeWarningPropagation propagation,
@@ -674,7 +676,7 @@ ValueLog *GeoTreeLog::find_socket_value_log(const bNodeSocket &query_socket)
{
/**
* Geometry nodes does not log values for every socket. That would produce a lot of redundant
* data,because often many linked sockets have the same value. To find the logged value for a
* data, because often many linked sockets have the same value. To find the logged value for a
* socket one might have to look at linked sockets as well.
*/