Nodes: Support for input/output sockets in same vertical space #112250
@ -401,26 +401,26 @@ static bool node_update_basis_buttons(
|
||||
static bool node_update_basis_socket(const bContext &C,
|
||||
bNodeTree &ntree,
|
||||
bNode &node,
|
||||
bNodeSocket &socket,
|
||||
bNodeSocket *input_socket,
|
||||
bNodeSocket *output_socket,
|
||||
uiBlock &block,
|
||||
const int &locx,
|
||||
int &locy)
|
||||
{
|
||||
if (!socket.is_visible()) {
|
||||
if ((!input_socket || !input_socket->is_visible()) &&
|
||||
(!output_socket || !output_socket->is_visible()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const int topy = locy;
|
||||
PointerRNA nodeptr = RNA_pointer_create(&ntree.id, &RNA_Node, &node);
|
||||
PointerRNA sockptr = RNA_pointer_create(&ntree.id, &RNA_NodeSocket, &socket);
|
||||
|
||||
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 bool is_multi_input = (input_socket ? input_socket->flag & SOCK_MULTI_INPUT : false);
|
||||
const float multi_input_socket_offset = is_multi_input ?
|
||||
std::max(socket.runtime->total_inputs - 2, 0) *
|
||||
std::max(input_socket->runtime->total_inputs - 2,
|
||||
0) *
|
||||
NODE_MULTI_INPUT_LINK_GAP :
|
||||
0.0f;
|
||||
locy -= multi_input_socket_offset * 0.5f;
|
||||
@ -439,39 +439,189 @@ static bool node_update_basis_socket(const bContext &C,
|
||||
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);
|
||||
PointerRNA nodeptr = RNA_pointer_create(&ntree.id, &RNA_Node, &node);
|
||||
uiLayoutSetContextPointer(row, "node", &nodeptr);
|
||||
|
||||
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));
|
||||
if (input_socket) {
|
||||
/* Context pointers for current node and socket. */
|
||||
PointerRNA sockptr = RNA_pointer_create(&ntree.id, &RNA_NodeSocket, input_socket);
|
||||
LukasTonne marked this conversation as resolved
|
||||
uiLayoutSetContextPointer(row, "socket", &sockptr);
|
||||
|
||||
node_socket_add_tooltip_in_node_editor(ntree, socket, *row);
|
||||
uiLayoutSetAlignment(row, UI_LAYOUT_ALIGN_EXPAND);
|
||||
|
||||
const char *socket_label = bke::nodeSocketLabel(input_socket);
|
||||
const char *socket_translation_context = node_socket_get_translation_context(*input_socket);
|
||||
input_socket->typeinfo->draw((bContext *)&C,
|
||||
row,
|
||||
&sockptr,
|
||||
&nodeptr,
|
||||
CTX_IFACE_(socket_translation_context, socket_label));
|
||||
}
|
||||
else {
|
||||
/* Context pointers for current node and socket. */
|
||||
PointerRNA sockptr = RNA_pointer_create(&ntree.id, &RNA_NodeSocket, output_socket);
|
||||
uiLayoutSetContextPointer(row, "socket", &sockptr);
|
||||
|
||||
/* Align output buttons to the right. */
|
||||
uiLayoutSetAlignment(row, UI_LAYOUT_ALIGN_RIGHT);
|
||||
|
||||
const char *socket_label = bke::nodeSocketLabel(output_socket);
|
||||
const char *socket_translation_context = node_socket_get_translation_context(*output_socket);
|
||||
output_socket->typeinfo->draw((bContext *)&C,
|
||||
row,
|
||||
&sockptr,
|
||||
&nodeptr,
|
||||
CTX_IFACE_(socket_translation_context, socket_label));
|
||||
}
|
||||
|
||||
if (input_socket) {
|
||||
node_socket_add_tooltip_in_node_editor(ntree, *input_socket, *row);
|
||||
/* Round the socket location to stop it from jiggling. */
|
||||
input_socket->runtime->location = float2(round(locx), round(locy - NODE_DYS));
|
||||
}
|
||||
if (output_socket) {
|
||||
node_socket_add_tooltip_in_node_editor(ntree, *output_socket, *row);
|
||||
/* Round the socket location to stop it from jiggling. */
|
||||
output_socket->runtime->location = float2(round(locx + NODE_WIDTH(node)),
|
||||
round(locy - NODE_DYS));
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
struct NodeInterfaceItemData {
|
||||
/* Declaration of a socket (only for socket items). */
|
||||
const nodes::SocketDeclaration *socket_decl = nullptr;
|
||||
bNodeSocket *input = nullptr;
|
||||
bNodeSocket *output = nullptr;
|
||||
LukasTonne marked this conversation as resolved
Outdated
Hans Goudey
commented
Since this is drawing code, it would be nice if these were const pointers. Same below. Since this is drawing code, it would be nice if these were const pointers. Same below.
Lukas Tönne
commented
I've added some comments to this struct. The I've added some comments to this struct. `state` could potentially be made const, but currently draw code also updates the `NODE_PANEL_PARENT_COLLAPSED` flag in there (hiding the panel and its content). This could be done in a separate pass, but that would just duplicate the drawing code loop.
The `runtime` data stores transient draw info like the location, so it needs to be mutable.
Hans Goudey
commented
Okay-- I'd like to move that runtime location storage to Okay-- I'd like to move that runtime location storage to `SpaceNode` runtime, but while we still reorder nodes based on selection that's too tricky.
|
||||
|
||||
/* Declaration of a panel (only for panel items). */
|
||||
const nodes::PanelDeclaration *panel_decl = nullptr;
|
||||
/* State of the panel instance on the node.
|
||||
* Mutable so that panel visibility can be updated. */
|
||||
bNodePanelState *state = nullptr;
|
||||
/* Runtime panel state for draw locations. */
|
||||
bke::bNodePanelRuntime *runtime = nullptr;
|
||||
|
||||
NodeInterfaceItemData(const nodes::SocketDeclaration *_socket_decl,
|
||||
LukasTonne marked this conversation as resolved
Outdated
Hans Goudey
commented
These are public members, which shouldn't have the These are public members, which shouldn't have the `_` suffix. Same above.
|
||||
bNodeSocket *_input,
|
||||
bNodeSocket *_output)
|
||||
: socket_decl(_socket_decl), input(_input), output(_output)
|
||||
{
|
||||
}
|
||||
NodeInterfaceItemData(const nodes::PanelDeclaration *_panel_decl,
|
||||
LukasTonne marked this conversation as resolved
Outdated
Hans Goudey
commented
It's not clear what "valid" means here It's not clear what "valid" means here
|
||||
bNodePanelState *_state,
|
||||
bke::bNodePanelRuntime *_runtime)
|
||||
: panel_decl(_panel_decl), state(_state), runtime(_runtime)
|
||||
{
|
||||
}
|
||||
|
||||
bool is_valid_socket() const
|
||||
{
|
||||
/* At least one socket pointer must be valid. */
|
||||
return this->socket_decl && (input || output);
|
||||
}
|
||||
Jacques Lucke
commented
In my experience it is often easier to just fill a vector with all the elements instead of building an iterator that has to keep track of the current iteration state explicitly. You could just build a In my experience it is often easier to just fill a vector with all the elements instead of building an iterator that has to keep track of the current iteration state explicitly. You could just build a `Vector<std::variant<NodeInterfaceSocketData, NodeInterfacePanelData>>` in one go (with some large inline buffer), and then iterate over it afterwards. That also often makes debugging easier, because it's much easier to check that the generated vector contains what you expect.
Lukas Tönne
commented
Sounds reasonable, i'll give it a try. Sounds reasonable, i'll give it a try.
Lukas Tönne
commented
I've replaced the iterator as suggested, hopefully the code is a little bit easier to understand. I've replaced the iterator as suggested, hopefully the code is a little bit easier to understand.
|
||||
|
||||
LukasTonne marked this conversation as resolved
Outdated
Hans Goudey
commented
Maybe it's worth mentioning the similarity to Maybe it's worth mentioning the similarity to `bNodeTreeInterface::foreach_item` here? Or how it's different too maybe?
|
||||
bool is_valid_panel() const
|
||||
LukasTonne marked this conversation as resolved
Outdated
Hans Goudey
commented
Instead of Instead of `struct ... { private:`, this could just be `class ...`
|
||||
{
|
||||
/* Panel can only be drawn when state data is available. */
|
||||
return this->panel_decl && this->state && this->runtime;
|
||||
}
|
||||
};
|
||||
|
||||
/* Compile relevant socket and panel pointer data into a vector.
|
||||
* This helps ensure correct pointer access in complex situations like inlined sockets.
|
||||
*/
|
||||
static Vector<NodeInterfaceItemData> node_build_item_data(bNode &node)
|
||||
{
|
||||
namespace nodes = blender::nodes;
|
||||
using ItemDeclIterator = blender::Span<nodes::ItemDeclarationPtr>::iterator;
|
||||
LukasTonne marked this conversation as resolved
Outdated
Hans Goudey
commented
Can use Can use `node.inputs()` and `node.outputs()` spans here instead of using the next and prev pointers I think
|
||||
using SocketIterator = blender::Span<bNodeSocket *>::iterator;
|
||||
using PanelStateIterator = blender::MutableSpan<bNodePanelState>::iterator;
|
||||
using PanelRuntimeIterator = blender::MutableSpan<bke::bNodePanelRuntime>::iterator;
|
||||
|
||||
BLI_assert(is_node_panels_supported(node));
|
||||
BLI_assert(node.runtime->panels.size() == node.num_panel_states);
|
||||
|
||||
ItemDeclIterator item_decl = node.declaration()->items.begin();
|
||||
SocketIterator input = node.input_sockets().begin();
|
||||
SocketIterator output = node.output_sockets().begin();
|
||||
PanelStateIterator panel_state = node.panel_states().begin();
|
||||
PanelRuntimeIterator panel_runtime = node.runtime->panels.begin();
|
||||
const ItemDeclIterator item_decl_end = node.declaration()->items.end();
|
||||
const SocketIterator input_end = node.input_sockets().end();
|
||||
const SocketIterator output_end = node.output_sockets().end();
|
||||
const PanelStateIterator panel_state_end = node.panel_states().end();
|
||||
const PanelRuntimeIterator panel_runtime_end = node.runtime->panels.end();
|
||||
|
||||
Vector<NodeInterfaceItemData> result;
|
||||
result.reserve(node.declaration()->items.size());
|
||||
|
||||
while (item_decl != item_decl_end) {
|
||||
if (const nodes::SocketDeclaration *socket_decl =
|
||||
dynamic_cast<const nodes::SocketDeclaration *>(item_decl->get()))
|
||||
{
|
||||
bNodeSocket *used_input = nullptr;
|
||||
bNodeSocket *used_output = nullptr;
|
||||
switch (socket_decl->in_out) {
|
||||
case SOCK_IN:
|
||||
BLI_assert(input != input_end);
|
||||
used_input = *input;
|
||||
++input;
|
||||
break;
|
||||
case SOCK_OUT:
|
||||
BLI_assert(output != output_end);
|
||||
used_output = *output;
|
||||
++output;
|
||||
break;
|
||||
}
|
||||
++item_decl;
|
||||
|
||||
if (socket_decl->inline_with_next && item_decl != item_decl_end) {
|
||||
/* Consume the next item as well when inlining sockets. */
|
||||
const nodes::SocketDeclaration *next_socket_decl =
|
||||
dynamic_cast<const nodes::SocketDeclaration *>(item_decl->get());
|
||||
if (next_socket_decl && next_socket_decl->in_out != socket_decl->in_out) {
|
||||
switch (next_socket_decl->in_out) {
|
||||
case SOCK_IN:
|
||||
BLI_assert(input != input_end);
|
||||
used_input = *input;
|
||||
++input;
|
||||
break;
|
||||
case SOCK_OUT:
|
||||
BLI_assert(output != output_end);
|
||||
used_output = *output;
|
||||
++output;
|
||||
break;
|
||||
}
|
||||
++item_decl;
|
||||
}
|
||||
}
|
||||
|
||||
result.append({socket_decl, used_input, used_output});
|
||||
}
|
||||
else if (const nodes::PanelDeclaration *panel_decl =
|
||||
dynamic_cast<const nodes::PanelDeclaration *>(item_decl->get()))
|
||||
{
|
||||
BLI_assert(panel_state != panel_state_end);
|
||||
BLI_assert(panel_runtime != panel_runtime_end);
|
||||
result.append({panel_decl, panel_state, panel_runtime});
|
||||
++item_decl;
|
||||
++panel_state;
|
||||
++panel_runtime;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/* 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)
|
||||
@ -480,7 +630,6 @@ static void node_update_basis_from_declaration(
|
||||
|
||||
BLI_assert(is_node_panels_supported(node));
|
||||
BLI_assert(node.runtime->panels.size() == node.num_panel_states);
|
||||
const nodes::NodeDeclaration &decl = *node.declaration();
|
||||
/* Checked at various places to avoid adding duplicate spacers without anything in between. */
|
||||
bool need_spacer_after_item = false;
|
||||
|
||||
@ -490,15 +639,10 @@ static void node_update_basis_from_declaration(
|
||||
/* Makes sure buttons are only drawn once. */
|
||||
bool buttons_drawn = false;
|
||||
|
||||
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();
|
||||
|
||||
/* 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. */
|
||||
* 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 declarations still to add. */
|
||||
LukasTonne marked this conversation as resolved
Outdated
Hans Goudey
commented
Unnecessary formatting change? Unnecessary formatting change?
Lukas Tönne
commented
Bah, clang-format keeps doing this, and it's usually in a multiline comment where it's not immediately obvious ... Bah, clang-format keeps doing this, and it's usually in a multiline comment where it's not immediately obvious ...
Lukas Tönne
commented
`make format` insists on this, guess it was wrong before 🤷
|
||||
int remaining_decls;
|
||||
@ -507,10 +651,12 @@ static void node_update_basis_from_declaration(
|
||||
/* Location data, needed to finalize the panel when all items have been added. */
|
||||
bke::bNodePanelRuntime *runtime;
|
||||
};
|
||||
|
||||
bool is_first = true;
|
||||
Stack<PanelUpdate> panel_updates;
|
||||
for (const nodes::ItemDeclarationPtr &item_decl : decl.items) {
|
||||
/* Only true for the first item in the layout. */
|
||||
bool is_first = true;
|
||||
|
||||
const Vector<NodeInterfaceItemData> item_data = node_build_item_data(node);
|
||||
for (const NodeInterfaceItemData &item : item_data) {
|
||||
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. */
|
||||
@ -519,11 +665,7 @@ static void node_update_basis_from_declaration(
|
||||
is_parent_collapsed = parent_update->is_collapsed;
|
||||
}
|
||||
|
||||
if (nodes::PanelDeclaration *panel_decl = dynamic_cast<nodes::PanelDeclaration *>(
|
||||
item_decl.get())) {
|
||||
BLI_assert(node.panel_states().contains_ptr(current_panel_state));
|
||||
BLI_assert(node.runtime->panels.as_span().contains_ptr(current_panel_runtime));
|
||||
|
||||
if (item.is_valid_panel()) {
|
||||
/* Draw buttons before the first panel. */
|
||||
if (!buttons_drawn) {
|
||||
buttons_drawn = true;
|
||||
@ -535,70 +677,58 @@ static void node_update_basis_from_declaration(
|
||||
is_first = false;
|
||||
}
|
||||
|
||||
SET_FLAG_FROM_TEST(
|
||||
current_panel_state->flag, is_parent_collapsed, NODE_PANEL_PARENT_COLLAPSED);
|
||||
SET_FLAG_FROM_TEST(item.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_child_decls, is_collapsed, current_panel_runtime});
|
||||
const bool is_collapsed = is_parent_collapsed || item.state->is_collapsed();
|
||||
panel_updates.push({item.panel_decl->num_child_decls, is_collapsed, item.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;
|
||||
item.runtime->location_y = round(locy + NODE_DYS);
|
||||
item.runtime->max_content_y = item.runtime->min_content_y = round(locy);
|
||||
}
|
||||
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);
|
||||
else if (item.is_valid_socket()) {
|
||||
if (item.input) {
|
||||
SET_FLAG_FROM_TEST(item.input->flag, is_parent_collapsed, SOCK_PANEL_COLLAPSED);
|
||||
/* Draw buttons before the first input, unless it's inline with an output. */
|
||||
if (!item.socket_decl->inline_with_next && !buttons_drawn) {
|
||||
buttons_drawn = true;
|
||||
need_spacer_after_item = node_update_basis_buttons(C, ntree, node, block, locy);
|
||||
}
|
||||
|
||||
/* Draw buttons before the first input. */
|
||||
if (!buttons_drawn) {
|
||||
buttons_drawn = true;
|
||||
need_spacer_after_item = node_update_basis_buttons(C, ntree, node, block, locy);
|
||||
if (is_parent_collapsed) {
|
||||
item.input->runtime->location = float2(locx, round(locy + NODE_DYS));
|
||||
}
|
||||
else {
|
||||
/* Space between items. */
|
||||
if (!is_first && item.input->is_visible()) {
|
||||
locy -= NODE_SOCKDY;
|
||||
}
|
||||
|
||||
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 {
|
||||
/* Space between items. */
|
||||
if (!is_first && current_input->is_visible()) {
|
||||
locy -= NODE_SOCKDY;
|
||||
}
|
||||
if (node_update_basis_socket(C, ntree, node, *current_input, block, locx, locy)) {
|
||||
is_first = false;
|
||||
need_spacer_after_item = true;
|
||||
}
|
||||
}
|
||||
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 {
|
||||
/* Space between items. */
|
||||
if (!is_first && current_output->is_visible()) {
|
||||
locy -= NODE_SOCKDY;
|
||||
}
|
||||
if (node_update_basis_socket(C, ntree, node, *current_output, block, locx, locy)) {
|
||||
is_first = false;
|
||||
need_spacer_after_item = true;
|
||||
}
|
||||
}
|
||||
current_output = current_output->next;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (item.output) {
|
||||
SET_FLAG_FROM_TEST(item.output->flag, is_parent_collapsed, SOCK_PANEL_COLLAPSED);
|
||||
if (is_parent_collapsed) {
|
||||
item.output->runtime->location = float2(round(locx + NODE_WIDTH(node)),
|
||||
round(locy + NODE_DYS));
|
||||
}
|
||||
else {
|
||||
/* Space between items. */
|
||||
if (!is_first && item.output->is_visible()) {
|
||||
locy -= NODE_SOCKDY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_parent_collapsed &&
|
||||
node_update_basis_socket(C, ntree, node, item.input, item.output, block, locx, locy))
|
||||
{
|
||||
is_first = false;
|
||||
need_spacer_after_item = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Should not happen. */
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
|
||||
/* Close parent panels that have all items added. */
|
||||
@ -646,7 +776,7 @@ static void node_update_basis_from_socket_lists(
|
||||
/* 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 (node_update_basis_socket(C, ntree, node, nullptr, socket, block, locx, locy)) {
|
||||
if (socket->next) {
|
||||
locy -= NODE_SOCKDY;
|
||||
}
|
||||
@ -665,7 +795,7 @@ static void node_update_basis_from_socket_lists(
|
||||
/* 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 (node_update_basis_socket(C, ntree, node, socket, nullptr, block, locx, locy)) {
|
||||
if (socket->next) {
|
||||
locy -= NODE_SOCKDY;
|
||||
}
|
||||
@ -904,13 +1034,14 @@ static void node_socket_draw_multi_input(const float color[4],
|
||||
const float height,
|
||||
const float2 location)
|
||||
{
|
||||
/* The other sockets are drawn with the keyframe shader. There, the outline has a base thickness
|
||||
* that can be varied but always scales with the size the socket is drawn at. Using
|
||||
/* The other sockets are drawn with the keyframe shader. There, the outline has a base
|
||||
* thickness that can be varied but always scales with the size the socket is drawn at. Using
|
||||
* `UI_SCALE_FAC` has the same effect here. It scales the outline correctly across different
|
||||
* screen DPI's and UI scales without being affected by the 'line-width'. */
|
||||
const float outline_width = NODE_SOCK_OUTLINE_SCALE * UI_SCALE_FAC;
|
||||
|
||||
/* UI_draw_roundbox draws the outline on the outer side, so compensate for the outline width. */
|
||||
/* UI_draw_roundbox draws the outline on the outer side, so compensate for the outline width.
|
||||
*/
|
||||
const rctf rect = {
|
||||
location.x - width + outline_width * 0.5f,
|
||||
location.x + width - outline_width * 0.5f,
|
||||
@ -2306,7 +2437,8 @@ static Vector<NodeExtraInfoRow> node_get_extra_info(TreeDrawContext &tree_draw_c
|
||||
row.text = node_get_execution_time_label(tree_draw_ctx, snode, node);
|
||||
if (!row.text.empty()) {
|
||||
row.tooltip = TIP_(
|
||||
"The execution time from the node tree's latest evaluation. For frame and group nodes, "
|
||||
"The execution time from the node tree's latest evaluation. For frame and group "
|
||||
"nodes, "
|
||||
"the time for all sub-nodes");
|
||||
row.icon = ICON_PREVIEW_RANGE;
|
||||
rows.append(std::move(row));
|
||||
@ -3601,7 +3733,8 @@ static void node_draw_zones(TreeDrawContext & /*tree_draw_ctx*/,
|
||||
return bounding_box_area_by_zone[a] > bounding_box_area_by_zone[b];
|
||||
});
|
||||
|
||||
/* Draw all the contour lines after to prevent them from getting hidden by overlapping zones. */
|
||||
/* Draw all the contour lines after to prevent them from getting hidden by overlapping zones.
|
||||
*/
|
||||
for (const int zone_i : zone_draw_order) {
|
||||
float zone_color[4];
|
||||
UI_GetThemeColor4fv(get_theme_id(zone_i), zone_color);
|
||||
|
@ -182,6 +182,7 @@ class SocketDeclaration : public ItemDeclaration {
|
||||
bool is_unavailable = false;
|
||||
bool is_attribute_name = false;
|
||||
bool is_default_link_socket = false;
|
||||
bool inline_with_next = false;
|
||||
|
||||
InputSocketFieldType input_field_type = InputSocketFieldType::None;
|
||||
OutputFieldDependency output_field_dependency;
|
||||
@ -246,7 +247,10 @@ class NodeDeclarationBuilder;
|
||||
|
||||
class BaseSocketDeclarationBuilder {
|
||||
protected:
|
||||
int index_ = -1;
|
||||
/* Socket builder can hold both an input and an output declaration.
|
||||
LukasTonne marked this conversation as resolved
Outdated
Hans Goudey
commented
Seems important to add comments here Seems important to add comments here
|
||||
* Each socket declaration has its own index for dependencies. */
|
||||
int index_in_ = -1;
|
||||
int index_out_ = -1;
|
||||
bool reference_pass_all_ = false;
|
||||
bool field_on_all_ = false;
|
||||
bool propagate_from_all_ = false;
|
||||
@ -258,7 +262,8 @@ class BaseSocketDeclarationBuilder {
|
||||
virtual ~BaseSocketDeclarationBuilder() = default;
|
||||
|
||||
protected:
|
||||
virtual SocketDeclaration *declaration() = 0;
|
||||
virtual SocketDeclaration *input_declaration() = 0;
|
||||
virtual SocketDeclaration *output_declaration() = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -271,44 +276,72 @@ class SocketDeclarationBuilder : public BaseSocketDeclarationBuilder {
|
||||
protected:
|
||||
using Self = typename SocketDecl::Builder;
|
||||
static_assert(std::is_base_of_v<SocketDeclaration, SocketDecl>);
|
||||
SocketDecl *decl_;
|
||||
SocketDecl *decl_in_;
|
||||
SocketDecl *decl_out_;
|
||||
LukasTonne marked this conversation as resolved
Jacques Lucke
commented
What do you think about also storing a What do you think about also storing a `Vector<SocketDecl *, 2>` here that either contains one or two decls? This could reduce some redundancy in the methods below because one could use a loop instead of repeating the code twice.
Lukas Tönne
commented
I've tried this and it doesn't meaningfully reduce the amount of code. There are also a bunch of places where an input declaration is treated differently from an output declaration and would have to check the type, for example (code from
We might eventually have cases where a single declaration builder can generate a whole bunch of sockets, like a multi-input, but i'd rather implement this when needed. I've tried this and it doesn't meaningfully reduce the amount of code. There are also a bunch of places where an input declaration is treated differently from an output declaration and would have to check the type, for example (code from `main`):
```cpp
Self &field_on_all()
{
if (decl_->in_out == SOCK_IN) {
this->supports_field();
}
else {
this->field_source();
}
field_on_all_ = true;
return *(Self *)this;
}
```
We might eventually have cases where a single declaration builder can generate a whole bunch of sockets, like a multi-input, but i'd rather implement this when needed.
|
||||
|
||||
friend class NodeDeclarationBuilder;
|
||||
|
||||
public:
|
||||
Self &hide_label(bool value = true)
|
||||
{
|
||||
decl_->hide_label = value;
|
||||
if (decl_in_) {
|
||||
decl_in_->hide_label = value;
|
||||
}
|
||||
if (decl_out_) {
|
||||
decl_out_->hide_label = value;
|
||||
}
|
||||
return *(Self *)this;
|
||||
}
|
||||
|
||||
Self &hide_value(bool value = true)
|
||||
{
|
||||
decl_->hide_value = value;
|
||||
if (decl_in_) {
|
||||
decl_in_->hide_value = value;
|
||||
}
|
||||
if (decl_out_) {
|
||||
decl_out_->hide_value = value;
|
||||
}
|
||||
return *(Self *)this;
|
||||
}
|
||||
|
||||
Self &multi_input(bool value = true)
|
||||
{
|
||||
decl_->is_multi_input = value;
|
||||
if (decl_in_) {
|
||||
decl_in_->is_multi_input = value;
|
||||
}
|
||||
return *(Self *)this;
|
||||
}
|
||||
|
||||
Self &description(std::string value = "")
|
||||
{
|
||||
decl_->description = std::move(value);
|
||||
if (decl_in_) {
|
||||
decl_in_->description = std::move(value);
|
||||
}
|
||||
if (decl_out_) {
|
||||
decl_out_->description = std::move(value);
|
||||
}
|
||||
return *(Self *)this;
|
||||
}
|
||||
|
||||
Self &translation_context(std::string value = BLT_I18NCONTEXT_DEFAULT)
|
||||
{
|
||||
decl_->translation_context = std::move(value);
|
||||
if (decl_in_) {
|
||||
decl_in_->translation_context = std::move(value);
|
||||
}
|
||||
if (decl_out_) {
|
||||
decl_out_->translation_context = std::move(value);
|
||||
}
|
||||
return *(Self *)this;
|
||||
}
|
||||
|
||||
Self &no_muted_links(bool value = true)
|
||||
{
|
||||
decl_->no_mute_links = value;
|
||||
if (decl_in_) {
|
||||
decl_in_->no_mute_links = value;
|
||||
}
|
||||
if (decl_out_) {
|
||||
decl_out_->no_mute_links = value;
|
||||
}
|
||||
return *(Self *)this;
|
||||
}
|
||||
|
||||
@ -318,26 +351,43 @@ class SocketDeclarationBuilder : public BaseSocketDeclarationBuilder {
|
||||
*/
|
||||
Self &unavailable(bool value = true)
|
||||
{
|
||||
decl_->is_unavailable = value;
|
||||
if (decl_in_) {
|
||||
decl_in_->is_unavailable = value;
|
||||
}
|
||||
if (decl_out_) {
|
||||
decl_out_->is_unavailable = value;
|
||||
}
|
||||
return *(Self *)this;
|
||||
}
|
||||
|
||||
Self &is_attribute_name(bool value = true)
|
||||
{
|
||||
decl_->is_attribute_name = value;
|
||||
if (decl_in_) {
|
||||
decl_in_->is_attribute_name = value;
|
||||
}
|
||||
if (decl_out_) {
|
||||
decl_out_->is_attribute_name = value;
|
||||
}
|
||||
return *(Self *)this;
|
||||
}
|
||||
|
||||
Self &is_default_link_socket(bool value = true)
|
||||
{
|
||||
decl_->is_default_link_socket = value;
|
||||
if (decl_in_) {
|
||||
decl_in_->is_default_link_socket = value;
|
||||
}
|
||||
if (decl_out_) {
|
||||
decl_out_->is_default_link_socket = value;
|
||||
}
|
||||
return *(Self *)this;
|
||||
}
|
||||
|
||||
/** The input socket allows passing in a field. */
|
||||
Self &supports_field()
|
||||
{
|
||||
decl_->input_field_type = InputSocketFieldType::IsSupported;
|
||||
if (decl_in_) {
|
||||
decl_in_->input_field_type = InputSocketFieldType::IsSupported;
|
||||
}
|
||||
return *(Self *)this;
|
||||
}
|
||||
|
||||
@ -350,10 +400,10 @@ class SocketDeclarationBuilder : public BaseSocketDeclarationBuilder {
|
||||
*/
|
||||
Self &field_on_all()
|
||||
{
|
||||
if (decl_->in_out == SOCK_IN) {
|
||||
if (decl_in_) {
|
||||
this->supports_field();
|
||||
}
|
||||
else {
|
||||
if (decl_out_) {
|
||||
this->field_source();
|
||||
}
|
||||
field_on_all_ = true;
|
||||
@ -367,8 +417,10 @@ class SocketDeclarationBuilder : public BaseSocketDeclarationBuilder {
|
||||
Self &implicit_field(ImplicitInputValueFn fn)
|
||||
{
|
||||
this->hide_value();
|
||||
decl_->input_field_type = InputSocketFieldType::Implicit;
|
||||
decl_->implicit_input_fn_ = std::make_unique<ImplicitInputValueFn>(std::move(fn));
|
||||
if (decl_in_) {
|
||||
decl_in_->input_field_type = InputSocketFieldType::Implicit;
|
||||
decl_in_->implicit_input_fn_ = std::make_unique<ImplicitInputValueFn>(std::move(fn));
|
||||
}
|
||||
return *(Self *)this;
|
||||
}
|
||||
|
||||
@ -391,14 +443,18 @@ class SocketDeclarationBuilder : public BaseSocketDeclarationBuilder {
|
||||
/** The output is always a field, regardless of any inputs. */
|
||||
Self &field_source()
|
||||
{
|
||||
decl_->output_field_dependency = OutputFieldDependency::ForFieldSource();
|
||||
if (decl_out_) {
|
||||
decl_out_->output_field_dependency = OutputFieldDependency::ForFieldSource();
|
||||
}
|
||||
return *(Self *)this;
|
||||
}
|
||||
|
||||
/** The output is a field if any of the inputs are a field. */
|
||||
Self &dependent_field()
|
||||
{
|
||||
decl_->output_field_dependency = OutputFieldDependency::ForDependentField();
|
||||
if (decl_out_) {
|
||||
decl_out_->output_field_dependency = OutputFieldDependency::ForDependentField();
|
||||
}
|
||||
this->reference_pass_all();
|
||||
return *(Self *)this;
|
||||
}
|
||||
@ -407,8 +463,10 @@ class SocketDeclarationBuilder : public BaseSocketDeclarationBuilder {
|
||||
Self &dependent_field(Vector<int> input_dependencies)
|
||||
{
|
||||
this->reference_pass(input_dependencies);
|
||||
decl_->output_field_dependency = OutputFieldDependency::ForPartiallyDependentField(
|
||||
std::move(input_dependencies));
|
||||
if (decl_out_) {
|
||||
decl_out_->output_field_dependency = OutputFieldDependency::ForPartiallyDependentField(
|
||||
std::move(input_dependencies));
|
||||
}
|
||||
return *(Self *)this;
|
||||
}
|
||||
|
||||
@ -446,7 +504,12 @@ class SocketDeclarationBuilder : public BaseSocketDeclarationBuilder {
|
||||
|
||||
Self &compositor_realization_options(CompositorInputRealizationOptions value)
|
||||
{
|
||||
decl_->compositor_realization_options_ = value;
|
||||
if (decl_in_) {
|
||||
decl_in_->compositor_realization_options_ = value;
|
||||
}
|
||||
if (decl_out_) {
|
||||
decl_out_->compositor_realization_options_ = value;
|
||||
}
|
||||
return *(Self *)this;
|
||||
}
|
||||
|
||||
@ -454,7 +517,12 @@ class SocketDeclarationBuilder : public BaseSocketDeclarationBuilder {
|
||||
* realtime_compositor::InputDescriptor for more information. */
|
||||
Self &compositor_domain_priority(int priority)
|
||||
{
|
||||
decl_->compositor_domain_priority_ = priority;
|
||||
if (decl_in_) {
|
||||
decl_in_->compositor_domain_priority_ = priority;
|
||||
}
|
||||
if (decl_out_) {
|
||||
decl_out_->compositor_domain_priority_ = priority;
|
||||
}
|
||||
return *(Self *)this;
|
||||
}
|
||||
|
||||
@ -462,7 +530,12 @@ class SocketDeclarationBuilder : public BaseSocketDeclarationBuilder {
|
||||
* realtime_compositor::InputDescriptor for more information. */
|
||||
Self &compositor_expects_single_value(bool value = true)
|
||||
{
|
||||
decl_->compositor_expects_single_value_ = value;
|
||||
if (decl_in_) {
|
||||
decl_in_->compositor_expects_single_value_ = value;
|
||||
}
|
||||
if (decl_out_) {
|
||||
decl_out_->compositor_expects_single_value_ = value;
|
||||
}
|
||||
return *(Self *)this;
|
||||
}
|
||||
|
||||
@ -474,14 +547,23 @@ class SocketDeclarationBuilder : public BaseSocketDeclarationBuilder {
|
||||
*/
|
||||
Self &make_available(std::function<void(bNode &)> fn)
|
||||
{
|
||||
decl_->make_available_fn_ = std::move(fn);
|
||||
if (decl_in_) {
|
||||
decl_in_->make_available_fn_ = std::move(fn);
|
||||
}
|
||||
if (decl_out_) {
|
||||
decl_out_->make_available_fn_ = std::move(fn);
|
||||
}
|
||||
return *(Self *)this;
|
||||
}
|
||||
|
||||
protected:
|
||||
SocketDeclaration *declaration() override
|
||||
SocketDeclaration *input_declaration() override
|
||||
{
|
||||
return decl_;
|
||||
return decl_in_;
|
||||
}
|
||||
SocketDeclaration *output_declaration() override
|
||||
{
|
||||
return decl_out_;
|
||||
}
|
||||
};
|
||||
|
||||
@ -541,6 +623,10 @@ class PanelDeclarationBuilder {
|
||||
typename DeclType::Builder &add_input(StringRef name, StringRef identifier = "");
|
||||
template<typename DeclType>
|
||||
typename DeclType::Builder &add_output(StringRef name, StringRef identifier = "");
|
||||
template<typename DeclType>
|
||||
typename DeclType::Builder &add_input_output(StringRef name,
|
||||
StringRef identifier_in = "",
|
||||
StringRef identifier_out = "");
|
||||
};
|
||||
|
||||
using PanelDeclarationPtr = std::unique_ptr<PanelDeclaration>;
|
||||
@ -583,8 +669,7 @@ class NodeDeclaration {
|
||||
class NodeDeclarationBuilder {
|
||||
private:
|
||||
NodeDeclaration &declaration_;
|
||||
Vector<std::unique_ptr<BaseSocketDeclarationBuilder>> input_builders_;
|
||||
Vector<std::unique_ptr<BaseSocketDeclarationBuilder>> output_builders_;
|
||||
Vector<std::unique_ptr<BaseSocketDeclarationBuilder>> socket_builders_;
|
||||
Vector<std::unique_ptr<PanelDeclarationBuilder>> panel_builders_;
|
||||
bool is_function_node_ = false;
|
||||
|
||||
@ -611,6 +696,10 @@ class NodeDeclarationBuilder {
|
||||
typename DeclType::Builder &add_input(StringRef name, StringRef identifier = "");
|
||||
template<typename DeclType>
|
||||
typename DeclType::Builder &add_output(StringRef name, StringRef identifier = "");
|
||||
template<typename DeclType>
|
||||
typename DeclType::Builder &add_input_output(StringRef name,
|
||||
StringRef identifier_in = "",
|
||||
StringRef identifier_out = "");
|
||||
PanelDeclarationBuilder &add_panel(StringRef name, int identifier = -1);
|
||||
|
||||
aal::RelationsInNode &get_anonymous_attribute_relations()
|
||||
@ -622,9 +711,12 @@ class NodeDeclarationBuilder {
|
||||
}
|
||||
|
||||
private:
|
||||
/* Note: in_out can be a combination of SOCK_IN and SOCK_OUT.
|
||||
* The generated socket declarations only have a single flag set. */
|
||||
template<typename DeclType>
|
||||
typename DeclType::Builder &add_socket(StringRef name,
|
||||
StringRef identifier,
|
||||
StringRef identifier_in,
|
||||
StringRef identifier_out,
|
||||
eNodeSocketInOut in_out);
|
||||
|
||||
/* Mark the most recent builder as 'complete' when changing builders
|
||||
@ -652,7 +744,7 @@ typename SocketDeclarationBuilder<SocketDecl>::Self &SocketDeclarationBuilder<
|
||||
for (const int from_input : input_indices) {
|
||||
aal::ReferenceRelation relation;
|
||||
relation.from_field_input = from_input;
|
||||
relation.to_field_output = index_;
|
||||
relation.to_field_output = index_out_;
|
||||
relations.reference_relations.append(relation);
|
||||
}
|
||||
return *(Self *)this;
|
||||
@ -663,20 +755,20 @@ typename SocketDeclarationBuilder<SocketDecl>::Self &SocketDeclarationBuilder<
|
||||
SocketDecl>::field_on(const Span<int> indices)
|
||||
{
|
||||
aal::RelationsInNode &relations = node_decl_builder_->get_anonymous_attribute_relations();
|
||||
if (decl_->in_out == SOCK_IN) {
|
||||
if (decl_in_) {
|
||||
this->supports_field();
|
||||
for (const int input_index : indices) {
|
||||
aal::EvalRelation relation;
|
||||
relation.field_input = index_;
|
||||
relation.field_input = index_in_;
|
||||
relation.geometry_input = input_index;
|
||||
relations.eval_relations.append(relation);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (decl_out_) {
|
||||
this->field_source();
|
||||
for (const int output_index : indices) {
|
||||
aal::AvailableRelation relation;
|
||||
relation.field_output = index_;
|
||||
relation.field_output = index_out_;
|
||||
relation.geometry_output = output_index;
|
||||
relations.available_relations.append(relation);
|
||||
}
|
||||
@ -803,7 +895,7 @@ typename DeclType::Builder &PanelDeclarationBuilder::add_input(StringRef name,
|
||||
return dummy_builder;
|
||||
}
|
||||
++this->decl_->num_child_decls;
|
||||
return node_decl_builder_->add_socket<DeclType>(name, identifier, SOCK_IN);
|
||||
return node_decl_builder_->add_socket<DeclType>(name, identifier, "", SOCK_IN);
|
||||
}
|
||||
|
||||
template<typename DeclType>
|
||||
@ -816,7 +908,22 @@ typename DeclType::Builder &PanelDeclarationBuilder::add_output(StringRef name,
|
||||
return dummy_builder;
|
||||
}
|
||||
++this->decl_->num_child_decls;
|
||||
return node_decl_builder_->add_socket<DeclType>(name, identifier, SOCK_OUT);
|
||||
return node_decl_builder_->add_socket<DeclType>(name, "", identifier, SOCK_OUT);
|
||||
}
|
||||
|
||||
template<typename DeclType>
|
||||
typename DeclType::Builder &PanelDeclarationBuilder::add_input_output(StringRef name,
|
||||
StringRef identifier_in,
|
||||
StringRef identifier_out)
|
||||
{
|
||||
if (is_complete_) {
|
||||
static typename DeclType::Builder dummy_builder = {};
|
||||
BLI_assert_unreachable();
|
||||
return dummy_builder;
|
||||
}
|
||||
++this->decl_->num_child_decls;
|
||||
return node_decl_builder_->add_socket<DeclType>(
|
||||
name, identifier_in, identifier_out, SOCK_IN | SOCK_OUT);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
@ -840,7 +947,7 @@ inline typename DeclType::Builder &NodeDeclarationBuilder::add_input(StringRef n
|
||||
StringRef identifier)
|
||||
{
|
||||
set_active_panel_builder(nullptr);
|
||||
return this->add_socket<DeclType>(name, identifier, SOCK_IN);
|
||||
return this->add_socket<DeclType>(name, identifier, "", SOCK_IN);
|
||||
}
|
||||
|
||||
template<typename DeclType>
|
||||
@ -848,34 +955,50 @@ inline typename DeclType::Builder &NodeDeclarationBuilder::add_output(StringRef
|
||||
StringRef identifier)
|
||||
{
|
||||
set_active_panel_builder(nullptr);
|
||||
return this->add_socket<DeclType>(name, identifier, SOCK_OUT);
|
||||
return this->add_socket<DeclType>(name, "", identifier, SOCK_OUT);
|
||||
}
|
||||
|
||||
template<typename DeclType>
|
||||
inline typename DeclType::Builder &NodeDeclarationBuilder::add_input_output(
|
||||
StringRef name, StringRef identifier_in, StringRef identifier_out)
|
||||
{
|
||||
set_active_panel_builder(nullptr);
|
||||
return this->add_socket<DeclType>(name, identifier_in, identifier_out, SOCK_IN | SOCK_OUT);
|
||||
}
|
||||
|
||||
template<typename DeclType>
|
||||
inline typename DeclType::Builder &NodeDeclarationBuilder::add_socket(StringRef name,
|
||||
StringRef identifier,
|
||||
StringRef identifier_in,
|
||||
StringRef identifier_out,
|
||||
eNodeSocketInOut in_out)
|
||||
{
|
||||
static_assert(std::is_base_of_v<SocketDeclaration, DeclType>);
|
||||
using Builder = typename DeclType::Builder;
|
||||
|
||||
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>();
|
||||
socket_decl_builder->decl_ = &*socket_decl;
|
||||
|
||||
socket_decl_builder->node_decl_builder_ = this;
|
||||
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(socket_decl.get());
|
||||
declaration_.items.append(std::move(socket_decl));
|
||||
|
||||
if (in_out & SOCK_IN) {
|
||||
std::unique_ptr<DeclType> socket_decl = std::make_unique<DeclType>();
|
||||
socket_decl_builder->decl_in_ = &*socket_decl;
|
||||
socket_decl->name = name;
|
||||
socket_decl->identifier = identifier_in.is_empty() ? name : identifier_in;
|
||||
socket_decl->in_out = SOCK_IN;
|
||||
socket_decl_builder->index_in_ = declaration_.inputs.append_and_get_index(socket_decl.get());
|
||||
declaration_.items.append(std::move(socket_decl));
|
||||
}
|
||||
if (in_out & SOCK_OUT) {
|
||||
std::unique_ptr<DeclType> socket_decl = std::make_unique<DeclType>();
|
||||
socket_decl_builder->decl_out_ = &*socket_decl;
|
||||
socket_decl->name = name;
|
||||
socket_decl->identifier = identifier_out.is_empty() ? name : identifier_out;
|
||||
socket_decl->in_out = SOCK_OUT;
|
||||
socket_decl_builder->index_out_ = declaration_.outputs.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));
|
||||
socket_builders_.append(std::move(socket_decl_builder));
|
||||
|
||||
return socket_decl_builder_ref;
|
||||
}
|
||||
|
@ -284,25 +284,45 @@ class Custom : public SocketDeclaration {
|
||||
|
||||
inline FloatBuilder &FloatBuilder::min(const float value)
|
||||
{
|
||||
decl_->soft_min_value = value;
|
||||
if (decl_in_) {
|
||||
decl_in_->soft_min_value = value;
|
||||
}
|
||||
if (decl_out_) {
|
||||
decl_out_->soft_min_value = value;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline FloatBuilder &FloatBuilder::max(const float value)
|
||||
{
|
||||
decl_->soft_max_value = value;
|
||||
if (decl_in_) {
|
||||
decl_in_->soft_max_value = value;
|
||||
}
|
||||
if (decl_out_) {
|
||||
decl_out_->soft_max_value = value;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline FloatBuilder &FloatBuilder::default_value(const float value)
|
||||
{
|
||||
decl_->default_value = value;
|
||||
if (decl_in_) {
|
||||
decl_in_->default_value = value;
|
||||
}
|
||||
if (decl_out_) {
|
||||
decl_out_->default_value = value;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline FloatBuilder &FloatBuilder::subtype(PropertySubType subtype)
|
||||
{
|
||||
decl_->subtype = subtype;
|
||||
if (decl_in_) {
|
||||
decl_in_->subtype = subtype;
|
||||
}
|
||||
if (decl_out_) {
|
||||
decl_out_->subtype = subtype;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -314,25 +334,45 @@ inline FloatBuilder &FloatBuilder::subtype(PropertySubType subtype)
|
||||
|
||||
inline IntBuilder &IntBuilder::min(const int value)
|
||||
{
|
||||
decl_->soft_min_value = value;
|
||||
if (decl_in_) {
|
||||
decl_in_->soft_min_value = value;
|
||||
}
|
||||
if (decl_out_) {
|
||||
decl_out_->soft_min_value = value;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline IntBuilder &IntBuilder::max(const int value)
|
||||
{
|
||||
decl_->soft_max_value = value;
|
||||
if (decl_in_) {
|
||||
decl_in_->soft_max_value = value;
|
||||
}
|
||||
if (decl_out_) {
|
||||
decl_out_->soft_max_value = value;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline IntBuilder &IntBuilder::default_value(const int value)
|
||||
{
|
||||
decl_->default_value = value;
|
||||
if (decl_in_) {
|
||||
decl_in_->default_value = value;
|
||||
}
|
||||
if (decl_out_) {
|
||||
decl_out_->default_value = value;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline IntBuilder &IntBuilder::subtype(PropertySubType subtype)
|
||||
{
|
||||
decl_->subtype = subtype;
|
||||
if (decl_in_) {
|
||||
decl_in_->subtype = subtype;
|
||||
}
|
||||
if (decl_out_) {
|
||||
decl_out_->subtype = subtype;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -344,31 +384,56 @@ inline IntBuilder &IntBuilder::subtype(PropertySubType subtype)
|
||||
|
||||
inline VectorBuilder &VectorBuilder::default_value(const float3 value)
|
||||
{
|
||||
decl_->default_value = value;
|
||||
if (decl_in_) {
|
||||
decl_in_->default_value = value;
|
||||
}
|
||||
if (decl_out_) {
|
||||
decl_out_->default_value = value;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline VectorBuilder &VectorBuilder::subtype(PropertySubType subtype)
|
||||
{
|
||||
decl_->subtype = subtype;
|
||||
if (decl_in_) {
|
||||
decl_in_->subtype = subtype;
|
||||
}
|
||||
if (decl_out_) {
|
||||
decl_out_->subtype = subtype;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline VectorBuilder &VectorBuilder::min(const float min)
|
||||
{
|
||||
decl_->soft_min_value = min;
|
||||
if (decl_in_) {
|
||||
decl_in_->soft_min_value = min;
|
||||
}
|
||||
if (decl_out_) {
|
||||
decl_out_->soft_min_value = min;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline VectorBuilder &VectorBuilder::max(const float max)
|
||||
{
|
||||
decl_->soft_max_value = max;
|
||||
if (decl_in_) {
|
||||
decl_in_->soft_max_value = max;
|
||||
}
|
||||
if (decl_out_) {
|
||||
decl_out_->soft_max_value = max;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline VectorBuilder &VectorBuilder::compact()
|
||||
{
|
||||
decl_->compact = true;
|
||||
if (decl_in_) {
|
||||
decl_in_->compact = true;
|
||||
}
|
||||
if (decl_out_) {
|
||||
decl_out_->compact = true;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -380,7 +445,12 @@ inline VectorBuilder &VectorBuilder::compact()
|
||||
|
||||
inline BoolBuilder &BoolBuilder::default_value(const bool value)
|
||||
{
|
||||
decl_->default_value = value;
|
||||
if (decl_in_) {
|
||||
decl_in_->default_value = value;
|
||||
}
|
||||
if (decl_out_) {
|
||||
decl_out_->default_value = value;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -392,7 +462,12 @@ inline BoolBuilder &BoolBuilder::default_value(const bool value)
|
||||
|
||||
inline ColorBuilder &ColorBuilder::default_value(const ColorGeometry4f value)
|
||||
{
|
||||
decl_->default_value = value;
|
||||
if (decl_in_) {
|
||||
decl_in_->default_value = value;
|
||||
}
|
||||
if (decl_out_) {
|
||||
decl_out_->default_value = value;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -404,7 +479,12 @@ inline ColorBuilder &ColorBuilder::default_value(const ColorGeometry4f value)
|
||||
|
||||
inline StringBuilder &StringBuilder::default_value(std::string value)
|
||||
{
|
||||
decl_->default_value = std::move(value);
|
||||
if (decl_in_) {
|
||||
decl_in_->default_value = std::move(value);
|
||||
}
|
||||
if (decl_out_) {
|
||||
decl_out_->default_value = std::move(value);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -416,7 +496,12 @@ inline StringBuilder &StringBuilder::default_value(std::string value)
|
||||
|
||||
inline RotationBuilder &RotationBuilder::default_value(const math::EulerXYZ &value)
|
||||
{
|
||||
decl_->default_value = value;
|
||||
if (decl_in_) {
|
||||
decl_in_->default_value = value;
|
||||
}
|
||||
if (decl_out_) {
|
||||
decl_out_->default_value = value;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
@ -378,19 +378,26 @@ void node_group_declare_dynamic(const bNodeTree & /*node_tree*/,
|
||||
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));
|
||||
}
|
||||
|
||||
SocketDeclarationPtr input_decl = (socket.flag & NODE_INTERFACE_SOCKET_INPUT) ?
|
||||
declaration_for_interface_socket(
|
||||
*group, socket, SOCK_IN) :
|
||||
nullptr;
|
||||
SocketDeclarationPtr output_decl = (socket.flag & NODE_INTERFACE_SOCKET_OUTPUT) ?
|
||||
declaration_for_interface_socket(
|
||||
*group, socket, SOCK_OUT) :
|
||||
nullptr;
|
||||
/* Inline with the output socket if using input+output mode. */
|
||||
if (input_decl && output_decl) {
|
||||
input_decl->inline_with_next = true;
|
||||
}
|
||||
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));
|
||||
}
|
||||
if (input_decl) {
|
||||
r_declaration.inputs.append(input_decl.get());
|
||||
r_declaration.items.append(std::move(input_decl));
|
||||
}
|
||||
if (output_decl) {
|
||||
r_declaration.outputs.append(output_decl.get());
|
||||
r_declaration.items.append(std::move(output_decl));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -34,19 +34,26 @@ void build_node_declaration_dynamic(const bNodeTree &node_tree,
|
||||
|
||||
void NodeDeclarationBuilder::finalize()
|
||||
{
|
||||
BLI_assert(declaration_.is_valid());
|
||||
/* Declarations generating both input and output should align these sockets. */
|
||||
for (std::unique_ptr<BaseSocketDeclarationBuilder> &socket_builder : socket_builders_) {
|
||||
if (socket_builder->input_declaration() && socket_builder->output_declaration()) {
|
||||
socket_builder->input_declaration()->inline_with_next = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_function_node_) {
|
||||
for (std::unique_ptr<BaseSocketDeclarationBuilder> &socket_builder : input_builders_) {
|
||||
SocketDeclaration &socket_decl = *socket_builder->declaration();
|
||||
if (socket_decl.input_field_type != InputSocketFieldType::Implicit) {
|
||||
socket_decl.input_field_type = InputSocketFieldType::IsSupported;
|
||||
for (std::unique_ptr<BaseSocketDeclarationBuilder> &socket_builder : socket_builders_) {
|
||||
if (SocketDeclaration *socket_decl = socket_builder->input_declaration()) {
|
||||
if (socket_decl->input_field_type != InputSocketFieldType::Implicit) {
|
||||
socket_decl->input_field_type = InputSocketFieldType::IsSupported;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (std::unique_ptr<BaseSocketDeclarationBuilder> &socket_builder : output_builders_) {
|
||||
SocketDeclaration &socket_decl = *socket_builder->declaration();
|
||||
socket_decl.output_field_dependency = OutputFieldDependency::ForDependentField();
|
||||
socket_builder->reference_pass_all_ = true;
|
||||
for (std::unique_ptr<BaseSocketDeclarationBuilder> &socket_builder : socket_builders_) {
|
||||
if (SocketDeclaration *socket_decl = socket_builder->output_declaration()) {
|
||||
socket_decl->output_field_dependency = OutputFieldDependency::ForDependentField();
|
||||
socket_builder->reference_pass_all_ = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -63,26 +70,34 @@ void NodeDeclarationBuilder::finalize()
|
||||
}
|
||||
}
|
||||
|
||||
for (std::unique_ptr<BaseSocketDeclarationBuilder> &socket_builder : input_builders_) {
|
||||
for (std::unique_ptr<BaseSocketDeclarationBuilder> &socket_builder : socket_builders_) {
|
||||
if (!socket_builder->input_declaration()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (socket_builder->field_on_all_) {
|
||||
aal::RelationsInNode &relations = this->get_anonymous_attribute_relations();
|
||||
const int field_input = socket_builder->index_;
|
||||
const int field_input = socket_builder->index_in_;
|
||||
for (const int geometry_input : geometry_inputs) {
|
||||
relations.eval_relations.append({field_input, geometry_input});
|
||||
}
|
||||
}
|
||||
}
|
||||
for (std::unique_ptr<BaseSocketDeclarationBuilder> &socket_builder : output_builders_) {
|
||||
for (std::unique_ptr<BaseSocketDeclarationBuilder> &socket_builder : socket_builders_) {
|
||||
if (!socket_builder->output_declaration()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (socket_builder->field_on_all_) {
|
||||
aal::RelationsInNode &relations = this->get_anonymous_attribute_relations();
|
||||
const int field_output = socket_builder->index_;
|
||||
const int field_output = socket_builder->index_out_;
|
||||
for (const int geometry_output : geometry_outputs) {
|
||||
relations.available_relations.append({field_output, geometry_output});
|
||||
}
|
||||
}
|
||||
if (socket_builder->reference_pass_all_) {
|
||||
aal::RelationsInNode &relations = this->get_anonymous_attribute_relations();
|
||||
const int field_output = socket_builder->index_;
|
||||
const int field_output = socket_builder->index_out_;
|
||||
for (const int input_i : declaration_.inputs.index_range()) {
|
||||
SocketDeclaration &input_socket_decl = *declaration_.inputs[input_i];
|
||||
if (input_socket_decl.input_field_type != InputSocketFieldType::None) {
|
||||
@ -92,12 +107,14 @@ void NodeDeclarationBuilder::finalize()
|
||||
}
|
||||
if (socket_builder->propagate_from_all_) {
|
||||
aal::RelationsInNode &relations = this->get_anonymous_attribute_relations();
|
||||
const int geometry_output = socket_builder->index_;
|
||||
const int geometry_output = socket_builder->index_out_;
|
||||
for (const int geometry_input : geometry_inputs) {
|
||||
relations.propagate_relations.append({geometry_input, geometry_output});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BLI_assert(declaration_.is_valid());
|
||||
}
|
||||
|
||||
void NodeDeclarationBuilder::set_active_panel_builder(const PanelDeclarationBuilder *panel_builder)
|
||||
@ -190,13 +207,17 @@ bool NodeDeclaration::is_valid() const
|
||||
return false;
|
||||
}
|
||||
|
||||
if (state.socket_in_out == SOCK_OUT && socket_decl->in_out == SOCK_IN) {
|
||||
/* Start of input sockets. */
|
||||
state.socket_in_out = SOCK_IN;
|
||||
}
|
||||
if (socket_decl->in_out != state.socket_in_out) {
|
||||
std::cout << "Output socket added after input socket" << std::endl;
|
||||
return false;
|
||||
/* Check for consistent outputs.., inputs.. blocks.
|
||||
* Ignored for sockets that are inline pairs. */
|
||||
if (!socket_decl->inline_with_next) {
|
||||
if (state.socket_in_out == SOCK_OUT && socket_decl->in_out == SOCK_IN) {
|
||||
/* Start of input sockets. */
|
||||
state.socket_in_out = SOCK_IN;
|
||||
}
|
||||
if (socket_decl->in_out != state.socket_in_out) {
|
||||
std::cout << "Output socket added after input socket" << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Item counting for the panels, but ignore for root items. */
|
||||
|
@ -570,26 +570,46 @@ bool Geometry::only_instances() const
|
||||
|
||||
GeometryBuilder &GeometryBuilder::supported_type(bke::GeometryComponent::Type supported_type)
|
||||
{
|
||||
decl_->supported_types_ = {supported_type};
|
||||
if (decl_in_) {
|
||||
decl_in_->supported_types_ = {supported_type};
|
||||
}
|
||||
if (decl_out_) {
|
||||
decl_out_->supported_types_ = {supported_type};
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
GeometryBuilder &GeometryBuilder::supported_type(
|
||||
blender::Vector<bke::GeometryComponent::Type> supported_types)
|
||||
{
|
||||
decl_->supported_types_ = std::move(supported_types);
|
||||
if (decl_in_) {
|
||||
decl_in_->supported_types_ = supported_types;
|
||||
}
|
||||
if (decl_out_) {
|
||||
decl_out_->supported_types_ = supported_types;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
GeometryBuilder &GeometryBuilder::only_realized_data(bool value)
|
||||
{
|
||||
decl_->only_realized_data_ = value;
|
||||
if (decl_in_) {
|
||||
decl_in_->only_realized_data_ = value;
|
||||
}
|
||||
if (decl_out_) {
|
||||
decl_out_->only_realized_data_ = value;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
GeometryBuilder &GeometryBuilder::only_instances(bool value)
|
||||
{
|
||||
decl_->only_instances_ = value;
|
||||
if (decl_in_) {
|
||||
decl_in_->only_instances_ = value;
|
||||
}
|
||||
if (decl_out_) {
|
||||
decl_out_->only_instances_ = value;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
Might as well set the node constext pointer only once above