Refactoring: Nodes: Dynamic declaration for dynamically-typed function nodes #113492

Open
Iliya Katushenock wants to merge 4 commits from mod_moder/blender:refactoring_declaration_in_function_nodes into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
6 changed files with 144 additions and 161 deletions

View File

@ -23,7 +23,14 @@ static void node_declare(NodeDeclarationBuilder &b)
{
b.is_function_node();
b.add_input<decl::Bool>("Boolean", "Boolean");
b.add_input<decl::Bool>("Boolean", "Boolean_001");
const bNode *node = b.node_or_null();
if (node != nullptr) {
const auto type = NodeBooleanMathOperation(node->custom1);
if (type != NODE_BOOLEAN_MATH_NOT) {
b.add_input<decl::Bool>("Boolean", "Boolean_001");
}
}
b.add_output<decl::Bool>("Boolean");
}
@ -32,13 +39,6 @@ static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
uiItemR(layout, ptr, "operation", UI_ITEM_NONE, "", ICON_NONE);
}
static void node_update(bNodeTree *ntree, bNode *node)
{
bNodeSocket *sockB = (bNodeSocket *)BLI_findlink(&node->inputs, 1);
bke::nodeSetSocketAvailability(ntree, sockB, !ELEM(node->custom1, NODE_BOOLEAN_MATH_NOT));
}
static void node_label(const bNodeTree * /*tree*/,
const bNode *node,
char *label,
@ -145,7 +145,6 @@ static void node_register()
fn_node_type_base(&ntype, FN_NODE_BOOLEAN_MATH, "Boolean Math", NODE_CLASS_CONVERTER);
ntype.declare = node_declare;
ntype.labelfunc = node_label;
ntype.updatefunc = node_update;
ntype.build_multi_function = node_build_multi_function;
ntype.draw_buttons = node_layout;
ntype.gather_link_search_ops = node_gather_link_searches;

View File

@ -18,24 +18,44 @@ NODE_STORAGE_FUNCS(NodeCombSepColor)
static void node_declare(NodeDeclarationBuilder &b)
{
b.is_function_node();
b.add_input<decl::Float>("Red").default_value(0.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
b.add_input<decl::Float>("Green").default_value(0.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
b.add_input<decl::Float>("Blue").default_value(0.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
const bNode *node = b.node_or_null();
if (node != nullptr) {
const NodeCombSepColor &storage = node_storage(*node);
const NodeCombSepColorMode mode = NodeCombSepColorMode(storage.mode);
switch (mode) {
case NODE_COMBSEP_COLOR_RGB:
b.add_input<decl::Float>("Red").min(0.0f).max(1.0f).subtype(PROP_FACTOR);
b.add_input<decl::Float>("Green").min(0.0f).max(1.0f).subtype(PROP_FACTOR);
b.add_input<decl::Float>("Blue").min(0.0f).max(1.0f).subtype(PROP_FACTOR);
break;
case NODE_COMBSEP_COLOR_HSL:
b.add_input<decl::Float>("Hue").min(0.0f).max(1.0f).subtype(PROP_FACTOR);
b.add_input<decl::Float>("Saturation").min(0.0f).max(1.0f).subtype(PROP_FACTOR);
b.add_input<decl::Float>("Lightness").min(0.0f).max(1.0f).subtype(PROP_FACTOR);
break;
case NODE_COMBSEP_COLOR_HSV:
b.add_input<decl::Float>("Hue").min(0.0f).max(1.0f).subtype(PROP_FACTOR);
b.add_input<decl::Float>("Saturation").min(0.0f).max(1.0f).subtype(PROP_FACTOR);
b.add_input<decl::Float>("Value").min(0.0f).max(1.0f).subtype(PROP_FACTOR);
break;
default: {
BLI_assert_unreachable();
break;
}
}
}
b.add_input<decl::Float>("Alpha").default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
b.add_output<decl::Color>("Color");
};
}
static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
{
uiItemR(layout, ptr, "mode", UI_ITEM_NONE, "", ICON_NONE);
}
static void node_update(bNodeTree * /*tree*/, bNode *node)
{
const NodeCombSepColor &storage = node_storage(*node);
node_combsep_color_label(&node->inputs, (NodeCombSepColorMode)storage.mode);
}
static void node_init(bNodeTree * /*tree*/, bNode *node)
{
NodeCombSepColor *data = MEM_cnew<NodeCombSepColor>(__func__);
@ -99,7 +119,6 @@ static void node_register()
fn_node_type_base(&ntype, FN_NODE_COMBINE_COLOR, "Combine Color", NODE_CLASS_CONVERTER);
ntype.declare = node_declare;
ntype.updatefunc = node_update;
ntype.initfunc = node_init;
node_type_storage(
&ntype, "NodeCombSepColor", node_free_standard_storage, node_copy_standard_storage);

View File

@ -28,26 +28,32 @@ NODE_STORAGE_FUNCS(NodeFunctionCompare)
static void node_declare(NodeDeclarationBuilder &b)
{
b.is_function_node();
b.add_input<decl::Float>("A").min(-10000.0f).max(10000.0f).translation_context(
BLT_I18NCONTEXT_ID_NODETREE);
b.add_input<decl::Float>("B").min(-10000.0f).max(10000.0f).translation_context(
BLT_I18NCONTEXT_ID_NODETREE);
b.add_input<decl::Int>("A", "A_INT").translation_context(BLT_I18NCONTEXT_ID_NODETREE);
b.add_input<decl::Int>("B", "B_INT").translation_context(BLT_I18NCONTEXT_ID_NODETREE);
const bNode *node = b.node_or_null();
if (node != nullptr) {
const NodeFunctionCompare &storage = node_storage(*node);
const NodeCompareOperation operation = NodeCompareOperation(storage.operation);
const eNodeSocketDatatype data_type = eNodeSocketDatatype(storage.data_type);
const NodeCompareMode mode = NodeCompareMode(storage.mode);
b.add_input<decl::Vector>("A", "A_VEC3").translation_context(BLT_I18NCONTEXT_ID_NODETREE);
b.add_input<decl::Vector>("B", "B_VEC3").translation_context(BLT_I18NCONTEXT_ID_NODETREE);
const bool type_is_floating = !ELEM(data_type, SOCK_INT, SOCK_STRING);
const bool is_vector = data_type == SOCK_VECTOR;
b.add_input<decl::Color>("A", "A_COL").translation_context(BLT_I18NCONTEXT_ID_NODETREE);
b.add_input<decl::Color>("B", "B_COL").translation_context(BLT_I18NCONTEXT_ID_NODETREE);
b.add_output(data_type, "A").translation_context(BLT_I18NCONTEXT_ID_NODETREE);
b.add_output(data_type, "B").translation_context(BLT_I18NCONTEXT_ID_NODETREE);
b.add_input<decl::String>("A", "A_STR").translation_context(BLT_I18NCONTEXT_ID_NODETREE);
b.add_input<decl::String>("B", "B_STR").translation_context(BLT_I18NCONTEXT_ID_NODETREE);
if (is_vector && mode == NODE_COMPARE_MODE_DOT_PRODUCT) {
b.add_input<decl::Float>("C").default_value(0.9f);
}
b.add_input<decl::Float>("C").default_value(0.9f);
b.add_input<decl::Float>("Angle").default_value(0.0872665f).subtype(PROP_ANGLE);
b.add_input<decl::Float>("Epsilon").default_value(0.001).min(-10000.0f).max(10000.0f);
if (is_vector && mode == NODE_COMPARE_MODE_DIRECTION) {
b.add_input<decl::Float>("Angle").default_value(0.0872665f).subtype(PROP_ANGLE);
}
if (type_is_floating && ELEM(operation, NODE_COMPARE_EQUAL, NODE_COMPARE_NOT_EQUAL)) {
b.add_input<decl::Float>("Epsilon").default_value(0.001);
}
}
b.add_output<decl::Bool>("Result");
}
@ -62,36 +68,6 @@ static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
uiItemR(layout, ptr, "operation", UI_ITEM_NONE, "", ICON_NONE);
}
static void node_update(bNodeTree *ntree, bNode *node)
{
NodeFunctionCompare *data = (NodeFunctionCompare *)node->storage;
bNodeSocket *sock_comp = (bNodeSocket *)BLI_findlink(&node->inputs, 10);
bNodeSocket *sock_angle = (bNodeSocket *)BLI_findlink(&node->inputs, 11);
bNodeSocket *sock_epsilon = (bNodeSocket *)BLI_findlink(&node->inputs, 12);
LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) {
bke::nodeSetSocketAvailability(
ntree, socket, socket->type == (eNodeSocketDatatype)data->data_type);
}
bke::nodeSetSocketAvailability(
ntree,
sock_epsilon,
ELEM(data->operation, NODE_COMPARE_EQUAL, NODE_COMPARE_NOT_EQUAL) &&
!ELEM(data->data_type, SOCK_INT, SOCK_STRING));
bke::nodeSetSocketAvailability(ntree,
sock_comp,
ELEM(data->mode, NODE_COMPARE_MODE_DOT_PRODUCT) &&
data->data_type == SOCK_VECTOR);
bke::nodeSetSocketAvailability(ntree,
sock_angle,
ELEM(data->mode, NODE_COMPARE_MODE_DIRECTION) &&
data->data_type == SOCK_VECTOR);
}
static void node_init(bNodeTree * /*tree*/, bNode *node)
{
NodeFunctionCompare *data = MEM_cnew<NodeFunctionCompare>(__func__);
@ -736,7 +712,6 @@ static void node_register()
fn_node_type_base(&ntype, FN_NODE_COMPARE, "Compare", NODE_CLASS_CONVERTER);
ntype.declare = node_declare;
ntype.labelfunc = node_label;
ntype.updatefunc = node_update;
ntype.initfunc = node_init;
node_type_storage(
&ntype, "NodeFunctionCompare", node_free_standard_storage, node_copy_standard_storage);

View File

@ -18,30 +18,45 @@ NODE_STORAGE_FUNCS(NodeRandomValue)
static void node_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Vector>("Min").supports_field();
b.add_input<decl::Vector>("Max").default_value({1.0f, 1.0f, 1.0f}).supports_field();
b.add_input<decl::Float>("Min", "Min_001").supports_field();
b.add_input<decl::Float>("Max", "Max_001").default_value(1.0f).supports_field();
b.add_input<decl::Int>("Min", "Min_002").min(-100000).max(100000).supports_field();
b.add_input<decl::Int>("Max", "Max_002")
.default_value(100)
.min(-100000)
.max(100000)
.supports_field();
b.add_input<decl::Float>("Probability")
.min(0.0f)
.max(1.0f)
.default_value(0.5f)
.subtype(PROP_FACTOR)
.supports_field()
.make_available([](bNode &node) { node_storage(node).data_type = CD_PROP_BOOL; });
b.add_input<decl::Int>("ID").implicit_field(implicit_field_inputs::id_or_index);
b.add_input<decl::Int>("Seed").default_value(0).min(-10000).max(10000).supports_field();
const bNode *node = b.node_or_null();
b.add_output<decl::Vector>("Value").dependent_field();
b.add_output<decl::Float>("Value", "Value_001").dependent_field();
b.add_output<decl::Int>("Value", "Value_002").dependent_field();
b.add_output<decl::Bool>("Value", "Value_003").dependent_field();
if (node != nullptr) {
const NodeRandomValue &storage = node_storage(*node);
const eCustomDataType data_type = eCustomDataType(storage.data_type);
switch (data_type) {
case CD_PROP_FLOAT3:
b.add_input<decl::Vector>("Min");
b.add_input<decl::Vector>("Max").default_value({1.0f, 1.0f, 1.0f});
break;
case CD_PROP_FLOAT:
b.add_input<decl::Float>("Min");
b.add_input<decl::Float>("Max").default_value(1.0f);
break;
case CD_PROP_INT32:
b.add_input<decl::Int>("Min");
b.add_input<decl::Int>("Max").default_value(100);
break;
case CD_PROP_BOOL:
b.add_input<decl::Float>("Probability")
.min(0.0f)
.max(1.0f)
.default_value(0.5f)
.subtype(PROP_FACTOR);
break;
default:
BLI_assert_unreachable();
break;
}
}
b.add_input<decl::Int>("ID").implicit_field(implicit_field_inputs::id_or_index);
b.add_input<decl::Int>("Seed");
if (node != nullptr) {
const NodeRandomValue &storage = node_storage(*node);
const eCustomDataType data_type = eCustomDataType(storage.data_type);
b.add_output(data_type, "Value");
}
}
static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
@ -56,38 +71,6 @@ static void fn_node_random_value_init(bNodeTree * /*tree*/, bNode *node)
node->storage = data;
}
static void fn_node_random_value_update(bNodeTree *ntree, bNode *node)
{
const NodeRandomValue &storage = node_storage(*node);
const eCustomDataType data_type = static_cast<eCustomDataType>(storage.data_type);
bNodeSocket *sock_min_vector = (bNodeSocket *)node->inputs.first;
bNodeSocket *sock_max_vector = sock_min_vector->next;
bNodeSocket *sock_min_float = sock_max_vector->next;
bNodeSocket *sock_max_float = sock_min_float->next;
bNodeSocket *sock_min_int = sock_max_float->next;
bNodeSocket *sock_max_int = sock_min_int->next;
bNodeSocket *sock_probability = sock_max_int->next;
bNodeSocket *sock_out_vector = (bNodeSocket *)node->outputs.first;
bNodeSocket *sock_out_float = sock_out_vector->next;
bNodeSocket *sock_out_int = sock_out_float->next;
bNodeSocket *sock_out_bool = sock_out_int->next;
bke::nodeSetSocketAvailability(ntree, sock_min_vector, data_type == CD_PROP_FLOAT3);
bke::nodeSetSocketAvailability(ntree, sock_max_vector, data_type == CD_PROP_FLOAT3);
bke::nodeSetSocketAvailability(ntree, sock_min_float, data_type == CD_PROP_FLOAT);
bke::nodeSetSocketAvailability(ntree, sock_max_float, data_type == CD_PROP_FLOAT);
bke::nodeSetSocketAvailability(ntree, sock_min_int, data_type == CD_PROP_INT32);
bke::nodeSetSocketAvailability(ntree, sock_max_int, data_type == CD_PROP_INT32);
bke::nodeSetSocketAvailability(ntree, sock_probability, data_type == CD_PROP_BOOL);
bke::nodeSetSocketAvailability(ntree, sock_out_vector, data_type == CD_PROP_FLOAT3);
bke::nodeSetSocketAvailability(ntree, sock_out_float, data_type == CD_PROP_FLOAT);
bke::nodeSetSocketAvailability(ntree, sock_out_int, data_type == CD_PROP_INT32);
bke::nodeSetSocketAvailability(ntree, sock_out_bool, data_type == CD_PROP_BOOL);
}
static std::optional<eCustomDataType> node_type_from_other_socket(const bNodeSocket &socket)
{
switch (socket.type) {
@ -202,7 +185,6 @@ static void node_register()
fn_node_type_base(&ntype, FN_NODE_RANDOM_VALUE, "Random Value", NODE_CLASS_CONVERTER);
ntype.initfunc = fn_node_random_value_init;
ntype.updatefunc = fn_node_random_value_update;
ntype.draw_buttons = node_layout;
ntype.declare = node_declare;
ntype.build_multi_function = node_build_multi_function;

View File

@ -19,37 +19,28 @@ namespace blender::nodes::node_fn_rotate_euler_cc {
static void node_declare(NodeDeclarationBuilder &b)
{
auto enable_axis_angle = [](bNode &node) {
node.custom1 = FN_NODE_ROTATE_EULER_TYPE_AXIS_ANGLE;
};
b.is_function_node();
b.add_input<decl::Vector>("Rotation").subtype(PROP_EULER).hide_value();
b.add_input<decl::Vector>("Rotate By").subtype(PROP_EULER).make_available([](bNode &node) {
node.custom1 = FN_NODE_ROTATE_EULER_TYPE_EULER;
});
b.add_input<decl::Vector>("Axis")
.default_value({0.0, 0.0, 1.0})
.subtype(PROP_XYZ)
.make_available(enable_axis_angle);
b.add_input<decl::Float>("Angle").subtype(PROP_ANGLE).make_available(enable_axis_angle);
const bNode *node = b.node_or_null();
if (node != nullptr) {
const auto type = FunctionNodeRotateEulerType(node->custom1);
switch (type) {
case FN_NODE_ROTATE_EULER_TYPE_EULER:
b.add_input<decl::Vector>("Rotate By").subtype(PROP_EULER);
break;
case FN_NODE_ROTATE_EULER_TYPE_AXIS_ANGLE:
b.add_input<decl::Vector>("Axis").default_value({0.0, 0.0, 1.0}).subtype(PROP_XYZ);
b.add_input<decl::Float>("Angle").subtype(PROP_ANGLE);
break;
default:
BLI_assert_unreachable();
break;
}
}
b.add_output<decl::Vector>("Rotation");
}
static void node_update(bNodeTree *ntree, bNode *node)
{
bNodeSocket *rotate_by_socket = static_cast<bNodeSocket *>(BLI_findlink(&node->inputs, 1));
bNodeSocket *axis_socket = static_cast<bNodeSocket *>(BLI_findlink(&node->inputs, 2));
bNodeSocket *angle_socket = static_cast<bNodeSocket *>(BLI_findlink(&node->inputs, 3));
bke::nodeSetSocketAvailability(
ntree, rotate_by_socket, ELEM(node->custom1, FN_NODE_ROTATE_EULER_TYPE_EULER));
bke::nodeSetSocketAvailability(
ntree, axis_socket, ELEM(node->custom1, FN_NODE_ROTATE_EULER_TYPE_AXIS_ANGLE));
bke::nodeSetSocketAvailability(
ntree, angle_socket, ELEM(node->custom1, FN_NODE_ROTATE_EULER_TYPE_AXIS_ANGLE));
}
static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
{
uiItemR(layout, ptr, "type", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
@ -134,9 +125,8 @@ static void node_register()
static bNodeType ntype;
fn_node_type_base(&ntype, FN_NODE_ROTATE_EULER, "Rotate Euler", NODE_CLASS_CONVERTER);
ntype.declare = node_declare;
ntype.draw_buttons = node_layout;
ntype.updatefunc = node_update;
ntype.declare = node_declare;
ntype.build_multi_function = node_build_multi_function;
nodeRegisterType(&ntype);
}

View File

@ -19,23 +19,42 @@ static void node_declare(NodeDeclarationBuilder &b)
{
b.is_function_node();
b.add_input<decl::Color>("Color").default_value({1.0f, 1.0f, 1.0f, 1.0f});
b.add_output<decl::Float>("Red");
b.add_output<decl::Float>("Green");
b.add_output<decl::Float>("Blue");
const bNode *node = b.node_or_null();
if (node != nullptr) {
const NodeCombSepColor &storage = node_storage(*node);
const NodeCombSepColorMode mode = NodeCombSepColorMode(storage.mode);
switch (mode) {
Review

I'm not sure about this kind of refactor yet, because it's something that is not supported by node groups and doesn't work if we expose the mode as menu socket.

I'm not sure about this kind of refactor yet, because it's something that is not supported by node groups and doesn't work if we expose the mode as menu socket.

That is what i tolk about eval-depend topology, we should't do this for such enumerations. Potentially, this node can be splitted in to multiple small atomic nodes at all.
Enums is good for switch and generic-socket type. But all other enums, usually, should be static.

That is what i tolk about `eval-depend topology`, we should't do this for such enumerations. Potentially, this node can be splitted in to multiple small atomic nodes at all. Enums is good for switch and generic-socket type. But all other enums, usually, should be static.
Review

Generally, I agree, but we also want to work towards feature parity between built-in nodes and node groups.
Note that I'm not suggesting to actually expose the enum as socket here, but I also don't want to do this with the declaration for now.

Generally, I agree, but we also want to work towards feature parity between built-in nodes and node groups. Note that I'm not suggesting to actually expose the enum as socket here, but I also don't want to do this with the declaration for now.
case NODE_COMBSEP_COLOR_RGB:
b.add_output<decl::Float>("Red");
b.add_output<decl::Float>("Green");
b.add_output<decl::Float>("Blue");
break;
case NODE_COMBSEP_COLOR_HSL:
b.add_output<decl::Float>("Hue");
b.add_output<decl::Float>("Saturation");
b.add_output<decl::Float>("Lightness");
break;
case NODE_COMBSEP_COLOR_HSV:
b.add_output<decl::Float>("Hue");
b.add_output<decl::Float>("Saturation");
b.add_output<decl::Float>("Value");
break;
default: {
BLI_assert_unreachable();
break;
}
}
}
b.add_output<decl::Float>("Alpha");
};
}
static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
{
uiItemR(layout, ptr, "mode", UI_ITEM_NONE, "", ICON_NONE);
}
static void node_update(bNodeTree * /*tree*/, bNode *node)
{
const NodeCombSepColor &storage = node_storage(*node);
node_combsep_color_label(&node->outputs, (NodeCombSepColorMode)storage.mode);
}
static void node_init(bNodeTree * /*tree*/, bNode *node)
{
NodeCombSepColor *data = MEM_cnew<NodeCombSepColor>(__func__);
@ -217,7 +236,6 @@ static void node_register()
fn_node_type_base(&ntype, FN_NODE_SEPARATE_COLOR, "Separate Color", NODE_CLASS_CONVERTER);
ntype.declare = node_declare;
ntype.updatefunc = node_update;
ntype.initfunc = node_init;
node_type_storage(
&ntype, "NodeCombSepColor", node_free_standard_storage, node_copy_standard_storage);