diff --git a/intern/cycles/kernel/svm/displace.h b/intern/cycles/kernel/svm/displace.h index cca9dc7a238..ba4aface005 100644 --- a/intern/cycles/kernel/svm/displace.h +++ b/intern/cycles/kernel/svm/displace.h @@ -165,7 +165,7 @@ ccl_device_noinline int svm_node_vector_displacement( tangent = normalize(sd->dPdu); } - float3 bitangent = normalize(cross(normal, tangent)); + float3 bitangent = safe_normalize(cross(normal, tangent)); const AttributeDescriptor attr_sign = find_attribute(kg, sd, node.w); if (attr_sign.offset != ATTR_STD_NOT_FOUND) { float sign = primitive_surface_attribute_float(kg, sd, attr_sign, NULL, NULL); diff --git a/scripts/modules/rna_prop_ui.py b/scripts/modules/rna_prop_ui.py index d83123d60d6..5340b0e37da 100644 --- a/scripts/modules/rna_prop_ui.py +++ b/scripts/modules/rna_prop_ui.py @@ -80,6 +80,7 @@ def rna_idprop_ui_create( description=None, overridable=False, subtype=None, + id_type='OBJECT', ): """Create and initialize a custom property with limits, defaults and other settings.""" @@ -91,11 +92,16 @@ def rna_idprop_ui_create( proptype, _ = rna_idprop_value_item_type(default) if (proptype is bool) or (proptype is str): - ui_data = item.id_properties_ui(prop) ui_data.update( description=description, default=default, ) + elif proptype is type(None) or issubclass(proptype, bpy.types.ID): + ui_data.update( + description=description, + default=default, + id_type=id_type, + ) else: if soft_min is None: soft_min = min @@ -156,6 +162,7 @@ def draw(layout, context, context_member, property_type, *, use_edit=True): to_dict = getattr(value, "to_dict", None) to_list = getattr(value, "to_list", None) + is_datablock = value is None or isinstance(value, bpy.types.ID) if to_dict: value = to_dict() @@ -178,6 +185,8 @@ def draw(layout, context, context_member, property_type, *, use_edit=True): props = value_column.operator("wm.properties_edit_value", text="Edit Value") props.data_path = context_member props.property_name = key + elif is_datablock: + value_column.template_ID(rna_item, '["%s"]' % escape_identifier(key), text="") else: value_column.prop(rna_item, '["%s"]' % escape_identifier(key), text="") diff --git a/scripts/startup/bl_operators/geometry_nodes.py b/scripts/startup/bl_operators/geometry_nodes.py index 776604cf397..06dc14c04f6 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"), is_input=True, socket_type='NodeSocketGeometry') + group.interface.new_socket(data_("Geometry"), is_output=True, 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..bc24e84105b 100644 --- a/scripts/startup/bl_operators/node.py +++ b/scripts/startup/bl_operators/node.py @@ -260,6 +260,133 @@ 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 Socket", ""), + ('OUTPUT', "Output Socket", ""), + ('PANEL', "Panel", "")], + default='INPUT', + ) + + socket_type = 'NodeSocketFloat' + + def execute(self, context): + snode = context.space_data + tree = snode.edit_tree + interface = tree.interface + + # Remember index to move the item. + dst_index = interface.active_index + 1 + if self.item_type == 'INPUT': + item = interface.new_socket("Socket", socket_type=self.socket_type, is_input=True) + elif self.item_type == 'OUTPUT': + item = interface.new_socket("Socket", socket_type=self.socket_type, is_output=True) + elif self.item_type == 'PANEL': + item = interface.new_panel("Panel") + else: + return {'CANCELLED'} + + interface.move(item, dst_index) + interface.active = item + + return {'FINISHED'} + + +class NODE_OT_interface_item_copy(NodeInterfaceOperator, Operator): + '''Add a copy of the active item to the interface''' + bl_idname = "node.interface_item_copy" + bl_label = "Copy 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: + interface.copy(item) + interface.active = item + + 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 -= 1 + + return {'FINISHED'} + + +class NODE_OT_interface_item_move(NodeInterfaceOperator, Operator): + '''Move an item up or down in the interface''' + bl_idname = "node.interface_item_move" + bl_label = "Move Item" + bl_options = {'REGISTER', 'UNDO'} + + direction: EnumProperty( + name="Direction", + items=[('UP', "Up", ""), ('DOWN', "Down", "")], + default='UP', + ) + + def execute(self, context): + snode = context.space_data + tree = snode.edit_tree + interface = tree.interface + item = interface.active + + if self.direction == 'UP': + interface.move(item, interface.active_index - 1) + interface.active_index -= 1 + elif self.direction == 'DOWN': + interface.move(item, interface.active_index + 1) + interface.active_index += 1 + + return {'FINISHED'} + + classes = ( NodeSetting, @@ -267,5 +394,9 @@ 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_copy, + NODE_OT_interface_item_remove, + NODE_OT_interface_item_move, NODE_OT_tree_path_parent, ) diff --git a/scripts/startup/bl_operators/wm.py b/scripts/startup/bl_operators/wm.py index bc199836433..b52e36940b1 100644 --- a/scripts/startup/bl_operators/wm.py +++ b/scripts/startup/bl_operators/wm.py @@ -1387,6 +1387,7 @@ rna_custom_property_type_items = ( ('BOOL', "Boolean", "A true or false value"), ('BOOL_ARRAY', "Boolean Array", "An array of true or false values"), ('STRING', "String", "A string value"), + ('DATA_BLOCK', "Data-Block", "A data-block value"), ('PYTHON', "Python", "Edit a Python value directly, for unsupported property types"), ) @@ -1412,6 +1413,9 @@ rna_custom_property_subtype_vector_items = ( ('QUATERNION', "Quaternion Rotation", "Quaternion rotation (affects NLA blending)"), ) +rna_id_type_items = tuple((item.identifier, item.name, item.description, item.icon, item.value) + for item in bpy.types.Action.bl_rna.properties['id_root'].enum_items) + class WM_OT_properties_edit(Operator): """Change a custom property's type, or adjust how it is displayed in the interface""" @@ -1554,6 +1558,14 @@ class WM_OT_properties_edit(Operator): maxlen=1024, ) + # Data-block properties. + + id_type: EnumProperty( + name="ID Type", + items=rna_id_type_items, + default='OBJECT', + ) + # Store the value converted to a string as a fallback for otherwise unsupported types. eval_string: StringProperty( name="Value", @@ -1623,9 +1635,21 @@ class WM_OT_properties_edit(Operator): if is_array: return 'PYTHON' return 'STRING' + elif prop_type == type(None) or issubclass(prop_type, bpy.types.ID): + if is_array: + return 'PYTHON' + return 'DATA_BLOCK' return 'PYTHON' + # For `DATA_BLOCK` types, return the `id_type` or an empty string for non data-block types. + @staticmethod + def get_property_id_type(item, property_name): + ui_data = item.id_properties_ui(property_name) + rna_data = ui_data.as_dict() + # For non `DATA_BLOCK` types, the `id_type` wont exist. + return rna_data.get("id_type", "") + def _init_subtype(self, subtype): self.subtype = subtype or 'NONE' @@ -1664,6 +1688,8 @@ class WM_OT_properties_edit(Operator): self.default_string = rna_data["default"] elif self.property_type in {'BOOL', 'BOOL_ARRAY'}: self.default_bool = self._convert_new_value_array(rna_data["default"], bool, 32) + elif self.property_type == 'DATA_BLOCK': + self.id_type = rna_data["id_type"] if self.property_type in {'FLOAT_ARRAY', 'INT_ARRAY', 'BOOL_ARRAY'}: self.array_length = len(item[name]) @@ -1677,7 +1703,7 @@ class WM_OT_properties_edit(Operator): # When the operator chooses a different type than the original property, # attempt to convert the old value to the new type for continuity and speed. - def _get_converted_value(self, item, name_old, prop_type_new): + def _get_converted_value(self, item, name_old, prop_type_new, id_type_old, id_type_new): if prop_type_new == 'INT': return self._convert_new_value_single(item[name_old], int) elif prop_type_new == 'FLOAT': @@ -1700,6 +1726,14 @@ class WM_OT_properties_edit(Operator): return [False] * self.array_length elif prop_type_new == 'STRING': return self.convert_custom_property_to_string(item, name_old) + elif prop_type_new == 'DATA_BLOCK': + if id_type_old != id_type_new: + return None + old_value = item[name_old] + if not isinstance(old_value, bpy.types.ID): + return None + return old_value + # If all else fails, create an empty string property. That should avoid errors later on anyway. return "" @@ -1761,6 +1795,12 @@ class WM_OT_properties_edit(Operator): default=self.default_string, description=self.description, ) + elif prop_type_new == 'DATA_BLOCK': + ui_data = item.id_properties_ui(name) + ui_data.update( + description=self.description, + id_type=self.id_type, + ) escaped_name = bpy.utils.escape_identifier(name) item.property_overridable_library_set('["%s"]' % escaped_name, self.is_overridable_library) @@ -1824,6 +1864,9 @@ class WM_OT_properties_edit(Operator): prop_type_new = self.property_type self._old_prop_name[:] = [name] + id_type_old = self.get_property_id_type(item, name_old) + id_type_new = self.id_type + if prop_type_new == 'PYTHON': try: new_value = eval(self.eval_string) @@ -1838,7 +1881,7 @@ class WM_OT_properties_edit(Operator): if name_old != name: del item[name_old] else: - new_value = self._get_converted_value(item, name_old, prop_type_new) + new_value = self._get_converted_value(item, name_old, prop_type_new, id_type_old, id_type_new) del item[name_old] item[name] = new_value @@ -1991,6 +2034,8 @@ class WM_OT_properties_edit(Operator): layout.prop(self, "default_bool", index=0) elif self.property_type == 'STRING': layout.prop(self, "default_string") + elif self.property_type == 'DATA_BLOCK': + layout.prop(self, "id_type") if self.property_type == 'PYTHON': layout.prop(self, "eval_string") diff --git a/scripts/startup/bl_ui/space_node.py b/scripts/startup/bl_ui/space_node.py index 1d6c57c5039..a270f4c8bad 100644 --- a/scripts/startup/bl_ui/space_node.py +++ b/scripts/startup/bl_ui/space_node.py @@ -860,22 +860,11 @@ 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) - - if self.layout_type in {'DEFAULT', 'COMPACT'}: - row = layout.row(align=True) - - 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) - - -class NodeTreeInterfacePanel(Panel): +class NODE_PT_node_tree_declaration(Panel): + bl_space_type = 'NODE_EDITOR' + bl_region_type = 'UI' + bl_category = "Group" + bl_label = "Sockets" @classmethod def poll(cls, context): @@ -889,119 +878,58 @@ 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_declaration(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 + add_remove_col.operator_menu_enum("node.interface_item_new", "item_type", icon='ADD', text="") + add_remove_col.operator("node.interface_item_copy", icon='DUPLICATE', text="") + add_remove_col.operator("node.interface_item_remove", icon='REMOVE', 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 = up_down_col.operator("node.interface_item_move", icon='TRIA_UP', text="") props.direction = 'UP' - props = up_down_col.operator("node.tree_socket_move", icon='TRIA_DOWN', text="") - props.in_out = in_out + props = up_down_col.operator("node.interface_item_move", icon='TRIA_DOWN', text="") 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) + active_item = tree.interface.active + if active_item is not None: + if active_item.item_type == 'SOCKET': + layout.prop(active_item, "name") + layout.prop(active_item, "description") + layout.prop(active_item, "is_input", toggle=True) + layout.prop(active_item, "is_output", toggle=True) + layout.prop(active_item, "socket_type") - label_column = layout_split.column(align=True) - label_column.alignment = 'RIGHT' - # Menu to change the socket type. - label_column.label(text="Type") + active_item.draw(context, layout) - 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)), - ) - - 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) - - -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") + # 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 active_item.is_output: + layout.prop(active_item, "attribute_domain") + layout.prop(active_item, "default_attribute_name") + if active_item.item_type == 'PANEL': + layout.prop(active_item, "name") + layout.prop(active_item, "description") + layout.prop(active_item, "default_closed") class NODE_UL_simulation_zone_items(bpy.types.UIList): @@ -1210,6 +1138,7 @@ classes = ( NODE_PT_material_slots, NODE_PT_geometry_node_asset_traits, NODE_PT_node_color_presets, + NODE_PT_node_tree_declaration, NODE_PT_active_node_generic, NODE_PT_active_node_color, NODE_PT_texture_mapping, @@ -1218,9 +1147,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..e1776da0767 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 @@ -52,6 +52,23 @@ class MyCustomSocket(NodeSocket): 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' + + mean_value: bpy.props.FloatProperty(default=10.0) + randomize: bpy.props.BoolProperty(default=False) + + def draw(self, context, layout): + layout.label(text="Here we can display properties of the socket") + layout.prop(self, "mean_value") + layout.prop(self, "randomize") + + def init_socket(self, node, socket, data_path): + print("I am doing the socket thing") + + # Mix-in class for all custom nodes in this tree type. # Defines a poll function to enable instantiation. class MyCustomTreeNode: @@ -163,6 +180,7 @@ node_categories = [ classes = ( MyCustomTree, MyCustomSocket, + MyCustomInterfaceSocket, MyCustomNode, ) diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index d3789a43ca7..b9c625c3ca2 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -116,9 +116,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 = @@ -172,17 +171,19 @@ typedef struct bNodeSocketType { struct PointerRNA *node_ptr, 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; @@ -540,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 * \{ */ @@ -586,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 ccfce8fb405..2cd99d0693d 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" @@ -60,37 +61,6 @@ void ntreeBlendReadLib(BlendLibReader *reader, bNodeTree *ntree); void ntreeBlendReadExpand(BlendExpander *expander, 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..eee28af7388 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,16 @@ class bNodeSocketRuntime : NonCopyable, NonMovable { int index_in_inout_sockets = -1; }; +class bNodePanelRuntime : NonCopyable, NonMovable { + public: + /** + * The 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). + */ + float2 location; +}; + /** * 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 +309,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 +482,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 +546,12 @@ inline blender::Span bNodeTree::nested_node_refs_span() const return {this->nested_node_refs, this->nested_node_refs_num}; } +inline const blender::bke::bNodeTreeInterfaceCache &bNodeTree::interface_cache() const +{ + BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this)); + return this->runtime->interface_cache; +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -693,6 +700,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 +773,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 +874,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..73ee8017bdf 100644 --- a/source/blender/blenkernel/BKE_node_tree_interface.hh +++ b/source/blender/blenkernel/BKE_node_tree_interface.hh @@ -177,12 +177,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 +193,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 67ce4138167..76541f02df7 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, bNodeSocket *sock); + static CLG_LogRef LOG = {"bke.node"}; namespace blender::bke { @@ -129,6 +133,9 @@ 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*/, @@ -138,6 +145,7 @@ static void node_socket_interface_free(bNodeTree * /*ntree*/, 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 +198,10 @@ 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); + /* Legacy inputs/outputs lists may contain unmanaged pointers, don't copy these. */ + BLI_listbase_clear(&ntree_dst->inputs_legacy); + BLI_listbase_clear(&ntree_dst->outputs_legacy); /* copy preview hash */ if (ntree_src->previews && (flag & LIB_ID_COPY_NO_PREVIEW) == 0) { bNodeInstanceHashIterator iter; @@ -295,15 +293,18 @@ 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); + ntree->tree_interface.free_data(); + /* Free legacy interface data */ + LISTBASE_FOREACH_MUTABLE (bNodeSocket *, socket, &ntree->inputs_legacy) { + node_socket_interface_free(ntree, socket, false); + MEM_freeN(socket); } - LISTBASE_FOREACH_MUTABLE (bNodeSocket *, sock, &ntree->outputs) { - node_socket_interface_free(ntree, sock, false); - MEM_freeN(sock); + LISTBASE_FOREACH_MUTABLE (bNodeSocket *, socket, &ntree->outputs_legacy) { + node_socket_interface_free(ntree, socket, false); + MEM_freeN(socket); } + BLI_listbase_clear(&ntree->inputs_legacy); + BLI_listbase_clear(&ntree->outputs_legacy); /* free preview hash */ if (ntree->previews) { @@ -399,12 +400,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, @@ -471,6 +467,117 @@ static ID **node_owner_pointer_get(ID *id) } // namespace blender::bke +namespace blender::bke::forward_compat { + +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); +} + +/* 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 write_interface_as_sockets(BlendWriter *writer, bNodeTree *ntree) +{ + /* Store reference to old sockets before replacing pointers. */ + ListBase old_inputs = ntree->inputs_legacy; + ListBase old_outputs = ntree->outputs_legacy; + BLI_listbase_clear(&ntree->inputs_legacy); + BLI_listbase_clear(&ntree->outputs_legacy); + + /* 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) { + bNodeSocket *iosock = make_socket( + ntree, SOCK_IN, socket->socket_type, socket->name, socket->identifier); + node_socket_copy_default_value_data(eNodeSocketDatatype(iosock->typeinfo->type), + iosock->default_value, + socket->socket_data); + BLI_addtail(&ntree->inputs_legacy, iosock); + } + if (socket->flag & NODE_INTERFACE_SOCKET_OUTPUT) { + bNodeSocket *iosock = make_socket( + ntree, SOCK_OUT, socket->socket_type, socket->name, socket->identifier); + node_socket_copy_default_value_data(eNodeSocketDatatype(iosock->typeinfo->type), + iosock->default_value, + socket->socket_data); + BLI_addtail(&ntree->outputs_legacy, iosock); + } + } + return true; + }); + + /* 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); + } + + /* Clean up temporary inputs/outputs. */ + LISTBASE_FOREACH_MUTABLE (bNodeSocket *, socket, &ntree->inputs_legacy) { + node_socket_interface_free(ntree, socket, false); + MEM_freeN(socket); + } + LISTBASE_FOREACH_MUTABLE (bNodeSocket *, socket, &ntree->outputs_legacy) { + node_socket_interface_free(ntree, socket, false); + MEM_freeN(socket); + } + BLI_listbase_clear(&ntree->inputs_legacy); + BLI_listbase_clear(&ntree->outputs_legacy); + + /* Restore old data */ + ntree->inputs_legacy = old_inputs; + ntree->outputs_legacy = old_outputs; +} + +} // namespace blender::bke::forward_compat + static void write_node_socket_default_value(BlendWriter *writer, bNodeSocket *sock) { if (sock->default_value == nullptr) { @@ -538,19 +645,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); @@ -576,6 +670,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) && @@ -687,14 +783,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_interface_as_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); @@ -783,6 +875,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); @@ -908,16 +1001,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); @@ -1016,8 +1111,7 @@ void ntreeBlendReadLib(BlendLibReader *reader, bNodeTree *ntree) lib_link_node_sockets(reader, &ntree->id, &node->outputs); } - lib_link_node_sockets(reader, &ntree->id, &ntree->inputs); - lib_link_node_sockets(reader, &ntree->id, &ntree->outputs); + ntree->tree_interface.read_lib(reader, &ntree->id); /* Set `node->typeinfo` pointers. This is done in lib linking, after the * first versioning that can change types still without functions that @@ -1113,8 +1207,7 @@ void ntreeBlendReadExpand(BlendExpander *expander, bNodeTree *ntree) expand_node_sockets(expander, &node->outputs); } - expand_node_sockets(expander, &ntree->inputs); - expand_node_sockets(expander, &ntree->outputs); + ntree->tree_interface.read_expand(expander); } static void ntree_blend_read_expand(BlendExpander *expander, ID *id) @@ -1130,12 +1223,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_cache().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_cache().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()); @@ -1376,18 +1470,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; } @@ -1408,13 +1490,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 { @@ -1579,7 +1654,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) { @@ -2019,81 +2094,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; } @@ -2567,6 +2642,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; @@ -3329,6 +3407,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); @@ -3646,152 +3726,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) @@ -3935,7 +3869,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..369e23a7224 100644 --- a/source/blender/blenkernel/intern/node_runtime.cc +++ b/source/blender/blenkernel/intern/node_runtime.cc @@ -24,11 +24,10 @@ 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; + tree_runtime.interface_cache.rebuild(const_cast(ntree.tree_interface)); } static void update_node_vector(const bNodeTree &ntree) @@ -92,6 +91,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 +536,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..d2f5f5331dd 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()], @@ -204,6 +204,8 @@ static AnonymousAttributeInferencingResult analyse_anonymous_attribute_usages( { BLI_assert(!tree.has_available_link_cycle()); + const bNodeTreeInterfaceCache &cache = tree.interface_cache(); + ResourceScope scope; const Array relations_by_node = get_relations_by_node(tree, scope); @@ -211,12 +213,14 @@ static AnonymousAttributeInferencingResult analyse_anonymous_attribute_usages( Vector all_geometry_sources; /* 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) { + for (const int i : cache.inputs.index_range()) { + const bNodeTreeInterfaceSocket &interface_socket = *cache.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 +372,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 +387,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 +454,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. */ - 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) { + tree.ensure_topology_cache(); + + for (const int interface_i : cache.inputs.index_range()) { + const bNodeTreeInterfaceSocket &interface_socket = *cache.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..5a1a6d434f7 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_cache().inputs.index_range()) { + const bNodeTreeInterfaceSocket *group_input = tree.interface_cache().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_cache().inputs.size(), InputSocketFieldType::IsSupported); - new_inferencing_interface->outputs.resize(BLI_listbase_count(&tree.outputs), + new_inferencing_interface->outputs.resize(tree.interface_cache().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 a539069e302..77c92b619cf 100644 --- a/source/blender/blenkernel/intern/node_tree_interface.cc +++ b/source/blender/blenkernel/intern/node_tree_interface.cc @@ -490,6 +490,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; } @@ -523,6 +524,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; } } @@ -551,6 +553,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); @@ -595,6 +598,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]); @@ -848,6 +852,10 @@ bNodeTreeInterfacePanel *bNodeTreeInterfacePanel::find_parent_recursive( void bNodeTreeInterfacePanel::add_item(bNodeTreeInterfaceItem &item) { + /* Are child panels allowed? */ + BLI_assert(item.item_type != NODE_INTERFACE_PANEL || + (flag & NODE_INTERFACE_PANEL_ALLOW_CHILD_PANELS)); + blender::MutableSpan old_items = this->items(); items_num++; items_array = MEM_cnew_array(items_num, __func__); @@ -861,6 +869,10 @@ void bNodeTreeInterfacePanel::add_item(bNodeTreeInterfaceItem &item) void bNodeTreeInterfacePanel::insert_item(bNodeTreeInterfaceItem &item, int position) { + /* Are child panels allowed? */ + BLI_assert(item.item_type != NODE_INTERFACE_PANEL || + (flag & NODE_INTERFACE_PANEL_ALLOW_CHILD_PANELS)); + position = std::min(std::max(position, 0), items_num); blender::MutableSpan old_items = this->items(); @@ -1016,7 +1028,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); @@ -1042,14 +1054,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; } @@ -1062,11 +1079,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); @@ -1148,7 +1173,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) { @@ -1164,13 +1189,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; @@ -1186,6 +1210,8 @@ bNodeTreeInterfaceSocket *bNodeTreeInterface::insert_socket( } bNodeTreeInterfacePanel *bNodeTreeInterface::add_panel(blender::StringRefNull name, + blender::StringRefNull description, + const NodeTreeInterfacePanelFlag flag, bNodeTreeInterfacePanel *parent) { if (parent == nullptr) { @@ -1193,7 +1219,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); } @@ -1201,6 +1232,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) { @@ -1209,7 +1242,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); } @@ -1225,8 +1263,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)); @@ -1246,6 +1287,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); @@ -1295,6 +1343,12 @@ bool bNodeTreeInterface::move_item_to_parent(bNodeTreeInterfaceItem &item, if (parent == nullptr) { return false; } + 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->remove_item(item, false)) { new_parent->insert_item(item, new_position); return true; diff --git a/source/blender/blenkernel/intern/node_tree_zones.cc b/source/blender/blenkernel/intern/node_tree_zones.cc index 175afa47671..cc901d3a8d7 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/readfile.cc b/source/blender/blenloader/intern/readfile.cc index 2b2eecae201..d9fa0287f4a 100644 --- a/source/blender/blenloader/intern/readfile.cc +++ b/source/blender/blenloader/intern/readfile.cc @@ -2061,6 +2061,10 @@ static void direct_link_id_common( } id->lib = current_library; + if (id->lib) { + /* Always fully clear fake user flag for linked data. */ + id->flag &= ~LIB_FAKEUSER; + } id->us = ID_FAKE_USERS(id); id->icon_id = 0; id->newid = nullptr; /* Needed because .blend may have been saved with crap value here... */ diff --git a/source/blender/blenloader/intern/versioning_250.cc b/source/blender/blenloader/intern/versioning_250.cc index d7b168e1bfb..60dcd6e3b90 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 03391812cfe..9edab195562 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 0e6a56ff6f4..1f8e6a1555b 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 7f709336411..d27d0e9d462 100644 --- a/source/blender/blenloader/intern/versioning_400.cc +++ b/source/blender/blenloader/intern/versioning_400.cc @@ -419,6 +419,36 @@ static void version_replace_principled_hair_model(bNodeTree *ntree) } } +static void versioning_convert_node_tree_socket_lists_to_interface(bNodeTree *ntree) +{ + bNodeTreeInterface &tree_interface = ntree->tree_interface; + + LISTBASE_FOREACH (const bNodeSocket *, socket, &ntree->inputs_legacy) { + NodeTreeInterfaceSocketFlag flag = NODE_INTERFACE_SOCKET_INPUT; + SET_FLAG_FROM_TEST(flag, socket->flag & SOCK_HIDE_VALUE, NODE_INTERFACE_SOCKET_HIDE_VALUE); + SET_FLAG_FROM_TEST( + flag, socket->flag & SOCK_HIDE_IN_MODIFIER, NODE_INTERFACE_SOCKET_HIDE_IN_MODIFIER); + bNodeTreeInterfaceSocket *new_socket = tree_interface.add_socket( + socket->name, socket->description, socket->idname, flag, nullptr); + BLI_assert(new_socket != nullptr); + + MEM_SAFE_FREE(new_socket->identifier); + new_socket->identifier = BLI_strdup(socket->identifier); + } + LISTBASE_FOREACH (const bNodeSocket *, socket, &ntree->outputs_legacy) { + NodeTreeInterfaceSocketFlag flag = NODE_INTERFACE_SOCKET_OUTPUT; + SET_FLAG_FROM_TEST(flag, socket->flag & SOCK_HIDE_VALUE, NODE_INTERFACE_SOCKET_HIDE_VALUE); + SET_FLAG_FROM_TEST( + flag, socket->flag & SOCK_HIDE_IN_MODIFIER, NODE_INTERFACE_SOCKET_HIDE_IN_MODIFIER); + bNodeTreeInterfaceSocket *new_socket = tree_interface.add_socket( + socket->name, socket->description, socket->idname, flag, nullptr); + BLI_assert(new_socket != nullptr); + + MEM_SAFE_FREE(new_socket->identifier); + new_socket->identifier = BLI_strdup(socket->identifier); + } +} + void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain) { if (!MAIN_VERSION_FILE_ATLEAST(bmain, 400, 1)) { @@ -576,6 +606,12 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain) } FOREACH_NODETREE_END; + /* Convert old socket lists into new interface items. */ + FOREACH_NODETREE_BEGIN (bmain, ntree, id) { + versioning_convert_node_tree_socket_lists_to_interface(ntree); + } + FOREACH_NODETREE_END; + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { diff --git a/source/blender/blenloader/intern/writefile.cc b/source/blender/blenloader/intern/writefile.cc index 34e44d58ffa..a7d7209d85f 100644 --- a/source/blender/blenloader/intern/writefile.cc +++ b/source/blender/blenloader/intern/writefile.cc @@ -1225,16 +1225,6 @@ static bool write_file_handle(Main *mainvar, * asap afterward. */ id_lib_extern(id_iter); } - else if (ID_FAKE_USERS(id_iter) > 0 && id_iter->asset_data == nullptr) { - /* Even though fake user is not directly editable by the user on linked data, it is a - * common 'work-around' to set it in library files on data-blocks that need to be linked - * but typically do not have an actual real user (e.g. texts, etc.). - * See e.g. #105687 and #103867. - * - * Would be good to find a better solution, but for now consider these as directly linked - * as well. */ - id_lib_extern(id_iter); - } else { id_iter->tag |= LIB_TAG_INDIRECT; id_iter->tag &= ~LIB_TAG_EXTERN; 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 83a5f454d68..329b75148a2 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -1871,6 +1871,9 @@ void DepsgraphNodeBuilder::build_nodetree(bNodeTree *ntree) if (built_map_.checkIsBuiltAndTag(ntree)) { return; } + + ntree->ensure_topology_cache(); + /* nodetree itself */ add_id_node(&ntree->id); /* General parameters. */ @@ -1953,11 +1956,11 @@ void DepsgraphNodeBuilder::build_nodetree(bNodeTree *ntree) } } - LISTBASE_FOREACH (bNodeSocket *, socket, &ntree->inputs) { - build_idproperties(socket->prop); + for (bNodeTreeInterfaceSocket *socket : ntree->interface_cache().inputs) { + build_idproperties(socket->properties); } - LISTBASE_FOREACH (bNodeSocket *, socket, &ntree->outputs) { - build_idproperties(socket->prop); + for (bNodeTreeInterfaceSocket *socket : ntree->interface_cache().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 3c825e66039..e4bd05d8fee 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -2929,11 +2929,11 @@ void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree) } } - LISTBASE_FOREACH (bNodeSocket *, socket, &ntree->inputs) { - build_idproperties(socket->prop); + for (bNodeTreeInterfaceSocket *socket : ntree->interface_cache().inputs) { + build_idproperties(socket->properties); } - LISTBASE_FOREACH (bNodeSocket *, socket, &ntree->outputs) { - build_idproperties(socket->prop); + for (bNodeTreeInterfaceSocket *socket : ntree->interface_cache().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 af56ecc5810..bb421d930a1 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_cache().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_abstract_view.hh b/source/blender/editors/include/UI_abstract_view.hh index b5874199ae7..b981969783c 100644 --- a/source/blender/editors/include/UI_abstract_view.hh +++ b/source/blender/editors/include/UI_abstract_view.hh @@ -188,7 +188,7 @@ class AbstractViewItem { * * \return True if the renaming was successful. */ - virtual bool rename(StringRefNull new_name); + virtual bool rename(const bContext &C, StringRefNull new_name); /** * Get the string that should be used for renaming, typically the item's label. This string will * not be modified, but if the renaming is canceled, the value will be reset to this. @@ -249,7 +249,7 @@ class AbstractViewItem { bool is_renaming() const; void begin_renaming(); void end_renaming(); - void rename_apply(); + void rename_apply(const bContext &C); template static ToType *from_item_handle(uiViewItemHandle *handle); diff --git a/source/blender/editors/include/UI_interface_c.hh b/source/blender/editors/include/UI_interface_c.hh index 76dac42a2a6..f77a080174b 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 uiTemplateNodeTreeDeclaration(struct uiLayout *layout, struct PointerRNA *ptr); + /** * \return: A RNA pointer for the operator properties. */ diff --git a/source/blender/editors/include/UI_tree_view.hh b/source/blender/editors/include/UI_tree_view.hh index 7d5690d33af..c0c85e7a985 100644 --- a/source/blender/editors/include/UI_tree_view.hh +++ b/source/blender/editors/include/UI_tree_view.hh @@ -207,7 +207,7 @@ class AbstractTreeViewItem : public AbstractViewItem, public TreeViewItemContain /** See AbstractViewItem::get_rename_string(). */ /* virtual */ StringRef get_rename_string() const override; /** See AbstractViewItem::rename(). */ - /* virtual */ bool rename(StringRefNull new_name) override; + /* virtual */ bool rename(const bContext &C, StringRefNull new_name) override; /** * Return whether the item can be collapsed. Used to disable collapsing for items with children. diff --git a/source/blender/editors/interface/CMakeLists.txt b/source/blender/editors/interface/CMakeLists.txt index 529ecf59127..a1553caa9c2 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_declaration.cc interface_template_search_menu.cc interface_template_search_operator.cc interface_templates.cc diff --git a/source/blender/editors/interface/interface_template_grease_pencil_layer_tree.cc b/source/blender/editors/interface/interface_template_grease_pencil_layer_tree.cc index fca714d4660..226e65d8b44 100644 --- a/source/blender/editors/interface/interface_template_grease_pencil_layer_tree.cc +++ b/source/blender/editors/interface/interface_template_grease_pencil_layer_tree.cc @@ -200,7 +200,7 @@ class LayerViewItem : public AbstractTreeViewItem { return true; } - bool rename(StringRefNull new_name) override + bool rename(const bContext & /*C*/, StringRefNull new_name) override { grease_pencil_.rename_node(layer_.as_node(), new_name); return true; @@ -309,7 +309,7 @@ class LayerGroupViewItem : public AbstractTreeViewItem { return true; } - bool rename(StringRefNull new_name) override + bool rename(const bContext & /*C*/, StringRefNull new_name) override { grease_pencil_.rename_node(group_.as_node(), new_name); return true; diff --git a/source/blender/editors/interface/interface_template_node_tree_declaration.cc b/source/blender/editors/interface/interface_template_node_tree_declaration.cc new file mode 100644 index 00000000000..e8fc1910d87 --- /dev/null +++ b/source/blender/editors/interface/interface_template_node_tree_declaration.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 { + public: + explicit NodeTreeInterfaceDragController(NodeTreeInterfaceView &view, + bNodeTreeInterfaceItem &item); + virtual ~NodeTreeInterfaceDragController() = default; + + eWM_DragDataType get_drag_type() const; + + void *create_drag_data() const; + + private: + bNodeTreeInterfaceItem &item_; +}; + +class NodeSocketDropTarget : public TreeViewItemDropTarget { + 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; + + private: + bNodeTreeInterfaceSocket &socket_; +}; + +class NodePanelDropTarget : public TreeViewItemDropTarget { + 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; + + private: + bNodeTreeInterfacePanel &panel_; +}; + +class NodeSocketViewItem : public BasicTreeViewItem { + 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; + + private: + bNodeTree &nodetree_; + bNodeTreeInterfaceSocket &socket_; +}; + +class NodePanelViewItem : public BasicTreeViewItem { + 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; + + private: + bNodeTree &nodetree_; + bNodeTreeInterfacePanel &panel_; +}; + +class NodeTreeInterfaceView : public AbstractTreeView { + 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; + } + } + } + } + + private: + bNodeTree &nodetree_; + bNodeTreeInterface &interface_; +}; + +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 uiTemplateNodeTreeDeclaration(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/interface/views/abstract_view_item.cc b/source/blender/editors/interface/views/abstract_view_item.cc index 061414d9adf..0e9759f606f 100644 --- a/source/blender/editors/interface/views/abstract_view_item.cc +++ b/source/blender/editors/interface/views/abstract_view_item.cc @@ -106,7 +106,7 @@ bool AbstractViewItem::supports_renaming() const /* No renaming by default. */ return false; } -bool AbstractViewItem::rename(StringRefNull /*new_name*/) +bool AbstractViewItem::rename(const bContext & /*C*/, StringRefNull /*new_name*/) { /* No renaming by default. */ return false; @@ -138,10 +138,10 @@ void AbstractViewItem::begin_renaming() std::copy(std::begin(initial_str), std::end(initial_str), std::begin(view.get_rename_buffer())); } -void AbstractViewItem::rename_apply() +void AbstractViewItem::rename_apply(const bContext &C) { const AbstractView &view = get_view(); - rename(view.get_rename_buffer().data()); + rename(C, view.get_rename_buffer().data()); end_renaming(); } @@ -179,12 +179,12 @@ static AbstractViewItem *find_item_from_rename_button(const uiBut &rename_but) return nullptr; } -static void rename_button_fn(bContext * /*C*/, void *arg, char * /*origstr*/) +static void rename_button_fn(bContext *C, void *arg, char * /*origstr*/) { const uiBut *rename_but = static_cast(arg); AbstractViewItem *item = find_item_from_rename_button(*rename_but); BLI_assert(item); - item->rename_apply(); + item->rename_apply(*C); } void AbstractViewItem::add_rename_button(uiBlock &block) diff --git a/source/blender/editors/interface/views/tree_view.cc b/source/blender/editors/interface/views/tree_view.cc index fc33f7bff68..f0bc5a246fd 100644 --- a/source/blender/editors/interface/views/tree_view.cc +++ b/source/blender/editors/interface/views/tree_view.cc @@ -410,7 +410,7 @@ StringRef AbstractTreeViewItem::get_rename_string() const return label_; } -bool AbstractTreeViewItem::rename(StringRefNull new_name) +bool AbstractTreeViewItem::rename(const bContext & /*C*/, StringRefNull new_name) { /* It is important to update the label after renaming, so #AbstractTreeViewItem::matches_single() * recognizes the item. (It only compares labels by default.) */ diff --git a/source/blender/editors/object/object_relations.cc b/source/blender/editors/object/object_relations.cc index 2e2e84325de..cdbb1bb5be3 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" @@ -2961,23 +2963,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_cache().inputs.is_empty()) { + const bNodeTreeInterfaceSocket *first_input = tree->interface_cache().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_cache().outputs.is_empty()) { + const bNodeTreeInterfaceSocket *first_output = tree->interface_cache().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_file/asset_catalog_tree_view.cc b/source/blender/editors/space_file/asset_catalog_tree_view.cc index ac2e1e92678..c251cafd53b 100644 --- a/source/blender/editors/space_file/asset_catalog_tree_view.cc +++ b/source/blender/editors/space_file/asset_catalog_tree_view.cc @@ -87,7 +87,7 @@ class AssetCatalogTreeViewItem : public ui::BasicTreeViewItem { void build_context_menu(bContext &C, uiLayout &column) const override; bool supports_renaming() const override; - bool rename(StringRefNull new_name) override; + bool rename(const bContext &C, StringRefNull new_name) override; /** Add drag support for catalog items. */ std::unique_ptr create_drag_controller() const override; @@ -330,10 +330,10 @@ bool AssetCatalogTreeViewItem::supports_renaming() const return !ED_asset_catalogs_read_only(*tree_view.asset_library_); } -bool AssetCatalogTreeViewItem::rename(StringRefNull new_name) +bool AssetCatalogTreeViewItem::rename(const bContext &C, StringRefNull new_name) { /* Important to keep state. */ - BasicTreeViewItem::rename(new_name); + BasicTreeViewItem::rename(C, new_name); const AssetCatalogTreeView &tree_view = static_cast( get_tree_view()); diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc index 2c428cfc9ce..ed801bc7446 100644 --- a/source/blender/editors/space_node/drawnode.cc +++ b/source/blender/editors/space_node/drawnode.cc @@ -1124,23 +1124,14 @@ 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*/) +static void node_socket_undefined_interface_draw(ID * /*id*/, + bNodeTreeInterfaceSocket * /*interface_socket*/, + bContext * /*C*/, + uiLayout *layout) { uiItemL(layout, IFACE_("Undefined Socket Type"), ICON_ERROR); } -static void node_socket_undefined_interface_draw_color(bContext * /*C*/, - PointerRNA * /*ptr*/, - float *r_color) -{ - r_color[0] = 1.0f; - r_color[1] = 0.0f; - r_color[2] = 0.0f; - r_color[3] = 1.0f; -} - /** \} */ } // namespace blender::ed::space_node @@ -1162,7 +1153,6 @@ void ED_node_init_butfuncs() NodeSocketTypeUndefined.draw = node_socket_undefined_draw; NodeSocketTypeUndefined.draw_color = node_socket_undefined_draw_color; 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 +1198,38 @@ static const float std_node_socket_colors[][4] = { {0.92, 0.46, 0.7, 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) -{ - 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[socket_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 +1440,41 @@ 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, "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, "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, "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 +1486,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); } } @@ -1503,9 +1521,8 @@ 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->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) @@ -1563,29 +1580,34 @@ void draw_nodespace_back_pix(const bContext &C, GPU_matrix_push_projection(); GPU_matrix_push(); - /* The draw manager is used to draw the backdrop image. */ + /* The draw manager is used to draw the + * backdrop image. */ GPUFrameBuffer *old_fb = GPU_framebuffer_active_get(); GPU_framebuffer_restore(); BLI_thread_lock(LOCK_DRAW_IMAGE); 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; Image *ima = BKE_image_ensure_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node"); ImBuf *ibuf = BKE_image_acquire_ibuf(ima, nullptr, &lock); if (ibuf) { - /* somehow the offset has to be calculated inverse */ + /* somehow the offset has to be + * calculated inverse */ wmOrtho2_region_pixelspace(®ion); const float offset_x = snode.xof + ima->offset_x * snode.zoom; const float offset_y = snode.yof + ima->offset_y * snode.zoom; 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; @@ -1984,7 +2006,8 @@ static void nodelink_batch_add_link(const SpaceNode &snode, const std::array &points, const NodeLinkDrawConfig &draw_config) { - /* Only allow these colors. If more is needed, you need to modify the shader accordingly. */ + /* Only allow these colors. If more is needed, you need to modify the shader accordingly. + */ BLI_assert( ELEM(draw_config.th_col1, TH_WIRE_INNER, TH_WIRE, TH_ACTIVE, TH_EDGE_SELECT, TH_REDALERT)); BLI_assert( 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 e72de7a93b7..086c5f1365a 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,285 @@ float2 node_from_view(const bNode &node, const float2 &co) ; } +/* 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; + } + + 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); + /* Alignment based on input/output. */ + uiLayoutSetAlignment(row, in_out == SOCK_IN ? UI_LAYOUT_ALIGN_LEFT : 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); + + /* 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(node.declaration() != nullptr); + 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; + + /* Parent panel stack with count of items still to be added. */ + struct PanelUpdate { + int remaining_items; + bool is_collapsed; + }; + + 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; + } + + 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}); + + /* Round the socket location to stop it from jiggling. */ + current_panel_runtime->location = float2(locx, round(locy + NODE_DYS)); + ++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()) { + if (panel_updates.peek().remaining_items > 0) { + /* Incomplete panel, continue adding items. */ + break; + } + /* 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 +637,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 +648,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 (node.declaration()) { + 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; @@ -740,13 +857,14 @@ static void node_socket_draw_multi_input(const float color[4], const float height, const float2 location) { - /* The other sockets are drawn with the keyframe shader. There, the outline has a base thickness - * that can be varied but always scales with the size the socket is drawn at. Using + /* The other sockets are drawn with the keyframe shader. There, the outline has a base + * thickness that can be varied but always scales with the size the socket is drawn at. Using * `UI_SCALE_FAC` has the same effect here. It scales the outline correctly across different * screen DPI's and UI scales without being affected by the 'line-width'. */ const float outline_width = NODE_SOCK_OUTLINE_SCALE * UI_SCALE_FAC; - /* UI_draw_roundbox draws the outline on the outer side, so compensate for the outline width. */ + /* UI_draw_roundbox draws the outline on the outer side, so compensate for the outline width. + */ const rctf rect = { location.x - width + outline_width * 0.5f, location.x + width - outline_width * 0.5f, @@ -1471,7 +1589,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)) { @@ -1504,7 +1622,7 @@ static void node_draw_sockets(const View2D &v2d, 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)) { @@ -1629,6 +1747,130 @@ 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 = (bNodePanelState *)panel_state_argv; + bNodeTree *ntree = (bNodeTree *)ntree_argv; + + panel_state->flag ^= NODE_PANEL_COLLAPSED; + + ED_node_tree_propagate_change(C, bmain, ntree); +} + +static void node_draw_panels(const View2D & /*v2d*/, + const bContext & /*C*/, + TreeDrawContext &tree_draw_ctx, + bNodeTree &ntree, + const bNode &node, + uiBlock &block) +{ + namespace nodes = blender::nodes; + + BLI_assert(node.declaration() != nullptr); + // BLI_assert(node.panel_states().size() == node.declaration().num_panels); + BLI_assert(node.runtime->panels.size() == node.panel_states().size()); + + const nodes::NodeDeclaration &decl = *node.declaration(); + const rctf &rct = node.runtime->totr; + const int color_id = node_get_colorid(tree_draw_ctx, node); + + 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()) { + 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); + + /* Panel background. */ + float color_panel[4]; + if (node.flag & NODE_MUTED) { + UI_GetThemeColorBlend4f(TH_BACK, color_id, 0.1f, color_panel); + } + else { + UI_GetThemeColorBlend4f(TH_NODE, color_id, 0.2f, color_panel); + } + UI_draw_roundbox_corner_set(UI_CNR_NONE); + UI_draw_roundbox_4fv(&rect, true, BASIS_RAD, color_panel); + + /* 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) { @@ -2012,7 +2254,8 @@ static Vector node_get_extra_info(TreeDrawContext &tree_draw_c row.text = node_get_execution_time_label(tree_draw_ctx, snode, node); if (!row.text.empty()) { row.tooltip = TIP_( - "The execution time from the node tree's latest evaluation. For frame and group nodes, " + "The execution time from the node tree's latest evaluation. For frame and group " + "nodes, " "the time for all sub-nodes"); row.icon = ICON_PREVIEW_RANGE; rows.append(std::move(row)); @@ -2565,6 +2808,10 @@ static void node_draw_basis(const bContext &C, node_draw_sockets(v2d, C, ntree, node, block, true, false); } + if (node.declaration() != nullptr) { + node_draw_panels(v2d, C, tree_draw_ctx, ntree, node, block); + } + UI_block_end(&C, &block); UI_block_draw(&C, &block); } @@ -3299,7 +3546,8 @@ 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); diff --git a/source/blender/editors/space_node/node_edit.cc b/source/blender/editors/space_node/node_edit.cc index cfaa85a9331..67d2967b280 100644 --- a/source/blender/editors/space_node/node_edit.cc +++ b/source/blender/editors/space_node/node_edit.cc @@ -2218,464 +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; - } - - if (!U.experimental.use_rotation_socket && socket_type->type == SOCK_ROTATION) { - return false; - } - - return true; -} - -static const EnumPropertyItem *socket_change_type_itemf(bContext *C, - PointerRNA * /*ptr*/, - PropertyRNA * /*prop*/, - bool *r_free) -{ - if (!C) { - return DummyRNA_NULL_items; - } - - SpaceNode *snode = CTX_wm_space_node(C); - if (!snode || !snode->edittree) { - return DummyRNA_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", DummyRNA_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 DummyRNA_NULL_items; - } - SpaceNode *snode = CTX_wm_space_node(C); - if (!snode || !snode->edittree) { - return DummyRNA_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 DummyRNA_NULL_items; - } - - const Set subtypes = socket_type_get_subtypes(eNodeSocketDatatype(active_socket->type)); - if (subtypes.is_empty()) { - return DummyRNA_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 DummyRNA_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", DummyRNA_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..02b6bf144cc 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. */ @@ -1049,6 +1046,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); } + else { + links_to_remove.add(link); + } } } for (bNodeSocket *output_socket : node->output_sockets()) { @@ -1065,7 +1065,14 @@ static void node_group_make_insert_selected(const bContext &C, internal_links_to_move.add(link); continue; } - output_links.append({link, add_interface_from_socket(ntree, group, *link->fromsock)}); + 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); + } } } } @@ -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 cf23d1b6f8e..7aed32c8ae0 100644 --- a/source/blender/editors/space_node/node_intern.hh +++ b/source/blender/editors/space_node/node_intern.hh @@ -378,12 +378,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 caabbb40d96..2b04473bddd 100644 --- a/source/blender/editors/space_node/node_ops.cc +++ b/source/blender/editors/space_node/node_ops.cc @@ -106,12 +106,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 2a9244070a3..8601c46da3a 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..d51aefdcfbf 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_cache().inputs : + ngroup->interface_cache().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++; diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_vector_displacement.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_vector_displacement.glsl index 0ff074bc04f..a382887d979 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_vector_displacement.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_vector_displacement.glsl @@ -3,7 +3,7 @@ void node_vector_displacement_tangent( { vec3 oN = normalize(normal_world_to_object(g_data.N)); vec3 oT = normalize(normal_world_to_object(T.xyz)); - vec3 oB = T.w * normalize(cross(oN, oT)); + vec3 oB = T.w * safe_normalize(cross(oN, oT)); result = (vector.xyz - midlevel) * scale; result = result.x * oT + result.y * oN + result.z * oB; diff --git a/source/blender/gpu/vulkan/vk_debug.cc b/source/blender/gpu/vulkan/vk_debug.cc index 80f8ca5bcf5..604741b3eb0 100644 --- a/source/blender/gpu/vulkan/vk_debug.cc +++ b/source/blender/gpu/vulkan/vk_debug.cc @@ -73,6 +73,7 @@ namespace blender::gpu::debug { void VKDebuggingTools::init(VkInstance vk_instance) { + PFN_vkGetInstanceProcAddr instance_proc_addr = vkGetInstanceProcAddr; enabled = false; vk_debug_utils_messenger = nullptr; @@ -248,6 +249,7 @@ void VKDebuggingTools::print_labels(const VkDebugUtilsMessengerCallbackDataEXT * ss << std::endl; printf("%s", ss.str().c_str()); } + VKAPI_ATTR VkBool32 VKAPI_CALL messenger_callback(VkDebugUtilsMessageSeverityFlagBitsEXT message_severity, VkDebugUtilsMessageTypeFlagsEXT /* message_type*/, @@ -263,71 +265,44 @@ messenger_callback(VkDebugUtilsMessageSeverityFlagBitsEXT message_severity, if (debugging_tools.is_ignore(callback_data->messageIdNumber)) { return VK_FALSE; } - bool use_color = CLG_color_support_get(&LOG); - UNUSED_VARS(use_color); - bool enabled = false; - if ((message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) || - (message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT)) + + CLG_Severity severity = CLG_SEVERITY_INFO; + if (message_severity & (VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT)) { - if ((LOG.type->flag & CLG_FLAG_USE) && (LOG.type->level >= CLG_SEVERITY_INFO)) { - const char *format = "{0x%x}% s\n %s "; - CLG_logf(LOG.type, - CLG_SEVERITY_INFO, - "", - "", - format, - callback_data->messageIdNumber, - callback_data->pMessageIdName, - callback_data->pMessage); - enabled = true; - } + severity = CLG_SEVERITY_INFO; } - else { - CLG_Severity clog_severity; - switch (message_severity) { - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: - clog_severity = CLG_SEVERITY_WARN; - break; - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: - clog_severity = CLG_SEVERITY_ERROR; - break; - default: - BLI_assert_unreachable(); - } - enabled = true; - if (clog_severity == CLG_SEVERITY_ERROR) { - const char *format = " %s {0x%x}\n %s "; - CLG_logf(LOG.type, - clog_severity, - "", - "", - format, - callback_data->pMessageIdName, - callback_data->messageIdNumber, - callback_data->pMessage); - } - else if (LOG.type->level >= CLG_SEVERITY_WARN) { - const char *format = " %s {0x%x}\n %s "; - CLG_logf(LOG.type, - clog_severity, - "", - "", - format, - callback_data->pMessageIdName, - callback_data->messageIdNumber, - callback_data->pMessage); - } + if (message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) { + severity = CLG_SEVERITY_WARN; } - if ((enabled) && ((callback_data->objectCount > 0) || (callback_data->cmdBufLabelCount > 0) || - (callback_data->queueLabelCount > 0))) - { + if (message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) { + severity = CLG_SEVERITY_ERROR; + } + + if ((LOG.type->flag & CLG_FLAG_USE) && (LOG.type->level <= severity)) { + const char *format = "{0x%x}% s\n %s "; + CLG_logf(LOG.type, + severity, + "", + "", + format, + callback_data->messageIdNumber, + callback_data->pMessageIdName, + callback_data->pMessage); + } + + const bool do_labels = (callback_data->objectCount + callback_data->cmdBufLabelCount + + callback_data->queueLabelCount) > 0; + if (do_labels) { debugging_tools.print_labels(callback_data); } + return VK_FALSE; }; VkResult VKDebuggingTools::init_messenger(VkInstance vk_instance) { + CLG_logref_init(&LOG); vk_message_id_number_ignored.clear(); BLI_assert(enabled); diff --git a/source/blender/makesdna/DNA_node_tree_interface_types.h b/source/blender/makesdna/DNA_node_tree_interface_types.h index 601c0840007..dfa6225b2ee 100644 --- a/source/blender/makesdna/DNA_node_tree_interface_types.h +++ b/source/blender/makesdna/DNA_node_tree_interface_types.h @@ -53,13 +53,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; @@ -101,11 +101,24 @@ 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, +} NodeTreeInterfacePanelFlag; +ENUM_OPERATORS(NodeTreeInterfacePanelFlag, NODE_INTERFACE_PANEL_DEFAULT_CLOSED); + typedef struct bNodeTreeInterfacePanel { bNodeTreeInterfaceItem item; /* UI name of the panel. */ char *name; + char *description; + /* eNodeTreeInterfacePanelFlag */ + int flag; + char _pad[4]; bNodeTreeInterfaceItem **items_array; int items_num; @@ -199,6 +212,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. */ @@ -258,7 +273,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. @@ -269,7 +284,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); @@ -278,7 +293,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, + const 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 @@ -286,6 +304,8 @@ typedef struct bNodeTreeInterface { * \param position: Position of the child panel within the parent panel. */ bNodeTreeInterfacePanel *insert_panel(blender::StringRefNull name, + blender::StringRefNull description, + const 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..441daa223fd 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. */ + short uid; + /* eNodePanelFlag */ + char flag; + char _pad; + +#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,11 @@ 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; + + const blender::bke::bNodeTreeInterfaceCache &interface_cache() 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/RNA_enum_items.hh b/source/blender/makesrna/RNA_enum_items.hh index ce169b29fd0..2eb0c1c5553 100644 --- a/source/blender/makesrna/RNA_enum_items.hh +++ b/source/blender/makesrna/RNA_enum_items.hh @@ -174,6 +174,8 @@ DEF_ENUM(rna_enum_navigation_mode_items) DEF_ENUM(rna_enum_node_socket_in_out_items) DEF_ENUM(rna_enum_node_socket_type_items) +DEF_ENUM(rna_enum_node_tree_interface_item_type_items) + DEF_ENUM(rna_enum_node_math_items) DEF_ENUM(rna_enum_mapping_type_items) DEF_ENUM(rna_enum_node_vec_math_items) diff --git a/source/blender/makesrna/RNA_enum_types.hh b/source/blender/makesrna/RNA_enum_types.hh index 7f5aebf26ef..c3156698142 100644 --- a/source/blender/makesrna/RNA_enum_types.hh +++ b/source/blender/makesrna/RNA_enum_types.hh @@ -46,6 +46,7 @@ const EnumPropertyItem *rna_node_tree_type_itemf(void *data, bool (*poll)(void *data, struct bNodeTreeType *), bool *r_free); +int rna_node_socket_idname_to_enum(const char *idname); struct bNodeSocketType *rna_node_socket_type_from_enum(int value); const EnumPropertyItem *rna_node_socket_type_itemf( void *data, bool (*poll)(void *data, struct bNodeSocketType *), bool *r_free); diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt index 58bd0e5bc4b..be59ce5ec8d 100644 --- a/source/blender/makesrna/intern/CMakeLists.txt +++ b/source/blender/makesrna/intern/CMakeLists.txt @@ -65,6 +65,7 @@ set(DEFSRC rna_nla.cc rna_nodetree.cc rna_node_socket.cc + rna_node_tree_interface.cc rna_object.cc rna_object_force.cc rna_packedfile.cc diff --git a/source/blender/makesrna/intern/makesrna.cc b/source/blender/makesrna/intern/makesrna.cc index 44bcc1d2200..d033c0b2751 100644 --- a/source/blender/makesrna/intern/makesrna.cc +++ b/source/blender/makesrna/intern/makesrna.cc @@ -4736,6 +4736,7 @@ static RNAProcessItem PROCESS_ITEMS[] = { {"rna_nla.cc", nullptr, RNA_def_nla}, {"rna_nodetree.cc", nullptr, RNA_def_nodetree}, {"rna_node_socket.cc", nullptr, RNA_def_node_socket_subtypes}, + {"rna_node_tree_interface.cc", nullptr, RNA_def_node_tree_interface}, {"rna_object.cc", "rna_object_api.cc", RNA_def_object}, {"rna_object_force.cc", nullptr, RNA_def_object_force}, {"rna_depsgraph.cc", nullptr, RNA_def_depsgraph}, diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h index 984766951ff..b28c411bd08 100644 --- a/source/blender/makesrna/intern/rna_internal.h +++ b/source/blender/makesrna/intern/rna_internal.h @@ -175,6 +175,7 @@ void RNA_def_modifier(struct BlenderRNA *brna); void RNA_def_nla(struct BlenderRNA *brna); void RNA_def_nodetree(struct BlenderRNA *brna); void RNA_def_node_socket_subtypes(struct BlenderRNA *brna); +void RNA_def_node_tree_interface(struct BlenderRNA *brna); void RNA_def_object(struct BlenderRNA *brna); void RNA_def_object_force(struct BlenderRNA *brna); void RNA_def_packedfile(struct BlenderRNA *brna); @@ -403,6 +404,9 @@ char *rna_TextureSlot_path(const struct PointerRNA *ptr); char *rna_Node_ImageUser_path(const struct PointerRNA *ptr); char *rna_CameraBackgroundImage_image_or_movieclip_user_path(const struct PointerRNA *ptr); +/* Node socket subtypes for group interface. */ +void rna_def_node_socket_interface_subtypes(BlenderRNA *brna); + /* Set U.is_dirty and redraw. */ /** diff --git a/source/blender/makesrna/intern/rna_node_socket.cc b/source/blender/makesrna/intern/rna_node_socket.cc index c4e709271d9..6c3a0e25f5f 100644 --- a/source/blender/makesrna/intern/rna_node_socket.cc +++ b/source/blender/makesrna/intern/rna_node_socket.cc @@ -48,14 +48,8 @@ const EnumPropertyItem rna_enum_node_socket_type_items[] = { # include "ED_node.hh" -extern "C" { 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; -} /* ******** Node Socket ******** */ @@ -287,236 +281,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, @@ -539,27 +303,9 @@ 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) -{ - PointerRNA ptr; - RNA_pointer_create(id, &RNA_NodeSocketInterface, sock, &ptr); - sock->typeinfo->interface_draw(C, layout, &ptr); -} +/* ******** Node Socket Subtypes ******** */ -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); -} - -static void rna_NodeSocketStandard_float_range( +void rna_NodeSocketStandard_float_range( PointerRNA *ptr, float *min, float *max, float *softmin, float *softmax) { bNodeSocket *sock = static_cast(ptr->data); @@ -576,7 +322,7 @@ static void rna_NodeSocketStandard_float_range( *softmax = dval->max; } -static void rna_NodeSocketStandard_int_range( +void rna_NodeSocketStandard_int_range( PointerRNA *ptr, int *min, int *max, int *softmin, int *softmax) { bNodeSocket *sock = static_cast(ptr->data); @@ -593,7 +339,7 @@ static void rna_NodeSocketStandard_int_range( *softmax = dval->max; } -static void rna_NodeSocketStandard_vector_range( +void rna_NodeSocketStandard_vector_range( PointerRNA *ptr, float *min, float *max, float *softmin, float *softmax) { bNodeSocket *sock = static_cast(ptr->data); @@ -623,8 +369,6 @@ static void rna_NodeSocketStandard_value_and_relation_update(bContext *C, Pointe DEG_relations_tag_update(bmain); } -/* ******** Node Socket Subtypes ******** */ - bool rna_NodeSocketMaterial_default_value_poll(PointerRNA * /*ptr*/, PointerRNA value) { /* Do not show grease pencil materials for now. */ @@ -810,722 +554,7 @@ static void rna_def_node_socket(BlenderRNA *brna) 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); - 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); - 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_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); -} - -static void rna_def_node_socket_standard_types(BlenderRNA *brna) +static void rna_def_node_socket_standard(BlenderRNA *brna) { /* XXX Workaround: Registered functions are not exposed in python by bpy, * it expects them to be registered from python and use the native implementation. @@ -1536,7 +565,7 @@ static void rna_def_node_socket_standard_types(BlenderRNA *brna) */ StructRNA *srna; - PropertyRNA *parm, *prop; + PropertyRNA *parm; FunctionRNA *func; static float default_draw_color[] = {0.0f, 0.0f, 0.0f, 1.0f}; @@ -1575,21 +604,21 @@ static void rna_def_node_socket_standard_types(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); +} - srna = RNA_def_struct(brna, "NodeSocketInterfaceStandard", "NodeSocketInterface"); - RNA_def_struct_sdna(srna, "bNodeSocket"); +/* Common functions for all builtin socket interface types. */ +static void rna_def_node_tree_interface_socket_builtin(StructRNA *srna) +{ + FunctionRNA *func; + PropertyRNA *parm; - /* 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"); + /* Override for functions, invoking the typeinfo callback directly + * instead of expecting an existing RNA registered function implementation. + */ - func = RNA_def_function(srna, "draw", "rna_NodeSocketInterfaceStandard_draw"); + func = RNA_def_function(srna, "draw", "rna_NodeTreeInterfaceSocket_draw_builtin"); RNA_def_function_flag(func, FUNC_USE_SELF_ID); - RNA_def_function_ui_description(func, "Draw template settings"); + RNA_def_function_ui_description(func, "Draw interface socket 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); @@ -1597,95 +626,878 @@ static void rna_def_node_socket_standard_types(BlenderRNA *brna) 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); - RNA_def_function_ui_description(func, "Color of the socket icon"); - parm = RNA_def_pointer(func, "context", "Context", "", ""); + func = RNA_def_function(srna, "init_socket", "rna_NodeTreeInterfaceSocket_init_socket_builtin"); + RNA_def_function_ui_description(func, "Initialize a node socket instance"); + RNA_def_function_flag(func, FUNC_USE_SELF_ID | 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_float_array( - func, "color", 4, default_draw_color, 0.0f, 1.0f, "Color", "", 0.0f, 1.0f); - RNA_def_function_output(func, parm); + parm = RNA_def_pointer(func, "socket", "NodeSocket", "Socket", "Socket to initialize"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + 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); - /* XXX These types should eventually be registered at runtime. - * Then use the nodeStaticSocketType and nodeStaticSocketInterfaceType functions - * to get the idname strings from int type and subtype - * (see node_socket.cc, register_standard_node_socket_types). - */ + func = RNA_def_function(srna, "from_socket", "rna_NodeTreeInterfaceSocket_from_socket_builtin"); + RNA_def_function_ui_description(func, "Setup template parameters from an existing socket"); + RNA_def_function_flag(func, FUNC_USE_SELF_ID | 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"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); +} - rna_def_node_socket_float(brna, "NodeSocketFloat", "NodeSocketInterfaceFloat", PROP_NONE); - rna_def_node_socket_float( - brna, "NodeSocketFloatUnsigned", "NodeSocketInterfaceFloatUnsigned", PROP_UNSIGNED); - rna_def_node_socket_float( - brna, "NodeSocketFloatPercentage", "NodeSocketInterfaceFloatPercentage", PROP_PERCENTAGE); - rna_def_node_socket_float( - brna, "NodeSocketFloatFactor", "NodeSocketInterfaceFloatFactor", PROP_FACTOR); - rna_def_node_socket_float( - brna, "NodeSocketFloatAngle", "NodeSocketInterfaceFloatAngle", PROP_ANGLE); - rna_def_node_socket_float( - brna, "NodeSocketFloatTime", "NodeSocketInterfaceFloatTime", PROP_TIME); - rna_def_node_socket_float(brna, - "NodeSocketFloatTimeAbsolute", - "NodeSocketInterfaceFloatTimeAbsolute", - PROP_TIME_ABSOLUTE); - rna_def_node_socket_float( - brna, "NodeSocketFloatDistance", "NodeSocketInterfaceFloatDistance", PROP_DISTANCE); +static void rna_def_node_socket_float(BlenderRNA *brna, + const char *identifier, + PropertySubType subtype) +{ + StructRNA *srna; + PropertyRNA *prop; - rna_def_node_socket_int(brna, "NodeSocketInt", "NodeSocketInterfaceInt", PROP_NONE); - rna_def_node_socket_int( - brna, "NodeSocketIntUnsigned", "NodeSocketInterfaceIntUnsigned", PROP_UNSIGNED); - rna_def_node_socket_int( - brna, "NodeSocketIntPercentage", "NodeSocketInterfaceIntPercentage", PROP_PERCENTAGE); - rna_def_node_socket_int( - brna, "NodeSocketIntFactor", "NodeSocketInterfaceIntFactor", PROP_FACTOR); + 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_node_socket_bool(brna, "NodeSocketBool", "NodeSocketInterfaceBool"); - rna_def_node_socket_rotation(brna, "NodeSocketRotation", "NodeSocketInterfaceRotation"); + RNA_def_struct_sdna_from(srna, "bNodeSocketValueFloat", "default_value"); - rna_def_node_socket_vector(brna, "NodeSocketVector", "NodeSocketInterfaceVector", PROP_NONE); - rna_def_node_socket_vector(brna, - "NodeSocketVectorTranslation", - "NodeSocketInterfaceVectorTranslation", - PROP_TRANSLATION); - rna_def_node_socket_vector( - brna, "NodeSocketVectorDirection", "NodeSocketInterfaceVectorDirection", PROP_DIRECTION); - rna_def_node_socket_vector( - brna, "NodeSocketVectorVelocity", "NodeSocketInterfaceVectorVelocity", PROP_VELOCITY); - rna_def_node_socket_vector(brna, - "NodeSocketVectorAcceleration", - "NodeSocketInterfaceVectorAcceleration", - PROP_ACCELERATION); - rna_def_node_socket_vector( - brna, "NodeSocketVectorEuler", "NodeSocketInterfaceVectorEuler", PROP_EULER); - rna_def_node_socket_vector( - brna, "NodeSocketVectorXYZ", "NodeSocketInterfaceVectorXYZ", PROP_XYZ); + 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_node_socket_color(brna, "NodeSocketColor", "NodeSocketInterfaceColor"); + RNA_def_struct_sdna_from(srna, "bNodeSocket", nullptr); +} - rna_def_node_socket_string(brna, "NodeSocketString", "NodeSocketInterfaceString"); +static void rna_def_node_socket_interface_float(BlenderRNA *brna, + const char *identifier, + PropertySubType subtype) +{ + StructRNA *srna; + PropertyRNA *prop; + float value_default; - rna_def_node_socket_shader(brna, "NodeSocketShader", "NodeSocketInterfaceShader"); + /* 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, identifier, "NodeTreeInterfaceSocket"); + RNA_def_struct_ui_text( + srna, "Float Node Socket Interface", "Floating-point number socket of a node"); + RNA_def_struct_sdna(srna, "bNodeTreeInterfaceSocket"); + + RNA_def_struct_sdna_from(srna, "bNodeSocketValueFloat", "socket_data"); + + 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_NodeTreeInterfaceSocketFloat_default_value_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_NodeTreeInterfaceSocket_value_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_NodeTreeInterfaceSocket_value_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_NodeTreeInterfaceSocket_value_update"); + + RNA_def_struct_sdna_from(srna, "bNodeTreeInterfaceSocket", nullptr); + + 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) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, identifier, "NodeTreeInterfaceSocket"); + RNA_def_struct_ui_text(srna, "Integer Node Socket Interface", "Integer number socket of a node"); + RNA_def_struct_sdna(srna, "bNodeTreeInterfaceSocket"); + + RNA_def_struct_sdna_from(srna, "bNodeSocketValueInt", "socket_data"); + + 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_NodeTreeInterfaceSocketInt_default_value_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_NodeTreeInterfaceSocket_value_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_NodeTreeInterfaceSocket_value_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_NodeTreeInterfaceSocket_value_update"); + + RNA_def_struct_sdna_from(srna, "bNodeTreeInterfaceSocket", nullptr); + + 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; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, identifier, "NodeTreeInterfaceSocket"); + RNA_def_struct_ui_text(srna, "Boolean Node Socket Interface", "Boolean value socket of a node"); + RNA_def_struct_sdna(srna, "bNodeTreeInterfaceSocket"); + + RNA_def_struct_sdna_from(srna, "bNodeSocketValueBoolean", "socket_data"); + + 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_NodeTreeInterfaceSocket_value_update"); + + RNA_def_struct_sdna_from(srna, "bNodeTreeInterfaceSocket", nullptr); + + 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; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, identifier, "NodeTreeInterfaceSocket"); + RNA_def_struct_ui_text( + srna, "Rotation Node Socket Interface", "Rotation value socket of a node"); + RNA_def_struct_sdna(srna, "bNodeTreeInterfaceSocket"); + + RNA_def_struct_sdna_from(srna, "bNodeSocketValueRotation", "socket_data"); + + 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_NodeTreeInterfaceSocket_value_update"); + + RNA_def_struct_sdna_from(srna, "bNodeTreeInterfaceSocket", nullptr); + + 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) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, identifier, "NodeTreeInterfaceSocket"); + RNA_def_struct_ui_text(srna, "Vector Node Socket Interface", "3D vector socket of a node"); + RNA_def_struct_sdna(srna, "bNodeTreeInterfaceSocket"); + + RNA_def_struct_sdna_from(srna, "bNodeSocketValueVector", "socket_data"); + + 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_NodeTreeInterfaceSocketVector_default_value_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_NodeTreeInterfaceSocket_value_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_NodeTreeInterfaceSocket_value_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_NodeTreeInterfaceSocket_value_update"); + + RNA_def_struct_sdna_from(srna, "bNodeTreeInterfaceSocket", nullptr); + + 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; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, identifier, "NodeTreeInterfaceSocket"); + RNA_def_struct_ui_text(srna, "Color Node Socket Interface", "RGBA color socket of a node"); + RNA_def_struct_sdna(srna, "bNodeTreeInterfaceSocket"); + + RNA_def_struct_sdna_from(srna, "bNodeSocketValueRGBA", "socket_data"); + + 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_NodeTreeInterfaceSocket_value_update"); + + RNA_def_struct_sdna_from(srna, "bNodeTreeInterfaceSocket", nullptr); + + 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; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, identifier, "NodeTreeInterfaceSocket"); + RNA_def_struct_ui_text(srna, "String Node Socket Interface", "String socket of a node"); + RNA_def_struct_sdna(srna, "bNodeTreeInterfaceSocket"); + + RNA_def_struct_sdna_from(srna, "bNodeSocketValueString", "socket_data"); + + 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_NodeTreeInterfaceSocket_value_update"); + + RNA_def_struct_sdna_from(srna, "bNodeTreeInterfaceSocket", nullptr); + + 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; + + srna = RNA_def_struct(brna, identifier, "NodeTreeInterfaceSocket"); + RNA_def_struct_ui_text(srna, "Shader Node Socket Interface", "Shader socket of a node"); + RNA_def_struct_sdna(srna, "bNodeTreeInterfaceSocket"); + + 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; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, identifier, "NodeTreeInterfaceSocket"); + RNA_def_struct_ui_text(srna, "Object Node Socket Interface", "Object socket of a node"); + RNA_def_struct_sdna(srna, "bNodeTreeInterfaceSocket"); + + RNA_def_struct_sdna_from(srna, "bNodeSocketValueObject", "socket_data"); + + 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_NodeTreeInterfaceSocket_value_update"); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); + + 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; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, identifier, "NodeTreeInterfaceSocket"); + RNA_def_struct_ui_text(srna, "Image Node Socket Interface", "Image socket of a node"); + RNA_def_struct_sdna(srna, "bNodeTreeInterfaceSocket"); + + RNA_def_struct_sdna_from(srna, "bNodeSocketValueImage", "socket_data"); + + 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_NodeTreeInterfaceSocket_value_update"); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); + + 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; + + srna = RNA_def_struct(brna, identifier, "NodeTreeInterfaceSocket"); + RNA_def_struct_ui_text(srna, "Geometry Node Socket Interface", "Geometry socket of a node"); + RNA_def_struct_sdna(srna, "bNodeTreeInterfaceSocket"); + + 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; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, identifier, "NodeTreeInterfaceSocket"); + RNA_def_struct_ui_text(srna, "Collection Node Socket Interface", "Collection socket of a node"); + RNA_def_struct_sdna(srna, "bNodeTreeInterfaceSocket"); + + RNA_def_struct_sdna_from(srna, "bNodeSocketValueCollection", "socket_data"); + + 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_NodeTreeInterfaceSocket_value_update"); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); + + 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; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, identifier, "NodeTreeInterfaceSocket"); + RNA_def_struct_ui_text(srna, "Texture Node Socket Interface", "Texture socket of a node"); + RNA_def_struct_sdna(srna, "bNodeTreeInterfaceSocket"); + + RNA_def_struct_sdna_from(srna, "bNodeSocketValueTexture", "socket_data"); + + 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_NodeTreeInterfaceSocket_value_update"); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); + + 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; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, identifier, "NodeTreeInterfaceSocket"); + RNA_def_struct_ui_text(srna, "Material Node Socket Interface", "Material socket of a node"); + RNA_def_struct_sdna(srna, "bNodeTreeInterfaceSocket"); + + RNA_def_struct_sdna_from(srna, "bNodeSocketValueMaterial", "socket_data"); + + 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_NodeTreeInterfaceSocketMaterial_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_NodeTreeInterfaceSocket_value_update"); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); + + 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; + eNodeSocketDatatype type; + PropertySubType subtype; + const char *label; +}; + +/* 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}, +}; + +static void rna_def_node_socket_subtypes(BlenderRNA *brna) +{ + for (const bNodeSocketStaticTypeInfo &info : node_socket_subtypes) { + const char *identifier = info.socket_identifier; + + switch (info.type) { + case SOCK_FLOAT: + rna_def_node_socket_float(brna, identifier, info.subtype); + break; + case SOCK_INT: + rna_def_node_socket_int(brna, identifier, info.subtype); + break; + case SOCK_BOOLEAN: + rna_def_node_socket_bool(brna, identifier); + break; + case SOCK_ROTATION: + rna_def_node_socket_rotation(brna, identifier); + break; + case SOCK_VECTOR: + rna_def_node_socket_vector(brna, identifier, info.subtype); + break; + case SOCK_RGBA: + rna_def_node_socket_color(brna, identifier); + break; + case SOCK_STRING: + rna_def_node_socket_string(brna, identifier); + break; + case SOCK_SHADER: + rna_def_node_socket_shader(brna, identifier); + break; + case SOCK_OBJECT: + rna_def_node_socket_object(brna, identifier); + break; + case SOCK_IMAGE: + rna_def_node_socket_image(brna, identifier); + break; + case SOCK_GEOMETRY: + rna_def_node_socket_geometry(brna, identifier); + break; + case SOCK_COLLECTION: + rna_def_node_socket_collection(brna, identifier); + break; + case SOCK_TEXTURE: + rna_def_node_socket_texture(brna, identifier); + break; + case SOCK_MATERIAL: + rna_def_node_socket_material(brna, identifier); + break; + + case SOCK_CUSTOM: + break; + } + } rna_def_node_socket_virtual(brna, "NodeSocketVirtual"); +} - rna_def_node_socket_object(brna, "NodeSocketObject", "NodeSocketInterfaceObject"); +/* Note: interface items are defined outside this file. + * The subtypes must be defined after the base type, so this function + * is called from the interface rna file to ensure correct order. */ +void rna_def_node_socket_interface_subtypes(BlenderRNA *brna) +{ + for (const bNodeSocketStaticTypeInfo &info : node_socket_subtypes) { + const char *identifier = info.interface_identifier; - rna_def_node_socket_image(brna, "NodeSocketImage", "NodeSocketInterfaceImage"); + switch (info.type) { + case SOCK_FLOAT: + rna_def_node_socket_interface_float(brna, identifier, info.subtype); + break; + case SOCK_INT: + rna_def_node_socket_interface_int(brna, identifier, info.subtype); + break; + case SOCK_BOOLEAN: + rna_def_node_socket_interface_bool(brna, identifier); + break; + case SOCK_ROTATION: + rna_def_node_socket_interface_rotation(brna, identifier); + break; + case SOCK_VECTOR: + rna_def_node_socket_interface_vector(brna, identifier, info.subtype); + break; + case SOCK_RGBA: + rna_def_node_socket_interface_color(brna, identifier); + break; + case SOCK_STRING: + rna_def_node_socket_interface_string(brna, identifier); + break; + case SOCK_SHADER: + rna_def_node_socket_interface_shader(brna, identifier); + break; + case SOCK_OBJECT: + rna_def_node_socket_interface_object(brna, identifier); + break; + case SOCK_IMAGE: + rna_def_node_socket_interface_image(brna, identifier); + break; + case SOCK_GEOMETRY: + rna_def_node_socket_interface_geometry(brna, identifier); + break; + case SOCK_COLLECTION: + rna_def_node_socket_interface_collection(brna, identifier); + break; + case SOCK_TEXTURE: + rna_def_node_socket_interface_texture(brna, identifier); + break; + case SOCK_MATERIAL: + rna_def_node_socket_interface_material(brna, identifier); + break; - rna_def_node_socket_geometry(brna, "NodeSocketGeometry", "NodeSocketInterfaceGeometry"); - - rna_def_node_socket_collection(brna, "NodeSocketCollection", "NodeSocketInterfaceCollection"); - - rna_def_node_socket_texture(brna, "NodeSocketTexture", "NodeSocketInterfaceTexture"); - - rna_def_node_socket_material(brna, "NodeSocketMaterial", "NodeSocketInterfaceMaterial"); + case SOCK_CUSTOM: + break; + } + } } void RNA_def_node_socket_subtypes(BlenderRNA *brna) { rna_def_node_socket(brna); - rna_def_node_socket_interface(brna); - rna_def_node_socket_standard_types(brna); + rna_def_node_socket_standard(brna); + rna_def_node_socket_subtypes(brna); } #endif diff --git a/source/blender/makesrna/intern/rna_node_tree_interface.cc b/source/blender/makesrna/intern/rna_node_tree_interface.cc new file mode 100644 index 00000000000..5b0d467eda6 --- /dev/null +++ b/source/blender/makesrna/intern/rna_node_tree_interface.cc @@ -0,0 +1,1074 @@ +/* SPDX-FileCopyrightText: 2023 Blender Foundation + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup RNA + */ + +#include "DNA_node_tree_interface_types.h" + +#include "RNA_define.hh" +#include "RNA_enum_types.hh" +#include "RNA_types.hh" + +#include "rna_internal.h" + +#include "WM_types.hh" + +const EnumPropertyItem rna_enum_node_tree_interface_item_type_items[] = { + {NODE_INTERFACE_SOCKET, "SOCKET", 0, "Socket", ""}, + {NODE_INTERFACE_PANEL, "PANEL", 0, "Panel", ""}, + {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 "DNA_material_types.h" +# include "ED_node.hh" +# include "WM_api.hh" + +/* Internal RNA function declarations, used to invoke registered callbacks. */ +extern FunctionRNA rna_NodeTreeInterfaceSocket_draw_func; +extern FunctionRNA rna_NodeTreeInterfaceSocket_init_socket_func; +extern FunctionRNA rna_NodeTreeInterfaceSocket_from_socket_func; + +namespace node_interface = blender::bke::node_interface; + +static void rna_NodeTreeInterfaceItem_update(Main *bmain, Scene * /*scene*/, PointerRNA *ptr) +{ + bNodeTree *ntree = reinterpret_cast(ptr->owner_id); + BKE_ntree_update_tag_interface(ntree); + ED_node_tree_propagate_change(nullptr, bmain, ntree); +} + +static StructRNA *rna_NodeTreeInterfaceItem_refine(PointerRNA *ptr) +{ + bNodeTreeInterfaceItem *item = static_cast(ptr->data); + + switch (item->item_type) { + case NODE_INTERFACE_SOCKET: { + bNodeTreeInterfaceSocket &socket = node_interface::get_item_as( + *item); + bNodeSocketType *socket_typeinfo = nodeSocketTypeFind(socket.socket_type); + if (socket_typeinfo && socket_typeinfo->ext_interface.srna) { + return socket_typeinfo->ext_interface.srna; + } + return &RNA_NodeTreeInterfaceSocket; + } + case NODE_INTERFACE_PANEL: + return &RNA_NodeTreeInterfacePanel; + default: + return &RNA_NodeTreeInterfaceItem; + } +} + +static char *rna_NodeTreeInterfaceItem_path(const PointerRNA *ptr) +{ + bNodeTree *ntree = reinterpret_cast(ptr->owner_id); + const bNodeTreeInterfaceItem *item = static_cast(ptr->data); + if (!ntree->runtime) { + return nullptr; + } + + ntree->ensure_topology_cache(); + 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); + } + } + return nullptr; +} + +static bool rna_NodeTreeInterfaceSocket_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 void rna_NodeTreeInterfaceSocket_draw_builtin(ID *id, + bNodeTreeInterfaceSocket *interface_socket, + bContext *C, + uiLayout *layout) +{ + bNodeSocketType *typeinfo = interface_socket->socket_typeinfo(); + if (typeinfo && typeinfo->interface_draw) { + typeinfo->interface_draw(id, interface_socket, C, 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) { + return; + } + + PointerRNA ptr; + RNA_pointer_create(id, &RNA_NodeTreeInterfaceSocket, interface_socket, &ptr); + + FunctionRNA *func = &rna_NodeTreeInterfaceSocket_draw_func; + + ParameterList list; + RNA_parameter_list_create(&list, &ptr, func); + RNA_parameter_set_lookup(&list, "context", &C); + RNA_parameter_set_lookup(&list, "layout", &layout); + typeinfo->ext_interface.call(C, &ptr, func, &list); + + RNA_parameter_list_free(&list); +} + +static void rna_NodeTreeInterfaceSocket_init_socket_builtin( + ID *id, + bNodeTreeInterfaceSocket *interface_socket, + bNode *node, + bNodeSocket *socket, + const char *data_path) +{ + bNodeSocketType *typeinfo = interface_socket->socket_typeinfo(); + if (typeinfo && typeinfo->interface_draw) { + typeinfo->interface_init_socket(id, interface_socket, node, socket, data_path); + } +} + +static void rna_NodeTreeInterfaceSocket_init_socket_custom( + ID *id, + const bNodeTreeInterfaceSocket *interface_socket, + bNode *node, + bNodeSocket *socket, + const char *data_path) +{ + bNodeSocketType *typeinfo = nodeSocketTypeFind(interface_socket->socket_type); + if (typeinfo == nullptr) { + return; + } + + PointerRNA ptr, node_ptr, socket_ptr; + RNA_pointer_create(id, + &RNA_NodeTreeInterfaceSocket, + const_cast(interface_socket), + &ptr); + RNA_pointer_create(id, &RNA_Node, node, &node_ptr); + RNA_pointer_create(id, &RNA_NodeSocket, socket, &socket_ptr); + + FunctionRNA *func = &rna_NodeTreeInterfaceSocket_init_socket_func; + + ParameterList list; + RNA_parameter_list_create(&list, &ptr, func); + RNA_parameter_set_lookup(&list, "node", &node_ptr); + RNA_parameter_set_lookup(&list, "socket", &socket_ptr); + RNA_parameter_set_lookup(&list, "data_path", &data_path); + typeinfo->ext_interface.call(nullptr, &ptr, func, &list); + + RNA_parameter_list_free(&list); +} + +static void rna_NodeTreeInterfaceSocket_from_socket_builtin( + ID *id, bNodeTreeInterfaceSocket *interface_socket, bNode *node, bNodeSocket *socket) +{ + bNodeSocketType *typeinfo = interface_socket->socket_typeinfo(); + if (typeinfo && typeinfo->interface_draw) { + typeinfo->interface_from_socket(id, interface_socket, node, socket); + } +} + +static void rna_NodeTreeInterfaceSocket_from_socket_custom( + ID *id, + bNodeTreeInterfaceSocket *interface_socket, + const bNode *node, + const bNodeSocket *socket) +{ + bNodeSocketType *typeinfo = nodeSocketTypeFind(interface_socket->socket_type); + if (typeinfo == nullptr) { + return; + } + + PointerRNA ptr, node_ptr, socket_ptr; + RNA_pointer_create(id, &RNA_NodeTreeInterfaceSocket, interface_socket, &ptr); + RNA_pointer_create(id, &RNA_Node, const_cast(node), &node_ptr); + RNA_pointer_create(id, &RNA_NodeSocket, const_cast(socket), &socket_ptr); + + FunctionRNA *func = &rna_NodeTreeInterfaceSocket_from_socket_func; + + ParameterList list; + RNA_parameter_list_create(&list, &ptr, func); + RNA_parameter_set_lookup(&list, "node", &node_ptr); + RNA_parameter_set_lookup(&list, "socket", &socket_ptr); + typeinfo->ext_interface.call(nullptr, &ptr, func, &list); + + RNA_parameter_list_free(&list); +} + +static StructRNA *rna_NodeTreeInterfaceSocket_register(Main * /*bmain*/, + ReportList * /*reports*/, + void *data, + const char *identifier, + StructValidateFunc validate, + StructCallbackFunc call, + StructFreeFunc free) +{ + bNodeTreeInterfaceSocket dummy_socket; + memset(&dummy_socket, 0, sizeof(bNodeTreeInterfaceSocket)); + /* Set #item_type so that refining the type ends up with RNA_NodeTreeInterfaceSocket. */ + dummy_socket.item.item_type = NODE_INTERFACE_SOCKET; + + PointerRNA dummy_socket_ptr; + RNA_pointer_create(nullptr, &RNA_NodeTreeInterfaceSocket, &dummy_socket, &dummy_socket_ptr); + + /* Validate the python class. */ + bool have_function[3]; + if (validate(&dummy_socket_ptr, data, have_function) != 0) { + return nullptr; + } + + /* Check if we have registered this socket type before. */ + bNodeSocketType *st = nodeSocketTypeFind(dummy_socket.socket_type); + if (st) { + /* Socket type registered before. */ + } + else { + /* Create a new node socket type. */ + st = MEM_cnew(__func__); + BLI_strncpy(st->idname, dummy_socket.socket_type, sizeof(st->idname)); + + 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_NodeTreeInterfaceSocket); + 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_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); + + /* Update while blender is running */ + WM_main_add_notifier(NC_NODE | NA_EDITED, nullptr); + + return st->ext_interface.srna; +} + +static IDProperty **rna_NodeTreeInterfaceSocket_idprops(PointerRNA *ptr) +{ + bNodeTreeInterfaceSocket *socket = static_cast(ptr->data); + return &socket->properties; +} + +static void rna_NodeTreeInterfaceSocket_identifier_get(PointerRNA *ptr, char *value) +{ + bNodeTreeInterfaceSocket *socket = static_cast(ptr->data); + strcpy(value, socket->identifier); +} + +static int rna_NodeTreeInterfaceSocket_identifier_length(PointerRNA *ptr) +{ + bNodeTreeInterfaceSocket *socket = static_cast(ptr->data); + return strlen(socket->identifier); +} + +static int rna_NodeTreeInterfaceSocket_socket_type_get(PointerRNA *ptr) +{ + bNodeTreeInterfaceSocket *socket = static_cast(ptr->data); + return rna_node_socket_idname_to_enum(socket->socket_type); +} + +static void rna_NodeTreeInterfaceSocket_socket_type_set(PointerRNA *ptr, int value) +{ + bNodeSocketType *typeinfo = rna_node_socket_type_from_enum(value); + + if (typeinfo) { + bNodeTreeInterfaceSocket *socket = static_cast(ptr->data); + socket->set_socket_type(typeinfo->idname); + } +} + +static bool is_socket_type_supported(bNodeTreeType *ntreetype, bNodeSocketType *socket_type) +{ + /* Check if the node tree supports the socket type. */ + 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; + } + + if (!U.experimental.use_rotation_socket && socket_type->type == SOCK_ROTATION) { + return false; + } + + return true; +} + +static bNodeSocketType *find_supported_socket_type(bNodeTreeType *ntree_type) +{ + NODE_SOCKET_TYPES_BEGIN (socket_type) { + if (is_socket_type_supported(ntree_type, socket_type)) { + return socket_type; + } + } + NODE_SOCKET_TYPES_END; + return nullptr; +} + +static bool rna_NodeTreeInterfaceSocket_socket_type_poll(void *userdata, + bNodeSocketType *socket_type) +{ + bNodeTreeType *ntreetype = static_cast(userdata); + return is_socket_type_supported(ntreetype, socket_type); +} + +static const EnumPropertyItem *rna_NodeTreeInterfaceSocket_socket_type_itemf( + bContext * /*C*/, PointerRNA *ptr, PropertyRNA * /*prop*/, bool *r_free) +{ + bNodeTree *ntree = reinterpret_cast(ptr->owner_id); + + if (!ntree) { + return DummyRNA_NULL_items; + } + + return rna_node_socket_type_itemf( + ntree->typeinfo, rna_NodeTreeInterfaceSocket_socket_type_poll, r_free); +} + +static PointerRNA rna_NodeTreeInterfaceItems_active_get(PointerRNA *ptr) +{ + bNodeTreeInterface *interface = static_cast(ptr->data); + PointerRNA r_ptr; + RNA_pointer_create(ptr->owner_id, &RNA_NodeTreeInterfaceItem, interface->active_item(), &r_ptr); + return r_ptr; +} + +static void rna_NodeTreeInterfaceItems_active_set(PointerRNA *ptr, + PointerRNA value, + ReportList * /*reports*/) +{ + bNodeTreeInterface *interface = static_cast(ptr->data); + bNodeTreeInterfaceItem *item = static_cast(value.data); + interface->active_item_set(item); +} + +static bNodeTreeInterfaceSocket *rna_NodeTreeInterfaceItems_new_socket( + ID *id, + bNodeTreeInterface *interface, + Main *bmain, + ReportList *reports, + const char *name, + const char *description, + bool is_input, + bool is_output, + int socket_type_enum, + 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; + } + bNodeTree *ntree = reinterpret_cast(id); + bNodeSocketType *typeinfo = rna_node_socket_type_from_enum(socket_type_enum); + if (typeinfo == nullptr) { + BKE_report(reports, RPT_ERROR_INVALID_INPUT, "Unknown socket type"); + return nullptr; + } + + /* If data type is unsupported try to find a valid type. */ + if (!is_socket_type_supported(ntree->typeinfo, typeinfo)) { + typeinfo = find_supported_socket_type(ntree->typeinfo); + if (typeinfo == nullptr) { + BKE_report(reports, RPT_ERROR, "Could not find supported socket type"); + return nullptr; + } + } + const char *socket_type = typeinfo->idname; + + NodeTreeInterfaceSocketFlag flag = NodeTreeInterfaceSocketFlag(0); + SET_FLAG_FROM_TEST(flag, is_input, NODE_INTERFACE_SOCKET_INPUT); + SET_FLAG_FROM_TEST(flag, is_output, NODE_INTERFACE_SOCKET_OUTPUT); + + bNodeTreeInterfaceSocket *socket = interface->add_socket(name ? name : "", + description ? description : "", + socket_type ? socket_type : "", + flag, + parent); + + if (socket == nullptr) { + BKE_report(reports, RPT_ERROR, "Unable to create socket"); + } + else { + BKE_ntree_update_tag_interface(ntree); + ED_node_tree_propagate_change(nullptr, bmain, ntree); + WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); + } + + return socket; +} + +static bNodeTreeInterfacePanel *rna_NodeTreeInterfaceItems_new_panel( + ID *id, + bNodeTreeInterface *interface, + Main *bmain, + ReportList *reports, + const char *name, + const char *description, + bool default_closed, + bNodeTreeInterfacePanel *parent) +{ + 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; + } + } + + 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"); + } + else { + bNodeTree *ntree = reinterpret_cast(id); + BKE_ntree_update_tag_interface(ntree); + ED_node_tree_propagate_change(nullptr, bmain, ntree); + WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); + } + + return panel; +} + +static bNodeTreeInterfaceItem *rna_NodeTreeInterfaceItems_copy_to_parent( + ID *id, + bNodeTreeInterface *interface, + Main *bmain, + ReportList *reports, + bNodeTreeInterfaceItem *item, + bNodeTreeInterfacePanel *parent) +{ + 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) { + parent = &interface->root_panel; + } + const int index = parent->items().as_span().first_index_try(item); + if (!parent->items().index_range().contains(index)) { + return nullptr; + } + + bNodeTreeInterfaceItem *item_copy = interface->insert_item_copy(*item, parent, index + 1); + + if (item_copy == nullptr) { + BKE_report(reports, RPT_ERROR, "Unable to copy item"); + } + else { + bNodeTree *ntree = reinterpret_cast(id); + BKE_ntree_update_tag_interface(ntree); + ED_node_tree_propagate_change(nullptr, bmain, ntree); + WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); + } + + return item_copy; +} + +static bNodeTreeInterfaceItem *rna_NodeTreeInterfaceItems_copy(ID *id, + bNodeTreeInterface *interface, + Main *bmain, + ReportList *reports, + bNodeTreeInterfaceItem *item) +{ + /* Copy to same parent as the item. */ + bNodeTreeInterfacePanel *parent = interface->find_item_parent(*item); + if (parent == nullptr) { + return nullptr; + } + return rna_NodeTreeInterfaceItems_copy_to_parent(id, interface, bmain, reports, item, parent); +} + +static void rna_NodeTreeInterfaceItems_remove(ID *id, + bNodeTreeInterface *interface, + Main *bmain, + bNodeTreeInterfaceItem *item, + bool move_content_to_parent) +{ + interface->remove_item(*item, move_content_to_parent); + + bNodeTree *ntree = reinterpret_cast(id); + 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_NodeTreeInterfaceItems_clear(ID *id, bNodeTreeInterface *interface, Main *bmain) +{ + interface->clear_items(); + + bNodeTree *ntree = reinterpret_cast(id); + 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_NodeTreeInterfaceItems_move( + ID *id, bNodeTreeInterface *interface, Main *bmain, bNodeTreeInterfaceItem *item, int to_index) +{ + interface->move_item(*item, to_index); + + bNodeTree *ntree = reinterpret_cast(id); + 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_NodeTreeInterfaceItems_move_to_parent(ID *id, + bNodeTreeInterface *interface, + Main *bmain, + ReportList *reports, + bNodeTreeInterfaceItem *item, + bNodeTreeInterfacePanel *parent, + int 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_index); + + bNodeTree *ntree = reinterpret_cast(id); + BKE_ntree_update_tag_interface(ntree); + ED_node_tree_propagate_change(nullptr, bmain, ntree); + WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); +} + +/* ******** Node Socket Subtypes ******** */ + +void rna_NodeTreeInterfaceSocketFloat_default_value_range( + PointerRNA *ptr, float *min, float *max, float *softmin, float *softmax) +{ + bNodeTreeInterfaceSocket *socket = static_cast(ptr->data); + bNodeSocketValueFloat *dval = static_cast(socket->socket_data); + bNodeSocketType *socket_typeinfo = nodeSocketTypeFind(socket->socket_type); + int subtype = socket_typeinfo ? socket_typeinfo->subtype : PROP_NONE; + + if (dval->max < dval->min) { + dval->max = dval->min; + } + + *min = (subtype == PROP_UNSIGNED ? 0.0f : -FLT_MAX); + *max = FLT_MAX; + *softmin = dval->min; + *softmax = dval->max; +} + +void rna_NodeTreeInterfaceSocketInt_default_value_range( + PointerRNA *ptr, int *min, int *max, int *softmin, int *softmax) +{ + bNodeTreeInterfaceSocket *socket = static_cast(ptr->data); + bNodeSocketValueInt *dval = static_cast(socket->socket_data); + bNodeSocketType *socket_typeinfo = nodeSocketTypeFind(socket->socket_type); + int subtype = socket_typeinfo ? socket_typeinfo->subtype : PROP_NONE; + + if (dval->max < dval->min) { + dval->max = dval->min; + } + + *min = (subtype == PROP_UNSIGNED ? 0 : INT_MIN); + *max = INT_MAX; + *softmin = dval->min; + *softmax = dval->max; +} + +void rna_NodeTreeInterfaceSocketVector_default_value_range( + PointerRNA *ptr, float *min, float *max, float *softmin, float *softmax) +{ + bNodeTreeInterfaceSocket *socket = static_cast(ptr->data); + bNodeSocketValueVector *dval = static_cast(socket->socket_data); + + if (dval->max < dval->min) { + dval->max = dval->min; + } + + *min = -FLT_MAX; + *max = FLT_MAX; + *softmin = dval->min; + *softmax = dval->max; +} + +/* using a context update function here, to avoid searching the node if possible */ +static void rna_NodeTreeInterfaceSocket_value_update(Main *bmain, Scene *scene, PointerRNA *ptr) +{ + /* default update */ + rna_NodeTreeInterfaceItem_update(bmain, scene, ptr); +} + +static bool rna_NodeTreeInterfaceSocketMaterial_default_value_poll(PointerRNA * /*ptr*/, + PointerRNA value) +{ + /* Do not show grease pencil materials for now. */ + Material *ma = static_cast(value.data); + return ma->gp_style == nullptr; +} + +static void rna_NodeTreeInterface_items_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) +{ + bNodeTree *ntree = reinterpret_cast(ptr->owner_id); + if (!ntree->runtime) { + return; + } + + ntree->ensure_topology_cache(); + 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); +} + +static int rna_NodeTreeInterface_items_length(PointerRNA *ptr) +{ + bNodeTree *ntree = reinterpret_cast(ptr->owner_id); + if (!ntree->runtime) { + return 0; + } + + ntree->ensure_topology_cache(); + return ntree->interface_cache().items.size(); +} + +static int rna_NodeTreeInterface_items_lookup_int(PointerRNA *ptr, int index, PointerRNA *r_ptr) +{ + bNodeTree *ntree = reinterpret_cast(ptr->owner_id); + if (!ntree->runtime) { + return 0; + } + + ntree->ensure_topology_cache(); + const blender::bke::bNodeTreeInterfaceCache &cache = ntree->interface_cache(); + if (!cache.items.index_range().contains(index)) { + return false; + } + + RNA_pointer_create(ptr->owner_id, &RNA_NodeTreeInterfaceItem, cache.items[index], r_ptr); + return true; +} + +static int rna_NodeTreeInterface_items_lookup_string(struct PointerRNA *ptr, + const char *key, + struct PointerRNA *r_ptr) +{ + bNodeTree *ntree = reinterpret_cast(ptr->owner_id); + if (!ntree->runtime) { + return 0; + } + + ntree->ensure_topology_cache(); + 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; + } + } + } + return false; +} + +#else + +static void rna_def_node_interface_item(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "NodeTreeInterfaceItem", nullptr); + RNA_def_struct_ui_text(srna, "Node Tree Interface Item", "Item in a node tree interface"); + RNA_def_struct_sdna(srna, "bNodeTreeInterfaceItem"); + RNA_def_struct_refine_func(srna, "rna_NodeTreeInterfaceItem_refine"); + RNA_def_struct_path_func(srna, "rna_NodeTreeInterfaceItem_path"); + + prop = RNA_def_property(srna, "item_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, nullptr, "item_type"); + 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"); +} + +static void rna_def_node_interface_socket(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + FunctionRNA *func; + PropertyRNA *parm; + + srna = RNA_def_struct(brna, "NodeTreeInterfaceSocket", "NodeTreeInterfaceItem"); + RNA_def_struct_ui_text(srna, "Node Tree Interface Socket", "Declaration of a node socket"); + RNA_def_struct_sdna(srna, "bNodeTreeInterfaceSocket"); + RNA_def_struct_register_funcs(srna, + "rna_NodeTreeInterfaceSocket_register", + "rna_NodeTreeInterfaceSocket_unregister", + nullptr); + RNA_def_struct_idprops_func(srna, "rna_NodeTreeInterfaceSocket_idprops"); + + 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_NodeTreeInterfaceItem_update"); + + prop = RNA_def_property(srna, "identifier", PROP_STRING, PROP_NONE); + RNA_def_property_string_funcs(prop, + "rna_NodeTreeInterfaceSocket_identifier_get", + "rna_NodeTreeInterfaceSocket_identifier_length", + nullptr); + 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, "Description", "Socket description"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceItem_update"); + + prop = RNA_def_property(srna, "socket_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, DummyRNA_DEFAULT_items); + RNA_def_property_enum_funcs(prop, + "rna_NodeTreeInterfaceSocket_socket_type_get", + "rna_NodeTreeInterfaceSocket_socket_type_set", + "rna_NodeTreeInterfaceSocket_socket_type_itemf"); + 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); + 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_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceItem_update"); + + prop = RNA_def_property(srna, "hide_value", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "flag", NODE_INTERFACE_SOCKET_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_NodeTreeInterfaceItem_update"); + + prop = RNA_def_property(srna, "hide_in_modifier", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "flag", NODE_INTERFACE_SOCKET_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_NodeTreeInterfaceItem_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_NodeTreeInterfaceItem_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_NodeTreeInterfaceItem_update"); + + /* Registered properties and functions for custom socket types. */ + prop = RNA_def_property(srna, "bl_socket_idname", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, nullptr, "socket_type"); + RNA_def_property_flag(prop, PROP_REGISTER); + 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_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); + 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, "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); + 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"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + 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_USE_SELF_ID | 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"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); +} + +static void rna_def_node_interface_panel(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "NodeTreeInterfacePanel", "NodeTreeInterfaceItem"); + RNA_def_struct_ui_text(srna, "Node Tree Interface Item", "Declaration of a node panel"); + RNA_def_struct_sdna(srna, "bNodeTreeInterfacePanel"); + + prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); + RNA_def_property_ui_text(prop, "Name", "Panel name"); + 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"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Items", "Items in the node panel"); + + prop = RNA_def_property(srna, "is_child_panel_allowed", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "flag", NODE_INTERFACE_PANEL_ALLOW_CHILD_PANELS); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text( + prop, "Is Child Panel Allowed", "True if the panel can contain child panels"); +} + +static void rna_def_node_tree_interface_items_api(StructRNA *srna) +{ + PropertyRNA *prop; + PropertyRNA *parm; + FunctionRNA *func; + + prop = RNA_def_property(srna, "active_index", PROP_INT, PROP_UNSIGNED); + RNA_def_property_int_sdna(prop, nullptr, "active_index"); + RNA_def_property_ui_text(prop, "Active Index", "Index of the active item"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_update(prop, NC_NODE, nullptr); + + prop = RNA_def_property(srna, "active", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "NodeTreeInterfaceItem"); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_pointer_funcs(prop, + "rna_NodeTreeInterfaceItems_active_get", + "rna_NodeTreeInterfaceItems_active_set", + nullptr, + nullptr); + RNA_def_property_ui_text(prop, "Active", "Active item"); + RNA_def_property_update(prop, NC_NODE, nullptr); + + func = RNA_def_function(srna, "new_socket", "rna_NodeTreeInterfaceItems_new_socket"); + RNA_def_function_ui_description(func, "Add a new socket 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 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"); + parm = RNA_def_enum(func, + "socket_type", + DummyRNA_DEFAULT_items, + 0, + "Socket Type", + "Type of socket generated on nodes"); + /* Note: itemf callback works for the function parameter, it does not require a data pointer. + */ + RNA_def_property_enum_funcs( + parm, nullptr, nullptr, "rna_NodeTreeInterfaceSocket_socket_type_itemf"); + RNA_def_pointer( + func, "parent", "NodeTreeInterfacePanel", "Parent", "Panel to add the socket in"); + /* return value */ + parm = RNA_def_pointer(func, "item", "NodeTreeInterfaceSocket", "Socket", "New socket"); + RNA_def_function_return(func, parm); + + func = RNA_def_function(srna, "new_panel", "rna_NodeTreeInterfaceItems_new_panel"); + 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", + "NodeTreeInterfacePanel", + "Parent", + "Add panel as a child of the parent panel"); + /* return value */ + parm = RNA_def_pointer(func, "item", "NodeTreeInterfacePanel", "Panel", "New panel"); + RNA_def_function_return(func, parm); + + func = RNA_def_function(srna, "copy", "rna_NodeTreeInterfaceItems_copy"); + RNA_def_function_ui_description(func, "Add a copy of an item to the interface"); + RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN | FUNC_USE_REPORTS); + parm = RNA_def_pointer(func, "item", "NodeTreeInterfaceItem", "Item", "Item to copy"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + /* return value */ + parm = RNA_def_pointer( + func, "item_copy", "NodeTreeInterfaceItem", "Item Copy", "Copy of the item"); + RNA_def_function_return(func, parm); + + func = RNA_def_function(srna, "remove", "rna_NodeTreeInterfaceItems_remove"); + RNA_def_function_ui_description(func, "Remove an item from the interface"); + 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); + RNA_def_boolean( + func, + "move_content_to_parent", + true, + "Move Content", + "If the item is a panel, move the contents to the parent instead of deleting it"); + + func = RNA_def_function(srna, "clear", "rna_NodeTreeInterfaceItems_clear"); + RNA_def_function_ui_description(func, "Remove all items from the interface"); + RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN); + + func = RNA_def_function(srna, "move", "rna_NodeTreeInterfaceItems_move"); + RNA_def_function_ui_description(func, "Move an item to another position"); + 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); + 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 | 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); + RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); +} + +static void rna_def_node_tree_interface(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "NodeTreeInterface", nullptr); + RNA_def_struct_ui_text( + srna, "Node Tree Interface", "Declaration of sockets and ui panels of a node group"); + RNA_def_struct_sdna(srna, "bNodeTreeInterface"); + + prop = RNA_def_property(srna, "ui_items", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_funcs(prop, + "rna_NodeTreeInterface_items_begin", + "rna_iterator_array_next", + "rna_iterator_array_end", + "rna_iterator_array_dereference_get", + "rna_NodeTreeInterface_items_length", + "rna_NodeTreeInterface_items_lookup_int", + "rna_NodeTreeInterface_items_lookup_string", + nullptr); + RNA_def_property_struct_type(prop, "NodeTreeInterfaceItem"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Items", "Items in the node interface"); + + rna_def_node_tree_interface_items_api(srna); +} + +void RNA_def_node_tree_interface(BlenderRNA *brna) +{ + rna_def_node_interface_item(brna); + rna_def_node_interface_socket(brna); + rna_def_node_interface_panel(brna); + rna_def_node_tree_interface(brna); + + rna_def_node_socket_interface_subtypes(brna); +} + +#endif diff --git a/source/blender/makesrna/intern/rna_nodetree.cc b/source/blender/makesrna/intern/rna_nodetree.cc index 8ba666af8b2..6946837c403 100644 --- a/source/blender/makesrna/intern/rna_nodetree.cc +++ b/source/blender/makesrna/intern/rna_nodetree.cc @@ -604,7 +604,6 @@ static const EnumPropertyItem node_cryptomatte_layer_name_items[] = { # include "DNA_scene_types.h" # include "WM_api.hh" -extern "C" { extern FunctionRNA rna_NodeTree_poll_func; extern FunctionRNA rna_NodeTree_update_func; extern FunctionRNA rna_NodeTree_get_from_context_func; @@ -619,7 +618,6 @@ extern FunctionRNA rna_Node_free_func; extern FunctionRNA rna_Node_draw_buttons_func; extern FunctionRNA rna_Node_draw_buttons_ext_func; extern FunctionRNA rna_Node_draw_label_func; -} void rna_Node_socket_update(Main *bmain, Scene * /*scene*/, PointerRNA *ptr); @@ -689,6 +687,20 @@ const EnumPropertyItem *rna_node_tree_type_itemf(void *data, return item; } +int rna_node_socket_idname_to_enum(const char *idname) +{ + int i = 0, result = -1; + NODE_SOCKET_TYPES_BEGIN (stype) { + if (STREQ(stype->idname, idname)) { + result = i; + break; + } + i++; + } + NODE_SOCKET_TYPES_END; + return result; +} + bNodeSocketType *rna_node_socket_type_from_enum(int value) { int i = 0; @@ -1275,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); @@ -10531,57 +10349,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; @@ -10595,6 +10362,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"}, @@ -10656,34 +10424,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 472b4ac5dc0..15965d995ee 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_declaration", "uiTemplateNodeTreeDeclaration"); + RNA_def_function_ui_description(func, "Show a node tree interface declaration"); + 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 f1ef2e800ea..b016519e6ea 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_cache().inputs.index_range()) { + const bNodeTreeInterfaceSocket *socket = nmd->node_group->interface_cache().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_cache().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_use_attribute = "[\"" + std::string(socket_id_esc) + nodes::input_use_attribute_suffix() + "\"]"; @@ -1134,7 +1142,7 @@ static void add_attribute_search_or_value_buttons(const bContext &C, uiLayoutSetAlignment(name_row, UI_LAYOUT_ALIGN_RIGHT); const int use_attribute = RNA_int_get(md_ptr, rna_path_use_attribute.c_str()) != 0; - if (socket.type == SOCK_BOOLEAN && !use_attribute) { + if (type == SOCK_BOOLEAN && !use_attribute) { uiItemL(name_row, "", ICON_NONE); } else { @@ -1142,7 +1150,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); } @@ -1152,7 +1160,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); } @@ -1178,11 +1186,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. */ @@ -1190,8 +1199,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); @@ -1202,7 +1211,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; @@ -1242,10 +1253,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() + "\"]"; @@ -1286,9 +1298,12 @@ 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_cache().inputs.index_range()) { + const bNodeTreeInterfaceSocket *socket = + nmd->node_group->interface_cache().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); } } @@ -1320,8 +1335,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_cache().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 fc1e575bdc4..c2a5ae0c7c0 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; @@ -33,7 +35,7 @@ StringRef input_attribute_name_suffix(); /** * \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 @@ -41,10 +43,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..44d2daa6633 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 uid; + 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 @@ -493,7 +546,7 @@ class NodeDeclaration { 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 +785,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 +796,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 +810,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..8c97b03df71 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,20 @@ 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); + SocketDeclarationPtr output_decl = socket_declaration_for_repeat_item( + item, SOCK_OUT, r_declaration.inputs.size() - 1); + 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)); } } // 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 cdc4259923c..3609516d303 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; @@ -326,20 +339,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_cache().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; } @@ -349,9 +363,9 @@ static void initialize_group_input(const bNodeTree &tree, } const IDProperty *property_use_attribute = IDP_GetPropertyFromGroup( - properties, (io_input.identifier + input_use_attribute_suffix()).c_str()); + properties, (std::string(io_input.identifier) + input_use_attribute_suffix()).c_str()); const IDProperty *property_attribute_name = IDP_GetPropertyFromGroup( - properties, (io_input.identifier + input_attribute_name_suffix()).c_str()); + properties, (std::string(io_input.identifier) + input_attribute_name_suffix()).c_str()); if (property_use_attribute == nullptr || property_attribute_name == nullptr) { init_socket_cpp_value_from_property(*property, socket_data_type, r_value); return; @@ -365,9 +379,9 @@ static void initialize_group_input(const bNodeTree &tree, return; } 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)); } @@ -398,7 +412,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; } @@ -421,7 +435,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_cache().outputs[index]; const eAttrDomain domain = (eAttrDomain)interface_socket->attribute_domain; OutputAttributeInfo output_info; output_info.field = std::move(field); @@ -572,15 +586,18 @@ bke::GeometrySet execute_geometry_nodes_on_geometry( Vector inputs_to_destruct; int input_index = -1; - for (const int i : btree.interface_inputs().index_range()) { + for (const int i : btree.interface_cache().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_cache().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); @@ -588,8 +605,8 @@ bke::GeometrySet execute_geometry_nodes_on_geometry( inputs_to_destruct.append({type, value}); } - Array output_used_inputs(btree.interface_outputs().size(), true); - for (const int i : btree.interface_outputs().index_range()) { + Array output_used_inputs(btree.interface_cache().outputs.size(), true); + for (const int i : btree.interface_cache().outputs.index_range()) { input_index++; param_inputs[input_index] = &output_used_inputs[i]; } @@ -640,26 +657,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_cache().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 @@ -680,9 +702,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( @@ -720,16 +742,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_cache().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..9e94e0a3dfc 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,11 @@ 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_cache().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 +2646,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_cache().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); } @@ -2800,7 +2803,7 @@ struct GeometryNodesLazyFunctionGraphBuilder { void handle_group_input_node(const bNode &bnode, BuildGraphParams &graph_params) { - for (const int i : btree_.interface_inputs().index_range()) { + for (const int i : btree_.interface_cache().inputs.index_range()) { const bNodeSocket &bsocket = bnode.output_socket(i); lf::OutputSocket &lf_socket = group_input_lf_node_->output(i); graph_params.lf_output_by_bsocket.add_new(&bsocket, &lf_socket); @@ -2813,8 +2816,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_cache().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); } @@ -3533,7 +3537,7 @@ struct GeometryNodesLazyFunctionGraphBuilder { for (const int i : output_indices.index_range()) { const int output_index = output_indices[i]; mapping_->attribute_set_by_geometry_output.add(output_index, &lf_node.output(i)); - debug_info.output_names.append(btree_.interface_outputs()[output_index]->name); + debug_info.output_names.append(btree_.interface_cache().outputs[output_index]->name); } } @@ -3542,7 +3546,8 @@ 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_cache().outputs; Vector cpp_types; cpp_types.append_n_times(&CPPType::get(), interface_outputs.size()); @@ -3562,7 +3567,8 @@ 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_cache().inputs; Vector cpp_types; cpp_types.append_n_times(&CPPType::get(), interface_inputs.size()); @@ -3630,7 +3636,7 @@ struct GeometryNodesLazyFunctionGraphBuilder { void build_group_input_usages(BuildGraphParams &graph_params) { const Span group_input_nodes = btree_.group_input_nodes(); - for (const int i : btree_.interface_inputs().index_range()) { + for (const int i : btree_.interface_cache().inputs.index_range()) { Vector target_usages; for (const bNode *group_input_node : group_input_nodes) { if (lf::OutputSocket *lf_socket = graph_params.usage_by_bsocket.lookup_default( @@ -3822,13 +3828,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_cache().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_cache().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..f7c17bf0f47 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,49 @@ 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) +{ + PanelDeclarationPtr dst = std::make_unique(); + dst->uid = 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 +341,35 @@ 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)); - } + 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_INPUT) { + 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)); + } + if (socket.flag & NODE_INTERFACE_SOCKET_OUTPUT) { + 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)); + } + break; + } + case NODE_INTERFACE_PANEL: { + const bNodeTreeInterfacePanel &panel = + node_interface::get_item_as(item); + 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 +572,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 +629,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 +650,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..ca1d4c2202e 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.uid = this->uid; + SET_FLAG_FROM_TEST(panel.flag, this->default_collapsed, NODE_PANEL_COLLAPSED); +} + +bool PanelDeclaration::matches(const bNodePanelState &panel) const +{ + return panel.uid == this->uid; +} + +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..17f8359d0b5 100644 --- a/source/blender/nodes/intern/node_socket.cc +++ b/source/blender/nodes/intern/node_socket.cc @@ -178,77 +178,168 @@ 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.uid == panel_decl.uid) { + /* 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->uid = -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; + } + } + /* 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; + { + Vector old_inputs = node.inputs; + Vector old_outputs = node.outputs; + Vector old_panels = Vector(node.panel_states()); + 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 +352,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 +396,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 +410,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 +420,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 +443,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 +451,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 +459,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 +481,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 +489,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 +497,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 +629,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 +665,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 +742,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 +757,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 +776,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 +791,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 +806,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 +821,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 +837,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 +854,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 +866,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 +878,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 +890,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 +902,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 +914,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/python/generic/idprop_py_ui_api.cc b/source/blender/python/generic/idprop_py_ui_api.cc index b187c0d10f8..455d1969dfb 100644 --- a/source/blender/python/generic/idprop_py_ui_api.cc +++ b/source/blender/python/generic/idprop_py_ui_api.cc @@ -439,9 +439,10 @@ static bool idprop_ui_data_update_id(IDProperty *idprop, PyObject *args, PyObjec { const char *rna_subtype = nullptr; const char *description = nullptr; - const char *kwlist[] = {"subtype", "description", nullptr}; + const char *id_type = nullptr; + const char *kwlist[] = {"subtype", "description", "id_type", nullptr}; if (!PyArg_ParseTupleAndKeywords( - args, kwargs, "|$zz:update", (char **)kwlist, &rna_subtype, &description)) + args, kwargs, "|$zzz:update", (char **)kwlist, &rna_subtype, &description, &id_type)) { return false; } @@ -455,6 +456,15 @@ static bool idprop_ui_data_update_id(IDProperty *idprop, PyObject *args, PyObjec return false; } + int id_type_tmp; + if (pyrna_enum_value_from_id( + rna_enum_id_type_items, id_type, &id_type_tmp, "IDPropertyUIManager.update") == -1) + { + return false; + } + + ui_data.id_type = short(id_type_tmp); + /* Write back to the property's UI data. */ IDP_ui_data_free_unique_contents(&ui_data_orig->base, IDP_ui_data_type(idprop), &ui_data.base); *ui_data_orig = ui_data; @@ -471,6 +481,7 @@ PyDoc_STRVAR(BPy_IDPropertyUIManager_update_doc, "precision=None, " "step=None, " "default=None, " + "id_type=None, " "description=None)\n" "\n" " Update the RNA information of the IDProperty used for interaction and\n" @@ -619,6 +630,17 @@ static void idprop_ui_data_to_dict_string(IDProperty *property, PyObject *dict) Py_DECREF(item); } +static void idprop_ui_data_to_dict_id(IDProperty *property, PyObject *dict) +{ + IDPropertyUIDataID *ui_data = (IDPropertyUIDataID *)property->ui_data; + + const char *id_type = nullptr; + RNA_enum_identifier(rna_enum_id_type_items, ui_data->id_type, &id_type); + PyObject *item = PyUnicode_FromString(id_type); + PyDict_SetItemString(dict, "id_type", item); + Py_DECREF(item); +} + PyDoc_STRVAR(BPy_IDPropertyUIManager_as_dict_doc, ".. method:: as_dict()\n" "\n" @@ -655,6 +677,7 @@ static PyObject *BPy_IDIDPropertyUIManager_as_dict(BPy_IDPropertyUIManager *self idprop_ui_data_to_dict_string(property, dict); break; case IDP_UI_DATA_TYPE_ID: + idprop_ui_data_to_dict_id(property, dict); break; case IDP_UI_DATA_TYPE_INT: idprop_ui_data_to_dict_int(property, dict); 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 95d8848cc9f..df4099adecb 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_blendfile_io.py b/tests/python/bl_blendfile_io.py index 50cd058fca8..418a49a069f 100644 --- a/tests/python/bl_blendfile_io.py +++ b/tests/python/bl_blendfile_io.py @@ -115,10 +115,11 @@ class TestIdRuntimeTag(TestHelper): assert linked_material.is_library_indirect is False link_dir = os.path.join(output_lib_path, "Mesh") - bpy.ops.wm.link(directory=link_dir, filename="LibMesh") + bpy.ops.wm.link(directory=link_dir, filename="LibMesh", instance_object_data=False) linked_mesh = bpy.data.meshes['LibMesh'] assert linked_mesh.is_library_indirect is False + assert linked_mesh.use_fake_user is False obj.data = linked_mesh obj.material_slots[0].link = 'OBJECT' @@ -131,17 +132,15 @@ class TestIdRuntimeTag(TestHelper): # so writing .blend file will have properly reset its tag to indirectly linked data. assert linked_material.is_library_indirect - # Only usage of this linked mesh is a runtime ID (object), but it is flagged as 'fake user' in its library, - # so writing .blend file will have kept its tag to directly linked data. - assert not linked_mesh.is_library_indirect + # Only usage of this linked mesh is a runtime ID (object), + # so writing .blend file will have properly reset its tag to indirectly linked data. + assert linked_mesh.is_library_indirect bpy.ops.wm.open_mainfile(filepath=output_work_path, load_ui=False) assert 'Cube' not in bpy.data.objects - assert 'LibMaterial' in bpy.data.materials # Pulled-in by the linked mesh. - linked_mesh = bpy.data.meshes['LibMesh'] - assert linked_mesh.use_fake_user is True - assert linked_mesh.is_library_indirect is False + assert 'LibMaterial' not in bpy.data.materials + assert 'libMesh' not in bpy.data.meshes TESTS = ( diff --git a/tests/python/bl_blendfile_liblink.py b/tests/python/bl_blendfile_liblink.py index 626abc27c95..6f058155c33 100644 --- a/tests/python/bl_blendfile_liblink.py +++ b/tests/python/bl_blendfile_liblink.py @@ -214,9 +214,9 @@ class TestBlendLibLinkIndirect(TestBlendLibLinkHelper): material = bpy.data.materials[0] assert material.library is not None - assert material.use_fake_user is True - assert material.users == 2 # Fake user is not cleared when linking. - assert material.is_library_indirect + assert material.use_fake_user is False # Fake user is cleared when linking. + assert material.users == 1 + assert material.is_library_indirect is True assert mesh.library is not None assert mesh.use_fake_user is False @@ -229,29 +229,28 @@ class TestBlendLibLinkIndirect(TestBlendLibLinkHelper): coll.objects.link(ob) bpy.context.scene.collection.children.link(coll) - assert material.users == 2 - assert material.is_library_indirect + assert material.users == 1 + assert material.is_library_indirect is True assert mesh.users == 1 assert mesh.is_library_indirect is False ob.material_slots[0].link = 'OBJECT' ob.material_slots[0].material = material - assert material.users == 3 + assert material.users == 2 assert material.is_library_indirect is False ob.material_slots[0].material = None - assert material.users == 2 + assert material.users == 1 # This is not properly updated whene removing a local user of linked data. assert material.is_library_indirect is False output_work_path = os.path.join(output_dir, self.unique_blendfile_name("blendfile")) bpy.ops.wm.save_as_mainfile(filepath=output_work_path, check_existing=False, compress=False) - assert material.users == 2 - # Currently linked data with 'fake user' set are considered as directly linked data. - assert not material.is_library_indirect + assert material.users == 1 + assert material.is_library_indirect is True bpy.ops.wm.open_mainfile(filepath=output_work_path, load_ui=False) @@ -264,10 +263,9 @@ class TestBlendLibLinkIndirect(TestBlendLibLinkHelper): material = bpy.data.materials[0] assert material.library is not None - assert material.use_fake_user is True - assert material.users == 2 # Fake user is not cleared when linking. - # Currently linked data with 'fake user' set are considered as directly linked data. - assert not material.is_library_indirect + assert material.use_fake_user is False # Fake user is cleared when linking. + assert material.users == 1 + assert material.is_library_indirect is True assert mesh.library is not None assert mesh.use_fake_user is False @@ -293,7 +291,7 @@ class TestBlendLibAppendBasic(TestBlendLibLinkHelper): assert len(bpy.data.materials) == 1 assert bpy.data.materials[0].library is not None - assert bpy.data.materials[0].users == 2 # Fake user is not cleared when linking. + assert bpy.data.materials[0].users == 1 # Fake user is cleared when linking. assert len(bpy.data.meshes) == 1 assert bpy.data.meshes[0].library is None assert bpy.data.meshes[0].use_fake_user is False @@ -310,7 +308,7 @@ class TestBlendLibAppendBasic(TestBlendLibLinkHelper): assert len(bpy.data.materials) == 1 assert bpy.data.materials[0].library is not None - assert bpy.data.materials[0].users == 2 # Fake user is not cleared when linking. + assert bpy.data.materials[0].users == 1 # Fake user is cleared when linking. assert len(bpy.data.meshes) == 1 assert bpy.data.meshes[0].library is None assert bpy.data.meshes[0].use_fake_user is False @@ -328,7 +326,7 @@ class TestBlendLibAppendBasic(TestBlendLibLinkHelper): assert len(bpy.data.materials) == 1 assert bpy.data.materials[0].library is not None - assert bpy.data.materials[0].users == 2 # Fake user is not cleared when linking. + assert bpy.data.materials[0].users == 1 # Fake user is cleared when linking. assert len(bpy.data.meshes) == 1 assert bpy.data.meshes[0].library is None assert bpy.data.meshes[0].use_fake_user is True @@ -345,7 +343,7 @@ class TestBlendLibAppendBasic(TestBlendLibLinkHelper): assert len(bpy.data.materials) == 1 assert bpy.data.materials[0].library is not None - assert bpy.data.materials[0].users == 2 # Fake user is not cleared when linking. + assert bpy.data.materials[0].users == 1 # Fake user is cleared when linking. assert len(bpy.data.meshes) == 1 assert bpy.data.meshes[0].library is None assert bpy.data.meshes[0].users == 1 diff --git a/tests/python/bl_node_group_interface.py b/tests/python/bl_node_group_interface.py new file mode 100644 index 00000000000..0e57eba785e --- /dev/null +++ b/tests/python/bl_node_group_interface.py @@ -0,0 +1,495 @@ +# 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")) + + # XXX Will fail when rotation sockets are no longer experimental. + # Once that happens just remove this line. + bpy.context.preferences.experimental.use_rotation_socket = True + + 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, is_input=True) + 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, is_output=True) + 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, is_output=True) + self.assertIsNotNone(out0, f"Could not create socket of type {socket_type}") + + in0 = tree.interface.new_socket("Input 0", socket_type=socket_type, is_input=True) + self.assertIsNotNone(in0, f"Could not create socket of type {socket_type}") + + in1 = tree.interface.new_socket("Input 1", socket_type=socket_type, is_input=True) + self.assertIsNotNone(in1, f"Could not create socket of type {socket_type}") + + out1 = tree.interface.new_socket("Output 1", socket_type=socket_type, is_output=True) + self.assertIsNotNone(out1, f"Could not create socket of type {socket_type}") + + inout0 = tree.interface.new_socket("Input/Output 0", socket_type=socket_type, is_output=True, is_input=True) + 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, is_input=True) + if default_value is not None: + in0.default_value = default_value + out0 = tree.interface.new_socket("Output 0", socket_type=socket_type, is_output=True) + 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, is_output=True) + tree.interface.new_socket("Input 0", socket_type=socket_type, is_input=True) + + 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, is_input=True) + tree.interface.new_socket("Output 0", socket_type=socket_type, is_output=True) + tree.interface.new_panel("Panel 1") + tree.interface.new_socket("Input 1", socket_type=socket_type, is_input=True) + tree.interface.new_panel("Panel 2") + tree.interface.new_socket("Output 1", socket_type=socket_type, is_output=True) + tree.interface.new_panel("Panel 3") + + self.assertSequenceEqual([(s.name, s.item_type) for s in tree.interface.ui_items], [ + ("Panel 0", 'PANEL'), + ("Input 0", 'SOCKET'), + ("Output 0", 'SOCKET'), + ("Panel 1", 'PANEL'), + ("Input 1", 'SOCKET'), + ("Panel 2", 'PANEL'), + ("Output 1", 'SOCKET'), + ("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, is_input=True) + 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, is_output=True) + 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, is_input=True, 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, is_output=True, 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, should produce error messages. + self.assertFalse(panel0.is_child_panel_allowed) + 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, is_input=True) + out0 = tree.interface.new_socket("Output 0", socket_type=socket_type, is_output=True) + panel0 = tree.interface.new_panel("Panel 0") + in1 = tree.interface.new_socket("Input 1", socket_type=socket_type, is_input=True, parent=panel0) + out1 = tree.interface.new_socket("Output 1", socket_type=socket_type, is_output=True, parent=panel0) + panel1 = tree.interface.new_panel("Panel 1") + in2 = tree.interface.new_socket("Input 2", socket_type=socket_type, is_input=True, parent=panel1) + out2 = tree.interface.new_socket("Output 2", socket_type=socket_type, is_output=True, 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"]) + + +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() diff --git a/tools/config/analysis/lsan.supp b/tools/config/analysis/lsan.supp index a45411baccf..5e0d263317c 100644 --- a/tools/config/analysis/lsan.supp +++ b/tools/config/analysis/lsan.supp @@ -1,11 +1,24 @@ +# Python - START leak:_PyObject_Malloc +leak:_PyObject_Calloc leak:_PyObject_Realloc leak:_PyObject_GC +leak:_PyArgv_* +leak:_PyBytes_* +leak:_Py_* +leak:_PyUnicodeWriter_* +leak:list_append +leak:list_resize leak:PyThread_allocate_lock leak:libpython* +leak:python +# Numpy +leak:PyUFunc_* +# Python - END leak:imb_exitopenexr leak:imb_filetypes_exit leak:libIlm* +leak:pxrInternal_* leak: leak:libX11* leak:libglib* @@ -14,4 +27,3 @@ leak:i965_dri leak:libdrm* leak:radeon* leak:libGLX* -# leak:libasan*