Geometry Nodes: new Join Bundle Node
Adds a Join Bundle node with a multi-input Bundle socket. Bundles are iterated on the top level of each input bundle and items added to the resulting single bundle. While creating the final bundle existing items have priority and new items with an already existing name are discarded. There is an info message when there are duplicate keys in the input bundles. Co-authored-by: Brady Johnston Co-authored-by: Jacques Lucke Pull Request: https://projects.blender.org/blender/blender/pulls/146750
This commit is contained in:
committed by
Jacques Lucke
parent
f8c4d743bc
commit
8b3be68b3d
@@ -790,6 +790,7 @@ class NODE_MT_category_utilities_bundle_base(node_add_menu.NodeMenu):
|
||||
layout = self.layout
|
||||
self.node_operator(layout, "NodeCombineBundle")
|
||||
self.node_operator(layout, "NodeSeparateBundle")
|
||||
self.node_operator(layout, "NodeJoinBundle")
|
||||
|
||||
self.draw_assets_for_catalog(layout, self.menu_path)
|
||||
|
||||
|
||||
@@ -9743,6 +9743,7 @@ static void rna_def_nodes(BlenderRNA *brna)
|
||||
define("NodeInternal", "NodeCombineBundle", def_combine_bundle);
|
||||
define("NodeInternal", "NodeEnableOutput");
|
||||
define("NodeInternal", "NodeEvaluateClosure", def_evaluate_closure);
|
||||
define("NodeInternal", "NodeJoinBundle");
|
||||
define("NodeInternal", "NodeSeparateBundle", def_separate_bundle);
|
||||
|
||||
define("ShaderNode", "ShaderNodeAddShader");
|
||||
|
||||
@@ -146,6 +146,7 @@ set(SRC
|
||||
nodes/node_geo_instances_to_points.cc
|
||||
nodes/node_geo_interpolate_curves.cc
|
||||
nodes/node_geo_is_viewport.cc
|
||||
nodes/node_geo_join_bundle.cc
|
||||
nodes/node_geo_join_geometry.cc
|
||||
nodes/node_geo_list.cc
|
||||
nodes/node_geo_list_get_item.cc
|
||||
|
||||
89
source/blender/nodes/geometry/nodes/node_geo_join_bundle.cc
Normal file
89
source/blender/nodes/geometry/nodes/node_geo_join_bundle.cc
Normal file
@@ -0,0 +1,89 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "NOD_geometry_nodes_bundle.hh"
|
||||
|
||||
#include "node_geometry_util.hh"
|
||||
|
||||
namespace blender::nodes::node_geo_join_bundle {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.use_custom_socket_order();
|
||||
b.allow_any_socket_order();
|
||||
b.add_input<decl::Bundle>("Bundle").multi_input().description(
|
||||
"Bundles to join together on the top level for each bundle. When there are duplicates, only "
|
||||
"the first occurence is used");
|
||||
b.add_output<decl::Bundle>("Bundle").align_with_previous();
|
||||
}
|
||||
|
||||
static void node_geo_exec(GeoNodeExecParams params)
|
||||
{
|
||||
GeoNodesMultiInput<BundlePtr> bundles = params.extract_input<GeoNodesMultiInput<BundlePtr>>(
|
||||
"Bundle");
|
||||
|
||||
if (bundles.values.is_empty()) {
|
||||
params.set_default_remaining_outputs();
|
||||
return;
|
||||
}
|
||||
|
||||
BundlePtr output_bundle;
|
||||
int bundle_i = 0;
|
||||
for (; bundle_i < bundles.values.size(); bundle_i++) {
|
||||
BundlePtr &bundle = bundles.values[bundle_i];
|
||||
if (bundle) {
|
||||
output_bundle = std::move(bundle);
|
||||
bundle_i++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!output_bundle) {
|
||||
output_bundle = Bundle::create();
|
||||
}
|
||||
else if (!output_bundle->is_mutable()) {
|
||||
output_bundle = output_bundle->copy();
|
||||
}
|
||||
else {
|
||||
output_bundle->tag_ensured_mutable();
|
||||
}
|
||||
Bundle &mutable_output_bundle = const_cast<Bundle &>(*output_bundle);
|
||||
|
||||
VectorSet<StringRef> overridden_keys;
|
||||
for (; bundle_i < bundles.values.size(); bundle_i++) {
|
||||
BundlePtr &bundle = bundles.values[bundle_i];
|
||||
if (!bundle) {
|
||||
continue;
|
||||
}
|
||||
for (const Bundle::StoredItem &item : bundle->items()) {
|
||||
if (!mutable_output_bundle.add(item.key, item.value)) {
|
||||
overridden_keys.add(item.key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!overridden_keys.is_empty()) {
|
||||
std::string message = fmt::format(
|
||||
"{}: {}", TIP_("Duplicate keys"), fmt::join(overridden_keys, ", "));
|
||||
params.error_message_add(NodeWarningType::Info, std::move(message));
|
||||
}
|
||||
|
||||
params.set_output("Bundle", output_bundle);
|
||||
}
|
||||
|
||||
static void node_register()
|
||||
{
|
||||
static blender::bke::bNodeType ntype;
|
||||
geo_node_type_base(&ntype, "NodeJoinBundle");
|
||||
ntype.ui_name = "Join Bundle";
|
||||
ntype.ui_description = "Join multiple bundles together";
|
||||
ntype.nclass = NODE_CLASS_CONVERTER;
|
||||
ntype.geometry_node_execute = node_geo_exec;
|
||||
ntype.declare = node_declare;
|
||||
blender::bke::node_register_type(ntype);
|
||||
}
|
||||
NOD_REGISTER_NODE(node_register)
|
||||
|
||||
} // namespace blender::nodes::node_geo_join_bundle
|
||||
@@ -446,6 +446,10 @@ static Vector<SocketInContext> find_origin_sockets_through_contexts(
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (node->is_type("NodeJoinBundle")) {
|
||||
add_if_new(node.input_socket(0), bundle_path);
|
||||
continue;
|
||||
}
|
||||
if (node->is_type("NodeEvaluateClosure")) {
|
||||
const auto &evaluate_storage = *static_cast<const NodeEvaluateClosure *>(node->storage);
|
||||
const StringRef key = evaluate_storage.output_items.items[socket->index()].name;
|
||||
@@ -611,12 +615,37 @@ LinkedBundleSignatures gather_linked_origin_bundle_signatures(
|
||||
{bundle_socket_context, &bundle_socket},
|
||||
compute_context_cache,
|
||||
[&](const SocketInContext &socket) {
|
||||
const bNode &node = socket->owner_node();
|
||||
if (socket->is_output() && node.is_type("NodeCombineBundle")) {
|
||||
const auto &storage = *static_cast<const NodeCombineBundle *>(node.storage);
|
||||
result.items.append({BundleSignature::from_combine_bundle_node(node, false),
|
||||
bool(storage.flag & NODE_COMBINE_BUNDLE_FLAG_DEFINE_SIGNATURE),
|
||||
socket});
|
||||
const NodeInContext node = socket.owner_node();
|
||||
if (socket->is_output()) {
|
||||
if (node->is_type("NodeCombineBundle")) {
|
||||
const auto &storage = *static_cast<const NodeCombineBundle *>(node->storage);
|
||||
result.items.append({BundleSignature::from_combine_bundle_node(*node, false),
|
||||
bool(storage.flag & NODE_COMBINE_BUNDLE_FLAG_DEFINE_SIGNATURE),
|
||||
socket});
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (node->is_type("NodeJoinBundle")) {
|
||||
const SocketInContext input_socket = node.input_socket(0);
|
||||
BundleSignature joined_signature;
|
||||
bool is_signature_definition = true;
|
||||
for (const bNodeLink *link : input_socket->directly_linked_links()) {
|
||||
if (!link->is_used()) {
|
||||
continue;
|
||||
}
|
||||
const bNodeSocket *socket_from = link->fromsock;
|
||||
const LinkedBundleSignatures sub_signatures = gather_linked_origin_bundle_signatures(
|
||||
node.context, *socket_from, compute_context_cache);
|
||||
for (const LinkedBundleSignatures::Item &sub_signature : sub_signatures.items) {
|
||||
if (!sub_signature.is_signature_definition) {
|
||||
is_signature_definition = false;
|
||||
}
|
||||
for (const BundleSignature::Item &item : sub_signature.signature.items) {
|
||||
joined_signature.items.add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
result.items.append({joined_signature, is_signature_definition, socket});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
BIN
tests/files/modeling/geometry_nodes/utilities/join_bundle.blend
(Stored with Git LFS)
Normal file
BIN
tests/files/modeling/geometry_nodes/utilities/join_bundle.blend
(Stored with Git LFS)
Normal file
Binary file not shown.
Reference in New Issue
Block a user