forked from blender/blender
Node panels: Enable new node group interfaces #1
@ -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);
|
||||
|
@ -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="")
|
||||
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
)
|
||||
|
@ -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")
|
||||
|
@ -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 = {
|
||||
field_socket_types = {
|
||||
"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")
|
||||
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,
|
||||
|
@ -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,
|
||||
)
|
||||
|
||||
|
@ -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. */
|
||||
|
@ -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);
|
||||
|
@ -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<bNode *> root_frames;
|
||||
Vector<bNodeSocket *> interface_inputs;
|
||||
Vector<bNodeSocket *> 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<bNodePanelRuntime> panels;
|
||||
};
|
||||
|
||||
namespace node_tree_runtime {
|
||||
@ -469,18 +482,6 @@ inline blender::Span<const bNode *> bNodeTree::group_input_nodes() const
|
||||
return this->nodes_by_type("NodeGroupInput");
|
||||
}
|
||||
|
||||
inline blender::Span<const bNodeSocket *> bNodeTree::interface_inputs() const
|
||||
{
|
||||
BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
|
||||
return this->runtime->interface_inputs;
|
||||
}
|
||||
|
||||
inline blender::Span<const bNodeSocket *> bNodeTree::interface_outputs() const
|
||||
{
|
||||
BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
|
||||
return this->runtime->interface_outputs;
|
||||
}
|
||||
|
||||
inline blender::Span<const bNodeSocket *> bNodeTree::all_input_sockets() const
|
||||
{
|
||||
BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
|
||||
@ -545,6 +546,12 @@ inline blender::Span<bNestedNodeRef> 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<bNodePanelState> bNode::panel_states() const
|
||||
{
|
||||
return {panel_states_array, num_panel_states};
|
||||
}
|
||||
|
||||
inline blender::MutableSpan<bNodePanelState> 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;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
@ -177,12 +177,12 @@ template<typename T> 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;
|
||||
}
|
||||
|
@ -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<bNodeTree *>(id);
|
||||
ntree->tree_interface.init_data();
|
||||
ntree->runtime = MEM_new<bNodeTreeRuntime>(__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<bNodeSocket *>(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<bNodeSocket *>(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<bNodeSocket>(__func__);
|
||||
sock->runtime = MEM_new<bNodeSocketRuntime>(__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<bNodeTreeInterfaceSocket>(&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<bNodeSocketType *>(
|
||||
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<bNodePanelState *>(
|
||||
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<bNodeSocket>("socket template");
|
||||
sock->runtime = MEM_new<bNodeSocketRuntime>(__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<const bNodeTree *> &already_passed)
|
||||
@ -3935,7 +3869,7 @@ int nodeSocketLinkLimit(const bNodeSocket *sock)
|
||||
namespace blender::bke {
|
||||
|
||||
static void update_socket_declarations(ListBase *sockets,
|
||||
Span<blender::nodes::SocketDeclarationPtr> declarations)
|
||||
Span<blender::nodes::SocketDeclaration *> declarations)
|
||||
{
|
||||
int index;
|
||||
LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, sockets, index) {
|
||||
|
@ -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<bNodeTreeInterface &>(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);
|
||||
|
@ -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<const aal::RelationsInNode *> relations_by_node = get_relations_by_node(tree, scope);
|
||||
|
||||
@ -211,12 +213,14 @@ static AnonymousAttributeInferencingResult analyse_anonymous_attribute_usages(
|
||||
Vector<GeometrySource> 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);
|
||||
|
@ -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<FieldInferencingInterface> new_inferencing_interface =
|
||||
std::make_unique<FieldInferencingInterface>();
|
||||
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(). */
|
||||
|
@ -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<bNodeTreeInterfacePanel &>(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<bNodeTreeInterfacePanel &>(item);
|
||||
BLO_read_data_address(reader, &panel.name);
|
||||
BLO_read_data_address(reader, &panel.description);
|
||||
BLO_read_pointer_array(reader, reinterpret_cast<void **>(&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<bNodeTreeInterfaceItem *> old_items = this->items();
|
||||
items_num++;
|
||||
items_array = MEM_cnew_array<bNodeTreeInterfaceItem *>(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<bNodeTreeInterfaceItem *> 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<bNodeTreeInterfacePanel>(__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<bNodeTreeInterfaceItem *>(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,11 +1189,10 @@ bNodeTreeInterfaceSocket *bNodeTreeInterface::add_socket(blender::StringRefNull
|
||||
return new_socket;
|
||||
}
|
||||
|
||||
bNodeTreeInterfaceSocket *bNodeTreeInterface::insert_socket(
|
||||
blender::StringRefNull name,
|
||||
bNodeTreeInterfaceSocket *bNodeTreeInterface::insert_socket(blender::StringRefNull name,
|
||||
blender::StringRefNull description,
|
||||
blender::StringRefNull socket_type,
|
||||
const eNodeTreeInterfaceSocketFlag flag,
|
||||
const NodeTreeInterfaceSocketFlag flag,
|
||||
bNodeTreeInterfacePanel *parent,
|
||||
const int position)
|
||||
{
|
||||
@ -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<bNodeTreeInterfaceItem *>(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<bNodeTreeInterfaceItem *>(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;
|
||||
|
@ -201,6 +201,7 @@ static void update_zone_border_links(const bNodeTree &tree, bNodeTreeZones &tree
|
||||
|
||||
static std::unique_ptr<bNodeTreeZones> discover_tree_zones(const bNodeTree &tree)
|
||||
{
|
||||
tree.ensure_topology_cache();
|
||||
if (tree.has_available_link_cycle()) {
|
||||
return {};
|
||||
}
|
||||
|
@ -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... */
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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? */
|
||||
|
@ -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)) {
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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<typename ToType = AbstractViewItem>
|
||||
static ToType *from_item_handle(uiViewItemHandle *handle);
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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<NodeSocketViewItem &>(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<const NodeSocketViewItem *>(&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<AbstractViewItemDragController> create_drag_controller() const override;
|
||||
std::unique_ptr<TreeViewItemDropTarget> 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<NodePanelViewItem &>(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<const NodePanelViewItem *>(&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<AbstractViewItemDragController> create_drag_controller() const override;
|
||||
std::unique_ptr<TreeViewItemDropTarget> 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<bNodeTreeInterfaceSocket>(
|
||||
item);
|
||||
NodeSocketViewItem &socket_item = parent_item.add_tree_item<NodeSocketViewItem>(
|
||||
nodetree_, interface_, *socket);
|
||||
socket_item.set_collapsed(false);
|
||||
break;
|
||||
}
|
||||
case NODE_INTERFACE_PANEL: {
|
||||
bNodeTreeInterfacePanel *panel = node_interface::get_item_as<bNodeTreeInterfacePanel>(
|
||||
item);
|
||||
NodePanelViewItem &panel_item = parent_item.add_tree_item<NodePanelViewItem>(
|
||||
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<AbstractViewItemDragController> NodeSocketViewItem::create_drag_controller() const
|
||||
{
|
||||
return std::make_unique<NodeTreeInterfaceDragController>(
|
||||
static_cast<NodeTreeInterfaceView &>(get_tree_view()), socket_.item);
|
||||
}
|
||||
|
||||
std::unique_ptr<TreeViewItemDropTarget> NodeSocketViewItem::create_drop_target()
|
||||
{
|
||||
return std::make_unique<NodeSocketDropTarget>(
|
||||
static_cast<NodeTreeInterfaceView &>(get_tree_view()), socket_);
|
||||
}
|
||||
|
||||
std::unique_ptr<AbstractViewItemDragController> NodePanelViewItem::create_drag_controller() const
|
||||
{
|
||||
return std::make_unique<NodeTreeInterfaceDragController>(
|
||||
static_cast<NodeTreeInterfaceView &>(get_tree_view()), panel_.item);
|
||||
}
|
||||
|
||||
std::unique_ptr<TreeViewItemDropTarget> NodePanelViewItem::create_drop_target()
|
||||
{
|
||||
return std::make_unique<NodePanelDropTarget>(
|
||||
static_cast<NodeTreeInterfaceView &>(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<wmDragNodeTreeInterface>(__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<bNodeTreeInterfacePanel>(
|
||||
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<NodeTreeInterfaceView>().nodetree();
|
||||
bNodeTreeInterface &interface = get_view<NodeTreeInterfaceView>().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<wmDragNodeTreeInterface *>(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<bNodeTreeInterfacePanel>(
|
||||
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<NodeTreeInterfaceView>().nodetree();
|
||||
bNodeTreeInterface &interface = get_view<NodeTreeInterfaceView>().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<wmDragNodeTreeInterface *>(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<bNodeTree *>(ptr->owner_id);
|
||||
bNodeTreeInterface &interface = *static_cast<bNodeTreeInterface *>(ptr->data);
|
||||
|
||||
uiBlock *block = uiLayoutGetBlock(layout);
|
||||
|
||||
ui::AbstractTreeView *tree_view = UI_block_add_view(
|
||||
*block,
|
||||
"Node Tree Declaration Tree View",
|
||||
std::make_unique<blender::ui::nodes::NodeTreeInterfaceView>(nodetree, interface));
|
||||
tree_view->set_min_rows(3);
|
||||
|
||||
ui::TreeViewBuilder::build_tree_view(*tree_view, *layout);
|
||||
}
|
@ -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<uiBut *>(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)
|
||||
|
@ -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.) */
|
||||
|
@ -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,24 +2963,33 @@ 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;
|
||||
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;
|
||||
}
|
||||
if (first_input->type != SOCK_GEOMETRY) {
|
||||
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;
|
||||
}
|
||||
const bNodeSocket *first_output = (const bNodeSocket *)tree->outputs.first;
|
||||
}
|
||||
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;
|
||||
}
|
||||
if (first_output->type != SOCK_GEOMETRY) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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<ui::AbstractViewItemDragController> 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<const AssetCatalogTreeView &>(
|
||||
get_tree_view());
|
||||
|
@ -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,
|
||||
/* Callback for colors that does not depend on the socket pointer argument to get the type. */
|
||||
template<int socket_type>
|
||||
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<SOCK_FLOAT>,
|
||||
std_node_socket_color_fn<SOCK_VECTOR>,
|
||||
std_node_socket_color_fn<SOCK_RGBA>,
|
||||
std_node_socket_color_fn<SOCK_SHADER>,
|
||||
std_node_socket_color_fn<SOCK_BOOLEAN>,
|
||||
nullptr /* UNUSED */,
|
||||
std_node_socket_color_fn<SOCK_INT>,
|
||||
std_node_socket_color_fn<SOCK_STRING>,
|
||||
std_node_socket_color_fn<SOCK_OBJECT>,
|
||||
std_node_socket_color_fn<SOCK_IMAGE>,
|
||||
std_node_socket_color_fn<SOCK_GEOMETRY>,
|
||||
std_node_socket_color_fn<SOCK_COLLECTION>,
|
||||
std_node_socket_color_fn<SOCK_TEXTURE>,
|
||||
std_node_socket_color_fn<SOCK_MATERIAL>,
|
||||
std_node_socket_color_fn<SOCK_ROTATION>,
|
||||
};
|
||||
|
||||
/* 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<const bNodeTree *>(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<const bNodeTree *>(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<float2, 4> &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(
|
||||
|
@ -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,48 +105,48 @@ 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);
|
||||
bNodeSocket *new_group_input_socket = nodeFindSocket(node, in_out, socket_iface->identifier);
|
||||
if (new_group_input_socket) {
|
||||
new_group_input_socket->flag |= SOCK_HIDDEN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Hide all existing inputs in the new group input node, to only display the new one. */
|
||||
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, 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);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -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;
|
||||
node_tree.tree_interface.foreach_item([&](const bNodeTreeInterfaceItem &item) {
|
||||
if (item.item_type != NODE_INTERFACE_SOCKET) {
|
||||
return true;
|
||||
}
|
||||
const bNodeTreeInterfaceSocket &interface_socket =
|
||||
reinterpret_cast<const bNodeTreeInterfaceSocket &>(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)) {
|
||||
continue;
|
||||
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);
|
||||
|
@ -141,8 +141,10 @@ void ED_node_tree_update(const bContext *C)
|
||||
if (snode) {
|
||||
snode_set_context(*C);
|
||||
|
||||
if (snode->nodetree) {
|
||||
id_us_ensure_real(&snode->nodetree->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* id is supposed to contain a node tree */
|
||||
@ -344,104 +346,25 @@ float2 node_from_view(const bNode &node, const float2 &co)
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* Based on settings and sockets in node, set drawing rect info.
|
||||
*/
|
||||
static void node_update_basis(const bContext &C,
|
||||
const TreeDrawContext & /*tree_draw_ctx*/,
|
||||
bNodeTree &ntree,
|
||||
bNode &node,
|
||||
uiBlock &block)
|
||||
/* 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);
|
||||
|
||||
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. */
|
||||
loc.x = round(loc.x);
|
||||
loc.y = round(loc.y);
|
||||
|
||||
int dy = loc.y;
|
||||
|
||||
/* Header. */
|
||||
dy -= NODE_DY;
|
||||
|
||||
/* Add a little bit of padding above the top socket. */
|
||||
if (node.outputs.first || inputs_first) {
|
||||
dy -= NODE_DYS / 2;
|
||||
}
|
||||
|
||||
/* 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,
|
||||
@ -463,36 +386,45 @@ static void node_update_basis(const bContext &C,
|
||||
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;
|
||||
}
|
||||
|
||||
/* Input sockets. */
|
||||
for (bNodeSocket *socket : node.input_sockets()) {
|
||||
if (!socket->is_visible()) {
|
||||
continue;
|
||||
}
|
||||
PointerRNA nodeptr, sockptr;
|
||||
RNA_pointer_create(&ntree.id, &RNA_Node, &node, &nodeptr);
|
||||
RNA_pointer_create(&ntree.id, &RNA_NodeSocket, &socket, &sockptr);
|
||||
|
||||
PointerRNA sockptr;
|
||||
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. */
|
||||
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;
|
||||
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,
|
||||
loc.x + NODE_DYS,
|
||||
dy,
|
||||
locx + NODE_DYS,
|
||||
locy,
|
||||
NODE_WIDTH(node) - NODE_DY,
|
||||
NODE_DY,
|
||||
0,
|
||||
@ -507,35 +439,220 @@ static void node_update_basis(const bContext &C,
|
||||
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,
|
||||
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);
|
||||
node_socket_add_tooltip_in_node_editor(ntree, socket, *row);
|
||||
|
||||
UI_block_align_end(&block);
|
||||
int buty;
|
||||
UI_block_layout_resolve(&block, nullptr, &buty);
|
||||
|
||||
/* Ensure minimum socket height in case layout is empty. */
|
||||
buty = min_ii(buty, dy - NODE_DY);
|
||||
/* Horizontal position for input/output. */
|
||||
const float offsetx = (in_out == SOCK_IN ? 0.0f : NODE_WIDTH(node));
|
||||
/* Round the socket location to stop it from jiggling. */
|
||||
socket.runtime->location = float2(round(locx + offsetx), round(locy - NODE_DYS));
|
||||
|
||||
/* Round the socket vertical position to stop it from jiggling. */
|
||||
socket->runtime->location = float2(loc.x, round(dy - NODE_DYS));
|
||||
locy = buty - multi_input_socket_offset * 0.5;
|
||||
return true;
|
||||
}
|
||||
|
||||
dy = buty - multi_input_socket_offset * 0.5;
|
||||
/* 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<bNodeSocket *>(node.inputs.first);
|
||||
bNodeSocket *current_output = static_cast<bNodeSocket *>(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<PanelUpdate> 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<nodes::PanelDeclaration *>(
|
||||
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<nodes::SocketDeclaration *>(
|
||||
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) {
|
||||
dy -= NODE_SOCKDY;
|
||||
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) {
|
||||
dy -= NODE_DYS / 2;
|
||||
locy -= NODE_DYS / 2;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Based on settings and sockets in node, set drawing rect info.
|
||||
*/
|
||||
static void node_update_basis(const bContext &C,
|
||||
const TreeDrawContext & /*tree_draw_ctx*/,
|
||||
bNodeTree &ntree,
|
||||
bNode &node,
|
||||
uiBlock &block)
|
||||
{
|
||||
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);
|
||||
|
||||
int dy = loc.y;
|
||||
|
||||
/* Header. */
|
||||
dy -= NODE_DY;
|
||||
|
||||
if (node.declaration()) {
|
||||
node_update_basis_from_declaration(C, ntree, node, block, loc.x, dy);
|
||||
}
|
||||
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<nodes::PanelDeclaration *>(
|
||||
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<bNodePanelState *>(&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<NodeExtraInfoRow> 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);
|
||||
|
@ -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<bNodeSocket *>(io_socket_ptr.data);
|
||||
if (!io_socket) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
bNodeTree &node_tree = *reinterpret_cast<bNodeTree *>(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<int> 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<const bNodeSocket *>(active_socket_ptr.data);
|
||||
if (!active_socket) {
|
||||
return DummyRNA_NULL_items;
|
||||
}
|
||||
|
||||
const Set<int> 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<const bNodeSocket *>(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
|
||||
* \{ */
|
||||
|
@ -907,7 +907,7 @@ 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,
|
||||
static bNodeTreeInterfaceSocket *add_interface_from_socket(const bNodeTree &original_tree,
|
||||
bNodeTree &tree_for_interface,
|
||||
const bNodeSocket &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<bNodeLink *> 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,10 +1095,12 @@ 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);
|
||||
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());
|
||||
expose_sockets(node->output_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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -2259,7 +2259,7 @@ 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<nodes::SocketDeclarationPtr> socket_decls = (in_out == SOCK_IN) ? node_decl->inputs :
|
||||
Span<nodes::SocketDeclaration *> socket_decls = (in_out == SOCK_IN) ? node_decl->inputs :
|
||||
node_decl->outputs;
|
||||
int index;
|
||||
LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, sockets, index) {
|
||||
|
@ -376,7 +376,8 @@ static void connect_nested_node_to_node(const Span<bNodeTreePath *> 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);
|
||||
|
@ -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<NodeLinkItem> 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<NodeLinkItem> 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<NodeLinkItem> 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<bNodeTreeInterfaceSocket *> 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<NodeLinkItem> ui_node_link_items(NodeLinkArg *arg,
|
||||
|
||||
r_node_decl.emplace(NodeDeclaration());
|
||||
blender::nodes::build_node_declaration(*arg->node_type, *r_node_decl);
|
||||
Span<SocketDeclarationPtr> socket_decls = (in_out == SOCK_IN) ? r_node_decl->inputs :
|
||||
Span<SocketDeclaration *> 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++;
|
||||
|
@ -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;
|
||||
|
@ -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)) {
|
||||
severity = CLG_SEVERITY_INFO;
|
||||
}
|
||||
if (message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) {
|
||||
severity = CLG_SEVERITY_WARN;
|
||||
}
|
||||
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,
|
||||
CLG_SEVERITY_INFO,
|
||||
severity,
|
||||
"",
|
||||
"",
|
||||
format,
|
||||
callback_data->messageIdNumber,
|
||||
callback_data->pMessageIdName,
|
||||
callback_data->pMessage);
|
||||
enabled = true;
|
||||
}
|
||||
}
|
||||
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 ((enabled) && ((callback_data->objectCount > 0) || (callback_data->cmdBufLabelCount > 0) ||
|
||||
(callback_data->queueLabelCount > 0)))
|
||||
{
|
||||
|
||||
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);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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<bNode *> direct_children_in_frame() const;
|
||||
blender::Span<bNodePanelState> panel_states() const;
|
||||
blender::MutableSpan<bNodePanelState> 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<const bNode *> group_input_nodes() const;
|
||||
/** Inputs and outputs of the entire node group. */
|
||||
blender::Span<const bNodeSocket *> interface_inputs() const;
|
||||
blender::Span<const bNodeSocket *> 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;
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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},
|
||||
|
@ -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. */
|
||||
|
||||
/**
|
||||
|
File diff suppressed because it is too large
Load Diff
1074
source/blender/makesrna/intern/rna_node_tree_interface.cc
Normal file
1074
source/blender/makesrna/intern/rna_node_tree_interface.cc
Normal file
File diff suppressed because it is too large
Load Diff
@ -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<bNodeTree *>(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<bNodeTree *>(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<bNodeTree *>(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<bNodeTree *>(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<bNodeSocket *>(BLI_findlink(&ntree->inputs, from_index));
|
||||
if (to_index < from_index) {
|
||||
bNodeSocket *nextsock = static_cast<bNodeSocket *>(BLI_findlink(&ntree->inputs, to_index));
|
||||
if (nextsock) {
|
||||
BLI_remlink(&ntree->inputs, sock);
|
||||
BLI_insertlinkbefore(&ntree->inputs, nextsock, sock);
|
||||
}
|
||||
}
|
||||
else {
|
||||
bNodeSocket *prevsock = static_cast<bNodeSocket *>(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<bNodeSocket *>(BLI_findlink(&ntree->outputs, from_index));
|
||||
if (to_index < from_index) {
|
||||
bNodeSocket *nextsock = static_cast<bNodeSocket *>(BLI_findlink(&ntree->outputs, to_index));
|
||||
if (nextsock) {
|
||||
BLI_remlink(&ntree->outputs, sock);
|
||||
BLI_insertlinkbefore(&ntree->outputs, nextsock, sock);
|
||||
}
|
||||
}
|
||||
else {
|
||||
bNodeSocket *prevsock = static_cast<bNodeSocket *>(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");
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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<IDProperty, bke::idprop::IDPropertyDeleter> id_property_create_from_socket(
|
||||
const bNodeSocket &socket);
|
||||
const bNodeTreeInterfaceSocket &socket);
|
||||
|
||||
bke::GeometrySet execute_geometry_nodes_on_geometry(
|
||||
const bNodeTree &btree,
|
||||
|
@ -154,10 +154,18 @@ namespace aal = anonymous_attribute_lifetime;
|
||||
|
||||
using ImplicitInputValueFn = std::function<void(const bNode &node, void *r_value)>;
|
||||
|
||||
/* Socket or panel declaration. */
|
||||
class ItemDeclaration {
|
||||
public:
|
||||
virtual ~ItemDeclaration() = default;
|
||||
};
|
||||
|
||||
using ItemDeclarationPtr = std::unique_ptr<ItemDeclaration>;
|
||||
|
||||
/**
|
||||
* 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<SocketDeclaration>;
|
||||
|
||||
/**
|
||||
* 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<PanelDeclaration>;
|
||||
|
||||
class NodeDeclaration {
|
||||
public:
|
||||
Vector<SocketDeclarationPtr> inputs;
|
||||
Vector<SocketDeclarationPtr> outputs;
|
||||
/* Combined list of socket and panel declarations.
|
||||
* This determines order of sockets in the UI and panel content. */
|
||||
Vector<ItemDeclarationPtr> items;
|
||||
/* Note: inputs and outputs pointers are owned by the items list. */
|
||||
Vector<SocketDeclaration *> inputs;
|
||||
Vector<SocketDeclaration *> outputs;
|
||||
std::unique_ptr<aal::RelationsInNode> 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<SocketDeclarationPtr> sockets(eNodeSocketInOut in_out) const;
|
||||
Span<SocketDeclaration *> sockets(eNodeSocketInOut in_out) const;
|
||||
|
||||
const aal::RelationsInNode *anonymous_attribute_relations() const
|
||||
{
|
||||
@ -732,7 +785,7 @@ inline typename DeclType::Builder &NodeDeclarationBuilder::add_socket(StringRef
|
||||
static_assert(std::is_base_of_v<SocketDeclaration, DeclType>);
|
||||
using Builder = typename DeclType::Builder;
|
||||
|
||||
Vector<SocketDeclarationPtr> &declarations = in_out == SOCK_IN ? declaration_.inputs :
|
||||
Vector<SocketDeclaration *> &declarations = in_out == SOCK_IN ? declaration_.inputs :
|
||||
declaration_.outputs;
|
||||
|
||||
std::unique_ptr<DeclType> socket_decl = std::make_unique<DeclType>();
|
||||
@ -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<SocketDeclarationPtr> NodeDeclaration::sockets(eNodeSocketInOut in_out) const
|
||||
inline Span<SocketDeclaration *> NodeDeclaration::sockets(eNodeSocketInOut in_out) const
|
||||
{
|
||||
if (in_out == SOCK_IN) {
|
||||
return inputs;
|
||||
|
@ -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();
|
||||
|
@ -270,6 +270,7 @@ class ExtendBuilder : public SocketDeclarationBuilder<Extend> {
|
||||
class Custom : public SocketDeclaration {
|
||||
public:
|
||||
const char *idname_;
|
||||
std::function<void(bNode &node, bNodeSocket &socket, const char *data_path)> init_socket_fn;
|
||||
|
||||
bNodeSocket &build(bNodeTree &ntree, bNode &node) const override;
|
||||
bool matches(const bNodeSocket &socket) const override;
|
||||
|
@ -146,6 +146,6 @@ class GatherLinkSearchOpParams {
|
||||
void search_link_ops_for_basic_node(GatherLinkSearchOpParams ¶ms);
|
||||
|
||||
void search_link_ops_for_declarations(GatherLinkSearchOpParams ¶ms,
|
||||
Span<SocketDeclarationPtr> declarations);
|
||||
Span<SocketDeclaration *> declarations);
|
||||
|
||||
} // namespace blender::nodes
|
||||
|
@ -96,12 +96,20 @@ void socket_declarations_for_repeat_items(const Span<NodeRepeatItem> 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 {
|
||||
|
@ -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<const NodeGeometrySimulationOutput *>(
|
||||
output_node->storage);
|
||||
|
@ -97,11 +97,19 @@ void socket_declarations_for_simulation_items(const Span<NodeSimulationItem> 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 {
|
||||
|
@ -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<IDProperty, bke::idprop::IDPropertyDeleter> 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<const bNodeSocketValueFloat *>(
|
||||
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<IDProperty, bke::idprop::IDPropertyDeleter> id_property_create_f
|
||||
}
|
||||
case SOCK_INT: {
|
||||
const bNodeSocketValueInt *value = static_cast<const bNodeSocketValueInt *>(
|
||||
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<IDProperty, bke::idprop::IDPropertyDeleter> id_property_create_f
|
||||
}
|
||||
case SOCK_VECTOR: {
|
||||
const bNodeSocketValueVector *value = static_cast<const bNodeSocketValueVector *>(
|
||||
socket.default_value);
|
||||
socket.socket_data);
|
||||
auto property = bke::idprop::create(
|
||||
socket.identifier, Span<float>{value->value[0], value->value[1], value->value[2]});
|
||||
identifier, Span<float>{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<IDProperty, bke::idprop::IDPropertyDeleter> id_property_create_f
|
||||
}
|
||||
case SOCK_RGBA: {
|
||||
const bNodeSocketValueRGBA *value = static_cast<const bNodeSocketValueRGBA *>(
|
||||
socket.default_value);
|
||||
socket.socket_data);
|
||||
auto property = bke::idprop::create(
|
||||
socket.identifier,
|
||||
identifier,
|
||||
Span<float>{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<IDProperty, bke::idprop::IDPropertyDeleter> id_property_create_f
|
||||
}
|
||||
case SOCK_BOOLEAN: {
|
||||
const bNodeSocketValueBoolean *value = static_cast<const bNodeSocketValueBoolean *>(
|
||||
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<const bNodeSocketValueRotation *>(
|
||||
socket.default_value);
|
||||
socket.socket_data);
|
||||
auto property = bke::idprop::create(
|
||||
socket.identifier,
|
||||
identifier,
|
||||
Span<float>{value->value_euler[0], value->value_euler[1], value->value_euler[2]});
|
||||
IDPropertyUIDataFloat *ui_data = reinterpret_cast<IDPropertyUIDataFloat *>(
|
||||
IDP_ui_data_ensure(property.get()));
|
||||
@ -135,8 +137,8 @@ std::unique_ptr<IDProperty, bke::idprop::IDPropertyDeleter> id_property_create_f
|
||||
}
|
||||
case SOCK_STRING: {
|
||||
const bNodeSocketValueString *value = static_cast<const bNodeSocketValueString *>(
|
||||
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<IDProperty, bke::idprop::IDPropertyDeleter> id_property_create_f
|
||||
}
|
||||
case SOCK_OBJECT: {
|
||||
const bNodeSocketValueObject *value = static_cast<const bNodeSocketValueObject *>(
|
||||
socket.default_value);
|
||||
auto property = bke::idprop::create(socket.identifier, reinterpret_cast<ID *>(value->value));
|
||||
socket.socket_data);
|
||||
auto property = bke::idprop::create(identifier, reinterpret_cast<ID *>(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<const bNodeSocketValueCollection *>(
|
||||
socket.default_value);
|
||||
return bke::idprop::create(socket.identifier, reinterpret_cast<ID *>(value->value));
|
||||
socket.socket_data);
|
||||
return bke::idprop::create(identifier, reinterpret_cast<ID *>(value->value));
|
||||
}
|
||||
case SOCK_TEXTURE: {
|
||||
const bNodeSocketValueTexture *value = static_cast<const bNodeSocketValueTexture *>(
|
||||
socket.default_value);
|
||||
return bke::idprop::create(socket.identifier, reinterpret_cast<ID *>(value->value));
|
||||
socket.socket_data);
|
||||
return bke::idprop::create(identifier, reinterpret_cast<ID *>(value->value));
|
||||
}
|
||||
case SOCK_IMAGE: {
|
||||
const bNodeSocketValueImage *value = static_cast<const bNodeSocketValueImage *>(
|
||||
socket.default_value);
|
||||
return bke::idprop::create(socket.identifier, reinterpret_cast<ID *>(value->value));
|
||||
socket.socket_data);
|
||||
return bke::idprop::create(identifier, reinterpret_cast<ID *>(value->value));
|
||||
}
|
||||
case SOCK_MATERIAL: {
|
||||
const bNodeSocketValueMaterial *value = static_cast<const bNodeSocketValueMaterial *>(
|
||||
socket.default_value);
|
||||
return bke::idprop::create(socket.identifier, reinterpret_cast<ID *>(value->value));
|
||||
socket.socket_data);
|
||||
return bke::idprop::create(identifier, reinterpret_cast<ID *>(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<eNodeSocketDatatype>(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<eAttrDomain, OutputAttributeInfo> find_output_attributes_to
|
||||
const bNode &output_node = *tree.group_output_node();
|
||||
MultiValueMap<eAttrDomain, OutputAttributeInfo> 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<eAttrDomain, OutputAttributeInfo> 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<GMutablePointer> 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<bool> output_used_inputs(btree.interface_outputs().size(), true);
|
||||
for (const int i : btree.interface_outputs().index_range()) {
|
||||
Array<bool> 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<const bNodeSocket *> tree_inputs = tree.interface_inputs();
|
||||
const Span<const bNodeTreeInterfaceSocket *> 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<const bNodeSocket *> tree_outputs = tree.interface_outputs();
|
||||
const Span<const bNodeTreeInterfaceSocket *> 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);
|
||||
}
|
||||
|
@ -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<const CPPType *, 16> input_cpp_types;
|
||||
const Span<const bNodeSocket *> 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<const bNodeTreeInterfaceSocket *> 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<const CPPType *, 16> output_cpp_types;
|
||||
auto &debug_info = scope_.construct<GroupOutputDebugInfo>();
|
||||
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<const CPPType *, 16> output_cpp_types;
|
||||
auto &debug_info = scope_.construct<GroupOutputDebugInfo>();
|
||||
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<const bNodeSocket *> interface_outputs = btree_.interface_outputs();
|
||||
const Span<const bNodeTreeInterfaceSocket *> interface_outputs =
|
||||
btree_.interface_cache().outputs;
|
||||
|
||||
Vector<const CPPType *> cpp_types;
|
||||
cpp_types.append_n_times(&CPPType::get<bool>(), interface_outputs.size());
|
||||
@ -3562,7 +3567,8 @@ struct GeometryNodesLazyFunctionGraphBuilder {
|
||||
*/
|
||||
void build_input_usage_output_node(lf::Graph &lf_graph)
|
||||
{
|
||||
const Span<const bNodeSocket *> interface_inputs = btree_.interface_inputs();
|
||||
const Span<const bNodeTreeInterfaceSocket *> interface_inputs =
|
||||
btree_.interface_cache().inputs;
|
||||
|
||||
Vector<const CPPType *> cpp_types;
|
||||
cpp_types.append_n_times(&CPPType::get<bool>(), interface_inputs.size());
|
||||
@ -3630,7 +3636,7 @@ struct GeometryNodesLazyFunctionGraphBuilder {
|
||||
void build_group_input_usages(BuildGraphParams &graph_params)
|
||||
{
|
||||
const Span<const bNode *> 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<lf::OutputSocket *> 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;
|
||||
}
|
||||
}
|
||||
|
@ -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<ID *(const bNode &node)> get_default_id_getter(const bNodeTree &ntree,
|
||||
const bNodeSocket &io_socket)
|
||||
static std::function<ID *(const bNode &node)> 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<ID *(const bNode &node)> get_default_id_getter(const bNodeT
|
||||
return nullptr;
|
||||
}
|
||||
const bNodeTree &ntree = *reinterpret_cast<const bNodeTree *>(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<ID **>(io_socket->default_value);
|
||||
const bNodeTreeInterfaceSocket &io_socket =
|
||||
node_interface::get_item_as<bNodeTreeInterfaceSocket>(*io_item);
|
||||
return *static_cast<ID **>(io_socket.socket_data);
|
||||
};
|
||||
}
|
||||
|
||||
static SocketDeclarationPtr declaration_for_interface_socket(const bNodeTree &ntree,
|
||||
const bNodeSocket &io_socket)
|
||||
static std::function<void(bNode &node, bNodeSocket &socket, const char *data_path)>
|
||||
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<bNodeTree *>(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<bNodeTreeInterfaceSocket>(*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<bNodeSocketValueFloat>();
|
||||
const auto &value = node_interface::get_socket_data_as<bNodeSocketValueFloat>(io_socket);
|
||||
std::unique_ptr<decl::Float> decl = std::make_unique<decl::Float>();
|
||||
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<bNodeSocketValueVector>();
|
||||
const auto &value = node_interface::get_socket_data_as<bNodeSocketValueVector>(io_socket);
|
||||
std::unique_ptr<decl::Vector> decl = std::make_unique<decl::Vector>();
|
||||
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<bNodeSocketValueRGBA>();
|
||||
const auto &value = node_interface::get_socket_data_as<bNodeSocketValueRGBA>(io_socket);
|
||||
std::unique_ptr<decl::Color> decl = std::make_unique<decl::Color>();
|
||||
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<bNodeSocketValueBoolean>();
|
||||
const auto &value = node_interface::get_socket_data_as<bNodeSocketValueBoolean>(io_socket);
|
||||
std::unique_ptr<decl::Bool> decl = std::make_unique<decl::Bool>();
|
||||
decl->default_value = value.value;
|
||||
dst = std::move(decl);
|
||||
break;
|
||||
}
|
||||
case SOCK_ROTATION: {
|
||||
const auto &value = *io_socket.default_value_typed<bNodeSocketValueRotation>();
|
||||
const auto &value = node_interface::get_socket_data_as<bNodeSocketValueRotation>(io_socket);
|
||||
std::unique_ptr<decl::Rotation> decl = std::make_unique<decl::Rotation>();
|
||||
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<bNodeSocketValueInt>();
|
||||
const auto &value = node_interface::get_socket_data_as<bNodeSocketValueInt>(io_socket);
|
||||
std::unique_ptr<decl::Int> decl = std::make_unique<decl::Int>();
|
||||
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<bNodeSocketValueString>();
|
||||
const auto &value = node_interface::get_socket_data_as<bNodeSocketValueString>(io_socket);
|
||||
std::unique_ptr<decl::String> decl = std::make_unique<decl::String>();
|
||||
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<decl::Object>();
|
||||
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<decl::Image>();
|
||||
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<decl::Collection>();
|
||||
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<decl::Texture>();
|
||||
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<decl::Material>();
|
||||
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::Custom> decl = std::make_unique<decl::Custom>();
|
||||
decl->idname_ = io_socket.idname;
|
||||
dst = std::move(decl);
|
||||
auto value = std::make_unique<decl::Custom>();
|
||||
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<PanelDeclaration>();
|
||||
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));
|
||||
group->tree_interface.foreach_item([&](const bNodeTreeInterfaceItem &item) {
|
||||
switch (item.item_type) {
|
||||
case NODE_INTERFACE_SOCKET: {
|
||||
const bNodeTreeInterfaceSocket &socket =
|
||||
node_interface::get_item_as<bNodeTreeInterfaceSocket>(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));
|
||||
}
|
||||
LISTBASE_FOREACH (const bNodeSocket *, output, &group->outputs) {
|
||||
r_declaration.outputs.append(declaration_for_interface_socket(*group, *output));
|
||||
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<bNodeTreeInterfacePanel>(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;
|
||||
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<bNodeTreeInterfaceSocket>(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));
|
||||
}
|
||||
r_declaration.outputs.append(decl::create_extend_declaration(SOCK_OUT));
|
||||
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;
|
||||
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<bNodeTreeInterfaceSocket>(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));
|
||||
}
|
||||
r_declaration.inputs.append(decl::create_extend_declaration(SOCK_IN));
|
||||
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;
|
||||
}
|
||||
|
@ -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<int> geometry_inputs;
|
||||
for (const int i : declaration_.inputs.index_range()) {
|
||||
if (dynamic_cast<decl::Geometry *>(declaration_.inputs[i].get())) {
|
||||
if (dynamic_cast<decl::Geometry *>(declaration_.inputs[i])) {
|
||||
geometry_inputs.append(i);
|
||||
}
|
||||
}
|
||||
Vector<int> geometry_outputs;
|
||||
for (const int i : declaration_.outputs.index_range()) {
|
||||
if (dynamic_cast<decl::Geometry *>(declaration_.outputs[i].get())) {
|
||||
if (dynamic_cast<decl::Geometry *>(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<SocketDeclarationPtr> socket_decls) {
|
||||
const int tot_sockets = BLI_listbase_count(&sockets);
|
||||
if (tot_sockets != socket_decls.size()) {
|
||||
const bNodeSocket *current_input = static_cast<bNodeSocket *>(node.inputs.first);
|
||||
const bNodeSocket *current_output = static_cast<bNodeSocket *>(node.outputs.first);
|
||||
const bNodePanelState *current_panel = node.panel_states_array;
|
||||
for (const ItemDeclarationPtr &item_decl : items) {
|
||||
if (const SocketDeclaration *socket_decl = dynamic_cast<const SocketDeclaration *>(
|
||||
item_decl.get()))
|
||||
{
|
||||
switch (socket_decl->in_out) {
|
||||
case SOCK_IN:
|
||||
if (current_input == nullptr || !socket_decl->matches(*current_input)) {
|
||||
return false;
|
||||
}
|
||||
int i;
|
||||
LISTBASE_FOREACH_INDEX (const bNodeSocket *, socket, &sockets, i) {
|
||||
const SocketDeclaration &socket_decl = *socket_decls[i];
|
||||
if (!socket_decl.matches(*socket)) {
|
||||
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)) {
|
||||
}
|
||||
else if (const PanelDeclaration *panel_decl = dynamic_cast<const PanelDeclaration *>(
|
||||
item_decl.get()))
|
||||
{
|
||||
if (!node.panel_states().contains_ptr(current_panel) || !panel_decl->matches(*current_panel))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!check_sockets(node.outputs, outputs)) {
|
||||
++current_panel;
|
||||
}
|
||||
else {
|
||||
/* Unknown item type. */
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
}
|
||||
/* 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)
|
||||
|
@ -178,20 +178,17 @@ 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<SocketDeclarationPtr> socket_decls,
|
||||
const bool do_id_user)
|
||||
const SocketDeclaration &socket_decl,
|
||||
Vector<bNodeSocket *> &old_sockets,
|
||||
VectorSet<bNodeSocket *> &new_sockets)
|
||||
{
|
||||
Vector<bNodeSocket *> old_sockets = sockets;
|
||||
VectorSet<bNodeSocket *> 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) {
|
||||
if (old_socket.identifier == socket_decl.identifier) {
|
||||
old_sockets.remove_and_reorder(i);
|
||||
old_socket_with_same_identifier = &old_socket;
|
||||
break;
|
||||
@ -200,22 +197,22 @@ static void refresh_socket_list(bNodeTree &ntree,
|
||||
bNodeSocket *new_socket = nullptr;
|
||||
if (old_socket_with_same_identifier == nullptr) {
|
||||
/* Create a completely new socket. */
|
||||
new_socket = &socket_decl->build(ntree, node);
|
||||
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)) {
|
||||
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 {
|
||||
/* 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);
|
||||
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());
|
||||
STRNCPY(new_socket->identifier, socket_decl.identifier.c_str());
|
||||
}
|
||||
else {
|
||||
/* Move links to new socket with same identifier. */
|
||||
@ -240,15 +237,109 @@ static void refresh_socket_list(bNodeTree &ntree,
|
||||
}
|
||||
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<bNodePanelState> &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;
|
||||
}
|
||||
LISTBASE_FOREACH_MUTABLE (bNodeSocket *, old_socket, &sockets) {
|
||||
if (!new_sockets.contains(old_socket)) {
|
||||
}
|
||||
|
||||
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<ItemDeclarationPtr> item_decls,
|
||||
const bool do_id_user)
|
||||
{
|
||||
/* Count panels */
|
||||
int new_num_panels = 0;
|
||||
for (const ItemDeclarationPtr &item_decl : item_decls) {
|
||||
if (dynamic_cast<const PanelDeclaration *>(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<bNodePanelState>(new_num_panels, __func__);
|
||||
|
||||
/* Find list of sockets to add, mixture of old and new sockets. */
|
||||
VectorSet<bNodeSocket *> new_inputs;
|
||||
VectorSet<bNodeSocket *> new_outputs;
|
||||
bNodePanelState *new_panel = node.panel_states_array;
|
||||
{
|
||||
Vector<bNodeSocket *> old_inputs = node.inputs;
|
||||
Vector<bNodeSocket *> old_outputs = node.outputs;
|
||||
Vector<bNodePanelState> old_panels = Vector<bNodePanelState>(node.panel_states());
|
||||
for (const ItemDeclarationPtr &item_decl : item_decls) {
|
||||
if (const SocketDeclaration *socket_decl = dynamic_cast<const SocketDeclaration *>(
|
||||
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<const PanelDeclaration *>(
|
||||
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<bNodeSocketValueFloat>("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<bNodeSocketValueBoolean>("node socket value bool");
|
||||
dval->value = false;
|
||||
|
||||
sock->default_value = dval;
|
||||
*data = dval;
|
||||
break;
|
||||
}
|
||||
case SOCK_ROTATION: {
|
||||
bNodeSocketValueRotation *dval = MEM_cnew<bNodeSocketValueRotation>(__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<bNodeSocketValueRGBA>("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<bNodeSocketValueObject>("node socket value object");
|
||||
dval->value = nullptr;
|
||||
|
||||
sock->default_value = dval;
|
||||
*data = dval;
|
||||
break;
|
||||
}
|
||||
case SOCK_IMAGE: {
|
||||
bNodeSocketValueImage *dval = MEM_cnew<bNodeSocketValueImage>("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,91 +629,15 @@ 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,
|
||||
static void standard_node_socket_interface_init_socket(
|
||||
ID * /*id*/,
|
||||
const bNodeTreeInterfaceSocket *interface_socket,
|
||||
bNode * /*node*/,
|
||||
bNodeSocket *sock,
|
||||
const char * /*data_path*/)
|
||||
@ -526,22 +645,19 @@ static void standard_node_socket_interface_init_socket(bNodeTree * /*ntree*/,
|
||||
/* 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<bNodeSocket *>(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<bool>();
|
||||
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<ValueOrField<bool>>();
|
||||
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<bool>(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<math::Quaternion>();
|
||||
socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) {
|
||||
const auto &value = *socket.default_value_typed<bNodeSocketValueRotation>();
|
||||
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<math::Quaternion *>(r_value) = math::to_quaternion(euler);
|
||||
};
|
||||
socktype->geometry_nodes_cpp_type = &blender::CPPType::get<ValueOrField<math::Quaternion>>();
|
||||
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<math::Quaternion>(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<float>();
|
||||
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<ValueOrField<float>>();
|
||||
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<float>(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<int>();
|
||||
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<ValueOrField<int>>();
|
||||
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<int>(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<blender::float3>();
|
||||
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<ValueOrField<blender::float3>>();
|
||||
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<blender::float3>(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<blender::ColorGeometry4f>();
|
||||
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<ValueOrField<blender::ColorGeometry4f>>();
|
||||
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<blender::ColorGeometry4f>(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<std::string>();
|
||||
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<ValueOrField<std::string>>();
|
||||
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<std::string>(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<Object *>();
|
||||
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<blender::bke::GeometrySet>();
|
||||
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<Collection *>();
|
||||
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<Tex *>();
|
||||
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<Image *>();
|
||||
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<Material *>();
|
||||
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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -94,7 +94,7 @@ void LinkSearchOpParams::update_and_connect_available_socket(bNode &new_node,
|
||||
}
|
||||
|
||||
void search_link_ops_for_declarations(GatherLinkSearchOpParams ¶ms,
|
||||
Span<SocketDeclarationPtr> declarations)
|
||||
Span<SocketDeclaration *> declarations)
|
||||
{
|
||||
const bNodeType &node_type = params.node_type();
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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 = (
|
||||
|
@ -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
|
||||
|
495
tests/python/bl_node_group_interface.py
Normal file
495
tests/python/bl_node_group_interface.py
Normal file
@ -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()
|
@ -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:<unknown module>
|
||||
leak:libX11*
|
||||
leak:libglib*
|
||||
@ -14,4 +27,3 @@ leak:i965_dri
|
||||
leak:libdrm*
|
||||
leak:radeon*
|
||||
leak:libGLX*
|
||||
# leak:libasan*
|
||||
|
Loading…
Reference in New Issue
Block a user