Blender V5.0
node_fn_align_rotation_to_vector.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
7#include "BLI_math_vector.h"
8#include "BLI_math_vector.hh"
9
11#include "UI_resources.hh"
12
13#include "NOD_rna_define.hh"
14
15#include "node_function_util.hh"
16
18
20{
21 b.use_custom_socket_order();
22 b.allow_any_socket_order();
23 b.add_default_layout();
24 b.is_function_node();
25 b.add_input<decl::Rotation>("Rotation").hide_value();
26 b.add_output<decl::Rotation>("Rotation").align_with_previous();
27 b.add_input<decl::Float>("Factor").default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
28 b.add_input<decl::Vector>("Vector").default_value({0.0, 0.0, 1.0}).subtype(PROP_XYZ);
29}
30
31static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
32{
33 layout->prop(ptr, "axis", UI_ITEM_R_EXPAND, std::nullopt, ICON_NONE);
34 layout->use_property_split_set(true);
35 layout->use_property_decorate_set(false);
36 layout->prop(ptr, "pivot_axis", UI_ITEM_NONE, IFACE_("Pivot"), ICON_NONE);
37}
38
39static void node_init(bNodeTree * /*tree*/, bNode *node)
40{
41 node->custom1 = int16_t(math::Axis::Z);
42}
43
45 const VArray<math::Quaternion> &input_rotations,
46 const VArray<float3> &vectors,
47 const VArray<float> &factors,
48 const float3 local_main_axis,
49 MutableSpan<math::Quaternion> output_rotations)
50{
51 mask.foreach_index([&](const int64_t i) {
52 const math::Quaternion old_rotation = input_rotations[i];
53 const float3 vector = vectors[i];
54 if (math::is_zero(vector)) {
55 output_rotations[i] = old_rotation;
56 return;
57 }
58
59 const float3 old_axis = math::transform_point(old_rotation, local_main_axis);
60
61 const float3 new_axis = math::normalize(vector);
62 float3 rotation_axis = math::cross_high_precision(old_axis, new_axis);
63 if (math::is_zero(rotation_axis)) {
64 /* The vectors are linearly dependent, so we fall back to another axis. */
65 rotation_axis = math::cross_high_precision(old_axis, float3(1, 0, 0));
66 if (math::is_zero(rotation_axis)) {
67 /* This is now guaranteed to not be zero. */
68 rotation_axis = math::cross_high_precision(old_axis, float3(0, 1, 0));
69 }
70 }
71
72 const float full_angle = angle_normalized_v3v3(old_axis, new_axis);
73 const float angle = factors[i] * full_angle;
74
75 const math::AxisAngle axis_angle = math::AxisAngle(math::normalize(rotation_axis), angle);
76 output_rotations[i] = math::to_quaternion(axis_angle) * old_rotation;
77 });
78}
79
81 const VArray<math::Quaternion> &input_rotations,
82 const VArray<float3> &vectors,
83 const VArray<float> &factors,
84 const float3 local_main_axis,
85 const float3 local_pivot_axis,
86 MutableSpan<math::Quaternion> output_rotations)
87{
88 mask.foreach_index([&](const int64_t i) {
89 const math::Quaternion old_rotation = input_rotations[i];
90 if (local_main_axis == local_pivot_axis) {
91 /* Can't compute any meaningful rotation angle in this case. */
92 output_rotations[i] = old_rotation;
93 return;
94 }
95
96 const float3 vector = vectors[i];
97 if (math::is_zero(vector)) {
98 output_rotations[i] = old_rotation;
99 return;
100 }
101
102 const float3 old_axis = math::transform_point(old_rotation, local_main_axis);
103 const float3 pivot_axis = math::transform_point(old_rotation, local_pivot_axis);
104
105 float full_angle = angle_signed_on_axis_v3v3_v3(vector, old_axis, pivot_axis);
106 if (full_angle > M_PI) {
107 /* Make sure the point is rotated as little as possible. */
108 full_angle -= 2.0f * M_PI;
109 }
110 const float angle = factors[i] * full_angle;
111
112 const math::AxisAngle axis_angle = math::AxisAngle(math::normalize(pivot_axis), angle);
113 output_rotations[i] = math::to_quaternion(axis_angle) * old_rotation;
114 });
115}
116
117class AlignRotationToVectorFunction : public mf::MultiFunction {
118 math::Axis main_axis_mode_;
119 NodeAlignEulerToVectorPivotAxis pivot_axis_mode_;
120
121 public:
123 const NodeAlignEulerToVectorPivotAxis pivot_axis_mode)
124 : main_axis_mode_(main_axis_mode), pivot_axis_mode_(pivot_axis_mode)
125 {
126 static const mf::Signature signature = []() {
127 mf::Signature signature;
128 mf::SignatureBuilder builder{"Align Rotation to Vector", signature};
129 builder.single_input<math::Quaternion>("Rotation");
130 builder.single_input<float>("Factor");
131 builder.single_input<float3>("Vector");
132 builder.single_output<math::Quaternion>("Rotation");
133 return signature;
134 }();
135 this->set_signature(&signature);
136 }
137
138 void call(const IndexMask &mask, mf::Params params, mf::Context /*context*/) const final
139 {
140 const VArray input_rotations = params.readonly_single_input<math::Quaternion>(0, "Rotation");
141 const VArray<float> factors = params.readonly_single_input<float>(1, "Factor");
142 const VArray<float3> vectors = params.readonly_single_input<float3>(2, "Vector");
143
144 MutableSpan output_rotations = params.uninitialized_single_output<math::Quaternion>(
145 3, "Rotation");
146
147 float3 local_main_axis = {0.0f, 0.0f, 0.0f};
148 local_main_axis[main_axis_mode_.as_int()] = 1.0f;
149
150 if (pivot_axis_mode_ == FN_NODE_ALIGN_EULER_TO_VECTOR_PIVOT_AXIS_AUTO) {
152 mask, input_rotations, vectors, factors, local_main_axis, output_rotations);
153 }
154 else {
155 float3 local_pivot_axis = {0.0f, 0.0f, 0.0f};
156 local_pivot_axis[pivot_axis_mode_ - 1] = 1;
158 input_rotations,
159 vectors,
160 factors,
161 local_main_axis,
162 local_pivot_axis,
163 output_rotations);
164 }
165 }
166
168 {
169 ExecutionHints hints;
170 hints.min_grain_size = 512;
171 return hints;
172 }
173};
174
181
182static void node_rna(StructRNA *srna)
183{
184 static const EnumPropertyItem axis_items[] = {
185 {int(math::Axis::X), "X", ICON_NONE, "X", "Align the X axis with the vector"},
186 {int(math::Axis::Y), "Y", ICON_NONE, "Y", "Align the Y axis with the vector"},
187 {int(math::Axis::Z), "Z", ICON_NONE, "Z", "Align the Z axis with the vector"},
188 {0, nullptr, 0, nullptr, nullptr},
189 };
190
192 "axis",
193 "Axis",
194 "Axis to align to the vector",
195 axis_items,
197
198 static const EnumPropertyItem pivot_axis_items[] = {
200 "AUTO",
201 ICON_NONE,
202 "Auto",
203 "Automatically detect the best rotation axis to rotate towards the vector"},
205 "X",
206 ICON_NONE,
207 "X",
208 "Rotate around the local X axis"},
210 "Y",
211 ICON_NONE,
212 "Y",
213 "Rotate around the local Y axis"},
215 "Z",
216 ICON_NONE,
217 "Z",
218 "Rotate around the local Z axis"},
219 {0, nullptr, 0, nullptr, nullptr},
220 };
221
223 "pivot_axis",
224 "Pivot Axis",
225 "Axis to rotate around",
226 pivot_axis_items,
228}
229
230static void node_register()
231{
232 static blender::bke::bNodeType ntype;
233
234 fn_node_type_base(&ntype, "FunctionNodeAlignRotationToVector", FN_NODE_ALIGN_ROTATION_TO_VECTOR);
235 ntype.ui_name = "Align Rotation to Vector";
236 ntype.ui_description = "Orient a rotation along the given direction";
237 ntype.enum_name_legacy = "ALIGN_ROTATION_TO_VECTOR";
239 ntype.declare = node_declare;
240 ntype.initfunc = node_init;
244
245 node_rna(ntype.rna_ext.srna);
246}
248
249} // namespace blender::nodes::node_fn_align_rotation_to_vector_cc
#define NODE_CLASS_CONVERTER
Definition BKE_node.hh:453
#define FN_NODE_ALIGN_ROTATION_TO_VECTOR
#define final(a, b, c)
Definition BLI_hash.h:19
#define M_PI
float angle_signed_on_axis_v3v3_v3(const float v1[3], const float v2[3], const float axis[3]) ATTR_WARN_UNUSED_RESULT
float angle_normalized_v3v3(const float v1[3], const float v2[3]) ATTR_WARN_UNUSED_RESULT
#define IFACE_(msgid)
NodeAlignEulerToVectorPivotAxis
@ FN_NODE_ALIGN_EULER_TO_VECTOR_PIVOT_AXIS_Y
@ FN_NODE_ALIGN_EULER_TO_VECTOR_PIVOT_AXIS_AUTO
@ FN_NODE_ALIGN_EULER_TO_VECTOR_PIVOT_AXIS_X
@ FN_NODE_ALIGN_EULER_TO_VECTOR_PIVOT_AXIS_Z
static double angle(const Eigen::Vector3d &v1, const Eigen::Vector3d &v2)
Definition IK_Math.h:117
#define NOD_REGISTER_NODE(REGISTER_FUNC)
#define NOD_inline_enum_accessors(member)
@ PROP_XYZ
Definition RNA_types.hh:269
@ PROP_FACTOR
Definition RNA_types.hh:251
@ UI_ITEM_R_EXPAND
#define UI_ITEM_NONE
long long int int64_t
void set_signature(const Signature *signature)
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 final
AlignRotationToVectorFunction(const math::Axis main_axis_mode, const NodeAlignEulerToVectorPivotAxis pivot_axis_mode)
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)
VecBase< float, 3 > cross_high_precision(const VecBase< float, 3 > &a, const VecBase< float, 3 > &b)
AxisAngleBase< float, AngleRadianBase< float > > AxisAngle
bool is_zero(const T &a)
MatBase< T, NumCol, NumRow > normalize(const MatBase< T, NumCol, NumRow > &a)
VecBase< T, 3 > transform_point(const CartesianBasis &basis, const VecBase< T, 3 > &v)
static void align_rotations_auto_pivot(const IndexMask &mask, const VArray< math::Quaternion > &input_rotations, const VArray< float3 > &vectors, const VArray< float > &factors, const float3 local_main_axis, MutableSpan< math::Quaternion > output_rotations)
static void node_layout(uiLayout *layout, bContext *, PointerRNA *ptr)
static void align_rotations_fixed_pivot(const IndexMask &mask, const VArray< math::Quaternion > &input_rotations, const VArray< float3 > &vectors, const VArray< float > &factors, const float3 local_main_axis, const float3 local_pivot_axis, MutableSpan< math::Quaternion > output_rotations)
static void node_build_multi_function(NodeMultiFunctionBuilder &builder)
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)
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
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 use_property_decorate_set(bool is_sep)
void use_property_split_set(bool value)
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
PointerRNA * ptr
Definition wm_files.cc:4238