Blender V5.0
node_fn_axes_to_rotation.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2024 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "BLI_math_matrix.hh"
6
8#include "UI_resources.hh"
9
11#include "NOD_rna_define.hh"
12
13#include "node_function_util.hh"
14
16
18{
19 b.is_function_node();
20 b.add_input<decl::Vector>(N_("Primary Axis")).default_value(float3(0, 0, 1));
21 b.add_input<decl::Vector>(N_("Secondary Axis")).default_value(float3(1, 0, 0));
22 b.add_output<decl::Rotation>(N_("Rotation"));
23}
24
25static void node_init(bNodeTree * /*tree*/, bNode *node)
26{
27 node->custom1 = int(math::Axis::Z);
28 node->custom2 = int(math::Axis::X);
29}
30
31static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
32{
33 layout->prop(ptr, "primary_axis", UI_ITEM_R_EXPAND, std::nullopt, ICON_NONE);
34 layout->prop(ptr, "secondary_axis", UI_ITEM_R_EXPAND, std::nullopt, ICON_NONE);
35}
36
38{
40 if (v.x != -v.y) {
41 return float3{-v.y, v.x, 0.0f};
42 }
43 if (v.x != -v.z) {
44 return float3(-v.z, 0.0f, v.x);
45 }
46 return {0.0f, -v.z, v.y};
47}
48
49class AxesToRotationFunction : public mf::MultiFunction {
50 private:
51 math::Axis primary_axis_;
52 math::Axis secondary_axis_;
53 math::Axis tertiary_axis_;
54
55 public:
56 AxesToRotationFunction(const math::Axis primary_axis, const math::Axis secondary_axis)
57 : primary_axis_(primary_axis), secondary_axis_(secondary_axis)
58 {
59 BLI_assert(primary_axis_ != secondary_axis_);
60
61 /* Through cancellation this will set the last axis to be the one that's neither the primary
62 * nor secondary axis. */
63 tertiary_axis_ = math::Axis::from_int((0 + 1 + 2) - primary_axis.as_int() -
64 secondary_axis.as_int());
65
66 static const mf::Signature signature = []() {
67 mf::Signature signature;
68 mf::SignatureBuilder builder{"Axes to Rotation", signature};
69 builder.single_input<float3>("Primary");
70 builder.single_input<float3>("Secondary");
71 builder.single_output<math::Quaternion>("Rotation");
72 return signature;
73 }();
74 this->set_signature(&signature);
75 }
76
77 void call(const IndexMask &mask, mf::Params params, mf::Context /*context*/) const override
78 {
79 const VArray<float3> primaries = params.readonly_single_input<float3>(0, "Primary");
80 const VArray<float3> secondaries = params.readonly_single_input<float3>(1, "Secondary");
81 MutableSpan r_rotations = params.uninitialized_single_output<math::Quaternion>(2, "Rotation");
82
83 /* Might have to invert the axis to make sure that the created matrix has determinant 1. */
84 const bool invert_tertiary = (secondary_axis_.as_int() + 1) % 3 == primary_axis_.as_int();
85 const float tertiary_factor = invert_tertiary ? -1.0f : 1.0f;
86
87 mask.foreach_index([&](const int64_t i) {
88 float3 primary = math::normalize(primaries[i]);
89 float3 secondary = secondaries[i];
90 float3 tertiary;
91
92 const bool primary_is_non_zero = !math::is_zero(primary);
93 const bool secondary_is_non_zero = !math::is_zero(secondary);
94 if (primary_is_non_zero && secondary_is_non_zero) {
95 tertiary = math::cross(primary, secondary);
96 if (math::is_zero(tertiary)) {
97 tertiary = get_orthogonal_of_non_zero_vector(primary);
98 }
99 tertiary = math::normalize(tertiary);
100 secondary = math::cross(tertiary, primary);
101 }
102 else if (primary_is_non_zero) {
103 secondary = get_orthogonal_of_non_zero_vector(primary);
104 secondary = math::normalize(secondary);
105 tertiary = math::cross(primary, secondary);
106 }
107 else if (secondary_is_non_zero) {
108 secondary = math::normalize(secondary);
109 primary = get_orthogonal_of_non_zero_vector(secondary);
110 primary = math::normalize(primary);
111 tertiary = math::cross(primary, secondary);
112 }
113 else {
114 r_rotations[i] = math::Quaternion::identity();
115 return;
116 }
117
118 float3x3 mat;
119 mat[primary_axis_.as_int()] = primary;
120 mat[secondary_axis_.as_int()] = secondary;
121 mat[tertiary_axis_.as_int()] = tertiary_factor * tertiary;
123 BLI_assert(std::abs(math::determinant(mat) - 1.0f) < 0.0001f);
124
125 r_rotations[i] = math::to_quaternion(mat);
126 });
127 };
128};
129
131{
132 const bNode &node = builder.node();
133 if (node.custom1 == node.custom2) {
134 return;
135 }
138}
139
141{
142 if (params.node.custom1 == params.node.custom2) {
144 row.text = RPT_("Equal Axes");
145 row.tooltip = TIP_("The primary and secondary axis have to be different");
146 row.icon = ICON_ERROR;
147 params.rows.append(std::move(row));
148 }
149}
150
151static void node_rna(StructRNA *srna)
152{
153 static const EnumPropertyItem axis_items[] = {
154 {int(math::Axis::X), "X", ICON_NONE, "X", ""},
155 {int(math::Axis::Y), "Y", ICON_NONE, "Y", ""},
156 {int(math::Axis::Z), "Z", ICON_NONE, "Z", ""},
157 {0, nullptr, 0, nullptr, nullptr},
158 };
159
161 "primary_axis",
162 "Primary Axis",
163 "Axis that is aligned exactly to the provided primary direction",
164 axis_items,
167 srna,
168 "secondary_axis",
169 "Secondary Axis",
170 "Axis that is aligned as well as possible given the alignment of the primary axis",
171 axis_items,
173}
174
175static void node_register()
176{
177 static blender::bke::bNodeType ntype;
178 fn_node_type_base(&ntype, "FunctionNodeAxesToRotation", FN_NODE_AXES_TO_ROTATION);
179 ntype.ui_name = "Axes to Rotation";
180 ntype.ui_description =
181 "Create a rotation from a primary and (ideally orthogonal) secondary axis";
182 ntype.enum_name_legacy = "AXES_TO_ROTATION";
184 ntype.declare = node_declare;
185 ntype.initfunc = node_init;
189 node_rna(ntype.rna_ext.srna);
191}
193
194} // namespace blender::nodes::node_fn_axes_to_rotation_cc
#define NODE_CLASS_CONVERTER
Definition BKE_node.hh:453
#define FN_NODE_AXES_TO_ROTATION
#define BLI_assert(a)
Definition BLI_assert.h:46
#define RPT_(msgid)
#define TIP_(msgid)
#define NOD_REGISTER_NODE(REGISTER_FUNC)
#define NOD_inline_enum_accessors(member)
@ UI_ITEM_R_EXPAND
ATTR_WARN_UNUSED_RESULT const BMVert * v
long long int int64_t
void set_signature(const Signature *signature)
constexpr int as_int() const
static constexpr Value Z
static constexpr Axis from_int(const int axis_int)
static constexpr Value X
static constexpr Value Y
void call(const IndexMask &mask, mf::Params params, mf::Context) const override
AxesToRotationFunction(const math::Axis primary_axis, const math::Axis secondary_axis)
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
ccl_device_inline float2 mask(const MaskType mask, const float2 a)
void node_register_type(bNodeType &ntype)
Definition node.cc:2416
QuaternionBase< float > Quaternion
QuaternionBase< T > to_quaternion(const AxisAngleBase< T, AngleT > &axis_angle)
bool is_orthonormal(const MatT &mat)
bool is_zero(const T &a)
AxisSigned cross(const AxisSigned a, const AxisSigned b)
MatBase< T, NumCol, NumRow > normalize(const MatBase< T, NumCol, NumRow > &a)
T determinant(const MatBase< T, Size, Size > &mat)
static void node_layout(uiLayout *layout, bContext *, PointerRNA *ptr)
static float3 get_orthogonal_of_non_zero_vector(const float3 &v)
static void node_build_multi_function(NodeMultiFunctionBuilder &builder)
static void node_declare(NodeDeclarationBuilder &b)
static void node_init(bNodeTree *, bNode *node)
static void node_extra_info(NodeExtraInfoParams &params)
PropertyRNA * RNA_def_node_enum(StructRNA *srna, const char *identifier, const char *ui_name, const char *ui_description, const EnumPropertyItem *static_items, const EnumRNAAccessors accessors, std::optional< int > default_value, const EnumPropertyItemFunc item_func, const bool allow_animation)
MatBase< float, 3, 3 > float3x3
VecBase< float, 3 > float3
void fn_node_type_base(blender::bke::bNodeType *ntype, std::string idname, const std::optional< int16_t > legacy_type)
StructRNA * srna
int16_t custom1
int16_t custom2
Defines a node type.
Definition BKE_node.hh:238
std::string ui_description
Definition BKE_node.hh:244
void(* initfunc)(bNodeTree *ntree, bNode *node)
Definition BKE_node.hh:289
NodeMultiFunctionBuildFunction build_multi_function
Definition BKE_node.hh:351
NodeExtraInfoFunction get_extra_info
Definition BKE_node.hh:381
const char * enum_name_legacy
Definition BKE_node.hh:247
void(* draw_buttons)(uiLayout *, bContext *C, PointerRNA *ptr)
Definition BKE_node.hh:259
NodeDeclareFunction declare
Definition BKE_node.hh:362
void prop(PointerRNA *ptr, PropertyRNA *prop, int index, int value, eUI_Item_Flag flag, std::optional< blender::StringRef > name_opt, int icon, std::optional< blender::StringRef > placeholder=std::nullopt)
i
Definition text_draw.cc:230
#define N_(msgid)
PointerRNA * ptr
Definition wm_files.cc:4238