Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJarrett Johnson <jarrett.johnson>2021-10-09 22:40:37 +0300
committerHans Goudey <h.goudey@me.com>2021-10-09 22:40:37 +0300
commit79425ed3267663ee482ee3ffcc542d86888103af (patch)
tree0992d63674d054980e0758ed933780f05c87eaff
parent2561145da8d1e6db6617fa67c5a306d6e07e34e5 (diff)
Geometry Nodes: Align Euler to Vector Node
This commit introduces the Align Euler to Vector function node which rotates to a body into a given direction. The node replaces the legacy "Align Rotation to Vector" node, which only worked on an attribute named `rotation` internally. The "Euler" in the name is meant to make it clearer that the rotation isn't interchangeable with a regular vector. Addresses T91374. Differential Revision: https://developer.blender.org/D12726
-rw-r--r--release/scripts/startup/nodeitems_builtins.py1
-rw-r--r--source/blender/blenkernel/BKE_node.h1
-rw-r--r--source/blender/blenkernel/intern/node.cc1
-rw-r--r--source/blender/makesdna/DNA_node_types.h13
-rw-r--r--source/blender/makesrna/intern/rna_nodetree.c60
-rw-r--r--source/blender/nodes/CMakeLists.txt1
-rw-r--r--source/blender/nodes/NOD_function.h1
-rw-r--r--source/blender/nodes/NOD_static_types.h1
-rw-r--r--source/blender/nodes/function/nodes/node_fn_align_euler_to_vector.cc215
9 files changed, 294 insertions, 0 deletions
diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py
index 923ea22e1e3..155fa59c315 100644
--- a/release/scripts/startup/nodeitems_builtins.py
+++ b/release/scripts/startup/nodeitems_builtins.py
@@ -630,6 +630,7 @@ geometry_node_categories = [
NodeItem("FunctionNodeFloatToInt"),
NodeItem("GeometryNodeSwitch"),
NodeItem("FunctionNodeRandomValue"),
+ NodeItem("FunctionNodeAlignEulerToVector"),
]),
GeometryNodeCategory("GEO_TEXTURE", "Texture", items=[
NodeItem("ShaderNodeTexNoise"),
diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h
index 79ae9d71762..447e0268701 100644
--- a/source/blender/blenkernel/BKE_node.h
+++ b/source/blender/blenkernel/BKE_node.h
@@ -1533,6 +1533,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define FN_NODE_INPUT_SPECIAL_CHARACTERS 1213
#define FN_NODE_RANDOM_VALUE 1214
#define FN_NODE_ROTATE_EULER 1215
+#define FN_NODE_ALIGN_EULER_TO_VECTOR 1216
/** \} */
diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc
index 75b9d07ca98..cad5d4eff41 100644
--- a/source/blender/blenkernel/intern/node.cc
+++ b/source/blender/blenkernel/intern/node.cc
@@ -5818,6 +5818,7 @@ static void registerFunctionNodes()
{
register_node_type_fn_legacy_random_float();
+ register_node_type_fn_align_euler_to_vector();
register_node_type_fn_boolean_math();
register_node_type_fn_float_compare();
register_node_type_fn_float_to_int();
diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h
index edbb0070462..cbfa4e702ea 100644
--- a/source/blender/makesdna/DNA_node_types.h
+++ b/source/blender/makesdna/DNA_node_types.h
@@ -2075,6 +2075,19 @@ typedef enum GeometryNodeAlignRotationToVectorPivotAxis {
GEO_NODE_ALIGN_ROTATION_TO_VECTOR_PIVOT_AXIS_Z = 3,
} GeometryNodeAlignRotationToVectorPivotAxis;
+typedef enum NodeAlignEulerToVectorAxis {
+ FN_NODE_ALIGN_EULER_TO_VECTOR_AXIS_X = 0,
+ FN_NODE_ALIGN_EULER_TO_VECTOR_AXIS_Y = 1,
+ FN_NODE_ALIGN_EULER_TO_VECTOR_AXIS_Z = 2,
+} NodeAlignEulerToVectorAxis;
+
+typedef enum NodeAlignEulerToVectorPivotAxis {
+ FN_NODE_ALIGN_EULER_TO_VECTOR_PIVOT_AXIS_AUTO = 0,
+ FN_NODE_ALIGN_EULER_TO_VECTOR_PIVOT_AXIS_X = 1,
+ FN_NODE_ALIGN_EULER_TO_VECTOR_PIVOT_AXIS_Y = 2,
+ FN_NODE_ALIGN_EULER_TO_VECTOR_PIVOT_AXIS_Z = 3,
+} NodeAlignEulerToVectorPivotAxis;
+
typedef enum GeometryNodeTransformSpace {
GEO_NODE_TRANSFORM_SPACE_ORIGINAL = 0,
GEO_NODE_TRANSFORM_SPACE_RELATIVE = 1,
diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c
index 5e997f81753..cab420ba990 100644
--- a/source/blender/makesrna/intern/rna_nodetree.c
+++ b/source/blender/makesrna/intern/rna_nodetree.c
@@ -9952,6 +9952,66 @@ static void def_geo_align_rotation_to_vector(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
+static void def_fn_align_euler_to_vector(StructRNA *srna)
+{
+ static const EnumPropertyItem axis_items[] = {
+ {FN_NODE_ALIGN_EULER_TO_VECTOR_AXIS_X,
+ "X",
+ ICON_NONE,
+ "X",
+ "Align the X axis with the vector"},
+ {FN_NODE_ALIGN_EULER_TO_VECTOR_AXIS_Y,
+ "Y",
+ ICON_NONE,
+ "Y",
+ "Align the Y axis with the vector"},
+ {FN_NODE_ALIGN_EULER_TO_VECTOR_AXIS_Z,
+ "Z",
+ ICON_NONE,
+ "Z",
+ "Align the Z axis with the vector"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ static const EnumPropertyItem pivot_axis_items[] = {
+ {FN_NODE_ALIGN_EULER_TO_VECTOR_PIVOT_AXIS_AUTO,
+ "AUTO",
+ ICON_NONE,
+ "Auto",
+ "Automatically detect the best rotation axis to rotate towards the vector"},
+ {FN_NODE_ALIGN_EULER_TO_VECTOR_PIVOT_AXIS_X,
+ "X",
+ ICON_NONE,
+ "X",
+ "Rotate around the local X axis"},
+ {FN_NODE_ALIGN_EULER_TO_VECTOR_PIVOT_AXIS_Y,
+ "Y",
+ ICON_NONE,
+ "Y",
+ "Rotate around the local Y axis"},
+ {FN_NODE_ALIGN_EULER_TO_VECTOR_PIVOT_AXIS_Z,
+ "Z",
+ ICON_NONE,
+ "Z",
+ "Rotate around the local Z axis"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ PropertyRNA *prop;
+
+ prop = RNA_def_property(srna, "axis", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "custom1");
+ RNA_def_property_enum_items(prop, axis_items);
+ RNA_def_property_ui_text(prop, "Axis", "Axis to align to the vector");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+
+ prop = RNA_def_property(srna, "pivot_axis", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "custom2");
+ RNA_def_property_enum_items(prop, pivot_axis_items);
+ RNA_def_property_ui_text(prop, "Pivot Axis", "Axis to rotate around");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+}
+
static void def_geo_point_scale(StructRNA *srna)
{
PropertyRNA *prop;
diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt
index a4350c10087..78a9bb72e26 100644
--- a/source/blender/nodes/CMakeLists.txt
+++ b/source/blender/nodes/CMakeLists.txt
@@ -135,6 +135,7 @@ set(SRC
function/nodes/legacy/node_fn_random_float.cc
+ function/nodes/node_fn_align_euler_to_vector.cc
function/nodes/node_fn_boolean_math.cc
function/nodes/node_fn_float_compare.cc
function/nodes/node_fn_float_to_int.cc
diff --git a/source/blender/nodes/NOD_function.h b/source/blender/nodes/NOD_function.h
index 450e999bea4..395a2d68b53 100644
--- a/source/blender/nodes/NOD_function.h
+++ b/source/blender/nodes/NOD_function.h
@@ -22,6 +22,7 @@ extern "C" {
void register_node_type_fn_legacy_random_float(void);
+void register_node_type_fn_align_euler_to_vector(void);
void register_node_type_fn_boolean_math(void);
void register_node_type_fn_float_compare(void);
void register_node_type_fn_float_to_int(void);
diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h
index 7e10b4055fd..80468adf3bc 100644
--- a/source/blender/nodes/NOD_static_types.h
+++ b/source/blender/nodes/NOD_static_types.h
@@ -265,6 +265,7 @@ DefNode(TextureNode, TEX_NODE_PROC+TEX_DISTNOISE, 0, "TEX_DI
DefNode(FunctionNode, FN_NODE_LEGACY_RANDOM_FLOAT, 0, "LEGACY_RANDOM_FLOAT", LegacyRandomFloat, "Random Float", "")
+DefNode(FunctionNode, FN_NODE_ALIGN_EULER_TO_VECTOR, def_fn_align_euler_to_vector, "ALIGN_EULER_TO_VECTOR", AlignEulerToVector, "Align Euler To Vector", "")
DefNode(FunctionNode, FN_NODE_BOOLEAN_MATH, def_boolean_math, "BOOLEAN_MATH", BooleanMath, "Boolean Math", "")
DefNode(FunctionNode, FN_NODE_FLOAT_COMPARE, def_float_compare, "FLOAT_COMPARE", FloatCompare, "Float Compare", "")
DefNode(FunctionNode, FN_NODE_FLOAT_TO_INT, def_float_to_int, "FLOAT_TO_INT", FloatToInt, "Float to Integer", "")
diff --git a/source/blender/nodes/function/nodes/node_fn_align_euler_to_vector.cc b/source/blender/nodes/function/nodes/node_fn_align_euler_to_vector.cc
new file mode 100644
index 00000000000..4c741c96bb8
--- /dev/null
+++ b/source/blender/nodes/function/nodes/node_fn_align_euler_to_vector.cc
@@ -0,0 +1,215 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BLI_task.hh"
+
+#include "RNA_enum_types.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_function_util.hh"
+
+namespace blender::nodes {
+
+static void fn_node_align_euler_to_vector_declare(NodeDeclarationBuilder &b)
+{
+ b.is_function_node();
+ b.add_input<decl::Vector>("Rotation").subtype(PROP_EULER).hide_value();
+ b.add_input<decl::Float>("Factor").default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
+ b.add_input<decl::Vector>("Vector").default_value({0.0, 0.0, 1.0});
+ b.add_output<decl::Vector>("Rotation").subtype(PROP_EULER);
+}
+
+static void fn_node_align_euler_to_vector_layout(uiLayout *layout,
+ bContext *UNUSED(C),
+ PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "axis", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
+ uiItemR(layout, ptr, "pivot_axis", 0, IFACE_("Pivot"), ICON_NONE);
+}
+
+static void align_rotations_auto_pivot(IndexMask mask,
+ const VArray<float3> &input_rotations,
+ const VArray<float3> &vectors,
+ const VArray<float> &factors,
+ const float3 local_main_axis,
+ const MutableSpan<float3> output_rotations)
+{
+ threading::parallel_for(mask.index_range(), 512, [&](IndexRange mask_range) {
+ for (const int maski : mask_range) {
+ const int64_t i = mask[maski];
+ const float3 vector = vectors[i];
+ if (is_zero_v3(vector)) {
+ output_rotations[i] = input_rotations[i];
+ }
+
+ float old_rotation[3][3];
+ eul_to_mat3(old_rotation, input_rotations[i]);
+ float3 old_axis;
+ mul_v3_m3v3(old_axis, old_rotation, local_main_axis);
+
+ const float3 new_axis = vector.normalized();
+ float3 rotation_axis = float3::cross_high_precision(old_axis, new_axis);
+ if (is_zero_v3(rotation_axis)) {
+ /* The vectors are linearly dependent, so we fall back to another axis. */
+ rotation_axis = float3::cross_high_precision(old_axis, float3(1, 0, 0));
+ if (is_zero_v3(rotation_axis)) {
+ /* This is now guaranteed to not be zero. */
+ rotation_axis = float3::cross_high_precision(old_axis, float3(0, 1, 0));
+ }
+ }
+
+ const float full_angle = angle_normalized_v3v3(old_axis, new_axis);
+ const float angle = factors[i] * full_angle;
+
+ float rotation[3][3];
+ axis_angle_to_mat3(rotation, rotation_axis, angle);
+
+ float new_rotation_matrix[3][3];
+ mul_m3_m3m3(new_rotation_matrix, rotation, old_rotation);
+
+ float3 new_rotation;
+ mat3_to_eul(new_rotation, new_rotation_matrix);
+
+ output_rotations[i] = new_rotation;
+ }
+ });
+}
+
+static void align_rotations_fixed_pivot(IndexMask mask,
+ const VArray<float3> &input_rotations,
+ const VArray<float3> &vectors,
+ const VArray<float> &factors,
+ const float3 local_main_axis,
+ const float3 local_pivot_axis,
+ const MutableSpan<float3> output_rotations)
+{
+ threading::parallel_for(mask.index_range(), 512, [&](IndexRange mask_range) {
+ for (const int64_t maski : mask_range) {
+ const int64_t i = mask[maski];
+ if (local_main_axis == local_pivot_axis) {
+ /* Can't compute any meaningful rotation angle in this case. */
+ output_rotations[i] = input_rotations[i];
+ }
+
+ const float3 vector = vectors[i];
+ if (is_zero_v3(vector)) {
+ continue;
+ }
+
+ float old_rotation[3][3];
+ eul_to_mat3(old_rotation, input_rotations[i]);
+ float3 old_axis;
+ mul_v3_m3v3(old_axis, old_rotation, local_main_axis);
+ float3 pivot_axis;
+ mul_v3_m3v3(pivot_axis, old_rotation, local_pivot_axis);
+
+ float full_angle = angle_signed_on_axis_v3v3_v3(vector, old_axis, pivot_axis);
+ if (full_angle > M_PI) {
+ /* Make sure the point is rotated as little as possible. */
+ full_angle -= 2.0f * M_PI;
+ }
+ const float angle = factors[i] * full_angle;
+
+ float rotation[3][3];
+ axis_angle_to_mat3(rotation, pivot_axis, angle);
+
+ float new_rotation_matrix[3][3];
+ mul_m3_m3m3(new_rotation_matrix, rotation, old_rotation);
+
+ float3 new_rotation;
+ mat3_to_eul(new_rotation, new_rotation_matrix);
+
+ output_rotations[i] = new_rotation;
+ }
+ });
+}
+
+class MF_AlignEulerToVector : public fn::MultiFunction {
+ private:
+ int main_axis_mode_;
+ int pivot_axis_mode_;
+
+ public:
+ MF_AlignEulerToVector(int main_axis_mode, int pivot_axis_mode)
+ : main_axis_mode_(main_axis_mode), pivot_axis_mode_(pivot_axis_mode)
+ {
+ static fn::MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ }
+
+ static fn::MFSignature create_signature()
+ {
+ fn::MFSignatureBuilder signature{"Align Euler To Vector"};
+ signature.single_input<float3>("Rotation");
+ signature.single_input<float>("Factor");
+ signature.single_input<float3>("Vector");
+
+ signature.single_output<float3>("Rotation");
+ return signature.build();
+ }
+
+ void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override
+ {
+ const VArray<float3> &input_rotations = params.readonly_single_input<float3>(0, "Rotation");
+ const VArray<float> &factors = params.readonly_single_input<float>(1, "Factor");
+ const VArray<float3> &vectors = params.readonly_single_input<float3>(2, "Vector");
+
+ auto output_rotations = params.uninitialized_single_output<float3>(3, "Rotation");
+
+ float3 local_main_axis = {0.0f, 0.0f, 0.0f};
+ local_main_axis[main_axis_mode_] = 1;
+
+ if (pivot_axis_mode_ == FN_NODE_ALIGN_EULER_TO_VECTOR_PIVOT_AXIS_AUTO) {
+ align_rotations_auto_pivot(
+ mask, input_rotations, vectors, factors, local_main_axis, output_rotations);
+ }
+ else {
+ float3 local_pivot_axis = {0.0f, 0.0f, 0.0f};
+ local_pivot_axis[main_axis_mode_] = 1;
+ align_rotations_fixed_pivot(mask,
+ input_rotations,
+ vectors,
+ factors,
+ local_main_axis,
+ local_pivot_axis,
+ output_rotations);
+ }
+ }
+};
+
+static void fn_node_align_euler_to_vector_build_multi_function(NodeMultiFunctionBuilder &builder)
+{
+ bNode &node = builder.node();
+ builder.construct_and_set_matching_fn<MF_AlignEulerToVector>(node.custom1, node.custom2);
+}
+
+} // namespace blender::nodes
+
+void register_node_type_fn_align_euler_to_vector()
+{
+ static bNodeType ntype;
+
+ fn_node_type_base(
+ &ntype, FN_NODE_ALIGN_EULER_TO_VECTOR, "Align Euler to Vector", NODE_CLASS_CONVERTER, 0);
+ ntype.declare = blender::nodes::fn_node_align_euler_to_vector_declare;
+ ntype.draw_buttons = blender::nodes::fn_node_align_euler_to_vector_layout;
+ ntype.build_multi_function = blender::nodes::fn_node_align_euler_to_vector_build_multi_function;
+ nodeRegisterType(&ntype);
+}