This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/source/blender/nodes/intern/node_declaration.cc
Hans Goudey 7026096099 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:25 -06:00

240 lines
8.3 KiB
C++

/* 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 "BKE_geometry_fields.hh"
#include "BKE_node.h"
namespace blender::nodes {
void build_node_declaration(const bNodeType &typeinfo, NodeDeclaration &r_declaration)
{
NodeDeclarationBuilder node_decl_builder{r_declaration};
typeinfo.declare(node_decl_builder);
node_decl_builder.finalize();
}
void build_node_declaration_dynamic(const bNodeTree &node_tree,
const bNode &node,
NodeDeclaration &r_declaration)
{
r_declaration.inputs.clear();
r_declaration.outputs.clear();
node.typeinfo->declare_dynamic(node_tree, node, r_declaration);
}
void NodeDeclarationBuilder::finalize()
{
if (is_function_node_) {
for (std::unique_ptr<BaseSocketDeclarationBuilder> &socket_builder : input_builders_) {
SocketDeclaration &socket_decl = *socket_builder->declaration();
if (socket_decl.input_field_type != InputSocketFieldType::Implicit) {
socket_decl.input_field_type = InputSocketFieldType::IsSupported;
}
}
for (std::unique_ptr<BaseSocketDeclarationBuilder> &socket_builder : output_builders_) {
SocketDeclaration &socket_decl = *socket_builder->declaration();
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()) {
if (dynamic_cast<decl::Geometry *>(declaration_.inputs[i].get())) {
geometry_inputs.append(i);
}
}
Vector<int> geometry_outputs;
for (const int i : declaration_.outputs.index_range()) {
if (dynamic_cast<decl::Geometry *>(declaration_.outputs[i].get())) {
geometry_outputs.append(i);
}
}
for (std::unique_ptr<BaseSocketDeclarationBuilder> &socket_builder : input_builders_) {
if (socket_builder->field_on_all_) {
aal::RelationsInNode &relations = this->get_anonymous_attribute_relations();
const int field_input = socket_builder->index_;
for (const int geometry_input : geometry_inputs) {
relations.eval_relations.append({field_input, geometry_input});
}
}
}
for (std::unique_ptr<BaseSocketDeclarationBuilder> &socket_builder : output_builders_) {
if (socket_builder->field_on_all_) {
aal::RelationsInNode &relations = this->get_anonymous_attribute_relations();
const int field_output = socket_builder->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->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->index_;
for (const int geometry_input : geometry_inputs) {
relations.propagate_relations.append({geometry_input, geometry_output});
}
}
}
}
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
bool NodeDeclaration::matches(const bNode &node) const
{
auto check_sockets = [&](ListBase sockets, Span<SocketDeclarationPtr> socket_decls) {
const int tot_sockets = BLI_listbase_count(&sockets);
if (tot_sockets != socket_decls.size()) {
return false;
}
int i;
LISTBASE_FOREACH_INDEX (const bNodeSocket *, socket, &sockets, i) {
const SocketDeclaration &socket_decl = *socket_decls[i];
if (!socket_decl.matches(*socket)) {
return false;
}
}
return true;
};
if (!check_sockets(node.inputs, inputs)) {
return false;
}
if (!check_sockets(node.outputs, outputs)) {
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;
}
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