Merge remote-tracking branch 'origin' into temp-sculpt-roll-mapping

This commit is contained in:
Joseph Eagar 2022-12-30 14:22:01 -08:00
commit 4faa5e30a5
10 changed files with 330 additions and 379 deletions

View File

@ -2398,7 +2398,7 @@ compile_LLVM() {
fi
# To be changed each time we make edits that would modify the compiled result!
llvm_magic=3
llvm_magic=4
_init_llvm
# Force having own builds for the dependencies.
@ -2447,9 +2447,9 @@ compile_LLVM() {
mkdir build
cd build
LLVM_TARGETS="X86"
LLVM_TARGETS="X86;NVPTX"
if [ $(uname -m) == "aarch64" ]; then
LLVM_TARGETS="AArch64"
LLVM_TARGETS="AArch64;NVPTX"
fi
cmake_d="-D CMAKE_BUILD_TYPE=Release"
@ -2516,7 +2516,7 @@ compile_OSL() {
fi
# To be changed each time we make edits that would modify the compiled result!
osl_magic=21
osl_magic=22
_init_osl
# Force having own builds for the dependencies.
@ -2547,8 +2547,9 @@ compile_OSL() {
INFO "Unpacking OpenShadingLanguage-$OSL_VERSION"
tar -C $SRC --transform "s,(.*/?)OpenShadingLanguage-[^/]*(.*),\1OpenShadingLanguage-$OSL_VERSION\2,x" \
-xf $_src.tar.gz
patch -d $_src -p1 < $SCRIPT_DIR/patches/osl.diff
fi
patch -d $_src -p1 < $SCRIPT_DIR/patches/osl.diff
fi
cd $_src
@ -2560,6 +2561,8 @@ compile_OSL() {
# Stick to same rev as windows' libs...
git checkout $OSL_SOURCE_REPO_UID
git reset --hard
patch -d $_src -p1 < $SCRIPT_DIR/patches/osl.diff
fi
# Always refresh the whole build!

View File

@ -713,6 +713,8 @@ bNode *node_copy_with_mapping(bNodeTree *dst_tree,
bNode *node_copy(bNodeTree *dst_tree, const bNode &src_node, int flag, bool use_unique);
void node_free_node(bNodeTree *tree, bNode *node);
} // namespace blender::bke
#endif
@ -865,20 +867,6 @@ bool nodeDeclarationEnsureOnOutdatedNode(struct bNodeTree *ntree, struct bNode *
*/
void nodeSocketDeclarationsUpdate(struct bNode *node);
/**
* Node Clipboard.
*/
void BKE_node_clipboard_clear(void);
void BKE_node_clipboard_free(void);
/**
* Return false when one or more ID's are lost.
*/
bool BKE_node_clipboard_validate(void);
void BKE_node_clipboard_add_node(struct bNode *node);
void BKE_node_clipboard_add_link(struct bNodeLink *link);
const struct ListBase *BKE_node_clipboard_get_nodes(void);
const struct ListBase *BKE_node_clipboard_get_links(void);
/**
* Node Instance Hash.
*/

View File

@ -118,7 +118,6 @@ static CLG_LogRef LOG = {"bke.node"};
static void ntree_set_typeinfo(bNodeTree *ntree, bNodeTreeType *typeinfo);
static void node_socket_copy(bNodeSocket *sock_dst, const bNodeSocket *sock_src, const int flag);
static void free_localized_node_groups(bNodeTree *ntree);
static void node_free_node(bNodeTree *ntree, bNode *node);
static void node_socket_interface_free(bNodeTree * /*ntree*/,
bNodeSocket *sock,
const bool do_id_user);
@ -243,7 +242,7 @@ static void ntree_free_data(ID *id)
BLI_freelistN(&ntree->links);
LISTBASE_FOREACH_MUTABLE (bNode *, node, &ntree->nodes) {
node_free_node(ntree, node);
blender::bke::node_free_node(ntree, node);
}
/* free interface sockets */
@ -2951,12 +2950,14 @@ void nodeRebuildIDVector(bNodeTree *node_tree)
}
}
namespace blender::bke {
/**
* Free the node itself.
*
* \note: ID user refcounting and changing the `nodes_by_id` vector are up to the caller.
*/
static void node_free_node(bNodeTree *ntree, bNode *node)
void node_free_node(bNodeTree *ntree, bNode *node)
{
/* since it is called while free database, node->id is undefined */
@ -3011,6 +3012,8 @@ static void node_free_node(bNodeTree *ntree, bNode *node)
}
}
} // namespace blender::bke
void ntreeFreeLocalNode(bNodeTree *ntree, bNode *node)
{
/* For removing nodes while editing localized node trees. */
@ -3021,7 +3024,7 @@ void ntreeFreeLocalNode(bNodeTree *ntree, bNode *node)
nodeUnlinkNode(ntree, node);
node_unlink_attached(ntree, node);
node_free_node(ntree, node);
blender::bke::node_free_node(ntree, node);
nodeRebuildIDVector(ntree);
}
@ -3081,7 +3084,7 @@ void nodeRemoveNode(Main *bmain, bNodeTree *ntree, bNode *node, bool do_id_user)
node_unlink_attached(ntree, node);
/* Free node itself. */
node_free_node(ntree, node);
blender::bke::node_free_node(ntree, node);
nodeRebuildIDVector(ntree);
}
@ -3649,149 +3652,6 @@ void nodeInternalLinks(bNode *node, bNodeLink ***r_links, int *r_len)
*r_len = node->runtime->internal_links.size();
}
/* ************** Node Clipboard *********** */
#define USE_NODE_CB_VALIDATE
#ifdef USE_NODE_CB_VALIDATE
/**
* This data structure is to validate the node on creation,
* otherwise we may reference missing data.
*
* Currently its only used for ID's, but nodes may one day
* reference other pointers which need validation.
*/
struct bNodeClipboardExtraInfo {
struct bNodeClipboardExtraInfo *next, *prev;
ID *id;
char id_name[MAX_ID_NAME];
char library_name[FILE_MAX];
};
#endif /* USE_NODE_CB_VALIDATE */
struct bNodeClipboard {
ListBase nodes;
#ifdef USE_NODE_CB_VALIDATE
ListBase nodes_extra_info;
#endif
ListBase links;
};
static bNodeClipboard node_clipboard = {{nullptr}};
void BKE_node_clipboard_clear()
{
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &node_clipboard.links) {
nodeRemLink(nullptr, link);
}
BLI_listbase_clear(&node_clipboard.links);
LISTBASE_FOREACH_MUTABLE (bNode *, node, &node_clipboard.nodes) {
node_free_node(nullptr, node);
}
BLI_listbase_clear(&node_clipboard.nodes);
#ifdef USE_NODE_CB_VALIDATE
BLI_freelistN(&node_clipboard.nodes_extra_info);
#endif
}
bool BKE_node_clipboard_validate()
{
bool ok = true;
#ifdef USE_NODE_CB_VALIDATE
bNodeClipboardExtraInfo *node_info;
bNode *node;
/* lists must be aligned */
BLI_assert(BLI_listbase_count(&node_clipboard.nodes) ==
BLI_listbase_count(&node_clipboard.nodes_extra_info));
for (node = (bNode *)node_clipboard.nodes.first,
node_info = (bNodeClipboardExtraInfo *)node_clipboard.nodes_extra_info.first;
node;
node = (bNode *)node->next, node_info = (bNodeClipboardExtraInfo *)node_info->next) {
/* validate the node against the stored node info */
/* re-assign each loop since we may clear,
* open a new file where the ID is valid, and paste again */
node->id = node_info->id;
/* currently only validate the ID */
if (node->id) {
/* We want to search into current blend file, so using G_MAIN is valid here too. */
ListBase *lb = which_libbase(G_MAIN, GS(node_info->id_name));
BLI_assert(lb != nullptr);
if (BLI_findindex(lb, node_info->id) == -1) {
/* May assign null. */
node->id = (ID *)BLI_findstring(lb, node_info->id_name + 2, offsetof(ID, name) + 2);
if (node->id == nullptr) {
ok = false;
}
}
}
}
#endif /* USE_NODE_CB_VALIDATE */
return ok;
}
void BKE_node_clipboard_add_node(bNode *node)
{
#ifdef USE_NODE_CB_VALIDATE
/* add extra info */
bNodeClipboardExtraInfo *node_info = (bNodeClipboardExtraInfo *)MEM_mallocN(
sizeof(bNodeClipboardExtraInfo), __func__);
node_info->id = node->id;
if (node->id) {
BLI_strncpy(node_info->id_name, node->id->name, sizeof(node_info->id_name));
if (ID_IS_LINKED(node->id)) {
BLI_strncpy(
node_info->library_name, node->id->lib->filepath_abs, sizeof(node_info->library_name));
}
else {
node_info->library_name[0] = '\0';
}
}
else {
node_info->id_name[0] = '\0';
node_info->library_name[0] = '\0';
}
BLI_addtail(&node_clipboard.nodes_extra_info, node_info);
/* end extra info */
#endif /* USE_NODE_CB_VALIDATE */
/* add node */
BLI_addtail(&node_clipboard.nodes, node);
}
void BKE_node_clipboard_add_link(bNodeLink *link)
{
BLI_addtail(&node_clipboard.links, link);
}
const ListBase *BKE_node_clipboard_get_nodes()
{
return &node_clipboard.nodes;
}
const ListBase *BKE_node_clipboard_get_links()
{
return &node_clipboard.links;
}
void BKE_node_clipboard_free()
{
BKE_node_clipboard_validate();
BKE_node_clipboard_clear();
}
/* Node Instance Hash */
const bNodeInstanceKey NODE_INSTANCE_KEY_BASE = {5381};

View File

@ -51,6 +51,10 @@ static void update_link_vector(const bNodeTree &ntree)
bNodeTreeRuntime &tree_runtime = *ntree.runtime;
tree_runtime.links.clear();
LISTBASE_FOREACH (bNodeLink *, link, &ntree.links) {
/* Check that the link connects nodes within this tree. */
BLI_assert(tree_runtime.nodes_by_id.contains(link->fromnode));
BLI_assert(tree_runtime.nodes_by_id.contains(link->tonode));
tree_runtime.links.append(link);
}
}

View File

@ -42,6 +42,10 @@ ENUM_OPERATORS(NodeBorder, NODE_RIGHT)
#define NODE_EDGE_PAN_DELAY 0.5f
#define NODE_EDGE_PAN_ZOOM_INFLUENCE 0.5f
/* clipboard.cc */
void ED_node_clipboard_free(void);
/* space_node.cc */
void ED_node_cursor_location_get(const struct SpaceNode *snode, float value[2]);

View File

@ -31,6 +31,7 @@ set(INC
set(SRC
add_menu_assets.cc
add_node_search.cc
clipboard.cc
drawnode.cc
link_drag_search.cc
node_add.cc

View File

@ -0,0 +1,301 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "DNA_space_types.h"
#include "BKE_context.h"
#include "BKE_global.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
#include "BKE_node.h"
#include "BKE_node_runtime.hh"
#include "BKE_node_tree_update.h"
#include "BKE_report.h"
#include "ED_node.h"
#include "ED_node.hh"
#include "ED_render.h"
#include "ED_screen.h"
#include "DEG_depsgraph_build.h"
#include "node_intern.hh"
namespace blender::ed::space_node {
struct NodeClipboardItem {
bNode *node;
/* Extra info to validate the node on creation. Otherwise we may reference missing data. */
ID *id;
std::string id_name;
std::string library_name;
};
struct NodeClipboard {
Vector<NodeClipboardItem> nodes;
Vector<bNodeLink> links;
void clear()
{
for (NodeClipboardItem &item : this->nodes) {
bke::node_free_node(nullptr, item.node);
}
this->nodes.clear_and_shrink();
this->links.clear_and_shrink();
}
/**
* Replace node IDs that are no longer available in the current file. Return false when one or
* more IDs are lost.
*/
bool validate()
{
bool ok = true;
for (NodeClipboardItem &item : this->nodes) {
bNode &node = *item.node;
/* Reassign each loop since we may clear, open a new file where the ID is valid, and paste
* again. */
node.id = item.id;
if (node.id) {
const ListBase *lb = which_libbase(G_MAIN, GS(item.id_name.c_str()));
if (BLI_findindex(lb, item.id) == -1) {
/* May assign null. */
node.id = static_cast<ID *>(
BLI_findstring(lb, item.id_name.c_str() + 2, offsetof(ID, name) + 2));
if (!node.id) {
ok = false;
}
}
}
}
return ok;
}
void add_node(bNode *node)
{
NodeClipboardItem item;
item.node = node;
item.id = node->id;
if (item.id) {
item.id_name = node->id->name;
if (ID_IS_LINKED(node->id)) {
item.library_name = node->id->lib->filepath_abs;
}
}
this->nodes.append(std::move(item));
}
};
static NodeClipboard &get_node_clipboard()
{
static NodeClipboard clipboard;
return clipboard;
}
/* -------------------------------------------------------------------- */
/** \name Copy
* \{ */
static int node_clipboard_copy_exec(bContext *C, wmOperator * /*op*/)
{
SpaceNode &snode = *CTX_wm_space_node(C);
bNodeTree &tree = *snode.edittree;
NodeClipboard &clipboard = get_node_clipboard();
clipboard.clear();
Map<const bNode *, bNode *> node_map;
Map<const bNodeSocket *, bNodeSocket *> socket_map;
for (bNode *node : tree.all_nodes()) {
if (node->flag & SELECT) {
/* No ID reference counting, this node is virtual, detached from any actual Blender data. */
bNode *new_node = bke::node_copy_with_mapping(nullptr,
*node,
LIB_ID_CREATE_NO_USER_REFCOUNT |
LIB_ID_CREATE_NO_MAIN,
false,
socket_map);
node_map.add_new(node, new_node);
}
}
for (bNode *new_node : node_map.values()) {
clipboard.add_node(new_node);
/* Parent pointer must be redirected to new node or detached if parent is not copied. */
if (new_node->parent) {
if (node_map.contains(new_node->parent)) {
new_node->parent = node_map.lookup(new_node->parent);
}
else {
nodeDetachNode(&tree, new_node);
}
}
}
/* Copy links between selected nodes. */
LISTBASE_FOREACH (bNodeLink *, link, &tree.links) {
BLI_assert(link->tonode);
BLI_assert(link->fromnode);
if (link->tonode->flag & NODE_SELECT && link->fromnode->flag & NODE_SELECT) {
bNodeLink new_link{};
new_link.flag = link->flag;
new_link.tonode = node_map.lookup(link->tonode);
new_link.tosock = socket_map.lookup(link->tosock);
new_link.fromnode = node_map.lookup(link->fromnode);
new_link.fromsock = socket_map.lookup(link->fromsock);
new_link.multi_input_socket_index = link->multi_input_socket_index;
clipboard.links.append(new_link);
}
}
return OPERATOR_FINISHED;
}
void NODE_OT_clipboard_copy(wmOperatorType *ot)
{
ot->name = "Copy to Clipboard";
ot->description = "Copies selected nodes to the clipboard";
ot->idname = "NODE_OT_clipboard_copy";
ot->exec = node_clipboard_copy_exec;
ot->poll = ED_operator_node_active;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Paste
* \{ */
static int node_clipboard_paste_exec(bContext *C, wmOperator *op)
{
SpaceNode &snode = *CTX_wm_space_node(C);
bNodeTree &tree = *snode.edittree;
NodeClipboard &clipboard = get_node_clipboard();
const bool is_valid = clipboard.validate();
if (clipboard.nodes.is_empty()) {
BKE_report(op->reports, RPT_ERROR, "Clipboard is empty");
return OPERATOR_CANCELLED;
}
if (!is_valid) {
BKE_report(op->reports,
RPT_WARNING,
"Some nodes references could not be restored, will be left empty");
}
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
node_deselect_all(tree);
/* calculate "barycenter" for placing on mouse cursor */
float2 center = {0.0f, 0.0f};
for (const NodeClipboardItem &item : clipboard.nodes) {
center.x += BLI_rctf_cent_x(&item.node->runtime->totr);
center.y += BLI_rctf_cent_y(&item.node->runtime->totr);
}
center /= clipboard.nodes.size();
Map<const bNode *, bNode *> node_map;
Map<const bNodeSocket *, bNodeSocket *> socket_map;
/* copy valid nodes from clipboard */
for (NodeClipboardItem &item : clipboard.nodes) {
const bNode &node = *item.node;
const char *disabled_hint = nullptr;
if (node.typeinfo->poll_instance &&
node.typeinfo->poll_instance(&node, &tree, &disabled_hint)) {
bNode *new_node = bke::node_copy_with_mapping(
&tree, node, LIB_ID_COPY_DEFAULT, true, socket_map);
node_map.add_new(&node, new_node);
}
else {
if (disabled_hint) {
BKE_reportf(op->reports,
RPT_ERROR,
"Cannot add node %s into node tree %s: %s",
node.name,
tree.id.name + 2,
disabled_hint);
}
else {
BKE_reportf(op->reports,
RPT_ERROR,
"Cannot add node %s into node tree %s",
node.name,
tree.id.name + 2);
}
}
}
for (bNode *new_node : node_map.values()) {
nodeSetSelected(new_node, true);
/* The parent pointer must be redirected to new node. */
if (new_node->parent) {
if (node_map.contains(new_node->parent)) {
new_node->parent = node_map.lookup(new_node->parent);
}
}
}
/* Add links between existing nodes. */
for (const bNodeLink &link : clipboard.links) {
const bNode *fromnode = link.fromnode;
const bNode *tonode = link.tonode;
if (node_map.lookup_key_ptr(fromnode) && node_map.lookup_key_ptr(tonode)) {
bNodeLink *new_link = nodeAddLink(&tree,
node_map.lookup(fromnode),
socket_map.lookup(link.fromsock),
node_map.lookup(tonode),
socket_map.lookup(link.tosock));
new_link->multi_input_socket_index = link.multi_input_socket_index;
}
}
tree.ensure_topology_cache();
for (bNode *new_node : node_map.values()) {
/* Update multi input socket indices in case all connected nodes weren't copied. */
update_multi_input_indices_for_removed_links(*new_node);
}
Main *bmain = CTX_data_main(C);
ED_node_tree_propagate_change(C, bmain, &tree);
/* Pasting nodes can create arbitrary new relations because nodes can reference IDs. */
DEG_relations_tag_update(bmain);
return OPERATOR_FINISHED;
}
void NODE_OT_clipboard_paste(wmOperatorType *ot)
{
ot->name = "Paste from Clipboard";
ot->description = "Pastes nodes from the clipboard to the active node tree";
ot->idname = "NODE_OT_clipboard_paste";
ot->exec = node_clipboard_paste_exec;
ot->poll = ED_operator_node_editable;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/** \} */
} // namespace blender::ed::space_node
void ED_node_clipboard_free()
{
using namespace blender::ed::space_node;
NodeClipboard &clipboard = get_node_clipboard();
clipboard.validate();
clipboard.clear();
}

View File

@ -2203,216 +2203,6 @@ void NODE_OT_node_copy_color(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
/** \name Node Copy to Clipboard Operator
* \{ */
static int node_clipboard_copy_exec(bContext *C, wmOperator * /*op*/)
{
SpaceNode *snode = CTX_wm_space_node(C);
bNodeTree *ntree = snode->edittree;
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
/* clear current clipboard */
BKE_node_clipboard_clear();
Map<const bNode *, bNode *> node_map;
Map<const bNodeSocket *, bNodeSocket *> socket_map;
for (bNode *node : ntree->all_nodes()) {
if (node->flag & SELECT) {
/* No ID refcounting, this node is virtual,
* detached from any actual Blender data currently. */
bNode *new_node = bke::node_copy_with_mapping(nullptr,
*node,
LIB_ID_CREATE_NO_USER_REFCOUNT |
LIB_ID_CREATE_NO_MAIN,
false,
socket_map);
node_map.add_new(node, new_node);
}
}
for (bNode *new_node : node_map.values()) {
BKE_node_clipboard_add_node(new_node);
/* Parent pointer must be redirected to new node or detached if parent is not copied. */
if (new_node->parent) {
if (node_map.contains(new_node->parent)) {
new_node->parent = node_map.lookup(new_node->parent);
}
else {
nodeDetachNode(ntree, new_node);
}
}
}
/* Copy links between selected nodes. */
LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
BLI_assert(link->tonode);
BLI_assert(link->fromnode);
if (link->tonode->flag & NODE_SELECT && link->fromnode->flag & NODE_SELECT) {
bNodeLink *newlink = MEM_cnew<bNodeLink>(__func__);
newlink->flag = link->flag;
newlink->tonode = node_map.lookup(link->tonode);
newlink->tosock = socket_map.lookup(link->tosock);
newlink->fromnode = node_map.lookup(link->fromnode);
newlink->fromsock = socket_map.lookup(link->fromsock);
newlink->multi_input_socket_index = link->multi_input_socket_index;
BKE_node_clipboard_add_link(newlink);
}
}
return OPERATOR_FINISHED;
}
void NODE_OT_clipboard_copy(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Copy to Clipboard";
ot->description = "Copies selected nodes to the clipboard";
ot->idname = "NODE_OT_clipboard_copy";
/* api callbacks */
ot->exec = node_clipboard_copy_exec;
ot->poll = ED_operator_node_active;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Node Paste from Clipboard
* \{ */
static int node_clipboard_paste_exec(bContext *C, wmOperator *op)
{
SpaceNode *snode = CTX_wm_space_node(C);
bNodeTree *ntree = snode->edittree;
/* validate pointers in the clipboard */
bool is_clipboard_valid = BKE_node_clipboard_validate();
const ListBase *clipboard_nodes_lb = BKE_node_clipboard_get_nodes();
const ListBase *clipboard_links_lb = BKE_node_clipboard_get_links();
if (BLI_listbase_is_empty(clipboard_nodes_lb)) {
BKE_report(op->reports, RPT_ERROR, "Clipboard is empty");
return OPERATOR_CANCELLED;
}
/* only warn */
if (is_clipboard_valid == false) {
BKE_report(op->reports,
RPT_WARNING,
"Some nodes references could not be restored, will be left empty");
}
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
/* deselect old nodes */
node_deselect_all(*ntree);
/* calculate "barycenter" for placing on mouse cursor */
float2 center = {0.0f, 0.0f};
int num_nodes = 0;
LISTBASE_FOREACH_INDEX (bNode *, node, clipboard_nodes_lb, num_nodes) {
center.x += BLI_rctf_cent_x(&node->runtime->totr);
center.y += BLI_rctf_cent_y(&node->runtime->totr);
}
mul_v2_fl(center, 1.0 / num_nodes);
Map<const bNode *, bNode *> node_map;
Map<const bNodeSocket *, bNodeSocket *> socket_map;
/* copy valid nodes from clipboard */
LISTBASE_FOREACH (bNode *, node, clipboard_nodes_lb) {
const char *disabled_hint = nullptr;
if (node->typeinfo->poll_instance &&
node->typeinfo->poll_instance(node, ntree, &disabled_hint)) {
bNode *new_node = bke::node_copy_with_mapping(
ntree, *node, LIB_ID_COPY_DEFAULT, true, socket_map);
node_map.add_new(node, new_node);
}
else {
if (disabled_hint) {
BKE_reportf(op->reports,
RPT_ERROR,
"Cannot add node %s into node tree %s: %s",
node->name,
ntree->id.name + 2,
disabled_hint);
}
else {
BKE_reportf(op->reports,
RPT_ERROR,
"Cannot add node %s into node tree %s",
node->name,
ntree->id.name + 2);
}
}
}
for (bNode *new_node : node_map.values()) {
/* pasted nodes are selected */
nodeSetSelected(new_node, true);
/* The parent pointer must be redirected to new node. */
if (new_node->parent) {
if (node_map.contains(new_node->parent)) {
new_node->parent = node_map.lookup(new_node->parent);
}
}
}
/* Add links between existing nodes. */
LISTBASE_FOREACH (bNodeLink *, link, clipboard_links_lb) {
const bNode *fromnode = link->fromnode;
const bNode *tonode = link->tonode;
if (node_map.lookup_key_ptr(fromnode) && node_map.lookup_key_ptr(tonode)) {
bNodeLink *new_link = nodeAddLink(ntree,
node_map.lookup(fromnode),
socket_map.lookup(link->fromsock),
node_map.lookup(tonode),
socket_map.lookup(link->tosock));
new_link->multi_input_socket_index = link->multi_input_socket_index;
}
}
ntree->ensure_topology_cache();
for (bNode *new_node : node_map.values()) {
/* Update multi input socket indices in case all connected nodes weren't copied. */
update_multi_input_indices_for_removed_links(*new_node);
}
Main *bmain = CTX_data_main(C);
ED_node_tree_propagate_change(C, bmain, snode->edittree);
/* Pasting nodes can create arbitrary new relations, because nodes can reference IDs. */
DEG_relations_tag_update(bmain);
return OPERATOR_FINISHED;
}
void NODE_OT_clipboard_paste(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Paste from Clipboard";
ot->description = "Pastes nodes from the clipboard to the active node tree";
ot->idname = "NODE_OT_clipboard_paste";
/* api callbacks */
ot->exec = node_clipboard_paste_exec;
ot->poll = ED_operator_node_editable;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Node-Tree Add Interface Socket Operator
* \{ */

View File

@ -59,7 +59,7 @@ bNodeSocket &SocketDeclaration::update_or_build(bNodeTree &ntree,
bNodeSocket &socket) const
{
/* By default just rebuild. */
BLI_assert(socket.in_out == in_out_);
BLI_assert(socket.in_out == this->in_out);
UNUSED_VARS_NDEBUG(socket);
return this->build(ntree, node);
}

View File

@ -537,7 +537,7 @@ void WM_exit_ex(bContext *C, const bool do_python)
BKE_tracking_clipboard_free();
BKE_mask_clipboard_free();
BKE_vfont_clipboard_free();
BKE_node_clipboard_free();
ED_node_clipboard_free();
UV_clipboard_free();
#ifdef WITH_COMPOSITOR_CPU