From e071288ab2901f43a5cde82a49c02101cd579aad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Wed, 30 Aug 2023 12:37:21 +0200 Subject: [PATCH] Nodes: Panels integration with blend files and UI Part 3/3 of #109135, #110272 Switch to new node group interfaces and deprecate old DNA and API. This completes support for panels in node drawing and in node group interface declarations in particular. The new node group interface DNA and RNA code has been added in parts 1 and 2 (#110885, #110952) but has not be enabled yet. This commit completes the integration by * enabling the new RNA API * using the new API in UI * read/write new interfaces from blend files * add versioning for backward compatibility * add forward-compatible writing code to reconstruct old interfaces All places accessing node group interface declarations should now be using the new API. A runtime cache has been added that allows simple linear access to socket inputs and outputs even when a panel hierarchy is used. Old DNA has been deprecated and should only be accessed for versioning (inputs/outputs renamed to inputs_legacy/outputs_legacy to catch errors). Versioning code ensures both backward and forward compatibility of existing files. The API for old interfaces is removed. The new API is very similar but is defined on the `ntree.interface` instead of the `ntree` directly. Breaking change notifications and detailed instructions for migrating will be added. A python test has been added for the node group API functions. This includes new functionality such as creating panels and moving items between different levels. This patch does not yet contain panel representations in the modifier UI. This has been tested in a separate branch and will be added with a later PR (#108565). Pull Request: https://projects.blender.org/blender/blender/pulls/111348 --- scripts/modules/bpy_types.py | 6 +- .../startup/bl_operators/geometry_nodes.py | 4 +- scripts/startup/bl_operators/node.py | 109 ++ scripts/startup/bl_ui/space_node.py | 158 +- scripts/templates_py/custom_nodes.py | 45 +- .../blender/blenkernel/BKE_blender_version.h | 2 +- source/blender/blenkernel/BKE_node.h | 39 +- source/blender/blenkernel/BKE_node.hh | 32 +- source/blender/blenkernel/BKE_node_runtime.hh | 80 +- .../blenkernel/BKE_node_tree_interface.hh | 169 +- source/blender/blenkernel/intern/node.cc | 510 +++--- .../blender/blenkernel/intern/node_runtime.cc | 19 +- .../intern/node_tree_anonymous_attributes.cc | 26 +- .../intern/node_tree_field_inferencing.cc | 13 +- .../blenkernel/intern/node_tree_interface.cc | 178 +- .../blenkernel/intern/node_tree_zones.cc | 1 + .../blenloader/intern/versioning_250.cc | 6 +- .../blenloader/intern/versioning_260.cc | 24 +- .../blenloader/intern/versioning_300.cc | 11 +- .../blenloader/intern/versioning_400.cc | 81 + .../realtime_compositor/intern/utilities.cc | 2 +- .../intern/builder/deg_builder_nodes.cc | 10 +- .../intern/builder/deg_builder_relations.cc | 8 +- .../editors/curves/intern/curves_add.cc | 7 +- .../editors/geometry/node_group_operator.cc | 28 +- .../blender/editors/include/UI_interface_c.hh | 2 + .../blender/editors/interface/CMakeLists.txt | 1 + .../interface_template_node_tree_interface.cc | 517 ++++++ .../editors/object/object_relations.cc | 43 +- source/blender/editors/space_node/drawnode.cc | 171 +- .../editors/space_node/link_drag_search.cc | 83 +- .../blender/editors/space_node/node_draw.cc | 730 +++++--- .../blender/editors/space_node/node_edit.cc | 454 ----- .../blender/editors/space_node/node_group.cc | 88 +- .../blender/editors/space_node/node_intern.hh | 23 +- source/blender/editors/space_node/node_ops.cc | 6 - .../editors/space_node/node_relationships.cc | 4 +- .../editors/space_node/node_shader_preview.cc | 3 +- .../editors/space_node/node_templates.cc | 30 +- .../makesdna/DNA_node_tree_interface_types.h | 55 +- source/blender/makesdna/DNA_node_types.h | 40 +- .../blender/makesdna/intern/dna_rename_defs.h | 2 + .../makesrna/intern/rna_node_socket.cc | 1548 +++++------------ .../intern/rna_node_tree_interface.cc | 370 ++-- .../blender/makesrna/intern/rna_nodetree.cc | 277 +-- source/blender/makesrna/intern/rna_ui_api.cc | 9 + source/blender/modifiers/intern/MOD_nodes.cc | 67 +- .../nodes/NOD_geometry_nodes_execute.hh | 11 +- source/blender/nodes/NOD_node_declaration.hh | 74 +- source/blender/nodes/NOD_socket.hh | 2 + .../blender/nodes/NOD_socket_declarations.hh | 1 + .../blender/nodes/NOD_socket_search_link.hh | 2 +- .../geometry/nodes/node_geo_repeat_output.cc | 20 +- .../nodes/node_geo_simulation_input.cc | 3 +- .../nodes/node_geo_simulation_output.cc | 16 +- .../nodes/intern/geometry_nodes_execute.cc | 144 +- .../intern/geometry_nodes_lazy_function.cc | 33 +- source/blender/nodes/intern/node_common.cc | 232 ++- .../blender/nodes/intern/node_declaration.cc | 81 +- source/blender/nodes/intern/node_socket.cc | 545 +++--- .../nodes/intern/node_socket_declarations.cc | 3 + .../nodes/intern/socket_search_link.cc | 2 +- source/blender/windowmanager/WM_types.hh | 5 + tests/python/CMakeLists.txt | 9 + tests/python/bl_node_group_interface.py | 501 ++++++ 65 files changed, 4302 insertions(+), 3473 deletions(-) create mode 100644 source/blender/editors/interface/interface_template_node_tree_interface.cc create mode 100644 tests/python/bl_node_group_interface.py 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()