mirror of
https://github.com/blender/blender
synced 2026-09-29 04:37:17 +03:00
Nodes: Type conversion node to force a specific data type
Simple generic type conversion nodes that implicitly convert the input to a specific data type. These nodes are conceptually similar to _Reroute_ nodes, except with a fixed, selectable data type. They can be useful for forcing a node socket to be converted to a given data type, instead of just relying on the source and target types. This will be needed for improving the _ungroup_ operator, which can then preserve the behavior of the original node group interface sockets (#151432). Pull Request: https://projects.blender.org/blender/blender/pulls/154497
This commit is contained in:
parent
52029db5b0
commit
5b8b780ff7
12 changed files with 300 additions and 2 deletions
|
|
@ -282,6 +282,7 @@ class NODE_MT_compositor_node_utilities_base(node_add_menu.NodeMenu):
|
|||
self.node_operator(layout, "CompositorNodeLevels")
|
||||
self.node_operator(layout, "CompositorNodeNormalize")
|
||||
layout.separator()
|
||||
self.node_operator(layout, "NodeImplicitConversion")
|
||||
self.node_operator(layout, "CompositorNodeSplit")
|
||||
self.node_operator(layout, "CompositorNodeSwitch")
|
||||
self.node_operator(layout, "GeometryNodeIndexSwitch")
|
||||
|
|
|
|||
|
|
@ -702,6 +702,7 @@ class NODE_MT_gn_utilities_base(node_add_menu.NodeMenu):
|
|||
self.draw_menu(layout, path="Utilities/Matrix")
|
||||
self.draw_menu(layout, path="Utilities/Rotation")
|
||||
layout.separator()
|
||||
self.node_operator(layout, "NodeImplicitConversion")
|
||||
self.for_each_element_zone(layout, label="For Each Element")
|
||||
self.node_operator(layout, "GeometryNodeIndexSwitch")
|
||||
self.node_operator(layout, "GeometryNodeMenuSwitch")
|
||||
|
|
|
|||
|
|
@ -419,6 +419,7 @@ class NODE_MT_shader_node_utilities_base(node_add_menu.NodeMenu):
|
|||
layout.separator()
|
||||
self.repeat_zone(layout, label="Repeat")
|
||||
layout.separator()
|
||||
self.node_operator(layout, "NodeImplicitConversion")
|
||||
self.closure_zone(layout, label="Closure")
|
||||
self.node_operator(layout, "NodeEvaluateClosure")
|
||||
self.node_operator(layout, "NodeCombineBundle")
|
||||
|
|
|
|||
|
|
@ -2137,6 +2137,13 @@ struct NodeReroute {
|
|||
char type_idname[64] = "";
|
||||
};
|
||||
|
||||
struct NodeImplicitConversion {
|
||||
DNA_DEFINE_CXX_METHODS(NodeImplicitConversion)
|
||||
|
||||
/** Name of the socket type (e.g. `NodeSocketFloat`). */
|
||||
char type_idname[64] = "";
|
||||
};
|
||||
|
||||
/** \note This one has been replaced with #ImageUser, keep it for do_versions(). */
|
||||
struct NodeImageAnim {
|
||||
DNA_DEFINE_CXX_METHODS(NodeImageAnim)
|
||||
|
|
|
|||
|
|
@ -4109,6 +4109,30 @@ static void rna_reroute_node_socket_type_set(PointerRNA *ptr, const char *value)
|
|||
STRNCPY(storage->type_idname, value);
|
||||
}
|
||||
|
||||
static void rna_implicit_conversion_node_socket_type_set(PointerRNA *ptr, const char *value)
|
||||
{
|
||||
const bNodeTree &ntree = *reinterpret_cast<bNodeTree *>(ptr->owner_id);
|
||||
bke::bNodeTreeType *ntree_type = ntree.typeinfo;
|
||||
|
||||
bNode &node = *ptr->data_as<bNode>();
|
||||
|
||||
if (value == nullptr) {
|
||||
return;
|
||||
}
|
||||
bke::bNodeSocketType *socket_type = bke::node_socket_type_find(value);
|
||||
if (socket_type == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (socket_type->subtype != PROP_NONE) {
|
||||
return;
|
||||
}
|
||||
if (ntree_type->valid_socket_type && !ntree_type->valid_socket_type(ntree_type, socket_type)) {
|
||||
return;
|
||||
}
|
||||
NodeImplicitConversion *storage = static_cast<NodeImplicitConversion *>(node.storage);
|
||||
STRNCPY(storage->type_idname, value);
|
||||
}
|
||||
|
||||
static const EnumPropertyItem *rna_NodeConvertColorSpace_color_space_itemf(bContext * /*C*/,
|
||||
PointerRNA * /*ptr*/,
|
||||
PropertyRNA * /*prop*/,
|
||||
|
|
@ -4148,6 +4172,46 @@ static PointerRNA rna_NodeMenuSwitch_enum_definition_get(PointerRNA *ptr)
|
|||
return *ptr;
|
||||
}
|
||||
|
||||
static int rna_NodeImplicitConversion_data_type_get(PointerRNA *ptr)
|
||||
{
|
||||
const bNode &node = *ptr->data_as<bNode>();
|
||||
const NodeImplicitConversion &data = *static_cast<NodeImplicitConversion *>(node.storage);
|
||||
const bke::bNodeSocketType *socket_type = bke::node_socket_type_find(data.type_idname);
|
||||
return socket_type ? socket_type->type : SOCK_CUSTOM;
|
||||
}
|
||||
|
||||
static void rna_NodeImplicitConversion_data_type_set(PointerRNA *ptr, const int value)
|
||||
{
|
||||
const bke::bNodeSocketType *socket_type = bke::node_socket_type_find_static(value);
|
||||
if (socket_type) {
|
||||
bNode &node = *ptr->data_as<bNode>();
|
||||
NodeImplicitConversion &data = *static_cast<NodeImplicitConversion *>(node.storage);
|
||||
STRNCPY(data.type_idname, socket_type->idname.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
static const EnumPropertyItem *rna_NodeImplicitConversion_data_type_itemf(bContext * /*C*/,
|
||||
PointerRNA *ptr,
|
||||
PropertyRNA * /*prop*/,
|
||||
bool *r_free)
|
||||
{
|
||||
*r_free = true;
|
||||
bNodeTree &ntree = *id_cast<bNodeTree *>(ptr->owner_id);
|
||||
return itemf_function_check(
|
||||
rna_enum_node_socket_data_type_items, [&](const EnumPropertyItem *item) {
|
||||
bke::bNodeSocketType *socket_type = bke::node_socket_type_find_static(item->value);
|
||||
if (!socket_type) {
|
||||
return false;
|
||||
}
|
||||
if (ntree.typeinfo->valid_socket_type &&
|
||||
!ntree.typeinfo->valid_socket_type(ntree.typeinfo, socket_type))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
|
||||
#else
|
||||
|
|
@ -8643,6 +8707,31 @@ static void def_reroute(BlenderRNA * /*brna*/, StructRNA *srna)
|
|||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
|
||||
}
|
||||
|
||||
static void def_implicit_conversion(BlenderRNA * /*brna*/, StructRNA *srna)
|
||||
{
|
||||
PropertyRNA *prop;
|
||||
|
||||
RNA_def_struct_sdna_from(srna, "NodeImplicitConversion", "storage");
|
||||
|
||||
prop = RNA_def_property(srna, "socket_idname", PROP_STRING, PROP_NONE);
|
||||
RNA_def_property_string_sdna(prop, nullptr, "type_idname");
|
||||
RNA_def_property_string_funcs(
|
||||
prop, nullptr, nullptr, "rna_implicit_conversion_node_socket_type_set");
|
||||
RNA_def_property_ui_text(prop, "Type of socket", "");
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
|
||||
|
||||
prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_items(prop, rna_enum_node_socket_data_type_items);
|
||||
RNA_def_property_enum_funcs(prop,
|
||||
"rna_NodeImplicitConversion_data_type_get",
|
||||
"rna_NodeImplicitConversion_data_type_set",
|
||||
"rna_NodeImplicitConversion_data_type_itemf");
|
||||
RNA_def_property_enum_default(prop, SOCK_FLOAT);
|
||||
RNA_def_property_ui_text(prop, "Data Type", "");
|
||||
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
|
||||
}
|
||||
|
||||
static void rna_def_internal_node(BlenderRNA *brna)
|
||||
{
|
||||
StructRNA *srna;
|
||||
|
|
@ -9787,6 +9876,7 @@ static void rna_def_nodes(BlenderRNA *brna)
|
|||
define("NodeInternal", "NodeGroupInput", def_group_input);
|
||||
define("NodeInternal", "NodeGroupOutput", def_group_output);
|
||||
define("NodeInternal", "NodeReroute", def_reroute);
|
||||
define("NodeInternal", "NodeImplicitConversion", def_implicit_conversion);
|
||||
|
||||
define("NodeInternal", "NodeClosureInput", def_closure_input);
|
||||
define("NodeInternal", "NodeClosureOutput", def_closure_output);
|
||||
|
|
|
|||
|
|
@ -55,6 +55,8 @@ set(INC
|
|||
texture
|
||||
geometry/include
|
||||
../animrig
|
||||
../compositor
|
||||
../compositor/cached_resources
|
||||
../editors/include
|
||||
../depsgraph
|
||||
../makesrna
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ void register_nodes();
|
|||
|
||||
void register_node_type_frame();
|
||||
void register_node_type_reroute();
|
||||
void register_node_type_implicit_conversion();
|
||||
|
||||
void register_node_type_group_input();
|
||||
void register_node_type_group_output();
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
#include "BLI_stack.hh"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_string_ref.hh"
|
||||
#include "BLI_string_utf8.h"
|
||||
#include "BLI_vector_set.hh"
|
||||
|
||||
#include "BLT_translation.hh"
|
||||
|
|
@ -32,10 +33,14 @@
|
|||
#include "BKE_node_runtime.hh"
|
||||
#include "BKE_node_tree_interface.hh"
|
||||
|
||||
#include "COM_node_operation.hh"
|
||||
#include "COM_result.hh"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "NOD_common.hh"
|
||||
#include "NOD_composite.hh"
|
||||
#include "NOD_geometry_exec.hh"
|
||||
#include "NOD_node_declaration.hh"
|
||||
#include "NOD_node_extra_info.hh"
|
||||
#include "NOD_register.hh"
|
||||
|
|
@ -43,6 +48,9 @@
|
|||
#include "NOD_socket_declarations.hh"
|
||||
#include "NOD_socket_declarations_geometry.hh"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
#include "RNA_enum_types.hh"
|
||||
|
||||
#include "UI_resources.hh"
|
||||
|
||||
#include "ED_node.hh"
|
||||
|
|
@ -227,9 +235,10 @@ static std::function<ID *(const bNode &node)> get_default_id_getter(
|
|||
}
|
||||
|
||||
static std::function<void(bNode &node, bNodeSocket &socket, const char *data_path)>
|
||||
get_init_socket_fn(const bNodeTreeInterface &interface, const bNodeTreeInterfaceSocket &io_socket)
|
||||
get_init_socket_fn(const bNodeTreeInterface &tree_interface,
|
||||
const bNodeTreeInterfaceSocket &io_socket)
|
||||
{
|
||||
const int item_index = interface.find_item_index(io_socket.item);
|
||||
const int item_index = tree_interface.find_item_index(io_socket.item);
|
||||
BLI_assert(item_index >= 0);
|
||||
|
||||
/* Avoid capturing pointers that can become dangling. */
|
||||
|
|
@ -807,6 +816,146 @@ bool bke::node_is_connected_to_output(const bNodeTree &ntree, const bNode &node)
|
|||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Implicit Conversion Node
|
||||
* \{ */
|
||||
|
||||
static void node_implicit_conversion_declare(nodes::NodeDeclarationBuilder &b)
|
||||
{
|
||||
const bNode *node = b.node_or_null();
|
||||
if (node == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const StringRefNull socket_idname(
|
||||
static_cast<const NodeImplicitConversion *>(node->storage)->type_idname);
|
||||
b.use_custom_socket_order();
|
||||
b.allow_any_socket_order();
|
||||
b.add_default_layout();
|
||||
b.add_input<nodes::decl::Custom>("Value")
|
||||
.idname(socket_idname.c_str())
|
||||
.structure_type(nodes::StructureType::Dynamic)
|
||||
.optional_label();
|
||||
b.add_output<nodes::decl::Custom>("Value")
|
||||
.idname(socket_idname.c_str())
|
||||
.structure_type(nodes::StructureType::Dynamic)
|
||||
.reference_pass_all()
|
||||
.propagate_all()
|
||||
.align_with_previous();
|
||||
}
|
||||
|
||||
static void node_implicit_conversion_label(const bNodeTree * /*ntree*/,
|
||||
const bNode *node,
|
||||
char *label,
|
||||
int label_maxncpy)
|
||||
{
|
||||
const auto &data = *static_cast<NodeImplicitConversion *>(node->storage);
|
||||
const bke::bNodeSocketType *socket_type = bke::node_socket_type_find(data.type_idname);
|
||||
if (!socket_type) {
|
||||
BLI_strncpy(label,
|
||||
CTX_IFACE_(BLT_I18NCONTEXT_ID_NODETREE, node->typeinfo->ui_name.c_str()),
|
||||
label_maxncpy);
|
||||
return;
|
||||
}
|
||||
|
||||
const char *name;
|
||||
bool enum_label = RNA_enum_name(rna_enum_node_socket_data_type_items, socket_type->type, &name);
|
||||
if (!enum_label) {
|
||||
BLI_strncpy(label,
|
||||
CTX_IFACE_(BLT_I18NCONTEXT_ID_NODETREE, node->typeinfo->ui_name.c_str()),
|
||||
label_maxncpy);
|
||||
return;
|
||||
}
|
||||
|
||||
BLI_snprintf_utf8(label, label_maxncpy, "To %s", CTX_IFACE_(BLT_I18NCONTEXT_ID_NODETREE, name));
|
||||
}
|
||||
|
||||
static void node_implicit_conversion_layout(ui::Layout &layout, bContext * /*C*/, PointerRNA *ptr)
|
||||
{
|
||||
layout.use_property_split_set(true);
|
||||
layout.use_property_decorate_set(false);
|
||||
layout.prop(ptr, "data_type", UI_ITEM_NONE, "", ICON_NONE);
|
||||
}
|
||||
|
||||
static void node_implicit_conversion_init(bNodeTree * /*ntree*/, bNode *node)
|
||||
{
|
||||
NodeImplicitConversion *data = MEM_new<NodeImplicitConversion>(__func__);
|
||||
STRNCPY(data->type_idname, "NodeSocketColor");
|
||||
node->storage = data;
|
||||
}
|
||||
|
||||
static bool node_implicit_conversion_poll_instance(const bNode *node,
|
||||
const bNodeTree *nodetree,
|
||||
const char **r_disabled_hint)
|
||||
{
|
||||
const auto &data = *static_cast<NodeImplicitConversion *>(node->storage);
|
||||
bke::bNodeSocketType *socket_type = bke::node_socket_type_find(data.type_idname);
|
||||
if (!socket_type) {
|
||||
if (r_disabled_hint) {
|
||||
*r_disabled_hint = "Socket type not found";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bke::bNodeTreeType &tree_type = *nodetree->typeinfo;
|
||||
if (tree_type.valid_socket_type && !tree_type.valid_socket_type(&tree_type, socket_type)) {
|
||||
if (r_disabled_hint) {
|
||||
*r_disabled_hint = "Socket type not supported";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void node_implicit_conversion_geo_exec(nodes::GeoNodeExecParams params)
|
||||
{
|
||||
auto input_value = params.extract_input<bke::SocketValueVariant>("Value");
|
||||
params.set_output("Value", std::move(input_value));
|
||||
}
|
||||
|
||||
class ImplicitConversionOperation : public compositor::NodeOperation {
|
||||
public:
|
||||
using NodeOperation::NodeOperation;
|
||||
|
||||
void execute() override
|
||||
{
|
||||
using namespace compositor;
|
||||
const Result &input = this->get_input("Value");
|
||||
Result &output = this->get_result("Value");
|
||||
output.share_data(input);
|
||||
}
|
||||
};
|
||||
|
||||
static compositor::NodeOperation *node_implicit_conversion_compositor_operation(
|
||||
compositor::Context &context, const bNode &node)
|
||||
{
|
||||
return new ImplicitConversionOperation(context, node);
|
||||
}
|
||||
|
||||
void register_node_type_implicit_conversion()
|
||||
{
|
||||
/* Adapt type node is used for all tree types, needs dynamic allocation. */
|
||||
bke::bNodeType *ntype = MEM_new<bke::bNodeType>("Implicit Conversion node type");
|
||||
ntype->free_self = [](bke::bNodeType *type) { MEM_delete(type); };
|
||||
|
||||
bke::node_type_base(*ntype, "NodeImplicitConversion");
|
||||
ntype->ui_name = "Implicit Conversion";
|
||||
ntype->ui_description = "Implicitly convert the input value to a fixed socket type";
|
||||
ntype->nclass = NODE_CLASS_CONVERTER;
|
||||
ntype->declare = node_implicit_conversion_declare;
|
||||
ntype->labelfunc = node_implicit_conversion_label;
|
||||
ntype->draw_buttons = node_implicit_conversion_layout;
|
||||
ntype->initfunc = node_implicit_conversion_init;
|
||||
node_type_storage(
|
||||
*ntype, "NodeImplicitConversion", node_free_standard_storage, node_copy_standard_storage);
|
||||
ntype->poll_instance = node_implicit_conversion_poll_instance;
|
||||
ntype->geometry_node_execute = node_implicit_conversion_geo_exec;
|
||||
ntype->get_compositor_operation = node_implicit_conversion_compositor_operation;
|
||||
|
||||
bke::node_register_type(*ntype);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Node #GROUP_INPUT / #GROUP_OUTPUT
|
||||
* \{ */
|
||||
|
|
|
|||
|
|
@ -147,6 +147,7 @@ void register_nodes()
|
|||
|
||||
register_node_type_frame();
|
||||
register_node_type_reroute();
|
||||
register_node_type_implicit_conversion();
|
||||
register_node_type_group_input();
|
||||
register_node_type_group_output();
|
||||
|
||||
|
|
|
|||
|
|
@ -198,6 +198,12 @@ void eval_downstream(
|
|||
forward_output({context, &node.output_socket(0)});
|
||||
}
|
||||
}
|
||||
if (node.is_type("NodeImplicitConversion")) {
|
||||
if (propagate_value_fn({context, &node.input_socket(0)}, {context, &node.output_socket(0)}))
|
||||
{
|
||||
forward_output({context, &node.output_socket(0)});
|
||||
}
|
||||
}
|
||||
else if (node.is_muted()) {
|
||||
for (const bNodeLink &link : node.internal_links()) {
|
||||
if (propagate_value_fn({context, link.fromsock}, {context, link.tosock})) {
|
||||
|
|
@ -380,6 +386,10 @@ UpstreamEvalTargets eval_upstream(
|
|||
propagate_value_fn({context, &node.output_socket(0)}, {context, &node.input_socket(0)});
|
||||
forward_input({context, &node.input_socket(0)});
|
||||
}
|
||||
else if (node.is_type("NodeImplicitConversion")) {
|
||||
propagate_value_fn({context, &node.output_socket(0)}, {context, &node.input_socket(0)});
|
||||
forward_input({context, &node.input_socket(0)});
|
||||
}
|
||||
else if (node.is_muted()) {
|
||||
for (const bNodeLink &link : node.internal_links()) {
|
||||
if (propagate_value_fn({context, link.tosock}, {context, link.fromsock})) {
|
||||
|
|
|
|||
|
|
@ -568,6 +568,10 @@ class ShaderNodesInliner {
|
|||
this->handle_output_socket__join_bundle(socket);
|
||||
return;
|
||||
}
|
||||
if (node->is_type("NodeImplicitConversion")) {
|
||||
this->handle_output_socket__implicit_conversion(socket);
|
||||
return;
|
||||
}
|
||||
this->handle_output_socket__eval(socket);
|
||||
}
|
||||
|
||||
|
|
@ -1081,6 +1085,22 @@ class ShaderNodesInliner {
|
|||
this->store_socket_value(socket, {PrimitiveSocketValue{is_selected}});
|
||||
}
|
||||
|
||||
void handle_output_socket__implicit_conversion(const SocketInContext &socket)
|
||||
{
|
||||
const NodeInContext node = socket.owner_node();
|
||||
|
||||
const SocketInContext input_socket = node.input_socket(0);
|
||||
const SocketValue *socket_value = value_by_socket_.lookup_ptr(input_socket);
|
||||
if (!socket_value) {
|
||||
/* The input bundle is not known yet, so schedule it for now. */
|
||||
this->schedule_socket(input_socket);
|
||||
return;
|
||||
}
|
||||
const SocketValue converted_value = this->handle_implicit_conversion(
|
||||
*socket_value, *socket->typeinfo, *socket->typeinfo);
|
||||
this->store_socket_value(socket, converted_value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate a node to compute the value of the given output socket. This may also compute all the
|
||||
* other outputs of the node.
|
||||
|
|
|
|||
|
|
@ -197,6 +197,10 @@ class SocketValueInferencerImpl {
|
|||
this->value_task__output__enable_output(socket);
|
||||
return;
|
||||
}
|
||||
if (node->is_type("NodeImplicitConversion")) {
|
||||
this->value_task__output__implicit_conversion_node(socket);
|
||||
return;
|
||||
}
|
||||
if (node->typeinfo->build_multi_function) {
|
||||
this->value_task__output__multi_function_node(socket);
|
||||
return;
|
||||
|
|
@ -276,6 +280,17 @@ class SocketValueInferencerImpl {
|
|||
all_socket_values_.add_new(socket, *value);
|
||||
}
|
||||
|
||||
void value_task__output__implicit_conversion_node(const SocketInContext &socket)
|
||||
{
|
||||
const SocketInContext input_socket = socket.owner_node().input_socket(0);
|
||||
const std::optional<InferenceValue> value = all_socket_values_.lookup_try(input_socket);
|
||||
if (!value.has_value()) {
|
||||
this->push_value_task(input_socket);
|
||||
return;
|
||||
}
|
||||
all_socket_values_.add_new(socket, *value);
|
||||
}
|
||||
|
||||
void value_task__output__menu_switch_selection(const SocketInContext &socket)
|
||||
{
|
||||
const NodeInContext node = socket.owner_node();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue