2022-02-11 09:07:11 +11:00
|
|
|
/* SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
|
* Copyright 2005 Blender Foundation. All rights reserved. */
|
2012-08-01 19:11:17 +00:00
|
|
|
|
2019-02-18 08:08:12 +11:00
|
|
|
/** \file
|
|
|
|
|
* \ingroup spnode
|
2012-08-01 19:11:17 +00:00
|
|
|
*/
|
|
|
|
|
|
2021-06-02 17:19:36 +02:00
|
|
|
#include <cstdlib>
|
2012-11-15 22:20:18 +00:00
|
|
|
|
2012-08-01 19:11:17 +00:00
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
|
|
|
|
|
|
#include "DNA_anim_types.h"
|
2020-03-19 09:33:03 +01:00
|
|
|
#include "DNA_node_types.h"
|
2012-08-01 19:11:17 +00:00
|
|
|
|
2017-07-19 14:32:31 +10:00
|
|
|
#include "BLI_linklist.h"
|
2020-03-19 09:33:03 +01:00
|
|
|
#include "BLI_listbase.h"
|
2022-11-02 12:33:09 +01:00
|
|
|
#include "BLI_map.hh"
|
BLI: Refactor vector types & functions to use templates
This patch implements the vector types (i.e:`float2`) by making heavy
usage of templating. All vector functions are now outside of the vector
classes (inside the `blender::math` namespace) and are not vector size
dependent for the most part.
In the ongoing effort to make shaders less GL centric, we are aiming
to share more code between GLSL and C++ to avoid code duplication.
####Motivations:
- We are aiming to share UBO and SSBO structures between GLSL and C++.
This means we will use many of the existing vector types and others
we currently don't have (uintX, intX). All these variations were
asking for many more code duplication.
- Deduplicate existing code which is duplicated for each vector size.
- We also want to share small functions. Which means that vector
functions should be static and not in the class namespace.
- Reduce friction to use these types in new projects due to their
incompleteness.
- The current state of the `BLI_(float|double|mpq)(2|3|4).hh` is a
bit of a let down. Most clases are incomplete, out of sync with each
others with different codestyles, and some functions that should be
static are not (i.e: `float3::reflect()`).
####Upsides:
- Still support `.x, .y, .z, .w` for readability.
- Compact, readable and easilly extendable.
- All of the vector functions are available for all the vectors types
and can be restricted to certain types. Also template specialization
let us define exception for special class (like mpq).
- With optimization ON, the compiler unroll the loops and performance
is the same.
####Downsides:
- Might impact debugability. Though I would arge that the bugs are
rarelly caused by the vector class itself (since the operations are
quite trivial) but by the type conversions.
- Might impact compile time. I did not saw a significant impact since
the usage is not really widespread.
- Functions needs to be rewritten to support arbitrary vector length.
For instance, one can't call `len_squared_v3v3` in
`math::length_squared()` and call it a day.
- Type cast does not work with the template version of the `math::`
vector functions. Meaning you need to manually cast `float *` and
`(float *)[3]` to `float3` for the function calls.
i.e: `math::distance_squared(float3(nearest.co), positions[i]);`
- Some parts might loose in readability:
`float3::dot(v1.normalized(), v2.normalized())`
becoming
`math::dot(math::normalize(v1), math::normalize(v2))`
But I propose, when appropriate, to use
`using namespace blender::math;` on function local or file scope to
increase readability.
`dot(normalize(v1), normalize(v2))`
####Consideration:
- Include back `.length()` method. It is quite handy and is more C++
oriented.
- I considered the GLM library as a candidate for replacement. It felt
like too much for what we need and would be difficult to extend / modify
to our needs.
- I used Macros to reduce code in operators declaration and potential
copy paste bugs. This could reduce debugability and could be reverted.
- This touches `delaunay_2d.cc` and the intersection code. I would like
to know @howardt opinion on the matter.
- The `noexcept` on the copy constructor of `mpq(2|3)` is being removed.
But according to @JacquesLucke it is not a real problem for now.
I would like to give a huge thanks to @JacquesLucke who helped during this
and pushed me to reduce the duplication further.
Reviewed By: brecht, sergey, JacquesLucke
Differential Revision: https://developer.blender.org/D13791
2022-01-12 12:57:07 +01:00
|
|
|
#include "BLI_math_vec_types.hh"
|
2022-12-21 12:26:09 -06:00
|
|
|
#include "BLI_set.hh"
|
2020-11-06 15:29:25 +11:00
|
|
|
#include "BLI_string.h"
|
2021-12-24 10:05:47 -06:00
|
|
|
#include "BLI_vector.hh"
|
2012-08-01 19:11:17 +00:00
|
|
|
|
2015-08-16 17:32:01 +10:00
|
|
|
#include "BLT_translation.h"
|
2013-02-24 15:40:28 +00:00
|
|
|
|
2012-08-01 19:11:17 +00:00
|
|
|
#include "BKE_action.h"
|
|
|
|
|
#include "BKE_animsys.h"
|
|
|
|
|
#include "BKE_context.h"
|
2020-02-10 12:58:59 +01:00
|
|
|
#include "BKE_lib_id.h"
|
2012-08-01 19:11:17 +00:00
|
|
|
#include "BKE_main.h"
|
2022-09-03 23:22:12 -05:00
|
|
|
#include "BKE_node_runtime.hh"
|
2021-12-21 15:18:56 +01:00
|
|
|
#include "BKE_node_tree_update.h"
|
2012-08-01 19:11:17 +00:00
|
|
|
#include "BKE_report.h"
|
|
|
|
|
|
2018-08-23 15:19:08 +02:00
|
|
|
#include "DEG_depsgraph_build.h"
|
|
|
|
|
|
2012-08-01 20:39:14 +00:00
|
|
|
#include "ED_node.h" /* own include */
|
2022-12-12 16:16:59 -06:00
|
|
|
#include "ED_node.hh"
|
2012-08-01 19:11:17 +00:00
|
|
|
#include "ED_render.h"
|
2020-03-19 09:33:03 +01:00
|
|
|
#include "ED_screen.h"
|
2012-08-01 19:11:17 +00:00
|
|
|
|
|
|
|
|
#include "RNA_access.h"
|
|
|
|
|
#include "RNA_define.h"
|
2022-07-29 16:56:48 +02:00
|
|
|
#include "RNA_path.h"
|
2022-03-14 16:54:46 +01:00
|
|
|
#include "RNA_prototypes.h"
|
2012-08-01 19:11:17 +00:00
|
|
|
|
|
|
|
|
#include "WM_api.h"
|
|
|
|
|
#include "WM_types.h"
|
|
|
|
|
|
|
|
|
|
#include "UI_resources.h"
|
|
|
|
|
|
2013-03-18 16:34:57 +00:00
|
|
|
#include "NOD_common.h"
|
2022-07-28 16:34:17 -05:00
|
|
|
#include "NOD_composite.h"
|
|
|
|
|
#include "NOD_geometry.h"
|
|
|
|
|
#include "NOD_shader.h"
|
2020-07-27 15:03:23 +03:00
|
|
|
#include "NOD_socket.h"
|
2022-07-28 16:34:17 -05:00
|
|
|
#include "NOD_texture.h"
|
|
|
|
|
|
2021-11-12 12:12:27 -06:00
|
|
|
#include "node_intern.hh" /* own include */
|
2012-08-01 19:11:17 +00:00
|
|
|
|
2022-01-20 10:36:56 -06:00
|
|
|
namespace blender::ed::space_node {
|
2021-12-03 11:43:10 -05:00
|
|
|
|
2020-11-06 16:14:43 +11:00
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Local Utilities
|
|
|
|
|
* \{ */
|
|
|
|
|
|
|
|
|
|
static bool node_group_operator_active_poll(bContext *C)
|
2013-06-05 19:28:59 +00:00
|
|
|
{
|
|
|
|
|
if (ED_operator_node_active(C)) {
|
|
|
|
|
SpaceNode *snode = CTX_wm_space_node(C);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-06-05 19:28:59 +00:00
|
|
|
/* Group operators only defined for standard node tree types.
|
2021-06-10 01:55:42 +10:00
|
|
|
* Disabled otherwise to allow python-nodes define their own operators
|
|
|
|
|
* with same key-map. */
|
2020-11-06 15:29:25 +11:00
|
|
|
if (STR_ELEM(snode->tree_idname,
|
|
|
|
|
"ShaderNodeTree",
|
|
|
|
|
"CompositorNodeTree",
|
|
|
|
|
"TextureNodeTree",
|
2020-12-02 13:25:25 +01:00
|
|
|
"GeometryNodeTree")) {
|
2013-06-05 19:28:59 +00:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-02 11:47:00 +02:00
|
|
|
static bool node_group_operator_editable(bContext *C)
|
2012-08-01 19:11:17 +00:00
|
|
|
{
|
2013-06-05 19:06:33 +00:00
|
|
|
if (ED_operator_node_editable(C)) {
|
2013-04-20 16:50:05 +00:00
|
|
|
SpaceNode *snode = CTX_wm_space_node(C);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-04-20 16:50:05 +00:00
|
|
|
/* Group operators only defined for standard node tree types.
|
2021-06-10 01:55:42 +10:00
|
|
|
* Disabled otherwise to allow python-nodes define their own operators
|
|
|
|
|
* with same key-map. */
|
2020-04-20 10:58:43 +02:00
|
|
|
if (ED_node_is_shader(snode) || ED_node_is_compositor(snode) || ED_node_is_texture(snode) ||
|
2020-12-02 13:25:25 +01:00
|
|
|
ED_node_is_geometry(snode)) {
|
2013-04-20 16:50:05 +00:00
|
|
|
return true;
|
2013-04-20 17:24:40 +00:00
|
|
|
}
|
2013-04-20 16:50:05 +00:00
|
|
|
}
|
|
|
|
|
return false;
|
2013-03-18 16:34:57 +00:00
|
|
|
}
|
2012-08-01 19:11:17 +00:00
|
|
|
|
2013-04-20 16:50:05 +00:00
|
|
|
static const char *group_ntree_idname(bContext *C)
|
|
|
|
|
{
|
|
|
|
|
SpaceNode *snode = CTX_wm_space_node(C);
|
|
|
|
|
return snode->tree_idname;
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-15 19:35:24 +01:00
|
|
|
const char *node_group_idname(bContext *C)
|
2013-03-18 16:34:57 +00:00
|
|
|
{
|
|
|
|
|
SpaceNode *snode = CTX_wm_space_node(C);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-03-26 21:16:47 +11:00
|
|
|
if (ED_node_is_shader(snode)) {
|
2022-07-28 16:34:17 -05:00
|
|
|
return ntreeType_Shader->group_idname;
|
2019-03-26 21:16:47 +11:00
|
|
|
}
|
2020-07-03 17:20:08 +02:00
|
|
|
if (ED_node_is_compositor(snode)) {
|
2022-07-28 16:34:17 -05:00
|
|
|
return ntreeType_Composite->group_idname;
|
2019-03-26 21:16:47 +11:00
|
|
|
}
|
2020-07-03 17:20:08 +02:00
|
|
|
if (ED_node_is_texture(snode)) {
|
2022-07-28 16:34:17 -05:00
|
|
|
return ntreeType_Texture->group_idname;
|
2019-03-26 21:16:47 +11:00
|
|
|
}
|
2020-12-02 13:25:25 +01:00
|
|
|
if (ED_node_is_geometry(snode)) {
|
2022-07-28 16:34:17 -05:00
|
|
|
return ntreeType_Geometry->group_idname;
|
2020-04-20 10:58:43 +02:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-04-20 16:50:05 +00:00
|
|
|
return "";
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
|
|
|
|
|
2013-03-18 16:34:57 +00:00
|
|
|
static bNode *node_group_get_active(bContext *C, const char *node_idname)
|
2012-08-01 19:11:17 +00:00
|
|
|
{
|
|
|
|
|
SpaceNode *snode = CTX_wm_space_node(C);
|
2013-03-18 16:34:57 +00:00
|
|
|
bNode *node = nodeGetActive(snode->edittree);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-03-26 21:16:47 +11:00
|
|
|
if (node && STREQ(node->idname, node_idname)) {
|
2013-03-18 16:34:57 +00:00
|
|
|
return node;
|
2019-03-26 21:16:47 +11:00
|
|
|
}
|
2021-06-02 17:19:36 +02:00
|
|
|
return nullptr;
|
2013-03-18 16:34:57 +00:00
|
|
|
}
|
2012-08-01 19:11:17 +00:00
|
|
|
|
2020-11-06 16:14:43 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Edit Group Operator
|
|
|
|
|
* \{ */
|
2012-08-01 19:11:17 +00:00
|
|
|
|
2013-03-18 16:34:57 +00:00
|
|
|
static int node_group_edit_exec(bContext *C, wmOperator *op)
|
|
|
|
|
{
|
|
|
|
|
SpaceNode *snode = CTX_wm_space_node(C);
|
2021-02-15 19:35:24 +01:00
|
|
|
const char *node_idname = node_group_idname(C);
|
2014-02-03 18:55:59 +11:00
|
|
|
const bool exit = RNA_boolean_get(op->ptr, "exit");
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2014-11-18 15:51:31 +01:00
|
|
|
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2020-10-16 17:30:12 +02:00
|
|
|
bNode *gnode = node_group_get_active(C, node_idname);
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2013-03-18 16:34:57 +00:00
|
|
|
if (gnode && !exit) {
|
2013-03-18 18:25:05 +00:00
|
|
|
bNodeTree *ngroup = (bNodeTree *)gnode->id;
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2019-03-26 21:16:47 +11:00
|
|
|
if (ngroup) {
|
2013-03-18 16:34:57 +00:00
|
|
|
ED_node_tree_push(snode, ngroup, gnode);
|
2019-03-26 21:16:47 +11:00
|
|
|
}
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
2019-03-26 21:16:47 +11:00
|
|
|
else {
|
2013-03-18 16:34:57 +00:00
|
|
|
ED_node_tree_pop(snode);
|
2019-03-26 21:16:47 +11:00
|
|
|
}
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2021-06-02 17:19:36 +02:00
|
|
|
WM_event_add_notifier(C, NC_SCENE | ND_NODES, nullptr);
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2012-08-01 19:11:17 +00:00
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void NODE_OT_group_edit(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
/* identifiers */
|
|
|
|
|
ot->name = "Edit Group";
|
|
|
|
|
ot->description = "Edit node group";
|
|
|
|
|
ot->idname = "NODE_OT_group_edit";
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2012-08-01 19:11:17 +00:00
|
|
|
/* api callbacks */
|
|
|
|
|
ot->exec = node_group_edit_exec;
|
2020-11-06 16:14:43 +11:00
|
|
|
ot->poll = node_group_operator_active_poll;
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2012-08-01 19:11:17 +00:00
|
|
|
/* flags */
|
|
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2014-04-01 11:34:00 +11:00
|
|
|
RNA_def_boolean(ot->srna, "exit", false, "Exit", "");
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
|
|
|
|
|
2020-11-06 16:14:43 +11:00
|
|
|
/** \} */
|
2012-08-01 19:11:17 +00:00
|
|
|
|
2020-11-06 16:14:43 +11:00
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Ungroup Operator
|
|
|
|
|
* \{ */
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* The given paths will be owned by the returned instance.
|
|
|
|
|
* Both pointers are allowed to point to the same string.
|
|
|
|
|
*/
|
2020-08-06 12:50:08 +02:00
|
|
|
static AnimationBasePathChange *animation_basepath_change_new(const char *src_basepath,
|
|
|
|
|
const char *dst_basepath)
|
|
|
|
|
{
|
2021-06-02 17:19:36 +02:00
|
|
|
AnimationBasePathChange *basepath_change = (AnimationBasePathChange *)MEM_callocN(
|
|
|
|
|
sizeof(*basepath_change), AT);
|
2020-08-06 12:50:08 +02:00
|
|
|
basepath_change->src_basepath = src_basepath;
|
|
|
|
|
basepath_change->dst_basepath = dst_basepath;
|
|
|
|
|
return basepath_change;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void animation_basepath_change_free(AnimationBasePathChange *basepath_change)
|
|
|
|
|
{
|
|
|
|
|
if (basepath_change->src_basepath != basepath_change->dst_basepath) {
|
|
|
|
|
MEM_freeN((void *)basepath_change->src_basepath);
|
|
|
|
|
}
|
|
|
|
|
MEM_freeN((void *)basepath_change->dst_basepath);
|
|
|
|
|
MEM_freeN(basepath_change);
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-24 10:05:47 -06:00
|
|
|
/**
|
|
|
|
|
* \return True if successful.
|
|
|
|
|
*/
|
|
|
|
|
static bool node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode)
|
2012-08-01 19:11:17 +00:00
|
|
|
{
|
2021-06-02 17:19:36 +02:00
|
|
|
ListBase anim_basepaths = {nullptr, nullptr};
|
2021-12-24 10:05:47 -06:00
|
|
|
Vector<bNode *> nodes_delayed_free;
|
2021-12-23 22:48:55 -06:00
|
|
|
const bNodeTree *ngroup = reinterpret_cast<const bNodeTree *>(gnode->id);
|
2020-10-16 17:30:12 +02:00
|
|
|
|
2022-01-06 13:54:52 +11:00
|
|
|
/* `wgroup` is a temporary copy of the #NodeTree we're merging in
|
|
|
|
|
* - All of wgroup's nodes are copied across to their new home.
|
|
|
|
|
* - `ngroup` (i.e. the source NodeTree) is left unscathed.
|
|
|
|
|
* - Temp copy. do change ID user-count for the copies.
|
2012-08-01 19:11:17 +00:00
|
|
|
*/
|
2021-12-23 22:48:55 -06:00
|
|
|
bNodeTree *wgroup = ntreeCopyTree(bmain, ngroup);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-01-06 13:54:52 +11:00
|
|
|
/* Add the nodes into the `ntree`. */
|
2020-10-16 17:30:12 +02:00
|
|
|
LISTBASE_FOREACH_MUTABLE (bNode *, node, &wgroup->nodes) {
|
2013-03-18 16:34:57 +00:00
|
|
|
/* Remove interface nodes.
|
|
|
|
|
* This also removes remaining links to and from interface nodes.
|
|
|
|
|
*/
|
|
|
|
|
if (ELEM(node->type, NODE_GROUP_INPUT, NODE_GROUP_OUTPUT)) {
|
2017-07-19 14:32:31 +10:00
|
|
|
/* We must delay removal since sockets will reference this node. see: T52092 */
|
2021-12-24 10:05:47 -06:00
|
|
|
nodes_delayed_free.append(node);
|
2013-03-18 16:34:57 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-01-06 13:54:52 +11:00
|
|
|
/* Keep track of this node's RNA "base" path (the part of the path identifying the node)
|
|
|
|
|
* if the old node-tree has animation data which potentially covers this node. */
|
2021-06-02 17:19:36 +02:00
|
|
|
const char *old_animation_basepath = nullptr;
|
2012-08-01 19:11:17 +00:00
|
|
|
if (wgroup->adt) {
|
|
|
|
|
PointerRNA ptr;
|
|
|
|
|
RNA_pointer_create(&wgroup->id, &RNA_Node, node, &ptr);
|
2020-08-06 12:50:08 +02:00
|
|
|
old_animation_basepath = RNA_path_from_ID_to_struct(&ptr);
|
2019-03-26 21:16:47 +11:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-08-01 19:11:17 +00:00
|
|
|
/* migrate node */
|
|
|
|
|
BLI_remlink(&wgroup->nodes, node);
|
|
|
|
|
BLI_addtail(&ntree->nodes, node);
|
2022-12-01 14:53:27 -06:00
|
|
|
nodeUniqueID(ntree, node);
|
2012-08-01 19:11:17 +00:00
|
|
|
nodeUniqueName(ntree, node);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-12-01 14:53:27 -06:00
|
|
|
BKE_ntree_update_tag_node_new(ntree, node);
|
|
|
|
|
|
2020-08-06 12:50:08 +02:00
|
|
|
if (wgroup->adt) {
|
|
|
|
|
PointerRNA ptr;
|
|
|
|
|
RNA_pointer_create(&ntree->id, &RNA_Node, node, &ptr);
|
|
|
|
|
const char *new_animation_basepath = RNA_path_from_ID_to_struct(&ptr);
|
|
|
|
|
BLI_addtail(&anim_basepaths,
|
|
|
|
|
animation_basepath_change_new(old_animation_basepath, new_animation_basepath));
|
|
|
|
|
}
|
|
|
|
|
|
2013-02-06 13:59:54 +00:00
|
|
|
if (!node->parent) {
|
|
|
|
|
node->locx += gnode->locx;
|
|
|
|
|
node->locy += gnode->locy;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-08-01 19:11:17 +00:00
|
|
|
node->flag |= NODE_SELECT;
|
|
|
|
|
}
|
2022-12-02 13:20:40 -06:00
|
|
|
wgroup->runtime->nodes_by_id.clear();
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-06-02 17:19:36 +02:00
|
|
|
bNodeLink *glinks_first = (bNodeLink *)ntree->links.last;
|
2019-10-02 15:47:36 +02:00
|
|
|
|
2013-03-18 16:34:57 +00:00
|
|
|
/* Add internal links to the ntree */
|
2020-10-16 17:30:12 +02:00
|
|
|
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &wgroup->links) {
|
2012-08-01 19:11:17 +00:00
|
|
|
BLI_remlink(&wgroup->links, link);
|
|
|
|
|
BLI_addtail(&ntree->links, link);
|
2021-12-21 15:18:56 +01:00
|
|
|
BKE_ntree_update_tag_link_added(ntree, link);
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-06-02 17:19:36 +02:00
|
|
|
bNodeLink *glinks_last = (bNodeLink *)ntree->links.last;
|
2019-10-02 15:47:36 +02:00
|
|
|
|
2012-08-01 19:11:17 +00:00
|
|
|
/* and copy across the animation,
|
2021-06-02 17:19:36 +02:00
|
|
|
* note that the animation data's action can be nullptr here */
|
2012-08-01 19:11:17 +00:00
|
|
|
if (wgroup->adt) {
|
|
|
|
|
bAction *waction;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-01-15 23:24:20 +11:00
|
|
|
/* firstly, wgroup needs to temporary dummy action
|
|
|
|
|
* that can be destroyed, as it shares copies */
|
2020-10-07 18:01:25 +02:00
|
|
|
waction = wgroup->adt->action = (bAction *)BKE_id_copy(bmain, &wgroup->adt->action->id);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-08-01 19:11:17 +00:00
|
|
|
/* now perform the moving */
|
2020-08-06 12:50:08 +02:00
|
|
|
BKE_animdata_transfer_by_basepath(bmain, &wgroup->id, &ntree->id, &anim_basepaths);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-08-01 19:11:17 +00:00
|
|
|
/* paths + their wrappers need to be freed */
|
2020-08-06 12:50:08 +02:00
|
|
|
LISTBASE_FOREACH_MUTABLE (AnimationBasePathChange *, basepath_change, &anim_basepaths) {
|
|
|
|
|
animation_basepath_change_free(basepath_change);
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-08-01 19:11:17 +00:00
|
|
|
/* free temp action too */
|
|
|
|
|
if (waction) {
|
2019-01-14 15:47:11 +01:00
|
|
|
BKE_id_free(bmain, waction);
|
2021-06-02 17:19:36 +02:00
|
|
|
wgroup->adt->action = nullptr;
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-08-01 19:11:17 +00:00
|
|
|
/* free the group tree (takes care of user count) */
|
2019-01-14 15:47:11 +01:00
|
|
|
BKE_id_free(bmain, wgroup);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-03-18 16:34:57 +00:00
|
|
|
/* restore external links to and from the gnode */
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-03-18 16:34:57 +00:00
|
|
|
/* input links */
|
2021-06-02 17:19:36 +02:00
|
|
|
if (glinks_first != nullptr) {
|
2020-10-16 17:30:12 +02:00
|
|
|
for (bNodeLink *link = glinks_first->next; link != glinks_last->next; link = link->next) {
|
2019-10-02 15:47:36 +02:00
|
|
|
if (link->fromnode->type == NODE_GROUP_INPUT) {
|
|
|
|
|
const char *identifier = link->fromsock->identifier;
|
|
|
|
|
int num_external_links = 0;
|
|
|
|
|
|
|
|
|
|
/* find external links to this input */
|
2021-06-02 17:19:36 +02:00
|
|
|
for (bNodeLink *tlink = (bNodeLink *)ntree->links.first; tlink != glinks_first->next;
|
2020-10-16 17:30:12 +02:00
|
|
|
tlink = tlink->next) {
|
2019-10-02 15:47:36 +02:00
|
|
|
if (tlink->tonode == gnode && STREQ(tlink->tosock->identifier, identifier)) {
|
|
|
|
|
nodeAddLink(ntree, tlink->fromnode, tlink->fromsock, link->tonode, link->tosock);
|
|
|
|
|
num_external_links++;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
|
|
2019-10-02 15:47:36 +02:00
|
|
|
/* if group output is not externally linked,
|
|
|
|
|
* convert the constant input value to ensure somewhat consistent behavior */
|
|
|
|
|
if (num_external_links == 0) {
|
2020-10-14 15:24:42 +11:00
|
|
|
/* TODO */
|
|
|
|
|
#if 0
|
|
|
|
|
bNodeSocket *sock = node_group_find_input_socket(gnode, identifier);
|
|
|
|
|
BLI_assert(sock);
|
|
|
|
|
|
|
|
|
|
nodeSocketCopy(
|
|
|
|
|
ntree, link->tosock->new_sock, link->tonode->new_node, ntree, sock, gnode);
|
|
|
|
|
#endif
|
2019-10-02 15:47:36 +02:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-02 15:47:36 +02:00
|
|
|
/* Also iterate over new links to cover passthrough links. */
|
2021-06-02 17:19:36 +02:00
|
|
|
glinks_last = (bNodeLink *)ntree->links.last;
|
2019-10-02 15:47:36 +02:00
|
|
|
|
|
|
|
|
/* output links */
|
2021-06-02 17:19:36 +02:00
|
|
|
for (bNodeLink *link = (bNodeLink *)ntree->links.first; link != glinks_first->next;
|
|
|
|
|
link = link->next) {
|
2019-10-02 15:47:36 +02:00
|
|
|
if (link->fromnode == gnode) {
|
|
|
|
|
const char *identifier = link->fromsock->identifier;
|
|
|
|
|
int num_internal_links = 0;
|
|
|
|
|
|
|
|
|
|
/* find internal links to this output */
|
2020-10-16 17:30:12 +02:00
|
|
|
for (bNodeLink *tlink = glinks_first->next; tlink != glinks_last->next;
|
|
|
|
|
tlink = tlink->next) {
|
2019-10-02 15:47:36 +02:00
|
|
|
/* only use active output node */
|
|
|
|
|
if (tlink->tonode->type == NODE_GROUP_OUTPUT && (tlink->tonode->flag & NODE_DO_OUTPUT)) {
|
|
|
|
|
if (STREQ(tlink->tosock->identifier, identifier)) {
|
|
|
|
|
nodeAddLink(ntree, tlink->fromnode, tlink->fromsock, link->tonode, link->tosock);
|
|
|
|
|
num_internal_links++;
|
|
|
|
|
}
|
2013-03-18 16:34:57 +00:00
|
|
|
}
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-10-02 15:47:36 +02:00
|
|
|
/* if group output is not internally linked,
|
|
|
|
|
* convert the constant output value to ensure somewhat consistent behavior */
|
|
|
|
|
if (num_internal_links == 0) {
|
2020-10-14 15:24:42 +11:00
|
|
|
/* TODO */
|
|
|
|
|
#if 0
|
|
|
|
|
bNodeSocket *sock = node_group_find_output_socket(gnode, identifier);
|
|
|
|
|
BLI_assert(sock);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-10-14 15:24:42 +11:00
|
|
|
nodeSocketCopy(ntree, link->tosock, link->tonode, ntree, sock, gnode);
|
|
|
|
|
#endif
|
2019-10-02 15:47:36 +02:00
|
|
|
}
|
2013-03-18 16:34:57 +00:00
|
|
|
}
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
|
|
2021-12-24 10:05:47 -06:00
|
|
|
for (bNode *node : nodes_delayed_free) {
|
2019-03-16 18:54:00 +01:00
|
|
|
nodeRemoveNode(bmain, ntree, node, false);
|
2017-07-19 14:32:31 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-04-06 16:23:43 +02:00
|
|
|
/* delete the group instance and dereference group tree */
|
|
|
|
|
nodeRemoveNode(bmain, ntree, gnode, true);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-12-24 10:05:47 -06:00
|
|
|
return true;
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int node_group_ungroup_exec(bContext *C, wmOperator *op)
|
|
|
|
|
{
|
2018-06-09 15:16:44 +02:00
|
|
|
Main *bmain = CTX_data_main(C);
|
2012-08-01 19:11:17 +00:00
|
|
|
SpaceNode *snode = CTX_wm_space_node(C);
|
2021-02-15 19:35:24 +01:00
|
|
|
const char *node_idname = node_group_idname(C);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-06-09 15:16:44 +02:00
|
|
|
ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-10-16 17:30:12 +02:00
|
|
|
bNode *gnode = node_group_get_active(C, node_idname);
|
2019-03-26 21:16:47 +11:00
|
|
|
if (!gnode) {
|
2012-08-01 19:11:17 +00:00
|
|
|
return OPERATOR_CANCELLED;
|
2019-03-26 21:16:47 +11:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-06-09 15:16:44 +02:00
|
|
|
if (gnode->id && node_group_ungroup(bmain, snode->edittree, gnode)) {
|
2021-12-21 15:18:56 +01:00
|
|
|
ED_node_tree_propagate_change(C, CTX_data_main(C), nullptr);
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
|
|
|
|
else {
|
2012-10-18 16:25:58 +00:00
|
|
|
BKE_report(op->reports, RPT_WARNING, "Cannot ungroup");
|
2012-08-01 19:11:17 +00:00
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-08-01 19:11:17 +00:00
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void NODE_OT_group_ungroup(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
/* identifiers */
|
|
|
|
|
ot->name = "Ungroup";
|
|
|
|
|
ot->description = "Ungroup selected nodes";
|
|
|
|
|
ot->idname = "NODE_OT_group_ungroup";
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2012-08-01 19:11:17 +00:00
|
|
|
/* api callbacks */
|
|
|
|
|
ot->exec = node_group_ungroup_exec;
|
2013-06-05 19:28:59 +00:00
|
|
|
ot->poll = node_group_operator_editable;
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2012-08-01 19:11:17 +00:00
|
|
|
/* flags */
|
|
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-06 16:14:43 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Separate Operator
|
|
|
|
|
* \{ */
|
2012-08-01 19:11:17 +00:00
|
|
|
|
2021-12-03 11:43:10 -05:00
|
|
|
/**
|
|
|
|
|
* \return True if successful.
|
|
|
|
|
*/
|
|
|
|
|
static bool node_group_separate_selected(
|
|
|
|
|
Main &bmain, bNodeTree &ntree, bNodeTree &ngroup, const float2 &offset, const bool make_copy)
|
2012-08-01 19:11:17 +00:00
|
|
|
{
|
2022-12-20 17:14:34 -06:00
|
|
|
node_deselect_all(ntree);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-06-02 17:19:36 +02:00
|
|
|
ListBase anim_basepaths = {nullptr, nullptr};
|
2020-10-16 17:30:12 +02:00
|
|
|
|
2021-12-22 08:47:46 -06:00
|
|
|
Map<const bNodeSocket *, bNodeSocket *> socket_map;
|
|
|
|
|
|
2022-12-02 13:20:40 -06:00
|
|
|
/* 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(); });
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-12-02 13:20:40 -06:00
|
|
|
for (bNode *node : nodes_to_move) {
|
2020-10-16 17:30:12 +02:00
|
|
|
bNode *newnode;
|
2013-03-18 16:34:57 +00:00
|
|
|
if (make_copy) {
|
2022-12-02 13:20:40 -06:00
|
|
|
newnode = bke::node_copy_with_mapping(&ntree, *node, LIB_ID_COPY_DEFAULT, true, socket_map);
|
2013-03-18 16:34:57 +00:00
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
newnode = node;
|
2022-12-02 13:20:40 -06:00
|
|
|
BLI_remlink(&ngroup.nodes, newnode);
|
|
|
|
|
BLI_addtail(&ntree.nodes, newnode);
|
|
|
|
|
nodeUniqueID(&ntree, newnode);
|
|
|
|
|
nodeUniqueName(&ntree, newnode);
|
2013-03-18 16:34:57 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-01-06 13:54:52 +11:00
|
|
|
/* Keep track of this node's RNA "base" path (the part of the path identifying the node)
|
|
|
|
|
* if the old node-tree has animation data which potentially covers this node. */
|
2021-12-03 11:43:10 -05:00
|
|
|
if (ngroup.adt) {
|
2013-03-18 16:34:57 +00:00
|
|
|
PointerRNA ptr;
|
|
|
|
|
char *path;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-12-03 11:43:10 -05:00
|
|
|
RNA_pointer_create(&ngroup.id, &RNA_Node, newnode, &ptr);
|
2013-03-18 16:34:57 +00:00
|
|
|
path = RNA_path_from_ID_to_struct(&ptr);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-03-26 21:16:47 +11:00
|
|
|
if (path) {
|
2020-08-06 12:50:08 +02:00
|
|
|
BLI_addtail(&anim_basepaths, animation_basepath_change_new(path, path));
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2019-03-26 21:16:47 +11:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-03-18 16:34:57 +00:00
|
|
|
/* ensure valid parent pointers, detach if parent stays inside the group */
|
2019-03-26 21:16:47 +11:00
|
|
|
if (newnode->parent && !(newnode->parent->flag & NODE_SELECT)) {
|
2022-11-18 11:20:13 +01:00
|
|
|
nodeDetachNode(&ngroup, newnode);
|
2019-03-26 21:16:47 +11:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-03-18 16:34:57 +00:00
|
|
|
if (!newnode->parent) {
|
2021-12-03 11:43:10 -05:00
|
|
|
newnode->locx += offset.x;
|
|
|
|
|
newnode->locy += offset.y;
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
2022-12-02 13:20:40 -06:00
|
|
|
if (!make_copy) {
|
|
|
|
|
nodeRebuildIDVector(&ngroup);
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-08-01 19:11:17 +00:00
|
|
|
/* add internal links to the ntree */
|
2021-12-03 11:43:10 -05:00
|
|
|
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ngroup.links) {
|
2015-04-20 23:37:04 +10:00
|
|
|
const bool fromselect = (link->fromnode && (link->fromnode->flag & NODE_SELECT));
|
|
|
|
|
const bool toselect = (link->tonode && (link->tonode->flag & NODE_SELECT));
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-08-01 19:11:17 +00:00
|
|
|
if (make_copy) {
|
|
|
|
|
/* make a copy of internal links */
|
2019-03-26 21:16:47 +11:00
|
|
|
if (fromselect && toselect) {
|
2021-12-03 11:43:10 -05:00
|
|
|
nodeAddLink(&ntree,
|
2022-12-02 13:20:40 -06:00
|
|
|
ntree.node_by_id(link->fromnode->identifier),
|
2021-12-22 08:47:46 -06:00
|
|
|
socket_map.lookup(link->fromsock),
|
2022-12-02 13:20:40 -06:00
|
|
|
ntree.node_by_id(link->tonode->identifier),
|
2021-12-22 08:47:46 -06:00
|
|
|
socket_map.lookup(link->tosock));
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2012-08-01 19:11:17 +00:00
|
|
|
else {
|
|
|
|
|
/* move valid links over, delete broken links */
|
|
|
|
|
if (fromselect && toselect) {
|
2021-12-03 11:43:10 -05:00
|
|
|
BLI_remlink(&ngroup.links, link);
|
|
|
|
|
BLI_addtail(&ntree.links, link);
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
|
|
|
|
else if (fromselect || toselect) {
|
2021-12-03 11:43:10 -05:00
|
|
|
nodeRemLink(&ngroup, link);
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
|
|
2012-08-01 19:11:17 +00:00
|
|
|
/* and copy across the animation,
|
2021-06-02 17:19:36 +02:00
|
|
|
* note that the animation data's action can be nullptr here */
|
2021-12-03 11:43:10 -05:00
|
|
|
if (ngroup.adt) {
|
2012-08-01 19:11:17 +00:00
|
|
|
/* now perform the moving */
|
2021-12-03 11:43:10 -05:00
|
|
|
BKE_animdata_transfer_by_basepath(&bmain, &ngroup.id, &ntree.id, &anim_basepaths);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-08-01 19:11:17 +00:00
|
|
|
/* paths + their wrappers need to be freed */
|
2020-08-06 12:50:08 +02:00
|
|
|
LISTBASE_FOREACH_MUTABLE (AnimationBasePathChange *, basepath_change, &anim_basepaths) {
|
|
|
|
|
animation_basepath_change_free(basepath_change);
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-12-21 15:18:56 +01:00
|
|
|
BKE_ntree_update_tag_all(&ntree);
|
2019-03-26 21:16:47 +11:00
|
|
|
if (!make_copy) {
|
2021-12-21 15:18:56 +01:00
|
|
|
BKE_ntree_update_tag_all(&ngroup);
|
2019-03-26 21:16:47 +11:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-12-03 11:43:10 -05:00
|
|
|
return true;
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
|
|
|
|
|
2021-06-02 17:19:36 +02:00
|
|
|
enum eNodeGroupSeparateType {
|
2012-08-01 19:11:17 +00:00
|
|
|
NODE_GS_COPY,
|
2019-04-16 16:40:47 +02:00
|
|
|
NODE_GS_MOVE,
|
2021-06-02 17:19:36 +02:00
|
|
|
};
|
2012-08-01 19:11:17 +00:00
|
|
|
|
|
|
|
|
/* Operator Property */
|
2017-10-18 15:07:26 +11:00
|
|
|
static const EnumPropertyItem node_group_separate_types[] = {
|
2012-08-01 19:11:17 +00:00
|
|
|
{NODE_GS_COPY, "COPY", 0, "Copy", "Copy to parent node tree, keep group intact"},
|
|
|
|
|
{NODE_GS_MOVE, "MOVE", 0, "Move", "Move to parent node tree, remove from group"},
|
2021-06-02 17:19:36 +02:00
|
|
|
{0, nullptr, 0, nullptr, nullptr},
|
2012-08-01 19:11:17 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static int node_group_separate_exec(bContext *C, wmOperator *op)
|
|
|
|
|
{
|
2018-06-12 12:28:14 +02:00
|
|
|
Main *bmain = CTX_data_main(C);
|
2012-08-01 19:11:17 +00:00
|
|
|
SpaceNode *snode = CTX_wm_space_node(C);
|
|
|
|
|
int type = RNA_enum_get(op->ptr, "type");
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-06-12 12:28:14 +02:00
|
|
|
ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-08-01 19:11:17 +00:00
|
|
|
/* are we inside of a group? */
|
2020-10-16 17:30:12 +02:00
|
|
|
bNodeTree *ngroup = snode->edittree;
|
|
|
|
|
bNodeTree *nparent = ED_node_tree_get(snode, 1);
|
2013-03-18 16:34:57 +00:00
|
|
|
if (!nparent) {
|
2012-08-01 19:11:17 +00:00
|
|
|
BKE_report(op->reports, RPT_WARNING, "Not inside node group");
|
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
2013-03-18 16:34:57 +00:00
|
|
|
/* get node tree offset */
|
2021-12-03 11:43:10 -05:00
|
|
|
const float2 offset = space_node_group_offset(*snode);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-08-01 19:11:17 +00:00
|
|
|
switch (type) {
|
|
|
|
|
case NODE_GS_COPY:
|
2021-12-03 11:43:10 -05:00
|
|
|
if (!node_group_separate_selected(*bmain, *nparent, *ngroup, offset, true)) {
|
2012-10-18 16:25:58 +00:00
|
|
|
BKE_report(op->reports, RPT_WARNING, "Cannot separate nodes");
|
2012-08-01 19:11:17 +00:00
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case NODE_GS_MOVE:
|
2021-12-03 11:43:10 -05:00
|
|
|
if (!node_group_separate_selected(*bmain, *nparent, *ngroup, offset, false)) {
|
2012-10-18 16:25:58 +00:00
|
|
|
BKE_report(op->reports, RPT_WARNING, "Cannot separate nodes");
|
2012-08-01 19:11:17 +00:00
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-08-01 19:11:17 +00:00
|
|
|
/* switch to parent tree */
|
2013-03-18 16:34:57 +00:00
|
|
|
ED_node_tree_pop(snode);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-12-21 15:18:56 +01:00
|
|
|
ED_node_tree_propagate_change(C, CTX_data_main(C), nullptr);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-08-01 19:11:17 +00:00
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-03 17:37:25 -05:00
|
|
|
static int node_group_separate_invoke(bContext *C, wmOperator * /*op*/, const wmEvent * /*event*/)
|
2012-08-01 19:11:17 +00:00
|
|
|
{
|
2015-08-16 17:32:01 +10:00
|
|
|
uiPopupMenu *pup = UI_popup_menu_begin(
|
|
|
|
|
C, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Separate"), ICON_NONE);
|
2014-11-09 21:20:40 +01:00
|
|
|
uiLayout *layout = UI_popup_menu_layout(pup);
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2013-04-20 16:50:05 +00:00
|
|
|
uiLayoutSetOperatorContext(layout, WM_OP_EXEC_DEFAULT);
|
2021-06-02 17:19:36 +02:00
|
|
|
uiItemEnumO(layout, "NODE_OT_group_separate", nullptr, 0, "type", NODE_GS_COPY);
|
|
|
|
|
uiItemEnumO(layout, "NODE_OT_group_separate", nullptr, 0, "type", NODE_GS_MOVE);
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2014-11-09 21:20:40 +01:00
|
|
|
UI_popup_menu_end(C, pup);
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2014-10-28 17:51:06 +01:00
|
|
|
return OPERATOR_INTERFACE;
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void NODE_OT_group_separate(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
/* identifiers */
|
|
|
|
|
ot->name = "Separate";
|
|
|
|
|
ot->description = "Separate selected nodes from the node group";
|
|
|
|
|
ot->idname = "NODE_OT_group_separate";
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2012-08-01 19:11:17 +00:00
|
|
|
/* api callbacks */
|
|
|
|
|
ot->invoke = node_group_separate_invoke;
|
|
|
|
|
ot->exec = node_group_separate_exec;
|
2013-06-05 19:28:59 +00:00
|
|
|
ot->poll = node_group_operator_editable;
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2012-08-01 19:11:17 +00:00
|
|
|
/* flags */
|
|
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2012-08-01 19:11:17 +00:00
|
|
|
RNA_def_enum(ot->srna, "type", node_group_separate_types, NODE_GS_COPY, "Type", "");
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-06 16:14:43 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Make Group Operator
|
|
|
|
|
* \{ */
|
2012-08-01 19:11:17 +00:00
|
|
|
|
2022-12-02 13:20:40 -06:00
|
|
|
static VectorSet<bNode *> get_nodes_to_group(bNodeTree &node_tree, bNode *group_node)
|
2012-08-01 19:11:17 +00:00
|
|
|
{
|
2022-12-02 13:20:40 -06:00
|
|
|
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;
|
2013-03-18 16:34:57 +00:00
|
|
|
}
|
|
|
|
|
|
2021-12-03 16:25:17 -05:00
|
|
|
static bool node_group_make_test_selected(bNodeTree &ntree,
|
2022-12-02 13:20:40 -06:00
|
|
|
const VectorSet<bNode *> &nodes_to_group,
|
2013-03-18 18:25:05 +00:00
|
|
|
const char *ntree_idname,
|
2022-09-02 17:37:10 -05:00
|
|
|
ReportList &reports)
|
2013-03-18 16:34:57 +00:00
|
|
|
{
|
2022-12-02 13:20:40 -06:00
|
|
|
if (nodes_to_group.is_empty()) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2013-03-18 16:34:57 +00:00
|
|
|
/* make a local pseudo node tree to pass to the node poll functions */
|
2021-06-02 17:19:36 +02:00
|
|
|
bNodeTree *ngroup = ntreeAddTree(nullptr, "Pseudo Node Group", ntree_idname);
|
2022-12-02 13:20:40 -06:00
|
|
|
BLI_SCOPED_DEFER([&]() {
|
|
|
|
|
ntreeFreeTree(ngroup);
|
|
|
|
|
MEM_freeN(ngroup);
|
|
|
|
|
});
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-03-18 16:34:57 +00:00
|
|
|
/* check poll functions for selected nodes */
|
2022-12-02 13:20:40 -06:00
|
|
|
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);
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
2022-12-02 13:20:40 -06:00
|
|
|
else {
|
|
|
|
|
BKE_reportf(&reports, RPT_WARNING, "Can not add node '%s' in a group", node->name);
|
|
|
|
|
}
|
|
|
|
|
return false;
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2019-03-26 21:16:47 +11:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-08-01 19:11:17 +00:00
|
|
|
/* check if all connections are OK, no unselected node has both
|
|
|
|
|
* inputs and outputs to a selection */
|
2022-12-02 13:20:40 -06:00
|
|
|
ntree.ensure_topology_cache();
|
2022-12-02 11:12:51 -06:00
|
|
|
for (bNode *node : ntree.all_nodes()) {
|
2022-12-02 13:20:40 -06:00
|
|
|
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())) {
|
2013-03-18 18:25:05 +00:00
|
|
|
return false;
|
|
|
|
|
}
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
2022-12-02 13:20:40 -06:00
|
|
|
|
2013-03-18 18:25:05 +00:00
|
|
|
return true;
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
|
|
|
|
|
2022-12-02 13:20:40 -06:00
|
|
|
static void get_min_max_of_nodes(const Span<bNode *> nodes,
|
|
|
|
|
const bool use_size,
|
|
|
|
|
float2 &min,
|
|
|
|
|
float2 &max)
|
2012-08-01 19:11:17 +00:00
|
|
|
{
|
2022-12-02 13:20:40 -06:00
|
|
|
if (nodes.is_empty()) {
|
|
|
|
|
min = float2(0);
|
|
|
|
|
max = float2(0);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-08-01 19:11:17 +00:00
|
|
|
INIT_MINMAX2(min, max);
|
2022-12-02 13:20:40 -06:00
|
|
|
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;
|
2022-09-02 14:09:32 -05:00
|
|
|
math::min_max(loc, min, max);
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-02 12:33:09 +01:00
|
|
|
/**
|
2022-12-21 12:26:09 -06:00
|
|
|
* Skip reroute nodes when finding the the socket to use as an example for a new group interface
|
|
|
|
|
* item. This moves "inward" into nodes selected for grouping to find properties like whether a
|
|
|
|
|
* connected socket has a hidden value. It only works in trivial situations-- a single line of
|
|
|
|
|
* connected reroutes with no branching.
|
2022-11-02 12:33:09 +01:00
|
|
|
*/
|
2022-12-21 12:26:09 -06:00
|
|
|
static const bNodeSocket &find_socket_to_use_for_interface(const bNodeTree &node_tree,
|
|
|
|
|
const bNodeSocket &socket)
|
2022-11-02 12:33:09 +01:00
|
|
|
{
|
2022-12-21 12:26:09 -06:00
|
|
|
if (node_tree.has_available_link_cycle()) {
|
|
|
|
|
return socket;
|
2022-11-02 12:33:09 +01:00
|
|
|
}
|
2022-12-21 12:26:09 -06:00
|
|
|
const bNode &node = socket.owner_node();
|
|
|
|
|
if (!node.is_reroute()) {
|
|
|
|
|
return socket;
|
|
|
|
|
}
|
|
|
|
|
const bNodeSocket &other_socket = socket.in_out == SOCK_IN ? node.output_socket(0) :
|
|
|
|
|
node.input_socket(0);
|
|
|
|
|
if (!other_socket.is_logically_linked()) {
|
|
|
|
|
return socket;
|
|
|
|
|
}
|
|
|
|
|
return *other_socket.logically_linked_sockets().first();
|
|
|
|
|
}
|
2022-11-02 12:33:09 +01:00
|
|
|
|
2022-12-21 12:26:09 -06:00
|
|
|
/**
|
|
|
|
|
* The output sockets of group nodes usually have consciously given names so they have
|
|
|
|
|
* precedence over socket names the link points to.
|
|
|
|
|
*/
|
|
|
|
|
static bool prefer_node_for_interface_name(const bNode &node)
|
|
|
|
|
{
|
|
|
|
|
return node.is_group() || node.is_group_input() || node.is_group_output();
|
|
|
|
|
}
|
2022-11-02 12:33:09 +01:00
|
|
|
|
2022-12-21 12:26:09 -06:00
|
|
|
static bNodeSocket *add_interface_from_socket(const bNodeTree &original_tree,
|
|
|
|
|
bNodeTree &tree_for_interface,
|
|
|
|
|
const bNodeSocket &socket)
|
|
|
|
|
{
|
|
|
|
|
/* The "example socket" has to have the same `in_out` status as the new interface socket. */
|
|
|
|
|
const bNodeSocket &socket_for_io = find_socket_to_use_for_interface(original_tree, socket);
|
|
|
|
|
const bNode &node_for_io = socket_for_io.owner_node();
|
|
|
|
|
const bNodeSocket &socket_for_name = prefer_node_for_interface_name(socket.owner_node()) ?
|
|
|
|
|
socket :
|
|
|
|
|
socket_for_io;
|
|
|
|
|
return ntreeAddSocketInterfaceFromSocketWithName(&tree_for_interface,
|
|
|
|
|
&node_for_io,
|
|
|
|
|
&socket_for_io,
|
|
|
|
|
socket_for_io.idname,
|
|
|
|
|
socket_for_name.name);
|
2022-11-02 12:33:09 +01:00
|
|
|
}
|
|
|
|
|
|
2022-12-02 13:20:40 -06:00
|
|
|
static void node_group_make_insert_selected(const bContext &C,
|
|
|
|
|
bNodeTree &ntree,
|
|
|
|
|
bNode *gnode,
|
|
|
|
|
const VectorSet<bNode *> &nodes_to_move)
|
2012-08-01 19:11:17 +00:00
|
|
|
{
|
2021-12-03 16:25:17 -05:00
|
|
|
Main *bmain = CTX_data_main(&C);
|
2022-12-21 12:26:09 -06:00
|
|
|
bNodeTree &group = *reinterpret_cast<bNodeTree *>(gnode->id);
|
2022-12-02 13:20:40 -06:00
|
|
|
BLI_assert(!nodes_to_move.contains(gnode));
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-12-21 12:26:09 -06:00
|
|
|
node_deselect_all(group);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-12-21 12:26:09 -06:00
|
|
|
float2 min, max;
|
2022-12-02 13:20:40 -06:00
|
|
|
get_min_max_of_nodes(nodes_to_move, false, min, max);
|
2022-12-21 12:26:09 -06:00
|
|
|
const float2 center = math::midpoint(min, max);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-12-03 16:25:17 -05:00
|
|
|
float2 real_min, real_max;
|
2022-12-02 13:20:40 -06:00
|
|
|
get_min_max_of_nodes(nodes_to_move, true, real_min, real_max);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-12-21 12:26:09 -06:00
|
|
|
/* Reuse an existing output node or create a new one. */
|
|
|
|
|
group.ensure_topology_cache();
|
|
|
|
|
bNode *output_node = [&]() {
|
|
|
|
|
if (bNode *node = group.group_output_node()) {
|
|
|
|
|
return node;
|
|
|
|
|
}
|
|
|
|
|
bNode *output_node = nodeAddStaticNode(&C, &group, NODE_GROUP_OUTPUT);
|
|
|
|
|
output_node->locx = real_max[0] - center[0] + 50.0f;
|
|
|
|
|
return output_node;
|
|
|
|
|
}();
|
|
|
|
|
|
|
|
|
|
/* Create new group input node for easier organization of the new nodes inside the group. */
|
|
|
|
|
bNode *input_node = nodeAddStaticNode(&C, &group, NODE_GROUP_INPUT);
|
|
|
|
|
input_node->locx = real_min[0] - center[0] - 200.0f;
|
|
|
|
|
|
|
|
|
|
struct InputSocketInfo {
|
|
|
|
|
/* The unselected node the original link came from. */
|
|
|
|
|
bNode *from_node;
|
|
|
|
|
/* All the links that came from the socket on the unselected node. */
|
|
|
|
|
Vector<bNodeLink *> links;
|
|
|
|
|
const bNodeSocket *interface_socket;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct OutputLinkInfo {
|
|
|
|
|
bNodeLink *link;
|
|
|
|
|
const bNodeSocket *interface_socket;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* Map from single non-selected output sockets to potentially many selected input sockets. */
|
|
|
|
|
Map<bNodeSocket *, InputSocketInfo> input_links;
|
|
|
|
|
Vector<OutputLinkInfo> output_links;
|
|
|
|
|
Set<bNodeLink *> internal_links_to_move;
|
|
|
|
|
Set<bNodeLink *> links_to_remove;
|
2020-10-16 17:30:12 +02:00
|
|
|
|
2022-12-21 12:26:09 -06:00
|
|
|
ntree.ensure_topology_cache();
|
|
|
|
|
for (bNode *node : nodes_to_move) {
|
|
|
|
|
for (bNodeSocket *input_socket : node->input_sockets()) {
|
|
|
|
|
for (bNodeLink *link : input_socket->directly_linked_links()) {
|
|
|
|
|
if (nodeLinkIsHidden(link)) {
|
|
|
|
|
links_to_remove.add(link);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (nodes_to_move.contains(link->fromnode)) {
|
|
|
|
|
internal_links_to_move.add(link);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
InputSocketInfo &info = input_links.lookup_or_add_default(link->fromsock);
|
|
|
|
|
info.from_node = link->fromnode;
|
|
|
|
|
info.links.append(link);
|
|
|
|
|
if (!info.interface_socket) {
|
|
|
|
|
info.interface_socket = add_interface_from_socket(ntree, group, *link->tosock);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-02-01 17:48:44 +01:00
|
|
|
}
|
2022-12-21 12:26:09 -06:00
|
|
|
for (bNodeSocket *output_socket : node->output_sockets()) {
|
|
|
|
|
for (bNodeLink *link : output_socket->directly_linked_links()) {
|
|
|
|
|
if (nodeLinkIsHidden(link)) {
|
|
|
|
|
links_to_remove.add(link);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (nodes_to_move.contains(link->tonode)) {
|
|
|
|
|
internal_links_to_move.add(link);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
output_links.append({link, add_interface_from_socket(ntree, group, *link->fromsock)});
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-02-01 17:48:44 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-21 12:26:09 -06:00
|
|
|
struct NewInternalLinkInfo {
|
|
|
|
|
bNode *node;
|
|
|
|
|
bNodeSocket *socket;
|
|
|
|
|
const bNodeSocket *interface_socket;
|
|
|
|
|
};
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-12-21 12:26:09 -06:00
|
|
|
const bool expose_visible = nodes_to_move.size() == 1;
|
|
|
|
|
Vector<NewInternalLinkInfo> new_internal_links;
|
|
|
|
|
if (expose_visible) {
|
|
|
|
|
for (bNode *node : nodes_to_move) {
|
|
|
|
|
auto expose_sockets = [&](const Span<bNodeSocket *> sockets) {
|
|
|
|
|
for (bNodeSocket *socket : sockets) {
|
|
|
|
|
if (!socket->is_available() || socket->is_hidden()) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (socket->is_directly_linked()) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
const bNodeSocket *io_socket = ntreeAddSocketInterfaceFromSocket(&group, node, socket);
|
|
|
|
|
new_internal_links.append({node, socket, io_socket});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
expose_sockets(node->input_sockets());
|
|
|
|
|
expose_sockets(node->output_sockets());
|
2022-12-02 13:20:40 -06:00
|
|
|
}
|
2022-12-21 12:26:09 -06:00
|
|
|
}
|
2022-12-01 14:53:27 -06:00
|
|
|
|
2022-12-21 12:26:09 -06:00
|
|
|
/* Un-parent nodes when only the parent or child moves into the group. */
|
|
|
|
|
for (bNode *node : ntree.all_nodes()) {
|
|
|
|
|
if (node->parent && nodes_to_move.contains(node->parent) && !nodes_to_move.contains(node)) {
|
2022-12-02 13:20:40 -06:00
|
|
|
nodeDetachNode(&ntree, node);
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2012-08-06 11:23:09 +00:00
|
|
|
}
|
2022-12-21 12:26:09 -06:00
|
|
|
for (bNode *node : nodes_to_move) {
|
|
|
|
|
if (node->parent && !nodes_to_move.contains(node->parent)) {
|
|
|
|
|
nodeDetachNode(&ntree, node);
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-12-21 12:26:09 -06:00
|
|
|
/* Move animation data from the parent tree to the group. */
|
|
|
|
|
if (ntree.adt) {
|
|
|
|
|
ListBase anim_basepaths = {nullptr, nullptr};
|
|
|
|
|
for (bNode *node : nodes_to_move) {
|
|
|
|
|
PointerRNA ptr;
|
|
|
|
|
RNA_pointer_create(&ntree.id, &RNA_Node, node, &ptr);
|
|
|
|
|
if (char *path = RNA_path_from_ID_to_struct(&ptr)) {
|
|
|
|
|
BLI_addtail(&anim_basepaths, animation_basepath_change_new(path, path));
|
2022-03-30 23:07:11 +02:00
|
|
|
}
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
2022-12-21 12:26:09 -06:00
|
|
|
BKE_animdata_transfer_by_basepath(bmain, &ntree.id, &group.id, &anim_basepaths);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-12-21 12:26:09 -06:00
|
|
|
LISTBASE_FOREACH_MUTABLE (AnimationBasePathChange *, basepath_change, &anim_basepaths) {
|
|
|
|
|
animation_basepath_change_free(basepath_change);
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
|
|
2022-12-21 12:26:09 -06:00
|
|
|
/* Move nodes into the group. */
|
|
|
|
|
for (bNode *node : nodes_to_move) {
|
|
|
|
|
BLI_remlink(&ntree.nodes, node);
|
|
|
|
|
BLI_addtail(&group.nodes, node);
|
|
|
|
|
nodeUniqueID(&group, node);
|
|
|
|
|
nodeUniqueName(&group, node);
|
2020-07-27 15:03:23 +03:00
|
|
|
|
2022-12-21 12:26:09 -06:00
|
|
|
BKE_ntree_update_tag_node_removed(&ntree);
|
|
|
|
|
BKE_ntree_update_tag_node_new(&group, node);
|
2020-07-27 15:03:23 +03:00
|
|
|
}
|
2022-12-21 12:26:09 -06:00
|
|
|
nodeRebuildIDVector(&ntree);
|
|
|
|
|
|
|
|
|
|
node_group_update(&ntree, gnode);
|
|
|
|
|
node_group_input_update(&group, input_node);
|
|
|
|
|
node_group_output_update(&group, output_node);
|
2020-07-27 15:03:23 +03:00
|
|
|
|
2013-03-18 16:34:57 +00:00
|
|
|
/* move nodes in the group to the center */
|
2022-12-02 13:20:40 -06:00
|
|
|
for (bNode *node : nodes_to_move) {
|
|
|
|
|
if (!node->parent) {
|
2013-03-18 16:34:57 +00:00
|
|
|
node->locx -= center[0];
|
|
|
|
|
node->locy -= center[1];
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
|
|
2022-12-21 12:26:09 -06:00
|
|
|
for (bNodeLink *link : internal_links_to_move) {
|
|
|
|
|
BLI_remlink(&ntree.links, link);
|
|
|
|
|
BLI_addtail(&group.links, link);
|
|
|
|
|
BKE_ntree_update_tag_link_removed(&ntree);
|
|
|
|
|
BKE_ntree_update_tag_link_added(&group, link);
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-12-21 12:26:09 -06:00
|
|
|
for (bNodeLink *link : links_to_remove) {
|
|
|
|
|
nodeRemLink(&ntree, link);
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-12-21 12:26:09 -06:00
|
|
|
for (const auto item : input_links.items()) {
|
|
|
|
|
const char *interface_identifier = item.value.interface_socket->identifier;
|
|
|
|
|
bNodeSocket *input_socket = node_group_input_find_socket(input_node, interface_identifier);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-12-21 12:26:09 -06:00
|
|
|
for (bNodeLink *link : item.value.links) {
|
|
|
|
|
/* Move the link into the new group, connected from the input node to the original socket. */
|
|
|
|
|
BLI_remlink(&ntree.links, link);
|
|
|
|
|
BLI_addtail(&group.links, link);
|
|
|
|
|
BKE_ntree_update_tag_link_removed(&ntree);
|
|
|
|
|
BKE_ntree_update_tag_link_added(&group, link);
|
|
|
|
|
link->fromnode = input_node;
|
|
|
|
|
link->fromsock = input_socket;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-12-21 12:26:09 -06:00
|
|
|
/* Add a new link outside of the group. */
|
|
|
|
|
bNodeSocket *group_node_socket = node_group_find_input_socket(gnode, interface_identifier);
|
|
|
|
|
nodeAddLink(&ntree, item.value.from_node, item.key, gnode, group_node_socket);
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-12-21 12:26:09 -06:00
|
|
|
for (const OutputLinkInfo &info : output_links) {
|
|
|
|
|
/* Create a new link inside of the group. */
|
|
|
|
|
const char *io_identifier = info.interface_socket->identifier;
|
|
|
|
|
bNodeSocket *output_sock = node_group_output_find_socket(output_node, io_identifier);
|
|
|
|
|
nodeAddLink(&group, info.link->fromnode, info.link->fromsock, output_node, output_sock);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-12-21 12:26:09 -06:00
|
|
|
/* Reconnect the link to the group node instead of the node now inside the group. */
|
|
|
|
|
info.link->fromnode = gnode;
|
|
|
|
|
info.link->fromsock = node_group_find_output_socket(gnode, io_identifier);
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-12-21 12:26:09 -06:00
|
|
|
for (const NewInternalLinkInfo &info : new_internal_links) {
|
|
|
|
|
const char *io_identifier = info.interface_socket->identifier;
|
|
|
|
|
if (info.socket->in_out == SOCK_IN) {
|
|
|
|
|
bNodeSocket *input_socket = node_group_input_find_socket(input_node, io_identifier);
|
|
|
|
|
nodeAddLink(&group, input_node, input_socket, info.node, info.socket);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
bNodeSocket *output_socket = node_group_output_find_socket(output_node, io_identifier);
|
|
|
|
|
nodeAddLink(&group, info.node, info.socket, output_node, output_socket);
|
2012-11-04 19:18:12 +00:00
|
|
|
}
|
|
|
|
|
}
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
|
|
|
|
|
2022-12-02 13:20:40 -06:00
|
|
|
static bNode *node_group_make_from_nodes(const bContext &C,
|
|
|
|
|
bNodeTree &ntree,
|
|
|
|
|
const VectorSet<bNode *> &nodes_to_group,
|
|
|
|
|
const char *ntype,
|
|
|
|
|
const char *ntreetype)
|
2012-08-01 19:11:17 +00:00
|
|
|
{
|
2021-12-03 16:25:17 -05:00
|
|
|
Main *bmain = CTX_data_main(&C);
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2021-12-03 16:25:17 -05:00
|
|
|
float2 min, max;
|
2022-12-02 13:20:40 -06:00
|
|
|
get_min_max_of_nodes(nodes_to_group, false, min, max);
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2022-01-06 13:54:52 +11:00
|
|
|
/* New node-tree. */
|
2020-10-16 17:30:12 +02:00
|
|
|
bNodeTree *ngroup = ntreeAddTree(bmain, "NodeGroup", ntreetype);
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2012-08-01 19:11:17 +00:00
|
|
|
/* make group node */
|
2021-12-03 16:25:17 -05:00
|
|
|
bNode *gnode = nodeAddNode(&C, &ntree, ntype);
|
2013-03-18 16:34:57 +00:00
|
|
|
gnode->id = (ID *)ngroup;
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2012-08-01 19:11:17 +00:00
|
|
|
gnode->locx = 0.5f * (min[0] + max[0]);
|
|
|
|
|
gnode->locy = 0.5f * (min[1] + max[1]);
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2022-12-02 13:20:40 -06:00
|
|
|
node_group_make_insert_selected(C, ntree, gnode, nodes_to_group);
|
2012-08-01 19:11:17 +00:00
|
|
|
|
|
|
|
|
return gnode;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int node_group_make_exec(bContext *C, wmOperator *op)
|
|
|
|
|
{
|
2021-12-03 16:25:17 -05:00
|
|
|
SpaceNode &snode = *CTX_wm_space_node(C);
|
|
|
|
|
bNodeTree &ntree = *snode.edittree;
|
2013-04-20 16:50:05 +00:00
|
|
|
const char *ntree_idname = group_ntree_idname(C);
|
2021-02-15 19:35:24 +01:00
|
|
|
const char *node_idname = node_group_idname(C);
|
2013-05-07 15:28:42 +00:00
|
|
|
Main *bmain = CTX_data_main(C);
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2014-11-18 15:51:31 +01:00
|
|
|
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2022-12-02 13:20:40 -06:00
|
|
|
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)) {
|
2013-03-18 16:34:57 +00:00
|
|
|
return OPERATOR_CANCELLED;
|
2019-03-26 21:16:47 +11:00
|
|
|
}
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2022-12-02 13:20:40 -06:00
|
|
|
bNode *gnode = node_group_make_from_nodes(*C, ntree, nodes_to_group, node_idname, ntree_idname);
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2012-08-01 19:11:17 +00:00
|
|
|
if (gnode) {
|
2020-10-16 17:30:12 +02:00
|
|
|
bNodeTree *ngroup = (bNodeTree *)gnode->id;
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2021-12-03 16:25:17 -05:00
|
|
|
nodeSetActive(&ntree, gnode);
|
2013-03-18 16:34:57 +00:00
|
|
|
if (ngroup) {
|
2021-12-03 16:25:17 -05:00
|
|
|
ED_node_tree_push(&snode, ngroup, gnode);
|
2013-03-18 16:34:57 +00:00
|
|
|
}
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2021-12-21 15:18:56 +01:00
|
|
|
ED_node_tree_propagate_change(C, bmain, nullptr);
|
2019-01-15 23:24:20 +11:00
|
|
|
|
2022-10-05 13:44:02 -05:00
|
|
|
WM_event_add_notifier(C, NC_NODE | NA_ADDED, nullptr);
|
2022-09-28 15:08:09 +02:00
|
|
|
|
2021-02-05 16:23:34 +11:00
|
|
|
/* We broke relations in node tree, need to rebuild them in the graphs. */
|
2019-01-15 23:24:20 +11:00
|
|
|
DEG_relations_tag_update(bmain);
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2012-08-01 19:11:17 +00:00
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void NODE_OT_group_make(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
/* identifiers */
|
2013-03-18 16:34:57 +00:00
|
|
|
ot->name = "Make Group";
|
2012-08-01 19:11:17 +00:00
|
|
|
ot->description = "Make group from selected nodes";
|
|
|
|
|
ot->idname = "NODE_OT_group_make";
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2012-08-01 19:11:17 +00:00
|
|
|
/* api callbacks */
|
|
|
|
|
ot->exec = node_group_make_exec;
|
2013-06-05 19:28:59 +00:00
|
|
|
ot->poll = node_group_operator_editable;
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2012-08-01 19:11:17 +00:00
|
|
|
/* flags */
|
|
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
2013-03-18 16:34:57 +00:00
|
|
|
}
|
2012-08-01 19:11:17 +00:00
|
|
|
|
2020-11-06 16:14:43 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Group Insert Operator
|
|
|
|
|
* \{ */
|
2013-03-18 16:34:57 +00:00
|
|
|
|
2022-12-14 19:35:43 +01:00
|
|
|
static bool node_tree_contains_tree_recursive(const bNodeTree &ntree_to_search_in,
|
|
|
|
|
const bNodeTree &ntree_to_search_for)
|
|
|
|
|
{
|
|
|
|
|
if (&ntree_to_search_in == &ntree_to_search_for) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
ntree_to_search_in.ensure_topology_cache();
|
|
|
|
|
for (const bNode *node : ntree_to_search_in.group_nodes()) {
|
|
|
|
|
if (node->id) {
|
|
|
|
|
if (node_tree_contains_tree_recursive(*reinterpret_cast<bNodeTree *>(node->id),
|
|
|
|
|
ntree_to_search_for)) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-18 16:34:57 +00:00
|
|
|
static int node_group_insert_exec(bContext *C, wmOperator *op)
|
|
|
|
|
{
|
|
|
|
|
SpaceNode *snode = CTX_wm_space_node(C);
|
|
|
|
|
bNodeTree *ntree = snode->edittree;
|
2021-02-15 19:35:24 +01:00
|
|
|
const char *node_idname = node_group_idname(C);
|
2013-05-07 15:28:42 +00:00
|
|
|
Main *bmain = CTX_data_main(C);
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2014-11-18 15:51:31 +01:00
|
|
|
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2020-10-16 17:30:12 +02:00
|
|
|
bNode *gnode = node_group_get_active(C, node_idname);
|
2019-03-26 21:16:47 +11:00
|
|
|
if (!gnode || !gnode->id) {
|
2013-03-18 16:34:57 +00:00
|
|
|
return OPERATOR_CANCELLED;
|
2019-03-26 21:16:47 +11:00
|
|
|
}
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2022-12-14 19:35:43 +01:00
|
|
|
bNodeTree *ngroup = reinterpret_cast<bNodeTree *>(gnode->id);
|
2022-12-02 13:20:40 -06:00
|
|
|
VectorSet<bNode *> nodes_to_group = get_nodes_to_group(*ntree, gnode);
|
2022-12-14 19:35:43 +01:00
|
|
|
|
|
|
|
|
/* Make sure that there won't be a node group containing itself afterwards. */
|
|
|
|
|
for (const bNode *group : nodes_to_group) {
|
|
|
|
|
if (!group->is_group() || group->id == nullptr) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (node_tree_contains_tree_recursive(*reinterpret_cast<bNodeTree *>(group->id), *ngroup)) {
|
|
|
|
|
BKE_reportf(
|
|
|
|
|
op->reports, RPT_WARNING, "Can not insert group '%s' in '%s'", group->name, gnode->name);
|
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-02 13:20:40 -06:00
|
|
|
if (!node_group_make_test_selected(*ntree, nodes_to_group, ngroup->idname, *op->reports)) {
|
2013-03-18 16:34:57 +00:00
|
|
|
return OPERATOR_CANCELLED;
|
2019-03-26 21:16:47 +11:00
|
|
|
}
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2022-12-02 13:20:40 -06:00
|
|
|
node_group_make_insert_selected(*C, *ntree, gnode, nodes_to_group);
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2013-03-18 16:34:57 +00:00
|
|
|
nodeSetActive(ntree, gnode);
|
|
|
|
|
ED_node_tree_push(snode, ngroup, gnode);
|
2021-12-21 15:18:56 +01:00
|
|
|
ED_node_tree_propagate_change(C, bmain, nullptr);
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2013-03-18 16:34:57 +00:00
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void NODE_OT_group_insert(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
/* identifiers */
|
|
|
|
|
ot->name = "Group Insert";
|
|
|
|
|
ot->description = "Insert selected nodes into a node group";
|
|
|
|
|
ot->idname = "NODE_OT_group_insert";
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2013-03-18 16:34:57 +00:00
|
|
|
/* api callbacks */
|
|
|
|
|
ot->exec = node_group_insert_exec;
|
2013-06-05 19:28:59 +00:00
|
|
|
ot->poll = node_group_operator_editable;
|
2018-06-04 09:31:30 +02:00
|
|
|
|
2013-03-18 16:34:57 +00:00
|
|
|
/* flags */
|
|
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
2012-08-01 19:11:17 +00:00
|
|
|
}
|
2020-11-06 16:14:43 +11:00
|
|
|
|
|
|
|
|
/** \} */
|
2022-01-20 10:36:56 -06:00
|
|
|
|
|
|
|
|
} // namespace blender::ed::space_node
|