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.

815 lines
25 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"
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
#include "BLI_stack.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 "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"
namespace blender::nodes {
static void reset_declaration(NodeDeclaration &declaration)
{
std::destroy_at(&declaration);
new (&declaration) NodeDeclaration();
}
void build_node_declaration(const bNodeType &typeinfo, NodeDeclaration &r_declaration)
{
reset_declaration(r_declaration);
NodeDeclarationBuilder node_decl_builder{r_declaration};
typeinfo.declare(node_decl_builder);
node_decl_builder.finalize();
}
Nodes: Use dynamic declarations for group nodes Since a year and a half ago we've been switching to a new way to represent what sockets a node should have called "declarations" that's easier to use, clearer, and more flexible for upcoming features like dynamic socket counts or generic type sockets. All builtin nodes with a static set of sockets have switched, but one missing area has been group nodes and group input/output nodes. These nodes have **dynamic** declarations which change based on their properties or the group they're inside of. This patch addresses that, in preparation for using the same dynamic declaration feature for simulation nodes. Generally there shouldn't be user-visible differences, but one benefit is that user-created socket descriptions are now visible directly in the node editor for group nodes and group input/output nodes. The commit contains a few changes: - Add a node type callback for building dynamic declarations with different arguments - Add an `Extend` socket declaration for the "virtual" sockets used for connecting new links - A similar `Custom` socket declaration is used for addon-defined socket - Simplify the node update loop to use the declaration to build update sockets - Replace the "group update" functions with the declaration building - Move the node group input/output link creation to link drag operator - Make the field status part of group node declarations (not for group input/output nodes though) - Some fixes for declarations to make them update and build properly Differential Revision: https://developer.blender.org/D16850
2023-01-16 15:47:10 -06:00
void build_node_declaration_dynamic(const bNodeTree &node_tree,
const bNode &node,
NodeDeclaration &r_declaration)
{
reset_declaration(r_declaration);
NodeDeclarationBuilder node_decl_builder{r_declaration};
node.typeinfo->declare_dynamic(node_tree, node, node_decl_builder);
node_decl_builder.finalize();
Nodes: Use dynamic declarations for group nodes Since a year and a half ago we've been switching to a new way to represent what sockets a node should have called "declarations" that's easier to use, clearer, and more flexible for upcoming features like dynamic socket counts or generic type sockets. All builtin nodes with a static set of sockets have switched, but one missing area has been group nodes and group input/output nodes. These nodes have **dynamic** declarations which change based on their properties or the group they're inside of. This patch addresses that, in preparation for using the same dynamic declaration feature for simulation nodes. Generally there shouldn't be user-visible differences, but one benefit is that user-created socket descriptions are now visible directly in the node editor for group nodes and group input/output nodes. The commit contains a few changes: - Add a node type callback for building dynamic declarations with different arguments - Add an `Extend` socket declaration for the "virtual" sockets used for connecting new links - A similar `Custom` socket declaration is used for addon-defined socket - Simplify the node update loop to use the declaration to build update sockets - Replace the "group update" functions with the declaration building - Move the node group input/output link creation to link drag operator - Make the field status part of group node declarations (not for group input/output nodes though) - Some fixes for declarations to make them update and build properly Differential Revision: https://developer.blender.org/D16850
2023-01-16 15:47:10 -06:00
}
void NodeDeclarationBuilder::finalize()
{
if (is_function_node_) {
for (std::unique_ptr<BaseSocketDeclarationBuilder> &socket_builder : socket_builders_) {
if (SocketDeclaration *socket_decl = socket_builder->decl_in_base_) {
if (socket_decl->input_field_type != InputSocketFieldType::Implicit) {
socket_decl->input_field_type = InputSocketFieldType::IsSupported;
}
}
}
for (std::unique_ptr<BaseSocketDeclarationBuilder> &socket_builder : socket_builders_) {
if (SocketDeclaration *socket_decl = socket_builder->decl_out_base_) {
socket_decl->output_field_dependency = OutputFieldDependency::ForDependentField();
socket_builder->reference_pass_all_ = true;
}
}
}
Vector<int> geometry_inputs;
for (const int i : declaration_.inputs.index_range()) {
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 (dynamic_cast<decl::Geometry *>(declaration_.inputs[i])) {
geometry_inputs.append(i);
}
}
Vector<int> geometry_outputs;
for (const int i : declaration_.outputs.index_range()) {
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 (dynamic_cast<decl::Geometry *>(declaration_.outputs[i])) {
geometry_outputs.append(i);
}
}
for (std::unique_ptr<BaseSocketDeclarationBuilder> &socket_builder : socket_builders_) {
if (!socket_builder->decl_in_base_) {
continue;
}
if (socket_builder->field_on_all_) {
aal::RelationsInNode &relations = this->get_anonymous_attribute_relations();
const int field_input = socket_builder->index_in_;
for (const int geometry_input : geometry_inputs) {
relations.eval_relations.append({field_input, geometry_input});
}
}
}
for (std::unique_ptr<BaseSocketDeclarationBuilder> &socket_builder : socket_builders_) {
if (!socket_builder->decl_out_base_) {
continue;
}
if (socket_builder->field_on_all_) {
aal::RelationsInNode &relations = this->get_anonymous_attribute_relations();
const int field_output = socket_builder->index_out_;
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->index_out_;
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->index_out_;
for (const int geometry_input : geometry_inputs) {
relations.propagate_relations.append({geometry_input, geometry_output});
}
}
}
BLI_assert(declaration_.is_valid());
}
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 NodeDeclarationBuilder::set_active_panel_builder(const PanelDeclarationBuilder *panel_builder)
{
if (panel_builders_.is_empty()) {
BLI_assert(panel_builder == nullptr);
return;
}
BLI_assert(!panel_builder || !panel_builder->is_complete_);
PanelDeclarationBuilder *last_panel_builder = panel_builders_.last().get();
if (last_panel_builder != panel_builder) {
last_panel_builder->is_complete_ = true;
}
}
namespace anonymous_attribute_lifetime {
bool operator==(const RelationsInNode &a, const RelationsInNode &b)
{
return a.propagate_relations == b.propagate_relations &&
a.reference_relations == b.reference_relations && a.eval_relations == b.eval_relations &&
a.available_relations == b.available_relations &&
a.available_on_none == b.available_on_none;
}
bool operator!=(const RelationsInNode &a, const RelationsInNode &b)
{
return !(a == b);
}
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
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::is_valid() const
{
if (!this->use_custom_socket_order) {
/* Skip validation for conventional socket layouts. */
return true;
}
/* Validation state for the interface root items as well as any panel content. */
struct ValidationState {
/* Remaining number of items expected in a panel */
int remaining_items = 0;
/* Sockets first, followed by panels. */
NodeTreeInterfaceItemType item_type = NODE_INTERFACE_SOCKET;
/* Output sockets first, followed by input sockets. */
eNodeSocketInOut socket_in_out = SOCK_OUT;
};
Stack<ValidationState> panel_states;
panel_states.push({});
for (const ItemDeclarationPtr &item_decl : items) {
BLI_assert(panel_states.size() >= 1);
ValidationState &state = panel_states.peek();
if (const SocketDeclaration *socket_decl = dynamic_cast<const SocketDeclaration *>(
item_decl.get()))
{
if (state.item_type != NODE_INTERFACE_SOCKET) {
std::cout << "Socket added after panel" << std::endl;
return false;
}
/* Check for consistent outputs.., inputs.. blocks. */
if (state.socket_in_out == SOCK_OUT && socket_decl->in_out == SOCK_IN) {
/* Start of input sockets. */
state.socket_in_out = SOCK_IN;
}
if (socket_decl->in_out != state.socket_in_out) {
std::cout << "Output socket added after input socket" << std::endl;
return false;
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
}
/* Item counting for the panels, but ignore for root items. */
if (panel_states.size() > 1) {
if (state.remaining_items <= 0) {
std::cout << "More sockets than expected in panel" << std::endl;
return false;
}
--state.remaining_items;
/* Panel closed after last item is added. */
if (state.remaining_items == 0) {
panel_states.pop();
}
}
}
else if (const PanelDeclaration *panel_decl = dynamic_cast<const PanelDeclaration *>(
item_decl.get()))
{
if (state.item_type == NODE_INTERFACE_SOCKET) {
/* Start of panels section */
state.item_type = NODE_INTERFACE_PANEL;
}
BLI_assert(state.item_type == NODE_INTERFACE_PANEL);
if (panel_decl->num_child_decls > 0) {
/* New panel started. */
panel_states.push({panel_decl->num_child_decls});
}
}
else {
BLI_assert_unreachable();
return false;
}
}
/* All panels complete? */
if (panel_states.size() != 1) {
std::cout << "Incomplete last panel" << std::endl;
return false;
}
return true;
}
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 : items) {
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 {
/* 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))
{
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_unavailable, 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_unavailable) {
return false;
}
return true;
}
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
PanelDeclarationBuilder &NodeDeclarationBuilder::add_panel(StringRef name, int identifier)
{
std::unique_ptr<PanelDeclaration> panel_decl = std::make_unique<PanelDeclaration>();
std::unique_ptr<PanelDeclarationBuilder> panel_decl_builder =
std::make_unique<PanelDeclarationBuilder>();
panel_decl_builder->decl_ = &*panel_decl;
panel_decl_builder->node_decl_builder_ = this;
if (identifier >= 0) {
panel_decl->identifier = identifier;
}
else {
/* Use index as identifier. */
panel_decl->identifier = declaration_.items.size();
}
panel_decl->name = name;
declaration_.items.append(std::move(panel_decl));
PanelDeclarationBuilder &builder_ref = *panel_decl_builder;
panel_builders_.append(std::move(panel_decl_builder));
set_active_panel_builder(&builder_ref);
return builder_ref;
}
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
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);
}
std::unique_ptr<SocketDeclaration> make_declaration_for_socket_type(
const eNodeSocketDatatype socket_type)
{
switch (socket_type) {
case SOCK_FLOAT:
return std::make_unique<decl::Float>();
case SOCK_VECTOR:
return std::make_unique<decl::Vector>();
case SOCK_RGBA:
return std::make_unique<decl::Color>();
case SOCK_BOOLEAN:
return std::make_unique<decl::Bool>();
case SOCK_ROTATION:
return std::make_unique<decl::Rotation>();
case SOCK_INT:
return std::make_unique<decl::Int>();
case SOCK_STRING:
return std::make_unique<decl::String>();
case SOCK_GEOMETRY:
return std::make_unique<decl::Geometry>();
case SOCK_OBJECT:
return std::make_unique<decl::Object>();
case SOCK_IMAGE:
return std::make_unique<decl::Image>();
case SOCK_COLLECTION:
return std::make_unique<decl::Collection>();
case SOCK_MATERIAL:
return std::make_unique<decl::Material>();
default:
return {};
}
}
BaseSocketDeclarationBuilder &NodeDeclarationBuilder::add_input(
const eNodeSocketDatatype socket_type, const StringRef name, const StringRef identifier)
{
switch (socket_type) {
case SOCK_FLOAT:
return this->add_input<decl::Float>(name, identifier);
case SOCK_VECTOR:
return this->add_input<decl::Vector>(name, identifier);
case SOCK_RGBA:
return this->add_input<decl::Color>(name, identifier);
case SOCK_BOOLEAN:
return this->add_input<decl::Bool>(name, identifier);
case SOCK_ROTATION:
return this->add_input<decl::Rotation>(name, identifier);
case SOCK_INT:
return this->add_input<decl::Int>(name, identifier);
case SOCK_STRING:
return this->add_input<decl::String>(name, identifier);
case SOCK_GEOMETRY:
return this->add_input<decl::Geometry>(name, identifier);
case SOCK_OBJECT:
return this->add_input<decl::Object>(name, identifier);
case SOCK_IMAGE:
return this->add_input<decl::Image>(name, identifier);
case SOCK_COLLECTION:
return this->add_input<decl::Collection>(name, identifier);
case SOCK_MATERIAL:
return this->add_input<decl::Material>(name, identifier);
default:
BLI_assert_unreachable();
return this->add_input<decl::Float>("", "");
}
}
BaseSocketDeclarationBuilder &NodeDeclarationBuilder::add_output(
const eNodeSocketDatatype socket_type, const StringRef name, const StringRef identifier)
{
switch (socket_type) {
case SOCK_FLOAT:
return this->add_output<decl::Float>(name, identifier);
case SOCK_VECTOR:
return this->add_output<decl::Vector>(name, identifier);
case SOCK_RGBA:
return this->add_output<decl::Color>(name, identifier);
case SOCK_BOOLEAN:
return this->add_output<decl::Bool>(name, identifier);
case SOCK_ROTATION:
return this->add_output<decl::Rotation>(name, identifier);
case SOCK_INT:
return this->add_output<decl::Int>(name, identifier);
case SOCK_STRING:
return this->add_output<decl::String>(name, identifier);
case SOCK_GEOMETRY:
return this->add_output<decl::Geometry>(name, identifier);
case SOCK_OBJECT:
return this->add_output<decl::Object>(name, identifier);
case SOCK_IMAGE:
return this->add_output<decl::Image>(name, identifier);
case SOCK_COLLECTION:
return this->add_output<decl::Collection>(name, identifier);
case SOCK_MATERIAL:
return this->add_output<decl::Material>(name, identifier);
default:
BLI_assert_unreachable();
return this->add_output<decl::Float>("", "");
}
}
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::supports_field()
{
if (decl_in_base_) {
decl_in_base_->input_field_type = InputSocketFieldType::IsSupported;
}
return *this;
}
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::dependent_field(
Vector<int> input_dependencies)
{
this->reference_pass(input_dependencies);
if (decl_out_base_) {
decl_out_base_->output_field_dependency = OutputFieldDependency::ForPartiallyDependentField(
std::move(input_dependencies));
}
return *this;
}
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::hide_label(bool value)
{
if (decl_in_base_) {
decl_in_base_->hide_label = value;
}
if (decl_out_base_) {
decl_out_base_->hide_label = value;
}
return *this;
}
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::hide_value(bool value)
{
if (decl_in_base_) {
decl_in_base_->hide_value = value;
}
if (decl_out_base_) {
decl_out_base_->hide_value = value;
}
return *this;
}
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::multi_input(bool value)
{
if (decl_in_base_) {
decl_in_base_->is_multi_input = value;
}
return *this;
}
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::reference_pass(
const Span<int> input_indices)
{
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 = index_out_;
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 (decl_in_base_) {
this->supports_field();
for (const int input_index : indices) {
aal::EvalRelation relation;
relation.field_input = index_in_;
relation.geometry_input = input_index;
relations.eval_relations.append(relation);
}
}
if (decl_out_base_) {
this->field_source();
for (const int output_index : indices) {
aal::AvailableRelation relation;
relation.field_output = index_out_;
relation.geometry_output = output_index;
relations.available_relations.append(relation);
}
}
return *this;
}
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::short_label(std::string value)
{
if (decl_in_base_) {
decl_in_base_->description = std::move(value);
}
if (decl_out_base_) {
decl_out_base_->description = std::move(value);
}
return *this;
}
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::description(std::string value)
{
if (decl_in_base_) {
decl_in_base_->description = std::move(value);
}
if (decl_out_base_) {
decl_out_base_->description = std::move(value);
}
return *this;
}
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::translation_context(std::string value)
{
if (decl_in_base_) {
decl_in_base_->translation_context = value;
}
if (decl_out_base_) {
decl_out_base_->translation_context = std::move(value);
}
return *this;
}
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::no_muted_links(bool value)
{
if (decl_in_base_) {
decl_in_base_->no_mute_links = value;
}
if (decl_out_base_) {
decl_out_base_->no_mute_links = value;
}
return *this;
}
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::unavailable(bool value)
{
if (decl_in_base_) {
decl_in_base_->is_unavailable = value;
}
if (decl_out_base_) {
decl_out_base_->is_unavailable = value;
}
return *this;
}
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::is_attribute_name(bool value)
{
if (decl_in_base_) {
decl_in_base_->is_attribute_name = value;
}
if (decl_out_base_) {
decl_out_base_->is_attribute_name = value;
}
return *this;
}
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::is_default_link_socket(bool value)
{
if (decl_in_base_) {
decl_in_base_->is_default_link_socket = value;
}
if (decl_out_base_) {
decl_out_base_->is_default_link_socket = value;
}
return *this;
}
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::field_on_all()
{
if (decl_in_base_) {
this->supports_field();
}
if (decl_out_base_) {
this->field_source();
}
field_on_all_ = true;
return *this;
}
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::field_source()
{
if (decl_out_base_) {
decl_out_base_->output_field_dependency = OutputFieldDependency::ForFieldSource();
}
return *this;
}
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::implicit_field(ImplicitInputValueFn fn)
{
this->hide_value();
if (decl_in_base_) {
decl_in_base_->input_field_type = InputSocketFieldType::Implicit;
decl_in_base_->implicit_input_fn = std::make_unique<ImplicitInputValueFn>(std::move(fn));
}
return *this;
}
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::implicit_field_on_all(
ImplicitInputValueFn fn)
{
this->implicit_field(fn);
field_on_all_ = true;
return *this;
}
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::implicit_field_on(
ImplicitInputValueFn fn, const Span<int> input_indices)
{
this->field_on(input_indices);
this->implicit_field(fn);
return *this;
}
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::dependent_field()
{
if (decl_out_base_) {
decl_out_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::compositor_realization_options(
CompositorInputRealizationOptions value)
{
if (decl_in_base_) {
decl_in_base_->compositor_realization_options_ = value;
}
if (decl_out_base_) {
decl_out_base_->compositor_realization_options_ = value;
}
return *this;
}
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::compositor_domain_priority(
int priority)
{
if (decl_in_base_) {
decl_in_base_->compositor_domain_priority_ = priority;
}
if (decl_out_base_) {
decl_out_base_->compositor_domain_priority_ = priority;
}
return *this;
}
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::compositor_expects_single_value(
bool value)
{
if (decl_in_base_) {
decl_in_base_->compositor_expects_single_value_ = value;
}
if (decl_out_base_) {
decl_out_base_->compositor_expects_single_value_ = value;
}
return *this;
}
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::make_available(
std::function<void(bNode &)> fn)
{
if (decl_in_base_) {
decl_in_base_->make_available_fn_ = std::move(fn);
}
if (decl_out_base_) {
decl_out_base_->make_available_fn_ = std::move(fn);
}
return *this;
}
namespace implicit_field_inputs {
void position(const bNode & /*node*/, void *r_value)
{
new (r_value) fn::ValueOrField<float3>(bke::AttributeFieldInput::Create<float3>("position"));
}
void normal(const bNode & /*node*/, void *r_value)
{
new (r_value)
fn::ValueOrField<float3>(fn::Field<float3>(std::make_shared<bke::NormalFieldInput>()));
}
void index(const bNode & /*node*/, void *r_value)
{
new (r_value) fn::ValueOrField<int>(fn::Field<int>(std::make_shared<fn::IndexFieldInput>()));
}
void id_or_index(const bNode & /*node*/, void *r_value)
{
new (r_value)
fn::ValueOrField<int>(fn::Field<int>(std::make_shared<bke::IDAttributeFieldInput>()));
}
} // namespace implicit_field_inputs
} // namespace blender::nodes