diff --git a/scripts/modules/bpy_types.py b/scripts/modules/bpy_types.py index 063102fb0ac..6d4042905ab 100644 --- a/scripts/modules/bpy_types.py +++ b/scripts/modules/bpy_types.py @@ -1218,7 +1218,11 @@ class NodeSocket(StructRNA, metaclass=RNAMetaPropGroup): link.to_socket == self)) -class NodeSocketInterface(StructRNA, metaclass=RNAMetaPropGroup): +class NodeTreeInterfaceItem(StructRNA): + __slots__ = () + + +class NodeTreeInterfaceSocket(NodeTreeInterfaceItem, metaclass=RNAMetaPropGroup): __slots__ = () diff --git a/scripts/startup/bl_operators/geometry_nodes.py b/scripts/startup/bl_operators/geometry_nodes.py index ab2d9638b64..b1f31129239 100644 --- a/scripts/startup/bl_operators/geometry_nodes.py +++ b/scripts/startup/bl_operators/geometry_nodes.py @@ -14,8 +14,8 @@ from bpy.props import ( def build_default_empty_geometry_node_group(name): group = bpy.data.node_groups.new(name, 'GeometryNodeTree') - group.inputs.new('NodeSocketGeometry', data_("Geometry")) - group.outputs.new('NodeSocketGeometry', data_("Geometry")) + group.interface.new_socket(data_("Geometry"), in_out={'OUTPUT'}, socket_type='NodeSocketGeometry') + group.interface.new_socket(data_("Geometry"), in_out={'INPUT'}, socket_type='NodeSocketGeometry') input_node = group.nodes.new('NodeGroupInput') output_node = group.nodes.new('NodeGroupOutput') output_node.is_active_output = True diff --git a/scripts/startup/bl_operators/node.py b/scripts/startup/bl_operators/node.py index 0838778b07e..4c7f15e96db 100644 --- a/scripts/startup/bl_operators/node.py +++ b/scripts/startup/bl_operators/node.py @@ -260,6 +260,112 @@ class NODE_OT_tree_path_parent(Operator): return {'FINISHED'} +class NodeInterfaceOperator(): + @classmethod + def poll(cls, context): + space = context.space_data + if not space or space.type != 'NODE_EDITOR' or not space.edit_tree: + return False + if space.edit_tree.is_embedded_data: + return False + return True + + +class NODE_OT_interface_item_new(NodeInterfaceOperator, Operator): + '''Add a new item to the interface''' + bl_idname = "node.interface_item_new" + bl_label = "New Item" + bl_options = {'REGISTER', 'UNDO'} + + item_type: EnumProperty( + name="Item Type", + description="Type of the item to create", + items=[ + ('INPUT', "Input", ""), + ('OUTPUT', "Output", ""), + ('PANEL', "Panel", "")], + default='INPUT', + ) + + socket_type = 'NodeSocketFloat' + + def execute(self, context): + snode = context.space_data + tree = snode.edit_tree + interface = tree.interface + + # Remember active item and position to determine target position. + active_item = interface.active + active_pos = active_item.position if active_item else -1 + + if self.item_type == 'INPUT': + item = interface.new_socket("Socket", socket_type=self.socket_type, in_out={'INPUT'}) + elif self.item_type == 'OUTPUT': + item = interface.new_socket("Socket", socket_type=self.socket_type, in_out={'OUTPUT'}) + elif self.item_type == 'PANEL': + item = interface.new_panel("Panel") + else: + return {'CANCELLED'} + + if active_item: + # Insert into active panel if possible, otherwise insert after active item. + if active_item.item_type == 'PANEL' and item.item_type != 'PANEL': + interface.move_to_parent(item, active_item, len(active_item.interface_items)) + else: + interface.move_to_parent(item, active_item.parent, active_pos + 1) + interface.active = item + + return {'FINISHED'} + + +class NODE_OT_interface_item_duplicate(NodeInterfaceOperator, Operator): + '''Add a copy of the active item to the interface''' + bl_idname = "node.interface_item_duplicate" + bl_label = "Duplicate Item" + bl_options = {'REGISTER', 'UNDO'} + + @classmethod + def poll(cls, context): + if not super().poll(context): + return False + + snode = context.space_data + tree = snode.edit_tree + interface = tree.interface + return interface.active is not None + + def execute(self, context): + snode = context.space_data + tree = snode.edit_tree + interface = tree.interface + item = interface.active + + if item: + item_copy = interface.copy(item) + interface.active = item_copy + + return {'FINISHED'} + + +class NODE_OT_interface_item_remove(NodeInterfaceOperator, Operator): + '''Remove active item from the interface''' + bl_idname = "node.interface_item_remove" + bl_label = "Remove Item" + bl_options = {'REGISTER', 'UNDO'} + + def execute(self, context): + snode = context.space_data + tree = snode.edit_tree + interface = tree.interface + item = interface.active + + if item: + interface.remove(item) + interface.active_index = min(interface.active_index, len(interface.ui_items) - 1) + + return {'FINISHED'} + + classes = ( NodeSetting, @@ -267,5 +373,8 @@ classes = ( NODE_OT_add_simulation_zone, NODE_OT_add_repeat_zone, NODE_OT_collapse_hide_unused_toggle, + NODE_OT_interface_item_new, + NODE_OT_interface_item_duplicate, + NODE_OT_interface_item_remove, NODE_OT_tree_path_parent, ) diff --git a/scripts/startup/bl_ui/space_node.py b/scripts/startup/bl_ui/space_node.py index 29cba4dfca4..dd8fdd031e4 100644 --- a/scripts/startup/bl_ui/space_node.py +++ b/scripts/startup/bl_ui/space_node.py @@ -859,22 +859,20 @@ class NODE_PT_overlay(Panel): col.prop(overlay, "show_named_attributes", text="Named Attributes") -class NODE_UL_interface_sockets(bpy.types.UIList): - def draw_item(self, context, layout, _data, item, icon, _active_data, _active_propname, _index): - socket = item - color = socket.draw_color(context) +class NODE_MT_node_tree_interface_context_menu(Menu): + bl_label = "Node Tree Interface Specials" - if self.layout_type in {'DEFAULT', 'COMPACT'}: - row = layout.row(align=True) + def draw(self, _context): + layout = self.layout - row.template_node_socket(color=color) - row.prop(socket, "name", text="", emboss=False, icon_value=icon) - elif self.layout_type == 'GRID': - layout.alignment = 'CENTER' - layout.template_node_socket(color=color) + layout.operator("node.interface_item_duplicate", icon='DUPLICATE') -class NodeTreeInterfacePanel(Panel): +class NODE_PT_node_tree_interface(Panel): + bl_space_type = 'NODE_EDITOR' + bl_region_type = 'UI' + bl_category = "Group" + bl_label = "Interface" @classmethod def poll(cls, context): @@ -888,119 +886,52 @@ class NodeTreeInterfacePanel(Panel): return False return True - def draw_socket_list(self, context, in_out, sockets_propname, active_socket_propname): + def draw(self, context): layout = self.layout - snode = context.space_data tree = snode.edit_tree - sockets = getattr(tree, sockets_propname) - active_socket_index = getattr(tree, active_socket_propname) - active_socket = sockets[active_socket_index] if active_socket_index >= 0 else None split = layout.row() - split.template_list("NODE_UL_interface_sockets", in_out, tree, sockets_propname, tree, active_socket_propname) + split.template_node_tree_interface(tree.interface) - ops_col = split.column() - - add_remove_col = ops_col.column(align=True) - props = add_remove_col.operator("node.tree_socket_add", icon='ADD', text="") - props.in_out = in_out - props = add_remove_col.operator("node.tree_socket_remove", icon='REMOVE', text="") - props.in_out = in_out + ops_col = split.column(align=True) + ops_col.operator_menu_enum("node.interface_item_new", "item_type", icon='ADD', text="") + ops_col.operator("node.interface_item_remove", icon='REMOVE', text="") + ops_col.separator() + ops_col.menu("NODE_MT_node_tree_interface_context_menu", icon='DOWNARROW_HLT', text="") ops_col.separator() - up_down_col = ops_col.column(align=True) - props = up_down_col.operator("node.tree_socket_move", icon='TRIA_UP', text="") - props.in_out = in_out - props.direction = 'UP' - props = up_down_col.operator("node.tree_socket_move", icon='TRIA_DOWN', text="") - props.in_out = in_out - props.direction = 'DOWN' - - if active_socket is not None: - # Mimicking property split. - layout.use_property_split = False - layout.use_property_decorate = False - layout_row = layout.row(align=True) - layout_split = layout_row.split(factor=0.4, align=True) - - label_column = layout_split.column(align=True) - label_column.alignment = 'RIGHT' - # Menu to change the socket type. - label_column.label(text="Type") - - property_row = layout_split.row(align=True) - props = property_row.operator_menu_enum( - "node.tree_socket_change_type", - "socket_type", - text=(iface_(active_socket.bl_label) if active_socket.bl_label - else iface_(active_socket.bl_idname)), - ) - props.in_out = in_out - - with context.temp_override(interface_socket=active_socket): - if bpy.ops.node.tree_socket_change_subtype.poll(): - layout_row = layout.row(align=True) - layout_split = layout_row.split(factor=0.4, align=True) - - label_column = layout_split.column(align=True) - label_column.alignment = 'RIGHT' - label_column.label(text="Subtype") - property_row = layout_split.row(align=True) - - property_row.context_pointer_set("interface_socket", active_socket) - props = property_row.operator_menu_enum( - "node.tree_socket_change_subtype", - "socket_subtype", - text=(iface_(active_socket.bl_subtype_label) if active_socket.bl_subtype_label - else iface_(active_socket.bl_idname)), - ) - + active_item = tree.interface.active + if active_item is not None: layout.use_property_split = True - layout.use_property_decorate = False - layout.prop(active_socket, "name") - # Display descriptions only for Geometry Nodes, since it's only used in the modifier panel. - if tree.type == 'GEOMETRY': - layout.prop(active_socket, "description") - field_socket_prefixes = { - "NodeSocketInt", - "NodeSocketColor", - "NodeSocketVector", - "NodeSocketBool", - "NodeSocketFloat", - } - is_field_type = any( - active_socket.bl_socket_idname.startswith(prefix) - for prefix in field_socket_prefixes - ) - if is_field_type: - if in_out == 'OUT': - layout.prop(active_socket, "attribute_domain") - layout.prop(active_socket, "default_attribute_name") - active_socket.draw(context, layout) + if active_item.item_type == 'SOCKET': + layout.prop(active_item, "socket_type", text="Type") + layout.prop(active_item, "description") + layout.prop(active_item, "in_out", text="Input/Output") + # Display descriptions only for Geometry Nodes, since it's only used in the modifier panel. + if tree.type == 'GEOMETRY': + field_socket_types = { + "NodeSocketInt", + "NodeSocketColor", + "NodeSocketVector", + "NodeSocketBool", + "NodeSocketFloat", + } + if active_item.socket_type in field_socket_types: + if 'OUTPUT' in active_item.in_out: + layout.prop(active_item, "attribute_domain") + layout.prop(active_item, "default_attribute_name") + active_item.draw(context, layout) + if active_item.item_type == 'PANEL': + layout.prop(active_item, "name") + layout.prop(active_item, "description") + layout.prop(active_item, "default_closed", text="Closed by Default") -class NODE_PT_node_tree_interface_inputs(NodeTreeInterfacePanel): - bl_space_type = 'NODE_EDITOR' - bl_region_type = 'UI' - bl_category = "Group" - bl_label = "Inputs" - - def draw(self, context): - self.draw_socket_list(context, "IN", "inputs", "active_input") - - -class NODE_PT_node_tree_interface_outputs(NodeTreeInterfacePanel): - bl_space_type = 'NODE_EDITOR' - bl_region_type = 'UI' - bl_category = "Group" - bl_label = "Outputs" - - def draw(self, context): - self.draw_socket_list(context, "OUT", "outputs", "active_output") + layout.use_property_split = False class NODE_UL_simulation_zone_items(bpy.types.UIList): @@ -1209,6 +1140,8 @@ classes = ( NODE_PT_material_slots, NODE_PT_geometry_node_asset_traits, NODE_PT_node_color_presets, + NODE_MT_node_tree_interface_context_menu, + NODE_PT_node_tree_interface, NODE_PT_active_node_generic, NODE_PT_active_node_color, NODE_PT_texture_mapping, @@ -1217,9 +1150,6 @@ classes = ( NODE_PT_quality, NODE_PT_annotation, NODE_PT_overlay, - NODE_UL_interface_sockets, - NODE_PT_node_tree_interface_inputs, - NODE_PT_node_tree_interface_outputs, NODE_UL_simulation_zone_items, NODE_PT_simulation_zone_items, NODE_UL_repeat_zone_items, diff --git a/scripts/templates_py/custom_nodes.py b/scripts/templates_py/custom_nodes.py index d337bebb1f9..f6fe0979ac1 100644 --- a/scripts/templates_py/custom_nodes.py +++ b/scripts/templates_py/custom_nodes.py @@ -1,5 +1,5 @@ import bpy -from bpy.types import NodeTree, Node, NodeSocket +from bpy.types import NodeTree, Node, NodeSocket, NodeTreeInterfaceSocket # Implementation of custom nodes from Python @@ -25,19 +25,9 @@ class MyCustomSocket(NodeSocket): # Label for nice name display bl_label = "Custom Node Socket" - # Enum items list - my_items = ( - ('DOWN', "Down", "Where your feet are"), - ('UP', "Up", "Where your head should be"), - ('LEFT', "Left", "Not right"), - ('RIGHT', "Right", "Not left"), - ) - - my_enum_prop: bpy.props.EnumProperty( - name="Direction", - description="Just an example", - items=my_items, - default='UP', + input_value: bpy.props.FloatProperty( + name="Value", + description="Value when the socket is not connected", ) # Optional function for drawing the socket input value @@ -45,13 +35,35 @@ class MyCustomSocket(NodeSocket): if self.is_output or self.is_linked: layout.label(text=text) else: - layout.prop(self, "my_enum_prop", text=text) + layout.prop(self, "input_value", text=text) # Socket color - def draw_color(self, context, node): + @classmethod + def draw_color_simple(cls): return (1.0, 0.4, 0.216, 0.5) +# Customizable interface properties to generate a socket from. +class MyCustomInterfaceSocket(NodeTreeInterfaceSocket): + # The type of socket that is generated. + bl_socket_idname = 'CustomSocketType' + + default_value: bpy.props.FloatProperty(default=1.0, description="Default input value for new sockets",) + + def draw(self, context, layout): + # Display properties of the interface. + layout.prop(self, "default_value") + + # Set properties of newly created sockets + def init_socket(self, node, socket, data_path): + socket.input_value = self.default_value + + # Use an existing socket to initialize the group interface + def from_socket(self, node, socket): + # Current value of the socket becomes the default + self.default_value = socket.input_value + + # Mix-in class for all custom nodes in this tree type. # Defines a poll function to enable instantiation. class MyCustomTreeNode: @@ -163,6 +175,7 @@ node_categories = [ classes = ( MyCustomTree, MyCustomSocket, + MyCustomInterfaceSocket, MyCustomNode, ) diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 20cc1a5a293..c9803ccd4f2 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -29,7 +29,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 19 +#define BLENDER_FILE_SUBVERSION 20 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and cancel loading the file, showing a warning to diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 003f9ff7037..3bd15931a37 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -115,9 +115,8 @@ using NodeDeclareFunction = void (*)(blender::nodes::NodeDeclarationBuilder &bui using NodeDeclareDynamicFunction = void (*)(const bNodeTree &tree, const bNode &node, blender::nodes::NodeDeclaration &r_declaration); -using SocketGetCPPValueFunction = void (*)(const struct bNodeSocket &socket, void *r_value); -using SocketGetGeometryNodesCPPValueFunction = void (*)(const struct bNodeSocket &socket, - void *r_value); +using SocketGetCPPValueFunction = void (*)(const void *socket_value, void *r_value); +using SocketGetGeometryNodesCPPValueFunction = void (*)(const void *socket_value, void *r_value); /* Adds socket link operations that are specific to this node type. */ using NodeGatherSocketLinkOperationsFunction = @@ -170,18 +169,21 @@ typedef struct bNodeSocketType { struct PointerRNA *ptr, struct PointerRNA *node_ptr, float *r_color); + void (*draw_color_simple)(const bNodeSocketType *socket_type, float *r_color); - void (*interface_draw)(struct bContext *C, struct uiLayout *layout, struct PointerRNA *ptr); - void (*interface_draw_color)(struct bContext *C, struct PointerRNA *ptr, float *r_color); - void (*interface_init_socket)(struct bNodeTree *ntree, - const struct bNodeSocket *interface_socket, + void (*interface_draw)(struct ID *id, + struct bNodeTreeInterfaceSocket *socket, + struct bContext *C, + struct uiLayout *layout); + void (*interface_init_socket)(struct ID *id, + const struct bNodeTreeInterfaceSocket *interface_socket, struct bNode *node, - struct bNodeSocket *sock, + struct bNodeSocket *socket, const char *data_path); - void (*interface_from_socket)(struct bNodeTree *ntree, - struct bNodeSocket *interface_socket, + void (*interface_from_socket)(struct ID *id, + struct bNodeTreeInterfaceSocket *interface_socket, const struct bNode *node, - const struct bNodeSocket *sock); + const struct bNodeSocket *socket); /* RNA integration */ ExtensionRNA ext_socket; @@ -539,19 +541,6 @@ void ntreeBlendWrite(struct BlendWriter *writer, struct bNodeTree *ntree); /** \} */ -/* -------------------------------------------------------------------- */ -/** \name Node Tree Interface - * \{ */ - -void ntreeRemoveSocketInterface(bNodeTree *ntree, bNodeSocket *sock); - -struct bNodeSocket *ntreeAddSocketInterface(struct bNodeTree *ntree, - eNodeSocketInOut in_out, - const char *idname, - const char *name); - -/** \} */ - /* -------------------------------------------------------------------- */ /** \name Generic API, Nodes * \{ */ @@ -585,7 +574,7 @@ struct GHashIterator *nodeSocketTypeGetIterator(void); const char *nodeSocketTypeLabel(const bNodeSocketType *stype); const char *nodeStaticSocketType(int type, int subtype); -const char *nodeStaticSocketInterfaceType(int type, int subtype); +const char *nodeStaticSocketInterfaceTypeNew(int type, int subtype); const char *nodeStaticSocketLabel(int type, int subtype); /* Helper macros for iterating over node types. */ diff --git a/source/blender/blenkernel/BKE_node.hh b/source/blender/blenkernel/BKE_node.hh index 8882ed17bda..fa8234d2488 100644 --- a/source/blender/blenkernel/BKE_node.hh +++ b/source/blender/blenkernel/BKE_node.hh @@ -11,6 +11,7 @@ #include "BLI_compiler_compat.h" #include "BLI_ghash.h" #include "BLI_math_vector_types.hh" +#include "BLI_span.hh" #include "DNA_listBase.h" @@ -57,37 +58,6 @@ void ntreeLocalMerge(Main *bmain, bNodeTree *localtree, bNodeTree *ntree); */ void ntreeBlendReadData(BlendDataReader *reader, ID *owner_id, bNodeTree *ntree); -/* -------------------------------------------------------------------- */ -/** \name Node Tree Interface - * \{ */ - -bNodeSocket *ntreeFindSocketInterface(bNodeTree *ntree, - eNodeSocketInOut in_out, - const char *identifier); - -bNodeSocket *ntreeInsertSocketInterface(bNodeTree *ntree, - eNodeSocketInOut in_out, - const char *idname, - bNodeSocket *next_sock, - const char *name); - -bNodeSocket *ntreeAddSocketInterfaceFromSocket(bNodeTree *ntree, - const bNode *from_node, - const bNodeSocket *from_sock); - -bNodeSocket *ntreeAddSocketInterfaceFromSocketWithName(bNodeTree *ntree, - const bNode *from_node, - const bNodeSocket *from_sock, - const char *idname, - const char *name); - -bNodeSocket *ntreeInsertSocketInterfaceFromSocket(bNodeTree *ntree, - bNodeSocket *next_sock, - const bNode *from_node, - const bNodeSocket *from_sock); - -/** \} */ - bool node_type_is_undefined(const bNode *node); bool nodeIsStaticSocketType(const bNodeSocketType *stype); diff --git a/source/blender/blenkernel/BKE_node_runtime.hh b/source/blender/blenkernel/BKE_node_runtime.hh index d429999b3af..d3a2aae14f4 100644 --- a/source/blender/blenkernel/BKE_node_runtime.hh +++ b/source/blender/blenkernel/BKE_node_runtime.hh @@ -18,6 +18,7 @@ #include "DNA_node_types.h" #include "BKE_node.hh" +#include "BKE_node_tree_interface.hh" struct bNode; struct bNodeSocket; @@ -170,8 +171,7 @@ class bNodeTreeRuntime : NonCopyable, NonMovable { bool has_undefined_nodes_or_sockets = false; bNode *group_output_node = nullptr; Vector root_frames; - Vector interface_inputs; - Vector interface_outputs; + bNodeTreeInterfaceCache interface_cache; }; /** @@ -216,6 +216,18 @@ class bNodeSocketRuntime : NonCopyable, NonMovable { int index_in_inout_sockets = -1; }; +class bNodePanelRuntime : NonCopyable, NonMovable { + public: + /* The vertical location of the panel in the tree, calculated while drawing the nodes and invalid + * if the node tree hasn't been drawn yet. In the node tree's "world space" (the same as + * #bNode::runtime::totr). */ + float location_y; + /* Vertical start location of the panel content. */ + float min_content_y; + /* Vertical end location of the panel content. */ + float max_content_y; +}; + /** * Run-time data for every node. This should only contain data that is somewhat persistent (i.e. * data that lives longer than a single depsgraph evaluation + redraw). Data that's only used in @@ -299,6 +311,9 @@ class bNodeRuntime : NonCopyable, NonMovable { /** Can be used to toposort a subset of nodes. */ int toposort_left_to_right_index = -1; int toposort_right_to_left_index = -1; + + /* Panel runtime state */ + Array panels; }; namespace node_tree_runtime { @@ -469,18 +484,6 @@ inline blender::Span bNodeTree::group_input_nodes() const return this->nodes_by_type("NodeGroupInput"); } -inline blender::Span bNodeTree::interface_inputs() const -{ - BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this)); - return this->runtime->interface_inputs; -} - -inline blender::Span bNodeTree::interface_outputs() const -{ - BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this)); - return this->runtime->interface_outputs; -} - inline blender::Span bNodeTree::all_input_sockets() const { BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this)); @@ -545,6 +548,24 @@ inline blender::Span bNodeTree::nested_node_refs_span() const return {this->nested_node_refs, this->nested_node_refs_num}; } +inline blender::Span bNodeTree::interface_inputs() const +{ + BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this)); + return this->runtime->interface_cache.inputs; +} + +inline blender::Span bNodeTree::interface_outputs() const +{ + BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this)); + return this->runtime->interface_cache.outputs; +} + +inline blender::Span bNodeTree::interface_items() const +{ + BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this)); + return this->runtime->interface_cache.items; +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -693,6 +714,16 @@ inline const blender::nodes::NodeDeclaration *bNode::declaration() const return this->runtime->declaration; } +inline blender::Span bNode::panel_states() const +{ + return {panel_states_array, num_panel_states}; +} + +inline blender::MutableSpan bNode::panel_states() +{ + return {panel_states_array, num_panel_states}; +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -756,6 +787,11 @@ inline bool bNodeSocket::is_available() const return (this->flag & SOCK_UNAVAIL) == 0; } +inline bool bNodeSocket::is_panel_collapsed() const +{ + return (this->flag & SOCK_PANEL_COLLAPSED) != 0; +} + inline bool bNodeSocket::is_visible() const { return !this->is_hidden() && this->is_available(); @@ -852,3 +888,19 @@ inline const bNode &bNodeSocket::owner_node() const } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #bNode Inline Methods + * \{ */ + +inline bool bNodePanelState::is_collapsed() const +{ + return flag & NODE_PANEL_COLLAPSED; +} + +inline bool bNodePanelState::is_parent_collapsed() const +{ + return flag & NODE_PANEL_PARENT_COLLAPSED; +} + +/** \} */ diff --git a/source/blender/blenkernel/BKE_node_tree_interface.hh b/source/blender/blenkernel/BKE_node_tree_interface.hh index 3e3da0b475e..49dec1478a2 100644 --- a/source/blender/blenkernel/BKE_node_tree_interface.hh +++ b/source/blender/blenkernel/BKE_node_tree_interface.hh @@ -78,57 +78,125 @@ template const T *get_item_as(const bNodeTreeInterfaceItem *item) namespace socket_types { -constexpr const char *node_socket_data_float = "NodeSocketFloat"; -constexpr const char *node_socket_data_int = "NodeSocketInt"; -constexpr const char *node_socket_data_bool = "NodeSocketBool"; -constexpr const char *node_socket_data_rotation = "NodeSocketRotation"; -constexpr const char *node_socket_data_vector = "NodeSocketVector"; -constexpr const char *node_socket_data_color = "NodeSocketColor"; -constexpr const char *node_socket_data_string = "NodeSocketString"; -constexpr const char *node_socket_data_object = "NodeSocketObject"; -constexpr const char *node_socket_data_image = "NodeSocketImage"; -constexpr const char *node_socket_data_collection = "NodeSocketCollection"; -constexpr const char *node_socket_data_texture = "NodeSocketTexture"; -constexpr const char *node_socket_data_material = "NodeSocketMaterial"; +/* Info for generating static subtypes. */ +struct bNodeSocketStaticTypeInfo { + const char *socket_identifier; + const char *interface_identifier; + eNodeSocketDatatype type; + PropertySubType subtype; + const char *label; +}; -template void socket_data_to_static_type(const char *socket_type, const Fn &fn) +/* Note: Socket and interface subtypes could be defined from a single central list, + * but makesrna cannot have a dependency on BKE, so this list would have to live in RNA itself, + * with BKE etc. accessing the RNA API to get the subtypes info. */ +static const bNodeSocketStaticTypeInfo node_socket_subtypes[] = { + {"NodeSocketFloat", "NodeTreeInterfaceSocketFloat", SOCK_FLOAT, PROP_NONE}, + {"NodeSocketFloatUnsigned", "NodeTreeInterfaceSocketFloatUnsigned", SOCK_FLOAT, PROP_UNSIGNED}, + {"NodeSocketFloatPercentage", + "NodeTreeInterfaceSocketFloatPercentage", + SOCK_FLOAT, + PROP_PERCENTAGE}, + {"NodeSocketFloatFactor", "NodeTreeInterfaceSocketFloatFactor", SOCK_FLOAT, PROP_FACTOR}, + {"NodeSocketFloatAngle", "NodeTreeInterfaceSocketFloatAngle", SOCK_FLOAT, PROP_ANGLE}, + {"NodeSocketFloatTime", "NodeTreeInterfaceSocketFloatTime", SOCK_FLOAT, PROP_TIME}, + {"NodeSocketFloatTimeAbsolute", + "NodeTreeInterfaceSocketFloatTimeAbsolute", + SOCK_FLOAT, + PROP_TIME_ABSOLUTE}, + {"NodeSocketFloatDistance", "NodeTreeInterfaceSocketFloatDistance", SOCK_FLOAT, PROP_DISTANCE}, + {"NodeSocketInt", "NodeTreeInterfaceSocketInt", SOCK_INT, PROP_NONE}, + {"NodeSocketIntUnsigned", "NodeTreeInterfaceSocketIntUnsigned", SOCK_INT, PROP_UNSIGNED}, + {"NodeSocketIntPercentage", "NodeTreeInterfaceSocketIntPercentage", SOCK_INT, PROP_PERCENTAGE}, + {"NodeSocketIntFactor", "NodeTreeInterfaceSocketIntFactor", SOCK_INT, PROP_FACTOR}, + {"NodeSocketBool", "NodeTreeInterfaceSocketBool", SOCK_BOOLEAN, PROP_NONE}, + {"NodeSocketRotation", "NodeTreeInterfaceSocketRotation", SOCK_ROTATION, PROP_NONE}, + {"NodeSocketVector", "NodeTreeInterfaceSocketVector", SOCK_VECTOR, PROP_NONE}, + {"NodeSocketVectorTranslation", + "NodeTreeInterfaceSocketVectorTranslation", + SOCK_VECTOR, + PROP_TRANSLATION}, + {"NodeSocketVectorDirection", + "NodeTreeInterfaceSocketVectorDirection", + SOCK_VECTOR, + PROP_DIRECTION}, + {"NodeSocketVectorVelocity", + "NodeTreeInterfaceSocketVectorVelocity", + SOCK_VECTOR, + PROP_VELOCITY}, + {"NodeSocketVectorAcceleration", + "NodeTreeInterfaceSocketVectorAcceleration", + SOCK_VECTOR, + PROP_ACCELERATION}, + {"NodeSocketVectorEuler", "NodeTreeInterfaceSocketVectorEuler", SOCK_VECTOR, PROP_EULER}, + {"NodeSocketVectorXYZ", "NodeTreeInterfaceSocketVectorXYZ", SOCK_VECTOR, PROP_XYZ}, + {"NodeSocketColor", "NodeTreeInterfaceSocketColor", SOCK_RGBA, PROP_NONE}, + {"NodeSocketString", "NodeTreeInterfaceSocketString", SOCK_STRING, PROP_NONE}, + {"NodeSocketShader", "NodeTreeInterfaceSocketShader", SOCK_SHADER, PROP_NONE}, + {"NodeSocketObject", "NodeTreeInterfaceSocketObject", SOCK_OBJECT, PROP_NONE}, + {"NodeSocketImage", "NodeTreeInterfaceSocketImage", SOCK_IMAGE, PROP_NONE}, + {"NodeSocketGeometry", "NodeTreeInterfaceSocketGeometry", SOCK_GEOMETRY, PROP_NONE}, + {"NodeSocketCollection", "NodeTreeInterfaceSocketCollection", SOCK_COLLECTION, PROP_NONE}, + {"NodeSocketTexture", "NodeTreeInterfaceSocketTexture", SOCK_TEXTURE, PROP_NONE}, + {"NodeSocketMaterial", "NodeTreeInterfaceSocketMaterial", SOCK_MATERIAL, PROP_NONE}, +}; + +template bool socket_data_to_static_type(const eNodeSocketDatatype type, const Fn &fn) { - if (STREQ(socket_type, socket_types::node_socket_data_float)) { - fn.template operator()(); + switch (type) { + case SOCK_FLOAT: + fn.template operator()(); + return true; + case SOCK_INT: + fn.template operator()(); + return true; + case SOCK_BOOLEAN: + fn.template operator()(); + return true; + case SOCK_ROTATION: + fn.template operator()(); + return true; + case SOCK_VECTOR: + fn.template operator()(); + return true; + case SOCK_RGBA: + fn.template operator()(); + return true; + case SOCK_STRING: + fn.template operator()(); + return true; + case SOCK_OBJECT: + fn.template operator()(); + return true; + case SOCK_IMAGE: + fn.template operator()(); + return true; + case SOCK_COLLECTION: + fn.template operator()(); + return true; + case SOCK_TEXTURE: + fn.template operator()(); + return true; + case SOCK_MATERIAL: + fn.template operator()(); + return true; + + case SOCK_CUSTOM: + case SOCK_SHADER: + case SOCK_GEOMETRY: + return true; } - else if (STREQ(socket_type, socket_types::node_socket_data_int)) { - fn.template operator()(); - } - else if (STREQ(socket_type, socket_types::node_socket_data_bool)) { - fn.template operator()(); - } - else if (STREQ(socket_type, socket_types::node_socket_data_rotation)) { - fn.template operator()(); - } - else if (STREQ(socket_type, socket_types::node_socket_data_vector)) { - fn.template operator()(); - } - else if (STREQ(socket_type, socket_types::node_socket_data_color)) { - fn.template operator()(); - } - else if (STREQ(socket_type, socket_types::node_socket_data_string)) { - fn.template operator()(); - } - else if (STREQ(socket_type, socket_types::node_socket_data_object)) { - fn.template operator()(); - } - else if (STREQ(socket_type, socket_types::node_socket_data_image)) { - fn.template operator()(); - } - else if (STREQ(socket_type, socket_types::node_socket_data_collection)) { - fn.template operator()(); - } - else if (STREQ(socket_type, socket_types::node_socket_data_texture)) { - fn.template operator()(); - } - else if (STREQ(socket_type, socket_types::node_socket_data_material)) { - fn.template operator()(); + return false; +} + +template bool socket_data_to_static_type(const char *socket_type, const Fn &fn) +{ + for (const bNodeSocketStaticTypeInfo &info : node_socket_subtypes) { + if (STREQ(socket_type, info.socket_identifier)) { + return socket_data_to_static_type(info.type, fn); + } } + return false; } namespace detail { @@ -177,12 +245,12 @@ template const T &get_socket_data_as(const bNodeTreeInterfaceSocket } inline bNodeTreeInterfaceSocket *add_interface_socket_from_node(bNodeTree &ntree, - const bNode & /*from_node*/, + const bNode &from_node, const bNodeSocket &from_sock, const StringRefNull socket_type, const StringRefNull name) { - eNodeTreeInterfaceSocketFlag flag = eNodeTreeInterfaceSocketFlag(0); + NodeTreeInterfaceSocketFlag flag = NodeTreeInterfaceSocketFlag(0); SET_FLAG_FROM_TEST(flag, from_sock.in_out & SOCK_IN, NODE_INTERFACE_SOCKET_INPUT); SET_FLAG_FROM_TEST(flag, from_sock.in_out & SOCK_OUT, NODE_INTERFACE_SOCKET_OUTPUT); @@ -193,9 +261,8 @@ inline bNodeTreeInterfaceSocket *add_interface_socket_from_node(bNodeTree &ntree } const bNodeSocketType *typeinfo = iosock->socket_typeinfo(); if (typeinfo->interface_from_socket) { - /* XXX Enable when bNodeSocketType callbacks have been updated. */ + typeinfo->interface_from_socket(&ntree.id, iosock, &from_node, &from_sock); UNUSED_VARS(from_sock); - // typeinfo->interface_from_socket(ntree.id, iosock, &from_node, &from_sock); } return iosock; } diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 38c225232d7..3f58877016a 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -68,6 +68,7 @@ #include "BKE_node.hh" #include "BKE_node_runtime.hh" #include "BKE_node_tree_anonymous_attributes.hh" +#include "BKE_node_tree_interface.hh" #include "BKE_node_tree_update.h" #include "BKE_node_tree_zones.hh" #include "BKE_type_conversions.hh" @@ -115,6 +116,9 @@ using blender::nodes::OutputFieldDependency; using blender::nodes::OutputSocketFieldType; using blender::nodes::SocketDeclaration; +/* Forward declaration. */ +static void write_node_socket_default_value(BlendWriter *writer, const bNodeSocket *sock); + static CLG_LogRef LOG = {"bke.node"}; namespace blender::bke { @@ -129,15 +133,17 @@ bNodeSocketType NodeSocketTypeUndefined; namespace blender::bke { static void ntree_set_typeinfo(bNodeTree *ntree, bNodeTreeType *typeinfo); +static void node_socket_set_typeinfo(bNodeTree *ntree, + bNodeSocket *sock, + bNodeSocketType *typeinfo); static void node_socket_copy(bNodeSocket *sock_dst, const bNodeSocket *sock_src, const int flag); static void free_localized_node_groups(bNodeTree *ntree); -static void node_socket_interface_free(bNodeTree * /*ntree*/, - bNodeSocket *sock, - const bool do_id_user); +static bool socket_id_user_decrement(bNodeSocket *sock); static void ntree_init_data(ID *id) { bNodeTree *ntree = reinterpret_cast(id); + ntree->tree_interface.init_data(); ntree->runtime = MEM_new(__func__); ntree_set_typeinfo(ntree, nullptr); } @@ -190,20 +196,7 @@ static void ntree_copy_data(Main * /*bmain*/, ID *id_dst, const ID *id_src, cons nodeDeclarationEnsure(ntree_dst, node); } - /* copy interface sockets */ - BLI_listbase_clear(&ntree_dst->inputs); - LISTBASE_FOREACH (const bNodeSocket *, src_socket, &ntree_src->inputs) { - bNodeSocket *dst_socket = static_cast(MEM_dupallocN(src_socket)); - node_socket_copy(dst_socket, src_socket, flag_subdata); - BLI_addtail(&ntree_dst->inputs, dst_socket); - } - BLI_listbase_clear(&ntree_dst->outputs); - LISTBASE_FOREACH (const bNodeSocket *, src_socket, &ntree_src->outputs) { - bNodeSocket *dst_socket = static_cast(MEM_dupallocN(src_socket)); - node_socket_copy(dst_socket, src_socket, flag_subdata); - BLI_addtail(&ntree_dst->outputs, dst_socket); - } - + ntree_dst->tree_interface.copy_data(ntree_src->tree_interface, flag); /* copy preview hash */ if (ntree_src->previews && (flag & LIB_ID_COPY_NO_PREVIEW) == 0) { bNodeInstanceHashIterator iter; @@ -295,15 +288,7 @@ static void ntree_free_data(ID *id) node_free_node(ntree, node); } - /* free interface sockets */ - LISTBASE_FOREACH_MUTABLE (bNodeSocket *, sock, &ntree->inputs) { - node_socket_interface_free(ntree, sock, false); - MEM_freeN(sock); - } - LISTBASE_FOREACH_MUTABLE (bNodeSocket *, sock, &ntree->outputs) { - node_socket_interface_free(ntree, sock, false); - MEM_freeN(sock); - } + ntree->tree_interface.free_data(); /* free preview hash */ if (ntree->previews) { @@ -402,12 +387,7 @@ static void node_foreach_id(ID *id, LibraryForeachIDData *data) } } - LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs) { - BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, library_foreach_node_socket(data, sock)); - } - LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs) { - BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, library_foreach_node_socket(data, sock)); - } + ntree->tree_interface.foreach_id(data); } static void node_foreach_cache(ID *id, @@ -474,7 +454,155 @@ static ID **node_owner_pointer_get(ID *id) } // namespace blender::bke -static void write_node_socket_default_value(BlendWriter *writer, bNodeSocket *sock) +namespace blender::bke::forward_compat { + +static void write_node_socket_interface(BlendWriter *writer, const bNodeSocket *sock) +{ + BLO_write_struct(writer, bNodeSocket, sock); + + if (sock->prop) { + IDP_BlendWrite(writer, sock->prop); + } + + BLO_write_string(writer, sock->default_attribute_name); + + write_node_socket_default_value(writer, sock); +} + +/* Construct a bNodeSocket that represents a node group socket the old way. */ +static bNodeSocket *make_socket(bNodeTree *ntree, + const eNodeSocketInOut in_out, + const StringRef idname, + const StringRef name, + const StringRef identifier) +{ + bNodeSocketType *stype = nodeSocketTypeFind(idname.data()); + if (stype == nullptr) { + return nullptr; + } + + bNodeSocket *sock = MEM_cnew(__func__); + sock->runtime = MEM_new(__func__); + STRNCPY(sock->idname, stype->idname); + sock->in_out = int(in_out); + sock->type = int(SOCK_CUSTOM); /* int type undefined by default */ + node_socket_set_typeinfo(ntree, sock, stype); + + sock->limit = (in_out == SOCK_IN ? 1 : 0xFFF); + + BLI_strncpy(sock->identifier, identifier.data(), sizeof(sock->identifier)); + BLI_strncpy(sock->name, name.data(), sizeof(sock->name)); + sock->storage = nullptr; + sock->flag |= SOCK_COLLAPSED; + + return sock; +} + +/** + * Socket interface reconstruction for forward compatibility. + * To enable previous Blender versions to read the new interface DNA data, + * construct the bNodeSocket inputs/outputs lists. + * This discards any information about panels and alternating input/output order, + * but all functional information is preserved for executing node trees. + */ +static void construct_interface_as_legacy_sockets(bNodeTree *ntree) +{ + BLI_assert(BLI_listbase_is_empty(&ntree->inputs_legacy)); + BLI_assert(BLI_listbase_is_empty(&ntree->outputs_legacy)); + + auto make_legacy_socket = [&](const bNodeTreeInterfaceSocket &socket, + eNodeSocketInOut in_out) -> bNodeSocket * { + bNodeSocket *iosock = make_socket( + ntree, in_out, socket.socket_type, socket.name, socket.identifier); + if (!iosock) { + return nullptr; + } + + if (socket.description) { + BLI_strncpy(iosock->description, socket.description, sizeof(iosock->description)); + } + node_socket_copy_default_value_data( + eNodeSocketDatatype(iosock->typeinfo->type), iosock->default_value, socket.socket_data); + if (socket.properties) { + iosock->prop = IDP_CopyProperty(socket.properties); + } + SET_FLAG_FROM_TEST( + iosock->flag, socket.flag & NODE_INTERFACE_SOCKET_HIDE_VALUE, SOCK_HIDE_VALUE); + SET_FLAG_FROM_TEST( + iosock->flag, socket.flag & NODE_INTERFACE_SOCKET_HIDE_IN_MODIFIER, SOCK_HIDE_IN_MODIFIER); + iosock->attribute_domain = socket.attribute_domain; + if (socket.default_attribute_name) { + BLI_strncpy(iosock->default_attribute_name, + socket.default_attribute_name, + sizeof(iosock->default_attribute_name)); + } + return iosock; + }; + + /* Construct inputs/outputs socket lists in the node tree. */ + ntree->tree_interface.foreach_item([&](const bNodeTreeInterfaceItem &item) { + if (const bNodeTreeInterfaceSocket *socket = + node_interface::get_item_as(&item)) + { + if (socket->flag & NODE_INTERFACE_SOCKET_INPUT) { + if (bNodeSocket *legacy_socket = make_legacy_socket(*socket, SOCK_IN)) { + BLI_addtail(&ntree->inputs_legacy, legacy_socket); + } + } + if (socket->flag & NODE_INTERFACE_SOCKET_OUTPUT) { + if (bNodeSocket *legacy_socket = make_legacy_socket(*socket, SOCK_OUT)) { + BLI_addtail(&ntree->outputs_legacy, legacy_socket); + } + } + } + return true; + }); +} + +static void write_legacy_sockets(BlendWriter *writer, bNodeTree *ntree) +{ + /* Write inputs/outputs */ + LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs_legacy) { + write_node_socket_interface(writer, sock); + } + LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs_legacy) { + write_node_socket_interface(writer, sock); + } +} + +static void legacy_socket_interface_free(bNodeSocket *sock) +{ + if (sock->prop) { + IDP_FreeProperty_ex(sock->prop, false); + } + + if (sock->default_value) { + MEM_freeN(sock->default_value); + } + if (sock->default_attribute_name) { + MEM_freeN(sock->default_attribute_name); + } + MEM_delete(sock->runtime); +} + +static void cleanup_legacy_sockets(bNodeTree *ntree) +{ + /* Clean up temporary inputs/outputs. */ + LISTBASE_FOREACH_MUTABLE (bNodeSocket *, socket, &ntree->inputs_legacy) { + legacy_socket_interface_free(socket); + MEM_freeN(socket); + } + LISTBASE_FOREACH_MUTABLE (bNodeSocket *, socket, &ntree->outputs_legacy) { + legacy_socket_interface_free(socket); + MEM_freeN(socket); + } + BLI_listbase_clear(&ntree->inputs_legacy); + BLI_listbase_clear(&ntree->outputs_legacy); +} + +} // namespace blender::bke::forward_compat + +static void write_node_socket_default_value(BlendWriter *writer, const bNodeSocket *sock) { if (sock->default_value == nullptr) { return; @@ -527,7 +655,7 @@ static void write_node_socket_default_value(BlendWriter *writer, bNodeSocket *so } } -static void write_node_socket(BlendWriter *writer, bNodeSocket *sock) +static void write_node_socket(BlendWriter *writer, const bNodeSocket *sock) { BLO_write_struct(writer, bNodeSocket, sock); @@ -541,19 +669,6 @@ static void write_node_socket(BlendWriter *writer, bNodeSocket *sock) write_node_socket_default_value(writer, sock); } -static void write_node_socket_interface(BlendWriter *writer, bNodeSocket *sock) -{ - BLO_write_struct(writer, bNodeSocket, sock); - - if (sock->prop) { - IDP_BlendWrite(writer, sock->prop); - } - - BLO_write_string(writer, sock->default_attribute_name); - - write_node_socket_default_value(writer, sock); -} - void ntreeBlendWrite(BlendWriter *writer, bNodeTree *ntree) { BKE_id_blend_write(writer, &ntree->id); @@ -579,6 +694,8 @@ void ntreeBlendWrite(BlendWriter *writer, bNodeTree *ntree) LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) { write_node_socket(writer, sock); } + BLO_write_struct_array( + writer, bNodePanelState, node->num_panel_states, node->panel_states_array); if (node->storage) { if (ELEM(ntree->type, NTREE_SHADER, NTREE_GEOMETRY) && @@ -690,14 +807,10 @@ void ntreeBlendWrite(BlendWriter *writer, bNodeTree *ntree) BLO_write_struct(writer, bNodeLink, link); } - LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs) { - write_node_socket_interface(writer, sock); + ntree->tree_interface.write(writer); + if (!BLO_write_is_undo(writer)) { + blender::bke::forward_compat::write_legacy_sockets(writer, ntree); } - LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs) { - write_node_socket_interface(writer, sock); - } - - BLO_write_struct(writer, GeometryNodeAssetTraits, ntree->geometry_node_asset_traits); BLO_write_struct_array( writer, bNestedNodeRef, ntree->nested_node_refs_num, ntree->nested_node_refs); @@ -715,9 +828,20 @@ static void ntree_blend_write(BlendWriter *writer, ID *id, const void *id_addres ntree->typeinfo = nullptr; ntree->runtime->execdata = nullptr; + if (!BLO_write_is_undo(writer)) { + /* Generate legacy inputs/outputs socket ListBase for forward compatibility. + * Note: this has to happen before writing the ntree struct itself so that the ListBase + * first/last pointers are valid. */ + blender::bke::forward_compat::construct_interface_as_legacy_sockets(ntree); + } + BLO_write_id_struct(writer, bNodeTree, id_address, &ntree->id); ntreeBlendWrite(writer, ntree); + + if (!BLO_write_is_undo(writer)) { + blender::bke::forward_compat::cleanup_legacy_sockets(ntree); + } } static void direct_link_node_socket(BlendDataReader *reader, bNodeSocket *sock) @@ -786,6 +910,7 @@ void ntreeBlendReadData(BlendDataReader *reader, ID *owner_id, bNodeTree *ntree) BLO_read_list(reader, &node->inputs); BLO_read_list(reader, &node->outputs); + BLO_read_data_address(reader, &node->panel_states_array); BLO_read_data_address(reader, &node->prop); IDP_BlendDataRead(reader, &node->prop); @@ -911,16 +1036,18 @@ void ntreeBlendReadData(BlendDataReader *reader, ID *owner_id, bNodeTree *ntree) } } - /* interface socket lists */ - BLO_read_list(reader, &ntree->inputs); - BLO_read_list(reader, &ntree->outputs); - LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs) { + /* Read legacy interface socket lists for versioning. */ + BLO_read_list(reader, &ntree->inputs_legacy); + BLO_read_list(reader, &ntree->outputs_legacy); + LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs_legacy) { direct_link_node_socket(reader, sock); } - LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs) { + LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs_legacy) { direct_link_node_socket(reader, sock); } + ntree->tree_interface.read_data(reader); + LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) { BLO_read_data_address(reader, &link->fromnode); BLO_read_data_address(reader, &link->tonode); @@ -976,12 +1103,13 @@ static void node_tree_asset_pre_save(void *asset_ptr, AssetMetaData *asset_data) BKE_asset_metadata_idprop_ensure(asset_data, idprop::create("type", node_tree.type).release()); auto inputs = idprop::create_group("inputs"); auto outputs = idprop::create_group("outputs"); - LISTBASE_FOREACH (const bNodeSocket *, socket, &node_tree.inputs) { - auto property = idprop::create(socket->name, socket->typeinfo->idname); + node_tree.ensure_topology_cache(); + for (const bNodeTreeInterfaceSocket *socket : node_tree.interface_inputs()) { + auto property = idprop::create(socket->name, socket->socket_type); IDP_AddToGroup(inputs.get(), property.release()); } - LISTBASE_FOREACH (const bNodeSocket *, socket, &node_tree.outputs) { - auto property = idprop::create(socket->name, socket->typeinfo->idname); + for (const bNodeTreeInterfaceSocket *socket : node_tree.interface_outputs()) { + auto property = idprop::create(socket->name, socket->socket_type); IDP_AddToGroup(outputs.get(), property.release()); } BKE_asset_metadata_idprop_ensure(asset_data, inputs.release()); @@ -1221,18 +1349,6 @@ static void update_typeinfo(Main *bmain, } } } - - /* initialize tree sockets */ - LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs) { - if (socktype && STREQ(sock->idname, socktype->idname)) { - node_socket_set_typeinfo(ntree, sock, unregister ? nullptr : socktype); - } - } - LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs) { - if (socktype && STREQ(sock->idname, socktype->idname)) { - node_socket_set_typeinfo(ntree, sock, unregister ? nullptr : socktype); - } - } } FOREACH_NODETREE_END; } @@ -1253,13 +1369,6 @@ void ntreeSetTypes(const bContext *C, bNodeTree *ntree) blender::bke::node_socket_set_typeinfo(ntree, sock, nodeSocketTypeFind(sock->idname)); } } - - LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs) { - blender::bke::node_socket_set_typeinfo(ntree, sock, nodeSocketTypeFind(sock->idname)); - } - LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs) { - blender::bke::node_socket_set_typeinfo(ntree, sock, nodeSocketTypeFind(sock->idname)); - } } namespace blender::bke { @@ -1424,7 +1533,7 @@ GHashIterator *nodeTypeGetIterator() bNodeSocketType *nodeSocketTypeFind(const char *idname) { - if (idname[0]) { + if (idname && idname[0]) { bNodeSocketType *st = static_cast( BLI_ghash_lookup(blender::bke::nodesockettypes_hash, idname)); if (st) { @@ -1864,81 +1973,81 @@ const char *nodeStaticSocketType(const int type, const int subtype) return nullptr; } -const char *nodeStaticSocketInterfaceType(const int type, const int subtype) +const char *nodeStaticSocketInterfaceTypeNew(const int type, const int subtype) { switch (eNodeSocketDatatype(type)) { case SOCK_FLOAT: switch (PropertySubType(subtype)) { case PROP_UNSIGNED: - return "NodeSocketInterfaceFloatUnsigned"; + return "NodeTreeInterfaceSocketFloatUnsigned"; case PROP_PERCENTAGE: - return "NodeSocketInterfaceFloatPercentage"; + return "NodeTreeInterfaceSocketFloatPercentage"; case PROP_FACTOR: - return "NodeSocketInterfaceFloatFactor"; + return "NodeTreeInterfaceSocketFloatFactor"; case PROP_ANGLE: - return "NodeSocketInterfaceFloatAngle"; + return "NodeTreeInterfaceSocketFloatAngle"; case PROP_TIME: - return "NodeSocketInterfaceFloatTime"; + return "NodeTreeInterfaceSocketFloatTime"; case PROP_TIME_ABSOLUTE: - return "NodeSocketInterfaceFloatTimeAbsolute"; + return "NodeTreeInterfaceSocketFloatTimeAbsolute"; case PROP_DISTANCE: - return "NodeSocketInterfaceFloatDistance"; + return "NodeTreeInterfaceSocketFloatDistance"; case PROP_NONE: default: - return "NodeSocketInterfaceFloat"; + return "NodeTreeInterfaceSocketFloat"; } case SOCK_INT: switch (PropertySubType(subtype)) { case PROP_UNSIGNED: - return "NodeSocketInterfaceIntUnsigned"; + return "NodeTreeInterfaceSocketIntUnsigned"; case PROP_PERCENTAGE: - return "NodeSocketInterfaceIntPercentage"; + return "NodeTreeInterfaceSocketIntPercentage"; case PROP_FACTOR: - return "NodeSocketInterfaceIntFactor"; + return "NodeTreeInterfaceSocketIntFactor"; case PROP_NONE: default: - return "NodeSocketInterfaceInt"; + return "NodeTreeInterfaceSocketInt"; } case SOCK_BOOLEAN: - return "NodeSocketInterfaceBool"; + return "NodeTreeInterfaceSocketBool"; case SOCK_ROTATION: - return "NodeSocketInterfaceRotation"; + return "NodeTreeInterfaceSocketRotation"; case SOCK_VECTOR: switch (PropertySubType(subtype)) { case PROP_TRANSLATION: - return "NodeSocketInterfaceVectorTranslation"; + return "NodeTreeInterfaceSocketVectorTranslation"; case PROP_DIRECTION: - return "NodeSocketInterfaceVectorDirection"; + return "NodeTreeInterfaceSocketVectorDirection"; case PROP_VELOCITY: - return "NodeSocketInterfaceVectorVelocity"; + return "NodeTreeInterfaceSocketVectorVelocity"; case PROP_ACCELERATION: - return "NodeSocketInterfaceVectorAcceleration"; + return "NodeTreeInterfaceSocketVectorAcceleration"; case PROP_EULER: - return "NodeSocketInterfaceVectorEuler"; + return "NodeTreeInterfaceSocketVectorEuler"; case PROP_XYZ: - return "NodeSocketInterfaceVectorXYZ"; + return "NodeTreeInterfaceSocketVectorXYZ"; case PROP_NONE: default: - return "NodeSocketInterfaceVector"; + return "NodeTreeInterfaceSocketVector"; } case SOCK_RGBA: - return "NodeSocketInterfaceColor"; + return "NodeTreeInterfaceSocketColor"; case SOCK_STRING: - return "NodeSocketInterfaceString"; + return "NodeTreeInterfaceSocketString"; case SOCK_SHADER: - return "NodeSocketInterfaceShader"; + return "NodeTreeInterfaceSocketShader"; case SOCK_OBJECT: - return "NodeSocketInterfaceObject"; + return "NodeTreeInterfaceSocketObject"; case SOCK_IMAGE: - return "NodeSocketInterfaceImage"; + return "NodeTreeInterfaceSocketImage"; case SOCK_GEOMETRY: - return "NodeSocketInterfaceGeometry"; + return "NodeTreeInterfaceSocketGeometry"; case SOCK_COLLECTION: - return "NodeSocketInterfaceCollection"; + return "NodeTreeInterfaceSocketCollection"; case SOCK_TEXTURE: - return "NodeSocketInterfaceTexture"; + return "NodeTreeInterfaceSocketTexture"; case SOCK_MATERIAL: - return "NodeSocketInterfaceMaterial"; + return "NodeTreeInterfaceSocketMaterial"; case SOCK_CUSTOM: break; } @@ -2387,6 +2496,9 @@ bNode *node_copy_with_mapping(bNodeTree *dst_tree, node_dst->prop = IDP_CopyProperty_ex(node_src.prop, flag); } + node_dst->panel_states_array = static_cast( + MEM_dupallocN(node_src.panel_states_array)); + node_dst->runtime->internal_links = node_src.runtime->internal_links; for (bNodeLink &dst_link : node_dst->runtime->internal_links) { dst_link.fromnode = node_dst; @@ -3149,6 +3261,8 @@ void node_free_node(bNodeTree *ntree, bNode *node) MEM_freeN(sock); } + MEM_SAFE_FREE(node->panel_states_array); + if (node->prop) { /* Remember, no ID user refcount management here! */ IDP_FreePropertyContent_ex(node->prop, false); @@ -3248,26 +3362,6 @@ void nodeRemoveNode(Main *bmain, bNodeTree *ntree, bNode *node, const bool do_id namespace blender::bke { -static void node_socket_interface_free(bNodeTree * /*ntree*/, - bNodeSocket *sock, - const bool do_id_user) -{ - if (sock->prop) { - IDP_FreeProperty_ex(sock->prop, do_id_user); - } - - if (sock->default_value) { - if (do_id_user) { - socket_id_user_decrement(sock); - } - MEM_freeN(sock->default_value); - } - if (sock->default_attribute_name) { - MEM_freeN(sock->default_attribute_name); - } - MEM_delete(sock->runtime); -} - static void free_localized_node_groups(bNodeTree *ntree) { /* Only localized node trees store a copy for each node group tree. @@ -3466,152 +3560,6 @@ void ntreeLocalMerge(Main *bmain, bNodeTree *localtree, bNodeTree *ntree) } } -/* ************ NODE TREE INTERFACE *************** */ - -static bNodeSocket *make_socket_interface(bNodeTree *ntree, - const eNodeSocketInOut in_out, - const char *idname, - const char *name) -{ - bNodeSocketType *stype = nodeSocketTypeFind(idname); - if (stype == nullptr) { - return nullptr; - } - - bNodeSocket *sock = MEM_cnew("socket template"); - sock->runtime = MEM_new(__func__); - STRNCPY(sock->idname, stype->idname); - sock->in_out = int(in_out); - sock->type = int(SOCK_CUSTOM); /* int type undefined by default */ - node_socket_set_typeinfo(ntree, sock, stype); - - /* assign new unique index */ - const int own_index = ntree->cur_index++; - /* use the own_index as socket identifier */ - if (in_out == SOCK_IN) { - SNPRINTF(sock->identifier, "Input_%d", own_index); - } - else { - SNPRINTF(sock->identifier, "Output_%d", own_index); - } - - sock->limit = (in_out == SOCK_IN ? 1 : 0xFFF); - - STRNCPY(sock->name, name); - sock->storage = nullptr; - sock->flag |= SOCK_COLLAPSED; - - return sock; -} - -bNodeSocket *ntreeFindSocketInterface(bNodeTree *ntree, - const eNodeSocketInOut in_out, - const char *identifier) -{ - ListBase *sockets = (in_out == SOCK_IN) ? &ntree->inputs : &ntree->outputs; - LISTBASE_FOREACH (bNodeSocket *, iosock, sockets) { - if (STREQ(iosock->identifier, identifier)) { - return iosock; - } - } - return nullptr; -} - -} // namespace blender::bke - -bNodeSocket *ntreeAddSocketInterface(bNodeTree *ntree, - const eNodeSocketInOut in_out, - const char *idname, - const char *name) -{ - bNodeSocket *iosock = blender::bke::make_socket_interface(ntree, in_out, idname, name); - if (in_out == SOCK_IN) { - BLI_addtail(&ntree->inputs, iosock); - } - else if (in_out == SOCK_OUT) { - BLI_addtail(&ntree->outputs, iosock); - } - - BKE_ntree_update_tag_interface(ntree); - return iosock; -} - -namespace blender::bke { - -bNodeSocket *ntreeInsertSocketInterface(bNodeTree *ntree, - const eNodeSocketInOut in_out, - const char *idname, - bNodeSocket *next_sock, - const char *name) -{ - bNodeSocket *iosock = make_socket_interface(ntree, in_out, idname, name); - if (in_out == SOCK_IN) { - BLI_insertlinkbefore(&ntree->inputs, next_sock, iosock); - } - else if (in_out == SOCK_OUT) { - BLI_insertlinkbefore(&ntree->outputs, next_sock, iosock); - } - - BKE_ntree_update_tag_interface(ntree); - return iosock; -} - -bNodeSocket *ntreeAddSocketInterfaceFromSocket(bNodeTree *ntree, - const bNode *from_node, - const bNodeSocket *from_sock) -{ - return ntreeAddSocketInterfaceFromSocketWithName( - ntree, from_node, from_sock, from_sock->idname, from_sock->name); -} - -bNodeSocket *ntreeAddSocketInterfaceFromSocketWithName(bNodeTree *ntree, - const bNode *from_node, - const bNodeSocket *from_sock, - const char *idname, - const char *name) -{ - bNodeSocket *iosock = ntreeAddSocketInterface( - ntree, eNodeSocketInOut(from_sock->in_out), idname, DATA_(name)); - if (iosock == nullptr) { - return nullptr; - } - if (iosock->typeinfo->interface_from_socket) { - iosock->typeinfo->interface_from_socket(ntree, iosock, from_node, from_sock); - } - return iosock; -} - -bNodeSocket *ntreeInsertSocketInterfaceFromSocket(bNodeTree *ntree, - bNodeSocket *next_sock, - const bNode *from_node, - const bNodeSocket *from_sock) -{ - bNodeSocket *iosock = ntreeInsertSocketInterface( - ntree, eNodeSocketInOut(from_sock->in_out), from_sock->idname, next_sock, from_sock->name); - if (iosock) { - if (iosock->typeinfo->interface_from_socket) { - iosock->typeinfo->interface_from_socket(ntree, iosock, from_node, from_sock); - } - } - return iosock; -} - -} // namespace blender::bke - -void ntreeRemoveSocketInterface(bNodeTree *ntree, bNodeSocket *sock) -{ - /* this is fast, this way we don't need an in_out argument */ - BLI_remlink(&ntree->inputs, sock); - BLI_remlink(&ntree->outputs, sock); - - blender::bke::node_socket_interface_free(ntree, sock, true); - MEM_freeN(sock); - - BKE_ntree_update_tag_interface(ntree); -} - -namespace blender::bke { - static bool ntree_contains_tree_exec(const bNodeTree *tree_to_search_in, const bNodeTree *tree_to_search_for, Set &already_passed) @@ -3755,7 +3703,7 @@ int nodeSocketLinkLimit(const bNodeSocket *sock) namespace blender::bke { static void update_socket_declarations(ListBase *sockets, - Span declarations) + Span declarations) { int index; LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, sockets, index) { diff --git a/source/blender/blenkernel/intern/node_runtime.cc b/source/blender/blenkernel/intern/node_runtime.cc index 29851ff5682..1cebf7f0d20 100644 --- a/source/blender/blenkernel/intern/node_runtime.cc +++ b/source/blender/blenkernel/intern/node_runtime.cc @@ -24,11 +24,12 @@ void preprocess_geometry_node_tree_for_evaluation(bNodeTree &tree_cow) blender::nodes::ensure_geometry_nodes_lazy_function_graph(tree_cow); } -static void update_interface_sockets(const bNodeTree &ntree) +static void update_interface(const bNodeTree &ntree) { bNodeTreeRuntime &tree_runtime = *ntree.runtime; - tree_runtime.interface_inputs = ntree.inputs; - tree_runtime.interface_outputs = ntree.outputs; + /* const_cast needed because the cache stores mutable item pointers, but needs a mutable + * interface in order to get them. The interface itself is not modified here. */ + tree_runtime.interface_cache.rebuild(const_cast(ntree.tree_interface)); } static void update_node_vector(const bNodeTree &ntree) @@ -92,6 +93,15 @@ static void update_socket_vectors_and_owner_node(const bNodeTree &ntree) } } +static void update_panels(const bNodeTree &ntree) +{ + bNodeTreeRuntime &tree_runtime = *ntree.runtime; + for (bNode *node : tree_runtime.nodes_by_id) { + bNodeRuntime &node_runtime = *node->runtime; + node_runtime.panels.reinitialize(node->num_panel_states); + } +} + static void update_internal_link_inputs(const bNodeTree &ntree) { bNodeTreeRuntime &tree_runtime = *ntree.runtime; @@ -528,10 +538,11 @@ static void ensure_topology_cache(const bNodeTree &ntree) { bNodeTreeRuntime &tree_runtime = *ntree.runtime; tree_runtime.topology_cache_mutex.ensure([&]() { - update_interface_sockets(ntree); + update_interface(ntree); update_node_vector(ntree); update_link_vector(ntree); update_socket_vectors_and_owner_node(ntree); + update_panels(ntree); update_internal_link_inputs(ntree); update_directly_linked_links_and_sockets(ntree); update_nodes_by_type(ntree); diff --git a/source/blender/blenkernel/intern/node_tree_anonymous_attributes.cc b/source/blender/blenkernel/intern/node_tree_anonymous_attributes.cc index 5e06978119b..4d6fa828f2f 100644 --- a/source/blender/blenkernel/intern/node_tree_anonymous_attributes.cc +++ b/source/blender/blenkernel/intern/node_tree_anonymous_attributes.cc @@ -19,9 +19,9 @@ namespace blender::bke::anonymous_attribute_inferencing { namespace aal = nodes::aal; using nodes::NodeDeclaration; -static bool is_possible_field_socket(const bNodeSocket &socket) +static bool is_possible_field_socket(const eNodeSocketDatatype type) { - return ELEM(socket.type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_BOOLEAN, SOCK_INT); + return ELEM(type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_BOOLEAN, SOCK_INT); } static bool socket_is_field(const bNodeSocket &socket) @@ -187,7 +187,7 @@ class bNodeTreeToDotOptionsForAnonymousAttributeInferencing : public bNodeTreeTo ss << "]"; return ss.str(); } - else if (is_possible_field_socket(socket)) { + else if (is_possible_field_socket(eNodeSocketDatatype(socket.type))) { std::stringstream ss; ss << socket.identifier << " ["; bits::foreach_1_index(result_.propagated_fields_by_socket[socket.index_in_tree()], @@ -212,11 +212,13 @@ static AnonymousAttributeInferencingResult analyse_anonymous_attribute_usages( /* Find input field and geometry sources. */ for (const int i : tree.interface_inputs().index_range()) { - const bNodeSocket &interface_socket = *tree.interface_inputs()[i]; - if (interface_socket.type == SOCK_GEOMETRY) { + const bNodeTreeInterfaceSocket &interface_socket = *tree.interface_inputs()[i]; + const bNodeSocketType *typeinfo = nodeSocketTypeFind(interface_socket.socket_type); + const eNodeSocketDatatype type = typeinfo ? eNodeSocketDatatype(typeinfo->type) : SOCK_CUSTOM; + if (type == SOCK_GEOMETRY) { all_geometry_sources.append_and_get_index({InputGeometrySource{i}}); } - else if (is_possible_field_socket(interface_socket)) { + else if (is_possible_field_socket(type)) { all_field_sources.append_and_get_index({InputFieldSource{i}}); } } @@ -368,7 +370,7 @@ static AnonymousAttributeInferencingResult analyse_anonymous_attribute_usages( for (const int field_source_index : geometry_source.field_sources) { for (const bNodeSocket *other_socket : group_output_node->input_sockets().drop_back(1)) { - if (!is_possible_field_socket(*other_socket)) { + if (!is_possible_field_socket(eNodeSocketDatatype(other_socket->type))) { continue; } if (propagated_fields_by_socket[other_socket->index_in_tree()][field_source_index] @@ -383,7 +385,7 @@ static AnonymousAttributeInferencingResult analyse_anonymous_attribute_usages( } }); } - else if (is_possible_field_socket(*socket)) { + else if (is_possible_field_socket(eNodeSocketDatatype(socket->type))) { const BoundedBitSpan propagated_fields = propagated_fields_by_socket[socket->index_in_tree()]; bits::foreach_1_index(propagated_fields, [&](const int field_source_index) { @@ -450,9 +452,13 @@ static AnonymousAttributeInferencingResult analyse_anonymous_attribute_usages( required_fields_by_geometry_socket.all_bits() &= available_fields_by_geometry_socket.all_bits(); /* Create #EvalRelation for the tree. */ + tree.ensure_topology_cache(); + for (const int interface_i : tree.interface_inputs().index_range()) { - const bNodeSocket &interface_socket = *tree.interface_inputs()[interface_i]; - if (interface_socket.type != SOCK_GEOMETRY) { + const bNodeTreeInterfaceSocket &interface_socket = *tree.interface_inputs()[interface_i]; + const bNodeSocketType *typeinfo = interface_socket.socket_typeinfo(); + eNodeSocketDatatype socket_type = typeinfo ? eNodeSocketDatatype(typeinfo->type) : SOCK_CUSTOM; + if (socket_type != SOCK_GEOMETRY) { continue; } BitVector<> required_fields(all_field_sources.size(), false); diff --git a/source/blender/blenkernel/intern/node_tree_field_inferencing.cc b/source/blender/blenkernel/intern/node_tree_field_inferencing.cc index 4cebc822171..97c872de5b8 100644 --- a/source/blender/blenkernel/intern/node_tree_field_inferencing.cc +++ b/source/blender/blenkernel/intern/node_tree_field_inferencing.cc @@ -500,9 +500,12 @@ static void determine_group_input_states( { { /* Non-field inputs never support fields. */ - int index; - LISTBASE_FOREACH_INDEX (bNodeSocket *, group_input, &tree.inputs, index) { - if (!is_field_socket_type((eNodeSocketDatatype)group_input->type)) { + for (const int index : tree.interface_inputs().index_range()) { + const bNodeTreeInterfaceSocket *group_input = tree.interface_inputs()[index]; + const bNodeSocketType *typeinfo = group_input->socket_typeinfo(); + const eNodeSocketDatatype type = typeinfo ? eNodeSocketDatatype(typeinfo->type) : + SOCK_CUSTOM; + if (!is_field_socket_type(type)) { new_inferencing_interface.inputs[index] = InputSocketFieldType::None; } } @@ -695,9 +698,9 @@ bool update_field_inferencing(const bNodeTree &tree) /* Create new inferencing interface for this node group. */ std::unique_ptr new_inferencing_interface = std::make_unique(); - new_inferencing_interface->inputs.resize(BLI_listbase_count(&tree.inputs), + new_inferencing_interface->inputs.resize(tree.interface_inputs().size(), InputSocketFieldType::IsSupported); - new_inferencing_interface->outputs.resize(BLI_listbase_count(&tree.outputs), + new_inferencing_interface->outputs.resize(tree.interface_outputs().size(), OutputFieldDependency::ForDataSource()); /* Keep track of the state of all sockets. The index into this array is #SocketRef::id(). */ diff --git a/source/blender/blenkernel/intern/node_tree_interface.cc b/source/blender/blenkernel/intern/node_tree_interface.cc index 335c7c3c3e7..b62838093d2 100644 --- a/source/blender/blenkernel/intern/node_tree_interface.cc +++ b/source/blender/blenkernel/intern/node_tree_interface.cc @@ -9,7 +9,6 @@ #include "BKE_node_tree_interface.hh" #include "BLI_math_vector.h" -#include "BLI_set.hh" #include "BLI_stack.hh" #include "BLI_string.h" #include "BLI_vector.hh" @@ -21,9 +20,6 @@ #include "DNA_node_tree_interface_types.h" #include "DNA_node_types.h" -#include "RNA_access.hh" -#include "RNA_prototypes.h" - namespace blender::bke::node_interface { namespace socket_types { @@ -405,6 +401,7 @@ static void item_copy(bNodeTreeInterfaceItem &dst, BLI_assert(src_panel.name != nullptr); dst_panel.name = BLI_strdup(src_panel.name); + dst_panel.description = BLI_strdup_null(src_panel.description); dst_panel.copy_from(src_panel.items(), flag); break; } @@ -438,6 +435,7 @@ static void item_free(bNodeTreeInterfaceItem &item, const bool do_id_user) panel.clear(do_id_user); MEM_SAFE_FREE(panel.name); + MEM_SAFE_FREE(panel.description); break; } } @@ -453,6 +451,7 @@ static void item_write_data(BlendWriter *writer, bNodeTreeInterfaceItem &item) case NODE_INTERFACE_SOCKET: { bNodeTreeInterfaceSocket &socket = reinterpret_cast(item); BLO_write_string(writer, socket.name); + BLO_write_string(writer, socket.identifier); BLO_write_string(writer, socket.description); BLO_write_string(writer, socket.socket_type); BLO_write_string(writer, socket.default_attribute_name); @@ -466,6 +465,7 @@ static void item_write_data(BlendWriter *writer, bNodeTreeInterfaceItem &item) case NODE_INTERFACE_PANEL: { bNodeTreeInterfacePanel &panel = reinterpret_cast(item); BLO_write_string(writer, panel.name); + BLO_write_string(writer, panel.description); BLO_write_pointer_array(writer, panel.items_num, panel.items_array); for (bNodeTreeInterfaceItem *child_item : panel.items()) { item_write_struct(writer, *child_item); @@ -510,6 +510,7 @@ static void item_read_data(BlendDataReader *reader, bNodeTreeInterfaceItem &item case NODE_INTERFACE_PANEL: { bNodeTreeInterfacePanel &panel = reinterpret_cast(item); BLO_read_data_address(reader, &panel.name); + BLO_read_data_address(reader, &panel.description); BLO_read_pointer_array(reader, reinterpret_cast(&panel.items_array)); for (const int i : blender::IndexRange(panel.items_num)) { BLO_read_data_address(reader, &panel.items_array[i]); @@ -575,12 +576,14 @@ bNodeSocketType *bNodeTreeInterfaceSocket::socket_typeinfo() const blender::ColorGeometry4f bNodeTreeInterfaceSocket::socket_color() const { bNodeSocketType *typeinfo = this->socket_typeinfo(); - if (!typeinfo || !typeinfo->draw_color) { + if (!typeinfo || !typeinfo->draw_color_simple) { return blender::ColorGeometry4f(1.0f, 0.0f, 1.0f, 1.0f); } float color[4]; - typeinfo->draw_color(nullptr, nullptr, nullptr, color); + if (typeinfo->draw_color_simple) { + typeinfo->draw_color_simple(typeinfo, color); + } return blender::ColorGeometry4f(color); } @@ -723,21 +726,63 @@ bNodeTreeInterfacePanel *bNodeTreeInterfacePanel::find_parent_recursive( return nullptr; } +int bNodeTreeInterfacePanel::find_valid_insert_position_for_item( + const bNodeTreeInterfaceItem &item, const int initial_pos) const +{ + const bool sockets_above_panels = !(this->flag & + NODE_INTERFACE_PANEL_ALLOW_SOCKETS_AFTER_PANELS); + const blender::Span items = this->items(); + + int pos = initial_pos; + + if (sockets_above_panels) { + if (item.item_type == NODE_INTERFACE_PANEL) { + /* Find the closest valid position from the end, only panels at or after #position. */ + for (int test_pos = items.size() - 1; test_pos >= initial_pos; test_pos--) { + if (test_pos < 0) { + /* Initial position is out of range but valid. */ + break; + } + if (items[test_pos]->item_type != NODE_INTERFACE_PANEL) { + /* Found valid position, insert after the last socket item. */ + pos = test_pos + 1; + break; + } + } + } + else { + /* Find the closest valid position from the start, no panels at or after #position. */ + for (int test_pos = 0; test_pos <= initial_pos; test_pos++) { + if (test_pos >= items.size()) { + /* Initial position is out of range but valid. */ + break; + } + if (items[test_pos]->item_type == NODE_INTERFACE_PANEL) { + /* Found valid position, insering moves the first panel. */ + pos = test_pos; + break; + } + } + } + } + + return pos; +} + void bNodeTreeInterfacePanel::add_item(bNodeTreeInterfaceItem &item) { - blender::MutableSpan old_items = this->items(); - items_num++; - items_array = MEM_cnew_array(items_num, __func__); - this->items().drop_back(1).copy_from(old_items); - this->items().last() = &item; - - if (old_items.data()) { - MEM_freeN(old_items.data()); - } + /* Same as inserting at the end. */ + insert_item(item, this->items_num); } void bNodeTreeInterfacePanel::insert_item(bNodeTreeInterfaceItem &item, int position) { + /* Are child panels allowed? */ + BLI_assert(item.item_type != NODE_INTERFACE_PANEL || + (this->flag & NODE_INTERFACE_PANEL_ALLOW_CHILD_PANELS)); + + /* Apply any constraints on the item positions. */ + position = find_valid_insert_position_for_item(item, position); position = std::min(std::max(position, 0), items_num); blender::MutableSpan old_items = this->items(); @@ -752,7 +797,7 @@ void bNodeTreeInterfacePanel::insert_item(bNodeTreeInterfaceItem &item, int posi } } -bool bNodeTreeInterfacePanel::remove_item(bNodeTreeInterfaceItem &item, bool free) +bool bNodeTreeInterfacePanel::remove_item(bNodeTreeInterfaceItem &item, const bool free) { const int position = this->item_position(item); if (!this->items().index_range().contains(position)) { @@ -785,26 +830,28 @@ void bNodeTreeInterfacePanel::clear(bool do_id_user) items_num = 0; } -bool bNodeTreeInterfacePanel::move_item(bNodeTreeInterfaceItem &item, const int new_position) +bool bNodeTreeInterfacePanel::move_item(bNodeTreeInterfaceItem &item, int new_position) { const int old_position = this->item_position(item); - if (!this->items().index_range().contains(old_position) || - !this->items().index_range().contains(new_position)) - { + if (!this->items().index_range().contains(old_position)) { return false; } - if (old_position == new_position) { /* Nothing changes. */ return true; } - else if (old_position < new_position) { + + new_position = find_valid_insert_position_for_item(item, new_position); + new_position = std::min(std::max(new_position, 0), items_num); + + if (old_position < new_position) { + /* Actual target position and all existing items shifted by 1. */ const blender::Span moved_items = this->items().slice( - old_position + 1, new_position - old_position); + old_position + 1, new_position - old_position - 1); bNodeTreeInterfaceItem *tmp = this->items()[old_position]; std::copy( moved_items.begin(), moved_items.end(), this->items().drop_front(old_position).data()); - this->items()[new_position] = tmp; + this->items()[new_position - 1] = tmp; } else /* old_position > new_position */ { const blender::Span moved_items = this->items().slice( @@ -893,7 +940,7 @@ static bNodeTreeInterfaceSocket *make_socket(const int uid, blender::StringRefNull name, blender::StringRefNull description, blender::StringRefNull socket_type, - const eNodeTreeInterfaceSocketFlag flag) + const NodeTreeInterfaceSocketFlag flag) { BLI_assert(name.c_str() != nullptr); BLI_assert(socket_type.c_str() != nullptr); @@ -919,14 +966,19 @@ static bNodeTreeInterfaceSocket *make_socket(const int uid, return new_socket; } -static bNodeTreeInterfacePanel *make_panel(const int uid, blender::StringRefNull name) +static bNodeTreeInterfacePanel *make_panel(const int uid, + blender::StringRefNull name, + blender::StringRefNull description, + const NodeTreeInterfacePanelFlag flag) { BLI_assert(name.c_str() != nullptr); bNodeTreeInterfacePanel *new_panel = MEM_cnew(__func__); new_panel->item.item_type = NODE_INTERFACE_PANEL; new_panel->name = BLI_strdup(name.c_str()); + new_panel->description = BLI_strdup_null(description.c_str()); new_panel->identifier = uid; + new_panel->flag = flag; return new_panel; } @@ -939,11 +991,19 @@ void bNodeTreeInterfacePanel::copy_from( /* Copy buffers. */ for (const int i : items_src.index_range()) { const bNodeTreeInterfaceItem *item_src = items_src[i]; + BLI_assert(item_src->item_type != NODE_INTERFACE_PANEL || + (flag & NODE_INTERFACE_PANEL_ALLOW_CHILD_PANELS)); items_array[i] = static_cast(MEM_dupallocN(item_src)); item_types::item_copy(*items_array[i], *item_src, flag); } } +void bNodeTreeInterface::init_data() +{ + /* Root panel is allowed to contain child panels. */ + root_panel.flag |= NODE_INTERFACE_PANEL_ALLOW_CHILD_PANELS; +} + void bNodeTreeInterface::copy_data(const bNodeTreeInterface &src, int flag) { this->root_panel.copy_from(src.root_panel.items(), flag); @@ -1015,7 +1075,7 @@ void bNodeTreeInterface::active_item_set(bNodeTreeInterfaceItem *item) bNodeTreeInterfaceSocket *bNodeTreeInterface::add_socket(blender::StringRefNull name, blender::StringRefNull description, blender::StringRefNull socket_type, - const eNodeTreeInterfaceSocketFlag flag, + const NodeTreeInterfaceSocketFlag flag, bNodeTreeInterfacePanel *parent) { if (parent == nullptr) { @@ -1031,13 +1091,12 @@ bNodeTreeInterfaceSocket *bNodeTreeInterface::add_socket(blender::StringRefNull return new_socket; } -bNodeTreeInterfaceSocket *bNodeTreeInterface::insert_socket( - blender::StringRefNull name, - blender::StringRefNull description, - blender::StringRefNull socket_type, - const eNodeTreeInterfaceSocketFlag flag, - bNodeTreeInterfacePanel *parent, - const int position) +bNodeTreeInterfaceSocket *bNodeTreeInterface::insert_socket(blender::StringRefNull name, + blender::StringRefNull description, + blender::StringRefNull socket_type, + const NodeTreeInterfaceSocketFlag flag, + bNodeTreeInterfacePanel *parent, + const int position) { if (parent == nullptr) { parent = &root_panel; @@ -1053,6 +1112,8 @@ bNodeTreeInterfaceSocket *bNodeTreeInterface::insert_socket( } bNodeTreeInterfacePanel *bNodeTreeInterface::add_panel(blender::StringRefNull name, + blender::StringRefNull description, + const NodeTreeInterfacePanelFlag flag, bNodeTreeInterfacePanel *parent) { if (parent == nullptr) { @@ -1060,7 +1121,12 @@ bNodeTreeInterfacePanel *bNodeTreeInterface::add_panel(blender::StringRefNull na } BLI_assert(this->find_item(parent->item)); - bNodeTreeInterfacePanel *new_panel = make_panel(next_uid++, name); + if (!(parent->flag & NODE_INTERFACE_PANEL_ALLOW_CHILD_PANELS)) { + /* Parent does not allow adding child panels. */ + return nullptr; + } + + bNodeTreeInterfacePanel *new_panel = make_panel(next_uid++, name, description, flag); if (new_panel) { parent->add_item(new_panel->item); } @@ -1068,6 +1134,8 @@ bNodeTreeInterfacePanel *bNodeTreeInterface::add_panel(blender::StringRefNull na } bNodeTreeInterfacePanel *bNodeTreeInterface::insert_panel(blender::StringRefNull name, + blender::StringRefNull description, + const NodeTreeInterfacePanelFlag flag, bNodeTreeInterfacePanel *parent, const int position) { @@ -1076,7 +1144,12 @@ bNodeTreeInterfacePanel *bNodeTreeInterface::insert_panel(blender::StringRefNull } BLI_assert(this->find_item(parent->item)); - bNodeTreeInterfacePanel *new_panel = make_panel(next_uid++, name); + if (!(parent->flag & NODE_INTERFACE_PANEL_ALLOW_CHILD_PANELS)) { + /* Parent does not allow adding child panels. */ + return nullptr; + } + + bNodeTreeInterfacePanel *new_panel = make_panel(next_uid++, name, description, flag); if (new_panel) { parent->insert_item(new_panel->item, position); } @@ -1092,8 +1165,11 @@ bNodeTreeInterfaceItem *bNodeTreeInterface::add_item_copy(const bNodeTreeInterfa BLI_assert(this->find_item(item)); BLI_assert(this->find_item(parent->item)); - if (parent == nullptr) { - parent = &root_panel; + if (item.item_type == NODE_INTERFACE_PANEL && + !(parent->flag & NODE_INTERFACE_PANEL_ALLOW_CHILD_PANELS)) + { + /* Parent does not allow adding child panels. */ + return nullptr; } bNodeTreeInterfaceItem *citem = static_cast(MEM_dupallocN(&item)); @@ -1113,6 +1189,13 @@ bNodeTreeInterfaceItem *bNodeTreeInterface::insert_item_copy(const bNodeTreeInte BLI_assert(this->find_item(item)); BLI_assert(this->find_item(parent->item)); + if (item.item_type == NODE_INTERFACE_PANEL && + !(parent->flag & NODE_INTERFACE_PANEL_ALLOW_CHILD_PANELS)) + { + /* Parent does not allow adding child panels. */ + return nullptr; + } + bNodeTreeInterfaceItem *citem = static_cast(MEM_dupallocN(&item)); item_types::item_copy(*citem, item, 0); parent->insert_item(*citem, position); @@ -1162,9 +1245,22 @@ bool bNodeTreeInterface::move_item_to_parent(bNodeTreeInterfaceItem &item, if (parent == nullptr) { return false; } - if (parent->remove_item(item, false)) { - new_parent->insert_item(item, new_position); - return true; + if (item.item_type == NODE_INTERFACE_PANEL && + !(new_parent->flag & NODE_INTERFACE_PANEL_ALLOW_CHILD_PANELS)) + { + /* Parent does not allow adding child panels. */ + return false; + } + if (parent == new_parent) { + return parent->move_item(item, new_position); + } + else { + /* Note: only remove and reinsert when parents different, otherwise removing the item can + * change the desired target position! */ + if (parent->remove_item(item, false)) { + new_parent->insert_item(item, new_position); + return true; + } } return false; } diff --git a/source/blender/blenkernel/intern/node_tree_zones.cc b/source/blender/blenkernel/intern/node_tree_zones.cc index a22463666dd..4986e6d5d9f 100644 --- a/source/blender/blenkernel/intern/node_tree_zones.cc +++ b/source/blender/blenkernel/intern/node_tree_zones.cc @@ -201,6 +201,7 @@ static void update_zone_border_links(const bNodeTree &tree, bNodeTreeZones &tree static std::unique_ptr discover_tree_zones(const bNodeTree &tree) { + tree.ensure_topology_cache(); if (tree.has_available_link_cycle()) { return {}; } diff --git a/source/blender/blenloader/intern/versioning_250.cc b/source/blender/blenloader/intern/versioning_250.cc index 6969f3d99ba..687a23f6ccf 100644 --- a/source/blender/blenloader/intern/versioning_250.cc +++ b/source/blender/blenloader/intern/versioning_250.cc @@ -577,7 +577,7 @@ static bNodeSocket *do_versions_node_group_add_socket_2_56_2(bNodeTree *ngroup, // if (stype->value_structsize > 0) // gsock->default_value = MEM_callocN(stype->value_structsize, "default socket value"); - BLI_addtail(in_out == SOCK_IN ? &ngroup->inputs : &ngroup->outputs, gsock); + BLI_addtail(in_out == SOCK_IN ? &ngroup->inputs_legacy : &ngroup->outputs_legacy, gsock); BKE_ntree_update_tag_interface(ngroup); @@ -2099,10 +2099,10 @@ void blo_do_versions_250(FileData *fd, Library * /*lib*/, Main *bmain) } } - LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs) { + LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs_legacy) { do_versions_socket_default_value_259(sock); } - LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs) { + LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs_legacy) { do_versions_socket_default_value_259(sock); } diff --git a/source/blender/blenloader/intern/versioning_260.cc b/source/blender/blenloader/intern/versioning_260.cc index f2c15cdd081..6fa175edc9a 100644 --- a/source/blender/blenloader/intern/versioning_260.cc +++ b/source/blender/blenloader/intern/versioning_260.cc @@ -229,10 +229,10 @@ static void do_versions_nodetree_socket_use_flags_2_62(bNodeTree *ntree) sock->flag &= ~SOCK_IS_LINKED; } } - LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs) { + LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs_legacy) { sock->flag &= ~SOCK_IS_LINKED; } - LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs) { + LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs_legacy) { sock->flag &= ~SOCK_IS_LINKED; } @@ -920,10 +920,10 @@ static void do_versions_nodetree_customnodes(bNodeTree *ntree, int /*is_group*/) } } /* tree sockets idname */ - LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs) { + LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs_legacy) { STRNCPY(sock->idname, node_socket_get_static_idname(sock)); } - LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs) { + LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs_legacy) { STRNCPY(sock->idname, node_socket_get_static_idname(sock)); } } @@ -939,10 +939,10 @@ static void do_versions_nodetree_customnodes(bNodeTree *ntree, int /*is_group*/) sock->in_out = SOCK_OUT; } } - LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs) { + LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs_legacy) { sock->in_out = SOCK_IN; } - LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs) { + LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs_legacy) { sock->in_out = SOCK_OUT; } } @@ -969,18 +969,18 @@ static void do_versions_nodetree_customnodes(bNodeTree *ntree, int /*is_group*/) sizeof(sock->identifier)); } } - LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs) { + LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs_legacy) { STRNCPY(sock->identifier, sock->name); - BLI_uniquename(&ntree->inputs, + BLI_uniquename(&ntree->inputs_legacy, sock, "socket", '.', offsetof(bNodeSocket, identifier), sizeof(sock->identifier)); } - LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs) { + LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs_legacy) { STRNCPY(sock->identifier, sock->name); - BLI_uniquename(&ntree->outputs, + BLI_uniquename(&ntree->outputs_legacy, sock, "socket", '.', @@ -2725,11 +2725,11 @@ void do_versions_after_linking_260(Main *bmain) // const float offsety = 0.0f; if (create_io_nodes) { - if (ntree->inputs.first) { + if (ntree->inputs_legacy.first) { input_node = nodeAddStaticNode(nullptr, ntree, NODE_GROUP_INPUT); } - if (ntree->outputs.first) { + if (ntree->outputs_legacy.first) { output_node = nodeAddStaticNode(nullptr, ntree, NODE_GROUP_OUTPUT); } } diff --git a/source/blender/blenloader/intern/versioning_300.cc b/source/blender/blenloader/intern/versioning_300.cc index e2d10af72ce..93509bab9e7 100644 --- a/source/blender/blenloader/intern/versioning_300.cc +++ b/source/blender/blenloader/intern/versioning_300.cc @@ -318,10 +318,10 @@ static void do_versions_idproperty_ui_data(Main *bmain) LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { version_idproperty_ui_data(node->prop); } - LISTBASE_FOREACH (bNodeSocket *, socket, &ntree->inputs) { + LISTBASE_FOREACH (bNodeSocket *, socket, &ntree->inputs_legacy) { version_idproperty_ui_data(socket->prop); } - LISTBASE_FOREACH (bNodeSocket *, socket, &ntree->outputs) { + LISTBASE_FOREACH (bNodeSocket *, socket, &ntree->outputs_legacy) { version_idproperty_ui_data(socket->prop); } } @@ -544,8 +544,11 @@ static bNodeTree *add_realize_node_tree(Main *bmain) { bNodeTree *node_tree = ntreeAddTree(bmain, "Realize Instances 2.93 Legacy", "GeometryNodeTree"); - ntreeAddSocketInterface(node_tree, SOCK_IN, "NodeSocketGeometry", "Geometry"); - ntreeAddSocketInterface(node_tree, SOCK_OUT, "NodeSocketGeometry", "Geometry"); + node_tree->tree_interface.add_socket("Geometry", + "", + "NodeSocketGeometry", + NODE_INTERFACE_SOCKET_INPUT | NODE_INTERFACE_SOCKET_OUTPUT, + nullptr); bNode *group_input = nodeAddStaticNode(nullptr, node_tree, NODE_GROUP_INPUT); group_input->locx = -400.0f; diff --git a/source/blender/blenloader/intern/versioning_400.cc b/source/blender/blenloader/intern/versioning_400.cc index 1c94741825d..3a85f1b7435 100644 --- a/source/blender/blenloader/intern/versioning_400.cc +++ b/source/blender/blenloader/intern/versioning_400.cc @@ -583,6 +583,64 @@ static void version_replace_principled_hair_model(bNodeTree *ntree) } } +static bNodeTreeInterfaceItem *legacy_socket_move_to_interface(bNodeSocket &legacy_socket, + const eNodeSocketInOut in_out) +{ + bNodeTreeInterfaceItem *new_item = static_cast( + MEM_mallocN(sizeof(bNodeTreeInterfaceSocket), __func__)); + new_item->item_type = NODE_INTERFACE_SOCKET; + bNodeTreeInterfaceSocket &new_socket = *reinterpret_cast(new_item); + + /* Move reusable data. */ + new_socket.name = BLI_strdup(legacy_socket.name); + new_socket.identifier = BLI_strdup(legacy_socket.identifier); + new_socket.description = BLI_strdup(legacy_socket.description); + new_socket.socket_type = BLI_strdup(legacy_socket.idname); + new_socket.flag = (in_out == SOCK_IN ? NODE_INTERFACE_SOCKET_INPUT : + NODE_INTERFACE_SOCKET_OUTPUT); + SET_FLAG_FROM_TEST( + new_socket.flag, legacy_socket.flag & SOCK_HIDE_VALUE, NODE_INTERFACE_SOCKET_HIDE_VALUE); + SET_FLAG_FROM_TEST(new_socket.flag, + legacy_socket.flag & SOCK_HIDE_IN_MODIFIER, + NODE_INTERFACE_SOCKET_HIDE_IN_MODIFIER); + new_socket.attribute_domain = legacy_socket.attribute_domain; + new_socket.default_attribute_name = BLI_strdup_null(legacy_socket.default_attribute_name); + new_socket.socket_data = legacy_socket.default_value; + new_socket.properties = legacy_socket.prop; + + /* Clear moved pointers in legacy data. */ + legacy_socket.default_value = nullptr; + legacy_socket.prop = nullptr; + + /* Unused data */ + MEM_delete(legacy_socket.runtime); + legacy_socket.runtime = nullptr; + + return new_item; +} + +static void versioning_convert_node_tree_socket_lists_to_interface(bNodeTree *ntree) +{ + bNodeTreeInterface &tree_interface = ntree->tree_interface; + + const int num_inputs = BLI_listbase_count(&ntree->inputs_legacy); + const int num_outputs = BLI_listbase_count(&ntree->outputs_legacy); + tree_interface.root_panel.items_num = num_inputs + num_outputs; + tree_interface.root_panel.items_array = static_cast(MEM_malloc_arrayN( + tree_interface.root_panel.items_num, sizeof(bNodeTreeInterfaceItem *), __func__)); + + /* Convert outputs first to retain old outputs/inputs ordering. */ + int index; + LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, &ntree->outputs_legacy, index) { + tree_interface.root_panel.items_array[index] = legacy_socket_move_to_interface(*socket, + SOCK_OUT); + } + LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, &ntree->inputs_legacy, index) { + tree_interface.root_panel.items_array[num_outputs + index] = legacy_socket_move_to_interface( + *socket, SOCK_IN); + } +} + void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain) { if (!MAIN_VERSION_FILE_ATLEAST(bmain, 400, 1)) { @@ -914,6 +972,29 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain) } } + if (!MAIN_VERSION_FILE_ATLEAST(bmain, 400, 20)) { + /* Convert old socket lists into new interface items. */ + FOREACH_NODETREE_BEGIN (bmain, ntree, id) { + versioning_convert_node_tree_socket_lists_to_interface(ntree); + /* Clear legacy sockets after conversion. + * Internal data pointers have been moved or freed already. */ + BLI_freelistN(&ntree->inputs_legacy); + BLI_freelistN(&ntree->outputs_legacy); + } + FOREACH_NODETREE_END; + } + else { + /* Legacy node tree sockets are created for forward compatibilty, + * but have to be freed after loading and versioning. */ + FOREACH_NODETREE_BEGIN (bmain, ntree, id) { + /* Clear legacy sockets after conversion. + * Internal data pointers have been moved or freed already. */ + BLI_freelistN(&ntree->inputs_legacy); + BLI_freelistN(&ntree->outputs_legacy); + } + FOREACH_NODETREE_END; + } + /** * Versioning code until next subversion bump goes here. * diff --git a/source/blender/compositor/realtime_compositor/intern/utilities.cc b/source/blender/compositor/realtime_compositor/intern/utilities.cc index 4f239280d16..d01564cb012 100644 --- a/source/blender/compositor/realtime_compositor/intern/utilities.cc +++ b/source/blender/compositor/realtime_compositor/intern/utilities.cc @@ -122,7 +122,7 @@ InputDescriptor input_descriptor_from_input_socket(const bNodeSocket *socket) if (!node_declaration) { return input_descriptor; } - const SocketDeclarationPtr &socket_declaration = node_declaration->inputs[socket->index()]; + const SocketDeclaration *socket_declaration = node_declaration->inputs[socket->index()]; input_descriptor.domain_priority = socket_declaration->compositor_domain_priority(); input_descriptor.expects_single_value = socket_declaration->compositor_expects_single_value(); diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index 686af9d2a3b..7ef1edeb909 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -1961,11 +1961,13 @@ void DepsgraphNodeBuilder::build_nodetree(bNodeTree *ntree) } } - LISTBASE_FOREACH (bNodeSocket *, socket, &ntree->inputs) { - build_idproperties(socket->prop); + /* Needed for interface cache. */ + ntree->ensure_topology_cache(); + for (bNodeTreeInterfaceSocket *socket : ntree->interface_inputs()) { + build_idproperties(socket->properties); } - LISTBASE_FOREACH (bNodeSocket *, socket, &ntree->outputs) { - build_idproperties(socket->prop); + for (bNodeTreeInterfaceSocket *socket : ntree->interface_outputs()) { + build_idproperties(socket->properties); } /* TODO: link from nodetree to owner_component? */ diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index 3222cd4a787..bd5978706c6 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -2938,11 +2938,11 @@ void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree) } } - LISTBASE_FOREACH (bNodeSocket *, socket, &ntree->inputs) { - build_idproperties(socket->prop); + for (bNodeTreeInterfaceSocket *socket : ntree->interface_inputs()) { + build_idproperties(socket->properties); } - LISTBASE_FOREACH (bNodeSocket *, socket, &ntree->outputs) { - build_idproperties(socket->prop); + for (bNodeTreeInterfaceSocket *socket : ntree->interface_outputs()) { + build_idproperties(socket->properties); } if (check_id_has_anim_component(&ntree->id)) { diff --git a/source/blender/editors/curves/intern/curves_add.cc b/source/blender/editors/curves/intern/curves_add.cc index 444f988086b..c940634db56 100644 --- a/source/blender/editors/curves/intern/curves_add.cc +++ b/source/blender/editors/curves/intern/curves_add.cc @@ -72,8 +72,11 @@ void ensure_surface_deformation_node_exists(bContext &C, Object &curves_ob) nmd.node_group = ntreeAddTree(bmain, DATA_("Surface Deform"), "GeometryNodeTree"); bNodeTree *ntree = nmd.node_group; - ntreeAddSocketInterface(ntree, SOCK_IN, "NodeSocketGeometry", "Geometry"); - ntreeAddSocketInterface(ntree, SOCK_OUT, "NodeSocketGeometry", "Geometry"); + ntree->tree_interface.add_socket("Geometry", + "", + "NodeSocketGeometry", + NODE_INTERFACE_SOCKET_INPUT | NODE_INTERFACE_SOCKET_OUTPUT, + nullptr); bNode *group_input = nodeAddStaticNode(&C, ntree, NODE_GROUP_INPUT); bNode *group_output = nodeAddStaticNode(&C, ntree, NODE_GROUP_OUTPUT); bNode *deform_node = nodeAddStaticNode(&C, ntree, GEO_NODE_DEFORM_CURVES_ON_SURFACE); diff --git a/source/blender/editors/geometry/node_group_operator.cc b/source/blender/editors/geometry/node_group_operator.cc index 5635816bf94..d4a82a33c97 100644 --- a/source/blender/editors/geometry/node_group_operator.cc +++ b/source/blender/editors/geometry/node_group_operator.cc @@ -386,9 +386,12 @@ static std::string run_node_group_get_description(bContext *C, static void add_attribute_search_or_value_buttons(uiLayout *layout, PointerRNA *md_ptr, - const bNodeSocket &socket) + const bNodeTreeInterfaceSocket &socket) { - char socket_id_esc[sizeof(socket.identifier) * 2]; + bNodeSocketType *typeinfo = nodeSocketTypeFind(socket.socket_type); + const eNodeSocketDatatype socket_type = eNodeSocketDatatype(typeinfo->type); + + char socket_id_esc[MAX_NAME * 2]; BLI_str_escape(socket_id_esc, socket.identifier, sizeof(socket_id_esc)); const std::string rna_path = "[\"" + std::string(socket_id_esc) + "\"]"; const std::string rna_path_use_attribute = "[\"" + std::string(socket_id_esc) + @@ -404,7 +407,7 @@ static void add_attribute_search_or_value_buttons(uiLayout *layout, uiLayoutSetAlignment(name_row, UI_LAYOUT_ALIGN_RIGHT); const bool use_attribute = RNA_boolean_get(md_ptr, rna_path_use_attribute.c_str()); - if (socket.type == SOCK_BOOLEAN && !use_attribute) { + if (socket_type == SOCK_BOOLEAN && !use_attribute) { uiItemL(name_row, "", ICON_NONE); } else { @@ -412,7 +415,7 @@ static void add_attribute_search_or_value_buttons(uiLayout *layout, } uiLayout *prop_row = uiLayoutRow(split, true); - if (socket.type == SOCK_BOOLEAN) { + if (socket_type == SOCK_BOOLEAN) { uiLayoutSetPropSep(prop_row, false); uiLayoutSetAlignment(prop_row, UI_LAYOUT_ALIGN_EXPAND); } @@ -422,7 +425,7 @@ static void add_attribute_search_or_value_buttons(uiLayout *layout, uiItemR(prop_row, md_ptr, rna_path_attribute_name.c_str(), UI_ITEM_NONE, "", ICON_NONE); } else { - const char *name = socket.type == SOCK_BOOLEAN ? socket.name : ""; + const char *name = socket_type == SOCK_BOOLEAN ? socket.name : ""; uiItemR(prop_row, md_ptr, rna_path.c_str(), UI_ITEM_NONE, name, ICON_NONE); } @@ -435,9 +438,12 @@ static void draw_property_for_socket(const bNodeTree &node_tree, IDProperty *op_properties, PointerRNA *bmain_ptr, PointerRNA *op_ptr, - const bNodeSocket &socket, + const bNodeTreeInterfaceSocket &socket, const int socket_index) { + bNodeSocketType *typeinfo = nodeSocketTypeFind(socket.socket_type); + const eNodeSocketDatatype socket_type = eNodeSocketDatatype(typeinfo->type); + /* The property should be created in #MOD_nodes_update_interface with the correct type. */ IDProperty *property = IDP_GetPropertyFromGroup(op_properties, socket.identifier); @@ -447,7 +453,7 @@ static void draw_property_for_socket(const bNodeTree &node_tree, return; } - char socket_id_esc[sizeof(socket.identifier) * 2]; + char socket_id_esc[MAX_NAME * 2]; BLI_str_escape(socket_id_esc, socket.identifier, sizeof(socket_id_esc)); char rna_path[sizeof(socket_id_esc) + 4]; @@ -459,7 +465,7 @@ static void draw_property_for_socket(const bNodeTree &node_tree, /* Use #uiItemPointerR to draw pointer properties because #uiItemR would not have enough * information about what type of ID to select for editing the values. This is because * pointer IDProperties contain no information about their type. */ - switch (socket.type) { + switch (socket_type) { case SOCK_OBJECT: uiItemPointerR(row, op_ptr, rna_path, bmain_ptr, "objects", socket.name, ICON_OBJECT_DATA); break; @@ -503,10 +509,12 @@ static void run_node_group_ui(bContext *C, wmOperator *op) return; } - int input_index; - LISTBASE_FOREACH_INDEX (bNodeSocket *, io_socket, &node_tree->inputs, input_index) { + node_tree->ensure_topology_cache(); + int input_index = 0; + for (bNodeTreeInterfaceSocket *io_socket : node_tree->interface_inputs()) { draw_property_for_socket( *node_tree, layout, op->properties, &bmain_ptr, op->ptr, *io_socket, input_index); + ++input_index; } } diff --git a/source/blender/editors/include/UI_interface_c.hh b/source/blender/editors/include/UI_interface_c.hh index 76dac42a2a6..3d18c6304e0 100644 --- a/source/blender/editors/include/UI_interface_c.hh +++ b/source/blender/editors/include/UI_interface_c.hh @@ -2583,6 +2583,8 @@ void uiTemplateLightLinkingCollection(uiLayout *layout, PointerRNA *ptr, const c void uiTemplateGreasePencilLayerTree(uiLayout *layout, bContext *C); +void uiTemplateNodeTreeInterface(struct uiLayout *layout, struct PointerRNA *ptr); + /** * \return: A RNA pointer for the operator properties. */ diff --git a/source/blender/editors/interface/CMakeLists.txt b/source/blender/editors/interface/CMakeLists.txt index 529ecf59127..d249126ca75 100644 --- a/source/blender/editors/interface/CMakeLists.txt +++ b/source/blender/editors/interface/CMakeLists.txt @@ -70,6 +70,7 @@ set(SRC interface_template_grease_pencil_layer_tree.cc interface_template_light_linking.cc interface_template_list.cc + interface_template_node_tree_interface.cc interface_template_search_menu.cc interface_template_search_operator.cc interface_templates.cc diff --git a/source/blender/editors/interface/interface_template_node_tree_interface.cc b/source/blender/editors/interface/interface_template_node_tree_interface.cc new file mode 100644 index 00000000000..1139cb0cb85 --- /dev/null +++ b/source/blender/editors/interface/interface_template_node_tree_interface.cc @@ -0,0 +1,517 @@ +/* SPDX-FileCopyrightText: 2023 Blender Foundation + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup edinterface + */ + +#include "BKE_context.h" +#include "BKE_node_tree_interface.hh" +#include "BKE_node_tree_update.h" + +#include "BLI_color.hh" +#include "BLI_string.h" + +#include "BLT_translation.h" + +#include "DNA_node_tree_interface_types.h" + +#include "ED_node.hh" + +#include "RNA_access.hh" +#include "RNA_prototypes.h" + +#include "UI_interface.hh" +#include "UI_resources.hh" +#include "UI_tree_view.hh" + +#include "WM_api.hh" + +namespace node_interface = blender::bke::node_interface; + +namespace blender::ui::nodes { + +namespace { + +class NodeTreeInterfaceView; + +class NodeTreeInterfaceDragController : public AbstractViewItemDragController { + private: + bNodeTreeInterfaceItem &item_; + + public: + explicit NodeTreeInterfaceDragController(NodeTreeInterfaceView &view, + bNodeTreeInterfaceItem &item); + virtual ~NodeTreeInterfaceDragController() = default; + + eWM_DragDataType get_drag_type() const; + + void *create_drag_data() const; +}; + +class NodeSocketDropTarget : public TreeViewItemDropTarget { + private: + bNodeTreeInterfaceSocket &socket_; + + public: + explicit NodeSocketDropTarget(NodeTreeInterfaceView &view, bNodeTreeInterfaceSocket &socket); + + bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const override; + std::string drop_tooltip(const DragInfo &drag_info) const override; + bool on_drop(struct bContext * /*C*/, const DragInfo &drag_info) const override; + + protected: + wmDragNodeTreeInterface *get_drag_node_tree_declaration(const wmDrag &drag) const; +}; + +class NodePanelDropTarget : public TreeViewItemDropTarget { + private: + bNodeTreeInterfacePanel &panel_; + + public: + explicit NodePanelDropTarget(NodeTreeInterfaceView &view, bNodeTreeInterfacePanel &panel); + + bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const override; + std::string drop_tooltip(const DragInfo &drag_info) const override; + bool on_drop(bContext *C, const DragInfo &drag_info) const override; + + protected: + wmDragNodeTreeInterface *get_drag_node_tree_declaration(const wmDrag &drag) const; +}; + +class NodeSocketViewItem : public BasicTreeViewItem { + private: + bNodeTree &nodetree_; + bNodeTreeInterfaceSocket &socket_; + + public: + NodeSocketViewItem(bNodeTree &nodetree, + bNodeTreeInterface &interface, + bNodeTreeInterfaceSocket &socket) + : BasicTreeViewItem(socket.name, ICON_NONE), nodetree_(nodetree), socket_(socket) + { + set_is_active_fn([interface, &socket]() { return interface.active_item() == &socket.item; }); + set_on_activate_fn([&interface](bContext & /*C*/, BasicTreeViewItem &new_active) { + NodeSocketViewItem &self = static_cast(new_active); + interface.active_item_set(&self.socket_.item); + }); + } + + void build_row(uiLayout &row) override + { + uiLayoutSetPropDecorate(&row, false); + + uiLayout *input_socket_layout = uiLayoutRow(&row, true); + if (socket_.flag & NODE_INTERFACE_SOCKET_INPUT) { + /* XXX Socket template only draws in embossed layouts (Julian). */ + uiLayoutSetEmboss(input_socket_layout, UI_EMBOSS); + /* Context is not used by the template function. */ + uiTemplateNodeSocket(input_socket_layout, /*C*/ nullptr, socket_.socket_color()); + } + else { + /* Blank item to align output socket labels with inputs. */ + uiItemL(input_socket_layout, "", ICON_BLANK1); + } + + add_label(row); + + uiLayout *output_socket_layout = uiLayoutRow(&row, true); + if (socket_.flag & NODE_INTERFACE_SOCKET_OUTPUT) { + /* XXX Socket template only draws in embossed layouts (Julian). */ + uiLayoutSetEmboss(output_socket_layout, UI_EMBOSS); + /* Context is not used by the template function. */ + uiTemplateNodeSocket(output_socket_layout, /*C*/ nullptr, socket_.socket_color()); + } + else { + /* Blank item to align input socket labels with outputs. */ + uiItemL(output_socket_layout, "", ICON_BLANK1); + } + } + + protected: + bool matches(const AbstractViewItem &other) const override + { + const NodeSocketViewItem *other_item = dynamic_cast(&other); + if (other_item == nullptr) { + return false; + } + + return &socket_ == &other_item->socket_; + } + + bool supports_renaming() const override + { + return true; + } + bool rename(const bContext &C, StringRefNull new_name) override + { + socket_.name = BLI_strdup(new_name.c_str()); + BKE_ntree_update_tag_interface(&nodetree_); + ED_node_tree_propagate_change(&C, CTX_data_main(&C), &nodetree_); + return true; + } + StringRef get_rename_string() const override + { + return socket_.name; + } + + std::unique_ptr create_drag_controller() const override; + std::unique_ptr create_drop_target() override; +}; + +class NodePanelViewItem : public BasicTreeViewItem { + private: + bNodeTree &nodetree_; + bNodeTreeInterfacePanel &panel_; + + public: + NodePanelViewItem(bNodeTree &nodetree, + bNodeTreeInterface &interface, + bNodeTreeInterfacePanel &panel) + : BasicTreeViewItem(panel.name, ICON_NONE), nodetree_(nodetree), panel_(panel) + { + set_is_active_fn([interface, &panel]() { return interface.active_item() == &panel.item; }); + set_on_activate_fn([&interface](bContext & /*C*/, BasicTreeViewItem &new_active) { + NodePanelViewItem &self = static_cast(new_active); + interface.active_item_set(&self.panel_.item); + }); + } + + void build_row(uiLayout &row) override + { + add_label(row); + + uiLayout *sub = uiLayoutRow(&row, true); + uiLayoutSetPropDecorate(sub, false); + } + + protected: + bool matches(const AbstractViewItem &other) const override + { + const NodePanelViewItem *other_item = dynamic_cast(&other); + if (other_item == nullptr) { + return false; + } + + return &panel_ == &other_item->panel_; + } + + bool supports_renaming() const override + { + return true; + } + bool rename(const bContext &C, StringRefNull new_name) override + { + panel_.name = BLI_strdup(new_name.c_str()); + BKE_ntree_update_tag_interface(&nodetree_); + ED_node_tree_propagate_change(&C, CTX_data_main(&C), &nodetree_); + return true; + } + StringRef get_rename_string() const override + { + return panel_.name; + } + + std::unique_ptr create_drag_controller() const override; + std::unique_ptr create_drop_target() override; +}; + +class NodeTreeInterfaceView : public AbstractTreeView { + private: + bNodeTree &nodetree_; + bNodeTreeInterface &interface_; + + public: + explicit NodeTreeInterfaceView(bNodeTree &nodetree, bNodeTreeInterface &interface) + : nodetree_(nodetree), interface_(interface) + { + } + + bNodeTree &nodetree() + { + return nodetree_; + } + + bNodeTreeInterface &interface() + { + return interface_; + } + + void build_tree() override + { + /* Draw root items */ + add_items_for_panel_recursive(interface_.root_panel, *this); + } + + protected: + void add_items_for_panel_recursive(bNodeTreeInterfacePanel &parent, + ui::TreeViewOrItem &parent_item) + { + for (bNodeTreeInterfaceItem *item : parent.items()) { + switch (item->item_type) { + case NODE_INTERFACE_SOCKET: { + bNodeTreeInterfaceSocket *socket = node_interface::get_item_as( + item); + NodeSocketViewItem &socket_item = parent_item.add_tree_item( + nodetree_, interface_, *socket); + socket_item.set_collapsed(false); + break; + } + case NODE_INTERFACE_PANEL: { + bNodeTreeInterfacePanel *panel = node_interface::get_item_as( + item); + NodePanelViewItem &panel_item = parent_item.add_tree_item( + nodetree_, interface_, *panel); + panel_item.set_collapsed(false); + add_items_for_panel_recursive(*panel, panel_item); + break; + } + } + } + } +}; + +std::unique_ptr NodeSocketViewItem::create_drag_controller() const +{ + return std::make_unique( + static_cast(get_tree_view()), socket_.item); +} + +std::unique_ptr NodeSocketViewItem::create_drop_target() +{ + return std::make_unique( + static_cast(get_tree_view()), socket_); +} + +std::unique_ptr NodePanelViewItem::create_drag_controller() const +{ + return std::make_unique( + static_cast(get_tree_view()), panel_.item); +} + +std::unique_ptr NodePanelViewItem::create_drop_target() +{ + return std::make_unique( + static_cast(get_tree_view()), panel_); +} + +NodeTreeInterfaceDragController::NodeTreeInterfaceDragController(NodeTreeInterfaceView &view, + bNodeTreeInterfaceItem &item) + : AbstractViewItemDragController(view), item_(item) +{ +} + +eWM_DragDataType NodeTreeInterfaceDragController::get_drag_type() const +{ + return WM_DRAG_NODE_TREE_INTERFACE; +} + +void *NodeTreeInterfaceDragController::create_drag_data() const +{ + wmDragNodeTreeInterface *drag_data = MEM_cnew(__func__); + drag_data->item = &item_; + return drag_data; +} + +NodeSocketDropTarget::NodeSocketDropTarget(NodeTreeInterfaceView &view, + bNodeTreeInterfaceSocket &socket) + : TreeViewItemDropTarget(view, DropBehavior::Reorder), socket_(socket) +{ +} + +bool NodeSocketDropTarget::can_drop(const wmDrag &drag, const char ** /*r_disabled_hint*/) const +{ + if (drag.type != WM_DRAG_NODE_TREE_INTERFACE) { + return false; + } + wmDragNodeTreeInterface *drag_data = get_drag_node_tree_declaration(drag); + + /* Can't drop an item onto its children. */ + if (const bNodeTreeInterfacePanel *panel = node_interface::get_item_as( + drag_data->item)) + { + if (panel->contains(socket_.item)) { + return false; + } + } + return true; +} + +std::string NodeSocketDropTarget::drop_tooltip(const DragInfo &drag_info) const +{ + switch (drag_info.drop_location) { + case DropLocation::Into: + return ""; + case DropLocation::Before: + return N_("Insert before socket"); + case DropLocation::After: + return N_("Insert after socket"); + } + return ""; +} + +bool NodeSocketDropTarget::on_drop(bContext *C, const DragInfo &drag_info) const +{ + wmDragNodeTreeInterface *drag_data = get_drag_node_tree_declaration(drag_info.drag_data); + BLI_assert(drag_data != nullptr); + bNodeTreeInterfaceItem *drag_item = drag_data->item; + BLI_assert(drag_item != nullptr); + + bNodeTree &nodetree = get_view().nodetree(); + bNodeTreeInterface &interface = get_view().interface(); + + bNodeTreeInterfacePanel *parent = interface.find_item_parent(socket_.item); + int index = -1; + + /* Insert into same panel as the target. */ + BLI_assert(parent != nullptr); + switch (drag_info.drop_location) { + case DropLocation::Before: + index = parent->items().as_span().first_index_try(&socket_.item); + break; + case DropLocation::After: + index = parent->items().as_span().first_index_try(&socket_.item) + 1; + break; + default: + /* All valid cases should be handled above. */ + BLI_assert_unreachable(); + break; + } + if (parent == nullptr || index < 0) { + return false; + } + + interface.move_item_to_parent(*drag_item, parent, index); + + /* General update */ + BKE_ntree_update_tag_interface(&nodetree); + ED_node_tree_propagate_change(C, CTX_data_main(C), &nodetree); + return true; +} + +wmDragNodeTreeInterface *NodeSocketDropTarget::get_drag_node_tree_declaration( + const wmDrag &drag) const +{ + BLI_assert(drag.type == WM_DRAG_NODE_TREE_INTERFACE); + return static_cast(drag.poin); +} + +NodePanelDropTarget::NodePanelDropTarget(NodeTreeInterfaceView &view, + bNodeTreeInterfacePanel &panel) + : TreeViewItemDropTarget(view, DropBehavior::ReorderAndInsert), panel_(panel) +{ +} + +bool NodePanelDropTarget::can_drop(const wmDrag &drag, const char ** /*r_disabled_hint*/) const +{ + if (drag.type != WM_DRAG_NODE_TREE_INTERFACE) { + return false; + } + wmDragNodeTreeInterface *drag_data = get_drag_node_tree_declaration(drag); + + /* Can't drop an item onto its children. */ + if (const bNodeTreeInterfacePanel *panel = node_interface::get_item_as( + drag_data->item)) + { + if (panel->contains(panel_.item)) { + return false; + } + } + + return true; +} + +std::string NodePanelDropTarget::drop_tooltip(const DragInfo &drag_info) const +{ + switch (drag_info.drop_location) { + case DropLocation::Into: + return "Insert into panel"; + case DropLocation::Before: + return N_("Insert before panel"); + case DropLocation::After: + return N_("Insert after panel"); + } + return ""; +} + +bool NodePanelDropTarget::on_drop(bContext *C, const DragInfo &drag_info) const +{ + wmDragNodeTreeInterface *drag_data = get_drag_node_tree_declaration(drag_info.drag_data); + BLI_assert(drag_data != nullptr); + bNodeTreeInterfaceItem *drag_item = drag_data->item; + BLI_assert(drag_item != nullptr); + + bNodeTree &nodetree = get_view().nodetree(); + bNodeTreeInterface &interface = get_view().interface(); + + bNodeTreeInterfacePanel *parent = nullptr; + int index = -1; + switch (drag_info.drop_location) { + case DropLocation::Into: { + /* Insert into target */ + parent = &panel_; + index = 0; + break; + } + case DropLocation::Before: { + /* Insert into same panel as the target. */ + parent = interface.find_item_parent(panel_.item); + BLI_assert(parent != nullptr); + index = parent->items().as_span().first_index_try(&panel_.item); + break; + } + case DropLocation::After: { + /* Insert into same panel as the target. */ + parent = interface.find_item_parent(panel_.item); + BLI_assert(parent != nullptr); + index = parent->items().as_span().first_index_try(&panel_.item) + 1; + break; + } + } + if (parent == nullptr || index < 0) { + return false; + } + + interface.move_item_to_parent(*drag_item, parent, index); + + /* General update */ + BKE_ntree_update_tag_interface(&nodetree); + ED_node_tree_propagate_change(C, CTX_data_main(C), &nodetree); + return true; +} + +wmDragNodeTreeInterface *NodePanelDropTarget::get_drag_node_tree_declaration( + const wmDrag &drag) const +{ + BLI_assert(drag.type == WM_DRAG_NODE_TREE_INTERFACE); + return static_cast(drag.poin); +} + +} // namespace + +} // namespace blender::ui::nodes + +namespace ui = blender::ui; + +void uiTemplateNodeTreeInterface(struct uiLayout *layout, struct PointerRNA *ptr) +{ + if (!ptr->data) { + return; + } + if (!RNA_struct_is_a(ptr->type, &RNA_NodeTreeInterface)) { + return; + } + bNodeTree &nodetree = *reinterpret_cast(ptr->owner_id); + bNodeTreeInterface &interface = *static_cast(ptr->data); + + uiBlock *block = uiLayoutGetBlock(layout); + + ui::AbstractTreeView *tree_view = UI_block_add_view( + *block, + "Node Tree Declaration Tree View", + std::make_unique(nodetree, interface)); + tree_view->set_min_rows(3); + + ui::TreeViewBuilder::build_tree_view(*tree_view, *layout); +} diff --git a/source/blender/editors/object/object_relations.cc b/source/blender/editors/object/object_relations.cc index fedec4736d4..f3876c2cf2b 100644 --- a/source/blender/editors/object/object_relations.cc +++ b/source/blender/editors/object/object_relations.cc @@ -70,6 +70,8 @@ #include "BKE_mesh.hh" #include "BKE_modifier.h" #include "BKE_node.h" +#include "BKE_node_runtime.hh" +#include "BKE_node_tree_interface.hh" #include "BKE_object.h" #include "BKE_pointcloud.h" #include "BKE_report.h" @@ -2964,23 +2966,32 @@ char *ED_object_ot_drop_geometry_nodes_tooltip(bContext *C, static bool check_geometry_node_group_sockets(wmOperator *op, const bNodeTree *tree) { - const bNodeSocket *first_input = (const bNodeSocket *)tree->inputs.first; - if (!first_input) { - BKE_report(op->reports, RPT_ERROR, "The node group must have a geometry input socket"); - return false; + tree->ensure_topology_cache(); + if (!tree->interface_inputs().is_empty()) { + const bNodeTreeInterfaceSocket *first_input = tree->interface_inputs()[0]; + if (!first_input) { + BKE_report(op->reports, RPT_ERROR, "The node group must have a geometry input socket"); + return false; + } + const bNodeSocketType *typeinfo = first_input->socket_typeinfo(); + const eNodeSocketDatatype type = typeinfo ? eNodeSocketDatatype(typeinfo->type) : SOCK_CUSTOM; + if (type != SOCK_GEOMETRY) { + BKE_report(op->reports, RPT_ERROR, "The first input must be a geometry socket"); + return false; + } } - if (first_input->type != SOCK_GEOMETRY) { - BKE_report(op->reports, RPT_ERROR, "The first input must be a geometry socket"); - return false; - } - const bNodeSocket *first_output = (const bNodeSocket *)tree->outputs.first; - if (!first_output) { - BKE_report(op->reports, RPT_ERROR, "The node group must have a geometry output socket"); - return false; - } - if (first_output->type != SOCK_GEOMETRY) { - BKE_report(op->reports, RPT_ERROR, "The first output must be a geometry socket"); - return false; + if (!tree->interface_outputs().is_empty()) { + const bNodeTreeInterfaceSocket *first_output = tree->interface_outputs()[0]; + if (!first_output) { + BKE_report(op->reports, RPT_ERROR, "The node group must have a geometry output socket"); + return false; + } + const bNodeSocketType *typeinfo = first_output->socket_typeinfo(); + const eNodeSocketDatatype type = typeinfo ? eNodeSocketDatatype(typeinfo->type) : SOCK_CUSTOM; + if (type != SOCK_GEOMETRY) { + BKE_report(op->reports, RPT_ERROR, "The first output must be a geometry socket"); + return false; + } } return true; } diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc index 92e43b87136..92440f6b167 100644 --- a/source/blender/editors/space_node/drawnode.cc +++ b/source/blender/editors/space_node/drawnode.cc @@ -1124,16 +1124,8 @@ static void node_socket_undefined_draw_color(bContext * /*C*/, r_color[3] = 1.0f; } -static void node_socket_undefined_interface_draw(bContext * /*C*/, - uiLayout *layout, - PointerRNA * /*ptr*/) -{ - uiItemL(layout, IFACE_("Undefined Socket Type"), ICON_ERROR); -} - -static void node_socket_undefined_interface_draw_color(bContext * /*C*/, - PointerRNA * /*ptr*/, - float *r_color) +static void node_socket_undefined_draw_color_simple(const bNodeSocketType * /*type*/, + float *r_color) { r_color[0] = 1.0f; r_color[1] = 0.0f; @@ -1141,6 +1133,14 @@ static void node_socket_undefined_interface_draw_color(bContext * /*C*/, r_color[3] = 1.0f; } +static void node_socket_undefined_interface_draw(ID * /*id*/, + bNodeTreeInterfaceSocket * /*interface_socket*/, + bContext * /*C*/, + uiLayout *layout) +{ + uiItemL(layout, IFACE_("Undefined Socket Type"), ICON_ERROR); +} + /** \} */ } // namespace blender::ed::space_node @@ -1161,8 +1161,8 @@ void ED_node_init_butfuncs() NodeSocketTypeUndefined.draw = node_socket_undefined_draw; NodeSocketTypeUndefined.draw_color = node_socket_undefined_draw_color; + NodeSocketTypeUndefined.draw_color_simple = node_socket_undefined_draw_color_simple; NodeSocketTypeUndefined.interface_draw = node_socket_undefined_interface_draw; - NodeSocketTypeUndefined.interface_draw_color = node_socket_undefined_interface_draw_color; /* node type ui functions */ NODE_TYPES_BEGIN (ntype) { @@ -1208,22 +1208,39 @@ static const float std_node_socket_colors[][4] = { {0.65, 0.39, 0.78, 1.0}, /* SOCK_ROTATION */ }; -/* common color callbacks for standard types */ -static void std_node_socket_draw_color(bContext * /*C*/, - PointerRNA *ptr, - PointerRNA * /*node_ptr*/, - float *r_color) +/* Callback for colors that does not depend on the socket pointer argument to get the type. */ +template +void std_node_socket_color_fn(bContext * /*C*/, + PointerRNA * /*ptr*/, + PointerRNA * /*node_ptr*/, + float *r_color) { - bNodeSocket *sock = (bNodeSocket *)ptr->data; - int type = sock->typeinfo->type; - copy_v4_v4(r_color, std_node_socket_colors[type]); -} -static void std_node_socket_interface_draw_color(bContext * /*C*/, PointerRNA *ptr, float *r_color) + copy_v4_v4(r_color, std_node_socket_colors[socket_type]); +}; +static void std_node_socket_color_simple_fn(const bNodeSocketType *type, float *r_color) { - bNodeSocket *sock = (bNodeSocket *)ptr->data; - int type = sock->typeinfo->type; - copy_v4_v4(r_color, std_node_socket_colors[type]); -} + copy_v4_v4(r_color, std_node_socket_colors[type->type]); +}; + +using SocketColorFn = void (*)(bContext *C, PointerRNA *ptr, PointerRNA *node_ptr, float *r_color); +/* Callbacks for all built-in socket types. */ +static const SocketColorFn std_node_socket_color_funcs[] = { + std_node_socket_color_fn, + std_node_socket_color_fn, + std_node_socket_color_fn, + std_node_socket_color_fn, + std_node_socket_color_fn, + nullptr /* UNUSED */, + std_node_socket_color_fn, + std_node_socket_color_fn, + std_node_socket_color_fn, + std_node_socket_color_fn, + std_node_socket_color_fn, + std_node_socket_color_fn, + std_node_socket_color_fn, + std_node_socket_color_fn, + std_node_socket_color_fn, +}; /* draw function for file output node sockets, * displays only sub-path and format, no value button */ @@ -1434,36 +1451,44 @@ static void std_node_socket_draw( } } -static void std_node_socket_interface_draw(bContext * /*C*/, uiLayout *layout, PointerRNA *ptr) +static void std_node_socket_interface_draw(ID *id, + bNodeTreeInterfaceSocket *interface_socket, + bContext * /*C*/, + uiLayout *layout) { - bNodeSocket *sock = (bNodeSocket *)ptr->data; - int type = sock->typeinfo->type; + PointerRNA ptr, tree_ptr; + RNA_pointer_create(id, &RNA_NodeTreeInterfaceSocket, interface_socket, &ptr); + RNA_id_pointer_create(id, &tree_ptr); - PointerRNA tree_ptr; - RNA_id_pointer_create(ptr->owner_id, &tree_ptr); + const bNodeSocketType *typeinfo = interface_socket->socket_typeinfo(); + BLI_assert(typeinfo != nullptr); + eNodeSocketDatatype type = eNodeSocketDatatype(typeinfo->type); uiLayout *col = uiLayoutColumn(layout, false); switch (type) { case SOCK_FLOAT: { - uiItemR(col, ptr, "default_value", DEFAULT_FLAGS, IFACE_("Default"), ICON_NONE); + uiItemR(col, &ptr, "subtype", DEFAULT_FLAGS, IFACE_("Subtype"), ICON_NONE); + uiItemR(col, &ptr, "default_value", DEFAULT_FLAGS, IFACE_("Default"), ICON_NONE); uiLayout *sub = uiLayoutColumn(col, true); - uiItemR(sub, ptr, "min_value", DEFAULT_FLAGS, IFACE_("Min"), ICON_NONE); - uiItemR(sub, ptr, "max_value", DEFAULT_FLAGS, IFACE_("Max"), ICON_NONE); + uiItemR(sub, &ptr, "min_value", DEFAULT_FLAGS, IFACE_("Min"), ICON_NONE); + uiItemR(sub, &ptr, "max_value", DEFAULT_FLAGS, IFACE_("Max"), ICON_NONE); break; } case SOCK_INT: { - uiItemR(col, ptr, "default_value", DEFAULT_FLAGS, IFACE_("Default"), ICON_NONE); + uiItemR(col, &ptr, "subtype", DEFAULT_FLAGS, IFACE_("Subtype"), ICON_NONE); + uiItemR(col, &ptr, "default_value", DEFAULT_FLAGS, IFACE_("Default"), ICON_NONE); uiLayout *sub = uiLayoutColumn(col, true); - uiItemR(sub, ptr, "min_value", DEFAULT_FLAGS, IFACE_("Min"), ICON_NONE); - uiItemR(sub, ptr, "max_value", DEFAULT_FLAGS, IFACE_("Max"), ICON_NONE); + uiItemR(sub, &ptr, "min_value", DEFAULT_FLAGS, IFACE_("Min"), ICON_NONE); + uiItemR(sub, &ptr, "max_value", DEFAULT_FLAGS, IFACE_("Max"), ICON_NONE); break; } case SOCK_VECTOR: { - uiItemR(col, ptr, "default_value", UI_ITEM_R_EXPAND, IFACE_("Default"), ICON_NONE); + uiItemR(col, &ptr, "subtype", DEFAULT_FLAGS, IFACE_("Subtype"), ICON_NONE); + uiItemR(col, &ptr, "default_value", UI_ITEM_R_EXPAND, IFACE_("Default"), ICON_NONE); uiLayout *sub = uiLayoutColumn(col, true); - uiItemR(sub, ptr, "min_value", DEFAULT_FLAGS, IFACE_("Min"), ICON_NONE); - uiItemR(sub, ptr, "max_value", DEFAULT_FLAGS, IFACE_("Max"), ICON_NONE); + uiItemR(sub, &ptr, "min_value", DEFAULT_FLAGS, IFACE_("Min"), ICON_NONE); + uiItemR(sub, &ptr, "max_value", DEFAULT_FLAGS, IFACE_("Max"), ICON_NONE); break; } case SOCK_BOOLEAN: @@ -1475,17 +1500,24 @@ static void std_node_socket_interface_draw(bContext * /*C*/, uiLayout *layout, P case SOCK_IMAGE: case SOCK_TEXTURE: case SOCK_MATERIAL: { - uiItemR(col, ptr, "default_value", DEFAULT_FLAGS, IFACE_("Default"), ICON_NONE); + uiItemR(col, &ptr, "default_value", DEFAULT_FLAGS, IFACE_("Default"), ICON_NONE); break; } + case SOCK_SHADER: + case SOCK_GEOMETRY: + break; + + case SOCK_CUSTOM: + BLI_assert_unreachable(); + break; } col = uiLayoutColumn(layout, false); - uiItemR(col, ptr, "hide_value", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(col, &ptr, "hide_value", DEFAULT_FLAGS, nullptr, ICON_NONE); - const bNodeTree *node_tree = reinterpret_cast(ptr->owner_id); - if (sock->in_out == SOCK_IN && node_tree->type == NTREE_GEOMETRY) { - uiItemR(col, ptr, "hide_in_modifier", DEFAULT_FLAGS, nullptr, ICON_NONE); + const bNodeTree *node_tree = reinterpret_cast(id); + if (interface_socket->flag & NODE_INTERFACE_SOCKET_INPUT && node_tree->type == NTREE_GEOMETRY) { + uiItemR(col, &ptr, "hide_in_modifier", DEFAULT_FLAGS, nullptr, ICON_NONE); } } @@ -1497,15 +1529,20 @@ static void node_socket_virtual_draw_color(bContext * /*C*/, copy_v4_v4(r_color, virtual_node_socket_color); } +static void node_socket_virtual_draw_color_simple(const bNodeSocketType * /*type*/, float *r_color) +{ + copy_v4_v4(r_color, virtual_node_socket_color); +} + } // namespace blender::ed::space_node void ED_init_standard_node_socket_type(bNodeSocketType *stype) { using namespace blender::ed::space_node; stype->draw = std_node_socket_draw; - stype->draw_color = std_node_socket_draw_color; + stype->draw_color = std_node_socket_color_funcs[stype->type]; + stype->draw_color_simple = std_node_socket_color_simple_fn; stype->interface_draw = std_node_socket_interface_draw; - stype->interface_draw_color = std_node_socket_interface_draw_color; } void ED_init_node_socket_type_virtual(bNodeSocketType *stype) @@ -1513,6 +1550,7 @@ void ED_init_node_socket_type_virtual(bNodeSocketType *stype) using namespace blender::ed::space_node; stype->draw = node_socket_button_label; stype->draw_color = node_socket_virtual_draw_color; + stype->draw_color_simple = node_socket_virtual_draw_color_simple; } void ED_node_type_draw_color(const char *idname, float *r_color) @@ -1570,8 +1608,8 @@ void draw_nodespace_back_pix(const bContext &C, DRW_draw_view(&C); BLI_thread_unlock(LOCK_DRAW_IMAGE); GPU_framebuffer_bind_no_srgb(old_fb); - /* Draw manager changes the depth state. Set it back to NONE. Without this the node preview - * images aren't drawn correctly. */ + /* Draw manager changes the depth state. Set it back to NONE. Without this the + * node preview images aren't drawn correctly. */ GPU_depth_test(GPU_DEPTH_NONE); void *lock; @@ -1585,7 +1623,8 @@ void draw_nodespace_back_pix(const bContext &C, const float x = (region.winx - snode.zoom * ibuf->x) / 2 + offset_x; const float y = (region.winy - snode.zoom * ibuf->y) / 2 + offset_y; - /** \note draw selected info on backdrop */ + /** \note draw selected info on backdrop + */ if (snode.edittree) { bNode *node = (bNode *)snode.edittree->nodes.first; rctf *viewer_border = &snode.nodetree->viewer_border; @@ -2051,8 +2090,7 @@ static bool node_link_is_field_link(const SpaceNode &snode, const bNodeLink &lin return false; } -static NodeLinkDrawConfig nodelink_get_draw_config(const bContext &C, - const View2D &v2d, +static NodeLinkDrawConfig nodelink_get_draw_config(const View2D &v2d, const SpaceNode &snode, const bNodeLink &link, const int th_col1, @@ -2066,8 +2104,6 @@ static NodeLinkDrawConfig nodelink_get_draw_config(const bContext &C, draw_config.th_col2 = th_col2; draw_config.th_col3 = th_col3; - const bNodeTree &node_tree = *snode.edittree; - draw_config.dim_factor = selected ? 1.0f : node_link_dim_factor(v2d, link); bTheme *btheme = UI_GetTheme(); @@ -2092,22 +2128,18 @@ static NodeLinkDrawConfig nodelink_get_draw_config(const bContext &C, if (snode.overlay.flag & SN_OVERLAY_SHOW_OVERLAYS && snode.overlay.flag & SN_OVERLAY_SHOW_WIRE_COLORS) { - PointerRNA from_node_ptr, to_node_ptr; - RNA_pointer_create((ID *)&node_tree, &RNA_Node, link.fromnode, &from_node_ptr); - RNA_pointer_create((ID *)&node_tree, &RNA_Node, link.tonode, &to_node_ptr); - if (link.fromsock) { - node_socket_color_get(C, node_tree, from_node_ptr, *link.fromsock, draw_config.start_color); + node_socket_color_get(*link.fromsock->typeinfo, draw_config.start_color); } else { - node_socket_color_get(C, node_tree, to_node_ptr, *link.tosock, draw_config.start_color); + node_socket_color_get(*link.tosock->typeinfo, draw_config.start_color); } if (link.tosock) { - node_socket_color_get(C, node_tree, to_node_ptr, *link.tosock, draw_config.end_color); + node_socket_color_get(*link.tosock->typeinfo, draw_config.end_color); } else { - node_socket_color_get(C, node_tree, from_node_ptr, *link.fromsock, draw_config.end_color); + node_socket_color_get(*link.fromsock->typeinfo, draw_config.end_color); } } else { @@ -2186,8 +2218,7 @@ static void node_draw_link_bezier_ex(const SpaceNode &snode, } } -void node_draw_link_bezier(const bContext &C, - const View2D &v2d, +void node_draw_link_bezier(const View2D &v2d, const SpaceNode &snode, const bNodeLink &link, const int th_col1, @@ -2200,13 +2231,12 @@ void node_draw_link_bezier(const bContext &C, return; } const NodeLinkDrawConfig draw_config = nodelink_get_draw_config( - C, v2d, snode, link, th_col1, th_col2, th_col3, selected); + v2d, snode, link, th_col1, th_col2, th_col3, selected); node_draw_link_bezier_ex(snode, draw_config, points); } -void node_draw_link(const bContext &C, - const View2D &v2d, +void node_draw_link(const View2D &v2d, const SpaceNode &snode, const bNodeLink &link, const bool selected) @@ -2249,7 +2279,7 @@ void node_draw_link(const bContext &C, } } - node_draw_link_bezier(C, v2d, snode, link, th_col1, th_col2, th_col3, selected); + node_draw_link_bezier(v2d, snode, link, th_col1, th_col2, th_col3, selected); } std::array node_link_bezier_points_dragged(const SpaceNode &snode, @@ -2266,10 +2296,7 @@ std::array node_link_bezier_points_dragged(const SpaceNode &snode, return points; } -void node_draw_link_dragged(const bContext &C, - const View2D &v2d, - const SpaceNode &snode, - const bNodeLink &link) +void node_draw_link_dragged(const View2D &v2d, const SpaceNode &snode, const bNodeLink &link) { if (link.fromsock == nullptr && link.tosock == nullptr) { return; @@ -2278,7 +2305,7 @@ void node_draw_link_dragged(const bContext &C, const std::array points = node_link_bezier_points_dragged(snode, link); const NodeLinkDrawConfig draw_config = nodelink_get_draw_config( - C, v2d, snode, link, TH_ACTIVE, TH_ACTIVE, TH_WIRE, true); + v2d, snode, link, TH_ACTIVE, TH_ACTIVE, TH_WIRE, true); /* End marker outline. */ node_draw_link_end_markers(link, draw_config, points, true); /* Link. */ diff --git a/source/blender/editors/space_node/link_drag_search.cc b/source/blender/editors/space_node/link_drag_search.cc index 131d39ea67b..cf18616b9e9 100644 --- a/source/blender/editors/space_node/link_drag_search.cc +++ b/source/blender/editors/space_node/link_drag_search.cc @@ -87,9 +87,16 @@ static void add_reroute_node_fn(nodes::LinkSearchOpParams ¶ms) static void add_group_input_node_fn(nodes::LinkSearchOpParams ¶ms) { /* Add a group input based on the connected socket, and add a new group input node. */ - bNodeSocket *interface_socket = bke::ntreeAddSocketInterfaceFromSocket( - ¶ms.node_tree, ¶ms.node, ¶ms.socket); - const int group_input_index = BLI_findindex(¶ms.node_tree.inputs, interface_socket); + const eNodeSocketInOut in_out = eNodeSocketInOut(params.socket.in_out); + NodeTreeInterfaceSocketFlag flag = NodeTreeInterfaceSocketFlag(0); + SET_FLAG_FROM_TEST(flag, in_out & SOCK_IN, NODE_INTERFACE_SOCKET_INPUT); + SET_FLAG_FROM_TEST(flag, in_out & SOCK_OUT, NODE_INTERFACE_SOCKET_OUTPUT); + bNodeTreeInterfaceSocket *socket_iface = params.node_tree.tree_interface.add_socket( + params.socket.name, + params.socket.description, + params.socket.typeinfo->idname, + flag, + nullptr); bNode &group_input = params.add_node("NodeGroupInput"); /* This is necessary to create the new sockets in the other input nodes. */ @@ -98,9 +105,10 @@ static void add_group_input_node_fn(nodes::LinkSearchOpParams ¶ms) /* Hide the new input in all other group input nodes, to avoid making them taller. */ for (bNode *node : params.node_tree.all_nodes()) { if (node->type == NODE_GROUP_INPUT) { - bNodeSocket *new_group_input_socket = (bNodeSocket *)BLI_findlink(&node->outputs, - group_input_index); - new_group_input_socket->flag |= SOCK_HIDDEN; + bNodeSocket *new_group_input_socket = nodeFindSocket(node, in_out, socket_iface->identifier); + if (new_group_input_socket) { + new_group_input_socket->flag |= SOCK_HIDDEN; + } } } @@ -109,37 +117,36 @@ static void add_group_input_node_fn(nodes::LinkSearchOpParams ¶ms) socket->flag |= SOCK_HIDDEN; } - bNodeSocket *socket = (bNodeSocket *)BLI_findlink(&group_input.outputs, group_input_index); - if (socket == nullptr) { - /* Adding sockets can fail in some cases. There's no good reason not to be safe here. */ - return; - } - /* Unhide the socket for the new input in the new node and make a connection to it. */ - socket->flag &= ~SOCK_HIDDEN; - nodeAddLink(¶ms.node_tree, &group_input, socket, ¶ms.node, ¶ms.socket); + bNodeSocket *socket = nodeFindSocket(&group_input, in_out, socket_iface->identifier); + if (socket) { + /* Unhide the socket for the new input in the new node and make a connection to it. */ + socket->flag &= ~SOCK_HIDDEN; + nodeAddLink(¶ms.node_tree, &group_input, socket, ¶ms.node, ¶ms.socket); - bke::node_socket_move_default_value( - *CTX_data_main(¶ms.C), params.node_tree, params.socket, *socket); + bke::node_socket_move_default_value( + *CTX_data_main(¶ms.C), params.node_tree, params.socket, *socket); + } } static void add_existing_group_input_fn(nodes::LinkSearchOpParams ¶ms, - const bNodeSocket &interface_socket) + const bNodeTreeInterfaceSocket &interface_socket) { - const int group_input_index = BLI_findindex(¶ms.node_tree.inputs, &interface_socket); + const eNodeSocketInOut in_out = eNodeSocketInOut(params.socket.in_out); + NodeTreeInterfaceSocketFlag flag = NodeTreeInterfaceSocketFlag(0); + SET_FLAG_FROM_TEST(flag, in_out & SOCK_IN, NODE_INTERFACE_SOCKET_INPUT); + SET_FLAG_FROM_TEST(flag, in_out & SOCK_OUT, NODE_INTERFACE_SOCKET_OUTPUT); + bNode &group_input = params.add_node("NodeGroupInput"); LISTBASE_FOREACH (bNodeSocket *, socket, &group_input.outputs) { socket->flag |= SOCK_HIDDEN; } - bNodeSocket *socket = (bNodeSocket *)BLI_findlink(&group_input.outputs, group_input_index); - if (socket == nullptr) { - /* Adding sockets can fail in some cases. There's no good reason not to be safe here. */ - return; + bNodeSocket *socket = nodeFindSocket(&group_input, in_out, interface_socket.identifier); + if (socket != nullptr) { + socket->flag &= ~SOCK_HIDDEN; + nodeAddLink(¶ms.node_tree, &group_input, socket, ¶ms.node, ¶ms.socket); } - - socket->flag &= ~SOCK_HIDDEN; - nodeAddLink(¶ms.node_tree, &group_input, socket, ¶ms.node, ¶ms.socket); } /** @@ -313,20 +320,30 @@ static void gather_socket_link_operations(const bContext &C, search_link_ops.append({IFACE_("Group Input"), add_group_input_node_fn}); int weight = -1; - LISTBASE_FOREACH (const bNodeSocket *, interface_socket, &node_tree.inputs) { - eNodeSocketDatatype from = (eNodeSocketDatatype)interface_socket->type; - eNodeSocketDatatype to = (eNodeSocketDatatype)socket.type; - if (node_tree.typeinfo->validate_link && !node_tree.typeinfo->validate_link(from, to)) { - continue; + node_tree.tree_interface.foreach_item([&](const bNodeTreeInterfaceItem &item) { + if (item.item_type != NODE_INTERFACE_SOCKET) { + return true; + } + const bNodeTreeInterfaceSocket &interface_socket = + reinterpret_cast(item); + { + const bNodeSocketType *from_typeinfo = nodeSocketTypeFind(interface_socket.socket_type); + const eNodeSocketDatatype from = from_typeinfo ? eNodeSocketDatatype(from_typeinfo->type) : + SOCK_CUSTOM; + const eNodeSocketDatatype to = eNodeSocketDatatype(socket.typeinfo->type); + if (node_tree.typeinfo->validate_link && !node_tree.typeinfo->validate_link(from, to)) { + return true; + } } search_link_ops.append( - {std::string(IFACE_("Group Input")) + " " + UI_MENU_ARROW_SEP + interface_socket->name, + {std::string(IFACE_("Group Input")) + " " + UI_MENU_ARROW_SEP + interface_socket.name, [interface_socket](nodes::LinkSearchOpParams ¶ms) { - add_existing_group_input_fn(params, *interface_socket); + add_existing_group_input_fn(params, interface_socket); }, weight}); weight--; - } + return true; + }); } gather_search_link_ops_for_all_assets(C, node_tree, socket, search_link_ops); diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index 9c478fcc66c..8efa2dc255d 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -141,7 +141,9 @@ void ED_node_tree_update(const bContext *C) if (snode) { snode_set_context(*C); - id_us_ensure_real(&snode->nodetree->id); + if (snode->nodetree) { + id_us_ensure_real(&snode->nodetree->id); + } } } @@ -344,6 +346,309 @@ float2 node_from_view(const bNode &node, const float2 &co) ; } +static bool is_node_panels_supported(const bNode &node) +{ + return node.declaration() && node.declaration()->use_custom_socket_order; +} + +/* Draw UI for options, buttons, and previews. */ +static bool node_update_basis_buttons( + const bContext &C, bNodeTree &ntree, bNode &node, uiBlock &block, int &dy) +{ + /* Buttons rect? */ + const bool node_options = node.typeinfo->draw_buttons && (node.flag & NODE_OPTIONS); + if (!node_options) { + return false; + } + + PointerRNA nodeptr; + RNA_pointer_create(&ntree.id, &RNA_Node, &node, &nodeptr); + + /* Get "global" coordinates. */ + float2 loc = node_to_view(node, float2(0)); + /* Round the node origin because text contents are always pixel-aligned. */ + loc.x = round(loc.x); + loc.y = round(loc.y); + + dy -= NODE_DYS / 2; + + uiLayout *layout = UI_block_layout(&block, + UI_LAYOUT_VERTICAL, + UI_LAYOUT_PANEL, + loc.x + NODE_DYS, + dy, + NODE_WIDTH(node) - NODE_DY, + 0, + 0, + UI_style_get_dpi()); + + if (node.flag & NODE_MUTED) { + uiLayoutSetActive(layout, false); + } + + uiLayoutSetContextPointer(layout, "node", &nodeptr); + + node.typeinfo->draw_buttons(layout, (bContext *)&C, &nodeptr); + + UI_block_align_end(&block); + int buty; + UI_block_layout_resolve(&block, nullptr, &buty); + + dy = buty - NODE_DYS / 2; + return true; +} + +static bool node_update_basis_socket(const bContext &C, + bNodeTree &ntree, + bNode &node, + bNodeSocket &socket, + uiBlock &block, + const int &locx, + int &locy) +{ + if (!socket.is_visible()) { + return false; + } + + const int topy = locy; + PointerRNA nodeptr, sockptr; + RNA_pointer_create(&ntree.id, &RNA_Node, &node, &nodeptr); + RNA_pointer_create(&ntree.id, &RNA_NodeSocket, &socket, &sockptr); + + const eNodeSocketInOut in_out = eNodeSocketInOut(socket.in_out); + + /* Add the half the height of a multi-input socket to cursor Y + * to account for the increased height of the taller sockets. */ + const bool is_multi_input = (in_out == SOCK_IN && socket.flag & SOCK_MULTI_INPUT); + const float multi_input_socket_offset = is_multi_input ? + std::max(socket.runtime->total_inputs - 2, 0) * + NODE_MULTI_INPUT_LINK_GAP : + 0.0f; + locy -= multi_input_socket_offset * 0.5f; + + uiLayout *layout = UI_block_layout(&block, + UI_LAYOUT_VERTICAL, + UI_LAYOUT_PANEL, + locx + NODE_DYS, + locy, + NODE_WIDTH(node) - NODE_DY, + NODE_DY, + 0, + UI_style_get_dpi()); + + if (node.flag & NODE_MUTED) { + uiLayoutSetActive(layout, false); + } + + /* Context pointers for current node and socket. */ + uiLayoutSetContextPointer(layout, "node", &nodeptr); + uiLayoutSetContextPointer(layout, "socket", &sockptr); + + uiLayout *row = uiLayoutRow(layout, true); + /* Align output buttons to the right. */ + uiLayoutSetAlignment(row, in_out == SOCK_IN ? UI_LAYOUT_ALIGN_EXPAND : UI_LAYOUT_ALIGN_RIGHT); + + const char *socket_label = bke::nodeSocketLabel(&socket); + const char *socket_translation_context = node_socket_get_translation_context(socket); + socket.typeinfo->draw((bContext *)&C, + row, + &sockptr, + &nodeptr, + CTX_IFACE_(socket_translation_context, socket_label)); + + node_socket_add_tooltip_in_node_editor(ntree, socket, *row); + + UI_block_align_end(&block); + int buty; + UI_block_layout_resolve(&block, nullptr, &buty); + /* Ensure minimum socket height in case layout is empty. */ + buty = min_ii(buty, topy - NODE_DY); + + /* Horizontal position for input/output. */ + const float offsetx = (in_out == SOCK_IN ? 0.0f : NODE_WIDTH(node)); + /* Round the socket location to stop it from jiggling. */ + socket.runtime->location = float2(round(locx + offsetx), round(locy - NODE_DYS)); + + locy = buty - multi_input_socket_offset * 0.5; + return true; +} + +/* Advanced drawing with panels and arbitrary input/output ordering. */ +static void node_update_basis_from_declaration( + const bContext &C, bNodeTree &ntree, bNode &node, uiBlock &block, const int locx, int &locy) +{ + namespace nodes = blender::nodes; + + BLI_assert(is_node_panels_supported(node)); + BLI_assert(node.runtime->panels.size() == node.num_panel_states); + + const nodes::NodeDeclaration &decl = *node.declaration(); + const bool has_buttons = node_update_basis_buttons(C, ntree, node, block, locy); + + bNodeSocket *current_input = static_cast(node.inputs.first); + bNodeSocket *current_output = static_cast(node.outputs.first); + bNodePanelState *current_panel_state = node.panel_states_array; + bke::bNodePanelRuntime *current_panel_runtime = node.runtime->panels.begin(); + bool has_sockets = false; + + /* The panel stack keeps track of the hierarchy of panels. When a panel declaration is found a + * new #PanelUpdate is added to the stack. Items in the declaration are added to the top panel of + * the stack. Each panel expects a number of items to be added, after which the panel is removed + * from the stack again. */ + struct PanelUpdate { + /* How many items still to add. */ + int remaining_items; + /* True if the panel or its parent is collapsed. */ + bool is_collapsed; + /* Location data, needed to finalize the panel when all items have been added. */ + bke::bNodePanelRuntime *runtime; + }; + + Stack panel_updates; + for (const nodes::ItemDeclarationPtr &item_decl : decl.items) { + bool is_parent_collapsed = false; + if (PanelUpdate *parent_update = panel_updates.is_empty() ? nullptr : &panel_updates.peek()) { + /* Adding an item to the parent panel, will be popped when reaching 0. */ + BLI_assert(parent_update->remaining_items > 0); + --parent_update->remaining_items; + is_parent_collapsed = parent_update->is_collapsed; + } + + /* Space after header and between items. */ + if (!is_parent_collapsed) { + locy -= NODE_SOCKDY; + } + + if (nodes::PanelDeclaration *panel_decl = dynamic_cast( + item_decl.get())) { + BLI_assert(node.panel_states().contains_ptr(current_panel_state)); + BLI_assert(node.runtime->panels.as_span().contains_ptr(current_panel_runtime)); + + if (!is_parent_collapsed) { + locy -= NODE_DY; + } + + SET_FLAG_FROM_TEST( + current_panel_state->flag, is_parent_collapsed, NODE_PANEL_PARENT_COLLAPSED); + /* New top panel is collapsed if self or parent is collapsed. */ + const bool is_collapsed = is_parent_collapsed || current_panel_state->is_collapsed(); + panel_updates.push({panel_decl->num_items, is_collapsed, current_panel_runtime}); + + /* Round the socket location to stop it from jiggling. */ + current_panel_runtime->location_y = round(locy + NODE_DYS); + current_panel_runtime->max_content_y = current_panel_runtime->min_content_y = round(locy); + ++current_panel_state; + ++current_panel_runtime; + } + else if (nodes::SocketDeclaration *socket_decl = dynamic_cast( + item_decl.get())) + { + switch (socket_decl->in_out) { + case SOCK_IN: + /* Must match the declaration. */ + BLI_assert(current_input != nullptr); + + SET_FLAG_FROM_TEST(current_input->flag, is_parent_collapsed, SOCK_PANEL_COLLAPSED); + if (is_parent_collapsed) { + current_input->runtime->location = float2(locx, round(locy + NODE_DYS)); + } + else { + has_sockets |= node_update_basis_socket( + C, ntree, node, *current_input, block, locx, locy); + } + current_input = current_input->next; + break; + case SOCK_OUT: + /* Must match the declaration. */ + BLI_assert(current_output != nullptr); + + SET_FLAG_FROM_TEST(current_output->flag, is_parent_collapsed, SOCK_PANEL_COLLAPSED); + if (is_parent_collapsed) { + current_output->runtime->location = float2(round(locx + NODE_WIDTH(node)), + round(locy + NODE_DYS)); + } + else { + has_sockets |= node_update_basis_socket( + C, ntree, node, *current_output, block, locx, locy); + } + current_output = current_output->next; + break; + } + } + + /* Close parent panels that have all items added. */ + while (!panel_updates.is_empty()) { + PanelUpdate &top_panel = panel_updates.peek(); + if (top_panel.remaining_items > 0) { + /* Incomplete panel, continue adding items. */ + break; + } + /* Finalize the vertical extent of the content. */ + top_panel.runtime->min_content_y = round(locy - NODE_DYS / 4); + /* Close panel and continue checking parent. */ + panel_updates.pop(); + } + } + /* Enough items should have been added to close all panels. */ + BLI_assert(panel_updates.is_empty()); + + /* Little bit of space in end. */ + if (has_sockets || !has_buttons) { + locy -= NODE_DYS / 2; + } +} + +/* Conventional drawing in outputs/buttons/inputs order. */ +static void node_update_basis_from_socket_lists( + const bContext &C, bNodeTree &ntree, bNode &node, uiBlock &block, const int locx, int &locy) +{ + const bool node_options = node.typeinfo->draw_buttons && (node.flag & NODE_OPTIONS); + const bool inputs_first = node.inputs.first && !(node.outputs.first || node_options); + + /* Add a little bit of padding above the top socket. */ + if (node.outputs.first || inputs_first) { + locy -= NODE_DYS / 2; + } + + /* Output sockets. */ + bool add_output_space = false; + + for (bNodeSocket *socket : node.output_sockets()) { + /* Clear flag, conventional drawing does not support panels. */ + socket->flag &= ~SOCK_PANEL_COLLAPSED; + + if (node_update_basis_socket(C, ntree, node, *socket, block, locx, locy)) { + if (socket->next) { + locy -= NODE_SOCKDY; + } + add_output_space = true; + } + } + + if (add_output_space) { + locy -= NODE_DY / 4; + } + + node_update_basis_buttons(C, ntree, node, block, locy); + + /* Input sockets. */ + for (bNodeSocket *socket : node.input_sockets()) { + /* Clear flag, conventional drawing does not support panels. */ + socket->flag &= ~SOCK_PANEL_COLLAPSED; + + if (node_update_basis_socket(C, ntree, node, *socket, block, locx, locy)) { + if (socket->next) { + locy -= NODE_SOCKDY; + } + } + } + + /* Little bit of space in end. */ + if (node.inputs.first || (node.flag & NODE_OPTIONS) == 0) { + locy -= NODE_DYS / 2; + } +} + /** * Based on settings and sockets in node, set drawing rect info. */ @@ -356,9 +661,6 @@ static void node_update_basis(const bContext &C, PointerRNA nodeptr; RNA_pointer_create(&ntree.id, &RNA_Node, &node, &nodeptr); - const bool node_options = node.typeinfo->draw_buttons && (node.flag & NODE_OPTIONS); - const bool inputs_first = node.inputs.first && !(node.outputs.first || node_options); - /* Get "global" coordinates. */ float2 loc = node_to_view(node, float2(0)); /* Round the node origin because text contents are always pixel-aligned. */ @@ -370,172 +672,11 @@ static void node_update_basis(const bContext &C, /* Header. */ dy -= NODE_DY; - /* Add a little bit of padding above the top socket. */ - if (node.outputs.first || inputs_first) { - dy -= NODE_DYS / 2; + if (is_node_panels_supported(node)) { + node_update_basis_from_declaration(C, ntree, node, block, loc.x, dy); } - - /* Output sockets. */ - bool add_output_space = false; - - int buty; - for (bNodeSocket *socket : node.output_sockets()) { - if (!socket->is_visible()) { - continue; - } - - PointerRNA sockptr; - RNA_pointer_create(&ntree.id, &RNA_NodeSocket, socket, &sockptr); - - uiLayout *layout = UI_block_layout(&block, - UI_LAYOUT_VERTICAL, - UI_LAYOUT_PANEL, - loc.x + NODE_DYS, - dy, - NODE_WIDTH(node) - NODE_DY, - NODE_DY, - 0, - UI_style_get_dpi()); - - if (node.flag & NODE_MUTED) { - uiLayoutSetActive(layout, false); - } - - /* Context pointers for current node and socket. */ - uiLayoutSetContextPointer(layout, "node", &nodeptr); - uiLayoutSetContextPointer(layout, "socket", &sockptr); - - /* Align output buttons to the right. */ - uiLayout *row = uiLayoutRow(layout, true); - uiLayoutSetAlignment(row, UI_LAYOUT_ALIGN_RIGHT); - - const char *socket_label = bke::nodeSocketLabel(socket); - const char *socket_translation_context = node_socket_get_translation_context(*socket); - socket->typeinfo->draw((bContext *)&C, - row, - &sockptr, - &nodeptr, - CTX_IFACE_(socket_translation_context, socket_label)); - - node_socket_add_tooltip_in_node_editor(ntree, *socket, *row); - - UI_block_align_end(&block); - UI_block_layout_resolve(&block, nullptr, &buty); - - /* Ensure minimum socket height in case layout is empty. */ - buty = min_ii(buty, dy - NODE_DY); - - /* Round the socket location to stop it from jiggling. */ - socket->runtime->location = float2(round(loc.x + NODE_WIDTH(node)), round(dy - NODE_DYS)); - - dy = buty; - if (socket->next) { - dy -= NODE_SOCKDY; - } - - add_output_space = true; - } - - if (add_output_space) { - dy -= NODE_DY / 4; - } - - /* Buttons rect? */ - if (node_options) { - dy -= NODE_DYS / 2; - - uiLayout *layout = UI_block_layout(&block, - UI_LAYOUT_VERTICAL, - UI_LAYOUT_PANEL, - loc.x + NODE_DYS, - dy, - NODE_WIDTH(node) - NODE_DY, - 0, - 0, - UI_style_get_dpi()); - - if (node.flag & NODE_MUTED) { - uiLayoutSetActive(layout, false); - } - - uiLayoutSetContextPointer(layout, "node", &nodeptr); - - node.typeinfo->draw_buttons(layout, (bContext *)&C, &nodeptr); - - UI_block_align_end(&block); - UI_block_layout_resolve(&block, nullptr, &buty); - - dy = buty - NODE_DYS / 2; - } - - /* Input sockets. */ - for (bNodeSocket *socket : node.input_sockets()) { - if (!socket->is_visible()) { - continue; - } - - PointerRNA sockptr; - RNA_pointer_create(&ntree.id, &RNA_NodeSocket, socket, &sockptr); - - /* Add the half the height of a multi-input socket to cursor Y - * to account for the increased height of the taller sockets. */ - float multi_input_socket_offset = 0.0f; - if (socket->flag & SOCK_MULTI_INPUT) { - if (socket->runtime->total_inputs > 2) { - multi_input_socket_offset = (socket->runtime->total_inputs - 2) * - NODE_MULTI_INPUT_LINK_GAP; - } - } - dy -= multi_input_socket_offset * 0.5f; - - uiLayout *layout = UI_block_layout(&block, - UI_LAYOUT_VERTICAL, - UI_LAYOUT_PANEL, - loc.x + NODE_DYS, - dy, - NODE_WIDTH(node) - NODE_DY, - NODE_DY, - 0, - UI_style_get_dpi()); - - if (node.flag & NODE_MUTED) { - uiLayoutSetActive(layout, false); - } - - /* Context pointers for current node and socket. */ - uiLayoutSetContextPointer(layout, "node", &nodeptr); - uiLayoutSetContextPointer(layout, "socket", &sockptr); - - uiLayout *row = uiLayoutRow(layout, true); - - const char *socket_label = bke::nodeSocketLabel(socket); - const char *socket_translation_context = node_socket_get_translation_context(*socket); - socket->typeinfo->draw((bContext *)&C, - row, - &sockptr, - &nodeptr, - CTX_IFACE_(socket_translation_context, socket_label)); - - node_socket_add_tooltip_in_node_editor(ntree, *socket, *row); - - UI_block_align_end(&block); - UI_block_layout_resolve(&block, nullptr, &buty); - - /* Ensure minimum socket height in case layout is empty. */ - buty = min_ii(buty, dy - NODE_DY); - - /* Round the socket vertical position to stop it from jiggling. */ - socket->runtime->location = float2(loc.x, round(dy - NODE_DYS)); - - dy = buty - multi_input_socket_offset * 0.5; - if (socket->next) { - dy -= NODE_SOCKDY; - } - } - - /* Little bit of space in end. */ - if (node.inputs.first || (node.flag & NODE_OPTIONS) == 0) { - dy -= NODE_DYS / 2; + else { + node_update_basis_from_socket_lists(C, ntree, node, block, loc.x, dy); } node.runtime->totr.xmin = loc.x; @@ -672,16 +813,13 @@ static int node_get_colorid(TreeDrawContext &tree_draw_ctx, const bNode &node) } } -static void node_draw_mute_line(const bContext &C, - const View2D &v2d, - const SpaceNode &snode, - const bNode &node) +static void node_draw_mute_line(const View2D &v2d, const SpaceNode &snode, const bNode &node) { GPU_blend(GPU_BLEND_ALPHA); for (const bNodeLink &link : node.internal_links()) { if (!nodeLinkIsHidden(&link)) { - node_draw_link_bezier(C, v2d, snode, link, TH_WIRE_INNER, TH_WIRE_INNER, TH_WIRE, false); + node_draw_link_bezier(v2d, snode, link, TH_WIRE_INNER, TH_WIRE_INNER, TH_WIRE, false); } } @@ -779,18 +917,9 @@ static void node_socket_outline_color_get(const bool selected, } } -void node_socket_color_get(const bContext &C, - const bNodeTree &ntree, - PointerRNA &node_ptr, - const bNodeSocket &sock, - float r_color[4]) +void node_socket_color_get(const bNodeSocketType &type, float r_color[4]) { - PointerRNA ptr; - BLI_assert(RNA_struct_is_a(node_ptr.type, &RNA_Node)); - RNA_pointer_create( - &const_cast(ntree.id), &RNA_NodeSocket, &const_cast(sock), &ptr); - - sock.typeinfo->draw_color((bContext *)&C, &ptr, &node_ptr, r_color); + type.draw_color_simple(&type, r_color); } static void create_inspection_string_for_generic_value(const bNodeSocket &socket, @@ -1215,9 +1344,7 @@ void node_socket_add_tooltip(const bNodeTree &ntree, const bNodeSocket &sock, ui MEM_freeN); } -static void node_socket_draw_nested(const bContext &C, - const bNodeTree &ntree, - PointerRNA &node_ptr, +static void node_socket_draw_nested(const bNodeTree &ntree, uiBlock &block, const bNodeSocket &sock, const uint pos_id, @@ -1232,7 +1359,7 @@ static void node_socket_draw_nested(const bContext &C, float color[4]; float outline_color[4]; - node_socket_color_get(C, ntree, node_ptr, sock, color); + node_socket_color_get(*sock.typeinfo, color); node_socket_outline_color_get(selected, sock.type, outline_color); node_socket_draw(sock, @@ -1427,7 +1554,6 @@ static void node_draw_shadow(const SpaceNode &snode, } static void node_draw_sockets(const View2D &v2d, - const bContext &C, const bNodeTree &ntree, const bNode &node, uiBlock &block, @@ -1471,7 +1597,7 @@ static void node_draw_sockets(const View2D &v2d, /* Socket inputs. */ int selected_input_len = 0; for (const bNodeSocket *sock : node.input_sockets()) { - if (!sock->is_visible()) { + if (!sock->is_visible() || sock->is_panel_collapsed()) { continue; } if (select_all || (sock->flag & SELECT)) { @@ -1486,25 +1612,15 @@ static void node_draw_sockets(const View2D &v2d, continue; } - node_socket_draw_nested(C, - ntree, - node_ptr, - block, - *sock, - pos_id, - col_id, - shape_id, - size_id, - outline_col_id, - scale, - selected); + node_socket_draw_nested( + ntree, block, *sock, pos_id, col_id, shape_id, size_id, outline_col_id, scale, selected); } /* Socket outputs. */ int selected_output_len = 0; if (draw_outputs) { for (const bNodeSocket *sock : node.output_sockets()) { - if (!sock->is_visible()) { + if (!sock->is_visible() || sock->is_panel_collapsed()) { continue; } if (select_all || (sock->flag & SELECT)) { @@ -1512,18 +1628,8 @@ static void node_draw_sockets(const View2D &v2d, continue; } - node_socket_draw_nested(C, - ntree, - node_ptr, - block, - *sock, - pos_id, - col_id, - shape_id, - size_id, - outline_col_id, - scale, - selected); + node_socket_draw_nested( + ntree, block, *sock, pos_id, col_id, shape_id, size_id, outline_col_id, scale, selected); } } @@ -1550,9 +1656,7 @@ static void node_draw_sockets(const View2D &v2d, continue; } if (select_all || (sock->flag & SELECT)) { - node_socket_draw_nested(C, - ntree, - node_ptr, + node_socket_draw_nested(ntree, block, *sock, pos_id, @@ -1577,9 +1681,7 @@ static void node_draw_sockets(const View2D &v2d, continue; } if (select_all || (sock->flag & SELECT)) { - node_socket_draw_nested(C, - ntree, - node_ptr, + node_socket_draw_nested(ntree, block, *sock, pos_id, @@ -1621,7 +1723,7 @@ static void node_draw_sockets(const View2D &v2d, float color[4]; float outline_color[4]; - node_socket_color_get(C, ntree, node_ptr, *socket, color); + node_socket_color_get(*socket->typeinfo, color); node_socket_outline_color_get(socket->flag & SELECT, socket->type, outline_color); const float2 location = socket->runtime->location; @@ -1629,6 +1731,162 @@ static void node_draw_sockets(const View2D &v2d, } } +static void node_panel_toggle_button_cb(bContext *C, void *panel_state_argv, void *ntree_argv) +{ + Main *bmain = CTX_data_main(C); + bNodePanelState *panel_state = static_cast(panel_state_argv); + bNodeTree *ntree = static_cast(ntree_argv); + + panel_state->flag ^= NODE_PANEL_COLLAPSED; + + ED_node_tree_propagate_change(C, bmain, ntree); +} + +/* Draw panel backgrounds first, so other node elements can be rendered on top. */ +static void node_draw_panels_background(const bNode &node, uiBlock &block) +{ + namespace nodes = blender::nodes; + + BLI_assert(is_node_panels_supported(node)); + BLI_assert(node.runtime->panels.size() == node.panel_states().size()); + + const nodes::NodeDeclaration &decl = *node.declaration(); + const rctf &rct = node.runtime->totr; + + int panel_i = 0; + for (const nodes::ItemDeclarationPtr &item_decl : decl.items) { + const nodes::PanelDeclaration *panel_decl = dynamic_cast( + item_decl.get()); + if (panel_decl == nullptr) { + /* Not a panel. */ + continue; + } + + const bNodePanelState &state = node.panel_states()[panel_i]; + /* Don't draw hidden or collapsed panels. */ + if (state.is_collapsed() || state.is_parent_collapsed()) { + ++panel_i; + continue; + } + const bke::bNodePanelRuntime &runtime = node.runtime->panels[panel_i]; + + const rctf content_rect = { + rct.xmin, + rct.xmax, + runtime.min_content_y, + runtime.max_content_y, + }; + + UI_block_emboss_set(&block, UI_EMBOSS_NONE); + + /* Panel background. */ + float color_panel[4]; + UI_GetThemeColorBlend4f(TH_BACK, TH_NODE, 0.2f, color_panel); + UI_draw_roundbox_corner_set(UI_CNR_NONE); + UI_draw_roundbox_4fv(&content_rect, true, BASIS_RAD, color_panel); + + UI_block_emboss_set(&block, UI_EMBOSS); + + ++panel_i; + } +} + +static void node_draw_panels(bNodeTree &ntree, const bNode &node, uiBlock &block) +{ + namespace nodes = blender::nodes; + + BLI_assert(is_node_panels_supported(node)); + BLI_assert(node.runtime->panels.size() == node.panel_states().size()); + + const nodes::NodeDeclaration &decl = *node.declaration(); + const rctf &rct = node.runtime->totr; + + int panel_i = 0; + for (const nodes::ItemDeclarationPtr &item_decl : decl.items) { + const nodes::PanelDeclaration *panel_decl = dynamic_cast( + item_decl.get()); + if (panel_decl == nullptr) { + /* Not a panel. */ + continue; + } + + const bNodePanelState &state = node.panel_states()[panel_i]; + /* Don't draw hidden panels. */ + if (state.is_parent_collapsed()) { + ++panel_i; + continue; + } + const bke::bNodePanelRuntime &runtime = node.runtime->panels[panel_i]; + + const rctf rect = { + rct.xmin, + rct.xmax, + runtime.location_y - NODE_DYS, + runtime.location_y + NODE_DYS, + }; + + UI_block_emboss_set(&block, UI_EMBOSS_NONE); + + /* Collapse/expand icon. */ + const int but_size = U.widget_unit * 0.8f; + uiDefIconBut(&block, + UI_BTYPE_BUT_TOGGLE, + 0, + state.is_collapsed() ? ICON_RIGHTARROW : ICON_DOWNARROW_HLT, + rct.xmin + (NODE_MARGIN_X / 3), + runtime.location_y - but_size / 2, + but_size, + but_size, + nullptr, + 0.0f, + 0.0f, + 0.0f, + 0.0f, + ""); + + /* Panel label. */ + uiBut *but = uiDefBut(&block, + UI_BTYPE_LABEL, + 0, + panel_decl->name.c_str(), + int(rct.xmin + NODE_MARGIN_X + 0.4f), + int(runtime.location_y - NODE_DYS), + short(rct.xmax - rct.xmin - 0.35f * U.widget_unit), + short(NODE_DY), + nullptr, + 0, + 0, + 0, + 0, + ""); + if (node.flag & NODE_MUTED) { + UI_but_flag_enable(but, UI_BUT_INACTIVE); + } + + /* Invisible button covering the entire header for collapsing/expanding. */ + but = uiDefIconBut(&block, + UI_BTYPE_BUT_TOGGLE, + 0, + ICON_NONE, + rect.xmin, + rect.ymin, + rect.xmax - rect.xmin, + rect.ymax - rect.ymin, + nullptr, + 0.0f, + 0.0f, + 0.0f, + 0.0f, + ""); + UI_but_func_set( + but, node_panel_toggle_button_cb, const_cast(&state), &ntree); + + UI_block_emboss_set(&block, UI_EMBOSS); + + ++panel_i; + } +} + static int node_error_type_to_icon(const geo_log::NodeWarningType type) { switch (type) { @@ -2458,7 +2716,7 @@ static void node_draw_basis(const bContext &C, /* Wire across the node when muted/disabled. */ if (node.flag & NODE_MUTED) { - node_draw_mute_line(C, v2d, snode, node); + node_draw_mute_line(v2d, snode, node); } /* Body. */ @@ -2498,6 +2756,10 @@ static void node_draw_basis(const bContext &C, UI_draw_roundbox_corner_set(UI_CNR_BOTTOM_LEFT | UI_CNR_BOTTOM_RIGHT); UI_draw_roundbox_4fv(&rect, true, BASIS_RAD, color); + + if (is_node_panels_supported(node)) { + node_draw_panels_background(node, block); + } } /* Header underline. */ @@ -2562,7 +2824,11 @@ static void node_draw_basis(const bContext &C, /* Skip slow socket drawing if zoom is small. */ if (scale > 0.2f) { - node_draw_sockets(v2d, C, ntree, node, block, true, false); + node_draw_sockets(v2d, ntree, node, block, true, false); + } + + if (is_node_panels_supported(node)) { + node_draw_panels(ntree, node, block); } UI_block_end(&C, &block); @@ -2593,7 +2859,7 @@ static void node_draw_hidden(const bContext &C, /* Wire across the node when muted/disabled. */ if (node.flag & NODE_MUTED) { - node_draw_mute_line(C, v2d, snode, node); + node_draw_mute_line(v2d, snode, node); } /* Body. */ @@ -2743,7 +3009,7 @@ static void node_draw_hidden(const bContext &C, immUnbindProgram(); GPU_blend(GPU_BLEND_NONE); - node_draw_sockets(v2d, C, ntree, node, block, true, false); + node_draw_sockets(v2d, ntree, node, block, true, false); UI_block_end(&C, &block); UI_block_draw(&C, &block); @@ -3118,7 +3384,7 @@ static void reroute_node_draw( /* Only draw input socket as they all are placed on the same position highlight * if node itself is selected, since we don't display the node body separately. */ - node_draw_sockets(region.v2d, C, ntree, node, block, false, node.flag & SELECT); + node_draw_sockets(region.v2d, ntree, node, block, false, node.flag & SELECT); UI_block_end(&C, &block); UI_block_draw(&C, &block); @@ -3299,7 +3565,7 @@ static void node_draw_zones(TreeDrawContext & /*tree_draw_ctx*/, return bounding_box_area_by_zone[a] > bounding_box_area_by_zone[b]; }); - /* Draw all the contour lines after to prevent them from getting hidden by overlapping zones. */ + /* Draw all the contour lines after to prevent them from getting hidden by overlapping zones. */ for (const int zone_i : zone_draw_order) { float zone_color[4]; UI_GetThemeColor4fv(get_theme_id(zone_i), zone_color); @@ -3378,14 +3644,14 @@ static void node_draw_nodetree(const bContext &C, for (const bNodeLink *link : ntree.all_links()) { if (!nodeLinkIsHidden(link) && !bke::nodeLinkIsSelected(link)) { - node_draw_link(C, region.v2d, snode, *link, false); + node_draw_link(region.v2d, snode, *link, false); } } /* Draw selected node links after the unselected ones, so they are shown on top. */ for (const bNodeLink *link : ntree.all_links()) { if (!nodeLinkIsHidden(link) && bke::nodeLinkIsSelected(link)) { - node_draw_link(C, region.v2d, snode, *link, true); + node_draw_link(region.v2d, snode, *link, true); } } @@ -3636,7 +3902,7 @@ void node_draw_space(const bContext &C, ARegion ®ion) GPU_line_smooth(true); if (snode.runtime->linkdrag) { for (const bNodeLink &link : snode.runtime->linkdrag->links) { - node_draw_link_dragged(C, v2d, snode, link); + node_draw_link_dragged(v2d, snode, link); } } GPU_line_smooth(false); diff --git a/source/blender/editors/space_node/node_edit.cc b/source/blender/editors/space_node/node_edit.cc index 7d18fc288ea..67d2967b280 100644 --- a/source/blender/editors/space_node/node_edit.cc +++ b/source/blender/editors/space_node/node_edit.cc @@ -2218,460 +2218,6 @@ void NODE_OT_node_copy_color(wmOperatorType *ot) /** \} */ -/* -------------------------------------------------------------------- */ -/** \name Node-Tree Add Interface Socket Operator - * \{ */ - -static bNodeSocket *ntree_get_active_interface_socket(const ListBase *lb) -{ - LISTBASE_FOREACH (bNodeSocket *, socket, lb) { - if (socket->flag & SELECT) { - return socket; - } - } - return nullptr; -} - -static int ntree_socket_add_exec(bContext *C, wmOperator *op) -{ - SpaceNode *snode = CTX_wm_space_node(C); - bNodeTree *ntree = snode->edittree; - - PointerRNA ntree_ptr; - RNA_id_pointer_create((ID *)ntree, &ntree_ptr); - - const eNodeSocketInOut in_out = (eNodeSocketInOut)RNA_enum_get(op->ptr, "in_out"); - ListBase *sockets = (in_out == SOCK_IN) ? &ntree->inputs : &ntree->outputs; - - const char *default_name = (in_out == SOCK_IN) ? DATA_("Input") : DATA_("Output"); - bNodeSocket *active_sock = ntree_get_active_interface_socket(sockets); - - bNodeSocket *sock; - if (active_sock) { - /* Insert a copy of the active socket right after it. */ - sock = blender::bke::ntreeInsertSocketInterface( - ntree, in_out, active_sock->idname, active_sock->next, active_sock->name); - /* XXX this only works for actual sockets, not interface templates! */ - // nodeSocketCopyValue(sock, &ntree_ptr, active_sock, &ntree_ptr); - } - else { - /* XXX TODO: define default socket type for a tree! */ - sock = ntreeAddSocketInterface(ntree, in_out, "NodeSocketFloat", default_name); - } - - /* Deactivate sockets. */ - LISTBASE_FOREACH (bNodeSocket *, socket_iter, sockets) { - socket_iter->flag &= ~SELECT; - } - /* Make the new socket selected. */ - sock->flag |= SELECT; - - ED_node_tree_propagate_change(C, CTX_data_main(C), snode->edittree); - - WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr); - - return OPERATOR_FINISHED; -} - -void NODE_OT_tree_socket_add(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Add Node Tree Interface Socket"; - ot->description = "Add an input or output to the active node tree"; - ot->idname = "NODE_OT_tree_socket_add"; - - /* api callbacks */ - ot->exec = ntree_socket_add_exec; - ot->poll = ED_operator_node_editable; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - RNA_def_enum(ot->srna, "in_out", rna_enum_node_socket_in_out_items, SOCK_IN, "Socket Type", ""); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Node-Tree Remove Interface Socket Operator - * \{ */ - -static int ntree_socket_remove_exec(bContext *C, wmOperator *op) -{ - SpaceNode *snode = CTX_wm_space_node(C); - bNodeTree *ntree = snode->edittree; - const eNodeSocketInOut in_out = (eNodeSocketInOut)RNA_enum_get(op->ptr, "in_out"); - - bNodeSocket *iosock = ntree_get_active_interface_socket(in_out == SOCK_IN ? &ntree->inputs : - &ntree->outputs); - if (iosock == nullptr) { - return OPERATOR_CANCELLED; - } - - /* Preferably next socket becomes active, otherwise try previous socket. */ - bNodeSocket *active_sock = (iosock->next ? iosock->next : iosock->prev); - ntreeRemoveSocketInterface(ntree, iosock); - - /* Set active socket. */ - if (active_sock) { - active_sock->flag |= SELECT; - } - - ED_node_tree_propagate_change(C, CTX_data_main(C), ntree); - - WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr); - - return OPERATOR_FINISHED; -} - -void NODE_OT_tree_socket_remove(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Remove Node Tree Interface Socket"; - ot->description = "Remove an input or output from the active node tree"; - ot->idname = "NODE_OT_tree_socket_remove"; - - /* api callbacks */ - ot->exec = ntree_socket_remove_exec; - ot->poll = ED_operator_node_editable; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - RNA_def_enum(ot->srna, "in_out", rna_enum_node_socket_in_out_items, SOCK_IN, "Socket Type", ""); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Node-Tree Change Interface Socket Type Operator - * \{ */ - -static int ntree_socket_change_type_exec(bContext *C, wmOperator *op) -{ - SpaceNode *snode = CTX_wm_space_node(C); - bNodeTree *ntree = snode->edittree; - const eNodeSocketInOut in_out = (eNodeSocketInOut)RNA_enum_get(op->ptr, "in_out"); - const bNodeSocketType *socket_type = rna_node_socket_type_from_enum( - RNA_enum_get(op->ptr, "socket_type")); - ListBase *sockets = (in_out == SOCK_IN) ? &ntree->inputs : &ntree->outputs; - - Main *main = CTX_data_main(C); - - bNodeSocket *iosock = ntree_get_active_interface_socket(sockets); - if (iosock == nullptr) { - return OPERATOR_CANCELLED; - } - - /* The type remains the same, so we don't need to change anything. */ - if (iosock->typeinfo == socket_type) { - return OPERATOR_FINISHED; - } - - blender::bke::nodeModifySocketType(ntree, nullptr, iosock, socket_type->idname); - - /* Need the extra update here because the loop above does not check for valid links in the node - * group we're currently editing. */ - BKE_ntree_update_tag_interface(ntree); - - /* Deactivate sockets. */ - LISTBASE_FOREACH (bNodeSocket *, socket_iter, sockets) { - socket_iter->flag &= ~SELECT; - } - /* Make the new socket active. */ - iosock->flag |= SELECT; - - ED_node_tree_propagate_change(C, main, ntree); - - WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr); - - return OPERATOR_FINISHED; -} - -static bool socket_change_poll_type(void *userdata, bNodeSocketType *socket_type) -{ - /* Check if the node tree supports the socket type. */ - bNodeTreeType *ntreetype = (bNodeTreeType *)userdata; - if (ntreetype->valid_socket_type && !ntreetype->valid_socket_type(ntreetype, socket_type)) { - return false; - } - - /* Only use basic socket types for this enum. */ - if (socket_type->subtype != PROP_NONE) { - return false; - } - - return true; -} - -static const EnumPropertyItem *socket_change_type_itemf(bContext *C, - PointerRNA * /*ptr*/, - PropertyRNA * /*prop*/, - bool *r_free) -{ - if (!C) { - return rna_enum_dummy_NULL_items; - } - - SpaceNode *snode = CTX_wm_space_node(C); - if (!snode || !snode->edittree) { - return rna_enum_dummy_NULL_items; - } - - return rna_node_socket_type_itemf(snode->edittree->typeinfo, socket_change_poll_type, r_free); -} - -void NODE_OT_tree_socket_change_type(wmOperatorType *ot) -{ - PropertyRNA *prop; - - /* identifiers */ - ot->name = "Change Node Tree Interface Socket Type"; - ot->description = "Change the type of an input or output of the active node tree"; - ot->idname = "NODE_OT_tree_socket_change_type"; - - /* api callbacks */ - ot->invoke = WM_menu_invoke; - ot->exec = ntree_socket_change_type_exec; - ot->poll = ED_operator_node_editable; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - RNA_def_enum(ot->srna, "in_out", rna_enum_node_socket_in_out_items, SOCK_IN, "Socket Type", ""); - prop = RNA_def_enum(ot->srna, "socket_type", rna_enum_dummy_DEFAULT_items, 0, "Socket Type", ""); - RNA_def_enum_funcs(prop, socket_change_type_itemf); - ot->prop = prop; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Node-Tree Change Interface Socket Subtype Operator - * \{ */ - -static int ntree_socket_change_subtype_exec(bContext *C, wmOperator *op) -{ - Main *main = CTX_data_main(C); - const int socket_subtype = RNA_enum_get(op->ptr, "socket_subtype"); - - PointerRNA io_socket_ptr = CTX_data_pointer_get_type( - C, "interface_socket", &RNA_NodeSocketInterface); - bNodeSocket *io_socket = static_cast(io_socket_ptr.data); - if (!io_socket) { - return OPERATOR_CANCELLED; - } - - bNodeTree &node_tree = *reinterpret_cast(io_socket_ptr.owner_id); - - ListBase *sockets; - if (node_tree.interface_inputs().contains(io_socket)) { - sockets = &node_tree.inputs; - } - else if (node_tree.interface_outputs().contains(io_socket)) { - sockets = &node_tree.outputs; - } - else { - /* The interface socket should be in the inputs or outputs. */ - BLI_assert_unreachable(); - return OPERATOR_CANCELLED; - } - - nodeModifySocketTypeStatic(&node_tree, nullptr, io_socket, io_socket->type, socket_subtype); - - /* Deactivate sockets. */ - LISTBASE_FOREACH (bNodeSocket *, socket_iter, sockets) { - socket_iter->flag &= ~SELECT; - } - /* Make the new socket active. */ - io_socket->flag |= SELECT; - - BKE_ntree_update_tag_interface(&node_tree); - ED_node_tree_propagate_change(C, main, &node_tree); - - return OPERATOR_FINISHED; -} - -static Set socket_type_get_subtypes(const eNodeSocketDatatype type) -{ - switch (type) { - case SOCK_FLOAT: - return {PROP_PERCENTAGE, - PROP_FACTOR, - PROP_ANGLE, - PROP_TIME, - PROP_TIME_ABSOLUTE, - PROP_DISTANCE, - PROP_NONE}; - case SOCK_INT: - return {PROP_PERCENTAGE, PROP_FACTOR, PROP_NONE}; - case SOCK_VECTOR: - return {PROP_TRANSLATION, - /* Direction doesn't seem to work. */ - // PROP_DIRECTION, - PROP_VELOCITY, - PROP_ACCELERATION, - PROP_EULER, - PROP_XYZ, - PROP_NONE}; - default: - return {}; - } -} - -static const EnumPropertyItem *socket_change_subtype_itemf(bContext *C, - PointerRNA * /*ptr*/, - PropertyRNA * /*prop*/, - bool *r_free) -{ - if (!C) { - return rna_enum_dummy_NULL_items; - } - SpaceNode *snode = CTX_wm_space_node(C); - if (!snode || !snode->edittree) { - return rna_enum_dummy_NULL_items; - } - - PointerRNA active_socket_ptr = CTX_data_pointer_get_type( - C, "interface_socket", &RNA_NodeSocketInterface); - const bNodeSocket *active_socket = static_cast(active_socket_ptr.data); - if (!active_socket) { - return rna_enum_dummy_NULL_items; - } - - const Set subtypes = socket_type_get_subtypes(eNodeSocketDatatype(active_socket->type)); - if (subtypes.is_empty()) { - return rna_enum_dummy_NULL_items; - } - - EnumPropertyItem *items = nullptr; - int items_count = 0; - for (const EnumPropertyItem *item = rna_enum_property_subtype_items; item->name != nullptr; - item++) { - if (subtypes.contains(item->value)) { - RNA_enum_item_add(&items, &items_count, item); - } - } - - if (items_count == 0) { - return rna_enum_dummy_NULL_items; - } - - RNA_enum_item_end(&items, &items_count); - *r_free = true; - return items; -} - -static bool ntree_socket_change_subtype_poll(bContext *C) -{ - if (!ED_operator_node_editable(C)) { - return false; - } - PointerRNA io_socket_ptr = CTX_data_pointer_get_type( - C, "interface_socket", &RNA_NodeSocketInterface); - const bNodeSocket *io_socket = static_cast(io_socket_ptr.data); - if (!io_socket) { - return false; - } - return !socket_type_get_subtypes(eNodeSocketDatatype(io_socket->type)).is_empty(); -} - -void NODE_OT_tree_socket_change_subtype(wmOperatorType *ot) -{ - ot->name = "Change Node Tree Socket Subtype"; - ot->description = "Change the subtype of a socket of the active node tree"; - ot->idname = "NODE_OT_tree_socket_change_subtype"; - - ot->invoke = WM_menu_invoke; - ot->exec = ntree_socket_change_subtype_exec; - ot->poll = ntree_socket_change_subtype_poll; - - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - ot->prop = RNA_def_enum( - ot->srna, "socket_subtype", rna_enum_dummy_DEFAULT_items, 0, "Socket Subtype", ""); - RNA_def_enum_funcs(ot->prop, socket_change_subtype_itemf); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Node-Tree Move Interface Socket Operator - * \{ */ - -static const EnumPropertyItem move_direction_items[] = { - {1, "UP", 0, "Up", ""}, - {2, "DOWN", 0, "Down", ""}, - {0, nullptr, 0, nullptr, nullptr}, -}; - -static int ntree_socket_move_exec(bContext *C, wmOperator *op) -{ - SpaceNode *snode = CTX_wm_space_node(C); - bNodeTree *ntree = snode->edittree; - int direction = RNA_enum_get(op->ptr, "direction"); - - const eNodeSocketInOut in_out = (eNodeSocketInOut)RNA_enum_get(op->ptr, "in_out"); - ListBase *sockets = in_out == SOCK_IN ? &ntree->inputs : &ntree->outputs; - - bNodeSocket *iosock = ntree_get_active_interface_socket(sockets); - - if (iosock == nullptr) { - return OPERATOR_CANCELLED; - } - - switch (direction) { - case 1: { /* up */ - bNodeSocket *before = iosock->prev; - BLI_remlink(sockets, iosock); - if (before) { - BLI_insertlinkbefore(sockets, before, iosock); - } - else { - BLI_addhead(sockets, iosock); - } - break; - } - case 2: { /* down */ - bNodeSocket *after = iosock->next; - BLI_remlink(sockets, iosock); - if (after) { - BLI_insertlinkafter(sockets, after, iosock); - } - else { - BLI_addtail(sockets, iosock); - } - break; - } - } - - BKE_ntree_update_tag_interface(ntree); - ED_node_tree_propagate_change(C, CTX_data_main(C), ntree); - - WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr); - - return OPERATOR_FINISHED; -} - -void NODE_OT_tree_socket_move(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Move Node Tree Socket"; - ot->description = "Move a socket up or down in the active node tree's interface"; - ot->idname = "NODE_OT_tree_socket_move"; - - /* api callbacks */ - ot->exec = ntree_socket_move_exec; - ot->poll = ED_operator_node_editable; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - RNA_def_enum(ot->srna, "direction", move_direction_items, 1, "Direction", ""); - RNA_def_enum(ot->srna, "in_out", rna_enum_node_socket_in_out_items, SOCK_IN, "Socket Type", ""); -} - -/** \} */ - /* -------------------------------------------------------------------- */ /** \name Node Shader Script Update * \{ */ diff --git a/source/blender/editors/space_node/node_group.cc b/source/blender/editors/space_node/node_group.cc index c2a5136dd60..1680f5762da 100644 --- a/source/blender/editors/space_node/node_group.cc +++ b/source/blender/editors/space_node/node_group.cc @@ -907,9 +907,9 @@ static bool prefer_node_for_interface_name(const bNode &node) return node.is_group() || node.is_group_input() || node.is_group_output(); } -static bNodeSocket *add_interface_from_socket(const bNodeTree &original_tree, - bNodeTree &tree_for_interface, - const bNodeSocket &socket) +static bNodeTreeInterfaceSocket *add_interface_from_socket(const bNodeTree &original_tree, + bNodeTree &tree_for_interface, + const bNodeSocket &socket) { /* The "example socket" has to have the same `in_out` status as the new interface socket. */ const bNodeSocket &socket_for_io = find_socket_to_use_for_interface(original_tree, socket); @@ -917,11 +917,8 @@ static bNodeSocket *add_interface_from_socket(const bNodeTree &original_tree, const bNodeSocket &socket_for_name = prefer_node_for_interface_name(socket.owner_node()) ? socket : socket_for_io; - return bke::ntreeAddSocketInterfaceFromSocketWithName(&tree_for_interface, - &node_for_io, - &socket_for_io, - socket_for_io.idname, - socket_for_name.name); + return bke::node_interface::add_interface_socket_from_node( + tree_for_interface, node_for_io, socket_for_io, socket_for_io.idname, socket_for_name.name); } static void update_nested_node_refs_after_moving_nodes_into_group( @@ -1011,12 +1008,12 @@ static void node_group_make_insert_selected(const bContext &C, bNode *from_node; /* All the links that came from the socket on the unselected node. */ Vector links; - const bNodeSocket *interface_socket; + const bNodeTreeInterfaceSocket *interface_socket; }; struct OutputLinkInfo { bNodeLink *link; - const bNodeSocket *interface_socket; + const bNodeTreeInterfaceSocket *interface_socket; }; /* Map from single non-selected output sockets to potentially many selected input sockets. */ @@ -1029,6 +1026,30 @@ static void node_group_make_insert_selected(const bContext &C, ntree.ensure_topology_cache(); for (bNode *node : nodes_to_move) { + for (bNodeSocket *output_socket : node->output_sockets()) { + for (bNodeLink *link : output_socket->directly_linked_links()) { + if (nodeLinkIsHidden(link)) { + links_to_remove.add(link); + continue; + } + if (link->tonode == gnode) { + links_to_remove.add(link); + continue; + } + if (nodes_to_move.contains(link->tonode)) { + internal_links_to_move.add(link); + continue; + } + bNodeTreeInterfaceSocket *io_socket = add_interface_from_socket( + ntree, group, *link->fromsock); + if (io_socket) { + output_links.append({link, io_socket}); + } + else { + links_to_remove.add(link); + } + } + } for (bNodeSocket *input_socket : node->input_sockets()) { for (bNodeLink *link : input_socket->directly_linked_links()) { if (nodeLinkIsHidden(link)) { @@ -1049,23 +1070,9 @@ static void node_group_make_insert_selected(const bContext &C, if (!info.interface_socket) { info.interface_socket = add_interface_from_socket(ntree, group, *link->tosock); } - } - } - for (bNodeSocket *output_socket : node->output_sockets()) { - for (bNodeLink *link : output_socket->directly_linked_links()) { - if (nodeLinkIsHidden(link)) { + else { links_to_remove.add(link); - continue; } - if (link->tonode == gnode) { - links_to_remove.add(link); - continue; - } - if (nodes_to_move.contains(link->tonode)) { - internal_links_to_move.add(link); - continue; - } - output_links.append({link, add_interface_from_socket(ntree, group, *link->fromsock)}); } } } @@ -1073,7 +1080,7 @@ static void node_group_make_insert_selected(const bContext &C, struct NewInternalLinkInfo { bNode *node; bNodeSocket *socket; - const bNodeSocket *interface_socket; + const bNodeTreeInterfaceSocket *interface_socket; }; const bool expose_visible = nodes_to_move.size() == 1; @@ -1088,9 +1095,11 @@ static void node_group_make_insert_selected(const bContext &C, if (socket->is_directly_linked()) { continue; } - const bNodeSocket *io_socket = bke::ntreeAddSocketInterfaceFromSocket( - &group, node, socket); - new_internal_links.append({node, socket, io_socket}); + const bNodeTreeInterfaceSocket *io_socket = + bke::node_interface::add_interface_socket_from_node(group, *node, *socket); + if (io_socket) { + new_internal_links.append({node, socket, io_socket}); + } } }; expose_sockets(node->input_sockets()); @@ -1168,8 +1177,9 @@ static void node_group_make_insert_selected(const bContext &C, /* Handle links to the new group inputs. */ for (const auto item : input_links.items()) { - const char *interface_identifier = item.value.interface_socket->identifier; - bNodeSocket *input_socket = node_group_input_find_socket(input_node, interface_identifier); + const StringRefNull interface_identifier = item.value.interface_socket->identifier; + bNodeSocket *input_socket = node_group_input_find_socket(input_node, + interface_identifier.c_str()); for (bNodeLink *link : item.value.links) { /* Move the link into the new group, connected from the input node to the original socket. */ @@ -1185,20 +1195,21 @@ static void node_group_make_insert_selected(const bContext &C, /* Handle links to new group outputs. */ for (const OutputLinkInfo &info : output_links) { /* Create a new link inside of the group. */ - const char *io_identifier = info.interface_socket->identifier; - bNodeSocket *output_sock = node_group_output_find_socket(output_node, io_identifier); + const StringRefNull io_identifier = info.interface_socket->identifier; + bNodeSocket *output_sock = node_group_output_find_socket(output_node, io_identifier.c_str()); nodeAddLink(&group, info.link->fromnode, info.link->fromsock, output_node, output_sock); } /* Handle new links inside the group. */ for (const NewInternalLinkInfo &info : new_internal_links) { - const char *io_identifier = info.interface_socket->identifier; + const StringRefNull io_identifier = info.interface_socket->identifier; if (info.socket->in_out == SOCK_IN) { - bNodeSocket *input_socket = node_group_input_find_socket(input_node, io_identifier); + bNodeSocket *input_socket = node_group_input_find_socket(input_node, io_identifier.c_str()); nodeAddLink(&group, input_node, input_socket, info.node, info.socket); } else { - bNodeSocket *output_socket = node_group_output_find_socket(output_node, io_identifier); + bNodeSocket *output_socket = node_group_output_find_socket(output_node, + io_identifier.c_str()); nodeAddLink(&group, info.node, info.socket, output_node, output_socket); } } @@ -1212,8 +1223,9 @@ static void node_group_make_insert_selected(const bContext &C, /* Add new links to inputs outside of the group. */ for (const auto item : input_links.items()) { - const char *interface_identifier = item.value.interface_socket->identifier; - bNodeSocket *group_node_socket = node_group_find_input_socket(gnode, interface_identifier); + const StringRefNull interface_identifier = item.value.interface_socket->identifier; + bNodeSocket *group_node_socket = node_group_find_input_socket(gnode, + interface_identifier.c_str()); nodeAddLink(&ntree, item.value.from_node, item.key, gnode, group_node_socket); } diff --git a/source/blender/editors/space_node/node_intern.hh b/source/blender/editors/space_node/node_intern.hh index 525ec67f54d..5875dd7fd5b 100644 --- a/source/blender/editors/space_node/node_intern.hh +++ b/source/blender/editors/space_node/node_intern.hh @@ -162,11 +162,7 @@ int node_get_resize_cursor(NodeResizeDirection directions); * Usual convention here would be #node_socket_get_color(), * but that's already used (for setting a color property socket). */ -void node_socket_color_get(const bContext &C, - const bNodeTree &ntree, - PointerRNA &node_ptr, - const bNodeSocket &sock, - float r_color[4]); +void node_socket_color_get(const bNodeSocketType &type, float r_color[4]); /* `node_draw.cc` */ @@ -244,20 +240,15 @@ void nodelink_batch_end(SpaceNode &snode); /** * \note this is used for fake links in groups too. */ -void node_draw_link(const bContext &C, - const View2D &v2d, +void node_draw_link(const View2D &v2d, const SpaceNode &snode, const bNodeLink &link, bool selected); -void node_draw_link_dragged(const bContext &C, - const View2D &v2d, - const SpaceNode &snode, - const bNodeLink &link); +void node_draw_link_dragged(const View2D &v2d, const SpaceNode &snode, const bNodeLink &link); /** * Don't do shadows if th_col3 is -1. */ -void node_draw_link_bezier(const bContext &C, - const View2D &v2d, +void node_draw_link_bezier(const View2D &v2d, const SpaceNode &snode, const bNodeLink &link, int th_col1, @@ -379,12 +370,6 @@ void NODE_OT_switch_view_update(wmOperatorType *ot); void NODE_OT_clipboard_copy(wmOperatorType *ot); void NODE_OT_clipboard_paste(wmOperatorType *ot); -void NODE_OT_tree_socket_add(wmOperatorType *ot); -void NODE_OT_tree_socket_remove(wmOperatorType *ot); -void NODE_OT_tree_socket_change_type(wmOperatorType *ot); -void NODE_OT_tree_socket_change_subtype(wmOperatorType *ot); -void NODE_OT_tree_socket_move(wmOperatorType *ot); - void NODE_OT_shader_script_update(wmOperatorType *ot); void NODE_OT_viewer_border(wmOperatorType *ot); diff --git a/source/blender/editors/space_node/node_ops.cc b/source/blender/editors/space_node/node_ops.cc index d468bd7a4ac..b738bdda5fd 100644 --- a/source/blender/editors/space_node/node_ops.cc +++ b/source/blender/editors/space_node/node_ops.cc @@ -107,12 +107,6 @@ void node_operatortypes() WM_operatortype_append(NODE_OT_switch_view_update); - WM_operatortype_append(NODE_OT_tree_socket_add); - WM_operatortype_append(NODE_OT_tree_socket_remove); - WM_operatortype_append(NODE_OT_tree_socket_change_type); - WM_operatortype_append(NODE_OT_tree_socket_change_subtype); - WM_operatortype_append(NODE_OT_tree_socket_move); - WM_operatortype_append(NODE_OT_cryptomatte_layer_add); WM_operatortype_append(NODE_OT_cryptomatte_layer_remove); } diff --git a/source/blender/editors/space_node/node_relationships.cc b/source/blender/editors/space_node/node_relationships.cc index 8bfe75415c3..7f03fc1cdf9 100644 --- a/source/blender/editors/space_node/node_relationships.cc +++ b/source/blender/editors/space_node/node_relationships.cc @@ -2259,8 +2259,8 @@ bNodeSocket *get_main_socket(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_ bke::nodeDeclarationEnsure(&ntree, &node); const nodes::NodeDeclaration *node_decl = node.declaration(); if (node_decl != nullptr) { - Span socket_decls = (in_out == SOCK_IN) ? node_decl->inputs : - node_decl->outputs; + Span socket_decls = (in_out == SOCK_IN) ? node_decl->inputs : + node_decl->outputs; int index; LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, sockets, index) { const nodes::SocketDeclaration &socket_decl = *socket_decls[index]; diff --git a/source/blender/editors/space_node/node_shader_preview.cc b/source/blender/editors/space_node/node_shader_preview.cc index 8e5125ff948..2edd51c2341 100644 --- a/source/blender/editors/space_node/node_shader_preview.cc +++ b/source/blender/editors/space_node/node_shader_preview.cc @@ -376,7 +376,8 @@ static void connect_nested_node_to_node(const Span treepath, output_node->flag |= NODE_DO_OUTPUT; } - ntreeAddSocketInterface(nested_nt, SOCK_OUT, nested_socket_iter->idname, route_name); + nested_nt->tree_interface.add_socket( + route_name, "", nested_socket_iter->idname, NODE_INTERFACE_SOCKET_OUTPUT, nullptr); BKE_ntree_update_main_tree(G.pr_main, nested_nt, nullptr); bNodeSocket *out_socket = blender::bke::node_find_enabled_input_socket(*output_node, route_name); diff --git a/source/blender/editors/space_node/node_templates.cc b/source/blender/editors/space_node/node_templates.cc index e5024190c7e..3df6aa1f6a7 100644 --- a/source/blender/editors/space_node/node_templates.cc +++ b/source/blender/editors/space_node/node_templates.cc @@ -26,6 +26,8 @@ #include "BKE_context.h" #include "BKE_lib_id.h" #include "BKE_main.h" +#include "BKE_node_runtime.hh" +#include "BKE_node_tree_interface.hh" #include "BKE_node_tree_update.h" #include "RNA_access.hh" @@ -315,7 +317,6 @@ static Vector ui_node_link_items(NodeLinkArg *arg, /* XXX this should become a callback for node types! */ if (arg->node_type->type == NODE_GROUP) { bNodeTree *ngroup; - int i; for (ngroup = (bNodeTree *)arg->bmain->nodetrees.first; ngroup; ngroup = (bNodeTree *)ngroup->id.next) @@ -327,7 +328,6 @@ static Vector ui_node_link_items(NodeLinkArg *arg, } } - i = 0; for (ngroup = (bNodeTree *)arg->bmain->nodetrees.first; ngroup; ngroup = (bNodeTree *)ngroup->id.next) { @@ -337,17 +337,19 @@ static Vector ui_node_link_items(NodeLinkArg *arg, continue; } - ListBase *lb = (in_out == SOCK_IN ? &ngroup->inputs : &ngroup->outputs); - bNodeSocket *stemp; - int index; - for (stemp = (bNodeSocket *)lb->first, index = 0; stemp; stemp = stemp->next, index++, i++) { + Span iosockets = (in_out == SOCK_IN ? + ngroup->interface_inputs() : + ngroup->interface_outputs()); + for (const int index : iosockets.index_range()) { + bNodeTreeInterfaceSocket *iosock = iosockets[index]; NodeLinkItem item; item.socket_index = index; /* NOTE: int stemp->type is not fully reliable, not used for node group * interface sockets. use the typeinfo->type instead. */ - item.socket_type = stemp->typeinfo->type; - item.socket_name = stemp->name; + const bNodeSocketType *typeinfo = iosock->socket_typeinfo(); + item.socket_type = typeinfo->type; + item.socket_name = iosock->name; item.node_name = ngroup->id.name + 2; item.ngroup = ngroup; @@ -361,10 +363,10 @@ static Vector ui_node_link_items(NodeLinkArg *arg, r_node_decl.emplace(NodeDeclaration()); blender::nodes::build_node_declaration(*arg->node_type, *r_node_decl); - Span socket_decls = (in_out == SOCK_IN) ? r_node_decl->inputs : - r_node_decl->outputs; + Span socket_decls = (in_out == SOCK_IN) ? r_node_decl->inputs : + r_node_decl->outputs; int index = 0; - for (const SocketDeclarationPtr &socket_decl_ptr : socket_decls) { + for (const SocketDeclaration *socket_decl_ptr : socket_decls) { const SocketDeclaration &socket_decl = *socket_decl_ptr; NodeLinkItem item; item.socket_index = index++; @@ -709,7 +711,7 @@ static void ui_template_node_link_menu(bContext *C, uiLayout *layout, void *but_ } // namespace blender::ed::space_node void uiTemplateNodeLink( - uiLayout *layout, bContext *C, bNodeTree *ntree, bNode *node, bNodeSocket *input) + uiLayout *layout, bContext * /*C*/, bNodeTree *ntree, bNode *node, bNodeSocket *input) { using namespace blender::ed::space_node; @@ -723,9 +725,7 @@ void uiTemplateNodeLink( arg->node = node; arg->sock = input; - PointerRNA node_ptr; - RNA_pointer_create((ID *)ntree, &RNA_Node, node, &node_ptr); - node_socket_color_get(*C, *ntree, node_ptr, *input, socket_col); + node_socket_color_get(*input->typeinfo, socket_col); UI_block_layout_set_current(block, layout); diff --git a/source/blender/makesdna/DNA_node_tree_interface_types.h b/source/blender/makesdna/DNA_node_tree_interface_types.h index d808a3cd5c1..57aae560cf8 100644 --- a/source/blender/makesdna/DNA_node_tree_interface_types.h +++ b/source/blender/makesdna/DNA_node_tree_interface_types.h @@ -52,13 +52,13 @@ typedef struct bNodeTreeInterfaceItem { } bNodeTreeInterfaceItem; /* Socket interface flags */ -typedef enum eNodeTreeInterfaceSocketFlag { +typedef enum NodeTreeInterfaceSocketFlag { NODE_INTERFACE_SOCKET_INPUT = 1 << 0, NODE_INTERFACE_SOCKET_OUTPUT = 1 << 1, NODE_INTERFACE_SOCKET_HIDE_VALUE = 1 << 2, NODE_INTERFACE_SOCKET_HIDE_IN_MODIFIER = 1 << 3, -} eNodeTreeInterfaceSocketFlag; -ENUM_OPERATORS(eNodeTreeInterfaceSocketFlag, NODE_INTERFACE_SOCKET_HIDE_IN_MODIFIER); +} NodeTreeInterfaceSocketFlag; +ENUM_OPERATORS(NodeTreeInterfaceSocketFlag, NODE_INTERFACE_SOCKET_HIDE_IN_MODIFIER); typedef struct bNodeTreeInterfaceSocket { bNodeTreeInterfaceItem item; @@ -68,7 +68,7 @@ typedef struct bNodeTreeInterfaceSocket { char *description; /* Type idname of the socket to generate, e.g. "NodeSocketFloat". */ char *socket_type; - /* eNodeTreeInterfaceSocketFlag */ + /* NodeTreeInterfaceSocketFlag */ int flag; /* eAttrDomain */ @@ -100,11 +100,26 @@ typedef struct bNodeTreeInterfaceSocket { #endif } bNodeTreeInterfaceSocket; +/* Panel interface flags */ +typedef enum NodeTreeInterfacePanelFlag { + /* Panel starts closed on new node instances. */ + NODE_INTERFACE_PANEL_DEFAULT_CLOSED = 1 << 0, + /* Allow child panels inside this panel. */ + NODE_INTERFACE_PANEL_ALLOW_CHILD_PANELS = 1 << 1, + /* Allow adding sockets after panels. */ + NODE_INTERFACE_PANEL_ALLOW_SOCKETS_AFTER_PANELS = 1 << 2, +} NodeTreeInterfacePanelFlag; +ENUM_OPERATORS(NodeTreeInterfacePanelFlag, NODE_INTERFACE_PANEL_DEFAULT_CLOSED); + typedef struct bNodeTreeInterfacePanel { bNodeTreeInterfaceItem item; /* UI name of the panel. */ char *name; + char *description; + /* NodeTreeInterfacePanelFlag */ + int flag; + char _pad[4]; bNodeTreeInterfaceItem **items_array; int items_num; @@ -186,6 +201,11 @@ typedef struct bNodeTreeInterfacePanel { /** Same as above but for a const interface. */ void foreach_item(blender::FunctionRef fn, bool include_self = false) const; + + private: + /** Find a valid position for inserting in the items span. */ + int find_valid_insert_position_for_item(const bNodeTreeInterfaceItem &item, + int initial_position) const; #endif } bNodeTreeInterfacePanel; @@ -198,6 +218,8 @@ typedef struct bNodeTreeInterface { #ifdef __cplusplus + /** Initialize data of new interface instance. */ + void init_data(); /** Copy data from another interface. * \param flag: ID creation/copying flags, e.g. LIB_ID_CREATE_NO_MAIN. */ @@ -216,7 +238,19 @@ typedef struct bNodeTreeInterface { void active_item_set(bNodeTreeInterfaceItem *item); /** - * Get the index of the item in the interface. + * Get the position of the item in its parent panel. + * \return Position if the item was found or -1 otherwise. + */ + int find_item_position(const bNodeTreeInterfaceItem &item) const + { + /* const_cast to avoid a const version of #find_parent_recursive. */ + const bNodeTreeInterfacePanel *parent = + const_cast(root_panel).find_parent_recursive(item); + BLI_assert(parent != nullptr); + return parent->item_position(item); + } + /** + * Get the global index of the item in the interface. * \return Index if the item was found or -1 otherwise. */ int find_item_index(const bNodeTreeInterfaceItem &item) const @@ -255,7 +289,7 @@ typedef struct bNodeTreeInterface { bNodeTreeInterfaceSocket *add_socket(blender::StringRefNull name, blender::StringRefNull description, blender::StringRefNull socket_type, - eNodeTreeInterfaceSocketFlag flag, + NodeTreeInterfaceSocketFlag flag, bNodeTreeInterfacePanel *parent); /** * Insert a new socket. @@ -266,7 +300,7 @@ typedef struct bNodeTreeInterface { bNodeTreeInterfaceSocket *insert_socket(blender::StringRefNull name, blender::StringRefNull description, blender::StringRefNull socket_type, - eNodeTreeInterfaceSocketFlag flag, + NodeTreeInterfaceSocketFlag flag, bNodeTreeInterfacePanel *parent, int position); @@ -275,7 +309,10 @@ typedef struct bNodeTreeInterface { * \param parent: Panel in which the new panel is added as a child. If parent is null the new * panel is made a child of the root panel. */ - bNodeTreeInterfacePanel *add_panel(blender::StringRefNull name, bNodeTreeInterfacePanel *parent); + bNodeTreeInterfacePanel *add_panel(blender::StringRefNull name, + blender::StringRefNull description, + NodeTreeInterfacePanelFlag flag, + bNodeTreeInterfacePanel *parent); /** * Insert a new panel. * \param parent: Panel in which the new panel is added as a child. If parent is null the new @@ -283,6 +320,8 @@ typedef struct bNodeTreeInterface { * \param position: Position of the child panel within the parent panel. */ bNodeTreeInterfacePanel *insert_panel(blender::StringRefNull name, + blender::StringRefNull description, + NodeTreeInterfacePanelFlag flag, bNodeTreeInterfacePanel *parent, int position); diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 4dc8fdff619..8b6a9e34928 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -34,6 +34,7 @@ namespace blender::bke { class bNodeTreeRuntime; class bNodeRuntime; class bNodeSocketRuntime; +struct bNodeTreeInterfaceCache; } // namespace blender::bke namespace blender::bke { class bNodeTreeZones; @@ -187,6 +188,7 @@ typedef struct bNodeSocket { #ifdef __cplusplus bool is_hidden() const; bool is_available() const; + bool is_panel_collapsed() const; bool is_visible() const; bool is_multi_input() const; bool is_input() const; @@ -304,8 +306,30 @@ typedef enum eNodeSocketFlag { * Only used for geometry nodes. Don't show the socket value in the modifier interface. */ SOCK_HIDE_IN_MODIFIER = (1 << 13), + /** The panel containing the socket is collapsed. */ + SOCK_PANEL_COLLAPSED = (1 << 14), } eNodeSocketFlag; +typedef enum eNodePanelFlag { + /* Panel is collapsed (user setting). */ + NODE_PANEL_COLLAPSED = (1 << 0), + /* The parent panel is collapsed. */ + NODE_PANEL_PARENT_COLLAPSED = (1 << 1), +} eNodePanelFlag; + +typedef struct bNodePanelState { + /* Unique identifier for validating state against panels in node declaration. */ + int identifier; + /* eNodePanelFlag */ + char flag; + char _pad[3]; + +#ifdef __cplusplus + bool is_collapsed() const; + bool is_parent_collapsed() const; +#endif +} bNodePanelState; + typedef struct bNode { struct bNode *next, *prev; @@ -383,7 +407,9 @@ typedef struct bNode { /** Custom user-defined color. */ float color[3]; - char _pad2[4]; + /** Panel states for this node instance. */ + int num_panel_states; + bNodePanelState *panel_states_array; bNodeRuntimeHandle *runtime; @@ -423,6 +449,8 @@ typedef struct bNode { bNodeSocket &output_by_identifier(blender::StringRef identifier); /** If node is frame, will return all children nodes. */ blender::Span direct_children_in_frame() const; + blender::Span panel_states() const; + blender::MutableSpan panel_states(); /** Node tree this node belongs to. */ const bNodeTree &owner_tree() const; #endif @@ -636,7 +664,7 @@ typedef struct bNodeTree { * Warning! Don't make links to these sockets, input/output nodes are used for that. * These sockets are used only for generating external interfaces. */ - ListBase inputs, outputs; + ListBase inputs_legacy DNA_DEPRECATED, outputs_legacy DNA_DEPRECATED; bNodeTreeInterface tree_interface; @@ -735,12 +763,14 @@ typedef struct bNodeTree { const bNode *group_output_node() const; /** Get all input nodes of the node group. */ blender::Span group_input_nodes() const; - /** Inputs and outputs of the entire node group. */ - blender::Span interface_inputs() const; - blender::Span interface_outputs() const; /** Zones in the node tree. Currently there are only simulation zones in geometry nodes. */ const blender::bke::bNodeTreeZones *zones() const; + + /* Cached interface item lists. */ + blender::Span interface_inputs() const; + blender::Span interface_outputs() const; + blender::Span interface_items() const; #endif } bNodeTree; diff --git a/source/blender/makesdna/intern/dna_rename_defs.h b/source/blender/makesdna/intern/dna_rename_defs.h index 444cfd655f5..3349879a804 100644 --- a/source/blender/makesdna/intern/dna_rename_defs.h +++ b/source/blender/makesdna/intern/dna_rename_defs.h @@ -183,6 +183,8 @@ DNA_STRUCT_RENAME_ELEM(bPoseChannel, scaleIn, scale_in_x) DNA_STRUCT_RENAME_ELEM(bPoseChannel, scaleOut, scale_out_x) DNA_STRUCT_RENAME_ELEM(bPoseChannel, scale_in_y, scale_in_z) DNA_STRUCT_RENAME_ELEM(bPoseChannel, scale_out_y, scale_out_z) +DNA_STRUCT_RENAME_ELEM(bNodeTree, inputs, inputs_legacy) +DNA_STRUCT_RENAME_ELEM(bNodeTree, outputs, outputs_legacy) DNA_STRUCT_RENAME_ELEM(bSameVolumeConstraint, flag, free_axis) DNA_STRUCT_RENAME_ELEM(bSound, name, filepath) DNA_STRUCT_RENAME_ELEM(bTheme, tact, space_action) diff --git a/source/blender/makesrna/intern/rna_node_socket.cc b/source/blender/makesrna/intern/rna_node_socket.cc index fad7ce99f55..f343755aded 100644 --- a/source/blender/makesrna/intern/rna_node_socket.cc +++ b/source/blender/makesrna/intern/rna_node_socket.cc @@ -50,10 +50,7 @@ const EnumPropertyItem rna_enum_node_socket_type_items[] = { extern FunctionRNA rna_NodeSocket_draw_func; extern FunctionRNA rna_NodeSocket_draw_color_func; -extern FunctionRNA rna_NodeSocketInterface_draw_func; -extern FunctionRNA rna_NodeSocketInterface_draw_color_func; -extern FunctionRNA rna_NodeSocketInterface_init_socket_func; -extern FunctionRNA rna_NodeSocketInterface_from_socket_func; +extern FunctionRNA rna_NodeSocket_draw_color_simple_func; /* ******** Node Socket ******** */ @@ -99,6 +96,27 @@ static void rna_NodeSocket_draw_color(bContext *C, RNA_parameter_list_free(&list); } +static void rna_NodeSocket_draw_color_simple(const bNodeSocketType *socket_type, float *r_color) +{ + ParameterList list; + FunctionRNA *func; + void *ret; + + func = &rna_NodeSocket_draw_color_simple_func; /* RNA_struct_find_function(&ptr, + "draw_color_simple"); */ + + PointerRNA ptr; + RNA_pointer_create(nullptr, socket_type->ext_socket.srna, nullptr, &ptr); + RNA_parameter_list_create(&list, &ptr, func); + RNA_parameter_set_lookup(&list, "type", socket_type); + socket_type->ext_socket.call(nullptr, &ptr, func, &list); + + RNA_parameter_get_lookup(&list, "color", &ret); + copy_v4_v4(r_color, static_cast(ret)); + + RNA_parameter_list_free(&list); +} + static bool rna_NodeSocket_unregister(Main * /*bmain*/, StructRNA *type) { bNodeSocketType *st = static_cast(RNA_struct_blender_type_get(type)); @@ -127,7 +145,7 @@ static StructRNA *rna_NodeSocket_register(Main * /*bmain*/, bNodeSocketType *st, dummy_st; bNodeSocket dummy_sock; PointerRNA dummy_sock_ptr; - bool have_function[2]; + bool have_function[3]; /* setup dummy socket & socket type to store static properties in */ memset(&dummy_st, 0, sizeof(bNodeSocketType)); @@ -180,6 +198,7 @@ static StructRNA *rna_NodeSocket_register(Main * /*bmain*/, st->draw = (have_function[0]) ? rna_NodeSocket_draw : nullptr; st->draw_color = (have_function[1]) ? rna_NodeSocket_draw_color : nullptr; + st->draw_color_simple = (have_function[2]) ? rna_NodeSocket_draw_color_simple : nullptr; /* update while blender is running */ WM_main_add_notifier(NC_NODE | NA_EDITED, nullptr); @@ -285,236 +304,6 @@ static void rna_NodeSocket_hide_set(PointerRNA *ptr, bool value) } } -static void rna_NodeSocketInterface_draw(bContext *C, uiLayout *layout, PointerRNA *ptr) -{ - bNodeSocket *stemp = static_cast(ptr->data); - ParameterList list; - FunctionRNA *func; - - if (!stemp->typeinfo) { - return; - } - - func = &rna_NodeSocketInterface_draw_func; /* RNA_struct_find_function(&ptr, "draw"); */ - - RNA_parameter_list_create(&list, ptr, func); - RNA_parameter_set_lookup(&list, "context", &C); - RNA_parameter_set_lookup(&list, "layout", &layout); - stemp->typeinfo->ext_interface.call(C, ptr, func, &list); - - RNA_parameter_list_free(&list); -} - -static void rna_NodeSocketInterface_draw_color(bContext *C, PointerRNA *ptr, float *r_color) -{ - bNodeSocket *sock = static_cast(ptr->data); - ParameterList list; - FunctionRNA *func; - void *ret; - - if (!sock->typeinfo) { - return; - } - - func = - &rna_NodeSocketInterface_draw_color_func; /* RNA_struct_find_function(&ptr, "draw_color"); */ - - RNA_parameter_list_create(&list, ptr, func); - RNA_parameter_set_lookup(&list, "context", &C); - sock->typeinfo->ext_interface.call(C, ptr, func, &list); - - RNA_parameter_get_lookup(&list, "color", &ret); - copy_v4_v4(r_color, static_cast(ret)); - - RNA_parameter_list_free(&list); -} - -static void rna_NodeSocketInterface_init_socket(bNodeTree *ntree, - const bNodeSocket *interface_socket, - bNode *node, - bNodeSocket *sock, - const char *data_path) -{ - PointerRNA ptr, node_ptr, sock_ptr; - ParameterList list; - FunctionRNA *func; - - if (!interface_socket->typeinfo) { - return; - } - - RNA_pointer_create( - &ntree->id, &RNA_NodeSocketInterface, const_cast(interface_socket), &ptr); - RNA_pointer_create(&ntree->id, &RNA_Node, node, &node_ptr); - RNA_pointer_create(&ntree->id, &RNA_NodeSocket, sock, &sock_ptr); - // RNA_struct_find_function(&ptr, "init_socket"); - func = &rna_NodeSocketInterface_init_socket_func; - - RNA_parameter_list_create(&list, &ptr, func); - RNA_parameter_set_lookup(&list, "node", &node_ptr); - RNA_parameter_set_lookup(&list, "socket", &sock_ptr); - RNA_parameter_set_lookup(&list, "data_path", &data_path); - interface_socket->typeinfo->ext_interface.call(nullptr, &ptr, func, &list); - - RNA_parameter_list_free(&list); -} - -static void rna_NodeSocketInterface_from_socket(bNodeTree *ntree, - bNodeSocket *interface_socket, - const bNode *node, - const bNodeSocket *sock) -{ - PointerRNA ptr, node_ptr, sock_ptr; - ParameterList list; - FunctionRNA *func; - - if (!interface_socket->typeinfo) { - return; - } - - RNA_pointer_create(&ntree->id, &RNA_NodeSocketInterface, interface_socket, &ptr); - RNA_pointer_create(&ntree->id, &RNA_Node, const_cast(node), &node_ptr); - RNA_pointer_create(&ntree->id, &RNA_NodeSocket, const_cast(sock), &sock_ptr); - // RNA_struct_find_function(&ptr, "from_socket"); - func = &rna_NodeSocketInterface_from_socket_func; - - RNA_parameter_list_create(&list, &ptr, func); - RNA_parameter_set_lookup(&list, "node", &node_ptr); - RNA_parameter_set_lookup(&list, "socket", &sock_ptr); - interface_socket->typeinfo->ext_interface.call(nullptr, &ptr, func, &list); - - RNA_parameter_list_free(&list); -} - -static bool rna_NodeSocketInterface_unregister(Main * /*bmain*/, StructRNA *type) -{ - bNodeSocketType *st = static_cast(RNA_struct_blender_type_get(type)); - if (!st) { - return false; - } - - RNA_struct_free_extension(type, &st->ext_interface); - - RNA_struct_free(&BLENDER_RNA, type); - - /* update while blender is running */ - WM_main_add_notifier(NC_NODE | NA_EDITED, nullptr); - return true; -} - -static StructRNA *rna_NodeSocketInterface_register(Main * /*bmain*/, - ReportList * /*reports*/, - void *data, - const char *identifier, - StructValidateFunc validate, - StructCallbackFunc call, - StructFreeFunc free) -{ - bNodeSocketType *st, dummy_st; - bNodeSocket dummy_sock; - PointerRNA dummy_sock_ptr; - bool have_function[4]; - - /* setup dummy socket & socket type to store static properties in */ - memset(&dummy_st, 0, sizeof(bNodeSocketType)); - - memset(&dummy_sock, 0, sizeof(bNodeSocket)); - dummy_sock.typeinfo = &dummy_st; - RNA_pointer_create(nullptr, &RNA_NodeSocketInterface, &dummy_sock, &dummy_sock_ptr); - - /* validate the python class */ - if (validate(&dummy_sock_ptr, data, have_function) != 0) { - return nullptr; - } - - /* check if we have registered this socket type before */ - st = nodeSocketTypeFind(dummy_st.idname); - if (st) { - /* basic socket type registered by a socket class before. */ - } - else { - /* create a new node socket type */ - st = static_cast(MEM_mallocN(sizeof(bNodeSocketType), "node socket type")); - memcpy(st, &dummy_st, sizeof(dummy_st)); - - nodeRegisterSocketType(st); - } - - st->free_self = (void (*)(bNodeSocketType * stype)) MEM_freeN; - - /* if RNA type is already registered, unregister first */ - if (st->ext_interface.srna) { - StructRNA *srna = st->ext_interface.srna; - RNA_struct_free_extension(srna, &st->ext_interface); - RNA_struct_free(&BLENDER_RNA, srna); - } - st->ext_interface.srna = RNA_def_struct_ptr(&BLENDER_RNA, identifier, &RNA_NodeSocketInterface); - st->ext_interface.data = data; - st->ext_interface.call = call; - st->ext_interface.free = free; - RNA_struct_blender_type_set(st->ext_interface.srna, st); - - st->interface_draw = (have_function[0]) ? rna_NodeSocketInterface_draw : nullptr; - st->interface_draw_color = (have_function[1]) ? rna_NodeSocketInterface_draw_color : nullptr; - st->interface_init_socket = (have_function[2]) ? rna_NodeSocketInterface_init_socket : nullptr; - st->interface_from_socket = (have_function[3]) ? rna_NodeSocketInterface_from_socket : nullptr; - - /* update while blender is running */ - WM_main_add_notifier(NC_NODE | NA_EDITED, nullptr); - - return st->ext_interface.srna; -} - -static StructRNA *rna_NodeSocketInterface_refine(PointerRNA *ptr) -{ - bNodeSocket *sock = static_cast(ptr->data); - - if (sock->typeinfo && sock->typeinfo->ext_interface.srna) { - return sock->typeinfo->ext_interface.srna; - } - else { - return &RNA_NodeSocketInterface; - } -} - -static char *rna_NodeSocketInterface_path(const PointerRNA *ptr) -{ - const bNodeTree *ntree = reinterpret_cast(ptr->owner_id); - const bNodeSocket *sock = static_cast(ptr->data); - int socketindex; - - socketindex = BLI_findindex(&ntree->inputs, sock); - if (socketindex != -1) { - return BLI_sprintfN("inputs[%d]", socketindex); - } - - socketindex = BLI_findindex(&ntree->outputs, sock); - if (socketindex != -1) { - return BLI_sprintfN("outputs[%d]", socketindex); - } - - return nullptr; -} - -static IDProperty **rna_NodeSocketInterface_idprops(PointerRNA *ptr) -{ - bNodeSocket *sock = static_cast(ptr->data); - return &sock->prop; -} - -static void rna_NodeSocketInterface_update(Main *bmain, Scene * /*scene*/, PointerRNA *ptr) -{ - bNodeTree *ntree = reinterpret_cast(ptr->owner_id); - bNodeSocket *stemp = static_cast(ptr->data); - - if (!stemp->typeinfo) { - return; - } - - BKE_ntree_update_tag_interface(ntree); - ED_node_tree_propagate_change(nullptr, bmain, ntree); -} - /* ******** Standard Node Socket Base Types ******** */ static void rna_NodeSocketStandard_draw(ID *id, @@ -537,25 +326,13 @@ static void rna_NodeSocketStandard_draw_color( sock->typeinfo->draw_color(C, &ptr, nodeptr, r_color); } -static void rna_NodeSocketInterfaceStandard_draw(ID *id, - bNodeSocket *sock, - bContext *C, - uiLayout *layout) +static void rna_NodeSocketStandard_draw_color_simple(struct StructRNA *type, float r_color[4]) { - PointerRNA ptr; - RNA_pointer_create(id, &RNA_NodeSocketInterface, sock, &ptr); - sock->typeinfo->interface_draw(C, layout, &ptr); + const bNodeSocketType *typeinfo = static_cast( + RNA_struct_blender_type_get(type)); + typeinfo->draw_color_simple(typeinfo, r_color); } -static void rna_NodeSocketInterfaceStandard_draw_color(ID *id, - bNodeSocket *sock, - bContext *C, - float r_color[4]) -{ - PointerRNA ptr; - RNA_pointer_create(id, &RNA_NodeSocketInterface, sock, &ptr); - sock->typeinfo->interface_draw_color(C, &ptr, r_color); -} /* ******** Node Socket Subtypes ******** */ void rna_NodeSocketStandard_float_range( @@ -795,7 +572,7 @@ static void rna_def_node_socket(BlenderRNA *brna) func = RNA_def_function(srna, "draw_color", nullptr); RNA_def_function_ui_description(func, "Color of the socket icon"); - RNA_def_function_flag(func, FUNC_REGISTER); + RNA_def_function_flag(func, FUNC_REGISTER_OPTIONAL); parm = RNA_def_pointer(func, "context", "Context", "", ""); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); parm = RNA_def_property(func, "node", PROP_POINTER, PROP_NONE); @@ -805,135 +582,13 @@ static void rna_def_node_socket(BlenderRNA *brna) parm = RNA_def_float_array( func, "color", 4, default_draw_color, 0.0f, 1.0f, "Color", "", 0.0f, 1.0f); RNA_def_function_output(func, parm); -} -static void rna_def_node_socket_interface(BlenderRNA *brna) -{ - StructRNA *srna; - PropertyRNA *prop; - PropertyRNA *parm; - FunctionRNA *func; - - static float default_draw_color[] = {0.0f, 0.0f, 0.0f, 1.0f}; - - srna = RNA_def_struct(brna, "NodeSocketInterface", nullptr); - RNA_def_struct_ui_text(srna, "Node Socket Template", "Parameters to define node sockets"); - /* XXX Using bNodeSocket DNA for templates is a compatibility hack. - * This allows to keep the inputs/outputs lists in bNodeTree working for earlier versions - * and at the same time use them for socket templates in groups. - */ - RNA_def_struct_sdna(srna, "bNodeSocket"); - RNA_def_struct_refine_func(srna, "rna_NodeSocketInterface_refine"); - RNA_def_struct_path_func(srna, "rna_NodeSocketInterface_path"); - RNA_def_struct_idprops_func(srna, "rna_NodeSocketInterface_idprops"); - RNA_def_struct_register_funcs( - srna, "rna_NodeSocketInterface_register", "rna_NodeSocketInterface_unregister", nullptr); - - prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); - RNA_def_property_ui_text(prop, "Name", "Socket name"); - RNA_def_struct_name_property(srna, prop); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); - - prop = RNA_def_property(srna, "identifier", PROP_STRING, PROP_NONE); - RNA_def_property_string_sdna(prop, nullptr, "identifier"); - RNA_def_property_clear_flag(prop, PROP_EDITABLE); - RNA_def_property_ui_text(prop, "Identifier", "Unique identifier for mapping sockets"); - - prop = RNA_def_property(srna, "description", PROP_STRING, PROP_NONE); - RNA_def_property_string_sdna(prop, nullptr, "description"); - RNA_def_property_ui_text(prop, "Tooltip", "Socket tooltip"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); - - prop = RNA_def_property(srna, "is_output", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_funcs(prop, "rna_NodeSocket_is_output_get", nullptr); - RNA_def_property_clear_flag(prop, PROP_EDITABLE); - RNA_def_property_ui_text(prop, "Is Output", "True if the socket is an output, otherwise input"); - - prop = RNA_def_property(srna, "hide_value", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, nullptr, "flag", SOCK_HIDE_VALUE); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_ui_text( - prop, "Hide Value", "Hide the socket input value even when the socket is not connected"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); - - prop = RNA_def_property(srna, "hide_in_modifier", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, nullptr, "flag", SOCK_HIDE_IN_MODIFIER); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_ui_text(prop, - "Hide in Modifier", - "Don't show the input value in the geometry nodes modifier interface"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); - - prop = RNA_def_property(srna, "attribute_domain", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, rna_enum_attribute_domain_items); - RNA_def_property_ui_text( - prop, - "Attribute Domain", - "Attribute domain used by the geometry nodes modifier to create an attribute output"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); - - prop = RNA_def_property(srna, "default_attribute_name", PROP_STRING, PROP_NONE); - RNA_def_property_string_sdna(prop, nullptr, "default_attribute_name"); - RNA_def_property_ui_text(prop, - "Default Attribute", - "The attribute name used by default when the node group is used by a " - "geometry nodes modifier"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); - - /* registration */ - prop = RNA_def_property(srna, "bl_socket_idname", PROP_STRING, PROP_NONE); - RNA_def_property_string_sdna(prop, nullptr, "typeinfo->idname"); - RNA_def_property_flag(prop, PROP_REGISTER); - RNA_def_property_ui_text(prop, "ID Name", ""); - - prop = RNA_def_property(srna, "bl_label", PROP_STRING, PROP_NONE); - RNA_def_property_string_sdna(prop, nullptr, "typeinfo->label"); - RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL); - RNA_def_property_ui_text(prop, "Type Label", "Label to display for the socket type in the UI"); - - prop = RNA_def_property(srna, "bl_subtype_label", PROP_STRING, PROP_NONE); - RNA_def_property_string_sdna(prop, nullptr, "typeinfo->subtype_label"); - RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL); - RNA_def_property_ui_text( - prop, "Subtype Label", "Label to display for the socket subtype in the UI"); - - func = RNA_def_function(srna, "draw", nullptr); - RNA_def_function_ui_description(func, "Draw template settings"); - RNA_def_function_flag(func, FUNC_REGISTER_OPTIONAL); - parm = RNA_def_pointer(func, "context", "Context", "", ""); - RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); - parm = RNA_def_property(func, "layout", PROP_POINTER, PROP_NONE); - RNA_def_property_struct_type(parm, "UILayout"); - RNA_def_property_ui_text(parm, "Layout", "Layout in the UI"); - RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); - - func = RNA_def_function(srna, "draw_color", nullptr); + func = RNA_def_function(srna, "draw_color_simple", nullptr); RNA_def_function_ui_description(func, "Color of the socket icon"); - RNA_def_function_flag(func, FUNC_REGISTER); - parm = RNA_def_pointer(func, "context", "Context", "", ""); - RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + RNA_def_function_flag(func, FUNC_NO_SELF | FUNC_REGISTER_OPTIONAL); parm = RNA_def_float_array( func, "color", 4, default_draw_color, 0.0f, 1.0f, "Color", "", 0.0f, 1.0f); RNA_def_function_output(func, parm); - - func = RNA_def_function(srna, "init_socket", nullptr); - RNA_def_function_ui_description(func, "Initialize a node socket instance"); - RNA_def_function_flag(func, FUNC_REGISTER_OPTIONAL | FUNC_ALLOW_WRITE); - parm = RNA_def_pointer(func, "node", "Node", "Node", "Node of the socket to initialize"); - RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); - parm = RNA_def_pointer(func, "socket", "NodeSocket", "Socket", "Socket to initialize"); - RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); - parm = RNA_def_string( - func, "data_path", nullptr, 0, "Data Path", "Path to specialized socket data"); - RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); - - func = RNA_def_function(srna, "from_socket", nullptr); - RNA_def_function_ui_description(func, "Setup template parameters from an existing socket"); - RNA_def_function_flag(func, FUNC_REGISTER_OPTIONAL | FUNC_ALLOW_WRITE); - parm = RNA_def_pointer(func, "node", "Node", "Node", "Node of the original socket"); - RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); - parm = RNA_def_pointer(func, "socket", "NodeSocket", "Socket", "Original socket"); - RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); } static void rna_def_node_socket_standard(BlenderRNA *brna) @@ -947,9 +602,8 @@ static void rna_def_node_socket_standard(BlenderRNA *brna) */ StructRNA *srna; - PropertyRNA *prop; - FunctionRNA *func; PropertyRNA *parm; + FunctionRNA *func; static float default_draw_color[] = {0.0f, 0.0f, 0.0f, 1.0f}; @@ -988,627 +642,14 @@ static void rna_def_node_socket_standard(BlenderRNA *brna) func, "color", 4, default_draw_color, 0.0f, 1.0f, "Color", "", 0.0f, 1.0f); RNA_def_function_output(func, parm); - /* Note: Legacy socket interface below. - * The new interface RNA is defined in a separate file, - * the NodeSocketInterface struct will be replaced. */ - - srna = RNA_def_struct(brna, "NodeSocketInterfaceStandard", "NodeSocketInterface"); - RNA_def_struct_sdna(srna, "bNodeSocket"); - - /* for easier type comparison in python */ - prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_sdna(prop, nullptr, "typeinfo->type"); - RNA_def_property_enum_items(prop, rna_enum_node_socket_type_items); - RNA_def_property_enum_default(prop, SOCK_FLOAT); - RNA_def_property_clear_flag(prop, PROP_EDITABLE); - RNA_def_property_ui_text(prop, "Type", "Data type"); - - func = RNA_def_function(srna, "draw", "rna_NodeSocketInterfaceStandard_draw"); - RNA_def_function_flag(func, FUNC_USE_SELF_ID); - RNA_def_function_ui_description(func, "Draw template settings"); - parm = RNA_def_pointer(func, "context", "Context", "", ""); - RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); - parm = RNA_def_property(func, "layout", PROP_POINTER, PROP_NONE); - RNA_def_property_struct_type(parm, "UILayout"); - RNA_def_property_ui_text(parm, "Layout", "Layout in the UI"); - RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); - - func = RNA_def_function(srna, "draw_color", "rna_NodeSocketInterfaceStandard_draw_color"); - RNA_def_function_flag(func, FUNC_USE_SELF_ID); + func = RNA_def_function(srna, "draw_color_simple", "rna_NodeSocketStandard_draw_color_simple"); RNA_def_function_ui_description(func, "Color of the socket icon"); - parm = RNA_def_pointer(func, "context", "Context", "", ""); - RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + RNA_def_function_flag(func, FUNC_NO_SELF | FUNC_USE_SELF_TYPE | FUNC_REGISTER_OPTIONAL); parm = RNA_def_float_array( func, "color", 4, default_draw_color, 0.0f, 1.0f, "Color", "", 0.0f, 1.0f); RNA_def_function_output(func, parm); } -static void rna_def_node_socket_float(BlenderRNA *brna, - const char *idname, - const char *interface_idname, - PropertySubType subtype) -{ - StructRNA *srna; - PropertyRNA *prop; - float value_default; - - /* choose sensible common default based on subtype */ - switch (subtype) { - case PROP_FACTOR: - value_default = 1.0f; - break; - case PROP_PERCENTAGE: - value_default = 100.0f; - break; - default: - value_default = 0.0f; - break; - } - - srna = RNA_def_struct(brna, idname, "NodeSocketStandard"); - RNA_def_struct_ui_text(srna, "Float Node Socket", "Floating-point number socket of a node"); - RNA_def_struct_sdna(srna, "bNodeSocket"); - - RNA_def_struct_sdna_from(srna, "bNodeSocketValueFloat", "default_value"); - - prop = RNA_def_property(srna, "default_value", PROP_FLOAT, subtype); - RNA_def_property_float_sdna(prop, nullptr, "value"); - RNA_def_property_float_funcs(prop, nullptr, nullptr, "rna_NodeSocketStandard_float_range"); - RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_update"); - RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); - - RNA_def_struct_sdna_from(srna, "bNodeSocket", nullptr); - - /* socket interface */ - srna = RNA_def_struct(brna, interface_idname, "NodeSocketInterfaceStandard"); - RNA_def_struct_ui_text( - srna, "Float Node Socket Interface", "Floating-point number socket of a node"); - RNA_def_struct_sdna(srna, "bNodeSocket"); - - RNA_def_struct_sdna_from(srna, "bNodeSocketValueFloat", "default_value"); - - prop = RNA_def_property(srna, "default_value", PROP_FLOAT, subtype); - RNA_def_property_float_sdna(prop, nullptr, "value"); - RNA_def_property_float_default(prop, value_default); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_float_funcs(prop, nullptr, nullptr, "rna_NodeSocketStandard_float_range"); - RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); - - prop = RNA_def_property(srna, "min_value", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, nullptr, "min"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_ui_text(prop, "Minimum Value", "Minimum value"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); - - prop = RNA_def_property(srna, "max_value", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, nullptr, "max"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_ui_text(prop, "Maximum Value", "Maximum value"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); - - RNA_def_struct_sdna_from(srna, "bNodeSocket", nullptr); -} - -static void rna_def_node_socket_int(BlenderRNA *brna, - const char *identifier, - const char *interface_idname, - PropertySubType subtype) -{ - StructRNA *srna; - PropertyRNA *prop; - int value_default; - - /* choose sensible common default based on subtype */ - switch (subtype) { - case PROP_FACTOR: - value_default = 1; - break; - case PROP_PERCENTAGE: - value_default = 100; - break; - default: - value_default = 0; - break; - } - - srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); - RNA_def_struct_ui_text(srna, "Integer Node Socket", "Integer number socket of a node"); - RNA_def_struct_sdna(srna, "bNodeSocket"); - - RNA_def_struct_sdna_from(srna, "bNodeSocketValueInt", "default_value"); - - prop = RNA_def_property(srna, "default_value", PROP_INT, subtype); - RNA_def_property_int_sdna(prop, nullptr, "value"); - RNA_def_property_int_default(prop, value_default); - RNA_def_property_int_funcs(prop, nullptr, nullptr, "rna_NodeSocketStandard_int_range"); - RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_update"); - RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); - - RNA_def_struct_sdna_from(srna, "bNodeSocket", nullptr); - - /* socket interface */ - srna = RNA_def_struct(brna, interface_idname, "NodeSocketInterfaceStandard"); - RNA_def_struct_ui_text(srna, "Integer Node Socket Interface", "Integer number socket of a node"); - RNA_def_struct_sdna(srna, "bNodeSocket"); - - RNA_def_struct_sdna_from(srna, "bNodeSocketValueInt", "default_value"); - - prop = RNA_def_property(srna, "default_value", PROP_INT, subtype); - RNA_def_property_int_sdna(prop, nullptr, "value"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_int_funcs(prop, nullptr, nullptr, "rna_NodeSocketStandard_int_range"); - RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); - - prop = RNA_def_property(srna, "min_value", PROP_INT, PROP_NONE); - RNA_def_property_int_sdna(prop, nullptr, "min"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_ui_text(prop, "Minimum Value", "Minimum value"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); - - prop = RNA_def_property(srna, "max_value", PROP_INT, PROP_NONE); - RNA_def_property_int_sdna(prop, nullptr, "max"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_ui_text(prop, "Maximum Value", "Maximum value"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); - - RNA_def_struct_sdna_from(srna, "bNodeSocket", nullptr); -} - -static void rna_def_node_socket_bool(BlenderRNA *brna, - const char *identifier, - const char *interface_idname) -{ - StructRNA *srna; - PropertyRNA *prop; - - srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); - RNA_def_struct_ui_text(srna, "Boolean Node Socket", "Boolean value socket of a node"); - RNA_def_struct_sdna(srna, "bNodeSocket"); - - RNA_def_struct_sdna_from(srna, "bNodeSocketValueBoolean", "default_value"); - - prop = RNA_def_property(srna, "default_value", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, nullptr, "value", 1); - RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_update"); - RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); - - RNA_def_struct_sdna_from(srna, "bNodeSocket", nullptr); - - /* socket interface */ - srna = RNA_def_struct(brna, interface_idname, "NodeSocketInterfaceStandard"); - RNA_def_struct_ui_text(srna, "Boolean Node Socket Interface", "Boolean value socket of a node"); - RNA_def_struct_sdna(srna, "bNodeSocket"); - - RNA_def_struct_sdna_from(srna, "bNodeSocketValueBoolean", "default_value"); - - prop = RNA_def_property(srna, "default_value", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, nullptr, "value", 1); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); - - RNA_def_struct_sdna_from(srna, "bNodeSocket", nullptr); -} - -static void rna_def_node_socket_rotation(BlenderRNA *brna, - const char *identifier, - const char *interface_idname) -{ - StructRNA *srna; - PropertyRNA *prop; - - srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); - RNA_def_struct_ui_text(srna, "Rotation Node Socket", "Rotation value socket of a node"); - RNA_def_struct_sdna(srna, "bNodeSocket"); - - RNA_def_struct_sdna_from(srna, "bNodeSocketValueRotation", "default_value"); - - prop = RNA_def_property(srna, "default_value", PROP_FLOAT, PROP_EULER); - RNA_def_property_float_sdna(prop, nullptr, "value_euler"); - // RNA_def_property_array(prop, 3); - RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); - - RNA_def_struct_sdna_from(srna, "bNodeSocket", nullptr); - - /* socket interface */ - srna = RNA_def_struct(brna, interface_idname, "NodeSocketInterfaceStandard"); - RNA_def_struct_ui_text( - srna, "Rotation Node Socket Interface", "Rotation value socket of a node"); - RNA_def_struct_sdna(srna, "bNodeSocket"); - - RNA_def_struct_sdna_from(srna, "bNodeSocketValueRotation", "default_value"); - - prop = RNA_def_property(srna, "default_value", PROP_FLOAT, PROP_EULER); - RNA_def_property_float_sdna(prop, nullptr, "value_euler"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); - - RNA_def_struct_sdna_from(srna, "bNodeSocket", nullptr); -} - -static void rna_def_node_socket_vector(BlenderRNA *brna, - const char *identifier, - const char *interface_idname, - PropertySubType subtype) -{ - StructRNA *srna; - PropertyRNA *prop; - const float *value_default; - - /* choose sensible common default based on subtype */ - switch (subtype) { - case PROP_DIRECTION: { - static const float default_direction[3] = {0.0f, 0.0f, 1.0f}; - value_default = default_direction; - break; - } - default: { - static const float default_vector[3] = {0.0f, 0.0f, 0.0f}; - value_default = default_vector; - break; - } - } - - srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); - RNA_def_struct_ui_text(srna, "Vector Node Socket", "3D vector socket of a node"); - RNA_def_struct_sdna(srna, "bNodeSocket"); - - RNA_def_struct_sdna_from(srna, "bNodeSocketValueVector", "default_value"); - - prop = RNA_def_property(srna, "default_value", PROP_FLOAT, subtype); - RNA_def_property_float_sdna(prop, nullptr, "value"); - RNA_def_property_float_array_default(prop, value_default); - RNA_def_property_float_funcs(prop, nullptr, nullptr, "rna_NodeSocketStandard_vector_range"); - RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_update"); - RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); - - RNA_def_struct_sdna_from(srna, "bNodeSocket", nullptr); - - /* socket interface */ - srna = RNA_def_struct(brna, interface_idname, "NodeSocketInterfaceStandard"); - RNA_def_struct_ui_text(srna, "Vector Node Socket Interface", "3D vector socket of a node"); - RNA_def_struct_sdna(srna, "bNodeSocket"); - - RNA_def_struct_sdna_from(srna, "bNodeSocketValueVector", "default_value"); - - prop = RNA_def_property(srna, "default_value", PROP_FLOAT, subtype); - RNA_def_property_float_sdna(prop, nullptr, "value"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_float_funcs(prop, nullptr, nullptr, "rna_NodeSocketStandard_vector_range"); - RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); - - prop = RNA_def_property(srna, "min_value", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, nullptr, "min"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_ui_text(prop, "Minimum Value", "Minimum value"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); - - prop = RNA_def_property(srna, "max_value", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, nullptr, "max"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_ui_text(prop, "Maximum Value", "Maximum value"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); - - RNA_def_struct_sdna_from(srna, "bNodeSocket", nullptr); -} - -static void rna_def_node_socket_color(BlenderRNA *brna, - const char *identifier, - const char *interface_idname) -{ - StructRNA *srna; - PropertyRNA *prop; - - srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); - RNA_def_struct_ui_text(srna, "Color Node Socket", "RGBA color socket of a node"); - RNA_def_struct_sdna(srna, "bNodeSocket"); - - RNA_def_struct_sdna_from(srna, "bNodeSocketValueRGBA", "default_value"); - - prop = RNA_def_property(srna, "default_value", PROP_FLOAT, PROP_COLOR); - RNA_def_property_float_sdna(prop, nullptr, "value"); - RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_update"); - RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); - - RNA_def_struct_sdna_from(srna, "bNodeSocket", nullptr); - - /* socket interface */ - srna = RNA_def_struct(brna, interface_idname, "NodeSocketInterfaceStandard"); - RNA_def_struct_ui_text(srna, "Color Node Socket Interface", "RGBA color socket of a node"); - RNA_def_struct_sdna(srna, "bNodeSocket"); - - RNA_def_struct_sdna_from(srna, "bNodeSocketValueRGBA", "default_value"); - - prop = RNA_def_property(srna, "default_value", PROP_FLOAT, PROP_COLOR); - RNA_def_property_float_sdna(prop, nullptr, "value"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); - - RNA_def_struct_sdna_from(srna, "bNodeSocket", nullptr); -} - -static void rna_def_node_socket_string(BlenderRNA *brna, - const char *identifier, - const char *interface_idname) -{ - StructRNA *srna; - PropertyRNA *prop; - - srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); - RNA_def_struct_ui_text(srna, "String Node Socket", "String socket of a node"); - RNA_def_struct_sdna(srna, "bNodeSocket"); - - RNA_def_struct_sdna_from(srna, "bNodeSocketValueString", "default_value"); - - prop = RNA_def_property(srna, "default_value", PROP_STRING, PROP_NONE); - RNA_def_property_string_sdna(prop, nullptr, "value"); - RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_update"); - RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); - - RNA_def_struct_sdna_from(srna, "bNodeSocket", nullptr); - - /* socket interface */ - srna = RNA_def_struct(brna, interface_idname, "NodeSocketInterfaceStandard"); - RNA_def_struct_ui_text(srna, "String Node Socket Interface", "String socket of a node"); - RNA_def_struct_sdna(srna, "bNodeSocket"); - - RNA_def_struct_sdna_from(srna, "bNodeSocketValueString", "default_value"); - - prop = RNA_def_property(srna, "default_value", PROP_STRING, PROP_NONE); - RNA_def_property_string_sdna(prop, nullptr, "value"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); - - RNA_def_struct_sdna_from(srna, "bNodeSocket", nullptr); -} - -static void rna_def_node_socket_shader(BlenderRNA *brna, - const char *identifier, - const char *interface_idname) -{ - StructRNA *srna; - - srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); - RNA_def_struct_ui_text(srna, "Shader Node Socket", "Shader socket of a node"); - RNA_def_struct_sdna(srna, "bNodeSocket"); - - /* socket interface */ - srna = RNA_def_struct(brna, interface_idname, "NodeSocketInterfaceStandard"); - RNA_def_struct_ui_text(srna, "Shader Node Socket Interface", "Shader socket of a node"); - RNA_def_struct_sdna(srna, "bNodeSocket"); -} - -static void rna_def_node_socket_virtual(BlenderRNA *brna, const char *identifier) -{ - StructRNA *srna; - - srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); - RNA_def_struct_ui_text(srna, "Virtual Node Socket", "Virtual socket of a node"); - RNA_def_struct_sdna(srna, "bNodeSocket"); -} - -static void rna_def_node_socket_object(BlenderRNA *brna, - const char *identifier, - const char *interface_idname) -{ - StructRNA *srna; - PropertyRNA *prop; - - srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); - RNA_def_struct_ui_text(srna, "Object Node Socket", "Object socket of a node"); - RNA_def_struct_sdna(srna, "bNodeSocket"); - - RNA_def_struct_sdna_from(srna, "bNodeSocketValueObject", "default_value"); - - prop = RNA_def_property(srna, "default_value", PROP_POINTER, PROP_NONE); - RNA_def_property_pointer_sdna(prop, nullptr, "value"); - RNA_def_property_struct_type(prop, "Object"); - RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update( - prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_and_relation_update"); - RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT | PROP_CONTEXT_UPDATE); - RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); - - /* socket interface */ - srna = RNA_def_struct(brna, interface_idname, "NodeSocketInterfaceStandard"); - RNA_def_struct_ui_text(srna, "Object Node Socket Interface", "Object socket of a node"); - RNA_def_struct_sdna(srna, "bNodeSocket"); - - RNA_def_struct_sdna_from(srna, "bNodeSocketValueObject", "default_value"); - - prop = RNA_def_property(srna, "default_value", PROP_POINTER, PROP_NONE); - RNA_def_property_pointer_sdna(prop, nullptr, "value"); - RNA_def_property_struct_type(prop, "Object"); - RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); - RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT); - RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); -} - -static void rna_def_node_socket_image(BlenderRNA *brna, - const char *identifier, - const char *interface_idname) -{ - StructRNA *srna; - PropertyRNA *prop; - - srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); - RNA_def_struct_ui_text(srna, "Image Node Socket", "Image socket of a node"); - RNA_def_struct_sdna(srna, "bNodeSocket"); - - RNA_def_struct_sdna_from(srna, "bNodeSocketValueImage", "default_value"); - - prop = RNA_def_property(srna, "default_value", PROP_POINTER, PROP_NONE); - RNA_def_property_pointer_sdna(prop, nullptr, "value"); - RNA_def_property_struct_type(prop, "Image"); - RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update( - prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_and_relation_update"); - RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT | PROP_CONTEXT_UPDATE); - RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); - - /* socket interface */ - srna = RNA_def_struct(brna, interface_idname, "NodeSocketInterfaceStandard"); - RNA_def_struct_ui_text(srna, "Image Node Socket Interface", "Image socket of a node"); - RNA_def_struct_sdna(srna, "bNodeSocket"); - - RNA_def_struct_sdna_from(srna, "bNodeSocketValueImage", "default_value"); - - prop = RNA_def_property(srna, "default_value", PROP_POINTER, PROP_NONE); - RNA_def_property_pointer_sdna(prop, nullptr, "value"); - RNA_def_property_struct_type(prop, "Image"); - RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); - RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT); - RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); -} - -static void rna_def_node_socket_geometry(BlenderRNA *brna, - const char *identifier, - const char *interface_idname) -{ - StructRNA *srna; - - srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); - RNA_def_struct_ui_text(srna, "Geometry Node Socket", "Geometry socket of a node"); - RNA_def_struct_sdna(srna, "bNodeSocket"); - - srna = RNA_def_struct(brna, interface_idname, "NodeSocketInterfaceStandard"); - RNA_def_struct_ui_text(srna, "Geometry Node Socket Interface", "Geometry socket of a node"); - RNA_def_struct_sdna(srna, "bNodeSocket"); -} - -static void rna_def_node_socket_collection(BlenderRNA *brna, - const char *identifier, - const char *interface_idname) -{ - StructRNA *srna; - PropertyRNA *prop; - - srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); - RNA_def_struct_ui_text(srna, "Collection Node Socket", "Collection socket of a node"); - RNA_def_struct_sdna(srna, "bNodeSocket"); - - RNA_def_struct_sdna_from(srna, "bNodeSocketValueCollection", "default_value"); - - prop = RNA_def_property(srna, "default_value", PROP_POINTER, PROP_NONE); - RNA_def_property_pointer_sdna(prop, nullptr, "value"); - RNA_def_property_struct_type(prop, "Collection"); - RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update( - prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_and_relation_update"); - RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT | PROP_CONTEXT_UPDATE); - RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); - - /* socket interface */ - srna = RNA_def_struct(brna, interface_idname, "NodeSocketInterfaceStandard"); - RNA_def_struct_ui_text(srna, "Collection Node Socket Interface", "Collection socket of a node"); - RNA_def_struct_sdna(srna, "bNodeSocket"); - - RNA_def_struct_sdna_from(srna, "bNodeSocketValueCollection", "default_value"); - - prop = RNA_def_property(srna, "default_value", PROP_POINTER, PROP_NONE); - RNA_def_property_pointer_sdna(prop, nullptr, "value"); - RNA_def_property_struct_type(prop, "Collection"); - RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); - RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT); - RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); -} - -static void rna_def_node_socket_texture(BlenderRNA *brna, - const char *identifier, - const char *interface_idname) -{ - StructRNA *srna; - PropertyRNA *prop; - - srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); - RNA_def_struct_ui_text(srna, "Texture Node Socket", "Texture socket of a node"); - RNA_def_struct_sdna(srna, "bNodeSocket"); - - RNA_def_struct_sdna_from(srna, "bNodeSocketValueTexture", "default_value"); - - prop = RNA_def_property(srna, "default_value", PROP_POINTER, PROP_NONE); - RNA_def_property_pointer_sdna(prop, nullptr, "value"); - RNA_def_property_struct_type(prop, "Texture"); - RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update( - prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_and_relation_update"); - RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT | PROP_CONTEXT_UPDATE); - RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); - - /* socket interface */ - srna = RNA_def_struct(brna, interface_idname, "NodeSocketInterfaceStandard"); - RNA_def_struct_ui_text(srna, "Texture Node Socket Interface", "Texture socket of a node"); - RNA_def_struct_sdna(srna, "bNodeSocket"); - - RNA_def_struct_sdna_from(srna, "bNodeSocketValueTexture", "default_value"); - - prop = RNA_def_property(srna, "default_value", PROP_POINTER, PROP_NONE); - RNA_def_property_pointer_sdna(prop, nullptr, "value"); - RNA_def_property_struct_type(prop, "Texture"); - RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); - RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT); - RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); -} - -static void rna_def_node_socket_material(BlenderRNA *brna, - const char *identifier, - const char *interface_idname) -{ - StructRNA *srna; - PropertyRNA *prop; - - srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); - RNA_def_struct_ui_text(srna, "Material Node Socket", "Material socket of a node"); - RNA_def_struct_sdna(srna, "bNodeSocket"); - - RNA_def_struct_sdna_from(srna, "bNodeSocketValueMaterial", "default_value"); - - prop = RNA_def_property(srna, "default_value", PROP_POINTER, PROP_NONE); - RNA_def_property_pointer_sdna(prop, nullptr, "value"); - RNA_def_property_struct_type(prop, "Material"); - RNA_def_property_pointer_funcs( - prop, nullptr, nullptr, nullptr, "rna_NodeSocketMaterial_default_value_poll"); - RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update( - prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_and_relation_update"); - RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT | PROP_CONTEXT_UPDATE); - RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); - - /* socket interface */ - srna = RNA_def_struct(brna, interface_idname, "NodeSocketInterfaceStandard"); - RNA_def_struct_ui_text(srna, "Material Node Socket Interface", "Material socket of a node"); - RNA_def_struct_sdna(srna, "bNodeSocket"); - - RNA_def_struct_sdna_from(srna, "bNodeSocketValueMaterial", "default_value"); - - prop = RNA_def_property(srna, "default_value", PROP_POINTER, PROP_NONE); - RNA_def_property_pointer_sdna(prop, nullptr, "value"); - RNA_def_property_struct_type(prop, "Material"); - RNA_def_property_pointer_funcs( - prop, nullptr, nullptr, nullptr, "rna_NodeSocketMaterial_default_value_poll"); - RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); - RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT); - RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); -} - /* Common functions for all builtin socket interface types. */ static void rna_def_node_tree_interface_socket_builtin(StructRNA *srna) { @@ -1649,6 +690,29 @@ static void rna_def_node_tree_interface_socket_builtin(StructRNA *srna) RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); } +static void rna_def_node_socket_float(BlenderRNA *brna, + const char *identifier, + PropertySubType subtype) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); + RNA_def_struct_ui_text(srna, "Float Node Socket", "Floating-point number socket of a node"); + RNA_def_struct_sdna(srna, "bNodeSocket"); + + RNA_def_struct_sdna_from(srna, "bNodeSocketValueFloat", "default_value"); + + prop = RNA_def_property(srna, "default_value", PROP_FLOAT, subtype); + RNA_def_property_float_sdna(prop, nullptr, "value"); + RNA_def_property_float_funcs(prop, nullptr, nullptr, "rna_NodeSocketStandard_float_range"); + RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_update"); + RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); + + RNA_def_struct_sdna_from(srna, "bNodeSocket", nullptr); +} + static void rna_def_node_socket_interface_float(BlenderRNA *brna, const char *identifier, PropertySubType subtype) @@ -1677,6 +741,15 @@ static void rna_def_node_socket_interface_float(BlenderRNA *brna, RNA_def_struct_sdna_from(srna, "bNodeSocketValueFloat", "socket_data"); + prop = RNA_def_property(srna, "subtype", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_enum_dummy_DEFAULT_items); + RNA_def_property_enum_sdna(prop, nullptr, "subtype"); + RNA_def_property_enum_funcs( + prop, nullptr, nullptr, "rna_NodeTreeInterfaceSocketFloat_subtype_itemf"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_text(prop, "Subtype", "Subtype of the default value"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceSocket_value_update"); + prop = RNA_def_property(srna, "default_value", PROP_FLOAT, subtype); RNA_def_property_float_sdna(prop, nullptr, "value"); RNA_def_property_float_default(prop, value_default); @@ -1703,6 +776,44 @@ static void rna_def_node_socket_interface_float(BlenderRNA *brna, rna_def_node_tree_interface_socket_builtin(srna); } +static void rna_def_node_socket_int(BlenderRNA *brna, + const char *identifier, + PropertySubType subtype) +{ + StructRNA *srna; + PropertyRNA *prop; + int value_default; + + /* choose sensible common default based on subtype */ + switch (subtype) { + case PROP_FACTOR: + value_default = 1; + break; + case PROP_PERCENTAGE: + value_default = 100; + break; + default: + value_default = 0; + break; + } + + srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); + RNA_def_struct_ui_text(srna, "Integer Node Socket", "Integer number socket of a node"); + RNA_def_struct_sdna(srna, "bNodeSocket"); + + RNA_def_struct_sdna_from(srna, "bNodeSocketValueInt", "default_value"); + + prop = RNA_def_property(srna, "default_value", PROP_INT, subtype); + RNA_def_property_int_sdna(prop, nullptr, "value"); + RNA_def_property_int_default(prop, value_default); + RNA_def_property_int_funcs(prop, nullptr, nullptr, "rna_NodeSocketStandard_int_range"); + RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_update"); + RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); + + RNA_def_struct_sdna_from(srna, "bNodeSocket", nullptr); +} + static void rna_def_node_socket_interface_int(BlenderRNA *brna, const char *identifier, PropertySubType subtype) @@ -1716,6 +827,15 @@ static void rna_def_node_socket_interface_int(BlenderRNA *brna, RNA_def_struct_sdna_from(srna, "bNodeSocketValueInt", "socket_data"); + prop = RNA_def_property(srna, "subtype", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_enum_dummy_DEFAULT_items); + RNA_def_property_enum_sdna(prop, nullptr, "subtype"); + RNA_def_property_enum_funcs( + prop, nullptr, nullptr, "rna_NodeTreeInterfaceSocketInt_subtype_itemf"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_text(prop, "Subtype", "Subtype of the default value"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceSocket_value_update"); + prop = RNA_def_property(srna, "default_value", PROP_INT, subtype); RNA_def_property_int_sdna(prop, nullptr, "value"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); @@ -1741,6 +861,26 @@ static void rna_def_node_socket_interface_int(BlenderRNA *brna, rna_def_node_tree_interface_socket_builtin(srna); } +static void rna_def_node_socket_bool(BlenderRNA *brna, const char *identifier) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); + RNA_def_struct_ui_text(srna, "Boolean Node Socket", "Boolean value socket of a node"); + RNA_def_struct_sdna(srna, "bNodeSocket"); + + RNA_def_struct_sdna_from(srna, "bNodeSocketValueBoolean", "default_value"); + + prop = RNA_def_property(srna, "default_value", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "value", 1); + RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_update"); + RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); + + RNA_def_struct_sdna_from(srna, "bNodeSocket", nullptr); +} + static void rna_def_node_socket_interface_bool(BlenderRNA *brna, const char *identifier) { StructRNA *srna; @@ -1763,6 +903,27 @@ static void rna_def_node_socket_interface_bool(BlenderRNA *brna, const char *ide rna_def_node_tree_interface_socket_builtin(srna); } +static void rna_def_node_socket_rotation(BlenderRNA *brna, const char *identifier) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); + RNA_def_struct_ui_text(srna, "Rotation Node Socket", "Rotation value socket of a node"); + RNA_def_struct_sdna(srna, "bNodeSocket"); + + RNA_def_struct_sdna_from(srna, "bNodeSocketValueRotation", "default_value"); + + prop = RNA_def_property(srna, "default_value", PROP_FLOAT, PROP_EULER); + RNA_def_property_float_sdna(prop, nullptr, "value_euler"); + // RNA_def_property_array(prop, 3); + RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_update"); + RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); + + RNA_def_struct_sdna_from(srna, "bNodeSocket", nullptr); +} + static void rna_def_node_socket_interface_rotation(BlenderRNA *brna, const char *identifier) { StructRNA *srna; @@ -1786,6 +947,45 @@ static void rna_def_node_socket_interface_rotation(BlenderRNA *brna, const char rna_def_node_tree_interface_socket_builtin(srna); } +static void rna_def_node_socket_vector(BlenderRNA *brna, + const char *identifier, + PropertySubType subtype) +{ + StructRNA *srna; + PropertyRNA *prop; + const float *value_default; + + /* choose sensible common default based on subtype */ + switch (subtype) { + case PROP_DIRECTION: { + static const float default_direction[3] = {0.0f, 0.0f, 1.0f}; + value_default = default_direction; + break; + } + default: { + static const float default_vector[3] = {0.0f, 0.0f, 0.0f}; + value_default = default_vector; + break; + } + } + + srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); + RNA_def_struct_ui_text(srna, "Vector Node Socket", "3D vector socket of a node"); + RNA_def_struct_sdna(srna, "bNodeSocket"); + + RNA_def_struct_sdna_from(srna, "bNodeSocketValueVector", "default_value"); + + prop = RNA_def_property(srna, "default_value", PROP_FLOAT, subtype); + RNA_def_property_float_sdna(prop, nullptr, "value"); + RNA_def_property_float_array_default(prop, value_default); + RNA_def_property_float_funcs(prop, nullptr, nullptr, "rna_NodeSocketStandard_vector_range"); + RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_update"); + RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); + + RNA_def_struct_sdna_from(srna, "bNodeSocket", nullptr); +} + static void rna_def_node_socket_interface_vector(BlenderRNA *brna, const char *identifier, PropertySubType subtype) @@ -1799,6 +999,15 @@ static void rna_def_node_socket_interface_vector(BlenderRNA *brna, RNA_def_struct_sdna_from(srna, "bNodeSocketValueVector", "socket_data"); + prop = RNA_def_property(srna, "subtype", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_enum_dummy_DEFAULT_items); + RNA_def_property_enum_sdna(prop, nullptr, "subtype"); + RNA_def_property_enum_funcs( + prop, nullptr, nullptr, "rna_NodeTreeInterfaceSocketVector_subtype_itemf"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_text(prop, "Subtype", "Subtype of the default value"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceSocket_value_update"); + prop = RNA_def_property(srna, "default_value", PROP_FLOAT, subtype); RNA_def_property_float_sdna(prop, nullptr, "value"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); @@ -1824,6 +1033,26 @@ static void rna_def_node_socket_interface_vector(BlenderRNA *brna, rna_def_node_tree_interface_socket_builtin(srna); } +static void rna_def_node_socket_color(BlenderRNA *brna, const char *identifier) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); + RNA_def_struct_ui_text(srna, "Color Node Socket", "RGBA color socket of a node"); + RNA_def_struct_sdna(srna, "bNodeSocket"); + + RNA_def_struct_sdna_from(srna, "bNodeSocketValueRGBA", "default_value"); + + prop = RNA_def_property(srna, "default_value", PROP_FLOAT, PROP_COLOR); + RNA_def_property_float_sdna(prop, nullptr, "value"); + RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_update"); + RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); + + RNA_def_struct_sdna_from(srna, "bNodeSocket", nullptr); +} + static void rna_def_node_socket_interface_color(BlenderRNA *brna, const char *identifier) { StructRNA *srna; @@ -1846,6 +1075,26 @@ static void rna_def_node_socket_interface_color(BlenderRNA *brna, const char *id rna_def_node_tree_interface_socket_builtin(srna); } +static void rna_def_node_socket_string(BlenderRNA *brna, const char *identifier) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); + RNA_def_struct_ui_text(srna, "String Node Socket", "String socket of a node"); + RNA_def_struct_sdna(srna, "bNodeSocket"); + + RNA_def_struct_sdna_from(srna, "bNodeSocketValueString", "default_value"); + + prop = RNA_def_property(srna, "default_value", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, nullptr, "value"); + RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_update"); + RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); + + RNA_def_struct_sdna_from(srna, "bNodeSocket", nullptr); +} + static void rna_def_node_socket_interface_string(BlenderRNA *brna, const char *identifier) { StructRNA *srna; @@ -1868,6 +1117,15 @@ static void rna_def_node_socket_interface_string(BlenderRNA *brna, const char *i rna_def_node_tree_interface_socket_builtin(srna); } +static void rna_def_node_socket_shader(BlenderRNA *brna, const char *identifier) +{ + StructRNA *srna; + + srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); + RNA_def_struct_ui_text(srna, "Shader Node Socket", "Shader socket of a node"); + RNA_def_struct_sdna(srna, "bNodeSocket"); +} + static void rna_def_node_socket_interface_shader(BlenderRNA *brna, const char *identifier) { StructRNA *srna; @@ -1879,6 +1137,27 @@ static void rna_def_node_socket_interface_shader(BlenderRNA *brna, const char *i rna_def_node_tree_interface_socket_builtin(srna); } +static void rna_def_node_socket_object(BlenderRNA *brna, const char *identifier) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); + RNA_def_struct_ui_text(srna, "Object Node Socket", "Object socket of a node"); + RNA_def_struct_sdna(srna, "bNodeSocket"); + + RNA_def_struct_sdna_from(srna, "bNodeSocketValueObject", "default_value"); + + prop = RNA_def_property(srna, "default_value", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, nullptr, "value"); + RNA_def_property_struct_type(prop, "Object"); + RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); + RNA_def_property_update( + prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_and_relation_update"); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT | PROP_CONTEXT_UPDATE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); +} + static void rna_def_node_socket_interface_object(BlenderRNA *brna, const char *identifier) { StructRNA *srna; @@ -1901,6 +1180,27 @@ static void rna_def_node_socket_interface_object(BlenderRNA *brna, const char *i rna_def_node_tree_interface_socket_builtin(srna); } +static void rna_def_node_socket_image(BlenderRNA *brna, const char *identifier) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); + RNA_def_struct_ui_text(srna, "Image Node Socket", "Image socket of a node"); + RNA_def_struct_sdna(srna, "bNodeSocket"); + + RNA_def_struct_sdna_from(srna, "bNodeSocketValueImage", "default_value"); + + prop = RNA_def_property(srna, "default_value", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, nullptr, "value"); + RNA_def_property_struct_type(prop, "Image"); + RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); + RNA_def_property_update( + prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_and_relation_update"); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT | PROP_CONTEXT_UPDATE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); +} + static void rna_def_node_socket_interface_image(BlenderRNA *brna, const char *identifier) { StructRNA *srna; @@ -1923,6 +1223,15 @@ static void rna_def_node_socket_interface_image(BlenderRNA *brna, const char *id rna_def_node_tree_interface_socket_builtin(srna); } +static void rna_def_node_socket_geometry(BlenderRNA *brna, const char *identifier) +{ + StructRNA *srna; + + srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); + RNA_def_struct_ui_text(srna, "Geometry Node Socket", "Geometry socket of a node"); + RNA_def_struct_sdna(srna, "bNodeSocket"); +} + static void rna_def_node_socket_interface_geometry(BlenderRNA *brna, const char *identifier) { StructRNA *srna; @@ -1934,6 +1243,27 @@ static void rna_def_node_socket_interface_geometry(BlenderRNA *brna, const char rna_def_node_tree_interface_socket_builtin(srna); } +static void rna_def_node_socket_collection(BlenderRNA *brna, const char *identifier) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); + RNA_def_struct_ui_text(srna, "Collection Node Socket", "Collection socket of a node"); + RNA_def_struct_sdna(srna, "bNodeSocket"); + + RNA_def_struct_sdna_from(srna, "bNodeSocketValueCollection", "default_value"); + + prop = RNA_def_property(srna, "default_value", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, nullptr, "value"); + RNA_def_property_struct_type(prop, "Collection"); + RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); + RNA_def_property_update( + prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_and_relation_update"); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT | PROP_CONTEXT_UPDATE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); +} + static void rna_def_node_socket_interface_collection(BlenderRNA *brna, const char *identifier) { StructRNA *srna; @@ -1956,6 +1286,27 @@ static void rna_def_node_socket_interface_collection(BlenderRNA *brna, const cha rna_def_node_tree_interface_socket_builtin(srna); } +static void rna_def_node_socket_texture(BlenderRNA *brna, const char *identifier) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); + RNA_def_struct_ui_text(srna, "Texture Node Socket", "Texture socket of a node"); + RNA_def_struct_sdna(srna, "bNodeSocket"); + + RNA_def_struct_sdna_from(srna, "bNodeSocketValueTexture", "default_value"); + + prop = RNA_def_property(srna, "default_value", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, nullptr, "value"); + RNA_def_property_struct_type(prop, "Texture"); + RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); + RNA_def_property_update( + prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_and_relation_update"); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT | PROP_CONTEXT_UPDATE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); +} + static void rna_def_node_socket_interface_texture(BlenderRNA *brna, const char *identifier) { StructRNA *srna; @@ -1978,6 +1329,29 @@ static void rna_def_node_socket_interface_texture(BlenderRNA *brna, const char * rna_def_node_tree_interface_socket_builtin(srna); } +static void rna_def_node_socket_material(BlenderRNA *brna, const char *identifier) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); + RNA_def_struct_ui_text(srna, "Material Node Socket", "Material socket of a node"); + RNA_def_struct_sdna(srna, "bNodeSocket"); + + RNA_def_struct_sdna_from(srna, "bNodeSocketValueMaterial", "default_value"); + + prop = RNA_def_property(srna, "default_value", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, nullptr, "value"); + RNA_def_property_struct_type(prop, "Material"); + RNA_def_property_pointer_funcs( + prop, nullptr, nullptr, nullptr, "rna_NodeSocketMaterial_default_value_poll"); + RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); + RNA_def_property_update( + prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_and_relation_update"); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT | PROP_CONTEXT_UPDATE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); +} + static void rna_def_node_socket_interface_material(BlenderRNA *brna, const char *identifier) { StructRNA *srna; @@ -2002,10 +1376,18 @@ static void rna_def_node_socket_interface_material(BlenderRNA *brna, const char rna_def_node_tree_interface_socket_builtin(srna); } +static void rna_def_node_socket_virtual(BlenderRNA *brna, const char *identifier) +{ + StructRNA *srna; + + srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); + RNA_def_struct_ui_text(srna, "Virtual Node Socket", "Virtual socket of a node"); + RNA_def_struct_sdna(srna, "bNodeSocket"); +} + /* Info for generating static subtypes. */ struct bNodeSocketStaticTypeInfo { const char *socket_identifier; - const char *interface_identifier_legacy; const char *interface_identifier; eNodeSocketDatatype type; PropertySubType subtype; @@ -2016,202 +1398,103 @@ struct bNodeSocketStaticTypeInfo { * but makesrna cannot have a dependency on BKE, so this list would have to live in RNA itself, * with BKE etc. accessing the RNA API to get the subtypes info. */ static const bNodeSocketStaticTypeInfo node_socket_subtypes[] = { - {"NodeSocketFloat", - "NodeSocketInterfaceFloat", - "NodeTreeInterfaceSocketFloat", - SOCK_FLOAT, - PROP_NONE}, - {"NodeSocketFloatUnsigned", - "NodeSocketInterfaceFloatUnsigned", - "NodeTreeInterfaceSocketFloatUnsigned", - SOCK_FLOAT, - PROP_UNSIGNED}, + {"NodeSocketFloat", "NodeTreeInterfaceSocketFloat", SOCK_FLOAT, PROP_NONE}, + {"NodeSocketFloatUnsigned", "NodeTreeInterfaceSocketFloatUnsigned", SOCK_FLOAT, PROP_UNSIGNED}, {"NodeSocketFloatPercentage", - "NodeSocketInterfaceFloatPercentage", "NodeTreeInterfaceSocketFloatPercentage", SOCK_FLOAT, PROP_PERCENTAGE}, - {"NodeSocketFloatFactor", - "NodeSocketInterfaceFloatFactor", - "NodeTreeInterfaceSocketFloatFactor", - SOCK_FLOAT, - PROP_FACTOR}, - {"NodeSocketFloatAngle", - "NodeSocketInterfaceFloatAngle", - "NodeTreeInterfaceSocketFloatAngle", - SOCK_FLOAT, - PROP_ANGLE}, - {"NodeSocketFloatTime", - "NodeSocketInterfaceFloatTime", - "NodeTreeInterfaceSocketFloatTime", - SOCK_FLOAT, - PROP_TIME}, + {"NodeSocketFloatFactor", "NodeTreeInterfaceSocketFloatFactor", SOCK_FLOAT, PROP_FACTOR}, + {"NodeSocketFloatAngle", "NodeTreeInterfaceSocketFloatAngle", SOCK_FLOAT, PROP_ANGLE}, + {"NodeSocketFloatTime", "NodeTreeInterfaceSocketFloatTime", SOCK_FLOAT, PROP_TIME}, {"NodeSocketFloatTimeAbsolute", - "NodeSocketInterfaceFloatTimeAbsolute", "NodeTreeInterfaceSocketFloatTimeAbsolute", SOCK_FLOAT, PROP_TIME_ABSOLUTE}, - {"NodeSocketFloatDistance", - "NodeSocketInterfaceFloatDistance", - "NodeTreeInterfaceSocketFloatDistance", - SOCK_FLOAT, - PROP_DISTANCE}, - {"NodeSocketInt", "NodeSocketInterfaceInt", "NodeTreeInterfaceSocketInt", SOCK_INT, PROP_NONE}, - {"NodeSocketIntUnsigned", - "NodeSocketInterfaceIntUnsigned", - "NodeTreeInterfaceSocketIntUnsigned", - SOCK_INT, - PROP_UNSIGNED}, - {"NodeSocketIntPercentage", - "NodeSocketInterfaceIntPercentage", - "NodeTreeInterfaceSocketIntPercentage", - SOCK_INT, - PROP_PERCENTAGE}, - {"NodeSocketIntFactor", - "NodeSocketInterfaceIntFactor", - "NodeTreeInterfaceSocketIntFactor", - SOCK_INT, - PROP_FACTOR}, - {"NodeSocketBool", - "NodeSocketInterfaceBool", - "NodeTreeInterfaceSocketBool", - SOCK_BOOLEAN, - PROP_NONE}, - {"NodeSocketRotation", - "NodeSocketInterfaceRotation", - "NodeTreeInterfaceSocketRotation", - SOCK_ROTATION, - PROP_NONE}, - {"NodeSocketVector", - "NodeSocketInterfaceVector", - "NodeTreeInterfaceSocketVector", - SOCK_VECTOR, - PROP_NONE}, + {"NodeSocketFloatDistance", "NodeTreeInterfaceSocketFloatDistance", SOCK_FLOAT, PROP_DISTANCE}, + {"NodeSocketInt", "NodeTreeInterfaceSocketInt", SOCK_INT, PROP_NONE}, + {"NodeSocketIntUnsigned", "NodeTreeInterfaceSocketIntUnsigned", SOCK_INT, PROP_UNSIGNED}, + {"NodeSocketIntPercentage", "NodeTreeInterfaceSocketIntPercentage", SOCK_INT, PROP_PERCENTAGE}, + {"NodeSocketIntFactor", "NodeTreeInterfaceSocketIntFactor", SOCK_INT, PROP_FACTOR}, + {"NodeSocketBool", "NodeTreeInterfaceSocketBool", SOCK_BOOLEAN, PROP_NONE}, + {"NodeSocketRotation", "NodeTreeInterfaceSocketRotation", SOCK_ROTATION, PROP_NONE}, + {"NodeSocketVector", "NodeTreeInterfaceSocketVector", SOCK_VECTOR, PROP_NONE}, {"NodeSocketVectorTranslation", - "NodeSocketInterfaceVectorTranslation", "NodeTreeInterfaceSocketVectorTranslation", SOCK_VECTOR, PROP_TRANSLATION}, {"NodeSocketVectorDirection", - "NodeSocketInterfaceVectorDirection", "NodeTreeInterfaceSocketVectorDirection", SOCK_VECTOR, PROP_DIRECTION}, {"NodeSocketVectorVelocity", - "NodeSocketInterfaceVectorVelocity", "NodeTreeInterfaceSocketVectorVelocity", SOCK_VECTOR, PROP_VELOCITY}, {"NodeSocketVectorAcceleration", - "NodeSocketInterfaceVectorAcceleration", "NodeTreeInterfaceSocketVectorAcceleration", SOCK_VECTOR, PROP_ACCELERATION}, - {"NodeSocketVectorEuler", - "NodeSocketInterfaceVectorEuler", - "NodeTreeInterfaceSocketVectorEuler", - SOCK_VECTOR, - PROP_EULER}, - {"NodeSocketVectorXYZ", - "NodeSocketInterfaceVectorXYZ", - "NodeTreeInterfaceSocketVectorXYZ", - SOCK_VECTOR, - PROP_XYZ}, - {"NodeSocketColor", - "NodeSocketInterfaceColor", - "NodeTreeInterfaceSocketColor", - SOCK_RGBA, - PROP_NONE}, - {"NodeSocketString", - "NodeSocketInterfaceString", - "NodeTreeInterfaceSocketString", - SOCK_STRING, - PROP_NONE}, - {"NodeSocketShader", - "NodeSocketInterfaceShader", - "NodeTreeInterfaceSocketShader", - SOCK_SHADER, - PROP_NONE}, - {"NodeSocketObject", - "NodeSocketInterfaceObject", - "NodeTreeInterfaceSocketObject", - SOCK_OBJECT, - PROP_NONE}, - {"NodeSocketImage", - "NodeSocketInterfaceImage", - "NodeTreeInterfaceSocketImage", - SOCK_IMAGE, - PROP_NONE}, - {"NodeSocketGeometry", - "NodeSocketInterfaceGeometry", - "NodeTreeInterfaceSocketGeometry", - SOCK_GEOMETRY, - PROP_NONE}, - {"NodeSocketCollection", - "NodeSocketInterfaceCollection", - "NodeTreeInterfaceSocketCollection", - SOCK_COLLECTION, - PROP_NONE}, - {"NodeSocketTexture", - "NodeSocketInterfaceTexture", - "NodeTreeInterfaceSocketTexture", - SOCK_TEXTURE, - PROP_NONE}, - {"NodeSocketMaterial", - "NodeSocketInterfaceMaterial", - "NodeTreeInterfaceSocketMaterial", - SOCK_MATERIAL, - PROP_NONE}, + {"NodeSocketVectorEuler", "NodeTreeInterfaceSocketVectorEuler", SOCK_VECTOR, PROP_EULER}, + {"NodeSocketVectorXYZ", "NodeTreeInterfaceSocketVectorXYZ", SOCK_VECTOR, PROP_XYZ}, + {"NodeSocketColor", "NodeTreeInterfaceSocketColor", SOCK_RGBA, PROP_NONE}, + {"NodeSocketString", "NodeTreeInterfaceSocketString", SOCK_STRING, PROP_NONE}, + {"NodeSocketShader", "NodeTreeInterfaceSocketShader", SOCK_SHADER, PROP_NONE}, + {"NodeSocketObject", "NodeTreeInterfaceSocketObject", SOCK_OBJECT, PROP_NONE}, + {"NodeSocketImage", "NodeTreeInterfaceSocketImage", SOCK_IMAGE, PROP_NONE}, + {"NodeSocketGeometry", "NodeTreeInterfaceSocketGeometry", SOCK_GEOMETRY, PROP_NONE}, + {"NodeSocketCollection", "NodeTreeInterfaceSocketCollection", SOCK_COLLECTION, PROP_NONE}, + {"NodeSocketTexture", "NodeTreeInterfaceSocketTexture", SOCK_TEXTURE, PROP_NONE}, + {"NodeSocketMaterial", "NodeTreeInterfaceSocketMaterial", SOCK_MATERIAL, PROP_NONE}, }; static void rna_def_node_socket_subtypes(BlenderRNA *brna) { for (const bNodeSocketStaticTypeInfo &info : node_socket_subtypes) { const char *identifier = info.socket_identifier; - const char *interface_identifier = info.interface_identifier_legacy; switch (info.type) { case SOCK_FLOAT: - rna_def_node_socket_float(brna, identifier, interface_identifier, info.subtype); + rna_def_node_socket_float(brna, identifier, info.subtype); break; case SOCK_INT: - rna_def_node_socket_int(brna, identifier, interface_identifier, info.subtype); + rna_def_node_socket_int(brna, identifier, info.subtype); break; case SOCK_BOOLEAN: - rna_def_node_socket_bool(brna, identifier, interface_identifier); + rna_def_node_socket_bool(brna, identifier); break; case SOCK_ROTATION: - rna_def_node_socket_rotation(brna, identifier, interface_identifier); + rna_def_node_socket_rotation(brna, identifier); break; case SOCK_VECTOR: - rna_def_node_socket_vector(brna, identifier, interface_identifier, info.subtype); + rna_def_node_socket_vector(brna, identifier, info.subtype); break; case SOCK_RGBA: - rna_def_node_socket_color(brna, identifier, interface_identifier); + rna_def_node_socket_color(brna, identifier); break; case SOCK_STRING: - rna_def_node_socket_string(brna, identifier, interface_identifier); + rna_def_node_socket_string(brna, identifier); break; case SOCK_SHADER: - rna_def_node_socket_shader(brna, identifier, interface_identifier); + rna_def_node_socket_shader(brna, identifier); break; case SOCK_OBJECT: - rna_def_node_socket_object(brna, identifier, interface_identifier); + rna_def_node_socket_object(brna, identifier); break; case SOCK_IMAGE: - rna_def_node_socket_image(brna, identifier, interface_identifier); + rna_def_node_socket_image(brna, identifier); break; case SOCK_GEOMETRY: - rna_def_node_socket_geometry(brna, identifier, interface_identifier); + rna_def_node_socket_geometry(brna, identifier); break; case SOCK_COLLECTION: - rna_def_node_socket_collection(brna, identifier, interface_identifier); + rna_def_node_socket_collection(brna, identifier); break; case SOCK_TEXTURE: - rna_def_node_socket_texture(brna, identifier, interface_identifier); + rna_def_node_socket_texture(brna, identifier); break; case SOCK_MATERIAL: - rna_def_node_socket_material(brna, identifier, interface_identifier); + rna_def_node_socket_material(brna, identifier); break; case SOCK_CUSTOM: @@ -2283,9 +1566,8 @@ void rna_def_node_socket_interface_subtypes(BlenderRNA *brna) void RNA_def_node_socket_subtypes(BlenderRNA *brna) { rna_def_node_socket(brna); - rna_def_node_socket_interface(brna); - rna_def_node_socket_standard(brna); + rna_def_node_socket_standard(brna); rna_def_node_socket_subtypes(brna); } diff --git a/source/blender/makesrna/intern/rna_node_tree_interface.cc b/source/blender/makesrna/intern/rna_node_tree_interface.cc index 5c31cc1f1a3..f62f8f3153c 100644 --- a/source/blender/makesrna/intern/rna_node_tree_interface.cc +++ b/source/blender/makesrna/intern/rna_node_tree_interface.cc @@ -21,12 +21,23 @@ const EnumPropertyItem rna_enum_node_tree_interface_item_type_items[] = { {NODE_INTERFACE_PANEL, "PANEL", 0, "Panel", ""}, {0, nullptr, 0, nullptr, nullptr}}; +static const EnumPropertyItem node_tree_interface_socket_in_out_items[] = { + {NODE_INTERFACE_SOCKET_INPUT, "INPUT", 0, "Input", "Generate a input node socket"}, + {NODE_INTERFACE_SOCKET_OUTPUT, "OUTPUT", 0, "Output", "Generate a output node socket"}, + {NODE_INTERFACE_SOCKET_INPUT | NODE_INTERFACE_SOCKET_OUTPUT, + "BOTH", + 0, + "Both", + "Generate both input and output node socket"}, + {0, nullptr, 0, nullptr, nullptr}}; + #ifdef RNA_RUNTIME # include "BKE_node.h" # include "BKE_node_runtime.hh" # include "BKE_node_tree_interface.hh" # include "BKE_node_tree_update.h" +# include "BLI_set.hh" # include "DNA_material_types.h" # include "ED_node.hh" # include "WM_api.hh" @@ -75,17 +86,38 @@ static char *rna_NodeTreeInterfaceItem_path(const PointerRNA *ptr) } ntree->ensure_topology_cache(); - UNUSED_VARS(item); - // Note: New API, will be enabled after new interface cache is added. - // const blender::bke::bNodeTreeInterfaceCache &cache = ntree->interface_cache(); - // for (const int index : cache.items.index_range()) { - // if (cache.items[index] == item) { - // return BLI_sprintfN("interface.ui_items[%d]", index); - // } - // } + for (const int index : ntree->interface_items().index_range()) { + if (ntree->interface_items()[index] == item) { + return BLI_sprintfN("interface.ui_items[%d]", index); + } + } return nullptr; } +static PointerRNA rna_NodeTreeInterfaceItem_parent_get(PointerRNA *ptr) +{ + bNodeTree *ntree = reinterpret_cast(ptr->owner_id); + const bNodeTreeInterfaceItem *item = static_cast(ptr->data); + bNodeTreeInterfacePanel *parent = ntree->tree_interface.find_item_parent(*item); + PointerRNA result; + RNA_pointer_create(&ntree->id, &RNA_NodeTreeInterfacePanel, parent, &result); + return result; +} + +static int rna_NodeTreeInterfaceItem_position_get(PointerRNA *ptr) +{ + bNodeTree *ntree = reinterpret_cast(ptr->owner_id); + const bNodeTreeInterfaceItem *item = static_cast(ptr->data); + return ntree->tree_interface.find_item_position(*item); +} + +static int rna_NodeTreeInterfaceItem_index_get(PointerRNA *ptr) +{ + bNodeTree *ntree = reinterpret_cast(ptr->owner_id); + const bNodeTreeInterfaceItem *item = static_cast(ptr->data); + return ntree->tree_interface.find_item_index(*item); +} + static bool rna_NodeTreeInterfaceSocket_unregister(Main * /*bmain*/, StructRNA *type) { bNodeSocketType *st = static_cast(RNA_struct_blender_type_get(type)); @@ -109,15 +141,14 @@ static void rna_NodeTreeInterfaceSocket_draw_builtin(ID *id, { bNodeSocketType *typeinfo = interface_socket->socket_typeinfo(); if (typeinfo && typeinfo->interface_draw) { - UNUSED_VARS(id, C, layout); - // Note: New API, will be enabled after typeinfo callbacks change. - // typeinfo->interface_draw(id, interface_socket, C, layout); + typeinfo->interface_draw(id, interface_socket, C, layout); } } -// Note: New API, interface draw callback used after changing callbacks. -static void UNUSED_FUNCTION(rna_NodeTreeInterfaceSocket_draw_custom)( - ID *id, bNodeTreeInterfaceSocket *interface_socket, bContext *C, uiLayout *layout) +static void rna_NodeTreeInterfaceSocket_draw_custom(ID *id, + bNodeTreeInterfaceSocket *interface_socket, + bContext *C, + uiLayout *layout) { bNodeSocketType *typeinfo = nodeSocketTypeFind(interface_socket->socket_type); if (typeinfo == nullptr) { @@ -147,14 +178,11 @@ static void rna_NodeTreeInterfaceSocket_init_socket_builtin( { bNodeSocketType *typeinfo = interface_socket->socket_typeinfo(); if (typeinfo && typeinfo->interface_draw) { - // Note: New API, callback signatures change. - UNUSED_VARS(id, node, socket, data_path); - // typeinfo->interface_init_socket(id, interface_socket, node, socket, data_path); + typeinfo->interface_init_socket(id, interface_socket, node, socket, data_path); } } -// Note: New API, used when callbacks change. -static void UNUSED_FUNCTION(rna_NodeTreeInterfaceSocket_init_socket_custom)( +static void rna_NodeTreeInterfaceSocket_init_socket_custom( ID *id, const bNodeTreeInterfaceSocket *interface_socket, bNode *node, @@ -191,14 +219,11 @@ static void rna_NodeTreeInterfaceSocket_from_socket_builtin( { bNodeSocketType *typeinfo = interface_socket->socket_typeinfo(); if (typeinfo && typeinfo->interface_draw) { - // Note: New API, callback signatures change. - UNUSED_VARS(id, node, socket); - // typeinfo->interface_from_socket(id, interface_socket, node, socket); + typeinfo->interface_from_socket(id, interface_socket, node, socket); } } -// Note: New API, used after callback signatures change. -static void UNUSED_FUNCTION(rna_NodeTreeInterfaceSocket_from_socket_custom)( +static void rna_NodeTreeInterfaceSocket_from_socket_custom( ID *id, bNodeTreeInterfaceSocket *interface_socket, const bNode *node, @@ -275,14 +300,11 @@ static StructRNA *rna_NodeTreeInterfaceSocket_register(Main * /*bmain*/, st->ext_interface.free = free; RNA_struct_blender_type_set(st->ext_interface.srna, st); - // Note: New API callbacks. - // st->interface_draw = (have_function[0]) ? rna_NodeTreeInterfaceSocket_draw_custom : nullptr; - // st->interface_init_socket = (have_function[1]) ? - // rna_NodeTreeInterfaceSocket_init_socket_custom : - // nullptr; - // st->interface_from_socket = (have_function[2]) ? - // rna_NodeTreeInterfaceSocket_from_socket_custom : - // nullptr; + st->interface_draw = (have_function[0]) ? rna_NodeTreeInterfaceSocket_draw_custom : nullptr; + st->interface_init_socket = (have_function[1]) ? rna_NodeTreeInterfaceSocket_init_socket_custom : + nullptr; + st->interface_from_socket = (have_function[2]) ? rna_NodeTreeInterfaceSocket_from_socket_custom : + nullptr; /* Cleanup local dummy type. */ MEM_SAFE_FREE(dummy_socket.socket_type); @@ -397,8 +419,7 @@ static bNodeTreeInterfaceSocket *rna_NodeTreeInterfaceItems_new_socket( ReportList *reports, const char *name, const char *description, - bool is_input, - bool is_output, + int in_out, int socket_type_enum, bNodeTreeInterfacePanel *parent) { @@ -422,11 +443,7 @@ static bNodeTreeInterfaceSocket *rna_NodeTreeInterfaceItems_new_socket( } } const char *socket_type = typeinfo->idname; - - eNodeTreeInterfaceSocketFlag flag = eNodeTreeInterfaceSocketFlag(0); - SET_FLAG_FROM_TEST(flag, is_input, NODE_INTERFACE_SOCKET_INPUT); - SET_FLAG_FROM_TEST(flag, is_output, NODE_INTERFACE_SOCKET_OUTPUT); - + NodeTreeInterfaceSocketFlag flag = NodeTreeInterfaceSocketFlag(in_out); bNodeTreeInterfaceSocket *socket = interface->add_socket(name ? name : "", description ? description : "", socket_type ? socket_type : "", @@ -451,14 +468,26 @@ static bNodeTreeInterfacePanel *rna_NodeTreeInterfaceItems_new_panel( Main *bmain, ReportList *reports, const char *name, + const char *description, + bool default_closed, bNodeTreeInterfacePanel *parent) { - if (parent != nullptr && !interface->find_item(parent->item)) { - BKE_report(reports, RPT_ERROR_INVALID_INPUT, "Parent is not part of the interface"); - return nullptr; + if (parent != nullptr) { + if (!interface->find_item(parent->item)) { + BKE_report(reports, RPT_ERROR_INVALID_INPUT, "Parent is not part of the interface"); + return nullptr; + } + if (!(parent->flag & NODE_INTERFACE_PANEL_ALLOW_CHILD_PANELS)) { + BKE_report(reports, RPT_WARNING, "Parent panel does not allow child panels"); + return nullptr; + } } - bNodeTreeInterfacePanel *panel = interface->add_panel(name ? name : "", parent); + NodeTreeInterfacePanelFlag flag = NodeTreeInterfacePanelFlag(0); + SET_FLAG_FROM_TEST(flag, default_closed, NODE_INTERFACE_PANEL_DEFAULT_CLOSED); + + bNodeTreeInterfacePanel *panel = interface->add_panel( + name ? name : "", description ? description : "", flag, parent); if (panel == nullptr) { BKE_report(reports, RPT_ERROR, "Unable to create panel"); @@ -481,9 +510,17 @@ static bNodeTreeInterfaceItem *rna_NodeTreeInterfaceItems_copy_to_parent( bNodeTreeInterfaceItem *item, bNodeTreeInterfacePanel *parent) { - if (parent != nullptr && !interface->find_item(parent->item)) { - BKE_report(reports, RPT_ERROR_INVALID_INPUT, "Parent is not part of the interface"); - return nullptr; + if (parent != nullptr) { + if (!interface->find_item(parent->item)) { + BKE_report(reports, RPT_ERROR_INVALID_INPUT, "Parent is not part of the interface"); + return nullptr; + } + if (item->item_type == NODE_INTERFACE_PANEL && + !(parent->flag & NODE_INTERFACE_PANEL_ALLOW_CHILD_PANELS)) + { + BKE_report(reports, RPT_WARNING, "Parent panel does not allow child panels"); + return nullptr; + } } if (parent == nullptr) { @@ -547,10 +584,13 @@ static void rna_NodeTreeInterfaceItems_clear(ID *id, bNodeTreeInterface *interfa WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); } -static void rna_NodeTreeInterfaceItems_move( - ID *id, bNodeTreeInterface *interface, Main *bmain, bNodeTreeInterfaceItem *item, int to_index) +static void rna_NodeTreeInterfaceItems_move(ID *id, + bNodeTreeInterface *interface, + Main *bmain, + bNodeTreeInterfaceItem *item, + int to_position) { - interface->move_item(*item, to_index); + interface->move_item(*item, to_position); bNodeTree *ntree = reinterpret_cast(id); BKE_ntree_update_tag_interface(ntree); @@ -561,11 +601,19 @@ static void rna_NodeTreeInterfaceItems_move( static void rna_NodeTreeInterfaceItems_move_to_parent(ID *id, bNodeTreeInterface *interface, Main *bmain, + ReportList *reports, bNodeTreeInterfaceItem *item, bNodeTreeInterfacePanel *parent, - int to_index) + int to_position) { - interface->move_item_to_parent(*item, parent, to_index); + if (item->item_type == NODE_INTERFACE_PANEL && + !(parent->flag & NODE_INTERFACE_PANEL_ALLOW_CHILD_PANELS)) + { + BKE_report(reports, RPT_WARNING, "Parent panel does not allow child panels"); + return; + } + + interface->move_item_to_parent(*item, parent, to_position); bNodeTree *ntree = reinterpret_cast(id); BKE_ntree_update_tag_interface(ntree); @@ -575,6 +623,44 @@ static void rna_NodeTreeInterfaceItems_move_to_parent(ID *id, /* ******** Node Socket Subtypes ******** */ +static const EnumPropertyItem *rna_subtype_filter_itemf(const blender::Set &subtypes, + bool *r_free) +{ + if (subtypes.is_empty()) { + return rna_enum_dummy_NULL_items; + } + + EnumPropertyItem *items = nullptr; + int items_count = 0; + for (const EnumPropertyItem *item = rna_enum_property_subtype_items; item->name != nullptr; + item++) { + if (subtypes.contains(item->value)) { + RNA_enum_item_add(&items, &items_count, item); + } + } + + if (items_count == 0) { + return rna_enum_dummy_NULL_items; + } + + RNA_enum_item_end(&items, &items_count); + *r_free = true; + return items; +} + +static const EnumPropertyItem *rna_NodeTreeInterfaceSocketFloat_subtype_itemf( + bContext * /*C*/, PointerRNA * /*ptr*/, PropertyRNA * /*prop*/, bool *r_free) +{ + return rna_subtype_filter_itemf({PROP_PERCENTAGE, + PROP_FACTOR, + PROP_ANGLE, + PROP_TIME, + PROP_TIME_ABSOLUTE, + PROP_DISTANCE, + PROP_NONE}, + r_free); +} + void rna_NodeTreeInterfaceSocketFloat_default_value_range( PointerRNA *ptr, float *min, float *max, float *softmin, float *softmax) { @@ -593,6 +679,14 @@ void rna_NodeTreeInterfaceSocketFloat_default_value_range( *softmax = dval->max; } +static const EnumPropertyItem *rna_NodeTreeInterfaceSocketInt_subtype_itemf(bContext * /*C*/, + PointerRNA * /*ptr*/, + PropertyRNA * /*prop*/, + bool *r_free) +{ + return rna_subtype_filter_itemf({PROP_PERCENTAGE, PROP_FACTOR, PROP_NONE}, r_free); +} + void rna_NodeTreeInterfaceSocketInt_default_value_range( PointerRNA *ptr, int *min, int *max, int *softmin, int *softmax) { @@ -611,6 +705,19 @@ void rna_NodeTreeInterfaceSocketInt_default_value_range( *softmax = dval->max; } +static const EnumPropertyItem *rna_NodeTreeInterfaceSocketVector_subtype_itemf( + bContext * /*C*/, PointerRNA * /*ptr*/, PropertyRNA * /*prop*/, bool *r_free) +{ + return rna_subtype_filter_itemf({PROP_TRANSLATION, + PROP_DIRECTION, + PROP_VELOCITY, + PROP_ACCELERATION, + PROP_EULER, + PROP_XYZ, + PROP_NONE}, + r_free); +} + void rna_NodeTreeInterfaceSocketVector_default_value_range( PointerRNA *ptr, float *min, float *max, float *softmin, float *softmax) { @@ -650,15 +757,12 @@ static void rna_NodeTreeInterface_items_begin(CollectionPropertyIterator *iter, } ntree->ensure_topology_cache(); - // Note: New API, enabled when interface cache is added. - UNUSED_VARS(iter); - // const blender::bke::bNodeTreeInterfaceCache &cache = ntree->interface_cache(); - // rna_iterator_array_begin(iter, - // const_cast(cache.items.data()), - // sizeof(bNodeTreeInterfaceItem *), - // cache.items.size(), - // false, - // nullptr); + rna_iterator_array_begin(iter, + const_cast(ntree->interface_items().data()), + sizeof(bNodeTreeInterfaceItem *), + ntree->interface_items().size(), + false, + nullptr); } static int rna_NodeTreeInterface_items_length(PointerRNA *ptr) @@ -669,9 +773,7 @@ static int rna_NodeTreeInterface_items_length(PointerRNA *ptr) } ntree->ensure_topology_cache(); - // Note: New API callbacks. - // return ntree->interface_cache().items.size(); - return 0; + return ntree->interface_items().size(); } static int rna_NodeTreeInterface_items_lookup_int(PointerRNA *ptr, int index, PointerRNA *r_ptr) @@ -682,14 +784,12 @@ static int rna_NodeTreeInterface_items_lookup_int(PointerRNA *ptr, int index, Po } ntree->ensure_topology_cache(); - // Note: New API, enabled when interface cache is added. - UNUSED_VARS(index, r_ptr); - // const blender::bke::bNodeTreeInterfaceCache &cache = ntree->interface_cache(); - // if (!cache.items.index_range().contains(index)) { - // return false; - // } + if (!ntree->interface_items().index_range().contains(index)) { + return false; + } - // RNA_pointer_create(ptr->owner_id, &RNA_NodeTreeInterfaceItem, cache.items[index], r_ptr); + RNA_pointer_create( + ptr->owner_id, &RNA_NodeTreeInterfaceItem, ntree->interface_items()[index], r_ptr); return true; } @@ -703,29 +803,26 @@ static int rna_NodeTreeInterface_items_lookup_string(struct PointerRNA *ptr, } ntree->ensure_topology_cache(); - // Note: New API, enabled when interface cache is added. - UNUSED_VARS(key, r_ptr); - // const blender::bke::bNodeTreeInterfaceCache &cache = ntree->interface_cache(); - // for (bNodeTreeInterfaceItem *item : cache.items) { - // switch (item->item_type) { - // case NODE_INTERFACE_SOCKET: { - // bNodeTreeInterfaceSocket *socket = reinterpret_cast(item); - // if (STREQ(socket->name, key)) { - // RNA_pointer_create(ptr->owner_id, &RNA_NodeTreeInterfaceSocket, socket, r_ptr); - // return true; - // } - // break; - // } - // case NODE_INTERFACE_PANEL: { - // bNodeTreeInterfacePanel *panel = reinterpret_cast(item); - // if (STREQ(panel->name, key)) { - // RNA_pointer_create(ptr->owner_id, &RNA_NodeTreeInterfacePanel, panel, r_ptr); - // return true; - // } - // break; - // } - // } - // } + for (bNodeTreeInterfaceItem *item : ntree->interface_items()) { + switch (item->item_type) { + case NODE_INTERFACE_SOCKET: { + bNodeTreeInterfaceSocket *socket = reinterpret_cast(item); + if (STREQ(socket->name, key)) { + RNA_pointer_create(ptr->owner_id, &RNA_NodeTreeInterfaceSocket, socket, r_ptr); + return true; + } + break; + } + case NODE_INTERFACE_PANEL: { + bNodeTreeInterfacePanel *panel = reinterpret_cast(item); + if (STREQ(panel->name, key)) { + RNA_pointer_create(ptr->owner_id, &RNA_NodeTreeInterfacePanel, panel, r_ptr); + return true; + } + break; + } + } + } return false; } @@ -747,6 +844,26 @@ static void rna_def_node_interface_item(BlenderRNA *brna) RNA_def_property_enum_items(prop, rna_enum_node_tree_interface_item_type_items); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Item Type", "Type of interface item"); + + prop = RNA_def_property(srna, "parent", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "NodeTreeInterfacePanel"); + RNA_def_property_pointer_funcs( + prop, "rna_NodeTreeInterfaceItem_parent_get", nullptr, nullptr, nullptr); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Parent", "Panel that contains the item"); + + prop = RNA_def_property(srna, "position", PROP_INT, PROP_NONE); + RNA_def_property_int_funcs(prop, "rna_NodeTreeInterfaceItem_position_get", nullptr, nullptr); + RNA_def_property_range(prop, -1, INT_MAX); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Position", "Position of the item in its parent panel"); + + prop = RNA_def_property(srna, "index", PROP_INT, PROP_NONE); + RNA_def_property_int_funcs(prop, "rna_NodeTreeInterfaceItem_index_get", nullptr, nullptr); + RNA_def_property_range(prop, -1, INT_MAX); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text( + prop, "Index", "Global index of the item among all items in the interface"); } static void rna_def_node_interface_socket(BlenderRNA *brna) @@ -789,20 +906,17 @@ static void rna_def_node_interface_socket(BlenderRNA *brna) "rna_NodeTreeInterfaceSocket_socket_type_get", "rna_NodeTreeInterfaceSocket_socket_type_set", "rna_NodeTreeInterfaceSocket_socket_type_itemf"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_ui_text( prop, "Socket Type", "Type of the socket generated by this interface item"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceItem_update"); - prop = RNA_def_property(srna, "is_input", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, nullptr, "flag", NODE_INTERFACE_SOCKET_INPUT); + prop = RNA_def_property(srna, "in_out", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_bitflag_sdna(prop, nullptr, "flag"); + RNA_def_property_enum_items(prop, node_tree_interface_socket_in_out_items); + RNA_def_property_enum_default(prop, NODE_INTERFACE_SOCKET_INPUT); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_ui_text(prop, "Is Input", "Whether the socket is an input"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceItem_update"); - - prop = RNA_def_property(srna, "is_output", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, nullptr, "flag", NODE_INTERFACE_SOCKET_OUTPUT); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_ui_text(prop, "Is Output", "Whether the socket is an output"); + RNA_def_property_ui_text(prop, "Input/Output Type", "Input or output socket type"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceItem_update"); prop = RNA_def_property(srna, "hide_value", PROP_BOOLEAN, PROP_NONE); @@ -843,7 +957,7 @@ static void rna_def_node_interface_socket(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Socket Type Name", "Name of the socket type"); func = RNA_def_function(srna, "draw", nullptr); - RNA_def_function_flag(func, FUNC_REGISTER_OPTIONAL | FUNC_USE_SELF_ID); + RNA_def_function_flag(func, FUNC_REGISTER_OPTIONAL); RNA_def_function_ui_description(func, "Draw properties of the socket interface"); parm = RNA_def_pointer(func, "context", "Context", "", ""); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); @@ -854,7 +968,7 @@ static void rna_def_node_interface_socket(BlenderRNA *brna) func = RNA_def_function(srna, "init_socket", nullptr); RNA_def_function_ui_description(func, "Initialize a node socket instance"); - RNA_def_function_flag(func, FUNC_REGISTER_OPTIONAL | FUNC_USE_SELF_ID | FUNC_ALLOW_WRITE); + RNA_def_function_flag(func, FUNC_REGISTER_OPTIONAL | FUNC_ALLOW_WRITE); parm = RNA_def_pointer(func, "node", "Node", "Node", "Node of the socket to initialize"); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); parm = RNA_def_pointer(func, "socket", "NodeSocket", "Socket", "Socket to initialize"); @@ -865,7 +979,7 @@ static void rna_def_node_interface_socket(BlenderRNA *brna) func = RNA_def_function(srna, "from_socket", nullptr); RNA_def_function_ui_description(func, "Setup template parameters from an existing socket"); - RNA_def_function_flag(func, FUNC_REGISTER_OPTIONAL | FUNC_USE_SELF_ID | FUNC_ALLOW_WRITE); + RNA_def_function_flag(func, FUNC_REGISTER_OPTIONAL | FUNC_ALLOW_WRITE); parm = RNA_def_pointer(func, "node", "Node", "Node", "Node of the original socket"); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); parm = RNA_def_pointer(func, "socket", "NodeSocket", "Socket", "Original socket"); @@ -886,6 +1000,17 @@ static void rna_def_node_interface_panel(BlenderRNA *brna) RNA_def_struct_name_property(srna, prop); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceItem_update"); + prop = RNA_def_property(srna, "description", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, nullptr, "description"); + RNA_def_property_ui_text(prop, "Description", "Panel description"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceItem_update"); + + prop = RNA_def_property(srna, "default_closed", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "flag", NODE_INTERFACE_PANEL_DEFAULT_CLOSED); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_text(prop, "Default Closed", "Panel is closed by default on new nodes"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceItem_update"); + prop = RNA_def_property(srna, "interface_items", PROP_COLLECTION, PROP_NONE); RNA_def_property_collection_sdna(prop, nullptr, "items_array", "items_num"); RNA_def_property_struct_type(prop, "NodeTreeInterfaceItem"); @@ -922,8 +1047,12 @@ static void rna_def_node_tree_interface_items_api(StructRNA *srna) parm = RNA_def_string(func, "name", nullptr, 0, "Name", "Name of the socket"); RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); RNA_def_string(func, "description", nullptr, 0, "Description", "Description of the socket"); - RNA_def_boolean(func, "is_input", false, "Is Input", "Create an input socket"); - RNA_def_boolean(func, "is_output", false, "Is Output", "Create an output socket"); + RNA_def_enum_flag(func, + "in_out", + node_tree_interface_socket_in_out_items, + NODE_INTERFACE_SOCKET_INPUT, + "Input/Output Type", + "Create an input or output socket"); parm = RNA_def_enum(func, "socket_type", rna_enum_dummy_DEFAULT_items, @@ -943,6 +1072,9 @@ static void rna_def_node_tree_interface_items_api(StructRNA *srna) RNA_def_function_ui_description(func, "Add a new panel to the interface"); RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN | FUNC_USE_REPORTS); parm = RNA_def_string(func, "name", nullptr, 0, "Name", "Name of the new panel"); + RNA_def_string(func, "description", nullptr, 0, "Description", "Description of the panel"); + RNA_def_boolean( + func, "default_closed", false, "Default Closed", "Panel is closed by default on new nodes"); RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); RNA_def_pointer(func, "parent", @@ -984,20 +1116,34 @@ static void rna_def_node_tree_interface_items_api(StructRNA *srna) RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN); parm = RNA_def_pointer(func, "item", "NodeTreeInterfaceItem", "Item", "The item to remove"); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); - parm = RNA_def_int( - func, "to_index", -1, 0, INT_MAX, "To Index", "Target index for the item", 0, 10000); + parm = RNA_def_int(func, + "to_position", + -1, + 0, + INT_MAX, + "To Position", + "Target position for the item in its current panel", + 0, + 10000); RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); func = RNA_def_function(srna, "move_to_parent", "rna_NodeTreeInterfaceItems_move_to_parent"); RNA_def_function_ui_description(func, "Move an item to a new panel and/or position."); - RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN); + RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN | FUNC_USE_REPORTS); parm = RNA_def_pointer(func, "item", "NodeTreeInterfaceItem", "Item", "The item to remove"); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); parm = RNA_def_pointer( func, "parent", "NodeTreeInterfacePanel", "Parent", "New parent of the item"); RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); - parm = RNA_def_int( - func, "to_index", -1, 0, INT_MAX, "To Index", "Target index for the item", 0, 10000); + parm = RNA_def_int(func, + "to_position", + -1, + 0, + INT_MAX, + "To Position", + "Target position for the item in the new parent panel", + 0, + 10000); RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); } diff --git a/source/blender/makesrna/intern/rna_nodetree.cc b/source/blender/makesrna/intern/rna_nodetree.cc index 2bde74f4831..e3fbc30b478 100644 --- a/source/blender/makesrna/intern/rna_nodetree.cc +++ b/source/blender/makesrna/intern/rna_nodetree.cc @@ -1287,205 +1287,11 @@ static void rna_NodeTree_link_clear(bNodeTree *ntree, Main *bmain, ReportList *r WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); } -static int rna_NodeTree_active_input_get(PointerRNA *ptr) -{ - bNodeTree *ntree = static_cast(ptr->data); - int index = 0; - LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, &ntree->inputs, index) { - if (socket->flag & SELECT) { - return index; - } - } - return -1; -} - -static void rna_NodeTree_active_input_set(PointerRNA *ptr, int value) -{ - bNodeTree *ntree = static_cast(ptr->data); - - int index = 0; - LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, &ntree->inputs, index) { - SET_FLAG_FROM_TEST(socket->flag, index == value, SELECT); - } -} - -static int rna_NodeTree_active_output_get(PointerRNA *ptr) -{ - bNodeTree *ntree = static_cast(ptr->data); - int index = 0; - LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, &ntree->outputs, index) { - if (socket->flag & SELECT) { - return index; - } - } - return -1; -} - -static void rna_NodeTree_active_output_set(PointerRNA *ptr, int value) -{ - bNodeTree *ntree = static_cast(ptr->data); - - int index = 0; - LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, &ntree->outputs, index) { - SET_FLAG_FROM_TEST(socket->flag, index == value, SELECT); - } -} - static bool rna_NodeTree_contains_tree(bNodeTree *tree, bNodeTree *sub_tree) { return ntreeContainsTree(tree, sub_tree); } -static bNodeSocket *rna_NodeTree_inputs_new( - bNodeTree *ntree, Main *bmain, ReportList *reports, const char *type, const char *name) -{ - if (!rna_NodeTree_check(ntree, reports)) { - return nullptr; - } - - bNodeSocket *sock = ntreeAddSocketInterface(ntree, SOCK_IN, type, name); - - if (sock == nullptr) { - BKE_report(reports, RPT_ERROR, "Unable to create socket"); - } - else { - ED_node_tree_propagate_change(nullptr, bmain, ntree); - WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); - } - - return sock; -} - -static bNodeSocket *rna_NodeTree_outputs_new( - bNodeTree *ntree, Main *bmain, ReportList *reports, const char *type, const char *name) -{ - if (!rna_NodeTree_check(ntree, reports)) { - return nullptr; - } - - bNodeSocket *sock = ntreeAddSocketInterface(ntree, SOCK_OUT, type, name); - - if (sock == nullptr) { - BKE_report(reports, RPT_ERROR, "Unable to create socket"); - } - else { - ED_node_tree_propagate_change(nullptr, bmain, ntree); - WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); - } - - return sock; -} - -static void rna_NodeTree_socket_remove(bNodeTree *ntree, - Main *bmain, - ReportList *reports, - bNodeSocket *sock) -{ - if (!rna_NodeTree_check(ntree, reports)) { - return; - } - - if (BLI_findindex(&ntree->inputs, sock) == -1 && BLI_findindex(&ntree->outputs, sock) == -1) { - BKE_reportf(reports, RPT_ERROR, "Unable to locate socket '%s' in node", sock->identifier); - } - else { - ntreeRemoveSocketInterface(ntree, sock); - - ED_node_tree_propagate_change(nullptr, bmain, ntree); - WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); - } -} - -static void rna_NodeTree_inputs_clear(bNodeTree *ntree, Main *bmain, ReportList *reports) -{ - if (!rna_NodeTree_check(ntree, reports)) { - return; - } - - LISTBASE_FOREACH_MUTABLE (bNodeSocket *, socket, &ntree->inputs) { - ntreeRemoveSocketInterface(ntree, socket); - } - - ED_node_tree_propagate_change(nullptr, bmain, ntree); - WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); -} - -static void rna_NodeTree_outputs_clear(bNodeTree *ntree, Main *bmain, ReportList *reports) -{ - if (!rna_NodeTree_check(ntree, reports)) { - return; - } - - LISTBASE_FOREACH_MUTABLE (bNodeSocket *, socket, &ntree->outputs) { - ntreeRemoveSocketInterface(ntree, socket); - } - - ED_node_tree_propagate_change(nullptr, bmain, ntree); - WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); -} - -static void rna_NodeTree_inputs_move(bNodeTree *ntree, Main *bmain, int from_index, int to_index) -{ - if (from_index == to_index) { - return; - } - if (from_index < 0 || to_index < 0) { - return; - } - - bNodeSocket *sock = static_cast(BLI_findlink(&ntree->inputs, from_index)); - if (to_index < from_index) { - bNodeSocket *nextsock = static_cast(BLI_findlink(&ntree->inputs, to_index)); - if (nextsock) { - BLI_remlink(&ntree->inputs, sock); - BLI_insertlinkbefore(&ntree->inputs, nextsock, sock); - } - } - else { - bNodeSocket *prevsock = static_cast(BLI_findlink(&ntree->inputs, to_index)); - if (prevsock) { - BLI_remlink(&ntree->inputs, sock); - BLI_insertlinkafter(&ntree->inputs, prevsock, sock); - } - } - - BKE_ntree_update_tag_interface(ntree); - - ED_node_tree_propagate_change(nullptr, bmain, ntree); - WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); -} - -static void rna_NodeTree_outputs_move(bNodeTree *ntree, Main *bmain, int from_index, int to_index) -{ - if (from_index == to_index) { - return; - } - if (from_index < 0 || to_index < 0) { - return; - } - - bNodeSocket *sock = static_cast(BLI_findlink(&ntree->outputs, from_index)); - if (to_index < from_index) { - bNodeSocket *nextsock = static_cast(BLI_findlink(&ntree->outputs, to_index)); - if (nextsock) { - BLI_remlink(&ntree->outputs, sock); - BLI_insertlinkbefore(&ntree->outputs, nextsock, sock); - } - } - else { - bNodeSocket *prevsock = static_cast(BLI_findlink(&ntree->outputs, to_index)); - if (prevsock) { - BLI_remlink(&ntree->outputs, sock); - BLI_insertlinkafter(&ntree->outputs, prevsock, sock); - } - } - - BKE_ntree_update_tag_interface(ntree); - - ED_node_tree_propagate_change(nullptr, bmain, ntree); - WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); -} - static void rna_NodeTree_interface_update(bNodeTree *ntree, bContext *C) { Main *bmain = CTX_data_main(C); @@ -10214,57 +10020,6 @@ static void rna_def_nodetree_link_api(BlenderRNA *brna, PropertyRNA *cprop) RNA_def_function_flag(func, FUNC_USE_MAIN | FUNC_USE_REPORTS); } -static void rna_def_node_tree_sockets_api(BlenderRNA *brna, PropertyRNA *cprop, int in_out) -{ - StructRNA *srna; - PropertyRNA *parm; - FunctionRNA *func; - const char *structtype = (in_out == SOCK_IN ? "NodeTreeInputs" : "NodeTreeOutputs"); - const char *uiname = (in_out == SOCK_IN ? "Node Tree Inputs" : "Node Tree Outputs"); - const char *newfunc = (in_out == SOCK_IN ? "rna_NodeTree_inputs_new" : - "rna_NodeTree_outputs_new"); - const char *clearfunc = (in_out == SOCK_IN ? "rna_NodeTree_inputs_clear" : - "rna_NodeTree_outputs_clear"); - const char *movefunc = (in_out == SOCK_IN ? "rna_NodeTree_inputs_move" : - "rna_NodeTree_outputs_move"); - - RNA_def_property_srna(cprop, structtype); - srna = RNA_def_struct(brna, structtype, nullptr); - RNA_def_struct_sdna(srna, "bNodeTree"); - RNA_def_struct_ui_text(srna, uiname, "Collection of Node Tree Sockets"); - - func = RNA_def_function(srna, "new", newfunc); - RNA_def_function_ui_description(func, "Add a socket to this node tree"); - RNA_def_function_flag(func, FUNC_USE_MAIN | FUNC_USE_REPORTS); - parm = RNA_def_string(func, "type", nullptr, MAX_NAME, "Type", "Data type"); - RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); - parm = RNA_def_string(func, "name", nullptr, MAX_NAME, "Name", ""); - RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); - /* return value */ - parm = RNA_def_pointer(func, "socket", "NodeSocketInterface", "", "New socket"); - RNA_def_function_return(func, parm); - - func = RNA_def_function(srna, "remove", "rna_NodeTree_socket_remove"); - RNA_def_function_ui_description(func, "Remove a socket from this node tree"); - RNA_def_function_flag(func, FUNC_USE_MAIN | FUNC_USE_REPORTS); - parm = RNA_def_pointer(func, "socket", "NodeSocketInterface", "", "The socket to remove"); - RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); - - func = RNA_def_function(srna, "clear", clearfunc); - RNA_def_function_ui_description(func, "Remove all sockets from this node tree"); - RNA_def_function_flag(func, FUNC_USE_MAIN | FUNC_USE_REPORTS); - - func = RNA_def_function(srna, "move", movefunc); - RNA_def_function_ui_description(func, "Move a socket to another position"); - RNA_def_function_flag(func, FUNC_USE_MAIN); - parm = RNA_def_int( - func, "from_index", -1, 0, INT_MAX, "From Index", "Index of the socket to move", 0, 10000); - RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); - parm = RNA_def_int( - func, "to_index", -1, 0, INT_MAX, "To Index", "Target index for the socket", 0, 10000); - RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); -} - static void rna_def_nodetree(BlenderRNA *brna) { StructRNA *srna; @@ -10278,6 +10033,7 @@ static void rna_def_nodetree(BlenderRNA *brna) ICON_QUESTION, "Undefined", "Undefined type of nodes (can happen e.g. when a linked node tree goes missing)"}, + {NTREE_CUSTOM, "CUSTOM", ICON_NONE, "Custom", "Custom nodes"}, {NTREE_SHADER, "SHADER", ICON_MATERIAL, "Shader", "Shader nodes"}, {NTREE_TEXTURE, "TEXTURE", ICON_TEXTURE, "Texture", "Texture nodes"}, {NTREE_COMPOSIT, "COMPOSITING", ICON_RENDERLAYERS, "Compositing", "Compositing nodes"}, @@ -10339,34 +10095,11 @@ static void rna_def_nodetree(BlenderRNA *brna) "Type", "Node Tree type (deprecated, bl_idname is the actual node tree type identifier)"); - prop = RNA_def_property(srna, "inputs", PROP_COLLECTION, PROP_NONE); - RNA_def_property_collection_sdna(prop, nullptr, "inputs", nullptr); - RNA_def_property_struct_type(prop, "NodeSocketInterface"); + prop = RNA_def_property(srna, "interface", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, nullptr, "tree_interface"); + RNA_def_property_struct_type(prop, "NodeTreeInterface"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); - RNA_def_property_ui_text(prop, "Inputs", "Node tree inputs"); - rna_def_node_tree_sockets_api(brna, prop, SOCK_IN); - - prop = RNA_def_property(srna, "active_input", PROP_INT, PROP_UNSIGNED); - RNA_def_property_int_funcs( - prop, "rna_NodeTree_active_input_get", "rna_NodeTree_active_input_set", nullptr); - RNA_def_property_ui_text(prop, "Active Input", "Index of the active input"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_update(prop, NC_NODE, nullptr); - - prop = RNA_def_property(srna, "outputs", PROP_COLLECTION, PROP_NONE); - RNA_def_property_collection_sdna(prop, nullptr, "outputs", nullptr); - RNA_def_property_struct_type(prop, "NodeSocketInterface"); - RNA_def_property_clear_flag(prop, PROP_EDITABLE); - RNA_def_property_ui_text(prop, "Outputs", "Node tree outputs"); - rna_def_node_tree_sockets_api(brna, prop, SOCK_OUT); - - prop = RNA_def_property(srna, "active_output", PROP_INT, PROP_UNSIGNED); - RNA_def_property_int_funcs( - prop, "rna_NodeTree_active_output_get", "rna_NodeTree_active_output_set", nullptr); - RNA_def_property_ui_text(prop, "Active Output", "Index of the active output"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_update(prop, NC_NODE, nullptr); - + RNA_def_property_ui_text(prop, "Interface", "Interface declaration for this node tree"); /* exposed as a function for runtime interface type properties */ func = RNA_def_function(srna, "interface_update", "rna_NodeTree_interface_update"); RNA_def_function_ui_description(func, "Updated node group interface"); diff --git a/source/blender/makesrna/intern/rna_ui_api.cc b/source/blender/makesrna/intern/rna_ui_api.cc index 45c12ccf87a..d1c4d031179 100644 --- a/source/blender/makesrna/intern/rna_ui_api.cc +++ b/source/blender/makesrna/intern/rna_ui_api.cc @@ -2085,6 +2085,15 @@ void RNA_api_ui_layout(StructRNA *srna) srna, "template_grease_pencil_layer_tree", "uiTemplateGreasePencilLayerTree"); RNA_def_function_ui_description(func, "View of the active grease pencil layer tree"); RNA_def_function_flag(func, FUNC_USE_CONTEXT); + + func = RNA_def_function(srna, "template_node_tree_interface", "uiTemplateNodeTreeInterface"); + RNA_def_function_ui_description(func, "Show a node tree interface"); + parm = RNA_def_pointer(func, + "interface", + "NodeTreeInterface", + "Node Tree Interface", + "Interface of a node tree to display"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); } #endif diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index 7cfece53f52..3c7a5f83a8a 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -622,17 +622,19 @@ static void check_property_socket_sync(const Object *ob, ModifierData *md) int geometry_socket_count = 0; - int i; - LISTBASE_FOREACH_INDEX (const bNodeSocket *, socket, &nmd->node_group->inputs, i) { + for (const int i : nmd->node_group->interface_inputs().index_range()) { + const bNodeTreeInterfaceSocket *socket = nmd->node_group->interface_inputs()[i]; + const bNodeSocketType *typeinfo = socket->socket_typeinfo(); + const eNodeSocketDatatype type = typeinfo ? eNodeSocketDatatype(typeinfo->type) : SOCK_CUSTOM; /* The first socket is the special geometry socket for the modifier object. */ - if (i == 0 && socket->type == SOCK_GEOMETRY) { + if (i == 0 && type == SOCK_GEOMETRY) { geometry_socket_count++; continue; } IDProperty *property = IDP_GetPropertyFromGroup(nmd->settings.properties, socket->identifier); if (property == nullptr) { - if (socket->type == SOCK_GEOMETRY) { + if (type == SOCK_GEOMETRY) { geometry_socket_count++; } else { @@ -649,7 +651,10 @@ static void check_property_socket_sync(const Object *ob, ModifierData *md) } if (geometry_socket_count == 1) { - if (((bNodeSocket *)nmd->node_group->inputs.first)->type != SOCK_GEOMETRY) { + const bNodeTreeInterfaceSocket *first_socket = nmd->node_group->interface_inputs()[0]; + const bNodeSocketType *typeinfo = first_socket->socket_typeinfo(); + const eNodeSocketDatatype type = typeinfo ? eNodeSocketDatatype(typeinfo->type) : SOCK_CUSTOM; + if (type != SOCK_GEOMETRY) { BKE_modifier_set_error(ob, md, "Node group's geometry input must be the first"); } } @@ -1053,7 +1058,7 @@ static void add_attribute_search_button(const bContext &C, const NodesModifierData &nmd, PointerRNA *md_ptr, const StringRefNull rna_path_attribute_name, - const bNodeSocket &socket, + const bNodeTreeInterfaceSocket &socket, const bool is_output) { if (!nmd.runtime->eval_log) { @@ -1116,10 +1121,13 @@ static void add_attribute_search_or_value_buttons(const bContext &C, uiLayout *layout, const NodesModifierData &nmd, PointerRNA *md_ptr, - const bNodeSocket &socket) + const bNodeTreeInterfaceSocket &socket) { - char socket_id_esc[sizeof(socket.identifier) * 2]; - BLI_str_escape(socket_id_esc, socket.identifier, sizeof(socket_id_esc)); + const StringRefNull identifier = socket.identifier; + const bNodeSocketType *typeinfo = socket.socket_typeinfo(); + const eNodeSocketDatatype type = typeinfo ? eNodeSocketDatatype(typeinfo->type) : SOCK_CUSTOM; + char socket_id_esc[MAX_NAME * 2]; + BLI_str_escape(socket_id_esc, identifier.c_str(), sizeof(socket_id_esc)); const std::string rna_path = "[\"" + std::string(socket_id_esc) + "\"]"; const std::string rna_path_attribute_name = "[\"" + std::string(socket_id_esc) + nodes::input_attribute_name_suffix() + "\"]"; @@ -1133,7 +1141,7 @@ static void add_attribute_search_or_value_buttons(const bContext &C, const std::optional attribute_name = nodes::input_attribute_name_get( *nmd.settings.properties, socket); - if (socket.type == SOCK_BOOLEAN && !attribute_name) { + if (type == SOCK_BOOLEAN && !attribute_name) { uiItemL(name_row, "", ICON_NONE); } else { @@ -1141,7 +1149,7 @@ static void add_attribute_search_or_value_buttons(const bContext &C, } uiLayout *prop_row = uiLayoutRow(split, true); - if (socket.type == SOCK_BOOLEAN) { + if (type == SOCK_BOOLEAN) { uiLayoutSetPropSep(prop_row, false); uiLayoutSetAlignment(prop_row, UI_LAYOUT_ALIGN_EXPAND); } @@ -1151,7 +1159,7 @@ static void add_attribute_search_or_value_buttons(const bContext &C, uiItemL(layout, "", ICON_BLANK1); } else { - const char *name = socket.type == SOCK_BOOLEAN ? socket.name : ""; + const char *name = type == SOCK_BOOLEAN ? socket.name : ""; uiItemR(prop_row, md_ptr, rna_path.c_str(), UI_ITEM_NONE, name, ICON_NONE); uiItemDecoratorR(layout, md_ptr, rna_path.c_str(), -1); } @@ -1177,11 +1185,12 @@ static void draw_property_for_socket(const bContext &C, NodesModifierData *nmd, PointerRNA *bmain_ptr, PointerRNA *md_ptr, - const bNodeSocket &socket, + const bNodeTreeInterfaceSocket &socket, const int socket_index) { + const StringRefNull identifier = socket.identifier; /* The property should be created in #MOD_nodes_update_interface with the correct type. */ - IDProperty *property = IDP_GetPropertyFromGroup(nmd->settings.properties, socket.identifier); + IDProperty *property = IDP_GetPropertyFromGroup(nmd->settings.properties, identifier.c_str()); /* IDProperties can be removed with python, so there could be a situation where * there isn't a property for a socket or it doesn't have the correct type. */ @@ -1189,8 +1198,8 @@ static void draw_property_for_socket(const bContext &C, return; } - char socket_id_esc[sizeof(socket.identifier) * 2]; - BLI_str_escape(socket_id_esc, socket.identifier, sizeof(socket_id_esc)); + char socket_id_esc[MAX_NAME * 2]; + BLI_str_escape(socket_id_esc, identifier.c_str(), sizeof(socket_id_esc)); char rna_path[sizeof(socket_id_esc) + 4]; SNPRINTF(rna_path, "[\"%s\"]", socket_id_esc); @@ -1201,7 +1210,9 @@ static void draw_property_for_socket(const bContext &C, /* Use #uiItemPointerR to draw pointer properties because #uiItemR would not have enough * information about what type of ID to select for editing the values. This is because * pointer IDProperties contain no information about their type. */ - switch (socket.type) { + const bNodeSocketType *typeinfo = socket.socket_typeinfo(); + const eNodeSocketDatatype type = typeinfo ? eNodeSocketDatatype(typeinfo->type) : SOCK_CUSTOM; + switch (type) { case SOCK_OBJECT: { uiItemPointerR(row, md_ptr, rna_path, bmain_ptr, "objects", socket.name, ICON_OBJECT_DATA); break; @@ -1241,10 +1252,11 @@ static void draw_property_for_output_socket(const bContext &C, uiLayout *layout, const NodesModifierData &nmd, PointerRNA *md_ptr, - const bNodeSocket &socket) + const bNodeTreeInterfaceSocket &socket) { - char socket_id_esc[sizeof(socket.identifier) * 2]; - BLI_str_escape(socket_id_esc, socket.identifier, sizeof(socket_id_esc)); + const StringRefNull identifier = socket.identifier; + char socket_id_esc[MAX_NAME * 2]; + BLI_str_escape(socket_id_esc, identifier.c_str(), sizeof(socket_id_esc)); const std::string rna_path_attribute_name = "[\"" + StringRef(socket_id_esc) + nodes::input_attribute_name_suffix() + "\"]"; @@ -1285,9 +1297,11 @@ static void panel_draw(const bContext *C, Panel *panel) PointerRNA bmain_ptr; RNA_main_pointer_create(bmain, &bmain_ptr); - int socket_index; - LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, &nmd->node_group->inputs, socket_index) { - if (!(socket->flag & SOCK_HIDE_IN_MODIFIER)) { + nmd->node_group->ensure_topology_cache(); + + for (const int socket_index : nmd->node_group->interface_inputs().index_range()) { + const bNodeTreeInterfaceSocket *socket = nmd->node_group->interface_inputs()[socket_index]; + if (!(socket->flag & NODE_INTERFACE_SOCKET_HIDE_IN_MODIFIER)) { draw_property_for_socket(*C, layout, nmd, &bmain_ptr, ptr, *socket, socket_index); } } @@ -1319,8 +1333,11 @@ static void output_attribute_panel_draw(const bContext *C, Panel *panel) bool has_output_attribute = false; if (nmd->node_group != nullptr && nmd->settings.properties != nullptr) { - LISTBASE_FOREACH (bNodeSocket *, socket, &nmd->node_group->outputs) { - if (nodes::socket_type_has_attribute_toggle(*socket)) { + for (const bNodeTreeInterfaceSocket *socket : nmd->node_group->interface_outputs()) { + const bNodeSocketType *typeinfo = socket->socket_typeinfo(); + const eNodeSocketDatatype type = typeinfo ? eNodeSocketDatatype(typeinfo->type) : + SOCK_CUSTOM; + if (nodes::socket_type_has_attribute_toggle(type)) { has_output_attribute = true; draw_property_for_output_socket(*C, layout, *nmd, ptr, *socket); } diff --git a/source/blender/nodes/NOD_geometry_nodes_execute.hh b/source/blender/nodes/NOD_geometry_nodes_execute.hh index 4b448f6747d..2e269409201 100644 --- a/source/blender/nodes/NOD_geometry_nodes_execute.hh +++ b/source/blender/nodes/NOD_geometry_nodes_execute.hh @@ -9,9 +9,11 @@ #include "BLI_multi_value_map.hh" #include "BKE_idprop.hh" +#include "BKE_node.h" struct bNodeTree; struct bNodeSocket; +struct bNodeTreeInterfaceSocket; struct Depsgraph; namespace blender::bke { struct GeometrySet; @@ -31,12 +33,12 @@ StringRef input_use_attribute_suffix(); StringRef input_attribute_name_suffix(); std::optional input_attribute_name_get(const IDProperty &props, - const bNodeSocket &io_input); + const bNodeTreeInterfaceSocket &io_input); /** * \return Whether using an attribute to input values of this type is supported. */ -bool socket_type_has_attribute_toggle(const bNodeSocket &socket); +bool socket_type_has_attribute_toggle(eNodeSocketDatatype type); /** * \return Whether using an attribute to input values of this type is supported, and the node @@ -44,10 +46,11 @@ bool socket_type_has_attribute_toggle(const bNodeSocket &socket); */ bool input_has_attribute_toggle(const bNodeTree &node_tree, const int socket_index); -bool id_property_type_matches_socket(const bNodeSocket &socket, const IDProperty &property); +bool id_property_type_matches_socket(const bNodeTreeInterfaceSocket &socket, + const IDProperty &property); std::unique_ptr id_property_create_from_socket( - const bNodeSocket &socket); + const bNodeTreeInterfaceSocket &socket); bke::GeometrySet execute_geometry_nodes_on_geometry( const bNodeTree &btree, diff --git a/source/blender/nodes/NOD_node_declaration.hh b/source/blender/nodes/NOD_node_declaration.hh index 9610a678308..47a0a6f57c4 100644 --- a/source/blender/nodes/NOD_node_declaration.hh +++ b/source/blender/nodes/NOD_node_declaration.hh @@ -154,10 +154,18 @@ namespace aal = anonymous_attribute_lifetime; using ImplicitInputValueFn = std::function; +/* Socket or panel declaration. */ +class ItemDeclaration { + public: + virtual ~ItemDeclaration() = default; +}; + +using ItemDeclarationPtr = std::unique_ptr; + /** * Describes a single input or output socket. This is subclassed for different socket types. */ -class SocketDeclaration { +class SocketDeclaration : public ItemDeclaration { public: std::string name; std::string identifier; @@ -479,10 +487,55 @@ class SocketDeclarationBuilder : public BaseSocketDeclarationBuilder { using SocketDeclarationPtr = std::unique_ptr; +/** + * Describes a panel containing sockets or other panels. + */ +class PanelDeclaration : public ItemDeclaration { + public: + int identifier; + std::string name; + std::string description; + std::string translation_context; + bool default_collapsed = false; + int num_items = 0; + + private: + friend NodeDeclarationBuilder; + friend class PanelDeclarationBuilder; + + public: + virtual ~PanelDeclaration() = default; + + void build(bNodePanelState &panel) const; + bool matches(const bNodePanelState &panel) const; + void update_or_build(const bNodePanelState &old_panel, bNodePanelState &new_panel) const; +}; + +class PanelDeclarationBuilder { + protected: + NodeDeclarationBuilder *node_decl_builder_ = nullptr; + PanelDeclaration *decl_; + + friend class NodeDeclarationBuilder; + + public: + PanelDeclarationBuilder &default_closed(bool collapsed) + { + decl_->default_collapsed = collapsed; + return *this; + } +}; + +using PanelDeclarationPtr = std::unique_ptr; + class NodeDeclaration { public: - Vector inputs; - Vector outputs; + /* Combined list of socket and panel declarations. + * This determines order of sockets in the UI and panel content. */ + Vector items; + /* Note: inputs and outputs pointers are owned by the items list. */ + Vector inputs; + Vector outputs; std::unique_ptr anonymous_attribute_relations_; /** Leave the sockets in place, even if they don't match the declaration. Used for dynamic @@ -490,10 +543,14 @@ class NodeDeclaration { * available again in the future. */ bool skip_updating_sockets = false; + /** Use order of socket declarations for socket order instead of conventional + * outputs | buttons | inputs order. Panels are only supported when using custom socket order. */ + bool use_custom_socket_order = false; + friend NodeDeclarationBuilder; bool matches(const bNode &node) const; - Span sockets(eNodeSocketInOut in_out) const; + Span sockets(eNodeSocketInOut in_out) const; const aal::RelationsInNode *anonymous_attribute_relations() const { @@ -732,8 +789,8 @@ inline typename DeclType::Builder &NodeDeclarationBuilder::add_socket(StringRef static_assert(std::is_base_of_v); using Builder = typename DeclType::Builder; - Vector &declarations = in_out == SOCK_IN ? declaration_.inputs : - declaration_.outputs; + Vector &declarations = in_out == SOCK_IN ? declaration_.inputs : + declaration_.outputs; std::unique_ptr socket_decl = std::make_unique(); std::unique_ptr socket_decl_builder = std::make_unique(); @@ -743,7 +800,8 @@ inline typename DeclType::Builder &NodeDeclarationBuilder::add_socket(StringRef socket_decl->name = name; socket_decl->identifier = identifier.is_empty() ? name : identifier; socket_decl->in_out = in_out; - socket_decl_builder->index_ = declarations.append_and_get_index(std::move(socket_decl)); + socket_decl_builder->index_ = declarations.append_and_get_index(socket_decl.get()); + declaration_.items.append(std::move(socket_decl)); Builder &socket_decl_builder_ref = *socket_decl_builder; ((in_out == SOCK_IN) ? input_builders_ : output_builders_) .append(std::move(socket_decl_builder)); @@ -756,7 +814,7 @@ inline typename DeclType::Builder &NodeDeclarationBuilder::add_socket(StringRef /** \name #NodeDeclaration Inline Methods * \{ */ -inline Span NodeDeclaration::sockets(eNodeSocketInOut in_out) const +inline Span NodeDeclaration::sockets(eNodeSocketInOut in_out) const { if (in_out == SOCK_IN) { return inputs; diff --git a/source/blender/nodes/NOD_socket.hh b/source/blender/nodes/NOD_socket.hh index 8438a89b635..99219f917f9 100644 --- a/source/blender/nodes/NOD_socket.hh +++ b/source/blender/nodes/NOD_socket.hh @@ -20,6 +20,8 @@ bNodeSocket *node_add_socket_from_template(bNodeTree *ntree, void node_verify_sockets(bNodeTree *ntree, bNode *node, bool do_id_user); +void node_socket_init_default_value_data(eNodeSocketDatatype datatype, int subtype, void **data); +void node_socket_copy_default_value_data(eNodeSocketDatatype datatype, void *to, const void *from); void node_socket_init_default_value(bNodeSocket *sock); void node_socket_copy_default_value(bNodeSocket *to, const bNodeSocket *from); void register_standard_node_socket_types(); diff --git a/source/blender/nodes/NOD_socket_declarations.hh b/source/blender/nodes/NOD_socket_declarations.hh index bb585b654b1..9de0aba344e 100644 --- a/source/blender/nodes/NOD_socket_declarations.hh +++ b/source/blender/nodes/NOD_socket_declarations.hh @@ -270,6 +270,7 @@ class ExtendBuilder : public SocketDeclarationBuilder { class Custom : public SocketDeclaration { public: const char *idname_; + std::function init_socket_fn; bNodeSocket &build(bNodeTree &ntree, bNode &node) const override; bool matches(const bNodeSocket &socket) const override; diff --git a/source/blender/nodes/NOD_socket_search_link.hh b/source/blender/nodes/NOD_socket_search_link.hh index 9209f5af8da..9dda1b4f008 100644 --- a/source/blender/nodes/NOD_socket_search_link.hh +++ b/source/blender/nodes/NOD_socket_search_link.hh @@ -146,6 +146,6 @@ class GatherLinkSearchOpParams { void search_link_ops_for_basic_node(GatherLinkSearchOpParams ¶ms); void search_link_ops_for_declarations(GatherLinkSearchOpParams ¶ms, - Span declarations); + Span declarations); } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_repeat_output.cc b/source/blender/nodes/geometry/nodes/node_geo_repeat_output.cc index 6841e367587..f3e77a1bc87 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_repeat_output.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_repeat_output.cc @@ -96,12 +96,22 @@ void socket_declarations_for_repeat_items(const Span items, { for (const int i : items.index_range()) { const NodeRepeatItem &item = items[i]; - r_declaration.inputs.append(socket_declaration_for_repeat_item(item, SOCK_IN)); - r_declaration.outputs.append( - socket_declaration_for_repeat_item(item, SOCK_OUT, r_declaration.inputs.size() - 1)); + SocketDeclarationPtr input_decl = socket_declaration_for_repeat_item(item, SOCK_IN); + r_declaration.inputs.append(input_decl.get()); + r_declaration.items.append(std::move(input_decl)); + + SocketDeclarationPtr output_decl = socket_declaration_for_repeat_item( + item, SOCK_OUT, r_declaration.inputs.size() - 1); + r_declaration.outputs.append(output_decl.get()); + r_declaration.items.append(std::move(output_decl)); } - r_declaration.inputs.append(decl::create_extend_declaration(SOCK_IN)); - r_declaration.outputs.append(decl::create_extend_declaration(SOCK_OUT)); + SocketDeclarationPtr input_extend_decl = decl::create_extend_declaration(SOCK_IN); + r_declaration.inputs.append(input_extend_decl.get()); + r_declaration.items.append(std::move(input_extend_decl)); + + SocketDeclarationPtr output_extend_decl = decl::create_extend_declaration(SOCK_OUT); + r_declaration.outputs.append(output_extend_decl.get()); + r_declaration.items.append(std::move(output_extend_decl)); } } // namespace blender::nodes namespace blender::nodes::node_geo_repeat_output_cc { diff --git a/source/blender/nodes/geometry/nodes/node_geo_simulation_input.cc b/source/blender/nodes/geometry/nodes/node_geo_simulation_input.cc index d0e9db57d0d..9934cba5f09 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_simulation_input.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_simulation_input.cc @@ -191,7 +191,8 @@ static void node_declare_dynamic(const bNodeTree &node_tree, delta_time->identifier = "Delta Time"; delta_time->name = DATA_("Delta Time"); delta_time->in_out = SOCK_OUT; - r_declaration.outputs.append(std::move(delta_time)); + r_declaration.outputs.append(delta_time.get()); + r_declaration.items.append(std::move(delta_time)); const NodeGeometrySimulationOutput &storage = *static_cast( output_node->storage); diff --git a/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc b/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc index 2b651476696..59b9b326a19 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc @@ -97,11 +97,19 @@ void socket_declarations_for_simulation_items(const Span ite { for (const int i : items.index_range()) { const NodeSimulationItem &item = items[i]; - r_declaration.inputs.append(socket_declaration_for_simulation_item(item, SOCK_IN, i)); - r_declaration.outputs.append(socket_declaration_for_simulation_item(item, SOCK_OUT, i)); + SocketDeclarationPtr input_decl = socket_declaration_for_simulation_item(item, SOCK_IN, i); + SocketDeclarationPtr output_decl = socket_declaration_for_simulation_item(item, SOCK_OUT, i); + r_declaration.inputs.append(input_decl.get()); + r_declaration.items.append(std::move(input_decl)); + r_declaration.outputs.append(output_decl.get()); + r_declaration.items.append(std::move(output_decl)); } - r_declaration.inputs.append(decl::create_extend_declaration(SOCK_IN)); - r_declaration.outputs.append(decl::create_extend_declaration(SOCK_OUT)); + SocketDeclarationPtr input_extend_decl = decl::create_extend_declaration(SOCK_IN); + SocketDeclarationPtr output_extend_decl = decl::create_extend_declaration(SOCK_OUT); + r_declaration.inputs.append(input_extend_decl.get()); + r_declaration.items.append(std::move(input_extend_decl)); + r_declaration.outputs.append(output_extend_decl.get()); + r_declaration.items.append(std::move(output_extend_decl)); } struct SimulationItemsUniqueNameArgs { diff --git a/source/blender/nodes/intern/geometry_nodes_execute.cc b/source/blender/nodes/intern/geometry_nodes_execute.cc index 0018010efc8..5393acc82be 100644 --- a/source/blender/nodes/intern/geometry_nodes_execute.cc +++ b/source/blender/nodes/intern/geometry_nodes_execute.cc @@ -39,10 +39,9 @@ StringRef input_attribute_name_suffix() return "_attribute_name"; } -bool socket_type_has_attribute_toggle(const bNodeSocket &socket) +bool socket_type_has_attribute_toggle(const eNodeSocketDatatype type) { - return ELEM( - socket.type, SOCK_FLOAT, SOCK_VECTOR, SOCK_BOOLEAN, SOCK_RGBA, SOCK_INT, SOCK_ROTATION); + return ELEM(type, SOCK_FLOAT, SOCK_VECTOR, SOCK_BOOLEAN, SOCK_RGBA, SOCK_INT, SOCK_ROTATION); } bool input_has_attribute_toggle(const bNodeTree &node_tree, const int socket_index) @@ -54,13 +53,16 @@ bool input_has_attribute_toggle(const bNodeTree &node_tree, const int socket_ind } std::unique_ptr id_property_create_from_socket( - const bNodeSocket &socket) + const bNodeTreeInterfaceSocket &socket) { - switch (socket.type) { + const StringRefNull identifier = socket.identifier; + const bNodeSocketType *typeinfo = socket.socket_typeinfo(); + const eNodeSocketDatatype type = typeinfo ? eNodeSocketDatatype(typeinfo->type) : SOCK_CUSTOM; + switch (type) { case SOCK_FLOAT: { const bNodeSocketValueFloat *value = static_cast( - socket.default_value); - auto property = bke::idprop::create(socket.identifier, value->value); + socket.socket_data); + auto property = bke::idprop::create(identifier, value->value); IDPropertyUIDataFloat *ui_data = (IDPropertyUIDataFloat *)IDP_ui_data_ensure(property.get()); ui_data->base.rna_subtype = value->subtype; ui_data->soft_min = double(value->min); @@ -70,8 +72,8 @@ std::unique_ptr id_property_create_f } case SOCK_INT: { const bNodeSocketValueInt *value = static_cast( - socket.default_value); - auto property = bke::idprop::create(socket.identifier, value->value); + socket.socket_data); + auto property = bke::idprop::create(identifier, value->value); IDPropertyUIDataInt *ui_data = (IDPropertyUIDataInt *)IDP_ui_data_ensure(property.get()); ui_data->base.rna_subtype = value->subtype; ui_data->soft_min = value->min; @@ -81,9 +83,9 @@ std::unique_ptr id_property_create_f } case SOCK_VECTOR: { const bNodeSocketValueVector *value = static_cast( - socket.default_value); + socket.socket_data); auto property = bke::idprop::create( - socket.identifier, Span{value->value[0], value->value[1], value->value[2]}); + identifier, Span{value->value[0], value->value[1], value->value[2]}); IDPropertyUIDataFloat *ui_data = (IDPropertyUIDataFloat *)IDP_ui_data_ensure(property.get()); ui_data->base.rna_subtype = value->subtype; ui_data->soft_min = double(value->min); @@ -97,9 +99,9 @@ std::unique_ptr id_property_create_f } case SOCK_RGBA: { const bNodeSocketValueRGBA *value = static_cast( - socket.default_value); + socket.socket_data); auto property = bke::idprop::create( - socket.identifier, + identifier, Span{value->value[0], value->value[1], value->value[2], value->value[3]}); IDPropertyUIDataFloat *ui_data = (IDPropertyUIDataFloat *)IDP_ui_data_ensure(property.get()); ui_data->base.rna_subtype = PROP_COLOR; @@ -116,17 +118,17 @@ std::unique_ptr id_property_create_f } case SOCK_BOOLEAN: { const bNodeSocketValueBoolean *value = static_cast( - socket.default_value); - auto property = bke::idprop::create_bool(socket.identifier, value->value); + socket.socket_data); + auto property = bke::idprop::create_bool(identifier, value->value); IDPropertyUIDataBool *ui_data = (IDPropertyUIDataBool *)IDP_ui_data_ensure(property.get()); ui_data->default_value = value->value != 0; return property; } case SOCK_ROTATION: { const bNodeSocketValueRotation *value = static_cast( - socket.default_value); + socket.socket_data); auto property = bke::idprop::create( - socket.identifier, + identifier, Span{value->value_euler[0], value->value_euler[1], value->value_euler[2]}); IDPropertyUIDataFloat *ui_data = reinterpret_cast( IDP_ui_data_ensure(property.get())); @@ -135,8 +137,8 @@ std::unique_ptr id_property_create_f } case SOCK_STRING: { const bNodeSocketValueString *value = static_cast( - socket.default_value); - auto property = bke::idprop::create(socket.identifier, value->value); + socket.socket_data); + auto property = bke::idprop::create(identifier, value->value); IDPropertyUIDataString *ui_data = (IDPropertyUIDataString *)IDP_ui_data_ensure( property.get()); ui_data->default_value = BLI_strdup(value->value); @@ -144,39 +146,46 @@ std::unique_ptr id_property_create_f } case SOCK_OBJECT: { const bNodeSocketValueObject *value = static_cast( - socket.default_value); - auto property = bke::idprop::create(socket.identifier, reinterpret_cast(value->value)); + socket.socket_data); + auto property = bke::idprop::create(identifier, reinterpret_cast(value->value)); IDPropertyUIDataID *ui_data = (IDPropertyUIDataID *)IDP_ui_data_ensure(property.get()); ui_data->id_type = ID_OB; return property; } case SOCK_COLLECTION: { const bNodeSocketValueCollection *value = static_cast( - socket.default_value); - return bke::idprop::create(socket.identifier, reinterpret_cast(value->value)); + socket.socket_data); + return bke::idprop::create(identifier, reinterpret_cast(value->value)); } case SOCK_TEXTURE: { const bNodeSocketValueTexture *value = static_cast( - socket.default_value); - return bke::idprop::create(socket.identifier, reinterpret_cast(value->value)); + socket.socket_data); + return bke::idprop::create(identifier, reinterpret_cast(value->value)); } case SOCK_IMAGE: { const bNodeSocketValueImage *value = static_cast( - socket.default_value); - return bke::idprop::create(socket.identifier, reinterpret_cast(value->value)); + socket.socket_data); + return bke::idprop::create(identifier, reinterpret_cast(value->value)); } case SOCK_MATERIAL: { const bNodeSocketValueMaterial *value = static_cast( - socket.default_value); - return bke::idprop::create(socket.identifier, reinterpret_cast(value->value)); + socket.socket_data); + return bke::idprop::create(identifier, reinterpret_cast(value->value)); } + case SOCK_CUSTOM: + case SOCK_GEOMETRY: + case SOCK_SHADER: + return nullptr; } return nullptr; } -bool id_property_type_matches_socket(const bNodeSocket &socket, const IDProperty &property) +bool id_property_type_matches_socket(const bNodeTreeInterfaceSocket &socket, + const IDProperty &property) { - switch (socket.type) { + const bNodeSocketType *typeinfo = socket.socket_typeinfo(); + const eNodeSocketDatatype type = typeinfo ? eNodeSocketDatatype(typeinfo->type) : SOCK_CUSTOM; + switch (type) { case SOCK_FLOAT: return ELEM(property.type, IDP_FLOAT, IDP_DOUBLE); case SOCK_INT: @@ -198,6 +207,10 @@ bool id_property_type_matches_socket(const bNodeSocket &socket, const IDProperty case SOCK_IMAGE: case SOCK_MATERIAL: return property.type == IDP_ID; + case SOCK_CUSTOM: + case SOCK_GEOMETRY: + case SOCK_SHADER: + return false; } BLI_assert_unreachable(); return false; @@ -322,10 +335,10 @@ static void init_socket_cpp_value_from_property(const IDProperty &property, } std::optional input_attribute_name_get(const IDProperty &props, - const bNodeSocket &io_input) + const bNodeTreeInterfaceSocket &io_input) { IDProperty *use_attribute = IDP_GetPropertyFromGroup( - &props, (io_input.identifier + input_use_attribute_suffix()).c_str()); + &props, (std::string(io_input.identifier) + input_use_attribute_suffix()).c_str()); if (!use_attribute) { return std::nullopt; } @@ -351,20 +364,21 @@ static void initialize_group_input(const bNodeTree &tree, const int input_index, void *r_value) { - const bNodeSocket &io_input = *tree.interface_inputs()[input_index]; - const bNodeSocketType &socket_type = *io_input.typeinfo; - const eNodeSocketDatatype socket_data_type = static_cast(io_input.type); + const bNodeTreeInterfaceSocket &io_input = *tree.interface_inputs()[input_index]; + const bNodeSocketType *typeinfo = io_input.socket_typeinfo(); + const eNodeSocketDatatype socket_data_type = typeinfo ? eNodeSocketDatatype(typeinfo->type) : + SOCK_CUSTOM; if (properties == nullptr) { - socket_type.get_geometry_nodes_cpp_value(io_input, r_value); + typeinfo->get_geometry_nodes_cpp_value(io_input.socket_data, r_value); return; } const IDProperty *property = IDP_GetPropertyFromGroup(properties, io_input.identifier); if (property == nullptr) { - socket_type.get_geometry_nodes_cpp_value(io_input, r_value); + typeinfo->get_geometry_nodes_cpp_value(io_input.socket_data, r_value); return; } if (!id_property_type_matches_socket(io_input, *property)) { - socket_type.get_geometry_nodes_cpp_value(io_input, r_value); + typeinfo->get_geometry_nodes_cpp_value(io_input.socket_data, r_value); return; } @@ -376,9 +390,9 @@ static void initialize_group_input(const bNodeTree &tree, const std::optional attribute_name = input_attribute_name_get(*properties, io_input); if (attribute_name && bke::allow_procedural_attribute_access(*attribute_name)) { fn::GField attribute_field = bke::AttributeFieldInput::Create(*attribute_name, - *socket_type.base_cpp_type); + *typeinfo->base_cpp_type); const auto *value_or_field_cpp_type = fn::ValueOrFieldCPPType::get_from_self( - *socket_type.geometry_nodes_cpp_type); + *typeinfo->geometry_nodes_cpp_type); BLI_assert(value_or_field_cpp_type != nullptr); value_or_field_cpp_type->construct_from_field(r_value, std::move(attribute_field)); } @@ -409,7 +423,7 @@ static MultiValueMap find_output_attributes_to const bNode &output_node = *tree.group_output_node(); MultiValueMap outputs_by_domain; for (const bNodeSocket *socket : output_node.input_sockets().drop_front(1).drop_back(1)) { - if (!socket_type_has_attribute_toggle(*socket)) { + if (!socket_type_has_attribute_toggle(eNodeSocketDatatype(socket->type))) { continue; } @@ -432,7 +446,7 @@ static MultiValueMap find_output_attributes_to BLI_assert(value_or_field_type != nullptr); const fn::GField field = value_or_field_type->as_field(value.get()); - const bNodeSocket *interface_socket = (const bNodeSocket *)BLI_findlink(&tree.outputs, index); + const bNodeTreeInterfaceSocket *interface_socket = tree.interface_outputs()[index]; const eAttrDomain domain = (eAttrDomain)interface_socket->attribute_domain; OutputAttributeInfo output_info; output_info.field = std::move(field); @@ -585,13 +599,16 @@ bke::GeometrySet execute_geometry_nodes_on_geometry( int input_index = -1; for (const int i : btree.interface_inputs().index_range()) { input_index++; - const bNodeSocket &interface_socket = *btree.interface_inputs()[i]; - if (interface_socket.type == SOCK_GEOMETRY && input_index == 0) { + const bNodeTreeInterfaceSocket &interface_socket = *btree.interface_inputs()[i]; + const bNodeSocketType *typeinfo = interface_socket.socket_typeinfo(); + const eNodeSocketDatatype socket_type = typeinfo ? eNodeSocketDatatype(typeinfo->type) : + SOCK_CUSTOM; + if (socket_type == SOCK_GEOMETRY && input_index == 0) { param_inputs[input_index] = &input_geometry; continue; } - const CPPType *type = interface_socket.typeinfo->geometry_nodes_cpp_type; + const CPPType *type = typeinfo->geometry_nodes_cpp_type; BLI_assert(type != nullptr); void *value = allocator.allocate(type->size(), type->alignment()); initialize_group_input(btree, properties, i, value); @@ -651,26 +668,31 @@ void update_input_properties_from_node_tree(const bNodeTree &tree, IDProperty &properties) { tree.ensure_topology_cache(); - const Span tree_inputs = tree.interface_inputs(); + const Span tree_inputs = tree.interface_inputs(); for (const int i : tree_inputs.index_range()) { - const bNodeSocket &socket = *tree_inputs[i]; + const bNodeTreeInterfaceSocket &socket = *tree_inputs[i]; + const StringRefNull socket_identifier = socket.identifier; + const bNodeSocketType *typeinfo = socket.socket_typeinfo(); + const eNodeSocketDatatype socket_type = typeinfo ? eNodeSocketDatatype(typeinfo->type) : + SOCK_CUSTOM; IDProperty *new_prop = nodes::id_property_create_from_socket(socket).release(); if (new_prop == nullptr) { /* Out of the set of supported input sockets, only * geometry sockets aren't added to the modifier. */ - BLI_assert(socket.type == SOCK_GEOMETRY); + BLI_assert(socket_type == SOCK_GEOMETRY); continue; } new_prop->flag |= IDP_FLAG_OVERRIDABLE_LIBRARY; - if (socket.description[0] != '\0') { + if (socket.description && socket.description[0] != '\0') { IDPropertyUIData *ui_data = IDP_ui_data_ensure(new_prop); ui_data->description = BLI_strdup(socket.description); } IDP_AddToGroup(&properties, new_prop); if (old_properties != nullptr) { - const IDProperty *old_prop = IDP_GetPropertyFromGroup(old_properties, socket.identifier); + const IDProperty *old_prop = IDP_GetPropertyFromGroup(old_properties, + socket_identifier.c_str()); if (old_prop != nullptr) { if (nodes::id_property_type_matches_socket(socket, *old_prop)) { /* #IDP_CopyPropertyContent replaces the UI data as well, which we don't (we only @@ -691,9 +713,9 @@ void update_input_properties_from_node_tree(const bNodeTree &tree, } } - if (nodes::socket_type_has_attribute_toggle(socket)) { - const std::string use_attribute_id = socket.identifier + input_use_attribute_suffix(); - const std::string attribute_name_id = socket.identifier + input_attribute_name_suffix(); + if (nodes::socket_type_has_attribute_toggle(eNodeSocketDatatype(socket_type))) { + const std::string use_attribute_id = socket_identifier + input_use_attribute_suffix(); + const std::string attribute_name_id = socket_identifier + input_attribute_name_suffix(); IDPropertyTemplate idprop = {0}; IDProperty *use_attribute_prop = IDP_New( @@ -731,16 +753,20 @@ void update_output_properties_from_node_tree(const bNodeTree &tree, IDProperty &properties) { tree.ensure_topology_cache(); - const Span tree_outputs = tree.interface_outputs(); + const Span tree_outputs = tree.interface_outputs(); for (const int i : tree_outputs.index_range()) { - const bNodeSocket &socket = *tree_outputs[i]; - if (!nodes::socket_type_has_attribute_toggle(socket)) { + const bNodeTreeInterfaceSocket &socket = *tree_outputs[i]; + const StringRefNull socket_identifier = socket.identifier; + const bNodeSocketType *typeinfo = socket.socket_typeinfo(); + const eNodeSocketDatatype socket_type = typeinfo ? eNodeSocketDatatype(typeinfo->type) : + SOCK_CUSTOM; + if (!nodes::socket_type_has_attribute_toggle(socket_type)) { continue; } - const std::string idprop_name = socket.identifier + input_attribute_name_suffix(); + const std::string idprop_name = socket_identifier + input_attribute_name_suffix(); IDProperty *new_prop = IDP_NewStringMaxSize("", idprop_name.c_str(), MAX_NAME); - if (socket.description[0] != '\0') { + if (socket.description && socket.description[0] != '\0') { IDPropertyUIData *ui_data = IDP_ui_data_ensure(new_prop); ui_data->description = BLI_strdup(socket.description); } diff --git a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc index eaf7854798e..45d09c300be 100644 --- a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc +++ b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc @@ -1085,7 +1085,7 @@ static GMutablePointer get_socket_default_value(LinearAllocator<> &allocator, return {}; } void *buffer = allocator.allocate(type->size(), type->alignment()); - typeinfo.get_geometry_nodes_cpp_value(bsocket, buffer); + typeinfo.get_geometry_nodes_cpp_value(bsocket.default_value, buffer); return {type, buffer}; } @@ -2621,9 +2621,10 @@ struct GeometryNodesLazyFunctionGraphBuilder { void build_group_input_node(lf::Graph &lf_graph) { Vector input_cpp_types; - const Span interface_inputs = btree_.interface_inputs(); - for (const bNodeSocket *interface_input : interface_inputs) { - input_cpp_types.append(interface_input->typeinfo->geometry_nodes_cpp_type); + const Span interface_inputs = btree_.interface_inputs(); + for (const bNodeTreeInterfaceSocket *interface_input : interface_inputs) { + const bNodeSocketType *typeinfo = interface_input->socket_typeinfo(); + input_cpp_types.append(typeinfo->geometry_nodes_cpp_type); } /* Create a dummy node for the group inputs. */ @@ -2644,8 +2645,9 @@ struct GeometryNodesLazyFunctionGraphBuilder { { Vector output_cpp_types; auto &debug_info = scope_.construct(); - for (const bNodeSocket *interface_output : btree_.interface_outputs()) { - output_cpp_types.append(interface_output->typeinfo->geometry_nodes_cpp_type); + for (const bNodeTreeInterfaceSocket *interface_output : btree_.interface_outputs()) { + const bNodeSocketType *typeinfo = interface_output->socket_typeinfo(); + output_cpp_types.append(typeinfo->geometry_nodes_cpp_type); debug_info.socket_names.append(interface_output->name); } @@ -2813,8 +2815,9 @@ struct GeometryNodesLazyFunctionGraphBuilder { { Vector output_cpp_types; auto &debug_info = scope_.construct(); - for (const bNodeSocket *interface_input : btree_.interface_outputs()) { - output_cpp_types.append(interface_input->typeinfo->geometry_nodes_cpp_type); + for (const bNodeTreeInterfaceSocket *interface_input : btree_.interface_outputs()) { + const bNodeSocketType *typeinfo = interface_input->socket_typeinfo(); + output_cpp_types.append(typeinfo->geometry_nodes_cpp_type); debug_info.socket_names.append(interface_input->name); } @@ -3542,7 +3545,7 @@ struct GeometryNodesLazyFunctionGraphBuilder { */ lf::DummyNode &build_output_usage_input_node(lf::Graph &lf_graph) { - const Span interface_outputs = btree_.interface_outputs(); + const Span interface_outputs = btree_.interface_outputs(); Vector cpp_types; cpp_types.append_n_times(&CPPType::get(), interface_outputs.size()); @@ -3562,7 +3565,7 @@ struct GeometryNodesLazyFunctionGraphBuilder { */ void build_input_usage_output_node(lf::Graph &lf_graph) { - const Span interface_inputs = btree_.interface_inputs(); + const Span interface_inputs = btree_.interface_inputs(); Vector cpp_types; cpp_types.append_n_times(&CPPType::get(), interface_inputs.size()); @@ -3822,13 +3825,15 @@ const GeometryNodesLazyFunctionGraphInfo *ensure_geometry_nodes_lazy_function_gr return nullptr; } } - for (const bNodeSocket *interface_bsocket : btree.interface_inputs()) { - if (interface_bsocket->typeinfo->geometry_nodes_cpp_type == nullptr) { + for (const bNodeTreeInterfaceSocket *interface_bsocket : btree.interface_inputs()) { + const bNodeSocketType *typeinfo = interface_bsocket->socket_typeinfo(); + if (typeinfo->geometry_nodes_cpp_type == nullptr) { return nullptr; } } - for (const bNodeSocket *interface_bsocket : btree.interface_outputs()) { - if (interface_bsocket->typeinfo->geometry_nodes_cpp_type == nullptr) { + for (const bNodeTreeInterfaceSocket *interface_bsocket : btree.interface_outputs()) { + const bNodeSocketType *typeinfo = interface_bsocket->socket_typeinfo(); + if (typeinfo->geometry_nodes_cpp_type == nullptr) { return nullptr; } } diff --git a/source/blender/nodes/intern/node_common.cc b/source/blender/nodes/intern/node_common.cc index 192537e4c2d..8b5f4e62c24 100644 --- a/source/blender/nodes/intern/node_common.cc +++ b/source/blender/nodes/intern/node_common.cc @@ -25,6 +25,7 @@ #include "BKE_node.hh" #include "BKE_node_runtime.hh" +#include "BKE_node_tree_interface.hh" #include "BKE_node_tree_update.h" #include "RNA_types.hh" @@ -48,6 +49,8 @@ using blender::Stack; using blender::StringRef; using blender::Vector; +namespace node_interface = blender::bke::node_interface; + /* -------------------------------------------------------------------- */ /** \name Node Group * \{ */ @@ -131,13 +134,14 @@ bool nodeGroupPoll(const bNodeTree *nodetree, namespace blender::nodes { -static std::function get_default_id_getter(const bNodeTree &ntree, - const bNodeSocket &io_socket) +static std::function get_default_id_getter( + const bNodeTreeInterface &tree_interface, const bNodeTreeInterfaceSocket &io_socket) { - const int socket_index = io_socket.in_out == SOCK_IN ? BLI_findindex(&ntree.inputs, &io_socket) : - BLI_findindex(&ntree.outputs, &io_socket); + const int item_index = tree_interface.find_item_index(io_socket.item); + BLI_assert(item_index >= 0); + /* Avoid capturing pointers that can become dangling. */ - return [in_out = io_socket.in_out, socket_index](const bNode &node) -> ID * { + return [item_index](const bNode &node) -> ID * { if (node.id == nullptr) { return nullptr; } @@ -145,34 +149,65 @@ static std::function get_default_id_getter(const bNodeT return nullptr; } const bNodeTree &ntree = *reinterpret_cast(node.id); - const bNodeSocket *io_socket = nullptr; - if (in_out == SOCK_IN) { - /* Better be safe than sorry when the underlying node group changed. */ - if (socket_index < ntree.interface_inputs().size()) { - io_socket = ntree.interface_inputs()[socket_index]; - } - } - else { - if (socket_index < ntree.interface_outputs().size()) { - io_socket = ntree.interface_outputs()[socket_index]; - } - } - if (io_socket == nullptr) { + const bNodeTreeInterfaceItem *io_item = ntree.tree_interface.get_item_at_index(item_index); + if (io_item == nullptr || io_item->item_type != NODE_INTERFACE_SOCKET) { return nullptr; } - return *static_cast(io_socket->default_value); + const bNodeTreeInterfaceSocket &io_socket = + node_interface::get_item_as(*io_item); + return *static_cast(io_socket.socket_data); }; } -static SocketDeclarationPtr declaration_for_interface_socket(const bNodeTree &ntree, - const bNodeSocket &io_socket) +static std::function +get_init_socket_fn(const bNodeTreeInterface &interface, const bNodeTreeInterfaceSocket &io_socket) +{ + const int item_index = interface.find_item_index(io_socket.item); + BLI_assert(item_index >= 0); + + /* Avoid capturing pointers that can become dangling. */ + return [item_index](bNode &node, bNodeSocket &socket, const char *data_path) { + if (node.id == nullptr) { + return; + } + if (GS(node.id->name) != ID_NT) { + return; + } + bNodeTree &ntree = *reinterpret_cast(node.id); + const bNodeTreeInterfaceItem *io_item = ntree.tree_interface.get_item_at_index(item_index); + if (io_item == nullptr || io_item->item_type != NODE_INTERFACE_SOCKET) { + return; + } + const bNodeTreeInterfaceSocket &io_socket = + node_interface::get_item_as(*io_item); + bNodeSocketType *typeinfo = io_socket.socket_typeinfo(); + if (typeinfo && typeinfo->interface_init_socket) { + typeinfo->interface_init_socket(&ntree.id, &io_socket, &node, &socket, data_path); + } + }; +} + +/* in_out overrides the socket declaration in/out type (bNodeTreeInterfaceSocket::flag) + * because a node group input is turned into an output socket for group input nodes. */ +static SocketDeclarationPtr declaration_for_interface_socket( + const bNodeTree &ntree, + const bNodeTreeInterfaceSocket &io_socket, + const eNodeSocketInOut in_out) { SocketDeclarationPtr dst; - switch (io_socket.type) { + + bNodeSocketType *typeinfo = nodeSocketTypeFind(io_socket.socket_type); + if (typeinfo == nullptr) { + return dst; + } + + eNodeSocketDatatype datatype = eNodeSocketDatatype(typeinfo->type); + + switch (datatype) { case SOCK_FLOAT: { - const auto &value = *io_socket.default_value_typed(); + const auto &value = node_interface::get_socket_data_as(io_socket); std::unique_ptr decl = std::make_unique(); - decl->subtype = PropertySubType(io_socket.typeinfo->subtype); + decl->subtype = PropertySubType(typeinfo->subtype); decl->default_value = value.value; decl->soft_min_value = value.min; decl->soft_max_value = value.max; @@ -180,9 +215,9 @@ static SocketDeclarationPtr declaration_for_interface_socket(const bNodeTree &nt break; } case SOCK_VECTOR: { - const auto &value = *io_socket.default_value_typed(); + const auto &value = node_interface::get_socket_data_as(io_socket); std::unique_ptr decl = std::make_unique(); - decl->subtype = PropertySubType(io_socket.typeinfo->subtype); + decl->subtype = PropertySubType(typeinfo->subtype); decl->default_value = value.value; decl->soft_min_value = value.min; decl->soft_max_value = value.max; @@ -190,7 +225,7 @@ static SocketDeclarationPtr declaration_for_interface_socket(const bNodeTree &nt break; } case SOCK_RGBA: { - const auto &value = *io_socket.default_value_typed(); + const auto &value = node_interface::get_socket_data_as(io_socket); std::unique_ptr decl = std::make_unique(); decl->default_value = value.value; dst = std::move(decl); @@ -202,23 +237,23 @@ static SocketDeclarationPtr declaration_for_interface_socket(const bNodeTree &nt break; } case SOCK_BOOLEAN: { - const auto &value = *io_socket.default_value_typed(); + const auto &value = node_interface::get_socket_data_as(io_socket); std::unique_ptr decl = std::make_unique(); decl->default_value = value.value; dst = std::move(decl); break; } case SOCK_ROTATION: { - const auto &value = *io_socket.default_value_typed(); + const auto &value = node_interface::get_socket_data_as(io_socket); std::unique_ptr decl = std::make_unique(); decl->default_value = math::EulerXYZ(float3(value.value_euler)); dst = std::move(decl); break; } case SOCK_INT: { - const auto &value = *io_socket.default_value_typed(); + const auto &value = node_interface::get_socket_data_as(io_socket); std::unique_ptr decl = std::make_unique(); - decl->subtype = PropertySubType(io_socket.typeinfo->subtype); + decl->subtype = PropertySubType(typeinfo->subtype); decl->default_value = value.value; decl->soft_min_value = value.min; decl->soft_max_value = value.max; @@ -226,7 +261,7 @@ static SocketDeclarationPtr declaration_for_interface_socket(const bNodeTree &nt break; } case SOCK_STRING: { - const auto &value = *io_socket.default_value_typed(); + const auto &value = node_interface::get_socket_data_as(io_socket); std::unique_ptr decl = std::make_unique(); decl->default_value = value.value; dst = std::move(decl); @@ -234,13 +269,13 @@ static SocketDeclarationPtr declaration_for_interface_socket(const bNodeTree &nt } case SOCK_OBJECT: { auto value = std::make_unique(); - value->default_value_fn = get_default_id_getter(ntree, io_socket); + value->default_value_fn = get_default_id_getter(ntree.tree_interface, io_socket); dst = std::move(value); break; } case SOCK_IMAGE: { auto value = std::make_unique(); - value->default_value_fn = get_default_id_getter(ntree, io_socket); + value->default_value_fn = get_default_id_getter(ntree.tree_interface, io_socket); dst = std::move(value); break; } @@ -249,37 +284,53 @@ static SocketDeclarationPtr declaration_for_interface_socket(const bNodeTree &nt break; case SOCK_COLLECTION: { auto value = std::make_unique(); - value->default_value_fn = get_default_id_getter(ntree, io_socket); + value->default_value_fn = get_default_id_getter(ntree.tree_interface, io_socket); dst = std::move(value); break; } case SOCK_TEXTURE: { auto value = std::make_unique(); - value->default_value_fn = get_default_id_getter(ntree, io_socket); + value->default_value_fn = get_default_id_getter(ntree.tree_interface, io_socket); dst = std::move(value); break; } case SOCK_MATERIAL: { auto value = std::make_unique(); - value->default_value_fn = get_default_id_getter(ntree, io_socket); + value->default_value_fn = get_default_id_getter(ntree.tree_interface, io_socket); dst = std::move(value); break; } case SOCK_CUSTOM: - std::unique_ptr decl = std::make_unique(); - decl->idname_ = io_socket.idname; - dst = std::move(decl); + auto value = std::make_unique(); + value->init_socket_fn = get_init_socket_fn(ntree.tree_interface, io_socket); + dst = std::move(value); break; } dst->name = io_socket.name; dst->identifier = io_socket.identifier; - dst->in_out = eNodeSocketInOut(io_socket.in_out); - dst->description = io_socket.description; + dst->in_out = in_out; + dst->description = io_socket.description ? io_socket.description : ""; dst->hide_value = io_socket.flag & SOCK_HIDE_VALUE; dst->compact = io_socket.flag & SOCK_COMPACT; return dst; } +static PanelDeclarationPtr declaration_for_interface_panel(const bNodeTree & /*ntree*/, + const bNodeTreeInterfacePanel &io_panel) +{ + if (io_panel.items_num == 0) { + return nullptr; + } + + PanelDeclarationPtr dst = std::make_unique(); + dst->identifier = io_panel.identifier; + dst->name = io_panel.name ? io_panel.name : ""; + dst->description = io_panel.description ? io_panel.description : ""; + dst->default_collapsed = (io_panel.flag & NODE_INTERFACE_PANEL_DEFAULT_CLOSED); + dst->num_items = io_panel.items_num; + return dst; +} + void node_group_declare_dynamic(const bNodeTree & /*node_tree*/, const bNode &node, NodeDeclaration &r_declaration) @@ -294,12 +345,41 @@ void node_group_declare_dynamic(const bNodeTree & /*node_tree*/, } r_declaration.skip_updating_sockets = false; - LISTBASE_FOREACH (const bNodeSocket *, input, &group->inputs) { - r_declaration.inputs.append(declaration_for_interface_socket(*group, *input)); - } - LISTBASE_FOREACH (const bNodeSocket *, output, &group->outputs) { - r_declaration.outputs.append(declaration_for_interface_socket(*group, *output)); - } + /* Allow the node group interface to define the socket order. */ + r_declaration.use_custom_socket_order = true; + + group->tree_interface.foreach_item([&](const bNodeTreeInterfaceItem &item) { + switch (item.item_type) { + case NODE_INTERFACE_SOCKET: { + const bNodeTreeInterfaceSocket &socket = + node_interface::get_item_as(item); + if (socket.flag & NODE_INTERFACE_SOCKET_OUTPUT) { + if (SocketDeclarationPtr socket_decl = declaration_for_interface_socket( + *group, socket, SOCK_OUT)) { + r_declaration.outputs.append(socket_decl.get()); + r_declaration.items.append(std::move(socket_decl)); + } + } + if (socket.flag & NODE_INTERFACE_SOCKET_INPUT) { + if (SocketDeclarationPtr socket_decl = declaration_for_interface_socket( + *group, socket, SOCK_IN)) { + r_declaration.inputs.append(socket_decl.get()); + r_declaration.items.append(std::move(socket_decl)); + } + } + break; + } + case NODE_INTERFACE_PANEL: { + const bNodeTreeInterfacePanel &panel = + node_interface::get_item_as(item); + if (PanelDeclarationPtr panel_decl = declaration_for_interface_panel(*group, panel)) { + r_declaration.items.append(std::move(panel_decl)); + } + break; + } + } + return true; + }); } } // namespace blender::nodes @@ -502,22 +582,50 @@ static void group_input_declare_dynamic(const bNodeTree &node_tree, const bNode & /*node*/, NodeDeclaration &r_declaration) { - LISTBASE_FOREACH (const bNodeSocket *, input, &node_tree.inputs) { - r_declaration.outputs.append(declaration_for_interface_socket(node_tree, *input)); - r_declaration.outputs.last()->in_out = SOCK_OUT; - } - r_declaration.outputs.append(decl::create_extend_declaration(SOCK_OUT)); + node_tree.tree_interface.foreach_item([&](const bNodeTreeInterfaceItem &item) { + switch (item.item_type) { + case NODE_INTERFACE_SOCKET: { + const bNodeTreeInterfaceSocket &socket = + node_interface::get_item_as(item); + if (socket.flag & NODE_INTERFACE_SOCKET_INPUT) { + SocketDeclarationPtr socket_decl = declaration_for_interface_socket( + node_tree, socket, SOCK_OUT); + r_declaration.outputs.append(socket_decl.get()); + r_declaration.items.append(std::move(socket_decl)); + } + break; + } + } + return true; + }); + SocketDeclarationPtr extend_decl = decl::create_extend_declaration(SOCK_OUT); + r_declaration.outputs.append(extend_decl.get()); + r_declaration.items.append(std::move(extend_decl)); } static void group_output_declare_dynamic(const bNodeTree &node_tree, const bNode & /*node*/, NodeDeclaration &r_declaration) { - LISTBASE_FOREACH (const bNodeSocket *, input, &node_tree.outputs) { - r_declaration.inputs.append(declaration_for_interface_socket(node_tree, *input)); - r_declaration.inputs.last()->in_out = SOCK_IN; - } - r_declaration.inputs.append(decl::create_extend_declaration(SOCK_IN)); + node_tree.tree_interface.foreach_item([&](const bNodeTreeInterfaceItem &item) { + switch (item.item_type) { + case NODE_INTERFACE_SOCKET: { + const bNodeTreeInterfaceSocket &socket = + node_interface::get_item_as(item); + if (socket.flag & NODE_INTERFACE_SOCKET_OUTPUT) { + SocketDeclarationPtr socket_decl = declaration_for_interface_socket( + node_tree, socket, SOCK_IN); + r_declaration.inputs.append(socket_decl.get()); + r_declaration.items.append(std::move(socket_decl)); + } + break; + } + } + return true; + }); + SocketDeclarationPtr extend_decl = decl::create_extend_declaration(SOCK_IN); + r_declaration.inputs.append(extend_decl.get()); + r_declaration.items.append(std::move(extend_decl)); } static bool group_input_insert_link(bNodeTree *ntree, bNode *node, bNodeLink *link) @@ -531,8 +639,8 @@ static bool group_input_insert_link(bNodeTree *ntree, bNode *node, bNodeLink *li /* Don't connect to other "extend" sockets. */ return false; } - const bNodeSocket *io_socket = blender::bke::ntreeAddSocketInterfaceFromSocket( - ntree, link->tonode, link->tosock); + const bNodeTreeInterfaceSocket *io_socket = node_interface::add_interface_socket_from_node( + *ntree, *link->tonode, *link->tosock); if (!io_socket) { return false; } @@ -552,8 +660,8 @@ static bool group_output_insert_link(bNodeTree *ntree, bNode *node, bNodeLink *l /* Don't connect to other "extend" sockets. */ return false; } - const bNodeSocket *io_socket = blender::bke::ntreeAddSocketInterfaceFromSocket( - ntree, link->fromnode, link->fromsock); + const bNodeTreeInterfaceSocket *io_socket = node_interface::add_interface_socket_from_node( + *ntree, *link->fromnode, *link->fromsock); if (!io_socket) { return false; } diff --git a/source/blender/nodes/intern/node_declaration.cc b/source/blender/nodes/intern/node_declaration.cc index 74aa7b2e3ca..6511a27faa9 100644 --- a/source/blender/nodes/intern/node_declaration.cc +++ b/source/blender/nodes/intern/node_declaration.cc @@ -6,8 +6,11 @@ #include "NOD_socket_declarations.hh" #include "NOD_socket_declarations_geometry.hh" +#include "BLI_utildefines.h" + #include "BKE_geometry_fields.hh" #include "BKE_node.hh" +#include "BKE_node_runtime.hh" namespace blender::nodes { @@ -22,6 +25,7 @@ void build_node_declaration_dynamic(const bNodeTree &node_tree, const bNode &node, NodeDeclaration &r_declaration) { + r_declaration.items.clear(); r_declaration.inputs.clear(); r_declaration.outputs.clear(); node.typeinfo->declare_dynamic(node_tree, node, r_declaration); @@ -45,13 +49,13 @@ void NodeDeclarationBuilder::finalize() Vector geometry_inputs; for (const int i : declaration_.inputs.index_range()) { - if (dynamic_cast(declaration_.inputs[i].get())) { + if (dynamic_cast(declaration_.inputs[i])) { geometry_inputs.append(i); } } Vector geometry_outputs; for (const int i : declaration_.outputs.index_range()) { - if (dynamic_cast(declaration_.outputs[i].get())) { + if (dynamic_cast(declaration_.outputs[i])) { geometry_outputs.append(i); } } @@ -139,25 +143,46 @@ std::ostream &operator<<(std::ostream &stream, const RelationsInNode &relations) bool NodeDeclaration::matches(const bNode &node) const { - auto check_sockets = [&](ListBase sockets, Span socket_decls) { - const int tot_sockets = BLI_listbase_count(&sockets); - if (tot_sockets != socket_decls.size()) { - return false; - } - int i; - LISTBASE_FOREACH_INDEX (const bNodeSocket *, socket, &sockets, i) { - const SocketDeclaration &socket_decl = *socket_decls[i]; - if (!socket_decl.matches(*socket)) { - return false; + const bNodeSocket *current_input = static_cast(node.inputs.first); + const bNodeSocket *current_output = static_cast(node.outputs.first); + const bNodePanelState *current_panel = node.panel_states_array; + for (const ItemDeclarationPtr &item_decl : items) { + if (const SocketDeclaration *socket_decl = dynamic_cast( + item_decl.get())) + { + switch (socket_decl->in_out) { + case SOCK_IN: + if (current_input == nullptr || !socket_decl->matches(*current_input)) { + return false; + } + current_input = current_input->next; + break; + case SOCK_OUT: + if (current_output == nullptr || !socket_decl->matches(*current_output)) { + return false; + } + current_output = current_output->next; + break; } } - return true; - }; - - if (!check_sockets(node.inputs, inputs)) { - return false; + else if (const PanelDeclaration *panel_decl = dynamic_cast( + item_decl.get())) + { + if (!node.panel_states().contains_ptr(current_panel) || !panel_decl->matches(*current_panel)) + { + return false; + } + ++current_panel; + } + else { + /* Unknown item type. */ + BLI_assert_unreachable(); + } } - if (!check_sockets(node.outputs, outputs)) { + /* If items are left over, some were removed from the declaration. */ + if (current_input == nullptr || current_output == nullptr || + !node.panel_states().contains_ptr(current_panel)) + { return false; } return true; @@ -212,6 +237,26 @@ bool SocketDeclaration::matches_common_data(const bNodeSocket &socket) const return true; } +void PanelDeclaration::build(bNodePanelState &panel) const +{ + panel = {0}; + panel.identifier = this->identifier; + SET_FLAG_FROM_TEST(panel.flag, this->default_collapsed, NODE_PANEL_COLLAPSED); +} + +bool PanelDeclaration::matches(const bNodePanelState &panel) const +{ + return panel.identifier == this->identifier; +} + +void PanelDeclaration::update_or_build(const bNodePanelState &old_panel, + bNodePanelState &new_panel) const +{ + build(new_panel); + /* Copy existing state to the new panel */ + SET_FLAG_FROM_TEST(new_panel.flag, old_panel.is_collapsed(), NODE_PANEL_COLLAPSED); +} + namespace implicit_field_inputs { void position(const bNode & /*node*/, void *r_value) diff --git a/source/blender/nodes/intern/node_socket.cc b/source/blender/nodes/intern/node_socket.cc index 64b25cb689a..2470418650a 100644 --- a/source/blender/nodes/intern/node_socket.cc +++ b/source/blender/nodes/intern/node_socket.cc @@ -178,77 +178,167 @@ static void verify_socket_template_list(bNodeTree *ntree, namespace blender::nodes { -static void refresh_socket_list(bNodeTree &ntree, +static void refresh_node_socket(bNodeTree &ntree, bNode &node, - ListBase &sockets, - Span socket_decls, - const bool do_id_user) + const SocketDeclaration &socket_decl, + Vector &old_sockets, + VectorSet &new_sockets) { - Vector old_sockets = sockets; - VectorSet new_sockets; - for (const SocketDeclarationPtr &socket_decl : socket_decls) { - /* Try to find a socket that corresponds to the declaration. */ - bNodeSocket *old_socket_with_same_identifier = nullptr; - for (const int i : old_sockets.index_range()) { - bNodeSocket &old_socket = *old_sockets[i]; - if (old_socket.identifier == socket_decl->identifier) { - old_sockets.remove_and_reorder(i); - old_socket_with_same_identifier = &old_socket; - break; - } + /* Try to find a socket that corresponds to the declaration. */ + bNodeSocket *old_socket_with_same_identifier = nullptr; + for (const int i : old_sockets.index_range()) { + bNodeSocket &old_socket = *old_sockets[i]; + if (old_socket.identifier == socket_decl.identifier) { + old_sockets.remove_and_reorder(i); + old_socket_with_same_identifier = &old_socket; + break; } - bNodeSocket *new_socket = nullptr; - if (old_socket_with_same_identifier == nullptr) { - /* Create a completely new socket. */ - new_socket = &socket_decl->build(ntree, node); + } + bNodeSocket *new_socket = nullptr; + if (old_socket_with_same_identifier == nullptr) { + /* Create a completely new socket. */ + new_socket = &socket_decl.build(ntree, node); + } + else { + STRNCPY(old_socket_with_same_identifier->name, socket_decl.name.c_str()); + if (socket_decl.matches(*old_socket_with_same_identifier)) { + /* The existing socket matches exactly, just use it. */ + new_socket = old_socket_with_same_identifier; } else { - STRNCPY(old_socket_with_same_identifier->name, socket_decl->name.c_str()); - if (socket_decl->matches(*old_socket_with_same_identifier)) { - /* The existing socket matches exactly, just use it. */ - new_socket = old_socket_with_same_identifier; + /* Clear out identifier to avoid name collisions when a new socket is created. */ + old_socket_with_same_identifier->identifier[0] = '\0'; + new_socket = &socket_decl.update_or_build(ntree, node, *old_socket_with_same_identifier); + + if (new_socket == old_socket_with_same_identifier) { + /* The existing socket has been updated, set the correct identifier again. */ + STRNCPY(new_socket->identifier, socket_decl.identifier.c_str()); } else { - /* Clear out identifier to avoid name collisions when a new socket is created. */ - old_socket_with_same_identifier->identifier[0] = '\0'; - new_socket = &socket_decl->update_or_build(ntree, node, *old_socket_with_same_identifier); - - if (new_socket == old_socket_with_same_identifier) { - /* The existing socket has been updated, set the correct identifier again. */ - STRNCPY(new_socket->identifier, socket_decl->identifier.c_str()); - } - else { - /* Move links to new socket with same identifier. */ - LISTBASE_FOREACH (bNodeLink *, link, &ntree.links) { - if (link->fromsock == old_socket_with_same_identifier) { - link->fromsock = new_socket; - } - else if (link->tosock == old_socket_with_same_identifier) { - link->tosock = new_socket; - } + /* Move links to new socket with same identifier. */ + LISTBASE_FOREACH (bNodeLink *, link, &ntree.links) { + if (link->fromsock == old_socket_with_same_identifier) { + link->fromsock = new_socket; } - for (bNodeLink &internal_link : node.runtime->internal_links) { - if (internal_link.fromsock == old_socket_with_same_identifier) { - internal_link.fromsock = new_socket; - } - else if (internal_link.tosock == old_socket_with_same_identifier) { - internal_link.tosock = new_socket; - } + else if (link->tosock == old_socket_with_same_identifier) { + link->tosock = new_socket; + } + } + for (bNodeLink &internal_link : node.runtime->internal_links) { + if (internal_link.fromsock == old_socket_with_same_identifier) { + internal_link.fromsock = new_socket; + } + else if (internal_link.tosock == old_socket_with_same_identifier) { + internal_link.tosock = new_socket; } } } } - new_sockets.add_new(new_socket); - BKE_ntree_update_tag_socket_new(&ntree, new_socket); } - LISTBASE_FOREACH_MUTABLE (bNodeSocket *, old_socket, &sockets) { - if (!new_sockets.contains(old_socket)) { + new_sockets.add_new(new_socket); + BKE_ntree_update_tag_socket_new(&ntree, new_socket); +} + +static void refresh_node_panel(const PanelDeclaration &panel_decl, + Vector &old_panels, + bNodePanelState &new_panel) +{ + /* Try to find a panel that corresponds to the declaration. */ + bNodePanelState *old_panel_with_same_identifier = nullptr; + for (const int i : old_panels.index_range()) { + bNodePanelState &old_panel = old_panels[i]; + if (old_panel.identifier == panel_decl.identifier) { + /* Panel is removed after copying to #new_panel. */ + old_panel_with_same_identifier = &old_panel; + break; + } + } + + if (old_panel_with_same_identifier == nullptr) { + /* Create a completely new panel. */ + panel_decl.build(new_panel); + } + else { + if (panel_decl.matches(*old_panel_with_same_identifier)) { + /* The existing socket matches exactly, just use it. */ + new_panel = *old_panel_with_same_identifier; + } + else { + /* Clear out identifier to avoid name collisions when a new panel is created. */ + old_panel_with_same_identifier->identifier = -1; + panel_decl.update_or_build(*old_panel_with_same_identifier, new_panel); + } + + /* Remove from old panels. */ + const int64_t old_panel_index = old_panel_with_same_identifier - old_panels.begin(); + old_panels.remove_and_reorder(old_panel_index); + } +} + +static void refresh_node_sockets_and_panels(bNodeTree &ntree, + bNode &node, + Span item_decls, + const bool do_id_user) +{ + /* Count panels */ + int new_num_panels = 0; + for (const ItemDeclarationPtr &item_decl : item_decls) { + if (dynamic_cast(item_decl.get())) { + ++new_num_panels; + } + } + Vector old_inputs = node.inputs; + Vector old_outputs = node.outputs; + Vector old_panels = Vector(node.panel_states()); + + /* New panel states buffer. */ + MEM_SAFE_FREE(node.panel_states_array); + node.num_panel_states = new_num_panels; + node.panel_states_array = MEM_cnew_array(new_num_panels, __func__); + + /* Find list of sockets to add, mixture of old and new sockets. */ + VectorSet new_inputs; + VectorSet new_outputs; + bNodePanelState *new_panel = node.panel_states_array; + for (const ItemDeclarationPtr &item_decl : item_decls) { + if (const SocketDeclaration *socket_decl = dynamic_cast( + item_decl.get())) + { + if (socket_decl->in_out == SOCK_IN) { + refresh_node_socket(ntree, node, *socket_decl, old_inputs, new_inputs); + } + else { + refresh_node_socket(ntree, node, *socket_decl, old_outputs, new_outputs); + } + } + else if (const PanelDeclaration *panel_decl = dynamic_cast( + item_decl.get())) + { + refresh_node_panel(*panel_decl, old_panels, *new_panel); + ++new_panel; + } + } + + /* Destroy any remaining sockets that are no longer in the declaration. */ + LISTBASE_FOREACH_MUTABLE (bNodeSocket *, old_socket, &node.inputs) { + if (!new_inputs.contains(old_socket)) { blender::bke::nodeRemoveSocketEx(&ntree, &node, old_socket, do_id_user); } } - BLI_listbase_clear(&sockets); - for (bNodeSocket *socket : new_sockets) { - BLI_addtail(&sockets, socket); + LISTBASE_FOREACH_MUTABLE (bNodeSocket *, old_socket, &node.outputs) { + if (!new_outputs.contains(old_socket)) { + blender::bke::nodeRemoveSocketEx(&ntree, &node, old_socket, do_id_user); + } + } + + /* Clear and reinsert sockets in the new order. */ + BLI_listbase_clear(&node.inputs); + BLI_listbase_clear(&node.outputs); + for (bNodeSocket *socket : new_inputs) { + BLI_addtail(&node.inputs, socket); + } + for (bNodeSocket *socket : new_outputs) { + BLI_addtail(&node.outputs, socket); } } @@ -261,8 +351,7 @@ static void refresh_node(bNodeTree &ntree, return; } if (!node_decl.matches(node)) { - refresh_socket_list(ntree, node, node.inputs, node_decl.inputs, do_id_user); - refresh_socket_list(ntree, node, node.outputs, node_decl.outputs, do_id_user); + refresh_node_sockets_and_panels(ntree, node, node_decl.items, do_id_user); } blender::bke::nodeSocketDeclarationsUpdate(&node); } @@ -306,16 +395,13 @@ void node_verify_sockets(bNodeTree *ntree, bNode *node, bool do_id_user) } } -void node_socket_init_default_value(bNodeSocket *sock) +void node_socket_init_default_value_data(eNodeSocketDatatype datatype, int subtype, void **data) { - int type = sock->typeinfo->type; - int subtype = sock->typeinfo->subtype; - - if (sock->default_value) { - return; /* already initialized */ + if (!data) { + return; } - switch (type) { + switch (datatype) { case SOCK_FLOAT: { bNodeSocketValueFloat *dval = MEM_cnew("node socket value float"); dval->subtype = subtype; @@ -323,7 +409,7 @@ void node_socket_init_default_value(bNodeSocket *sock) dval->min = -FLT_MAX; dval->max = FLT_MAX; - sock->default_value = dval; + *data = dval; break; } case SOCK_INT: { @@ -333,19 +419,19 @@ void node_socket_init_default_value(bNodeSocket *sock) dval->min = INT_MIN; dval->max = INT_MAX; - sock->default_value = dval; + *data = dval; break; } case SOCK_BOOLEAN: { bNodeSocketValueBoolean *dval = MEM_cnew("node socket value bool"); dval->value = false; - sock->default_value = dval; + *data = dval; break; } case SOCK_ROTATION: { bNodeSocketValueRotation *dval = MEM_cnew(__func__); - sock->default_value = dval; + *data = dval; break; } case SOCK_VECTOR: { @@ -356,7 +442,7 @@ void node_socket_init_default_value(bNodeSocket *sock) dval->min = -FLT_MAX; dval->max = FLT_MAX; - sock->default_value = dval; + *data = dval; break; } case SOCK_RGBA: { @@ -364,7 +450,7 @@ void node_socket_init_default_value(bNodeSocket *sock) bNodeSocketValueRGBA *dval = MEM_cnew("node socket value color"); copy_v4_v4(dval->value, default_value); - sock->default_value = dval; + *data = dval; break; } case SOCK_STRING: { @@ -372,21 +458,21 @@ void node_socket_init_default_value(bNodeSocket *sock) dval->subtype = subtype; dval->value[0] = '\0'; - sock->default_value = dval; + *data = dval; break; } case SOCK_OBJECT: { bNodeSocketValueObject *dval = MEM_cnew("node socket value object"); dval->value = nullptr; - sock->default_value = dval; + *data = dval; break; } case SOCK_IMAGE: { bNodeSocketValueImage *dval = MEM_cnew("node socket value image"); dval->value = nullptr; - sock->default_value = dval; + *data = dval; break; } case SOCK_COLLECTION: { @@ -394,7 +480,7 @@ void node_socket_init_default_value(bNodeSocket *sock) "node socket value object"); dval->value = nullptr; - sock->default_value = dval; + *data = dval; break; } case SOCK_TEXTURE: { @@ -402,7 +488,7 @@ void node_socket_init_default_value(bNodeSocket *sock) "node socket value texture"); dval->value = nullptr; - sock->default_value = dval; + *data = dval; break; } case SOCK_MATERIAL: { @@ -410,12 +496,120 @@ void node_socket_init_default_value(bNodeSocket *sock) "node socket value material"); dval->value = nullptr; - sock->default_value = dval; + *data = dval; break; } + + case SOCK_CUSTOM: + case SOCK_GEOMETRY: + case SOCK_SHADER: + break; } } +void node_socket_copy_default_value_data(eNodeSocketDatatype datatype, void *to, const void *from) +{ + if (!to || !from) { + return; + } + + switch (datatype) { + case SOCK_FLOAT: { + bNodeSocketValueFloat *toval = (bNodeSocketValueFloat *)to; + bNodeSocketValueFloat *fromval = (bNodeSocketValueFloat *)from; + *toval = *fromval; + break; + } + case SOCK_INT: { + bNodeSocketValueInt *toval = (bNodeSocketValueInt *)to; + bNodeSocketValueInt *fromval = (bNodeSocketValueInt *)from; + *toval = *fromval; + break; + } + case SOCK_BOOLEAN: { + bNodeSocketValueBoolean *toval = (bNodeSocketValueBoolean *)to; + bNodeSocketValueBoolean *fromval = (bNodeSocketValueBoolean *)from; + *toval = *fromval; + break; + } + case SOCK_VECTOR: { + bNodeSocketValueVector *toval = (bNodeSocketValueVector *)to; + bNodeSocketValueVector *fromval = (bNodeSocketValueVector *)from; + *toval = *fromval; + break; + } + case SOCK_RGBA: { + bNodeSocketValueRGBA *toval = (bNodeSocketValueRGBA *)to; + bNodeSocketValueRGBA *fromval = (bNodeSocketValueRGBA *)from; + *toval = *fromval; + break; + } + case SOCK_ROTATION: { + bNodeSocketValueRotation *toval = (bNodeSocketValueRotation *)to; + bNodeSocketValueRotation *fromval = (bNodeSocketValueRotation *)from; + *toval = *fromval; + break; + } + case SOCK_STRING: { + bNodeSocketValueString *toval = (bNodeSocketValueString *)to; + bNodeSocketValueString *fromval = (bNodeSocketValueString *)from; + *toval = *fromval; + break; + } + case SOCK_OBJECT: { + bNodeSocketValueObject *toval = (bNodeSocketValueObject *)to; + bNodeSocketValueObject *fromval = (bNodeSocketValueObject *)from; + *toval = *fromval; + id_us_plus(&toval->value->id); + break; + } + case SOCK_IMAGE: { + bNodeSocketValueImage *toval = (bNodeSocketValueImage *)to; + bNodeSocketValueImage *fromval = (bNodeSocketValueImage *)from; + *toval = *fromval; + id_us_plus(&toval->value->id); + break; + } + case SOCK_COLLECTION: { + bNodeSocketValueCollection *toval = (bNodeSocketValueCollection *)to; + bNodeSocketValueCollection *fromval = (bNodeSocketValueCollection *)from; + *toval = *fromval; + id_us_plus(&toval->value->id); + break; + } + case SOCK_TEXTURE: { + bNodeSocketValueTexture *toval = (bNodeSocketValueTexture *)to; + bNodeSocketValueTexture *fromval = (bNodeSocketValueTexture *)from; + *toval = *fromval; + id_us_plus(&toval->value->id); + break; + } + case SOCK_MATERIAL: { + bNodeSocketValueMaterial *toval = (bNodeSocketValueMaterial *)to; + bNodeSocketValueMaterial *fromval = (bNodeSocketValueMaterial *)from; + *toval = *fromval; + id_us_plus(&toval->value->id); + break; + } + + case SOCK_CUSTOM: + case SOCK_GEOMETRY: + case SOCK_SHADER: + break; + } +} + +void node_socket_init_default_value(bNodeSocket *sock) +{ + if (sock->default_value) { + return; /* already initialized */ + } + + node_socket_init_default_value_data(eNodeSocketDatatype(sock->typeinfo->type), + PropertySubType(sock->typeinfo->subtype), + &sock->default_value); +} + void node_socket_copy_default_value(bNodeSocket *to, const bNodeSocket *from) { /* sanity check */ @@ -434,114 +628,35 @@ void node_socket_copy_default_value(bNodeSocket *to, const bNodeSocket *from) STRNCPY(to->name, from->label); } - switch (from->typeinfo->type) { - case SOCK_FLOAT: { - bNodeSocketValueFloat *toval = (bNodeSocketValueFloat *)to->default_value; - bNodeSocketValueFloat *fromval = (bNodeSocketValueFloat *)from->default_value; - *toval = *fromval; - break; - } - case SOCK_INT: { - bNodeSocketValueInt *toval = (bNodeSocketValueInt *)to->default_value; - bNodeSocketValueInt *fromval = (bNodeSocketValueInt *)from->default_value; - *toval = *fromval; - break; - } - case SOCK_BOOLEAN: { - bNodeSocketValueBoolean *toval = (bNodeSocketValueBoolean *)to->default_value; - bNodeSocketValueBoolean *fromval = (bNodeSocketValueBoolean *)from->default_value; - *toval = *fromval; - break; - } - case SOCK_VECTOR: { - bNodeSocketValueVector *toval = (bNodeSocketValueVector *)to->default_value; - bNodeSocketValueVector *fromval = (bNodeSocketValueVector *)from->default_value; - *toval = *fromval; - break; - } - case SOCK_RGBA: { - bNodeSocketValueRGBA *toval = (bNodeSocketValueRGBA *)to->default_value; - bNodeSocketValueRGBA *fromval = (bNodeSocketValueRGBA *)from->default_value; - *toval = *fromval; - break; - } - case SOCK_ROTATION: { - bNodeSocketValueRotation *toval = (bNodeSocketValueRotation *)to->default_value; - bNodeSocketValueRotation *fromval = (bNodeSocketValueRotation *)from->default_value; - *toval = *fromval; - break; - } - case SOCK_STRING: { - bNodeSocketValueString *toval = (bNodeSocketValueString *)to->default_value; - bNodeSocketValueString *fromval = (bNodeSocketValueString *)from->default_value; - *toval = *fromval; - break; - } - case SOCK_OBJECT: { - bNodeSocketValueObject *toval = (bNodeSocketValueObject *)to->default_value; - bNodeSocketValueObject *fromval = (bNodeSocketValueObject *)from->default_value; - *toval = *fromval; - id_us_plus(&toval->value->id); - break; - } - case SOCK_IMAGE: { - bNodeSocketValueImage *toval = (bNodeSocketValueImage *)to->default_value; - bNodeSocketValueImage *fromval = (bNodeSocketValueImage *)from->default_value; - *toval = *fromval; - id_us_plus(&toval->value->id); - break; - } - case SOCK_COLLECTION: { - bNodeSocketValueCollection *toval = (bNodeSocketValueCollection *)to->default_value; - bNodeSocketValueCollection *fromval = (bNodeSocketValueCollection *)from->default_value; - *toval = *fromval; - id_us_plus(&toval->value->id); - break; - } - case SOCK_TEXTURE: { - bNodeSocketValueTexture *toval = (bNodeSocketValueTexture *)to->default_value; - bNodeSocketValueTexture *fromval = (bNodeSocketValueTexture *)from->default_value; - *toval = *fromval; - id_us_plus(&toval->value->id); - break; - } - case SOCK_MATERIAL: { - bNodeSocketValueMaterial *toval = (bNodeSocketValueMaterial *)to->default_value; - bNodeSocketValueMaterial *fromval = (bNodeSocketValueMaterial *)from->default_value; - *toval = *fromval; - id_us_plus(&toval->value->id); - break; - } - } + node_socket_copy_default_value_data( + eNodeSocketDatatype(to->typeinfo->type), to->default_value, from->default_value); to->flag |= (from->flag & SOCK_HIDE_VALUE); } -static void standard_node_socket_interface_init_socket(bNodeTree * /*ntree*/, - const bNodeSocket *interface_socket, - bNode * /*node*/, - bNodeSocket *sock, - const char * /*data_path*/) +static void standard_node_socket_interface_init_socket( + ID * /*id*/, + const bNodeTreeInterfaceSocket *interface_socket, + bNode * /*node*/, + bNodeSocket *sock, + const char * /*data_path*/) { /* initialize the type value */ sock->type = sock->typeinfo->type; - /* XXX socket interface 'type' value is not used really, - * but has to match or the copy function will bail out - */ - const_cast(interface_socket)->type = interface_socket->typeinfo->type; - /* copy default_value settings */ - node_socket_copy_default_value(sock, interface_socket); + node_socket_init_default_value_data( + eNodeSocketDatatype(sock->type), sock->typeinfo->subtype, &sock->default_value); + node_socket_copy_default_value_data( + eNodeSocketDatatype(sock->type), sock->default_value, interface_socket->socket_data); } -static void standard_node_socket_interface_from_socket(bNodeTree * /*ntree*/, - bNodeSocket *stemp, +static void standard_node_socket_interface_from_socket(ID * /*id*/, + bNodeTreeInterfaceSocket *iosock, const bNode * /*node*/, const bNodeSocket *sock) { /* initialize settings */ - stemp->type = stemp->typeinfo->type; - node_socket_copy_default_value(stemp, sock); + iosock->init_from_socket_instance(sock); } void ED_init_standard_node_socket_type(bNodeSocketType *); @@ -549,7 +664,7 @@ void ED_init_standard_node_socket_type(bNodeSocketType *); static bNodeSocketType *make_standard_socket_type(int type, int subtype) { const char *socket_idname = nodeStaticSocketType(type, subtype); - const char *interface_idname = nodeStaticSocketInterfaceType(type, subtype); + const char *interface_idname = nodeStaticSocketInterfaceTypeNew(type, subtype); const char *socket_label = nodeStaticSocketLabel(type, subtype); const char *socket_subtype_label = blender::bke::nodeSocketSubTypeLabel(subtype); bNodeSocketType *stype; @@ -626,13 +741,12 @@ static bNodeSocketType *make_socket_type_bool() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_BOOLEAN, PROP_NONE); socktype->base_cpp_type = &blender::CPPType::get(); - socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { - *(bool *)r_value = ((bNodeSocketValueBoolean *)socket.default_value)->value; + socktype->get_base_cpp_value = [](const void *socket_value, void *r_value) { + *(bool *)r_value = ((bNodeSocketValueBoolean *)socket_value)->value; }; socktype->geometry_nodes_cpp_type = &blender::CPPType::get>(); - socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) { - bool value; - socket.typeinfo->get_base_cpp_value(socket, &value); + socktype->get_geometry_nodes_cpp_value = [](const void *socket_value, void *r_value) { + const bool value = ((bNodeSocketValueBoolean *)socket_value)->value; new (r_value) ValueOrField(value); }; return socktype; @@ -642,15 +756,16 @@ static bNodeSocketType *make_socket_type_rotation() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_ROTATION, PROP_NONE); socktype->base_cpp_type = &blender::CPPType::get(); - socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { - const auto &value = *socket.default_value_typed(); - const math::EulerXYZ euler(float3(value.value_euler)); + socktype->get_base_cpp_value = [](const void *socket_value, void *r_value) { + const auto &typed_value = *(bNodeSocketValueRotation *)socket_value; + const math::EulerXYZ euler(float3(typed_value.value_euler)); *static_cast(r_value) = math::to_quaternion(euler); }; socktype->geometry_nodes_cpp_type = &blender::CPPType::get>(); - socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) { - math::Quaternion value; - socket.typeinfo->get_base_cpp_value(socket, &value); + socktype->get_geometry_nodes_cpp_value = [](const void *socket_value, void *r_value) { + const auto &typed_value = *(bNodeSocketValueRotation *)socket_value; + const math::EulerXYZ euler(float3(typed_value.value_euler)); + const math::Quaternion value = math::to_quaternion(euler); new (r_value) ValueOrField(value); }; return socktype; @@ -660,13 +775,12 @@ static bNodeSocketType *make_socket_type_float(PropertySubType subtype) { bNodeSocketType *socktype = make_standard_socket_type(SOCK_FLOAT, subtype); socktype->base_cpp_type = &blender::CPPType::get(); - socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { - *(float *)r_value = ((bNodeSocketValueFloat *)socket.default_value)->value; + socktype->get_base_cpp_value = [](const void *socket_value, void *r_value) { + *(float *)r_value = ((bNodeSocketValueFloat *)socket_value)->value; }; socktype->geometry_nodes_cpp_type = &blender::CPPType::get>(); - socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) { - float value; - socket.typeinfo->get_base_cpp_value(socket, &value); + socktype->get_geometry_nodes_cpp_value = [](const void *socket_value, void *r_value) { + const float value = ((bNodeSocketValueFloat *)socket_value)->value; new (r_value) ValueOrField(value); }; return socktype; @@ -676,13 +790,12 @@ static bNodeSocketType *make_socket_type_int(PropertySubType subtype) { bNodeSocketType *socktype = make_standard_socket_type(SOCK_INT, subtype); socktype->base_cpp_type = &blender::CPPType::get(); - socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { - *(int *)r_value = ((bNodeSocketValueInt *)socket.default_value)->value; + socktype->get_base_cpp_value = [](const void *socket_value, void *r_value) { + *(int *)r_value = ((bNodeSocketValueInt *)socket_value)->value; }; socktype->geometry_nodes_cpp_type = &blender::CPPType::get>(); - socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) { - int value; - socket.typeinfo->get_base_cpp_value(socket, &value); + socktype->get_geometry_nodes_cpp_value = [](const void *socket_value, void *r_value) { + const int value = ((bNodeSocketValueInt *)socket_value)->value; new (r_value) ValueOrField(value); }; return socktype; @@ -692,13 +805,12 @@ static bNodeSocketType *make_socket_type_vector(PropertySubType subtype) { bNodeSocketType *socktype = make_standard_socket_type(SOCK_VECTOR, subtype); socktype->base_cpp_type = &blender::CPPType::get(); - socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { - *(blender::float3 *)r_value = ((bNodeSocketValueVector *)socket.default_value)->value; + socktype->get_base_cpp_value = [](const void *socket_value, void *r_value) { + *(blender::float3 *)r_value = ((bNodeSocketValueVector *)socket_value)->value; }; socktype->geometry_nodes_cpp_type = &blender::CPPType::get>(); - socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) { - blender::float3 value; - socket.typeinfo->get_base_cpp_value(socket, &value); + socktype->get_geometry_nodes_cpp_value = [](const void *socket_value, void *r_value) { + const blender::float3 value = ((bNodeSocketValueVector *)socket_value)->value; new (r_value) ValueOrField(value); }; return socktype; @@ -708,14 +820,13 @@ static bNodeSocketType *make_socket_type_rgba() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_RGBA, PROP_NONE); socktype->base_cpp_type = &blender::CPPType::get(); - socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { - *(blender::ColorGeometry4f *)r_value = ((bNodeSocketValueRGBA *)socket.default_value)->value; + socktype->get_base_cpp_value = [](const void *socket_value, void *r_value) { + *(blender::ColorGeometry4f *)r_value = ((bNodeSocketValueRGBA *)socket_value)->value; }; socktype->geometry_nodes_cpp_type = &blender::CPPType::get>(); - socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) { - blender::ColorGeometry4f value; - socket.typeinfo->get_base_cpp_value(socket, &value); + socktype->get_geometry_nodes_cpp_value = [](const void *socket_value, void *r_value) { + const blender::ColorGeometry4f value = ((bNodeSocketValueRGBA *)socket_value)->value; new (r_value) ValueOrField(value); }; return socktype; @@ -725,14 +836,14 @@ static bNodeSocketType *make_socket_type_string() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_STRING, PROP_NONE); socktype->base_cpp_type = &blender::CPPType::get(); - socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { - new (r_value) std::string(((bNodeSocketValueString *)socket.default_value)->value); + socktype->get_base_cpp_value = [](const void *socket_value, void *r_value) { + new (r_value) std::string(((bNodeSocketValueString *)socket_value)->value); }; socktype->geometry_nodes_cpp_type = &blender::CPPType::get>(); - socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) { + socktype->get_geometry_nodes_cpp_value = [](const void *socket_value, void *r_value) { std::string value; value.~basic_string(); - socket.typeinfo->get_base_cpp_value(socket, &value); + new (&value) std::string(((bNodeSocketValueString *)socket_value)->value); new (r_value) ValueOrField(value); }; return socktype; @@ -742,8 +853,8 @@ static bNodeSocketType *make_socket_type_object() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_OBJECT, PROP_NONE); socktype->base_cpp_type = &blender::CPPType::get(); - socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { - *(Object **)r_value = ((bNodeSocketValueObject *)socket.default_value)->value; + socktype->get_base_cpp_value = [](const void *socket_value, void *r_value) { + *(Object **)r_value = ((bNodeSocketValueObject *)socket_value)->value; }; socktype->geometry_nodes_cpp_type = socktype->base_cpp_type; socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value; @@ -754,7 +865,7 @@ static bNodeSocketType *make_socket_type_geometry() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_GEOMETRY, PROP_NONE); socktype->base_cpp_type = &blender::CPPType::get(); - socktype->get_base_cpp_value = [](const bNodeSocket & /*socket*/, void *r_value) { + socktype->get_base_cpp_value = [](const void * /*socket_value*/, void *r_value) { new (r_value) blender::bke::GeometrySet(); }; socktype->geometry_nodes_cpp_type = socktype->base_cpp_type; @@ -766,8 +877,8 @@ static bNodeSocketType *make_socket_type_collection() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_COLLECTION, PROP_NONE); socktype->base_cpp_type = &blender::CPPType::get(); - socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { - *(Collection **)r_value = ((bNodeSocketValueCollection *)socket.default_value)->value; + socktype->get_base_cpp_value = [](const void *socket_value, void *r_value) { + *(Collection **)r_value = ((bNodeSocketValueCollection *)socket_value)->value; }; socktype->geometry_nodes_cpp_type = socktype->base_cpp_type; socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value; @@ -778,8 +889,8 @@ static bNodeSocketType *make_socket_type_texture() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_TEXTURE, PROP_NONE); socktype->base_cpp_type = &blender::CPPType::get(); - socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { - *(Tex **)r_value = ((bNodeSocketValueTexture *)socket.default_value)->value; + socktype->get_base_cpp_value = [](const void *socket_value, void *r_value) { + *(Tex **)r_value = ((bNodeSocketValueTexture *)socket_value)->value; }; socktype->geometry_nodes_cpp_type = socktype->base_cpp_type; socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value; @@ -790,8 +901,8 @@ static bNodeSocketType *make_socket_type_image() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_IMAGE, PROP_NONE); socktype->base_cpp_type = &blender::CPPType::get(); - socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { - *(Image **)r_value = ((bNodeSocketValueImage *)socket.default_value)->value; + socktype->get_base_cpp_value = [](const void *socket_value, void *r_value) { + *(Image **)r_value = ((bNodeSocketValueImage *)socket_value)->value; }; socktype->geometry_nodes_cpp_type = socktype->base_cpp_type; socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value; @@ -802,8 +913,8 @@ static bNodeSocketType *make_socket_type_material() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_MATERIAL, PROP_NONE); socktype->base_cpp_type = &blender::CPPType::get(); - socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { - *(Material **)r_value = ((bNodeSocketValueMaterial *)socket.default_value)->value; + socktype->get_base_cpp_value = [](const void *socket_value, void *r_value) { + *(Material **)r_value = ((bNodeSocketValueMaterial *)socket_value)->value; }; socktype->geometry_nodes_cpp_type = socktype->base_cpp_type; socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value; diff --git a/source/blender/nodes/intern/node_socket_declarations.cc b/source/blender/nodes/intern/node_socket_declarations.cc index bbe82498df6..c63140a9d2d 100644 --- a/source/blender/nodes/intern/node_socket_declarations.cc +++ b/source/blender/nodes/intern/node_socket_declarations.cc @@ -680,6 +680,9 @@ bNodeSocket &Custom::build(bNodeTree &ntree, bNode &node) const { bNodeSocket &socket = *nodeAddSocket( &ntree, &node, this->in_out, idname_, this->identifier.c_str(), this->name.c_str()); + if (this->init_socket_fn) { + this->init_socket_fn(node, socket, "interface"); + } return socket; } diff --git a/source/blender/nodes/intern/socket_search_link.cc b/source/blender/nodes/intern/socket_search_link.cc index 79eee66a910..0edb8c2589d 100644 --- a/source/blender/nodes/intern/socket_search_link.cc +++ b/source/blender/nodes/intern/socket_search_link.cc @@ -94,7 +94,7 @@ void LinkSearchOpParams::update_and_connect_available_socket(bNode &new_node, } void search_link_ops_for_declarations(GatherLinkSearchOpParams ¶ms, - Span declarations) + Span declarations) { const bNodeType &node_type = params.node_type(); diff --git a/source/blender/windowmanager/WM_types.hh b/source/blender/windowmanager/WM_types.hh index 04ab71f220f..5600054aaf6 100644 --- a/source/blender/windowmanager/WM_types.hh +++ b/source/blender/windowmanager/WM_types.hh @@ -1084,6 +1084,7 @@ enum eWM_DragDataType { WM_DRAG_DATASTACK, WM_DRAG_ASSET_CATALOG, WM_DRAG_GREASE_PENCIL_LAYER, + WM_DRAG_NODE_TREE_INTERFACE, }; enum eWM_DragFlags { @@ -1109,6 +1110,10 @@ struct wmDragAssetCatalog { bUUID drag_catalog_id; }; +typedef struct wmDragNodeTreeInterface { + struct bNodeTreeInterfaceItem *item; +} wmDragNodeTreeInterface; + /** * For some specific cases we support dragging multiple assets (#WM_DRAG_ASSET_LIST). There is no * proper support for dragging multiple items in the `wmDrag`/`wmDrop` API yet, so this is really diff --git a/tests/python/CMakeLists.txt b/tests/python/CMakeLists.txt index 48a923f0d7f..a846721ad9f 100644 --- a/tests/python/CMakeLists.txt +++ b/tests/python/CMakeLists.txt @@ -313,6 +313,15 @@ add_blender_test( --testdir "${TEST_SRC_DIR}/animation" ) +# ------------------------------------------------------------------------------ +# NODE INTERFACE TESTS +add_blender_test( + bl_node_group_interface + --python ${CMAKE_CURRENT_LIST_DIR}/bl_node_group_interface.py + -- + --testdir "${TEST_SRC_DIR}/node_group" +) + # ------------------------------------------------------------------------------ # IO TESTS diff --git a/tests/python/bl_node_group_interface.py b/tests/python/bl_node_group_interface.py new file mode 100644 index 00000000000..c1870b70d84 --- /dev/null +++ b/tests/python/bl_node_group_interface.py @@ -0,0 +1,501 @@ +# SPDX-FileCopyrightText: 2021-2023 Blender Foundation +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import pathlib +import sys +import unittest +import tempfile + +import bpy + +args = None + + +class AbstractNodeGroupInterfaceTest(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.testdir = args.testdir + cls._tempdir = tempfile.TemporaryDirectory() + cls.tempdir = pathlib.Path(cls._tempdir.name) + + def setUp(self): + self.assertTrue(self.testdir.exists(), + 'Test dir {0} should exist'.format(self.testdir)) + + # Make sure we always start with a known-empty file. + bpy.ops.wm.open_mainfile(filepath=str(self.testdir / "empty.blend")) + + def tearDown(self): + self._tempdir.cleanup() + + +class NodeGroupInterfaceTests: + tree_type = None + group_node_type = None + # Tree instance where node groups can be added + main_tree = None + + def make_group(self): + tree = bpy.data.node_groups.new("test", self.tree_type) + return tree + + def make_instance(self, tree): + group_node = self.main_tree.nodes.new(self.group_node_type) + group_node.node_tree = tree + return group_node + + def make_group_and_instance(self): + tree = self.make_group() + group_node = self.make_instance(tree) + return tree, group_node + + # Utility method for generating a non-zero default value. + @staticmethod + def make_default_socket_value(socket_type): + if (socket_type == "NodeSocketBool"): + return True + elif (socket_type == "NodeSocketColor"): + return (.5, 1.0, .3, .7) + elif (socket_type == "NodeSocketFloat"): + return 1.23 + elif (socket_type == "NodeSocketImage"): + return bpy.data.images.new("test", 4, 4) + elif (socket_type == "NodeSocketInt"): + return -6 + elif (socket_type == "NodeSocketMaterial"): + return bpy.data.materials.new("test") + elif (socket_type == "NodeSocketObject"): + return bpy.data.objects.new("test", bpy.data.meshes.new("test")) + elif (socket_type == "NodeSocketRotation"): + return (0.3, 5.0, -42) + elif (socket_type == "NodeSocketString"): + return "Hello World!" + elif (socket_type == "NodeSocketTexture"): + return bpy.data.textures.new("test", 'MAGIC') + elif (socket_type == "NodeSocketVector"): + return (4.0, -1.0, 0.0) + + # Utility method returning a comparator for socket values. + # Not all socket value types are trivially comparable, e.g. colors. + @staticmethod + def make_socket_value_comparator(socket_type): + def cmp_default(test, value, expected): + test.assertEqual(value, expected, f"Value {value} does not match expected value {expected}") + + def cmp_array(test, value, expected): + test.assertSequenceEqual(value[:], expected[:], f"Value {value} does not match expected value {expected}") + + if (socket_type in {"NodeSocketBool", + "NodeSocketFloat", + "NodeSocketImage", + "NodeSocketInt", + "NodeSocketMaterial", + "NodeSocketObject", + "NodeSocketRotation", + "NodeSocketString", + "NodeSocketTexture"}): + return cmp_default + elif (socket_type in {"NodeSocketColor", + "NodeSocketVector"}): + return cmp_array + + def test_empty_nodegroup(self): + tree, group_node = self.make_group_and_instance() + + self.assertFalse(tree.interface.ui_items, "Interface not empty") + self.assertFalse(group_node.inputs) + self.assertFalse(group_node.outputs) + + def do_test_invalid_socket_type(self, socket_type): + tree = self.make_group() + + with self.assertRaises(TypeError): + in0 = tree.interface.new_socket("Input 0", socket_type=socket_type, in_out={'INPUT'}) + self.assertIsNone(in0, f"Socket created for invalid type {socket_type}") + with self.assertRaises(TypeError): + out0 = tree.interface.new_socket("Output 0", socket_type=socket_type, in_out={'OUTPUT'}) + self.assertIsNone(out0, f"Socket created for invalid type {socket_type}") + + def do_test_sockets_in_out(self, socket_type): + tree, group_node = self.make_group_and_instance() + + out0 = tree.interface.new_socket("Output 0", socket_type=socket_type, in_out={'OUTPUT'}) + self.assertIsNotNone(out0, f"Could not create socket of type {socket_type}") + + in0 = tree.interface.new_socket("Input 0", socket_type=socket_type, in_out={'INPUT'}) + self.assertIsNotNone(in0, f"Could not create socket of type {socket_type}") + + in1 = tree.interface.new_socket("Input 1", socket_type=socket_type, in_out={'INPUT'}) + self.assertIsNotNone(in1, f"Could not create socket of type {socket_type}") + + out1 = tree.interface.new_socket("Output 1", socket_type=socket_type, in_out={'OUTPUT'}) + self.assertIsNotNone(out1, f"Could not create socket of type {socket_type}") + + inout0 = tree.interface.new_socket("Input/Output 0", socket_type=socket_type, in_out={'OUTPUT', 'INPUT'}) + self.assertIsNotNone(inout0, f"Could not create socket of type {socket_type}") + + self.assertSequenceEqual([(s.name, s.bl_idname) for s in group_node.inputs], [ + ("Input 0", socket_type), + ("Input 1", socket_type), + ("Input/Output 0", socket_type), + ]) + self.assertSequenceEqual([(s.name, s.bl_idname) for s in group_node.outputs], [ + ("Output 0", socket_type), + ("Output 1", socket_type), + ("Input/Output 0", socket_type), + ]) + + def do_test_user_count(self, value, expected_users): + if (isinstance(value, bpy.types.ID)): + self.assertEqual( + value.users, + expected_users, + f"Socket default value has user count {value.users}, expected {expected_users}") + + def do_test_socket_type(self, socket_type): + default_value = self.make_default_socket_value(socket_type) + compare_value = self.make_socket_value_comparator(socket_type) + + # Create the tree first, add sockets, then create a group instance. + # That way the new instance should reflect the expected default values. + tree = self.make_group() + + in0 = tree.interface.new_socket("Input 0", socket_type=socket_type, in_out={'INPUT'}) + if default_value is not None: + in0.default_value = default_value + out0 = tree.interface.new_socket("Output 0", socket_type=socket_type, in_out={'OUTPUT'}) + self.assertIsNotNone(in0, f"Could not create socket of type {socket_type}") + self.assertIsNotNone(out0, f"Could not create socket of type {socket_type}") + + # Now make a node group instance to check default values. + group_node = self.make_instance(tree) + if compare_value: + compare_value(self, group_node.inputs[0].default_value, in0.default_value) + + # Test ID user count after assigning. + if (hasattr(in0, "default_value")): + # The default value is stored in both the interface and node, it should have 2 users now. + self.do_test_user_count(in0.default_value, 2) + + # Copy sockets + in1 = tree.interface.copy(in0) + out1 = tree.interface.copy(out0) + self.assertIsNotNone(in1, "Could not copy socket") + self.assertIsNotNone(out1, "Could not copy socket") + # User count on default values should increment by 2 after copy, + # one user for the interface and one for the group node instance. + if (hasattr(in1, "default_value")): + self.do_test_user_count(in1.default_value, 4) + + # Classic outputs..inputs socket layout + def do_test_items_order_classic(self, socket_type): + tree, group_node = self.make_group_and_instance() + + tree.interface.new_socket("Output 0", socket_type=socket_type, in_out={'OUTPUT'}) + tree.interface.new_socket("Input 0", socket_type=socket_type, in_out={'INPUT'}) + + self.assertSequenceEqual([(s.name, s.item_type) for s in tree.interface.ui_items], [ + ("Output 0", 'SOCKET'), + ("Input 0", 'SOCKET'), + ]) + self.assertSequenceEqual([s.name for s in group_node.inputs], [ + "Input 0", + ]) + self.assertSequenceEqual([s.name for s in group_node.outputs], [ + "Output 0", + ]) + # XXX currently no panel state access on node instances. + # self.assertFalse(group_node.panels) + + # Mixed sockets and panels + def do_test_items_order_mixed_with_panels(self, socket_type): + tree, group_node = self.make_group_and_instance() + + tree.interface.new_panel("Panel 0") + tree.interface.new_socket("Input 0", socket_type=socket_type, in_out={'INPUT'}) + tree.interface.new_socket("Output 0", socket_type=socket_type, in_out={'OUTPUT'}) + tree.interface.new_panel("Panel 1") + tree.interface.new_socket("Input 1", socket_type=socket_type, in_out={'INPUT'}) + tree.interface.new_panel("Panel 2") + tree.interface.new_socket("Output 1", socket_type=socket_type, in_out={'OUTPUT'}) + tree.interface.new_panel("Panel 3") + + # Panels after sockets + self.assertSequenceEqual([(s.name, s.item_type) for s in tree.interface.ui_items], [ + ("Input 0", 'SOCKET'), + ("Output 0", 'SOCKET'), + ("Input 1", 'SOCKET'), + ("Output 1", 'SOCKET'), + ("Panel 0", 'PANEL'), + ("Panel 1", 'PANEL'), + ("Panel 2", 'PANEL'), + ("Panel 3", 'PANEL'), + ]) + self.assertSequenceEqual([s.name for s in group_node.inputs], [ + "Input 0", + "Input 1", + ]) + self.assertSequenceEqual([s.name for s in group_node.outputs], [ + "Output 0", + "Output 1", + ]) + # XXX currently no panel state access on node instances. + # self.assertSequenceEqual([p.name for p in group_node.panels], [ + # "Panel 0", + # "Panel 1", + # "Panel 2", + # "Panel 3", + # ]) + + def do_test_add(self, socket_type): + tree, group_node = self.make_group_and_instance() + + in0 = tree.interface.new_socket("Input 0", socket_type=socket_type, in_out={'INPUT'}) + self.assertSequenceEqual(tree.interface.ui_items, [in0]) + self.assertSequenceEqual([s.name for s in group_node.inputs], ["Input 0"]) + self.assertSequenceEqual([s.name for s in group_node.outputs], []) + + out0 = tree.interface.new_socket("Output 0", socket_type=socket_type, in_out={'OUTPUT'}) + self.assertSequenceEqual(tree.interface.ui_items, [in0, out0]) + self.assertSequenceEqual([s.name for s in group_node.inputs], ["Input 0"]) + self.assertSequenceEqual([s.name for s in group_node.outputs], ["Output 0"]) + + panel0 = tree.interface.new_panel("Panel 0") + self.assertSequenceEqual(tree.interface.ui_items, [in0, out0, panel0]) + self.assertSequenceEqual([s.name for s in group_node.inputs], ["Input 0"]) + self.assertSequenceEqual([s.name for s in group_node.outputs], ["Output 0"]) + + # Add items to the panel. + in1 = tree.interface.new_socket("Input 1", socket_type=socket_type, in_out={'INPUT'}, parent=panel0) + self.assertSequenceEqual(tree.interface.ui_items, [in0, out0, panel0, in1]) + self.assertSequenceEqual([s.name for s in group_node.inputs], ["Input 0", "Input 1"]) + self.assertSequenceEqual([s.name for s in group_node.outputs], ["Output 0"]) + + out1 = tree.interface.new_socket("Output 1", socket_type=socket_type, in_out={'OUTPUT'}, parent=panel0) + self.assertSequenceEqual(tree.interface.ui_items, [in0, out0, panel0, in1, out1]) + self.assertSequenceEqual([s.name for s in group_node.inputs], ["Input 0", "Input 1"]) + self.assertSequenceEqual([s.name for s in group_node.outputs], ["Output 0", "Output 1"]) + + # Nested panel is not allowed and should return None. + panel1 = tree.interface.new_panel("Panel 1", parent=panel0) + self.assertIsNone(panel1) + self.assertSequenceEqual(tree.interface.ui_items, [in0, out0, panel0, in1, out1]) + self.assertSequenceEqual([s.name for s in group_node.inputs], ["Input 0", "Input 1"]) + self.assertSequenceEqual([s.name for s in group_node.outputs], ["Output 0", "Output 1"]) + + def do_test_remove(self, socket_type): + tree, group_node = self.make_group_and_instance() + + in0 = tree.interface.new_socket("Input 0", socket_type=socket_type, in_out={'INPUT'}) + out0 = tree.interface.new_socket("Output 0", socket_type=socket_type, in_out={'OUTPUT'}) + panel0 = tree.interface.new_panel("Panel 0") + in1 = tree.interface.new_socket("Input 1", socket_type=socket_type, in_out={'INPUT'}, parent=panel0) + out1 = tree.interface.new_socket("Output 1", socket_type=socket_type, in_out={'OUTPUT'}, parent=panel0) + panel1 = tree.interface.new_panel("Panel 1") + in2 = tree.interface.new_socket("Input 2", socket_type=socket_type, in_out={'INPUT'}, parent=panel1) + out2 = tree.interface.new_socket("Output 2", socket_type=socket_type, in_out={'OUTPUT'}, parent=panel1) + panel2 = tree.interface.new_panel("Panel 2") + + self.assertSequenceEqual(tree.interface.ui_items, [in0, out0, panel0, in1, out1, panel1, in2, out2, panel2]) + self.assertSequenceEqual([s.name for s in group_node.inputs], ["Input 0", "Input 1", "Input 2"]) + self.assertSequenceEqual([s.name for s in group_node.outputs], ["Output 0", "Output 1", "Output 2"]) + + # Remove from root panel. + tree.interface.remove(in0) + self.assertSequenceEqual(tree.interface.ui_items, [out0, panel0, in1, out1, panel1, in2, out2, panel2]) + self.assertSequenceEqual([s.name for s in group_node.inputs], ["Input 1", "Input 2"]) + self.assertSequenceEqual([s.name for s in group_node.outputs], ["Output 0", "Output 1", "Output 2"]) + + # Removing a panel should move content to the parent. + tree.interface.remove(panel0) + self.assertSequenceEqual(tree.interface.ui_items, [out0, in1, out1, panel1, in2, out2, panel2]) + self.assertSequenceEqual([s.name for s in group_node.inputs], ["Input 1", "Input 2"]) + self.assertSequenceEqual([s.name for s in group_node.outputs], ["Output 0", "Output 1", "Output 2"]) + + tree.interface.remove(out0) + self.assertSequenceEqual(tree.interface.ui_items, [in1, out1, panel1, in2, out2, panel2]) + self.assertSequenceEqual([s.name for s in group_node.inputs], ["Input 1", "Input 2"]) + self.assertSequenceEqual([s.name for s in group_node.outputs], ["Output 1", "Output 2"]) + + # Remove content from panel + tree.interface.remove(out2) + self.assertSequenceEqual(tree.interface.ui_items, [in1, out1, panel1, in2, panel2]) + self.assertSequenceEqual([s.name for s in group_node.inputs], ["Input 1", "Input 2"]) + self.assertSequenceEqual([s.name for s in group_node.outputs], ["Output 1"]) + + # Remove a panel and its content + tree.interface.remove(panel1, move_content_to_parent=False) + self.assertSequenceEqual(tree.interface.ui_items, [in1, out1, panel2]) + self.assertSequenceEqual([s.name for s in group_node.inputs], ["Input 1"]) + self.assertSequenceEqual([s.name for s in group_node.outputs], ["Output 1"]) + + # Remove empty panel + tree.interface.remove(panel2) + self.assertSequenceEqual(tree.interface.ui_items, [in1, out1]) + self.assertSequenceEqual([s.name for s in group_node.inputs], ["Input 1"]) + self.assertSequenceEqual([s.name for s in group_node.outputs], ["Output 1"]) + + def do_test_move(self, socket_type): + tree, group_node = self.make_group_and_instance() + + in0 = tree.interface.new_socket("Input 0", socket_type=socket_type, in_out={'INPUT'}) + in1 = tree.interface.new_socket("Input 1", socket_type=socket_type, in_out={'INPUT'}, parent=panel0) + out0 = tree.interface.new_socket("Output 0", socket_type=socket_type, in_out={'OUTPUT'}) + out1 = tree.interface.new_socket("Output 1", socket_type=socket_type, in_out={'OUTPUT'}, parent=panel0) + panel0 = tree.interface.new_panel("Panel 0") + panel1 = tree.interface.new_panel("Panel 1") + + +class GeometryNodeGroupInterfaceTest(AbstractNodeGroupInterfaceTest, NodeGroupInterfaceTests): + tree_type = "GeometryNodeTree" + group_node_type = "GeometryNodeGroup" + + def setUp(self): + super().setUp() + self.main_tree = bpy.data.node_groups.new("main", self.tree_type) + + def test_sockets_in_out(self): + self.do_test_sockets_in_out("NodeSocketFloat") + + def test_all_socket_types(self): + self.do_test_invalid_socket_type("INVALID_SOCKET_TYPE_11!1") + self.do_test_socket_type("NodeSocketBool") + self.do_test_socket_type("NodeSocketCollection") + self.do_test_socket_type("NodeSocketColor") + self.do_test_socket_type("NodeSocketFloat") + self.do_test_socket_type("NodeSocketGeometry") + self.do_test_socket_type("NodeSocketImage") + self.do_test_socket_type("NodeSocketInt") + self.do_test_socket_type("NodeSocketMaterial") + self.do_test_socket_type("NodeSocketObject") + self.do_test_socket_type("NodeSocketRotation") + self.do_test_invalid_socket_type("NodeSocketShader") + self.do_test_socket_type("NodeSocketString") + self.do_test_socket_type("NodeSocketTexture") + self.do_test_socket_type("NodeSocketVector") + self.do_test_invalid_socket_type("NodeSocketVirtual") + + def test_items_order_classic(self): + self.do_test_items_order_classic("NodeSocketFloat") + + def test_items_order_mixed_with_panels(self): + self.do_test_items_order_mixed_with_panels("NodeSocketFloat") + + def test_add(self): + self.do_test_add("NodeSocketFloat") + + def test_remove(self): + self.do_test_remove("NodeSocketFloat") + + +class ShaderNodeGroupInterfaceTest(AbstractNodeGroupInterfaceTest, NodeGroupInterfaceTests): + tree_type = "ShaderNodeTree" + group_node_type = "ShaderNodeGroup" + + def setUp(self): + super().setUp() + self.material = bpy.data.materials.new("test") + self.material.use_nodes = True + self.main_tree = self.material.node_tree + + def test_invalid_socket_type(self): + self.do_test_invalid_socket_type("INVALID_SOCKET_TYPE_11!1") + + def test_sockets_in_out(self): + self.do_test_sockets_in_out("NodeSocketFloat") + + def test_all_socket_types(self): + self.do_test_invalid_socket_type("NodeSocketBool") + self.do_test_invalid_socket_type("NodeSocketCollection") + self.do_test_socket_type("NodeSocketColor") + self.do_test_socket_type("NodeSocketFloat") + self.do_test_invalid_socket_type("NodeSocketGeometry") + self.do_test_invalid_socket_type("NodeSocketImage") + self.do_test_invalid_socket_type("NodeSocketInt") + self.do_test_invalid_socket_type("NodeSocketMaterial") + self.do_test_invalid_socket_type("NodeSocketObject") + self.do_test_invalid_socket_type("NodeSocketRotation") + self.do_test_socket_type("NodeSocketShader") + self.do_test_invalid_socket_type("NodeSocketString") + self.do_test_invalid_socket_type("NodeSocketTexture") + self.do_test_socket_type("NodeSocketVector") + self.do_test_invalid_socket_type("NodeSocketVirtual") + + def test_items_order_classic(self): + self.do_test_items_order_classic("NodeSocketFloat") + + def test_items_order_mixed_with_panels(self): + self.do_test_items_order_mixed_with_panels("NodeSocketFloat") + + def test_add(self): + self.do_test_add("NodeSocketFloat") + + def test_remove(self): + self.do_test_remove("NodeSocketFloat") + + +class CompositorNodeGroupInterfaceTest(AbstractNodeGroupInterfaceTest, NodeGroupInterfaceTests): + tree_type = "CompositorNodeTree" + group_node_type = "CompositorNodeGroup" + + def setUp(self): + super().setUp() + self.scene = bpy.data.scenes.new("test") + self.scene.use_nodes = True + self.main_tree = self.scene.node_tree + + def test_invalid_socket_type(self): + self.do_test_invalid_socket_type("INVALID_SOCKET_TYPE_11!1") + + def test_sockets_in_out(self): + self.do_test_sockets_in_out("NodeSocketFloat") + + def test_all_socket_types(self): + self.do_test_invalid_socket_type("NodeSocketBool") + self.do_test_invalid_socket_type("NodeSocketCollection") + self.do_test_socket_type("NodeSocketColor") + self.do_test_socket_type("NodeSocketFloat") + self.do_test_invalid_socket_type("NodeSocketGeometry") + self.do_test_invalid_socket_type("NodeSocketImage") + self.do_test_invalid_socket_type("NodeSocketInt") + self.do_test_invalid_socket_type("NodeSocketMaterial") + self.do_test_invalid_socket_type("NodeSocketObject") + self.do_test_invalid_socket_type("NodeSocketRotation") + self.do_test_invalid_socket_type("NodeSocketShader") + self.do_test_invalid_socket_type("NodeSocketString") + self.do_test_invalid_socket_type("NodeSocketTexture") + self.do_test_socket_type("NodeSocketVector") + self.do_test_invalid_socket_type("NodeSocketVirtual") + + def test_items_order_classic(self): + self.do_test_items_order_classic("NodeSocketFloat") + + def test_items_order_mixed_with_panels(self): + self.do_test_items_order_mixed_with_panels("NodeSocketFloat") + + def test_add(self): + self.do_test_add("NodeSocketFloat") + + def test_remove(self): + self.do_test_remove("NodeSocketFloat") + + +def main(): + global args + import argparse + + if '--' in sys.argv: + argv = [sys.argv[0]] + sys.argv[sys.argv.index('--') + 1:] + else: + argv = sys.argv + + parser = argparse.ArgumentParser() + parser.add_argument('--testdir', required=True, type=pathlib.Path) + args, remaining = parser.parse_known_args(argv) + + unittest.main(argv=remaining) + + +if __name__ == "__main__": + main()