Support for having an input and output socket in the same socket declaration builder was added for the original node panels to be able to support inline sockets. However, those were generally disabled for now. As can be seen in the simulation and repeat zone, inline sockets can work differently too. Having an input and output in the same socket declaration builder builder makes some things simpler but makes other things much harder. For example, the current design wouldn't work all that well if the input and output socket has different types. This is easier to do with the `align_with_previous_socket` approach. I'm not yet entirely sure whether we want to use the same approach for corresponding sockets in the node tree interface, but that can be tried and decided separately. Pull Request: https://projects.blender.org/blender/blender/pulls/119599
188 lines
6.1 KiB
C++
188 lines
6.1 KiB
C++
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
#include "BLI_string.h"
|
|
#include "BLI_string_utf8.h"
|
|
|
|
#include "NOD_socket.hh"
|
|
#include "NOD_zone_socket_items.hh"
|
|
|
|
#include "BLI_string_utils.hh"
|
|
|
|
#include "node_geometry_util.hh"
|
|
|
|
namespace blender::nodes::node_geo_repeat_cc {
|
|
|
|
namespace repeat_input_node {
|
|
|
|
NODE_STORAGE_FUNCS(NodeGeometryRepeatInput);
|
|
|
|
static void node_declare(NodeDeclarationBuilder &b)
|
|
{
|
|
b.use_custom_socket_order();
|
|
b.allow_any_socket_order();
|
|
b.add_input<decl::Int>("Iterations").min(0).default_value(1);
|
|
|
|
const bNode *node = b.node_or_null();
|
|
const bNodeTree *tree = b.tree_or_null();
|
|
if (node && tree) {
|
|
const NodeGeometryRepeatInput &storage = node_storage(*node);
|
|
const bNode *output_node = tree->node_by_id(storage.output_node_id);
|
|
if (output_node) {
|
|
const auto &output_storage = *static_cast<const NodeGeometryRepeatOutput *>(
|
|
output_node->storage);
|
|
for (const int i : IndexRange(output_storage.items_num)) {
|
|
const NodeRepeatItem &item = output_storage.items[i];
|
|
const eNodeSocketDatatype socket_type = eNodeSocketDatatype(item.socket_type);
|
|
const StringRef name = item.name ? item.name : "";
|
|
const std::string identifier = RepeatItemsAccessor::socket_identifier_for_item(item);
|
|
auto &input_decl = b.add_input(socket_type, name, identifier);
|
|
auto &output_decl = b.add_output(socket_type, name, identifier).align_with_previous();
|
|
if (socket_type_supports_fields(socket_type)) {
|
|
input_decl.supports_field();
|
|
output_decl.dependent_field({input_decl.index()});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
b.add_input<decl::Extend>("", "__extend__");
|
|
b.add_output<decl::Extend>("", "__extend__").align_with_previous();
|
|
}
|
|
|
|
static void node_init(bNodeTree * /*tree*/, bNode *node)
|
|
{
|
|
NodeGeometryRepeatInput *data = MEM_cnew<NodeGeometryRepeatInput>(__func__);
|
|
/* Needs to be initialized for the node to work. */
|
|
data->output_node_id = 0;
|
|
node->storage = data;
|
|
}
|
|
|
|
static void node_label(const bNodeTree * /*ntree*/,
|
|
const bNode * /*node*/,
|
|
char *label,
|
|
const int label_maxncpy)
|
|
{
|
|
BLI_strncpy_utf8(label, IFACE_("Repeat"), label_maxncpy);
|
|
}
|
|
|
|
static bool node_insert_link(bNodeTree *ntree, bNode *node, bNodeLink *link)
|
|
{
|
|
bNode *output_node = ntree->node_by_id(node_storage(*node).output_node_id);
|
|
if (!output_node) {
|
|
return true;
|
|
}
|
|
return socket_items::try_add_item_via_any_extend_socket<RepeatItemsAccessor>(
|
|
*ntree, *node, *output_node, *link);
|
|
}
|
|
|
|
static void node_register()
|
|
{
|
|
static bNodeType ntype;
|
|
geo_node_type_base(&ntype, GEO_NODE_REPEAT_INPUT, "Repeat Input", NODE_CLASS_INTERFACE);
|
|
ntype.initfunc = node_init;
|
|
ntype.declare = node_declare;
|
|
ntype.labelfunc = node_label;
|
|
ntype.gather_link_search_ops = nullptr;
|
|
ntype.insert_link = node_insert_link;
|
|
ntype.no_muting = true;
|
|
node_type_storage(
|
|
&ntype, "NodeGeometryRepeatInput", node_free_standard_storage, node_copy_standard_storage);
|
|
nodeRegisterType(&ntype);
|
|
}
|
|
NOD_REGISTER_NODE(node_register)
|
|
|
|
} // namespace repeat_input_node
|
|
|
|
namespace repeat_output_node {
|
|
|
|
NODE_STORAGE_FUNCS(NodeGeometryRepeatOutput);
|
|
|
|
static void node_declare(NodeDeclarationBuilder &b)
|
|
{
|
|
b.use_custom_socket_order();
|
|
b.allow_any_socket_order();
|
|
const bNode *node = b.node_or_null();
|
|
if (node) {
|
|
const NodeGeometryRepeatOutput &storage = node_storage(*node);
|
|
for (const int i : IndexRange(storage.items_num)) {
|
|
const NodeRepeatItem &item = storage.items[i];
|
|
const eNodeSocketDatatype socket_type = eNodeSocketDatatype(item.socket_type);
|
|
const StringRef name = item.name ? item.name : "";
|
|
const std::string identifier = RepeatItemsAccessor::socket_identifier_for_item(item);
|
|
auto &input_decl = b.add_input(socket_type, name, identifier);
|
|
auto &output_decl = b.add_output(socket_type, name, identifier).align_with_previous();
|
|
if (socket_type_supports_fields(socket_type)) {
|
|
input_decl.supports_field();
|
|
output_decl.dependent_field({input_decl.index()});
|
|
}
|
|
}
|
|
}
|
|
b.add_input<decl::Extend>("", "__extend__");
|
|
b.add_output<decl::Extend>("", "__extend__").align_with_previous();
|
|
}
|
|
|
|
static void node_init(bNodeTree * /*tree*/, bNode *node)
|
|
{
|
|
NodeGeometryRepeatOutput *data = MEM_cnew<NodeGeometryRepeatOutput>(__func__);
|
|
|
|
data->next_identifier = 0;
|
|
|
|
data->items = MEM_cnew_array<NodeRepeatItem>(1, __func__);
|
|
data->items[0].name = BLI_strdup(DATA_("Geometry"));
|
|
data->items[0].socket_type = SOCK_GEOMETRY;
|
|
data->items[0].identifier = data->next_identifier++;
|
|
data->items_num = 1;
|
|
|
|
node->storage = data;
|
|
}
|
|
|
|
static void node_free_storage(bNode *node)
|
|
{
|
|
socket_items::destruct_array<RepeatItemsAccessor>(*node);
|
|
MEM_freeN(node->storage);
|
|
}
|
|
|
|
static void node_copy_storage(bNodeTree * /*dst_tree*/, bNode *dst_node, const bNode *src_node)
|
|
{
|
|
const NodeGeometryRepeatOutput &src_storage = node_storage(*src_node);
|
|
auto *dst_storage = MEM_new<NodeGeometryRepeatOutput>(__func__, src_storage);
|
|
dst_node->storage = dst_storage;
|
|
|
|
socket_items::copy_array<RepeatItemsAccessor>(*src_node, *dst_node);
|
|
}
|
|
|
|
static bool node_insert_link(bNodeTree *ntree, bNode *node, bNodeLink *link)
|
|
{
|
|
return socket_items::try_add_item_via_any_extend_socket<RepeatItemsAccessor>(
|
|
*ntree, *node, *node, *link);
|
|
}
|
|
|
|
static void node_register()
|
|
{
|
|
static bNodeType ntype;
|
|
geo_node_type_base(&ntype, GEO_NODE_REPEAT_OUTPUT, "Repeat Output", NODE_CLASS_INTERFACE);
|
|
ntype.initfunc = node_init;
|
|
ntype.declare = node_declare;
|
|
ntype.labelfunc = repeat_input_node::node_label;
|
|
ntype.insert_link = node_insert_link;
|
|
ntype.no_muting = true;
|
|
node_type_storage(&ntype, "NodeGeometryRepeatOutput", node_free_storage, node_copy_storage);
|
|
nodeRegisterType(&ntype);
|
|
}
|
|
NOD_REGISTER_NODE(node_register)
|
|
|
|
} // namespace repeat_output_node
|
|
|
|
} // namespace blender::nodes::node_geo_repeat_cc
|
|
|
|
blender::Span<NodeRepeatItem> NodeGeometryRepeatOutput::items_span() const
|
|
{
|
|
return blender::Span<NodeRepeatItem>(items, items_num);
|
|
}
|
|
|
|
blender::MutableSpan<NodeRepeatItem> NodeGeometryRepeatOutput::items_span()
|
|
{
|
|
return blender::MutableSpan<NodeRepeatItem>(items, items_num);
|
|
}
|