Geometry Nodes: support fixed pivot axis in Align Rotation to Vector node

When the pivot axis is not set to auto, the node will try to align the rotation
to vector as best as possible, given the selected rotation axis.

Ref T85211.

Differential Revision: https://developer.blender.org/D10292
This commit is contained in:
Jacques Lucke 2021-02-05 16:10:54 +01:00
parent 56903024dc
commit 46e0efb462
Notes: blender-bot 2023-02-14 09:33:11 +01:00
Referenced by issue #85211, Pivot Axis option to the "Align Rotation to Vector" node, for the tree leaves use case
4 changed files with 126 additions and 32 deletions

View File

@ -3336,6 +3336,7 @@ static void node_geometry_buts_align_rotation_to_vector(uiLayout *layout,
PointerRNA *ptr)
{
uiItemR(layout, ptr, "axis", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE);
uiItemR(layout, ptr, "pivot_axis", DEFAULT_FLAGS, IFACE_("Pivot"), ICON_NONE);
uiLayout *col = uiLayoutColumn(layout, false);
uiItemR(col, ptr, "input_type_factor", DEFAULT_FLAGS, IFACE_("Factor"), ICON_NONE);
uiItemR(col, ptr, "input_type_vector", DEFAULT_FLAGS, IFACE_("Vector"), ICON_NONE);

View File

@ -1148,12 +1148,12 @@ typedef struct NodeGeometryRotatePoints {
typedef struct NodeGeometryAlignRotationToVector {
/* GeometryNodeAlignRotationToVectorAxis */
uint8_t axis;
/* GeometryNodeAlignRotationToVectorPivotAxis */
uint8_t pivot_axis;
/* GeometryNodeAttributeInputMode */
uint8_t input_type_factor;
uint8_t input_type_vector;
char _pad[5];
} NodeGeometryAlignRotationToVector;
typedef struct NodeGeometryPointScale {
@ -1660,6 +1660,13 @@ typedef enum GeometryNodeAlignRotationToVectorAxis {
GEO_NODE_ALIGN_ROTATION_TO_VECTOR_AXIS_Z = 2,
} GeometryNodeAlignRotationToVectorAxis;
typedef enum GeometryNodeAlignRotationToVectorPivotAxis {
GEO_NODE_ALIGN_ROTATION_TO_VECTOR_PIVOT_AXIS_AUTO = 0,
GEO_NODE_ALIGN_ROTATION_TO_VECTOR_PIVOT_AXIS_X = 1,
GEO_NODE_ALIGN_ROTATION_TO_VECTOR_PIVOT_AXIS_Y = 2,
GEO_NODE_ALIGN_ROTATION_TO_VECTOR_PIVOT_AXIS_Z = 3,
} GeometryNodeAlignRotationToVectorPivotAxis;
typedef enum GeometryNodeTransformSpace {
GEO_NODE_TRANSFORM_SPACE_ORIGINAL = 0,
GEO_NODE_TRANSFORM_SPACE_RELATIVE = 1,

View File

@ -8793,6 +8793,30 @@ static void def_geo_align_rotation_to_vector(StructRNA *srna)
{0, NULL, 0, NULL, NULL},
};
static const EnumPropertyItem pivot_axis_items[] = {
{GEO_NODE_ALIGN_ROTATION_TO_VECTOR_PIVOT_AXIS_AUTO,
"AUTO",
ICON_NONE,
"Auto",
"Automatically detect the best rotation axis to rotate towards the vector"},
{GEO_NODE_ALIGN_ROTATION_TO_VECTOR_PIVOT_AXIS_X,
"X",
ICON_NONE,
"X",
"Rotate around the local X axis"},
{GEO_NODE_ALIGN_ROTATION_TO_VECTOR_PIVOT_AXIS_Y,
"Y",
ICON_NONE,
"Y",
"Rotate around the local Y axis"},
{GEO_NODE_ALIGN_ROTATION_TO_VECTOR_PIVOT_AXIS_Z,
"Z",
ICON_NONE,
"Z",
"Rotate around the local Z axis"},
{0, NULL, 0, NULL, NULL},
};
PropertyRNA *prop;
RNA_def_struct_sdna_from(srna, "NodeGeometryAlignRotationToVector", "storage");
@ -8802,6 +8826,11 @@ static void def_geo_align_rotation_to_vector(StructRNA *srna)
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_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");
prop = RNA_def_property(srna, "input_type_factor", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_float);
RNA_def_property_ui_text(prop, "Input Type Factor", "");

View File

@ -34,6 +34,84 @@ static bNodeSocketTemplate geo_node_align_rotation_to_vector_out[] = {
namespace blender::nodes {
static void align_rotations_auto_pivot(const Float3ReadAttribute &vectors,
const FloatReadAttribute &factors,
const float3 local_main_axis,
MutableSpan<float3> rotations)
{
for (const int i : IndexRange(vectors.size())) {
const float3 vector = vectors[i];
if (is_zero_v3(vector)) {
continue;
}
float old_rotation[3][3];
eul_to_mat3(old_rotation, rotations[i]);
float3 old_axis;
mul_v3_m3v3(old_axis, old_rotation, local_main_axis);
const float3 new_axis = vector.normalized();
const float3 rotation_axis = float3::cross_high_precision(old_axis, new_axis);
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);
rotations[i] = new_rotation;
}
}
static void align_rotations_fixed_pivot(const Float3ReadAttribute &vectors,
const FloatReadAttribute &factors,
const float3 local_main_axis,
const float3 local_pivot_axis,
MutableSpan<float3> rotations)
{
if (local_main_axis == local_pivot_axis) {
/* Can't compute any meaningful rotation angle in this case. */
return;
}
for (const int i : IndexRange(vectors.size())) {
const float3 vector = vectors[i];
if (is_zero_v3(vector)) {
continue;
}
float old_rotation[3][3];
eul_to_mat3(old_rotation, 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);
rotations[i] = new_rotation;
}
}
static void align_rotations_on_component(GeometryComponent &component,
const GeoNodeExecParams &params)
{
@ -53,36 +131,15 @@ static void align_rotations_on_component(GeometryComponent &component,
Float3ReadAttribute vectors = params.get_input_attribute<float3>(
"Vector", component, ATTR_DOMAIN_POINT, {0, 0, 1});
float3 main_axis{0, 0, 0};
main_axis[storage.axis] = 1;
const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT);
for (const int i : IndexRange(domain_size)) {
const float3 vector = vectors[i];
if (is_zero_v3(vector)) {
continue;
}
float old_rotation[3][3];
eul_to_mat3(old_rotation, rotations[i]);
float3 old_axis;
mul_v3_m3v3(old_axis, old_rotation, main_axis);
const float3 new_axis = vector.normalized();
const float3 rotation_axis = float3::cross_high_precision(old_axis, new_axis);
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);
rotations[i] = new_rotation;
float3 local_main_axis{0, 0, 0};
local_main_axis[storage.axis] = 1;
if (storage.pivot_axis == GEO_NODE_ALIGN_ROTATION_TO_VECTOR_PIVOT_AXIS_AUTO) {
align_rotations_auto_pivot(vectors, factors, local_main_axis, rotations);
}
else {
float3 local_pivot_axis{0, 0, 0};
local_pivot_axis[storage.pivot_axis - 1] = 1;
align_rotations_fixed_pivot(vectors, factors, local_main_axis, local_pivot_axis, rotations);
}
rotation_attribute.apply_span_and_save();