Fix #106208: Initialize socket value by declaration callback if value is data block #106816

Closed
Iliya Katushenock wants to merge 4 commits from mod_moder:default_value_cb into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
3 changed files with 157 additions and 21 deletions

View File

@ -158,6 +158,7 @@ class StringBuilder : public SocketDeclarationBuilder<String> {
class IDSocketDeclaration : public SocketDeclaration {
public:
const char *idname;
std::function<ID *(const bNodeSocket &socket)> init_socket_value_cd;
public:
IDSocketDeclaration(const char *idname);
@ -168,23 +169,30 @@ class IDSocketDeclaration : public SocketDeclaration {
bool can_connect(const bNodeSocket &socket) const override;
};
template<typename Declaration>
class IDSocketDeclarationBuilder : public SocketDeclarationBuilder<Declaration> {
public:
IDSocketDeclarationBuilder &socket_value(
std::function<ID *(const bNodeSocket &socket)> &&value_cb);
};
class Object : public IDSocketDeclaration {
public:
using Builder = SocketDeclarationBuilder<Object>;
using Builder = IDSocketDeclarationBuilder<Object>;
Object();
};
class Material : public IDSocketDeclaration {
public:
using Builder = SocketDeclarationBuilder<Material>;
using Builder = IDSocketDeclarationBuilder<Material>;
Material();
};
class Collection : public IDSocketDeclaration {
public:
using Builder = SocketDeclarationBuilder<Collection>;
using Builder = IDSocketDeclarationBuilder<Collection>;
Collection();
};
@ -367,6 +375,21 @@ inline ColorBuilder &ColorBuilder::default_value(const ColorGeometry4f value)
/** \} */
/* -------------------------------------------------------------------- */
/** \name #ColorBuilder Inline Methods
* \{ */
template<typename Declaration>
inline IDSocketDeclarationBuilder<Declaration>
&IDSocketDeclarationBuilder<Declaration>::socket_value(
std::function<ID *(const bNodeSocket &socket)> &&value_cb)
{
decl_->init_socket_value_cd = std::move(value_cb);
return *this;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name #StringBuilder Inline Methods
* \{ */

View File

@ -8,6 +8,8 @@
#include <cstddef>
#include <cstring>
#include "DNA_collection_types.h"
#include "DNA_material_types.h"
#include "DNA_node_types.h"
#include "BLI_listbase.h"
@ -124,7 +126,41 @@ bool nodeGroupPoll(const bNodeTree *nodetree,
namespace blender::nodes {
static SocketDeclarationPtr declaration_for_interface_socket(const bNodeSocket &io_socket)
static std::function<ID *(const bNodeSocket &socket)> default_node_group_id_value(
bNodeTree &node_group, StringRefNull socket_name_p)
{
std::string socket_name = socket_name_p;
return [node_group = &node_group,
Review

I agree that we need to avoid ID pointers in the declarations. But this solves one problem by creating another: Now we have the bNodeTree bound in the callback.

A weak pointer of some kind seems to be needed here, so that ID management code does not have to worry about references stored in node declarations.

I agree that we need to avoid ID pointers in the declarations. But this solves one problem by creating another: Now we have the `bNodeTree` bound in the callback. A weak pointer of some kind seems to be needed here, so that ID management code does not have to worry about references stored in node declarations.

Trees cause refresh on their own. That is, it must be safe.
It would be possible to add a requirement to the topology cache in order to find a node from the socket and take its pointer to the tree. But this can increase the overhead. Or we can pass a node. But at one point it may require also a tree as well ... there is not enough some context

Trees cause refresh on their own. That is, it must be safe. It would be possible to add a requirement to the topology cache in order to find a node from the socket and take its pointer to the tree. But this can increase the overhead. Or we can pass a node. But at one point it may require also a tree as well ... there is not enough some context
socket_name = std::move(socket_name)](const bNodeSocket & /*socket*/) -> ID * {
LISTBASE_FOREACH (bNodeSocket *, input, &node_group->inputs) {
if (StringRef(input->name) == StringRef(socket_name)) {
switch (input->type) {
case SOCK_OBJECT: {
return &input->default_value_typed<bNodeSocketValueObject>()->value->id;
}
case SOCK_IMAGE: {
return &input->default_value_typed<bNodeSocketValueImage>()->value->id;
}
case SOCK_TEXTURE: {
return &input->default_value_typed<bNodeSocketValueTexture>()->value->id;
}
case SOCK_COLLECTION: {
return &input->default_value_typed<bNodeSocketValueCollection>()->value->id;
}
case SOCK_MATERIAL: {
return &input->default_value_typed<bNodeSocketValueMaterial>()->value->id;
}
default:
break;
}
}
}
return nullptr;
};
}
static SocketDeclarationPtr declaration_for_interface_socket(const bNodeSocket &io_socket,
const bNodeTree *node_group)
{
SocketDeclarationPtr dst;
switch (io_socket.type) {
@ -184,24 +220,64 @@ static SocketDeclarationPtr declaration_for_interface_socket(const bNodeSocket &
dst = std::move(decl);
break;
}
case SOCK_OBJECT:
dst = std::make_unique<decl::Object>();
case SOCK_OBJECT: {
std::unique_ptr<decl::Object> decl = std::make_unique<decl::Object>();
if (node_group == nullptr) {
dst = std::move(decl);
break;
}
decl->init_socket_value_cd = default_node_group_id_value(
*const_cast<bNodeTree *>(node_group), io_socket.name);
dst = std::move(decl);
break;
case SOCK_IMAGE:
dst = std::make_unique<decl::Image>();
}
case SOCK_IMAGE: {
std::unique_ptr<decl::Image> decl = std::make_unique<decl::Image>();
if (node_group == nullptr) {
dst = std::move(decl);
break;
}
decl->init_socket_value_cd = default_node_group_id_value(
*const_cast<bNodeTree *>(node_group), io_socket.name);
dst = std::move(decl);
break;
}
case SOCK_COLLECTION: {
std::unique_ptr<decl::Collection> decl = std::make_unique<decl::Collection>();
if (node_group == nullptr) {
dst = std::move(decl);
break;
}
decl->init_socket_value_cd = default_node_group_id_value(
*const_cast<bNodeTree *>(node_group), io_socket.name);
dst = std::move(decl);
break;
}
case SOCK_TEXTURE: {
std::unique_ptr<decl::Texture> decl = std::make_unique<decl::Texture>();
if (node_group == nullptr) {
dst = std::move(decl);
break;
}
decl->init_socket_value_cd = default_node_group_id_value(
*const_cast<bNodeTree *>(node_group), io_socket.name);
dst = std::move(decl);
break;
}
case SOCK_MATERIAL: {
std::unique_ptr<decl::Material> decl = std::make_unique<decl::Material>();
if (node_group == nullptr) {
dst = std::move(decl);
break;
}
decl->init_socket_value_cd = default_node_group_id_value(
*const_cast<bNodeTree *>(node_group), io_socket.name);
dst = std::move(decl);
break;
}
case SOCK_GEOMETRY:
dst = std::make_unique<decl::Geometry>();
break;
case SOCK_COLLECTION:
dst = std::make_unique<decl::Collection>();
break;
case SOCK_TEXTURE:
dst = std::make_unique<decl::Texture>();
break;
case SOCK_MATERIAL:
dst = std::make_unique<decl::Material>();
break;
case SOCK_CUSTOM:
std::unique_ptr<decl::Custom> decl = std::make_unique<decl::Custom>();
decl->idname_ = io_socket.idname;
@ -232,10 +308,10 @@ void node_group_declare_dynamic(const bNodeTree & /*node_tree*/,
r_declaration.skip_updating_sockets = false;
LISTBASE_FOREACH (const bNodeSocket *, input, &group->inputs) {
r_declaration.inputs.append(declaration_for_interface_socket(*input));
r_declaration.inputs.append(declaration_for_interface_socket(*input, group));
}
LISTBASE_FOREACH (const bNodeSocket *, output, &group->outputs) {
r_declaration.outputs.append(declaration_for_interface_socket(*output));
r_declaration.outputs.append(declaration_for_interface_socket(*output, group));
}
}
@ -450,7 +526,7 @@ static void group_input_declare_dynamic(const bNodeTree &node_tree,
NodeDeclaration &r_declaration)
{
LISTBASE_FOREACH (const bNodeSocket *, input, &node_tree.inputs) {
r_declaration.outputs.append(declaration_for_interface_socket(*input));
r_declaration.outputs.append(declaration_for_interface_socket(*input, &node_tree));
r_declaration.outputs.last()->in_out = SOCK_OUT;
}
r_declaration.outputs.append(extend_declaration(SOCK_OUT));
@ -461,7 +537,7 @@ static void group_output_declare_dynamic(const bNodeTree &node_tree,
NodeDeclaration &r_declaration)
{
LISTBASE_FOREACH (const bNodeSocket *, input, &node_tree.outputs) {
r_declaration.inputs.append(declaration_for_interface_socket(*input));
r_declaration.inputs.append(declaration_for_interface_socket(*input, &node_tree));
r_declaration.inputs.last()->in_out = SOCK_IN;
}
r_declaration.inputs.append(extend_declaration(SOCK_IN));

View File

@ -431,11 +431,48 @@ bNodeSocket &String::update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket
/** \name #IDSocketDeclaration
* \{ */
static void id_socket_fill_value_try(ID *src, bNodeSocket &r_socket)
{
if (src == nullptr) {
return;
}
switch ((ID_Type)GS(src->name)) {
case ID_OB: {
using TPtr = decltype(r_socket.default_value_typed<bNodeSocketValueObject>()->value);
r_socket.default_value_typed<bNodeSocketValueObject>()->value = reinterpret_cast<TPtr>(src);
}
case ID_MA: {
using TPtr = decltype(r_socket.default_value_typed<bNodeSocketValueMaterial>()->value);
r_socket.default_value_typed<bNodeSocketValueMaterial>()->value = reinterpret_cast<TPtr>(
src);
}
case ID_IM: {
using TPtr = decltype(r_socket.default_value_typed<bNodeSocketValueImage>()->value);
r_socket.default_value_typed<bNodeSocketValueImage>()->value = reinterpret_cast<TPtr>(src);
}
case ID_TE: {
using TPtr = decltype(r_socket.default_value_typed<bNodeSocketValueTexture>()->value);
r_socket.default_value_typed<bNodeSocketValueTexture>()->value = reinterpret_cast<TPtr>(src);
}
case ID_CO: {
using TPtr = decltype(r_socket.default_value_typed<bNodeSocketValueCollection>()->value);
r_socket.default_value_typed<bNodeSocketValueCollection>()->value = reinterpret_cast<TPtr>(
src);
}
default: {
}
}
}
bNodeSocket &IDSocketDeclaration::build(bNodeTree &ntree, bNode &node) const
{
bNodeSocket &socket = *nodeAddSocket(
&ntree, &node, this->in_out, this->idname, this->identifier.c_str(), this->name.c_str());
this->set_common_flags(socket);
if (this->init_socket_value_cd) {
ID *scr_socket_value = this->init_socket_value_cd(socket);
id_socket_fill_value_try(scr_socket_value, socket);
}
return socket;
}