Add initial version of Node Sections to keep sockets more manageable
Nodes can now have sections, and each socket can belong to a section. These only affect how the node is displayed. In the Node Editor, the node first shows all sockets without a section as usual, and following that the sections are shown. Each section can be expanded or collapsed, hiding the sockets inside. Node links are still drawn if the socket they're going to is invisible - in that case, they'll be going to the section header instead. However, dragging them or connecting a link to a hidden socket via dragging is not possible. Node Groups can also have sections defined by the user, which are reflected in their group input/output and in the group nodes that use them. Note that the code for hidden nodes is still missing, I'm not sure yet how to handle this there. Also, we might eventually support drawing node properties (such as SSS type for the principled BSDF) inside a section. Similarly, especially for custom groups it might be nice to support nested sections.
This commit is contained in:
@@ -813,6 +813,18 @@ class NODE_UL_interface_sockets(bpy.types.UIList):
|
||||
layout.template_node_socket(color=color)
|
||||
|
||||
|
||||
class NODE_UL_interface_sections(bpy.types.UIList):
|
||||
def draw_item(self, context, layout, _data, item, icon, _active_data, _active_propname, _index):
|
||||
socket = item
|
||||
|
||||
if self.layout_type in {'DEFAULT', 'COMPACT'}:
|
||||
row = layout.row(align=True)
|
||||
|
||||
row.prop(item, "name", text="", emboss=False, icon_value=icon)
|
||||
elif self.layout_type == 'GRID':
|
||||
layout.alignment = 'CENTER'
|
||||
|
||||
|
||||
class NodeTreeInterfacePanel(Panel):
|
||||
|
||||
@classmethod
|
||||
@@ -881,6 +893,7 @@ class NodeTreeInterfacePanel(Panel):
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
|
||||
layout.prop_search(active_socket, "section", tree, "sections")
|
||||
layout.prop(active_socket, "name")
|
||||
# Display descriptions only for Geometry Nodes, since it's only used in the modifier panel.
|
||||
if tree.type == 'GEOMETRY':
|
||||
@@ -923,6 +936,37 @@ class NODE_PT_node_tree_interface_outputs(NodeTreeInterfacePanel):
|
||||
self.draw_socket_list(context, "OUT", "outputs", "active_output")
|
||||
|
||||
|
||||
class NODE_PT_node_tree_interface_sections(NodeTreeInterfacePanel):
|
||||
bl_space_type = 'NODE_EDITOR'
|
||||
bl_region_type = 'UI'
|
||||
bl_category = "Group"
|
||||
bl_label = "Sections"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
snode = context.space_data
|
||||
tree = snode.edit_tree
|
||||
|
||||
split = layout.row()
|
||||
|
||||
split.template_list("NODE_UL_interface_sections", "", tree, "sections", tree, "active_section")
|
||||
|
||||
ops_col = split.column()
|
||||
|
||||
add_remove_col = ops_col.column(align=True)
|
||||
props = add_remove_col.operator("node.tree_section_add", icon='ADD', text="")
|
||||
props = add_remove_col.operator("node.tree_section_remove", icon='REMOVE', text="")
|
||||
|
||||
ops_col.separator()
|
||||
|
||||
up_down_col = ops_col.column(align=True)
|
||||
props = up_down_col.operator("node.tree_section_move", icon='TRIA_UP', text="")
|
||||
props.direction = 'UP'
|
||||
props = up_down_col.operator("node.tree_section_move", icon='TRIA_DOWN', text="")
|
||||
props.direction = 'DOWN'
|
||||
|
||||
|
||||
# Grease Pencil properties
|
||||
class NODE_PT_annotation(AnnotationDataPanel, Panel):
|
||||
bl_space_type = 'NODE_EDITOR'
|
||||
@@ -985,8 +1029,10 @@ classes = (
|
||||
NODE_PT_annotation,
|
||||
NODE_PT_overlay,
|
||||
NODE_UL_interface_sockets,
|
||||
NODE_UL_interface_sections,
|
||||
NODE_PT_node_tree_interface_inputs,
|
||||
NODE_PT_node_tree_interface_outputs,
|
||||
NODE_PT_node_tree_interface_sections,
|
||||
|
||||
node_panel(EEVEE_MATERIAL_PT_settings),
|
||||
node_panel(MATERIAL_PT_viewport),
|
||||
|
@@ -580,6 +580,9 @@ struct bNodeSocket *ntreeInsertSocketInterfaceFromSocket(struct bNodeTree *ntree
|
||||
struct bNodeSocket *from_sock);
|
||||
void ntreeRemoveSocketInterface(struct bNodeTree *ntree, struct bNodeSocket *sock);
|
||||
|
||||
struct bNodeSection *ntreeAddSection(struct bNodeTree *ntree, const char *name);
|
||||
void ntreeRemoveSection(struct bNodeTree *ntree, struct bNodeSection *section);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
@@ -662,6 +665,9 @@ void nodeModifySocketType(struct bNodeTree *ntree,
|
||||
void nodeModifySocketTypeStatic(
|
||||
struct bNodeTree *ntree, struct bNode *node, struct bNodeSocket *sock, int type, int subtype);
|
||||
|
||||
struct bNodeSection *nodeAddSection(struct bNode *node, const char *name);
|
||||
void nodeRemoveSection(struct bNode *node, struct bNodeSection *section);
|
||||
|
||||
struct bNode *nodeAddNode(const struct bContext *C, struct bNodeTree *ntree, const char *idname);
|
||||
struct bNode *nodeAddStaticNode(const struct bContext *C, struct bNodeTree *ntree, int type);
|
||||
/**
|
||||
@@ -837,6 +843,8 @@ void nodeSetSocketAvailability(struct bNodeTree *ntree,
|
||||
struct bNodeSocket *sock,
|
||||
bool is_available);
|
||||
|
||||
void nodeSetSectionAvailability(struct bNodeSection *section, bool is_available);
|
||||
|
||||
int nodeSocketLinkLimit(const struct bNodeSocket *sock);
|
||||
|
||||
/**
|
||||
|
@@ -113,7 +113,10 @@ bNodeSocketType NodeSocketTypeUndefined;
|
||||
static CLG_LogRef LOG = {"bke.node"};
|
||||
|
||||
static void ntree_set_typeinfo(bNodeTree *ntree, bNodeTreeType *typeinfo);
|
||||
static void node_socket_copy(bNodeSocket *sock_dst, const bNodeSocket *sock_src, const int flag);
|
||||
static void node_socket_copy(bNodeSocket *sock_dst,
|
||||
const bNodeSocket *sock_src,
|
||||
const int flag,
|
||||
const Map<const bNodeSection *, bNodeSection *> §ion_map);
|
||||
static void free_localized_node_groups(bNodeTree *ntree);
|
||||
static void node_free_node(bNodeTree *ntree, bNode *node);
|
||||
static void node_socket_interface_free(bNodeTree * /*ntree*/,
|
||||
@@ -167,17 +170,27 @@ static void ntree_copy_data(Main * /*bmain*/, ID *id_dst, const ID *id_src, cons
|
||||
BLI_addtail(&ntree_dst->links, dst_link);
|
||||
}
|
||||
|
||||
/* copy interface sections */
|
||||
Map<const bNodeSection *, bNodeSection *> section_map;
|
||||
section_map.add_new(nullptr, nullptr);
|
||||
BLI_listbase_clear(&ntree_dst->sections);
|
||||
LISTBASE_FOREACH (const bNodeSection *, src_section, &ntree_src->sections) {
|
||||
bNodeSection *dst_section = (bNodeSection *)MEM_dupallocN(src_section);
|
||||
BLI_addtail(&ntree_dst->sections, dst_section);
|
||||
section_map.add_new(src_section, dst_section);
|
||||
}
|
||||
|
||||
/* copy interface sockets */
|
||||
BLI_listbase_clear(&ntree_dst->inputs);
|
||||
LISTBASE_FOREACH (const bNodeSocket *, src_socket, &ntree_src->inputs) {
|
||||
bNodeSocket *dst_socket = (bNodeSocket *)MEM_dupallocN(src_socket);
|
||||
node_socket_copy(dst_socket, src_socket, flag_subdata);
|
||||
node_socket_copy(dst_socket, src_socket, flag_subdata, section_map);
|
||||
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 = (bNodeSocket *)MEM_dupallocN(src_socket);
|
||||
node_socket_copy(dst_socket, src_socket, flag_subdata);
|
||||
node_socket_copy(dst_socket, src_socket, flag_subdata, section_map);
|
||||
BLI_addtail(&ntree_dst->outputs, dst_socket);
|
||||
}
|
||||
|
||||
@@ -256,6 +269,8 @@ static void ntree_free_data(ID *id)
|
||||
MEM_freeN(sock);
|
||||
}
|
||||
|
||||
BLI_freelistN(&ntree->sections);
|
||||
|
||||
/* free preview hash */
|
||||
if (ntree->previews) {
|
||||
BKE_node_instance_hash_free(ntree->previews, (bNodeInstanceValueFP)BKE_node_preview_free);
|
||||
@@ -504,6 +519,10 @@ void ntreeBlendWrite(BlendWriter *writer, bNodeTree *ntree)
|
||||
IDP_BlendWrite(writer, node->prop);
|
||||
}
|
||||
|
||||
LISTBASE_FOREACH (bNodeSection *, section, &node->sections) {
|
||||
BLO_write_struct(writer, bNodeSection, section);
|
||||
}
|
||||
|
||||
LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
|
||||
write_node_socket(writer, sock);
|
||||
}
|
||||
@@ -600,6 +619,10 @@ void ntreeBlendWrite(BlendWriter *writer, bNodeTree *ntree)
|
||||
BLO_write_struct(writer, bNodeLink, link);
|
||||
}
|
||||
|
||||
LISTBASE_FOREACH (bNodeSection *, section, &ntree->sections) {
|
||||
BLO_write_struct(writer, bNodeSection, section);
|
||||
}
|
||||
|
||||
LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs) {
|
||||
write_node_socket_interface(writer, sock);
|
||||
}
|
||||
@@ -681,10 +704,18 @@ void ntreeBlendReadData(BlendDataReader *reader, ID *owner_id, bNodeTree *ntree)
|
||||
|
||||
BLO_read_list(reader, &node->inputs);
|
||||
BLO_read_list(reader, &node->outputs);
|
||||
BLO_read_list(reader, &node->sections);
|
||||
|
||||
BLO_read_data_address(reader, &node->prop);
|
||||
IDP_BlendDataRead(reader, &node->prop);
|
||||
|
||||
LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
|
||||
BLO_read_data_address(reader, &sock->section);
|
||||
}
|
||||
LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
|
||||
BLO_read_data_address(reader, &sock->section);
|
||||
}
|
||||
|
||||
if (node->type == CMP_NODE_MOVIEDISTORTION) {
|
||||
/* Do nothing, this is runtime cache and hence handled by generic code using
|
||||
* `IDTypeInfo.foreach_cache` callback. */
|
||||
@@ -788,10 +819,13 @@ void ntreeBlendReadData(BlendDataReader *reader, ID *owner_id, bNodeTree *ntree)
|
||||
/* interface socket lists */
|
||||
BLO_read_list(reader, &ntree->inputs);
|
||||
BLO_read_list(reader, &ntree->outputs);
|
||||
BLO_read_list(reader, &ntree->sections);
|
||||
LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs) {
|
||||
BLO_read_data_address(reader, &sock->section);
|
||||
direct_link_node_socket(reader, sock);
|
||||
}
|
||||
LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs) {
|
||||
BLO_read_data_address(reader, &sock->section);
|
||||
direct_link_node_socket(reader, sock);
|
||||
}
|
||||
|
||||
@@ -2005,6 +2039,39 @@ void nodeRemoveAllSockets(bNodeTree *ntree, bNode *node)
|
||||
BKE_ntree_update_tag_socket_removed(ntree);
|
||||
}
|
||||
|
||||
struct bNodeSection *nodeAddSection(struct bNode *node, const char *name)
|
||||
{
|
||||
bNodeSection *section = MEM_cnew<bNodeSection>("section");
|
||||
BLI_strncpy(section->name, name, NODE_MAXSTR);
|
||||
section->flag = NODE_SECTION_CLOSED;
|
||||
|
||||
BLI_addtail(&node->sections, section);
|
||||
BLI_uniquename(&node->sections,
|
||||
section,
|
||||
DATA_("Section"),
|
||||
'.',
|
||||
offsetof(bNodeSection, name),
|
||||
sizeof(section->name));
|
||||
return section;
|
||||
}
|
||||
|
||||
void nodeRemoveSection(struct bNode *node, struct bNodeSection *section)
|
||||
{
|
||||
LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
|
||||
if (sock->section == section) {
|
||||
sock->section = nullptr;
|
||||
}
|
||||
}
|
||||
LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
|
||||
if (sock->section == section) {
|
||||
sock->section = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
BLI_remlink(&node->sections, section);
|
||||
MEM_freeN(section);
|
||||
}
|
||||
|
||||
bNode *nodeFindNodebyName(bNodeTree *ntree, const char *name)
|
||||
{
|
||||
return (bNode *)BLI_findstring(&ntree->nodes, name, offsetof(bNode, name));
|
||||
@@ -2231,7 +2298,10 @@ bNode *nodeAddStaticNode(const struct bContext *C, bNodeTree *ntree, int type)
|
||||
return nodeAddNode(C, ntree, idname);
|
||||
}
|
||||
|
||||
static void node_socket_copy(bNodeSocket *sock_dst, const bNodeSocket *sock_src, const int flag)
|
||||
static void node_socket_copy(bNodeSocket *sock_dst,
|
||||
const bNodeSocket *sock_src,
|
||||
const int flag,
|
||||
const Map<const bNodeSection *, bNodeSection *> §ion_map)
|
||||
{
|
||||
sock_dst->runtime = MEM_new<bNodeSocketRuntime>(__func__);
|
||||
if (sock_src->prop) {
|
||||
@@ -2253,6 +2323,8 @@ static void node_socket_copy(bNodeSocket *sock_dst, const bNodeSocket *sock_src,
|
||||
/* XXX some compositor nodes (e.g. image, render layers) still store
|
||||
* some persistent buffer data here, need to clear this to avoid dangling pointers. */
|
||||
sock_dst->runtime->cache = nullptr;
|
||||
|
||||
sock_dst->section = section_map.lookup(sock_src->section);
|
||||
}
|
||||
|
||||
namespace blender::bke {
|
||||
@@ -2276,10 +2348,19 @@ bNode *node_copy_with_mapping(bNodeTree *dst_tree,
|
||||
BLI_addtail(&dst_tree->nodes, node_dst);
|
||||
}
|
||||
|
||||
Map<const bNodeSection *, bNodeSection *> section_map;
|
||||
section_map.add_new(nullptr, nullptr);
|
||||
BLI_listbase_clear(&node_dst->sections);
|
||||
LISTBASE_FOREACH (const bNodeSection *, src_section, &node_src.sections) {
|
||||
bNodeSection *dst_section = (bNodeSection *)MEM_dupallocN(src_section);
|
||||
BLI_addtail(&node_dst->sections, dst_section);
|
||||
section_map.add_new(src_section, dst_section);
|
||||
}
|
||||
|
||||
BLI_listbase_clear(&node_dst->inputs);
|
||||
LISTBASE_FOREACH (const bNodeSocket *, src_socket, &node_src.inputs) {
|
||||
bNodeSocket *dst_socket = (bNodeSocket *)MEM_dupallocN(src_socket);
|
||||
node_socket_copy(dst_socket, src_socket, flag);
|
||||
node_socket_copy(dst_socket, src_socket, flag, section_map);
|
||||
BLI_addtail(&node_dst->inputs, dst_socket);
|
||||
socket_map.add_new(src_socket, dst_socket);
|
||||
}
|
||||
@@ -2287,7 +2368,7 @@ bNode *node_copy_with_mapping(bNodeTree *dst_tree,
|
||||
BLI_listbase_clear(&node_dst->outputs);
|
||||
LISTBASE_FOREACH (const bNodeSocket *, src_socket, &node_src.outputs) {
|
||||
bNodeSocket *dst_socket = (bNodeSocket *)MEM_dupallocN(src_socket);
|
||||
node_socket_copy(dst_socket, src_socket, flag);
|
||||
node_socket_copy(dst_socket, src_socket, flag, section_map);
|
||||
BLI_addtail(&node_dst->outputs, dst_socket);
|
||||
socket_map.add_new(src_socket, dst_socket);
|
||||
}
|
||||
@@ -2434,7 +2515,13 @@ void nodeRemSocketLinks(bNodeTree *ntree, bNodeSocket *sock)
|
||||
|
||||
bool nodeLinkIsHidden(const bNodeLink *link)
|
||||
{
|
||||
return nodeSocketIsHidden(link->fromsock) || nodeSocketIsHidden(link->tosock);
|
||||
if ((link->fromsock->flag & (SOCK_HIDDEN | SOCK_UNAVAIL)) != 0) {
|
||||
return true;
|
||||
}
|
||||
if ((link->tosock->flag & (SOCK_HIDDEN | SOCK_UNAVAIL)) != 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool nodeLinkIsSelected(const bNodeLink *link)
|
||||
@@ -2969,6 +3056,8 @@ static void node_free_node(bNodeTree *ntree, bNode *node)
|
||||
MEM_freeN(sock);
|
||||
}
|
||||
|
||||
BLI_freelistN(&node->sections);
|
||||
|
||||
for (bNodeLink *link : node->runtime->internal_links) {
|
||||
MEM_freeN(link);
|
||||
}
|
||||
@@ -3434,6 +3523,44 @@ void ntreeRemoveSocketInterface(bNodeTree *ntree, bNodeSocket *sock)
|
||||
BKE_ntree_update_tag_interface(ntree);
|
||||
}
|
||||
|
||||
bNodeSection *ntreeAddSection(bNodeTree *ntree, const char *name)
|
||||
{
|
||||
bNodeSection *section = MEM_cnew<bNodeSection>("section");
|
||||
BLI_strncpy(section->name, name, NODE_MAXSTR);
|
||||
section->flag = NODE_SECTION_CLOSED;
|
||||
|
||||
BLI_addtail(&ntree->sections, section);
|
||||
|
||||
BLI_uniquename(&ntree->sections,
|
||||
section,
|
||||
DATA_("Section"),
|
||||
'.',
|
||||
offsetof(bNodeSection, name),
|
||||
sizeof(section->name));
|
||||
|
||||
BKE_ntree_update_tag_interface(ntree);
|
||||
return section;
|
||||
}
|
||||
|
||||
void ntreeRemoveSection(bNodeTree *ntree, bNodeSection *section)
|
||||
{
|
||||
LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs) {
|
||||
if (sock->section == section) {
|
||||
sock->section = nullptr;
|
||||
}
|
||||
}
|
||||
LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs) {
|
||||
if (sock->section == section) {
|
||||
sock->section = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
BLI_remlink(&ntree->sections, section);
|
||||
MEM_freeN(section);
|
||||
|
||||
BKE_ntree_update_tag_interface(ntree);
|
||||
}
|
||||
|
||||
/* ************ find stuff *************** */
|
||||
|
||||
bNode *ntreeFindType(const bNodeTree *ntree, int type)
|
||||
@@ -3547,7 +3674,14 @@ void nodeSetActive(bNodeTree *ntree, bNode *node)
|
||||
|
||||
int nodeSocketIsHidden(const bNodeSocket *sock)
|
||||
{
|
||||
return ((sock->flag & (SOCK_HIDDEN | SOCK_UNAVAIL)) != 0);
|
||||
if ((sock->flag & (SOCK_HIDDEN | SOCK_UNAVAIL)) != 0) {
|
||||
return true;
|
||||
}
|
||||
if ((sock->section != nullptr) &&
|
||||
((sock->section->flag & (NODE_SECTION_CLOSED | NODE_SECTION_UNAVAIL)) != 0)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void nodeSetSocketAvailability(bNodeTree *ntree, bNodeSocket *sock, bool is_available)
|
||||
@@ -3565,6 +3699,16 @@ void nodeSetSocketAvailability(bNodeTree *ntree, bNodeSocket *sock, bool is_avai
|
||||
}
|
||||
}
|
||||
|
||||
void nodeSetSectionAvailability(bNodeSection *section, bool is_available)
|
||||
{
|
||||
if (is_available) {
|
||||
section->flag &= ~NODE_SECTION_UNAVAIL;
|
||||
}
|
||||
else {
|
||||
section->flag |= NODE_SECTION_UNAVAIL;
|
||||
}
|
||||
}
|
||||
|
||||
int nodeSocketLinkLimit(const bNodeSocket *sock)
|
||||
{
|
||||
bNodeSocketType *stype = sock->typeinfo;
|
||||
|
@@ -3691,7 +3691,6 @@ void blo_do_versions_300(FileData *fd, Library * /*lib*/, Main *bmain)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!MAIN_VERSION_ATLEAST(bmain, 305, 2)) {
|
||||
LISTBASE_FOREACH (MovieClip *, clip, &bmain->movieclips) {
|
||||
MovieTracking *tracking = &clip->tracking;
|
||||
@@ -3708,6 +3707,8 @@ void blo_do_versions_300(FileData *fd, Library * /*lib*/, Main *bmain)
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: Automatically expand/collapse node sections based on existing connections. */
|
||||
|
||||
/**
|
||||
* Versioning code until next subversion bump goes here.
|
||||
*
|
||||
|
@@ -363,6 +363,7 @@ static void node_update_basis(const bContext &C,
|
||||
loc.y = round(loc.y);
|
||||
|
||||
int dy = loc.y;
|
||||
bool add_output_space = false;
|
||||
|
||||
/* Header. */
|
||||
dy -= NODE_DY;
|
||||
@@ -372,17 +373,23 @@ static void node_update_basis(const bContext &C,
|
||||
dy -= NODE_DYS / 2;
|
||||
}
|
||||
|
||||
/* Output sockets. */
|
||||
bool add_output_space = false;
|
||||
|
||||
int buty;
|
||||
LISTBASE_FOREACH (bNodeSocket *, socket, &node.outputs) {
|
||||
if (nodeSocketIsHidden(socket)) {
|
||||
continue;
|
||||
const auto update_sockets = [&](bNodeSocket &socket, bool is_output) {
|
||||
if (nodeSocketIsHidden(&socket)) {
|
||||
return;
|
||||
}
|
||||
|
||||
PointerRNA sockptr;
|
||||
RNA_pointer_create(&ntree.id, &RNA_NodeSocket, socket, &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 (!is_output && (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,
|
||||
@@ -402,14 +409,18 @@ static void node_update_basis(const bContext &C,
|
||||
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 = nodeSocketLabel(socket);
|
||||
socket->typeinfo->draw((bContext *)&C, row, &sockptr, &nodeptr, IFACE_(socket_label));
|
||||
if (is_output) {
|
||||
/* Align output buttons to the right. */
|
||||
uiLayoutSetAlignment(row, UI_LAYOUT_ALIGN_RIGHT);
|
||||
}
|
||||
|
||||
node_socket_add_tooltip_in_node_editor(&tree_draw_ctx, &ntree, &node, socket, row);
|
||||
const char *socket_label = nodeSocketLabel(&socket);
|
||||
socket.typeinfo->draw((bContext *)&C, row, &sockptr, &nodeptr, IFACE_(socket_label));
|
||||
|
||||
node_socket_add_tooltip_in_node_editor(&tree_draw_ctx, &ntree, &node, &socket, row);
|
||||
|
||||
int buty;
|
||||
UI_block_align_end(&block);
|
||||
UI_block_layout_resolve(&block, nullptr, &buty);
|
||||
|
||||
@@ -417,15 +428,29 @@ static void node_update_basis(const bContext &C,
|
||||
buty = min_ii(buty, dy - NODE_DY);
|
||||
|
||||
/* Round the socket location to stop it from jiggling. */
|
||||
socket->runtime->locx = round(loc.x + NODE_WIDTH(node));
|
||||
socket->runtime->locy = round(dy - NODE_DYS);
|
||||
if (is_output) {
|
||||
socket.runtime->locx = round(loc.x + NODE_WIDTH(node));
|
||||
}
|
||||
else {
|
||||
socket.runtime->locx = loc.x;
|
||||
}
|
||||
socket.runtime->locy = round(dy - NODE_DYS);
|
||||
|
||||
dy = buty;
|
||||
if (socket->next) {
|
||||
dy = buty - multi_input_socket_offset * 0.5;
|
||||
if (socket.next) {
|
||||
dy -= NODE_SOCKDY;
|
||||
}
|
||||
|
||||
add_output_space = true;
|
||||
if (is_output) {
|
||||
add_output_space = true;
|
||||
}
|
||||
};
|
||||
|
||||
/* Output sockets. */
|
||||
LISTBASE_FOREACH (bNodeSocket *, socket, &node.outputs) {
|
||||
if (socket->section == nullptr) {
|
||||
update_sockets(*socket, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (add_output_space) {
|
||||
@@ -492,6 +517,7 @@ static void node_update_basis(const bContext &C,
|
||||
|
||||
node.typeinfo->draw_buttons(layout, (bContext *)&C, &nodeptr);
|
||||
|
||||
int buty;
|
||||
UI_block_align_end(&block);
|
||||
UI_block_layout_resolve(&block, nullptr, &buty);
|
||||
|
||||
@@ -500,62 +526,8 @@ static void node_update_basis(const bContext &C,
|
||||
|
||||
/* Input sockets. */
|
||||
LISTBASE_FOREACH (bNodeSocket *, socket, &node.inputs) {
|
||||
if (nodeSocketIsHidden(socket)) {
|
||||
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 = nodeSocketLabel(socket);
|
||||
socket->typeinfo->draw((bContext *)&C, row, &sockptr, &nodeptr, IFACE_(socket_label));
|
||||
|
||||
node_socket_add_tooltip_in_node_editor(&tree_draw_ctx, &ntree, &node, 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);
|
||||
|
||||
socket->runtime->locx = loc.x;
|
||||
/* Round the socket vertical position to stop it from jiggling. */
|
||||
socket->runtime->locy = round(dy - NODE_DYS);
|
||||
|
||||
dy = buty - multi_input_socket_offset * 0.5;
|
||||
if (socket->next) {
|
||||
dy -= NODE_SOCKDY;
|
||||
if (socket->section == nullptr) {
|
||||
update_sockets(*socket, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -564,6 +536,48 @@ static void node_update_basis(const bContext &C,
|
||||
dy -= NODE_DYS / 2;
|
||||
}
|
||||
|
||||
/* Sections. */
|
||||
LISTBASE_FOREACH (bNodeSection *, section, &node.sections) {
|
||||
if (section->flag & NODE_SECTION_UNAVAIL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const bool hidden = (section->flag & NODE_SECTION_CLOSED);
|
||||
dy -= NODE_DY;
|
||||
section->in_locy = dy + NODE_SOCKDY;
|
||||
|
||||
if (hidden) {
|
||||
/* If the section is hidden, update socket positions so that links are
|
||||
* drawn as expected. */
|
||||
LISTBASE_FOREACH (bNodeSocket *, socket, &node.outputs) {
|
||||
if (socket->section == section) {
|
||||
socket->runtime->locx = round(loc.x + NODE_WIDTH(node));
|
||||
socket->runtime->locy = round(section->in_locy + NODE_DYS);
|
||||
}
|
||||
}
|
||||
LISTBASE_FOREACH (bNodeSocket *, socket, &node.inputs) {
|
||||
if (socket->section == section) {
|
||||
socket->runtime->locx = loc.x;
|
||||
socket->runtime->locy = round(section->in_locy + NODE_DYS);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
dy -= NODE_DYS / 2;
|
||||
LISTBASE_FOREACH (bNodeSocket *, socket, &node.outputs) {
|
||||
if (socket->section == section) {
|
||||
update_sockets(*socket, true);
|
||||
}
|
||||
}
|
||||
LISTBASE_FOREACH (bNodeSocket *, socket, &node.inputs) {
|
||||
if (socket->section == section) {
|
||||
update_sockets(*socket, false);
|
||||
}
|
||||
}
|
||||
dy -= NODE_DYS / 2;
|
||||
}
|
||||
}
|
||||
|
||||
node.runtime->totr.xmin = loc.x;
|
||||
node.runtime->totr.xmax = loc.x + NODE_WIDTH(node);
|
||||
node.runtime->totr.ymax = loc.y;
|
||||
@@ -591,6 +605,9 @@ static void node_update_hidden(bNode &node, uiBlock &block)
|
||||
loc.x = round(loc.x);
|
||||
loc.y = round(loc.y);
|
||||
|
||||
/* TODO: Update for sections.
|
||||
* Need to account for draw order and need to figure out how to handle collapsed. */
|
||||
|
||||
/* Calculate minimal radius. */
|
||||
LISTBASE_FOREACH (bNodeSocket *, socket, &node.inputs) {
|
||||
if (!nodeSocketIsHidden(socket)) {
|
||||
@@ -1370,6 +1387,17 @@ static void node_toggle_button_cb(bContext *C, void *node_argv, void *op_argv)
|
||||
WM_operator_name_call(C, opname, WM_OP_INVOKE_DEFAULT, nullptr, nullptr);
|
||||
}
|
||||
|
||||
static void node_section_toggle_button_cb(bContext *C, void *section_argv, void *ntree_argv)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
bNodeTree *ntree = (bNodeTree *)ntree_argv;
|
||||
bNodeSection *section = (bNodeSection *)section_argv;
|
||||
|
||||
section->flag ^= NODE_SECTION_CLOSED;
|
||||
|
||||
ED_node_tree_propagate_change(C, bmain, ntree);
|
||||
}
|
||||
|
||||
static void node_draw_shadow(const SpaceNode &snode,
|
||||
const bNode &node,
|
||||
const float radius,
|
||||
@@ -2381,6 +2409,88 @@ static void node_draw_basis(const bContext &C,
|
||||
node_draw_sockets(v2d, C, ntree, node, block, true, false);
|
||||
}
|
||||
|
||||
/* Sections. */
|
||||
LISTBASE_FOREACH (bNodeSection *, section, &node.sections) {
|
||||
if (section->flag & NODE_SECTION_UNAVAIL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const rctf rect = {
|
||||
rct.xmin,
|
||||
rct.xmax,
|
||||
section->in_locy,
|
||||
section->in_locy + NODE_DY,
|
||||
};
|
||||
UI_block_emboss_set(&block, UI_EMBOSS_NONE);
|
||||
|
||||
/* Section background. */
|
||||
float color_section[4];
|
||||
if (node.flag & NODE_MUTED) {
|
||||
UI_GetThemeColorBlend4f(TH_BACK, color_id, 0.1f, color_section);
|
||||
}
|
||||
else {
|
||||
UI_GetThemeColorBlend4f(TH_NODE, color_id, 0.2f, color_section);
|
||||
}
|
||||
UI_draw_roundbox_corner_set(UI_CNR_NONE);
|
||||
UI_draw_roundbox_4fv(&rect, true, BASIS_RAD, color_section);
|
||||
|
||||
/* Collapse/expand icon. */
|
||||
const int but_size = U.widget_unit * 0.8f;
|
||||
const bool hidden = (section->flag & NODE_SECTION_CLOSED);
|
||||
uiDefIconBut(&block,
|
||||
UI_BTYPE_BUT_TOGGLE,
|
||||
0,
|
||||
hidden ? ICON_RIGHTARROW : ICON_DOWNARROW_HLT,
|
||||
rct.xmin + (NODE_MARGIN_X / 3),
|
||||
section->in_locy + NODE_DY / 2 - but_size / 2,
|
||||
but_size,
|
||||
but_size,
|
||||
nullptr,
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
"");
|
||||
|
||||
/* Section label. */
|
||||
but = uiDefBut(&block,
|
||||
UI_BTYPE_LABEL,
|
||||
0,
|
||||
section->name,
|
||||
int(rct.xmin + NODE_MARGIN_X + 0.4f),
|
||||
int(section->in_locy),
|
||||
short(iconofs - rct.xmin - (18.0f * U.dpi_fac)),
|
||||
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_section_toggle_button_cb, section, &ntree);
|
||||
|
||||
UI_block_emboss_set(&block, UI_EMBOSS);
|
||||
}
|
||||
|
||||
/* Preview. */
|
||||
bNodeInstanceHash *previews =
|
||||
(bNodeInstanceHash *)CTX_data_pointer_get(&C, "node_previews").data;
|
||||
|
@@ -2552,7 +2552,7 @@ void NODE_OT_tree_socket_remove(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Remove Node Tree Interface Socket";
|
||||
ot->description = "Remove an input or output socket to the current node tree";
|
||||
ot->description = "Remove an input or output socket from the current node tree";
|
||||
ot->idname = "NODE_OT_tree_socket_remove";
|
||||
|
||||
/* api callbacks */
|
||||
@@ -2748,6 +2748,186 @@ void NODE_OT_tree_socket_move(wmOperatorType *ot)
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Node-Tree Add Section Operator
|
||||
* \{ */
|
||||
|
||||
static bNodeSection *ntree_get_active_section(bNodeTree *ntree)
|
||||
{
|
||||
LISTBASE_FOREACH (bNodeSection *, section, &ntree->sections) {
|
||||
if (section->flag & NODE_SECTION_SELECTED) {
|
||||
return section;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static int ntree_section_add_exec(bContext *C, wmOperator * /*op*/)
|
||||
{
|
||||
SpaceNode *snode = CTX_wm_space_node(C);
|
||||
bNodeTree *ntree = snode->edittree;
|
||||
ListBase *sections = &ntree->sections;
|
||||
|
||||
PointerRNA ntree_ptr;
|
||||
RNA_id_pointer_create((ID *)ntree, &ntree_ptr);
|
||||
|
||||
bNodeSection *active_section = ntree_get_active_section(ntree);
|
||||
|
||||
const char *name = (active_section != nullptr) ? active_section->name : "Section";
|
||||
bNodeSection *section = ntreeAddSection(ntree, name);
|
||||
|
||||
if (active_section != nullptr) {
|
||||
BLI_remlink(sections, section);
|
||||
BLI_insertlinkafter(sections, active_section, section);
|
||||
}
|
||||
|
||||
/* Deactivate sections. */
|
||||
LISTBASE_FOREACH (bNodeSection *, section_iter, sections) {
|
||||
section_iter->flag &= ~NODE_SECTION_SELECTED;
|
||||
}
|
||||
/* make the new section active */
|
||||
section->flag |= NODE_SECTION_SELECTED;
|
||||
|
||||
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_section_add(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Add Node Tree Section";
|
||||
ot->description = "Add a section to the current node tree";
|
||||
ot->idname = "NODE_OT_tree_section_add";
|
||||
|
||||
/* api callbacks */
|
||||
ot->exec = ntree_section_add_exec;
|
||||
ot->poll = ED_operator_node_editable;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Node-Tree Remove Section Operator
|
||||
* \{ */
|
||||
|
||||
static int ntree_section_remove_exec(bContext *C, wmOperator * /*op*/)
|
||||
{
|
||||
SpaceNode *snode = CTX_wm_space_node(C);
|
||||
bNodeTree *ntree = snode->edittree;
|
||||
|
||||
bNodeSection *section = ntree_get_active_section(ntree);
|
||||
if (section == nullptr) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
/* preferably next section becomes active, otherwise try previous section */
|
||||
bNodeSection *active_section = (section->next ? section->next : section->prev);
|
||||
ntreeRemoveSection(ntree, section);
|
||||
|
||||
/* set active section */
|
||||
if (active_section) {
|
||||
active_section->flag |= NODE_SECTION_SELECTED;
|
||||
}
|
||||
|
||||
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_section_remove(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Remove Node Tree Section";
|
||||
ot->description = "Remove a section from the current node tree";
|
||||
ot->idname = "NODE_OT_tree_section_remove";
|
||||
|
||||
/* api callbacks */
|
||||
ot->exec = ntree_section_remove_exec;
|
||||
ot->poll = ED_operator_node_editable;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Node-Tree Move Section Operator
|
||||
* \{ */
|
||||
|
||||
static int ntree_section_move_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
SpaceNode *snode = CTX_wm_space_node(C);
|
||||
bNodeTree *ntree = snode->edittree;
|
||||
ListBase *sections = &ntree->sections;
|
||||
int direction = RNA_enum_get(op->ptr, "direction");
|
||||
|
||||
bNodeSection *section = ntree_get_active_section(ntree);
|
||||
|
||||
if (section == nullptr) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
switch (direction) {
|
||||
case 1: { /* up */
|
||||
bNodeSection *before = section->prev;
|
||||
BLI_remlink(sections, section);
|
||||
if (before) {
|
||||
BLI_insertlinkbefore(sections, before, section);
|
||||
}
|
||||
else {
|
||||
BLI_addhead(sections, section);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 2: { /* down */
|
||||
bNodeSection *after = section->next;
|
||||
BLI_remlink(sections, section);
|
||||
if (after) {
|
||||
BLI_insertlinkafter(sections, after, section);
|
||||
}
|
||||
else {
|
||||
BLI_addtail(sections, section);
|
||||
}
|
||||
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_section_move(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Move Node Tree Section";
|
||||
ot->description = "Move a section up or down in the current node tree's sections stack";
|
||||
ot->idname = "NODE_OT_tree_section_move";
|
||||
|
||||
/* api callbacks */
|
||||
ot->exec = ntree_section_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", "");
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Node Shader Script Update
|
||||
* \{ */
|
||||
|
@@ -358,6 +358,10 @@ void NODE_OT_tree_socket_remove(wmOperatorType *ot);
|
||||
void NODE_OT_tree_socket_change_type(wmOperatorType *ot);
|
||||
void NODE_OT_tree_socket_move(wmOperatorType *ot);
|
||||
|
||||
void NODE_OT_tree_section_add(wmOperatorType *ot);
|
||||
void NODE_OT_tree_section_remove(wmOperatorType *ot);
|
||||
void NODE_OT_tree_section_move(wmOperatorType *ot);
|
||||
|
||||
void NODE_OT_shader_script_update(wmOperatorType *ot);
|
||||
|
||||
void NODE_OT_viewer_border(wmOperatorType *ot);
|
||||
|
@@ -110,6 +110,10 @@ void node_operatortypes()
|
||||
WM_operatortype_append(NODE_OT_tree_socket_change_type);
|
||||
WM_operatortype_append(NODE_OT_tree_socket_move);
|
||||
|
||||
WM_operatortype_append(NODE_OT_tree_section_add);
|
||||
WM_operatortype_append(NODE_OT_tree_section_remove);
|
||||
WM_operatortype_append(NODE_OT_tree_section_move);
|
||||
|
||||
WM_operatortype_append(NODE_OT_cryptomatte_layer_add);
|
||||
WM_operatortype_append(NODE_OT_cryptomatte_layer_remove);
|
||||
}
|
||||
|
@@ -40,6 +40,7 @@
|
||||
|
||||
#include "ED_undo.h"
|
||||
|
||||
using blender::Vector;
|
||||
using blender::nodes::NodeDeclaration;
|
||||
|
||||
namespace blender::ed::space_node {
|
||||
@@ -774,8 +775,49 @@ static void ui_node_draw_node(
|
||||
}
|
||||
}
|
||||
|
||||
uiLayout *column = uiLayoutColumn(&layout, false);
|
||||
|
||||
LISTBASE_FOREACH (bNodeSocket *, input, &node.inputs) {
|
||||
ui_node_draw_input(layout, C, ntree, node, *input, depth + 1);
|
||||
if (input->section == nullptr) {
|
||||
ui_node_draw_input(*column, C, ntree, node, *input, depth + 1);
|
||||
}
|
||||
}
|
||||
|
||||
column = uiLayoutColumn(&layout, true);
|
||||
|
||||
LISTBASE_FOREACH (bNodeSection *, section, &node.sections) {
|
||||
if (section->flag & NODE_SECTION_UNAVAIL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Sections might only contain outputs, in which case we want to ignore them here. */
|
||||
Vector<bNodeSocket *> sectionInputs;
|
||||
LISTBASE_FOREACH (bNodeSocket *, input, &node.inputs) {
|
||||
if (input->section == section) {
|
||||
sectionInputs.append(input);
|
||||
}
|
||||
}
|
||||
if (sectionInputs.is_empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Draw section enable/disable button. */
|
||||
PointerRNA sectionptr;
|
||||
RNA_pointer_create(&ntree.id, &RNA_NodeSection, section, §ionptr);
|
||||
uiItemR(column, §ionptr, "opened", UI_ITEM_R_TOGGLE, section->name, ICON_NONE);
|
||||
|
||||
/* Draw inputs inside the section.*/
|
||||
if (section->flag & NODE_SECTION_CLOSED) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* The buttons for closed sections are aligned together, but once we hit an open one
|
||||
* we interrupt that and draw its inputs separately. */
|
||||
uiLayout *sectionColumn = uiLayoutColumn(&layout, true);
|
||||
for (bNodeSocket *input : sectionInputs) {
|
||||
ui_node_draw_input(*sectionColumn, C, ntree, node, *input, depth + 1);
|
||||
}
|
||||
column = uiLayoutColumn(&layout, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -91,6 +91,24 @@ typedef struct bNodeStack {
|
||||
#define NS_CR_FIT 4
|
||||
#define NS_CR_STRETCH 5
|
||||
|
||||
typedef struct bNodeSection {
|
||||
struct bNodeSection *next, *prev;
|
||||
|
||||
char name[64];
|
||||
|
||||
/** #eNodeSectionFlag */
|
||||
int flag;
|
||||
|
||||
float in_locy; // TODO don't store here!
|
||||
} bNodeSection;
|
||||
|
||||
/** #bNodeSection.flag. */
|
||||
typedef enum eNodeSectionFlag {
|
||||
NODE_SECTION_CLOSED = (1 << 1),
|
||||
NODE_SECTION_UNAVAIL = (1 << 2),
|
||||
NODE_SECTION_SELECTED = (1 << 3),
|
||||
} eNodeSectionFlag;
|
||||
|
||||
typedef struct bNodeSocket {
|
||||
struct bNodeSocket *next, *prev;
|
||||
|
||||
@@ -162,6 +180,9 @@ typedef struct bNodeSocket {
|
||||
/** A link pointer, set in #BKE_ntree_update_main. */
|
||||
struct bNodeLink *link;
|
||||
|
||||
/** The node section that the socket is in, may be null. */
|
||||
struct bNodeSection *section;
|
||||
|
||||
/* XXX deprecated, socket input values are stored in default_value now.
|
||||
* kept for forward compatibility */
|
||||
/** Custom data for inputs, only UI writes in this. */
|
||||
@@ -310,6 +331,9 @@ typedef struct bNode {
|
||||
/** Custom data, must be struct, for storage in file. */
|
||||
void *storage;
|
||||
|
||||
/** List of socket sections. */
|
||||
ListBase sections;
|
||||
|
||||
/** Root offset for drawing (parent space). */
|
||||
float locx, locy;
|
||||
/** Node custom width and height. */
|
||||
@@ -531,6 +555,9 @@ typedef struct bNodeTree {
|
||||
*/
|
||||
ListBase inputs, outputs;
|
||||
|
||||
/** List of socket sections. */
|
||||
ListBase sections;
|
||||
|
||||
/* Node preview hash table
|
||||
* Only available in base node trees (e.g. scene->node_tree)
|
||||
*/
|
||||
|
@@ -9,6 +9,7 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_string_utils.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BLT_translation.h"
|
||||
@@ -1585,6 +1586,115 @@ static void rna_NodeTree_outputs_move(bNodeTree *ntree, Main *bmain, int from_in
|
||||
WM_main_add_notifier(NC_NODE | NA_EDITED, ntree);
|
||||
}
|
||||
|
||||
static int rna_NodeTree_active_section_get(PointerRNA *ptr)
|
||||
{
|
||||
bNodeTree *ntree = (bNodeTree *)ptr->data;
|
||||
int index = 0;
|
||||
LISTBASE_FOREACH_INDEX (bNodeSection *, section, &ntree->sections, index) {
|
||||
if (section->flag & NODE_SECTION_SELECTED) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void rna_NodeTree_active_section_set(PointerRNA *ptr, int value)
|
||||
{
|
||||
bNodeTree *ntree = (bNodeTree *)ptr->data;
|
||||
|
||||
int index = 0;
|
||||
LISTBASE_FOREACH_INDEX (bNodeSection *, section, &ntree->sections, index) {
|
||||
SET_FLAG_FROM_TEST(section->flag, index == value, NODE_SECTION_SELECTED);
|
||||
}
|
||||
}
|
||||
|
||||
static bNodeSection *rna_NodeTree_sections_new(bNodeTree *ntree,
|
||||
Main *bmain,
|
||||
ReportList *reports,
|
||||
const char *name)
|
||||
{
|
||||
if (!rna_NodeTree_check(ntree, reports)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bNodeSection *section = ntreeAddSection(ntree, name);
|
||||
|
||||
if (section == NULL) {
|
||||
BKE_report(reports, RPT_ERROR, "Unable to create section");
|
||||
}
|
||||
else {
|
||||
ED_node_tree_propagate_change(NULL, bmain, ntree);
|
||||
WM_main_add_notifier(NC_NODE | NA_EDITED, ntree);
|
||||
}
|
||||
|
||||
return section;
|
||||
}
|
||||
|
||||
static void rna_NodeTree_section_remove(bNodeTree *ntree,
|
||||
Main *bmain,
|
||||
ReportList *reports,
|
||||
bNodeSection *section)
|
||||
{
|
||||
if (!rna_NodeTree_check(ntree, reports)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (BLI_findindex(&ntree->sections, section) == -1) {
|
||||
BKE_reportf(reports, RPT_ERROR, "Unable to locate section '%s' in node", section->name);
|
||||
}
|
||||
else {
|
||||
ntreeRemoveSection(ntree, section);
|
||||
|
||||
ED_node_tree_propagate_change(NULL, bmain, ntree);
|
||||
WM_main_add_notifier(NC_NODE | NA_EDITED, ntree);
|
||||
}
|
||||
}
|
||||
|
||||
static void rna_NodeTree_sections_clear(bNodeTree *ntree, Main *bmain, ReportList *reports)
|
||||
{
|
||||
if (!rna_NodeTree_check(ntree, reports)) {
|
||||
return;
|
||||
}
|
||||
|
||||
LISTBASE_FOREACH_MUTABLE (bNodeSection *, section, &ntree->sections) {
|
||||
ntreeRemoveSection(ntree, section);
|
||||
}
|
||||
|
||||
ED_node_tree_propagate_change(NULL, bmain, ntree);
|
||||
WM_main_add_notifier(NC_NODE | NA_EDITED, ntree);
|
||||
}
|
||||
|
||||
static void rna_NodeTree_sections_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;
|
||||
}
|
||||
|
||||
bNodeSection *section = BLI_findlink(&ntree->sections, from_index);
|
||||
if (to_index < from_index) {
|
||||
bNodeSection *nextsection = BLI_findlink(&ntree->sections, to_index);
|
||||
if (nextsection) {
|
||||
BLI_remlink(&ntree->sections, section);
|
||||
BLI_insertlinkbefore(&ntree->sections, nextsection, section);
|
||||
}
|
||||
}
|
||||
else {
|
||||
bNodeSection *prevsection = BLI_findlink(&ntree->sections, to_index);
|
||||
if (prevsection) {
|
||||
BLI_remlink(&ntree->sections, section);
|
||||
BLI_insertlinkafter(&ntree->sections, prevsection, section);
|
||||
}
|
||||
}
|
||||
|
||||
BKE_ntree_update_tag_interface(ntree);
|
||||
|
||||
ED_node_tree_propagate_change(NULL, 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);
|
||||
@@ -3123,6 +3233,64 @@ static void rna_NodeSocketInterface_update(Main *bmain, Scene *UNUSED(scene), Po
|
||||
ED_node_tree_propagate_change(NULL, bmain, ntree);
|
||||
}
|
||||
|
||||
static void rna_NodeSection_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr)
|
||||
{
|
||||
bNodeTree *ntree = (bNodeTree *)ptr->owner_id;
|
||||
|
||||
ED_node_tree_propagate_change(NULL, bmain, ntree);
|
||||
}
|
||||
|
||||
static char *rna_NodeSection_path(const PointerRNA *ptr)
|
||||
{
|
||||
const bNodeTree *ntree = (bNodeTree *)ptr->owner_id;
|
||||
const bNodeSection *section = (bNodeSection *)ptr->data;
|
||||
|
||||
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
|
||||
int sectionindex = BLI_findindex(&node->sections, section);
|
||||
if (sectionindex != -1) {
|
||||
char name_esc[sizeof(node->name) * 2];
|
||||
BLI_str_escape(name_esc, node->name, sizeof(name_esc));
|
||||
return BLI_sprintfN("nodes[\"%s\"].sections[%d]", name_esc, sectionindex);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static char *rna_NodeSectionInterface_path(const PointerRNA *ptr)
|
||||
{
|
||||
const bNodeTree *ntree = (bNodeTree *)ptr->owner_id;
|
||||
const bNodeSection *section = (bNodeSection *)ptr->data;
|
||||
|
||||
int index = BLI_findindex(&ntree->sections, section);
|
||||
if (index != -1) {
|
||||
return BLI_sprintfN("sections[%d]", index);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void rna_NodeSectionInterface_name_set(PointerRNA *ptr, const char *value)
|
||||
{
|
||||
bNodeTree *ntree = (bNodeTree *)ptr->owner_id;
|
||||
bNodeSection *section = (bNodeSection *)ptr->data;
|
||||
BLI_strncpy_utf8(section->name, value, sizeof(section->name));
|
||||
BLI_uniquename(&ntree->sections,
|
||||
section,
|
||||
DATA_("Section"),
|
||||
'.',
|
||||
offsetof(bNodeSection, name),
|
||||
sizeof(section->name));
|
||||
}
|
||||
|
||||
static void rna_NodeSectionInterface_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr)
|
||||
{
|
||||
bNodeTree *ntree = (bNodeTree *)ptr->owner_id;
|
||||
|
||||
BKE_ntree_update_tag_interface(ntree);
|
||||
ED_node_tree_propagate_change(NULL, bmain, ntree);
|
||||
}
|
||||
|
||||
/* ******** Standard Node Socket Base Types ******** */
|
||||
|
||||
static void rna_NodeSocketStandard_draw(ID *id,
|
||||
@@ -11190,6 +11358,14 @@ static void rna_def_node_socket_interface(BlenderRNA *brna)
|
||||
"geometry nodes modifier");
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update");
|
||||
|
||||
prop = RNA_def_property(srna, "section", PROP_POINTER, PROP_NONE);
|
||||
RNA_def_property_struct_type(prop, "NodeSectionInterface");
|
||||
RNA_def_property_pointer_sdna(prop, NULL, "section");
|
||||
RNA_def_property_ui_text(prop, "Section", "The section that the socket is part of");
|
||||
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_PTR_NO_OWNERSHIP);
|
||||
RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON);
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update");
|
||||
|
||||
/* registration */
|
||||
prop = RNA_def_property(srna, "bl_socket_idname", PROP_STRING, PROP_NONE);
|
||||
RNA_def_property_string_sdna(prop, NULL, "typeinfo->idname");
|
||||
@@ -11944,6 +12120,54 @@ static void rna_def_node_socket_standard_types(BlenderRNA *brna)
|
||||
rna_def_node_socket_material(brna, "NodeSocketMaterial", "NodeSocketInterfaceMaterial");
|
||||
}
|
||||
|
||||
static void rna_def_node_section(BlenderRNA *brna)
|
||||
{
|
||||
StructRNA *srna;
|
||||
PropertyRNA *prop;
|
||||
|
||||
srna = RNA_def_struct(brna, "NodeSection", NULL);
|
||||
RNA_def_struct_ui_text(srna, "Node Section", "");
|
||||
RNA_def_struct_sdna(srna, "bNodeSection");
|
||||
RNA_def_struct_path_func(srna, "rna_NodeSection_path");
|
||||
|
||||
prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
|
||||
RNA_def_property_ui_text(prop, "Name", "Section name");
|
||||
RNA_def_struct_name_property(srna, prop);
|
||||
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||
|
||||
prop = RNA_def_property(srna, "opened", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", NODE_SECTION_CLOSED);
|
||||
RNA_def_property_ui_text(prop, "Opened", "Whether the section is expanded");
|
||||
RNA_def_property_update(prop, NC_NODE | NA_SELECTED, "rna_NodeSection_update");
|
||||
|
||||
prop = RNA_def_property(srna, "enabled", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", NODE_SECTION_UNAVAIL);
|
||||
RNA_def_property_ui_text(prop, "Enabled", "");
|
||||
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||
}
|
||||
|
||||
static void rna_def_node_section_interface(BlenderRNA *brna)
|
||||
{
|
||||
StructRNA *srna;
|
||||
PropertyRNA *prop;
|
||||
|
||||
srna = RNA_def_struct(brna, "NodeSectionInterface", NULL);
|
||||
RNA_def_struct_ui_text(srna, "Node Section", "");
|
||||
RNA_def_struct_sdna(srna, "bNodeSection");
|
||||
RNA_def_struct_path_func(srna, "rna_NodeSectionInterface_path");
|
||||
|
||||
prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
|
||||
RNA_def_property_ui_text(prop, "Name", "Section name");
|
||||
RNA_def_struct_name_property(srna, prop);
|
||||
RNA_def_property_string_funcs(prop, NULL, NULL, "rna_NodeSectionInterface_name_set");
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSectionInterface_update");
|
||||
|
||||
prop = RNA_def_property(srna, "selected", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "flag", NODE_SECTION_SELECTED);
|
||||
RNA_def_property_ui_text(prop, "Selected", "");
|
||||
RNA_def_property_update(prop, NC_NODE | ND_DISPLAY, NULL);
|
||||
}
|
||||
|
||||
static void rna_def_internal_node(BlenderRNA *brna)
|
||||
{
|
||||
StructRNA *srna;
|
||||
@@ -12170,6 +12394,12 @@ static void rna_def_node(BlenderRNA *brna)
|
||||
RNA_def_property_ui_text(prop, "Outputs", "");
|
||||
rna_def_node_sockets_api(brna, prop, SOCK_OUT);
|
||||
|
||||
prop = RNA_def_property(srna, "sections", PROP_COLLECTION, PROP_NONE);
|
||||
RNA_def_property_collection_sdna(prop, NULL, "sections", NULL);
|
||||
RNA_def_property_struct_type(prop, "NodeSection");
|
||||
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
|
||||
RNA_def_property_ui_text(prop, "Sections", "");
|
||||
|
||||
prop = RNA_def_property(srna, "internal_links", PROP_COLLECTION, PROP_NONE);
|
||||
RNA_def_property_collection_funcs(prop,
|
||||
"rna_Node_internal_links_begin",
|
||||
@@ -12533,9 +12763,51 @@ 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_section_api(BlenderRNA *brna, PropertyRNA *cprop)
|
||||
{
|
||||
StructRNA *srna;
|
||||
PropertyRNA *parm;
|
||||
FunctionRNA *func;
|
||||
|
||||
RNA_def_property_srna(cprop, "NodeTreeSections");
|
||||
srna = RNA_def_struct(brna, "NodeTreeSections", NULL);
|
||||
RNA_def_struct_sdna(srna, "bNodeTree");
|
||||
RNA_def_struct_ui_text(srna, "Node Tree Sections", "Collection of Node Tree Sections");
|
||||
|
||||
func = RNA_def_function(srna, "new", "rna_NodeTree_sections_new");
|
||||
RNA_def_function_ui_description(func, "Add a section to this node tree");
|
||||
RNA_def_function_flag(func, FUNC_USE_MAIN | FUNC_USE_REPORTS);
|
||||
parm = RNA_def_string(func, "name", NULL, MAX_NAME, "Name", "");
|
||||
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
|
||||
/* return value */
|
||||
parm = RNA_def_pointer(func, "section", "NodeSectionInterface", "", "New section");
|
||||
RNA_def_function_return(func, parm);
|
||||
|
||||
func = RNA_def_function(srna, "remove", "rna_NodeTree_section_remove");
|
||||
RNA_def_function_ui_description(func, "Remove a section from this node tree");
|
||||
RNA_def_function_flag(func, FUNC_USE_MAIN | FUNC_USE_REPORTS);
|
||||
parm = RNA_def_pointer(func, "section", "NodeSectionInterface", "", "The section to remove");
|
||||
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
|
||||
|
||||
func = RNA_def_function(srna, "clear", "rna_NodeTree_sections_clear");
|
||||
RNA_def_function_ui_description(func, "Remove all sections from this node tree");
|
||||
RNA_def_function_flag(func, FUNC_USE_MAIN | FUNC_USE_REPORTS);
|
||||
|
||||
func = RNA_def_function(srna, "move", "rna_NodeTree_sections_move");
|
||||
RNA_def_function_ui_description(func, "Move a section 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 section to move", 0, 10000);
|
||||
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
|
||||
parm = RNA_def_int(
|
||||
func, "to_index", -1, 0, INT_MAX, "To Index", "Target index for the section", 0, 10000);
|
||||
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
|
||||
}
|
||||
|
||||
static void rna_def_node_tree_sockets_api(BlenderRNA *brna, PropertyRNA *cprop, int in_out)
|
||||
{
|
||||
StructRNA *srna;
|
||||
PropertyRNA *prop;
|
||||
PropertyRNA *parm;
|
||||
FunctionRNA *func;
|
||||
const char *structtype = (in_out == SOCK_IN ? "NodeTreeInputs" : "NodeTreeOutputs");
|
||||
@@ -12686,6 +12958,20 @@ static void rna_def_nodetree(BlenderRNA *brna)
|
||||
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
|
||||
RNA_def_property_update(prop, NC_NODE, NULL);
|
||||
|
||||
prop = RNA_def_property(srna, "sections", PROP_COLLECTION, PROP_NONE);
|
||||
RNA_def_property_collection_sdna(prop, NULL, "sections", NULL);
|
||||
RNA_def_property_struct_type(prop, "NodeSectionInterface");
|
||||
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||
RNA_def_property_ui_text(prop, "Sections", "Node tree sections");
|
||||
rna_def_node_tree_section_api(brna, prop);
|
||||
|
||||
prop = RNA_def_property(srna, "active_section", PROP_INT, PROP_UNSIGNED);
|
||||
RNA_def_property_int_funcs(
|
||||
prop, "rna_NodeTree_active_section_get", "rna_NodeTree_active_section_set", NULL);
|
||||
RNA_def_property_ui_text(prop, "Active Section", "Index of the active section");
|
||||
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
|
||||
RNA_def_property_update(prop, NC_NODE, NULL);
|
||||
|
||||
/* 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");
|
||||
@@ -12935,6 +13221,9 @@ void RNA_def_nodetree(BlenderRNA *brna)
|
||||
rna_def_node_socket(brna);
|
||||
rna_def_node_socket_interface(brna);
|
||||
|
||||
rna_def_node_section(brna);
|
||||
rna_def_node_section_interface(brna);
|
||||
|
||||
rna_def_node(brna);
|
||||
rna_def_node_link(brna);
|
||||
|
||||
|
@@ -67,6 +67,21 @@ struct FieldInferencingInterface {
|
||||
|
||||
using ImplicitInputValueFn = std::function<void(const bNode &node, void *r_value)>;
|
||||
|
||||
/**
|
||||
* Describes a single node section.
|
||||
*/
|
||||
class SectionDeclaration {
|
||||
protected:
|
||||
std::string name_;
|
||||
|
||||
friend class NodeDeclarationBuilder;
|
||||
friend class SectionDeclarationBuilder;
|
||||
|
||||
public:
|
||||
StringRefNull name() const;
|
||||
bNodeSection &build(bNode &node);
|
||||
};
|
||||
|
||||
/**
|
||||
* Describes a single input or output socket. This is subclassed for different socket types.
|
||||
*/
|
||||
@@ -86,6 +101,7 @@ class SocketDeclaration {
|
||||
bool is_unavailable_ = false;
|
||||
bool is_attribute_name_ = false;
|
||||
bool is_default_link_socket_ = false;
|
||||
const SectionDeclaration *section_ = nullptr;
|
||||
|
||||
InputSocketFieldType input_field_type_ = InputSocketFieldType::None;
|
||||
OutputFieldDependency output_field_dependency_;
|
||||
@@ -151,6 +167,8 @@ class SocketDeclaration {
|
||||
return implicit_input_fn_.get();
|
||||
}
|
||||
|
||||
const SectionDeclaration *section() const;
|
||||
|
||||
protected:
|
||||
void set_common_flags(bNodeSocket &socket) const;
|
||||
bool matches_common_data(const bNodeSocket &socket) const;
|
||||
@@ -161,6 +179,23 @@ class BaseSocketDeclarationBuilder {
|
||||
virtual ~BaseSocketDeclarationBuilder() = default;
|
||||
};
|
||||
|
||||
template<typename SocketDecl> class SocketDeclarationBuilder;
|
||||
|
||||
class SectionDeclarationBuilder {
|
||||
protected:
|
||||
SectionDeclaration *decl_;
|
||||
|
||||
friend class NodeDeclarationBuilder;
|
||||
template<typename> friend class SocketDeclarationBuilder;
|
||||
|
||||
public:
|
||||
SectionDeclarationBuilder &add_parent()
|
||||
{
|
||||
// TODO
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Wraps a #SocketDeclaration and provides methods to set it up correctly.
|
||||
* This is separate from #SocketDeclaration, because it allows separating the API used by nodes to
|
||||
@@ -301,14 +336,22 @@ class SocketDeclarationBuilder : public BaseSocketDeclarationBuilder {
|
||||
decl_->make_available_fn_ = std::move(fn);
|
||||
return *(Self *)this;
|
||||
}
|
||||
|
||||
Self §ion(const SectionDeclarationBuilder §ion)
|
||||
{
|
||||
decl_->section_ = section.decl_;
|
||||
return *(Self *)this;
|
||||
}
|
||||
};
|
||||
|
||||
using SocketDeclarationPtr = std::unique_ptr<SocketDeclaration>;
|
||||
using SectionDeclarationPtr = std::unique_ptr<SectionDeclaration>;
|
||||
|
||||
class NodeDeclaration {
|
||||
private:
|
||||
Vector<SocketDeclarationPtr> inputs_;
|
||||
Vector<SocketDeclarationPtr> outputs_;
|
||||
Vector<SectionDeclarationPtr> sections_;
|
||||
bool is_function_node_ = false;
|
||||
|
||||
friend NodeDeclarationBuilder;
|
||||
@@ -319,6 +362,7 @@ class NodeDeclaration {
|
||||
Span<SocketDeclarationPtr> inputs() const;
|
||||
Span<SocketDeclarationPtr> outputs() const;
|
||||
Span<SocketDeclarationPtr> sockets(eNodeSocketInOut in_out) const;
|
||||
Span<SectionDeclarationPtr> sections() const;
|
||||
|
||||
bool is_function_node() const
|
||||
{
|
||||
@@ -332,6 +376,7 @@ class NodeDeclarationBuilder {
|
||||
private:
|
||||
NodeDeclaration &declaration_;
|
||||
Vector<std::unique_ptr<BaseSocketDeclarationBuilder>> builders_;
|
||||
Vector<std::unique_ptr<SectionDeclarationBuilder>> section_builders_;
|
||||
|
||||
public:
|
||||
NodeDeclarationBuilder(NodeDeclaration &declaration);
|
||||
@@ -353,6 +398,8 @@ class NodeDeclarationBuilder {
|
||||
template<typename DeclType>
|
||||
typename DeclType::Builder &add_output(StringRef name, StringRef identifier = "");
|
||||
|
||||
SectionDeclarationBuilder &add_section(StringRef name);
|
||||
|
||||
private:
|
||||
template<typename DeclType>
|
||||
typename DeclType::Builder &add_socket(StringRef name,
|
||||
@@ -502,6 +549,11 @@ inline bool SocketDeclaration::compositor_expects_single_value() const
|
||||
return compositor_expects_single_value_;
|
||||
}
|
||||
|
||||
inline const SectionDeclaration *SocketDeclaration::section() const
|
||||
{
|
||||
return section_;
|
||||
}
|
||||
|
||||
inline void SocketDeclaration::make_available(bNode &node) const
|
||||
{
|
||||
if (make_available_fn_) {
|
||||
@@ -511,6 +563,17 @@ inline void SocketDeclaration::make_available(bNode &node) const
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name #SectionDeclaration Inline Methods
|
||||
* \{ */
|
||||
|
||||
inline StringRefNull SectionDeclaration::name() const
|
||||
{
|
||||
return name_;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name #NodeDeclarationBuilder Inline Methods
|
||||
* \{ */
|
||||
@@ -561,6 +624,19 @@ inline typename DeclType::Builder &NodeDeclarationBuilder::add_socket(StringRef
|
||||
return socket_decl_builder_ref;
|
||||
}
|
||||
|
||||
inline SectionDeclarationBuilder &NodeDeclarationBuilder::add_section(StringRef name)
|
||||
{
|
||||
std::unique_ptr<SectionDeclaration> section_decl = std::make_unique<SectionDeclaration>();
|
||||
std::unique_ptr<SectionDeclarationBuilder> section_decl_builder =
|
||||
std::make_unique<SectionDeclarationBuilder>();
|
||||
section_decl_builder->decl_ = &*section_decl;
|
||||
section_decl->name_ = name;
|
||||
declaration_.sections_.append(std::move(section_decl));
|
||||
SectionDeclarationBuilder §ion_decl_builder_ref = *section_decl_builder;
|
||||
section_builders_.append(std::move(section_decl_builder));
|
||||
return section_decl_builder_ref;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
@@ -585,6 +661,11 @@ inline Span<SocketDeclarationPtr> NodeDeclaration::sockets(eNodeSocketInOut in_o
|
||||
return outputs_;
|
||||
}
|
||||
|
||||
inline Span<SectionDeclarationPtr> NodeDeclaration::sections() const
|
||||
{
|
||||
return sections_;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::nodes
|
||||
|
@@ -132,6 +132,11 @@ static void add_new_socket_from_interface(bNodeTree &node_tree,
|
||||
interface_socket.typeinfo->interface_init_socket(
|
||||
&node_tree, &interface_socket, &node, socket, "interface");
|
||||
}
|
||||
|
||||
if (interface_socket.section) {
|
||||
socket->section = (bNodeSection *)BLI_findstring(
|
||||
&node.sections, interface_socket.section->name, offsetof(bNodeSection, name));
|
||||
}
|
||||
}
|
||||
|
||||
static void update_socket_to_match_interface(bNodeTree &node_tree,
|
||||
@@ -153,6 +158,14 @@ static void update_socket_to_match_interface(bNodeTree &node_tree,
|
||||
interface_socket.typeinfo->interface_verify_socket(
|
||||
&node_tree, &interface_socket, &node, &socket_to_update, "interface");
|
||||
}
|
||||
|
||||
if (interface_socket.section) {
|
||||
socket_to_update.section = (bNodeSection *)BLI_findstring(
|
||||
&node.sections, interface_socket.section->name, offsetof(bNodeSection, name));
|
||||
}
|
||||
else {
|
||||
socket_to_update.section = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -218,6 +231,63 @@ static void group_verify_socket_list(bNodeTree &node_tree,
|
||||
}
|
||||
}
|
||||
|
||||
static void group_verify_section_list(bNodeTree &node_tree,
|
||||
bNode &node,
|
||||
bNodeTree &group,
|
||||
ListBase &verify_lb,
|
||||
const eNodeSocketInOut direction)
|
||||
{
|
||||
Set<const bNodeSection *> usedSections;
|
||||
if (direction & SOCK_IN) {
|
||||
LISTBASE_FOREACH (const bNodeSocket *, sock, &group.inputs) {
|
||||
usedSections.add(sock->section);
|
||||
}
|
||||
}
|
||||
if (direction & SOCK_OUT) {
|
||||
LISTBASE_FOREACH (const bNodeSocket *, sock, &group.outputs) {
|
||||
usedSections.add(sock->section);
|
||||
}
|
||||
}
|
||||
|
||||
ListBase old_sections = verify_lb;
|
||||
Vector<bNodeSection *> ordered_old_sections = old_sections;
|
||||
BLI_listbase_clear(&verify_lb);
|
||||
|
||||
LISTBASE_FOREACH (const bNodeSection *, interface_section, &group.sections) {
|
||||
if (!usedSections.contains(interface_section)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bNodeSection *matching_section = (bNodeSection *)BLI_findstring(
|
||||
&old_sections, interface_section->name, offsetof(bNodeSection, name));
|
||||
if (matching_section) {
|
||||
BLI_remlink(&old_sections, matching_section);
|
||||
BLI_addtail(&verify_lb, matching_section);
|
||||
}
|
||||
else {
|
||||
nodeAddSection(&node, interface_section->name);
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove leftover sections that didn't match the node group's interface. */
|
||||
LISTBASE_FOREACH_MUTABLE (bNodeSection *, unused_section, &old_sections) {
|
||||
nodeRemoveSection(&node, unused_section);
|
||||
}
|
||||
|
||||
{
|
||||
/* Check if new sections match the old sections. */
|
||||
int index;
|
||||
LISTBASE_FOREACH_INDEX (bNodeSection *, new_section, &verify_lb, index) {
|
||||
if (index < ordered_old_sections.size()) {
|
||||
if (ordered_old_sections[index] != new_section) {
|
||||
BKE_ntree_update_tag_interface(&node_tree);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void node_group_update(struct bNodeTree *ntree, struct bNode *node)
|
||||
{
|
||||
/* check inputs and outputs, and remove or insert them */
|
||||
@@ -230,6 +300,9 @@ void node_group_update(struct bNodeTree *ntree, struct bNode *node)
|
||||
}
|
||||
else {
|
||||
bNodeTree *ngroup = (bNodeTree *)node->id;
|
||||
|
||||
group_verify_section_list(
|
||||
*ntree, *node, *ngroup, node->sections, (eNodeSocketInOut)(SOCK_IN | SOCK_OUT));
|
||||
group_verify_socket_list(*ntree, *node, ngroup->inputs, node->inputs, SOCK_IN, false);
|
||||
group_verify_socket_list(*ntree, *node, ngroup->outputs, node->outputs, SOCK_OUT, false);
|
||||
}
|
||||
@@ -486,6 +559,7 @@ void node_group_input_update(bNodeTree *ntree, bNode *node)
|
||||
}
|
||||
}
|
||||
|
||||
group_verify_section_list(*ntree, *node, *ntree, node->sections, SOCK_IN);
|
||||
group_verify_socket_list(*ntree, *node, ntree->inputs, node->outputs, SOCK_OUT, true);
|
||||
}
|
||||
|
||||
@@ -567,6 +641,7 @@ void node_group_output_update(bNodeTree *ntree, bNode *node)
|
||||
}
|
||||
}
|
||||
|
||||
group_verify_section_list(*ntree, *node, *ntree, node->sections, SOCK_OUT);
|
||||
group_verify_socket_list(*ntree, *node, ntree->outputs, node->inputs, SOCK_IN, true);
|
||||
}
|
||||
|
||||
|
@@ -82,6 +82,12 @@ bool SocketDeclaration::matches_common_data(const bNodeSocket &socket) const
|
||||
return true;
|
||||
}
|
||||
|
||||
bNodeSection &SectionDeclaration::build(bNode &node)
|
||||
{
|
||||
bNodeSection §ion = *nodeAddSection(&node, name_.c_str());
|
||||
return section;
|
||||
}
|
||||
|
||||
namespace implicit_field_inputs {
|
||||
|
||||
void position(const bNode & /*node*/, void *r_value)
|
||||
|
@@ -35,6 +35,8 @@
|
||||
|
||||
using namespace blender;
|
||||
using blender::fn::ValueOrField;
|
||||
using blender::nodes::SectionDeclaration;
|
||||
using blender::nodes::SectionDeclarationPtr;
|
||||
using blender::nodes::SocketDeclarationPtr;
|
||||
|
||||
struct bNodeSocket *node_add_socket_from_template(struct bNodeTree *ntree,
|
||||
@@ -171,10 +173,48 @@ static void verify_socket_template_list(bNodeTree *ntree,
|
||||
}
|
||||
}
|
||||
|
||||
static Map<const SectionDeclaration *, bNodeSection *> refresh_sections_list(
|
||||
bNode &node, Span<SectionDeclarationPtr> section_decls)
|
||||
{
|
||||
Vector<bNodeSection *> old_sections = node.sections;
|
||||
VectorSet<bNodeSection *> new_sections;
|
||||
Map<const SectionDeclaration *, bNodeSection *> section_map;
|
||||
for (const SectionDeclarationPtr §ion_decl : section_decls) {
|
||||
/* Try to find an existing section of the same name. */
|
||||
bNodeSection *old_section = nullptr;
|
||||
for (const int i : old_sections.index_range()) {
|
||||
bNodeSection §ion = *old_sections[i];
|
||||
if (section.name == section_decl->name()) {
|
||||
old_sections.remove_and_reorder(i);
|
||||
old_section = §ion;
|
||||
break;
|
||||
}
|
||||
}
|
||||
bNodeSection *new_section = old_section;
|
||||
if (new_section == nullptr) {
|
||||
/* Create a completely new socket. */
|
||||
new_section = §ion_decl->build(node);
|
||||
}
|
||||
section_map.add_new(section_decl.get(), new_section);
|
||||
new_sections.add_new(new_section);
|
||||
}
|
||||
LISTBASE_FOREACH_MUTABLE (bNodeSection *, old_section, &node.sections) {
|
||||
if (!new_sections.contains(old_section)) {
|
||||
nodeRemoveSection(&node, old_section);
|
||||
}
|
||||
}
|
||||
BLI_listbase_clear(&node.sections);
|
||||
for (bNodeSection *section : new_sections) {
|
||||
BLI_addtail(&node.sections, section);
|
||||
}
|
||||
return section_map;
|
||||
}
|
||||
|
||||
static void refresh_socket_list(bNodeTree &ntree,
|
||||
bNode &node,
|
||||
ListBase &sockets,
|
||||
Span<SocketDeclarationPtr> socket_decls,
|
||||
const Map<const SectionDeclaration *, bNodeSection *> §ion_map,
|
||||
const bool do_id_user)
|
||||
{
|
||||
Vector<bNodeSocket *> old_sockets = sockets;
|
||||
@@ -231,6 +271,7 @@ static void refresh_socket_list(bNodeTree &ntree,
|
||||
}
|
||||
}
|
||||
}
|
||||
new_socket->section = section_map.lookup_default(socket_decl->section(), nullptr);
|
||||
new_sockets.add_new(new_socket);
|
||||
}
|
||||
LISTBASE_FOREACH_MUTABLE (bNodeSocket *, old_socket, &sockets) {
|
||||
@@ -249,8 +290,10 @@ static void refresh_node(bNodeTree &ntree,
|
||||
blender::nodes::NodeDeclaration &node_decl,
|
||||
bool do_id_user)
|
||||
{
|
||||
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);
|
||||
Map<const SectionDeclaration *, bNodeSection *> section_map = refresh_sections_list(
|
||||
node, node_decl.sections());
|
||||
refresh_socket_list(ntree, node, node.inputs, node_decl.inputs(), section_map, do_id_user);
|
||||
refresh_socket_list(ntree, node, node.outputs, node_decl.outputs(), section_map, do_id_user);
|
||||
}
|
||||
|
||||
void node_verify_sockets(bNodeTree *ntree, bNode *node, bool do_id_user)
|
||||
|
@@ -12,38 +12,57 @@ namespace blender::nodes::node_shader_bsdf_principled_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
auto &subsurface = b.add_section(N_("Subsurface"));
|
||||
auto &specular = b.add_section(N_("Specular"));
|
||||
auto &sheen = b.add_section(N_("Sheen"));
|
||||
auto &clearcoat = b.add_section(N_("Clearcoat"));
|
||||
auto &emission = b.add_section(N_("Emission"));
|
||||
auto &thin_film = b.add_section(N_("Thin Film"));
|
||||
|
||||
/* TODO: Tooltips depending on old/new model. */
|
||||
b.add_input<decl::Color>(N_("Base Color")).default_value({0.8f, 0.8f, 0.8f, 1.0f});
|
||||
b.add_input<decl::Float>(N_("Subsurface"))
|
||||
.default_value(0.0f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR);
|
||||
.subtype(PROP_FACTOR)
|
||||
.section(subsurface);
|
||||
/* TODO: Somehow merge with "Subsurface". Needs different subtype though... */
|
||||
b.add_input<decl::Float>(N_("Subsurface Scale")).default_value(0.0f).min(0.0f).max(100.0f);
|
||||
b.add_input<decl::Float>(N_("Subsurface Scale"))
|
||||
.default_value(0.0f)
|
||||
.min(0.0f)
|
||||
.max(100.0f)
|
||||
.section(subsurface);
|
||||
b.add_input<decl::Vector>(N_("Subsurface Radius"))
|
||||
.default_value({1.0f, 0.2f, 0.1f})
|
||||
.min(0.0f)
|
||||
.max(100.0f)
|
||||
.compact();
|
||||
b.add_input<decl::Color>(N_("Subsurface Color")).default_value({0.8f, 0.8f, 0.8f, 1.0f});
|
||||
.compact()
|
||||
.section(subsurface);
|
||||
b.add_input<decl::Color>(N_("Subsurface Color"))
|
||||
.default_value({0.8f, 0.8f, 0.8f, 1.0f})
|
||||
.section(subsurface);
|
||||
b.add_input<decl::Float>(N_("Subsurface IOR"))
|
||||
.default_value(1.4f)
|
||||
.min(1.01f)
|
||||
.max(3.8f)
|
||||
.subtype(PROP_FACTOR);
|
||||
.subtype(PROP_FACTOR)
|
||||
.section(subsurface);
|
||||
b.add_input<decl::Float>(N_("Subsurface Anisotropy"))
|
||||
.default_value(0.0f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR);
|
||||
.subtype(PROP_FACTOR)
|
||||
.section(subsurface);
|
||||
b.add_input<decl::Float>(N_("Metallic"))
|
||||
.default_value(0.0f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR);
|
||||
/* TODO: Also add support to Principled v1? Would be compatible at defaults afaics. */
|
||||
b.add_input<decl::Color>(N_("Metallic Edge")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
|
||||
b.add_input<decl::Color>(N_("Metallic Edge"))
|
||||
.default_value({1.0f, 1.0f, 1.0f, 1.0f})
|
||||
.section(specular);
|
||||
b.add_input<decl::Float>(N_("Specular"))
|
||||
.default_value(0.5f)
|
||||
.min(0.0f)
|
||||
@@ -55,7 +74,8 @@ static void node_declare(NodeDeclarationBuilder &b)
|
||||
.default_value(0.0f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR);
|
||||
.subtype(PROP_FACTOR)
|
||||
.section(subsurface);
|
||||
b.add_input<decl::Float>(N_("Roughness"))
|
||||
.default_value(0.5f)
|
||||
.min(0.0f)
|
||||
@@ -65,47 +85,61 @@ static void node_declare(NodeDeclarationBuilder &b)
|
||||
.default_value(0.0f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR);
|
||||
.subtype(PROP_FACTOR)
|
||||
.section(specular);
|
||||
b.add_input<decl::Float>(N_("Anisotropic Rotation"))
|
||||
.default_value(0.0f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR);
|
||||
.subtype(PROP_FACTOR)
|
||||
.section(specular);
|
||||
b.add_input<decl::Float>(N_("Sheen"))
|
||||
.default_value(0.0f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR);
|
||||
.subtype(PROP_FACTOR)
|
||||
.section(sheen);
|
||||
/* TODO: Should be a color input in v2. Any way to keep compatibility?
|
||||
* Maybe change to color everywhere and detect special case when float is connected? */
|
||||
b.add_input<decl::Float>(N_("Sheen Tint"))
|
||||
.default_value(0.5f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR);
|
||||
.subtype(PROP_FACTOR)
|
||||
.section(sheen);
|
||||
b.add_input<decl::Float>(N_("Sheen Roughness"))
|
||||
.default_value(0.5f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR);
|
||||
.subtype(PROP_FACTOR)
|
||||
.section(sheen);
|
||||
b.add_input<decl::Float>(N_("Clearcoat"))
|
||||
.default_value(0.0f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR);
|
||||
.subtype(PROP_FACTOR)
|
||||
.section(clearcoat);
|
||||
b.add_input<decl::Float>(N_("Clearcoat Roughness"))
|
||||
.default_value(0.03f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR);
|
||||
.subtype(PROP_FACTOR)
|
||||
.section(clearcoat);
|
||||
/* TODO: Also add support to Principled v1? Would remain compatible and reduce differences. */
|
||||
b.add_input<decl::Color>(N_("Clearcoat Tint")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
|
||||
b.add_input<decl::Color>(N_("Clearcoat Tint"))
|
||||
.default_value({1.0f, 1.0f, 1.0f, 1.0f})
|
||||
.section(clearcoat);
|
||||
b.add_input<decl::Float>(N_("Thin Film Thickness"))
|
||||
.default_value(0.0f)
|
||||
.min(0.0f)
|
||||
.max(10000.0f)
|
||||
.subtype(PROP_WAVELENGTH);
|
||||
b.add_input<decl::Float>(N_("Thin Film IOR")).default_value(1.5f).min(1.0f).max(10.0f);
|
||||
.subtype(PROP_WAVELENGTH)
|
||||
.section(thin_film);
|
||||
b.add_input<decl::Float>(N_("Thin Film IOR"))
|
||||
.default_value(1.5f)
|
||||
.min(1.0f)
|
||||
.max(10.0f)
|
||||
.section(thin_film);
|
||||
/* TODO: Restrict min/max (e.g. 0.1 to 10) */
|
||||
b.add_input<decl::Float>(N_("IOR")).default_value(1.45f).min(0.0f).max(1000.0f);
|
||||
b.add_input<decl::Float>(N_("Transmission"))
|
||||
@@ -119,16 +153,22 @@ static void node_declare(NodeDeclarationBuilder &b)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR);
|
||||
/* TODO: Swap defaults (white, strength 0)? */
|
||||
b.add_input<decl::Color>(N_("Emission")).default_value({0.0f, 0.0f, 0.0f, 1.0f});
|
||||
b.add_input<decl::Float>(N_("Emission Strength")).default_value(1.0).min(0.0f).max(1000000.0f);
|
||||
b.add_input<decl::Color>(N_("Emission"))
|
||||
.default_value({0.0f, 0.0f, 0.0f, 1.0f})
|
||||
.section(emission);
|
||||
b.add_input<decl::Float>(N_("Emission Strength"))
|
||||
.default_value(1.0)
|
||||
.min(0.0f)
|
||||
.max(1000000.0f)
|
||||
.section(emission);
|
||||
b.add_input<decl::Float>(N_("Alpha"))
|
||||
.default_value(1.0f)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR);
|
||||
b.add_input<decl::Vector>(N_("Normal")).hide_value();
|
||||
b.add_input<decl::Vector>(N_("Clearcoat Normal")).hide_value();
|
||||
b.add_input<decl::Vector>(N_("Tangent")).hide_value();
|
||||
b.add_input<decl::Vector>(N_("Clearcoat Normal")).hide_value().section(clearcoat);
|
||||
b.add_input<decl::Vector>(N_("Tangent")).hide_value().section(specular);
|
||||
b.add_input<decl::Float>(N_("Weight")).unavailable();
|
||||
b.add_output<decl::Shader>(N_("BSDF"));
|
||||
}
|
||||
@@ -253,9 +293,9 @@ static void node_shader_update_principled(bNodeTree *ntree, bNode *node)
|
||||
{
|
||||
const int distribution = node->custom1;
|
||||
const int sss_method = node->custom2;
|
||||
const bool is_v2 = (distribution == SHD_PRINCIPLED_V2);
|
||||
|
||||
LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
|
||||
const bool is_v2 = (distribution == SHD_PRINCIPLED_V2);
|
||||
if (STREQ(sock->name, "Transmission Roughness")) {
|
||||
/* Only supported by the old separable glass model. */
|
||||
nodeSetSocketAvailability(ntree, sock, distribution == SHD_PRINCIPLED_GGX);
|
||||
@@ -293,6 +333,13 @@ static void node_shader_update_principled(bNodeTree *ntree, bNode *node)
|
||||
nodeSetSocketAvailability(ntree, sock, is_v2);
|
||||
}
|
||||
}
|
||||
|
||||
LISTBASE_FOREACH (bNodeSection *, section, &node->sections) {
|
||||
if (STREQ(section->name, "Thin Film")) {
|
||||
/* Sections exclusive to Principled v2. */
|
||||
nodeSetSectionAvailability(section, is_v2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::nodes::node_shader_bsdf_principled_cc
|
||||
|
Reference in New Issue
Block a user