Merge branch 'master' into geometry-nodes-simulation
This commit is contained in:
commit
cff291d1f3
|
@ -970,16 +970,9 @@ if(WITH_COMPILER_CCACHE)
|
|||
endif()
|
||||
endif()
|
||||
|
||||
# On some platforms certain atomic operations are not possible with assembly and/or intrinsics and
|
||||
# they are emulated in software with locks. For example, on armel there is no intrinsics to grant
|
||||
# 64 bit atomic operations and STL library uses libatomic to offload software emulation of atomics
|
||||
# to.
|
||||
# This function will check whether libatomic is required and if so will configure linker flags.
|
||||
# If atomic operations are possible without libatomic then linker flags are left as-is.
|
||||
function(CONFIGURE_ATOMIC_LIB_IF_NEEDED)
|
||||
# Source which is used to enforce situation when software emulation of atomics is required.
|
||||
# Assume that using 64bit integer gives a definitive answer (as in, if 64bit atomic operations
|
||||
# are possible using assembly/intrinsics 8, 16, and 32 bit operations will also be possible.
|
||||
# Always link with libatomic if available, as it is required for data types
|
||||
# which don't have intrinsics.
|
||||
function(configure_atomic_lib_if_needed)
|
||||
set(_source
|
||||
"#include <atomic>
|
||||
#include <cstdint>
|
||||
|
@ -990,25 +983,12 @@ function(CONFIGURE_ATOMIC_LIB_IF_NEEDED)
|
|||
)
|
||||
|
||||
include(CheckCXXSourceCompiles)
|
||||
check_cxx_source_compiles("${_source}" ATOMIC_OPS_WITHOUT_LIBATOMIC)
|
||||
set(CMAKE_REQUIRED_LIBRARIES atomic)
|
||||
check_cxx_source_compiles("${_source}" ATOMIC_OPS_WITH_LIBATOMIC)
|
||||
unset(CMAKE_REQUIRED_LIBRARIES)
|
||||
|
||||
if(NOT ATOMIC_OPS_WITHOUT_LIBATOMIC)
|
||||
# Compilation of the test program has failed.
|
||||
# Try it again with -latomic to see if this is what is needed, or whether something else is
|
||||
# going on.
|
||||
|
||||
set(CMAKE_REQUIRED_LIBRARIES atomic)
|
||||
check_cxx_source_compiles("${_source}" ATOMIC_OPS_WITH_LIBATOMIC)
|
||||
unset(CMAKE_REQUIRED_LIBRARIES)
|
||||
|
||||
if(ATOMIC_OPS_WITH_LIBATOMIC)
|
||||
set(PLATFORM_LINKFLAGS "${PLATFORM_LINKFLAGS} -latomic" PARENT_SCOPE)
|
||||
else()
|
||||
# Atomic operations are required part of Blender and it is not possible to process forward.
|
||||
# We expect that either standard library or libatomic will make atomics to work. If both
|
||||
# cases has failed something fishy o na bigger scope is going on.
|
||||
message(FATAL_ERROR "Failed to detect required configuration for atomic operations")
|
||||
endif()
|
||||
if(ATOMIC_OPS_WITH_LIBATOMIC)
|
||||
set(PLATFORM_LINKFLAGS "${PLATFORM_LINKFLAGS} -latomic" PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
|
|
|
@ -265,7 +265,7 @@ bool LightManager::object_usable_as_light(Object *object)
|
|||
return false;
|
||||
}
|
||||
|
||||
void LightManager::device_update_distribution(Device *device,
|
||||
void LightManager::device_update_distribution(Device *,
|
||||
DeviceScene *dscene,
|
||||
Scene *scene,
|
||||
Progress &progress)
|
||||
|
|
|
@ -307,8 +307,10 @@ class NODE_MT_select(Menu):
|
|||
class NODE_MT_node(Menu):
|
||||
bl_label = "Node"
|
||||
|
||||
def draw(self, _context):
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
snode = context.space_data
|
||||
is_compositor = snode.tree_type == 'CompositorNodeTree'
|
||||
|
||||
layout.operator("transform.translate")
|
||||
layout.operator("transform.rotate")
|
||||
|
@ -346,14 +348,17 @@ class NODE_MT_node(Menu):
|
|||
|
||||
layout.operator("node.hide_toggle")
|
||||
layout.operator("node.mute_toggle")
|
||||
layout.operator("node.preview_toggle")
|
||||
if is_compositor:
|
||||
layout.operator("node.preview_toggle")
|
||||
layout.operator("node.hide_socket_toggle")
|
||||
layout.operator("node.options_toggle")
|
||||
layout.operator("node.collapse_hide_unused_toggle")
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.operator("node.read_viewlayers")
|
||||
if is_compositor:
|
||||
layout.separator()
|
||||
|
||||
layout.operator("node.read_viewlayers")
|
||||
|
||||
|
||||
class NODE_MT_view_pie(Menu):
|
||||
|
|
|
@ -670,6 +670,13 @@ void nodeUnlinkNode(struct bNodeTree *ntree, struct bNode *node);
|
|||
void nodeUniqueName(struct bNodeTree *ntree, struct bNode *node);
|
||||
void nodeUniqueID(struct bNodeTree *ntree, struct bNode *node);
|
||||
|
||||
/**
|
||||
* Rebuild the `node_by_id` runtime vector set. Call after removing a node if not handled
|
||||
* separately. This is important instead of just using `nodes_by_id.remove()` since it maintains
|
||||
* the node order.
|
||||
*/
|
||||
void nodeRebuildIDVector(struct bNodeTree *node_tree);
|
||||
|
||||
/**
|
||||
* Delete node, associated animation data and ID user count.
|
||||
*/
|
||||
|
|
|
@ -214,9 +214,6 @@ class bNodeRuntime : NonCopyable, NonMovable {
|
|||
/** #eNodeTreeChangedFlag. */
|
||||
uint32_t changed_flag = 0;
|
||||
|
||||
/** For dependency and sorting. */
|
||||
short done = 0;
|
||||
|
||||
/** Used as a boolean for execution. */
|
||||
uint8_t need_exec = 0;
|
||||
|
||||
|
|
|
@ -33,8 +33,8 @@ AssetMetaData *BKE_asset_metadata_create()
|
|||
|
||||
void BKE_asset_metadata_free(AssetMetaData **asset_data)
|
||||
{
|
||||
(*asset_data)->~AssetMetaData();
|
||||
MEM_SAFE_FREE(*asset_data);
|
||||
MEM_delete(*asset_data);
|
||||
*asset_data = nullptr;
|
||||
}
|
||||
|
||||
AssetMetaData::~AssetMetaData()
|
||||
|
|
|
@ -2936,12 +2936,12 @@ static void node_unlink_attached(bNodeTree *ntree, bNode *parent)
|
|||
}
|
||||
}
|
||||
|
||||
static void rebuild_nodes_vector(bNodeTree &node_tree)
|
||||
void nodeRebuildIDVector(bNodeTree *node_tree)
|
||||
{
|
||||
/* Rebuild nodes #VectorSet which must have the same order as the list. */
|
||||
node_tree.runtime->nodes_by_id.clear();
|
||||
LISTBASE_FOREACH (bNode *, node, &node_tree.nodes) {
|
||||
node_tree.runtime->nodes_by_id.add_new(node);
|
||||
node_tree->runtime->nodes_by_id.clear();
|
||||
LISTBASE_FOREACH (bNode *, node, &node_tree->nodes) {
|
||||
node_tree->runtime->nodes_by_id.add_new(node);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2958,7 +2958,7 @@ static void node_free_node(bNodeTree *ntree, bNode *node)
|
|||
if (ntree) {
|
||||
BLI_remlink(&ntree->nodes, node);
|
||||
/* Rebuild nodes #VectorSet which must have the same order as the list. */
|
||||
rebuild_nodes_vector(*ntree);
|
||||
nodeRebuildIDVector(ntree);
|
||||
|
||||
/* texture node has bad habit of keeping exec data around */
|
||||
if (ntree->type == NTREE_TEXTURE && ntree->runtime->execdata) {
|
||||
|
@ -3016,7 +3016,7 @@ void ntreeFreeLocalNode(bNodeTree *ntree, bNode *node)
|
|||
node_unlink_attached(ntree, node);
|
||||
|
||||
node_free_node(ntree, node);
|
||||
rebuild_nodes_vector(*ntree);
|
||||
nodeRebuildIDVector(ntree);
|
||||
}
|
||||
|
||||
void nodeRemoveNode(Main *bmain, bNodeTree *ntree, bNode *node, bool do_id_user)
|
||||
|
@ -3079,7 +3079,7 @@ void nodeRemoveNode(Main *bmain, bNodeTree *ntree, bNode *node, bool do_id_user)
|
|||
|
||||
/* Free node itself. */
|
||||
node_free_node(ntree, node);
|
||||
rebuild_nodes_vector(*ntree);
|
||||
nodeRebuildIDVector(ntree);
|
||||
}
|
||||
|
||||
static void node_socket_interface_free(bNodeTree * /*ntree*/,
|
||||
|
|
|
@ -2001,6 +2001,7 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *bmain)
|
|||
*/
|
||||
link = MEM_callocN(sizeof(bNodeLink), "link");
|
||||
BLI_addtail(&ntree->links, link);
|
||||
nodeUniqueID(ntree, node);
|
||||
link->fromnode = NULL;
|
||||
link->fromsock = gsock;
|
||||
link->tonode = node;
|
||||
|
@ -2024,6 +2025,7 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *bmain)
|
|||
*/
|
||||
link = MEM_callocN(sizeof(bNodeLink), "link");
|
||||
BLI_addtail(&ntree->links, link);
|
||||
nodeUniqueID(ntree, node);
|
||||
link->fromnode = node;
|
||||
link->fromsock = sock;
|
||||
link->tonode = NULL;
|
||||
|
|
|
@ -581,9 +581,9 @@ static BMVert **bm_to_mesh_vertex_map(BMesh *bm, int ototvert)
|
|||
* Also not correct but it's better then having it zeroed for e.g.
|
||||
*
|
||||
* - Missing key-index layer.
|
||||
* In this case the basis key wont apply it's deltas to other keys and in the case
|
||||
* a shape-key layer is missing, its coordinates will be initialized from the edit-mesh
|
||||
* vertex locations instead of attempting to remap the shape-keys coordinates.
|
||||
* In this case the basis key won't apply its deltas to other keys and if a shape-key layer is
|
||||
* missing, its coordinates will be initialized from the edit-mesh vertex locations instead of
|
||||
* attempting to remap the shape-keys coordinates.
|
||||
*
|
||||
* \note These cases are considered abnormal and shouldn't occur in typical usage.
|
||||
* A warning is logged in this case to help troubleshooting bugs with shape-keys.
|
||||
|
|
|
@ -1709,7 +1709,7 @@ void NODE_OT_preview_toggle(wmOperatorType *ot)
|
|||
|
||||
/* callbacks */
|
||||
ot->exec = node_preview_toggle_exec;
|
||||
ot->poll = ED_operator_node_active;
|
||||
ot->poll = composite_node_active;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
@ -2795,7 +2795,7 @@ static bool node_shader_script_update_text_recursive(RenderEngine *engine,
|
|||
RenderEngineType *type,
|
||||
bNodeTree *ntree,
|
||||
Text *text,
|
||||
Set<bNodeTree *> &done_trees)
|
||||
VectorSet<bNodeTree *> &done_trees)
|
||||
{
|
||||
bool found = false;
|
||||
|
||||
|
@ -2855,7 +2855,7 @@ static int node_shader_script_update_exec(bContext *C, wmOperator *op)
|
|||
|
||||
if (text) {
|
||||
|
||||
Set<bNodeTree *> done_trees;
|
||||
VectorSet<bNodeTree *> done_trees;
|
||||
|
||||
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
|
||||
if (ntree->type == NTREE_SHADER) {
|
||||
|
|
|
@ -269,6 +269,7 @@ static bool node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode)
|
|||
|
||||
node->flag |= NODE_SELECT;
|
||||
}
|
||||
wgroup->runtime->nodes_by_id.clear();
|
||||
|
||||
bNodeLink *glinks_first = (bNodeLink *)ntree->links.last;
|
||||
|
||||
|
@ -446,30 +447,24 @@ static bool node_group_separate_selected(
|
|||
|
||||
ListBase anim_basepaths = {nullptr, nullptr};
|
||||
|
||||
Map<const bNode *, bNode *> node_map;
|
||||
Map<const bNodeSocket *, bNodeSocket *> socket_map;
|
||||
|
||||
/* add selected nodes into the ntree */
|
||||
LISTBASE_FOREACH_MUTABLE (bNode *, node, &ngroup.nodes) {
|
||||
if (!(node->flag & NODE_SELECT)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* ignore interface nodes */
|
||||
if (ELEM(node->type, NODE_GROUP_INPUT, NODE_GROUP_OUTPUT)) {
|
||||
nodeSetSelected(node, false);
|
||||
continue;
|
||||
}
|
||||
/* Add selected nodes into the ntree, ignoring interface nodes. */
|
||||
VectorSet<bNode *> nodes_to_move = get_selected_nodes(ngroup);
|
||||
nodes_to_move.remove_if(
|
||||
[](const bNode *node) { return node->is_group_input() || node->is_group_output(); });
|
||||
|
||||
for (bNode *node : nodes_to_move) {
|
||||
bNode *newnode;
|
||||
if (make_copy) {
|
||||
/* make a copy */
|
||||
newnode = bke::node_copy_with_mapping(&ngroup, *node, LIB_ID_COPY_DEFAULT, true, socket_map);
|
||||
node_map.add_new(node, newnode);
|
||||
newnode = bke::node_copy_with_mapping(&ntree, *node, LIB_ID_COPY_DEFAULT, true, socket_map);
|
||||
}
|
||||
else {
|
||||
/* use the existing node */
|
||||
newnode = node;
|
||||
BLI_remlink(&ngroup.nodes, newnode);
|
||||
BLI_addtail(&ntree.nodes, newnode);
|
||||
nodeUniqueID(&ntree, newnode);
|
||||
nodeUniqueName(&ntree, newnode);
|
||||
}
|
||||
|
||||
/* Keep track of this node's RNA "base" path (the part of the path identifying the node)
|
||||
|
@ -491,17 +486,14 @@ static bool node_group_separate_selected(
|
|||
nodeDetachNode(&ngroup, newnode);
|
||||
}
|
||||
|
||||
/* migrate node */
|
||||
BLI_remlink(&ngroup.nodes, newnode);
|
||||
BLI_addtail(&ntree.nodes, newnode);
|
||||
nodeUniqueID(&ntree, newnode);
|
||||
nodeUniqueName(&ntree, newnode);
|
||||
|
||||
if (!newnode->parent) {
|
||||
newnode->locx += offset.x;
|
||||
newnode->locy += offset.y;
|
||||
}
|
||||
}
|
||||
if (!make_copy) {
|
||||
nodeRebuildIDVector(&ngroup);
|
||||
}
|
||||
|
||||
/* add internal links to the ntree */
|
||||
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ngroup.links) {
|
||||
|
@ -512,9 +504,9 @@ static bool node_group_separate_selected(
|
|||
/* make a copy of internal links */
|
||||
if (fromselect && toselect) {
|
||||
nodeAddLink(&ntree,
|
||||
node_map.lookup(link->fromnode),
|
||||
ntree.node_by_id(link->fromnode->identifier),
|
||||
socket_map.lookup(link->fromsock),
|
||||
node_map.lookup(link->tonode),
|
||||
ntree.node_by_id(link->tonode->identifier),
|
||||
socket_map.lookup(link->tosock));
|
||||
}
|
||||
}
|
||||
|
@ -642,97 +634,97 @@ void NODE_OT_group_separate(wmOperatorType *ot)
|
|||
/** \name Make Group Operator
|
||||
* \{ */
|
||||
|
||||
static bool node_group_make_use_node(const bNode &node, bNode *gnode)
|
||||
static VectorSet<bNode *> get_nodes_to_group(bNodeTree &node_tree, bNode *group_node)
|
||||
{
|
||||
return (&node != gnode && !ELEM(node.type, NODE_GROUP_INPUT, NODE_GROUP_OUTPUT) &&
|
||||
(node.flag & NODE_SELECT));
|
||||
VectorSet<bNode *> nodes_to_group = get_selected_nodes(node_tree);
|
||||
nodes_to_group.remove_if(
|
||||
[](bNode *node) { return node->is_group_input() || node->is_group_output(); });
|
||||
nodes_to_group.remove(group_node);
|
||||
return nodes_to_group;
|
||||
}
|
||||
|
||||
static bool node_group_make_test_selected(bNodeTree &ntree,
|
||||
bNode *gnode,
|
||||
const VectorSet<bNode *> &nodes_to_group,
|
||||
const char *ntree_idname,
|
||||
ReportList &reports)
|
||||
{
|
||||
int ok = true;
|
||||
|
||||
if (nodes_to_group.is_empty()) {
|
||||
return false;
|
||||
}
|
||||
/* make a local pseudo node tree to pass to the node poll functions */
|
||||
bNodeTree *ngroup = ntreeAddTree(nullptr, "Pseudo Node Group", ntree_idname);
|
||||
BLI_SCOPED_DEFER([&]() {
|
||||
ntreeFreeTree(ngroup);
|
||||
MEM_freeN(ngroup);
|
||||
});
|
||||
|
||||
/* check poll functions for selected nodes */
|
||||
for (bNode *node : ntree.all_nodes()) {
|
||||
if (node_group_make_use_node(*node, gnode)) {
|
||||
const char *disabled_hint = nullptr;
|
||||
if (node->typeinfo->poll_instance &&
|
||||
!node->typeinfo->poll_instance(node, ngroup, &disabled_hint)) {
|
||||
if (disabled_hint) {
|
||||
BKE_reportf(&reports,
|
||||
RPT_WARNING,
|
||||
"Can not add node '%s' in a group:\n %s",
|
||||
node->name,
|
||||
disabled_hint);
|
||||
}
|
||||
else {
|
||||
BKE_reportf(&reports, RPT_WARNING, "Can not add node '%s' in a group", node->name);
|
||||
}
|
||||
ok = false;
|
||||
break;
|
||||
for (bNode *node : nodes_to_group) {
|
||||
const char *disabled_hint = nullptr;
|
||||
if (node->typeinfo->poll_instance &&
|
||||
!node->typeinfo->poll_instance(node, ngroup, &disabled_hint)) {
|
||||
if (disabled_hint) {
|
||||
BKE_reportf(&reports,
|
||||
RPT_WARNING,
|
||||
"Can not add node '%s' in a group:\n %s",
|
||||
node->name,
|
||||
disabled_hint);
|
||||
}
|
||||
else {
|
||||
BKE_reportf(&reports, RPT_WARNING, "Can not add node '%s' in a group", node->name);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
node->runtime->done = 0;
|
||||
}
|
||||
|
||||
/* free local pseudo node tree again */
|
||||
ntreeFreeTree(ngroup);
|
||||
MEM_freeN(ngroup);
|
||||
if (!ok) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* check if all connections are OK, no unselected node has both
|
||||
* inputs and outputs to a selection */
|
||||
LISTBASE_FOREACH (bNodeLink *, link, &ntree.links) {
|
||||
if (node_group_make_use_node(*link->fromnode, gnode)) {
|
||||
link->tonode->runtime->done |= 1;
|
||||
}
|
||||
if (node_group_make_use_node(*link->tonode, gnode)) {
|
||||
link->fromnode->runtime->done |= 2;
|
||||
}
|
||||
}
|
||||
ntree.ensure_topology_cache();
|
||||
for (bNode *node : ntree.all_nodes()) {
|
||||
if (!(node->flag & NODE_SELECT) && node != gnode && node->runtime->done == 3) {
|
||||
if (nodes_to_group.contains(node)) {
|
||||
continue;
|
||||
}
|
||||
auto sockets_connected_to_group = [&](const Span<bNodeSocket *> sockets) {
|
||||
for (const bNodeSocket *socket : sockets) {
|
||||
for (const bNodeSocket *other_socket : socket->directly_linked_sockets()) {
|
||||
if (nodes_to_group.contains(const_cast<bNode *>(&other_socket->owner_node()))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
if (sockets_connected_to_group(node->input_sockets()) &&
|
||||
sockets_connected_to_group(node->output_sockets())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int node_get_selected_minmax(
|
||||
bNodeTree &ntree, bNode *gnode, float2 &min, float2 &max, bool use_size)
|
||||
static void get_min_max_of_nodes(const Span<bNode *> nodes,
|
||||
const bool use_size,
|
||||
float2 &min,
|
||||
float2 &max)
|
||||
{
|
||||
int totselect = 0;
|
||||
if (nodes.is_empty()) {
|
||||
min = float2(0);
|
||||
max = float2(0);
|
||||
return;
|
||||
}
|
||||
|
||||
INIT_MINMAX2(min, max);
|
||||
for (const bNode *node : ntree.all_nodes()) {
|
||||
if (node_group_make_use_node(*node, gnode)) {
|
||||
float2 loc;
|
||||
nodeToView(node, node->offsetx, node->offsety, &loc.x, &loc.y);
|
||||
for (const bNode *node : nodes) {
|
||||
float2 loc;
|
||||
nodeToView(node, node->offsetx, node->offsety, &loc.x, &loc.y);
|
||||
math::min_max(loc, min, max);
|
||||
if (use_size) {
|
||||
loc.x += node->width;
|
||||
loc.y -= node->height;
|
||||
math::min_max(loc, min, max);
|
||||
if (use_size) {
|
||||
loc.x += node->width;
|
||||
loc.y -= node->height;
|
||||
math::min_max(loc, min, max);
|
||||
}
|
||||
totselect++;
|
||||
}
|
||||
}
|
||||
|
||||
/* sane min/max if no selected nodes */
|
||||
if (totselect == 0) {
|
||||
min[0] = min[1] = max[0] = max[1] = 0.0f;
|
||||
}
|
||||
|
||||
return totselect;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -803,11 +795,14 @@ static void node_group_make_redirect_incoming_link(
|
|||
}
|
||||
}
|
||||
|
||||
static void node_group_make_insert_selected(const bContext &C, bNodeTree &ntree, bNode *gnode)
|
||||
static void node_group_make_insert_selected(const bContext &C,
|
||||
bNodeTree &ntree,
|
||||
bNode *gnode,
|
||||
const VectorSet<bNode *> &nodes_to_move)
|
||||
{
|
||||
Main *bmain = CTX_data_main(&C);
|
||||
bNodeTree *ngroup = (bNodeTree *)gnode->id;
|
||||
bool expose_visible = false;
|
||||
BLI_assert(!nodes_to_move.contains(gnode));
|
||||
|
||||
/* XXX rough guess, not nice but we don't have access to UI constants here ... */
|
||||
static const float offsetx = 200;
|
||||
|
@ -818,18 +813,16 @@ static void node_group_make_insert_selected(const bContext &C, bNodeTree &ntree,
|
|||
nodeSetSelected(node, false);
|
||||
}
|
||||
|
||||
/* auto-add interface for "solo" nodes */
|
||||
const bool expose_visible = nodes_to_move.size() == 1;
|
||||
|
||||
float2 center, min, max;
|
||||
const int totselect = node_get_selected_minmax(ntree, gnode, min, max, false);
|
||||
get_min_max_of_nodes(nodes_to_move, false, min, max);
|
||||
add_v2_v2v2(center, min, max);
|
||||
mul_v2_fl(center, 0.5f);
|
||||
|
||||
float2 real_min, real_max;
|
||||
node_get_selected_minmax(ntree, gnode, real_min, real_max, true);
|
||||
|
||||
/* auto-add interface for "solo" nodes */
|
||||
if (totselect == 1) {
|
||||
expose_visible = true;
|
||||
}
|
||||
get_min_max_of_nodes(nodes_to_move, true, real_min, real_max);
|
||||
|
||||
ListBase anim_basepaths = {nullptr, nullptr};
|
||||
|
||||
|
@ -839,44 +832,42 @@ static void node_group_make_insert_selected(const bContext &C, bNodeTree &ntree,
|
|||
if (node->parent == nullptr) {
|
||||
continue;
|
||||
}
|
||||
if (node_group_make_use_node(*node->parent, gnode) &&
|
||||
!node_group_make_use_node(*node, gnode)) {
|
||||
if (nodes_to_move.contains(node->parent) && nodes_to_move.contains(node)) {
|
||||
nodeDetachNode(&ntree, node);
|
||||
}
|
||||
}
|
||||
|
||||
/* move nodes over */
|
||||
LISTBASE_FOREACH_MUTABLE (bNode *, node, &ntree.nodes) {
|
||||
if (node_group_make_use_node(*node, gnode)) {
|
||||
/* Keep track of this node's RNA "base" path (the part of the pat identifying the node)
|
||||
* if the old node-tree has animation data which potentially covers this node. */
|
||||
if (ntree.adt) {
|
||||
PointerRNA ptr;
|
||||
char *path;
|
||||
for (bNode *node : nodes_to_move) {
|
||||
/* Keep track of this node's RNA "base" path (the part of the pat identifying the node)
|
||||
* if the old node-tree has animation data which potentially covers this node. */
|
||||
if (ntree.adt) {
|
||||
PointerRNA ptr;
|
||||
char *path;
|
||||
|
||||
RNA_pointer_create(&ntree.id, &RNA_Node, node, &ptr);
|
||||
path = RNA_path_from_ID_to_struct(&ptr);
|
||||
RNA_pointer_create(&ntree.id, &RNA_Node, node, &ptr);
|
||||
path = RNA_path_from_ID_to_struct(&ptr);
|
||||
|
||||
if (path) {
|
||||
BLI_addtail(&anim_basepaths, animation_basepath_change_new(path, path));
|
||||
}
|
||||
if (path) {
|
||||
BLI_addtail(&anim_basepaths, animation_basepath_change_new(path, path));
|
||||
}
|
||||
|
||||
/* ensure valid parent pointers, detach if parent stays outside the group */
|
||||
if (node->parent && !(node->parent->flag & NODE_SELECT)) {
|
||||
nodeDetachNode(&ntree, node);
|
||||
}
|
||||
|
||||
/* change node-collection membership */
|
||||
BLI_remlink(&ntree.nodes, node);
|
||||
BLI_addtail(&ngroup->nodes, node);
|
||||
nodeUniqueID(ngroup, node);
|
||||
nodeUniqueName(ngroup, node);
|
||||
|
||||
BKE_ntree_update_tag_node_removed(&ntree);
|
||||
BKE_ntree_update_tag_node_new(ngroup, node);
|
||||
}
|
||||
|
||||
/* ensure valid parent pointers, detach if parent stays outside the group */
|
||||
if (node->parent && !(node->parent->flag & NODE_SELECT)) {
|
||||
nodeDetachNode(&ntree, node);
|
||||
}
|
||||
|
||||
/* change node-collection membership */
|
||||
BLI_remlink(&ntree.nodes, node);
|
||||
BLI_addtail(&ngroup->nodes, node);
|
||||
nodeUniqueID(ngroup, node);
|
||||
nodeUniqueName(ngroup, node);
|
||||
|
||||
BKE_ntree_update_tag_node_removed(&ntree);
|
||||
BKE_ntree_update_tag_node_new(ngroup, node);
|
||||
}
|
||||
nodeRebuildIDVector(&ntree);
|
||||
|
||||
/* move animation data over */
|
||||
if (ntree.adt) {
|
||||
|
@ -904,8 +895,8 @@ static void node_group_make_insert_selected(const bContext &C, bNodeTree &ntree,
|
|||
Map<bNodeSocket *, bNodeSocket *> reusable_sockets;
|
||||
|
||||
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree.links) {
|
||||
const bool fromselect = node_group_make_use_node(*link->fromnode, gnode);
|
||||
const bool toselect = node_group_make_use_node(*link->tonode, gnode);
|
||||
const bool fromselect = nodes_to_move.contains(link->fromnode);
|
||||
const bool toselect = nodes_to_move.contains(link->tonode);
|
||||
|
||||
if ((fromselect && link->tonode == gnode) || (toselect && link->fromnode == gnode)) {
|
||||
/* remove all links to/from the gnode.
|
||||
|
@ -969,8 +960,8 @@ static void node_group_make_insert_selected(const bContext &C, bNodeTree &ntree,
|
|||
|
||||
/* move internal links */
|
||||
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree.links) {
|
||||
const bool fromselect = node_group_make_use_node(*link->fromnode, gnode);
|
||||
const bool toselect = node_group_make_use_node(*link->tonode, gnode);
|
||||
const bool fromselect = nodes_to_move.contains(link->fromnode);
|
||||
const bool toselect = nodes_to_move.contains(link->tonode);
|
||||
|
||||
if (fromselect && toselect) {
|
||||
BLI_remlink(&ntree.links, link);
|
||||
|
@ -979,8 +970,8 @@ static void node_group_make_insert_selected(const bContext &C, bNodeTree &ntree,
|
|||
}
|
||||
|
||||
/* move nodes in the group to the center */
|
||||
for (bNode *node : ngroup->all_nodes()) {
|
||||
if (node_group_make_use_node(*node, gnode) && !node->parent) {
|
||||
for (bNode *node : nodes_to_move) {
|
||||
if (!node->parent) {
|
||||
node->locx -= center[0];
|
||||
node->locy -= center[1];
|
||||
}
|
||||
|
@ -988,73 +979,67 @@ static void node_group_make_insert_selected(const bContext &C, bNodeTree &ntree,
|
|||
|
||||
/* Expose all unlinked sockets too but only the visible ones. */
|
||||
if (expose_visible) {
|
||||
for (bNode *node : ngroup->all_nodes()) {
|
||||
if (node_group_make_use_node(*node, gnode)) {
|
||||
LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
|
||||
bool skip = false;
|
||||
LISTBASE_FOREACH (bNodeLink *, link, &ngroup->links) {
|
||||
if (link->tosock == sock) {
|
||||
skip = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (sock->flag & (SOCK_HIDDEN | SOCK_UNAVAIL)) {
|
||||
for (bNode *node : nodes_to_move) {
|
||||
LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
|
||||
bool skip = false;
|
||||
LISTBASE_FOREACH (bNodeLink *, link, &ngroup->links) {
|
||||
if (link->tosock == sock) {
|
||||
skip = true;
|
||||
break;
|
||||
}
|
||||
if (skip) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bNodeSocket *iosock = ntreeAddSocketInterfaceFromSocket(ngroup, node, sock);
|
||||
|
||||
node_group_input_update(ngroup, input_node);
|
||||
|
||||
/* create new internal link */
|
||||
bNodeSocket *input_sock = node_group_input_find_socket(input_node, iosock->identifier);
|
||||
nodeAddLink(ngroup, input_node, input_sock, node, sock);
|
||||
}
|
||||
if (sock->flag & (SOCK_HIDDEN | SOCK_UNAVAIL)) {
|
||||
skip = true;
|
||||
}
|
||||
if (skip) {
|
||||
continue;
|
||||
}
|
||||
|
||||
LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
|
||||
bool skip = false;
|
||||
LISTBASE_FOREACH (bNodeLink *, link, &ngroup->links) {
|
||||
if (link->fromsock == sock) {
|
||||
skip = true;
|
||||
}
|
||||
}
|
||||
if (sock->flag & (SOCK_HIDDEN | SOCK_UNAVAIL)) {
|
||||
bNodeSocket *iosock = ntreeAddSocketInterfaceFromSocket(ngroup, node, sock);
|
||||
|
||||
node_group_input_update(ngroup, input_node);
|
||||
|
||||
/* create new internal link */
|
||||
bNodeSocket *input_sock = node_group_input_find_socket(input_node, iosock->identifier);
|
||||
nodeAddLink(ngroup, input_node, input_sock, node, sock);
|
||||
}
|
||||
|
||||
LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
|
||||
bool skip = false;
|
||||
LISTBASE_FOREACH (bNodeLink *, link, &ngroup->links) {
|
||||
if (link->fromsock == sock) {
|
||||
skip = true;
|
||||
}
|
||||
if (skip) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bNodeSocket *iosock = ntreeAddSocketInterfaceFromSocket(ngroup, node, sock);
|
||||
|
||||
node_group_output_update(ngroup, output_node);
|
||||
|
||||
/* create new internal link */
|
||||
bNodeSocket *output_sock = node_group_output_find_socket(output_node,
|
||||
iosock->identifier);
|
||||
nodeAddLink(ngroup, node, sock, output_node, output_sock);
|
||||
}
|
||||
if (sock->flag & (SOCK_HIDDEN | SOCK_UNAVAIL)) {
|
||||
skip = true;
|
||||
}
|
||||
if (skip) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bNodeSocket *iosock = ntreeAddSocketInterfaceFromSocket(ngroup, node, sock);
|
||||
|
||||
node_group_output_update(ngroup, output_node);
|
||||
|
||||
/* create new internal link */
|
||||
bNodeSocket *output_sock = node_group_output_find_socket(output_node, iosock->identifier);
|
||||
nodeAddLink(ngroup, node, sock, output_node, output_sock);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bNode *node_group_make_from_selected(const bContext &C,
|
||||
bNodeTree &ntree,
|
||||
const char *ntype,
|
||||
const char *ntreetype)
|
||||
static bNode *node_group_make_from_nodes(const bContext &C,
|
||||
bNodeTree &ntree,
|
||||
const VectorSet<bNode *> &nodes_to_group,
|
||||
const char *ntype,
|
||||
const char *ntreetype)
|
||||
{
|
||||
Main *bmain = CTX_data_main(&C);
|
||||
|
||||
float2 min, max;
|
||||
const int totselect = node_get_selected_minmax(ntree, nullptr, min, max, false);
|
||||
/* don't make empty group */
|
||||
if (totselect == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
get_min_max_of_nodes(nodes_to_group, false, min, max);
|
||||
|
||||
/* New node-tree. */
|
||||
bNodeTree *ngroup = ntreeAddTree(bmain, "NodeGroup", ntreetype);
|
||||
|
@ -1066,7 +1051,7 @@ static bNode *node_group_make_from_selected(const bContext &C,
|
|||
gnode->locx = 0.5f * (min[0] + max[0]);
|
||||
gnode->locy = 0.5f * (min[1] + max[1]);
|
||||
|
||||
node_group_make_insert_selected(C, ntree, gnode);
|
||||
node_group_make_insert_selected(C, ntree, gnode, nodes_to_group);
|
||||
|
||||
return gnode;
|
||||
}
|
||||
|
@ -1081,11 +1066,12 @@ static int node_group_make_exec(bContext *C, wmOperator *op)
|
|||
|
||||
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
|
||||
|
||||
if (!node_group_make_test_selected(ntree, nullptr, ntree_idname, *op->reports)) {
|
||||
VectorSet<bNode *> nodes_to_group = get_nodes_to_group(ntree, nullptr);
|
||||
if (!node_group_make_test_selected(ntree, nodes_to_group, ntree_idname, *op->reports)) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
bNode *gnode = node_group_make_from_selected(*C, ntree, node_idname, ntree_idname);
|
||||
bNode *gnode = node_group_make_from_nodes(*C, ntree, nodes_to_group, node_idname, ntree_idname);
|
||||
|
||||
if (gnode) {
|
||||
bNodeTree *ngroup = (bNodeTree *)gnode->id;
|
||||
|
@ -1137,17 +1123,17 @@ static int node_group_insert_exec(bContext *C, wmOperator *op)
|
|||
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
|
||||
|
||||
bNode *gnode = node_group_get_active(C, node_idname);
|
||||
|
||||
if (!gnode || !gnode->id) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
bNodeTree *ngroup = (bNodeTree *)gnode->id;
|
||||
if (!node_group_make_test_selected(*ntree, gnode, ngroup->idname, *op->reports)) {
|
||||
VectorSet<bNode *> nodes_to_group = get_nodes_to_group(*ntree, gnode);
|
||||
if (!node_group_make_test_selected(*ntree, nodes_to_group, ngroup->idname, *op->reports)) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
node_group_make_insert_selected(*C, *ntree, gnode);
|
||||
node_group_make_insert_selected(*C, *ntree, gnode, nodes_to_group);
|
||||
|
||||
nodeSetActive(ntree, gnode);
|
||||
ED_node_tree_push(snode, ngroup, gnode);
|
||||
|
|
|
@ -9,8 +9,8 @@
|
|||
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_math_vector.hh"
|
||||
#include "BLI_set.hh"
|
||||
#include "BLI_vector.hh"
|
||||
#include "BLI_vector_set.hh"
|
||||
|
||||
#include "BKE_node.h"
|
||||
|
||||
|
@ -183,7 +183,7 @@ void node_keymap(wmKeyConfig *keyconf);
|
|||
rctf node_frame_rect_inside(const bNode &node);
|
||||
bool node_or_socket_isect_event(const bContext &C, const wmEvent &event);
|
||||
|
||||
Set<bNode *> get_selected_nodes(bNodeTree &node_tree);
|
||||
VectorSet<bNode *> get_selected_nodes(bNodeTree &node_tree);
|
||||
void node_deselect_all(SpaceNode &snode);
|
||||
void node_socket_select(bNode *node, bNodeSocket &sock);
|
||||
void node_socket_deselect(bNode *node, bNodeSocket &sock, bool deselect_node);
|
||||
|
|
|
@ -1630,40 +1630,42 @@ void NODE_OT_parent_set(wmOperatorType *ot)
|
|||
/** \name Join Nodes Operator
|
||||
* \{ */
|
||||
|
||||
/* tags for depth-first search */
|
||||
#define NODE_JOIN_DONE 1
|
||||
#define NODE_JOIN_IS_DESCENDANT 2
|
||||
struct NodeJoinState {
|
||||
bool done;
|
||||
bool descendent;
|
||||
};
|
||||
|
||||
static void node_join_attach_recursive(bNodeTree &ntree,
|
||||
MutableSpan<NodeJoinState> join_states,
|
||||
bNode *node,
|
||||
bNode *frame,
|
||||
const Set<bNode *> &selected_nodes)
|
||||
const VectorSet<bNode *> &selected_nodes)
|
||||
{
|
||||
node->runtime->done |= NODE_JOIN_DONE;
|
||||
join_states[node->runtime->index_in_tree].done = true;
|
||||
|
||||
if (node == frame) {
|
||||
node->runtime->done |= NODE_JOIN_IS_DESCENDANT;
|
||||
join_states[node->runtime->index_in_tree].descendent = true;
|
||||
}
|
||||
else if (node->parent) {
|
||||
/* call recursively */
|
||||
if (!(node->parent->runtime->done & NODE_JOIN_DONE)) {
|
||||
node_join_attach_recursive(ntree, node->parent, frame, selected_nodes);
|
||||
if (!join_states[node->parent->runtime->index_in_tree].done) {
|
||||
node_join_attach_recursive(ntree, join_states, node->parent, frame, selected_nodes);
|
||||
}
|
||||
|
||||
/* in any case: if the parent is a descendant, so is the child */
|
||||
if (node->parent->runtime->done & NODE_JOIN_IS_DESCENDANT) {
|
||||
node->runtime->done |= NODE_JOIN_IS_DESCENDANT;
|
||||
if (join_states[node->parent->runtime->index_in_tree].descendent) {
|
||||
join_states[node->runtime->index_in_tree].descendent = true;
|
||||
}
|
||||
else if (selected_nodes.contains(node)) {
|
||||
/* if parent is not an descendant of the frame, reattach the node */
|
||||
nodeDetachNode(&ntree, node);
|
||||
nodeAttachNode(&ntree, node, frame);
|
||||
node->runtime->done |= NODE_JOIN_IS_DESCENDANT;
|
||||
join_states[node->runtime->index_in_tree].descendent = true;
|
||||
}
|
||||
}
|
||||
else if (selected_nodes.contains(node)) {
|
||||
nodeAttachNode(&ntree, node, frame);
|
||||
node->runtime->done |= NODE_JOIN_IS_DESCENDANT;
|
||||
join_states[node->runtime->index_in_tree].descendent = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1673,19 +1675,16 @@ static int node_join_exec(bContext *C, wmOperator * /*op*/)
|
|||
SpaceNode &snode = *CTX_wm_space_node(C);
|
||||
bNodeTree &ntree = *snode.edittree;
|
||||
|
||||
const Set<bNode *> selected_nodes = get_selected_nodes(ntree);
|
||||
const VectorSet<bNode *> selected_nodes = get_selected_nodes(ntree);
|
||||
|
||||
bNode *frame_node = nodeAddStaticNode(C, &ntree, NODE_FRAME);
|
||||
nodeSetActive(&ntree, frame_node);
|
||||
|
||||
/* reset tags */
|
||||
for (bNode *node : ntree.all_nodes()) {
|
||||
node->runtime->done = 0;
|
||||
}
|
||||
Array<NodeJoinState> join_states(ntree.all_nodes().size(), NodeJoinState{false, false});
|
||||
|
||||
for (bNode *node : ntree.all_nodes()) {
|
||||
if (!(node->runtime->done & NODE_JOIN_DONE)) {
|
||||
node_join_attach_recursive(ntree, node, frame_node, selected_nodes);
|
||||
if (!join_states[node->runtime->index_in_tree].done) {
|
||||
node_join_attach_recursive(ntree, join_states, node, frame_node, selected_nodes);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1808,32 +1807,35 @@ void NODE_OT_attach(wmOperatorType *ot)
|
|||
/** \name Detach Operator
|
||||
* \{ */
|
||||
|
||||
/* tags for depth-first search */
|
||||
#define NODE_DETACH_DONE 1
|
||||
#define NODE_DETACH_IS_DESCENDANT 2
|
||||
struct NodeDetachstate {
|
||||
bool done;
|
||||
bool descendent;
|
||||
};
|
||||
|
||||
static void node_detach_recursive(bNodeTree &ntree, bNode *node)
|
||||
static void node_detach_recursive(bNodeTree &ntree,
|
||||
MutableSpan<NodeDetachstate> detach_states,
|
||||
bNode *node)
|
||||
{
|
||||
node->runtime->done |= NODE_DETACH_DONE;
|
||||
detach_states[node->runtime->index_in_tree].done = true;
|
||||
|
||||
if (node->parent) {
|
||||
/* call recursively */
|
||||
if (!(node->parent->runtime->done & NODE_DETACH_DONE)) {
|
||||
node_detach_recursive(ntree, node->parent);
|
||||
if (!detach_states[node->parent->runtime->index_in_tree].done) {
|
||||
node_detach_recursive(ntree, detach_states, node->parent);
|
||||
}
|
||||
|
||||
/* in any case: if the parent is a descendant, so is the child */
|
||||
if (node->parent->runtime->done & NODE_DETACH_IS_DESCENDANT) {
|
||||
node->runtime->done |= NODE_DETACH_IS_DESCENDANT;
|
||||
if (detach_states[node->parent->runtime->index_in_tree].descendent) {
|
||||
detach_states[node->runtime->index_in_tree].descendent = true;
|
||||
}
|
||||
else if (node->flag & NODE_SELECT) {
|
||||
/* if parent is not a descendant of a selected node, detach */
|
||||
nodeDetachNode(&ntree, node);
|
||||
node->runtime->done |= NODE_DETACH_IS_DESCENDANT;
|
||||
detach_states[node->runtime->index_in_tree].descendent = true;
|
||||
}
|
||||
}
|
||||
else if (node->flag & NODE_SELECT) {
|
||||
node->runtime->done |= NODE_DETACH_IS_DESCENDANT;
|
||||
detach_states[node->runtime->index_in_tree].descendent = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1843,16 +1845,14 @@ static int node_detach_exec(bContext *C, wmOperator * /*op*/)
|
|||
SpaceNode &snode = *CTX_wm_space_node(C);
|
||||
bNodeTree &ntree = *snode.edittree;
|
||||
|
||||
/* reset tags */
|
||||
for (bNode *node : ntree.all_nodes()) {
|
||||
node->runtime->done = 0;
|
||||
}
|
||||
Array<NodeDetachstate> detach_states(ntree.all_nodes().size(), NodeDetachstate{false, false});
|
||||
|
||||
/* detach nodes recursively
|
||||
* relative order is preserved here!
|
||||
*/
|
||||
for (bNode *node : ntree.all_nodes()) {
|
||||
if (!(node->runtime->done & NODE_DETACH_DONE)) {
|
||||
node_detach_recursive(ntree, node);
|
||||
if (!detach_states[node->runtime->index_in_tree].done) {
|
||||
node_detach_recursive(ntree, detach_states, node);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -312,9 +312,9 @@ void node_deselect_all_output_sockets(SpaceNode &snode, const bool deselect_node
|
|||
}
|
||||
}
|
||||
|
||||
Set<bNode *> get_selected_nodes(bNodeTree &node_tree)
|
||||
VectorSet<bNode *> get_selected_nodes(bNodeTree &node_tree)
|
||||
{
|
||||
Set<bNode *> selected_nodes;
|
||||
VectorSet<bNode *> selected_nodes;
|
||||
for (bNode *node : node_tree.all_nodes()) {
|
||||
if (node->flag & NODE_SELECT) {
|
||||
selected_nodes.add(node);
|
||||
|
@ -1142,7 +1142,7 @@ static int node_select_linked_to_exec(bContext *C, wmOperator * /*op*/)
|
|||
|
||||
node_tree.ensure_topology_cache();
|
||||
|
||||
Set<bNode *> initial_selection = get_selected_nodes(node_tree);
|
||||
VectorSet<bNode *> initial_selection = get_selected_nodes(node_tree);
|
||||
|
||||
for (bNode *node : initial_selection) {
|
||||
for (bNodeSocket *output_socket : node->output_sockets()) {
|
||||
|
@ -1192,7 +1192,7 @@ static int node_select_linked_from_exec(bContext *C, wmOperator * /*op*/)
|
|||
|
||||
node_tree.ensure_topology_cache();
|
||||
|
||||
Set<bNode *> initial_selection = get_selected_nodes(node_tree);
|
||||
VectorSet<bNode *> initial_selection = get_selected_nodes(node_tree);
|
||||
|
||||
for (bNode *node : initial_selection) {
|
||||
for (bNodeSocket *input_socket : node->input_sockets()) {
|
||||
|
|
|
@ -469,7 +469,9 @@ Vector<GVArray> evaluate_fields(ResourceScope &scope,
|
|||
}
|
||||
/* Still have to copy over the data in the destination provided by the caller. */
|
||||
if (dst_varray.is_span()) {
|
||||
array_utils::copy(computed_varray, mask, dst_varray.get_internal_span());
|
||||
array_utils::copy(computed_varray,
|
||||
mask,
|
||||
dst_varray.get_internal_span().take_front(mask.min_array_size()));
|
||||
}
|
||||
else {
|
||||
/* Slower materialize into a different structure. */
|
||||
|
|
|
@ -446,10 +446,12 @@ static void flatten_group_do(bNodeTree *ntree, bNode *gnode)
|
|||
/* migrate node */
|
||||
BLI_remlink(&ngroup->nodes, node);
|
||||
BLI_addtail(&ntree->nodes, node);
|
||||
nodeUniqueID(ntree, node);
|
||||
/* ensure unique node name in the node tree */
|
||||
/* This is very slow and it has no use for GPU nodetree. (see T70609) */
|
||||
// nodeUniqueName(ntree, node);
|
||||
}
|
||||
ngroup->runtime->nodes_by_id.clear();
|
||||
|
||||
/* Save first and last link to iterate over flattened group links. */
|
||||
bNodeLink *glinks_first = static_cast<bNodeLink *>(ntree->links.last);
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
#include "DNA_node_types.h"
|
||||
|
||||
#include "BKE_node_runtime.hh"
|
||||
|
||||
#include "node_shader_util.hh"
|
||||
|
||||
#include "NOD_socket_search_link.hh"
|
||||
|
|
Loading…
Reference in New Issue