Nodes: Panels integration with blend files and UI

Part 3/3 of #109135, #110272

Switch to new node group interfaces and deprecate old DNA and API.
This completes support for panels in node drawing and in node group
interface declarations in particular.

The new node group interface DNA and RNA code has been added in parts
1 and 2 (#110885, #110952) but has not be enabled yet. This commit
completes the integration by
* enabling the new RNA API
* using the new API in UI
* read/write new interfaces from blend files
* add versioning for backward compatibility
* add forward-compatible writing code to reconstruct old interfaces

All places accessing node group interface declarations should now be
using the new API. A runtime cache has been added that allows simple
linear access to socket inputs and outputs even when a panel hierarchy
is used.

Old DNA has been deprecated and should only be accessed for versioning
(inputs/outputs renamed to inputs_legacy/outputs_legacy to catch
errors). Versioning code ensures both backward and forward
compatibility of existing files.

The API for old interfaces is removed. The new API is very similar but
is defined on the `ntree.interface` instead of the `ntree` directly.
Breaking change notifications and detailed instructions for migrating
will be added.

A python test has been added for the node group API functions. This
includes new functionality such as creating panels and moving items
between different levels.

This patch does not yet contain panel representations in the modifier
UI. This has been tested in a separate branch and will be added with a
later PR (#108565).

Pull Request: https://projects.blender.org/blender/blender/pulls/111348
This commit is contained in:
Lukas Tönne
2023-08-30 12:37:21 +02:00
parent f77fdbff8a
commit e071288ab2
65 changed files with 4302 additions and 3473 deletions
+5 -1
View File
@@ -1218,7 +1218,11 @@ class NodeSocket(StructRNA, metaclass=RNAMetaPropGroup):
link.to_socket == self))
class NodeSocketInterface(StructRNA, metaclass=RNAMetaPropGroup):
class NodeTreeInterfaceItem(StructRNA):
__slots__ = ()
class NodeTreeInterfaceSocket(NodeTreeInterfaceItem, metaclass=RNAMetaPropGroup):
__slots__ = ()
@@ -14,8 +14,8 @@ from bpy.props import (
def build_default_empty_geometry_node_group(name):
group = bpy.data.node_groups.new(name, 'GeometryNodeTree')
group.inputs.new('NodeSocketGeometry', data_("Geometry"))
group.outputs.new('NodeSocketGeometry', data_("Geometry"))
group.interface.new_socket(data_("Geometry"), in_out={'OUTPUT'}, socket_type='NodeSocketGeometry')
group.interface.new_socket(data_("Geometry"), in_out={'INPUT'}, socket_type='NodeSocketGeometry')
input_node = group.nodes.new('NodeGroupInput')
output_node = group.nodes.new('NodeGroupOutput')
output_node.is_active_output = True
+109
View File
@@ -260,6 +260,112 @@ class NODE_OT_tree_path_parent(Operator):
return {'FINISHED'}
class NodeInterfaceOperator():
@classmethod
def poll(cls, context):
space = context.space_data
if not space or space.type != 'NODE_EDITOR' or not space.edit_tree:
return False
if space.edit_tree.is_embedded_data:
return False
return True
class NODE_OT_interface_item_new(NodeInterfaceOperator, Operator):
'''Add a new item to the interface'''
bl_idname = "node.interface_item_new"
bl_label = "New Item"
bl_options = {'REGISTER', 'UNDO'}
item_type: EnumProperty(
name="Item Type",
description="Type of the item to create",
items=[
('INPUT', "Input", ""),
('OUTPUT', "Output", ""),
('PANEL', "Panel", "")],
default='INPUT',
)
socket_type = 'NodeSocketFloat'
def execute(self, context):
snode = context.space_data
tree = snode.edit_tree
interface = tree.interface
# Remember active item and position to determine target position.
active_item = interface.active
active_pos = active_item.position if active_item else -1
if self.item_type == 'INPUT':
item = interface.new_socket("Socket", socket_type=self.socket_type, in_out={'INPUT'})
elif self.item_type == 'OUTPUT':
item = interface.new_socket("Socket", socket_type=self.socket_type, in_out={'OUTPUT'})
elif self.item_type == 'PANEL':
item = interface.new_panel("Panel")
else:
return {'CANCELLED'}
if active_item:
# Insert into active panel if possible, otherwise insert after active item.
if active_item.item_type == 'PANEL' and item.item_type != 'PANEL':
interface.move_to_parent(item, active_item, len(active_item.interface_items))
else:
interface.move_to_parent(item, active_item.parent, active_pos + 1)
interface.active = item
return {'FINISHED'}
class NODE_OT_interface_item_duplicate(NodeInterfaceOperator, Operator):
'''Add a copy of the active item to the interface'''
bl_idname = "node.interface_item_duplicate"
bl_label = "Duplicate Item"
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
if not super().poll(context):
return False
snode = context.space_data
tree = snode.edit_tree
interface = tree.interface
return interface.active is not None
def execute(self, context):
snode = context.space_data
tree = snode.edit_tree
interface = tree.interface
item = interface.active
if item:
item_copy = interface.copy(item)
interface.active = item_copy
return {'FINISHED'}
class NODE_OT_interface_item_remove(NodeInterfaceOperator, Operator):
'''Remove active item from the interface'''
bl_idname = "node.interface_item_remove"
bl_label = "Remove Item"
bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
snode = context.space_data
tree = snode.edit_tree
interface = tree.interface
item = interface.active
if item:
interface.remove(item)
interface.active_index = min(interface.active_index, len(interface.ui_items) - 1)
return {'FINISHED'}
classes = (
NodeSetting,
@@ -267,5 +373,8 @@ classes = (
NODE_OT_add_simulation_zone,
NODE_OT_add_repeat_zone,
NODE_OT_collapse_hide_unused_toggle,
NODE_OT_interface_item_new,
NODE_OT_interface_item_duplicate,
NODE_OT_interface_item_remove,
NODE_OT_tree_path_parent,
)
+44 -114
View File
@@ -859,22 +859,20 @@ class NODE_PT_overlay(Panel):
col.prop(overlay, "show_named_attributes", text="Named Attributes")
class NODE_UL_interface_sockets(bpy.types.UIList):
def draw_item(self, context, layout, _data, item, icon, _active_data, _active_propname, _index):
socket = item
color = socket.draw_color(context)
class NODE_MT_node_tree_interface_context_menu(Menu):
bl_label = "Node Tree Interface Specials"
if self.layout_type in {'DEFAULT', 'COMPACT'}:
row = layout.row(align=True)
def draw(self, _context):
layout = self.layout
row.template_node_socket(color=color)
row.prop(socket, "name", text="", emboss=False, icon_value=icon)
elif self.layout_type == 'GRID':
layout.alignment = 'CENTER'
layout.template_node_socket(color=color)
layout.operator("node.interface_item_duplicate", icon='DUPLICATE')
class NodeTreeInterfacePanel(Panel):
class NODE_PT_node_tree_interface(Panel):
bl_space_type = 'NODE_EDITOR'
bl_region_type = 'UI'
bl_category = "Group"
bl_label = "Interface"
@classmethod
def poll(cls, context):
@@ -888,119 +886,52 @@ class NodeTreeInterfacePanel(Panel):
return False
return True
def draw_socket_list(self, context, in_out, sockets_propname, active_socket_propname):
def draw(self, context):
layout = self.layout
snode = context.space_data
tree = snode.edit_tree
sockets = getattr(tree, sockets_propname)
active_socket_index = getattr(tree, active_socket_propname)
active_socket = sockets[active_socket_index] if active_socket_index >= 0 else None
split = layout.row()
split.template_list("NODE_UL_interface_sockets", in_out, tree, sockets_propname, tree, active_socket_propname)
split.template_node_tree_interface(tree.interface)
ops_col = split.column()
add_remove_col = ops_col.column(align=True)
props = add_remove_col.operator("node.tree_socket_add", icon='ADD', text="")
props.in_out = in_out
props = add_remove_col.operator("node.tree_socket_remove", icon='REMOVE', text="")
props.in_out = in_out
ops_col = split.column(align=True)
ops_col.operator_menu_enum("node.interface_item_new", "item_type", icon='ADD', text="")
ops_col.operator("node.interface_item_remove", icon='REMOVE', text="")
ops_col.separator()
ops_col.menu("NODE_MT_node_tree_interface_context_menu", icon='DOWNARROW_HLT', text="")
ops_col.separator()
up_down_col = ops_col.column(align=True)
props = up_down_col.operator("node.tree_socket_move", icon='TRIA_UP', text="")
props.in_out = in_out
props.direction = 'UP'
props = up_down_col.operator("node.tree_socket_move", icon='TRIA_DOWN', text="")
props.in_out = in_out
props.direction = 'DOWN'
if active_socket is not None:
# Mimicking property split.
layout.use_property_split = False
layout.use_property_decorate = False
layout_row = layout.row(align=True)
layout_split = layout_row.split(factor=0.4, align=True)
label_column = layout_split.column(align=True)
label_column.alignment = 'RIGHT'
# Menu to change the socket type.
label_column.label(text="Type")
property_row = layout_split.row(align=True)
props = property_row.operator_menu_enum(
"node.tree_socket_change_type",
"socket_type",
text=(iface_(active_socket.bl_label) if active_socket.bl_label
else iface_(active_socket.bl_idname)),
)
props.in_out = in_out
with context.temp_override(interface_socket=active_socket):
if bpy.ops.node.tree_socket_change_subtype.poll():
layout_row = layout.row(align=True)
layout_split = layout_row.split(factor=0.4, align=True)
label_column = layout_split.column(align=True)
label_column.alignment = 'RIGHT'
label_column.label(text="Subtype")
property_row = layout_split.row(align=True)
property_row.context_pointer_set("interface_socket", active_socket)
props = property_row.operator_menu_enum(
"node.tree_socket_change_subtype",
"socket_subtype",
text=(iface_(active_socket.bl_subtype_label) if active_socket.bl_subtype_label
else iface_(active_socket.bl_idname)),
)
active_item = tree.interface.active
if active_item is not None:
layout.use_property_split = True
layout.use_property_decorate = False
layout.prop(active_socket, "name")
# Display descriptions only for Geometry Nodes, since it's only used in the modifier panel.
if tree.type == 'GEOMETRY':
layout.prop(active_socket, "description")
field_socket_prefixes = {
"NodeSocketInt",
"NodeSocketColor",
"NodeSocketVector",
"NodeSocketBool",
"NodeSocketFloat",
}
is_field_type = any(
active_socket.bl_socket_idname.startswith(prefix)
for prefix in field_socket_prefixes
)
if is_field_type:
if in_out == 'OUT':
layout.prop(active_socket, "attribute_domain")
layout.prop(active_socket, "default_attribute_name")
active_socket.draw(context, layout)
if active_item.item_type == 'SOCKET':
layout.prop(active_item, "socket_type", text="Type")
layout.prop(active_item, "description")
layout.prop(active_item, "in_out", text="Input/Output")
# Display descriptions only for Geometry Nodes, since it's only used in the modifier panel.
if tree.type == 'GEOMETRY':
field_socket_types = {
"NodeSocketInt",
"NodeSocketColor",
"NodeSocketVector",
"NodeSocketBool",
"NodeSocketFloat",
}
if active_item.socket_type in field_socket_types:
if 'OUTPUT' in active_item.in_out:
layout.prop(active_item, "attribute_domain")
layout.prop(active_item, "default_attribute_name")
active_item.draw(context, layout)
if active_item.item_type == 'PANEL':
layout.prop(active_item, "name")
layout.prop(active_item, "description")
layout.prop(active_item, "default_closed", text="Closed by Default")
class NODE_PT_node_tree_interface_inputs(NodeTreeInterfacePanel):
bl_space_type = 'NODE_EDITOR'
bl_region_type = 'UI'
bl_category = "Group"
bl_label = "Inputs"
def draw(self, context):
self.draw_socket_list(context, "IN", "inputs", "active_input")
class NODE_PT_node_tree_interface_outputs(NodeTreeInterfacePanel):
bl_space_type = 'NODE_EDITOR'
bl_region_type = 'UI'
bl_category = "Group"
bl_label = "Outputs"
def draw(self, context):
self.draw_socket_list(context, "OUT", "outputs", "active_output")
layout.use_property_split = False
class NODE_UL_simulation_zone_items(bpy.types.UIList):
@@ -1209,6 +1140,8 @@ classes = (
NODE_PT_material_slots,
NODE_PT_geometry_node_asset_traits,
NODE_PT_node_color_presets,
NODE_MT_node_tree_interface_context_menu,
NODE_PT_node_tree_interface,
NODE_PT_active_node_generic,
NODE_PT_active_node_color,
NODE_PT_texture_mapping,
@@ -1217,9 +1150,6 @@ classes = (
NODE_PT_quality,
NODE_PT_annotation,
NODE_PT_overlay,
NODE_UL_interface_sockets,
NODE_PT_node_tree_interface_inputs,
NODE_PT_node_tree_interface_outputs,
NODE_UL_simulation_zone_items,
NODE_PT_simulation_zone_items,
NODE_UL_repeat_zone_items,
+29 -16
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
@@ -25,19 +25,9 @@ class MyCustomSocket(NodeSocket):
# Label for nice name display
bl_label = "Custom Node Socket"
# Enum items list
my_items = (
('DOWN', "Down", "Where your feet are"),
('UP', "Up", "Where your head should be"),
('LEFT', "Left", "Not right"),
('RIGHT', "Right", "Not left"),
)
my_enum_prop: bpy.props.EnumProperty(
name="Direction",
description="Just an example",
items=my_items,
default='UP',
input_value: bpy.props.FloatProperty(
name="Value",
description="Value when the socket is not connected",
)
# Optional function for drawing the socket input value
@@ -45,13 +35,35 @@ class MyCustomSocket(NodeSocket):
if self.is_output or self.is_linked:
layout.label(text=text)
else:
layout.prop(self, "my_enum_prop", text=text)
layout.prop(self, "input_value", text=text)
# Socket color
def draw_color(self, context, node):
@classmethod
def draw_color_simple(cls):
return (1.0, 0.4, 0.216, 0.5)
# Customizable interface properties to generate a socket from.
class MyCustomInterfaceSocket(NodeTreeInterfaceSocket):
# The type of socket that is generated.
bl_socket_idname = 'CustomSocketType'
default_value: bpy.props.FloatProperty(default=1.0, description="Default input value for new sockets",)
def draw(self, context, layout):
# Display properties of the interface.
layout.prop(self, "default_value")
# Set properties of newly created sockets
def init_socket(self, node, socket, data_path):
socket.input_value = self.default_value
# Use an existing socket to initialize the group interface
def from_socket(self, node, socket):
# Current value of the socket becomes the default
self.default_value = socket.input_value
# Mix-in class for all custom nodes in this tree type.
# Defines a poll function to enable instantiation.
class MyCustomTreeNode:
@@ -163,6 +175,7 @@ node_categories = [
classes = (
MyCustomTree,
MyCustomSocket,
MyCustomInterfaceSocket,
MyCustomNode,
)
@@ -29,7 +29,7 @@ extern "C" {
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
#define BLENDER_FILE_SUBVERSION 19
#define BLENDER_FILE_SUBVERSION 20
/* Minimum Blender version that supports reading file written with the current
* version. Older Blender versions will test this and cancel loading the file, showing a warning to
+14 -25
View File
@@ -115,9 +115,8 @@ using NodeDeclareFunction = void (*)(blender::nodes::NodeDeclarationBuilder &bui
using NodeDeclareDynamicFunction = void (*)(const bNodeTree &tree,
const bNode &node,
blender::nodes::NodeDeclaration &r_declaration);
using SocketGetCPPValueFunction = void (*)(const struct bNodeSocket &socket, void *r_value);
using SocketGetGeometryNodesCPPValueFunction = void (*)(const struct bNodeSocket &socket,
void *r_value);
using SocketGetCPPValueFunction = void (*)(const void *socket_value, void *r_value);
using SocketGetGeometryNodesCPPValueFunction = void (*)(const void *socket_value, void *r_value);
/* Adds socket link operations that are specific to this node type. */
using NodeGatherSocketLinkOperationsFunction =
@@ -170,18 +169,21 @@ typedef struct bNodeSocketType {
struct PointerRNA *ptr,
struct PointerRNA *node_ptr,
float *r_color);
void (*draw_color_simple)(const bNodeSocketType *socket_type, float *r_color);
void (*interface_draw)(struct bContext *C, struct uiLayout *layout, struct PointerRNA *ptr);
void (*interface_draw_color)(struct bContext *C, struct PointerRNA *ptr, float *r_color);
void (*interface_init_socket)(struct bNodeTree *ntree,
const struct bNodeSocket *interface_socket,
void (*interface_draw)(struct ID *id,
struct bNodeTreeInterfaceSocket *socket,
struct bContext *C,
struct uiLayout *layout);
void (*interface_init_socket)(struct ID *id,
const struct bNodeTreeInterfaceSocket *interface_socket,
struct bNode *node,
struct bNodeSocket *sock,
struct bNodeSocket *socket,
const char *data_path);
void (*interface_from_socket)(struct bNodeTree *ntree,
struct bNodeSocket *interface_socket,
void (*interface_from_socket)(struct ID *id,
struct bNodeTreeInterfaceSocket *interface_socket,
const struct bNode *node,
const struct bNodeSocket *sock);
const struct bNodeSocket *socket);
/* RNA integration */
ExtensionRNA ext_socket;
@@ -539,19 +541,6 @@ void ntreeBlendWrite(struct BlendWriter *writer, struct bNodeTree *ntree);
/** \} */
/* -------------------------------------------------------------------- */
/** \name Node Tree Interface
* \{ */
void ntreeRemoveSocketInterface(bNodeTree *ntree, bNodeSocket *sock);
struct bNodeSocket *ntreeAddSocketInterface(struct bNodeTree *ntree,
eNodeSocketInOut in_out,
const char *idname,
const char *name);
/** \} */
/* -------------------------------------------------------------------- */
/** \name Generic API, Nodes
* \{ */
@@ -585,7 +574,7 @@ struct GHashIterator *nodeSocketTypeGetIterator(void);
const char *nodeSocketTypeLabel(const bNodeSocketType *stype);
const char *nodeStaticSocketType(int type, int subtype);
const char *nodeStaticSocketInterfaceType(int type, int subtype);
const char *nodeStaticSocketInterfaceTypeNew(int type, int subtype);
const char *nodeStaticSocketLabel(int type, int subtype);
/* Helper macros for iterating over node types. */
+1 -31
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"
@@ -57,37 +58,6 @@ void ntreeLocalMerge(Main *bmain, bNodeTree *localtree, bNodeTree *ntree);
*/
void ntreeBlendReadData(BlendDataReader *reader, ID *owner_id, bNodeTree *ntree);
/* -------------------------------------------------------------------- */
/** \name Node Tree Interface
* \{ */
bNodeSocket *ntreeFindSocketInterface(bNodeTree *ntree,
eNodeSocketInOut in_out,
const char *identifier);
bNodeSocket *ntreeInsertSocketInterface(bNodeTree *ntree,
eNodeSocketInOut in_out,
const char *idname,
bNodeSocket *next_sock,
const char *name);
bNodeSocket *ntreeAddSocketInterfaceFromSocket(bNodeTree *ntree,
const bNode *from_node,
const bNodeSocket *from_sock);
bNodeSocket *ntreeAddSocketInterfaceFromSocketWithName(bNodeTree *ntree,
const bNode *from_node,
const bNodeSocket *from_sock,
const char *idname,
const char *name);
bNodeSocket *ntreeInsertSocketInterfaceFromSocket(bNodeTree *ntree,
bNodeSocket *next_sock,
const bNode *from_node,
const bNodeSocket *from_sock);
/** \} */
bool node_type_is_undefined(const bNode *node);
bool nodeIsStaticSocketType(const bNodeSocketType *stype);
+66 -14
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,18 @@ class bNodeSocketRuntime : NonCopyable, NonMovable {
int index_in_inout_sockets = -1;
};
class bNodePanelRuntime : NonCopyable, NonMovable {
public:
/* The vertical location of the panel in the tree, calculated while drawing the nodes and invalid
* if the node tree hasn't been drawn yet. In the node tree's "world space" (the same as
* #bNode::runtime::totr). */
float location_y;
/* Vertical start location of the panel content. */
float min_content_y;
/* Vertical end location of the panel content. */
float max_content_y;
};
/**
* Run-time data for every node. This should only contain data that is somewhat persistent (i.e.
* data that lives longer than a single depsgraph evaluation + redraw). Data that's only used in
@@ -299,6 +311,9 @@ class bNodeRuntime : NonCopyable, NonMovable {
/** Can be used to toposort a subset of nodes. */
int toposort_left_to_right_index = -1;
int toposort_right_to_left_index = -1;
/* Panel runtime state */
Array<bNodePanelRuntime> panels;
};
namespace node_tree_runtime {
@@ -469,18 +484,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 +548,24 @@ inline blender::Span<bNestedNodeRef> bNodeTree::nested_node_refs_span() const
return {this->nested_node_refs, this->nested_node_refs_num};
}
inline blender::Span<bNodeTreeInterfaceSocket *> bNodeTree::interface_inputs() const
{
BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
return this->runtime->interface_cache.inputs;
}
inline blender::Span<bNodeTreeInterfaceSocket *> bNodeTree::interface_outputs() const
{
BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
return this->runtime->interface_cache.outputs;
}
inline blender::Span<bNodeTreeInterfaceItem *> bNodeTree::interface_items() const
{
BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
return this->runtime->interface_cache.items;
}
/** \} */
/* -------------------------------------------------------------------- */
@@ -693,6 +714,16 @@ inline const blender::nodes::NodeDeclaration *bNode::declaration() const
return this->runtime->declaration;
}
inline blender::Span<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 +787,11 @@ inline bool bNodeSocket::is_available() const
return (this->flag & SOCK_UNAVAIL) == 0;
}
inline bool bNodeSocket::is_panel_collapsed() const
{
return (this->flag & SOCK_PANEL_COLLAPSED) != 0;
}
inline bool bNodeSocket::is_visible() const
{
return !this->is_hidden() && this->is_available();
@@ -852,3 +888,19 @@ inline const bNode &bNodeSocket::owner_node() const
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name #bNode Inline Methods
* \{ */
inline bool bNodePanelState::is_collapsed() const
{
return flag & NODE_PANEL_COLLAPSED;
}
inline bool bNodePanelState::is_parent_collapsed() const
{
return flag & NODE_PANEL_PARENT_COLLAPSED;
}
/** \} */
@@ -78,57 +78,125 @@ template<typename T> const T *get_item_as(const bNodeTreeInterfaceItem *item)
namespace socket_types {
constexpr const char *node_socket_data_float = "NodeSocketFloat";
constexpr const char *node_socket_data_int = "NodeSocketInt";
constexpr const char *node_socket_data_bool = "NodeSocketBool";
constexpr const char *node_socket_data_rotation = "NodeSocketRotation";
constexpr const char *node_socket_data_vector = "NodeSocketVector";
constexpr const char *node_socket_data_color = "NodeSocketColor";
constexpr const char *node_socket_data_string = "NodeSocketString";
constexpr const char *node_socket_data_object = "NodeSocketObject";
constexpr const char *node_socket_data_image = "NodeSocketImage";
constexpr const char *node_socket_data_collection = "NodeSocketCollection";
constexpr const char *node_socket_data_texture = "NodeSocketTexture";
constexpr const char *node_socket_data_material = "NodeSocketMaterial";
/* Info for generating static subtypes. */
struct bNodeSocketStaticTypeInfo {
const char *socket_identifier;
const char *interface_identifier;
eNodeSocketDatatype type;
PropertySubType subtype;
const char *label;
};
template<typename Fn> void socket_data_to_static_type(const char *socket_type, const Fn &fn)
/* Note: Socket and interface subtypes could be defined from a single central list,
* but makesrna cannot have a dependency on BKE, so this list would have to live in RNA itself,
* with BKE etc. accessing the RNA API to get the subtypes info. */
static const bNodeSocketStaticTypeInfo node_socket_subtypes[] = {
{"NodeSocketFloat", "NodeTreeInterfaceSocketFloat", SOCK_FLOAT, PROP_NONE},
{"NodeSocketFloatUnsigned", "NodeTreeInterfaceSocketFloatUnsigned", SOCK_FLOAT, PROP_UNSIGNED},
{"NodeSocketFloatPercentage",
"NodeTreeInterfaceSocketFloatPercentage",
SOCK_FLOAT,
PROP_PERCENTAGE},
{"NodeSocketFloatFactor", "NodeTreeInterfaceSocketFloatFactor", SOCK_FLOAT, PROP_FACTOR},
{"NodeSocketFloatAngle", "NodeTreeInterfaceSocketFloatAngle", SOCK_FLOAT, PROP_ANGLE},
{"NodeSocketFloatTime", "NodeTreeInterfaceSocketFloatTime", SOCK_FLOAT, PROP_TIME},
{"NodeSocketFloatTimeAbsolute",
"NodeTreeInterfaceSocketFloatTimeAbsolute",
SOCK_FLOAT,
PROP_TIME_ABSOLUTE},
{"NodeSocketFloatDistance", "NodeTreeInterfaceSocketFloatDistance", SOCK_FLOAT, PROP_DISTANCE},
{"NodeSocketInt", "NodeTreeInterfaceSocketInt", SOCK_INT, PROP_NONE},
{"NodeSocketIntUnsigned", "NodeTreeInterfaceSocketIntUnsigned", SOCK_INT, PROP_UNSIGNED},
{"NodeSocketIntPercentage", "NodeTreeInterfaceSocketIntPercentage", SOCK_INT, PROP_PERCENTAGE},
{"NodeSocketIntFactor", "NodeTreeInterfaceSocketIntFactor", SOCK_INT, PROP_FACTOR},
{"NodeSocketBool", "NodeTreeInterfaceSocketBool", SOCK_BOOLEAN, PROP_NONE},
{"NodeSocketRotation", "NodeTreeInterfaceSocketRotation", SOCK_ROTATION, PROP_NONE},
{"NodeSocketVector", "NodeTreeInterfaceSocketVector", SOCK_VECTOR, PROP_NONE},
{"NodeSocketVectorTranslation",
"NodeTreeInterfaceSocketVectorTranslation",
SOCK_VECTOR,
PROP_TRANSLATION},
{"NodeSocketVectorDirection",
"NodeTreeInterfaceSocketVectorDirection",
SOCK_VECTOR,
PROP_DIRECTION},
{"NodeSocketVectorVelocity",
"NodeTreeInterfaceSocketVectorVelocity",
SOCK_VECTOR,
PROP_VELOCITY},
{"NodeSocketVectorAcceleration",
"NodeTreeInterfaceSocketVectorAcceleration",
SOCK_VECTOR,
PROP_ACCELERATION},
{"NodeSocketVectorEuler", "NodeTreeInterfaceSocketVectorEuler", SOCK_VECTOR, PROP_EULER},
{"NodeSocketVectorXYZ", "NodeTreeInterfaceSocketVectorXYZ", SOCK_VECTOR, PROP_XYZ},
{"NodeSocketColor", "NodeTreeInterfaceSocketColor", SOCK_RGBA, PROP_NONE},
{"NodeSocketString", "NodeTreeInterfaceSocketString", SOCK_STRING, PROP_NONE},
{"NodeSocketShader", "NodeTreeInterfaceSocketShader", SOCK_SHADER, PROP_NONE},
{"NodeSocketObject", "NodeTreeInterfaceSocketObject", SOCK_OBJECT, PROP_NONE},
{"NodeSocketImage", "NodeTreeInterfaceSocketImage", SOCK_IMAGE, PROP_NONE},
{"NodeSocketGeometry", "NodeTreeInterfaceSocketGeometry", SOCK_GEOMETRY, PROP_NONE},
{"NodeSocketCollection", "NodeTreeInterfaceSocketCollection", SOCK_COLLECTION, PROP_NONE},
{"NodeSocketTexture", "NodeTreeInterfaceSocketTexture", SOCK_TEXTURE, PROP_NONE},
{"NodeSocketMaterial", "NodeTreeInterfaceSocketMaterial", SOCK_MATERIAL, PROP_NONE},
};
template<typename Fn> bool socket_data_to_static_type(const eNodeSocketDatatype type, const Fn &fn)
{
if (STREQ(socket_type, socket_types::node_socket_data_float)) {
fn.template operator()<bNodeSocketValueFloat>();
switch (type) {
case SOCK_FLOAT:
fn.template operator()<bNodeSocketValueFloat>();
return true;
case SOCK_INT:
fn.template operator()<bNodeSocketValueInt>();
return true;
case SOCK_BOOLEAN:
fn.template operator()<bNodeSocketValueBoolean>();
return true;
case SOCK_ROTATION:
fn.template operator()<bNodeSocketValueRotation>();
return true;
case SOCK_VECTOR:
fn.template operator()<bNodeSocketValueVector>();
return true;
case SOCK_RGBA:
fn.template operator()<bNodeSocketValueRGBA>();
return true;
case SOCK_STRING:
fn.template operator()<bNodeSocketValueString>();
return true;
case SOCK_OBJECT:
fn.template operator()<bNodeSocketValueObject>();
return true;
case SOCK_IMAGE:
fn.template operator()<bNodeSocketValueImage>();
return true;
case SOCK_COLLECTION:
fn.template operator()<bNodeSocketValueCollection>();
return true;
case SOCK_TEXTURE:
fn.template operator()<bNodeSocketValueTexture>();
return true;
case SOCK_MATERIAL:
fn.template operator()<bNodeSocketValueMaterial>();
return true;
case SOCK_CUSTOM:
case SOCK_SHADER:
case SOCK_GEOMETRY:
return true;
}
else if (STREQ(socket_type, socket_types::node_socket_data_int)) {
fn.template operator()<bNodeSocketValueInt>();
}
else if (STREQ(socket_type, socket_types::node_socket_data_bool)) {
fn.template operator()<bNodeSocketValueBoolean>();
}
else if (STREQ(socket_type, socket_types::node_socket_data_rotation)) {
fn.template operator()<bNodeSocketValueRotation>();
}
else if (STREQ(socket_type, socket_types::node_socket_data_vector)) {
fn.template operator()<bNodeSocketValueVector>();
}
else if (STREQ(socket_type, socket_types::node_socket_data_color)) {
fn.template operator()<bNodeSocketValueRGBA>();
}
else if (STREQ(socket_type, socket_types::node_socket_data_string)) {
fn.template operator()<bNodeSocketValueString>();
}
else if (STREQ(socket_type, socket_types::node_socket_data_object)) {
fn.template operator()<bNodeSocketValueObject>();
}
else if (STREQ(socket_type, socket_types::node_socket_data_image)) {
fn.template operator()<bNodeSocketValueImage>();
}
else if (STREQ(socket_type, socket_types::node_socket_data_collection)) {
fn.template operator()<bNodeSocketValueCollection>();
}
else if (STREQ(socket_type, socket_types::node_socket_data_texture)) {
fn.template operator()<bNodeSocketValueTexture>();
}
else if (STREQ(socket_type, socket_types::node_socket_data_material)) {
fn.template operator()<bNodeSocketValueMaterial>();
return false;
}
template<typename Fn> bool socket_data_to_static_type(const char *socket_type, const Fn &fn)
{
for (const bNodeSocketStaticTypeInfo &info : node_socket_subtypes) {
if (STREQ(socket_type, info.socket_identifier)) {
return socket_data_to_static_type(info.type, fn);
}
}
return false;
}
namespace detail {
@@ -177,12 +245,12 @@ template<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 +261,8 @@ inline bNodeTreeInterfaceSocket *add_interface_socket_from_node(bNodeTree &ntree
}
const bNodeSocketType *typeinfo = iosock->socket_typeinfo();
if (typeinfo->interface_from_socket) {
/* XXX Enable when bNodeSocketType callbacks have been updated. */
typeinfo->interface_from_socket(&ntree.id, iosock, &from_node, &from_sock);
UNUSED_VARS(from_sock);
// typeinfo->interface_from_socket(ntree.id, iosock, &from_node, &from_sock);
}
return iosock;
}
+229 -281
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, const bNodeSocket *sock);
static CLG_LogRef LOG = {"bke.node"};
namespace blender::bke {
@@ -129,15 +133,17 @@ bNodeSocketType NodeSocketTypeUndefined;
namespace blender::bke {
static void ntree_set_typeinfo(bNodeTree *ntree, bNodeTreeType *typeinfo);
static void node_socket_set_typeinfo(bNodeTree *ntree,
bNodeSocket *sock,
bNodeSocketType *typeinfo);
static void node_socket_copy(bNodeSocket *sock_dst, const bNodeSocket *sock_src, const int flag);
static void free_localized_node_groups(bNodeTree *ntree);
static void node_socket_interface_free(bNodeTree * /*ntree*/,
bNodeSocket *sock,
const bool do_id_user);
static bool socket_id_user_decrement(bNodeSocket *sock);
static void ntree_init_data(ID *id)
{
bNodeTree *ntree = reinterpret_cast<bNodeTree *>(id);
ntree->tree_interface.init_data();
ntree->runtime = MEM_new<bNodeTreeRuntime>(__func__);
ntree_set_typeinfo(ntree, nullptr);
}
@@ -190,20 +196,7 @@ static void ntree_copy_data(Main * /*bmain*/, ID *id_dst, const ID *id_src, cons
nodeDeclarationEnsure(ntree_dst, node);
}
/* copy interface sockets */
BLI_listbase_clear(&ntree_dst->inputs);
LISTBASE_FOREACH (const bNodeSocket *, src_socket, &ntree_src->inputs) {
bNodeSocket *dst_socket = static_cast<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);
/* copy preview hash */
if (ntree_src->previews && (flag & LIB_ID_COPY_NO_PREVIEW) == 0) {
bNodeInstanceHashIterator iter;
@@ -295,15 +288,7 @@ static void ntree_free_data(ID *id)
node_free_node(ntree, node);
}
/* free interface sockets */
LISTBASE_FOREACH_MUTABLE (bNodeSocket *, sock, &ntree->inputs) {
node_socket_interface_free(ntree, sock, false);
MEM_freeN(sock);
}
LISTBASE_FOREACH_MUTABLE (bNodeSocket *, sock, &ntree->outputs) {
node_socket_interface_free(ntree, sock, false);
MEM_freeN(sock);
}
ntree->tree_interface.free_data();
/* free preview hash */
if (ntree->previews) {
@@ -402,12 +387,7 @@ static void node_foreach_id(ID *id, LibraryForeachIDData *data)
}
}
LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs) {
BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, library_foreach_node_socket(data, sock));
}
LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs) {
BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, library_foreach_node_socket(data, sock));
}
ntree->tree_interface.foreach_id(data);
}
static void node_foreach_cache(ID *id,
@@ -474,7 +454,155 @@ static ID **node_owner_pointer_get(ID *id)
} // namespace blender::bke
static void write_node_socket_default_value(BlendWriter *writer, bNodeSocket *sock)
namespace blender::bke::forward_compat {
static void write_node_socket_interface(BlendWriter *writer, const bNodeSocket *sock)
{
BLO_write_struct(writer, bNodeSocket, sock);
if (sock->prop) {
IDP_BlendWrite(writer, sock->prop);
}
BLO_write_string(writer, sock->default_attribute_name);
write_node_socket_default_value(writer, sock);
}
/* Construct a bNodeSocket that represents a node group socket the old way. */
static bNodeSocket *make_socket(bNodeTree *ntree,
const eNodeSocketInOut in_out,
const StringRef idname,
const StringRef name,
const StringRef identifier)
{
bNodeSocketType *stype = nodeSocketTypeFind(idname.data());
if (stype == nullptr) {
return nullptr;
}
bNodeSocket *sock = MEM_cnew<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 construct_interface_as_legacy_sockets(bNodeTree *ntree)
{
BLI_assert(BLI_listbase_is_empty(&ntree->inputs_legacy));
BLI_assert(BLI_listbase_is_empty(&ntree->outputs_legacy));
auto make_legacy_socket = [&](const bNodeTreeInterfaceSocket &socket,
eNodeSocketInOut in_out) -> bNodeSocket * {
bNodeSocket *iosock = make_socket(
ntree, in_out, socket.socket_type, socket.name, socket.identifier);
if (!iosock) {
return nullptr;
}
if (socket.description) {
BLI_strncpy(iosock->description, socket.description, sizeof(iosock->description));
}
node_socket_copy_default_value_data(
eNodeSocketDatatype(iosock->typeinfo->type), iosock->default_value, socket.socket_data);
if (socket.properties) {
iosock->prop = IDP_CopyProperty(socket.properties);
}
SET_FLAG_FROM_TEST(
iosock->flag, socket.flag & NODE_INTERFACE_SOCKET_HIDE_VALUE, SOCK_HIDE_VALUE);
SET_FLAG_FROM_TEST(
iosock->flag, socket.flag & NODE_INTERFACE_SOCKET_HIDE_IN_MODIFIER, SOCK_HIDE_IN_MODIFIER);
iosock->attribute_domain = socket.attribute_domain;
if (socket.default_attribute_name) {
BLI_strncpy(iosock->default_attribute_name,
socket.default_attribute_name,
sizeof(iosock->default_attribute_name));
}
return iosock;
};
/* Construct inputs/outputs socket lists in the node tree. */
ntree->tree_interface.foreach_item([&](const bNodeTreeInterfaceItem &item) {
if (const bNodeTreeInterfaceSocket *socket =
node_interface::get_item_as<bNodeTreeInterfaceSocket>(&item))
{
if (socket->flag & NODE_INTERFACE_SOCKET_INPUT) {
if (bNodeSocket *legacy_socket = make_legacy_socket(*socket, SOCK_IN)) {
BLI_addtail(&ntree->inputs_legacy, legacy_socket);
}
}
if (socket->flag & NODE_INTERFACE_SOCKET_OUTPUT) {
if (bNodeSocket *legacy_socket = make_legacy_socket(*socket, SOCK_OUT)) {
BLI_addtail(&ntree->outputs_legacy, legacy_socket);
}
}
}
return true;
});
}
static void write_legacy_sockets(BlendWriter *writer, bNodeTree *ntree)
{
/* Write inputs/outputs */
LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs_legacy) {
write_node_socket_interface(writer, sock);
}
LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs_legacy) {
write_node_socket_interface(writer, sock);
}
}
static void legacy_socket_interface_free(bNodeSocket *sock)
{
if (sock->prop) {
IDP_FreeProperty_ex(sock->prop, false);
}
if (sock->default_value) {
MEM_freeN(sock->default_value);
}
if (sock->default_attribute_name) {
MEM_freeN(sock->default_attribute_name);
}
MEM_delete(sock->runtime);
}
static void cleanup_legacy_sockets(bNodeTree *ntree)
{
/* Clean up temporary inputs/outputs. */
LISTBASE_FOREACH_MUTABLE (bNodeSocket *, socket, &ntree->inputs_legacy) {
legacy_socket_interface_free(socket);
MEM_freeN(socket);
}
LISTBASE_FOREACH_MUTABLE (bNodeSocket *, socket, &ntree->outputs_legacy) {
legacy_socket_interface_free(socket);
MEM_freeN(socket);
}
BLI_listbase_clear(&ntree->inputs_legacy);
BLI_listbase_clear(&ntree->outputs_legacy);
}
} // namespace blender::bke::forward_compat
static void write_node_socket_default_value(BlendWriter *writer, const bNodeSocket *sock)
{
if (sock->default_value == nullptr) {
return;
@@ -527,7 +655,7 @@ static void write_node_socket_default_value(BlendWriter *writer, bNodeSocket *so
}
}
static void write_node_socket(BlendWriter *writer, bNodeSocket *sock)
static void write_node_socket(BlendWriter *writer, const bNodeSocket *sock)
{
BLO_write_struct(writer, bNodeSocket, sock);
@@ -541,19 +669,6 @@ static void write_node_socket(BlendWriter *writer, bNodeSocket *sock)
write_node_socket_default_value(writer, sock);
}
static void write_node_socket_interface(BlendWriter *writer, bNodeSocket *sock)
{
BLO_write_struct(writer, bNodeSocket, sock);
if (sock->prop) {
IDP_BlendWrite(writer, sock->prop);
}
BLO_write_string(writer, sock->default_attribute_name);
write_node_socket_default_value(writer, sock);
}
void ntreeBlendWrite(BlendWriter *writer, bNodeTree *ntree)
{
BKE_id_blend_write(writer, &ntree->id);
@@ -579,6 +694,8 @@ void ntreeBlendWrite(BlendWriter *writer, bNodeTree *ntree)
LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
write_node_socket(writer, sock);
}
BLO_write_struct_array(
writer, bNodePanelState, node->num_panel_states, node->panel_states_array);
if (node->storage) {
if (ELEM(ntree->type, NTREE_SHADER, NTREE_GEOMETRY) &&
@@ -690,14 +807,10 @@ void ntreeBlendWrite(BlendWriter *writer, bNodeTree *ntree)
BLO_write_struct(writer, bNodeLink, link);
}
LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs) {
write_node_socket_interface(writer, sock);
ntree->tree_interface.write(writer);
if (!BLO_write_is_undo(writer)) {
blender::bke::forward_compat::write_legacy_sockets(writer, ntree);
}
LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs) {
write_node_socket_interface(writer, sock);
}
BLO_write_struct(writer, GeometryNodeAssetTraits, ntree->geometry_node_asset_traits);
BLO_write_struct_array(
writer, bNestedNodeRef, ntree->nested_node_refs_num, ntree->nested_node_refs);
@@ -715,9 +828,20 @@ static void ntree_blend_write(BlendWriter *writer, ID *id, const void *id_addres
ntree->typeinfo = nullptr;
ntree->runtime->execdata = nullptr;
if (!BLO_write_is_undo(writer)) {
/* Generate legacy inputs/outputs socket ListBase for forward compatibility.
* Note: this has to happen before writing the ntree struct itself so that the ListBase
* first/last pointers are valid. */
blender::bke::forward_compat::construct_interface_as_legacy_sockets(ntree);
}
BLO_write_id_struct(writer, bNodeTree, id_address, &ntree->id);
ntreeBlendWrite(writer, ntree);
if (!BLO_write_is_undo(writer)) {
blender::bke::forward_compat::cleanup_legacy_sockets(ntree);
}
}
static void direct_link_node_socket(BlendDataReader *reader, bNodeSocket *sock)
@@ -786,6 +910,7 @@ void ntreeBlendReadData(BlendDataReader *reader, ID *owner_id, bNodeTree *ntree)
BLO_read_list(reader, &node->inputs);
BLO_read_list(reader, &node->outputs);
BLO_read_data_address(reader, &node->panel_states_array);
BLO_read_data_address(reader, &node->prop);
IDP_BlendDataRead(reader, &node->prop);
@@ -911,16 +1036,18 @@ void ntreeBlendReadData(BlendDataReader *reader, ID *owner_id, bNodeTree *ntree)
}
}
/* interface socket lists */
BLO_read_list(reader, &ntree->inputs);
BLO_read_list(reader, &ntree->outputs);
LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs) {
/* Read legacy interface socket lists for versioning. */
BLO_read_list(reader, &ntree->inputs_legacy);
BLO_read_list(reader, &ntree->outputs_legacy);
LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs_legacy) {
direct_link_node_socket(reader, sock);
}
LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs) {
LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs_legacy) {
direct_link_node_socket(reader, sock);
}
ntree->tree_interface.read_data(reader);
LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
BLO_read_data_address(reader, &link->fromnode);
BLO_read_data_address(reader, &link->tonode);
@@ -976,12 +1103,13 @@ static void node_tree_asset_pre_save(void *asset_ptr, AssetMetaData *asset_data)
BKE_asset_metadata_idprop_ensure(asset_data, idprop::create("type", node_tree.type).release());
auto inputs = idprop::create_group("inputs");
auto outputs = idprop::create_group("outputs");
LISTBASE_FOREACH (const bNodeSocket *, socket, &node_tree.inputs) {
auto property = idprop::create(socket->name, socket->typeinfo->idname);
node_tree.ensure_topology_cache();
for (const bNodeTreeInterfaceSocket *socket : node_tree.interface_inputs()) {
auto property = idprop::create(socket->name, socket->socket_type);
IDP_AddToGroup(inputs.get(), property.release());
}
LISTBASE_FOREACH (const bNodeSocket *, socket, &node_tree.outputs) {
auto property = idprop::create(socket->name, socket->typeinfo->idname);
for (const bNodeTreeInterfaceSocket *socket : node_tree.interface_outputs()) {
auto property = idprop::create(socket->name, socket->socket_type);
IDP_AddToGroup(outputs.get(), property.release());
}
BKE_asset_metadata_idprop_ensure(asset_data, inputs.release());
@@ -1221,18 +1349,6 @@ static void update_typeinfo(Main *bmain,
}
}
}
/* initialize tree sockets */
LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs) {
if (socktype && STREQ(sock->idname, socktype->idname)) {
node_socket_set_typeinfo(ntree, sock, unregister ? nullptr : socktype);
}
}
LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs) {
if (socktype && STREQ(sock->idname, socktype->idname)) {
node_socket_set_typeinfo(ntree, sock, unregister ? nullptr : socktype);
}
}
}
FOREACH_NODETREE_END;
}
@@ -1253,13 +1369,6 @@ void ntreeSetTypes(const bContext *C, bNodeTree *ntree)
blender::bke::node_socket_set_typeinfo(ntree, sock, nodeSocketTypeFind(sock->idname));
}
}
LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs) {
blender::bke::node_socket_set_typeinfo(ntree, sock, nodeSocketTypeFind(sock->idname));
}
LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs) {
blender::bke::node_socket_set_typeinfo(ntree, sock, nodeSocketTypeFind(sock->idname));
}
}
namespace blender::bke {
@@ -1424,7 +1533,7 @@ GHashIterator *nodeTypeGetIterator()
bNodeSocketType *nodeSocketTypeFind(const char *idname)
{
if (idname[0]) {
if (idname && idname[0]) {
bNodeSocketType *st = static_cast<bNodeSocketType *>(
BLI_ghash_lookup(blender::bke::nodesockettypes_hash, idname));
if (st) {
@@ -1864,81 +1973,81 @@ const char *nodeStaticSocketType(const int type, const int subtype)
return nullptr;
}
const char *nodeStaticSocketInterfaceType(const int type, const int subtype)
const char *nodeStaticSocketInterfaceTypeNew(const int type, const int subtype)
{
switch (eNodeSocketDatatype(type)) {
case SOCK_FLOAT:
switch (PropertySubType(subtype)) {
case PROP_UNSIGNED:
return "NodeSocketInterfaceFloatUnsigned";
return "NodeTreeInterfaceSocketFloatUnsigned";
case PROP_PERCENTAGE:
return "NodeSocketInterfaceFloatPercentage";
return "NodeTreeInterfaceSocketFloatPercentage";
case PROP_FACTOR:
return "NodeSocketInterfaceFloatFactor";
return "NodeTreeInterfaceSocketFloatFactor";
case PROP_ANGLE:
return "NodeSocketInterfaceFloatAngle";
return "NodeTreeInterfaceSocketFloatAngle";
case PROP_TIME:
return "NodeSocketInterfaceFloatTime";
return "NodeTreeInterfaceSocketFloatTime";
case PROP_TIME_ABSOLUTE:
return "NodeSocketInterfaceFloatTimeAbsolute";
return "NodeTreeInterfaceSocketFloatTimeAbsolute";
case PROP_DISTANCE:
return "NodeSocketInterfaceFloatDistance";
return "NodeTreeInterfaceSocketFloatDistance";
case PROP_NONE:
default:
return "NodeSocketInterfaceFloat";
return "NodeTreeInterfaceSocketFloat";
}
case SOCK_INT:
switch (PropertySubType(subtype)) {
case PROP_UNSIGNED:
return "NodeSocketInterfaceIntUnsigned";
return "NodeTreeInterfaceSocketIntUnsigned";
case PROP_PERCENTAGE:
return "NodeSocketInterfaceIntPercentage";
return "NodeTreeInterfaceSocketIntPercentage";
case PROP_FACTOR:
return "NodeSocketInterfaceIntFactor";
return "NodeTreeInterfaceSocketIntFactor";
case PROP_NONE:
default:
return "NodeSocketInterfaceInt";
return "NodeTreeInterfaceSocketInt";
}
case SOCK_BOOLEAN:
return "NodeSocketInterfaceBool";
return "NodeTreeInterfaceSocketBool";
case SOCK_ROTATION:
return "NodeSocketInterfaceRotation";
return "NodeTreeInterfaceSocketRotation";
case SOCK_VECTOR:
switch (PropertySubType(subtype)) {
case PROP_TRANSLATION:
return "NodeSocketInterfaceVectorTranslation";
return "NodeTreeInterfaceSocketVectorTranslation";
case PROP_DIRECTION:
return "NodeSocketInterfaceVectorDirection";
return "NodeTreeInterfaceSocketVectorDirection";
case PROP_VELOCITY:
return "NodeSocketInterfaceVectorVelocity";
return "NodeTreeInterfaceSocketVectorVelocity";
case PROP_ACCELERATION:
return "NodeSocketInterfaceVectorAcceleration";
return "NodeTreeInterfaceSocketVectorAcceleration";
case PROP_EULER:
return "NodeSocketInterfaceVectorEuler";
return "NodeTreeInterfaceSocketVectorEuler";
case PROP_XYZ:
return "NodeSocketInterfaceVectorXYZ";
return "NodeTreeInterfaceSocketVectorXYZ";
case PROP_NONE:
default:
return "NodeSocketInterfaceVector";
return "NodeTreeInterfaceSocketVector";
}
case SOCK_RGBA:
return "NodeSocketInterfaceColor";
return "NodeTreeInterfaceSocketColor";
case SOCK_STRING:
return "NodeSocketInterfaceString";
return "NodeTreeInterfaceSocketString";
case SOCK_SHADER:
return "NodeSocketInterfaceShader";
return "NodeTreeInterfaceSocketShader";
case SOCK_OBJECT:
return "NodeSocketInterfaceObject";
return "NodeTreeInterfaceSocketObject";
case SOCK_IMAGE:
return "NodeSocketInterfaceImage";
return "NodeTreeInterfaceSocketImage";
case SOCK_GEOMETRY:
return "NodeSocketInterfaceGeometry";
return "NodeTreeInterfaceSocketGeometry";
case SOCK_COLLECTION:
return "NodeSocketInterfaceCollection";
return "NodeTreeInterfaceSocketCollection";
case SOCK_TEXTURE:
return "NodeSocketInterfaceTexture";
return "NodeTreeInterfaceSocketTexture";
case SOCK_MATERIAL:
return "NodeSocketInterfaceMaterial";
return "NodeTreeInterfaceSocketMaterial";
case SOCK_CUSTOM:
break;
}
@@ -2387,6 +2496,9 @@ bNode *node_copy_with_mapping(bNodeTree *dst_tree,
node_dst->prop = IDP_CopyProperty_ex(node_src.prop, flag);
}
node_dst->panel_states_array = static_cast<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;
@@ -3149,6 +3261,8 @@ void node_free_node(bNodeTree *ntree, bNode *node)
MEM_freeN(sock);
}
MEM_SAFE_FREE(node->panel_states_array);
if (node->prop) {
/* Remember, no ID user refcount management here! */
IDP_FreePropertyContent_ex(node->prop, false);
@@ -3248,26 +3362,6 @@ void nodeRemoveNode(Main *bmain, bNodeTree *ntree, bNode *node, const bool do_id
namespace blender::bke {
static void node_socket_interface_free(bNodeTree * /*ntree*/,
bNodeSocket *sock,
const bool do_id_user)
{
if (sock->prop) {
IDP_FreeProperty_ex(sock->prop, do_id_user);
}
if (sock->default_value) {
if (do_id_user) {
socket_id_user_decrement(sock);
}
MEM_freeN(sock->default_value);
}
if (sock->default_attribute_name) {
MEM_freeN(sock->default_attribute_name);
}
MEM_delete(sock->runtime);
}
static void free_localized_node_groups(bNodeTree *ntree)
{
/* Only localized node trees store a copy for each node group tree.
@@ -3466,152 +3560,6 @@ void ntreeLocalMerge(Main *bmain, bNodeTree *localtree, bNodeTree *ntree)
}
}
/* ************ NODE TREE INTERFACE *************** */
static bNodeSocket *make_socket_interface(bNodeTree *ntree,
const eNodeSocketInOut in_out,
const char *idname,
const char *name)
{
bNodeSocketType *stype = nodeSocketTypeFind(idname);
if (stype == nullptr) {
return nullptr;
}
bNodeSocket *sock = MEM_cnew<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)
@@ -3755,7 +3703,7 @@ int nodeSocketLinkLimit(const bNodeSocket *sock)
namespace blender::bke {
static void update_socket_declarations(ListBase *sockets,
Span<blender::nodes::SocketDeclarationPtr> declarations)
Span<blender::nodes::SocketDeclaration *> declarations)
{
int index;
LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, sockets, index) {
@@ -24,11 +24,12 @@ void preprocess_geometry_node_tree_for_evaluation(bNodeTree &tree_cow)
blender::nodes::ensure_geometry_nodes_lazy_function_graph(tree_cow);
}
static void update_interface_sockets(const bNodeTree &ntree)
static void update_interface(const bNodeTree &ntree)
{
bNodeTreeRuntime &tree_runtime = *ntree.runtime;
tree_runtime.interface_inputs = ntree.inputs;
tree_runtime.interface_outputs = ntree.outputs;
/* const_cast needed because the cache stores mutable item pointers, but needs a mutable
* interface in order to get them. The interface itself is not modified here. */
tree_runtime.interface_cache.rebuild(const_cast<bNodeTreeInterface &>(ntree.tree_interface));
}
static void update_node_vector(const bNodeTree &ntree)
@@ -92,6 +93,15 @@ static void update_socket_vectors_and_owner_node(const bNodeTree &ntree)
}
}
static void update_panels(const bNodeTree &ntree)
{
bNodeTreeRuntime &tree_runtime = *ntree.runtime;
for (bNode *node : tree_runtime.nodes_by_id) {
bNodeRuntime &node_runtime = *node->runtime;
node_runtime.panels.reinitialize(node->num_panel_states);
}
}
static void update_internal_link_inputs(const bNodeTree &ntree)
{
bNodeTreeRuntime &tree_runtime = *ntree.runtime;
@@ -528,10 +538,11 @@ static void ensure_topology_cache(const bNodeTree &ntree)
{
bNodeTreeRuntime &tree_runtime = *ntree.runtime;
tree_runtime.topology_cache_mutex.ensure([&]() {
update_interface_sockets(ntree);
update_interface(ntree);
update_node_vector(ntree);
update_link_vector(ntree);
update_socket_vectors_and_owner_node(ntree);
update_panels(ntree);
update_internal_link_inputs(ntree);
update_directly_linked_links_and_sockets(ntree);
update_nodes_by_type(ntree);
@@ -19,9 +19,9 @@ namespace blender::bke::anonymous_attribute_inferencing {
namespace aal = nodes::aal;
using nodes::NodeDeclaration;
static bool is_possible_field_socket(const bNodeSocket &socket)
static bool is_possible_field_socket(const eNodeSocketDatatype type)
{
return ELEM(socket.type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_BOOLEAN, SOCK_INT);
return ELEM(type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_BOOLEAN, SOCK_INT);
}
static bool socket_is_field(const bNodeSocket &socket)
@@ -187,7 +187,7 @@ class bNodeTreeToDotOptionsForAnonymousAttributeInferencing : public bNodeTreeTo
ss << "]";
return ss.str();
}
else if (is_possible_field_socket(socket)) {
else if (is_possible_field_socket(eNodeSocketDatatype(socket.type))) {
std::stringstream ss;
ss << socket.identifier << " [";
bits::foreach_1_index(result_.propagated_fields_by_socket[socket.index_in_tree()],
@@ -212,11 +212,13 @@ static AnonymousAttributeInferencingResult analyse_anonymous_attribute_usages(
/* Find input field and geometry sources. */
for (const int i : tree.interface_inputs().index_range()) {
const bNodeSocket &interface_socket = *tree.interface_inputs()[i];
if (interface_socket.type == SOCK_GEOMETRY) {
const bNodeTreeInterfaceSocket &interface_socket = *tree.interface_inputs()[i];
const bNodeSocketType *typeinfo = nodeSocketTypeFind(interface_socket.socket_type);
const eNodeSocketDatatype type = typeinfo ? eNodeSocketDatatype(typeinfo->type) : SOCK_CUSTOM;
if (type == SOCK_GEOMETRY) {
all_geometry_sources.append_and_get_index({InputGeometrySource{i}});
}
else if (is_possible_field_socket(interface_socket)) {
else if (is_possible_field_socket(type)) {
all_field_sources.append_and_get_index({InputFieldSource{i}});
}
}
@@ -368,7 +370,7 @@ static AnonymousAttributeInferencingResult analyse_anonymous_attribute_usages(
for (const int field_source_index : geometry_source.field_sources) {
for (const bNodeSocket *other_socket :
group_output_node->input_sockets().drop_back(1)) {
if (!is_possible_field_socket(*other_socket)) {
if (!is_possible_field_socket(eNodeSocketDatatype(other_socket->type))) {
continue;
}
if (propagated_fields_by_socket[other_socket->index_in_tree()][field_source_index]
@@ -383,7 +385,7 @@ static AnonymousAttributeInferencingResult analyse_anonymous_attribute_usages(
}
});
}
else if (is_possible_field_socket(*socket)) {
else if (is_possible_field_socket(eNodeSocketDatatype(socket->type))) {
const BoundedBitSpan propagated_fields =
propagated_fields_by_socket[socket->index_in_tree()];
bits::foreach_1_index(propagated_fields, [&](const int field_source_index) {
@@ -450,9 +452,13 @@ static AnonymousAttributeInferencingResult analyse_anonymous_attribute_usages(
required_fields_by_geometry_socket.all_bits() &= available_fields_by_geometry_socket.all_bits();
/* Create #EvalRelation for the tree. */
tree.ensure_topology_cache();
for (const int interface_i : tree.interface_inputs().index_range()) {
const bNodeSocket &interface_socket = *tree.interface_inputs()[interface_i];
if (interface_socket.type != SOCK_GEOMETRY) {
const bNodeTreeInterfaceSocket &interface_socket = *tree.interface_inputs()[interface_i];
const bNodeSocketType *typeinfo = interface_socket.socket_typeinfo();
eNodeSocketDatatype socket_type = typeinfo ? eNodeSocketDatatype(typeinfo->type) : SOCK_CUSTOM;
if (socket_type != SOCK_GEOMETRY) {
continue;
}
BitVector<> required_fields(all_field_sources.size(), false);
@@ -500,9 +500,12 @@ static void determine_group_input_states(
{
{
/* Non-field inputs never support fields. */
int index;
LISTBASE_FOREACH_INDEX (bNodeSocket *, group_input, &tree.inputs, index) {
if (!is_field_socket_type((eNodeSocketDatatype)group_input->type)) {
for (const int index : tree.interface_inputs().index_range()) {
const bNodeTreeInterfaceSocket *group_input = tree.interface_inputs()[index];
const bNodeSocketType *typeinfo = group_input->socket_typeinfo();
const eNodeSocketDatatype type = typeinfo ? eNodeSocketDatatype(typeinfo->type) :
SOCK_CUSTOM;
if (!is_field_socket_type(type)) {
new_inferencing_interface.inputs[index] = InputSocketFieldType::None;
}
}
@@ -695,9 +698,9 @@ bool update_field_inferencing(const bNodeTree &tree)
/* Create new inferencing interface for this node group. */
std::unique_ptr<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_inputs().size(),
InputSocketFieldType::IsSupported);
new_inferencing_interface->outputs.resize(BLI_listbase_count(&tree.outputs),
new_inferencing_interface->outputs.resize(tree.interface_outputs().size(),
OutputFieldDependency::ForDataSource());
/* Keep track of the state of all sockets. The index into this array is #SocketRef::id(). */
@@ -9,7 +9,6 @@
#include "BKE_node_tree_interface.hh"
#include "BLI_math_vector.h"
#include "BLI_set.hh"
#include "BLI_stack.hh"
#include "BLI_string.h"
#include "BLI_vector.hh"
@@ -21,9 +20,6 @@
#include "DNA_node_tree_interface_types.h"
#include "DNA_node_types.h"
#include "RNA_access.hh"
#include "RNA_prototypes.h"
namespace blender::bke::node_interface {
namespace socket_types {
@@ -405,6 +401,7 @@ static void item_copy(bNodeTreeInterfaceItem &dst,
BLI_assert(src_panel.name != nullptr);
dst_panel.name = BLI_strdup(src_panel.name);
dst_panel.description = BLI_strdup_null(src_panel.description);
dst_panel.copy_from(src_panel.items(), flag);
break;
}
@@ -438,6 +435,7 @@ static void item_free(bNodeTreeInterfaceItem &item, const bool do_id_user)
panel.clear(do_id_user);
MEM_SAFE_FREE(panel.name);
MEM_SAFE_FREE(panel.description);
break;
}
}
@@ -453,6 +451,7 @@ static void item_write_data(BlendWriter *writer, bNodeTreeInterfaceItem &item)
case NODE_INTERFACE_SOCKET: {
bNodeTreeInterfaceSocket &socket = reinterpret_cast<bNodeTreeInterfaceSocket &>(item);
BLO_write_string(writer, socket.name);
BLO_write_string(writer, socket.identifier);
BLO_write_string(writer, socket.description);
BLO_write_string(writer, socket.socket_type);
BLO_write_string(writer, socket.default_attribute_name);
@@ -466,6 +465,7 @@ static void item_write_data(BlendWriter *writer, bNodeTreeInterfaceItem &item)
case NODE_INTERFACE_PANEL: {
bNodeTreeInterfacePanel &panel = reinterpret_cast<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);
@@ -510,6 +510,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]);
@@ -575,12 +576,14 @@ bNodeSocketType *bNodeTreeInterfaceSocket::socket_typeinfo() const
blender::ColorGeometry4f bNodeTreeInterfaceSocket::socket_color() const
{
bNodeSocketType *typeinfo = this->socket_typeinfo();
if (!typeinfo || !typeinfo->draw_color) {
if (!typeinfo || !typeinfo->draw_color_simple) {
return blender::ColorGeometry4f(1.0f, 0.0f, 1.0f, 1.0f);
}
float color[4];
typeinfo->draw_color(nullptr, nullptr, nullptr, color);
if (typeinfo->draw_color_simple) {
typeinfo->draw_color_simple(typeinfo, color);
}
return blender::ColorGeometry4f(color);
}
@@ -723,21 +726,63 @@ bNodeTreeInterfacePanel *bNodeTreeInterfacePanel::find_parent_recursive(
return nullptr;
}
int bNodeTreeInterfacePanel::find_valid_insert_position_for_item(
const bNodeTreeInterfaceItem &item, const int initial_pos) const
{
const bool sockets_above_panels = !(this->flag &
NODE_INTERFACE_PANEL_ALLOW_SOCKETS_AFTER_PANELS);
const blender::Span<const bNodeTreeInterfaceItem *> items = this->items();
int pos = initial_pos;
if (sockets_above_panels) {
if (item.item_type == NODE_INTERFACE_PANEL) {
/* Find the closest valid position from the end, only panels at or after #position. */
for (int test_pos = items.size() - 1; test_pos >= initial_pos; test_pos--) {
if (test_pos < 0) {
/* Initial position is out of range but valid. */
break;
}
if (items[test_pos]->item_type != NODE_INTERFACE_PANEL) {
/* Found valid position, insert after the last socket item. */
pos = test_pos + 1;
break;
}
}
}
else {
/* Find the closest valid position from the start, no panels at or after #position. */
for (int test_pos = 0; test_pos <= initial_pos; test_pos++) {
if (test_pos >= items.size()) {
/* Initial position is out of range but valid. */
break;
}
if (items[test_pos]->item_type == NODE_INTERFACE_PANEL) {
/* Found valid position, insering moves the first panel. */
pos = test_pos;
break;
}
}
}
}
return pos;
}
void bNodeTreeInterfacePanel::add_item(bNodeTreeInterfaceItem &item)
{
blender::MutableSpan<bNodeTreeInterfaceItem *> old_items = this->items();
items_num++;
items_array = MEM_cnew_array<bNodeTreeInterfaceItem *>(items_num, __func__);
this->items().drop_back(1).copy_from(old_items);
this->items().last() = &item;
if (old_items.data()) {
MEM_freeN(old_items.data());
}
/* Same as inserting at the end. */
insert_item(item, this->items_num);
}
void bNodeTreeInterfacePanel::insert_item(bNodeTreeInterfaceItem &item, int position)
{
/* Are child panels allowed? */
BLI_assert(item.item_type != NODE_INTERFACE_PANEL ||
(this->flag & NODE_INTERFACE_PANEL_ALLOW_CHILD_PANELS));
/* Apply any constraints on the item positions. */
position = find_valid_insert_position_for_item(item, position);
position = std::min(std::max(position, 0), items_num);
blender::MutableSpan<bNodeTreeInterfaceItem *> old_items = this->items();
@@ -752,7 +797,7 @@ void bNodeTreeInterfacePanel::insert_item(bNodeTreeInterfaceItem &item, int posi
}
}
bool bNodeTreeInterfacePanel::remove_item(bNodeTreeInterfaceItem &item, bool free)
bool bNodeTreeInterfacePanel::remove_item(bNodeTreeInterfaceItem &item, const bool free)
{
const int position = this->item_position(item);
if (!this->items().index_range().contains(position)) {
@@ -785,26 +830,28 @@ void bNodeTreeInterfacePanel::clear(bool do_id_user)
items_num = 0;
}
bool bNodeTreeInterfacePanel::move_item(bNodeTreeInterfaceItem &item, const int new_position)
bool bNodeTreeInterfacePanel::move_item(bNodeTreeInterfaceItem &item, int new_position)
{
const int old_position = this->item_position(item);
if (!this->items().index_range().contains(old_position) ||
!this->items().index_range().contains(new_position))
{
if (!this->items().index_range().contains(old_position)) {
return false;
}
if (old_position == new_position) {
/* Nothing changes. */
return true;
}
else if (old_position < new_position) {
new_position = find_valid_insert_position_for_item(item, new_position);
new_position = std::min(std::max(new_position, 0), items_num);
if (old_position < new_position) {
/* Actual target position and all existing items shifted by 1. */
const blender::Span<bNodeTreeInterfaceItem *> moved_items = this->items().slice(
old_position + 1, new_position - old_position);
old_position + 1, new_position - old_position - 1);
bNodeTreeInterfaceItem *tmp = this->items()[old_position];
std::copy(
moved_items.begin(), moved_items.end(), this->items().drop_front(old_position).data());
this->items()[new_position] = tmp;
this->items()[new_position - 1] = tmp;
}
else /* old_position > new_position */ {
const blender::Span<bNodeTreeInterfaceItem *> moved_items = this->items().slice(
@@ -893,7 +940,7 @@ static bNodeTreeInterfaceSocket *make_socket(const int uid,
blender::StringRefNull name,
blender::StringRefNull description,
blender::StringRefNull socket_type,
const eNodeTreeInterfaceSocketFlag flag)
const NodeTreeInterfaceSocketFlag flag)
{
BLI_assert(name.c_str() != nullptr);
BLI_assert(socket_type.c_str() != nullptr);
@@ -919,14 +966,19 @@ static bNodeTreeInterfaceSocket *make_socket(const int uid,
return new_socket;
}
static bNodeTreeInterfacePanel *make_panel(const int uid, blender::StringRefNull name)
static bNodeTreeInterfacePanel *make_panel(const int uid,
blender::StringRefNull name,
blender::StringRefNull description,
const NodeTreeInterfacePanelFlag flag)
{
BLI_assert(name.c_str() != nullptr);
bNodeTreeInterfacePanel *new_panel = MEM_cnew<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;
}
@@ -939,11 +991,19 @@ void bNodeTreeInterfacePanel::copy_from(
/* Copy buffers. */
for (const int i : items_src.index_range()) {
const bNodeTreeInterfaceItem *item_src = items_src[i];
BLI_assert(item_src->item_type != NODE_INTERFACE_PANEL ||
(flag & NODE_INTERFACE_PANEL_ALLOW_CHILD_PANELS));
items_array[i] = static_cast<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);
@@ -1015,7 +1075,7 @@ void bNodeTreeInterface::active_item_set(bNodeTreeInterfaceItem *item)
bNodeTreeInterfaceSocket *bNodeTreeInterface::add_socket(blender::StringRefNull name,
blender::StringRefNull description,
blender::StringRefNull socket_type,
const eNodeTreeInterfaceSocketFlag flag,
const NodeTreeInterfaceSocketFlag flag,
bNodeTreeInterfacePanel *parent)
{
if (parent == nullptr) {
@@ -1031,13 +1091,12 @@ bNodeTreeInterfaceSocket *bNodeTreeInterface::add_socket(blender::StringRefNull
return new_socket;
}
bNodeTreeInterfaceSocket *bNodeTreeInterface::insert_socket(
blender::StringRefNull name,
blender::StringRefNull description,
blender::StringRefNull socket_type,
const eNodeTreeInterfaceSocketFlag flag,
bNodeTreeInterfacePanel *parent,
const int position)
bNodeTreeInterfaceSocket *bNodeTreeInterface::insert_socket(blender::StringRefNull name,
blender::StringRefNull description,
blender::StringRefNull socket_type,
const NodeTreeInterfaceSocketFlag flag,
bNodeTreeInterfacePanel *parent,
const int position)
{
if (parent == nullptr) {
parent = &root_panel;
@@ -1053,6 +1112,8 @@ bNodeTreeInterfaceSocket *bNodeTreeInterface::insert_socket(
}
bNodeTreeInterfacePanel *bNodeTreeInterface::add_panel(blender::StringRefNull name,
blender::StringRefNull description,
const NodeTreeInterfacePanelFlag flag,
bNodeTreeInterfacePanel *parent)
{
if (parent == nullptr) {
@@ -1060,7 +1121,12 @@ bNodeTreeInterfacePanel *bNodeTreeInterface::add_panel(blender::StringRefNull na
}
BLI_assert(this->find_item(parent->item));
bNodeTreeInterfacePanel *new_panel = make_panel(next_uid++, name);
if (!(parent->flag & NODE_INTERFACE_PANEL_ALLOW_CHILD_PANELS)) {
/* Parent does not allow adding child panels. */
return nullptr;
}
bNodeTreeInterfacePanel *new_panel = make_panel(next_uid++, name, description, flag);
if (new_panel) {
parent->add_item(new_panel->item);
}
@@ -1068,6 +1134,8 @@ bNodeTreeInterfacePanel *bNodeTreeInterface::add_panel(blender::StringRefNull na
}
bNodeTreeInterfacePanel *bNodeTreeInterface::insert_panel(blender::StringRefNull name,
blender::StringRefNull description,
const NodeTreeInterfacePanelFlag flag,
bNodeTreeInterfacePanel *parent,
const int position)
{
@@ -1076,7 +1144,12 @@ bNodeTreeInterfacePanel *bNodeTreeInterface::insert_panel(blender::StringRefNull
}
BLI_assert(this->find_item(parent->item));
bNodeTreeInterfacePanel *new_panel = make_panel(next_uid++, name);
if (!(parent->flag & NODE_INTERFACE_PANEL_ALLOW_CHILD_PANELS)) {
/* Parent does not allow adding child panels. */
return nullptr;
}
bNodeTreeInterfacePanel *new_panel = make_panel(next_uid++, name, description, flag);
if (new_panel) {
parent->insert_item(new_panel->item, position);
}
@@ -1092,8 +1165,11 @@ bNodeTreeInterfaceItem *bNodeTreeInterface::add_item_copy(const bNodeTreeInterfa
BLI_assert(this->find_item(item));
BLI_assert(this->find_item(parent->item));
if (parent == nullptr) {
parent = &root_panel;
if (item.item_type == NODE_INTERFACE_PANEL &&
!(parent->flag & NODE_INTERFACE_PANEL_ALLOW_CHILD_PANELS))
{
/* Parent does not allow adding child panels. */
return nullptr;
}
bNodeTreeInterfaceItem *citem = static_cast<bNodeTreeInterfaceItem *>(MEM_dupallocN(&item));
@@ -1113,6 +1189,13 @@ bNodeTreeInterfaceItem *bNodeTreeInterface::insert_item_copy(const bNodeTreeInte
BLI_assert(this->find_item(item));
BLI_assert(this->find_item(parent->item));
if (item.item_type == NODE_INTERFACE_PANEL &&
!(parent->flag & NODE_INTERFACE_PANEL_ALLOW_CHILD_PANELS))
{
/* Parent does not allow adding child panels. */
return nullptr;
}
bNodeTreeInterfaceItem *citem = static_cast<bNodeTreeInterfaceItem *>(MEM_dupallocN(&item));
item_types::item_copy(*citem, item, 0);
parent->insert_item(*citem, position);
@@ -1162,9 +1245,22 @@ bool bNodeTreeInterface::move_item_to_parent(bNodeTreeInterfaceItem &item,
if (parent == nullptr) {
return false;
}
if (parent->remove_item(item, false)) {
new_parent->insert_item(item, new_position);
return true;
if (item.item_type == NODE_INTERFACE_PANEL &&
!(new_parent->flag & NODE_INTERFACE_PANEL_ALLOW_CHILD_PANELS))
{
/* Parent does not allow adding child panels. */
return false;
}
if (parent == new_parent) {
return parent->move_item(item, new_position);
}
else {
/* Note: only remove and reinsert when parents different, otherwise removing the item can
* change the desired target position! */
if (parent->remove_item(item, false)) {
new_parent->insert_item(item, new_position);
return true;
}
}
return false;
}
@@ -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 {};
}
@@ -577,7 +577,7 @@ static bNodeSocket *do_versions_node_group_add_socket_2_56_2(bNodeTree *ngroup,
// if (stype->value_structsize > 0)
// gsock->default_value = MEM_callocN(stype->value_structsize, "default socket value");
BLI_addtail(in_out == SOCK_IN ? &ngroup->inputs : &ngroup->outputs, gsock);
BLI_addtail(in_out == SOCK_IN ? &ngroup->inputs_legacy : &ngroup->outputs_legacy, gsock);
BKE_ntree_update_tag_interface(ngroup);
@@ -2099,10 +2099,10 @@ void blo_do_versions_250(FileData *fd, Library * /*lib*/, Main *bmain)
}
}
LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs) {
LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs_legacy) {
do_versions_socket_default_value_259(sock);
}
LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs) {
LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs_legacy) {
do_versions_socket_default_value_259(sock);
}
@@ -229,10 +229,10 @@ static void do_versions_nodetree_socket_use_flags_2_62(bNodeTree *ntree)
sock->flag &= ~SOCK_IS_LINKED;
}
}
LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs) {
LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs_legacy) {
sock->flag &= ~SOCK_IS_LINKED;
}
LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs) {
LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs_legacy) {
sock->flag &= ~SOCK_IS_LINKED;
}
@@ -920,10 +920,10 @@ static void do_versions_nodetree_customnodes(bNodeTree *ntree, int /*is_group*/)
}
}
/* tree sockets idname */
LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs) {
LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs_legacy) {
STRNCPY(sock->idname, node_socket_get_static_idname(sock));
}
LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs) {
LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs_legacy) {
STRNCPY(sock->idname, node_socket_get_static_idname(sock));
}
}
@@ -939,10 +939,10 @@ static void do_versions_nodetree_customnodes(bNodeTree *ntree, int /*is_group*/)
sock->in_out = SOCK_OUT;
}
}
LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs) {
LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs_legacy) {
sock->in_out = SOCK_IN;
}
LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs) {
LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs_legacy) {
sock->in_out = SOCK_OUT;
}
}
@@ -969,18 +969,18 @@ static void do_versions_nodetree_customnodes(bNodeTree *ntree, int /*is_group*/)
sizeof(sock->identifier));
}
}
LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs) {
LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs_legacy) {
STRNCPY(sock->identifier, sock->name);
BLI_uniquename(&ntree->inputs,
BLI_uniquename(&ntree->inputs_legacy,
sock,
"socket",
'.',
offsetof(bNodeSocket, identifier),
sizeof(sock->identifier));
}
LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs) {
LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs_legacy) {
STRNCPY(sock->identifier, sock->name);
BLI_uniquename(&ntree->outputs,
BLI_uniquename(&ntree->outputs_legacy,
sock,
"socket",
'.',
@@ -2725,11 +2725,11 @@ void do_versions_after_linking_260(Main *bmain)
// const float offsety = 0.0f;
if (create_io_nodes) {
if (ntree->inputs.first) {
if (ntree->inputs_legacy.first) {
input_node = nodeAddStaticNode(nullptr, ntree, NODE_GROUP_INPUT);
}
if (ntree->outputs.first) {
if (ntree->outputs_legacy.first) {
output_node = nodeAddStaticNode(nullptr, ntree, NODE_GROUP_OUTPUT);
}
}
@@ -318,10 +318,10 @@ static void do_versions_idproperty_ui_data(Main *bmain)
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
version_idproperty_ui_data(node->prop);
}
LISTBASE_FOREACH (bNodeSocket *, socket, &ntree->inputs) {
LISTBASE_FOREACH (bNodeSocket *, socket, &ntree->inputs_legacy) {
version_idproperty_ui_data(socket->prop);
}
LISTBASE_FOREACH (bNodeSocket *, socket, &ntree->outputs) {
LISTBASE_FOREACH (bNodeSocket *, socket, &ntree->outputs_legacy) {
version_idproperty_ui_data(socket->prop);
}
}
@@ -544,8 +544,11 @@ static bNodeTree *add_realize_node_tree(Main *bmain)
{
bNodeTree *node_tree = ntreeAddTree(bmain, "Realize Instances 2.93 Legacy", "GeometryNodeTree");
ntreeAddSocketInterface(node_tree, SOCK_IN, "NodeSocketGeometry", "Geometry");
ntreeAddSocketInterface(node_tree, SOCK_OUT, "NodeSocketGeometry", "Geometry");
node_tree->tree_interface.add_socket("Geometry",
"",
"NodeSocketGeometry",
NODE_INTERFACE_SOCKET_INPUT | NODE_INTERFACE_SOCKET_OUTPUT,
nullptr);
bNode *group_input = nodeAddStaticNode(nullptr, node_tree, NODE_GROUP_INPUT);
group_input->locx = -400.0f;
@@ -583,6 +583,64 @@ static void version_replace_principled_hair_model(bNodeTree *ntree)
}
}
static bNodeTreeInterfaceItem *legacy_socket_move_to_interface(bNodeSocket &legacy_socket,
const eNodeSocketInOut in_out)
{
bNodeTreeInterfaceItem *new_item = static_cast<bNodeTreeInterfaceItem *>(
MEM_mallocN(sizeof(bNodeTreeInterfaceSocket), __func__));
new_item->item_type = NODE_INTERFACE_SOCKET;
bNodeTreeInterfaceSocket &new_socket = *reinterpret_cast<bNodeTreeInterfaceSocket *>(new_item);
/* Move reusable data. */
new_socket.name = BLI_strdup(legacy_socket.name);
new_socket.identifier = BLI_strdup(legacy_socket.identifier);
new_socket.description = BLI_strdup(legacy_socket.description);
new_socket.socket_type = BLI_strdup(legacy_socket.idname);
new_socket.flag = (in_out == SOCK_IN ? NODE_INTERFACE_SOCKET_INPUT :
NODE_INTERFACE_SOCKET_OUTPUT);
SET_FLAG_FROM_TEST(
new_socket.flag, legacy_socket.flag & SOCK_HIDE_VALUE, NODE_INTERFACE_SOCKET_HIDE_VALUE);
SET_FLAG_FROM_TEST(new_socket.flag,
legacy_socket.flag & SOCK_HIDE_IN_MODIFIER,
NODE_INTERFACE_SOCKET_HIDE_IN_MODIFIER);
new_socket.attribute_domain = legacy_socket.attribute_domain;
new_socket.default_attribute_name = BLI_strdup_null(legacy_socket.default_attribute_name);
new_socket.socket_data = legacy_socket.default_value;
new_socket.properties = legacy_socket.prop;
/* Clear moved pointers in legacy data. */
legacy_socket.default_value = nullptr;
legacy_socket.prop = nullptr;
/* Unused data */
MEM_delete(legacy_socket.runtime);
legacy_socket.runtime = nullptr;
return new_item;
}
static void versioning_convert_node_tree_socket_lists_to_interface(bNodeTree *ntree)
{
bNodeTreeInterface &tree_interface = ntree->tree_interface;
const int num_inputs = BLI_listbase_count(&ntree->inputs_legacy);
const int num_outputs = BLI_listbase_count(&ntree->outputs_legacy);
tree_interface.root_panel.items_num = num_inputs + num_outputs;
tree_interface.root_panel.items_array = static_cast<bNodeTreeInterfaceItem **>(MEM_malloc_arrayN(
tree_interface.root_panel.items_num, sizeof(bNodeTreeInterfaceItem *), __func__));
/* Convert outputs first to retain old outputs/inputs ordering. */
int index;
LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, &ntree->outputs_legacy, index) {
tree_interface.root_panel.items_array[index] = legacy_socket_move_to_interface(*socket,
SOCK_OUT);
}
LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, &ntree->inputs_legacy, index) {
tree_interface.root_panel.items_array[num_outputs + index] = legacy_socket_move_to_interface(
*socket, SOCK_IN);
}
}
void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
{
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 400, 1)) {
@@ -914,6 +972,29 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 400, 20)) {
/* Convert old socket lists into new interface items. */
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
versioning_convert_node_tree_socket_lists_to_interface(ntree);
/* Clear legacy sockets after conversion.
* Internal data pointers have been moved or freed already. */
BLI_freelistN(&ntree->inputs_legacy);
BLI_freelistN(&ntree->outputs_legacy);
}
FOREACH_NODETREE_END;
}
else {
/* Legacy node tree sockets are created for forward compatibilty,
* but have to be freed after loading and versioning. */
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
/* Clear legacy sockets after conversion.
* Internal data pointers have been moved or freed already. */
BLI_freelistN(&ntree->inputs_legacy);
BLI_freelistN(&ntree->outputs_legacy);
}
FOREACH_NODETREE_END;
}
/**
* Versioning code until next subversion bump goes here.
*
@@ -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();
@@ -1961,11 +1961,13 @@ void DepsgraphNodeBuilder::build_nodetree(bNodeTree *ntree)
}
}
LISTBASE_FOREACH (bNodeSocket *, socket, &ntree->inputs) {
build_idproperties(socket->prop);
/* Needed for interface cache. */
ntree->ensure_topology_cache();
for (bNodeTreeInterfaceSocket *socket : ntree->interface_inputs()) {
build_idproperties(socket->properties);
}
LISTBASE_FOREACH (bNodeSocket *, socket, &ntree->outputs) {
build_idproperties(socket->prop);
for (bNodeTreeInterfaceSocket *socket : ntree->interface_outputs()) {
build_idproperties(socket->properties);
}
/* TODO: link from nodetree to owner_component? */
@@ -2938,11 +2938,11 @@ void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree)
}
}
LISTBASE_FOREACH (bNodeSocket *, socket, &ntree->inputs) {
build_idproperties(socket->prop);
for (bNodeTreeInterfaceSocket *socket : ntree->interface_inputs()) {
build_idproperties(socket->properties);
}
LISTBASE_FOREACH (bNodeSocket *, socket, &ntree->outputs) {
build_idproperties(socket->prop);
for (bNodeTreeInterfaceSocket *socket : ntree->interface_outputs()) {
build_idproperties(socket->properties);
}
if (check_id_has_anim_component(&ntree->id)) {
@@ -72,8 +72,11 @@ void ensure_surface_deformation_node_exists(bContext &C, Object &curves_ob)
nmd.node_group = ntreeAddTree(bmain, DATA_("Surface Deform"), "GeometryNodeTree");
bNodeTree *ntree = nmd.node_group;
ntreeAddSocketInterface(ntree, SOCK_IN, "NodeSocketGeometry", "Geometry");
ntreeAddSocketInterface(ntree, SOCK_OUT, "NodeSocketGeometry", "Geometry");
ntree->tree_interface.add_socket("Geometry",
"",
"NodeSocketGeometry",
NODE_INTERFACE_SOCKET_INPUT | NODE_INTERFACE_SOCKET_OUTPUT,
nullptr);
bNode *group_input = nodeAddStaticNode(&C, ntree, NODE_GROUP_INPUT);
bNode *group_output = nodeAddStaticNode(&C, ntree, NODE_GROUP_OUTPUT);
bNode *deform_node = nodeAddStaticNode(&C, ntree, GEO_NODE_DEFORM_CURVES_ON_SURFACE);
@@ -386,9 +386,12 @@ static std::string run_node_group_get_description(bContext *C,
static void add_attribute_search_or_value_buttons(uiLayout *layout,
PointerRNA *md_ptr,
const bNodeSocket &socket)
const bNodeTreeInterfaceSocket &socket)
{
char socket_id_esc[sizeof(socket.identifier) * 2];
bNodeSocketType *typeinfo = nodeSocketTypeFind(socket.socket_type);
const eNodeSocketDatatype socket_type = eNodeSocketDatatype(typeinfo->type);
char socket_id_esc[MAX_NAME * 2];
BLI_str_escape(socket_id_esc, socket.identifier, sizeof(socket_id_esc));
const std::string rna_path = "[\"" + std::string(socket_id_esc) + "\"]";
const std::string rna_path_use_attribute = "[\"" + std::string(socket_id_esc) +
@@ -404,7 +407,7 @@ static void add_attribute_search_or_value_buttons(uiLayout *layout,
uiLayoutSetAlignment(name_row, UI_LAYOUT_ALIGN_RIGHT);
const bool use_attribute = RNA_boolean_get(md_ptr, rna_path_use_attribute.c_str());
if (socket.type == SOCK_BOOLEAN && !use_attribute) {
if (socket_type == SOCK_BOOLEAN && !use_attribute) {
uiItemL(name_row, "", ICON_NONE);
}
else {
@@ -412,7 +415,7 @@ static void add_attribute_search_or_value_buttons(uiLayout *layout,
}
uiLayout *prop_row = uiLayoutRow(split, true);
if (socket.type == SOCK_BOOLEAN) {
if (socket_type == SOCK_BOOLEAN) {
uiLayoutSetPropSep(prop_row, false);
uiLayoutSetAlignment(prop_row, UI_LAYOUT_ALIGN_EXPAND);
}
@@ -422,7 +425,7 @@ static void add_attribute_search_or_value_buttons(uiLayout *layout,
uiItemR(prop_row, md_ptr, rna_path_attribute_name.c_str(), UI_ITEM_NONE, "", ICON_NONE);
}
else {
const char *name = socket.type == SOCK_BOOLEAN ? socket.name : "";
const char *name = socket_type == SOCK_BOOLEAN ? socket.name : "";
uiItemR(prop_row, md_ptr, rna_path.c_str(), UI_ITEM_NONE, name, ICON_NONE);
}
@@ -435,9 +438,12 @@ static void draw_property_for_socket(const bNodeTree &node_tree,
IDProperty *op_properties,
PointerRNA *bmain_ptr,
PointerRNA *op_ptr,
const bNodeSocket &socket,
const bNodeTreeInterfaceSocket &socket,
const int socket_index)
{
bNodeSocketType *typeinfo = nodeSocketTypeFind(socket.socket_type);
const eNodeSocketDatatype socket_type = eNodeSocketDatatype(typeinfo->type);
/* The property should be created in #MOD_nodes_update_interface with the correct type. */
IDProperty *property = IDP_GetPropertyFromGroup(op_properties, socket.identifier);
@@ -447,7 +453,7 @@ static void draw_property_for_socket(const bNodeTree &node_tree,
return;
}
char socket_id_esc[sizeof(socket.identifier) * 2];
char socket_id_esc[MAX_NAME * 2];
BLI_str_escape(socket_id_esc, socket.identifier, sizeof(socket_id_esc));
char rna_path[sizeof(socket_id_esc) + 4];
@@ -459,7 +465,7 @@ static void draw_property_for_socket(const bNodeTree &node_tree,
/* Use #uiItemPointerR to draw pointer properties because #uiItemR would not have enough
* information about what type of ID to select for editing the values. This is because
* pointer IDProperties contain no information about their type. */
switch (socket.type) {
switch (socket_type) {
case SOCK_OBJECT:
uiItemPointerR(row, op_ptr, rna_path, bmain_ptr, "objects", socket.name, ICON_OBJECT_DATA);
break;
@@ -503,10 +509,12 @@ static void run_node_group_ui(bContext *C, wmOperator *op)
return;
}
int input_index;
LISTBASE_FOREACH_INDEX (bNodeSocket *, io_socket, &node_tree->inputs, input_index) {
node_tree->ensure_topology_cache();
int input_index = 0;
for (bNodeTreeInterfaceSocket *io_socket : node_tree->interface_inputs()) {
draw_property_for_socket(
*node_tree, layout, op->properties, &bmain_ptr, op->ptr, *io_socket, input_index);
++input_index;
}
}
@@ -2583,6 +2583,8 @@ void uiTemplateLightLinkingCollection(uiLayout *layout, PointerRNA *ptr, const c
void uiTemplateGreasePencilLayerTree(uiLayout *layout, bContext *C);
void uiTemplateNodeTreeInterface(struct uiLayout *layout, struct PointerRNA *ptr);
/**
* \return: A RNA pointer for the operator properties.
*/
@@ -70,6 +70,7 @@ set(SRC
interface_template_grease_pencil_layer_tree.cc
interface_template_light_linking.cc
interface_template_list.cc
interface_template_node_tree_interface.cc
interface_template_search_menu.cc
interface_template_search_operator.cc
interface_templates.cc
@@ -0,0 +1,517 @@
/* SPDX-FileCopyrightText: 2023 Blender Foundation
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup edinterface
*/
#include "BKE_context.h"
#include "BKE_node_tree_interface.hh"
#include "BKE_node_tree_update.h"
#include "BLI_color.hh"
#include "BLI_string.h"
#include "BLT_translation.h"
#include "DNA_node_tree_interface_types.h"
#include "ED_node.hh"
#include "RNA_access.hh"
#include "RNA_prototypes.h"
#include "UI_interface.hh"
#include "UI_resources.hh"
#include "UI_tree_view.hh"
#include "WM_api.hh"
namespace node_interface = blender::bke::node_interface;
namespace blender::ui::nodes {
namespace {
class NodeTreeInterfaceView;
class NodeTreeInterfaceDragController : public AbstractViewItemDragController {
private:
bNodeTreeInterfaceItem &item_;
public:
explicit NodeTreeInterfaceDragController(NodeTreeInterfaceView &view,
bNodeTreeInterfaceItem &item);
virtual ~NodeTreeInterfaceDragController() = default;
eWM_DragDataType get_drag_type() const;
void *create_drag_data() const;
};
class NodeSocketDropTarget : public TreeViewItemDropTarget {
private:
bNodeTreeInterfaceSocket &socket_;
public:
explicit NodeSocketDropTarget(NodeTreeInterfaceView &view, bNodeTreeInterfaceSocket &socket);
bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const override;
std::string drop_tooltip(const DragInfo &drag_info) const override;
bool on_drop(struct bContext * /*C*/, const DragInfo &drag_info) const override;
protected:
wmDragNodeTreeInterface *get_drag_node_tree_declaration(const wmDrag &drag) const;
};
class NodePanelDropTarget : public TreeViewItemDropTarget {
private:
bNodeTreeInterfacePanel &panel_;
public:
explicit NodePanelDropTarget(NodeTreeInterfaceView &view, bNodeTreeInterfacePanel &panel);
bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const override;
std::string drop_tooltip(const DragInfo &drag_info) const override;
bool on_drop(bContext *C, const DragInfo &drag_info) const override;
protected:
wmDragNodeTreeInterface *get_drag_node_tree_declaration(const wmDrag &drag) const;
};
class NodeSocketViewItem : public BasicTreeViewItem {
private:
bNodeTree &nodetree_;
bNodeTreeInterfaceSocket &socket_;
public:
NodeSocketViewItem(bNodeTree &nodetree,
bNodeTreeInterface &interface,
bNodeTreeInterfaceSocket &socket)
: BasicTreeViewItem(socket.name, ICON_NONE), nodetree_(nodetree), socket_(socket)
{
set_is_active_fn([interface, &socket]() { return interface.active_item() == &socket.item; });
set_on_activate_fn([&interface](bContext & /*C*/, BasicTreeViewItem &new_active) {
NodeSocketViewItem &self = static_cast<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;
};
class NodePanelViewItem : public BasicTreeViewItem {
private:
bNodeTree &nodetree_;
bNodeTreeInterfacePanel &panel_;
public:
NodePanelViewItem(bNodeTree &nodetree,
bNodeTreeInterface &interface,
bNodeTreeInterfacePanel &panel)
: BasicTreeViewItem(panel.name, ICON_NONE), nodetree_(nodetree), panel_(panel)
{
set_is_active_fn([interface, &panel]() { return interface.active_item() == &panel.item; });
set_on_activate_fn([&interface](bContext & /*C*/, BasicTreeViewItem &new_active) {
NodePanelViewItem &self = static_cast<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;
};
class NodeTreeInterfaceView : public AbstractTreeView {
private:
bNodeTree &nodetree_;
bNodeTreeInterface &interface_;
public:
explicit NodeTreeInterfaceView(bNodeTree &nodetree, bNodeTreeInterface &interface)
: nodetree_(nodetree), interface_(interface)
{
}
bNodeTree &nodetree()
{
return nodetree_;
}
bNodeTreeInterface &interface()
{
return interface_;
}
void build_tree() override
{
/* Draw root items */
add_items_for_panel_recursive(interface_.root_panel, *this);
}
protected:
void add_items_for_panel_recursive(bNodeTreeInterfacePanel &parent,
ui::TreeViewOrItem &parent_item)
{
for (bNodeTreeInterfaceItem *item : parent.items()) {
switch (item->item_type) {
case NODE_INTERFACE_SOCKET: {
bNodeTreeInterfaceSocket *socket = node_interface::get_item_as<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;
}
}
}
}
};
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 uiTemplateNodeTreeInterface(struct uiLayout *layout, struct PointerRNA *ptr)
{
if (!ptr->data) {
return;
}
if (!RNA_struct_is_a(ptr->type, &RNA_NodeTreeInterface)) {
return;
}
bNodeTree &nodetree = *reinterpret_cast<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);
}
@@ -70,6 +70,8 @@
#include "BKE_mesh.hh"
#include "BKE_modifier.h"
#include "BKE_node.h"
#include "BKE_node_runtime.hh"
#include "BKE_node_tree_interface.hh"
#include "BKE_object.h"
#include "BKE_pointcloud.h"
#include "BKE_report.h"
@@ -2964,23 +2966,32 @@ char *ED_object_ot_drop_geometry_nodes_tooltip(bContext *C,
static bool check_geometry_node_group_sockets(wmOperator *op, const bNodeTree *tree)
{
const bNodeSocket *first_input = (const bNodeSocket *)tree->inputs.first;
if (!first_input) {
BKE_report(op->reports, RPT_ERROR, "The node group must have a geometry input socket");
return false;
tree->ensure_topology_cache();
if (!tree->interface_inputs().is_empty()) {
const bNodeTreeInterfaceSocket *first_input = tree->interface_inputs()[0];
if (!first_input) {
BKE_report(op->reports, RPT_ERROR, "The node group must have a geometry input socket");
return false;
}
const bNodeSocketType *typeinfo = first_input->socket_typeinfo();
const eNodeSocketDatatype type = typeinfo ? eNodeSocketDatatype(typeinfo->type) : SOCK_CUSTOM;
if (type != SOCK_GEOMETRY) {
BKE_report(op->reports, RPT_ERROR, "The first input must be a geometry socket");
return false;
}
}
if (first_input->type != SOCK_GEOMETRY) {
BKE_report(op->reports, RPT_ERROR, "The first input must be a geometry socket");
return false;
}
const bNodeSocket *first_output = (const bNodeSocket *)tree->outputs.first;
if (!first_output) {
BKE_report(op->reports, RPT_ERROR, "The node group must have a geometry output socket");
return false;
}
if (first_output->type != SOCK_GEOMETRY) {
BKE_report(op->reports, RPT_ERROR, "The first output must be a geometry socket");
return false;
if (!tree->interface_outputs().is_empty()) {
const bNodeTreeInterfaceSocket *first_output = tree->interface_outputs()[0];
if (!first_output) {
BKE_report(op->reports, RPT_ERROR, "The node group must have a geometry output socket");
return false;
}
const bNodeSocketType *typeinfo = first_output->socket_typeinfo();
const eNodeSocketDatatype type = typeinfo ? eNodeSocketDatatype(typeinfo->type) : SOCK_CUSTOM;
if (type != SOCK_GEOMETRY) {
BKE_report(op->reports, RPT_ERROR, "The first output must be a geometry socket");
return false;
}
}
return true;
}
+99 -72
View File
@@ -1124,16 +1124,8 @@ static void node_socket_undefined_draw_color(bContext * /*C*/,
r_color[3] = 1.0f;
}
static void node_socket_undefined_interface_draw(bContext * /*C*/,
uiLayout *layout,
PointerRNA * /*ptr*/)
{
uiItemL(layout, IFACE_("Undefined Socket Type"), ICON_ERROR);
}
static void node_socket_undefined_interface_draw_color(bContext * /*C*/,
PointerRNA * /*ptr*/,
float *r_color)
static void node_socket_undefined_draw_color_simple(const bNodeSocketType * /*type*/,
float *r_color)
{
r_color[0] = 1.0f;
r_color[1] = 0.0f;
@@ -1141,6 +1133,14 @@ static void node_socket_undefined_interface_draw_color(bContext * /*C*/,
r_color[3] = 1.0f;
}
static void node_socket_undefined_interface_draw(ID * /*id*/,
bNodeTreeInterfaceSocket * /*interface_socket*/,
bContext * /*C*/,
uiLayout *layout)
{
uiItemL(layout, IFACE_("Undefined Socket Type"), ICON_ERROR);
}
/** \} */
} // namespace blender::ed::space_node
@@ -1161,8 +1161,8 @@ void ED_node_init_butfuncs()
NodeSocketTypeUndefined.draw = node_socket_undefined_draw;
NodeSocketTypeUndefined.draw_color = node_socket_undefined_draw_color;
NodeSocketTypeUndefined.draw_color_simple = node_socket_undefined_draw_color_simple;
NodeSocketTypeUndefined.interface_draw = node_socket_undefined_interface_draw;
NodeSocketTypeUndefined.interface_draw_color = node_socket_undefined_interface_draw_color;
/* node type ui functions */
NODE_TYPES_BEGIN (ntype) {
@@ -1208,22 +1208,39 @@ static const float std_node_socket_colors[][4] = {
{0.65, 0.39, 0.78, 1.0}, /* SOCK_ROTATION */
};
/* common color callbacks for standard types */
static void std_node_socket_draw_color(bContext * /*C*/,
PointerRNA *ptr,
PointerRNA * /*node_ptr*/,
float *r_color)
/* Callback for colors that does not depend on the socket pointer argument to get the type. */
template<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)
copy_v4_v4(r_color, std_node_socket_colors[socket_type]);
};
static void std_node_socket_color_simple_fn(const bNodeSocketType *type, float *r_color)
{
bNodeSocket *sock = (bNodeSocket *)ptr->data;
int type = sock->typeinfo->type;
copy_v4_v4(r_color, std_node_socket_colors[type]);
}
copy_v4_v4(r_color, std_node_socket_colors[type->type]);
};
using SocketColorFn = void (*)(bContext *C, PointerRNA *ptr, PointerRNA *node_ptr, float *r_color);
/* Callbacks for all built-in socket types. */
static const SocketColorFn std_node_socket_color_funcs[] = {
std_node_socket_color_fn<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 +1451,44 @@ static void std_node_socket_draw(
}
}
static void std_node_socket_interface_draw(bContext * /*C*/, uiLayout *layout, PointerRNA *ptr)
static void std_node_socket_interface_draw(ID *id,
bNodeTreeInterfaceSocket *interface_socket,
bContext * /*C*/,
uiLayout *layout)
{
bNodeSocket *sock = (bNodeSocket *)ptr->data;
int type = sock->typeinfo->type;
PointerRNA ptr, tree_ptr;
RNA_pointer_create(id, &RNA_NodeTreeInterfaceSocket, interface_socket, &ptr);
RNA_id_pointer_create(id, &tree_ptr);
PointerRNA tree_ptr;
RNA_id_pointer_create(ptr->owner_id, &tree_ptr);
const bNodeSocketType *typeinfo = interface_socket->socket_typeinfo();
BLI_assert(typeinfo != nullptr);
eNodeSocketDatatype type = eNodeSocketDatatype(typeinfo->type);
uiLayout *col = uiLayoutColumn(layout, false);
switch (type) {
case SOCK_FLOAT: {
uiItemR(col, ptr, "default_value", DEFAULT_FLAGS, IFACE_("Default"), ICON_NONE);
uiItemR(col, &ptr, "subtype", DEFAULT_FLAGS, IFACE_("Subtype"), ICON_NONE);
uiItemR(col, &ptr, "default_value", DEFAULT_FLAGS, IFACE_("Default"), ICON_NONE);
uiLayout *sub = uiLayoutColumn(col, true);
uiItemR(sub, ptr, "min_value", DEFAULT_FLAGS, IFACE_("Min"), ICON_NONE);
uiItemR(sub, ptr, "max_value", DEFAULT_FLAGS, IFACE_("Max"), ICON_NONE);
uiItemR(sub, &ptr, "min_value", DEFAULT_FLAGS, IFACE_("Min"), ICON_NONE);
uiItemR(sub, &ptr, "max_value", DEFAULT_FLAGS, IFACE_("Max"), ICON_NONE);
break;
}
case SOCK_INT: {
uiItemR(col, ptr, "default_value", DEFAULT_FLAGS, IFACE_("Default"), ICON_NONE);
uiItemR(col, &ptr, "subtype", DEFAULT_FLAGS, IFACE_("Subtype"), ICON_NONE);
uiItemR(col, &ptr, "default_value", DEFAULT_FLAGS, IFACE_("Default"), ICON_NONE);
uiLayout *sub = uiLayoutColumn(col, true);
uiItemR(sub, ptr, "min_value", DEFAULT_FLAGS, IFACE_("Min"), ICON_NONE);
uiItemR(sub, ptr, "max_value", DEFAULT_FLAGS, IFACE_("Max"), ICON_NONE);
uiItemR(sub, &ptr, "min_value", DEFAULT_FLAGS, IFACE_("Min"), ICON_NONE);
uiItemR(sub, &ptr, "max_value", DEFAULT_FLAGS, IFACE_("Max"), ICON_NONE);
break;
}
case SOCK_VECTOR: {
uiItemR(col, ptr, "default_value", UI_ITEM_R_EXPAND, IFACE_("Default"), ICON_NONE);
uiItemR(col, &ptr, "subtype", DEFAULT_FLAGS, IFACE_("Subtype"), ICON_NONE);
uiItemR(col, &ptr, "default_value", UI_ITEM_R_EXPAND, IFACE_("Default"), ICON_NONE);
uiLayout *sub = uiLayoutColumn(col, true);
uiItemR(sub, ptr, "min_value", DEFAULT_FLAGS, IFACE_("Min"), ICON_NONE);
uiItemR(sub, ptr, "max_value", DEFAULT_FLAGS, IFACE_("Max"), ICON_NONE);
uiItemR(sub, &ptr, "min_value", DEFAULT_FLAGS, IFACE_("Min"), ICON_NONE);
uiItemR(sub, &ptr, "max_value", DEFAULT_FLAGS, IFACE_("Max"), ICON_NONE);
break;
}
case SOCK_BOOLEAN:
@@ -1475,17 +1500,24 @@ static void std_node_socket_interface_draw(bContext * /*C*/, uiLayout *layout, P
case SOCK_IMAGE:
case SOCK_TEXTURE:
case SOCK_MATERIAL: {
uiItemR(col, ptr, "default_value", DEFAULT_FLAGS, IFACE_("Default"), ICON_NONE);
uiItemR(col, &ptr, "default_value", DEFAULT_FLAGS, IFACE_("Default"), ICON_NONE);
break;
}
case SOCK_SHADER:
case SOCK_GEOMETRY:
break;
case SOCK_CUSTOM:
BLI_assert_unreachable();
break;
}
col = uiLayoutColumn(layout, false);
uiItemR(col, ptr, "hide_value", DEFAULT_FLAGS, nullptr, ICON_NONE);
uiItemR(col, &ptr, "hide_value", DEFAULT_FLAGS, nullptr, ICON_NONE);
const bNodeTree *node_tree = reinterpret_cast<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);
}
}
@@ -1497,15 +1529,20 @@ static void node_socket_virtual_draw_color(bContext * /*C*/,
copy_v4_v4(r_color, virtual_node_socket_color);
}
static void node_socket_virtual_draw_color_simple(const bNodeSocketType * /*type*/, float *r_color)
{
copy_v4_v4(r_color, virtual_node_socket_color);
}
} // namespace blender::ed::space_node
void ED_init_standard_node_socket_type(bNodeSocketType *stype)
{
using namespace blender::ed::space_node;
stype->draw = std_node_socket_draw;
stype->draw_color = std_node_socket_draw_color;
stype->draw_color = std_node_socket_color_funcs[stype->type];
stype->draw_color_simple = std_node_socket_color_simple_fn;
stype->interface_draw = std_node_socket_interface_draw;
stype->interface_draw_color = std_node_socket_interface_draw_color;
}
void ED_init_node_socket_type_virtual(bNodeSocketType *stype)
@@ -1513,6 +1550,7 @@ void ED_init_node_socket_type_virtual(bNodeSocketType *stype)
using namespace blender::ed::space_node;
stype->draw = node_socket_button_label;
stype->draw_color = node_socket_virtual_draw_color;
stype->draw_color_simple = node_socket_virtual_draw_color_simple;
}
void ED_node_type_draw_color(const char *idname, float *r_color)
@@ -1570,8 +1608,8 @@ void draw_nodespace_back_pix(const bContext &C,
DRW_draw_view(&C);
BLI_thread_unlock(LOCK_DRAW_IMAGE);
GPU_framebuffer_bind_no_srgb(old_fb);
/* Draw manager changes the depth state. Set it back to NONE. Without this the node preview
* images aren't drawn correctly. */
/* Draw manager changes the depth state. Set it back to NONE. Without this the
* node preview images aren't drawn correctly. */
GPU_depth_test(GPU_DEPTH_NONE);
void *lock;
@@ -1585,7 +1623,8 @@ void draw_nodespace_back_pix(const bContext &C,
const float x = (region.winx - snode.zoom * ibuf->x) / 2 + offset_x;
const float y = (region.winy - snode.zoom * ibuf->y) / 2 + offset_y;
/** \note draw selected info on backdrop */
/** \note draw selected info on backdrop
*/
if (snode.edittree) {
bNode *node = (bNode *)snode.edittree->nodes.first;
rctf *viewer_border = &snode.nodetree->viewer_border;
@@ -2051,8 +2090,7 @@ static bool node_link_is_field_link(const SpaceNode &snode, const bNodeLink &lin
return false;
}
static NodeLinkDrawConfig nodelink_get_draw_config(const bContext &C,
const View2D &v2d,
static NodeLinkDrawConfig nodelink_get_draw_config(const View2D &v2d,
const SpaceNode &snode,
const bNodeLink &link,
const int th_col1,
@@ -2066,8 +2104,6 @@ static NodeLinkDrawConfig nodelink_get_draw_config(const bContext &C,
draw_config.th_col2 = th_col2;
draw_config.th_col3 = th_col3;
const bNodeTree &node_tree = *snode.edittree;
draw_config.dim_factor = selected ? 1.0f : node_link_dim_factor(v2d, link);
bTheme *btheme = UI_GetTheme();
@@ -2092,22 +2128,18 @@ static NodeLinkDrawConfig nodelink_get_draw_config(const bContext &C,
if (snode.overlay.flag & SN_OVERLAY_SHOW_OVERLAYS &&
snode.overlay.flag & SN_OVERLAY_SHOW_WIRE_COLORS)
{
PointerRNA from_node_ptr, to_node_ptr;
RNA_pointer_create((ID *)&node_tree, &RNA_Node, link.fromnode, &from_node_ptr);
RNA_pointer_create((ID *)&node_tree, &RNA_Node, link.tonode, &to_node_ptr);
if (link.fromsock) {
node_socket_color_get(C, node_tree, from_node_ptr, *link.fromsock, draw_config.start_color);
node_socket_color_get(*link.fromsock->typeinfo, draw_config.start_color);
}
else {
node_socket_color_get(C, node_tree, to_node_ptr, *link.tosock, draw_config.start_color);
node_socket_color_get(*link.tosock->typeinfo, draw_config.start_color);
}
if (link.tosock) {
node_socket_color_get(C, node_tree, to_node_ptr, *link.tosock, draw_config.end_color);
node_socket_color_get(*link.tosock->typeinfo, draw_config.end_color);
}
else {
node_socket_color_get(C, node_tree, from_node_ptr, *link.fromsock, draw_config.end_color);
node_socket_color_get(*link.fromsock->typeinfo, draw_config.end_color);
}
}
else {
@@ -2186,8 +2218,7 @@ static void node_draw_link_bezier_ex(const SpaceNode &snode,
}
}
void node_draw_link_bezier(const bContext &C,
const View2D &v2d,
void node_draw_link_bezier(const View2D &v2d,
const SpaceNode &snode,
const bNodeLink &link,
const int th_col1,
@@ -2200,13 +2231,12 @@ void node_draw_link_bezier(const bContext &C,
return;
}
const NodeLinkDrawConfig draw_config = nodelink_get_draw_config(
C, v2d, snode, link, th_col1, th_col2, th_col3, selected);
v2d, snode, link, th_col1, th_col2, th_col3, selected);
node_draw_link_bezier_ex(snode, draw_config, points);
}
void node_draw_link(const bContext &C,
const View2D &v2d,
void node_draw_link(const View2D &v2d,
const SpaceNode &snode,
const bNodeLink &link,
const bool selected)
@@ -2249,7 +2279,7 @@ void node_draw_link(const bContext &C,
}
}
node_draw_link_bezier(C, v2d, snode, link, th_col1, th_col2, th_col3, selected);
node_draw_link_bezier(v2d, snode, link, th_col1, th_col2, th_col3, selected);
}
std::array<float2, 4> node_link_bezier_points_dragged(const SpaceNode &snode,
@@ -2266,10 +2296,7 @@ std::array<float2, 4> node_link_bezier_points_dragged(const SpaceNode &snode,
return points;
}
void node_draw_link_dragged(const bContext &C,
const View2D &v2d,
const SpaceNode &snode,
const bNodeLink &link)
void node_draw_link_dragged(const View2D &v2d, const SpaceNode &snode, const bNodeLink &link)
{
if (link.fromsock == nullptr && link.tosock == nullptr) {
return;
@@ -2278,7 +2305,7 @@ void node_draw_link_dragged(const bContext &C,
const std::array<float2, 4> points = node_link_bezier_points_dragged(snode, link);
const NodeLinkDrawConfig draw_config = nodelink_get_draw_config(
C, v2d, snode, link, TH_ACTIVE, TH_ACTIVE, TH_WIRE, true);
v2d, snode, link, TH_ACTIVE, TH_ACTIVE, TH_WIRE, true);
/* End marker outline. */
node_draw_link_end_markers(link, draw_config, points, true);
/* Link. */
@@ -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,9 +105,10 @@ 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);
new_group_input_socket->flag |= SOCK_HIDDEN;
bNodeSocket *new_group_input_socket = nodeFindSocket(node, in_out, socket_iface->identifier);
if (new_group_input_socket) {
new_group_input_socket->flag |= SOCK_HIDDEN;
}
}
}
@@ -109,37 +117,36 @@ static void add_group_input_node_fn(nodes::LinkSearchOpParams &params)
socket->flag |= SOCK_HIDDEN;
}
bNodeSocket *socket = (bNodeSocket *)BLI_findlink(&group_input.outputs, group_input_index);
if (socket == nullptr) {
/* Adding sockets can fail in some cases. There's no good reason not to be safe here. */
return;
}
/* Unhide the socket for the new input in the new node and make a connection to it. */
socket->flag &= ~SOCK_HIDDEN;
nodeAddLink(&params.node_tree, &group_input, socket, &params.node, &params.socket);
bNodeSocket *socket = nodeFindSocket(&group_input, in_out, socket_iface->identifier);
if (socket) {
/* Unhide the socket for the new input in the new node and make a connection to it. */
socket->flag &= ~SOCK_HIDDEN;
nodeAddLink(&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);
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);
}
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;
if (node_tree.typeinfo->validate_link && !node_tree.typeinfo->validate_link(from, to)) {
continue;
node_tree.tree_interface.foreach_item([&](const bNodeTreeInterfaceItem &item) {
if (item.item_type != NODE_INTERFACE_SOCKET) {
return true;
}
const bNodeTreeInterfaceSocket &interface_socket =
reinterpret_cast<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)) {
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);
+498 -232
View File
@@ -141,7 +141,9 @@ void ED_node_tree_update(const bContext *C)
if (snode) {
snode_set_context(*C);
id_us_ensure_real(&snode->nodetree->id);
if (snode->nodetree) {
id_us_ensure_real(&snode->nodetree->id);
}
}
}
@@ -344,6 +346,309 @@ float2 node_from_view(const bNode &node, const float2 &co)
;
}
static bool is_node_panels_supported(const bNode &node)
{
return node.declaration() && node.declaration()->use_custom_socket_order;
}
/* Draw UI for options, buttons, and previews. */
static bool node_update_basis_buttons(
const bContext &C, bNodeTree &ntree, bNode &node, uiBlock &block, int &dy)
{
/* Buttons rect? */
const bool node_options = node.typeinfo->draw_buttons && (node.flag & NODE_OPTIONS);
if (!node_options) {
return false;
}
PointerRNA nodeptr;
RNA_pointer_create(&ntree.id, &RNA_Node, &node, &nodeptr);
/* Get "global" coordinates. */
float2 loc = node_to_view(node, float2(0));
/* Round the node origin because text contents are always pixel-aligned. */
loc.x = round(loc.x);
loc.y = round(loc.y);
dy -= NODE_DYS / 2;
uiLayout *layout = UI_block_layout(&block,
UI_LAYOUT_VERTICAL,
UI_LAYOUT_PANEL,
loc.x + NODE_DYS,
dy,
NODE_WIDTH(node) - NODE_DY,
0,
0,
UI_style_get_dpi());
if (node.flag & NODE_MUTED) {
uiLayoutSetActive(layout, false);
}
uiLayoutSetContextPointer(layout, "node", &nodeptr);
node.typeinfo->draw_buttons(layout, (bContext *)&C, &nodeptr);
UI_block_align_end(&block);
int buty;
UI_block_layout_resolve(&block, nullptr, &buty);
dy = buty - NODE_DYS / 2;
return true;
}
static bool node_update_basis_socket(const bContext &C,
bNodeTree &ntree,
bNode &node,
bNodeSocket &socket,
uiBlock &block,
const int &locx,
int &locy)
{
if (!socket.is_visible()) {
return false;
}
const int topy = locy;
PointerRNA nodeptr, sockptr;
RNA_pointer_create(&ntree.id, &RNA_Node, &node, &nodeptr);
RNA_pointer_create(&ntree.id, &RNA_NodeSocket, &socket, &sockptr);
const eNodeSocketInOut in_out = eNodeSocketInOut(socket.in_out);
/* Add the half the height of a multi-input socket to cursor Y
* to account for the increased height of the taller sockets. */
const bool is_multi_input = (in_out == SOCK_IN && socket.flag & SOCK_MULTI_INPUT);
const float multi_input_socket_offset = is_multi_input ?
std::max(socket.runtime->total_inputs - 2, 0) *
NODE_MULTI_INPUT_LINK_GAP :
0.0f;
locy -= multi_input_socket_offset * 0.5f;
uiLayout *layout = UI_block_layout(&block,
UI_LAYOUT_VERTICAL,
UI_LAYOUT_PANEL,
locx + NODE_DYS,
locy,
NODE_WIDTH(node) - NODE_DY,
NODE_DY,
0,
UI_style_get_dpi());
if (node.flag & NODE_MUTED) {
uiLayoutSetActive(layout, false);
}
/* Context pointers for current node and socket. */
uiLayoutSetContextPointer(layout, "node", &nodeptr);
uiLayoutSetContextPointer(layout, "socket", &sockptr);
uiLayout *row = uiLayoutRow(layout, true);
/* Align output buttons to the right. */
uiLayoutSetAlignment(row, in_out == SOCK_IN ? UI_LAYOUT_ALIGN_EXPAND : UI_LAYOUT_ALIGN_RIGHT);
const char *socket_label = bke::nodeSocketLabel(&socket);
const char *socket_translation_context = node_socket_get_translation_context(socket);
socket.typeinfo->draw((bContext *)&C,
row,
&sockptr,
&nodeptr,
CTX_IFACE_(socket_translation_context, socket_label));
node_socket_add_tooltip_in_node_editor(ntree, socket, *row);
UI_block_align_end(&block);
int buty;
UI_block_layout_resolve(&block, nullptr, &buty);
/* Ensure minimum socket height in case layout is empty. */
buty = min_ii(buty, topy - NODE_DY);
/* Horizontal position for input/output. */
const float offsetx = (in_out == SOCK_IN ? 0.0f : NODE_WIDTH(node));
/* Round the socket location to stop it from jiggling. */
socket.runtime->location = float2(round(locx + offsetx), round(locy - NODE_DYS));
locy = buty - multi_input_socket_offset * 0.5;
return true;
}
/* Advanced drawing with panels and arbitrary input/output ordering. */
static void node_update_basis_from_declaration(
const bContext &C, bNodeTree &ntree, bNode &node, uiBlock &block, const int locx, int &locy)
{
namespace nodes = blender::nodes;
BLI_assert(is_node_panels_supported(node));
BLI_assert(node.runtime->panels.size() == node.num_panel_states);
const nodes::NodeDeclaration &decl = *node.declaration();
const bool has_buttons = node_update_basis_buttons(C, ntree, node, block, locy);
bNodeSocket *current_input = static_cast<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;
/* The panel stack keeps track of the hierarchy of panels. When a panel declaration is found a
* new #PanelUpdate is added to the stack. Items in the declaration are added to the top panel of
* the stack. Each panel expects a number of items to be added, after which the panel is removed
* from the stack again. */
struct PanelUpdate {
/* How many items still to add. */
int remaining_items;
/* True if the panel or its parent is collapsed. */
bool is_collapsed;
/* Location data, needed to finalize the panel when all items have been added. */
bke::bNodePanelRuntime *runtime;
};
Stack<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;
}
/* Space after header and between items. */
if (!is_parent_collapsed) {
locy -= NODE_SOCKDY;
}
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, current_panel_runtime});
/* Round the socket location to stop it from jiggling. */
current_panel_runtime->location_y = round(locy + NODE_DYS);
current_panel_runtime->max_content_y = current_panel_runtime->min_content_y = round(locy);
++current_panel_state;
++current_panel_runtime;
}
else if (nodes::SocketDeclaration *socket_decl = dynamic_cast<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()) {
PanelUpdate &top_panel = panel_updates.peek();
if (top_panel.remaining_items > 0) {
/* Incomplete panel, continue adding items. */
break;
}
/* Finalize the vertical extent of the content. */
top_panel.runtime->min_content_y = round(locy - NODE_DYS / 4);
/* Close panel and continue checking parent. */
panel_updates.pop();
}
}
/* Enough items should have been added to close all panels. */
BLI_assert(panel_updates.is_empty());
/* Little bit of space in end. */
if (has_sockets || !has_buttons) {
locy -= NODE_DYS / 2;
}
}
/* Conventional drawing in outputs/buttons/inputs order. */
static void node_update_basis_from_socket_lists(
const bContext &C, bNodeTree &ntree, bNode &node, uiBlock &block, const int locx, int &locy)
{
const bool node_options = node.typeinfo->draw_buttons && (node.flag & NODE_OPTIONS);
const bool inputs_first = node.inputs.first && !(node.outputs.first || node_options);
/* Add a little bit of padding above the top socket. */
if (node.outputs.first || inputs_first) {
locy -= NODE_DYS / 2;
}
/* Output sockets. */
bool add_output_space = false;
for (bNodeSocket *socket : node.output_sockets()) {
/* Clear flag, conventional drawing does not support panels. */
socket->flag &= ~SOCK_PANEL_COLLAPSED;
if (node_update_basis_socket(C, ntree, node, *socket, block, locx, locy)) {
if (socket->next) {
locy -= NODE_SOCKDY;
}
add_output_space = true;
}
}
if (add_output_space) {
locy -= NODE_DY / 4;
}
node_update_basis_buttons(C, ntree, node, block, locy);
/* Input sockets. */
for (bNodeSocket *socket : node.input_sockets()) {
/* Clear flag, conventional drawing does not support panels. */
socket->flag &= ~SOCK_PANEL_COLLAPSED;
if (node_update_basis_socket(C, ntree, node, *socket, block, locx, locy)) {
if (socket->next) {
locy -= NODE_SOCKDY;
}
}
}
/* Little bit of space in end. */
if (node.inputs.first || (node.flag & NODE_OPTIONS) == 0) {
locy -= NODE_DYS / 2;
}
}
/**
* Based on settings and sockets in node, set drawing rect info.
*/
@@ -356,9 +661,6 @@ static void node_update_basis(const bContext &C,
PointerRNA nodeptr;
RNA_pointer_create(&ntree.id, &RNA_Node, &node, &nodeptr);
const bool node_options = node.typeinfo->draw_buttons && (node.flag & NODE_OPTIONS);
const bool inputs_first = node.inputs.first && !(node.outputs.first || node_options);
/* Get "global" coordinates. */
float2 loc = node_to_view(node, float2(0));
/* Round the node origin because text contents are always pixel-aligned. */
@@ -370,172 +672,11 @@ static void node_update_basis(const bContext &C,
/* Header. */
dy -= NODE_DY;
/* Add a little bit of padding above the top socket. */
if (node.outputs.first || inputs_first) {
dy -= NODE_DYS / 2;
if (is_node_panels_supported(node)) {
node_update_basis_from_declaration(C, ntree, node, block, loc.x, dy);
}
/* Output sockets. */
bool add_output_space = false;
int buty;
for (bNodeSocket *socket : node.output_sockets()) {
if (!socket->is_visible()) {
continue;
}
PointerRNA sockptr;
RNA_pointer_create(&ntree.id, &RNA_NodeSocket, socket, &sockptr);
uiLayout *layout = UI_block_layout(&block,
UI_LAYOUT_VERTICAL,
UI_LAYOUT_PANEL,
loc.x + NODE_DYS,
dy,
NODE_WIDTH(node) - NODE_DY,
NODE_DY,
0,
UI_style_get_dpi());
if (node.flag & NODE_MUTED) {
uiLayoutSetActive(layout, false);
}
/* Context pointers for current node and socket. */
uiLayoutSetContextPointer(layout, "node", &nodeptr);
uiLayoutSetContextPointer(layout, "socket", &sockptr);
/* Align output buttons to the right. */
uiLayout *row = uiLayoutRow(layout, true);
uiLayoutSetAlignment(row, UI_LAYOUT_ALIGN_RIGHT);
const char *socket_label = bke::nodeSocketLabel(socket);
const char *socket_translation_context = node_socket_get_translation_context(*socket);
socket->typeinfo->draw((bContext *)&C,
row,
&sockptr,
&nodeptr,
CTX_IFACE_(socket_translation_context, socket_label));
node_socket_add_tooltip_in_node_editor(ntree, *socket, *row);
UI_block_align_end(&block);
UI_block_layout_resolve(&block, nullptr, &buty);
/* Ensure minimum socket height in case layout is empty. */
buty = min_ii(buty, dy - NODE_DY);
/* Round the socket location to stop it from jiggling. */
socket->runtime->location = float2(round(loc.x + NODE_WIDTH(node)), round(dy - NODE_DYS));
dy = buty;
if (socket->next) {
dy -= NODE_SOCKDY;
}
add_output_space = true;
}
if (add_output_space) {
dy -= NODE_DY / 4;
}
/* Buttons rect? */
if (node_options) {
dy -= NODE_DYS / 2;
uiLayout *layout = UI_block_layout(&block,
UI_LAYOUT_VERTICAL,
UI_LAYOUT_PANEL,
loc.x + NODE_DYS,
dy,
NODE_WIDTH(node) - NODE_DY,
0,
0,
UI_style_get_dpi());
if (node.flag & NODE_MUTED) {
uiLayoutSetActive(layout, false);
}
uiLayoutSetContextPointer(layout, "node", &nodeptr);
node.typeinfo->draw_buttons(layout, (bContext *)&C, &nodeptr);
UI_block_align_end(&block);
UI_block_layout_resolve(&block, nullptr, &buty);
dy = buty - NODE_DYS / 2;
}
/* Input sockets. */
for (bNodeSocket *socket : node.input_sockets()) {
if (!socket->is_visible()) {
continue;
}
PointerRNA sockptr;
RNA_pointer_create(&ntree.id, &RNA_NodeSocket, socket, &sockptr);
/* Add the half the height of a multi-input socket to cursor Y
* to account for the increased height of the taller sockets. */
float multi_input_socket_offset = 0.0f;
if (socket->flag & SOCK_MULTI_INPUT) {
if (socket->runtime->total_inputs > 2) {
multi_input_socket_offset = (socket->runtime->total_inputs - 2) *
NODE_MULTI_INPUT_LINK_GAP;
}
}
dy -= multi_input_socket_offset * 0.5f;
uiLayout *layout = UI_block_layout(&block,
UI_LAYOUT_VERTICAL,
UI_LAYOUT_PANEL,
loc.x + NODE_DYS,
dy,
NODE_WIDTH(node) - NODE_DY,
NODE_DY,
0,
UI_style_get_dpi());
if (node.flag & NODE_MUTED) {
uiLayoutSetActive(layout, false);
}
/* Context pointers for current node and socket. */
uiLayoutSetContextPointer(layout, "node", &nodeptr);
uiLayoutSetContextPointer(layout, "socket", &sockptr);
uiLayout *row = uiLayoutRow(layout, true);
const char *socket_label = bke::nodeSocketLabel(socket);
const char *socket_translation_context = node_socket_get_translation_context(*socket);
socket->typeinfo->draw((bContext *)&C,
row,
&sockptr,
&nodeptr,
CTX_IFACE_(socket_translation_context, socket_label));
node_socket_add_tooltip_in_node_editor(ntree, *socket, *row);
UI_block_align_end(&block);
UI_block_layout_resolve(&block, nullptr, &buty);
/* Ensure minimum socket height in case layout is empty. */
buty = min_ii(buty, dy - NODE_DY);
/* Round the socket vertical position to stop it from jiggling. */
socket->runtime->location = float2(loc.x, round(dy - NODE_DYS));
dy = buty - multi_input_socket_offset * 0.5;
if (socket->next) {
dy -= NODE_SOCKDY;
}
}
/* Little bit of space in end. */
if (node.inputs.first || (node.flag & NODE_OPTIONS) == 0) {
dy -= NODE_DYS / 2;
else {
node_update_basis_from_socket_lists(C, ntree, node, block, loc.x, dy);
}
node.runtime->totr.xmin = loc.x;
@@ -672,16 +813,13 @@ static int node_get_colorid(TreeDrawContext &tree_draw_ctx, const bNode &node)
}
}
static void node_draw_mute_line(const bContext &C,
const View2D &v2d,
const SpaceNode &snode,
const bNode &node)
static void node_draw_mute_line(const View2D &v2d, const SpaceNode &snode, const bNode &node)
{
GPU_blend(GPU_BLEND_ALPHA);
for (const bNodeLink &link : node.internal_links()) {
if (!nodeLinkIsHidden(&link)) {
node_draw_link_bezier(C, v2d, snode, link, TH_WIRE_INNER, TH_WIRE_INNER, TH_WIRE, false);
node_draw_link_bezier(v2d, snode, link, TH_WIRE_INNER, TH_WIRE_INNER, TH_WIRE, false);
}
}
@@ -779,18 +917,9 @@ static void node_socket_outline_color_get(const bool selected,
}
}
void node_socket_color_get(const bContext &C,
const bNodeTree &ntree,
PointerRNA &node_ptr,
const bNodeSocket &sock,
float r_color[4])
void node_socket_color_get(const bNodeSocketType &type, float r_color[4])
{
PointerRNA ptr;
BLI_assert(RNA_struct_is_a(node_ptr.type, &RNA_Node));
RNA_pointer_create(
&const_cast<ID &>(ntree.id), &RNA_NodeSocket, &const_cast<bNodeSocket &>(sock), &ptr);
sock.typeinfo->draw_color((bContext *)&C, &ptr, &node_ptr, r_color);
type.draw_color_simple(&type, r_color);
}
static void create_inspection_string_for_generic_value(const bNodeSocket &socket,
@@ -1215,9 +1344,7 @@ void node_socket_add_tooltip(const bNodeTree &ntree, const bNodeSocket &sock, ui
MEM_freeN);
}
static void node_socket_draw_nested(const bContext &C,
const bNodeTree &ntree,
PointerRNA &node_ptr,
static void node_socket_draw_nested(const bNodeTree &ntree,
uiBlock &block,
const bNodeSocket &sock,
const uint pos_id,
@@ -1232,7 +1359,7 @@ static void node_socket_draw_nested(const bContext &C,
float color[4];
float outline_color[4];
node_socket_color_get(C, ntree, node_ptr, sock, color);
node_socket_color_get(*sock.typeinfo, color);
node_socket_outline_color_get(selected, sock.type, outline_color);
node_socket_draw(sock,
@@ -1427,7 +1554,6 @@ static void node_draw_shadow(const SpaceNode &snode,
}
static void node_draw_sockets(const View2D &v2d,
const bContext &C,
const bNodeTree &ntree,
const bNode &node,
uiBlock &block,
@@ -1471,7 +1597,7 @@ static void node_draw_sockets(const View2D &v2d,
/* Socket inputs. */
int selected_input_len = 0;
for (const bNodeSocket *sock : node.input_sockets()) {
if (!sock->is_visible()) {
if (!sock->is_visible() || sock->is_panel_collapsed()) {
continue;
}
if (select_all || (sock->flag & SELECT)) {
@@ -1486,25 +1612,15 @@ static void node_draw_sockets(const View2D &v2d,
continue;
}
node_socket_draw_nested(C,
ntree,
node_ptr,
block,
*sock,
pos_id,
col_id,
shape_id,
size_id,
outline_col_id,
scale,
selected);
node_socket_draw_nested(
ntree, block, *sock, pos_id, col_id, shape_id, size_id, outline_col_id, scale, selected);
}
/* Socket outputs. */
int selected_output_len = 0;
if (draw_outputs) {
for (const bNodeSocket *sock : node.output_sockets()) {
if (!sock->is_visible()) {
if (!sock->is_visible() || sock->is_panel_collapsed()) {
continue;
}
if (select_all || (sock->flag & SELECT)) {
@@ -1512,18 +1628,8 @@ static void node_draw_sockets(const View2D &v2d,
continue;
}
node_socket_draw_nested(C,
ntree,
node_ptr,
block,
*sock,
pos_id,
col_id,
shape_id,
size_id,
outline_col_id,
scale,
selected);
node_socket_draw_nested(
ntree, block, *sock, pos_id, col_id, shape_id, size_id, outline_col_id, scale, selected);
}
}
@@ -1550,9 +1656,7 @@ static void node_draw_sockets(const View2D &v2d,
continue;
}
if (select_all || (sock->flag & SELECT)) {
node_socket_draw_nested(C,
ntree,
node_ptr,
node_socket_draw_nested(ntree,
block,
*sock,
pos_id,
@@ -1577,9 +1681,7 @@ static void node_draw_sockets(const View2D &v2d,
continue;
}
if (select_all || (sock->flag & SELECT)) {
node_socket_draw_nested(C,
ntree,
node_ptr,
node_socket_draw_nested(ntree,
block,
*sock,
pos_id,
@@ -1621,7 +1723,7 @@ static void node_draw_sockets(const View2D &v2d,
float color[4];
float outline_color[4];
node_socket_color_get(C, ntree, node_ptr, *socket, color);
node_socket_color_get(*socket->typeinfo, color);
node_socket_outline_color_get(socket->flag & SELECT, socket->type, outline_color);
const float2 location = socket->runtime->location;
@@ -1629,6 +1731,162 @@ static void node_draw_sockets(const View2D &v2d,
}
}
static void node_panel_toggle_button_cb(bContext *C, void *panel_state_argv, void *ntree_argv)
{
Main *bmain = CTX_data_main(C);
bNodePanelState *panel_state = static_cast<bNodePanelState *>(panel_state_argv);
bNodeTree *ntree = static_cast<bNodeTree *>(ntree_argv);
panel_state->flag ^= NODE_PANEL_COLLAPSED;
ED_node_tree_propagate_change(C, bmain, ntree);
}
/* Draw panel backgrounds first, so other node elements can be rendered on top. */
static void node_draw_panels_background(const bNode &node, uiBlock &block)
{
namespace nodes = blender::nodes;
BLI_assert(is_node_panels_supported(node));
BLI_assert(node.runtime->panels.size() == node.panel_states().size());
const nodes::NodeDeclaration &decl = *node.declaration();
const rctf &rct = node.runtime->totr;
int panel_i = 0;
for (const nodes::ItemDeclarationPtr &item_decl : decl.items) {
const nodes::PanelDeclaration *panel_decl = dynamic_cast<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 or collapsed panels. */
if (state.is_collapsed() || state.is_parent_collapsed()) {
++panel_i;
continue;
}
const bke::bNodePanelRuntime &runtime = node.runtime->panels[panel_i];
const rctf content_rect = {
rct.xmin,
rct.xmax,
runtime.min_content_y,
runtime.max_content_y,
};
UI_block_emboss_set(&block, UI_EMBOSS_NONE);
/* Panel background. */
float color_panel[4];
UI_GetThemeColorBlend4f(TH_BACK, TH_NODE, 0.2f, color_panel);
UI_draw_roundbox_corner_set(UI_CNR_NONE);
UI_draw_roundbox_4fv(&content_rect, true, BASIS_RAD, color_panel);
UI_block_emboss_set(&block, UI_EMBOSS);
++panel_i;
}
}
static void node_draw_panels(bNodeTree &ntree, const bNode &node, uiBlock &block)
{
namespace nodes = blender::nodes;
BLI_assert(is_node_panels_supported(node));
BLI_assert(node.runtime->panels.size() == node.panel_states().size());
const nodes::NodeDeclaration &decl = *node.declaration();
const rctf &rct = node.runtime->totr;
int panel_i = 0;
for (const nodes::ItemDeclarationPtr &item_decl : decl.items) {
const nodes::PanelDeclaration *panel_decl = dynamic_cast<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()) {
++panel_i;
continue;
}
const bke::bNodePanelRuntime &runtime = node.runtime->panels[panel_i];
const rctf rect = {
rct.xmin,
rct.xmax,
runtime.location_y - NODE_DYS,
runtime.location_y + NODE_DYS,
};
UI_block_emboss_set(&block, UI_EMBOSS_NONE);
/* Collapse/expand icon. */
const int but_size = U.widget_unit * 0.8f;
uiDefIconBut(&block,
UI_BTYPE_BUT_TOGGLE,
0,
state.is_collapsed() ? ICON_RIGHTARROW : ICON_DOWNARROW_HLT,
rct.xmin + (NODE_MARGIN_X / 3),
runtime.location_y - but_size / 2,
but_size,
but_size,
nullptr,
0.0f,
0.0f,
0.0f,
0.0f,
"");
/* Panel label. */
uiBut *but = uiDefBut(&block,
UI_BTYPE_LABEL,
0,
panel_decl->name.c_str(),
int(rct.xmin + NODE_MARGIN_X + 0.4f),
int(runtime.location_y - NODE_DYS),
short(rct.xmax - rct.xmin - 0.35f * U.widget_unit),
short(NODE_DY),
nullptr,
0,
0,
0,
0,
"");
if (node.flag & NODE_MUTED) {
UI_but_flag_enable(but, UI_BUT_INACTIVE);
}
/* Invisible button covering the entire header for collapsing/expanding. */
but = uiDefIconBut(&block,
UI_BTYPE_BUT_TOGGLE,
0,
ICON_NONE,
rect.xmin,
rect.ymin,
rect.xmax - rect.xmin,
rect.ymax - rect.ymin,
nullptr,
0.0f,
0.0f,
0.0f,
0.0f,
"");
UI_but_func_set(
but, node_panel_toggle_button_cb, const_cast<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) {
@@ -2458,7 +2716,7 @@ static void node_draw_basis(const bContext &C,
/* Wire across the node when muted/disabled. */
if (node.flag & NODE_MUTED) {
node_draw_mute_line(C, v2d, snode, node);
node_draw_mute_line(v2d, snode, node);
}
/* Body. */
@@ -2498,6 +2756,10 @@ static void node_draw_basis(const bContext &C,
UI_draw_roundbox_corner_set(UI_CNR_BOTTOM_LEFT | UI_CNR_BOTTOM_RIGHT);
UI_draw_roundbox_4fv(&rect, true, BASIS_RAD, color);
if (is_node_panels_supported(node)) {
node_draw_panels_background(node, block);
}
}
/* Header underline. */
@@ -2562,7 +2824,11 @@ static void node_draw_basis(const bContext &C,
/* Skip slow socket drawing if zoom is small. */
if (scale > 0.2f) {
node_draw_sockets(v2d, C, ntree, node, block, true, false);
node_draw_sockets(v2d, ntree, node, block, true, false);
}
if (is_node_panels_supported(node)) {
node_draw_panels(ntree, node, block);
}
UI_block_end(&C, &block);
@@ -2593,7 +2859,7 @@ static void node_draw_hidden(const bContext &C,
/* Wire across the node when muted/disabled. */
if (node.flag & NODE_MUTED) {
node_draw_mute_line(C, v2d, snode, node);
node_draw_mute_line(v2d, snode, node);
}
/* Body. */
@@ -2743,7 +3009,7 @@ static void node_draw_hidden(const bContext &C,
immUnbindProgram();
GPU_blend(GPU_BLEND_NONE);
node_draw_sockets(v2d, C, ntree, node, block, true, false);
node_draw_sockets(v2d, ntree, node, block, true, false);
UI_block_end(&C, &block);
UI_block_draw(&C, &block);
@@ -3118,7 +3384,7 @@ static void reroute_node_draw(
/* Only draw input socket as they all are placed on the same position highlight
* if node itself is selected, since we don't display the node body separately. */
node_draw_sockets(region.v2d, C, ntree, node, block, false, node.flag & SELECT);
node_draw_sockets(region.v2d, ntree, node, block, false, node.flag & SELECT);
UI_block_end(&C, &block);
UI_block_draw(&C, &block);
@@ -3299,7 +3565,7 @@ static void node_draw_zones(TreeDrawContext & /*tree_draw_ctx*/,
return bounding_box_area_by_zone[a] > bounding_box_area_by_zone[b];
});
/* Draw all the contour lines after to prevent them from getting hidden by overlapping zones. */
/* Draw all the contour lines after to prevent them from getting hidden by overlapping zones. */
for (const int zone_i : zone_draw_order) {
float zone_color[4];
UI_GetThemeColor4fv(get_theme_id(zone_i), zone_color);
@@ -3378,14 +3644,14 @@ static void node_draw_nodetree(const bContext &C,
for (const bNodeLink *link : ntree.all_links()) {
if (!nodeLinkIsHidden(link) && !bke::nodeLinkIsSelected(link)) {
node_draw_link(C, region.v2d, snode, *link, false);
node_draw_link(region.v2d, snode, *link, false);
}
}
/* Draw selected node links after the unselected ones, so they are shown on top. */
for (const bNodeLink *link : ntree.all_links()) {
if (!nodeLinkIsHidden(link) && bke::nodeLinkIsSelected(link)) {
node_draw_link(C, region.v2d, snode, *link, true);
node_draw_link(region.v2d, snode, *link, true);
}
}
@@ -3636,7 +3902,7 @@ void node_draw_space(const bContext &C, ARegion &region)
GPU_line_smooth(true);
if (snode.runtime->linkdrag) {
for (const bNodeLink &link : snode.runtime->linkdrag->links) {
node_draw_link_dragged(C, v2d, snode, link);
node_draw_link_dragged(v2d, snode, link);
}
}
GPU_line_smooth(false);
@@ -2218,460 +2218,6 @@ void NODE_OT_node_copy_color(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
/** \name Node-Tree Add Interface Socket Operator
* \{ */
static bNodeSocket *ntree_get_active_interface_socket(const ListBase *lb)
{
LISTBASE_FOREACH (bNodeSocket *, socket, lb) {
if (socket->flag & SELECT) {
return socket;
}
}
return nullptr;
}
static int ntree_socket_add_exec(bContext *C, wmOperator *op)
{
SpaceNode *snode = CTX_wm_space_node(C);
bNodeTree *ntree = snode->edittree;
PointerRNA ntree_ptr;
RNA_id_pointer_create((ID *)ntree, &ntree_ptr);
const eNodeSocketInOut in_out = (eNodeSocketInOut)RNA_enum_get(op->ptr, "in_out");
ListBase *sockets = (in_out == SOCK_IN) ? &ntree->inputs : &ntree->outputs;
const char *default_name = (in_out == SOCK_IN) ? DATA_("Input") : DATA_("Output");
bNodeSocket *active_sock = ntree_get_active_interface_socket(sockets);
bNodeSocket *sock;
if (active_sock) {
/* Insert a copy of the active socket right after it. */
sock = blender::bke::ntreeInsertSocketInterface(
ntree, in_out, active_sock->idname, active_sock->next, active_sock->name);
/* XXX this only works for actual sockets, not interface templates! */
// nodeSocketCopyValue(sock, &ntree_ptr, active_sock, &ntree_ptr);
}
else {
/* XXX TODO: define default socket type for a tree! */
sock = ntreeAddSocketInterface(ntree, in_out, "NodeSocketFloat", default_name);
}
/* Deactivate sockets. */
LISTBASE_FOREACH (bNodeSocket *, socket_iter, sockets) {
socket_iter->flag &= ~SELECT;
}
/* Make the new socket selected. */
sock->flag |= SELECT;
ED_node_tree_propagate_change(C, CTX_data_main(C), snode->edittree);
WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr);
return OPERATOR_FINISHED;
}
void NODE_OT_tree_socket_add(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Add Node Tree Interface Socket";
ot->description = "Add an input or output to the active node tree";
ot->idname = "NODE_OT_tree_socket_add";
/* api callbacks */
ot->exec = ntree_socket_add_exec;
ot->poll = ED_operator_node_editable;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
RNA_def_enum(ot->srna, "in_out", rna_enum_node_socket_in_out_items, SOCK_IN, "Socket Type", "");
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Node-Tree Remove Interface Socket Operator
* \{ */
static int ntree_socket_remove_exec(bContext *C, wmOperator *op)
{
SpaceNode *snode = CTX_wm_space_node(C);
bNodeTree *ntree = snode->edittree;
const eNodeSocketInOut in_out = (eNodeSocketInOut)RNA_enum_get(op->ptr, "in_out");
bNodeSocket *iosock = ntree_get_active_interface_socket(in_out == SOCK_IN ? &ntree->inputs :
&ntree->outputs);
if (iosock == nullptr) {
return OPERATOR_CANCELLED;
}
/* Preferably next socket becomes active, otherwise try previous socket. */
bNodeSocket *active_sock = (iosock->next ? iosock->next : iosock->prev);
ntreeRemoveSocketInterface(ntree, iosock);
/* Set active socket. */
if (active_sock) {
active_sock->flag |= SELECT;
}
ED_node_tree_propagate_change(C, CTX_data_main(C), ntree);
WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr);
return OPERATOR_FINISHED;
}
void NODE_OT_tree_socket_remove(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Remove Node Tree Interface Socket";
ot->description = "Remove an input or output from the active node tree";
ot->idname = "NODE_OT_tree_socket_remove";
/* api callbacks */
ot->exec = ntree_socket_remove_exec;
ot->poll = ED_operator_node_editable;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
RNA_def_enum(ot->srna, "in_out", rna_enum_node_socket_in_out_items, SOCK_IN, "Socket Type", "");
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Node-Tree Change Interface Socket Type Operator
* \{ */
static int ntree_socket_change_type_exec(bContext *C, wmOperator *op)
{
SpaceNode *snode = CTX_wm_space_node(C);
bNodeTree *ntree = snode->edittree;
const eNodeSocketInOut in_out = (eNodeSocketInOut)RNA_enum_get(op->ptr, "in_out");
const bNodeSocketType *socket_type = rna_node_socket_type_from_enum(
RNA_enum_get(op->ptr, "socket_type"));
ListBase *sockets = (in_out == SOCK_IN) ? &ntree->inputs : &ntree->outputs;
Main *main = CTX_data_main(C);
bNodeSocket *iosock = ntree_get_active_interface_socket(sockets);
if (iosock == nullptr) {
return OPERATOR_CANCELLED;
}
/* The type remains the same, so we don't need to change anything. */
if (iosock->typeinfo == socket_type) {
return OPERATOR_FINISHED;
}
blender::bke::nodeModifySocketType(ntree, nullptr, iosock, socket_type->idname);
/* Need the extra update here because the loop above does not check for valid links in the node
* group we're currently editing. */
BKE_ntree_update_tag_interface(ntree);
/* Deactivate sockets. */
LISTBASE_FOREACH (bNodeSocket *, socket_iter, sockets) {
socket_iter->flag &= ~SELECT;
}
/* Make the new socket active. */
iosock->flag |= SELECT;
ED_node_tree_propagate_change(C, main, ntree);
WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr);
return OPERATOR_FINISHED;
}
static bool socket_change_poll_type(void *userdata, bNodeSocketType *socket_type)
{
/* Check if the node tree supports the socket type. */
bNodeTreeType *ntreetype = (bNodeTreeType *)userdata;
if (ntreetype->valid_socket_type && !ntreetype->valid_socket_type(ntreetype, socket_type)) {
return false;
}
/* Only use basic socket types for this enum. */
if (socket_type->subtype != PROP_NONE) {
return false;
}
return true;
}
static const EnumPropertyItem *socket_change_type_itemf(bContext *C,
PointerRNA * /*ptr*/,
PropertyRNA * /*prop*/,
bool *r_free)
{
if (!C) {
return rna_enum_dummy_NULL_items;
}
SpaceNode *snode = CTX_wm_space_node(C);
if (!snode || !snode->edittree) {
return rna_enum_dummy_NULL_items;
}
return rna_node_socket_type_itemf(snode->edittree->typeinfo, socket_change_poll_type, r_free);
}
void NODE_OT_tree_socket_change_type(wmOperatorType *ot)
{
PropertyRNA *prop;
/* identifiers */
ot->name = "Change Node Tree Interface Socket Type";
ot->description = "Change the type of an input or output of the active node tree";
ot->idname = "NODE_OT_tree_socket_change_type";
/* api callbacks */
ot->invoke = WM_menu_invoke;
ot->exec = ntree_socket_change_type_exec;
ot->poll = ED_operator_node_editable;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
RNA_def_enum(ot->srna, "in_out", rna_enum_node_socket_in_out_items, SOCK_IN, "Socket Type", "");
prop = RNA_def_enum(ot->srna, "socket_type", rna_enum_dummy_DEFAULT_items, 0, "Socket Type", "");
RNA_def_enum_funcs(prop, socket_change_type_itemf);
ot->prop = prop;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Node-Tree Change Interface Socket Subtype Operator
* \{ */
static int ntree_socket_change_subtype_exec(bContext *C, wmOperator *op)
{
Main *main = CTX_data_main(C);
const int socket_subtype = RNA_enum_get(op->ptr, "socket_subtype");
PointerRNA io_socket_ptr = CTX_data_pointer_get_type(
C, "interface_socket", &RNA_NodeSocketInterface);
bNodeSocket *io_socket = static_cast<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 rna_enum_dummy_NULL_items;
}
SpaceNode *snode = CTX_wm_space_node(C);
if (!snode || !snode->edittree) {
return rna_enum_dummy_NULL_items;
}
PointerRNA active_socket_ptr = CTX_data_pointer_get_type(
C, "interface_socket", &RNA_NodeSocketInterface);
const bNodeSocket *active_socket = static_cast<const bNodeSocket *>(active_socket_ptr.data);
if (!active_socket) {
return rna_enum_dummy_NULL_items;
}
const Set<int> subtypes = socket_type_get_subtypes(eNodeSocketDatatype(active_socket->type));
if (subtypes.is_empty()) {
return rna_enum_dummy_NULL_items;
}
EnumPropertyItem *items = nullptr;
int items_count = 0;
for (const EnumPropertyItem *item = rna_enum_property_subtype_items; item->name != nullptr;
item++) {
if (subtypes.contains(item->value)) {
RNA_enum_item_add(&items, &items_count, item);
}
}
if (items_count == 0) {
return rna_enum_dummy_NULL_items;
}
RNA_enum_item_end(&items, &items_count);
*r_free = true;
return items;
}
static bool ntree_socket_change_subtype_poll(bContext *C)
{
if (!ED_operator_node_editable(C)) {
return false;
}
PointerRNA io_socket_ptr = CTX_data_pointer_get_type(
C, "interface_socket", &RNA_NodeSocketInterface);
const bNodeSocket *io_socket = static_cast<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", rna_enum_dummy_DEFAULT_items, 0, "Socket Subtype", "");
RNA_def_enum_funcs(ot->prop, socket_change_subtype_itemf);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Node-Tree Move Interface Socket Operator
* \{ */
static const EnumPropertyItem move_direction_items[] = {
{1, "UP", 0, "Up", ""},
{2, "DOWN", 0, "Down", ""},
{0, nullptr, 0, nullptr, nullptr},
};
static int ntree_socket_move_exec(bContext *C, wmOperator *op)
{
SpaceNode *snode = CTX_wm_space_node(C);
bNodeTree *ntree = snode->edittree;
int direction = RNA_enum_get(op->ptr, "direction");
const eNodeSocketInOut in_out = (eNodeSocketInOut)RNA_enum_get(op->ptr, "in_out");
ListBase *sockets = in_out == SOCK_IN ? &ntree->inputs : &ntree->outputs;
bNodeSocket *iosock = ntree_get_active_interface_socket(sockets);
if (iosock == nullptr) {
return OPERATOR_CANCELLED;
}
switch (direction) {
case 1: { /* up */
bNodeSocket *before = iosock->prev;
BLI_remlink(sockets, iosock);
if (before) {
BLI_insertlinkbefore(sockets, before, iosock);
}
else {
BLI_addhead(sockets, iosock);
}
break;
}
case 2: { /* down */
bNodeSocket *after = iosock->next;
BLI_remlink(sockets, iosock);
if (after) {
BLI_insertlinkafter(sockets, after, iosock);
}
else {
BLI_addtail(sockets, iosock);
}
break;
}
}
BKE_ntree_update_tag_interface(ntree);
ED_node_tree_propagate_change(C, CTX_data_main(C), ntree);
WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr);
return OPERATOR_FINISHED;
}
void NODE_OT_tree_socket_move(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Move Node Tree Socket";
ot->description = "Move a socket up or down in the active node tree's interface";
ot->idname = "NODE_OT_tree_socket_move";
/* api callbacks */
ot->exec = ntree_socket_move_exec;
ot->poll = ED_operator_node_editable;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
RNA_def_enum(ot->srna, "direction", move_direction_items, 1, "Direction", "");
RNA_def_enum(ot->srna, "in_out", rna_enum_node_socket_in_out_items, SOCK_IN, "Socket Type", "");
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Node Shader Script Update
* \{ */
+50 -38
View File
@@ -907,9 +907,9 @@ static bool prefer_node_for_interface_name(const bNode &node)
return node.is_group() || node.is_group_input() || node.is_group_output();
}
static bNodeSocket *add_interface_from_socket(const bNodeTree &original_tree,
bNodeTree &tree_for_interface,
const bNodeSocket &socket)
static bNodeTreeInterfaceSocket *add_interface_from_socket(const bNodeTree &original_tree,
bNodeTree &tree_for_interface,
const bNodeSocket &socket)
{
/* The "example socket" has to have the same `in_out` status as the new interface socket. */
const bNodeSocket &socket_for_io = find_socket_to_use_for_interface(original_tree, socket);
@@ -917,11 +917,8 @@ static bNodeSocket *add_interface_from_socket(const bNodeTree &original_tree,
const bNodeSocket &socket_for_name = prefer_node_for_interface_name(socket.owner_node()) ?
socket :
socket_for_io;
return bke::ntreeAddSocketInterfaceFromSocketWithName(&tree_for_interface,
&node_for_io,
&socket_for_io,
socket_for_io.idname,
socket_for_name.name);
return bke::node_interface::add_interface_socket_from_node(
tree_for_interface, node_for_io, socket_for_io, socket_for_io.idname, socket_for_name.name);
}
static void update_nested_node_refs_after_moving_nodes_into_group(
@@ -1011,12 +1008,12 @@ static void node_group_make_insert_selected(const bContext &C,
bNode *from_node;
/* All the links that came from the socket on the unselected node. */
Vector<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. */
@@ -1029,6 +1026,30 @@ static void node_group_make_insert_selected(const bContext &C,
ntree.ensure_topology_cache();
for (bNode *node : nodes_to_move) {
for (bNodeSocket *output_socket : node->output_sockets()) {
for (bNodeLink *link : output_socket->directly_linked_links()) {
if (nodeLinkIsHidden(link)) {
links_to_remove.add(link);
continue;
}
if (link->tonode == gnode) {
links_to_remove.add(link);
continue;
}
if (nodes_to_move.contains(link->tonode)) {
internal_links_to_move.add(link);
continue;
}
bNodeTreeInterfaceSocket *io_socket = add_interface_from_socket(
ntree, group, *link->fromsock);
if (io_socket) {
output_links.append({link, io_socket});
}
else {
links_to_remove.add(link);
}
}
}
for (bNodeSocket *input_socket : node->input_sockets()) {
for (bNodeLink *link : input_socket->directly_linked_links()) {
if (nodeLinkIsHidden(link)) {
@@ -1049,23 +1070,9 @@ static void node_group_make_insert_selected(const bContext &C,
if (!info.interface_socket) {
info.interface_socket = add_interface_from_socket(ntree, group, *link->tosock);
}
}
}
for (bNodeSocket *output_socket : node->output_sockets()) {
for (bNodeLink *link : output_socket->directly_linked_links()) {
if (nodeLinkIsHidden(link)) {
else {
links_to_remove.add(link);
continue;
}
if (link->tonode == gnode) {
links_to_remove.add(link);
continue;
}
if (nodes_to_move.contains(link->tonode)) {
internal_links_to_move.add(link);
continue;
}
output_links.append({link, add_interface_from_socket(ntree, group, *link->fromsock)});
}
}
}
@@ -1073,7 +1080,7 @@ static void node_group_make_insert_selected(const bContext &C,
struct NewInternalLinkInfo {
bNode *node;
bNodeSocket *socket;
const bNodeSocket *interface_socket;
const bNodeTreeInterfaceSocket *interface_socket;
};
const bool expose_visible = nodes_to_move.size() == 1;
@@ -1088,9 +1095,11 @@ static void node_group_make_insert_selected(const bContext &C,
if (socket->is_directly_linked()) {
continue;
}
const bNodeSocket *io_socket = bke::ntreeAddSocketInterfaceFromSocket(
&group, node, socket);
new_internal_links.append({node, socket, io_socket});
const bNodeTreeInterfaceSocket *io_socket =
bke::node_interface::add_interface_socket_from_node(group, *node, *socket);
if (io_socket) {
new_internal_links.append({node, socket, io_socket});
}
}
};
expose_sockets(node->input_sockets());
@@ -1168,8 +1177,9 @@ static void node_group_make_insert_selected(const bContext &C,
/* Handle links to the new group inputs. */
for (const auto item : input_links.items()) {
const char *interface_identifier = item.value.interface_socket->identifier;
bNodeSocket *input_socket = node_group_input_find_socket(input_node, interface_identifier);
const StringRefNull interface_identifier = item.value.interface_socket->identifier;
bNodeSocket *input_socket = node_group_input_find_socket(input_node,
interface_identifier.c_str());
for (bNodeLink *link : item.value.links) {
/* Move the link into the new group, connected from the input node to the original socket. */
@@ -1185,20 +1195,21 @@ static void node_group_make_insert_selected(const bContext &C,
/* Handle links to new group outputs. */
for (const OutputLinkInfo &info : output_links) {
/* Create a new link inside of the group. */
const char *io_identifier = info.interface_socket->identifier;
bNodeSocket *output_sock = node_group_output_find_socket(output_node, io_identifier);
const StringRefNull io_identifier = info.interface_socket->identifier;
bNodeSocket *output_sock = node_group_output_find_socket(output_node, io_identifier.c_str());
nodeAddLink(&group, info.link->fromnode, info.link->fromsock, output_node, output_sock);
}
/* Handle new links inside the group. */
for (const NewInternalLinkInfo &info : new_internal_links) {
const char *io_identifier = info.interface_socket->identifier;
const StringRefNull io_identifier = info.interface_socket->identifier;
if (info.socket->in_out == SOCK_IN) {
bNodeSocket *input_socket = node_group_input_find_socket(input_node, io_identifier);
bNodeSocket *input_socket = node_group_input_find_socket(input_node, io_identifier.c_str());
nodeAddLink(&group, input_node, input_socket, info.node, info.socket);
}
else {
bNodeSocket *output_socket = node_group_output_find_socket(output_node, io_identifier);
bNodeSocket *output_socket = node_group_output_find_socket(output_node,
io_identifier.c_str());
nodeAddLink(&group, info.node, info.socket, output_node, output_socket);
}
}
@@ -1212,8 +1223,9 @@ static void node_group_make_insert_selected(const bContext &C,
/* Add new links to inputs outside of the group. */
for (const auto item : input_links.items()) {
const char *interface_identifier = item.value.interface_socket->identifier;
bNodeSocket *group_node_socket = node_group_find_input_socket(gnode, interface_identifier);
const StringRefNull interface_identifier = item.value.interface_socket->identifier;
bNodeSocket *group_node_socket = node_group_find_input_socket(gnode,
interface_identifier.c_str());
nodeAddLink(&ntree, item.value.from_node, item.key, gnode, group_node_socket);
}
@@ -162,11 +162,7 @@ int node_get_resize_cursor(NodeResizeDirection directions);
* Usual convention here would be #node_socket_get_color(),
* but that's already used (for setting a color property socket).
*/
void node_socket_color_get(const bContext &C,
const bNodeTree &ntree,
PointerRNA &node_ptr,
const bNodeSocket &sock,
float r_color[4]);
void node_socket_color_get(const bNodeSocketType &type, float r_color[4]);
/* `node_draw.cc` */
@@ -244,20 +240,15 @@ void nodelink_batch_end(SpaceNode &snode);
/**
* \note this is used for fake links in groups too.
*/
void node_draw_link(const bContext &C,
const View2D &v2d,
void node_draw_link(const View2D &v2d,
const SpaceNode &snode,
const bNodeLink &link,
bool selected);
void node_draw_link_dragged(const bContext &C,
const View2D &v2d,
const SpaceNode &snode,
const bNodeLink &link);
void node_draw_link_dragged(const View2D &v2d, const SpaceNode &snode, const bNodeLink &link);
/**
* Don't do shadows if th_col3 is -1.
*/
void node_draw_link_bezier(const bContext &C,
const View2D &v2d,
void node_draw_link_bezier(const View2D &v2d,
const SpaceNode &snode,
const bNodeLink &link,
int th_col1,
@@ -379,12 +370,6 @@ void NODE_OT_switch_view_update(wmOperatorType *ot);
void NODE_OT_clipboard_copy(wmOperatorType *ot);
void NODE_OT_clipboard_paste(wmOperatorType *ot);
void NODE_OT_tree_socket_add(wmOperatorType *ot);
void NODE_OT_tree_socket_remove(wmOperatorType *ot);
void NODE_OT_tree_socket_change_type(wmOperatorType *ot);
void NODE_OT_tree_socket_change_subtype(wmOperatorType *ot);
void NODE_OT_tree_socket_move(wmOperatorType *ot);
void NODE_OT_shader_script_update(wmOperatorType *ot);
void NODE_OT_viewer_border(wmOperatorType *ot);
@@ -107,12 +107,6 @@ void node_operatortypes()
WM_operatortype_append(NODE_OT_switch_view_update);
WM_operatortype_append(NODE_OT_tree_socket_add);
WM_operatortype_append(NODE_OT_tree_socket_remove);
WM_operatortype_append(NODE_OT_tree_socket_change_type);
WM_operatortype_append(NODE_OT_tree_socket_change_subtype);
WM_operatortype_append(NODE_OT_tree_socket_move);
WM_operatortype_append(NODE_OT_cryptomatte_layer_add);
WM_operatortype_append(NODE_OT_cryptomatte_layer_remove);
}
@@ -2259,8 +2259,8 @@ bNodeSocket *get_main_socket(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_
bke::nodeDeclarationEnsure(&ntree, &node);
const nodes::NodeDeclaration *node_decl = node.declaration();
if (node_decl != nullptr) {
Span<nodes::SocketDeclarationPtr> socket_decls = (in_out == SOCK_IN) ? node_decl->inputs :
node_decl->outputs;
Span<nodes::SocketDeclaration *> socket_decls = (in_out == SOCK_IN) ? node_decl->inputs :
node_decl->outputs;
int index;
LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, sockets, index) {
const nodes::SocketDeclaration &socket_decl = *socket_decls[index];
@@ -376,7 +376,8 @@ static void connect_nested_node_to_node(const Span<bNodeTreePath *> treepath,
output_node->flag |= NODE_DO_OUTPUT;
}
ntreeAddSocketInterface(nested_nt, SOCK_OUT, nested_socket_iter->idname, route_name);
nested_nt->tree_interface.add_socket(
route_name, "", nested_socket_iter->idname, NODE_INTERFACE_SOCKET_OUTPUT, nullptr);
BKE_ntree_update_main_tree(G.pr_main, nested_nt, nullptr);
bNodeSocket *out_socket = blender::bke::node_find_enabled_input_socket(*output_node,
route_name);
@@ -26,6 +26,8 @@
#include "BKE_context.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
#include "BKE_node_runtime.hh"
#include "BKE_node_tree_interface.hh"
#include "BKE_node_tree_update.h"
#include "RNA_access.hh"
@@ -315,7 +317,6 @@ static Vector<NodeLinkItem> ui_node_link_items(NodeLinkArg *arg,
/* XXX this should become a callback for node types! */
if (arg->node_type->type == NODE_GROUP) {
bNodeTree *ngroup;
int i;
for (ngroup = (bNodeTree *)arg->bmain->nodetrees.first; ngroup;
ngroup = (bNodeTree *)ngroup->id.next)
@@ -327,7 +328,6 @@ static Vector<NodeLinkItem> ui_node_link_items(NodeLinkArg *arg,
}
}
i = 0;
for (ngroup = (bNodeTree *)arg->bmain->nodetrees.first; ngroup;
ngroup = (bNodeTree *)ngroup->id.next)
{
@@ -337,17 +337,19 @@ static Vector<NodeLinkItem> ui_node_link_items(NodeLinkArg *arg,
continue;
}
ListBase *lb = (in_out == SOCK_IN ? &ngroup->inputs : &ngroup->outputs);
bNodeSocket *stemp;
int index;
for (stemp = (bNodeSocket *)lb->first, index = 0; stemp; stemp = stemp->next, index++, i++) {
Span<bNodeTreeInterfaceSocket *> iosockets = (in_out == SOCK_IN ?
ngroup->interface_inputs() :
ngroup->interface_outputs());
for (const int index : iosockets.index_range()) {
bNodeTreeInterfaceSocket *iosock = iosockets[index];
NodeLinkItem item;
item.socket_index = index;
/* NOTE: int stemp->type is not fully reliable, not used for node group
* interface sockets. use the typeinfo->type instead.
*/
item.socket_type = stemp->typeinfo->type;
item.socket_name = stemp->name;
const bNodeSocketType *typeinfo = iosock->socket_typeinfo();
item.socket_type = typeinfo->type;
item.socket_name = iosock->name;
item.node_name = ngroup->id.name + 2;
item.ngroup = ngroup;
@@ -361,10 +363,10 @@ static Vector<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 :
r_node_decl->outputs;
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++;
@@ -709,7 +711,7 @@ static void ui_template_node_link_menu(bContext *C, uiLayout *layout, void *but_
} // namespace blender::ed::space_node
void uiTemplateNodeLink(
uiLayout *layout, bContext *C, bNodeTree *ntree, bNode *node, bNodeSocket *input)
uiLayout *layout, bContext * /*C*/, bNodeTree *ntree, bNode *node, bNodeSocket *input)
{
using namespace blender::ed::space_node;
@@ -723,9 +725,7 @@ void uiTemplateNodeLink(
arg->node = node;
arg->sock = input;
PointerRNA node_ptr;
RNA_pointer_create((ID *)ntree, &RNA_Node, node, &node_ptr);
node_socket_color_get(*C, *ntree, node_ptr, *input, socket_col);
node_socket_color_get(*input->typeinfo, socket_col);
UI_block_layout_set_current(block, layout);
@@ -52,13 +52,13 @@ typedef struct bNodeTreeInterfaceItem {
} bNodeTreeInterfaceItem;
/* Socket interface flags */
typedef enum eNodeTreeInterfaceSocketFlag {
typedef enum NodeTreeInterfaceSocketFlag {
NODE_INTERFACE_SOCKET_INPUT = 1 << 0,
NODE_INTERFACE_SOCKET_OUTPUT = 1 << 1,
NODE_INTERFACE_SOCKET_HIDE_VALUE = 1 << 2,
NODE_INTERFACE_SOCKET_HIDE_IN_MODIFIER = 1 << 3,
} eNodeTreeInterfaceSocketFlag;
ENUM_OPERATORS(eNodeTreeInterfaceSocketFlag, NODE_INTERFACE_SOCKET_HIDE_IN_MODIFIER);
} NodeTreeInterfaceSocketFlag;
ENUM_OPERATORS(NodeTreeInterfaceSocketFlag, NODE_INTERFACE_SOCKET_HIDE_IN_MODIFIER);
typedef struct bNodeTreeInterfaceSocket {
bNodeTreeInterfaceItem item;
@@ -68,7 +68,7 @@ typedef struct bNodeTreeInterfaceSocket {
char *description;
/* Type idname of the socket to generate, e.g. "NodeSocketFloat". */
char *socket_type;
/* eNodeTreeInterfaceSocketFlag */
/* NodeTreeInterfaceSocketFlag */
int flag;
/* eAttrDomain */
@@ -100,11 +100,26 @@ typedef struct bNodeTreeInterfaceSocket {
#endif
} bNodeTreeInterfaceSocket;
/* Panel interface flags */
typedef enum NodeTreeInterfacePanelFlag {
/* Panel starts closed on new node instances. */
NODE_INTERFACE_PANEL_DEFAULT_CLOSED = 1 << 0,
/* Allow child panels inside this panel. */
NODE_INTERFACE_PANEL_ALLOW_CHILD_PANELS = 1 << 1,
/* Allow adding sockets after panels. */
NODE_INTERFACE_PANEL_ALLOW_SOCKETS_AFTER_PANELS = 1 << 2,
} NodeTreeInterfacePanelFlag;
ENUM_OPERATORS(NodeTreeInterfacePanelFlag, NODE_INTERFACE_PANEL_DEFAULT_CLOSED);
typedef struct bNodeTreeInterfacePanel {
bNodeTreeInterfaceItem item;
/* UI name of the panel. */
char *name;
char *description;
/* NodeTreeInterfacePanelFlag */
int flag;
char _pad[4];
bNodeTreeInterfaceItem **items_array;
int items_num;
@@ -186,6 +201,11 @@ typedef struct bNodeTreeInterfacePanel {
/** Same as above but for a const interface. */
void foreach_item(blender::FunctionRef<bool(const bNodeTreeInterfaceItem &item)> fn,
bool include_self = false) const;
private:
/** Find a valid position for inserting in the items span. */
int find_valid_insert_position_for_item(const bNodeTreeInterfaceItem &item,
int initial_position) const;
#endif
} bNodeTreeInterfacePanel;
@@ -198,6 +218,8 @@ typedef struct bNodeTreeInterface {
#ifdef __cplusplus
/** Initialize data of new interface instance. */
void init_data();
/** Copy data from another interface.
* \param flag: ID creation/copying flags, e.g. LIB_ID_CREATE_NO_MAIN.
*/
@@ -216,7 +238,19 @@ typedef struct bNodeTreeInterface {
void active_item_set(bNodeTreeInterfaceItem *item);
/**
* Get the index of the item in the interface.
* Get the position of the item in its parent panel.
* \return Position if the item was found or -1 otherwise.
*/
int find_item_position(const bNodeTreeInterfaceItem &item) const
{
/* const_cast to avoid a const version of #find_parent_recursive. */
const bNodeTreeInterfacePanel *parent =
const_cast<bNodeTreeInterfacePanel &>(root_panel).find_parent_recursive(item);
BLI_assert(parent != nullptr);
return parent->item_position(item);
}
/**
* Get the global index of the item in the interface.
* \return Index if the item was found or -1 otherwise.
*/
int find_item_index(const bNodeTreeInterfaceItem &item) const
@@ -255,7 +289,7 @@ typedef struct bNodeTreeInterface {
bNodeTreeInterfaceSocket *add_socket(blender::StringRefNull name,
blender::StringRefNull description,
blender::StringRefNull socket_type,
eNodeTreeInterfaceSocketFlag flag,
NodeTreeInterfaceSocketFlag flag,
bNodeTreeInterfacePanel *parent);
/**
* Insert a new socket.
@@ -266,7 +300,7 @@ typedef struct bNodeTreeInterface {
bNodeTreeInterfaceSocket *insert_socket(blender::StringRefNull name,
blender::StringRefNull description,
blender::StringRefNull socket_type,
eNodeTreeInterfaceSocketFlag flag,
NodeTreeInterfaceSocketFlag flag,
bNodeTreeInterfacePanel *parent,
int position);
@@ -275,7 +309,10 @@ typedef struct bNodeTreeInterface {
* \param parent: Panel in which the new panel is added as a child. If parent is null the new
* panel is made a child of the root panel.
*/
bNodeTreeInterfacePanel *add_panel(blender::StringRefNull name, bNodeTreeInterfacePanel *parent);
bNodeTreeInterfacePanel *add_panel(blender::StringRefNull name,
blender::StringRefNull description,
NodeTreeInterfacePanelFlag flag,
bNodeTreeInterfacePanel *parent);
/**
* Insert a new panel.
* \param parent: Panel in which the new panel is added as a child. If parent is null the new
@@ -283,6 +320,8 @@ typedef struct bNodeTreeInterface {
* \param position: Position of the child panel within the parent panel.
*/
bNodeTreeInterfacePanel *insert_panel(blender::StringRefNull name,
blender::StringRefNull description,
NodeTreeInterfacePanelFlag flag,
bNodeTreeInterfacePanel *parent,
int position);
+35 -5
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. */
int identifier;
/* eNodePanelFlag */
char flag;
char _pad[3];
#ifdef __cplusplus
bool is_collapsed() const;
bool is_parent_collapsed() const;
#endif
} bNodePanelState;
typedef struct bNode {
struct bNode *next, *prev;
@@ -383,7 +407,9 @@ typedef struct bNode {
/** Custom user-defined color. */
float color[3];
char _pad2[4];
/** Panel states for this node instance. */
int num_panel_states;
bNodePanelState *panel_states_array;
bNodeRuntimeHandle *runtime;
@@ -423,6 +449,8 @@ typedef struct bNode {
bNodeSocket &output_by_identifier(blender::StringRef identifier);
/** If node is frame, will return all children nodes. */
blender::Span<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,14 @@ 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;
/* Cached interface item lists. */
blender::Span<bNodeTreeInterfaceSocket *> interface_inputs() const;
blender::Span<bNodeTreeInterfaceSocket *> interface_outputs() const;
blender::Span<bNodeTreeInterfaceItem *> interface_items() const;
#endif
} bNodeTree;
@@ -183,6 +183,8 @@ DNA_STRUCT_RENAME_ELEM(bPoseChannel, scaleIn, scale_in_x)
DNA_STRUCT_RENAME_ELEM(bPoseChannel, scaleOut, scale_out_x)
DNA_STRUCT_RENAME_ELEM(bPoseChannel, scale_in_y, scale_in_z)
DNA_STRUCT_RENAME_ELEM(bPoseChannel, scale_out_y, scale_out_z)
DNA_STRUCT_RENAME_ELEM(bNodeTree, inputs, inputs_legacy)
DNA_STRUCT_RENAME_ELEM(bNodeTree, outputs, outputs_legacy)
DNA_STRUCT_RENAME_ELEM(bSameVolumeConstraint, flag, free_axis)
DNA_STRUCT_RENAME_ELEM(bSound, name, filepath)
DNA_STRUCT_RENAME_ELEM(bTheme, tact, space_action)
File diff suppressed because it is too large Load Diff
@@ -21,12 +21,23 @@ const EnumPropertyItem rna_enum_node_tree_interface_item_type_items[] = {
{NODE_INTERFACE_PANEL, "PANEL", 0, "Panel", ""},
{0, nullptr, 0, nullptr, nullptr}};
static const EnumPropertyItem node_tree_interface_socket_in_out_items[] = {
{NODE_INTERFACE_SOCKET_INPUT, "INPUT", 0, "Input", "Generate a input node socket"},
{NODE_INTERFACE_SOCKET_OUTPUT, "OUTPUT", 0, "Output", "Generate a output node socket"},
{NODE_INTERFACE_SOCKET_INPUT | NODE_INTERFACE_SOCKET_OUTPUT,
"BOTH",
0,
"Both",
"Generate both input and output node socket"},
{0, nullptr, 0, nullptr, nullptr}};
#ifdef RNA_RUNTIME
# include "BKE_node.h"
# include "BKE_node_runtime.hh"
# include "BKE_node_tree_interface.hh"
# include "BKE_node_tree_update.h"
# include "BLI_set.hh"
# include "DNA_material_types.h"
# include "ED_node.hh"
# include "WM_api.hh"
@@ -75,17 +86,38 @@ static char *rna_NodeTreeInterfaceItem_path(const PointerRNA *ptr)
}
ntree->ensure_topology_cache();
UNUSED_VARS(item);
// Note: New API, will be enabled after new interface cache is added.
// const blender::bke::bNodeTreeInterfaceCache &cache = ntree->interface_cache();
// for (const int index : cache.items.index_range()) {
// if (cache.items[index] == item) {
// return BLI_sprintfN("interface.ui_items[%d]", index);
// }
// }
for (const int index : ntree->interface_items().index_range()) {
if (ntree->interface_items()[index] == item) {
return BLI_sprintfN("interface.ui_items[%d]", index);
}
}
return nullptr;
}
static PointerRNA rna_NodeTreeInterfaceItem_parent_get(PointerRNA *ptr)
{
bNodeTree *ntree = reinterpret_cast<bNodeTree *>(ptr->owner_id);
const bNodeTreeInterfaceItem *item = static_cast<const bNodeTreeInterfaceItem *>(ptr->data);
bNodeTreeInterfacePanel *parent = ntree->tree_interface.find_item_parent(*item);
PointerRNA result;
RNA_pointer_create(&ntree->id, &RNA_NodeTreeInterfacePanel, parent, &result);
return result;
}
static int rna_NodeTreeInterfaceItem_position_get(PointerRNA *ptr)
{
bNodeTree *ntree = reinterpret_cast<bNodeTree *>(ptr->owner_id);
const bNodeTreeInterfaceItem *item = static_cast<const bNodeTreeInterfaceItem *>(ptr->data);
return ntree->tree_interface.find_item_position(*item);
}
static int rna_NodeTreeInterfaceItem_index_get(PointerRNA *ptr)
{
bNodeTree *ntree = reinterpret_cast<bNodeTree *>(ptr->owner_id);
const bNodeTreeInterfaceItem *item = static_cast<const bNodeTreeInterfaceItem *>(ptr->data);
return ntree->tree_interface.find_item_index(*item);
}
static bool rna_NodeTreeInterfaceSocket_unregister(Main * /*bmain*/, StructRNA *type)
{
bNodeSocketType *st = static_cast<bNodeSocketType *>(RNA_struct_blender_type_get(type));
@@ -109,15 +141,14 @@ static void rna_NodeTreeInterfaceSocket_draw_builtin(ID *id,
{
bNodeSocketType *typeinfo = interface_socket->socket_typeinfo();
if (typeinfo && typeinfo->interface_draw) {
UNUSED_VARS(id, C, layout);
// Note: New API, will be enabled after typeinfo callbacks change.
// typeinfo->interface_draw(id, interface_socket, C, layout);
typeinfo->interface_draw(id, interface_socket, C, layout);
}
}
// Note: New API, interface draw callback used after changing callbacks.
static void UNUSED_FUNCTION(rna_NodeTreeInterfaceSocket_draw_custom)(
ID *id, bNodeTreeInterfaceSocket *interface_socket, bContext *C, uiLayout *layout)
static void rna_NodeTreeInterfaceSocket_draw_custom(ID *id,
bNodeTreeInterfaceSocket *interface_socket,
bContext *C,
uiLayout *layout)
{
bNodeSocketType *typeinfo = nodeSocketTypeFind(interface_socket->socket_type);
if (typeinfo == nullptr) {
@@ -147,14 +178,11 @@ static void rna_NodeTreeInterfaceSocket_init_socket_builtin(
{
bNodeSocketType *typeinfo = interface_socket->socket_typeinfo();
if (typeinfo && typeinfo->interface_draw) {
// Note: New API, callback signatures change.
UNUSED_VARS(id, node, socket, data_path);
// typeinfo->interface_init_socket(id, interface_socket, node, socket, data_path);
typeinfo->interface_init_socket(id, interface_socket, node, socket, data_path);
}
}
// Note: New API, used when callbacks change.
static void UNUSED_FUNCTION(rna_NodeTreeInterfaceSocket_init_socket_custom)(
static void rna_NodeTreeInterfaceSocket_init_socket_custom(
ID *id,
const bNodeTreeInterfaceSocket *interface_socket,
bNode *node,
@@ -191,14 +219,11 @@ static void rna_NodeTreeInterfaceSocket_from_socket_builtin(
{
bNodeSocketType *typeinfo = interface_socket->socket_typeinfo();
if (typeinfo && typeinfo->interface_draw) {
// Note: New API, callback signatures change.
UNUSED_VARS(id, node, socket);
// typeinfo->interface_from_socket(id, interface_socket, node, socket);
typeinfo->interface_from_socket(id, interface_socket, node, socket);
}
}
// Note: New API, used after callback signatures change.
static void UNUSED_FUNCTION(rna_NodeTreeInterfaceSocket_from_socket_custom)(
static void rna_NodeTreeInterfaceSocket_from_socket_custom(
ID *id,
bNodeTreeInterfaceSocket *interface_socket,
const bNode *node,
@@ -275,14 +300,11 @@ static StructRNA *rna_NodeTreeInterfaceSocket_register(Main * /*bmain*/,
st->ext_interface.free = free;
RNA_struct_blender_type_set(st->ext_interface.srna, st);
// Note: New API callbacks.
// st->interface_draw = (have_function[0]) ? rna_NodeTreeInterfaceSocket_draw_custom : nullptr;
// st->interface_init_socket = (have_function[1]) ?
// rna_NodeTreeInterfaceSocket_init_socket_custom :
// nullptr;
// st->interface_from_socket = (have_function[2]) ?
// rna_NodeTreeInterfaceSocket_from_socket_custom :
// nullptr;
st->interface_draw = (have_function[0]) ? rna_NodeTreeInterfaceSocket_draw_custom : nullptr;
st->interface_init_socket = (have_function[1]) ? rna_NodeTreeInterfaceSocket_init_socket_custom :
nullptr;
st->interface_from_socket = (have_function[2]) ? rna_NodeTreeInterfaceSocket_from_socket_custom :
nullptr;
/* Cleanup local dummy type. */
MEM_SAFE_FREE(dummy_socket.socket_type);
@@ -397,8 +419,7 @@ static bNodeTreeInterfaceSocket *rna_NodeTreeInterfaceItems_new_socket(
ReportList *reports,
const char *name,
const char *description,
bool is_input,
bool is_output,
int in_out,
int socket_type_enum,
bNodeTreeInterfacePanel *parent)
{
@@ -422,11 +443,7 @@ static bNodeTreeInterfaceSocket *rna_NodeTreeInterfaceItems_new_socket(
}
}
const char *socket_type = typeinfo->idname;
eNodeTreeInterfaceSocketFlag flag = eNodeTreeInterfaceSocketFlag(0);
SET_FLAG_FROM_TEST(flag, is_input, NODE_INTERFACE_SOCKET_INPUT);
SET_FLAG_FROM_TEST(flag, is_output, NODE_INTERFACE_SOCKET_OUTPUT);
NodeTreeInterfaceSocketFlag flag = NodeTreeInterfaceSocketFlag(in_out);
bNodeTreeInterfaceSocket *socket = interface->add_socket(name ? name : "",
description ? description : "",
socket_type ? socket_type : "",
@@ -451,14 +468,26 @@ static bNodeTreeInterfacePanel *rna_NodeTreeInterfaceItems_new_panel(
Main *bmain,
ReportList *reports,
const char *name,
const char *description,
bool default_closed,
bNodeTreeInterfacePanel *parent)
{
if (parent != nullptr && !interface->find_item(parent->item)) {
BKE_report(reports, RPT_ERROR_INVALID_INPUT, "Parent is not part of the interface");
return nullptr;
if (parent != nullptr) {
if (!interface->find_item(parent->item)) {
BKE_report(reports, RPT_ERROR_INVALID_INPUT, "Parent is not part of the interface");
return nullptr;
}
if (!(parent->flag & NODE_INTERFACE_PANEL_ALLOW_CHILD_PANELS)) {
BKE_report(reports, RPT_WARNING, "Parent panel does not allow child panels");
return nullptr;
}
}
bNodeTreeInterfacePanel *panel = interface->add_panel(name ? name : "", parent);
NodeTreeInterfacePanelFlag flag = NodeTreeInterfacePanelFlag(0);
SET_FLAG_FROM_TEST(flag, default_closed, NODE_INTERFACE_PANEL_DEFAULT_CLOSED);
bNodeTreeInterfacePanel *panel = interface->add_panel(
name ? name : "", description ? description : "", flag, parent);
if (panel == nullptr) {
BKE_report(reports, RPT_ERROR, "Unable to create panel");
@@ -481,9 +510,17 @@ static bNodeTreeInterfaceItem *rna_NodeTreeInterfaceItems_copy_to_parent(
bNodeTreeInterfaceItem *item,
bNodeTreeInterfacePanel *parent)
{
if (parent != nullptr && !interface->find_item(parent->item)) {
BKE_report(reports, RPT_ERROR_INVALID_INPUT, "Parent is not part of the interface");
return nullptr;
if (parent != nullptr) {
if (!interface->find_item(parent->item)) {
BKE_report(reports, RPT_ERROR_INVALID_INPUT, "Parent is not part of the interface");
return nullptr;
}
if (item->item_type == NODE_INTERFACE_PANEL &&
!(parent->flag & NODE_INTERFACE_PANEL_ALLOW_CHILD_PANELS))
{
BKE_report(reports, RPT_WARNING, "Parent panel does not allow child panels");
return nullptr;
}
}
if (parent == nullptr) {
@@ -547,10 +584,13 @@ static void rna_NodeTreeInterfaceItems_clear(ID *id, bNodeTreeInterface *interfa
WM_main_add_notifier(NC_NODE | NA_EDITED, ntree);
}
static void rna_NodeTreeInterfaceItems_move(
ID *id, bNodeTreeInterface *interface, Main *bmain, bNodeTreeInterfaceItem *item, int to_index)
static void rna_NodeTreeInterfaceItems_move(ID *id,
bNodeTreeInterface *interface,
Main *bmain,
bNodeTreeInterfaceItem *item,
int to_position)
{
interface->move_item(*item, to_index);
interface->move_item(*item, to_position);
bNodeTree *ntree = reinterpret_cast<bNodeTree *>(id);
BKE_ntree_update_tag_interface(ntree);
@@ -561,11 +601,19 @@ static void rna_NodeTreeInterfaceItems_move(
static void rna_NodeTreeInterfaceItems_move_to_parent(ID *id,
bNodeTreeInterface *interface,
Main *bmain,
ReportList *reports,
bNodeTreeInterfaceItem *item,
bNodeTreeInterfacePanel *parent,
int to_index)
int to_position)
{
interface->move_item_to_parent(*item, parent, to_index);
if (item->item_type == NODE_INTERFACE_PANEL &&
!(parent->flag & NODE_INTERFACE_PANEL_ALLOW_CHILD_PANELS))
{
BKE_report(reports, RPT_WARNING, "Parent panel does not allow child panels");
return;
}
interface->move_item_to_parent(*item, parent, to_position);
bNodeTree *ntree = reinterpret_cast<bNodeTree *>(id);
BKE_ntree_update_tag_interface(ntree);
@@ -575,6 +623,44 @@ static void rna_NodeTreeInterfaceItems_move_to_parent(ID *id,
/* ******** Node Socket Subtypes ******** */
static const EnumPropertyItem *rna_subtype_filter_itemf(const blender::Set<int> &subtypes,
bool *r_free)
{
if (subtypes.is_empty()) {
return rna_enum_dummy_NULL_items;
}
EnumPropertyItem *items = nullptr;
int items_count = 0;
for (const EnumPropertyItem *item = rna_enum_property_subtype_items; item->name != nullptr;
item++) {
if (subtypes.contains(item->value)) {
RNA_enum_item_add(&items, &items_count, item);
}
}
if (items_count == 0) {
return rna_enum_dummy_NULL_items;
}
RNA_enum_item_end(&items, &items_count);
*r_free = true;
return items;
}
static const EnumPropertyItem *rna_NodeTreeInterfaceSocketFloat_subtype_itemf(
bContext * /*C*/, PointerRNA * /*ptr*/, PropertyRNA * /*prop*/, bool *r_free)
{
return rna_subtype_filter_itemf({PROP_PERCENTAGE,
PROP_FACTOR,
PROP_ANGLE,
PROP_TIME,
PROP_TIME_ABSOLUTE,
PROP_DISTANCE,
PROP_NONE},
r_free);
}
void rna_NodeTreeInterfaceSocketFloat_default_value_range(
PointerRNA *ptr, float *min, float *max, float *softmin, float *softmax)
{
@@ -593,6 +679,14 @@ void rna_NodeTreeInterfaceSocketFloat_default_value_range(
*softmax = dval->max;
}
static const EnumPropertyItem *rna_NodeTreeInterfaceSocketInt_subtype_itemf(bContext * /*C*/,
PointerRNA * /*ptr*/,
PropertyRNA * /*prop*/,
bool *r_free)
{
return rna_subtype_filter_itemf({PROP_PERCENTAGE, PROP_FACTOR, PROP_NONE}, r_free);
}
void rna_NodeTreeInterfaceSocketInt_default_value_range(
PointerRNA *ptr, int *min, int *max, int *softmin, int *softmax)
{
@@ -611,6 +705,19 @@ void rna_NodeTreeInterfaceSocketInt_default_value_range(
*softmax = dval->max;
}
static const EnumPropertyItem *rna_NodeTreeInterfaceSocketVector_subtype_itemf(
bContext * /*C*/, PointerRNA * /*ptr*/, PropertyRNA * /*prop*/, bool *r_free)
{
return rna_subtype_filter_itemf({PROP_TRANSLATION,
PROP_DIRECTION,
PROP_VELOCITY,
PROP_ACCELERATION,
PROP_EULER,
PROP_XYZ,
PROP_NONE},
r_free);
}
void rna_NodeTreeInterfaceSocketVector_default_value_range(
PointerRNA *ptr, float *min, float *max, float *softmin, float *softmax)
{
@@ -650,15 +757,12 @@ static void rna_NodeTreeInterface_items_begin(CollectionPropertyIterator *iter,
}
ntree->ensure_topology_cache();
// Note: New API, enabled when interface cache is added.
UNUSED_VARS(iter);
// const blender::bke::bNodeTreeInterfaceCache &cache = ntree->interface_cache();
// rna_iterator_array_begin(iter,
// const_cast<bNodeTreeInterfaceItem **>(cache.items.data()),
// sizeof(bNodeTreeInterfaceItem *),
// cache.items.size(),
// false,
// nullptr);
rna_iterator_array_begin(iter,
const_cast<bNodeTreeInterfaceItem **>(ntree->interface_items().data()),
sizeof(bNodeTreeInterfaceItem *),
ntree->interface_items().size(),
false,
nullptr);
}
static int rna_NodeTreeInterface_items_length(PointerRNA *ptr)
@@ -669,9 +773,7 @@ static int rna_NodeTreeInterface_items_length(PointerRNA *ptr)
}
ntree->ensure_topology_cache();
// Note: New API callbacks.
// return ntree->interface_cache().items.size();
return 0;
return ntree->interface_items().size();
}
static int rna_NodeTreeInterface_items_lookup_int(PointerRNA *ptr, int index, PointerRNA *r_ptr)
@@ -682,14 +784,12 @@ static int rna_NodeTreeInterface_items_lookup_int(PointerRNA *ptr, int index, Po
}
ntree->ensure_topology_cache();
// Note: New API, enabled when interface cache is added.
UNUSED_VARS(index, r_ptr);
// const blender::bke::bNodeTreeInterfaceCache &cache = ntree->interface_cache();
// if (!cache.items.index_range().contains(index)) {
// return false;
// }
if (!ntree->interface_items().index_range().contains(index)) {
return false;
}
// RNA_pointer_create(ptr->owner_id, &RNA_NodeTreeInterfaceItem, cache.items[index], r_ptr);
RNA_pointer_create(
ptr->owner_id, &RNA_NodeTreeInterfaceItem, ntree->interface_items()[index], r_ptr);
return true;
}
@@ -703,29 +803,26 @@ static int rna_NodeTreeInterface_items_lookup_string(struct PointerRNA *ptr,
}
ntree->ensure_topology_cache();
// Note: New API, enabled when interface cache is added.
UNUSED_VARS(key, r_ptr);
// const blender::bke::bNodeTreeInterfaceCache &cache = ntree->interface_cache();
// for (bNodeTreeInterfaceItem *item : cache.items) {
// switch (item->item_type) {
// case NODE_INTERFACE_SOCKET: {
// bNodeTreeInterfaceSocket *socket = reinterpret_cast<bNodeTreeInterfaceSocket *>(item);
// if (STREQ(socket->name, key)) {
// RNA_pointer_create(ptr->owner_id, &RNA_NodeTreeInterfaceSocket, socket, r_ptr);
// return true;
// }
// break;
// }
// case NODE_INTERFACE_PANEL: {
// bNodeTreeInterfacePanel *panel = reinterpret_cast<bNodeTreeInterfacePanel *>(item);
// if (STREQ(panel->name, key)) {
// RNA_pointer_create(ptr->owner_id, &RNA_NodeTreeInterfacePanel, panel, r_ptr);
// return true;
// }
// break;
// }
// }
// }
for (bNodeTreeInterfaceItem *item : ntree->interface_items()) {
switch (item->item_type) {
case NODE_INTERFACE_SOCKET: {
bNodeTreeInterfaceSocket *socket = reinterpret_cast<bNodeTreeInterfaceSocket *>(item);
if (STREQ(socket->name, key)) {
RNA_pointer_create(ptr->owner_id, &RNA_NodeTreeInterfaceSocket, socket, r_ptr);
return true;
}
break;
}
case NODE_INTERFACE_PANEL: {
bNodeTreeInterfacePanel *panel = reinterpret_cast<bNodeTreeInterfacePanel *>(item);
if (STREQ(panel->name, key)) {
RNA_pointer_create(ptr->owner_id, &RNA_NodeTreeInterfacePanel, panel, r_ptr);
return true;
}
break;
}
}
}
return false;
}
@@ -747,6 +844,26 @@ static void rna_def_node_interface_item(BlenderRNA *brna)
RNA_def_property_enum_items(prop, rna_enum_node_tree_interface_item_type_items);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Item Type", "Type of interface item");
prop = RNA_def_property(srna, "parent", PROP_POINTER, PROP_NONE);
RNA_def_property_struct_type(prop, "NodeTreeInterfacePanel");
RNA_def_property_pointer_funcs(
prop, "rna_NodeTreeInterfaceItem_parent_get", nullptr, nullptr, nullptr);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Parent", "Panel that contains the item");
prop = RNA_def_property(srna, "position", PROP_INT, PROP_NONE);
RNA_def_property_int_funcs(prop, "rna_NodeTreeInterfaceItem_position_get", nullptr, nullptr);
RNA_def_property_range(prop, -1, INT_MAX);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Position", "Position of the item in its parent panel");
prop = RNA_def_property(srna, "index", PROP_INT, PROP_NONE);
RNA_def_property_int_funcs(prop, "rna_NodeTreeInterfaceItem_index_get", nullptr, nullptr);
RNA_def_property_range(prop, -1, INT_MAX);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(
prop, "Index", "Global index of the item among all items in the interface");
}
static void rna_def_node_interface_socket(BlenderRNA *brna)
@@ -789,20 +906,17 @@ static void rna_def_node_interface_socket(BlenderRNA *brna)
"rna_NodeTreeInterfaceSocket_socket_type_get",
"rna_NodeTreeInterfaceSocket_socket_type_set",
"rna_NodeTreeInterfaceSocket_socket_type_itemf");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_ui_text(
prop, "Socket Type", "Type of the socket generated by this interface item");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceItem_update");
prop = RNA_def_property(srna, "is_input", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "flag", NODE_INTERFACE_SOCKET_INPUT);
prop = RNA_def_property(srna, "in_out", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_bitflag_sdna(prop, nullptr, "flag");
RNA_def_property_enum_items(prop, node_tree_interface_socket_in_out_items);
RNA_def_property_enum_default(prop, NODE_INTERFACE_SOCKET_INPUT);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_ui_text(prop, "Is Input", "Whether the socket is an input");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceItem_update");
prop = RNA_def_property(srna, "is_output", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "flag", NODE_INTERFACE_SOCKET_OUTPUT);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_ui_text(prop, "Is Output", "Whether the socket is an output");
RNA_def_property_ui_text(prop, "Input/Output Type", "Input or output socket type");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceItem_update");
prop = RNA_def_property(srna, "hide_value", PROP_BOOLEAN, PROP_NONE);
@@ -843,7 +957,7 @@ static void rna_def_node_interface_socket(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Socket Type Name", "Name of the socket type");
func = RNA_def_function(srna, "draw", nullptr);
RNA_def_function_flag(func, FUNC_REGISTER_OPTIONAL | FUNC_USE_SELF_ID);
RNA_def_function_flag(func, FUNC_REGISTER_OPTIONAL);
RNA_def_function_ui_description(func, "Draw properties of the socket interface");
parm = RNA_def_pointer(func, "context", "Context", "", "");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
@@ -854,7 +968,7 @@ static void rna_def_node_interface_socket(BlenderRNA *brna)
func = RNA_def_function(srna, "init_socket", nullptr);
RNA_def_function_ui_description(func, "Initialize a node socket instance");
RNA_def_function_flag(func, FUNC_REGISTER_OPTIONAL | FUNC_USE_SELF_ID | FUNC_ALLOW_WRITE);
RNA_def_function_flag(func, FUNC_REGISTER_OPTIONAL | FUNC_ALLOW_WRITE);
parm = RNA_def_pointer(func, "node", "Node", "Node", "Node of the socket to initialize");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
parm = RNA_def_pointer(func, "socket", "NodeSocket", "Socket", "Socket to initialize");
@@ -865,7 +979,7 @@ static void rna_def_node_interface_socket(BlenderRNA *brna)
func = RNA_def_function(srna, "from_socket", nullptr);
RNA_def_function_ui_description(func, "Setup template parameters from an existing socket");
RNA_def_function_flag(func, FUNC_REGISTER_OPTIONAL | FUNC_USE_SELF_ID | FUNC_ALLOW_WRITE);
RNA_def_function_flag(func, FUNC_REGISTER_OPTIONAL | FUNC_ALLOW_WRITE);
parm = RNA_def_pointer(func, "node", "Node", "Node", "Node of the original socket");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
parm = RNA_def_pointer(func, "socket", "NodeSocket", "Socket", "Original socket");
@@ -886,6 +1000,17 @@ static void rna_def_node_interface_panel(BlenderRNA *brna)
RNA_def_struct_name_property(srna, prop);
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceItem_update");
prop = RNA_def_property(srna, "description", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, nullptr, "description");
RNA_def_property_ui_text(prop, "Description", "Panel description");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceItem_update");
prop = RNA_def_property(srna, "default_closed", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "flag", NODE_INTERFACE_PANEL_DEFAULT_CLOSED);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_ui_text(prop, "Default Closed", "Panel is closed by default on new nodes");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceItem_update");
prop = RNA_def_property(srna, "interface_items", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, nullptr, "items_array", "items_num");
RNA_def_property_struct_type(prop, "NodeTreeInterfaceItem");
@@ -922,8 +1047,12 @@ static void rna_def_node_tree_interface_items_api(StructRNA *srna)
parm = RNA_def_string(func, "name", nullptr, 0, "Name", "Name of the socket");
RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED);
RNA_def_string(func, "description", nullptr, 0, "Description", "Description of the socket");
RNA_def_boolean(func, "is_input", false, "Is Input", "Create an input socket");
RNA_def_boolean(func, "is_output", false, "Is Output", "Create an output socket");
RNA_def_enum_flag(func,
"in_out",
node_tree_interface_socket_in_out_items,
NODE_INTERFACE_SOCKET_INPUT,
"Input/Output Type",
"Create an input or output socket");
parm = RNA_def_enum(func,
"socket_type",
rna_enum_dummy_DEFAULT_items,
@@ -943,6 +1072,9 @@ static void rna_def_node_tree_interface_items_api(StructRNA *srna)
RNA_def_function_ui_description(func, "Add a new panel to the interface");
RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN | FUNC_USE_REPORTS);
parm = RNA_def_string(func, "name", nullptr, 0, "Name", "Name of the new panel");
RNA_def_string(func, "description", nullptr, 0, "Description", "Description of the panel");
RNA_def_boolean(
func, "default_closed", false, "Default Closed", "Panel is closed by default on new nodes");
RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED);
RNA_def_pointer(func,
"parent",
@@ -984,20 +1116,34 @@ static void rna_def_node_tree_interface_items_api(StructRNA *srna)
RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN);
parm = RNA_def_pointer(func, "item", "NodeTreeInterfaceItem", "Item", "The item to remove");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
parm = RNA_def_int(
func, "to_index", -1, 0, INT_MAX, "To Index", "Target index for the item", 0, 10000);
parm = RNA_def_int(func,
"to_position",
-1,
0,
INT_MAX,
"To Position",
"Target position for the item in its current panel",
0,
10000);
RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED);
func = RNA_def_function(srna, "move_to_parent", "rna_NodeTreeInterfaceItems_move_to_parent");
RNA_def_function_ui_description(func, "Move an item to a new panel and/or position.");
RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN);
RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN | FUNC_USE_REPORTS);
parm = RNA_def_pointer(func, "item", "NodeTreeInterfaceItem", "Item", "The item to remove");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
parm = RNA_def_pointer(
func, "parent", "NodeTreeInterfacePanel", "Parent", "New parent of the item");
RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED);
parm = RNA_def_int(
func, "to_index", -1, 0, INT_MAX, "To Index", "Target index for the item", 0, 10000);
parm = RNA_def_int(func,
"to_position",
-1,
0,
INT_MAX,
"To Position",
"Target position for the item in the new parent panel",
0,
10000);
RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED);
}
+5 -272
View File
@@ -1287,205 +1287,11 @@ static void rna_NodeTree_link_clear(bNodeTree *ntree, Main *bmain, ReportList *r
WM_main_add_notifier(NC_NODE | NA_EDITED, ntree);
}
static int rna_NodeTree_active_input_get(PointerRNA *ptr)
{
bNodeTree *ntree = static_cast<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);
@@ -10214,57 +10020,6 @@ static void rna_def_nodetree_link_api(BlenderRNA *brna, PropertyRNA *cprop)
RNA_def_function_flag(func, FUNC_USE_MAIN | FUNC_USE_REPORTS);
}
static void rna_def_node_tree_sockets_api(BlenderRNA *brna, PropertyRNA *cprop, int in_out)
{
StructRNA *srna;
PropertyRNA *parm;
FunctionRNA *func;
const char *structtype = (in_out == SOCK_IN ? "NodeTreeInputs" : "NodeTreeOutputs");
const char *uiname = (in_out == SOCK_IN ? "Node Tree Inputs" : "Node Tree Outputs");
const char *newfunc = (in_out == SOCK_IN ? "rna_NodeTree_inputs_new" :
"rna_NodeTree_outputs_new");
const char *clearfunc = (in_out == SOCK_IN ? "rna_NodeTree_inputs_clear" :
"rna_NodeTree_outputs_clear");
const char *movefunc = (in_out == SOCK_IN ? "rna_NodeTree_inputs_move" :
"rna_NodeTree_outputs_move");
RNA_def_property_srna(cprop, structtype);
srna = RNA_def_struct(brna, structtype, nullptr);
RNA_def_struct_sdna(srna, "bNodeTree");
RNA_def_struct_ui_text(srna, uiname, "Collection of Node Tree Sockets");
func = RNA_def_function(srna, "new", newfunc);
RNA_def_function_ui_description(func, "Add a socket to this node tree");
RNA_def_function_flag(func, FUNC_USE_MAIN | FUNC_USE_REPORTS);
parm = RNA_def_string(func, "type", nullptr, MAX_NAME, "Type", "Data type");
RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED);
parm = RNA_def_string(func, "name", nullptr, MAX_NAME, "Name", "");
RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED);
/* return value */
parm = RNA_def_pointer(func, "socket", "NodeSocketInterface", "", "New socket");
RNA_def_function_return(func, parm);
func = RNA_def_function(srna, "remove", "rna_NodeTree_socket_remove");
RNA_def_function_ui_description(func, "Remove a socket from this node tree");
RNA_def_function_flag(func, FUNC_USE_MAIN | FUNC_USE_REPORTS);
parm = RNA_def_pointer(func, "socket", "NodeSocketInterface", "", "The socket to remove");
RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED);
func = RNA_def_function(srna, "clear", clearfunc);
RNA_def_function_ui_description(func, "Remove all sockets from this node tree");
RNA_def_function_flag(func, FUNC_USE_MAIN | FUNC_USE_REPORTS);
func = RNA_def_function(srna, "move", movefunc);
RNA_def_function_ui_description(func, "Move a socket to another position");
RNA_def_function_flag(func, FUNC_USE_MAIN);
parm = RNA_def_int(
func, "from_index", -1, 0, INT_MAX, "From Index", "Index of the socket to move", 0, 10000);
RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED);
parm = RNA_def_int(
func, "to_index", -1, 0, INT_MAX, "To Index", "Target index for the socket", 0, 10000);
RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED);
}
static void rna_def_nodetree(BlenderRNA *brna)
{
StructRNA *srna;
@@ -10278,6 +10033,7 @@ static void rna_def_nodetree(BlenderRNA *brna)
ICON_QUESTION,
"Undefined",
"Undefined type of nodes (can happen e.g. when a linked node tree goes missing)"},
{NTREE_CUSTOM, "CUSTOM", ICON_NONE, "Custom", "Custom nodes"},
{NTREE_SHADER, "SHADER", ICON_MATERIAL, "Shader", "Shader nodes"},
{NTREE_TEXTURE, "TEXTURE", ICON_TEXTURE, "Texture", "Texture nodes"},
{NTREE_COMPOSIT, "COMPOSITING", ICON_RENDERLAYERS, "Compositing", "Compositing nodes"},
@@ -10339,34 +10095,11 @@ static void rna_def_nodetree(BlenderRNA *brna)
"Type",
"Node Tree type (deprecated, bl_idname is the actual node tree type identifier)");
prop = RNA_def_property(srna, "inputs", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, nullptr, "inputs", nullptr);
RNA_def_property_struct_type(prop, "NodeSocketInterface");
prop = RNA_def_property(srna, "interface", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, nullptr, "tree_interface");
RNA_def_property_struct_type(prop, "NodeTreeInterface");
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Inputs", "Node tree inputs");
rna_def_node_tree_sockets_api(brna, prop, SOCK_IN);
prop = RNA_def_property(srna, "active_input", PROP_INT, PROP_UNSIGNED);
RNA_def_property_int_funcs(
prop, "rna_NodeTree_active_input_get", "rna_NodeTree_active_input_set", nullptr);
RNA_def_property_ui_text(prop, "Active Input", "Index of the active input");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_update(prop, NC_NODE, nullptr);
prop = RNA_def_property(srna, "outputs", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, nullptr, "outputs", nullptr);
RNA_def_property_struct_type(prop, "NodeSocketInterface");
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Outputs", "Node tree outputs");
rna_def_node_tree_sockets_api(brna, prop, SOCK_OUT);
prop = RNA_def_property(srna, "active_output", PROP_INT, PROP_UNSIGNED);
RNA_def_property_int_funcs(
prop, "rna_NodeTree_active_output_get", "rna_NodeTree_active_output_set", nullptr);
RNA_def_property_ui_text(prop, "Active Output", "Index of the active output");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_update(prop, NC_NODE, nullptr);
RNA_def_property_ui_text(prop, "Interface", "Interface declaration for this node tree");
/* exposed as a function for runtime interface type properties */
func = RNA_def_function(srna, "interface_update", "rna_NodeTree_interface_update");
RNA_def_function_ui_description(func, "Updated node group interface");
@@ -2085,6 +2085,15 @@ void RNA_api_ui_layout(StructRNA *srna)
srna, "template_grease_pencil_layer_tree", "uiTemplateGreasePencilLayerTree");
RNA_def_function_ui_description(func, "View of the active grease pencil layer tree");
RNA_def_function_flag(func, FUNC_USE_CONTEXT);
func = RNA_def_function(srna, "template_node_tree_interface", "uiTemplateNodeTreeInterface");
RNA_def_function_ui_description(func, "Show a node tree interface");
parm = RNA_def_pointer(func,
"interface",
"NodeTreeInterface",
"Node Tree Interface",
"Interface of a node tree to display");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR);
}
#endif
+42 -25
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_inputs().index_range()) {
const bNodeTreeInterfaceSocket *socket = nmd->node_group->interface_inputs()[i];
const bNodeSocketType *typeinfo = socket->socket_typeinfo();
const eNodeSocketDatatype type = typeinfo ? eNodeSocketDatatype(typeinfo->type) : SOCK_CUSTOM;
/* The first socket is the special geometry socket for the modifier object. */
if (i == 0 && socket->type == SOCK_GEOMETRY) {
if (i == 0 && type == SOCK_GEOMETRY) {
geometry_socket_count++;
continue;
}
IDProperty *property = IDP_GetPropertyFromGroup(nmd->settings.properties, socket->identifier);
if (property == nullptr) {
if (socket->type == SOCK_GEOMETRY) {
if (type == SOCK_GEOMETRY) {
geometry_socket_count++;
}
else {
@@ -649,7 +651,10 @@ static void check_property_socket_sync(const Object *ob, ModifierData *md)
}
if (geometry_socket_count == 1) {
if (((bNodeSocket *)nmd->node_group->inputs.first)->type != SOCK_GEOMETRY) {
const bNodeTreeInterfaceSocket *first_socket = nmd->node_group->interface_inputs()[0];
const bNodeSocketType *typeinfo = first_socket->socket_typeinfo();
const eNodeSocketDatatype type = typeinfo ? eNodeSocketDatatype(typeinfo->type) : SOCK_CUSTOM;
if (type != SOCK_GEOMETRY) {
BKE_modifier_set_error(ob, md, "Node group's geometry input must be the first");
}
}
@@ -1053,7 +1058,7 @@ static void add_attribute_search_button(const bContext &C,
const NodesModifierData &nmd,
PointerRNA *md_ptr,
const StringRefNull rna_path_attribute_name,
const bNodeSocket &socket,
const bNodeTreeInterfaceSocket &socket,
const bool is_output)
{
if (!nmd.runtime->eval_log) {
@@ -1116,10 +1121,13 @@ static void add_attribute_search_or_value_buttons(const bContext &C,
uiLayout *layout,
const NodesModifierData &nmd,
PointerRNA *md_ptr,
const bNodeSocket &socket)
const bNodeTreeInterfaceSocket &socket)
{
char socket_id_esc[sizeof(socket.identifier) * 2];
BLI_str_escape(socket_id_esc, socket.identifier, sizeof(socket_id_esc));
const StringRefNull identifier = socket.identifier;
const bNodeSocketType *typeinfo = socket.socket_typeinfo();
const eNodeSocketDatatype type = typeinfo ? eNodeSocketDatatype(typeinfo->type) : SOCK_CUSTOM;
char socket_id_esc[MAX_NAME * 2];
BLI_str_escape(socket_id_esc, identifier.c_str(), sizeof(socket_id_esc));
const std::string rna_path = "[\"" + std::string(socket_id_esc) + "\"]";
const std::string rna_path_attribute_name = "[\"" + std::string(socket_id_esc) +
nodes::input_attribute_name_suffix() + "\"]";
@@ -1133,7 +1141,7 @@ static void add_attribute_search_or_value_buttons(const bContext &C,
const std::optional<StringRef> attribute_name = nodes::input_attribute_name_get(
*nmd.settings.properties, socket);
if (socket.type == SOCK_BOOLEAN && !attribute_name) {
if (type == SOCK_BOOLEAN && !attribute_name) {
uiItemL(name_row, "", ICON_NONE);
}
else {
@@ -1141,7 +1149,7 @@ static void add_attribute_search_or_value_buttons(const bContext &C,
}
uiLayout *prop_row = uiLayoutRow(split, true);
if (socket.type == SOCK_BOOLEAN) {
if (type == SOCK_BOOLEAN) {
uiLayoutSetPropSep(prop_row, false);
uiLayoutSetAlignment(prop_row, UI_LAYOUT_ALIGN_EXPAND);
}
@@ -1151,7 +1159,7 @@ static void add_attribute_search_or_value_buttons(const bContext &C,
uiItemL(layout, "", ICON_BLANK1);
}
else {
const char *name = socket.type == SOCK_BOOLEAN ? socket.name : "";
const char *name = type == SOCK_BOOLEAN ? socket.name : "";
uiItemR(prop_row, md_ptr, rna_path.c_str(), UI_ITEM_NONE, name, ICON_NONE);
uiItemDecoratorR(layout, md_ptr, rna_path.c_str(), -1);
}
@@ -1177,11 +1185,12 @@ static void draw_property_for_socket(const bContext &C,
NodesModifierData *nmd,
PointerRNA *bmain_ptr,
PointerRNA *md_ptr,
const bNodeSocket &socket,
const bNodeTreeInterfaceSocket &socket,
const int socket_index)
{
const StringRefNull identifier = socket.identifier;
/* The property should be created in #MOD_nodes_update_interface with the correct type. */
IDProperty *property = IDP_GetPropertyFromGroup(nmd->settings.properties, socket.identifier);
IDProperty *property = IDP_GetPropertyFromGroup(nmd->settings.properties, identifier.c_str());
/* IDProperties can be removed with python, so there could be a situation where
* there isn't a property for a socket or it doesn't have the correct type. */
@@ -1189,8 +1198,8 @@ static void draw_property_for_socket(const bContext &C,
return;
}
char socket_id_esc[sizeof(socket.identifier) * 2];
BLI_str_escape(socket_id_esc, socket.identifier, sizeof(socket_id_esc));
char socket_id_esc[MAX_NAME * 2];
BLI_str_escape(socket_id_esc, identifier.c_str(), sizeof(socket_id_esc));
char rna_path[sizeof(socket_id_esc) + 4];
SNPRINTF(rna_path, "[\"%s\"]", socket_id_esc);
@@ -1201,7 +1210,9 @@ static void draw_property_for_socket(const bContext &C,
/* Use #uiItemPointerR to draw pointer properties because #uiItemR would not have enough
* information about what type of ID to select for editing the values. This is because
* pointer IDProperties contain no information about their type. */
switch (socket.type) {
const bNodeSocketType *typeinfo = socket.socket_typeinfo();
const eNodeSocketDatatype type = typeinfo ? eNodeSocketDatatype(typeinfo->type) : SOCK_CUSTOM;
switch (type) {
case SOCK_OBJECT: {
uiItemPointerR(row, md_ptr, rna_path, bmain_ptr, "objects", socket.name, ICON_OBJECT_DATA);
break;
@@ -1241,10 +1252,11 @@ static void draw_property_for_output_socket(const bContext &C,
uiLayout *layout,
const NodesModifierData &nmd,
PointerRNA *md_ptr,
const bNodeSocket &socket)
const bNodeTreeInterfaceSocket &socket)
{
char socket_id_esc[sizeof(socket.identifier) * 2];
BLI_str_escape(socket_id_esc, socket.identifier, sizeof(socket_id_esc));
const StringRefNull identifier = socket.identifier;
char socket_id_esc[MAX_NAME * 2];
BLI_str_escape(socket_id_esc, identifier.c_str(), sizeof(socket_id_esc));
const std::string rna_path_attribute_name = "[\"" + StringRef(socket_id_esc) +
nodes::input_attribute_name_suffix() + "\"]";
@@ -1285,9 +1297,11 @@ static void panel_draw(const bContext *C, Panel *panel)
PointerRNA bmain_ptr;
RNA_main_pointer_create(bmain, &bmain_ptr);
int socket_index;
LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, &nmd->node_group->inputs, socket_index) {
if (!(socket->flag & SOCK_HIDE_IN_MODIFIER)) {
nmd->node_group->ensure_topology_cache();
for (const int socket_index : nmd->node_group->interface_inputs().index_range()) {
const bNodeTreeInterfaceSocket *socket = nmd->node_group->interface_inputs()[socket_index];
if (!(socket->flag & NODE_INTERFACE_SOCKET_HIDE_IN_MODIFIER)) {
draw_property_for_socket(*C, layout, nmd, &bmain_ptr, ptr, *socket, socket_index);
}
}
@@ -1319,8 +1333,11 @@ static void output_attribute_panel_draw(const bContext *C, Panel *panel)
bool has_output_attribute = false;
if (nmd->node_group != nullptr && nmd->settings.properties != nullptr) {
LISTBASE_FOREACH (bNodeSocket *, socket, &nmd->node_group->outputs) {
if (nodes::socket_type_has_attribute_toggle(*socket)) {
for (const bNodeTreeInterfaceSocket *socket : nmd->node_group->interface_outputs()) {
const bNodeSocketType *typeinfo = socket->socket_typeinfo();
const eNodeSocketDatatype type = typeinfo ? eNodeSocketDatatype(typeinfo->type) :
SOCK_CUSTOM;
if (nodes::socket_type_has_attribute_toggle(type)) {
has_output_attribute = true;
draw_property_for_output_socket(*C, layout, *nmd, ptr, *socket);
}
@@ -9,9 +9,11 @@
#include "BLI_multi_value_map.hh"
#include "BKE_idprop.hh"
#include "BKE_node.h"
struct bNodeTree;
struct bNodeSocket;
struct bNodeTreeInterfaceSocket;
struct Depsgraph;
namespace blender::bke {
struct GeometrySet;
@@ -31,12 +33,12 @@ StringRef input_use_attribute_suffix();
StringRef input_attribute_name_suffix();
std::optional<StringRef> input_attribute_name_get(const IDProperty &props,
const bNodeSocket &io_input);
const bNodeTreeInterfaceSocket &io_input);
/**
* \return Whether using an attribute to input values of this type is supported.
*/
bool socket_type_has_attribute_toggle(const bNodeSocket &socket);
bool socket_type_has_attribute_toggle(eNodeSocketDatatype type);
/**
* \return Whether using an attribute to input values of this type is supported, and the node
@@ -44,10 +46,11 @@ bool socket_type_has_attribute_toggle(const bNodeSocket &socket);
*/
bool input_has_attribute_toggle(const bNodeTree &node_tree, const int socket_index);
bool id_property_type_matches_socket(const bNodeSocket &socket, const IDProperty &property);
bool id_property_type_matches_socket(const bNodeTreeInterfaceSocket &socket,
const IDProperty &property);
std::unique_ptr<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,
+66 -8
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 identifier;
std::string name;
std::string description;
std::string translation_context;
bool default_collapsed = false;
int num_items = 0;
private:
friend NodeDeclarationBuilder;
friend class PanelDeclarationBuilder;
public:
virtual ~PanelDeclaration() = default;
void build(bNodePanelState &panel) const;
bool matches(const bNodePanelState &panel) const;
void update_or_build(const bNodePanelState &old_panel, bNodePanelState &new_panel) const;
};
class PanelDeclarationBuilder {
protected:
NodeDeclarationBuilder *node_decl_builder_ = nullptr;
PanelDeclaration *decl_;
friend class NodeDeclarationBuilder;
public:
PanelDeclarationBuilder &default_closed(bool collapsed)
{
decl_->default_collapsed = collapsed;
return *this;
}
};
using PanelDeclarationPtr = std::unique_ptr<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
@@ -490,10 +543,14 @@ class NodeDeclaration {
* available again in the future. */
bool skip_updating_sockets = false;
/** Use order of socket declarations for socket order instead of conventional
* outputs | buttons | inputs order. Panels are only supported when using custom socket order. */
bool use_custom_socket_order = false;
friend NodeDeclarationBuilder;
bool matches(const bNode &node) const;
Span<SocketDeclarationPtr> sockets(eNodeSocketInOut in_out) const;
Span<SocketDeclaration *> sockets(eNodeSocketInOut in_out) const;
const aal::RelationsInNode *anonymous_attribute_relations() const
{
@@ -732,8 +789,8 @@ inline typename DeclType::Builder &NodeDeclarationBuilder::add_socket(StringRef
static_assert(std::is_base_of_v<SocketDeclaration, DeclType>);
using Builder = typename DeclType::Builder;
Vector<SocketDeclarationPtr> &declarations = in_out == SOCK_IN ? declaration_.inputs :
declaration_.outputs;
Vector<SocketDeclaration *> &declarations = in_out == SOCK_IN ? declaration_.inputs :
declaration_.outputs;
std::unique_ptr<DeclType> socket_decl = std::make_unique<DeclType>();
std::unique_ptr<Builder> socket_decl_builder = std::make_unique<Builder>();
@@ -743,7 +800,8 @@ inline typename DeclType::Builder &NodeDeclarationBuilder::add_socket(StringRef
socket_decl->name = name;
socket_decl->identifier = identifier.is_empty() ? name : identifier;
socket_decl->in_out = in_out;
socket_decl_builder->index_ = declarations.append_and_get_index(std::move(socket_decl));
socket_decl_builder->index_ = declarations.append_and_get_index(socket_decl.get());
declaration_.items.append(std::move(socket_decl));
Builder &socket_decl_builder_ref = *socket_decl_builder;
((in_out == SOCK_IN) ? input_builders_ : output_builders_)
.append(std::move(socket_decl_builder));
@@ -756,7 +814,7 @@ inline typename DeclType::Builder &NodeDeclarationBuilder::add_socket(StringRef
/** \name #NodeDeclaration Inline Methods
* \{ */
inline Span<SocketDeclarationPtr> NodeDeclaration::sockets(eNodeSocketInOut in_out) const
inline Span<SocketDeclaration *> NodeDeclaration::sockets(eNodeSocketInOut in_out) const
{
if (in_out == SOCK_IN) {
return inputs;
+2
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();
@@ -270,6 +270,7 @@ class ExtendBuilder : public SocketDeclarationBuilder<Extend> {
class Custom : public SocketDeclaration {
public:
const char *idname_;
std::function<void(bNode &node, bNodeSocket &socket, const char *data_path)> init_socket_fn;
bNodeSocket &build(bNodeTree &ntree, bNode &node) const override;
bool matches(const bNodeSocket &socket) const override;
@@ -146,6 +146,6 @@ class GatherLinkSearchOpParams {
void search_link_ops_for_basic_node(GatherLinkSearchOpParams &params);
void search_link_ops_for_declarations(GatherLinkSearchOpParams &params,
Span<SocketDeclarationPtr> declarations);
Span<SocketDeclaration *> declarations);
} // namespace blender::nodes
@@ -96,12 +96,22 @@ 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);
r_declaration.inputs.append(input_decl.get());
r_declaration.items.append(std::move(input_decl));
SocketDeclarationPtr output_decl = socket_declaration_for_repeat_item(
item, SOCK_OUT, r_declaration.inputs.size() - 1);
r_declaration.outputs.append(output_decl.get());
r_declaration.items.append(std::move(output_decl));
}
r_declaration.inputs.append(decl::create_extend_declaration(SOCK_IN));
r_declaration.outputs.append(decl::create_extend_declaration(SOCK_OUT));
SocketDeclarationPtr input_extend_decl = decl::create_extend_declaration(SOCK_IN);
r_declaration.inputs.append(input_extend_decl.get());
r_declaration.items.append(std::move(input_extend_decl));
SocketDeclarationPtr output_extend_decl = decl::create_extend_declaration(SOCK_OUT);
r_declaration.outputs.append(output_extend_decl.get());
r_declaration.items.append(std::move(output_extend_decl));
}
} // namespace blender::nodes
namespace blender::nodes::node_geo_repeat_output_cc {
@@ -191,7 +191,8 @@ static void node_declare_dynamic(const bNodeTree &node_tree,
delta_time->identifier = "Delta Time";
delta_time->name = DATA_("Delta Time");
delta_time->in_out = SOCK_OUT;
r_declaration.outputs.append(std::move(delta_time));
r_declaration.outputs.append(delta_time.get());
r_declaration.items.append(std::move(delta_time));
const NodeGeometrySimulationOutput &storage = *static_cast<const NodeGeometrySimulationOutput *>(
output_node->storage);
@@ -97,11 +97,19 @@ void socket_declarations_for_simulation_items(const Span<NodeSimulationItem> ite
{
for (const int i : items.index_range()) {
const NodeSimulationItem &item = items[i];
r_declaration.inputs.append(socket_declaration_for_simulation_item(item, SOCK_IN, i));
r_declaration.outputs.append(socket_declaration_for_simulation_item(item, SOCK_OUT, i));
SocketDeclarationPtr input_decl = socket_declaration_for_simulation_item(item, SOCK_IN, i);
SocketDeclarationPtr output_decl = socket_declaration_for_simulation_item(item, SOCK_OUT, i);
r_declaration.inputs.append(input_decl.get());
r_declaration.items.append(std::move(input_decl));
r_declaration.outputs.append(output_decl.get());
r_declaration.items.append(std::move(output_decl));
}
r_declaration.inputs.append(decl::create_extend_declaration(SOCK_IN));
r_declaration.outputs.append(decl::create_extend_declaration(SOCK_OUT));
SocketDeclarationPtr input_extend_decl = decl::create_extend_declaration(SOCK_IN);
SocketDeclarationPtr output_extend_decl = decl::create_extend_declaration(SOCK_OUT);
r_declaration.inputs.append(input_extend_decl.get());
r_declaration.items.append(std::move(input_extend_decl));
r_declaration.outputs.append(output_extend_decl.get());
r_declaration.items.append(std::move(output_extend_decl));
}
struct SimulationItemsUniqueNameArgs {
@@ -39,10 +39,9 @@ StringRef input_attribute_name_suffix()
return "_attribute_name";
}
bool socket_type_has_attribute_toggle(const bNodeSocket &socket)
bool socket_type_has_attribute_toggle(const eNodeSocketDatatype type)
{
return ELEM(
socket.type, SOCK_FLOAT, SOCK_VECTOR, SOCK_BOOLEAN, SOCK_RGBA, SOCK_INT, SOCK_ROTATION);
return ELEM(type, SOCK_FLOAT, SOCK_VECTOR, SOCK_BOOLEAN, SOCK_RGBA, SOCK_INT, SOCK_ROTATION);
}
bool input_has_attribute_toggle(const bNodeTree &node_tree, const int socket_index)
@@ -54,13 +53,16 @@ bool input_has_attribute_toggle(const bNodeTree &node_tree, const int socket_ind
}
std::unique_ptr<IDProperty, bke::idprop::IDPropertyDeleter> id_property_create_from_socket(
const bNodeSocket &socket)
const bNodeTreeInterfaceSocket &socket)
{
switch (socket.type) {
const StringRefNull identifier = socket.identifier;
const bNodeSocketType *typeinfo = socket.socket_typeinfo();
const eNodeSocketDatatype type = typeinfo ? eNodeSocketDatatype(typeinfo->type) : SOCK_CUSTOM;
switch (type) {
case SOCK_FLOAT: {
const bNodeSocketValueFloat *value = static_cast<const bNodeSocketValueFloat *>(
socket.default_value);
auto property = bke::idprop::create(socket.identifier, value->value);
socket.socket_data);
auto property = bke::idprop::create(identifier, value->value);
IDPropertyUIDataFloat *ui_data = (IDPropertyUIDataFloat *)IDP_ui_data_ensure(property.get());
ui_data->base.rna_subtype = value->subtype;
ui_data->soft_min = double(value->min);
@@ -70,8 +72,8 @@ std::unique_ptr<IDProperty, bke::idprop::IDPropertyDeleter> id_property_create_f
}
case SOCK_INT: {
const bNodeSocketValueInt *value = static_cast<const bNodeSocketValueInt *>(
socket.default_value);
auto property = bke::idprop::create(socket.identifier, value->value);
socket.socket_data);
auto property = bke::idprop::create(identifier, value->value);
IDPropertyUIDataInt *ui_data = (IDPropertyUIDataInt *)IDP_ui_data_ensure(property.get());
ui_data->base.rna_subtype = value->subtype;
ui_data->soft_min = value->min;
@@ -81,9 +83,9 @@ std::unique_ptr<IDProperty, bke::idprop::IDPropertyDeleter> id_property_create_f
}
case SOCK_VECTOR: {
const bNodeSocketValueVector *value = static_cast<const bNodeSocketValueVector *>(
socket.default_value);
socket.socket_data);
auto property = bke::idprop::create(
socket.identifier, Span<float>{value->value[0], value->value[1], value->value[2]});
identifier, Span<float>{value->value[0], value->value[1], value->value[2]});
IDPropertyUIDataFloat *ui_data = (IDPropertyUIDataFloat *)IDP_ui_data_ensure(property.get());
ui_data->base.rna_subtype = value->subtype;
ui_data->soft_min = double(value->min);
@@ -97,9 +99,9 @@ std::unique_ptr<IDProperty, bke::idprop::IDPropertyDeleter> id_property_create_f
}
case SOCK_RGBA: {
const bNodeSocketValueRGBA *value = static_cast<const bNodeSocketValueRGBA *>(
socket.default_value);
socket.socket_data);
auto property = bke::idprop::create(
socket.identifier,
identifier,
Span<float>{value->value[0], value->value[1], value->value[2], value->value[3]});
IDPropertyUIDataFloat *ui_data = (IDPropertyUIDataFloat *)IDP_ui_data_ensure(property.get());
ui_data->base.rna_subtype = PROP_COLOR;
@@ -116,17 +118,17 @@ std::unique_ptr<IDProperty, bke::idprop::IDPropertyDeleter> id_property_create_f
}
case SOCK_BOOLEAN: {
const bNodeSocketValueBoolean *value = static_cast<const bNodeSocketValueBoolean *>(
socket.default_value);
auto property = bke::idprop::create_bool(socket.identifier, value->value);
socket.socket_data);
auto property = bke::idprop::create_bool(identifier, value->value);
IDPropertyUIDataBool *ui_data = (IDPropertyUIDataBool *)IDP_ui_data_ensure(property.get());
ui_data->default_value = value->value != 0;
return property;
}
case SOCK_ROTATION: {
const bNodeSocketValueRotation *value = static_cast<const bNodeSocketValueRotation *>(
socket.default_value);
socket.socket_data);
auto property = bke::idprop::create(
socket.identifier,
identifier,
Span<float>{value->value_euler[0], value->value_euler[1], value->value_euler[2]});
IDPropertyUIDataFloat *ui_data = reinterpret_cast<IDPropertyUIDataFloat *>(
IDP_ui_data_ensure(property.get()));
@@ -135,8 +137,8 @@ std::unique_ptr<IDProperty, bke::idprop::IDPropertyDeleter> id_property_create_f
}
case SOCK_STRING: {
const bNodeSocketValueString *value = static_cast<const bNodeSocketValueString *>(
socket.default_value);
auto property = bke::idprop::create(socket.identifier, value->value);
socket.socket_data);
auto property = bke::idprop::create(identifier, value->value);
IDPropertyUIDataString *ui_data = (IDPropertyUIDataString *)IDP_ui_data_ensure(
property.get());
ui_data->default_value = BLI_strdup(value->value);
@@ -144,39 +146,46 @@ std::unique_ptr<IDProperty, bke::idprop::IDPropertyDeleter> id_property_create_f
}
case SOCK_OBJECT: {
const bNodeSocketValueObject *value = static_cast<const bNodeSocketValueObject *>(
socket.default_value);
auto property = bke::idprop::create(socket.identifier, reinterpret_cast<ID *>(value->value));
socket.socket_data);
auto property = bke::idprop::create(identifier, reinterpret_cast<ID *>(value->value));
IDPropertyUIDataID *ui_data = (IDPropertyUIDataID *)IDP_ui_data_ensure(property.get());
ui_data->id_type = ID_OB;
return property;
}
case SOCK_COLLECTION: {
const bNodeSocketValueCollection *value = static_cast<const bNodeSocketValueCollection *>(
socket.default_value);
return bke::idprop::create(socket.identifier, reinterpret_cast<ID *>(value->value));
socket.socket_data);
return bke::idprop::create(identifier, reinterpret_cast<ID *>(value->value));
}
case SOCK_TEXTURE: {
const bNodeSocketValueTexture *value = static_cast<const bNodeSocketValueTexture *>(
socket.default_value);
return bke::idprop::create(socket.identifier, reinterpret_cast<ID *>(value->value));
socket.socket_data);
return bke::idprop::create(identifier, reinterpret_cast<ID *>(value->value));
}
case SOCK_IMAGE: {
const bNodeSocketValueImage *value = static_cast<const bNodeSocketValueImage *>(
socket.default_value);
return bke::idprop::create(socket.identifier, reinterpret_cast<ID *>(value->value));
socket.socket_data);
return bke::idprop::create(identifier, reinterpret_cast<ID *>(value->value));
}
case SOCK_MATERIAL: {
const bNodeSocketValueMaterial *value = static_cast<const bNodeSocketValueMaterial *>(
socket.default_value);
return bke::idprop::create(socket.identifier, reinterpret_cast<ID *>(value->value));
socket.socket_data);
return bke::idprop::create(identifier, reinterpret_cast<ID *>(value->value));
}
case SOCK_CUSTOM:
case SOCK_GEOMETRY:
case SOCK_SHADER:
return nullptr;
}
return nullptr;
}
bool id_property_type_matches_socket(const bNodeSocket &socket, const IDProperty &property)
bool id_property_type_matches_socket(const bNodeTreeInterfaceSocket &socket,
const IDProperty &property)
{
switch (socket.type) {
const bNodeSocketType *typeinfo = socket.socket_typeinfo();
const eNodeSocketDatatype type = typeinfo ? eNodeSocketDatatype(typeinfo->type) : SOCK_CUSTOM;
switch (type) {
case SOCK_FLOAT:
return ELEM(property.type, IDP_FLOAT, IDP_DOUBLE);
case SOCK_INT:
@@ -198,6 +207,10 @@ bool id_property_type_matches_socket(const bNodeSocket &socket, const IDProperty
case SOCK_IMAGE:
case SOCK_MATERIAL:
return property.type == IDP_ID;
case SOCK_CUSTOM:
case SOCK_GEOMETRY:
case SOCK_SHADER:
return false;
}
BLI_assert_unreachable();
return false;
@@ -322,10 +335,10 @@ static void init_socket_cpp_value_from_property(const IDProperty &property,
}
std::optional<StringRef> input_attribute_name_get(const IDProperty &props,
const bNodeSocket &io_input)
const bNodeTreeInterfaceSocket &io_input)
{
IDProperty *use_attribute = IDP_GetPropertyFromGroup(
&props, (io_input.identifier + input_use_attribute_suffix()).c_str());
&props, (std::string(io_input.identifier) + input_use_attribute_suffix()).c_str());
if (!use_attribute) {
return std::nullopt;
}
@@ -351,20 +364,21 @@ static void initialize_group_input(const bNodeTree &tree,
const int input_index,
void *r_value)
{
const bNodeSocket &io_input = *tree.interface_inputs()[input_index];
const bNodeSocketType &socket_type = *io_input.typeinfo;
const eNodeSocketDatatype socket_data_type = static_cast<eNodeSocketDatatype>(io_input.type);
const bNodeTreeInterfaceSocket &io_input = *tree.interface_inputs()[input_index];
const bNodeSocketType *typeinfo = io_input.socket_typeinfo();
const eNodeSocketDatatype socket_data_type = typeinfo ? eNodeSocketDatatype(typeinfo->type) :
SOCK_CUSTOM;
if (properties == nullptr) {
socket_type.get_geometry_nodes_cpp_value(io_input, r_value);
typeinfo->get_geometry_nodes_cpp_value(io_input.socket_data, r_value);
return;
}
const IDProperty *property = IDP_GetPropertyFromGroup(properties, io_input.identifier);
if (property == nullptr) {
socket_type.get_geometry_nodes_cpp_value(io_input, r_value);
typeinfo->get_geometry_nodes_cpp_value(io_input.socket_data, r_value);
return;
}
if (!id_property_type_matches_socket(io_input, *property)) {
socket_type.get_geometry_nodes_cpp_value(io_input, r_value);
typeinfo->get_geometry_nodes_cpp_value(io_input.socket_data, r_value);
return;
}
@@ -376,9 +390,9 @@ static void initialize_group_input(const bNodeTree &tree,
const std::optional<StringRef> attribute_name = input_attribute_name_get(*properties, io_input);
if (attribute_name && bke::allow_procedural_attribute_access(*attribute_name)) {
fn::GField attribute_field = bke::AttributeFieldInput::Create(*attribute_name,
*socket_type.base_cpp_type);
*typeinfo->base_cpp_type);
const auto *value_or_field_cpp_type = fn::ValueOrFieldCPPType::get_from_self(
*socket_type.geometry_nodes_cpp_type);
*typeinfo->geometry_nodes_cpp_type);
BLI_assert(value_or_field_cpp_type != nullptr);
value_or_field_cpp_type->construct_from_field(r_value, std::move(attribute_field));
}
@@ -409,7 +423,7 @@ static MultiValueMap<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;
}
@@ -432,7 +446,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_outputs()[index];
const eAttrDomain domain = (eAttrDomain)interface_socket->attribute_domain;
OutputAttributeInfo output_info;
output_info.field = std::move(field);
@@ -585,13 +599,16 @@ bke::GeometrySet execute_geometry_nodes_on_geometry(
int input_index = -1;
for (const int i : btree.interface_inputs().index_range()) {
input_index++;
const bNodeSocket &interface_socket = *btree.interface_inputs()[i];
if (interface_socket.type == SOCK_GEOMETRY && input_index == 0) {
const bNodeTreeInterfaceSocket &interface_socket = *btree.interface_inputs()[i];
const bNodeSocketType *typeinfo = interface_socket.socket_typeinfo();
const eNodeSocketDatatype socket_type = typeinfo ? eNodeSocketDatatype(typeinfo->type) :
SOCK_CUSTOM;
if (socket_type == SOCK_GEOMETRY && input_index == 0) {
param_inputs[input_index] = &input_geometry;
continue;
}
const CPPType *type = interface_socket.typeinfo->geometry_nodes_cpp_type;
const CPPType *type = typeinfo->geometry_nodes_cpp_type;
BLI_assert(type != nullptr);
void *value = allocator.allocate(type->size(), type->alignment());
initialize_group_input(btree, properties, i, value);
@@ -651,26 +668,31 @@ void update_input_properties_from_node_tree(const bNodeTree &tree,
IDProperty &properties)
{
tree.ensure_topology_cache();
const Span<const bNodeSocket *> tree_inputs = tree.interface_inputs();
const Span<const bNodeTreeInterfaceSocket *> tree_inputs = tree.interface_inputs();
for (const int i : tree_inputs.index_range()) {
const bNodeSocket &socket = *tree_inputs[i];
const bNodeTreeInterfaceSocket &socket = *tree_inputs[i];
const StringRefNull socket_identifier = socket.identifier;
const bNodeSocketType *typeinfo = socket.socket_typeinfo();
const eNodeSocketDatatype socket_type = typeinfo ? eNodeSocketDatatype(typeinfo->type) :
SOCK_CUSTOM;
IDProperty *new_prop = nodes::id_property_create_from_socket(socket).release();
if (new_prop == nullptr) {
/* Out of the set of supported input sockets, only
* geometry sockets aren't added to the modifier. */
BLI_assert(socket.type == SOCK_GEOMETRY);
BLI_assert(socket_type == SOCK_GEOMETRY);
continue;
}
new_prop->flag |= IDP_FLAG_OVERRIDABLE_LIBRARY;
if (socket.description[0] != '\0') {
if (socket.description && socket.description[0] != '\0') {
IDPropertyUIData *ui_data = IDP_ui_data_ensure(new_prop);
ui_data->description = BLI_strdup(socket.description);
}
IDP_AddToGroup(&properties, new_prop);
if (old_properties != nullptr) {
const IDProperty *old_prop = IDP_GetPropertyFromGroup(old_properties, socket.identifier);
const IDProperty *old_prop = IDP_GetPropertyFromGroup(old_properties,
socket_identifier.c_str());
if (old_prop != nullptr) {
if (nodes::id_property_type_matches_socket(socket, *old_prop)) {
/* #IDP_CopyPropertyContent replaces the UI data as well, which we don't (we only
@@ -691,9 +713,9 @@ void update_input_properties_from_node_tree(const bNodeTree &tree,
}
}
if (nodes::socket_type_has_attribute_toggle(socket)) {
const std::string use_attribute_id = socket.identifier + input_use_attribute_suffix();
const std::string attribute_name_id = socket.identifier + input_attribute_name_suffix();
if (nodes::socket_type_has_attribute_toggle(eNodeSocketDatatype(socket_type))) {
const std::string use_attribute_id = socket_identifier + input_use_attribute_suffix();
const std::string attribute_name_id = socket_identifier + input_attribute_name_suffix();
IDPropertyTemplate idprop = {0};
IDProperty *use_attribute_prop = IDP_New(
@@ -731,16 +753,20 @@ void update_output_properties_from_node_tree(const bNodeTree &tree,
IDProperty &properties)
{
tree.ensure_topology_cache();
const Span<const bNodeSocket *> tree_outputs = tree.interface_outputs();
const Span<const bNodeTreeInterfaceSocket *> tree_outputs = tree.interface_outputs();
for (const int i : tree_outputs.index_range()) {
const bNodeSocket &socket = *tree_outputs[i];
if (!nodes::socket_type_has_attribute_toggle(socket)) {
const bNodeTreeInterfaceSocket &socket = *tree_outputs[i];
const StringRefNull socket_identifier = socket.identifier;
const bNodeSocketType *typeinfo = socket.socket_typeinfo();
const eNodeSocketDatatype socket_type = typeinfo ? eNodeSocketDatatype(typeinfo->type) :
SOCK_CUSTOM;
if (!nodes::socket_type_has_attribute_toggle(socket_type)) {
continue;
}
const std::string idprop_name = socket.identifier + input_attribute_name_suffix();
const std::string idprop_name = socket_identifier + input_attribute_name_suffix();
IDProperty *new_prop = IDP_NewStringMaxSize("", idprop_name.c_str(), MAX_NAME);
if (socket.description[0] != '\0') {
if (socket.description && socket.description[0] != '\0') {
IDPropertyUIData *ui_data = IDP_ui_data_ensure(new_prop);
ui_data->description = BLI_strdup(socket.description);
}
@@ -1085,7 +1085,7 @@ static GMutablePointer get_socket_default_value(LinearAllocator<> &allocator,
return {};
}
void *buffer = allocator.allocate(type->size(), type->alignment());
typeinfo.get_geometry_nodes_cpp_value(bsocket, buffer);
typeinfo.get_geometry_nodes_cpp_value(bsocket.default_value, buffer);
return {type, buffer};
}
@@ -2621,9 +2621,10 @@ struct GeometryNodesLazyFunctionGraphBuilder {
void build_group_input_node(lf::Graph &lf_graph)
{
Vector<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_inputs();
for (const bNodeTreeInterfaceSocket *interface_input : interface_inputs) {
const bNodeSocketType *typeinfo = interface_input->socket_typeinfo();
input_cpp_types.append(typeinfo->geometry_nodes_cpp_type);
}
/* Create a dummy node for the group inputs. */
@@ -2644,8 +2645,9 @@ struct GeometryNodesLazyFunctionGraphBuilder {
{
Vector<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_outputs()) {
const bNodeSocketType *typeinfo = interface_output->socket_typeinfo();
output_cpp_types.append(typeinfo->geometry_nodes_cpp_type);
debug_info.socket_names.append(interface_output->name);
}
@@ -2813,8 +2815,9 @@ struct GeometryNodesLazyFunctionGraphBuilder {
{
Vector<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_outputs()) {
const bNodeSocketType *typeinfo = interface_input->socket_typeinfo();
output_cpp_types.append(typeinfo->geometry_nodes_cpp_type);
debug_info.socket_names.append(interface_input->name);
}
@@ -3542,7 +3545,7 @@ struct GeometryNodesLazyFunctionGraphBuilder {
*/
lf::DummyNode &build_output_usage_input_node(lf::Graph &lf_graph)
{
const Span<const bNodeSocket *> interface_outputs = btree_.interface_outputs();
const Span<const bNodeTreeInterfaceSocket *> interface_outputs = btree_.interface_outputs();
Vector<const CPPType *> cpp_types;
cpp_types.append_n_times(&CPPType::get<bool>(), interface_outputs.size());
@@ -3562,7 +3565,7 @@ 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_inputs();
Vector<const CPPType *> cpp_types;
cpp_types.append_n_times(&CPPType::get<bool>(), interface_inputs.size());
@@ -3822,13 +3825,15 @@ const GeometryNodesLazyFunctionGraphInfo *ensure_geometry_nodes_lazy_function_gr
return nullptr;
}
}
for (const bNodeSocket *interface_bsocket : btree.interface_inputs()) {
if (interface_bsocket->typeinfo->geometry_nodes_cpp_type == nullptr) {
for (const bNodeTreeInterfaceSocket *interface_bsocket : btree.interface_inputs()) {
const bNodeSocketType *typeinfo = interface_bsocket->socket_typeinfo();
if (typeinfo->geometry_nodes_cpp_type == nullptr) {
return nullptr;
}
}
for (const bNodeSocket *interface_bsocket : btree.interface_outputs()) {
if (interface_bsocket->typeinfo->geometry_nodes_cpp_type == nullptr) {
for (const bNodeTreeInterfaceSocket *interface_bsocket : btree.interface_outputs()) {
const bNodeSocketType *typeinfo = interface_bsocket->socket_typeinfo();
if (typeinfo->geometry_nodes_cpp_type == nullptr) {
return nullptr;
}
}
+170 -62
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,53 @@ 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)
{
if (io_panel.items_num == 0) {
return nullptr;
}
PanelDeclarationPtr dst = std::make_unique<PanelDeclaration>();
dst->identifier = io_panel.identifier;
dst->name = io_panel.name ? io_panel.name : "";
dst->description = io_panel.description ? io_panel.description : "";
dst->default_collapsed = (io_panel.flag & NODE_INTERFACE_PANEL_DEFAULT_CLOSED);
dst->num_items = io_panel.items_num;
return dst;
}
void node_group_declare_dynamic(const bNodeTree & /*node_tree*/,
const bNode &node,
NodeDeclaration &r_declaration)
@@ -294,12 +345,41 @@ void node_group_declare_dynamic(const bNodeTree & /*node_tree*/,
}
r_declaration.skip_updating_sockets = false;
LISTBASE_FOREACH (const bNodeSocket *, input, &group->inputs) {
r_declaration.inputs.append(declaration_for_interface_socket(*group, *input));
}
LISTBASE_FOREACH (const bNodeSocket *, output, &group->outputs) {
r_declaration.outputs.append(declaration_for_interface_socket(*group, *output));
}
/* Allow the node group interface to define the socket order. */
r_declaration.use_custom_socket_order = true;
group->tree_interface.foreach_item([&](const bNodeTreeInterfaceItem &item) {
switch (item.item_type) {
case NODE_INTERFACE_SOCKET: {
const bNodeTreeInterfaceSocket &socket =
node_interface::get_item_as<bNodeTreeInterfaceSocket>(item);
if (socket.flag & NODE_INTERFACE_SOCKET_OUTPUT) {
if (SocketDeclarationPtr socket_decl = declaration_for_interface_socket(
*group, socket, SOCK_OUT)) {
r_declaration.outputs.append(socket_decl.get());
r_declaration.items.append(std::move(socket_decl));
}
}
if (socket.flag & NODE_INTERFACE_SOCKET_INPUT) {
if (SocketDeclarationPtr socket_decl = declaration_for_interface_socket(
*group, socket, SOCK_IN)) {
r_declaration.inputs.append(socket_decl.get());
r_declaration.items.append(std::move(socket_decl));
}
}
break;
}
case NODE_INTERFACE_PANEL: {
const bNodeTreeInterfacePanel &panel =
node_interface::get_item_as<bNodeTreeInterfacePanel>(item);
if (PanelDeclarationPtr panel_decl = declaration_for_interface_panel(*group, panel)) {
r_declaration.items.append(std::move(panel_decl));
}
break;
}
}
return true;
});
}
} // namespace blender::nodes
@@ -502,22 +582,50 @@ static void group_input_declare_dynamic(const bNodeTree &node_tree,
const bNode & /*node*/,
NodeDeclaration &r_declaration)
{
LISTBASE_FOREACH (const bNodeSocket *, input, &node_tree.inputs) {
r_declaration.outputs.append(declaration_for_interface_socket(node_tree, *input));
r_declaration.outputs.last()->in_out = SOCK_OUT;
}
r_declaration.outputs.append(decl::create_extend_declaration(SOCK_OUT));
node_tree.tree_interface.foreach_item([&](const bNodeTreeInterfaceItem &item) {
switch (item.item_type) {
case NODE_INTERFACE_SOCKET: {
const bNodeTreeInterfaceSocket &socket =
node_interface::get_item_as<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));
}
break;
}
}
return true;
});
SocketDeclarationPtr extend_decl = decl::create_extend_declaration(SOCK_OUT);
r_declaration.outputs.append(extend_decl.get());
r_declaration.items.append(std::move(extend_decl));
}
static void group_output_declare_dynamic(const bNodeTree &node_tree,
const bNode & /*node*/,
NodeDeclaration &r_declaration)
{
LISTBASE_FOREACH (const bNodeSocket *, input, &node_tree.outputs) {
r_declaration.inputs.append(declaration_for_interface_socket(node_tree, *input));
r_declaration.inputs.last()->in_out = SOCK_IN;
}
r_declaration.inputs.append(decl::create_extend_declaration(SOCK_IN));
node_tree.tree_interface.foreach_item([&](const bNodeTreeInterfaceItem &item) {
switch (item.item_type) {
case NODE_INTERFACE_SOCKET: {
const bNodeTreeInterfaceSocket &socket =
node_interface::get_item_as<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));
}
break;
}
}
return true;
});
SocketDeclarationPtr extend_decl = decl::create_extend_declaration(SOCK_IN);
r_declaration.inputs.append(extend_decl.get());
r_declaration.items.append(std::move(extend_decl));
}
static bool group_input_insert_link(bNodeTree *ntree, bNode *node, bNodeLink *link)
@@ -531,8 +639,8 @@ static bool group_input_insert_link(bNodeTree *ntree, bNode *node, bNodeLink *li
/* Don't connect to other "extend" sockets. */
return false;
}
const bNodeSocket *io_socket = blender::bke::ntreeAddSocketInterfaceFromSocket(
ntree, link->tonode, link->tosock);
const bNodeTreeInterfaceSocket *io_socket = node_interface::add_interface_socket_from_node(
*ntree, *link->tonode, *link->tosock);
if (!io_socket) {
return false;
}
@@ -552,8 +660,8 @@ static bool group_output_insert_link(bNodeTree *ntree, bNode *node, bNodeLink *l
/* Don't connect to other "extend" sockets. */
return false;
}
const bNodeSocket *io_socket = blender::bke::ntreeAddSocketInterfaceFromSocket(
ntree, link->fromnode, link->fromsock);
const bNodeTreeInterfaceSocket *io_socket = node_interface::add_interface_socket_from_node(
*ntree, *link->fromnode, *link->fromsock);
if (!io_socket) {
return false;
}
+63 -18
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()) {
return false;
}
int i;
LISTBASE_FOREACH_INDEX (const bNodeSocket *, socket, &sockets, i) {
const SocketDeclaration &socket_decl = *socket_decls[i];
if (!socket_decl.matches(*socket)) {
return false;
const bNodeSocket *current_input = static_cast<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;
}
current_input = current_input->next;
break;
case SOCK_OUT:
if (current_output == nullptr || !socket_decl->matches(*current_output)) {
return false;
}
current_output = current_output->next;
break;
}
}
return true;
};
if (!check_sockets(node.inputs, inputs)) {
return false;
else if (const PanelDeclaration *panel_decl = dynamic_cast<const PanelDeclaration *>(
item_decl.get()))
{
if (!node.panel_states().contains_ptr(current_panel) || !panel_decl->matches(*current_panel))
{
return false;
}
++current_panel;
}
else {
/* Unknown item type. */
BLI_assert_unreachable();
}
}
if (!check_sockets(node.outputs, outputs)) {
/* If items are left over, some were removed from the declaration. */
if (current_input == nullptr || current_output == nullptr ||
!node.panel_states().contains_ptr(current_panel))
{
return false;
}
return true;
@@ -212,6 +237,26 @@ bool SocketDeclaration::matches_common_data(const bNodeSocket &socket) const
return true;
}
void PanelDeclaration::build(bNodePanelState &panel) const
{
panel = {0};
panel.identifier = this->identifier;
SET_FLAG_FROM_TEST(panel.flag, this->default_collapsed, NODE_PANEL_COLLAPSED);
}
bool PanelDeclaration::matches(const bNodePanelState &panel) const
{
return panel.identifier == this->identifier;
}
void PanelDeclaration::update_or_build(const bNodePanelState &old_panel,
bNodePanelState &new_panel) const
{
build(new_panel);
/* Copy existing state to the new panel */
SET_FLAG_FROM_TEST(new_panel.flag, old_panel.is_collapsed(), NODE_PANEL_COLLAPSED);
}
namespace implicit_field_inputs {
void position(const bNode & /*node*/, void *r_value)
+328 -217
View File
@@ -178,77 +178,167 @@ static void verify_socket_template_list(bNodeTree *ntree,
namespace blender::nodes {
static void refresh_socket_list(bNodeTree &ntree,
static void refresh_node_socket(bNodeTree &ntree,
bNode &node,
ListBase &sockets,
Span<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) {
old_sockets.remove_and_reorder(i);
old_socket_with_same_identifier = &old_socket;
break;
}
/* Try to find a socket that corresponds to the declaration. */
bNodeSocket *old_socket_with_same_identifier = nullptr;
for (const int i : old_sockets.index_range()) {
bNodeSocket &old_socket = *old_sockets[i];
if (old_socket.identifier == socket_decl.identifier) {
old_sockets.remove_and_reorder(i);
old_socket_with_same_identifier = &old_socket;
break;
}
bNodeSocket *new_socket = nullptr;
if (old_socket_with_same_identifier == nullptr) {
/* Create a completely new socket. */
new_socket = &socket_decl->build(ntree, node);
}
bNodeSocket *new_socket = nullptr;
if (old_socket_with_same_identifier == nullptr) {
/* Create a completely new socket. */
new_socket = &socket_decl.build(ntree, node);
}
else {
STRNCPY(old_socket_with_same_identifier->name, socket_decl.name.c_str());
if (socket_decl.matches(*old_socket_with_same_identifier)) {
/* The existing socket matches exactly, just use it. */
new_socket = old_socket_with_same_identifier;
}
else {
STRNCPY(old_socket_with_same_identifier->name, socket_decl->name.c_str());
if (socket_decl->matches(*old_socket_with_same_identifier)) {
/* The existing socket matches exactly, just use it. */
new_socket = old_socket_with_same_identifier;
/* Clear out identifier to avoid name collisions when a new socket is created. */
old_socket_with_same_identifier->identifier[0] = '\0';
new_socket = &socket_decl.update_or_build(ntree, node, *old_socket_with_same_identifier);
if (new_socket == old_socket_with_same_identifier) {
/* The existing socket has been updated, set the correct identifier again. */
STRNCPY(new_socket->identifier, socket_decl.identifier.c_str());
}
else {
/* Clear out identifier to avoid name collisions when a new socket is created. */
old_socket_with_same_identifier->identifier[0] = '\0';
new_socket = &socket_decl->update_or_build(ntree, node, *old_socket_with_same_identifier);
if (new_socket == old_socket_with_same_identifier) {
/* The existing socket has been updated, set the correct identifier again. */
STRNCPY(new_socket->identifier, socket_decl->identifier.c_str());
}
else {
/* Move links to new socket with same identifier. */
LISTBASE_FOREACH (bNodeLink *, link, &ntree.links) {
if (link->fromsock == old_socket_with_same_identifier) {
link->fromsock = new_socket;
}
else if (link->tosock == old_socket_with_same_identifier) {
link->tosock = new_socket;
}
/* Move links to new socket with same identifier. */
LISTBASE_FOREACH (bNodeLink *, link, &ntree.links) {
if (link->fromsock == old_socket_with_same_identifier) {
link->fromsock = new_socket;
}
for (bNodeLink &internal_link : node.runtime->internal_links) {
if (internal_link.fromsock == old_socket_with_same_identifier) {
internal_link.fromsock = new_socket;
}
else if (internal_link.tosock == old_socket_with_same_identifier) {
internal_link.tosock = new_socket;
}
else if (link->tosock == old_socket_with_same_identifier) {
link->tosock = new_socket;
}
}
for (bNodeLink &internal_link : node.runtime->internal_links) {
if (internal_link.fromsock == old_socket_with_same_identifier) {
internal_link.fromsock = new_socket;
}
else if (internal_link.tosock == old_socket_with_same_identifier) {
internal_link.tosock = new_socket;
}
}
}
}
new_sockets.add_new(new_socket);
BKE_ntree_update_tag_socket_new(&ntree, new_socket);
}
LISTBASE_FOREACH_MUTABLE (bNodeSocket *, old_socket, &sockets) {
if (!new_sockets.contains(old_socket)) {
new_sockets.add_new(new_socket);
BKE_ntree_update_tag_socket_new(&ntree, new_socket);
}
static void refresh_node_panel(const PanelDeclaration &panel_decl,
Vector<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.identifier == panel_decl.identifier) {
/* Panel is removed after copying to #new_panel. */
old_panel_with_same_identifier = &old_panel;
break;
}
}
if (old_panel_with_same_identifier == nullptr) {
/* Create a completely new panel. */
panel_decl.build(new_panel);
}
else {
if (panel_decl.matches(*old_panel_with_same_identifier)) {
/* The existing socket matches exactly, just use it. */
new_panel = *old_panel_with_same_identifier;
}
else {
/* Clear out identifier to avoid name collisions when a new panel is created. */
old_panel_with_same_identifier->identifier = -1;
panel_decl.update_or_build(*old_panel_with_same_identifier, new_panel);
}
/* Remove from old panels. */
const int64_t old_panel_index = old_panel_with_same_identifier - old_panels.begin();
old_panels.remove_and_reorder(old_panel_index);
}
}
static void refresh_node_sockets_and_panels(bNodeTree &ntree,
bNode &node,
Span<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;
}
}
Vector<bNodeSocket *> old_inputs = node.inputs;
Vector<bNodeSocket *> old_outputs = node.outputs;
Vector<bNodePanelState> old_panels = Vector<bNodePanelState>(node.panel_states());
/* New panel states buffer. */
MEM_SAFE_FREE(node.panel_states_array);
node.num_panel_states = new_num_panels;
node.panel_states_array = MEM_cnew_array<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;
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 +351,7 @@ static void refresh_node(bNodeTree &ntree,
return;
}
if (!node_decl.matches(node)) {
refresh_socket_list(ntree, node, node.inputs, node_decl.inputs, do_id_user);
refresh_socket_list(ntree, node, node.outputs, node_decl.outputs, do_id_user);
refresh_node_sockets_and_panels(ntree, node, node_decl.items, do_id_user);
}
blender::bke::nodeSocketDeclarationsUpdate(&node);
}
@@ -306,16 +395,13 @@ void node_verify_sockets(bNodeTree *ntree, bNode *node, bool do_id_user)
}
}
void node_socket_init_default_value(bNodeSocket *sock)
void node_socket_init_default_value_data(eNodeSocketDatatype datatype, int subtype, void **data)
{
int type = sock->typeinfo->type;
int subtype = sock->typeinfo->subtype;
if (sock->default_value) {
return; /* already initialized */
if (!data) {
return;
}
switch (type) {
switch (datatype) {
case SOCK_FLOAT: {
bNodeSocketValueFloat *dval = MEM_cnew<bNodeSocketValueFloat>("node socket value float");
dval->subtype = subtype;
@@ -323,7 +409,7 @@ void node_socket_init_default_value(bNodeSocket *sock)
dval->min = -FLT_MAX;
dval->max = FLT_MAX;
sock->default_value = dval;
*data = dval;
break;
}
case SOCK_INT: {
@@ -333,19 +419,19 @@ void node_socket_init_default_value(bNodeSocket *sock)
dval->min = INT_MIN;
dval->max = INT_MAX;
sock->default_value = dval;
*data = dval;
break;
}
case SOCK_BOOLEAN: {
bNodeSocketValueBoolean *dval = MEM_cnew<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 +442,7 @@ void node_socket_init_default_value(bNodeSocket *sock)
dval->min = -FLT_MAX;
dval->max = FLT_MAX;
sock->default_value = dval;
*data = dval;
break;
}
case SOCK_RGBA: {
@@ -364,7 +450,7 @@ void node_socket_init_default_value(bNodeSocket *sock)
bNodeSocketValueRGBA *dval = MEM_cnew<bNodeSocketValueRGBA>("node socket value color");
copy_v4_v4(dval->value, default_value);
sock->default_value = dval;
*data = dval;
break;
}
case SOCK_STRING: {
@@ -372,21 +458,21 @@ void node_socket_init_default_value(bNodeSocket *sock)
dval->subtype = subtype;
dval->value[0] = '\0';
sock->default_value = dval;
*data = dval;
break;
}
case SOCK_OBJECT: {
bNodeSocketValueObject *dval = MEM_cnew<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 +480,7 @@ void node_socket_init_default_value(bNodeSocket *sock)
"node socket value object");
dval->value = nullptr;
sock->default_value = dval;
*data = dval;
break;
}
case SOCK_TEXTURE: {
@@ -402,7 +488,7 @@ void node_socket_init_default_value(bNodeSocket *sock)
"node socket value texture");
dval->value = nullptr;
sock->default_value = dval;
*data = dval;
break;
}
case SOCK_MATERIAL: {
@@ -410,12 +496,120 @@ void node_socket_init_default_value(bNodeSocket *sock)
"node socket value material");
dval->value = nullptr;
sock->default_value = dval;
*data = dval;
break;
}
case SOCK_CUSTOM:
case SOCK_GEOMETRY:
case SOCK_SHADER:
break;
}
}
void node_socket_copy_default_value_data(eNodeSocketDatatype datatype, void *to, const void *from)
{
if (!to || !from) {
return;
}
switch (datatype) {
case SOCK_FLOAT: {
bNodeSocketValueFloat *toval = (bNodeSocketValueFloat *)to;
bNodeSocketValueFloat *fromval = (bNodeSocketValueFloat *)from;
*toval = *fromval;
break;
}
case SOCK_INT: {
bNodeSocketValueInt *toval = (bNodeSocketValueInt *)to;
bNodeSocketValueInt *fromval = (bNodeSocketValueInt *)from;
*toval = *fromval;
break;
}
case SOCK_BOOLEAN: {
bNodeSocketValueBoolean *toval = (bNodeSocketValueBoolean *)to;
bNodeSocketValueBoolean *fromval = (bNodeSocketValueBoolean *)from;
*toval = *fromval;
break;
}
case SOCK_VECTOR: {
bNodeSocketValueVector *toval = (bNodeSocketValueVector *)to;
bNodeSocketValueVector *fromval = (bNodeSocketValueVector *)from;
*toval = *fromval;
break;
}
case SOCK_RGBA: {
bNodeSocketValueRGBA *toval = (bNodeSocketValueRGBA *)to;
bNodeSocketValueRGBA *fromval = (bNodeSocketValueRGBA *)from;
*toval = *fromval;
break;
}
case SOCK_ROTATION: {
bNodeSocketValueRotation *toval = (bNodeSocketValueRotation *)to;
bNodeSocketValueRotation *fromval = (bNodeSocketValueRotation *)from;
*toval = *fromval;
break;
}
case SOCK_STRING: {
bNodeSocketValueString *toval = (bNodeSocketValueString *)to;
bNodeSocketValueString *fromval = (bNodeSocketValueString *)from;
*toval = *fromval;
break;
}
case SOCK_OBJECT: {
bNodeSocketValueObject *toval = (bNodeSocketValueObject *)to;
bNodeSocketValueObject *fromval = (bNodeSocketValueObject *)from;
*toval = *fromval;
id_us_plus(&toval->value->id);
break;
}
case SOCK_IMAGE: {
bNodeSocketValueImage *toval = (bNodeSocketValueImage *)to;
bNodeSocketValueImage *fromval = (bNodeSocketValueImage *)from;
*toval = *fromval;
id_us_plus(&toval->value->id);
break;
}
case SOCK_COLLECTION: {
bNodeSocketValueCollection *toval = (bNodeSocketValueCollection *)to;
bNodeSocketValueCollection *fromval = (bNodeSocketValueCollection *)from;
*toval = *fromval;
id_us_plus(&toval->value->id);
break;
}
case SOCK_TEXTURE: {
bNodeSocketValueTexture *toval = (bNodeSocketValueTexture *)to;
bNodeSocketValueTexture *fromval = (bNodeSocketValueTexture *)from;
*toval = *fromval;
id_us_plus(&toval->value->id);
break;
}
case SOCK_MATERIAL: {
bNodeSocketValueMaterial *toval = (bNodeSocketValueMaterial *)to;
bNodeSocketValueMaterial *fromval = (bNodeSocketValueMaterial *)from;
*toval = *fromval;
id_us_plus(&toval->value->id);
break;
}
case SOCK_CUSTOM:
case SOCK_GEOMETRY:
case SOCK_SHADER:
break;
}
}
void node_socket_init_default_value(bNodeSocket *sock)
{
if (sock->default_value) {
return; /* already initialized */
}
node_socket_init_default_value_data(eNodeSocketDatatype(sock->typeinfo->type),
PropertySubType(sock->typeinfo->subtype),
&sock->default_value);
}
void node_socket_copy_default_value(bNodeSocket *to, const bNodeSocket *from)
{
/* sanity check */
@@ -434,114 +628,35 @@ void node_socket_copy_default_value(bNodeSocket *to, const bNodeSocket *from)
STRNCPY(to->name, from->label);
}
switch (from->typeinfo->type) {
case SOCK_FLOAT: {
bNodeSocketValueFloat *toval = (bNodeSocketValueFloat *)to->default_value;
bNodeSocketValueFloat *fromval = (bNodeSocketValueFloat *)from->default_value;
*toval = *fromval;
break;
}
case SOCK_INT: {
bNodeSocketValueInt *toval = (bNodeSocketValueInt *)to->default_value;
bNodeSocketValueInt *fromval = (bNodeSocketValueInt *)from->default_value;
*toval = *fromval;
break;
}
case SOCK_BOOLEAN: {
bNodeSocketValueBoolean *toval = (bNodeSocketValueBoolean *)to->default_value;
bNodeSocketValueBoolean *fromval = (bNodeSocketValueBoolean *)from->default_value;
*toval = *fromval;
break;
}
case SOCK_VECTOR: {
bNodeSocketValueVector *toval = (bNodeSocketValueVector *)to->default_value;
bNodeSocketValueVector *fromval = (bNodeSocketValueVector *)from->default_value;
*toval = *fromval;
break;
}
case SOCK_RGBA: {
bNodeSocketValueRGBA *toval = (bNodeSocketValueRGBA *)to->default_value;
bNodeSocketValueRGBA *fromval = (bNodeSocketValueRGBA *)from->default_value;
*toval = *fromval;
break;
}
case SOCK_ROTATION: {
bNodeSocketValueRotation *toval = (bNodeSocketValueRotation *)to->default_value;
bNodeSocketValueRotation *fromval = (bNodeSocketValueRotation *)from->default_value;
*toval = *fromval;
break;
}
case SOCK_STRING: {
bNodeSocketValueString *toval = (bNodeSocketValueString *)to->default_value;
bNodeSocketValueString *fromval = (bNodeSocketValueString *)from->default_value;
*toval = *fromval;
break;
}
case SOCK_OBJECT: {
bNodeSocketValueObject *toval = (bNodeSocketValueObject *)to->default_value;
bNodeSocketValueObject *fromval = (bNodeSocketValueObject *)from->default_value;
*toval = *fromval;
id_us_plus(&toval->value->id);
break;
}
case SOCK_IMAGE: {
bNodeSocketValueImage *toval = (bNodeSocketValueImage *)to->default_value;
bNodeSocketValueImage *fromval = (bNodeSocketValueImage *)from->default_value;
*toval = *fromval;
id_us_plus(&toval->value->id);
break;
}
case SOCK_COLLECTION: {
bNodeSocketValueCollection *toval = (bNodeSocketValueCollection *)to->default_value;
bNodeSocketValueCollection *fromval = (bNodeSocketValueCollection *)from->default_value;
*toval = *fromval;
id_us_plus(&toval->value->id);
break;
}
case SOCK_TEXTURE: {
bNodeSocketValueTexture *toval = (bNodeSocketValueTexture *)to->default_value;
bNodeSocketValueTexture *fromval = (bNodeSocketValueTexture *)from->default_value;
*toval = *fromval;
id_us_plus(&toval->value->id);
break;
}
case SOCK_MATERIAL: {
bNodeSocketValueMaterial *toval = (bNodeSocketValueMaterial *)to->default_value;
bNodeSocketValueMaterial *fromval = (bNodeSocketValueMaterial *)from->default_value;
*toval = *fromval;
id_us_plus(&toval->value->id);
break;
}
}
node_socket_copy_default_value_data(
eNodeSocketDatatype(to->typeinfo->type), to->default_value, from->default_value);
to->flag |= (from->flag & SOCK_HIDE_VALUE);
}
static void standard_node_socket_interface_init_socket(bNodeTree * /*ntree*/,
const bNodeSocket *interface_socket,
bNode * /*node*/,
bNodeSocket *sock,
const char * /*data_path*/)
static void standard_node_socket_interface_init_socket(
ID * /*id*/,
const bNodeTreeInterfaceSocket *interface_socket,
bNode * /*node*/,
bNodeSocket *sock,
const char * /*data_path*/)
{
/* initialize the type value */
sock->type = sock->typeinfo->type;
/* XXX socket interface 'type' value is not used really,
* but has to match or the copy function will bail out
*/
const_cast<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 +664,7 @@ void ED_init_standard_node_socket_type(bNodeSocketType *);
static bNodeSocketType *make_standard_socket_type(int type, int subtype)
{
const char *socket_idname = nodeStaticSocketType(type, subtype);
const char *interface_idname = nodeStaticSocketInterfaceType(type, subtype);
const char *interface_idname = nodeStaticSocketInterfaceTypeNew(type, subtype);
const char *socket_label = nodeStaticSocketLabel(type, subtype);
const char *socket_subtype_label = blender::bke::nodeSocketSubTypeLabel(subtype);
bNodeSocketType *stype;
@@ -626,13 +741,12 @@ static bNodeSocketType *make_socket_type_bool()
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_BOOLEAN, PROP_NONE);
socktype->base_cpp_type = &blender::CPPType::get<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 +756,16 @@ static bNodeSocketType *make_socket_type_rotation()
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_ROTATION, PROP_NONE);
socktype->base_cpp_type = &blender::CPPType::get<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 +775,12 @@ static bNodeSocketType *make_socket_type_float(PropertySubType subtype)
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_FLOAT, subtype);
socktype->base_cpp_type = &blender::CPPType::get<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 +790,12 @@ static bNodeSocketType *make_socket_type_int(PropertySubType subtype)
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_INT, subtype);
socktype->base_cpp_type = &blender::CPPType::get<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 +805,12 @@ static bNodeSocketType *make_socket_type_vector(PropertySubType subtype)
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_VECTOR, subtype);
socktype->base_cpp_type = &blender::CPPType::get<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 +820,13 @@ static bNodeSocketType *make_socket_type_rgba()
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_RGBA, PROP_NONE);
socktype->base_cpp_type = &blender::CPPType::get<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 +836,14 @@ static bNodeSocketType *make_socket_type_string()
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_STRING, PROP_NONE);
socktype->base_cpp_type = &blender::CPPType::get<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 +853,8 @@ static bNodeSocketType *make_socket_type_object()
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_OBJECT, PROP_NONE);
socktype->base_cpp_type = &blender::CPPType::get<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 +865,7 @@ static bNodeSocketType *make_socket_type_geometry()
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_GEOMETRY, PROP_NONE);
socktype->base_cpp_type = &blender::CPPType::get<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 +877,8 @@ static bNodeSocketType *make_socket_type_collection()
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_COLLECTION, PROP_NONE);
socktype->base_cpp_type = &blender::CPPType::get<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 +889,8 @@ static bNodeSocketType *make_socket_type_texture()
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_TEXTURE, PROP_NONE);
socktype->base_cpp_type = &blender::CPPType::get<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 +901,8 @@ static bNodeSocketType *make_socket_type_image()
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_IMAGE, PROP_NONE);
socktype->base_cpp_type = &blender::CPPType::get<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 +913,8 @@ static bNodeSocketType *make_socket_type_material()
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_MATERIAL, PROP_NONE);
socktype->base_cpp_type = &blender::CPPType::get<Material *>();
socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) {
*(Material **)r_value = ((bNodeSocketValueMaterial *)socket.default_value)->value;
socktype->get_base_cpp_value = [](const void *socket_value, void *r_value) {
*(Material **)r_value = ((bNodeSocketValueMaterial *)socket_value)->value;
};
socktype->geometry_nodes_cpp_type = socktype->base_cpp_type;
socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value;
@@ -680,6 +680,9 @@ bNodeSocket &Custom::build(bNodeTree &ntree, bNode &node) const
{
bNodeSocket &socket = *nodeAddSocket(
&ntree, &node, this->in_out, idname_, this->identifier.c_str(), this->name.c_str());
if (this->init_socket_fn) {
this->init_socket_fn(node, socket, "interface");
}
return socket;
}
@@ -94,7 +94,7 @@ void LinkSearchOpParams::update_and_connect_available_socket(bNode &new_node,
}
void search_link_ops_for_declarations(GatherLinkSearchOpParams &params,
Span<SocketDeclarationPtr> declarations)
Span<SocketDeclaration *> declarations)
{
const bNodeType &node_type = params.node_type();
+5
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
+9
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
+501
View File
@@ -0,0 +1,501 @@
# SPDX-FileCopyrightText: 2021-2023 Blender Foundation
#
# SPDX-License-Identifier: GPL-2.0-or-later
import pathlib
import sys
import unittest
import tempfile
import bpy
args = None
class AbstractNodeGroupInterfaceTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.testdir = args.testdir
cls._tempdir = tempfile.TemporaryDirectory()
cls.tempdir = pathlib.Path(cls._tempdir.name)
def setUp(self):
self.assertTrue(self.testdir.exists(),
'Test dir {0} should exist'.format(self.testdir))
# Make sure we always start with a known-empty file.
bpy.ops.wm.open_mainfile(filepath=str(self.testdir / "empty.blend"))
def tearDown(self):
self._tempdir.cleanup()
class NodeGroupInterfaceTests:
tree_type = None
group_node_type = None
# Tree instance where node groups can be added
main_tree = None
def make_group(self):
tree = bpy.data.node_groups.new("test", self.tree_type)
return tree
def make_instance(self, tree):
group_node = self.main_tree.nodes.new(self.group_node_type)
group_node.node_tree = tree
return group_node
def make_group_and_instance(self):
tree = self.make_group()
group_node = self.make_instance(tree)
return tree, group_node
# Utility method for generating a non-zero default value.
@staticmethod
def make_default_socket_value(socket_type):
if (socket_type == "NodeSocketBool"):
return True
elif (socket_type == "NodeSocketColor"):
return (.5, 1.0, .3, .7)
elif (socket_type == "NodeSocketFloat"):
return 1.23
elif (socket_type == "NodeSocketImage"):
return bpy.data.images.new("test", 4, 4)
elif (socket_type == "NodeSocketInt"):
return -6
elif (socket_type == "NodeSocketMaterial"):
return bpy.data.materials.new("test")
elif (socket_type == "NodeSocketObject"):
return bpy.data.objects.new("test", bpy.data.meshes.new("test"))
elif (socket_type == "NodeSocketRotation"):
return (0.3, 5.0, -42)
elif (socket_type == "NodeSocketString"):
return "Hello World!"
elif (socket_type == "NodeSocketTexture"):
return bpy.data.textures.new("test", 'MAGIC')
elif (socket_type == "NodeSocketVector"):
return (4.0, -1.0, 0.0)
# Utility method returning a comparator for socket values.
# Not all socket value types are trivially comparable, e.g. colors.
@staticmethod
def make_socket_value_comparator(socket_type):
def cmp_default(test, value, expected):
test.assertEqual(value, expected, f"Value {value} does not match expected value {expected}")
def cmp_array(test, value, expected):
test.assertSequenceEqual(value[:], expected[:], f"Value {value} does not match expected value {expected}")
if (socket_type in {"NodeSocketBool",
"NodeSocketFloat",
"NodeSocketImage",
"NodeSocketInt",
"NodeSocketMaterial",
"NodeSocketObject",
"NodeSocketRotation",
"NodeSocketString",
"NodeSocketTexture"}):
return cmp_default
elif (socket_type in {"NodeSocketColor",
"NodeSocketVector"}):
return cmp_array
def test_empty_nodegroup(self):
tree, group_node = self.make_group_and_instance()
self.assertFalse(tree.interface.ui_items, "Interface not empty")
self.assertFalse(group_node.inputs)
self.assertFalse(group_node.outputs)
def do_test_invalid_socket_type(self, socket_type):
tree = self.make_group()
with self.assertRaises(TypeError):
in0 = tree.interface.new_socket("Input 0", socket_type=socket_type, in_out={'INPUT'})
self.assertIsNone(in0, f"Socket created for invalid type {socket_type}")
with self.assertRaises(TypeError):
out0 = tree.interface.new_socket("Output 0", socket_type=socket_type, in_out={'OUTPUT'})
self.assertIsNone(out0, f"Socket created for invalid type {socket_type}")
def do_test_sockets_in_out(self, socket_type):
tree, group_node = self.make_group_and_instance()
out0 = tree.interface.new_socket("Output 0", socket_type=socket_type, in_out={'OUTPUT'})
self.assertIsNotNone(out0, f"Could not create socket of type {socket_type}")
in0 = tree.interface.new_socket("Input 0", socket_type=socket_type, in_out={'INPUT'})
self.assertIsNotNone(in0, f"Could not create socket of type {socket_type}")
in1 = tree.interface.new_socket("Input 1", socket_type=socket_type, in_out={'INPUT'})
self.assertIsNotNone(in1, f"Could not create socket of type {socket_type}")
out1 = tree.interface.new_socket("Output 1", socket_type=socket_type, in_out={'OUTPUT'})
self.assertIsNotNone(out1, f"Could not create socket of type {socket_type}")
inout0 = tree.interface.new_socket("Input/Output 0", socket_type=socket_type, in_out={'OUTPUT', 'INPUT'})
self.assertIsNotNone(inout0, f"Could not create socket of type {socket_type}")
self.assertSequenceEqual([(s.name, s.bl_idname) for s in group_node.inputs], [
("Input 0", socket_type),
("Input 1", socket_type),
("Input/Output 0", socket_type),
])
self.assertSequenceEqual([(s.name, s.bl_idname) for s in group_node.outputs], [
("Output 0", socket_type),
("Output 1", socket_type),
("Input/Output 0", socket_type),
])
def do_test_user_count(self, value, expected_users):
if (isinstance(value, bpy.types.ID)):
self.assertEqual(
value.users,
expected_users,
f"Socket default value has user count {value.users}, expected {expected_users}")
def do_test_socket_type(self, socket_type):
default_value = self.make_default_socket_value(socket_type)
compare_value = self.make_socket_value_comparator(socket_type)
# Create the tree first, add sockets, then create a group instance.
# That way the new instance should reflect the expected default values.
tree = self.make_group()
in0 = tree.interface.new_socket("Input 0", socket_type=socket_type, in_out={'INPUT'})
if default_value is not None:
in0.default_value = default_value
out0 = tree.interface.new_socket("Output 0", socket_type=socket_type, in_out={'OUTPUT'})
self.assertIsNotNone(in0, f"Could not create socket of type {socket_type}")
self.assertIsNotNone(out0, f"Could not create socket of type {socket_type}")
# Now make a node group instance to check default values.
group_node = self.make_instance(tree)
if compare_value:
compare_value(self, group_node.inputs[0].default_value, in0.default_value)
# Test ID user count after assigning.
if (hasattr(in0, "default_value")):
# The default value is stored in both the interface and node, it should have 2 users now.
self.do_test_user_count(in0.default_value, 2)
# Copy sockets
in1 = tree.interface.copy(in0)
out1 = tree.interface.copy(out0)
self.assertIsNotNone(in1, "Could not copy socket")
self.assertIsNotNone(out1, "Could not copy socket")
# User count on default values should increment by 2 after copy,
# one user for the interface and one for the group node instance.
if (hasattr(in1, "default_value")):
self.do_test_user_count(in1.default_value, 4)
# Classic outputs..inputs socket layout
def do_test_items_order_classic(self, socket_type):
tree, group_node = self.make_group_and_instance()
tree.interface.new_socket("Output 0", socket_type=socket_type, in_out={'OUTPUT'})
tree.interface.new_socket("Input 0", socket_type=socket_type, in_out={'INPUT'})
self.assertSequenceEqual([(s.name, s.item_type) for s in tree.interface.ui_items], [
("Output 0", 'SOCKET'),
("Input 0", 'SOCKET'),
])
self.assertSequenceEqual([s.name for s in group_node.inputs], [
"Input 0",
])
self.assertSequenceEqual([s.name for s in group_node.outputs], [
"Output 0",
])
# XXX currently no panel state access on node instances.
# self.assertFalse(group_node.panels)
# Mixed sockets and panels
def do_test_items_order_mixed_with_panels(self, socket_type):
tree, group_node = self.make_group_and_instance()
tree.interface.new_panel("Panel 0")
tree.interface.new_socket("Input 0", socket_type=socket_type, in_out={'INPUT'})
tree.interface.new_socket("Output 0", socket_type=socket_type, in_out={'OUTPUT'})
tree.interface.new_panel("Panel 1")
tree.interface.new_socket("Input 1", socket_type=socket_type, in_out={'INPUT'})
tree.interface.new_panel("Panel 2")
tree.interface.new_socket("Output 1", socket_type=socket_type, in_out={'OUTPUT'})
tree.interface.new_panel("Panel 3")
# Panels after sockets
self.assertSequenceEqual([(s.name, s.item_type) for s in tree.interface.ui_items], [
("Input 0", 'SOCKET'),
("Output 0", 'SOCKET'),
("Input 1", 'SOCKET'),
("Output 1", 'SOCKET'),
("Panel 0", 'PANEL'),
("Panel 1", 'PANEL'),
("Panel 2", 'PANEL'),
("Panel 3", 'PANEL'),
])
self.assertSequenceEqual([s.name for s in group_node.inputs], [
"Input 0",
"Input 1",
])
self.assertSequenceEqual([s.name for s in group_node.outputs], [
"Output 0",
"Output 1",
])
# XXX currently no panel state access on node instances.
# self.assertSequenceEqual([p.name for p in group_node.panels], [
# "Panel 0",
# "Panel 1",
# "Panel 2",
# "Panel 3",
# ])
def do_test_add(self, socket_type):
tree, group_node = self.make_group_and_instance()
in0 = tree.interface.new_socket("Input 0", socket_type=socket_type, in_out={'INPUT'})
self.assertSequenceEqual(tree.interface.ui_items, [in0])
self.assertSequenceEqual([s.name for s in group_node.inputs], ["Input 0"])
self.assertSequenceEqual([s.name for s in group_node.outputs], [])
out0 = tree.interface.new_socket("Output 0", socket_type=socket_type, in_out={'OUTPUT'})
self.assertSequenceEqual(tree.interface.ui_items, [in0, out0])
self.assertSequenceEqual([s.name for s in group_node.inputs], ["Input 0"])
self.assertSequenceEqual([s.name for s in group_node.outputs], ["Output 0"])
panel0 = tree.interface.new_panel("Panel 0")
self.assertSequenceEqual(tree.interface.ui_items, [in0, out0, panel0])
self.assertSequenceEqual([s.name for s in group_node.inputs], ["Input 0"])
self.assertSequenceEqual([s.name for s in group_node.outputs], ["Output 0"])
# Add items to the panel.
in1 = tree.interface.new_socket("Input 1", socket_type=socket_type, in_out={'INPUT'}, parent=panel0)
self.assertSequenceEqual(tree.interface.ui_items, [in0, out0, panel0, in1])
self.assertSequenceEqual([s.name for s in group_node.inputs], ["Input 0", "Input 1"])
self.assertSequenceEqual([s.name for s in group_node.outputs], ["Output 0"])
out1 = tree.interface.new_socket("Output 1", socket_type=socket_type, in_out={'OUTPUT'}, parent=panel0)
self.assertSequenceEqual(tree.interface.ui_items, [in0, out0, panel0, in1, out1])
self.assertSequenceEqual([s.name for s in group_node.inputs], ["Input 0", "Input 1"])
self.assertSequenceEqual([s.name for s in group_node.outputs], ["Output 0", "Output 1"])
# Nested panel is not allowed and should return None.
panel1 = tree.interface.new_panel("Panel 1", parent=panel0)
self.assertIsNone(panel1)
self.assertSequenceEqual(tree.interface.ui_items, [in0, out0, panel0, in1, out1])
self.assertSequenceEqual([s.name for s in group_node.inputs], ["Input 0", "Input 1"])
self.assertSequenceEqual([s.name for s in group_node.outputs], ["Output 0", "Output 1"])
def do_test_remove(self, socket_type):
tree, group_node = self.make_group_and_instance()
in0 = tree.interface.new_socket("Input 0", socket_type=socket_type, in_out={'INPUT'})
out0 = tree.interface.new_socket("Output 0", socket_type=socket_type, in_out={'OUTPUT'})
panel0 = tree.interface.new_panel("Panel 0")
in1 = tree.interface.new_socket("Input 1", socket_type=socket_type, in_out={'INPUT'}, parent=panel0)
out1 = tree.interface.new_socket("Output 1", socket_type=socket_type, in_out={'OUTPUT'}, parent=panel0)
panel1 = tree.interface.new_panel("Panel 1")
in2 = tree.interface.new_socket("Input 2", socket_type=socket_type, in_out={'INPUT'}, parent=panel1)
out2 = tree.interface.new_socket("Output 2", socket_type=socket_type, in_out={'OUTPUT'}, parent=panel1)
panel2 = tree.interface.new_panel("Panel 2")
self.assertSequenceEqual(tree.interface.ui_items, [in0, out0, panel0, in1, out1, panel1, in2, out2, panel2])
self.assertSequenceEqual([s.name for s in group_node.inputs], ["Input 0", "Input 1", "Input 2"])
self.assertSequenceEqual([s.name for s in group_node.outputs], ["Output 0", "Output 1", "Output 2"])
# Remove from root panel.
tree.interface.remove(in0)
self.assertSequenceEqual(tree.interface.ui_items, [out0, panel0, in1, out1, panel1, in2, out2, panel2])
self.assertSequenceEqual([s.name for s in group_node.inputs], ["Input 1", "Input 2"])
self.assertSequenceEqual([s.name for s in group_node.outputs], ["Output 0", "Output 1", "Output 2"])
# Removing a panel should move content to the parent.
tree.interface.remove(panel0)
self.assertSequenceEqual(tree.interface.ui_items, [out0, in1, out1, panel1, in2, out2, panel2])
self.assertSequenceEqual([s.name for s in group_node.inputs], ["Input 1", "Input 2"])
self.assertSequenceEqual([s.name for s in group_node.outputs], ["Output 0", "Output 1", "Output 2"])
tree.interface.remove(out0)
self.assertSequenceEqual(tree.interface.ui_items, [in1, out1, panel1, in2, out2, panel2])
self.assertSequenceEqual([s.name for s in group_node.inputs], ["Input 1", "Input 2"])
self.assertSequenceEqual([s.name for s in group_node.outputs], ["Output 1", "Output 2"])
# Remove content from panel
tree.interface.remove(out2)
self.assertSequenceEqual(tree.interface.ui_items, [in1, out1, panel1, in2, panel2])
self.assertSequenceEqual([s.name for s in group_node.inputs], ["Input 1", "Input 2"])
self.assertSequenceEqual([s.name for s in group_node.outputs], ["Output 1"])
# Remove a panel and its content
tree.interface.remove(panel1, move_content_to_parent=False)
self.assertSequenceEqual(tree.interface.ui_items, [in1, out1, panel2])
self.assertSequenceEqual([s.name for s in group_node.inputs], ["Input 1"])
self.assertSequenceEqual([s.name for s in group_node.outputs], ["Output 1"])
# Remove empty panel
tree.interface.remove(panel2)
self.assertSequenceEqual(tree.interface.ui_items, [in1, out1])
self.assertSequenceEqual([s.name for s in group_node.inputs], ["Input 1"])
self.assertSequenceEqual([s.name for s in group_node.outputs], ["Output 1"])
def do_test_move(self, socket_type):
tree, group_node = self.make_group_and_instance()
in0 = tree.interface.new_socket("Input 0", socket_type=socket_type, in_out={'INPUT'})
in1 = tree.interface.new_socket("Input 1", socket_type=socket_type, in_out={'INPUT'}, parent=panel0)
out0 = tree.interface.new_socket("Output 0", socket_type=socket_type, in_out={'OUTPUT'})
out1 = tree.interface.new_socket("Output 1", socket_type=socket_type, in_out={'OUTPUT'}, parent=panel0)
panel0 = tree.interface.new_panel("Panel 0")
panel1 = tree.interface.new_panel("Panel 1")
class GeometryNodeGroupInterfaceTest(AbstractNodeGroupInterfaceTest, NodeGroupInterfaceTests):
tree_type = "GeometryNodeTree"
group_node_type = "GeometryNodeGroup"
def setUp(self):
super().setUp()
self.main_tree = bpy.data.node_groups.new("main", self.tree_type)
def test_sockets_in_out(self):
self.do_test_sockets_in_out("NodeSocketFloat")
def test_all_socket_types(self):
self.do_test_invalid_socket_type("INVALID_SOCKET_TYPE_11!1")
self.do_test_socket_type("NodeSocketBool")
self.do_test_socket_type("NodeSocketCollection")
self.do_test_socket_type("NodeSocketColor")
self.do_test_socket_type("NodeSocketFloat")
self.do_test_socket_type("NodeSocketGeometry")
self.do_test_socket_type("NodeSocketImage")
self.do_test_socket_type("NodeSocketInt")
self.do_test_socket_type("NodeSocketMaterial")
self.do_test_socket_type("NodeSocketObject")
self.do_test_socket_type("NodeSocketRotation")
self.do_test_invalid_socket_type("NodeSocketShader")
self.do_test_socket_type("NodeSocketString")
self.do_test_socket_type("NodeSocketTexture")
self.do_test_socket_type("NodeSocketVector")
self.do_test_invalid_socket_type("NodeSocketVirtual")
def test_items_order_classic(self):
self.do_test_items_order_classic("NodeSocketFloat")
def test_items_order_mixed_with_panels(self):
self.do_test_items_order_mixed_with_panels("NodeSocketFloat")
def test_add(self):
self.do_test_add("NodeSocketFloat")
def test_remove(self):
self.do_test_remove("NodeSocketFloat")
class ShaderNodeGroupInterfaceTest(AbstractNodeGroupInterfaceTest, NodeGroupInterfaceTests):
tree_type = "ShaderNodeTree"
group_node_type = "ShaderNodeGroup"
def setUp(self):
super().setUp()
self.material = bpy.data.materials.new("test")
self.material.use_nodes = True
self.main_tree = self.material.node_tree
def test_invalid_socket_type(self):
self.do_test_invalid_socket_type("INVALID_SOCKET_TYPE_11!1")
def test_sockets_in_out(self):
self.do_test_sockets_in_out("NodeSocketFloat")
def test_all_socket_types(self):
self.do_test_invalid_socket_type("NodeSocketBool")
self.do_test_invalid_socket_type("NodeSocketCollection")
self.do_test_socket_type("NodeSocketColor")
self.do_test_socket_type("NodeSocketFloat")
self.do_test_invalid_socket_type("NodeSocketGeometry")
self.do_test_invalid_socket_type("NodeSocketImage")
self.do_test_invalid_socket_type("NodeSocketInt")
self.do_test_invalid_socket_type("NodeSocketMaterial")
self.do_test_invalid_socket_type("NodeSocketObject")
self.do_test_invalid_socket_type("NodeSocketRotation")
self.do_test_socket_type("NodeSocketShader")
self.do_test_invalid_socket_type("NodeSocketString")
self.do_test_invalid_socket_type("NodeSocketTexture")
self.do_test_socket_type("NodeSocketVector")
self.do_test_invalid_socket_type("NodeSocketVirtual")
def test_items_order_classic(self):
self.do_test_items_order_classic("NodeSocketFloat")
def test_items_order_mixed_with_panels(self):
self.do_test_items_order_mixed_with_panels("NodeSocketFloat")
def test_add(self):
self.do_test_add("NodeSocketFloat")
def test_remove(self):
self.do_test_remove("NodeSocketFloat")
class CompositorNodeGroupInterfaceTest(AbstractNodeGroupInterfaceTest, NodeGroupInterfaceTests):
tree_type = "CompositorNodeTree"
group_node_type = "CompositorNodeGroup"
def setUp(self):
super().setUp()
self.scene = bpy.data.scenes.new("test")
self.scene.use_nodes = True
self.main_tree = self.scene.node_tree
def test_invalid_socket_type(self):
self.do_test_invalid_socket_type("INVALID_SOCKET_TYPE_11!1")
def test_sockets_in_out(self):
self.do_test_sockets_in_out("NodeSocketFloat")
def test_all_socket_types(self):
self.do_test_invalid_socket_type("NodeSocketBool")
self.do_test_invalid_socket_type("NodeSocketCollection")
self.do_test_socket_type("NodeSocketColor")
self.do_test_socket_type("NodeSocketFloat")
self.do_test_invalid_socket_type("NodeSocketGeometry")
self.do_test_invalid_socket_type("NodeSocketImage")
self.do_test_invalid_socket_type("NodeSocketInt")
self.do_test_invalid_socket_type("NodeSocketMaterial")
self.do_test_invalid_socket_type("NodeSocketObject")
self.do_test_invalid_socket_type("NodeSocketRotation")
self.do_test_invalid_socket_type("NodeSocketShader")
self.do_test_invalid_socket_type("NodeSocketString")
self.do_test_invalid_socket_type("NodeSocketTexture")
self.do_test_socket_type("NodeSocketVector")
self.do_test_invalid_socket_type("NodeSocketVirtual")
def test_items_order_classic(self):
self.do_test_items_order_classic("NodeSocketFloat")
def test_items_order_mixed_with_panels(self):
self.do_test_items_order_mixed_with_panels("NodeSocketFloat")
def test_add(self):
self.do_test_add("NodeSocketFloat")
def test_remove(self):
self.do_test_remove("NodeSocketFloat")
def main():
global args
import argparse
if '--' in sys.argv:
argv = [sys.argv[0]] + sys.argv[sys.argv.index('--') + 1:]
else:
argv = sys.argv
parser = argparse.ArgumentParser()
parser.add_argument('--testdir', required=True, type=pathlib.Path)
args, remaining = parser.parse_known_args(argv)
unittest.main(argv=remaining)
if __name__ == "__main__":
main()