Node panels: Enable new node group interfaces #1

Closed
Lukas Tönne wants to merge 14 commits from node-panels-final into node-panels-rna

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
85 changed files with 5336 additions and 3191 deletions

View File

@ -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);

View File

@ -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="")

View File

@ -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

View File

@ -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,
)

View File

@ -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")

View File

@ -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,

View File

@ -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,
)

View File

@ -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. */

View File

@ -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);

View File

@ -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;
}
/** \} */

View File

@ -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;
}

View File

@ -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) {

View File

@ -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);

View File

@ -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);

View File

@ -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(). */

View File

@ -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;

View File

@ -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 {};
}

View File

@ -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... */

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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) {

View File

@ -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;

View File

@ -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();

View File

@ -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? */

View File

@ -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)) {

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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.
*/

View File

@ -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.

View File

@ -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

View File

@ -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;

View File

@ -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);
}

View File

@ -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)

View File

@ -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.) */

View File

@ -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;
}

View File

@ -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());

View File

@ -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(&region);
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(

View File

@ -87,9 +87,16 @@ static void add_reroute_node_fn(nodes::LinkSearchOpParams &params)
static void add_group_input_node_fn(nodes::LinkSearchOpParams &params)
{
/* Add a group input based on the connected socket, and add a new group input node. */
bNodeSocket *interface_socket = bke::ntreeAddSocketInterfaceFromSocket(
&params.node_tree, &params.node, &params.socket);
const int group_input_index = BLI_findindex(&params.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 &params)
/* 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(&params.node_tree, &group_input, socket, &params.node, &params.socket);
bke::node_socket_move_default_value(
*CTX_data_main(&params.C), params.node_tree, params.socket, *socket);
}
}
static void add_existing_group_input_fn(nodes::LinkSearchOpParams &params,
const bNodeSocket &interface_socket)
const bNodeTreeInterfaceSocket &interface_socket)
{
const int group_input_index = BLI_findindex(&params.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(&params.node_tree, &group_input, socket, &params.node, &params.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 &params) {
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);

View File

@ -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);

View File

@ -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
* \{ */

View File

@ -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);
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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) {

View File

@ -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);

View File

@ -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++;

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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)

View File

@ -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)

View File

@ -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);

View File

@ -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

View File

@ -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},

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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");

View File

@ -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

View File

@ -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);
}

View File

@ -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,

View File

@ -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;

View File

@ -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();

View File

@ -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;

View File

@ -146,6 +146,6 @@ class GatherLinkSearchOpParams {
void search_link_ops_for_basic_node(GatherLinkSearchOpParams &params);
void search_link_ops_for_declarations(GatherLinkSearchOpParams &params,
Span<SocketDeclarationPtr> declarations);
Span<SocketDeclaration *> declarations);
} // namespace blender::nodes

View File

@ -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 {

View File

@ -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);

View File

@ -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 {

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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)

View File

@ -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;

View File

@ -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;
}

View File

@ -94,7 +94,7 @@ void LinkSearchOpParams::update_and_connect_available_socket(bNode &new_node,
}
void search_link_ops_for_declarations(GatherLinkSearchOpParams &params,
Span<SocketDeclarationPtr> declarations)
Span<SocketDeclaration *> declarations)
{
const bNodeType &node_type = params.node_type();

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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 = (

View File

@ -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

View 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()

View File

@ -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*