diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 21ca65baf00..2e843e82a9f 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -480,7 +480,7 @@ bool ntreeHasType(const struct bNodeTree *ntree, int type); bool ntreeHasTree(const struct bNodeTree *ntree, const struct bNodeTree *lookup); void ntreeUpdateTree(struct Main *main, struct bNodeTree *ntree); void ntreeUpdateAllNew(struct Main *main); -void ntreeUpdateAllUsers(struct Main *main, struct ID *id); +void ntreeUpdateAllUsers(struct Main *main, struct ID *id, int tree_update_flag); void ntreeGetDependencyList(struct bNodeTree *ntree, struct bNode ***r_deplist, diff --git a/source/blender/blenkernel/intern/lib_remap.c b/source/blender/blenkernel/intern/lib_remap.c index 250b8d4d515..48396c5e6d9 100644 --- a/source/blender/blenkernel/intern/lib_remap.c +++ b/source/blender/blenkernel/intern/lib_remap.c @@ -345,7 +345,7 @@ static void libblock_remap_data_postprocess_obdata_relink(Main *bmain, Object *o static void libblock_remap_data_postprocess_nodetree_update(Main *bmain, ID *new_id) { /* Update all group nodes using a node group. */ - ntreeUpdateAllUsers(bmain, new_id); + ntreeUpdateAllUsers(bmain, new_id, 0); } /** diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index d56a7bf8fb4..f223ed28301 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -52,9 +52,12 @@ #include "BLI_map.hh" #include "BLI_math.h" #include "BLI_path_util.h" +#include "BLI_set.hh" +#include "BLI_stack.hh" #include "BLI_string.h" #include "BLI_string_utils.h" #include "BLI_utildefines.h" +#include "BLI_vector_set.hh" #include "BLT_translation.h" @@ -80,6 +83,7 @@ #include "NOD_function.h" #include "NOD_geometry.h" #include "NOD_node_declaration.hh" +#include "NOD_node_tree_ref.hh" #include "NOD_shader.h" #include "NOD_socket.h" #include "NOD_texture.h" @@ -93,6 +97,20 @@ #define NODE_DEFAULT_MAX_WIDTH 700 +using blender::Array; +using blender::MutableSpan; +using blender::Set; +using blender::Span; +using blender::Stack; +using blender::Vector; +using blender::VectorSet; +using blender::nodes::InputSocketFieldType; +using blender::nodes::NodeDeclaration; +using blender::nodes::OutputFieldDependency; +using blender::nodes::OutputSocketFieldType; +using blender::nodes::SocketDeclaration; +using namespace blender::nodes::node_tree_ref_types; + /* Fallback types for undefined tree, nodes, sockets */ static bNodeTreeType NodeTreeTypeUndefined; bNodeType NodeTypeUndefined; @@ -110,6 +128,10 @@ static void node_socket_interface_free(bNodeTree *UNUSED(ntree), static void nodeMuteRerouteOutputLinks(struct bNodeTree *ntree, struct bNode *node, const bool mute); +static FieldInferencingInterface *node_field_inferencing_interface_copy( + const FieldInferencingInterface &field_inferencing_interface); +static void node_field_inferencing_interface_free( + const FieldInferencingInterface *field_inferencing_interface); static void ntree_init_data(ID *id) { @@ -220,6 +242,11 @@ static void ntree_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, c /* node tree will generate its own interface type */ ntree_dst->interface_type = nullptr; + + if (ntree_src->field_inferencing_interface) { + ntree_dst->field_inferencing_interface = node_field_inferencing_interface_copy( + *ntree_src->field_inferencing_interface); + } } static void ntree_free_data(ID *id) @@ -265,6 +292,8 @@ static void ntree_free_data(ID *id) MEM_freeN(sock); } + node_field_inferencing_interface_free(ntree->field_inferencing_interface); + /* free preview hash */ if (ntree->previews) { BKE_node_instance_hash_free(ntree->previews, (bNodeInstanceValueFP)BKE_node_preview_free); @@ -647,6 +676,8 @@ void ntreeBlendReadData(BlendDataReader *reader, bNodeTree *ntree) ntree->progress = nullptr; ntree->execdata = nullptr; + ntree->field_inferencing_interface = nullptr; + BLO_read_data_address(reader, &ntree->adt); BKE_animdata_blend_read_data(reader, ntree->adt); @@ -4425,7 +4456,518 @@ void ntreeUpdateAllNew(Main *main) FOREACH_NODETREE_END; } -void ntreeUpdateAllUsers(Main *main, ID *id) +/** + * Information about how a node interacts with fields. + */ +struct FieldInferencingInterface { + Vector inputs; + Vector outputs; + + friend bool operator==(const FieldInferencingInterface &a, const FieldInferencingInterface &b) + { + return a.inputs == b.inputs && a.outputs == b.outputs; + } + + friend bool operator!=(const FieldInferencingInterface &a, const FieldInferencingInterface &b) + { + return !(a == b); + } +}; + +static FieldInferencingInterface *node_field_inferencing_interface_copy( + const FieldInferencingInterface &field_inferencing_interface) +{ + return new FieldInferencingInterface(field_inferencing_interface); +} + +static void node_field_inferencing_interface_free( + const FieldInferencingInterface *field_inferencing_interface) +{ + delete field_inferencing_interface; +} + +namespace blender::bke::node_field_inferencing { + +static bool is_field_socket_type(eNodeSocketDatatype type) +{ + return ELEM(type, SOCK_FLOAT, SOCK_INT, SOCK_BOOLEAN, SOCK_VECTOR, SOCK_RGBA); +} + +static bool is_field_socket_type(const SocketRef &socket) +{ + return is_field_socket_type((eNodeSocketDatatype)socket.typeinfo()->type); +} + +static bool update_field_inferencing(bNodeTree &btree); + +static InputSocketFieldType get_interface_input_field_type(const NodeRef &node, + const InputSocketRef &socket) +{ + if (!is_field_socket_type(socket)) { + return InputSocketFieldType::None; + } + if (node.is_reroute_node()) { + return InputSocketFieldType::IsSupported; + } + if (node.is_group_output_node()) { + /* Outputs always support fields when the data type is correct. */ + return InputSocketFieldType::IsSupported; + } + + const NodeDeclaration *node_decl = node.declaration(); + + /* Node declarations should be implemented for nodes involved here. */ + BLI_assert(node_decl != nullptr); + + if (node_decl->is_function_node()) { + /* In a function node, every socket supports fields. */ + return InputSocketFieldType::IsSupported; + } + /* Get the field type from the declaration. */ + const SocketDeclaration &socket_decl = *node_decl->inputs()[socket.index()]; + return socket_decl.input_field_type(); +} + +static OutputFieldDependency get_interface_output_field_dependency(const NodeRef &node, + const OutputSocketRef &socket) +{ + if (!is_field_socket_type(socket)) { + /* Non-field sockets always output data. */ + return OutputFieldDependency::ForDataSource(); + } + if (node.is_reroute_node()) { + /* The reroute just forwards what is passed in. */ + return OutputFieldDependency::ForDependentField(); + } + if (node.is_group_input_node()) { + /* Input nodes get special treatment in #determine_group_input_states. */ + return OutputFieldDependency::ForDependentField(); + } + + const NodeDeclaration *node_decl = node.declaration(); + + /* Node declarations should be implemented for nodes involved here. */ + BLI_assert(node_decl != nullptr); + + if (node_decl->is_function_node()) { + /* In a generic function node, all outputs depend on all inputs. */ + return OutputFieldDependency::ForDependentField(); + } + + /* Use the socket declaration. */ + const SocketDeclaration &socket_decl = *node_decl->outputs()[socket.index()]; + return socket_decl.output_field_dependency(); +} + +/** + * Retrieves information about how the node interacts with fields. + * In the future, this information can be stored in the node declaration. This would allow this + * function to return a reference, making it more efficient. + */ +static FieldInferencingInterface get_node_field_inferencing_interface(const NodeRef &node) +{ + /* Node groups already reference all required information, so just return that. */ + if (node.is_group_node()) { + bNodeTree *group = (bNodeTree *)node.bnode()->id; + if (group == nullptr) { + return FieldInferencingInterface(); + } + if (group->field_inferencing_interface == nullptr) { + /* Update group recursively. */ + update_field_inferencing(*group); + } + return *group->field_inferencing_interface; + } + + FieldInferencingInterface inferencing_interface; + for (const InputSocketRef *input_socket : node.inputs()) { + inferencing_interface.inputs.append(get_interface_input_field_type(node, *input_socket)); + } + + for (const OutputSocketRef *output_socket : node.outputs()) { + inferencing_interface.outputs.append( + get_interface_output_field_dependency(node, *output_socket)); + } + return inferencing_interface; +} + +/** + * This struct contains information for every socket. The values are propagated through the + * network. + */ +struct SocketFieldState { + /* This socket is currently a single value. It could become a field though. */ + bool is_single = true; + /* This socket is required to be a single value. It must not be a field. */ + bool requires_single = false; + /* This socket starts a new field. */ + bool is_field_source = false; +}; + +static Vector gather_input_socket_dependencies( + const OutputFieldDependency &field_dependency, const NodeRef &node) +{ + const OutputSocketFieldType type = field_dependency.field_type(); + Vector input_sockets; + switch (type) { + case OutputSocketFieldType::FieldSource: + case OutputSocketFieldType::None: { + break; + } + case OutputSocketFieldType::DependentField: { + /* This output depends on all inputs. */ + input_sockets.extend(node.inputs()); + break; + } + case OutputSocketFieldType::PartiallyDependent: { + /* This output depends only on a few inputs. */ + for (const int i : field_dependency.linked_input_indices()) { + input_sockets.append(&node.input(i)); + } + break; + } + } + return input_sockets; +} + +/** + * Check what the group output socket depends on. Potentially traverses the node tree + * to figure out if it is always a field or if it depends on any group inputs. + */ +static OutputFieldDependency find_group_output_dependencies( + const InputSocketRef &group_output_socket, + const Span field_state_by_socket_id) +{ + if (!is_field_socket_type(group_output_socket)) { + return OutputFieldDependency::ForDataSource(); + } + + /* Use a Set here instead of an array indexed by socket id, because we my only need to look at + * very few sockets. */ + Set handled_sockets; + Stack sockets_to_check; + + handled_sockets.add(&group_output_socket); + sockets_to_check.push(&group_output_socket); + + /* Keeps track of group input indices that are (indirectly) connected to the output. */ + Vector linked_input_indices; + + while (!sockets_to_check.is_empty()) { + const InputSocketRef *input_socket = sockets_to_check.pop(); + + for (const OutputSocketRef *origin_socket : input_socket->logically_linked_sockets()) { + const NodeRef &origin_node = origin_socket->node(); + const SocketFieldState &origin_state = field_state_by_socket_id[origin_socket->id()]; + + if (origin_state.is_field_source) { + if (origin_node.is_group_input_node()) { + /* Found a group input that the group output depends on. */ + linked_input_indices.append_non_duplicates(origin_socket->index()); + } + else { + /* Found a field source that is not the group input. So the output is always a field. */ + return OutputFieldDependency::ForFieldSource(); + } + } + else if (!origin_state.is_single) { + const FieldInferencingInterface inferencing_interface = + get_node_field_inferencing_interface(origin_node); + const OutputFieldDependency &field_dependency = + inferencing_interface.outputs[origin_socket->index()]; + + /* Propagate search further to the left. */ + for (const InputSocketRef *origin_input_socket : + gather_input_socket_dependencies(field_dependency, origin_node)) { + if (!field_state_by_socket_id[origin_input_socket->id()].is_single) { + if (handled_sockets.add(origin_input_socket)) { + sockets_to_check.push(origin_input_socket); + } + } + } + } + } + } + return OutputFieldDependency::ForPartiallyDependentField(std::move(linked_input_indices)); +} + +static void propagate_data_requirements_from_right_to_left( + const NodeTreeRef &tree, const MutableSpan field_state_by_socket_id) +{ + const Vector sorted_nodes = tree.toposort( + NodeTreeRef::ToposortDirection::RightToLeft); + + for (const NodeRef *node : sorted_nodes) { + const FieldInferencingInterface inferencing_interface = get_node_field_inferencing_interface( + *node); + + for (const OutputSocketRef *output_socket : node->outputs()) { + SocketFieldState &state = field_state_by_socket_id[output_socket->id()]; + + const OutputFieldDependency &field_dependency = + inferencing_interface.outputs[output_socket->index()]; + + if (field_dependency.field_type() == OutputSocketFieldType::FieldSource) { + continue; + } + if (field_dependency.field_type() == OutputSocketFieldType::None) { + state.requires_single = true; + continue; + } + + /* The output is required to be a single value when it is connected to any input that does + * not support fields. */ + for (const InputSocketRef *target_socket : output_socket->directly_linked_sockets()) { + state.requires_single |= field_state_by_socket_id[target_socket->id()].requires_single; + } + + if (state.requires_single) { + bool any_input_is_field_implicitly = false; + const Vector connected_inputs = gather_input_socket_dependencies( + field_dependency, *node); + for (const InputSocketRef *input_socket : connected_inputs) { + if (inferencing_interface.inputs[input_socket->index()] == + InputSocketFieldType::Implicit) { + if (!input_socket->is_logically_linked()) { + any_input_is_field_implicitly = true; + break; + } + } + } + if (any_input_is_field_implicitly) { + /* This output isn't a single value actually. */ + state.requires_single = false; + } + else { + /* If the output is required to be a single value, the connected inputs in the same node + * must not be fields as well. */ + for (const InputSocketRef *input_socket : connected_inputs) { + field_state_by_socket_id[input_socket->id()].requires_single = true; + } + } + } + } + + /* Some inputs do not require fields independent of what the outputs are connected to. */ + for (const InputSocketRef *input_socket : node->inputs()) { + SocketFieldState &state = field_state_by_socket_id[input_socket->id()]; + if (inferencing_interface.inputs[input_socket->index()] == InputSocketFieldType::None) { + state.requires_single = true; + } + } + } +} + +static void determine_group_input_states( + const NodeTreeRef &tree, + FieldInferencingInterface &new_inferencing_interface, + const MutableSpan field_state_by_socket_id) +{ + { + /* Non-field inputs never support fields. */ + int index; + LISTBASE_FOREACH_INDEX (bNodeSocket *, group_input, &tree.btree()->inputs, index) { + if (!is_field_socket_type((eNodeSocketDatatype)group_input->type)) { + new_inferencing_interface.inputs[index] = InputSocketFieldType::None; + } + } + } + /* Check if group inputs are required to be single values, because they are (indirectly) + * connected to some socket that does not support fields. */ + for (const NodeRef *node : tree.nodes_by_type("NodeGroupInput")) { + for (const OutputSocketRef *output_socket : node->outputs().drop_back(1)) { + SocketFieldState &state = field_state_by_socket_id[output_socket->id()]; + if (state.requires_single) { + new_inferencing_interface.inputs[output_socket->index()] = InputSocketFieldType::None; + } + } + } + /* If an input does not support fields, this should be reflected in all Group Input nodes. */ + for (const NodeRef *node : tree.nodes_by_type("NodeGroupInput")) { + for (const OutputSocketRef *output_socket : node->outputs().drop_back(1)) { + SocketFieldState &state = field_state_by_socket_id[output_socket->id()]; + const bool supports_field = new_inferencing_interface.inputs[output_socket->index()] != + InputSocketFieldType::None; + if (supports_field) { + state.is_single = false; + state.is_field_source = true; + } + else { + state.requires_single = true; + } + } + SocketFieldState &dummy_socket_state = field_state_by_socket_id[node->outputs().last()->id()]; + dummy_socket_state.requires_single = true; + } +} + +static void propagate_field_status_from_left_to_right( + const NodeTreeRef &tree, const MutableSpan field_state_by_socket_id) +{ + Vector sorted_nodes = tree.toposort( + NodeTreeRef::ToposortDirection::LeftToRight); + + for (const NodeRef *node : sorted_nodes) { + if (node->is_group_input_node()) { + continue; + } + + const FieldInferencingInterface inferencing_interface = get_node_field_inferencing_interface( + *node); + + /* Update field state of input sockets, also taking into account linked origin sockets. */ + for (const InputSocketRef *input_socket : node->inputs()) { + SocketFieldState &state = field_state_by_socket_id[input_socket->id()]; + if (state.requires_single) { + state.is_single = true; + continue; + } + state.is_single = true; + if (input_socket->logically_linked_sockets().is_empty()) { + if (inferencing_interface.inputs[input_socket->index()] == + InputSocketFieldType::Implicit) { + state.is_single = false; + } + } + else { + for (const OutputSocketRef *origin_socket : input_socket->logically_linked_sockets()) { + if (!field_state_by_socket_id[origin_socket->id()].is_single) { + state.is_single = false; + break; + } + } + } + } + + /* Update field state of output sockets, also taking into account input sockets. */ + for (const OutputSocketRef *output_socket : node->outputs()) { + SocketFieldState &state = field_state_by_socket_id[output_socket->id()]; + const OutputFieldDependency &field_dependency = + inferencing_interface.outputs[output_socket->index()]; + + switch (field_dependency.field_type()) { + case OutputSocketFieldType::None: { + state.is_single = true; + break; + } + case OutputSocketFieldType::FieldSource: { + state.is_single = false; + state.is_field_source = true; + break; + } + case OutputSocketFieldType::PartiallyDependent: + case OutputSocketFieldType::DependentField: { + for (const InputSocketRef *input_socket : + gather_input_socket_dependencies(field_dependency, *node)) { + if (!field_state_by_socket_id[input_socket->id()].is_single) { + state.is_single = false; + break; + } + } + break; + } + } + } + } +} + +static void determine_group_output_states(const NodeTreeRef &tree, + FieldInferencingInterface &new_inferencing_interface, + const Span field_state_by_socket_id) +{ + for (const NodeRef *group_output_node : tree.nodes_by_type("NodeGroupOutput")) { + /* Ignore inactive group output nodes. */ + if (!(group_output_node->bnode()->flag & NODE_DO_OUTPUT)) { + continue; + } + /* Determine dependencies of all group outputs. */ + for (const InputSocketRef *group_output_socket : group_output_node->inputs().drop_back(1)) { + OutputFieldDependency field_dependency = find_group_output_dependencies( + *group_output_socket, field_state_by_socket_id); + new_inferencing_interface.outputs[group_output_socket->index()] = std::move( + field_dependency); + } + break; + } +} + +static void update_socket_shapes(const NodeTreeRef &tree, + const Span field_state_by_socket_id) +{ + const eNodeSocketDisplayShape requires_data_shape = SOCK_DISPLAY_SHAPE_CIRCLE; + const eNodeSocketDisplayShape data_but_can_be_field_shape = SOCK_DISPLAY_SHAPE_DIAMOND_DOT; + const eNodeSocketDisplayShape is_field_shape = SOCK_DISPLAY_SHAPE_DIAMOND; + + for (const InputSocketRef *socket : tree.input_sockets()) { + bNodeSocket *bsocket = socket->bsocket(); + const SocketFieldState &state = field_state_by_socket_id[socket->id()]; + if (state.requires_single) { + bsocket->display_shape = requires_data_shape; + } + else if (state.is_single) { + bsocket->display_shape = data_but_can_be_field_shape; + } + else { + bsocket->display_shape = is_field_shape; + } + } + for (const OutputSocketRef *socket : tree.output_sockets()) { + bNodeSocket *bsocket = socket->bsocket(); + const SocketFieldState &state = field_state_by_socket_id[socket->id()]; + if (state.requires_single) { + bsocket->display_shape = requires_data_shape; + } + else if (state.is_single) { + bsocket->display_shape = data_but_can_be_field_shape; + } + else { + bsocket->display_shape = is_field_shape; + } + } +} + +static bool update_field_inferencing(bNodeTree &btree) +{ + using namespace blender::nodes; + if (btree.type != NTREE_GEOMETRY) { + return false; + } + + /* Create new inferencing interface for this node group. */ + FieldInferencingInterface *new_inferencing_interface = new FieldInferencingInterface(); + new_inferencing_interface->inputs.resize(BLI_listbase_count(&btree.inputs), + InputSocketFieldType::IsSupported); + new_inferencing_interface->outputs.resize(BLI_listbase_count(&btree.outputs), + OutputFieldDependency::ForDataSource()); + + /* Create #NodeTreeRef to accelerate various queries on the node tree (e.g. linked sockets). */ + const NodeTreeRef tree{&btree}; + + /* Keep track of the state of all sockets. The index into this array is #SocketRef::id(). */ + Array field_state_by_socket_id(tree.sockets().size()); + + propagate_data_requirements_from_right_to_left(tree, field_state_by_socket_id); + determine_group_input_states(tree, *new_inferencing_interface, field_state_by_socket_id); + propagate_field_status_from_left_to_right(tree, field_state_by_socket_id); + determine_group_output_states(tree, *new_inferencing_interface, field_state_by_socket_id); + update_socket_shapes(tree, field_state_by_socket_id); + + /* Update the previous group interface. */ + const bool group_interface_changed = btree.field_inferencing_interface == nullptr || + *btree.field_inferencing_interface != + *new_inferencing_interface; + delete btree.field_inferencing_interface; + btree.field_inferencing_interface = new_inferencing_interface; + + return group_interface_changed; +} + +} // namespace blender::bke::node_field_inferencing + +/** + * \param tree_update_flag: #eNodeTreeUpdate enum. + */ +void ntreeUpdateAllUsers(Main *main, ID *id, const int tree_update_flag) { if (id == nullptr) { return; @@ -4446,7 +4988,8 @@ void ntreeUpdateAllUsers(Main *main, ID *id) } if (need_update) { - ntreeUpdateTree(nullptr, ntree); + ntree->update |= tree_update_flag; + ntreeUpdateTree(tree_update_flag ? main : nullptr, ntree); } } FOREACH_NODETREE_END; @@ -4508,8 +5051,18 @@ void ntreeUpdateTree(Main *bmain, bNodeTree *ntree) ntreeInterfaceTypeUpdate(ntree); } + int tree_user_update_flag = 0; + + if (ntree->update & NTREE_UPDATE) { + /* If the field interface of this node tree has changed, all node trees using + * this group will need to recalculate their interface as well. */ + if (blender::bke::node_field_inferencing::update_field_inferencing(*ntree)) { + tree_user_update_flag |= NTREE_UPDATE_FIELD_INFERENCING; + } + } + if (bmain) { - ntreeUpdateAllUsers(bmain, &ntree->id); + ntreeUpdateAllUsers(bmain, &ntree->id, tree_user_update_flag); } if (ntree->update & (NTREE_UPDATE_LINKS | NTREE_UPDATE_NODES)) { diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc index 62f40152416..b5746dc772a 100644 --- a/source/blender/editors/space_node/drawnode.cc +++ b/source/blender/editors/space_node/drawnode.cc @@ -4282,6 +4282,13 @@ void node_draw_link(View2D *v2d, SpaceNode *snode, bNodeLink *link) // th_col3 = -1; /* no shadow */ } } + /* Links from field to non-field sockets are not allowed. */ + if (snode->edittree->type == NTREE_GEOMETRY && !(link->flag & NODE_LINK_DRAGGED)) { + if ((link->fromsock && link->fromsock->display_shape == SOCK_DISPLAY_SHAPE_DIAMOND) && + (link->tosock && link->tosock->display_shape == SOCK_DISPLAY_SHAPE_CIRCLE)) { + th_col1 = th_col2 = th_col3 = TH_REDALERT; + } + } node_draw_link_bezier(v2d, snode, link, th_col1, th_col2, th_col3); } diff --git a/source/blender/editors/space_node/node_relationships.cc b/source/blender/editors/space_node/node_relationships.cc index 7d95659e403..b69e7e98bca 100644 --- a/source/blender/editors/space_node/node_relationships.cc +++ b/source/blender/editors/space_node/node_relationships.cc @@ -220,6 +220,7 @@ static LinkData *create_drag_link(Main *bmain, SpaceNode *snode, bNode *node, bN if (node_connected_to_output(bmain, snode->edittree, node)) { oplink->flag |= NODE_LINK_TEST; } + oplink->flag |= NODE_LINK_DRAGGED; return linkdata; } @@ -894,6 +895,8 @@ static void node_link_exit(bContext *C, wmOperator *op, bool apply_links) */ do_tag_update |= (link->flag & NODE_LINK_TEST) != 0; + link->flag &= ~NODE_LINK_DRAGGED; + if (apply_links && link->tosock && link->fromsock) { /* before actually adding the link, * let nodes perform special link insertion handling @@ -1097,6 +1100,7 @@ static bNodeLinkDrag *node_link_init(Main *bmain, SpaceNode *snode, float cursor *oplink = *link; oplink->next = oplink->prev = nullptr; oplink->flag |= NODE_LINK_VALID; + oplink->flag |= NODE_LINK_DRAGGED; /* The link could be disconnected and in that case we * wouldn't be able to check whether tag update is @@ -1150,6 +1154,7 @@ static bNodeLinkDrag *node_link_init(Main *bmain, SpaceNode *snode, float cursor *oplink = *link_to_pick; oplink->next = oplink->prev = nullptr; oplink->flag |= NODE_LINK_VALID; + oplink->flag |= NODE_LINK_DRAGGED; oplink->flag &= ~NODE_LINK_TEST; if (node_connected_to_output(bmain, snode->edittree, link_to_pick->tonode)) { oplink->flag |= NODE_LINK_TEST; diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 18545666796..a52816ecf57 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -445,6 +445,7 @@ typedef struct bNodeLink { #define NODE_LINK_TEST (1 << 2) /* free test flag, undefined */ #define NODE_LINK_TEMP_HIGHLIGHT (1 << 3) /* Link is highlighted for picking. */ #define NODE_LINK_MUTED (1 << 4) /* Link is muted. */ +#define NODE_LINK_DRAGGED (1 << 5) /* Node link is being dragged by the user. */ /* tree->edit_quality/tree->render_quality */ #define NTREE_QUALITY_HIGH 0 @@ -459,6 +460,8 @@ typedef struct bNodeLink { #define NTREE_CHUNKSIZE_512 512 #define NTREE_CHUNKSIZE_1024 1024 +struct FieldInferencingInterface; + /* the basis for a Node tree, all links and nodes reside internal here */ /* only re-usable node trees are in the library though, * materials and textures allocate own tree struct */ @@ -481,6 +484,8 @@ typedef struct bNodeTree { float view_center[2]; ListBase nodes, links; + /** Information about how inputs and outputs of the node group interact with fields. */ + struct FieldInferencingInterface *field_inferencing_interface; /** Set init on fileread. */ int type, init; @@ -575,6 +580,9 @@ typedef enum eNodeTreeUpdate { NTREE_UPDATE_NODES = (1 << 1), /* nodes or sockets have been added or removed */ NTREE_UPDATE_GROUP_IN = (1 << 4), /* group inputs have changed */ NTREE_UPDATE_GROUP_OUT = (1 << 5), /* group outputs have changed */ + /* The field interface has changed. So e.g. an output that was always a field before is not + * anymore. This implies that the field type inferencing has to be done again. */ + NTREE_UPDATE_FIELD_INFERENCING = (1 << 6), /* group has changed (generic flag including all other group flags) */ NTREE_UPDATE_GROUP = (NTREE_UPDATE_GROUP_IN | NTREE_UPDATE_GROUP_OUT), } eNodeTreeUpdate; diff --git a/source/blender/nodes/NOD_node_declaration.hh b/source/blender/nodes/NOD_node_declaration.hh index 8e9e72bf4c8..32e63ffb2df 100644 --- a/source/blender/nodes/NOD_node_declaration.hh +++ b/source/blender/nodes/NOD_node_declaration.hh @@ -27,6 +27,91 @@ namespace blender::nodes { class NodeDeclarationBuilder; +enum class InputSocketFieldType { + /** The input is required to be a single value. */ + None, + /** The input can be a field. */ + IsSupported, + /** The input can be a field and is a field implicitly if nothing is connected. */ + Implicit, +}; + +enum class OutputSocketFieldType { + /** The output is always a single value. */ + None, + /** The output is always a field, independent of the inputs. */ + FieldSource, + /** If any input is a field, this output will be a field as well. */ + DependentField, + /** If any of a subset of inputs is a field, this out will be a field as well. + * The subset is defined by the vector of indices. */ + PartiallyDependent, +}; + +/** + * Contains information about how a node output's field state depends on inputs of the same node. + */ +class OutputFieldDependency { + private: + OutputSocketFieldType type_ = OutputSocketFieldType::None; + Vector linked_input_indices_; + + public: + static OutputFieldDependency ForFieldSource() + { + OutputFieldDependency field_dependency; + field_dependency.type_ = OutputSocketFieldType::FieldSource; + return field_dependency; + } + + static OutputFieldDependency ForDataSource() + { + OutputFieldDependency field_dependency; + field_dependency.type_ = OutputSocketFieldType::None; + return field_dependency; + } + + static OutputFieldDependency ForPartiallyDependentField(Vector 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; + } + + static OutputFieldDependency ForDependentField() + { + OutputFieldDependency field_dependency; + field_dependency.type_ = OutputSocketFieldType::DependentField; + return field_dependency; + } + + OutputSocketFieldType field_type() const + { + return type_; + } + + Span linked_input_indices() const + { + return linked_input_indices_; + } + + friend bool operator==(const OutputFieldDependency &a, const OutputFieldDependency &b) + { + return a.type_ == b.type_ && a.linked_input_indices_ == b.linked_input_indices_; + } + + friend bool operator!=(const OutputFieldDependency &a, const OutputFieldDependency &b) + { + return !(a == b); + } +}; + /** * Describes a single input or output socket. This is subclassed for different socket types. */ @@ -39,6 +124,9 @@ class SocketDeclaration { bool is_multi_input_ = false; bool no_mute_links_ = false; + InputSocketFieldType input_field_type_ = InputSocketFieldType::None; + OutputFieldDependency output_field_dependency_; + friend NodeDeclarationBuilder; template friend class SocketDeclarationBuilder; @@ -52,6 +140,9 @@ class SocketDeclaration { StringRefNull name() const; StringRefNull identifier() const; + InputSocketFieldType input_field_type() const; + const OutputFieldDependency &output_field_dependency() const; + protected: void set_common_flags(bNodeSocket &socket) const; bool matches_common_data(const bNodeSocket &socket) const; @@ -100,6 +191,41 @@ class SocketDeclarationBuilder : public BaseSocketDeclarationBuilder { decl_->no_mute_links_ = value; return *(Self *)this; } + + /** The input socket allows passing in a field. */ + Self &supports_field() + { + decl_->input_field_type_ = InputSocketFieldType::IsSupported; + return *(Self *)this; + } + + /** The input supports a field and is a field by default when nothing is connected. */ + Self &implicit_field() + { + decl_->input_field_type_ = InputSocketFieldType::Implicit; + return *(Self *)this; + } + + /** The output is always a field, regardless of any inputs. */ + Self &field_source() + { + decl_->output_field_dependency_ = OutputFieldDependency::ForFieldSource(); + return *(Self *)this; + } + + /** The output is a field if any of the inputs is a field. */ + Self &dependent_field() + { + decl_->output_field_dependency_ = OutputFieldDependency::ForDependentField(); + return *(Self *)this; + } + + /** The output is a field if any of the inputs with indices in the given list is a field. */ + Self &dependent_field(Vector input_dependencies) + { + decl_->output_field_dependency_ = OutputFieldDependency::ForPartiallyDependentField( + std::move(input_dependencies)); + } }; using SocketDeclarationPtr = std::unique_ptr; @@ -108,6 +234,7 @@ class NodeDeclaration { private: Vector inputs_; Vector outputs_; + bool is_function_node_ = false; friend NodeDeclarationBuilder; @@ -118,6 +245,11 @@ class NodeDeclaration { Span inputs() const; Span outputs() const; + bool is_function_node() const + { + return is_function_node_; + } + MEM_CXX_CLASS_ALLOC_FUNCS("NodeDeclaration") }; @@ -129,6 +261,15 @@ class NodeDeclarationBuilder { public: NodeDeclarationBuilder(NodeDeclaration &declaration); + /** + * All inputs support fields, and all outputs are fields if any of the inputs is a field. + * Calling field status definitions on each socket is unnecessary. + */ + void is_function_node(bool value = true) + { + declaration_.is_function_node_ = value; + } + template typename DeclType::Builder &add_input(StringRef name, StringRef identifier = ""); template @@ -155,6 +296,16 @@ inline StringRefNull SocketDeclaration::identifier() const return identifier_; } +inline InputSocketFieldType SocketDeclaration::input_field_type() const +{ + return input_field_type_; +} + +inline const OutputFieldDependency &SocketDeclaration::output_field_dependency() const +{ + return output_field_dependency_; +} + /* -------------------------------------------------------------------- * NodeDeclarationBuilder inline methods. */ diff --git a/source/blender/nodes/NOD_node_tree_ref.hh b/source/blender/nodes/NOD_node_tree_ref.hh index 4f2565cbbaf..1da42fb6425 100644 --- a/source/blender/nodes/NOD_node_tree_ref.hh +++ b/source/blender/nodes/NOD_node_tree_ref.hh @@ -182,6 +182,7 @@ class NodeRef : NonCopyable, NonMovable { Span inputs() const; Span outputs() const; Span internal_links() const; + Span sockets(eNodeSocketInOut in_out) const; const InputSocketRef &input(int index) const; const OutputSocketRef &output(int index) const; @@ -189,6 +190,10 @@ class NodeRef : NonCopyable, NonMovable { const InputSocketRef &input_by_identifier(StringRef identifier) const; const OutputSocketRef &output_by_identifier(StringRef identifier) const; + bool any_input_is_directly_linked() const; + bool any_output_is_directly_linked() const; + bool any_socket_is_directly_linked(eNodeSocketInOut in_out) const; + bNode *bnode() const; bNodeTree *btree() const; @@ -196,6 +201,7 @@ class NodeRef : NonCopyable, NonMovable { StringRefNull idname() const; StringRefNull name() const; bNodeType *typeinfo() const; + const NodeDeclaration *declaration() const; int id() const; @@ -272,6 +278,13 @@ class NodeTreeRef : NonCopyable, NonMovable { bool has_link_cycles() const; bool has_undefined_nodes_or_sockets() const; + enum class ToposortDirection { + LeftToRight, + RightToLeft, + }; + + Vector toposort(ToposortDirection direction) const; + bNodeTree *btree() const; StringRefNull name() const; @@ -496,6 +509,12 @@ inline Span NodeRef::outputs() const return outputs_; } +inline Span NodeRef::sockets(const eNodeSocketInOut in_out) const +{ + return in_out == SOCK_IN ? inputs_.as_span().cast() : + outputs_.as_span().cast(); +} + inline Span NodeRef::internal_links() const { return internal_links_; @@ -553,6 +572,13 @@ inline bNodeType *NodeRef::typeinfo() const return bnode_->typeinfo; } +/* Returns a pointer because not all nodes have declarations currently. */ +inline const NodeDeclaration *NodeRef::declaration() const +{ + nodeDeclarationEnsure(this->tree().btree(), bnode_); + return bnode_->declaration; +} + inline int NodeRef::id() const { return id_; diff --git a/source/blender/nodes/function/nodes/node_fn_boolean_math.cc b/source/blender/nodes/function/nodes/node_fn_boolean_math.cc index b71ee092de6..d10490bb2ee 100644 --- a/source/blender/nodes/function/nodes/node_fn_boolean_math.cc +++ b/source/blender/nodes/function/nodes/node_fn_boolean_math.cc @@ -28,6 +28,7 @@ namespace blender::nodes { static void fn_node_boolean_math_declare(NodeDeclarationBuilder &b) { + b.is_function_node(); b.add_input("Boolean", "Boolean"); b.add_input("Boolean", "Boolean_001"); b.add_output("Boolean"); diff --git a/source/blender/nodes/function/nodes/node_fn_float_compare.cc b/source/blender/nodes/function/nodes/node_fn_float_compare.cc index 4f4830afabc..9736c52e895 100644 --- a/source/blender/nodes/function/nodes/node_fn_float_compare.cc +++ b/source/blender/nodes/function/nodes/node_fn_float_compare.cc @@ -30,6 +30,7 @@ namespace blender::nodes { static void fn_node_float_compare_declare(NodeDeclarationBuilder &b) { + b.is_function_node(); b.add_input("A").min(-10000.0f).max(10000.0f); b.add_input("B").min(-10000.0f).max(10000.0f); b.add_input("Epsilon").default_value(0.001f).min(-10000.0f).max(10000.0f); diff --git a/source/blender/nodes/function/nodes/node_fn_float_to_int.cc b/source/blender/nodes/function/nodes/node_fn_float_to_int.cc index e59c78d2c04..8bb5dafff8a 100644 --- a/source/blender/nodes/function/nodes/node_fn_float_to_int.cc +++ b/source/blender/nodes/function/nodes/node_fn_float_to_int.cc @@ -29,6 +29,7 @@ namespace blender::nodes { static void fn_node_float_to_int_declare(NodeDeclarationBuilder &b) { + b.is_function_node(); b.add_input("Float"); b.add_output("Integer"); }; diff --git a/source/blender/nodes/function/nodes/node_fn_input_string.cc b/source/blender/nodes/function/nodes/node_fn_input_string.cc index 4a8e898fb9b..704ae9d900c 100644 --- a/source/blender/nodes/function/nodes/node_fn_input_string.cc +++ b/source/blender/nodes/function/nodes/node_fn_input_string.cc @@ -23,6 +23,7 @@ namespace blender::nodes { static void fn_node_input_string_declare(NodeDeclarationBuilder &b) { + b.is_function_node(); b.add_output("String"); }; diff --git a/source/blender/nodes/function/nodes/node_fn_input_vector.cc b/source/blender/nodes/function/nodes/node_fn_input_vector.cc index 9548df7b423..387689b3d1e 100644 --- a/source/blender/nodes/function/nodes/node_fn_input_vector.cc +++ b/source/blender/nodes/function/nodes/node_fn_input_vector.cc @@ -25,6 +25,7 @@ namespace blender::nodes { static void fn_node_input_vector_declare(NodeDeclarationBuilder &b) { + b.is_function_node(); b.add_output("Vector"); }; diff --git a/source/blender/nodes/function/nodes/node_fn_random_float.cc b/source/blender/nodes/function/nodes/node_fn_random_float.cc index 1bd39aacdca..20f5fa87685 100644 --- a/source/blender/nodes/function/nodes/node_fn_random_float.cc +++ b/source/blender/nodes/function/nodes/node_fn_random_float.cc @@ -22,6 +22,7 @@ namespace blender::nodes { static void fn_node_random_float_declare(NodeDeclarationBuilder &b) { + b.is_function_node(); b.add_input("Min").min(-10000.0f).max(10000.0f); b.add_input("Max").default_value(1.0f).min(-10000.0f).max(10000.0f); b.add_input("Seed").min(-10000).max(10000); diff --git a/source/blender/nodes/function/nodes/node_fn_string_length.cc b/source/blender/nodes/function/nodes/node_fn_string_length.cc index a0f85dfd2bf..89038629c3c 100644 --- a/source/blender/nodes/function/nodes/node_fn_string_length.cc +++ b/source/blender/nodes/function/nodes/node_fn_string_length.cc @@ -24,6 +24,7 @@ namespace blender::nodes { static void fn_node_string_length_declare(NodeDeclarationBuilder &b) { + b.is_function_node(); b.add_input("String"); b.add_output("Length"); }; diff --git a/source/blender/nodes/function/nodes/node_fn_string_substring.cc b/source/blender/nodes/function/nodes/node_fn_string_substring.cc index 55a01093ae9..b91171923d6 100644 --- a/source/blender/nodes/function/nodes/node_fn_string_substring.cc +++ b/source/blender/nodes/function/nodes/node_fn_string_substring.cc @@ -22,6 +22,7 @@ namespace blender::nodes { static void fn_node_string_substring_declare(NodeDeclarationBuilder &b) { + b.is_function_node(); b.add_input("String"); b.add_input("Position"); b.add_input("Length").min(0); diff --git a/source/blender/nodes/function/nodes/node_fn_value_to_string.cc b/source/blender/nodes/function/nodes/node_fn_value_to_string.cc index c1e6373cb6d..56206af2eb2 100644 --- a/source/blender/nodes/function/nodes/node_fn_value_to_string.cc +++ b/source/blender/nodes/function/nodes/node_fn_value_to_string.cc @@ -21,6 +21,7 @@ namespace blender::nodes { static void fn_node_value_to_string_declare(NodeDeclarationBuilder &b) { + b.is_function_node(); b.add_input("Value"); b.add_input("Decimals").min(0); b.add_output("String"); diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc index c8a33205de4..81629a0f8b4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc @@ -26,18 +26,18 @@ namespace blender::nodes { static void geo_node_attribute_capture_declare(NodeDeclarationBuilder &b) { b.add_input("Geometry"); - b.add_input("Value"); - b.add_input("Value", "Value_001"); - b.add_input("Value", "Value_002"); - b.add_input("Value", "Value_003"); - b.add_input("Value", "Value_004"); + b.add_input("Value").supports_field(); + b.add_input("Value", "Value_001").supports_field(); + b.add_input("Value", "Value_002").supports_field(); + b.add_input("Value", "Value_003").supports_field(); + b.add_input("Value", "Value_004").supports_field(); b.add_output("Geometry"); - b.add_output("Attribute"); - b.add_output("Attribute", "Attribute_001"); - b.add_output("Attribute", "Attribute_002"); - b.add_output("Attribute", "Attribute_003"); - b.add_output("Attribute", "Attribute_004"); + b.add_output("Attribute").field_source(); + b.add_output("Attribute", "Attribute_001").field_source(); + b.add_output("Attribute", "Attribute_002").field_source(); + b.add_output("Attribute", "Attribute_003").field_source(); + b.add_output("Attribute", "Attribute_004").field_source(); } static void geo_node_attribute_capture_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc index 5001034518c..1b7d2fe28a1 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc @@ -29,8 +29,8 @@ namespace blender::nodes { static void geo_node_attribute_statistic_declare(NodeDeclarationBuilder &b) { b.add_input("Geometry"); - b.add_input("Attribute").hide_value(); - b.add_input("Attribute", "Attribute_001").hide_value(); + b.add_input("Attribute").hide_value().supports_field(); + b.add_input("Attribute", "Attribute_001").hide_value().supports_field(); b.add_output("Mean"); b.add_output("Median"); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_parameter.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_parameter.cc index 2cde198e679..90853387ec7 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_parameter.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_parameter.cc @@ -24,7 +24,7 @@ namespace blender::nodes { static void geo_node_curve_parameter_declare(NodeDeclarationBuilder &b) { - b.add_output("Factor"); + b.add_output("Factor").field_source(); } /** diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc index ac0cd510ffa..1266f525861 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc @@ -28,12 +28,12 @@ namespace blender::nodes { static void geo_node_curve_sample_declare(NodeDeclarationBuilder &b) { b.add_input("Curve"); - b.add_input("Factor").min(0.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_input("Length").min(0.0f).subtype(PROP_DISTANCE); + b.add_input("Factor").min(0.0f).max(1.0f).subtype(PROP_FACTOR).supports_field(); + b.add_input("Length").min(0.0f).subtype(PROP_DISTANCE).supports_field(); - b.add_output("Position"); - b.add_output("Tangent"); - b.add_output("Normal"); + b.add_output("Position").dependent_field(); + b.add_output("Tangent").dependent_field(); + b.add_output("Normal").dependent_field(); } static void geo_node_curve_sample_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_index.cc b/source/blender/nodes/geometry/nodes/node_geo_input_index.cc index c52ff3d448e..f18b0ab090a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_index.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_index.cc @@ -20,7 +20,7 @@ namespace blender::nodes { static void geo_node_input_index_declare(NodeDeclarationBuilder &b) { - b.add_output("Index"); + b.add_output("Index").field_source(); } class IndexFieldInput final : public fn::FieldInput { diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc b/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc index f92086acdf0..5a2495afb9e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc @@ -28,7 +28,7 @@ namespace blender::nodes { static void geo_node_input_normal_declare(NodeDeclarationBuilder &b) { - b.add_output("Normal"); + b.add_output("Normal").field_source(); } static GVArrayPtr mesh_face_normals(const Mesh &mesh, diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_position.cc b/source/blender/nodes/geometry/nodes/node_geo_input_position.cc index 3f3457a3acb..f439fb32d31 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_position.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_position.cc @@ -20,7 +20,7 @@ namespace blender::nodes { static void geo_node_input_position_declare(NodeDeclarationBuilder &b) { - b.add_output("Position"); + b.add_output("Position").field_source(); } static void geo_node_input_position_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc b/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc index 68788709f1e..d690642373a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc @@ -24,7 +24,7 @@ namespace blender::nodes { static void geo_node_input_tangent_declare(NodeDeclarationBuilder &b) { - b.add_output("Tangent"); + b.add_output("Tangent").field_source(); } static void calculate_bezier_tangents(const BezierSpline &spline, MutableSpan tangents) diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc b/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc index 43818947272..2a4481d5404 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc @@ -30,7 +30,7 @@ static void geo_node_material_assign_declare(NodeDeclarationBuilder &b) { b.add_input("Geometry"); b.add_input("Material").hide_label(); - b.add_input("Selection").default_value(true).hide_value(); + b.add_input("Selection").default_value(true).hide_value().supports_field(); b.add_output("Geometry"); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc index 22c24e34314..337bd88c6e6 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc @@ -31,7 +31,7 @@ namespace blender::nodes { static void geo_node_material_selection_declare(NodeDeclarationBuilder &b) { b.add_input("Material").hide_label(true); - b.add_output("Selection"); + b.add_output("Selection").field_source(); } static void select_mesh_by_material(const Mesh &mesh, diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc index c5e10b788ac..78bdac1b01b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc @@ -23,8 +23,8 @@ namespace blender::nodes { static void geo_node_set_position_declare(NodeDeclarationBuilder &b) { b.add_input("Geometry"); - b.add_input("Position").hide_value(); - b.add_input("Selection").default_value(true).hide_value(); + b.add_input("Position").hide_value().implicit_field(); + b.add_input("Selection").default_value(true).hide_value().supports_field(); b.add_output("Geometry"); } diff --git a/source/blender/nodes/intern/node_tree_ref.cc b/source/blender/nodes/intern/node_tree_ref.cc index 641d02af902..d299f062fab 100644 --- a/source/blender/nodes/intern/node_tree_ref.cc +++ b/source/blender/nodes/intern/node_tree_ref.cc @@ -19,6 +19,7 @@ #include "NOD_node_tree_ref.hh" #include "BLI_dot_export.hh" +#include "BLI_stack.hh" namespace blender::nodes { @@ -473,6 +474,108 @@ bool NodeTreeRef::has_undefined_nodes_or_sockets() const return false; } +bool NodeRef::any_input_is_directly_linked() const +{ + for (const SocketRef *socket : inputs_) { + if (!socket->directly_linked_sockets().is_empty()) { + return true; + } + } + return false; +} + +bool NodeRef::any_output_is_directly_linked() const +{ + for (const SocketRef *socket : outputs_) { + if (!socket->directly_linked_sockets().is_empty()) { + return true; + } + } + return false; +} + +bool NodeRef::any_socket_is_directly_linked(eNodeSocketInOut in_out) const +{ + if (in_out == SOCK_IN) { + return this->any_input_is_directly_linked(); + } + return this->any_output_is_directly_linked(); +} + +/** + * Sort nodes topologically from left to right or right to left. + * In the future the result if this could be cached on #NodeTreeRef. + */ +Vector NodeTreeRef::toposort(const ToposortDirection direction) const +{ + struct Item { + const NodeRef *node; + /* Index of the next socket that is checked in the depth-first search. */ + int socket_index = 0; + /* Link index in the next socket that is checked in the depth-first search. */ + int link_index = 0; + }; + + Vector toposort; + toposort.reserve(nodes_by_id_.size()); + Array node_is_done_by_id(nodes_by_id_.size(), false); + Stack nodes_to_check; + + for (const NodeRef *start_node : nodes_by_id_) { + if (node_is_done_by_id[start_node->id()]) { + /* Ignore nodes that are done already. */ + continue; + } + if (start_node->any_socket_is_directly_linked( + direction == ToposortDirection::LeftToRight ? SOCK_OUT : SOCK_IN)) { + /* Ignore non-start nodes. */ + continue; + } + + /* Do a depth-first search to sort nodes topologically. */ + nodes_to_check.push({start_node}); + while (!nodes_to_check.is_empty()) { + Item &item = nodes_to_check.peek(); + const NodeRef &node = *item.node; + const Span sockets = node.sockets( + direction == ToposortDirection::LeftToRight ? SOCK_IN : SOCK_OUT); + + while (true) { + if (item.socket_index == sockets.size()) { + /* All sockets have already been visited. */ + break; + } + const SocketRef &socket = *sockets[item.socket_index]; + const Span linked_sockets = socket.directly_linked_sockets(); + if (item.link_index == linked_sockets.size()) { + /* All linkes connected to this socket have already been visited. */ + item.socket_index++; + item.link_index = 0; + continue; + } + const SocketRef &linked_socket = *linked_sockets[item.link_index]; + const NodeRef &linked_node = linked_socket.node(); + if (node_is_done_by_id[linked_node.id()]) { + /* The linked node has already been visited. */ + item.link_index++; + continue; + } + nodes_to_check.push({&linked_node}); + break; + } + + /* If no other element has been pushed, the current node can be pushed to the sorted list. */ + if (&item == &nodes_to_check.peek()) { + node_is_done_by_id[node.id()] = true; + toposort.append(&node); + nodes_to_check.pop(); + } + } + } + + return toposort; +} + std::string NodeTreeRef::to_dot() const { dot::DirectedGraph digraph; diff --git a/source/blender/nodes/shader/nodes/node_shader_clamp.cc b/source/blender/nodes/shader/nodes/node_shader_clamp.cc index 31d8f8ef15c..e8d4239937f 100644 --- a/source/blender/nodes/shader/nodes/node_shader_clamp.cc +++ b/source/blender/nodes/shader/nodes/node_shader_clamp.cc @@ -27,6 +27,7 @@ namespace blender::nodes { static void sh_node_clamp_declare(NodeDeclarationBuilder &b) { + b.is_function_node(); b.add_input("Value").min(0.0f).max(1.0f).default_value(1.0f); b.add_input("Min").default_value(0.0f).min(-10000.0f).max(10000.0f); b.add_input("Max").default_value(1.0f).min(-10000.0f).max(10000.0f); diff --git a/source/blender/nodes/shader/nodes/node_shader_curves.cc b/source/blender/nodes/shader/nodes/node_shader_curves.cc index e4ada06133e..887cc84bb76 100644 --- a/source/blender/nodes/shader/nodes/node_shader_curves.cc +++ b/source/blender/nodes/shader/nodes/node_shader_curves.cc @@ -27,6 +27,7 @@ namespace blender::nodes { static void sh_node_curve_vec_declare(NodeDeclarationBuilder &b) { + b.is_function_node(); b.add_input("Fac").min(0.0f).max(1.0f).default_value(1.0f).subtype(PROP_FACTOR); b.add_input("Vector").min(-1.0f).max(1.0f); b.add_output("Vector"); diff --git a/source/blender/nodes/shader/nodes/node_shader_map_range.cc b/source/blender/nodes/shader/nodes/node_shader_map_range.cc index a5f9a24a728..5ea194ddc83 100644 --- a/source/blender/nodes/shader/nodes/node_shader_map_range.cc +++ b/source/blender/nodes/shader/nodes/node_shader_map_range.cc @@ -29,6 +29,7 @@ namespace blender::nodes { static void sh_node_map_range_declare(NodeDeclarationBuilder &b) { + b.is_function_node(); b.add_input("Value").min(-10000.0f).max(10000.0f).default_value(1.0f); b.add_input("From Min").min(-10000.0f).max(10000.0f); b.add_input("From Max").min(-10000.0f).max(10000.0f).default_value(1.0f); diff --git a/source/blender/nodes/shader/nodes/node_shader_math.cc b/source/blender/nodes/shader/nodes/node_shader_math.cc index 80a27b8e6a1..96d1be49c04 100644 --- a/source/blender/nodes/shader/nodes/node_shader_math.cc +++ b/source/blender/nodes/shader/nodes/node_shader_math.cc @@ -31,6 +31,7 @@ namespace blender::nodes { static void sh_node_math_declare(NodeDeclarationBuilder &b) { + b.is_function_node(); b.add_input("Value").default_value(0.5f).min(-10000.0f).max(10000.0f); b.add_input("Value", "Value_001").default_value(0.5f).min(-10000.0f).max(10000.0f); b.add_input("Value", "Value_002").default_value(0.5f).min(-10000.0f).max(10000.0f); diff --git a/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc b/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc index 860cc260d5d..d4d02e80ada 100644 --- a/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc +++ b/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc @@ -27,6 +27,7 @@ namespace blender::nodes { static void sh_node_mix_rgb_declare(NodeDeclarationBuilder &b) { + b.is_function_node(); b.add_input("Fac").default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); b.add_input("Color1").default_value({0.5f, 0.5f, 0.5f, 1.0f}); b.add_input("Color2").default_value({0.5f, 0.5f, 0.5f, 1.0f}); diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc b/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc index 63be399366f..eac81a077bc 100644 --- a/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc +++ b/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc @@ -27,6 +27,7 @@ namespace blender::nodes { static void sh_node_seprgb_declare(NodeDeclarationBuilder &b) { + b.is_function_node(); b.add_input("Image").default_value({0.8f, 0.8f, 0.8f, 1.0f}); b.add_output("R"); b.add_output("G"); diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc b/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc index b4b3c48482f..5a1cb3ecd52 100644 --- a/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc +++ b/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc @@ -27,6 +27,7 @@ namespace blender::nodes { static void sh_node_sepxyz_declare(NodeDeclarationBuilder &b) { + b.is_function_node(); b.add_input("Vector").min(-10000.0f).max(10000.0f); b.add_output("X"); b.add_output("Y"); diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc b/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc index d33d92f25fd..23f150d8135 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc @@ -23,6 +23,7 @@ namespace blender::nodes { static void sh_node_tex_musgrave_declare(NodeDeclarationBuilder &b) { + b.is_function_node(); b.add_input("Vector").hide_value(); b.add_input("W").min(-1000.0f).max(1000.0f); b.add_input("Scale").min(-1000.0f).max(1000.0f).default_value(5.0f); diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc b/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc index 1ae6b3a616c..5bf5e0f9876 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc @@ -25,7 +25,8 @@ namespace blender::nodes { static void sh_node_tex_noise_declare(NodeDeclarationBuilder &b) { - b.add_input("Vector").hide_value(); + b.is_function_node(); + b.add_input("Vector").hide_value().implicit_field(); b.add_input("W").min(-1000.0f).max(1000.0f); b.add_input("Scale").min(-1000.0f).max(1000.0f).default_value(5.0f); b.add_input("Detail").min(0.0f).max(16.0f).default_value(2.0f); diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc b/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc index cea7af247c1..e12e5724e8e 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc @@ -23,6 +23,7 @@ namespace blender::nodes { static void sh_node_tex_voronoi_declare(NodeDeclarationBuilder &b) { + b.is_function_node(); b.add_input("Vector").hide_value(); b.add_input("W").min(-1000.0f).max(1000.0f); b.add_input("Scale").min(-1000.0f).max(1000.0f).default_value(5.0f); diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc b/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc index bae16e10120..03543e5f7fe 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc @@ -23,6 +23,7 @@ namespace blender::nodes { static void sh_node_tex_white_noise_declare(NodeDeclarationBuilder &b) { + b.is_function_node(); b.add_input("Vector").min(-10000.0f).max(10000.0f); b.add_input("W").min(-10000.0f).max(10000.0f); b.add_output("Value"); diff --git a/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc b/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc index 1870caffbb1..d4d08be5d49 100644 --- a/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc +++ b/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc @@ -33,6 +33,7 @@ namespace blender::nodes { static void sh_node_valtorgb_declare(NodeDeclarationBuilder &b) { + b.is_function_node(); b.add_input("Fac").default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); b.add_output("Color"); b.add_output("Alpha"); diff --git a/source/blender/nodes/shader/nodes/node_shader_vector_math.cc b/source/blender/nodes/shader/nodes/node_shader_vector_math.cc index 5b24e8bb72d..f49ff06cef1 100644 --- a/source/blender/nodes/shader/nodes/node_shader_vector_math.cc +++ b/source/blender/nodes/shader/nodes/node_shader_vector_math.cc @@ -29,6 +29,7 @@ namespace blender::nodes { static void sh_node_vector_math_declare(NodeDeclarationBuilder &b) { + b.is_function_node(); b.add_input("Vector").min(-10000.0f).max(10000.0f); b.add_input("Vector", "Vector_001").min(-10000.0f).max(10000.0f); b.add_input("Vector", "Vector_002").min(-10000.0f).max(10000.0f); diff --git a/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc b/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc index e9fd6c4f31e..fecd203e80a 100644 --- a/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc +++ b/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc @@ -27,6 +27,7 @@ namespace blender::nodes { static void sh_node_vector_rotate_declare(NodeDeclarationBuilder &b) { + b.is_function_node(); b.add_input("Vector").min(0.0f).max(1.0f).hide_value(); b.add_input("Vector"); b.add_input("Axis").min(-1.0f).max(1.0f).default_value({0.0f, 0.0f, 1.0f});