Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender')
-rw-r--r--source/blender/blenkernel/BKE_node.h2
-rw-r--r--source/blender/blenkernel/intern/lib_remap.c2
-rw-r--r--source/blender/blenkernel/intern/node.cc559
-rw-r--r--source/blender/editors/space_node/drawnode.cc7
-rw-r--r--source/blender/editors/space_node/node_relationships.cc5
-rw-r--r--source/blender/makesdna/DNA_node_types.h8
-rw-r--r--source/blender/nodes/NOD_node_declaration.hh151
-rw-r--r--source/blender/nodes/NOD_node_tree_ref.hh26
-rw-r--r--source/blender/nodes/function/nodes/node_fn_boolean_math.cc1
-rw-r--r--source/blender/nodes/function/nodes/node_fn_float_compare.cc1
-rw-r--r--source/blender/nodes/function/nodes/node_fn_float_to_int.cc1
-rw-r--r--source/blender/nodes/function/nodes/node_fn_input_string.cc1
-rw-r--r--source/blender/nodes/function/nodes/node_fn_input_vector.cc1
-rw-r--r--source/blender/nodes/function/nodes/node_fn_random_float.cc1
-rw-r--r--source/blender/nodes/function/nodes/node_fn_string_length.cc1
-rw-r--r--source/blender/nodes/function/nodes/node_fn_string_substring.cc1
-rw-r--r--source/blender/nodes/function/nodes/node_fn_value_to_string.cc1
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc20
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_parameter.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc10
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_index.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_normal.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_position.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_material_assign.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_material_selection.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_position.cc4
-rw-r--r--source/blender/nodes/intern/node_tree_ref.cc103
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_clamp.cc1
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_curves.cc1
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_map_range.cc1
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_math.cc1
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_mixRgb.cc1
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc1
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc1
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc1
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_noise.cc3
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc1
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc1
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_valToRgb.cc1
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_vector_math.cc1
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc1
43 files changed, 908 insertions, 32 deletions
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<InputSocketFieldType> inputs;
+ Vector<OutputFieldDependency> 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<const InputSocketRef *> gather_input_socket_dependencies(
+ const OutputFieldDependency &field_dependency, const NodeRef &node)
+{
+ const OutputSocketFieldType type = field_dependency.field_type();
+ Vector<const InputSocketRef *> 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<SocketFieldState> 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<const InputSocketRef *> handled_sockets;
+ Stack<const InputSocketRef *> 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<int> 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<SocketFieldState> field_state_by_socket_id)
+{
+ const Vector<const NodeRef *> 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<const InputSocketRef *> 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<SocketFieldState> 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<SocketFieldState> field_state_by_socket_id)
+{
+ Vector<const NodeRef *> 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<SocketFieldState> 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<SocketFieldState> 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<SocketFieldState> 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<int> 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<int> indices)
+ {
+ OutputFieldDependency field_dependency;
+ if (indices.is_empty()) {
+ field_dependency.type_ = OutputSocketFieldType::None;
+ }
+ else {
+ field_dependency.type_ = OutputSocketFieldType::PartiallyDependent;
+ field_dependency.linked_input_indices_ = std::move(indices);
+ }
+ return field_dependency;
+ }
+
+ static OutputFieldDependency ForDependentField()
+ {
+ OutputFieldDependency field_dependency;
+ field_dependency.type_ = OutputSocketFieldType::DependentField;
+ return field_dependency;
+ }
+
+ OutputSocketFieldType field_type() const
+ {
+ return type_;
+ }
+
+ Span<int> 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<typename SocketDecl> 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<int> input_dependencies)
+ {
+ decl_->output_field_dependency_ = OutputFieldDependency::ForPartiallyDependentField(
+ std::move(input_dependencies));
+ }
};
using SocketDeclarationPtr = std::unique_ptr<SocketDeclaration>;
@@ -108,6 +234,7 @@ class NodeDeclaration {
private:
Vector<SocketDeclarationPtr> inputs_;
Vector<SocketDeclarationPtr> outputs_;
+ bool is_function_node_ = false;
friend NodeDeclarationBuilder;
@@ -118,6 +245,11 @@ class NodeDeclaration {
Span<SocketDeclarationPtr> inputs() const;
Span<SocketDeclarationPtr> 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>
typename DeclType::Builder &add_input(StringRef name, StringRef identifier = "");
template<typename DeclType>
@@ -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<const InputSocketRef *> inputs() const;
Span<const OutputSocketRef *> outputs() const;
Span<const InternalLinkRef *> internal_links() const;
+ Span<const SocketRef *> 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<const NodeRef *> toposort(ToposortDirection direction) const;
+
bNodeTree *btree() const;
StringRefNull name() const;
@@ -496,6 +509,12 @@ inline Span<const OutputSocketRef *> NodeRef::outputs() const
return outputs_;
}
+inline Span<const SocketRef *> NodeRef::sockets(const eNodeSocketInOut in_out) const
+{
+ return in_out == SOCK_IN ? inputs_.as_span().cast<const SocketRef *>() :
+ outputs_.as_span().cast<const SocketRef *>();
+}
+
inline Span<const InternalLinkRef *> 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<decl::Bool>("Boolean", "Boolean");
b.add_input<decl::Bool>("Boolean", "Boolean_001");
b.add_output<decl::Bool>("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<decl::Float>("A").min(-10000.0f).max(10000.0f);
b.add_input<decl::Float>("B").min(-10000.0f).max(10000.0f);
b.add_input<decl::Float>("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<decl::Float>("Float");
b.add_output<decl::Int>("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<decl::String>("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<decl::Vector>("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<decl::Float>("Min").min(-10000.0f).max(10000.0f);
b.add_input<decl::Float>("Max").default_value(1.0f).min(-10000.0f).max(10000.0f);
b.add_input<decl::Int>("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<decl::String>("String");
b.add_output<decl::Int>("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<decl::String>("String");
b.add_input<decl::Int>("Position");
b.add_input<decl::Int>("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<decl::Float>("Value");
b.add_input<decl::Int>("Decimals").min(0);
b.add_output<decl::String>("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<decl::Geometry>("Geometry");
- b.add_input<decl::Vector>("Value");
- b.add_input<decl::Float>("Value", "Value_001");
- b.add_input<decl::Color>("Value", "Value_002");
- b.add_input<decl::Bool>("Value", "Value_003");
- b.add_input<decl::Int>("Value", "Value_004");
+ b.add_input<decl::Vector>("Value").supports_field();
+ b.add_input<decl::Float>("Value", "Value_001").supports_field();
+ b.add_input<decl::Color>("Value", "Value_002").supports_field();
+ b.add_input<decl::Bool>("Value", "Value_003").supports_field();
+ b.add_input<decl::Int>("Value", "Value_004").supports_field();
b.add_output<decl::Geometry>("Geometry");
- b.add_output<decl::Vector>("Attribute");
- b.add_output<decl::Float>("Attribute", "Attribute_001");
- b.add_output<decl::Color>("Attribute", "Attribute_002");
- b.add_output<decl::Bool>("Attribute", "Attribute_003");
- b.add_output<decl::Int>("Attribute", "Attribute_004");
+ b.add_output<decl::Vector>("Attribute").field_source();
+ b.add_output<decl::Float>("Attribute", "Attribute_001").field_source();
+ b.add_output<decl::Color>("Attribute", "Attribute_002").field_source();
+ b.add_output<decl::Bool>("Attribute", "Attribute_003").field_source();
+ b.add_output<decl::Int>("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<decl::Geometry>("Geometry");
- b.add_input<decl::Float>("Attribute").hide_value();
- b.add_input<decl::Vector>("Attribute", "Attribute_001").hide_value();
+ b.add_input<decl::Float>("Attribute").hide_value().supports_field();
+ b.add_input<decl::Vector>("Attribute", "Attribute_001").hide_value().supports_field();
b.add_output<decl::Float>("Mean");
b.add_output<decl::Float>("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<decl::Float>("Factor");
+ b.add_output<decl::Float>("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<decl::Geometry>("Curve");
- b.add_input<decl::Float>("Factor").min(0.0f).max(1.0f).subtype(PROP_FACTOR);
- b.add_input<decl::Float>("Length").min(0.0f).subtype(PROP_DISTANCE);
+ b.add_input<decl::Float>("Factor").min(0.0f).max(1.0f).subtype(PROP_FACTOR).supports_field();
+ b.add_input<decl::Float>("Length").min(0.0f).subtype(PROP_DISTANCE).supports_field();
- b.add_output<decl::Vector>("Position");
- b.add_output<decl::Vector>("Tangent");
- b.add_output<decl::Vector>("Normal");
+ b.add_output<decl::Vector>("Position").dependent_field();
+ b.add_output<decl::Vector>("Tangent").dependent_field();
+ b.add_output<decl::Vector>("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<decl::Int>("Index");
+ b.add_output<decl::Int>("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<decl::Vector>("Normal");
+ b.add_output<decl::Vector>("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<decl::Vector>("Position");
+ b.add_output<decl::Vector>("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<decl::Vector>("Tangent");
+ b.add_output<decl::Vector>("Tangent").field_source();
}
static void calculate_bezier_tangents(const BezierSpline &spline, MutableSpan<float3> 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<decl::Geometry>("Geometry");
b.add_input<decl::Material>("Material").hide_label();
- b.add_input<decl::Bool>("Selection").default_value(true).hide_value();
+ b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field();
b.add_output<decl::Geometry>("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<decl::Material>("Material").hide_label(true);
- b.add_output<decl::Bool>("Selection");
+ b.add_output<decl::Bool>("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<decl::Geometry>("Geometry");
- b.add_input<decl::Vector>("Position").hide_value();
- b.add_input<decl::Bool>("Selection").default_value(true).hide_value();
+ b.add_input<decl::Vector>("Position").hide_value().implicit_field();
+ b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field();
b.add_output<decl::Geometry>("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<const NodeRef *> 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<const NodeRef *> toposort;
+ toposort.reserve(nodes_by_id_.size());
+ Array<bool> node_is_done_by_id(nodes_by_id_.size(), false);
+ Stack<Item> 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<const SocketRef *> 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<const SocketRef *> 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<decl::Float>("Value").min(0.0f).max(1.0f).default_value(1.0f);
b.add_input<decl::Float>("Min").default_value(0.0f).min(-10000.0f).max(10000.0f);
b.add_input<decl::Float>("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<decl::Float>("Fac").min(0.0f).max(1.0f).default_value(1.0f).subtype(PROP_FACTOR);
b.add_input<decl::Vector>("Vector").min(-1.0f).max(1.0f);
b.add_output<decl::Vector>("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<decl::Float>("Value").min(-10000.0f).max(10000.0f).default_value(1.0f);
b.add_input<decl::Float>("From Min").min(-10000.0f).max(10000.0f);
b.add_input<decl::Float>("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<decl::Float>("Value").default_value(0.5f).min(-10000.0f).max(10000.0f);
b.add_input<decl::Float>("Value", "Value_001").default_value(0.5f).min(-10000.0f).max(10000.0f);
b.add_input<decl::Float>("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<decl::Float>("Fac").default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
b.add_input<decl::Color>("Color1").default_value({0.5f, 0.5f, 0.5f, 1.0f});
b.add_input<decl::Color>("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<decl::Color>("Image").default_value({0.8f, 0.8f, 0.8f, 1.0f});
b.add_output<decl::Float>("R");
b.add_output<decl::Float>("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<decl::Vector>("Vector").min(-10000.0f).max(10000.0f);
b.add_output<decl::Float>("X");
b.add_output<decl::Float>("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<decl::Vector>("Vector").hide_value();
b.add_input<decl::Float>("W").min(-1000.0f).max(1000.0f);
b.add_input<decl::Float>("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<decl::Vector>("Vector").hide_value();
+ b.is_function_node();
+ b.add_input<decl::Vector>("Vector").hide_value().implicit_field();
b.add_input<decl::Float>("W").min(-1000.0f).max(1000.0f);
b.add_input<decl::Float>("Scale").min(-1000.0f).max(1000.0f).default_value(5.0f);
b.add_input<decl::Float>("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<decl::Vector>("Vector").hide_value();
b.add_input<decl::Float>("W").min(-1000.0f).max(1000.0f);
b.add_input<decl::Float>("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<decl::Vector>("Vector").min(-10000.0f).max(10000.0f);
b.add_input<decl::Float>("W").min(-10000.0f).max(10000.0f);
b.add_output<decl::Float>("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<decl::Float>("Fac").default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
b.add_output<decl::Color>("Color");
b.add_output<decl::Float>("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<decl::Vector>("Vector").min(-10000.0f).max(10000.0f);
b.add_input<decl::Vector>("Vector", "Vector_001").min(-10000.0f).max(10000.0f);
b.add_input<decl::Vector>("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<decl::Vector>("Vector").min(0.0f).max(1.0f).hide_value();
b.add_input<decl::Vector>("Vector");
b.add_input<decl::Vector>("Axis").min(-1.0f).max(1.0f).default_value({0.0f, 0.0f, 1.0f});