2023-08-16 00:20:26 +10:00
|
|
|
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
2023-05-31 16:19:06 +02:00
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
2021-08-30 17:13:46 +02:00
|
|
|
|
|
|
|
|
#include "NOD_node_declaration.hh"
|
2023-01-03 12:24:00 +01:00
|
|
|
#include "NOD_socket_declarations.hh"
|
|
|
|
|
#include "NOD_socket_declarations_geometry.hh"
|
Nodes: initial support for built-in menu sockets
So far, only node group were able to have menu input sockets. Built-in nodes did
not support them. Currently, all menus of built-in nodes are stored on the node
instead of on the sockets. This limits their flexibility because it's not
possible to expose these inputs.
This patch adds initial support for having menu inputs in built-in nodes. For
testing purposes, it also changes a couple built-in nodes to use an input socket
instead of a node property: Points to Volume, Transform Geometry, Triangulate,
Volume to Mesh and Match String.
### Compatibility
Forward and backward compatibility is maintained where possible (it's not
possible when the menu input is linked in 5.0). The overall compatibility
approach is the same as what was done for the compositor with two differences:
there are no wrapper RNA properties (not necessary for 5.0, those were removed
for the compositor already too), no need to version animation (animation on the
menu properties was already disabled).
This also makes menu sockets not animatable in general which is kind of brittle
(e.g. doesn't properly update when the menu definition changes). To animate a
menu it's better to animate an integer and to drive an index switch with it.
### Which nodes to update?
Many existing menu properties can become sockets, but it's currently not the
intention to convert all of them. In some cases, converting them might restrict
future improvements too much. This mainly affects Math nodes.
Other existing nodes should be updated but are a bit more tricky to update for
different reasons:
* We don't support dynamic output visibility yet. This is something I'll need to
look into at some point.
* They are shared with shader/compositor nodes, which may be more limited in
what can become a socket.
* There may be performance implications unless extra special cases are
implemented, especially for multi-function nodes.
* Some nodes use socket renaming instead of dynamic socket visibility which
isn't something we support more generally yet.
### Implementation
The core implementation is fairly straight forward. The heavy lifting is done by
the existing socket visibility inferencing. There is a new simple API that
allows individual nodes to implement custom input-usage-rules based on other
inputs in a decentralized way.
In most cases, the nodes to update just have a single menu, so there is a new
node-declaration utility that links a socket to a specific value of the menu
input. This internally handles the usage inferencing as well as making the
socket available when using link-drag-search.
In the modified nodes, I also had to explicitly set the "main input" now which
is used when inserting the node in a link. The automatic behavior doesn't work
currently when the first input is a menu. This is something we'll have to solve
more generally at some point but is out of scope for this patch.
Pull Request: https://projects.blender.org/blender/blender/pulls/140705
2025-07-16 08:31:59 +02:00
|
|
|
#include "NOD_socket_usage_inference.hh"
|
2021-08-30 17:13:46 +02:00
|
|
|
|
2025-03-26 16:14:05 +02:00
|
|
|
#include "BLI_assert.h"
|
Nodes: initial support for built-in menu sockets
So far, only node group were able to have menu input sockets. Built-in nodes did
not support them. Currently, all menus of built-in nodes are stored on the node
instead of on the sockets. This limits their flexibility because it's not
possible to expose these inputs.
This patch adds initial support for having menu inputs in built-in nodes. For
testing purposes, it also changes a couple built-in nodes to use an input socket
instead of a node property: Points to Volume, Transform Geometry, Triangulate,
Volume to Mesh and Match String.
### Compatibility
Forward and backward compatibility is maintained where possible (it's not
possible when the menu input is linked in 5.0). The overall compatibility
approach is the same as what was done for the compositor with two differences:
there are no wrapper RNA properties (not necessary for 5.0, those were removed
for the compositor already too), no need to version animation (animation on the
menu properties was already disabled).
This also makes menu sockets not animatable in general which is kind of brittle
(e.g. doesn't properly update when the menu definition changes). To animate a
menu it's better to animate an integer and to drive an index switch with it.
### Which nodes to update?
Many existing menu properties can become sockets, but it's currently not the
intention to convert all of them. In some cases, converting them might restrict
future improvements too much. This mainly affects Math nodes.
Other existing nodes should be updated but are a bit more tricky to update for
different reasons:
* We don't support dynamic output visibility yet. This is something I'll need to
look into at some point.
* They are shared with shader/compositor nodes, which may be more limited in
what can become a socket.
* There may be performance implications unless extra special cases are
implemented, especially for multi-function nodes.
* Some nodes use socket renaming instead of dynamic socket visibility which
isn't something we support more generally yet.
### Implementation
The core implementation is fairly straight forward. The heavy lifting is done by
the existing socket visibility inferencing. There is a new simple API that
allows individual nodes to implement custom input-usage-rules based on other
inputs in a decentralized way.
In most cases, the nodes to update just have a single menu, so there is a new
node-declaration utility that links a socket to a specific value of the menu
input. This internally handles the usage inferencing as well as making the
socket available when using link-drag-search.
In the modified nodes, I also had to explicitly set the "main input" now which
is used when inserting the node in a link. The automatic behavior doesn't work
currently when the first input is a menu. This is something we'll have to solve
more generally at some point but is out of scope for this patch.
Pull Request: https://projects.blender.org/blender/blender/pulls/140705
2025-07-16 08:31:59 +02:00
|
|
|
#include "BLI_listbase.h"
|
2023-08-30 12:37:21 +02:00
|
|
|
#include "BLI_utildefines.h"
|
|
|
|
|
|
2022-09-25 19:16:53 +02:00
|
|
|
#include "BKE_geometry_fields.hh"
|
2023-05-15 15:14:22 +02:00
|
|
|
#include "BKE_node.hh"
|
2023-08-30 12:37:21 +02:00
|
|
|
#include "BKE_node_runtime.hh"
|
2023-11-18 13:11:39 +01:00
|
|
|
#include "BKE_node_socket_value.hh"
|
2021-09-15 16:09:00 +02:00
|
|
|
|
2024-05-30 10:41:05 +02:00
|
|
|
#include "RNA_access.hh"
|
|
|
|
|
|
2021-08-30 17:13:46 +02:00
|
|
|
namespace blender::nodes {
|
|
|
|
|
|
2023-10-08 14:38:28 +02:00
|
|
|
static void reset_declaration(NodeDeclaration &declaration)
|
|
|
|
|
{
|
|
|
|
|
std::destroy_at(&declaration);
|
|
|
|
|
new (&declaration) NodeDeclaration();
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-13 16:07:12 +02:00
|
|
|
void build_node_declaration(const bke::bNodeType &typeinfo,
|
2023-10-15 20:28:23 +02:00
|
|
|
NodeDeclaration &r_declaration,
|
|
|
|
|
const bNodeTree *ntree,
|
|
|
|
|
const bNode *node)
|
2022-12-11 19:12:09 +01:00
|
|
|
{
|
2023-10-08 14:38:28 +02:00
|
|
|
reset_declaration(r_declaration);
|
2024-10-11 12:20:58 +02:00
|
|
|
NodeDeclarationBuilder node_decl_builder{typeinfo, r_declaration, ntree, node};
|
2022-12-11 19:12:09 +01:00
|
|
|
typeinfo.declare(node_decl_builder);
|
2022-12-11 19:38:26 +01:00
|
|
|
node_decl_builder.finalize();
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-19 19:54:27 +01:00
|
|
|
void NodeDeclarationBuilder::build_remaining_anonymous_attribute_relations()
|
2022-12-11 19:38:26 +01:00
|
|
|
{
|
2024-10-07 12:59:39 +02:00
|
|
|
auto is_data_socket_decl = [](const SocketDeclaration *socket_decl) {
|
2025-04-03 15:44:06 +02:00
|
|
|
return ELEM(socket_decl->socket_type, SOCK_GEOMETRY, SOCK_BUNDLE, SOCK_CLOSURE);
|
2024-10-07 12:59:39 +02:00
|
|
|
};
|
|
|
|
|
|
2023-01-03 12:24:00 +01:00
|
|
|
Vector<int> geometry_inputs;
|
|
|
|
|
for (const int i : declaration_.inputs.index_range()) {
|
2024-10-07 12:59:39 +02:00
|
|
|
if (is_data_socket_decl(declaration_.inputs[i])) {
|
2023-01-03 12:24:00 +01:00
|
|
|
geometry_inputs.append(i);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Vector<int> geometry_outputs;
|
|
|
|
|
for (const int i : declaration_.outputs.index_range()) {
|
2024-10-07 12:59:39 +02:00
|
|
|
if (is_data_socket_decl(declaration_.outputs[i])) {
|
2023-01-03 12:24:00 +01:00
|
|
|
geometry_outputs.append(i);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-18 14:36:03 +01:00
|
|
|
for (BaseSocketDeclarationBuilder *socket_builder : input_socket_builders_) {
|
2023-01-03 12:24:00 +01:00
|
|
|
if (socket_builder->field_on_all_) {
|
|
|
|
|
aal::RelationsInNode &relations = this->get_anonymous_attribute_relations();
|
2024-10-11 12:20:58 +02:00
|
|
|
const int field_input = socket_builder->decl_base_->index;
|
2023-01-03 12:24:00 +01:00
|
|
|
for (const int geometry_input : geometry_inputs) {
|
|
|
|
|
relations.eval_relations.append({field_input, geometry_input});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-03-18 14:36:03 +01:00
|
|
|
for (BaseSocketDeclarationBuilder *socket_builder : output_socket_builders_) {
|
2023-01-03 12:24:00 +01:00
|
|
|
if (socket_builder->field_on_all_) {
|
|
|
|
|
aal::RelationsInNode &relations = this->get_anonymous_attribute_relations();
|
2024-10-11 12:20:58 +02:00
|
|
|
const int field_output = socket_builder->decl_base_->index;
|
2023-01-03 12:24:00 +01:00
|
|
|
for (const int geometry_output : geometry_outputs) {
|
|
|
|
|
relations.available_relations.append({field_output, geometry_output});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (socket_builder->reference_pass_all_) {
|
|
|
|
|
aal::RelationsInNode &relations = this->get_anonymous_attribute_relations();
|
2024-10-11 12:20:58 +02:00
|
|
|
const int field_output = socket_builder->decl_base_->index;
|
2023-01-03 12:24:00 +01:00
|
|
|
for (const int input_i : declaration_.inputs.index_range()) {
|
|
|
|
|
SocketDeclaration &input_socket_decl = *declaration_.inputs[input_i];
|
|
|
|
|
if (input_socket_decl.input_field_type != InputSocketFieldType::None) {
|
|
|
|
|
relations.reference_relations.append({input_i, field_output});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (socket_builder->propagate_from_all_) {
|
|
|
|
|
aal::RelationsInNode &relations = this->get_anonymous_attribute_relations();
|
2024-10-11 12:20:58 +02:00
|
|
|
const int geometry_output = socket_builder->decl_base_->index;
|
2023-01-03 12:24:00 +01:00
|
|
|
for (const int geometry_input : geometry_inputs) {
|
|
|
|
|
relations.propagate_relations.append({geometry_input, geometry_output});
|
|
|
|
|
}
|
2022-12-11 19:38:26 +01:00
|
|
|
}
|
|
|
|
|
}
|
2024-03-19 19:54:27 +01:00
|
|
|
}
|
2023-09-14 16:08:04 +02:00
|
|
|
|
2024-03-19 19:54:27 +01:00
|
|
|
void NodeDeclarationBuilder::finalize()
|
|
|
|
|
{
|
|
|
|
|
this->build_remaining_anonymous_attribute_relations();
|
2025-05-28 02:33:47 +02:00
|
|
|
if (is_function_node_) {
|
|
|
|
|
for (SocketDeclaration *socket_decl : declaration_.inputs) {
|
|
|
|
|
socket_decl->structure_type = StructureType::Dynamic;
|
|
|
|
|
}
|
|
|
|
|
for (SocketDeclaration *socket_decl : declaration_.outputs) {
|
|
|
|
|
socket_decl->structure_type = StructureType::Dynamic;
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-10-09 15:45:43 +02:00
|
|
|
#ifndef NDEBUG
|
|
|
|
|
declaration_.assert_valid();
|
|
|
|
|
#endif
|
2022-12-11 19:12:09 +01:00
|
|
|
}
|
|
|
|
|
|
2024-10-11 12:20:58 +02:00
|
|
|
NodeDeclarationBuilder::NodeDeclarationBuilder(const bke::bNodeType &typeinfo,
|
|
|
|
|
NodeDeclaration &declaration,
|
2023-10-15 20:28:23 +02:00
|
|
|
const bNodeTree *ntree,
|
|
|
|
|
const bNode *node)
|
2024-10-09 15:45:43 +02:00
|
|
|
: DeclarationListBuilder(*this, declaration.root_items),
|
2024-10-11 12:20:58 +02:00
|
|
|
typeinfo_(typeinfo),
|
2024-10-09 15:45:43 +02:00
|
|
|
declaration_(declaration),
|
|
|
|
|
ntree_(ntree),
|
|
|
|
|
node_(node)
|
2023-10-15 12:43:02 +02:00
|
|
|
{
|
2024-12-16 16:18:20 +01:00
|
|
|
/* Unused in release builds, but used for BLI_assert() in debug builds. */
|
|
|
|
|
UNUSED_VARS(typeinfo_);
|
2023-10-15 12:43:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void NodeDeclarationBuilder::use_custom_socket_order(bool enable)
|
|
|
|
|
{
|
|
|
|
|
declaration_.use_custom_socket_order = enable;
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-19 13:32:01 +01:00
|
|
|
void NodeDeclarationBuilder::allow_any_socket_order(bool enable)
|
|
|
|
|
{
|
|
|
|
|
BLI_assert(declaration_.use_custom_socket_order);
|
|
|
|
|
declaration_.allow_any_socket_order = enable;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-15 12:43:02 +02:00
|
|
|
Span<SocketDeclaration *> NodeDeclaration::sockets(eNodeSocketInOut in_out) const
|
|
|
|
|
{
|
|
|
|
|
if (in_out == SOCK_IN) {
|
|
|
|
|
return inputs;
|
|
|
|
|
}
|
|
|
|
|
return outputs;
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-03 12:24:00 +01:00
|
|
|
namespace anonymous_attribute_lifetime {
|
|
|
|
|
|
|
|
|
|
std::ostream &operator<<(std::ostream &stream, const RelationsInNode &relations)
|
|
|
|
|
{
|
|
|
|
|
stream << "Propagate Relations: " << relations.propagate_relations.size() << "\n";
|
|
|
|
|
for (const PropagateRelation &relation : relations.propagate_relations) {
|
|
|
|
|
stream << " " << relation.from_geometry_input << " -> " << relation.to_geometry_output
|
|
|
|
|
<< "\n";
|
|
|
|
|
}
|
|
|
|
|
stream << "Reference Relations: " << relations.reference_relations.size() << "\n";
|
|
|
|
|
for (const ReferenceRelation &relation : relations.reference_relations) {
|
|
|
|
|
stream << " " << relation.from_field_input << " -> " << relation.to_field_output << "\n";
|
|
|
|
|
}
|
|
|
|
|
stream << "Eval Relations: " << relations.eval_relations.size() << "\n";
|
|
|
|
|
for (const EvalRelation &relation : relations.eval_relations) {
|
|
|
|
|
stream << " eval " << relation.field_input << " on " << relation.geometry_input << "\n";
|
|
|
|
|
}
|
|
|
|
|
stream << "Available Relations: " << relations.available_relations.size() << "\n";
|
|
|
|
|
for (const AvailableRelation &relation : relations.available_relations) {
|
|
|
|
|
stream << " " << relation.field_output << " available on " << relation.geometry_output
|
|
|
|
|
<< "\n";
|
|
|
|
|
}
|
|
|
|
|
stream << "Available on None: " << relations.available_on_none.size() << "\n";
|
|
|
|
|
for (const int i : relations.available_on_none) {
|
|
|
|
|
stream << " output " << i << " available on none\n";
|
|
|
|
|
}
|
|
|
|
|
return stream;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace anonymous_attribute_lifetime
|
|
|
|
|
|
2024-10-09 15:45:43 +02:00
|
|
|
static void assert_valid_panels_recursive(const NodeDeclaration &node_decl,
|
|
|
|
|
const Span<const ItemDeclaration *> items,
|
|
|
|
|
Vector<const SocketDeclaration *> &r_flat_inputs,
|
|
|
|
|
Vector<const SocketDeclaration *> &r_flat_outputs)
|
|
|
|
|
{
|
|
|
|
|
/* Expected item order unless any order is allowed: outputs, inputs, panels. */
|
|
|
|
|
bool found_input = false;
|
|
|
|
|
bool found_panel = false;
|
|
|
|
|
|
|
|
|
|
for (const ItemDeclaration *item_decl : items) {
|
|
|
|
|
if (const auto *socket_decl = dynamic_cast<const SocketDeclaration *>(item_decl)) {
|
|
|
|
|
if (socket_decl->in_out == SOCK_IN) {
|
|
|
|
|
BLI_assert(node_decl.allow_any_socket_order || !found_panel);
|
|
|
|
|
found_input = true;
|
|
|
|
|
r_flat_inputs.append(socket_decl);
|
2023-09-22 16:56:59 +02:00
|
|
|
}
|
2024-10-09 15:45:43 +02:00
|
|
|
else {
|
|
|
|
|
BLI_assert(node_decl.allow_any_socket_order || (!found_input && !found_panel));
|
|
|
|
|
r_flat_outputs.append(socket_decl);
|
2023-09-11 13:39:28 +02:00
|
|
|
}
|
|
|
|
|
}
|
2024-10-09 15:45:43 +02:00
|
|
|
else if (const auto *panel_decl = dynamic_cast<const PanelDeclaration *>(item_decl)) {
|
|
|
|
|
found_panel = true;
|
|
|
|
|
assert_valid_panels_recursive(node_decl, panel_decl->items, r_flat_inputs, r_flat_outputs);
|
2023-09-11 13:39:28 +02:00
|
|
|
}
|
|
|
|
|
}
|
2024-10-10 18:36:08 +11:00
|
|
|
UNUSED_VARS(found_input, found_panel);
|
2024-10-09 15:45:43 +02:00
|
|
|
}
|
2023-09-11 13:39:28 +02:00
|
|
|
|
2024-10-09 15:45:43 +02:00
|
|
|
void NodeDeclaration::assert_valid() const
|
|
|
|
|
{
|
|
|
|
|
if (!this->use_custom_socket_order) {
|
|
|
|
|
/* Skip validation for conventional socket layouts. Those are reordered in drawing code. */
|
|
|
|
|
return;
|
2023-09-11 13:39:28 +02:00
|
|
|
}
|
2024-10-09 15:45:43 +02:00
|
|
|
|
|
|
|
|
Vector<const SocketDeclaration *> flat_inputs;
|
|
|
|
|
Vector<const SocketDeclaration *> flat_outputs;
|
|
|
|
|
assert_valid_panels_recursive(*this, this->root_items, flat_inputs, flat_outputs);
|
|
|
|
|
|
|
|
|
|
BLI_assert(this->inputs.as_span() == flat_inputs);
|
|
|
|
|
BLI_assert(this->outputs.as_span() == flat_outputs);
|
2023-09-11 13:39:28 +02:00
|
|
|
}
|
|
|
|
|
|
2021-08-30 17:13:46 +02:00
|
|
|
bool NodeDeclaration::matches(const bNode &node) const
|
|
|
|
|
{
|
2023-08-30 12:37:21 +02:00
|
|
|
const bNodeSocket *current_input = static_cast<bNodeSocket *>(node.inputs.first);
|
|
|
|
|
const bNodeSocket *current_output = static_cast<bNodeSocket *>(node.outputs.first);
|
|
|
|
|
const bNodePanelState *current_panel = node.panel_states_array;
|
2024-10-09 15:45:43 +02:00
|
|
|
for (const ItemDeclarationPtr &item_decl : this->all_items) {
|
2023-08-30 12:37:21 +02:00
|
|
|
if (const SocketDeclaration *socket_decl = dynamic_cast<const SocketDeclaration *>(
|
|
|
|
|
item_decl.get()))
|
|
|
|
|
{
|
|
|
|
|
switch (socket_decl->in_out) {
|
|
|
|
|
case SOCK_IN:
|
|
|
|
|
if (current_input == nullptr || !socket_decl->matches(*current_input)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
current_input = current_input->next;
|
|
|
|
|
break;
|
|
|
|
|
case SOCK_OUT:
|
|
|
|
|
if (current_output == nullptr || !socket_decl->matches(*current_output)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
current_output = current_output->next;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2021-08-30 17:13:46 +02:00
|
|
|
}
|
2023-08-30 12:37:21 +02:00
|
|
|
else if (const PanelDeclaration *panel_decl = dynamic_cast<const PanelDeclaration *>(
|
|
|
|
|
item_decl.get()))
|
|
|
|
|
{
|
|
|
|
|
if (!node.panel_states().contains_ptr(current_panel) || !panel_decl->matches(*current_panel))
|
|
|
|
|
{
|
2021-08-30 17:13:46 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
2023-08-30 12:37:21 +02:00
|
|
|
++current_panel;
|
|
|
|
|
}
|
2024-10-11 12:20:58 +02:00
|
|
|
else if (dynamic_cast<const SeparatorDeclaration *>(item_decl.get()) ||
|
|
|
|
|
dynamic_cast<const LayoutDeclaration *>(item_decl.get()))
|
|
|
|
|
{
|
|
|
|
|
/* Ignored because they don't have corresponding data in DNA. */
|
2024-09-12 17:38:51 +02:00
|
|
|
}
|
2023-08-30 12:37:21 +02:00
|
|
|
else {
|
|
|
|
|
/* Unknown item type. */
|
|
|
|
|
BLI_assert_unreachable();
|
2021-08-30 17:13:46 +02:00
|
|
|
}
|
|
|
|
|
}
|
2023-08-30 12:37:21 +02:00
|
|
|
/* If items are left over, some were removed from the declaration. */
|
2024-09-11 13:59:16 +02:00
|
|
|
if (current_input != nullptr || current_output != nullptr ||
|
|
|
|
|
node.panel_states().contains_ptr(current_panel))
|
2023-08-30 12:37:21 +02:00
|
|
|
{
|
2021-08-30 17:13:46 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bNodeSocket &SocketDeclaration::update_or_build(bNodeTree &ntree,
|
|
|
|
|
bNode &node,
|
|
|
|
|
bNodeSocket &socket) const
|
|
|
|
|
{
|
|
|
|
|
/* By default just rebuild. */
|
2022-12-29 15:36:33 -05:00
|
|
|
BLI_assert(socket.in_out == this->in_out);
|
2021-12-15 09:51:57 -06:00
|
|
|
UNUSED_VARS_NDEBUG(socket);
|
|
|
|
|
return this->build(ntree, node);
|
2021-08-30 17:13:46 +02:00
|
|
|
}
|
|
|
|
|
|
2021-09-15 16:09:00 +02:00
|
|
|
void SocketDeclaration::set_common_flags(bNodeSocket &socket) const
|
|
|
|
|
{
|
2022-12-29 14:55:27 -05:00
|
|
|
SET_FLAG_FROM_TEST(socket.flag, compact, SOCK_COMPACT);
|
|
|
|
|
SET_FLAG_FROM_TEST(socket.flag, hide_value, SOCK_HIDE_VALUE);
|
|
|
|
|
SET_FLAG_FROM_TEST(socket.flag, hide_label, SOCK_HIDE_LABEL);
|
|
|
|
|
SET_FLAG_FROM_TEST(socket.flag, is_multi_input, SOCK_MULTI_INPUT);
|
|
|
|
|
SET_FLAG_FROM_TEST(socket.flag, no_mute_links, SOCK_NO_INTERNAL_LINK);
|
2024-09-18 16:08:05 +02:00
|
|
|
SET_FLAG_FROM_TEST(socket.flag, !is_available, SOCK_UNAVAIL);
|
2021-09-15 16:09:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool SocketDeclaration::matches_common_data(const bNodeSocket &socket) const
|
|
|
|
|
{
|
2022-12-29 14:55:27 -05:00
|
|
|
if (socket.name != this->name) {
|
2021-09-15 16:09:00 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
2022-12-29 14:55:27 -05:00
|
|
|
if (socket.identifier != this->identifier) {
|
2021-09-15 16:09:00 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
2022-12-29 14:55:27 -05:00
|
|
|
if (((socket.flag & SOCK_COMPACT) != 0) != this->compact) {
|
2021-12-06 13:39:19 -05:00
|
|
|
return false;
|
|
|
|
|
}
|
2022-12-29 14:55:27 -05:00
|
|
|
if (((socket.flag & SOCK_HIDE_VALUE) != 0) != this->hide_value) {
|
2021-09-15 16:09:00 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
2022-12-29 14:55:27 -05:00
|
|
|
if (((socket.flag & SOCK_HIDE_LABEL) != 0) != this->hide_label) {
|
2021-09-15 16:09:00 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
2022-12-29 14:55:27 -05:00
|
|
|
if (((socket.flag & SOCK_MULTI_INPUT) != 0) != this->is_multi_input) {
|
2021-09-15 16:09:00 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
2022-12-29 14:55:27 -05:00
|
|
|
if (((socket.flag & SOCK_NO_INTERNAL_LINK) != 0) != this->no_mute_links) {
|
2021-09-22 17:34:09 -05:00
|
|
|
return false;
|
|
|
|
|
}
|
2024-09-18 16:08:05 +02:00
|
|
|
if (((socket.flag & SOCK_UNAVAIL) != 0) != !this->is_available) {
|
2021-12-25 11:12:43 -05:00
|
|
|
return false;
|
|
|
|
|
}
|
2021-09-15 16:09:00 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-30 15:39:11 +02:00
|
|
|
template<typename Fn>
|
|
|
|
|
static bool socket_type_to_static_decl_type(const eNodeSocketDatatype socket_type, Fn &&fn)
|
2023-10-04 13:01:45 +02:00
|
|
|
{
|
|
|
|
|
switch (socket_type) {
|
|
|
|
|
case SOCK_FLOAT:
|
2024-09-30 15:39:11 +02:00
|
|
|
fn(TypeTag<decl::Float>());
|
|
|
|
|
return true;
|
2023-10-04 13:01:45 +02:00
|
|
|
case SOCK_VECTOR:
|
2024-09-30 15:39:11 +02:00
|
|
|
fn(TypeTag<decl::Vector>());
|
|
|
|
|
return true;
|
2023-10-04 13:01:45 +02:00
|
|
|
case SOCK_RGBA:
|
2024-09-30 15:39:11 +02:00
|
|
|
fn(TypeTag<decl::Color>());
|
|
|
|
|
return true;
|
2023-10-04 13:01:45 +02:00
|
|
|
case SOCK_BOOLEAN:
|
2024-09-30 15:39:11 +02:00
|
|
|
fn(TypeTag<decl::Bool>());
|
|
|
|
|
return true;
|
2023-10-04 13:01:45 +02:00
|
|
|
case SOCK_ROTATION:
|
2024-09-30 15:39:11 +02:00
|
|
|
fn(TypeTag<decl::Rotation>());
|
|
|
|
|
return true;
|
2024-02-13 18:59:36 +01:00
|
|
|
case SOCK_MATRIX:
|
2024-09-30 15:39:11 +02:00
|
|
|
fn(TypeTag<decl::Matrix>());
|
|
|
|
|
return true;
|
2023-10-04 13:01:45 +02:00
|
|
|
case SOCK_INT:
|
2024-09-30 15:39:11 +02:00
|
|
|
fn(TypeTag<decl::Int>());
|
|
|
|
|
return true;
|
2023-10-04 13:01:45 +02:00
|
|
|
case SOCK_STRING:
|
2024-09-30 15:39:11 +02:00
|
|
|
fn(TypeTag<decl::String>());
|
|
|
|
|
return true;
|
2023-10-04 13:01:45 +02:00
|
|
|
case SOCK_GEOMETRY:
|
2024-09-30 15:39:11 +02:00
|
|
|
fn(TypeTag<decl::Geometry>());
|
|
|
|
|
return true;
|
2023-10-04 13:01:45 +02:00
|
|
|
case SOCK_OBJECT:
|
2024-09-30 15:39:11 +02:00
|
|
|
fn(TypeTag<decl::Object>());
|
|
|
|
|
return true;
|
2023-10-04 13:01:45 +02:00
|
|
|
case SOCK_IMAGE:
|
2024-09-30 15:39:11 +02:00
|
|
|
fn(TypeTag<decl::Image>());
|
|
|
|
|
return true;
|
2023-10-04 13:01:45 +02:00
|
|
|
case SOCK_COLLECTION:
|
2024-09-30 15:39:11 +02:00
|
|
|
fn(TypeTag<decl::Collection>());
|
|
|
|
|
return true;
|
2023-10-04 13:01:45 +02:00
|
|
|
case SOCK_MATERIAL:
|
2024-09-30 15:39:11 +02:00
|
|
|
fn(TypeTag<decl::Material>());
|
|
|
|
|
return true;
|
2024-01-26 12:40:01 +01:00
|
|
|
case SOCK_MENU:
|
2024-09-30 15:39:11 +02:00
|
|
|
fn(TypeTag<decl::Menu>());
|
|
|
|
|
return true;
|
2025-04-03 15:44:06 +02:00
|
|
|
case SOCK_BUNDLE:
|
|
|
|
|
fn(TypeTag<decl::Bundle>());
|
|
|
|
|
return true;
|
|
|
|
|
case SOCK_CLOSURE:
|
|
|
|
|
fn(TypeTag<decl::Closure>());
|
|
|
|
|
return true;
|
2023-10-04 13:01:45 +02:00
|
|
|
default:
|
2024-09-30 15:39:11 +02:00
|
|
|
return false;
|
2023-10-04 13:01:45 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-30 15:39:11 +02:00
|
|
|
std::unique_ptr<SocketDeclaration> make_declaration_for_socket_type(
|
|
|
|
|
const eNodeSocketDatatype socket_type)
|
|
|
|
|
{
|
|
|
|
|
std::unique_ptr<SocketDeclaration> decl;
|
|
|
|
|
socket_type_to_static_decl_type(socket_type, [&](auto type_tag) {
|
|
|
|
|
using DeclT = typename decltype(type_tag)::type;
|
|
|
|
|
decl = std::make_unique<DeclT>();
|
|
|
|
|
});
|
|
|
|
|
return decl;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-09 15:45:43 +02:00
|
|
|
BaseSocketDeclarationBuilder &DeclarationListBuilder::add_input(
|
2023-10-08 13:58:22 +02:00
|
|
|
const eNodeSocketDatatype socket_type, const StringRef name, const StringRef identifier)
|
|
|
|
|
{
|
2024-09-30 15:39:11 +02:00
|
|
|
BaseSocketDeclarationBuilder *decl = nullptr;
|
|
|
|
|
socket_type_to_static_decl_type(socket_type, [&](auto type_tag) {
|
|
|
|
|
using DeclT = typename decltype(type_tag)::type;
|
|
|
|
|
decl = &this->add_input<DeclT>(name, identifier);
|
|
|
|
|
});
|
|
|
|
|
if (!decl) {
|
|
|
|
|
BLI_assert_unreachable();
|
|
|
|
|
decl = &this->add_input<decl::Float>("", "");
|
2023-10-08 13:58:22 +02:00
|
|
|
}
|
2024-09-30 15:39:11 +02:00
|
|
|
return *decl;
|
2023-10-08 13:58:22 +02:00
|
|
|
}
|
|
|
|
|
|
2024-10-09 15:45:43 +02:00
|
|
|
BaseSocketDeclarationBuilder &DeclarationListBuilder::add_input(const eCustomDataType data_type,
|
2023-11-17 16:23:34 +01:00
|
|
|
const StringRef name,
|
|
|
|
|
const StringRef identifier)
|
|
|
|
|
{
|
|
|
|
|
return this->add_input(*bke::custom_data_type_to_socket_type(data_type), name, identifier);
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-09 15:45:43 +02:00
|
|
|
BaseSocketDeclarationBuilder &DeclarationListBuilder::add_output(
|
2023-10-08 13:58:22 +02:00
|
|
|
const eNodeSocketDatatype socket_type, const StringRef name, const StringRef identifier)
|
|
|
|
|
{
|
2024-09-30 15:39:11 +02:00
|
|
|
BaseSocketDeclarationBuilder *decl = nullptr;
|
|
|
|
|
socket_type_to_static_decl_type(socket_type, [&](auto type_tag) {
|
|
|
|
|
using DeclT = typename decltype(type_tag)::type;
|
|
|
|
|
decl = &this->add_output<DeclT>(name, identifier);
|
|
|
|
|
});
|
|
|
|
|
if (!decl) {
|
|
|
|
|
BLI_assert_unreachable();
|
|
|
|
|
decl = &this->add_output<decl::Float>("", "");
|
2023-10-08 13:58:22 +02:00
|
|
|
}
|
2024-09-30 15:39:11 +02:00
|
|
|
return *decl;
|
2023-10-08 13:58:22 +02:00
|
|
|
}
|
|
|
|
|
|
2024-10-09 15:45:43 +02:00
|
|
|
BaseSocketDeclarationBuilder &DeclarationListBuilder::add_output(const eCustomDataType data_type,
|
2023-11-17 16:23:34 +01:00
|
|
|
const StringRef name,
|
|
|
|
|
const StringRef identifier)
|
|
|
|
|
{
|
|
|
|
|
return this->add_output(*bke::custom_data_type_to_socket_type(data_type), name, identifier);
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-09 15:45:43 +02:00
|
|
|
void DeclarationListBuilder::add_separator()
|
2024-09-12 17:38:51 +02:00
|
|
|
{
|
2024-10-09 15:45:43 +02:00
|
|
|
auto decl_ptr = std::make_unique<SeparatorDeclaration>();
|
|
|
|
|
SeparatorDeclaration &decl = *decl_ptr;
|
|
|
|
|
this->node_decl_builder.declaration_.all_items.append(std::move(decl_ptr));
|
|
|
|
|
this->items.append(&decl);
|
2024-09-12 17:38:51 +02:00
|
|
|
}
|
|
|
|
|
|
2024-10-11 12:20:58 +02:00
|
|
|
void DeclarationListBuilder::add_default_layout()
|
|
|
|
|
{
|
|
|
|
|
BLI_assert(this->node_decl_builder.typeinfo_.draw_buttons);
|
|
|
|
|
this->add_layout([](uiLayout *layout, bContext *C, PointerRNA *ptr) {
|
|
|
|
|
const bNode &node = *static_cast<bNode *>(ptr->data);
|
|
|
|
|
node.typeinfo->draw_buttons(layout, C, ptr);
|
|
|
|
|
});
|
|
|
|
|
static_cast<LayoutDeclaration &>(*this->items.last()).is_default = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DeclarationListBuilder::add_layout(
|
|
|
|
|
std::function<void(uiLayout *, bContext *, PointerRNA *)> draw)
|
|
|
|
|
{
|
|
|
|
|
auto decl_ptr = std::make_unique<LayoutDeclaration>();
|
|
|
|
|
LayoutDeclaration &decl = *decl_ptr;
|
|
|
|
|
decl.draw = std::move(draw);
|
|
|
|
|
this->node_decl_builder.declaration_.all_items.append(std::move(decl_ptr));
|
|
|
|
|
this->items.append(&decl);
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-09 15:45:43 +02:00
|
|
|
PanelDeclarationBuilder &DeclarationListBuilder::add_panel(const StringRef name, int identifier)
|
2024-09-30 16:07:26 +02:00
|
|
|
{
|
2024-10-09 15:45:43 +02:00
|
|
|
auto panel_decl_ptr = std::make_unique<PanelDeclaration>();
|
|
|
|
|
PanelDeclaration &panel_decl = *panel_decl_ptr;
|
|
|
|
|
auto panel_decl_builder_ptr = std::make_unique<PanelDeclarationBuilder>(this->node_decl_builder,
|
|
|
|
|
panel_decl);
|
|
|
|
|
PanelDeclarationBuilder &panel_decl_builder = *panel_decl_builder_ptr;
|
2024-09-30 16:07:26 +02:00
|
|
|
|
2024-10-09 15:45:43 +02:00
|
|
|
if (identifier >= 0) {
|
|
|
|
|
panel_decl.identifier = identifier;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
/* Use index as identifier. */
|
|
|
|
|
panel_decl.identifier = this->node_decl_builder.declaration_.all_items.size();
|
|
|
|
|
}
|
|
|
|
|
panel_decl.name = name;
|
2024-10-11 12:20:58 +02:00
|
|
|
panel_decl.parent_panel = this->parent_panel_decl;
|
|
|
|
|
panel_decl.index = this->node_decl_builder.declaration_.panels.append_and_get_index(&panel_decl);
|
2024-10-09 15:45:43 +02:00
|
|
|
this->node_decl_builder.declaration_.all_items.append(std::move(panel_decl_ptr));
|
2024-10-11 12:20:58 +02:00
|
|
|
this->node_decl_builder.panel_builders_.append_and_get_index(std::move(panel_decl_builder_ptr));
|
2024-10-09 15:45:43 +02:00
|
|
|
this->items.append(&panel_decl);
|
|
|
|
|
return panel_decl_builder;
|
2024-09-30 16:07:26 +02:00
|
|
|
}
|
|
|
|
|
|
2024-10-09 15:45:43 +02:00
|
|
|
void PanelDeclaration::build(bNodePanelState &panel) const
|
2024-09-30 16:07:26 +02:00
|
|
|
{
|
2024-10-09 15:45:43 +02:00
|
|
|
panel = {0};
|
|
|
|
|
panel.identifier = this->identifier;
|
|
|
|
|
SET_FLAG_FROM_TEST(panel.flag, this->default_collapsed, NODE_PANEL_COLLAPSED);
|
2024-09-30 16:07:26 +02:00
|
|
|
}
|
|
|
|
|
|
2024-10-09 15:45:43 +02:00
|
|
|
bool PanelDeclaration::matches(const bNodePanelState &panel) const
|
2024-09-30 16:07:26 +02:00
|
|
|
{
|
2024-10-09 15:45:43 +02:00
|
|
|
return panel.identifier == this->identifier;
|
2024-09-30 16:07:26 +02:00
|
|
|
}
|
|
|
|
|
|
2024-10-09 15:45:43 +02:00
|
|
|
void PanelDeclaration::update_or_build(const bNodePanelState &old_panel,
|
|
|
|
|
bNodePanelState &new_panel) const
|
2024-09-30 16:07:26 +02:00
|
|
|
{
|
2024-10-09 15:45:43 +02:00
|
|
|
build(new_panel);
|
|
|
|
|
/* Copy existing state to the new panel */
|
|
|
|
|
SET_FLAG_FROM_TEST(new_panel.flag, old_panel.is_collapsed(), NODE_PANEL_COLLAPSED);
|
2024-09-30 16:07:26 +02:00
|
|
|
}
|
|
|
|
|
|
2024-10-11 12:20:58 +02:00
|
|
|
int PanelDeclaration::depth() const
|
|
|
|
|
{
|
|
|
|
|
int count = 0;
|
|
|
|
|
for (const PanelDeclaration *parent = this->parent_panel; parent; parent = parent->parent_panel)
|
|
|
|
|
{
|
|
|
|
|
count++;
|
|
|
|
|
}
|
|
|
|
|
return count;
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-28 19:07:02 +01:00
|
|
|
const nodes::SocketDeclaration *PanelDeclaration::panel_input_decl() const
|
|
|
|
|
{
|
|
|
|
|
if (this->items.is_empty()) {
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
const nodes::ItemDeclaration *item_decl = this->items.first();
|
|
|
|
|
if (const auto *socket_decl = dynamic_cast<const nodes::SocketDeclaration *>(item_decl)) {
|
|
|
|
|
if (socket_decl->is_panel_toggle && (socket_decl->in_out & SOCK_IN) &&
|
|
|
|
|
(socket_decl->socket_type & SOCK_BOOLEAN))
|
|
|
|
|
{
|
|
|
|
|
return socket_decl;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-08 13:30:31 +02:00
|
|
|
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::supports_field()
|
|
|
|
|
{
|
2024-03-18 14:36:03 +01:00
|
|
|
BLI_assert(this->is_input());
|
|
|
|
|
decl_base_->input_field_type = InputSocketFieldType::IsSupported;
|
2025-05-28 02:33:47 +02:00
|
|
|
this->structure_type(StructureType::Field);
|
2023-10-08 13:30:31 +02:00
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::dependent_field(
|
|
|
|
|
Vector<int> input_dependencies)
|
|
|
|
|
{
|
2024-03-18 14:36:03 +01:00
|
|
|
BLI_assert(this->is_output());
|
2023-10-08 13:30:31 +02:00
|
|
|
this->reference_pass(input_dependencies);
|
2024-03-18 14:36:03 +01:00
|
|
|
decl_base_->output_field_dependency = OutputFieldDependency::ForPartiallyDependentField(
|
|
|
|
|
std::move(input_dependencies));
|
2025-05-28 02:33:47 +02:00
|
|
|
this->structure_type(StructureType::Dynamic);
|
2023-10-08 13:30:31 +02:00
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::hide_label(bool value)
|
|
|
|
|
{
|
2024-03-18 14:36:03 +01:00
|
|
|
decl_base_->hide_label = value;
|
2023-10-08 13:30:31 +02:00
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::hide_value(bool value)
|
|
|
|
|
{
|
2024-03-18 14:36:03 +01:00
|
|
|
decl_base_->hide_value = value;
|
2023-10-08 13:30:31 +02:00
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::multi_input(bool value)
|
|
|
|
|
{
|
2024-03-18 14:36:03 +01:00
|
|
|
BLI_assert(this->is_input());
|
|
|
|
|
decl_base_->is_multi_input = value;
|
2023-10-08 13:30:31 +02:00
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-09 15:45:43 +02:00
|
|
|
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::compact(bool value)
|
|
|
|
|
{
|
|
|
|
|
decl_base_->compact = value;
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-08 13:30:31 +02:00
|
|
|
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::reference_pass(
|
|
|
|
|
const Span<int> input_indices)
|
|
|
|
|
{
|
2024-03-18 14:36:03 +01:00
|
|
|
BLI_assert(this->is_output());
|
2023-10-08 13:30:31 +02:00
|
|
|
aal::RelationsInNode &relations = node_decl_builder_->get_anonymous_attribute_relations();
|
|
|
|
|
for (const int from_input : input_indices) {
|
|
|
|
|
aal::ReferenceRelation relation;
|
|
|
|
|
relation.from_field_input = from_input;
|
2024-10-11 12:20:58 +02:00
|
|
|
relation.to_field_output = decl_base_->index;
|
2023-10-08 13:30:31 +02:00
|
|
|
relations.reference_relations.append(relation);
|
|
|
|
|
}
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::field_on(const Span<int> indices)
|
|
|
|
|
{
|
|
|
|
|
aal::RelationsInNode &relations = node_decl_builder_->get_anonymous_attribute_relations();
|
2024-03-18 14:36:03 +01:00
|
|
|
if (this->is_input()) {
|
2023-10-08 13:30:31 +02:00
|
|
|
this->supports_field();
|
|
|
|
|
for (const int input_index : indices) {
|
|
|
|
|
aal::EvalRelation relation;
|
2024-10-11 12:20:58 +02:00
|
|
|
relation.field_input = decl_base_->index;
|
2023-10-08 13:30:31 +02:00
|
|
|
relation.geometry_input = input_index;
|
|
|
|
|
relations.eval_relations.append(relation);
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-03-18 14:36:03 +01:00
|
|
|
else {
|
2023-10-08 13:30:31 +02:00
|
|
|
this->field_source();
|
|
|
|
|
for (const int output_index : indices) {
|
|
|
|
|
aal::AvailableRelation relation;
|
2024-10-11 12:20:58 +02:00
|
|
|
relation.field_output = decl_base_->index;
|
2023-10-08 13:30:31 +02:00
|
|
|
relation.geometry_output = output_index;
|
|
|
|
|
relations.available_relations.append(relation);
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-05-28 02:33:47 +02:00
|
|
|
this->structure_type(StructureType::Field);
|
2023-10-08 13:30:31 +02:00
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-12 18:53:43 +02:00
|
|
|
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::short_label(std::string value)
|
|
|
|
|
{
|
2024-03-18 14:36:03 +01:00
|
|
|
decl_base_->short_label = std::move(value);
|
2023-10-12 18:53:43 +02:00
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-08 13:30:31 +02:00
|
|
|
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::description(std::string value)
|
|
|
|
|
{
|
2024-03-18 14:36:03 +01:00
|
|
|
decl_base_->description = std::move(value);
|
2023-10-08 13:30:31 +02:00
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-16 23:48:43 +01:00
|
|
|
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::translation_context(
|
|
|
|
|
std::optional<std::string> value)
|
2023-10-08 13:30:31 +02:00
|
|
|
{
|
2024-03-18 14:36:03 +01:00
|
|
|
decl_base_->translation_context = std::move(value);
|
2023-10-08 13:30:31 +02:00
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::no_muted_links(bool value)
|
|
|
|
|
{
|
2024-03-18 14:36:03 +01:00
|
|
|
decl_base_->no_mute_links = value;
|
2023-10-08 13:30:31 +02:00
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-18 16:08:05 +02:00
|
|
|
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::available(bool value)
|
2023-10-08 13:30:31 +02:00
|
|
|
{
|
2024-09-18 16:08:05 +02:00
|
|
|
decl_base_->is_available = value;
|
2023-10-08 13:30:31 +02:00
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::is_attribute_name(bool value)
|
|
|
|
|
{
|
2024-03-18 14:36:03 +01:00
|
|
|
decl_base_->is_attribute_name = value;
|
2023-10-08 13:30:31 +02:00
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::is_default_link_socket(bool value)
|
|
|
|
|
{
|
2024-03-18 14:36:03 +01:00
|
|
|
decl_base_->is_default_link_socket = value;
|
2023-10-08 13:30:31 +02:00
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-20 17:54:49 +02:00
|
|
|
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::default_input_type(
|
|
|
|
|
const NodeDefaultInputType value)
|
|
|
|
|
{
|
|
|
|
|
decl_base_->default_input_type = value;
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-08 13:30:31 +02:00
|
|
|
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::field_on_all()
|
|
|
|
|
{
|
2024-03-18 14:36:03 +01:00
|
|
|
if (this->is_input()) {
|
2023-10-08 13:30:31 +02:00
|
|
|
this->supports_field();
|
|
|
|
|
}
|
2024-03-18 14:36:03 +01:00
|
|
|
if (this->is_output()) {
|
2023-10-08 13:30:31 +02:00
|
|
|
this->field_source();
|
|
|
|
|
}
|
|
|
|
|
field_on_all_ = true;
|
2025-05-28 02:33:47 +02:00
|
|
|
this->structure_type(StructureType::Field);
|
2023-10-08 13:30:31 +02:00
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::field_source()
|
|
|
|
|
{
|
2024-03-18 14:36:03 +01:00
|
|
|
BLI_assert(this->is_output());
|
|
|
|
|
decl_base_->output_field_dependency = OutputFieldDependency::ForFieldSource();
|
2025-05-28 02:33:47 +02:00
|
|
|
this->structure_type(StructureType::Field);
|
2023-10-08 13:30:31 +02:00
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-20 17:54:49 +02:00
|
|
|
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::implicit_field(
|
|
|
|
|
const NodeDefaultInputType default_input_type)
|
2023-10-08 13:30:31 +02:00
|
|
|
{
|
2024-03-18 14:36:03 +01:00
|
|
|
BLI_assert(this->is_input());
|
2023-10-08 13:30:31 +02:00
|
|
|
this->hide_value();
|
2025-05-28 02:33:47 +02:00
|
|
|
this->structure_type(StructureType::Dynamic);
|
2024-03-18 14:36:03 +01:00
|
|
|
decl_base_->input_field_type = InputSocketFieldType::Implicit;
|
2025-05-20 17:54:49 +02:00
|
|
|
decl_base_->default_input_type = default_input_type;
|
2023-10-08 13:30:31 +02:00
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::implicit_field_on_all(
|
2025-05-20 17:54:49 +02:00
|
|
|
const NodeDefaultInputType default_input_type)
|
2023-10-08 13:30:31 +02:00
|
|
|
{
|
2025-05-20 17:54:49 +02:00
|
|
|
this->implicit_field(default_input_type);
|
2023-10-08 13:30:31 +02:00
|
|
|
field_on_all_ = true;
|
2025-05-28 02:33:47 +02:00
|
|
|
this->structure_type(StructureType::Field);
|
2023-10-08 13:30:31 +02:00
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::implicit_field_on(
|
2025-05-20 17:54:49 +02:00
|
|
|
const NodeDefaultInputType default_input_type, const Span<int> input_indices)
|
2023-10-08 13:30:31 +02:00
|
|
|
{
|
|
|
|
|
this->field_on(input_indices);
|
2025-05-20 17:54:49 +02:00
|
|
|
this->implicit_field(default_input_type);
|
2025-05-28 02:33:47 +02:00
|
|
|
this->structure_type(StructureType::Field);
|
2023-10-08 13:30:31 +02:00
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::dependent_field()
|
|
|
|
|
{
|
2024-03-18 14:36:03 +01:00
|
|
|
BLI_assert(this->is_output());
|
|
|
|
|
decl_base_->output_field_dependency = OutputFieldDependency::ForDependentField();
|
2025-05-28 02:33:47 +02:00
|
|
|
this->structure_type(StructureType::Dynamic);
|
2023-10-08 13:30:31 +02:00
|
|
|
this->reference_pass_all();
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::field_source_reference_all()
|
|
|
|
|
{
|
|
|
|
|
this->field_source();
|
|
|
|
|
this->reference_pass_all();
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::reference_pass_all()
|
|
|
|
|
{
|
|
|
|
|
reference_pass_all_ = true;
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::propagate_all()
|
|
|
|
|
{
|
|
|
|
|
propagate_from_all_ = true;
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-14 12:41:11 +01:00
|
|
|
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::propagate_all_instance_attributes()
|
|
|
|
|
{
|
|
|
|
|
/* We can't distinguish between actually propagating everything or just instance attributes
|
|
|
|
|
* currently. It's still nice to be more explicit at the node declaration level. */
|
|
|
|
|
this->propagate_all();
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-31 10:14:54 +01:00
|
|
|
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::compositor_realization_mode(
|
|
|
|
|
CompositorInputRealizationMode value)
|
2023-10-08 13:30:31 +02:00
|
|
|
{
|
2025-01-31 10:14:54 +01:00
|
|
|
decl_base_->compositor_realization_mode_ = value;
|
2023-10-08 13:30:31 +02:00
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::compositor_domain_priority(
|
|
|
|
|
int priority)
|
|
|
|
|
{
|
2025-03-26 16:14:05 +02:00
|
|
|
BLI_assert(priority >= 0);
|
2024-03-18 14:36:03 +01:00
|
|
|
decl_base_->compositor_domain_priority_ = priority;
|
2023-10-08 13:30:31 +02:00
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::make_available(
|
|
|
|
|
std::function<void(bNode &)> fn)
|
|
|
|
|
{
|
2024-03-18 14:36:03 +01:00
|
|
|
decl_base_->make_available_fn_ = std::move(fn);
|
2023-10-08 13:30:31 +02:00
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-26 15:47:54 +02:00
|
|
|
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::custom_draw(CustomSocketDrawFn fn)
|
|
|
|
|
{
|
|
|
|
|
decl_base_->custom_draw_fn = std::make_unique<CustomSocketDrawFn>(std::move(fn));
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
Nodes: initial support for built-in menu sockets
So far, only node group were able to have menu input sockets. Built-in nodes did
not support them. Currently, all menus of built-in nodes are stored on the node
instead of on the sockets. This limits their flexibility because it's not
possible to expose these inputs.
This patch adds initial support for having menu inputs in built-in nodes. For
testing purposes, it also changes a couple built-in nodes to use an input socket
instead of a node property: Points to Volume, Transform Geometry, Triangulate,
Volume to Mesh and Match String.
### Compatibility
Forward and backward compatibility is maintained where possible (it's not
possible when the menu input is linked in 5.0). The overall compatibility
approach is the same as what was done for the compositor with two differences:
there are no wrapper RNA properties (not necessary for 5.0, those were removed
for the compositor already too), no need to version animation (animation on the
menu properties was already disabled).
This also makes menu sockets not animatable in general which is kind of brittle
(e.g. doesn't properly update when the menu definition changes). To animate a
menu it's better to animate an integer and to drive an index switch with it.
### Which nodes to update?
Many existing menu properties can become sockets, but it's currently not the
intention to convert all of them. In some cases, converting them might restrict
future improvements too much. This mainly affects Math nodes.
Other existing nodes should be updated but are a bit more tricky to update for
different reasons:
* We don't support dynamic output visibility yet. This is something I'll need to
look into at some point.
* They are shared with shader/compositor nodes, which may be more limited in
what can become a socket.
* There may be performance implications unless extra special cases are
implemented, especially for multi-function nodes.
* Some nodes use socket renaming instead of dynamic socket visibility which
isn't something we support more generally yet.
### Implementation
The core implementation is fairly straight forward. The heavy lifting is done by
the existing socket visibility inferencing. There is a new simple API that
allows individual nodes to implement custom input-usage-rules based on other
inputs in a decentralized way.
In most cases, the nodes to update just have a single menu, so there is a new
node-declaration utility that links a socket to a specific value of the menu
input. This internally handles the usage inferencing as well as making the
socket available when using link-drag-search.
In the modified nodes, I also had to explicitly set the "main input" now which
is used when inserting the node in a link. The automatic behavior doesn't work
currently when the first input is a menu. This is something we'll have to solve
more generally at some point but is out of scope for this patch.
Pull Request: https://projects.blender.org/blender/blender/pulls/140705
2025-07-16 08:31:59 +02:00
|
|
|
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::usage_inference(
|
|
|
|
|
InputSocketUsageInferenceFn fn)
|
|
|
|
|
{
|
|
|
|
|
decl_base_->usage_inference_fn = std::make_unique<InputSocketUsageInferenceFn>(std::move(fn));
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const bNodeSocket &find_single_menu_input(const bNode &node)
|
|
|
|
|
{
|
|
|
|
|
#ifndef NDEBUG
|
|
|
|
|
int menu_input_count = 0;
|
|
|
|
|
/* Topology cache may not be available here and this function may be called while doing tree
|
|
|
|
|
* modifications. */
|
|
|
|
|
LISTBASE_FOREACH (bNodeSocket *, socket, &node.inputs) {
|
|
|
|
|
if (socket->type == SOCK_MENU) {
|
|
|
|
|
menu_input_count++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
BLI_assert(menu_input_count == 1);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
LISTBASE_FOREACH (bNodeSocket *, socket, &node.inputs) {
|
|
|
|
|
if (!socket->is_available()) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (socket->type != SOCK_MENU) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
return *socket;
|
|
|
|
|
}
|
|
|
|
|
BLI_assert_unreachable();
|
|
|
|
|
return node.input_socket(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::usage_by_single_menu(
|
|
|
|
|
const int menu_value)
|
|
|
|
|
{
|
|
|
|
|
this->make_available([menu_value](bNode &node) {
|
|
|
|
|
bNodeSocket &socket = const_cast<bNodeSocket &>(find_single_menu_input(node));
|
|
|
|
|
bNodeSocketValueMenu *value = socket.default_value_typed<bNodeSocketValueMenu>();
|
|
|
|
|
value->value = menu_value;
|
|
|
|
|
});
|
|
|
|
|
this->usage_inference([menu_value](const socket_usage_inference::InputSocketUsageParams ¶ms)
|
|
|
|
|
-> std::optional<bool> {
|
|
|
|
|
const bNodeSocket &socket = find_single_menu_input(params.node);
|
|
|
|
|
return params.menu_input_may_be(socket.identifier, menu_value);
|
|
|
|
|
});
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-19 13:32:01 +01:00
|
|
|
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::align_with_previous(const bool value)
|
|
|
|
|
{
|
2024-03-18 14:36:03 +01:00
|
|
|
decl_base_->align_with_previous_socket = value;
|
2024-02-19 13:32:01 +01:00
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-28 02:33:47 +02:00
|
|
|
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::structure_type(
|
|
|
|
|
const StructureType structure_type)
|
|
|
|
|
{
|
2025-06-10 12:25:51 +02:00
|
|
|
BLI_assert(NodeSocketInterfaceStructureType(structure_type) !=
|
|
|
|
|
NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_AUTO);
|
2025-05-28 02:33:47 +02:00
|
|
|
decl_base_->structure_type = structure_type;
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-30 10:41:05 +02:00
|
|
|
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder ::socket_name_ptr(
|
|
|
|
|
const PointerRNA ptr, const StringRef property_name)
|
|
|
|
|
{
|
|
|
|
|
decl_base_->socket_name_rna = std::make_unique<SocketNameRNA>();
|
|
|
|
|
decl_base_->socket_name_rna->owner = ptr;
|
|
|
|
|
decl_base_->socket_name_rna->property_name = property_name;
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::socket_name_ptr(
|
|
|
|
|
const ID *id, const StructRNA *srna, const void *data, StringRef property_name)
|
|
|
|
|
{
|
|
|
|
|
/* Doing const-casts here because this data is generally only available as const when creating
|
|
|
|
|
* the declaration, but it's still valid to modify later. */
|
2025-01-24 16:45:32 +01:00
|
|
|
return this->socket_name_ptr(RNA_pointer_create_discrete(const_cast<ID *>(id),
|
|
|
|
|
const_cast<StructRNA *>(srna),
|
|
|
|
|
const_cast<void *>(data)),
|
2024-05-30 10:41:05 +02:00
|
|
|
property_name);
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-28 19:07:02 +01:00
|
|
|
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::panel_toggle(const bool value)
|
|
|
|
|
{
|
|
|
|
|
decl_base_->is_panel_toggle = value;
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-18 12:35:49 +02:00
|
|
|
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::is_layer_name(const bool value)
|
|
|
|
|
{
|
|
|
|
|
decl_base_->is_layer_name = value;
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-15 12:43:02 +02:00
|
|
|
OutputFieldDependency OutputFieldDependency::ForFieldSource()
|
|
|
|
|
{
|
|
|
|
|
OutputFieldDependency field_dependency;
|
|
|
|
|
field_dependency.type_ = OutputSocketFieldType::FieldSource;
|
|
|
|
|
return field_dependency;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
OutputFieldDependency OutputFieldDependency::ForDataSource()
|
|
|
|
|
{
|
|
|
|
|
OutputFieldDependency field_dependency;
|
|
|
|
|
field_dependency.type_ = OutputSocketFieldType::None;
|
|
|
|
|
return field_dependency;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
OutputFieldDependency OutputFieldDependency::ForDependentField()
|
|
|
|
|
{
|
|
|
|
|
OutputFieldDependency field_dependency;
|
|
|
|
|
field_dependency.type_ = OutputSocketFieldType::DependentField;
|
|
|
|
|
return field_dependency;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
OutputFieldDependency OutputFieldDependency::ForPartiallyDependentField(Vector<int> indices)
|
|
|
|
|
{
|
|
|
|
|
OutputFieldDependency field_dependency;
|
|
|
|
|
if (indices.is_empty()) {
|
|
|
|
|
field_dependency.type_ = OutputSocketFieldType::None;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
field_dependency.type_ = OutputSocketFieldType::PartiallyDependent;
|
|
|
|
|
field_dependency.linked_input_indices_ = std::move(indices);
|
|
|
|
|
}
|
|
|
|
|
return field_dependency;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
OutputSocketFieldType OutputFieldDependency::field_type() const
|
|
|
|
|
{
|
|
|
|
|
return type_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Span<int> OutputFieldDependency::linked_input_indices() const
|
|
|
|
|
{
|
|
|
|
|
return linked_input_indices_;
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-31 10:14:54 +01:00
|
|
|
const CompositorInputRealizationMode &SocketDeclaration::compositor_realization_mode() const
|
2023-10-15 12:43:02 +02:00
|
|
|
{
|
2025-01-31 10:14:54 +01:00
|
|
|
return compositor_realization_mode_;
|
2023-10-15 12:43:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int SocketDeclaration::compositor_domain_priority() const
|
|
|
|
|
{
|
|
|
|
|
return compositor_domain_priority_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SocketDeclaration::make_available(bNode &node) const
|
|
|
|
|
{
|
|
|
|
|
if (make_available_fn_) {
|
|
|
|
|
make_available_fn_(node);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PanelDeclarationBuilder &PanelDeclarationBuilder::description(std::string value)
|
|
|
|
|
{
|
|
|
|
|
decl_->description = std::move(value);
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-21 13:54:11 +02:00
|
|
|
PanelDeclarationBuilder &PanelDeclarationBuilder::translation_context(
|
|
|
|
|
std::optional<std::string> value)
|
|
|
|
|
{
|
|
|
|
|
decl_->translation_context = value;
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-15 12:43:02 +02:00
|
|
|
PanelDeclarationBuilder &PanelDeclarationBuilder::default_closed(bool closed)
|
|
|
|
|
{
|
|
|
|
|
decl_->default_collapsed = closed;
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-25 19:16:53 +02:00
|
|
|
namespace implicit_field_inputs {
|
|
|
|
|
|
2025-05-20 17:54:49 +02:00
|
|
|
static void position(const bNode & /*node*/, void *r_value)
|
2022-09-25 19:16:53 +02:00
|
|
|
{
|
2025-06-23 10:08:43 +02:00
|
|
|
bke::SocketValueVariant::ConstructIn(r_value,
|
2025-07-17 09:09:16 +02:00
|
|
|
bke::AttributeFieldInput::from<float3>("position"));
|
2022-09-25 19:16:53 +02:00
|
|
|
}
|
|
|
|
|
|
2025-05-20 17:54:49 +02:00
|
|
|
static void normal(const bNode & /*node*/, void *r_value)
|
2022-09-25 19:16:53 +02:00
|
|
|
{
|
2025-06-23 10:08:43 +02:00
|
|
|
bke::SocketValueVariant::ConstructIn(
|
|
|
|
|
r_value, fn::Field<float3>(std::make_shared<bke::NormalFieldInput>()));
|
2022-09-25 19:16:53 +02:00
|
|
|
}
|
|
|
|
|
|
2025-05-20 17:54:49 +02:00
|
|
|
static void index(const bNode & /*node*/, void *r_value)
|
2022-09-25 19:16:53 +02:00
|
|
|
{
|
2025-06-23 10:08:43 +02:00
|
|
|
bke::SocketValueVariant::ConstructIn(r_value,
|
|
|
|
|
fn::Field<int>(std::make_shared<fn::IndexFieldInput>()));
|
2022-09-25 19:16:53 +02:00
|
|
|
}
|
|
|
|
|
|
2025-05-20 17:54:49 +02:00
|
|
|
static void id_or_index(const bNode & /*node*/, void *r_value)
|
2022-09-25 19:16:53 +02:00
|
|
|
{
|
2025-06-23 10:08:43 +02:00
|
|
|
bke::SocketValueVariant::ConstructIn(
|
|
|
|
|
r_value, fn::Field<int>(std::make_shared<bke::IDAttributeFieldInput>()));
|
2022-09-25 19:16:53 +02:00
|
|
|
}
|
|
|
|
|
|
2025-05-20 17:54:49 +02:00
|
|
|
static void instance_transform(const bNode & /*node*/, void *r_value)
|
2024-05-06 17:22:24 +02:00
|
|
|
{
|
2025-06-23 10:08:43 +02:00
|
|
|
bke::SocketValueVariant::ConstructIn(
|
2025-07-17 09:09:16 +02:00
|
|
|
r_value, bke::AttributeFieldInput::from<float4x4>("instance_transform"));
|
2024-05-06 17:22:24 +02:00
|
|
|
}
|
|
|
|
|
|
2025-05-20 17:54:49 +02:00
|
|
|
static void handle_left(const bNode & /*node*/, void *r_value)
|
|
|
|
|
{
|
2025-06-23 10:08:43 +02:00
|
|
|
bke::SocketValueVariant::ConstructIn(r_value,
|
2025-07-17 09:09:16 +02:00
|
|
|
bke::AttributeFieldInput::from<float3>("handle_left"));
|
2025-05-20 17:54:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void handle_right(const bNode & /*node*/, void *r_value)
|
|
|
|
|
{
|
2025-06-23 10:08:43 +02:00
|
|
|
bke::SocketValueVariant::ConstructIn(r_value,
|
2025-07-17 09:09:16 +02:00
|
|
|
bke::AttributeFieldInput::from<float3>("handle_right"));
|
2025-05-20 17:54:49 +02:00
|
|
|
}
|
|
|
|
|
|
2022-09-25 19:16:53 +02:00
|
|
|
} // namespace implicit_field_inputs
|
|
|
|
|
|
2025-05-20 17:54:49 +02:00
|
|
|
std::optional<ImplicitInputValueFn> get_implicit_input_value_fn(const NodeDefaultInputType type)
|
|
|
|
|
{
|
|
|
|
|
switch (type) {
|
|
|
|
|
case NODE_DEFAULT_INPUT_VALUE:
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
case NODE_DEFAULT_INPUT_INDEX_FIELD:
|
|
|
|
|
return std::make_optional(implicit_field_inputs::index);
|
|
|
|
|
case NODE_DEFAULT_INPUT_ID_INDEX_FIELD:
|
|
|
|
|
return std::make_optional(implicit_field_inputs::id_or_index);
|
|
|
|
|
case NODE_DEFAULT_INPUT_NORMAL_FIELD:
|
|
|
|
|
return std::make_optional(implicit_field_inputs::normal);
|
|
|
|
|
case NODE_DEFAULT_INPUT_POSITION_FIELD:
|
|
|
|
|
return std::make_optional(implicit_field_inputs::position);
|
|
|
|
|
case NODE_DEFAULT_INPUT_INSTANCE_TRANSFORM_FIELD:
|
|
|
|
|
return std::make_optional(implicit_field_inputs::instance_transform);
|
|
|
|
|
case NODE_DEFAULT_INPUT_HANDLE_LEFT_FIELD:
|
|
|
|
|
return std::make_optional(implicit_field_inputs::handle_left);
|
|
|
|
|
case NODE_DEFAULT_INPUT_HANDLE_RIGHT_FIELD:
|
|
|
|
|
return std::make_optional(implicit_field_inputs::handle_right);
|
|
|
|
|
}
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool socket_type_supports_default_input_type(const bke::bNodeSocketType &socket_type,
|
|
|
|
|
const NodeDefaultInputType input_type)
|
|
|
|
|
{
|
2025-06-02 13:50:07 +02:00
|
|
|
const eNodeSocketDatatype stype = socket_type.type;
|
2025-05-20 17:54:49 +02:00
|
|
|
switch (input_type) {
|
|
|
|
|
case NODE_DEFAULT_INPUT_VALUE:
|
|
|
|
|
return true;
|
|
|
|
|
case NODE_DEFAULT_INPUT_ID_INDEX_FIELD:
|
|
|
|
|
case NODE_DEFAULT_INPUT_INDEX_FIELD:
|
|
|
|
|
return stype == SOCK_INT;
|
|
|
|
|
case NODE_DEFAULT_INPUT_NORMAL_FIELD:
|
|
|
|
|
case NODE_DEFAULT_INPUT_POSITION_FIELD:
|
|
|
|
|
case NODE_DEFAULT_INPUT_HANDLE_LEFT_FIELD:
|
|
|
|
|
case NODE_DEFAULT_INPUT_HANDLE_RIGHT_FIELD:
|
|
|
|
|
return stype == SOCK_VECTOR;
|
|
|
|
|
case NODE_DEFAULT_INPUT_INSTANCE_TRANSFORM_FIELD:
|
|
|
|
|
return stype == SOCK_MATRIX;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-30 17:13:46 +02:00
|
|
|
} // namespace blender::nodes
|