Geometry Nodes: new Axes to Rotation node #104416

Merged
Jacques Lucke merged 32 commits from JacquesLucke/blender:axis-to-euler into main 2024-05-08 13:34:26 +02:00
5 changed files with 194 additions and 0 deletions

View File

@ -595,6 +595,7 @@ class NODE_MT_category_GEO_UTILITIES_ROTATION(Menu):
def draw(self, _context):
layout = self.layout
node_add_menu.add_node_type(layout, "FunctionNodeAlignRotationToVector")
node_add_menu.add_node_type(layout, "FunctionNodeAxesToRotation")
node_add_menu.add_node_type(layout, "FunctionNodeAxisAngleToRotation")
node_add_menu.add_node_type(layout, "FunctionNodeEulerToRotation")
node_add_menu.add_node_type(layout, "FunctionNodeInvertRotation")

View File

@ -1343,6 +1343,7 @@ void BKE_nodetree_remove_layer_n(bNodeTree *ntree, Scene *scene, int layer_index
#define FN_NODE_COMBINE_MATRIX 1241
#define FN_NODE_SEPARATE_MATRIX 1242
#define FN_NODE_INPUT_ROTATION 1243
#define FN_NODE_AXES_TO_ROTATION 1244
/** \} */

View File

@ -265,6 +265,7 @@ DefNode(TextureNode, TEX_NODE_PROC+TEX_DISTNOISE, 0, "TEX_DI
DefNode(FunctionNode, FN_NODE_ALIGN_EULER_TO_VECTOR, 0, "ALIGN_EULER_TO_VECTOR", AlignEulerToVector, "Align Euler to Vector", "")
DefNode(FunctionNode, FN_NODE_ALIGN_ROTATION_TO_VECTOR, 0, "ALIGN_ROTATION_TO_VECTOR", AlignRotationToVector, "Align Rotation to Vector", "")
DefNode(FunctionNode, FN_NODE_AXES_TO_ROTATION, 0, "AXES_TO_ROTATION", AxesToRotation, "Axes to Rotation", "Create a rotation from a primary and (ideally orthogonal) secondary axis")
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", "")

View File

@ -20,6 +20,7 @@ set(INC_SYS
set(SRC
nodes/node_fn_align_euler_to_vector.cc
nodes/node_fn_align_rotation_to_vector.cc
nodes/node_fn_axes_to_rotation.cc
nodes/node_fn_axis_angle_to_rotation.cc
nodes/node_fn_boolean_math.cc
nodes/node_fn_combine_color.cc

View File

@ -0,0 +1,190 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
JacquesLucke marked this conversation as resolved Outdated

Missing copyright info

Missing copyright info
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_math_matrix.hh"
#include "BLI_math_rotation.h"
#include "UI_interface.hh"
#include "UI_resources.hh"
#include "NOD_node_extra_info.hh"
#include "NOD_rna_define.hh"
#include "node_function_util.hh"
namespace blender::nodes::node_fn_axes_to_rotation_cc {
static void node_declare(NodeDeclarationBuilder &b)
{
b.is_function_node();
b.add_input<decl::Vector>(N_("Primary Axis")).default_value(float3(0, 0, 1));
b.add_input<decl::Vector>(N_("Secondary Axis")).default_value(float3(1, 0, 0));
b.add_output<decl::Rotation>(N_("Rotation"));
}
static void node_init(bNodeTree * /*tree*/, bNode *node)
{
node->custom1 = int(math::Axis::Z);
node->custom2 = int(math::Axis::X);
}
static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
{
uiItemR(layout, ptr, "primary_axis", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
mod_moder marked this conversation as resolved Outdated

Now unused.

Now unused.
uiItemR(layout, ptr, "secondary_axis", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
}
static float3 get_orthogonal_of_non_zero_vector(const float3 &v)
JacquesLucke marked this conversation as resolved Outdated

Still think its might be better to display error in node header as usual werning. Currently this message is not displayed on node group body for nested nodes.

Still think its might be better to display error in node header as usual werning. Currently this message is not displayed on node group body for nested nodes.

I moved it to an overlay. Report it as a proper error is a bit tricky right now, but also not super urgent I think. It's not something that can be triggered by group inputs.

I moved it to an overlay. Report it as a proper error is a bit tricky right now, but also not super urgent I think. It's not something that can be triggered by group inputs.
{
BLI_assert(!math::is_zero(v));
if (v.x != -v.y) {
return float3{-v.y, v.x, 0.0f};
}
if (v.x != -v.z) {
return float3(-v.z, 0.0f, v.x);
}
return {0.0f, -v.z, v.y};
}
class AxesToRotationFunction : public mf::MultiFunction {
private:
math::Axis primary_axis_;
math::Axis secondary_axis_;
math::Axis tertiary_axis_;
public:
AxesToRotationFunction(const math::Axis primary_axis, const math::Axis secondary_axis)
: primary_axis_(primary_axis), secondary_axis_(secondary_axis)
{
BLI_assert(primary_axis_ != secondary_axis_);
/* Through cancellation this will set the last axis to be the one that's neither the primary
* nor secondary axis. */
tertiary_axis_ = math::Axis::from_int((0 + 1 + 2) - primary_axis.as_int() -
secondary_axis.as_int());
static const mf::Signature signature = []() {
mf::Signature signature;
mf::SignatureBuilder builder{"Axes to Rotation", signature};
builder.single_input<float3>("Primary");
builder.single_input<float3>("Secondary");
builder.single_output<math::Quaternion>("Rotation");
return signature;
}();
this->set_signature(&signature);
}
void call(const IndexMask &mask, mf::Params params, mf::Context /*context*/) const override
{
const VArray<float3> primaries = params.readonly_single_input<float3>(0, "Primary");
const VArray<float3> secondaries = params.readonly_single_input<float3>(1, "Secondary");
MutableSpan r_rotations = params.uninitialized_single_output<math::Quaternion>(2, "Rotation");
/* Might have to invert the axis to make sure that the created matrix has determinant 1. */
const bool invert_tertiary = (secondary_axis_.as_int() + 1) % 3 == primary_axis_.as_int();
const float tertiary_factor = invert_tertiary ? -1.0f : 1.0f;
mask.foreach_index([&](const int64_t i) {
float3 primary = math::normalize(primaries[i]);
float3 secondary = secondaries[i];
float3 tertiary;
const bool primary_is_non_zero = !math::is_zero(primary);
const bool secondary_is_non_zero = !math::is_zero(secondary);
if (primary_is_non_zero && secondary_is_non_zero) {
tertiary = math::cross(primary, secondary);
if (math::is_zero(tertiary)) {
tertiary = get_orthogonal_of_non_zero_vector(primary);
}
tertiary = math::normalize(tertiary);
secondary = math::cross(tertiary, primary);
}
else if (primary_is_non_zero) {
secondary = get_orthogonal_of_non_zero_vector(primary);
secondary = math::normalize(secondary);
tertiary = math::cross(primary, secondary);
}
else if (secondary_is_non_zero) {
secondary = math::normalize(secondary);
primary = get_orthogonal_of_non_zero_vector(secondary);
primary = math::normalize(primary);
tertiary = math::cross(primary, secondary);
}
else {
r_rotations[i] = math::Quaternion::identity();
return;
}
float3x3 mat;
mat[primary_axis_.as_int()] = primary;
mat[secondary_axis_.as_int()] = secondary;
mat[tertiary_axis_.as_int()] = tertiary_factor * tertiary;
BLI_assert(math::is_orthonormal(mat));
BLI_assert(std::abs(math::determinant(mat) - 1.0f) < 0.0001f);
r_rotations[i] = math::to_quaternion(mat);
});
mod_moder marked this conversation as resolved Outdated

I getting this assertion for just 2 vectors.

I getting this assertion for just 2 vectors.

Can you provide a concrete example where you get this error?

Can you provide a concrete example where you get this error?

If without build, this was something like that\ (probably mesh can be arbirtary)
image

If without build, this was something like that\ (probably mesh can be arbirtary) ![image](/attachments/d4362bf7-95e1-4314-b2dc-08b1c3b33793)
113 KiB

Can't reproduce it right now, it would be really useful if you could provide a .blend file..

Can't reproduce it right now, it would be really useful if you could provide a .blend file..

Also cant reproduce this now\

Also cant reproduce this now\
};
};
static void node_build_multi_function(NodeMultiFunctionBuilder &builder)
{
const bNode &node = builder.node();
if (node.custom1 == node.custom2) {
return;
}
builder.construct_and_set_matching_fn<AxesToRotationFunction>(
math::Axis::from_int(node.custom1), math::Axis::from_int(node.custom2));
}
static void node_extra_info(NodeExtraInfoParams &params)
{
if (params.node.custom1 == params.node.custom2) {
NodeExtraInfoRow row;
row.text = RPT_("Equal Axes");
row.tooltip = TIP_("The primary and secondary axis have to be different");
row.icon = ICON_ERROR;
params.rows.append(std::move(row));
}
}
static void node_rna(StructRNA *srna)
{
static const EnumPropertyItem axis_items[] = {
{int(math::Axis::X), "X", ICON_NONE, "X", ""},
{int(math::Axis::Y), "Y", ICON_NONE, "Y", ""},
{int(math::Axis::Z), "Z", ICON_NONE, "Z", ""},
{0, nullptr, 0, nullptr, nullptr},
};
RNA_def_node_enum(srna,
"primary_axis",
"Primary Axis",
"Axis that is aligned exactly to the provided primary direction",
axis_items,
NOD_inline_enum_accessors(custom1));
RNA_def_node_enum(
srna,
"secondary_axis",
"Secondary Axis",
"Axis that is aligned as well as possible given the alignment of the primary axis",
axis_items,
NOD_inline_enum_accessors(custom2));
}
static void node_register()
{
static bNodeType ntype;
fn_node_type_base(&ntype, FN_NODE_AXES_TO_ROTATION, "Axes to Rotation", NODE_CLASS_CONVERTER);
ntype.declare = node_declare;
ntype.initfunc = node_init;
ntype.build_multi_function = node_build_multi_function;
ntype.draw_buttons = node_layout;
ntype.get_extra_info = node_extra_info;
node_rna(ntype.rna_ext.srna);
nodeRegisterType(&ntype);
}
NOD_REGISTER_NODE(node_register)
} // namespace blender::nodes::node_fn_axes_to_rotation_cc