2023-08-16 00:20:26 +10:00
|
|
|
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
2023-05-31 16:19:06 +02:00
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
2012-08-01 19:11:17 +00:00
|
|
|
|
2019-02-18 08:08:12 +11:00
|
|
|
/** \file
|
|
|
|
|
* \ingroup spnode
|
2012-08-01 19:11:17 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
|
|
2018-01-16 12:06:22 +01:00
|
|
|
#include "DNA_anim_types.h"
|
2012-08-01 19:11:17 +00:00
|
|
|
#include "DNA_node_types.h"
|
|
|
|
|
|
2015-08-01 17:39:48 +02:00
|
|
|
#include "BLI_easing.h"
|
Cleanup: reduce amount of math-related includes
Using ClangBuildAnalyzer on the whole Blender build, it was pointing
out that BLI_math.h is the heaviest "header hub" (i.e. non tiny file
that is included a lot).
However, there's very little (actually zero) source files in Blender
that need "all the math" (base, colors, vectors, matrices,
quaternions, intersection, interpolation, statistics, solvers and
time). A common use case is source files needing just vectors, or
just vectors & matrices, or just colors etc. Actually, 181 files
were including the whole math thing without needing it at all.
This change removes BLI_math.h completely, and instead in all the
places that need it, includes BLI_math_vector.h or BLI_math_color.h
and so on.
Change from that:
- BLI_math_color.h was included 1399 times -> now 408 (took 114.0sec
to parse -> now 36.3sec)
- BLI_simd.h 1403 -> 418 (109.7sec -> 34.9sec).
Full rebuild of Blender (Apple M1, Xcode, RelWithDebInfo) is not
affected much (342sec -> 334sec). Most of benefit would be when
someone's changing BLI_simd.h or BLI_math_color.h or similar files,
that now there's 3x fewer files result in a recompile.
Pull Request #110944
2023-08-09 11:39:20 +03:00
|
|
|
#include "BLI_math_geom.h"
|
2022-09-03 17:57:53 -05:00
|
|
|
#include "BLI_stack.hh"
|
2012-08-01 19:11:17 +00:00
|
|
|
|
2020-04-03 13:07:36 +02:00
|
|
|
#include "BKE_anim_data.h"
|
2023-11-16 11:41:55 +01:00
|
|
|
#include "BKE_context.hh"
|
|
|
|
|
#include "BKE_curve.hh"
|
2020-02-10 12:58:59 +01:00
|
|
|
#include "BKE_lib_id.h"
|
2023-12-01 19:43:16 +01:00
|
|
|
#include "BKE_main.hh"
|
2023-05-15 15:14:22 +02:00
|
|
|
#include "BKE_node.hh"
|
2022-08-31 12:15:57 +02:00
|
|
|
#include "BKE_node_runtime.hh"
|
2023-11-16 11:41:55 +01:00
|
|
|
#include "BKE_node_tree_update.hh"
|
2023-09-25 17:48:21 -04:00
|
|
|
#include "BKE_screen.hh"
|
2012-08-01 19:11:17 +00:00
|
|
|
|
2022-11-25 12:18:10 +01:00
|
|
|
#include "ED_node.hh" /* own include */
|
2023-08-05 02:57:52 +02:00
|
|
|
#include "ED_render.hh"
|
2023-08-04 23:11:22 +02:00
|
|
|
#include "ED_screen.hh"
|
2023-08-05 02:57:52 +02:00
|
|
|
#include "ED_space_api.hh"
|
2023-08-04 23:11:22 +02:00
|
|
|
#include "ED_util.hh"
|
Geometry Nodes: viewport preview
This adds support for showing geometry passed to the Viewer in the 3d
viewport (instead of just in the spreadsheet). The "viewer geometry"
bypasses the group output. So it is not necessary to change the final
output of the node group to be able to see the intermediate geometry.
**Activation and deactivation of a viewer node**
* A viewer node is activated by clicking on it.
* Ctrl+shift+click on any node/socket connects it to the viewer and
makes it active.
* Ctrl+shift+click in empty space deactivates the active viewer.
* When the active viewer is not visible anymore (e.g. another object
is selected, or the current node group is exit), it is deactivated.
* Clicking on the icon in the header of the Viewer node toggles whether
its active or not.
**Pinning**
* The spreadsheet still allows pinning the active viewer as before.
When pinned, the spreadsheet still references the viewer node even
when it becomes inactive.
* The viewport does not support pinning at the moment. It always shows
the active viewer.
**Attribute**
* When a field is linked to the second input of the viewer node it is
displayed as an overlay in the viewport.
* When possible the correct domain for the attribute is determined
automatically. This does not work in all cases. It falls back to the
face corner domain on meshes and the point domain on curves. When
necessary, the domain can be picked manually.
* The spreadsheet now only shows the "Viewer" column for the domain
that is selected in the Viewer node.
* Instance attributes are visualized as a constant color per instance.
**Viewport Options**
* The attribute overlay opacity can be controlled with the "Viewer Node"
setting in the overlays popover.
* A viewport can be configured not to show intermediate viewer-geometry
by disabling the "Viewer Node" option in the "View" menu.
**Implementation Details**
* The "spreadsheet context path" was generalized to a "viewer path" that
is used in more places now.
* The viewer node itself determines the attribute domain, evaluates the
field and stores the result in a `.viewer` attribute.
* A new "viewer attribute' overlay displays the data from the `.viewer`
attribute.
* The ground truth for the active viewer node is stored in the workspace
now. Node editors, spreadsheets and viewports retrieve the active
viewer from there unless they are pinned.
* The depsgraph object iterator has a new "viewer path" setting. When set,
the viewed geometry of the corresponding object is part of the iterator
instead of the final evaluated geometry.
* To support the instance attribute overlay `DupliObject` was extended
to contain the information necessary for drawing the overlay.
* The ctrl+shift+click operator has been refactored so that it can make
existing links to viewers active again.
* The auto-domain-detection in the Viewer node works by checking the
"preferred domain" for every field input. If there is not exactly one
preferred domain, the fallback is used.
Known limitations:
* Loose edges of meshes don't have the attribute overlay. This could be
added separately if necessary.
* Some attributes are hard to visualize as a color directly. For example,
the values might have to be normalized or some should be drawn as arrays.
For now, we encourage users to build node groups that generate appropriate
viewer-geometry. We might include some of that functionality in future versions.
Support for displaying attribute values as text in the viewport is planned as well.
* There seems to be an issue with the attribute overlay for pointclouds on
nvidia gpus, to be investigated.
Differential Revision: https://developer.blender.org/D15954
2022-09-28 17:54:59 +02:00
|
|
|
#include "ED_viewer_path.hh"
|
2012-08-01 19:11:17 +00:00
|
|
|
|
2023-08-10 22:40:27 +02:00
|
|
|
#include "RNA_access.hh"
|
|
|
|
|
#include "RNA_define.hh"
|
2022-03-14 16:54:46 +01:00
|
|
|
#include "RNA_prototypes.h"
|
2012-08-01 19:11:17 +00:00
|
|
|
|
2023-09-22 03:18:17 +02:00
|
|
|
#include "DEG_depsgraph.hh"
|
2021-07-05 10:46:00 +02:00
|
|
|
|
2023-08-04 23:11:22 +02:00
|
|
|
#include "WM_api.hh"
|
|
|
|
|
#include "WM_types.hh"
|
2012-08-01 19:11:17 +00:00
|
|
|
|
2021-12-15 09:51:57 -06:00
|
|
|
#include "GPU_state.h"
|
|
|
|
|
|
2023-08-05 02:57:52 +02:00
|
|
|
#include "UI_interface_icons.hh"
|
|
|
|
|
#include "UI_resources.hh"
|
|
|
|
|
#include "UI_view2d.hh"
|
2012-08-01 19:11:17 +00:00
|
|
|
|
2015-08-16 17:32:01 +10:00
|
|
|
#include "BLT_translation.h"
|
2014-04-29 11:28:16 +02:00
|
|
|
|
2021-11-04 14:44:21 +01:00
|
|
|
#include "NOD_node_declaration.hh"
|
2023-11-21 10:43:25 +01:00
|
|
|
#include "NOD_socket.hh"
|
2021-12-15 09:51:57 -06:00
|
|
|
#include "NOD_socket_declarations.hh"
|
|
|
|
|
#include "NOD_socket_declarations_geometry.hh"
|
2021-10-26 11:25:32 +02:00
|
|
|
|
2021-11-12 12:12:27 -06:00
|
|
|
#include "node_intern.hh" /* own include */
|
2012-08-01 19:11:17 +00:00
|
|
|
|
2021-06-02 17:19:36 +02:00
|
|
|
struct NodeInsertOfsData {
|
2015-08-01 17:39:48 +02:00
|
|
|
bNodeTree *ntree;
|
2023-01-03 20:02:01 -05:00
|
|
|
bNode *insert; /* Inserted node. */
|
2023-01-06 13:57:21 +11:00
|
|
|
bNode *prev, *next; /* Previous/next node in the chain. */
|
2015-08-01 17:39:48 +02:00
|
|
|
bNode *insert_parent;
|
|
|
|
|
|
|
|
|
|
wmTimer *anim_timer;
|
|
|
|
|
|
2023-01-03 20:02:01 -05:00
|
|
|
float offset_x; /* Offset to apply to node chain. */
|
2021-06-02 17:19:36 +02:00
|
|
|
};
|
2015-08-01 17:39:48 +02:00
|
|
|
|
2022-10-11 20:07:53 -05:00
|
|
|
namespace blender::ed::space_node {
|
|
|
|
|
|
2021-02-11 01:16:17 -06:00
|
|
|
static void clear_picking_highlight(ListBase *links)
|
|
|
|
|
{
|
|
|
|
|
LISTBASE_FOREACH (bNodeLink *, link, links) {
|
|
|
|
|
link->flag &= ~NODE_LINK_TEMP_HIGHLIGHT;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-15 17:29:54 +11:00
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Add Node
|
|
|
|
|
* \{ */
|
|
|
|
|
|
2022-12-29 14:09:58 -05:00
|
|
|
static bNodeLink create_drag_link(bNode &node, bNodeSocket &socket)
|
2021-02-11 01:16:17 -06:00
|
|
|
{
|
2022-12-29 14:09:58 -05:00
|
|
|
bNodeLink oplink{};
|
|
|
|
|
if (socket.in_out == SOCK_OUT) {
|
|
|
|
|
oplink.fromnode = &node;
|
|
|
|
|
oplink.fromsock = &socket;
|
2021-02-11 01:16:17 -06:00
|
|
|
}
|
|
|
|
|
else {
|
2022-12-29 14:09:58 -05:00
|
|
|
oplink.tonode = &node;
|
|
|
|
|
oplink.tosock = &socket;
|
2021-02-11 01:16:17 -06:00
|
|
|
}
|
2022-12-29 14:09:58 -05:00
|
|
|
oplink.flag |= NODE_LINK_VALID;
|
2021-11-19 15:36:32 -05:00
|
|
|
return oplink;
|
2021-02-11 01:16:17 -06:00
|
|
|
}
|
|
|
|
|
|
2022-12-16 13:22:34 -06:00
|
|
|
static void pick_link(bNodeLinkDrag &nldrag,
|
|
|
|
|
SpaceNode &snode,
|
|
|
|
|
bNode *node,
|
|
|
|
|
bNodeLink &link_to_pick)
|
2021-02-11 01:16:17 -06:00
|
|
|
{
|
2021-12-03 16:25:17 -05:00
|
|
|
clear_picking_highlight(&snode.edittree->links);
|
2021-02-11 01:16:17 -06:00
|
|
|
|
2022-12-29 14:09:58 -05:00
|
|
|
bNodeLink link = create_drag_link(*link_to_pick.fromnode, *link_to_pick.fromsock);
|
2021-02-11 01:16:17 -06:00
|
|
|
|
2021-12-03 16:25:17 -05:00
|
|
|
nldrag.links.append(link);
|
|
|
|
|
nodeRemLink(snode.edittree, &link_to_pick);
|
2022-09-02 17:37:10 -05:00
|
|
|
snode.edittree->ensure_topology_cache();
|
2022-09-03 23:22:12 -05:00
|
|
|
BLI_assert(nldrag.last_node_hovered_while_dragging_a_link != nullptr);
|
|
|
|
|
update_multi_input_indices_for_removed_links(*nldrag.last_node_hovered_while_dragging_a_link);
|
2021-04-15 10:00:25 +02:00
|
|
|
|
2021-02-11 01:16:17 -06:00
|
|
|
/* Send changed event to original link->tonode. */
|
|
|
|
|
if (node) {
|
2021-12-21 15:18:56 +01:00
|
|
|
BKE_ntree_update_tag_node_property(snode.edittree, node);
|
2021-02-11 01:16:17 -06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-03 16:25:17 -05:00
|
|
|
static void pick_input_link_by_link_intersect(const bContext &C,
|
|
|
|
|
wmOperator &op,
|
|
|
|
|
bNodeLinkDrag &nldrag,
|
|
|
|
|
const float2 &cursor)
|
2021-02-11 01:16:17 -06:00
|
|
|
{
|
2021-12-03 16:25:17 -05:00
|
|
|
SpaceNode *snode = CTX_wm_space_node(&C);
|
2023-11-25 15:23:31 +01:00
|
|
|
ARegion *region = CTX_wm_region(&C);
|
2023-02-28 11:35:32 -05:00
|
|
|
bNodeTree &node_tree = *snode->edittree;
|
2021-02-11 01:16:17 -06:00
|
|
|
|
2022-09-02 14:09:32 -05:00
|
|
|
float2 drag_start;
|
2021-12-03 16:25:17 -05:00
|
|
|
RNA_float_get_array(op.ptr, "drag_start", drag_start);
|
2023-11-25 15:23:31 +01:00
|
|
|
bNodeSocket *socket = node_find_indicated_socket(*snode, *region, drag_start, SOCK_IN);
|
2023-01-03 13:34:51 -05:00
|
|
|
bNode &node = socket->owner_node();
|
2021-02-11 01:16:17 -06:00
|
|
|
|
2021-03-26 17:29:10 +01:00
|
|
|
/* Distance to test overlapping of cursor on link. */
|
2023-03-17 04:19:05 +01:00
|
|
|
const float cursor_link_touch_distance = 12.5f * UI_SCALE_FAC;
|
2021-02-11 01:16:17 -06:00
|
|
|
|
2021-06-02 17:19:36 +02:00
|
|
|
bNodeLink *link_to_pick = nullptr;
|
2023-02-28 11:35:32 -05:00
|
|
|
clear_picking_highlight(&node_tree.links);
|
2023-01-03 13:34:51 -05:00
|
|
|
for (bNodeLink *link : socket->directly_linked_links()) {
|
|
|
|
|
/* Test if the cursor is near a link. */
|
|
|
|
|
std::array<float2, NODE_LINK_RESOL + 1> coords;
|
2023-03-19 07:03:01 +01:00
|
|
|
node_link_bezier_points_evaluated(*link, coords);
|
2023-01-03 13:34:51 -05:00
|
|
|
|
|
|
|
|
for (const int i : IndexRange(coords.size() - 1)) {
|
|
|
|
|
const float distance = dist_squared_to_line_segment_v2(cursor, coords[i], coords[i + 1]);
|
|
|
|
|
if (distance < cursor_link_touch_distance) {
|
|
|
|
|
link_to_pick = link;
|
|
|
|
|
nldrag.last_picked_multi_input_socket_link = link_to_pick;
|
2021-02-11 01:16:17 -06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If no linked was picked in this call, try using the one picked in the previous call.
|
|
|
|
|
* Not essential for the basic behavior, but can make interaction feel a bit better if
|
2021-07-23 16:56:00 +10:00
|
|
|
* the mouse moves to the right and loses the "selection." */
|
2021-02-11 01:16:17 -06:00
|
|
|
if (!link_to_pick) {
|
2021-12-03 16:25:17 -05:00
|
|
|
bNodeLink *last_picked_link = nldrag.last_picked_multi_input_socket_link;
|
2021-03-02 23:09:38 +01:00
|
|
|
if (last_picked_link) {
|
|
|
|
|
link_to_pick = last_picked_link;
|
2021-02-11 01:16:17 -06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (link_to_pick) {
|
|
|
|
|
/* Highlight is set here and cleared in the next iteration or if the operation finishes. */
|
|
|
|
|
link_to_pick->flag |= NODE_LINK_TEMP_HIGHLIGHT;
|
2021-12-03 16:25:17 -05:00
|
|
|
ED_area_tag_redraw(CTX_wm_area(&C));
|
2021-02-11 01:16:17 -06:00
|
|
|
|
2023-11-25 15:23:31 +01:00
|
|
|
if (!node_find_indicated_socket(*snode, *region, cursor, SOCK_IN)) {
|
2023-01-03 13:34:51 -05:00
|
|
|
pick_link(nldrag, *snode, &node, *link_to_pick);
|
2021-02-11 01:16:17 -06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-03 17:37:25 -05:00
|
|
|
static bool socket_is_available(bNodeTree * /*ntree*/, bNodeSocket *sock, const bool allow_used)
|
2012-08-01 19:11:17 +00:00
|
|
|
{
|
2023-09-19 10:47:21 +02:00
|
|
|
if (!sock->is_visible()) {
|
2021-06-02 17:19:36 +02:00
|
|
|
return false;
|
2019-03-26 21:16:47 +11:00
|
|
|
}
|
2012-08-01 19:11:17 +00:00
|
|
|
|
2022-12-29 10:28:29 -05:00
|
|
|
if (!allow_used && (sock->flag & SOCK_IS_LINKED)) {
|
2022-01-18 16:20:43 +01:00
|
|
|
/* Multi input sockets are available (even if used). */
|
|
|
|
|
if (!(sock->flag & SOCK_MULTI_INPUT)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2019-03-26 21:16:47 +11:00
|
|
|
}
|
2012-08-01 19:11:17 +00:00
|
|
|
|
2021-06-02 17:19:36 +02:00
|
|
|
return true;
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
|
|
|
|
|
2014-02-03 18:55:59 +11:00
|
|
|
static bNodeSocket *best_socket_output(bNodeTree *ntree,
|
|
|
|
|
bNode *node,
|
|
|
|
|
bNodeSocket *sock_target,
|
|
|
|
|
const bool allow_multiple)
|
2012-08-01 19:11:17 +00:00
|
|
|
{
|
2023-01-03 20:02:01 -05:00
|
|
|
/* First look for selected output. */
|
2020-10-16 18:06:30 +02:00
|
|
|
LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
|
2019-03-26 21:16:47 +11:00
|
|
|
if (!socket_is_available(ntree, sock, allow_multiple)) {
|
2012-08-01 19:11:17 +00:00
|
|
|
continue;
|
2019-03-26 21:16:47 +11:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-03-26 21:16:47 +11:00
|
|
|
if (sock->flag & SELECT) {
|
2012-08-01 19:11:17 +00:00
|
|
|
return sock;
|
2019-03-26 21:16:47 +11:00
|
|
|
}
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-01-03 20:02:01 -05:00
|
|
|
/* Try to find a socket with a matching name. */
|
2020-10-16 18:06:30 +02:00
|
|
|
LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
|
2019-03-26 21:16:47 +11:00
|
|
|
if (!socket_is_available(ntree, sock, allow_multiple)) {
|
2012-08-01 19:11:17 +00:00
|
|
|
continue;
|
2019-03-26 21:16:47 +11:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-01-03 20:02:01 -05:00
|
|
|
/* Check for same types. */
|
2012-08-01 19:11:17 +00:00
|
|
|
if (sock->type == sock_target->type) {
|
2019-03-26 21:16:47 +11:00
|
|
|
if (STREQ(sock->name, sock_target->name)) {
|
2012-08-01 19:11:17 +00:00
|
|
|
return sock;
|
2019-03-26 21:16:47 +11:00
|
|
|
}
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-01-03 20:02:01 -05:00
|
|
|
/* Otherwise settle for the first available socket of the right type. */
|
2020-10-16 18:06:30 +02:00
|
|
|
LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
|
2019-03-26 21:16:47 +11:00
|
|
|
if (!socket_is_available(ntree, sock, allow_multiple)) {
|
2012-08-01 19:11:17 +00:00
|
|
|
continue;
|
2019-03-26 21:16:47 +11:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-01-03 20:02:01 -05:00
|
|
|
/* Check for same types. */
|
2012-08-01 19:11:17 +00:00
|
|
|
if (sock->type == sock_target->type) {
|
|
|
|
|
return sock;
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-08-04 17:36:25 +02:00
|
|
|
/* Always allow linking to an reroute node. The socket type of the reroute sockets might change
|
|
|
|
|
* after the link has been created. */
|
|
|
|
|
if (node->type == NODE_REROUTE) {
|
2021-06-02 17:19:36 +02:00
|
|
|
return (bNodeSocket *)node->outputs.first;
|
2020-08-04 17:36:25 +02:00
|
|
|
}
|
|
|
|
|
|
2021-06-02 17:19:36 +02:00
|
|
|
return nullptr;
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
|
|
|
|
|
2023-01-03 20:02:01 -05:00
|
|
|
/* This is a bit complicated, but designed to prioritize finding
|
|
|
|
|
* sockets of higher types, such as image, first. */
|
2012-08-01 19:11:17 +00:00
|
|
|
static bNodeSocket *best_socket_input(bNodeTree *ntree, bNode *node, int num, int replace)
|
|
|
|
|
{
|
2020-10-16 18:06:30 +02:00
|
|
|
int maxtype = 0;
|
|
|
|
|
LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
|
2012-10-27 11:18:54 +00:00
|
|
|
maxtype = max_ii(sock->type, maxtype);
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-01-03 20:02:01 -05:00
|
|
|
/* Find sockets of higher 'types' first (i.e. image). */
|
2020-10-16 18:06:30 +02:00
|
|
|
int a = 0;
|
|
|
|
|
for (int socktype = maxtype; socktype >= 0; socktype--) {
|
|
|
|
|
LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
|
2012-08-01 19:11:17 +00:00
|
|
|
if (!socket_is_available(ntree, sock, replace)) {
|
|
|
|
|
a++;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-08-01 19:11:17 +00:00
|
|
|
if (sock->type == socktype) {
|
2023-01-03 20:02:01 -05:00
|
|
|
/* Increment to make sure we don't keep finding the same socket on every attempt running
|
|
|
|
|
* this function. */
|
2012-08-01 19:11:17 +00:00
|
|
|
a++;
|
2019-03-26 21:16:47 +11:00
|
|
|
if (a > num) {
|
2012-08-01 19:11:17 +00:00
|
|
|
return sock;
|
2019-03-26 21:16:47 +11:00
|
|
|
}
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-06-02 17:19:36 +02:00
|
|
|
return nullptr;
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
|
|
|
|
|
2021-12-03 16:25:17 -05:00
|
|
|
static bool snode_autoconnect_input(SpaceNode &snode,
|
2016-08-13 16:31:46 +02:00
|
|
|
bNode *node_fr,
|
|
|
|
|
bNodeSocket *sock_fr,
|
|
|
|
|
bNode *node_to,
|
|
|
|
|
bNodeSocket *sock_to,
|
|
|
|
|
int replace)
|
2012-08-01 19:11:17 +00:00
|
|
|
{
|
2021-12-03 16:25:17 -05:00
|
|
|
bNodeTree *ntree = snode.edittree;
|
2012-08-01 19:11:17 +00:00
|
|
|
|
2019-03-26 21:16:47 +11:00
|
|
|
if (replace) {
|
2012-08-01 19:11:17 +00:00
|
|
|
nodeRemSocketLinks(ntree, sock_to);
|
2019-03-26 21:16:47 +11:00
|
|
|
}
|
2012-08-01 19:11:17 +00:00
|
|
|
|
2016-08-13 16:31:46 +02:00
|
|
|
nodeAddLink(ntree, node_fr, sock_fr, node_to, sock_to);
|
|
|
|
|
return true;
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
|
|
|
|
|
2021-06-02 17:19:36 +02:00
|
|
|
struct LinkAndPosition {
|
2021-12-03 16:25:17 -05:00
|
|
|
bNodeLink *link;
|
|
|
|
|
float2 multi_socket_position;
|
2021-06-02 17:19:36 +02:00
|
|
|
};
|
2021-03-10 14:57:57 +01:00
|
|
|
|
2023-03-19 07:03:01 +01:00
|
|
|
static void sort_multi_input_socket_links_with_drag(bNodeSocket &socket,
|
2022-09-03 23:22:12 -05:00
|
|
|
bNodeLink &drag_link,
|
|
|
|
|
const float2 &cursor)
|
2021-03-10 14:57:57 +01:00
|
|
|
{
|
2023-03-19 07:03:01 +01:00
|
|
|
const float2 &socket_location = socket.runtime->location;
|
2021-03-10 14:57:57 +01:00
|
|
|
|
2023-01-04 16:37:32 -05:00
|
|
|
Vector<LinkAndPosition, 8> links;
|
|
|
|
|
for (bNodeLink *link : socket.directly_linked_links()) {
|
|
|
|
|
const float2 location = node_link_calculate_multi_input_position(
|
|
|
|
|
socket_location, link->multi_input_socket_index, link->tosock->runtime->total_inputs);
|
|
|
|
|
links.append({link, location});
|
|
|
|
|
};
|
2021-03-10 14:57:57 +01:00
|
|
|
|
2023-01-04 16:37:32 -05:00
|
|
|
links.append({&drag_link, cursor});
|
2021-03-10 14:57:57 +01:00
|
|
|
|
2023-01-04 16:37:32 -05:00
|
|
|
std::sort(links.begin(), links.end(), [](const LinkAndPosition a, const LinkAndPosition b) {
|
|
|
|
|
return a.multi_socket_position.y < b.multi_socket_position.y;
|
|
|
|
|
});
|
2021-03-10 14:57:57 +01:00
|
|
|
|
2023-01-04 16:37:32 -05:00
|
|
|
for (const int i : links.index_range()) {
|
|
|
|
|
links[i].link->multi_input_socket_index = i;
|
2021-03-10 14:57:57 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-03 23:22:12 -05:00
|
|
|
void update_multi_input_indices_for_removed_links(bNode &node)
|
|
|
|
|
{
|
|
|
|
|
for (bNodeSocket *socket : node.input_sockets()) {
|
|
|
|
|
if (!socket->is_multi_input()) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
Vector<bNodeLink *, 8> links = socket->directly_linked_links();
|
|
|
|
|
std::sort(links.begin(), links.end(), [](const bNodeLink *a, const bNodeLink *b) {
|
|
|
|
|
return a->multi_input_socket_index < b->multi_input_socket_index;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
for (const int i : links.index_range()) {
|
|
|
|
|
links[i]->multi_input_socket_index = i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-07 17:56:25 +01:00
|
|
|
static void snode_autoconnect(SpaceNode &snode, const bool allow_multiple, const bool replace)
|
2012-08-01 19:11:17 +00:00
|
|
|
{
|
2021-12-03 16:25:17 -05:00
|
|
|
bNodeTree *ntree = snode.edittree;
|
2021-11-19 15:57:31 -05:00
|
|
|
Vector<bNode *> sorted_nodes;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-12-02 11:12:51 -06:00
|
|
|
for (bNode *node : ntree->all_nodes()) {
|
2012-08-01 19:11:17 +00:00
|
|
|
if (node->flag & NODE_SELECT) {
|
2021-11-19 15:57:31 -05:00
|
|
|
sorted_nodes.append(node);
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-11-19 15:57:31 -05:00
|
|
|
/* Sort nodes left to right. */
|
|
|
|
|
std::sort(sorted_nodes.begin(), sorted_nodes.end(), [](const bNode *a, const bNode *b) {
|
|
|
|
|
return a->locx < b->locx;
|
|
|
|
|
});
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-01-19 17:07:29 +11:00
|
|
|
// int numlinks = 0; /* UNUSED */
|
2021-11-19 15:57:31 -05:00
|
|
|
for (const int i : sorted_nodes.as_mutable_span().drop_back(1).index_range()) {
|
2014-04-11 11:25:41 +10:00
|
|
|
bool has_selected_inputs = false;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-11-19 15:57:31 -05:00
|
|
|
bNode *node_fr = sorted_nodes[i];
|
|
|
|
|
bNode *node_to = sorted_nodes[i + 1];
|
2023-02-12 14:37:16 +11:00
|
|
|
/* Corner case: input/output node aligned the wrong way around (#47729). */
|
2016-03-09 11:23:56 +01:00
|
|
|
if (BLI_listbase_is_empty(&node_to->inputs) || BLI_listbase_is_empty(&node_fr->outputs)) {
|
2023-01-09 11:12:03 -05:00
|
|
|
std::swap(node_fr, node_to);
|
2016-03-09 11:23:56 +01:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-01-03 20:02:01 -05:00
|
|
|
/* If there are selected sockets, connect those. */
|
2020-10-16 18:06:30 +02:00
|
|
|
LISTBASE_FOREACH (bNodeSocket *, sock_to, &node_to->inputs) {
|
2012-08-01 19:11:17 +00:00
|
|
|
if (sock_to->flag & SELECT) {
|
2021-06-02 17:19:36 +02:00
|
|
|
has_selected_inputs = true;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-03-26 21:16:47 +11:00
|
|
|
if (!socket_is_available(ntree, sock_to, replace)) {
|
2012-08-01 19:11:17 +00:00
|
|
|
continue;
|
2019-03-26 21:16:47 +11:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-01-03 20:02:01 -05:00
|
|
|
/* Check for an appropriate output socket to connect from. */
|
2020-10-16 18:06:30 +02:00
|
|
|
bNodeSocket *sock_fr = best_socket_output(ntree, node_fr, sock_to, allow_multiple);
|
2019-03-26 21:16:47 +11:00
|
|
|
if (!sock_fr) {
|
2012-08-01 19:11:17 +00:00
|
|
|
continue;
|
2019-03-26 21:16:47 +11:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-08-22 16:44:32 +00:00
|
|
|
if (snode_autoconnect_input(snode, node_fr, sock_fr, node_to, sock_to, replace)) {
|
2023-01-19 17:07:29 +11:00
|
|
|
// numlinks++;
|
2012-08-22 16:44:32 +00:00
|
|
|
}
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-08-01 19:11:17 +00:00
|
|
|
if (!has_selected_inputs) {
|
2023-01-03 20:02:01 -05:00
|
|
|
/* No selected inputs, connect by finding suitable match. */
|
2014-11-16 13:57:58 +01:00
|
|
|
int num_inputs = BLI_listbase_count(&node_to->inputs);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-09-09 18:41:07 +02:00
|
|
|
for (int i = 0; i < num_inputs; i++) {
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-01-03 20:02:01 -05:00
|
|
|
/* Find the best guess input socket. */
|
2020-10-16 18:06:30 +02:00
|
|
|
bNodeSocket *sock_to = best_socket_input(ntree, node_to, i, replace);
|
2019-03-26 21:16:47 +11:00
|
|
|
if (!sock_to) {
|
2012-08-01 19:11:17 +00:00
|
|
|
continue;
|
2019-03-26 21:16:47 +11:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-01-03 20:02:01 -05:00
|
|
|
/* Check for an appropriate output socket to connect from. */
|
2020-10-16 18:06:30 +02:00
|
|
|
bNodeSocket *sock_fr = best_socket_output(ntree, node_fr, sock_to, allow_multiple);
|
2019-03-26 21:16:47 +11:00
|
|
|
if (!sock_fr) {
|
2012-08-01 19:11:17 +00:00
|
|
|
continue;
|
2019-03-26 21:16:47 +11:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-08-01 19:11:17 +00:00
|
|
|
if (snode_autoconnect_input(snode, node_fr, sock_fr, node_to, sock_to, replace)) {
|
2023-01-19 17:07:29 +11:00
|
|
|
// numlinks++;
|
2012-08-01 19:11:17 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-03 10:35:46 +10:00
|
|
|
/** \} */
|
|
|
|
|
|
2022-03-15 17:29:54 +11:00
|
|
|
namespace viewer_linking {
|
|
|
|
|
|
2021-06-03 10:35:46 +10:00
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Link Viewer Operator
|
|
|
|
|
* \{ */
|
2012-08-01 19:11:17 +00:00
|
|
|
|
2021-10-26 11:25:32 +02:00
|
|
|
/* Depending on the node tree type, different socket types are supported by viewer nodes. */
|
2023-09-19 10:47:21 +02:00
|
|
|
static bool socket_can_be_viewed(const bNode &node, const bNodeSocket &socket)
|
2021-10-26 11:25:32 +02:00
|
|
|
{
|
2023-09-19 10:47:21 +02:00
|
|
|
if (!node.is_socket_icon_drawn(socket)) {
|
2021-10-26 11:25:32 +02:00
|
|
|
return false;
|
2019-03-26 21:16:47 +11:00
|
|
|
}
|
2022-08-31 12:15:57 +02:00
|
|
|
if (STREQ(socket.idname, "NodeSocketVirtual")) {
|
2021-10-26 11:25:32 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
2022-08-31 12:15:57 +02:00
|
|
|
if (socket.owner_tree().type != NTREE_GEOMETRY) {
|
2021-10-26 11:25:32 +02:00
|
|
|
return true;
|
2019-03-26 21:16:47 +11:00
|
|
|
}
|
2022-08-31 12:15:57 +02:00
|
|
|
return ELEM(socket.typeinfo->type,
|
2021-10-26 11:25:32 +02:00
|
|
|
SOCK_GEOMETRY,
|
|
|
|
|
SOCK_FLOAT,
|
|
|
|
|
SOCK_VECTOR,
|
|
|
|
|
SOCK_INT,
|
|
|
|
|
SOCK_BOOLEAN,
|
2023-06-14 20:30:10 +02:00
|
|
|
SOCK_ROTATION,
|
2021-10-26 11:25:32 +02:00
|
|
|
SOCK_RGBA);
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-10-26 11:25:32 +02:00
|
|
|
/**
|
|
|
|
|
* Find the socket to link to in a viewer node.
|
|
|
|
|
*/
|
2021-12-03 16:25:17 -05:00
|
|
|
static bNodeSocket *node_link_viewer_get_socket(bNodeTree &ntree,
|
|
|
|
|
bNode &viewer_node,
|
|
|
|
|
bNodeSocket &src_socket)
|
2021-10-26 11:25:32 +02:00
|
|
|
{
|
2021-12-03 16:25:17 -05:00
|
|
|
if (viewer_node.type != GEO_NODE_VIEWER) {
|
2021-10-26 11:25:32 +02:00
|
|
|
/* In viewer nodes in the compositor, only the first input should be linked to. */
|
2021-12-03 16:25:17 -05:00
|
|
|
return (bNodeSocket *)viewer_node.inputs.first;
|
2021-10-26 11:25:32 +02:00
|
|
|
}
|
|
|
|
|
/* For the geometry nodes viewer, find the socket with the correct type. */
|
2023-11-21 10:43:25 +01:00
|
|
|
|
|
|
|
|
if (src_socket.type == SOCK_GEOMETRY) {
|
|
|
|
|
return static_cast<bNodeSocket *>(viewer_node.inputs.first);
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
2023-11-21 10:43:25 +01:00
|
|
|
|
|
|
|
|
ntree.ensure_topology_cache();
|
|
|
|
|
if (!socket_can_be_viewed(src_socket.owner_node(), src_socket)) {
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NodeGeometryViewer &storage = *static_cast<NodeGeometryViewer *>(viewer_node.storage);
|
|
|
|
|
const eCustomDataType data_type = *bke::socket_type_to_custom_data_type(
|
|
|
|
|
eNodeSocketDatatype(src_socket.type));
|
|
|
|
|
BLI_assert(data_type != CD_AUTO_FROM_NAME);
|
|
|
|
|
storage.data_type = data_type;
|
|
|
|
|
nodes::update_node_declaration_and_sockets(ntree, viewer_node);
|
|
|
|
|
|
|
|
|
|
return static_cast<bNodeSocket *>(viewer_node.inputs.last);
|
2021-10-26 11:25:32 +02:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-08-31 12:15:57 +02:00
|
|
|
static bool is_viewer_node(const bNode &node)
|
2021-10-26 11:25:32 +02:00
|
|
|
{
|
2023-12-03 23:20:44 +01:00
|
|
|
return ELEM(node.type, CMP_NODE_VIEWER, GEO_NODE_VIEWER);
|
2021-10-26 11:25:32 +02:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-08-31 12:15:57 +02:00
|
|
|
static bool is_viewer_socket_in_viewer(const bNodeSocket &socket)
|
2021-10-26 11:25:32 +02:00
|
|
|
{
|
2022-08-31 12:15:57 +02:00
|
|
|
const bNode &node = socket.owner_node();
|
2021-10-26 11:25:32 +02:00
|
|
|
BLI_assert(is_viewer_node(node));
|
2022-08-31 12:15:57 +02:00
|
|
|
if (node.typeinfo->type == GEO_NODE_VIEWER) {
|
2021-10-26 11:25:32 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return socket.index() == 0;
|
|
|
|
|
}
|
|
|
|
|
|
Geometry Nodes: viewport preview
This adds support for showing geometry passed to the Viewer in the 3d
viewport (instead of just in the spreadsheet). The "viewer geometry"
bypasses the group output. So it is not necessary to change the final
output of the node group to be able to see the intermediate geometry.
**Activation and deactivation of a viewer node**
* A viewer node is activated by clicking on it.
* Ctrl+shift+click on any node/socket connects it to the viewer and
makes it active.
* Ctrl+shift+click in empty space deactivates the active viewer.
* When the active viewer is not visible anymore (e.g. another object
is selected, or the current node group is exit), it is deactivated.
* Clicking on the icon in the header of the Viewer node toggles whether
its active or not.
**Pinning**
* The spreadsheet still allows pinning the active viewer as before.
When pinned, the spreadsheet still references the viewer node even
when it becomes inactive.
* The viewport does not support pinning at the moment. It always shows
the active viewer.
**Attribute**
* When a field is linked to the second input of the viewer node it is
displayed as an overlay in the viewport.
* When possible the correct domain for the attribute is determined
automatically. This does not work in all cases. It falls back to the
face corner domain on meshes and the point domain on curves. When
necessary, the domain can be picked manually.
* The spreadsheet now only shows the "Viewer" column for the domain
that is selected in the Viewer node.
* Instance attributes are visualized as a constant color per instance.
**Viewport Options**
* The attribute overlay opacity can be controlled with the "Viewer Node"
setting in the overlays popover.
* A viewport can be configured not to show intermediate viewer-geometry
by disabling the "Viewer Node" option in the "View" menu.
**Implementation Details**
* The "spreadsheet context path" was generalized to a "viewer path" that
is used in more places now.
* The viewer node itself determines the attribute domain, evaluates the
field and stores the result in a `.viewer` attribute.
* A new "viewer attribute' overlay displays the data from the `.viewer`
attribute.
* The ground truth for the active viewer node is stored in the workspace
now. Node editors, spreadsheets and viewports retrieve the active
viewer from there unless they are pinned.
* The depsgraph object iterator has a new "viewer path" setting. When set,
the viewed geometry of the corresponding object is part of the iterator
instead of the final evaluated geometry.
* To support the instance attribute overlay `DupliObject` was extended
to contain the information necessary for drawing the overlay.
* The ctrl+shift+click operator has been refactored so that it can make
existing links to viewers active again.
* The auto-domain-detection in the Viewer node works by checking the
"preferred domain" for every field input. If there is not exactly one
preferred domain, the fallback is used.
Known limitations:
* Loose edges of meshes don't have the attribute overlay. This could be
added separately if necessary.
* Some attributes are hard to visualize as a color directly. For example,
the values might have to be normalized or some should be drawn as arrays.
For now, we encourage users to build node groups that generate appropriate
viewer-geometry. We might include some of that functionality in future versions.
Support for displaying attribute values as text in the viewport is planned as well.
* There seems to be an issue with the attribute overlay for pointclouds on
nvidia gpus, to be investigated.
Differential Revision: https://developer.blender.org/D15954
2022-09-28 17:54:59 +02:00
|
|
|
static bool is_viewer_socket(const bNodeSocket &socket)
|
2021-10-26 11:25:32 +02:00
|
|
|
{
|
Geometry Nodes: viewport preview
This adds support for showing geometry passed to the Viewer in the 3d
viewport (instead of just in the spreadsheet). The "viewer geometry"
bypasses the group output. So it is not necessary to change the final
output of the node group to be able to see the intermediate geometry.
**Activation and deactivation of a viewer node**
* A viewer node is activated by clicking on it.
* Ctrl+shift+click on any node/socket connects it to the viewer and
makes it active.
* Ctrl+shift+click in empty space deactivates the active viewer.
* When the active viewer is not visible anymore (e.g. another object
is selected, or the current node group is exit), it is deactivated.
* Clicking on the icon in the header of the Viewer node toggles whether
its active or not.
**Pinning**
* The spreadsheet still allows pinning the active viewer as before.
When pinned, the spreadsheet still references the viewer node even
when it becomes inactive.
* The viewport does not support pinning at the moment. It always shows
the active viewer.
**Attribute**
* When a field is linked to the second input of the viewer node it is
displayed as an overlay in the viewport.
* When possible the correct domain for the attribute is determined
automatically. This does not work in all cases. It falls back to the
face corner domain on meshes and the point domain on curves. When
necessary, the domain can be picked manually.
* The spreadsheet now only shows the "Viewer" column for the domain
that is selected in the Viewer node.
* Instance attributes are visualized as a constant color per instance.
**Viewport Options**
* The attribute overlay opacity can be controlled with the "Viewer Node"
setting in the overlays popover.
* A viewport can be configured not to show intermediate viewer-geometry
by disabling the "Viewer Node" option in the "View" menu.
**Implementation Details**
* The "spreadsheet context path" was generalized to a "viewer path" that
is used in more places now.
* The viewer node itself determines the attribute domain, evaluates the
field and stores the result in a `.viewer` attribute.
* A new "viewer attribute' overlay displays the data from the `.viewer`
attribute.
* The ground truth for the active viewer node is stored in the workspace
now. Node editors, spreadsheets and viewports retrieve the active
viewer from there unless they are pinned.
* The depsgraph object iterator has a new "viewer path" setting. When set,
the viewed geometry of the corresponding object is part of the iterator
instead of the final evaluated geometry.
* To support the instance attribute overlay `DupliObject` was extended
to contain the information necessary for drawing the overlay.
* The ctrl+shift+click operator has been refactored so that it can make
existing links to viewers active again.
* The auto-domain-detection in the Viewer node works by checking the
"preferred domain" for every field input. If there is not exactly one
preferred domain, the fallback is used.
Known limitations:
* Loose edges of meshes don't have the attribute overlay. This could be
added separately if necessary.
* Some attributes are hard to visualize as a color directly. For example,
the values might have to be normalized or some should be drawn as arrays.
For now, we encourage users to build node groups that generate appropriate
viewer-geometry. We might include some of that functionality in future versions.
Support for displaying attribute values as text in the viewport is planned as well.
* There seems to be an issue with the attribute overlay for pointclouds on
nvidia gpus, to be investigated.
Differential Revision: https://developer.blender.org/D15954
2022-09-28 17:54:59 +02:00
|
|
|
if (is_viewer_node(socket.owner_node())) {
|
|
|
|
|
return is_viewer_socket_in_viewer(socket);
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
2021-10-26 11:25:32 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-10-26 11:25:32 +02:00
|
|
|
static int get_default_viewer_type(const bContext *C)
|
|
|
|
|
{
|
|
|
|
|
SpaceNode *snode = CTX_wm_space_node(C);
|
|
|
|
|
return ED_node_is_compositor(snode) ? CMP_NODE_VIEWER : GEO_NODE_VIEWER;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void remove_links_to_unavailable_viewer_sockets(bNodeTree &btree, bNode &viewer_node)
|
|
|
|
|
{
|
|
|
|
|
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &btree.links) {
|
|
|
|
|
if (link->tonode == &viewer_node) {
|
|
|
|
|
if (link->tosock->flag & SOCK_UNAVAIL) {
|
|
|
|
|
nodeRemLink(&btree, link);
|
2021-07-28 15:48:31 +02:00
|
|
|
}
|
|
|
|
|
}
|
2021-10-26 11:25:32 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
Geometry Nodes: viewport preview
This adds support for showing geometry passed to the Viewer in the 3d
viewport (instead of just in the spreadsheet). The "viewer geometry"
bypasses the group output. So it is not necessary to change the final
output of the node group to be able to see the intermediate geometry.
**Activation and deactivation of a viewer node**
* A viewer node is activated by clicking on it.
* Ctrl+shift+click on any node/socket connects it to the viewer and
makes it active.
* Ctrl+shift+click in empty space deactivates the active viewer.
* When the active viewer is not visible anymore (e.g. another object
is selected, or the current node group is exit), it is deactivated.
* Clicking on the icon in the header of the Viewer node toggles whether
its active or not.
**Pinning**
* The spreadsheet still allows pinning the active viewer as before.
When pinned, the spreadsheet still references the viewer node even
when it becomes inactive.
* The viewport does not support pinning at the moment. It always shows
the active viewer.
**Attribute**
* When a field is linked to the second input of the viewer node it is
displayed as an overlay in the viewport.
* When possible the correct domain for the attribute is determined
automatically. This does not work in all cases. It falls back to the
face corner domain on meshes and the point domain on curves. When
necessary, the domain can be picked manually.
* The spreadsheet now only shows the "Viewer" column for the domain
that is selected in the Viewer node.
* Instance attributes are visualized as a constant color per instance.
**Viewport Options**
* The attribute overlay opacity can be controlled with the "Viewer Node"
setting in the overlays popover.
* A viewport can be configured not to show intermediate viewer-geometry
by disabling the "Viewer Node" option in the "View" menu.
**Implementation Details**
* The "spreadsheet context path" was generalized to a "viewer path" that
is used in more places now.
* The viewer node itself determines the attribute domain, evaluates the
field and stores the result in a `.viewer` attribute.
* A new "viewer attribute' overlay displays the data from the `.viewer`
attribute.
* The ground truth for the active viewer node is stored in the workspace
now. Node editors, spreadsheets and viewports retrieve the active
viewer from there unless they are pinned.
* The depsgraph object iterator has a new "viewer path" setting. When set,
the viewed geometry of the corresponding object is part of the iterator
instead of the final evaluated geometry.
* To support the instance attribute overlay `DupliObject` was extended
to contain the information necessary for drawing the overlay.
* The ctrl+shift+click operator has been refactored so that it can make
existing links to viewers active again.
* The auto-domain-detection in the Viewer node works by checking the
"preferred domain" for every field input. If there is not exactly one
preferred domain, the fallback is used.
Known limitations:
* Loose edges of meshes don't have the attribute overlay. This could be
added separately if necessary.
* Some attributes are hard to visualize as a color directly. For example,
the values might have to be normalized or some should be drawn as arrays.
For now, we encourage users to build node groups that generate appropriate
viewer-geometry. We might include some of that functionality in future versions.
Support for displaying attribute values as text in the viewport is planned as well.
* There seems to be an issue with the attribute overlay for pointclouds on
nvidia gpus, to be investigated.
Differential Revision: https://developer.blender.org/D15954
2022-09-28 17:54:59 +02:00
|
|
|
static bNodeSocket *determine_socket_to_view(bNode &node_to_view)
|
2021-10-26 11:25:32 +02:00
|
|
|
{
|
2023-03-30 16:56:56 +02:00
|
|
|
int last_linked_data_socket_index = -1;
|
2023-03-16 16:49:56 +01:00
|
|
|
bool has_linked_geometry_socket = false;
|
Geometry Nodes: viewport preview
This adds support for showing geometry passed to the Viewer in the 3d
viewport (instead of just in the spreadsheet). The "viewer geometry"
bypasses the group output. So it is not necessary to change the final
output of the node group to be able to see the intermediate geometry.
**Activation and deactivation of a viewer node**
* A viewer node is activated by clicking on it.
* Ctrl+shift+click on any node/socket connects it to the viewer and
makes it active.
* Ctrl+shift+click in empty space deactivates the active viewer.
* When the active viewer is not visible anymore (e.g. another object
is selected, or the current node group is exit), it is deactivated.
* Clicking on the icon in the header of the Viewer node toggles whether
its active or not.
**Pinning**
* The spreadsheet still allows pinning the active viewer as before.
When pinned, the spreadsheet still references the viewer node even
when it becomes inactive.
* The viewport does not support pinning at the moment. It always shows
the active viewer.
**Attribute**
* When a field is linked to the second input of the viewer node it is
displayed as an overlay in the viewport.
* When possible the correct domain for the attribute is determined
automatically. This does not work in all cases. It falls back to the
face corner domain on meshes and the point domain on curves. When
necessary, the domain can be picked manually.
* The spreadsheet now only shows the "Viewer" column for the domain
that is selected in the Viewer node.
* Instance attributes are visualized as a constant color per instance.
**Viewport Options**
* The attribute overlay opacity can be controlled with the "Viewer Node"
setting in the overlays popover.
* A viewport can be configured not to show intermediate viewer-geometry
by disabling the "Viewer Node" option in the "View" menu.
**Implementation Details**
* The "spreadsheet context path" was generalized to a "viewer path" that
is used in more places now.
* The viewer node itself determines the attribute domain, evaluates the
field and stores the result in a `.viewer` attribute.
* A new "viewer attribute' overlay displays the data from the `.viewer`
attribute.
* The ground truth for the active viewer node is stored in the workspace
now. Node editors, spreadsheets and viewports retrieve the active
viewer from there unless they are pinned.
* The depsgraph object iterator has a new "viewer path" setting. When set,
the viewed geometry of the corresponding object is part of the iterator
instead of the final evaluated geometry.
* To support the instance attribute overlay `DupliObject` was extended
to contain the information necessary for drawing the overlay.
* The ctrl+shift+click operator has been refactored so that it can make
existing links to viewers active again.
* The auto-domain-detection in the Viewer node works by checking the
"preferred domain" for every field input. If there is not exactly one
preferred domain, the fallback is used.
Known limitations:
* Loose edges of meshes don't have the attribute overlay. This could be
added separately if necessary.
* Some attributes are hard to visualize as a color directly. For example,
the values might have to be normalized or some should be drawn as arrays.
For now, we encourage users to build node groups that generate appropriate
viewer-geometry. We might include some of that functionality in future versions.
Support for displaying attribute values as text in the viewport is planned as well.
* There seems to be an issue with the attribute overlay for pointclouds on
nvidia gpus, to be investigated.
Differential Revision: https://developer.blender.org/D15954
2022-09-28 17:54:59 +02:00
|
|
|
for (bNodeSocket *socket : node_to_view.output_sockets()) {
|
2023-09-19 10:47:21 +02:00
|
|
|
if (!socket_can_be_viewed(node_to_view, *socket)) {
|
Geometry Nodes: viewport preview
This adds support for showing geometry passed to the Viewer in the 3d
viewport (instead of just in the spreadsheet). The "viewer geometry"
bypasses the group output. So it is not necessary to change the final
output of the node group to be able to see the intermediate geometry.
**Activation and deactivation of a viewer node**
* A viewer node is activated by clicking on it.
* Ctrl+shift+click on any node/socket connects it to the viewer and
makes it active.
* Ctrl+shift+click in empty space deactivates the active viewer.
* When the active viewer is not visible anymore (e.g. another object
is selected, or the current node group is exit), it is deactivated.
* Clicking on the icon in the header of the Viewer node toggles whether
its active or not.
**Pinning**
* The spreadsheet still allows pinning the active viewer as before.
When pinned, the spreadsheet still references the viewer node even
when it becomes inactive.
* The viewport does not support pinning at the moment. It always shows
the active viewer.
**Attribute**
* When a field is linked to the second input of the viewer node it is
displayed as an overlay in the viewport.
* When possible the correct domain for the attribute is determined
automatically. This does not work in all cases. It falls back to the
face corner domain on meshes and the point domain on curves. When
necessary, the domain can be picked manually.
* The spreadsheet now only shows the "Viewer" column for the domain
that is selected in the Viewer node.
* Instance attributes are visualized as a constant color per instance.
**Viewport Options**
* The attribute overlay opacity can be controlled with the "Viewer Node"
setting in the overlays popover.
* A viewport can be configured not to show intermediate viewer-geometry
by disabling the "Viewer Node" option in the "View" menu.
**Implementation Details**
* The "spreadsheet context path" was generalized to a "viewer path" that
is used in more places now.
* The viewer node itself determines the attribute domain, evaluates the
field and stores the result in a `.viewer` attribute.
* A new "viewer attribute' overlay displays the data from the `.viewer`
attribute.
* The ground truth for the active viewer node is stored in the workspace
now. Node editors, spreadsheets and viewports retrieve the active
viewer from there unless they are pinned.
* The depsgraph object iterator has a new "viewer path" setting. When set,
the viewed geometry of the corresponding object is part of the iterator
instead of the final evaluated geometry.
* To support the instance attribute overlay `DupliObject` was extended
to contain the information necessary for drawing the overlay.
* The ctrl+shift+click operator has been refactored so that it can make
existing links to viewers active again.
* The auto-domain-detection in the Viewer node works by checking the
"preferred domain" for every field input. If there is not exactly one
preferred domain, the fallback is used.
Known limitations:
* Loose edges of meshes don't have the attribute overlay. This could be
added separately if necessary.
* Some attributes are hard to visualize as a color directly. For example,
the values might have to be normalized or some should be drawn as arrays.
For now, we encourage users to build node groups that generate appropriate
viewer-geometry. We might include some of that functionality in future versions.
Support for displaying attribute values as text in the viewport is planned as well.
* There seems to be an issue with the attribute overlay for pointclouds on
nvidia gpus, to be investigated.
Differential Revision: https://developer.blender.org/D15954
2022-09-28 17:54:59 +02:00
|
|
|
continue;
|
2019-03-22 17:03:02 +01:00
|
|
|
}
|
Geometry Nodes: viewport preview
This adds support for showing geometry passed to the Viewer in the 3d
viewport (instead of just in the spreadsheet). The "viewer geometry"
bypasses the group output. So it is not necessary to change the final
output of the node group to be able to see the intermediate geometry.
**Activation and deactivation of a viewer node**
* A viewer node is activated by clicking on it.
* Ctrl+shift+click on any node/socket connects it to the viewer and
makes it active.
* Ctrl+shift+click in empty space deactivates the active viewer.
* When the active viewer is not visible anymore (e.g. another object
is selected, or the current node group is exit), it is deactivated.
* Clicking on the icon in the header of the Viewer node toggles whether
its active or not.
**Pinning**
* The spreadsheet still allows pinning the active viewer as before.
When pinned, the spreadsheet still references the viewer node even
when it becomes inactive.
* The viewport does not support pinning at the moment. It always shows
the active viewer.
**Attribute**
* When a field is linked to the second input of the viewer node it is
displayed as an overlay in the viewport.
* When possible the correct domain for the attribute is determined
automatically. This does not work in all cases. It falls back to the
face corner domain on meshes and the point domain on curves. When
necessary, the domain can be picked manually.
* The spreadsheet now only shows the "Viewer" column for the domain
that is selected in the Viewer node.
* Instance attributes are visualized as a constant color per instance.
**Viewport Options**
* The attribute overlay opacity can be controlled with the "Viewer Node"
setting in the overlays popover.
* A viewport can be configured not to show intermediate viewer-geometry
by disabling the "Viewer Node" option in the "View" menu.
**Implementation Details**
* The "spreadsheet context path" was generalized to a "viewer path" that
is used in more places now.
* The viewer node itself determines the attribute domain, evaluates the
field and stores the result in a `.viewer` attribute.
* A new "viewer attribute' overlay displays the data from the `.viewer`
attribute.
* The ground truth for the active viewer node is stored in the workspace
now. Node editors, spreadsheets and viewports retrieve the active
viewer from there unless they are pinned.
* The depsgraph object iterator has a new "viewer path" setting. When set,
the viewed geometry of the corresponding object is part of the iterator
instead of the final evaluated geometry.
* To support the instance attribute overlay `DupliObject` was extended
to contain the information necessary for drawing the overlay.
* The ctrl+shift+click operator has been refactored so that it can make
existing links to viewers active again.
* The auto-domain-detection in the Viewer node works by checking the
"preferred domain" for every field input. If there is not exactly one
preferred domain, the fallback is used.
Known limitations:
* Loose edges of meshes don't have the attribute overlay. This could be
added separately if necessary.
* Some attributes are hard to visualize as a color directly. For example,
the values might have to be normalized or some should be drawn as arrays.
For now, we encourage users to build node groups that generate appropriate
viewer-geometry. We might include some of that functionality in future versions.
Support for displaying attribute values as text in the viewport is planned as well.
* There seems to be an issue with the attribute overlay for pointclouds on
nvidia gpus, to be investigated.
Differential Revision: https://developer.blender.org/D15954
2022-09-28 17:54:59 +02:00
|
|
|
for (bNodeLink *link : socket->directly_linked_links()) {
|
|
|
|
|
bNodeSocket &target_socket = *link->tosock;
|
|
|
|
|
bNode &target_node = *link->tonode;
|
|
|
|
|
if (is_viewer_socket(target_socket)) {
|
|
|
|
|
if (link->is_muted() || !(target_node.flag & NODE_DO_OUTPUT)) {
|
|
|
|
|
/* This socket is linked to a deactivated viewer, the viewer should be activated. */
|
|
|
|
|
return socket;
|
|
|
|
|
}
|
2023-03-16 16:49:56 +01:00
|
|
|
if (socket->type == SOCK_GEOMETRY) {
|
|
|
|
|
has_linked_geometry_socket = true;
|
2023-02-07 16:01:39 -05:00
|
|
|
}
|
2023-03-30 16:56:56 +02:00
|
|
|
else {
|
|
|
|
|
last_linked_data_socket_index = socket->index();
|
|
|
|
|
}
|
Geometry Nodes: viewport preview
This adds support for showing geometry passed to the Viewer in the 3d
viewport (instead of just in the spreadsheet). The "viewer geometry"
bypasses the group output. So it is not necessary to change the final
output of the node group to be able to see the intermediate geometry.
**Activation and deactivation of a viewer node**
* A viewer node is activated by clicking on it.
* Ctrl+shift+click on any node/socket connects it to the viewer and
makes it active.
* Ctrl+shift+click in empty space deactivates the active viewer.
* When the active viewer is not visible anymore (e.g. another object
is selected, or the current node group is exit), it is deactivated.
* Clicking on the icon in the header of the Viewer node toggles whether
its active or not.
**Pinning**
* The spreadsheet still allows pinning the active viewer as before.
When pinned, the spreadsheet still references the viewer node even
when it becomes inactive.
* The viewport does not support pinning at the moment. It always shows
the active viewer.
**Attribute**
* When a field is linked to the second input of the viewer node it is
displayed as an overlay in the viewport.
* When possible the correct domain for the attribute is determined
automatically. This does not work in all cases. It falls back to the
face corner domain on meshes and the point domain on curves. When
necessary, the domain can be picked manually.
* The spreadsheet now only shows the "Viewer" column for the domain
that is selected in the Viewer node.
* Instance attributes are visualized as a constant color per instance.
**Viewport Options**
* The attribute overlay opacity can be controlled with the "Viewer Node"
setting in the overlays popover.
* A viewport can be configured not to show intermediate viewer-geometry
by disabling the "Viewer Node" option in the "View" menu.
**Implementation Details**
* The "spreadsheet context path" was generalized to a "viewer path" that
is used in more places now.
* The viewer node itself determines the attribute domain, evaluates the
field and stores the result in a `.viewer` attribute.
* A new "viewer attribute' overlay displays the data from the `.viewer`
attribute.
* The ground truth for the active viewer node is stored in the workspace
now. Node editors, spreadsheets and viewports retrieve the active
viewer from there unless they are pinned.
* The depsgraph object iterator has a new "viewer path" setting. When set,
the viewed geometry of the corresponding object is part of the iterator
instead of the final evaluated geometry.
* To support the instance attribute overlay `DupliObject` was extended
to contain the information necessary for drawing the overlay.
* The ctrl+shift+click operator has been refactored so that it can make
existing links to viewers active again.
* The auto-domain-detection in the Viewer node works by checking the
"preferred domain" for every field input. If there is not exactly one
preferred domain, the fallback is used.
Known limitations:
* Loose edges of meshes don't have the attribute overlay. This could be
added separately if necessary.
* Some attributes are hard to visualize as a color directly. For example,
the values might have to be normalized or some should be drawn as arrays.
For now, we encourage users to build node groups that generate appropriate
viewer-geometry. We might include some of that functionality in future versions.
Support for displaying attribute values as text in the viewport is planned as well.
* There seems to be an issue with the attribute overlay for pointclouds on
nvidia gpus, to be investigated.
Differential Revision: https://developer.blender.org/D15954
2022-09-28 17:54:59 +02:00
|
|
|
}
|
2021-10-26 17:53:29 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-30 16:56:56 +02:00
|
|
|
if (last_linked_data_socket_index == -1 && !has_linked_geometry_socket) {
|
2022-09-29 10:28:39 +10:00
|
|
|
/* Return the first socket that can be viewed. */
|
Geometry Nodes: viewport preview
This adds support for showing geometry passed to the Viewer in the 3d
viewport (instead of just in the spreadsheet). The "viewer geometry"
bypasses the group output. So it is not necessary to change the final
output of the node group to be able to see the intermediate geometry.
**Activation and deactivation of a viewer node**
* A viewer node is activated by clicking on it.
* Ctrl+shift+click on any node/socket connects it to the viewer and
makes it active.
* Ctrl+shift+click in empty space deactivates the active viewer.
* When the active viewer is not visible anymore (e.g. another object
is selected, or the current node group is exit), it is deactivated.
* Clicking on the icon in the header of the Viewer node toggles whether
its active or not.
**Pinning**
* The spreadsheet still allows pinning the active viewer as before.
When pinned, the spreadsheet still references the viewer node even
when it becomes inactive.
* The viewport does not support pinning at the moment. It always shows
the active viewer.
**Attribute**
* When a field is linked to the second input of the viewer node it is
displayed as an overlay in the viewport.
* When possible the correct domain for the attribute is determined
automatically. This does not work in all cases. It falls back to the
face corner domain on meshes and the point domain on curves. When
necessary, the domain can be picked manually.
* The spreadsheet now only shows the "Viewer" column for the domain
that is selected in the Viewer node.
* Instance attributes are visualized as a constant color per instance.
**Viewport Options**
* The attribute overlay opacity can be controlled with the "Viewer Node"
setting in the overlays popover.
* A viewport can be configured not to show intermediate viewer-geometry
by disabling the "Viewer Node" option in the "View" menu.
**Implementation Details**
* The "spreadsheet context path" was generalized to a "viewer path" that
is used in more places now.
* The viewer node itself determines the attribute domain, evaluates the
field and stores the result in a `.viewer` attribute.
* A new "viewer attribute' overlay displays the data from the `.viewer`
attribute.
* The ground truth for the active viewer node is stored in the workspace
now. Node editors, spreadsheets and viewports retrieve the active
viewer from there unless they are pinned.
* The depsgraph object iterator has a new "viewer path" setting. When set,
the viewed geometry of the corresponding object is part of the iterator
instead of the final evaluated geometry.
* To support the instance attribute overlay `DupliObject` was extended
to contain the information necessary for drawing the overlay.
* The ctrl+shift+click operator has been refactored so that it can make
existing links to viewers active again.
* The auto-domain-detection in the Viewer node works by checking the
"preferred domain" for every field input. If there is not exactly one
preferred domain, the fallback is used.
Known limitations:
* Loose edges of meshes don't have the attribute overlay. This could be
added separately if necessary.
* Some attributes are hard to visualize as a color directly. For example,
the values might have to be normalized or some should be drawn as arrays.
For now, we encourage users to build node groups that generate appropriate
viewer-geometry. We might include some of that functionality in future versions.
Support for displaying attribute values as text in the viewport is planned as well.
* There seems to be an issue with the attribute overlay for pointclouds on
nvidia gpus, to be investigated.
Differential Revision: https://developer.blender.org/D15954
2022-09-28 17:54:59 +02:00
|
|
|
for (bNodeSocket *socket : node_to_view.output_sockets()) {
|
2023-09-19 10:47:21 +02:00
|
|
|
if (socket_can_be_viewed(node_to_view, *socket)) {
|
Geometry Nodes: viewport preview
This adds support for showing geometry passed to the Viewer in the 3d
viewport (instead of just in the spreadsheet). The "viewer geometry"
bypasses the group output. So it is not necessary to change the final
output of the node group to be able to see the intermediate geometry.
**Activation and deactivation of a viewer node**
* A viewer node is activated by clicking on it.
* Ctrl+shift+click on any node/socket connects it to the viewer and
makes it active.
* Ctrl+shift+click in empty space deactivates the active viewer.
* When the active viewer is not visible anymore (e.g. another object
is selected, or the current node group is exit), it is deactivated.
* Clicking on the icon in the header of the Viewer node toggles whether
its active or not.
**Pinning**
* The spreadsheet still allows pinning the active viewer as before.
When pinned, the spreadsheet still references the viewer node even
when it becomes inactive.
* The viewport does not support pinning at the moment. It always shows
the active viewer.
**Attribute**
* When a field is linked to the second input of the viewer node it is
displayed as an overlay in the viewport.
* When possible the correct domain for the attribute is determined
automatically. This does not work in all cases. It falls back to the
face corner domain on meshes and the point domain on curves. When
necessary, the domain can be picked manually.
* The spreadsheet now only shows the "Viewer" column for the domain
that is selected in the Viewer node.
* Instance attributes are visualized as a constant color per instance.
**Viewport Options**
* The attribute overlay opacity can be controlled with the "Viewer Node"
setting in the overlays popover.
* A viewport can be configured not to show intermediate viewer-geometry
by disabling the "Viewer Node" option in the "View" menu.
**Implementation Details**
* The "spreadsheet context path" was generalized to a "viewer path" that
is used in more places now.
* The viewer node itself determines the attribute domain, evaluates the
field and stores the result in a `.viewer` attribute.
* A new "viewer attribute' overlay displays the data from the `.viewer`
attribute.
* The ground truth for the active viewer node is stored in the workspace
now. Node editors, spreadsheets and viewports retrieve the active
viewer from there unless they are pinned.
* The depsgraph object iterator has a new "viewer path" setting. When set,
the viewed geometry of the corresponding object is part of the iterator
instead of the final evaluated geometry.
* To support the instance attribute overlay `DupliObject` was extended
to contain the information necessary for drawing the overlay.
* The ctrl+shift+click operator has been refactored so that it can make
existing links to viewers active again.
* The auto-domain-detection in the Viewer node works by checking the
"preferred domain" for every field input. If there is not exactly one
preferred domain, the fallback is used.
Known limitations:
* Loose edges of meshes don't have the attribute overlay. This could be
added separately if necessary.
* Some attributes are hard to visualize as a color directly. For example,
the values might have to be normalized or some should be drawn as arrays.
For now, we encourage users to build node groups that generate appropriate
viewer-geometry. We might include some of that functionality in future versions.
Support for displaying attribute values as text in the viewport is planned as well.
* There seems to be an issue with the attribute overlay for pointclouds on
nvidia gpus, to be investigated.
Differential Revision: https://developer.blender.org/D15954
2022-09-28 17:54:59 +02:00
|
|
|
return socket;
|
2019-03-26 21:16:47 +11:00
|
|
|
}
|
|
|
|
|
}
|
Geometry Nodes: viewport preview
This adds support for showing geometry passed to the Viewer in the 3d
viewport (instead of just in the spreadsheet). The "viewer geometry"
bypasses the group output. So it is not necessary to change the final
output of the node group to be able to see the intermediate geometry.
**Activation and deactivation of a viewer node**
* A viewer node is activated by clicking on it.
* Ctrl+shift+click on any node/socket connects it to the viewer and
makes it active.
* Ctrl+shift+click in empty space deactivates the active viewer.
* When the active viewer is not visible anymore (e.g. another object
is selected, or the current node group is exit), it is deactivated.
* Clicking on the icon in the header of the Viewer node toggles whether
its active or not.
**Pinning**
* The spreadsheet still allows pinning the active viewer as before.
When pinned, the spreadsheet still references the viewer node even
when it becomes inactive.
* The viewport does not support pinning at the moment. It always shows
the active viewer.
**Attribute**
* When a field is linked to the second input of the viewer node it is
displayed as an overlay in the viewport.
* When possible the correct domain for the attribute is determined
automatically. This does not work in all cases. It falls back to the
face corner domain on meshes and the point domain on curves. When
necessary, the domain can be picked manually.
* The spreadsheet now only shows the "Viewer" column for the domain
that is selected in the Viewer node.
* Instance attributes are visualized as a constant color per instance.
**Viewport Options**
* The attribute overlay opacity can be controlled with the "Viewer Node"
setting in the overlays popover.
* A viewport can be configured not to show intermediate viewer-geometry
by disabling the "Viewer Node" option in the "View" menu.
**Implementation Details**
* The "spreadsheet context path" was generalized to a "viewer path" that
is used in more places now.
* The viewer node itself determines the attribute domain, evaluates the
field and stores the result in a `.viewer` attribute.
* A new "viewer attribute' overlay displays the data from the `.viewer`
attribute.
* The ground truth for the active viewer node is stored in the workspace
now. Node editors, spreadsheets and viewports retrieve the active
viewer from there unless they are pinned.
* The depsgraph object iterator has a new "viewer path" setting. When set,
the viewed geometry of the corresponding object is part of the iterator
instead of the final evaluated geometry.
* To support the instance attribute overlay `DupliObject` was extended
to contain the information necessary for drawing the overlay.
* The ctrl+shift+click operator has been refactored so that it can make
existing links to viewers active again.
* The auto-domain-detection in the Viewer node works by checking the
"preferred domain" for every field input. If there is not exactly one
preferred domain, the fallback is used.
Known limitations:
* Loose edges of meshes don't have the attribute overlay. This could be
added separately if necessary.
* Some attributes are hard to visualize as a color directly. For example,
the values might have to be normalized or some should be drawn as arrays.
For now, we encourage users to build node groups that generate appropriate
viewer-geometry. We might include some of that functionality in future versions.
Support for displaying attribute values as text in the viewport is planned as well.
* There seems to be an issue with the attribute overlay for pointclouds on
nvidia gpus, to be investigated.
Differential Revision: https://developer.blender.org/D15954
2022-09-28 17:54:59 +02:00
|
|
|
return nullptr;
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
Geometry Nodes: viewport preview
This adds support for showing geometry passed to the Viewer in the 3d
viewport (instead of just in the spreadsheet). The "viewer geometry"
bypasses the group output. So it is not necessary to change the final
output of the node group to be able to see the intermediate geometry.
**Activation and deactivation of a viewer node**
* A viewer node is activated by clicking on it.
* Ctrl+shift+click on any node/socket connects it to the viewer and
makes it active.
* Ctrl+shift+click in empty space deactivates the active viewer.
* When the active viewer is not visible anymore (e.g. another object
is selected, or the current node group is exit), it is deactivated.
* Clicking on the icon in the header of the Viewer node toggles whether
its active or not.
**Pinning**
* The spreadsheet still allows pinning the active viewer as before.
When pinned, the spreadsheet still references the viewer node even
when it becomes inactive.
* The viewport does not support pinning at the moment. It always shows
the active viewer.
**Attribute**
* When a field is linked to the second input of the viewer node it is
displayed as an overlay in the viewport.
* When possible the correct domain for the attribute is determined
automatically. This does not work in all cases. It falls back to the
face corner domain on meshes and the point domain on curves. When
necessary, the domain can be picked manually.
* The spreadsheet now only shows the "Viewer" column for the domain
that is selected in the Viewer node.
* Instance attributes are visualized as a constant color per instance.
**Viewport Options**
* The attribute overlay opacity can be controlled with the "Viewer Node"
setting in the overlays popover.
* A viewport can be configured not to show intermediate viewer-geometry
by disabling the "Viewer Node" option in the "View" menu.
**Implementation Details**
* The "spreadsheet context path" was generalized to a "viewer path" that
is used in more places now.
* The viewer node itself determines the attribute domain, evaluates the
field and stores the result in a `.viewer` attribute.
* A new "viewer attribute' overlay displays the data from the `.viewer`
attribute.
* The ground truth for the active viewer node is stored in the workspace
now. Node editors, spreadsheets and viewports retrieve the active
viewer from there unless they are pinned.
* The depsgraph object iterator has a new "viewer path" setting. When set,
the viewed geometry of the corresponding object is part of the iterator
instead of the final evaluated geometry.
* To support the instance attribute overlay `DupliObject` was extended
to contain the information necessary for drawing the overlay.
* The ctrl+shift+click operator has been refactored so that it can make
existing links to viewers active again.
* The auto-domain-detection in the Viewer node works by checking the
"preferred domain" for every field input. If there is not exactly one
preferred domain, the fallback is used.
Known limitations:
* Loose edges of meshes don't have the attribute overlay. This could be
added separately if necessary.
* Some attributes are hard to visualize as a color directly. For example,
the values might have to be normalized or some should be drawn as arrays.
For now, we encourage users to build node groups that generate appropriate
viewer-geometry. We might include some of that functionality in future versions.
Support for displaying attribute values as text in the viewport is planned as well.
* There seems to be an issue with the attribute overlay for pointclouds on
nvidia gpus, to be investigated.
Differential Revision: https://developer.blender.org/D15954
2022-09-28 17:54:59 +02:00
|
|
|
|
|
|
|
|
/* Pick the next socket to be linked to the viewer. */
|
|
|
|
|
const int tot_outputs = node_to_view.output_sockets().size();
|
|
|
|
|
for (const int offset : IndexRange(1, tot_outputs)) {
|
2023-03-30 16:56:56 +02:00
|
|
|
const int index = (last_linked_data_socket_index + offset) % tot_outputs;
|
Geometry Nodes: viewport preview
This adds support for showing geometry passed to the Viewer in the 3d
viewport (instead of just in the spreadsheet). The "viewer geometry"
bypasses the group output. So it is not necessary to change the final
output of the node group to be able to see the intermediate geometry.
**Activation and deactivation of a viewer node**
* A viewer node is activated by clicking on it.
* Ctrl+shift+click on any node/socket connects it to the viewer and
makes it active.
* Ctrl+shift+click in empty space deactivates the active viewer.
* When the active viewer is not visible anymore (e.g. another object
is selected, or the current node group is exit), it is deactivated.
* Clicking on the icon in the header of the Viewer node toggles whether
its active or not.
**Pinning**
* The spreadsheet still allows pinning the active viewer as before.
When pinned, the spreadsheet still references the viewer node even
when it becomes inactive.
* The viewport does not support pinning at the moment. It always shows
the active viewer.
**Attribute**
* When a field is linked to the second input of the viewer node it is
displayed as an overlay in the viewport.
* When possible the correct domain for the attribute is determined
automatically. This does not work in all cases. It falls back to the
face corner domain on meshes and the point domain on curves. When
necessary, the domain can be picked manually.
* The spreadsheet now only shows the "Viewer" column for the domain
that is selected in the Viewer node.
* Instance attributes are visualized as a constant color per instance.
**Viewport Options**
* The attribute overlay opacity can be controlled with the "Viewer Node"
setting in the overlays popover.
* A viewport can be configured not to show intermediate viewer-geometry
by disabling the "Viewer Node" option in the "View" menu.
**Implementation Details**
* The "spreadsheet context path" was generalized to a "viewer path" that
is used in more places now.
* The viewer node itself determines the attribute domain, evaluates the
field and stores the result in a `.viewer` attribute.
* A new "viewer attribute' overlay displays the data from the `.viewer`
attribute.
* The ground truth for the active viewer node is stored in the workspace
now. Node editors, spreadsheets and viewports retrieve the active
viewer from there unless they are pinned.
* The depsgraph object iterator has a new "viewer path" setting. When set,
the viewed geometry of the corresponding object is part of the iterator
instead of the final evaluated geometry.
* To support the instance attribute overlay `DupliObject` was extended
to contain the information necessary for drawing the overlay.
* The ctrl+shift+click operator has been refactored so that it can make
existing links to viewers active again.
* The auto-domain-detection in the Viewer node works by checking the
"preferred domain" for every field input. If there is not exactly one
preferred domain, the fallback is used.
Known limitations:
* Loose edges of meshes don't have the attribute overlay. This could be
added separately if necessary.
* Some attributes are hard to visualize as a color directly. For example,
the values might have to be normalized or some should be drawn as arrays.
For now, we encourage users to build node groups that generate appropriate
viewer-geometry. We might include some of that functionality in future versions.
Support for displaying attribute values as text in the viewport is planned as well.
* There seems to be an issue with the attribute overlay for pointclouds on
nvidia gpus, to be investigated.
Differential Revision: https://developer.blender.org/D15954
2022-09-28 17:54:59 +02:00
|
|
|
bNodeSocket &output_socket = node_to_view.output_socket(index);
|
2023-09-19 10:47:21 +02:00
|
|
|
if (!socket_can_be_viewed(node_to_view, output_socket)) {
|
Geometry Nodes: viewport preview
This adds support for showing geometry passed to the Viewer in the 3d
viewport (instead of just in the spreadsheet). The "viewer geometry"
bypasses the group output. So it is not necessary to change the final
output of the node group to be able to see the intermediate geometry.
**Activation and deactivation of a viewer node**
* A viewer node is activated by clicking on it.
* Ctrl+shift+click on any node/socket connects it to the viewer and
makes it active.
* Ctrl+shift+click in empty space deactivates the active viewer.
* When the active viewer is not visible anymore (e.g. another object
is selected, or the current node group is exit), it is deactivated.
* Clicking on the icon in the header of the Viewer node toggles whether
its active or not.
**Pinning**
* The spreadsheet still allows pinning the active viewer as before.
When pinned, the spreadsheet still references the viewer node even
when it becomes inactive.
* The viewport does not support pinning at the moment. It always shows
the active viewer.
**Attribute**
* When a field is linked to the second input of the viewer node it is
displayed as an overlay in the viewport.
* When possible the correct domain for the attribute is determined
automatically. This does not work in all cases. It falls back to the
face corner domain on meshes and the point domain on curves. When
necessary, the domain can be picked manually.
* The spreadsheet now only shows the "Viewer" column for the domain
that is selected in the Viewer node.
* Instance attributes are visualized as a constant color per instance.
**Viewport Options**
* The attribute overlay opacity can be controlled with the "Viewer Node"
setting in the overlays popover.
* A viewport can be configured not to show intermediate viewer-geometry
by disabling the "Viewer Node" option in the "View" menu.
**Implementation Details**
* The "spreadsheet context path" was generalized to a "viewer path" that
is used in more places now.
* The viewer node itself determines the attribute domain, evaluates the
field and stores the result in a `.viewer` attribute.
* A new "viewer attribute' overlay displays the data from the `.viewer`
attribute.
* The ground truth for the active viewer node is stored in the workspace
now. Node editors, spreadsheets and viewports retrieve the active
viewer from there unless they are pinned.
* The depsgraph object iterator has a new "viewer path" setting. When set,
the viewed geometry of the corresponding object is part of the iterator
instead of the final evaluated geometry.
* To support the instance attribute overlay `DupliObject` was extended
to contain the information necessary for drawing the overlay.
* The ctrl+shift+click operator has been refactored so that it can make
existing links to viewers active again.
* The auto-domain-detection in the Viewer node works by checking the
"preferred domain" for every field input. If there is not exactly one
preferred domain, the fallback is used.
Known limitations:
* Loose edges of meshes don't have the attribute overlay. This could be
added separately if necessary.
* Some attributes are hard to visualize as a color directly. For example,
the values might have to be normalized or some should be drawn as arrays.
For now, we encourage users to build node groups that generate appropriate
viewer-geometry. We might include some of that functionality in future versions.
Support for displaying attribute values as text in the viewport is planned as well.
* There seems to be an issue with the attribute overlay for pointclouds on
nvidia gpus, to be investigated.
Differential Revision: https://developer.blender.org/D15954
2022-09-28 17:54:59 +02:00
|
|
|
continue;
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
2023-03-16 16:49:56 +01:00
|
|
|
if (has_linked_geometry_socket && output_socket.type == SOCK_GEOMETRY) {
|
|
|
|
|
/* Skip geometry sockets when cycling if one is already viewed. */
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
Geometry Nodes: viewport preview
This adds support for showing geometry passed to the Viewer in the 3d
viewport (instead of just in the spreadsheet). The "viewer geometry"
bypasses the group output. So it is not necessary to change the final
output of the node group to be able to see the intermediate geometry.
**Activation and deactivation of a viewer node**
* A viewer node is activated by clicking on it.
* Ctrl+shift+click on any node/socket connects it to the viewer and
makes it active.
* Ctrl+shift+click in empty space deactivates the active viewer.
* When the active viewer is not visible anymore (e.g. another object
is selected, or the current node group is exit), it is deactivated.
* Clicking on the icon in the header of the Viewer node toggles whether
its active or not.
**Pinning**
* The spreadsheet still allows pinning the active viewer as before.
When pinned, the spreadsheet still references the viewer node even
when it becomes inactive.
* The viewport does not support pinning at the moment. It always shows
the active viewer.
**Attribute**
* When a field is linked to the second input of the viewer node it is
displayed as an overlay in the viewport.
* When possible the correct domain for the attribute is determined
automatically. This does not work in all cases. It falls back to the
face corner domain on meshes and the point domain on curves. When
necessary, the domain can be picked manually.
* The spreadsheet now only shows the "Viewer" column for the domain
that is selected in the Viewer node.
* Instance attributes are visualized as a constant color per instance.
**Viewport Options**
* The attribute overlay opacity can be controlled with the "Viewer Node"
setting in the overlays popover.
* A viewport can be configured not to show intermediate viewer-geometry
by disabling the "Viewer Node" option in the "View" menu.
**Implementation Details**
* The "spreadsheet context path" was generalized to a "viewer path" that
is used in more places now.
* The viewer node itself determines the attribute domain, evaluates the
field and stores the result in a `.viewer` attribute.
* A new "viewer attribute' overlay displays the data from the `.viewer`
attribute.
* The ground truth for the active viewer node is stored in the workspace
now. Node editors, spreadsheets and viewports retrieve the active
viewer from there unless they are pinned.
* The depsgraph object iterator has a new "viewer path" setting. When set,
the viewed geometry of the corresponding object is part of the iterator
instead of the final evaluated geometry.
* To support the instance attribute overlay `DupliObject` was extended
to contain the information necessary for drawing the overlay.
* The ctrl+shift+click operator has been refactored so that it can make
existing links to viewers active again.
* The auto-domain-detection in the Viewer node works by checking the
"preferred domain" for every field input. If there is not exactly one
preferred domain, the fallback is used.
Known limitations:
* Loose edges of meshes don't have the attribute overlay. This could be
added separately if necessary.
* Some attributes are hard to visualize as a color directly. For example,
the values might have to be normalized or some should be drawn as arrays.
For now, we encourage users to build node groups that generate appropriate
viewer-geometry. We might include some of that functionality in future versions.
Support for displaying attribute values as text in the viewport is planned as well.
* There seems to be an issue with the attribute overlay for pointclouds on
nvidia gpus, to be investigated.
Differential Revision: https://developer.blender.org/D15954
2022-09-28 17:54:59 +02:00
|
|
|
bool is_currently_viewed = false;
|
|
|
|
|
for (const bNodeLink *link : output_socket.directly_linked_links()) {
|
|
|
|
|
bNodeSocket &target_socket = *link->tosock;
|
|
|
|
|
bNode &target_node = *link->tonode;
|
|
|
|
|
if (!is_viewer_socket(target_socket)) {
|
2021-10-26 11:25:32 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
Geometry Nodes: viewport preview
This adds support for showing geometry passed to the Viewer in the 3d
viewport (instead of just in the spreadsheet). The "viewer geometry"
bypasses the group output. So it is not necessary to change the final
output of the node group to be able to see the intermediate geometry.
**Activation and deactivation of a viewer node**
* A viewer node is activated by clicking on it.
* Ctrl+shift+click on any node/socket connects it to the viewer and
makes it active.
* Ctrl+shift+click in empty space deactivates the active viewer.
* When the active viewer is not visible anymore (e.g. another object
is selected, or the current node group is exit), it is deactivated.
* Clicking on the icon in the header of the Viewer node toggles whether
its active or not.
**Pinning**
* The spreadsheet still allows pinning the active viewer as before.
When pinned, the spreadsheet still references the viewer node even
when it becomes inactive.
* The viewport does not support pinning at the moment. It always shows
the active viewer.
**Attribute**
* When a field is linked to the second input of the viewer node it is
displayed as an overlay in the viewport.
* When possible the correct domain for the attribute is determined
automatically. This does not work in all cases. It falls back to the
face corner domain on meshes and the point domain on curves. When
necessary, the domain can be picked manually.
* The spreadsheet now only shows the "Viewer" column for the domain
that is selected in the Viewer node.
* Instance attributes are visualized as a constant color per instance.
**Viewport Options**
* The attribute overlay opacity can be controlled with the "Viewer Node"
setting in the overlays popover.
* A viewport can be configured not to show intermediate viewer-geometry
by disabling the "Viewer Node" option in the "View" menu.
**Implementation Details**
* The "spreadsheet context path" was generalized to a "viewer path" that
is used in more places now.
* The viewer node itself determines the attribute domain, evaluates the
field and stores the result in a `.viewer` attribute.
* A new "viewer attribute' overlay displays the data from the `.viewer`
attribute.
* The ground truth for the active viewer node is stored in the workspace
now. Node editors, spreadsheets and viewports retrieve the active
viewer from there unless they are pinned.
* The depsgraph object iterator has a new "viewer path" setting. When set,
the viewed geometry of the corresponding object is part of the iterator
instead of the final evaluated geometry.
* To support the instance attribute overlay `DupliObject` was extended
to contain the information necessary for drawing the overlay.
* The ctrl+shift+click operator has been refactored so that it can make
existing links to viewers active again.
* The auto-domain-detection in the Viewer node works by checking the
"preferred domain" for every field input. If there is not exactly one
preferred domain, the fallback is used.
Known limitations:
* Loose edges of meshes don't have the attribute overlay. This could be
added separately if necessary.
* Some attributes are hard to visualize as a color directly. For example,
the values might have to be normalized or some should be drawn as arrays.
For now, we encourage users to build node groups that generate appropriate
viewer-geometry. We might include some of that functionality in future versions.
Support for displaying attribute values as text in the viewport is planned as well.
* There seems to be an issue with the attribute overlay for pointclouds on
nvidia gpus, to be investigated.
Differential Revision: https://developer.blender.org/D15954
2022-09-28 17:54:59 +02:00
|
|
|
if (link->is_muted()) {
|
2021-10-26 11:25:32 +02:00
|
|
|
continue;
|
2019-03-26 21:16:47 +11:00
|
|
|
}
|
Geometry Nodes: viewport preview
This adds support for showing geometry passed to the Viewer in the 3d
viewport (instead of just in the spreadsheet). The "viewer geometry"
bypasses the group output. So it is not necessary to change the final
output of the node group to be able to see the intermediate geometry.
**Activation and deactivation of a viewer node**
* A viewer node is activated by clicking on it.
* Ctrl+shift+click on any node/socket connects it to the viewer and
makes it active.
* Ctrl+shift+click in empty space deactivates the active viewer.
* When the active viewer is not visible anymore (e.g. another object
is selected, or the current node group is exit), it is deactivated.
* Clicking on the icon in the header of the Viewer node toggles whether
its active or not.
**Pinning**
* The spreadsheet still allows pinning the active viewer as before.
When pinned, the spreadsheet still references the viewer node even
when it becomes inactive.
* The viewport does not support pinning at the moment. It always shows
the active viewer.
**Attribute**
* When a field is linked to the second input of the viewer node it is
displayed as an overlay in the viewport.
* When possible the correct domain for the attribute is determined
automatically. This does not work in all cases. It falls back to the
face corner domain on meshes and the point domain on curves. When
necessary, the domain can be picked manually.
* The spreadsheet now only shows the "Viewer" column for the domain
that is selected in the Viewer node.
* Instance attributes are visualized as a constant color per instance.
**Viewport Options**
* The attribute overlay opacity can be controlled with the "Viewer Node"
setting in the overlays popover.
* A viewport can be configured not to show intermediate viewer-geometry
by disabling the "Viewer Node" option in the "View" menu.
**Implementation Details**
* The "spreadsheet context path" was generalized to a "viewer path" that
is used in more places now.
* The viewer node itself determines the attribute domain, evaluates the
field and stores the result in a `.viewer` attribute.
* A new "viewer attribute' overlay displays the data from the `.viewer`
attribute.
* The ground truth for the active viewer node is stored in the workspace
now. Node editors, spreadsheets and viewports retrieve the active
viewer from there unless they are pinned.
* The depsgraph object iterator has a new "viewer path" setting. When set,
the viewed geometry of the corresponding object is part of the iterator
instead of the final evaluated geometry.
* To support the instance attribute overlay `DupliObject` was extended
to contain the information necessary for drawing the overlay.
* The ctrl+shift+click operator has been refactored so that it can make
existing links to viewers active again.
* The auto-domain-detection in the Viewer node works by checking the
"preferred domain" for every field input. If there is not exactly one
preferred domain, the fallback is used.
Known limitations:
* Loose edges of meshes don't have the attribute overlay. This could be
added separately if necessary.
* Some attributes are hard to visualize as a color directly. For example,
the values might have to be normalized or some should be drawn as arrays.
For now, we encourage users to build node groups that generate appropriate
viewer-geometry. We might include some of that functionality in future versions.
Support for displaying attribute values as text in the viewport is planned as well.
* There seems to be an issue with the attribute overlay for pointclouds on
nvidia gpus, to be investigated.
Differential Revision: https://developer.blender.org/D15954
2022-09-28 17:54:59 +02:00
|
|
|
if (!(target_node.flag & NODE_DO_OUTPUT)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
is_currently_viewed = true;
|
|
|
|
|
break;
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
Geometry Nodes: viewport preview
This adds support for showing geometry passed to the Viewer in the 3d
viewport (instead of just in the spreadsheet). The "viewer geometry"
bypasses the group output. So it is not necessary to change the final
output of the node group to be able to see the intermediate geometry.
**Activation and deactivation of a viewer node**
* A viewer node is activated by clicking on it.
* Ctrl+shift+click on any node/socket connects it to the viewer and
makes it active.
* Ctrl+shift+click in empty space deactivates the active viewer.
* When the active viewer is not visible anymore (e.g. another object
is selected, or the current node group is exit), it is deactivated.
* Clicking on the icon in the header of the Viewer node toggles whether
its active or not.
**Pinning**
* The spreadsheet still allows pinning the active viewer as before.
When pinned, the spreadsheet still references the viewer node even
when it becomes inactive.
* The viewport does not support pinning at the moment. It always shows
the active viewer.
**Attribute**
* When a field is linked to the second input of the viewer node it is
displayed as an overlay in the viewport.
* When possible the correct domain for the attribute is determined
automatically. This does not work in all cases. It falls back to the
face corner domain on meshes and the point domain on curves. When
necessary, the domain can be picked manually.
* The spreadsheet now only shows the "Viewer" column for the domain
that is selected in the Viewer node.
* Instance attributes are visualized as a constant color per instance.
**Viewport Options**
* The attribute overlay opacity can be controlled with the "Viewer Node"
setting in the overlays popover.
* A viewport can be configured not to show intermediate viewer-geometry
by disabling the "Viewer Node" option in the "View" menu.
**Implementation Details**
* The "spreadsheet context path" was generalized to a "viewer path" that
is used in more places now.
* The viewer node itself determines the attribute domain, evaluates the
field and stores the result in a `.viewer` attribute.
* A new "viewer attribute' overlay displays the data from the `.viewer`
attribute.
* The ground truth for the active viewer node is stored in the workspace
now. Node editors, spreadsheets and viewports retrieve the active
viewer from there unless they are pinned.
* The depsgraph object iterator has a new "viewer path" setting. When set,
the viewed geometry of the corresponding object is part of the iterator
instead of the final evaluated geometry.
* To support the instance attribute overlay `DupliObject` was extended
to contain the information necessary for drawing the overlay.
* The ctrl+shift+click operator has been refactored so that it can make
existing links to viewers active again.
* The auto-domain-detection in the Viewer node works by checking the
"preferred domain" for every field input. If there is not exactly one
preferred domain, the fallback is used.
Known limitations:
* Loose edges of meshes don't have the attribute overlay. This could be
added separately if necessary.
* Some attributes are hard to visualize as a color directly. For example,
the values might have to be normalized or some should be drawn as arrays.
For now, we encourage users to build node groups that generate appropriate
viewer-geometry. We might include some of that functionality in future versions.
Support for displaying attribute values as text in the viewport is planned as well.
* There seems to be an issue with the attribute overlay for pointclouds on
nvidia gpus, to be investigated.
Differential Revision: https://developer.blender.org/D15954
2022-09-28 17:54:59 +02:00
|
|
|
if (is_currently_viewed) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
return &output_socket;
|
2021-10-26 11:25:32 +02:00
|
|
|
}
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
Geometry Nodes: viewport preview
This adds support for showing geometry passed to the Viewer in the 3d
viewport (instead of just in the spreadsheet). The "viewer geometry"
bypasses the group output. So it is not necessary to change the final
output of the node group to be able to see the intermediate geometry.
**Activation and deactivation of a viewer node**
* A viewer node is activated by clicking on it.
* Ctrl+shift+click on any node/socket connects it to the viewer and
makes it active.
* Ctrl+shift+click in empty space deactivates the active viewer.
* When the active viewer is not visible anymore (e.g. another object
is selected, or the current node group is exit), it is deactivated.
* Clicking on the icon in the header of the Viewer node toggles whether
its active or not.
**Pinning**
* The spreadsheet still allows pinning the active viewer as before.
When pinned, the spreadsheet still references the viewer node even
when it becomes inactive.
* The viewport does not support pinning at the moment. It always shows
the active viewer.
**Attribute**
* When a field is linked to the second input of the viewer node it is
displayed as an overlay in the viewport.
* When possible the correct domain for the attribute is determined
automatically. This does not work in all cases. It falls back to the
face corner domain on meshes and the point domain on curves. When
necessary, the domain can be picked manually.
* The spreadsheet now only shows the "Viewer" column for the domain
that is selected in the Viewer node.
* Instance attributes are visualized as a constant color per instance.
**Viewport Options**
* The attribute overlay opacity can be controlled with the "Viewer Node"
setting in the overlays popover.
* A viewport can be configured not to show intermediate viewer-geometry
by disabling the "Viewer Node" option in the "View" menu.
**Implementation Details**
* The "spreadsheet context path" was generalized to a "viewer path" that
is used in more places now.
* The viewer node itself determines the attribute domain, evaluates the
field and stores the result in a `.viewer` attribute.
* A new "viewer attribute' overlay displays the data from the `.viewer`
attribute.
* The ground truth for the active viewer node is stored in the workspace
now. Node editors, spreadsheets and viewports retrieve the active
viewer from there unless they are pinned.
* The depsgraph object iterator has a new "viewer path" setting. When set,
the viewed geometry of the corresponding object is part of the iterator
instead of the final evaluated geometry.
* To support the instance attribute overlay `DupliObject` was extended
to contain the information necessary for drawing the overlay.
* The ctrl+shift+click operator has been refactored so that it can make
existing links to viewers active again.
* The auto-domain-detection in the Viewer node works by checking the
"preferred domain" for every field input. If there is not exactly one
preferred domain, the fallback is used.
Known limitations:
* Loose edges of meshes don't have the attribute overlay. This could be
added separately if necessary.
* Some attributes are hard to visualize as a color directly. For example,
the values might have to be normalized or some should be drawn as arrays.
For now, we encourage users to build node groups that generate appropriate
viewer-geometry. We might include some of that functionality in future versions.
Support for displaying attribute values as text in the viewport is planned as well.
* There seems to be an issue with the attribute overlay for pointclouds on
nvidia gpus, to be investigated.
Differential Revision: https://developer.blender.org/D15954
2022-09-28 17:54:59 +02:00
|
|
|
static void finalize_viewer_link(const bContext &C,
|
|
|
|
|
SpaceNode &snode,
|
|
|
|
|
bNode &viewer_node,
|
|
|
|
|
bNodeLink &viewer_link)
|
2021-10-26 11:25:32 +02:00
|
|
|
{
|
Geometry Nodes: viewport preview
This adds support for showing geometry passed to the Viewer in the 3d
viewport (instead of just in the spreadsheet). The "viewer geometry"
bypasses the group output. So it is not necessary to change the final
output of the node group to be able to see the intermediate geometry.
**Activation and deactivation of a viewer node**
* A viewer node is activated by clicking on it.
* Ctrl+shift+click on any node/socket connects it to the viewer and
makes it active.
* Ctrl+shift+click in empty space deactivates the active viewer.
* When the active viewer is not visible anymore (e.g. another object
is selected, or the current node group is exit), it is deactivated.
* Clicking on the icon in the header of the Viewer node toggles whether
its active or not.
**Pinning**
* The spreadsheet still allows pinning the active viewer as before.
When pinned, the spreadsheet still references the viewer node even
when it becomes inactive.
* The viewport does not support pinning at the moment. It always shows
the active viewer.
**Attribute**
* When a field is linked to the second input of the viewer node it is
displayed as an overlay in the viewport.
* When possible the correct domain for the attribute is determined
automatically. This does not work in all cases. It falls back to the
face corner domain on meshes and the point domain on curves. When
necessary, the domain can be picked manually.
* The spreadsheet now only shows the "Viewer" column for the domain
that is selected in the Viewer node.
* Instance attributes are visualized as a constant color per instance.
**Viewport Options**
* The attribute overlay opacity can be controlled with the "Viewer Node"
setting in the overlays popover.
* A viewport can be configured not to show intermediate viewer-geometry
by disabling the "Viewer Node" option in the "View" menu.
**Implementation Details**
* The "spreadsheet context path" was generalized to a "viewer path" that
is used in more places now.
* The viewer node itself determines the attribute domain, evaluates the
field and stores the result in a `.viewer` attribute.
* A new "viewer attribute' overlay displays the data from the `.viewer`
attribute.
* The ground truth for the active viewer node is stored in the workspace
now. Node editors, spreadsheets and viewports retrieve the active
viewer from there unless they are pinned.
* The depsgraph object iterator has a new "viewer path" setting. When set,
the viewed geometry of the corresponding object is part of the iterator
instead of the final evaluated geometry.
* To support the instance attribute overlay `DupliObject` was extended
to contain the information necessary for drawing the overlay.
* The ctrl+shift+click operator has been refactored so that it can make
existing links to viewers active again.
* The auto-domain-detection in the Viewer node works by checking the
"preferred domain" for every field input. If there is not exactly one
preferred domain, the fallback is used.
Known limitations:
* Loose edges of meshes don't have the attribute overlay. This could be
added separately if necessary.
* Some attributes are hard to visualize as a color directly. For example,
the values might have to be normalized or some should be drawn as arrays.
For now, we encourage users to build node groups that generate appropriate
viewer-geometry. We might include some of that functionality in future versions.
Support for displaying attribute values as text in the viewport is planned as well.
* There seems to be an issue with the attribute overlay for pointclouds on
nvidia gpus, to be investigated.
Differential Revision: https://developer.blender.org/D15954
2022-09-28 17:54:59 +02:00
|
|
|
Main *bmain = CTX_data_main(&C);
|
|
|
|
|
remove_links_to_unavailable_viewer_sockets(*snode.edittree, viewer_node);
|
|
|
|
|
viewer_link.flag &= ~NODE_LINK_MUTED;
|
|
|
|
|
viewer_node.flag &= ~NODE_MUTED;
|
|
|
|
|
viewer_node.flag |= NODE_DO_OUTPUT;
|
|
|
|
|
if (snode.edittree->type == NTREE_GEOMETRY) {
|
|
|
|
|
viewer_path::activate_geometry_node(*bmain, snode, viewer_node);
|
|
|
|
|
}
|
|
|
|
|
ED_node_tree_propagate_change(&C, bmain, snode.edittree);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int view_socket(const bContext &C,
|
|
|
|
|
SpaceNode &snode,
|
|
|
|
|
bNodeTree &btree,
|
|
|
|
|
bNode &bnode_to_view,
|
|
|
|
|
bNodeSocket &bsocket_to_view)
|
|
|
|
|
{
|
|
|
|
|
bNode *viewer_node = nullptr;
|
|
|
|
|
/* Try to find a viewer that is already active. */
|
2022-12-02 11:12:51 -06:00
|
|
|
for (bNode *node : btree.all_nodes()) {
|
Geometry Nodes: viewport preview
This adds support for showing geometry passed to the Viewer in the 3d
viewport (instead of just in the spreadsheet). The "viewer geometry"
bypasses the group output. So it is not necessary to change the final
output of the node group to be able to see the intermediate geometry.
**Activation and deactivation of a viewer node**
* A viewer node is activated by clicking on it.
* Ctrl+shift+click on any node/socket connects it to the viewer and
makes it active.
* Ctrl+shift+click in empty space deactivates the active viewer.
* When the active viewer is not visible anymore (e.g. another object
is selected, or the current node group is exit), it is deactivated.
* Clicking on the icon in the header of the Viewer node toggles whether
its active or not.
**Pinning**
* The spreadsheet still allows pinning the active viewer as before.
When pinned, the spreadsheet still references the viewer node even
when it becomes inactive.
* The viewport does not support pinning at the moment. It always shows
the active viewer.
**Attribute**
* When a field is linked to the second input of the viewer node it is
displayed as an overlay in the viewport.
* When possible the correct domain for the attribute is determined
automatically. This does not work in all cases. It falls back to the
face corner domain on meshes and the point domain on curves. When
necessary, the domain can be picked manually.
* The spreadsheet now only shows the "Viewer" column for the domain
that is selected in the Viewer node.
* Instance attributes are visualized as a constant color per instance.
**Viewport Options**
* The attribute overlay opacity can be controlled with the "Viewer Node"
setting in the overlays popover.
* A viewport can be configured not to show intermediate viewer-geometry
by disabling the "Viewer Node" option in the "View" menu.
**Implementation Details**
* The "spreadsheet context path" was generalized to a "viewer path" that
is used in more places now.
* The viewer node itself determines the attribute domain, evaluates the
field and stores the result in a `.viewer` attribute.
* A new "viewer attribute' overlay displays the data from the `.viewer`
attribute.
* The ground truth for the active viewer node is stored in the workspace
now. Node editors, spreadsheets and viewports retrieve the active
viewer from there unless they are pinned.
* The depsgraph object iterator has a new "viewer path" setting. When set,
the viewed geometry of the corresponding object is part of the iterator
instead of the final evaluated geometry.
* To support the instance attribute overlay `DupliObject` was extended
to contain the information necessary for drawing the overlay.
* The ctrl+shift+click operator has been refactored so that it can make
existing links to viewers active again.
* The auto-domain-detection in the Viewer node works by checking the
"preferred domain" for every field input. If there is not exactly one
preferred domain, the fallback is used.
Known limitations:
* Loose edges of meshes don't have the attribute overlay. This could be
added separately if necessary.
* Some attributes are hard to visualize as a color directly. For example,
the values might have to be normalized or some should be drawn as arrays.
For now, we encourage users to build node groups that generate appropriate
viewer-geometry. We might include some of that functionality in future versions.
Support for displaying attribute values as text in the viewport is planned as well.
* There seems to be an issue with the attribute overlay for pointclouds on
nvidia gpus, to be investigated.
Differential Revision: https://developer.blender.org/D15954
2022-09-28 17:54:59 +02:00
|
|
|
if (is_viewer_node(*node)) {
|
|
|
|
|
if (node->flag & NODE_DO_OUTPUT) {
|
|
|
|
|
viewer_node = node;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-10-26 11:25:32 +02:00
|
|
|
|
Geometry Nodes: viewport preview
This adds support for showing geometry passed to the Viewer in the 3d
viewport (instead of just in the spreadsheet). The "viewer geometry"
bypasses the group output. So it is not necessary to change the final
output of the node group to be able to see the intermediate geometry.
**Activation and deactivation of a viewer node**
* A viewer node is activated by clicking on it.
* Ctrl+shift+click on any node/socket connects it to the viewer and
makes it active.
* Ctrl+shift+click in empty space deactivates the active viewer.
* When the active viewer is not visible anymore (e.g. another object
is selected, or the current node group is exit), it is deactivated.
* Clicking on the icon in the header of the Viewer node toggles whether
its active or not.
**Pinning**
* The spreadsheet still allows pinning the active viewer as before.
When pinned, the spreadsheet still references the viewer node even
when it becomes inactive.
* The viewport does not support pinning at the moment. It always shows
the active viewer.
**Attribute**
* When a field is linked to the second input of the viewer node it is
displayed as an overlay in the viewport.
* When possible the correct domain for the attribute is determined
automatically. This does not work in all cases. It falls back to the
face corner domain on meshes and the point domain on curves. When
necessary, the domain can be picked manually.
* The spreadsheet now only shows the "Viewer" column for the domain
that is selected in the Viewer node.
* Instance attributes are visualized as a constant color per instance.
**Viewport Options**
* The attribute overlay opacity can be controlled with the "Viewer Node"
setting in the overlays popover.
* A viewport can be configured not to show intermediate viewer-geometry
by disabling the "Viewer Node" option in the "View" menu.
**Implementation Details**
* The "spreadsheet context path" was generalized to a "viewer path" that
is used in more places now.
* The viewer node itself determines the attribute domain, evaluates the
field and stores the result in a `.viewer` attribute.
* A new "viewer attribute' overlay displays the data from the `.viewer`
attribute.
* The ground truth for the active viewer node is stored in the workspace
now. Node editors, spreadsheets and viewports retrieve the active
viewer from there unless they are pinned.
* The depsgraph object iterator has a new "viewer path" setting. When set,
the viewed geometry of the corresponding object is part of the iterator
instead of the final evaluated geometry.
* To support the instance attribute overlay `DupliObject` was extended
to contain the information necessary for drawing the overlay.
* The ctrl+shift+click operator has been refactored so that it can make
existing links to viewers active again.
* The auto-domain-detection in the Viewer node works by checking the
"preferred domain" for every field input. If there is not exactly one
preferred domain, the fallback is used.
Known limitations:
* Loose edges of meshes don't have the attribute overlay. This could be
added separately if necessary.
* Some attributes are hard to visualize as a color directly. For example,
the values might have to be normalized or some should be drawn as arrays.
For now, we encourage users to build node groups that generate appropriate
viewer-geometry. We might include some of that functionality in future versions.
Support for displaying attribute values as text in the viewport is planned as well.
* There seems to be an issue with the attribute overlay for pointclouds on
nvidia gpus, to be investigated.
Differential Revision: https://developer.blender.org/D15954
2022-09-28 17:54:59 +02:00
|
|
|
/* Try to reactivate existing viewer connection. */
|
|
|
|
|
for (bNodeLink *link : bsocket_to_view.directly_linked_links()) {
|
|
|
|
|
bNodeSocket &target_socket = *link->tosock;
|
|
|
|
|
bNode &target_node = *link->tonode;
|
|
|
|
|
if (is_viewer_socket(target_socket) && ELEM(viewer_node, nullptr, &target_node)) {
|
|
|
|
|
finalize_viewer_link(C, snode, target_node, *link);
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (viewer_node == nullptr) {
|
2022-12-02 11:12:51 -06:00
|
|
|
for (bNode *node : btree.all_nodes()) {
|
Geometry Nodes: viewport preview
This adds support for showing geometry passed to the Viewer in the 3d
viewport (instead of just in the spreadsheet). The "viewer geometry"
bypasses the group output. So it is not necessary to change the final
output of the node group to be able to see the intermediate geometry.
**Activation and deactivation of a viewer node**
* A viewer node is activated by clicking on it.
* Ctrl+shift+click on any node/socket connects it to the viewer and
makes it active.
* Ctrl+shift+click in empty space deactivates the active viewer.
* When the active viewer is not visible anymore (e.g. another object
is selected, or the current node group is exit), it is deactivated.
* Clicking on the icon in the header of the Viewer node toggles whether
its active or not.
**Pinning**
* The spreadsheet still allows pinning the active viewer as before.
When pinned, the spreadsheet still references the viewer node even
when it becomes inactive.
* The viewport does not support pinning at the moment. It always shows
the active viewer.
**Attribute**
* When a field is linked to the second input of the viewer node it is
displayed as an overlay in the viewport.
* When possible the correct domain for the attribute is determined
automatically. This does not work in all cases. It falls back to the
face corner domain on meshes and the point domain on curves. When
necessary, the domain can be picked manually.
* The spreadsheet now only shows the "Viewer" column for the domain
that is selected in the Viewer node.
* Instance attributes are visualized as a constant color per instance.
**Viewport Options**
* The attribute overlay opacity can be controlled with the "Viewer Node"
setting in the overlays popover.
* A viewport can be configured not to show intermediate viewer-geometry
by disabling the "Viewer Node" option in the "View" menu.
**Implementation Details**
* The "spreadsheet context path" was generalized to a "viewer path" that
is used in more places now.
* The viewer node itself determines the attribute domain, evaluates the
field and stores the result in a `.viewer` attribute.
* A new "viewer attribute' overlay displays the data from the `.viewer`
attribute.
* The ground truth for the active viewer node is stored in the workspace
now. Node editors, spreadsheets and viewports retrieve the active
viewer from there unless they are pinned.
* The depsgraph object iterator has a new "viewer path" setting. When set,
the viewed geometry of the corresponding object is part of the iterator
instead of the final evaluated geometry.
* To support the instance attribute overlay `DupliObject` was extended
to contain the information necessary for drawing the overlay.
* The ctrl+shift+click operator has been refactored so that it can make
existing links to viewers active again.
* The auto-domain-detection in the Viewer node works by checking the
"preferred domain" for every field input. If there is not exactly one
preferred domain, the fallback is used.
Known limitations:
* Loose edges of meshes don't have the attribute overlay. This could be
added separately if necessary.
* Some attributes are hard to visualize as a color directly. For example,
the values might have to be normalized or some should be drawn as arrays.
For now, we encourage users to build node groups that generate appropriate
viewer-geometry. We might include some of that functionality in future versions.
Support for displaying attribute values as text in the viewport is planned as well.
* There seems to be an issue with the attribute overlay for pointclouds on
nvidia gpus, to be investigated.
Differential Revision: https://developer.blender.org/D15954
2022-09-28 17:54:59 +02:00
|
|
|
if (is_viewer_node(*node)) {
|
|
|
|
|
viewer_node = node;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (viewer_node == nullptr) {
|
2023-03-19 07:03:01 +01:00
|
|
|
const float2 socket_location = bsocket_to_view.runtime->location;
|
2021-12-03 16:25:17 -05:00
|
|
|
const int viewer_type = get_default_viewer_type(&C);
|
2023-03-17 04:19:05 +01:00
|
|
|
const float2 location{socket_location.x / UI_SCALE_FAC + 100,
|
|
|
|
|
socket_location.y / UI_SCALE_FAC};
|
Geometry Nodes: viewport preview
This adds support for showing geometry passed to the Viewer in the 3d
viewport (instead of just in the spreadsheet). The "viewer geometry"
bypasses the group output. So it is not necessary to change the final
output of the node group to be able to see the intermediate geometry.
**Activation and deactivation of a viewer node**
* A viewer node is activated by clicking on it.
* Ctrl+shift+click on any node/socket connects it to the viewer and
makes it active.
* Ctrl+shift+click in empty space deactivates the active viewer.
* When the active viewer is not visible anymore (e.g. another object
is selected, or the current node group is exit), it is deactivated.
* Clicking on the icon in the header of the Viewer node toggles whether
its active or not.
**Pinning**
* The spreadsheet still allows pinning the active viewer as before.
When pinned, the spreadsheet still references the viewer node even
when it becomes inactive.
* The viewport does not support pinning at the moment. It always shows
the active viewer.
**Attribute**
* When a field is linked to the second input of the viewer node it is
displayed as an overlay in the viewport.
* When possible the correct domain for the attribute is determined
automatically. This does not work in all cases. It falls back to the
face corner domain on meshes and the point domain on curves. When
necessary, the domain can be picked manually.
* The spreadsheet now only shows the "Viewer" column for the domain
that is selected in the Viewer node.
* Instance attributes are visualized as a constant color per instance.
**Viewport Options**
* The attribute overlay opacity can be controlled with the "Viewer Node"
setting in the overlays popover.
* A viewport can be configured not to show intermediate viewer-geometry
by disabling the "Viewer Node" option in the "View" menu.
**Implementation Details**
* The "spreadsheet context path" was generalized to a "viewer path" that
is used in more places now.
* The viewer node itself determines the attribute domain, evaluates the
field and stores the result in a `.viewer` attribute.
* A new "viewer attribute' overlay displays the data from the `.viewer`
attribute.
* The ground truth for the active viewer node is stored in the workspace
now. Node editors, spreadsheets and viewports retrieve the active
viewer from there unless they are pinned.
* The depsgraph object iterator has a new "viewer path" setting. When set,
the viewed geometry of the corresponding object is part of the iterator
instead of the final evaluated geometry.
* To support the instance attribute overlay `DupliObject` was extended
to contain the information necessary for drawing the overlay.
* The ctrl+shift+click operator has been refactored so that it can make
existing links to viewers active again.
* The auto-domain-detection in the Viewer node works by checking the
"preferred domain" for every field input. If there is not exactly one
preferred domain, the fallback is used.
Known limitations:
* Loose edges of meshes don't have the attribute overlay. This could be
added separately if necessary.
* Some attributes are hard to visualize as a color directly. For example,
the values might have to be normalized or some should be drawn as arrays.
For now, we encourage users to build node groups that generate appropriate
viewer-geometry. We might include some of that functionality in future versions.
Support for displaying attribute values as text in the viewport is planned as well.
* There seems to be an issue with the attribute overlay for pointclouds on
nvidia gpus, to be investigated.
Differential Revision: https://developer.blender.org/D15954
2022-09-28 17:54:59 +02:00
|
|
|
viewer_node = add_static_node(C, viewer_type, location);
|
2021-10-26 11:25:32 +02:00
|
|
|
}
|
|
|
|
|
|
Geometry Nodes: viewport preview
This adds support for showing geometry passed to the Viewer in the 3d
viewport (instead of just in the spreadsheet). The "viewer geometry"
bypasses the group output. So it is not necessary to change the final
output of the node group to be able to see the intermediate geometry.
**Activation and deactivation of a viewer node**
* A viewer node is activated by clicking on it.
* Ctrl+shift+click on any node/socket connects it to the viewer and
makes it active.
* Ctrl+shift+click in empty space deactivates the active viewer.
* When the active viewer is not visible anymore (e.g. another object
is selected, or the current node group is exit), it is deactivated.
* Clicking on the icon in the header of the Viewer node toggles whether
its active or not.
**Pinning**
* The spreadsheet still allows pinning the active viewer as before.
When pinned, the spreadsheet still references the viewer node even
when it becomes inactive.
* The viewport does not support pinning at the moment. It always shows
the active viewer.
**Attribute**
* When a field is linked to the second input of the viewer node it is
displayed as an overlay in the viewport.
* When possible the correct domain for the attribute is determined
automatically. This does not work in all cases. It falls back to the
face corner domain on meshes and the point domain on curves. When
necessary, the domain can be picked manually.
* The spreadsheet now only shows the "Viewer" column for the domain
that is selected in the Viewer node.
* Instance attributes are visualized as a constant color per instance.
**Viewport Options**
* The attribute overlay opacity can be controlled with the "Viewer Node"
setting in the overlays popover.
* A viewport can be configured not to show intermediate viewer-geometry
by disabling the "Viewer Node" option in the "View" menu.
**Implementation Details**
* The "spreadsheet context path" was generalized to a "viewer path" that
is used in more places now.
* The viewer node itself determines the attribute domain, evaluates the
field and stores the result in a `.viewer` attribute.
* A new "viewer attribute' overlay displays the data from the `.viewer`
attribute.
* The ground truth for the active viewer node is stored in the workspace
now. Node editors, spreadsheets and viewports retrieve the active
viewer from there unless they are pinned.
* The depsgraph object iterator has a new "viewer path" setting. When set,
the viewed geometry of the corresponding object is part of the iterator
instead of the final evaluated geometry.
* To support the instance attribute overlay `DupliObject` was extended
to contain the information necessary for drawing the overlay.
* The ctrl+shift+click operator has been refactored so that it can make
existing links to viewers active again.
* The auto-domain-detection in the Viewer node works by checking the
"preferred domain" for every field input. If there is not exactly one
preferred domain, the fallback is used.
Known limitations:
* Loose edges of meshes don't have the attribute overlay. This could be
added separately if necessary.
* Some attributes are hard to visualize as a color directly. For example,
the values might have to be normalized or some should be drawn as arrays.
For now, we encourage users to build node groups that generate appropriate
viewer-geometry. We might include some of that functionality in future versions.
Support for displaying attribute values as text in the viewport is planned as well.
* There seems to be an issue with the attribute overlay for pointclouds on
nvidia gpus, to be investigated.
Differential Revision: https://developer.blender.org/D15954
2022-09-28 17:54:59 +02:00
|
|
|
bNodeSocket *viewer_bsocket = node_link_viewer_get_socket(btree, *viewer_node, bsocket_to_view);
|
2021-10-26 11:25:32 +02:00
|
|
|
if (viewer_bsocket == nullptr) {
|
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
Geometry Nodes: viewport preview
This adds support for showing geometry passed to the Viewer in the 3d
viewport (instead of just in the spreadsheet). The "viewer geometry"
bypasses the group output. So it is not necessary to change the final
output of the node group to be able to see the intermediate geometry.
**Activation and deactivation of a viewer node**
* A viewer node is activated by clicking on it.
* Ctrl+shift+click on any node/socket connects it to the viewer and
makes it active.
* Ctrl+shift+click in empty space deactivates the active viewer.
* When the active viewer is not visible anymore (e.g. another object
is selected, or the current node group is exit), it is deactivated.
* Clicking on the icon in the header of the Viewer node toggles whether
its active or not.
**Pinning**
* The spreadsheet still allows pinning the active viewer as before.
When pinned, the spreadsheet still references the viewer node even
when it becomes inactive.
* The viewport does not support pinning at the moment. It always shows
the active viewer.
**Attribute**
* When a field is linked to the second input of the viewer node it is
displayed as an overlay in the viewport.
* When possible the correct domain for the attribute is determined
automatically. This does not work in all cases. It falls back to the
face corner domain on meshes and the point domain on curves. When
necessary, the domain can be picked manually.
* The spreadsheet now only shows the "Viewer" column for the domain
that is selected in the Viewer node.
* Instance attributes are visualized as a constant color per instance.
**Viewport Options**
* The attribute overlay opacity can be controlled with the "Viewer Node"
setting in the overlays popover.
* A viewport can be configured not to show intermediate viewer-geometry
by disabling the "Viewer Node" option in the "View" menu.
**Implementation Details**
* The "spreadsheet context path" was generalized to a "viewer path" that
is used in more places now.
* The viewer node itself determines the attribute domain, evaluates the
field and stores the result in a `.viewer` attribute.
* A new "viewer attribute' overlay displays the data from the `.viewer`
attribute.
* The ground truth for the active viewer node is stored in the workspace
now. Node editors, spreadsheets and viewports retrieve the active
viewer from there unless they are pinned.
* The depsgraph object iterator has a new "viewer path" setting. When set,
the viewed geometry of the corresponding object is part of the iterator
instead of the final evaluated geometry.
* To support the instance attribute overlay `DupliObject` was extended
to contain the information necessary for drawing the overlay.
* The ctrl+shift+click operator has been refactored so that it can make
existing links to viewers active again.
* The auto-domain-detection in the Viewer node works by checking the
"preferred domain" for every field input. If there is not exactly one
preferred domain, the fallback is used.
Known limitations:
* Loose edges of meshes don't have the attribute overlay. This could be
added separately if necessary.
* Some attributes are hard to visualize as a color directly. For example,
the values might have to be normalized or some should be drawn as arrays.
For now, we encourage users to build node groups that generate appropriate
viewer-geometry. We might include some of that functionality in future versions.
Support for displaying attribute values as text in the viewport is planned as well.
* There seems to be an issue with the attribute overlay for pointclouds on
nvidia gpus, to be investigated.
Differential Revision: https://developer.blender.org/D15954
2022-09-28 17:54:59 +02:00
|
|
|
bNodeLink *viewer_link = nullptr;
|
|
|
|
|
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &btree.links) {
|
2021-10-26 11:25:32 +02:00
|
|
|
if (link->tosock == viewer_bsocket) {
|
Geometry Nodes: viewport preview
This adds support for showing geometry passed to the Viewer in the 3d
viewport (instead of just in the spreadsheet). The "viewer geometry"
bypasses the group output. So it is not necessary to change the final
output of the node group to be able to see the intermediate geometry.
**Activation and deactivation of a viewer node**
* A viewer node is activated by clicking on it.
* Ctrl+shift+click on any node/socket connects it to the viewer and
makes it active.
* Ctrl+shift+click in empty space deactivates the active viewer.
* When the active viewer is not visible anymore (e.g. another object
is selected, or the current node group is exit), it is deactivated.
* Clicking on the icon in the header of the Viewer node toggles whether
its active or not.
**Pinning**
* The spreadsheet still allows pinning the active viewer as before.
When pinned, the spreadsheet still references the viewer node even
when it becomes inactive.
* The viewport does not support pinning at the moment. It always shows
the active viewer.
**Attribute**
* When a field is linked to the second input of the viewer node it is
displayed as an overlay in the viewport.
* When possible the correct domain for the attribute is determined
automatically. This does not work in all cases. It falls back to the
face corner domain on meshes and the point domain on curves. When
necessary, the domain can be picked manually.
* The spreadsheet now only shows the "Viewer" column for the domain
that is selected in the Viewer node.
* Instance attributes are visualized as a constant color per instance.
**Viewport Options**
* The attribute overlay opacity can be controlled with the "Viewer Node"
setting in the overlays popover.
* A viewport can be configured not to show intermediate viewer-geometry
by disabling the "Viewer Node" option in the "View" menu.
**Implementation Details**
* The "spreadsheet context path" was generalized to a "viewer path" that
is used in more places now.
* The viewer node itself determines the attribute domain, evaluates the
field and stores the result in a `.viewer` attribute.
* A new "viewer attribute' overlay displays the data from the `.viewer`
attribute.
* The ground truth for the active viewer node is stored in the workspace
now. Node editors, spreadsheets and viewports retrieve the active
viewer from there unless they are pinned.
* The depsgraph object iterator has a new "viewer path" setting. When set,
the viewed geometry of the corresponding object is part of the iterator
instead of the final evaluated geometry.
* To support the instance attribute overlay `DupliObject` was extended
to contain the information necessary for drawing the overlay.
* The ctrl+shift+click operator has been refactored so that it can make
existing links to viewers active again.
* The auto-domain-detection in the Viewer node works by checking the
"preferred domain" for every field input. If there is not exactly one
preferred domain, the fallback is used.
Known limitations:
* Loose edges of meshes don't have the attribute overlay. This could be
added separately if necessary.
* Some attributes are hard to visualize as a color directly. For example,
the values might have to be normalized or some should be drawn as arrays.
For now, we encourage users to build node groups that generate appropriate
viewer-geometry. We might include some of that functionality in future versions.
Support for displaying attribute values as text in the viewport is planned as well.
* There seems to be an issue with the attribute overlay for pointclouds on
nvidia gpus, to be investigated.
Differential Revision: https://developer.blender.org/D15954
2022-09-28 17:54:59 +02:00
|
|
|
viewer_link = link;
|
2021-10-26 11:25:32 +02:00
|
|
|
break;
|
2021-07-05 10:46:00 +02:00
|
|
|
}
|
2021-10-26 11:25:32 +02:00
|
|
|
}
|
Geometry Nodes: viewport preview
This adds support for showing geometry passed to the Viewer in the 3d
viewport (instead of just in the spreadsheet). The "viewer geometry"
bypasses the group output. So it is not necessary to change the final
output of the node group to be able to see the intermediate geometry.
**Activation and deactivation of a viewer node**
* A viewer node is activated by clicking on it.
* Ctrl+shift+click on any node/socket connects it to the viewer and
makes it active.
* Ctrl+shift+click in empty space deactivates the active viewer.
* When the active viewer is not visible anymore (e.g. another object
is selected, or the current node group is exit), it is deactivated.
* Clicking on the icon in the header of the Viewer node toggles whether
its active or not.
**Pinning**
* The spreadsheet still allows pinning the active viewer as before.
When pinned, the spreadsheet still references the viewer node even
when it becomes inactive.
* The viewport does not support pinning at the moment. It always shows
the active viewer.
**Attribute**
* When a field is linked to the second input of the viewer node it is
displayed as an overlay in the viewport.
* When possible the correct domain for the attribute is determined
automatically. This does not work in all cases. It falls back to the
face corner domain on meshes and the point domain on curves. When
necessary, the domain can be picked manually.
* The spreadsheet now only shows the "Viewer" column for the domain
that is selected in the Viewer node.
* Instance attributes are visualized as a constant color per instance.
**Viewport Options**
* The attribute overlay opacity can be controlled with the "Viewer Node"
setting in the overlays popover.
* A viewport can be configured not to show intermediate viewer-geometry
by disabling the "Viewer Node" option in the "View" menu.
**Implementation Details**
* The "spreadsheet context path" was generalized to a "viewer path" that
is used in more places now.
* The viewer node itself determines the attribute domain, evaluates the
field and stores the result in a `.viewer` attribute.
* A new "viewer attribute' overlay displays the data from the `.viewer`
attribute.
* The ground truth for the active viewer node is stored in the workspace
now. Node editors, spreadsheets and viewports retrieve the active
viewer from there unless they are pinned.
* The depsgraph object iterator has a new "viewer path" setting. When set,
the viewed geometry of the corresponding object is part of the iterator
instead of the final evaluated geometry.
* To support the instance attribute overlay `DupliObject` was extended
to contain the information necessary for drawing the overlay.
* The ctrl+shift+click operator has been refactored so that it can make
existing links to viewers active again.
* The auto-domain-detection in the Viewer node works by checking the
"preferred domain" for every field input. If there is not exactly one
preferred domain, the fallback is used.
Known limitations:
* Loose edges of meshes don't have the attribute overlay. This could be
added separately if necessary.
* Some attributes are hard to visualize as a color directly. For example,
the values might have to be normalized or some should be drawn as arrays.
For now, we encourage users to build node groups that generate appropriate
viewer-geometry. We might include some of that functionality in future versions.
Support for displaying attribute values as text in the viewport is planned as well.
* There seems to be an issue with the attribute overlay for pointclouds on
nvidia gpus, to be investigated.
Differential Revision: https://developer.blender.org/D15954
2022-09-28 17:54:59 +02:00
|
|
|
if (viewer_link == nullptr) {
|
|
|
|
|
viewer_link = nodeAddLink(
|
|
|
|
|
&btree, &bnode_to_view, &bsocket_to_view, viewer_node, viewer_bsocket);
|
2021-10-26 11:25:32 +02:00
|
|
|
}
|
|
|
|
|
else {
|
Geometry Nodes: viewport preview
This adds support for showing geometry passed to the Viewer in the 3d
viewport (instead of just in the spreadsheet). The "viewer geometry"
bypasses the group output. So it is not necessary to change the final
output of the node group to be able to see the intermediate geometry.
**Activation and deactivation of a viewer node**
* A viewer node is activated by clicking on it.
* Ctrl+shift+click on any node/socket connects it to the viewer and
makes it active.
* Ctrl+shift+click in empty space deactivates the active viewer.
* When the active viewer is not visible anymore (e.g. another object
is selected, or the current node group is exit), it is deactivated.
* Clicking on the icon in the header of the Viewer node toggles whether
its active or not.
**Pinning**
* The spreadsheet still allows pinning the active viewer as before.
When pinned, the spreadsheet still references the viewer node even
when it becomes inactive.
* The viewport does not support pinning at the moment. It always shows
the active viewer.
**Attribute**
* When a field is linked to the second input of the viewer node it is
displayed as an overlay in the viewport.
* When possible the correct domain for the attribute is determined
automatically. This does not work in all cases. It falls back to the
face corner domain on meshes and the point domain on curves. When
necessary, the domain can be picked manually.
* The spreadsheet now only shows the "Viewer" column for the domain
that is selected in the Viewer node.
* Instance attributes are visualized as a constant color per instance.
**Viewport Options**
* The attribute overlay opacity can be controlled with the "Viewer Node"
setting in the overlays popover.
* A viewport can be configured not to show intermediate viewer-geometry
by disabling the "Viewer Node" option in the "View" menu.
**Implementation Details**
* The "spreadsheet context path" was generalized to a "viewer path" that
is used in more places now.
* The viewer node itself determines the attribute domain, evaluates the
field and stores the result in a `.viewer` attribute.
* A new "viewer attribute' overlay displays the data from the `.viewer`
attribute.
* The ground truth for the active viewer node is stored in the workspace
now. Node editors, spreadsheets and viewports retrieve the active
viewer from there unless they are pinned.
* The depsgraph object iterator has a new "viewer path" setting. When set,
the viewed geometry of the corresponding object is part of the iterator
instead of the final evaluated geometry.
* To support the instance attribute overlay `DupliObject` was extended
to contain the information necessary for drawing the overlay.
* The ctrl+shift+click operator has been refactored so that it can make
existing links to viewers active again.
* The auto-domain-detection in the Viewer node works by checking the
"preferred domain" for every field input. If there is not exactly one
preferred domain, the fallback is used.
Known limitations:
* Loose edges of meshes don't have the attribute overlay. This could be
added separately if necessary.
* Some attributes are hard to visualize as a color directly. For example,
the values might have to be normalized or some should be drawn as arrays.
For now, we encourage users to build node groups that generate appropriate
viewer-geometry. We might include some of that functionality in future versions.
Support for displaying attribute values as text in the viewport is planned as well.
* There seems to be an issue with the attribute overlay for pointclouds on
nvidia gpus, to be investigated.
Differential Revision: https://developer.blender.org/D15954
2022-09-28 17:54:59 +02:00
|
|
|
viewer_link->fromnode = &bnode_to_view;
|
|
|
|
|
viewer_link->fromsock = &bsocket_to_view;
|
2021-12-21 15:18:56 +01:00
|
|
|
BKE_ntree_update_tag_link_changed(&btree);
|
2021-10-26 11:25:32 +02:00
|
|
|
}
|
Geometry Nodes: viewport preview
This adds support for showing geometry passed to the Viewer in the 3d
viewport (instead of just in the spreadsheet). The "viewer geometry"
bypasses the group output. So it is not necessary to change the final
output of the node group to be able to see the intermediate geometry.
**Activation and deactivation of a viewer node**
* A viewer node is activated by clicking on it.
* Ctrl+shift+click on any node/socket connects it to the viewer and
makes it active.
* Ctrl+shift+click in empty space deactivates the active viewer.
* When the active viewer is not visible anymore (e.g. another object
is selected, or the current node group is exit), it is deactivated.
* Clicking on the icon in the header of the Viewer node toggles whether
its active or not.
**Pinning**
* The spreadsheet still allows pinning the active viewer as before.
When pinned, the spreadsheet still references the viewer node even
when it becomes inactive.
* The viewport does not support pinning at the moment. It always shows
the active viewer.
**Attribute**
* When a field is linked to the second input of the viewer node it is
displayed as an overlay in the viewport.
* When possible the correct domain for the attribute is determined
automatically. This does not work in all cases. It falls back to the
face corner domain on meshes and the point domain on curves. When
necessary, the domain can be picked manually.
* The spreadsheet now only shows the "Viewer" column for the domain
that is selected in the Viewer node.
* Instance attributes are visualized as a constant color per instance.
**Viewport Options**
* The attribute overlay opacity can be controlled with the "Viewer Node"
setting in the overlays popover.
* A viewport can be configured not to show intermediate viewer-geometry
by disabling the "Viewer Node" option in the "View" menu.
**Implementation Details**
* The "spreadsheet context path" was generalized to a "viewer path" that
is used in more places now.
* The viewer node itself determines the attribute domain, evaluates the
field and stores the result in a `.viewer` attribute.
* A new "viewer attribute' overlay displays the data from the `.viewer`
attribute.
* The ground truth for the active viewer node is stored in the workspace
now. Node editors, spreadsheets and viewports retrieve the active
viewer from there unless they are pinned.
* The depsgraph object iterator has a new "viewer path" setting. When set,
the viewed geometry of the corresponding object is part of the iterator
instead of the final evaluated geometry.
* To support the instance attribute overlay `DupliObject` was extended
to contain the information necessary for drawing the overlay.
* The ctrl+shift+click operator has been refactored so that it can make
existing links to viewers active again.
* The auto-domain-detection in the Viewer node works by checking the
"preferred domain" for every field input. If there is not exactly one
preferred domain, the fallback is used.
Known limitations:
* Loose edges of meshes don't have the attribute overlay. This could be
added separately if necessary.
* Some attributes are hard to visualize as a color directly. For example,
the values might have to be normalized or some should be drawn as arrays.
For now, we encourage users to build node groups that generate appropriate
viewer-geometry. We might include some of that functionality in future versions.
Support for displaying attribute values as text in the viewport is planned as well.
* There seems to be an issue with the attribute overlay for pointclouds on
nvidia gpus, to be investigated.
Differential Revision: https://developer.blender.org/D15954
2022-09-28 17:54:59 +02:00
|
|
|
finalize_viewer_link(C, snode, *viewer_node, *viewer_link);
|
|
|
|
|
return OPERATOR_CANCELLED;
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
|
|
|
|
|
Geometry Nodes: viewport preview
This adds support for showing geometry passed to the Viewer in the 3d
viewport (instead of just in the spreadsheet). The "viewer geometry"
bypasses the group output. So it is not necessary to change the final
output of the node group to be able to see the intermediate geometry.
**Activation and deactivation of a viewer node**
* A viewer node is activated by clicking on it.
* Ctrl+shift+click on any node/socket connects it to the viewer and
makes it active.
* Ctrl+shift+click in empty space deactivates the active viewer.
* When the active viewer is not visible anymore (e.g. another object
is selected, or the current node group is exit), it is deactivated.
* Clicking on the icon in the header of the Viewer node toggles whether
its active or not.
**Pinning**
* The spreadsheet still allows pinning the active viewer as before.
When pinned, the spreadsheet still references the viewer node even
when it becomes inactive.
* The viewport does not support pinning at the moment. It always shows
the active viewer.
**Attribute**
* When a field is linked to the second input of the viewer node it is
displayed as an overlay in the viewport.
* When possible the correct domain for the attribute is determined
automatically. This does not work in all cases. It falls back to the
face corner domain on meshes and the point domain on curves. When
necessary, the domain can be picked manually.
* The spreadsheet now only shows the "Viewer" column for the domain
that is selected in the Viewer node.
* Instance attributes are visualized as a constant color per instance.
**Viewport Options**
* The attribute overlay opacity can be controlled with the "Viewer Node"
setting in the overlays popover.
* A viewport can be configured not to show intermediate viewer-geometry
by disabling the "Viewer Node" option in the "View" menu.
**Implementation Details**
* The "spreadsheet context path" was generalized to a "viewer path" that
is used in more places now.
* The viewer node itself determines the attribute domain, evaluates the
field and stores the result in a `.viewer` attribute.
* A new "viewer attribute' overlay displays the data from the `.viewer`
attribute.
* The ground truth for the active viewer node is stored in the workspace
now. Node editors, spreadsheets and viewports retrieve the active
viewer from there unless they are pinned.
* The depsgraph object iterator has a new "viewer path" setting. When set,
the viewed geometry of the corresponding object is part of the iterator
instead of the final evaluated geometry.
* To support the instance attribute overlay `DupliObject` was extended
to contain the information necessary for drawing the overlay.
* The ctrl+shift+click operator has been refactored so that it can make
existing links to viewers active again.
* The auto-domain-detection in the Viewer node works by checking the
"preferred domain" for every field input. If there is not exactly one
preferred domain, the fallback is used.
Known limitations:
* Loose edges of meshes don't have the attribute overlay. This could be
added separately if necessary.
* Some attributes are hard to visualize as a color directly. For example,
the values might have to be normalized or some should be drawn as arrays.
For now, we encourage users to build node groups that generate appropriate
viewer-geometry. We might include some of that functionality in future versions.
Support for displaying attribute values as text in the viewport is planned as well.
* There seems to be an issue with the attribute overlay for pointclouds on
nvidia gpus, to be investigated.
Differential Revision: https://developer.blender.org/D15954
2022-09-28 17:54:59 +02:00
|
|
|
static int node_link_viewer(const bContext &C, bNode &bnode_to_view, bNodeSocket *bsocket_to_view)
|
2021-10-26 11:25:32 +02:00
|
|
|
{
|
2021-12-03 16:25:17 -05:00
|
|
|
SpaceNode &snode = *CTX_wm_space_node(&C);
|
|
|
|
|
bNodeTree *btree = snode.edittree;
|
2022-08-31 12:15:57 +02:00
|
|
|
btree->ensure_topology_cache();
|
2021-10-26 11:25:32 +02:00
|
|
|
|
2022-08-31 12:15:57 +02:00
|
|
|
if (bsocket_to_view == nullptr) {
|
Geometry Nodes: viewport preview
This adds support for showing geometry passed to the Viewer in the 3d
viewport (instead of just in the spreadsheet). The "viewer geometry"
bypasses the group output. So it is not necessary to change the final
output of the node group to be able to see the intermediate geometry.
**Activation and deactivation of a viewer node**
* A viewer node is activated by clicking on it.
* Ctrl+shift+click on any node/socket connects it to the viewer and
makes it active.
* Ctrl+shift+click in empty space deactivates the active viewer.
* When the active viewer is not visible anymore (e.g. another object
is selected, or the current node group is exit), it is deactivated.
* Clicking on the icon in the header of the Viewer node toggles whether
its active or not.
**Pinning**
* The spreadsheet still allows pinning the active viewer as before.
When pinned, the spreadsheet still references the viewer node even
when it becomes inactive.
* The viewport does not support pinning at the moment. It always shows
the active viewer.
**Attribute**
* When a field is linked to the second input of the viewer node it is
displayed as an overlay in the viewport.
* When possible the correct domain for the attribute is determined
automatically. This does not work in all cases. It falls back to the
face corner domain on meshes and the point domain on curves. When
necessary, the domain can be picked manually.
* The spreadsheet now only shows the "Viewer" column for the domain
that is selected in the Viewer node.
* Instance attributes are visualized as a constant color per instance.
**Viewport Options**
* The attribute overlay opacity can be controlled with the "Viewer Node"
setting in the overlays popover.
* A viewport can be configured not to show intermediate viewer-geometry
by disabling the "Viewer Node" option in the "View" menu.
**Implementation Details**
* The "spreadsheet context path" was generalized to a "viewer path" that
is used in more places now.
* The viewer node itself determines the attribute domain, evaluates the
field and stores the result in a `.viewer` attribute.
* A new "viewer attribute' overlay displays the data from the `.viewer`
attribute.
* The ground truth for the active viewer node is stored in the workspace
now. Node editors, spreadsheets and viewports retrieve the active
viewer from there unless they are pinned.
* The depsgraph object iterator has a new "viewer path" setting. When set,
the viewed geometry of the corresponding object is part of the iterator
instead of the final evaluated geometry.
* To support the instance attribute overlay `DupliObject` was extended
to contain the information necessary for drawing the overlay.
* The ctrl+shift+click operator has been refactored so that it can make
existing links to viewers active again.
* The auto-domain-detection in the Viewer node works by checking the
"preferred domain" for every field input. If there is not exactly one
preferred domain, the fallback is used.
Known limitations:
* Loose edges of meshes don't have the attribute overlay. This could be
added separately if necessary.
* Some attributes are hard to visualize as a color directly. For example,
the values might have to be normalized or some should be drawn as arrays.
For now, we encourage users to build node groups that generate appropriate
viewer-geometry. We might include some of that functionality in future versions.
Support for displaying attribute values as text in the viewport is planned as well.
* There seems to be an issue with the attribute overlay for pointclouds on
nvidia gpus, to be investigated.
Differential Revision: https://developer.blender.org/D15954
2022-09-28 17:54:59 +02:00
|
|
|
bsocket_to_view = determine_socket_to_view(bnode_to_view);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (bsocket_to_view == nullptr) {
|
|
|
|
|
return OPERATOR_CANCELLED;
|
2021-10-26 11:25:32 +02:00
|
|
|
}
|
Geometry Nodes: viewport preview
This adds support for showing geometry passed to the Viewer in the 3d
viewport (instead of just in the spreadsheet). The "viewer geometry"
bypasses the group output. So it is not necessary to change the final
output of the node group to be able to see the intermediate geometry.
**Activation and deactivation of a viewer node**
* A viewer node is activated by clicking on it.
* Ctrl+shift+click on any node/socket connects it to the viewer and
makes it active.
* Ctrl+shift+click in empty space deactivates the active viewer.
* When the active viewer is not visible anymore (e.g. another object
is selected, or the current node group is exit), it is deactivated.
* Clicking on the icon in the header of the Viewer node toggles whether
its active or not.
**Pinning**
* The spreadsheet still allows pinning the active viewer as before.
When pinned, the spreadsheet still references the viewer node even
when it becomes inactive.
* The viewport does not support pinning at the moment. It always shows
the active viewer.
**Attribute**
* When a field is linked to the second input of the viewer node it is
displayed as an overlay in the viewport.
* When possible the correct domain for the attribute is determined
automatically. This does not work in all cases. It falls back to the
face corner domain on meshes and the point domain on curves. When
necessary, the domain can be picked manually.
* The spreadsheet now only shows the "Viewer" column for the domain
that is selected in the Viewer node.
* Instance attributes are visualized as a constant color per instance.
**Viewport Options**
* The attribute overlay opacity can be controlled with the "Viewer Node"
setting in the overlays popover.
* A viewport can be configured not to show intermediate viewer-geometry
by disabling the "Viewer Node" option in the "View" menu.
**Implementation Details**
* The "spreadsheet context path" was generalized to a "viewer path" that
is used in more places now.
* The viewer node itself determines the attribute domain, evaluates the
field and stores the result in a `.viewer` attribute.
* A new "viewer attribute' overlay displays the data from the `.viewer`
attribute.
* The ground truth for the active viewer node is stored in the workspace
now. Node editors, spreadsheets and viewports retrieve the active
viewer from there unless they are pinned.
* The depsgraph object iterator has a new "viewer path" setting. When set,
the viewed geometry of the corresponding object is part of the iterator
instead of the final evaluated geometry.
* To support the instance attribute overlay `DupliObject` was extended
to contain the information necessary for drawing the overlay.
* The ctrl+shift+click operator has been refactored so that it can make
existing links to viewers active again.
* The auto-domain-detection in the Viewer node works by checking the
"preferred domain" for every field input. If there is not exactly one
preferred domain, the fallback is used.
Known limitations:
* Loose edges of meshes don't have the attribute overlay. This could be
added separately if necessary.
* Some attributes are hard to visualize as a color directly. For example,
the values might have to be normalized or some should be drawn as arrays.
For now, we encourage users to build node groups that generate appropriate
viewer-geometry. We might include some of that functionality in future versions.
Support for displaying attribute values as text in the viewport is planned as well.
* There seems to be an issue with the attribute overlay for pointclouds on
nvidia gpus, to be investigated.
Differential Revision: https://developer.blender.org/D15954
2022-09-28 17:54:59 +02:00
|
|
|
|
|
|
|
|
return view_socket(C, snode, *btree, bnode_to_view, *bsocket_to_view);
|
2021-10-26 11:25:32 +02:00
|
|
|
}
|
|
|
|
|
|
2022-03-15 17:29:54 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
2022-01-20 10:36:56 -06:00
|
|
|
} // namespace viewer_linking
|
2021-10-26 11:25:32 +02:00
|
|
|
|
2022-03-15 17:29:54 +11:00
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Link to Viewer Node Operator
|
|
|
|
|
* \{ */
|
|
|
|
|
|
2022-10-03 17:37:25 -05:00
|
|
|
static int node_active_link_viewer_exec(bContext *C, wmOperator * /*op*/)
|
2012-08-01 19:11:17 +00:00
|
|
|
{
|
2021-12-03 16:25:17 -05:00
|
|
|
SpaceNode &snode = *CTX_wm_space_node(C);
|
|
|
|
|
bNode *node = nodeGetActive(snode.edittree);
|
2012-08-01 19:11:17 +00:00
|
|
|
|
2019-03-26 21:16:47 +11:00
|
|
|
if (!node) {
|
2012-08-01 19:11:17 +00:00
|
|
|
return OPERATOR_CANCELLED;
|
2019-03-26 21:16:47 +11:00
|
|
|
}
|
2012-08-01 19:11:17 +00:00
|
|
|
|
2014-11-18 15:51:31 +01:00
|
|
|
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
|
2012-08-01 19:11:17 +00:00
|
|
|
|
Geometry Nodes: viewport preview
This adds support for showing geometry passed to the Viewer in the 3d
viewport (instead of just in the spreadsheet). The "viewer geometry"
bypasses the group output. So it is not necessary to change the final
output of the node group to be able to see the intermediate geometry.
**Activation and deactivation of a viewer node**
* A viewer node is activated by clicking on it.
* Ctrl+shift+click on any node/socket connects it to the viewer and
makes it active.
* Ctrl+shift+click in empty space deactivates the active viewer.
* When the active viewer is not visible anymore (e.g. another object
is selected, or the current node group is exit), it is deactivated.
* Clicking on the icon in the header of the Viewer node toggles whether
its active or not.
**Pinning**
* The spreadsheet still allows pinning the active viewer as before.
When pinned, the spreadsheet still references the viewer node even
when it becomes inactive.
* The viewport does not support pinning at the moment. It always shows
the active viewer.
**Attribute**
* When a field is linked to the second input of the viewer node it is
displayed as an overlay in the viewport.
* When possible the correct domain for the attribute is determined
automatically. This does not work in all cases. It falls back to the
face corner domain on meshes and the point domain on curves. When
necessary, the domain can be picked manually.
* The spreadsheet now only shows the "Viewer" column for the domain
that is selected in the Viewer node.
* Instance attributes are visualized as a constant color per instance.
**Viewport Options**
* The attribute overlay opacity can be controlled with the "Viewer Node"
setting in the overlays popover.
* A viewport can be configured not to show intermediate viewer-geometry
by disabling the "Viewer Node" option in the "View" menu.
**Implementation Details**
* The "spreadsheet context path" was generalized to a "viewer path" that
is used in more places now.
* The viewer node itself determines the attribute domain, evaluates the
field and stores the result in a `.viewer` attribute.
* A new "viewer attribute' overlay displays the data from the `.viewer`
attribute.
* The ground truth for the active viewer node is stored in the workspace
now. Node editors, spreadsheets and viewports retrieve the active
viewer from there unless they are pinned.
* The depsgraph object iterator has a new "viewer path" setting. When set,
the viewed geometry of the corresponding object is part of the iterator
instead of the final evaluated geometry.
* To support the instance attribute overlay `DupliObject` was extended
to contain the information necessary for drawing the overlay.
* The ctrl+shift+click operator has been refactored so that it can make
existing links to viewers active again.
* The auto-domain-detection in the Viewer node works by checking the
"preferred domain" for every field input. If there is not exactly one
preferred domain, the fallback is used.
Known limitations:
* Loose edges of meshes don't have the attribute overlay. This could be
added separately if necessary.
* Some attributes are hard to visualize as a color directly. For example,
the values might have to be normalized or some should be drawn as arrays.
For now, we encourage users to build node groups that generate appropriate
viewer-geometry. We might include some of that functionality in future versions.
Support for displaying attribute values as text in the viewport is planned as well.
* There seems to be an issue with the attribute overlay for pointclouds on
nvidia gpus, to be investigated.
Differential Revision: https://developer.blender.org/D15954
2022-09-28 17:54:59 +02:00
|
|
|
bNodeSocket *socket_to_view = nullptr;
|
|
|
|
|
LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) {
|
|
|
|
|
if (socket->flag & SELECT) {
|
|
|
|
|
socket_to_view = socket;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (viewer_linking::node_link_viewer(*C, *node, socket_to_view) == OPERATOR_CANCELLED) {
|
2012-08-01 19:11:17 +00:00
|
|
|
return OPERATOR_CANCELLED;
|
2019-03-26 21:16:47 +11:00
|
|
|
}
|
2012-08-01 19:11:17 +00:00
|
|
|
|
2021-12-21 15:18:56 +01:00
|
|
|
ED_node_tree_propagate_change(C, CTX_data_main(C), snode.edittree);
|
2012-08-01 19:11:17 +00:00
|
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-05 10:46:00 +02:00
|
|
|
static bool node_active_link_viewer_poll(bContext *C)
|
|
|
|
|
{
|
|
|
|
|
if (!ED_operator_node_editable(C)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
SpaceNode *snode = CTX_wm_space_node(C);
|
2023-09-22 11:59:01 -04:00
|
|
|
if (ED_node_is_compositor(snode)) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if (ED_node_is_geometry(snode)) {
|
|
|
|
|
if (snode->geometry_nodes_type == SNODE_GEOMETRY_TOOL) {
|
|
|
|
|
/* The viewer node is not supported in the "Tool" context. */
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
2021-07-05 10:46:00 +02:00
|
|
|
}
|
|
|
|
|
|
2012-08-01 19:11:17 +00:00
|
|
|
void NODE_OT_link_viewer(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
/* identifiers */
|
|
|
|
|
ot->name = "Link to Viewer Node";
|
|
|
|
|
ot->description = "Link to viewer node";
|
|
|
|
|
ot->idname = "NODE_OT_link_viewer";
|
|
|
|
|
|
|
|
|
|
/* api callbacks */
|
2013-07-28 17:06:31 +00:00
|
|
|
ot->exec = node_active_link_viewer_exec;
|
2021-07-05 10:46:00 +02:00
|
|
|
ot->poll = node_active_link_viewer_poll;
|
2012-08-01 19:11:17 +00:00
|
|
|
|
|
|
|
|
/* flags */
|
|
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-03 10:35:46 +10:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Add Link Operator
|
|
|
|
|
* \{ */
|
2012-08-01 19:11:17 +00:00
|
|
|
|
2021-12-15 09:51:57 -06:00
|
|
|
/**
|
|
|
|
|
* Check if any of the dragged links are connected to a socket on the side that they are dragged
|
|
|
|
|
* from.
|
|
|
|
|
*/
|
|
|
|
|
static bool dragged_links_are_detached(const bNodeLinkDrag &nldrag)
|
|
|
|
|
{
|
|
|
|
|
if (nldrag.in_out == SOCK_OUT) {
|
2022-12-29 14:09:58 -05:00
|
|
|
for (const bNodeLink &link : nldrag.links) {
|
|
|
|
|
if (link.tonode && link.tosock) {
|
2021-12-15 09:51:57 -06:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
2022-12-29 14:09:58 -05:00
|
|
|
for (const bNodeLink &link : nldrag.links) {
|
|
|
|
|
if (link.fromnode && link.fromsock) {
|
2021-12-15 09:51:57 -06:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool should_create_drag_link_search_menu(const bNodeTree &node_tree,
|
|
|
|
|
const bNodeLinkDrag &nldrag)
|
|
|
|
|
{
|
|
|
|
|
/* Custom node trees aren't supported yet. */
|
|
|
|
|
if (node_tree.type == NTREE_CUSTOM) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
/* Only create the search menu when the drag has not already connected the links to a socket. */
|
|
|
|
|
if (!dragged_links_are_detached(nldrag)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2023-01-28 10:07:29 +01:00
|
|
|
if (nldrag.swap_links) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2021-12-15 09:51:57 -06:00
|
|
|
/* Don't create the search menu if the drag is disconnecting a link from an input node. */
|
|
|
|
|
if (nldrag.start_socket->in_out == SOCK_IN && nldrag.start_link_count > 0) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2023-05-04 19:27:55 +02:00
|
|
|
/* Don't allow a drag from the "new socket" (group input node or simulation nodes currently).
|
|
|
|
|
* Handling these properly in node callbacks increases the complexity too much for now. */
|
|
|
|
|
if (nldrag.start_socket->type == SOCK_CUSTOM) {
|
|
|
|
|
return false;
|
2021-12-15 09:51:57 -06:00
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-28 10:07:29 +01:00
|
|
|
static bool need_drag_link_tooltip(const bNodeTree &node_tree, const bNodeLinkDrag &nldrag)
|
|
|
|
|
{
|
|
|
|
|
return nldrag.swap_links || should_create_drag_link_search_menu(node_tree, nldrag);
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-03 17:37:25 -05:00
|
|
|
static void draw_draglink_tooltip(const bContext * /*C*/, ARegion * /*region*/, void *arg)
|
2021-12-15 09:51:57 -06:00
|
|
|
{
|
|
|
|
|
bNodeLinkDrag *nldrag = static_cast<bNodeLinkDrag *>(arg);
|
|
|
|
|
|
2023-01-28 10:07:29 +01:00
|
|
|
uchar text_col[4];
|
|
|
|
|
UI_GetThemeColor4ubv(TH_TEXT, text_col);
|
|
|
|
|
|
2023-03-17 04:19:05 +01:00
|
|
|
const int padding = 4 * UI_SCALE_FAC;
|
2021-12-15 09:51:57 -06:00
|
|
|
const float x = nldrag->in_out == SOCK_IN ? nldrag->cursor[0] - 3.3f * padding :
|
|
|
|
|
nldrag->cursor[0];
|
2023-03-17 04:19:05 +01:00
|
|
|
const float y = nldrag->cursor[1] - 2.0f * UI_SCALE_FAC;
|
2021-12-15 09:51:57 -06:00
|
|
|
|
2023-01-28 10:07:29 +01:00
|
|
|
const bool new_link = nldrag->in_out == nldrag->start_socket->in_out;
|
|
|
|
|
const bool swap_links = nldrag->swap_links;
|
|
|
|
|
|
|
|
|
|
const int icon = !swap_links ? ICON_ADD : (new_link ? ICON_ANIM : ICON_UV_SYNC_SELECT);
|
|
|
|
|
|
2023-03-17 04:19:05 +01:00
|
|
|
UI_icon_draw_ex(
|
|
|
|
|
x, y, icon, UI_INV_SCALE_FAC, 1.0f, 0.0f, text_col, false, UI_NO_ICON_OVERLAY_TEXT);
|
2021-12-15 09:51:57 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void draw_draglink_tooltip_activate(const ARegion ®ion, bNodeLinkDrag &nldrag)
|
|
|
|
|
{
|
|
|
|
|
if (nldrag.draw_handle == nullptr) {
|
|
|
|
|
nldrag.draw_handle = ED_region_draw_cb_activate(
|
|
|
|
|
region.type, draw_draglink_tooltip, &nldrag, REGION_DRAW_POST_PIXEL);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void draw_draglink_tooltip_deactivate(const ARegion ®ion, bNodeLinkDrag &nldrag)
|
|
|
|
|
{
|
|
|
|
|
if (nldrag.draw_handle) {
|
|
|
|
|
ED_region_draw_cb_exit(region.type, nldrag.draw_handle);
|
|
|
|
|
nldrag.draw_handle = nullptr;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-28 10:07:29 +01:00
|
|
|
static int node_socket_count_links(const bNodeTree &ntree, const bNodeSocket &socket)
|
2014-04-29 11:28:16 +02:00
|
|
|
{
|
|
|
|
|
int count = 0;
|
2021-12-03 16:25:17 -05:00
|
|
|
LISTBASE_FOREACH (bNodeLink *, link, &ntree.links) {
|
|
|
|
|
if (ELEM(&socket, link->fromsock, link->tosock)) {
|
2019-09-08 00:12:26 +10:00
|
|
|
count++;
|
2019-03-26 21:16:47 +11:00
|
|
|
}
|
2014-04-29 11:28:16 +02:00
|
|
|
}
|
|
|
|
|
return count;
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-28 10:07:29 +01:00
|
|
|
static bNodeSocket *node_find_linkable_socket(const bNodeTree &ntree,
|
|
|
|
|
const bNode *node,
|
|
|
|
|
bNodeSocket *socket_to_match)
|
2012-08-01 19:11:17 +00:00
|
|
|
{
|
2023-01-28 10:07:29 +01:00
|
|
|
bNodeSocket *first_socket = socket_to_match->in_out == SOCK_IN ?
|
|
|
|
|
static_cast<bNodeSocket *>(node->inputs.first) :
|
|
|
|
|
static_cast<bNodeSocket *>(node->outputs.first);
|
|
|
|
|
|
|
|
|
|
bNodeSocket *socket = socket_to_match->next ? socket_to_match->next : first_socket;
|
|
|
|
|
while (socket != socket_to_match) {
|
|
|
|
|
if (!socket->is_hidden() && socket->is_available()) {
|
|
|
|
|
const bool sockets_are_compatible = socket->typeinfo == socket_to_match->typeinfo;
|
|
|
|
|
if (sockets_are_compatible) {
|
|
|
|
|
const int link_count = node_socket_count_links(ntree, *socket);
|
|
|
|
|
const bool socket_has_capacity = link_count < nodeSocketLinkLimit(socket);
|
|
|
|
|
if (socket_has_capacity) {
|
|
|
|
|
/* Found a valid free socket we can swap to. */
|
|
|
|
|
return socket;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/* Wrap around the list end. */
|
|
|
|
|
socket = socket->next ? socket->next : first_socket;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void displace_links(bNodeTree *ntree, const bNode *node, bNodeLink *inserted_link)
|
|
|
|
|
{
|
|
|
|
|
bNodeSocket *linked_socket = node == inserted_link->tonode ? inserted_link->tosock :
|
|
|
|
|
inserted_link->fromsock;
|
|
|
|
|
bNodeSocket *replacement_socket = node_find_linkable_socket(*ntree, node, linked_socket);
|
|
|
|
|
|
|
|
|
|
if (linked_socket->is_input()) {
|
2023-03-11 08:27:56 +01:00
|
|
|
BLI_assert(!linked_socket->is_multi_input());
|
|
|
|
|
ntree->ensure_topology_cache();
|
2023-03-17 13:55:01 +01:00
|
|
|
|
|
|
|
|
if (linked_socket->directly_linked_links().is_empty()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
bNodeLink *displaced_link = linked_socket->directly_linked_links().first();
|
2023-03-11 08:27:56 +01:00
|
|
|
|
|
|
|
|
if (!replacement_socket) {
|
|
|
|
|
nodeRemLink(ntree, displaced_link);
|
2023-01-28 10:07:29 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-11 08:27:56 +01:00
|
|
|
displaced_link->tosock = replacement_socket;
|
2023-01-28 10:07:29 +01:00
|
|
|
|
2023-03-11 08:27:56 +01:00
|
|
|
if (replacement_socket->is_multi_input()) {
|
|
|
|
|
/* Check for duplicate links when linking to multi input sockets. */
|
|
|
|
|
for (bNodeLink *existing_link : replacement_socket->runtime->directly_linked_links) {
|
|
|
|
|
if (existing_link->fromsock == displaced_link->fromsock) {
|
|
|
|
|
nodeRemLink(ntree, displaced_link);
|
|
|
|
|
return;
|
2023-01-28 10:07:29 +01:00
|
|
|
}
|
|
|
|
|
}
|
2023-03-11 08:27:56 +01:00
|
|
|
const int multi_input_index = node_socket_count_links(*ntree, *replacement_socket) - 1;
|
|
|
|
|
displaced_link->multi_input_socket_index = multi_input_index;
|
2023-01-28 10:07:29 +01:00
|
|
|
}
|
2023-03-11 08:27:56 +01:00
|
|
|
|
|
|
|
|
BKE_ntree_update_tag_link_changed(ntree);
|
|
|
|
|
return;
|
2023-01-28 10:07:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree->links) {
|
|
|
|
|
if (link->fromsock == linked_socket) {
|
|
|
|
|
if (replacement_socket) {
|
|
|
|
|
link->fromsock = replacement_socket;
|
|
|
|
|
BKE_ntree_update_tag_link_changed(ntree);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
nodeRemLink(ntree, link);
|
|
|
|
|
BKE_ntree_update_tag_link_removed(ntree);
|
|
|
|
|
}
|
2019-03-26 21:16:47 +11:00
|
|
|
}
|
2023-01-28 10:07:29 +01:00
|
|
|
}
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-01-28 10:07:29 +01:00
|
|
|
static void node_displace_existing_links(bNodeLinkDrag &nldrag, bNodeTree &ntree)
|
|
|
|
|
{
|
|
|
|
|
bNodeLink &link = nldrag.links.first();
|
|
|
|
|
if (nldrag.start_socket->is_input()) {
|
|
|
|
|
displace_links(&ntree, link.fromnode, &link);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
displace_links(&ntree, link.tonode, &link);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void node_swap_links(bNodeLinkDrag &nldrag, bNodeTree &ntree)
|
|
|
|
|
{
|
|
|
|
|
bNodeSocket &linked_socket = *nldrag.hovered_socket;
|
|
|
|
|
bNodeSocket *start_socket = nldrag.start_socket;
|
|
|
|
|
bNode *start_node = nldrag.start_node;
|
|
|
|
|
|
|
|
|
|
if (linked_socket.is_input()) {
|
|
|
|
|
LISTBASE_FOREACH (bNodeLink *, link, &ntree.links) {
|
2023-03-16 21:39:32 +01:00
|
|
|
if (link->tosock != &linked_socket) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (link->fromnode == start_node) {
|
|
|
|
|
/* Don't link a node to itself. */
|
|
|
|
|
nodeRemLink(&ntree, link);
|
|
|
|
|
continue;
|
2013-11-15 16:54:05 +01:00
|
|
|
}
|
2023-03-16 21:39:32 +01:00
|
|
|
|
|
|
|
|
link->tosock = start_socket;
|
|
|
|
|
link->tonode = start_node;
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
2023-01-28 10:07:29 +01:00
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
LISTBASE_FOREACH (bNodeLink *, link, &ntree.links) {
|
2023-03-16 21:39:32 +01:00
|
|
|
if (link->fromsock != &linked_socket) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (link->tonode == start_node) {
|
|
|
|
|
/* Don't link a node to itself. */
|
|
|
|
|
nodeRemLink(&ntree, link);
|
|
|
|
|
continue;
|
2023-01-28 10:07:29 +01:00
|
|
|
}
|
2023-03-16 21:39:32 +01:00
|
|
|
link->fromsock = start_socket;
|
|
|
|
|
link->fromnode = start_node;
|
2023-01-28 10:07:29 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BKE_ntree_update_tag_link_changed(&ntree);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void node_remove_existing_links_if_needed(bNodeLinkDrag &nldrag, bNodeTree &ntree)
|
|
|
|
|
{
|
|
|
|
|
bNodeSocket &linked_socket = *nldrag.hovered_socket;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-03-11 08:27:56 +01:00
|
|
|
int link_count = node_socket_count_links(ntree, linked_socket);
|
2023-01-28 10:07:29 +01:00
|
|
|
const int link_limit = nodeSocketLinkLimit(&linked_socket);
|
2023-03-11 08:27:56 +01:00
|
|
|
Set<bNodeLink *> links_to_remove;
|
|
|
|
|
|
|
|
|
|
ntree.ensure_topology_cache();
|
|
|
|
|
|
|
|
|
|
/* Remove duplicate links first. */
|
|
|
|
|
for (const bNodeLink dragged_link : nldrag.links) {
|
|
|
|
|
if (linked_socket.is_input()) {
|
|
|
|
|
for (bNodeLink *link : linked_socket.runtime->directly_linked_links) {
|
|
|
|
|
const bool duplicate_link = link->fromsock == dragged_link.fromsock;
|
|
|
|
|
if (duplicate_link) {
|
|
|
|
|
links_to_remove.add(link);
|
|
|
|
|
link_count--;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
for (bNodeLink *link : linked_socket.runtime->directly_linked_links) {
|
|
|
|
|
const bool duplicate_link = link->tosock == dragged_link.tosock;
|
|
|
|
|
if (duplicate_link) {
|
|
|
|
|
links_to_remove.add(link);
|
|
|
|
|
link_count--;
|
|
|
|
|
}
|
2013-11-15 16:54:05 +01:00
|
|
|
}
|
2023-01-28 10:07:29 +01:00
|
|
|
}
|
|
|
|
|
}
|
2023-03-11 08:27:56 +01:00
|
|
|
|
|
|
|
|
for (bNodeLink *link : linked_socket.runtime->directly_linked_links) {
|
|
|
|
|
const bool link_limit_exceeded = !(link_count < link_limit);
|
|
|
|
|
if (link_limit_exceeded) {
|
|
|
|
|
if (links_to_remove.add(link)) {
|
|
|
|
|
link_count--;
|
2021-02-11 01:16:17 -06:00
|
|
|
}
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
|
|
|
|
}
|
2023-03-11 08:27:56 +01:00
|
|
|
|
|
|
|
|
for (bNodeLink *link : links_to_remove) {
|
|
|
|
|
nodeRemLink(&ntree, link);
|
|
|
|
|
}
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
|
|
|
|
|
2022-12-16 14:03:49 -06:00
|
|
|
static void add_dragged_links_to_tree(bContext &C, bNodeLinkDrag &nldrag)
|
2012-08-01 19:11:17 +00:00
|
|
|
{
|
2021-12-03 16:25:17 -05:00
|
|
|
Main *bmain = CTX_data_main(&C);
|
2021-12-15 09:51:57 -06:00
|
|
|
ARegion ®ion = *CTX_wm_region(&C);
|
2021-12-03 16:25:17 -05:00
|
|
|
SpaceNode &snode = *CTX_wm_space_node(&C);
|
|
|
|
|
bNodeTree &ntree = *snode.edittree;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-01-28 10:07:29 +01:00
|
|
|
/* Handle node links already occupying the socket. */
|
|
|
|
|
if (const bNodeSocket *linked_socket = nldrag.hovered_socket) {
|
|
|
|
|
/* Swapping existing links out of multi input sockets is not supported. */
|
|
|
|
|
const bool connecting_to_multi_input = linked_socket->is_multi_input() ||
|
|
|
|
|
nldrag.start_socket->is_multi_input();
|
|
|
|
|
if (nldrag.swap_links && !connecting_to_multi_input) {
|
|
|
|
|
const bool is_new_link = nldrag.in_out == nldrag.start_socket->in_out;
|
|
|
|
|
if (is_new_link) {
|
|
|
|
|
node_displace_existing_links(nldrag, ntree);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
node_swap_links(nldrag, ntree);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
node_remove_existing_links_if_needed(nldrag, ntree);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-29 14:09:58 -05:00
|
|
|
for (const bNodeLink &link : nldrag.links) {
|
|
|
|
|
if (!link.tosock || !link.fromsock) {
|
2022-12-16 14:03:49 -06:00
|
|
|
continue;
|
|
|
|
|
}
|
2023-01-28 10:07:29 +01:00
|
|
|
|
2023-01-03 20:02:01 -05:00
|
|
|
/* Before actually adding the link let nodes perform special link insertion handling. */
|
2022-12-29 14:09:58 -05:00
|
|
|
bNodeLink *new_link = MEM_new<bNodeLink>(__func__, link);
|
|
|
|
|
if (link.fromnode->typeinfo->insert_link) {
|
2023-01-16 15:47:10 -06:00
|
|
|
if (!link.fromnode->typeinfo->insert_link(&ntree, link.fromnode, new_link)) {
|
2023-01-28 10:07:29 +01:00
|
|
|
MEM_freeN(new_link);
|
2023-01-16 15:47:10 -06:00
|
|
|
continue;
|
|
|
|
|
}
|
2014-04-29 11:28:16 +02:00
|
|
|
}
|
2022-12-29 14:09:58 -05:00
|
|
|
if (link.tonode->typeinfo->insert_link) {
|
2023-01-16 15:47:10 -06:00
|
|
|
if (!link.tonode->typeinfo->insert_link(&ntree, link.tonode, new_link)) {
|
2023-01-28 10:07:29 +01:00
|
|
|
MEM_freeN(new_link);
|
2023-01-16 15:47:10 -06:00
|
|
|
continue;
|
|
|
|
|
}
|
2016-02-05 01:39:42 +05:00
|
|
|
}
|
2022-12-16 14:03:49 -06:00
|
|
|
|
2022-12-29 14:09:58 -05:00
|
|
|
BLI_addtail(&ntree.links, new_link);
|
|
|
|
|
BKE_ntree_update_tag_link_added(&ntree, new_link);
|
2014-04-29 11:28:16 +02:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-12-21 15:18:56 +01:00
|
|
|
ED_node_tree_propagate_change(&C, bmain, &ntree);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-09-29 10:28:39 +10:00
|
|
|
/* Ensure drag-link tool-tip is disabled. */
|
2022-12-16 14:03:49 -06:00
|
|
|
draw_draglink_tooltip_deactivate(region, nldrag);
|
2021-12-15 09:51:57 -06:00
|
|
|
|
|
|
|
|
ED_workspace_status_text(&C, nullptr);
|
|
|
|
|
ED_region_tag_redraw(®ion);
|
|
|
|
|
clear_picking_highlight(&snode.edittree->links);
|
2021-08-24 17:45:40 +01:00
|
|
|
|
2021-12-03 16:25:17 -05:00
|
|
|
snode.runtime->linkdrag.reset();
|
2014-04-29 11:28:16 +02:00
|
|
|
}
|
|
|
|
|
|
2022-12-16 14:03:49 -06:00
|
|
|
static void node_link_cancel(bContext *C, wmOperator *op)
|
|
|
|
|
{
|
|
|
|
|
SpaceNode *snode = CTX_wm_space_node(C);
|
|
|
|
|
bNodeLinkDrag *nldrag = (bNodeLinkDrag *)op->customdata;
|
|
|
|
|
draw_draglink_tooltip_deactivate(*CTX_wm_region(C), *nldrag);
|
|
|
|
|
UI_view2d_edge_pan_cancel(C, &nldrag->pan_data);
|
|
|
|
|
snode->runtime->linkdrag.reset();
|
|
|
|
|
clear_picking_highlight(&snode->edittree->links);
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-03 16:25:17 -05:00
|
|
|
static void node_link_find_socket(bContext &C, wmOperator &op, const float2 &cursor)
|
2014-04-29 11:28:16 +02:00
|
|
|
{
|
2021-12-03 16:25:17 -05:00
|
|
|
SpaceNode &snode = *CTX_wm_space_node(&C);
|
2023-11-25 15:23:31 +01:00
|
|
|
ARegion ®ion = *CTX_wm_region(&C);
|
2022-12-29 14:09:58 -05:00
|
|
|
bNodeLinkDrag &nldrag = *static_cast<bNodeLinkDrag *>(op.customdata);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-12-29 14:09:58 -05:00
|
|
|
if (nldrag.in_out == SOCK_OUT) {
|
2023-11-25 15:23:31 +01:00
|
|
|
if (bNodeSocket *tsock = node_find_indicated_socket(snode, region, cursor, SOCK_IN)) {
|
2023-01-28 10:07:29 +01:00
|
|
|
nldrag.hovered_socket = tsock;
|
2023-01-03 13:34:51 -05:00
|
|
|
bNode &tnode = tsock->owner_node();
|
2022-12-29 14:09:58 -05:00
|
|
|
for (bNodeLink &link : nldrag.links) {
|
2023-01-03 20:02:01 -05:00
|
|
|
/* Skip if socket is on the same node as the fromsock. */
|
2023-01-03 13:34:51 -05:00
|
|
|
if (link.fromnode == &tnode) {
|
2014-04-29 11:28:16 +02:00
|
|
|
continue;
|
2019-03-26 21:16:47 +11:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-03-11 18:53:29 +01:00
|
|
|
/* Skip if tsock is already linked with this output. */
|
2021-06-02 17:19:36 +02:00
|
|
|
bNodeLink *existing_link_connected_to_fromsock = nullptr;
|
2021-12-03 16:25:17 -05:00
|
|
|
LISTBASE_FOREACH (bNodeLink *, existing_link, &snode.edittree->links) {
|
2022-12-29 14:09:58 -05:00
|
|
|
if (existing_link->fromsock == link.fromsock && existing_link->tosock == tsock) {
|
2021-03-11 18:53:29 +01:00
|
|
|
existing_link_connected_to_fromsock = existing_link;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-03 20:02:01 -05:00
|
|
|
/* Attach links to the socket. */
|
2023-01-03 13:34:51 -05:00
|
|
|
link.tonode = &tnode;
|
2022-12-29 14:09:58 -05:00
|
|
|
link.tosock = tsock;
|
2023-01-03 13:34:51 -05:00
|
|
|
nldrag.last_node_hovered_while_dragging_a_link = &tnode;
|
2021-03-11 18:53:29 +01:00
|
|
|
if (existing_link_connected_to_fromsock) {
|
2022-12-29 14:09:58 -05:00
|
|
|
link.multi_input_socket_index =
|
2021-03-11 18:53:29 +01:00
|
|
|
existing_link_connected_to_fromsock->multi_input_socket_index;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2023-01-04 16:37:32 -05:00
|
|
|
if (tsock && tsock->is_multi_input()) {
|
2023-03-19 07:03:01 +01:00
|
|
|
sort_multi_input_socket_links_with_drag(*tsock, link, cursor);
|
2021-04-15 10:00:25 +02:00
|
|
|
}
|
2014-04-29 11:28:16 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
2023-01-28 10:07:29 +01:00
|
|
|
nldrag.hovered_socket = nullptr;
|
2022-12-29 14:09:58 -05:00
|
|
|
for (bNodeLink &link : nldrag.links) {
|
|
|
|
|
link.tonode = nullptr;
|
|
|
|
|
link.tosock = nullptr;
|
2014-04-29 11:28:16 +02:00
|
|
|
}
|
2022-12-29 14:09:58 -05:00
|
|
|
if (nldrag.last_node_hovered_while_dragging_a_link) {
|
2022-09-03 23:22:12 -05:00
|
|
|
update_multi_input_indices_for_removed_links(
|
2022-12-29 14:09:58 -05:00
|
|
|
*nldrag.last_node_hovered_while_dragging_a_link);
|
2022-09-03 23:22:12 -05:00
|
|
|
}
|
2014-04-29 11:28:16 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
2023-11-25 15:23:31 +01:00
|
|
|
if (bNodeSocket *tsock = node_find_indicated_socket(snode, region, cursor, SOCK_OUT)) {
|
2023-01-28 10:07:29 +01:00
|
|
|
nldrag.hovered_socket = tsock;
|
2023-01-03 13:34:51 -05:00
|
|
|
bNode &node = tsock->owner_node();
|
2022-12-29 14:09:58 -05:00
|
|
|
for (bNodeLink &link : nldrag.links) {
|
2023-01-03 20:02:01 -05:00
|
|
|
/* Skip if this is already the target socket. */
|
2022-12-29 14:09:58 -05:00
|
|
|
if (link.fromsock == tsock) {
|
2014-04-29 11:28:16 +02:00
|
|
|
continue;
|
2019-03-26 21:16:47 +11:00
|
|
|
}
|
2023-01-03 20:02:01 -05:00
|
|
|
/* Skip if socket is on the same node as the `fromsock`. */
|
2023-01-03 13:34:51 -05:00
|
|
|
if (link.tonode == &node) {
|
2014-04-29 11:28:16 +02:00
|
|
|
continue;
|
2019-03-26 21:16:47 +11:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-01-03 20:02:01 -05:00
|
|
|
/* Attach links to the socket. */
|
2023-01-03 13:34:51 -05:00
|
|
|
link.fromnode = &node;
|
2022-12-29 14:09:58 -05:00
|
|
|
link.fromsock = tsock;
|
2014-04-29 11:28:16 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
2023-01-28 10:07:29 +01:00
|
|
|
nldrag.hovered_socket = nullptr;
|
2022-12-29 14:09:58 -05:00
|
|
|
for (bNodeLink &link : nldrag.links) {
|
|
|
|
|
link.fromnode = nullptr;
|
|
|
|
|
link.fromsock = nullptr;
|
2014-04-29 11:28:16 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-03 17:41:12 +01:00
|
|
|
enum class NodeLinkAction : int {
|
2023-11-29 17:26:06 +11:00
|
|
|
Begin = 0,
|
|
|
|
|
Cancel = 1,
|
|
|
|
|
Swap = 2,
|
|
|
|
|
Confirm = 3,
|
2023-02-03 17:41:12 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
wmKeyMap *node_link_modal_keymap(wmKeyConfig *keyconf)
|
|
|
|
|
{
|
|
|
|
|
static const EnumPropertyItem modal_items[] = {
|
|
|
|
|
{int(NodeLinkAction::Begin), "BEGIN", 0, "Drag Node-link", ""},
|
|
|
|
|
{int(NodeLinkAction::Confirm), "CONFIRM", 0, "Confirm Link", ""},
|
|
|
|
|
{int(NodeLinkAction::Cancel), "CANCEL", 0, "Cancel", ""},
|
|
|
|
|
{int(NodeLinkAction::Swap), "SWAP", 0, "Swap Links", ""},
|
|
|
|
|
{0, nullptr, 0, nullptr, nullptr},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "Node Link Modal Map");
|
|
|
|
|
|
|
|
|
|
/* This function is called for each space-type, only needs to add map once. */
|
|
|
|
|
if (keymap && keymap->modal_items) {
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
keymap = WM_modalkeymap_ensure(keyconf, "Node Link Modal Map", modal_items);
|
|
|
|
|
|
|
|
|
|
WM_modalkeymap_assign(keymap, "NODE_OT_link");
|
|
|
|
|
|
|
|
|
|
return keymap;
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-29 11:28:16 +02:00
|
|
|
static int node_link_modal(bContext *C, wmOperator *op, const wmEvent *event)
|
|
|
|
|
{
|
2022-12-29 14:09:58 -05:00
|
|
|
bNodeLinkDrag &nldrag = *static_cast<bNodeLinkDrag *>(op->customdata);
|
2021-12-15 09:51:57 -06:00
|
|
|
SpaceNode &snode = *CTX_wm_space_node(C);
|
2020-03-06 16:56:42 +01:00
|
|
|
ARegion *region = CTX_wm_region(C);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-12-29 14:09:58 -05:00
|
|
|
UI_view2d_edge_pan_apply_event(C, &nldrag.pan_data, event);
|
2021-06-16 18:17:07 +01:00
|
|
|
|
2021-12-03 16:25:17 -05:00
|
|
|
float2 cursor;
|
|
|
|
|
UI_view2d_region_to_view(®ion->v2d, event->mval[0], event->mval[1], &cursor.x, &cursor.y);
|
2022-12-29 14:09:58 -05:00
|
|
|
nldrag.cursor[0] = event->mval[0];
|
|
|
|
|
nldrag.cursor[1] = event->mval[1];
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-02-03 17:41:12 +01:00
|
|
|
if (event->type == EVT_MODAL_MAP) {
|
|
|
|
|
switch (event->val) {
|
|
|
|
|
case int(NodeLinkAction::Begin): {
|
|
|
|
|
return OPERATOR_RUNNING_MODAL;
|
2021-12-15 09:51:57 -06:00
|
|
|
}
|
2023-02-03 17:41:12 +01:00
|
|
|
case int(NodeLinkAction::Confirm): {
|
2021-12-15 09:51:57 -06:00
|
|
|
/* Add a search menu for compatible sockets if the drag released on empty space. */
|
2022-12-29 14:09:58 -05:00
|
|
|
if (should_create_drag_link_search_menu(*snode.edittree, nldrag)) {
|
|
|
|
|
bNodeLink &link = nldrag.links.first();
|
|
|
|
|
if (nldrag.in_out == SOCK_OUT) {
|
2022-01-20 10:36:56 -06:00
|
|
|
invoke_node_link_drag_add_menu(*C, *link.fromnode, *link.fromsock, cursor);
|
2021-12-15 09:51:57 -06:00
|
|
|
}
|
|
|
|
|
else {
|
2022-01-20 10:36:56 -06:00
|
|
|
invoke_node_link_drag_add_menu(*C, *link.tonode, *link.tosock, cursor);
|
2021-12-15 09:51:57 -06:00
|
|
|
}
|
|
|
|
|
}
|
2022-12-29 14:09:58 -05:00
|
|
|
add_dragged_links_to_tree(*C, nldrag);
|
2021-12-15 09:51:57 -06:00
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
2023-02-03 17:41:12 +01:00
|
|
|
case int(NodeLinkAction::Cancel): {
|
2022-12-16 14:03:49 -06:00
|
|
|
node_link_cancel(C, op);
|
|
|
|
|
return OPERATOR_CANCELLED;
|
2018-06-08 18:52:00 +02:00
|
|
|
}
|
2023-02-03 17:41:12 +01:00
|
|
|
case int(NodeLinkAction::Swap):
|
|
|
|
|
if (event->prev_val == KM_PRESS) {
|
|
|
|
|
nldrag.swap_links = true;
|
|
|
|
|
}
|
|
|
|
|
else if (event->prev_val == KM_RELEASE) {
|
|
|
|
|
nldrag.swap_links = false;
|
|
|
|
|
}
|
|
|
|
|
return OPERATOR_RUNNING_MODAL;
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
2023-02-03 17:41:12 +01:00
|
|
|
}
|
|
|
|
|
else if (event->type == MOUSEMOVE) {
|
|
|
|
|
if (nldrag.start_socket->is_multi_input() && nldrag.links.is_empty()) {
|
|
|
|
|
pick_input_link_by_link_intersect(*C, *op, nldrag, cursor);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
node_link_find_socket(*C, *op, cursor);
|
|
|
|
|
ED_region_tag_redraw(region);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (need_drag_link_tooltip(*snode.edittree, nldrag)) {
|
|
|
|
|
draw_draglink_tooltip_activate(*region, nldrag);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
draw_draglink_tooltip_deactivate(*region, nldrag);
|
2021-12-15 09:51:57 -06:00
|
|
|
}
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-08-01 19:11:17 +00:00
|
|
|
return OPERATOR_RUNNING_MODAL;
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-25 15:23:31 +01:00
|
|
|
static std::unique_ptr<bNodeLinkDrag> node_link_init(ARegion ®ion,
|
|
|
|
|
SpaceNode &snode,
|
2022-12-16 13:22:34 -06:00
|
|
|
const float2 cursor,
|
2021-12-03 16:25:17 -05:00
|
|
|
const bool detach)
|
2012-08-01 19:11:17 +00:00
|
|
|
{
|
2023-11-25 15:23:31 +01:00
|
|
|
if (bNodeSocket *sock = node_find_indicated_socket(snode, region, cursor, SOCK_OUT)) {
|
2023-01-03 13:34:51 -05:00
|
|
|
bNode &node = sock->owner_node();
|
|
|
|
|
|
2021-11-19 15:36:32 -05:00
|
|
|
std::unique_ptr<bNodeLinkDrag> nldrag = std::make_unique<bNodeLinkDrag>();
|
2023-01-03 13:34:51 -05:00
|
|
|
nldrag->start_node = &node;
|
2021-12-15 09:51:57 -06:00
|
|
|
nldrag->start_socket = sock;
|
|
|
|
|
nldrag->start_link_count = nodeCountSocketLinks(snode.edittree, sock);
|
Nodes: Support storing socket link limits in bNodeSocketType
Currently the link limit of sockets is stored in bNodeSocket->limit.
This allows for a lot of flexibility, but is also very redundant.
In every case I've had to deal with so far, it would have "more correct"
to set the link limit per socket type and not per socket. I did not enforce
this constraint yet, because the link limit is exposed in the Python API,
which I did not want to break here.
In the future it might even make sense to only support only three kinds of link limits:
a) no links, b) at most one link, c) an arbitrary number links links. The other link
limits usually don't work well with tools (e.g. which link should be removed when a new
one is connected?) and is not used in practice. However, that is for another day.
Eventually, I would like to get rid of bNodeSocket->limit completely and replace it
either with fixed link limits or a callback in bNodeSocketType.
This patch consists of three parts:
**1. Support defining link limit in socket type**
This introduces a new `nodeSocketLinkLimit` function that serves as an indirection to
hide where the link limit of a socket is defined.
**2. Define link limits for builtin sockets on socket type**
Data sockets: one input, many outputs
Virtual sockets: one input, one output
Undefined sockets: many inputs, many outputs (to avoid that links are removed when the type of the socket is not known)
**3. Remove `bNodeSocketTemplate->limit`**
This wasn't used anymore after the second commit. Removing it simplifies socket definitions
in hundreds of places and removes a lot of redundancy.
Differential Revision: https://developer.blender.org/D7038
Reviewers: brecht
2020-03-06 12:20:05 +01:00
|
|
|
int link_limit = nodeSocketLinkLimit(sock);
|
2021-12-15 09:51:57 -06:00
|
|
|
if (nldrag->start_link_count > 0 && (nldrag->start_link_count >= link_limit || detach)) {
|
2023-01-03 20:02:01 -05:00
|
|
|
/* Dragged links are fixed on input side. */
|
2012-08-01 19:11:17 +00:00
|
|
|
nldrag->in_out = SOCK_IN;
|
2023-01-03 20:02:01 -05:00
|
|
|
/* Detach current links and store them in the operator data. */
|
2021-12-03 16:25:17 -05:00
|
|
|
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &snode.edittree->links) {
|
2012-08-01 19:11:17 +00:00
|
|
|
if (link->fromsock == sock) {
|
2022-12-29 14:09:58 -05:00
|
|
|
bNodeLink oplink = *link;
|
|
|
|
|
oplink.next = oplink.prev = nullptr;
|
|
|
|
|
oplink.flag |= NODE_LINK_VALID;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-11-19 15:36:32 -05:00
|
|
|
nldrag->links.append(oplink);
|
2021-12-03 16:25:17 -05:00
|
|
|
nodeRemLink(snode.edittree, link);
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
2023-01-03 20:02:01 -05:00
|
|
|
/* Dragged links are fixed on output side. */
|
2012-08-01 19:11:17 +00:00
|
|
|
nldrag->in_out = SOCK_OUT;
|
2023-01-03 13:34:51 -05:00
|
|
|
nldrag->links.append(create_drag_link(node, *sock));
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
2021-11-19 15:36:32 -05:00
|
|
|
return nldrag;
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
2021-11-19 15:36:32 -05:00
|
|
|
|
2023-11-25 15:23:31 +01:00
|
|
|
if (bNodeSocket *sock = node_find_indicated_socket(snode, region, cursor, SOCK_IN)) {
|
2023-01-03 13:34:51 -05:00
|
|
|
bNode &node = sock->owner_node();
|
2021-11-19 15:36:32 -05:00
|
|
|
std::unique_ptr<bNodeLinkDrag> nldrag = std::make_unique<bNodeLinkDrag>();
|
2023-01-03 13:34:51 -05:00
|
|
|
nldrag->last_node_hovered_while_dragging_a_link = &node;
|
|
|
|
|
nldrag->start_node = &node;
|
2021-12-15 09:51:57 -06:00
|
|
|
nldrag->start_socket = sock;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-12-15 09:51:57 -06:00
|
|
|
nldrag->start_link_count = nodeCountSocketLinks(snode.edittree, sock);
|
|
|
|
|
if (nldrag->start_link_count > 0) {
|
2023-01-03 20:02:01 -05:00
|
|
|
/* Dragged links are fixed on output side. */
|
2012-08-01 19:11:17 +00:00
|
|
|
nldrag->in_out = SOCK_OUT;
|
2023-01-03 20:02:01 -05:00
|
|
|
/* Detach current links and store them in the operator data. */
|
2021-02-03 11:02:01 -06:00
|
|
|
bNodeLink *link_to_pick;
|
2021-12-03 16:25:17 -05:00
|
|
|
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &snode.edittree->links) {
|
2012-08-01 19:11:17 +00:00
|
|
|
if (link->tosock == sock) {
|
2021-02-03 11:02:01 -06:00
|
|
|
link_to_pick = link;
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-12-16 12:57:15 -06:00
|
|
|
if (link_to_pick != nullptr && !nldrag->start_socket->is_multi_input()) {
|
2022-12-29 14:09:58 -05:00
|
|
|
bNodeLink oplink = *link_to_pick;
|
|
|
|
|
oplink.next = oplink.prev = nullptr;
|
|
|
|
|
oplink.flag |= NODE_LINK_VALID;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-11-19 15:36:32 -05:00
|
|
|
nldrag->links.append(oplink);
|
2021-12-03 16:25:17 -05:00
|
|
|
nodeRemLink(snode.edittree, link_to_pick);
|
2021-02-03 11:02:01 -06:00
|
|
|
|
2023-01-03 20:02:01 -05:00
|
|
|
/* Send changed event to original link->tonode. */
|
2023-01-03 13:34:51 -05:00
|
|
|
BKE_ntree_update_tag_node_property(snode.edittree, &node);
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
2023-01-03 20:02:01 -05:00
|
|
|
/* Dragged links are fixed on input side. */
|
2012-08-01 19:11:17 +00:00
|
|
|
nldrag->in_out = SOCK_IN;
|
2023-01-03 13:34:51 -05:00
|
|
|
nldrag->links.append(create_drag_link(node, *sock));
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
2021-11-19 15:36:32 -05:00
|
|
|
return nldrag;
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-11-19 15:36:32 -05:00
|
|
|
return {};
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
|
|
|
|
|
2013-03-13 09:03:46 +00:00
|
|
|
static int node_link_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
2012-08-01 19:11:17 +00:00
|
|
|
{
|
2021-12-03 16:25:17 -05:00
|
|
|
Main &bmain = *CTX_data_main(C);
|
|
|
|
|
SpaceNode &snode = *CTX_wm_space_node(C);
|
|
|
|
|
ARegion ®ion = *CTX_wm_region(C);
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2014-04-29 11:28:16 +02:00
|
|
|
bool detach = RNA_boolean_get(op->ptr, "detach");
|
2012-08-01 19:11:17 +00:00
|
|
|
|
2022-09-02 14:09:32 -05:00
|
|
|
int2 mval;
|
2022-03-09 08:36:36 +11:00
|
|
|
WM_event_drag_start_mval(event, ®ion, mval);
|
|
|
|
|
|
2021-12-03 16:25:17 -05:00
|
|
|
float2 cursor;
|
2022-03-09 08:36:36 +11:00
|
|
|
UI_view2d_region_to_view(®ion.v2d, mval[0], mval[1], &cursor[0], &cursor[1]);
|
2021-02-03 11:02:01 -06:00
|
|
|
RNA_float_set_array(op->ptr, "drag_start", cursor);
|
2012-08-01 19:11:17 +00:00
|
|
|
|
2021-12-03 16:25:17 -05:00
|
|
|
ED_preview_kill_jobs(CTX_wm_manager(C), &bmain);
|
2012-08-01 19:11:17 +00:00
|
|
|
|
2023-11-25 15:23:31 +01:00
|
|
|
std::unique_ptr<bNodeLinkDrag> nldrag = node_link_init(region, snode, cursor, detach);
|
2022-12-16 13:22:34 -06:00
|
|
|
if (!nldrag) {
|
|
|
|
|
return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
|
|
|
|
|
}
|
2012-08-01 19:11:17 +00:00
|
|
|
|
2022-12-16 13:22:34 -06:00
|
|
|
UI_view2d_edge_pan_operator_init(C, &nldrag->pan_data, op);
|
2021-06-16 18:17:07 +01:00
|
|
|
|
2023-01-28 10:07:29 +01:00
|
|
|
/* Add icons at the cursor when the link is dragged in empty space. */
|
|
|
|
|
if (need_drag_link_tooltip(*snode.edittree, *nldrag)) {
|
2022-12-16 13:22:34 -06:00
|
|
|
draw_draglink_tooltip_activate(*CTX_wm_region(C), *nldrag);
|
|
|
|
|
}
|
|
|
|
|
snode.runtime->linkdrag = std::move(nldrag);
|
|
|
|
|
op->customdata = snode.runtime->linkdrag.get();
|
2012-08-01 19:11:17 +00:00
|
|
|
|
2022-12-16 13:22:34 -06:00
|
|
|
WM_event_add_modal_handler(C, op);
|
2012-08-01 19:11:17 +00:00
|
|
|
|
2022-12-16 13:22:34 -06:00
|
|
|
return OPERATOR_RUNNING_MODAL;
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void NODE_OT_link(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
/* identifiers */
|
|
|
|
|
ot->name = "Link Nodes";
|
|
|
|
|
ot->idname = "NODE_OT_link";
|
|
|
|
|
ot->description = "Use the mouse to create a link between two nodes";
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-08-01 19:11:17 +00:00
|
|
|
/* api callbacks */
|
|
|
|
|
ot->invoke = node_link_invoke;
|
|
|
|
|
ot->modal = node_link_modal;
|
2013-06-05 19:06:33 +00:00
|
|
|
ot->poll = ED_operator_node_editable;
|
2012-08-01 19:11:17 +00:00
|
|
|
ot->cancel = node_link_cancel;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-08-01 19:11:17 +00:00
|
|
|
/* flags */
|
|
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2014-04-01 11:34:00 +11:00
|
|
|
RNA_def_boolean(ot->srna, "detach", false, "Detach", "Detach and redirect existing links");
|
2021-02-03 11:02:01 -06:00
|
|
|
RNA_def_float_array(ot->srna,
|
|
|
|
|
"drag_start",
|
|
|
|
|
2,
|
2021-06-02 17:19:36 +02:00
|
|
|
nullptr,
|
2021-02-03 11:02:01 -06:00
|
|
|
-UI_PRECISION_FLOAT_MAX,
|
|
|
|
|
UI_PRECISION_FLOAT_MAX,
|
|
|
|
|
"Drag Start",
|
2021-02-03 20:04:28 +01:00
|
|
|
"The position of the mouse cursor at the start of the operation",
|
2021-02-03 11:02:01 -06:00
|
|
|
-UI_PRECISION_FLOAT_MAX,
|
|
|
|
|
UI_PRECISION_FLOAT_MAX);
|
2021-06-16 18:17:07 +01:00
|
|
|
|
|
|
|
|
UI_view2d_edge_pan_operator_properties_ex(ot,
|
|
|
|
|
NODE_EDGE_PAN_INSIDE_PAD,
|
|
|
|
|
NODE_EDGE_PAN_OUTSIDE_PAD,
|
|
|
|
|
NODE_EDGE_PAN_SPEED_RAMP,
|
|
|
|
|
NODE_EDGE_PAN_MAX_SPEED,
|
2021-08-24 17:45:40 +01:00
|
|
|
NODE_EDGE_PAN_DELAY,
|
|
|
|
|
NODE_EDGE_PAN_ZOOM_INFLUENCE);
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
|
|
|
|
|
2021-06-03 10:35:46 +10:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Make Link Operator
|
|
|
|
|
* \{ */
|
2012-08-01 19:11:17 +00:00
|
|
|
|
2023-01-03 20:02:01 -05:00
|
|
|
/* Makes a link between selected output and input sockets. */
|
2012-08-01 19:11:17 +00:00
|
|
|
static int node_make_link_exec(bContext *C, wmOperator *op)
|
|
|
|
|
{
|
2021-12-03 16:25:17 -05:00
|
|
|
Main &bmain = *CTX_data_main(C);
|
|
|
|
|
SpaceNode &snode = *CTX_wm_space_node(C);
|
2022-12-20 17:14:34 -06:00
|
|
|
bNodeTree &node_tree = *snode.edittree;
|
2014-02-03 18:55:59 +11:00
|
|
|
const bool replace = RNA_boolean_get(op->ptr, "replace");
|
2012-08-01 19:11:17 +00:00
|
|
|
|
2021-12-03 16:25:17 -05:00
|
|
|
ED_preview_kill_jobs(CTX_wm_manager(C), &bmain);
|
2012-08-01 19:11:17 +00:00
|
|
|
|
2022-02-07 17:56:25 +01:00
|
|
|
snode_autoconnect(snode, true, replace);
|
2012-08-01 19:11:17 +00:00
|
|
|
|
2023-01-03 20:02:01 -05:00
|
|
|
/* Deselect sockets after linking. */
|
2022-12-20 17:14:34 -06:00
|
|
|
node_deselect_all_input_sockets(node_tree, false);
|
|
|
|
|
node_deselect_all_output_sockets(node_tree, false);
|
2012-08-01 19:11:17 +00:00
|
|
|
|
2022-12-20 17:14:34 -06:00
|
|
|
ED_node_tree_propagate_change(C, &bmain, &node_tree);
|
2012-08-01 19:11:17 +00:00
|
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void NODE_OT_link_make(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
/* identifiers */
|
|
|
|
|
ot->name = "Make Links";
|
|
|
|
|
ot->description = "Makes a link between selected output in input sockets";
|
|
|
|
|
ot->idname = "NODE_OT_link_make";
|
|
|
|
|
|
|
|
|
|
/* callbacks */
|
|
|
|
|
ot->exec = node_make_link_exec;
|
2019-04-22 00:18:34 +10:00
|
|
|
/* XXX we need a special poll which checks that there are selected input/output sockets. */
|
|
|
|
|
ot->poll = ED_operator_node_editable;
|
2012-08-01 19:11:17 +00:00
|
|
|
|
|
|
|
|
/* flags */
|
|
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
|
|
|
|
|
|
|
|
RNA_def_boolean(
|
2021-06-02 17:19:36 +02:00
|
|
|
ot->srna, "replace", false, "Replace", "Replace socket connections with the new links");
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
|
|
|
|
|
2021-06-03 10:35:46 +10:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Cut Link Operator
|
|
|
|
|
* \{ */
|
|
|
|
|
|
2012-08-01 19:11:17 +00:00
|
|
|
static int cut_links_exec(bContext *C, wmOperator *op)
|
|
|
|
|
{
|
2021-12-03 16:25:17 -05:00
|
|
|
Main &bmain = *CTX_data_main(C);
|
|
|
|
|
SpaceNode &snode = *CTX_wm_space_node(C);
|
2022-09-03 14:52:27 -05:00
|
|
|
const ARegion ®ion = *CTX_wm_region(C);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-09-03 14:52:27 -05:00
|
|
|
Vector<float2> path;
|
2013-08-24 10:05:29 +00:00
|
|
|
RNA_BEGIN (op->ptr, itemptr, "path") {
|
2022-09-03 14:52:27 -05:00
|
|
|
float2 loc_region;
|
|
|
|
|
RNA_float_get_array(&itemptr, "loc", loc_region);
|
|
|
|
|
float2 loc_view;
|
|
|
|
|
UI_view2d_region_to_view(®ion.v2d, loc_region.x, loc_region.y, &loc_view.x, &loc_view.y);
|
|
|
|
|
path.append(loc_view);
|
|
|
|
|
if (path.size() >= 256) {
|
2019-03-26 21:16:47 +11:00
|
|
|
break;
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2019-03-26 21:16:47 +11:00
|
|
|
}
|
2012-08-01 19:11:17 +00:00
|
|
|
RNA_END;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-09-03 14:52:27 -05:00
|
|
|
if (path.is_empty()) {
|
2022-09-02 16:20:01 -05:00
|
|
|
return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-09-02 16:20:01 -05:00
|
|
|
bool found = false;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-09-02 16:20:01 -05:00
|
|
|
ED_preview_kill_jobs(CTX_wm_manager(C), &bmain);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-09-02 17:37:10 -05:00
|
|
|
bNodeTree &node_tree = *snode.edittree;
|
2023-01-06 09:26:48 -05:00
|
|
|
node_tree.ensure_topology_cache();
|
2022-09-02 17:37:10 -05:00
|
|
|
|
2023-01-06 09:26:48 -05:00
|
|
|
Set<bNodeLink *> links_to_remove;
|
|
|
|
|
LISTBASE_FOREACH (bNodeLink *, link, &node_tree.links) {
|
2023-03-19 07:03:01 +01:00
|
|
|
if (node_link_is_hidden_or_dimmed(region.v2d, *link)) {
|
2022-09-02 16:20:01 -05:00
|
|
|
continue;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-03-19 07:03:01 +01:00
|
|
|
if (link_path_intersection(*link, path)) {
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-09-02 16:20:01 -05:00
|
|
|
if (!found) {
|
|
|
|
|
/* TODO(sergey): Why did we kill jobs twice? */
|
|
|
|
|
ED_preview_kill_jobs(CTX_wm_manager(C), &bmain);
|
|
|
|
|
found = true;
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
2023-01-06 09:26:48 -05:00
|
|
|
links_to_remove.add(link);
|
2012-08-03 23:44:19 +00:00
|
|
|
}
|
2022-09-02 16:20:01 -05:00
|
|
|
}
|
2020-07-03 17:20:08 +02:00
|
|
|
|
2023-01-06 09:26:48 -05:00
|
|
|
Set<bNode *> affected_nodes;
|
|
|
|
|
for (bNodeLink *link : links_to_remove) {
|
|
|
|
|
bNode *to_node = link->tonode;
|
|
|
|
|
nodeRemLink(snode.edittree, link);
|
|
|
|
|
affected_nodes.add(to_node);
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-02 17:37:10 -05:00
|
|
|
node_tree.ensure_topology_cache();
|
|
|
|
|
for (bNode *node : affected_nodes) {
|
2022-09-03 23:22:12 -05:00
|
|
|
update_multi_input_indices_for_removed_links(*node);
|
2022-09-02 17:37:10 -05:00
|
|
|
}
|
|
|
|
|
|
2022-09-02 16:20:01 -05:00
|
|
|
ED_node_tree_propagate_change(C, CTX_data_main(C), snode.edittree);
|
|
|
|
|
if (found) {
|
|
|
|
|
return OPERATOR_FINISHED;
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-09-02 16:20:01 -05:00
|
|
|
return OPERATOR_CANCELLED;
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void NODE_OT_links_cut(wmOperatorType *ot)
|
|
|
|
|
{
|
2013-01-27 07:23:58 +00:00
|
|
|
ot->name = "Cut Links";
|
2012-08-01 19:11:17 +00:00
|
|
|
ot->idname = "NODE_OT_links_cut";
|
|
|
|
|
ot->description = "Use the mouse to cut (remove) some links";
|
|
|
|
|
|
|
|
|
|
ot->invoke = WM_gesture_lines_invoke;
|
|
|
|
|
ot->modal = WM_gesture_lines_modal;
|
|
|
|
|
ot->exec = cut_links_exec;
|
|
|
|
|
ot->cancel = WM_gesture_lines_cancel;
|
|
|
|
|
|
2013-06-05 19:06:33 +00:00
|
|
|
ot->poll = ED_operator_node_editable;
|
2012-08-01 19:11:17 +00:00
|
|
|
|
|
|
|
|
/* flags */
|
2021-09-17 12:09:26 +10:00
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_DEPENDS_ON_CURSOR;
|
2012-08-01 19:11:17 +00:00
|
|
|
|
2017-10-16 16:11:04 +11:00
|
|
|
/* properties */
|
|
|
|
|
PropertyRNA *prop;
|
|
|
|
|
prop = RNA_def_collection_runtime(ot->srna, "path", &RNA_OperatorMousePath, "Path", "");
|
2021-06-02 17:19:36 +02:00
|
|
|
RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE));
|
2017-10-16 16:11:04 +11:00
|
|
|
|
2012-08-01 19:11:17 +00:00
|
|
|
/* internal */
|
2019-09-26 14:31:48 +02:00
|
|
|
RNA_def_int(ot->srna, "cursor", WM_CURSOR_KNIFE, 0, INT_MAX, "Cursor", "", 0, INT_MAX);
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
|
|
|
|
|
2021-06-03 10:35:46 +10:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Mute Links Operator
|
|
|
|
|
* \{ */
|
2021-03-16 19:11:54 +00:00
|
|
|
|
2023-06-05 20:21:07 +02:00
|
|
|
bool all_links_muted(const bNodeSocket &socket)
|
2022-09-03 17:57:53 -05:00
|
|
|
{
|
|
|
|
|
for (const bNodeLink *link : socket.directly_linked_links()) {
|
|
|
|
|
if (!(link->flag & NODE_LINK_MUTED)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-16 19:11:54 +00:00
|
|
|
static int mute_links_exec(bContext *C, wmOperator *op)
|
|
|
|
|
{
|
2021-12-03 16:25:17 -05:00
|
|
|
Main &bmain = *CTX_data_main(C);
|
|
|
|
|
SpaceNode &snode = *CTX_wm_space_node(C);
|
2022-09-03 14:52:27 -05:00
|
|
|
const ARegion ®ion = *CTX_wm_region(C);
|
2022-09-03 17:57:53 -05:00
|
|
|
bNodeTree &ntree = *snode.edittree;
|
2021-03-16 19:11:54 +00:00
|
|
|
|
2022-09-03 14:52:27 -05:00
|
|
|
Vector<float2> path;
|
2021-03-16 19:11:54 +00:00
|
|
|
RNA_BEGIN (op->ptr, itemptr, "path") {
|
2022-09-03 14:52:27 -05:00
|
|
|
float2 loc_region;
|
|
|
|
|
RNA_float_get_array(&itemptr, "loc", loc_region);
|
|
|
|
|
float2 loc_view;
|
|
|
|
|
UI_view2d_region_to_view(®ion.v2d, loc_region.x, loc_region.y, &loc_view.x, &loc_view.y);
|
|
|
|
|
path.append(loc_view);
|
|
|
|
|
if (path.size() >= 256) {
|
2021-03-16 19:11:54 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
RNA_END;
|
|
|
|
|
|
2022-09-03 14:52:27 -05:00
|
|
|
if (path.is_empty()) {
|
2022-09-02 19:06:12 -05:00
|
|
|
return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
|
|
|
|
|
}
|
2021-03-16 19:11:54 +00:00
|
|
|
|
2022-09-02 19:06:12 -05:00
|
|
|
ED_preview_kill_jobs(CTX_wm_manager(C), &bmain);
|
|
|
|
|
|
2022-09-03 17:57:53 -05:00
|
|
|
ntree.ensure_topology_cache();
|
|
|
|
|
|
|
|
|
|
Set<bNodeLink *> affected_links;
|
|
|
|
|
LISTBASE_FOREACH (bNodeLink *, link, &ntree.links) {
|
2023-03-19 07:03:01 +01:00
|
|
|
if (node_link_is_hidden_or_dimmed(region.v2d, *link)) {
|
2022-09-02 19:06:12 -05:00
|
|
|
continue;
|
2021-03-16 19:11:54 +00:00
|
|
|
}
|
2023-03-19 07:03:01 +01:00
|
|
|
if (!link_path_intersection(*link, path)) {
|
2022-09-03 17:57:53 -05:00
|
|
|
continue;
|
2021-03-16 19:11:54 +00:00
|
|
|
}
|
2022-09-03 17:57:53 -05:00
|
|
|
affected_links.add(link);
|
2022-09-02 19:06:12 -05:00
|
|
|
}
|
2022-09-03 17:57:53 -05:00
|
|
|
|
|
|
|
|
if (affected_links.is_empty()) {
|
2022-09-02 19:06:12 -05:00
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
2021-03-16 19:11:54 +00:00
|
|
|
|
2022-09-03 17:57:53 -05:00
|
|
|
bke::node_tree_runtime::AllowUsingOutdatedInfo allow_outdated_info{ntree};
|
2021-03-16 19:11:54 +00:00
|
|
|
|
2022-09-03 17:57:53 -05:00
|
|
|
for (bNodeLink *link : affected_links) {
|
2023-05-15 15:14:22 +02:00
|
|
|
bke::nodeLinkSetMute(&ntree, link, !(link->flag & NODE_LINK_MUTED));
|
2022-09-03 17:57:53 -05:00
|
|
|
const bool muted = link->flag & NODE_LINK_MUTED;
|
2021-03-16 19:11:54 +00:00
|
|
|
|
2022-09-06 16:25:20 +10:00
|
|
|
/* Propagate mute status downstream past reroute nodes. */
|
2022-09-03 17:57:53 -05:00
|
|
|
if (link->tonode->is_reroute()) {
|
|
|
|
|
Stack<bNodeLink *> links;
|
2023-01-06 09:26:48 -05:00
|
|
|
links.push_multiple(link->tonode->output_socket(0).directly_linked_links());
|
2022-09-03 17:57:53 -05:00
|
|
|
while (!links.is_empty()) {
|
|
|
|
|
bNodeLink *link = links.pop();
|
2023-05-15 15:14:22 +02:00
|
|
|
bke::nodeLinkSetMute(&ntree, link, muted);
|
2022-09-03 17:57:53 -05:00
|
|
|
if (!link->tonode->is_reroute()) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2023-01-06 09:26:48 -05:00
|
|
|
links.push_multiple(link->tonode->output_socket(0).directly_linked_links());
|
2022-09-03 17:57:53 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/* Propagate mute status upstream past reroutes, but only if all outputs are muted. */
|
|
|
|
|
if (link->fromnode->is_reroute()) {
|
|
|
|
|
if (!muted || all_links_muted(*link->fromsock)) {
|
|
|
|
|
Stack<bNodeLink *> links;
|
2023-01-06 09:26:48 -05:00
|
|
|
links.push_multiple(link->fromnode->input_socket(0).directly_linked_links());
|
2022-09-03 17:57:53 -05:00
|
|
|
while (!links.is_empty()) {
|
|
|
|
|
bNodeLink *link = links.pop();
|
2023-05-15 15:14:22 +02:00
|
|
|
bke::nodeLinkSetMute(&ntree, link, muted);
|
2022-09-03 17:57:53 -05:00
|
|
|
if (!link->fromnode->is_reroute()) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (!muted || all_links_muted(*link->fromsock)) {
|
2023-01-06 09:26:48 -05:00
|
|
|
links.push_multiple(link->fromnode->input_socket(0).directly_linked_links());
|
2022-09-03 17:57:53 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-09-02 19:06:12 -05:00
|
|
|
}
|
2021-03-16 19:11:54 +00:00
|
|
|
}
|
|
|
|
|
|
2022-09-03 17:57:53 -05:00
|
|
|
ED_node_tree_propagate_change(C, CTX_data_main(C), &ntree);
|
2022-09-02 19:06:12 -05:00
|
|
|
return OPERATOR_FINISHED;
|
2021-03-16 19:11:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void NODE_OT_links_mute(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
ot->name = "Mute Links";
|
|
|
|
|
ot->idname = "NODE_OT_links_mute";
|
|
|
|
|
ot->description = "Use the mouse to mute links";
|
|
|
|
|
|
|
|
|
|
ot->invoke = WM_gesture_lines_invoke;
|
|
|
|
|
ot->modal = WM_gesture_lines_modal;
|
|
|
|
|
ot->exec = mute_links_exec;
|
|
|
|
|
ot->cancel = WM_gesture_lines_cancel;
|
|
|
|
|
|
|
|
|
|
ot->poll = ED_operator_node_editable;
|
|
|
|
|
|
|
|
|
|
/* flags */
|
2021-09-17 12:09:26 +10:00
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_DEPENDS_ON_CURSOR;
|
2021-03-16 19:11:54 +00:00
|
|
|
|
|
|
|
|
/* properties */
|
|
|
|
|
PropertyRNA *prop;
|
|
|
|
|
prop = RNA_def_collection_runtime(ot->srna, "path", &RNA_OperatorMousePath, "Path", "");
|
2021-06-02 17:19:36 +02:00
|
|
|
RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE));
|
2021-03-16 19:11:54 +00:00
|
|
|
|
|
|
|
|
/* internal */
|
|
|
|
|
RNA_def_int(ot->srna, "cursor", WM_CURSOR_MUTE, 0, INT_MAX, "Cursor", "", 0, INT_MAX);
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-03 10:35:46 +10:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Detach Links Operator
|
|
|
|
|
* \{ */
|
2012-08-01 19:11:17 +00:00
|
|
|
|
2022-10-03 17:37:25 -05:00
|
|
|
static int detach_links_exec(bContext *C, wmOperator * /*op*/)
|
2012-08-01 19:11:17 +00:00
|
|
|
{
|
2021-12-03 16:25:17 -05:00
|
|
|
SpaceNode &snode = *CTX_wm_space_node(C);
|
|
|
|
|
bNodeTree &ntree = *snode.edittree;
|
2012-08-01 19:11:17 +00:00
|
|
|
|
2014-11-18 15:51:31 +01:00
|
|
|
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
|
2012-08-01 19:11:17 +00:00
|
|
|
|
2022-12-02 11:12:51 -06:00
|
|
|
for (bNode *node : ntree.all_nodes()) {
|
2012-08-01 19:11:17 +00:00
|
|
|
if (node->flag & SELECT) {
|
2023-05-15 15:14:22 +02:00
|
|
|
bke::nodeInternalRelink(&ntree, node);
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-21 15:18:56 +01:00
|
|
|
ED_node_tree_propagate_change(C, CTX_data_main(C), &ntree);
|
2012-08-01 19:11:17 +00:00
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void NODE_OT_links_detach(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
ot->name = "Detach Links";
|
|
|
|
|
ot->idname = "NODE_OT_links_detach";
|
|
|
|
|
ot->description =
|
|
|
|
|
"Remove all links to selected nodes, and try to connect neighbor nodes together";
|
|
|
|
|
|
|
|
|
|
ot->exec = detach_links_exec;
|
2013-06-05 19:06:33 +00:00
|
|
|
ot->poll = ED_operator_node_editable;
|
2012-08-01 19:11:17 +00:00
|
|
|
|
|
|
|
|
/* flags */
|
|
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-03 10:35:46 +10:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Set Parent Operator
|
|
|
|
|
* \{ */
|
2012-08-01 19:11:17 +00:00
|
|
|
|
2022-10-03 17:37:25 -05:00
|
|
|
static int node_parent_set_exec(bContext *C, wmOperator * /*op*/)
|
2012-08-01 19:11:17 +00:00
|
|
|
{
|
2022-01-18 13:32:36 -06:00
|
|
|
SpaceNode &snode = *CTX_wm_space_node(C);
|
|
|
|
|
bNodeTree &ntree = *snode.edittree;
|
|
|
|
|
bNode *frame = nodeGetActive(&ntree);
|
2019-03-26 21:16:47 +11:00
|
|
|
if (!frame || frame->type != NODE_FRAME) {
|
2012-08-01 19:11:17 +00:00
|
|
|
return OPERATOR_CANCELLED;
|
2019-03-26 21:16:47 +11:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-12-02 11:12:51 -06:00
|
|
|
for (bNode *node : ntree.all_nodes()) {
|
2019-03-26 21:16:47 +11:00
|
|
|
if (node == frame) {
|
2012-08-01 19:11:17 +00:00
|
|
|
continue;
|
2019-03-26 21:16:47 +11:00
|
|
|
}
|
2012-08-01 19:11:17 +00:00
|
|
|
if (node->flag & NODE_SELECT) {
|
2022-11-18 11:20:13 +01:00
|
|
|
nodeDetachNode(&ntree, node);
|
|
|
|
|
nodeAttachNode(&ntree, node, frame);
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-10-10 10:57:51 +02:00
|
|
|
tree_draw_order_update(ntree);
|
2021-06-02 17:19:36 +02:00
|
|
|
WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-08-01 19:11:17 +00:00
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void NODE_OT_parent_set(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
/* identifiers */
|
|
|
|
|
ot->name = "Make Parent";
|
|
|
|
|
ot->description = "Attach selected nodes";
|
|
|
|
|
ot->idname = "NODE_OT_parent_set";
|
|
|
|
|
|
|
|
|
|
/* api callbacks */
|
|
|
|
|
ot->exec = node_parent_set_exec;
|
2013-06-05 19:06:33 +00:00
|
|
|
ot->poll = ED_operator_node_editable;
|
2012-08-01 19:11:17 +00:00
|
|
|
|
|
|
|
|
/* flags */
|
|
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-03 10:35:46 +10:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Join Nodes Operator
|
|
|
|
|
* \{ */
|
2012-08-01 19:11:17 +00:00
|
|
|
|
2022-12-02 14:14:14 -06:00
|
|
|
struct NodeJoinState {
|
|
|
|
|
bool done;
|
|
|
|
|
bool descendent;
|
|
|
|
|
};
|
2012-08-01 19:11:17 +00:00
|
|
|
|
2022-11-18 11:20:13 +01:00
|
|
|
static void node_join_attach_recursive(bNodeTree &ntree,
|
2022-12-02 14:14:14 -06:00
|
|
|
MutableSpan<NodeJoinState> join_states,
|
2022-11-18 11:20:13 +01:00
|
|
|
bNode *node,
|
2022-09-06 12:11:04 -05:00
|
|
|
bNode *frame,
|
2022-12-02 13:20:40 -06:00
|
|
|
const VectorSet<bNode *> &selected_nodes)
|
2012-08-01 19:11:17 +00:00
|
|
|
{
|
2022-12-11 20:23:18 -06:00
|
|
|
join_states[node->index()].done = true;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-08-01 19:11:17 +00:00
|
|
|
if (node == frame) {
|
2022-12-11 20:23:18 -06:00
|
|
|
join_states[node->index()].descendent = true;
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
|
|
|
|
else if (node->parent) {
|
|
|
|
|
/* call recursively */
|
2022-12-11 20:23:18 -06:00
|
|
|
if (!join_states[node->parent->index()].done) {
|
2022-12-02 14:14:14 -06:00
|
|
|
node_join_attach_recursive(ntree, join_states, node->parent, frame, selected_nodes);
|
2019-03-26 21:16:47 +11:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-08-01 19:11:17 +00:00
|
|
|
/* in any case: if the parent is a descendant, so is the child */
|
2022-12-11 20:23:18 -06:00
|
|
|
if (join_states[node->parent->index()].descendent) {
|
|
|
|
|
join_states[node->index()].descendent = true;
|
2019-03-26 21:16:47 +11:00
|
|
|
}
|
2022-09-06 12:11:04 -05:00
|
|
|
else if (selected_nodes.contains(node)) {
|
2018-09-27 15:35:22 +02:00
|
|
|
/* if parent is not an descendant of the frame, reattach the node */
|
2022-11-18 11:20:13 +01:00
|
|
|
nodeDetachNode(&ntree, node);
|
|
|
|
|
nodeAttachNode(&ntree, node, frame);
|
2022-12-11 20:23:18 -06:00
|
|
|
join_states[node->index()].descendent = true;
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
|
|
|
|
}
|
2022-09-06 12:11:04 -05:00
|
|
|
else if (selected_nodes.contains(node)) {
|
2022-11-18 11:20:13 +01:00
|
|
|
nodeAttachNode(&ntree, node, frame);
|
2022-12-11 20:23:18 -06:00
|
|
|
join_states[node->index()].descendent = true;
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-03 17:37:25 -05:00
|
|
|
static int node_join_exec(bContext *C, wmOperator * /*op*/)
|
2012-08-01 19:11:17 +00:00
|
|
|
{
|
2022-09-06 12:11:04 -05:00
|
|
|
Main &bmain = *CTX_data_main(C);
|
2021-12-03 16:25:17 -05:00
|
|
|
SpaceNode &snode = *CTX_wm_space_node(C);
|
|
|
|
|
bNodeTree &ntree = *snode.edittree;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-12-02 13:20:40 -06:00
|
|
|
const VectorSet<bNode *> selected_nodes = get_selected_nodes(ntree);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-09-06 12:11:04 -05:00
|
|
|
bNode *frame_node = nodeAddStaticNode(C, &ntree, NODE_FRAME);
|
2022-11-09 22:26:03 +01:00
|
|
|
nodeSetActive(&ntree, frame_node);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-12-08 15:12:10 -06:00
|
|
|
ntree.ensure_topology_cache();
|
|
|
|
|
|
2022-12-02 14:14:14 -06:00
|
|
|
Array<NodeJoinState> join_states(ntree.all_nodes().size(), NodeJoinState{false, false});
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-12-02 11:12:51 -06:00
|
|
|
for (bNode *node : ntree.all_nodes()) {
|
2022-12-11 20:23:18 -06:00
|
|
|
if (!join_states[node->index()].done) {
|
2022-12-02 14:14:14 -06:00
|
|
|
node_join_attach_recursive(ntree, join_states, node, frame_node, selected_nodes);
|
2019-03-26 21:16:47 +11:00
|
|
|
}
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-10-10 10:57:51 +02:00
|
|
|
tree_draw_order_update(ntree);
|
2022-09-06 12:11:04 -05:00
|
|
|
ED_node_tree_propagate_change(C, &bmain, snode.edittree);
|
2021-06-02 17:19:36 +02:00
|
|
|
WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-08-01 19:11:17 +00:00
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void NODE_OT_join(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
/* identifiers */
|
|
|
|
|
ot->name = "Join Nodes";
|
|
|
|
|
ot->description = "Attach selected nodes to a new common frame";
|
|
|
|
|
ot->idname = "NODE_OT_join";
|
|
|
|
|
|
|
|
|
|
/* api callbacks */
|
|
|
|
|
ot->exec = node_join_exec;
|
2013-06-05 19:06:33 +00:00
|
|
|
ot->poll = ED_operator_node_editable;
|
2012-08-01 19:11:17 +00:00
|
|
|
|
|
|
|
|
/* flags */
|
|
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-03 10:35:46 +10:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Attach Operator
|
|
|
|
|
* \{ */
|
2012-08-01 19:11:17 +00:00
|
|
|
|
2023-10-10 10:57:51 +02:00
|
|
|
static bNode *node_find_frame_to_attach(ARegion ®ion, bNodeTree &ntree, const int2 mouse_xy)
|
2012-08-01 19:11:17 +00:00
|
|
|
{
|
2013-09-05 13:03:03 +00:00
|
|
|
/* convert mouse coordinates to v2d space */
|
2022-09-02 14:09:32 -05:00
|
|
|
float2 cursor;
|
|
|
|
|
UI_view2d_region_to_view(®ion.v2d, mouse_xy.x, mouse_xy.y, &cursor.x, &cursor.y);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-10-10 10:57:51 +02:00
|
|
|
for (bNode *frame : tree_draw_order_calc_nodes_reversed(ntree)) {
|
2012-08-01 19:11:17 +00:00
|
|
|
/* skip selected, those are the nodes we want to attach */
|
2019-03-26 21:16:47 +11:00
|
|
|
if ((frame->type != NODE_FRAME) || (frame->flag & NODE_SELECT)) {
|
2012-08-01 19:11:17 +00:00
|
|
|
continue;
|
2019-03-26 21:16:47 +11:00
|
|
|
}
|
2022-11-18 12:46:20 +01:00
|
|
|
if (BLI_rctf_isect_pt_v(&frame->runtime->totr, cursor)) {
|
2015-08-01 17:39:48 +02:00
|
|
|
return frame;
|
2019-03-26 21:16:47 +11:00
|
|
|
}
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-06-02 17:19:36 +02:00
|
|
|
return nullptr;
|
2015-08-01 17:39:48 +02:00
|
|
|
}
|
|
|
|
|
|
2022-10-03 17:37:25 -05:00
|
|
|
static int node_attach_invoke(bContext *C, wmOperator * /*op*/, const wmEvent *event)
|
2015-08-01 17:39:48 +02:00
|
|
|
{
|
2022-01-18 13:32:36 -06:00
|
|
|
ARegion ®ion = *CTX_wm_region(C);
|
|
|
|
|
SpaceNode &snode = *CTX_wm_space_node(C);
|
|
|
|
|
bNodeTree &ntree = *snode.edittree;
|
2020-03-06 16:56:42 +01:00
|
|
|
bNode *frame = node_find_frame_to_attach(region, ntree, event->mval);
|
2022-09-12 18:21:04 -05:00
|
|
|
if (frame == nullptr) {
|
|
|
|
|
/* Return "finished" so that auto offset operator macros can work. */
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-10-10 10:57:51 +02:00
|
|
|
for (bNode *node : tree_draw_order_calc_nodes_reversed(*snode.edittree)) {
|
2022-10-11 16:50:04 -05:00
|
|
|
if (!(node->flag & NODE_SELECT)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-03 22:38:49 +01:00
|
|
|
/* Disallow moving a parent into its child. */
|
|
|
|
|
if (node->is_frame() && nodeIsParentAndChild(node, frame)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-11 16:50:04 -05:00
|
|
|
if (node->parent == nullptr) {
|
2023-03-03 22:38:49 +01:00
|
|
|
nodeAttachNode(&ntree, node, frame);
|
|
|
|
|
continue;
|
2022-10-11 16:50:04 -05:00
|
|
|
}
|
2022-09-09 11:50:21 +02:00
|
|
|
|
2023-03-03 22:38:49 +01:00
|
|
|
/* Attach nodes which share parent with the frame. */
|
|
|
|
|
const bool share_parent = nodeIsParentAndChild(node->parent, frame);
|
|
|
|
|
if (!share_parent) {
|
|
|
|
|
continue;
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
2023-03-03 22:38:49 +01:00
|
|
|
|
|
|
|
|
nodeDetachNode(&ntree, node);
|
|
|
|
|
nodeAttachNode(&ntree, node, frame);
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-10-10 10:57:51 +02:00
|
|
|
tree_draw_order_update(ntree);
|
2021-06-02 17:19:36 +02:00
|
|
|
WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-08-01 19:11:17 +00:00
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void NODE_OT_attach(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
/* identifiers */
|
|
|
|
|
ot->name = "Attach Nodes";
|
|
|
|
|
ot->description = "Attach active node to a frame";
|
|
|
|
|
ot->idname = "NODE_OT_attach";
|
|
|
|
|
|
|
|
|
|
/* api callbacks */
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2012-08-01 19:11:17 +00:00
|
|
|
ot->invoke = node_attach_invoke;
|
2013-06-05 19:06:33 +00:00
|
|
|
ot->poll = ED_operator_node_editable;
|
2012-08-01 19:11:17 +00:00
|
|
|
|
|
|
|
|
/* flags */
|
|
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-03 10:35:46 +10:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Detach Operator
|
|
|
|
|
* \{ */
|
2012-08-01 19:11:17 +00:00
|
|
|
|
2022-12-02 14:14:14 -06:00
|
|
|
struct NodeDetachstate {
|
|
|
|
|
bool done;
|
|
|
|
|
bool descendent;
|
|
|
|
|
};
|
2012-08-01 19:11:17 +00:00
|
|
|
|
2022-12-02 14:14:14 -06:00
|
|
|
static void node_detach_recursive(bNodeTree &ntree,
|
|
|
|
|
MutableSpan<NodeDetachstate> detach_states,
|
|
|
|
|
bNode *node)
|
2012-08-01 19:11:17 +00:00
|
|
|
{
|
2022-12-11 20:23:18 -06:00
|
|
|
detach_states[node->index()].done = true;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-08-01 19:11:17 +00:00
|
|
|
if (node->parent) {
|
2023-01-03 20:02:01 -05:00
|
|
|
/* Call recursively. */
|
2022-12-11 20:23:18 -06:00
|
|
|
if (!detach_states[node->parent->index()].done) {
|
2022-12-02 14:14:14 -06:00
|
|
|
node_detach_recursive(ntree, detach_states, node->parent);
|
2019-03-26 21:16:47 +11:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-01-03 20:02:01 -05:00
|
|
|
/* In any case: if the parent is a descendant, so is the child. */
|
2022-12-11 20:23:18 -06:00
|
|
|
if (detach_states[node->parent->index()].descendent) {
|
|
|
|
|
detach_states[node->index()].descendent = true;
|
2019-03-26 21:16:47 +11:00
|
|
|
}
|
2012-08-01 19:11:17 +00:00
|
|
|
else if (node->flag & NODE_SELECT) {
|
2023-01-03 20:02:01 -05:00
|
|
|
/* If parent is not a descendant of a selected node, detach. */
|
2022-11-18 11:20:13 +01:00
|
|
|
nodeDetachNode(&ntree, node);
|
2022-12-11 20:23:18 -06:00
|
|
|
detach_states[node->index()].descendent = true;
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (node->flag & NODE_SELECT) {
|
2022-12-11 20:23:18 -06:00
|
|
|
detach_states[node->index()].descendent = true;
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-03 20:02:01 -05:00
|
|
|
/* Detach the root nodes in the current selection. */
|
2022-10-03 17:37:25 -05:00
|
|
|
static int node_detach_exec(bContext *C, wmOperator * /*op*/)
|
2012-08-01 19:11:17 +00:00
|
|
|
{
|
2022-01-18 13:32:36 -06:00
|
|
|
SpaceNode &snode = *CTX_wm_space_node(C);
|
|
|
|
|
bNodeTree &ntree = *snode.edittree;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-12-02 14:14:14 -06:00
|
|
|
Array<NodeDetachstate> detach_states(ntree.all_nodes().size(), NodeDetachstate{false, false});
|
|
|
|
|
|
2023-01-03 20:02:01 -05:00
|
|
|
/* Detach nodes recursively. Relative order is preserved here. */
|
2022-12-02 11:12:51 -06:00
|
|
|
for (bNode *node : ntree.all_nodes()) {
|
2022-12-11 20:23:18 -06:00
|
|
|
if (!detach_states[node->index()].done) {
|
2022-12-02 14:14:14 -06:00
|
|
|
node_detach_recursive(ntree, detach_states, node);
|
2019-03-26 21:16:47 +11:00
|
|
|
}
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-10-10 10:57:51 +02:00
|
|
|
tree_draw_order_update(ntree);
|
2021-06-02 17:19:36 +02:00
|
|
|
WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-08-01 19:11:17 +00:00
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void NODE_OT_detach(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
/* identifiers */
|
|
|
|
|
ot->name = "Detach Nodes";
|
|
|
|
|
ot->description = "Detach selected nodes from parents";
|
|
|
|
|
ot->idname = "NODE_OT_detach";
|
|
|
|
|
|
|
|
|
|
/* api callbacks */
|
|
|
|
|
ot->exec = node_detach_exec;
|
2013-06-05 19:06:33 +00:00
|
|
|
ot->poll = ED_operator_node_editable;
|
2012-08-01 19:11:17 +00:00
|
|
|
|
|
|
|
|
/* flags */
|
|
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-03 10:35:46 +10:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Automatic Node Insert on Dragging
|
|
|
|
|
* \{ */
|
2012-08-01 19:11:17 +00:00
|
|
|
|
2022-10-11 20:07:53 -05:00
|
|
|
static bNode *get_selected_node_for_insertion(bNodeTree &node_tree)
|
2012-08-01 19:11:17 +00:00
|
|
|
{
|
2022-10-11 20:07:53 -05:00
|
|
|
bNode *selected_node = nullptr;
|
|
|
|
|
int selected_node_count = 0;
|
|
|
|
|
for (bNode *node : node_tree.all_nodes()) {
|
2012-08-01 19:11:17 +00:00
|
|
|
if (node->flag & SELECT) {
|
2022-10-11 20:07:53 -05:00
|
|
|
selected_node = node;
|
|
|
|
|
selected_node_count++;
|
|
|
|
|
}
|
|
|
|
|
if (selected_node_count > 1) {
|
|
|
|
|
return nullptr;
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
|
|
|
|
}
|
2022-10-11 20:07:53 -05:00
|
|
|
if (!selected_node) {
|
|
|
|
|
return nullptr;
|
2019-03-26 21:16:47 +11:00
|
|
|
}
|
2022-10-11 20:07:53 -05:00
|
|
|
if (selected_node->input_sockets().is_empty() || selected_node->output_sockets().is_empty()) {
|
|
|
|
|
return nullptr;
|
2019-03-26 21:16:47 +11:00
|
|
|
}
|
2022-10-11 20:07:53 -05:00
|
|
|
if (std::any_of(selected_node->input_sockets().begin(),
|
|
|
|
|
selected_node->input_sockets().end(),
|
|
|
|
|
[&](const bNodeSocket *socket) { return socket->is_directly_linked(); }))
|
|
|
|
|
{
|
|
|
|
|
return nullptr;
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
2022-10-11 20:07:53 -05:00
|
|
|
if (std::any_of(selected_node->output_sockets().begin(),
|
|
|
|
|
selected_node->output_sockets().end(),
|
|
|
|
|
[&](const bNodeSocket *socket) { return socket->is_directly_linked(); }))
|
|
|
|
|
{
|
|
|
|
|
return nullptr;
|
|
|
|
|
};
|
|
|
|
|
return selected_node;
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
|
|
|
|
|
2022-10-11 20:07:53 -05:00
|
|
|
void node_insert_on_link_flags_set(SpaceNode &snode, const ARegion ®ion)
|
2012-08-01 19:11:17 +00:00
|
|
|
{
|
2022-10-11 20:07:53 -05:00
|
|
|
bNodeTree &node_tree = *snode.edittree;
|
|
|
|
|
node_tree.ensure_topology_cache();
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-10-11 20:07:53 -05:00
|
|
|
node_insert_on_link_flags_clear(node_tree);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-10-11 20:07:53 -05:00
|
|
|
bNode *node_to_insert = get_selected_node_for_insertion(node_tree);
|
|
|
|
|
if (!node_to_insert) {
|
2019-03-26 21:16:47 +11:00
|
|
|
return;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-01-03 20:02:01 -05:00
|
|
|
/* Find link to select/highlight. */
|
2021-06-02 17:19:36 +02:00
|
|
|
bNodeLink *selink = nullptr;
|
2020-10-16 18:06:30 +02:00
|
|
|
float dist_best = FLT_MAX;
|
2022-10-11 20:07:53 -05:00
|
|
|
LISTBASE_FOREACH (bNodeLink *, link, &node_tree.links) {
|
2023-03-19 07:03:01 +01:00
|
|
|
if (node_link_is_hidden_or_dimmed(region.v2d, *link)) {
|
2013-03-18 16:34:57 +00:00
|
|
|
continue;
|
2019-03-26 21:16:47 +11:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-09-02 15:43:53 -05:00
|
|
|
std::array<float2, NODE_LINK_RESOL + 1> coords;
|
2023-03-19 07:03:01 +01:00
|
|
|
node_link_bezier_points_evaluated(*link, coords);
|
2022-09-02 15:43:53 -05:00
|
|
|
float dist = FLT_MAX;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-01-03 20:02:01 -05:00
|
|
|
/* Loop over link coords to find shortest dist to upper left node edge of a intersected line
|
|
|
|
|
* segment. */
|
2022-09-02 15:43:53 -05:00
|
|
|
for (int i = 0; i < NODE_LINK_RESOL; i++) {
|
|
|
|
|
/* Check if the node rectangle intersects the line from this point to next one. */
|
2022-11-18 12:46:20 +01:00
|
|
|
if (BLI_rctf_isect_segment(&node_to_insert->runtime->totr, coords[i], coords[i + 1])) {
|
2023-01-03 20:02:01 -05:00
|
|
|
/* Store the shortest distance to the upper left edge of all intersections found so far. */
|
2022-11-18 12:46:20 +01:00
|
|
|
const float node_xy[] = {node_to_insert->runtime->totr.xmin,
|
|
|
|
|
node_to_insert->runtime->totr.ymax};
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-01-03 20:02:01 -05:00
|
|
|
/* To be precise coords should be clipped by `select->totr`, but not done since there's no
|
|
|
|
|
* real noticeable difference. */
|
2022-09-02 15:43:53 -05:00
|
|
|
dist = min_ff(dist_squared_to_line_segment_v2(node_xy, coords[i], coords[i + 1]), dist);
|
2015-08-04 15:25:19 +02:00
|
|
|
}
|
2022-09-02 15:43:53 -05:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-01-03 20:02:01 -05:00
|
|
|
/* We want the link with the shortest distance to node center. */
|
2022-09-02 15:43:53 -05:00
|
|
|
if (dist < dist_best) {
|
|
|
|
|
dist_best = dist;
|
|
|
|
|
selink = link;
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-03-26 21:16:47 +11:00
|
|
|
if (selink) {
|
2012-08-01 19:11:17 +00:00
|
|
|
selink->flag |= NODE_LINKFLAG_HILITE;
|
2019-03-26 21:16:47 +11:00
|
|
|
}
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
|
|
|
|
|
2022-10-11 20:07:53 -05:00
|
|
|
void node_insert_on_link_flags_clear(bNodeTree &node_tree)
|
|
|
|
|
{
|
|
|
|
|
LISTBASE_FOREACH (bNodeLink *, link, &node_tree.links) {
|
|
|
|
|
link->flag &= ~NODE_LINKFLAG_HILITE;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-06-03 10:35:46 +10:00
|
|
|
|
2022-10-11 20:07:53 -05:00
|
|
|
void node_insert_on_link_flags(Main &bmain, SpaceNode &snode)
|
|
|
|
|
{
|
|
|
|
|
bNodeTree &node_tree = *snode.edittree;
|
|
|
|
|
node_tree.ensure_topology_cache();
|
|
|
|
|
bNode *node_to_insert = get_selected_node_for_insertion(node_tree);
|
|
|
|
|
if (!node_to_insert) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Find link to insert on. */
|
|
|
|
|
bNodeTree &ntree = *snode.edittree;
|
|
|
|
|
bNodeLink *old_link = nullptr;
|
|
|
|
|
LISTBASE_FOREACH (bNodeLink *, link, &ntree.links) {
|
|
|
|
|
if (link->flag & NODE_LINKFLAG_HILITE) {
|
|
|
|
|
old_link = link;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (old_link == nullptr) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
old_link->flag &= ~NODE_LINKFLAG_HILITE;
|
|
|
|
|
|
|
|
|
|
bNodeSocket *best_input = get_main_socket(ntree, *node_to_insert, SOCK_IN);
|
|
|
|
|
bNodeSocket *best_output = get_main_socket(ntree, *node_to_insert, SOCK_OUT);
|
|
|
|
|
|
|
|
|
|
if (node_to_insert->type != NODE_REROUTE) {
|
|
|
|
|
/* Ignore main sockets when the types don't match. */
|
|
|
|
|
if (best_input != nullptr && ntree.typeinfo->validate_link != nullptr &&
|
|
|
|
|
!ntree.typeinfo->validate_link(static_cast<eNodeSocketDatatype>(old_link->fromsock->type),
|
|
|
|
|
static_cast<eNodeSocketDatatype>(best_input->type)))
|
|
|
|
|
{
|
|
|
|
|
best_input = nullptr;
|
|
|
|
|
}
|
|
|
|
|
if (best_output != nullptr && ntree.typeinfo->validate_link != nullptr &&
|
|
|
|
|
!ntree.typeinfo->validate_link(static_cast<eNodeSocketDatatype>(best_output->type),
|
|
|
|
|
static_cast<eNodeSocketDatatype>(old_link->tosock->type)))
|
|
|
|
|
{
|
|
|
|
|
best_output = nullptr;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bNode *from_node = old_link->fromnode;
|
|
|
|
|
bNodeSocket *from_socket = old_link->fromsock;
|
|
|
|
|
bNode *to_node = old_link->tonode;
|
|
|
|
|
|
|
|
|
|
if (best_output != nullptr) {
|
|
|
|
|
/* Relink the "start" of the existing link to the newly inserted node. */
|
|
|
|
|
old_link->fromnode = node_to_insert;
|
|
|
|
|
old_link->fromsock = best_output;
|
|
|
|
|
BKE_ntree_update_tag_link_changed(&ntree);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
nodeRemLink(&ntree, old_link);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (best_input != nullptr) {
|
|
|
|
|
/* Add a new link that connects the node on the left to the newly inserted node. */
|
|
|
|
|
nodeAddLink(&ntree, from_node, from_socket, node_to_insert, best_input);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Set up insert offset data, it needs stuff from here. */
|
2023-08-29 01:18:33 +02:00
|
|
|
if (U.uiflag & USER_NODE_AUTO_OFFSET) {
|
2022-10-11 20:07:53 -05:00
|
|
|
BLI_assert(snode.runtime->iofsd == nullptr);
|
|
|
|
|
NodeInsertOfsData *iofsd = MEM_cnew<NodeInsertOfsData>(__func__);
|
|
|
|
|
|
|
|
|
|
iofsd->insert = node_to_insert;
|
|
|
|
|
iofsd->prev = from_node;
|
|
|
|
|
iofsd->next = to_node;
|
|
|
|
|
|
|
|
|
|
snode.runtime->iofsd = iofsd;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ED_node_tree_propagate_change(nullptr, &bmain, &ntree);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** \} */
|
2022-03-15 17:29:54 +11:00
|
|
|
|
2021-06-03 10:35:46 +10:00
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Node Insert Offset Operator
|
|
|
|
|
* \{ */
|
|
|
|
|
|
2021-05-14 16:01:55 +02:00
|
|
|
static int get_main_socket_priority(const bNodeSocket *socket)
|
|
|
|
|
{
|
|
|
|
|
switch ((eNodeSocketDatatype)socket->type) {
|
2021-11-29 12:34:11 +02:00
|
|
|
case SOCK_CUSTOM:
|
2021-05-14 16:01:55 +02:00
|
|
|
return 0;
|
2021-11-29 12:34:11 +02:00
|
|
|
case SOCK_BOOLEAN:
|
2021-05-14 16:01:55 +02:00
|
|
|
return 1;
|
2021-11-29 12:34:11 +02:00
|
|
|
case SOCK_INT:
|
2021-05-14 16:01:55 +02:00
|
|
|
return 2;
|
2021-11-29 12:34:11 +02:00
|
|
|
case SOCK_FLOAT:
|
2021-05-14 16:01:55 +02:00
|
|
|
return 3;
|
2021-11-29 12:34:11 +02:00
|
|
|
case SOCK_VECTOR:
|
2021-05-14 16:01:55 +02:00
|
|
|
return 4;
|
2021-11-29 12:34:11 +02:00
|
|
|
case SOCK_RGBA:
|
|
|
|
|
return 5;
|
2021-05-14 16:01:55 +02:00
|
|
|
case SOCK_STRING:
|
|
|
|
|
case SOCK_SHADER:
|
|
|
|
|
case SOCK_OBJECT:
|
|
|
|
|
case SOCK_IMAGE:
|
2023-06-14 20:30:10 +02:00
|
|
|
case SOCK_ROTATION:
|
2021-05-14 16:01:55 +02:00
|
|
|
case SOCK_GEOMETRY:
|
|
|
|
|
case SOCK_COLLECTION:
|
|
|
|
|
case SOCK_TEXTURE:
|
|
|
|
|
case SOCK_MATERIAL:
|
2021-11-29 12:34:11 +02:00
|
|
|
return 6;
|
2021-05-14 16:01:55 +02:00
|
|
|
}
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-04 14:44:21 +01:00
|
|
|
/** Get the "main" socket based on the node declaration or an heuristic. */
|
2022-10-11 20:07:53 -05:00
|
|
|
bNodeSocket *get_main_socket(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out)
|
2021-05-14 16:01:55 +02:00
|
|
|
{
|
2021-11-04 14:44:21 +01:00
|
|
|
ListBase *sockets = (in_out == SOCK_IN) ? &node.inputs : &node.outputs;
|
|
|
|
|
|
|
|
|
|
/* Try to get the main socket based on the socket declaration. */
|
2023-05-15 15:14:22 +02:00
|
|
|
bke::nodeDeclarationEnsure(&ntree, &node);
|
2022-08-31 12:15:57 +02:00
|
|
|
const nodes::NodeDeclaration *node_decl = node.declaration();
|
2021-11-04 14:44:21 +01:00
|
|
|
if (node_decl != nullptr) {
|
2023-08-30 12:37:21 +02:00
|
|
|
Span<nodes::SocketDeclaration *> socket_decls = (in_out == SOCK_IN) ? node_decl->inputs :
|
|
|
|
|
node_decl->outputs;
|
2021-11-04 14:44:21 +01:00
|
|
|
int index;
|
|
|
|
|
LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, sockets, index) {
|
2022-01-20 10:36:56 -06:00
|
|
|
const nodes::SocketDeclaration &socket_decl = *socket_decls[index];
|
2023-09-19 10:47:21 +02:00
|
|
|
if (!socket->is_visible()) {
|
2021-11-04 14:44:21 +01:00
|
|
|
continue;
|
|
|
|
|
}
|
2022-12-29 14:55:27 -05:00
|
|
|
if (socket_decl.is_default_link_socket) {
|
2021-11-04 14:44:21 +01:00
|
|
|
return socket;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-03 20:02:01 -05:00
|
|
|
/* Find priority range. */
|
2021-05-14 16:01:55 +02:00
|
|
|
int maxpriority = -1;
|
2020-10-16 18:06:30 +02:00
|
|
|
LISTBASE_FOREACH (bNodeSocket *, sock, sockets) {
|
2021-05-14 16:01:55 +02:00
|
|
|
if (sock->flag & SOCK_UNAVAIL) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
maxpriority = max_ii(get_main_socket_priority(sock), maxpriority);
|
2019-03-26 21:16:47 +11:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-01-03 20:02:01 -05:00
|
|
|
/* Try all priorities, starting from 'highest'. */
|
2021-05-14 16:01:55 +02:00
|
|
|
for (int priority = maxpriority; priority >= 0; priority--) {
|
2020-10-16 18:06:30 +02:00
|
|
|
LISTBASE_FOREACH (bNodeSocket *, sock, sockets) {
|
2023-09-19 10:47:21 +02:00
|
|
|
if (!!sock->is_visible() && priority == get_main_socket_priority(sock)) {
|
2012-08-01 19:11:17 +00:00
|
|
|
return sock;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-01-03 20:02:01 -05:00
|
|
|
/* No visible sockets, unhide first of highest priority. */
|
2021-05-14 16:01:55 +02:00
|
|
|
for (int priority = maxpriority; priority >= 0; priority--) {
|
2020-10-16 18:06:30 +02:00
|
|
|
LISTBASE_FOREACH (bNodeSocket *, sock, sockets) {
|
2021-05-14 16:01:55 +02:00
|
|
|
if (sock->flag & SOCK_UNAVAIL) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (priority == get_main_socket_priority(sock)) {
|
2012-08-01 19:11:17 +00:00
|
|
|
sock->flag &= ~SOCK_HIDDEN;
|
|
|
|
|
return sock;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-06-02 17:19:36 +02:00
|
|
|
return nullptr;
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
|
|
|
|
|
2022-10-03 17:37:25 -05:00
|
|
|
static bool node_parents_offset_flag_enable_cb(bNode *parent, void * /*userdata*/)
|
2015-08-01 17:39:48 +02:00
|
|
|
{
|
|
|
|
|
/* NODE_TEST is used to flag nodes that shouldn't be offset (again) */
|
|
|
|
|
parent->flag |= NODE_TEST;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-03 16:25:17 -05:00
|
|
|
static void node_offset_apply(bNode &node, const float offset_x)
|
2015-08-01 17:39:48 +02:00
|
|
|
{
|
|
|
|
|
/* NODE_TEST is used to flag nodes that shouldn't be offset (again) */
|
2021-12-03 16:25:17 -05:00
|
|
|
if ((node.flag & NODE_TEST) == 0) {
|
2023-03-17 04:19:05 +01:00
|
|
|
node.runtime->anim_ofsx = (offset_x / UI_SCALE_FAC);
|
2021-12-03 16:25:17 -05:00
|
|
|
node.flag |= NODE_TEST;
|
2015-08-01 17:39:48 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void node_parent_offset_apply(NodeInsertOfsData *data, bNode *parent, const float offset_x)
|
|
|
|
|
{
|
2021-12-03 16:25:17 -05:00
|
|
|
node_offset_apply(*parent, offset_x);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-04-14 10:41:21 +10:00
|
|
|
/* Flag all children as offset to prevent them from being offset
|
|
|
|
|
* separately (they've already moved with the parent). */
|
2022-12-02 11:12:51 -06:00
|
|
|
for (bNode *node : data->ntree->all_nodes()) {
|
2023-03-03 22:38:49 +01:00
|
|
|
if (nodeIsParentAndChild(parent, node)) {
|
2015-08-01 17:39:48 +02:00
|
|
|
/* NODE_TEST is used to flag nodes that shouldn't be offset (again) */
|
|
|
|
|
node->flag |= NODE_TEST;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define NODE_INSOFS_ANIM_DURATION 0.25f
|
|
|
|
|
|
|
|
|
|
/**
|
2021-06-03 10:46:16 +10:00
|
|
|
* Callback that applies #NodeInsertOfsData.offset_x to a node or its parent, similar
|
2015-08-01 17:39:48 +02:00
|
|
|
* to node_link_insert_offset_output_chain_cb below, but with slightly different logic
|
|
|
|
|
*/
|
|
|
|
|
static bool node_link_insert_offset_frame_chain_cb(bNode *fromnode,
|
|
|
|
|
bNode *tonode,
|
|
|
|
|
void *userdata,
|
|
|
|
|
const bool reversed)
|
|
|
|
|
{
|
2021-06-02 17:19:36 +02:00
|
|
|
NodeInsertOfsData *data = (NodeInsertOfsData *)userdata;
|
2015-08-01 17:39:48 +02:00
|
|
|
bNode *ofs_node = reversed ? fromnode : tonode;
|
|
|
|
|
|
|
|
|
|
if (ofs_node->parent && ofs_node->parent != data->insert_parent) {
|
2021-12-03 16:25:17 -05:00
|
|
|
node_offset_apply(*ofs_node->parent, data->offset_x);
|
2015-08-01 17:39:48 +02:00
|
|
|
}
|
|
|
|
|
else {
|
2021-12-03 16:25:17 -05:00
|
|
|
node_offset_apply(*ofs_node, data->offset_x);
|
2015-08-01 17:39:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2020-05-09 17:15:25 +10:00
|
|
|
* Applies #NodeInsertOfsData.offset_x to all children of \a parent.
|
2015-08-01 17:39:48 +02:00
|
|
|
*/
|
2022-12-02 11:12:51 -06:00
|
|
|
static void node_link_insert_offset_frame_chains(bNodeTree *ntree,
|
2015-08-01 17:39:48 +02:00
|
|
|
const bNode *parent,
|
|
|
|
|
NodeInsertOfsData *data,
|
|
|
|
|
const bool reversed)
|
|
|
|
|
{
|
2022-12-02 11:12:51 -06:00
|
|
|
for (bNode *node : ntree->all_nodes()) {
|
2023-03-03 22:38:49 +01:00
|
|
|
if (nodeIsParentAndChild(parent, node)) {
|
2023-05-15 15:14:22 +02:00
|
|
|
bke::nodeChainIter(ntree, node, node_link_insert_offset_frame_chain_cb, data, reversed);
|
2015-08-01 17:39:48 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Callback that applies NodeInsertOfsData.offset_x to a node or its parent,
|
2018-09-27 15:35:22 +02:00
|
|
|
* considering the logic needed for offsetting nodes after link insert
|
2015-08-01 17:39:48 +02:00
|
|
|
*/
|
|
|
|
|
static bool node_link_insert_offset_chain_cb(bNode *fromnode,
|
|
|
|
|
bNode *tonode,
|
|
|
|
|
void *userdata,
|
|
|
|
|
const bool reversed)
|
|
|
|
|
{
|
2021-06-02 17:19:36 +02:00
|
|
|
NodeInsertOfsData *data = (NodeInsertOfsData *)userdata;
|
2015-08-01 17:39:48 +02:00
|
|
|
bNode *ofs_node = reversed ? fromnode : tonode;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-08-01 17:39:48 +02:00
|
|
|
if (data->insert_parent) {
|
|
|
|
|
if (ofs_node->parent && (ofs_node->parent->flag & NODE_TEST) == 0) {
|
|
|
|
|
node_parent_offset_apply(data, ofs_node->parent, data->offset_x);
|
|
|
|
|
node_link_insert_offset_frame_chains(data->ntree, ofs_node->parent, data, reversed);
|
|
|
|
|
}
|
|
|
|
|
else {
|
2021-12-03 16:25:17 -05:00
|
|
|
node_offset_apply(*ofs_node, data->offset_x);
|
2015-08-01 17:39:48 +02:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-03-03 22:38:49 +01:00
|
|
|
if (!nodeIsParentAndChild(data->insert_parent, ofs_node)) {
|
2021-06-02 17:19:36 +02:00
|
|
|
data->insert_parent = nullptr;
|
2015-08-01 17:39:48 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (ofs_node->parent) {
|
2023-05-15 15:14:22 +02:00
|
|
|
bNode *node = bke::nodeFindRootParent(ofs_node);
|
2021-12-03 16:25:17 -05:00
|
|
|
node_offset_apply(*node, data->offset_x);
|
2015-08-01 17:39:48 +02:00
|
|
|
}
|
|
|
|
|
else {
|
2021-12-03 16:25:17 -05:00
|
|
|
node_offset_apply(*ofs_node, data->offset_x);
|
2015-08-01 17:39:48 +02:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-08-01 17:39:48 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void node_link_insert_offset_ntree(NodeInsertOfsData *iofsd,
|
2020-03-06 16:56:42 +01:00
|
|
|
ARegion *region,
|
2015-08-01 17:39:48 +02:00
|
|
|
const int mouse_xy[2],
|
|
|
|
|
const bool right_alignment)
|
|
|
|
|
{
|
|
|
|
|
bNodeTree *ntree = iofsd->ntree;
|
2021-12-03 16:25:17 -05:00
|
|
|
bNode &insert = *iofsd->insert;
|
2015-08-01 17:39:48 +02:00
|
|
|
bNode *prev = iofsd->prev, *next = iofsd->next;
|
2021-12-03 16:25:17 -05:00
|
|
|
bNode *init_parent = insert.parent; /* store old insert.parent for restoring later */
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-03-17 04:19:05 +01:00
|
|
|
const float min_margin = U.node_margin * UI_SCALE_FAC;
|
2015-08-01 17:39:48 +02:00
|
|
|
const float width = NODE_WIDTH(insert);
|
2022-11-18 12:46:20 +01:00
|
|
|
const bool needs_alignment = (next->runtime->totr.xmin - prev->runtime->totr.xmax) <
|
|
|
|
|
(width + (min_margin * 2.0f));
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-08-01 17:39:48 +02:00
|
|
|
float margin = width;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-08-01 17:39:48 +02:00
|
|
|
/* NODE_TEST will be used later, so disable for all nodes */
|
2023-05-15 15:14:22 +02:00
|
|
|
bke::ntreeNodeFlagSet(ntree, NODE_TEST, false);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-12-03 16:25:17 -05:00
|
|
|
/* `insert.totr` isn't updated yet,
|
2021-11-13 13:07:13 +11:00
|
|
|
* so `totr_insert` is used to get the correct world-space coords. */
|
2020-10-16 18:06:30 +02:00
|
|
|
rctf totr_insert;
|
2021-12-03 16:25:17 -05:00
|
|
|
node_to_updated_rect(insert, totr_insert);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-01-03 20:02:01 -05:00
|
|
|
/* Frame attachment wasn't handled yet so we search the frame that the node will be attached to
|
|
|
|
|
* later. */
|
2022-01-18 13:32:36 -06:00
|
|
|
insert.parent = node_find_frame_to_attach(*region, *ntree, mouse_xy);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-01-03 20:02:01 -05:00
|
|
|
/* This makes sure nodes are also correctly offset when inserting a node on top of a frame
|
2015-08-01 17:39:48 +02:00
|
|
|
* without actually making it a part of the frame (because mouse isn't intersecting it)
|
2023-01-03 20:02:01 -05:00
|
|
|
* - logic here is similar to node_find_frame_to_attach. */
|
2021-12-03 16:25:17 -05:00
|
|
|
if (!insert.parent ||
|
|
|
|
|
(prev->parent && (prev->parent == next->parent) && (prev->parent != insert.parent)))
|
|
|
|
|
{
|
2015-08-01 17:39:48 +02:00
|
|
|
rctf totr_frame;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-08-01 17:39:48 +02:00
|
|
|
/* check nodes front to back */
|
2023-10-10 10:57:51 +02:00
|
|
|
for (bNode *frame : tree_draw_order_calc_nodes_reversed(*ntree)) {
|
2015-08-01 17:39:48 +02:00
|
|
|
/* skip selected, those are the nodes we want to attach */
|
2019-03-26 21:16:47 +11:00
|
|
|
if ((frame->type != NODE_FRAME) || (frame->flag & NODE_SELECT)) {
|
2015-08-01 17:39:48 +02:00
|
|
|
continue;
|
2019-03-26 21:16:47 +11:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-08-01 17:39:48 +02:00
|
|
|
/* for some reason frame y coords aren't correct yet */
|
2021-12-03 16:25:17 -05:00
|
|
|
node_to_updated_rect(*frame, totr_frame);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-08-01 17:39:48 +02:00
|
|
|
if (BLI_rctf_isect_x(&totr_frame, totr_insert.xmin) &&
|
|
|
|
|
BLI_rctf_isect_x(&totr_frame, totr_insert.xmax))
|
|
|
|
|
{
|
|
|
|
|
if (BLI_rctf_isect_y(&totr_frame, totr_insert.ymin) ||
|
|
|
|
|
BLI_rctf_isect_y(&totr_frame, totr_insert.ymax))
|
|
|
|
|
{
|
2021-12-03 16:25:17 -05:00
|
|
|
/* frame isn't insert.parent actually, but this is needed to make offsetting
|
2015-08-01 17:39:48 +02:00
|
|
|
* nodes work correctly for above checked cases (it is restored later) */
|
2021-12-03 16:25:17 -05:00
|
|
|
insert.parent = frame;
|
2015-08-01 17:39:48 +02:00
|
|
|
break;
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
|
}
|
2015-08-01 17:39:48 +02:00
|
|
|
}
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-08-01 17:39:48 +02:00
|
|
|
/* *** ensure offset at the left (or right for right_alignment case) of insert_node *** */
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-11-18 12:46:20 +01:00
|
|
|
float dist = right_alignment ? totr_insert.xmin - prev->runtime->totr.xmax :
|
|
|
|
|
next->runtime->totr.xmin - totr_insert.xmax;
|
2015-08-01 17:39:48 +02:00
|
|
|
/* distance between insert_node and prev is smaller than min margin */
|
|
|
|
|
if (dist < min_margin) {
|
2020-10-16 18:06:30 +02:00
|
|
|
const float addval = (min_margin - dist) * (right_alignment ? 1.0f : -1.0f);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-08-01 17:39:48 +02:00
|
|
|
node_offset_apply(insert, addval);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-08-01 17:39:48 +02:00
|
|
|
totr_insert.xmin += addval;
|
|
|
|
|
totr_insert.xmax += addval;
|
|
|
|
|
margin += min_margin;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-08-01 17:39:48 +02:00
|
|
|
/* *** ensure offset at the right (or left for right_alignment case) of insert_node *** */
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-11-18 12:46:20 +01:00
|
|
|
dist = right_alignment ? next->runtime->totr.xmin - totr_insert.xmax :
|
|
|
|
|
totr_insert.xmin - prev->runtime->totr.xmax;
|
2015-08-01 17:39:48 +02:00
|
|
|
/* distance between insert_node and next is smaller than min margin */
|
|
|
|
|
if (dist < min_margin) {
|
2020-10-16 18:06:30 +02:00
|
|
|
const float addval = (min_margin - dist) * (right_alignment ? 1.0f : -1.0f);
|
2015-08-01 17:39:48 +02:00
|
|
|
if (needs_alignment) {
|
|
|
|
|
bNode *offs_node = right_alignment ? next : prev;
|
2021-12-03 16:25:17 -05:00
|
|
|
if (!offs_node->parent || offs_node->parent == insert.parent ||
|
2023-03-03 22:38:49 +01:00
|
|
|
nodeIsParentAndChild(offs_node->parent, &insert))
|
|
|
|
|
{
|
2021-12-03 16:25:17 -05:00
|
|
|
node_offset_apply(*offs_node, addval);
|
2015-08-01 17:39:48 +02:00
|
|
|
}
|
2021-12-03 16:25:17 -05:00
|
|
|
else if (!insert.parent && offs_node->parent) {
|
2023-05-15 15:14:22 +02:00
|
|
|
node_offset_apply(*bke::nodeFindRootParent(offs_node), addval);
|
2015-08-01 17:39:48 +02:00
|
|
|
}
|
|
|
|
|
margin = addval;
|
|
|
|
|
}
|
|
|
|
|
/* enough room is available, but we want to ensure the min margin at the right */
|
|
|
|
|
else {
|
|
|
|
|
/* offset inserted node so that min margin is kept at the right */
|
|
|
|
|
node_offset_apply(insert, -addval);
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2015-08-01 17:39:48 +02:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-08-01 17:39:48 +02:00
|
|
|
if (needs_alignment) {
|
2021-12-03 16:25:17 -05:00
|
|
|
iofsd->insert_parent = insert.parent;
|
2015-08-01 17:39:48 +02:00
|
|
|
iofsd->offset_x = margin;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-08-01 17:39:48 +02:00
|
|
|
/* flag all parents of insert as offset to prevent them from being offset */
|
2023-05-15 15:14:22 +02:00
|
|
|
bke::nodeParentsIter(&insert, node_parents_offset_flag_enable_cb, nullptr);
|
2015-08-01 17:39:48 +02:00
|
|
|
/* iterate over entire chain and apply offsets */
|
2023-05-15 15:14:22 +02:00
|
|
|
bke::nodeChainIter(ntree,
|
|
|
|
|
right_alignment ? next : prev,
|
|
|
|
|
node_link_insert_offset_chain_cb,
|
|
|
|
|
iofsd,
|
|
|
|
|
!right_alignment);
|
2015-08-01 17:39:48 +02:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-12-03 16:25:17 -05:00
|
|
|
insert.parent = init_parent;
|
2015-08-01 17:39:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Modal handler for insert offset animation
|
|
|
|
|
*/
|
2022-09-14 08:42:57 -03:00
|
|
|
static int node_insert_offset_modal(bContext *C, wmOperator *op, const wmEvent *event)
|
2015-08-01 17:39:48 +02:00
|
|
|
{
|
|
|
|
|
SpaceNode *snode = CTX_wm_space_node(C);
|
2022-09-14 08:42:57 -03:00
|
|
|
NodeInsertOfsData *iofsd = static_cast<NodeInsertOfsData *>(op->customdata);
|
2015-09-21 00:55:37 +02:00
|
|
|
bool redraw = false;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-06-02 17:19:36 +02:00
|
|
|
if (!snode || event->type != TIMER || iofsd == nullptr || iofsd->anim_timer != event->customdata)
|
|
|
|
|
{
|
2015-08-01 17:39:48 +02:00
|
|
|
return OPERATOR_PASS_THROUGH;
|
2019-03-26 21:16:47 +11:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-09-01 11:39:35 +10:00
|
|
|
const float duration = float(iofsd->anim_timer->time_duration);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-09-21 00:55:37 +02:00
|
|
|
/* handle animation - do this before possibly aborting due to duration, since
|
|
|
|
|
* main thread might be so busy that node hasn't reached final position yet */
|
2022-12-02 11:12:51 -06:00
|
|
|
for (bNode *node : snode->edittree->all_nodes()) {
|
2022-11-18 12:46:20 +01:00
|
|
|
if (UNLIKELY(node->runtime->anim_ofsx)) {
|
2023-09-01 11:39:35 +10:00
|
|
|
const float prev_duration = duration - float(iofsd->anim_timer->time_delta);
|
2023-08-30 22:08:23 +02:00
|
|
|
/* Clamp duration to not overshoot. */
|
|
|
|
|
const float clamped_duration = math::min(duration, NODE_INSOFS_ANIM_DURATION);
|
|
|
|
|
if (prev_duration < clamped_duration) {
|
|
|
|
|
const float offset_step = node->runtime->anim_ofsx *
|
|
|
|
|
(BLI_easing_cubic_ease_in_out(
|
|
|
|
|
clamped_duration, 0.0f, 1.0f, NODE_INSOFS_ANIM_DURATION) -
|
|
|
|
|
BLI_easing_cubic_ease_in_out(
|
|
|
|
|
prev_duration, 0.0f, 1.0f, NODE_INSOFS_ANIM_DURATION));
|
|
|
|
|
node->locx += offset_step;
|
2015-09-21 00:55:37 +02:00
|
|
|
redraw = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (redraw) {
|
|
|
|
|
ED_region_tag_redraw(CTX_wm_region(C));
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-09-21 00:55:37 +02:00
|
|
|
/* end timer + free insert offset data */
|
2015-08-01 17:39:48 +02:00
|
|
|
if (duration > NODE_INSOFS_ANIM_DURATION) {
|
2023-07-11 13:16:04 +10:00
|
|
|
WM_event_timer_remove(CTX_wm_manager(C), nullptr, iofsd->anim_timer);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-12-02 11:12:51 -06:00
|
|
|
for (bNode *node : snode->edittree->all_nodes()) {
|
2023-08-30 22:08:23 +02:00
|
|
|
node->runtime->anim_ofsx = 0.0f;
|
2015-08-01 17:39:48 +02:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-08-01 17:39:48 +02:00
|
|
|
MEM_freeN(iofsd);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-08-01 17:39:48 +02:00
|
|
|
return (OPERATOR_FINISHED | OPERATOR_PASS_THROUGH);
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-08-01 17:39:48 +02:00
|
|
|
return OPERATOR_RUNNING_MODAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#undef NODE_INSOFS_ANIM_DURATION
|
|
|
|
|
|
|
|
|
|
static int node_insert_offset_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
|
|
|
|
{
|
|
|
|
|
const SpaceNode *snode = CTX_wm_space_node(C);
|
2021-01-19 16:43:08 -06:00
|
|
|
NodeInsertOfsData *iofsd = snode->runtime->iofsd;
|
2022-09-14 08:42:57 -03:00
|
|
|
snode->runtime->iofsd = nullptr;
|
|
|
|
|
op->customdata = iofsd;
|
2015-08-01 17:39:48 +02:00
|
|
|
|
2019-03-26 21:16:47 +11:00
|
|
|
if (!iofsd || !iofsd->insert) {
|
2015-08-01 17:39:48 +02:00
|
|
|
return OPERATOR_CANCELLED;
|
2019-03-26 21:16:47 +11:00
|
|
|
}
|
2015-08-01 17:39:48 +02:00
|
|
|
|
2023-08-29 01:18:33 +02:00
|
|
|
BLI_assert(U.uiflag & USER_NODE_AUTO_OFFSET);
|
2015-08-01 17:39:48 +02:00
|
|
|
|
|
|
|
|
iofsd->ntree = snode->edittree;
|
2023-07-11 13:16:04 +10:00
|
|
|
iofsd->anim_timer = WM_event_timer_add(CTX_wm_manager(C), CTX_wm_window(C), TIMER, 0.02);
|
2015-08-01 17:39:48 +02:00
|
|
|
|
|
|
|
|
node_link_insert_offset_ntree(
|
|
|
|
|
iofsd, CTX_wm_region(C), event->mval, (snode->insert_ofs_dir == SNODE_INSERTOFS_DIR_RIGHT));
|
|
|
|
|
|
|
|
|
|
/* add temp handler */
|
|
|
|
|
WM_event_add_modal_handler(C, op);
|
|
|
|
|
|
|
|
|
|
return OPERATOR_RUNNING_MODAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void NODE_OT_insert_offset(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
/* identifiers */
|
|
|
|
|
ot->name = "Insert Offset";
|
|
|
|
|
ot->description = "Automatically offset nodes on insertion";
|
|
|
|
|
ot->idname = "NODE_OT_insert_offset";
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-08-01 17:39:48 +02:00
|
|
|
/* callbacks */
|
|
|
|
|
ot->invoke = node_insert_offset_invoke;
|
|
|
|
|
ot->modal = node_insert_offset_modal;
|
|
|
|
|
ot->poll = ED_operator_node_editable;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-08-01 17:39:48 +02:00
|
|
|
/* flags */
|
|
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-03 10:35:46 +10:00
|
|
|
/** \} */
|
|
|
|
|
|
2022-01-20 10:36:56 -06:00
|
|
|
} // namespace blender::ed::space_node
|