WIP: Geometry Nodes: Viewer Node Group #112152

Draft
Iliya Katushenock wants to merge 24 commits from mod_moder/blender:group_viewer_branch into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
24 changed files with 945 additions and 118 deletions

View File

@ -25,33 +25,48 @@ def add_node_type(layout, node_type, *, label=None, poll=None):
return props
def draw_node_group_add_menu(context, layout):
"""Add items to the layout used for interacting with node groups."""
def node_groups(context):
"""All node groups allowed in current context."""
space_node = context.space_data
node_tree = space_node.edit_tree
all_node_groups = context.blend_data.node_groups
if node_tree is None:
return None
def group_allowed_in_context(group):
if group.bl_idname is node_tree.bl_idname:
return False
if group.name.startswith('.'):
return False
if group.contains_tree(node_tree):
return False
return True
return [group for group in all_node_groups if group_allowed_in_context(group)]
def draw_node_group_add_menu(context, layout, groups = None):
"""Add items to the layout used for interacting with node groups."""
space_node = context.space_data
node_tree = space_node.edit_tree
if groups is None:
groups = node_groups(context)
all_node_groups = context.blend_data.node_groups
if node_tree in all_node_groups.values():
layout.separator()
add_node_type(layout, "NodeGroupInput")
add_node_type(layout, "NodeGroupOutput")
if node_tree:
if node_tree and groups:
from nodeitems_builtins import node_tree_group_type
groups = [
group for group in context.blend_data.node_groups
if (group.bl_idname == node_tree.bl_idname and
not group.contains_tree(node_tree) and
not group.name.startswith('.'))
]
if groups:
layout.separator()
for group in groups:
props = add_node_type(layout, node_tree_group_type[group.bl_idname], label=group.name)
ops = props.settings.add()
ops.name = "node_tree"
ops.value = "bpy.data.node_groups[%r]" % group.name
layout.separator()
for group in groups:
props = add_node_type(layout, node_tree_group_type[group.bl_idname], label=group.name)
ops = props.settings.add()
ops.name = "node_tree"
ops.value = "bpy.data.node_groups[%r]" % group.name
def draw_assets_for_catalog(layout, catalog_path):

View File

@ -456,10 +456,13 @@ class NODE_MT_category_GEO_OUTPUT(Menu):
bl_idname = "NODE_MT_category_GEO_OUTPUT"
bl_label = "Output"
def draw(self, _context):
def draw(self, context):
layout = self.layout
node_add_menu.add_node_type(layout, "NodeGroupOutput")
node_add_menu.add_node_type(layout, "GeometryNodeViewer")
groups = node_add_menu.node_groups(context)
groups = [group for group in groups if group.is_viewer]
node_add_menu.draw_node_group_add_menu(context, layout, groups)
node_add_menu.draw_assets_for_catalog(layout, self.bl_label)

View File

@ -64,6 +64,31 @@ class NodeGroupComputeContext : public ComputeContext {
void print_current_in_line(std::ostream &stream) const override;
};
class NodeViewerGroupComputeContext : public ComputeContext {
private:
static constexpr const char *s_static_type = "NODE_GROUP";
int32_t node_id_;
#ifdef DEBUG
std::string debug_node_name_;
#endif
public:
NodeViewerGroupComputeContext(const ComputeContext *parent,
int32_t node_id,
const std::optional<ComputeContextHash> &cached_hash = {});
NodeViewerGroupComputeContext(const ComputeContext *parent, const bNode &node);
int32_t node_id() const
{
return node_id_;
}
private:
void print_current_in_line(std::ostream &stream) const override;
};
class SimulationZoneComputeContext : public ComputeContext {
private:
static constexpr const char *s_static_type = "SIMULATION_ZONE";

View File

@ -125,6 +125,10 @@ bool nodeLinkIsSelected(const bNodeLink *link);
void nodeInternalRelink(bNodeTree *ntree, bNode *node);
bool node_is_viewer_group(const bNode &node);
int get_internal_link_type_priority(const bNodeSocketType &from, const bNodeSocketType &to);
float2 nodeToView(const bNode *node, float2 loc);
float2 nodeFromView(const bNode *node, float2 view_loc);

View File

@ -589,6 +589,11 @@ inline blender::Span<const bNodeTreeInterfaceItem *> bNodeTree::interface_items(
return this->tree_interface.runtime->items_;
}
inline bool bNodeTree::is_viewer() const
{
return this->flag & NTREE_IS_VIEWER;
}
/** \} */
/* -------------------------------------------------------------------- */

View File

@ -56,6 +56,7 @@ ModifierViewerPathElem *BKE_viewer_path_elem_new_modifier(void);
GroupNodeViewerPathElem *BKE_viewer_path_elem_new_group_node(void);
SimulationZoneViewerPathElem *BKE_viewer_path_elem_new_simulation_zone(void);
ViewerNodeViewerPathElem *BKE_viewer_path_elem_new_viewer_node(void);
ViewerNodeGroupViewerPathElem *BKE_viewer_path_elem_new_viewer_node_group(void);
RepeatZoneViewerPathElem *BKE_viewer_path_elem_new_repeat_zone(void);
ViewerPathElem *BKE_viewer_path_elem_copy(const ViewerPathElem *src);
bool BKE_viewer_path_elem_equal(const ViewerPathElem *a,

View File

@ -64,6 +64,48 @@ void NodeGroupComputeContext::print_current_in_line(std::ostream &stream) const
stream << "Node ID: " << node_id_;
}
NodeViewerGroupComputeContext::NodeViewerGroupComputeContext(
const ComputeContext *parent,
const int32_t node_id,
const std::optional<ComputeContextHash> &cached_hash)
: ComputeContext(s_static_type, parent), node_id_(node_id)
{
if (cached_hash.has_value()) {
hash_ = *cached_hash;
}
else {
/* Mix static type and node id into a single buffer so that only a single call to #mix_in is
* necessary. */
const int type_size = strlen(s_static_type);
const int buffer_size = type_size + 1 + sizeof(int32_t);
DynamicStackBuffer<64, 8> buffer_owner(buffer_size, 8);
char *buffer = static_cast<char *>(buffer_owner.buffer());
memcpy(buffer, s_static_type, type_size + 1);
memcpy(buffer + type_size + 1, &node_id_, sizeof(int32_t));
hash_.mix_in(buffer, buffer_size);
}
}
NodeViewerGroupComputeContext::NodeViewerGroupComputeContext(const ComputeContext *parent,
const bNode &node)
: NodeViewerGroupComputeContext(parent, node.identifier)
{
#ifdef DEBUG
debug_node_name_ = node.name;
#endif
}
void NodeViewerGroupComputeContext::print_current_in_line(std::ostream &stream) const
{
#ifdef DEBUG
if (!debug_node_name_.empty()) {
stream << "Viewer Node: " << debug_node_name_;
return;
}
#endif
stream << "Viewer Node ID: " << node_id_;
}
SimulationZoneComputeContext::SimulationZoneComputeContext(const ComputeContext *parent,
const int32_t output_node_id)
: ComputeContext(s_static_type, parent), output_node_id_(output_node_id)

View File

@ -3702,6 +3702,12 @@ void nodeSetActive(bNodeTree *ntree, bNode *node)
namespace blender::bke {
bool node_is_viewer_group(const bNode &node)
{
const bNodeTree *tree = reinterpret_cast<bNodeTree *>(node.id);
return node.is_group() && (tree != nullptr) && tree->is_viewer();
}
void nodeSetSocketAvailability(bNodeTree *ntree, bNodeSocket *sock, const bool is_available)
{
if (is_available == sock->is_available()) {

View File

@ -82,11 +82,11 @@ namespace blender::bke {
* `< 0`: never connect these types.
* `>= 0`: priority of connection (higher values chosen first).
*/
static int get_internal_link_type_priority(const bNodeSocketType *from, const bNodeSocketType *to)
int get_internal_link_type_priority(const bNodeSocketType &from, const bNodeSocketType &to)
{
switch (to->type) {
switch (to.type) {
case SOCK_RGBA:
switch (from->type) {
switch (from.type) {
case SOCK_RGBA:
return 4;
case SOCK_FLOAT:
@ -98,7 +98,7 @@ static int get_internal_link_type_priority(const bNodeSocketType *from, const bN
}
return -1;
case SOCK_VECTOR:
switch (from->type) {
switch (from.type) {
case SOCK_VECTOR:
return 4;
case SOCK_FLOAT:
@ -110,7 +110,7 @@ static int get_internal_link_type_priority(const bNodeSocketType *from, const bN
}
return -1;
case SOCK_FLOAT:
switch (from->type) {
switch (from.type) {
case SOCK_FLOAT:
return 5;
case SOCK_INT:
@ -124,7 +124,7 @@ static int get_internal_link_type_priority(const bNodeSocketType *from, const bN
}
return -1;
case SOCK_INT:
switch (from->type) {
switch (from.type) {
case SOCK_INT:
return 5;
case SOCK_FLOAT:
@ -138,7 +138,7 @@ static int get_internal_link_type_priority(const bNodeSocketType *from, const bN
}
return -1;
case SOCK_BOOLEAN:
switch (from->type) {
switch (from.type) {
case SOCK_BOOLEAN:
return 5;
case SOCK_INT:
@ -155,7 +155,7 @@ static int get_internal_link_type_priority(const bNodeSocketType *from, const bN
/* The rest of the socket types only allow an internal link if both the input and output socket
* have the same type. If the sockets are custom, we check the idname instead. */
if (to->type == from->type && (to->type != SOCK_CUSTOM || STREQ(to->idname, from->idname))) {
if (to.type == from.type && (to.type != SOCK_CUSTOM || STREQ(to.idname, from.idname))) {
return 1;
}
@ -485,6 +485,8 @@ class NodeTreeMainUpdater {
this->remove_unused_previews_when_necessary(ntree);
this->make_node_previews_dirty(ntree);
this->propagate_viewers(ntree);
this->propagate_runtime_flags(ntree);
if (ntree.type == NTREE_GEOMETRY) {
if (node_field_inferencing::update_field_inferencing(ntree)) {
@ -664,8 +666,8 @@ class NodeTreeMainUpdater {
if (input_socket->flag & SOCK_NO_INTERNAL_LINK) {
continue;
}
const int priority = get_internal_link_type_priority(input_socket->typeinfo,
output_socket->typeinfo);
const int priority = get_internal_link_type_priority(*input_socket->typeinfo,
*output_socket->typeinfo);
if (priority < 0) {
continue;
}
@ -733,6 +735,37 @@ class NodeTreeMainUpdater {
}
}
void propagate_viewers(bNodeTree &ntree)
{
ntree.ensure_topology_cache();
ntree.ensure_interface_cache();
ntree.flag &= ~NTREE_IS_VIEWER;
if (!ntree.interface_outputs().is_empty()) {
return;
}
const Span<bNode *> viewers = ntree.nodes_by_type("GeometryNodeViewer");
const Span<bNode *> groups = ntree.group_nodes();
const bNode *viewer_node = viewers.size() != 1 ? nullptr : viewers.first();
const bNode *viewer_group = [&]() -> bNode * {
bNode *viewer_group = nullptr;
for (bNode *group_node : groups) {
if (bke::node_is_viewer_group(*group_node)) {
if (viewer_group != nullptr) {
return nullptr;
}
viewer_group = group_node;
}
}
return viewer_group;
}();
if ((viewer_node == nullptr) != (viewer_group == nullptr)) {
ntree.flag |= NTREE_IS_VIEWER;
}
}
void propagate_runtime_flags(const bNodeTree &ntree)
{
ntree.ensure_topology_cache();
@ -892,6 +925,9 @@ class NodeTreeMainUpdater {
if (node.typeinfo->nclass == NODE_CLASS_OUTPUT) {
return true;
}
if (bke::node_is_viewer_group(node)) {
return true;
}
if (node.type == NODE_GROUP_OUTPUT) {
return true;
}

View File

@ -80,6 +80,11 @@ void BKE_viewer_path_blend_write(BlendWriter *writer, const ViewerPath *viewer_p
BLO_write_struct(writer, GroupNodeViewerPathElem, typed_elem);
break;
}
case VIEWER_PATH_ELEM_TYPE_VIEWER_NODE_GROUP: {
const auto *typed_elem = reinterpret_cast<ViewerNodeGroupViewerPathElem *>(elem);
BLO_write_struct(writer, ViewerNodeGroupViewerPathElem, typed_elem);
break;
}
case VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE: {
const auto *typed_elem = reinterpret_cast<SimulationZoneViewerPathElem *>(elem);
BLO_write_struct(writer, SimulationZoneViewerPathElem, typed_elem);
@ -109,6 +114,7 @@ void BKE_viewer_path_blend_read_data(BlendDataReader *reader, ViewerPath *viewer
case VIEWER_PATH_ELEM_TYPE_GROUP_NODE:
case VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE:
case VIEWER_PATH_ELEM_TYPE_VIEWER_NODE:
case VIEWER_PATH_ELEM_TYPE_VIEWER_NODE_GROUP:
case VIEWER_PATH_ELEM_TYPE_REPEAT_ZONE:
case VIEWER_PATH_ELEM_TYPE_ID: {
break;
@ -133,6 +139,7 @@ void BKE_viewer_path_foreach_id(LibraryForeachIDData *data, ViewerPath *viewer_p
}
case VIEWER_PATH_ELEM_TYPE_MODIFIER:
case VIEWER_PATH_ELEM_TYPE_GROUP_NODE:
case VIEWER_PATH_ELEM_TYPE_VIEWER_NODE_GROUP:
case VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE:
case VIEWER_PATH_ELEM_TYPE_VIEWER_NODE:
case VIEWER_PATH_ELEM_TYPE_REPEAT_ZONE: {
@ -153,6 +160,7 @@ void BKE_viewer_path_id_remap(ViewerPath *viewer_path, const IDRemapper *mapping
}
case VIEWER_PATH_ELEM_TYPE_MODIFIER:
case VIEWER_PATH_ELEM_TYPE_GROUP_NODE:
case VIEWER_PATH_ELEM_TYPE_VIEWER_NODE_GROUP:
case VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE:
case VIEWER_PATH_ELEM_TYPE_VIEWER_NODE:
case VIEWER_PATH_ELEM_TYPE_REPEAT_ZONE: {
@ -181,6 +189,9 @@ ViewerPathElem *BKE_viewer_path_elem_new(const ViewerPathElemType type)
case VIEWER_PATH_ELEM_TYPE_GROUP_NODE: {
return &make_elem<GroupNodeViewerPathElem>(type)->base;
}
case VIEWER_PATH_ELEM_TYPE_VIEWER_NODE_GROUP: {
return &make_elem<ViewerNodeGroupViewerPathElem>(type)->base;
}
case VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE: {
return &make_elem<SimulationZoneViewerPathElem>(type)->base;
}
@ -224,6 +235,12 @@ ViewerNodeViewerPathElem *BKE_viewer_path_elem_new_viewer_node()
BKE_viewer_path_elem_new(VIEWER_PATH_ELEM_TYPE_VIEWER_NODE));
}
ViewerNodeGroupViewerPathElem *BKE_viewer_path_elem_new_viewer_node_group()
{
return reinterpret_cast<ViewerNodeGroupViewerPathElem *>(
BKE_viewer_path_elem_new(VIEWER_PATH_ELEM_TYPE_VIEWER_NODE_GROUP));
}
RepeatZoneViewerPathElem *BKE_viewer_path_elem_new_repeat_zone()
{
return reinterpret_cast<RepeatZoneViewerPathElem *>(
@ -257,6 +274,12 @@ ViewerPathElem *BKE_viewer_path_elem_copy(const ViewerPathElem *src)
new_elem->node_id = old_elem->node_id;
break;
}
case VIEWER_PATH_ELEM_TYPE_VIEWER_NODE_GROUP: {
const auto *old_elem = reinterpret_cast<const ViewerNodeGroupViewerPathElem *>(src);
auto *new_elem = reinterpret_cast<ViewerNodeGroupViewerPathElem *>(dst);
new_elem->node_id = old_elem->node_id;
break;
}
case VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE: {
const auto *old_elem = reinterpret_cast<const SimulationZoneViewerPathElem *>(src);
auto *new_elem = reinterpret_cast<SimulationZoneViewerPathElem *>(dst);
@ -303,6 +326,11 @@ bool BKE_viewer_path_elem_equal(const ViewerPathElem *a,
const auto *b_elem = reinterpret_cast<const GroupNodeViewerPathElem *>(b);
return a_elem->node_id == b_elem->node_id;
}
case VIEWER_PATH_ELEM_TYPE_VIEWER_NODE_GROUP: {
const auto *a_elem = reinterpret_cast<const ViewerNodeGroupViewerPathElem *>(a);
const auto *b_elem = reinterpret_cast<const ViewerNodeGroupViewerPathElem *>(b);
return a_elem->node_id == b_elem->node_id;
}
case VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE: {
const auto *a_elem = reinterpret_cast<const SimulationZoneViewerPathElem *>(a);
const auto *b_elem = reinterpret_cast<const SimulationZoneViewerPathElem *>(b);
@ -329,6 +357,7 @@ void BKE_viewer_path_elem_free(ViewerPathElem *elem)
switch (ViewerPathElemType(elem->type)) {
case VIEWER_PATH_ELEM_TYPE_ID:
case VIEWER_PATH_ELEM_TYPE_GROUP_NODE:
case VIEWER_PATH_ELEM_TYPE_VIEWER_NODE_GROUP:
case VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE:
case VIEWER_PATH_ELEM_TYPE_VIEWER_NODE:
case VIEWER_PATH_ELEM_TYPE_REPEAT_ZONE: {

View File

@ -40,7 +40,14 @@ struct ViewerPathForGeometryNodesViewer {
blender::StringRefNull modifier_name;
/* Contains only group node and simulation zone elements. */
blender::Vector<const ViewerPathElem *> node_path;
int32_t viewer_node_id;
Vector<int32_t> node_ids;
};
struct ViewerPathMemory {
ViewerPath viewer_group_path;
ViewerPathMemory();
~ViewerPathMemory();
};
/**
@ -48,14 +55,14 @@ struct ViewerPathForGeometryNodesViewer {
* work.
*/
std::optional<ViewerPathForGeometryNodesViewer> parse_geometry_nodes_viewer(
const ViewerPath &viewer_path);
const ViewerPath &viewer_path, ViewerPathMemory &memory);
/**
* Finds the node referenced by the #ViewerPath within the provided editor. If no node is
* referenced, null is returned. When two different editors show the same node group but in a
* different context, it's possible that the same node is active in one editor but not the other.
*/
bNode *find_geometry_nodes_viewer(const ViewerPath &viewer_path, SpaceNode &snode);
bNode *find_geometry_nodes_viewer_in_space(const ViewerPath &viewer_path, SpaceNode &snode);
/**
* Checks if the node referenced by the viewer path and its entire context still exists. The node

View File

@ -2,6 +2,8 @@
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include <iostream>
#include "BKE_context.h"
#include "ED_screen.hh"
@ -34,6 +36,8 @@ static void validate_viewer_paths(bContext &C, WorkSpace &workspace)
break;
}
std::cout << __func__ << std::endl;
WM_event_add_notifier(&C, NC_VIEWER_PATH, nullptr);
}

View File

@ -1063,8 +1063,12 @@ static int node_get_colorid(TreeDrawContext &tree_draw_ctx, const bNode &node)
return TH_NODE_VECTOR;
case NODE_CLASS_OP_FILTER:
return TH_NODE_FILTER;
case NODE_CLASS_GROUP:
case NODE_CLASS_GROUP: {
if (bke::node_is_viewer_group(node)) {
return &node == tree_draw_ctx.active_geometry_nodes_viewer ? TH_NODE_OUTPUT : TH_NODE;
}
return TH_NODE_GROUP;
}
case NODE_CLASS_INTERFACE:
return TH_NODE_INTERFACE;
case NODE_CLASS_MATTE:
@ -2984,7 +2988,7 @@ static void node_draw_basis(const bContext &C,
"");
UI_block_emboss_set(&block, UI_EMBOSS);
}
if (node.type == GEO_NODE_VIEWER) {
if (node.type == GEO_NODE_VIEWER || bke::node_is_viewer_group(node)) {
const bool is_active = &node == tree_draw_ctx.active_geometry_nodes_viewer;
iconofs -= iconbutw;
UI_block_emboss_set(&block, UI_EMBOSS_NONE);
@ -4114,7 +4118,7 @@ static void draw_nodetree(const bContext &C,
log->ensure_node_run_time();
}
const WorkSpace *workspace = CTX_wm_workspace(&C);
tree_draw_ctx.active_geometry_nodes_viewer = viewer_path::find_geometry_nodes_viewer(
tree_draw_ctx.active_geometry_nodes_viewer = viewer_path::find_geometry_nodes_viewer_in_space(
workspace->viewer_path, *snode);
}
else if (ntree.type == NTREE_COMPOSIT) {

View File

@ -691,7 +691,7 @@ void ED_node_set_active(
}
nodeSetActive(ntree, node);
if (node->type == NODE_GROUP) {
if (node->is_group() && !blender::bke::node_is_viewer_group(*node)) {
return;
}
@ -813,10 +813,10 @@ void ED_node_set_active(
}
}
else if (ntree->type == NTREE_GEOMETRY) {
if (node->type == GEO_NODE_VIEWER) {
if (node->type == GEO_NODE_VIEWER || blender::bke::node_is_viewer_group(*node)) {
if ((node->flag & NODE_DO_OUTPUT) == 0) {
for (bNode *node_iter : ntree->all_nodes()) {
if (node_iter->type == GEO_NODE_VIEWER) {
if (node_iter->type == GEO_NODE_VIEWER || blender::bke::node_is_viewer_group(*node)) {
node_iter->flag &= ~NODE_DO_OUTPUT;
}
}
@ -1684,21 +1684,14 @@ static int node_deactivate_viewer_exec(bContext *C, wmOperator * /*op*/)
SpaceNode &snode = *CTX_wm_space_node(C);
WorkSpace &workspace = *CTX_wm_workspace(C);
bNode *active_viewer = viewer_path::find_geometry_nodes_viewer(workspace.viewer_path, snode);
bNode *active_viewer = viewer_path::find_geometry_nodes_viewer_in_space(workspace.viewer_path,
snode);
for (bNode *node : snode.edittree->all_nodes()) {
if (node->type != GEO_NODE_VIEWER) {
continue;
}
if (!(node->flag & SELECT)) {
continue;
}
if (node == active_viewer) {
node->flag &= ~NODE_DO_OUTPUT;
BKE_ntree_update_tag_node_property(snode.edittree, node);
}
if (active_viewer == nullptr) {
return OPERATOR_FINISHED;
}
active_viewer->flag &= ~NODE_DO_OUTPUT;
BKE_ntree_update_tag_node_property(snode.edittree, active_viewer);
ED_node_tree_propagate_change(C, CTX_data_main(C), snode.edittree);
return OPERATOR_FINISHED;

View File

@ -50,6 +50,7 @@
#include "BLT_translation.h"
#include "NOD_node_declaration.hh"
#include "NOD_socket.hh"
#include "NOD_socket_declarations.hh"
#include "NOD_socket_declarations_geometry.hh"
@ -413,6 +414,28 @@ namespace viewer_linking {
/** \name Link Viewer Operator
* \{ */
/* Find index of socket int viewer that the best to connect to a to_viewer. */
static std::optional<int> best_socket_of_viewer(const bNode &viewer, const bNodeSocket &to_viewer)
{
const Span<const bNodeSocket *> inputs = viewer.input_sockets();
if (inputs.is_empty()) {
return std::nullopt;
}
int max_value = 0;
std::optional<int> index_of_max;
for (const int index : inputs.index_range()) {
const bNodeSocket &socket = *inputs[index];
const int priority = bke::get_internal_link_type_priority(*to_viewer.typeinfo,
*socket.typeinfo);
if (priority > max_value) {
max_value = priority;
index_of_max.emplace(index);
}
}
return index_of_max;
}
/* Depending on the node tree type, different socket types are supported by viewer nodes. */
static bool socket_can_be_viewed(const bNode &node, const bNodeSocket &socket)
{
@ -425,19 +448,13 @@ static bool socket_can_be_viewed(const bNode &node, const bNodeSocket &socket)
if (socket.owner_tree().type != NTREE_GEOMETRY) {
return true;
}
return ELEM(socket.typeinfo->type,
SOCK_GEOMETRY,
SOCK_FLOAT,
SOCK_VECTOR,
SOCK_INT,
SOCK_BOOLEAN,
SOCK_ROTATION,
SOCK_RGBA);
return true;
}
/**
* Find the socket to link to in a viewer node.
*/
static bNodeSocket *node_link_viewer_get_socket(bNodeTree &ntree,
bNode &viewer_node,
bNodeSocket &src_socket)
@ -466,7 +483,8 @@ static bNodeSocket *node_link_viewer_get_socket(bNodeTree &ntree,
static bool is_viewer_node(const bNode &node)
{
return ELEM(node.type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER, GEO_NODE_VIEWER);
return ELEM(node.type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER, GEO_NODE_VIEWER) ||
bke::node_is_viewer_group(node);
}
static bool is_viewer_socket_in_viewer(const bNodeSocket &socket)
@ -593,16 +611,60 @@ static void finalize_viewer_link(const bContext &C,
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)
static Array<const bNode *> all_view_nodes(const bNodeTree &tree)
{
return {};
}
static Array<const bNode *> all_connected_viewer(const bNodeTree &tree, const bNode &bnode_to_view)
{
return {};
}
static const bNode *active_viewer_in_tree(const bNodeTree &tree)
{
return {};
}
static int link_view_sockets(const bContext &C,
SpaceNode &snode,
bNodeTree &btree,
bNodeSocket &bsocket_to_view,
bNodeSocket &bsocket_of_viewer)
{
bNode &node = bsocket_to_view.owner_node();
bNode &viewer = bsocket_of_viewer.owner_node();
bNodeLink *viewer_link = nullptr;
for (bNodeLink *link : btree.all_links()) {
if (link->tosock == &bsocket_of_viewer) {
viewer_link = link;
break;
}
}
if (viewer_link == nullptr) {
viewer_link = nodeAddLink(&btree, &node, &bsocket_to_view, &viewer, &bsocket_of_viewer);
}
else {
viewer_link->fromnode = &node;
viewer_link->fromsock = &bsocket_to_view;
BKE_ntree_update_tag_link_changed(&btree);
}
finalize_viewer_link(C, snode, viewer, *viewer_link);
return OPERATOR_CANCELLED;
}
static bNodeSocket *best_view_socket_find_or_new(const bNodeTree &btree,
const bNodeSocket &bsocket_to_view,
const Span<const bNode *> viewers,
const Span<const bNode *> connected_viewers,
const bNode *active_viewer)
{
return {};
/*
bNode *viewer_node = nullptr;
/* Try to find a viewer that is already active. */
for (bNode *node : btree.all_nodes()) {
if (is_viewer_node(*node)) {
if (is_viewer_node(*node) || bke::node_is_viewer_group(*node)) {
if (node->flag & NODE_DO_OUTPUT) {
viewer_node = node;
break;
@ -610,7 +672,6 @@ static int view_socket(const bContext &C,
}
}
/* 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;
@ -635,46 +696,357 @@ static int view_socket(const bContext &C,
socket_location.y / UI_SCALE_FAC};
viewer_node = add_static_node(C, viewer_type, location);
}
bNodeSocket *viewer_bsocket = node_link_viewer_get_socket(btree, *viewer_node, bsocket_to_view);
if (viewer_bsocket == nullptr) {
return OPERATOR_CANCELLED;
}
bNodeLink *viewer_link = nullptr;
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &btree.links) {
if (link->tosock == viewer_bsocket) {
viewer_link = link;
break;
}
}
if (viewer_link == nullptr) {
viewer_link = nodeAddLink(
&btree, &bnode_to_view, &bsocket_to_view, viewer_node, viewer_bsocket);
}
else {
viewer_link->fromnode = &bnode_to_view;
viewer_link->fromsock = &bsocket_to_view;
BKE_ntree_update_tag_link_changed(&btree);
}
finalize_viewer_link(C, snode, *viewer_node, *viewer_link);
return OPERATOR_CANCELLED;
*/
}
static int node_link_viewer(const bContext &C, bNode &bnode_to_view, bNodeSocket *bsocket_to_view)
struct ViewerSocketTrait {
std::function<std::pair<bNode *, bNodeSocket *>(bNodeTree &)> finalize;
eNodeSocketDatatype type;
StringRef socket_name;
StringRef node_name;
bool node_is_connected = false;
float prioriti = 1.0f;
};
static void traits_for_viewer(Vector<ViewerSocketTrait> &r_traits)
{
static const Array<eNodeSocketDatatype> viewer_types = {
SOCK_BOOLEAN, SOCK_FLOAT, SOCK_RGBA, SOCK_INT, SOCK_ROTATION, SOCK_VECTOR};
static const auto new_viewer = [](bNodeTree &tree) -> bNode & {
return *nodeAddStaticNode(nullptr, &tree, GEO_NODE_VIEWER);
};
ViewerSocketTrait trait;
trait.type = SOCK_GEOMETRY;
trait.socket_name = "Geometry";
trait.node_name = "Viewer";
trait.prioriti = 1.0f;
trait.finalize = [=](bNodeTree &tree) -> std::pair<bNode *, bNodeSocket *> {
bNode &viewer = new_viewer(tree);
tree.ensure_topology_cache();
return {&viewer, viewer.input_sockets().first()};
};
r_traits.append(std::move(trait));
for (const eNodeSocketDatatype socket_type : viewer_types) {
ViewerSocketTrait trait;
trait.type = socket_type;
trait.socket_name = "Value";
trait.node_name = "Viewer";
trait.prioriti = 0.5f;
trait.finalize = [=](bNodeTree &tree) -> std::pair<bNode *, bNodeSocket *> {
bNode &viewer = new_viewer(tree);
tree.ensure_topology_cache();
for (bNodeSocket *socket : viewer.input_sockets().drop_front(1)) {
const eNodeSocketDatatype other_socket_type = eNodeSocketDatatype(socket->type);
if (socket_type == other_socket_type) {
NodeGeometryViewer &storage = *static_cast<NodeGeometryViewer *>(viewer.storage);
storage.data_type = *bke::socket_type_to_custom_data_type(other_socket_type);
viewer.typeinfo->updatefunc(&tree, &viewer);
return {&viewer, socket};
}
}
BLI_assert_unreachable();
return {};
};
r_traits.append(std::move(trait));
}
}
static void traits_for_viewers(const Span<const bNode *> viewers,
const Span<bool> depend_on_node,
Vector<ViewerSocketTrait> &r_traits)
{
static const Array<eNodeSocketDatatype> viewer_types = {
SOCK_BOOLEAN, SOCK_FLOAT, SOCK_RGBA, SOCK_INT, SOCK_ROTATION, SOCK_VECTOR};
for (const bNode *viewer : viewers) {
if (!viewer->input_sockets().first()->is_directly_linked()) {
ViewerSocketTrait trait;
trait.type = SOCK_GEOMETRY;
trait.socket_name = "Geometry";
trait.node_name = viewer->name;
trait.prioriti = 1.0f;
trait.node_is_connected = depend_on_node[viewer->index()];
const int32_t identifiers = viewer->identifier;
trait.finalize = [=](bNodeTree &tree) -> std::pair<bNode *, bNodeSocket *> {
tree.ensure_topology_cache();
bNode &viewer = *tree.node_by_id(identifiers);
return {&viewer, viewer.input_sockets().first()};
};
r_traits.append(std::move(trait));
}
bool value_is_linked = false;
for (const bNodeSocket *socket : viewer->input_sockets().drop_front(1)) {
value_is_linked |= socket->is_directly_linked();
}
if (!value_is_linked) {
for (const eNodeSocketDatatype socket_type : viewer_types) {
ViewerSocketTrait trait;
trait.type = socket_type;
trait.socket_name = "Value";
trait.node_name = viewer->name;
trait.prioriti = 1.0f;
trait.node_is_connected = depend_on_node[viewer->index()];
const int32_t identifiers = viewer->identifier;
NodeGeometryViewer &storage = *static_cast<NodeGeometryViewer *>(viewer->storage);
if (storage.data_type == *bke::socket_type_to_custom_data_type(socket_type)) {
trait.prioriti = 2.0f;
}
trait.finalize = [=](bNodeTree &tree) -> std::pair<bNode *, bNodeSocket *> {
tree.ensure_topology_cache();
bNode &viewer = *tree.node_by_id(identifiers);
for (bNodeSocket *socket : viewer.input_sockets().drop_front(1)) {
const eNodeSocketDatatype other_socket_type = eNodeSocketDatatype(socket->type);
if (socket_type == other_socket_type) {
NodeGeometryViewer &storage = *static_cast<NodeGeometryViewer *>(viewer.storage);
storage.data_type = *bke::socket_type_to_custom_data_type(other_socket_type);
viewer.typeinfo->updatefunc(&tree, &viewer);
return {&viewer, socket};
}
}
BLI_assert_unreachable();
return {};
};
r_traits.append(std::move(trait));
}
}
}
}
static void traits_for_groups(Main &bmain, Vector<ViewerSocketTrait> &r_traits)
{
LISTBASE_FOREACH (bNodeTree *, group, &bmain.nodetrees) {
if (!group->is_viewer()) {
continue;
}
group->ensure_topology_cache();
group->ensure_interface_cache();
const Span<const bNodeTreeInterfaceSocket *> inputs = group->interface_inputs();
for (const int index : inputs.index_range()) {
const bNodeTreeInterfaceSocket &input = *inputs[index];
ViewerSocketTrait trait;
trait.type = eNodeSocketDatatype(input.socket_typeinfo()->type);
trait.socket_name = input.name;
trait.node_name = group->id.name;
trait.prioriti = 3.0f;
trait.finalize = [=](bNodeTree &tree) -> std::pair<bNode *, bNodeSocket *> {
tree.ensure_topology_cache();
bNode *node_group = nodeAddStaticNode(nullptr, &tree, NODE_GROUP);
node_group->id = &group->id;
id_us_plus(&group->id);
tree.ensure_topology_cache();
bke::node_field_inferencing::update_field_inferencing(*group);
nodes::update_node_declaration_and_sockets(tree, *node_group);
tree.ensure_topology_cache();
return {node_group, node_group->input_sockets()[index]};
};
r_traits.append(std::move(trait));
}
}
}
static void traits_for_exist_groups(const Span<const bNode *> groups,
Vector<ViewerSocketTrait> &r_traits)
{
}
int get_link_type_priority(const eNodeSocketDatatype from, const eNodeSocketDatatype to)
{
switch (to) {
case SOCK_RGBA:
switch (from) {
case SOCK_RGBA:
return 4;
case SOCK_FLOAT:
return 3;
case SOCK_INT:
return 2;
case SOCK_BOOLEAN:
return 1;
default:
break;
}
return -1;
case SOCK_VECTOR:
switch (from) {
case SOCK_VECTOR:
return 4;
case SOCK_FLOAT:
return 3;
case SOCK_INT:
return 2;
case SOCK_BOOLEAN:
return 1;
default:
break;
}
return -1;
case SOCK_FLOAT:
switch (from) {
case SOCK_FLOAT:
return 5;
case SOCK_INT:
return 4;
case SOCK_BOOLEAN:
return 3;
case SOCK_RGBA:
return 2;
case SOCK_VECTOR:
return 1;
default:
break;
}
return -1;
case SOCK_INT:
switch (from) {
case SOCK_INT:
return 5;
case SOCK_FLOAT:
return 4;
case SOCK_BOOLEAN:
return 3;
case SOCK_RGBA:
return 2;
case SOCK_VECTOR:
return 1;
default:
break;
}
return -1;
case SOCK_BOOLEAN:
switch (from) {
case SOCK_BOOLEAN:
return 5;
case SOCK_INT:
return 4;
case SOCK_FLOAT:
return 3;
case SOCK_RGBA:
return 2;
case SOCK_VECTOR:
return 1;
default:
break;
}
return -1;
default:
break;
}
if (to == from) {
return 1;
}
return -1;
}
static int node_link_viewer(const bContext &C, bNode &bnode_to_view, bNodeSocket *node_socket)
{
SpaceNode &snode = *CTX_wm_space_node(&C);
bNodeTree *btree = snode.edittree;
btree->ensure_topology_cache();
if (bsocket_to_view == nullptr) {
bsocket_to_view = determine_socket_to_view(bnode_to_view);
const Span<const bNode *> viewers = btree->nodes_by_type("GeometryNodeViewer");
const Span<const bNode *> groups = btree->group_nodes();
// const Array<const bNode *> connected_viewers = all_connected_viewer(*btree, bnode_to_view);
const bNode *active_viewer = active_viewer_in_tree(*btree);
if (node_socket == nullptr) {
node_socket = determine_socket_to_view(bnode_to_view);
}
if (bsocket_to_view == nullptr) {
if (node_socket == nullptr) {
return OPERATOR_CANCELLED;
}
return view_socket(C, snode, *btree, bnode_to_view, *bsocket_to_view);
const Span<const bNode *> left_to_right = btree->toposort_left_to_right();
Array<bool> depend_on_node(left_to_right.size(), false);
for (const bNode *node : left_to_right) {
for (const bNodeSocket *input : node->input_sockets()) {
for (const bNodeSocket *output_socket : input->directly_linked_sockets()) {
const bNode &other = output_socket->owner_node();
if (&other == &bnode_to_view) {
depend_on_node[node->index()] = true;
}
depend_on_node[node->index()] |= depend_on_node[other.index()];
}
}
}
Vector<ViewerSocketTrait> traits;
traits_for_viewer(traits);
traits_for_viewers(viewers, depend_on_node, traits);
// traits_for_viewers({active_viewer}, traits);
traits_for_exist_groups(groups, traits);
traits_for_groups(*CTX_data_main(&C), traits);
if (traits.is_empty()) {
return OPERATOR_CANCELLED;
}
const ViewerSocketTrait &viewer_trait = *std::max_element(
traits.begin(),
traits.end(),
[&](const ViewerSocketTrait &a, const ViewerSocketTrait &b) -> bool {
const bool to_connected = a.node_is_connected != b.node_is_connected;
const bool connected_node = b.node_is_connected;
const int a_type_priority = get_link_type_priority(a.type,
eNodeSocketDatatype(node_socket->type));
const int b_type_priority = get_link_type_priority(b.type,
eNodeSocketDatatype(node_socket->type));
if (ELEM(-1, a_type_priority, b_type_priority)) {
return a_type_priority < b_type_priority;
}
/* Avoid conversions if possible. */
if (ELEM(4, a_type_priority, b_type_priority) && a_type_priority != b_type_priority) {
return a_type_priority < b_type_priority;
}
if (to_connected) {
const float connection_factor = connected_node ? 5.0f : 0.2f;
return a.prioriti < b.prioriti * connection_factor;
}
return a.prioriti < b.prioriti;
});
if (-1 == get_link_type_priority(viewer_trait.type, eNodeSocketDatatype(node_socket->type))) {
return OPERATOR_CANCELLED;
}
auto [viewer_node, viewer_socket] = viewer_trait.finalize(*btree);
btree->ensure_topology_cache();
return link_view_sockets(C, snode, *btree, *node_socket, *viewer_socket);
}
/** \} */

View File

@ -6,6 +6,8 @@
* \ingroup spnode
*/
#include <iostream>
#include <array>
#include <cstdlib>
#include <iostream>
@ -676,14 +678,18 @@ static bool node_mouse_select(bContext *C,
return false;
}
std::cout << __func__ << std::endl;
bool active_texture_changed = false;
bool viewer_node_changed = false;
if ((node != nullptr) && (node_was_selected == false || params->select_passthrough == false)) {
viewer_node_changed = (node->flag & NODE_DO_OUTPUT) == 0 && node->type == GEO_NODE_VIEWER;
ED_node_set_active(&bmain, &snode, snode.edittree, node, &active_texture_changed);
std::cout << __LINE__ << std::endl;
}
else if (node != nullptr && node->type == GEO_NODE_VIEWER) {
viewer_path::activate_geometry_node(bmain, snode, *node);
std::cout << __LINE__ << std::endl;
}
ED_node_set_active_viewer_key(&snode);
tree_draw_order_update(node_tree);
@ -691,6 +697,7 @@ static bool node_mouse_select(bContext *C,
viewer_node_changed)
{
DEG_id_tag_update(&snode.edittree->id, ID_RECALC_COPY_ON_WRITE);
std::cout << __LINE__ << std::endl;
}
WM_event_add_notifier(C, NC_NODE | NA_SELECTED, nullptr);

View File

@ -255,8 +255,10 @@ static void spreadsheet_update_context(const bContext *C)
case SPREADSHEET_OBJECT_EVAL_STATE_VIEWER_NODE: {
WorkSpace *workspace = CTX_wm_workspace(C);
if (sspreadsheet->flag & SPREADSHEET_FLAG_PINNED) {
ed::viewer_path::ViewerPathMemory memory;
const std::optional<ViewerPathForGeometryNodesViewer> parsed_path =
blender::ed::viewer_path::parse_geometry_nodes_viewer(sspreadsheet->viewer_path);
blender::ed::viewer_path::parse_geometry_nodes_viewer(sspreadsheet->viewer_path,
memory);
if (parsed_path.has_value()) {
if (blender::ed::viewer_path::exists_geometry_nodes_viewer(*parsed_path)) {
/* The pinned path is still valid, do nothing. */
@ -271,8 +273,9 @@ static void spreadsheet_update_context(const bContext *C)
}
}
/* Now try to update the viewer path from the workspace. */
ed::viewer_path::ViewerPathMemory memory;
const std::optional<ViewerPathForGeometryNodesViewer> workspace_parsed_path =
blender::ed::viewer_path::parse_geometry_nodes_viewer(workspace->viewer_path);
blender::ed::viewer_path::parse_geometry_nodes_viewer(workspace->viewer_path, memory);
if (workspace_parsed_path.has_value()) {
if (BKE_viewer_path_equal(&sspreadsheet->viewer_path, &workspace->viewer_path)) {
/* Nothing changed. */