diff options
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); } |