diff options
author | Charlie Jolly <charlie> | 2021-05-11 12:53:13 +0300 |
---|---|---|
committer | Charlie Jolly <mistajolly@gmail.com> | 2021-05-11 13:15:06 +0300 |
commit | 93933ee8bbed20fa36d9968eed55f7f2ecfcbe4e (patch) | |
tree | c654ac599866995bb6676259f3320483a1611504 /source/blender/nodes | |
parent | fc5bf09fd88c331b568c5b485bb0e487179ecb44 (diff) |
Geometry Nodes: Add Attribute Vector Rotate node
Port vector rotate node to geo attributes.
Request by @simonthommes
Reviewed By: simonthommes, HooglyBoogly
Diffstat (limited to 'source/blender/nodes')
-rw-r--r-- | source/blender/nodes/CMakeLists.txt | 1 | ||||
-rw-r--r-- | source/blender/nodes/NOD_geometry.h | 1 | ||||
-rw-r--r-- | source/blender/nodes/NOD_static_types.h | 1 | ||||
-rw-r--r-- | source/blender/nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc | 355 |
4 files changed, 358 insertions, 0 deletions
diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 73645d5eb27..a1d7e6c8522 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -157,6 +157,7 @@ set(SRC geometry/nodes/node_geo_attribute_separate_xyz.cc geometry/nodes/node_geo_attribute_transfer.cc geometry/nodes/node_geo_attribute_vector_math.cc + geometry/nodes/node_geo_attribute_vector_rotate.cc geometry/nodes/node_geo_boolean.cc geometry/nodes/node_geo_bounding_box.cc geometry/nodes/node_geo_collection_info.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index 847e8ac4b00..76c9ab8a502 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -45,6 +45,7 @@ void register_node_type_geo_attribute_randomize(void); void register_node_type_geo_attribute_separate_xyz(void); void register_node_type_geo_attribute_transfer(void); void register_node_type_geo_attribute_vector_math(void); +void register_node_type_geo_attribute_vector_rotate(void); void register_node_type_geo_attribute_remove(void); void register_node_type_geo_boolean(void); void register_node_type_geo_bounding_box(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index d6522e5a616..8b8a0d2912d 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -285,6 +285,7 @@ DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE, def_geo_attribute_sampl DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_SEPARATE_XYZ, def_geo_attribute_separate_xyz, "ATTRIBUTE_SEPARATE_XYZ", AttributeSeparateXYZ, "Attribute Separate XYZ", "") DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_TRANSFER, def_geo_attribute_transfer, "ATTRIBUTE_TRANSFER", AttributeTransfer, "Attribute Transfer", "") DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_VECTOR_MATH, def_geo_attribute_vector_math, "ATTRIBUTE_VECTOR_MATH", AttributeVectorMath, "Attribute Vector Math", "") +DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_VECTOR_ROTATE, def_geo_attribute_vector_rotate, "ATTRIBUTE_VECTOR_ROTATE", AttributeVectorRotate, "Attribute Vector Rotate", "") DefNode(GeometryNode, GEO_NODE_BOOLEAN, def_geo_boolean, "BOOLEAN", Boolean, "Boolean", "") DefNode(GeometryNode, GEO_NODE_BOUNDING_BOX, 0, "BOUNDING_BOX", BoundBox, "Bounding Box", "") DefNode(GeometryNode, GEO_NODE_COLLECTION_INFO, def_geo_collection_info, "COLLECTION_INFO", CollectionInfo, "Collection Info", "") diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc new file mode 100644 index 00000000000..e4a2a8d2e22 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc @@ -0,0 +1,355 @@ +/* + * 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 "node_geometry_util.hh" + +#include "BLI_task.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +static bNodeSocketTemplate geo_node_attribute_vector_rotate_in[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {SOCK_STRING, N_("Vector")}, + {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, + {SOCK_STRING, N_("Center")}, + {SOCK_VECTOR, N_("Center"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_XYZ}, + {SOCK_STRING, N_("Axis")}, + {SOCK_VECTOR, N_("Axis"), 0.0f, 0.0f, 1.0f, 0.0f, -1.0f, 1.0f, PROP_XYZ, PROP_NONE}, + {SOCK_STRING, N_("Angle")}, + {SOCK_FLOAT, N_("Angle"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_ANGLE, PROP_NONE}, + {SOCK_STRING, N_("Rotation")}, + {SOCK_VECTOR, N_("Rotation"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_EULER}, + {SOCK_BOOLEAN, N_("Invert")}, + {SOCK_STRING, N_("Result")}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_attribute_vector_rotate_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +static void geo_node_attribute_vector_rotate_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + bNode *node = (bNode *)ptr->data; + const NodeAttributeVectorRotate &node_storage = *(NodeAttributeVectorRotate *)node->storage; + const GeometryNodeAttributeVectorRotateMode mode = (const GeometryNodeAttributeVectorRotateMode) + node_storage.mode; + + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + uiLayout *column = uiLayoutColumn(layout, false); + + uiItemR(column, ptr, "rotation_mode", 0, "", ICON_NONE); + + uiItemR(column, ptr, "input_type_vector", 0, IFACE_("Vector"), ICON_NONE); + uiItemR(column, ptr, "input_type_center", 0, IFACE_("Center"), ICON_NONE); + if (mode == GEO_NODE_VECTOR_ROTATE_TYPE_AXIS) { + uiItemR(column, ptr, "input_type_axis", 0, IFACE_("Axis"), ICON_NONE); + } + if (mode != GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ) { + uiItemR(column, ptr, "input_type_angle", 0, IFACE_("Angle"), ICON_NONE); + } + if (mode == GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ) { + uiItemR(column, ptr, "input_type_rotation", 0, IFACE_("Rotation"), ICON_NONE); + } +} + +namespace blender::nodes { + +static void geo_node_attribute_vector_rotate_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + const NodeAttributeVectorRotate *node_storage = (NodeAttributeVectorRotate *)node->storage; + const GeometryNodeAttributeVectorRotateMode mode = (const GeometryNodeAttributeVectorRotateMode) + node_storage->mode; + + update_attribute_input_socket_availabilities( + *node, "Vector", (GeometryNodeAttributeInputMode)node_storage->input_type_vector); + update_attribute_input_socket_availabilities( + *node, "Center", (GeometryNodeAttributeInputMode)node_storage->input_type_center); + update_attribute_input_socket_availabilities( + *node, + "Axis", + (GeometryNodeAttributeInputMode)node_storage->input_type_axis, + (mode == GEO_NODE_VECTOR_ROTATE_TYPE_AXIS)); + update_attribute_input_socket_availabilities( + *node, + "Angle", + (GeometryNodeAttributeInputMode)node_storage->input_type_angle, + (mode != GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ)); + update_attribute_input_socket_availabilities( + *node, + "Rotation", + (GeometryNodeAttributeInputMode)node_storage->input_type_rotation, + (mode == GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ)); +} + +static float3 vector_rotate_around_axis(const float3 vector, + const float3 center, + const float3 axis, + const float angle) +{ + float3 result = vector - center; + float mat[3][3]; + axis_angle_to_mat3(mat, axis, angle); + mul_m3_v3(mat, result); + return result + center; +} + +static void geo_node_attribute_vector_rotate_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeAttributeVectorRotate *node_storage = (NodeAttributeVectorRotate *)MEM_callocN( + sizeof(NodeAttributeVectorRotate), __func__); + + node_storage->mode = GEO_NODE_VECTOR_ROTATE_TYPE_AXIS; + node_storage->input_type_vector = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE; + node_storage->input_type_center = GEO_NODE_ATTRIBUTE_INPUT_VECTOR; + node_storage->input_type_axis = GEO_NODE_ATTRIBUTE_INPUT_VECTOR; + node_storage->input_type_angle = GEO_NODE_ATTRIBUTE_INPUT_FLOAT; + node_storage->input_type_rotation = GEO_NODE_ATTRIBUTE_INPUT_VECTOR; + + node->storage = node_storage; +} + +static float3 vector_rotate_euler(const float3 vector, + const float3 center, + const float3 rotation, + const bool invert) +{ + float mat[3][3]; + float3 result = vector - center; + eul_to_mat3(mat, rotation); + if (invert) { + invert_m3(mat); + } + mul_m3_v3(mat, result); + return result + center; +} + +static void do_vector_rotate_around_axis(const VArray<float3> &vector, + const VArray<float3> ¢er, + const VArray<float3> &axis, + const VArray<float> &angle, + MutableSpan<float3> results, + const bool invert) +{ + const int size = vector.size(); + VArray_Span<float3> span_vector{vector}; + VArray_Span<float3> span_center{center}; + VArray_Span<float3> span_axis{axis}; + VArray_Span<float> span_angle{angle}; + + parallel_for(IndexRange(results.size()), 1024, [&](IndexRange range) { + for (const int i : range) { + float angle = (invert) ? -span_angle[i] : span_angle[i]; + results[i] = vector_rotate_around_axis(span_vector[i], span_center[i], span_axis[i], angle); + } + }); +} + +static void do_vector_rotate_around_fixed_axis(const VArray<float3> &vector, + const VArray<float3> ¢er, + const float3 axis, + const VArray<float> &angle, + MutableSpan<float3> results, + const bool invert) +{ + const int size = vector.size(); + VArray_Span<float3> span_vector{vector}; + VArray_Span<float3> span_center{center}; + VArray_Span<float> span_angle{angle}; + + parallel_for(IndexRange(results.size()), 1024, [&](IndexRange range) { + for (const int i : range) { + float angle = (invert) ? -span_angle[i] : span_angle[i]; + results[i] = vector_rotate_around_axis(span_vector[i], span_center[i], axis, angle); + } + }); +} + +static void do_vector_rotate_euler(const VArray<float3> &vector, + const VArray<float3> ¢er, + const VArray<float3> &rotation, + MutableSpan<float3> results, + const bool invert) +{ + const int size = vector.size(); + VArray_Span<float3> span_vector{vector}; + VArray_Span<float3> span_center{center}; + VArray_Span<float3> span_rotation{rotation}; + + parallel_for(IndexRange(results.size()), 1024, [&](IndexRange range) { + for (const int i : range) { + results[i] = vector_rotate_euler(span_vector[i], span_center[i], span_rotation[i], invert); + } + }); +} + +static AttributeDomain get_result_domain(const GeometryComponent &component, + const GeoNodeExecParams ¶ms, + StringRef result_name) +{ + /* Use the domain of the result attribute if it already exists. */ + std::optional<AttributeMetaData> meta_data = component.attribute_get_meta_data(result_name); + if (meta_data) { + return meta_data->domain; + } + + /* Otherwise use the highest priority domain from existing input attributes, or the default. */ + const AttributeDomain default_domain = ATTR_DOMAIN_POINT; + return params.get_highest_priority_input_domain({"Vector", "Center"}, component, default_domain); +} + +static void execute_on_component(const GeoNodeExecParams ¶ms, GeometryComponent &component) +{ + const bNode &node = params.node(); + const NodeAttributeVectorRotate *node_storage = (const NodeAttributeVectorRotate *)node.storage; + const GeometryNodeAttributeVectorRotateMode mode = (GeometryNodeAttributeVectorRotateMode) + node_storage->mode; + const std::string result_name = params.get_input<std::string>("Result"); + const AttributeDomain result_domain = get_result_domain(component, params, result_name); + const bool invert = params.get_input<bool>("Invert"); + + GVArrayPtr attribute_vector = params.get_input_attribute( + "Vector", component, result_domain, CD_PROP_FLOAT3, nullptr); + if (!attribute_vector) { + return; + } + GVArrayPtr attribute_center = params.get_input_attribute( + "Center", component, result_domain, CD_PROP_FLOAT3, nullptr); + if (!attribute_center) { + return; + } + + OutputAttribute attribute_result = component.attribute_try_get_for_output_only( + result_name, result_domain, CD_PROP_FLOAT3); + if (!attribute_result) { + return; + } + + if (mode == GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ) { + GVArrayPtr attribute_rotation = params.get_input_attribute( + "Rotation", component, result_domain, CD_PROP_FLOAT3, nullptr); + if (!attribute_rotation) { + return; + } + do_vector_rotate_euler(attribute_vector->typed<float3>(), + attribute_center->typed<float3>(), + attribute_rotation->typed<float3>(), + attribute_result.as_span<float3>(), + invert); + attribute_result.save(); + return; + } + + GVArrayPtr attribute_angle = params.get_input_attribute( + "Angle", component, result_domain, CD_PROP_FLOAT, nullptr); + if (!attribute_angle) { + return; + } + + switch (mode) { + case GEO_NODE_VECTOR_ROTATE_TYPE_AXIS: { + GVArrayPtr attribute_axis = params.get_input_attribute( + "Axis", component, result_domain, CD_PROP_FLOAT3, nullptr); + if (!attribute_axis) { + return; + } + do_vector_rotate_around_axis(attribute_vector->typed<float3>(), + attribute_center->typed<float3>(), + attribute_axis->typed<float3>(), + attribute_angle->typed<float>(), + attribute_result.as_span<float3>(), + invert); + } break; + case GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_X: + do_vector_rotate_around_fixed_axis(attribute_vector->typed<float3>(), + attribute_center->typed<float3>(), + float3(1.0f, 0.0f, 0.0f), + attribute_angle->typed<float>(), + attribute_result.as_span<float3>(), + invert); + break; + case GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_Y: + do_vector_rotate_around_fixed_axis(attribute_vector->typed<float3>(), + attribute_center->typed<float3>(), + float3(0.0f, 1.0f, 0.0f), + attribute_angle->typed<float>(), + attribute_result.as_span<float3>(), + invert); + + break; + case GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_Z: + do_vector_rotate_around_fixed_axis(attribute_vector->typed<float3>(), + attribute_center->typed<float3>(), + float3(0.0f, 0.0f, 1.0f), + attribute_angle->typed<float>(), + attribute_result.as_span<float3>(), + invert); + + break; + case GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ: + /* Euler is handled before other modes to avoid processing the unavailable angle socket. */ + BLI_assert_unreachable(); + break; + } + attribute_result.save(); +} + +static void geo_node_attribute_vector_rotate_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + + geometry_set = geometry_set_realize_instances(geometry_set); + + if (geometry_set.has<MeshComponent>()) { + execute_on_component(params, geometry_set.get_component_for_write<MeshComponent>()); + } + if (geometry_set.has<PointCloudComponent>()) { + execute_on_component(params, geometry_set.get_component_for_write<PointCloudComponent>()); + } + if (geometry_set.has<CurveComponent>()) { + execute_on_component(params, geometry_set.get_component_for_write<CurveComponent>()); + } + + params.set_output("Geometry", std::move(geometry_set)); +} + +} // namespace blender::nodes + +void register_node_type_geo_attribute_vector_rotate() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, + GEO_NODE_ATTRIBUTE_VECTOR_ROTATE, + "Attribute Vector Rotate", + NODE_CLASS_ATTRIBUTE, + 0); + node_type_socket_templates( + &ntype, geo_node_attribute_vector_rotate_in, geo_node_attribute_vector_rotate_out); + node_type_update(&ntype, blender::nodes::geo_node_attribute_vector_rotate_update); + node_type_init(&ntype, blender::nodes::geo_node_attribute_vector_rotate_init); + node_type_size(&ntype, 165, 100, 600); + node_type_storage( + &ntype, "NodeAttributeVectorRotate", node_free_standard_storage, node_copy_standard_storage); + ntype.geometry_node_execute = blender::nodes::geo_node_attribute_vector_rotate_exec; + ntype.draw_buttons = geo_node_attribute_vector_rotate_layout; + nodeRegisterType(&ntype); +} |