Geometry Nodes: Mix Rotations

Add support a new Rotation socket/data type to a Mix node.

Rotation socket avala ible only in Geometry Nodes right now,
list of supported types for node depend on own node tree type.

Mixing kind is slerp (interpolation) of a quaternion. As initial
phase this is enough. In a future, other modes of interpolation
can be added (Euler for XY+Z, YZ+X, ZX+Y, ...). Clamping for
factor work the same as for all other data types.

Drag and drop should take care avoiding create links between
Rotation sockets and all other socket types, this requires chages
is mix node callback for drag & drop system.

See: https://projects.blender.org/blender/blender/issues/92967

Pull Request: https://projects.blender.org/blender/blender/pulls/109084
This commit is contained in:
Iliya Katueshenock 2023-06-27 00:50:53 +02:00 committed by Hans Goudey
parent bef20cd3f1
commit 3957a1ad03
2 changed files with 95 additions and 27 deletions

@ -10,6 +10,7 @@
#include <stdlib.h>
#include <string.h>
#include "BLI_function_ref.hh"
#include "BLI_math.h"
#include "BLI_utildefines.h"
@ -513,6 +514,14 @@ static const EnumPropertyItem rna_node_combsep_color_items[] = {
{0, nullptr, 0, nullptr, nullptr},
};
static const EnumPropertyItem rna_enum_mix_data_type_items[] = {
{SOCK_FLOAT, "FLOAT", 0, "Float", ""},
{SOCK_VECTOR, "VECTOR", 0, "Vector", ""},
{SOCK_RGBA, "RGBA", 0, "Color", ""},
{SOCK_ROTATION, "ROTATION", 0, "Rotation", ""},
{0, nullptr, 0, nullptr, nullptr},
};
#ifndef RNA_RUNTIME
static const EnumPropertyItem node_sampler_type_items[] = {
{0, "NEAREST", 0, "Nearest", ""},
@ -2062,7 +2071,7 @@ static StructRNA *rna_Node_register(Main *bmain,
static const EnumPropertyItem *itemf_function_check(
const EnumPropertyItem *original_item_array,
bool (*value_supported)(const EnumPropertyItem *item))
blender::FunctionRef<bool(const EnumPropertyItem *item)> value_supported)
{
EnumPropertyItem *item_array = nullptr;
int items_len = 0;
@ -3793,6 +3802,27 @@ static const EnumPropertyItem *renderresult_layers_add_enum(RenderLayer *rl)
return item;
}
static const EnumPropertyItem *rna_ShaderNodeMix_data_type_itemf(bContext * /*C*/,
PointerRNA *ptr,
PropertyRNA * /*prop*/,
bool *r_free)
{
*r_free = true;
const auto rotation_supported_mix = [&](const EnumPropertyItem *item) -> bool {
const eNodeSocketDatatype data_type = eNodeSocketDatatype(item->value);
if (U.experimental.use_rotation_socket && data_type == SOCK_ROTATION) {
const bNodeTree *tree = reinterpret_cast<const bNodeTree *>(ptr->owner_id);
if (tree->type == NTREE_GEOMETRY) {
return true;
}
}
return ELEM(data_type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA);
};
return itemf_function_check(rna_enum_mix_data_type_items, rotation_supported_mix);
}
static const EnumPropertyItem *rna_Node_image_layer_itemf(bContext * /*C*/,
PointerRNA *ptr,
PropertyRNA * /*prop*/,
@ -5206,13 +5236,6 @@ static void def_compare(StructRNA *srna)
static void def_sh_mix(StructRNA *srna)
{
static const EnumPropertyItem rna_enum_mix_data_type_items[] = {
{SOCK_FLOAT, "FLOAT", 0, "Float", ""},
{SOCK_VECTOR, "VECTOR", 0, "Vector", ""},
{SOCK_RGBA, "RGBA", 0, "Color", ""},
{0, nullptr, 0, nullptr, nullptr},
};
static const EnumPropertyItem rna_enum_mix_mode_items[] = {
{NODE_MIX_MODE_UNIFORM, "UNIFORM", 0, "Uniform", "Use a single factor for all components"},
{NODE_MIX_MODE_NON_UNIFORM, "NON_UNIFORM", 0, "Non-Uniform", "Per component factor"},
@ -5224,6 +5247,7 @@ static void def_sh_mix(StructRNA *srna)
RNA_def_struct_sdna_from(srna, "NodeShaderMix", "storage");
prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_funcs(prop, nullptr, nullptr, "rna_ShaderNodeMix_data_type_itemf");
RNA_def_property_enum_items(prop, rna_enum_mix_data_type_items);
RNA_def_property_enum_default(prop, SOCK_FLOAT);
RNA_def_property_ui_text(prop, "Data Type", "");

@ -8,6 +8,8 @@
#include <algorithm>
#include "BLI_math_quaternion.hh"
#include "UI_interface.h"
#include "UI_resources.h"
@ -62,26 +64,37 @@ static void sh_node_mix_declare(NodeDeclarationBuilder &b)
.default_value({0.5f, 0.5f, 0.5f, 1.0f})
.translation_context(BLT_I18NCONTEXT_ID_NODETREE);
b.add_input<decl::Rotation>("A", "A_Rotation")
.is_default_link_socket()
.translation_context(BLT_I18NCONTEXT_ID_NODETREE);
b.add_input<decl::Rotation>("B", "B_Rotation").translation_context(BLT_I18NCONTEXT_ID_NODETREE);
b.add_output<decl::Float>("Result", "Result_Float");
b.add_output<decl::Vector>("Result", "Result_Vector");
b.add_output<decl::Color>("Result", "Result_Color");
b.add_output<decl::Rotation>("Result", "Result_Rotation");
};
static void sh_node_mix_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
{
const NodeShaderMix &data = node_storage(*static_cast<const bNode *>(ptr->data));
uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE);
if (data.data_type == SOCK_VECTOR) {
uiItemR(layout, ptr, "factor_mode", 0, "", ICON_NONE);
}
if (data.data_type == SOCK_RGBA) {
uiItemR(layout, ptr, "blend_type", 0, "", ICON_NONE);
uiItemR(layout, ptr, "clamp_result", 0, nullptr, ICON_NONE);
uiItemR(layout, ptr, "clamp_factor", 0, nullptr, ICON_NONE);
}
else {
uiItemR(layout, ptr, "clamp_factor", 0, nullptr, ICON_NONE);
switch (data.data_type) {
case SOCK_FLOAT:
break;
case SOCK_VECTOR:
uiItemR(layout, ptr, "factor_mode", 0, "", ICON_NONE);
break;
case SOCK_RGBA:
uiItemR(layout, ptr, "blend_type", 0, "", ICON_NONE);
uiItemR(layout, ptr, "clamp_result", 0, nullptr, ICON_NONE);
break;
case SOCK_ROTATION:
break;
default:
BLI_assert_unreachable();
}
uiItemR(layout, ptr, "clamp_factor", 0, nullptr, ICON_NONE);
}
static void sh_node_mix_label(const bNodeTree * /*ntree*/,
@ -167,6 +180,9 @@ static void node_mix_gather_link_searches(GatherLinkSearchOpParams &params)
case SOCK_RGBA:
type = SOCK_RGBA;
break;
case SOCK_ROTATION:
type = SOCK_ROTATION;
break;
default:
return;
}
@ -210,15 +226,21 @@ static void node_mix_gather_link_searches(GatherLinkSearchOpParams &params)
weight);
weight--;
}
params.add_item(
IFACE_("Factor"),
[type](LinkSearchOpParams &params) {
bNode &node = params.add_node("ShaderNodeMix");
node_storage(node).data_type = type;
params.update_and_connect_available_socket(node, "Factor");
},
weight);
weight--;
if (type != SOCK_ROTATION) {
params.add_item(
IFACE_("Factor"),
[type](LinkSearchOpParams &params) {
bNode &node = params.add_node("ShaderNodeMix");
node_storage(node).data_type = type;
params.update_and_connect_available_socket(node, "Factor");
},
weight);
weight--;
}
}
if (type == SOCK_ROTATION) {
return;
}
if (type != SOCK_RGBA) {
@ -309,6 +331,8 @@ static const char *gpu_shader_get_name(eNodeSocketDatatype data_type,
BLI_assert_unreachable();
return nullptr;
}
case SOCK_ROTATION:
return nullptr;
default:
BLI_assert_unreachable();
return nullptr;
@ -480,6 +504,26 @@ static const mf::MultiFunction *get_multi_function(const bNode &node)
}
}
}
case SOCK_ROTATION: {
if (clamp_factor) {
static auto fn =
mf::build::SI3_SO<float, math::Quaternion, math::Quaternion, math::Quaternion>(
"Clamp Mix Rotation",
[](const float t, const math::Quaternion &a, const math::Quaternion &b) {
return math::interpolate(a, b, math::clamp(t, 0.0f, 1.0f));
});
return &fn;
}
else {
static auto fn =
mf::build::SI3_SO<float, math::Quaternion, math::Quaternion, math::Quaternion>(
"Mix Rotation",
[](const float t, const math::Quaternion &a, const math::Quaternion &b) {
return math::interpolate(a, b, t);
});
return &fn;
}
}
}
BLI_assert_unreachable();
return nullptr;