Blender V5.0
node_geo_rotate_instances.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
5#include "BLI_math_matrix.hh"
7#include "BLI_task.hh"
8
9#include "BKE_instances.hh"
10
11#include "node_geometry_util.hh"
12
14
16{
17 b.use_custom_socket_order();
18 b.allow_any_socket_order();
19 b.add_input<decl::Geometry>("Instances")
20 .only_instances()
21 .description("Instances to rotate individually");
22 b.add_output<decl::Geometry>("Instances").propagate_all().align_with_previous();
23 b.add_input<decl::Bool>("Selection").default_value(true).hide_value().field_on_all();
24 b.add_input<decl::Rotation>("Rotation").field_on_all();
25 b.add_input<decl::Vector>("Pivot Point").subtype(PROP_TRANSLATION).field_on_all();
26 b.add_input<decl::Bool>("Local Space").default_value(true).field_on_all();
27}
28
30{
31 using namespace blender::math;
32
33 const bke::InstancesFieldContext context{instances};
34 fn::FieldEvaluator evaluator{context, instances.instances_num()};
35 evaluator.set_selection(params.extract_input<Field<bool>>("Selection"));
36 evaluator.add(params.extract_input<Field<math::Quaternion>>("Rotation"));
37 evaluator.add(params.extract_input<Field<float3>>("Pivot Point"));
38 evaluator.add(params.extract_input<Field<bool>>("Local Space"));
39 evaluator.evaluate();
40
41 const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
42 const VArray<math::Quaternion> rotations = evaluator.get_evaluated<math::Quaternion>(0);
43 const VArray<float3> pivots = evaluator.get_evaluated<float3>(1);
44 const VArray<bool> local_spaces = evaluator.get_evaluated<bool>(2);
45
46 MutableSpan<float4x4> transforms = instances.transforms_for_write();
47
48 selection.foreach_index(GrainSize(512), [&](const int64_t i) {
49 const float3 pivot = pivots[i];
50 const math::Quaternion rotation = rotations[i];
51 float4x4 &instance_transform = transforms[i];
52
53 float4x4 rotation_matrix;
54 float3 used_pivot;
55
56 if (local_spaces[i]) {
57 /* Find rotation axis from the matrix. This should work even if the instance is skewed. */
58 /* Create rotations around the individual axis. This could be optimized to skip some axis
59 * when the angle is zero. */
60 const EulerXYZ euler = math::to_euler(rotation);
61 const float3x3 rotation_x = from_rotation<float3x3>(
62 AxisAngle(normalize(instance_transform.x_axis()), euler.x()));
63 const float3x3 rotation_y = from_rotation<float3x3>(
64 AxisAngle(normalize(instance_transform.y_axis()), euler.y()));
65 const float3x3 rotation_z = from_rotation<float3x3>(
66 AxisAngle(normalize(instance_transform.z_axis()), euler.z()));
67
68 /* Combine the previously computed rotations into the final rotation matrix. */
69 rotation_matrix = float4x4(rotation_z * rotation_y * rotation_x);
70
71 /* Transform the passed in pivot into the local space of the instance. */
72 used_pivot = transform_point(instance_transform, pivot);
73 }
74 else {
75 used_pivot = pivot;
76 rotation_matrix = from_rotation<float4x4>(rotation);
77 }
78 /* Move the pivot to the origin so that we can rotate around it. */
79 instance_transform.location() -= used_pivot;
80 /* Perform the actual rotation. */
81 instance_transform = rotation_matrix * instance_transform;
82 /* Undo the pivot shifting done before. */
83 instance_transform.location() += used_pivot;
84 });
85}
86
88{
89 GeometrySet geometry_set = params.extract_input<GeometrySet>("Instances");
90 if (bke::Instances *instances = geometry_set.get_instances_for_write()) {
91 rotate_instances(params, *instances);
92 }
93 params.set_output("Instances", std::move(geometry_set));
94}
95
96static void node_register()
97{
98 static blender::bke::bNodeType ntype;
99
100 geo_node_type_base(&ntype, "GeometryNodeRotateInstances", GEO_NODE_ROTATE_INSTANCES);
101 ntype.ui_name = "Rotate Instances";
102 ntype.ui_description = "Rotate geometry instances in local or global space";
103 ntype.enum_name_legacy = "ROTATE_INSTANCES";
106 ntype.declare = node_declare;
108}
110
111} // namespace blender::nodes::node_geo_rotate_instances_cc
#define NODE_CLASS_GEOMETRY
Definition BKE_node.hh:461
#define GEO_NODE_ROTATE_INSTANCES
#define NOD_REGISTER_NODE(REGISTER_FUNC)
@ PROP_TRANSLATION
Definition RNA_types.hh:261
long long int int64_t
int instances_num() const
Definition instances.cc:393
MutableSpan< float4x4 > transforms_for_write()
Definition instances.cc:235
void set_selection(Field< bool > selection)
Definition FN_field.hh:383
int add(GField field, GVArray *varray_ptr)
Definition field.cc:751
IndexMask get_evaluated_selection_as_mask() const
Definition field.cc:817
const GVArray & get_evaluated(const int field_index) const
Definition FN_field.hh:448
void foreach_index(Fn &&fn) const
VecBase< float, D > normalize(VecOp< float, D >) RET
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void node_register_type(bNodeType &ntype)
Definition node.cc:2416
QuaternionBase< float > Quaternion
AxisAngleBase< float, AngleRadianBase< float > > AxisAngle
EulerXYZBase< float > EulerXYZ
MatT from_rotation(const RotationT &rotation)
Euler3Base< T > to_euler(const AxisAngleBase< T, AngleT > &axis_angle, EulerOrder order)
static void node_geo_exec(GeoNodeExecParams params)
static void rotate_instances(GeoNodeExecParams &params, bke::Instances &instances)
static void node_declare(NodeDeclarationBuilder &b)
MatBase< float, 4, 4 > float4x4
MatBase< float, 3, 3 > float3x3
VecBase< float, 3 > float3
void geo_node_type_base(blender::bke::bNodeType *ntype, std::string idname, const std::optional< int16_t > legacy_type)
Instances * get_instances_for_write()
Defines a node type.
Definition BKE_node.hh:238
std::string ui_description
Definition BKE_node.hh:244
NodeGeometryExecFunction geometry_node_execute
Definition BKE_node.hh:354
const char * enum_name_legacy
Definition BKE_node.hh:247
NodeDeclareFunction declare
Definition BKE_node.hh:362
i
Definition text_draw.cc:230
ccl_device_inline float3 transform_point(const ccl_private Transform *t, const float3 a)
Definition transform.h:56