Geometry Nodes: Initial basic rotation socket nodes

This PR adds an initial set of nodes using the new rotation socket.
6 nodes build rotations or convert them to other formats, a 7th rotates
a vector with a rotation, and the last inverts rotations.

The design task #109965 describes the choice to use separate nodes
for the rotation construction and separation operations. In the future,
a "Switch Node" operator (#111438) will help to make working with
these separated nodes faster.

- **Axis Angle to Rotation**
- **Rotation to Axis Angle**
- **Combine Quaternion**
- **Separate Quaternion**
- **Euler to Rotation**
- **Rotation to Euler**
- **Rotate Vector**
- **Invert Rotation**

See #92967

Pull Request: https://projects.blender.org/blender/blender/pulls/109030
This commit is contained in:
Hans Goudey 2023-08-24 14:58:55 +02:00 committed by Hans Goudey
parent 81eb7edc8b
commit 45d8a8b0c3
12 changed files with 396 additions and 0 deletions

View File

@ -564,7 +564,15 @@ class NODE_MT_category_GEO_UTILITIES_ROTATION(Menu):
def draw(self, _context):
layout = self.layout
node_add_menu.add_node_type(layout, "FunctionNodeAlignEulerToVector")
node_add_menu.add_node_type(layout, "FunctionNodeAxisAngleToRotation")
node_add_menu.add_node_type(layout, "FunctionNodeEulerToRotation")
node_add_menu.add_node_type(layout, "FunctionNodeInvertRotation")
node_add_menu.add_node_type(layout, "FunctionNodeRotateEuler")
node_add_menu.add_node_type(layout, "FunctionNodeRotateVector")
node_add_menu.add_node_type(layout, "FunctionNodeRotationToAxisAngle")
node_add_menu.add_node_type(layout, "FunctionNodeRotationToEuler")
node_add_menu.add_node_type(layout, "FunctionNodeRotationToQuaternion")
node_add_menu.add_node_type(layout, "FunctionNodeQuaternionToRotation")
node_add_menu.draw_assets_for_catalog(layout, self.bl_label)

View File

@ -1348,6 +1348,15 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i
#define FN_NODE_INPUT_INT 1220
#define FN_NODE_SEPARATE_COLOR 1221
#define FN_NODE_COMBINE_COLOR 1222
#define FN_NODE_AXIS_ANGLE_TO_ROTATION 1223
#define FN_NODE_EULER_TO_ROTATION 1224
#define FN_NODE_QUATERNION_TO_ROTATION 1225
#define FN_NODE_ROTATION_TO_AXIS_ANGLE 1226
#define FN_NODE_ROTATION_TO_EULER 1227
#define FN_NODE_ROTATION_TO_QUATERNION 1228
#define FN_NODE_ROTATE_VECTOR 1229
#define FN_NODE_ROTATE_ROTATION 1230
#define FN_NODE_INVERT_ROTATION 1231
/** \} */

View File

@ -264,9 +264,12 @@ DefNode(TextureNode, TEX_NODE_PROC+TEX_STUCCI, 0, "TEX_ST
DefNode(TextureNode, TEX_NODE_PROC+TEX_DISTNOISE, 0, "TEX_DISTNOISE", TexDistNoise, "Distorted Noise", "" )
DefNode(FunctionNode, FN_NODE_ALIGN_EULER_TO_VECTOR, 0, "ALIGN_EULER_TO_VECTOR", AlignEulerToVector, "Align Euler to Vector", "")
DefNode(FunctionNode, FN_NODE_AXIS_ANGLE_TO_ROTATION, 0, "AXIS_ANGLE_TO_ROTATION", AxisAngleToRotation, "Axis Angle to Rotation", "")
DefNode(FunctionNode, FN_NODE_BOOLEAN_MATH, 0, "BOOLEAN_MATH", BooleanMath, "Boolean Math", "")
DefNode(FunctionNode, FN_NODE_COMBINE_COLOR, 0, "COMBINE_COLOR", CombineColor, "Combine Color", "")
DefNode(FunctionNode, FN_NODE_QUATERNION_TO_ROTATION, 0, "QUATERNION_TO_ROTATION", QuaternionToRotation, "Quaternion to Rotation", "")
DefNode(FunctionNode, FN_NODE_COMPARE, 0, "COMPARE", Compare, "Compare", "")
DefNode(FunctionNode, FN_NODE_EULER_TO_ROTATION, 0, "EULER_TO_ROTATION", EulerToRotation, "Euler to Rotation", "")
DefNode(FunctionNode, FN_NODE_FLOAT_TO_INT, def_float_to_int, "FLOAT_TO_INT", FloatToInt, "Float to Integer", "")
DefNode(FunctionNode, FN_NODE_INPUT_BOOL, def_fn_input_bool, "INPUT_BOOL", InputBool, "Boolean", "")
DefNode(FunctionNode, FN_NODE_INPUT_COLOR, def_fn_input_color, "INPUT_COLOR", InputColor, "Color", "")
@ -274,10 +277,15 @@ DefNode(FunctionNode, FN_NODE_INPUT_INT, def_fn_input_int, "INPUT_INT", InputInt
DefNode(FunctionNode, FN_NODE_INPUT_SPECIAL_CHARACTERS, 0, "INPUT_SPECIAL_CHARACTERS", InputSpecialCharacters, "Special Characters", "")
DefNode(FunctionNode, FN_NODE_INPUT_STRING, def_fn_input_string, "INPUT_STRING", InputString, "String", "")
DefNode(FunctionNode, FN_NODE_INPUT_VECTOR, def_fn_input_vector, "INPUT_VECTOR", InputVector, "Vector", "")
DefNode(FunctionNode, FN_NODE_INVERT_ROTATION, 0, "INVERT_ROTATION", InvertRotation, "Invert Rotation", "")
DefNode(FunctionNode, FN_NODE_RANDOM_VALUE, def_fn_random_value, "RANDOM_VALUE", RandomValue, "Random Value", "")
DefNode(FunctionNode, FN_NODE_REPLACE_STRING, 0, "REPLACE_STRING", ReplaceString, "Replace String", "")
DefNode(FunctionNode, FN_NODE_ROTATE_EULER, def_fn_rotate_euler, "ROTATE_EULER", RotateEuler, "Rotate Euler", "")
DefNode(FunctionNode, FN_NODE_ROTATE_VECTOR, 0, "ROTATE_VECTOR", RotateVector, "Rotate Vector", "")
DefNode(FunctionNode, FN_NODE_ROTATION_TO_AXIS_ANGLE, 0, "ROTATION_TO_AXIS_ANGLE", RotationToAxisAngle, "Rotation to Axis Angle", "")
DefNode(FunctionNode, FN_NODE_ROTATION_TO_EULER, 0, "ROTATION_TO_EULER", RotationToEuler, "Rotation to Euler", "")
DefNode(FunctionNode, FN_NODE_SEPARATE_COLOR, 0, "SEPARATE_COLOR", SeparateColor, "Separate Color", "")
DefNode(FunctionNode, FN_NODE_ROTATION_TO_QUATERNION, 0, "ROTATION_TO_QUATERNION", RotationToQuaternion, "Rotation to Quaternion", "")
DefNode(FunctionNode, FN_NODE_SLICE_STRING, 0, "SLICE_STRING", SliceString, "Slice String", "")
DefNode(FunctionNode, FN_NODE_STRING_LENGTH, 0, "STRING_LENGTH", StringLength, "String Length", "")
DefNode(FunctionNode, FN_NODE_VALUE_TO_STRING, 0, "VALUE_TO_STRING", ValueToString, "Value to String", "")

View File

@ -19,9 +19,11 @@ set(INC_SYS
set(SRC
nodes/node_fn_align_euler_to_vector.cc
nodes/node_fn_axis_angle_to_rotation.cc
nodes/node_fn_boolean_math.cc
nodes/node_fn_combine_color.cc
nodes/node_fn_compare.cc
nodes/node_fn_euler_to_rotation.cc
nodes/node_fn_float_to_int.cc
nodes/node_fn_input_bool.cc
nodes/node_fn_input_color.cc
@ -29,9 +31,15 @@ set(SRC
nodes/node_fn_input_special_characters.cc
nodes/node_fn_input_string.cc
nodes/node_fn_input_vector.cc
nodes/node_fn_invert_rotation.cc
nodes/node_fn_quaternion_to_rotation.cc
nodes/node_fn_random_value.cc
nodes/node_fn_replace_string.cc
nodes/node_fn_rotate_euler.cc
nodes/node_fn_rotate_vector.cc
nodes/node_fn_rotation_to_axis_angle.cc
nodes/node_fn_rotation_to_euler.cc
nodes/node_fn_rotation_to_quaternion.cc
nodes/node_fn_separate_color.cc
nodes/node_fn_slice_string.cc
nodes/node_fn_string_length.cc

View File

@ -0,0 +1,45 @@
/* SPDX-FileCopyrightText: 2023 Blender Foundation
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_math_axis_angle.hh"
#include "BLI_math_quaternion.hh"
#include "node_function_util.hh"
namespace blender::nodes::node_fn_axis_angle_to_rotation_cc {
static void node_declare(NodeDeclarationBuilder &b)
{
b.is_function_node();
b.add_input<decl::Vector>("Axis").default_value({0.0f, 0.0f, 1.0f});
b.add_input<decl::Float>("Angle").subtype(PROP_ANGLE);
b.add_output<decl::Rotation>("Rotation");
};
static void node_build_multi_function(NodeMultiFunctionBuilder &builder)
{
static auto fn = mf::build::SI2_SO<float3, float, math::Quaternion>(
"Axis Angle to Quaternion", [](float3 axis, float angle) {
if (UNLIKELY(math::is_zero(axis))) {
return math::Quaternion::identity();
}
const float3 axis_normalized = math::normalize(axis);
const math::AxisAngle axis_angle = math::AxisAngle(axis_normalized, angle);
return math::to_quaternion(axis_angle);
});
builder.set_matching_fn(fn);
}
static void node_register()
{
static bNodeType ntype;
fn_node_type_base(
&ntype, FN_NODE_AXIS_ANGLE_TO_ROTATION, "Axis Angle to Rotation", NODE_CLASS_CONVERTER);
ntype.declare = node_declare;
ntype.build_multi_function = node_build_multi_function;
nodeRegisterType(&ntype);
}
NOD_REGISTER_NODE(node_register)
} // namespace blender::nodes::node_fn_axis_angle_to_rotation_cc

View File

@ -0,0 +1,36 @@
/* SPDX-FileCopyrightText: 2023 Blender Foundation
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_math_euler.hh"
#include "node_function_util.hh"
namespace blender::nodes::node_fn_euler_to_rotation_cc {
static void node_declare(NodeDeclarationBuilder &b)
{
b.is_function_node();
b.add_input<decl::Vector>("Euler").subtype(PROP_EULER);
b.add_output<decl::Rotation>("Rotation");
};
static void node_build_multi_function(NodeMultiFunctionBuilder &builder)
{
static auto fn = mf::build::SI1_SO<float3, math::Quaternion>(
"Euler XYZ to Quaternion",
[](float3 euler) { return math::to_quaternion(math::EulerXYZ(euler)); });
builder.set_matching_fn(fn);
}
static void node_register()
{
static bNodeType ntype;
fn_node_type_base(&ntype, FN_NODE_EULER_TO_ROTATION, "Euler to Rotation", NODE_CLASS_CONVERTER);
ntype.declare = node_declare;
ntype.build_multi_function = node_build_multi_function;
nodeRegisterType(&ntype);
}
NOD_REGISTER_NODE(node_register)
} // namespace blender::nodes::node_fn_euler_to_rotation_cc

View File

@ -0,0 +1,35 @@
/* SPDX-FileCopyrightText: 2023 Blender Foundation
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_math_quaternion.hh"
#include "node_function_util.hh"
namespace blender::nodes::node_fn_invert_rotation_cc {
static void node_declare(NodeDeclarationBuilder &b)
{
b.is_function_node();
b.add_input<decl::Rotation>("Rotation");
b.add_output<decl::Rotation>("Rotation");
};
static void node_build_multi_function(NodeMultiFunctionBuilder &builder)
{
static auto fn = mf::build::SI1_SO<math::Quaternion, math::Quaternion>(
"Invert Quaternion", [](math::Quaternion quat) { return math::invert(quat); });
builder.set_matching_fn(fn);
}
static void node_register()
{
static bNodeType ntype;
fn_node_type_base(&ntype, FN_NODE_INVERT_ROTATION, "Invert Rotation", NODE_CLASS_CONVERTER);
ntype.declare = node_declare;
ntype.build_multi_function = node_build_multi_function;
nodeRegisterType(&ntype);
}
NOD_REGISTER_NODE(node_register)
} // namespace blender::nodes::node_fn_invert_rotation_cc

View File

@ -0,0 +1,42 @@
/* SPDX-FileCopyrightText: 2023 Blender Foundation
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_math_quaternion.hh"
#include "node_function_util.hh"
namespace blender::nodes::node_fn_quaternion_to_rotation_cc {
static void node_declare(NodeDeclarationBuilder &b)
{
b.is_function_node();
b.add_input<decl::Float>("W").default_value(1.0f);
b.add_input<decl::Float>("X").default_value(0.0f);
b.add_input<decl::Float>("Y").default_value(0.0f);
b.add_input<decl::Float>("Z").default_value(0.0f);
b.add_output<decl::Rotation>("Rotation");
};
static void node_build_multi_function(NodeMultiFunctionBuilder &builder)
{
static auto fn = mf::build::SI4_SO<float, float, float, float, math::Quaternion>(
"Quaternion to Rotation", [](float w, float x, float y, float z) {
math::Quaternion combined(w, x, y, z);
return math::normalize(combined);
});
builder.set_matching_fn(fn);
}
static void node_register()
{
static bNodeType ntype;
fn_node_type_base(
&ntype, FN_NODE_QUATERNION_TO_ROTATION, "Quaternion to Rotation", NODE_CLASS_CONVERTER);
ntype.declare = node_declare;
ntype.build_multi_function = node_build_multi_function;
nodeRegisterType(&ntype);
}
NOD_REGISTER_NODE(node_register)
} // namespace blender::nodes::node_fn_quaternion_to_rotation_cc

View File

@ -0,0 +1,37 @@
/* SPDX-FileCopyrightText: 2023 Blender Foundation
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_math_quaternion.hh"
#include "node_function_util.hh"
namespace blender::nodes::node_fn_rotate_vector_cc {
static void node_declare(NodeDeclarationBuilder &b)
{
b.is_function_node();
b.add_input<decl::Vector>("Vector");
b.add_input<decl::Rotation>("Rotation");
b.add_output<decl::Vector>("Vector");
};
static void node_build_multi_function(NodeMultiFunctionBuilder &builder)
{
static auto fn = mf::build::SI2_SO<float3, math::Quaternion, float3>(
"Rotate Vector",
[](float3 vector, math::Quaternion quat) { return math::transform_point(quat, vector); });
builder.set_matching_fn(fn);
}
static void node_register()
{
static bNodeType ntype;
fn_node_type_base(&ntype, FN_NODE_ROTATE_VECTOR, "Rotate Vector", NODE_CLASS_CONVERTER);
ntype.declare = node_declare;
ntype.build_multi_function = node_build_multi_function;
nodeRegisterType(&ntype);
}
NOD_REGISTER_NODE(node_register)
} // namespace blender::nodes::node_fn_rotate_vector_cc

View File

@ -0,0 +1,63 @@
/* SPDX-FileCopyrightText: 2023 Blender Foundation
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_math_axis_angle.hh"
#include "BLI_math_quaternion.hh"
#include "node_function_util.hh"
namespace blender::nodes::node_fn_rotation_to_axis_angle_cc {
static void node_declare(NodeDeclarationBuilder &b)
{
b.is_function_node();
b.add_input<decl::Rotation>("Rotation");
b.add_output<decl::Vector>("Axis");
b.add_output<decl::Float>("Angle").subtype(PROP_ANGLE);
};
class QuaterniontoAxisAngleFunction : public mf::MultiFunction {
public:
QuaterniontoAxisAngleFunction()
{
static mf::Signature signature_;
mf::SignatureBuilder builder{"Quaternion to Axis Angle", signature_};
builder.single_input<math::Quaternion>("Quaternion");
builder.single_output<float3>("Axis");
builder.single_output<float>("Angle");
this->set_signature(&signature_);
}
void call(const IndexMask &mask, mf::Params params, mf::Context /*context*/) const override
{
const VArraySpan<math::Quaternion> quaternions =
params.readonly_single_input<math::Quaternion>(0, "Quaternion");
MutableSpan<float3> axes = params.uninitialized_single_output<float3>(1, "Axis");
MutableSpan<float> angles = params.uninitialized_single_output<float>(2, "Angle");
mask.foreach_index([&](const int64_t i) {
const math::AxisAngle axis_angle = math::to_axis_angle(quaternions[i]);
axes[i] = axis_angle.axis();
angles[i] = axis_angle.angle().radian();
});
}
};
static void node_build_multi_function(NodeMultiFunctionBuilder &builder)
{
static QuaterniontoAxisAngleFunction fn;
builder.set_matching_fn(fn);
}
static void node_register()
{
static bNodeType ntype;
fn_node_type_base(
&ntype, FN_NODE_ROTATION_TO_AXIS_ANGLE, "Rotation to Axis Angle", NODE_CLASS_CONVERTER);
ntype.declare = node_declare;
ntype.build_multi_function = node_build_multi_function;
nodeRegisterType(&ntype);
}
NOD_REGISTER_NODE(node_register)
} // namespace blender::nodes::node_fn_rotation_to_axis_angle_cc

View File

@ -0,0 +1,35 @@
/* SPDX-FileCopyrightText: 2023 Blender Foundation
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_math_euler.hh"
#include "node_function_util.hh"
namespace blender::nodes::node_fn_rotation_to_euler_cc {
static void node_declare(NodeDeclarationBuilder &b)
{
b.is_function_node();
b.add_input<decl::Rotation>("Rotation");
b.add_output<decl::Vector>("Euler").subtype(PROP_EULER);
};
static void node_build_multi_function(NodeMultiFunctionBuilder &builder)
{
static auto fn = mf::build::SI1_SO<math::Quaternion, float3>(
"Quaternion to Euler XYZ", [](math::Quaternion quat) { return math::to_euler(quat); });
builder.set_matching_fn(fn);
}
static void node_register()
{
static bNodeType ntype;
fn_node_type_base(&ntype, FN_NODE_ROTATION_TO_EULER, "Rotation to Euler", NODE_CLASS_CONVERTER);
ntype.declare = node_declare;
ntype.build_multi_function = node_build_multi_function;
nodeRegisterType(&ntype);
}
NOD_REGISTER_NODE(node_register)
} // namespace blender::nodes::node_fn_rotation_to_euler_cc

View File

@ -0,0 +1,70 @@
/* SPDX-FileCopyrightText: 2023 Blender Foundation
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_math_quaternion_types.hh"
#include "node_function_util.hh"
namespace blender::nodes::node_fn_rotation_to_quaternion_cc {
static void node_declare(NodeDeclarationBuilder &b)
{
b.is_function_node();
b.add_input<decl::Rotation>("Rotation");
b.add_output<decl::Float>("W");
b.add_output<decl::Float>("X");
b.add_output<decl::Float>("Y");
b.add_output<decl::Float>("Z");
};
class SeparateQuaternionFunction : public mf::MultiFunction {
public:
SeparateQuaternionFunction()
{
static mf::Signature signature_;
mf::SignatureBuilder builder{"Rotation to Quaternion", signature_};
builder.single_input<math::Quaternion>("Quaternion");
builder.single_output<float>("W");
builder.single_output<float>("X");
builder.single_output<float>("Y");
builder.single_output<float>("Z");
this->set_signature(&signature_);
}
void call(const IndexMask &mask, mf::Params params, mf::Context /*context*/) const override
{
const VArraySpan<math::Quaternion> quats = params.readonly_single_input<math::Quaternion>(
0, "Quaternion");
MutableSpan<float> w = params.uninitialized_single_output<float>(1, "W");
MutableSpan<float> x = params.uninitialized_single_output<float>(2, "X");
MutableSpan<float> y = params.uninitialized_single_output<float>(3, "Y");
MutableSpan<float> z = params.uninitialized_single_output<float>(4, "Z");
mask.foreach_index([&](const int64_t i) {
const math::Quaternion quat = quats[i];
w[i] = quat.w;
x[i] = quat.x;
y[i] = quat.y;
z[i] = quat.z;
});
}
};
static void node_build_multi_function(NodeMultiFunctionBuilder &builder)
{
static SeparateQuaternionFunction fn;
builder.set_matching_fn(fn);
}
static void node_register()
{
static bNodeType ntype;
fn_node_type_base(
&ntype, FN_NODE_ROTATION_TO_QUATERNION, "Rotation to Quaternion", NODE_CLASS_CONVERTER);
ntype.declare = node_declare;
ntype.build_multi_function = node_build_multi_function;
nodeRegisterType(&ntype);
}
NOD_REGISTER_NODE(node_register)
} // namespace blender::nodes::node_fn_rotation_to_quaternion_cc