Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source/blender/blenloader/intern/versioning_300.c17
-rw-r--r--source/blender/editors/space_node/node_relationships.cc354
-rw-r--r--source/blender/editors/space_spreadsheet/CMakeLists.txt2
-rw-r--r--source/blender/editors/space_spreadsheet/space_spreadsheet.cc66
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_cache.cc79
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_cache.hh78
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh5
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_data_source.hh6
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc297
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh38
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_intern.hh22
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_layout.cc4
-rw-r--r--source/blender/makesdna/DNA_node_types.h5
-rw-r--r--source/blender/makesrna/intern/rna_nodetree.c14
-rw-r--r--source/blender/nodes/NOD_static_types.h2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_viewer.cc61
16 files changed, 840 insertions, 210 deletions
diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c
index 044659e4fbe..57447db8723 100644
--- a/source/blender/blenloader/intern/versioning_300.c
+++ b/source/blender/blenloader/intern/versioning_300.c
@@ -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;
+ }
+ }
+ }
+ }
}
}
diff --git a/source/blender/editors/space_node/node_relationships.cc b/source/blender/editors/space_node/node_relationships.cc
index b69e7e98bca..459608a67ea 100644
--- a/source/blender/editors/space_node/node_relationships.cc
+++ b/source/blender/editors/space_node/node_relationships.cc
@@ -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)
-{
- SpaceNode *snode = CTX_wm_space_node(C);
+namespace blender::ed::nodes::viewer_linking {
- /* context check */
- if (tonode == nullptr || BLI_listbase_is_empty(&tonode->outputs)) {
- return OPERATOR_CANCELLED;
+/* 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 (ELEM(tonode->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER, GEO_NODE_VIEWER)) {
- return OPERATOR_CANCELLED;
+ 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);
+}
- /* 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;
- }
- }
+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;
}
- /* 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;
+}
+
+/**
+ * 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;
+}
- bNodeSocket *sock = nullptr;
- bNodeLink *link = nullptr;
-
- /* try to find an already connected socket to cycle to the next */
- if (viewer_node) {
- link = nullptr;
+static bool is_viewer_node(const NodeRef &node)
+{
+ return ELEM(node.bnode()->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER, GEO_NODE_VIEWER);
+}
- 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;
- }
- }
+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);
}
- if (link) {
- /* unlink existing connection */
- sock = link->fromsock;
- nodeRemLink(snode->edittree, link);
+ }
+ return viewer_nodes;
+}
- /* 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;
- }
- }
- }
+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;
+}
- 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;
- }
+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;
+}
+
+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);
}
}
- else {
- LISTBASE_FOREACH (bNodeSocket *, sock2, &tonode->outputs) {
- if (!nodeSocketIsHidden(sock2) && sock2->flag & SELECT) {
- sock = sock2;
- break;
- }
- }
+ }
+}
+
+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;
}
}
- /* 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;
- }
+ /* 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;
}
- }
- else {
- for (sock = (bNodeSocket *)tonode->outputs.first; sock; sock = sock->next) {
- if (!nodeSocketIsHidden(sock)) {
- break;
- }
+ if (is_linked_to_viewer(*output_socket, *active_viewer_node)) {
+ last_socket_linked_to_viewer = output_socket;
}
}
}
-
- 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;
+ 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;
}
-
- 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;
- }
+ }
+ 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;
+}
- 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;
+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;
}
- if (ED_node_is_geometry(snode)) {
- ED_spreadsheet_context_paths_set_geometry_node(CTX_data_main(C), snode, viewer_node);
+ }
+
+ bNodeSocket *viewer_bsocket = node_link_viewer_get_socket(btree, viewer_bnode, bsocket_to_view);
+ if (viewer_bsocket == nullptr) {
+ return OPERATOR_CANCELLED;
+ }
+
+ bNodeLink *link_to_change = nullptr;
+ LISTBASE_FOREACH (bNodeLink *, link, &btree->links) {
+ if (link->tosock == viewer_bsocket) {
+ link_to_change = link;
+ break;
}
+ }
- ntreeUpdateTree(CTX_data_main(C), snode->edittree);
- snode_update(snode, viewer_node);
- DEG_id_tag_update(&snode->edittree->id, 0);
+ 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;
}
+ 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);
+ }
+
+ 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;
}
diff --git a/source/blender/editors/space_spreadsheet/CMakeLists.txt b/source/blender/editors/space_spreadsheet/CMakeLists.txt
index e903feeec1b..91fe1bc01b7 100644
--- a/source/blender/editors/space_spreadsheet/CMakeLists.txt
+++ b/source/blender/editors/space_spreadsheet/CMakeLists.txt
@@ -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
diff --git a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc
index a82648aeee0..73e0be76466 100644
--- a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc
+++ b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc
@@ -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)
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_cache.cc b/source/blender/editors/space_spreadsheet/spreadsheet_cache.cc
new file mode 100644
index 00000000000..2a399e018b6
--- /dev/null
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_cache.cc
@@ -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
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_cache.hh b/source/blender/editors/space_spreadsheet/spreadsheet_cache.hh
new file mode 100644
index 00000000000..d370bdab5c1
--- /dev/null
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_cache.hh
@@ -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
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh b/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh
index 68370cf6a44..877651d6530 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh
@@ -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
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source.hh b/source/blender/editors/space_spreadsheet/spreadsheet_data_source.hh
index 2ea7fb5809f..873735c81e5 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source.hh
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source.hh
@@ -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);
}
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc
index 78d9f61d8d5..136dc244d16 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc
@@ -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 {
+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 &)> fn) const
+ 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
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh
index d1b5dc6845e..6c88a94f585 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh
@@ -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;
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_intern.hh b/source/blender/editors/space_spreadsheet/spreadsheet_intern.hh
index 8be5283fd63..8b050c2e69b 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_intern.hh
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_intern.hh
@@ -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;
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc b/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc
index 1a5eac53306..355899be279 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc
@@ -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;
diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h
index c2eb67b7dd8..d5d2520ddf6 100644
--- a/source/blender/makesdna/DNA_node_types.h
+++ b/source/blender/makesdna/DNA_node_types.h
@@ -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
diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c
index 8ce9bc264ba..8535d334f39 100644
--- a/source/blender/makesrna/intern/rna_nodetree.c
+++ b/source/blender/makesrna/intern/rna_nodetree.c
@@ -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)
diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h
index 29cbe0d13c8..7fd4840489e 100644
--- a/source/blender/nodes/NOD_static_types.h
+++ b/source/blender/nodes/NOD_static_types.h
@@ -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 */
diff --git a/source/blender/nodes/geometry/nodes/node_geo_viewer.cc b/source/blender/nodes/geometry/nodes/node_geo_viewer.cc
index 3331962341f..920a6e1ed5d 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_viewer.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_viewer.cc
@@ -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);
}