Changeset View
Changeset View
Standalone View
Standalone View
source/blender/nodes/function/nodes/node_fn_align_euler_to_vector.cc
- This file was added.
| /* | |||||
| * 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_listbase.h" | |||||
| #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}).subtype(PROP_ANGLE); | |||||
| 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 float3 align_euler_auto_pivot(const float3 &input_rotation, | |||||
| const float3 &vector, | |||||
| const float factor, | |||||
| const float3 &local_main_axis) | |||||
| { | |||||
| if (is_zero_v3(vector)) { | |||||
| return input_rotation; | |||||
| } | |||||
| float old_rotation[3][3]; | |||||
| eul_to_mat3(old_rotation, input_rotation); | |||||
| 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 = factor * 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); | |||||
| return new_rotation; | |||||
| } | |||||
| static float3 align_euler_fixed_pivot(const float3 &input_rotation, | |||||
| const float3 &vector, | |||||
| const float factor, | |||||
| const float3 &local_main_axis, | |||||
| const float3 &local_pivot_axis) | |||||
| { | |||||
| if (local_main_axis == local_pivot_axis) { | |||||
| /* Can't compute any meaningful rotation angle in this case. */ | |||||
| return input_rotation; | |||||
| } | |||||
| if (is_zero_v3(vector)) { | |||||
| return input_rotation; | |||||
| } | |||||
| float old_rotation[3][3]; | |||||
| eul_to_mat3(old_rotation, input_rotation); | |||||
| 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 = factor * 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); | |||||
| return new_rotation; | |||||
| } | |||||
| static const fn::MultiFunction *get_multi_function(bNode &bnode) | |||||
| { | |||||
| static float3 local_main_axis{0, 0, 0}; | |||||
| local_main_axis = float3{}; // Reset on reentry | |||||
| local_main_axis[bnode.custom1] = 1; | |||||
| if (bnode.custom2 == FN_NODE_ALIGN_EULER_TO_VECTOR_PIVOT_AXIS_AUTO) { | |||||
| static fn::CustomMF_SI_SI_SI_SO<float3, float, float3, float3> align_euler_auto{ | |||||
| "Align Euler to Vector Auto Pivot", | |||||
| [&](const float3 &input_rotation, const float factor, const float3 &vector) { | |||||
| std::cout << local_main_axis[0] << " " << local_main_axis[1] << " " | |||||
| << local_main_axis[2] << std::endl; | |||||
| return align_euler_auto_pivot(input_rotation, vector, factor, local_main_axis); | |||||
| }}; | |||||
| return &align_euler_auto; | |||||
| } | |||||
| else { | |||||
jarrett.johnson: I don't have a clang-tidy setup yet (hard to figure out on Windows), but I think this going to… | |||||
| static float3 local_pivot_axis{0, 0, 0}; | |||||
| local_pivot_axis = float3{}; // Reset on reentry | |||||
| local_pivot_axis[bnode.custom2 - 1] = 1; | |||||
HooglyBooglyUnsubmitted Done Inline ActionsI don't think this will work, because the same multi-function could be used by multiple threads that use different pivot axes. HooglyBoogly: I don't think this will work, because the same multi-function could be used by multiple threads… | |||||
| static fn::CustomMF_SI_SI_SI_SO<float3, float, float3, float3> align_euler_fixed{ | |||||
| "Align Euler to Vector Fixed Pivot", | |||||
| [&](const float3 &input_rotation, const float factor, const float3 &vector) { | |||||
| return align_euler_fixed_pivot( | |||||
| input_rotation, vector, factor, local_main_axis, local_pivot_axis); | |||||
| }}; | |||||
| return &align_euler_fixed; | |||||
| } | |||||
| } | |||||
| static void fn_node_align_euler_to_vector_build_multi_function(NodeMultiFunctionBuilder &builder) | |||||
| { | |||||
| const fn::MultiFunction *fn = get_multi_function(builder.node()); | |||||
| builder.set_matching_fn(fn); | |||||
| } | |||||
| } // namespace blender::nodes | |||||
Done Inline ActionsLooks like this isn't using the mask, the static functions above should use that to avoid doing unnecessary calculations. HooglyBoogly: Looks like this isn't using the mask, the static functions above should use that to avoid doing… | |||||
| 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); | |||||
| } | |||||
I don't have a clang-tidy setup yet (hard to figure out on Windows), but I think this going to fire an else-after-return warning. I'll fix this and and also add an unreachable assert at the end of this function.