2023-08-16 00:20:26 +10:00
|
|
|
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
2023-05-31 16:19:06 +02:00
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
2005-12-18 13:46:01 +00:00
|
|
|
|
2019-02-18 08:08:12 +11:00
|
|
|
/** \file
|
|
|
|
|
* \ingroup bke
|
2011-02-27 20:40:57 +00:00
|
|
|
*/
|
|
|
|
|
|
2019-02-01 12:44:19 +11:00
|
|
|
#include "CLG_log.h"
|
|
|
|
|
|
2011-02-21 13:47:49 +00:00
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
|
|
2021-02-02 09:51:38 -06:00
|
|
|
#include <climits>
|
|
|
|
|
#include <cstddef>
|
|
|
|
|
#include <cstdlib>
|
|
|
|
|
#include <cstring>
|
2023-05-26 19:17:51 +02:00
|
|
|
#include <optional>
|
2005-12-18 13:46:01 +00:00
|
|
|
|
2020-09-10 13:33:29 +02:00
|
|
|
/* Allow using deprecated functionality for .blend file I/O. */
|
|
|
|
|
#define DNA_DEPRECATED_ALLOW
|
|
|
|
|
|
2011-11-02 19:24:30 +00:00
|
|
|
#include "DNA_action_types.h"
|
2010-01-27 05:42:17 +00:00
|
|
|
#include "DNA_anim_types.h"
|
2020-12-15 10:47:58 +11:00
|
|
|
#include "DNA_collection_types.h"
|
2023-03-13 10:42:51 +01:00
|
|
|
#include "DNA_gpencil_legacy_types.h"
|
2019-02-27 12:34:56 +11:00
|
|
|
#include "DNA_light_types.h"
|
2020-03-19 09:33:03 +01:00
|
|
|
#include "DNA_linestyle_types.h"
|
2012-12-03 16:21:43 +00:00
|
|
|
#include "DNA_material_types.h"
|
2020-12-02 13:25:25 +01:00
|
|
|
#include "DNA_modifier_types.h"
|
2011-02-21 13:47:49 +00:00
|
|
|
#include "DNA_node_types.h"
|
2011-09-05 21:01:50 +00:00
|
|
|
#include "DNA_scene_types.h"
|
2012-12-03 16:21:43 +00:00
|
|
|
#include "DNA_texture_types.h"
|
|
|
|
|
#include "DNA_world_types.h"
|
2011-02-21 13:47:49 +00:00
|
|
|
|
2021-12-06 16:50:44 -05:00
|
|
|
#include "BLI_color.hh"
|
2019-06-03 17:08:25 +02:00
|
|
|
#include "BLI_ghash.h"
|
2020-03-19 09:33:03 +01:00
|
|
|
#include "BLI_listbase.h"
|
2021-02-16 17:15:08 -06:00
|
|
|
#include "BLI_map.hh"
|
2023-12-17 14:00:07 +01:00
|
|
|
#include "BLI_math_rotation_types.hh"
|
2011-09-05 21:01:50 +00:00
|
|
|
#include "BLI_path_util.h"
|
2022-12-01 14:53:27 -06:00
|
|
|
#include "BLI_rand.hh"
|
2021-09-23 15:21:31 -05:00
|
|
|
#include "BLI_set.hh"
|
|
|
|
|
#include "BLI_stack.hh"
|
2017-01-16 17:33:34 +01:00
|
|
|
#include "BLI_string.h"
|
2023-05-13 17:38:48 +10:00
|
|
|
#include "BLI_string_utf8.h"
|
2023-10-18 17:15:30 +02:00
|
|
|
#include "BLI_string_utils.hh"
|
2021-12-06 16:50:44 -05:00
|
|
|
#include "BLI_threads.h"
|
2024-01-19 14:32:28 +01:00
|
|
|
#include "BLI_time.h"
|
2011-09-05 21:01:50 +00:00
|
|
|
#include "BLI_utildefines.h"
|
2021-09-23 15:21:31 -05:00
|
|
|
#include "BLI_vector_set.hh"
|
2024-02-09 18:59:42 +01:00
|
|
|
#include "BLT_translation.hh"
|
2012-03-17 14:42:44 +00:00
|
|
|
|
2024-01-18 22:50:23 +02:00
|
|
|
#include "IMB_imbuf.hh"
|
2023-07-17 20:13:52 +02:00
|
|
|
|
2024-02-28 11:51:03 +01:00
|
|
|
#include "BKE_anim_data.hh"
|
2010-12-29 11:51:53 +00:00
|
|
|
#include "BKE_animsys.h"
|
2023-11-16 11:41:55 +01:00
|
|
|
#include "BKE_asset.hh"
|
2024-02-09 19:23:03 +01:00
|
|
|
#include "BKE_bpath.hh"
|
2023-12-21 10:10:53 +01:00
|
|
|
#include "BKE_colortools.hh"
|
2023-11-16 11:41:55 +01:00
|
|
|
#include "BKE_context.hh"
|
2020-12-15 12:17:31 +01:00
|
|
|
#include "BKE_cryptomatte.h"
|
2024-02-10 18:25:14 +01:00
|
|
|
#include "BKE_global.hh"
|
2024-03-26 12:57:30 -04:00
|
|
|
#include "BKE_idprop.hh"
|
2024-01-20 19:17:36 +01:00
|
|
|
#include "BKE_idtype.hh"
|
2022-03-11 17:50:57 +01:00
|
|
|
#include "BKE_image_format.h"
|
2024-01-15 12:44:04 -05:00
|
|
|
#include "BKE_lib_id.hh"
|
2024-01-18 12:20:42 +01:00
|
|
|
#include "BKE_lib_query.hh"
|
2023-12-01 19:43:16 +01:00
|
|
|
#include "BKE_main.hh"
|
2023-05-15 15:14:22 +02:00
|
|
|
#include "BKE_node.hh"
|
2024-01-26 12:40:01 +01:00
|
|
|
#include "BKE_node_enum.hh"
|
2022-05-30 12:54:07 +02:00
|
|
|
#include "BKE_node_runtime.hh"
|
2023-06-14 13:56:57 +02:00
|
|
|
#include "BKE_node_tree_anonymous_attributes.hh"
|
2023-08-30 12:37:21 +02:00
|
|
|
#include "BKE_node_tree_interface.hh"
|
2023-11-16 11:41:55 +01:00
|
|
|
#include "BKE_node_tree_update.hh"
|
Geometry Nodes: add simulation support
This adds support for building simulations with geometry nodes. A new
`Simulation Input` and `Simulation Output` node allow maintaining a
simulation state across multiple frames. Together these two nodes form
a `simulation zone` which contains all the nodes that update the simulation
state from one frame to the next.
A new simulation zone can be added via the menu
(`Simulation > Simulation Zone`) or with the node add search.
The simulation state contains a geometry by default. However, it is possible
to add multiple geometry sockets as well as other socket types. Currently,
field inputs are evaluated and stored for the preceding geometry socket in
the order that the sockets are shown. Simulation state items can be added
by linking one of the empty sockets to something else. In the sidebar, there
is a new panel that allows adding, removing and reordering these sockets.
The simulation nodes behave as follows:
* On the first frame, the inputs of the `Simulation Input` node are evaluated
to initialize the simulation state. In later frames these sockets are not
evaluated anymore. The `Delta Time` at the first frame is zero, but the
simulation zone is still evaluated.
* On every next frame, the `Simulation Input` node outputs the simulation
state of the previous frame. Nodes in the simulation zone can edit that
data in arbitrary ways, also taking into account the `Delta Time`. The new
simulation state has to be passed to the `Simulation Output` node where it
is cached and forwarded.
* On a frame that is already cached or baked, the nodes in the simulation
zone are not evaluated, because the `Simulation Output` node can return
the previously cached data directly.
It is not allowed to connect sockets from inside the simulation zone to the
outside without going through the `Simulation Output` node. This is a necessary
restriction to make caching and sub-frame interpolation work. Links can go into
the simulation zone without problems though.
Anonymous attributes are not propagated by the simulation nodes unless they
are explicitly stored in the simulation state. This is unfortunate, but
currently there is no practical and reliable alternative. The core problem
is detecting which anonymous attributes will be required for the simulation
and afterwards. While we can detect this for the current evaluation, we can't
look into the future in time to see what data will be necessary. We intend to
make it easier to explicitly pass data through a simulation in the future,
even if the simulation is in a nested node group.
There is a new `Simulation Nodes` panel in the physics tab in the properties
editor. It allows baking all simulation zones on the selected objects. The
baking options are intentially kept at a minimum for this MVP. More features
for simulation baking as well as baking in general can be expected to be added
separately.
All baked data is stored on disk in a folder next to the .blend file. #106937
describes how baking is implemented in more detail. Volumes can not be baked
yet and materials are lost during baking for now. Packing the baked data into
the .blend file is not yet supported.
The timeline indicates which frames are currently cached, baked or cached but
invalidated by user-changes.
Simulation input and output nodes are internally linked together by their
`bNode.identifier` which stays the same even if the node name changes. They
are generally added and removed together. However, there are still cases where
"dangling" simulation nodes can be created currently. Those generally don't
cause harm, but would be nice to avoid this in more cases in the future.
Co-authored-by: Hans Goudey <h.goudey@me.com>
Co-authored-by: Lukas Tönne <lukas@blender.org>
Pull Request: https://projects.blender.org/blender/blender/pulls/104924
2023-05-03 13:18:51 +02:00
|
|
|
#include "BKE_node_tree_zones.hh"
|
2023-09-04 18:02:16 +02:00
|
|
|
#include "BKE_preview_image.hh"
|
2023-05-02 18:50:34 +02:00
|
|
|
#include "BKE_type_conversions.hh"
|
Three-in-one commit:
- Compositor now is threaded
Enable it with the Scene buttons "Threads". This will handle over nodes to
individual threads to be calculated. However, if nodes depend on others
they have to wait. The current system only threads per entire node, not for
calculating results in parts.
I've reshuffled the node execution code to evaluate 'changed' events, and
prepare the entire tree to become simply parsed for open jobs with a call
to node = getExecutableNode()
By default, even without 'thread' option active, all node execution is
done within a separate thread.
Also fixed issues in yesterdays commit for 'event based' calculations, it
didn't do animated images, or execute (on rendering) the correct nodes
when you don't have Render-Result nodes included.
- Added generic Thread support in blenlib/ module
The renderer and the node system now both use same code for controlling the
threads. This has been moved to a new C file in blenlib/intern/threads.c.
Check this c file for an extensive doc and example how to use it.
The current implementation for Compositing allows unlimited amount of
threads. For rendering it is still tied to two threads, although it is
pretty easy to extend to 4 already. People with giant amounts of cpus can
poke me once for tests. :)
- Bugfix in creating group nodes
Group node definitions demand a clear separation of 'internal sockets' and
'external sockets'. The first are sockets being linked internally, the latter
are sockets exposed as sockets for the group itself.
When sockets were linked both internal and external, Blender crashed. It is
solved now by removing the external link(s).
2006-01-29 11:36:33 +00:00
|
|
|
|
2023-08-10 22:40:27 +02:00
|
|
|
#include "RNA_access.hh"
|
|
|
|
|
#include "RNA_define.hh"
|
|
|
|
|
#include "RNA_enum_types.hh"
|
2022-03-14 16:54:46 +01:00
|
|
|
#include "RNA_prototypes.h"
|
2007-03-24 18:41:54 +00:00
|
|
|
|
2013-03-18 16:34:57 +00:00
|
|
|
#include "NOD_common.h"
|
2023-10-16 10:45:54 +02:00
|
|
|
#include "NOD_composite.hh"
|
2024-04-23 11:13:27 +02:00
|
|
|
#include "NOD_geo_bake.hh"
|
2024-05-31 16:23:31 +02:00
|
|
|
#include "NOD_geo_capture_attribute.hh"
|
2024-04-23 11:13:27 +02:00
|
|
|
#include "NOD_geo_index_switch.hh"
|
2024-04-30 10:19:32 +02:00
|
|
|
#include "NOD_geo_menu_switch.hh"
|
2024-04-23 11:13:27 +02:00
|
|
|
#include "NOD_geo_repeat.hh"
|
|
|
|
|
#include "NOD_geo_simulation.hh"
|
2023-06-23 22:15:42 +02:00
|
|
|
#include "NOD_geometry.hh"
|
2022-09-13 08:44:26 +02:00
|
|
|
#include "NOD_geometry_nodes_lazy_function.hh"
|
2021-08-30 17:13:46 +02:00
|
|
|
#include "NOD_node_declaration.hh"
|
2022-11-26 13:20:18 +01:00
|
|
|
#include "NOD_register.hh"
|
2011-09-05 21:01:50 +00:00
|
|
|
#include "NOD_shader.h"
|
2023-07-02 21:01:57 +02:00
|
|
|
#include "NOD_socket.hh"
|
2011-09-05 21:01:50 +00:00
|
|
|
#include "NOD_texture.h"
|
Christmas coding work!
********* Node editor work:
- To enable Nodes for Materials, you have to set the "Use Nodes"
button, in the new Material buttons "Nodes" Panel or in header
of the Node editor. Doing this will disable Material-Layers.
- Nodes now execute materials ("shaders"), but still only using the
previewrender code.
- Nodes have (optional) previews for rendered images.
- Node headers allow to hide buttons and/or preview image
- Nodes can be dragged larger/smaller (right-bottom corner)
- Nodes can be hidden (minimized) with hotkey H
- CTRL+click on an Input Socket gives a popup with default values.
- Changing Material/Texture or Mix node will adjust Node title.
- Click-drag outside of a Node changes cursor to "Knife' and allows to
draw a rect where to cut Links.
- Added new node types RGBtoBW, Texture, In/Output, ColorRamp
- Material Nodes have options to ouput diffuse or specular, or to use
a negative normal. The input socket 'Normal' will force the material
to use that normal, otherwise it uses the normal from the Material
that has the node tree.
- When drawing a link between two not-matching sockets, Blender inserts
a converting node (now only for value/rgb combos)
- When drawing a link to an input socket that's already in use, the
old link will either disappear or flip to another unused socket.
- A click on a Material Node will activate it, and show all its settings
in the Material Buttons. Active Material Nodes draw the material icon
in red.
- A click on any node will show its options in the Node Panel in the
Material buttons.
- Multiple Output Nodes can be used, to sample contents of a tree, but
only one Output is the real one, which is indicated in a different
color and red material icon.
- Added ThemeColors for node types
- ALT+C will convert existing Material-Layers to Node... this currently
only adds the material/mix nodes and connects them. Dunno if this is
worth a lot of coding work to make perfect?
- Press C to call another "Solve order", which will show all possible
cyclic conflicts (if there are).
- Technical: nodes now use "Type" structs which define the
structure of nodes and in/output sockets. The Type structs store all
fixed info, callbacks, and allow to reconstruct saved Nodes to match
what is required by Blender.
- Defining (new) nodes now is as simple as filling in a fixed
Type struct, plus code some callbacks. A doc will be made!
- Node preview images are by default float
********* Icon drawing:
- Cleanup of how old icons were implemented in new system, making
them 16x16 too, correctly centered *and* scaled.
- Made drawing Icons use float coordinates
- Moved BIF_calcpreview_image() into interface_icons.c, renamed it
icon_from_image(). Removed a lot of unneeded Imbuf magic here! :)
- Skipped scaling and imbuf copying when icons are OK size
********* Preview render:
- Huge cleanup of code....
- renaming BIF_xxx calls that only were used internally
- BIF_previewrender() now accepts an argument for rendering method,
so it supports icons, buttonwindow previewrender and node editor
- Only a single BIF_preview_changed() call now exists, supporting all
signals as needed for buttos and node editor
********* More stuff:
- glutil.c, glaDrawPixelsSafe() and glaDrawPixelsTex() now accept format
argument for GL_FLOAT rects
- Made the ColorBand become a built-in button for interface.c
Was a load of cleanup work in buttons_shading.c...
- removed a load of unneeded glBlendFunc() calls
- Fixed bug in calculating text length for buttons (ancient!)
2005-12-28 15:42:51 +00:00
|
|
|
|
2023-09-22 03:18:17 +02:00
|
|
|
#include "DEG_depsgraph.hh"
|
|
|
|
|
#include "DEG_depsgraph_build.hh"
|
2018-03-16 11:17:45 +01:00
|
|
|
|
2023-08-28 15:01:05 +02:00
|
|
|
#include "BLO_read_write.hh"
|
2020-09-10 13:33:29 +02:00
|
|
|
|
2016-03-12 19:10:36 +01:00
|
|
|
#define NODE_DEFAULT_MAX_WIDTH 700
|
|
|
|
|
|
2021-09-23 15:21:31 -05:00
|
|
|
using blender::Array;
|
2021-12-22 08:47:46 -06:00
|
|
|
using blender::Map;
|
2021-09-23 15:21:31 -05:00
|
|
|
using blender::MutableSpan;
|
|
|
|
|
using blender::Set;
|
|
|
|
|
using blender::Span;
|
|
|
|
|
using blender::Stack;
|
2021-12-15 09:51:57 -06:00
|
|
|
using blender::StringRef;
|
2021-09-23 15:21:31 -05:00
|
|
|
using blender::Vector;
|
|
|
|
|
using blender::VectorSet;
|
2022-05-30 15:31:13 +02:00
|
|
|
using blender::bke::bNodeRuntime;
|
|
|
|
|
using blender::bke::bNodeSocketRuntime;
|
2022-05-30 12:54:07 +02:00
|
|
|
using blender::bke::bNodeTreeRuntime;
|
2021-09-28 12:05:42 -05:00
|
|
|
using blender::nodes::FieldInferencingInterface;
|
2021-09-23 15:21:31 -05:00
|
|
|
using blender::nodes::InputSocketFieldType;
|
|
|
|
|
using blender::nodes::NodeDeclaration;
|
|
|
|
|
using blender::nodes::OutputFieldDependency;
|
|
|
|
|
using blender::nodes::OutputSocketFieldType;
|
|
|
|
|
using blender::nodes::SocketDeclaration;
|
|
|
|
|
|
2023-05-15 15:14:22 +02:00
|
|
|
static CLG_LogRef LOG = {"bke.node"};
|
|
|
|
|
|
|
|
|
|
namespace blender::bke {
|
|
|
|
|
|
2024-05-13 16:07:12 +02:00
|
|
|
/* Forward declaration. */
|
|
|
|
|
static void write_node_socket_default_value(BlendWriter *writer, const bNodeSocket *sock);
|
|
|
|
|
|
2023-05-15 15:14:22 +02:00
|
|
|
/* Fallback types for undefined tree, nodes, sockets. */
|
2022-11-26 13:20:18 +01:00
|
|
|
bNodeTreeType NodeTreeTypeUndefined;
|
2013-03-19 13:40:16 +00:00
|
|
|
bNodeType NodeTypeUndefined;
|
|
|
|
|
bNodeSocketType NodeSocketTypeUndefined;
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
static void ntree_set_typeinfo(bNodeTree *ntree, bNodeTreeType *typeinfo);
|
2023-08-30 12:37:21 +02:00
|
|
|
static void node_socket_set_typeinfo(bNodeTree *ntree,
|
|
|
|
|
bNodeSocket *sock,
|
|
|
|
|
bNodeSocketType *typeinfo);
|
2023-05-16 09:39:42 -04:00
|
|
|
static void node_socket_copy(bNodeSocket *sock_dst, const bNodeSocket *sock_src, const int flag);
|
|
|
|
|
static void free_localized_node_groups(bNodeTree *ntree);
|
2023-08-30 12:37:21 +02:00
|
|
|
static bool socket_id_user_decrement(bNodeSocket *sock);
|
2020-03-06 14:57:26 +01:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
static void ntree_init_data(ID *id)
|
2020-03-06 14:57:26 +01:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
bNodeTree *ntree = reinterpret_cast<bNodeTree *>(id);
|
2023-08-30 12:37:21 +02:00
|
|
|
ntree->tree_interface.init_data();
|
2023-05-16 09:39:42 -04:00
|
|
|
ntree->runtime = MEM_new<bNodeTreeRuntime>(__func__);
|
|
|
|
|
ntree_set_typeinfo(ntree, nullptr);
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-26 19:17:51 +02:00
|
|
|
static void ntree_copy_data(Main * /*bmain*/,
|
|
|
|
|
std::optional<Library *> /*owner_library*/,
|
|
|
|
|
ID *id_dst,
|
|
|
|
|
const ID *id_src,
|
|
|
|
|
const int flag)
|
2023-05-16 09:39:42 -04:00
|
|
|
{
|
|
|
|
|
bNodeTree *ntree_dst = reinterpret_cast<bNodeTree *>(id_dst);
|
|
|
|
|
const bNodeTree *ntree_src = reinterpret_cast<const bNodeTree *>(id_src);
|
|
|
|
|
|
2024-03-07 16:20:36 -05:00
|
|
|
/* We never handle user-count here for owned data. */
|
2023-05-16 09:39:42 -04:00
|
|
|
const int flag_subdata = flag | LIB_ID_CREATE_NO_USER_REFCOUNT;
|
|
|
|
|
|
|
|
|
|
ntree_dst->runtime = MEM_new<bNodeTreeRuntime>(__func__);
|
|
|
|
|
bNodeTreeRuntime &dst_runtime = *ntree_dst->runtime;
|
|
|
|
|
|
|
|
|
|
Map<const bNodeSocket *, bNodeSocket *> socket_map;
|
|
|
|
|
|
|
|
|
|
dst_runtime.nodes_by_id.reserve(ntree_src->all_nodes().size());
|
|
|
|
|
BLI_listbase_clear(&ntree_dst->nodes);
|
|
|
|
|
int i;
|
|
|
|
|
LISTBASE_FOREACH_INDEX (const bNode *, src_node, &ntree_src->nodes, i) {
|
|
|
|
|
/* Don't find a unique name for every node, since they should have valid names already. */
|
|
|
|
|
bNode *new_node = node_copy_with_mapping(
|
|
|
|
|
ntree_dst, *src_node, flag_subdata, false, socket_map);
|
|
|
|
|
dst_runtime.nodes_by_id.add_new(new_node);
|
|
|
|
|
new_node->runtime->index_in_tree = i;
|
Geometry Nodes: add simulation support
This adds support for building simulations with geometry nodes. A new
`Simulation Input` and `Simulation Output` node allow maintaining a
simulation state across multiple frames. Together these two nodes form
a `simulation zone` which contains all the nodes that update the simulation
state from one frame to the next.
A new simulation zone can be added via the menu
(`Simulation > Simulation Zone`) or with the node add search.
The simulation state contains a geometry by default. However, it is possible
to add multiple geometry sockets as well as other socket types. Currently,
field inputs are evaluated and stored for the preceding geometry socket in
the order that the sockets are shown. Simulation state items can be added
by linking one of the empty sockets to something else. In the sidebar, there
is a new panel that allows adding, removing and reordering these sockets.
The simulation nodes behave as follows:
* On the first frame, the inputs of the `Simulation Input` node are evaluated
to initialize the simulation state. In later frames these sockets are not
evaluated anymore. The `Delta Time` at the first frame is zero, but the
simulation zone is still evaluated.
* On every next frame, the `Simulation Input` node outputs the simulation
state of the previous frame. Nodes in the simulation zone can edit that
data in arbitrary ways, also taking into account the `Delta Time`. The new
simulation state has to be passed to the `Simulation Output` node where it
is cached and forwarded.
* On a frame that is already cached or baked, the nodes in the simulation
zone are not evaluated, because the `Simulation Output` node can return
the previously cached data directly.
It is not allowed to connect sockets from inside the simulation zone to the
outside without going through the `Simulation Output` node. This is a necessary
restriction to make caching and sub-frame interpolation work. Links can go into
the simulation zone without problems though.
Anonymous attributes are not propagated by the simulation nodes unless they
are explicitly stored in the simulation state. This is unfortunate, but
currently there is no practical and reliable alternative. The core problem
is detecting which anonymous attributes will be required for the simulation
and afterwards. While we can detect this for the current evaluation, we can't
look into the future in time to see what data will be necessary. We intend to
make it easier to explicitly pass data through a simulation in the future,
even if the simulation is in a nested node group.
There is a new `Simulation Nodes` panel in the physics tab in the properties
editor. It allows baking all simulation zones on the selected objects. The
baking options are intentially kept at a minimum for this MVP. More features
for simulation baking as well as baking in general can be expected to be added
separately.
All baked data is stored on disk in a folder next to the .blend file. #106937
describes how baking is implemented in more detail. Volumes can not be baked
yet and materials are lost during baking for now. Packing the baked data into
the .blend file is not yet supported.
The timeline indicates which frames are currently cached, baked or cached but
invalidated by user-changes.
Simulation input and output nodes are internally linked together by their
`bNode.identifier` which stays the same even if the node name changes. They
are generally added and removed together. However, there are still cases where
"dangling" simulation nodes can be created currently. Those generally don't
cause harm, but would be nice to avoid this in more cases in the future.
Co-authored-by: Hans Goudey <h.goudey@me.com>
Co-authored-by: Lukas Tönne <lukas@blender.org>
Pull Request: https://projects.blender.org/blender/blender/pulls/104924
2023-05-03 13:18:51 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/* copy links */
|
|
|
|
|
BLI_listbase_clear(&ntree_dst->links);
|
|
|
|
|
LISTBASE_FOREACH (const bNodeLink *, src_link, &ntree_src->links) {
|
|
|
|
|
bNodeLink *dst_link = static_cast<bNodeLink *>(MEM_dupallocN(src_link));
|
|
|
|
|
dst_link->fromnode = dst_runtime.nodes_by_id.lookup_key_as(src_link->fromnode->identifier);
|
|
|
|
|
dst_link->fromsock = socket_map.lookup(src_link->fromsock);
|
|
|
|
|
dst_link->tonode = dst_runtime.nodes_by_id.lookup_key_as(src_link->tonode->identifier);
|
|
|
|
|
dst_link->tosock = socket_map.lookup(src_link->tosock);
|
|
|
|
|
BLI_assert(dst_link->tosock);
|
|
|
|
|
dst_link->tosock->link = dst_link;
|
|
|
|
|
BLI_addtail(&ntree_dst->links, dst_link);
|
2020-03-06 14:57:26 +01:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/* update node->parent pointers */
|
|
|
|
|
for (bNode *node : ntree_dst->all_nodes()) {
|
|
|
|
|
if (node->parent) {
|
|
|
|
|
node->parent = dst_runtime.nodes_by_id.lookup_key_as(node->parent->identifier);
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-03-06 14:57:26 +01:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
for (bNode *node : ntree_dst->all_nodes()) {
|
|
|
|
|
nodeDeclarationEnsure(ntree_dst, node);
|
2020-03-06 14:57:26 +01:00
|
|
|
}
|
|
|
|
|
|
2023-08-30 12:37:21 +02:00
|
|
|
ntree_dst->tree_interface.copy_data(ntree_src->tree_interface, flag);
|
2023-05-16 09:39:42 -04:00
|
|
|
/* copy preview hash */
|
|
|
|
|
if (ntree_src->previews && (flag & LIB_ID_COPY_NO_PREVIEW) == 0) {
|
|
|
|
|
bNodeInstanceHashIterator iter;
|
2020-03-06 14:57:26 +01:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
ntree_dst->previews = BKE_node_instance_hash_new("node previews");
|
2020-03-06 14:57:26 +01:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
NODE_INSTANCE_HASH_ITER (iter, ntree_src->previews) {
|
|
|
|
|
bNodeInstanceKey key = node_instance_hash_iterator_get_key(&iter);
|
|
|
|
|
bNodePreview *preview = static_cast<bNodePreview *>(
|
|
|
|
|
node_instance_hash_iterator_get_value(&iter));
|
|
|
|
|
BKE_node_instance_hash_insert(ntree_dst->previews, key, node_preview_copy(preview));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
ntree_dst->previews = nullptr;
|
2020-03-06 14:57:26 +01:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
if (ntree_src->runtime->field_inferencing_interface) {
|
|
|
|
|
dst_runtime.field_inferencing_interface = std::make_unique<FieldInferencingInterface>(
|
|
|
|
|
*ntree_src->runtime->field_inferencing_interface);
|
|
|
|
|
}
|
2023-06-14 13:56:57 +02:00
|
|
|
if (ntree_src->runtime->anonymous_attribute_inferencing) {
|
2024-05-13 16:07:12 +02:00
|
|
|
using namespace anonymous_attribute_inferencing;
|
2023-06-14 13:56:57 +02:00
|
|
|
dst_runtime.anonymous_attribute_inferencing =
|
|
|
|
|
std::make_unique<AnonymousAttributeInferencingResult>(
|
|
|
|
|
*ntree_src->runtime->anonymous_attribute_inferencing);
|
|
|
|
|
for (FieldSource &field_source :
|
2024-01-02 18:12:54 +01:00
|
|
|
dst_runtime.anonymous_attribute_inferencing->all_field_sources)
|
|
|
|
|
{
|
2023-06-14 13:56:57 +02:00
|
|
|
if (auto *socket_field_source = std::get_if<SocketFieldSource>(&field_source.data)) {
|
|
|
|
|
socket_field_source->socket = socket_map.lookup(socket_field_source->socket);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (GeometrySource &geometry_source :
|
|
|
|
|
dst_runtime.anonymous_attribute_inferencing->all_geometry_sources)
|
|
|
|
|
{
|
|
|
|
|
if (auto *socket_geometry_source = std::get_if<SocketGeometrySource>(&geometry_source.data))
|
|
|
|
|
{
|
|
|
|
|
socket_geometry_source->socket = socket_map.lookup(socket_geometry_source->socket);
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
}
|
2020-03-06 14:57:26 +01:00
|
|
|
|
2023-08-04 18:22:45 +02:00
|
|
|
if (ntree_src->geometry_node_asset_traits) {
|
|
|
|
|
ntree_dst->geometry_node_asset_traits = MEM_new<GeometryNodeAssetTraits>(
|
|
|
|
|
__func__, *ntree_src->geometry_node_asset_traits);
|
|
|
|
|
}
|
|
|
|
|
|
Nodes: add nested node ids and use them for simulation state
The simulation state used by simulation nodes is owned by the modifier. Since a
geometry nodes setup can contain an arbitrary number of simulations, the modifier
has a mapping from `SimulationZoneID` to `SimulationZoneState`. This patch changes
what is used as `SimulationZoneID`.
Previously, the `SimulationZoneID` contained a list of `bNode::identifier` that described
the path from the root node tree to the simulation output node. This works ok in many
cases, but also has a significant problem: The `SimulationZoneID` changes when moving
the simulation zone into or out of a node group. This implies that any of these operations
loses the mapping from zone to simulation state, invalidating the cache or even baked data.
The goal of this patch is to introduce a single-integer ID that identifies a (nested) simulation
zone and is stable even when grouping and un-grouping. The ID should be stable even if the
node group containing the (nested) simulation zone is in a separate linked .blend file and
that linked file is changed.
In the future, the same kind of ID can be used to store e.g. checkpoint/baked/frozen data
in the modifier.
To achieve the described goal, node trees can now store an arbitrary number of nested node
references (an array of `bNestedNodeRef`). Each nested node reference has an ID that is
unique within the current node tree. The node tree does not store the entire path to the
nested node. Instead it only know which group node the nested node is in, and what the
nested node ID of the node is within that group. Grouping and un-grouping operations
have to update the nested node references to keep the IDs stable. Importantly though,
these operations only have to care about the two node groups that are affected. IDs in
higher level node groups remain unchanged by design.
A consequence of this design is that every `bNodeTree` now has a `bNestedNodeRef`
for every (nested) simulation zone. Two instances of the same simulation zone (because
a node group is reused) are referenced by two separate `bNestedNodeRef`. This is
important to keep in mind, because it also means that this solution doesn't scale well if
we wanted to use it to keep stable references to *all* nested nodes. I can't think of a
solution that fulfills the described requirements but scales better with more nodes. For
that reason, this solution should only be used when we want to store data for each
referenced nested node at the top level (like we do for simulations).
This is not a replacement for `ViewerPath` which can store a path to data in a node tree
without changing the node tree. Also `ViewerPath` can contain information like the loop
iteration that should be viewed (#109164). `bNestedNodeRef` can't differentiate between
different iterations of a loop. This also means that simulations can't be used inside of a
loop (loops inside of a simulation work fine though).
When baking, the new stable ID is now written to disk, which means that baked data is
not invalidated by grouping/un-grouping operations. Backward compatibility for baked
data is provided, but only works as long as the simulation zone has not been moved to
a different node group yet. Forward compatibility for the baked data is not provided
(so older versions can't load the data baked with a newer version of Blender).
Pull Request: https://projects.blender.org/blender/blender/pulls/109444
2023-07-01 11:54:32 +02:00
|
|
|
if (ntree_src->nested_node_refs) {
|
|
|
|
|
ntree_dst->nested_node_refs = static_cast<bNestedNodeRef *>(
|
|
|
|
|
MEM_malloc_arrayN(ntree_src->nested_node_refs_num, sizeof(bNestedNodeRef), __func__));
|
|
|
|
|
uninitialized_copy_n(
|
|
|
|
|
ntree_src->nested_node_refs, ntree_src->nested_node_refs_num, ntree_dst->nested_node_refs);
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
if (flag & LIB_ID_COPY_NO_PREVIEW) {
|
|
|
|
|
ntree_dst->preview = nullptr;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
BKE_previewimg_id_copy(&ntree_dst->id, &ntree_src->id);
|
|
|
|
|
}
|
2024-05-09 09:28:00 -04:00
|
|
|
|
|
|
|
|
ntree_dst->description = BLI_strdup_null(ntree_src->description);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2020-03-06 14:57:26 +01:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
static void ntree_free_data(ID *id)
|
2023-05-15 15:14:22 +02:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
bNodeTree *ntree = reinterpret_cast<bNodeTree *>(id);
|
2020-03-06 14:57:26 +01:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/* XXX hack! node trees should not store execution graphs at all.
|
|
|
|
|
* This should be removed when old tree types no longer require it.
|
|
|
|
|
* Currently the execution data for texture nodes remains in the tree
|
|
|
|
|
* after execution, until the node tree is updated or freed. */
|
|
|
|
|
if (ntree->runtime->execdata) {
|
|
|
|
|
switch (ntree->type) {
|
|
|
|
|
case NTREE_SHADER:
|
|
|
|
|
ntreeShaderEndExecTree(ntree->runtime->execdata);
|
|
|
|
|
break;
|
|
|
|
|
case NTREE_TEXTURE:
|
|
|
|
|
ntreeTexEndExecTree(ntree->runtime->execdata);
|
|
|
|
|
ntree->runtime->execdata = nullptr;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2020-03-06 14:57:26 +01:00
|
|
|
}
|
2021-11-06 16:43:26 +01:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/* XXX not nice, but needed to free localized node groups properly */
|
|
|
|
|
free_localized_node_groups(ntree);
|
2020-05-07 16:54:32 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
BLI_freelistN(&ntree->links);
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
LISTBASE_FOREACH_MUTABLE (bNode *, node, &ntree->nodes) {
|
|
|
|
|
node_free_node(ntree, node);
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-30 12:37:21 +02:00
|
|
|
ntree->tree_interface.free_data();
|
2023-05-16 09:39:42 -04:00
|
|
|
|
|
|
|
|
/* free preview hash */
|
|
|
|
|
if (ntree->previews) {
|
|
|
|
|
BKE_node_instance_hash_free(ntree->previews, (bNodeInstanceValueFP)node_preview_free);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ntree->id.tag & LIB_TAG_LOCALIZED) {
|
|
|
|
|
BKE_libblock_free_data(&ntree->id, true);
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-04 18:22:45 +02:00
|
|
|
MEM_delete(ntree->geometry_node_asset_traits);
|
|
|
|
|
|
Nodes: add nested node ids and use them for simulation state
The simulation state used by simulation nodes is owned by the modifier. Since a
geometry nodes setup can contain an arbitrary number of simulations, the modifier
has a mapping from `SimulationZoneID` to `SimulationZoneState`. This patch changes
what is used as `SimulationZoneID`.
Previously, the `SimulationZoneID` contained a list of `bNode::identifier` that described
the path from the root node tree to the simulation output node. This works ok in many
cases, but also has a significant problem: The `SimulationZoneID` changes when moving
the simulation zone into or out of a node group. This implies that any of these operations
loses the mapping from zone to simulation state, invalidating the cache or even baked data.
The goal of this patch is to introduce a single-integer ID that identifies a (nested) simulation
zone and is stable even when grouping and un-grouping. The ID should be stable even if the
node group containing the (nested) simulation zone is in a separate linked .blend file and
that linked file is changed.
In the future, the same kind of ID can be used to store e.g. checkpoint/baked/frozen data
in the modifier.
To achieve the described goal, node trees can now store an arbitrary number of nested node
references (an array of `bNestedNodeRef`). Each nested node reference has an ID that is
unique within the current node tree. The node tree does not store the entire path to the
nested node. Instead it only know which group node the nested node is in, and what the
nested node ID of the node is within that group. Grouping and un-grouping operations
have to update the nested node references to keep the IDs stable. Importantly though,
these operations only have to care about the two node groups that are affected. IDs in
higher level node groups remain unchanged by design.
A consequence of this design is that every `bNodeTree` now has a `bNestedNodeRef`
for every (nested) simulation zone. Two instances of the same simulation zone (because
a node group is reused) are referenced by two separate `bNestedNodeRef`. This is
important to keep in mind, because it also means that this solution doesn't scale well if
we wanted to use it to keep stable references to *all* nested nodes. I can't think of a
solution that fulfills the described requirements but scales better with more nodes. For
that reason, this solution should only be used when we want to store data for each
referenced nested node at the top level (like we do for simulations).
This is not a replacement for `ViewerPath` which can store a path to data in a node tree
without changing the node tree. Also `ViewerPath` can contain information like the loop
iteration that should be viewed (#109164). `bNestedNodeRef` can't differentiate between
different iterations of a loop. This also means that simulations can't be used inside of a
loop (loops inside of a simulation work fine though).
When baking, the new stable ID is now written to disk, which means that baked data is
not invalidated by grouping/un-grouping operations. Backward compatibility for baked
data is provided, but only works as long as the simulation zone has not been moved to
a different node group yet. Forward compatibility for the baked data is not provided
(so older versions can't load the data baked with a newer version of Blender).
Pull Request: https://projects.blender.org/blender/blender/pulls/109444
2023-07-01 11:54:32 +02:00
|
|
|
if (ntree->nested_node_refs) {
|
|
|
|
|
MEM_freeN(ntree->nested_node_refs);
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-09 09:28:00 -04:00
|
|
|
MEM_SAFE_FREE(ntree->description);
|
2023-05-16 09:39:42 -04:00
|
|
|
BKE_previewimg_free(&ntree->preview);
|
|
|
|
|
MEM_delete(ntree->runtime);
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-28 17:35:02 +02:00
|
|
|
static void library_foreach_node_socket(bNodeSocket *sock, LibraryForeachIDData *data)
|
2023-05-16 09:39:42 -04:00
|
|
|
{
|
|
|
|
|
BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(
|
2024-03-26 13:36:30 -04:00
|
|
|
data, IDP_foreach_property(sock->prop, IDP_TYPE_FILTER_ID, [&](IDProperty *prop) {
|
|
|
|
|
BKE_lib_query_idpropertiesForeachIDLink_callback(prop, data);
|
|
|
|
|
}));
|
2023-05-16 09:39:42 -04:00
|
|
|
|
|
|
|
|
switch (eNodeSocketDatatype(sock->type)) {
|
|
|
|
|
case SOCK_OBJECT: {
|
|
|
|
|
bNodeSocketValueObject &default_value = *sock->default_value_typed<bNodeSocketValueObject>();
|
|
|
|
|
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, default_value.value, IDWALK_CB_USER);
|
|
|
|
|
break;
|
2021-05-12 12:41:21 +02:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
case SOCK_IMAGE: {
|
|
|
|
|
bNodeSocketValueImage &default_value = *sock->default_value_typed<bNodeSocketValueImage>();
|
|
|
|
|
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, default_value.value, IDWALK_CB_USER);
|
|
|
|
|
break;
|
2020-09-10 13:33:29 +02:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
case SOCK_COLLECTION: {
|
|
|
|
|
bNodeSocketValueCollection &default_value =
|
|
|
|
|
*sock->default_value_typed<bNodeSocketValueCollection>();
|
|
|
|
|
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, default_value.value, IDWALK_CB_USER);
|
|
|
|
|
break;
|
2020-09-10 13:33:29 +02:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
case SOCK_TEXTURE: {
|
|
|
|
|
bNodeSocketValueTexture &default_value =
|
|
|
|
|
*sock->default_value_typed<bNodeSocketValueTexture>();
|
|
|
|
|
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, default_value.value, IDWALK_CB_USER);
|
|
|
|
|
break;
|
2020-09-10 13:33:29 +02:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
case SOCK_MATERIAL: {
|
|
|
|
|
bNodeSocketValueMaterial &default_value =
|
|
|
|
|
*sock->default_value_typed<bNodeSocketValueMaterial>();
|
|
|
|
|
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, default_value.value, IDWALK_CB_USER);
|
|
|
|
|
break;
|
Geometry Nodes: add simulation support
This adds support for building simulations with geometry nodes. A new
`Simulation Input` and `Simulation Output` node allow maintaining a
simulation state across multiple frames. Together these two nodes form
a `simulation zone` which contains all the nodes that update the simulation
state from one frame to the next.
A new simulation zone can be added via the menu
(`Simulation > Simulation Zone`) or with the node add search.
The simulation state contains a geometry by default. However, it is possible
to add multiple geometry sockets as well as other socket types. Currently,
field inputs are evaluated and stored for the preceding geometry socket in
the order that the sockets are shown. Simulation state items can be added
by linking one of the empty sockets to something else. In the sidebar, there
is a new panel that allows adding, removing and reordering these sockets.
The simulation nodes behave as follows:
* On the first frame, the inputs of the `Simulation Input` node are evaluated
to initialize the simulation state. In later frames these sockets are not
evaluated anymore. The `Delta Time` at the first frame is zero, but the
simulation zone is still evaluated.
* On every next frame, the `Simulation Input` node outputs the simulation
state of the previous frame. Nodes in the simulation zone can edit that
data in arbitrary ways, also taking into account the `Delta Time`. The new
simulation state has to be passed to the `Simulation Output` node where it
is cached and forwarded.
* On a frame that is already cached or baked, the nodes in the simulation
zone are not evaluated, because the `Simulation Output` node can return
the previously cached data directly.
It is not allowed to connect sockets from inside the simulation zone to the
outside without going through the `Simulation Output` node. This is a necessary
restriction to make caching and sub-frame interpolation work. Links can go into
the simulation zone without problems though.
Anonymous attributes are not propagated by the simulation nodes unless they
are explicitly stored in the simulation state. This is unfortunate, but
currently there is no practical and reliable alternative. The core problem
is detecting which anonymous attributes will be required for the simulation
and afterwards. While we can detect this for the current evaluation, we can't
look into the future in time to see what data will be necessary. We intend to
make it easier to explicitly pass data through a simulation in the future,
even if the simulation is in a nested node group.
There is a new `Simulation Nodes` panel in the physics tab in the properties
editor. It allows baking all simulation zones on the selected objects. The
baking options are intentially kept at a minimum for this MVP. More features
for simulation baking as well as baking in general can be expected to be added
separately.
All baked data is stored on disk in a folder next to the .blend file. #106937
describes how baking is implemented in more detail. Volumes can not be baked
yet and materials are lost during baking for now. Packing the baked data into
the .blend file is not yet supported.
The timeline indicates which frames are currently cached, baked or cached but
invalidated by user-changes.
Simulation input and output nodes are internally linked together by their
`bNode.identifier` which stays the same even if the node name changes. They
are generally added and removed together. However, there are still cases where
"dangling" simulation nodes can be created currently. Those generally don't
cause harm, but would be nice to avoid this in more cases in the future.
Co-authored-by: Hans Goudey <h.goudey@me.com>
Co-authored-by: Lukas Tönne <lukas@blender.org>
Pull Request: https://projects.blender.org/blender/blender/pulls/104924
2023-05-03 13:18:51 +02:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
case SOCK_FLOAT:
|
|
|
|
|
case SOCK_VECTOR:
|
|
|
|
|
case SOCK_RGBA:
|
|
|
|
|
case SOCK_BOOLEAN:
|
2023-06-14 20:30:10 +02:00
|
|
|
case SOCK_ROTATION:
|
2024-02-13 18:59:36 +01:00
|
|
|
case SOCK_MATRIX:
|
2023-05-16 09:39:42 -04:00
|
|
|
case SOCK_INT:
|
|
|
|
|
case SOCK_STRING:
|
|
|
|
|
case SOCK_CUSTOM:
|
|
|
|
|
case SOCK_SHADER:
|
|
|
|
|
case SOCK_GEOMETRY:
|
2024-01-26 12:40:01 +01:00
|
|
|
case SOCK_MENU:
|
2023-05-16 09:39:42 -04:00
|
|
|
break;
|
2020-09-10 13:33:29 +02:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
}
|
2020-09-10 13:33:29 +02:00
|
|
|
|
2024-04-28 17:35:02 +02:00
|
|
|
void node_node_foreach_id(bNode *node, LibraryForeachIDData *data)
|
|
|
|
|
{
|
|
|
|
|
BKE_LIB_FOREACHID_PROCESS_ID(data, node->id, IDWALK_CB_USER);
|
|
|
|
|
|
|
|
|
|
BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(
|
|
|
|
|
data, IDP_foreach_property(node->prop, IDP_TYPE_FILTER_ID, [&](IDProperty *prop) {
|
|
|
|
|
BKE_lib_query_idpropertiesForeachIDLink_callback(prop, data);
|
|
|
|
|
}));
|
|
|
|
|
LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
|
|
|
|
|
BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, library_foreach_node_socket(sock, data));
|
|
|
|
|
}
|
|
|
|
|
LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
|
|
|
|
|
BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, library_foreach_node_socket(sock, data));
|
|
|
|
|
}
|
2024-05-01 21:29:04 +02:00
|
|
|
|
|
|
|
|
/* Note that this ID pointer is only a cache, it may be outdated. */
|
|
|
|
|
BKE_LIB_FOREACHID_PROCESS_ID(data, node->runtime->owner_tree, IDWALK_CB_LOOPBACK);
|
2024-04-28 17:35:02 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
static void node_foreach_id(ID *id, LibraryForeachIDData *data)
|
|
|
|
|
{
|
|
|
|
|
bNodeTree *ntree = reinterpret_cast<bNodeTree *>(id);
|
2020-09-10 13:33:29 +02:00
|
|
|
|
2023-08-22 19:01:04 +02:00
|
|
|
BKE_LIB_FOREACHID_PROCESS_ID(
|
|
|
|
|
data,
|
|
|
|
|
ntree->owner_id,
|
|
|
|
|
(IDWALK_CB_LOOPBACK | IDWALK_CB_NEVER_SELF | IDWALK_CB_READFILE_IGNORE));
|
2021-11-06 16:43:26 +01:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, ntree->gpd, IDWALK_CB_USER);
|
2020-09-10 13:33:29 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
for (bNode *node : ntree->all_nodes()) {
|
2024-04-28 17:35:02 +02:00
|
|
|
node_node_foreach_id(node, data);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2020-09-10 13:33:29 +02:00
|
|
|
|
2023-08-30 12:37:21 +02:00
|
|
|
ntree->tree_interface.foreach_id(data);
|
2023-05-16 09:39:42 -04:00
|
|
|
}
|
2022-12-01 14:53:27 -06:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
static void node_foreach_cache(ID *id,
|
|
|
|
|
IDTypeForeachCacheFunctionCallback function_callback,
|
|
|
|
|
void *user_data)
|
|
|
|
|
{
|
|
|
|
|
bNodeTree *nodetree = reinterpret_cast<bNodeTree *>(id);
|
|
|
|
|
IDCacheKey key = {0};
|
2024-01-22 13:47:13 +01:00
|
|
|
key.id_session_uid = id->session_uid;
|
2024-01-18 22:50:40 +01:00
|
|
|
key.identifier = offsetof(bNodeTree, previews);
|
2020-09-10 13:33:29 +02:00
|
|
|
|
2023-07-31 11:50:54 +10:00
|
|
|
/* TODO: see also `direct_link_nodetree()` in `readfile.cc`. */
|
2023-05-16 09:39:42 -04:00
|
|
|
#if 0
|
|
|
|
|
function_callback(id, &key, static_cast<void **>(&nodetree->previews), 0, user_data);
|
|
|
|
|
#endif
|
2020-09-10 13:33:29 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
if (nodetree->type == NTREE_COMPOSIT) {
|
|
|
|
|
for (bNode *node : nodetree->all_nodes()) {
|
|
|
|
|
if (node->type == CMP_NODE_MOVIEDISTORTION) {
|
2024-01-18 22:50:40 +01:00
|
|
|
key.identifier = size_t(BLI_ghashutil_strhash_p(node->name));
|
2023-05-16 09:39:42 -04:00
|
|
|
function_callback(id, &key, static_cast<void **>(&node->storage), 0, user_data);
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
}
|
2020-09-10 13:33:29 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
static void node_foreach_path(ID *id, BPathForeachPathData *bpath_data)
|
|
|
|
|
{
|
|
|
|
|
bNodeTree *ntree = reinterpret_cast<bNodeTree *>(id);
|
Geometry Nodes: add simulation support
This adds support for building simulations with geometry nodes. A new
`Simulation Input` and `Simulation Output` node allow maintaining a
simulation state across multiple frames. Together these two nodes form
a `simulation zone` which contains all the nodes that update the simulation
state from one frame to the next.
A new simulation zone can be added via the menu
(`Simulation > Simulation Zone`) or with the node add search.
The simulation state contains a geometry by default. However, it is possible
to add multiple geometry sockets as well as other socket types. Currently,
field inputs are evaluated and stored for the preceding geometry socket in
the order that the sockets are shown. Simulation state items can be added
by linking one of the empty sockets to something else. In the sidebar, there
is a new panel that allows adding, removing and reordering these sockets.
The simulation nodes behave as follows:
* On the first frame, the inputs of the `Simulation Input` node are evaluated
to initialize the simulation state. In later frames these sockets are not
evaluated anymore. The `Delta Time` at the first frame is zero, but the
simulation zone is still evaluated.
* On every next frame, the `Simulation Input` node outputs the simulation
state of the previous frame. Nodes in the simulation zone can edit that
data in arbitrary ways, also taking into account the `Delta Time`. The new
simulation state has to be passed to the `Simulation Output` node where it
is cached and forwarded.
* On a frame that is already cached or baked, the nodes in the simulation
zone are not evaluated, because the `Simulation Output` node can return
the previously cached data directly.
It is not allowed to connect sockets from inside the simulation zone to the
outside without going through the `Simulation Output` node. This is a necessary
restriction to make caching and sub-frame interpolation work. Links can go into
the simulation zone without problems though.
Anonymous attributes are not propagated by the simulation nodes unless they
are explicitly stored in the simulation state. This is unfortunate, but
currently there is no practical and reliable alternative. The core problem
is detecting which anonymous attributes will be required for the simulation
and afterwards. While we can detect this for the current evaluation, we can't
look into the future in time to see what data will be necessary. We intend to
make it easier to explicitly pass data through a simulation in the future,
even if the simulation is in a nested node group.
There is a new `Simulation Nodes` panel in the physics tab in the properties
editor. It allows baking all simulation zones on the selected objects. The
baking options are intentially kept at a minimum for this MVP. More features
for simulation baking as well as baking in general can be expected to be added
separately.
All baked data is stored on disk in a folder next to the .blend file. #106937
describes how baking is implemented in more detail. Volumes can not be baked
yet and materials are lost during baking for now. Packing the baked data into
the .blend file is not yet supported.
The timeline indicates which frames are currently cached, baked or cached but
invalidated by user-changes.
Simulation input and output nodes are internally linked together by their
`bNode.identifier` which stays the same even if the node name changes. They
are generally added and removed together. However, there are still cases where
"dangling" simulation nodes can be created currently. Those generally don't
cause harm, but would be nice to avoid this in more cases in the future.
Co-authored-by: Hans Goudey <h.goudey@me.com>
Co-authored-by: Lukas Tönne <lukas@blender.org>
Pull Request: https://projects.blender.org/blender/blender/pulls/104924
2023-05-03 13:18:51 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
switch (ntree->type) {
|
|
|
|
|
case NTREE_SHADER: {
|
|
|
|
|
for (bNode *node : ntree->all_nodes()) {
|
|
|
|
|
if (node->type == SH_NODE_SCRIPT) {
|
|
|
|
|
NodeShaderScript *nss = static_cast<NodeShaderScript *>(node->storage);
|
2023-06-23 10:09:01 +10:00
|
|
|
BKE_bpath_foreach_path_fixed_process(bpath_data, nss->filepath, sizeof(nss->filepath));
|
2023-05-16 09:39:42 -04:00
|
|
|
}
|
|
|
|
|
else if (node->type == SH_NODE_TEX_IES) {
|
|
|
|
|
NodeShaderTexIES *ies = static_cast<NodeShaderTexIES *>(node->storage);
|
2023-06-23 10:09:01 +10:00
|
|
|
BKE_bpath_foreach_path_fixed_process(bpath_data, ies->filepath, sizeof(ies->filepath));
|
2023-05-16 09:39:42 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
break;
|
2020-09-10 13:33:29 +02:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
}
|
2020-09-10 13:33:29 +02:00
|
|
|
|
2024-05-02 14:04:22 +02:00
|
|
|
static ID **node_owner_pointer_get(ID *id, const bool debug_relationship_assert)
|
2023-05-16 09:39:42 -04:00
|
|
|
{
|
|
|
|
|
if ((id->flag & LIB_EMBEDDED_DATA) == 0) {
|
|
|
|
|
return nullptr;
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
/* TODO: Sort this NO_MAIN or not for embedded node trees. See #86119. */
|
|
|
|
|
// BLI_assert((id->tag & LIB_TAG_NO_MAIN) == 0);
|
2020-09-10 13:33:29 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
bNodeTree *ntree = reinterpret_cast<bNodeTree *>(id);
|
2024-05-02 14:04:22 +02:00
|
|
|
if (debug_relationship_assert) {
|
|
|
|
|
BLI_assert(ntree->owner_id != nullptr);
|
|
|
|
|
BLI_assert(ntreeFromID(ntree->owner_id) == ntree);
|
|
|
|
|
}
|
2020-09-10 13:33:29 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
return &ntree->owner_id;
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
|
2024-05-13 16:07:12 +02:00
|
|
|
namespace forward_compat {
|
2023-08-30 12:37:21 +02:00
|
|
|
|
|
|
|
|
static void write_node_socket_interface(BlendWriter *writer, const bNodeSocket *sock)
|
|
|
|
|
{
|
|
|
|
|
BLO_write_struct(writer, bNodeSocket, sock);
|
|
|
|
|
|
|
|
|
|
if (sock->prop) {
|
|
|
|
|
IDP_BlendWrite(writer, sock->prop);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BLO_write_string(writer, sock->default_attribute_name);
|
|
|
|
|
|
|
|
|
|
write_node_socket_default_value(writer, sock);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Construct a bNodeSocket that represents a node group socket the old way. */
|
|
|
|
|
static bNodeSocket *make_socket(bNodeTree *ntree,
|
|
|
|
|
const eNodeSocketInOut in_out,
|
|
|
|
|
const StringRef idname,
|
2024-01-16 15:32:33 +01:00
|
|
|
|
2023-08-30 12:37:21 +02:00
|
|
|
const StringRef name,
|
|
|
|
|
const StringRef identifier)
|
|
|
|
|
{
|
|
|
|
|
bNodeSocketType *stype = nodeSocketTypeFind(idname.data());
|
|
|
|
|
if (stype == nullptr) {
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bNodeSocket *sock = MEM_cnew<bNodeSocket>(__func__);
|
|
|
|
|
sock->runtime = MEM_new<bNodeSocketRuntime>(__func__);
|
|
|
|
|
STRNCPY(sock->idname, stype->idname);
|
|
|
|
|
sock->in_out = int(in_out);
|
|
|
|
|
sock->type = int(SOCK_CUSTOM); /* int type undefined by default */
|
|
|
|
|
node_socket_set_typeinfo(ntree, sock, stype);
|
|
|
|
|
|
|
|
|
|
sock->limit = (in_out == SOCK_IN ? 1 : 0xFFF);
|
|
|
|
|
|
2023-09-01 11:19:44 +10:00
|
|
|
STRNCPY(sock->identifier, identifier.data());
|
|
|
|
|
STRNCPY(sock->name, name.data());
|
2023-08-30 12:37:21 +02:00
|
|
|
sock->storage = nullptr;
|
|
|
|
|
sock->flag |= SOCK_COLLAPSED;
|
|
|
|
|
|
|
|
|
|
return sock;
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-16 15:32:33 +01:00
|
|
|
/* Include the subtype suffix for old socket idnames. */
|
|
|
|
|
static StringRef get_legacy_socket_subtype_idname(StringRef idname, const void *socket_data)
|
|
|
|
|
{
|
|
|
|
|
if (idname == "NodeSocketFloat") {
|
|
|
|
|
const bNodeSocketValueFloat &float_data = *static_cast<const bNodeSocketValueFloat *>(
|
|
|
|
|
socket_data);
|
|
|
|
|
switch (float_data.subtype) {
|
|
|
|
|
case PROP_UNSIGNED:
|
|
|
|
|
return "NodeSocketFloatUnsigned";
|
|
|
|
|
case PROP_PERCENTAGE:
|
|
|
|
|
return "NodeSocketFloatPercentage";
|
|
|
|
|
case PROP_FACTOR:
|
|
|
|
|
return "NodeSocketFloatFactor";
|
|
|
|
|
case PROP_ANGLE:
|
|
|
|
|
return "NodeSocketFloatAngle";
|
|
|
|
|
case PROP_TIME:
|
|
|
|
|
return "NodeSocketFloatTime";
|
|
|
|
|
case PROP_TIME_ABSOLUTE:
|
|
|
|
|
return "NodeSocketFloatTimeAbsolute";
|
|
|
|
|
case PROP_DISTANCE:
|
|
|
|
|
return "NodeSocketFloatDistance";
|
2024-04-22 13:10:16 +02:00
|
|
|
case PROP_WAVELENGTH:
|
|
|
|
|
return "NodeSocketFloatWavelength";
|
2024-01-16 15:32:33 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (idname == "NodeSocketInt") {
|
|
|
|
|
const bNodeSocketValueInt &int_data = *static_cast<const bNodeSocketValueInt *>(socket_data);
|
|
|
|
|
switch (int_data.subtype) {
|
|
|
|
|
case PROP_UNSIGNED:
|
|
|
|
|
return "NodeSocketIntUnsigned";
|
|
|
|
|
case PROP_PERCENTAGE:
|
|
|
|
|
return "NodeSocketIntPercentage";
|
|
|
|
|
case PROP_FACTOR:
|
|
|
|
|
return "NodeSocketIntFactor";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (idname == "NodeSocketVector") {
|
|
|
|
|
const bNodeSocketValueVector &vector_data = *static_cast<const bNodeSocketValueVector *>(
|
|
|
|
|
socket_data);
|
|
|
|
|
switch (vector_data.subtype) {
|
|
|
|
|
case PROP_TRANSLATION:
|
|
|
|
|
return "NodeSocketVectorTranslation";
|
|
|
|
|
case PROP_DIRECTION:
|
|
|
|
|
return "NodeSocketVectorDirection";
|
|
|
|
|
case PROP_VELOCITY:
|
|
|
|
|
return "NodeSocketVectorVelocity";
|
|
|
|
|
case PROP_ACCELERATION:
|
|
|
|
|
return "NodeSocketVectorAcceleration";
|
|
|
|
|
case PROP_EULER:
|
|
|
|
|
return "NodeSocketVectorEuler";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return idname;
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-30 12:37:21 +02:00
|
|
|
/**
|
|
|
|
|
* Socket interface reconstruction for forward compatibility.
|
|
|
|
|
* To enable previous Blender versions to read the new interface DNA data,
|
|
|
|
|
* construct the bNodeSocket inputs/outputs lists.
|
|
|
|
|
* This discards any information about panels and alternating input/output order,
|
|
|
|
|
* but all functional information is preserved for executing node trees.
|
|
|
|
|
*/
|
|
|
|
|
static void construct_interface_as_legacy_sockets(bNodeTree *ntree)
|
|
|
|
|
{
|
|
|
|
|
BLI_assert(BLI_listbase_is_empty(&ntree->inputs_legacy));
|
|
|
|
|
BLI_assert(BLI_listbase_is_empty(&ntree->outputs_legacy));
|
|
|
|
|
|
|
|
|
|
auto make_legacy_socket = [&](const bNodeTreeInterfaceSocket &socket,
|
|
|
|
|
eNodeSocketInOut in_out) -> bNodeSocket * {
|
|
|
|
|
bNodeSocket *iosock = make_socket(
|
2024-01-16 15:32:33 +01:00
|
|
|
ntree,
|
|
|
|
|
in_out,
|
|
|
|
|
get_legacy_socket_subtype_idname(socket.socket_type, socket.socket_data),
|
|
|
|
|
socket.name ? socket.name : "",
|
|
|
|
|
socket.identifier);
|
2023-08-30 12:37:21 +02:00
|
|
|
if (!iosock) {
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (socket.description) {
|
2023-09-01 11:19:44 +10:00
|
|
|
STRNCPY(iosock->description, socket.description);
|
2023-08-30 12:37:21 +02:00
|
|
|
}
|
|
|
|
|
node_socket_copy_default_value_data(
|
|
|
|
|
eNodeSocketDatatype(iosock->typeinfo->type), iosock->default_value, socket.socket_data);
|
|
|
|
|
if (socket.properties) {
|
|
|
|
|
iosock->prop = IDP_CopyProperty(socket.properties);
|
|
|
|
|
}
|
|
|
|
|
SET_FLAG_FROM_TEST(
|
|
|
|
|
iosock->flag, socket.flag & NODE_INTERFACE_SOCKET_HIDE_VALUE, SOCK_HIDE_VALUE);
|
|
|
|
|
SET_FLAG_FROM_TEST(
|
|
|
|
|
iosock->flag, socket.flag & NODE_INTERFACE_SOCKET_HIDE_IN_MODIFIER, SOCK_HIDE_IN_MODIFIER);
|
|
|
|
|
iosock->attribute_domain = socket.attribute_domain;
|
2023-09-02 14:07:54 +02:00
|
|
|
iosock->default_attribute_name = BLI_strdup_null(socket.default_attribute_name);
|
2023-08-30 12:37:21 +02:00
|
|
|
return iosock;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* Construct inputs/outputs socket lists in the node tree. */
|
|
|
|
|
ntree->tree_interface.foreach_item([&](const bNodeTreeInterfaceItem &item) {
|
|
|
|
|
if (const bNodeTreeInterfaceSocket *socket =
|
|
|
|
|
node_interface::get_item_as<bNodeTreeInterfaceSocket>(&item))
|
|
|
|
|
{
|
|
|
|
|
if (socket->flag & NODE_INTERFACE_SOCKET_INPUT) {
|
|
|
|
|
if (bNodeSocket *legacy_socket = make_legacy_socket(*socket, SOCK_IN)) {
|
|
|
|
|
BLI_addtail(&ntree->inputs_legacy, legacy_socket);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (socket->flag & NODE_INTERFACE_SOCKET_OUTPUT) {
|
|
|
|
|
if (bNodeSocket *legacy_socket = make_legacy_socket(*socket, SOCK_OUT)) {
|
|
|
|
|
BLI_addtail(&ntree->outputs_legacy, legacy_socket);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void write_legacy_sockets(BlendWriter *writer, bNodeTree *ntree)
|
|
|
|
|
{
|
|
|
|
|
/* Write inputs/outputs */
|
|
|
|
|
LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs_legacy) {
|
|
|
|
|
write_node_socket_interface(writer, sock);
|
|
|
|
|
}
|
|
|
|
|
LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs_legacy) {
|
|
|
|
|
write_node_socket_interface(writer, sock);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void legacy_socket_interface_free(bNodeSocket *sock)
|
|
|
|
|
{
|
|
|
|
|
if (sock->prop) {
|
|
|
|
|
IDP_FreeProperty_ex(sock->prop, false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (sock->default_value) {
|
|
|
|
|
MEM_freeN(sock->default_value);
|
|
|
|
|
}
|
|
|
|
|
if (sock->default_attribute_name) {
|
|
|
|
|
MEM_freeN(sock->default_attribute_name);
|
|
|
|
|
}
|
|
|
|
|
MEM_delete(sock->runtime);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void cleanup_legacy_sockets(bNodeTree *ntree)
|
|
|
|
|
{
|
|
|
|
|
/* Clean up temporary inputs/outputs. */
|
|
|
|
|
LISTBASE_FOREACH_MUTABLE (bNodeSocket *, socket, &ntree->inputs_legacy) {
|
|
|
|
|
legacy_socket_interface_free(socket);
|
|
|
|
|
MEM_freeN(socket);
|
|
|
|
|
}
|
|
|
|
|
LISTBASE_FOREACH_MUTABLE (bNodeSocket *, socket, &ntree->outputs_legacy) {
|
|
|
|
|
legacy_socket_interface_free(socket);
|
|
|
|
|
MEM_freeN(socket);
|
|
|
|
|
}
|
|
|
|
|
BLI_listbase_clear(&ntree->inputs_legacy);
|
|
|
|
|
BLI_listbase_clear(&ntree->outputs_legacy);
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-13 16:07:12 +02:00
|
|
|
} // namespace forward_compat
|
2023-08-30 12:37:21 +02:00
|
|
|
|
|
|
|
|
static void write_node_socket_default_value(BlendWriter *writer, const bNodeSocket *sock)
|
2023-05-15 15:14:22 +02:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
if (sock->default_value == nullptr) {
|
|
|
|
|
return;
|
2020-09-10 13:33:29 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
switch (eNodeSocketDatatype(sock->type)) {
|
|
|
|
|
case SOCK_FLOAT:
|
|
|
|
|
BLO_write_struct(writer, bNodeSocketValueFloat, sock->default_value);
|
|
|
|
|
break;
|
|
|
|
|
case SOCK_VECTOR:
|
|
|
|
|
BLO_write_struct(writer, bNodeSocketValueVector, sock->default_value);
|
|
|
|
|
break;
|
|
|
|
|
case SOCK_RGBA:
|
|
|
|
|
BLO_write_struct(writer, bNodeSocketValueRGBA, sock->default_value);
|
|
|
|
|
break;
|
|
|
|
|
case SOCK_BOOLEAN:
|
|
|
|
|
BLO_write_struct(writer, bNodeSocketValueBoolean, sock->default_value);
|
|
|
|
|
break;
|
|
|
|
|
case SOCK_INT:
|
|
|
|
|
BLO_write_struct(writer, bNodeSocketValueInt, sock->default_value);
|
|
|
|
|
break;
|
|
|
|
|
case SOCK_STRING:
|
|
|
|
|
BLO_write_struct(writer, bNodeSocketValueString, sock->default_value);
|
|
|
|
|
break;
|
|
|
|
|
case SOCK_OBJECT:
|
|
|
|
|
BLO_write_struct(writer, bNodeSocketValueObject, sock->default_value);
|
|
|
|
|
break;
|
|
|
|
|
case SOCK_IMAGE:
|
|
|
|
|
BLO_write_struct(writer, bNodeSocketValueImage, sock->default_value);
|
|
|
|
|
break;
|
|
|
|
|
case SOCK_COLLECTION:
|
|
|
|
|
BLO_write_struct(writer, bNodeSocketValueCollection, sock->default_value);
|
|
|
|
|
break;
|
|
|
|
|
case SOCK_TEXTURE:
|
|
|
|
|
BLO_write_struct(writer, bNodeSocketValueTexture, sock->default_value);
|
|
|
|
|
break;
|
|
|
|
|
case SOCK_MATERIAL:
|
|
|
|
|
BLO_write_struct(writer, bNodeSocketValueMaterial, sock->default_value);
|
|
|
|
|
break;
|
2023-06-14 20:30:10 +02:00
|
|
|
case SOCK_ROTATION:
|
|
|
|
|
BLO_write_struct(writer, bNodeSocketValueRotation, sock->default_value);
|
|
|
|
|
break;
|
2024-02-13 18:59:36 +01:00
|
|
|
case SOCK_MENU:
|
2024-01-26 12:40:01 +01:00
|
|
|
BLO_write_struct(writer, bNodeSocketValueMenu, sock->default_value);
|
|
|
|
|
break;
|
2024-02-13 18:59:36 +01:00
|
|
|
case SOCK_MATRIX:
|
|
|
|
|
/* Matrix sockets currently have no default value. */
|
|
|
|
|
break;
|
2023-05-16 09:39:42 -04:00
|
|
|
case SOCK_CUSTOM:
|
|
|
|
|
/* Custom node sockets where default_value is defined uses custom properties for storage. */
|
|
|
|
|
break;
|
|
|
|
|
case SOCK_SHADER:
|
|
|
|
|
case SOCK_GEOMETRY:
|
|
|
|
|
BLI_assert_unreachable();
|
|
|
|
|
break;
|
|
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
|
2023-08-30 12:37:21 +02:00
|
|
|
static void write_node_socket(BlendWriter *writer, const bNodeSocket *sock)
|
2023-05-15 15:14:22 +02:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
BLO_write_struct(writer, bNodeSocket, sock);
|
2020-09-10 13:33:29 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
if (sock->prop) {
|
|
|
|
|
IDP_BlendWrite(writer, sock->prop);
|
|
|
|
|
}
|
2020-09-10 13:33:29 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/* This property should only be used for group node "interface" sockets. */
|
|
|
|
|
BLI_assert(sock->default_attribute_name == nullptr);
|
2021-11-06 16:43:26 +01:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
write_node_socket_default_value(writer, sock);
|
2020-09-10 13:33:29 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void ntreeBlendWrite(BlendWriter *writer, bNodeTree *ntree)
|
2023-05-15 15:14:22 +02:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
BKE_id_blend_write(writer, &ntree->id);
|
2024-05-08 11:25:00 +02:00
|
|
|
BLO_write_string(writer, ntree->description);
|
2023-05-16 09:39:42 -04:00
|
|
|
|
|
|
|
|
for (bNode *node : ntree->all_nodes()) {
|
Cycles: new Principled Hair BSDF variant with elliptical cross-section support
Implements the paper [A Microfacet-based Hair Scattering
Model](https://onlinelibrary.wiley.com/doi/full/10.1111/cgf.14588) by
Weizhen Huang, Matthias B. Hullin and Johannes Hanika.
### Features:
- This is a far-field model, as opposed to the previous near-field
Principled Hair BSDF model. The hair is expected to be less noisy, but
lower roughness values takes longer to render due to numerical
integration along the hair width. The hair also appears to be flat when
viewed up-close.
- The longitudinal width of the scattering lobe differs along the
azimuth, providing a higher contrast compared to the evenly spread
scattering in the near-field Principled Hair BSDF model. For a more
detailed comparison, please refer to the original paper.
- Supports elliptical cross-sections, adding more realism as human hairs
are usually elliptical. The orientation of the cross-section is aligned
with the curve normal, which can be adjusted using geometry nodes.
Default is minimal twist. During sampling, light rays that hit outside
the hair width will continue propogating as if the material is
transparent.
- There is non-physical modulation factors for the first three
lobes (Reflection, Transmission, Secondary Reflection).
### Missing:
- A good default for cross-section orientation. There was an
attempt (9039f76928) to default the orientation to align with the curve
normal in the mathematical sense, but the stability (when animated) is
unclear and it would be a hassle to generalise to all curve types. After
the model is in main, we could experiment with the geometry nodes team
to see what works the best as a default.
Co-authored-by: Lukas Stockner <lukas.stockner@freenet.de>
Pull Request: https://projects.blender.org/blender/blender/pulls/105600
2023-08-18 12:46:13 +02:00
|
|
|
if (ntree->type == NTREE_SHADER && node->type == SH_NODE_BSDF_HAIR_PRINCIPLED) {
|
|
|
|
|
/* For Principeld Hair BSDF, also write to `node->custom1` for forward compatibility, because
|
|
|
|
|
* prior to 4.0 `node->custom1` was used for color parametrization instead of
|
|
|
|
|
* `node->storage->parametrization`. */
|
|
|
|
|
NodeShaderHairPrincipled *data = static_cast<NodeShaderHairPrincipled *>(node->storage);
|
|
|
|
|
node->custom1 = data->parametrization;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
BLO_write_struct(writer, bNode, node);
|
|
|
|
|
|
|
|
|
|
if (node->prop) {
|
|
|
|
|
IDP_BlendWrite(writer, node->prop);
|
2020-09-10 13:33:29 +02:00
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
|
|
|
|
|
write_node_socket(writer, sock);
|
|
|
|
|
}
|
|
|
|
|
LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
|
|
|
|
|
write_node_socket(writer, sock);
|
|
|
|
|
}
|
2023-08-30 12:37:21 +02:00
|
|
|
BLO_write_struct_array(
|
|
|
|
|
writer, bNodePanelState, node->num_panel_states, node->panel_states_array);
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
if (node->storage) {
|
|
|
|
|
if (ELEM(ntree->type, NTREE_SHADER, NTREE_GEOMETRY) &&
|
|
|
|
|
ELEM(node->type, SH_NODE_CURVE_VEC, SH_NODE_CURVE_RGB, SH_NODE_CURVE_FLOAT))
|
|
|
|
|
{
|
|
|
|
|
BKE_curvemapping_blend_write(writer, static_cast<const CurveMapping *>(node->storage));
|
|
|
|
|
}
|
|
|
|
|
else if (ntree->type == NTREE_SHADER && (node->type == SH_NODE_SCRIPT)) {
|
|
|
|
|
NodeShaderScript *nss = static_cast<NodeShaderScript *>(node->storage);
|
|
|
|
|
if (nss->bytecode) {
|
|
|
|
|
BLO_write_string(writer, nss->bytecode);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
BLO_write_struct_by_name(writer, node->typeinfo->storagename, node->storage);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
else if ((ntree->type == NTREE_COMPOSIT) && ELEM(node->type,
|
|
|
|
|
CMP_NODE_TIME,
|
|
|
|
|
CMP_NODE_CURVE_VEC,
|
|
|
|
|
CMP_NODE_CURVE_RGB,
|
|
|
|
|
CMP_NODE_HUECORRECT))
|
|
|
|
|
{
|
|
|
|
|
BKE_curvemapping_blend_write(writer, static_cast<const CurveMapping *>(node->storage));
|
|
|
|
|
}
|
|
|
|
|
else if ((ntree->type == NTREE_TEXTURE) &&
|
|
|
|
|
ELEM(node->type, TEX_NODE_CURVE_RGB, TEX_NODE_CURVE_TIME))
|
|
|
|
|
{
|
|
|
|
|
BKE_curvemapping_blend_write(writer, static_cast<const CurveMapping *>(node->storage));
|
|
|
|
|
}
|
|
|
|
|
else if ((ntree->type == NTREE_COMPOSIT) && (node->type == CMP_NODE_MOVIEDISTORTION)) {
|
|
|
|
|
/* pass */
|
|
|
|
|
}
|
|
|
|
|
else if ((ntree->type == NTREE_COMPOSIT) && (node->type == CMP_NODE_GLARE)) {
|
|
|
|
|
/* Simple forward compatibility for fix for #50736.
|
|
|
|
|
* Not ideal (there is no ideal solution here), but should do for now. */
|
|
|
|
|
NodeGlare *ndg = static_cast<NodeGlare *>(node->storage);
|
|
|
|
|
/* Not in undo case. */
|
|
|
|
|
if (!BLO_write_is_undo(writer)) {
|
|
|
|
|
switch (ndg->type) {
|
2024-03-05 11:51:52 +02:00
|
|
|
case CMP_NODE_GLARE_STREAKS:
|
2023-05-16 09:39:42 -04:00
|
|
|
ndg->angle = ndg->streaks;
|
|
|
|
|
break;
|
2024-03-05 11:51:52 +02:00
|
|
|
case CMP_NODE_GLARE_SIMPLE_STAR:
|
2023-05-16 09:39:42 -04:00
|
|
|
ndg->angle = ndg->star_45;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
BLO_write_struct_by_name(writer, node->typeinfo->storagename, node->storage);
|
|
|
|
|
}
|
|
|
|
|
else if ((ntree->type == NTREE_COMPOSIT) &&
|
|
|
|
|
ELEM(node->type, CMP_NODE_CRYPTOMATTE, CMP_NODE_CRYPTOMATTE_LEGACY))
|
|
|
|
|
{
|
|
|
|
|
NodeCryptomatte *nc = static_cast<NodeCryptomatte *>(node->storage);
|
|
|
|
|
BLO_write_string(writer, nc->matte_id);
|
|
|
|
|
LISTBASE_FOREACH (CryptomatteEntry *, entry, &nc->entries) {
|
|
|
|
|
BLO_write_struct(writer, CryptomatteEntry, entry);
|
|
|
|
|
}
|
|
|
|
|
BLO_write_struct_by_name(writer, node->typeinfo->storagename, node->storage);
|
|
|
|
|
}
|
|
|
|
|
else if (node->type == FN_NODE_INPUT_STRING) {
|
|
|
|
|
NodeInputString *storage = static_cast<NodeInputString *>(node->storage);
|
|
|
|
|
if (storage->string) {
|
|
|
|
|
BLO_write_string(writer, storage->string);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
BLO_write_struct_by_name(writer, node->typeinfo->storagename, storage);
|
|
|
|
|
}
|
2024-05-31 16:23:31 +02:00
|
|
|
else if (node->type == GEO_NODE_CAPTURE_ATTRIBUTE) {
|
|
|
|
|
auto &storage = *static_cast<NodeGeometryAttributeCapture *>(node->storage);
|
|
|
|
|
/* Improve forward compatibility. */
|
|
|
|
|
storage.data_type_legacy = CD_PROP_FLOAT;
|
|
|
|
|
for (const NodeGeometryAttributeCaptureItem &item :
|
|
|
|
|
Span{storage.capture_items, storage.capture_items_num})
|
|
|
|
|
{
|
|
|
|
|
if (item.identifier == 0) {
|
|
|
|
|
/* The sockets of this item have the same identifiers that have been used by older
|
|
|
|
|
* Blender versions before the node supported capturing multiple attributes. */
|
|
|
|
|
storage.data_type_legacy = item.data_type;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
BLO_write_struct(writer, NodeGeometryAttributeCapture, node->storage);
|
|
|
|
|
nodes::CaptureAttributeItemsAccessor::blend_write(writer, *node);
|
|
|
|
|
}
|
2024-05-13 16:07:12 +02:00
|
|
|
else if (node->typeinfo != &NodeTypeUndefined) {
|
2023-05-16 09:39:42 -04:00
|
|
|
BLO_write_struct_by_name(writer, node->typeinfo->storagename, node->storage);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2020-12-11 17:32:08 +01:00
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
if (node->type == CMP_NODE_OUTPUT_FILE) {
|
|
|
|
|
/* Inputs have their own storage data. */
|
|
|
|
|
NodeImageMultiFile *nimf = (NodeImageMultiFile *)node->storage;
|
|
|
|
|
BKE_image_format_blend_write(writer, &nimf->format);
|
|
|
|
|
|
|
|
|
|
LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
|
|
|
|
|
NodeImageMultiFileSocket *sockdata = static_cast<NodeImageMultiFileSocket *>(
|
|
|
|
|
sock->storage);
|
|
|
|
|
BLO_write_struct(writer, NodeImageMultiFileSocket, sockdata);
|
|
|
|
|
BKE_image_format_blend_write(writer, &sockdata->format);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2021-05-12 12:41:21 +02:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
if (ELEM(node->type, CMP_NODE_IMAGE, CMP_NODE_R_LAYERS)) {
|
|
|
|
|
/* Write extra socket info. */
|
|
|
|
|
LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
|
|
|
|
|
BLO_write_struct(writer, NodeImageLayer, sock->storage);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (node->type == GEO_NODE_SIMULATION_OUTPUT) {
|
2024-05-13 16:07:12 +02:00
|
|
|
nodes::SimulationItemsAccessor::blend_write(writer, *node);
|
2021-05-12 12:41:21 +02:00
|
|
|
}
|
2023-07-11 22:36:10 +02:00
|
|
|
if (node->type == GEO_NODE_REPEAT_OUTPUT) {
|
2024-05-13 16:07:12 +02:00
|
|
|
nodes::RepeatItemsAccessor::blend_write(writer, *node);
|
2023-07-11 22:36:10 +02:00
|
|
|
}
|
2023-11-22 16:11:32 +01:00
|
|
|
if (node->type == GEO_NODE_INDEX_SWITCH) {
|
2024-05-13 16:07:12 +02:00
|
|
|
nodes::IndexSwitchItemsAccessor::blend_write(writer, *node);
|
2023-11-22 16:11:32 +01:00
|
|
|
}
|
2023-12-18 13:01:06 +01:00
|
|
|
if (node->type == GEO_NODE_BAKE) {
|
2024-05-13 16:07:12 +02:00
|
|
|
nodes::BakeItemsAccessor::blend_write(writer, *node);
|
2023-12-18 13:01:06 +01:00
|
|
|
}
|
2024-01-26 12:40:01 +01:00
|
|
|
if (node->type == GEO_NODE_MENU_SWITCH) {
|
2024-05-13 16:07:12 +02:00
|
|
|
nodes::MenuSwitchItemsAccessor::blend_write(writer, *node);
|
2024-01-26 12:40:01 +01:00
|
|
|
}
|
2020-09-10 13:33:29 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
|
|
|
|
|
BLO_write_struct(writer, bNodeLink, link);
|
2020-09-10 13:33:29 +02:00
|
|
|
}
|
|
|
|
|
|
2023-08-30 12:37:21 +02:00
|
|
|
ntree->tree_interface.write(writer);
|
|
|
|
|
if (!BLO_write_is_undo(writer)) {
|
2024-05-13 16:07:12 +02:00
|
|
|
forward_compat::write_legacy_sockets(writer, ntree);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
|
2023-08-30 16:35:38 -04:00
|
|
|
BLO_write_struct(writer, GeometryNodeAssetTraits, ntree->geometry_node_asset_traits);
|
|
|
|
|
|
Nodes: add nested node ids and use them for simulation state
The simulation state used by simulation nodes is owned by the modifier. Since a
geometry nodes setup can contain an arbitrary number of simulations, the modifier
has a mapping from `SimulationZoneID` to `SimulationZoneState`. This patch changes
what is used as `SimulationZoneID`.
Previously, the `SimulationZoneID` contained a list of `bNode::identifier` that described
the path from the root node tree to the simulation output node. This works ok in many
cases, but also has a significant problem: The `SimulationZoneID` changes when moving
the simulation zone into or out of a node group. This implies that any of these operations
loses the mapping from zone to simulation state, invalidating the cache or even baked data.
The goal of this patch is to introduce a single-integer ID that identifies a (nested) simulation
zone and is stable even when grouping and un-grouping. The ID should be stable even if the
node group containing the (nested) simulation zone is in a separate linked .blend file and
that linked file is changed.
In the future, the same kind of ID can be used to store e.g. checkpoint/baked/frozen data
in the modifier.
To achieve the described goal, node trees can now store an arbitrary number of nested node
references (an array of `bNestedNodeRef`). Each nested node reference has an ID that is
unique within the current node tree. The node tree does not store the entire path to the
nested node. Instead it only know which group node the nested node is in, and what the
nested node ID of the node is within that group. Grouping and un-grouping operations
have to update the nested node references to keep the IDs stable. Importantly though,
these operations only have to care about the two node groups that are affected. IDs in
higher level node groups remain unchanged by design.
A consequence of this design is that every `bNodeTree` now has a `bNestedNodeRef`
for every (nested) simulation zone. Two instances of the same simulation zone (because
a node group is reused) are referenced by two separate `bNestedNodeRef`. This is
important to keep in mind, because it also means that this solution doesn't scale well if
we wanted to use it to keep stable references to *all* nested nodes. I can't think of a
solution that fulfills the described requirements but scales better with more nodes. For
that reason, this solution should only be used when we want to store data for each
referenced nested node at the top level (like we do for simulations).
This is not a replacement for `ViewerPath` which can store a path to data in a node tree
without changing the node tree. Also `ViewerPath` can contain information like the loop
iteration that should be viewed (#109164). `bNestedNodeRef` can't differentiate between
different iterations of a loop. This also means that simulations can't be used inside of a
loop (loops inside of a simulation work fine though).
When baking, the new stable ID is now written to disk, which means that baked data is
not invalidated by grouping/un-grouping operations. Backward compatibility for baked
data is provided, but only works as long as the simulation zone has not been moved to
a different node group yet. Forward compatibility for the baked data is not provided
(so older versions can't load the data baked with a newer version of Blender).
Pull Request: https://projects.blender.org/blender/blender/pulls/109444
2023-07-01 11:54:32 +02:00
|
|
|
BLO_write_struct_array(
|
|
|
|
|
writer, bNestedNodeRef, ntree->nested_node_refs_num, ntree->nested_node_refs);
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
BKE_previewimg_blend_write(writer, ntree->preview);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2020-09-10 13:33:29 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
static void ntree_blend_write(BlendWriter *writer, ID *id, const void *id_address)
|
2023-05-15 15:14:22 +02:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
bNodeTree *ntree = reinterpret_cast<bNodeTree *>(id);
|
2020-09-10 13:33:29 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/* Clean up, important in undo case to reduce false detection of changed datablocks. */
|
|
|
|
|
ntree->typeinfo = nullptr;
|
|
|
|
|
ntree->runtime->execdata = nullptr;
|
2020-09-10 13:33:29 +02:00
|
|
|
|
2023-08-30 12:37:21 +02:00
|
|
|
if (!BLO_write_is_undo(writer)) {
|
|
|
|
|
/* Generate legacy inputs/outputs socket ListBase for forward compatibility.
|
2024-05-03 11:32:43 +10:00
|
|
|
* NOTE: this has to happen before writing the ntree struct itself so that the ListBase
|
2023-08-30 12:37:21 +02:00
|
|
|
* first/last pointers are valid. */
|
2024-05-13 16:07:12 +02:00
|
|
|
forward_compat::construct_interface_as_legacy_sockets(ntree);
|
2023-08-30 12:37:21 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
BLO_write_id_struct(writer, bNodeTree, id_address, &ntree->id);
|
2020-09-10 13:33:29 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
ntreeBlendWrite(writer, ntree);
|
2023-08-30 12:37:21 +02:00
|
|
|
|
|
|
|
|
if (!BLO_write_is_undo(writer)) {
|
2024-05-13 16:07:12 +02:00
|
|
|
forward_compat::cleanup_legacy_sockets(ntree);
|
2023-08-30 12:37:21 +02:00
|
|
|
}
|
2020-09-10 13:33:29 +02:00
|
|
|
}
|
|
|
|
|
|
2023-11-02 12:55:37 +01:00
|
|
|
/**
|
|
|
|
|
* Sockets with default_value data must be known built-in types, otherwise reading and writing data
|
|
|
|
|
* correctly cannot be guaranteed. Discard any socket with default_value data that has an unknown
|
|
|
|
|
* type.
|
|
|
|
|
*/
|
|
|
|
|
static bool is_node_socket_supported(const bNodeSocket *sock)
|
|
|
|
|
{
|
|
|
|
|
switch (eNodeSocketDatatype(sock->type)) {
|
|
|
|
|
case SOCK_FLOAT:
|
|
|
|
|
case SOCK_VECTOR:
|
|
|
|
|
case SOCK_RGBA:
|
|
|
|
|
case SOCK_BOOLEAN:
|
|
|
|
|
case SOCK_INT:
|
|
|
|
|
case SOCK_STRING:
|
|
|
|
|
case SOCK_CUSTOM:
|
|
|
|
|
case SOCK_SHADER:
|
|
|
|
|
case SOCK_GEOMETRY:
|
|
|
|
|
case SOCK_OBJECT:
|
|
|
|
|
case SOCK_IMAGE:
|
|
|
|
|
case SOCK_COLLECTION:
|
|
|
|
|
case SOCK_TEXTURE:
|
|
|
|
|
case SOCK_MATERIAL:
|
|
|
|
|
case SOCK_ROTATION:
|
2024-01-26 12:40:01 +01:00
|
|
|
case SOCK_MENU:
|
2024-02-13 18:59:36 +01:00
|
|
|
case SOCK_MATRIX:
|
2023-11-02 12:55:37 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
static void direct_link_node_socket(BlendDataReader *reader, bNodeSocket *sock)
|
2020-09-10 13:33:29 +02:00
|
|
|
{
|
2024-04-24 17:01:22 +02:00
|
|
|
BLO_read_struct(reader, IDProperty, &sock->prop);
|
2023-05-16 09:39:42 -04:00
|
|
|
IDP_BlendDataRead(reader, &sock->prop);
|
2020-09-10 13:33:29 +02:00
|
|
|
|
2024-04-24 17:01:22 +02:00
|
|
|
BLO_read_struct(reader, bNodeLink, &sock->link);
|
2023-05-16 09:39:42 -04:00
|
|
|
sock->typeinfo = nullptr;
|
|
|
|
|
BLO_read_data_address(reader, &sock->storage);
|
|
|
|
|
BLO_read_data_address(reader, &sock->default_value);
|
2024-04-24 17:01:22 +02:00
|
|
|
BLO_read_string(reader, &sock->default_attribute_name);
|
2023-05-16 09:39:42 -04:00
|
|
|
sock->runtime = MEM_new<bNodeSocketRuntime>(__func__);
|
2024-01-26 12:40:01 +01:00
|
|
|
|
|
|
|
|
switch (eNodeSocketDatatype(sock->type)) {
|
|
|
|
|
case SOCK_MENU: {
|
|
|
|
|
bNodeSocketValueMenu &default_value = *sock->default_value_typed<bNodeSocketValueMenu>();
|
|
|
|
|
/* Clear runtime data. */
|
|
|
|
|
default_value.enum_items = nullptr;
|
|
|
|
|
default_value.runtime_flag = 0;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2020-09-10 13:33:29 +02:00
|
|
|
|
2024-04-06 11:23:53 +02:00
|
|
|
static void remove_unsupported_sockets(ListBase *sockets, ListBase *links)
|
2023-11-02 12:55:37 +01:00
|
|
|
{
|
2024-04-06 11:23:53 +02:00
|
|
|
LISTBASE_FOREACH_MUTABLE (bNodeSocket *, sock, sockets) {
|
2023-11-02 12:55:37 +01:00
|
|
|
if (is_node_socket_supported(sock)) {
|
2024-04-06 11:23:53 +02:00
|
|
|
continue;
|
2023-11-02 12:55:37 +01:00
|
|
|
}
|
2024-04-06 11:23:53 +02:00
|
|
|
|
|
|
|
|
/* First remove any link pointing to the socket. */
|
|
|
|
|
if (links) {
|
|
|
|
|
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, links) {
|
|
|
|
|
if (link->fromsock == sock || link->tosock == sock) {
|
|
|
|
|
BLI_remlink(links, link);
|
|
|
|
|
if (link->tosock) {
|
|
|
|
|
link->tosock->link = nullptr;
|
|
|
|
|
}
|
|
|
|
|
MEM_freeN(link);
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-11-02 12:55:37 +01:00
|
|
|
}
|
2024-04-06 11:23:53 +02:00
|
|
|
|
|
|
|
|
BLI_remlink(sockets, sock);
|
|
|
|
|
MEM_delete(sock->runtime);
|
|
|
|
|
MEM_freeN(sock);
|
2023-11-02 12:55:37 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void ntreeBlendReadData(BlendDataReader *reader, ID *owner_id, bNodeTree *ntree)
|
2023-05-15 15:14:22 +02:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
/* Special case for this pointer, do not rely on regular `lib_link` process here. Avoids needs
|
|
|
|
|
* for do_versioning, and ensures coherence of data in any case.
|
|
|
|
|
*
|
|
|
|
|
* NOTE: Old versions are very often 'broken' here, just fix it silently in these cases.
|
|
|
|
|
*/
|
|
|
|
|
if (BLO_read_fileversion_get(reader) > 300) {
|
|
|
|
|
BLI_assert((ntree->id.flag & LIB_EMBEDDED_DATA) != 0 || owner_id == nullptr);
|
|
|
|
|
}
|
|
|
|
|
BLI_assert(owner_id == nullptr || owner_id->lib == ntree->id.lib);
|
|
|
|
|
if (owner_id != nullptr && (ntree->id.flag & LIB_EMBEDDED_DATA) == 0) {
|
|
|
|
|
/* This is unfortunate, but currently a lot of existing files (including startup ones) have
|
|
|
|
|
* missing `LIB_EMBEDDED_DATA` flag.
|
|
|
|
|
*
|
|
|
|
|
* NOTE: Using do_version is not a solution here, since this code will be called before any
|
|
|
|
|
* do_version takes place. Keeping it here also ensures future (or unknown existing) similar
|
|
|
|
|
* bugs won't go easily unnoticed. */
|
|
|
|
|
if (BLO_read_fileversion_get(reader) > 300) {
|
|
|
|
|
CLOG_WARN(&LOG,
|
|
|
|
|
"Fixing root node tree '%s' owned by '%s' missing EMBEDDED tag, please consider "
|
|
|
|
|
"re-saving your (startup) file",
|
|
|
|
|
ntree->id.name,
|
|
|
|
|
owner_id->name);
|
2020-09-10 13:33:29 +02:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
ntree->id.flag |= LIB_EMBEDDED_DATA;
|
2020-09-10 13:33:29 +02:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
ntree->owner_id = owner_id;
|
2020-09-10 13:33:29 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/* NOTE: writing and reading goes in sync, for speed. */
|
|
|
|
|
ntree->typeinfo = nullptr;
|
2022-09-19 11:57:10 -05:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
ntree->runtime = MEM_new<bNodeTreeRuntime>(__func__);
|
|
|
|
|
BKE_ntree_update_tag_missing_runtime_data(ntree);
|
2022-09-19 11:57:10 -05:00
|
|
|
|
2024-05-08 11:25:00 +02:00
|
|
|
BLO_read_string(reader, &ntree->description);
|
|
|
|
|
|
2024-04-24 17:01:22 +02:00
|
|
|
BLO_read_struct_list(reader, bNode, &ntree->nodes);
|
2023-05-16 09:39:42 -04:00
|
|
|
int i;
|
|
|
|
|
LISTBASE_FOREACH_INDEX (bNode *, node, &ntree->nodes, i) {
|
|
|
|
|
node->runtime = MEM_new<bNodeRuntime>(__func__);
|
|
|
|
|
node->typeinfo = nullptr;
|
|
|
|
|
node->runtime->index_in_tree = i;
|
2013-03-18 16:34:57 +00:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/* Create the `nodes_by_id` cache eagerly so it can be expected to be valid. Because
|
|
|
|
|
* we create it here we also have to check for zero identifiers from previous versions. */
|
|
|
|
|
if (node->identifier == 0 || ntree->runtime->nodes_by_id.contains_as(node->identifier)) {
|
|
|
|
|
nodeUniqueID(ntree, node);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
ntree->runtime->nodes_by_id.add_new(node);
|
2013-03-18 16:34:57 +00:00
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2024-04-24 17:01:22 +02:00
|
|
|
BLO_read_struct_list(reader, bNodeSocket, &node->inputs);
|
|
|
|
|
BLO_read_struct_list(reader, bNodeSocket, &node->outputs);
|
|
|
|
|
BLO_read_struct_array(
|
|
|
|
|
reader, bNodePanelState, node->num_panel_states, &node->panel_states_array);
|
2013-03-18 16:34:57 +00:00
|
|
|
|
2024-04-24 17:01:22 +02:00
|
|
|
BLO_read_struct(reader, IDProperty, &node->prop);
|
2023-05-16 09:39:42 -04:00
|
|
|
IDP_BlendDataRead(reader, &node->prop);
|
2018-06-17 17:05:51 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
if (node->type == CMP_NODE_MOVIEDISTORTION) {
|
|
|
|
|
/* Do nothing, this is runtime cache and hence handled by generic code using
|
|
|
|
|
* `IDTypeInfo.foreach_cache` callback. */
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
BLO_read_data_address(reader, &node->storage);
|
|
|
|
|
}
|
2018-06-17 17:05:51 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
if (node->storage) {
|
|
|
|
|
switch (node->type) {
|
|
|
|
|
case SH_NODE_CURVE_VEC:
|
|
|
|
|
case SH_NODE_CURVE_RGB:
|
|
|
|
|
case SH_NODE_CURVE_FLOAT:
|
|
|
|
|
case CMP_NODE_TIME:
|
|
|
|
|
case CMP_NODE_CURVE_VEC:
|
|
|
|
|
case CMP_NODE_CURVE_RGB:
|
|
|
|
|
case CMP_NODE_HUECORRECT:
|
|
|
|
|
case TEX_NODE_CURVE_RGB:
|
|
|
|
|
case TEX_NODE_CURVE_TIME: {
|
|
|
|
|
BKE_curvemapping_blend_read(reader, static_cast<CurveMapping *>(node->storage));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case SH_NODE_SCRIPT: {
|
|
|
|
|
NodeShaderScript *nss = static_cast<NodeShaderScript *>(node->storage);
|
2024-04-24 17:01:22 +02:00
|
|
|
BLO_read_string(reader, &nss->bytecode);
|
2023-05-16 09:39:42 -04:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case SH_NODE_TEX_POINTDENSITY: {
|
|
|
|
|
NodeShaderTexPointDensity *npd = static_cast<NodeShaderTexPointDensity *>(node->storage);
|
2024-05-13 16:07:12 +02:00
|
|
|
npd->pd = dna::shallow_zero_initialize();
|
2023-05-16 09:39:42 -04:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case SH_NODE_TEX_IMAGE: {
|
|
|
|
|
NodeTexImage *tex = static_cast<NodeTexImage *>(node->storage);
|
|
|
|
|
tex->iuser.scene = nullptr;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case SH_NODE_TEX_ENVIRONMENT: {
|
|
|
|
|
NodeTexEnvironment *tex = static_cast<NodeTexEnvironment *>(node->storage);
|
|
|
|
|
tex->iuser.scene = nullptr;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case CMP_NODE_IMAGE:
|
|
|
|
|
case CMP_NODE_R_LAYERS:
|
2023-12-03 23:20:44 +01:00
|
|
|
case CMP_NODE_VIEWER: {
|
2023-05-16 09:39:42 -04:00
|
|
|
ImageUser *iuser = static_cast<ImageUser *>(node->storage);
|
|
|
|
|
iuser->scene = nullptr;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case CMP_NODE_CRYPTOMATTE_LEGACY:
|
|
|
|
|
case CMP_NODE_CRYPTOMATTE: {
|
|
|
|
|
NodeCryptomatte *nc = static_cast<NodeCryptomatte *>(node->storage);
|
2024-04-24 17:01:22 +02:00
|
|
|
BLO_read_string(reader, &nc->matte_id);
|
|
|
|
|
BLO_read_struct_list(reader, CryptomatteEntry, &nc->entries);
|
2023-05-16 09:39:42 -04:00
|
|
|
BLI_listbase_clear(&nc->runtime.layers);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case TEX_NODE_IMAGE: {
|
|
|
|
|
ImageUser *iuser = static_cast<ImageUser *>(node->storage);
|
|
|
|
|
iuser->scene = nullptr;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case CMP_NODE_OUTPUT_FILE: {
|
|
|
|
|
NodeImageMultiFile *nimf = static_cast<NodeImageMultiFile *>(node->storage);
|
|
|
|
|
BKE_image_format_blend_read_data(reader, &nimf->format);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case FN_NODE_INPUT_STRING: {
|
|
|
|
|
NodeInputString *storage = static_cast<NodeInputString *>(node->storage);
|
2024-04-24 17:01:22 +02:00
|
|
|
BLO_read_string(reader, &storage->string);
|
2023-05-16 09:39:42 -04:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case GEO_NODE_SIMULATION_OUTPUT: {
|
2024-05-13 16:07:12 +02:00
|
|
|
nodes::SimulationItemsAccessor::blend_read_data(reader, *node);
|
2023-05-16 09:39:42 -04:00
|
|
|
break;
|
|
|
|
|
}
|
2023-07-11 22:36:10 +02:00
|
|
|
case GEO_NODE_REPEAT_OUTPUT: {
|
2024-05-13 16:07:12 +02:00
|
|
|
nodes::RepeatItemsAccessor::blend_read_data(reader, *node);
|
2023-07-11 22:36:10 +02:00
|
|
|
break;
|
|
|
|
|
}
|
2023-11-22 16:11:32 +01:00
|
|
|
case GEO_NODE_INDEX_SWITCH: {
|
2024-05-13 16:07:12 +02:00
|
|
|
nodes::IndexSwitchItemsAccessor::blend_read_data(reader, *node);
|
2023-11-22 16:11:32 +01:00
|
|
|
break;
|
|
|
|
|
}
|
2023-12-18 13:01:06 +01:00
|
|
|
case GEO_NODE_BAKE: {
|
2024-05-13 16:07:12 +02:00
|
|
|
nodes::BakeItemsAccessor::blend_read_data(reader, *node);
|
2023-12-18 13:01:06 +01:00
|
|
|
break;
|
|
|
|
|
}
|
2024-01-26 12:40:01 +01:00
|
|
|
case GEO_NODE_MENU_SWITCH: {
|
2024-05-13 16:07:12 +02:00
|
|
|
nodes::MenuSwitchItemsAccessor::blend_read_data(reader, *node);
|
2024-01-26 12:40:01 +01:00
|
|
|
break;
|
|
|
|
|
}
|
2024-05-31 16:23:31 +02:00
|
|
|
case GEO_NODE_CAPTURE_ATTRIBUTE: {
|
|
|
|
|
nodes::CaptureAttributeItemsAccessor::blend_read_data(reader, *node);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
Geometry Nodes: add simulation support
This adds support for building simulations with geometry nodes. A new
`Simulation Input` and `Simulation Output` node allow maintaining a
simulation state across multiple frames. Together these two nodes form
a `simulation zone` which contains all the nodes that update the simulation
state from one frame to the next.
A new simulation zone can be added via the menu
(`Simulation > Simulation Zone`) or with the node add search.
The simulation state contains a geometry by default. However, it is possible
to add multiple geometry sockets as well as other socket types. Currently,
field inputs are evaluated and stored for the preceding geometry socket in
the order that the sockets are shown. Simulation state items can be added
by linking one of the empty sockets to something else. In the sidebar, there
is a new panel that allows adding, removing and reordering these sockets.
The simulation nodes behave as follows:
* On the first frame, the inputs of the `Simulation Input` node are evaluated
to initialize the simulation state. In later frames these sockets are not
evaluated anymore. The `Delta Time` at the first frame is zero, but the
simulation zone is still evaluated.
* On every next frame, the `Simulation Input` node outputs the simulation
state of the previous frame. Nodes in the simulation zone can edit that
data in arbitrary ways, also taking into account the `Delta Time`. The new
simulation state has to be passed to the `Simulation Output` node where it
is cached and forwarded.
* On a frame that is already cached or baked, the nodes in the simulation
zone are not evaluated, because the `Simulation Output` node can return
the previously cached data directly.
It is not allowed to connect sockets from inside the simulation zone to the
outside without going through the `Simulation Output` node. This is a necessary
restriction to make caching and sub-frame interpolation work. Links can go into
the simulation zone without problems though.
Anonymous attributes are not propagated by the simulation nodes unless they
are explicitly stored in the simulation state. This is unfortunate, but
currently there is no practical and reliable alternative. The core problem
is detecting which anonymous attributes will be required for the simulation
and afterwards. While we can detect this for the current evaluation, we can't
look into the future in time to see what data will be necessary. We intend to
make it easier to explicitly pass data through a simulation in the future,
even if the simulation is in a nested node group.
There is a new `Simulation Nodes` panel in the physics tab in the properties
editor. It allows baking all simulation zones on the selected objects. The
baking options are intentially kept at a minimum for this MVP. More features
for simulation baking as well as baking in general can be expected to be added
separately.
All baked data is stored on disk in a folder next to the .blend file. #106937
describes how baking is implemented in more detail. Volumes can not be baked
yet and materials are lost during baking for now. Packing the baked data into
the .blend file is not yet supported.
The timeline indicates which frames are currently cached, baked or cached but
invalidated by user-changes.
Simulation input and output nodes are internally linked together by their
`bNode.identifier` which stays the same even if the node name changes. They
are generally added and removed together. However, there are still cases where
"dangling" simulation nodes can be created currently. Those generally don't
cause harm, but would be nice to avoid this in more cases in the future.
Co-authored-by: Hans Goudey <h.goudey@me.com>
Co-authored-by: Lukas Tönne <lukas@blender.org>
Pull Request: https://projects.blender.org/blender/blender/pulls/104924
2023-05-03 13:18:51 +02:00
|
|
|
}
|
2024-04-24 17:01:22 +02:00
|
|
|
BLO_read_struct_list(reader, bNodeLink, &ntree->links);
|
2023-05-16 09:39:42 -04:00
|
|
|
BLI_assert(ntree->all_nodes().size() == BLI_listbase_count(&ntree->nodes));
|
2013-12-01 21:56:52 +01:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/* and we connect the rest */
|
|
|
|
|
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
|
2024-04-24 17:01:22 +02:00
|
|
|
BLO_read_struct(reader, bNode, &node->parent);
|
2013-12-08 21:53:35 +01:00
|
|
|
|
2024-04-06 11:23:53 +02:00
|
|
|
LISTBASE_FOREACH_MUTABLE (bNodeSocket *, sock, &node->inputs) {
|
|
|
|
|
direct_link_node_socket(reader, sock);
|
|
|
|
|
}
|
|
|
|
|
LISTBASE_FOREACH_MUTABLE (bNodeSocket *, sock, &node->outputs) {
|
|
|
|
|
direct_link_node_socket(reader, sock);
|
|
|
|
|
}
|
2013-12-08 21:53:35 +01:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/* Socket storage. */
|
|
|
|
|
if (node->type == CMP_NODE_OUTPUT_FILE) {
|
|
|
|
|
LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
|
|
|
|
|
NodeImageMultiFileSocket *sockdata = static_cast<NodeImageMultiFileSocket *>(
|
|
|
|
|
sock->storage);
|
|
|
|
|
BKE_image_format_blend_read_data(reader, &sockdata->format);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
Geometry Nodes: add simulation support
This adds support for building simulations with geometry nodes. A new
`Simulation Input` and `Simulation Output` node allow maintaining a
simulation state across multiple frames. Together these two nodes form
a `simulation zone` which contains all the nodes that update the simulation
state from one frame to the next.
A new simulation zone can be added via the menu
(`Simulation > Simulation Zone`) or with the node add search.
The simulation state contains a geometry by default. However, it is possible
to add multiple geometry sockets as well as other socket types. Currently,
field inputs are evaluated and stored for the preceding geometry socket in
the order that the sockets are shown. Simulation state items can be added
by linking one of the empty sockets to something else. In the sidebar, there
is a new panel that allows adding, removing and reordering these sockets.
The simulation nodes behave as follows:
* On the first frame, the inputs of the `Simulation Input` node are evaluated
to initialize the simulation state. In later frames these sockets are not
evaluated anymore. The `Delta Time` at the first frame is zero, but the
simulation zone is still evaluated.
* On every next frame, the `Simulation Input` node outputs the simulation
state of the previous frame. Nodes in the simulation zone can edit that
data in arbitrary ways, also taking into account the `Delta Time`. The new
simulation state has to be passed to the `Simulation Output` node where it
is cached and forwarded.
* On a frame that is already cached or baked, the nodes in the simulation
zone are not evaluated, because the `Simulation Output` node can return
the previously cached data directly.
It is not allowed to connect sockets from inside the simulation zone to the
outside without going through the `Simulation Output` node. This is a necessary
restriction to make caching and sub-frame interpolation work. Links can go into
the simulation zone without problems though.
Anonymous attributes are not propagated by the simulation nodes unless they
are explicitly stored in the simulation state. This is unfortunate, but
currently there is no practical and reliable alternative. The core problem
is detecting which anonymous attributes will be required for the simulation
and afterwards. While we can detect this for the current evaluation, we can't
look into the future in time to see what data will be necessary. We intend to
make it easier to explicitly pass data through a simulation in the future,
even if the simulation is in a nested node group.
There is a new `Simulation Nodes` panel in the physics tab in the properties
editor. It allows baking all simulation zones on the selected objects. The
baking options are intentially kept at a minimum for this MVP. More features
for simulation baking as well as baking in general can be expected to be added
separately.
All baked data is stored on disk in a folder next to the .blend file. #106937
describes how baking is implemented in more detail. Volumes can not be baked
yet and materials are lost during baking for now. Packing the baked data into
the .blend file is not yet supported.
The timeline indicates which frames are currently cached, baked or cached but
invalidated by user-changes.
Simulation input and output nodes are internally linked together by their
`bNode.identifier` which stays the same even if the node name changes. They
are generally added and removed together. However, there are still cases where
"dangling" simulation nodes can be created currently. Those generally don't
cause harm, but would be nice to avoid this in more cases in the future.
Co-authored-by: Hans Goudey <h.goudey@me.com>
Co-authored-by: Lukas Tönne <lukas@blender.org>
Pull Request: https://projects.blender.org/blender/blender/pulls/104924
2023-05-03 13:18:51 +02:00
|
|
|
|
2023-08-30 12:37:21 +02:00
|
|
|
/* Read legacy interface socket lists for versioning. */
|
2024-04-24 17:01:22 +02:00
|
|
|
BLO_read_struct_list(reader, bNodeSocket, &ntree->inputs_legacy);
|
|
|
|
|
BLO_read_struct_list(reader, bNodeSocket, &ntree->outputs_legacy);
|
2024-04-06 11:23:53 +02:00
|
|
|
LISTBASE_FOREACH_MUTABLE (bNodeSocket *, sock, &ntree->inputs_legacy) {
|
|
|
|
|
direct_link_node_socket(reader, sock);
|
|
|
|
|
}
|
|
|
|
|
LISTBASE_FOREACH_MUTABLE (bNodeSocket *, sock, &ntree->outputs_legacy) {
|
|
|
|
|
direct_link_node_socket(reader, sock);
|
|
|
|
|
}
|
2018-06-17 17:05:51 +02:00
|
|
|
|
2023-08-30 12:37:21 +02:00
|
|
|
ntree->tree_interface.read_data(reader);
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
|
2024-04-24 17:01:22 +02:00
|
|
|
BLO_read_struct(reader, bNode, &link->fromnode);
|
|
|
|
|
BLO_read_struct(reader, bNode, &link->tonode);
|
|
|
|
|
BLO_read_struct(reader, bNodeSocket, &link->fromsock);
|
|
|
|
|
BLO_read_struct(reader, bNodeSocket, &link->tosock);
|
2023-05-16 09:39:42 -04:00
|
|
|
}
|
2018-06-17 17:05:51 +02:00
|
|
|
|
2024-04-06 11:23:53 +02:00
|
|
|
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
|
|
|
|
|
remove_unsupported_sockets(&node->inputs, &ntree->links);
|
|
|
|
|
remove_unsupported_sockets(&node->outputs, &ntree->links);
|
|
|
|
|
}
|
|
|
|
|
remove_unsupported_sockets(&ntree->inputs_legacy, nullptr);
|
|
|
|
|
remove_unsupported_sockets(&ntree->outputs_legacy, nullptr);
|
|
|
|
|
|
2024-04-24 17:01:22 +02:00
|
|
|
BLO_read_struct(reader, GeometryNodeAssetTraits, &ntree->geometry_node_asset_traits);
|
|
|
|
|
BLO_read_struct_array(
|
|
|
|
|
reader, bNestedNodeRef, ntree->nested_node_refs_num, &ntree->nested_node_refs);
|
Nodes: add nested node ids and use them for simulation state
The simulation state used by simulation nodes is owned by the modifier. Since a
geometry nodes setup can contain an arbitrary number of simulations, the modifier
has a mapping from `SimulationZoneID` to `SimulationZoneState`. This patch changes
what is used as `SimulationZoneID`.
Previously, the `SimulationZoneID` contained a list of `bNode::identifier` that described
the path from the root node tree to the simulation output node. This works ok in many
cases, but also has a significant problem: The `SimulationZoneID` changes when moving
the simulation zone into or out of a node group. This implies that any of these operations
loses the mapping from zone to simulation state, invalidating the cache or even baked data.
The goal of this patch is to introduce a single-integer ID that identifies a (nested) simulation
zone and is stable even when grouping and un-grouping. The ID should be stable even if the
node group containing the (nested) simulation zone is in a separate linked .blend file and
that linked file is changed.
In the future, the same kind of ID can be used to store e.g. checkpoint/baked/frozen data
in the modifier.
To achieve the described goal, node trees can now store an arbitrary number of nested node
references (an array of `bNestedNodeRef`). Each nested node reference has an ID that is
unique within the current node tree. The node tree does not store the entire path to the
nested node. Instead it only know which group node the nested node is in, and what the
nested node ID of the node is within that group. Grouping and un-grouping operations
have to update the nested node references to keep the IDs stable. Importantly though,
these operations only have to care about the two node groups that are affected. IDs in
higher level node groups remain unchanged by design.
A consequence of this design is that every `bNodeTree` now has a `bNestedNodeRef`
for every (nested) simulation zone. Two instances of the same simulation zone (because
a node group is reused) are referenced by two separate `bNestedNodeRef`. This is
important to keep in mind, because it also means that this solution doesn't scale well if
we wanted to use it to keep stable references to *all* nested nodes. I can't think of a
solution that fulfills the described requirements but scales better with more nodes. For
that reason, this solution should only be used when we want to store data for each
referenced nested node at the top level (like we do for simulations).
This is not a replacement for `ViewerPath` which can store a path to data in a node tree
without changing the node tree. Also `ViewerPath` can contain information like the loop
iteration that should be viewed (#109164). `bNestedNodeRef` can't differentiate between
different iterations of a loop. This also means that simulations can't be used inside of a
loop (loops inside of a simulation work fine though).
When baking, the new stable ID is now written to disk, which means that baked data is
not invalidated by grouping/un-grouping operations. Backward compatibility for baked
data is provided, but only works as long as the simulation zone has not been moved to
a different node group yet. Forward compatibility for the baked data is not provided
(so older versions can't load the data baked with a newer version of Blender).
Pull Request: https://projects.blender.org/blender/blender/pulls/109444
2023-07-01 11:54:32 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/* TODO: should be dealt by new generic cache handling of IDs... */
|
|
|
|
|
ntree->previews = nullptr;
|
2013-03-18 16:34:57 +00:00
|
|
|
|
2024-04-24 17:01:22 +02:00
|
|
|
BLO_read_struct(reader, PreviewImage, &ntree->preview);
|
2023-05-16 09:39:42 -04:00
|
|
|
BKE_previewimg_blend_read(reader, ntree->preview);
|
Orange: more noodle updates!
**** NEW: Group Nodes
Node trees usually become messy and confusing quickly, so we need
not only a way to collapse Nodes into single 'groups', but also a
way to re-use that data to create libraries of effects.
This has been done by making a new Library data type, the NodeTree.
Everything that has been grouped is stored here, and available for
re-use, appending or linking. These NodeTrees are fully generic,
i.e. can store shader trees, composit trees, and so on. The 'type'
value as stored in the NodeTree will keep track of internal type
definitions and execute/drawing callbacks. Needless to say, re-using
shader trees in a composit tree is a bit useless, and will be
prevented in the browsing code. :)
So; any NodeTree can become a "Goup Node" inside in a NodeTree. This
Group Node then works just like any Node.
To prevent the current code to become too complex, I've disabled
the possibility to insert Groups inside of Groups. That might be
enabled later, but is a real nasty piece of code to get OK.
Since Group Nodes are a dynamic Node type, a lot of work has been
done to ensure Node definitions can be dynamic too, but still allow
to be stored in files, and allow to be verified for type-definition
changes on reloading. This system needs a little bit maturing still,
so the Python gurus should better wait a little bit! (Also for me to
write the definite API docs for it).
What works now:
- Press CTRL+G to create a new Group. The grouping code checks for
impossible selections (like an unselected node between selected nodes).
Everthing that's selected then gets removed from the current tree, and
inserted in a new NodeTree library data block. A Group Node then is
added which links to this new NodeTree.
- Press ALT+G to ungroup. This will not delete the NodeTree library
data, but just duplicate the Group into the current tree.
- Press TAB, or click on the NodeTree icon to edit Groups. Note that
NodeTrees are instances, so editing one Group will also change the
other users.
This also means that when removing nodes in a Group (or hiding sockets
or changing internal links) this is immediately corrected for all users
of this Group, also in other Materials.
- While editing Groups, only the internal Nodes can be edited. A single
click outside of the Group boundary will close this 'edit mode'.
What needs to be done:
- SHIFT+A menu in toolbox style, also including a list of Groups
- Enable the single-user button in the Group Node
- Displaying all (visible) internal group UI elements in the Node Panel
- Enable Library linking and prevent editing of Groups then.
**** NEW: Socket Visibility control
Node types will be generated with a lot of possible inputs or outputs,
and drawing all sockets all the time isn't very useful then.
A new option in the Node header ('plus' icon) allows to either hide all
unused sockets (first keypress) or to reveil them (when there are hidden
sockets, the icon displays black, otherwise it's blended).
Hidden sockets in Nodes also are not exported to a Group, so this way
you can control what options (in/outputs) exactly are available.
To be done:
- a way to hide individual sockets, like with a RMB click on it.
**** NEW: Nodes now render!
This is still quite primitive, more on a level to replace the (now
obsolete and disabled) Material Layers.
What needs to be done:
- make the "Geometry" node work properly, also for AA textures
- make the Texture Node work (does very little at the moment)
- give Material Nodes all inputs as needed (like Map-to Panel)
- find a way to export more data from a Material Node, like the
shadow value, or light intensity only, etc
Very important also to separate from the Material Buttons the
"global" options, like "Ztransp" or "Wire" or "Halo". These can not
be set for each Material-Node individually.
Also note that the Preview Render (Buttons window) now renders a bit
differently. This was a horrid piece of antique code, using a totally
incompatible way of rendering. Target is to fully re-use internal
render code for previews.
OK... that's it mostly. Now test!
2006-01-02 13:06:05 +00:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/* type verification is in lib-link */
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2018-06-17 17:05:51 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
static void ntree_blend_read_data(BlendDataReader *reader, ID *id)
|
2023-05-15 15:14:22 +02:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
bNodeTree *ntree = reinterpret_cast<bNodeTree *>(id);
|
|
|
|
|
ntreeBlendReadData(reader, nullptr, ntree);
|
2013-03-18 16:34:57 +00:00
|
|
|
}
|
|
|
|
|
|
2023-03-11 18:07:59 +01:00
|
|
|
static void ntree_blend_read_after_liblink(BlendLibReader *reader, ID *id)
|
2013-03-18 16:34:57 +00:00
|
|
|
{
|
2023-03-11 18:07:59 +01:00
|
|
|
bNodeTree *ntree = reinterpret_cast<bNodeTree *>(id);
|
2023-05-16 09:39:42 -04:00
|
|
|
|
|
|
|
|
/* Set `node->typeinfo` pointers. This is done in lib linking, after the
|
|
|
|
|
* first versioning that can change types still without functions that
|
|
|
|
|
* update the `typeinfo` pointers. Versioning after lib linking needs
|
|
|
|
|
* these top be valid. */
|
|
|
|
|
ntreeSetTypes(nullptr, ntree);
|
|
|
|
|
|
|
|
|
|
/* For nodes with static socket layout, add/remove sockets as needed
|
|
|
|
|
* to match the static layout. */
|
|
|
|
|
if (!BLO_read_lib_is_undo(reader)) {
|
|
|
|
|
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
|
|
|
|
|
/* Don't update node groups here because they may depend on other node groups which are not
|
|
|
|
|
* fully versioned yet and don't have `typeinfo` pointers set. */
|
|
|
|
|
if (!node->is_group()) {
|
|
|
|
|
node_verify_sockets(ntree, node, false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-14 17:43:33 +02:00
|
|
|
void node_update_asset_metadata(bNodeTree &node_tree)
|
2013-03-18 16:34:57 +00:00
|
|
|
{
|
2023-09-14 17:43:33 +02:00
|
|
|
AssetMetaData *asset_data = node_tree.id.asset_data;
|
|
|
|
|
if (!asset_data) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2013-03-18 16:34:57 +00:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
BKE_asset_metadata_idprop_ensure(asset_data, idprop::create("type", node_tree.type).release());
|
|
|
|
|
auto inputs = idprop::create_group("inputs");
|
|
|
|
|
auto outputs = idprop::create_group("outputs");
|
2023-09-14 14:13:07 +02:00
|
|
|
node_tree.ensure_interface_cache();
|
2023-08-30 12:37:21 +02:00
|
|
|
for (const bNodeTreeInterfaceSocket *socket : node_tree.interface_inputs()) {
|
2023-10-19 11:48:08 +02:00
|
|
|
auto property = idprop::create(socket->name ? socket->name : "", socket->socket_type);
|
2023-05-16 09:39:42 -04:00
|
|
|
IDP_AddToGroup(inputs.get(), property.release());
|
2023-04-03 18:23:30 +02:00
|
|
|
}
|
2023-08-30 12:37:21 +02:00
|
|
|
for (const bNodeTreeInterfaceSocket *socket : node_tree.interface_outputs()) {
|
2023-10-19 11:48:08 +02:00
|
|
|
auto property = idprop::create(socket->name ? socket->name : "", socket->socket_type);
|
2023-05-16 09:39:42 -04:00
|
|
|
IDP_AddToGroup(outputs.get(), property.release());
|
2013-03-18 16:34:57 +00:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
BKE_asset_metadata_idprop_ensure(asset_data, inputs.release());
|
|
|
|
|
BKE_asset_metadata_idprop_ensure(asset_data, outputs.release());
|
2023-08-04 18:22:45 +02:00
|
|
|
if (node_tree.geometry_node_asset_traits) {
|
|
|
|
|
auto property = idprop::create("geometry_node_asset_traits_flag",
|
|
|
|
|
node_tree.geometry_node_asset_traits->flag);
|
|
|
|
|
BKE_asset_metadata_idprop_ensure(asset_data, property.release());
|
|
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2021-12-15 09:51:57 -06:00
|
|
|
|
2023-09-14 17:43:33 +02:00
|
|
|
static void node_tree_asset_pre_save(void *asset_ptr, AssetMetaData * /*asset_data*/)
|
|
|
|
|
{
|
2024-05-08 11:25:00 +02:00
|
|
|
bNodeTree &ntree = *static_cast<bNodeTree *>(asset_ptr);
|
|
|
|
|
node_update_asset_metadata(ntree);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void node_tree_asset_on_mark_asset(void *asset_ptr, AssetMetaData *asset_data)
|
|
|
|
|
{
|
|
|
|
|
bNodeTree &ntree = *static_cast<bNodeTree *>(asset_ptr);
|
|
|
|
|
node_update_asset_metadata(ntree);
|
|
|
|
|
|
|
|
|
|
/* Copy node tree description to asset description so that the user does not have to write it
|
|
|
|
|
* again. */
|
|
|
|
|
if (!asset_data->description) {
|
|
|
|
|
asset_data->description = BLI_strdup_null(ntree.description);
|
|
|
|
|
}
|
2023-09-14 17:43:33 +02:00
|
|
|
}
|
|
|
|
|
|
2024-05-08 11:25:00 +02:00
|
|
|
static void node_tree_asset_on_clear_asset(void *asset_ptr, AssetMetaData *asset_data)
|
2023-09-26 16:38:50 +02:00
|
|
|
{
|
2024-05-08 11:25:00 +02:00
|
|
|
bNodeTree &ntree = *static_cast<bNodeTree *>(asset_ptr);
|
|
|
|
|
|
|
|
|
|
/* Copy asset description to node tree description so that it is not lost when the asset data is
|
|
|
|
|
* removed. */
|
|
|
|
|
if (asset_data->description) {
|
|
|
|
|
MEM_SAFE_FREE(ntree.description);
|
|
|
|
|
ntree.description = BLI_strdup_null(asset_data->description);
|
|
|
|
|
}
|
2023-09-26 16:38:50 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
} // namespace blender::bke
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
static AssetTypeInfo AssetType_NT = {
|
|
|
|
|
/*pre_save_fn*/ blender::bke::node_tree_asset_pre_save,
|
2023-09-26 16:38:50 +02:00
|
|
|
/*on_mark_asset_fn*/ blender::bke::node_tree_asset_on_mark_asset,
|
2024-05-08 11:25:00 +02:00
|
|
|
/*on_clear_asset_fn*/ blender::bke::node_tree_asset_on_clear_asset,
|
2023-05-16 09:39:42 -04:00
|
|
|
};
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
IDTypeInfo IDType_ID_NT = {
|
|
|
|
|
/*id_code*/ ID_NT,
|
|
|
|
|
/*id_filter*/ FILTER_ID_NT,
|
2024-02-15 18:34:17 +01:00
|
|
|
/* IDProps of nodes, and #bNode.id, can use any type of ID. */
|
|
|
|
|
/*dependencies_id_types*/ FILTER_ID_ALL,
|
2023-05-16 09:39:42 -04:00
|
|
|
/*main_listbase_index*/ INDEX_ID_NT,
|
|
|
|
|
/*struct_size*/ sizeof(bNodeTree),
|
|
|
|
|
/*name*/ "NodeTree",
|
2023-10-22 08:47:26 +02:00
|
|
|
/*name_plural*/ N_("node_groups"),
|
2023-05-16 09:39:42 -04:00
|
|
|
/*translation_context*/ BLT_I18NCONTEXT_ID_NODETREE,
|
|
|
|
|
/*flags*/ IDTYPE_FLAGS_APPEND_IS_REUSABLE,
|
|
|
|
|
/*asset_type_info*/ &AssetType_NT,
|
2021-12-15 09:51:57 -06:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/*init_data*/ blender::bke::ntree_init_data,
|
|
|
|
|
/*copy_data*/ blender::bke::ntree_copy_data,
|
|
|
|
|
/*free_data*/ blender::bke::ntree_free_data,
|
|
|
|
|
/*make_local*/ nullptr,
|
|
|
|
|
/*foreach_id*/ blender::bke::node_foreach_id,
|
|
|
|
|
/*foreach_cache*/ blender::bke::node_foreach_cache,
|
|
|
|
|
/*foreach_path*/ blender::bke::node_foreach_path,
|
|
|
|
|
/*owner_pointer_get*/ blender::bke::node_owner_pointer_get,
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/*blend_write*/ blender::bke::ntree_blend_write,
|
|
|
|
|
/*blend_read_data*/ blender::bke::ntree_blend_read_data,
|
2023-03-11 18:07:59 +01:00
|
|
|
/*blend_read_after_liblink*/ blender::bke::ntree_blend_read_after_liblink,
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/*blend_read_undo_preserve*/ nullptr,
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/*lib_override_apply_post*/ nullptr,
|
|
|
|
|
};
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
namespace blender::bke {
|
2021-12-15 09:51:57 -06:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
static void node_add_sockets_from_type(bNodeTree *ntree, bNode *node, bNodeType *ntype)
|
2021-12-15 09:51:57 -06:00
|
|
|
{
|
2023-10-15 20:28:23 +02:00
|
|
|
if (ntype->declare) {
|
2023-05-16 09:39:42 -04:00
|
|
|
node_verify_sockets(ntree, node, true);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
bNodeSocketTemplate *sockdef;
|
2021-12-15 09:51:57 -06:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
if (ntype->inputs) {
|
|
|
|
|
sockdef = ntype->inputs;
|
|
|
|
|
while (sockdef->type != -1) {
|
|
|
|
|
node_add_socket_from_template(ntree, node, sockdef, SOCK_IN);
|
|
|
|
|
sockdef++;
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
if (ntype->outputs) {
|
|
|
|
|
sockdef = ntype->outputs;
|
|
|
|
|
while (sockdef->type != -1) {
|
|
|
|
|
node_add_socket_from_template(ntree, node, sockdef, SOCK_OUT);
|
|
|
|
|
sockdef++;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2013-03-18 16:34:57 +00:00
|
|
|
}
|
Christmas coding work!
********* Node editor work:
- To enable Nodes for Materials, you have to set the "Use Nodes"
button, in the new Material buttons "Nodes" Panel or in header
of the Node editor. Doing this will disable Material-Layers.
- Nodes now execute materials ("shaders"), but still only using the
previewrender code.
- Nodes have (optional) previews for rendered images.
- Node headers allow to hide buttons and/or preview image
- Nodes can be dragged larger/smaller (right-bottom corner)
- Nodes can be hidden (minimized) with hotkey H
- CTRL+click on an Input Socket gives a popup with default values.
- Changing Material/Texture or Mix node will adjust Node title.
- Click-drag outside of a Node changes cursor to "Knife' and allows to
draw a rect where to cut Links.
- Added new node types RGBtoBW, Texture, In/Output, ColorRamp
- Material Nodes have options to ouput diffuse or specular, or to use
a negative normal. The input socket 'Normal' will force the material
to use that normal, otherwise it uses the normal from the Material
that has the node tree.
- When drawing a link between two not-matching sockets, Blender inserts
a converting node (now only for value/rgb combos)
- When drawing a link to an input socket that's already in use, the
old link will either disappear or flip to another unused socket.
- A click on a Material Node will activate it, and show all its settings
in the Material Buttons. Active Material Nodes draw the material icon
in red.
- A click on any node will show its options in the Node Panel in the
Material buttons.
- Multiple Output Nodes can be used, to sample contents of a tree, but
only one Output is the real one, which is indicated in a different
color and red material icon.
- Added ThemeColors for node types
- ALT+C will convert existing Material-Layers to Node... this currently
only adds the material/mix nodes and connects them. Dunno if this is
worth a lot of coding work to make perfect?
- Press C to call another "Solve order", which will show all possible
cyclic conflicts (if there are).
- Technical: nodes now use "Type" structs which define the
structure of nodes and in/output sockets. The Type structs store all
fixed info, callbacks, and allow to reconstruct saved Nodes to match
what is required by Blender.
- Defining (new) nodes now is as simple as filling in a fixed
Type struct, plus code some callbacks. A doc will be made!
- Node preview images are by default float
********* Icon drawing:
- Cleanup of how old icons were implemented in new system, making
them 16x16 too, correctly centered *and* scaled.
- Made drawing Icons use float coordinates
- Moved BIF_calcpreview_image() into interface_icons.c, renamed it
icon_from_image(). Removed a lot of unneeded Imbuf magic here! :)
- Skipped scaling and imbuf copying when icons are OK size
********* Preview render:
- Huge cleanup of code....
- renaming BIF_xxx calls that only were used internally
- BIF_previewrender() now accepts an argument for rendering method,
so it supports icons, buttonwindow previewrender and node editor
- Only a single BIF_preview_changed() call now exists, supporting all
signals as needed for buttos and node editor
********* More stuff:
- glutil.c, glaDrawPixelsSafe() and glaDrawPixelsTex() now accept format
argument for GL_FLOAT rects
- Made the ColorBand become a built-in button for interface.c
Was a load of cleanup work in buttons_shading.c...
- removed a load of unneeded glBlendFunc() calls
- Fixed bug in calculating text length for buttons (ancient!)
2005-12-28 15:42:51 +00:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/* NOTE: This function is called to initialize node data based on the type.
|
|
|
|
|
* The #bNodeType may not be registered at creation time of the node,
|
|
|
|
|
* so this can be delayed until the node type gets registered.
|
|
|
|
|
*/
|
|
|
|
|
static void node_init(const bContext *C, bNodeTree *ntree, bNode *node)
|
2008-02-09 23:17:15 +00:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
BLI_assert(ntree != nullptr);
|
|
|
|
|
bNodeType *ntype = node->typeinfo;
|
2024-05-13 16:07:12 +02:00
|
|
|
if (ntype == &NodeTypeUndefined) {
|
2023-05-16 09:39:42 -04:00
|
|
|
return;
|
|
|
|
|
}
|
2018-06-17 17:05:51 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/* only do this once */
|
|
|
|
|
if (node->flag & NODE_INIT) {
|
|
|
|
|
return;
|
2013-03-18 16:34:57 +00:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
|
|
|
|
|
node->flag = NODE_SELECT | NODE_OPTIONS | ntype->flag;
|
|
|
|
|
node->width = ntype->width;
|
|
|
|
|
node->height = ntype->height;
|
|
|
|
|
node->color[0] = node->color[1] = node->color[2] = 0.608; /* default theme color */
|
|
|
|
|
/* initialize the node name with the node label.
|
|
|
|
|
* NOTE: do this after the initfunc so nodes get their data set which may be used in naming
|
|
|
|
|
* (node groups for example) */
|
|
|
|
|
/* XXX Do not use nodeLabel() here, it returns translated content for UI,
|
|
|
|
|
* which should *only* be used in UI, *never* in data...
|
|
|
|
|
* Data have their own translation option!
|
|
|
|
|
* This solution may be a bit rougher than nodeLabel()'s returned string, but it's simpler
|
|
|
|
|
* than adding "do_translate" flags to this func (and labelfunc() as well). */
|
|
|
|
|
STRNCPY_UTF8(node->name, DATA_(ntype->ui_name));
|
|
|
|
|
nodeUniqueName(ntree, node);
|
|
|
|
|
|
|
|
|
|
/* Generally sockets should be added after the initialization, because the set of sockets might
|
|
|
|
|
* depend on node properties. */
|
|
|
|
|
const bool add_sockets_before_init = node->type == CMP_NODE_R_LAYERS;
|
|
|
|
|
if (add_sockets_before_init) {
|
|
|
|
|
node_add_sockets_from_type(ntree, node, ntype);
|
2013-03-18 16:34:57 +00:00
|
|
|
}
|
2018-06-17 17:05:51 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
if (ntype->initfunc != nullptr) {
|
|
|
|
|
ntype->initfunc(ntree, node);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2018-06-17 17:05:51 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
if (ntree->typeinfo && ntree->typeinfo->node_add_init) {
|
|
|
|
|
ntree->typeinfo->node_add_init(ntree, node);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2018-06-17 17:05:51 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
if (!add_sockets_before_init) {
|
|
|
|
|
node_add_sockets_from_type(ntree, node, ntype);
|
|
|
|
|
}
|
2018-06-17 17:05:51 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
if (node->id) {
|
|
|
|
|
id_us_plus(node->id);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2018-06-17 17:05:51 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
if (ntype->initfunc_api) {
|
2023-09-06 00:48:50 +02:00
|
|
|
PointerRNA ptr = RNA_pointer_create(&ntree->id, &RNA_Node, node);
|
2023-05-16 09:39:42 -04:00
|
|
|
|
|
|
|
|
/* XXX WARNING: context can be nullptr in case nodes are added in do_versions.
|
|
|
|
|
* Delayed init is not supported for nodes with context-based `initfunc_api` at the moment. */
|
|
|
|
|
BLI_assert(C != nullptr);
|
|
|
|
|
ntype->initfunc_api(C, &ptr);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
node->flag |= NODE_INIT;
|
2008-02-09 23:17:15 +00:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
static void ntree_set_typeinfo(bNodeTree *ntree, bNodeTreeType *typeinfo)
|
2020-04-20 13:22:20 +02:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
if (typeinfo) {
|
|
|
|
|
ntree->typeinfo = typeinfo;
|
|
|
|
|
}
|
|
|
|
|
else {
|
2024-05-13 16:07:12 +02:00
|
|
|
ntree->typeinfo = &NodeTreeTypeUndefined;
|
2020-04-20 13:22:20 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/* Deprecated integer type. */
|
|
|
|
|
ntree->type = ntree->typeinfo->type;
|
|
|
|
|
BKE_ntree_update_tag_all(ntree);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
static void node_set_typeinfo(const bContext *C,
|
|
|
|
|
bNodeTree *ntree,
|
|
|
|
|
bNode *node,
|
|
|
|
|
bNodeType *typeinfo)
|
2023-05-15 15:14:22 +02:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
/* for nodes saved in older versions storage can get lost, make undefined then */
|
|
|
|
|
if (node->flag & NODE_INIT) {
|
|
|
|
|
if (typeinfo && typeinfo->storagename[0] && !node->storage) {
|
|
|
|
|
typeinfo = nullptr;
|
2021-05-12 12:41:21 +02:00
|
|
|
}
|
2020-04-20 13:22:20 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
if (typeinfo) {
|
|
|
|
|
node->typeinfo = typeinfo;
|
2018-01-20 01:04:07 +01:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/* deprecated integer type */
|
|
|
|
|
node->type = typeinfo->type;
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/* initialize the node if necessary */
|
|
|
|
|
node_init(C, ntree, node);
|
|
|
|
|
}
|
|
|
|
|
else {
|
2024-05-13 16:07:12 +02:00
|
|
|
node->typeinfo = &NodeTypeUndefined;
|
2023-05-16 09:39:42 -04:00
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/* WARNING: default_value must either be null or match the typeinfo at this point.
|
|
|
|
|
* This function is called both for initializing new sockets and after loading files.
|
|
|
|
|
*/
|
|
|
|
|
static void node_socket_set_typeinfo(bNodeTree *ntree,
|
|
|
|
|
bNodeSocket *sock,
|
|
|
|
|
bNodeSocketType *typeinfo)
|
2023-05-15 15:14:22 +02:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
if (typeinfo) {
|
|
|
|
|
sock->typeinfo = typeinfo;
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/* deprecated integer type */
|
|
|
|
|
sock->type = typeinfo->type;
|
2018-01-20 01:04:07 +01:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
if (sock->default_value == nullptr) {
|
|
|
|
|
/* initialize the default_value pointer used by standard socket types */
|
|
|
|
|
node_socket_init_default_value(sock);
|
|
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
else {
|
2024-05-13 16:07:12 +02:00
|
|
|
sock->typeinfo = &NodeSocketTypeUndefined;
|
2018-01-20 01:04:07 +01:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
BKE_ntree_update_tag_socket_type(ntree, sock);
|
2021-07-06 18:36:11 +01:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/* Set specific typeinfo pointers in all node trees on register/unregister */
|
|
|
|
|
static void update_typeinfo(Main *bmain,
|
|
|
|
|
const bContext *C,
|
|
|
|
|
bNodeTreeType *treetype,
|
|
|
|
|
bNodeType *nodetype,
|
|
|
|
|
bNodeSocketType *socktype,
|
|
|
|
|
const bool unregister)
|
2021-07-06 18:36:11 +01:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
if (!bmain) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
|
|
|
|
|
if (treetype && STREQ(ntree->idname, treetype->idname)) {
|
|
|
|
|
ntree_set_typeinfo(ntree, unregister ? nullptr : treetype);
|
|
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/* initialize nodes */
|
|
|
|
|
for (bNode *node : ntree->all_nodes()) {
|
|
|
|
|
if (nodetype && STREQ(node->idname, nodetype->idname)) {
|
|
|
|
|
node_set_typeinfo(C, ntree, node, unregister ? nullptr : nodetype);
|
|
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/* initialize node sockets */
|
|
|
|
|
LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
|
|
|
|
|
if (socktype && STREQ(sock->idname, socktype->idname)) {
|
|
|
|
|
node_socket_set_typeinfo(ntree, sock, unregister ? nullptr : socktype);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
|
|
|
|
|
if (socktype && STREQ(sock->idname, socktype->idname)) {
|
|
|
|
|
node_socket_set_typeinfo(ntree, sock, unregister ? nullptr : socktype);
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
FOREACH_NODETREE_END;
|
2018-01-20 01:04:07 +01:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void ntreeSetTypes(const bContext *C, bNodeTree *ntree)
|
Christmas coding work!
********* Node editor work:
- To enable Nodes for Materials, you have to set the "Use Nodes"
button, in the new Material buttons "Nodes" Panel or in header
of the Node editor. Doing this will disable Material-Layers.
- Nodes now execute materials ("shaders"), but still only using the
previewrender code.
- Nodes have (optional) previews for rendered images.
- Node headers allow to hide buttons and/or preview image
- Nodes can be dragged larger/smaller (right-bottom corner)
- Nodes can be hidden (minimized) with hotkey H
- CTRL+click on an Input Socket gives a popup with default values.
- Changing Material/Texture or Mix node will adjust Node title.
- Click-drag outside of a Node changes cursor to "Knife' and allows to
draw a rect where to cut Links.
- Added new node types RGBtoBW, Texture, In/Output, ColorRamp
- Material Nodes have options to ouput diffuse or specular, or to use
a negative normal. The input socket 'Normal' will force the material
to use that normal, otherwise it uses the normal from the Material
that has the node tree.
- When drawing a link between two not-matching sockets, Blender inserts
a converting node (now only for value/rgb combos)
- When drawing a link to an input socket that's already in use, the
old link will either disappear or flip to another unused socket.
- A click on a Material Node will activate it, and show all its settings
in the Material Buttons. Active Material Nodes draw the material icon
in red.
- A click on any node will show its options in the Node Panel in the
Material buttons.
- Multiple Output Nodes can be used, to sample contents of a tree, but
only one Output is the real one, which is indicated in a different
color and red material icon.
- Added ThemeColors for node types
- ALT+C will convert existing Material-Layers to Node... this currently
only adds the material/mix nodes and connects them. Dunno if this is
worth a lot of coding work to make perfect?
- Press C to call another "Solve order", which will show all possible
cyclic conflicts (if there are).
- Technical: nodes now use "Type" structs which define the
structure of nodes and in/output sockets. The Type structs store all
fixed info, callbacks, and allow to reconstruct saved Nodes to match
what is required by Blender.
- Defining (new) nodes now is as simple as filling in a fixed
Type struct, plus code some callbacks. A doc will be made!
- Node preview images are by default float
********* Icon drawing:
- Cleanup of how old icons were implemented in new system, making
them 16x16 too, correctly centered *and* scaled.
- Made drawing Icons use float coordinates
- Moved BIF_calcpreview_image() into interface_icons.c, renamed it
icon_from_image(). Removed a lot of unneeded Imbuf magic here! :)
- Skipped scaling and imbuf copying when icons are OK size
********* Preview render:
- Huge cleanup of code....
- renaming BIF_xxx calls that only were used internally
- BIF_previewrender() now accepts an argument for rendering method,
so it supports icons, buttonwindow previewrender and node editor
- Only a single BIF_preview_changed() call now exists, supporting all
signals as needed for buttos and node editor
********* More stuff:
- glutil.c, glaDrawPixelsSafe() and glaDrawPixelsTex() now accept format
argument for GL_FLOAT rects
- Made the ColorBand become a built-in button for interface.c
Was a load of cleanup work in buttons_shading.c...
- removed a load of unneeded glBlendFunc() calls
- Fixed bug in calculating text length for buttons (ancient!)
2005-12-28 15:42:51 +00:00
|
|
|
{
|
2024-05-13 16:07:12 +02:00
|
|
|
ntree_set_typeinfo(ntree, ntreeTypeFind(ntree->idname));
|
2023-05-16 09:39:42 -04:00
|
|
|
|
2023-05-15 15:14:22 +02:00
|
|
|
for (bNode *node : ntree->all_nodes()) {
|
2023-09-05 12:37:05 +02:00
|
|
|
/* Set socket typeinfo first because node initialization may rely on socket typeinfo for
|
|
|
|
|
* generating declarations. */
|
2023-05-16 09:39:42 -04:00
|
|
|
LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
|
2024-05-13 16:07:12 +02:00
|
|
|
node_socket_set_typeinfo(ntree, sock, nodeSocketTypeFind(sock->idname));
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
|
2024-05-13 16:07:12 +02:00
|
|
|
node_socket_set_typeinfo(ntree, sock, nodeSocketTypeFind(sock->idname));
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2023-09-05 12:37:05 +02:00
|
|
|
|
2024-05-13 16:07:12 +02:00
|
|
|
node_set_typeinfo(C, ntree, node, nodeTypeFind(node->idname));
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
Christmas coding work!
********* Node editor work:
- To enable Nodes for Materials, you have to set the "Use Nodes"
button, in the new Material buttons "Nodes" Panel or in header
of the Node editor. Doing this will disable Material-Layers.
- Nodes now execute materials ("shaders"), but still only using the
previewrender code.
- Nodes have (optional) previews for rendered images.
- Node headers allow to hide buttons and/or preview image
- Nodes can be dragged larger/smaller (right-bottom corner)
- Nodes can be hidden (minimized) with hotkey H
- CTRL+click on an Input Socket gives a popup with default values.
- Changing Material/Texture or Mix node will adjust Node title.
- Click-drag outside of a Node changes cursor to "Knife' and allows to
draw a rect where to cut Links.
- Added new node types RGBtoBW, Texture, In/Output, ColorRamp
- Material Nodes have options to ouput diffuse or specular, or to use
a negative normal. The input socket 'Normal' will force the material
to use that normal, otherwise it uses the normal from the Material
that has the node tree.
- When drawing a link between two not-matching sockets, Blender inserts
a converting node (now only for value/rgb combos)
- When drawing a link to an input socket that's already in use, the
old link will either disappear or flip to another unused socket.
- A click on a Material Node will activate it, and show all its settings
in the Material Buttons. Active Material Nodes draw the material icon
in red.
- A click on any node will show its options in the Node Panel in the
Material buttons.
- Multiple Output Nodes can be used, to sample contents of a tree, but
only one Output is the real one, which is indicated in a different
color and red material icon.
- Added ThemeColors for node types
- ALT+C will convert existing Material-Layers to Node... this currently
only adds the material/mix nodes and connects them. Dunno if this is
worth a lot of coding work to make perfect?
- Press C to call another "Solve order", which will show all possible
cyclic conflicts (if there are).
- Technical: nodes now use "Type" structs which define the
structure of nodes and in/output sockets. The Type structs store all
fixed info, callbacks, and allow to reconstruct saved Nodes to match
what is required by Blender.
- Defining (new) nodes now is as simple as filling in a fixed
Type struct, plus code some callbacks. A doc will be made!
- Node preview images are by default float
********* Icon drawing:
- Cleanup of how old icons were implemented in new system, making
them 16x16 too, correctly centered *and* scaled.
- Made drawing Icons use float coordinates
- Moved BIF_calcpreview_image() into interface_icons.c, renamed it
icon_from_image(). Removed a lot of unneeded Imbuf magic here! :)
- Skipped scaling and imbuf copying when icons are OK size
********* Preview render:
- Huge cleanup of code....
- renaming BIF_xxx calls that only were used internally
- BIF_previewrender() now accepts an argument for rendering method,
so it supports icons, buttonwindow previewrender and node editor
- Only a single BIF_preview_changed() call now exists, supporting all
signals as needed for buttos and node editor
********* More stuff:
- glutil.c, glaDrawPixelsSafe() and glaDrawPixelsTex() now accept format
argument for GL_FLOAT rects
- Made the ColorBand become a built-in button for interface.c
Was a load of cleanup work in buttons_shading.c...
- removed a load of unneeded glBlendFunc() calls
- Fixed bug in calculating text length for buttons (ancient!)
2005-12-28 15:42:51 +00:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
static GHash *nodetreetypes_hash = nullptr;
|
|
|
|
|
static GHash *nodetypes_hash = nullptr;
|
Cycles: Merge Anisotropic BSDF node into Glossy BSDF node
Used to be https://archive.blender.org/developer/D17123.
Internally these are already using the same code path anyways, there's no point in maintaining two distinct nodes.
The obvious approach would be to add Anisotropy controls to the Glossy BSDF node and remove the Anisotropic BSDF node. However, that would break forward compability, since older Blender versions don't know how to handle the Anisotropy input on the Glossy BSDF node.
Therefore, this commit technically removes the Glossy BSDF node, uses versioning to replace them with an Anisotropic BSDF node, and renames that node to "Glossy BSDF".
That way, when you open a new file in an older version, all the nodes show up as Anisotropic BSDF nodes and render correctly.
This is a bit ugly internally since we need to preserve the old `idname` which now no longer matches the UI name, but that's not too bad.
Also removes the "Sharp" distribution option and replaces it with GGX, sets Roughness to zero and disconnects any input to the Roughness socket.
Pull Request: https://projects.blender.org/blender/blender/pulls/104445
2023-05-18 23:12:20 +02:00
|
|
|
static GHash *nodetypes_alias_hash = nullptr;
|
2023-05-16 09:39:42 -04:00
|
|
|
static GHash *nodesockettypes_hash = nullptr;
|
|
|
|
|
|
|
|
|
|
bNodeTreeType *ntreeTypeFind(const char *idname)
|
2021-07-06 18:36:11 +01:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
if (idname[0]) {
|
2024-05-13 16:07:12 +02:00
|
|
|
bNodeTreeType *nt = static_cast<bNodeTreeType *>(BLI_ghash_lookup(nodetreetypes_hash, idname));
|
2023-05-16 09:39:42 -04:00
|
|
|
if (nt) {
|
|
|
|
|
return nt;
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
|
|
|
|
|
return nullptr;
|
2021-07-06 18:36:11 +01:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void ntreeTypeAdd(bNodeTreeType *nt)
|
2013-03-18 16:34:57 +00:00
|
|
|
{
|
2024-05-13 16:07:12 +02:00
|
|
|
BLI_ghash_insert(nodetreetypes_hash, nt->idname, nt);
|
2023-05-16 09:39:42 -04:00
|
|
|
/* XXX pass Main to register function? */
|
|
|
|
|
/* Probably not. It is pretty much expected we want to update G_MAIN here I think -
|
|
|
|
|
* or we'd want to update *all* active Mains, which we cannot do anyway currently. */
|
2024-05-13 16:07:12 +02:00
|
|
|
update_typeinfo(G_MAIN, nullptr, nt, nullptr, nullptr, false);
|
2023-05-16 09:39:42 -04:00
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
static void ntree_free_type(void *treetype_v)
|
|
|
|
|
{
|
|
|
|
|
bNodeTreeType *treetype = static_cast<bNodeTreeType *>(treetype_v);
|
|
|
|
|
/* XXX pass Main to unregister function? */
|
|
|
|
|
/* Probably not. It is pretty much expected we want to update G_MAIN here I think -
|
|
|
|
|
* or we'd want to update *all* active Mains, which we cannot do anyway currently. */
|
2024-05-13 16:07:12 +02:00
|
|
|
update_typeinfo(G_MAIN, nullptr, treetype, nullptr, nullptr, true);
|
2023-05-16 09:39:42 -04:00
|
|
|
MEM_freeN(treetype);
|
|
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void ntreeTypeFreeLink(const bNodeTreeType *nt)
|
|
|
|
|
{
|
2024-05-13 16:07:12 +02:00
|
|
|
BLI_ghash_remove(nodetreetypes_hash, nt->idname, nullptr, ntree_free_type);
|
2023-05-16 09:39:42 -04:00
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
bool ntreeIsRegistered(const bNodeTree *ntree)
|
|
|
|
|
{
|
2024-05-13 16:07:12 +02:00
|
|
|
return (ntree->typeinfo != &NodeTreeTypeUndefined);
|
2023-05-16 09:39:42 -04:00
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
GHashIterator *ntreeTypeGetIterator()
|
|
|
|
|
{
|
2024-05-13 16:07:12 +02:00
|
|
|
return BLI_ghashIterator_new(nodetreetypes_hash);
|
2023-05-16 09:39:42 -04:00
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
bNodeType *nodeTypeFind(const char *idname)
|
|
|
|
|
{
|
|
|
|
|
if (idname[0]) {
|
2024-05-13 16:07:12 +02:00
|
|
|
bNodeType *nt = static_cast<bNodeType *>(BLI_ghash_lookup(nodetypes_hash, idname));
|
2023-05-16 09:39:42 -04:00
|
|
|
if (nt) {
|
|
|
|
|
return nt;
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
return nullptr;
|
|
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
|
Cycles: Merge Anisotropic BSDF node into Glossy BSDF node
Used to be https://archive.blender.org/developer/D17123.
Internally these are already using the same code path anyways, there's no point in maintaining two distinct nodes.
The obvious approach would be to add Anisotropy controls to the Glossy BSDF node and remove the Anisotropic BSDF node. However, that would break forward compability, since older Blender versions don't know how to handle the Anisotropy input on the Glossy BSDF node.
Therefore, this commit technically removes the Glossy BSDF node, uses versioning to replace them with an Anisotropic BSDF node, and renames that node to "Glossy BSDF".
That way, when you open a new file in an older version, all the nodes show up as Anisotropic BSDF nodes and render correctly.
This is a bit ugly internally since we need to preserve the old `idname` which now no longer matches the UI name, but that's not too bad.
Also removes the "Sharp" distribution option and replaces it with GGX, sets Roughness to zero and disconnects any input to the Roughness socket.
Pull Request: https://projects.blender.org/blender/blender/pulls/104445
2023-05-18 23:12:20 +02:00
|
|
|
const char *nodeTypeFindAlias(const char *alias)
|
|
|
|
|
{
|
|
|
|
|
if (alias[0]) {
|
2024-05-13 16:07:12 +02:00
|
|
|
const char *idname = static_cast<const char *>(BLI_ghash_lookup(nodetypes_alias_hash, alias));
|
Cycles: Merge Anisotropic BSDF node into Glossy BSDF node
Used to be https://archive.blender.org/developer/D17123.
Internally these are already using the same code path anyways, there's no point in maintaining two distinct nodes.
The obvious approach would be to add Anisotropy controls to the Glossy BSDF node and remove the Anisotropic BSDF node. However, that would break forward compability, since older Blender versions don't know how to handle the Anisotropy input on the Glossy BSDF node.
Therefore, this commit technically removes the Glossy BSDF node, uses versioning to replace them with an Anisotropic BSDF node, and renames that node to "Glossy BSDF".
That way, when you open a new file in an older version, all the nodes show up as Anisotropic BSDF nodes and render correctly.
This is a bit ugly internally since we need to preserve the old `idname` which now no longer matches the UI name, but that's not too bad.
Also removes the "Sharp" distribution option and replaces it with GGX, sets Roughness to zero and disconnects any input to the Roughness socket.
Pull Request: https://projects.blender.org/blender/blender/pulls/104445
2023-05-18 23:12:20 +02:00
|
|
|
if (idname) {
|
|
|
|
|
return idname;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return alias;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
static void node_free_type(void *nodetype_v)
|
|
|
|
|
{
|
|
|
|
|
bNodeType *nodetype = static_cast<bNodeType *>(nodetype_v);
|
|
|
|
|
/* XXX pass Main to unregister function? */
|
|
|
|
|
/* Probably not. It is pretty much expected we want to update G_MAIN here I think -
|
|
|
|
|
* or we'd want to update *all* active Mains, which we cannot do anyway currently. */
|
2024-05-13 16:07:12 +02:00
|
|
|
update_typeinfo(G_MAIN, nullptr, nullptr, nodetype, nullptr, true);
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-10-15 20:28:23 +02:00
|
|
|
delete nodetype->static_declaration;
|
|
|
|
|
nodetype->static_declaration = nullptr;
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/* Can be null when the type is not dynamically allocated. */
|
|
|
|
|
if (nodetype->free_self) {
|
|
|
|
|
nodetype->free_self(nodetype);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void nodeRegisterType(bNodeType *nt)
|
2023-05-15 15:14:22 +02:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
/* debug only: basic verification of registered types */
|
|
|
|
|
BLI_assert(nt->idname[0] != '\0');
|
|
|
|
|
BLI_assert(nt->poll != nullptr);
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-10-15 20:28:23 +02:00
|
|
|
if (nt->declare) {
|
2024-05-13 16:07:12 +02:00
|
|
|
nt->static_declaration = new nodes::NodeDeclaration();
|
|
|
|
|
nodes::build_node_declaration(*nt, *nt->static_declaration, nullptr, nullptr);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
|
2024-05-13 16:07:12 +02:00
|
|
|
BLI_ghash_insert(nodetypes_hash, nt->idname, nt);
|
2023-05-16 09:39:42 -04:00
|
|
|
/* XXX pass Main to register function? */
|
|
|
|
|
/* Probably not. It is pretty much expected we want to update G_MAIN here I think -
|
|
|
|
|
* or we'd want to update *all* active Mains, which we cannot do anyway currently. */
|
2024-05-13 16:07:12 +02:00
|
|
|
update_typeinfo(G_MAIN, nullptr, nullptr, nt, nullptr, false);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void nodeUnregisterType(bNodeType *nt)
|
2023-05-15 15:14:22 +02:00
|
|
|
{
|
2024-05-13 16:07:12 +02:00
|
|
|
BLI_ghash_remove(nodetypes_hash, nt->idname, nullptr, node_free_type);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
|
Cycles: Merge Anisotropic BSDF node into Glossy BSDF node
Used to be https://archive.blender.org/developer/D17123.
Internally these are already using the same code path anyways, there's no point in maintaining two distinct nodes.
The obvious approach would be to add Anisotropy controls to the Glossy BSDF node and remove the Anisotropic BSDF node. However, that would break forward compability, since older Blender versions don't know how to handle the Anisotropy input on the Glossy BSDF node.
Therefore, this commit technically removes the Glossy BSDF node, uses versioning to replace them with an Anisotropic BSDF node, and renames that node to "Glossy BSDF".
That way, when you open a new file in an older version, all the nodes show up as Anisotropic BSDF nodes and render correctly.
This is a bit ugly internally since we need to preserve the old `idname` which now no longer matches the UI name, but that's not too bad.
Also removes the "Sharp" distribution option and replaces it with GGX, sets Roughness to zero and disconnects any input to the Roughness socket.
Pull Request: https://projects.blender.org/blender/blender/pulls/104445
2023-05-18 23:12:20 +02:00
|
|
|
void nodeRegisterAlias(bNodeType *nt, const char *alias)
|
|
|
|
|
{
|
2024-05-13 16:07:12 +02:00
|
|
|
BLI_ghash_insert(nodetypes_alias_hash, BLI_strdup(alias), BLI_strdup(nt->idname));
|
Cycles: Merge Anisotropic BSDF node into Glossy BSDF node
Used to be https://archive.blender.org/developer/D17123.
Internally these are already using the same code path anyways, there's no point in maintaining two distinct nodes.
The obvious approach would be to add Anisotropy controls to the Glossy BSDF node and remove the Anisotropic BSDF node. However, that would break forward compability, since older Blender versions don't know how to handle the Anisotropy input on the Glossy BSDF node.
Therefore, this commit technically removes the Glossy BSDF node, uses versioning to replace them with an Anisotropic BSDF node, and renames that node to "Glossy BSDF".
That way, when you open a new file in an older version, all the nodes show up as Anisotropic BSDF nodes and render correctly.
This is a bit ugly internally since we need to preserve the old `idname` which now no longer matches the UI name, but that's not too bad.
Also removes the "Sharp" distribution option and replaces it with GGX, sets Roughness to zero and disconnects any input to the Roughness socket.
Pull Request: https://projects.blender.org/blender/blender/pulls/104445
2023-05-18 23:12:20 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
bool node_type_is_undefined(const bNode *node)
|
2023-05-15 15:14:22 +02:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
if (node->typeinfo == &NodeTypeUndefined) {
|
|
|
|
|
return true;
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
|
|
|
|
|
if (node->is_group()) {
|
|
|
|
|
const ID *group_tree = node->id;
|
|
|
|
|
if (group_tree == nullptr) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (!ID_IS_LINKED(group_tree)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if ((group_tree->tag & LIB_TAG_MISSING) == 0) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
return false;
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
GHashIterator *nodeTypeGetIterator()
|
|
|
|
|
{
|
2024-05-13 16:07:12 +02:00
|
|
|
return BLI_ghashIterator_new(nodetypes_hash);
|
2023-05-16 09:39:42 -04:00
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
bNodeSocketType *nodeSocketTypeFind(const char *idname)
|
|
|
|
|
{
|
2023-08-30 12:37:21 +02:00
|
|
|
if (idname && idname[0]) {
|
2023-05-16 09:39:42 -04:00
|
|
|
bNodeSocketType *st = static_cast<bNodeSocketType *>(
|
2024-05-13 16:07:12 +02:00
|
|
|
BLI_ghash_lookup(nodesockettypes_hash, idname));
|
2023-05-16 09:39:42 -04:00
|
|
|
if (st) {
|
|
|
|
|
return st;
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
return nullptr;
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
static void node_free_socket_type(void *socktype_v)
|
2023-05-15 15:14:22 +02:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
bNodeSocketType *socktype = static_cast<bNodeSocketType *>(socktype_v);
|
|
|
|
|
/* XXX pass Main to unregister function? */
|
|
|
|
|
/* Probably not. It is pretty much expected we want to update G_MAIN here I think -
|
|
|
|
|
* or we'd want to update *all* active Mains, which we cannot do anyway currently. */
|
2024-05-13 16:07:12 +02:00
|
|
|
update_typeinfo(G_MAIN, nullptr, nullptr, nullptr, socktype, true);
|
2023-05-16 09:39:42 -04:00
|
|
|
|
|
|
|
|
socktype->free_self(socktype);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void nodeRegisterSocketType(bNodeSocketType *st)
|
2023-05-15 15:14:22 +02:00
|
|
|
{
|
2024-05-13 16:07:12 +02:00
|
|
|
BLI_ghash_insert(nodesockettypes_hash, st->idname, st);
|
2023-05-16 09:39:42 -04:00
|
|
|
/* XXX pass Main to register function? */
|
|
|
|
|
/* Probably not. It is pretty much expected we want to update G_MAIN here I think -
|
|
|
|
|
* or we'd want to update *all* active Mains, which we cannot do anyway currently. */
|
2024-05-13 16:07:12 +02:00
|
|
|
update_typeinfo(G_MAIN, nullptr, nullptr, nullptr, st, false);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void nodeUnregisterSocketType(bNodeSocketType *st)
|
2023-05-15 15:14:22 +02:00
|
|
|
{
|
2024-05-13 16:07:12 +02:00
|
|
|
BLI_ghash_remove(nodesockettypes_hash, st->idname, nullptr, node_free_socket_type);
|
2023-05-16 09:39:42 -04:00
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
bool nodeSocketIsRegistered(const bNodeSocket *sock)
|
|
|
|
|
{
|
2024-05-13 16:07:12 +02:00
|
|
|
return (sock->typeinfo != &NodeSocketTypeUndefined);
|
2023-05-16 09:39:42 -04:00
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
GHashIterator *nodeSocketTypeGetIterator()
|
|
|
|
|
{
|
2024-05-13 16:07:12 +02:00
|
|
|
return BLI_ghashIterator_new(nodesockettypes_hash);
|
2023-05-16 09:39:42 -04:00
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
const char *nodeSocketTypeLabel(const bNodeSocketType *stype)
|
|
|
|
|
{
|
|
|
|
|
/* Use socket type name as a fallback if label is undefined. */
|
|
|
|
|
if (stype->label[0] == '\0') {
|
|
|
|
|
return RNA_struct_ui_name(stype->ext_socket.srna);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
return stype->label;
|
|
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
const char *nodeSocketSubTypeLabel(int subtype)
|
|
|
|
|
{
|
|
|
|
|
const char *name;
|
|
|
|
|
if (RNA_enum_name(rna_enum_property_subtype_items, subtype, &name)) {
|
|
|
|
|
return name;
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
return "";
|
|
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2024-05-20 11:16:34 -04:00
|
|
|
bNodeSocket *nodeFindSocket(bNode *node, const eNodeSocketInOut in_out, const StringRef identifier)
|
2023-05-16 09:39:42 -04:00
|
|
|
{
|
|
|
|
|
const ListBase *sockets = (in_out == SOCK_IN) ? &node->inputs : &node->outputs;
|
|
|
|
|
LISTBASE_FOREACH (bNodeSocket *, sock, sockets) {
|
2024-05-20 11:08:19 -04:00
|
|
|
if (sock->identifier == identifier) {
|
2023-05-16 09:39:42 -04:00
|
|
|
return sock;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nullptr;
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
|
2024-05-20 11:16:34 -04:00
|
|
|
const bNodeSocket *nodeFindSocket(const bNode *node,
|
|
|
|
|
const eNodeSocketInOut in_out,
|
|
|
|
|
const StringRef identifier)
|
|
|
|
|
{
|
|
|
|
|
/* Reuse the implementation of the mutable accessor. */
|
|
|
|
|
return nodeFindSocket(const_cast<bNode *>(node), in_out, identifier);
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
bNodeSocket *node_find_enabled_socket(bNode &node,
|
|
|
|
|
const eNodeSocketInOut in_out,
|
|
|
|
|
const StringRef name)
|
2023-05-15 15:14:22 +02:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
ListBase *sockets = (in_out == SOCK_IN) ? &node.inputs : &node.outputs;
|
|
|
|
|
LISTBASE_FOREACH (bNodeSocket *, socket, sockets) {
|
|
|
|
|
if (socket->is_available() && socket->name == name) {
|
|
|
|
|
return socket;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
bNodeSocket *node_find_enabled_input_socket(bNode &node, const StringRef name)
|
|
|
|
|
{
|
|
|
|
|
return node_find_enabled_socket(node, SOCK_IN, name);
|
|
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
bNodeSocket *node_find_enabled_output_socket(bNode &node, const StringRef name)
|
|
|
|
|
{
|
|
|
|
|
return node_find_enabled_socket(node, SOCK_OUT, name);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
static bool unique_identifier_check(void *arg, const char *identifier)
|
2023-05-15 15:14:22 +02:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
const ListBase *lb = static_cast<const ListBase *>(arg);
|
|
|
|
|
LISTBASE_FOREACH (bNodeSocket *, sock, lb) {
|
|
|
|
|
if (STREQ(sock->identifier, identifier)) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
return false;
|
|
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
static bNodeSocket *make_socket(bNodeTree *ntree,
|
|
|
|
|
bNode * /*node*/,
|
|
|
|
|
const int in_out,
|
|
|
|
|
ListBase *lb,
|
|
|
|
|
const char *idname,
|
|
|
|
|
const char *identifier,
|
|
|
|
|
const char *name)
|
|
|
|
|
{
|
|
|
|
|
char auto_identifier[MAX_NAME];
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
if (identifier && identifier[0] != '\0') {
|
|
|
|
|
/* use explicit identifier */
|
|
|
|
|
STRNCPY(auto_identifier, identifier);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
else {
|
2023-05-16 09:39:42 -04:00
|
|
|
/* if no explicit identifier is given, assign a unique identifier based on the name */
|
|
|
|
|
STRNCPY(auto_identifier, name);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
/* Make the identifier unique. */
|
|
|
|
|
BLI_uniquename_cb(
|
|
|
|
|
unique_identifier_check, lb, "socket", '_', auto_identifier, sizeof(auto_identifier));
|
|
|
|
|
|
|
|
|
|
bNodeSocket *sock = MEM_cnew<bNodeSocket>("sock");
|
|
|
|
|
sock->runtime = MEM_new<bNodeSocketRuntime>(__func__);
|
|
|
|
|
sock->in_out = in_out;
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
STRNCPY(sock->identifier, auto_identifier);
|
2023-05-15 15:14:22 +02:00
|
|
|
sock->limit = (in_out == SOCK_IN ? 1 : 0xFFF);
|
|
|
|
|
|
|
|
|
|
STRNCPY(sock->name, name);
|
|
|
|
|
sock->storage = nullptr;
|
|
|
|
|
sock->flag |= SOCK_COLLAPSED;
|
2023-05-16 09:39:42 -04:00
|
|
|
sock->type = SOCK_CUSTOM; /* int type undefined by default */
|
|
|
|
|
|
|
|
|
|
STRNCPY(sock->idname, idname);
|
|
|
|
|
node_socket_set_typeinfo(ntree, sock, nodeSocketTypeFind(idname));
|
2023-05-15 15:14:22 +02:00
|
|
|
|
|
|
|
|
return sock;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
static void socket_id_user_increment(bNodeSocket *sock)
|
2023-05-15 15:14:22 +02:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
switch (eNodeSocketDatatype(sock->type)) {
|
|
|
|
|
case SOCK_OBJECT: {
|
|
|
|
|
bNodeSocketValueObject &default_value = *sock->default_value_typed<bNodeSocketValueObject>();
|
|
|
|
|
id_us_plus(reinterpret_cast<ID *>(default_value.value));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case SOCK_IMAGE: {
|
|
|
|
|
bNodeSocketValueImage &default_value = *sock->default_value_typed<bNodeSocketValueImage>();
|
|
|
|
|
id_us_plus(reinterpret_cast<ID *>(default_value.value));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case SOCK_COLLECTION: {
|
|
|
|
|
bNodeSocketValueCollection &default_value =
|
|
|
|
|
*sock->default_value_typed<bNodeSocketValueCollection>();
|
|
|
|
|
id_us_plus(reinterpret_cast<ID *>(default_value.value));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case SOCK_TEXTURE: {
|
|
|
|
|
bNodeSocketValueTexture &default_value =
|
|
|
|
|
*sock->default_value_typed<bNodeSocketValueTexture>();
|
|
|
|
|
id_us_plus(reinterpret_cast<ID *>(default_value.value));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case SOCK_MATERIAL: {
|
|
|
|
|
bNodeSocketValueMaterial &default_value =
|
|
|
|
|
*sock->default_value_typed<bNodeSocketValueMaterial>();
|
|
|
|
|
id_us_plus(reinterpret_cast<ID *>(default_value.value));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case SOCK_FLOAT:
|
|
|
|
|
case SOCK_VECTOR:
|
|
|
|
|
case SOCK_RGBA:
|
|
|
|
|
case SOCK_BOOLEAN:
|
2023-06-14 20:30:10 +02:00
|
|
|
case SOCK_ROTATION:
|
2024-02-13 18:59:36 +01:00
|
|
|
case SOCK_MATRIX:
|
2023-05-16 09:39:42 -04:00
|
|
|
case SOCK_INT:
|
|
|
|
|
case SOCK_STRING:
|
2024-01-26 12:40:01 +01:00
|
|
|
case SOCK_MENU:
|
2023-05-16 09:39:42 -04:00
|
|
|
case SOCK_CUSTOM:
|
|
|
|
|
case SOCK_SHADER:
|
|
|
|
|
case SOCK_GEOMETRY:
|
|
|
|
|
break;
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/** \return True if the socket had an ID default value. */
|
|
|
|
|
static bool socket_id_user_decrement(bNodeSocket *sock)
|
|
|
|
|
{
|
|
|
|
|
switch (eNodeSocketDatatype(sock->type)) {
|
|
|
|
|
case SOCK_OBJECT: {
|
|
|
|
|
bNodeSocketValueObject &default_value = *sock->default_value_typed<bNodeSocketValueObject>();
|
|
|
|
|
id_us_min(reinterpret_cast<ID *>(default_value.value));
|
|
|
|
|
return default_value.value != nullptr;
|
|
|
|
|
}
|
|
|
|
|
case SOCK_IMAGE: {
|
|
|
|
|
bNodeSocketValueImage &default_value = *sock->default_value_typed<bNodeSocketValueImage>();
|
|
|
|
|
id_us_min(reinterpret_cast<ID *>(default_value.value));
|
|
|
|
|
return default_value.value != nullptr;
|
|
|
|
|
}
|
|
|
|
|
case SOCK_COLLECTION: {
|
|
|
|
|
bNodeSocketValueCollection &default_value =
|
|
|
|
|
*sock->default_value_typed<bNodeSocketValueCollection>();
|
|
|
|
|
id_us_min(reinterpret_cast<ID *>(default_value.value));
|
|
|
|
|
return default_value.value != nullptr;
|
|
|
|
|
}
|
|
|
|
|
case SOCK_TEXTURE: {
|
|
|
|
|
bNodeSocketValueTexture &default_value =
|
|
|
|
|
*sock->default_value_typed<bNodeSocketValueTexture>();
|
|
|
|
|
id_us_min(reinterpret_cast<ID *>(default_value.value));
|
|
|
|
|
return default_value.value != nullptr;
|
|
|
|
|
}
|
|
|
|
|
case SOCK_MATERIAL: {
|
|
|
|
|
bNodeSocketValueMaterial &default_value =
|
|
|
|
|
*sock->default_value_typed<bNodeSocketValueMaterial>();
|
|
|
|
|
id_us_min(reinterpret_cast<ID *>(default_value.value));
|
|
|
|
|
return default_value.value != nullptr;
|
|
|
|
|
}
|
|
|
|
|
case SOCK_FLOAT:
|
|
|
|
|
case SOCK_VECTOR:
|
|
|
|
|
case SOCK_RGBA:
|
|
|
|
|
case SOCK_BOOLEAN:
|
2023-06-14 20:30:10 +02:00
|
|
|
case SOCK_ROTATION:
|
2024-02-13 18:59:36 +01:00
|
|
|
case SOCK_MATRIX:
|
2023-05-16 09:39:42 -04:00
|
|
|
case SOCK_INT:
|
|
|
|
|
case SOCK_STRING:
|
2024-01-26 12:40:01 +01:00
|
|
|
case SOCK_MENU:
|
2023-05-16 09:39:42 -04:00
|
|
|
case SOCK_CUSTOM:
|
|
|
|
|
case SOCK_SHADER:
|
|
|
|
|
case SOCK_GEOMETRY:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void nodeModifySocketType(bNodeTree *ntree,
|
|
|
|
|
bNode * /*node*/,
|
|
|
|
|
bNodeSocket *sock,
|
|
|
|
|
const char *idname)
|
2023-05-15 15:14:22 +02:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
bNodeSocketType *socktype = nodeSocketTypeFind(idname);
|
|
|
|
|
|
|
|
|
|
if (!socktype) {
|
|
|
|
|
CLOG_ERROR(&LOG, "node socket type %s undefined", idname);
|
|
|
|
|
return;
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
if (sock->default_value) {
|
|
|
|
|
if (sock->type != socktype->type) {
|
|
|
|
|
/* Only reallocate the default value if the type changed so that UI data like min and max
|
|
|
|
|
* isn't removed. This assumes that the default value is stored in the same format for all
|
2023-06-02 10:16:16 +10:00
|
|
|
* socket types with the same #eNodeSocketDatatype. */
|
2023-05-16 09:39:42 -04:00
|
|
|
socket_id_user_decrement(sock);
|
|
|
|
|
MEM_freeN(sock->default_value);
|
|
|
|
|
sock->default_value = nullptr;
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
else {
|
|
|
|
|
/* Update the socket subtype when the storage isn't freed and recreated. */
|
|
|
|
|
switch (eNodeSocketDatatype(sock->type)) {
|
|
|
|
|
case SOCK_FLOAT: {
|
|
|
|
|
sock->default_value_typed<bNodeSocketValueFloat>()->subtype = socktype->subtype;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case SOCK_VECTOR: {
|
|
|
|
|
sock->default_value_typed<bNodeSocketValueVector>()->subtype = socktype->subtype;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case SOCK_INT: {
|
|
|
|
|
sock->default_value_typed<bNodeSocketValueInt>()->subtype = socktype->subtype;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case SOCK_STRING: {
|
|
|
|
|
sock->default_value_typed<bNodeSocketValueString>()->subtype = socktype->subtype;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case SOCK_RGBA:
|
|
|
|
|
case SOCK_SHADER:
|
|
|
|
|
case SOCK_BOOLEAN:
|
2023-06-14 20:30:10 +02:00
|
|
|
case SOCK_ROTATION:
|
2024-02-13 18:59:36 +01:00
|
|
|
case SOCK_MATRIX:
|
2023-05-16 09:39:42 -04:00
|
|
|
case SOCK_CUSTOM:
|
|
|
|
|
case SOCK_OBJECT:
|
|
|
|
|
case SOCK_IMAGE:
|
|
|
|
|
case SOCK_GEOMETRY:
|
|
|
|
|
case SOCK_COLLECTION:
|
|
|
|
|
case SOCK_TEXTURE:
|
|
|
|
|
case SOCK_MATERIAL:
|
2024-01-26 12:40:01 +01:00
|
|
|
case SOCK_MENU:
|
2023-05-16 09:39:42 -04:00
|
|
|
break;
|
|
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
STRNCPY(sock->idname, idname);
|
|
|
|
|
node_socket_set_typeinfo(ntree, sock, socktype);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void nodeModifySocketTypeStatic(
|
|
|
|
|
bNodeTree *ntree, bNode *node, bNodeSocket *sock, const int type, const int subtype)
|
2023-05-15 15:14:22 +02:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
const char *idname = nodeStaticSocketType(type, subtype);
|
|
|
|
|
|
|
|
|
|
if (!idname) {
|
|
|
|
|
CLOG_ERROR(&LOG, "static node socket type %d undefined", type);
|
|
|
|
|
return;
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
|
2024-05-13 16:07:12 +02:00
|
|
|
nodeModifySocketType(ntree, node, sock, idname);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
bNodeSocket *nodeAddSocket(bNodeTree *ntree,
|
|
|
|
|
bNode *node,
|
|
|
|
|
const eNodeSocketInOut in_out,
|
|
|
|
|
const char *idname,
|
|
|
|
|
const char *identifier,
|
|
|
|
|
const char *name)
|
2023-05-15 15:14:22 +02:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
BLI_assert(node->type != NODE_FRAME);
|
|
|
|
|
BLI_assert(!(in_out == SOCK_IN && node->type == NODE_GROUP_INPUT));
|
|
|
|
|
BLI_assert(!(in_out == SOCK_OUT && node->type == NODE_GROUP_OUTPUT));
|
|
|
|
|
|
|
|
|
|
ListBase *lb = (in_out == SOCK_IN ? &node->inputs : &node->outputs);
|
2024-05-13 16:07:12 +02:00
|
|
|
bNodeSocket *sock = make_socket(ntree, node, in_out, lb, idname, identifier, name);
|
2023-05-16 09:39:42 -04:00
|
|
|
|
|
|
|
|
BLI_remlink(lb, sock); /* does nothing for new socket */
|
|
|
|
|
BLI_addtail(lb, sock);
|
|
|
|
|
|
|
|
|
|
BKE_ntree_update_tag_socket_new(ntree, sock);
|
|
|
|
|
|
|
|
|
|
return sock;
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
bool nodeIsStaticSocketType(const bNodeSocketType *stype)
|
2023-05-15 15:14:22 +02:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
/*
|
|
|
|
|
* Cannot rely on type==SOCK_CUSTOM here, because type is 0 by default
|
|
|
|
|
* and can be changed on custom sockets.
|
|
|
|
|
*/
|
|
|
|
|
return RNA_struct_is_a(stype->ext_socket.srna, &RNA_NodeSocketStandard);
|
|
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
const char *nodeStaticSocketType(const int type, const int subtype)
|
|
|
|
|
{
|
|
|
|
|
switch (eNodeSocketDatatype(type)) {
|
|
|
|
|
case SOCK_FLOAT:
|
|
|
|
|
switch (PropertySubType(subtype)) {
|
|
|
|
|
case PROP_UNSIGNED:
|
|
|
|
|
return "NodeSocketFloatUnsigned";
|
|
|
|
|
case PROP_PERCENTAGE:
|
|
|
|
|
return "NodeSocketFloatPercentage";
|
|
|
|
|
case PROP_FACTOR:
|
|
|
|
|
return "NodeSocketFloatFactor";
|
|
|
|
|
case PROP_ANGLE:
|
|
|
|
|
return "NodeSocketFloatAngle";
|
|
|
|
|
case PROP_TIME:
|
|
|
|
|
return "NodeSocketFloatTime";
|
|
|
|
|
case PROP_TIME_ABSOLUTE:
|
|
|
|
|
return "NodeSocketFloatTimeAbsolute";
|
|
|
|
|
case PROP_DISTANCE:
|
|
|
|
|
return "NodeSocketFloatDistance";
|
2024-04-22 13:10:16 +02:00
|
|
|
case PROP_WAVELENGTH:
|
|
|
|
|
return "NodeSocketFloatWavelength";
|
2023-05-16 09:39:42 -04:00
|
|
|
case PROP_NONE:
|
|
|
|
|
default:
|
|
|
|
|
return "NodeSocketFloat";
|
|
|
|
|
}
|
|
|
|
|
case SOCK_INT:
|
|
|
|
|
switch (PropertySubType(subtype)) {
|
|
|
|
|
case PROP_UNSIGNED:
|
|
|
|
|
return "NodeSocketIntUnsigned";
|
|
|
|
|
case PROP_PERCENTAGE:
|
|
|
|
|
return "NodeSocketIntPercentage";
|
|
|
|
|
case PROP_FACTOR:
|
|
|
|
|
return "NodeSocketIntFactor";
|
|
|
|
|
case PROP_NONE:
|
|
|
|
|
default:
|
|
|
|
|
return "NodeSocketInt";
|
|
|
|
|
}
|
|
|
|
|
case SOCK_BOOLEAN:
|
|
|
|
|
return "NodeSocketBool";
|
2023-06-14 20:30:10 +02:00
|
|
|
case SOCK_ROTATION:
|
|
|
|
|
return "NodeSocketRotation";
|
2024-02-13 18:59:36 +01:00
|
|
|
case SOCK_MATRIX:
|
|
|
|
|
return "NodeSocketMatrix";
|
2023-05-16 09:39:42 -04:00
|
|
|
case SOCK_VECTOR:
|
|
|
|
|
switch (PropertySubType(subtype)) {
|
|
|
|
|
case PROP_TRANSLATION:
|
|
|
|
|
return "NodeSocketVectorTranslation";
|
|
|
|
|
case PROP_DIRECTION:
|
|
|
|
|
return "NodeSocketVectorDirection";
|
|
|
|
|
case PROP_VELOCITY:
|
|
|
|
|
return "NodeSocketVectorVelocity";
|
|
|
|
|
case PROP_ACCELERATION:
|
|
|
|
|
return "NodeSocketVectorAcceleration";
|
|
|
|
|
case PROP_EULER:
|
|
|
|
|
return "NodeSocketVectorEuler";
|
|
|
|
|
case PROP_XYZ:
|
|
|
|
|
return "NodeSocketVectorXYZ";
|
|
|
|
|
case PROP_NONE:
|
|
|
|
|
default:
|
|
|
|
|
return "NodeSocketVector";
|
|
|
|
|
}
|
|
|
|
|
case SOCK_RGBA:
|
|
|
|
|
return "NodeSocketColor";
|
|
|
|
|
case SOCK_STRING:
|
|
|
|
|
return "NodeSocketString";
|
|
|
|
|
case SOCK_SHADER:
|
|
|
|
|
return "NodeSocketShader";
|
|
|
|
|
case SOCK_OBJECT:
|
|
|
|
|
return "NodeSocketObject";
|
|
|
|
|
case SOCK_IMAGE:
|
|
|
|
|
return "NodeSocketImage";
|
|
|
|
|
case SOCK_GEOMETRY:
|
|
|
|
|
return "NodeSocketGeometry";
|
|
|
|
|
case SOCK_COLLECTION:
|
|
|
|
|
return "NodeSocketCollection";
|
|
|
|
|
case SOCK_TEXTURE:
|
|
|
|
|
return "NodeSocketTexture";
|
|
|
|
|
case SOCK_MATERIAL:
|
|
|
|
|
return "NodeSocketMaterial";
|
2024-01-26 12:40:01 +01:00
|
|
|
case SOCK_MENU:
|
|
|
|
|
return "NodeSocketMenu";
|
2023-06-06 19:19:53 +02:00
|
|
|
case SOCK_CUSTOM:
|
2023-05-16 09:39:42 -04:00
|
|
|
break;
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-30 12:37:21 +02:00
|
|
|
const char *nodeStaticSocketInterfaceTypeNew(const int type, const int subtype)
|
2023-05-15 15:14:22 +02:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
switch (eNodeSocketDatatype(type)) {
|
|
|
|
|
case SOCK_FLOAT:
|
|
|
|
|
switch (PropertySubType(subtype)) {
|
|
|
|
|
case PROP_UNSIGNED:
|
2023-08-30 12:37:21 +02:00
|
|
|
return "NodeTreeInterfaceSocketFloatUnsigned";
|
2023-05-16 09:39:42 -04:00
|
|
|
case PROP_PERCENTAGE:
|
2023-08-30 12:37:21 +02:00
|
|
|
return "NodeTreeInterfaceSocketFloatPercentage";
|
2023-05-16 09:39:42 -04:00
|
|
|
case PROP_FACTOR:
|
2023-08-30 12:37:21 +02:00
|
|
|
return "NodeTreeInterfaceSocketFloatFactor";
|
2023-05-16 09:39:42 -04:00
|
|
|
case PROP_ANGLE:
|
2023-08-30 12:37:21 +02:00
|
|
|
return "NodeTreeInterfaceSocketFloatAngle";
|
2023-05-16 09:39:42 -04:00
|
|
|
case PROP_TIME:
|
2023-08-30 12:37:21 +02:00
|
|
|
return "NodeTreeInterfaceSocketFloatTime";
|
2023-05-16 09:39:42 -04:00
|
|
|
case PROP_TIME_ABSOLUTE:
|
2023-08-30 12:37:21 +02:00
|
|
|
return "NodeTreeInterfaceSocketFloatTimeAbsolute";
|
2023-05-16 09:39:42 -04:00
|
|
|
case PROP_DISTANCE:
|
2023-08-30 12:37:21 +02:00
|
|
|
return "NodeTreeInterfaceSocketFloatDistance";
|
2024-04-22 13:10:16 +02:00
|
|
|
case PROP_WAVELENGTH:
|
|
|
|
|
return "NodeTreeInterfaceSocketFloatWavelength";
|
2023-05-16 09:39:42 -04:00
|
|
|
case PROP_NONE:
|
|
|
|
|
default:
|
2023-08-30 12:37:21 +02:00
|
|
|
return "NodeTreeInterfaceSocketFloat";
|
2023-05-16 09:39:42 -04:00
|
|
|
}
|
|
|
|
|
case SOCK_INT:
|
|
|
|
|
switch (PropertySubType(subtype)) {
|
|
|
|
|
case PROP_UNSIGNED:
|
2023-08-30 12:37:21 +02:00
|
|
|
return "NodeTreeInterfaceSocketIntUnsigned";
|
2023-05-16 09:39:42 -04:00
|
|
|
case PROP_PERCENTAGE:
|
2023-08-30 12:37:21 +02:00
|
|
|
return "NodeTreeInterfaceSocketIntPercentage";
|
2023-05-16 09:39:42 -04:00
|
|
|
case PROP_FACTOR:
|
2023-08-30 12:37:21 +02:00
|
|
|
return "NodeTreeInterfaceSocketIntFactor";
|
2023-05-16 09:39:42 -04:00
|
|
|
case PROP_NONE:
|
|
|
|
|
default:
|
2023-08-30 12:37:21 +02:00
|
|
|
return "NodeTreeInterfaceSocketInt";
|
2023-05-16 09:39:42 -04:00
|
|
|
}
|
|
|
|
|
case SOCK_BOOLEAN:
|
2023-08-30 12:37:21 +02:00
|
|
|
return "NodeTreeInterfaceSocketBool";
|
2023-06-14 20:30:10 +02:00
|
|
|
case SOCK_ROTATION:
|
2023-08-30 12:37:21 +02:00
|
|
|
return "NodeTreeInterfaceSocketRotation";
|
2024-02-13 18:59:36 +01:00
|
|
|
case SOCK_MATRIX:
|
|
|
|
|
return "NodeTreeInterfaceSocketMatrix";
|
2023-05-16 09:39:42 -04:00
|
|
|
case SOCK_VECTOR:
|
|
|
|
|
switch (PropertySubType(subtype)) {
|
|
|
|
|
case PROP_TRANSLATION:
|
2023-08-30 12:37:21 +02:00
|
|
|
return "NodeTreeInterfaceSocketVectorTranslation";
|
2023-05-16 09:39:42 -04:00
|
|
|
case PROP_DIRECTION:
|
2023-08-30 12:37:21 +02:00
|
|
|
return "NodeTreeInterfaceSocketVectorDirection";
|
2023-05-16 09:39:42 -04:00
|
|
|
case PROP_VELOCITY:
|
2023-08-30 12:37:21 +02:00
|
|
|
return "NodeTreeInterfaceSocketVectorVelocity";
|
2023-05-16 09:39:42 -04:00
|
|
|
case PROP_ACCELERATION:
|
2023-08-30 12:37:21 +02:00
|
|
|
return "NodeTreeInterfaceSocketVectorAcceleration";
|
2023-05-16 09:39:42 -04:00
|
|
|
case PROP_EULER:
|
2023-08-30 12:37:21 +02:00
|
|
|
return "NodeTreeInterfaceSocketVectorEuler";
|
2023-05-16 09:39:42 -04:00
|
|
|
case PROP_XYZ:
|
2023-08-30 12:37:21 +02:00
|
|
|
return "NodeTreeInterfaceSocketVectorXYZ";
|
2023-05-16 09:39:42 -04:00
|
|
|
case PROP_NONE:
|
|
|
|
|
default:
|
2023-08-30 12:37:21 +02:00
|
|
|
return "NodeTreeInterfaceSocketVector";
|
2023-05-16 09:39:42 -04:00
|
|
|
}
|
|
|
|
|
case SOCK_RGBA:
|
2023-08-30 12:37:21 +02:00
|
|
|
return "NodeTreeInterfaceSocketColor";
|
2023-05-16 09:39:42 -04:00
|
|
|
case SOCK_STRING:
|
2023-08-30 12:37:21 +02:00
|
|
|
return "NodeTreeInterfaceSocketString";
|
2023-05-16 09:39:42 -04:00
|
|
|
case SOCK_SHADER:
|
2023-08-30 12:37:21 +02:00
|
|
|
return "NodeTreeInterfaceSocketShader";
|
2023-05-16 09:39:42 -04:00
|
|
|
case SOCK_OBJECT:
|
2023-08-30 12:37:21 +02:00
|
|
|
return "NodeTreeInterfaceSocketObject";
|
2023-05-16 09:39:42 -04:00
|
|
|
case SOCK_IMAGE:
|
2023-08-30 12:37:21 +02:00
|
|
|
return "NodeTreeInterfaceSocketImage";
|
2023-05-16 09:39:42 -04:00
|
|
|
case SOCK_GEOMETRY:
|
2023-08-30 12:37:21 +02:00
|
|
|
return "NodeTreeInterfaceSocketGeometry";
|
2023-05-16 09:39:42 -04:00
|
|
|
case SOCK_COLLECTION:
|
2023-08-30 12:37:21 +02:00
|
|
|
return "NodeTreeInterfaceSocketCollection";
|
2023-05-16 09:39:42 -04:00
|
|
|
case SOCK_TEXTURE:
|
2023-08-30 12:37:21 +02:00
|
|
|
return "NodeTreeInterfaceSocketTexture";
|
2023-05-16 09:39:42 -04:00
|
|
|
case SOCK_MATERIAL:
|
2023-08-30 12:37:21 +02:00
|
|
|
return "NodeTreeInterfaceSocketMaterial";
|
2024-01-26 12:40:01 +01:00
|
|
|
case SOCK_MENU:
|
|
|
|
|
return "NodeTreeInterfaceSocketMenu";
|
2023-06-06 19:19:53 +02:00
|
|
|
case SOCK_CUSTOM:
|
2023-05-16 09:39:42 -04:00
|
|
|
break;
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *nodeStaticSocketLabel(const int type, const int /*subtype*/)
|
|
|
|
|
{
|
|
|
|
|
switch (eNodeSocketDatatype(type)) {
|
|
|
|
|
case SOCK_FLOAT:
|
|
|
|
|
return "Float";
|
|
|
|
|
case SOCK_INT:
|
|
|
|
|
return "Integer";
|
|
|
|
|
case SOCK_BOOLEAN:
|
|
|
|
|
return "Boolean";
|
2023-06-14 20:30:10 +02:00
|
|
|
case SOCK_ROTATION:
|
|
|
|
|
return "Rotation";
|
2024-02-13 18:59:36 +01:00
|
|
|
case SOCK_MATRIX:
|
|
|
|
|
return "Matrix";
|
2023-05-16 09:39:42 -04:00
|
|
|
case SOCK_VECTOR:
|
|
|
|
|
return "Vector";
|
|
|
|
|
case SOCK_RGBA:
|
|
|
|
|
return "Color";
|
|
|
|
|
case SOCK_STRING:
|
|
|
|
|
return "String";
|
|
|
|
|
case SOCK_SHADER:
|
|
|
|
|
return "Shader";
|
|
|
|
|
case SOCK_OBJECT:
|
|
|
|
|
return "Object";
|
|
|
|
|
case SOCK_IMAGE:
|
|
|
|
|
return "Image";
|
|
|
|
|
case SOCK_GEOMETRY:
|
|
|
|
|
return "Geometry";
|
|
|
|
|
case SOCK_COLLECTION:
|
|
|
|
|
return "Collection";
|
|
|
|
|
case SOCK_TEXTURE:
|
|
|
|
|
return "Texture";
|
|
|
|
|
case SOCK_MATERIAL:
|
|
|
|
|
return "Material";
|
2024-01-26 12:40:01 +01:00
|
|
|
case SOCK_MENU:
|
|
|
|
|
return "Menu";
|
2023-06-06 19:19:53 +02:00
|
|
|
case SOCK_CUSTOM:
|
2023-05-16 09:39:42 -04:00
|
|
|
break;
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
return nullptr;
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
bNodeSocket *nodeAddStaticSocket(bNodeTree *ntree,
|
|
|
|
|
bNode *node,
|
|
|
|
|
eNodeSocketInOut in_out,
|
|
|
|
|
int type,
|
|
|
|
|
int subtype,
|
|
|
|
|
const char *identifier,
|
|
|
|
|
const char *name)
|
2023-05-15 15:14:22 +02:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
const char *idname = nodeStaticSocketType(type, subtype);
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
if (!idname) {
|
|
|
|
|
CLOG_ERROR(&LOG, "static node socket type %d undefined", type);
|
|
|
|
|
return nullptr;
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
bNodeSocket *sock = nodeAddSocket(ntree, node, in_out, idname, identifier, name);
|
|
|
|
|
sock->type = type;
|
|
|
|
|
return sock;
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
static void node_socket_free(bNodeSocket *sock, const bool do_id_user)
|
2023-05-15 15:14:22 +02:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
if (sock->prop) {
|
|
|
|
|
IDP_FreePropertyContent_ex(sock->prop, do_id_user);
|
|
|
|
|
MEM_freeN(sock->prop);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
|
|
|
|
|
if (sock->default_value) {
|
|
|
|
|
if (do_id_user) {
|
|
|
|
|
socket_id_user_decrement(sock);
|
|
|
|
|
}
|
2024-01-26 12:40:01 +01:00
|
|
|
if (sock->type == SOCK_MENU) {
|
|
|
|
|
auto &default_value_menu = *sock->default_value_typed<bNodeSocketValueMenu>();
|
|
|
|
|
if (default_value_menu.enum_items) {
|
|
|
|
|
/* Release shared data pointer. */
|
|
|
|
|
default_value_menu.enum_items->remove_user_and_delete_if_last();
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
MEM_freeN(sock->default_value);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
if (sock->default_attribute_name) {
|
|
|
|
|
MEM_freeN(sock->default_attribute_name);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
MEM_delete(sock->runtime);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void nodeRemoveSocket(bNodeTree *ntree, bNode *node, bNodeSocket *sock)
|
2023-05-15 15:14:22 +02:00
|
|
|
{
|
2024-05-13 16:07:12 +02:00
|
|
|
nodeRemoveSocketEx(ntree, node, sock, true);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void nodeRemoveSocketEx(bNodeTree *ntree, bNode *node, bNodeSocket *sock, const bool do_id_user)
|
2023-05-15 15:14:22 +02:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree->links) {
|
|
|
|
|
if (link->fromsock == sock || link->tosock == sock) {
|
|
|
|
|
nodeRemLink(ntree, link);
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
for (const int64_t i : node->runtime->internal_links.index_range()) {
|
|
|
|
|
const bNodeLink &link = node->runtime->internal_links[i];
|
|
|
|
|
if (link.fromsock == sock || link.tosock == sock) {
|
|
|
|
|
node->runtime->internal_links.remove_and_reorder(i);
|
|
|
|
|
BKE_ntree_update_tag_node_internal_link(ntree, node);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/* this is fast, this way we don't need an in_out argument */
|
|
|
|
|
BLI_remlink(&node->inputs, sock);
|
|
|
|
|
BLI_remlink(&node->outputs, sock);
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2024-05-13 16:07:12 +02:00
|
|
|
node_socket_free(sock, do_id_user);
|
2023-05-16 09:39:42 -04:00
|
|
|
MEM_freeN(sock);
|
|
|
|
|
|
|
|
|
|
BKE_ntree_update_tag_socket_removed(ntree);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
bNode *nodeFindNodebyName(bNodeTree *ntree, const char *name)
|
2023-05-15 15:14:22 +02:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
return reinterpret_cast<bNode *>(BLI_findstring(&ntree->nodes, name, offsetof(bNode, name)));
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void nodeFindNode(bNodeTree *ntree, bNodeSocket *sock, bNode **r_node, int *r_sockindex)
|
2023-05-15 15:14:22 +02:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
*r_node = nullptr;
|
|
|
|
|
if (ntree->runtime->topology_cache_mutex.is_cached()) {
|
|
|
|
|
bNode *node = &sock->owner_node();
|
|
|
|
|
*r_node = node;
|
|
|
|
|
if (r_sockindex) {
|
|
|
|
|
const ListBase *sockets = (sock->in_out == SOCK_IN) ? &node->inputs : &node->outputs;
|
|
|
|
|
*r_sockindex = BLI_findindex(sockets, sock);
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const bool success = nodeFindNodeTry(ntree, sock, r_node, r_sockindex);
|
|
|
|
|
BLI_assert(success);
|
|
|
|
|
UNUSED_VARS_NDEBUG(success);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
bool nodeFindNodeTry(bNodeTree *ntree, bNodeSocket *sock, bNode **r_node, int *r_sockindex)
|
2023-05-15 15:14:22 +02:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
for (bNode *node : ntree->all_nodes()) {
|
|
|
|
|
const ListBase *sockets = (sock->in_out == SOCK_IN) ? &node->inputs : &node->outputs;
|
|
|
|
|
int i;
|
|
|
|
|
LISTBASE_FOREACH_INDEX (const bNodeSocket *, tsock, sockets, i) {
|
|
|
|
|
if (sock == tsock) {
|
|
|
|
|
if (r_node != nullptr) {
|
|
|
|
|
*r_node = node;
|
|
|
|
|
}
|
|
|
|
|
if (r_sockindex != nullptr) {
|
|
|
|
|
*r_sockindex = i;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
bNode *nodeFindRootParent(bNode *node)
|
2023-05-15 15:14:22 +02:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
bNode *parent_iter = node;
|
|
|
|
|
while (parent_iter->parent != nullptr) {
|
|
|
|
|
parent_iter = parent_iter->parent;
|
|
|
|
|
}
|
|
|
|
|
if (parent_iter->type != NODE_FRAME) {
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
return parent_iter;
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
bool nodeIsParentAndChild(const bNode *parent, const bNode *child)
|
2023-05-15 15:14:22 +02:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
for (const bNode *child_iter = child; child_iter; child_iter = child_iter->parent) {
|
|
|
|
|
if (child_iter == parent) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void nodeChainIter(const bNodeTree *ntree,
|
|
|
|
|
const bNode *node_start,
|
|
|
|
|
bool (*callback)(bNode *, bNode *, void *, const bool),
|
|
|
|
|
void *userdata,
|
|
|
|
|
const bool reversed)
|
2023-05-15 15:14:22 +02:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
|
|
|
|
|
if ((link->flag & NODE_LINK_VALID) == 0) {
|
|
|
|
|
/* Skip links marked as cyclic. */
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
/* Is the link part of the chain meaning node_start == fromnode
|
|
|
|
|
* (or tonode for reversed case)? */
|
|
|
|
|
if (!reversed) {
|
|
|
|
|
if (link->fromnode != node_start) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if (link->tonode != node_start) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
if (!callback(link->fromnode, link->tonode, userdata, reversed)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
nodeChainIter(ntree, reversed ? link->fromnode : link->tonode, callback, userdata, reversed);
|
|
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
static void iter_backwards_ex(const bNodeTree *ntree,
|
|
|
|
|
const bNode *node_start,
|
|
|
|
|
bool (*callback)(bNode *, bNode *, void *),
|
|
|
|
|
void *userdata,
|
|
|
|
|
const char recursion_mask)
|
2023-05-15 15:14:22 +02:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
LISTBASE_FOREACH (bNodeSocket *, sock, &node_start->inputs) {
|
|
|
|
|
bNodeLink *link = sock->link;
|
|
|
|
|
if (link == nullptr) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if ((link->flag & NODE_LINK_VALID) == 0) {
|
|
|
|
|
/* Skip links marked as cyclic. */
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (link->fromnode->runtime->iter_flag & recursion_mask) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
link->fromnode->runtime->iter_flag |= recursion_mask;
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
if (!callback(link->fromnode, link->tonode, userdata)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
iter_backwards_ex(ntree, link->fromnode, callback, userdata, recursion_mask);
|
2013-03-18 16:34:57 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void nodeChainIterBackwards(const bNodeTree *ntree,
|
|
|
|
|
const bNode *node_start,
|
|
|
|
|
bool (*callback)(bNode *, bNode *, void *),
|
|
|
|
|
void *userdata,
|
|
|
|
|
const int recursion_lvl)
|
2013-03-18 16:34:57 +00:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
if (!node_start) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/* Limited by iter_flag type. */
|
|
|
|
|
BLI_assert(recursion_lvl < 8);
|
|
|
|
|
const char recursion_mask = (1 << recursion_lvl);
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/* Reset flag. */
|
|
|
|
|
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
|
|
|
|
|
node->runtime->iter_flag &= ~recursion_mask;
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
iter_backwards_ex(ntree, node_start, callback, userdata, recursion_mask);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void nodeParentsIter(bNode *node, bool (*callback)(bNode *, void *), void *userdata)
|
2023-05-15 15:14:22 +02:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
if (node->parent) {
|
|
|
|
|
if (!callback(node->parent, userdata)) {
|
|
|
|
|
return;
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
nodeParentsIter(node->parent, callback, userdata);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void nodeUniqueName(bNodeTree *ntree, bNode *node)
|
2023-05-15 15:14:22 +02:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
BLI_uniquename(
|
|
|
|
|
&ntree->nodes, node, DATA_("Node"), '.', offsetof(bNode, name), sizeof(node->name));
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void nodeUniqueID(bNodeTree *ntree, bNode *node)
|
2023-05-15 15:14:22 +02:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
/* Use a pointer cast to avoid overflow warnings. */
|
2024-02-15 13:15:56 +11:00
|
|
|
const double time = BLI_time_now_seconds() * 1000000.0;
|
2024-05-13 16:07:12 +02:00
|
|
|
RandomNumberGenerator id_rng{*reinterpret_cast<const uint32_t *>(&time)};
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/* In the unlikely case that the random ID doesn't match, choose a new one until it does. */
|
|
|
|
|
int32_t new_id = id_rng.get_int32();
|
|
|
|
|
while (ntree->runtime->nodes_by_id.contains_as(new_id) || new_id <= 0) {
|
|
|
|
|
new_id = id_rng.get_int32();
|
2021-07-06 18:36:11 +01:00
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
node->identifier = new_id;
|
|
|
|
|
ntree->runtime->nodes_by_id.add_new(node);
|
|
|
|
|
node->runtime->index_in_tree = ntree->runtime->nodes_by_id.index_range().last();
|
|
|
|
|
BLI_assert(node->runtime->index_in_tree == ntree->runtime->nodes_by_id.index_of(node));
|
2021-07-06 18:36:11 +01:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
bNode *nodeAddNode(const bContext *C, bNodeTree *ntree, const char *idname)
|
2013-03-18 16:34:57 +00:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
bNode *node = MEM_cnew<bNode>("new node");
|
|
|
|
|
node->runtime = MEM_new<bNodeRuntime>(__func__);
|
|
|
|
|
BLI_addtail(&ntree->nodes, node);
|
|
|
|
|
nodeUniqueID(ntree, node);
|
2024-01-18 16:12:52 -05:00
|
|
|
node->ui_order = ntree->all_nodes().size();
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
STRNCPY(node->idname, idname);
|
2024-05-13 16:07:12 +02:00
|
|
|
node_set_typeinfo(C, ntree, node, nodeTypeFind(idname));
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
BKE_ntree_update_tag_node_new(ntree, node);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-12-13 16:24:25 +01:00
|
|
|
if (ELEM(node->type,
|
|
|
|
|
GEO_NODE_INPUT_SCENE_TIME,
|
|
|
|
|
GEO_NODE_INPUT_ACTIVE_CAMERA,
|
|
|
|
|
GEO_NODE_SELF_OBJECT,
|
|
|
|
|
GEO_NODE_SIMULATION_INPUT))
|
2023-05-16 09:39:42 -04:00
|
|
|
{
|
|
|
|
|
DEG_relations_tag_update(CTX_data_main(C));
|
|
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
return node;
|
2013-03-18 16:34:57 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
bNode *nodeAddStaticNode(const bContext *C, bNodeTree *ntree, const int type)
|
2013-03-18 16:34:57 +00:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
const char *idname = nullptr;
|
|
|
|
|
|
|
|
|
|
NODE_TYPES_BEGIN (ntype) {
|
|
|
|
|
/* Do an extra poll here, because some int types are used
|
|
|
|
|
* for multiple node types, this helps find the desired type. */
|
|
|
|
|
if (ntype->type != type) {
|
|
|
|
|
continue;
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
const char *disabled_hint;
|
|
|
|
|
if (ntype->poll && ntype->poll(ntype, ntree, &disabled_hint)) {
|
|
|
|
|
idname = ntype->idname;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
NODE_TYPES_END;
|
|
|
|
|
if (!idname) {
|
|
|
|
|
CLOG_ERROR(&LOG, "static node type %d undefined", type);
|
|
|
|
|
return nullptr;
|
2013-03-18 16:34:57 +00:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
return nodeAddNode(C, ntree, idname);
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
static void node_socket_copy(bNodeSocket *sock_dst, const bNodeSocket *sock_src, const int flag)
|
|
|
|
|
{
|
|
|
|
|
sock_dst->runtime = MEM_new<bNodeSocketRuntime>(__func__);
|
|
|
|
|
if (sock_src->prop) {
|
|
|
|
|
sock_dst->prop = IDP_CopyProperty_ex(sock_src->prop, flag);
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
if (sock_src->default_value) {
|
|
|
|
|
sock_dst->default_value = MEM_dupallocN(sock_src->default_value);
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) {
|
|
|
|
|
socket_id_user_increment(sock_dst);
|
|
|
|
|
}
|
2024-01-26 12:40:01 +01:00
|
|
|
|
|
|
|
|
if (sock_src->type == SOCK_MENU) {
|
|
|
|
|
auto &default_value_menu = *sock_dst->default_value_typed<bNodeSocketValueMenu>();
|
|
|
|
|
if (default_value_menu.enum_items) {
|
|
|
|
|
/* Copy of shared data pointer. */
|
|
|
|
|
default_value_menu.enum_items->add_user();
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-09-09 13:35:21 +02:00
|
|
|
}
|
2013-03-18 16:34:57 +00:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
sock_dst->default_attribute_name = static_cast<char *>(
|
|
|
|
|
MEM_dupallocN(sock_src->default_attribute_name));
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
sock_dst->stack_index = 0;
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
|
|
|
|
|
bNode *node_copy_with_mapping(bNodeTree *dst_tree,
|
|
|
|
|
const bNode &node_src,
|
|
|
|
|
const int flag,
|
|
|
|
|
const bool use_unique,
|
|
|
|
|
Map<const bNodeSocket *, bNodeSocket *> &socket_map)
|
2021-08-30 17:13:46 +02:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
bNode *node_dst = static_cast<bNode *>(MEM_mallocN(sizeof(bNode), __func__));
|
|
|
|
|
*node_dst = node_src;
|
|
|
|
|
|
|
|
|
|
node_dst->runtime = MEM_new<bNodeRuntime>(__func__);
|
|
|
|
|
|
|
|
|
|
/* Can be called for nodes outside a node tree (e.g. clipboard). */
|
|
|
|
|
if (dst_tree) {
|
|
|
|
|
if (use_unique) {
|
|
|
|
|
nodeUniqueName(dst_tree, node_dst);
|
|
|
|
|
nodeUniqueID(dst_tree, node_dst);
|
|
|
|
|
}
|
|
|
|
|
BLI_addtail(&dst_tree->nodes, node_dst);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
|
|
|
|
|
BLI_listbase_clear(&node_dst->inputs);
|
|
|
|
|
LISTBASE_FOREACH (const bNodeSocket *, src_socket, &node_src.inputs) {
|
|
|
|
|
bNodeSocket *dst_socket = static_cast<bNodeSocket *>(MEM_dupallocN(src_socket));
|
|
|
|
|
node_socket_copy(dst_socket, src_socket, flag);
|
|
|
|
|
BLI_addtail(&node_dst->inputs, dst_socket);
|
|
|
|
|
socket_map.add_new(src_socket, dst_socket);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
|
|
|
|
|
BLI_listbase_clear(&node_dst->outputs);
|
|
|
|
|
LISTBASE_FOREACH (const bNodeSocket *, src_socket, &node_src.outputs) {
|
|
|
|
|
bNodeSocket *dst_socket = static_cast<bNodeSocket *>(MEM_dupallocN(src_socket));
|
|
|
|
|
node_socket_copy(dst_socket, src_socket, flag);
|
|
|
|
|
BLI_addtail(&node_dst->outputs, dst_socket);
|
|
|
|
|
socket_map.add_new(src_socket, dst_socket);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
|
|
|
|
|
if (node_src.prop) {
|
|
|
|
|
node_dst->prop = IDP_CopyProperty_ex(node_src.prop, flag);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
|
2023-08-30 12:37:21 +02:00
|
|
|
node_dst->panel_states_array = static_cast<bNodePanelState *>(
|
|
|
|
|
MEM_dupallocN(node_src.panel_states_array));
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
node_dst->runtime->internal_links = node_src.runtime->internal_links;
|
|
|
|
|
for (bNodeLink &dst_link : node_dst->runtime->internal_links) {
|
|
|
|
|
dst_link.fromnode = node_dst;
|
|
|
|
|
dst_link.tonode = node_dst;
|
|
|
|
|
dst_link.fromsock = socket_map.lookup(dst_link.fromsock);
|
|
|
|
|
dst_link.tosock = socket_map.lookup(dst_link.tosock);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
|
|
|
|
|
if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) {
|
|
|
|
|
id_us_plus(node_dst->id);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
|
|
|
|
|
if (node_src.typeinfo->copyfunc) {
|
|
|
|
|
node_src.typeinfo->copyfunc(dst_tree, node_dst, &node_src);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
|
|
|
|
|
if (dst_tree) {
|
|
|
|
|
BKE_ntree_update_tag_node_new(dst_tree, node_dst);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/* Only call copy function when a copy is made for the main database, not
|
|
|
|
|
* for cases like the dependency graph and localization. */
|
|
|
|
|
if (node_dst->typeinfo->copyfunc_api && !(flag & LIB_ID_CREATE_NO_MAIN)) {
|
2023-09-06 00:48:50 +02:00
|
|
|
PointerRNA ptr = RNA_pointer_create(reinterpret_cast<ID *>(dst_tree), &RNA_Node, node_dst);
|
2021-08-30 17:13:46 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
node_dst->typeinfo->copyfunc_api(&ptr, &node_src);
|
Christmas coding work!
********* Node editor work:
- To enable Nodes for Materials, you have to set the "Use Nodes"
button, in the new Material buttons "Nodes" Panel or in header
of the Node editor. Doing this will disable Material-Layers.
- Nodes now execute materials ("shaders"), but still only using the
previewrender code.
- Nodes have (optional) previews for rendered images.
- Node headers allow to hide buttons and/or preview image
- Nodes can be dragged larger/smaller (right-bottom corner)
- Nodes can be hidden (minimized) with hotkey H
- CTRL+click on an Input Socket gives a popup with default values.
- Changing Material/Texture or Mix node will adjust Node title.
- Click-drag outside of a Node changes cursor to "Knife' and allows to
draw a rect where to cut Links.
- Added new node types RGBtoBW, Texture, In/Output, ColorRamp
- Material Nodes have options to ouput diffuse or specular, or to use
a negative normal. The input socket 'Normal' will force the material
to use that normal, otherwise it uses the normal from the Material
that has the node tree.
- When drawing a link between two not-matching sockets, Blender inserts
a converting node (now only for value/rgb combos)
- When drawing a link to an input socket that's already in use, the
old link will either disappear or flip to another unused socket.
- A click on a Material Node will activate it, and show all its settings
in the Material Buttons. Active Material Nodes draw the material icon
in red.
- A click on any node will show its options in the Node Panel in the
Material buttons.
- Multiple Output Nodes can be used, to sample contents of a tree, but
only one Output is the real one, which is indicated in a different
color and red material icon.
- Added ThemeColors for node types
- ALT+C will convert existing Material-Layers to Node... this currently
only adds the material/mix nodes and connects them. Dunno if this is
worth a lot of coding work to make perfect?
- Press C to call another "Solve order", which will show all possible
cyclic conflicts (if there are).
- Technical: nodes now use "Type" structs which define the
structure of nodes and in/output sockets. The Type structs store all
fixed info, callbacks, and allow to reconstruct saved Nodes to match
what is required by Blender.
- Defining (new) nodes now is as simple as filling in a fixed
Type struct, plus code some callbacks. A doc will be made!
- Node preview images are by default float
********* Icon drawing:
- Cleanup of how old icons were implemented in new system, making
them 16x16 too, correctly centered *and* scaled.
- Made drawing Icons use float coordinates
- Moved BIF_calcpreview_image() into interface_icons.c, renamed it
icon_from_image(). Removed a lot of unneeded Imbuf magic here! :)
- Skipped scaling and imbuf copying when icons are OK size
********* Preview render:
- Huge cleanup of code....
- renaming BIF_xxx calls that only were used internally
- BIF_previewrender() now accepts an argument for rendering method,
so it supports icons, buttonwindow previewrender and node editor
- Only a single BIF_preview_changed() call now exists, supporting all
signals as needed for buttos and node editor
********* More stuff:
- glutil.c, glaDrawPixelsSafe() and glaDrawPixelsTex() now accept format
argument for GL_FLOAT rects
- Made the ColorBand become a built-in button for interface.c
Was a load of cleanup work in buttons_shading.c...
- removed a load of unneeded glBlendFunc() calls
- Fixed bug in calculating text length for buttons (ancient!)
2005-12-28 15:42:51 +00:00
|
|
|
}
|
2018-06-17 17:05:51 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
return node_dst;
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2022-01-08 17:36:43 +01:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/**
|
|
|
|
|
* Type of value storage related with socket is the same.
|
|
|
|
|
* \param socket: Node can have multiple sockets & storages pairs.
|
|
|
|
|
*/
|
|
|
|
|
static void *node_static_value_storage_for(bNode &node, const bNodeSocket &socket)
|
2023-05-15 15:14:22 +02:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
if (!socket.is_output()) {
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
switch (node.type) {
|
|
|
|
|
case FN_NODE_INPUT_BOOL:
|
|
|
|
|
return &reinterpret_cast<NodeInputBool *>(node.storage)->boolean;
|
2023-09-22 15:45:05 +02:00
|
|
|
case SH_NODE_VALUE:
|
|
|
|
|
/* The value is stored in the default value of the first output socket. */
|
|
|
|
|
return &static_cast<bNodeSocket *>(node.outputs.first)
|
|
|
|
|
->default_value_typed<bNodeSocketValueFloat>()
|
|
|
|
|
->value;
|
2023-05-16 09:39:42 -04:00
|
|
|
case FN_NODE_INPUT_INT:
|
|
|
|
|
return &reinterpret_cast<NodeInputInt *>(node.storage)->integer;
|
|
|
|
|
case FN_NODE_INPUT_VECTOR:
|
|
|
|
|
return &reinterpret_cast<NodeInputVector *>(node.storage)->vector;
|
|
|
|
|
case FN_NODE_INPUT_COLOR:
|
|
|
|
|
return &reinterpret_cast<NodeInputColor *>(node.storage)->color;
|
|
|
|
|
case GEO_NODE_IMAGE:
|
|
|
|
|
return &node.id;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
Orange: more noodle updates!
**** NEW: Group Nodes
Node trees usually become messy and confusing quickly, so we need
not only a way to collapse Nodes into single 'groups', but also a
way to re-use that data to create libraries of effects.
This has been done by making a new Library data type, the NodeTree.
Everything that has been grouped is stored here, and available for
re-use, appending or linking. These NodeTrees are fully generic,
i.e. can store shader trees, composit trees, and so on. The 'type'
value as stored in the NodeTree will keep track of internal type
definitions and execute/drawing callbacks. Needless to say, re-using
shader trees in a composit tree is a bit useless, and will be
prevented in the browsing code. :)
So; any NodeTree can become a "Goup Node" inside in a NodeTree. This
Group Node then works just like any Node.
To prevent the current code to become too complex, I've disabled
the possibility to insert Groups inside of Groups. That might be
enabled later, but is a real nasty piece of code to get OK.
Since Group Nodes are a dynamic Node type, a lot of work has been
done to ensure Node definitions can be dynamic too, but still allow
to be stored in files, and allow to be verified for type-definition
changes on reloading. This system needs a little bit maturing still,
so the Python gurus should better wait a little bit! (Also for me to
write the definite API docs for it).
What works now:
- Press CTRL+G to create a new Group. The grouping code checks for
impossible selections (like an unselected node between selected nodes).
Everthing that's selected then gets removed from the current tree, and
inserted in a new NodeTree library data block. A Group Node then is
added which links to this new NodeTree.
- Press ALT+G to ungroup. This will not delete the NodeTree library
data, but just duplicate the Group into the current tree.
- Press TAB, or click on the NodeTree icon to edit Groups. Note that
NodeTrees are instances, so editing one Group will also change the
other users.
This also means that when removing nodes in a Group (or hiding sockets
or changing internal links) this is immediately corrected for all users
of this Group, also in other Materials.
- While editing Groups, only the internal Nodes can be edited. A single
click outside of the Group boundary will close this 'edit mode'.
What needs to be done:
- SHIFT+A menu in toolbox style, also including a list of Groups
- Enable the single-user button in the Group Node
- Displaying all (visible) internal group UI elements in the Node Panel
- Enable Library linking and prevent editing of Groups then.
**** NEW: Socket Visibility control
Node types will be generated with a lot of possible inputs or outputs,
and drawing all sockets all the time isn't very useful then.
A new option in the Node header ('plus' icon) allows to either hide all
unused sockets (first keypress) or to reveil them (when there are hidden
sockets, the icon displays black, otherwise it's blended).
Hidden sockets in Nodes also are not exported to a Group, so this way
you can control what options (in/outputs) exactly are available.
To be done:
- a way to hide individual sockets, like with a RMB click on it.
**** NEW: Nodes now render!
This is still quite primitive, more on a level to replace the (now
obsolete and disabled) Material Layers.
What needs to be done:
- make the "Geometry" node work properly, also for AA textures
- make the Texture Node work (does very little at the moment)
- give Material Nodes all inputs as needed (like Map-to Panel)
- find a way to export more data from a Material Node, like the
shadow value, or light intensity only, etc
Very important also to separate from the Material Buttons the
"global" options, like "Ztransp" or "Wire" or "Halo". These can not
be set for each Material-Node individually.
Also note that the Preview Render (Buttons window) now renders a bit
differently. This was a horrid piece of antique code, using a totally
incompatible way of rendering. Target is to fully re-use internal
render code for previews.
OK... that's it mostly. Now test!
2006-01-02 13:06:05 +00:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
return nullptr;
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
static void *socket_value_storage(bNodeSocket &socket)
|
2023-05-15 15:14:22 +02:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
switch (eNodeSocketDatatype(socket.type)) {
|
|
|
|
|
case SOCK_BOOLEAN:
|
|
|
|
|
return &socket.default_value_typed<bNodeSocketValueBoolean>()->value;
|
|
|
|
|
case SOCK_INT:
|
|
|
|
|
return &socket.default_value_typed<bNodeSocketValueInt>()->value;
|
|
|
|
|
case SOCK_FLOAT:
|
|
|
|
|
return &socket.default_value_typed<bNodeSocketValueFloat>()->value;
|
|
|
|
|
case SOCK_VECTOR:
|
|
|
|
|
return &socket.default_value_typed<bNodeSocketValueVector>()->value;
|
|
|
|
|
case SOCK_RGBA:
|
|
|
|
|
return &socket.default_value_typed<bNodeSocketValueRGBA>()->value;
|
|
|
|
|
case SOCK_IMAGE:
|
|
|
|
|
return &socket.default_value_typed<bNodeSocketValueImage>()->value;
|
|
|
|
|
case SOCK_TEXTURE:
|
|
|
|
|
return &socket.default_value_typed<bNodeSocketValueTexture>()->value;
|
|
|
|
|
case SOCK_COLLECTION:
|
|
|
|
|
return &socket.default_value_typed<bNodeSocketValueCollection>()->value;
|
|
|
|
|
case SOCK_OBJECT:
|
|
|
|
|
return &socket.default_value_typed<bNodeSocketValueObject>()->value;
|
|
|
|
|
case SOCK_MATERIAL:
|
|
|
|
|
return &socket.default_value_typed<bNodeSocketValueMaterial>()->value;
|
2023-06-14 20:30:10 +02:00
|
|
|
case SOCK_ROTATION:
|
2023-09-19 19:42:55 +02:00
|
|
|
return &socket.default_value_typed<bNodeSocketValueRotation>()->value_euler;
|
2024-01-26 12:40:01 +01:00
|
|
|
case SOCK_MENU:
|
|
|
|
|
return &socket.default_value_typed<bNodeSocketValueMenu>()->value;
|
2024-02-13 18:59:36 +01:00
|
|
|
case SOCK_MATRIX:
|
|
|
|
|
/* Matrix sockets currently have no default value. */
|
|
|
|
|
return nullptr;
|
2023-09-19 19:42:55 +02:00
|
|
|
case SOCK_STRING:
|
2023-05-16 09:39:42 -04:00
|
|
|
/* We don't want do this now! */
|
|
|
|
|
return nullptr;
|
|
|
|
|
case SOCK_CUSTOM:
|
|
|
|
|
case SOCK_SHADER:
|
|
|
|
|
case SOCK_GEOMETRY:
|
|
|
|
|
/* Unmovable types. */
|
|
|
|
|
break;
|
|
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
return nullptr;
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void node_socket_move_default_value(Main & /*bmain*/,
|
|
|
|
|
bNodeTree &tree,
|
|
|
|
|
bNodeSocket &src,
|
|
|
|
|
bNodeSocket &dst)
|
|
|
|
|
{
|
|
|
|
|
tree.ensure_topology_cache();
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
bNode &dst_node = dst.owner_node();
|
|
|
|
|
bNode &src_node = src.owner_node();
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
const CPPType &src_type = *src.typeinfo->base_cpp_type;
|
|
|
|
|
const CPPType &dst_type = *dst.typeinfo->base_cpp_type;
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
const bke::DataTypeConversions &convert = bke::get_implicit_type_conversions();
|
2018-06-17 17:05:51 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
if (src.is_multi_input()) {
|
|
|
|
|
/* Multi input sockets no have value. */
|
|
|
|
|
return;
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
if (ELEM(NODE_REROUTE, dst_node.type, src_node.type)) {
|
|
|
|
|
/* Reroute node can't have ownership of socket value directly. */
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (&src_type != &dst_type) {
|
|
|
|
|
if (!convert.is_convertible(src_type, dst_type)) {
|
|
|
|
|
return;
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2013-03-18 16:34:57 +00:00
|
|
|
}
|
2016-11-19 12:16:14 +01:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void *src_value = socket_value_storage(src);
|
|
|
|
|
void *dst_value = node_static_value_storage_for(dst_node, dst);
|
|
|
|
|
if (!dst_value || !src_value) {
|
|
|
|
|
return;
|
2013-03-18 16:34:57 +00:00
|
|
|
}
|
2018-06-17 17:05:51 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
convert.convert_to_uninitialized(src_type, dst_type, src_value, dst_value);
|
Christmas coding work!
********* Node editor work:
- To enable Nodes for Materials, you have to set the "Use Nodes"
button, in the new Material buttons "Nodes" Panel or in header
of the Node editor. Doing this will disable Material-Layers.
- Nodes now execute materials ("shaders"), but still only using the
previewrender code.
- Nodes have (optional) previews for rendered images.
- Node headers allow to hide buttons and/or preview image
- Nodes can be dragged larger/smaller (right-bottom corner)
- Nodes can be hidden (minimized) with hotkey H
- CTRL+click on an Input Socket gives a popup with default values.
- Changing Material/Texture or Mix node will adjust Node title.
- Click-drag outside of a Node changes cursor to "Knife' and allows to
draw a rect where to cut Links.
- Added new node types RGBtoBW, Texture, In/Output, ColorRamp
- Material Nodes have options to ouput diffuse or specular, or to use
a negative normal. The input socket 'Normal' will force the material
to use that normal, otherwise it uses the normal from the Material
that has the node tree.
- When drawing a link between two not-matching sockets, Blender inserts
a converting node (now only for value/rgb combos)
- When drawing a link to an input socket that's already in use, the
old link will either disappear or flip to another unused socket.
- A click on a Material Node will activate it, and show all its settings
in the Material Buttons. Active Material Nodes draw the material icon
in red.
- A click on any node will show its options in the Node Panel in the
Material buttons.
- Multiple Output Nodes can be used, to sample contents of a tree, but
only one Output is the real one, which is indicated in a different
color and red material icon.
- Added ThemeColors for node types
- ALT+C will convert existing Material-Layers to Node... this currently
only adds the material/mix nodes and connects them. Dunno if this is
worth a lot of coding work to make perfect?
- Press C to call another "Solve order", which will show all possible
cyclic conflicts (if there are).
- Technical: nodes now use "Type" structs which define the
structure of nodes and in/output sockets. The Type structs store all
fixed info, callbacks, and allow to reconstruct saved Nodes to match
what is required by Blender.
- Defining (new) nodes now is as simple as filling in a fixed
Type struct, plus code some callbacks. A doc will be made!
- Node preview images are by default float
********* Icon drawing:
- Cleanup of how old icons were implemented in new system, making
them 16x16 too, correctly centered *and* scaled.
- Made drawing Icons use float coordinates
- Moved BIF_calcpreview_image() into interface_icons.c, renamed it
icon_from_image(). Removed a lot of unneeded Imbuf magic here! :)
- Skipped scaling and imbuf copying when icons are OK size
********* Preview render:
- Huge cleanup of code....
- renaming BIF_xxx calls that only were used internally
- BIF_previewrender() now accepts an argument for rendering method,
so it supports icons, buttonwindow previewrender and node editor
- Only a single BIF_preview_changed() call now exists, supporting all
signals as needed for buttos and node editor
********* More stuff:
- glutil.c, glaDrawPixelsSafe() and glaDrawPixelsTex() now accept format
argument for GL_FLOAT rects
- Made the ColorBand become a built-in button for interface.c
Was a load of cleanup work in buttons_shading.c...
- removed a load of unneeded glBlendFunc() calls
- Fixed bug in calculating text length for buttons (ancient!)
2005-12-28 15:42:51 +00:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
src_type.destruct(src_value);
|
|
|
|
|
if (ELEM(eNodeSocketDatatype(src.type),
|
|
|
|
|
SOCK_COLLECTION,
|
|
|
|
|
SOCK_IMAGE,
|
|
|
|
|
SOCK_MATERIAL,
|
|
|
|
|
SOCK_TEXTURE,
|
|
|
|
|
SOCK_OBJECT))
|
|
|
|
|
{
|
|
|
|
|
src_type.value_initialize(src_value);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2009-12-17 04:55:15 +00:00
|
|
|
}
|
Orange: more noodle updates!
**** NEW: Group Nodes
Node trees usually become messy and confusing quickly, so we need
not only a way to collapse Nodes into single 'groups', but also a
way to re-use that data to create libraries of effects.
This has been done by making a new Library data type, the NodeTree.
Everything that has been grouped is stored here, and available for
re-use, appending or linking. These NodeTrees are fully generic,
i.e. can store shader trees, composit trees, and so on. The 'type'
value as stored in the NodeTree will keep track of internal type
definitions and execute/drawing callbacks. Needless to say, re-using
shader trees in a composit tree is a bit useless, and will be
prevented in the browsing code. :)
So; any NodeTree can become a "Goup Node" inside in a NodeTree. This
Group Node then works just like any Node.
To prevent the current code to become too complex, I've disabled
the possibility to insert Groups inside of Groups. That might be
enabled later, but is a real nasty piece of code to get OK.
Since Group Nodes are a dynamic Node type, a lot of work has been
done to ensure Node definitions can be dynamic too, but still allow
to be stored in files, and allow to be verified for type-definition
changes on reloading. This system needs a little bit maturing still,
so the Python gurus should better wait a little bit! (Also for me to
write the definite API docs for it).
What works now:
- Press CTRL+G to create a new Group. The grouping code checks for
impossible selections (like an unselected node between selected nodes).
Everthing that's selected then gets removed from the current tree, and
inserted in a new NodeTree library data block. A Group Node then is
added which links to this new NodeTree.
- Press ALT+G to ungroup. This will not delete the NodeTree library
data, but just duplicate the Group into the current tree.
- Press TAB, or click on the NodeTree icon to edit Groups. Note that
NodeTrees are instances, so editing one Group will also change the
other users.
This also means that when removing nodes in a Group (or hiding sockets
or changing internal links) this is immediately corrected for all users
of this Group, also in other Materials.
- While editing Groups, only the internal Nodes can be edited. A single
click outside of the Group boundary will close this 'edit mode'.
What needs to be done:
- SHIFT+A menu in toolbox style, also including a list of Groups
- Enable the single-user button in the Group Node
- Displaying all (visible) internal group UI elements in the Node Panel
- Enable Library linking and prevent editing of Groups then.
**** NEW: Socket Visibility control
Node types will be generated with a lot of possible inputs or outputs,
and drawing all sockets all the time isn't very useful then.
A new option in the Node header ('plus' icon) allows to either hide all
unused sockets (first keypress) or to reveil them (when there are hidden
sockets, the icon displays black, otherwise it's blended).
Hidden sockets in Nodes also are not exported to a Group, so this way
you can control what options (in/outputs) exactly are available.
To be done:
- a way to hide individual sockets, like with a RMB click on it.
**** NEW: Nodes now render!
This is still quite primitive, more on a level to replace the (now
obsolete and disabled) Material Layers.
What needs to be done:
- make the "Geometry" node work properly, also for AA textures
- make the Texture Node work (does very little at the moment)
- give Material Nodes all inputs as needed (like Map-to Panel)
- find a way to export more data from a Material Node, like the
shadow value, or light intensity only, etc
Very important also to separate from the Material Buttons the
"global" options, like "Ztransp" or "Wire" or "Halo". These can not
be set for each Material-Node individually.
Also note that the Preview Render (Buttons window) now renders a bit
differently. This was a horrid piece of antique code, using a totally
incompatible way of rendering. Target is to fully re-use internal
render code for previews.
OK... that's it mostly. Now test!
2006-01-02 13:06:05 +00:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
bNode *node_copy(bNodeTree *dst_tree, const bNode &src_node, const int flag, const bool use_unique)
|
Orange: more noodle updates!
**** NEW: Group Nodes
Node trees usually become messy and confusing quickly, so we need
not only a way to collapse Nodes into single 'groups', but also a
way to re-use that data to create libraries of effects.
This has been done by making a new Library data type, the NodeTree.
Everything that has been grouped is stored here, and available for
re-use, appending or linking. These NodeTrees are fully generic,
i.e. can store shader trees, composit trees, and so on. The 'type'
value as stored in the NodeTree will keep track of internal type
definitions and execute/drawing callbacks. Needless to say, re-using
shader trees in a composit tree is a bit useless, and will be
prevented in the browsing code. :)
So; any NodeTree can become a "Goup Node" inside in a NodeTree. This
Group Node then works just like any Node.
To prevent the current code to become too complex, I've disabled
the possibility to insert Groups inside of Groups. That might be
enabled later, but is a real nasty piece of code to get OK.
Since Group Nodes are a dynamic Node type, a lot of work has been
done to ensure Node definitions can be dynamic too, but still allow
to be stored in files, and allow to be verified for type-definition
changes on reloading. This system needs a little bit maturing still,
so the Python gurus should better wait a little bit! (Also for me to
write the definite API docs for it).
What works now:
- Press CTRL+G to create a new Group. The grouping code checks for
impossible selections (like an unselected node between selected nodes).
Everthing that's selected then gets removed from the current tree, and
inserted in a new NodeTree library data block. A Group Node then is
added which links to this new NodeTree.
- Press ALT+G to ungroup. This will not delete the NodeTree library
data, but just duplicate the Group into the current tree.
- Press TAB, or click on the NodeTree icon to edit Groups. Note that
NodeTrees are instances, so editing one Group will also change the
other users.
This also means that when removing nodes in a Group (or hiding sockets
or changing internal links) this is immediately corrected for all users
of this Group, also in other Materials.
- While editing Groups, only the internal Nodes can be edited. A single
click outside of the Group boundary will close this 'edit mode'.
What needs to be done:
- SHIFT+A menu in toolbox style, also including a list of Groups
- Enable the single-user button in the Group Node
- Displaying all (visible) internal group UI elements in the Node Panel
- Enable Library linking and prevent editing of Groups then.
**** NEW: Socket Visibility control
Node types will be generated with a lot of possible inputs or outputs,
and drawing all sockets all the time isn't very useful then.
A new option in the Node header ('plus' icon) allows to either hide all
unused sockets (first keypress) or to reveil them (when there are hidden
sockets, the icon displays black, otherwise it's blended).
Hidden sockets in Nodes also are not exported to a Group, so this way
you can control what options (in/outputs) exactly are available.
To be done:
- a way to hide individual sockets, like with a RMB click on it.
**** NEW: Nodes now render!
This is still quite primitive, more on a level to replace the (now
obsolete and disabled) Material Layers.
What needs to be done:
- make the "Geometry" node work properly, also for AA textures
- make the Texture Node work (does very little at the moment)
- give Material Nodes all inputs as needed (like Map-to Panel)
- find a way to export more data from a Material Node, like the
shadow value, or light intensity only, etc
Very important also to separate from the Material Buttons the
"global" options, like "Ztransp" or "Wire" or "Halo". These can not
be set for each Material-Node individually.
Also note that the Preview Render (Buttons window) now renders a bit
differently. This was a horrid piece of antique code, using a totally
incompatible way of rendering. Target is to fully re-use internal
render code for previews.
OK... that's it mostly. Now test!
2006-01-02 13:06:05 +00:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
Map<const bNodeSocket *, bNodeSocket *> socket_map;
|
|
|
|
|
return node_copy_with_mapping(dst_tree, src_node, flag, use_unique, socket_map);
|
|
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
static int node_count_links(const bNodeTree *ntree, const bNodeSocket *socket)
|
|
|
|
|
{
|
|
|
|
|
int count = 0;
|
|
|
|
|
LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
|
|
|
|
|
if (ELEM(socket, link->fromsock, link->tosock)) {
|
|
|
|
|
count++;
|
|
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
return count;
|
2022-11-11 11:48:56 +01:00
|
|
|
}
|
2018-06-17 17:05:51 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
bNodeLink *nodeAddLink(
|
|
|
|
|
bNodeTree *ntree, bNode *fromnode, bNodeSocket *fromsock, bNode *tonode, bNodeSocket *tosock)
|
2022-11-11 11:48:56 +01:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
BLI_assert(fromnode);
|
|
|
|
|
BLI_assert(tonode);
|
|
|
|
|
BLI_assert(ntree->all_nodes().contains(fromnode));
|
|
|
|
|
BLI_assert(ntree->all_nodes().contains(tonode));
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
bNodeLink *link = nullptr;
|
|
|
|
|
if (eNodeSocketInOut(fromsock->in_out) == SOCK_OUT &&
|
2024-01-02 18:12:54 +01:00
|
|
|
eNodeSocketInOut(tosock->in_out) == SOCK_IN)
|
|
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
link = MEM_cnew<bNodeLink>("link");
|
|
|
|
|
if (ntree) {
|
|
|
|
|
BLI_addtail(&ntree->links, link);
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
link->fromnode = fromnode;
|
|
|
|
|
link->fromsock = fromsock;
|
|
|
|
|
link->tonode = tonode;
|
|
|
|
|
link->tosock = tosock;
|
|
|
|
|
}
|
|
|
|
|
else if (eNodeSocketInOut(fromsock->in_out) == SOCK_IN &&
|
|
|
|
|
eNodeSocketInOut(tosock->in_out) == SOCK_OUT)
|
|
|
|
|
{
|
|
|
|
|
/* OK but flip */
|
|
|
|
|
link = MEM_cnew<bNodeLink>("link");
|
|
|
|
|
if (ntree) {
|
|
|
|
|
BLI_addtail(&ntree->links, link);
|
|
|
|
|
}
|
|
|
|
|
link->fromnode = tonode;
|
|
|
|
|
link->fromsock = tosock;
|
|
|
|
|
link->tonode = fromnode;
|
|
|
|
|
link->tosock = fromsock;
|
Orange: more noodle updates!
**** NEW: Group Nodes
Node trees usually become messy and confusing quickly, so we need
not only a way to collapse Nodes into single 'groups', but also a
way to re-use that data to create libraries of effects.
This has been done by making a new Library data type, the NodeTree.
Everything that has been grouped is stored here, and available for
re-use, appending or linking. These NodeTrees are fully generic,
i.e. can store shader trees, composit trees, and so on. The 'type'
value as stored in the NodeTree will keep track of internal type
definitions and execute/drawing callbacks. Needless to say, re-using
shader trees in a composit tree is a bit useless, and will be
prevented in the browsing code. :)
So; any NodeTree can become a "Goup Node" inside in a NodeTree. This
Group Node then works just like any Node.
To prevent the current code to become too complex, I've disabled
the possibility to insert Groups inside of Groups. That might be
enabled later, but is a real nasty piece of code to get OK.
Since Group Nodes are a dynamic Node type, a lot of work has been
done to ensure Node definitions can be dynamic too, but still allow
to be stored in files, and allow to be verified for type-definition
changes on reloading. This system needs a little bit maturing still,
so the Python gurus should better wait a little bit! (Also for me to
write the definite API docs for it).
What works now:
- Press CTRL+G to create a new Group. The grouping code checks for
impossible selections (like an unselected node between selected nodes).
Everthing that's selected then gets removed from the current tree, and
inserted in a new NodeTree library data block. A Group Node then is
added which links to this new NodeTree.
- Press ALT+G to ungroup. This will not delete the NodeTree library
data, but just duplicate the Group into the current tree.
- Press TAB, or click on the NodeTree icon to edit Groups. Note that
NodeTrees are instances, so editing one Group will also change the
other users.
This also means that when removing nodes in a Group (or hiding sockets
or changing internal links) this is immediately corrected for all users
of this Group, also in other Materials.
- While editing Groups, only the internal Nodes can be edited. A single
click outside of the Group boundary will close this 'edit mode'.
What needs to be done:
- SHIFT+A menu in toolbox style, also including a list of Groups
- Enable the single-user button in the Group Node
- Displaying all (visible) internal group UI elements in the Node Panel
- Enable Library linking and prevent editing of Groups then.
**** NEW: Socket Visibility control
Node types will be generated with a lot of possible inputs or outputs,
and drawing all sockets all the time isn't very useful then.
A new option in the Node header ('plus' icon) allows to either hide all
unused sockets (first keypress) or to reveil them (when there are hidden
sockets, the icon displays black, otherwise it's blended).
Hidden sockets in Nodes also are not exported to a Group, so this way
you can control what options (in/outputs) exactly are available.
To be done:
- a way to hide individual sockets, like with a RMB click on it.
**** NEW: Nodes now render!
This is still quite primitive, more on a level to replace the (now
obsolete and disabled) Material Layers.
What needs to be done:
- make the "Geometry" node work properly, also for AA textures
- make the Texture Node work (does very little at the moment)
- give Material Nodes all inputs as needed (like Map-to Panel)
- find a way to export more data from a Material Node, like the
shadow value, or light intensity only, etc
Very important also to separate from the Material Buttons the
"global" options, like "Ztransp" or "Wire" or "Halo". These can not
be set for each Material-Node individually.
Also note that the Preview Render (Buttons window) now renders a bit
differently. This was a horrid piece of antique code, using a totally
incompatible way of rendering. Target is to fully re-use internal
render code for previews.
OK... that's it mostly. Now test!
2006-01-02 13:06:05 +00:00
|
|
|
}
|
Orange; daily noodler update commit.
- Adding execution code for Node trees. Was a bit a puzzle, since I want
it to be multithreading by design. This now is solved by defining a
stack per tree for all data that's being written into. This stack, which
resides now in the NodeTree itself, then can be allocated per thread.
- For testing pleasure, I've added a 'mix node' and a 'show node', so
you can already see it do something. :)
- reshuffled structure, to put things nice together, and have easier node
adding. Current state is still WIP though, structure might change.
For the record; new file node_shaders.c will contain all shader node
definitions, apart from the drawing callbacks.
Next: I'm going to check on Andrea's work on icons now, since this is very
much needed for true shader/composit work.
Now back to release work...
2005-12-21 14:24:51 +00:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
if (ntree) {
|
|
|
|
|
BKE_ntree_update_tag_link_added(ntree, link);
|
2015-08-01 16:16:16 +02:00
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
if (link != nullptr && link->tosock->is_multi_input()) {
|
2024-03-19 13:42:09 +01:00
|
|
|
link->multi_input_sort_id = node_count_links(ntree, link->tosock) - 1;
|
2023-05-16 09:39:42 -04:00
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
return link;
|
2015-08-01 16:16:16 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void nodeRemLink(bNodeTree *ntree, bNodeLink *link)
|
2015-08-01 16:16:16 +02:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
/* Can be called for links outside a node tree (e.g. clipboard). */
|
|
|
|
|
if (ntree) {
|
|
|
|
|
BLI_remlink(&ntree->links, link);
|
|
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
if (link->tosock) {
|
|
|
|
|
link->tosock->link = nullptr;
|
|
|
|
|
}
|
|
|
|
|
MEM_freeN(link);
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
if (ntree) {
|
|
|
|
|
BKE_ntree_update_tag_link_removed(ntree);
|
2015-08-01 16:16:16 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void nodeLinkSetMute(bNodeTree *ntree, bNodeLink *link, const bool muted)
|
|
|
|
|
{
|
|
|
|
|
const bool was_muted = link->is_muted();
|
|
|
|
|
SET_FLAG_FROM_TEST(link->flag, muted, NODE_LINK_MUTED);
|
|
|
|
|
if (muted != was_muted) {
|
|
|
|
|
BKE_ntree_update_tag_link_mute(ntree, link);
|
2015-08-01 16:16:16 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void nodeRemSocketLinks(bNodeTree *ntree, bNodeSocket *sock)
|
2019-09-30 13:09:39 +02:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree->links) {
|
|
|
|
|
if (link->fromsock == sock || link->tosock == sock) {
|
|
|
|
|
nodeRemLink(ntree, link);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
bool nodeLinkIsHidden(const bNodeLink *link)
|
|
|
|
|
{
|
2023-09-19 10:47:21 +02:00
|
|
|
return !(link->fromsock->is_visible() && link->tosock->is_visible());
|
2023-05-16 09:39:42 -04:00
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
bool nodeLinkIsSelected(const bNodeLink *link)
|
|
|
|
|
{
|
|
|
|
|
return (link->fromnode->flag & NODE_SELECT) || (link->tonode->flag & NODE_SELECT);
|
|
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/* Adjust the indices of links connected to the given multi input socket after deleting the link at
|
|
|
|
|
* `deleted_index`. This function also works if the link has not yet been deleted. */
|
|
|
|
|
static void adjust_multi_input_indices_after_removed_link(bNodeTree *ntree,
|
|
|
|
|
const bNodeSocket *sock,
|
|
|
|
|
const int deleted_index)
|
|
|
|
|
{
|
|
|
|
|
LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
|
|
|
|
|
/* We only need to adjust those with a greater index, because the others will have the same
|
|
|
|
|
* index. */
|
2024-03-19 13:42:09 +01:00
|
|
|
if (link->tosock != sock || link->multi_input_sort_id <= deleted_index) {
|
2023-05-16 09:39:42 -04:00
|
|
|
continue;
|
2019-09-30 13:09:39 +02:00
|
|
|
}
|
2024-03-19 13:42:09 +01:00
|
|
|
link->multi_input_sort_id -= 1;
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void nodeInternalRelink(bNodeTree *ntree, bNode *node)
|
2023-05-15 15:14:22 +02:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
/* store link pointers in output sockets, for efficient lookup */
|
|
|
|
|
for (bNodeLink &link : node->runtime->internal_links) {
|
|
|
|
|
link.tosock->link = &link;
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2019-09-30 13:09:39 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
Vector<bNodeLink *> duplicate_links_to_remove;
|
|
|
|
|
|
|
|
|
|
/* redirect downstream links */
|
|
|
|
|
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree->links) {
|
|
|
|
|
/* do we have internal link? */
|
|
|
|
|
if (link->fromnode != node) {
|
|
|
|
|
continue;
|
2019-09-30 13:09:39 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
bNodeLink *internal_link = link->fromsock->link;
|
|
|
|
|
bNodeLink *fromlink = internal_link ? internal_link->fromsock->link : nullptr;
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
if (fromlink == nullptr) {
|
|
|
|
|
if (link->tosock->is_multi_input()) {
|
2024-05-13 16:07:12 +02:00
|
|
|
adjust_multi_input_indices_after_removed_link(
|
2024-03-19 13:42:09 +01:00
|
|
|
ntree, link->tosock, link->multi_input_sort_id);
|
2023-05-16 09:39:42 -04:00
|
|
|
}
|
|
|
|
|
nodeRemLink(ntree, link);
|
|
|
|
|
continue;
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2019-10-02 12:50:14 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
if (link->tosock->is_multi_input()) {
|
|
|
|
|
/* remove the link that would be the same as the relinked one */
|
|
|
|
|
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link_to_compare, &ntree->links) {
|
|
|
|
|
if (link_to_compare->fromsock == fromlink->fromsock &&
|
2024-01-02 18:12:54 +01:00
|
|
|
link_to_compare->tosock == link->tosock)
|
|
|
|
|
{
|
2024-05-13 16:07:12 +02:00
|
|
|
adjust_multi_input_indices_after_removed_link(
|
2024-03-19 13:42:09 +01:00
|
|
|
ntree, link_to_compare->tosock, link_to_compare->multi_input_sort_id);
|
2023-05-16 09:39:42 -04:00
|
|
|
duplicate_links_to_remove.append_non_duplicates(link_to_compare);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-10-10 00:31:47 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
link->fromnode = fromlink->fromnode;
|
|
|
|
|
link->fromsock = fromlink->fromsock;
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/* if the up- or downstream link is invalid,
|
|
|
|
|
* the replacement link will be invalid too.
|
|
|
|
|
*/
|
|
|
|
|
if (!(fromlink->flag & NODE_LINK_VALID)) {
|
|
|
|
|
link->flag &= ~NODE_LINK_VALID;
|
|
|
|
|
}
|
2019-09-30 13:09:39 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
if (fromlink->flag & NODE_LINK_MUTED) {
|
|
|
|
|
link->flag |= NODE_LINK_MUTED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BKE_ntree_update_tag_link_changed(ntree);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2019-09-30 13:09:39 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
for (bNodeLink *link : duplicate_links_to_remove) {
|
|
|
|
|
nodeRemLink(ntree, link);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/* remove remaining upstream links */
|
|
|
|
|
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree->links) {
|
|
|
|
|
if (link->tonode == node) {
|
|
|
|
|
nodeRemLink(ntree, link);
|
|
|
|
|
}
|
2015-08-01 16:16:16 +02:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-06-15 16:04:23 +02:00
|
|
|
float2 nodeToView(const bNode *node, const float2 loc)
|
2023-05-16 09:39:42 -04:00
|
|
|
{
|
2023-06-15 16:04:23 +02:00
|
|
|
float2 view_loc = loc;
|
2023-05-16 09:39:42 -04:00
|
|
|
for (const bNode *node_iter = node; node_iter; node_iter = node_iter->parent) {
|
2023-06-15 16:04:23 +02:00
|
|
|
view_loc += float2(node_iter->locx, node_iter->locy);
|
2023-05-16 09:39:42 -04:00
|
|
|
}
|
2023-06-15 16:04:23 +02:00
|
|
|
return view_loc;
|
2015-08-01 16:16:16 +02:00
|
|
|
}
|
|
|
|
|
|
2023-06-15 16:04:23 +02:00
|
|
|
float2 nodeFromView(const bNode *node, const float2 view_loc)
|
2022-10-17 12:00:09 +02:00
|
|
|
{
|
2023-06-15 16:04:23 +02:00
|
|
|
float2 loc = view_loc;
|
2023-05-16 09:39:42 -04:00
|
|
|
for (const bNode *node_iter = node; node_iter; node_iter = node_iter->parent) {
|
2023-06-15 16:04:23 +02:00
|
|
|
loc -= float2(node_iter->locx, node_iter->locy);
|
2022-10-17 12:00:09 +02:00
|
|
|
}
|
2023-06-15 16:04:23 +02:00
|
|
|
return loc;
|
2023-05-16 09:39:42 -04:00
|
|
|
}
|
2022-10-17 12:00:09 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void nodeAttachNode(bNodeTree *ntree, bNode *node, bNode *parent)
|
|
|
|
|
{
|
|
|
|
|
BLI_assert(parent->type == NODE_FRAME);
|
|
|
|
|
BLI_assert(!nodeIsParentAndChild(parent, node));
|
2022-10-17 12:00:09 +02:00
|
|
|
|
2024-05-13 16:07:12 +02:00
|
|
|
const float2 loc = nodeToView(node, {});
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
node->parent = parent;
|
|
|
|
|
BKE_ntree_update_tag_parent_change(ntree, node);
|
|
|
|
|
/* transform to parent space */
|
2024-05-13 16:07:12 +02:00
|
|
|
const float2 new_loc = nodeFromView(parent, loc);
|
2023-06-15 16:04:23 +02:00
|
|
|
node->locx = new_loc.x;
|
|
|
|
|
node->locy = new_loc.y;
|
2009-11-11 09:11:21 +00:00
|
|
|
}
|
2008-02-09 23:17:15 +00:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void nodeDetachNode(bNodeTree *ntree, bNode *node)
|
2022-12-01 14:53:27 -06:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
if (node->parent) {
|
|
|
|
|
BLI_assert(node->parent->type == NODE_FRAME);
|
2022-12-01 14:53:27 -06:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/* transform to view space */
|
2024-05-13 16:07:12 +02:00
|
|
|
const float2 loc = nodeToView(node, {});
|
2023-06-15 16:04:23 +02:00
|
|
|
node->locx = loc.x;
|
|
|
|
|
node->locy = loc.y;
|
2023-05-16 09:39:42 -04:00
|
|
|
node->parent = nullptr;
|
|
|
|
|
BKE_ntree_update_tag_parent_change(ntree, node);
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-12-01 14:53:27 -06:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void nodePositionRelative(bNode *from_node,
|
|
|
|
|
const bNode *to_node,
|
|
|
|
|
const bNodeSocket *from_sock,
|
|
|
|
|
const bNodeSocket *to_sock)
|
|
|
|
|
{
|
|
|
|
|
float offset_x;
|
|
|
|
|
int tot_sock_idx;
|
2018-06-17 17:05:51 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/* Socket to plug into. */
|
|
|
|
|
if (eNodeSocketInOut(to_sock->in_out) == SOCK_IN) {
|
|
|
|
|
offset_x = -(from_node->typeinfo->width + 50);
|
|
|
|
|
tot_sock_idx = BLI_listbase_count(&to_node->outputs);
|
|
|
|
|
tot_sock_idx += BLI_findindex(&to_node->inputs, to_sock);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
else {
|
|
|
|
|
offset_x = to_node->typeinfo->width + 50;
|
|
|
|
|
tot_sock_idx = BLI_findindex(&to_node->outputs, to_sock);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2018-06-17 17:05:51 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
BLI_assert(tot_sock_idx != -1);
|
2021-12-29 19:40:00 +01:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
float offset_y = U.widget_unit * tot_sock_idx;
|
2008-02-09 23:17:15 +00:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/* Output socket. */
|
|
|
|
|
if (from_sock) {
|
|
|
|
|
if (eNodeSocketInOut(from_sock->in_out) == SOCK_IN) {
|
|
|
|
|
tot_sock_idx = BLI_listbase_count(&from_node->outputs);
|
|
|
|
|
tot_sock_idx += BLI_findindex(&from_node->inputs, from_sock);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
tot_sock_idx = BLI_findindex(&from_node->outputs, from_sock);
|
|
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
BLI_assert(tot_sock_idx != -1);
|
2023-04-16 22:53:09 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
offset_y -= U.widget_unit * tot_sock_idx;
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
from_node->locx = to_node->locx + offset_x;
|
|
|
|
|
from_node->locy = to_node->locy - offset_y;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void nodePositionPropagate(bNode *node)
|
|
|
|
|
{
|
|
|
|
|
LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) {
|
|
|
|
|
if (socket->link != nullptr) {
|
|
|
|
|
bNodeLink *link = socket->link;
|
|
|
|
|
nodePositionRelative(link->fromnode, link->tonode, link->fromsock, link->tosock);
|
|
|
|
|
nodePositionPropagate(link->fromnode);
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2013-03-18 16:34:57 +00:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
}
|
2013-03-18 16:34:57 +00:00
|
|
|
|
2024-04-16 17:35:22 +02:00
|
|
|
static bNodeTree *ntreeAddTree_do(Main *bmain,
|
|
|
|
|
std::optional<Library *> owner_library,
|
|
|
|
|
ID *owner_id,
|
|
|
|
|
const bool is_embedded,
|
|
|
|
|
const char *name,
|
|
|
|
|
const char *idname)
|
2023-05-16 09:39:42 -04:00
|
|
|
{
|
|
|
|
|
/* trees are created as local trees for compositor, material or texture nodes,
|
|
|
|
|
* node groups and other tree types are created as library data.
|
|
|
|
|
*/
|
|
|
|
|
int flag = 0;
|
|
|
|
|
if (is_embedded || bmain == nullptr) {
|
|
|
|
|
flag |= LIB_ID_CREATE_NO_MAIN;
|
|
|
|
|
}
|
2024-04-16 17:35:22 +02:00
|
|
|
BLI_assert_msg(!owner_library || !owner_id,
|
|
|
|
|
"Embedded NTrees should never have a defined owner library here");
|
|
|
|
|
bNodeTree *ntree = reinterpret_cast<bNodeTree *>(
|
|
|
|
|
BKE_libblock_alloc_in_lib(bmain, owner_library, ID_NT, name, flag));
|
2023-05-16 09:39:42 -04:00
|
|
|
BKE_libblock_init_empty(&ntree->id);
|
|
|
|
|
if (is_embedded) {
|
|
|
|
|
BLI_assert(owner_id != nullptr);
|
|
|
|
|
ntree->id.flag |= LIB_EMBEDDED_DATA;
|
|
|
|
|
ntree->owner_id = owner_id;
|
|
|
|
|
bNodeTree **ntree_owner_ptr = BKE_ntree_ptr_from_id(owner_id);
|
|
|
|
|
BLI_assert(ntree_owner_ptr != nullptr);
|
|
|
|
|
*ntree_owner_ptr = ntree;
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
else {
|
2023-05-16 09:39:42 -04:00
|
|
|
BLI_assert(owner_id == nullptr);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
|
|
|
|
|
STRNCPY(ntree->idname, idname);
|
2024-05-13 16:07:12 +02:00
|
|
|
ntree_set_typeinfo(ntree, ntreeTypeFind(idname));
|
2023-05-16 09:39:42 -04:00
|
|
|
|
|
|
|
|
return ntree;
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2020-04-20 13:22:20 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
bNodeTree *ntreeAddTree(Main *bmain, const char *name, const char *idname)
|
2023-05-15 15:14:22 +02:00
|
|
|
{
|
2024-04-16 17:35:22 +02:00
|
|
|
return ntreeAddTree_do(bmain, std::nullopt, nullptr, false, name, idname);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bNodeTree *BKE_node_tree_add_in_lib(Main *bmain,
|
|
|
|
|
Library *owner_library,
|
|
|
|
|
const char *name,
|
|
|
|
|
const char *idname)
|
|
|
|
|
{
|
|
|
|
|
return ntreeAddTree_do(bmain, owner_library, nullptr, false, name, idname);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
bNodeTree *ntreeAddTreeEmbedded(Main * /*bmain*/,
|
|
|
|
|
ID *owner_id,
|
|
|
|
|
const char *name,
|
|
|
|
|
const char *idname)
|
2023-05-15 15:14:22 +02:00
|
|
|
{
|
2024-04-16 17:35:22 +02:00
|
|
|
return ntreeAddTree_do(nullptr, std::nullopt, owner_id, true, name, idname);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
Refactor ID copying (and to some extent, ID freeing).
This will allow much finer controll over how we copy data-blocks, from
full copy in Main database, to "lighter" ones (out of Main, inside an
already allocated datablock, etc.).
This commit also transfers a llot of what was previously handled by
per-ID-type custom code to generic ID handling code in BKE_library.
Hopefully will avoid in future inconsistencies and missing bits we had
all over the codebase in the past.
It also adds missing copying handling for a few types, most notably
Scene (which where using a fully customized handling previously).
Note that the type of allocation used during copying (regular in Main,
allocated but outside of Main, or not allocated by ID handling code at
all) is stored in ID's, which allows to handle them correctly when
freeing. This needs to be taken care of with caution when doing 'weird'
unusual things with ID copying and/or allocation!
As a final note, while rather noisy, this commit will hopefully not
break too much existing branches, old 'API' has been kept for the main
part, as a wrapper around new code. Cleaning it up will happen later.
Design task : T51804
Phab Diff: D2714
2017-08-07 16:39:55 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
bNodeTree *ntreeCopyTree_ex(const bNodeTree *ntree, Main *bmain, const bool do_id_user)
|
2023-05-15 15:14:22 +02:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
const int flag = do_id_user ? 0 : LIB_ID_CREATE_NO_USER_REFCOUNT | LIB_ID_CREATE_NO_MAIN;
|
2013-03-18 16:34:57 +00:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
bNodeTree *ntree_copy = reinterpret_cast<bNodeTree *>(
|
|
|
|
|
BKE_id_copy_ex(bmain, reinterpret_cast<const ID *>(ntree), nullptr, flag));
|
|
|
|
|
return ntree_copy;
|
|
|
|
|
}
|
|
|
|
|
bNodeTree *ntreeCopyTree(Main *bmain, const bNodeTree *ntree)
|
2023-05-15 15:14:22 +02:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
return ntreeCopyTree_ex(ntree, bmain, true);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/* *************** Node Preview *********** */
|
2021-09-14 16:34:31 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/* XXX this should be removed eventually ...
|
|
|
|
|
* Currently BKE functions are modeled closely on previous code,
|
|
|
|
|
* using node_preview_init_tree to set up previews for a whole node tree in advance.
|
|
|
|
|
* This should be left more to the individual node tree implementations. */
|
2022-05-30 15:31:13 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
bool node_preview_used(const bNode *node)
|
|
|
|
|
{
|
|
|
|
|
/* XXX check for closed nodes? */
|
|
|
|
|
return (node->typeinfo->flag & NODE_PREVIEW) != 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bNodePreview *node_preview_verify(bNodeInstanceHash *previews,
|
|
|
|
|
bNodeInstanceKey key,
|
|
|
|
|
const int xsize,
|
|
|
|
|
const int ysize,
|
|
|
|
|
const bool create)
|
|
|
|
|
{
|
|
|
|
|
bNodePreview *preview = static_cast<bNodePreview *>(
|
|
|
|
|
BKE_node_instance_hash_lookup(previews, key));
|
|
|
|
|
if (!preview) {
|
|
|
|
|
if (create) {
|
|
|
|
|
preview = MEM_cnew<bNodePreview>("node preview");
|
2023-07-17 20:13:52 +02:00
|
|
|
preview->ibuf = IMB_allocImBuf(xsize, ysize, 32, IB_rect);
|
2023-05-16 09:39:42 -04:00
|
|
|
BKE_node_instance_hash_insert(previews, key, preview);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
return nullptr;
|
2020-01-16 15:04:06 +01:00
|
|
|
}
|
2012-08-02 09:52:37 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/* node previews can get added with variable size this way */
|
|
|
|
|
if (xsize == 0 || ysize == 0) {
|
|
|
|
|
return preview;
|
2012-10-25 16:49:06 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/* sanity checks & initialize */
|
2023-07-18 14:18:07 +10:00
|
|
|
const uint size[2] = {uint(xsize), uint(ysize)};
|
2023-07-17 20:13:52 +02:00
|
|
|
IMB_rect_size_set(preview->ibuf, size);
|
|
|
|
|
if (preview->ibuf->byte_buffer.data == nullptr) {
|
|
|
|
|
imb_addrectImBuf(preview->ibuf);
|
Refactor ID copying (and to some extent, ID freeing).
This will allow much finer controll over how we copy data-blocks, from
full copy in Main database, to "lighter" ones (out of Main, inside an
already allocated datablock, etc.).
This commit also transfers a llot of what was previously handled by
per-ID-type custom code to generic ID handling code in BKE_library.
Hopefully will avoid in future inconsistencies and missing bits we had
all over the codebase in the past.
It also adds missing copying handling for a few types, most notably
Scene (which where using a fully customized handling previously).
Note that the type of allocation used during copying (regular in Main,
allocated but outside of Main, or not allocated by ID handling code at
all) is stored in ID's, which allows to handle them correctly when
freeing. This needs to be taken care of with caution when doing 'weird'
unusual things with ID copying and/or allocation!
As a final note, while rather noisy, this commit will hopefully not
break too much existing branches, old 'API' has been kept for the main
part, as a wrapper around new code. Cleaning it up will happen later.
Design task : T51804
Phab Diff: D2714
2017-08-07 16:39:55 +02:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
/* no clear, makes nicer previews */
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
return preview;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
bNodePreview *node_preview_copy(bNodePreview *preview)
|
|
|
|
|
{
|
|
|
|
|
bNodePreview *new_preview = static_cast<bNodePreview *>(MEM_dupallocN(preview));
|
2023-07-17 20:13:52 +02:00
|
|
|
new_preview->ibuf = IMB_dupImBuf(preview->ibuf);
|
2023-05-16 09:39:42 -04:00
|
|
|
return new_preview;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void node_preview_free(bNodePreview *preview)
|
|
|
|
|
{
|
2023-07-17 20:13:52 +02:00
|
|
|
if (preview->ibuf) {
|
|
|
|
|
IMB_freeImBuf(preview->ibuf);
|
Refactor ID copying (and to some extent, ID freeing).
This will allow much finer controll over how we copy data-blocks, from
full copy in Main database, to "lighter" ones (out of Main, inside an
already allocated datablock, etc.).
This commit also transfers a llot of what was previously handled by
per-ID-type custom code to generic ID handling code in BKE_library.
Hopefully will avoid in future inconsistencies and missing bits we had
all over the codebase in the past.
It also adds missing copying handling for a few types, most notably
Scene (which where using a fully customized handling previously).
Note that the type of allocation used during copying (regular in Main,
allocated but outside of Main, or not allocated by ID handling code at
all) is stored in ID's, which allows to handle them correctly when
freeing. This needs to be taken care of with caution when doing 'weird'
unusual things with ID copying and/or allocation!
As a final note, while rather noisy, this commit will hopefully not
break too much existing branches, old 'API' has been kept for the main
part, as a wrapper around new code. Cleaning it up will happen later.
Design task : T51804
Phab Diff: D2714
2017-08-07 16:39:55 +02:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
MEM_freeN(preview);
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
static void node_preview_init_tree_recursive(bNodeInstanceHash *previews,
|
|
|
|
|
bNodeTree *ntree,
|
|
|
|
|
bNodeInstanceKey parent_key,
|
|
|
|
|
const int xsize,
|
|
|
|
|
const int ysize)
|
|
|
|
|
{
|
|
|
|
|
for (bNode *node : ntree->all_nodes()) {
|
|
|
|
|
bNodeInstanceKey key = BKE_node_instance_key(parent_key, ntree, node);
|
2022-12-18 14:06:43 -06:00
|
|
|
|
2024-05-13 16:07:12 +02:00
|
|
|
if (node_preview_used(node)) {
|
2023-05-16 09:39:42 -04:00
|
|
|
node->runtime->preview_xsize = xsize;
|
|
|
|
|
node->runtime->preview_ysize = ysize;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2024-05-13 16:07:12 +02:00
|
|
|
node_preview_verify(previews, key, xsize, ysize, false);
|
2023-05-16 09:39:42 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bNodeTree *group = reinterpret_cast<bNodeTree *>(node->id);
|
|
|
|
|
if (node->is_group() && group != nullptr) {
|
|
|
|
|
node_preview_init_tree_recursive(previews, group, key, xsize, ysize);
|
|
|
|
|
}
|
2012-10-26 11:29:30 +00:00
|
|
|
}
|
Refactor ID copying (and to some extent, ID freeing).
This will allow much finer controll over how we copy data-blocks, from
full copy in Main database, to "lighter" ones (out of Main, inside an
already allocated datablock, etc.).
This commit also transfers a llot of what was previously handled by
per-ID-type custom code to generic ID handling code in BKE_library.
Hopefully will avoid in future inconsistencies and missing bits we had
all over the codebase in the past.
It also adds missing copying handling for a few types, most notably
Scene (which where using a fully customized handling previously).
Note that the type of allocation used during copying (regular in Main,
allocated but outside of Main, or not allocated by ID handling code at
all) is stored in ID's, which allows to handle them correctly when
freeing. This needs to be taken care of with caution when doing 'weird'
unusual things with ID copying and/or allocation!
As a final note, while rather noisy, this commit will hopefully not
break too much existing branches, old 'API' has been kept for the main
part, as a wrapper around new code. Cleaning it up will happen later.
Design task : T51804
Phab Diff: D2714
2017-08-07 16:39:55 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void node_preview_init_tree(bNodeTree *ntree, int xsize, int ysize)
|
2023-05-02 18:50:34 +02:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
if (!ntree) {
|
|
|
|
|
return;
|
2023-05-02 18:50:34 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
if (!ntree->previews) {
|
|
|
|
|
ntree->previews = BKE_node_instance_hash_new("node previews");
|
2023-05-02 18:50:34 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
node_preview_init_tree_recursive(ntree->previews, ntree, NODE_INSTANCE_KEY_BASE, xsize, ysize);
|
2023-05-02 18:50:34 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
static void node_preview_tag_used_recursive(bNodeInstanceHash *previews,
|
|
|
|
|
bNodeTree *ntree,
|
|
|
|
|
bNodeInstanceKey parent_key)
|
2023-05-02 18:50:34 +02:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
for (bNode *node : ntree->all_nodes()) {
|
|
|
|
|
bNodeInstanceKey key = BKE_node_instance_key(parent_key, ntree, node);
|
2023-05-02 18:50:34 +02:00
|
|
|
|
2024-05-13 16:07:12 +02:00
|
|
|
if (node_preview_used(node)) {
|
2023-05-16 09:39:42 -04:00
|
|
|
BKE_node_instance_hash_tag_key(previews, key);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bNodeTree *group = reinterpret_cast<bNodeTree *>(node->id);
|
|
|
|
|
if (node->is_group() && group != nullptr) {
|
|
|
|
|
node_preview_tag_used_recursive(previews, group, key);
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-05-02 18:50:34 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void node_preview_remove_unused(bNodeTree *ntree)
|
2023-04-03 19:33:39 +02:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
if (!ntree || !ntree->previews) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2023-04-03 19:33:39 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/* use the instance hash functions for tagging and removing unused previews */
|
|
|
|
|
BKE_node_instance_hash_clear_tags(ntree->previews);
|
|
|
|
|
node_preview_tag_used_recursive(ntree->previews, ntree, NODE_INSTANCE_KEY_BASE);
|
2023-04-03 19:33:39 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
BKE_node_instance_hash_remove_untagged(
|
|
|
|
|
ntree->previews, reinterpret_cast<bNodeInstanceValueFP>(node_preview_free));
|
|
|
|
|
}
|
2023-05-02 18:50:34 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void node_preview_merge_tree(bNodeTree *to_ntree, bNodeTree *from_ntree, bool remove_old)
|
|
|
|
|
{
|
|
|
|
|
if (remove_old || !to_ntree->previews) {
|
|
|
|
|
/* free old previews */
|
|
|
|
|
if (to_ntree->previews) {
|
|
|
|
|
BKE_node_instance_hash_free(to_ntree->previews,
|
|
|
|
|
reinterpret_cast<bNodeInstanceValueFP>(node_preview_free));
|
2023-04-04 14:23:40 +02:00
|
|
|
}
|
2023-04-03 19:33:39 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/* transfer previews */
|
|
|
|
|
to_ntree->previews = from_ntree->previews;
|
|
|
|
|
from_ntree->previews = nullptr;
|
|
|
|
|
|
|
|
|
|
/* clean up, in case any to_ntree nodes have been removed */
|
|
|
|
|
node_preview_remove_unused(to_ntree);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
else {
|
|
|
|
|
if (from_ntree->previews) {
|
|
|
|
|
bNodeInstanceHashIterator iter;
|
|
|
|
|
NODE_INSTANCE_HASH_ITER (iter, from_ntree->previews) {
|
|
|
|
|
bNodeInstanceKey key = node_instance_hash_iterator_get_key(&iter);
|
|
|
|
|
bNodePreview *preview = static_cast<bNodePreview *>(
|
|
|
|
|
node_instance_hash_iterator_get_value(&iter));
|
2012-08-05 20:40:26 +00:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/* replace existing previews */
|
|
|
|
|
BKE_node_instance_hash_remove(
|
|
|
|
|
to_ntree->previews, key, reinterpret_cast<bNodeInstanceValueFP>(node_preview_free));
|
|
|
|
|
BKE_node_instance_hash_insert(to_ntree->previews, key, preview);
|
|
|
|
|
}
|
2018-06-17 17:05:51 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/* NOTE: null free function here,
|
|
|
|
|
* because pointers have already been moved over to to_ntree->previews! */
|
|
|
|
|
BKE_node_instance_hash_free(from_ntree->previews, nullptr);
|
|
|
|
|
from_ntree->previews = nullptr;
|
|
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2011-09-05 21:01:50 +00:00
|
|
|
}
|
2010-12-03 03:44:39 +00:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void nodeUnlinkNode(bNodeTree *ntree, bNode *node)
|
2011-09-05 21:01:50 +00:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree->links) {
|
|
|
|
|
ListBase *lb = nullptr;
|
|
|
|
|
if (link->fromnode == node) {
|
|
|
|
|
lb = &node->outputs;
|
|
|
|
|
}
|
|
|
|
|
else if (link->tonode == node) {
|
|
|
|
|
lb = &node->inputs;
|
|
|
|
|
}
|
2011-09-05 21:01:50 +00:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
if (lb) {
|
|
|
|
|
/* Only bother adjusting if the socket is not on the node we're deleting. */
|
|
|
|
|
if (link->tonode != node && link->tosock->is_multi_input()) {
|
|
|
|
|
adjust_multi_input_indices_after_removed_link(
|
2024-03-19 13:42:09 +01:00
|
|
|
ntree, link->tosock, link->multi_input_sort_id);
|
2023-05-16 09:39:42 -04:00
|
|
|
}
|
|
|
|
|
LISTBASE_FOREACH (const bNodeSocket *, sock, lb) {
|
|
|
|
|
if (link->fromsock == sock || link->tosock == sock) {
|
|
|
|
|
nodeRemLink(ntree, link);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
}
|
2018-09-20 19:53:16 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
static void node_unlink_attached(bNodeTree *ntree, const bNode *parent)
|
|
|
|
|
{
|
|
|
|
|
for (bNode *node : ntree->all_nodes()) {
|
|
|
|
|
if (node->parent == parent) {
|
|
|
|
|
nodeDetachNode(ntree, node);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2018-09-20 19:53:16 +02:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void nodeRebuildIDVector(bNodeTree *node_tree)
|
|
|
|
|
{
|
|
|
|
|
/* Rebuild nodes #VectorSet which must have the same order as the list. */
|
|
|
|
|
node_tree->runtime->nodes_by_id.clear();
|
|
|
|
|
int i;
|
|
|
|
|
LISTBASE_FOREACH_INDEX (bNode *, node, &node_tree->nodes, i) {
|
|
|
|
|
node_tree->runtime->nodes_by_id.add_new(node);
|
|
|
|
|
node->runtime->index_in_tree = i;
|
2018-09-20 19:53:16 +02:00
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2018-09-20 19:53:16 +02:00
|
|
|
|
2023-05-15 15:14:22 +02:00
|
|
|
void node_free_node(bNodeTree *ntree, bNode *node)
|
|
|
|
|
{
|
|
|
|
|
/* since it is called while free database, node->id is undefined */
|
2018-09-20 19:53:16 +02:00
|
|
|
|
2023-05-15 15:14:22 +02:00
|
|
|
/* can be called for nodes outside a node tree (e.g. clipboard) */
|
|
|
|
|
if (ntree) {
|
|
|
|
|
BLI_remlink(&ntree->nodes, node);
|
|
|
|
|
/* Rebuild nodes #VectorSet which must have the same order as the list. */
|
|
|
|
|
nodeRebuildIDVector(ntree);
|
2018-09-20 19:53:16 +02:00
|
|
|
|
2023-05-15 15:14:22 +02:00
|
|
|
/* texture node has bad habit of keeping exec data around */
|
|
|
|
|
if (ntree->type == NTREE_TEXTURE && ntree->runtime->execdata) {
|
|
|
|
|
ntreeTexEndExecTree(ntree->runtime->execdata);
|
|
|
|
|
ntree->runtime->execdata = nullptr;
|
2018-11-02 17:49:34 +01:00
|
|
|
}
|
2018-09-20 19:53:16 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-15 15:14:22 +02:00
|
|
|
if (node->typeinfo->freefunc) {
|
|
|
|
|
node->typeinfo->freefunc(node);
|
2018-09-20 19:53:16 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-15 15:14:22 +02:00
|
|
|
LISTBASE_FOREACH_MUTABLE (bNodeSocket *, sock, &node->inputs) {
|
|
|
|
|
/* Remember, no ID user refcount management here! */
|
|
|
|
|
node_socket_free(sock, false);
|
|
|
|
|
MEM_freeN(sock);
|
2013-03-18 16:34:57 +00:00
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
LISTBASE_FOREACH_MUTABLE (bNodeSocket *, sock, &node->outputs) {
|
|
|
|
|
/* Remember, no ID user refcount management here! */
|
|
|
|
|
node_socket_free(sock, false);
|
|
|
|
|
MEM_freeN(sock);
|
2022-09-05 15:46:00 +02:00
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-08-30 12:37:21 +02:00
|
|
|
MEM_SAFE_FREE(node->panel_states_array);
|
|
|
|
|
|
2023-05-15 15:14:22 +02:00
|
|
|
if (node->prop) {
|
|
|
|
|
/* Remember, no ID user refcount management here! */
|
|
|
|
|
IDP_FreePropertyContent_ex(node->prop, false);
|
|
|
|
|
MEM_freeN(node->prop);
|
2010-12-03 03:44:39 +00:00
|
|
|
}
|
2018-06-17 17:05:51 +02:00
|
|
|
|
2023-10-15 20:28:23 +02:00
|
|
|
if (node->runtime->declaration) {
|
|
|
|
|
/* Only free if this declaration is not shared with the node type, which can happen if it does
|
|
|
|
|
* not depend on any context. */
|
|
|
|
|
if (node->runtime->declaration != node->typeinfo->static_declaration) {
|
|
|
|
|
delete node->runtime->declaration;
|
|
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
MEM_delete(node->runtime);
|
|
|
|
|
MEM_freeN(node);
|
|
|
|
|
|
|
|
|
|
if (ntree) {
|
|
|
|
|
BKE_ntree_update_tag_node_removed(ntree);
|
|
|
|
|
}
|
2012-07-05 12:50:50 +00:00
|
|
|
}
|
|
|
|
|
|
2023-05-15 15:14:22 +02:00
|
|
|
void ntreeFreeLocalNode(bNodeTree *ntree, bNode *node)
|
2009-01-27 17:12:40 +00:00
|
|
|
{
|
2023-05-15 15:14:22 +02:00
|
|
|
/* For removing nodes while editing localized node trees. */
|
|
|
|
|
BLI_assert((ntree->id.tag & LIB_TAG_LOCALIZED) != 0);
|
2013-03-18 16:34:57 +00:00
|
|
|
|
2023-05-15 15:14:22 +02:00
|
|
|
/* These two lines assume the caller might want to free a single node and maintain
|
|
|
|
|
* a valid state in the node tree. */
|
|
|
|
|
nodeUnlinkNode(ntree, node);
|
|
|
|
|
node_unlink_attached(ntree, node);
|
2013-03-18 16:34:57 +00:00
|
|
|
|
2023-05-15 15:14:22 +02:00
|
|
|
node_free_node(ntree, node);
|
|
|
|
|
nodeRebuildIDVector(ntree);
|
2013-03-18 16:34:57 +00:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void nodeRemoveNode(Main *bmain, bNodeTree *ntree, bNode *node, const bool do_id_user)
|
2013-03-18 16:34:57 +00:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
BLI_assert(ntree != nullptr);
|
|
|
|
|
/* This function is not for localized node trees, we do not want
|
2023-08-18 08:51:29 +10:00
|
|
|
* do to ID user reference-counting and removal of animation data then. */
|
2023-05-16 09:39:42 -04:00
|
|
|
BLI_assert((ntree->id.tag & LIB_TAG_LOCALIZED) == 0);
|
2013-03-18 16:34:57 +00:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
bool node_has_id = false;
|
2018-06-17 17:05:51 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
if (do_id_user) {
|
|
|
|
|
/* Free callback for NodeCustomGroup. */
|
|
|
|
|
if (node->typeinfo->freefunc_api) {
|
2023-09-06 00:48:50 +02:00
|
|
|
PointerRNA ptr = RNA_pointer_create(&ntree->id, &RNA_Node, node);
|
2013-03-18 16:34:57 +00:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
node->typeinfo->freefunc_api(&ptr);
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2013-03-18 16:34:57 +00:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/* Do user counting. */
|
|
|
|
|
if (node->id) {
|
|
|
|
|
id_us_min(node->id);
|
|
|
|
|
node_has_id = true;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
|
2024-05-13 16:07:12 +02:00
|
|
|
node_has_id |= socket_id_user_decrement(sock);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
|
2024-05-13 16:07:12 +02:00
|
|
|
node_has_id |= socket_id_user_decrement(sock);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
}
|
2018-06-17 17:05:51 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/* Remove animation data. */
|
|
|
|
|
char propname_esc[MAX_IDPROP_NAME * 2];
|
|
|
|
|
char prefix[MAX_IDPROP_NAME * 2];
|
2018-06-17 17:05:51 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
BLI_str_escape(propname_esc, node->name, sizeof(propname_esc));
|
|
|
|
|
SNPRINTF(prefix, "nodes[\"%s\"]", propname_esc);
|
2017-07-11 11:06:36 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
if (BKE_animdata_fix_paths_remove(&ntree->id, prefix)) {
|
|
|
|
|
if (bmain != nullptr) {
|
|
|
|
|
DEG_relations_tag_update(bmain);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2017-07-11 11:06:36 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/* Also update relations for the scene time node, which causes a dependency
|
|
|
|
|
* on time that users expect to be removed when the node is removed. */
|
|
|
|
|
if (node_has_id ||
|
|
|
|
|
ELEM(node->type, GEO_NODE_INPUT_SCENE_TIME, GEO_NODE_SELF_OBJECT, GEO_NODE_SIMULATION_INPUT))
|
|
|
|
|
{
|
|
|
|
|
if (bmain != nullptr) {
|
|
|
|
|
DEG_relations_tag_update(bmain);
|
|
|
|
|
}
|
2011-09-05 21:01:50 +00:00
|
|
|
}
|
|
|
|
|
|
2024-05-13 16:07:12 +02:00
|
|
|
nodeUnlinkNode(ntree, node);
|
|
|
|
|
node_unlink_attached(ntree, node);
|
2009-01-27 17:12:40 +00:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/* Free node itself. */
|
2024-05-13 16:07:12 +02:00
|
|
|
node_free_node(ntree, node);
|
|
|
|
|
nodeRebuildIDVector(ntree);
|
2023-05-16 09:39:42 -04:00
|
|
|
}
|
2021-09-14 16:34:31 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
static void free_localized_node_groups(bNodeTree *ntree)
|
2013-12-05 15:02:17 +01:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
/* Only localized node trees store a copy for each node group tree.
|
|
|
|
|
* Each node group tree in a localized node tree can be freed,
|
2023-12-07 12:15:45 +11:00
|
|
|
* since it is a localized copy itself (no risk of accessing freed
|
2023-05-16 09:39:42 -04:00
|
|
|
* data in main, see #37939). */
|
|
|
|
|
if (!(ntree->id.tag & LIB_TAG_LOCALIZED)) {
|
2023-05-15 15:14:22 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2021-12-21 09:23:48 -06:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
|
|
|
|
|
bNodeTree *ngroup = reinterpret_cast<bNodeTree *>(node->id);
|
|
|
|
|
if (node->is_group() && ngroup != nullptr) {
|
|
|
|
|
ntreeFreeTree(ngroup);
|
|
|
|
|
MEM_freeN(ngroup);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
}
|
2018-11-23 17:02:55 +01:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void ntreeFreeTree(bNodeTree *ntree)
|
2018-11-23 17:02:55 +01:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
ntree_free_data(&ntree->id);
|
|
|
|
|
BKE_animdata_free(&ntree->id, false);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2019-03-16 18:54:00 +01:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void ntreeFreeEmbeddedTree(bNodeTree *ntree)
|
2023-05-15 15:14:22 +02:00
|
|
|
{
|
2024-05-13 16:07:12 +02:00
|
|
|
ntreeFreeTree(ntree);
|
2023-05-16 09:39:42 -04:00
|
|
|
BKE_libblock_free_data(&ntree->id, true);
|
|
|
|
|
BKE_libblock_free_data_py(&ntree->id);
|
|
|
|
|
}
|
2022-02-18 09:24:28 -06:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void ntreeFreeLocalTree(bNodeTree *ntree)
|
|
|
|
|
{
|
|
|
|
|
if (ntree->id.tag & LIB_TAG_LOCALIZED) {
|
2024-05-13 16:07:12 +02:00
|
|
|
ntreeFreeTree(ntree);
|
2023-05-16 09:39:42 -04:00
|
|
|
}
|
|
|
|
|
else {
|
2024-05-13 16:07:12 +02:00
|
|
|
ntreeFreeTree(ntree);
|
2023-05-16 09:39:42 -04:00
|
|
|
BKE_libblock_free_data(&ntree->id, true);
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-03-16 18:54:00 +01:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void ntreeSetOutput(bNodeTree *ntree)
|
|
|
|
|
{
|
|
|
|
|
const bool is_compositor = ntree->type == NTREE_COMPOSIT;
|
|
|
|
|
/* find the active outputs, might become tree type dependent handler */
|
2023-05-15 15:14:22 +02:00
|
|
|
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
|
2023-05-16 09:39:42 -04:00
|
|
|
if (node->typeinfo->nclass == NODE_CLASS_OUTPUT) {
|
|
|
|
|
/* we need a check for which output node should be tagged like this, below an exception */
|
|
|
|
|
if (ELEM(node->type, CMP_NODE_OUTPUT_FILE, GEO_NODE_VIEWER)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2023-12-03 23:20:44 +01:00
|
|
|
const bool node_is_output = node->type == CMP_NODE_VIEWER;
|
2019-03-16 18:54:00 +01:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
int output = 0;
|
|
|
|
|
/* there is more types having output class, each one is checked */
|
2020-04-20 13:22:20 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
LISTBASE_FOREACH (bNode *, tnode, &ntree->nodes) {
|
|
|
|
|
if (tnode->typeinfo->nclass != NODE_CLASS_OUTPUT) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2019-03-16 18:54:00 +01:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/* same type, exception for viewer */
|
2023-12-03 23:20:44 +01:00
|
|
|
const bool tnode_is_output = tnode->type == CMP_NODE_VIEWER;
|
2023-05-16 09:39:42 -04:00
|
|
|
const bool compositor_case = is_compositor && tnode_is_output && node_is_output;
|
|
|
|
|
if (tnode->type == node->type || compositor_case) {
|
|
|
|
|
if (tnode->flag & NODE_DO_OUTPUT) {
|
|
|
|
|
output++;
|
|
|
|
|
if (output > 1) {
|
|
|
|
|
tnode->flag &= ~NODE_DO_OUTPUT;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-03-16 18:54:00 +01:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
if (output == 0) {
|
|
|
|
|
node->flag |= NODE_DO_OUTPUT;
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-03-16 18:54:00 +01:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/* group node outputs use this flag too */
|
|
|
|
|
if (node->type == NODE_GROUP_OUTPUT) {
|
|
|
|
|
int output = 0;
|
|
|
|
|
LISTBASE_FOREACH (bNode *, tnode, &ntree->nodes) {
|
|
|
|
|
if (tnode->type != NODE_GROUP_OUTPUT) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (tnode->flag & NODE_DO_OUTPUT) {
|
|
|
|
|
output++;
|
|
|
|
|
if (output > 1) {
|
|
|
|
|
tnode->flag &= ~NODE_DO_OUTPUT;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (output == 0) {
|
|
|
|
|
node->flag |= NODE_DO_OUTPUT;
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2019-03-16 18:54:00 +01:00
|
|
|
}
|
|
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
|
|
|
|
|
/* here we could recursively set which nodes have to be done,
|
|
|
|
|
* might be different for editor or for "real" use... */
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2019-03-16 18:54:00 +01:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
bNodeTree **BKE_ntree_ptr_from_id(ID *id)
|
2023-05-15 15:14:22 +02:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
switch (GS(id->name)) {
|
|
|
|
|
case ID_MA:
|
|
|
|
|
return &reinterpret_cast<Material *>(id)->nodetree;
|
|
|
|
|
case ID_LA:
|
|
|
|
|
return &reinterpret_cast<Light *>(id)->nodetree;
|
|
|
|
|
case ID_WO:
|
|
|
|
|
return &reinterpret_cast<World *>(id)->nodetree;
|
|
|
|
|
case ID_TE:
|
|
|
|
|
return &reinterpret_cast<Tex *>(id)->nodetree;
|
|
|
|
|
case ID_SCE:
|
|
|
|
|
return &reinterpret_cast<Scene *>(id)->nodetree;
|
|
|
|
|
case ID_LS:
|
|
|
|
|
return &reinterpret_cast<FreestyleLineStyle *>(id)->nodetree;
|
|
|
|
|
default:
|
|
|
|
|
return nullptr;
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2013-12-05 15:02:17 +01:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
bNodeTree *ntreeFromID(ID *id)
|
2013-03-18 16:34:57 +00:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
bNodeTree **nodetree = BKE_ntree_ptr_from_id(id);
|
|
|
|
|
return (nodetree != nullptr) ? *nodetree : nullptr;
|
2013-03-18 16:34:57 +00:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void ntreeNodeFlagSet(const bNodeTree *ntree, const int flag, const bool enable)
|
|
|
|
|
{
|
2020-09-02 18:28:04 +02:00
|
|
|
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
|
2023-05-16 09:39:42 -04:00
|
|
|
if (enable) {
|
|
|
|
|
node->flag |= flag;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
node->flag &= ~flag;
|
2013-10-01 09:29:56 +00:00
|
|
|
}
|
2012-10-28 17:09:50 +00:00
|
|
|
}
|
2005-12-18 13:46:01 +00:00
|
|
|
}
|
|
|
|
|
|
2024-05-02 14:35:57 +02:00
|
|
|
bNodeTree *ntreeLocalize(bNodeTree *ntree, ID *new_owner_id)
|
2023-05-16 09:39:42 -04:00
|
|
|
{
|
|
|
|
|
if (ntree == nullptr) {
|
|
|
|
|
return nullptr;
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2023-04-16 22:53:09 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/* Make full copy outside of Main database.
|
|
|
|
|
* NOTE: previews are not copied here. */
|
2024-05-02 14:35:57 +02:00
|
|
|
bNodeTree *ltree = reinterpret_cast<bNodeTree *>(
|
|
|
|
|
BKE_id_copy_in_lib(nullptr,
|
|
|
|
|
std::nullopt,
|
|
|
|
|
&ntree->id,
|
|
|
|
|
new_owner_id,
|
|
|
|
|
nullptr,
|
|
|
|
|
(LIB_ID_COPY_LOCALIZE | LIB_ID_COPY_NO_ANIMDATA)));
|
2023-05-16 09:39:42 -04:00
|
|
|
|
|
|
|
|
ltree->id.tag |= LIB_TAG_LOCALIZED;
|
|
|
|
|
|
|
|
|
|
LISTBASE_FOREACH (bNode *, node, <ree->nodes) {
|
|
|
|
|
bNodeTree *group = reinterpret_cast<bNodeTree *>(node->id);
|
|
|
|
|
if (node->is_group() && group != nullptr) {
|
2024-05-02 14:35:57 +02:00
|
|
|
node->id = reinterpret_cast<ID *>(ntreeLocalize(group, nullptr));
|
2013-03-18 16:34:57 +00:00
|
|
|
}
|
2011-09-05 21:01:50 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/* Ensures only a single output node is enabled. */
|
|
|
|
|
ntreeSetOutput(ntree);
|
2011-09-05 21:01:50 +00:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
bNode *node_src = reinterpret_cast<bNode *>(ntree->nodes.first);
|
|
|
|
|
bNode *node_local = reinterpret_cast<bNode *>(ltree->nodes.first);
|
|
|
|
|
while (node_src != nullptr) {
|
|
|
|
|
node_local->runtime->original = node_src;
|
|
|
|
|
node_src = node_src->next;
|
|
|
|
|
node_local = node_local->next;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ntree->typeinfo->localize) {
|
|
|
|
|
ntree->typeinfo->localize(ltree, ntree);
|
2012-12-03 16:21:43 +00:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
|
|
|
|
|
return ltree;
|
2012-12-03 16:21:43 +00:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void ntreeLocalMerge(Main *bmain, bNodeTree *localtree, bNodeTree *ntree)
|
2020-02-06 16:24:19 +01:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
if (ntree && localtree) {
|
|
|
|
|
if (ntree->typeinfo->local_merge) {
|
|
|
|
|
ntree->typeinfo->local_merge(bmain, localtree, ntree);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
}
|
2020-02-06 16:24:19 +01:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
static bool ntree_contains_tree_exec(const bNodeTree *tree_to_search_in,
|
|
|
|
|
const bNodeTree *tree_to_search_for,
|
|
|
|
|
Set<const bNodeTree *> &already_passed)
|
2023-05-15 15:14:22 +02:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
if (tree_to_search_in == tree_to_search_for) {
|
|
|
|
|
return true;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
|
|
|
|
|
tree_to_search_in->ensure_topology_cache();
|
|
|
|
|
for (const bNode *node_group : tree_to_search_in->group_nodes()) {
|
|
|
|
|
const bNodeTree *sub_tree_search_in = reinterpret_cast<const bNodeTree *>(node_group->id);
|
|
|
|
|
if (!sub_tree_search_in) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (!already_passed.add(sub_tree_search_in)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (ntree_contains_tree_exec(sub_tree_search_in, tree_to_search_for, already_passed)) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
|
|
|
|
|
return false;
|
2013-03-18 16:34:57 +00:00
|
|
|
}
|
2011-09-05 21:01:50 +00:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
bool ntreeContainsTree(const bNodeTree *tree_to_search_in, const bNodeTree *tree_to_search_for)
|
2013-03-18 16:34:57 +00:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
if (tree_to_search_in == tree_to_search_for) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Set<const bNodeTree *> already_passed;
|
2024-05-13 16:07:12 +02:00
|
|
|
return ntree_contains_tree_exec(tree_to_search_in, tree_to_search_for, already_passed);
|
2023-05-16 09:39:42 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int nodeCountSocketLinks(const bNodeTree *ntree, const bNodeSocket *sock)
|
2013-03-18 16:34:57 +00:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
int tot = 0;
|
|
|
|
|
LISTBASE_FOREACH (const bNodeLink *, link, &ntree->links) {
|
|
|
|
|
if (link->fromsock == sock || link->tosock == sock) {
|
|
|
|
|
tot++;
|
|
|
|
|
}
|
2011-09-05 21:01:50 +00:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
return tot;
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
bNode *nodeGetActive(bNodeTree *ntree)
|
2023-05-15 15:14:22 +02:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
if (ntree == nullptr) {
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (bNode *node : ntree->all_nodes()) {
|
|
|
|
|
if (node->flag & NODE_ACTIVE) {
|
|
|
|
|
return node;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nullptr;
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
|
2024-05-02 01:43:45 +02:00
|
|
|
bool nodeSetSelected(bNode *node, const bool select)
|
2023-05-15 15:14:22 +02:00
|
|
|
{
|
2024-05-02 01:43:45 +02:00
|
|
|
bool changed = false;
|
|
|
|
|
if (select != ((node->flag & NODE_SELECT) != 0)) {
|
|
|
|
|
changed = true;
|
|
|
|
|
SET_FLAG_FROM_TEST(node->flag, select, NODE_SELECT);
|
|
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
if (select) {
|
2024-05-02 01:43:45 +02:00
|
|
|
return changed;
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2024-05-02 01:43:45 +02:00
|
|
|
/* Deselect sockets too. */
|
2023-05-16 09:39:42 -04:00
|
|
|
LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
|
2024-05-02 01:43:45 +02:00
|
|
|
changed |= (sock->flag & NODE_SELECT) != 0;
|
2023-05-16 09:39:42 -04:00
|
|
|
sock->flag &= ~NODE_SELECT;
|
|
|
|
|
}
|
|
|
|
|
LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
|
2024-05-02 01:43:45 +02:00
|
|
|
changed |= (sock->flag & NODE_SELECT) != 0;
|
2023-05-16 09:39:42 -04:00
|
|
|
sock->flag &= ~NODE_SELECT;
|
|
|
|
|
}
|
2024-05-02 01:43:45 +02:00
|
|
|
return changed;
|
2023-05-16 09:39:42 -04:00
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void nodeClearActive(bNodeTree *ntree)
|
|
|
|
|
{
|
|
|
|
|
if (ntree == nullptr) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
for (bNode *node : ntree->all_nodes()) {
|
|
|
|
|
node->flag &= ~NODE_ACTIVE;
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void nodeSetActive(bNodeTree *ntree, bNode *node)
|
|
|
|
|
{
|
2024-05-13 16:07:12 +02:00
|
|
|
const bool is_paint_canvas = nodeSupportsActiveFlag(node, NODE_ACTIVE_PAINT_CANVAS);
|
|
|
|
|
const bool is_texture_class = nodeSupportsActiveFlag(node, NODE_ACTIVE_TEXTURE);
|
2023-05-16 09:39:42 -04:00
|
|
|
int flags_to_set = NODE_ACTIVE;
|
|
|
|
|
SET_FLAG_FROM_TEST(flags_to_set, is_paint_canvas, NODE_ACTIVE_PAINT_CANVAS);
|
|
|
|
|
SET_FLAG_FROM_TEST(flags_to_set, is_texture_class, NODE_ACTIVE_TEXTURE);
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/* Make sure only one node is active per node tree. */
|
|
|
|
|
for (bNode *tnode : ntree->all_nodes()) {
|
|
|
|
|
tnode->flag &= ~flags_to_set;
|
|
|
|
|
}
|
|
|
|
|
node->flag |= flags_to_set;
|
|
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void nodeSetSocketAvailability(bNodeTree *ntree, bNodeSocket *sock, const bool is_available)
|
|
|
|
|
{
|
|
|
|
|
if (is_available == sock->is_available()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (is_available) {
|
|
|
|
|
sock->flag &= ~SOCK_UNAVAIL;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
sock->flag |= SOCK_UNAVAIL;
|
|
|
|
|
}
|
|
|
|
|
BKE_ntree_update_tag_socket_availability(ntree, sock);
|
|
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
int nodeSocketLinkLimit(const bNodeSocket *sock)
|
|
|
|
|
{
|
|
|
|
|
if (sock->is_multi_input()) {
|
|
|
|
|
return 4095;
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
if (sock->typeinfo == nullptr) {
|
|
|
|
|
return sock->limit;
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
const bNodeSocketType &stype = *sock->typeinfo;
|
|
|
|
|
if (!stype.use_link_limits_of_type) {
|
|
|
|
|
return sock->limit;
|
2013-03-18 16:34:57 +00:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
return eNodeSocketInOut(sock->in_out) == SOCK_IN ? stype.input_link_limit :
|
|
|
|
|
stype.output_link_limit;
|
2013-03-18 16:34:57 +00:00
|
|
|
}
|
2011-09-05 21:01:50 +00:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
static void update_socket_declarations(ListBase *sockets,
|
2024-05-13 16:07:12 +02:00
|
|
|
Span<nodes::SocketDeclaration *> declarations)
|
2013-03-18 16:34:57 +00:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
int index;
|
|
|
|
|
LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, sockets, index) {
|
|
|
|
|
const SocketDeclaration &socket_decl = *declarations[index];
|
|
|
|
|
socket->runtime->declaration = &socket_decl;
|
2013-03-18 16:34:57 +00:00
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
static void reset_socket_declarations(ListBase *sockets)
|
2023-05-15 15:14:22 +02:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
LISTBASE_FOREACH (bNodeSocket *, socket, sockets) {
|
|
|
|
|
socket->runtime->declaration = nullptr;
|
2013-03-18 16:34:57 +00:00
|
|
|
}
|
|
|
|
|
}
|
2011-09-05 21:01:50 +00:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void nodeSocketDeclarationsUpdate(bNode *node)
|
2013-03-18 16:34:57 +00:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
BLI_assert(node->runtime->declaration != nullptr);
|
|
|
|
|
if (node->runtime->declaration->skip_updating_sockets) {
|
|
|
|
|
reset_socket_declarations(&node->inputs);
|
|
|
|
|
reset_socket_declarations(&node->outputs);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
update_socket_declarations(&node->inputs, node->runtime->declaration->inputs);
|
|
|
|
|
update_socket_declarations(&node->outputs, node->runtime->declaration->outputs);
|
|
|
|
|
}
|
2022-11-02 12:33:09 +01:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
bool nodeDeclarationEnsureOnOutdatedNode(bNodeTree *ntree, bNode *node)
|
|
|
|
|
{
|
|
|
|
|
if (node->runtime->declaration != nullptr) {
|
|
|
|
|
return false;
|
2023-04-16 22:53:09 +02:00
|
|
|
}
|
2023-10-15 20:28:23 +02:00
|
|
|
if (node->typeinfo->declare) {
|
|
|
|
|
if (node->typeinfo->static_declaration) {
|
|
|
|
|
if (!node->typeinfo->static_declaration->is_context_dependent) {
|
|
|
|
|
node->runtime->declaration = node->typeinfo->static_declaration;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (node->typeinfo->declare) {
|
2023-05-16 09:39:42 -04:00
|
|
|
BLI_assert(ntree != nullptr);
|
|
|
|
|
BLI_assert(node != nullptr);
|
2024-05-13 16:07:12 +02:00
|
|
|
nodes::update_node_declaration_and_sockets(*ntree, *node);
|
2023-05-16 09:39:42 -04:00
|
|
|
return true;
|
2011-09-05 21:01:50 +00:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
return false;
|
|
|
|
|
}
|
2011-09-05 21:01:50 +00:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
bool nodeDeclarationEnsure(bNodeTree *ntree, bNode *node)
|
|
|
|
|
{
|
|
|
|
|
if (nodeDeclarationEnsureOnOutdatedNode(ntree, node)) {
|
|
|
|
|
nodeSocketDeclarationsUpdate(node);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2018-06-17 17:05:51 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void nodeDimensionsGet(const bNode *node, float *r_width, float *r_height)
|
|
|
|
|
{
|
|
|
|
|
*r_width = node->runtime->totr.xmax - node->runtime->totr.xmin;
|
|
|
|
|
*r_height = node->runtime->totr.ymax - node->runtime->totr.ymin;
|
2011-09-05 21:01:50 +00:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void nodeTagUpdateID(bNode *node)
|
2016-06-19 06:25:54 +10:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
node->runtime->update |= NODE_UPDATE_ID;
|
2016-06-14 11:31:00 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void nodeInternalLinks(bNode *node, bNodeLink **r_links, int *r_len)
|
2014-01-27 20:17:56 +11:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
*r_links = node->runtime->internal_links.data();
|
|
|
|
|
*r_len = node->runtime->internal_links.size();
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2023-02-10 17:30:55 +01:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/* Node Instance Hash */
|
2023-02-10 17:30:55 +01:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
const bNodeInstanceKey NODE_INSTANCE_KEY_BASE = {5381};
|
|
|
|
|
const bNodeInstanceKey NODE_INSTANCE_KEY_NONE = {0};
|
2014-01-27 20:17:56 +11:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/* Generate a hash key from ntree and node names
|
|
|
|
|
* Uses the djb2 algorithm with xor by Bernstein:
|
|
|
|
|
* http://www.cse.yorku.ca/~oz/hash.html
|
|
|
|
|
*/
|
|
|
|
|
static bNodeInstanceKey node_hash_int_str(bNodeInstanceKey hash, const char *str)
|
|
|
|
|
{
|
|
|
|
|
char c;
|
|
|
|
|
|
|
|
|
|
while ((c = *str++)) {
|
|
|
|
|
hash.value = ((hash.value << 5) + hash.value) ^ c; /* (hash * 33) ^ c */
|
2023-02-10 17:30:55 +01:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
|
|
|
|
|
/* separator '\0' character, to avoid ambiguity from concatenated strings */
|
|
|
|
|
hash.value = (hash.value << 5) + hash.value; /* hash * 33 */
|
|
|
|
|
|
|
|
|
|
return hash;
|
2023-02-10 17:30:55 +01:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
bNodeInstanceKey BKE_node_instance_key(bNodeInstanceKey parent_key,
|
|
|
|
|
const bNodeTree *ntree,
|
|
|
|
|
const bNode *node)
|
2005-12-18 23:08:22 +00:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
bNodeInstanceKey key = node_hash_int_str(parent_key, ntree->id.name + 2);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
if (node) {
|
|
|
|
|
key = node_hash_int_str(key, node->name);
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
|
|
|
|
|
return key;
|
Christmas coding work!
********* Node editor work:
- To enable Nodes for Materials, you have to set the "Use Nodes"
button, in the new Material buttons "Nodes" Panel or in header
of the Node editor. Doing this will disable Material-Layers.
- Nodes now execute materials ("shaders"), but still only using the
previewrender code.
- Nodes have (optional) previews for rendered images.
- Node headers allow to hide buttons and/or preview image
- Nodes can be dragged larger/smaller (right-bottom corner)
- Nodes can be hidden (minimized) with hotkey H
- CTRL+click on an Input Socket gives a popup with default values.
- Changing Material/Texture or Mix node will adjust Node title.
- Click-drag outside of a Node changes cursor to "Knife' and allows to
draw a rect where to cut Links.
- Added new node types RGBtoBW, Texture, In/Output, ColorRamp
- Material Nodes have options to ouput diffuse or specular, or to use
a negative normal. The input socket 'Normal' will force the material
to use that normal, otherwise it uses the normal from the Material
that has the node tree.
- When drawing a link between two not-matching sockets, Blender inserts
a converting node (now only for value/rgb combos)
- When drawing a link to an input socket that's already in use, the
old link will either disappear or flip to another unused socket.
- A click on a Material Node will activate it, and show all its settings
in the Material Buttons. Active Material Nodes draw the material icon
in red.
- A click on any node will show its options in the Node Panel in the
Material buttons.
- Multiple Output Nodes can be used, to sample contents of a tree, but
only one Output is the real one, which is indicated in a different
color and red material icon.
- Added ThemeColors for node types
- ALT+C will convert existing Material-Layers to Node... this currently
only adds the material/mix nodes and connects them. Dunno if this is
worth a lot of coding work to make perfect?
- Press C to call another "Solve order", which will show all possible
cyclic conflicts (if there are).
- Technical: nodes now use "Type" structs which define the
structure of nodes and in/output sockets. The Type structs store all
fixed info, callbacks, and allow to reconstruct saved Nodes to match
what is required by Blender.
- Defining (new) nodes now is as simple as filling in a fixed
Type struct, plus code some callbacks. A doc will be made!
- Node preview images are by default float
********* Icon drawing:
- Cleanup of how old icons were implemented in new system, making
them 16x16 too, correctly centered *and* scaled.
- Made drawing Icons use float coordinates
- Moved BIF_calcpreview_image() into interface_icons.c, renamed it
icon_from_image(). Removed a lot of unneeded Imbuf magic here! :)
- Skipped scaling and imbuf copying when icons are OK size
********* Preview render:
- Huge cleanup of code....
- renaming BIF_xxx calls that only were used internally
- BIF_previewrender() now accepts an argument for rendering method,
so it supports icons, buttonwindow previewrender and node editor
- Only a single BIF_preview_changed() call now exists, supporting all
signals as needed for buttos and node editor
********* More stuff:
- glutil.c, glaDrawPixelsSafe() and glaDrawPixelsTex() now accept format
argument for GL_FLOAT rects
- Made the ColorBand become a built-in button for interface.c
Was a load of cleanup work in buttons_shading.c...
- removed a load of unneeded glBlendFunc() calls
- Fixed bug in calculating text length for buttons (ancient!)
2005-12-28 15:42:51 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
static uint node_instance_hash_key(const void *key)
|
2013-03-18 16:34:57 +00:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
return static_cast<const bNodeInstanceKey *>(key)->value;
|
|
|
|
|
}
|
2013-03-18 16:34:57 +00:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
static bool node_instance_hash_key_cmp(const void *a, const void *b)
|
|
|
|
|
{
|
|
|
|
|
uint value_a = static_cast<const bNodeInstanceKey *>(a)->value;
|
|
|
|
|
uint value_b = static_cast<const bNodeInstanceKey *>(b)->value;
|
2012-06-11 18:43:48 +00:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
return (value_a != value_b);
|
|
|
|
|
}
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
bNodeInstanceHash *BKE_node_instance_hash_new(const char *info)
|
|
|
|
|
{
|
|
|
|
|
bNodeInstanceHash *hash = static_cast<bNodeInstanceHash *>(
|
|
|
|
|
MEM_mallocN(sizeof(bNodeInstanceHash), info));
|
|
|
|
|
hash->ghash = BLI_ghash_new(
|
|
|
|
|
node_instance_hash_key, node_instance_hash_key_cmp, "node instance hash ghash");
|
|
|
|
|
return hash;
|
2012-06-11 18:43:48 +00:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void BKE_node_instance_hash_free(bNodeInstanceHash *hash, bNodeInstanceValueFP valfreefp)
|
More node goodies!
First note; this is a WIP project, some commits might change things that
make formerly saved situations not to work identically... like now!
------ New Material integration ------
Until now, the Node system worked on top of the 'current' Material, just
like how the Material Layers worked. That's quite confusing in practice,
especially to see what Material is a Node, or what is the "base material"
Best solution is to completely separate the two. This has been implemented
as follows now;
- The confusing "Input" node has been removed.
- When choosing a Material in Blender, you can define this Material to be
either 'normal' (default) or be the root of a Node tree.
- If a Material is a Node tree, you have to add Nodes in the tree to see
something happen. An empty Node tree doesn't do anything (black).
- If a Material is a Node Tree, the 'data browse' menus show it with an
'N' mark before the name. The 'data block' buttons display it with the
suffix 'NT' (instead of 'MA').
- In a Node Tree, any Material can be inserted, including itself. Only in
that case the Material is being used itself for shading.
UI changes:
Added a new Panel "Links", which shows:
- where the Material is linked to (Object, Mesh, etc)
- if the Material is a NodeTree or not
- the actual active Material in the Tree
The "Node" Panel itself now only shows buttons from the other nodes, when
they are active.
Further the Material Nodes themselves allow browsing and renaming or adding
new Materials now too.
Second half of today's work was cleaning up selection when the Nodes
overlap... it was possible to drag links from invisible sockets, or click
headers for invisible nodes, etc. This because the mouse input code was
not checking for visibility yet.
Works now even for buttons. :)
2005-12-29 18:08:01 +00:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
BLI_ghash_free(hash->ghash, nullptr, reinterpret_cast<GHashValFreeFP>(valfreefp));
|
|
|
|
|
MEM_freeN(hash);
|
2009-01-27 17:12:40 +00:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void BKE_node_instance_hash_insert(bNodeInstanceHash *hash, bNodeInstanceKey key, void *value)
|
2019-08-18 11:16:04 +02:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
bNodeInstanceHashEntry *entry = static_cast<bNodeInstanceHashEntry *>(value);
|
|
|
|
|
entry->key = key;
|
|
|
|
|
entry->tag = 0;
|
|
|
|
|
BLI_ghash_insert(hash->ghash, &entry->key, value);
|
2019-08-18 11:16:04 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void *BKE_node_instance_hash_lookup(bNodeInstanceHash *hash, bNodeInstanceKey key)
|
Nodes: Support storing socket link limits in bNodeSocketType
Currently the link limit of sockets is stored in bNodeSocket->limit.
This allows for a lot of flexibility, but is also very redundant.
In every case I've had to deal with so far, it would have "more correct"
to set the link limit per socket type and not per socket. I did not enforce
this constraint yet, because the link limit is exposed in the Python API,
which I did not want to break here.
In the future it might even make sense to only support only three kinds of link limits:
a) no links, b) at most one link, c) an arbitrary number links links. The other link
limits usually don't work well with tools (e.g. which link should be removed when a new
one is connected?) and is not used in practice. However, that is for another day.
Eventually, I would like to get rid of bNodeSocket->limit completely and replace it
either with fixed link limits or a callback in bNodeSocketType.
This patch consists of three parts:
**1. Support defining link limit in socket type**
This introduces a new `nodeSocketLinkLimit` function that serves as an indirection to
hide where the link limit of a socket is defined.
**2. Define link limits for builtin sockets on socket type**
Data sockets: one input, many outputs
Virtual sockets: one input, one output
Undefined sockets: many inputs, many outputs (to avoid that links are removed when the type of the socket is not known)
**3. Remove `bNodeSocketTemplate->limit`**
This wasn't used anymore after the second commit. Removing it simplifies socket definitions
in hundreds of places and removes a lot of redundancy.
Differential Revision: https://developer.blender.org/D7038
Reviewers: brecht
2020-03-06 12:20:05 +01:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
return BLI_ghash_lookup(hash->ghash, &key);
|
Nodes: Support storing socket link limits in bNodeSocketType
Currently the link limit of sockets is stored in bNodeSocket->limit.
This allows for a lot of flexibility, but is also very redundant.
In every case I've had to deal with so far, it would have "more correct"
to set the link limit per socket type and not per socket. I did not enforce
this constraint yet, because the link limit is exposed in the Python API,
which I did not want to break here.
In the future it might even make sense to only support only three kinds of link limits:
a) no links, b) at most one link, c) an arbitrary number links links. The other link
limits usually don't work well with tools (e.g. which link should be removed when a new
one is connected?) and is not used in practice. However, that is for another day.
Eventually, I would like to get rid of bNodeSocket->limit completely and replace it
either with fixed link limits or a callback in bNodeSocketType.
This patch consists of three parts:
**1. Support defining link limit in socket type**
This introduces a new `nodeSocketLinkLimit` function that serves as an indirection to
hide where the link limit of a socket is defined.
**2. Define link limits for builtin sockets on socket type**
Data sockets: one input, many outputs
Virtual sockets: one input, one output
Undefined sockets: many inputs, many outputs (to avoid that links are removed when the type of the socket is not known)
**3. Remove `bNodeSocketTemplate->limit`**
This wasn't used anymore after the second commit. Removing it simplifies socket definitions
in hundreds of places and removes a lot of redundancy.
Differential Revision: https://developer.blender.org/D7038
Reviewers: brecht
2020-03-06 12:20:05 +01:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
int BKE_node_instance_hash_remove(bNodeInstanceHash *hash,
|
|
|
|
|
bNodeInstanceKey key,
|
|
|
|
|
bNodeInstanceValueFP valfreefp)
|
2021-11-08 12:23:50 +01:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
return BLI_ghash_remove(hash->ghash, &key, nullptr, reinterpret_cast<GHashValFreeFP>(valfreefp));
|
2021-11-08 12:23:50 +01:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void BKE_node_instance_hash_clear(bNodeInstanceHash *hash, bNodeInstanceValueFP valfreefp)
|
2023-02-02 16:34:33 +01:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
BLI_ghash_clear(hash->ghash, nullptr, reinterpret_cast<GHashValFreeFP>(valfreefp));
|
2023-02-02 16:34:33 +01:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void *BKE_node_instance_hash_pop(bNodeInstanceHash *hash, bNodeInstanceKey key)
|
2021-11-08 12:23:50 +01:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
return BLI_ghash_popkey(hash->ghash, &key, nullptr);
|
2021-11-08 12:23:50 +01:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
int BKE_node_instance_hash_haskey(bNodeInstanceHash *hash, bNodeInstanceKey key)
|
2021-09-14 16:34:31 +02:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
return BLI_ghash_haskey(hash->ghash, &key);
|
2021-11-08 12:23:50 +01:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
int BKE_node_instance_hash_size(bNodeInstanceHash *hash)
|
2021-11-08 12:23:50 +01:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
return BLI_ghash_len(hash->ghash);
|
2021-09-14 16:34:31 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void BKE_node_instance_hash_clear_tags(bNodeInstanceHash *hash)
|
2013-03-18 16:34:57 +00:00
|
|
|
{
|
2024-05-13 16:07:12 +02:00
|
|
|
bNodeInstanceHashIterator iter;
|
2023-05-16 09:39:42 -04:00
|
|
|
|
|
|
|
|
NODE_INSTANCE_HASH_ITER (iter, hash) {
|
|
|
|
|
bNodeInstanceHashEntry *value = static_cast<bNodeInstanceHashEntry *>(
|
2024-05-13 16:07:12 +02:00
|
|
|
node_instance_hash_iterator_get_value(&iter));
|
2023-05-16 09:39:42 -04:00
|
|
|
|
|
|
|
|
value->tag = 0;
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2013-03-18 16:34:57 +00:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void BKE_node_instance_hash_tag(bNodeInstanceHash * /*hash*/, void *value)
|
2013-03-18 16:34:57 +00:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
bNodeInstanceHashEntry *entry = static_cast<bNodeInstanceHashEntry *>(value);
|
|
|
|
|
entry->tag = 1;
|
|
|
|
|
}
|
2013-03-18 16:34:57 +00:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
bool BKE_node_instance_hash_tag_key(bNodeInstanceHash *hash, bNodeInstanceKey key)
|
|
|
|
|
{
|
|
|
|
|
bNodeInstanceHashEntry *entry = static_cast<bNodeInstanceHashEntry *>(
|
|
|
|
|
BKE_node_instance_hash_lookup(hash, key));
|
2013-03-18 16:34:57 +00:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
if (entry) {
|
|
|
|
|
entry->tag = 1;
|
|
|
|
|
return true;
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2018-06-17 17:05:51 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
return false;
|
|
|
|
|
}
|
2018-06-17 17:05:51 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void BKE_node_instance_hash_remove_untagged(bNodeInstanceHash *hash,
|
|
|
|
|
bNodeInstanceValueFP valfreefp)
|
|
|
|
|
{
|
|
|
|
|
/* NOTE: Hash must not be mutated during iterating!
|
|
|
|
|
* Store tagged entries in a separate list and remove items afterward.
|
|
|
|
|
*/
|
|
|
|
|
bNodeInstanceKey *untagged = static_cast<bNodeInstanceKey *>(
|
|
|
|
|
MEM_mallocN(sizeof(bNodeInstanceKey) * BKE_node_instance_hash_size(hash),
|
|
|
|
|
"temporary node instance key list"));
|
2024-05-13 16:07:12 +02:00
|
|
|
bNodeInstanceHashIterator iter;
|
2023-05-16 09:39:42 -04:00
|
|
|
int num_untagged = 0;
|
|
|
|
|
NODE_INSTANCE_HASH_ITER (iter, hash) {
|
|
|
|
|
bNodeInstanceHashEntry *value = static_cast<bNodeInstanceHashEntry *>(
|
2024-05-13 16:07:12 +02:00
|
|
|
node_instance_hash_iterator_get_value(&iter));
|
2023-05-16 09:39:42 -04:00
|
|
|
|
|
|
|
|
if (!value->tag) {
|
2024-05-13 16:07:12 +02:00
|
|
|
untagged[num_untagged++] = node_instance_hash_iterator_get_key(&iter);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2013-03-18 16:34:57 +00:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
for (int i = 0; i < num_untagged; i++) {
|
|
|
|
|
BKE_node_instance_hash_remove(hash, untagged[i], valfreefp);
|
2013-03-18 16:34:57 +00:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
|
|
|
|
|
MEM_freeN(untagged);
|
2013-03-18 16:34:57 +00:00
|
|
|
}
|
|
|
|
|
|
2024-01-23 15:15:08 +01:00
|
|
|
/* Build a set of built-in node types to check for known types. */
|
2024-05-13 16:07:12 +02:00
|
|
|
static Set<int> get_known_node_types_set()
|
2024-01-23 15:15:08 +01:00
|
|
|
{
|
2024-05-13 16:07:12 +02:00
|
|
|
Set<int> result;
|
2024-01-23 15:15:08 +01:00
|
|
|
NODE_TYPES_BEGIN (ntype) {
|
|
|
|
|
result.add(ntype->type);
|
|
|
|
|
}
|
|
|
|
|
NODE_TYPES_END;
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool can_read_node_type(const int type)
|
|
|
|
|
{
|
|
|
|
|
/* Can always read custom node types. */
|
2024-04-02 16:07:45 +02:00
|
|
|
if (ELEM(type, NODE_CUSTOM, NODE_CUSTOM_GROUP)) {
|
2024-01-23 15:15:08 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Check known built-in types. */
|
2024-05-13 16:07:12 +02:00
|
|
|
static Set<int> known_types = get_known_node_types_set();
|
2024-01-23 15:15:08 +01:00
|
|
|
return known_types.contains(type);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void node_replace_undefined_types(bNode *node)
|
|
|
|
|
{
|
|
|
|
|
/* If the integer type is unknown then this node cannot be read. */
|
|
|
|
|
if (!can_read_node_type(node->type)) {
|
|
|
|
|
node->type = NODE_CUSTOM;
|
|
|
|
|
/* This type name is arbitrary, it just has to be unique enough to not match a future node
|
|
|
|
|
* idname. Includes the old type identifier for debugging purposes. */
|
|
|
|
|
const std::string old_idname = node->idname;
|
2024-02-14 13:56:03 +11:00
|
|
|
SNPRINTF(node->idname, "Undefined[%s]", old_idname.c_str());
|
2024-01-23 15:15:08 +01:00
|
|
|
node->typeinfo = &NodeTypeUndefined;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void ntreeUpdateAllNew(Main *main)
|
2013-03-18 16:34:57 +00:00
|
|
|
{
|
2024-01-23 15:15:08 +01:00
|
|
|
/* Replace unknown node types with "Undefined".
|
|
|
|
|
* This happens when loading files from newer Blender versions. Such nodes cannot be read
|
|
|
|
|
* reliably so replace the idname with an undefined type. This keeps links and socket names but
|
|
|
|
|
* discards storage and other type-specific data.
|
|
|
|
|
*
|
|
|
|
|
* Replacement has to happen after after-liblink-versioning, since some node types still get
|
|
|
|
|
* replaced in those late versioning steps. */
|
|
|
|
|
FOREACH_NODETREE_BEGIN (main, ntree, owner_id) {
|
|
|
|
|
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
|
|
|
|
|
node_replace_undefined_types(node);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
FOREACH_NODETREE_END;
|
2023-05-16 09:39:42 -04:00
|
|
|
/* Update all new node trees on file read or append, to add/remove sockets
|
|
|
|
|
* in groups nodes if the group changed, and handle any update flags that
|
|
|
|
|
* might have been set in file reading or versioning. */
|
|
|
|
|
FOREACH_NODETREE_BEGIN (main, ntree, owner_id) {
|
|
|
|
|
if (owner_id->tag & LIB_TAG_NEW) {
|
|
|
|
|
BKE_ntree_update_tag_all(ntree);
|
|
|
|
|
}
|
2013-03-18 16:34:57 +00:00
|
|
|
}
|
2023-05-16 09:39:42 -04:00
|
|
|
FOREACH_NODETREE_END;
|
|
|
|
|
BKE_ntree_update_main(main, nullptr);
|
2019-04-20 20:25:22 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void ntreeUpdateAllUsers(Main *main, ID *id)
|
2019-04-20 20:25:22 +02:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
if (id == nullptr) {
|
2020-12-02 13:25:25 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
bool need_update = false;
|
2023-05-15 15:14:22 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/* Update all users of ngroup, to add/remove sockets as needed. */
|
|
|
|
|
FOREACH_NODETREE_BEGIN (main, ntree, owner_id) {
|
|
|
|
|
for (bNode *node : ntree->all_nodes()) {
|
|
|
|
|
if (node->id == id) {
|
|
|
|
|
BKE_ntree_update_tag_node_property(ntree, node);
|
|
|
|
|
need_update = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
FOREACH_NODETREE_END;
|
|
|
|
|
if (need_update) {
|
|
|
|
|
BKE_ntree_update_main(main, nullptr);
|
2023-05-15 15:14:22 +02:00
|
|
|
}
|
2012-10-25 16:49:06 +00:00
|
|
|
}
|
|
|
|
|
|
2013-03-18 16:34:57 +00:00
|
|
|
/* ************* node type access ********** */
|
|
|
|
|
|
2023-05-07 15:22:58 +10:00
|
|
|
void nodeLabel(const bNodeTree *ntree, const bNode *node, char *label, const int label_maxncpy)
|
2011-02-07 16:41:57 +00:00
|
|
|
{
|
2018-01-07 22:29:25 +01:00
|
|
|
label[0] = '\0';
|
|
|
|
|
|
2017-06-28 20:50:21 +02:00
|
|
|
if (node->label[0] != '\0') {
|
2023-05-07 15:22:58 +10:00
|
|
|
BLI_strncpy(label, node->label, label_maxncpy);
|
2017-06-28 20:50:21 +02:00
|
|
|
}
|
|
|
|
|
else if (node->typeinfo->labelfunc) {
|
2023-05-07 15:22:58 +10:00
|
|
|
node->typeinfo->labelfunc(ntree, node, label, label_maxncpy);
|
2017-06-28 20:50:21 +02:00
|
|
|
}
|
2023-04-16 22:53:09 +02:00
|
|
|
if (label[0] != '\0') {
|
|
|
|
|
/* The previous methods (labelfunc) could not provide an adequate label for the node. */
|
|
|
|
|
return;
|
|
|
|
|
}
|
2018-01-07 22:29:25 +01:00
|
|
|
|
2023-05-24 20:27:10 +02:00
|
|
|
BLI_strncpy(label, IFACE_(node->typeinfo->ui_name), label_maxncpy);
|
2011-02-07 16:41:57 +00:00
|
|
|
}
|
|
|
|
|
|
2023-10-12 17:51:37 +02:00
|
|
|
const char *nodeSocketShortLabel(const bNodeSocket *sock)
|
|
|
|
|
{
|
|
|
|
|
if (sock->runtime->declaration != nullptr) {
|
2024-05-13 16:07:12 +02:00
|
|
|
StringRefNull short_label = sock->runtime->declaration->short_label;
|
2023-10-12 17:51:37 +02:00
|
|
|
if (!short_label.is_empty()) {
|
|
|
|
|
return sock->runtime->declaration->short_label.data();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-11 15:31:40 +00:00
|
|
|
const char *nodeSocketLabel(const bNodeSocket *sock)
|
|
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
return (sock->label[0] != '\0') ? sock->label : sock->name;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void node_type_base_defaults(bNodeType *ntype)
|
|
|
|
|
{
|
|
|
|
|
/* default size values */
|
2024-05-13 16:07:12 +02:00
|
|
|
node_type_size_preset(ntype, eNodeSizePreset::Default);
|
2023-05-16 09:39:42 -04:00
|
|
|
ntype->height = 100;
|
|
|
|
|
ntype->minheight = 30;
|
|
|
|
|
ntype->maxheight = FLT_MAX;
|
2020-02-11 15:31:40 +00:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
/* allow this node for any tree type */
|
|
|
|
|
static bool node_poll_default(const bNodeType * /*ntype*/,
|
|
|
|
|
const bNodeTree * /*ntree*/,
|
|
|
|
|
const char ** /*disabled_hint*/)
|
2008-11-12 22:03:11 +00:00
|
|
|
{
|
2023-05-16 09:39:42 -04:00
|
|
|
return true;
|
2011-09-05 21:01:50 +00:00
|
|
|
}
|
|
|
|
|
|
2022-12-28 20:15:41 -05:00
|
|
|
static bool node_poll_instance_default(const bNode *node,
|
|
|
|
|
const bNodeTree *ntree,
|
|
|
|
|
const char **disabled_hint)
|
2011-09-05 21:01:50 +00:00
|
|
|
{
|
2021-04-12 18:43:23 +02:00
|
|
|
return node->typeinfo->poll(node->typeinfo, ntree, disabled_hint);
|
2008-11-12 22:03:11 +00:00
|
|
|
}
|
|
|
|
|
|
2023-03-02 23:14:59 +01:00
|
|
|
void node_type_base(bNodeType *ntype, const int type, const char *name, const short nclass)
|
2011-09-05 21:01:50 +00:00
|
|
|
{
|
2013-03-18 16:34:57 +00:00
|
|
|
/* Use static type info header to map static int type to identifier string and RNA struct type.
|
|
|
|
|
* Associate the RNA struct type with the bNodeType.
|
|
|
|
|
* Dynamically registered nodes will create an RNA type at runtime
|
|
|
|
|
* and call RNA_struct_blender_type_set, so this only needs to be done for old RNA types
|
|
|
|
|
* created in makesrna, which can not be associated to a bNodeType immediately,
|
|
|
|
|
* since bNodeTypes are registered afterward ...
|
|
|
|
|
*/
|
2013-12-22 14:11:10 +11:00
|
|
|
#define DefNode(Category, ID, DefFunc, EnumName, StructName, UIName, UIDesc) \
|
2023-08-05 09:12:27 +02:00
|
|
|
case ID: { \
|
2023-05-09 12:50:37 +10:00
|
|
|
STRNCPY(ntype->idname, #Category #StructName); \
|
2023-08-05 09:12:27 +02:00
|
|
|
StructRNA *srna = RNA_struct_find(#Category #StructName); \
|
|
|
|
|
BLI_assert(srna != nullptr); \
|
|
|
|
|
ntype->rna_ext.srna = srna; \
|
|
|
|
|
RNA_struct_blender_type_set(srna, ntype); \
|
|
|
|
|
RNA_def_struct_ui_text(srna, UIName, UIDesc); \
|
2023-08-04 21:43:54 +02:00
|
|
|
ntype->enum_name_legacy = EnumName; \
|
|
|
|
|
STRNCPY(ntype->ui_description, UIDesc); \
|
2023-08-05 09:12:27 +02:00
|
|
|
break; \
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-03-18 16:34:57 +00:00
|
|
|
switch (type) {
|
2013-12-22 14:11:10 +11:00
|
|
|
#include "NOD_static_types.h"
|
2011-09-05 21:01:50 +00:00
|
|
|
}
|
2018-06-17 17:05:51 +02:00
|
|
|
|
2013-03-18 16:34:57 +00:00
|
|
|
/* make sure we have a valid type (everything registered) */
|
|
|
|
|
BLI_assert(ntype->idname[0] != '\0');
|
2018-06-17 17:05:51 +02:00
|
|
|
|
2013-03-18 16:34:57 +00:00
|
|
|
ntype->type = type;
|
2023-05-09 12:50:37 +10:00
|
|
|
STRNCPY(ntype->ui_name, name);
|
2013-03-18 16:34:57 +00:00
|
|
|
ntype->nclass = nclass;
|
|
|
|
|
|
|
|
|
|
node_type_base_defaults(ntype);
|
|
|
|
|
|
|
|
|
|
ntype->poll = node_poll_default;
|
|
|
|
|
ntype->poll_instance = node_poll_instance_default;
|
2011-09-05 21:01:50 +00:00
|
|
|
}
|
2007-03-26 15:07:38 +00:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void node_type_base_custom(bNodeType *ntype,
|
|
|
|
|
const char *idname,
|
|
|
|
|
const char *name,
|
2023-08-16 09:29:46 +02:00
|
|
|
const char *enum_name,
|
2023-05-16 09:39:42 -04:00
|
|
|
const short nclass)
|
|
|
|
|
{
|
|
|
|
|
STRNCPY(ntype->idname, idname);
|
|
|
|
|
ntype->type = NODE_CUSTOM;
|
|
|
|
|
STRNCPY(ntype->ui_name, name);
|
|
|
|
|
ntype->nclass = nclass;
|
2023-08-16 09:29:46 +02:00
|
|
|
ntype->enum_name_legacy = enum_name;
|
2023-05-16 09:39:42 -04:00
|
|
|
|
2024-05-13 16:07:12 +02:00
|
|
|
node_type_base_defaults(ntype);
|
2023-05-16 09:39:42 -04:00
|
|
|
}
|
|
|
|
|
|
2023-10-16 19:09:07 +02:00
|
|
|
std::optional<eCustomDataType> socket_type_to_custom_data_type(eNodeSocketDatatype type)
|
|
|
|
|
{
|
|
|
|
|
switch (type) {
|
|
|
|
|
case SOCK_FLOAT:
|
|
|
|
|
return CD_PROP_FLOAT;
|
|
|
|
|
case SOCK_VECTOR:
|
|
|
|
|
return CD_PROP_FLOAT3;
|
|
|
|
|
case SOCK_RGBA:
|
|
|
|
|
return CD_PROP_COLOR;
|
|
|
|
|
case SOCK_BOOLEAN:
|
|
|
|
|
return CD_PROP_BOOL;
|
|
|
|
|
case SOCK_ROTATION:
|
|
|
|
|
return CD_PROP_QUATERNION;
|
2024-02-13 18:59:36 +01:00
|
|
|
case SOCK_MATRIX:
|
|
|
|
|
return CD_PROP_FLOAT4X4;
|
2023-10-16 19:09:07 +02:00
|
|
|
case SOCK_INT:
|
|
|
|
|
return CD_PROP_INT32;
|
|
|
|
|
case SOCK_STRING:
|
|
|
|
|
return CD_PROP_STRING;
|
|
|
|
|
default:
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::optional<eNodeSocketDatatype> custom_data_type_to_socket_type(eCustomDataType type)
|
|
|
|
|
{
|
|
|
|
|
switch (type) {
|
|
|
|
|
case CD_PROP_FLOAT:
|
|
|
|
|
return SOCK_FLOAT;
|
2024-03-25 23:04:11 +01:00
|
|
|
case CD_PROP_INT8:
|
|
|
|
|
return SOCK_INT;
|
2023-10-16 19:09:07 +02:00
|
|
|
case CD_PROP_INT32:
|
|
|
|
|
return SOCK_INT;
|
|
|
|
|
case CD_PROP_FLOAT3:
|
|
|
|
|
return SOCK_VECTOR;
|
|
|
|
|
case CD_PROP_FLOAT2:
|
|
|
|
|
return SOCK_VECTOR;
|
|
|
|
|
case CD_PROP_BOOL:
|
|
|
|
|
return SOCK_BOOLEAN;
|
|
|
|
|
case CD_PROP_COLOR:
|
|
|
|
|
return SOCK_RGBA;
|
|
|
|
|
case CD_PROP_BYTE_COLOR:
|
|
|
|
|
return SOCK_RGBA;
|
|
|
|
|
case CD_PROP_QUATERNION:
|
|
|
|
|
return SOCK_ROTATION;
|
2024-02-13 18:59:36 +01:00
|
|
|
case CD_PROP_FLOAT4X4:
|
|
|
|
|
return SOCK_MATRIX;
|
2023-10-16 19:09:07 +02:00
|
|
|
default:
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-17 14:00:07 +01:00
|
|
|
static const CPPType *slow_socket_type_to_geo_nodes_base_cpp_type(const eNodeSocketDatatype type)
|
|
|
|
|
{
|
|
|
|
|
const char *socket_idname = nodeStaticSocketType(type, 0);
|
|
|
|
|
const bNodeSocketType *typeinfo = nodeSocketTypeFind(socket_idname);
|
|
|
|
|
return typeinfo->base_cpp_type;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const CPPType *socket_type_to_geo_nodes_base_cpp_type(const eNodeSocketDatatype type)
|
|
|
|
|
{
|
|
|
|
|
const CPPType *cpp_type;
|
|
|
|
|
switch (type) {
|
|
|
|
|
case SOCK_FLOAT:
|
|
|
|
|
cpp_type = &CPPType::get<float>();
|
|
|
|
|
break;
|
|
|
|
|
case SOCK_INT:
|
|
|
|
|
cpp_type = &CPPType::get<int>();
|
|
|
|
|
break;
|
|
|
|
|
case SOCK_RGBA:
|
|
|
|
|
cpp_type = &CPPType::get<ColorGeometry4f>();
|
|
|
|
|
break;
|
|
|
|
|
case SOCK_BOOLEAN:
|
|
|
|
|
cpp_type = &CPPType::get<bool>();
|
|
|
|
|
break;
|
|
|
|
|
case SOCK_VECTOR:
|
|
|
|
|
cpp_type = &CPPType::get<float3>();
|
|
|
|
|
break;
|
|
|
|
|
case SOCK_ROTATION:
|
|
|
|
|
cpp_type = &CPPType::get<math::Quaternion>();
|
|
|
|
|
break;
|
2024-02-19 21:49:38 -05:00
|
|
|
case SOCK_MATRIX:
|
|
|
|
|
cpp_type = &CPPType::get<float4x4>();
|
|
|
|
|
break;
|
2023-12-17 14:00:07 +01:00
|
|
|
default:
|
|
|
|
|
cpp_type = slow_socket_type_to_geo_nodes_base_cpp_type(type);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
BLI_assert(cpp_type == slow_socket_type_to_geo_nodes_base_cpp_type(type));
|
|
|
|
|
return cpp_type;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::optional<eNodeSocketDatatype> geo_nodes_base_cpp_type_to_socket_type(const CPPType &type)
|
|
|
|
|
{
|
|
|
|
|
if (type.is<float>()) {
|
|
|
|
|
return SOCK_FLOAT;
|
|
|
|
|
}
|
|
|
|
|
if (type.is<int>()) {
|
|
|
|
|
return SOCK_INT;
|
|
|
|
|
}
|
|
|
|
|
if (type.is<float3>()) {
|
|
|
|
|
return SOCK_VECTOR;
|
|
|
|
|
}
|
|
|
|
|
if (type.is<ColorGeometry4f>()) {
|
|
|
|
|
return SOCK_RGBA;
|
|
|
|
|
}
|
|
|
|
|
if (type.is<bool>()) {
|
|
|
|
|
return SOCK_BOOLEAN;
|
|
|
|
|
}
|
|
|
|
|
if (type.is<math::Quaternion>()) {
|
|
|
|
|
return SOCK_ROTATION;
|
|
|
|
|
}
|
2024-02-13 18:59:36 +01:00
|
|
|
if (type.is<float4x4>()) {
|
|
|
|
|
return SOCK_MATRIX;
|
|
|
|
|
}
|
2023-12-17 14:00:07 +01:00
|
|
|
if (type.is<std::string>()) {
|
|
|
|
|
return SOCK_STRING;
|
|
|
|
|
}
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-20 22:33:17 +01:00
|
|
|
std::optional<VolumeGridType> socket_type_to_grid_type(const eNodeSocketDatatype type)
|
|
|
|
|
{
|
|
|
|
|
switch (type) {
|
|
|
|
|
case SOCK_BOOLEAN:
|
|
|
|
|
return VOLUME_GRID_BOOLEAN;
|
|
|
|
|
case SOCK_FLOAT:
|
|
|
|
|
return VOLUME_GRID_FLOAT;
|
|
|
|
|
case SOCK_INT:
|
|
|
|
|
return VOLUME_GRID_INT;
|
|
|
|
|
case SOCK_VECTOR:
|
|
|
|
|
return VOLUME_GRID_VECTOR_FLOAT;
|
|
|
|
|
default:
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::optional<eNodeSocketDatatype> grid_type_to_socket_type(const VolumeGridType type)
|
|
|
|
|
{
|
|
|
|
|
switch (type) {
|
|
|
|
|
case VOLUME_GRID_BOOLEAN:
|
|
|
|
|
return SOCK_BOOLEAN;
|
|
|
|
|
case VOLUME_GRID_FLOAT:
|
|
|
|
|
return SOCK_FLOAT;
|
|
|
|
|
case VOLUME_GRID_INT:
|
|
|
|
|
return SOCK_INT;
|
|
|
|
|
case VOLUME_GRID_VECTOR_FLOAT:
|
|
|
|
|
return SOCK_VECTOR;
|
|
|
|
|
default:
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
struct SocketTemplateIdentifierCallbackData {
|
|
|
|
|
bNodeSocketTemplate *list;
|
|
|
|
|
bNodeSocketTemplate *ntemp;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static bool unique_socket_template_identifier_check(void *arg, const char *name)
|
|
|
|
|
{
|
|
|
|
|
const SocketTemplateIdentifierCallbackData *data =
|
|
|
|
|
static_cast<const SocketTemplateIdentifierCallbackData *>(arg);
|
|
|
|
|
|
|
|
|
|
for (bNodeSocketTemplate *ntemp = data->list; ntemp->type >= 0; ntemp++) {
|
|
|
|
|
if (ntemp != data->ntemp) {
|
|
|
|
|
if (STREQ(ntemp->identifier, name)) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void unique_socket_template_identifier(bNodeSocketTemplate *list,
|
|
|
|
|
bNodeSocketTemplate *ntemp,
|
|
|
|
|
const char defname[],
|
|
|
|
|
const char delim)
|
|
|
|
|
{
|
|
|
|
|
SocketTemplateIdentifierCallbackData data;
|
|
|
|
|
data.list = list;
|
|
|
|
|
data.ntemp = ntemp;
|
|
|
|
|
|
|
|
|
|
BLI_uniquename_cb(unique_socket_template_identifier_check,
|
|
|
|
|
&data,
|
|
|
|
|
defname,
|
|
|
|
|
delim,
|
|
|
|
|
ntemp->identifier,
|
|
|
|
|
sizeof(ntemp->identifier));
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-30 13:06:54 -06:00
|
|
|
void node_type_socket_templates(bNodeType *ntype,
|
|
|
|
|
bNodeSocketTemplate *inputs,
|
|
|
|
|
bNodeSocketTemplate *outputs)
|
2011-09-05 21:01:50 +00:00
|
|
|
{
|
|
|
|
|
ntype->inputs = inputs;
|
|
|
|
|
ntype->outputs = outputs;
|
2018-06-17 17:05:51 +02:00
|
|
|
|
2013-03-18 16:34:57 +00:00
|
|
|
/* automatically generate unique identifiers */
|
|
|
|
|
if (inputs) {
|
|
|
|
|
/* clear identifier strings (uninitialized memory) */
|
2020-09-02 18:28:04 +02:00
|
|
|
for (bNodeSocketTemplate *ntemp = inputs; ntemp->type >= 0; ntemp++) {
|
2013-03-18 16:34:57 +00:00
|
|
|
ntemp->identifier[0] = '\0';
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2018-06-17 17:05:51 +02:00
|
|
|
|
2020-09-02 18:28:04 +02:00
|
|
|
for (bNodeSocketTemplate *ntemp = inputs; ntemp->type >= 0; ntemp++) {
|
2023-05-09 12:50:37 +10:00
|
|
|
STRNCPY(ntemp->identifier, ntemp->name);
|
2013-03-18 16:34:57 +00:00
|
|
|
unique_socket_template_identifier(inputs, ntemp, ntemp->identifier, '_');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (outputs) {
|
|
|
|
|
/* clear identifier strings (uninitialized memory) */
|
2020-09-02 18:28:04 +02:00
|
|
|
for (bNodeSocketTemplate *ntemp = outputs; ntemp->type >= 0; ntemp++) {
|
2013-03-18 16:34:57 +00:00
|
|
|
ntemp->identifier[0] = '\0';
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2018-06-17 17:05:51 +02:00
|
|
|
|
2020-09-02 18:28:04 +02:00
|
|
|
for (bNodeSocketTemplate *ntemp = outputs; ntemp->type >= 0; ntemp++) {
|
2023-05-09 12:50:37 +10:00
|
|
|
STRNCPY(ntemp->identifier, ntemp->name);
|
2013-03-18 16:34:57 +00:00
|
|
|
unique_socket_template_identifier(outputs, ntemp, ntemp->identifier, '_');
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-02-07 09:33:36 +00:00
|
|
|
}
|
|
|
|
|
|
2023-03-02 23:14:59 +01:00
|
|
|
void node_type_size(bNodeType *ntype, const int width, const int minwidth, const int maxwidth)
|
2011-02-07 09:33:36 +00:00
|
|
|
{
|
|
|
|
|
ntype->width = width;
|
|
|
|
|
ntype->minwidth = minwidth;
|
2019-04-22 09:39:35 +10:00
|
|
|
if (maxwidth <= minwidth) {
|
2011-09-05 21:01:50 +00:00
|
|
|
ntype->maxwidth = FLT_MAX;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
|
|
|
|
else {
|
2011-09-05 21:01:50 +00:00
|
|
|
ntype->maxwidth = maxwidth;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2011-02-07 09:33:36 +00:00
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void node_type_size_preset(bNodeType *ntype, const eNodeSizePreset size)
|
|
|
|
|
{
|
|
|
|
|
switch (size) {
|
2024-05-01 15:27:34 +02:00
|
|
|
case eNodeSizePreset::Default:
|
2023-05-16 09:39:42 -04:00
|
|
|
node_type_size(ntype, 140, 100, NODE_DEFAULT_MAX_WIDTH);
|
|
|
|
|
break;
|
2024-05-01 15:27:34 +02:00
|
|
|
case eNodeSizePreset::Small:
|
2023-05-16 09:39:42 -04:00
|
|
|
node_type_size(ntype, 100, 80, NODE_DEFAULT_MAX_WIDTH);
|
|
|
|
|
break;
|
2024-05-01 15:27:34 +02:00
|
|
|
case eNodeSizePreset::Middle:
|
2023-05-16 09:39:42 -04:00
|
|
|
node_type_size(ntype, 150, 120, NODE_DEFAULT_MAX_WIDTH);
|
|
|
|
|
break;
|
2024-05-01 15:27:34 +02:00
|
|
|
case eNodeSizePreset::Large:
|
2023-05-16 09:39:42 -04:00
|
|
|
node_type_size(ntype, 240, 140, NODE_DEFAULT_MAX_WIDTH);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void node_type_storage(bNodeType *ntype,
|
|
|
|
|
const char *storagename,
|
|
|
|
|
void (*freefunc)(bNode *node),
|
|
|
|
|
void (*copyfunc)(bNodeTree *dest_ntree,
|
|
|
|
|
bNode *dest_node,
|
|
|
|
|
const bNode *src_node))
|
|
|
|
|
{
|
|
|
|
|
if (storagename) {
|
|
|
|
|
STRNCPY(ntype->storagename, storagename);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
ntype->storagename[0] = '\0';
|
|
|
|
|
}
|
|
|
|
|
ntype->copyfunc = copyfunc;
|
|
|
|
|
ntype->freefunc = freefunc;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void BKE_node_system_init()
|
|
|
|
|
{
|
2024-05-13 16:07:12 +02:00
|
|
|
nodetreetypes_hash = BLI_ghash_str_new("nodetreetypes_hash gh");
|
|
|
|
|
nodetypes_hash = BLI_ghash_str_new("nodetypes_hash gh");
|
|
|
|
|
nodetypes_alias_hash = BLI_ghash_str_new("nodetypes_alias_hash gh");
|
|
|
|
|
nodesockettypes_hash = BLI_ghash_str_new("nodesockettypes_hash gh");
|
2009-08-15 16:43:03 +00:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
register_nodes();
|
|
|
|
|
}
|
2013-03-26 22:37:41 +00:00
|
|
|
|
2023-05-16 09:39:42 -04:00
|
|
|
void BKE_node_system_exit()
|
|
|
|
|
{
|
2024-05-13 16:07:12 +02:00
|
|
|
if (nodetypes_alias_hash) {
|
|
|
|
|
BLI_ghash_free(nodetypes_alias_hash, MEM_freeN, MEM_freeN);
|
|
|
|
|
nodetypes_alias_hash = nullptr;
|
Cycles: Merge Anisotropic BSDF node into Glossy BSDF node
Used to be https://archive.blender.org/developer/D17123.
Internally these are already using the same code path anyways, there's no point in maintaining two distinct nodes.
The obvious approach would be to add Anisotropy controls to the Glossy BSDF node and remove the Anisotropic BSDF node. However, that would break forward compability, since older Blender versions don't know how to handle the Anisotropy input on the Glossy BSDF node.
Therefore, this commit technically removes the Glossy BSDF node, uses versioning to replace them with an Anisotropic BSDF node, and renames that node to "Glossy BSDF".
That way, when you open a new file in an older version, all the nodes show up as Anisotropic BSDF nodes and render correctly.
This is a bit ugly internally since we need to preserve the old `idname` which now no longer matches the UI name, but that's not too bad.
Also removes the "Sharp" distribution option and replaces it with GGX, sets Roughness to zero and disconnects any input to the Roughness socket.
Pull Request: https://projects.blender.org/blender/blender/pulls/104445
2023-05-18 23:12:20 +02:00
|
|
|
}
|
|
|
|
|
|
2024-05-13 16:07:12 +02:00
|
|
|
if (nodetypes_hash) {
|
2023-05-16 09:39:42 -04:00
|
|
|
NODE_TYPES_BEGIN (nt) {
|
|
|
|
|
if (nt->rna_ext.free) {
|
|
|
|
|
nt->rna_ext.free(nt->rna_ext.data);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
NODE_TYPES_END;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2024-05-13 16:07:12 +02:00
|
|
|
BLI_ghash_free(nodetypes_hash, nullptr, node_free_type);
|
|
|
|
|
nodetypes_hash = nullptr;
|
2023-05-16 09:39:42 -04:00
|
|
|
}
|
Render Layers and Collections (merge from render-layers)
Design Documents
----------------
* https://wiki.blender.org/index.php/Dev:2.8/Source/Layers
* https://wiki.blender.org/index.php/Dev:2.8/Source/DataDesignRevised
User Commit Log
---------------
* New Layer and Collection system to replace render layers and viewport layers.
* A layer is a set of collections of objects (and their drawing options) required for specific tasks.
* A collection is a set of objects, equivalent of the old layers in Blender. A collection can be shared across multiple layers.
* All Scenes have a master collection that all other collections are children of.
* New collection "context" tab (in Properties Editor)
* New temporary viewport "collections" panel to control per-collection
visibility
Missing User Features
---------------------
* Collection "Filter"
Option to add objects based on their names
* Collection Manager operators
The existing buttons are placeholders
* Collection Manager drawing
The editor main region is empty
* Collection Override
* Per-Collection engine settings
This will come as a separate commit, as part of the clay-engine branch
Dev Commit Log
--------------
* New DNA file (DNA_layer_types.h) with the new structs
We are replacing Base by a new extended Base while keeping it backward
compatible with some legacy settings (i.e., lay, flag_legacy).
Renamed all Base to BaseLegacy to make it clear the areas of code that
still need to be converted
Note: manual changes were required on - deg_builder_nodes.h, rna_object.c, KX_Light.cpp
* Unittesting for main syncronization requirements
- read, write, add/copy/remove objects, copy scene, collection
link/unlinking, context)
* New Editor: Collection Manager
Based on patch by Julian Eisel
This is extracted from the layer-manager branch. With the following changes:
- Renamed references of layer manager to collections manager
- I doesn't include the editors/space_collections/ draw and util files
- The drawing code itself will be implemented separately by Julian
* Base / Object:
A little note about them. Original Blender code would try to keep them
in sync through the code, juggling flags back and forth. This will now
be handled by Depsgraph, keeping Object and Bases more separated
throughout the non-rendering code.
Scene.base is being cleared in doversion, and the old viewport drawing
code was poorly converted to use the new bases while the new viewport
code doesn't get merged and replace the old one.
Python API Changes
------------------
```
- scene.layers
+ # no longer exists
- scene.objects
+ scene.scene_layers.active.objects
- scene.objects.active
+ scene.render_layers.active.objects.active
- bpy.context.scene.objects.link()
+ bpy.context.scene_collection.objects.link()
- bpy_extras.object_utils.object_data_add(context, obdata, operator=None, use_active_layer=True, name=None)
+ bpy_extras.object_utils.object_data_add(context, obdata, operator=None, name=None)
- bpy.context.object.select
+ bpy.context.object.select = True
+ bpy.context.object.select = False
+ bpy.context.object.select_get()
+ bpy.context.object.select_set(action='SELECT')
+ bpy.context.object.select_set(action='DESELECT')
-AddObjectHelper.layers
+ # no longer exists
```
2017-02-07 10:18:38 +01:00
|
|
|
|
2024-05-13 16:07:12 +02:00
|
|
|
if (nodesockettypes_hash) {
|
2023-05-16 09:39:42 -04:00
|
|
|
NODE_SOCKET_TYPES_BEGIN (st) {
|
|
|
|
|
if (st->ext_socket.free) {
|
|
|
|
|
st->ext_socket.free(st->ext_socket.data);
|
|
|
|
|
}
|
|
|
|
|
if (st->ext_interface.free) {
|
|
|
|
|
st->ext_interface.free(st->ext_interface.data);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
NODE_SOCKET_TYPES_END;
|
|
|
|
|
|
2024-05-13 16:07:12 +02:00
|
|
|
BLI_ghash_free(nodesockettypes_hash, nullptr, node_free_socket_type);
|
|
|
|
|
nodesockettypes_hash = nullptr;
|
2023-05-16 09:39:42 -04:00
|
|
|
}
|
|
|
|
|
|
2024-05-13 16:07:12 +02:00
|
|
|
if (nodetreetypes_hash) {
|
2023-05-16 09:39:42 -04:00
|
|
|
NODE_TREE_TYPES_BEGIN (nt) {
|
|
|
|
|
if (nt->rna_ext.free) {
|
|
|
|
|
nt->rna_ext.free(nt->rna_ext.data);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
NODE_TREE_TYPES_END;
|
|
|
|
|
|
2024-05-13 16:07:12 +02:00
|
|
|
BLI_ghash_free(nodetreetypes_hash, nullptr, ntree_free_type);
|
|
|
|
|
nodetreetypes_hash = nullptr;
|
2023-05-16 09:39:42 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/* NodeTree Iterator Helpers (FOREACH_NODETREE_BEGIN) */
|
|
|
|
|
|
|
|
|
|
void BKE_node_tree_iter_init(NodeTreeIterStore *ntreeiter, Main *bmain)
|
|
|
|
|
{
|
|
|
|
|
ntreeiter->ngroup = (bNodeTree *)bmain->nodetrees.first;
|
|
|
|
|
ntreeiter->scene = (Scene *)bmain->scenes.first;
|
|
|
|
|
ntreeiter->mat = (Material *)bmain->materials.first;
|
|
|
|
|
ntreeiter->tex = (Tex *)bmain->textures.first;
|
|
|
|
|
ntreeiter->light = (Light *)bmain->lights.first;
|
|
|
|
|
ntreeiter->world = (World *)bmain->worlds.first;
|
|
|
|
|
ntreeiter->linestyle = (FreestyleLineStyle *)bmain->linestyles.first;
|
|
|
|
|
}
|
|
|
|
|
bool BKE_node_tree_iter_step(NodeTreeIterStore *ntreeiter, bNodeTree **r_nodetree, ID **r_id)
|
|
|
|
|
{
|
|
|
|
|
if (ntreeiter->ngroup) {
|
|
|
|
|
bNodeTree &node_tree = *reinterpret_cast<bNodeTree *>(ntreeiter->ngroup);
|
|
|
|
|
*r_nodetree = &node_tree;
|
|
|
|
|
*r_id = &node_tree.id;
|
|
|
|
|
ntreeiter->ngroup = reinterpret_cast<bNodeTree *>(node_tree.id.next);
|
|
|
|
|
}
|
|
|
|
|
else if (ntreeiter->scene) {
|
|
|
|
|
*r_nodetree = reinterpret_cast<bNodeTree *>(ntreeiter->scene->nodetree);
|
|
|
|
|
*r_id = &ntreeiter->scene->id;
|
|
|
|
|
ntreeiter->scene = reinterpret_cast<Scene *>(ntreeiter->scene->id.next);
|
|
|
|
|
}
|
|
|
|
|
else if (ntreeiter->mat) {
|
|
|
|
|
*r_nodetree = reinterpret_cast<bNodeTree *>(ntreeiter->mat->nodetree);
|
|
|
|
|
*r_id = &ntreeiter->mat->id;
|
|
|
|
|
ntreeiter->mat = reinterpret_cast<Material *>(ntreeiter->mat->id.next);
|
|
|
|
|
}
|
|
|
|
|
else if (ntreeiter->tex) {
|
|
|
|
|
*r_nodetree = reinterpret_cast<bNodeTree *>(ntreeiter->tex->nodetree);
|
|
|
|
|
*r_id = &ntreeiter->tex->id;
|
|
|
|
|
ntreeiter->tex = reinterpret_cast<Tex *>(ntreeiter->tex->id.next);
|
|
|
|
|
}
|
|
|
|
|
else if (ntreeiter->light) {
|
|
|
|
|
*r_nodetree = reinterpret_cast<bNodeTree *>(ntreeiter->light->nodetree);
|
|
|
|
|
*r_id = &ntreeiter->light->id;
|
|
|
|
|
ntreeiter->light = reinterpret_cast<Light *>(ntreeiter->light->id.next);
|
|
|
|
|
}
|
|
|
|
|
else if (ntreeiter->world) {
|
|
|
|
|
*r_nodetree = reinterpret_cast<bNodeTree *>(ntreeiter->world->nodetree);
|
|
|
|
|
*r_id = &ntreeiter->world->id;
|
|
|
|
|
ntreeiter->world = reinterpret_cast<World *>(ntreeiter->world->id.next);
|
|
|
|
|
}
|
|
|
|
|
else if (ntreeiter->linestyle) {
|
|
|
|
|
*r_nodetree = reinterpret_cast<bNodeTree *>(ntreeiter->linestyle->nodetree);
|
|
|
|
|
*r_id = &ntreeiter->linestyle->id;
|
|
|
|
|
ntreeiter->linestyle = reinterpret_cast<FreestyleLineStyle *>(ntreeiter->linestyle->id.next);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BKE_nodetree_remove_layer_n(bNodeTree *ntree, Scene *scene, const int layer_index)
|
|
|
|
|
{
|
|
|
|
|
BLI_assert(layer_index != -1);
|
|
|
|
|
BLI_assert(scene != nullptr);
|
|
|
|
|
for (bNode *node : ntree->all_nodes()) {
|
|
|
|
|
if (node->type == CMP_NODE_R_LAYERS && node->id == &scene->id) {
|
|
|
|
|
if (node->custom1 == layer_index) {
|
|
|
|
|
node->custom1 = 0;
|
|
|
|
|
}
|
|
|
|
|
else if (node->custom1 > layer_index) {
|
|
|
|
|
node->custom1--;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-05-13 16:07:12 +02:00
|
|
|
|
|
|
|
|
} // namespace blender::bke
|