Node panels: New DNA and C++ API for node group interfaces

Part 1/3 of #109135, #110272

Adds a new DNA structure for defining node group interfaces without
using `bNodeSocket` and with additional node UI item types.

Node group interfaces are organized as a hierarchy of "items", which
can be sockets or panels. Panels can contain both sockets and other
panels (although nested panels beyond the root panel may be disabled to
avoid complexity on the user level).

Sockets can be added to the interface in any order, not just the
conventional outputs..inputs order. Sockets can be marked as both input
and output, generating 2 sockets on node instances.

The C++ API in the DNA struct allows manipulating the interface
declaration by adding and removing items, moving them inside the
interface or into a different panel.

Pull Request: https://projects.blender.org/blender/blender/pulls/110885
This commit is contained in:
Lukas Tönne
2023-08-09 10:06:31 +02:00
parent 0df7d100a3
commit 090f365cbd
9 changed files with 1938 additions and 1 deletions

View File

@@ -53,6 +53,7 @@ set(SRC_DNA_INC
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_movieclip_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_nla_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_node_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_node_tree_interface_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_object_enums.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_object_fluidsim_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_object_force_types.h

View File

@@ -0,0 +1,220 @@
/* SPDX-FileCopyrightText: 2023 Blender Foundation
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "DNA_node_tree_interface_types.h"
#include "DNA_node_types.h"
#include "BKE_node.h"
#include <queue>
#include <type_traits>
#include "BLI_parameter_pack_utils.hh"
#include "BLI_vector.hh"
namespace blender::bke {
/* Runtime topology cache for linear access to items. */
struct bNodeTreeInterfaceCache {
Vector<bNodeTreeInterfaceItem *> items;
Vector<bNodeTreeInterfaceSocket *> inputs;
Vector<bNodeTreeInterfaceSocket *> outputs;
void rebuild(bNodeTreeInterface &tree_interface);
};
namespace node_interface {
namespace detail {
template<typename T> static bool item_is_type(const bNodeTreeInterfaceItem &item)
{
bool match = false;
switch (item.item_type) {
case NODE_INTERFACE_SOCKET: {
match |= std::is_same<T, bNodeTreeInterfaceSocket>::value;
break;
}
case NODE_INTERFACE_PANEL: {
match |= std::is_same<T, bNodeTreeInterfacePanel>::value;
break;
}
}
return match;
}
} // namespace detail
template<typename T> T &get_item_as(bNodeTreeInterfaceItem &item)
{
BLI_assert(detail::item_is_type<T>(item));
return reinterpret_cast<T &>(item);
}
template<typename T> const T &get_item_as(const bNodeTreeInterfaceItem &item)
{
BLI_assert(detail::item_is_type<T>(item));
return reinterpret_cast<const T &>(item);
}
template<typename T> T *get_item_as(bNodeTreeInterfaceItem *item)
{
if (item && detail::item_is_type<T>(*item)) {
return reinterpret_cast<T *>(item);
}
return nullptr;
}
template<typename T> const T *get_item_as(const bNodeTreeInterfaceItem *item)
{
if (item && detail::item_is_type<T>(*item)) {
return reinterpret_cast<const T *>(item);
}
return nullptr;
}
namespace socket_types {
constexpr const char *node_socket_data_float = "NodeSocketFloat";
constexpr const char *node_socket_data_int = "NodeSocketInt";
constexpr const char *node_socket_data_bool = "NodeSocketBool";
constexpr const char *node_socket_data_rotation = "NodeSocketRotation";
constexpr const char *node_socket_data_vector = "NodeSocketVector";
constexpr const char *node_socket_data_color = "NodeSocketColor";
constexpr const char *node_socket_data_string = "NodeSocketString";
constexpr const char *node_socket_data_object = "NodeSocketObject";
constexpr const char *node_socket_data_image = "NodeSocketImage";
constexpr const char *node_socket_data_collection = "NodeSocketCollection";
constexpr const char *node_socket_data_texture = "NodeSocketTexture";
constexpr const char *node_socket_data_material = "NodeSocketMaterial";
template<typename Fn> void socket_data_to_static_type(const char *socket_type, const Fn &fn)
{
if (STREQ(socket_type, socket_types::node_socket_data_float)) {
fn.template operator()<bNodeSocketValueFloat>();
}
else if (STREQ(socket_type, socket_types::node_socket_data_int)) {
fn.template operator()<bNodeSocketValueInt>();
}
else if (STREQ(socket_type, socket_types::node_socket_data_bool)) {
fn.template operator()<bNodeSocketValueBoolean>();
}
else if (STREQ(socket_type, socket_types::node_socket_data_rotation)) {
fn.template operator()<bNodeSocketValueRotation>();
}
else if (STREQ(socket_type, socket_types::node_socket_data_vector)) {
fn.template operator()<bNodeSocketValueVector>();
}
else if (STREQ(socket_type, socket_types::node_socket_data_color)) {
fn.template operator()<bNodeSocketValueRGBA>();
}
else if (STREQ(socket_type, socket_types::node_socket_data_string)) {
fn.template operator()<bNodeSocketValueString>();
}
else if (STREQ(socket_type, socket_types::node_socket_data_object)) {
fn.template operator()<bNodeSocketValueObject>();
}
else if (STREQ(socket_type, socket_types::node_socket_data_image)) {
fn.template operator()<bNodeSocketValueImage>();
}
else if (STREQ(socket_type, socket_types::node_socket_data_collection)) {
fn.template operator()<bNodeSocketValueCollection>();
}
else if (STREQ(socket_type, socket_types::node_socket_data_texture)) {
fn.template operator()<bNodeSocketValueTexture>();
}
else if (STREQ(socket_type, socket_types::node_socket_data_material)) {
fn.template operator()<bNodeSocketValueMaterial>();
}
}
namespace detail {
template<typename Fn> struct TypeTagExecutor {
const Fn &fn;
TypeTagExecutor(const Fn &fn_) : fn(fn_) {}
template<typename T> void operator()() const
{
fn(TypeTag<T>{});
}
};
} // namespace detail
template<typename Fn> void socket_data_to_static_type_tag(const char *socket_type, const Fn &fn)
{
detail::TypeTagExecutor executor{fn};
socket_data_to_static_type(socket_type, executor);
}
} // namespace socket_types
template<typename T> bool socket_data_is_type(const char *socket_type)
{
bool match = false;
socket_types::socket_data_to_static_type_tag(socket_type, [&match](auto type_tag) {
using SocketDataType = typename decltype(type_tag)::type;
match |= std::is_same_v<T, SocketDataType>;
});
return match;
}
template<typename T> T &get_socket_data_as(bNodeTreeInterfaceSocket &item)
{
BLI_assert(socket_data_is_type<T>(item.socket_type));
return *static_cast<T *>(item.socket_data);
}
template<typename T> const T &get_socket_data_as(const bNodeTreeInterfaceSocket &item)
{
BLI_assert(socket_data_is_type<T>(item.socket_type));
return *static_cast<const T *>(item.socket_data);
}
inline bNodeTreeInterfaceSocket *add_interface_socket_from_node(bNodeTree &ntree,
const bNode &from_node,
const bNodeSocket &from_sock,
const StringRefNull socket_type,
const StringRefNull name)
{
eNodeTreeInterfaceSocketFlag flag = eNodeTreeInterfaceSocketFlag(0);
SET_FLAG_FROM_TEST(flag, from_sock.in_out & SOCK_IN, NODE_INTERFACE_SOCKET_INPUT);
SET_FLAG_FROM_TEST(flag, from_sock.in_out & SOCK_OUT, NODE_INTERFACE_SOCKET_OUTPUT);
bNodeTreeInterfaceSocket *iosock = ntree.tree_interface.add_socket(
name.data(), from_sock.description, socket_type, flag, nullptr);
if (iosock == nullptr) {
return nullptr;
}
const bNodeSocketType *typeinfo = iosock->socket_typeinfo();
if (typeinfo->interface_from_socket) {
/* XXX Enable when bNodeSocketType callbacks have been updated. */
// typeinfo->interface_from_socket(ntree.id, iosock, &from_node, &from_sock);
}
return iosock;
}
inline bNodeTreeInterfaceSocket *add_interface_socket_from_node(bNodeTree &ntree,
const bNode &from_node,
const bNodeSocket &from_sock,
const StringRefNull socket_type)
{
return add_interface_socket_from_node(ntree, from_node, from_sock, socket_type, from_sock.name);
}
inline bNodeTreeInterfaceSocket *add_interface_socket_from_node(bNodeTree &ntree,
const bNode &from_node,
const bNodeSocket &from_sock)
{
return add_interface_socket_from_node(
ntree, from_node, from_sock, from_sock.typeinfo->idname, from_sock.name);
}
} // namespace node_interface
} // namespace blender::bke

View File

@@ -235,6 +235,7 @@ set(SRC
intern/node_tree_anonymous_attributes.cc
intern/node_tree_dot_export.cc
intern/node_tree_field_inferencing.cc
intern/node_tree_interface.cc
intern/node_tree_update.cc
intern/node_tree_zones.cc
intern/object.cc
@@ -454,6 +455,7 @@ set(SRC
BKE_node_runtime.hh
BKE_node_tree_anonymous_attributes.hh
BKE_node_tree_dot_export.hh
BKE_node_tree_interface.hh
BKE_node_tree_update.h
BKE_node_tree_zones.hh
BKE_object.h

View File

@@ -0,0 +1,1333 @@
/* SPDX-FileCopyrightText: 2023 Blender Foundation
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BKE_idprop.h"
#include "BKE_lib_id.h"
#include "BKE_lib_query.h"
#include "BKE_node.hh"
#include "BKE_node_tree_interface.hh"
#include "BLI_math.h"
#include "BLI_set.hh"
#include "BLI_stack.hh"
#include "BLI_string.h"
#include "BLI_vector.hh"
#include "BLO_read_write.h"
#include "DNA_collection_types.h"
#include "DNA_material_types.h"
#include "DNA_node_tree_interface_types.h"
#include "DNA_node_types.h"
#include "RNA_access.h"
#include "RNA_prototypes.h"
namespace blender::bke::node_interface {
namespace socket_types {
/* Try to get a supported socket type from some final type.
* Built-in socket can have multiple registered RNA types for the base type, e.g.
* `NodeSocketFloatUnsigned`, `NodeSocketFloatFactor`. Only the "base type" (`NodeSocketFloat`)
* is considered valid for interface sockets.
*/
static const char *try_get_supported_socket_type(StringRefNull socket_type)
{
const bNodeSocketType *typeinfo = nodeSocketTypeFind(socket_type.c_str());
if (typeinfo == nullptr) {
return nullptr;
}
/* For builtin socket types only the base type is supported. */
if (nodeIsStaticSocketType(typeinfo)) {
return nodeStaticSocketType(typeinfo->type, PROP_NONE);
}
return typeinfo->idname;
}
/* -------------------------------------------------------------------- */
/** \name ID User Increment in Socket Data
* \{ */
template<typename T> void socket_data_id_user_increment(T & /*data*/) {}
template<> void socket_data_id_user_increment(bNodeSocketValueObject &data)
{
id_us_plus(reinterpret_cast<ID *>(data.value));
}
template<> void socket_data_id_user_increment(bNodeSocketValueImage &data)
{
id_us_plus(reinterpret_cast<ID *>(data.value));
}
template<> void socket_data_id_user_increment(bNodeSocketValueCollection &data)
{
id_us_plus(reinterpret_cast<ID *>(data.value));
}
template<> void socket_data_id_user_increment(bNodeSocketValueTexture &data)
{
id_us_plus(reinterpret_cast<ID *>(data.value));
}
template<> void socket_data_id_user_increment(bNodeSocketValueMaterial &data)
{
id_us_plus(reinterpret_cast<ID *>(data.value));
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name ID User Decrement in Socket Data
* \{ */
template<typename T> void socket_data_id_user_decrement(T & /*data*/) {}
template<> void socket_data_id_user_decrement(bNodeSocketValueObject &data)
{
id_us_min(reinterpret_cast<ID *>(data.value));
}
template<> void socket_data_id_user_decrement(bNodeSocketValueImage &data)
{
id_us_min(reinterpret_cast<ID *>(data.value));
}
template<> void socket_data_id_user_decrement(bNodeSocketValueCollection &data)
{
id_us_min(reinterpret_cast<ID *>(data.value));
}
template<> void socket_data_id_user_decrement(bNodeSocketValueTexture &data)
{
id_us_min(reinterpret_cast<ID *>(data.value));
}
template<> void socket_data_id_user_decrement(bNodeSocketValueMaterial &data)
{
id_us_min(reinterpret_cast<ID *>(data.value));
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Initialize Socket Data
* \{ */
template<typename T> void socket_data_init_impl(T & /*data*/) {}
template<> void socket_data_init_impl(bNodeSocketValueFloat &data)
{
data.subtype = PROP_NONE;
data.value = 0.0f;
data.min = -FLT_MAX;
data.max = FLT_MAX;
}
template<> void socket_data_init_impl(bNodeSocketValueInt &data)
{
data.subtype = PROP_NONE;
data.value = 0;
data.min = INT_MIN;
data.max = INT_MAX;
}
template<> void socket_data_init_impl(bNodeSocketValueBoolean &data)
{
data.value = false;
}
template<> void socket_data_init_impl(bNodeSocketValueRotation & /*data*/) {}
template<> void socket_data_init_impl(bNodeSocketValueVector &data)
{
static float default_value[] = {0.0f, 0.0f, 0.0f};
data.subtype = PROP_NONE;
copy_v3_v3(data.value, default_value);
data.min = -FLT_MAX;
data.max = FLT_MAX;
}
template<> void socket_data_init_impl(bNodeSocketValueRGBA &data)
{
static float default_value[] = {0.0f, 0.0f, 0.0f, 1.0f};
copy_v4_v4(data.value, default_value);
}
template<> void socket_data_init_impl(bNodeSocketValueString &data)
{
data.subtype = PROP_NONE;
data.value[0] = '\0';
}
template<> void socket_data_init_impl(bNodeSocketValueObject &data)
{
data.value = nullptr;
}
template<> void socket_data_init_impl(bNodeSocketValueImage &data)
{
data.value = nullptr;
}
template<> void socket_data_init_impl(bNodeSocketValueCollection &data)
{
data.value = nullptr;
}
template<> void socket_data_init_impl(bNodeSocketValueTexture &data)
{
data.value = nullptr;
}
template<> void socket_data_init_impl(bNodeSocketValueMaterial &data)
{
data.value = nullptr;
}
static void *make_socket_data(const char *socket_type)
{
void *socket_data = nullptr;
socket_data_to_static_type_tag(socket_type, [&socket_data](auto type_tag) {
using SocketDataType = typename decltype(type_tag)::type;
SocketDataType *new_socket_data = MEM_cnew<SocketDataType>(__func__);
socket_data_init_impl(*new_socket_data);
socket_data = new_socket_data;
});
return socket_data;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Free Allocated Socket Data
* \{ */
template<typename T> void socket_data_free_impl(T & /*data*/, const bool /*do_id_user*/) {}
static void socket_data_free(bNodeTreeInterfaceSocket &socket, const bool do_id_user)
{
socket_data_to_static_type_tag(socket.socket_type, [&](auto type_tag) {
using SocketDataType = typename decltype(type_tag)::type;
if (do_id_user) {
socket_data_id_user_decrement(get_socket_data_as<SocketDataType>(socket));
}
socket_data_free_impl(get_socket_data_as<SocketDataType>(socket), do_id_user);
});
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Copy Allocated Socket Data
* \{ */
template<typename T> void socket_data_copy_impl(T & /*dst*/, const T & /*src*/) {}
static void socket_data_copy(bNodeTreeInterfaceSocket &dst,
const bNodeTreeInterfaceSocket &src,
int flag)
{
socket_data_to_static_type_tag(dst.socket_type, [&](auto type_tag) {
using SocketDataType = typename decltype(type_tag)::type;
dst.socket_data = MEM_dupallocN(src.socket_data);
socket_data_copy_impl(get_socket_data_as<SocketDataType>(dst),
get_socket_data_as<SocketDataType>(src));
if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) {
socket_data_id_user_increment(get_socket_data_as<SocketDataType>(dst));
}
});
}
/* Copy socket data from a raw pointer, e.g. from a #bNodeSocket. */
static void socket_data_copy_ptr(bNodeTreeInterfaceSocket &dst,
const void *src_socket_data,
int flag)
{
socket_data_to_static_type_tag(dst.socket_type, [&](auto type_tag) {
using SocketDataType = typename decltype(type_tag)::type;
if (dst.socket_data != nullptr) {
socket_data_free(dst, true);
MEM_SAFE_FREE(dst.socket_data);
}
dst.socket_data = MEM_dupallocN(src_socket_data);
socket_data_copy_impl(get_socket_data_as<SocketDataType>(dst),
*static_cast<const SocketDataType *>(src_socket_data));
if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) {
socket_data_id_user_increment(get_socket_data_as<SocketDataType>(dst));
}
});
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Write Socket Data to Blend File
* \{ */
/* Note: no default implementation, every used type must write at least the base struct. */
inline void socket_data_write_impl(BlendWriter *writer, bNodeSocketValueFloat &data)
{
BLO_write_struct(writer, bNodeSocketValueFloat, &data);
}
inline void socket_data_write_impl(BlendWriter *writer, bNodeSocketValueInt &data)
{
BLO_write_struct(writer, bNodeSocketValueInt, &data);
}
inline void socket_data_write_impl(BlendWriter *writer, bNodeSocketValueBoolean &data)
{
BLO_write_struct(writer, bNodeSocketValueBoolean, &data);
}
inline void socket_data_write_impl(BlendWriter *writer, bNodeSocketValueRotation &data)
{
BLO_write_struct(writer, bNodeSocketValueRotation, &data);
}
inline void socket_data_write_impl(BlendWriter *writer, bNodeSocketValueVector &data)
{
BLO_write_struct(writer, bNodeSocketValueVector, &data);
}
inline void socket_data_write_impl(BlendWriter *writer, bNodeSocketValueRGBA &data)
{
BLO_write_struct(writer, bNodeSocketValueRGBA, &data);
}
inline void socket_data_write_impl(BlendWriter *writer, bNodeSocketValueString &data)
{
BLO_write_struct(writer, bNodeSocketValueString, &data);
}
inline void socket_data_write_impl(BlendWriter *writer, bNodeSocketValueObject &data)
{
BLO_write_struct(writer, bNodeSocketValueObject, &data);
}
inline void socket_data_write_impl(BlendWriter *writer, bNodeSocketValueImage &data)
{
BLO_write_struct(writer, bNodeSocketValueImage, &data);
}
inline void socket_data_write_impl(BlendWriter *writer, bNodeSocketValueCollection &data)
{
BLO_write_struct(writer, bNodeSocketValueCollection, &data);
}
inline void socket_data_write_impl(BlendWriter *writer, bNodeSocketValueTexture &data)
{
BLO_write_struct(writer, bNodeSocketValueTexture, &data);
}
inline void socket_data_write_impl(BlendWriter *writer, bNodeSocketValueMaterial &data)
{
BLO_write_struct(writer, bNodeSocketValueMaterial, &data);
}
static void socket_data_write(BlendWriter *writer, bNodeTreeInterfaceSocket &socket)
{
socket_data_to_static_type_tag(socket.socket_type, [&](auto type_tag) {
using SocketDataType = typename decltype(type_tag)::type;
socket_data_write_impl(writer, get_socket_data_as<SocketDataType>(socket));
});
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Read Socket Data from Blend File
* \{ */
template<typename T> void socket_data_read_data_impl(BlendDataReader *reader, T **data)
{
BLO_read_data_address(reader, data);
}
static void socket_data_read_data(BlendDataReader *reader, bNodeTreeInterfaceSocket &socket)
{
socket_data_to_static_type_tag(socket.socket_type, [&](auto type_tag) {
using SocketDataType = typename decltype(type_tag)::type;
socket_data_read_data_impl(reader, reinterpret_cast<SocketDataType **>(&socket.socket_data));
});
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Read ID Pointer Data
* \{ */
template<typename T>
void socket_data_read_lib_impl(BlendLibReader * /*reader*/, ID * /*id*/, T & /*data*/)
{
}
template<>
void socket_data_read_lib_impl(BlendLibReader *reader, ID *id, bNodeSocketValueObject &data)
{
BLI_assert(id != nullptr);
BLO_read_id_address(reader, id, &data.value);
}
template<>
void socket_data_read_lib_impl(BlendLibReader *reader, ID *id, bNodeSocketValueImage &data)
{
BLI_assert(id != nullptr);
BLO_read_id_address(reader, id, &data.value);
}
template<>
void socket_data_read_lib_impl(BlendLibReader *reader, ID *id, bNodeSocketValueCollection &data)
{
BLI_assert(id != nullptr);
BLO_read_id_address(reader, id, &data.value);
}
template<>
void socket_data_read_lib_impl(BlendLibReader *reader, ID *id, bNodeSocketValueTexture &data)
{
BLI_assert(id != nullptr);
BLO_read_id_address(reader, id, &data.value);
}
template<>
void socket_data_read_lib_impl(BlendLibReader *reader, ID *id, bNodeSocketValueMaterial &data)
{
BLI_assert(id != nullptr);
BLO_read_id_address(reader, id, &data.value);
}
static void socket_data_read_lib(BlendLibReader *reader, ID *id, bNodeTreeInterfaceSocket &socket)
{
socket_data_to_static_type_tag(socket.socket_type, [&](auto type_tag) {
using SocketDataType = typename decltype(type_tag)::type;
socket_data_read_lib_impl(reader, id, get_socket_data_as<SocketDataType>(socket));
});
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Expand Socket Data
* \{ */
template<typename T> void socket_data_expand_impl(BlendExpander * /*expander*/, T & /*data*/) {}
template<> void socket_data_expand_impl(BlendExpander *expander, bNodeSocketValueObject &data)
{
BLO_expand(expander, &data.value);
}
template<> void socket_data_expand_impl(BlendExpander *expander, bNodeSocketValueImage &data)
{
BLO_expand(expander, &data.value);
}
template<> void socket_data_expand_impl(BlendExpander *expander, bNodeSocketValueCollection &data)
{
BLO_expand(expander, &data.value);
}
template<> void socket_data_expand_impl(BlendExpander *expander, bNodeSocketValueTexture &data)
{
BLO_expand(expander, &data.value);
}
template<> void socket_data_expand_impl(BlendExpander *expander, bNodeSocketValueMaterial &data)
{
BLO_expand(expander, &data.value);
}
static void socket_data_expand(BlendExpander *expander, bNodeTreeInterfaceSocket &socket)
{
socket_data_to_static_type_tag(socket.socket_type, [&](auto type_tag) {
using SocketDataType = typename decltype(type_tag)::type;
socket_data_expand_impl(expander, get_socket_data_as<SocketDataType>(socket));
});
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Callback per ID Pointer
* \{ */
template<typename T>
void socket_data_foreach_id_impl(LibraryForeachIDData * /*data*/, T & /*data*/)
{
}
template<> void socket_data_foreach_id_impl(LibraryForeachIDData *cb, bNodeSocketValueObject &data)
{
BKE_LIB_FOREACHID_PROCESS_IDSUPER(cb, data.value, IDWALK_CB_USER);
}
template<> void socket_data_foreach_id_impl(LibraryForeachIDData *cb, bNodeSocketValueImage &data)
{
BKE_LIB_FOREACHID_PROCESS_IDSUPER(cb, data.value, IDWALK_CB_USER);
}
template<>
void socket_data_foreach_id_impl(LibraryForeachIDData *cb, bNodeSocketValueCollection &data)
{
BKE_LIB_FOREACHID_PROCESS_IDSUPER(cb, data.value, IDWALK_CB_USER);
}
template<>
void socket_data_foreach_id_impl(LibraryForeachIDData *cb, bNodeSocketValueTexture &data)
{
BKE_LIB_FOREACHID_PROCESS_IDSUPER(cb, data.value, IDWALK_CB_USER);
}
template<>
void socket_data_foreach_id_impl(LibraryForeachIDData *cb, bNodeSocketValueMaterial &data)
{
BKE_LIB_FOREACHID_PROCESS_IDSUPER(cb, data.value, IDWALK_CB_USER);
}
static void socket_data_foreach_id(LibraryForeachIDData *data, bNodeTreeInterfaceSocket &socket)
{
socket_data_to_static_type_tag(socket.socket_type, [&](auto type_tag) {
using SocketDataType = typename decltype(type_tag)::type;
socket_data_foreach_id_impl(data, get_socket_data_as<SocketDataType>(socket));
});
}
/** \} */
} // namespace socket_types
namespace item_types {
static void item_copy(bNodeTreeInterfaceItem &dst,
const bNodeTreeInterfaceItem &src,
const int flag)
{
switch (dst.item_type) {
case NODE_INTERFACE_SOCKET: {
bNodeTreeInterfaceSocket &dst_socket = reinterpret_cast<bNodeTreeInterfaceSocket &>(dst);
const bNodeTreeInterfaceSocket &src_socket =
reinterpret_cast<const bNodeTreeInterfaceSocket &>(src);
BLI_assert(src_socket.name != nullptr);
BLI_assert(src_socket.socket_type != nullptr);
dst_socket.name = BLI_strdup(src_socket.name);
dst_socket.description = BLI_strdup_null(src_socket.description);
dst_socket.socket_type = BLI_strdup(src_socket.socket_type);
dst_socket.default_attribute_name = BLI_strdup_null(src_socket.default_attribute_name);
dst_socket.identifier = BLI_strdup(src_socket.identifier);
if (src_socket.properties) {
dst_socket.properties = IDP_CopyProperty_ex(src_socket.properties, flag);
}
if (src_socket.socket_data != nullptr) {
socket_types::socket_data_copy(dst_socket, src_socket, flag);
}
break;
}
case NODE_INTERFACE_PANEL: {
bNodeTreeInterfacePanel &dst_panel = reinterpret_cast<bNodeTreeInterfacePanel &>(dst);
const bNodeTreeInterfacePanel &src_panel = reinterpret_cast<const bNodeTreeInterfacePanel &>(
src);
BLI_assert(src_panel.name != nullptr);
dst_panel.name = BLI_strdup(src_panel.name);
dst_panel.copy_from(src_panel.items(), flag);
break;
}
}
}
static void item_free(bNodeTreeInterfaceItem &item, const bool do_id_user)
{
switch (item.item_type) {
case NODE_INTERFACE_SOCKET: {
bNodeTreeInterfaceSocket &socket = reinterpret_cast<bNodeTreeInterfaceSocket &>(item);
if (socket.socket_data != nullptr) {
socket_types::socket_data_free(socket, do_id_user);
MEM_SAFE_FREE(socket.socket_data);
}
MEM_SAFE_FREE(socket.name);
MEM_SAFE_FREE(socket.description);
MEM_SAFE_FREE(socket.socket_type);
MEM_SAFE_FREE(socket.default_attribute_name);
MEM_SAFE_FREE(socket.identifier);
if (socket.properties) {
IDP_FreePropertyContent_ex(socket.properties, do_id_user);
MEM_freeN(socket.properties);
}
break;
}
case NODE_INTERFACE_PANEL: {
bNodeTreeInterfacePanel &panel = reinterpret_cast<bNodeTreeInterfacePanel &>(item);
panel.clear(do_id_user);
MEM_SAFE_FREE(panel.name);
break;
}
}
MEM_freeN(&item);
}
void item_write_struct(BlendWriter *writer, bNodeTreeInterfaceItem &item);
static void item_write_data(BlendWriter *writer, bNodeTreeInterfaceItem &item)
{
switch (item.item_type) {
case NODE_INTERFACE_SOCKET: {
bNodeTreeInterfaceSocket &socket = reinterpret_cast<bNodeTreeInterfaceSocket &>(item);
BLO_write_string(writer, socket.name);
BLO_write_string(writer, socket.description);
BLO_write_string(writer, socket.socket_type);
BLO_write_string(writer, socket.default_attribute_name);
if (socket.properties) {
IDP_BlendWrite(writer, socket.properties);
}
socket_types::socket_data_write(writer, socket);
break;
}
case NODE_INTERFACE_PANEL: {
bNodeTreeInterfacePanel &panel = reinterpret_cast<bNodeTreeInterfacePanel &>(item);
BLO_write_string(writer, panel.name);
BLO_write_pointer_array(writer, panel.items_num, panel.items_array);
for (bNodeTreeInterfaceItem *child_item : panel.items()) {
item_write_struct(writer, *child_item);
}
break;
}
}
}
void item_write_struct(BlendWriter *writer, bNodeTreeInterfaceItem &item)
{
switch (item.item_type) {
case NODE_INTERFACE_SOCKET: {
BLO_write_struct(writer, bNodeTreeInterfaceSocket, &item);
break;
}
case NODE_INTERFACE_PANEL: {
BLO_write_struct(writer, bNodeTreeInterfacePanel, &item);
break;
}
}
item_write_data(writer, item);
}
static void item_read_data(BlendDataReader *reader, bNodeTreeInterfaceItem &item)
{
switch (item.item_type) {
case NODE_INTERFACE_SOCKET: {
bNodeTreeInterfaceSocket &socket = reinterpret_cast<bNodeTreeInterfaceSocket &>(item);
BLO_read_data_address(reader, &socket.name);
BLO_read_data_address(reader, &socket.description);
BLO_read_data_address(reader, &socket.socket_type);
BLO_read_data_address(reader, &socket.default_attribute_name);
BLO_read_data_address(reader, &socket.identifier);
BLO_read_data_address(reader, &socket.properties);
IDP_BlendDataRead(reader, &socket.properties);
socket_types::socket_data_read_data(reader, socket);
break;
}
case NODE_INTERFACE_PANEL: {
bNodeTreeInterfacePanel &panel = reinterpret_cast<bNodeTreeInterfacePanel &>(item);
BLO_read_data_address(reader, &panel.name);
BLO_read_pointer_array(reader, reinterpret_cast<void **>(&panel.items_array));
for (const int i : blender::IndexRange(panel.items_num)) {
BLO_read_data_address(reader, &panel.items_array[i]);
item_read_data(reader, *panel.items_array[i]);
}
break;
}
}
}
static void item_read_lib(BlendLibReader *reader, ID *id, bNodeTreeInterfaceItem &item)
{
switch (item.item_type) {
case NODE_INTERFACE_SOCKET: {
bNodeTreeInterfaceSocket &socket = reinterpret_cast<bNodeTreeInterfaceSocket &>(item);
IDP_BlendReadLib(reader, id, socket.properties);
socket_types::socket_data_read_lib(reader, id, socket);
break;
}
case NODE_INTERFACE_PANEL: {
bNodeTreeInterfacePanel &panel = reinterpret_cast<bNodeTreeInterfacePanel &>(item);
for (bNodeTreeInterfaceItem *item : panel.items()) {
item_read_lib(reader, id, *item);
}
break;
}
}
}
static void item_read_expand(BlendExpander *expander, bNodeTreeInterfaceItem &item)
{
switch (item.item_type) {
case NODE_INTERFACE_SOCKET: {
bNodeTreeInterfaceSocket &socket = reinterpret_cast<bNodeTreeInterfaceSocket &>(item);
IDP_BlendReadExpand(expander, socket.properties);
socket_types::socket_data_expand(expander, socket);
break;
}
case NODE_INTERFACE_PANEL: {
bNodeTreeInterfacePanel &panel = reinterpret_cast<bNodeTreeInterfacePanel &>(item);
for (bNodeTreeInterfaceItem *item : panel.items()) {
item_read_expand(expander, *item);
}
break;
}
}
}
static void item_foreach_id(LibraryForeachIDData *data, bNodeTreeInterfaceItem &item)
{
switch (item.item_type) {
case NODE_INTERFACE_SOCKET: {
bNodeTreeInterfaceSocket &socket = reinterpret_cast<bNodeTreeInterfaceSocket &>(item);
BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(
data,
IDP_foreach_property(socket.properties,
IDP_TYPE_FILTER_ID,
BKE_lib_query_idpropertiesForeachIDLink_callback,
data));
socket_types::socket_data_foreach_id(data, socket);
break;
}
case NODE_INTERFACE_PANEL: {
bNodeTreeInterfacePanel &panel = reinterpret_cast<bNodeTreeInterfacePanel &>(item);
for (bNodeTreeInterfaceItem *item : panel.items()) {
item_foreach_id(data, *item);
}
break;
}
}
}
/* Move all child items to the new parent. */
static Span<bNodeTreeInterfaceItem *> item_children(bNodeTreeInterfaceItem &item)
{
switch (item.item_type) {
case NODE_INTERFACE_SOCKET: {
return {};
}
case NODE_INTERFACE_PANEL: {
bNodeTreeInterfacePanel &panel = reinterpret_cast<bNodeTreeInterfacePanel &>(item);
return panel.items();
}
}
return {};
}
} // namespace item_types
} // namespace blender::bke::node_interface
using namespace blender::bke::node_interface;
bNodeSocketType *bNodeTreeInterfaceSocket::socket_typeinfo() const
{
return nodeSocketTypeFind(socket_type);
}
blender::ColorGeometry4f bNodeTreeInterfaceSocket::socket_color() const
{
bNodeSocketType *typeinfo = this->socket_typeinfo();
if (!typeinfo || !typeinfo->draw_color) {
return blender::ColorGeometry4f(1.0f, 0.0f, 1.0f, 1.0f);
}
float color[4];
typeinfo->draw_color(nullptr, nullptr, nullptr, color);
return blender::ColorGeometry4f(color);
}
bool bNodeTreeInterfaceSocket::set_socket_type(const char *new_socket_type)
{
const char *idname = socket_types::try_get_supported_socket_type(new_socket_type);
if (idname == nullptr) {
return false;
}
if (this->socket_data != nullptr) {
socket_types::socket_data_free(*this, true);
MEM_SAFE_FREE(this->socket_data);
}
MEM_SAFE_FREE(this->socket_type);
this->socket_type = BLI_strdup(new_socket_type);
this->socket_data = socket_types::make_socket_data(new_socket_type);
return true;
}
void bNodeTreeInterfaceSocket::init_from_socket_instance(const bNodeSocket *socket)
{
const char *idname = socket_types::try_get_supported_socket_type(socket->idname);
BLI_assert(idname != nullptr);
if (this->socket_data != nullptr) {
socket_types::socket_data_free(*this, true);
MEM_SAFE_FREE(this->socket_data);
}
MEM_SAFE_FREE(this->socket_type);
this->socket_type = BLI_strdup(idname);
this->socket_data = socket_types::make_socket_data(idname);
socket_types::socket_data_copy_ptr(*this, socket->default_value, 0);
}
blender::IndexRange bNodeTreeInterfacePanel::items_range() const
{
return blender::IndexRange(items_num);
}
blender::Span<const bNodeTreeInterfaceItem *> bNodeTreeInterfacePanel::items() const
{
return blender::Span(items_array, items_num);
}
blender::MutableSpan<bNodeTreeInterfaceItem *> bNodeTreeInterfacePanel::items()
{
return blender::MutableSpan(items_array, items_num);
}
bool bNodeTreeInterfacePanel::contains(const bNodeTreeInterfaceItem &item) const
{
return items().contains(&item);
}
bool bNodeTreeInterfacePanel::contains_recursive(const bNodeTreeInterfaceItem &item) const
{
bool is_child = false;
/* Have to capture item address here instead of just a reference,
* otherwise pointer comparison will not work. */
this->foreach_item(
[&](const bNodeTreeInterfaceItem &titem) {
if (&titem == &item) {
is_child = true;
return false;
}
return true;
},
true);
return is_child;
}
int bNodeTreeInterfacePanel::item_position(const bNodeTreeInterfaceItem &item) const
{
return items().first_index_try(&item);
}
int bNodeTreeInterfacePanel::item_index(const bNodeTreeInterfaceItem &item) const
{
int index = 0;
bool found = false;
/* Have to capture item address here instead of just a reference,
* otherwise pointer comparison will not work. */
this->foreach_item([&](const bNodeTreeInterfaceItem &titem) {
if (&titem == &item) {
found = true;
return false;
}
++index;
return true;
});
return found ? index : -1;
}
const bNodeTreeInterfaceItem *bNodeTreeInterfacePanel::item_at_index(int index) const
{
int i = 0;
const bNodeTreeInterfaceItem *result = nullptr;
this->foreach_item([&](const bNodeTreeInterfaceItem &item) {
if (i == index) {
result = &item;
return false;
}
++i;
return true;
});
return result;
}
bNodeTreeInterfacePanel *bNodeTreeInterfacePanel::find_parent_recursive(
const bNodeTreeInterfaceItem &item)
{
std::queue<bNodeTreeInterfacePanel *> queue;
if (this->contains(item)) {
return this;
}
queue.push(this);
while (!queue.empty()) {
bNodeTreeInterfacePanel *parent = queue.front();
queue.pop();
for (bNodeTreeInterfaceItem *titem : parent->items()) {
if (titem->item_type != NODE_INTERFACE_PANEL) {
continue;
}
bNodeTreeInterfacePanel *tpanel = get_item_as<bNodeTreeInterfacePanel>(titem);
if (tpanel->contains(item)) {
return tpanel;
}
queue.push(tpanel);
}
}
return nullptr;
}
void bNodeTreeInterfacePanel::add_item(bNodeTreeInterfaceItem &item)
{
blender::MutableSpan<bNodeTreeInterfaceItem *> old_items = this->items();
items_num++;
items_array = MEM_cnew_array<bNodeTreeInterfaceItem *>(items_num, __func__);
this->items().drop_back(1).copy_from(old_items);
this->items().last() = &item;
if (old_items.data()) {
MEM_freeN(old_items.data());
}
}
void bNodeTreeInterfacePanel::insert_item(bNodeTreeInterfaceItem &item, int position)
{
position = std::min(std::max(position, 0), items_num);
blender::MutableSpan<bNodeTreeInterfaceItem *> old_items = this->items();
items_num++;
items_array = MEM_cnew_array<bNodeTreeInterfaceItem *>(items_num, __func__);
this->items().take_front(position).copy_from(old_items.take_front(position));
this->items().drop_front(position + 1).copy_from(old_items.drop_front(position));
this->items()[position] = &item;
if (old_items.data()) {
MEM_freeN(old_items.data());
}
}
bool bNodeTreeInterfacePanel::remove_item(bNodeTreeInterfaceItem &item, bool free)
{
const int position = this->item_position(item);
if (!this->items().index_range().contains(position)) {
return false;
}
blender::MutableSpan<bNodeTreeInterfaceItem *> old_items = this->items();
items_num--;
items_array = MEM_cnew_array<bNodeTreeInterfaceItem *>(items_num, __func__);
this->items().take_front(position).copy_from(old_items.take_front(position));
this->items().drop_front(position).copy_from(old_items.drop_front(position + 1));
/* Guaranteed not empty, contains at least the removed item */
MEM_freeN(old_items.data());
if (free) {
item_types::item_free(item, true);
}
return true;
}
void bNodeTreeInterfacePanel::clear(bool do_id_user)
{
for (bNodeTreeInterfaceItem *item : this->items()) {
item_types::item_free(*item, do_id_user);
}
MEM_SAFE_FREE(items_array);
items_array = nullptr;
items_num = 0;
}
bool bNodeTreeInterfacePanel::move_item(bNodeTreeInterfaceItem &item, const int new_position)
{
const int old_position = this->item_position(item);
if (!this->items().index_range().contains(old_position) ||
!this->items().index_range().contains(new_position))
{
return false;
}
if (old_position == new_position) {
/* Nothing changes. */
return true;
}
else if (old_position < new_position) {
const blender::Span<bNodeTreeInterfaceItem *> moved_items = this->items().slice(
old_position + 1, new_position - old_position);
bNodeTreeInterfaceItem *tmp = this->items()[old_position];
std::copy(
moved_items.begin(), moved_items.end(), this->items().drop_front(old_position).data());
this->items()[new_position] = tmp;
}
else /* old_position > new_position */ {
const blender::Span<bNodeTreeInterfaceItem *> moved_items = this->items().slice(
new_position, old_position - new_position);
bNodeTreeInterfaceItem *tmp = this->items()[old_position];
std::copy_backward(
moved_items.begin(), moved_items.end(), this->items().drop_front(old_position + 1).data());
this->items()[new_position] = tmp;
}
return true;
}
void bNodeTreeInterfacePanel::foreach_item(
blender::FunctionRef<bool(bNodeTreeInterfaceItem &item)> fn, bool include_self)
{
using ItemSpan = blender::Span<bNodeTreeInterfaceItem *>;
blender::Stack<ItemSpan> stack;
if (include_self && fn(this->item) == false) {
return;
}
stack.push(this->items());
while (!stack.is_empty()) {
const ItemSpan current_items = stack.pop();
for (const int index : current_items.index_range()) {
bNodeTreeInterfaceItem *item = current_items[index];
if (fn(*item) == false) {
return;
}
if (item->item_type == NODE_INTERFACE_PANEL) {
bNodeTreeInterfacePanel *panel = reinterpret_cast<bNodeTreeInterfacePanel *>(item);
/* Reinsert remaining items. */
if (index < current_items.size() - 1) {
const ItemSpan remaining_items = current_items.drop_front(index + 1);
stack.push(remaining_items);
}
/* Handle child items first before continuing with current span. */
stack.push(panel->items());
break;
}
}
}
}
void bNodeTreeInterfacePanel::foreach_item(
blender::FunctionRef<bool(const bNodeTreeInterfaceItem &item)> fn, bool include_self) const
{
using ItemSpan = blender::Span<const bNodeTreeInterfaceItem *>;
blender::Stack<ItemSpan> stack;
if (include_self && fn(this->item) == false) {
return;
}
stack.push(this->items());
while (!stack.is_empty()) {
const ItemSpan current_items = stack.pop();
for (const int index : current_items.index_range()) {
const bNodeTreeInterfaceItem *item = current_items[index];
if (fn(*item) == false) {
return;
}
if (item->item_type == NODE_INTERFACE_PANEL) {
const bNodeTreeInterfacePanel *panel = reinterpret_cast<const bNodeTreeInterfacePanel *>(
item);
/* Reinsert remaining items. */
if (index < current_items.size() - 1) {
const ItemSpan remaining_items = current_items.drop_front(index + 1);
stack.push(remaining_items);
}
/* Handle child items first before continuing with current span. */
stack.push(panel->items());
break;
}
}
}
}
static bNodeTreeInterfaceSocket *make_socket(const int uid,
blender::StringRefNull name,
blender::StringRefNull description,
blender::StringRefNull socket_type,
const eNodeTreeInterfaceSocketFlag flag)
{
BLI_assert(name.c_str() != nullptr);
BLI_assert(socket_type.c_str() != nullptr);
const char *idname = socket_types::try_get_supported_socket_type(socket_type.c_str());
if (idname == nullptr) {
return nullptr;
}
bNodeTreeInterfaceSocket *new_socket = MEM_cnew<bNodeTreeInterfaceSocket>(__func__);
BLI_assert(new_socket);
/* Init common socket properties. */
new_socket->identifier = BLI_sprintfN("Socket_%d", uid);
new_socket->item.item_type = NODE_INTERFACE_SOCKET;
new_socket->name = BLI_strdup(name.c_str());
new_socket->description = BLI_strdup_null(description.c_str());
new_socket->socket_type = BLI_strdup(socket_type.c_str());
new_socket->flag = flag;
new_socket->socket_data = socket_types::make_socket_data(socket_type.c_str());
return new_socket;
}
static bNodeTreeInterfacePanel *make_panel(const int uid, blender::StringRefNull name)
{
BLI_assert(name.c_str() != nullptr);
bNodeTreeInterfacePanel *new_panel = MEM_cnew<bNodeTreeInterfacePanel>(__func__);
new_panel->item.item_type = NODE_INTERFACE_PANEL;
new_panel->name = BLI_strdup(name.c_str());
new_panel->identifier = uid;
return new_panel;
}
void bNodeTreeInterfacePanel::copy_from(
const blender::Span<const bNodeTreeInterfaceItem *> items_src, int flag)
{
items_num = items_src.size();
items_array = MEM_cnew_array<bNodeTreeInterfaceItem *>(items_num, __func__);
/* Copy buffers. */
for (const int i : items_src.index_range()) {
const bNodeTreeInterfaceItem *item_src = items_src[i];
items_array[i] = static_cast<bNodeTreeInterfaceItem *>(MEM_dupallocN(item_src));
item_types::item_copy(*items_array[i], *item_src, flag);
}
}
void bNodeTreeInterface::copy_data(const bNodeTreeInterface &src, int flag)
{
this->root_panel.copy_from(src.root_panel.items(), flag);
this->active_index = src.active_index;
}
void bNodeTreeInterface::free_data()
{
/* Called when freeing the main database, don't do user refcount here. */
this->root_panel.clear(false);
}
void bNodeTreeInterface::write(BlendWriter *writer)
{
BLO_write_struct(writer, bNodeTreeInterface, this);
/* Don't write the root panel struct itself, it's nested in the interface struct. */
item_types::item_write_data(writer, this->root_panel.item);
}
void bNodeTreeInterface::read_data(BlendDataReader *reader)
{
item_types::item_read_data(reader, this->root_panel.item);
}
void bNodeTreeInterface::read_lib(BlendLibReader *reader, ID *id)
{
item_types::item_read_lib(reader, id, this->root_panel.item);
}
void bNodeTreeInterface::read_expand(BlendExpander *expander)
{
item_types::item_read_expand(expander, this->root_panel.item);
}
bNodeTreeInterfaceItem *bNodeTreeInterface::active_item()
{
bNodeTreeInterfaceItem *active = nullptr;
int count = active_index;
this->foreach_item([&](bNodeTreeInterfaceItem &item) {
if (count == 0) {
active = &item;
return false;
}
--count;
return true;
});
return active;
}
const bNodeTreeInterfaceItem *bNodeTreeInterface::active_item() const
{
const bNodeTreeInterfaceItem *active = nullptr;
int count = active_index;
this->foreach_item([&](const bNodeTreeInterfaceItem &item) {
if (count == 0) {
active = &item;
return false;
}
--count;
return true;
});
return active;
}
void bNodeTreeInterface::active_item_set(bNodeTreeInterfaceItem *item)
{
active_index = 0;
int count = 0;
this->foreach_item([&](bNodeTreeInterfaceItem &titem) {
if (&titem == item) {
active_index = count;
return false;
}
++count;
return true;
});
}
bNodeTreeInterfaceSocket *bNodeTreeInterface::add_socket(blender::StringRefNull name,
blender::StringRefNull description,
blender::StringRefNull socket_type,
const eNodeTreeInterfaceSocketFlag flag,
bNodeTreeInterfacePanel *parent)
{
if (parent == nullptr) {
parent = &root_panel;
}
BLI_assert(this->find_item(parent->item));
bNodeTreeInterfaceSocket *new_socket = make_socket(
next_uid++, name, description, socket_type, flag);
if (new_socket) {
parent->add_item(new_socket->item);
}
return new_socket;
}
bNodeTreeInterfaceSocket *bNodeTreeInterface::insert_socket(
blender::StringRefNull name,
blender::StringRefNull description,
blender::StringRefNull socket_type,
const eNodeTreeInterfaceSocketFlag flag,
bNodeTreeInterfacePanel *parent,
const int position)
{
if (parent == nullptr) {
parent = &root_panel;
}
BLI_assert(this->find_item(parent->item));
bNodeTreeInterfaceSocket *new_socket = make_socket(
next_uid++, name, description, socket_type, flag);
if (new_socket) {
parent->insert_item(new_socket->item, position);
}
return new_socket;
}
bNodeTreeInterfacePanel *bNodeTreeInterface::add_panel(blender::StringRefNull name,
bNodeTreeInterfacePanel *parent)
{
if (parent == nullptr) {
parent = &root_panel;
}
BLI_assert(this->find_item(parent->item));
bNodeTreeInterfacePanel *new_panel = make_panel(next_uid++, name);
if (new_panel) {
parent->add_item(new_panel->item);
}
return new_panel;
}
bNodeTreeInterfacePanel *bNodeTreeInterface::insert_panel(blender::StringRefNull name,
bNodeTreeInterfacePanel *parent,
const int position)
{
if (parent == nullptr) {
parent = &root_panel;
}
BLI_assert(this->find_item(parent->item));
bNodeTreeInterfacePanel *new_panel = make_panel(next_uid++, name);
if (new_panel) {
parent->insert_item(new_panel->item, position);
}
return new_panel;
}
bNodeTreeInterfaceItem *bNodeTreeInterface::add_item_copy(const bNodeTreeInterfaceItem &item,
bNodeTreeInterfacePanel *parent)
{
if (parent == nullptr) {
parent = &root_panel;
}
BLI_assert(this->find_item(item));
BLI_assert(this->find_item(parent->item));
if (parent == nullptr) {
parent = &root_panel;
}
bNodeTreeInterfaceItem *citem = static_cast<bNodeTreeInterfaceItem *>(MEM_dupallocN(&item));
item_types::item_copy(*citem, item, 0);
parent->add_item(*citem);
return citem;
}
bNodeTreeInterfaceItem *bNodeTreeInterface::insert_item_copy(const bNodeTreeInterfaceItem &item,
bNodeTreeInterfacePanel *parent,
int position)
{
if (parent == nullptr) {
parent = &root_panel;
}
BLI_assert(this->find_item(item));
BLI_assert(this->find_item(parent->item));
bNodeTreeInterfaceItem *citem = static_cast<bNodeTreeInterfaceItem *>(MEM_dupallocN(&item));
item_types::item_copy(*citem, item, 0);
parent->insert_item(*citem, position);
return citem;
}
bool bNodeTreeInterface::remove_item(bNodeTreeInterfaceItem &item, bool move_content_to_parent)
{
bNodeTreeInterfacePanel *parent = this->find_item_parent(item);
if (parent == nullptr) {
return false;
}
if (move_content_to_parent) {
int position = parent->item_position(item);
/* Cache children to avoid invalidating the iterator. */
blender::Array<bNodeTreeInterfaceItem *> children(item_types::item_children(item));
for (bNodeTreeInterfaceItem *child : children) {
this->move_item_to_parent(*child, parent, position++);
}
}
if (parent->remove_item(item, true)) {
return true;
}
return false;
}
void bNodeTreeInterface::clear_items()
{
root_panel.clear(true);
}
bool bNodeTreeInterface::move_item(bNodeTreeInterfaceItem &item, const int new_position)
{
bNodeTreeInterfacePanel *parent = this->find_item_parent(item);
if (parent == nullptr) {
return false;
}
return parent->move_item(item, new_position);
}
bool bNodeTreeInterface::move_item_to_parent(bNodeTreeInterfaceItem &item,
bNodeTreeInterfacePanel *new_parent,
int new_position)
{
bNodeTreeInterfacePanel *parent = this->find_item_parent(item);
if (parent == nullptr) {
return false;
}
if (parent->remove_item(item, false)) {
new_parent->insert_item(item, new_position);
return true;
}
return false;
}
void bNodeTreeInterface::foreach_id(LibraryForeachIDData *cb)
{
item_types::item_foreach_id(cb, root_panel.item);
}
namespace blender::bke {
void bNodeTreeInterfaceCache::rebuild(bNodeTreeInterface &interface)
{
/* Rebuild draw-order list of interface items for linear access. */
items.clear();
inputs.clear();
outputs.clear();
interface.foreach_item([&](bNodeTreeInterfaceItem &item) {
items.append(&item);
if (bNodeTreeInterfaceSocket *socket = get_item_as<bNodeTreeInterfaceSocket>(&item)) {
if (socket->flag & NODE_INTERFACE_SOCKET_INPUT) {
inputs.append(socket);
}
if (socket->flag & NODE_INTERFACE_SOCKET_OUTPUT) {
outputs.append(socket);
}
}
return true;
});
}
} // namespace blender::bke

View File

@@ -53,6 +53,15 @@ char *BLI_strdupn(const char *str, size_t len) ATTR_MALLOC ATTR_WARN_UNUSED_RESU
*/
char *BLI_strdup(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC;
/**
* Duplicates the C-string \a str into a newly mallocN'd
* string and returns it.
*
* \param str: The string to be duplicated, can be null
* \retval Returns the duplicated string or null if \a str is null
*/
char *BLI_strdup_null(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_MALLOC;
/**
* Appends the two strings, and returns new mallocN'ed string
* \param str1: first string for copy

View File

@@ -42,6 +42,11 @@ char *BLI_strdup(const char *str)
return BLI_strdupn(str, strlen(str));
}
char *BLI_strdup_null(const char *str)
{
return (str != NULL) ? BLI_strdupn(str, strlen(str)) : NULL;
}
char *BLI_strdupcat(const char *__restrict str1, const char *__restrict str2)
{
/* include the NULL terminator of str2 only */

View File

@@ -90,7 +90,6 @@ static void add_group_input_node_fn(nodes::LinkSearchOpParams &params)
bNodeSocket *interface_socket = bke::ntreeAddSocketInterfaceFromSocket(
&params.node_tree, &params.node, &params.socket);
const int group_input_index = BLI_findindex(&params.node_tree.inputs, interface_socket);
bNode &group_input = params.add_node("NodeGroupInput");
/* This is necessary to create the new sockets in the other input nodes. */

View File

@@ -0,0 +1,365 @@
/* SPDX-FileCopyrightText: 2005 Blender Foundation
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup DNA
*/
#pragma once
#include "BLI_utildefines.h"
#ifdef __cplusplus
# include "BLI_color.hh"
# include "BLI_function_ref.hh"
# include "BLI_span.hh"
# include "BLI_string_ref.hh"
# include <memory>
#endif
#ifdef __cplusplus
extern "C" {
#endif
struct bContext;
struct bNodeSocket;
struct bNodeSocketType;
struct bNodeTreeInterfaceItem;
struct bNodeTreeInterfacePanel;
struct bNodeTreeInterfaceSocket;
struct ID;
struct IDProperty;
struct LibraryForeachIDData;
struct PointerRNA;
struct uiLayout;
struct BlendWriter;
struct BlendDataReader;
struct BlendLibReader;
struct BlendExpander;
/** Type of interface item. */
typedef enum NodeTreeInterfaceItemType {
NODE_INTERFACE_PANEL = 0,
NODE_INTERFACE_SOCKET = 1,
} eNodeTreeInterfaceItemType;
/** Describes a socket and all necessary details for a node declaration. */
typedef struct bNodeTreeInterfaceItem {
/* eNodeTreeInterfaceItemType */
char item_type;
char _pad[7];
} bNodeTreeInterfaceItem;
/* Socket interface flags */
typedef enum eNodeTreeInterfaceSocketFlag {
NODE_INTERFACE_SOCKET_INPUT = 1 << 0,
NODE_INTERFACE_SOCKET_OUTPUT = 1 << 1,
NODE_INTERFACE_SOCKET_HIDE_VALUE = 1 << 2,
NODE_INTERFACE_SOCKET_HIDE_IN_MODIFIER = 1 << 3,
} eNodeTreeInterfaceSocketFlag;
ENUM_OPERATORS(eNodeTreeInterfaceSocketFlag, NODE_INTERFACE_SOCKET_HIDE_IN_MODIFIER);
typedef struct bNodeTreeInterfaceSocket {
bNodeTreeInterfaceItem item;
/* UI name of the socket. */
char *name;
char *description;
/* Type idname of the socket to generate, e.g. "NodeSocketFloat". */
char *socket_type;
/* eNodeTreeInterfaceSocketFlag */
int flag;
/* eAttrDomain */
int attribute_domain;
char *default_attribute_name;
/* Unique identifier for generated sockets. */
char *identifier;
/* Socket default value and associated data, e.g. bNodeSocketValueFloat. */
void *socket_data;
IDProperty *properties;
#ifdef __cplusplus
bNodeSocketType *socket_typeinfo() const;
blender::ColorGeometry4f socket_color() const;
/**
* Set the \a socket_type and replace the \a socket_data.
* \param new_socket_type: Socket type idname, e.g. "NodeSocketFloat"
*/
bool set_socket_type(const char *new_socket_type);
/**
* Use an existing socket to define an interface socket.
* Replaces the current \a socket_type and any existing \a socket data if called again.
*/
void init_from_socket_instance(const bNodeSocket *socket);
#endif
} bNodeTreeInterfaceSocket;
typedef struct bNodeTreeInterfacePanel {
bNodeTreeInterfaceItem item;
/* UI name of the panel. */
char *name;
bNodeTreeInterfaceItem **items_array;
int items_num;
/* Internal unique identifier for validating panel states. */
int identifier;
#ifdef __cplusplus
blender::IndexRange items_range() const;
blender::Span<const bNodeTreeInterfaceItem *> items() const;
blender::MutableSpan<bNodeTreeInterfaceItem *> items();
/**
* Check if the item is a direct child of the panel.
*/
bool contains(const bNodeTreeInterfaceItem &item) const;
/**
* Search for an item in the interface.
* \return True if the item was found.
*/
bool contains_recursive(const bNodeTreeInterfaceItem &item) const;
/**
* Get the position of an item in this panel.
* \return Position relative to the start of the panel items or -1 if the item is not in the
* panel.
*/
int item_position(const bNodeTreeInterfaceItem &item) const;
/**
* Get the index of the item in the interface.
* \return Index if the item was found or -1 otherwise.
*/
int item_index(const bNodeTreeInterfaceItem &item) const;
/**
* Get the item at the given index of the interface draw list.
*/
const bNodeTreeInterfaceItem *item_at_index(int index) const;
/**
* Find the panel containing the item among this panel and all children.
* \return Parent panel containing the item.
*/
bNodeTreeInterfacePanel *find_parent_recursive(const bNodeTreeInterfaceItem &item);
/** Create a copy of items in the span and add them to the interface. */
void copy_from(blender::Span<const bNodeTreeInterfaceItem *> items_src, int flag);
/** Remove all items from the panel. */
void clear(bool do_id_user);
/**
* Add item at the end of the panel.
* \note Takes ownership of the item.
*/
void add_item(bNodeTreeInterfaceItem &item);
/**
* Insert an item at the given position.
* \note Takes ownership of the item.
*/
void insert_item(bNodeTreeInterfaceItem &item, int position);
/**
* Remove item from the panel.
* \param free: Destruct and deallocate the item.
* \return True if the item was found.
*/
bool remove_item(bNodeTreeInterfaceItem &item, bool free);
/**
* Move item to a new position within the panel.
* \return True if the item was found.
*/
bool move_item(bNodeTreeInterfaceItem &item, int new_position);
/**
* Apply a function to every item in the panel, including child panels.
* \note: The items are visited in drawing order from top to bottom.
*
* \param fn: Function to execute for each item, iterations stops if false is returned.
* \param include_self: Include the panel itself in the iteration.
*/
void foreach_item(blender::FunctionRef<bool(bNodeTreeInterfaceItem &item)> fn,
bool include_self = false);
/** Same as above but for a const interface. */
void foreach_item(blender::FunctionRef<bool(const bNodeTreeInterfaceItem &item)> fn,
bool include_self = false) const;
#endif
} bNodeTreeInterfacePanel;
typedef struct bNodeTreeInterface {
bNodeTreeInterfacePanel root_panel;
/* Global index of the active item. */
int active_index;
int next_uid;
#ifdef __cplusplus
/** Copy data from another interface.
* \param flag: ID creation/copying flags, e.g. LIB_ID_CREATE_NO_MAIN.
*/
void copy_data(const bNodeTreeInterface &src, int flag);
/** Free data before the owning data block is freed.
* \note Does not decrement ID user counts, this has to be done by the caller.
*/
void free_data();
/** Read/write blend file data. */
void write(BlendWriter *writer);
void read_data(BlendDataReader *reader);
void read_lib(BlendLibReader *reader, ID *id);
void read_expand(BlendExpander *expander);
bNodeTreeInterfaceItem *active_item();
const bNodeTreeInterfaceItem *active_item() const;
void active_item_set(bNodeTreeInterfaceItem *item);
/**
* Get the index of the item in the interface.
* \return Index if the item was found or -1 otherwise.
*/
int find_item_index(const bNodeTreeInterfaceItem &item) const
{
return root_panel.item_index(item);
}
/**
* Search for an item in the interface.
* \return True if the item was found.
*/
bool find_item(const bNodeTreeInterfaceItem &item) const
{
return root_panel.contains_recursive(item);
}
/**
* Get the item at the given index of the interface draw list.
*/
const bNodeTreeInterfaceItem *get_item_at_index(int index) const
{
return root_panel.item_at_index(index);
}
/**
* Find the panel containing the item.
* \return Parent panel containing the item.
*/
bNodeTreeInterfacePanel *find_item_parent(const bNodeTreeInterfaceItem &item)
{
return root_panel.find_parent_recursive(item);
}
/**
* Add a new socket at the end of the items list.
* \param parent: Panel in which to add the socket. If parent is null the socket is added in the
* root panel.
*/
bNodeTreeInterfaceSocket *add_socket(blender::StringRefNull name,
blender::StringRefNull description,
blender::StringRefNull socket_type,
eNodeTreeInterfaceSocketFlag flag,
bNodeTreeInterfacePanel *parent);
/**
* Insert a new socket.
* \param parent: Panel in which to add the socket. If parent is null the socket is added in the
* root panel.
* \param position: Position of the socket within the parent panel.
*/
bNodeTreeInterfaceSocket *insert_socket(blender::StringRefNull name,
blender::StringRefNull description,
blender::StringRefNull socket_type,
eNodeTreeInterfaceSocketFlag flag,
bNodeTreeInterfacePanel *parent,
int position);
/**
* Add a new panel at the end of the items list.
* \param parent: Panel in which the new panel is aded as a child. If parent is null the new
* panel is made a child of the root panel.
*/
bNodeTreeInterfacePanel *add_panel(blender::StringRefNull name, bNodeTreeInterfacePanel *parent);
/**
* Insert a new panel.
* \param parent: Panel in which the new panel is aded as a child. If parent is null the new
* panel is made a child of the root panel.
* \param position: Position of the child panel within the parent panel.
*/
bNodeTreeInterfacePanel *insert_panel(blender::StringRefNull name,
bNodeTreeInterfacePanel *parent,
int position);
/**
* Add a copy of an item at the end of the items list.
* \param parent: Add the item inside the parent panel. If parent is null the item is made a
* child of the root panel.
*/
bNodeTreeInterfaceItem *add_item_copy(const bNodeTreeInterfaceItem &item,
bNodeTreeInterfacePanel *parent);
/**
* Insert a copy of an item.
* \param parent: Add the item inside the parent panel. If parent is null the item is made a
* child of the root panel.
* \param position: Position of the item within the parent panel.
*/
bNodeTreeInterfaceItem *insert_item_copy(const bNodeTreeInterfaceItem &item,
bNodeTreeInterfacePanel *parent,
int position);
/**
* Remove an item from the interface.
* \param move_content_to_parent: If the item is a panel, move the contents to the parent instead
* of deleting it.
* \return True if the item was found and successfully removed.
*/
bool remove_item(bNodeTreeInterfaceItem &item, bool move_content_to_parent = true);
void clear_items();
/**
* Move an item to a new position.
* \param new_position: New position of the item in the parent panel.
*/
bool move_item(bNodeTreeInterfaceItem &item, int new_position);
/**
* Move an item to a new panel and/or position.
* \param new_parent: Panel that the item is moved to. If null the item is added to the root
* panel.
* \param new_position: New position of the item in the parent panel.
*/
bool move_item_to_parent(bNodeTreeInterfaceItem &item,
bNodeTreeInterfacePanel *new_parent,
int new_position);
/**
* Apply a function to every item in the interface.
* \note: The items are visited in drawing order from top to bottom.
*
* \param fn: Function to execute for each item, iterations stops if false is returned.
* \param include_root: Include the root panel in the iteration.
*/
void foreach_item(blender::FunctionRef<bool(bNodeTreeInterfaceItem &item)> fn,
bool include_root = false)
{
root_panel.foreach_item(fn, /*include_self=*/include_root);
}
/**
* Apply a function to every item in the interface.
* \note: The items are visited in drawing order from top to bottom.
*
* \param fn: Function to execute for each item, iterations stops if false is returned.
* \param include_root: Include the root panel in the iteration.
*/
void foreach_item(blender::FunctionRef<bool(const bNodeTreeInterfaceItem &item)> fn,
bool include_root = false) const
{
root_panel.foreach_item(fn, /*include_self=*/include_root);
}
void foreach_id(LibraryForeachIDData *cb);
#endif
} bNodeTreeInterface;
#ifdef __cplusplus
}
#endif

View File

@@ -10,6 +10,7 @@
#include "DNA_ID.h"
#include "DNA_listBase.h"
#include "DNA_node_tree_interface_types.h"
#include "DNA_scene_types.h" /* for #ImageFormatData */
#include "DNA_vec_types.h" /* for #rctf */
@@ -637,6 +638,8 @@ typedef struct bNodeTree {
*/
ListBase inputs, outputs;
bNodeTreeInterface tree_interface;
/**
* Node preview hash table.
* Only available in base node trees (e.g. scene->node_tree).