From dedc679ecabb43e79b0160a7c64bbd616adfa829 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 23 Sep 2022 13:56:35 -0500 Subject: Geometry Nodes: Split transfer attribute node This patch replaces the existing transfer attribute node with three nodes, "Sample Nearest Surface", "Sample Index", and "Sample Nearest". This follows the design in T100010, allowing for new nodes like UV sampling in the future. There is versioning so the new nodes replace the old ones and are relinked as necessary. The "Sample Nearest Surface" node is meant for the more complex sampling algorithms that only work on meshes and interpolate values inside of faces. The new "Sample Index" just retrieves attributes from a geometry at specific indices. It doesn't have implicit behavior like the old transfer mode, which should make it more predictable. In order to not change the behavior from existing files, the node has a has a "Clamp", which is off by default for consistency with the "Field at Index" node. The "Sample Nearest" node returns the index of the nearest element on a geometry. It can be combined with the "Sample Index" node for the same functionality as the old transfer node. This node can support curves in the future. Backwards compatibility is handled by versioning, but old versions can not understand these nodes. The warning from 680fa8a523e0 should make this explicit in 3.3 and earlier. Differential Revision: https://developer.blender.org/D15909 --- source/blender/blenloader/intern/versioning_300.cc | 160 ++++++++++++++++++++- .../blender/blenloader/intern/versioning_common.cc | 29 ++++ .../blender/blenloader/intern/versioning_common.h | 11 ++ 3 files changed, 194 insertions(+), 6 deletions(-) (limited to 'source/blender/blenloader/intern') diff --git a/source/blender/blenloader/intern/versioning_300.cc b/source/blender/blenloader/intern/versioning_300.cc index 8f1f2fa2c17..9ea662ac000 100644 --- a/source/blender/blenloader/intern/versioning_300.cc +++ b/source/blender/blenloader/intern/versioning_300.cc @@ -1791,6 +1791,147 @@ static void version_fix_image_format_copy(Main *bmain, ImageFormatData *format) } } +static void version_geometry_nodes_replace_transfer_attribute_node(bNodeTree *ntree) +{ + using namespace blender; + /* Otherwise `ntree->typeInfo` is null. */ + ntreeSetTypes(NULL, ntree); + LISTBASE_FOREACH_MUTABLE (bNode *, node, &ntree->nodes) { + if (node->type != GEO_NODE_TRANSFER_ATTRIBUTE_DEPRECATED) { + continue; + } + bNodeSocket *old_geometry_socket = nodeFindSocket(node, SOCK_IN, "Source"); + const NodeGeometryTransferAttribute *storage = (const NodeGeometryTransferAttribute *) + node->storage; + switch (storage->mode) { + case GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED: { + bNode *sample_nearest_surface = nodeAddStaticNode( + NULL, ntree, GEO_NODE_SAMPLE_NEAREST_SURFACE); + sample_nearest_surface->parent = node->parent; + sample_nearest_surface->custom1 = storage->data_type; + sample_nearest_surface->locx = node->locx; + sample_nearest_surface->locy = node->locy; + static auto socket_remap = []() { + Map map; + map.add_new("Attribute", "Value_Vector"); + map.add_new("Attribute_001", "Value_Float"); + map.add_new("Attribute_002", "Value_Color"); + map.add_new("Attribute_003", "Value_Bool"); + map.add_new("Attribute_004", "Value_Int"); + map.add_new("Source", "Mesh"); + map.add_new("Source Position", "Sample Position"); + return map; + }(); + node_tree_relink_with_socket_id_map(*ntree, *node, *sample_nearest_surface, socket_remap); + break; + } + case GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST: { + /* These domains weren't supported by the index transfer mode, but were selectable. */ + const eAttrDomain domain = ELEM(storage->domain, ATTR_DOMAIN_INSTANCE, ATTR_DOMAIN_CURVE) ? + ATTR_DOMAIN_POINT : + eAttrDomain(storage->domain); + + /* Use a sample index node to retrieve the data with this node's index output. */ + bNode *sample_index = nodeAddStaticNode(NULL, ntree, GEO_NODE_SAMPLE_INDEX); + NodeGeometrySampleIndex *sample_storage = static_cast( + sample_index->storage); + sample_storage->data_type = storage->data_type; + sample_storage->domain = domain; + sample_index->parent = node->parent; + sample_index->locx = node->locx + 25.0f; + sample_index->locy = node->locy; + if (old_geometry_socket->link) { + nodeAddLink(ntree, + old_geometry_socket->link->fromnode, + old_geometry_socket->link->fromsock, + sample_index, + nodeFindSocket(sample_index, SOCK_IN, "Geometry")); + } + + bNode *sample_nearest = nodeAddStaticNode(NULL, ntree, GEO_NODE_SAMPLE_NEAREST); + sample_nearest->parent = node->parent; + sample_nearest->custom1 = storage->data_type; + sample_nearest->custom2 = domain; + sample_nearest->locx = node->locx - 25.0f; + sample_nearest->locy = node->locy; + if (old_geometry_socket->link) { + nodeAddLink(ntree, + old_geometry_socket->link->fromnode, + old_geometry_socket->link->fromsock, + sample_nearest, + nodeFindSocket(sample_nearest, SOCK_IN, "Geometry")); + } + static auto sample_nearest_remap = []() { + Map map; + map.add_new("Source Position", "Sample Position"); + return map; + }(); + node_tree_relink_with_socket_id_map(*ntree, *node, *sample_nearest, sample_nearest_remap); + + static auto sample_index_remap = []() { + Map map; + map.add_new("Attribute", "Value_Vector"); + map.add_new("Attribute_001", "Value_Float"); + map.add_new("Attribute_002", "Value_Color"); + map.add_new("Attribute_003", "Value_Bool"); + map.add_new("Attribute_004", "Value_Int"); + map.add_new("Source Position", "Sample Position"); + return map; + }(); + node_tree_relink_with_socket_id_map(*ntree, *node, *sample_index, sample_index_remap); + + nodeAddLink(ntree, + sample_nearest, + nodeFindSocket(sample_nearest, SOCK_OUT, "Index"), + sample_index, + nodeFindSocket(sample_index, SOCK_IN, "Index")); + break; + } + case GEO_NODE_ATTRIBUTE_TRANSFER_INDEX: { + bNode *sample_index = nodeAddStaticNode(NULL, ntree, GEO_NODE_SAMPLE_INDEX); + NodeGeometrySampleIndex *sample_storage = static_cast( + sample_index->storage); + sample_storage->data_type = storage->data_type; + sample_storage->domain = storage->domain; + sample_storage->clamp = 1; + sample_index->parent = node->parent; + sample_index->locx = node->locx; + sample_index->locy = node->locy; + const bool index_was_linked = nodeFindSocket(node, SOCK_IN, "Index")->link != nullptr; + static auto socket_remap = []() { + Map map; + map.add_new("Attribute", "Value_Vector"); + map.add_new("Attribute_001", "Value_Float"); + map.add_new("Attribute_002", "Value_Color"); + map.add_new("Attribute_003", "Value_Bool"); + map.add_new("Attribute_004", "Value_Int"); + map.add_new("Source", "Geometry"); + map.add_new("Index", "Index"); + return map; + }(); + node_tree_relink_with_socket_id_map(*ntree, *node, *sample_index, socket_remap); + + if (!index_was_linked) { + /* Add an index input node, since the new node doesn't use an implicit input. */ + bNode *index = nodeAddStaticNode(NULL, ntree, GEO_NODE_INPUT_INDEX); + index->parent = node->parent; + index->locx = node->locx - 25.0f; + index->locy = node->locy - 25.0f; + nodeAddLink(ntree, + index, + nodeFindSocket(index, SOCK_OUT, "Index"), + sample_index, + nodeFindSocket(sample_index, SOCK_IN, "Index")); + } + break; + } + } + /* The storage must be feeed manually because the node type isn't defined anymore. */ + MEM_freeN(node->storage); + nodeRemoveNode(NULL, ntree, node, false); + } +} + /* NOLINTNEXTLINE: readability-function-size */ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) { @@ -2803,7 +2944,8 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) ntree, GEO_NODE_INPUT_MESH_EDGE_ANGLE, "Angle", "Unsigned Angle"); version_node_output_socket_name( ntree, GEO_NODE_INPUT_MESH_ISLAND, "Index", "Island Index"); - version_node_input_socket_name(ntree, GEO_NODE_TRANSFER_ATTRIBUTE, "Target", "Source"); + version_node_input_socket_name( + ntree, GEO_NODE_TRANSFER_ATTRIBUTE_DEPRECATED, "Target", "Source"); } } } @@ -3405,12 +3547,10 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } /* Convert mix rgb node to new mix node and add storage. */ - { - FOREACH_NODETREE_BEGIN (bmain, ntree, id) { - versioning_replace_legacy_mix_rgb_node(ntree); - } - FOREACH_NODETREE_END; + FOREACH_NODETREE_BEGIN (bmain, ntree, id) { + versioning_replace_legacy_mix_rgb_node(ntree); } + FOREACH_NODETREE_END; /* Face sets no longer store whether the corresponding face is hidden. */ LISTBASE_FOREACH (Mesh *, mesh, &bmain->meshes) { @@ -3437,5 +3577,13 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } } + + /* Split the transfer attribute node into multiple smaller nodes. */ + FOREACH_NODETREE_BEGIN (bmain, ntree, id) { + if (ntree->type == NTREE_GEOMETRY) { + version_geometry_nodes_replace_transfer_attribute_node(ntree); + } + } + FOREACH_NODETREE_END; } } diff --git a/source/blender/blenloader/intern/versioning_common.cc b/source/blender/blenloader/intern/versioning_common.cc index 823385727e1..c87983f1287 100644 --- a/source/blender/blenloader/intern/versioning_common.cc +++ b/source/blender/blenloader/intern/versioning_common.cc @@ -12,6 +12,7 @@ #include "DNA_screen_types.h" #include "BLI_listbase.h" +#include "BLI_map.hh" #include "BLI_string.h" #include "BLI_string_ref.hh" @@ -25,6 +26,7 @@ #include "versioning_common.h" +using blender::Map; using blender::StringRef; ARegion *do_versions_add_region_if_not_found(ListBase *regionbase, @@ -234,3 +236,30 @@ ARegion *do_versions_add_region(int regiontype, const char *name) region->regiontype = regiontype; return region; } + +void node_tree_relink_with_socket_id_map(bNodeTree &ntree, + bNode &old_node, + bNode &new_node, + const Map &map) +{ + LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree.links) { + if (link->tonode == &old_node) { + bNodeSocket *old_socket = link->tosock; + if (const std::string *new_identifier = map.lookup_ptr_as(old_socket->identifier)) { + bNodeSocket *new_socket = nodeFindSocket(&new_node, SOCK_IN, new_identifier->c_str()); + link->tonode = &new_node; + link->tosock = new_socket; + old_socket->link = NULL; + } + } + if (link->fromnode == &old_node) { + bNodeSocket *old_socket = link->fromsock; + if (const std::string *new_identifier = map.lookup_ptr_as(old_socket->identifier)) { + bNodeSocket *new_socket = nodeFindSocket(&new_node, SOCK_OUT, new_identifier->c_str()); + link->fromnode = &new_node; + link->fromsock = new_socket; + old_socket->link = NULL; + } + } + } +} diff --git a/source/blender/blenloader/intern/versioning_common.h b/source/blender/blenloader/intern/versioning_common.h index c8c7dcc7cff..a8844d076b3 100644 --- a/source/blender/blenloader/intern/versioning_common.h +++ b/source/blender/blenloader/intern/versioning_common.h @@ -6,6 +6,10 @@ #pragma once +#ifdef __cplusplus +# include "BLI_map.hh" +#endif + struct ARegion; struct ListBase; struct Main; @@ -93,3 +97,10 @@ ARegion *do_versions_add_region(int regiontype, const char *name); #ifdef __cplusplus } #endif + +#ifdef __cplusplus +void node_tree_relink_with_socket_id_map(bNodeTree &ntree, + bNode &old_node, + bNode &new_node, + const blender::Map &map); +#endif -- cgit v1.2.3