Geometry Nodes: support viewing field values in spreadsheet

The viewer node has been expanded to have a field input next to the
geometry input. When both are connected (by ctrl+shift clicking on a node)
the spreadsheet will show the evaluated field on the geometry.

The operator to link to the viewer has become a bit smarter. It automatically
detects if it should link to the geometry or field input. In the future some more
smartness could be added, such as automatically relinking the "right" geometry
when viewing a field.

Internally, there are two major changes:
* Refactor of what happens when ctrl+shift clicking on a node to link to
  a viewer. The behavior of the geometry nodes viewer is a bit more complex
  than that of the compositor viewers. The behavior in compositing nodes
  should not have changed. Any change should be reported as a bug (and then
  we can decide if it's worse than before or if it needs fixing).
*  Evaluation, display and caching of fields in the spreadsheet editor.

Differential Revision: https://developer.blender.org/D12938
This commit is contained in:
Jacques Lucke 2021-10-26 11:25:32 +02:00
parent fee2cedb33
commit 5bfe09df22
Notes: blender-bot 2023-02-13 17:26:32 +01:00
Referenced by commit 01d7211380, Fix T92505: previewing specific node outputs did not work anymore
Referenced by issue #92667, Opening a certain file and splitting the main editor results in a crash
Referenced by issue #92655, 3.0b crash after splitting area
Referenced by issue #92505, Previewing specific node outputs in Compositor no longer works like before
Referenced by issue #92167, Add field input to viewer node for spreadsheet visualization
16 changed files with 860 additions and 230 deletions

View File

@ -2100,5 +2100,22 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
version_geometry_nodes_set_position_node_offset(ntree);
}
/* Keep this block, even when empty. */
/* Add storage to viewer node. */
LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) {
if (ntree->type != NTREE_GEOMETRY) {
continue;
}
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->type == GEO_NODE_VIEWER) {
if (node->storage == NULL) {
NodeGeometryViewer *data = (NodeGeometryViewer *)MEM_callocN(
sizeof(NodeGeometryViewer), __func__);
data->data_type = CD_PROP_FLOAT;
node->storage = data;
}
}
}
}
}
}

View File

@ -57,8 +57,12 @@
#include "BLT_translation.h"
#include "NOD_node_tree_ref.hh"
#include "node_intern.h" /* own include */
using namespace blender::nodes::node_tree_ref_types;
/* -------------------------------------------------------------------- */
/** \name Relations Helpers
* \{ */
@ -612,160 +616,274 @@ static void snode_autoconnect(Main *bmain,
/** \name Link Viewer Operator
* \{ */
static int node_link_viewer(const bContext *C, bNode *tonode)
namespace blender::ed::nodes::viewer_linking {
/* Depending on the node tree type, different socket types are supported by viewer nodes. */
static bool socket_can_be_viewed(const OutputSocketRef &socket)
{
if (nodeSocketIsHidden(socket.bsocket())) {
return false;
}
if (socket.idname() == "NodeSocketVirtual") {
return false;
}
if (socket.tree().btree()->type != NTREE_GEOMETRY) {
return true;
}
return ELEM(socket.typeinfo()->type,
SOCK_GEOMETRY,
SOCK_FLOAT,
SOCK_VECTOR,
SOCK_INT,
SOCK_BOOLEAN,
SOCK_RGBA);
}
static CustomDataType socket_type_to_custom_data_type(const eNodeSocketDatatype socket_type)
{
switch (socket_type) {
case SOCK_FLOAT:
return CD_PROP_FLOAT;
case SOCK_INT:
return CD_PROP_INT32;
case SOCK_VECTOR:
return CD_PROP_FLOAT3;
case SOCK_BOOLEAN:
return CD_PROP_BOOL;
case SOCK_RGBA:
return CD_PROP_COLOR;
default:
/* Fallback. */
return CD_AUTO_FROM_NAME;
}
}
/**
* Find the socket to link to in a viewer node.
*/
static bNodeSocket *node_link_viewer_get_socket(bNodeTree *ntree,
bNode *viewer_node,
bNodeSocket *src_socket)
{
if (viewer_node->type != GEO_NODE_VIEWER) {
/* In viewer nodes in the compositor, only the first input should be linked to. */
return (bNodeSocket *)viewer_node->inputs.first;
}
/* For the geometry nodes viewer, find the socket with the correct type. */
LISTBASE_FOREACH (bNodeSocket *, viewer_socket, &viewer_node->inputs) {
if (viewer_socket->type == src_socket->type) {
if (viewer_socket->type == SOCK_GEOMETRY) {
return viewer_socket;
}
NodeGeometryViewer *storage = (NodeGeometryViewer *)viewer_node->storage;
const CustomDataType data_type = socket_type_to_custom_data_type(
(eNodeSocketDatatype)src_socket->type);
BLI_assert(data_type != CD_AUTO_FROM_NAME);
storage->data_type = data_type;
nodeUpdate(ntree, viewer_node);
return viewer_socket;
}
}
return nullptr;
}
static bool is_viewer_node(const NodeRef &node)
{
return ELEM(node.bnode()->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER, GEO_NODE_VIEWER);
}
static Vector<const NodeRef *> find_viewer_nodes(const NodeTreeRef &tree)
{
Vector<const NodeRef *> viewer_nodes;
for (const NodeRef *node : tree.nodes()) {
if (is_viewer_node(*node)) {
viewer_nodes.append(node);
}
}
return viewer_nodes;
}
static bool is_viewer_socket_in_viewer(const InputSocketRef &socket)
{
const NodeRef &node = socket.node();
BLI_assert(is_viewer_node(node));
if (node.typeinfo()->type == GEO_NODE_VIEWER) {
return true;
}
return socket.index() == 0;
}
static bool is_linked_to_viewer(const OutputSocketRef &socket, const NodeRef &viewer_node)
{
for (const InputSocketRef *target_socket : socket.directly_linked_sockets()) {
if (&target_socket->node() != &viewer_node) {
continue;
}
if (!target_socket->is_available()) {
continue;
}
if (is_viewer_socket_in_viewer(*target_socket)) {
return true;
}
}
return false;
}
static int get_default_viewer_type(const bContext *C)
{
SpaceNode *snode = CTX_wm_space_node(C);
return ED_node_is_compositor(snode) ? CMP_NODE_VIEWER : GEO_NODE_VIEWER;
}
/* context check */
if (tonode == nullptr || BLI_listbase_is_empty(&tonode->outputs)) {
return OPERATOR_CANCELLED;
static void remove_links_to_unavailable_viewer_sockets(bNodeTree &btree, bNode &viewer_node)
{
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &btree.links) {
if (link->tonode == &viewer_node) {
if (link->tosock->flag & SOCK_UNAVAIL) {
nodeRemLink(&btree, link);
}
}
}
if (ELEM(tonode->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER, GEO_NODE_VIEWER)) {
}
static const NodeRef *get_existing_viewer(const NodeTreeRef &tree)
{
Vector<const NodeRef *> viewer_nodes = find_viewer_nodes(tree);
/* Check if there is already an active viewer node that should be used. */
for (const NodeRef *viewer_node : viewer_nodes) {
if (viewer_node->bnode()->flag & NODE_DO_OUTPUT) {
return viewer_node;
}
}
/* If no active but non-active viewers exist, make one active. */
if (!viewer_nodes.is_empty()) {
viewer_nodes[0]->bnode()->flag |= NODE_DO_OUTPUT;
return viewer_nodes[0];
}
return nullptr;
}
static const OutputSocketRef *find_output_socket_to_be_viewed(const NodeRef *active_viewer_node,
const NodeRef &node_to_view)
{
const OutputSocketRef *last_socket_linked_to_viewer = nullptr;
if (active_viewer_node != nullptr) {
for (const OutputSocketRef *output_socket : node_to_view.outputs()) {
if (!socket_can_be_viewed(*output_socket)) {
continue;
}
if (is_linked_to_viewer(*output_socket, *active_viewer_node)) {
last_socket_linked_to_viewer = output_socket;
}
}
}
if (last_socket_linked_to_viewer == nullptr) {
/* If no output is connected to a viewer, use the first output that can be viewed. */
for (const OutputSocketRef *output_socket : node_to_view.outputs()) {
if (socket_can_be_viewed(*output_socket)) {
return output_socket;
}
}
}
else {
/* Pick the next socket to be linked to the viewer. */
const int tot_outputs = node_to_view.outputs().size();
for (const int offset : IndexRange(1, tot_outputs - 1)) {
const int index = (last_socket_linked_to_viewer->index() + offset) % tot_outputs;
const OutputSocketRef &output_socket = node_to_view.output(index);
if (!socket_can_be_viewed(output_socket)) {
continue;
}
if (is_linked_to_viewer(output_socket, *active_viewer_node)) {
continue;
}
return &output_socket;
}
}
return nullptr;
}
static int link_socket_to_viewer(const bContext *C,
bNode *viewer_bnode,
bNode *bnode_to_view,
bNodeSocket *bsocket_to_view)
{
SpaceNode *snode = CTX_wm_space_node(C);
bNodeTree *btree = snode->edittree;
if (viewer_bnode == nullptr) {
/* Create a new viewer node if none exists. */
const int viewer_type = get_default_viewer_type(C);
viewer_bnode = node_add_node(
C, nullptr, viewer_type, bsocket_to_view->locx + 100, bsocket_to_view->locy);
if (viewer_bnode == nullptr) {
return OPERATOR_CANCELLED;
}
}
bNodeSocket *viewer_bsocket = node_link_viewer_get_socket(btree, viewer_bnode, bsocket_to_view);
if (viewer_bsocket == nullptr) {
return OPERATOR_CANCELLED;
}
/* get viewer */
bNode *viewer_node = nullptr;
LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) {
if (ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER, GEO_NODE_VIEWER)) {
if (node->flag & NODE_DO_OUTPUT) {
viewer_node = node;
break;
}
}
}
/* no viewer, we make one active */
if (viewer_node == nullptr) {
LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) {
if (ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER, GEO_NODE_VIEWER)) {
node->flag |= NODE_DO_OUTPUT;
viewer_node = node;
break;
}
bNodeLink *link_to_change = nullptr;
LISTBASE_FOREACH (bNodeLink *, link, &btree->links) {
if (link->tosock == viewer_bsocket) {
link_to_change = link;
break;
}
}
bNodeSocket *sock = nullptr;
bNodeLink *link = nullptr;
/* try to find an already connected socket to cycle to the next */
if (viewer_node) {
link = nullptr;
for (link = (bNodeLink *)snode->edittree->links.first; link; link = link->next) {
if (link->tonode == viewer_node && link->fromnode == tonode) {
if (link->tosock == viewer_node->inputs.first) {
break;
}
}
}
if (link) {
/* unlink existing connection */
sock = link->fromsock;
nodeRemLink(snode->edittree, link);
/* find a socket after the previously connected socket */
if (ED_node_is_geometry(snode)) {
/* Geometry nodes viewer only supports geometry sockets for now. */
for (sock = sock->next; sock; sock = sock->next) {
if (sock->type == SOCK_GEOMETRY && !nodeSocketIsHidden(sock)) {
break;
}
}
}
else {
for (sock = sock->next; sock; sock = sock->next) {
if (!nodeSocketIsHidden(sock)) {
break;
}
}
}
}
if (link_to_change == nullptr) {
nodeAddLink(btree, bnode_to_view, bsocket_to_view, viewer_bnode, viewer_bsocket);
}
else {
link_to_change->fromnode = bnode_to_view;
link_to_change->fromsock = bsocket_to_view;
btree->update |= NTREE_UPDATE_LINKS;
}
if (tonode) {
/* Find a selected socket that overrides the socket to connect to */
if (ED_node_is_geometry(snode)) {
/* Geometry nodes viewer only supports geometry sockets for now. */
LISTBASE_FOREACH (bNodeSocket *, sock2, &tonode->outputs) {
if (sock2->type == SOCK_GEOMETRY && !nodeSocketIsHidden(sock2) && sock2->flag & SELECT) {
sock = sock2;
break;
}
}
}
else {
LISTBASE_FOREACH (bNodeSocket *, sock2, &tonode->outputs) {
if (!nodeSocketIsHidden(sock2) && sock2->flag & SELECT) {
sock = sock2;
break;
}
}
}
remove_links_to_unavailable_viewer_sockets(*btree, *viewer_bnode);
if (btree->type == NTREE_GEOMETRY) {
ED_spreadsheet_context_paths_set_geometry_node(CTX_data_main(C), snode, viewer_bnode);
}
/* find a socket starting from the first socket */
if (!sock) {
if (ED_node_is_geometry(snode)) {
/* Geometry nodes viewer only supports geometry sockets for now. */
for (sock = (bNodeSocket *)tonode->outputs.first; sock; sock = sock->next) {
if (sock->type == SOCK_GEOMETRY && !nodeSocketIsHidden(sock)) {
break;
}
}
}
else {
for (sock = (bNodeSocket *)tonode->outputs.first; sock; sock = sock->next) {
if (!nodeSocketIsHidden(sock)) {
break;
}
}
}
}
if (sock) {
/* add a new viewer if none exists yet */
if (!viewer_node) {
/* XXX location is a quick hack, just place it next to the linked socket */
const int viewer_type = ED_node_is_compositor(snode) ? CMP_NODE_VIEWER : GEO_NODE_VIEWER;
viewer_node = node_add_node(C, nullptr, viewer_type, sock->locx + 100, sock->locy);
if (!viewer_node) {
return OPERATOR_CANCELLED;
}
link = nullptr;
}
else {
/* get link to viewer */
for (link = (bNodeLink *)snode->edittree->links.first; link; link = link->next) {
if (link->tonode == viewer_node && link->tosock == viewer_node->inputs.first) {
break;
}
}
}
if (link == nullptr) {
nodeAddLink(
snode->edittree, tonode, sock, viewer_node, (bNodeSocket *)viewer_node->inputs.first);
}
else {
link->fromnode = tonode;
link->fromsock = sock;
/* make sure the dependency sorting is updated */
snode->edittree->update |= NTREE_UPDATE_LINKS;
}
if (ED_node_is_geometry(snode)) {
ED_spreadsheet_context_paths_set_geometry_node(CTX_data_main(C), snode, viewer_node);
}
ntreeUpdateTree(CTX_data_main(C), snode->edittree);
snode_update(snode, viewer_node);
DEG_id_tag_update(&snode->edittree->id, 0);
}
ntreeUpdateTree(CTX_data_main(C), btree);
snode_update(snode, viewer_bnode);
DEG_id_tag_update(&btree->id, 0);
return OPERATOR_FINISHED;
}
static int node_link_viewer(const bContext *C, bNode *bnode_to_view)
{
if (bnode_to_view == nullptr) {
return OPERATOR_CANCELLED;
}
SpaceNode *snode = CTX_wm_space_node(C);
bNodeTree *btree = snode->edittree;
const NodeTreeRef tree{btree};
const NodeRef &node_to_view = *tree.find_node(*bnode_to_view);
const NodeRef *active_viewer_node = get_existing_viewer(tree);
const OutputSocketRef *socket_to_view = find_output_socket_to_be_viewed(active_viewer_node,
node_to_view);
if (socket_to_view == nullptr) {
return OPERATOR_FINISHED;
}
bNodeSocket *bsocket_to_view = socket_to_view->bsocket();
bNode *viewer_bnode = active_viewer_node ? active_viewer_node->bnode() : nullptr;
return link_socket_to_viewer(C, viewer_bnode, bnode_to_view, bsocket_to_view);
}
} // namespace blender::ed::nodes::viewer_linking
static int node_active_link_viewer_exec(bContext *C, wmOperator *UNUSED(op))
{
SpaceNode *snode = CTX_wm_space_node(C);
@ -777,7 +895,7 @@ static int node_active_link_viewer_exec(bContext *C, wmOperator *UNUSED(op))
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
if (node_link_viewer(C, node) == OPERATOR_CANCELLED) {
if (blender::ed::nodes::viewer_linking::node_link_viewer(C, node) == OPERATOR_CANCELLED) {
return OPERATOR_CANCELLED;
}

View File

@ -35,6 +35,7 @@ set(INC
set(SRC
space_spreadsheet.cc
spreadsheet_cache.cc
spreadsheet_column.cc
spreadsheet_context.cc
spreadsheet_data_source.cc
@ -47,6 +48,7 @@ set(SRC
spreadsheet_row_filter.cc
spreadsheet_row_filter_ui.cc
spreadsheet_cache.hh
spreadsheet_cell_value.hh
spreadsheet_column.hh
spreadsheet_column_values.hh

View File

@ -112,7 +112,7 @@ static void spreadsheet_free(SpaceLink *sl)
{
SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl;
MEM_SAFE_FREE(sspreadsheet->runtime);
delete sspreadsheet->runtime;
LISTBASE_FOREACH_MUTABLE (SpreadsheetRowFilter *, row_filter, &sspreadsheet->row_filters) {
spreadsheet_row_filter_free(row_filter);
@ -129,8 +129,7 @@ static void spreadsheet_init(wmWindowManager *UNUSED(wm), ScrArea *area)
{
SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)area->spacedata.first;
if (sspreadsheet->runtime == nullptr) {
sspreadsheet->runtime = (SpaceSpreadsheet_Runtime *)MEM_callocN(
sizeof(SpaceSpreadsheet_Runtime), __func__);
sspreadsheet->runtime = new SpaceSpreadsheet_Runtime();
}
}
@ -138,7 +137,7 @@ static SpaceLink *spreadsheet_duplicate(SpaceLink *sl)
{
const SpaceSpreadsheet *sspreadsheet_old = (SpaceSpreadsheet *)sl;
SpaceSpreadsheet *sspreadsheet_new = (SpaceSpreadsheet *)MEM_dupallocN(sspreadsheet_old);
sspreadsheet_new->runtime = (SpaceSpreadsheet_Runtime *)MEM_dupallocN(sspreadsheet_old->runtime);
sspreadsheet_new->runtime = new SpaceSpreadsheet_Runtime(*sspreadsheet_old->runtime);
BLI_listbase_clear(&sspreadsheet_new->row_filters);
LISTBASE_FOREACH (const SpreadsheetRowFilter *, src_filter, &sspreadsheet_old->row_filters) {
@ -294,16 +293,39 @@ static std::unique_ptr<DataSource> get_data_source(const bContext *C)
return {};
}
static float get_column_width(const ColumnValues &values)
static float get_default_column_width(const ColumnValues &values)
{
if (values.default_width > 0) {
if (values.default_width > 0.0f) {
return values.default_width;
}
static const float float_width = 3;
switch (values.type()) {
case SPREADSHEET_VALUE_TYPE_BOOL:
return 2.0f;
case SPREADSHEET_VALUE_TYPE_INT32:
return float_width;
case SPREADSHEET_VALUE_TYPE_FLOAT:
return float_width;
case SPREADSHEET_VALUE_TYPE_FLOAT2:
return 2.0f * float_width;
case SPREADSHEET_VALUE_TYPE_FLOAT3:
return 3.0f * float_width;
case SPREADSHEET_VALUE_TYPE_COLOR:
return 4.0f * float_width;
case SPREADSHEET_VALUE_TYPE_INSTANCES:
return 8.0f;
}
return float_width;
}
static float get_column_width(const ColumnValues &values)
{
float data_width = get_default_column_width(values);
const int fontid = UI_style_get()->widget.uifont_id;
BLF_size(fontid, UI_DEFAULT_TEXT_POINTS, U.dpi);
const StringRefNull name = values.name();
const float name_width = BLF_width(fontid, name.data(), name.size());
return std::max<float>(name_width / UI_UNIT_X + 1.0f, 3.0f);
return std::max<float>(name_width / UI_UNIT_X + 1.0f, data_width);
}
static float get_column_width_in_pixels(const ColumnValues &values)
@ -339,21 +361,28 @@ static void update_visible_columns(ListBase &columns, DataSource &data_source)
}
}
data_source.foreach_default_column_ids([&](const SpreadsheetColumnID &column_id) {
std::unique_ptr<ColumnValues> values = data_source.get_column_values(column_id);
if (values) {
if (used_ids.add(column_id)) {
SpreadsheetColumnID *new_id = spreadsheet_column_id_copy(&column_id);
SpreadsheetColumn *new_column = spreadsheet_column_new(new_id);
BLI_addtail(&columns, new_column);
}
}
});
data_source.foreach_default_column_ids(
[&](const SpreadsheetColumnID &column_id, const bool is_extra) {
std::unique_ptr<ColumnValues> values = data_source.get_column_values(column_id);
if (values) {
if (used_ids.add(column_id)) {
SpreadsheetColumnID *new_id = spreadsheet_column_id_copy(&column_id);
SpreadsheetColumn *new_column = spreadsheet_column_new(new_id);
if (is_extra) {
BLI_addhead(&columns, new_column);
}
else {
BLI_addtail(&columns, new_column);
}
}
}
});
}
static void spreadsheet_main_region_draw(const bContext *C, ARegion *region)
{
SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
sspreadsheet->runtime->cache.set_all_unused();
spreadsheet_update_context_path(C);
std::unique_ptr<DataSource> data_source = get_data_source(C);
@ -394,6 +423,9 @@ static void spreadsheet_main_region_draw(const bContext *C, ARegion *region)
ED_region_tag_redraw(footer);
ARegion *sidebar = BKE_area_find_region_type(CTX_wm_area(C), RGN_TYPE_UI);
ED_region_tag_redraw(sidebar);
/* Free all cache items that have not been used. */
sspreadsheet->runtime->cache.remove_all_unused();
}
static void spreadsheet_main_region_listener(const wmRegionListenerParams *params)

View File

@ -0,0 +1,79 @@
/*
* 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.
*/
#include "spreadsheet_cache.hh"
namespace blender::ed::spreadsheet {
void SpreadsheetCache::add(std::unique_ptr<Key> key, std::unique_ptr<Value> value)
{
key->is_used = true;
cache_map_.add_overwrite(*key, std::move(value));
keys_.append(std::move(key));
}
SpreadsheetCache::Value *SpreadsheetCache::lookup(const Key &key)
{
std::unique_ptr<Value> *value = cache_map_.lookup_ptr(key);
if (value == nullptr) {
return nullptr;
}
const Key &stored_cache_key = cache_map_.lookup_key(key);
stored_cache_key.is_used = true;
return value->get();
}
SpreadsheetCache::Value &SpreadsheetCache::lookup_or_add(
std::unique_ptr<Key> key, FunctionRef<std::unique_ptr<Value>()> create_value)
{
Value *value = this->lookup(*key);
if (value != nullptr) {
return *value;
}
std::unique_ptr<Value> new_value = create_value();
value = new_value.get();
this->add(std::move(key), std::move(new_value));
return *value;
}
void SpreadsheetCache::set_all_unused()
{
for (std::unique_ptr<Key> &key : keys_) {
key->is_used = false;
}
}
void SpreadsheetCache::remove_all_unused()
{
/* First remove the keys from the map and free the values. */
for (auto it = cache_map_.keys().begin(); it != cache_map_.keys().end(); ++it) {
const Key &key = *it;
if (!key.is_used) {
cache_map_.remove(it);
}
}
/* Then free the keys. */
for (int i = 0; i < keys_.size();) {
if (keys_[i]->is_used) {
i++;
}
else {
keys_.remove_and_reorder(i);
}
}
}
} // namespace blender::ed::spreadsheet

View File

@ -0,0 +1,78 @@
/*
* 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.
*/
#pragma once
#include <atomic>
#include "BLI_function_ref.hh"
#include "BLI_map.hh"
#include "BLI_vector.hh"
namespace blender::ed::spreadsheet {
/**
* A generic cache for the spreadsheet. Different data sources can cache custom data using custom
* keys.
*
* Elements are removed from the cache when they are not used during a redraw.
*/
class SpreadsheetCache {
public:
class Key {
public:
virtual ~Key() = default;
mutable bool is_used = false;
virtual uint64_t hash() const = 0;
friend bool operator==(const Key &a, const Key &b)
{
return a.is_equal_to(b);
}
private:
virtual bool is_equal_to(const Key &other) const = 0;
};
class Value {
public:
virtual ~Value() = default;
};
private:
Vector<std::unique_ptr<Key>> keys_;
Map<std::reference_wrapper<const Key>, std::unique_ptr<Value>> cache_map_;
public:
/* Adding or looking up a key tags it as being used, so that it won't be removed. */
void add(std::unique_ptr<Key> key, std::unique_ptr<Value> value);
Value *lookup(const Key &key);
Value &lookup_or_add(std::unique_ptr<Key> key,
FunctionRef<std::unique_ptr<Value>()> create_value);
void set_all_unused();
void remove_all_unused();
template<typename T> T &lookup_or_add(std::unique_ptr<Key> key)
{
return dynamic_cast<T &>(
this->lookup_or_add(std::move(key), []() { return std::make_unique<T>(); }));
}
};
} // namespace blender::ed::spreadsheet

View File

@ -97,9 +97,4 @@ std::unique_ptr<ColumnValues> column_values_from_function(const eSpreadsheetColu
return column_values;
}
static constexpr float default_float_column_width = 3;
static constexpr float default_float2_column_width = 2 * default_float_column_width;
static constexpr float default_float3_column_width = 3 * default_float_column_width;
static constexpr float default_color_column_width = 4 * default_float_column_width;
} // namespace blender::ed::spreadsheet

View File

@ -36,8 +36,12 @@ class DataSource {
* Calls the callback with all the column ids that should be displayed as long as the user does
* not manually add or remove columns. The column id can be stack allocated. Therefore, the
* callback should not keep a reference to it (and copy it instead).
*
* The `is_extra` argument indicates that this column is special and should be drawn as the first
* column. (This can be made a bit more generic in the future when necessary.)
*/
virtual void foreach_default_column_ids(FunctionRef<void(const SpreadsheetColumnID &)> fn) const
virtual void foreach_default_column_ids(
FunctionRef<void(const SpreadsheetColumnID &, bool is_extra)> fn) const
{
UNUSED_VARS(fn);
}

View File

@ -33,18 +33,99 @@
#include "NOD_geometry_nodes_eval_log.hh"
#include "FN_field_cpp_type.hh"
#include "bmesh.h"
#include "spreadsheet_data_source_geometry.hh"
#include "spreadsheet_intern.hh"
namespace geo_log = blender::nodes::geometry_nodes_eval_log;
using blender::fn::GField;
namespace blender::ed::spreadsheet {
void GeometryDataSource::foreach_default_column_ids(
FunctionRef<void(const SpreadsheetColumnID &)> fn) const
static std::optional<eSpreadsheetColumnValueType> cpp_type_to_column_value_type(
const fn::CPPType &type)
{
if (type.is<bool>()) {
return SPREADSHEET_VALUE_TYPE_BOOL;
}
if (type.is<int>()) {
return SPREADSHEET_VALUE_TYPE_INT32;
}
if (type.is<float>()) {
return SPREADSHEET_VALUE_TYPE_FLOAT;
}
if (type.is<float2>()) {
return SPREADSHEET_VALUE_TYPE_FLOAT2;
}
if (type.is<float3>()) {
return SPREADSHEET_VALUE_TYPE_FLOAT3;
}
if (type.is<ColorGeometry4f>()) {
return SPREADSHEET_VALUE_TYPE_COLOR;
}
return std::nullopt;
}
void ExtraColumns::foreach_default_column_ids(
FunctionRef<void(const SpreadsheetColumnID &, bool is_extra)> fn) const
{
for (const auto &item : columns_.items()) {
SpreadsheetColumnID column_id;
column_id.name = (char *)item.key.c_str();
fn(column_id, true);
}
}
std::unique_ptr<ColumnValues> ExtraColumns::get_column_values(
const SpreadsheetColumnID &column_id) const
{
const fn::GSpan *values = columns_.lookup_ptr(column_id.name);
if (values == nullptr) {
return {};
}
eSpreadsheetColumnValueType column_type = *cpp_type_to_column_value_type(values->type());
return column_values_from_function(column_type,
column_id.name,
values->size(),
[column_type, values](int index, CellValue &r_cell_value) {
const void *value = (*values)[index];
switch (column_type) {
case SPREADSHEET_VALUE_TYPE_BOOL:
r_cell_value.value_bool = *(const bool *)value;
break;
case SPREADSHEET_VALUE_TYPE_INT32:
r_cell_value.value_int = *(const int *)value;
break;
case SPREADSHEET_VALUE_TYPE_FLOAT:
r_cell_value.value_float = *(const float *)value;
break;
case SPREADSHEET_VALUE_TYPE_FLOAT2:
r_cell_value.value_float2 = *(const float2 *)value;
break;
case SPREADSHEET_VALUE_TYPE_FLOAT3:
r_cell_value.value_float3 = *(const float3 *)value;
break;
case SPREADSHEET_VALUE_TYPE_COLOR:
r_cell_value.value_color = *(
const ColorGeometry4f *)value;
break;
case SPREADSHEET_VALUE_TYPE_INSTANCES:
break;
}
});
}
void GeometryDataSource::foreach_default_column_ids(
FunctionRef<void(const SpreadsheetColumnID &, bool is_extra)> fn) const
{
if (component_->attribute_domain_size(domain_) == 0) {
return;
}
extra_columns_.foreach_default_column_ids(fn);
component_->attribute_foreach(
[&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
if (meta_data.domain != domain_) {
@ -55,7 +136,7 @@ void GeometryDataSource::foreach_default_column_ids(
}
SpreadsheetColumnID column_id;
column_id.name = (char *)attribute_id.name().data();
fn(column_id);
fn(column_id, false);
return true;
});
}
@ -63,8 +144,17 @@ void GeometryDataSource::foreach_default_column_ids(
std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values(
const SpreadsheetColumnID &column_id) const
{
if (component_->attribute_domain_size(domain_) == 0) {
return {};
}
std::lock_guard lock{mutex_};
std::unique_ptr<ColumnValues> extra_column_values = extra_columns_.get_column_values(column_id);
if (extra_column_values) {
return extra_column_values;
}
bke::ReadAttributeLookup attribute = component_->attribute_try_get_for_read(column_id.name);
if (!attribute) {
return {};
@ -104,40 +194,34 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values(
r_cell_value.value_bool = value;
});
case CD_PROP_FLOAT2: {
return column_values_from_function(
SPREADSHEET_VALUE_TYPE_FLOAT2,
column_id.name,
domain_size,
[varray](int index, CellValue &r_cell_value) {
float2 value;
varray->get(index, &value);
r_cell_value.value_float2 = value;
},
default_float2_column_width);
return column_values_from_function(SPREADSHEET_VALUE_TYPE_FLOAT2,
column_id.name,
domain_size,
[varray](int index, CellValue &r_cell_value) {
float2 value;
varray->get(index, &value);
r_cell_value.value_float2 = value;
});
}
case CD_PROP_FLOAT3: {
return column_values_from_function(
SPREADSHEET_VALUE_TYPE_FLOAT3,
column_id.name,
domain_size,
[varray](int index, CellValue &r_cell_value) {
float3 value;
varray->get(index, &value);
r_cell_value.value_float3 = value;
},
default_float3_column_width);
return column_values_from_function(SPREADSHEET_VALUE_TYPE_FLOAT3,
column_id.name,
domain_size,
[varray](int index, CellValue &r_cell_value) {
float3 value;
varray->get(index, &value);
r_cell_value.value_float3 = value;
});
}
case CD_PROP_COLOR: {
return column_values_from_function(
SPREADSHEET_VALUE_TYPE_COLOR,
column_id.name,
domain_size,
[varray](int index, CellValue &r_cell_value) {
ColorGeometry4f value;
varray->get(index, &value);
r_cell_value.value_color = value;
},
default_color_column_width);
return column_values_from_function(SPREADSHEET_VALUE_TYPE_COLOR,
column_id.name,
domain_size,
[varray](int index, CellValue &r_cell_value) {
ColorGeometry4f value;
varray->get(index, &value);
r_cell_value.value_color = value;
});
}
default:
break;
@ -293,18 +377,20 @@ void GeometryDataSource::apply_selection_filter(MutableSpan<bool> rows_included)
}
void InstancesDataSource::foreach_default_column_ids(
FunctionRef<void(const SpreadsheetColumnID &)> fn) const
FunctionRef<void(const SpreadsheetColumnID &, bool is_extra)> fn) const
{
if (component_->instances_amount() == 0) {
return;
}
extra_columns_.foreach_default_column_ids(fn);
SpreadsheetColumnID column_id;
column_id.name = (char *)"Name";
fn(column_id);
fn(column_id, false);
for (const char *name : {"Position", "Rotation", "Scale", "ID"}) {
column_id.name = (char *)name;
fn(column_id);
fn(column_id, false);
}
}
@ -315,6 +401,11 @@ std::unique_ptr<ColumnValues> InstancesDataSource::get_column_values(
return {};
}
std::unique_ptr<ColumnValues> extra_column_values = extra_columns_.get_column_values(column_id);
if (extra_column_values) {
return extra_column_values;
}
const int size = this->tot_rows();
if (STREQ(column_id.name, "Name")) {
Span<int> reference_handles = component_->instance_reference_handles();
@ -346,7 +437,6 @@ std::unique_ptr<ColumnValues> InstancesDataSource::get_column_values(
}
}
});
values->default_width = 8.0f;
return values;
}
Span<float4x4> transforms = component_->instance_transforms();
@ -357,28 +447,23 @@ std::unique_ptr<ColumnValues> InstancesDataSource::get_column_values(
size,
[transforms](int index, CellValue &r_cell_value) {
r_cell_value.value_float3 = transforms[index].translation();
},
default_float3_column_width);
});
}
if (STREQ(column_id.name, "Rotation")) {
return column_values_from_function(
SPREADSHEET_VALUE_TYPE_FLOAT3,
column_id.name,
size,
[transforms](int index, CellValue &r_cell_value) {
r_cell_value.value_float3 = transforms[index].to_euler();
},
default_float3_column_width);
return column_values_from_function(SPREADSHEET_VALUE_TYPE_FLOAT3,
column_id.name,
size,
[transforms](int index, CellValue &r_cell_value) {
r_cell_value.value_float3 = transforms[index].to_euler();
});
}
if (STREQ(column_id.name, "Scale")) {
return column_values_from_function(
SPREADSHEET_VALUE_TYPE_FLOAT3,
column_id.name,
size,
[transforms](int index, CellValue &r_cell_value) {
r_cell_value.value_float3 = transforms[index].scale();
},
default_float3_column_width);
return column_values_from_function(SPREADSHEET_VALUE_TYPE_FLOAT3,
column_id.name,
size,
[transforms](int index, CellValue &r_cell_value) {
r_cell_value.value_float3 = transforms[index].scale();
});
}
Span<int> ids = component_->instance_ids();
if (STREQ(column_id.name, "ID")) {
@ -469,6 +554,38 @@ GeometrySet spreadsheet_get_display_geometry_set(const SpaceSpreadsheet *sspread
return geometry_set;
}
static void find_fields_to_evaluate(const SpaceSpreadsheet *sspreadsheet,
Map<std::string, GField> &r_fields)
{
if (sspreadsheet->object_eval_state != SPREADSHEET_OBJECT_EVAL_STATE_VIEWER_NODE) {
return;
}
if (BLI_listbase_count(&sspreadsheet->context_path) <= 1) {
/* No viewer is currently referenced by the context path. */
return;
}
const geo_log::NodeLog *node_log = geo_log::ModifierLog::find_node_by_spreadsheet_editor_context(
*sspreadsheet);
if (node_log == nullptr) {
return;
}
for (const geo_log::SocketLog &socket_log : node_log->input_logs()) {
const geo_log::ValueLog *value_log = socket_log.value();
if (value_log == nullptr) {
continue;
}
if (const geo_log::GenericValueLog *generic_value_log =
dynamic_cast<const geo_log::GenericValueLog *>(value_log)) {
const fn::GPointer value = generic_value_log->value();
if (!dynamic_cast<const fn::FieldCPPType *>(value.type())) {
continue;
}
GField field = *(const GField *)value.get();
r_fields.add("Viewer", std::move(field));
}
}
}
static GeometryComponentType get_display_component_type(const bContext *C, Object *object_eval)
{
SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
@ -481,6 +598,69 @@ static GeometryComponentType get_display_component_type(const bContext *C, Objec
return GEO_COMPONENT_TYPE_MESH;
}
class GeometryComponentCacheKey : public SpreadsheetCache::Key {
public:
/* Use the pointer to the geometry component as a key to detect when the geometry changed. */
const GeometryComponent *component;
GeometryComponentCacheKey(const GeometryComponent &component) : component(&component)
{
}
uint64_t hash() const override
{
return get_default_hash(this->component);
}
bool is_equal_to(const Key &other) const override
{
if (const GeometryComponentCacheKey *other_geo =
dynamic_cast<const GeometryComponentCacheKey *>(&other)) {
return this->component == other_geo->component;
}
return false;
}
};
class GeometryComponentCacheValue : public SpreadsheetCache::Value {
public:
/* Stores the result of fields evaluated on a geometry component. Without this, fields would have
* to be reevaluated on every redraw. */
Map<std::pair<AttributeDomain, GField>, fn::GArray<>> arrays;
};
static void add_fields_as_extra_columns(SpaceSpreadsheet *sspreadsheet,
const GeometryComponent &component,
ExtraColumns &r_extra_columns)
{
Map<std::string, GField> fields_to_show;
find_fields_to_evaluate(sspreadsheet, fields_to_show);
GeometryComponentCacheValue &cache =
sspreadsheet->runtime->cache.lookup_or_add<GeometryComponentCacheValue>(
std::make_unique<GeometryComponentCacheKey>(component));
const AttributeDomain domain = (AttributeDomain)sspreadsheet->attribute_domain;
const int domain_size = component.attribute_domain_size(domain);
for (const auto &item : fields_to_show.items()) {
StringRef name = item.key;
const GField &field = item.value;
/* Use the cached evaluated array if it exists, otherwise evaluate the field now. */
fn::GArray<> &evaluated_array = cache.arrays.lookup_or_add_cb({domain, field}, [&]() {
fn::GArray<> evaluated_array(field.cpp_type(), domain_size);
bke::GeometryComponentFieldContext field_context{component, domain};
fn::FieldEvaluator field_evaluator{field_context, domain_size};
field_evaluator.add_with_destination(field, evaluated_array);
field_evaluator.evaluate();
return evaluated_array;
});
r_extra_columns.add(std::move(name), evaluated_array.as_span());
}
}
std::unique_ptr<DataSource> data_source_from_geometry(const bContext *C, Object *object_eval)
{
SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
@ -493,10 +673,15 @@ std::unique_ptr<DataSource> data_source_from_geometry(const bContext *C, Object
return {};
}
const GeometryComponent &component = *geometry_set.get_component_for_read(component_type);
ExtraColumns extra_columns;
add_fields_as_extra_columns(sspreadsheet, component, extra_columns);
if (component_type == GEO_COMPONENT_TYPE_INSTANCES) {
return std::make_unique<InstancesDataSource>(geometry_set);
return std::make_unique<InstancesDataSource>(geometry_set, std::move(extra_columns));
}
return std::make_unique<GeometryDataSource>(object_eval, geometry_set, component_type, domain);
return std::make_unique<GeometryDataSource>(
object_eval, geometry_set, component_type, domain, std::move(extra_columns));
}
} // namespace blender::ed::spreadsheet

View File

@ -28,12 +28,34 @@ struct bContext;
namespace blender::ed::spreadsheet {
/**
* Contains additional named columns that should be displayed that are not stored on the geometry
* directly. This is used for displaying the evaluated fields connected to a viewer node.
*/
class ExtraColumns {
private:
/** Maps column names to their data. The data is actually stored in the spreadsheet cache. */
Map<std::string, fn::GSpan> columns_;
public:
void add(std::string name, fn::GSpan data)
{
columns_.add(std::move(name), data);
}
void foreach_default_column_ids(
FunctionRef<void(const SpreadsheetColumnID &, bool is_extra)> fn) const;
std::unique_ptr<ColumnValues> get_column_values(const SpreadsheetColumnID &column_id) const;
};
class GeometryDataSource : public DataSource {
private:
Object *object_eval_;
const GeometrySet geometry_set_;
const GeometryComponent *component_;
AttributeDomain domain_;
ExtraColumns extra_columns_;
/* Some data is computed on the fly only when it is requested. Computing it does not change the
* logical state of this data source. Therefore, the corresponding methods are const and need to
@ -45,11 +67,13 @@ class GeometryDataSource : public DataSource {
GeometryDataSource(Object *object_eval,
GeometrySet geometry_set,
const GeometryComponentType component_type,
const AttributeDomain domain)
const AttributeDomain domain,
ExtraColumns extra_columns)
: object_eval_(object_eval),
geometry_set_(std::move(geometry_set)),
component_(geometry_set_.get_component_for_read(component_type)),
domain_(domain)
domain_(domain),
extra_columns_(std::move(extra_columns))
{
}
@ -62,7 +86,7 @@ class GeometryDataSource : public DataSource {
void apply_selection_filter(MutableSpan<bool> rows_included) const;
void foreach_default_column_ids(
FunctionRef<void(const SpreadsheetColumnID &)> fn) const override;
FunctionRef<void(const SpreadsheetColumnID &, bool is_extra)> fn) const override;
std::unique_ptr<ColumnValues> get_column_values(
const SpreadsheetColumnID &column_id) const override;
@ -73,16 +97,18 @@ class GeometryDataSource : public DataSource {
class InstancesDataSource : public DataSource {
const GeometrySet geometry_set_;
const InstancesComponent *component_;
ExtraColumns extra_columns_;
public:
InstancesDataSource(GeometrySet geometry_set)
InstancesDataSource(GeometrySet geometry_set, ExtraColumns extra_columns)
: geometry_set_(std::move(geometry_set)),
component_(geometry_set_.get_component_for_read<InstancesComponent>())
component_(geometry_set_.get_component_for_read<InstancesComponent>()),
extra_columns_(std::move(extra_columns))
{
}
void foreach_default_column_ids(
FunctionRef<void(const SpreadsheetColumnID &)> fn) const override;
FunctionRef<void(const SpreadsheetColumnID &, bool is_extra)> fn) const override;
std::unique_ptr<ColumnValues> get_column_values(
const SpreadsheetColumnID &column_id) const override;

View File

@ -17,12 +17,24 @@
#pragma once
#include "BKE_geometry_set.hh"
#include "spreadsheet_cache.hh"
typedef struct SpaceSpreadsheet_Runtime {
int visible_rows;
int tot_rows;
int tot_columns;
} SpaceSpreadsheet_Runtime;
struct SpaceSpreadsheet_Runtime {
public:
int visible_rows = 0;
int tot_rows = 0;
int tot_columns = 0;
blender::ed::spreadsheet::SpreadsheetCache cache;
SpaceSpreadsheet_Runtime() = default;
/* The cache is not copied currently. */
SpaceSpreadsheet_Runtime(const SpaceSpreadsheet_Runtime &other)
: visible_rows(other.visible_rows), tot_rows(other.tot_rows), tot_columns(other.tot_columns)
{
}
};
struct bContext;

View File

@ -93,7 +93,9 @@ class SpreadsheetLayoutDrawer : public SpreadsheetDrawer {
const int real_index = spreadsheet_layout_.row_indices[row_index];
const ColumnValues &column = *spreadsheet_layout_.columns[column_index].values;
CellValue cell_value;
column.get_value(real_index, cell_value);
if (real_index < column.size()) {
column.get_value(real_index, cell_value);
}
if (cell_value.value_int.has_value()) {
const int value = *cell_value.value_int;

View File

@ -1586,6 +1586,11 @@ typedef struct NodeGeometryImageTexture {
int extension;
} NodeGeometryImageTexture;
typedef struct NodeGeometryViewer {
/* CustomDataType. */
int8_t data_type;
} NodeGeometryViewer;
/* script node mode */
#define NODE_SCRIPT_INTERNAL 0
#define NODE_SCRIPT_EXTERNAL 1

View File

@ -11076,6 +11076,20 @@ static void def_geo_separate_geometry(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
static void def_geo_viewer(StructRNA *srna)
{
PropertyRNA *prop;
RNA_def_struct_sdna_from(srna, "NodeGeometryViewer", "storage");
prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, rna_enum_attribute_type_items);
RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeFill_type_itemf");
RNA_def_property_enum_default(prop, CD_PROP_FLOAT);
RNA_def_property_ui_text(prop, "Data Type", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update");
}
/* -------------------------------------------------------------------------- */
static void rna_def_shader_node(BlenderRNA *brna)

View File

@ -413,7 +413,7 @@ DefNode(GeometryNode, GEO_NODE_TRANSFER_ATTRIBUTE, def_geo_transfer_attribute, "
DefNode(GeometryNode, GEO_NODE_TRANSFORM, 0, "TRANSFORM", Transform, "Transform", "")
DefNode(GeometryNode, GEO_NODE_TRANSLATE_INSTANCES, 0, "TRANSLATE_INSTANCES", TranslateInstances, "Translate Instances", "")
DefNode(GeometryNode, GEO_NODE_TRIANGULATE, def_geo_triangulate, "TRIANGULATE", Triangulate, "Triangulate", "")
DefNode(GeometryNode, GEO_NODE_VIEWER, 0, "VIEWER", Viewer, "Viewer", "")
DefNode(GeometryNode, GEO_NODE_VIEWER, def_geo_viewer, "VIEWER", Viewer, "Viewer", "")
DefNode(GeometryNode, GEO_NODE_VOLUME_TO_MESH, def_geo_volume_to_mesh, "VOLUME_TO_MESH", VolumeToMesh, "Volume to Mesh", "")
/* undefine macros */

View File

@ -14,13 +14,69 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "UI_interface.h"
#include "UI_resources.h"
#include "node_geometry_util.hh"
namespace blender::nodes {
static void geo_node_viewer_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Geometry>("Geometry");
b.add_input<decl::Float>("Value").supports_field().hide_value();
b.add_input<decl::Vector>("Value", "Value_001").supports_field().hide_value();
b.add_input<decl::Color>("Value", "Value_002").supports_field().hide_value();
b.add_input<decl::Int>("Value", "Value_003").supports_field().hide_value();
b.add_input<decl::Bool>("Value", "Value_004").supports_field().hide_value();
}
static void geo_node_viewer_init(bNodeTree *UNUSED(tree), bNode *node)
{
NodeGeometryViewer *data = (NodeGeometryViewer *)MEM_callocN(sizeof(NodeGeometryViewer),
__func__);
data->data_type = CD_PROP_FLOAT;
node->storage = data;
}
static void geo_node_viewer_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE);
}
static eNodeSocketDatatype custom_data_type_to_socket_type(const CustomDataType type)
{
switch (type) {
case CD_PROP_FLOAT:
return SOCK_FLOAT;
case CD_PROP_INT32:
return SOCK_INT;
case CD_PROP_FLOAT3:
return SOCK_VECTOR;
case CD_PROP_BOOL:
return SOCK_BOOLEAN;
case CD_PROP_COLOR:
return SOCK_RGBA;
default:
BLI_assert_unreachable();
return SOCK_FLOAT;
}
}
static void geo_node_viewer_update(bNodeTree *UNUSED(ntree), bNode *node)
{
const NodeGeometryViewer &storage = *(const NodeGeometryViewer *)node->storage;
const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type);
const eNodeSocketDatatype socket_type = custom_data_type_to_socket_type(data_type);
LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) {
if (socket->type == SOCK_GEOMETRY) {
continue;
}
nodeSetSocketAvailability(socket, socket->type == socket_type);
}
}
} // namespace blender::nodes
void register_node_type_geo_viewer()
@ -28,6 +84,11 @@ void register_node_type_geo_viewer()
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_VIEWER, "Viewer", NODE_CLASS_OUTPUT, 0);
node_type_storage(
&ntype, "NodeGeometryViewer", node_free_standard_storage, node_copy_standard_storage);
node_type_update(&ntype, blender::nodes::geo_node_viewer_update);
node_type_init(&ntype, blender::nodes::geo_node_viewer_init);
ntype.declare = blender::nodes::geo_node_viewer_declare;
ntype.draw_buttons_ex = blender::nodes::geo_node_viewer_layout;
nodeRegisterType(&ntype);
}