Files
test/source/blender/nodes/intern/node_declaration.cc

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

970 lines
30 KiB
C++
Raw Normal View History

/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "NOD_node_declaration.hh"
#include "NOD_socket_declarations.hh"
#include "NOD_socket_declarations_geometry.hh"
#include "BLI_assert.h"
Nodes: Panels integration with blend files and UI Part 3/3 of #109135, #110272 Switch to new node group interfaces and deprecate old DNA and API. This completes support for panels in node drawing and in node group interface declarations in particular. The new node group interface DNA and RNA code has been added in parts 1 and 2 (#110885, #110952) but has not be enabled yet. This commit completes the integration by * enabling the new RNA API * using the new API in UI * read/write new interfaces from blend files * add versioning for backward compatibility * add forward-compatible writing code to reconstruct old interfaces All places accessing node group interface declarations should now be using the new API. A runtime cache has been added that allows simple linear access to socket inputs and outputs even when a panel hierarchy is used. Old DNA has been deprecated and should only be accessed for versioning (inputs/outputs renamed to inputs_legacy/outputs_legacy to catch errors). Versioning code ensures both backward and forward compatibility of existing files. The API for old interfaces is removed. The new API is very similar but is defined on the `ntree.interface` instead of the `ntree` directly. Breaking change notifications and detailed instructions for migrating will be added. A python test has been added for the node group API functions. This includes new functionality such as creating panels and moving items between different levels. This patch does not yet contain panel representations in the modifier UI. This has been tested in a separate branch and will be added with a later PR (#108565). Pull Request: https://projects.blender.org/blender/blender/pulls/111348
2023-08-30 12:37:21 +02:00
#include "BLI_utildefines.h"
#include "BKE_geometry_fields.hh"
#include "BKE_node.hh"
Nodes: Panels integration with blend files and UI Part 3/3 of #109135, #110272 Switch to new node group interfaces and deprecate old DNA and API. This completes support for panels in node drawing and in node group interface declarations in particular. The new node group interface DNA and RNA code has been added in parts 1 and 2 (#110885, #110952) but has not be enabled yet. This commit completes the integration by * enabling the new RNA API * using the new API in UI * read/write new interfaces from blend files * add versioning for backward compatibility * add forward-compatible writing code to reconstruct old interfaces All places accessing node group interface declarations should now be using the new API. A runtime cache has been added that allows simple linear access to socket inputs and outputs even when a panel hierarchy is used. Old DNA has been deprecated and should only be accessed for versioning (inputs/outputs renamed to inputs_legacy/outputs_legacy to catch errors). Versioning code ensures both backward and forward compatibility of existing files. The API for old interfaces is removed. The new API is very similar but is defined on the `ntree.interface` instead of the `ntree` directly. Breaking change notifications and detailed instructions for migrating will be added. A python test has been added for the node group API functions. This includes new functionality such as creating panels and moving items between different levels. This patch does not yet contain panel representations in the modifier UI. This has been tested in a separate branch and will be added with a later PR (#108565). Pull Request: https://projects.blender.org/blender/blender/pulls/111348
2023-08-30 12:37:21 +02:00
#include "BKE_node_runtime.hh"
#include "BKE_node_socket_value.hh"
#include "RNA_access.hh"
namespace blender::nodes {
static void reset_declaration(NodeDeclaration &declaration)
{
std::destroy_at(&declaration);
new (&declaration) NodeDeclaration();
}
void build_node_declaration(const bke::bNodeType &typeinfo,
Nodes: unify static and dynamic declarations This helps solving the problem encountered in #113553. The problem is that we currently can't support link-drag-search for nodes which have a dynamic declaration. With this patch, there is only a single `declare` function per node type, instead of the separate `declare` and `declare_dynamic` functions. The new `declare` function has access to the node and tree. However, both are allowed to be null. The final node declaration has a flag for whether it depends on the node context or not. Nodes that previously had a dynamic declaration should now create as much of the declaration as possible that does not depend on the node. This allows code like for link-drag-search to take those sockets into account even if the other sockets are dynamic. For node declarations that have dynamic types (e.g. Switch node), we can also add extra information to the static node declaration, like the identifier of the socket with the dynamic type. This is not part of this patch though. I can think of two main alternatives to the approach implemented here: * Define two separate functions for dynamic nodes. One that creates the "static declaration" without node context, and on that creates the actual declaration with node context. * Have a single declare function that generates "build instructions" for the actual node declaration. So instead of building the final declaration directly, one can for example add a socket whose type depends on a specific rna path in the node. The actual node declaration is then automatically generated based on the build instructions. This becomes quite a bit more tricky with dynamic amounts of sockets and introduces another indirection between declarations and what sockets the node actually has. I found the approach implemented in this patch to lead to the least amount of boilerplate (doesn't require a seperate "build instructions" data structure) and code duplication (socket properties are still only defined in one place). At the same time, it offers more flexibility to how nodes can be dynamic. Pull Request: https://projects.blender.org/blender/blender/pulls/113742
2023-10-15 20:28:23 +02:00
NodeDeclaration &r_declaration,
const bNodeTree *ntree,
const bNode *node)
{
reset_declaration(r_declaration);
NodeDeclarationBuilder node_decl_builder{typeinfo, r_declaration, ntree, node};
typeinfo.declare(node_decl_builder);
node_decl_builder.finalize();
}
void NodeDeclarationBuilder::build_remaining_anonymous_attribute_relations()
{
Refactor: Geometry Nodes: improve lifetime analysis for anonymous attributes This refactors the lifetime analysis of anonymous attributes in geometry nodes. The refactor has a couple of goals: * Use a better and simpler abstraction that can be used when building the lazy-function graph. We currently have a bunch of duplicate code to handle "field source" and "caller propagation" attributes. This is now unified so that one only has to worry about one kind of "reference sets". * Make the abstraction compatible with handling bundles and closures in case we want to support them in the future. Both types can contain geometries and fields so they need to be taken into account when determining lifetimes. * Make more parts independent of the concept of "anonymous attributes". In theory, there could be more kinds of referenced data whose lifetimes need to be managed. I don't have any concrete plans for adding any though. At its core, deterministic anonymous attributes still work the same they have been since they became deterministic [0]. Even the generated lazy-function graph is still pretty much or even exactly the same as before. The patch renames `AnonymousAttributeSet` to the more general `GeometryNodesReferenceSet` which is more. This also makes more places independent of the concept of anonymous attributes. Functionally, this still the same though. It's only used in the internals of geometry nodes nowadays. Most code just gets an `AttributeFilter` that is based on it. [0]: https://archive.blender.org/developer/D16858 Pull Request: https://projects.blender.org/blender/blender/pulls/128667
2024-10-07 12:59:39 +02:00
auto is_data_socket_decl = [](const SocketDeclaration *socket_decl) {
Geometry Nodes: add Closures and Bundles behind experimental feature flag This implements bundles and closures which are described in more detail in this blog post: https://code.blender.org/2024/11/geometry-nodes-workshop-october-2024/ tl;dr: * Bundles are containers that allow storing multiple socket values in a single value. Each value in the bundle is identified by a name. Bundles can be nested. * Closures are functions that are created with the Closure Zone and can be evaluated with the Evaluate Closure node. To use the patch, the `Bundle and Closure Nodes` experimental feature has to be enabled. This is necessary, because these features are not fully done yet and still need iterations to improve the workflow before they can be officially released. These iterations are easier to do in `main` than in a separate branch though. That's because this patch is quite large and somewhat prone to merge conflicts. Also other work we want to do, depends on this. This adds the following new nodes: * Combine Bundle: can pack multiple values into one. * Separate Bundle: extracts values from a bundle. * Closure Zone: outputs a closure zone for use in the `Evaluate Closure` node. * Evaluate Closure: evaluates the passed in closure. Things that will be added soon after this lands: * Fields in bundles and closures. The way this is done changes with #134811, so I rather implement this once both are in `main`. * UI features for keeping sockets in sync (right now there are warnings only). One bigger issue is the limited support for lazyness. For example, all inputs of a Combine Bundle node will be evaluated, even if they are not all needed. The same is true for all captured values of a closure. This is a deeper limitation that needs to be resolved at some point. This will likely be done after an initial version of this patch is done. Pull Request: https://projects.blender.org/blender/blender/pulls/128340
2025-04-03 15:44:06 +02:00
return ELEM(socket_decl->socket_type, SOCK_GEOMETRY, SOCK_BUNDLE, SOCK_CLOSURE);
Refactor: Geometry Nodes: improve lifetime analysis for anonymous attributes This refactors the lifetime analysis of anonymous attributes in geometry nodes. The refactor has a couple of goals: * Use a better and simpler abstraction that can be used when building the lazy-function graph. We currently have a bunch of duplicate code to handle "field source" and "caller propagation" attributes. This is now unified so that one only has to worry about one kind of "reference sets". * Make the abstraction compatible with handling bundles and closures in case we want to support them in the future. Both types can contain geometries and fields so they need to be taken into account when determining lifetimes. * Make more parts independent of the concept of "anonymous attributes". In theory, there could be more kinds of referenced data whose lifetimes need to be managed. I don't have any concrete plans for adding any though. At its core, deterministic anonymous attributes still work the same they have been since they became deterministic [0]. Even the generated lazy-function graph is still pretty much or even exactly the same as before. The patch renames `AnonymousAttributeSet` to the more general `GeometryNodesReferenceSet` which is more. This also makes more places independent of the concept of anonymous attributes. Functionally, this still the same though. It's only used in the internals of geometry nodes nowadays. Most code just gets an `AttributeFilter` that is based on it. [0]: https://archive.blender.org/developer/D16858 Pull Request: https://projects.blender.org/blender/blender/pulls/128667
2024-10-07 12:59:39 +02:00
};
Vector<int> geometry_inputs;
for (const int i : declaration_.inputs.index_range()) {
Refactor: Geometry Nodes: improve lifetime analysis for anonymous attributes This refactors the lifetime analysis of anonymous attributes in geometry nodes. The refactor has a couple of goals: * Use a better and simpler abstraction that can be used when building the lazy-function graph. We currently have a bunch of duplicate code to handle "field source" and "caller propagation" attributes. This is now unified so that one only has to worry about one kind of "reference sets". * Make the abstraction compatible with handling bundles and closures in case we want to support them in the future. Both types can contain geometries and fields so they need to be taken into account when determining lifetimes. * Make more parts independent of the concept of "anonymous attributes". In theory, there could be more kinds of referenced data whose lifetimes need to be managed. I don't have any concrete plans for adding any though. At its core, deterministic anonymous attributes still work the same they have been since they became deterministic [0]. Even the generated lazy-function graph is still pretty much or even exactly the same as before. The patch renames `AnonymousAttributeSet` to the more general `GeometryNodesReferenceSet` which is more. This also makes more places independent of the concept of anonymous attributes. Functionally, this still the same though. It's only used in the internals of geometry nodes nowadays. Most code just gets an `AttributeFilter` that is based on it. [0]: https://archive.blender.org/developer/D16858 Pull Request: https://projects.blender.org/blender/blender/pulls/128667
2024-10-07 12:59:39 +02:00
if (is_data_socket_decl(declaration_.inputs[i])) {
geometry_inputs.append(i);
}
}
Vector<int> geometry_outputs;
for (const int i : declaration_.outputs.index_range()) {
Refactor: Geometry Nodes: improve lifetime analysis for anonymous attributes This refactors the lifetime analysis of anonymous attributes in geometry nodes. The refactor has a couple of goals: * Use a better and simpler abstraction that can be used when building the lazy-function graph. We currently have a bunch of duplicate code to handle "field source" and "caller propagation" attributes. This is now unified so that one only has to worry about one kind of "reference sets". * Make the abstraction compatible with handling bundles and closures in case we want to support them in the future. Both types can contain geometries and fields so they need to be taken into account when determining lifetimes. * Make more parts independent of the concept of "anonymous attributes". In theory, there could be more kinds of referenced data whose lifetimes need to be managed. I don't have any concrete plans for adding any though. At its core, deterministic anonymous attributes still work the same they have been since they became deterministic [0]. Even the generated lazy-function graph is still pretty much or even exactly the same as before. The patch renames `AnonymousAttributeSet` to the more general `GeometryNodesReferenceSet` which is more. This also makes more places independent of the concept of anonymous attributes. Functionally, this still the same though. It's only used in the internals of geometry nodes nowadays. Most code just gets an `AttributeFilter` that is based on it. [0]: https://archive.blender.org/developer/D16858 Pull Request: https://projects.blender.org/blender/blender/pulls/128667
2024-10-07 12:59:39 +02:00
if (is_data_socket_decl(declaration_.outputs[i])) {
geometry_outputs.append(i);
}
}
for (BaseSocketDeclarationBuilder *socket_builder : input_socket_builders_) {
if (socket_builder->field_on_all_) {
aal::RelationsInNode &relations = this->get_anonymous_attribute_relations();
const int field_input = socket_builder->decl_base_->index;
for (const int geometry_input : geometry_inputs) {
relations.eval_relations.append({field_input, geometry_input});
}
}
}
for (BaseSocketDeclarationBuilder *socket_builder : output_socket_builders_) {
if (socket_builder->field_on_all_) {
aal::RelationsInNode &relations = this->get_anonymous_attribute_relations();
const int field_output = socket_builder->decl_base_->index;
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();
const int field_output = socket_builder->decl_base_->index;
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();
const int geometry_output = socket_builder->decl_base_->index;
for (const int geometry_input : geometry_inputs) {
relations.propagate_relations.append({geometry_input, geometry_output});
}
}
}
}
void NodeDeclarationBuilder::finalize()
{
this->build_remaining_anonymous_attribute_relations();
#ifndef NDEBUG
declaration_.assert_valid();
#endif
}
NodeDeclarationBuilder::NodeDeclarationBuilder(const bke::bNodeType &typeinfo,
NodeDeclaration &declaration,
Nodes: unify static and dynamic declarations This helps solving the problem encountered in #113553. The problem is that we currently can't support link-drag-search for nodes which have a dynamic declaration. With this patch, there is only a single `declare` function per node type, instead of the separate `declare` and `declare_dynamic` functions. The new `declare` function has access to the node and tree. However, both are allowed to be null. The final node declaration has a flag for whether it depends on the node context or not. Nodes that previously had a dynamic declaration should now create as much of the declaration as possible that does not depend on the node. This allows code like for link-drag-search to take those sockets into account even if the other sockets are dynamic. For node declarations that have dynamic types (e.g. Switch node), we can also add extra information to the static node declaration, like the identifier of the socket with the dynamic type. This is not part of this patch though. I can think of two main alternatives to the approach implemented here: * Define two separate functions for dynamic nodes. One that creates the "static declaration" without node context, and on that creates the actual declaration with node context. * Have a single declare function that generates "build instructions" for the actual node declaration. So instead of building the final declaration directly, one can for example add a socket whose type depends on a specific rna path in the node. The actual node declaration is then automatically generated based on the build instructions. This becomes quite a bit more tricky with dynamic amounts of sockets and introduces another indirection between declarations and what sockets the node actually has. I found the approach implemented in this patch to lead to the least amount of boilerplate (doesn't require a seperate "build instructions" data structure) and code duplication (socket properties are still only defined in one place). At the same time, it offers more flexibility to how nodes can be dynamic. Pull Request: https://projects.blender.org/blender/blender/pulls/113742
2023-10-15 20:28:23 +02:00
const bNodeTree *ntree,
const bNode *node)
: DeclarationListBuilder(*this, declaration.root_items),
typeinfo_(typeinfo),
declaration_(declaration),
ntree_(ntree),
node_(node)
{
/* Unused in release builds, but used for BLI_assert() in debug builds. */
UNUSED_VARS(typeinfo_);
}
void NodeDeclarationBuilder::use_custom_socket_order(bool enable)
{
declaration_.use_custom_socket_order = enable;
}
void NodeDeclarationBuilder::allow_any_socket_order(bool enable)
{
BLI_assert(declaration_.use_custom_socket_order);
declaration_.allow_any_socket_order = enable;
}
Span<SocketDeclaration *> NodeDeclaration::sockets(eNodeSocketInOut in_out) const
{
if (in_out == SOCK_IN) {
return inputs;
}
return outputs;
}
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
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);
}
else {
BLI_assert(node_decl.allow_any_socket_order || (!found_input && !found_panel));
r_flat_outputs.append(socket_decl);
Nodes: Add PanelDeclarationBuilder for panels in builtin nodes Node groups already have panels, but they modify the node declaration directly, which is not something we want to do for builtin nodes. For those the `PanelDeclarationBuilder` should be used. `PanelDeclarationBuilder` has `add_input`/`add_output` methods just like `NodeDeclarationBuilder`. Adding sockets to a panel increases its size by one. All sockets must be added in order: Adding sockets or panels to the root `NodeDeclarationBuilder` after a panel will complete the panel and adding more sockets to it after that will fail. This is to enforce a stable item order where indices don't change after adding a socket, which is important for things like field dependencies. Example: ```cpp static void node_declare(NodeDeclarationBuilder &b) { // Currently this is necessary to enable custom layouts and panels. // Will go away eventually when most nodes uses custom layout. b.use_custom_socket_order(); // Create a panel. PanelDeclarationBuilder &pb = b.add_panel("My Panel").description("A demo panel").default_closed(true); // Add to the panel instead of the root layout. pb.add_input<decl::Color>("Color").default_value({0.8f, 0.8f, 0.8f, 1.0f}); pb.add_input<decl::Float>("Weight").unavailable(); // Continue socket declarations as usual. b.add_output<decl::Shader>("BSDF"); // !!! Warning: continuing the panel after other items is not allowed and will show an error. pb.add_output<decl::Float>("Bad Socket"); } ``` Pull Request: https://projects.blender.org/blender/blender/pulls/111695
2023-09-11 13:39:28 +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);
Nodes: Add PanelDeclarationBuilder for panels in builtin nodes Node groups already have panels, but they modify the node declaration directly, which is not something we want to do for builtin nodes. For those the `PanelDeclarationBuilder` should be used. `PanelDeclarationBuilder` has `add_input`/`add_output` methods just like `NodeDeclarationBuilder`. Adding sockets to a panel increases its size by one. All sockets must be added in order: Adding sockets or panels to the root `NodeDeclarationBuilder` after a panel will complete the panel and adding more sockets to it after that will fail. This is to enforce a stable item order where indices don't change after adding a socket, which is important for things like field dependencies. Example: ```cpp static void node_declare(NodeDeclarationBuilder &b) { // Currently this is necessary to enable custom layouts and panels. // Will go away eventually when most nodes uses custom layout. b.use_custom_socket_order(); // Create a panel. PanelDeclarationBuilder &pb = b.add_panel("My Panel").description("A demo panel").default_closed(true); // Add to the panel instead of the root layout. pb.add_input<decl::Color>("Color").default_value({0.8f, 0.8f, 0.8f, 1.0f}); pb.add_input<decl::Float>("Weight").unavailable(); // Continue socket declarations as usual. b.add_output<decl::Shader>("BSDF"); // !!! Warning: continuing the panel after other items is not allowed and will show an error. pb.add_output<decl::Float>("Bad Socket"); } ``` Pull Request: https://projects.blender.org/blender/blender/pulls/111695
2023-09-11 13:39:28 +02:00
}
}
2024-10-10 18:36:08 +11:00
UNUSED_VARS(found_input, found_panel);
}
Nodes: Add PanelDeclarationBuilder for panels in builtin nodes Node groups already have panels, but they modify the node declaration directly, which is not something we want to do for builtin nodes. For those the `PanelDeclarationBuilder` should be used. `PanelDeclarationBuilder` has `add_input`/`add_output` methods just like `NodeDeclarationBuilder`. Adding sockets to a panel increases its size by one. All sockets must be added in order: Adding sockets or panels to the root `NodeDeclarationBuilder` after a panel will complete the panel and adding more sockets to it after that will fail. This is to enforce a stable item order where indices don't change after adding a socket, which is important for things like field dependencies. Example: ```cpp static void node_declare(NodeDeclarationBuilder &b) { // Currently this is necessary to enable custom layouts and panels. // Will go away eventually when most nodes uses custom layout. b.use_custom_socket_order(); // Create a panel. PanelDeclarationBuilder &pb = b.add_panel("My Panel").description("A demo panel").default_closed(true); // Add to the panel instead of the root layout. pb.add_input<decl::Color>("Color").default_value({0.8f, 0.8f, 0.8f, 1.0f}); pb.add_input<decl::Float>("Weight").unavailable(); // Continue socket declarations as usual. b.add_output<decl::Shader>("BSDF"); // !!! Warning: continuing the panel after other items is not allowed and will show an error. pb.add_output<decl::Float>("Bad Socket"); } ``` Pull Request: https://projects.blender.org/blender/blender/pulls/111695
2023-09-11 13:39:28 +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;
Nodes: Add PanelDeclarationBuilder for panels in builtin nodes Node groups already have panels, but they modify the node declaration directly, which is not something we want to do for builtin nodes. For those the `PanelDeclarationBuilder` should be used. `PanelDeclarationBuilder` has `add_input`/`add_output` methods just like `NodeDeclarationBuilder`. Adding sockets to a panel increases its size by one. All sockets must be added in order: Adding sockets or panels to the root `NodeDeclarationBuilder` after a panel will complete the panel and adding more sockets to it after that will fail. This is to enforce a stable item order where indices don't change after adding a socket, which is important for things like field dependencies. Example: ```cpp static void node_declare(NodeDeclarationBuilder &b) { // Currently this is necessary to enable custom layouts and panels. // Will go away eventually when most nodes uses custom layout. b.use_custom_socket_order(); // Create a panel. PanelDeclarationBuilder &pb = b.add_panel("My Panel").description("A demo panel").default_closed(true); // Add to the panel instead of the root layout. pb.add_input<decl::Color>("Color").default_value({0.8f, 0.8f, 0.8f, 1.0f}); pb.add_input<decl::Float>("Weight").unavailable(); // Continue socket declarations as usual. b.add_output<decl::Shader>("BSDF"); // !!! Warning: continuing the panel after other items is not allowed and will show an error. pb.add_output<decl::Float>("Bad Socket"); } ``` Pull Request: https://projects.blender.org/blender/blender/pulls/111695
2023-09-11 13:39:28 +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);
Nodes: Add PanelDeclarationBuilder for panels in builtin nodes Node groups already have panels, but they modify the node declaration directly, which is not something we want to do for builtin nodes. For those the `PanelDeclarationBuilder` should be used. `PanelDeclarationBuilder` has `add_input`/`add_output` methods just like `NodeDeclarationBuilder`. Adding sockets to a panel increases its size by one. All sockets must be added in order: Adding sockets or panels to the root `NodeDeclarationBuilder` after a panel will complete the panel and adding more sockets to it after that will fail. This is to enforce a stable item order where indices don't change after adding a socket, which is important for things like field dependencies. Example: ```cpp static void node_declare(NodeDeclarationBuilder &b) { // Currently this is necessary to enable custom layouts and panels. // Will go away eventually when most nodes uses custom layout. b.use_custom_socket_order(); // Create a panel. PanelDeclarationBuilder &pb = b.add_panel("My Panel").description("A demo panel").default_closed(true); // Add to the panel instead of the root layout. pb.add_input<decl::Color>("Color").default_value({0.8f, 0.8f, 0.8f, 1.0f}); pb.add_input<decl::Float>("Weight").unavailable(); // Continue socket declarations as usual. b.add_output<decl::Shader>("BSDF"); // !!! Warning: continuing the panel after other items is not allowed and will show an error. pb.add_output<decl::Float>("Bad Socket"); } ``` Pull Request: https://projects.blender.org/blender/blender/pulls/111695
2023-09-11 13:39:28 +02:00
}
bool NodeDeclaration::matches(const bNode &node) const
{
Nodes: Panels integration with blend files and UI Part 3/3 of #109135, #110272 Switch to new node group interfaces and deprecate old DNA and API. This completes support for panels in node drawing and in node group interface declarations in particular. The new node group interface DNA and RNA code has been added in parts 1 and 2 (#110885, #110952) but has not be enabled yet. This commit completes the integration by * enabling the new RNA API * using the new API in UI * read/write new interfaces from blend files * add versioning for backward compatibility * add forward-compatible writing code to reconstruct old interfaces All places accessing node group interface declarations should now be using the new API. A runtime cache has been added that allows simple linear access to socket inputs and outputs even when a panel hierarchy is used. Old DNA has been deprecated and should only be accessed for versioning (inputs/outputs renamed to inputs_legacy/outputs_legacy to catch errors). Versioning code ensures both backward and forward compatibility of existing files. The API for old interfaces is removed. The new API is very similar but is defined on the `ntree.interface` instead of the `ntree` directly. Breaking change notifications and detailed instructions for migrating will be added. A python test has been added for the node group API functions. This includes new functionality such as creating panels and moving items between different levels. This patch does not yet contain panel representations in the modifier UI. This has been tested in a separate branch and will be added with a later PR (#108565). Pull Request: https://projects.blender.org/blender/blender/pulls/111348
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;
for (const ItemDeclarationPtr &item_decl : this->all_items) {
Nodes: Panels integration with blend files and UI Part 3/3 of #109135, #110272 Switch to new node group interfaces and deprecate old DNA and API. This completes support for panels in node drawing and in node group interface declarations in particular. The new node group interface DNA and RNA code has been added in parts 1 and 2 (#110885, #110952) but has not be enabled yet. This commit completes the integration by * enabling the new RNA API * using the new API in UI * read/write new interfaces from blend files * add versioning for backward compatibility * add forward-compatible writing code to reconstruct old interfaces All places accessing node group interface declarations should now be using the new API. A runtime cache has been added that allows simple linear access to socket inputs and outputs even when a panel hierarchy is used. Old DNA has been deprecated and should only be accessed for versioning (inputs/outputs renamed to inputs_legacy/outputs_legacy to catch errors). Versioning code ensures both backward and forward compatibility of existing files. The API for old interfaces is removed. The new API is very similar but is defined on the `ntree.interface` instead of the `ntree` directly. Breaking change notifications and detailed instructions for migrating will be added. A python test has been added for the node group API functions. This includes new functionality such as creating panels and moving items between different levels. This patch does not yet contain panel representations in the modifier UI. This has been tested in a separate branch and will be added with a later PR (#108565). Pull Request: https://projects.blender.org/blender/blender/pulls/111348
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;
}
}
Nodes: Panels integration with blend files and UI Part 3/3 of #109135, #110272 Switch to new node group interfaces and deprecate old DNA and API. This completes support for panels in node drawing and in node group interface declarations in particular. The new node group interface DNA and RNA code has been added in parts 1 and 2 (#110885, #110952) but has not be enabled yet. This commit completes the integration by * enabling the new RNA API * using the new API in UI * read/write new interfaces from blend files * add versioning for backward compatibility * add forward-compatible writing code to reconstruct old interfaces All places accessing node group interface declarations should now be using the new API. A runtime cache has been added that allows simple linear access to socket inputs and outputs even when a panel hierarchy is used. Old DNA has been deprecated and should only be accessed for versioning (inputs/outputs renamed to inputs_legacy/outputs_legacy to catch errors). Versioning code ensures both backward and forward compatibility of existing files. The API for old interfaces is removed. The new API is very similar but is defined on the `ntree.interface` instead of the `ntree` directly. Breaking change notifications and detailed instructions for migrating will be added. A python test has been added for the node group API functions. This includes new functionality such as creating panels and moving items between different levels. This patch does not yet contain panel representations in the modifier UI. This has been tested in a separate branch and will be added with a later PR (#108565). Pull Request: https://projects.blender.org/blender/blender/pulls/111348
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))
{
return false;
}
Nodes: Panels integration with blend files and UI Part 3/3 of #109135, #110272 Switch to new node group interfaces and deprecate old DNA and API. This completes support for panels in node drawing and in node group interface declarations in particular. The new node group interface DNA and RNA code has been added in parts 1 and 2 (#110885, #110952) but has not be enabled yet. This commit completes the integration by * enabling the new RNA API * using the new API in UI * read/write new interfaces from blend files * add versioning for backward compatibility * add forward-compatible writing code to reconstruct old interfaces All places accessing node group interface declarations should now be using the new API. A runtime cache has been added that allows simple linear access to socket inputs and outputs even when a panel hierarchy is used. Old DNA has been deprecated and should only be accessed for versioning (inputs/outputs renamed to inputs_legacy/outputs_legacy to catch errors). Versioning code ensures both backward and forward compatibility of existing files. The API for old interfaces is removed. The new API is very similar but is defined on the `ntree.interface` instead of the `ntree` directly. Breaking change notifications and detailed instructions for migrating will be added. A python test has been added for the node group API functions. This includes new functionality such as creating panels and moving items between different levels. This patch does not yet contain panel representations in the modifier UI. This has been tested in a separate branch and will be added with a later PR (#108565). Pull Request: https://projects.blender.org/blender/blender/pulls/111348
2023-08-30 12:37:21 +02:00
++current_panel;
}
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. */
}
Nodes: Panels integration with blend files and UI Part 3/3 of #109135, #110272 Switch to new node group interfaces and deprecate old DNA and API. This completes support for panels in node drawing and in node group interface declarations in particular. The new node group interface DNA and RNA code has been added in parts 1 and 2 (#110885, #110952) but has not be enabled yet. This commit completes the integration by * enabling the new RNA API * using the new API in UI * read/write new interfaces from blend files * add versioning for backward compatibility * add forward-compatible writing code to reconstruct old interfaces All places accessing node group interface declarations should now be using the new API. A runtime cache has been added that allows simple linear access to socket inputs and outputs even when a panel hierarchy is used. Old DNA has been deprecated and should only be accessed for versioning (inputs/outputs renamed to inputs_legacy/outputs_legacy to catch errors). Versioning code ensures both backward and forward compatibility of existing files. The API for old interfaces is removed. The new API is very similar but is defined on the `ntree.interface` instead of the `ntree` directly. Breaking change notifications and detailed instructions for migrating will be added. A python test has been added for the node group API functions. This includes new functionality such as creating panels and moving items between different levels. This patch does not yet contain panel representations in the modifier UI. This has been tested in a separate branch and will be added with a later PR (#108565). Pull Request: https://projects.blender.org/blender/blender/pulls/111348
2023-08-30 12:37:21 +02:00
else {
/* Unknown item type. */
BLI_assert_unreachable();
}
}
Nodes: Panels integration with blend files and UI Part 3/3 of #109135, #110272 Switch to new node group interfaces and deprecate old DNA and API. This completes support for panels in node drawing and in node group interface declarations in particular. The new node group interface DNA and RNA code has been added in parts 1 and 2 (#110885, #110952) but has not be enabled yet. This commit completes the integration by * enabling the new RNA API * using the new API in UI * read/write new interfaces from blend files * add versioning for backward compatibility * add forward-compatible writing code to reconstruct old interfaces All places accessing node group interface declarations should now be using the new API. A runtime cache has been added that allows simple linear access to socket inputs and outputs even when a panel hierarchy is used. Old DNA has been deprecated and should only be accessed for versioning (inputs/outputs renamed to inputs_legacy/outputs_legacy to catch errors). Versioning code ensures both backward and forward compatibility of existing files. The API for old interfaces is removed. The new API is very similar but is defined on the `ntree.interface` instead of the `ntree` directly. Breaking change notifications and detailed instructions for migrating will be added. A python test has been added for the node group API functions. This includes new functionality such as creating panels and moving items between different levels. This patch does not yet contain panel representations in the modifier UI. This has been tested in a separate branch and will be added with a later PR (#108565). Pull Request: https://projects.blender.org/blender/blender/pulls/111348
2023-08-30 12:37:21 +02:00
/* If items are left over, some were removed from the declaration. */
if (current_input != nullptr || current_output != nullptr ||
node.panel_states().contains_ptr(current_panel))
Nodes: Panels integration with blend files and UI Part 3/3 of #109135, #110272 Switch to new node group interfaces and deprecate old DNA and API. This completes support for panels in node drawing and in node group interface declarations in particular. The new node group interface DNA and RNA code has been added in parts 1 and 2 (#110885, #110952) but has not be enabled yet. This commit completes the integration by * enabling the new RNA API * using the new API in UI * read/write new interfaces from blend files * add versioning for backward compatibility * add forward-compatible writing code to reconstruct old interfaces All places accessing node group interface declarations should now be using the new API. A runtime cache has been added that allows simple linear access to socket inputs and outputs even when a panel hierarchy is used. Old DNA has been deprecated and should only be accessed for versioning (inputs/outputs renamed to inputs_legacy/outputs_legacy to catch errors). Versioning code ensures both backward and forward compatibility of existing files. The API for old interfaces is removed. The new API is very similar but is defined on the `ntree.interface` instead of the `ntree` directly. Breaking change notifications and detailed instructions for migrating will be added. A python test has been added for the node group API functions. This includes new functionality such as creating panels and moving items between different levels. This patch does not yet contain panel representations in the modifier UI. This has been tested in a separate branch and will be added with a later PR (#108565). Pull Request: https://projects.blender.org/blender/blender/pulls/111348
2023-08-30 12:37:21 +02:00
{
return false;
}
return true;
}
bNodeSocket &SocketDeclaration::update_or_build(bNodeTree &ntree,
bNode &node,
bNodeSocket &socket) const
{
/* By default just rebuild. */
BLI_assert(socket.in_out == this->in_out);
UNUSED_VARS_NDEBUG(socket);
return this->build(ntree, node);
}
void SocketDeclaration::set_common_flags(bNodeSocket &socket) const
{
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);
SET_FLAG_FROM_TEST(socket.flag, !is_available, SOCK_UNAVAIL);
}
bool SocketDeclaration::matches_common_data(const bNodeSocket &socket) const
{
if (socket.name != this->name) {
return false;
}
if (socket.identifier != this->identifier) {
return false;
}
if (((socket.flag & SOCK_COMPACT) != 0) != this->compact) {
return false;
}
if (((socket.flag & SOCK_HIDE_VALUE) != 0) != this->hide_value) {
return false;
}
if (((socket.flag & SOCK_HIDE_LABEL) != 0) != this->hide_label) {
return false;
}
if (((socket.flag & SOCK_MULTI_INPUT) != 0) != this->is_multi_input) {
return false;
}
if (((socket.flag & SOCK_NO_INTERNAL_LINK) != 0) != this->no_mute_links) {
return false;
}
if (((socket.flag & SOCK_UNAVAIL) != 0) != !this->is_available) {
return false;
}
return true;
}
template<typename Fn>
static bool socket_type_to_static_decl_type(const eNodeSocketDatatype socket_type, Fn &&fn)
{
switch (socket_type) {
case SOCK_FLOAT:
fn(TypeTag<decl::Float>());
return true;
case SOCK_VECTOR:
fn(TypeTag<decl::Vector>());
return true;
case SOCK_RGBA:
fn(TypeTag<decl::Color>());
return true;
case SOCK_BOOLEAN:
fn(TypeTag<decl::Bool>());
return true;
case SOCK_ROTATION:
fn(TypeTag<decl::Rotation>());
return true;
case SOCK_MATRIX:
fn(TypeTag<decl::Matrix>());
return true;
case SOCK_INT:
fn(TypeTag<decl::Int>());
return true;
case SOCK_STRING:
fn(TypeTag<decl::String>());
return true;
case SOCK_GEOMETRY:
fn(TypeTag<decl::Geometry>());
return true;
case SOCK_OBJECT:
fn(TypeTag<decl::Object>());
return true;
case SOCK_IMAGE:
fn(TypeTag<decl::Image>());
return true;
case SOCK_COLLECTION:
fn(TypeTag<decl::Collection>());
return true;
case SOCK_MATERIAL:
fn(TypeTag<decl::Material>());
return true;
Geometry Nodes: Menu Switch Node This patch adds support for _Menu Switch_ nodes and enum definitions in node trees more generally. The design is based on the outcome of the [2022 Nodes Workshop](https://code.blender.org/2022/11/geometry-nodes-workshop-2022/#menu-switch). The _Menu Switch_ node is an advanced version of the _Switch_ node which has a customizable **menu input socket** instead of a simple boolean. The _items_ of this menu are owned by the node itself. Each item has a name and description and unique identifier that is used internally. A menu _socket_ represents a concrete value out of the list of items. To enable selection of an enum value for unconnected sockets the menu is presented as a dropdown list like built-in enums. When the socket is connected a shared pointer to the enum definition is propagated along links and stored in socket default values. This allows node groups to expose a menu from an internal menu switch as a parameter. The enum definition is a runtime copy of the enum items in DNA that allows sharing. A menu socket can have multiple connections, which can lead to ambiguity. If two or more different menu source nodes are connected to a socket it gets marked as _undefined_. Any connection to an undefined menu socket is invalid as a hint to users that there is a problem. A warning/error is also shown on nodes with undefined menu sockets. At runtime the value of a menu socket is the simple integer identifier. This can also be a field in geometry nodes. The identifier is unique within each enum definition, and it is persistent even when items are added, removed, or changed. Changing the name of an item does not affect the internal identifier, so users can rename enum items without breaking existing input values. This also persists if, for example, a linked node group is temporarily unavailable. Pull Request: https://projects.blender.org/blender/blender/pulls/113445
2024-01-26 12:40:01 +01:00
case SOCK_MENU:
fn(TypeTag<decl::Menu>());
return true;
Geometry Nodes: add Closures and Bundles behind experimental feature flag This implements bundles and closures which are described in more detail in this blog post: https://code.blender.org/2024/11/geometry-nodes-workshop-october-2024/ tl;dr: * Bundles are containers that allow storing multiple socket values in a single value. Each value in the bundle is identified by a name. Bundles can be nested. * Closures are functions that are created with the Closure Zone and can be evaluated with the Evaluate Closure node. To use the patch, the `Bundle and Closure Nodes` experimental feature has to be enabled. This is necessary, because these features are not fully done yet and still need iterations to improve the workflow before they can be officially released. These iterations are easier to do in `main` than in a separate branch though. That's because this patch is quite large and somewhat prone to merge conflicts. Also other work we want to do, depends on this. This adds the following new nodes: * Combine Bundle: can pack multiple values into one. * Separate Bundle: extracts values from a bundle. * Closure Zone: outputs a closure zone for use in the `Evaluate Closure` node. * Evaluate Closure: evaluates the passed in closure. Things that will be added soon after this lands: * Fields in bundles and closures. The way this is done changes with #134811, so I rather implement this once both are in `main`. * UI features for keeping sockets in sync (right now there are warnings only). One bigger issue is the limited support for lazyness. For example, all inputs of a Combine Bundle node will be evaluated, even if they are not all needed. The same is true for all captured values of a closure. This is a deeper limitation that needs to be resolved at some point. This will likely be done after an initial version of this patch is done. Pull Request: https://projects.blender.org/blender/blender/pulls/128340
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;
default:
return false;
}
}
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;
}
BaseSocketDeclarationBuilder &DeclarationListBuilder::add_input(
const eNodeSocketDatatype socket_type, const StringRef name, const StringRef identifier)
{
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>("", "");
}
return *decl;
}
BaseSocketDeclarationBuilder &DeclarationListBuilder::add_input(const eCustomDataType data_type,
const StringRef name,
const StringRef identifier)
{
return this->add_input(*bke::custom_data_type_to_socket_type(data_type), name, identifier);
}
BaseSocketDeclarationBuilder &DeclarationListBuilder::add_output(
const eNodeSocketDatatype socket_type, const StringRef name, const StringRef identifier)
{
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>("", "");
}
return *decl;
}
BaseSocketDeclarationBuilder &DeclarationListBuilder::add_output(const eCustomDataType data_type,
const StringRef name,
const StringRef identifier)
{
return this->add_output(*bke::custom_data_type_to_socket_type(data_type), name, identifier);
}
void DeclarationListBuilder::add_separator()
{
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);
}
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);
}
PanelDeclarationBuilder &DeclarationListBuilder::add_panel(const StringRef name, int identifier)
{
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;
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;
panel_decl.parent_panel = this->parent_panel_decl;
panel_decl.index = this->node_decl_builder.declaration_.panels.append_and_get_index(&panel_decl);
this->node_decl_builder.declaration_.all_items.append(std::move(panel_decl_ptr));
this->node_decl_builder.panel_builders_.append_and_get_index(std::move(panel_decl_builder_ptr));
this->items.append(&panel_decl);
return panel_decl_builder;
}
void PanelDeclaration::build(bNodePanelState &panel) const
{
panel = {0};
panel.identifier = this->identifier;
SET_FLAG_FROM_TEST(panel.flag, this->default_collapsed, NODE_PANEL_COLLAPSED);
}
bool PanelDeclaration::matches(const bNodePanelState &panel) const
{
return panel.identifier == this->identifier;
}
void PanelDeclaration::update_or_build(const bNodePanelState &old_panel,
bNodePanelState &new_panel) const
{
build(new_panel);
/* Copy existing state to the new panel */
SET_FLAG_FROM_TEST(new_panel.flag, old_panel.is_collapsed(), NODE_PANEL_COLLAPSED);
}
int PanelDeclaration::depth() const
{
int count = 0;
for (const PanelDeclaration *parent = this->parent_panel; parent; parent = parent->parent_panel)
{
count++;
}
return count;
}
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;
}
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::supports_field()
{
BLI_assert(this->is_input());
decl_base_->input_field_type = InputSocketFieldType::IsSupported;
return *this;
}
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::dependent_field(
Vector<int> input_dependencies)
{
BLI_assert(this->is_output());
this->reference_pass(input_dependencies);
decl_base_->output_field_dependency = OutputFieldDependency::ForPartiallyDependentField(
std::move(input_dependencies));
return *this;
}
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::hide_label(bool value)
{
decl_base_->hide_label = value;
return *this;
}
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::hide_value(bool value)
{
decl_base_->hide_value = value;
return *this;
}
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::multi_input(bool value)
{
BLI_assert(this->is_input());
decl_base_->is_multi_input = value;
return *this;
}
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::compact(bool value)
{
decl_base_->compact = value;
return *this;
}
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::reference_pass(
const Span<int> input_indices)
{
BLI_assert(this->is_output());
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;
relation.to_field_output = decl_base_->index;
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();
if (this->is_input()) {
this->supports_field();
for (const int input_index : indices) {
aal::EvalRelation relation;
relation.field_input = decl_base_->index;
relation.geometry_input = input_index;
relations.eval_relations.append(relation);
}
}
else {
this->field_source();
for (const int output_index : indices) {
aal::AvailableRelation relation;
relation.field_output = decl_base_->index;
relation.geometry_output = output_index;
relations.available_relations.append(relation);
}
}
return *this;
}
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::short_label(std::string value)
{
decl_base_->short_label = std::move(value);
return *this;
}
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::description(std::string value)
{
decl_base_->description = std::move(value);
return *this;
}
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::translation_context(
std::optional<std::string> value)
{
decl_base_->translation_context = std::move(value);
return *this;
}
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::no_muted_links(bool value)
{
decl_base_->no_mute_links = value;
return *this;
}
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::available(bool value)
{
decl_base_->is_available = value;
return *this;
}
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::is_attribute_name(bool value)
{
decl_base_->is_attribute_name = value;
return *this;
}
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::is_default_link_socket(bool value)
{
decl_base_->is_default_link_socket = value;
return *this;
}
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::default_input_type(
const NodeDefaultInputType value)
{
decl_base_->default_input_type = value;
return *this;
}
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::field_on_all()
{
if (this->is_input()) {
this->supports_field();
}
if (this->is_output()) {
this->field_source();
}
field_on_all_ = true;
return *this;
}
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::field_source()
{
BLI_assert(this->is_output());
decl_base_->output_field_dependency = OutputFieldDependency::ForFieldSource();
return *this;
}
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::implicit_field(
const NodeDefaultInputType default_input_type)
{
BLI_assert(this->is_input());
this->hide_value();
decl_base_->input_field_type = InputSocketFieldType::Implicit;
decl_base_->default_input_type = default_input_type;
return *this;
}
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::implicit_field_on_all(
const NodeDefaultInputType default_input_type)
{
this->implicit_field(default_input_type);
field_on_all_ = true;
return *this;
}
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::implicit_field_on(
const NodeDefaultInputType default_input_type, const Span<int> input_indices)
{
this->field_on(input_indices);
this->implicit_field(default_input_type);
return *this;
}
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::dependent_field()
{
BLI_assert(this->is_output());
decl_base_->output_field_dependency = OutputFieldDependency::ForDependentField();
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;
}
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;
}
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::compositor_realization_mode(
CompositorInputRealizationMode value)
{
decl_base_->compositor_realization_mode_ = value;
return *this;
}
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::compositor_domain_priority(
int priority)
{
BLI_assert(priority >= 0);
decl_base_->compositor_domain_priority_ = priority;
return *this;
}
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::compositor_expects_single_value(
bool value)
{
decl_base_->compositor_expects_single_value_ = value;
return *this;
}
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::make_available(
std::function<void(bNode &)> fn)
{
decl_base_->make_available_fn_ = std::move(fn);
return *this;
}
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::align_with_previous(const bool value)
{
decl_base_->align_with_previous_socket = value;
return *this;
}
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. */
return this->socket_name_ptr(RNA_pointer_create_discrete(const_cast<ID *>(id),
const_cast<StructRNA *>(srna),
const_cast<void *>(data)),
property_name);
}
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::panel_toggle(const bool value)
{
decl_base_->is_panel_toggle = value;
return *this;
}
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::is_layer_name(const bool value)
{
decl_base_->is_layer_name = value;
return *this;
}
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_;
}
const CompositorInputRealizationMode &SocketDeclaration::compositor_realization_mode() const
{
return compositor_realization_mode_;
}
int SocketDeclaration::compositor_domain_priority() const
{
return compositor_domain_priority_;
}
bool SocketDeclaration::compositor_expects_single_value() const
{
return compositor_expects_single_value_;
}
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;
}
PanelDeclarationBuilder &PanelDeclarationBuilder::translation_context(
std::optional<std::string> value)
{
decl_->translation_context = value;
return *this;
}
PanelDeclarationBuilder &PanelDeclarationBuilder::default_closed(bool closed)
{
decl_->default_collapsed = closed;
return *this;
}
namespace implicit_field_inputs {
static void position(const bNode & /*node*/, void *r_value)
{
new (r_value) bke::SocketValueVariant(bke::AttributeFieldInput::Create<float3>("position"));
}
static void normal(const bNode & /*node*/, void *r_value)
{
new (r_value)
bke::SocketValueVariant(fn::Field<float3>(std::make_shared<bke::NormalFieldInput>()));
}
static void index(const bNode & /*node*/, void *r_value)
{
new (r_value) bke::SocketValueVariant(fn::Field<int>(std::make_shared<fn::IndexFieldInput>()));
}
static void id_or_index(const bNode & /*node*/, void *r_value)
{
new (r_value)
bke::SocketValueVariant(fn::Field<int>(std::make_shared<bke::IDAttributeFieldInput>()));
}
static void instance_transform(const bNode & /*node*/, void *r_value)
{
new (r_value)
bke::SocketValueVariant(bke::AttributeFieldInput::Create<float4x4>("instance_transform"));
}
static void handle_left(const bNode & /*node*/, void *r_value)
{
new (r_value) bke::SocketValueVariant(bke::AttributeFieldInput::Create<float3>("handle_left"));
}
static void handle_right(const bNode & /*node*/, void *r_value)
{
new (r_value) bke::SocketValueVariant(bke::AttributeFieldInput::Create<float3>("handle_right"));
}
} // namespace implicit_field_inputs
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)
{
const eNodeSocketDatatype stype = eNodeSocketDatatype(socket_type.type);
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;
}
} // namespace blender::nodes