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:
Diffstat (limited to 'source/blender/nodes/geometry')
-rw-r--r--source/blender/nodes/geometry/CMakeLists.txt290
-rw-r--r--source/blender/nodes/geometry/node_geometry_tree.cc16
-rw-r--r--source/blender/nodes/geometry/node_geometry_util.cc47
-rw-r--r--source/blender/nodes/geometry/node_geometry_util.hh70
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_align_rotation_to_vector.cc (renamed from source/blender/nodes/geometry/nodes/legacy/node_geo_align_rotation_to_vector.cc)65
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_clamp.cc (renamed from source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_clamp.cc)87
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_color_ramp.cc (renamed from source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_color_ramp.cc)46
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_combine_xyz.cc (renamed from source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_combine_xyz.cc)66
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_compare.cc (renamed from source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_compare.cc)103
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_convert.cc (renamed from source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_convert.cc)45
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_curve_map.cc (renamed from source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_curve_map.cc)72
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_fill.cc (renamed from source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_fill.cc)56
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_map_range.cc (renamed from source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_map_range.cc)90
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_math.cc (renamed from source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_math.cc)67
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_mix.cc (renamed from source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_mix.cc)87
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_proximity.cc (renamed from source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_proximity.cc)48
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_randomize.cc (renamed from source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_randomize.cc)131
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_sample_texture.cc (renamed from source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_sample_texture.cc)31
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_separate_xyz.cc (renamed from source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_separate_xyz.cc)56
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_transfer.cc (renamed from source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_transfer.cc)68
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_vector_math.cc (renamed from source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_vector_math.cc)126
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_vector_rotate.cc (renamed from source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_vector_rotate.cc)115
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_endpoints.cc (renamed from source/blender/nodes/geometry/nodes/legacy/node_geo_curve_endpoints.cc)44
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_reverse.cc (renamed from source/blender/nodes/geometry/nodes/legacy/node_geo_curve_reverse.cc)27
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_select_by_handle_type.cc (renamed from source/blender/nodes/geometry/nodes/legacy/node_geo_curve_select_by_handle_type.cc)44
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_set_handles.cc (renamed from source/blender/nodes/geometry/nodes/legacy/node_geo_curve_set_handles.cc)41
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_spline_type.cc (renamed from source/blender/nodes/geometry/nodes/legacy/node_geo_curve_spline_type.cc)41
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_subdivide.cc (renamed from source/blender/nodes/geometry/nodes/legacy/node_geo_curve_subdivide.cc)64
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_to_points.cc (renamed from source/blender/nodes/geometry/nodes/legacy/node_geo_curve_to_points.cc)181
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_delete_geometry.cc (renamed from source/blender/nodes/geometry/nodes/legacy/node_geo_delete_geometry.cc)125
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_edge_split.cc (renamed from source/blender/nodes/geometry/nodes/legacy/node_geo_edge_split.cc)30
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_material_assign.cc (renamed from source/blender/nodes/geometry/nodes/legacy/node_geo_material_assign.cc)28
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_mesh_to_curve.cc80
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_distribute.cc (renamed from source/blender/nodes/geometry/nodes/legacy/node_geo_point_distribute.cc)94
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_instance.cc (renamed from source/blender/nodes/geometry/nodes/legacy/node_geo_point_instance.cc)76
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_rotate.cc (renamed from source/blender/nodes/geometry/nodes/legacy/node_geo_point_rotate.cc)60
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_scale.cc (renamed from source/blender/nodes/geometry/nodes/legacy/node_geo_point_scale.cc)51
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_separate.cc (renamed from source/blender/nodes/geometry/nodes/legacy/node_geo_point_separate.cc)40
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_translate.cc (renamed from source/blender/nodes/geometry/nodes/legacy/node_geo_point_translate.cc)47
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_points_to_volume.cc (renamed from source/blender/nodes/geometry/nodes/legacy/node_geo_points_to_volume.cc)70
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_raycast.cc (renamed from source/blender/nodes/geometry/nodes/legacy/node_geo_raycast.cc)84
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_select_by_material.cc (renamed from source/blender/nodes/geometry/nodes/legacy/node_geo_select_by_material.cc)26
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_subdivision_surface.cc (renamed from source/blender/nodes/geometry/nodes/legacy/node_geo_subdivision_surface.cc)41
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_volume_to_mesh.cc176
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_mesh_to_curve.cc314
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc430
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc149
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc154
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc27
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc253
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_boolean.cc63
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc178
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_collection_info.cc87
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_common.cc11
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc42
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc147
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc37
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc105
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc169
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_length.cc22
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_parameter.cc206
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_primitive_arc.cc391
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc116
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc127
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc80
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadratic_bezier.cc61
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc179
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc74
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc84
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc157
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc24
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc91
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc149
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc326
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc434
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc366
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc62
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc414
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc376
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc1399
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc191
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc931
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_edge_split.cc97
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc1365
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc193
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc114
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_geometry_to_instance.cc55
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_image_texture.cc428
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc128
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_curve_tilt.cc44
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_id.cc44
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_index.cc18
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_material.cc22
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_material_index.cc44
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_angle.cc222
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_neighbors.cc93
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_vertices.cc186
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_area.cc96
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_neighbors.cc158
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc157
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_mesh_vertex_neighbors.cc155
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_normal.cc273
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_position.cc18
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_radius.cc44
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_scene_time.cc50
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_shade_smooth.cc44
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_spline_cyclic.cc44
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc163
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_spline_resolution.cc45
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc62
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc201
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc137
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_is_viewport.cc18
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc356
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_material_assign.cc97
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_material_replace.cc25
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_material_selection.cc84
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc108
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc66
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc1099
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc104
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc138
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc194
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc37
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc178
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc60
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc30
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc71
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc70
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_object_info.cc106
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc30
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc284
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_proximity.cc76
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_raycast.cc462
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_realize_instances.cc36
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc119
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_scale_elements.cc485
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc98
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_separate_components.cc28
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_separate_geometry.cc117
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc181
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc82
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc78
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_id.cc94
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_material.cc134
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc77
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc83
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_position.cc133
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc77
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc78
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc95
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_string_join.cc24
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc99
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc166
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_switch.cc326
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc842
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_transform.cc226
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc84
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_triangulate.cc95
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_viewer.cc131
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc197
161 files changed, 19335 insertions, 4921 deletions
diff --git a/source/blender/nodes/geometry/CMakeLists.txt b/source/blender/nodes/geometry/CMakeLists.txt
new file mode 100644
index 00000000000..b4add633b0c
--- /dev/null
+++ b/source/blender/nodes/geometry/CMakeLists.txt
@@ -0,0 +1,290 @@
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+# 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.
+#
+# ***** END GPL LICENSE BLOCK *****
+
+set(INC
+ .
+ ..
+ ../intern
+ ../../editors/include
+ ../../blenkernel
+ ../../blenlib
+ ../../blentranslation
+ ../../bmesh
+ ../../depsgraph
+ ../../functions
+ ../../geometry
+ ../../gpu
+ ../../imbuf
+ ../../makesdna
+ ../../makesrna
+ ../../render
+ ../../windowmanager
+ ../../../../intern/guardedalloc
+)
+
+
+set(SRC
+ nodes/legacy/node_geo_legacy_align_rotation_to_vector.cc
+ nodes/legacy/node_geo_legacy_attribute_clamp.cc
+ nodes/legacy/node_geo_legacy_attribute_color_ramp.cc
+ nodes/legacy/node_geo_legacy_attribute_combine_xyz.cc
+ nodes/legacy/node_geo_legacy_attribute_compare.cc
+ nodes/legacy/node_geo_legacy_attribute_convert.cc
+ nodes/legacy/node_geo_legacy_attribute_curve_map.cc
+ nodes/legacy/node_geo_legacy_attribute_fill.cc
+ nodes/legacy/node_geo_legacy_attribute_map_range.cc
+ nodes/legacy/node_geo_legacy_attribute_math.cc
+ nodes/legacy/node_geo_legacy_attribute_mix.cc
+ nodes/legacy/node_geo_legacy_attribute_proximity.cc
+ nodes/legacy/node_geo_legacy_attribute_randomize.cc
+ nodes/legacy/node_geo_legacy_attribute_sample_texture.cc
+ nodes/legacy/node_geo_legacy_attribute_separate_xyz.cc
+ nodes/legacy/node_geo_legacy_attribute_transfer.cc
+ nodes/legacy/node_geo_legacy_attribute_vector_math.cc
+ nodes/legacy/node_geo_legacy_attribute_vector_rotate.cc
+ nodes/legacy/node_geo_legacy_curve_endpoints.cc
+ nodes/legacy/node_geo_legacy_curve_reverse.cc
+ nodes/legacy/node_geo_legacy_curve_select_by_handle_type.cc
+ nodes/legacy/node_geo_legacy_curve_set_handles.cc
+ nodes/legacy/node_geo_legacy_curve_spline_type.cc
+ nodes/legacy/node_geo_legacy_curve_subdivide.cc
+ nodes/legacy/node_geo_legacy_curve_to_points.cc
+ nodes/legacy/node_geo_legacy_delete_geometry.cc
+ nodes/legacy/node_geo_legacy_edge_split.cc
+ nodes/legacy/node_geo_legacy_material_assign.cc
+ nodes/legacy/node_geo_legacy_mesh_to_curve.cc
+ nodes/legacy/node_geo_legacy_point_distribute.cc
+ nodes/legacy/node_geo_legacy_point_instance.cc
+ nodes/legacy/node_geo_legacy_point_rotate.cc
+ nodes/legacy/node_geo_legacy_point_scale.cc
+ nodes/legacy/node_geo_legacy_point_separate.cc
+ nodes/legacy/node_geo_legacy_point_translate.cc
+ nodes/legacy/node_geo_legacy_points_to_volume.cc
+ nodes/legacy/node_geo_legacy_raycast.cc
+ nodes/legacy/node_geo_legacy_select_by_material.cc
+ nodes/legacy/node_geo_legacy_subdivision_surface.cc
+ nodes/legacy/node_geo_legacy_volume_to_mesh.cc
+
+ nodes/node_geo_accumulate_field.cc
+ nodes/node_geo_attribute_capture.cc
+ nodes/node_geo_attribute_domain_size.cc
+ nodes/node_geo_attribute_remove.cc
+ nodes/node_geo_attribute_statistic.cc
+ nodes/node_geo_boolean.cc
+ nodes/node_geo_bounding_box.cc
+ nodes/node_geo_collection_info.cc
+ nodes/node_geo_common.cc
+ nodes/node_geo_convex_hull.cc
+ nodes/node_geo_curve_endpoint_selection.cc
+ nodes/node_geo_curve_fill.cc
+ nodes/node_geo_curve_fillet.cc
+ nodes/node_geo_curve_handle_type_selection.cc
+ nodes/node_geo_curve_length.cc
+ nodes/node_geo_curve_primitive_arc.cc
+ nodes/node_geo_curve_primitive_bezier_segment.cc
+ nodes/node_geo_curve_primitive_circle.cc
+ nodes/node_geo_curve_primitive_line.cc
+ nodes/node_geo_curve_primitive_quadratic_bezier.cc
+ nodes/node_geo_curve_primitive_quadrilateral.cc
+ nodes/node_geo_curve_primitive_spiral.cc
+ nodes/node_geo_curve_primitive_star.cc
+ nodes/node_geo_curve_resample.cc
+ nodes/node_geo_curve_reverse.cc
+ nodes/node_geo_curve_sample.cc
+ nodes/node_geo_curve_set_handles.cc
+ nodes/node_geo_curve_spline_parameter.cc
+ nodes/node_geo_curve_spline_type.cc
+ nodes/node_geo_curve_subdivide.cc
+ nodes/node_geo_curve_to_mesh.cc
+ nodes/node_geo_curve_to_points.cc
+ nodes/node_geo_curve_trim.cc
+ nodes/node_geo_delete_geometry.cc
+ nodes/node_geo_distribute_points_on_faces.cc
+ nodes/node_geo_dual_mesh.cc
+ nodes/node_geo_edge_split.cc
+ nodes/node_geo_extrude_mesh.cc
+ nodes/node_geo_field_at_index.cc
+ nodes/node_geo_flip_faces.cc
+ nodes/node_geo_geometry_to_instance.cc
+ nodes/node_geo_image_texture.cc
+ nodes/node_geo_input_curve_handles.cc
+ nodes/node_geo_input_curve_tilt.cc
+ nodes/node_geo_input_id.cc
+ nodes/node_geo_input_index.cc
+ nodes/node_geo_input_material.cc
+ nodes/node_geo_input_material_index.cc
+ nodes/node_geo_input_mesh_edge_angle.cc
+ nodes/node_geo_input_mesh_edge_neighbors.cc
+ nodes/node_geo_input_mesh_edge_vertices.cc
+ nodes/node_geo_input_mesh_face_area.cc
+ nodes/node_geo_input_mesh_face_neighbors.cc
+ nodes/node_geo_input_mesh_island.cc
+ nodes/node_geo_input_mesh_vertex_neighbors.cc
+ nodes/node_geo_input_normal.cc
+ nodes/node_geo_input_position.cc
+ nodes/node_geo_input_radius.cc
+ nodes/node_geo_input_scene_time.cc
+ nodes/node_geo_input_shade_smooth.cc
+ nodes/node_geo_input_spline_cyclic.cc
+ nodes/node_geo_input_spline_length.cc
+ nodes/node_geo_input_spline_resolution.cc
+ nodes/node_geo_input_tangent.cc
+ nodes/node_geo_instance_on_points.cc
+ nodes/node_geo_instances_to_points.cc
+ nodes/node_geo_is_viewport.cc
+ nodes/node_geo_join_geometry.cc
+ nodes/node_geo_material_replace.cc
+ nodes/node_geo_material_selection.cc
+ nodes/node_geo_merge_by_distance.cc
+ nodes/node_geo_mesh_primitive_circle.cc
+ nodes/node_geo_mesh_primitive_cone.cc
+ nodes/node_geo_mesh_primitive_cube.cc
+ nodes/node_geo_mesh_primitive_cylinder.cc
+ nodes/node_geo_mesh_primitive_grid.cc
+ nodes/node_geo_mesh_primitive_ico_sphere.cc
+ nodes/node_geo_mesh_primitive_line.cc
+ nodes/node_geo_mesh_primitive_uv_sphere.cc
+ nodes/node_geo_mesh_subdivide.cc
+ nodes/node_geo_mesh_to_curve.cc
+ nodes/node_geo_mesh_to_points.cc
+ nodes/node_geo_object_info.cc
+ nodes/node_geo_points_to_vertices.cc
+ nodes/node_geo_points_to_volume.cc
+ nodes/node_geo_proximity.cc
+ nodes/node_geo_raycast.cc
+ nodes/node_geo_realize_instances.cc
+ nodes/node_geo_rotate_instances.cc
+ nodes/node_geo_scale_elements.cc
+ nodes/node_geo_scale_instances.cc
+ nodes/node_geo_separate_components.cc
+ nodes/node_geo_separate_geometry.cc
+ nodes/node_geo_set_curve_handles.cc
+ nodes/node_geo_set_curve_radius.cc
+ nodes/node_geo_set_curve_tilt.cc
+ nodes/node_geo_set_id.cc
+ nodes/node_geo_set_material.cc
+ nodes/node_geo_set_material_index.cc
+ nodes/node_geo_set_point_radius.cc
+ nodes/node_geo_set_position.cc
+ nodes/node_geo_set_shade_smooth.cc
+ nodes/node_geo_set_spline_cyclic.cc
+ nodes/node_geo_set_spline_resolution.cc
+ nodes/node_geo_string_join.cc
+ nodes/node_geo_string_to_curves.cc
+ nodes/node_geo_subdivision_surface.cc
+ nodes/node_geo_switch.cc
+ nodes/node_geo_transfer_attribute.cc
+ nodes/node_geo_transform.cc
+ nodes/node_geo_translate_instances.cc
+ nodes/node_geo_triangulate.cc
+ nodes/node_geo_viewer.cc
+ nodes/node_geo_volume_to_mesh.cc
+
+ node_geometry_exec.cc
+ node_geometry_tree.cc
+ node_geometry_util.cc
+
+ node_geometry_util.hh
+)
+
+set(LIB
+ bf_bmesh
+ bf_functions
+ bf_geometry
+)
+
+if(WITH_BULLET)
+ list(APPEND INC_SYS
+ ${BULLET_INCLUDE_DIRS}
+ ../../../../intern/rigidbody
+ )
+ if(NOT WITH_SYSTEM_BULLET)
+ list(APPEND LIB
+ extern_bullet
+ )
+ endif()
+
+ list(APPEND LIB
+ ${BULLET_LIBRARIES}
+ )
+ add_definitions(-DWITH_BULLET)
+endif()
+
+if(WITH_PYTHON)
+ list(APPEND INC
+ ../../python
+ )
+ list(APPEND INC_SYS
+ ${PYTHON_INCLUDE_DIRS}
+ )
+ list(APPEND LIB
+ ${PYTHON_LINKFLAGS}
+ ${PYTHON_LIBRARIES}
+ )
+ add_definitions(-DWITH_PYTHON)
+endif()
+
+if(WITH_INTERNATIONAL)
+ add_definitions(-DWITH_INTERNATIONAL)
+endif()
+
+if(WITH_TBB)
+ list(APPEND INC_SYS
+ ${TBB_INCLUDE_DIRS}
+ )
+ add_definitions(-DWITH_TBB)
+ if(WIN32)
+ # TBB includes Windows.h which will define min/max macros
+ # that will collide with the stl versions.
+ add_definitions(-DNOMINMAX)
+ endif()
+endif()
+
+if(WITH_IMAGE_OPENEXR)
+ add_definitions(-DWITH_OPENEXR)
+endif()
+
+if(WITH_OPENSUBDIV)
+ add_definitions(-DWITH_OPENSUBDIV)
+endif()
+
+if(WITH_GMP)
+ add_definitions(-DWITH_GMP)
+
+ list(APPEND INC_SYS
+ ${GMP_INCLUDE_DIRS}
+ )
+
+ list(APPEND LIB
+ ${GMP_LIBRARIES}
+ )
+endif()
+
+if(WITH_OPENVDB)
+ list(APPEND INC_SYS
+ ${OPENVDB_INCLUDE_DIRS}
+ )
+ add_definitions(-DWITH_OPENVDB ${OPENVDB_DEFINITIONS})
+endif()
+
+blender_add_lib(bf_nodes_geometry "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
+
+if(WITH_UNITY_BUILD)
+ set_target_properties(bf_nodes_geometry PROPERTIES UNITY_BUILD ON)
+ set_target_properties(bf_nodes_geometry PROPERTIES UNITY_BUILD_BATCH_SIZE 10)
+endif()
diff --git a/source/blender/nodes/geometry/node_geometry_tree.cc b/source/blender/nodes/geometry/node_geometry_tree.cc
index d6b23c38ee4..a6dec71ed06 100644
--- a/source/blender/nodes/geometry/node_geometry_tree.cc
+++ b/source/blender/nodes/geometry/node_geometry_tree.cc
@@ -32,6 +32,8 @@
#include "RNA_access.h"
+#include "UI_resources.h"
+
#include "node_common.h"
bNodeTreeType *ntreeType_Geometry;
@@ -84,15 +86,16 @@ static void foreach_nodeclass(Scene *UNUSED(scene), void *calldata, bNodeClassCa
func(calldata, NODE_CLASS_LAYOUT, N_("Layout"));
}
-static bool geometry_node_tree_validate_link(bNodeTree *UNUSED(ntree), bNodeLink *link)
+static bool geometry_node_tree_validate_link(eNodeSocketDatatype type_a,
+ eNodeSocketDatatype type_b)
{
/* Geometry, string, object, material, texture and collection sockets can only be connected to
* themselves. The other types can be converted between each other. */
- if (ELEM(link->fromsock->type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_BOOLEAN, SOCK_INT) &&
- ELEM(link->tosock->type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_BOOLEAN, SOCK_INT)) {
+ if (ELEM(type_a, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_BOOLEAN, SOCK_INT) &&
+ ELEM(type_b, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_BOOLEAN, SOCK_INT)) {
return true;
}
- return (link->tosock->type == link->fromsock->type);
+ return type_a == type_b;
}
static bool geometry_node_tree_socket_type_valid(bNodeTreeType *UNUSED(ntreetype),
@@ -109,17 +112,18 @@ static bool geometry_node_tree_socket_type_valid(bNodeTreeType *UNUSED(ntreetype
SOCK_GEOMETRY,
SOCK_COLLECTION,
SOCK_TEXTURE,
+ SOCK_IMAGE,
SOCK_MATERIAL);
}
-void register_node_tree_type_geo(void)
+void register_node_tree_type_geo()
{
bNodeTreeType *tt = ntreeType_Geometry = static_cast<bNodeTreeType *>(
MEM_callocN(sizeof(bNodeTreeType), "geometry node tree type"));
tt->type = NTREE_GEOMETRY;
strcpy(tt->idname, "GeometryNodeTree");
strcpy(tt->ui_name, N_("Geometry Node Editor"));
- tt->ui_icon = 0; /* defined in drawnode.c */
+ tt->ui_icon = ICON_NODETREE;
strcpy(tt->ui_description, N_("Geometry nodes"));
tt->rna_ext.srna = &RNA_GeometryNodeTree;
tt->update = geometry_node_tree_update;
diff --git a/source/blender/nodes/geometry/node_geometry_util.cc b/source/blender/nodes/geometry/node_geometry_util.cc
index 46e9d36c09c..ceb9a7e1467 100644
--- a/source/blender/nodes/geometry/node_geometry_util.cc
+++ b/source/blender/nodes/geometry/node_geometry_util.cc
@@ -24,18 +24,14 @@
#include "BKE_mesh_runtime.h"
#include "BKE_pointcloud.h"
+#include "NOD_socket_search_link.hh"
+
namespace blender::nodes {
using bke::GeometryInstanceGroup;
-/**
- * Update the availability of a group of input sockets with the same name,
- * used for switching between attribute inputs or single values.
- *
- * \param mode: Controls which socket of the group to make available.
- * \param name_is_available: If false, make all sockets with this name unavailable.
- */
-void update_attribute_input_socket_availabilities(bNode &node,
+void update_attribute_input_socket_availabilities(bNodeTree &ntree,
+ bNode &node,
const StringRef name,
const GeometryNodeAttributeInputMode mode,
const bool name_is_available)
@@ -50,11 +46,36 @@ void update_attribute_input_socket_availabilities(bNode &node,
(socket->type == SOCK_INT && mode_ == GEO_NODE_ATTRIBUTE_INPUT_INTEGER) ||
(socket->type == SOCK_VECTOR && mode_ == GEO_NODE_ATTRIBUTE_INPUT_VECTOR) ||
(socket->type == SOCK_RGBA && mode_ == GEO_NODE_ATTRIBUTE_INPUT_COLOR));
- nodeSetSocketAvailability(socket, socket_is_available);
+ nodeSetSocketAvailability(&ntree, socket, socket_is_available);
}
}
}
+std::optional<CustomDataType> node_data_type_to_custom_data_type(const eNodeSocketDatatype type)
+{
+ switch (type) {
+ case SOCK_FLOAT:
+ return CD_PROP_FLOAT;
+ case SOCK_VECTOR:
+ return CD_PROP_FLOAT3;
+ case SOCK_RGBA:
+ return CD_PROP_COLOR;
+ case SOCK_BOOLEAN:
+ return CD_PROP_BOOL;
+ case SOCK_INT:
+ return CD_PROP_INT32;
+ case SOCK_STRING:
+ return CD_PROP_STRING;
+ default:
+ return {};
+ }
+}
+
+std::optional<CustomDataType> node_socket_to_custom_data_type(const bNodeSocket &socket)
+{
+ return node_data_type_to_custom_data_type(static_cast<eNodeSocketDatatype>(socket.type));
+}
+
} // namespace blender::nodes
bool geo_node_poll_default(bNodeType *UNUSED(ntype),
@@ -62,16 +83,16 @@ bool geo_node_poll_default(bNodeType *UNUSED(ntype),
const char **r_disabled_hint)
{
if (!STREQ(ntree->idname, "GeometryNodeTree")) {
- *r_disabled_hint = "Not a geometry node tree";
+ *r_disabled_hint = TIP_("Not a geometry node tree");
return false;
}
return true;
}
-void geo_node_type_base(bNodeType *ntype, int type, const char *name, short nclass, short flag)
+void geo_node_type_base(bNodeType *ntype, int type, const char *name, short nclass)
{
- node_type_base(ntype, type, name, nclass, flag);
+ node_type_base(ntype, type, name, nclass);
ntype->poll = geo_node_poll_default;
- ntype->update_internal_links = node_update_internal_links_default;
ntype->insert_link = node_insert_link_default;
+ ntype->gather_link_search_ops = blender::nodes::search_link_ops_for_basic_node;
}
diff --git a/source/blender/nodes/geometry/node_geometry_util.hh b/source/blender/nodes/geometry/node_geometry_util.hh
index 015ac0de002..dddc3527124 100644
--- a/source/blender/nodes/geometry/node_geometry_util.hh
+++ b/source/blender/nodes/geometry/node_geometry_util.hh
@@ -18,7 +18,7 @@
#include <string.h>
-#include "BLI_float3.hh"
+#include "BLI_math_vec_types.hh"
#include "BLI_utildefines.h"
#include "MEM_guardedalloc.h"
@@ -32,41 +32,59 @@
#include "NOD_geometry.h"
#include "NOD_geometry_exec.hh"
#include "NOD_socket_declarations.hh"
+#include "NOD_socket_declarations_geometry.hh"
#include "node_util.h"
-void geo_node_type_base(
- struct bNodeType *ntype, int type, const char *name, short nclass, short flag);
+void geo_node_type_base(struct bNodeType *ntype, int type, const char *name, short nclass);
bool geo_node_poll_default(struct bNodeType *ntype,
struct bNodeTree *ntree,
const char **r_disabled_hint);
namespace blender::nodes {
-void update_attribute_input_socket_availabilities(bNode &node,
+/**
+ * Update the availability of a group of input sockets with the same name,
+ * used for switching between attribute inputs or single values.
+ *
+ * \param mode: Controls which socket of the group to make available.
+ * \param name_is_available: If false, make all sockets with this name unavailable.
+ */
+void update_attribute_input_socket_availabilities(bNodeTree &ntree,
+ bNode &node,
const StringRef name,
- const GeometryNodeAttributeInputMode mode,
- const bool name_is_available = true);
+ GeometryNodeAttributeInputMode mode,
+ bool name_is_available = true);
Array<uint32_t> get_geometry_element_ids_as_uints(const GeometryComponent &component,
- const AttributeDomain domain);
+ AttributeDomain domain);
-void transform_mesh(Mesh *mesh,
+void transform_mesh(Mesh &mesh,
const float3 translation,
const float3 rotation,
const float3 scale);
-Mesh *create_line_mesh(const float3 start, const float3 delta, const int count);
+void transform_geometry_set(GeometrySet &geometry,
+ const float4x4 &transform,
+ const Depsgraph &depsgraph);
-Mesh *create_grid_mesh(const int verts_x,
- const int verts_y,
- const float size_x,
- const float size_y);
+Mesh *create_line_mesh(const float3 start, const float3 delta, int count);
-Mesh *create_cylinder_or_cone_mesh(const float radius_top,
- const float radius_bottom,
- const float depth,
- const int verts_num,
- const GeometryNodeMeshCircleFillType fill_type);
+Mesh *create_grid_mesh(int verts_x, int verts_y, float size_x, float size_y);
+
+struct ConeAttributeOutputs {
+ StrongAnonymousAttributeID top_id;
+ StrongAnonymousAttributeID bottom_id;
+ StrongAnonymousAttributeID side_id;
+};
+
+Mesh *create_cylinder_or_cone_mesh(float radius_top,
+ float radius_bottom,
+ float depth,
+ int circle_segments,
+ int side_segments,
+ int fill_segments,
+ GeometryNodeMeshCircleFillType fill_type,
+ ConeAttributeOutputs &attribute_outputs);
Mesh *create_cuboid_mesh(float3 size, int verts_x, int verts_y, int verts_z);
@@ -76,7 +94,18 @@ Mesh *create_cuboid_mesh(float3 size, int verts_x, int verts_y, int verts_z);
void copy_point_attributes_based_on_mask(const GeometryComponent &in_component,
GeometryComponent &result_component,
Span<bool> masks,
- const bool invert);
+ bool invert);
+/**
+ * Returns the parts of the geometry that are on the selection for the given domain. If the domain
+ * is not applicable for the component, e.g. face domain for point cloud, nothing happens to that
+ * component. If no component can work with the domain, then `error_message` is set to true.
+ */
+void separate_geometry(GeometrySet &geometry_set,
+ AttributeDomain domain,
+ GeometryNodeDeleteGeometryMode mode,
+ const Field<bool> &selection_field,
+ bool invert,
+ bool &r_is_error);
struct CurveToPointsResults {
int result_size;
@@ -100,4 +129,7 @@ void curve_create_default_rotation_attribute(Span<float3> tangents,
Span<float3> normals,
MutableSpan<float3> rotations);
+std::optional<CustomDataType> node_data_type_to_custom_data_type(eNodeSocketDatatype type);
+std::optional<CustomDataType> node_socket_to_custom_data_type(const bNodeSocket &socket);
+
} // namespace blender::nodes
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_align_rotation_to_vector.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_align_rotation_to_vector.cc
index d0bb906e8af..1d064586238 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_align_rotation_to_vector.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_align_rotation_to_vector.cc
@@ -22,27 +22,25 @@
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_legacy_align_rotation_to_vector_cc {
-static void geo_node_align_rotation_to_vector_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::String>("Factor");
- b.add_input<decl::Float>("Factor", "Factor_001")
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::String>(N_("Factor"));
+ b.add_input<decl::Float>(N_("Factor"), "Factor_001")
.default_value(1.0f)
.min(0.0f)
.max(1.0f)
.subtype(PROP_FACTOR);
- b.add_input<decl::String>("Vector");
- b.add_input<decl::Vector>("Vector", "Vector_001")
+ b.add_input<decl::String>(N_("Vector"));
+ b.add_input<decl::Vector>(N_("Vector"), "Vector_001")
.default_value({0.0, 0.0, 1.0})
.subtype(PROP_ANGLE);
- b.add_output<decl::Geometry>("Geometry");
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
-static void geo_node_align_rotation_to_vector_layout(uiLayout *layout,
- bContext *UNUSED(C),
- PointerRNA *ptr)
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiItemR(layout, ptr, "axis", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
uiLayoutSetPropSep(layout, true);
@@ -53,10 +51,10 @@ static void geo_node_align_rotation_to_vector_layout(uiLayout *layout,
uiItemR(col, ptr, "input_type_vector", 0, IFACE_("Vector"), ICON_NONE);
}
-static void geo_node_align_rotation_to_vector_init(bNodeTree *UNUSED(ntree), bNode *node)
+static void node_init(bNodeTree *UNUSED(ntree), bNode *node)
{
- NodeGeometryAlignRotationToVector *node_storage = (NodeGeometryAlignRotationToVector *)
- MEM_callocN(sizeof(NodeGeometryAlignRotationToVector), __func__);
+ NodeGeometryAlignRotationToVector *node_storage = MEM_cnew<NodeGeometryAlignRotationToVector>(
+ __func__);
node_storage->axis = GEO_NODE_ALIGN_ROTATION_TO_VECTOR_AXIS_X;
node_storage->input_type_factor = GEO_NODE_ATTRIBUTE_INPUT_FLOAT;
@@ -65,14 +63,14 @@ static void geo_node_align_rotation_to_vector_init(bNodeTree *UNUSED(ntree), bNo
node->storage = node_storage;
}
-static void geo_node_align_rotation_to_vector_update(bNodeTree *UNUSED(ntree), bNode *node)
+static void node_update(bNodeTree *ntree, bNode *node)
{
NodeGeometryAlignRotationToVector *node_storage = (NodeGeometryAlignRotationToVector *)
node->storage;
update_attribute_input_socket_availabilities(
- *node, "Factor", (GeometryNodeAttributeInputMode)node_storage->input_type_factor);
+ *ntree, *node, "Factor", (GeometryNodeAttributeInputMode)node_storage->input_type_factor);
update_attribute_input_socket_availabilities(
- *node, "Vector", (GeometryNodeAttributeInputMode)node_storage->input_type_vector);
+ *ntree, *node, "Vector", (GeometryNodeAttributeInputMode)node_storage->input_type_vector);
}
static void align_rotations_auto_pivot(const VArray<float3> &vectors,
@@ -92,14 +90,14 @@ static void align_rotations_auto_pivot(const VArray<float3> &vectors,
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);
+ const float3 new_axis = math::normalize(vector);
+ float3 rotation_axis = math::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));
+ rotation_axis = math::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));
+ rotation_axis = math::cross_high_precision(old_axis, float3(0, 1, 0));
}
}
@@ -179,9 +177,9 @@ static void align_rotations_on_component(GeometryComponent &component,
return;
}
- GVArray_Typed<float> factors = params.get_input_attribute<float>(
+ VArray<float> factors = params.get_input_attribute<float>(
"Factor", component, ATTR_DOMAIN_POINT, 1.0f);
- GVArray_Typed<float3> vectors = params.get_input_attribute<float3>(
+ VArray<float3> vectors = params.get_input_attribute<float3>(
"Vector", component, ATTR_DOMAIN_POINT, {0, 0, 1});
float3 local_main_axis{0, 0, 0};
@@ -199,11 +197,11 @@ static void align_rotations_on_component(GeometryComponent &component,
rotations.save();
}
-static void geo_node_align_rotation_to_vector_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
if (geometry_set.has<MeshComponent>()) {
align_rotations_on_component(geometry_set.get_component_for_write<MeshComponent>(), params);
@@ -219,25 +217,26 @@ static void geo_node_align_rotation_to_vector_exec(GeoNodeExecParams params)
params.set_output("Geometry", geometry_set);
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_legacy_align_rotation_to_vector_cc
void register_node_type_geo_align_rotation_to_vector()
{
+ namespace file_ns = blender::nodes::node_geo_legacy_align_rotation_to_vector_cc;
+
static bNodeType ntype;
geo_node_type_base(&ntype,
GEO_NODE_LEGACY_ALIGN_ROTATION_TO_VECTOR,
"Align Rotation to Vector",
- NODE_CLASS_GEOMETRY,
- 0);
- node_type_init(&ntype, blender::nodes::geo_node_align_rotation_to_vector_init);
- node_type_update(&ntype, blender::nodes::geo_node_align_rotation_to_vector_update);
+ NODE_CLASS_GEOMETRY);
+ node_type_init(&ntype, file_ns::node_init);
+ node_type_update(&ntype, file_ns::node_update);
node_type_storage(&ntype,
"NodeGeometryAlignRotationToVector",
node_free_standard_storage,
node_copy_standard_storage);
- ntype.declare = blender::nodes::geo_node_align_rotation_to_vector_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_align_rotation_to_vector_exec;
- ntype.draw_buttons = blender::nodes::geo_node_align_rotation_to_vector_layout;
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.draw_buttons = file_ns::node_layout;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_clamp.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_clamp.cc
index 2e931a2da98..cac2a90a76c 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_clamp.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_clamp.cc
@@ -20,40 +20,39 @@
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_legacy_attribute_clamp_cc {
-static void geo_node_attribute_clamp_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::String>("Attribute");
- b.add_input<decl::String>("Result");
- b.add_input<decl::Vector>("Min");
- b.add_input<decl::Vector>("Max").default_value({1.0f, 1.0f, 1.0f});
- b.add_input<decl::Float>("Min", "Min_001");
- b.add_input<decl::Float>("Max", "Max_001").default_value(1.0f);
- b.add_input<decl::Int>("Min", "Min_002").min(-100000).max(100000);
- b.add_input<decl::Int>("Max", "Max_002").default_value(100).min(-100000).max(100000);
- b.add_input<decl::Color>("Min", "Min_003").default_value({0.5f, 0.5f, 0.5f, 1.0f});
- b.add_input<decl::Color>("Max", "Max_003").default_value({0.5f, 0.5f, 0.5f, 1.0f});
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::String>(N_("Attribute"));
+ b.add_input<decl::String>(N_("Result"));
+ b.add_input<decl::Vector>(N_("Min"));
+ b.add_input<decl::Vector>(N_("Max")).default_value({1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Float>(N_("Min"), "Min_001");
+ b.add_input<decl::Float>(N_("Max"), "Max_001").default_value(1.0f);
+ b.add_input<decl::Int>(N_("Min"), "Min_002").min(-100000).max(100000);
+ b.add_input<decl::Int>(N_("Max"), "Max_002").default_value(100).min(-100000).max(100000);
+ b.add_input<decl::Color>(N_("Min"), "Min_003").default_value({0.5f, 0.5f, 0.5f, 1.0f});
+ b.add_input<decl::Color>(N_("Max"), "Max_003").default_value({0.5f, 0.5f, 0.5f, 1.0f});
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
-static void geo_node_attribute_clamp_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE);
uiItemR(layout, ptr, "operation", 0, "", ICON_NONE);
}
-static void geo_node_attribute_clamp_init(bNodeTree *UNUSED(tree), bNode *node)
+static void node_init(bNodeTree *UNUSED(tree), bNode *node)
{
- NodeAttributeClamp *data = (NodeAttributeClamp *)MEM_callocN(sizeof(NodeAttributeClamp),
- __func__);
+ NodeAttributeClamp *data = MEM_cnew<NodeAttributeClamp>(__func__);
data->data_type = CD_PROP_FLOAT;
data->operation = NODE_CLAMP_MINMAX;
node->storage = data;
}
-static void geo_node_attribute_clamp_update(bNodeTree *UNUSED(ntree), bNode *node)
+static void node_update(bNodeTree *ntree, bNode *node)
{
bNodeSocket *sock_min_vector = (bNodeSocket *)BLI_findlink(&node->inputs, 3);
bNodeSocket *sock_max_vector = sock_min_vector->next;
@@ -66,14 +65,14 @@ static void geo_node_attribute_clamp_update(bNodeTree *UNUSED(ntree), bNode *nod
const NodeAttributeClamp &storage = *(const NodeAttributeClamp *)node->storage;
const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type);
- nodeSetSocketAvailability(sock_min_vector, data_type == CD_PROP_FLOAT3);
- nodeSetSocketAvailability(sock_max_vector, data_type == CD_PROP_FLOAT3);
- nodeSetSocketAvailability(sock_min_float, data_type == CD_PROP_FLOAT);
- nodeSetSocketAvailability(sock_max_float, data_type == CD_PROP_FLOAT);
- nodeSetSocketAvailability(sock_min_int, data_type == CD_PROP_INT32);
- nodeSetSocketAvailability(sock_max_int, data_type == CD_PROP_INT32);
- nodeSetSocketAvailability(sock_min_color, data_type == CD_PROP_COLOR);
- nodeSetSocketAvailability(sock_max_color, data_type == CD_PROP_COLOR);
+ nodeSetSocketAvailability(ntree, sock_min_vector, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(ntree, sock_max_vector, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(ntree, sock_min_float, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(ntree, sock_max_float, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(ntree, sock_min_int, data_type == CD_PROP_INT32);
+ nodeSetSocketAvailability(ntree, sock_max_int, data_type == CD_PROP_INT32);
+ nodeSetSocketAvailability(ntree, sock_min_color, data_type == CD_PROP_COLOR);
+ nodeSetSocketAvailability(ntree, sock_max_color, data_type == CD_PROP_COLOR);
}
template<typename T> T clamp_value(const T val, const T min, const T max);
@@ -156,7 +155,7 @@ static void clamp_attribute(GeometryComponent &component, const GeoNodeExecParam
const AttributeDomain domain = get_result_domain(component, attribute_name, result_name);
const int operation = static_cast<int>(storage.operation);
- GVArrayPtr attribute_input = component.attribute_try_get_for_read(
+ GVArray attribute_input = component.attribute_try_get_for_read(
attribute_name, domain, data_type);
OutputAttribute attribute_result = component.attribute_try_get_for_output_only(
@@ -185,7 +184,7 @@ static void clamp_attribute(GeometryComponent &component, const GeoNodeExecParam
}
}
MutableSpan<float3> results = attribute_result.as_span<float3>();
- clamp_attribute<float3>(attribute_input->typed<float3>(), results, min, max);
+ clamp_attribute<float3>(attribute_input.typed<float3>(), results, min, max);
break;
}
case CD_PROP_FLOAT: {
@@ -193,10 +192,10 @@ static void clamp_attribute(GeometryComponent &component, const GeoNodeExecParam
const float max = params.get_input<float>("Max_001");
MutableSpan<float> results = attribute_result.as_span<float>();
if (operation == NODE_CLAMP_RANGE && min > max) {
- clamp_attribute<float>(attribute_input->typed<float>(), results, max, min);
+ clamp_attribute<float>(attribute_input.typed<float>(), results, max, min);
}
else {
- clamp_attribute<float>(attribute_input->typed<float>(), results, min, max);
+ clamp_attribute<float>(attribute_input.typed<float>(), results, min, max);
}
break;
}
@@ -205,10 +204,10 @@ static void clamp_attribute(GeometryComponent &component, const GeoNodeExecParam
const int max = params.get_input<int>("Max_002");
MutableSpan<int> results = attribute_result.as_span<int>();
if (operation == NODE_CLAMP_RANGE && min > max) {
- clamp_attribute<int>(attribute_input->typed<int>(), results, max, min);
+ clamp_attribute<int>(attribute_input.typed<int>(), results, max, min);
}
else {
- clamp_attribute<int>(attribute_input->typed<int>(), results, min, max);
+ clamp_attribute<int>(attribute_input.typed<int>(), results, min, max);
}
break;
}
@@ -231,7 +230,7 @@ static void clamp_attribute(GeometryComponent &component, const GeoNodeExecParam
}
MutableSpan<ColorGeometry4f> results = attribute_result.as_span<ColorGeometry4f>();
clamp_attribute<ColorGeometry4f>(
- attribute_input->typed<ColorGeometry4f>(), results, min, max);
+ attribute_input.typed<ColorGeometry4f>(), results, min, max);
break;
}
default: {
@@ -243,11 +242,11 @@ static void clamp_attribute(GeometryComponent &component, const GeoNodeExecParam
attribute_result.save();
}
-static void geo_node_attribute_clamp_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
if (geometry_set.has<MeshComponent>()) {
clamp_attribute(geometry_set.get_component_for_write<MeshComponent>(), params);
@@ -262,19 +261,21 @@ static void geo_node_attribute_clamp_exec(GeoNodeExecParams params)
params.set_output("Geometry", geometry_set);
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_legacy_attribute_clamp_cc
void register_node_type_geo_attribute_clamp()
{
+ namespace file_ns = blender::nodes::node_geo_legacy_attribute_clamp_cc;
+
static bNodeType ntype;
geo_node_type_base(
- &ntype, GEO_NODE_LEGACY_ATTRIBUTE_CLAMP, "Attribute Clamp", NODE_CLASS_ATTRIBUTE, 0);
- node_type_init(&ntype, blender::nodes::geo_node_attribute_clamp_init);
- node_type_update(&ntype, blender::nodes::geo_node_attribute_clamp_update);
- ntype.declare = blender::nodes::geo_node_attribute_clamp_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_attribute_clamp_exec;
- ntype.draw_buttons = blender::nodes::geo_node_attribute_clamp_layout;
+ &ntype, GEO_NODE_LEGACY_ATTRIBUTE_CLAMP, "Attribute Clamp", NODE_CLASS_ATTRIBUTE);
+ node_type_init(&ntype, file_ns::node_init);
+ node_type_update(&ntype, file_ns::node_update);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.draw_buttons = file_ns::node_layout;
node_type_storage(
&ntype, "NodeAttributeClamp", node_free_standard_storage, node_copy_standard_storage);
nodeRegisterType(&ntype);
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_color_ramp.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_color_ramp.cc
index aa054af3acd..ec57422a531 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_color_ramp.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_color_ramp.cc
@@ -23,27 +23,24 @@
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_legacy_attribute_color_ramp_cc {
-static void geo_node_attribute_color_ramp_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::String>("Attribute");
- b.add_input<decl::String>("Result");
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::String>(N_("Attribute"));
+ b.add_input<decl::String>(N_("Result"));
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
-static void geo_node_attribute_color_ramp_layout(uiLayout *layout,
- bContext *UNUSED(C),
- PointerRNA *ptr)
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiTemplateColorRamp(layout, ptr, "color_ramp", false);
}
-static void geo_node_attribute_color_ramp_init(bNodeTree *UNUSED(ntree), bNode *node)
+static void node_init(bNodeTree *UNUSED(ntree), bNode *node)
{
- NodeAttributeColorRamp *node_storage = (NodeAttributeColorRamp *)MEM_callocN(
- sizeof(NodeAttributeColorRamp), __func__);
+ NodeAttributeColorRamp *node_storage = MEM_cnew<NodeAttributeColorRamp>(__func__);
BKE_colorband_init(&node_storage->color_ramp, true);
node->storage = node_storage;
}
@@ -85,7 +82,7 @@ static void execute_on_component(const GeoNodeExecParams &params, GeometryCompon
return;
}
- GVArray_Typed<float> attribute_in = component.attribute_get_for_read<float>(
+ VArray<float> attribute_in = component.attribute_get_for_read<float>(
input_name, result_domain, 0.0f);
MutableSpan<ColorGeometry4f> results = attribute_result.as_span();
@@ -100,11 +97,11 @@ static void execute_on_component(const GeoNodeExecParams &params, GeometryCompon
attribute_result.save();
}
-static void geo_node_attribute_color_ramp_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
if (geometry_set.has<MeshComponent>()) {
execute_on_component(params, geometry_set.get_component_for_write<MeshComponent>());
@@ -119,23 +116,22 @@ static void geo_node_attribute_color_ramp_exec(GeoNodeExecParams params)
params.set_output("Geometry", std::move(geometry_set));
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_legacy_attribute_color_ramp_cc
void register_node_type_geo_attribute_color_ramp()
{
+ namespace file_ns = blender::nodes::node_geo_legacy_attribute_color_ramp_cc;
+
static bNodeType ntype;
- geo_node_type_base(&ntype,
- GEO_NODE_LEGACY_ATTRIBUTE_COLOR_RAMP,
- "Attribute Color Ramp",
- NODE_CLASS_ATTRIBUTE,
- 0);
+ geo_node_type_base(
+ &ntype, GEO_NODE_LEGACY_ATTRIBUTE_COLOR_RAMP, "Attribute Color Ramp", NODE_CLASS_ATTRIBUTE);
node_type_storage(
&ntype, "NodeAttributeColorRamp", node_free_standard_storage, node_copy_standard_storage);
- node_type_init(&ntype, blender::nodes::geo_node_attribute_color_ramp_init);
+ node_type_init(&ntype, file_ns::node_init);
node_type_size_preset(&ntype, NODE_SIZE_LARGE);
- ntype.declare = blender::nodes::geo_node_attribute_color_ramp_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_attribute_color_ramp_exec;
- ntype.draw_buttons = blender::nodes::geo_node_attribute_color_ramp_layout;
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.draw_buttons = file_ns::node_layout;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_combine_xyz.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_combine_xyz.cc
index 569d5a824ca..403b9446f75 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_combine_xyz.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_combine_xyz.cc
@@ -19,24 +19,22 @@
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_legacy_attribute_combine_xyz_cc {
-static void geo_node_attribute_combine_xyz_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::String>("X");
- b.add_input<decl::Float>("X", "X_001");
- b.add_input<decl::String>("Y");
- b.add_input<decl::Float>("Y", "Y_001");
- b.add_input<decl::String>("Z");
- b.add_input<decl::Float>("Z", "Z_001");
- b.add_input<decl::String>("Result");
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::String>(N_("X"));
+ b.add_input<decl::Float>(N_("X"), "X_001");
+ b.add_input<decl::String>(N_("Y"));
+ b.add_input<decl::Float>(N_("Y"), "Y_001");
+ b.add_input<decl::String>(N_("Z"));
+ b.add_input<decl::Float>(N_("Z"), "Z_001");
+ b.add_input<decl::String>(N_("Result"));
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
-static void geo_node_attribute_combine_xyz_layout(uiLayout *layout,
- bContext *UNUSED(C),
- PointerRNA *ptr)
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
@@ -46,10 +44,9 @@ static void geo_node_attribute_combine_xyz_layout(uiLayout *layout,
uiItemR(col, ptr, "input_type_z", 0, IFACE_("Z"), ICON_NONE);
}
-static void geo_node_attribute_combine_xyz_init(bNodeTree *UNUSED(tree), bNode *node)
+static void node_init(bNodeTree *UNUSED(tree), bNode *node)
{
- NodeAttributeCombineXYZ *data = (NodeAttributeCombineXYZ *)MEM_callocN(
- sizeof(NodeAttributeCombineXYZ), __func__);
+ NodeAttributeCombineXYZ *data = MEM_cnew<NodeAttributeCombineXYZ>(__func__);
data->input_type_x = GEO_NODE_ATTRIBUTE_INPUT_FLOAT;
data->input_type_y = GEO_NODE_ATTRIBUTE_INPUT_FLOAT;
@@ -57,15 +54,15 @@ static void geo_node_attribute_combine_xyz_init(bNodeTree *UNUSED(tree), bNode *
node->storage = data;
}
-static void geo_node_attribute_combine_xyz_update(bNodeTree *UNUSED(ntree), bNode *node)
+static void node_update(bNodeTree *ntree, bNode *node)
{
NodeAttributeCombineXYZ *node_storage = (NodeAttributeCombineXYZ *)node->storage;
update_attribute_input_socket_availabilities(
- *node, "X", (GeometryNodeAttributeInputMode)node_storage->input_type_x);
+ *ntree, *node, "X", (GeometryNodeAttributeInputMode)node_storage->input_type_x);
update_attribute_input_socket_availabilities(
- *node, "Y", (GeometryNodeAttributeInputMode)node_storage->input_type_y);
+ *ntree, *node, "Y", (GeometryNodeAttributeInputMode)node_storage->input_type_y);
update_attribute_input_socket_availabilities(
- *node, "Z", (GeometryNodeAttributeInputMode)node_storage->input_type_z);
+ *ntree, *node, "Z", (GeometryNodeAttributeInputMode)node_storage->input_type_z);
}
static AttributeDomain get_result_domain(const GeometryComponent &component,
@@ -95,11 +92,11 @@ static void combine_attributes(GeometryComponent &component, const GeoNodeExecPa
if (!attribute_result) {
return;
}
- GVArray_Typed<float> attribute_x = params.get_input_attribute<float>(
+ VArray<float> attribute_x = params.get_input_attribute<float>(
"X", component, result_domain, 0.0f);
- GVArray_Typed<float> attribute_y = params.get_input_attribute<float>(
+ VArray<float> attribute_y = params.get_input_attribute<float>(
"Y", component, result_domain, 0.0f);
- GVArray_Typed<float> attribute_z = params.get_input_attribute<float>(
+ VArray<float> attribute_z = params.get_input_attribute<float>(
"Z", component, result_domain, 0.0f);
for (const int i : IndexRange(attribute_result->size())) {
@@ -111,11 +108,11 @@ static void combine_attributes(GeometryComponent &component, const GeoNodeExecPa
attribute_result.save();
}
-static void geo_node_attribute_combine_xyz_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
if (geometry_set.has<MeshComponent>()) {
combine_attributes(geometry_set.get_component_for_write<MeshComponent>(), params);
@@ -130,24 +127,25 @@ static void geo_node_attribute_combine_xyz_exec(GeoNodeExecParams params)
params.set_output("Geometry", geometry_set);
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_legacy_attribute_combine_xyz_cc
void register_node_type_geo_attribute_combine_xyz()
{
+ namespace file_ns = blender::nodes::node_geo_legacy_attribute_combine_xyz_cc;
+
static bNodeType ntype;
geo_node_type_base(&ntype,
GEO_NODE_LEGACY_ATTRIBUTE_COMBINE_XYZ,
"Attribute Combine XYZ",
- NODE_CLASS_ATTRIBUTE,
- 0);
- node_type_init(&ntype, blender::nodes::geo_node_attribute_combine_xyz_init);
- node_type_update(&ntype, blender::nodes::geo_node_attribute_combine_xyz_update);
+ NODE_CLASS_ATTRIBUTE);
+ node_type_init(&ntype, file_ns::node_init);
+ node_type_update(&ntype, file_ns::node_update);
node_type_storage(
&ntype, "NodeAttributeCombineXYZ", node_free_standard_storage, node_copy_standard_storage);
- ntype.declare = blender::nodes::geo_node_attribute_combine_xyz_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_attribute_combine_xyz_exec;
- ntype.draw_buttons = blender::nodes::geo_node_attribute_combine_xyz_layout;
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.draw_buttons = file_ns::node_layout;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_compare.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_compare.cc
index 0b9708dae14..6cec73d76a2 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_compare.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_compare.cc
@@ -21,27 +21,25 @@
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_legacy_attribute_compare_cc {
-static void geo_node_attribute_compare_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::String>("A");
- b.add_input<decl::Float>("A", "A_001");
- b.add_input<decl::Vector>("A", "A_002");
- b.add_input<decl::Color>("A", "A_003").default_value({0.5, 0.5, 0.5, 1.0});
- b.add_input<decl::String>("B");
- b.add_input<decl::Float>("B", "B_001");
- b.add_input<decl::Vector>("B", "B_002");
- b.add_input<decl::Color>("B", "B_003").default_value({0.5, 0.5, 0.5, 1.0});
- b.add_input<decl::Float>("Threshold").default_value(0.01f).min(0.0f);
- b.add_input<decl::String>("Result");
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::String>(N_("A"));
+ b.add_input<decl::Float>(N_("A"), "A_001");
+ b.add_input<decl::Vector>(N_("A"), "A_002");
+ b.add_input<decl::Color>(N_("A"), "A_003").default_value({0.5, 0.5, 0.5, 1.0});
+ b.add_input<decl::String>(N_("B"));
+ b.add_input<decl::Float>(N_("B"), "B_001");
+ b.add_input<decl::Vector>(N_("B"), "B_002");
+ b.add_input<decl::Color>(N_("B"), "B_003").default_value({0.5, 0.5, 0.5, 1.0});
+ b.add_input<decl::Float>(N_("Threshold")).default_value(0.01f).min(0.0f);
+ b.add_input<decl::String>(N_("Result"));
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
-static void geo_node_attribute_compare_layout(uiLayout *layout,
- bContext *UNUSED(C),
- PointerRNA *ptr)
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiItemR(layout, ptr, "operation", 0, "", ICON_NONE);
uiLayoutSetPropSep(layout, true);
@@ -50,11 +48,10 @@ static void geo_node_attribute_compare_layout(uiLayout *layout,
uiItemR(layout, ptr, "input_type_b", 0, IFACE_("B"), ICON_NONE);
}
-static void geo_node_attribute_compare_init(bNodeTree *UNUSED(tree), bNode *node)
+static void node_init(bNodeTree *UNUSED(tree), bNode *node)
{
- NodeAttributeCompare *data = (NodeAttributeCompare *)MEM_callocN(sizeof(NodeAttributeCompare),
- __func__);
- data->operation = NODE_FLOAT_COMPARE_GREATER_THAN;
+ NodeAttributeCompare *data = MEM_cnew<NodeAttributeCompare>(__func__);
+ data->operation = NODE_COMPARE_GREATER_THAN;
data->input_type_a = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE;
data->input_type_b = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE;
node->storage = data;
@@ -62,24 +59,24 @@ static void geo_node_attribute_compare_init(bNodeTree *UNUSED(tree), bNode *node
static bool operation_tests_equality(const NodeAttributeCompare &node_storage)
{
- return ELEM(node_storage.operation, NODE_FLOAT_COMPARE_EQUAL, NODE_FLOAT_COMPARE_NOT_EQUAL);
+ return ELEM(node_storage.operation, NODE_COMPARE_EQUAL, NODE_COMPARE_NOT_EQUAL);
}
-static void geo_node_attribute_compare_update(bNodeTree *UNUSED(ntree), bNode *node)
+static void node_update(bNodeTree *ntree, bNode *node)
{
NodeAttributeCompare *node_storage = (NodeAttributeCompare *)node->storage;
update_attribute_input_socket_availabilities(
- *node, "A", (GeometryNodeAttributeInputMode)node_storage->input_type_a);
+ *ntree, *node, "A", (GeometryNodeAttributeInputMode)node_storage->input_type_a);
update_attribute_input_socket_availabilities(
- *node, "B", (GeometryNodeAttributeInputMode)node_storage->input_type_b);
+ *ntree, *node, "B", (GeometryNodeAttributeInputMode)node_storage->input_type_b);
bNodeSocket *socket_threshold = (bNodeSocket *)BLI_findlink(&node->inputs, 9);
- nodeSetSocketAvailability(socket_threshold, operation_tests_equality(*node_storage));
+ nodeSetSocketAvailability(ntree, socket_threshold, operation_tests_equality(*node_storage));
}
static void do_math_operation(const VArray<float> &input_a,
const VArray<float> &input_b,
- const FloatCompareOperation operation,
+ const NodeCompareOperation operation,
MutableSpan<bool> span_result)
{
const int size = input_a.size();
@@ -243,7 +240,7 @@ static void attribute_compare_calc(GeometryComponent &component, const GeoNodeEx
{
const bNode &node = params.node();
NodeAttributeCompare *node_storage = (NodeAttributeCompare *)node.storage;
- const FloatCompareOperation operation = static_cast<FloatCompareOperation>(
+ const NodeCompareOperation operation = static_cast<NodeCompareOperation>(
node_storage->operation);
const std::string result_name = params.get_input<std::string>("Result");
@@ -257,9 +254,9 @@ static void attribute_compare_calc(GeometryComponent &component, const GeoNodeEx
const CustomDataType input_data_type = get_data_type(component, params, *node_storage);
- GVArrayPtr attribute_a = params.get_input_attribute(
+ GVArray attribute_a = params.get_input_attribute(
"A", component, result_domain, input_data_type, nullptr);
- GVArrayPtr attribute_b = params.get_input_attribute(
+ GVArray attribute_b = params.get_input_attribute(
"B", component, result_domain, input_data_type, nullptr);
if (!attribute_a || !attribute_b) {
@@ -273,60 +270,60 @@ static void attribute_compare_calc(GeometryComponent &component, const GeoNodeEx
* conversions and float comparison. In other words, the comparison is not element-wise. */
if (operation_tests_equality(*node_storage)) {
const float threshold = params.get_input<float>("Threshold");
- if (operation == NODE_FLOAT_COMPARE_EQUAL) {
+ if (operation == NODE_COMPARE_EQUAL) {
if (input_data_type == CD_PROP_FLOAT) {
do_equal_operation_float(
- attribute_a->typed<float>(), attribute_b->typed<float>(), threshold, result_span);
+ attribute_a.typed<float>(), attribute_b.typed<float>(), threshold, result_span);
}
else if (input_data_type == CD_PROP_FLOAT3) {
do_equal_operation_float3(
- attribute_a->typed<float3>(), attribute_b->typed<float3>(), threshold, result_span);
+ attribute_a.typed<float3>(), attribute_b.typed<float3>(), threshold, result_span);
}
else if (input_data_type == CD_PROP_COLOR) {
- do_equal_operation_color4f(attribute_a->typed<ColorGeometry4f>(),
- attribute_b->typed<ColorGeometry4f>(),
+ do_equal_operation_color4f(attribute_a.typed<ColorGeometry4f>(),
+ attribute_b.typed<ColorGeometry4f>(),
threshold,
result_span);
}
else if (input_data_type == CD_PROP_BOOL) {
do_equal_operation_bool(
- attribute_a->typed<bool>(), attribute_b->typed<bool>(), threshold, result_span);
+ attribute_a.typed<bool>(), attribute_b.typed<bool>(), threshold, result_span);
}
}
- else if (operation == NODE_FLOAT_COMPARE_NOT_EQUAL) {
+ else if (operation == NODE_COMPARE_NOT_EQUAL) {
if (input_data_type == CD_PROP_FLOAT) {
do_not_equal_operation_float(
- attribute_a->typed<float>(), attribute_b->typed<float>(), threshold, result_span);
+ attribute_a.typed<float>(), attribute_b.typed<float>(), threshold, result_span);
}
else if (input_data_type == CD_PROP_FLOAT3) {
do_not_equal_operation_float3(
- attribute_a->typed<float3>(), attribute_b->typed<float3>(), threshold, result_span);
+ attribute_a.typed<float3>(), attribute_b.typed<float3>(), threshold, result_span);
}
else if (input_data_type == CD_PROP_COLOR) {
- do_not_equal_operation_color4f(attribute_a->typed<ColorGeometry4f>(),
- attribute_b->typed<ColorGeometry4f>(),
+ do_not_equal_operation_color4f(attribute_a.typed<ColorGeometry4f>(),
+ attribute_b.typed<ColorGeometry4f>(),
threshold,
result_span);
}
else if (input_data_type == CD_PROP_BOOL) {
do_not_equal_operation_bool(
- attribute_a->typed<bool>(), attribute_b->typed<bool>(), threshold, result_span);
+ attribute_a.typed<bool>(), attribute_b.typed<bool>(), threshold, result_span);
}
}
}
else {
do_math_operation(
- attribute_a->typed<float>(), attribute_b->typed<float>(), operation, result_span);
+ attribute_a.typed<float>(), attribute_b.typed<float>(), operation, result_span);
}
attribute_result.save();
}
-static void geo_node_attribute_compare_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
if (geometry_set.has<MeshComponent>()) {
attribute_compare_calc(geometry_set.get_component_for_write<MeshComponent>(), params);
@@ -341,20 +338,22 @@ static void geo_node_attribute_compare_exec(GeoNodeExecParams params)
params.set_output("Geometry", geometry_set);
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_legacy_attribute_compare_cc
void register_node_type_geo_attribute_compare()
{
+ namespace file_ns = blender::nodes::node_geo_legacy_attribute_compare_cc;
+
static bNodeType ntype;
geo_node_type_base(
- &ntype, GEO_NODE_LEGACY_ATTRIBUTE_COMPARE, "Attribute Compare", NODE_CLASS_ATTRIBUTE, 0);
- ntype.declare = blender::nodes::geo_node_attribute_compare_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_attribute_compare_exec;
- ntype.draw_buttons = blender::nodes::geo_node_attribute_compare_layout;
- node_type_update(&ntype, blender::nodes::geo_node_attribute_compare_update);
+ &ntype, GEO_NODE_LEGACY_ATTRIBUTE_COMPARE, "Attribute Compare", NODE_CLASS_ATTRIBUTE);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.draw_buttons = file_ns::node_layout;
+ node_type_update(&ntype, file_ns::node_update);
node_type_storage(
&ntype, "NodeAttributeCompare", node_free_standard_storage, node_copy_standard_storage);
- node_type_init(&ntype, blender::nodes::geo_node_attribute_compare_init);
+ node_type_init(&ntype, file_ns::node_init);
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_convert.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_convert.cc
index a2382aa9d25..2b13f57e990 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_convert.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_convert.cc
@@ -19,19 +19,17 @@
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_legacy_attribute_convert_cc {
-static void geo_node_attribute_convert_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::String>("Attribute");
- b.add_input<decl::String>("Result");
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::String>(N_("Attribute"));
+ b.add_input<decl::String>(N_("Result"));
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
-static void geo_node_attribute_convert_layout(uiLayout *layout,
- bContext *UNUSED(C),
- PointerRNA *ptr)
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
@@ -39,10 +37,9 @@ static void geo_node_attribute_convert_layout(uiLayout *layout,
uiItemR(layout, ptr, "data_type", 0, IFACE_("Type"), ICON_NONE);
}
-static void geo_node_attribute_convert_init(bNodeTree *UNUSED(tree), bNode *node)
+static void node_init(bNodeTree *UNUSED(tree), bNode *node)
{
- NodeAttributeConvert *data = (NodeAttributeConvert *)MEM_callocN(sizeof(NodeAttributeConvert),
- __func__);
+ NodeAttributeConvert *data = MEM_cnew<NodeAttributeConvert>(__func__);
data->data_type = CD_AUTO_FROM_NAME;
data->domain = ATTR_DOMAIN_AUTO;
@@ -104,7 +101,7 @@ static void attribute_convert_calc(GeometryComponent &component,
return;
}
- GVArrayPtr source_attribute = component.attribute_try_get_for_read(
+ GVArray source_attribute = component.attribute_try_get_for_read(
source_name, result_domain, result_type);
if (!source_attribute) {
params.error_message_add(NodeWarningType::Error,
@@ -118,7 +115,7 @@ static void attribute_convert_calc(GeometryComponent &component,
return;
}
- GVArray_GSpan source_span{*source_attribute};
+ GVArray_GSpan source_span{source_attribute};
GMutableSpan result_span = result_attribute.as_span();
BLI_assert(source_span.size() == result_span.size());
@@ -130,11 +127,11 @@ static void attribute_convert_calc(GeometryComponent &component,
result_attribute.save();
}
-static void geo_node_attribute_convert_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
const std::string result_name = params.extract_input<std::string>("Result");
const std::string source_name = params.extract_input<std::string>("Attribute");
@@ -143,7 +140,7 @@ static void geo_node_attribute_convert_exec(GeoNodeExecParams params)
const AttributeDomain domain = static_cast<AttributeDomain>(node_storage.domain);
if (result_name.empty()) {
- params.set_output("Geometry", geometry_set);
+ params.set_default_remaining_outputs();
return;
}
@@ -175,18 +172,20 @@ static void geo_node_attribute_convert_exec(GeoNodeExecParams params)
params.set_output("Geometry", geometry_set);
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_legacy_attribute_convert_cc
void register_node_type_geo_attribute_convert()
{
+ namespace file_ns = blender::nodes::node_geo_legacy_attribute_convert_cc;
+
static bNodeType ntype;
geo_node_type_base(
- &ntype, GEO_NODE_LEGACY_ATTRIBUTE_CONVERT, "Attribute Convert", NODE_CLASS_ATTRIBUTE, 0);
- ntype.declare = blender::nodes::geo_node_attribute_convert_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_attribute_convert_exec;
- ntype.draw_buttons = blender::nodes::geo_node_attribute_convert_layout;
- node_type_init(&ntype, blender::nodes::geo_node_attribute_convert_init);
+ &ntype, GEO_NODE_LEGACY_ATTRIBUTE_CONVERT, "Attribute Convert", NODE_CLASS_ATTRIBUTE);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.draw_buttons = file_ns::node_layout;
+ node_type_init(&ntype, file_ns::node_init);
node_type_storage(
&ntype, "NodeAttributeConvert", node_free_standard_storage, node_copy_standard_storage);
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_curve_map.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_curve_map.cc
index b9621b4ae92..8e1e763f1ad 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_curve_map.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_curve_map.cc
@@ -24,19 +24,17 @@
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_legacy_attribute_curve_map_cc {
-static void geo_node_attribute_curve_map_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::String>("Attribute");
- b.add_input<decl::String>("Result");
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::String>(N_("Attribute"));
+ b.add_input<decl::String>(N_("Result"));
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
-static void geo_node_attribute_curve_map_layout(uiLayout *layout,
- bContext *UNUSED(C),
- PointerRNA *ptr)
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE);
bNode *node = (bNode *)ptr->data;
@@ -54,7 +52,7 @@ static void geo_node_attribute_curve_map_layout(uiLayout *layout,
}
}
-static void geo_node_attribute_curve_map_free_storage(bNode *node)
+static void node_free_storage(bNode *node)
{
if (node->storage) {
NodeAttributeCurveMap *data = (NodeAttributeCurveMap *)node->storage;
@@ -64,9 +62,9 @@ static void geo_node_attribute_curve_map_free_storage(bNode *node)
}
}
-static void geo_node_attribute_curve_map_copy_storage(bNodeTree *UNUSED(dest_ntree),
- bNode *dest_node,
- const bNode *src_node)
+static void node_copy_storage(bNodeTree *UNUSED(dest_ntree),
+ bNode *dest_node,
+ const bNode *src_node)
{
dest_node->storage = MEM_dupallocN(src_node->storage);
NodeAttributeCurveMap *src_data = (NodeAttributeCurveMap *)src_node->storage;
@@ -75,10 +73,9 @@ static void geo_node_attribute_curve_map_copy_storage(bNodeTree *UNUSED(dest_ntr
dest_data->curve_rgb = BKE_curvemapping_copy(src_data->curve_rgb);
}
-static void geo_node_attribute_curve_map_init(bNodeTree *UNUSED(ntree), bNode *node)
+static void node_init(bNodeTree *UNUSED(ntree), bNode *node)
{
- NodeAttributeCurveMap *data = (NodeAttributeCurveMap *)MEM_callocN(sizeof(NodeAttributeCurveMap),
- __func__);
+ NodeAttributeCurveMap *data = MEM_cnew<NodeAttributeCurveMap>(__func__);
data->data_type = CD_PROP_FLOAT;
data->curve_vec = BKE_curvemapping_add(4, -1.0f, -1.0f, 1.0f, 1.0f);
@@ -87,7 +84,7 @@ static void geo_node_attribute_curve_map_init(bNodeTree *UNUSED(ntree), bNode *n
node->storage = data;
}
-static void geo_node_attribute_curve_map_update(bNodeTree *UNUSED(ntree), bNode *node)
+static void node_update(bNodeTree *UNUSED(ntree), bNode *node)
{
/* Set the active curve when data type is changed. */
NodeAttributeCurveMap *data = (NodeAttributeCurveMap *)node->storage;
@@ -136,10 +133,10 @@ static void execute_on_component(const GeoNodeExecParams &params, GeometryCompon
switch (result_type) {
case CD_PROP_FLOAT: {
const CurveMapping *cumap = (CurveMapping *)node_storage.curve_vec;
- GVArray_Typed<float> attribute_in = component.attribute_get_for_read<float>(
+ VArray<float> attribute_in = component.attribute_get_for_read<float>(
input_name, result_domain, float(0.0f));
MutableSpan<float> results = attribute_result.as_span<float>();
- threading::parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) {
+ threading::parallel_for(attribute_in.index_range(), 512, [&](IndexRange range) {
for (const int i : range) {
results[i] = BKE_curvemapping_evaluateF(cumap, 3, attribute_in[i]);
}
@@ -148,10 +145,10 @@ static void execute_on_component(const GeoNodeExecParams &params, GeometryCompon
}
case CD_PROP_FLOAT3: {
const CurveMapping *cumap = (CurveMapping *)node_storage.curve_vec;
- GVArray_Typed<float3> attribute_in = component.attribute_get_for_read<float3>(
+ VArray<float3> attribute_in = component.attribute_get_for_read<float3>(
input_name, result_domain, float3(0.0f));
MutableSpan<float3> results = attribute_result.as_span<float3>();
- threading::parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) {
+ threading::parallel_for(attribute_in.index_range(), 512, [&](IndexRange range) {
for (const int i : range) {
BKE_curvemapping_evaluate3F(cumap, results[i], attribute_in[i]);
}
@@ -160,11 +157,10 @@ static void execute_on_component(const GeoNodeExecParams &params, GeometryCompon
}
case CD_PROP_COLOR: {
const CurveMapping *cumap = (CurveMapping *)node_storage.curve_rgb;
- GVArray_Typed<ColorGeometry4f> attribute_in =
- component.attribute_get_for_read<ColorGeometry4f>(
- input_name, result_domain, ColorGeometry4f(0.0f, 0.0f, 0.0f, 1.0f));
+ VArray<ColorGeometry4f> attribute_in = component.attribute_get_for_read<ColorGeometry4f>(
+ input_name, result_domain, ColorGeometry4f(0.0f, 0.0f, 0.0f, 1.0f));
MutableSpan<ColorGeometry4f> results = attribute_result.as_span<ColorGeometry4f>();
- threading::parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) {
+ threading::parallel_for(attribute_in.index_range(), 512, [&](IndexRange range) {
for (const int i : range) {
BKE_curvemapping_evaluateRGBF(cumap, results[i], attribute_in[i]);
}
@@ -180,7 +176,7 @@ static void execute_on_component(const GeoNodeExecParams &params, GeometryCompon
attribute_result.save();
}
-static void geo_node_attribute_curve_map_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
const bNode &bnode = params.node();
NodeAttributeCurveMap *data = (NodeAttributeCurveMap *)bnode.storage;
@@ -189,7 +185,7 @@ static void geo_node_attribute_curve_map_exec(GeoNodeExecParams params)
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
if (geometry_set.has<MeshComponent>()) {
execute_on_component(params, geometry_set.get_component_for_write<MeshComponent>());
@@ -204,23 +200,23 @@ static void geo_node_attribute_curve_map_exec(GeoNodeExecParams params)
params.set_output("Geometry", std::move(geometry_set));
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_legacy_attribute_curve_map_cc
void register_node_type_geo_attribute_curve_map()
{
+ namespace file_ns = blender::nodes::node_geo_legacy_attribute_curve_map_cc;
+
static bNodeType ntype;
geo_node_type_base(
- &ntype, GEO_NODE_LEGACY_ATTRIBUTE_CURVE_MAP, "Attribute Curve Map", NODE_CLASS_ATTRIBUTE, 0);
- node_type_update(&ntype, blender::nodes::geo_node_attribute_curve_map_update);
- node_type_init(&ntype, blender::nodes::geo_node_attribute_curve_map_init);
+ &ntype, GEO_NODE_LEGACY_ATTRIBUTE_CURVE_MAP, "Attribute Curve Map", NODE_CLASS_ATTRIBUTE);
+ node_type_update(&ntype, file_ns::node_update);
+ node_type_init(&ntype, file_ns::node_init);
node_type_size_preset(&ntype, NODE_SIZE_LARGE);
- node_type_storage(&ntype,
- "NodeAttributeCurveMap",
- blender::nodes::geo_node_attribute_curve_map_free_storage,
- blender::nodes::geo_node_attribute_curve_map_copy_storage);
- ntype.declare = blender::nodes::geo_node_attribute_curve_map_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_attribute_curve_map_exec;
- ntype.draw_buttons = blender::nodes::geo_node_attribute_curve_map_layout;
+ node_type_storage(
+ &ntype, "NodeAttributeCurveMap", file_ns::node_free_storage, file_ns::node_copy_storage);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.draw_buttons = file_ns::node_layout;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_fill.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_fill.cc
index 3c50ae5c837..a32e3b7412f 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_fill.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_fill.cc
@@ -19,21 +19,21 @@
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_legacy_attribute_fill_cc {
-static void geo_node_attribute_fill_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::String>("Attribute");
- b.add_input<decl::Vector>("Value", "Value");
- b.add_input<decl::Float>("Value", "Value_001");
- b.add_input<decl::Color>("Value", "Value_002");
- b.add_input<decl::Bool>("Value", "Value_003");
- b.add_input<decl::Int>("Value", "Value_004");
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::String>(N_("Attribute")).is_attribute_name();
+ b.add_input<decl::Vector>(N_("Value"), "Value");
+ b.add_input<decl::Float>(N_("Value"), "Value_001");
+ b.add_input<decl::Color>(N_("Value"), "Value_002");
+ b.add_input<decl::Bool>(N_("Value"), "Value_003");
+ b.add_input<decl::Int>(N_("Value"), "Value_004");
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
-static void geo_node_attribute_fill_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
@@ -41,13 +41,13 @@ static void geo_node_attribute_fill_layout(uiLayout *layout, bContext *UNUSED(C)
uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE);
}
-static void geo_node_attribute_fill_init(bNodeTree *UNUSED(tree), bNode *node)
+static void node_init(bNodeTree *UNUSED(tree), bNode *node)
{
node->custom1 = CD_PROP_FLOAT;
node->custom2 = ATTR_DOMAIN_AUTO;
}
-static void geo_node_attribute_fill_update(bNodeTree *UNUSED(ntree), bNode *node)
+static void node_update(bNodeTree *ntree, bNode *node)
{
bNodeSocket *socket_value_vector = (bNodeSocket *)BLI_findlink(&node->inputs, 2);
bNodeSocket *socket_value_float = socket_value_vector->next;
@@ -57,11 +57,11 @@ static void geo_node_attribute_fill_update(bNodeTree *UNUSED(ntree), bNode *node
const CustomDataType data_type = static_cast<CustomDataType>(node->custom1);
- nodeSetSocketAvailability(socket_value_vector, data_type == CD_PROP_FLOAT3);
- nodeSetSocketAvailability(socket_value_float, data_type == CD_PROP_FLOAT);
- nodeSetSocketAvailability(socket_value_color4f, data_type == CD_PROP_COLOR);
- nodeSetSocketAvailability(socket_value_boolean, data_type == CD_PROP_BOOL);
- nodeSetSocketAvailability(socket_value_int32, data_type == CD_PROP_INT32);
+ nodeSetSocketAvailability(ntree, socket_value_vector, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(ntree, socket_value_float, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(ntree, socket_value_color4f, data_type == CD_PROP_COLOR);
+ nodeSetSocketAvailability(ntree, socket_value_boolean, data_type == CD_PROP_BOOL);
+ nodeSetSocketAvailability(ntree, socket_value_int32, data_type == CD_PROP_INT32);
}
static AttributeDomain get_result_domain(const GeometryComponent &component, const StringRef name)
@@ -127,11 +127,11 @@ static void fill_attribute(GeometryComponent &component, const GeoNodeExecParams
attribute.save();
}
-static void geo_node_attribute_fill_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
if (geometry_set.has<MeshComponent>()) {
fill_attribute(geometry_set.get_component_for_write<MeshComponent>(), params);
@@ -146,18 +146,20 @@ static void geo_node_attribute_fill_exec(GeoNodeExecParams params)
params.set_output("Geometry", geometry_set);
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_legacy_attribute_fill_cc
void register_node_type_geo_attribute_fill()
{
+ namespace file_ns = blender::nodes::node_geo_legacy_attribute_fill_cc;
+
static bNodeType ntype;
geo_node_type_base(
- &ntype, GEO_NODE_LEGACY_ATTRIBUTE_FILL, "Attribute Fill", NODE_CLASS_ATTRIBUTE, 0);
- node_type_init(&ntype, blender::nodes::geo_node_attribute_fill_init);
- node_type_update(&ntype, blender::nodes::geo_node_attribute_fill_update);
- ntype.geometry_node_execute = blender::nodes::geo_node_attribute_fill_exec;
- ntype.draw_buttons = blender::nodes::geo_node_attribute_fill_layout;
- ntype.declare = blender::nodes::geo_node_attribute_fill_declare;
+ &ntype, GEO_NODE_LEGACY_ATTRIBUTE_FILL, "Attribute Fill", NODE_CLASS_ATTRIBUTE);
+ node_type_init(&ntype, file_ns::node_init);
+ node_type_update(&ntype, file_ns::node_update);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.draw_buttons = file_ns::node_layout;
+ ntype.declare = file_ns::node_declare;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_map_range.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_map_range.cc
index 0ea3bbe1e45..398af087499 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_map_range.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_map_range.cc
@@ -22,25 +22,25 @@
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_legacy_attribute_map_range_cc {
-static void geo_node_attribute_map_range_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::String>("Attribute");
- b.add_input<decl::String>("Result");
- b.add_input<decl::Float>("From Min");
- b.add_input<decl::Float>("From Max").default_value(1.0f);
- b.add_input<decl::Float>("To Min");
- b.add_input<decl::Float>("To Max").default_value(1.0f);
- b.add_input<decl::Float>("Steps").default_value(4.0f);
- b.add_input<decl::Vector>("From Min", "From Min_001");
- b.add_input<decl::Vector>("From Max", "From Max_001").default_value({1.0f, 1.0f, 1.0f});
- b.add_input<decl::Vector>("To Min", "To Min_001");
- b.add_input<decl::Vector>("To Max", "To Max_001").default_value({1.0f, 1.0f, 1.0f});
- b.add_input<decl::Vector>("Steps", "Steps_001").default_value({4.0f, 4.0f, 4.0f});
- b.add_input<decl::Bool>("Clamp");
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::String>(N_("Attribute"));
+ b.add_input<decl::String>(N_("Result"));
+ b.add_input<decl::Float>(N_("From Min"));
+ b.add_input<decl::Float>(N_("From Max")).default_value(1.0f);
+ b.add_input<decl::Float>(N_("To Min"));
+ b.add_input<decl::Float>(N_("To Max")).default_value(1.0f);
+ b.add_input<decl::Float>(N_("Steps")).default_value(4.0f);
+ b.add_input<decl::Vector>(N_("From Min"), "From Min_001");
+ b.add_input<decl::Vector>(N_("From Max"), "From Max_001").default_value({1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Vector>(N_("To Min"), "To Min_001");
+ b.add_input<decl::Vector>(N_("To Max"), "To Max_001").default_value({1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Vector>(N_("Steps"), "Steps_001").default_value({4.0f, 4.0f, 4.0f});
+ b.add_input<decl::Bool>(N_("Clamp"));
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
static void fn_attribute_map_range_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
@@ -49,16 +49,15 @@ static void fn_attribute_map_range_layout(uiLayout *layout, bContext *UNUSED(C),
uiItemR(layout, ptr, "interpolation_type", 0, "", ICON_NONE);
}
-static void geo_node_attribute_map_range_init(bNodeTree *UNUSED(ntree), bNode *node)
+static void node_init(bNodeTree *UNUSED(ntree), bNode *node)
{
- NodeAttributeMapRange *data = (NodeAttributeMapRange *)MEM_callocN(sizeof(NodeAttributeMapRange),
- __func__);
+ NodeAttributeMapRange *data = MEM_cnew<NodeAttributeMapRange>(__func__);
data->data_type = CD_PROP_FLOAT;
data->interpolation_type = NODE_MAP_RANGE_LINEAR;
node->storage = data;
}
-static void geo_node_attribute_map_range_update(bNodeTree *UNUSED(ntree), bNode *node)
+static void node_update(bNodeTree *ntree, bNode *node)
{
NodeAttributeMapRange &node_storage = *(NodeAttributeMapRange *)node->storage;
@@ -78,23 +77,26 @@ static void geo_node_attribute_map_range_update(bNodeTree *UNUSED(ntree), bNode
const CustomDataType data_type = static_cast<CustomDataType>(node_storage.data_type);
- nodeSetSocketAvailability(sock_clamp,
+ nodeSetSocketAvailability(ntree,
+ sock_clamp,
node_storage.interpolation_type == NODE_MAP_RANGE_LINEAR ||
node_storage.interpolation_type == NODE_MAP_RANGE_STEPPED);
- nodeSetSocketAvailability(sock_from_min_float, data_type == CD_PROP_FLOAT);
- nodeSetSocketAvailability(sock_from_max_float, data_type == CD_PROP_FLOAT);
- nodeSetSocketAvailability(sock_to_min_float, data_type == CD_PROP_FLOAT);
- nodeSetSocketAvailability(sock_to_max_float, data_type == CD_PROP_FLOAT);
- nodeSetSocketAvailability(sock_steps_float,
+ nodeSetSocketAvailability(ntree, sock_from_min_float, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(ntree, sock_from_max_float, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(ntree, sock_to_min_float, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(ntree, sock_to_max_float, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(ntree,
+ sock_steps_float,
data_type == CD_PROP_FLOAT &&
node_storage.interpolation_type == NODE_MAP_RANGE_STEPPED);
- nodeSetSocketAvailability(sock_from_min_vector, data_type == CD_PROP_FLOAT3);
- nodeSetSocketAvailability(sock_from_max_vector, data_type == CD_PROP_FLOAT3);
- nodeSetSocketAvailability(sock_to_min_vector, data_type == CD_PROP_FLOAT3);
- nodeSetSocketAvailability(sock_to_max_vector, data_type == CD_PROP_FLOAT3);
- nodeSetSocketAvailability(sock_steps_vector,
+ nodeSetSocketAvailability(ntree, sock_from_min_vector, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(ntree, sock_from_max_vector, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(ntree, sock_to_min_vector, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(ntree, sock_to_max_vector, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(ntree,
+ sock_steps_vector,
data_type == CD_PROP_FLOAT3 &&
node_storage.interpolation_type == NODE_MAP_RANGE_STEPPED);
}
@@ -362,7 +364,7 @@ static void map_range_attribute(GeometryComponent &component, const GeoNodeExecP
const AttributeDomain domain = get_result_domain(component, input_name, result_name);
- GVArrayPtr attribute_input = component.attribute_try_get_for_read(input_name, domain, data_type);
+ GVArray attribute_input = component.attribute_try_get_for_read(input_name, domain, data_type);
if (!attribute_input) {
params.error_message_add(NodeWarningType::Error,
@@ -381,12 +383,12 @@ static void map_range_attribute(GeometryComponent &component, const GeoNodeExecP
switch (data_type) {
case CD_PROP_FLOAT: {
- map_range_float(attribute_input->typed<float>(), attribute_result.as_span<float>(), params);
+ map_range_float(attribute_input.typed<float>(), attribute_result.as_span<float>(), params);
break;
}
case CD_PROP_FLOAT3: {
map_range_float3(
- attribute_input->typed<float3>(), attribute_result.as_span<float3>(), params);
+ attribute_input.typed<float3>(), attribute_result.as_span<float3>(), params);
break;
}
default:
@@ -396,7 +398,7 @@ static void map_range_attribute(GeometryComponent &component, const GeoNodeExecP
attribute_result.save();
}
-static void geo_node_attribute_map_range_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
@@ -413,20 +415,22 @@ static void geo_node_attribute_map_range_exec(GeoNodeExecParams params)
params.set_output("Geometry", geometry_set);
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_legacy_attribute_map_range_cc
void register_node_type_geo_attribute_map_range()
{
+ namespace file_ns = blender::nodes::node_geo_legacy_attribute_map_range_cc;
+
static bNodeType ntype;
geo_node_type_base(
- &ntype, GEO_NODE_LEGACY_ATTRIBUTE_MAP_RANGE, "Attribute Map Range", NODE_CLASS_ATTRIBUTE, 0);
- ntype.geometry_node_execute = blender::nodes::geo_node_attribute_map_range_exec;
- node_type_init(&ntype, blender::nodes::geo_node_attribute_map_range_init);
- node_type_update(&ntype, blender::nodes::geo_node_attribute_map_range_update);
+ &ntype, GEO_NODE_LEGACY_ATTRIBUTE_MAP_RANGE, "Attribute Map Range", NODE_CLASS_ATTRIBUTE);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ node_type_init(&ntype, file_ns::node_init);
+ node_type_update(&ntype, file_ns::node_update);
node_type_storage(
&ntype, "NodeAttributeMapRange", node_free_standard_storage, node_copy_standard_storage);
- ntype.declare = blender::nodes::geo_node_attribute_map_range_declare;
- ntype.draw_buttons = blender::nodes::fn_attribute_map_range_layout;
+ ntype.declare = file_ns::node_declare;
+ ntype.draw_buttons = file_ns::fn_attribute_map_range_layout;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_math.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_math.cc
index efa09215b45..3257d2d7358 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_math.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_math.cc
@@ -25,19 +25,19 @@
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_legacy_attribute_math_cc {
-static void geo_node_attribute_math_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::String>("A");
- b.add_input<decl::Float>("A", "A_001");
- b.add_input<decl::String>("B");
- b.add_input<decl::Float>("B", "B_001");
- b.add_input<decl::String>("C");
- b.add_input<decl::Float>("C", "C_001");
- b.add_input<decl::String>("Result");
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::String>(N_("A"));
+ b.add_input<decl::Float>(N_("A"), "A_001");
+ b.add_input<decl::String>(N_("B"));
+ b.add_input<decl::Float>(N_("B"), "B_001");
+ b.add_input<decl::String>(N_("C"));
+ b.add_input<decl::Float>(N_("C"), "C_001");
+ b.add_input<decl::String>(N_("Result"));
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
static bool operation_use_input_c(const NodeMathOperation operation)
@@ -100,7 +100,7 @@ static bool operation_use_input_b(const NodeMathOperation operation)
return false;
}
-static void geo_node_attribute_math_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
bNode *node = (bNode *)ptr->data;
NodeAttributeMath *node_storage = (NodeAttributeMath *)node->storage;
@@ -119,9 +119,9 @@ static void geo_node_attribute_math_layout(uiLayout *layout, bContext *UNUSED(C)
}
}
-static void geo_node_attribute_math_init(bNodeTree *UNUSED(tree), bNode *node)
+static void node_init(bNodeTree *UNUSED(tree), bNode *node)
{
- NodeAttributeMath *data = (NodeAttributeMath *)MEM_callocN(sizeof(NodeAttributeMath), __func__);
+ NodeAttributeMath *data = MEM_cnew<NodeAttributeMath>(__func__);
data->operation = NODE_MATH_ADD;
data->input_type_a = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE;
@@ -130,7 +130,10 @@ static void geo_node_attribute_math_init(bNodeTree *UNUSED(tree), bNode *node)
node->storage = data;
}
-static void geo_node_math_label(bNodeTree *UNUSED(ntree), bNode *node, char *label, int maxlen)
+static void geo_node_math_label(const bNodeTree *UNUSED(ntree),
+ const bNode *node,
+ char *label,
+ int maxlen)
{
NodeAttributeMath &node_storage = *(NodeAttributeMath *)node->storage;
const char *name;
@@ -141,19 +144,21 @@ static void geo_node_math_label(bNodeTree *UNUSED(ntree), bNode *node, char *lab
BLI_strncpy(label, IFACE_(name), maxlen);
}
-static void geo_node_attribute_math_update(bNodeTree *UNUSED(ntree), bNode *node)
+static void node_update(bNodeTree *ntree, bNode *node)
{
NodeAttributeMath &node_storage = *(NodeAttributeMath *)node->storage;
NodeMathOperation operation = static_cast<NodeMathOperation>(node_storage.operation);
update_attribute_input_socket_availabilities(
- *node, "A", (GeometryNodeAttributeInputMode)node_storage.input_type_a);
+ *ntree, *node, "A", (GeometryNodeAttributeInputMode)node_storage.input_type_a);
update_attribute_input_socket_availabilities(
+ *ntree,
*node,
"B",
(GeometryNodeAttributeInputMode)node_storage.input_type_b,
operation_use_input_b(operation));
update_attribute_input_socket_availabilities(
+ *ntree,
*node,
"C",
(GeometryNodeAttributeInputMode)node_storage.input_type_c,
@@ -250,7 +255,7 @@ static void attribute_math_calc(GeometryComponent &component, const GeoNodeExecP
return;
}
- GVArray_Typed<float> attribute_a = params.get_input_attribute<float>(
+ VArray<float> attribute_a = params.get_input_attribute<float>(
"A", component, result_domain, 0.0f);
MutableSpan<float> result_span = attribute_result.as_span();
@@ -258,10 +263,10 @@ static void attribute_math_calc(GeometryComponent &component, const GeoNodeExecP
/* Note that passing the data with `get_internal_span<float>()` works
* because the attributes were accessed with #CD_PROP_FLOAT. */
if (operation_use_input_b(operation)) {
- GVArray_Typed<float> attribute_b = params.get_input_attribute<float>(
+ VArray<float> attribute_b = params.get_input_attribute<float>(
"B", component, result_domain, 0.0f);
if (operation_use_input_c(operation)) {
- GVArray_Typed<float> attribute_c = params.get_input_attribute<float>(
+ VArray<float> attribute_c = params.get_input_attribute<float>(
"C", component, result_domain, 0.0f);
do_math_operation(attribute_a, attribute_b, attribute_c, result_span, operation);
}
@@ -276,11 +281,11 @@ static void attribute_math_calc(GeometryComponent &component, const GeoNodeExecP
attribute_result.save();
}
-static void geo_node_attribute_math_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
if (geometry_set.has<MeshComponent>()) {
attribute_math_calc(geometry_set.get_component_for_write<MeshComponent>(), params);
@@ -295,20 +300,22 @@ static void geo_node_attribute_math_exec(GeoNodeExecParams params)
params.set_output("Geometry", geometry_set);
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_legacy_attribute_math_cc
void register_node_type_geo_attribute_math()
{
+ namespace file_ns = blender::nodes::node_geo_legacy_attribute_math_cc;
+
static bNodeType ntype;
geo_node_type_base(
- &ntype, GEO_NODE_LEGACY_ATTRIBUTE_MATH, "Attribute Math", NODE_CLASS_ATTRIBUTE, 0);
- ntype.declare = blender::nodes::geo_node_attribute_math_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_attribute_math_exec;
- ntype.draw_buttons = blender::nodes::geo_node_attribute_math_layout;
- node_type_label(&ntype, blender::nodes::geo_node_math_label);
- node_type_update(&ntype, blender::nodes::geo_node_attribute_math_update);
- node_type_init(&ntype, blender::nodes::geo_node_attribute_math_init);
+ &ntype, GEO_NODE_LEGACY_ATTRIBUTE_MATH, "Attribute Math", NODE_CLASS_ATTRIBUTE);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.draw_buttons = file_ns::node_layout;
+ ntype.labelfunc = file_ns::geo_node_math_label;
+ node_type_update(&ntype, file_ns::node_update);
+ node_type_init(&ntype, file_ns::node_init);
node_type_storage(
&ntype, "NodeAttributeMath", node_free_standard_storage, node_copy_standard_storage);
nodeRegisterType(&ntype);
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_mix.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_mix.cc
index 74e05cb997d..c0c30898584 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_mix.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_mix.cc
@@ -25,30 +25,30 @@
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_legacy_attribute_mix_cc {
-static void geo_node_mix_attribute_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::String>("Factor");
- b.add_input<decl::Float>("Factor", "Factor_001")
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::String>(N_("Factor"));
+ b.add_input<decl::Float>(N_("Factor"), "Factor_001")
.default_value(0.5f)
.min(0.0f)
.max(1.0f)
.subtype(PROP_FACTOR);
- b.add_input<decl::String>("A");
- b.add_input<decl::Float>("A", "A_001");
- b.add_input<decl::Vector>("A", "A_002");
- b.add_input<decl::Color>("A", "A_003").default_value({0.5f, 0.5f, 0.5f, 1.0f});
- b.add_input<decl::String>("B");
- b.add_input<decl::Float>("B", "B_001");
- b.add_input<decl::Vector>("B", "B_002");
- b.add_input<decl::Color>("B", "B_003").default_value({0.5f, 0.5f, 0.5f, 1.0f});
- b.add_input<decl::String>("Result");
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::String>(N_("A"));
+ b.add_input<decl::Float>(N_("A"), "A_001");
+ b.add_input<decl::Vector>(N_("A"), "A_002");
+ b.add_input<decl::Color>(N_("A"), "A_003").default_value({0.5f, 0.5f, 0.5f, 1.0f});
+ b.add_input<decl::String>(N_("B"));
+ b.add_input<decl::Float>(N_("B"), "B_001");
+ b.add_input<decl::Vector>(N_("B"), "B_002");
+ b.add_input<decl::Color>(N_("B"), "B_003").default_value({0.5f, 0.5f, 0.5f, 1.0f});
+ b.add_input<decl::String>(N_("Result"));
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
-static void geo_node_attribute_mix_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
@@ -59,10 +59,9 @@ static void geo_node_attribute_mix_layout(uiLayout *layout, bContext *UNUSED(C),
uiItemR(col, ptr, "input_type_b", 0, IFACE_("B"), ICON_NONE);
}
-static void geo_node_attribute_mix_init(bNodeTree *UNUSED(ntree), bNode *node)
+static void node_init(bNodeTree *UNUSED(ntree), bNode *node)
{
- NodeAttributeMix *data = (NodeAttributeMix *)MEM_callocN(sizeof(NodeAttributeMix),
- "attribute mix node");
+ NodeAttributeMix *data = MEM_cnew<NodeAttributeMix>("attribute mix node");
data->blend_type = MA_RAMP_BLEND;
data->input_type_factor = GEO_NODE_ATTRIBUTE_INPUT_FLOAT;
data->input_type_a = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE;
@@ -70,15 +69,15 @@ static void geo_node_attribute_mix_init(bNodeTree *UNUSED(ntree), bNode *node)
node->storage = data;
}
-static void geo_node_attribute_mix_update(bNodeTree *UNUSED(ntree), bNode *node)
+static void node_update(bNodeTree *ntree, bNode *node)
{
NodeAttributeMix *node_storage = (NodeAttributeMix *)node->storage;
update_attribute_input_socket_availabilities(
- *node, "Factor", (GeometryNodeAttributeInputMode)node_storage->input_type_factor);
+ *ntree, *node, "Factor", (GeometryNodeAttributeInputMode)node_storage->input_type_factor);
update_attribute_input_socket_availabilities(
- *node, "A", (GeometryNodeAttributeInputMode)node_storage->input_type_a);
+ *ntree, *node, "A", (GeometryNodeAttributeInputMode)node_storage->input_type_a);
update_attribute_input_socket_availabilities(
- *node, "B", (GeometryNodeAttributeInputMode)node_storage->input_type_b);
+ *ntree, *node, "B", (GeometryNodeAttributeInputMode)node_storage->input_type_b);
}
static void do_mix_operation_float(const int blend_mode,
@@ -144,25 +143,28 @@ static void do_mix_operation(const CustomDataType result_type,
GVMutableArray &attribute_result)
{
if (result_type == CD_PROP_FLOAT) {
+ VMutableArray<float> result = attribute_result.typed<float>();
do_mix_operation_float(blend_mode,
attribute_factor,
attribute_a.typed<float>(),
attribute_b.typed<float>(),
- attribute_result.typed<float>());
+ result);
}
else if (result_type == CD_PROP_FLOAT3) {
+ VMutableArray<float3> result = attribute_result.typed<float3>();
do_mix_operation_float3(blend_mode,
attribute_factor,
attribute_a.typed<float3>(),
attribute_b.typed<float3>(),
- attribute_result.typed<float3>());
+ result);
}
else if (result_type == CD_PROP_COLOR) {
+ VMutableArray<ColorGeometry4f> result = attribute_result.typed<ColorGeometry4f>();
do_mix_operation_color4f(blend_mode,
attribute_factor,
attribute_a.typed<ColorGeometry4f>(),
attribute_b.typed<ColorGeometry4f>(),
- attribute_result.typed<ColorGeometry4f>());
+ result);
}
}
@@ -203,27 +205,27 @@ static void attribute_mix_calc(GeometryComponent &component, const GeoNodeExecPa
return;
}
- GVArray_Typed<float> attribute_factor = params.get_input_attribute<float>(
+ VArray<float> attribute_factor = params.get_input_attribute<float>(
"Factor", component, result_domain, 0.5f);
- GVArrayPtr attribute_a = params.get_input_attribute(
+ GVArray attribute_a = params.get_input_attribute(
"A", component, result_domain, result_type, nullptr);
- GVArrayPtr attribute_b = params.get_input_attribute(
+ GVArray attribute_b = params.get_input_attribute(
"B", component, result_domain, result_type, nullptr);
do_mix_operation(result_type,
node_storage->blend_type,
attribute_factor,
- *attribute_a,
- *attribute_b,
- *attribute_result);
+ attribute_a,
+ attribute_b,
+ attribute_result.varray());
attribute_result.save();
}
-static void geo_node_attribute_mix_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
if (geometry_set.has<MeshComponent>()) {
attribute_mix_calc(geometry_set.get_component_for_write<MeshComponent>(), params);
@@ -238,19 +240,20 @@ static void geo_node_attribute_mix_exec(GeoNodeExecParams params)
params.set_output("Geometry", geometry_set);
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_legacy_attribute_mix_cc
void register_node_type_geo_attribute_mix()
{
+ namespace file_ns = blender::nodes::node_geo_legacy_attribute_mix_cc;
+
static bNodeType ntype;
- geo_node_type_base(
- &ntype, GEO_NODE_LEGACY_ATTRIBUTE_MIX, "Attribute Mix", NODE_CLASS_ATTRIBUTE, 0);
- node_type_init(&ntype, blender::nodes::geo_node_attribute_mix_init);
- node_type_update(&ntype, blender::nodes::geo_node_attribute_mix_update);
- ntype.declare = blender::nodes::geo_node_mix_attribute_declare;
- ntype.draw_buttons = blender::nodes::geo_node_attribute_mix_layout;
+ geo_node_type_base(&ntype, GEO_NODE_LEGACY_ATTRIBUTE_MIX, "Attribute Mix", NODE_CLASS_ATTRIBUTE);
+ node_type_init(&ntype, file_ns::node_init);
+ node_type_update(&ntype, file_ns::node_update);
+ ntype.declare = file_ns::node_declare;
+ ntype.draw_buttons = file_ns::node_layout;
node_type_storage(
&ntype, "NodeAttributeMix", node_free_standard_storage, node_copy_standard_storage);
- ntype.geometry_node_execute = blender::nodes::geo_node_attribute_mix_exec;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_proximity.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_proximity.cc
index 6120118f611..20f500b1bd8 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_proximity.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_proximity.cc
@@ -26,28 +26,26 @@
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_legacy_attribute_proximity_cc {
-static void geo_node_attribute_proximity_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Geometry>("Target");
- b.add_input<decl::String>("Distance");
- b.add_input<decl::String>("Position");
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::Geometry>(N_("Target"));
+ b.add_input<decl::String>(N_("Distance"));
+ b.add_input<decl::String>(N_("Position"));
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
-static void geo_node_attribute_proximity_layout(uiLayout *layout,
- bContext *UNUSED(C),
- PointerRNA *ptr)
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiItemR(layout, ptr, "target_geometry_element", 0, "", ICON_NONE);
}
-static void geo_attribute_proximity_init(bNodeTree *UNUSED(ntree), bNode *node)
+static void node_init(bNodeTree *UNUSED(ntree), bNode *node)
{
- NodeGeometryAttributeProximity *node_storage = (NodeGeometryAttributeProximity *)MEM_callocN(
- sizeof(NodeGeometryAttributeProximity), __func__);
+ NodeGeometryAttributeProximity *node_storage = MEM_cnew<NodeGeometryAttributeProximity>(
+ __func__);
node_storage->target_geometry_element = GEO_NODE_PROXIMITY_TARGET_FACES;
node->storage = node_storage;
@@ -83,7 +81,7 @@ static void calculate_mesh_proximity(const VArray<float3> &positions,
for (int i : range) {
/* Use the distance to the last found point as upper bound to speedup the bvh lookup. */
- nearest.dist_sq = float3::distance_squared(nearest.co, positions[i]);
+ nearest.dist_sq = math::distance_squared(float3(nearest.co), positions[i]);
BLI_bvhtree_find_nearest(
bvh_data.tree, positions[i], &nearest, bvh_data.nearest_callback, &bvh_data);
@@ -153,7 +151,7 @@ static void attribute_calc_proximity(GeometryComponent &component,
if (!position_attribute || (!distance_attribute && !location_attribute)) {
return;
}
- GVArray_Typed<float3> positions{*position_attribute.varray};
+ VArray<float3> positions = position_attribute.varray.typed<float3>();
const NodeGeometryAttributeProximity &storage =
*(const NodeGeometryAttributeProximity *)params.node().storage;
@@ -203,16 +201,16 @@ static void attribute_calc_proximity(GeometryComponent &component,
}
}
-static void geo_node_attribute_proximity_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
GeometrySet geometry_set_target = params.extract_input<GeometrySet>("Target");
- geometry_set = geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
/* This isn't required. This node should be rewritten to handle instances
* for the target geometry set. However, the generic BVH API complicates this. */
- geometry_set_target = geometry_set_realize_instances(geometry_set_target);
+ geometry_set_target = geometry::realize_instances_legacy(geometry_set_target);
if (geometry_set.has<MeshComponent>()) {
attribute_calc_proximity(
@@ -230,22 +228,24 @@ static void geo_node_attribute_proximity_exec(GeoNodeExecParams params)
params.set_output("Geometry", geometry_set);
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_legacy_attribute_proximity_cc
void register_node_type_geo_legacy_attribute_proximity()
{
+ namespace file_ns = blender::nodes::node_geo_legacy_attribute_proximity_cc;
+
static bNodeType ntype;
geo_node_type_base(
- &ntype, GEO_NODE_LEGACY_ATTRIBUTE_PROXIMITY, "Attribute Proximity", NODE_CLASS_ATTRIBUTE, 0);
- node_type_init(&ntype, blender::nodes::geo_attribute_proximity_init);
+ &ntype, GEO_NODE_LEGACY_ATTRIBUTE_PROXIMITY, "Attribute Proximity", NODE_CLASS_ATTRIBUTE);
+ node_type_init(&ntype, file_ns::node_init);
node_type_storage(&ntype,
"NodeGeometryAttributeProximity",
node_free_standard_storage,
node_copy_standard_storage);
- ntype.declare = blender::nodes::geo_node_attribute_proximity_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_attribute_proximity_exec;
- ntype.draw_buttons = blender::nodes::geo_node_attribute_proximity_layout;
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.draw_buttons = file_ns::node_layout;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_randomize.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_randomize.cc
index 2e6ba456725..92a946b225b 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_randomize.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_randomize.cc
@@ -25,39 +25,70 @@
namespace blender::nodes {
-static void geo_node_legacy_attribute_randomize_declare(NodeDeclarationBuilder &b)
+Array<uint32_t> get_geometry_element_ids_as_uints(const GeometryComponent &component,
+ const AttributeDomain domain)
+{
+ const int domain_size = component.attribute_domain_size(domain);
+
+ /* Hash the reserved name attribute "id" as a (hopefully) stable seed for each point. */
+ GVArray hash_attribute = component.attribute_try_get_for_read("id", domain);
+ Array<uint32_t> hashes(domain_size);
+ if (hash_attribute) {
+ BLI_assert(hashes.size() == hash_attribute.size());
+ const CPPType &cpp_type = hash_attribute.type();
+ BLI_assert(cpp_type.is_hashable());
+ GVArray_GSpan items{hash_attribute};
+ threading::parallel_for(hashes.index_range(), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ hashes[i] = cpp_type.hash(items[i]);
+ }
+ });
+ }
+ else {
+ /* If there is no "id" attribute for per-point variation, just create it here. */
+ RandomNumberGenerator rng(0);
+ for (const int i : hashes.index_range()) {
+ hashes[i] = rng.get_uint32();
+ }
+ }
+
+ return hashes;
+}
+
+} // namespace blender::nodes
+
+namespace blender::nodes::node_geo_legacy_attribute_randomize_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::String>("Attribute");
- b.add_input<decl::Vector>("Min");
- b.add_input<decl::Vector>("Max").default_value({1.0f, 1.0f, 1.0f});
- b.add_input<decl::Float>("Min", "Min_001");
- b.add_input<decl::Float>("Max", "Max_001").default_value(1.0f);
- b.add_input<decl::Int>("Min", "Min_002").min(-100000).max(100000);
- b.add_input<decl::Int>("Max", "Max_002").default_value(100).min(-100000).max(100000);
- b.add_input<decl::Int>("Seed").min(-10000).max(10000);
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::String>(N_("Attribute"));
+ b.add_input<decl::Vector>(N_("Min"));
+ b.add_input<decl::Vector>(N_("Max")).default_value({1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Float>(N_("Min"), "Min_001");
+ b.add_input<decl::Float>(N_("Max"), "Max_001").default_value(1.0f);
+ b.add_input<decl::Int>(N_("Min"), "Min_002").min(-100000).max(100000);
+ b.add_input<decl::Int>(N_("Max"), "Max_002").default_value(100).min(-100000).max(100000);
+ b.add_input<decl::Int>(N_("Seed")).min(-10000).max(10000);
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
-static void geo_node_legacy_attribute_random_layout(uiLayout *layout,
- bContext *UNUSED(C),
- PointerRNA *ptr)
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE);
uiItemR(layout, ptr, "operation", 0, "", ICON_NONE);
}
-static void geo_node_legacy_attribute_randomize_init(bNodeTree *UNUSED(tree), bNode *node)
+static void node_init(bNodeTree *UNUSED(tree), bNode *node)
{
- NodeAttributeRandomize *data = (NodeAttributeRandomize *)MEM_callocN(
- sizeof(NodeAttributeRandomize), __func__);
+ NodeAttributeRandomize *data = MEM_cnew<NodeAttributeRandomize>(__func__);
data->data_type = CD_PROP_FLOAT;
data->domain = ATTR_DOMAIN_POINT;
data->operation = GEO_NODE_ATTRIBUTE_RANDOMIZE_REPLACE_CREATE;
node->storage = data;
}
-static void geo_node_legacy_attribute_randomize_update(bNodeTree *UNUSED(ntree), bNode *node)
+static void node_update(bNodeTree *ntree, bNode *node)
{
bNodeSocket *sock_min_vector = (bNodeSocket *)BLI_findlink(&node->inputs, 2);
bNodeSocket *sock_max_vector = sock_min_vector->next;
@@ -68,12 +99,12 @@ static void geo_node_legacy_attribute_randomize_update(bNodeTree *UNUSED(ntree),
const NodeAttributeRandomize &storage = *(const NodeAttributeRandomize *)node->storage;
const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type);
- nodeSetSocketAvailability(sock_min_vector, data_type == CD_PROP_FLOAT3);
- nodeSetSocketAvailability(sock_max_vector, data_type == CD_PROP_FLOAT3);
- nodeSetSocketAvailability(sock_min_float, data_type == CD_PROP_FLOAT);
- nodeSetSocketAvailability(sock_max_float, data_type == CD_PROP_FLOAT);
- nodeSetSocketAvailability(sock_min_int, data_type == CD_PROP_INT32);
- nodeSetSocketAvailability(sock_max_int, data_type == CD_PROP_INT32);
+ nodeSetSocketAvailability(ntree, sock_min_vector, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(ntree, sock_max_vector, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(ntree, sock_min_float, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(ntree, sock_max_float, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(ntree, sock_min_int, data_type == CD_PROP_INT32);
+ nodeSetSocketAvailability(ntree, sock_max_int, data_type == CD_PROP_INT32);
}
template<typename T>
@@ -174,36 +205,6 @@ static void randomize_attribute_bool(MutableSpan<bool> span,
});
}
-Array<uint32_t> get_geometry_element_ids_as_uints(const GeometryComponent &component,
- const AttributeDomain domain)
-{
- const int domain_size = component.attribute_domain_size(domain);
-
- /* Hash the reserved name attribute "id" as a (hopefully) stable seed for each point. */
- GVArrayPtr hash_attribute = component.attribute_try_get_for_read("id", domain);
- Array<uint32_t> hashes(domain_size);
- if (hash_attribute) {
- BLI_assert(hashes.size() == hash_attribute->size());
- const CPPType &cpp_type = hash_attribute->type();
- BLI_assert(cpp_type.is_hashable());
- GVArray_GSpan items{*hash_attribute};
- threading::parallel_for(hashes.index_range(), 512, [&](IndexRange range) {
- for (const int i : range) {
- hashes[i] = cpp_type.hash(items[i]);
- }
- });
- }
- else {
- /* If there is no "id" attribute for per-point variation, just create it here. */
- RandomNumberGenerator rng(0);
- for (const int i : hashes.index_range()) {
- hashes[i] = rng.get_uint32();
- }
- }
-
- return hashes;
-}
-
static AttributeDomain get_result_domain(const GeometryComponent &component,
const GeoNodeExecParams &params,
const StringRef name)
@@ -280,12 +281,12 @@ static void randomize_attribute_on_component(GeometryComponent &component,
attribute.save();
}
-static void geo_node_legacy_random_attribute_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
const std::string attribute_name = params.get_input<std::string>("Attribute");
if (attribute_name.empty()) {
- params.set_output("Geometry", geometry_set);
+ params.set_default_remaining_outputs();
return;
}
const int seed = params.get_input<int>("Seed");
@@ -294,7 +295,7 @@ static void geo_node_legacy_random_attribute_exec(GeoNodeExecParams params)
const GeometryNodeAttributeRandomizeMode operation =
static_cast<GeometryNodeAttributeRandomizeMode>(storage.operation);
- geometry_set = geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
if (geometry_set.has<MeshComponent>()) {
randomize_attribute_on_component(geometry_set.get_component_for_write<MeshComponent>(),
@@ -324,20 +325,22 @@ static void geo_node_legacy_random_attribute_exec(GeoNodeExecParams params)
params.set_output("Geometry", geometry_set);
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_legacy_attribute_randomize_cc
void register_node_type_geo_legacy_attribute_randomize()
{
+ namespace file_ns = blender::nodes::node_geo_legacy_attribute_randomize_cc;
+
static bNodeType ntype;
geo_node_type_base(
- &ntype, GEO_NODE_LEGACY_ATTRIBUTE_RANDOMIZE, "Attribute Randomize", NODE_CLASS_ATTRIBUTE, 0);
- node_type_init(&ntype, blender::nodes::geo_node_legacy_attribute_randomize_init);
- node_type_update(&ntype, blender::nodes::geo_node_legacy_attribute_randomize_update);
+ &ntype, GEO_NODE_LEGACY_ATTRIBUTE_RANDOMIZE, "Attribute Randomize", NODE_CLASS_ATTRIBUTE);
+ node_type_init(&ntype, file_ns::node_init);
+ node_type_update(&ntype, file_ns::node_update);
- ntype.declare = blender::nodes::geo_node_legacy_attribute_randomize_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_legacy_random_attribute_exec;
- ntype.draw_buttons = blender::nodes::geo_node_legacy_attribute_random_layout;
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.draw_buttons = file_ns::node_layout;
node_type_storage(
&ntype, "NodeAttributeRandomize", node_free_standard_storage, node_copy_standard_storage);
nodeRegisterType(&ntype);
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_sample_texture.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_sample_texture.cc
index 52f97475941..ae034d152be 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_sample_texture.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_sample_texture.cc
@@ -28,15 +28,15 @@
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_legacy_attribute_sample_texture_cc {
-static void geo_node_attribute_sample_texture_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Texture>("Texture").hide_label();
- b.add_input<decl::String>("Mapping");
- b.add_input<decl::String>("Result");
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::Texture>(N_("Texture")).hide_label();
+ b.add_input<decl::String>(N_("Mapping"));
+ b.add_input<decl::String>(N_("Result"));
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
static AttributeDomain get_result_domain(const GeometryComponent &component,
@@ -82,7 +82,7 @@ static void execute_on_component(GeometryComponent &component, const GeoNodeExec
return;
}
- GVArray_Typed<float3> mapping_attribute = component.attribute_get_for_read<float3>(
+ VArray<float3> mapping_attribute = component.attribute_get_for_read<float3>(
mapping_name, result_domain, {0, 0, 0});
MutableSpan<ColorGeometry4f> colors = attribute_out.as_span();
@@ -100,11 +100,11 @@ static void execute_on_component(GeometryComponent &component, const GeoNodeExec
attribute_out.save();
}
-static void geo_node_attribute_sample_texture_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
if (geometry_set.has<MeshComponent>()) {
execute_on_component(geometry_set.get_component_for_write<MeshComponent>(), params);
@@ -119,19 +119,20 @@ static void geo_node_attribute_sample_texture_exec(GeoNodeExecParams params)
params.set_output("Geometry", geometry_set);
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_legacy_attribute_sample_texture_cc
void register_node_type_geo_sample_texture()
{
+ namespace file_ns = blender::nodes::node_geo_legacy_attribute_sample_texture_cc;
+
static bNodeType ntype;
geo_node_type_base(&ntype,
GEO_NODE_LEGACY_ATTRIBUTE_SAMPLE_TEXTURE,
"Attribute Sample Texture",
- NODE_CLASS_ATTRIBUTE,
- 0);
+ NODE_CLASS_ATTRIBUTE);
node_type_size_preset(&ntype, NODE_SIZE_LARGE);
- ntype.declare = blender::nodes::geo_node_attribute_sample_texture_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_attribute_sample_texture_exec;
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_separate_xyz.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_separate_xyz.cc
index de0090406c6..960ec540556 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_separate_xyz.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_separate_xyz.cc
@@ -19,41 +19,38 @@
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_legacy_attribute_separate_xyz_cc {
-static void geo_node_attribute_separate_xyz_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::String>("Vector");
- b.add_input<decl::Vector>("Vector", "Vector_001");
- b.add_input<decl::String>("Result X");
- b.add_input<decl::String>("Result Y");
- b.add_input<decl::String>("Result Z");
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::String>(N_("Vector"));
+ b.add_input<decl::Vector>(N_("Vector"), "Vector_001");
+ b.add_input<decl::String>(N_("Result X"));
+ b.add_input<decl::String>(N_("Result Y"));
+ b.add_input<decl::String>(N_("Result Z"));
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
-static void geo_node_attribute_separate_xyz_layout(uiLayout *layout,
- bContext *UNUSED(C),
- PointerRNA *ptr)
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
uiItemR(layout, ptr, "input_type", 0, IFACE_("Type"), ICON_NONE);
}
-static void geo_node_attribute_separate_xyz_init(bNodeTree *UNUSED(tree), bNode *node)
+static void node_init(bNodeTree *UNUSED(tree), bNode *node)
{
- NodeAttributeSeparateXYZ *data = (NodeAttributeSeparateXYZ *)MEM_callocN(
- sizeof(NodeAttributeSeparateXYZ), __func__);
+ NodeAttributeSeparateXYZ *data = MEM_cnew<NodeAttributeSeparateXYZ>(__func__);
data->input_type = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE;
node->storage = data;
}
-static void geo_node_attribute_separate_xyz_update(bNodeTree *UNUSED(ntree), bNode *node)
+static void node_update(bNodeTree *ntree, bNode *node)
{
NodeAttributeSeparateXYZ *node_storage = (NodeAttributeSeparateXYZ *)node->storage;
update_attribute_input_socket_availabilities(
- *node, "Vector", (GeometryNodeAttributeInputMode)node_storage->input_type);
+ *ntree, *node, "Vector", (GeometryNodeAttributeInputMode)node_storage->input_type);
}
static void extract_input(const int index, const Span<float3> &input, MutableSpan<float> result)
@@ -106,9 +103,9 @@ static void separate_attribute(GeometryComponent &component, const GeoNodeExecPa
const AttributeDomain result_domain = get_result_domain(
component, params, result_name_x, result_name_y, result_name_z);
- GVArray_Typed<float3> attribute_input = params.get_input_attribute<float3>(
+ VArray<float3> attribute_input = params.get_input_attribute<float3>(
"Vector", component, result_domain, {0, 0, 0});
- VArray_Span<float3> input_span{*attribute_input};
+ VArray_Span<float3> input_span{attribute_input};
OutputAttribute_Typed<float> attribute_result_x =
component.attribute_try_get_for_output_only<float>(result_name_x, result_domain);
@@ -132,11 +129,11 @@ static void separate_attribute(GeometryComponent &component, const GeoNodeExecPa
}
}
-static void geo_node_attribute_separate_xyz_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
if (geometry_set.has<MeshComponent>()) {
separate_attribute(geometry_set.get_component_for_write<MeshComponent>(), params);
@@ -151,23 +148,24 @@ static void geo_node_attribute_separate_xyz_exec(GeoNodeExecParams params)
params.set_output("Geometry", geometry_set);
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_legacy_attribute_separate_xyz_cc
void register_node_type_geo_attribute_separate_xyz()
{
+ namespace file_ns = blender::nodes::node_geo_legacy_attribute_separate_xyz_cc;
+
static bNodeType ntype;
geo_node_type_base(&ntype,
GEO_NODE_LEGACY_ATTRIBUTE_SEPARATE_XYZ,
"Attribute Separate XYZ",
- NODE_CLASS_ATTRIBUTE,
- 0);
- ntype.declare = blender::nodes::geo_node_attribute_separate_xyz_declare;
- node_type_init(&ntype, blender::nodes::geo_node_attribute_separate_xyz_init);
- node_type_update(&ntype, blender::nodes::geo_node_attribute_separate_xyz_update);
+ NODE_CLASS_ATTRIBUTE);
+ ntype.declare = file_ns::node_declare;
+ node_type_init(&ntype, file_ns::node_init);
+ node_type_update(&ntype, file_ns::node_update);
node_type_storage(
&ntype, "NodeAttributeSeparateXYZ", node_free_standard_storage, node_copy_standard_storage);
- ntype.geometry_node_execute = blender::nodes::geo_node_attribute_separate_xyz_exec;
- ntype.draw_buttons = blender::nodes::geo_node_attribute_separate_xyz_layout;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.draw_buttons = file_ns::node_layout;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_transfer.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_transfer.cc
index f187ee39b94..a85a7c56cb9 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_transfer.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_transfer.cc
@@ -29,20 +29,18 @@
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_legacy_attribute_transfer_cc {
-static void geo_node_attribute_transfer_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Geometry>("Source Geometry");
- b.add_input<decl::String>("Source");
- b.add_input<decl::String>("Destination");
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::Geometry>(N_("Source Geometry"));
+ b.add_input<decl::String>(N_("Source"));
+ b.add_input<decl::String>(N_("Destination"));
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
-static void geo_node_attribute_transfer_layout(uiLayout *layout,
- bContext *UNUSED(C),
- PointerRNA *ptr)
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
@@ -50,10 +48,9 @@ static void geo_node_attribute_transfer_layout(uiLayout *layout,
uiItemR(layout, ptr, "mapping", 0, IFACE_("Mapping"), ICON_NONE);
}
-static void geo_node_attribute_transfer_init(bNodeTree *UNUSED(tree), bNode *node)
+static void node_init(bNodeTree *UNUSED(tree), bNode *node)
{
- NodeGeometryAttributeTransfer *data = (NodeGeometryAttributeTransfer *)MEM_callocN(
- sizeof(NodeGeometryAttributeTransfer), __func__);
+ NodeGeometryAttributeTransfer *data = MEM_cnew<NodeGeometryAttributeTransfer>(__func__);
data->domain = ATTR_DOMAIN_AUTO;
node->storage = data;
}
@@ -232,7 +229,7 @@ static void get_closest_mesh_corners(const Mesh &mesh,
const MLoop &loop = mesh.mloop[loop_index];
const int vertex_index = loop.v;
const MVert &mvert = mesh.mvert[vertex_index];
- const float distance_sq = float3::distance_squared(position, mvert.co);
+ const float distance_sq = math::distance_squared(position, float3(mvert.co));
if (distance_sq < min_distance_sq) {
min_distance_sq = distance_sq;
closest_loop_index = loop_index;
@@ -284,7 +281,8 @@ static void transfer_attribute_nearest_face_interpolated(const GeometrySet &src_
Array<float3> positions(tot_samples);
get_closest_mesh_looptris(*mesh, dst_positions, looptri_indices, {}, positions);
- bke::mesh_surface_sample::MeshAttributeInterpolator interp(mesh, positions, looptri_indices);
+ bke::mesh_surface_sample::MeshAttributeInterpolator interp(
+ mesh, IndexMask(tot_samples), positions, looptri_indices);
interp.sample_attribute(
src_attribute, dst_attribute, bke::mesh_surface_sample::eAttributeMapMode::INTERPOLATED);
@@ -404,15 +402,15 @@ static void transfer_attribute_nearest(const GeometrySet &src_geometry,
data_type);
for (const int i : IndexRange(tot_samples)) {
if (pointcloud_distances_sq[i] < mesh_distances_sq[i]) {
- /* Point-cloud point is closer. */
+ /* Point cloud point is closer. */
const int index = pointcloud_indices[i];
- pointcloud_src_attribute.varray->get(index, buffer);
+ pointcloud_src_attribute.varray.get(index, buffer);
dst_attribute->set_by_relocate(i, buffer);
}
else {
/* Mesh element is closer. */
const int index = mesh_indices[i];
- mesh_src_attribute.varray->get(index, buffer);
+ mesh_src_attribute.varray.get(index, buffer);
dst_attribute->set_by_relocate(i, buffer);
}
}
@@ -423,7 +421,7 @@ static void transfer_attribute_nearest(const GeometrySet &src_geometry,
src_name, data_type);
for (const int i : IndexRange(tot_samples)) {
const int index = pointcloud_indices[i];
- src_attribute.varray->get(index, buffer);
+ src_attribute.varray.get(index, buffer);
dst_attribute->set_by_relocate(i, buffer);
}
}
@@ -433,7 +431,7 @@ static void transfer_attribute_nearest(const GeometrySet &src_geometry,
data_type);
for (const int i : IndexRange(tot_samples)) {
const int index = mesh_indices[i];
- src_attribute.varray->get(index, buffer);
+ src_attribute.varray.get(index, buffer);
dst_attribute->set_by_relocate(i, buffer);
}
}
@@ -459,16 +457,16 @@ static void transfer_attribute(const GeoNodeExecParams &params,
const AttributeDomain dst_domain = (input_domain == ATTR_DOMAIN_AUTO) ? auto_domain :
input_domain;
- GVArray_Typed<float3> dst_positions = dst_component.attribute_get_for_read<float3>(
+ VArray<float3> dst_positions = dst_component.attribute_get_for_read<float3>(
"position", dst_domain, {0, 0, 0});
switch (mapping) {
- case GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED: {
+ case GEO_NODE_LEGACY_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED: {
transfer_attribute_nearest_face_interpolated(
src_geometry, dst_component, dst_positions, dst_domain, data_type, src_name, dst_name);
break;
}
- case GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST: {
+ case GEO_NODE_LEGACY_ATTRIBUTE_TRANSFER_NEAREST: {
transfer_attribute_nearest(
src_geometry, dst_component, dst_positions, dst_domain, data_type, src_name, dst_name);
break;
@@ -476,7 +474,7 @@ static void transfer_attribute(const GeoNodeExecParams &params,
}
}
-static void geo_node_attribute_transfer_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet dst_geometry_set = params.extract_input<GeometrySet>("Geometry");
GeometrySet src_geometry_set = params.extract_input<GeometrySet>("Source Geometry");
@@ -484,12 +482,12 @@ static void geo_node_attribute_transfer_exec(GeoNodeExecParams params)
const std::string dst_attribute_name = params.extract_input<std::string>("Destination");
if (src_attribute_name.empty() || dst_attribute_name.empty()) {
- params.set_output("Geometry", dst_geometry_set);
+ params.set_default_remaining_outputs();
return;
}
- dst_geometry_set = bke::geometry_set_realize_instances(dst_geometry_set);
- src_geometry_set = bke::geometry_set_realize_instances(src_geometry_set);
+ dst_geometry_set = geometry::realize_instances_legacy(dst_geometry_set);
+ src_geometry_set = geometry::realize_instances_legacy(src_geometry_set);
if (dst_geometry_set.has<MeshComponent>()) {
transfer_attribute(params,
@@ -509,21 +507,23 @@ static void geo_node_attribute_transfer_exec(GeoNodeExecParams params)
params.set_output("Geometry", dst_geometry_set);
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_legacy_attribute_transfer_cc
-void register_node_type_geo_attribute_transfer()
+void register_node_type_geo_legacy_attribute_transfer()
{
+ namespace file_ns = blender::nodes::node_geo_legacy_attribute_transfer_cc;
+
static bNodeType ntype;
geo_node_type_base(
- &ntype, GEO_NODE_LEGACY_ATTRIBUTE_TRANSFER, "Attribute Transfer", NODE_CLASS_ATTRIBUTE, 0);
- node_type_init(&ntype, blender::nodes::geo_node_attribute_transfer_init);
+ &ntype, GEO_NODE_LEGACY_ATTRIBUTE_TRANSFER, "Attribute Transfer", NODE_CLASS_ATTRIBUTE);
+ node_type_init(&ntype, file_ns::node_init);
node_type_storage(&ntype,
"NodeGeometryAttributeTransfer",
node_free_standard_storage,
node_copy_standard_storage);
- ntype.declare = blender::nodes::geo_node_attribute_transfer_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_attribute_transfer_exec;
- ntype.draw_buttons = blender::nodes::geo_node_attribute_transfer_layout;
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.draw_buttons = file_ns::node_layout;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_vector_math.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_vector_math.cc
index 59903050f88..5b3c3c05a6a 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_vector_math.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_vector_math.cc
@@ -26,21 +26,21 @@
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_legacy_attribute_vector_math_cc {
-static void geo_node_attribute_vector_math_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::String>("A");
- b.add_input<decl::Vector>("A", "A_001");
- b.add_input<decl::String>("B");
- b.add_input<decl::Vector>("B", "B_001");
- b.add_input<decl::Float>("B", "B_002");
- b.add_input<decl::String>("C");
- b.add_input<decl::Vector>("C", "C_001");
- b.add_input<decl::Float>("C", "C_002");
- b.add_input<decl::String>("Result");
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::String>(N_("A"));
+ b.add_input<decl::Vector>(N_("A"), "A_001");
+ b.add_input<decl::String>(N_("B"));
+ b.add_input<decl::Vector>(N_("B"), "B_001");
+ b.add_input<decl::Float>(N_("B"), "B_002");
+ b.add_input<decl::String>(N_("C"));
+ b.add_input<decl::Vector>(N_("C"), "C_001");
+ b.add_input<decl::Float>(N_("C"), "C_002");
+ b.add_input<decl::String>(N_("Result"));
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
static bool operation_use_input_b(const NodeVectorMathOperation operation)
@@ -66,9 +66,7 @@ static bool operation_use_input_c(const NodeVectorMathOperation operation)
NODE_VECTOR_MATH_MULTIPLY_ADD);
}
-static void geo_node_attribute_vector_math_layout(uiLayout *layout,
- bContext *UNUSED(C),
- PointerRNA *ptr)
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
bNode *node = (bNode *)ptr->data;
const NodeAttributeVectorMath &node_storage = *(NodeAttributeVectorMath *)node->storage;
@@ -103,10 +101,9 @@ static CustomDataType operation_get_read_type_c(const NodeVectorMathOperation op
return CD_PROP_FLOAT3;
}
-static void geo_node_attribute_vector_math_init(bNodeTree *UNUSED(tree), bNode *node)
+static void node_init(bNodeTree *UNUSED(tree), bNode *node)
{
- NodeAttributeVectorMath *data = (NodeAttributeVectorMath *)MEM_callocN(
- sizeof(NodeAttributeVectorMath), __func__);
+ NodeAttributeVectorMath *data = MEM_cnew<NodeAttributeVectorMath>(__func__);
data->operation = NODE_VECTOR_MATH_ADD;
data->input_type_a = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE;
@@ -152,8 +149,8 @@ static CustomDataType operation_get_result_type(const NodeVectorMathOperation op
return CD_PROP_FLOAT3;
}
-static void geo_node_vector_math_label(bNodeTree *UNUSED(ntree),
- bNode *node,
+static void geo_node_vector_math_label(const bNodeTree *UNUSED(ntree),
+ const bNode *node,
char *label,
int maxlen)
{
@@ -166,19 +163,21 @@ static void geo_node_vector_math_label(bNodeTree *UNUSED(ntree),
BLI_snprintf(label, maxlen, IFACE_("Vector %s"), IFACE_(name));
}
-static void geo_node_attribute_vector_math_update(bNodeTree *UNUSED(ntree), bNode *node)
+static void node_update(bNodeTree *ntree, bNode *node)
{
const NodeAttributeVectorMath *node_storage = (NodeAttributeVectorMath *)node->storage;
const NodeVectorMathOperation operation = (const NodeVectorMathOperation)node_storage->operation;
update_attribute_input_socket_availabilities(
- *node, "A", (GeometryNodeAttributeInputMode)node_storage->input_type_a);
+ *ntree, *node, "A", (GeometryNodeAttributeInputMode)node_storage->input_type_a);
update_attribute_input_socket_availabilities(
+ *ntree,
*node,
"B",
(GeometryNodeAttributeInputMode)node_storage->input_type_b,
operation_use_input_b(operation));
update_attribute_input_socket_availabilities(
+ *ntree,
*node,
"C",
(GeometryNodeAttributeInputMode)node_storage->input_type_c,
@@ -187,7 +186,7 @@ static void geo_node_attribute_vector_math_update(bNodeTree *UNUSED(ntree), bNod
static void do_math_operation_fl3_fl3_to_fl3(const VArray<float3> &input_a,
const VArray<float3> &input_b,
- VMutableArray<float3> &result,
+ const VMutableArray<float3> &result,
const NodeVectorMathOperation operation)
{
const int size = input_a.size();
@@ -218,7 +217,7 @@ static void do_math_operation_fl3_fl3_to_fl3(const VArray<float3> &input_a,
static void do_math_operation_fl3_fl3_fl3_to_fl3(const VArray<float3> &input_a,
const VArray<float3> &input_b,
const VArray<float3> &input_c,
- VMutableArray<float3> &result,
+ const VMutableArray<float3> &result,
const NodeVectorMathOperation operation)
{
const int size = input_a.size();
@@ -251,7 +250,7 @@ static void do_math_operation_fl3_fl3_fl3_to_fl3(const VArray<float3> &input_a,
static void do_math_operation_fl3_fl3_fl_to_fl3(const VArray<float3> &input_a,
const VArray<float3> &input_b,
const VArray<float> &input_c,
- VMutableArray<float3> &result,
+ const VMutableArray<float3> &result,
const NodeVectorMathOperation operation)
{
const int size = input_a.size();
@@ -283,7 +282,7 @@ static void do_math_operation_fl3_fl3_fl_to_fl3(const VArray<float3> &input_a,
static void do_math_operation_fl3_fl3_to_fl(const VArray<float3> &input_a,
const VArray<float3> &input_b,
- VMutableArray<float> &result,
+ const VMutableArray<float> &result,
const NodeVectorMathOperation operation)
{
const int size = input_a.size();
@@ -313,7 +312,7 @@ static void do_math_operation_fl3_fl3_to_fl(const VArray<float3> &input_a,
static void do_math_operation_fl3_fl_to_fl3(const VArray<float3> &input_a,
const VArray<float> &input_b,
- VMutableArray<float3> &result,
+ const VMutableArray<float3> &result,
const NodeVectorMathOperation operation)
{
const int size = input_a.size();
@@ -342,7 +341,7 @@ static void do_math_operation_fl3_fl_to_fl3(const VArray<float3> &input_a,
}
static void do_math_operation_fl3_to_fl3(const VArray<float3> &input_a,
- VMutableArray<float3> &result,
+ const VMutableArray<float3> &result,
const NodeVectorMathOperation operation)
{
const int size = input_a.size();
@@ -369,7 +368,7 @@ static void do_math_operation_fl3_to_fl3(const VArray<float3> &input_a,
}
static void do_math_operation_fl3_to_fl(const VArray<float3> &input_a,
- VMutableArray<float> &result,
+ const VMutableArray<float> &result,
const NodeVectorMathOperation operation)
{
const int size = input_a.size();
@@ -437,13 +436,13 @@ static void attribute_vector_math_calc(GeometryComponent &component,
const AttributeDomain result_domain = get_result_domain(
component, params, operation, result_name);
- GVArrayPtr attribute_a = params.get_input_attribute(
+ GVArray attribute_a = params.get_input_attribute(
"A", component, result_domain, read_type_a, nullptr);
if (!attribute_a) {
return;
}
- GVArrayPtr attribute_b;
- GVArrayPtr attribute_c;
+ GVArray attribute_b;
+ GVArray attribute_c;
if (use_input_b) {
attribute_b = params.get_input_attribute("B", component, result_domain, read_type_b, nullptr);
if (!attribute_b) {
@@ -476,26 +475,26 @@ static void attribute_vector_math_calc(GeometryComponent &component,
case NODE_VECTOR_MATH_MODULO:
case NODE_VECTOR_MATH_MINIMUM:
case NODE_VECTOR_MATH_MAXIMUM:
- do_math_operation_fl3_fl3_to_fl3(attribute_a->typed<float3>(),
- attribute_b->typed<float3>(),
- attribute_result->typed<float3>(),
+ do_math_operation_fl3_fl3_to_fl3(attribute_a.typed<float3>(),
+ attribute_b.typed<float3>(),
+ attribute_result.varray().typed<float3>(),
operation);
break;
case NODE_VECTOR_MATH_DOT_PRODUCT:
case NODE_VECTOR_MATH_DISTANCE:
- do_math_operation_fl3_fl3_to_fl(attribute_a->typed<float3>(),
- attribute_b->typed<float3>(),
- attribute_result->typed<float>(),
+ do_math_operation_fl3_fl3_to_fl(attribute_a.typed<float3>(),
+ attribute_b.typed<float3>(),
+ attribute_result.varray().typed<float>(),
operation);
break;
case NODE_VECTOR_MATH_LENGTH:
do_math_operation_fl3_to_fl(
- attribute_a->typed<float3>(), attribute_result->typed<float>(), operation);
+ attribute_a.typed<float3>(), attribute_result.varray().typed<float>(), operation);
break;
case NODE_VECTOR_MATH_SCALE:
- do_math_operation_fl3_fl_to_fl3(attribute_a->typed<float3>(),
- attribute_b->typed<float>(),
- attribute_result->typed<float3>(),
+ do_math_operation_fl3_fl_to_fl3(attribute_a.typed<float3>(),
+ attribute_b.typed<float>(),
+ attribute_result.varray().typed<float3>(),
operation);
break;
case NODE_VECTOR_MATH_NORMALIZE:
@@ -507,33 +506,33 @@ static void attribute_vector_math_calc(GeometryComponent &component,
case NODE_VECTOR_MATH_COSINE:
case NODE_VECTOR_MATH_TANGENT:
do_math_operation_fl3_to_fl3(
- attribute_a->typed<float3>(), attribute_result->typed<float3>(), operation);
+ attribute_a.typed<float3>(), attribute_result.varray().typed<float3>(), operation);
break;
case NODE_VECTOR_MATH_WRAP:
case NODE_VECTOR_MATH_FACEFORWARD:
case NODE_VECTOR_MATH_MULTIPLY_ADD:
- do_math_operation_fl3_fl3_fl3_to_fl3(attribute_a->typed<float3>(),
- attribute_b->typed<float3>(),
- attribute_c->typed<float3>(),
- attribute_result->typed<float3>(),
+ do_math_operation_fl3_fl3_fl3_to_fl3(attribute_a.typed<float3>(),
+ attribute_b.typed<float3>(),
+ attribute_c.typed<float3>(),
+ attribute_result.varray().typed<float3>(),
operation);
break;
case NODE_VECTOR_MATH_REFRACT:
- do_math_operation_fl3_fl3_fl_to_fl3(attribute_a->typed<float3>(),
- attribute_b->typed<float3>(),
- attribute_c->typed<float>(),
- attribute_result->typed<float3>(),
+ do_math_operation_fl3_fl3_fl_to_fl3(attribute_a.typed<float3>(),
+ attribute_b.typed<float3>(),
+ attribute_c.typed<float>(),
+ attribute_result.varray().typed<float3>(),
operation);
break;
}
attribute_result.save();
}
-static void geo_node_attribute_vector_math_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
if (geometry_set.has<MeshComponent>()) {
attribute_vector_math_calc(geometry_set.get_component_for_write<MeshComponent>(), params);
@@ -549,23 +548,24 @@ static void geo_node_attribute_vector_math_exec(GeoNodeExecParams params)
params.set_output("Geometry", geometry_set);
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_legacy_attribute_vector_math_cc
void register_node_type_geo_attribute_vector_math()
{
+ namespace file_ns = blender::nodes::node_geo_legacy_attribute_vector_math_cc;
+
static bNodeType ntype;
geo_node_type_base(&ntype,
GEO_NODE_LEGACY_ATTRIBUTE_VECTOR_MATH,
"Attribute Vector Math",
- NODE_CLASS_ATTRIBUTE,
- 0);
- ntype.declare = blender::nodes::geo_node_attribute_vector_math_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_attribute_vector_math_exec;
- ntype.draw_buttons = blender::nodes::geo_node_attribute_vector_math_layout;
- node_type_label(&ntype, blender::nodes::geo_node_vector_math_label);
- node_type_update(&ntype, blender::nodes::geo_node_attribute_vector_math_update);
- node_type_init(&ntype, blender::nodes::geo_node_attribute_vector_math_init);
+ NODE_CLASS_ATTRIBUTE);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.draw_buttons = file_ns::node_layout;
+ ntype.labelfunc = file_ns::geo_node_vector_math_label;
+ node_type_update(&ntype, file_ns::node_update);
+ node_type_init(&ntype, file_ns::node_init);
node_type_storage(
&ntype, "NodeAttributeVectorMath", node_free_standard_storage, node_copy_standard_storage);
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_vector_rotate.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_vector_rotate.cc
index 0c515fa63fb..3738c4ad14d 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_vector_rotate.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_vector_rotate.cc
@@ -21,30 +21,28 @@
#include "UI_interface.h"
#include "UI_resources.h"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_legacy_attribute_vector_rotate_cc {
-static void geo_node_attribute_vector_rotate_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::String>("Vector");
- b.add_input<decl::Vector>("Vector", "Vector_001").min(0.0f).max(1.0f).hide_value();
- b.add_input<decl::String>("Center");
- b.add_input<decl::Vector>("Center", "Center_001").subtype(PROP_XYZ);
- b.add_input<decl::String>("Axis");
- b.add_input<decl::Vector>("Axis", "Axis_001").min(-1.0f).max(1.0f).subtype(PROP_XYZ);
- b.add_input<decl::String>("Angle");
- b.add_input<decl::Float>("Angle", "Angle_001").subtype(PROP_ANGLE);
- b.add_input<decl::String>("Rotation");
- b.add_input<decl::Vector>("Rotation", "Rotation_001").subtype(PROP_EULER);
- b.add_input<decl::Bool>("Invert");
- b.add_input<decl::String>("Result");
-
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::String>(N_("Vector"));
+ b.add_input<decl::Vector>(N_("Vector"), "Vector_001").min(0.0f).max(1.0f).hide_value();
+ b.add_input<decl::String>(N_("Center"));
+ b.add_input<decl::Vector>(N_("Center"), "Center_001").subtype(PROP_XYZ);
+ b.add_input<decl::String>(N_("Axis"));
+ b.add_input<decl::Vector>(N_("Axis"), "Axis_001").min(-1.0f).max(1.0f).subtype(PROP_XYZ);
+ b.add_input<decl::String>(N_("Angle"));
+ b.add_input<decl::Float>(N_("Angle"), "Angle_001").subtype(PROP_ANGLE);
+ b.add_input<decl::String>(N_("Rotation"));
+ b.add_input<decl::Vector>(N_("Rotation"), "Rotation_001").subtype(PROP_EULER);
+ b.add_input<decl::Bool>(N_("Invert"));
+ b.add_input<decl::String>(N_("Result"));
+
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
-static void geo_node_attribute_vector_rotate_layout(uiLayout *layout,
- bContext *UNUSED(C),
- PointerRNA *ptr)
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
bNode *node = (bNode *)ptr->data;
const NodeAttributeVectorRotate &node_storage = *(NodeAttributeVectorRotate *)node->storage;
@@ -70,27 +68,30 @@ static void geo_node_attribute_vector_rotate_layout(uiLayout *layout,
}
}
-static void geo_node_attribute_vector_rotate_update(bNodeTree *UNUSED(ntree), bNode *node)
+static void node_update(bNodeTree *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);
+ *ntree, *node, "Vector", (GeometryNodeAttributeInputMode)node_storage->input_type_vector);
update_attribute_input_socket_availabilities(
- *node, "Center", (GeometryNodeAttributeInputMode)node_storage->input_type_center);
+ *ntree, *node, "Center", (GeometryNodeAttributeInputMode)node_storage->input_type_center);
update_attribute_input_socket_availabilities(
+ *ntree,
*node,
"Axis",
(GeometryNodeAttributeInputMode)node_storage->input_type_axis,
(mode == GEO_NODE_VECTOR_ROTATE_TYPE_AXIS));
update_attribute_input_socket_availabilities(
+ *ntree,
*node,
"Angle",
(GeometryNodeAttributeInputMode)node_storage->input_type_angle,
(mode != GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ));
update_attribute_input_socket_availabilities(
+ *ntree,
*node,
"Rotation",
(GeometryNodeAttributeInputMode)node_storage->input_type_rotation,
@@ -109,10 +110,9 @@ static float3 vector_rotate_around_axis(const float3 vector,
return result + center;
}
-static void geo_node_attribute_vector_rotate_init(bNodeTree *UNUSED(ntree), bNode *node)
+static void node_init(bNodeTree *UNUSED(ntree), bNode *node)
{
- NodeAttributeVectorRotate *node_storage = (NodeAttributeVectorRotate *)MEM_callocN(
- sizeof(NodeAttributeVectorRotate), __func__);
+ NodeAttributeVectorRotate *node_storage = MEM_cnew<NodeAttributeVectorRotate>(__func__);
node_storage->mode = GEO_NODE_VECTOR_ROTATE_TYPE_AXIS;
node_storage->input_type_vector = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE;
@@ -220,12 +220,12 @@ static void execute_on_component(const GeoNodeExecParams &params, GeometryCompon
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(
+ GVArray 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(
+ GVArray attribute_center = params.get_input_attribute(
"Center", component, result_domain, CD_PROP_FLOAT3, nullptr);
if (!attribute_center) {
return;
@@ -238,21 +238,21 @@ static void execute_on_component(const GeoNodeExecParams &params, GeometryCompon
}
if (mode == GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ) {
- GVArrayPtr attribute_rotation = params.get_input_attribute(
+ GVArray 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>(),
+ 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(
+ GVArray attribute_angle = params.get_input_attribute(
"Angle", component, result_domain, CD_PROP_FLOAT, nullptr);
if (!attribute_angle) {
return;
@@ -260,40 +260,40 @@ static void execute_on_component(const GeoNodeExecParams &params, GeometryCompon
switch (mode) {
case GEO_NODE_VECTOR_ROTATE_TYPE_AXIS: {
- GVArrayPtr attribute_axis = params.get_input_attribute(
+ GVArray 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>(),
+ 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>(),
+ 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_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>(),
+ 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_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>(),
+ 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_angle.typed<float>(),
attribute_result.as_span<float3>(),
invert);
@@ -306,11 +306,11 @@ static void execute_on_component(const GeoNodeExecParams &params, GeometryCompon
attribute_result.save();
}
-static void geo_node_attribute_vector_rotate_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
if (geometry_set.has<MeshComponent>()) {
execute_on_component(params, geometry_set.get_component_for_write<MeshComponent>());
@@ -325,24 +325,25 @@ static void geo_node_attribute_vector_rotate_exec(GeoNodeExecParams params)
params.set_output("Geometry", std::move(geometry_set));
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_legacy_attribute_vector_rotate_cc
void register_node_type_geo_attribute_vector_rotate()
{
+ namespace file_ns = blender::nodes::node_geo_legacy_attribute_vector_rotate_cc;
+
static bNodeType ntype;
geo_node_type_base(&ntype,
GEO_NODE_LEGACY_ATTRIBUTE_VECTOR_ROTATE,
"Attribute Vector Rotate",
- NODE_CLASS_ATTRIBUTE,
- 0);
- 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_CLASS_ATTRIBUTE);
+ node_type_update(&ntype, file_ns::node_update);
+ node_type_init(&ntype, file_ns::node_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 = blender::nodes::geo_node_attribute_vector_rotate_layout;
- ntype.declare = blender::nodes::geo_node_attribute_vector_rotate_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.draw_buttons = file_ns::node_layout;
+ ntype.declare = file_ns::node_declare;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_endpoints.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_endpoints.cc
index 65d22eca39c..51564d8d200 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_endpoints.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_endpoints.cc
@@ -25,13 +25,13 @@
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_legacy_curve_endpoints_cc {
-static void geo_node_curve_endpoints_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_output<decl::Geometry>("Start Points");
- b.add_output<decl::Geometry>("End Points");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_output<decl::Geometry>(N_("Start Points"));
+ b.add_output<decl::Geometry>(N_("End Points"));
}
/**
@@ -61,7 +61,7 @@ static void copy_spline_domain_attributes(const CurveComponent &curve_component,
if (meta_data.domain != ATTR_DOMAIN_CURVE) {
return true;
}
- GVArrayPtr spline_attribute = curve_component.attribute_get_for_read(
+ GVArray spline_attribute = curve_component.attribute_get_for_read(
attribute_id, ATTR_DOMAIN_CURVE, meta_data.data_type);
OutputAttribute result_attribute = points.attribute_try_get_for_output_only(
@@ -70,7 +70,7 @@ static void copy_spline_domain_attributes(const CurveComponent &curve_component,
/* Only copy the attributes of splines in the offsets. */
for (const int i : offsets.index_range()) {
- spline_attribute->get(offsets[i], result[i]);
+ spline_attribute.get(offsets[i], result[i]);
}
result_attribute.save();
@@ -124,36 +124,35 @@ static void copy_endpoint_attributes(Span<SplinePtr> splines,
end_data.tilts[i] = spline.tilts().last();
/* Copy the point attribute data over. */
- for (const auto &item : start_data.point_attributes.items()) {
+ for (const auto item : start_data.point_attributes.items()) {
const AttributeIDRef attribute_id = item.key;
GMutableSpan point_span = item.value;
BLI_assert(spline.attributes.get_for_read(attribute_id));
GSpan spline_span = *spline.attributes.get_for_read(attribute_id);
- blender::fn::GVArray_For_GSpan(spline_span).get(0, point_span[i]);
+ spline_span.type().copy_assign(spline_span[0], point_span[i]);
}
- for (const auto &item : end_data.point_attributes.items()) {
+ for (const auto item : end_data.point_attributes.items()) {
const AttributeIDRef attribute_id = item.key;
GMutableSpan point_span = item.value;
BLI_assert(spline.attributes.get_for_read(attribute_id));
GSpan spline_span = *spline.attributes.get_for_read(attribute_id);
- blender::fn::GVArray_For_GSpan(spline_span).get(spline.size() - 1, point_span[i]);
+ spline_span.type().copy_assign(spline_span[spline.size() - 1], point_span[i]);
}
}
});
}
-static void geo_node_curve_endpoints_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = bke::geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
if (!geometry_set.has_curve()) {
- params.set_output("Start Points", GeometrySet());
- params.set_output("End Points", GeometrySet());
+ params.set_default_remaining_outputs();
return;
}
@@ -168,8 +167,7 @@ static void geo_node_curve_endpoints_exec(GeoNodeExecParams params)
const int total_size = offsets.size();
if (total_size == 0) {
- params.set_output("Start Points", GeometrySet());
- params.set_output("End Points", GeometrySet());
+ params.set_default_remaining_outputs();
return;
}
@@ -206,16 +204,18 @@ static void geo_node_curve_endpoints_exec(GeoNodeExecParams params)
params.set_output("End Points", std::move(end_result));
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_legacy_curve_endpoints_cc
-void register_node_type_geo_curve_endpoints()
+void register_node_type_geo_legacy_curve_endpoints()
{
+ namespace file_ns = blender::nodes::node_geo_legacy_curve_endpoints_cc;
+
static bNodeType ntype;
geo_node_type_base(
- &ntype, GEO_NODE_LEGACY_CURVE_ENDPOINTS, "Curve Endpoints", NODE_CLASS_GEOMETRY, 0);
- ntype.declare = blender::nodes::geo_node_curve_endpoints_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_curve_endpoints_exec;
+ &ntype, GEO_NODE_LEGACY_CURVE_ENDPOINTS, "Curve Endpoints", NODE_CLASS_GEOMETRY);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_reverse.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_reverse.cc
index d1c81333c30..844baa53962 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_reverse.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_reverse.cc
@@ -20,19 +20,19 @@
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_legacy_curve_reverse_cc {
-static void geo_node_curve_reverse_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Curve");
- b.add_input<decl::String>("Selection");
- b.add_output<decl::Geometry>("Curve");
+ b.add_input<decl::Geometry>(N_("Curve"));
+ b.add_input<decl::String>(N_("Selection"));
+ b.add_output<decl::Geometry>(N_("Curve"));
}
-static void geo_node_curve_reverse_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
- geometry_set = bke::geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
if (!geometry_set.has_curve()) {
params.set_output("Curve", geometry_set);
return;
@@ -44,7 +44,7 @@ static void geo_node_curve_reverse_exec(GeoNodeExecParams params)
MutableSpan<SplinePtr> splines = curve.splines();
const std::string selection_name = params.extract_input<std::string>("Selection");
- GVArray_Typed<bool> selection = curve_component.attribute_get_for_read(
+ VArray<bool> selection = curve_component.attribute_get_for_read(
selection_name, ATTR_DOMAIN_CURVE, true);
threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) {
@@ -58,14 +58,15 @@ static void geo_node_curve_reverse_exec(GeoNodeExecParams params)
params.set_output("Curve", geometry_set);
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_legacy_curve_reverse_cc
void register_node_type_geo_legacy_curve_reverse()
{
+ namespace file_ns = blender::nodes::node_geo_legacy_curve_reverse_cc;
+
static bNodeType ntype;
- geo_node_type_base(
- &ntype, GEO_NODE_LEGACY_CURVE_REVERSE, "Curve Reverse", NODE_CLASS_GEOMETRY, 0);
- ntype.declare = blender::nodes::geo_node_curve_reverse_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_curve_reverse_exec;
+ geo_node_type_base(&ntype, GEO_NODE_LEGACY_CURVE_REVERSE, "Curve Reverse", NODE_CLASS_GEOMETRY);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_select_by_handle_type.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_select_by_handle_type.cc
index dfcae2e65b0..780756bcbca 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_select_by_handle_type.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_select_by_handle_type.cc
@@ -23,27 +23,24 @@
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_legacy_curve_select_by_handle_type_cc {
-static void geo_node_select_by_handle_type_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::String>("Selection");
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::String>(N_("Selection"));
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
-static void geo_node_curve_select_by_handle_type_layout(uiLayout *layout,
- bContext *UNUSED(C),
- PointerRNA *ptr)
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
uiItemR(layout, ptr, "handle_type", 0, "", ICON_NONE);
}
-static void geo_node_curve_select_by_handle_type_init(bNodeTree *UNUSED(tree), bNode *node)
+static void node_init(bNodeTree *UNUSED(tree), bNode *node)
{
- NodeGeometryCurveSelectHandles *data = (NodeGeometryCurveSelectHandles *)MEM_callocN(
- sizeof(NodeGeometryCurveSelectHandles), __func__);
+ NodeGeometryCurveSelectHandles *data = MEM_cnew<NodeGeometryCurveSelectHandles>(__func__);
data->handle_type = GEO_NODE_CURVE_HANDLE_AUTO;
data->mode = GEO_NODE_CURVE_HANDLE_LEFT | GEO_NODE_CURVE_HANDLE_RIGHT;
@@ -94,7 +91,7 @@ static void select_curve_by_handle_type(const CurveEval &curve,
});
}
-static void geo_node_select_by_handle_type_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
const NodeGeometryCurveSelectHandles *storage =
(const NodeGeometryCurveSelectHandles *)params.node().storage;
@@ -103,7 +100,7 @@ static void geo_node_select_by_handle_type_exec(GeoNodeExecParams params)
const GeometryNodeCurveHandleMode mode = (GeometryNodeCurveHandleMode)storage->mode;
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = bke::geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>();
const CurveEval *curve = curve_component.get_for_read();
@@ -121,25 +118,24 @@ static void geo_node_select_by_handle_type_exec(GeoNodeExecParams params)
params.set_output("Geometry", std::move(geometry_set));
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_legacy_curve_select_by_handle_type_cc
-void register_node_type_geo_select_by_handle_type()
+void register_node_type_geo_legacy_select_by_handle_type()
{
+ namespace file_ns = blender::nodes::node_geo_legacy_curve_select_by_handle_type_cc;
+
static bNodeType ntype;
- geo_node_type_base(&ntype,
- GEO_NODE_LEGACY_CURVE_SELECT_HANDLES,
- "Select by Handle Type",
- NODE_CLASS_GEOMETRY,
- 0);
- ntype.declare = blender::nodes::geo_node_select_by_handle_type_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_select_by_handle_type_exec;
- node_type_init(&ntype, blender::nodes::geo_node_curve_select_by_handle_type_init);
+ geo_node_type_base(
+ &ntype, GEO_NODE_LEGACY_CURVE_SELECT_HANDLES, "Select by Handle Type", NODE_CLASS_GEOMETRY);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ node_type_init(&ntype, file_ns::node_init);
node_type_storage(&ntype,
"NodeGeometryCurveSelectHandles",
node_free_standard_storage,
node_copy_standard_storage);
- ntype.draw_buttons = blender::nodes::geo_node_curve_select_by_handle_type_layout;
+ ntype.draw_buttons = file_ns::node_layout;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_set_handles.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_set_handles.cc
index 31c13134f79..a82b917e817 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_set_handles.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_set_handles.cc
@@ -21,27 +21,24 @@
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_legacy_curve_set_handles_cc {
-static void geo_node_curve_set_handles_decalre(NodeDeclarationBuilder &b)
+static void node_decalre(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Curve");
- b.add_input<decl::String>("Selection");
- b.add_output<decl::Geometry>("Curve");
+ b.add_input<decl::Geometry>(N_("Curve"));
+ b.add_input<decl::String>(N_("Selection"));
+ b.add_output<decl::Geometry>(N_("Curve"));
}
-static void geo_node_curve_set_handles_layout(uiLayout *layout,
- bContext *UNUSED(C),
- PointerRNA *ptr)
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
uiItemR(layout, ptr, "handle_type", 0, "", ICON_NONE);
}
-static void geo_node_curve_set_handles_init(bNodeTree *UNUSED(tree), bNode *node)
+static void node_init(bNodeTree *UNUSED(tree), bNode *node)
{
- NodeGeometryCurveSetHandles *data = (NodeGeometryCurveSetHandles *)MEM_callocN(
- sizeof(NodeGeometryCurveSetHandles), __func__);
+ NodeGeometryCurveSetHandles *data = MEM_cnew<NodeGeometryCurveSetHandles>(__func__);
data->handle_type = GEO_NODE_CURVE_HANDLE_AUTO;
data->mode = GEO_NODE_CURVE_HANDLE_LEFT | GEO_NODE_CURVE_HANDLE_RIGHT;
@@ -64,7 +61,7 @@ static BezierSpline::HandleType handle_type_from_input_type(GeometryNodeCurveHan
return BezierSpline::HandleType::Auto;
}
-static void geo_node_curve_set_handles_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
const NodeGeometryCurveSetHandles *node_storage =
(NodeGeometryCurveSetHandles *)params.node().storage;
@@ -72,7 +69,7 @@ static void geo_node_curve_set_handles_exec(GeoNodeExecParams params)
const GeometryNodeCurveHandleMode mode = (GeometryNodeCurveHandleMode)node_storage->mode;
GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
- geometry_set = bke::geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
if (!geometry_set.has_curve()) {
params.set_output("Curve", geometry_set);
return;
@@ -84,7 +81,7 @@ static void geo_node_curve_set_handles_exec(GeoNodeExecParams params)
MutableSpan<SplinePtr> splines = curve.splines();
const std::string selection_name = params.extract_input<std::string>("Selection");
- GVArray_Typed<bool> selection = curve_component.attribute_get_for_read(
+ VArray<bool> selection = curve_component.attribute_get_for_read(
selection_name, ATTR_DOMAIN_POINT, true);
const BezierSpline::HandleType new_handle_type = handle_type_from_input_type(type);
@@ -124,21 +121,23 @@ static void geo_node_curve_set_handles_exec(GeoNodeExecParams params)
params.set_output("Curve", geometry_set);
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_legacy_curve_set_handles_cc
-void register_node_type_geo_curve_set_handles()
+void register_node_type_geo_legacy_curve_set_handles()
{
+ namespace file_ns = blender::nodes::node_geo_legacy_curve_set_handles_cc;
+
static bNodeType ntype;
geo_node_type_base(
- &ntype, GEO_NODE_LEGACY_CURVE_SET_HANDLES, "Set Handle Type", NODE_CLASS_GEOMETRY, 0);
- ntype.declare = blender::nodes::geo_node_curve_set_handles_decalre;
- ntype.geometry_node_execute = blender::nodes::geo_node_curve_set_handles_exec;
- node_type_init(&ntype, blender::nodes::geo_node_curve_set_handles_init);
+ &ntype, GEO_NODE_LEGACY_CURVE_SET_HANDLES, "Set Handle Type", NODE_CLASS_GEOMETRY);
+ ntype.declare = file_ns::node_decalre;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ node_type_init(&ntype, file_ns::node_init);
node_type_storage(&ntype,
"NodeGeometryCurveSetHandles",
node_free_standard_storage,
node_copy_standard_storage);
- ntype.draw_buttons = blender::nodes::geo_node_curve_set_handles_layout;
+ ntype.draw_buttons = file_ns::node_layout;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_spline_type.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_spline_type.cc
index 0ef107fd8a4..6fd82e6a1bb 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_spline_type.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_spline_type.cc
@@ -23,26 +23,23 @@
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_legacy_curve_spline_type_cc {
-static void geo_node_curve_spline_type_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Curve");
- b.add_input<decl::String>("Selection");
- b.add_output<decl::Geometry>("Curve");
+ b.add_input<decl::Geometry>(N_("Curve"));
+ b.add_input<decl::String>(N_("Selection"));
+ b.add_output<decl::Geometry>(N_("Curve"));
}
-static void geo_node_curve_spline_type_layout(uiLayout *layout,
- bContext *UNUSED(C),
- PointerRNA *ptr)
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiItemR(layout, ptr, "spline_type", 0, "", ICON_NONE);
}
-static void geo_node_curve_spline_type_init(bNodeTree *UNUSED(tree), bNode *node)
+static void node_init(bNodeTree *UNUSED(tree), bNode *node)
{
- NodeGeometryCurveSplineType *data = (NodeGeometryCurveSplineType *)MEM_callocN(
- sizeof(NodeGeometryCurveSplineType), __func__);
+ NodeGeometryCurveSplineType *data = MEM_cnew<NodeGeometryCurveSplineType>(__func__);
data->spline_type = GEO_NODE_SPLINE_TYPE_POLY;
node->storage = data;
@@ -238,14 +235,14 @@ static SplinePtr convert_to_nurbs(const Spline &input)
return {};
}
-static void geo_node_curve_spline_type_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
const NodeGeometryCurveSplineType *storage =
(const NodeGeometryCurveSplineType *)params.node().storage;
const GeometryNodeSplineType output_type = (const GeometryNodeSplineType)storage->spline_type;
GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
- geometry_set = bke::geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
if (!geometry_set.has_curve()) {
params.set_output("Curve", geometry_set);
return;
@@ -255,7 +252,7 @@ static void geo_node_curve_spline_type_exec(GeoNodeExecParams params)
const CurveEval &curve = *curve_component->get_for_read();
const std::string selection_name = params.extract_input<std::string>("Selection");
- GVArray_Typed<bool> selection = curve_component->attribute_get_for_read(
+ VArray<bool> selection = curve_component->attribute_get_for_read(
selection_name, ATTR_DOMAIN_CURVE, true);
std::unique_ptr<CurveEval> new_curve = std::make_unique<CurveEval>();
@@ -282,21 +279,23 @@ static void geo_node_curve_spline_type_exec(GeoNodeExecParams params)
params.set_output("Curve", GeometrySet::create_with_curve(new_curve.release()));
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_legacy_curve_spline_type_cc
-void register_node_type_geo_curve_spline_type()
+void register_node_type_geo_legacy_curve_spline_type()
{
+ namespace file_ns = blender::nodes::node_geo_legacy_curve_spline_type_cc;
+
static bNodeType ntype;
geo_node_type_base(
- &ntype, GEO_NODE_LEGACY_CURVE_SPLINE_TYPE, "Set Spline Type", NODE_CLASS_GEOMETRY, 0);
- ntype.declare = blender::nodes::geo_node_curve_spline_type_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_curve_spline_type_exec;
- node_type_init(&ntype, blender::nodes::geo_node_curve_spline_type_init);
+ &ntype, GEO_NODE_LEGACY_CURVE_SPLINE_TYPE, "Set Spline Type", NODE_CLASS_GEOMETRY);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ node_type_init(&ntype, file_ns::node_init);
node_type_storage(&ntype,
"NodeGeometryCurveSplineType",
node_free_standard_storage,
node_copy_standard_storage);
- ntype.draw_buttons = blender::nodes::geo_node_curve_spline_type_layout;
+ ntype.draw_buttons = file_ns::node_layout;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_subdivide.cc
index 0522f2b8981..4621a1656aa 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_subdivide.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_subdivide.cc
@@ -25,42 +25,37 @@
#include "node_geometry_util.hh"
-using blender::fn::GVArray_For_GSpan;
-using blender::fn::GVArray_For_Span;
-using blender::fn::GVArray_Typed;
+namespace blender::nodes::node_geo_legacy_curve_subdivide_cc {
-namespace blender::nodes {
-
-static void geo_node_curve_subdivide_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::String>("Cuts");
- b.add_input<decl::Int>("Cuts", "Cuts_001").default_value(1).min(0).max(1000);
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::String>(N_("Cuts"));
+ b.add_input<decl::Int>(N_("Cuts"), "Cuts_001").default_value(1).min(0).max(1000);
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
-static void geo_node_curve_subdivide_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
uiItemR(layout, ptr, "cuts_type", 0, IFACE_("Cuts"), ICON_NONE);
}
-static void geo_node_curve_subdivide_init(bNodeTree *UNUSED(tree), bNode *node)
+static void node_init(bNodeTree *UNUSED(tree), bNode *node)
{
- NodeGeometryCurveSubdivide *data = (NodeGeometryCurveSubdivide *)MEM_callocN(
- sizeof(NodeGeometryCurveSubdivide), __func__);
+ NodeGeometryCurveSubdivide *data = MEM_cnew<NodeGeometryCurveSubdivide>(__func__);
data->cuts_type = GEO_NODE_ATTRIBUTE_INPUT_INTEGER;
node->storage = data;
}
-static void geo_node_curve_subdivide_update(bNodeTree *UNUSED(ntree), bNode *node)
+static void node_update(bNodeTree *ntree, bNode *node)
{
NodeGeometryPointTranslate &node_storage = *(NodeGeometryPointTranslate *)node->storage;
update_attribute_input_socket_availabilities(
- *node, "Cuts", (GeometryNodeAttributeInputMode)node_storage.input_type);
+ *ntree, *node, "Cuts", (GeometryNodeAttributeInputMode)node_storage.input_type);
}
static Array<int> get_subdivided_offsets(const Spline &spline,
@@ -308,8 +303,12 @@ static SplinePtr subdivide_spline(const Spline &spline,
const VArray<int> &cuts,
const int spline_offset)
{
- /* Since we expect to access each value many times, it should be worth it to make sure the
- * attribute is a real span (especially considering the note below). Using the offset at each
+ if (spline.size() <= 1) {
+ return spline.copy();
+ }
+
+ /* Since we expect to access each value many times, it should be worth it to make sure count
+ * of cuts is a real span (especially considering the note below). Using the offset at each
* point facilitates subdividing in parallel later. */
Array<int> offsets = get_subdivided_offsets(spline, cuts, spline_offset);
const int result_size = offsets.last() + int(!spline.is_cyclic());
@@ -347,11 +346,11 @@ static std::unique_ptr<CurveEval> subdivide_curve(const CurveEval &input_curve,
return output_curve;
}
-static void geo_node_subdivide_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = bke::geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
if (!geometry_set.has_curve()) {
params.set_output("Geometry", geometry_set);
@@ -359,34 +358,35 @@ static void geo_node_subdivide_exec(GeoNodeExecParams params)
}
const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>();
- GVArray_Typed<int> cuts = params.get_input_attribute<int>(
- "Cuts", component, ATTR_DOMAIN_POINT, 0);
- if (cuts->is_single() && cuts->get_internal_single() < 1) {
+ VArray<int> cuts = params.get_input_attribute<int>("Cuts", component, ATTR_DOMAIN_POINT, 0);
+ if (cuts.is_single() && cuts.get_internal_single() < 1) {
params.set_output("Geometry", geometry_set);
return;
}
- std::unique_ptr<CurveEval> output_curve = subdivide_curve(*component.get_for_read(), *cuts);
+ std::unique_ptr<CurveEval> output_curve = subdivide_curve(*component.get_for_read(), cuts);
params.set_output("Geometry", GeometrySet::create_with_curve(output_curve.release()));
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_legacy_curve_subdivide_cc
-void register_node_type_geo_curve_subdivide()
+void register_node_type_geo_legacy_curve_subdivide()
{
+ namespace file_ns = blender::nodes::node_geo_legacy_curve_subdivide_cc;
+
static bNodeType ntype;
geo_node_type_base(
- &ntype, GEO_NODE_LEGACY_CURVE_SUBDIVIDE, "Curve Subdivide", NODE_CLASS_GEOMETRY, 0);
- ntype.declare = blender::nodes::geo_node_curve_subdivide_declare;
- ntype.draw_buttons = blender::nodes::geo_node_curve_subdivide_layout;
+ &ntype, GEO_NODE_LEGACY_CURVE_SUBDIVIDE, "Curve Subdivide", NODE_CLASS_GEOMETRY);
+ ntype.declare = file_ns::node_declare;
+ ntype.draw_buttons = file_ns::node_layout;
node_type_storage(&ntype,
"NodeGeometryCurveSubdivide",
node_free_standard_storage,
node_copy_standard_storage);
- node_type_init(&ntype, blender::nodes::geo_node_curve_subdivide_init);
- node_type_update(&ntype, blender::nodes::geo_node_curve_subdivide_update);
- ntype.geometry_node_execute = blender::nodes::geo_node_subdivide_exec;
+ node_type_init(&ntype, file_ns::node_init);
+ node_type_update(&ntype, file_ns::node_update);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_to_points.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_to_points.cc
index 0c435d69991..1e6b7f92a77 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_to_points.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_to_points.cc
@@ -28,29 +28,80 @@
namespace blender::nodes {
-static void geo_node_curve_to_points_declare(NodeDeclarationBuilder &b)
+static GMutableSpan create_attribute_and_retrieve_span(PointCloudComponent &points,
+ const AttributeIDRef &attribute_id,
+ const CustomDataType data_type)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Int>("Count").default_value(10).min(2).max(100000);
- b.add_input<decl::Float>("Length").default_value(0.1f).min(0.001f).subtype(PROP_DISTANCE);
- b.add_output<decl::Geometry>("Geometry");
+ points.attribute_try_create(attribute_id, ATTR_DOMAIN_POINT, data_type, AttributeInitDefault());
+ WriteAttributeLookup attribute = points.attribute_try_get_for_write(attribute_id);
+ BLI_assert(attribute);
+ return attribute.varray.get_internal_span();
}
-static void geo_node_curve_to_points_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+template<typename T>
+static MutableSpan<T> create_attribute_and_retrieve_span(PointCloudComponent &points,
+ const AttributeIDRef &attribute_id)
+{
+ GMutableSpan attribute = create_attribute_and_retrieve_span(
+ points, attribute_id, bke::cpp_type_to_custom_data_type(CPPType::get<T>()));
+ return attribute.typed<T>();
+}
+
+CurveToPointsResults curve_to_points_create_result_attributes(PointCloudComponent &points,
+ const CurveEval &curve)
+{
+ CurveToPointsResults attributes;
+
+ attributes.result_size = points.attribute_domain_size(ATTR_DOMAIN_POINT);
+
+ attributes.positions = create_attribute_and_retrieve_span<float3>(points, "position");
+ attributes.radii = create_attribute_and_retrieve_span<float>(points, "radius");
+ attributes.tilts = create_attribute_and_retrieve_span<float>(points, "tilt");
+
+ /* Because of the invariants of the curve component, we use the attributes of the
+ * first spline as a representative for the attribute meta data all splines. */
+ curve.splines().first()->attributes.foreach_attribute(
+ [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
+ attributes.point_attributes.add_new(
+ attribute_id,
+ create_attribute_and_retrieve_span(points, attribute_id, meta_data.data_type));
+ return true;
+ },
+ ATTR_DOMAIN_POINT);
+
+ attributes.tangents = create_attribute_and_retrieve_span<float3>(points, "tangent");
+ attributes.normals = create_attribute_and_retrieve_span<float3>(points, "normal");
+ attributes.rotations = create_attribute_and_retrieve_span<float3>(points, "rotation");
+
+ return attributes;
+}
+
+} // namespace blender::nodes
+
+namespace blender::nodes::node_geo_legacy_curve_to_points_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::Int>(N_("Count")).default_value(10).min(2).max(100000);
+ b.add_input<decl::Float>(N_("Length")).default_value(0.1f).min(0.001f).subtype(PROP_DISTANCE);
+ b.add_output<decl::Geometry>(N_("Geometry"));
+}
+
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiItemR(layout, ptr, "mode", 0, "", ICON_NONE);
}
-static void geo_node_curve_to_points_init(bNodeTree *UNUSED(tree), bNode *node)
+static void node_init(bNodeTree *UNUSED(tree), bNode *node)
{
- NodeGeometryCurveToPoints *data = (NodeGeometryCurveToPoints *)MEM_callocN(
- sizeof(NodeGeometryCurveToPoints), __func__);
+ NodeGeometryCurveToPoints *data = MEM_cnew<NodeGeometryCurveToPoints>(__func__);
data->mode = GEO_NODE_CURVE_RESAMPLE_COUNT;
node->storage = data;
}
-static void geo_node_curve_to_points_update(bNodeTree *UNUSED(ntree), bNode *node)
+static void node_update(bNodeTree *ntree, bNode *node)
{
NodeGeometryCurveToPoints &node_storage = *(NodeGeometryCurveToPoints *)node->storage;
const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)node_storage.mode;
@@ -58,8 +109,8 @@ static void geo_node_curve_to_points_update(bNodeTree *UNUSED(ntree), bNode *nod
bNodeSocket *count_socket = ((bNodeSocket *)node->inputs.first)->next;
bNodeSocket *length_socket = count_socket->next;
- nodeSetSocketAvailability(count_socket, mode == GEO_NODE_CURVE_RESAMPLE_COUNT);
- nodeSetSocketAvailability(length_socket, mode == GEO_NODE_CURVE_RESAMPLE_LENGTH);
+ nodeSetSocketAvailability(ntree, count_socket, mode == GEO_NODE_CURVE_RESAMPLE_COUNT);
+ nodeSetSocketAvailability(ntree, length_socket, mode == GEO_NODE_CURVE_RESAMPLE_LENGTH);
}
/**
@@ -114,54 +165,6 @@ static Array<int> calculate_spline_point_offsets(GeoNodeExecParams &params,
return {0};
}
-static GMutableSpan create_attribute_and_retrieve_span(PointCloudComponent &points,
- const AttributeIDRef &attribute_id,
- const CustomDataType data_type)
-{
- points.attribute_try_create(attribute_id, ATTR_DOMAIN_POINT, data_type, AttributeInitDefault());
- WriteAttributeLookup attribute = points.attribute_try_get_for_write(attribute_id);
- BLI_assert(attribute);
- return attribute.varray->get_internal_span();
-}
-
-template<typename T>
-static MutableSpan<T> create_attribute_and_retrieve_span(PointCloudComponent &points,
- const AttributeIDRef &attribute_id)
-{
- GMutableSpan attribute = create_attribute_and_retrieve_span(
- points, attribute_id, bke::cpp_type_to_custom_data_type(CPPType::get<T>()));
- return attribute.typed<T>();
-}
-
-CurveToPointsResults curve_to_points_create_result_attributes(PointCloudComponent &points,
- const CurveEval &curve)
-{
- CurveToPointsResults attributes;
-
- attributes.result_size = points.attribute_domain_size(ATTR_DOMAIN_POINT);
-
- attributes.positions = create_attribute_and_retrieve_span<float3>(points, "position");
- attributes.radii = create_attribute_and_retrieve_span<float>(points, "radius");
- attributes.tilts = create_attribute_and_retrieve_span<float>(points, "tilt");
-
- /* Because of the invariants of the curve component, we use the attributes of the
- * first spline as a representative for the attribute meta data all splines. */
- curve.splines().first()->attributes.foreach_attribute(
- [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
- attributes.point_attributes.add_new(
- attribute_id,
- create_attribute_and_retrieve_span(points, attribute_id, meta_data.data_type));
- return true;
- },
- ATTR_DOMAIN_POINT);
-
- attributes.tangents = create_attribute_and_retrieve_span<float3>(points, "tangent");
- attributes.normals = create_attribute_and_retrieve_span<float3>(points, "normal");
- attributes.rotations = create_attribute_and_retrieve_span<float3>(points, "rotation");
-
- return attributes;
-}
-
/**
* TODO: For non-poly splines, this has double copies that could be avoided as part
* of a general look at optimizing uses of #Spline::interpolate_to_evaluated.
@@ -177,10 +180,10 @@ static void copy_evaluated_point_attributes(Span<SplinePtr> splines,
const int size = offsets[i + 1] - offsets[i];
data.positions.slice(offset, size).copy_from(spline.evaluated_positions());
- spline.interpolate_to_evaluated(spline.radii())->materialize(data.radii.slice(offset, size));
- spline.interpolate_to_evaluated(spline.tilts())->materialize(data.tilts.slice(offset, size));
+ spline.interpolate_to_evaluated(spline.radii()).materialize(data.radii.slice(offset, size));
+ spline.interpolate_to_evaluated(spline.tilts()).materialize(data.tilts.slice(offset, size));
- for (const Map<AttributeIDRef, GMutableSpan>::Item &item : data.point_attributes.items()) {
+ for (const Map<AttributeIDRef, GMutableSpan>::Item item : data.point_attributes.items()) {
const AttributeIDRef attribute_id = item.key;
GMutableSpan point_span = item.value;
@@ -188,7 +191,7 @@ static void copy_evaluated_point_attributes(Span<SplinePtr> splines,
GSpan spline_span = *spline.attributes.get_for_read(attribute_id);
spline.interpolate_to_evaluated(spline_span)
- ->materialize(point_span.slice(offset, size).data());
+ .materialize(point_span.slice(offset, size).data());
}
data.tangents.slice(offset, size).copy_from(spline.evaluated_tangents());
@@ -223,14 +226,14 @@ static void copy_uniform_sample_point_attributes(Span<SplinePtr> splines,
uniform_samples,
data.tilts.slice(offset, size));
- for (const Map<AttributeIDRef, GMutableSpan>::Item &item : data.point_attributes.items()) {
+ for (const Map<AttributeIDRef, GMutableSpan>::Item item : data.point_attributes.items()) {
const AttributeIDRef attribute_id = item.key;
GMutableSpan point_span = item.value;
BLI_assert(spline.attributes.get_for_read(attribute_id));
GSpan spline_span = *spline.attributes.get_for_read(attribute_id);
- spline.sample_with_index_factors(*spline.interpolate_to_evaluated(spline_span),
+ spline.sample_with_index_factors(spline.interpolate_to_evaluated(spline_span),
uniform_samples,
point_span.slice(offset, size));
}
@@ -238,13 +241,13 @@ static void copy_uniform_sample_point_attributes(Span<SplinePtr> splines,
spline.sample_with_index_factors<float3>(
spline.evaluated_tangents(), uniform_samples, data.tangents.slice(offset, size));
for (float3 &tangent : data.tangents) {
- tangent.normalize();
+ tangent = math::normalize(tangent);
}
spline.sample_with_index_factors<float3>(
spline.evaluated_normals(), uniform_samples, data.normals.slice(offset, size));
for (float3 &normals : data.normals) {
- normals.normalize();
+ normals = math::normalize(normals);
}
}
});
@@ -263,20 +266,20 @@ static void copy_spline_domain_attributes(const CurveComponent &curve_component,
if (meta_data.domain != ATTR_DOMAIN_CURVE) {
return true;
}
- GVArrayPtr spline_attribute = curve_component.attribute_get_for_read(
+ GVArray spline_attribute = curve_component.attribute_get_for_read(
attribute_id, ATTR_DOMAIN_CURVE, meta_data.data_type);
- const CPPType &type = spline_attribute->type();
+ const CPPType &type = spline_attribute.type();
OutputAttribute result_attribute = points.attribute_try_get_for_output_only(
attribute_id, ATTR_DOMAIN_POINT, meta_data.data_type);
GMutableSpan result = result_attribute.as_span();
- for (const int i : IndexRange(spline_attribute->size())) {
+ for (const int i : spline_attribute.index_range()) {
const int offset = offsets[i];
const int size = offsets[i + 1] - offsets[i];
if (size != 0) {
BUFFER_FOR_CPP_TYPE_VALUE(type, buffer);
- spline_attribute->get(i, buffer);
+ spline_attribute.get(i, buffer);
type.fill_assign_n(buffer, result[offset], size);
}
}
@@ -286,25 +289,13 @@ static void copy_spline_domain_attributes(const CurveComponent &curve_component,
});
}
-void curve_create_default_rotation_attribute(Span<float3> tangents,
- Span<float3> normals,
- MutableSpan<float3> rotations)
-{
- threading::parallel_for(IndexRange(rotations.size()), 512, [&](IndexRange range) {
- for (const int i : range) {
- rotations[i] =
- float4x4::from_normalized_axis_data({0, 0, 0}, normals[i], tangents[i]).to_euler();
- }
- });
-}
-
-static void geo_node_curve_to_points_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
NodeGeometryCurveToPoints &node_storage = *(NodeGeometryCurveToPoints *)params.node().storage;
const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)node_storage.mode;
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = bke::geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
if (!geometry_set.has_curve()) {
params.set_output("Geometry", GeometrySet());
@@ -352,21 +343,23 @@ static void geo_node_curve_to_points_exec(GeoNodeExecParams params)
params.set_output("Geometry", std::move(result));
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_legacy_curve_to_points_cc
-void register_node_type_geo_curve_to_points()
+void register_node_type_geo_legacy_curve_to_points()
{
+ namespace file_ns = blender::nodes::node_geo_legacy_curve_to_points_cc;
+
static bNodeType ntype;
geo_node_type_base(
- &ntype, GEO_NODE_LEGACY_CURVE_TO_POINTS, "Curve to Points", NODE_CLASS_GEOMETRY, 0);
- ntype.declare = blender::nodes::geo_node_curve_to_points_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_curve_to_points_exec;
- ntype.draw_buttons = blender::nodes::geo_node_curve_to_points_layout;
+ &ntype, GEO_NODE_LEGACY_CURVE_TO_POINTS, "Curve to Points", NODE_CLASS_GEOMETRY);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.draw_buttons = file_ns::node_layout;
node_type_storage(
&ntype, "NodeGeometryCurveToPoints", node_free_standard_storage, node_copy_standard_storage);
- node_type_init(&ntype, blender::nodes::geo_node_curve_to_points_init);
- node_type_update(&ntype, blender::nodes::geo_node_curve_to_points_update);
+ node_type_init(&ntype, file_ns::node_init);
+ node_type_update(&ntype, file_ns::node_update);
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_delete_geometry.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_delete_geometry.cc
index 1e2f652cd78..f7fd12d775a 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_delete_geometry.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_delete_geometry.cc
@@ -26,31 +26,16 @@
#include "node_geometry_util.hh"
-using blender::bke::CustomDataAttributes;
-
-/* Code from the mask modifier in MOD_mask.cc. */
-extern void copy_masked_vertices_to_new_mesh(const Mesh &src_mesh,
- Mesh &dst_mesh,
- blender::Span<int> vertex_map);
-extern void copy_masked_edges_to_new_mesh(const Mesh &src_mesh,
- Mesh &dst_mesh,
- blender::Span<int> vertex_map,
- blender::Span<int> edge_map);
-extern void copy_masked_polys_to_new_mesh(const Mesh &src_mesh,
- Mesh &dst_mesh,
- blender::Span<int> vertex_map,
- blender::Span<int> edge_map,
- blender::Span<int> masked_poly_indices,
- blender::Span<int> new_loop_starts);
+namespace blender::nodes::node_geo_legacy_delete_geometry_cc {
-namespace blender::nodes {
+using blender::bke::CustomDataAttributes;
-static void geo_node_delete_geometry_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::String>("Selection");
- b.add_input<decl::Bool>("Invert");
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::String>(N_("Selection"));
+ b.add_input<decl::Bool>(N_("Invert"));
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
template<typename T> static void copy_data(Span<T> data, MutableSpan<T> r_data, IndexMask mask)
@@ -60,6 +45,78 @@ template<typename T> static void copy_data(Span<T> data, MutableSpan<T> r_data,
}
}
+static void copy_masked_vertices_to_new_mesh(const Mesh &src_mesh,
+ Mesh &dst_mesh,
+ Span<int> vertex_map)
+{
+ BLI_assert(src_mesh.totvert == vertex_map.size());
+ for (const int i_src : vertex_map.index_range()) {
+ const int i_dst = vertex_map[i_src];
+ if (i_dst == -1) {
+ continue;
+ }
+
+ const MVert &v_src = src_mesh.mvert[i_src];
+ MVert &v_dst = dst_mesh.mvert[i_dst];
+
+ v_dst = v_src;
+ CustomData_copy_data(&src_mesh.vdata, &dst_mesh.vdata, i_src, i_dst, 1);
+ }
+}
+
+static void copy_masked_edges_to_new_mesh(const Mesh &src_mesh,
+ Mesh &dst_mesh,
+ Span<int> vertex_map,
+ Span<int> edge_map)
+{
+ BLI_assert(src_mesh.totvert == vertex_map.size());
+ BLI_assert(src_mesh.totedge == edge_map.size());
+ for (const int i_src : IndexRange(src_mesh.totedge)) {
+ const int i_dst = edge_map[i_src];
+ if (ELEM(i_dst, -1, -2)) {
+ continue;
+ }
+
+ const MEdge &e_src = src_mesh.medge[i_src];
+ MEdge &e_dst = dst_mesh.medge[i_dst];
+
+ CustomData_copy_data(&src_mesh.edata, &dst_mesh.edata, i_src, i_dst, 1);
+ e_dst = e_src;
+ e_dst.v1 = vertex_map[e_src.v1];
+ e_dst.v2 = vertex_map[e_src.v2];
+ }
+}
+
+static void copy_masked_polys_to_new_mesh(const Mesh &src_mesh,
+ Mesh &dst_mesh,
+ Span<int> vertex_map,
+ Span<int> edge_map,
+ Span<int> masked_poly_indices,
+ Span<int> new_loop_starts)
+{
+ for (const int i_dst : masked_poly_indices.index_range()) {
+ const int i_src = masked_poly_indices[i_dst];
+
+ const MPoly &mp_src = src_mesh.mpoly[i_src];
+ MPoly &mp_dst = dst_mesh.mpoly[i_dst];
+ const int i_ml_src = mp_src.loopstart;
+ const int i_ml_dst = new_loop_starts[i_dst];
+
+ CustomData_copy_data(&src_mesh.pdata, &dst_mesh.pdata, i_src, i_dst, 1);
+ CustomData_copy_data(&src_mesh.ldata, &dst_mesh.ldata, i_ml_src, i_ml_dst, mp_src.totloop);
+
+ const MLoop *ml_src = src_mesh.mloop + i_ml_src;
+ MLoop *ml_dst = dst_mesh.mloop + i_ml_dst;
+
+ mp_dst = mp_src;
+ mp_dst.loopstart = i_ml_dst;
+ for (int i : IndexRange(mp_src.totloop)) {
+ ml_dst[i].v = vertex_map[ml_src[i].v];
+ ml_dst[i].e = edge_map[ml_src[i].e];
+ }
+ }
+}
+
static void spline_copy_builtin_attributes(const Spline &spline,
Spline &r_spline,
const IndexMask mask)
@@ -137,7 +194,7 @@ static std::unique_ptr<CurveEval> curve_delete(const CurveEval &input_curve,
Vector<int64_t> copied_splines;
if (input_curve.attributes.get_for_read(name)) {
- GVArray_Typed<bool> selection = input_curve.attributes.get_for_read<bool>(name, false);
+ VArray<bool> selection = input_curve.attributes.get_for_read<bool>(name, false);
for (const int i : input_splines.index_range()) {
if (selection[i] == invert) {
output_curve->add_spline(input_splines[i]->copy());
@@ -151,7 +208,7 @@ static std::unique_ptr<CurveEval> curve_delete(const CurveEval &input_curve,
for (const int i : input_splines.index_range()) {
const Spline &spline = *input_splines[i];
- GVArray_Typed<bool> selection = spline.attributes.get_for_read<bool>(name, false);
+ VArray<bool> selection = spline.attributes.get_for_read<bool>(name, false);
indices_to_copy.clear();
for (const int i_point : IndexRange(spline.size())) {
@@ -202,7 +259,7 @@ static void delete_point_cloud_selection(const PointCloudComponent &in_component
const StringRef selection_name,
const bool invert)
{
- const GVArray_Typed<bool> selection_attribute = in_component.attribute_get_for_read<bool>(
+ const VArray<bool> selection_attribute = in_component.attribute_get_for_read<bool>(
selection_name, ATTR_DOMAIN_POINT, false);
VArray_Span<bool> selection{selection_attribute};
@@ -590,7 +647,7 @@ static void delete_mesh_selection(MeshComponent &component,
const AttributeDomain selection_domain = get_mesh_selection_domain(component, selection_name);
/* This already checks if the attribute exists, and displays a warning in that case. */
- GVArray_Typed<bool> selection = component.attribute_get_for_read<bool>(
+ VArray<bool> selection = component.attribute_get_for_read<bool>(
selection_name, selection_domain, false);
/* Check if there is anything to delete. */
@@ -627,10 +684,10 @@ static void delete_mesh_selection(MeshComponent &component,
component.replace(mesh_out);
}
-static void geo_node_delete_geometry_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = bke::geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
const bool invert = params.extract_input<bool>("Invert");
const std::string selection_name = params.extract_input<std::string>("Selection");
@@ -662,16 +719,18 @@ static void geo_node_delete_geometry_exec(GeoNodeExecParams params)
params.set_output("Geometry", std::move(out_set));
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_legacy_delete_geometry_cc
-void register_node_type_geo_delete_geometry()
+void register_node_type_geo_legacy_delete_geometry()
{
+ namespace file_ns = blender::nodes::node_geo_legacy_delete_geometry_cc;
+
static bNodeType ntype;
geo_node_type_base(
- &ntype, GEO_NODE_LEGACY_DELETE_GEOMETRY, "Delete Geometry", NODE_CLASS_GEOMETRY, 0);
+ &ntype, GEO_NODE_LEGACY_DELETE_GEOMETRY, "Delete Geometry", NODE_CLASS_GEOMETRY);
- ntype.declare = blender::nodes::geo_node_delete_geometry_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_delete_geometry_exec;
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_edge_split.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_edge_split.cc
index 2ea6516996d..e628edb7e17 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_edge_split.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_edge_split.cc
@@ -22,26 +22,26 @@ extern "C" {
Mesh *doEdgeSplit(const Mesh *mesh, EdgeSplitModifierData *emd);
}
-namespace blender::nodes {
+namespace blender::nodes::node_geo_legacy_edge_split_cc {
-static void geo_node_edge_split_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Bool>("Edge Angle").default_value(true);
- b.add_input<decl::Float>("Angle")
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::Bool>(N_("Edge Angle")).default_value(true);
+ b.add_input<decl::Float>(N_("Angle"))
.default_value(DEG2RADF(30.0f))
.min(0.0f)
.max(DEG2RADF(180.0f))
.subtype(PROP_ANGLE);
- b.add_input<decl::Bool>("Sharp Edges");
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Bool>(N_("Sharp Edges"));
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
-static void geo_node_edge_split_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
if (!geometry_set.has_mesh()) {
params.set_output("Geometry", std::move(geometry_set));
@@ -76,14 +76,16 @@ static void geo_node_edge_split_exec(GeoNodeExecParams params)
params.set_output("Geometry", std::move(geometry_set));
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_legacy_edge_split_cc
-void register_node_type_geo_edge_split()
+void register_node_type_geo_legacy_edge_split()
{
+ namespace file_ns = blender::nodes::node_geo_legacy_edge_split_cc;
+
static bNodeType ntype;
- geo_node_type_base(&ntype, GEO_NODE_LEGACY_EDGE_SPLIT, "Edge Split", NODE_CLASS_GEOMETRY, 0);
- ntype.geometry_node_execute = blender::nodes::geo_node_edge_split_exec;
- ntype.declare = blender::nodes::geo_node_edge_split_declare;
+ geo_node_type_base(&ntype, GEO_NODE_LEGACY_EDGE_SPLIT, "Edge Split", NODE_CLASS_GEOMETRY);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.declare = file_ns::node_declare;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_material_assign.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_material_assign.cc
index 7d3481c1067..8fd6b1e299f 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_material_assign.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_material_assign.cc
@@ -24,14 +24,14 @@
#include "BKE_material.h"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_legacy_material_assign_cc {
-static void geo_node_legacy_material_assign_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Material>("Material").hide_label(true);
- b.add_input<decl::String>("Selection");
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::Material>(N_("Material")).hide_label(true);
+ b.add_input<decl::String>(N_("Selection"));
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
static void assign_material_to_faces(Mesh &mesh, const VArray<bool> &face_mask, Material *material)
@@ -59,20 +59,20 @@ static void assign_material_to_faces(Mesh &mesh, const VArray<bool> &face_mask,
}
}
-static void geo_node_legacy_material_assign_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
Material *material = params.extract_input<Material *>("Material");
const std::string mask_name = params.extract_input<std::string>("Selection");
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
if (geometry_set.has<MeshComponent>()) {
MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
Mesh *mesh = mesh_component.get_for_write();
if (mesh != nullptr) {
- GVArray_Typed<bool> face_mask = mesh_component.attribute_get_for_read<bool>(
+ VArray<bool> face_mask = mesh_component.attribute_get_for_read<bool>(
mask_name, ATTR_DOMAIN_FACE, true);
assign_material_to_faces(*mesh, face_mask, material);
}
@@ -81,15 +81,17 @@ static void geo_node_legacy_material_assign_exec(GeoNodeExecParams params)
params.set_output("Geometry", std::move(geometry_set));
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_legacy_material_assign_cc
void register_node_type_geo_legacy_material_assign()
{
+ namespace file_ns = blender::nodes::node_geo_legacy_material_assign_cc;
+
static bNodeType ntype;
geo_node_type_base(
- &ntype, GEO_NODE_LEGACY_MATERIAL_ASSIGN, "Material Assign", NODE_CLASS_GEOMETRY, 0);
- ntype.declare = blender::nodes::geo_node_legacy_material_assign_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_legacy_material_assign_exec;
+ &ntype, GEO_NODE_LEGACY_MATERIAL_ASSIGN, "Material Assign", NODE_CLASS_GEOMETRY);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_mesh_to_curve.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_mesh_to_curve.cc
new file mode 100644
index 00000000000..d026fff003f
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_mesh_to_curve.cc
@@ -0,0 +1,80 @@
+/*
+ * 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 "GEO_mesh_to_curve.hh"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_legacy_mesh_to_curve_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>(N_("Mesh"));
+ b.add_input<decl::String>(N_("Selection"));
+ b.add_output<decl::Geometry>(N_("Curve"));
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh");
+
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
+
+ if (!geometry_set.has_mesh()) {
+ params.set_default_remaining_outputs();
+ return;
+ }
+
+ const MeshComponent &component = *geometry_set.get_component_for_read<MeshComponent>();
+ const std::string selection_name = params.extract_input<std::string>("Selection");
+ if (!selection_name.empty() && !component.attribute_exists(selection_name)) {
+ params.error_message_add(NodeWarningType::Error,
+ TIP_("No attribute with name \"") + selection_name + "\"");
+ }
+ VArray<bool> selection = component.attribute_get_for_read<bool>(
+ selection_name, ATTR_DOMAIN_EDGE, true);
+
+ Vector<int64_t> selected_edge_indices;
+ for (const int64_t i : IndexRange(component.attribute_domain_size(ATTR_DOMAIN_EDGE))) {
+ if (selection[i]) {
+ selected_edge_indices.append(i);
+ }
+ }
+
+ if (selected_edge_indices.size() == 0) {
+ params.set_default_remaining_outputs();
+ return;
+ }
+
+ std::unique_ptr<CurveEval> curve = geometry::mesh_to_curve_convert(
+ component, IndexMask(selected_edge_indices));
+
+ params.set_output("Curve", GeometrySet::create_with_curve(curve.release()));
+}
+
+} // namespace blender::nodes::node_geo_legacy_mesh_to_curve_cc
+
+void register_node_type_geo_legacy_mesh_to_curve()
+{
+ namespace file_ns = blender::nodes::node_geo_legacy_mesh_to_curve_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_LEGACY_MESH_TO_CURVE, "Mesh to Curve", NODE_CLASS_GEOMETRY);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_distribute.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_distribute.cc
index f95b0da86ed..29eff373d15 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_distribute.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_distribute.cc
@@ -36,36 +36,35 @@
#include "node_geometry_util.hh"
-using blender::bke::GeometryInstanceGroup;
+namespace blender::nodes::node_geo_legacy_point_distribute_cc {
-namespace blender::nodes {
+using blender::bke::GeometryInstanceGroup;
-static void geo_node_point_distribute_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Float>("Distance Min").min(0.0f).max(100000.0f).subtype(PROP_DISTANCE);
- b.add_input<decl::Float>("Density Max")
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::Float>(N_("Distance Min")).min(0.0f).max(100000.0f).subtype(PROP_DISTANCE);
+ b.add_input<decl::Float>(N_("Density Max"))
.default_value(1.0f)
.min(0.0f)
.max(100000.0f)
.subtype(PROP_NONE);
- b.add_input<decl::String>("Density Attribute");
- b.add_input<decl::Int>("Seed").min(-10000).max(10000);
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::String>(N_("Density Attribute"));
+ b.add_input<decl::Int>(N_("Seed")).min(-10000).max(10000);
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
-static void geo_node_point_distribute_layout(uiLayout *layout,
- bContext *UNUSED(C),
- PointerRNA *ptr)
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiItemR(layout, ptr, "distribute_method", 0, "", ICON_NONE);
}
-static void node_point_distribute_update(bNodeTree *UNUSED(ntree), bNode *node)
+static void node_point_distribute_update(bNodeTree *ntree, bNode *node)
{
bNodeSocket *sock_min_dist = (bNodeSocket *)BLI_findlink(&node->inputs, 1);
- nodeSetSocketAvailability(sock_min_dist, ELEM(node->custom1, GEO_NODE_POINT_DISTRIBUTE_POISSON));
+ nodeSetSocketAvailability(
+ ntree, sock_min_dist, ELEM(node->custom1, GEO_NODE_POINT_DISTRIBUTE_POISSON));
}
/**
@@ -106,9 +105,9 @@ static void sample_mesh_surface(const Mesh &mesh,
float looptri_density_factor = 1.0f;
if (density_factors != nullptr) {
- const float v0_density_factor = std::max(0.0f, density_factors->get(v0_loop));
- const float v1_density_factor = std::max(0.0f, density_factors->get(v1_loop));
- const float v2_density_factor = std::max(0.0f, density_factors->get(v2_loop));
+ const float v0_density_factor = std::max(0.0f, (*density_factors)[v0_loop]);
+ const float v1_density_factor = std::max(0.0f, (*density_factors)[v1_loop]);
+ const float v2_density_factor = std::max(0.0f, (*density_factors)[v2_loop]);
looptri_density_factor = (v0_density_factor + v1_density_factor + v2_density_factor) / 3.0f;
}
const float area = area_tri_v3(v0_pos, v1_pos, v2_pos);
@@ -252,18 +251,26 @@ BLI_NOINLINE static void interpolate_attribute(const Mesh &mesh,
{
switch (source_domain) {
case ATTR_DOMAIN_POINT: {
- bke::mesh_surface_sample::sample_point_attribute(
- mesh, looptri_indices, bary_coords, source_data, output_data);
+ bke::mesh_surface_sample::sample_point_attribute(mesh,
+ looptri_indices,
+ bary_coords,
+ source_data,
+ IndexMask(output_data.size()),
+ output_data);
break;
}
case ATTR_DOMAIN_CORNER: {
- bke::mesh_surface_sample::sample_corner_attribute(
- mesh, looptri_indices, bary_coords, source_data, output_data);
+ bke::mesh_surface_sample::sample_corner_attribute(mesh,
+ looptri_indices,
+ bary_coords,
+ source_data,
+ IndexMask(output_data.size()),
+ output_data);
break;
}
case ATTR_DOMAIN_FACE: {
bke::mesh_surface_sample::sample_face_attribute(
- mesh, looptri_indices, source_data, output_data);
+ mesh, looptri_indices, source_data, IndexMask(output_data.size()), output_data);
break;
}
default: {
@@ -307,21 +314,21 @@ BLI_NOINLINE static void interpolate_existing_attributes(
}
const AttributeDomain source_domain = attribute_info->domain;
- GVArrayPtr source_attribute = source_component.attribute_get_for_read(
+ GVArray source_attribute = source_component.attribute_get_for_read(
attribute_id, source_domain, output_data_type, nullptr);
if (!source_attribute) {
i_instance += set_group.transforms.size();
continue;
}
- for (const int UNUSED(i_set_instance) : set_group.transforms.index_range()) {
+ for ([[maybe_unused]] const int i_set_instance : set_group.transforms.index_range()) {
const int offset = instance_start_offsets[i_instance];
Span<float3> bary_coords = bary_coords_array[i_instance];
Span<int> looptri_indices = looptri_indices_array[i_instance];
GMutableSpan instance_span = out_span.slice(offset, bary_coords.size());
interpolate_attribute(
- mesh, bary_coords, looptri_indices, source_domain, *source_attribute, instance_span);
+ mesh, bary_coords, looptri_indices, source_domain, source_attribute, instance_span);
i_instance++;
}
@@ -329,7 +336,7 @@ BLI_NOINLINE static void interpolate_existing_attributes(
attribute_math::convert_to_static_type(output_data_type, [&](auto dummy) {
using T = decltype(dummy);
- GVArray_Span<T> source_span{*source_attribute};
+ VArray_Span source_span{source_attribute.typed<T>()};
});
}
@@ -437,7 +444,7 @@ static void distribute_points_random(Span<GeometryInstanceGroup> set_groups,
for (const GeometryInstanceGroup &set_group : set_groups) {
const GeometrySet &set = set_group.geometry_set;
const MeshComponent &component = *set.get_component_for_read<MeshComponent>();
- GVArray_Typed<float> density_factors = component.attribute_get_for_read<float>(
+ VArray<float> density_factors = component.attribute_get_for_read<float>(
density_attribute_name, ATTR_DOMAIN_CORNER, use_one_default ? 1.0f : 0.0f);
const Mesh &mesh = *component.get_for_read();
for (const float4x4 &transform : set_group.transforms) {
@@ -447,7 +454,7 @@ static void distribute_points_random(Span<GeometryInstanceGroup> set_groups,
sample_mesh_surface(mesh,
transform,
density,
- &*density_factors,
+ &density_factors,
seed,
positions,
bary_coords,
@@ -506,10 +513,10 @@ static void distribute_points_poisson_disk(Span<GeometryInstanceGroup> set_group
const GeometrySet &set = set_group.geometry_set;
const MeshComponent &component = *set.get_component_for_read<MeshComponent>();
const Mesh &mesh = *component.get_for_read();
- const GVArray_Typed<float> density_factors = component.attribute_get_for_read<float>(
+ const VArray<float> density_factors = component.attribute_get_for_read<float>(
density_attribute_name, ATTR_DOMAIN_CORNER, use_one_default ? 1.0f : 0.0f);
- for (const int UNUSED(i_set_instance) : set_group.transforms.index_range()) {
+ for ([[maybe_unused]] const int i_set_instance : set_group.transforms.index_range()) {
Vector<float3> &positions = positions_all[i_instance];
Vector<float3> &bary_coords = bary_coords_all[i_instance];
Vector<int> &looptri_indices = looptri_indices_all[i_instance];
@@ -532,7 +539,7 @@ static void distribute_points_poisson_disk(Span<GeometryInstanceGroup> set_group
}
}
-static void geo_node_point_distribute_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
@@ -545,14 +552,14 @@ static void geo_node_point_distribute_exec(GeoNodeExecParams params)
"Density Attribute");
if (density <= 0.0f) {
- params.set_output("Geometry", GeometrySet());
+ params.set_default_remaining_outputs();
return;
}
Vector<GeometryInstanceGroup> set_groups;
geometry_set_gather_instances(geometry_set, set_groups);
if (set_groups.is_empty()) {
- params.set_output("Geometry", GeometrySet());
+ params.set_default_remaining_outputs();
return;
}
@@ -566,7 +573,7 @@ static void geo_node_point_distribute_exec(GeoNodeExecParams params)
if (set_groups.is_empty()) {
params.error_message_add(NodeWarningType::Error, TIP_("Input geometry must contain a mesh"));
- params.set_output("Geometry", GeometrySet());
+ params.set_default_remaining_outputs();
return;
}
@@ -615,6 +622,11 @@ static void geo_node_point_distribute_exec(GeoNodeExecParams params)
final_points_len += positions.size();
}
+ if (final_points_len == 0) {
+ params.set_default_remaining_outputs();
+ return;
+ }
+
PointCloud *pointcloud = BKE_pointcloud_new_nomain(final_points_len);
for (const int instance_index : positions_all.index_range()) {
const int offset = instance_start_offsets[instance_index];
@@ -641,17 +653,19 @@ static void geo_node_point_distribute_exec(GeoNodeExecParams params)
params.set_output("Geometry", std::move(geometry_set_out));
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_legacy_point_distribute_cc
void register_node_type_geo_point_distribute()
{
+ namespace file_ns = blender::nodes::node_geo_legacy_point_distribute_cc;
+
static bNodeType ntype;
geo_node_type_base(
- &ntype, GEO_NODE_LEGACY_POINT_DISTRIBUTE, "Point Distribute", NODE_CLASS_GEOMETRY, 0);
- node_type_update(&ntype, blender::nodes::node_point_distribute_update);
- ntype.declare = blender::nodes::geo_node_point_distribute_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_point_distribute_exec;
- ntype.draw_buttons = blender::nodes::geo_node_point_distribute_layout;
+ &ntype, GEO_NODE_LEGACY_POINT_DISTRIBUTE, "Point Distribute", NODE_CLASS_GEOMETRY);
+ node_type_update(&ntype, file_ns::node_point_distribute_update);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.draw_buttons = file_ns::node_layout;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_instance.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_instance.cc
index fb45c22ced4..faf0b1a5fe7 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_instance.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_instance.cc
@@ -24,19 +24,19 @@
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_legacy_point_instance_cc {
-static void geo_node_point_instance_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Object>("Object").hide_label();
- b.add_input<decl::Collection>("Collection").hide_label();
- b.add_input<decl::Geometry>("Instance Geometry");
- b.add_input<decl::Int>("Seed").min(-10000).max(10000);
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::Object>(N_("Object")).hide_label();
+ b.add_input<decl::Collection>(N_("Collection")).hide_label();
+ b.add_input<decl::Geometry>(N_("Instance Geometry"));
+ b.add_input<decl::Int>(N_("Seed")).min(-10000).max(10000);
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
-static void geo_node_point_instance_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiItemR(layout, ptr, "instance_type", 0, "", ICON_NONE);
if (RNA_enum_get(ptr, "instance_type") == GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION) {
@@ -44,16 +44,15 @@ static void geo_node_point_instance_layout(uiLayout *layout, bContext *UNUSED(C)
}
}
-static void geo_node_point_instance_init(bNodeTree *UNUSED(tree), bNode *node)
+static void node_init(bNodeTree *UNUSED(tree), bNode *node)
{
- NodeGeometryPointInstance *data = (NodeGeometryPointInstance *)MEM_callocN(
- sizeof(NodeGeometryPointInstance), __func__);
+ NodeGeometryPointInstance *data = MEM_cnew<NodeGeometryPointInstance>(__func__);
data->instance_type = GEO_NODE_POINT_INSTANCE_TYPE_OBJECT;
data->flag |= GEO_NODE_POINT_INSTANCE_WHOLE_COLLECTION;
node->storage = data;
}
-static void geo_node_point_instance_update(bNodeTree *UNUSED(tree), bNode *node)
+static void node_update(bNodeTree *ntree, bNode *node)
{
bNodeSocket *object_socket = (bNodeSocket *)BLI_findlink(&node->inputs, 1);
bNodeSocket *collection_socket = object_socket->next;
@@ -65,12 +64,15 @@ static void geo_node_point_instance_update(bNodeTree *UNUSED(tree), bNode *node)
const bool use_whole_collection = (node_storage->flag &
GEO_NODE_POINT_INSTANCE_WHOLE_COLLECTION) != 0;
- nodeSetSocketAvailability(object_socket, type == GEO_NODE_POINT_INSTANCE_TYPE_OBJECT);
- nodeSetSocketAvailability(collection_socket, type == GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION);
- nodeSetSocketAvailability(instance_geometry_socket,
- type == GEO_NODE_POINT_INSTANCE_TYPE_GEOMETRY);
+ nodeSetSocketAvailability(ntree, object_socket, type == GEO_NODE_POINT_INSTANCE_TYPE_OBJECT);
nodeSetSocketAvailability(
- seed_socket, type == GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION && !use_whole_collection);
+ ntree, collection_socket, type == GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION);
+ nodeSetSocketAvailability(
+ ntree, instance_geometry_socket, type == GEO_NODE_POINT_INSTANCE_TYPE_GEOMETRY);
+ nodeSetSocketAvailability(ntree,
+ seed_socket,
+ type == GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION &&
+ !use_whole_collection);
}
static Vector<InstanceReference> get_instance_references__object(GeoNodeExecParams &params)
@@ -170,21 +172,25 @@ static void add_instances_from_component(InstancesComponent &instances,
const AttributeDomain domain = ATTR_DOMAIN_POINT;
const int domain_size = src_geometry.attribute_domain_size(domain);
+ if (domain_size == 0) {
+ return;
+ }
- GVArray_Typed<float3> positions = src_geometry.attribute_get_for_read<float3>(
+ VArray<float3> positions = src_geometry.attribute_get_for_read<float3>(
"position", domain, {0, 0, 0});
- GVArray_Typed<float3> rotations = src_geometry.attribute_get_for_read<float3>(
+ VArray<float3> rotations = src_geometry.attribute_get_for_read<float3>(
"rotation", domain, {0, 0, 0});
- GVArray_Typed<float3> scales = src_geometry.attribute_get_for_read<float3>(
- "scale", domain, {1, 1, 1});
- GVArray_Typed<int> id_attribute = src_geometry.attribute_get_for_read<int>("id", domain, -1);
+ VArray<float3> scales = src_geometry.attribute_get_for_read<float3>("scale", domain, {1, 1, 1});
+ VArray<int> id_attribute = src_geometry.attribute_get_for_read<int>("id", domain, -1);
/* The initial size of the component might be non-zero if there are two component types. */
const int start_len = instances.instances_amount();
instances.resize(start_len + domain_size);
MutableSpan<int> handles = instances.instance_reference_handles().slice(start_len, domain_size);
MutableSpan<float4x4> transforms = instances.instance_transforms().slice(start_len, domain_size);
- MutableSpan<int> instance_ids = instances.instance_ids().slice(start_len, domain_size);
+ OutputAttribute_Typed<int> instance_id_attribute =
+ instances.attribute_try_get_for_output_only<int>("id", ATTR_DOMAIN_INSTANCE);
+ MutableSpan<int> instance_ids = instance_id_attribute.as_span();
/* Skip all of the randomness handling if there is only a single possible instance
* (anything except for collection mode with "Whole Collection" turned off). */
@@ -211,16 +217,18 @@ static void add_instances_from_component(InstancesComponent &instances,
}
});
}
+
+ instance_id_attribute.save();
}
-static void geo_node_point_instance_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
GeometrySet geometry_set_out;
/* TODO: This node should be able to instance on the input instances component
* rather than making the entire input geometry set real. */
- geometry_set = geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
const Vector<InstanceReference> possible_references = get_instance_references(params);
if (possible_references.is_empty()) {
@@ -253,20 +261,22 @@ static void geo_node_point_instance_exec(GeoNodeExecParams params)
params.set_output("Geometry", std::move(geometry_set_out));
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_legacy_point_instance_cc
void register_node_type_geo_point_instance()
{
+ namespace file_ns = blender::nodes::node_geo_legacy_point_instance_cc;
+
static bNodeType ntype;
geo_node_type_base(
- &ntype, GEO_NODE_LEGACY_POINT_INSTANCE, "Point Instance", NODE_CLASS_GEOMETRY, 0);
- node_type_init(&ntype, blender::nodes::geo_node_point_instance_init);
+ &ntype, GEO_NODE_LEGACY_POINT_INSTANCE, "Point Instance", NODE_CLASS_GEOMETRY);
+ node_type_init(&ntype, file_ns::node_init);
node_type_storage(
&ntype, "NodeGeometryPointInstance", node_free_standard_storage, node_copy_standard_storage);
- ntype.declare = blender::nodes::geo_node_point_instance_declare;
- ntype.draw_buttons = blender::nodes::geo_node_point_instance_layout;
- node_type_update(&ntype, blender::nodes::geo_node_point_instance_update);
- ntype.geometry_node_execute = blender::nodes::geo_node_point_instance_exec;
+ ntype.declare = file_ns::node_declare;
+ ntype.draw_buttons = file_ns::node_layout;
+ node_type_update(&ntype, file_ns::node_update);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_rotate.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_rotate.cc
index 60c82360007..ad87ec5541b 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_rotate.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_rotate.cc
@@ -21,21 +21,23 @@
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_legacy_point_rotate_cc {
-static void geo_node_point_rotate_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::String>("Axis");
- b.add_input<decl::Vector>("Axis", "Axis_001").default_value({0.0, 0.0, 1.0}).subtype(PROP_XYZ);
- b.add_input<decl::String>("Angle");
- b.add_input<decl::Float>("Angle", "Angle_001").subtype(PROP_ANGLE);
- b.add_input<decl::String>("Rotation");
- b.add_input<decl::Vector>("Rotation", "Rotation_001").subtype(PROP_EULER);
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::String>(N_("Axis"));
+ b.add_input<decl::Vector>(N_("Axis"), "Axis_001")
+ .default_value({0.0, 0.0, 1.0})
+ .subtype(PROP_XYZ);
+ b.add_input<decl::String>(N_("Angle"));
+ b.add_input<decl::Float>(N_("Angle"), "Angle_001").subtype(PROP_ANGLE);
+ b.add_input<decl::String>(N_("Rotation"));
+ b.add_input<decl::Vector>(N_("Rotation"), "Rotation_001").subtype(PROP_EULER);
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
-static void geo_node_point_rotate_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
NodeGeometryRotatePoints *storage = (NodeGeometryRotatePoints *)((bNode *)ptr->data)->storage;
@@ -55,10 +57,9 @@ static void geo_node_point_rotate_layout(uiLayout *layout, bContext *UNUSED(C),
}
}
-static void geo_node_point_rotate_init(bNodeTree *UNUSED(ntree), bNode *node)
+static void node_init(bNodeTree *UNUSED(ntree), bNode *node)
{
- NodeGeometryRotatePoints *node_storage = (NodeGeometryRotatePoints *)MEM_callocN(
- sizeof(NodeGeometryRotatePoints), __func__);
+ NodeGeometryRotatePoints *node_storage = MEM_cnew<NodeGeometryRotatePoints>(__func__);
node_storage->type = GEO_NODE_POINT_ROTATE_TYPE_EULER;
node_storage->space = GEO_NODE_POINT_ROTATE_SPACE_OBJECT;
@@ -69,20 +70,23 @@ static void geo_node_point_rotate_init(bNodeTree *UNUSED(ntree), bNode *node)
node->storage = node_storage;
}
-static void geo_node_point_rotate_update(bNodeTree *UNUSED(ntree), bNode *node)
+static void node_update(bNodeTree *ntree, bNode *node)
{
NodeGeometryRotatePoints *node_storage = (NodeGeometryRotatePoints *)node->storage;
update_attribute_input_socket_availabilities(
+ *ntree,
*node,
"Axis",
(GeometryNodeAttributeInputMode)node_storage->input_type_axis,
node_storage->type == GEO_NODE_POINT_ROTATE_TYPE_AXIS_ANGLE);
update_attribute_input_socket_availabilities(
+ *ntree,
*node,
"Angle",
(GeometryNodeAttributeInputMode)node_storage->input_type_angle,
node_storage->type == GEO_NODE_POINT_ROTATE_TYPE_AXIS_ANGLE);
update_attribute_input_socket_availabilities(
+ *ntree,
*node,
"Rotation",
(GeometryNodeAttributeInputMode)node_storage->input_type_rotation,
@@ -167,9 +171,9 @@ static void point_rotate_on_component(GeometryComponent &component,
const int domain_size = rotations.size();
if (storage.type == GEO_NODE_POINT_ROTATE_TYPE_AXIS_ANGLE) {
- GVArray_Typed<float3> axis = params.get_input_attribute<float3>(
+ VArray<float3> axis = params.get_input_attribute<float3>(
"Axis", component, ATTR_DOMAIN_POINT, {0, 0, 1});
- GVArray_Typed<float> angles = params.get_input_attribute<float>(
+ VArray<float> angles = params.get_input_attribute<float>(
"Angle", component, ATTR_DOMAIN_POINT, 0);
if (storage.space == GEO_NODE_POINT_ROTATE_SPACE_OBJECT) {
@@ -180,7 +184,7 @@ static void point_rotate_on_component(GeometryComponent &component,
}
}
else {
- GVArray_Typed<float3> eulers = params.get_input_attribute<float3>(
+ VArray<float3> eulers = params.get_input_attribute<float3>(
"Rotation", component, ATTR_DOMAIN_POINT, {0, 0, 0});
if (storage.space == GEO_NODE_POINT_ROTATE_SPACE_OBJECT) {
@@ -194,11 +198,11 @@ static void point_rotate_on_component(GeometryComponent &component,
rotation_attribute.save();
}
-static void geo_node_point_rotate_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
if (geometry_set.has<MeshComponent>()) {
point_rotate_on_component(geometry_set.get_component_for_write<MeshComponent>(), params);
@@ -213,19 +217,21 @@ static void geo_node_point_rotate_exec(GeoNodeExecParams params)
params.set_output("Geometry", geometry_set);
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_legacy_point_rotate_cc
void register_node_type_geo_point_rotate()
{
+ namespace file_ns = blender::nodes::node_geo_legacy_point_rotate_cc;
+
static bNodeType ntype;
- geo_node_type_base(&ntype, GEO_NODE_LEGACY_POINT_ROTATE, "Point Rotate", NODE_CLASS_GEOMETRY, 0);
- node_type_init(&ntype, blender::nodes::geo_node_point_rotate_init);
- node_type_update(&ntype, blender::nodes::geo_node_point_rotate_update);
+ geo_node_type_base(&ntype, GEO_NODE_LEGACY_POINT_ROTATE, "Point Rotate", NODE_CLASS_GEOMETRY);
+ node_type_init(&ntype, file_ns::node_init);
+ node_type_update(&ntype, file_ns::node_update);
node_type_storage(
&ntype, "NodeGeometryRotatePoints", node_free_standard_storage, node_copy_standard_storage);
- ntype.declare = blender::nodes::geo_node_point_rotate_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_point_rotate_exec;
- ntype.draw_buttons = blender::nodes::geo_node_point_rotate_layout;
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.draw_buttons = file_ns::node_layout;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_scale.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_scale.cc
index 99adce149e9..69e69a24e29 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_scale.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_scale.cc
@@ -21,41 +21,40 @@
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_legacy_point_scale_cc {
-static void geo_node_point_scale_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::String>("Factor");
- b.add_input<decl::Vector>("Factor", "Factor_001")
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::String>(N_("Factor"));
+ b.add_input<decl::Vector>(N_("Factor"), "Factor_001")
.default_value({1.0f, 1.0f, 1.0f})
.subtype(PROP_XYZ);
- b.add_input<decl::Float>("Factor", "Factor_002").default_value(1.0f).min(0.0f);
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Float>(N_("Factor"), "Factor_002").default_value(1.0f).min(0.0f);
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
-static void geo_node_point_scale_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
uiItemR(layout, ptr, "input_type", 0, IFACE_("Type"), ICON_NONE);
}
-static void geo_node_point_scale_init(bNodeTree *UNUSED(tree), bNode *node)
+static void node_init(bNodeTree *UNUSED(tree), bNode *node)
{
- NodeGeometryPointScale *data = (NodeGeometryPointScale *)MEM_callocN(
- sizeof(NodeGeometryPointScale), __func__);
+ NodeGeometryPointScale *data = MEM_cnew<NodeGeometryPointScale>(__func__);
data->input_type = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
node->storage = data;
}
-static void geo_node_point_scale_update(bNodeTree *UNUSED(ntree), bNode *node)
+static void node_update(bNodeTree *ntree, bNode *node)
{
NodeGeometryPointScale &node_storage = *(NodeGeometryPointScale *)node->storage;
update_attribute_input_socket_availabilities(
- *node, "Factor", (GeometryNodeAttributeInputMode)node_storage.input_type);
+ *ntree, *node, "Factor", (GeometryNodeAttributeInputMode)node_storage.input_type);
}
static void execute_on_component(GeoNodeExecParams params, GeometryComponent &component)
@@ -78,7 +77,7 @@ static void execute_on_component(GeoNodeExecParams params, GeometryComponent &co
const CustomDataType data_type = (input_type == GEO_NODE_ATTRIBUTE_INPUT_FLOAT) ? CD_PROP_FLOAT :
CD_PROP_FLOAT3;
- GVArrayPtr attribute = params.get_input_attribute(
+ GVArray attribute = params.get_input_attribute(
"Factor", component, ATTR_DOMAIN_POINT, data_type, nullptr);
if (!attribute) {
return;
@@ -86,13 +85,13 @@ static void execute_on_component(GeoNodeExecParams params, GeometryComponent &co
MutableSpan<float3> scale_span = scale_attribute.as_span();
if (data_type == CD_PROP_FLOAT) {
- GVArray_Typed<float> factors{*attribute};
+ VArray<float> factors = attribute.typed<float>();
for (const int i : scale_span.index_range()) {
scale_span[i] = scale_span[i] * factors[i];
}
}
else if (data_type == CD_PROP_FLOAT3) {
- GVArray_Typed<float3> factors{*attribute};
+ VArray<float3> factors = attribute.typed<float3>();
for (const int i : scale_span.index_range()) {
scale_span[i] = scale_span[i] * factors[i];
}
@@ -101,11 +100,11 @@ static void execute_on_component(GeoNodeExecParams params, GeometryComponent &co
scale_attribute.save();
}
-static void geo_node_point_scale_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
if (geometry_set.has<MeshComponent>()) {
execute_on_component(params, geometry_set.get_component_for_write<MeshComponent>());
@@ -120,20 +119,22 @@ static void geo_node_point_scale_exec(GeoNodeExecParams params)
params.set_output("Geometry", std::move(geometry_set));
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_legacy_point_scale_cc
void register_node_type_geo_point_scale()
{
+ namespace file_ns = blender::nodes::node_geo_legacy_point_scale_cc;
+
static bNodeType ntype;
- geo_node_type_base(&ntype, GEO_NODE_LEGACY_POINT_SCALE, "Point Scale", NODE_CLASS_GEOMETRY, 0);
+ geo_node_type_base(&ntype, GEO_NODE_LEGACY_POINT_SCALE, "Point Scale", NODE_CLASS_GEOMETRY);
- ntype.declare = blender::nodes::geo_node_point_scale_declare;
- node_type_init(&ntype, blender::nodes::geo_node_point_scale_init);
- node_type_update(&ntype, blender::nodes::geo_node_point_scale_update);
+ ntype.declare = file_ns::node_declare;
+ node_type_init(&ntype, file_ns::node_init);
+ node_type_update(&ntype, file_ns::node_update);
node_type_storage(
&ntype, "NodeGeometryPointScale", node_free_standard_storage, node_copy_standard_storage);
- ntype.geometry_node_execute = blender::nodes::geo_node_point_scale_exec;
- ntype.draw_buttons = blender::nodes::geo_node_point_scale_layout;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.draw_buttons = file_ns::node_layout;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_separate.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_separate.cc
index 48b6676c1dd..b9760587706 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_separate.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_separate.cc
@@ -25,14 +25,6 @@
namespace blender::nodes {
-static void geo_node_point_instance_declare(NodeDeclarationBuilder &b)
-{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::String>("Mask");
- b.add_output<decl::Geometry>("Geometry 1");
- b.add_output<decl::Geometry>("Geometry 2");
-}
-
template<typename T>
static void copy_data_based_on_mask(Span<T> data,
Span<bool> masks,
@@ -55,7 +47,7 @@ void copy_point_attributes_based_on_mask(const GeometryComponent &in_component,
{
for (const AttributeIDRef &attribute_id : in_component.attribute_ids()) {
ReadAttributeLookup attribute = in_component.attribute_try_get_for_read(attribute_id);
- const CustomDataType data_type = bke::cpp_type_to_custom_data_type(attribute.varray->type());
+ const CustomDataType data_type = bke::cpp_type_to_custom_data_type(attribute.varray.type());
/* Only copy point attributes. Theoretically this could interpolate attributes on other
* domains to the point domain, but that would conflict with attributes that are built-in
@@ -69,7 +61,7 @@ void copy_point_attributes_based_on_mask(const GeometryComponent &in_component,
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
using T = decltype(dummy);
- GVArray_Span<T> span{*attribute.varray};
+ VArray_Span span{attribute.varray.typed<T>()};
MutableSpan<T> out_span = result_attribute.as_span<T>();
copy_data_based_on_mask(span, masks, invert, out_span);
});
@@ -78,6 +70,18 @@ void copy_point_attributes_based_on_mask(const GeometryComponent &in_component,
}
}
+} // namespace blender::nodes
+
+namespace blender::nodes::node_geo_legacy_point_separate_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::String>(N_("Mask"));
+ b.add_output<decl::Geometry>(N_("Geometry 1"));
+ b.add_output<decl::Geometry>(N_("Geometry 2"));
+}
+
static void create_component_points(GeometryComponent &component, const int total)
{
switch (component.type()) {
@@ -103,7 +107,7 @@ static void separate_points_from_component(const GeometryComponent &in_component
return;
}
- const GVArray_Typed<bool> mask_attribute = in_component.attribute_get_for_read<bool>(
+ const VArray<bool> mask_attribute = in_component.attribute_get_for_read<bool>(
mask_name, ATTR_DOMAIN_POINT, false);
VArray_Span<bool> masks{mask_attribute};
@@ -133,7 +137,7 @@ static GeometrySet separate_geometry_set(const GeometrySet &set_in,
return set_out;
}
-static void geo_node_point_separate_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
bool wait_for_inputs = false;
wait_for_inputs |= params.lazy_require_input("Geometry");
@@ -146,7 +150,7 @@ static void geo_node_point_separate_exec(GeoNodeExecParams params)
/* TODO: This is not necessary-- the input geometry set can be read only,
* but it must be rewritten to handle instance groups. */
- geometry_set = geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
if (params.lazy_output_is_required("Geometry 1")) {
params.set_output("Geometry 1",
@@ -158,16 +162,18 @@ static void geo_node_point_separate_exec(GeoNodeExecParams params)
}
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_legacy_point_separate_cc
void register_node_type_geo_point_separate()
{
+ namespace file_ns = blender::nodes::node_geo_legacy_point_separate_cc;
+
static bNodeType ntype;
geo_node_type_base(
- &ntype, GEO_NODE_LEGACY_POINT_SEPARATE, "Point Separate", NODE_CLASS_GEOMETRY, 0);
- ntype.declare = blender::nodes::geo_node_point_instance_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_point_separate_exec;
+ &ntype, GEO_NODE_LEGACY_POINT_SEPARATE, "Point Separate", NODE_CLASS_GEOMETRY);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
ntype.geometry_node_execute_supports_laziness = true;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_translate.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_translate.cc
index f2fce45c57b..385c3d9f22d 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_translate.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_translate.cc
@@ -19,17 +19,17 @@
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_legacy_point_translate_cc {
-static void geo_node_point_translate_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::String>("Translation");
- b.add_input<decl::Vector>("Translation", "Translation_001").subtype(PROP_TRANSLATION);
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::String>(N_("Translation"));
+ b.add_input<decl::Vector>(N_("Translation"), "Translation_001").subtype(PROP_TRANSLATION);
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
-static void geo_node_point_translate_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
@@ -43,21 +43,21 @@ static void execute_on_component(GeoNodeExecParams params, GeometryComponent &co
if (!position_attribute) {
return;
}
- GVArray_Typed<float3> attribute = params.get_input_attribute<float3>(
+ VArray<float3> attribute = params.get_input_attribute<float3>(
"Translation", component, ATTR_DOMAIN_POINT, {0, 0, 0});
- for (const int i : IndexRange(attribute.size())) {
+ for (const int i : attribute.index_range()) {
position_attribute->set(i, position_attribute->get(i) + attribute[i]);
}
position_attribute.save();
}
-static void geo_node_point_translate_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
if (geometry_set.has<MeshComponent>()) {
execute_on_component(params, geometry_set.get_component_for_write<MeshComponent>());
@@ -72,39 +72,40 @@ static void geo_node_point_translate_exec(GeoNodeExecParams params)
params.set_output("Geometry", std::move(geometry_set));
}
-static void geo_node_point_translate_init(bNodeTree *UNUSED(tree), bNode *node)
+static void node_init(bNodeTree *UNUSED(tree), bNode *node)
{
- NodeGeometryPointTranslate *data = (NodeGeometryPointTranslate *)MEM_callocN(
- sizeof(NodeGeometryPointTranslate), __func__);
+ NodeGeometryPointTranslate *data = MEM_cnew<NodeGeometryPointTranslate>(__func__);
data->input_type = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
node->storage = data;
}
-static void geo_node_point_translate_update(bNodeTree *UNUSED(ntree), bNode *node)
+static void node_update(bNodeTree *ntree, bNode *node)
{
NodeGeometryPointTranslate &node_storage = *(NodeGeometryPointTranslate *)node->storage;
update_attribute_input_socket_availabilities(
- *node, "Translation", (GeometryNodeAttributeInputMode)node_storage.input_type);
+ *ntree, *node, "Translation", (GeometryNodeAttributeInputMode)node_storage.input_type);
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_legacy_point_translate_cc
void register_node_type_geo_point_translate()
{
+ namespace file_ns = blender::nodes::node_geo_legacy_point_translate_cc;
+
static bNodeType ntype;
geo_node_type_base(
- &ntype, GEO_NODE_LEGACY_POINT_TRANSLATE, "Point Translate", NODE_CLASS_GEOMETRY, 0);
- node_type_init(&ntype, blender::nodes::geo_node_point_translate_init);
- node_type_update(&ntype, blender::nodes::geo_node_point_translate_update);
+ &ntype, GEO_NODE_LEGACY_POINT_TRANSLATE, "Point Translate", NODE_CLASS_GEOMETRY);
+ node_type_init(&ntype, file_ns::node_init);
+ node_type_update(&ntype, file_ns::node_update);
node_type_storage(&ntype,
"NodeGeometryPointTranslate",
node_free_standard_storage,
node_copy_standard_storage);
- ntype.declare = blender::nodes::geo_node_point_translate_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_point_translate_exec;
- ntype.draw_buttons = blender::nodes::geo_node_point_translate_layout;
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.draw_buttons = file_ns::node_layout;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_points_to_volume.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_points_to_volume.cc
index d920c8de9f0..f54ffc53a6e 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_points_to_volume.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_points_to_volume.cc
@@ -28,22 +28,20 @@
#include "UI_interface.h"
#include "UI_resources.h"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_legacy_points_to_volume_cc {
-static void geo_node_points_to_volume_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Float>("Density").default_value(1.0f).min(0.0f);
- b.add_input<decl::Float>("Voxel Size").default_value(0.3f).min(0.01f).subtype(PROP_DISTANCE);
- b.add_input<decl::Float>("Voxel Amount").default_value(64.0f).min(0.0f);
- b.add_input<decl::String>("Radius");
- b.add_input<decl::Float>("Radius", "Radius_001").default_value(0.5f).min(0.0f);
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::Float>(N_("Density")).default_value(1.0f).min(0.0f);
+ b.add_input<decl::Float>(N_("Voxel Size")).default_value(0.3f).min(0.01f).subtype(PROP_DISTANCE);
+ b.add_input<decl::Float>(N_("Voxel Amount")).default_value(64.0f).min(0.0f);
+ b.add_input<decl::String>(N_("Radius"));
+ b.add_input<decl::Float>(N_("Radius"), "Radius_001").default_value(0.5f).min(0.0f);
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
-static void geo_node_points_to_volume_layout(uiLayout *layout,
- bContext *UNUSED(C),
- PointerRNA *ptr)
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
@@ -51,10 +49,9 @@ static void geo_node_points_to_volume_layout(uiLayout *layout,
uiItemR(layout, ptr, "input_type_radius", 0, IFACE_("Radius"), ICON_NONE);
}
-static void geo_node_points_to_volume_init(bNodeTree *UNUSED(ntree), bNode *node)
+static void node_init(bNodeTree *UNUSED(ntree), bNode *node)
{
- NodeGeometryPointsToVolume *data = (NodeGeometryPointsToVolume *)MEM_callocN(
- sizeof(NodeGeometryPointsToVolume), __func__);
+ NodeGeometryPointsToVolume *data = MEM_cnew<NodeGeometryPointsToVolume>(__func__);
data->resolution_mode = GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_AMOUNT;
data->input_type_radius = GEO_NODE_ATTRIBUTE_INPUT_FLOAT;
node->storage = data;
@@ -65,19 +62,22 @@ static void geo_node_points_to_volume_init(bNodeTree *UNUSED(ntree), bNode *node
STRNCPY(radius_attribute_socket_value->value, "radius");
}
-static void geo_node_points_to_volume_update(bNodeTree *UNUSED(ntree), bNode *node)
+static void node_update(bNodeTree *ntree, bNode *node)
{
NodeGeometryPointsToVolume *data = (NodeGeometryPointsToVolume *)node->storage;
bNodeSocket *voxel_size_socket = nodeFindSocket(node, SOCK_IN, "Voxel Size");
bNodeSocket *voxel_amount_socket = nodeFindSocket(node, SOCK_IN, "Voxel Amount");
- nodeSetSocketAvailability(voxel_amount_socket,
+ nodeSetSocketAvailability(ntree,
+ voxel_amount_socket,
data->resolution_mode ==
GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_AMOUNT);
- nodeSetSocketAvailability(
- voxel_size_socket, data->resolution_mode == GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_SIZE);
+ nodeSetSocketAvailability(ntree,
+ voxel_size_socket,
+ data->resolution_mode ==
+ GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_SIZE);
update_attribute_input_socket_availabilities(
- *node, "Radius", (GeometryNodeAttributeInputMode)data->input_type_radius);
+ *ntree, *node, "Radius", (GeometryNodeAttributeInputMode)data->input_type_radius);
}
#ifdef WITH_OPENVDB
@@ -161,7 +161,7 @@ static float compute_voxel_size(const GeoNodeExecParams &params,
}
/* The voxel size adapts to the final size of the volume. */
- const float diagonal = float3::distance(min, max);
+ const float diagonal = math::distance(min, max);
const float extended_diagonal = diagonal + 2.0f * radius;
const float voxel_size = extended_diagonal / voxel_amount;
return voxel_size;
@@ -172,12 +172,12 @@ static void gather_point_data_from_component(const GeoNodeExecParams &params,
Vector<float3> &r_positions,
Vector<float> &r_radii)
{
- GVArray_Typed<float3> positions = component.attribute_get_for_read<float3>(
+ VArray<float3> positions = component.attribute_get_for_read<float3>(
"position", ATTR_DOMAIN_POINT, {0, 0, 0});
- GVArray_Typed<float> radii = params.get_input_attribute<float>(
+ VArray<float> radii = params.get_input_attribute<float>(
"Radius", component, ATTR_DOMAIN_POINT, 0.0f);
- for (const int i : IndexRange(positions.size())) {
+ for (const int i : positions.index_range()) {
r_positions.append(positions[i]);
r_radii.append(radii[i]);
}
@@ -241,13 +241,13 @@ static void initialize_volume_component_from_points(const GeometrySet &geometry_
}
#endif
-static void geo_node_points_to_volume_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set_in = params.extract_input<GeometrySet>("Geometry");
GeometrySet geometry_set_out;
/* TODO: Read-only access to instances should be supported here, for now they are made real. */
- geometry_set_in = geometry_set_realize_instances(geometry_set_in);
+ geometry_set_in = geometry::realize_instances_legacy(geometry_set_in);
#ifdef WITH_OPENVDB
initialize_volume_component_from_points(geometry_set_in, geometry_set_out, params);
@@ -256,23 +256,25 @@ static void geo_node_points_to_volume_exec(GeoNodeExecParams params)
params.set_output("Geometry", std::move(geometry_set_out));
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_legacy_points_to_volume_cc
-void register_node_type_geo_points_to_volume()
+void register_node_type_geo_legacy_points_to_volume()
{
+ namespace file_ns = blender::nodes::node_geo_legacy_points_to_volume_cc;
+
static bNodeType ntype;
geo_node_type_base(
- &ntype, GEO_NODE_LEGACY_POINTS_TO_VOLUME, "Points to Volume", NODE_CLASS_GEOMETRY, 0);
+ &ntype, GEO_NODE_LEGACY_POINTS_TO_VOLUME, "Points to Volume", NODE_CLASS_GEOMETRY);
node_type_storage(&ntype,
"NodeGeometryPointsToVolume",
node_free_standard_storage,
node_copy_standard_storage);
node_type_size(&ntype, 170, 120, 700);
- node_type_init(&ntype, blender::nodes::geo_node_points_to_volume_init);
- node_type_update(&ntype, blender::nodes::geo_node_points_to_volume_update);
- ntype.declare = blender::nodes::geo_node_points_to_volume_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_points_to_volume_exec;
- ntype.draw_buttons = blender::nodes::geo_node_points_to_volume_layout;
+ node_type_init(&ntype, file_ns::node_init);
+ node_type_update(&ntype, file_ns::node_update);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.draw_buttons = file_ns::node_layout;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_raycast.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_raycast.cc
index 401a478f04c..cfae88e0625 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_raycast.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_raycast.cc
@@ -24,30 +24,30 @@
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_legacy_raycast_cc {
-static void geo_node_raycast_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Geometry>("Target Geometry");
- b.add_input<decl::String>("Ray Direction");
- b.add_input<decl::Vector>("Ray Direction", "Ray Direction_001")
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::Geometry>(N_("Target Geometry"));
+ b.add_input<decl::String>(N_("Ray Direction"));
+ b.add_input<decl::Vector>(N_("Ray Direction"), "Ray Direction_001")
.default_value({0.0f, 0.0f, 1.0f});
- b.add_input<decl::String>("Ray Length");
- b.add_input<decl::Float>("Ray Length", "Ray Length_001")
+ b.add_input<decl::String>(N_("Ray Length"));
+ b.add_input<decl::Float>(N_("Ray Length"), "Ray Length_001")
.default_value(100.0f)
.min(0.0f)
.subtype(PROP_DISTANCE);
- b.add_input<decl::String>("Target Attribute");
- b.add_input<decl::String>("Is Hit");
- b.add_input<decl::String>("Hit Position");
- b.add_input<decl::String>("Hit Normal");
- b.add_input<decl::String>("Hit Distance");
- b.add_input<decl::String>("Hit Attribute");
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::String>(N_("Target Attribute"));
+ b.add_input<decl::String>(N_("Is Hit"));
+ b.add_input<decl::String>(N_("Hit Position"));
+ b.add_input<decl::String>(N_("Hit Normal"));
+ b.add_input<decl::String>(N_("Hit Distance"));
+ b.add_input<decl::String>(N_("Hit Attribute"));
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
-static void geo_node_raycast_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
@@ -56,24 +56,27 @@ static void geo_node_raycast_layout(uiLayout *layout, bContext *UNUSED(C), Point
uiItemR(layout, ptr, "input_type_ray_length", 0, IFACE_("Ray Length"), ICON_NONE);
}
-static void geo_node_raycast_init(bNodeTree *UNUSED(tree), bNode *node)
+static void node_init(bNodeTree *UNUSED(tree), bNode *node)
{
- NodeGeometryRaycast *data = (NodeGeometryRaycast *)MEM_callocN(sizeof(NodeGeometryRaycast),
- __func__);
+ NodeGeometryRaycast *data = MEM_cnew<NodeGeometryRaycast>(__func__);
data->input_type_ray_direction = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
data->input_type_ray_length = GEO_NODE_ATTRIBUTE_INPUT_FLOAT;
node->storage = data;
}
-static void geo_node_raycast_update(bNodeTree *UNUSED(ntree), bNode *node)
+static void node_update(bNodeTree *ntree, bNode *node)
{
NodeGeometryRaycast *node_storage = (NodeGeometryRaycast *)node->storage;
update_attribute_input_socket_availabilities(
+ *ntree,
*node,
"Ray Direction",
(GeometryNodeAttributeInputMode)node_storage->input_type_ray_direction);
update_attribute_input_socket_availabilities(
- *node, "Ray Length", (GeometryNodeAttributeInputMode)node_storage->input_type_ray_length);
+ *ntree,
+ *node,
+ "Ray Length",
+ (GeometryNodeAttributeInputMode)node_storage->input_type_ray_length);
}
static void raycast_to_mesh(const Mesh &mesh,
@@ -104,7 +107,7 @@ static void raycast_to_mesh(const Mesh &mesh,
for (const int i : ray_origins.index_range()) {
const float ray_length = ray_lengths[i];
const float3 ray_origin = ray_origins[i];
- const float3 ray_direction = ray_directions[i].normalized();
+ const float3 ray_direction = math::normalize(ray_directions[i]);
BVHTreeRayHit hit;
hit.index = -1;
@@ -197,11 +200,11 @@ static void raycast_from_points(const GeoNodeExecParams &params,
(GeometryNodeRaycastMapMode)storage.mapping);
const AttributeDomain result_domain = ATTR_DOMAIN_POINT;
- GVArray_Typed<float3> ray_origins = dst_component.attribute_get_for_read<float3>(
+ VArray<float3> ray_origins = dst_component.attribute_get_for_read<float3>(
"position", result_domain, {0, 0, 0});
- GVArray_Typed<float3> ray_directions = params.get_input_attribute<float3>(
+ VArray<float3> ray_directions = params.get_input_attribute<float3>(
"Ray Direction", dst_component, result_domain, {0, 0, 0});
- GVArray_Typed<float> ray_lengths = params.get_input_attribute<float>(
+ VArray<float> ray_lengths = params.get_input_attribute<float>(
"Ray Length", dst_component, result_domain, 0);
OutputAttribute_Typed<bool> hit_attribute =
@@ -218,10 +221,10 @@ static void raycast_from_points(const GeoNodeExecParams &params,
Array<int> hit_indices;
Array<float3> hit_positions_internal;
if (!hit_attribute_names.is_empty()) {
- hit_indices.reinitialize(ray_origins->size());
+ hit_indices.reinitialize(ray_origins.size());
if (!hit_position_attribute) {
- hit_positions_internal.reinitialize(ray_origins->size());
+ hit_positions_internal.reinitialize(ray_origins.size());
}
}
const MutableSpan<bool> is_hit = hit_attribute ? hit_attribute.as_span() : MutableSpan<bool>();
@@ -250,7 +253,8 @@ static void raycast_from_points(const GeoNodeExecParams &params,
hit_distance_attribute.save();
/* Custom interpolated attributes */
- bke::mesh_surface_sample::MeshAttributeInterpolator interp(src_mesh, hit_positions, hit_indices);
+ bke::mesh_surface_sample::MeshAttributeInterpolator interp(
+ src_mesh, IndexMask(ray_origins.size()), hit_positions, hit_indices);
for (const int i : hit_attribute_names.index_range()) {
const std::optional<AttributeMetaData> meta_data = src_mesh_component->attribute_get_meta_data(
hit_attribute_names[i]);
@@ -267,7 +271,7 @@ static void raycast_from_points(const GeoNodeExecParams &params,
}
}
-static void geo_node_raycast_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
GeometrySet target_geometry_set = params.extract_input<GeometrySet>("Target Geometry");
@@ -280,8 +284,8 @@ static void geo_node_raycast_exec(GeoNodeExecParams params)
const Array<std::string> hit_names = {params.extract_input<std::string>("Target Attribute")};
const Array<std::string> hit_output_names = {params.extract_input<std::string>("Hit Attribute")};
- geometry_set = bke::geometry_set_realize_instances(geometry_set);
- target_geometry_set = bke::geometry_set_realize_instances(target_geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
+ target_geometry_set = geometry::realize_instances_legacy(target_geometry_set);
static const Array<GeometryComponentType> types = {
GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE};
@@ -302,20 +306,22 @@ static void geo_node_raycast_exec(GeoNodeExecParams params)
params.set_output("Geometry", geometry_set);
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_legacy_raycast_cc
-void register_node_type_geo_raycast()
+void register_node_type_geo_legacy_raycast()
{
+ namespace file_ns = blender::nodes::node_geo_legacy_raycast_cc;
+
static bNodeType ntype;
- geo_node_type_base(&ntype, GEO_NODE_LEGACY_RAYCAST, "Raycast", NODE_CLASS_GEOMETRY, 0);
+ geo_node_type_base(&ntype, GEO_NODE_LEGACY_RAYCAST, "Raycast", NODE_CLASS_GEOMETRY);
node_type_size_preset(&ntype, NODE_SIZE_LARGE);
- node_type_init(&ntype, blender::nodes::geo_node_raycast_init);
- node_type_update(&ntype, blender::nodes::geo_node_raycast_update);
+ node_type_init(&ntype, file_ns::node_init);
+ node_type_update(&ntype, file_ns::node_update);
node_type_storage(
&ntype, "NodeGeometryRaycast", node_free_standard_storage, node_copy_standard_storage);
- ntype.declare = blender::nodes::geo_node_raycast_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_raycast_exec;
- ntype.draw_buttons = blender::nodes::geo_node_raycast_layout;
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.draw_buttons = file_ns::node_layout;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_select_by_material.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_select_by_material.cc
index eabdd2bcd5a..59ac697b658 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_select_by_material.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_select_by_material.cc
@@ -26,14 +26,14 @@
#include "BKE_material.h"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_legacy_select_by_material_cc {
-static void geo_node_legacy_select_by_material_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Material>("Material").hide_label();
- b.add_input<decl::String>("Selection");
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::Material>(N_("Material")).hide_label();
+ b.add_input<decl::String>(N_("Selection"));
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
static void select_mesh_by_material(const Mesh &mesh,
@@ -54,13 +54,13 @@ static void select_mesh_by_material(const Mesh &mesh,
});
}
-static void geo_node_legacy_select_by_material_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
Material *material = params.extract_input<Material *>("Material");
const std::string selection_name = params.extract_input<std::string>("Selection");
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
if (geometry_set.has<MeshComponent>()) {
MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
@@ -78,15 +78,17 @@ static void geo_node_legacy_select_by_material_exec(GeoNodeExecParams params)
params.set_output("Geometry", std::move(geometry_set));
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_legacy_select_by_material_cc
void register_node_type_geo_legacy_select_by_material()
{
+ namespace file_ns = blender::nodes::node_geo_legacy_select_by_material_cc;
+
static bNodeType ntype;
geo_node_type_base(
- &ntype, GEO_NODE_LEGACY_SELECT_BY_MATERIAL, "Select by Material", NODE_CLASS_GEOMETRY, 0);
- ntype.declare = blender::nodes::geo_node_legacy_select_by_material_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_legacy_select_by_material_exec;
+ &ntype, GEO_NODE_LEGACY_SELECT_BY_MATERIAL, "Select by Material", NODE_CLASS_GEOMETRY);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_subdivision_surface.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_subdivision_surface.cc
index 07d3f89bdb7..7c5553cb5e4 100644
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_subdivision_surface.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_subdivision_surface.cc
@@ -23,19 +23,17 @@
#include "UI_resources.h"
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_legacy_subdivision_surface_cc {
-static void geo_node_subdivision_surface_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Int>("Level").default_value(1).min(0).max(6);
- b.add_input<decl::Bool>("Use Creases");
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::Int>(N_("Level")).default_value(1).min(0).max(6);
+ b.add_input<decl::Bool>(N_("Use Creases"));
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
-static void geo_node_subdivision_surface_layout(uiLayout *layout,
- bContext *UNUSED(C),
- PointerRNA *ptr)
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
#ifdef WITH_OPENSUBDIV
uiLayoutSetPropSep(layout, true);
@@ -47,20 +45,19 @@ static void geo_node_subdivision_surface_layout(uiLayout *layout,
#endif
}
-static void geo_node_subdivision_surface_init(bNodeTree *UNUSED(ntree), bNode *node)
+static void node_init(bNodeTree *UNUSED(ntree), bNode *node)
{
- NodeGeometrySubdivisionSurface *data = (NodeGeometrySubdivisionSurface *)MEM_callocN(
- sizeof(NodeGeometrySubdivisionSurface), __func__);
+ NodeGeometrySubdivisionSurface *data = MEM_cnew<NodeGeometrySubdivisionSurface>(__func__);
data->uv_smooth = SUBSURF_UV_SMOOTH_PRESERVE_BOUNDARIES;
data->boundary_smooth = SUBSURF_BOUNDARY_SMOOTH_ALL;
node->storage = data;
}
-static void geo_node_subdivision_surface_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = geometry_set_realize_instances(geometry_set);
+ geometry_set = geometry::realize_instances_legacy(geometry_set);
if (!geometry_set.has_mesh()) {
params.set_output("Geometry", geometry_set);
@@ -126,18 +123,20 @@ static void geo_node_subdivision_surface_exec(GeoNodeExecParams params)
params.set_output("Geometry", std::move(geometry_set));
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_legacy_subdivision_surface_cc
-void register_node_type_geo_subdivision_surface()
+void register_node_type_geo_legacy_subdivision_surface()
{
+ namespace file_ns = blender::nodes::node_geo_legacy_subdivision_surface_cc;
+
static bNodeType ntype;
geo_node_type_base(
- &ntype, GEO_NODE_LEGACY_SUBDIVISION_SURFACE, "Subdivision Surface", NODE_CLASS_GEOMETRY, 0);
- ntype.declare = blender::nodes::geo_node_subdivision_surface_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_subdivision_surface_exec;
- ntype.draw_buttons = blender::nodes::geo_node_subdivision_surface_layout;
- node_type_init(&ntype, blender::nodes::geo_node_subdivision_surface_init);
+ &ntype, GEO_NODE_LEGACY_SUBDIVISION_SURFACE, "Subdivision Surface", NODE_CLASS_GEOMETRY);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.draw_buttons = file_ns::node_layout;
+ node_type_init(&ntype, file_ns::node_init);
node_type_size_preset(&ntype, NODE_SIZE_MIDDLE);
node_type_storage(&ntype,
"NodeGeometrySubdivisionSurface",
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_volume_to_mesh.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_volume_to_mesh.cc
new file mode 100644
index 00000000000..42fbc49ed4b
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_volume_to_mesh.cc
@@ -0,0 +1,176 @@
+/*
+ * 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 "DEG_depsgraph_query.h"
+#ifdef WITH_OPENVDB
+# include <openvdb/tools/GridTransformer.h>
+# include <openvdb/tools/VolumeToMesh.h>
+#endif
+
+#include "node_geometry_util.hh"
+
+#include "BKE_lib_id.h"
+#include "BKE_material.h"
+#include "BKE_mesh.h"
+#include "BKE_mesh_runtime.h"
+#include "BKE_volume.h"
+#include "BKE_volume_to_mesh.hh"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+namespace blender::nodes::node_geo_legacy_volume_to_mesh_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::String>(N_("Density"));
+ b.add_input<decl::Float>(N_("Voxel Size")).default_value(0.3f).min(0.01f).subtype(PROP_DISTANCE);
+ b.add_input<decl::Float>(N_("Voxel Amount")).default_value(64.0f).min(0.0f);
+ b.add_input<decl::Float>(N_("Threshold")).default_value(0.1f).min(0.0f);
+ b.add_input<decl::Float>(N_("Adaptivity")).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
+ b.add_output<decl::Geometry>(N_("Geometry"));
+}
+
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
+ uiItemR(layout, ptr, "resolution_mode", 0, IFACE_("Resolution"), ICON_NONE);
+}
+
+static void node_init(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeGeometryVolumeToMesh *data = MEM_cnew<NodeGeometryVolumeToMesh>(__func__);
+ data->resolution_mode = VOLUME_TO_MESH_RESOLUTION_MODE_GRID;
+
+ bNodeSocket *grid_socket = nodeFindSocket(node, SOCK_IN, "Density");
+ bNodeSocketValueString *grid_socket_value = (bNodeSocketValueString *)grid_socket->default_value;
+ STRNCPY(grid_socket_value->value, "density");
+
+ node->storage = data;
+}
+
+static void node_update(bNodeTree *ntree, bNode *node)
+{
+ NodeGeometryVolumeToMesh *data = (NodeGeometryVolumeToMesh *)node->storage;
+
+ bNodeSocket *voxel_size_socket = nodeFindSocket(node, SOCK_IN, "Voxel Size");
+ bNodeSocket *voxel_amount_socket = nodeFindSocket(node, SOCK_IN, "Voxel Amount");
+ nodeSetSocketAvailability(ntree,
+ voxel_amount_socket,
+ data->resolution_mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_AMOUNT);
+ nodeSetSocketAvailability(ntree,
+ voxel_size_socket,
+ data->resolution_mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_SIZE);
+}
+
+#ifdef WITH_OPENVDB
+
+static void create_mesh_from_volume(GeometrySet &geometry_set_in,
+ GeometrySet &geometry_set_out,
+ GeoNodeExecParams &params)
+{
+ if (!geometry_set_in.has<VolumeComponent>()) {
+ return;
+ }
+
+ const NodeGeometryVolumeToMesh &storage =
+ *(const NodeGeometryVolumeToMesh *)params.node().storage;
+
+ bke::VolumeToMeshResolution resolution;
+ resolution.mode = (VolumeToMeshResolutionMode)storage.resolution_mode;
+ if (resolution.mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_AMOUNT) {
+ resolution.settings.voxel_amount = params.get_input<float>("Voxel Amount");
+ if (resolution.settings.voxel_amount <= 0.0f) {
+ return;
+ }
+ }
+ else if (resolution.mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_SIZE) {
+ resolution.settings.voxel_size = params.get_input<float>("Voxel Size");
+ if (resolution.settings.voxel_size <= 0.0f) {
+ return;
+ }
+ }
+
+ const VolumeComponent *component = geometry_set_in.get_component_for_read<VolumeComponent>();
+ const Volume *volume = component->get_for_read();
+ if (volume == nullptr) {
+ return;
+ }
+
+ const Main *bmain = DEG_get_bmain(params.depsgraph());
+ BKE_volume_load(volume, bmain);
+
+ const std::string grid_name = params.get_input<std::string>("Density");
+ const VolumeGrid *volume_grid = BKE_volume_grid_find_for_read(volume, grid_name.c_str());
+ if (volume_grid == nullptr) {
+ return;
+ }
+
+ float threshold = params.get_input<float>("Threshold");
+ float adaptivity = params.get_input<float>("Adaptivity");
+
+ const openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid);
+ Mesh *mesh = bke::volume_to_mesh(*grid, resolution, threshold, adaptivity);
+ if (mesh == nullptr) {
+ return;
+ }
+ BKE_id_material_eval_ensure_default_slot(&mesh->id);
+ MeshComponent &dst_component = geometry_set_out.get_component_for_write<MeshComponent>();
+ dst_component.replace(mesh);
+}
+
+#endif /* WITH_OPENVDB */
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set_in = params.extract_input<GeometrySet>("Geometry");
+ GeometrySet geometry_set_out;
+
+#ifdef WITH_OPENVDB
+ create_mesh_from_volume(geometry_set_in, geometry_set_out, params);
+#else
+ params.error_message_add(NodeWarningType::Error,
+ TIP_("Disabled, Blender was compiled without OpenVDB"));
+#endif
+
+ params.set_output("Geometry", geometry_set_out);
+}
+
+} // namespace blender::nodes::node_geo_legacy_volume_to_mesh_cc
+
+void register_node_type_geo_legacy_volume_to_mesh()
+{
+ namespace file_ns = blender::nodes::node_geo_legacy_volume_to_mesh_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_LEGACY_VOLUME_TO_MESH, "Volume to Mesh", NODE_CLASS_GEOMETRY);
+ ntype.declare = file_ns::node_declare;
+ node_type_storage(
+ &ntype, "NodeGeometryVolumeToMesh", node_free_standard_storage, node_copy_standard_storage);
+ node_type_size(&ntype, 170, 120, 700);
+ node_type_init(&ntype, file_ns::node_init);
+ node_type_update(&ntype, file_ns::node_update);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.draw_buttons = file_ns::node_layout;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_mesh_to_curve.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_mesh_to_curve.cc
deleted file mode 100644
index 11349dc7d42..00000000000
--- a/source/blender/nodes/geometry/nodes/legacy/node_geo_mesh_to_curve.cc
+++ /dev/null
@@ -1,314 +0,0 @@
-/*
- * 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_array.hh"
-#include "BLI_task.hh"
-
-#include "DNA_mesh_types.h"
-#include "DNA_meshdata_types.h"
-
-#include "BKE_attribute_math.hh"
-#include "BKE_spline.hh"
-
-#include "node_geometry_util.hh"
-
-using blender::Array;
-
-namespace blender::nodes {
-
-static void geo_node_mesh_to_curve_declare(NodeDeclarationBuilder &b)
-{
- b.add_input<decl::Geometry>("Mesh");
- b.add_input<decl::String>("Selection");
- b.add_output<decl::Geometry>("Curve");
-}
-
-template<typename T>
-static void copy_attribute_to_points(const VArray<T> &source_data,
- Span<int> map,
- MutableSpan<T> dest_data)
-{
- for (const int point_index : map.index_range()) {
- const int vert_index = map[point_index];
- dest_data[point_index] = source_data[vert_index];
- }
-}
-
-static void copy_attributes_to_points(CurveEval &curve,
- const MeshComponent &mesh_component,
- Span<Vector<int>> point_to_vert_maps)
-{
- MutableSpan<SplinePtr> splines = curve.splines();
- Set<AttributeIDRef> source_attribute_ids = mesh_component.attribute_ids();
-
- /* Copy builtin control point attributes. */
- if (source_attribute_ids.contains("tilt")) {
- const GVArray_Typed<float> tilt_attribute = mesh_component.attribute_get_for_read<float>(
- "tilt", ATTR_DOMAIN_POINT, 0.0f);
- threading::parallel_for(splines.index_range(), 256, [&](IndexRange range) {
- for (const int i : range) {
- copy_attribute_to_points<float>(
- *tilt_attribute, point_to_vert_maps[i], splines[i]->tilts());
- }
- });
- source_attribute_ids.remove_contained("tilt");
- }
- if (source_attribute_ids.contains("radius")) {
- const GVArray_Typed<float> radius_attribute = mesh_component.attribute_get_for_read<float>(
- "radius", ATTR_DOMAIN_POINT, 1.0f);
- threading::parallel_for(splines.index_range(), 256, [&](IndexRange range) {
- for (const int i : range) {
- copy_attribute_to_points<float>(
- *radius_attribute, point_to_vert_maps[i], splines[i]->radii());
- }
- });
- source_attribute_ids.remove_contained("radius");
- }
-
- /* Don't copy other builtin control point attributes. */
- source_attribute_ids.remove("position");
-
- /* Copy dynamic control point attributes. */
- for (const AttributeIDRef &attribute_id : source_attribute_ids) {
- const GVArrayPtr mesh_attribute = mesh_component.attribute_try_get_for_read(attribute_id,
- ATTR_DOMAIN_POINT);
- /* Some attributes might not exist if they were builtin attribute on domains that don't
- * have any elements, i.e. a face attribute on the output of the line primitive node. */
- if (!mesh_attribute) {
- continue;
- }
-
- const CustomDataType data_type = bke::cpp_type_to_custom_data_type(mesh_attribute->type());
-
- threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) {
- for (const int i : range) {
- /* Create attribute on the spline points. */
- splines[i]->attributes.create(attribute_id, data_type);
- std::optional<GMutableSpan> spline_attribute = splines[i]->attributes.get_for_write(
- attribute_id);
- BLI_assert(spline_attribute);
-
- /* Copy attribute based on the map for this spline. */
- attribute_math::convert_to_static_type(mesh_attribute->type(), [&](auto dummy) {
- using T = decltype(dummy);
- copy_attribute_to_points<T>(
- mesh_attribute->typed<T>(), point_to_vert_maps[i], spline_attribute->typed<T>());
- });
- }
- });
- }
-
- curve.assert_valid_point_attributes();
-}
-
-struct CurveFromEdgesOutput {
- std::unique_ptr<CurveEval> curve;
- Vector<Vector<int>> point_to_vert_maps;
-};
-
-static CurveFromEdgesOutput mesh_to_curve(Span<MVert> verts, Span<std::pair<int, int>> edges)
-{
- std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>();
- Vector<Vector<int>> point_to_vert_maps;
-
- /* Compute the number of edges connecting to each vertex. */
- Array<int> neighbor_count(verts.size(), 0);
- for (const std::pair<int, int> &edge : edges) {
- neighbor_count[edge.first]++;
- neighbor_count[edge.second]++;
- }
-
- /* Compute an offset into the array of neighbor edges based on the counts. */
- Array<int> neighbor_offsets(verts.size());
- int start = 0;
- for (const int i : verts.index_range()) {
- neighbor_offsets[i] = start;
- start += neighbor_count[i];
- }
-
- /* Use as an index into the "neighbor group" for each vertex. */
- Array<int> used_slots(verts.size(), 0);
- /* Calculate the indices of each vertex's neighboring edges. */
- Array<int> neighbors(edges.size() * 2);
- for (const int i : edges.index_range()) {
- const int v1 = edges[i].first;
- const int v2 = edges[i].second;
- neighbors[neighbor_offsets[v1] + used_slots[v1]] = v2;
- neighbors[neighbor_offsets[v2] + used_slots[v2]] = v1;
- used_slots[v1]++;
- used_slots[v2]++;
- }
-
- /* Now use the neighbor group offsets calculated above as a count used edges at each vertex. */
- Array<int> unused_edges = std::move(used_slots);
-
- for (const int start_vert : verts.index_range()) {
- /* The vertex will be part of a cyclic spline. */
- if (neighbor_count[start_vert] == 2) {
- continue;
- }
-
- /* The vertex has no connected edges, or they were already used. */
- if (unused_edges[start_vert] == 0) {
- continue;
- }
-
- for (const int i : IndexRange(neighbor_count[start_vert])) {
- int current_vert = start_vert;
- int next_vert = neighbors[neighbor_offsets[current_vert] + i];
-
- if (unused_edges[next_vert] == 0) {
- continue;
- }
-
- std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>();
- Vector<int> point_to_vert_map;
-
- spline->add_point(verts[current_vert].co, 1.0f, 0.0f);
- point_to_vert_map.append(current_vert);
-
- /* Follow connected edges until we read a vertex with more than two connected edges. */
- while (true) {
- int last_vert = current_vert;
- current_vert = next_vert;
-
- spline->add_point(verts[current_vert].co, 1.0f, 0.0f);
- point_to_vert_map.append(current_vert);
- unused_edges[current_vert]--;
- unused_edges[last_vert]--;
-
- if (neighbor_count[current_vert] != 2) {
- break;
- }
-
- const int offset = neighbor_offsets[current_vert];
- const int next_a = neighbors[offset];
- const int next_b = neighbors[offset + 1];
- next_vert = (last_vert == next_a) ? next_b : next_a;
- }
-
- spline->attributes.reallocate(spline->size());
- curve->add_spline(std::move(spline));
- point_to_vert_maps.append(std::move(point_to_vert_map));
- }
- }
-
- /* All remaining edges are part of cyclic splines (we skipped vertices with two edges before). */
- for (const int start_vert : verts.index_range()) {
- if (unused_edges[start_vert] != 2) {
- continue;
- }
-
- int current_vert = start_vert;
- int next_vert = neighbors[neighbor_offsets[current_vert]];
-
- std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>();
- Vector<int> point_to_vert_map;
- spline->set_cyclic(true);
-
- spline->add_point(verts[current_vert].co, 1.0f, 0.0f);
- point_to_vert_map.append(current_vert);
-
- /* Follow connected edges until we loop back to the start vertex. */
- while (next_vert != start_vert) {
- const int last_vert = current_vert;
- current_vert = next_vert;
-
- spline->add_point(verts[current_vert].co, 1.0f, 0.0f);
- point_to_vert_map.append(current_vert);
- unused_edges[current_vert]--;
- unused_edges[last_vert]--;
-
- const int offset = neighbor_offsets[current_vert];
- const int next_a = neighbors[offset];
- const int next_b = neighbors[offset + 1];
- next_vert = (last_vert == next_a) ? next_b : next_a;
- }
-
- spline->attributes.reallocate(spline->size());
- curve->add_spline(std::move(spline));
- point_to_vert_maps.append(std::move(point_to_vert_map));
- }
-
- curve->attributes.reallocate(curve->splines().size());
- return {std::move(curve), std::move(point_to_vert_maps)};
-}
-
-/**
- * Get a separate array of the indices for edges in a selection (a boolean attribute).
- * This helps to make the above algorithm simpler by removing the need to check for selection
- * in many places.
- */
-static Vector<std::pair<int, int>> get_selected_edges(GeoNodeExecParams params,
- const MeshComponent &component)
-{
- const Mesh &mesh = *component.get_for_read();
- const std::string selection_name = params.extract_input<std::string>("Selection");
- if (!selection_name.empty() && !component.attribute_exists(selection_name)) {
- params.error_message_add(NodeWarningType::Error,
- TIP_("No attribute with name \"") + selection_name + "\"");
- }
- GVArray_Typed<bool> selection = component.attribute_get_for_read<bool>(
- selection_name, ATTR_DOMAIN_EDGE, true);
-
- Vector<std::pair<int, int>> selected_edges;
- for (const int i : IndexRange(mesh.totedge)) {
- if (selection[i]) {
- selected_edges.append({mesh.medge[i].v1, mesh.medge[i].v2});
- }
- }
-
- return selected_edges;
-}
-
-static void geo_node_mesh_to_curve_exec(GeoNodeExecParams params)
-{
- GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh");
-
- geometry_set = bke::geometry_set_realize_instances(geometry_set);
-
- if (!geometry_set.has_mesh()) {
- params.set_output("Curve", GeometrySet());
- return;
- }
-
- const MeshComponent &component = *geometry_set.get_component_for_read<MeshComponent>();
- const Mesh &mesh = *component.get_for_read();
- Span<MVert> verts = Span{mesh.mvert, mesh.totvert};
- Vector<std::pair<int, int>> selected_edges = get_selected_edges(params, component);
- if (selected_edges.size() == 0) {
- params.set_output("Curve", GeometrySet());
- return;
- }
-
- CurveFromEdgesOutput output = mesh_to_curve(verts, selected_edges);
- copy_attributes_to_points(*output.curve, component, output.point_to_vert_maps);
-
- params.set_output("Curve", GeometrySet::create_with_curve(output.curve.release()));
-}
-
-} // namespace blender::nodes
-
-void register_node_type_geo_mesh_to_curve()
-{
- static bNodeType ntype;
-
- geo_node_type_base(
- &ntype, GEO_NODE_LEGACY_MESH_TO_CURVE, "Mesh to Curve", NODE_CLASS_GEOMETRY, 0);
- ntype.declare = blender::nodes::geo_node_mesh_to_curve_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_mesh_to_curve_exec;
- nodeRegisterType(&ntype);
-}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc b/source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc
new file mode 100644
index 00000000000..6c2e72cf14f
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc
@@ -0,0 +1,430 @@
+/*
+ * 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 "BKE_attribute_math.hh"
+
+#include "NOD_socket_search_link.hh"
+
+#include "node_geometry_util.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+namespace blender::nodes::node_geo_accumulate_field_cc {
+
+NODE_STORAGE_FUNCS(NodeAccumulateField)
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ std::string value_in_description = "The values to be accumulated";
+ std::string leading_out_description =
+ "The running total of values in the corresponding group, starting at the first value";
+ std::string trailing_out_description =
+ "The running total of values in the corresponding group, starting at zero";
+ std::string total_out_description = "The total of all of the values in the corresponding group";
+
+ b.add_input<decl::Vector>(N_("Value"), "Value Vector")
+ .default_value({1.0f, 1.0f, 1.0f})
+ .supports_field()
+ .description(N_(value_in_description));
+ b.add_input<decl::Float>(N_("Value"), "Value Float")
+ .default_value(1.0f)
+ .supports_field()
+ .description(N_(value_in_description));
+ b.add_input<decl::Int>(N_("Value"), "Value Int")
+ .default_value(1)
+ .supports_field()
+ .description(N_(value_in_description));
+ b.add_input<decl::Int>(N_("Group Index"))
+ .supports_field()
+ .description(
+ N_("An index used to group values together for multiple separate accumulations"));
+
+ b.add_output<decl::Vector>(N_("Leading"), "Leading Vector")
+ .field_source()
+ .description(N_(leading_out_description));
+ b.add_output<decl::Float>(N_("Leading"), "Leading Float")
+ .field_source()
+ .description(N_(leading_out_description));
+ b.add_output<decl::Int>(N_("Leading"), "Leading Int")
+ .field_source()
+ .description(N_(leading_out_description));
+
+ b.add_output<decl::Vector>(N_("Trailing"), "Trailing Vector")
+ .field_source()
+ .description(N_(trailing_out_description));
+ b.add_output<decl::Float>(N_("Trailing"), "Trailing Float")
+ .field_source()
+ .description(N_(trailing_out_description));
+ b.add_output<decl::Int>(N_("Trailing"), "Trailing Int")
+ .field_source()
+ .description(N_(trailing_out_description));
+
+ b.add_output<decl::Vector>(N_("Total"), "Total Vector")
+ .field_source()
+ .description(N_(total_out_description));
+ b.add_output<decl::Float>(N_("Total"), "Total Float")
+ .field_source()
+ .description(N_(total_out_description));
+ b.add_output<decl::Int>(N_("Total"), "Total Int")
+ .field_source()
+ .description(N_(total_out_description));
+}
+
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE);
+ uiItemR(layout, ptr, "domain", 0, "", ICON_NONE);
+}
+
+static void node_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeAccumulateField *data = MEM_cnew<NodeAccumulateField>(__func__);
+ data->data_type = CD_PROP_FLOAT;
+ data->domain = ATTR_DOMAIN_POINT;
+ node->storage = data;
+}
+
+static void node_update(bNodeTree *ntree, bNode *node)
+{
+ const NodeAccumulateField &storage = node_storage(*node);
+ const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type);
+
+ bNodeSocket *sock_in_vector = (bNodeSocket *)node->inputs.first;
+ bNodeSocket *sock_in_float = sock_in_vector->next;
+ bNodeSocket *sock_in_int = sock_in_float->next;
+
+ bNodeSocket *sock_out_vector = (bNodeSocket *)node->outputs.first;
+ bNodeSocket *sock_out_float = sock_out_vector->next;
+ bNodeSocket *sock_out_int = sock_out_float->next;
+
+ bNodeSocket *sock_out_first_vector = sock_out_int->next;
+ bNodeSocket *sock_out_first_float = sock_out_first_vector->next;
+ bNodeSocket *sock_out_first_int = sock_out_first_float->next;
+ bNodeSocket *sock_out_total_vector = sock_out_first_int->next;
+ bNodeSocket *sock_out_total_float = sock_out_total_vector->next;
+ bNodeSocket *sock_out_total_int = sock_out_total_float->next;
+
+ nodeSetSocketAvailability(ntree, sock_in_vector, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(ntree, sock_in_float, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(ntree, sock_in_int, data_type == CD_PROP_INT32);
+
+ nodeSetSocketAvailability(ntree, sock_out_vector, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(ntree, sock_out_float, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(ntree, sock_out_int, data_type == CD_PROP_INT32);
+
+ nodeSetSocketAvailability(ntree, sock_out_first_vector, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(ntree, sock_out_first_float, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(ntree, sock_out_first_int, data_type == CD_PROP_INT32);
+
+ nodeSetSocketAvailability(ntree, sock_out_total_vector, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(ntree, sock_out_total_float, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(ntree, sock_out_total_int, data_type == CD_PROP_INT32);
+}
+
+enum class AccumulationMode { Leading = 0, Trailing = 1 };
+
+static std::optional<CustomDataType> node_type_from_other_socket(const bNodeSocket &socket)
+{
+ switch (socket.type) {
+ case SOCK_FLOAT:
+ return CD_PROP_FLOAT;
+ case SOCK_BOOLEAN:
+ case SOCK_INT:
+ return CD_PROP_INT32;
+ case SOCK_VECTOR:
+ case SOCK_RGBA:
+ return CD_PROP_FLOAT3;
+ default:
+ return {};
+ }
+}
+
+static void node_gather_link_searches(GatherLinkSearchOpParams &params)
+{
+ const std::optional<CustomDataType> type = node_type_from_other_socket(params.other_socket());
+ if (!type) {
+ return;
+ }
+ if (params.in_out() == SOCK_OUT) {
+ params.add_item(
+ IFACE_("Leading"),
+ [type](LinkSearchOpParams &params) {
+ bNode &node = params.add_node("GeometryNodeAccumulateField");
+ node_storage(node).data_type = *type;
+ params.update_and_connect_available_socket(node, "Leading");
+ },
+ 0);
+ params.add_item(
+ IFACE_("Trailing"),
+ [type](LinkSearchOpParams &params) {
+ bNode &node = params.add_node("GeometryNodeAccumulateField");
+ node_storage(node).data_type = *type;
+ params.update_and_connect_available_socket(node, "Trailing");
+ },
+ -1);
+ params.add_item(
+ IFACE_("Total"),
+ [type](LinkSearchOpParams &params) {
+ bNode &node = params.add_node("GeometryNodeAccumulateField");
+ node_storage(node).data_type = *type;
+ params.update_and_connect_available_socket(node, "Total");
+ },
+ -2);
+ }
+ else {
+ params.add_item(
+ IFACE_("Value"),
+ [type](LinkSearchOpParams &params) {
+ bNode &node = params.add_node("GeometryNodeAccumulateField");
+ node_storage(node).data_type = *type;
+ params.update_and_connect_available_socket(node, "Value");
+ },
+ 0);
+
+ params.add_item(
+ IFACE_("Group Index"),
+ [type](LinkSearchOpParams &params) {
+ bNode &node = params.add_node("GeometryNodeAccumulateField");
+ node_storage(node).data_type = *type;
+ params.update_and_connect_available_socket(node, "Group Index");
+ },
+ -1);
+ }
+}
+
+template<typename T> class AccumulateFieldInput final : public GeometryFieldInput {
+ private:
+ Field<T> input_;
+ Field<int> group_index_;
+ AttributeDomain source_domain_;
+ AccumulationMode accumulation_mode_;
+
+ public:
+ AccumulateFieldInput(const AttributeDomain source_domain,
+ Field<T> input,
+ Field<int> group_index,
+ AccumulationMode accumulation_mode)
+ : GeometryFieldInput(CPPType::get<T>(), "Accumulation"),
+ input_(input),
+ group_index_(group_index),
+ source_domain_(source_domain),
+ accumulation_mode_(accumulation_mode)
+ {
+ }
+
+ GVArray get_varray_for_context(const GeometryComponent &component,
+ const AttributeDomain domain,
+ IndexMask UNUSED(mask)) const final
+ {
+ const GeometryComponentFieldContext field_context{component, source_domain_};
+ const int domain_size = component.attribute_domain_size(field_context.domain());
+
+ fn::FieldEvaluator evaluator{field_context, domain_size};
+ evaluator.add(input_);
+ evaluator.add(group_index_);
+ evaluator.evaluate();
+ const VArray<T> &values = evaluator.get_evaluated<T>(0);
+ const VArray<int> &group_indices = evaluator.get_evaluated<int>(1);
+
+ Array<T> accumulations_out(domain_size);
+
+ if (group_indices.is_single()) {
+ T accumulation = T();
+ if (accumulation_mode_ == AccumulationMode::Leading) {
+ for (const int i : values.index_range()) {
+ accumulation = values[i] + accumulation;
+ accumulations_out[i] = accumulation;
+ }
+ }
+ else {
+ for (const int i : values.index_range()) {
+ accumulations_out[i] = accumulation;
+ accumulation = values[i] + accumulation;
+ }
+ }
+ }
+ else {
+ Map<int, T> accumulations;
+ if (accumulation_mode_ == AccumulationMode::Leading) {
+ for (const int i : values.index_range()) {
+ T &accumulation_value = accumulations.lookup_or_add_default(group_indices[i]);
+ accumulation_value += values[i];
+ accumulations_out[i] = accumulation_value;
+ }
+ }
+ else {
+ for (const int i : values.index_range()) {
+ T &accumulation_value = accumulations.lookup_or_add_default(group_indices[i]);
+ accumulations_out[i] = accumulation_value;
+ accumulation_value += values[i];
+ }
+ }
+ }
+
+ return component.attribute_try_adapt_domain<T>(
+ VArray<T>::ForContainer(std::move(accumulations_out)), source_domain_, domain);
+ }
+
+ uint64_t hash() const override
+ {
+ return get_default_hash_4(input_, group_index_, source_domain_, accumulation_mode_);
+ }
+
+ bool is_equal_to(const fn::FieldNode &other) const override
+ {
+ if (const AccumulateFieldInput *other_accumulate = dynamic_cast<const AccumulateFieldInput *>(
+ &other)) {
+ return input_ == other_accumulate->input_ &&
+ group_index_ == other_accumulate->group_index_ &&
+ source_domain_ == other_accumulate->source_domain_ &&
+ accumulation_mode_ == other_accumulate->accumulation_mode_;
+ }
+ return false;
+ }
+};
+
+template<typename T> class TotalFieldInput final : public GeometryFieldInput {
+ private:
+ Field<T> input_;
+ Field<int> group_index_;
+ AttributeDomain source_domain_;
+
+ public:
+ TotalFieldInput(const AttributeDomain source_domain, Field<T> input, Field<int> group_index)
+ : GeometryFieldInput(CPPType::get<T>(), "Total Value"),
+ input_(input),
+ group_index_(group_index),
+ source_domain_(source_domain)
+ {
+ }
+
+ GVArray get_varray_for_context(const GeometryComponent &component,
+ const AttributeDomain domain,
+ IndexMask UNUSED(mask)) const final
+ {
+ const GeometryComponentFieldContext field_context{component, source_domain_};
+ const int domain_size = component.attribute_domain_size(field_context.domain());
+
+ fn::FieldEvaluator evaluator{field_context, domain_size};
+ evaluator.add(input_);
+ evaluator.add(group_index_);
+ evaluator.evaluate();
+ const VArray<T> &values = evaluator.get_evaluated<T>(0);
+ const VArray<int> &group_indices = evaluator.get_evaluated<int>(1);
+
+ if (group_indices.is_single()) {
+ T accumulation = T();
+ for (const int i : values.index_range()) {
+ accumulation = values[i] + accumulation;
+ }
+ return VArray<T>::ForSingle(accumulation, domain_size);
+ }
+
+ Array<T> accumulations_out(domain_size);
+ Map<int, T> accumulations;
+ for (const int i : values.index_range()) {
+ T &value = accumulations.lookup_or_add_default(group_indices[i]);
+ value = value + values[i];
+ }
+ for (const int i : values.index_range()) {
+ accumulations_out[i] = accumulations.lookup(group_indices[i]);
+ }
+
+ return component.attribute_try_adapt_domain<T>(
+ VArray<T>::ForContainer(std::move(accumulations_out)), source_domain_, domain);
+ }
+
+ uint64_t hash() const override
+ {
+ return get_default_hash_3(input_, group_index_, source_domain_);
+ }
+
+ bool is_equal_to(const fn::FieldNode &other) const override
+ {
+ if (const TotalFieldInput *other_field = dynamic_cast<const TotalFieldInput *>(&other)) {
+ return input_ == other_field->input_ && group_index_ == other_field->group_index_ &&
+ source_domain_ == other_field->source_domain_;
+ }
+ return false;
+ }
+};
+
+template<typename T> std::string identifier_suffix()
+{
+ if constexpr (std::is_same_v<T, int>) {
+ return "Int";
+ }
+ if constexpr (std::is_same_v<T, float>) {
+ return "Float";
+ }
+ if constexpr (std::is_same_v<T, float3>) {
+ return "Vector";
+ }
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ const NodeAccumulateField &storage = node_storage(params.node());
+ const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type);
+ const AttributeDomain source_domain = static_cast<AttributeDomain>(storage.domain);
+
+ Field<int> group_index_field = params.extract_input<Field<int>>("Group Index");
+ attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ using T = decltype(dummy);
+ if constexpr (std::is_same_v<T, int> || std::is_same_v<T, float> ||
+ std::is_same_v<T, float3>) {
+ const std::string suffix = " " + identifier_suffix<T>();
+ Field<T> input_field = params.extract_input<Field<T>>("Value" + suffix);
+ if (params.output_is_required("Leading" + suffix)) {
+ params.set_output(
+ "Leading" + suffix,
+ Field<T>{std::make_shared<AccumulateFieldInput<T>>(
+ source_domain, input_field, group_index_field, AccumulationMode::Leading)});
+ }
+ if (params.output_is_required("Trailing" + suffix)) {
+ params.set_output(
+ "Trailing" + suffix,
+ Field<T>{std::make_shared<AccumulateFieldInput<T>>(
+ source_domain, input_field, group_index_field, AccumulationMode::Trailing)});
+ }
+ if (params.output_is_required("Total" + suffix)) {
+ params.set_output("Total" + suffix,
+ Field<T>{std::make_shared<TotalFieldInput<T>>(
+ source_domain, input_field, group_index_field)});
+ }
+ }
+ });
+}
+} // namespace blender::nodes::node_geo_accumulate_field_cc
+
+void register_node_type_geo_accumulate_field()
+{
+ namespace file_ns = blender::nodes::node_geo_accumulate_field_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_ACCUMULATE_FIELD, "Accumulate Field", NODE_CLASS_CONVERTER);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ node_type_init(&ntype, file_ns::node_init);
+ node_type_update(&ntype, file_ns::node_update);
+ ntype.draw_buttons = file_ns::node_layout;
+ ntype.declare = file_ns::node_declare;
+ ntype.gather_link_search_ops = file_ns::node_gather_link_searches;
+ node_type_storage(
+ &ntype, "NodeAccumulateField", node_free_standard_storage, node_copy_standard_storage);
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc
index 43fb00a482c..840dfd2fbd3 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc
@@ -19,51 +19,51 @@
#include "BKE_attribute_math.hh"
+#include "NOD_socket_search_link.hh"
+
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_attribute_capture_cc {
+
+NODE_STORAGE_FUNCS(NodeGeometryAttributeCapture)
-static void geo_node_attribute_capture_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Vector>("Value").supports_field();
- b.add_input<decl::Float>("Value", "Value_001").supports_field();
- b.add_input<decl::Color>("Value", "Value_002").supports_field();
- b.add_input<decl::Bool>("Value", "Value_003").supports_field();
- b.add_input<decl::Int>("Value", "Value_004").supports_field();
-
- b.add_output<decl::Geometry>("Geometry");
- b.add_output<decl::Vector>("Attribute").field_source();
- b.add_output<decl::Float>("Attribute", "Attribute_001").field_source();
- b.add_output<decl::Color>("Attribute", "Attribute_002").field_source();
- b.add_output<decl::Bool>("Attribute", "Attribute_003").field_source();
- b.add_output<decl::Int>("Attribute", "Attribute_004").field_source();
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::Vector>(N_("Value")).supports_field();
+ b.add_input<decl::Float>(N_("Value"), "Value_001").supports_field();
+ b.add_input<decl::Color>(N_("Value"), "Value_002").supports_field();
+ b.add_input<decl::Bool>(N_("Value"), "Value_003").supports_field();
+ b.add_input<decl::Int>(N_("Value"), "Value_004").supports_field();
+
+ b.add_output<decl::Geometry>(N_("Geometry"));
+ b.add_output<decl::Vector>(N_("Attribute")).field_source();
+ b.add_output<decl::Float>(N_("Attribute"), "Attribute_001").field_source();
+ b.add_output<decl::Color>(N_("Attribute"), "Attribute_002").field_source();
+ b.add_output<decl::Bool>(N_("Attribute"), "Attribute_003").field_source();
+ b.add_output<decl::Int>(N_("Attribute"), "Attribute_004").field_source();
}
-static void geo_node_attribute_capture_layout(uiLayout *layout,
- bContext *UNUSED(C),
- PointerRNA *ptr)
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
- uiItemR(layout, ptr, "domain", 0, "", ICON_NONE);
uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE);
+ uiItemR(layout, ptr, "domain", 0, "", ICON_NONE);
}
-static void geo_node_attribute_capture_init(bNodeTree *UNUSED(tree), bNode *node)
+static void node_init(bNodeTree *UNUSED(tree), bNode *node)
{
- NodeGeometryAttributeCapture *data = (NodeGeometryAttributeCapture *)MEM_callocN(
- sizeof(NodeGeometryAttributeCapture), __func__);
+ NodeGeometryAttributeCapture *data = MEM_cnew<NodeGeometryAttributeCapture>(__func__);
data->data_type = CD_PROP_FLOAT;
data->domain = ATTR_DOMAIN_POINT;
node->storage = data;
}
-static void geo_node_attribute_capture_update(bNodeTree *UNUSED(ntree), bNode *node)
+static void node_update(bNodeTree *ntree, bNode *node)
{
- const NodeGeometryAttributeCapture &storage = *(const NodeGeometryAttributeCapture *)
- node->storage;
+ const NodeGeometryAttributeCapture &storage = node_storage(*node);
const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type);
bNodeSocket *socket_value_geometry = (bNodeSocket *)node->inputs.first;
@@ -73,11 +73,11 @@ static void geo_node_attribute_capture_update(bNodeTree *UNUSED(ntree), bNode *n
bNodeSocket *socket_value_boolean = socket_value_color4f->next;
bNodeSocket *socket_value_int32 = socket_value_boolean->next;
- nodeSetSocketAvailability(socket_value_vector, data_type == CD_PROP_FLOAT3);
- nodeSetSocketAvailability(socket_value_float, data_type == CD_PROP_FLOAT);
- nodeSetSocketAvailability(socket_value_color4f, data_type == CD_PROP_COLOR);
- nodeSetSocketAvailability(socket_value_boolean, data_type == CD_PROP_BOOL);
- nodeSetSocketAvailability(socket_value_int32, data_type == CD_PROP_INT32);
+ nodeSetSocketAvailability(ntree, socket_value_vector, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(ntree, socket_value_float, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(ntree, socket_value_color4f, data_type == CD_PROP_COLOR);
+ nodeSetSocketAvailability(ntree, socket_value_boolean, data_type == CD_PROP_BOOL);
+ nodeSetSocketAvailability(ntree, socket_value_int32, data_type == CD_PROP_INT32);
bNodeSocket *out_socket_value_geometry = (bNodeSocket *)node->outputs.first;
bNodeSocket *out_socket_value_vector = out_socket_value_geometry->next;
@@ -86,11 +86,38 @@ static void geo_node_attribute_capture_update(bNodeTree *UNUSED(ntree), bNode *n
bNodeSocket *out_socket_value_boolean = out_socket_value_color4f->next;
bNodeSocket *out_socket_value_int32 = out_socket_value_boolean->next;
- nodeSetSocketAvailability(out_socket_value_vector, data_type == CD_PROP_FLOAT3);
- nodeSetSocketAvailability(out_socket_value_float, data_type == CD_PROP_FLOAT);
- nodeSetSocketAvailability(out_socket_value_color4f, data_type == CD_PROP_COLOR);
- nodeSetSocketAvailability(out_socket_value_boolean, data_type == CD_PROP_BOOL);
- nodeSetSocketAvailability(out_socket_value_int32, data_type == CD_PROP_INT32);
+ nodeSetSocketAvailability(ntree, out_socket_value_vector, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(ntree, out_socket_value_float, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(ntree, out_socket_value_color4f, data_type == CD_PROP_COLOR);
+ nodeSetSocketAvailability(ntree, out_socket_value_boolean, data_type == CD_PROP_BOOL);
+ nodeSetSocketAvailability(ntree, out_socket_value_int32, data_type == CD_PROP_INT32);
+}
+
+static void node_gather_link_searches(GatherLinkSearchOpParams &params)
+{
+ const NodeDeclaration &declaration = *params.node_type().fixed_declaration;
+ search_link_ops_for_declarations(params, declaration.inputs().take_front(1));
+ search_link_ops_for_declarations(params, declaration.outputs().take_front(1));
+
+ const bNodeType &node_type = params.node_type();
+ const std::optional<CustomDataType> type = node_data_type_to_custom_data_type(
+ (eNodeSocketDatatype)params.other_socket().type);
+ if (type && *type != CD_PROP_STRING) {
+ if (params.in_out() == SOCK_OUT) {
+ params.add_item(IFACE_("Attribute"), [node_type, type](LinkSearchOpParams &params) {
+ bNode &node = params.add_node(node_type);
+ node_storage(node).data_type = *type;
+ params.update_and_connect_available_socket(node, "Attribute");
+ });
+ }
+ else {
+ params.add_item(IFACE_("Value"), [node_type, type](LinkSearchOpParams &params) {
+ bNode &node = params.add_node(node_type);
+ node_storage(node).data_type = *type;
+ params.update_and_connect_available_socket(node, "Value");
+ });
+ }
+ }
}
static void try_capture_field_on_geometry(GeometryComponent &component,
@@ -113,13 +140,11 @@ static void try_capture_field_on_geometry(GeometryComponent &component,
output_attribute.save();
}
-static void geo_node_attribute_capture_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- const bNode &node = params.node();
- const NodeGeometryAttributeCapture &storage = *(const NodeGeometryAttributeCapture *)
- node.storage;
+ const NodeGeometryAttributeCapture &storage = node_storage(params.node());
const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type);
const AttributeDomain domain = static_cast<AttributeDomain>(storage.domain);
@@ -144,20 +169,33 @@ static void geo_node_attribute_capture_exec(GeoNodeExecParams params)
break;
}
- WeakAnonymousAttributeID anonymous_id{"Attribute Capture"};
+ WeakAnonymousAttributeID anonymous_id{"Attribute"};
const CPPType &type = field.cpp_type();
- static const Array<GeometryComponentType> types = {
- GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE};
- for (const GeometryComponentType type : types) {
- if (geometry_set.has(type)) {
- GeometryComponent &component = geometry_set.get_component_for_write(type);
+ /* Run on the instances component separately to only affect the top level of instances. */
+ if (domain == ATTR_DOMAIN_INSTANCE) {
+ if (geometry_set.has_instances()) {
+ GeometryComponent &component = geometry_set.get_component_for_write(
+ GEO_COMPONENT_TYPE_INSTANCES);
try_capture_field_on_geometry(component, anonymous_id.get(), domain, field);
}
}
+ else {
+ static const Array<GeometryComponentType> types = {
+ GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE};
+
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ for (const GeometryComponentType type : types) {
+ if (geometry_set.has(type)) {
+ GeometryComponent &component = geometry_set.get_component_for_write(type);
+ try_capture_field_on_geometry(component, anonymous_id.get(), domain, field);
+ }
+ }
+ });
+ }
- GField output_field{
- std::make_shared<bke::AnonymousAttributeFieldInput>(std::move(anonymous_id), type)};
+ GField output_field{std::make_shared<bke::AnonymousAttributeFieldInput>(
+ std::move(anonymous_id), type, params.attribute_producer_name())};
switch (data_type) {
case CD_PROP_FLOAT: {
@@ -187,22 +225,25 @@ static void geo_node_attribute_capture_exec(GeoNodeExecParams params)
params.set_output("Geometry", geometry_set);
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_attribute_capture_cc
void register_node_type_geo_attribute_capture()
{
+ namespace file_ns = blender::nodes::node_geo_attribute_capture_cc;
+
static bNodeType ntype;
geo_node_type_base(
- &ntype, GEO_NODE_ATTRIBUTE_CAPTURE, "Attribute Capture", NODE_CLASS_ATTRIBUTE, 0);
+ &ntype, GEO_NODE_CAPTURE_ATTRIBUTE, "Capture Attribute", NODE_CLASS_ATTRIBUTE);
node_type_storage(&ntype,
"NodeGeometryAttributeCapture",
node_free_standard_storage,
node_copy_standard_storage);
- node_type_init(&ntype, blender::nodes::geo_node_attribute_capture_init);
- node_type_update(&ntype, blender::nodes::geo_node_attribute_capture_update);
- ntype.declare = blender::nodes::geo_node_attribute_capture_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_attribute_capture_exec;
- ntype.draw_buttons = blender::nodes::geo_node_attribute_capture_layout;
+ node_type_init(&ntype, file_ns::node_init);
+ node_type_update(&ntype, file_ns::node_update);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.draw_buttons = file_ns::node_layout;
+ ntype.gather_link_search_ops = file_ns::node_gather_link_searches;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc
new file mode 100644
index 00000000000..609ef39eb3f
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc
@@ -0,0 +1,154 @@
+/*
+ * 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 "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_attribute_domain_size_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>("Geometry");
+ b.add_output<decl::Int>("Point Count").make_available([](bNode &node) {
+ node.custom1 = GEO_COMPONENT_TYPE_MESH;
+ });
+ b.add_output<decl::Int>("Edge Count").make_available([](bNode &node) {
+ node.custom1 = GEO_COMPONENT_TYPE_MESH;
+ });
+ b.add_output<decl::Int>("Face Count").make_available([](bNode &node) {
+ node.custom1 = GEO_COMPONENT_TYPE_MESH;
+ });
+ b.add_output<decl::Int>("Face Corner Count").make_available([](bNode &node) {
+ node.custom1 = GEO_COMPONENT_TYPE_MESH;
+ });
+ b.add_output<decl::Int>("Spline Count").make_available([](bNode &node) {
+ node.custom1 = GEO_COMPONENT_TYPE_CURVE;
+ });
+ b.add_output<decl::Int>("Instance Count").make_available([](bNode &node) {
+ node.custom1 = GEO_COMPONENT_TYPE_INSTANCES;
+ });
+}
+
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "component", 0, "", ICON_NONE);
+}
+
+static void node_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ node->custom1 = GEO_COMPONENT_TYPE_MESH;
+}
+
+static void node_update(bNodeTree *ntree, bNode *node)
+{
+ bNodeSocket *point_socket = (bNodeSocket *)node->outputs.first;
+ bNodeSocket *edge_socket = point_socket->next;
+ bNodeSocket *face_socket = edge_socket->next;
+ bNodeSocket *face_corner_socket = face_socket->next;
+ bNodeSocket *spline_socket = face_corner_socket->next;
+ bNodeSocket *instances_socket = spline_socket->next;
+
+ nodeSetSocketAvailability(ntree,
+ point_socket,
+ ELEM(node->custom1,
+ GEO_COMPONENT_TYPE_MESH,
+ GEO_COMPONENT_TYPE_CURVE,
+ GEO_COMPONENT_TYPE_POINT_CLOUD));
+ nodeSetSocketAvailability(ntree, edge_socket, node->custom1 == GEO_COMPONENT_TYPE_MESH);
+ nodeSetSocketAvailability(ntree, face_socket, node->custom1 == GEO_COMPONENT_TYPE_MESH);
+ nodeSetSocketAvailability(ntree, face_corner_socket, node->custom1 == GEO_COMPONENT_TYPE_MESH);
+ nodeSetSocketAvailability(ntree, spline_socket, node->custom1 == GEO_COMPONENT_TYPE_CURVE);
+ nodeSetSocketAvailability(
+ ntree, instances_socket, node->custom1 == GEO_COMPONENT_TYPE_INSTANCES);
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ GeometryComponentType component = (GeometryComponentType)params.node().custom1;
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+ switch (component) {
+ case GEO_COMPONENT_TYPE_MESH: {
+ if (geometry_set.has_mesh()) {
+ const MeshComponent *component = geometry_set.get_component_for_read<MeshComponent>();
+ params.set_output("Point Count", component->attribute_domain_size(ATTR_DOMAIN_POINT));
+ params.set_output("Edge Count", component->attribute_domain_size(ATTR_DOMAIN_EDGE));
+ params.set_output("Face Count", component->attribute_domain_size(ATTR_DOMAIN_FACE));
+ params.set_output("Face Corner Count",
+ component->attribute_domain_size(ATTR_DOMAIN_CORNER));
+ }
+ else {
+ params.set_default_remaining_outputs();
+ }
+ break;
+ }
+ case GEO_COMPONENT_TYPE_CURVE: {
+ if (geometry_set.has_curve()) {
+ const CurveComponent *component = geometry_set.get_component_for_read<CurveComponent>();
+ params.set_output("Point Count", component->attribute_domain_size(ATTR_DOMAIN_POINT));
+ params.set_output("Spline Count", component->attribute_domain_size(ATTR_DOMAIN_CURVE));
+ }
+ else {
+ params.set_default_remaining_outputs();
+ }
+ break;
+ }
+ case GEO_COMPONENT_TYPE_POINT_CLOUD: {
+ if (geometry_set.has_pointcloud()) {
+ const PointCloudComponent *component =
+ geometry_set.get_component_for_read<PointCloudComponent>();
+ params.set_output("Point Count", component->attribute_domain_size(ATTR_DOMAIN_POINT));
+ }
+ else {
+ params.set_default_remaining_outputs();
+ }
+ break;
+ }
+ case GEO_COMPONENT_TYPE_INSTANCES: {
+ if (geometry_set.has_instances()) {
+ const InstancesComponent *component =
+ geometry_set.get_component_for_read<InstancesComponent>();
+ params.set_output("Instance Count",
+ component->attribute_domain_size(ATTR_DOMAIN_INSTANCE));
+ }
+ else {
+ params.set_default_remaining_outputs();
+ }
+ break;
+ }
+ default:
+ BLI_assert_unreachable();
+ }
+}
+
+} // namespace blender::nodes::node_geo_attribute_domain_size_cc
+
+void register_node_type_geo_attribute_domain_size()
+{
+ namespace file_ns = blender::nodes::node_geo_attribute_domain_size_cc;
+
+ static bNodeType ntype;
+ geo_node_type_base(&ntype, GEO_NODE_ATTRIBUTE_DOMAIN_SIZE, "Domain Size", NODE_CLASS_ATTRIBUTE);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.declare = file_ns::node_declare;
+ ntype.draw_buttons = file_ns::node_layout;
+ node_type_init(&ntype, file_ns::node_init);
+ ntype.updatefunc = file_ns::node_update;
+
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc
index f93ef6f1db3..8ed50b2cc75 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc
@@ -16,13 +16,13 @@
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_attribute_remove_cc {
-static void geo_node_attribute_remove_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::String>("Attribute").multi_input();
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::String>(N_("Attribute")).multi_input();
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
static void remove_attribute(GeometryComponent &component,
@@ -42,7 +42,7 @@ static void remove_attribute(GeometryComponent &component,
}
}
-static void geo_node_attribute_remove_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
Vector<std::string> attribute_names = params.extract_multi_input<std::string>("Attribute");
@@ -59,18 +59,23 @@ static void geo_node_attribute_remove_exec(GeoNodeExecParams params)
remove_attribute(
geometry_set.get_component_for_write<CurveComponent>(), params, attribute_names);
}
+ if (geometry_set.has<InstancesComponent>()) {
+ remove_attribute(
+ geometry_set.get_component_for_write<InstancesComponent>(), params, attribute_names);
+ }
params.set_output("Geometry", geometry_set);
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_attribute_remove_cc
void register_node_type_geo_attribute_remove()
{
+ namespace file_ns = blender::nodes::node_geo_attribute_remove_cc;
+
static bNodeType ntype;
- geo_node_type_base(
- &ntype, GEO_NODE_ATTRIBUTE_REMOVE, "Attribute Remove", NODE_CLASS_ATTRIBUTE, 0);
- ntype.geometry_node_execute = blender::nodes::geo_node_attribute_remove_exec;
- ntype.declare = blender::nodes::geo_node_attribute_remove_declare;
+ geo_node_type_base(&ntype, GEO_NODE_ATTRIBUTE_REMOVE, "Attribute Remove", NODE_CLASS_ATTRIBUTE);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.declare = file_ns::node_declare;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc
index 1b7d2fe28a1..7df032b150b 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc
@@ -22,53 +22,55 @@
#include "BLI_math_base_safe.h"
+#include "NOD_socket_search_link.hh"
+
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_attribute_statistic_cc {
-static void geo_node_attribute_statistic_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Float>("Attribute").hide_value().supports_field();
- b.add_input<decl::Vector>("Attribute", "Attribute_001").hide_value().supports_field();
-
- b.add_output<decl::Float>("Mean");
- b.add_output<decl::Float>("Median");
- b.add_output<decl::Float>("Sum");
- b.add_output<decl::Float>("Min");
- b.add_output<decl::Float>("Max");
- b.add_output<decl::Float>("Range");
- b.add_output<decl::Float>("Standard Deviation");
- b.add_output<decl::Float>("Variance");
-
- b.add_output<decl::Vector>("Mean", "Mean_001");
- b.add_output<decl::Vector>("Median", "Median_001");
- b.add_output<decl::Vector>("Sum", "Sum_001");
- b.add_output<decl::Vector>("Min", "Min_001");
- b.add_output<decl::Vector>("Max", "Max_001");
- b.add_output<decl::Vector>("Range", "Range_001");
- b.add_output<decl::Vector>("Standard Deviation", "Standard Deviation_001");
- b.add_output<decl::Vector>("Variance", "Variance_001");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).supports_field().hide_value();
+ b.add_input<decl::Float>(N_("Attribute")).hide_value().supports_field();
+ b.add_input<decl::Vector>(N_("Attribute"), "Attribute_001").hide_value().supports_field();
+
+ b.add_output<decl::Float>(N_("Mean"));
+ b.add_output<decl::Float>(N_("Median"));
+ b.add_output<decl::Float>(N_("Sum"));
+ b.add_output<decl::Float>(N_("Min"));
+ b.add_output<decl::Float>(N_("Max"));
+ b.add_output<decl::Float>(N_("Range"));
+ b.add_output<decl::Float>(N_("Standard Deviation"));
+ b.add_output<decl::Float>(N_("Variance"));
+
+ b.add_output<decl::Vector>(N_("Mean"), "Mean_001");
+ b.add_output<decl::Vector>(N_("Median"), "Median_001");
+ b.add_output<decl::Vector>(N_("Sum"), "Sum_001");
+ b.add_output<decl::Vector>(N_("Min"), "Min_001");
+ b.add_output<decl::Vector>(N_("Max"), "Max_001");
+ b.add_output<decl::Vector>(N_("Range"), "Range_001");
+ b.add_output<decl::Vector>(N_("Standard Deviation"), "Standard Deviation_001");
+ b.add_output<decl::Vector>(N_("Variance"), "Variance_001");
}
-static void geo_node_attribute_statistic_layout(uiLayout *layout,
- bContext *UNUSED(C),
- PointerRNA *ptr)
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE);
uiItemR(layout, ptr, "domain", 0, "", ICON_NONE);
}
-static void geo_node_attribute_statistic_init(bNodeTree *UNUSED(tree), bNode *node)
+static void node_init(bNodeTree *UNUSED(tree), bNode *node)
{
node->custom1 = CD_PROP_FLOAT;
node->custom2 = ATTR_DOMAIN_POINT;
}
-static void geo_node_attribute_statistic_update(bNodeTree *UNUSED(ntree), bNode *node)
+static void node_update(bNodeTree *ntree, bNode *node)
{
bNodeSocket *socket_geo = (bNodeSocket *)node->inputs.first;
- bNodeSocket *socket_float_attr = socket_geo->next;
+ bNodeSocket *socket_selection = socket_geo->next;
+ bNodeSocket *socket_float_attr = socket_selection->next;
bNodeSocket *socket_float3_attr = socket_float_attr->next;
bNodeSocket *socket_float_mean = (bNodeSocket *)node->outputs.first;
@@ -91,25 +93,70 @@ static void geo_node_attribute_statistic_update(bNodeTree *UNUSED(ntree), bNode
const CustomDataType data_type = static_cast<CustomDataType>(node->custom1);
- nodeSetSocketAvailability(socket_float_attr, data_type == CD_PROP_FLOAT);
- nodeSetSocketAvailability(socket_float_mean, data_type == CD_PROP_FLOAT);
- nodeSetSocketAvailability(socket_float_median, data_type == CD_PROP_FLOAT);
- nodeSetSocketAvailability(socket_float_sum, data_type == CD_PROP_FLOAT);
- nodeSetSocketAvailability(socket_float_min, data_type == CD_PROP_FLOAT);
- nodeSetSocketAvailability(socket_float_max, data_type == CD_PROP_FLOAT);
- nodeSetSocketAvailability(socket_float_range, data_type == CD_PROP_FLOAT);
- nodeSetSocketAvailability(socket_float_std, data_type == CD_PROP_FLOAT);
- nodeSetSocketAvailability(socket_float_variance, data_type == CD_PROP_FLOAT);
-
- nodeSetSocketAvailability(socket_float3_attr, data_type == CD_PROP_FLOAT3);
- nodeSetSocketAvailability(socket_vector_mean, data_type == CD_PROP_FLOAT3);
- nodeSetSocketAvailability(socket_vector_median, data_type == CD_PROP_FLOAT3);
- nodeSetSocketAvailability(socket_vector_sum, data_type == CD_PROP_FLOAT3);
- nodeSetSocketAvailability(socket_vector_min, data_type == CD_PROP_FLOAT3);
- nodeSetSocketAvailability(socket_vector_max, data_type == CD_PROP_FLOAT3);
- nodeSetSocketAvailability(socket_vector_range, data_type == CD_PROP_FLOAT3);
- nodeSetSocketAvailability(socket_vector_std, data_type == CD_PROP_FLOAT3);
- nodeSetSocketAvailability(socket_vector_variance, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(ntree, socket_float_attr, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(ntree, socket_float_mean, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(ntree, socket_float_median, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(ntree, socket_float_sum, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(ntree, socket_float_min, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(ntree, socket_float_max, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(ntree, socket_float_range, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(ntree, socket_float_std, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(ntree, socket_float_variance, data_type == CD_PROP_FLOAT);
+
+ nodeSetSocketAvailability(ntree, socket_float3_attr, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(ntree, socket_vector_mean, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(ntree, socket_vector_median, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(ntree, socket_vector_sum, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(ntree, socket_vector_min, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(ntree, socket_vector_max, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(ntree, socket_vector_range, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(ntree, socket_vector_std, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(ntree, socket_vector_variance, data_type == CD_PROP_FLOAT3);
+}
+
+static std::optional<CustomDataType> node_type_from_other_socket(const bNodeSocket &socket)
+{
+ switch (socket.type) {
+ case SOCK_FLOAT:
+ case SOCK_BOOLEAN:
+ case SOCK_INT:
+ return CD_PROP_FLOAT;
+ case SOCK_VECTOR:
+ case SOCK_RGBA:
+ return CD_PROP_FLOAT3;
+ default:
+ return {};
+ }
+}
+
+static void node_gather_link_searches(GatherLinkSearchOpParams &params)
+{
+ const bNodeType &node_type = params.node_type();
+ const NodeDeclaration &declaration = *params.node_type().fixed_declaration;
+ search_link_ops_for_declarations(params, declaration.inputs().take_front(2));
+
+ const std::optional<CustomDataType> type = node_type_from_other_socket(params.other_socket());
+ if (!type) {
+ return;
+ }
+
+ if (params.in_out() == SOCK_IN) {
+ params.add_item(IFACE_("Attribute"), [node_type, type](LinkSearchOpParams &params) {
+ bNode &node = params.add_node(node_type);
+ node.custom1 = *type;
+ params.update_and_connect_available_socket(node, "Attribute");
+ });
+ }
+ else {
+ for (const StringRefNull name :
+ {"Mean", "Median", "Sum", "Min", "Max", "Range", "Standard Deviation", "Variance"}) {
+ params.add_item(IFACE_(name.c_str()), [node_type, name, type](LinkSearchOpParams &params) {
+ bNode &node = params.add_node(node_type);
+ node.custom1 = *type;
+ params.update_and_connect_available_socket(node, name);
+ });
+ }
+ }
}
template<typename T> static T compute_sum(const Span<T> data)
@@ -146,65 +193,40 @@ static float median_of_sorted_span(const Span<float> data)
}
return median;
}
-static void set_empty(CustomDataType data_type, GeoNodeExecParams &params)
-{
- if (data_type == CD_PROP_FLOAT) {
- params.set_output("Mean", 0.0f);
- params.set_output("Median", 0.0f);
- params.set_output("Sum", 0.0f);
- params.set_output("Min", 0.0f);
- params.set_output("Max", 0.0f);
- params.set_output("Range", 0.0f);
- params.set_output("Standard Deviation", 0.0f);
- params.set_output("Variance", 0.0f);
- }
- else if (data_type == CD_PROP_FLOAT3) {
- params.set_output("Mean_001", float3{0.0f, 0.0f, 0.0f});
- params.set_output("Median_001", float3{0.0f, 0.0f, 0.0f});
- params.set_output("Sum_001", float3{0.0f, 0.0f, 0.0f});
- params.set_output("Min_001", float3{0.0f, 0.0f, 0.0f});
- params.set_output("Max_001", float3{0.0f, 0.0f, 0.0f});
- params.set_output("Range_001", float3{0.0f, 0.0f, 0.0f});
- params.set_output("Standard Deviation_001", float3{0.0f, 0.0f, 0.0f});
- params.set_output("Variance_001", float3{0.0f, 0.0f, 0.0f});
- }
-}
-static void geo_node_attribute_statistic_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.get_input<GeometrySet>("Geometry");
-
const bNode &node = params.node();
const CustomDataType data_type = static_cast<CustomDataType>(node.custom1);
const AttributeDomain domain = static_cast<AttributeDomain>(node.custom2);
-
- int64_t total_size = 0;
Vector<const GeometryComponent *> components = geometry_set.get_components_for_read();
- for (const GeometryComponent *component : components) {
- if (component->attribute_domain_supported(domain)) {
- total_size += component->attribute_domain_size(domain);
- }
- }
- if (total_size == 0) {
- set_empty(data_type, params);
- return;
- }
+ const Field<bool> selection_field = params.get_input<Field<bool>>("Selection");
switch (data_type) {
case CD_PROP_FLOAT: {
const Field<float> input_field = params.get_input<Field<float>>("Attribute");
- Array<float> data = Array<float>(total_size);
- int offset = 0;
+ Vector<float> data;
for (const GeometryComponent *component : components) {
if (component->attribute_domain_supported(domain)) {
GeometryComponentFieldContext field_context{*component, domain};
const int domain_size = component->attribute_domain_size(domain);
+
fn::FieldEvaluator data_evaluator{field_context, domain_size};
- MutableSpan<float> component_result = data.as_mutable_span().slice(offset, domain_size);
- data_evaluator.add_with_destination(input_field, component_result);
+ data_evaluator.add(input_field);
+ data_evaluator.set_selection(selection_field);
data_evaluator.evaluate();
- offset += domain_size;
+ const VArray<float> &component_data = data_evaluator.get_evaluated<float>(0);
+ const IndexMask selection = data_evaluator.get_evaluated_selection_as_mask();
+
+ const int next_data_index = data.size();
+ data.resize(next_data_index + selection.size());
+ MutableSpan<float> selected_data = data.as_mutable_span().slice(next_data_index,
+ selection.size());
+ for (const int i : selection.index_range()) {
+ selected_data[i] = component_data[selection[i]];
+ }
}
}
@@ -225,7 +247,7 @@ static void geo_node_attribute_statistic_exec(GeoNodeExecParams params)
const bool variance_required = params.output_is_required("Standard Deviation") ||
params.output_is_required("Variance");
- if (total_size != 0) {
+ if (data.size() != 0) {
if (sort_required) {
std::sort(data.begin(), data.end());
median = median_of_sorted_span(data);
@@ -236,7 +258,7 @@ static void geo_node_attribute_statistic_exec(GeoNodeExecParams params)
}
if (sum_required || variance_required) {
sum = compute_sum<float>(data);
- mean = sum / total_size;
+ mean = sum / data.size();
if (variance_required) {
variance = compute_variance(data, mean);
@@ -263,18 +285,26 @@ static void geo_node_attribute_statistic_exec(GeoNodeExecParams params)
}
case CD_PROP_FLOAT3: {
const Field<float3> input_field = params.get_input<Field<float3>>("Attribute_001");
-
- Array<float3> data = Array<float3>(total_size);
- int offset = 0;
+ Vector<float3> data;
for (const GeometryComponent *component : components) {
if (component->attribute_domain_supported(domain)) {
GeometryComponentFieldContext field_context{*component, domain};
const int domain_size = component->attribute_domain_size(domain);
+
fn::FieldEvaluator data_evaluator{field_context, domain_size};
- MutableSpan<float3> component_result = data.as_mutable_span().slice(offset, domain_size);
- data_evaluator.add_with_destination(input_field, component_result);
+ data_evaluator.add(input_field);
+ data_evaluator.set_selection(selection_field);
data_evaluator.evaluate();
- offset += domain_size;
+ const VArray<float3> &component_data = data_evaluator.get_evaluated<float3>(0);
+ const IndexMask selection = data_evaluator.get_evaluated_selection_as_mask();
+
+ const int next_data_index = data.size();
+ data.resize(data.size() + selection.size());
+ MutableSpan<float3> selected_data = data.as_mutable_span().slice(next_data_index,
+ selection.size());
+ for (const int i : selection.index_range()) {
+ selected_data[i] = component_data[selection[i]];
+ }
}
}
@@ -299,9 +329,9 @@ static void geo_node_attribute_statistic_exec(GeoNodeExecParams params)
Array<float> data_y;
Array<float> data_z;
if (sort_required || variance_required) {
- data_x.reinitialize(total_size);
- data_y.reinitialize(total_size);
- data_z.reinitialize(total_size);
+ data_x.reinitialize(data.size());
+ data_y.reinitialize(data.size());
+ data_z.reinitialize(data.size());
for (const int i : data.index_range()) {
data_x[i] = data[i].x;
data_y[i] = data[i].y;
@@ -309,7 +339,7 @@ static void geo_node_attribute_statistic_exec(GeoNodeExecParams params)
}
}
- if (total_size != 0) {
+ if (data.size() != 0) {
if (sort_required) {
std::sort(data_x.begin(), data_x.end());
std::sort(data_y.begin(), data_y.end());
@@ -326,7 +356,7 @@ static void geo_node_attribute_statistic_exec(GeoNodeExecParams params)
}
if (sum_required || variance_required) {
sum = compute_sum(data.as_span());
- mean = sum / total_size;
+ mean = sum / data.size();
if (variance_required) {
const float x_variance = compute_variance(data_x, mean.x);
@@ -360,19 +390,22 @@ static void geo_node_attribute_statistic_exec(GeoNodeExecParams params)
}
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_attribute_statistic_cc
void register_node_type_geo_attribute_statistic()
{
+ namespace file_ns = blender::nodes::node_geo_attribute_statistic_cc;
+
static bNodeType ntype;
geo_node_type_base(
- &ntype, GEO_NODE_ATTRIBUTE_STATISTIC, "Attribute Statistic", NODE_CLASS_ATTRIBUTE, 0);
-
- ntype.declare = blender::nodes::geo_node_attribute_statistic_declare;
- node_type_init(&ntype, blender::nodes::geo_node_attribute_statistic_init);
- node_type_update(&ntype, blender::nodes::geo_node_attribute_statistic_update);
- ntype.geometry_node_execute = blender::nodes::geo_node_attribute_statistic_exec;
- ntype.draw_buttons = blender::nodes::geo_node_attribute_statistic_layout;
+ &ntype, GEO_NODE_ATTRIBUTE_STATISTIC, "Attribute Statistic", NODE_CLASS_ATTRIBUTE);
+
+ ntype.declare = file_ns::node_declare;
+ node_type_init(&ntype, file_ns::node_init);
+ node_type_update(&ntype, file_ns::node_update);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.draw_buttons = file_ns::node_layout;
+ ntype.gather_link_search_ops = file_ns::node_gather_link_searches;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc
index 21b425c0ed4..a9158e0ef7a 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc
@@ -23,23 +23,25 @@
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_boolean_cc {
-static void geo_node_boolean_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry 1");
- b.add_input<decl::Geometry>("Geometry 2").multi_input();
- b.add_input<decl::Bool>("Self Intersection");
- b.add_input<decl::Bool>("Hole Tolerant");
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Mesh 1"))
+ .only_realized_data()
+ .supported_type(GEO_COMPONENT_TYPE_MESH);
+ b.add_input<decl::Geometry>(N_("Mesh 2")).multi_input().supported_type(GEO_COMPONENT_TYPE_MESH);
+ b.add_input<decl::Bool>(N_("Self Intersection"));
+ b.add_input<decl::Bool>(N_("Hole Tolerant"));
+ b.add_output<decl::Geometry>(N_("Mesh"));
}
-static void geo_node_boolean_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiItemR(layout, ptr, "operation", 0, "", ICON_NONE);
}
-static void geo_node_boolean_update(bNodeTree *UNUSED(ntree), bNode *node)
+static void node_update(bNodeTree *ntree, bNode *node)
{
GeometryNodeBooleanOperation operation = (GeometryNodeBooleanOperation)node->custom1;
@@ -49,24 +51,24 @@ static void geo_node_boolean_update(bNodeTree *UNUSED(ntree), bNode *node)
switch (operation) {
case GEO_NODE_BOOLEAN_INTERSECT:
case GEO_NODE_BOOLEAN_UNION:
- nodeSetSocketAvailability(geometry_1_socket, false);
- nodeSetSocketAvailability(geometry_2_socket, true);
- node_sock_label(geometry_2_socket, N_("Geometry"));
+ nodeSetSocketAvailability(ntree, geometry_1_socket, false);
+ nodeSetSocketAvailability(ntree, geometry_2_socket, true);
+ node_sock_label(geometry_2_socket, N_("Mesh"));
break;
case GEO_NODE_BOOLEAN_DIFFERENCE:
- nodeSetSocketAvailability(geometry_1_socket, true);
- nodeSetSocketAvailability(geometry_2_socket, true);
- node_sock_label(geometry_2_socket, N_("Geometry 2"));
+ nodeSetSocketAvailability(ntree, geometry_1_socket, true);
+ nodeSetSocketAvailability(ntree, geometry_2_socket, true);
+ node_sock_label(geometry_2_socket, N_("Mesh 2"));
break;
}
}
-static void geo_node_boolean_init(bNodeTree *UNUSED(tree), bNode *node)
+static void node_init(bNodeTree *UNUSED(tree), bNode *node)
{
node->custom1 = GEO_NODE_BOOLEAN_DIFFERENCE;
}
-static void geo_node_boolean_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
GeometryNodeBooleanOperation operation = (GeometryNodeBooleanOperation)params.node().custom1;
const bool use_self = params.get_input<bool>("Self Intersection");
@@ -82,12 +84,7 @@ static void geo_node_boolean_exec(GeoNodeExecParams params)
GeometrySet set_a;
if (operation == GEO_NODE_BOOLEAN_DIFFERENCE) {
- set_a = params.extract_input<GeometrySet>("Geometry 1");
- if (set_a.has_instances()) {
- params.error_message_add(
- NodeWarningType::Info,
- TIP_("Instances are not supported for the first geometry input, and will not be used"));
- }
+ set_a = params.extract_input<GeometrySet>("Mesh 1");
/* Note that it technically wouldn't be necessary to realize the instances for the first
* geometry input, but the boolean code expects the first shape for the difference operation
* to be a single mesh. */
@@ -101,7 +98,7 @@ static void geo_node_boolean_exec(GeoNodeExecParams params)
/* The instance transform matrices are owned by the instance group, so we have to
* keep all of them around for use during the boolean operation. */
Vector<bke::GeometryInstanceGroup> set_groups;
- Vector<GeometrySet> geometry_sets = params.extract_multi_input<GeometrySet>("Geometry 2");
+ Vector<GeometrySet> geometry_sets = params.extract_multi_input<GeometrySet>("Mesh 2");
for (const GeometrySet &geometry_set : geometry_sets) {
bke::geometry_set_gather_instances(geometry_set, set_groups);
}
@@ -119,20 +116,22 @@ static void geo_node_boolean_exec(GeoNodeExecParams params)
Mesh *result = blender::meshintersect::direct_mesh_boolean(
meshes, transforms, float4x4::identity(), {}, use_self, hole_tolerant, operation);
- params.set_output("Geometry", GeometrySet::create_with_mesh(result));
+ params.set_output("Mesh", GeometrySet::create_with_mesh(result));
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_boolean_cc
void register_node_type_geo_boolean()
{
+ namespace file_ns = blender::nodes::node_geo_boolean_cc;
+
static bNodeType ntype;
- geo_node_type_base(&ntype, GEO_NODE_BOOLEAN, "Boolean", NODE_CLASS_GEOMETRY, 0);
- ntype.declare = blender::nodes::geo_node_boolean_declare;
- ntype.draw_buttons = blender::nodes::geo_node_boolean_layout;
- ntype.updatefunc = blender::nodes::geo_node_boolean_update;
- node_type_init(&ntype, blender::nodes::geo_node_boolean_init);
- ntype.geometry_node_execute = blender::nodes::geo_node_boolean_exec;
+ geo_node_type_base(&ntype, GEO_NODE_MESH_BOOLEAN, "Mesh Boolean", NODE_CLASS_GEOMETRY);
+ ntype.declare = file_ns::node_declare;
+ ntype.draw_buttons = file_ns::node_layout;
+ ntype.updatefunc = file_ns::node_update;
+ node_type_init(&ntype, file_ns::node_init);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc b/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc
index fdc6b12095c..465bd72b57a 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc
@@ -14,160 +14,80 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "BKE_spline.hh"
-#include "BKE_volume.h"
-
#include "node_geometry_util.hh"
-namespace blender::nodes {
-
-static void geo_node_bounding_box_declare(NodeDeclarationBuilder &b)
-{
- b.add_input<decl::Geometry>("Geometry");
- b.add_output<decl::Geometry>("Bounding Box");
- b.add_output<decl::Vector>("Min");
- b.add_output<decl::Vector>("Max");
-}
-
-using bke::GeometryInstanceGroup;
+namespace blender::nodes::node_geo_bounding_box_cc {
-static void compute_min_max_from_position_and_transform(const GeometryComponent &component,
- Span<float4x4> transforms,
- float3 &r_min,
- float3 &r_max)
+static void node_declare(NodeDeclarationBuilder &b)
{
- GVArray_Typed<float3> positions = component.attribute_get_for_read<float3>(
- "position", ATTR_DOMAIN_POINT, {0, 0, 0});
-
- for (const float4x4 &transform : transforms) {
- for (const int i : positions.index_range()) {
- const float3 position = positions[i];
- const float3 transformed_position = transform * position;
- minmax_v3v3_v3(r_min, r_max, transformed_position);
- }
- }
-}
-
-static void compute_min_max_from_volume_and_transforms(const VolumeComponent &volume_component,
- Span<float4x4> transforms,
- float3 &r_min,
- float3 &r_max)
-{
-#ifdef WITH_OPENVDB
- const Volume *volume = volume_component.get_for_read();
- if (volume == nullptr) {
- return;
- }
- for (const int i : IndexRange(BKE_volume_num_grids(volume))) {
- const VolumeGrid *volume_grid = BKE_volume_grid_get_for_read(volume, i);
- openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid);
-
- for (const float4x4 &transform : transforms) {
- openvdb::GridBase::ConstPtr instance_grid = BKE_volume_grid_shallow_transform(grid,
- transform);
- float3 grid_min = float3(FLT_MAX);
- float3 grid_max = float3(-FLT_MAX);
- if (BKE_volume_grid_bounds(instance_grid, grid_min, grid_max)) {
- DO_MIN(grid_min, r_min);
- DO_MAX(grid_max, r_max);
- }
- }
- }
-#else
- UNUSED_VARS(volume_component, transforms, r_min, r_max);
-#endif
-}
-
-static void compute_min_max_from_curve_and_transforms(const CurveComponent &curve_component,
- Span<float4x4> transforms,
- float3 &r_min,
- float3 &r_max)
-{
- const CurveEval *curve = curve_component.get_for_read();
- if (curve == nullptr) {
- return;
- }
- for (const SplinePtr &spline : curve->splines()) {
- Span<float3> positions = spline->evaluated_positions();
-
- for (const float4x4 &transform : transforms) {
- for (const int i : positions.index_range()) {
- const float3 position = positions[i];
- const float3 transformed_position = transform * position;
- minmax_v3v3_v3(r_min, r_max, transformed_position);
- }
- }
- }
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_output<decl::Geometry>(N_("Bounding Box"));
+ b.add_output<decl::Vector>(N_("Min"));
+ b.add_output<decl::Vector>(N_("Max"));
}
-static void compute_geometry_set_instances_boundbox(const GeometrySet &geometry_set,
- float3 &r_min,
- float3 &r_max)
-{
- Vector<GeometryInstanceGroup> set_groups;
- bke::geometry_set_gather_instances(geometry_set, set_groups);
-
- for (const GeometryInstanceGroup &set_group : set_groups) {
- const GeometrySet &set = set_group.geometry_set;
- Span<float4x4> transforms = set_group.transforms;
-
- if (set.has<PointCloudComponent>()) {
- compute_min_max_from_position_and_transform(
- *set.get_component_for_read<PointCloudComponent>(), transforms, r_min, r_max);
- }
- if (set.has<MeshComponent>()) {
- compute_min_max_from_position_and_transform(
- *set.get_component_for_read<MeshComponent>(), transforms, r_min, r_max);
- }
- if (set.has<VolumeComponent>()) {
- compute_min_max_from_volume_and_transforms(
- *set.get_component_for_read<VolumeComponent>(), transforms, r_min, r_max);
- }
- if (set.has<CurveComponent>()) {
- compute_min_max_from_curve_and_transforms(
- *set.get_component_for_read<CurveComponent>(), transforms, r_min, r_max);
- }
- }
-}
-
-static void geo_node_bounding_box_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ /* Compute the min and max of all realized geometry for the two
+ * vector outputs, which are only meant to consider real geometry. */
float3 min = float3(FLT_MAX);
float3 max = float3(-FLT_MAX);
-
- if (geometry_set.has_instances()) {
- compute_geometry_set_instances_boundbox(geometry_set, min, max);
- }
- else {
- geometry_set.compute_boundbox_without_instances(&min, &max);
- }
-
+ geometry_set.compute_boundbox_without_instances(&min, &max);
if (min == float3(FLT_MAX)) {
- params.set_output("Bounding Box", GeometrySet());
params.set_output("Min", float3(0));
params.set_output("Max", float3(0));
}
else {
- const float3 scale = max - min;
- const float3 center = min + scale / 2.0f;
- Mesh *mesh = create_cuboid_mesh(scale, 2, 2, 2);
- transform_mesh(mesh, center, float3(0), float3(1));
- params.set_output("Bounding Box", GeometrySet::create_with_mesh(mesh));
params.set_output("Min", min);
params.set_output("Max", max);
}
+
+ /* Generate the bounding box meshes inside each unique geometry set (including individually for
+ * every instance). Because geometry components are reference counted anyway, we can just
+ * repurpose the original geometry sets for the output. */
+ if (params.output_is_required("Bounding Box")) {
+ geometry_set.modify_geometry_sets([&](GeometrySet &sub_geometry) {
+ float3 sub_min = float3(FLT_MAX);
+ float3 sub_max = float3(-FLT_MAX);
+
+ /* Reuse the min and max calculation if this is the main "real" geometry set. */
+ if (&sub_geometry == &geometry_set) {
+ sub_min = min;
+ sub_max = max;
+ }
+ else {
+ sub_geometry.compute_boundbox_without_instances(&sub_min, &sub_max);
+ }
+
+ if (sub_min == float3(FLT_MAX)) {
+ sub_geometry.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ }
+ else {
+ const float3 scale = sub_max - sub_min;
+ const float3 center = sub_min + scale / 2.0f;
+ Mesh *mesh = create_cuboid_mesh(scale, 2, 2, 2);
+ transform_mesh(*mesh, center, float3(0), float3(1));
+ sub_geometry.replace_mesh(mesh);
+ sub_geometry.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES});
+ }
+ });
+
+ params.set_output("Bounding Box", std::move(geometry_set));
+ }
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_bounding_box_cc
void register_node_type_geo_bounding_box()
{
+ namespace file_ns = blender::nodes::node_geo_bounding_box_cc;
+
static bNodeType ntype;
- geo_node_type_base(&ntype, GEO_NODE_BOUNDING_BOX, "Bounding Box", NODE_CLASS_GEOMETRY, 0);
- ntype.declare = blender::nodes::geo_node_bounding_box_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_bounding_box_exec;
+ geo_node_type_base(&ntype, GEO_NODE_BOUNDING_BOX, "Bounding Box", NODE_CLASS_GEOMETRY);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc b/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc
index d03221703f0..43816b8d8dc 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc
@@ -25,53 +25,67 @@
#include "node_geometry_util.hh"
-namespace blender::nodes {
+#include <algorithm>
-static void geo_node_collection_info_declare(NodeDeclarationBuilder &b)
+namespace blender::nodes::node_geo_collection_info_cc {
+
+NODE_STORAGE_FUNCS(NodeGeometryCollectionInfo)
+
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Collection>("Collection").hide_label();
- b.add_input<decl::Bool>("Separate Children")
- .description("Output each child of the collection as a separate instance");
- b.add_input<decl::Bool>("Reset Children")
+ b.add_input<decl::Collection>(N_("Collection")).hide_label();
+ b.add_input<decl::Bool>(N_("Separate Children"))
.description(
- "Reset the transforms of every child instance in the output. Only used when Separate "
- "Children is enabled");
- b.add_output<decl::Geometry>("Geometry");
+ N_("Output each child of the collection as a separate instance, sorted alphabetically"));
+ b.add_input<decl::Bool>(N_("Reset Children"))
+ .description(
+ N_("Reset the transforms of every child instance in the output. Only used when Separate "
+ "Children is enabled"));
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
-static void geo_node_collection_info_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiItemR(layout, ptr, "transform_space", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
}
-static void geo_node_collection_info_node_init(bNodeTree *UNUSED(tree), bNode *node)
+static void node_node_init(bNodeTree *UNUSED(tree), bNode *node)
{
- NodeGeometryCollectionInfo *data = (NodeGeometryCollectionInfo *)MEM_callocN(
- sizeof(NodeGeometryCollectionInfo), __func__);
+ NodeGeometryCollectionInfo *data = MEM_cnew<NodeGeometryCollectionInfo>(__func__);
data->transform_space = GEO_NODE_TRANSFORM_SPACE_ORIGINAL;
node->storage = data;
}
-static void geo_node_collection_info_exec(GeoNodeExecParams params)
+struct InstanceListEntry {
+ int handle;
+ char *name;
+ float4x4 transform;
+};
+
+static void node_geo_exec(GeoNodeExecParams params)
{
Collection *collection = params.get_input<Collection *>("Collection");
- GeometrySet geometry_set_out;
-
if (collection == nullptr) {
- params.set_output("Geometry", geometry_set_out);
+ params.set_default_remaining_outputs();
+ return;
+ }
+ const Object *self_object = params.self_object();
+ const bool is_recursive = BKE_collection_has_object_recursive_instanced(collection,
+ (Object *)self_object);
+ if (is_recursive) {
+ params.error_message_add(NodeWarningType::Error, "Collection contains current object");
+ params.set_default_remaining_outputs();
return;
}
- const bNode &bnode = params.node();
- NodeGeometryCollectionInfo *node_storage = (NodeGeometryCollectionInfo *)bnode.storage;
- const bool use_relative_transform = (node_storage->transform_space ==
+ const NodeGeometryCollectionInfo &storage = node_storage(params.node());
+ const bool use_relative_transform = (storage.transform_space ==
GEO_NODE_TRANSFORM_SPACE_RELATIVE);
+ GeometrySet geometry_set_out;
InstancesComponent &instances = geometry_set_out.get_component_for_write<InstancesComponent>();
- const Object *self_object = params.self_object();
-
const bool separate_children = params.get_input<bool>("Separate Children");
if (separate_children) {
const bool reset_children = params.get_input<bool>("Reset Children");
@@ -85,6 +99,8 @@ static void geo_node_collection_info_exec(GeoNodeExecParams params)
}
instances.reserve(children_collections.size() + children_objects.size());
+ Vector<InstanceListEntry> entries;
+ entries.reserve(children_collections.size() + children_objects.size());
for (Collection *child_collection : children_collections) {
float4x4 transform = float4x4::identity();
@@ -98,7 +114,7 @@ static void geo_node_collection_info_exec(GeoNodeExecParams params)
}
}
const int handle = instances.add_reference(*child_collection);
- instances.add_instance(handle, transform);
+ entries.append({handle, &(child_collection->id.name[2]), transform});
}
for (Object *child_object : children_objects) {
const int handle = instances.add_reference(*child_object);
@@ -112,7 +128,16 @@ static void geo_node_collection_info_exec(GeoNodeExecParams params)
}
mul_m4_m4_post(transform.values, child_object->obmat);
}
- instances.add_instance(handle, transform);
+ entries.append({handle, &(child_object->id.name[2]), transform});
+ }
+
+ std::sort(entries.begin(),
+ entries.end(),
+ [](const InstanceListEntry &a, const InstanceListEntry &b) {
+ return BLI_strcasecmp_natural(a.name, b.name) <= 0;
+ });
+ for (const InstanceListEntry &entry : entries) {
+ instances.add_instance(entry.handle, entry.transform);
}
}
else {
@@ -129,20 +154,22 @@ static void geo_node_collection_info_exec(GeoNodeExecParams params)
params.set_output("Geometry", geometry_set_out);
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_collection_info_cc
void register_node_type_geo_collection_info()
{
+ namespace file_ns = blender::nodes::node_geo_collection_info_cc;
+
static bNodeType ntype;
- geo_node_type_base(&ntype, GEO_NODE_COLLECTION_INFO, "Collection Info", NODE_CLASS_INPUT, 0);
- ntype.declare = blender::nodes::geo_node_collection_info_declare;
- node_type_init(&ntype, blender::nodes::geo_node_collection_info_node_init);
+ geo_node_type_base(&ntype, GEO_NODE_COLLECTION_INFO, "Collection Info", NODE_CLASS_INPUT);
+ ntype.declare = file_ns::node_declare;
+ node_type_init(&ntype, file_ns::node_node_init);
node_type_storage(&ntype,
"NodeGeometryCollectionInfo",
node_free_standard_storage,
node_copy_standard_storage);
- ntype.geometry_node_execute = blender::nodes::geo_node_collection_info_exec;
- ntype.draw_buttons = blender::nodes::geo_node_collection_info_layout;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.draw_buttons = file_ns::node_layout;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_common.cc b/source/blender/nodes/geometry/nodes/node_geo_common.cc
index e2bb7e9f939..093b4450657 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_common.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_common.cc
@@ -22,23 +22,21 @@
#include "node_common.h"
#include "node_geometry_util.hh"
-void register_node_type_geo_group(void)
+void register_node_type_geo_group()
{
static bNodeType ntype;
- node_type_base_custom(&ntype, "GeometryNodeGroup", "Group", NODE_CLASS_GROUP, 0);
+ node_type_base_custom(&ntype, "GeometryNodeGroup", "Group", NODE_CLASS_GROUP);
ntype.type = NODE_GROUP;
ntype.poll = geo_node_poll_default;
ntype.poll_instance = node_group_poll_instance;
ntype.insert_link = node_insert_link_default;
- ntype.update_internal_links = node_update_internal_links_default;
ntype.rna_ext.srna = RNA_struct_find("GeometryNodeGroup");
BLI_assert(ntype.rna_ext.srna != nullptr);
RNA_struct_blender_type_set(ntype.rna_ext.srna, &ntype);
- node_type_socket_templates(&ntype, nullptr, nullptr);
node_type_size(&ntype, 140, 60, 400);
- node_type_label(&ntype, node_group_label);
+ ntype.labelfunc = node_group_label;
node_type_group_update(&ntype, node_group_update);
nodeRegisterType(&ntype);
@@ -53,7 +51,4 @@ void register_node_type_geo_custom_group(bNodeType *ntype)
if (ntype->insert_link == nullptr) {
ntype->insert_link = node_insert_link_default;
}
- if (ntype->update_internal_links == nullptr) {
- ntype->update_internal_links = node_update_internal_links_default;
- }
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc
index 4377d32210d..11bb8a61df5 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc
@@ -28,12 +28,12 @@
# include "RBI_hull_api.h"
#endif
-namespace blender::nodes {
+namespace blender::nodes::node_geo_convex_hull_cc {
-static void geo_node_convex_hull_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_output<decl::Geometry>("Convex Hull");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_output<decl::Geometry>(N_("Convex Hull"));
}
using bke::GeometryInstanceGroup;
@@ -169,10 +169,10 @@ static Mesh *compute_hull(const GeometrySet &geometry_set)
span_count++;
const PointCloudComponent *component =
geometry_set.get_component_for_read<PointCloudComponent>();
- GVArray_Typed<float3> varray = component->attribute_get_for_read<float3>(
+ VArray<float3> varray = component->attribute_get_for_read<float3>(
"position", ATTR_DOMAIN_POINT, {0, 0, 0});
- total_size += varray->size();
- positions_span = varray->get_internal_span();
+ total_size += varray.size();
+ positions_span = varray.get_internal_span();
}
if (geometry_set.has_curve()) {
@@ -200,18 +200,18 @@ static Mesh *compute_hull(const GeometrySet &geometry_set)
if (geometry_set.has_mesh()) {
const MeshComponent *component = geometry_set.get_component_for_read<MeshComponent>();
- GVArray_Typed<float3> varray = component->attribute_get_for_read<float3>(
+ VArray<float3> varray = component->attribute_get_for_read<float3>(
"position", ATTR_DOMAIN_POINT, {0, 0, 0});
- varray->materialize(positions.as_mutable_span().slice(offset, varray.size()));
+ varray.materialize(positions.as_mutable_span().slice(offset, varray.size()));
offset += varray.size();
}
if (geometry_set.has_pointcloud()) {
const PointCloudComponent *component =
geometry_set.get_component_for_read<PointCloudComponent>();
- GVArray_Typed<float3> varray = component->attribute_get_for_read<float3>(
+ VArray<float3> varray = component->attribute_get_for_read<float3>(
"position", ATTR_DOMAIN_POINT, {0, 0, 0});
- varray->materialize(positions.as_mutable_span().slice(offset, varray.size()));
+ varray.materialize(positions.as_mutable_span().slice(offset, varray.size()));
offset += varray.size();
}
@@ -235,16 +235,16 @@ static void read_positions(const GeometryComponent &component,
Span<float4x4> transforms,
Vector<float3> *r_coords)
{
- GVArray_Typed<float3> positions = component.attribute_get_for_read<float3>(
+ VArray<float3> positions = component.attribute_get_for_read<float3>(
"position", ATTR_DOMAIN_POINT, {0, 0, 0});
/* NOTE: could use convex hull operation here to
* cut out some vertices, before accumulating,
* but can also be done by the user beforehand. */
- r_coords->reserve(r_coords->size() + positions.size() * transforms.size());
+ r_coords->reserve(r_coords->size() + positions->size() * transforms.size());
for (const float4x4 &transform : transforms) {
- for (const int i : positions.index_range()) {
+ for (const int i : positions->index_range()) {
const float3 position = positions[i];
const float3 transformed_position = transform * position;
r_coords->append(transformed_position);
@@ -296,7 +296,7 @@ static Mesh *convex_hull_from_instances(const GeometrySet &geometry_set)
#endif /* WITH_BULLET */
-static void geo_node_convex_hull_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
@@ -312,18 +312,20 @@ static void geo_node_convex_hull_exec(GeoNodeExecParams params)
#else
params.error_message_add(NodeWarningType::Error,
TIP_("Disabled, Blender was compiled without Bullet"));
- params.set_output("Convex Hull", geometry_set);
+ params.set_default_remaining_outputs();
#endif /* WITH_BULLET */
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_convex_hull_cc
void register_node_type_geo_convex_hull()
{
+ namespace file_ns = blender::nodes::node_geo_convex_hull_cc;
+
static bNodeType ntype;
- geo_node_type_base(&ntype, GEO_NODE_CONVEX_HULL, "Convex Hull", NODE_CLASS_GEOMETRY, 0);
- ntype.declare = blender::nodes::geo_node_convex_hull_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_convex_hull_exec;
+ geo_node_type_base(&ntype, GEO_NODE_CONVEX_HULL, "Convex Hull", NODE_CLASS_GEOMETRY);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc
new file mode 100644
index 00000000000..666100ffd7f
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc
@@ -0,0 +1,147 @@
+/*
+ * 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 "BKE_spline.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_curve_endpoint_selection_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Int>(N_("Start Size"))
+ .min(0)
+ .default_value(1)
+ .supports_field()
+ .description(N_("The amount of points to select from the start of each spline"));
+ b.add_input<decl::Int>(N_("End Size"))
+ .min(0)
+ .default_value(1)
+ .supports_field()
+ .description(N_("The amount of points to select from the end of each spline"));
+ b.add_output<decl::Bool>(N_("Selection"))
+ .field_source()
+ .description(
+ N_("The selection from the start and end of the splines based on the input sizes"));
+}
+
+static void select_by_spline(const int start, const int end, MutableSpan<bool> r_selection)
+{
+ const int size = r_selection.size();
+ const int start_use = std::min(start, size);
+ const int end_use = std::min(end, size);
+
+ r_selection.slice(0, start_use).fill(true);
+ r_selection.slice(size - end_use, end_use).fill(true);
+}
+
+class EndpointFieldInput final : public GeometryFieldInput {
+ Field<int> start_size_;
+ Field<int> end_size_;
+
+ public:
+ EndpointFieldInput(Field<int> start_size, Field<int> end_size)
+ : GeometryFieldInput(CPPType::get<bool>(), "Endpoint Selection node"),
+ start_size_(start_size),
+ end_size_(end_size)
+ {
+ category_ = Category::Generated;
+ }
+
+ GVArray get_varray_for_context(const GeometryComponent &component,
+ const AttributeDomain domain,
+ IndexMask UNUSED(mask)) const final
+ {
+ if (component.type() != GEO_COMPONENT_TYPE_CURVE || domain != ATTR_DOMAIN_POINT) {
+ return nullptr;
+ }
+
+ const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
+ const CurveEval *curve = curve_component.get_for_read();
+
+ Array<int> control_point_offsets = curve->control_point_offsets();
+
+ if (curve == nullptr || control_point_offsets.last() == 0) {
+ return nullptr;
+ }
+
+ GeometryComponentFieldContext size_context{curve_component, ATTR_DOMAIN_CURVE};
+ fn::FieldEvaluator evaluator{size_context, curve->splines().size()};
+ evaluator.add(start_size_);
+ evaluator.add(end_size_);
+ evaluator.evaluate();
+ const VArray<int> &start_size = evaluator.get_evaluated<int>(0);
+ const VArray<int> &end_size = evaluator.get_evaluated<int>(1);
+
+ const int point_size = control_point_offsets.last();
+ Array<bool> selection(point_size, false);
+ int current_point = 0;
+ MutableSpan<bool> selection_span = selection.as_mutable_span();
+ for (int i : IndexRange(curve->splines().size())) {
+ const SplinePtr &spline = curve->splines()[i];
+ if (start_size[i] <= 0 && end_size[i] <= 0) {
+ selection_span.slice(current_point, spline->size()).fill(false);
+ }
+ else {
+ int start_use = std::max(start_size[i], 0);
+ int end_use = std::max(end_size[i], 0);
+ select_by_spline(start_use, end_use, selection_span.slice(current_point, spline->size()));
+ }
+ current_point += spline->size();
+ }
+ return VArray<bool>::ForContainer(std::move(selection));
+ };
+
+ uint64_t hash() const override
+ {
+ return get_default_hash_2(start_size_, end_size_);
+ }
+
+ bool is_equal_to(const fn::FieldNode &other) const override
+ {
+ if (const EndpointFieldInput *other_endpoint = dynamic_cast<const EndpointFieldInput *>(
+ &other)) {
+ return start_size_ == other_endpoint->start_size_ && end_size_ == other_endpoint->end_size_;
+ }
+ return false;
+ }
+};
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ Field<int> start_size = params.extract_input<Field<int>>("Start Size");
+ Field<int> end_size = params.extract_input<Field<int>>("End Size");
+ Field<bool> selection_field{std::make_shared<EndpointFieldInput>(start_size, end_size)};
+ params.set_output("Selection", std::move(selection_field));
+}
+} // namespace blender::nodes::node_geo_curve_endpoint_selection_cc
+
+void register_node_type_geo_curve_endpoint_selection()
+{
+ namespace file_ns = blender::nodes::node_geo_curve_endpoint_selection_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_CURVE_ENDPOINT_SELECTION, "Endpoint Selection", NODE_CLASS_INPUT);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc
index c30741cf786..929d9046f98 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc
@@ -16,7 +16,7 @@
#include "BLI_array.hh"
#include "BLI_delaunay_2d.h"
-#include "BLI_double2.hh"
+#include "BLI_math_vec_types.hh"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
@@ -31,23 +31,24 @@
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_curve_fill_cc {
-static void geo_node_curve_fill_declare(NodeDeclarationBuilder &b)
+NODE_STORAGE_FUNCS(NodeGeometryCurveFill)
+
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Curve");
- b.add_output<decl::Geometry>("Mesh");
+ b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE);
+ b.add_output<decl::Geometry>(N_("Mesh"));
}
-static void geo_node_curve_fill_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
}
-static void geo_node_curve_fill_init(bNodeTree *UNUSED(ntree), bNode *node)
+static void node_init(bNodeTree *UNUSED(ntree), bNode *node)
{
- NodeGeometryCurveFill *data = (NodeGeometryCurveFill *)MEM_callocN(sizeof(NodeGeometryCurveFill),
- __func__);
+ NodeGeometryCurveFill *data = MEM_cnew<NodeGeometryCurveFill>(__func__);
data->mode = GEO_NODE_CURVE_FILL_MODE_TRIANGULATED;
node->storage = data;
@@ -147,11 +148,11 @@ static void curve_fill_calculate(GeometrySet &geometry_set, const GeometryNodeCu
geometry_set.replace_curve(nullptr);
}
-static void geo_node_curve_fill_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
- const NodeGeometryCurveFill &storage = *(const NodeGeometryCurveFill *)params.node().storage;
+ const NodeGeometryCurveFill &storage = node_storage(params.node());
const GeometryNodeCurveFillMode mode = (GeometryNodeCurveFillMode)storage.mode;
geometry_set.modify_geometry_sets(
@@ -160,19 +161,21 @@ static void geo_node_curve_fill_exec(GeoNodeExecParams params)
params.set_output("Mesh", std::move(geometry_set));
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_curve_fill_cc
void register_node_type_geo_curve_fill()
{
+ namespace file_ns = blender::nodes::node_geo_curve_fill_cc;
+
static bNodeType ntype;
- geo_node_type_base(&ntype, GEO_NODE_CURVE_FILL, "Curve Fill", NODE_CLASS_GEOMETRY, 0);
+ geo_node_type_base(&ntype, GEO_NODE_FILL_CURVE, "Fill Curve", NODE_CLASS_GEOMETRY);
- node_type_init(&ntype, blender::nodes::geo_node_curve_fill_init);
+ node_type_init(&ntype, file_ns::node_init);
node_type_storage(
&ntype, "NodeGeometryCurveFill", node_free_standard_storage, node_copy_standard_storage);
- ntype.declare = blender::nodes::geo_node_curve_fill_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_curve_fill_exec;
- ntype.draw_buttons = blender::nodes::geo_node_curve_fill_layout;
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.draw_buttons = file_ns::node_layout;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc
index 67ce20efd9d..68b609f8045 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc
@@ -25,30 +25,37 @@
#include "BKE_spline.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_curve_fillet_cc {
-static void geo_node_curve_fillet_declare(NodeDeclarationBuilder &b)
+NODE_STORAGE_FUNCS(NodeGeometryCurveFillet)
+
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Curve");
- b.add_input<decl::Int>("Count").default_value(1).min(1).max(1000);
- b.add_input<decl::Float>("Radius")
+ b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE);
+ b.add_input<decl::Int>(N_("Count"))
+ .default_value(1)
+ .min(1)
+ .max(1000)
+ .supports_field()
+ .make_available([](bNode &node) { node_storage(node).mode = GEO_NODE_CURVE_FILLET_POLY; });
+ b.add_input<decl::Float>(N_("Radius"))
.min(0.0f)
.max(FLT_MAX)
.subtype(PropertySubType::PROP_DISTANCE)
- .default_value(0.25f);
- b.add_input<decl::Bool>("Limit Radius");
- b.add_output<decl::Geometry>("Curve");
+ .default_value(0.25f)
+ .supports_field();
+ b.add_input<decl::Bool>(N_("Limit Radius"));
+ b.add_output<decl::Geometry>(N_("Curve"));
}
-static void geo_node_curve_fillet_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
}
-static void geo_node_curve_fillet_init(bNodeTree *UNUSED(tree), bNode *node)
+static void node_init(bNodeTree *UNUSED(tree), bNode *node)
{
- NodeGeometryCurveFillet *data = (NodeGeometryCurveFillet *)MEM_callocN(
- sizeof(NodeGeometryCurveFillet), __func__);
+ NodeGeometryCurveFillet *data = MEM_cnew<NodeGeometryCurveFillet>(__func__);
data->mode = GEO_NODE_CURVE_FILLET_BEZIER;
node->storage = data;
@@ -58,10 +65,10 @@ struct FilletParam {
GeometryNodeCurveFilletMode mode;
/* Number of points to be added. */
- const VArray<int> *counts;
+ VArray<int> counts;
/* Radii for fillet arc at all vertices. */
- const VArray<float> *radii;
+ VArray<float> radii;
/* Whether or not fillets are allowed to overlap. */
bool limit_radius;
@@ -75,14 +82,14 @@ struct FilletData {
Array<int> counts;
};
-static void geo_node_curve_fillet_update(bNodeTree *UNUSED(ntree), bNode *node)
+static void node_update(bNodeTree *ntree, bNode *node)
{
- NodeGeometryCurveFillet &node_storage = *(NodeGeometryCurveFillet *)node->storage;
- const GeometryNodeCurveFilletMode mode = (GeometryNodeCurveFilletMode)node_storage.mode;
+ const NodeGeometryCurveFillet &storage = node_storage(*node);
+ const GeometryNodeCurveFilletMode mode = (GeometryNodeCurveFilletMode)storage.mode;
bNodeSocket *poly_socket = ((bNodeSocket *)node->inputs.first)->next;
- nodeSetSocketAvailability(poly_socket, mode == GEO_NODE_CURVE_FILLET_POLY);
+ nodeSetSocketAvailability(ntree, poly_socket, mode == GEO_NODE_CURVE_FILLET_POLY);
}
/* Function to get the center of a fillet. */
@@ -115,9 +122,9 @@ static Array<float3> calculate_directions(const Span<float3> positions)
Array<float3> directions(size);
for (const int i : IndexRange(size - 1)) {
- directions[i] = (positions[i + 1] - positions[i]).normalized();
+ directions[i] = math::normalize(positions[i + 1] - positions[i]);
}
- directions[size - 1] = (positions[0] - positions[size - 1]).normalized();
+ directions[size - 1] = math::normalize(positions[0] - positions[size - 1]);
return directions;
}
@@ -128,9 +135,9 @@ static Array<float3> calculate_axes(const Span<float3> directions)
const int size = directions.size();
Array<float3> axes(size);
- axes[0] = float3::cross(-directions[size - 1], directions[0]).normalized();
+ axes[0] = math::normalize(math::cross(-directions[size - 1], directions[0]));
for (const int i : IndexRange(1, size - 1)) {
- axes[i] = float3::cross(-directions[i - 1], directions[i]).normalized();
+ axes[i] = math::normalize(math::cross(-directions[i - 1], directions[i]));
}
return axes;
@@ -159,7 +166,7 @@ static Array<int> calculate_counts(const FilletParam &fillet_param,
Array<int> counts(size, 1);
if (fillet_param.mode == GEO_NODE_CURVE_FILLET_POLY) {
for (const int i : IndexRange(size)) {
- counts[i] = (*fillet_param.counts)[spline_offset + i];
+ counts[i] = fillet_param.counts[spline_offset + i];
}
}
if (!cyclic) {
@@ -177,12 +184,12 @@ static Array<float> calculate_radii(const FilletParam &fillet_param,
Array<float> radii(size, 0.0f);
if (fillet_param.limit_radius) {
for (const int i : IndexRange(size)) {
- radii[i] = std::max((*fillet_param.radii)[spline_offset + i], 0.0f);
+ radii[i] = std::max(fillet_param.radii[spline_offset + i], 0.0f);
}
}
else {
for (const int i : IndexRange(size)) {
- radii[i] = (*fillet_param.radii)[spline_offset + i];
+ radii[i] = fillet_param.radii[spline_offset + i];
}
}
@@ -241,8 +248,8 @@ static void limit_radii(FilletData &fd, const bool cyclic)
if (cyclic) {
/* Calculate lengths between adjacent control points. */
- const float len_prev = float3::distance(positions[0], positions[size - 1]);
- const float len_next = float3::distance(positions[0], positions[1]);
+ const float len_prev = math::distance(positions[0], positions[size - 1]);
+ const float len_next = math::distance(positions[0], positions[1]);
/* Calculate tangent lengths of fillets in control points. */
const float tan_len = radii[0] * tan(angles[0] / 2.0f);
@@ -264,16 +271,16 @@ static void limit_radii(FilletData &fd, const bool cyclic)
}
/* Initialize max_radii to largest possible radii. */
- float prev_dist = float3::distance(positions[1], positions[0]);
+ float prev_dist = math::distance(positions[1], positions[0]);
for (const int i : IndexRange(1, size - 2)) {
- const float temp_dist = float3::distance(positions[i], positions[i + 1]);
+ const float temp_dist = math::distance(positions[i], positions[i + 1]);
max_radii[i] = std::min(prev_dist, temp_dist) / tan(angles[i] / 2.0f);
prev_dist = temp_dist;
}
/* Max radii calculations for each index. */
for (const int i : IndexRange(start, fillet_count - 1)) {
- const float len_next = float3::distance(positions[i], positions[i + 1]);
+ const float len_next = math::distance(positions[i], positions[i + 1]);
const float tan_len = radii[i] * tan(angles[i] / 2.0f);
const float tan_len_next = radii[i + 1] * tan(angles[i + 1] / 2.0f);
@@ -331,14 +338,17 @@ static void copy_common_attributes_by_mapping(const Spline &src,
copy_attribute_by_mapping(src.radii(), dst.radii(), mapping);
copy_attribute_by_mapping(src.tilts(), dst.tilts(), mapping);
- dst.attributes.reallocate(1);
src.attributes.foreach_attribute(
[&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
std::optional<GSpan> src_attribute = src.attributes.get_for_read(attribute_id);
if (dst.attributes.create(attribute_id, meta_data.data_type)) {
std::optional<GMutableSpan> dst_attribute = dst.attributes.get_for_write(attribute_id);
if (dst_attribute) {
- src_attribute->type().copy_assign(src_attribute->data(), dst_attribute->data());
+ attribute_math::convert_to_static_type(dst_attribute->type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ copy_attribute_by_mapping(
+ src_attribute->typed<T>(), dst_attribute->typed<T>(), mapping);
+ });
return true;
}
}
@@ -405,7 +415,8 @@ static void update_bezier_positions(const FilletData &fd,
const float3 center = get_center(dst_spline.positions()[i_dst] - positions[i_src], fd, i_src);
/* Calculate the vector of the radius formed by the first vertex. */
float3 radius_vec = dst_spline.positions()[i_dst] - center;
- const float radius = radius_vec.normalize_and_get_length();
+ float radius;
+ radius_vec = math::normalize_and_get_length(radius_vec, radius);
dst_spline.handle_types_right().slice(1, count - 2).fill(BezierSpline::HandleType::Align);
dst_spline.handle_types_left().slice(1, count - 2).fill(BezierSpline::HandleType::Align);
@@ -589,13 +600,13 @@ static void calculate_curve_fillet(GeometrySet &geometry_set,
field_evaluator.evaluate();
- fillet_param.radii = &field_evaluator.get_evaluated<float>(0);
- if (fillet_param.radii->is_single() && fillet_param.radii->get_internal_single() < 0.0f) {
+ fillet_param.radii = field_evaluator.get_evaluated<float>(0);
+ if (fillet_param.radii.is_single() && fillet_param.radii.get_internal_single() < 0.0f) {
return;
}
if (mode == GEO_NODE_CURVE_FILLET_POLY) {
- fillet_param.counts = &field_evaluator.get_evaluated<int>(1);
+ fillet_param.counts = field_evaluator.get_evaluated<int>(1);
}
fillet_param.limit_radius = limit_radius;
@@ -606,12 +617,12 @@ static void calculate_curve_fillet(GeometrySet &geometry_set,
geometry_set.replace_curve(output_curve.release());
}
-static void geo_node_fillet_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
- NodeGeometryCurveFillet &node_storage = *(NodeGeometryCurveFillet *)params.node().storage;
- const GeometryNodeCurveFilletMode mode = (GeometryNodeCurveFilletMode)node_storage.mode;
+ const NodeGeometryCurveFillet &storage = node_storage(params.node());
+ const GeometryNodeCurveFilletMode mode = (GeometryNodeCurveFilletMode)storage.mode;
Field<float> radius_field = params.extract_input<Field<float>>("Radius");
const bool limit_radius = params.extract_input<bool>("Limit Radius");
@@ -628,19 +639,21 @@ static void geo_node_fillet_exec(GeoNodeExecParams params)
params.set_output("Curve", std::move(geometry_set));
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_curve_fillet_cc
void register_node_type_geo_curve_fillet()
{
+ namespace file_ns = blender::nodes::node_geo_curve_fillet_cc;
+
static bNodeType ntype;
- geo_node_type_base(&ntype, GEO_NODE_CURVE_FILLET, "Curve Fillet", NODE_CLASS_GEOMETRY, 0);
- ntype.draw_buttons = blender::nodes::geo_node_curve_fillet_layout;
+ geo_node_type_base(&ntype, GEO_NODE_FILLET_CURVE, "Fillet Curve", NODE_CLASS_GEOMETRY);
+ ntype.draw_buttons = file_ns::node_layout;
node_type_storage(
&ntype, "NodeGeometryCurveFillet", node_free_standard_storage, node_copy_standard_storage);
- ntype.declare = blender::nodes::geo_node_curve_fillet_declare;
- node_type_init(&ntype, blender::nodes::geo_node_curve_fillet_init);
- node_type_update(&ntype, blender::nodes::geo_node_curve_fillet_update);
- ntype.geometry_node_execute = blender::nodes::geo_node_fillet_exec;
+ ntype.declare = file_ns::node_declare;
+ node_type_init(&ntype, file_ns::node_init);
+ node_type_update(&ntype, file_ns::node_update);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc
new file mode 100644
index 00000000000..e4e87e519f7
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc
@@ -0,0 +1,169 @@
+/*
+ * 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 "BKE_spline.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_curve_handle_type_selection_cc {
+
+NODE_STORAGE_FUNCS(NodeGeometryCurveSelectHandles)
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_output<decl::Bool>(N_("Selection")).field_source();
+}
+
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "handle_type", 0, "", ICON_NONE);
+}
+
+static void node_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometryCurveSelectHandles *data = MEM_cnew<NodeGeometryCurveSelectHandles>(__func__);
+
+ data->handle_type = GEO_NODE_CURVE_HANDLE_AUTO;
+ data->mode = GEO_NODE_CURVE_HANDLE_LEFT | GEO_NODE_CURVE_HANDLE_RIGHT;
+ node->storage = data;
+}
+
+static BezierSpline::HandleType handle_type_from_input_type(const GeometryNodeCurveHandleType type)
+{
+ switch (type) {
+ case GEO_NODE_CURVE_HANDLE_AUTO:
+ return BezierSpline::HandleType::Auto;
+ case GEO_NODE_CURVE_HANDLE_ALIGN:
+ return BezierSpline::HandleType::Align;
+ case GEO_NODE_CURVE_HANDLE_FREE:
+ return BezierSpline::HandleType::Free;
+ case GEO_NODE_CURVE_HANDLE_VECTOR:
+ return BezierSpline::HandleType::Vector;
+ }
+ BLI_assert_unreachable();
+ return BezierSpline::HandleType::Auto;
+}
+
+static void select_by_handle_type(const CurveEval &curve,
+ const BezierSpline::HandleType type,
+ const GeometryNodeCurveHandleMode mode,
+ const MutableSpan<bool> r_selection)
+{
+ int offset = 0;
+ for (const SplinePtr &spline : curve.splines()) {
+ if (spline->type() != Spline::Type::Bezier) {
+ r_selection.slice(offset, spline->size()).fill(false);
+ offset += spline->size();
+ }
+ else {
+ BezierSpline *b = static_cast<BezierSpline *>(spline.get());
+ for (int i : IndexRange(b->size())) {
+ r_selection[offset++] = (mode & GEO_NODE_CURVE_HANDLE_LEFT &&
+ b->handle_types_left()[i] == type) ||
+ (mode & GEO_NODE_CURVE_HANDLE_RIGHT &&
+ b->handle_types_right()[i] == type);
+ }
+ }
+ }
+}
+
+class HandleTypeFieldInput final : public GeometryFieldInput {
+ BezierSpline::HandleType type_;
+ GeometryNodeCurveHandleMode mode_;
+
+ public:
+ HandleTypeFieldInput(BezierSpline::HandleType type, GeometryNodeCurveHandleMode mode)
+ : GeometryFieldInput(CPPType::get<bool>(), "Handle Type Selection node"),
+ type_(type),
+ mode_(mode)
+ {
+ category_ = Category::Generated;
+ }
+
+ GVArray get_varray_for_context(const GeometryComponent &component,
+ const AttributeDomain domain,
+ IndexMask mask) const final
+ {
+ if (component.type() != GEO_COMPONENT_TYPE_CURVE) {
+ return {};
+ }
+
+ const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
+ const CurveEval *curve = curve_component.get_for_read();
+ if (curve == nullptr) {
+ return {};
+ }
+
+ if (domain == ATTR_DOMAIN_POINT) {
+ Array<bool> selection(mask.min_array_size());
+ select_by_handle_type(*curve, type_, mode_, selection);
+ return VArray<bool>::ForContainer(std::move(selection));
+ }
+ return {};
+ }
+
+ uint64_t hash() const override
+ {
+ return get_default_hash_2((int)mode_, (int)type_);
+ }
+
+ bool is_equal_to(const fn::FieldNode &other) const override
+ {
+ return dynamic_cast<const HandleTypeFieldInput *>(&other) != nullptr;
+ if (const HandleTypeFieldInput *other_handle_selection =
+ dynamic_cast<const HandleTypeFieldInput *>(&other)) {
+ return mode_ == other_handle_selection->mode_ && type_ == other_handle_selection->type_;
+ }
+ return false;
+ }
+};
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ const NodeGeometryCurveSelectHandles &storage = node_storage(params.node());
+ const BezierSpline::HandleType handle_type = handle_type_from_input_type(
+ (GeometryNodeCurveHandleType)storage.handle_type);
+ const GeometryNodeCurveHandleMode mode = (GeometryNodeCurveHandleMode)storage.mode;
+
+ Field<bool> selection_field{std::make_shared<HandleTypeFieldInput>(handle_type, mode)};
+ params.set_output("Selection", std::move(selection_field));
+}
+
+} // namespace blender::nodes::node_geo_curve_handle_type_selection_cc
+
+void register_node_type_geo_curve_handle_type_selection()
+{
+ namespace file_ns = blender::nodes::node_geo_curve_handle_type_selection_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_CURVE_HANDLE_TYPE_SELECTION, "Handle Type Selection", NODE_CLASS_INPUT);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ node_type_init(&ntype, file_ns::node_init);
+ node_type_storage(&ntype,
+ "NodeGeometryCurveSelectHandles",
+ node_free_standard_storage,
+ node_copy_standard_storage);
+ ntype.draw_buttons = file_ns::node_layout;
+
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc
index ac7df35bb72..21ae88a6852 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc
@@ -17,19 +17,19 @@
#include "BKE_spline.hh"
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_curve_length_cc {
-static void geo_node_curve_length_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Curve");
- b.add_output<decl::Float>("Length");
+ b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE);
+ b.add_output<decl::Float>(N_("Length"));
}
-static void geo_node_curve_length_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet curve_set = params.extract_input<GeometrySet>("Curve");
if (!curve_set.has_curve()) {
- params.set_output("Length", 0.0f);
+ params.set_default_remaining_outputs();
return;
}
const CurveEval &curve = *curve_set.get_curve_for_read();
@@ -40,14 +40,16 @@ static void geo_node_curve_length_exec(GeoNodeExecParams params)
params.set_output("Length", length);
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_curve_length_cc
void register_node_type_geo_curve_length()
{
+ namespace file_ns = blender::nodes::node_geo_curve_length_cc;
+
static bNodeType ntype;
- geo_node_type_base(&ntype, GEO_NODE_CURVE_LENGTH, "Curve Length", NODE_CLASS_GEOMETRY, 0);
- ntype.declare = blender::nodes::geo_node_curve_length_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_curve_length_exec;
+ geo_node_type_base(&ntype, GEO_NODE_CURVE_LENGTH, "Curve Length", NODE_CLASS_GEOMETRY);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_parameter.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_parameter.cc
deleted file mode 100644
index 90853387ec7..00000000000
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_parameter.cc
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * 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 "BKE_spline.hh"
-
-#include "node_geometry_util.hh"
-
-namespace blender::nodes {
-
-static void geo_node_curve_parameter_declare(NodeDeclarationBuilder &b)
-{
- b.add_output<decl::Float>("Factor").field_source();
-}
-
-/**
- * A basic interpolation from the point domain to the spline domain would be useless, since the
- * average parameter for each spline would just be 0.5, or close to it. Instead, the parameter for
- * each spline is the portion of the total length at the start of the spline.
- */
-static Array<float> curve_parameter_spline_domain(const CurveEval &curve, const IndexMask mask)
-{
- Span<SplinePtr> splines = curve.splines();
- float length = 0.0f;
- Array<float> parameters(splines.size());
- for (const int i : splines.index_range()) {
- parameters[i] = length;
- length += splines[i]->length();
- }
- const float total_length_inverse = length == 0.0f ? 0.0f : 1.0f / length;
- mask.foreach_index([&](const int64_t i) { parameters[i] *= total_length_inverse; });
-
- return parameters;
-}
-
-/**
- * The parameter at each control point is the factor at the corresponding evaluated point.
- */
-static void calculate_bezier_parameters(const BezierSpline &spline, MutableSpan<float> parameters)
-{
- Span<int> offsets = spline.control_point_offsets();
- Span<float> lengths = spline.evaluated_lengths();
- const float total_length = spline.length();
- const float total_length_inverse = total_length == 0.0f ? 0.0f : 1.0f / total_length;
-
- for (const int i : IndexRange(1, spline.size() - 1)) {
- parameters[i] = lengths[offsets[i] - 1] * total_length_inverse;
- }
-}
-
-/**
- * The parameter for poly splines is simply the evaluated lengths divided by the total length.
- */
-static void calculate_poly_parameters(const PolySpline &spline, MutableSpan<float> parameters)
-{
- Span<float> lengths = spline.evaluated_lengths();
- const float total_length = spline.length();
- const float total_length_inverse = total_length == 0.0f ? 0.0f : 1.0f / total_length;
-
- for (const int i : IndexRange(1, spline.size() - 1)) {
- parameters[i] = lengths[i - 1] * total_length_inverse;
- }
-}
-
-/**
- * Since NURBS control points do not necessarily coincide with the evaluated curve's path, and
- * each control point doesn't correspond well to a specific evaluated point, the parameter at
- * each point is not well defined. So instead, treat the control points as if they were a poly
- * spline.
- */
-static void calculate_nurbs_parameters(const NURBSpline &spline, MutableSpan<float> parameters)
-{
- Span<float3> positions = spline.positions();
- Array<float> control_point_lengths(spline.size());
-
- float length = 0.0f;
- for (const int i : IndexRange(positions.size() - 1)) {
- parameters[i] = length;
- length += float3::distance(positions[i], positions[i + 1]);
- }
-
- const float total_length_inverse = length == 0.0f ? 0.0f : 1.0f / length;
- for (float &parameter : parameters) {
- parameter *= total_length_inverse;
- }
-}
-
-static Array<float> curve_parameter_point_domain(const CurveEval &curve)
-{
- Span<SplinePtr> splines = curve.splines();
- Array<int> offsets = curve.control_point_offsets();
- const int total_size = offsets.last();
- Array<float> parameters(total_size);
-
- threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) {
- for (const int i : range) {
- const Spline &spline = *splines[i];
- MutableSpan spline_factors{parameters.as_mutable_span().slice(offsets[i], spline.size())};
- spline_factors.first() = 0.0f;
- switch (splines[i]->type()) {
- case Spline::Type::Bezier: {
- calculate_bezier_parameters(static_cast<const BezierSpline &>(spline), spline_factors);
- break;
- }
- case Spline::Type::Poly: {
- calculate_poly_parameters(static_cast<const PolySpline &>(spline), spline_factors);
- break;
- }
- case Spline::Type::NURBS: {
- calculate_nurbs_parameters(static_cast<const NURBSpline &>(spline), spline_factors);
- break;
- }
- }
- }
- });
- return parameters;
-}
-
-static const GVArray *construct_curve_parameter_gvarray(const CurveEval &curve,
- const IndexMask mask,
- const AttributeDomain domain,
- ResourceScope &scope)
-{
- if (domain == ATTR_DOMAIN_POINT) {
- Array<float> parameters = curve_parameter_point_domain(curve);
- return &scope.construct<fn::GVArray_For_ArrayContainer<Array<float>>>(std::move(parameters));
- }
-
- if (domain == ATTR_DOMAIN_CURVE) {
- Array<float> parameters = curve_parameter_spline_domain(curve, mask);
- return &scope.construct<fn::GVArray_For_ArrayContainer<Array<float>>>(std::move(parameters));
- }
-
- return nullptr;
-}
-
-class CurveParameterFieldInput final : public fn::FieldInput {
- public:
- CurveParameterFieldInput() : fn::FieldInput(CPPType::get<float>(), "Curve Parameter")
- {
- }
-
- const GVArray *get_varray_for_context(const fn::FieldContext &context,
- IndexMask mask,
- ResourceScope &scope) const final
- {
- if (const GeometryComponentFieldContext *geometry_context =
- dynamic_cast<const GeometryComponentFieldContext *>(&context)) {
-
- const GeometryComponent &component = geometry_context->geometry_component();
- const AttributeDomain domain = geometry_context->domain();
-
- if (component.type() == GEO_COMPONENT_TYPE_CURVE) {
- const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
- const CurveEval *curve = curve_component.get_for_read();
- if (curve) {
- return construct_curve_parameter_gvarray(*curve, mask, domain, scope);
- }
- }
- }
- return nullptr;
- }
-
- uint64_t hash() const override
- {
- /* Some random constant hash. */
- return 29837456298;
- }
-
- bool is_equal_to(const fn::FieldNode &other) const override
- {
- return dynamic_cast<const CurveParameterFieldInput *>(&other) != nullptr;
- }
-};
-
-static void geo_node_curve_parameter_exec(GeoNodeExecParams params)
-{
- Field<float> parameter_field{std::make_shared<CurveParameterFieldInput>()};
- params.set_output("Factor", std::move(parameter_field));
-}
-
-} // namespace blender::nodes
-
-void register_node_type_geo_curve_parameter()
-{
- static bNodeType ntype;
-
- geo_node_type_base(&ntype, GEO_NODE_CURVE_PARAMETER, "Curve Parameter", NODE_CLASS_INPUT, 0);
- ntype.geometry_node_execute = blender::nodes::geo_node_curve_parameter_exec;
- ntype.declare = blender::nodes::geo_node_curve_parameter_declare;
- nodeRegisterType(&ntype);
-}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_arc.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_arc.cc
new file mode 100644
index 00000000000..3f6298168a2
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_arc.cc
@@ -0,0 +1,391 @@
+/*
+ * 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 "BKE_spline.hh"
+#include "BLI_math_base_safe.h"
+#include "UI_interface.h"
+#include "UI_resources.h"
+#include "node_geometry_util.hh"
+#include <numeric>
+
+namespace blender::nodes::node_geo_curve_primitive_arc_cc {
+
+NODE_STORAGE_FUNCS(NodeGeometryCurvePrimitiveArc)
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Int>(N_("Resolution"))
+ .default_value(16)
+ .min(2)
+ .max(256)
+ .subtype(PROP_UNSIGNED)
+ .description(N_("The number of points on the arc"));
+ b.add_input<decl::Vector>(N_("Start"))
+ .default_value({-1.0f, 0.0f, 0.0f})
+ .subtype(PROP_TRANSLATION)
+ .description(N_("Position of the first control point"));
+ b.add_input<decl::Vector>(N_("Middle"))
+ .default_value({0.0f, 2.0f, 0.0f})
+ .subtype(PROP_TRANSLATION)
+ .description(N_("Position of the middle control point"));
+ b.add_input<decl::Vector>(N_("End"))
+ .default_value({1.0f, 0.0f, 0.0f})
+ .subtype(PROP_TRANSLATION)
+ .description(N_("Position of the last control point"));
+ b.add_input<decl::Float>(N_("Radius"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .subtype(PROP_DISTANCE)
+ .description(N_("Distance of the points from the origin"));
+ b.add_input<decl::Float>(N_("Start Angle"))
+ .default_value(0.0f)
+ .subtype(PROP_ANGLE)
+ .description(N_("Starting angle of the arc"));
+ b.add_input<decl::Float>(N_("Sweep Angle"))
+ .default_value(1.75f * M_PI)
+ .min(-2 * M_PI)
+ .max(2 * M_PI)
+ .subtype(PROP_ANGLE)
+ .description(N_("Length of the arc"));
+ b.add_input<decl::Float>(N_("Offset Angle"))
+ .default_value(0.0f)
+ .subtype(PROP_ANGLE)
+ .description(N_("Offset angle of the arc"));
+ b.add_input<decl::Bool>(N_("Connect Center"))
+ .default_value(false)
+ .description(N_("Connect the arc at the center"));
+ b.add_input<decl::Bool>(N_("Invert Arc"))
+ .default_value(false)
+ .description(N_("Invert and draw opposite arc"));
+
+ b.add_output<decl::Geometry>(N_("Curve"));
+ b.add_output<decl::Vector>(N_("Center"))
+ .description(N_("The center of the circle described by the three points"))
+ .make_available(
+ [](bNode &node) { node_storage(node).mode = GEO_NODE_CURVE_PRIMITIVE_ARC_TYPE_POINTS; });
+ b.add_output<decl::Vector>(N_("Normal"))
+ .description(N_("The normal direction of the plane described by the three points, pointing "
+ "towards the positive Z axis"))
+ .make_available(
+ [](bNode &node) { node_storage(node).mode = GEO_NODE_CURVE_PRIMITIVE_ARC_TYPE_POINTS; });
+ b.add_output<decl::Float>(N_("Radius"))
+ .description(N_("The radius of the circle described by the three points"))
+ .make_available(
+ [](bNode &node) { node_storage(node).mode = GEO_NODE_CURVE_PRIMITIVE_ARC_TYPE_POINTS; });
+}
+
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
+}
+
+static void node_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometryCurvePrimitiveArc *data = MEM_cnew<NodeGeometryCurvePrimitiveArc>(__func__);
+
+ data->mode = GEO_NODE_CURVE_PRIMITIVE_ARC_TYPE_RADIUS;
+ node->storage = data;
+}
+
+static void node_update(bNodeTree *ntree, bNode *node)
+{
+ const NodeGeometryCurvePrimitiveArc &storage = node_storage(*node);
+ const GeometryNodeCurvePrimitiveArcMode mode = (GeometryNodeCurvePrimitiveArcMode)storage.mode;
+
+ bNodeSocket *start_socket = ((bNodeSocket *)node->inputs.first)->next;
+ bNodeSocket *middle_socket = start_socket->next;
+ bNodeSocket *end_socket = middle_socket->next;
+
+ bNodeSocket *radius_socket = end_socket->next;
+ bNodeSocket *start_angle_socket = radius_socket->next;
+ bNodeSocket *sweep_angle_socket = start_angle_socket->next;
+
+ bNodeSocket *offset_angle_socket = sweep_angle_socket->next;
+
+ bNodeSocket *center_out_socket = ((bNodeSocket *)node->outputs.first)->next;
+ bNodeSocket *normal_out_socket = center_out_socket->next;
+ bNodeSocket *radius_out_socket = normal_out_socket->next;
+
+ const bool radius_mode = (mode == GEO_NODE_CURVE_PRIMITIVE_ARC_TYPE_RADIUS);
+ const bool points_mode = (mode == GEO_NODE_CURVE_PRIMITIVE_ARC_TYPE_POINTS);
+
+ nodeSetSocketAvailability(ntree, start_socket, points_mode);
+ nodeSetSocketAvailability(ntree, middle_socket, points_mode);
+ nodeSetSocketAvailability(ntree, end_socket, points_mode);
+
+ nodeSetSocketAvailability(ntree, radius_socket, radius_mode);
+ nodeSetSocketAvailability(ntree, start_angle_socket, radius_mode);
+ nodeSetSocketAvailability(ntree, sweep_angle_socket, radius_mode);
+
+ nodeSetSocketAvailability(ntree, offset_angle_socket, points_mode);
+
+ nodeSetSocketAvailability(ntree, center_out_socket, points_mode);
+ nodeSetSocketAvailability(ntree, normal_out_socket, points_mode);
+ nodeSetSocketAvailability(ntree, radius_out_socket, points_mode);
+}
+
+static float3 rotate_vector_around_axis(const float3 vector, const float3 axis, const float angle)
+{
+ float3 result = vector;
+ float mat[3][3];
+ axis_angle_to_mat3(mat, axis, angle);
+ mul_m3_v3(mat, result);
+ return result;
+}
+
+static bool colinear_f3_f3_f3(const float3 p1, const float3 p2, const float3 p3)
+{
+ const float3 a = math::normalize(p2 - p1);
+ const float3 b = math::normalize(p3 - p1);
+ return (ELEM(a, b, b * -1.0f));
+}
+
+static std::unique_ptr<CurveEval> create_arc_curve_from_points(const int resolution,
+ const float3 a,
+ const float3 b,
+ const float3 c,
+ float angle_offset,
+ const bool connect_center,
+ const bool invert_arc,
+ float3 &r_center,
+ float3 &r_normal,
+ float &r_radius)
+{
+ std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>();
+ std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>();
+
+ if (connect_center) {
+ spline->resize(resolution + 1);
+ }
+ else {
+ spline->resize(resolution);
+ }
+
+ const int stepcount = resolution - 1;
+ const int centerpoint = resolution;
+ MutableSpan<float3> positions = spline->positions();
+ spline->radii().fill(1.0f);
+ spline->tilts().fill(0.0f);
+
+ const bool is_colinear = colinear_f3_f3_f3(a, b, c);
+
+ float3 center;
+ float3 normal;
+ float radius;
+ const float3 mid_ac = math::midpoint(a, c);
+ normal_tri_v3(normal, a, c, b);
+
+ if (is_colinear || a == c || a == b || b == c || resolution == 2) {
+ /* If colinear, generate a point line between points. */
+ float3 p1, p2;
+
+ /* Find the two points that are furthest away from each other. */
+ const float ab = math::distance_squared(a, b);
+ const float ac = math::distance_squared(a, c);
+ const float bc = math::distance_squared(b, c);
+ if (ab > ac && ab > bc) {
+ p1 = a;
+ p2 = b;
+ }
+ else if (bc > ab && bc > ac) {
+ p1 = b;
+ p2 = c;
+ }
+ else {
+ p1 = a;
+ p2 = c;
+ }
+
+ const float step = 1.0f / stepcount;
+ for (const int i : IndexRange(resolution)) {
+ const float factor = step * i;
+ positions[i] = math::interpolate(p1, p2, factor);
+ }
+ center = mid_ac;
+ radius = 0.0f;
+ }
+ else {
+ /* Midpoints of `A->B` and `B->C`. */
+ const float3 mid_ab = math::midpoint(a, b);
+ const float3 mid_bc = math::midpoint(c, b);
+
+ /* Normalized vectors of `A->B` and `B->C`. */
+ const float3 nba = math::normalize(b - a);
+ const float3 ncb = math::normalize(c - b);
+
+ /* Normal of plane of main 2 segments A->B and `B->C`. */
+ const float3 nabc = math::normalize(math::cross(nba, ncb));
+
+ /* Determine center point from the intersection of 3 planes. */
+ float plane_1[4], plane_2[4], plane_3[4];
+ plane_from_point_normal_v3(plane_1, mid_ab, nabc);
+ plane_from_point_normal_v3(plane_2, mid_ab, nba);
+ plane_from_point_normal_v3(plane_3, mid_bc, ncb);
+
+ /* If the 3 planes do not intersect at one point, just return empty geometry. */
+ if (!isect_plane_plane_plane_v3(plane_1, plane_2, plane_3, center)) {
+ r_center = mid_ac;
+ r_normal = normal;
+ r_radius = 0.0f;
+ return nullptr;
+ }
+
+ /* Radial vectors. */
+ const float3 rad_a = math::normalize(a - center);
+ const float3 rad_b = math::normalize(b - center);
+ const float3 rad_c = math::normalize(c - center);
+
+ /* Calculate angles. */
+ radius = math::distance(center, b);
+ float angle_ab = angle_signed_on_axis_v3v3_v3(rad_a, rad_b, normal) + 2.0f * M_PI;
+ float angle_ac = angle_signed_on_axis_v3v3_v3(rad_a, rad_c, normal) + 2.0f * M_PI;
+ float angle = (angle_ac > angle_ab) ? angle_ac : angle_ab;
+ angle -= 2.0f * M_PI;
+ if (invert_arc) {
+ angle = -(2.0f * M_PI - angle);
+ }
+
+ /* Create arc. */
+ const float step = angle / stepcount;
+ for (const int i : IndexRange(resolution)) {
+ const float factor = step * i + angle_offset;
+ float3 out = rotate_vector_around_axis(rad_a, -normal, factor);
+ positions[i] = out * radius + center;
+ }
+ }
+
+ if (connect_center) {
+ spline->set_cyclic(true);
+ positions[centerpoint] = center;
+ }
+
+ /* Ensure normal is relative to Z-up. */
+ if (math::dot(float3(0, 0, 1), normal) < 0) {
+ normal = -normal;
+ }
+
+ curve->add_spline(std::move(spline));
+ curve->attributes.reallocate(curve->splines().size());
+ r_center = center;
+ r_radius = radius;
+ r_normal = normal;
+ return curve;
+}
+
+static std::unique_ptr<CurveEval> create_arc_curve_from_radius(const int resolution,
+ const float radius,
+ const float start_angle,
+ const float sweep_angle,
+ const bool connect_center,
+ const bool invert_arc)
+{
+ std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>();
+ std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>();
+
+ if (connect_center) {
+ spline->resize(resolution + 1);
+ }
+ else {
+ spline->resize(resolution);
+ }
+
+ const int stepcount = resolution - 1;
+ const int centerpoint = resolution;
+ MutableSpan<float3> positions = spline->positions();
+ spline->radii().fill(1.0f);
+ spline->tilts().fill(0.0f);
+
+ const float sweep = (invert_arc) ? -(2.0f * M_PI - sweep_angle) : sweep_angle;
+
+ const float theta_step = sweep / float(stepcount);
+ for (const int i : IndexRange(resolution)) {
+ const float theta = theta_step * i + start_angle;
+ const float x = radius * cos(theta);
+ const float y = radius * sin(theta);
+ positions[i] = float3(x, y, 0.0f);
+ }
+
+ if (connect_center) {
+ spline->set_cyclic(true);
+ positions[centerpoint] = float3(0.0f, 0.0f, 0.0f);
+ }
+
+ curve->add_spline(std::move(spline));
+ curve->attributes.reallocate(curve->splines().size());
+ return curve;
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ const NodeGeometryCurvePrimitiveArc &storage = node_storage(params.node());
+
+ const GeometryNodeCurvePrimitiveArcMode mode = (GeometryNodeCurvePrimitiveArcMode)storage.mode;
+
+ switch (mode) {
+ case GEO_NODE_CURVE_PRIMITIVE_ARC_TYPE_POINTS: {
+ std::unique_ptr<CurveEval> curve;
+ float3 r_center, r_normal;
+ float r_radius;
+ curve = create_arc_curve_from_points(std::max(params.extract_input<int>("Resolution"), 2),
+ params.extract_input<float3>("Start"),
+ params.extract_input<float3>("Middle"),
+ params.extract_input<float3>("End"),
+ params.extract_input<float>("Offset Angle"),
+ params.extract_input<bool>("Connect Center"),
+ params.extract_input<bool>("Invert Arc"),
+ r_center,
+ r_normal,
+ r_radius);
+ params.set_output("Curve", GeometrySet::create_with_curve(curve.release()));
+ params.set_output("Center", r_center);
+ params.set_output("Normal", r_normal);
+ params.set_output("Radius", r_radius);
+ break;
+ }
+ case GEO_NODE_CURVE_PRIMITIVE_ARC_TYPE_RADIUS: {
+ std::unique_ptr<CurveEval> curve;
+ curve = create_arc_curve_from_radius(std::max(params.extract_input<int>("Resolution"), 2),
+ params.extract_input<float>("Radius"),
+ params.extract_input<float>("Start Angle"),
+ params.extract_input<float>("Sweep Angle"),
+ params.extract_input<bool>("Connect Center"),
+ params.extract_input<bool>("Invert Arc"));
+
+ params.set_output("Curve", GeometrySet::create_with_curve(curve.release()));
+ break;
+ }
+ }
+}
+
+} // namespace blender::nodes::node_geo_curve_primitive_arc_cc
+
+void register_node_type_geo_curve_primitive_arc()
+{
+ namespace file_ns = blender::nodes::node_geo_curve_primitive_arc_cc;
+
+ static bNodeType ntype;
+ geo_node_type_base(&ntype, GEO_NODE_CURVE_PRIMITIVE_ARC, "Arc", NODE_CLASS_GEOMETRY);
+ node_type_init(&ntype, file_ns::node_init);
+ node_type_update(&ntype, file_ns::node_update);
+ node_type_storage(&ntype,
+ "NodeGeometryCurvePrimitiveArc",
+ node_free_standard_storage,
+ node_copy_standard_storage);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.draw_buttons = file_ns::node_layout;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc
index 313473e3442..7d84ddf9917 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc
@@ -21,31 +21,49 @@
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_curve_primitive_bezier_segment_cc {
-static void geo_node_curve_primitive_bezier_segment_declare(NodeDeclarationBuilder &b)
+NODE_STORAGE_FUNCS(NodeGeometryCurvePrimitiveBezierSegment)
+
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Int>("Resolution").default_value(16).min(1).max(256).subtype(PROP_UNSIGNED);
- b.add_input<decl::Vector>("Start").default_value({-1.0f, 0.0f, 0.0f}).subtype(PROP_TRANSLATION);
- b.add_input<decl::Vector>("Start Handle")
+ b.add_input<decl::Int>(N_("Resolution"))
+ .default_value(16)
+ .min(1)
+ .max(256)
+ .subtype(PROP_UNSIGNED)
+ .description(N_("The number of evaluated points on the curve"));
+ b.add_input<decl::Vector>(N_("Start"))
+ .default_value({-1.0f, 0.0f, 0.0f})
+ .subtype(PROP_TRANSLATION)
+ .description(N_("Position of the start control point of the curve"));
+ b.add_input<decl::Vector>(N_("Start Handle"))
.default_value({-0.5f, 0.5f, 0.0f})
- .subtype(PROP_TRANSLATION);
- b.add_input<decl::Vector>("End Handle").subtype(PROP_TRANSLATION);
- b.add_input<decl::Vector>("End").default_value({1.0f, 0.0f, 0.0f}).subtype(PROP_TRANSLATION);
- b.add_output<decl::Geometry>("Curve");
+ .subtype(PROP_TRANSLATION)
+ .description(
+ N_("Position of the start handle used to define the shape of the curve. In Offset mode, "
+ "relative to Start point"));
+ b.add_input<decl::Vector>(N_("End Handle"))
+ .subtype(PROP_TRANSLATION)
+ .description(
+ N_("Position of the end handle used to define the shape of the curve. In Offset mode, "
+ "relative to End point"));
+ b.add_input<decl::Vector>(N_("End"))
+ .default_value({1.0f, 0.0f, 0.0f})
+ .subtype(PROP_TRANSLATION)
+ .description(N_("Position of the end control point of the curve"));
+ b.add_output<decl::Geometry>(N_("Curve"));
}
-static void geo_node_curve_primitive_bezier_segment_layout(uiLayout *layout,
- bContext *UNUSED(C),
- PointerRNA *ptr)
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
}
-static void geo_node_curve_primitive_bezier_segment_init(bNodeTree *UNUSED(tree), bNode *node)
+static void node_init(bNodeTree *UNUSED(tree), bNode *node)
{
- NodeGeometryCurvePrimitiveBezierSegment *data = (NodeGeometryCurvePrimitiveBezierSegment *)
- MEM_callocN(sizeof(NodeGeometryCurvePrimitiveBezierSegment), __func__);
+ NodeGeometryCurvePrimitiveBezierSegment *data =
+ MEM_cnew<NodeGeometryCurvePrimitiveBezierSegment>(__func__);
data->mode = GEO_NODE_CURVE_PRIMITIVE_BEZIER_SEGMENT_POSITION;
node->storage = data;
@@ -61,53 +79,37 @@ static std::unique_ptr<CurveEval> create_bezier_segment_curve(
{
std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>();
std::unique_ptr<BezierSpline> spline = std::make_unique<BezierSpline>();
+ spline->set_resolution(resolution);
+
+ spline->resize(2);
+ MutableSpan<float3> positions = spline->positions();
+ spline->handle_types_left().fill(BezierSpline::HandleType::Align);
+ spline->handle_types_right().fill(BezierSpline::HandleType::Align);
+ spline->radii().fill(1.0f);
+ spline->tilts().fill(0.0f);
+
+ positions.first() = start;
+ positions.last() = end;
if (mode == GEO_NODE_CURVE_PRIMITIVE_BEZIER_SEGMENT_POSITION) {
- spline->add_point(start,
- BezierSpline::HandleType::Align,
- 2.0f * start - start_handle_right,
- BezierSpline::HandleType::Align,
- start_handle_right,
- 1.0f,
- 0.0f);
- spline->add_point(end,
- BezierSpline::HandleType::Align,
- end_handle_left,
- BezierSpline::HandleType::Align,
- 2.0f * end - end_handle_left,
- 1.0f,
- 0.0f);
+ spline->set_handle_position_right(0, start_handle_right);
+ spline->set_handle_position_left(1, end_handle_left);
}
else {
- spline->add_point(start,
- BezierSpline::HandleType::Align,
- start - start_handle_right,
- BezierSpline::HandleType::Align,
- start + start_handle_right,
- 1.0f,
- 0.0f);
- spline->add_point(end,
- BezierSpline::HandleType::Align,
- end + end_handle_left,
- BezierSpline::HandleType::Align,
- end - end_handle_left,
- 1.0f,
- 0.0f);
+ spline->set_handle_position_right(0, start + start_handle_right);
+ spline->set_handle_position_left(1, end + end_handle_left);
}
- spline->set_resolution(resolution);
- spline->attributes.reallocate(spline->size());
curve->add_spline(std::move(spline));
- curve->attributes.reallocate(curve->splines().size());
+ curve->attributes.reallocate(1);
return curve;
}
-static void geo_node_curve_primitive_bezier_segment_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
- const NodeGeometryCurvePrimitiveBezierSegment *node_storage =
- (NodeGeometryCurvePrimitiveBezierSegment *)params.node().storage;
+ const NodeGeometryCurvePrimitiveBezierSegment &storage = node_storage(params.node());
const GeometryNodeCurvePrimitiveBezierSegmentMode mode =
- (const GeometryNodeCurvePrimitiveBezierSegmentMode)node_storage->mode;
+ (const GeometryNodeCurvePrimitiveBezierSegmentMode)storage.mode;
std::unique_ptr<CurveEval> curve = create_bezier_segment_curve(
params.extract_input<float3>("Start"),
@@ -119,20 +121,22 @@ static void geo_node_curve_primitive_bezier_segment_exec(GeoNodeExecParams param
params.set_output("Curve", GeometrySet::create_with_curve(curve.release()));
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_curve_primitive_bezier_segment_cc
void register_node_type_geo_curve_primitive_bezier_segment()
{
+ namespace file_ns = blender::nodes::node_geo_curve_primitive_bezier_segment_cc;
+
static bNodeType ntype;
geo_node_type_base(
- &ntype, GEO_NODE_CURVE_PRIMITIVE_BEZIER_SEGMENT, "Bezier Segment", NODE_CLASS_GEOMETRY, 0);
- node_type_init(&ntype, blender::nodes::geo_node_curve_primitive_bezier_segment_init);
+ &ntype, GEO_NODE_CURVE_PRIMITIVE_BEZIER_SEGMENT, "Bezier Segment", NODE_CLASS_GEOMETRY);
+ node_type_init(&ntype, file_ns::node_init);
node_type_storage(&ntype,
"NodeGeometryCurvePrimitiveBezierSegment",
node_free_standard_storage,
node_copy_standard_storage);
- ntype.declare = blender::nodes::geo_node_curve_primitive_bezier_segment_declare;
- ntype.draw_buttons = blender::nodes::geo_node_curve_primitive_bezier_segment_layout;
- ntype.geometry_node_execute = blender::nodes::geo_node_curve_primitive_bezier_segment_exec;
+ ntype.declare = file_ns::node_declare;
+ ntype.draw_buttons = file_ns::node_layout;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc
index f5eb83ea4fd..7b5d1a1dc80 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc
@@ -21,43 +21,64 @@
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_curve_primitive_circle_cc {
-static void geo_node_curve_primitive_circle_declare(NodeDeclarationBuilder &b)
+NODE_STORAGE_FUNCS(NodeGeometryCurvePrimitiveCircle)
+
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Int>("Resolution").default_value(32).min(3).max(512);
- b.add_input<decl::Vector>("Point 1")
+ b.add_input<decl::Int>(N_("Resolution"))
+ .default_value(32)
+ .min(3)
+ .max(512)
+ .description(N_("Number of points on the circle"));
+ b.add_input<decl::Vector>(N_("Point 1"))
.default_value({-1.0f, 0.0f, 0.0f})
- .subtype(PROP_TRANSLATION);
- b.add_input<decl::Vector>("Point 2").default_value({0.0f, 1.0f, 0.0f}).subtype(PROP_TRANSLATION);
- b.add_input<decl::Vector>("Point 3").default_value({1.0f, 0.0f, 0.0f}).subtype(PROP_TRANSLATION);
- b.add_input<decl::Float>("Radius").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE);
- b.add_output<decl::Geometry>("Curve");
- b.add_output<decl::Vector>("Center");
+ .subtype(PROP_TRANSLATION)
+ .description(
+ N_("One of the three points on the circle. The point order determines the circle's "
+ "direction"));
+ b.add_input<decl::Vector>(N_("Point 2"))
+ .default_value({0.0f, 1.0f, 0.0f})
+ .subtype(PROP_TRANSLATION)
+ .description(
+ N_("One of the three points on the circle. The point order determines the circle's "
+ "direction"));
+ b.add_input<decl::Vector>(N_("Point 3"))
+ .default_value({1.0f, 0.0f, 0.0f})
+ .subtype(PROP_TRANSLATION)
+ .description(
+ N_("One of the three points on the circle. The point order determines the circle's "
+ "direction"));
+ b.add_input<decl::Float>(N_("Radius"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .subtype(PROP_DISTANCE)
+ .description(N_("Distance of the points from the origin"));
+ b.add_output<decl::Geometry>(N_("Curve"));
+ b.add_output<decl::Vector>(N_("Center")).make_available([](bNode &node) {
+ node_storage(node).mode = GEO_NODE_CURVE_PRIMITIVE_CIRCLE_TYPE_POINTS;
+ });
}
-static void geo_node_curve_primitive_circle_layout(uiLayout *layout,
- bContext *UNUSED(C),
- PointerRNA *ptr)
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
}
-static void geo_node_curve_primitive_circle_init(bNodeTree *UNUSED(tree), bNode *node)
+static void node_init(bNodeTree *UNUSED(tree), bNode *node)
{
- NodeGeometryCurvePrimitiveCircle *data = (NodeGeometryCurvePrimitiveCircle *)MEM_callocN(
- sizeof(NodeGeometryCurvePrimitiveCircle), __func__);
+ NodeGeometryCurvePrimitiveCircle *data = MEM_cnew<NodeGeometryCurvePrimitiveCircle>(__func__);
data->mode = GEO_NODE_CURVE_PRIMITIVE_CIRCLE_TYPE_RADIUS;
node->storage = data;
}
-static void geo_node_curve_primitive_circle_update(bNodeTree *UNUSED(ntree), bNode *node)
+static void node_update(bNodeTree *ntree, bNode *node)
{
- const NodeGeometryCurvePrimitiveCircle *node_storage = (NodeGeometryCurvePrimitiveCircle *)
- node->storage;
- const GeometryNodeCurvePrimitiveCircleMode mode = (const GeometryNodeCurvePrimitiveCircleMode)
- node_storage->mode;
+ const NodeGeometryCurvePrimitiveCircle &storage = node_storage(*node);
+ const GeometryNodeCurvePrimitiveCircleMode mode = (GeometryNodeCurvePrimitiveCircleMode)
+ storage.mode;
bNodeSocket *start_socket = ((bNodeSocket *)node->inputs.first)->next;
bNodeSocket *middle_socket = start_socket->next;
@@ -66,18 +87,23 @@ static void geo_node_curve_primitive_circle_update(bNodeTree *UNUSED(ntree), bNo
bNodeSocket *center_socket = ((bNodeSocket *)node->outputs.first)->next;
- nodeSetSocketAvailability(start_socket, mode == GEO_NODE_CURVE_PRIMITIVE_CIRCLE_TYPE_POINTS);
- nodeSetSocketAvailability(middle_socket, mode == GEO_NODE_CURVE_PRIMITIVE_CIRCLE_TYPE_POINTS);
- nodeSetSocketAvailability(end_socket, mode == GEO_NODE_CURVE_PRIMITIVE_CIRCLE_TYPE_POINTS);
- nodeSetSocketAvailability(center_socket, mode == GEO_NODE_CURVE_PRIMITIVE_CIRCLE_TYPE_POINTS);
- nodeSetSocketAvailability(radius_socket, mode == GEO_NODE_CURVE_PRIMITIVE_CIRCLE_TYPE_RADIUS);
+ nodeSetSocketAvailability(
+ ntree, start_socket, mode == GEO_NODE_CURVE_PRIMITIVE_CIRCLE_TYPE_POINTS);
+ nodeSetSocketAvailability(
+ ntree, middle_socket, mode == GEO_NODE_CURVE_PRIMITIVE_CIRCLE_TYPE_POINTS);
+ nodeSetSocketAvailability(
+ ntree, end_socket, mode == GEO_NODE_CURVE_PRIMITIVE_CIRCLE_TYPE_POINTS);
+ nodeSetSocketAvailability(
+ ntree, center_socket, mode == GEO_NODE_CURVE_PRIMITIVE_CIRCLE_TYPE_POINTS);
+ nodeSetSocketAvailability(
+ ntree, radius_socket, mode == GEO_NODE_CURVE_PRIMITIVE_CIRCLE_TYPE_RADIUS);
}
static bool colinear_f3_f3_f3(const float3 p1, const float3 p2, const float3 p3)
{
- const float3 a = (p2 - p1).normalized();
- const float3 b = (p3 - p1).normalized();
- return (a == b || a == b * -1.0f);
+ const float3 a = math::normalize(p2 - p1);
+ const float3 b = math::normalize(p3 - p1);
+ return (ELEM(a, b, b * -1.0f));
}
static std::unique_ptr<CurveEval> create_point_circle_curve(
@@ -96,18 +122,18 @@ static std::unique_ptr<CurveEval> create_point_circle_curve(
float3 center;
/* Midpoints of `P1->P2` and `P2->P3`. */
- const float3 q1 = float3::interpolate(p1, p2, 0.5f);
- const float3 q2 = float3::interpolate(p2, p3, 0.5f);
+ const float3 q1 = math::interpolate(p1, p2, 0.5f);
+ const float3 q2 = math::interpolate(p2, p3, 0.5f);
/* Normal Vectors of `P1->P2` and `P2->P3` */
- const float3 v1 = (p2 - p1).normalized();
- const float3 v2 = (p3 - p2).normalized();
+ const float3 v1 = math::normalize(p2 - p1);
+ const float3 v2 = math::normalize(p3 - p2);
/* Normal of plane of main 2 segments P1->P2 and `P2->P3`. */
- const float3 v3 = float3::cross(v1, v2).normalized();
+ const float3 v3 = math::normalize(math::cross(v1, v2));
/* Normal of plane of first perpendicular bisector and `P1->P2`. */
- const float3 v4 = float3::cross(v3, v1).normalized();
+ const float3 v4 = math::normalize(math::cross(v3, v1));
/* Determine Center-point from the intersection of 3 planes. */
float plane_1[4], plane_2[4], plane_3[4];
@@ -122,7 +148,7 @@ static std::unique_ptr<CurveEval> create_point_circle_curve(
}
/* Get the radius from the center-point to p1. */
- const float r = float3::distance(p1, center);
+ const float r = math::distance(p1, center);
const float theta_step = ((2 * M_PI) / (float)resolution);
for (const int i : IndexRange(resolution)) {
@@ -132,7 +158,7 @@ static std::unique_ptr<CurveEval> create_point_circle_curve(
*/
const float theta = theta_step * i;
- positions[i] = center + r * cos(theta) * v1 + r * sin(theta) * v4;
+ positions[i] = center + r * sin(theta) * v1 + r * cos(theta) * v4;
}
spline->radii().fill(1.0f);
@@ -169,13 +195,11 @@ static std::unique_ptr<CurveEval> create_radius_circle_curve(const int resolutio
return curve;
}
-static void geo_node_curve_primitive_circle_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
- const NodeGeometryCurvePrimitiveCircle *node_storage =
- (NodeGeometryCurvePrimitiveCircle *)params.node().storage;
-
+ const NodeGeometryCurvePrimitiveCircle &storage = node_storage(params.node());
const GeometryNodeCurvePrimitiveCircleMode mode = (GeometryNodeCurvePrimitiveCircleMode)
- node_storage->mode;
+ storage.mode;
std::unique_ptr<CurveEval> curve;
if (mode == GEO_NODE_CURVE_PRIMITIVE_CIRCLE_TYPE_POINTS) {
@@ -196,26 +220,27 @@ static void geo_node_curve_primitive_circle_exec(GeoNodeExecParams params)
params.set_output("Curve", GeometrySet::create_with_curve(curve.release()));
}
else {
- params.set_output("Curve", GeometrySet());
+ params.set_default_remaining_outputs();
}
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_curve_primitive_circle_cc
void register_node_type_geo_curve_primitive_circle()
{
+ namespace file_ns = blender::nodes::node_geo_curve_primitive_circle_cc;
+
static bNodeType ntype;
- geo_node_type_base(
- &ntype, GEO_NODE_CURVE_PRIMITIVE_CIRCLE, "Curve Circle", NODE_CLASS_GEOMETRY, 0);
+ geo_node_type_base(&ntype, GEO_NODE_CURVE_PRIMITIVE_CIRCLE, "Curve Circle", NODE_CLASS_GEOMETRY);
- node_type_init(&ntype, blender::nodes::geo_node_curve_primitive_circle_init);
- node_type_update(&ntype, blender::nodes::geo_node_curve_primitive_circle_update);
+ node_type_init(&ntype, file_ns::node_init);
+ node_type_update(&ntype, file_ns::node_update);
node_type_storage(&ntype,
"NodeGeometryCurvePrimitiveCircle",
node_free_standard_storage,
node_copy_standard_storage);
- ntype.declare = blender::nodes::geo_node_curve_primitive_circle_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_curve_primitive_circle_exec;
- ntype.draw_buttons = blender::nodes::geo_node_curve_primitive_circle_layout;
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.draw_buttons = file_ns::node_layout;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc
index a3d2ada612f..d35fa0a2fdc 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc
@@ -21,48 +21,57 @@
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_curve_primitive_line_cc {
-static void geo_node_curve_primitive_line_declare(NodeDeclarationBuilder &b)
+NODE_STORAGE_FUNCS(NodeGeometryCurvePrimitiveLine)
+
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Vector>("Start").subtype(PROP_TRANSLATION);
- b.add_input<decl::Vector>("End").default_value({0.0f, 0.0f, 1.0f}).subtype(PROP_TRANSLATION);
- b.add_input<decl::Vector>("Direction").default_value({0.0f, 0.0f, 1.0f});
- b.add_input<decl::Float>("Length").default_value(1.0f).subtype(PROP_DISTANCE);
- b.add_output<decl::Geometry>("Curve");
+ b.add_input<decl::Vector>(N_("Start"))
+ .subtype(PROP_TRANSLATION)
+ .description(N_("Position of the first control point"));
+ b.add_input<decl::Vector>(N_("End"))
+ .default_value({0.0f, 0.0f, 1.0f})
+ .subtype(PROP_TRANSLATION)
+ .description(N_("Position of the second control point"));
+ b.add_input<decl::Vector>(N_("Direction"))
+ .default_value({0.0f, 0.0f, 1.0f})
+ .description(
+ N_("Direction the line is going in. The length of this vector does not matter"));
+ b.add_input<decl::Float>(N_("Length"))
+ .default_value(1.0f)
+ .subtype(PROP_DISTANCE)
+ .description(N_("Distance between the two points"));
+ b.add_output<decl::Geometry>(N_("Curve"));
}
-static void geo_node_curve_primitive_line_layout(uiLayout *layout,
- bContext *UNUSED(C),
- PointerRNA *ptr)
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
}
-static void geo_node_curve_primitive_line_init(bNodeTree *UNUSED(tree), bNode *node)
+static void node_init(bNodeTree *UNUSED(tree), bNode *node)
{
- NodeGeometryCurvePrimitiveLine *data = (NodeGeometryCurvePrimitiveLine *)MEM_callocN(
- sizeof(NodeGeometryCurvePrimitiveLine), __func__);
+ NodeGeometryCurvePrimitiveLine *data = MEM_cnew<NodeGeometryCurvePrimitiveLine>(__func__);
data->mode = GEO_NODE_CURVE_PRIMITIVE_LINE_MODE_POINTS;
node->storage = data;
}
-static void geo_node_curve_primitive_line_update(bNodeTree *UNUSED(ntree), bNode *node)
+static void node_update(bNodeTree *ntree, bNode *node)
{
- const NodeGeometryCurvePrimitiveLine *node_storage = (NodeGeometryCurvePrimitiveLine *)
- node->storage;
- const GeometryNodeCurvePrimitiveLineMode mode = (const GeometryNodeCurvePrimitiveLineMode)
- node_storage->mode;
+ const NodeGeometryCurvePrimitiveLine &storage = node_storage(*node);
+ const GeometryNodeCurvePrimitiveLineMode mode = (GeometryNodeCurvePrimitiveLineMode)storage.mode;
bNodeSocket *p2_socket = ((bNodeSocket *)node->inputs.first)->next;
bNodeSocket *direction_socket = p2_socket->next;
bNodeSocket *length_socket = direction_socket->next;
- nodeSetSocketAvailability(p2_socket, mode == GEO_NODE_CURVE_PRIMITIVE_LINE_MODE_POINTS);
- nodeSetSocketAvailability(direction_socket,
- mode == GEO_NODE_CURVE_PRIMITIVE_LINE_MODE_DIRECTION);
- nodeSetSocketAvailability(length_socket, mode == GEO_NODE_CURVE_PRIMITIVE_LINE_MODE_DIRECTION);
+ nodeSetSocketAvailability(ntree, p2_socket, mode == GEO_NODE_CURVE_PRIMITIVE_LINE_MODE_POINTS);
+ nodeSetSocketAvailability(
+ ntree, direction_socket, mode == GEO_NODE_CURVE_PRIMITIVE_LINE_MODE_DIRECTION);
+ nodeSetSocketAvailability(
+ ntree, length_socket, mode == GEO_NODE_CURVE_PRIMITIVE_LINE_MODE_DIRECTION);
}
static std::unique_ptr<CurveEval> create_point_line_curve(const float3 start, const float3 end)
@@ -91,7 +100,7 @@ static std::unique_ptr<CurveEval> create_direction_line_curve(const float3 start
spline->resize(2);
MutableSpan<float3> positions = spline->positions();
positions[0] = start;
- positions[1] = direction.normalized() * length + start;
+ positions[1] = math::normalize(direction) * length + start;
spline->radii().fill(1.0f);
spline->tilts().fill(0.0f);
@@ -100,13 +109,10 @@ static std::unique_ptr<CurveEval> create_direction_line_curve(const float3 start
return curve;
}
-static void geo_node_curve_primitive_line_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
-
- const NodeGeometryCurvePrimitiveLine *node_storage =
- (NodeGeometryCurvePrimitiveLine *)params.node().storage;
-
- GeometryNodeCurvePrimitiveLineMode mode = (GeometryNodeCurvePrimitiveLineMode)node_storage->mode;
+ const NodeGeometryCurvePrimitiveLine &storage = node_storage(params.node());
+ const GeometryNodeCurvePrimitiveLineMode mode = (GeometryNodeCurvePrimitiveLineMode)storage.mode;
std::unique_ptr<CurveEval> curve;
if (mode == GEO_NODE_CURVE_PRIMITIVE_LINE_MODE_POINTS) {
@@ -122,20 +128,22 @@ static void geo_node_curve_primitive_line_exec(GeoNodeExecParams params)
params.set_output("Curve", GeometrySet::create_with_curve(curve.release()));
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_curve_primitive_line_cc
void register_node_type_geo_curve_primitive_line()
{
+ namespace file_ns = blender::nodes::node_geo_curve_primitive_line_cc;
+
static bNodeType ntype;
- geo_node_type_base(&ntype, GEO_NODE_CURVE_PRIMITIVE_LINE, "Curve Line", NODE_CLASS_GEOMETRY, 0);
- node_type_init(&ntype, blender::nodes::geo_node_curve_primitive_line_init);
- node_type_update(&ntype, blender::nodes::geo_node_curve_primitive_line_update);
+ geo_node_type_base(&ntype, GEO_NODE_CURVE_PRIMITIVE_LINE, "Curve Line", NODE_CLASS_GEOMETRY);
+ node_type_init(&ntype, file_ns::node_init);
+ node_type_update(&ntype, file_ns::node_update);
node_type_storage(&ntype,
"NodeGeometryCurvePrimitiveLine",
node_free_standard_storage,
node_copy_standard_storage);
- ntype.declare = blender::nodes::geo_node_curve_primitive_line_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_curve_primitive_line_exec;
- ntype.draw_buttons = blender::nodes::geo_node_curve_primitive_line_layout;
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.draw_buttons = file_ns::node_layout;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadratic_bezier.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadratic_bezier.cc
index a54fd971ac4..885d92a111b 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadratic_bezier.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadratic_bezier.cc
@@ -17,15 +17,29 @@
#include "BKE_spline.hh"
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_curve_primitive_quadratic_bezier_cc {
-static void geo_node_curve_primitive_quadratic_bezier_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Int>("Resolution").default_value(16).min(3).max(256).subtype(PROP_UNSIGNED);
- b.add_input<decl::Vector>("Start").default_value({-1.0f, 0.0f, 0.0f}).subtype(PROP_TRANSLATION);
- b.add_input<decl::Vector>("Middle").default_value({0.0f, 2.0f, 0.0f}).subtype(PROP_TRANSLATION);
- b.add_input<decl::Vector>("End").default_value({1.0f, 0.0f, 0.0f}).subtype(PROP_TRANSLATION);
- b.add_output<decl::Geometry>("Curve");
+ b.add_input<decl::Int>(N_("Resolution"))
+ .default_value(16)
+ .min(3)
+ .max(256)
+ .subtype(PROP_UNSIGNED)
+ .description(N_("The number of edges on the curve"));
+ b.add_input<decl::Vector>(N_("Start"))
+ .default_value({-1.0f, 0.0f, 0.0f})
+ .subtype(PROP_TRANSLATION)
+ .description(N_("Position of the first control point"));
+ b.add_input<decl::Vector>(N_("Middle"))
+ .default_value({0.0f, 2.0f, 0.0f})
+ .subtype(PROP_TRANSLATION)
+ .description(N_("Position of the middle control point"));
+ b.add_input<decl::Vector>(N_("End"))
+ .default_value({1.0f, 0.0f, 0.0f})
+ .subtype(PROP_TRANSLATION)
+ .description(N_("Position of the last control point"));
+ b.add_output<decl::Geometry>(N_("Curve"));
}
static std::unique_ptr<CurveEval> create_quadratic_bezier_curve(const float3 p1,
@@ -36,21 +50,25 @@ static std::unique_ptr<CurveEval> create_quadratic_bezier_curve(const float3 p1,
std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>();
std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>();
+ spline->resize(resolution + 1);
+ MutableSpan<float3> positions = spline->positions();
+ spline->radii().fill(1.0f);
+ spline->tilts().fill(0.0f);
+
const float step = 1.0f / resolution;
- for (int i : IndexRange(resolution + 1)) {
+ for (const int i : IndexRange(resolution + 1)) {
const float factor = step * i;
- const float3 q1 = float3::interpolate(p1, p2, factor);
- const float3 q2 = float3::interpolate(p2, p3, factor);
- const float3 out = float3::interpolate(q1, q2, factor);
- spline->add_point(out, 1.0f, 0.0f);
+ const float3 q1 = math::interpolate(p1, p2, factor);
+ const float3 q2 = math::interpolate(p2, p3, factor);
+ positions[i] = math::interpolate(q1, q2, factor);
}
- spline->attributes.reallocate(spline->size());
+
curve->add_spline(std::move(spline));
curve->attributes.reallocate(curve->splines().size());
return curve;
}
-static void geo_node_curve_primitive_quadratic_bezier_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
std::unique_ptr<CurveEval> curve = create_quadratic_bezier_curve(
params.extract_input<float3>("Start"),
@@ -60,17 +78,16 @@ static void geo_node_curve_primitive_quadratic_bezier_exec(GeoNodeExecParams par
params.set_output("Curve", GeometrySet::create_with_curve(curve.release()));
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_curve_primitive_quadratic_bezier_cc
void register_node_type_geo_curve_primitive_quadratic_bezier()
{
+ namespace file_ns = blender::nodes::node_geo_curve_primitive_quadratic_bezier_cc;
+
static bNodeType ntype;
- geo_node_type_base(&ntype,
- GEO_NODE_CURVE_PRIMITIVE_QUADRATIC_BEZIER,
- "Quadratic Bezier",
- NODE_CLASS_GEOMETRY,
- 0);
- ntype.declare = blender::nodes::geo_node_curve_primitive_quadratic_bezier_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_curve_primitive_quadratic_bezier_exec;
+ geo_node_type_base(
+ &ntype, GEO_NODE_CURVE_PRIMITIVE_QUADRATIC_BEZIER, "Quadratic Bezier", NODE_CLASS_GEOMETRY);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc
index 07ddaa8f61e..8ec42cb1c45 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc
@@ -17,46 +17,88 @@
#include "BKE_spline.hh"
#include "UI_interface.h"
#include "UI_resources.h"
+
+#include "NOD_socket_search_link.hh"
+
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_curve_primitive_quadrilateral_cc {
-static void geo_node_curve_primitive_quadrilateral_declare(NodeDeclarationBuilder &b)
+NODE_STORAGE_FUNCS(NodeGeometryCurvePrimitiveQuad)
+
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>("Width").default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE);
- b.add_input<decl::Float>("Height").default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE);
- b.add_input<decl::Float>("Bottom Width").default_value(4.0f).min(0.0f).subtype(PROP_DISTANCE);
- b.add_input<decl::Float>("Top Width").default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE);
- b.add_input<decl::Float>("Offset").default_value(1.0f).subtype(PROP_DISTANCE);
- b.add_input<decl::Float>("Bottom Height").default_value(3.0f).min(0.0f).subtype(PROP_DISTANCE);
- b.add_input<decl::Float>("Top Height").default_value(1.0f).subtype(PROP_DISTANCE);
- b.add_input<decl::Vector>("Point 1").default_value({-1.0f, -1.0f, 0.0f}).subtype(PROP_DISTANCE);
- b.add_input<decl::Vector>("Point 2").default_value({1.0f, -1.0f, 0.0f}).subtype(PROP_DISTANCE);
- b.add_input<decl::Vector>("Point 3").default_value({1.0f, 1.0f, 0.0f}).subtype(PROP_DISTANCE);
- b.add_input<decl::Vector>("Point 4").default_value({-1.0f, 1.0f, 0.0f}).subtype(PROP_DISTANCE);
- b.add_output<decl::Geometry>("Curve");
+ b.add_input<decl::Float>(N_("Width"))
+ .default_value(2.0f)
+ .min(0.0f)
+ .subtype(PROP_DISTANCE)
+ .description(N_("The X axis size of the shape"));
+ b.add_input<decl::Float>(N_("Height"))
+ .default_value(2.0f)
+ .min(0.0f)
+ .subtype(PROP_DISTANCE)
+ .description(N_("The Y axis size of the shape"));
+ b.add_input<decl::Float>(N_("Bottom Width"))
+ .default_value(4.0f)
+ .min(0.0f)
+ .subtype(PROP_DISTANCE)
+ .description(N_("The X axis size of the shape"));
+ b.add_input<decl::Float>(N_("Top Width"))
+ .default_value(2.0f)
+ .min(0.0f)
+ .subtype(PROP_DISTANCE)
+ .description(N_("The X axis size of the shape"));
+ b.add_input<decl::Float>(N_("Offset"))
+ .default_value(1.0f)
+ .subtype(PROP_DISTANCE)
+ .description(
+ N_("For Parallelogram, the relative X difference between the top and bottom edges. For "
+ "Trapezoid, the amount to move the top edge in the positive X axis"));
+ b.add_input<decl::Float>(N_("Bottom Height"))
+ .default_value(3.0f)
+ .min(0.0f)
+ .subtype(PROP_DISTANCE)
+ .description(N_("The distance between the bottom point and the X axis"));
+ b.add_input<decl::Float>(N_("Top Height"))
+ .default_value(1.0f)
+ .subtype(PROP_DISTANCE)
+ .description(N_("The distance between the top point and the X axis"));
+ b.add_input<decl::Vector>(N_("Point 1"))
+ .default_value({-1.0f, -1.0f, 0.0f})
+ .subtype(PROP_DISTANCE)
+ .description(N_("The exact location of the point to use"));
+ b.add_input<decl::Vector>(N_("Point 2"))
+ .default_value({1.0f, -1.0f, 0.0f})
+ .subtype(PROP_DISTANCE)
+ .description(N_("The exact location of the point to use"));
+ b.add_input<decl::Vector>(N_("Point 3"))
+ .default_value({1.0f, 1.0f, 0.0f})
+ .subtype(PROP_DISTANCE)
+ .description(N_("The exact location of the point to use"));
+ b.add_input<decl::Vector>(N_("Point 4"))
+ .default_value({-1.0f, 1.0f, 0.0f})
+ .subtype(PROP_DISTANCE)
+ .description(N_("The exact location of the point to use"));
+ b.add_output<decl::Geometry>(N_("Curve"));
}
-static void geo_node_curve_primitive_quadrilateral_layout(uiLayout *layout,
- bContext *UNUSED(C),
- PointerRNA *ptr)
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiItemR(layout, ptr, "mode", 0, "", ICON_NONE);
}
-static void geo_node_curve_primitive_quadrilateral_init(bNodeTree *UNUSED(tree), bNode *node)
+static void node_init(bNodeTree *UNUSED(tree), bNode *node)
{
- NodeGeometryCurvePrimitiveQuad *data = (NodeGeometryCurvePrimitiveQuad *)MEM_callocN(
- sizeof(NodeGeometryCurvePrimitiveQuad), __func__);
+ NodeGeometryCurvePrimitiveQuad *data = MEM_cnew<NodeGeometryCurvePrimitiveQuad>(__func__);
data->mode = GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_RECTANGLE;
node->storage = data;
}
-static void geo_node_curve_primitive_quadrilateral_update(bNodeTree *UNUSED(ntree), bNode *node)
+static void node_update(bNodeTree *ntree, bNode *node)
{
- NodeGeometryCurvePrimitiveQuad &node_storage = *(NodeGeometryCurvePrimitiveQuad *)node->storage;
+ const NodeGeometryCurvePrimitiveQuad &storage = node_storage(*node);
GeometryNodeCurvePrimitiveQuadMode mode = static_cast<GeometryNodeCurvePrimitiveQuadMode>(
- node_storage.mode);
+ storage.mode);
bNodeSocket *width = ((bNodeSocket *)node->inputs.first);
bNodeSocket *height = width->next;
@@ -70,35 +112,61 @@ static void geo_node_curve_primitive_quadrilateral_update(bNodeTree *UNUSED(ntre
bNodeSocket *p3 = p2->next;
bNodeSocket *p4 = p3->next;
- LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
- nodeSetSocketAvailability(sock, false);
- }
+ Vector<bNodeSocket *> available_sockets;
if (mode == GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_RECTANGLE) {
- nodeSetSocketAvailability(width, true);
- nodeSetSocketAvailability(height, true);
+ available_sockets.extend({width, height});
}
else if (mode == GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_PARALLELOGRAM) {
- nodeSetSocketAvailability(width, true);
- nodeSetSocketAvailability(height, true);
- nodeSetSocketAvailability(offset, true);
+ available_sockets.extend({width, height, offset});
}
else if (mode == GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_TRAPEZOID) {
- nodeSetSocketAvailability(bottom, true);
- nodeSetSocketAvailability(top, true);
- nodeSetSocketAvailability(offset, true);
- nodeSetSocketAvailability(height, true);
+ available_sockets.extend({bottom, top, offset, height});
}
else if (mode == GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_KITE) {
- nodeSetSocketAvailability(width, true);
- nodeSetSocketAvailability(bottom_height, true);
- nodeSetSocketAvailability(top_height, true);
+ available_sockets.extend({width, bottom_height, top_height});
}
else if (mode == GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_POINTS) {
- nodeSetSocketAvailability(p1, true);
- nodeSetSocketAvailability(p2, true);
- nodeSetSocketAvailability(p3, true);
- nodeSetSocketAvailability(p4, true);
+ available_sockets.extend({p1, p2, p3, p4});
+ }
+
+ LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
+ nodeSetSocketAvailability(ntree, sock, available_sockets.contains(sock));
+ }
+}
+
+class SocketSearchOp {
+ public:
+ std::string socket_name;
+ GeometryNodeCurvePrimitiveQuadMode quad_mode;
+ void operator()(LinkSearchOpParams &params)
+ {
+ bNode &node = params.add_node("GeometryNodeCurvePrimitiveQuadrilateral");
+ node_storage(node).mode = quad_mode;
+ params.update_and_connect_available_socket(node, socket_name);
+ }
+};
+
+static void node_gather_link_searches(GatherLinkSearchOpParams &params)
+{
+ const NodeDeclaration &declaration = *params.node_type().fixed_declaration;
+ if (params.in_out() == SOCK_OUT) {
+ search_link_ops_for_declarations(params, declaration.outputs());
+ }
+ else if (params.node_tree().typeinfo->validate_link(
+ static_cast<eNodeSocketDatatype>(params.other_socket().type), SOCK_FLOAT)) {
+ params.add_item(IFACE_("Width"),
+ SocketSearchOp{"Width", GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_RECTANGLE});
+ params.add_item(IFACE_("Height"),
+ SocketSearchOp{"Height", GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_RECTANGLE});
+ params.add_item(IFACE_("Bottom Width"),
+ SocketSearchOp{"Bottom Width", GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_TRAPEZOID});
+ params.add_item(IFACE_("Top Width"),
+ SocketSearchOp{"Top Width", GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_TRAPEZOID});
+ params.add_item(IFACE_("Offset"),
+ SocketSearchOp{"Offset", GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_PARALLELOGRAM});
+ params.add_item(IFACE_("Point 1"),
+ SocketSearchOp{"Point 1", GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_POINTS});
}
}
@@ -157,12 +225,10 @@ static void create_kite_curve(MutableSpan<float3> positions,
positions[3] = float3(-width / 2.0f, 0, 0);
}
-static void geo_node_curve_primitive_quadrilateral_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
- const NodeGeometryCurvePrimitiveQuad &node_storage =
- *(NodeGeometryCurvePrimitiveQuad *)(params.node()).storage;
- const GeometryNodeCurvePrimitiveQuadMode mode = static_cast<GeometryNodeCurvePrimitiveQuadMode>(
- node_storage.mode);
+ const NodeGeometryCurvePrimitiveQuad &storage = node_storage(params.node());
+ const GeometryNodeCurvePrimitiveQuadMode mode = (GeometryNodeCurvePrimitiveQuadMode)storage.mode;
std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>();
std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>();
@@ -206,7 +272,7 @@ static void geo_node_curve_primitive_quadrilateral_exec(GeoNodeExecParams params
params.extract_input<float3>("Point 4"));
break;
default:
- params.set_output("Curve", GeometrySet());
+ params.set_default_remaining_outputs();
return;
}
@@ -215,21 +281,24 @@ static void geo_node_curve_primitive_quadrilateral_exec(GeoNodeExecParams params
params.set_output("Curve", GeometrySet::create_with_curve(curve.release()));
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_curve_primitive_quadrilateral_cc
void register_node_type_geo_curve_primitive_quadrilateral()
{
+ namespace file_ns = blender::nodes::node_geo_curve_primitive_quadrilateral_cc;
+
static bNodeType ntype;
geo_node_type_base(
- &ntype, GEO_NODE_CURVE_PRIMITIVE_QUADRILATERAL, "Quadrilateral", NODE_CLASS_GEOMETRY, 0);
- ntype.declare = blender::nodes::geo_node_curve_primitive_quadrilateral_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_curve_primitive_quadrilateral_exec;
- ntype.draw_buttons = blender::nodes::geo_node_curve_primitive_quadrilateral_layout;
- node_type_update(&ntype, blender::nodes::geo_node_curve_primitive_quadrilateral_update);
- node_type_init(&ntype, blender::nodes::geo_node_curve_primitive_quadrilateral_init);
+ &ntype, GEO_NODE_CURVE_PRIMITIVE_QUADRILATERAL, "Quadrilateral", NODE_CLASS_GEOMETRY);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.draw_buttons = file_ns::node_layout;
+ node_type_update(&ntype, file_ns::node_update);
+ node_type_init(&ntype, file_ns::node_init);
node_type_storage(&ntype,
"NodeGeometryCurvePrimitiveQuad",
node_free_standard_storage,
node_copy_standard_storage);
+ ntype.gather_link_search_ops = file_ns::node_gather_link_searches;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc
index 0803d43e5c3..6aba65b5638 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc
@@ -18,17 +18,35 @@
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_curve_primitive_spiral_cc {
-static void geo_node_curve_primitive_spiral_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Int>("Resolution").default_value(32).min(1).max(1024).subtype(PROP_UNSIGNED);
- b.add_input<decl::Float>("Rotations").default_value(2.0f).min(0.0f);
- b.add_input<decl::Float>("Start Radius").default_value(1.0f).subtype(PROP_DISTANCE);
- b.add_input<decl::Float>("End Radius").default_value(2.0f).subtype(PROP_DISTANCE);
- b.add_input<decl::Float>("Height").default_value(2.0f).subtype(PROP_DISTANCE);
- b.add_input<decl::Bool>("Reverse");
- b.add_output<decl::Geometry>("Curve");
+ b.add_input<decl::Int>(N_("Resolution"))
+ .default_value(32)
+ .min(1)
+ .max(1024)
+ .subtype(PROP_UNSIGNED)
+ .description(N_("Number of points in one rotation of the spiral"));
+ b.add_input<decl::Float>(N_("Rotations"))
+ .default_value(2.0f)
+ .min(0.0f)
+ .description(N_("Number of times the spiral makes a full rotation"));
+ b.add_input<decl::Float>(N_("Start Radius"))
+ .default_value(1.0f)
+ .subtype(PROP_DISTANCE)
+ .description(N_("Horizontal Distance from the Z axis at the start of the spiral"));
+ b.add_input<decl::Float>(N_("End Radius"))
+ .default_value(2.0f)
+ .subtype(PROP_DISTANCE)
+ .description(N_("Horizontal Distance from the Z axis at the end of the spiral"));
+ b.add_input<decl::Float>(N_("Height"))
+ .default_value(2.0f)
+ .subtype(PROP_DISTANCE)
+ .description(N_("The height perpendicular to the base of the spiral"));
+ b.add_input<decl::Bool>(N_("Reverse"))
+ .description(N_("Switch the direction from clockwise to counterclockwise"));
+ b.add_output<decl::Geometry>(N_("Curve"));
}
static std::unique_ptr<CurveEval> create_spiral_curve(const float rotations,
@@ -43,39 +61,35 @@ static std::unique_ptr<CurveEval> create_spiral_curve(const float rotations,
const int totalpoints = std::max(int(resolution * rotations), 1);
const float delta_radius = (end_radius - start_radius) / (float)totalpoints;
- float radius = start_radius;
const float delta_height = height / (float)totalpoints;
- const float delta_theta = (M_PI * 2 * rotations) / (float)totalpoints;
- float theta = 0.0f;
+ const float delta_theta = (M_PI * 2 * rotations) / (float)totalpoints *
+ (direction ? 1.0f : -1.0f);
+
+ spline->resize(totalpoints + 1);
+ MutableSpan<float3> positions = spline->positions();
+ spline->radii().fill(1.0f);
+ spline->tilts().fill(0.0f);
for (const int i : IndexRange(totalpoints + 1)) {
+ const float theta = i * delta_theta;
+ const float radius = start_radius + i * delta_radius;
const float x = radius * cos(theta);
const float y = radius * sin(theta);
const float z = delta_height * i;
- spline->add_point(float3(x, y, z), 1.0f, 0.0f);
-
- radius += delta_radius;
-
- if (direction) {
- theta += delta_theta;
- }
- else {
- theta -= delta_theta;
- }
+ positions[i] = {x, y, z};
}
- spline->attributes.reallocate(spline->size());
curve->add_spline(std::move(spline));
curve->attributes.reallocate(curve->splines().size());
return curve;
}
-static void geo_node_curve_primitive_spiral_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
const float rotations = std::max(params.extract_input<float>("Rotations"), 0.0f);
if (rotations == 0.0f) {
- params.set_output("Curve", GeometrySet());
+ params.set_default_remaining_outputs();
return;
}
@@ -89,14 +103,16 @@ static void geo_node_curve_primitive_spiral_exec(GeoNodeExecParams params)
params.set_output("Curve", GeometrySet::create_with_curve(curve.release()));
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_curve_primitive_spiral_cc
void register_node_type_geo_curve_primitive_spiral()
{
+ namespace file_ns = blender::nodes::node_geo_curve_primitive_spiral_cc;
+
static bNodeType ntype;
- geo_node_type_base(&ntype, GEO_NODE_CURVE_PRIMITIVE_SPIRAL, "Spiral", NODE_CLASS_GEOMETRY, 0);
- ntype.declare = blender::nodes::geo_node_curve_primitive_spiral_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_curve_primitive_spiral_exec;
+ geo_node_type_base(&ntype, GEO_NODE_CURVE_PRIMITIVE_SPIRAL, "Spiral", NODE_CLASS_GEOMETRY);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc
index 6261146562d..14517a79037 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc
@@ -18,15 +18,33 @@
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_curve_primitive_star_cc {
-static void geo_node_curve_primitive_star_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Int>("Points").default_value(8).min(3).max(256).subtype(PROP_UNSIGNED);
- b.add_input<decl::Float>("Inner Radius").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE);
- b.add_input<decl::Float>("Outer Radius").default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE);
- b.add_input<decl::Float>("Twist").subtype(PROP_ANGLE);
- b.add_output<decl::Geometry>("Curve");
+ b.add_input<decl::Int>(N_("Points"))
+ .default_value(8)
+ .min(3)
+ .max(256)
+ .subtype(PROP_UNSIGNED)
+ .description(N_("Number of points on each of the circles"));
+ b.add_input<decl::Float>(N_("Inner Radius"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .subtype(PROP_DISTANCE)
+ .description(N_("Radius of the inner circle; can be larger than outer radius"));
+ b.add_input<decl::Float>(N_("Outer Radius"))
+ .default_value(2.0f)
+ .min(0.0f)
+ .subtype(PROP_DISTANCE)
+ .description(N_("Radius of the outer circle; can be smaller than inner radius"));
+ b.add_input<decl::Float>(N_("Twist"))
+ .subtype(PROP_ANGLE)
+ .description(N_("The counterclockwise rotation of the inner set of points"));
+ b.add_output<decl::Geometry>(N_("Curve"));
+ b.add_output<decl::Bool>(N_("Outer Points"))
+ .field_source()
+ .description(N_("An attribute field with a selection of the outer points"));
}
static std::unique_ptr<CurveEval> create_star_curve(const float inner_radius,
@@ -36,41 +54,69 @@ static std::unique_ptr<CurveEval> create_star_curve(const float inner_radius,
{
std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>();
std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>();
+ spline->set_cyclic(true);
+
+ spline->resize(points * 2);
+ MutableSpan<float3> positions = spline->positions();
+ spline->radii().fill(1.0f);
+ spline->tilts().fill(0.0f);
const float theta_step = (2.0f * M_PI) / float(points);
- for (int i : IndexRange(points)) {
+ for (const int i : IndexRange(points)) {
const float x = outer_radius * cos(theta_step * i);
const float y = outer_radius * sin(theta_step * i);
- spline->add_point(float3(x, y, 0.0f), 1.0f, 0.0f);
+ positions[i * 2] = {x, y, 0.0f};
const float inner_x = inner_radius * cos(theta_step * i + theta_step * 0.5f + twist);
const float inner_y = inner_radius * sin(theta_step * i + theta_step * 0.5f + twist);
- spline->add_point(float3(inner_x, inner_y, 0.0f), 1.0f, 0.0f);
+ positions[i * 2 + 1] = {inner_x, inner_y, 0.0f};
}
- spline->set_cyclic(true);
- spline->attributes.reallocate(spline->size());
+
curve->add_spline(std::move(spline));
curve->attributes.reallocate(curve->splines().size());
+
return curve;
}
-static void geo_node_curve_primitive_star_exec(GeoNodeExecParams params)
+static void create_selection_output(CurveComponent &component,
+ StrongAnonymousAttributeID &r_attribute)
+{
+ OutputAttribute_Typed<bool> attribute = component.attribute_try_get_for_output_only<bool>(
+ r_attribute.get(), ATTR_DOMAIN_POINT);
+ MutableSpan<bool> selection = attribute.as_span();
+ for (int i : selection.index_range()) {
+ selection[i] = i % 2 == 0;
+ }
+ attribute.save();
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
{
std::unique_ptr<CurveEval> curve = create_star_curve(
std::max(params.extract_input<float>("Inner Radius"), 0.0f),
std::max(params.extract_input<float>("Outer Radius"), 0.0f),
params.extract_input<float>("Twist"),
std::max(params.extract_input<int>("Points"), 3));
- params.set_output("Curve", GeometrySet::create_with_curve(curve.release()));
-}
+ GeometrySet output = GeometrySet::create_with_curve(curve.release());
-} // namespace blender::nodes
+ if (params.output_is_required("Outer Points")) {
+ StrongAnonymousAttributeID attribute_output("Outer Points");
+ create_selection_output(output.get_component_for_write<CurveComponent>(), attribute_output);
+ params.set_output("Outer Points",
+ AnonymousAttributeFieldInput::Create<bool>(
+ std::move(attribute_output), params.attribute_producer_name()));
+ }
+ params.set_output("Curve", std::move(output));
+}
+} // namespace blender::nodes::node_geo_curve_primitive_star_cc
void register_node_type_geo_curve_primitive_star()
{
+ namespace file_ns = blender::nodes::node_geo_curve_primitive_star_cc;
+
static bNodeType ntype;
- geo_node_type_base(&ntype, GEO_NODE_CURVE_PRIMITIVE_STAR, "Star", NODE_CLASS_GEOMETRY, 0);
- ntype.declare = blender::nodes::geo_node_curve_primitive_star_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_curve_primitive_star_exec;
+ geo_node_type_base(&ntype, GEO_NODE_CURVE_PRIMITIVE_STAR, "Star", NODE_CLASS_GEOMETRY);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc
index b8f62460069..8494b4868e8 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc
@@ -26,50 +26,53 @@
#include "node_geometry_util.hh"
-using blender::fn::GVArray_For_GSpan;
-using blender::fn::GVArray_For_Span;
-using blender::fn::GVArray_Typed;
+namespace blender::nodes::node_geo_curve_resample_cc {
-namespace blender::nodes {
+NODE_STORAGE_FUNCS(NodeGeometryCurveResample)
-static void geo_node_curve_resample_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Int>("Count").default_value(10).min(1).max(100000);
- b.add_input<decl::Float>("Length").default_value(0.1f).min(0.001f).subtype(PROP_DISTANCE);
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE);
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).supports_field().hide_value();
+ b.add_input<decl::Int>(N_("Count")).default_value(10).min(1).max(100000).supports_field();
+ b.add_input<decl::Float>(N_("Length"))
+ .default_value(0.1f)
+ .min(0.001f)
+ .supports_field()
+ .subtype(PROP_DISTANCE);
+ b.add_output<decl::Geometry>(N_("Curve"));
}
-static void geo_node_curve_resample_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiItemR(layout, ptr, "mode", 0, "", ICON_NONE);
}
-static void geo_node_curve_resample_init(bNodeTree *UNUSED(tree), bNode *node)
+static void node_init(bNodeTree *UNUSED(tree), bNode *node)
{
- NodeGeometryCurveResample *data = (NodeGeometryCurveResample *)MEM_callocN(
- sizeof(NodeGeometryCurveResample), __func__);
+ NodeGeometryCurveResample *data = MEM_cnew<NodeGeometryCurveResample>(__func__);
data->mode = GEO_NODE_CURVE_RESAMPLE_COUNT;
node->storage = data;
}
-static void geo_node_curve_resample_update(bNodeTree *UNUSED(ntree), bNode *node)
+static void node_update(bNodeTree *ntree, bNode *node)
{
- NodeGeometryCurveResample &node_storage = *(NodeGeometryCurveResample *)node->storage;
- const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)node_storage.mode;
+ const NodeGeometryCurveResample &storage = node_storage(*node);
+ const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)storage.mode;
- bNodeSocket *count_socket = ((bNodeSocket *)node->inputs.first)->next;
+ bNodeSocket *count_socket = ((bNodeSocket *)node->inputs.first)->next->next;
bNodeSocket *length_socket = count_socket->next;
- nodeSetSocketAvailability(count_socket, mode == GEO_NODE_CURVE_RESAMPLE_COUNT);
- nodeSetSocketAvailability(length_socket, mode == GEO_NODE_CURVE_RESAMPLE_LENGTH);
+ nodeSetSocketAvailability(ntree, count_socket, mode == GEO_NODE_CURVE_RESAMPLE_COUNT);
+ nodeSetSocketAvailability(ntree, length_socket, mode == GEO_NODE_CURVE_RESAMPLE_LENGTH);
}
struct SampleModeParam {
GeometryNodeCurveResampleMode mode;
- std::optional<float> length;
- std::optional<int> count;
+ std::optional<Field<float>> length;
+ std::optional<Field<int>> count;
+ Field<bool> selection;
};
static SplinePtr resample_spline(const Spline &src, const int count)
@@ -78,8 +81,11 @@ static SplinePtr resample_spline(const Spline &src, const int count)
Spline::copy_base_settings(src, *dst);
if (src.evaluated_edges_size() < 1 || count == 1) {
- dst->add_point(src.positions().first(), src.tilts().first(), src.radii().first());
- dst->attributes.reallocate(1);
+ dst->resize(1);
+ dst->positions().first() = src.positions().first();
+ dst->radii().first() = src.radii().first();
+ dst->tilts().first() = src.tilts().first();
+
src.attributes.foreach_attribute(
[&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
std::optional<GSpan> src_attribute = src.attributes.get_for_read(attribute_id);
@@ -118,7 +124,7 @@ static SplinePtr resample_spline(const Spline &src, const int count)
std::optional<GMutableSpan> output_attribute = dst->attributes.get_for_write(
attribute_id);
if (output_attribute) {
- src.sample_with_index_factors(*src.interpolate_to_evaluated(*input_attribute),
+ src.sample_with_index_factors(src.interpolate_to_evaluated(*input_attribute),
uniform_samples,
*output_attribute);
return true;
@@ -141,8 +147,8 @@ static SplinePtr resample_spline_evaluated(const Spline &src)
dst->positions().copy_from(src.evaluated_positions());
dst->positions().copy_from(src.evaluated_positions());
- src.interpolate_to_evaluated(src.radii())->materialize(dst->radii());
- src.interpolate_to_evaluated(src.tilts())->materialize(dst->tilts());
+ src.interpolate_to_evaluated(src.radii()).materialize(dst->radii());
+ src.interpolate_to_evaluated(src.tilts()).materialize(dst->tilts());
src.attributes.foreach_attribute(
[&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
@@ -150,7 +156,7 @@ static SplinePtr resample_spline_evaluated(const Spline &src)
if (dst->attributes.create(attribute_id, meta_data.data_type)) {
std::optional<GMutableSpan> dst_attribute = dst->attributes.get_for_write(attribute_id);
if (dst_attribute) {
- src.interpolate_to_evaluated(*src_attribute)->materialize(dst_attribute->data());
+ src.interpolate_to_evaluated(*src_attribute).materialize(dst_attribute->data());
return true;
}
}
@@ -163,42 +169,80 @@ static SplinePtr resample_spline_evaluated(const Spline &src)
return dst;
}
-static std::unique_ptr<CurveEval> resample_curve(const CurveEval &input_curve,
+static std::unique_ptr<CurveEval> resample_curve(const CurveComponent *component,
const SampleModeParam &mode_param)
{
- Span<SplinePtr> input_splines = input_curve.splines();
+ const CurveEval *input_curve = component->get_for_read();
+ GeometryComponentFieldContext field_context{*component, ATTR_DOMAIN_CURVE};
+ const int domain_size = component->attribute_domain_size(ATTR_DOMAIN_CURVE);
+
+ Span<SplinePtr> input_splines = input_curve->splines();
std::unique_ptr<CurveEval> output_curve = std::make_unique<CurveEval>();
output_curve->resize(input_splines.size());
MutableSpan<SplinePtr> output_splines = output_curve->splines();
if (mode_param.mode == GEO_NODE_CURVE_RESAMPLE_COUNT) {
+ fn::FieldEvaluator evaluator{field_context, domain_size};
+ evaluator.add(*mode_param.count);
+ evaluator.add(mode_param.selection);
+ evaluator.evaluate();
+ const VArray<int> &cuts = evaluator.get_evaluated<int>(0);
+ const VArray<bool> &selections = evaluator.get_evaluated<bool>(1);
+
threading::parallel_for(input_splines.index_range(), 128, [&](IndexRange range) {
for (const int i : range) {
BLI_assert(mode_param.count);
- output_splines[i] = resample_spline(*input_splines[i], *mode_param.count);
+ if (selections[i] && input_splines[i]->evaluated_points_size() > 0) {
+ output_splines[i] = resample_spline(*input_splines[i], std::max(cuts[i], 1));
+ }
+ else {
+ output_splines[i] = input_splines[i]->copy();
+ }
}
});
}
else if (mode_param.mode == GEO_NODE_CURVE_RESAMPLE_LENGTH) {
+ fn::FieldEvaluator evaluator{field_context, domain_size};
+ evaluator.add(*mode_param.length);
+ evaluator.add(mode_param.selection);
+ evaluator.evaluate();
+ const VArray<float> &lengths = evaluator.get_evaluated<float>(0);
+ const VArray<bool> &selections = evaluator.get_evaluated<bool>(1);
+
threading::parallel_for(input_splines.index_range(), 128, [&](IndexRange range) {
for (const int i : range) {
- const float length = input_splines[i]->length();
- const int count = std::max(int(length / *mode_param.length) + 1, 1);
- output_splines[i] = resample_spline(*input_splines[i], count);
+ if (selections[i] && input_splines[i]->evaluated_points_size() > 0) {
+ /* Don't allow asymptotic count increase for low resolution values. */
+ const float divide_length = std::max(lengths[i], 0.0001f);
+ const float spline_length = input_splines[i]->length();
+ const int count = std::max(int(spline_length / divide_length) + 1, 1);
+ output_splines[i] = resample_spline(*input_splines[i], count);
+ }
+ else {
+ output_splines[i] = input_splines[i]->copy();
+ }
}
});
}
else if (mode_param.mode == GEO_NODE_CURVE_RESAMPLE_EVALUATED) {
+ fn::FieldEvaluator evaluator{field_context, domain_size};
+ evaluator.add(mode_param.selection);
+ evaluator.evaluate();
+ const VArray<bool> &selections = evaluator.get_evaluated<bool>(0);
+
threading::parallel_for(input_splines.index_range(), 128, [&](IndexRange range) {
for (const int i : range) {
- output_splines[i] = resample_spline_evaluated(*input_splines[i]);
+ if (selections[i] && input_splines[i]->evaluated_points_size() > 0) {
+ output_splines[i] = resample_spline_evaluated(*input_splines[i]);
+ }
+ else {
+ output_splines[i] = input_splines[i]->copy();
+ }
}
});
}
-
- output_curve->attributes = input_curve.attributes;
-
+ output_curve->attributes = input_curve->attributes;
return output_curve;
}
@@ -209,54 +253,57 @@ static void geometry_set_curve_resample(GeometrySet &geometry_set,
return;
}
- const CurveEval &input_curve = *geometry_set.get_curve_for_read();
- std::unique_ptr<CurveEval> output_curve = resample_curve(input_curve, mode_param);
+ std::unique_ptr<CurveEval> output_curve = resample_curve(
+ geometry_set.get_component_for_read<CurveComponent>(), mode_param);
geometry_set.replace_curve(output_curve.release());
}
-static void geo_node_resample_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
- GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
- NodeGeometryCurveResample &node_storage = *(NodeGeometryCurveResample *)params.node().storage;
- const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)node_storage.mode;
+ const NodeGeometryCurveResample &storage = node_storage(params.node());
+ const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)storage.mode;
SampleModeParam mode_param;
mode_param.mode = mode;
+ mode_param.selection = params.extract_input<Field<bool>>("Selection");
+
if (mode == GEO_NODE_CURVE_RESAMPLE_COUNT) {
- const int count = params.extract_input<int>("Count");
+ Field<int> count = params.extract_input<Field<int>>("Count");
if (count < 1) {
- params.set_output("Geometry", GeometrySet());
+ params.set_default_remaining_outputs();
return;
}
mode_param.count.emplace(count);
}
else if (mode == GEO_NODE_CURVE_RESAMPLE_LENGTH) {
- /* Don't allow asymptotic count increase for low resolution values. */
- const float resolution = std::max(params.extract_input<float>("Length"), 0.0001f);
+ Field<float> resolution = params.extract_input<Field<float>>("Length");
mode_param.length.emplace(resolution);
}
geometry_set.modify_geometry_sets(
[&](GeometrySet &geometry_set) { geometry_set_curve_resample(geometry_set, mode_param); });
- params.set_output("Geometry", std::move(geometry_set));
+ params.set_output("Curve", std::move(geometry_set));
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_curve_resample_cc
void register_node_type_geo_curve_resample()
{
+ namespace file_ns = blender::nodes::node_geo_curve_resample_cc;
+
static bNodeType ntype;
- geo_node_type_base(&ntype, GEO_NODE_CURVE_RESAMPLE, "Resample Curve", NODE_CLASS_GEOMETRY, 0);
- ntype.declare = blender::nodes::geo_node_curve_resample_declare;
- ntype.draw_buttons = blender::nodes::geo_node_curve_resample_layout;
+ geo_node_type_base(&ntype, GEO_NODE_RESAMPLE_CURVE, "Resample Curve", NODE_CLASS_GEOMETRY);
+ ntype.declare = file_ns::node_declare;
+ ntype.draw_buttons = file_ns::node_layout;
node_type_storage(
&ntype, "NodeGeometryCurveResample", node_free_standard_storage, node_copy_standard_storage);
- node_type_init(&ntype, blender::nodes::geo_node_curve_resample_init);
- node_type_update(&ntype, blender::nodes::geo_node_curve_resample_update);
- ntype.geometry_node_execute = blender::nodes::geo_node_resample_exec;
+ node_type_init(&ntype, file_ns::node_init);
+ node_type_update(&ntype, file_ns::node_update);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc
index b644faabedb..38974fafce7 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc
@@ -20,16 +20,16 @@
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_curve_reverse_cc {
-static void geo_node_curve_reverse_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Curve");
- b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field();
- b.add_output<decl::Geometry>("Curve");
+ b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE);
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field();
+ b.add_output<decl::Geometry>(N_("Curve"));
}
-static void geo_node_curve_reverse_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
@@ -38,7 +38,7 @@ static void geo_node_curve_reverse_exec(GeoNodeExecParams params)
return;
}
- Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
+ Field<bool> selection_field = params.get_input<Field<bool>>("Selection");
CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>();
GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE};
const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE);
@@ -60,13 +60,15 @@ static void geo_node_curve_reverse_exec(GeoNodeExecParams params)
params.set_output("Curve", std::move(geometry_set));
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_curve_reverse_cc
void register_node_type_geo_curve_reverse()
{
+ namespace file_ns = blender::nodes::node_geo_curve_reverse_cc;
+
static bNodeType ntype;
- geo_node_type_base(&ntype, GEO_NODE_CURVE_REVERSE, "Curve Reverse", NODE_CLASS_GEOMETRY, 0);
- ntype.declare = blender::nodes::geo_node_curve_reverse_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_curve_reverse_exec;
+ geo_node_type_base(&ntype, GEO_NODE_REVERSE_CURVE, "Reverse Curve", NODE_CLASS_GEOMETRY);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc
index 1266f525861..56fbc50f033 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc
@@ -23,42 +23,53 @@
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_curve_sample_cc {
-static void geo_node_curve_sample_declare(NodeDeclarationBuilder &b)
-{
- b.add_input<decl::Geometry>("Curve");
- b.add_input<decl::Float>("Factor").min(0.0f).max(1.0f).subtype(PROP_FACTOR).supports_field();
- b.add_input<decl::Float>("Length").min(0.0f).subtype(PROP_DISTANCE).supports_field();
+NODE_STORAGE_FUNCS(NodeGeometryCurveSample)
- b.add_output<decl::Vector>("Position").dependent_field();
- b.add_output<decl::Vector>("Tangent").dependent_field();
- b.add_output<decl::Vector>("Normal").dependent_field();
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>(N_("Curve"))
+ .only_realized_data()
+ .supported_type(GEO_COMPONENT_TYPE_CURVE);
+ b.add_input<decl::Float>(N_("Factor"))
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR)
+ .supports_field()
+ .make_available([](bNode &node) { node_storage(node).mode = GEO_NODE_CURVE_SAMPLE_FACTOR; });
+ b.add_input<decl::Float>(N_("Length"))
+ .min(0.0f)
+ .subtype(PROP_DISTANCE)
+ .supports_field()
+ .make_available([](bNode &node) { node_storage(node).mode = GEO_NODE_CURVE_SAMPLE_LENGTH; });
+ b.add_output<decl::Vector>(N_("Position")).dependent_field();
+ b.add_output<decl::Vector>(N_("Tangent")).dependent_field();
+ b.add_output<decl::Vector>(N_("Normal")).dependent_field();
}
-static void geo_node_curve_sample_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
}
-static void geo_node_curve_sample_type_init(bNodeTree *UNUSED(tree), bNode *node)
+static void node_type_init(bNodeTree *UNUSED(tree), bNode *node)
{
- NodeGeometryCurveSample *data = (NodeGeometryCurveSample *)MEM_callocN(
- sizeof(NodeGeometryCurveSample), __func__);
+ NodeGeometryCurveSample *data = MEM_cnew<NodeGeometryCurveSample>(__func__);
data->mode = GEO_NODE_CURVE_SAMPLE_LENGTH;
node->storage = data;
}
-static void geo_node_curve_sample_update(bNodeTree *UNUSED(ntree), bNode *node)
+static void node_update(bNodeTree *ntree, bNode *node)
{
- const NodeGeometryCurveSample &node_storage = *(NodeGeometryCurveSample *)node->storage;
- const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode;
+ const NodeGeometryCurveSample &storage = node_storage(*node);
+ const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)storage.mode;
bNodeSocket *factor = ((bNodeSocket *)node->inputs.first)->next;
bNodeSocket *length = factor->next;
- nodeSetSocketAvailability(factor, mode == GEO_NODE_CURVE_SAMPLE_FACTOR);
- nodeSetSocketAvailability(length, mode == GEO_NODE_CURVE_SAMPLE_LENGTH);
+ nodeSetSocketAvailability(ntree, factor, mode == GEO_NODE_CURVE_SAMPLE_FACTOR);
+ nodeSetSocketAvailability(ntree, length, mode == GEO_NODE_CURVE_SAMPLE_LENGTH);
}
template<typename T> static T sample_with_lookup(const Spline::LookupResult lookup, Span<T> data)
@@ -174,7 +185,7 @@ class SampleCurveFunction : public fn::MultiFunction {
for (const int i : mask) {
const Spline::LookupResult &lookup = lookups[i];
const Span<float3> evaluated_tangents = splines[spline_indices[i]]->evaluated_tangents();
- sampled_tangents[i] = sample_with_lookup(lookup, evaluated_tangents).normalized();
+ sampled_tangents[i] = math::normalize(sample_with_lookup(lookup, evaluated_tangents));
}
}
@@ -182,7 +193,7 @@ class SampleCurveFunction : public fn::MultiFunction {
for (const int i : mask) {
const Spline::LookupResult &lookup = lookups[i];
const Span<float3> evaluated_normals = splines[spline_indices[i]]->evaluated_normals();
- sampled_normals[i] = sample_with_lookup(lookup, evaluated_normals).normalized();
+ sampled_normals[i] = math::normalize(sample_with_lookup(lookup, evaluated_normals));
}
}
}
@@ -198,8 +209,8 @@ class SampleCurveFunction : public fn::MultiFunction {
static Field<float> get_length_input_field(const GeoNodeExecParams &params,
const float curve_total_length)
{
- const NodeGeometryCurveSample &node_storage = *(NodeGeometryCurveSample *)params.node().storage;
- const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode;
+ const NodeGeometryCurveSample &storage = node_storage(params.node());
+ const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)storage.mode;
if (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) {
/* Just make sure the length is in bounds of the curve. */
@@ -227,34 +238,32 @@ static Field<float> get_length_input_field(const GeoNodeExecParams &params,
return Field<float>(std::move(process_op), 0);
}
-static void geo_node_curve_sample_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
- auto return_default = [&]() {
- params.set_output("Position", fn::make_constant_field<float3>({0.0f, 0.0f, 0.0f}));
- params.set_output("Tangent", fn::make_constant_field<float3>({0.0f, 0.0f, 0.0f}));
- params.set_output("Normal", fn::make_constant_field<float3>({0.0f, 0.0f, 0.0f}));
- };
-
const CurveComponent *component = geometry_set.get_component_for_read<CurveComponent>();
if (component == nullptr) {
- return return_default();
+ params.set_default_remaining_outputs();
+ return;
}
const CurveEval *curve = component->get_for_read();
if (curve == nullptr) {
- return return_default();
+ params.set_default_remaining_outputs();
+ return;
}
if (curve->splines().is_empty()) {
- return return_default();
+ params.set_default_remaining_outputs();
+ return;
}
Array<float> spline_lengths = curve->accumulated_spline_lengths();
const float total_length = spline_lengths.last();
if (total_length == 0.0f) {
- return return_default();
+ params.set_default_remaining_outputs();
+ return;
}
Field<float> length_field = get_length_input_field(params, total_length);
@@ -269,20 +278,22 @@ static void geo_node_curve_sample_exec(GeoNodeExecParams params)
params.set_output("Normal", Field<float3>(sample_op, 2));
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_curve_sample_cc
void register_node_type_geo_curve_sample()
{
+ namespace file_ns = blender::nodes::node_geo_curve_sample_cc;
+
static bNodeType ntype;
- geo_node_type_base(&ntype, GEO_NODE_CURVE_SAMPLE, "Curve Sample", NODE_CLASS_GEOMETRY, 0);
- ntype.geometry_node_execute = blender::nodes::geo_node_curve_sample_exec;
- ntype.declare = blender::nodes::geo_node_curve_sample_declare;
- node_type_init(&ntype, blender::nodes::geo_node_curve_sample_type_init);
- node_type_update(&ntype, blender::nodes::geo_node_curve_sample_update);
+ geo_node_type_base(&ntype, GEO_NODE_SAMPLE_CURVE, "Sample Curve", NODE_CLASS_GEOMETRY);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.declare = file_ns::node_declare;
+ node_type_init(&ntype, file_ns::node_type_init);
+ node_type_update(&ntype, file_ns::node_update);
node_type_storage(
&ntype, "NodeGeometryCurveSample", node_free_standard_storage, node_copy_standard_storage);
- ntype.draw_buttons = blender::nodes::geo_node_curve_sample_layout;
+ ntype.draw_buttons = file_ns::node_layout;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc
new file mode 100644
index 00000000000..74bdce4cef3
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc
@@ -0,0 +1,149 @@
+/*
+ * 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 "BKE_spline.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_curve_set_handles_cc {
+
+NODE_STORAGE_FUNCS(NodeGeometryCurveSetHandles)
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE);
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field();
+ b.add_output<decl::Geometry>(N_("Curve"));
+}
+
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "handle_type", 0, "", ICON_NONE);
+}
+
+static void node_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometryCurveSetHandles *data = MEM_cnew<NodeGeometryCurveSetHandles>(__func__);
+
+ data->handle_type = GEO_NODE_CURVE_HANDLE_AUTO;
+ data->mode = GEO_NODE_CURVE_HANDLE_LEFT | GEO_NODE_CURVE_HANDLE_RIGHT;
+ node->storage = data;
+}
+
+static BezierSpline::HandleType handle_type_from_input_type(GeometryNodeCurveHandleType type)
+{
+ switch (type) {
+ case GEO_NODE_CURVE_HANDLE_AUTO:
+ return BezierSpline::HandleType::Auto;
+ case GEO_NODE_CURVE_HANDLE_ALIGN:
+ return BezierSpline::HandleType::Align;
+ case GEO_NODE_CURVE_HANDLE_FREE:
+ return BezierSpline::HandleType::Free;
+ case GEO_NODE_CURVE_HANDLE_VECTOR:
+ return BezierSpline::HandleType::Vector;
+ }
+ BLI_assert_unreachable();
+ return BezierSpline::HandleType::Auto;
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ const NodeGeometryCurveSetHandles &storage = node_storage(params.node());
+ const GeometryNodeCurveHandleType type = (GeometryNodeCurveHandleType)storage.handle_type;
+ const GeometryNodeCurveHandleMode mode = (GeometryNodeCurveHandleMode)storage.mode;
+
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
+ Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
+
+ bool has_bezier_spline = false;
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ if (!geometry_set.has_curve()) {
+ return;
+ }
+
+ /* Retrieve data for write access so we can avoid new allocations for the handles data. */
+ CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>();
+ CurveEval &curve = *curve_component.get_for_write();
+ MutableSpan<SplinePtr> splines = curve.splines();
+
+ GeometryComponentFieldContext field_context{curve_component, ATTR_DOMAIN_POINT};
+ const int domain_size = curve_component.attribute_domain_size(ATTR_DOMAIN_POINT);
+
+ fn::FieldEvaluator selection_evaluator{field_context, domain_size};
+ selection_evaluator.add(selection_field);
+ selection_evaluator.evaluate();
+ const VArray<bool> &selection = selection_evaluator.get_evaluated<bool>(0);
+
+ const BezierSpline::HandleType new_handle_type = handle_type_from_input_type(type);
+ int point_index = 0;
+
+ for (SplinePtr &spline : splines) {
+ if (spline->type() != Spline::Type::Bezier) {
+ point_index += spline->positions().size();
+ continue;
+ }
+
+ has_bezier_spline = true;
+ BezierSpline &bezier_spline = static_cast<BezierSpline &>(*spline);
+ if (ELEM(new_handle_type, BezierSpline::HandleType::Free, BezierSpline::HandleType::Align)) {
+ /* In this case the automatically calculated handle types need to be "baked", because
+ * they're possibly changing from a type that is calculated automatically to a type that
+ * is positioned manually. */
+ bezier_spline.ensure_auto_handles();
+ }
+
+ for (int i_point : IndexRange(bezier_spline.size())) {
+ if (selection[point_index]) {
+ if (mode & GEO_NODE_CURVE_HANDLE_LEFT) {
+ bezier_spline.handle_types_left()[i_point] = new_handle_type;
+ }
+ if (mode & GEO_NODE_CURVE_HANDLE_RIGHT) {
+ bezier_spline.handle_types_right()[i_point] = new_handle_type;
+ }
+ }
+ point_index++;
+ }
+ bezier_spline.mark_cache_invalid();
+ }
+ });
+ if (!has_bezier_spline) {
+ params.error_message_add(NodeWarningType::Info, TIP_("No Bezier splines in input curve"));
+ }
+ params.set_output("Curve", std::move(geometry_set));
+}
+} // namespace blender::nodes::node_geo_curve_set_handles_cc
+
+void register_node_type_geo_curve_set_handles()
+{
+ namespace file_ns = blender::nodes::node_geo_curve_set_handles_cc;
+
+ static bNodeType ntype;
+ geo_node_type_base(&ntype, GEO_NODE_CURVE_SET_HANDLES, "Set Handle Type", NODE_CLASS_GEOMETRY);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ node_type_init(&ntype, file_ns::node_init);
+ node_type_storage(&ntype,
+ "NodeGeometryCurveSetHandles",
+ node_free_standard_storage,
+ node_copy_standard_storage);
+ ntype.draw_buttons = file_ns::node_layout;
+
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc
new file mode 100644
index 00000000000..257a5b8df00
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc
@@ -0,0 +1,326 @@
+/*
+ * 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 "BKE_spline.hh"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_curve_spline_parameter_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_output<decl::Float>(N_("Factor"))
+ .field_source()
+ .description(
+ N_("For points, the portion of the spline's total length at the control point. For "
+ "Splines, the factor of that spline within the entire curve"));
+ b.add_output<decl::Float>(N_("Length"))
+ .field_source()
+ .description(
+ N_("For points, the distance along the control point's spline, For splines, the "
+ "distance along the entire curve"));
+ b.add_output<decl::Int>(N_("Index"))
+ .field_source()
+ .description(N_("Each control point's index on its spline"));
+}
+
+/**
+ * A basic interpolation from the point domain to the spline domain would be useless, since the
+ * average parameter for each spline would just be 0.5, or close to it. Instead, the parameter for
+ * each spline is the portion of the total length at the start of the spline.
+ */
+static Array<float> curve_length_spline_domain(const CurveEval &curve,
+ const IndexMask UNUSED(mask))
+{
+ Span<SplinePtr> splines = curve.splines();
+ float length = 0.0f;
+ Array<float> lengths(splines.size());
+ for (const int i : splines.index_range()) {
+ lengths[i] = length;
+ length += splines[i]->length();
+ }
+ return lengths;
+}
+
+/**
+ * The parameter at each control point is the factor at the corresponding evaluated point.
+ */
+static void calculate_bezier_lengths(const BezierSpline &spline, MutableSpan<float> lengths)
+{
+ Span<int> offsets = spline.control_point_offsets();
+ Span<float> lengths_eval = spline.evaluated_lengths();
+ for (const int i : IndexRange(1, spline.size() - 1)) {
+ lengths[i] = lengths_eval[offsets[i] - 1];
+ }
+}
+
+/**
+ * The parameter for poly splines is simply the evaluated lengths divided by the total length.
+ */
+static void calculate_poly_length(const PolySpline &spline, MutableSpan<float> lengths)
+{
+ Span<float> lengths_eval = spline.evaluated_lengths();
+ if (spline.is_cyclic()) {
+ lengths.drop_front(1).copy_from(lengths_eval.drop_back(1));
+ }
+ else {
+ lengths.drop_front(1).copy_from(lengths_eval);
+ }
+}
+
+/**
+ * Since NURBS control points do not necessarily coincide with the evaluated curve's path, and
+ * each control point doesn't correspond well to a specific evaluated point, the parameter at
+ * each point is not well defined. So instead, treat the control points as if they were a poly
+ * spline.
+ */
+static void calculate_nurbs_lengths(const NURBSpline &spline, MutableSpan<float> lengths)
+{
+ Span<float3> positions = spline.positions();
+ Array<float> control_point_lengths(spline.size());
+ float length = 0.0f;
+ for (const int i : IndexRange(positions.size() - 1)) {
+ lengths[i] = length;
+ length += math::distance(positions[i], positions[i + 1]);
+ }
+ lengths.last() = length;
+}
+
+static Array<float> curve_length_point_domain(const CurveEval &curve)
+{
+ Span<SplinePtr> splines = curve.splines();
+ Array<int> offsets = curve.control_point_offsets();
+ const int total_size = offsets.last();
+ Array<float> lengths(total_size);
+
+ threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) {
+ for (const int i : range) {
+ const Spline &spline = *splines[i];
+ MutableSpan spline_factors{lengths.as_mutable_span().slice(offsets[i], spline.size())};
+ spline_factors.first() = 0.0f;
+ switch (splines[i]->type()) {
+ case Spline::Type::Bezier: {
+ calculate_bezier_lengths(static_cast<const BezierSpline &>(spline), spline_factors);
+ break;
+ }
+ case Spline::Type::Poly: {
+ calculate_poly_length(static_cast<const PolySpline &>(spline), spline_factors);
+ break;
+ }
+ case Spline::Type::NURBS: {
+ calculate_nurbs_lengths(static_cast<const NURBSpline &>(spline), spline_factors);
+ break;
+ }
+ }
+ }
+ });
+ return lengths;
+}
+
+static VArray<float> construct_curve_parameter_varray(const CurveEval &curve,
+ const IndexMask mask,
+ const AttributeDomain domain)
+{
+ if (domain == ATTR_DOMAIN_POINT) {
+ Span<SplinePtr> splines = curve.splines();
+ Array<float> values = curve_length_point_domain(curve);
+
+ const Array<int> offsets = curve.control_point_offsets();
+ for (const int i_spline : curve.splines().index_range()) {
+ const Spline &spline = *splines[i_spline];
+ const float spline_length = spline.length();
+ const float spline_length_inv = spline_length == 0.0f ? 0.0f : 1.0f / spline_length;
+ for (const int i : IndexRange(spline.size())) {
+ values[offsets[i_spline] + i] *= spline_length_inv;
+ }
+ }
+ return VArray<float>::ForContainer(std::move(values));
+ }
+
+ if (domain == ATTR_DOMAIN_CURVE) {
+ Array<float> values = curve.accumulated_spline_lengths();
+ const float total_length_inv = values.last() == 0.0f ? 0.0f : 1.0f / values.last();
+ for (const int i : mask) {
+ values[i] *= total_length_inv;
+ }
+ return VArray<float>::ForContainer(std::move(values));
+ }
+ return {};
+}
+
+static VArray<float> construct_curve_length_varray(const CurveEval &curve,
+ const IndexMask mask,
+ const AttributeDomain domain)
+{
+ if (domain == ATTR_DOMAIN_POINT) {
+ Array<float> lengths = curve_length_point_domain(curve);
+ return VArray<float>::ForContainer(std::move(lengths));
+ }
+
+ if (domain == ATTR_DOMAIN_CURVE) {
+ if (curve.splines().size() == 1) {
+ Array<float> lengths(1, 0.0f);
+ return VArray<float>::ForContainer(std::move(lengths));
+ }
+
+ Array<float> lengths = curve_length_spline_domain(curve, mask);
+ return VArray<float>::ForContainer(std::move(lengths));
+ }
+
+ return {};
+}
+
+static VArray<int> construct_index_on_spline_varray(const CurveEval &curve,
+ const IndexMask UNUSED(mask),
+ const AttributeDomain domain)
+{
+ if (domain == ATTR_DOMAIN_POINT) {
+ Array<int> output(curve.total_control_point_size());
+ int output_index = 0;
+ for (int spline_index : curve.splines().index_range()) {
+ for (int point_index : IndexRange(curve.splines()[spline_index]->size())) {
+ output[output_index++] = point_index;
+ }
+ }
+ return VArray<int>::ForContainer(std::move(output));
+ }
+ return {};
+}
+
+class CurveParameterFieldInput final : public GeometryFieldInput {
+ public:
+ CurveParameterFieldInput() : GeometryFieldInput(CPPType::get<float>(), "Curve Parameter node")
+ {
+ category_ = Category::Generated;
+ }
+
+ GVArray get_varray_for_context(const GeometryComponent &component,
+ const AttributeDomain domain,
+ IndexMask mask) const final
+ {
+ if (component.type() == GEO_COMPONENT_TYPE_CURVE) {
+ const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
+ const CurveEval *curve = curve_component.get_for_read();
+ if (curve) {
+ return construct_curve_parameter_varray(*curve, mask, domain);
+ }
+ }
+ return {};
+ }
+
+ uint64_t hash() const override
+ {
+ /* Some random constant hash. */
+ return 29837456298;
+ }
+
+ bool is_equal_to(const fn::FieldNode &other) const override
+ {
+ return dynamic_cast<const CurveParameterFieldInput *>(&other) != nullptr;
+ }
+};
+
+class CurveLengthFieldInput final : public GeometryFieldInput {
+ public:
+ CurveLengthFieldInput() : GeometryFieldInput(CPPType::get<float>(), "Curve Length node")
+ {
+ category_ = Category::Generated;
+ }
+
+ GVArray get_varray_for_context(const GeometryComponent &component,
+ const AttributeDomain domain,
+ IndexMask mask) const final
+ {
+ if (component.type() == GEO_COMPONENT_TYPE_CURVE) {
+ const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
+ const CurveEval *curve = curve_component.get_for_read();
+ if (curve) {
+ return construct_curve_length_varray(*curve, mask, domain);
+ }
+ }
+ return {};
+ }
+
+ uint64_t hash() const override
+ {
+ /* Some random constant hash. */
+ return 345634563454;
+ }
+
+ bool is_equal_to(const fn::FieldNode &other) const override
+ {
+ return dynamic_cast<const CurveLengthFieldInput *>(&other) != nullptr;
+ }
+};
+
+class IndexOnSplineFieldInput final : public GeometryFieldInput {
+ public:
+ IndexOnSplineFieldInput() : GeometryFieldInput(CPPType::get<int>(), "Spline Index")
+ {
+ category_ = Category::Generated;
+ }
+
+ GVArray get_varray_for_context(const GeometryComponent &component,
+ const AttributeDomain domain,
+ IndexMask mask) const final
+ {
+ if (component.type() == GEO_COMPONENT_TYPE_CURVE) {
+ const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
+ const CurveEval *curve = curve_component.get_for_read();
+ if (curve) {
+ return construct_index_on_spline_varray(*curve, mask, domain);
+ }
+ }
+ return {};
+ }
+
+ uint64_t hash() const override
+ {
+ /* Some random constant hash. */
+ return 4536246522;
+ }
+
+ bool is_equal_to(const fn::FieldNode &other) const override
+ {
+ return dynamic_cast<const IndexOnSplineFieldInput *>(&other) != nullptr;
+ }
+};
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ Field<float> parameter_field{std::make_shared<CurveParameterFieldInput>()};
+ Field<float> length_field{std::make_shared<CurveLengthFieldInput>()};
+ Field<int> index_on_spline_field{std::make_shared<IndexOnSplineFieldInput>()};
+ params.set_output("Factor", std::move(parameter_field));
+ params.set_output("Length", std::move(length_field));
+ params.set_output("Index", std::move(index_on_spline_field));
+}
+
+} // namespace blender::nodes::node_geo_curve_spline_parameter_cc
+
+void register_node_type_geo_curve_spline_parameter()
+{
+ namespace file_ns = blender::nodes::node_geo_curve_spline_parameter_cc;
+
+ static bNodeType ntype;
+ geo_node_type_base(
+ &ntype, GEO_NODE_CURVE_SPLINE_PARAMETER, "Spline Parameter", NODE_CLASS_INPUT);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.declare = file_ns::node_declare;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc
new file mode 100644
index 00000000000..b91ddd7bc7a
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc
@@ -0,0 +1,434 @@
+/*
+ * 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 "BKE_spline.hh"
+
+#include "BLI_task.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_curve_spline_type_cc {
+
+NODE_STORAGE_FUNCS(NodeGeometryCurveSplineType)
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE);
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field();
+ b.add_output<decl::Geometry>(N_("Curve"));
+}
+
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "spline_type", 0, "", ICON_NONE);
+}
+
+static void node_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometryCurveSplineType *data = MEM_cnew<NodeGeometryCurveSplineType>(__func__);
+
+ data->spline_type = GEO_NODE_SPLINE_TYPE_POLY;
+ node->storage = data;
+}
+
+template<class T>
+static void scale_input_assign(const Span<T> input,
+ const int scale,
+ const int offset,
+ const MutableSpan<T> r_output)
+{
+ for (const int i : IndexRange(r_output.size())) {
+ r_output[i] = input[i * scale + offset];
+ }
+}
+
+template<class T>
+static void scale_output_assign(const Span<T> input,
+ const int scale,
+ const int offset,
+ const MutableSpan<T> &r_output)
+{
+ for (const int i : IndexRange(input.size())) {
+ r_output[i * scale + offset] = input[i];
+ }
+}
+
+template<class T>
+static void nurbs_to_bezier_assign(const Span<T> input,
+ const MutableSpan<T> r_output,
+ const NURBSpline::KnotsMode knotsMode)
+{
+ const int input_size = input.size();
+ const int output_size = r_output.size();
+
+ switch (knotsMode) {
+ case NURBSpline::KnotsMode::Bezier:
+ scale_input_assign<T>(input, 3, 1, r_output);
+ break;
+ case NURBSpline::KnotsMode::Normal:
+ for (const int i : IndexRange(output_size)) {
+ r_output[i] = input[(i + 1) % input_size];
+ }
+ break;
+ case NURBSpline::KnotsMode::EndPoint:
+ for (const int i : IndexRange(1, output_size - 2)) {
+ r_output[i] = input[i + 1];
+ }
+ r_output.first() = input.first();
+ r_output.last() = input.last();
+ break;
+ }
+}
+
+template<typename CopyFn>
+static void copy_attributes(const Spline &input_spline, Spline &output_spline, CopyFn copy_fn)
+{
+ input_spline.attributes.foreach_attribute(
+ [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
+ std::optional<GSpan> src = input_spline.attributes.get_for_read(attribute_id);
+ BLI_assert(src);
+ if (!output_spline.attributes.create(attribute_id, meta_data.data_type)) {
+ BLI_assert_unreachable();
+ return false;
+ }
+ std::optional<GMutableSpan> dst = output_spline.attributes.get_for_write(attribute_id);
+ if (!dst) {
+ BLI_assert_unreachable();
+ return false;
+ }
+
+ copy_fn(*src, *dst);
+
+ return true;
+ },
+ ATTR_DOMAIN_POINT);
+}
+
+static Vector<float3> create_nurbs_to_bezier_handles(const Span<float3> nurbs_positions,
+ const NURBSpline::KnotsMode knots_mode)
+{
+ const int nurbs_positions_size = nurbs_positions.size();
+ Vector<float3> handle_positions;
+ if (knots_mode == NURBSpline::KnotsMode::Bezier) {
+ for (const int i : IndexRange(nurbs_positions_size)) {
+ if (i % 3 == 1) {
+ continue;
+ }
+ handle_positions.append(nurbs_positions[i]);
+ }
+ if (nurbs_positions_size % 3 == 1) {
+ handle_positions.pop_last();
+ }
+ else if (nurbs_positions_size % 3 == 2) {
+ const int last_index = nurbs_positions_size - 1;
+ handle_positions.append(2 * nurbs_positions[last_index] - nurbs_positions[last_index - 1]);
+ }
+ }
+ else {
+ const bool is_periodic = knots_mode == NURBSpline::KnotsMode::Normal;
+ if (is_periodic) {
+ handle_positions.append(nurbs_positions[1] +
+ ((nurbs_positions[0] - nurbs_positions[1]) / 3));
+ }
+ else {
+ handle_positions.append(2 * nurbs_positions[0] - nurbs_positions[1]);
+ handle_positions.append(nurbs_positions[1]);
+ }
+ const int segments_size = nurbs_positions_size - 1;
+ const bool ignore_interior_segment = segments_size == 3 && is_periodic == false;
+ if (ignore_interior_segment == false) {
+ const float mid_offset = (float)(segments_size - 1) / 2.0f;
+ for (const int i : IndexRange(1, segments_size - 2)) {
+ const int divisor = is_periodic ?
+ 3 :
+ std::min(3, (int)(-std::abs(i - mid_offset) + mid_offset + 1.0f));
+ const float3 &p1 = nurbs_positions[i];
+ const float3 &p2 = nurbs_positions[i + 1];
+ const float3 displacement = (p2 - p1) / divisor;
+ const int num_handles_on_segment = divisor < 3 ? 1 : 2;
+ for (int j : IndexRange(1, num_handles_on_segment)) {
+ handle_positions.append(p1 + (displacement * j));
+ }
+ }
+ }
+ const int last_index = nurbs_positions_size - 1;
+ if (is_periodic) {
+ handle_positions.append(
+ nurbs_positions[last_index - 1] +
+ ((nurbs_positions[last_index] - nurbs_positions[last_index - 1]) / 3));
+ }
+ else {
+ handle_positions.append(nurbs_positions[last_index - 1]);
+ handle_positions.append(2 * nurbs_positions[last_index] - nurbs_positions[last_index - 1]);
+ }
+ }
+ return handle_positions;
+}
+
+static Array<float3> create_nurbs_to_bezier_positions(const Span<float3> nurbs_positions,
+ const Span<float3> handle_positions,
+ const NURBSpline::KnotsMode knots_mode)
+{
+ if (knots_mode == NURBSpline::KnotsMode::Bezier) {
+ /* Every third NURBS position (starting from index 1) should be converted to Bezier position */
+ const int scale = 3;
+ const int offset = 1;
+ Array<float3> bezier_positions((nurbs_positions.size() + offset) / scale);
+ scale_input_assign(nurbs_positions, scale, offset, bezier_positions.as_mutable_span());
+ return bezier_positions;
+ }
+
+ Array<float3> bezier_positions(handle_positions.size() / 2);
+ for (const int i : IndexRange(bezier_positions.size())) {
+ bezier_positions[i] = math::interpolate(
+ handle_positions[i * 2], handle_positions[i * 2 + 1], 0.5f);
+ }
+ return bezier_positions;
+}
+
+static SplinePtr convert_to_poly_spline(const Spline &input)
+{
+ std::unique_ptr<PolySpline> output = std::make_unique<PolySpline>();
+ output->resize(input.positions().size());
+ output->positions().copy_from(input.positions());
+ output->radii().copy_from(input.radii());
+ output->tilts().copy_from(input.tilts());
+ Spline::copy_base_settings(input, *output);
+ output->attributes = input.attributes;
+ return output;
+}
+
+static SplinePtr poly_to_nurbs(const Spline &input)
+{
+ std::unique_ptr<NURBSpline> output = std::make_unique<NURBSpline>();
+ output->resize(input.positions().size());
+ output->positions().copy_from(input.positions());
+ output->radii().copy_from(input.radii());
+ output->tilts().copy_from(input.tilts());
+ output->weights().fill(1.0f);
+ output->set_resolution(12);
+ output->set_order(4);
+ Spline::copy_base_settings(input, *output);
+ output->knots_mode = NURBSpline::KnotsMode::Bezier;
+ output->attributes = input.attributes;
+ return output;
+}
+
+static SplinePtr bezier_to_nurbs(const Spline &input)
+{
+ const BezierSpline &bezier_spline = static_cast<const BezierSpline &>(input);
+ std::unique_ptr<NURBSpline> output = std::make_unique<NURBSpline>();
+ output->resize(input.size() * 3);
+
+ scale_output_assign(bezier_spline.handle_positions_left(), 3, 0, output->positions());
+ scale_output_assign(input.radii(), 3, 0, output->radii());
+ scale_output_assign(input.tilts(), 3, 0, output->tilts());
+
+ scale_output_assign(bezier_spline.positions(), 3, 1, output->positions());
+ scale_output_assign(input.radii(), 3, 1, output->radii());
+ scale_output_assign(input.tilts(), 3, 1, output->tilts());
+
+ scale_output_assign(bezier_spline.handle_positions_right(), 3, 2, output->positions());
+ scale_output_assign(input.radii(), 3, 2, output->radii());
+ scale_output_assign(input.tilts(), 3, 2, output->tilts());
+
+ Spline::copy_base_settings(input, *output);
+ output->weights().fill(1.0f);
+ output->set_resolution(12);
+ output->set_order(4);
+ output->set_cyclic(input.is_cyclic());
+ output->knots_mode = NURBSpline::KnotsMode::Bezier;
+ output->attributes.reallocate(output->size());
+ copy_attributes(input, *output, [](GSpan src, GMutableSpan dst) {
+ attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ scale_output_assign<T>(src.typed<T>(), 3, 0, dst.typed<T>());
+ scale_output_assign<T>(src.typed<T>(), 3, 1, dst.typed<T>());
+ scale_output_assign<T>(src.typed<T>(), 3, 2, dst.typed<T>());
+ });
+ });
+ return output;
+}
+
+static SplinePtr poly_to_bezier(const Spline &input)
+{
+ std::unique_ptr<BezierSpline> output = std::make_unique<BezierSpline>();
+ output->resize(input.size());
+ output->positions().copy_from(input.positions());
+ output->radii().copy_from(input.radii());
+ output->tilts().copy_from(input.tilts());
+ output->handle_types_left().fill(BezierSpline::HandleType::Vector);
+ output->handle_types_right().fill(BezierSpline::HandleType::Vector);
+ output->set_resolution(12);
+ Spline::copy_base_settings(input, *output);
+ output->attributes = input.attributes;
+ return output;
+}
+
+static SplinePtr nurbs_to_bezier(const Spline &input)
+{
+ const NURBSpline &nurbs_spline = static_cast<const NURBSpline &>(input);
+ Span<float3> nurbs_positions;
+ Vector<float3> nurbs_positions_vector;
+ NURBSpline::KnotsMode knots_mode;
+ if (nurbs_spline.is_cyclic()) {
+ nurbs_positions_vector = nurbs_spline.positions();
+ nurbs_positions_vector.append(nurbs_spline.positions()[0]);
+ nurbs_positions_vector.append(nurbs_spline.positions()[1]);
+ nurbs_positions = nurbs_positions_vector;
+ knots_mode = NURBSpline::KnotsMode::Normal;
+ }
+ else {
+ nurbs_positions = nurbs_spline.positions();
+ knots_mode = nurbs_spline.knots_mode;
+ }
+ const Vector<float3> handle_positions = create_nurbs_to_bezier_handles(nurbs_positions,
+ knots_mode);
+ BLI_assert(handle_positions.size() % 2 == 0);
+ const Array<float3> bezier_positions = create_nurbs_to_bezier_positions(
+ nurbs_positions, handle_positions.as_span(), knots_mode);
+ BLI_assert(handle_positions.size() == bezier_positions.size() * 2);
+
+ std::unique_ptr<BezierSpline> output = std::make_unique<BezierSpline>();
+ output->resize(bezier_positions.size());
+ output->positions().copy_from(bezier_positions);
+ nurbs_to_bezier_assign(nurbs_spline.radii(), output->radii(), knots_mode);
+ nurbs_to_bezier_assign(nurbs_spline.tilts(), output->tilts(), knots_mode);
+ scale_input_assign(handle_positions.as_span(), 2, 0, output->handle_positions_left());
+ scale_input_assign(handle_positions.as_span(), 2, 1, output->handle_positions_right());
+ output->handle_types_left().fill(BezierSpline::HandleType::Align);
+ output->handle_types_right().fill(BezierSpline::HandleType::Align);
+ output->set_resolution(nurbs_spline.resolution());
+ Spline::copy_base_settings(nurbs_spline, *output);
+ output->attributes.reallocate(output->size());
+ copy_attributes(nurbs_spline, *output, [knots_mode](GSpan src, GMutableSpan dst) {
+ attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ nurbs_to_bezier_assign(src.typed<T>(), dst.typed<T>(), knots_mode);
+ });
+ });
+ return output;
+}
+
+static SplinePtr convert_to_bezier(const Spline &input, GeoNodeExecParams params)
+{
+ switch (input.type()) {
+ case Spline::Type::Bezier:
+ return input.copy();
+ case Spline::Type::Poly:
+ return poly_to_bezier(input);
+ case Spline::Type::NURBS:
+ if (input.size() < 4) {
+ params.error_message_add(
+ NodeWarningType::Info,
+ TIP_("NURBS must have minimum of 4 points for Bezier Conversion"));
+ return input.copy();
+ }
+ return nurbs_to_bezier(input);
+ }
+ BLI_assert_unreachable();
+ return {};
+}
+
+static SplinePtr convert_to_nurbs(const Spline &input)
+{
+ switch (input.type()) {
+ case Spline::Type::NURBS:
+ return input.copy();
+ case Spline::Type::Bezier:
+ return bezier_to_nurbs(input);
+ case Spline::Type::Poly:
+ return poly_to_nurbs(input);
+ }
+ BLI_assert_unreachable();
+ return {};
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ const NodeGeometryCurveSplineType &storage = node_storage(params.node());
+ const GeometryNodeSplineType output_type = (const GeometryNodeSplineType)storage.spline_type;
+
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
+ Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
+
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ if (!geometry_set.has_curve()) {
+ return;
+ }
+
+ const CurveComponent *curve_component = geometry_set.get_component_for_read<CurveComponent>();
+ const CurveEval &curve = *curve_component->get_for_read();
+ GeometryComponentFieldContext field_context{*curve_component, ATTR_DOMAIN_CURVE};
+ const int domain_size = curve_component->attribute_domain_size(ATTR_DOMAIN_CURVE);
+
+ fn::FieldEvaluator selection_evaluator{field_context, domain_size};
+ selection_evaluator.add(selection_field);
+ selection_evaluator.evaluate();
+ const VArray<bool> &selection = selection_evaluator.get_evaluated<bool>(0);
+
+ std::unique_ptr<CurveEval> new_curve = std::make_unique<CurveEval>();
+ new_curve->resize(curve.splines().size());
+
+ threading::parallel_for(curve.splines().index_range(), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ if (selection[i]) {
+ switch (output_type) {
+ case GEO_NODE_SPLINE_TYPE_POLY:
+ new_curve->splines()[i] = convert_to_poly_spline(*curve.splines()[i]);
+ break;
+ case GEO_NODE_SPLINE_TYPE_BEZIER:
+ new_curve->splines()[i] = convert_to_bezier(*curve.splines()[i], params);
+ break;
+ case GEO_NODE_SPLINE_TYPE_NURBS:
+ new_curve->splines()[i] = convert_to_nurbs(*curve.splines()[i]);
+ break;
+ }
+ }
+ else {
+ new_curve->splines()[i] = curve.splines()[i]->copy();
+ }
+ }
+ });
+ new_curve->attributes = curve.attributes;
+ geometry_set.replace_curve(new_curve.release());
+ });
+
+ params.set_output("Curve", std::move(geometry_set));
+}
+
+} // namespace blender::nodes::node_geo_curve_spline_type_cc
+
+void register_node_type_geo_curve_spline_type()
+{
+ namespace file_ns = blender::nodes::node_geo_curve_spline_type_cc;
+
+ static bNodeType ntype;
+ geo_node_type_base(&ntype, GEO_NODE_CURVE_SPLINE_TYPE, "Set Spline Type", NODE_CLASS_GEOMETRY);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ node_type_init(&ntype, file_ns::node_init);
+ node_type_storage(&ntype,
+ "NodeGeometryCurveSplineType",
+ node_free_standard_storage,
+ node_copy_standard_storage);
+ ntype.draw_buttons = file_ns::node_layout;
+
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc
new file mode 100644
index 00000000000..ae282017e0c
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc
@@ -0,0 +1,366 @@
+/*
+ * 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 "BLI_timeit.hh"
+
+#include "BKE_attribute_math.hh"
+#include "BKE_spline.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_curve_subdivide_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE);
+ b.add_input<decl::Int>(N_("Cuts")).default_value(1).min(0).max(1000).supports_field();
+ b.add_output<decl::Geometry>(N_("Curve"));
+}
+
+static Array<int> get_subdivided_offsets(const Spline &spline,
+ const VArray<int> &cuts,
+ const int spline_offset)
+{
+ Array<int> offsets(spline.segments_size() + 1);
+ int offset = 0;
+ for (const int i : IndexRange(spline.segments_size())) {
+ offsets[i] = offset;
+ offset = offset + std::max(cuts[spline_offset + i], 0) + 1;
+ }
+ offsets.last() = offset;
+ return offsets;
+}
+
+template<typename T>
+static void subdivide_attribute(Span<T> src,
+ const Span<int> offsets,
+ const bool is_cyclic,
+ MutableSpan<T> dst)
+{
+ const int src_size = src.size();
+ threading::parallel_for(IndexRange(src_size - 1), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ const int cuts = offsets[i + 1] - offsets[i];
+ dst[offsets[i]] = src[i];
+ const float factor_delta = cuts == 0 ? 1.0f : 1.0f / cuts;
+ for (const int cut : IndexRange(cuts)) {
+ const float factor = cut * factor_delta;
+ dst[offsets[i] + cut] = attribute_math::mix2(factor, src[i], src[i + 1]);
+ }
+ }
+ });
+
+ if (is_cyclic) {
+ const int i = src_size - 1;
+ const int cuts = offsets[i + 1] - offsets[i];
+ dst[offsets[i]] = src.last();
+ const float factor_delta = cuts == 0 ? 1.0f : 1.0f / cuts;
+ for (const int cut : IndexRange(cuts)) {
+ const float factor = cut * factor_delta;
+ dst[offsets[i] + cut] = attribute_math::mix2(factor, src.last(), src.first());
+ }
+ }
+ else {
+ dst.last() = src.last();
+ }
+}
+
+/**
+ * In order to generate a Bezier spline with the same shape as the input spline, apply the
+ * De Casteljau algorithm iteratively for the provided number of cuts, constantly updating the
+ * previous result point's right handle and the left handle at the end of the segment.
+ *
+ * \note Non-vector segments in the result spline are given free handles. This could possibly be
+ * improved with another pass that sets handles to aligned where possible, but currently that does
+ * not provide much benefit for the increased complexity.
+ */
+static void subdivide_bezier_segment(const BezierSpline &src,
+ const int index,
+ const int offset,
+ const int result_size,
+ Span<float3> src_positions,
+ Span<float3> src_handles_left,
+ Span<float3> src_handles_right,
+ MutableSpan<float3> dst_positions,
+ MutableSpan<float3> dst_handles_left,
+ MutableSpan<float3> dst_handles_right,
+ MutableSpan<BezierSpline::HandleType> dst_type_left,
+ MutableSpan<BezierSpline::HandleType> dst_type_right)
+{
+ const bool is_last_cyclic_segment = index == (src.size() - 1);
+ const int next_index = is_last_cyclic_segment ? 0 : index + 1;
+
+ /* The first point in the segment is always copied. */
+ dst_positions[offset] = src_positions[index];
+
+ if (src.segment_is_vector(index)) {
+ if (is_last_cyclic_segment) {
+ dst_type_left.first() = BezierSpline::HandleType::Vector;
+ }
+ dst_type_left.slice(offset + 1, result_size).fill(BezierSpline::HandleType::Vector);
+ dst_type_right.slice(offset, result_size).fill(BezierSpline::HandleType::Vector);
+
+ const float factor_delta = 1.0f / result_size;
+ for (const int cut : IndexRange(result_size)) {
+ const float factor = cut * factor_delta;
+ dst_positions[offset + cut] = attribute_math::mix2(
+ factor, src_positions[index], src_positions[next_index]);
+ }
+ }
+ else {
+ if (is_last_cyclic_segment) {
+ dst_type_left.first() = BezierSpline::HandleType::Free;
+ }
+ dst_type_left.slice(offset + 1, result_size).fill(BezierSpline::HandleType::Free);
+ dst_type_right.slice(offset, result_size).fill(BezierSpline::HandleType::Free);
+
+ const int i_segment_last = is_last_cyclic_segment ? 0 : offset + result_size;
+
+ /* Create a Bezier segment to update iteratively for every subdivision
+ * and references to the meaningful values for ease of use. */
+ BezierSpline temp;
+ temp.resize(2);
+ float3 &segment_start = temp.positions().first();
+ float3 &segment_end = temp.positions().last();
+ float3 &handle_prev = temp.handle_positions_right().first();
+ float3 &handle_next = temp.handle_positions_left().last();
+ segment_start = src_positions[index];
+ segment_end = src_positions[next_index];
+ handle_prev = src_handles_right[index];
+ handle_next = src_handles_left[next_index];
+
+ for (const int cut : IndexRange(result_size - 1)) {
+ const float parameter = 1.0f / (result_size - cut);
+ const BezierSpline::InsertResult insert = temp.calculate_segment_insertion(0, 1, parameter);
+
+ /* Copy relevant temporary data to the result. */
+ dst_handles_right[offset + cut] = insert.handle_prev;
+ dst_handles_left[offset + cut + 1] = insert.left_handle;
+ dst_positions[offset + cut + 1] = insert.position;
+
+ /* Update the segment to prepare it for the next subdivision. */
+ segment_start = insert.position;
+ handle_prev = insert.right_handle;
+ handle_next = insert.handle_next;
+ }
+
+ /* Copy the handles for the last segment from the temporary spline. */
+ dst_handles_right[offset + result_size - 1] = handle_prev;
+ dst_handles_left[i_segment_last] = handle_next;
+ }
+}
+
+static void subdivide_bezier_spline(const BezierSpline &src,
+ const Span<int> offsets,
+ BezierSpline &dst)
+{
+ Span<float3> src_positions = src.positions();
+ Span<float3> src_handles_left = src.handle_positions_left();
+ Span<float3> src_handles_right = src.handle_positions_right();
+ MutableSpan<float3> dst_positions = dst.positions();
+ MutableSpan<float3> dst_handles_left = dst.handle_positions_left();
+ MutableSpan<float3> dst_handles_right = dst.handle_positions_right();
+ MutableSpan<BezierSpline::HandleType> dst_type_left = dst.handle_types_left();
+ MutableSpan<BezierSpline::HandleType> dst_type_right = dst.handle_types_right();
+
+ threading::parallel_for(IndexRange(src.size() - 1), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ subdivide_bezier_segment(src,
+ i,
+ offsets[i],
+ offsets[i + 1] - offsets[i],
+ src_positions,
+ src_handles_left,
+ src_handles_right,
+ dst_positions,
+ dst_handles_left,
+ dst_handles_right,
+ dst_type_left,
+ dst_type_right);
+ }
+ });
+
+ if (src.is_cyclic()) {
+ const int i_last = src.size() - 1;
+ subdivide_bezier_segment(src,
+ i_last,
+ offsets[i_last],
+ offsets.last() - offsets[i_last],
+ src_positions,
+ src_handles_left,
+ src_handles_right,
+ dst_positions,
+ dst_handles_left,
+ dst_handles_right,
+ dst_type_left,
+ dst_type_right);
+ }
+ else {
+ dst_positions.last() = src_positions.last();
+ }
+}
+
+static void subdivide_builtin_attributes(const Spline &src_spline,
+ const Span<int> offsets,
+ Spline &dst_spline)
+{
+ const bool is_cyclic = src_spline.is_cyclic();
+ subdivide_attribute<float>(src_spline.radii(), offsets, is_cyclic, dst_spline.radii());
+ subdivide_attribute<float>(src_spline.tilts(), offsets, is_cyclic, dst_spline.tilts());
+ switch (src_spline.type()) {
+ case Spline::Type::Poly: {
+ const PolySpline &src = static_cast<const PolySpline &>(src_spline);
+ PolySpline &dst = static_cast<PolySpline &>(dst_spline);
+ subdivide_attribute<float3>(src.positions(), offsets, is_cyclic, dst.positions());
+ break;
+ }
+ case Spline::Type::Bezier: {
+ const BezierSpline &src = static_cast<const BezierSpline &>(src_spline);
+ BezierSpline &dst = static_cast<BezierSpline &>(dst_spline);
+ subdivide_bezier_spline(src, offsets, dst);
+ dst.mark_cache_invalid();
+ break;
+ }
+ case Spline::Type::NURBS: {
+ const NURBSpline &src = static_cast<const NURBSpline &>(src_spline);
+ NURBSpline &dst = static_cast<NURBSpline &>(dst_spline);
+ subdivide_attribute<float3>(src.positions(), offsets, is_cyclic, dst.positions());
+ subdivide_attribute<float>(src.weights(), offsets, is_cyclic, dst.weights());
+ break;
+ }
+ }
+}
+
+static void subdivide_dynamic_attributes(const Spline &src_spline,
+ const Span<int> offsets,
+ Spline &dst_spline)
+{
+ const bool is_cyclic = src_spline.is_cyclic();
+ src_spline.attributes.foreach_attribute(
+ [&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
+ std::optional<GSpan> src = src_spline.attributes.get_for_read(attribute_id);
+ BLI_assert(src);
+
+ if (!dst_spline.attributes.create(attribute_id, meta_data.data_type)) {
+ /* Since the source spline of the same type had the attribute, adding it should work. */
+ BLI_assert_unreachable();
+ }
+
+ std::optional<GMutableSpan> dst = dst_spline.attributes.get_for_write(attribute_id);
+ BLI_assert(dst);
+
+ attribute_math::convert_to_static_type(dst->type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ subdivide_attribute<T>(src->typed<T>(), offsets, is_cyclic, dst->typed<T>());
+ });
+ return true;
+ },
+ ATTR_DOMAIN_POINT);
+}
+
+static SplinePtr subdivide_spline(const Spline &spline,
+ const VArray<int> &cuts,
+ const int spline_offset)
+{
+ if (spline.size() <= 1) {
+ return spline.copy();
+ }
+
+ /* Since we expect to access each value many times, it should be worth it to make sure count
+ * of cuts is a real span (especially considering the note below). Using the offset at each
+ * point facilitates subdividing in parallel later. */
+ Array<int> offsets = get_subdivided_offsets(spline, cuts, spline_offset);
+ const int result_size = offsets.last() + int(!spline.is_cyclic());
+ SplinePtr new_spline = spline.copy_only_settings();
+ new_spline->resize(result_size);
+ subdivide_builtin_attributes(spline, offsets, *new_spline);
+ subdivide_dynamic_attributes(spline, offsets, *new_spline);
+ return new_spline;
+}
+
+/**
+ * \note Passing the virtual array for the entire spline is possibly quite inefficient here when
+ * the attribute was on the point domain and stored separately for each spline already, and it
+ * prevents some other optimizations like skipping splines with a single attribute value of < 1.
+ * However, it allows the node to access builtin attribute easily, so it the makes most sense this
+ * way until the attribute API is refactored.
+ */
+static std::unique_ptr<CurveEval> subdivide_curve(const CurveEval &input_curve,
+ const VArray<int> &cuts)
+{
+ const Array<int> control_point_offsets = input_curve.control_point_offsets();
+ const Span<SplinePtr> input_splines = input_curve.splines();
+
+ std::unique_ptr<CurveEval> output_curve = std::make_unique<CurveEval>();
+ output_curve->resize(input_splines.size());
+ output_curve->attributes = input_curve.attributes;
+ MutableSpan<SplinePtr> output_splines = output_curve->splines();
+
+ threading::parallel_for(input_splines.index_range(), 128, [&](IndexRange range) {
+ for (const int i : range) {
+ output_splines[i] = subdivide_spline(*input_splines[i], cuts, control_point_offsets[i]);
+ }
+ });
+
+ return output_curve;
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
+ Field<int> cuts_field = params.extract_input<Field<int>>("Cuts");
+
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ if (!geometry_set.has_curve()) {
+ return;
+ }
+
+ const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>();
+ GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
+ const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT);
+
+ fn::FieldEvaluator evaluator{field_context, domain_size};
+ evaluator.add(cuts_field);
+ evaluator.evaluate();
+ const VArray<int> &cuts = evaluator.get_evaluated<int>(0);
+
+ if (cuts.is_single() && cuts.get_internal_single() < 1) {
+ return;
+ }
+
+ std::unique_ptr<CurveEval> output_curve = subdivide_curve(*component.get_for_read(), cuts);
+ geometry_set.replace_curve(output_curve.release());
+ });
+ params.set_output("Curve", geometry_set);
+}
+
+} // namespace blender::nodes::node_geo_curve_subdivide_cc
+
+void register_node_type_geo_curve_subdivide()
+{
+ namespace file_ns = blender::nodes::node_geo_curve_subdivide_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_SUBDIVIDE_CURVE, "Subdivide Curve", NODE_CLASS_GEOMETRY);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc
index f7cef9bbf63..ef4fc51d1b3 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc
@@ -23,71 +23,65 @@
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_curve_to_mesh_cc {
-static void geo_node_curve_to_mesh_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Curve");
- b.add_input<decl::Geometry>("Profile Curve");
- b.add_output<decl::Geometry>("Mesh");
+ b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE);
+ b.add_input<decl::Geometry>(N_("Profile Curve"))
+ .only_realized_data()
+ .supported_type(GEO_COMPONENT_TYPE_CURVE);
+ b.add_input<decl::Bool>(N_("Fill Caps"))
+ .description(
+ N_("If the profile spline is cyclic, fill the ends of the generated mesh with N-gons"));
+ b.add_output<decl::Geometry>(N_("Mesh"));
}
static void geometry_set_curve_to_mesh(GeometrySet &geometry_set,
const GeometrySet &profile_set,
- const GeoNodeExecParams &params)
+ const bool fill_caps)
{
- if (!geometry_set.has_curve()) {
- if (!geometry_set.is_empty()) {
- params.error_message_add(NodeWarningType::Warning,
- TIP_("No curve data available in curve input"));
- }
- return;
- }
-
+ const CurveEval *curve = geometry_set.get_curve_for_read();
const CurveEval *profile_curve = profile_set.get_curve_for_read();
if (profile_curve == nullptr) {
- Mesh *mesh = bke::curve_to_wire_mesh(*geometry_set.get_curve_for_read());
+ Mesh *mesh = bke::curve_to_wire_mesh(*curve);
geometry_set.replace_mesh(mesh);
}
else {
- Mesh *mesh = bke::curve_to_mesh_sweep(*geometry_set.get_curve_for_read(), *profile_curve);
+ Mesh *mesh = bke::curve_to_mesh_sweep(*curve, *profile_curve, fill_caps);
geometry_set.replace_mesh(mesh);
}
}
-static void geo_node_curve_to_mesh_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet curve_set = params.extract_input<GeometrySet>("Curve");
GeometrySet profile_set = params.extract_input<GeometrySet>("Profile Curve");
+ const bool fill_caps = params.extract_input<bool>("Fill Caps");
- if (profile_set.has_instances()) {
- params.error_message_add(NodeWarningType::Error,
- TIP_("Instances are not supported in the profile input"));
- params.set_output("Mesh", GeometrySet());
- return;
- }
-
- if (!profile_set.has_curve() && !profile_set.is_empty()) {
- params.error_message_add(NodeWarningType::Warning,
- TIP_("No curve data available in the profile input"));
- }
-
+ bool has_curve = false;
curve_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
- geometry_set_curve_to_mesh(geometry_set, profile_set, params);
+ if (geometry_set.has_curve()) {
+ has_curve = true;
+ geometry_set_curve_to_mesh(geometry_set, profile_set, fill_caps);
+ }
+ geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES});
});
params.set_output("Mesh", std::move(curve_set));
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_curve_to_mesh_cc
void register_node_type_geo_curve_to_mesh()
{
+ namespace file_ns = blender::nodes::node_geo_curve_to_mesh_cc;
+
static bNodeType ntype;
- geo_node_type_base(&ntype, GEO_NODE_CURVE_TO_MESH, "Curve to Mesh", NODE_CLASS_GEOMETRY, 0);
- ntype.declare = blender::nodes::geo_node_curve_to_mesh_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_curve_to_mesh_exec;
+ geo_node_type_base(&ntype, GEO_NODE_CURVE_TO_MESH, "Curve to Mesh", NODE_CLASS_GEOMETRY);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc
new file mode 100644
index 00000000000..19efd4b7508
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc
@@ -0,0 +1,414 @@
+/*
+ * 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_array.hh"
+#include "BLI_task.hh"
+#include "BLI_timeit.hh"
+
+#include "BKE_pointcloud.h"
+#include "BKE_spline.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes {
+void curve_create_default_rotation_attribute(Span<float3> tangents,
+ Span<float3> normals,
+ MutableSpan<float3> rotations)
+{
+ threading::parallel_for(IndexRange(rotations.size()), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ rotations[i] =
+ float4x4::from_normalized_axis_data({0, 0, 0}, normals[i], tangents[i]).to_euler();
+ }
+ });
+}
+} // namespace blender::nodes
+
+namespace blender::nodes::node_geo_curve_to_points_cc {
+
+NODE_STORAGE_FUNCS(NodeGeometryCurveToPoints)
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE);
+ b.add_input<decl::Int>(N_("Count"))
+ .default_value(10)
+ .min(2)
+ .max(100000)
+ .make_available(
+ [](bNode &node) { node_storage(node).mode = GEO_NODE_CURVE_RESAMPLE_COUNT; });
+ b.add_input<decl::Float>(N_("Length"))
+ .default_value(0.1f)
+ .min(0.001f)
+ .subtype(PROP_DISTANCE)
+ .make_available(
+ [](bNode &node) { node_storage(node).mode = GEO_NODE_CURVE_RESAMPLE_LENGTH; });
+ b.add_output<decl::Geometry>(N_("Points"));
+ b.add_output<decl::Vector>(N_("Tangent")).field_source();
+ b.add_output<decl::Vector>(N_("Normal")).field_source();
+ b.add_output<decl::Vector>(N_("Rotation")).field_source();
+}
+
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "mode", 0, "", ICON_NONE);
+}
+
+static void node_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometryCurveToPoints *data = MEM_cnew<NodeGeometryCurveToPoints>(__func__);
+
+ data->mode = GEO_NODE_CURVE_RESAMPLE_COUNT;
+ node->storage = data;
+}
+
+static void node_update(bNodeTree *ntree, bNode *node)
+{
+ const NodeGeometryCurveToPoints &storage = node_storage(*node);
+ const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)storage.mode;
+
+ bNodeSocket *count_socket = ((bNodeSocket *)node->inputs.first)->next;
+ bNodeSocket *length_socket = count_socket->next;
+
+ nodeSetSocketAvailability(ntree, count_socket, mode == GEO_NODE_CURVE_RESAMPLE_COUNT);
+ nodeSetSocketAvailability(ntree, length_socket, mode == GEO_NODE_CURVE_RESAMPLE_LENGTH);
+}
+
+static Array<int> calculate_spline_point_offsets(GeoNodeExecParams &params,
+ const GeometryNodeCurveResampleMode mode,
+ const CurveEval &curve,
+ const Span<SplinePtr> splines)
+{
+ const int size = curve.splines().size();
+ switch (mode) {
+ case GEO_NODE_CURVE_RESAMPLE_COUNT: {
+ const int count = params.get_input<int>("Count");
+ if (count < 1) {
+ return {0};
+ }
+ Array<int> offsets(size + 1);
+ int offset = 0;
+ for (const int i : IndexRange(size)) {
+ offsets[i] = offset;
+ if (splines[i]->evaluated_points_size() > 0) {
+ offset += count;
+ }
+ }
+ offsets.last() = offset;
+ return offsets;
+ }
+ case GEO_NODE_CURVE_RESAMPLE_LENGTH: {
+ /* Don't allow asymptotic count increase for low resolution values. */
+ const float resolution = std::max(params.get_input<float>("Length"), 0.0001f);
+ Array<int> offsets(size + 1);
+ int offset = 0;
+ for (const int i : IndexRange(size)) {
+ offsets[i] = offset;
+ if (splines[i]->evaluated_points_size() > 0) {
+ offset += splines[i]->length() / resolution + 1;
+ }
+ }
+ offsets.last() = offset;
+ return offsets;
+ }
+ case GEO_NODE_CURVE_RESAMPLE_EVALUATED: {
+ return curve.evaluated_point_offsets();
+ }
+ }
+ BLI_assert_unreachable();
+ return {0};
+}
+
+/**
+ * \note: Relies on the fact that all attributes on point clouds are stored contiguously.
+ */
+static GMutableSpan ensure_point_attribute(PointCloudComponent &points,
+ const AttributeIDRef &attribute_id,
+ const CustomDataType data_type)
+{
+ points.attribute_try_create(attribute_id, ATTR_DOMAIN_POINT, data_type, AttributeInitDefault());
+ WriteAttributeLookup attribute = points.attribute_try_get_for_write(attribute_id);
+ BLI_assert(attribute);
+ return attribute.varray.get_internal_span();
+}
+
+template<typename T>
+static MutableSpan<T> ensure_point_attribute(PointCloudComponent &points,
+ const AttributeIDRef &attribute_id)
+{
+ GMutableSpan attribute = ensure_point_attribute(
+ points, attribute_id, bke::cpp_type_to_custom_data_type(CPPType::get<T>()));
+ return attribute.typed<T>();
+}
+
+namespace {
+struct AnonymousAttributeIDs {
+ StrongAnonymousAttributeID tangent_id;
+ StrongAnonymousAttributeID normal_id;
+ StrongAnonymousAttributeID rotation_id;
+};
+
+struct ResultAttributes {
+ MutableSpan<float3> positions;
+ MutableSpan<float> radii;
+
+ Map<AttributeIDRef, GMutableSpan> point_attributes;
+
+ MutableSpan<float3> tangents;
+ MutableSpan<float3> normals;
+ MutableSpan<float3> rotations;
+};
+} // namespace
+
+static ResultAttributes create_attributes_for_transfer(PointCloudComponent &points,
+ const CurveEval &curve,
+ const AnonymousAttributeIDs &attributes)
+{
+ ResultAttributes outputs;
+
+ outputs.positions = ensure_point_attribute<float3>(points, "position");
+ outputs.radii = ensure_point_attribute<float>(points, "radius");
+
+ if (attributes.tangent_id) {
+ outputs.tangents = ensure_point_attribute<float3>(points, attributes.tangent_id.get());
+ }
+ if (attributes.normal_id) {
+ outputs.normals = ensure_point_attribute<float3>(points, attributes.normal_id.get());
+ }
+ if (attributes.rotation_id) {
+ outputs.rotations = ensure_point_attribute<float3>(points, attributes.rotation_id.get());
+ }
+
+ /* Because of the invariants of the curve component, we use the attributes of the first spline
+ * as a representative for the attribute meta data all splines. Attributes from the spline domain
+ * are handled separately. */
+ curve.splines().first()->attributes.foreach_attribute(
+ [&](const AttributeIDRef &id, const AttributeMetaData &meta_data) {
+ if (id.should_be_kept()) {
+ outputs.point_attributes.add_new(
+ id, ensure_point_attribute(points, id, meta_data.data_type));
+ }
+ return true;
+ },
+ ATTR_DOMAIN_POINT);
+
+ return outputs;
+}
+
+/**
+ * TODO: For non-poly splines, this has double copies that could be avoided as part
+ * of a general look at optimizing uses of #Spline::interpolate_to_evaluated.
+ */
+static void copy_evaluated_point_attributes(const Span<SplinePtr> splines,
+ const Span<int> offsets,
+ ResultAttributes &data)
+{
+ threading::parallel_for(splines.index_range(), 64, [&](IndexRange range) {
+ for (const int i : range) {
+ const Spline &spline = *splines[i];
+ const int offset = offsets[i];
+ const int size = offsets[i + 1] - offsets[i];
+
+ data.positions.slice(offset, size).copy_from(spline.evaluated_positions());
+ spline.interpolate_to_evaluated(spline.radii()).materialize(data.radii.slice(offset, size));
+
+ for (const Map<AttributeIDRef, GMutableSpan>::Item item : data.point_attributes.items()) {
+ const AttributeIDRef attribute_id = item.key;
+ const GMutableSpan dst = item.value;
+
+ BLI_assert(spline.attributes.get_for_read(attribute_id));
+ GSpan spline_span = *spline.attributes.get_for_read(attribute_id);
+
+ spline.interpolate_to_evaluated(spline_span).materialize(dst.slice(offset, size).data());
+ }
+
+ if (!data.tangents.is_empty()) {
+ data.tangents.slice(offset, size).copy_from(spline.evaluated_tangents());
+ }
+ if (!data.normals.is_empty()) {
+ data.normals.slice(offset, size).copy_from(spline.evaluated_normals());
+ }
+ }
+ });
+}
+
+static void copy_uniform_sample_point_attributes(const Span<SplinePtr> splines,
+ const Span<int> offsets,
+ ResultAttributes &data)
+{
+ threading::parallel_for(splines.index_range(), 64, [&](IndexRange range) {
+ for (const int i : range) {
+ const Spline &spline = *splines[i];
+ const int offset = offsets[i];
+ const int size = offsets[i + 1] - offsets[i];
+ if (size == 0) {
+ continue;
+ }
+
+ const Array<float> uniform_samples = spline.sample_uniform_index_factors(size);
+
+ spline.sample_with_index_factors<float3>(
+ spline.evaluated_positions(), uniform_samples, data.positions.slice(offset, size));
+ spline.sample_with_index_factors<float>(spline.interpolate_to_evaluated(spline.radii()),
+ uniform_samples,
+ data.radii.slice(offset, size));
+
+ for (const Map<AttributeIDRef, GMutableSpan>::Item item : data.point_attributes.items()) {
+ const AttributeIDRef attribute_id = item.key;
+ const GMutableSpan dst = item.value;
+
+ BLI_assert(spline.attributes.get_for_read(attribute_id));
+ GSpan spline_span = *spline.attributes.get_for_read(attribute_id);
+
+ spline.sample_with_index_factors(spline.interpolate_to_evaluated(spline_span),
+ uniform_samples,
+ dst.slice(offset, size));
+ }
+
+ if (!data.tangents.is_empty()) {
+ spline.sample_with_index_factors<float3>(
+ spline.evaluated_tangents(), uniform_samples, data.tangents.slice(offset, size));
+ for (float3 &tangent : data.tangents) {
+ tangent = math::normalize(tangent);
+ }
+ }
+
+ if (!data.normals.is_empty()) {
+ spline.sample_with_index_factors<float3>(
+ spline.evaluated_normals(), uniform_samples, data.normals.slice(offset, size));
+ for (float3 &normals : data.normals) {
+ normals = math::normalize(normals);
+ }
+ }
+ }
+ });
+}
+
+static void copy_spline_domain_attributes(const CurveEval &curve,
+ const Span<int> offsets,
+ PointCloudComponent &points)
+{
+ curve.attributes.foreach_attribute(
+ [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
+ const GSpan curve_attribute = *curve.attributes.get_for_read(attribute_id);
+ const CPPType &type = curve_attribute.type();
+ const GMutableSpan dst = ensure_point_attribute(points, attribute_id, meta_data.data_type);
+
+ for (const int i : curve.splines().index_range()) {
+ const int offset = offsets[i];
+ const int size = offsets[i + 1] - offsets[i];
+ type.fill_assign_n(curve_attribute[i], dst[offset], size);
+ }
+
+ return true;
+ },
+ ATTR_DOMAIN_CURVE);
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ const NodeGeometryCurveToPoints &storage = node_storage(params.node());
+ const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)storage.mode;
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
+
+ AnonymousAttributeIDs attribute_outputs;
+ attribute_outputs.tangent_id = StrongAnonymousAttributeID("Tangent");
+ attribute_outputs.normal_id = StrongAnonymousAttributeID("Normal");
+ attribute_outputs.rotation_id = StrongAnonymousAttributeID("Rotation");
+
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ if (!geometry_set.has_curve()) {
+ geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ return;
+ }
+ const CurveEval &curve = *geometry_set.get_curve_for_read();
+ const Span<SplinePtr> splines = curve.splines();
+ curve.assert_valid_point_attributes();
+
+ const Array<int> offsets = calculate_spline_point_offsets(params, mode, curve, splines);
+ const int total_size = offsets.last();
+ if (total_size == 0) {
+ geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ return;
+ }
+
+ geometry_set.replace_pointcloud(BKE_pointcloud_new_nomain(total_size));
+ PointCloudComponent &points = geometry_set.get_component_for_write<PointCloudComponent>();
+ ResultAttributes point_attributes = create_attributes_for_transfer(
+ points, curve, attribute_outputs);
+
+ switch (mode) {
+ case GEO_NODE_CURVE_RESAMPLE_COUNT:
+ case GEO_NODE_CURVE_RESAMPLE_LENGTH:
+ copy_uniform_sample_point_attributes(splines, offsets, point_attributes);
+ break;
+ case GEO_NODE_CURVE_RESAMPLE_EVALUATED:
+ copy_evaluated_point_attributes(splines, offsets, point_attributes);
+ break;
+ }
+
+ copy_spline_domain_attributes(curve, offsets, points);
+
+ if (!point_attributes.rotations.is_empty()) {
+ curve_create_default_rotation_attribute(
+ point_attributes.tangents, point_attributes.normals, point_attributes.rotations);
+ }
+
+ geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES, GEO_COMPONENT_TYPE_POINT_CLOUD});
+ });
+
+ params.set_output("Points", std::move(geometry_set));
+ if (attribute_outputs.tangent_id) {
+ params.set_output(
+ "Tangent",
+ AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.tangent_id),
+ params.attribute_producer_name()));
+ }
+ if (attribute_outputs.normal_id) {
+ params.set_output(
+ "Normal",
+ AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.normal_id),
+ params.attribute_producer_name()));
+ }
+ if (attribute_outputs.rotation_id) {
+ params.set_output(
+ "Rotation",
+ AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.rotation_id),
+ params.attribute_producer_name()));
+ }
+}
+
+} // namespace blender::nodes::node_geo_curve_to_points_cc
+
+void register_node_type_geo_curve_to_points()
+{
+ namespace file_ns = blender::nodes::node_geo_curve_to_points_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_CURVE_TO_POINTS, "Curve to Points", NODE_CLASS_GEOMETRY);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.draw_buttons = file_ns::node_layout;
+ node_type_storage(
+ &ntype, "NodeGeometryCurveToPoints", node_free_standard_storage, node_copy_standard_storage);
+ node_type_init(&ntype, file_ns::node_init);
+ node_type_update(&ntype, file_ns::node_update);
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc
index 97043980899..359863d39e0 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc
@@ -20,50 +20,105 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "NOD_socket_search_link.hh"
+
#include "node_geometry_util.hh"
+namespace blender::nodes::node_geo_curve_trim_cc {
+
using blender::attribute_math::mix2;
-namespace blender::nodes {
+NODE_STORAGE_FUNCS(NodeGeometryCurveTrim)
-static void geo_node_curve_trim_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Curve");
- b.add_input<decl::Float>("Start").min(0.0f).max(1.0f).subtype(PROP_FACTOR);
- b.add_input<decl::Float>("End").min(0.0f).max(1.0f).default_value(1.0f).subtype(PROP_FACTOR);
- b.add_input<decl::Float>("Start", "Start_001").min(0.0f).subtype(PROP_DISTANCE);
- b.add_input<decl::Float>("End", "End_001").min(0.0f).default_value(1.0f).subtype(PROP_DISTANCE);
- b.add_output<decl::Geometry>("Curve");
+ b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE);
+ b.add_input<decl::Float>(N_("Start"))
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR)
+ .make_available([](bNode &node) { node_storage(node).mode = GEO_NODE_CURVE_SAMPLE_FACTOR; })
+ .supports_field();
+ b.add_input<decl::Float>(N_("End"))
+ .min(0.0f)
+ .max(1.0f)
+ .default_value(1.0f)
+ .subtype(PROP_FACTOR)
+ .make_available([](bNode &node) { node_storage(node).mode = GEO_NODE_CURVE_SAMPLE_FACTOR; })
+ .supports_field();
+ b.add_input<decl::Float>(N_("Start"), "Start_001")
+ .min(0.0f)
+ .subtype(PROP_DISTANCE)
+ .make_available([](bNode &node) { node_storage(node).mode = GEO_NODE_CURVE_SAMPLE_LENGTH; })
+ .supports_field();
+ b.add_input<decl::Float>(N_("End"), "End_001")
+ .min(0.0f)
+ .default_value(1.0f)
+ .subtype(PROP_DISTANCE)
+ .make_available([](bNode &node) { node_storage(node).mode = GEO_NODE_CURVE_SAMPLE_LENGTH; })
+ .supports_field();
+ b.add_output<decl::Geometry>(N_("Curve"));
}
-static void geo_node_curve_trim_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
}
-static void geo_node_curve_trim_init(bNodeTree *UNUSED(tree), bNode *node)
+static void node_init(bNodeTree *UNUSED(tree), bNode *node)
{
- NodeGeometryCurveTrim *data = (NodeGeometryCurveTrim *)MEM_callocN(sizeof(NodeGeometryCurveTrim),
- __func__);
+ NodeGeometryCurveTrim *data = MEM_cnew<NodeGeometryCurveTrim>(__func__);
data->mode = GEO_NODE_CURVE_SAMPLE_FACTOR;
node->storage = data;
}
-static void geo_node_curve_trim_update(bNodeTree *UNUSED(ntree), bNode *node)
+static void node_update(bNodeTree *ntree, bNode *node)
{
- const NodeGeometryCurveTrim &node_storage = *(NodeGeometryCurveTrim *)node->storage;
- const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode;
+ const NodeGeometryCurveTrim &storage = node_storage(*node);
+ const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)storage.mode;
bNodeSocket *start_fac = ((bNodeSocket *)node->inputs.first)->next;
bNodeSocket *end_fac = start_fac->next;
bNodeSocket *start_len = end_fac->next;
bNodeSocket *end_len = start_len->next;
- nodeSetSocketAvailability(start_fac, mode == GEO_NODE_CURVE_SAMPLE_FACTOR);
- nodeSetSocketAvailability(end_fac, mode == GEO_NODE_CURVE_SAMPLE_FACTOR);
- nodeSetSocketAvailability(start_len, mode == GEO_NODE_CURVE_SAMPLE_LENGTH);
- nodeSetSocketAvailability(end_len, mode == GEO_NODE_CURVE_SAMPLE_LENGTH);
+ nodeSetSocketAvailability(ntree, start_fac, mode == GEO_NODE_CURVE_SAMPLE_FACTOR);
+ nodeSetSocketAvailability(ntree, end_fac, mode == GEO_NODE_CURVE_SAMPLE_FACTOR);
+ nodeSetSocketAvailability(ntree, start_len, mode == GEO_NODE_CURVE_SAMPLE_LENGTH);
+ nodeSetSocketAvailability(ntree, end_len, mode == GEO_NODE_CURVE_SAMPLE_LENGTH);
+}
+
+class SocketSearchOp {
+ public:
+ StringRef socket_name;
+ GeometryNodeCurveSampleMode mode;
+ void operator()(LinkSearchOpParams &params)
+ {
+ bNode &node = params.add_node("GeometryNodeTrimCurve");
+ node_storage(node).mode = mode;
+ params.update_and_connect_available_socket(node, socket_name);
+ }
+};
+
+static void node_gather_link_searches(GatherLinkSearchOpParams &params)
+{
+ const NodeDeclaration &declaration = *params.node_type().fixed_declaration;
+
+ search_link_ops_for_declarations(params, declaration.outputs());
+ search_link_ops_for_declarations(params, declaration.inputs().take_front(1));
+
+ if (params.in_out() == SOCK_IN) {
+ if (params.node_tree().typeinfo->validate_link(
+ static_cast<eNodeSocketDatatype>(params.other_socket().type), SOCK_FLOAT)) {
+ params.add_item(IFACE_("Start (Factor)"),
+ SocketSearchOp{"Start", GEO_NODE_CURVE_SAMPLE_FACTOR});
+ params.add_item(IFACE_("End (Factor)"), SocketSearchOp{"End", GEO_NODE_CURVE_SAMPLE_FACTOR});
+ params.add_item(IFACE_("Start (Length)"),
+ SocketSearchOp{"Start", GEO_NODE_CURVE_SAMPLE_LENGTH});
+ params.add_item(IFACE_("End (Length)"), SocketSearchOp{"End", GEO_NODE_CURVE_SAMPLE_LENGTH});
+ }
+ }
}
struct TrimLocation {
@@ -123,19 +178,19 @@ static void linear_trim_to_output_data(const TrimLocation &start,
/* Look up the control points to the left and right of factor, and get the factor between them. */
static TrimLocation lookup_control_point_position(const Spline::LookupResult &lookup,
- Span<int> control_point_offsets)
+ const BezierSpline &spline)
{
- const int *left_offset = std::lower_bound(
- control_point_offsets.begin(), control_point_offsets.end(), lookup.evaluated_index);
- const int index = left_offset - control_point_offsets.begin();
- const int left = control_point_offsets[index] > lookup.evaluated_index ? index - 1 : index;
- const int right = left + 1;
-
- const float factor = std::clamp(
- (lookup.evaluated_index + lookup.factor - control_point_offsets[left]) /
- (control_point_offsets[right] - control_point_offsets[left]),
- 0.0f,
- 1.0f);
+ Span<int> offsets = spline.control_point_offsets();
+
+ const int *offset = std::lower_bound(offsets.begin(), offsets.end(), lookup.evaluated_index);
+ const int index = offset - offsets.begin();
+
+ const int left = offsets[index] > lookup.evaluated_index ? index - 1 : index;
+ const int right = left == (spline.size() - 1) ? 0 : left + 1;
+
+ const float offset_in_segment = lookup.evaluated_index + lookup.factor - offsets[left];
+ const int segment_eval_size = offsets[left + 1] - offsets[left];
+ const float factor = std::clamp(offset_in_segment / segment_eval_size, 0.0f, 1.0f);
return {left, right, factor};
}
@@ -204,9 +259,9 @@ static PolySpline trim_nurbs_spline(const Spline &spline,
attribute_math::convert_to_static_type(src->type(), [&](auto dummy) {
using T = decltype(dummy);
- GVArray_Typed<T> eval_data = spline.interpolate_to_evaluated<T>(src->typed<T>());
+ VArray<T> eval_data = spline.interpolate_to_evaluated<T>(src->typed<T>());
linear_trim_to_output_data<T>(
- start, end, eval_data->get_internal_span(), dst->typed<T>());
+ start, end, eval_data.get_internal_span(), dst->typed<T>());
});
return true;
},
@@ -215,13 +270,13 @@ static PolySpline trim_nurbs_spline(const Spline &spline,
linear_trim_to_output_data<float3>(
start, end, spline.evaluated_positions(), new_spline.positions());
- GVArray_Typed<float> evaluated_radii = spline.interpolate_to_evaluated(spline.radii());
+ VArray<float> evaluated_radii = spline.interpolate_to_evaluated(spline.radii());
linear_trim_to_output_data<float>(
- start, end, evaluated_radii->get_internal_span(), new_spline.radii());
+ start, end, evaluated_radii.get_internal_span(), new_spline.radii());
- GVArray_Typed<float> evaluated_tilts = spline.interpolate_to_evaluated(spline.tilts());
+ VArray<float> evaluated_tilts = spline.interpolate_to_evaluated(spline.tilts());
linear_trim_to_output_data<float>(
- start, end, evaluated_tilts->get_internal_span(), new_spline.tilts());
+ start, end, evaluated_tilts.get_internal_span(), new_spline.tilts());
return new_spline;
}
@@ -235,10 +290,11 @@ static void trim_bezier_spline(Spline &spline,
const Spline::LookupResult &end_lookup)
{
BezierSpline &bezier_spline = static_cast<BezierSpline &>(spline);
- Span<int> control_offsets = bezier_spline.control_point_offsets();
- const TrimLocation start = lookup_control_point_position(start_lookup, control_offsets);
- TrimLocation end = lookup_control_point_position(end_lookup, control_offsets);
+ const TrimLocation start = lookup_control_point_position(start_lookup, bezier_spline);
+ TrimLocation end = lookup_control_point_position(end_lookup, bezier_spline);
+
+ const Span<int> control_offsets = bezier_spline.control_point_offsets();
/* The number of control points in the resulting spline. */
const int size = end.right_index - start.left_index + 1;
@@ -320,97 +376,245 @@ static void trim_bezier_spline(Spline &spline,
bezier_spline.resize(size);
}
+static void trim_spline(SplinePtr &spline,
+ const Spline::LookupResult start,
+ const Spline::LookupResult end)
+{
+ switch (spline->type()) {
+ case Spline::Type::Bezier:
+ trim_bezier_spline(*spline, start, end);
+ break;
+ case Spline::Type::Poly:
+ trim_poly_spline(*spline, start, end);
+ break;
+ case Spline::Type::NURBS:
+ spline = std::make_unique<PolySpline>(trim_nurbs_spline(*spline, start, end));
+ break;
+ }
+ spline->mark_cache_invalid();
+}
+
+template<typename T>
+static void to_single_point_data(const TrimLocation &trim, MutableSpan<T> data)
+{
+ data.first() = mix2<T>(trim.factor, data[trim.left_index], data[trim.right_index]);
+}
+template<typename T>
+static void to_single_point_data(const TrimLocation &trim, Span<T> src, MutableSpan<T> dst)
+{
+ dst.first() = mix2<T>(trim.factor, src[trim.left_index], src[trim.right_index]);
+}
+
+static void to_single_point_bezier(Spline &spline, const Spline::LookupResult &lookup)
+{
+ BezierSpline &bezier = static_cast<BezierSpline &>(spline);
+
+ const TrimLocation trim = lookup_control_point_position(lookup, bezier);
+
+ const BezierSpline::InsertResult new_point = bezier.calculate_segment_insertion(
+ trim.left_index, trim.right_index, trim.factor);
+ bezier.positions().first() = new_point.position;
+ bezier.handle_types_left().first() = BezierSpline::HandleType::Free;
+ bezier.handle_types_right().first() = BezierSpline::HandleType::Free;
+ bezier.handle_positions_left().first() = new_point.left_handle;
+ bezier.handle_positions_right().first() = new_point.right_handle;
+
+ to_single_point_data<float>(trim, bezier.radii());
+ to_single_point_data<float>(trim, bezier.tilts());
+ spline.attributes.foreach_attribute(
+ [&](const AttributeIDRef &attribute_id, const AttributeMetaData &UNUSED(meta_data)) {
+ std::optional<GMutableSpan> data = spline.attributes.get_for_write(attribute_id);
+ attribute_math::convert_to_static_type(data->type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ to_single_point_data<T>(trim, data->typed<T>());
+ });
+ return true;
+ },
+ ATTR_DOMAIN_POINT);
+ spline.resize(1);
+}
+
+static void to_single_point_poly(Spline &spline, const Spline::LookupResult &lookup)
+{
+ const TrimLocation trim{lookup.evaluated_index, lookup.next_evaluated_index, lookup.factor};
+
+ to_single_point_data<float3>(trim, spline.positions());
+ to_single_point_data<float>(trim, spline.radii());
+ to_single_point_data<float>(trim, spline.tilts());
+ spline.attributes.foreach_attribute(
+ [&](const AttributeIDRef &attribute_id, const AttributeMetaData &UNUSED(meta_data)) {
+ std::optional<GMutableSpan> data = spline.attributes.get_for_write(attribute_id);
+ attribute_math::convert_to_static_type(data->type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ to_single_point_data<T>(trim, data->typed<T>());
+ });
+ return true;
+ },
+ ATTR_DOMAIN_POINT);
+ spline.resize(1);
+}
+
+static PolySpline to_single_point_nurbs(const Spline &spline, const Spline::LookupResult &lookup)
+{
+ /* Since this outputs a poly spline, the evaluated indices are the control point indices. */
+ const TrimLocation trim{lookup.evaluated_index, lookup.next_evaluated_index, lookup.factor};
+
+ /* Create poly spline and copy trimmed data to it. */
+ PolySpline new_spline;
+ new_spline.resize(1);
+
+ spline.attributes.foreach_attribute(
+ [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
+ new_spline.attributes.create(attribute_id, meta_data.data_type);
+ std::optional<GSpan> src = spline.attributes.get_for_read(attribute_id);
+ std::optional<GMutableSpan> dst = new_spline.attributes.get_for_write(attribute_id);
+ attribute_math::convert_to_static_type(src->type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ VArray<T> eval_data = spline.interpolate_to_evaluated<T>(src->typed<T>());
+ to_single_point_data<T>(trim, eval_data.get_internal_span(), dst->typed<T>());
+ });
+ return true;
+ },
+ ATTR_DOMAIN_POINT);
+
+ to_single_point_data<float3>(trim, spline.evaluated_positions(), new_spline.positions());
+
+ VArray<float> evaluated_radii = spline.interpolate_to_evaluated(spline.radii());
+ to_single_point_data<float>(trim, evaluated_radii.get_internal_span(), new_spline.radii());
+
+ VArray<float> evaluated_tilts = spline.interpolate_to_evaluated(spline.tilts());
+ to_single_point_data<float>(trim, evaluated_tilts.get_internal_span(), new_spline.tilts());
+
+ return new_spline;
+}
+
+static void to_single_point_spline(SplinePtr &spline, const Spline::LookupResult &lookup)
+{
+ switch (spline->type()) {
+ case Spline::Type::Bezier:
+ to_single_point_bezier(*spline, lookup);
+ break;
+ case Spline::Type::Poly:
+ to_single_point_poly(*spline, lookup);
+ break;
+ case Spline::Type::NURBS:
+ spline = std::make_unique<PolySpline>(to_single_point_nurbs(*spline, lookup));
+ break;
+ }
+}
+
static void geometry_set_curve_trim(GeometrySet &geometry_set,
const GeometryNodeCurveSampleMode mode,
- const float start,
- const float end)
+ Field<float> &start_field,
+ Field<float> &end_field)
{
if (!geometry_set.has_curve()) {
return;
}
+ CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>();
+ GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE};
+ const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE);
+
+ fn::FieldEvaluator evaluator{field_context, domain_size};
+ evaluator.add(start_field);
+ evaluator.add(end_field);
+ evaluator.evaluate();
+ const blender::VArray<float> &starts = evaluator.get_evaluated<float>(0);
+ const blender::VArray<float> &ends = evaluator.get_evaluated<float>(1);
+
CurveEval &curve = *geometry_set.get_curve_for_write();
MutableSpan<SplinePtr> splines = curve.splines();
threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) {
for (const int i : range) {
- Spline &spline = *splines[i];
+ SplinePtr &spline = splines[i];
- /* Currently this node doesn't support cyclic splines, it could in the future though. */
- if (spline.is_cyclic()) {
+ /* Currently trimming cyclic splines is not supported. It could be in the future though. */
+ if (spline->is_cyclic()) {
continue;
}
- /* Return a spline with one point instead of implicitly
- * reversing the spline or switching the parameters. */
- if (end < start) {
- spline.resize(1);
+ if (spline->evaluated_edges_size() == 0) {
continue;
}
- const Spline::LookupResult start_lookup =
- (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) ?
- spline.lookup_evaluated_length(std::clamp(start, 0.0f, spline.length())) :
- spline.lookup_evaluated_factor(std::clamp(start, 0.0f, 1.0f));
- const Spline::LookupResult end_lookup =
- (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) ?
- spline.lookup_evaluated_length(std::clamp(end, 0.0f, spline.length())) :
- spline.lookup_evaluated_factor(std::clamp(end, 0.0f, 1.0f));
-
- switch (spline.type()) {
- case Spline::Type::Bezier:
- trim_bezier_spline(spline, start_lookup, end_lookup);
- break;
- case Spline::Type::Poly:
- trim_poly_spline(spline, start_lookup, end_lookup);
- break;
- case Spline::Type::NURBS:
- splines[i] = std::make_unique<PolySpline>(
- trim_nurbs_spline(spline, start_lookup, end_lookup));
- break;
+ const float length = spline->length();
+ if (length == 0.0f) {
+ continue;
+ }
+
+ const float start = starts[i];
+ const float end = ends[i];
+
+ /* When the start and end samples are reversed, instead of implicitly reversing the spline
+ * or switching the parameters, create a single point spline with the end sample point. */
+ if (end <= start) {
+ if (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) {
+ to_single_point_spline(spline,
+ spline->lookup_evaluated_length(std::clamp(start, 0.0f, length)));
+ }
+ else {
+ to_single_point_spline(spline,
+ spline->lookup_evaluated_factor(std::clamp(start, 0.0f, 1.0f)));
+ }
+ continue;
+ }
+
+ if (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) {
+ trim_spline(spline,
+ spline->lookup_evaluated_length(std::clamp(start, 0.0f, length)),
+ spline->lookup_evaluated_length(std::clamp(end, 0.0f, length)));
+ }
+ else {
+ trim_spline(spline,
+ spline->lookup_evaluated_factor(std::clamp(start, 0.0f, 1.0f)),
+ spline->lookup_evaluated_factor(std::clamp(end, 0.0f, 1.0f)));
}
- splines[i]->mark_cache_invalid();
}
});
}
-static void geo_node_curve_trim_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
- const NodeGeometryCurveTrim &node_storage = *(NodeGeometryCurveTrim *)params.node().storage;
- const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode;
+ const NodeGeometryCurveTrim &storage = node_storage(params.node());
+ const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)storage.mode;
GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
if (mode == GEO_NODE_CURVE_SAMPLE_FACTOR) {
- const float start = params.extract_input<float>("Start");
- const float end = params.extract_input<float>("End");
+ Field<float> start_field = params.extract_input<Field<float>>("Start");
+ Field<float> end_field = params.extract_input<Field<float>>("End");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
- geometry_set_curve_trim(geometry_set, mode, start, end);
+ geometry_set_curve_trim(geometry_set, mode, start_field, end_field);
});
}
else if (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) {
- const float start = params.extract_input<float>("Start_001");
- const float end = params.extract_input<float>("End_001");
+ Field<float> start_field = params.extract_input<Field<float>>("Start_001");
+ Field<float> end_field = params.extract_input<Field<float>>("End_001");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
- geometry_set_curve_trim(geometry_set, mode, start, end);
+ geometry_set_curve_trim(geometry_set, mode, start_field, end_field);
});
}
params.set_output("Curve", std::move(geometry_set));
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_curve_trim_cc
void register_node_type_geo_curve_trim()
{
+ namespace file_ns = blender::nodes::node_geo_curve_trim_cc;
+
static bNodeType ntype;
- geo_node_type_base(&ntype, GEO_NODE_CURVE_TRIM, "Curve Trim", NODE_CLASS_GEOMETRY, 0);
- ntype.geometry_node_execute = blender::nodes::geo_node_curve_trim_exec;
- ntype.draw_buttons = blender::nodes::geo_node_curve_trim_layout;
- ntype.declare = blender::nodes::geo_node_curve_trim_declare;
+ geo_node_type_base(&ntype, GEO_NODE_TRIM_CURVE, "Trim Curve", NODE_CLASS_GEOMETRY);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.draw_buttons = file_ns::node_layout;
+ ntype.declare = file_ns::node_declare;
node_type_storage(
&ntype, "NodeGeometryCurveTrim", node_free_standard_storage, node_copy_standard_storage);
- node_type_init(&ntype, blender::nodes::geo_node_curve_trim_init);
- node_type_update(&ntype, blender::nodes::geo_node_curve_trim_update);
+ node_type_init(&ntype, file_ns::node_init);
+ node_type_update(&ntype, file_ns::node_update);
+ ntype.gather_link_search_ops = file_ns::node_gather_link_searches;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc
new file mode 100644
index 00000000000..8b762abd29b
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc
@@ -0,0 +1,1399 @@
+/*
+ * 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 "UI_interface.h"
+#include "UI_resources.h"
+
+#include "BLI_array.hh"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_customdata.h"
+#include "BKE_mesh.h"
+#include "BKE_pointcloud.h"
+#include "BKE_spline.hh"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_delete_geometry_cc {
+
+using blender::bke::CustomDataAttributes;
+
+template<typename T>
+static void copy_data_based_on_mask(Span<T> data, MutableSpan<T> r_data, IndexMask mask)
+{
+ for (const int i_out : mask.index_range()) {
+ r_data[i_out] = data[mask[i_out]];
+ }
+}
+
+template<typename T>
+static void copy_data_based_on_map(Span<T> src, MutableSpan<T> dst, Span<int> index_map)
+{
+ for (const int i_src : index_map.index_range()) {
+ const int i_dst = index_map[i_src];
+ if (i_dst != -1) {
+ dst[i_dst] = src[i_src];
+ }
+ }
+}
+
+/** Utility function for making an IndexMask from a boolean selection. The indices vector should
+ * live at least as long as the returned IndexMask.
+ */
+static IndexMask index_mask_indices(Span<bool> mask, const bool invert, Vector<int64_t> &indices)
+{
+ for (const int i : mask.index_range()) {
+ if (mask[i] != invert) {
+ indices.append(i);
+ }
+ }
+ return IndexMask(indices);
+}
+
+/**
+ * Copies the attributes with a domain in `domains` to `result_component`.
+ */
+static void copy_attributes(const Map<AttributeIDRef, AttributeKind> &attributes,
+ const GeometryComponent &in_component,
+ GeometryComponent &result_component,
+ const Span<AttributeDomain> domains)
+{
+ for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
+ const AttributeIDRef attribute_id = entry.key;
+ ReadAttributeLookup attribute = in_component.attribute_try_get_for_read(attribute_id);
+ if (!attribute) {
+ continue;
+ }
+
+ /* Only copy if it is on a domain we want. */
+ if (!domains.contains(attribute.domain)) {
+ continue;
+ }
+ const CustomDataType data_type = bke::cpp_type_to_custom_data_type(attribute.varray.type());
+
+ OutputAttribute result_attribute = result_component.attribute_try_get_for_output_only(
+ attribute_id, attribute.domain, data_type);
+
+ if (!result_attribute) {
+ continue;
+ }
+
+ attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ using T = decltype(dummy);
+ VArray_Span<T> span{attribute.varray.typed<T>()};
+ MutableSpan<T> out_span = result_attribute.as_span<T>();
+ out_span.copy_from(span);
+ });
+ result_attribute.save();
+ }
+}
+
+/**
+ * For each attribute with a domain in `domains` it copies the parts of that attribute which lie in
+ * the mask to `result_component`.
+ */
+static void copy_attributes_based_on_mask(const Map<AttributeIDRef, AttributeKind> &attributes,
+ const GeometryComponent &in_component,
+ GeometryComponent &result_component,
+ const AttributeDomain domain,
+ const IndexMask mask)
+{
+ for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
+ const AttributeIDRef attribute_id = entry.key;
+ ReadAttributeLookup attribute = in_component.attribute_try_get_for_read(attribute_id);
+ if (!attribute) {
+ continue;
+ }
+
+ /* Only copy if it is on a domain we want. */
+ if (domain != attribute.domain) {
+ continue;
+ }
+ const CustomDataType data_type = bke::cpp_type_to_custom_data_type(attribute.varray.type());
+
+ OutputAttribute result_attribute = result_component.attribute_try_get_for_output_only(
+ attribute_id, attribute.domain, data_type);
+
+ if (!result_attribute) {
+ continue;
+ }
+
+ attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ using T = decltype(dummy);
+ VArray_Span<T> span{attribute.varray.typed<T>()};
+ MutableSpan<T> out_span = result_attribute.as_span<T>();
+ copy_data_based_on_mask(span, out_span, mask);
+ });
+ result_attribute.save();
+ }
+}
+
+static void copy_attributes_based_on_map(const Map<AttributeIDRef, AttributeKind> &attributes,
+ const GeometryComponent &in_component,
+ GeometryComponent &result_component,
+ const AttributeDomain domain,
+ const Span<int> index_map)
+{
+ for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
+ const AttributeIDRef attribute_id = entry.key;
+ ReadAttributeLookup attribute = in_component.attribute_try_get_for_read(attribute_id);
+ if (!attribute) {
+ continue;
+ }
+
+ /* Only copy if it is on a domain we want. */
+ if (domain != attribute.domain) {
+ continue;
+ }
+ const CustomDataType data_type = bke::cpp_type_to_custom_data_type(attribute.varray.type());
+
+ OutputAttribute result_attribute = result_component.attribute_try_get_for_output_only(
+ attribute_id, attribute.domain, data_type);
+
+ if (!result_attribute) {
+ continue;
+ }
+
+ attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ using T = decltype(dummy);
+ VArray_Span<T> span{attribute.varray.typed<T>()};
+ MutableSpan<T> out_span = result_attribute.as_span<T>();
+ copy_data_based_on_map(span, out_span, index_map);
+ });
+ result_attribute.save();
+ }
+}
+
+static void copy_face_corner_attributes(const Map<AttributeIDRef, AttributeKind> &attributes,
+ const GeometryComponent &in_component,
+ GeometryComponent &out_component,
+ const int num_selected_loops,
+ const Span<int> selected_poly_indices,
+ const Mesh &mesh_in)
+{
+ Vector<int64_t> indices;
+ indices.reserve(num_selected_loops);
+ for (const int src_poly_index : selected_poly_indices) {
+ const MPoly &src_poly = mesh_in.mpoly[src_poly_index];
+ const int src_loop_start = src_poly.loopstart;
+ const int tot_loop = src_poly.totloop;
+ for (const int i : IndexRange(tot_loop)) {
+ indices.append_unchecked(src_loop_start + i);
+ }
+ }
+ copy_attributes_based_on_mask(
+ attributes, in_component, out_component, ATTR_DOMAIN_CORNER, IndexMask(indices));
+}
+
+static void copy_masked_vertices_to_new_mesh(const Mesh &src_mesh,
+ Mesh &dst_mesh,
+ Span<int> vertex_map)
+{
+ BLI_assert(src_mesh.totvert == vertex_map.size());
+ for (const int i_src : vertex_map.index_range()) {
+ const int i_dst = vertex_map[i_src];
+ if (i_dst == -1) {
+ continue;
+ }
+
+ const MVert &v_src = src_mesh.mvert[i_src];
+ MVert &v_dst = dst_mesh.mvert[i_dst];
+
+ v_dst = v_src;
+ }
+}
+
+static void copy_masked_edges_to_new_mesh(const Mesh &src_mesh, Mesh &dst_mesh, Span<int> edge_map)
+{
+ BLI_assert(src_mesh.totedge == edge_map.size());
+ for (const int i_src : IndexRange(src_mesh.totedge)) {
+ const int i_dst = edge_map[i_src];
+ if (ELEM(i_dst, -1, -2)) {
+ continue;
+ }
+
+ const MEdge &e_src = src_mesh.medge[i_src];
+ MEdge &e_dst = dst_mesh.medge[i_dst];
+
+ e_dst = e_src;
+ e_dst.v1 = e_src.v1;
+ e_dst.v2 = e_src.v2;
+ }
+}
+
+static void copy_masked_edges_to_new_mesh(const Mesh &src_mesh,
+ Mesh &dst_mesh,
+ Span<int> vertex_map,
+ Span<int> edge_map)
+{
+ BLI_assert(src_mesh.totvert == vertex_map.size());
+ BLI_assert(src_mesh.totedge == edge_map.size());
+ for (const int i_src : IndexRange(src_mesh.totedge)) {
+ const int i_dst = edge_map[i_src];
+ if (i_dst == -1) {
+ continue;
+ }
+
+ const MEdge &e_src = src_mesh.medge[i_src];
+ MEdge &e_dst = dst_mesh.medge[i_dst];
+
+ e_dst = e_src;
+ e_dst.v1 = vertex_map[e_src.v1];
+ e_dst.v2 = vertex_map[e_src.v2];
+ }
+}
+
+/* Faces and edges changed but vertices are the same. */
+static void copy_masked_polys_to_new_mesh(const Mesh &src_mesh,
+ Mesh &dst_mesh,
+ Span<int> edge_map,
+ Span<int> masked_poly_indices,
+ Span<int> new_loop_starts)
+{
+ for (const int i_dst : masked_poly_indices.index_range()) {
+ const int i_src = masked_poly_indices[i_dst];
+
+ const MPoly &mp_src = src_mesh.mpoly[i_src];
+ MPoly &mp_dst = dst_mesh.mpoly[i_dst];
+ const int i_ml_src = mp_src.loopstart;
+ const int i_ml_dst = new_loop_starts[i_dst];
+
+ const MLoop *ml_src = src_mesh.mloop + i_ml_src;
+ MLoop *ml_dst = dst_mesh.mloop + i_ml_dst;
+
+ mp_dst = mp_src;
+ mp_dst.loopstart = i_ml_dst;
+ for (int i : IndexRange(mp_src.totloop)) {
+ ml_dst[i].v = ml_src[i].v;
+ ml_dst[i].e = edge_map[ml_src[i].e];
+ }
+ }
+}
+
+/* Only faces changed. */
+static void copy_masked_polys_to_new_mesh(const Mesh &src_mesh,
+ Mesh &dst_mesh,
+ Span<int> masked_poly_indices,
+ Span<int> new_loop_starts)
+{
+ for (const int i_dst : masked_poly_indices.index_range()) {
+ const int i_src = masked_poly_indices[i_dst];
+
+ const MPoly &mp_src = src_mesh.mpoly[i_src];
+ MPoly &mp_dst = dst_mesh.mpoly[i_dst];
+ const int i_ml_src = mp_src.loopstart;
+ const int i_ml_dst = new_loop_starts[i_dst];
+
+ const MLoop *ml_src = src_mesh.mloop + i_ml_src;
+ MLoop *ml_dst = dst_mesh.mloop + i_ml_dst;
+
+ mp_dst = mp_src;
+ mp_dst.loopstart = i_ml_dst;
+ for (int i : IndexRange(mp_src.totloop)) {
+ ml_dst[i].v = ml_src[i].v;
+ ml_dst[i].e = ml_src[i].e;
+ }
+ }
+}
+
+static void copy_masked_polys_to_new_mesh(const Mesh &src_mesh,
+ Mesh &dst_mesh,
+ Span<int> vertex_map,
+ Span<int> edge_map,
+ Span<int> masked_poly_indices,
+ Span<int> new_loop_starts)
+{
+ for (const int i_dst : masked_poly_indices.index_range()) {
+ const int i_src = masked_poly_indices[i_dst];
+
+ const MPoly &mp_src = src_mesh.mpoly[i_src];
+ MPoly &mp_dst = dst_mesh.mpoly[i_dst];
+ const int i_ml_src = mp_src.loopstart;
+ const int i_ml_dst = new_loop_starts[i_dst];
+
+ const MLoop *ml_src = src_mesh.mloop + i_ml_src;
+ MLoop *ml_dst = dst_mesh.mloop + i_ml_dst;
+
+ mp_dst = mp_src;
+ mp_dst.loopstart = i_ml_dst;
+ for (int i : IndexRange(mp_src.totloop)) {
+ ml_dst[i].v = vertex_map[ml_src[i].v];
+ ml_dst[i].e = edge_map[ml_src[i].e];
+ }
+ }
+}
+
+static void spline_copy_builtin_attributes(const Spline &spline,
+ Spline &r_spline,
+ const IndexMask mask)
+{
+ copy_data_based_on_mask(spline.positions(), r_spline.positions(), mask);
+ copy_data_based_on_mask(spline.radii(), r_spline.radii(), mask);
+ copy_data_based_on_mask(spline.tilts(), r_spline.tilts(), mask);
+ switch (spline.type()) {
+ case Spline::Type::Poly:
+ break;
+ case Spline::Type::Bezier: {
+ const BezierSpline &src = static_cast<const BezierSpline &>(spline);
+ BezierSpline &dst = static_cast<BezierSpline &>(r_spline);
+ copy_data_based_on_mask(src.handle_positions_left(), dst.handle_positions_left(), mask);
+ copy_data_based_on_mask(src.handle_positions_right(), dst.handle_positions_right(), mask);
+ copy_data_based_on_mask(src.handle_types_left(), dst.handle_types_left(), mask);
+ copy_data_based_on_mask(src.handle_types_right(), dst.handle_types_right(), mask);
+ break;
+ }
+ case Spline::Type::NURBS: {
+ const NURBSpline &src = static_cast<const NURBSpline &>(spline);
+ NURBSpline &dst = static_cast<NURBSpline &>(r_spline);
+ copy_data_based_on_mask(src.weights(), dst.weights(), mask);
+ break;
+ }
+ }
+}
+
+static void copy_dynamic_attributes(const CustomDataAttributes &src,
+ CustomDataAttributes &dst,
+ const IndexMask mask)
+{
+ src.foreach_attribute(
+ [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
+ std::optional<GSpan> src_attribute = src.get_for_read(attribute_id);
+ BLI_assert(src_attribute);
+
+ if (!dst.create(attribute_id, meta_data.data_type)) {
+ /* Since the source spline of the same type had the attribute, adding it should work.
+ */
+ BLI_assert_unreachable();
+ }
+
+ std::optional<GMutableSpan> new_attribute = dst.get_for_write(attribute_id);
+ BLI_assert(new_attribute);
+
+ attribute_math::convert_to_static_type(new_attribute->type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ copy_data_based_on_mask(src_attribute->typed<T>(), new_attribute->typed<T>(), mask);
+ });
+ return true;
+ },
+ ATTR_DOMAIN_POINT);
+}
+
+/**
+ * Deletes points in the spline. Those not in the mask are deleted. The spline is not split into
+ * multiple newer splines.
+ */
+static SplinePtr spline_delete(const Spline &spline, const IndexMask mask)
+{
+ SplinePtr new_spline = spline.copy_only_settings();
+ new_spline->resize(mask.size());
+
+ spline_copy_builtin_attributes(spline, *new_spline, mask);
+ copy_dynamic_attributes(spline.attributes, new_spline->attributes, mask);
+
+ return new_spline;
+}
+
+static std::unique_ptr<CurveEval> curve_separate(const CurveEval &input_curve,
+ const Span<bool> selection,
+ const AttributeDomain selection_domain,
+ const bool invert)
+{
+ Span<SplinePtr> input_splines = input_curve.splines();
+ std::unique_ptr<CurveEval> output_curve = std::make_unique<CurveEval>();
+
+ /* Keep track of which splines were copied to the result to copy spline domain attributes. */
+ Vector<int64_t> copied_splines;
+
+ if (selection_domain == ATTR_DOMAIN_CURVE) {
+ /* Operates on each of the splines as a whole, i.e. not on the points in the splines
+ * themselves. */
+ for (const int i : selection.index_range()) {
+ if (selection[i] != invert) {
+ output_curve->add_spline(input_splines[i]->copy());
+ copied_splines.append(i);
+ }
+ }
+ }
+ else {
+ /* Operates on the points in the splines themselves. */
+
+ /* Reuse index vector for each spline. */
+ Vector<int64_t> indices_to_copy;
+
+ int selection_index = 0;
+ for (const int i : input_splines.index_range()) {
+ const Spline &spline = *input_splines[i];
+
+ indices_to_copy.clear();
+ for (const int i_point : IndexRange(spline.size())) {
+ if (selection[selection_index] != invert) {
+ /* Append i_point instead of selection_index because we need indices local to the spline
+ * for copying. */
+ indices_to_copy.append(i_point);
+ }
+ selection_index++;
+ }
+
+ /* Avoid creating an empty spline. */
+ if (indices_to_copy.is_empty()) {
+ continue;
+ }
+
+ SplinePtr new_spline = spline_delete(spline, IndexMask(indices_to_copy));
+ output_curve->add_spline(std::move(new_spline));
+ copied_splines.append(i);
+ }
+ }
+
+ if (copied_splines.is_empty()) {
+ return {};
+ }
+
+ output_curve->attributes.reallocate(output_curve->splines().size());
+ copy_dynamic_attributes(
+ input_curve.attributes, output_curve->attributes, IndexMask(copied_splines));
+
+ return output_curve;
+}
+
+static void separate_curve_selection(GeometrySet &geometry_set,
+ const Field<bool> &selection_field,
+ const AttributeDomain selection_domain,
+ const bool invert)
+{
+ const CurveComponent &src_component = *geometry_set.get_component_for_read<CurveComponent>();
+ GeometryComponentFieldContext field_context{src_component, selection_domain};
+
+ fn::FieldEvaluator selection_evaluator{field_context,
+ src_component.attribute_domain_size(selection_domain)};
+ selection_evaluator.add(selection_field);
+ selection_evaluator.evaluate();
+ const VArray_Span<bool> &selection = selection_evaluator.get_evaluated<bool>(0);
+ std::unique_ptr<CurveEval> r_curve = curve_separate(
+ *src_component.get_for_read(), selection, selection_domain, invert);
+ if (r_curve) {
+ geometry_set.replace_curve(r_curve.release());
+ }
+ else {
+ geometry_set.replace_curve(nullptr);
+ }
+}
+
+static void separate_point_cloud_selection(GeometrySet &geometry_set,
+ const Field<bool> &selection_field,
+ const bool invert)
+{
+ const PointCloudComponent &src_points =
+ *geometry_set.get_component_for_read<PointCloudComponent>();
+ GeometryComponentFieldContext field_context{src_points, ATTR_DOMAIN_POINT};
+
+ fn::FieldEvaluator selection_evaluator{field_context,
+ src_points.attribute_domain_size(ATTR_DOMAIN_POINT)};
+ selection_evaluator.add(selection_field);
+ selection_evaluator.evaluate();
+ const VArray_Span<bool> &selection = selection_evaluator.get_evaluated<bool>(0);
+
+ Vector<int64_t> indices;
+ const IndexMask mask = index_mask_indices(selection, invert, indices);
+ const int total = mask.size();
+ PointCloud *pointcloud = BKE_pointcloud_new_nomain(total);
+
+ if (total == 0) {
+ geometry_set.replace_pointcloud(pointcloud);
+ return;
+ }
+
+ PointCloudComponent dst_points;
+ dst_points.replace(pointcloud, GeometryOwnershipType::Editable);
+
+ Map<AttributeIDRef, AttributeKind> attributes;
+ geometry_set.gather_attributes_for_propagation(
+ {GEO_COMPONENT_TYPE_POINT_CLOUD}, GEO_COMPONENT_TYPE_POINT_CLOUD, false, attributes);
+
+ copy_attributes_based_on_mask(attributes, src_points, dst_points, ATTR_DOMAIN_POINT, mask);
+ geometry_set.replace_pointcloud(pointcloud);
+}
+
+static void separate_instance_selection(GeometrySet &geometry_set,
+ const Field<bool> &selection_field,
+ const bool invert)
+{
+ InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>();
+ GeometryComponentFieldContext field_context{instances, ATTR_DOMAIN_INSTANCE};
+
+ const int domain_size = instances.attribute_domain_size(ATTR_DOMAIN_INSTANCE);
+ fn::FieldEvaluator evaluator{field_context, domain_size};
+ evaluator.add(selection_field);
+ evaluator.evaluate();
+ const VArray_Span<bool> &selection = evaluator.get_evaluated<bool>(0);
+
+ Vector<int64_t> indices;
+ const IndexMask mask = index_mask_indices(selection, invert, indices);
+
+ if (mask.is_empty()) {
+ geometry_set.remove<InstancesComponent>();
+ return;
+ }
+
+ instances.remove_instances(mask);
+}
+
+static void compute_selected_vertices_from_vertex_selection(const Span<bool> vertex_selection,
+ const bool invert,
+ MutableSpan<int> r_vertex_map,
+ int *r_num_selected_vertices)
+{
+ BLI_assert(vertex_selection.size() == r_vertex_map.size());
+
+ int num_selected_vertices = 0;
+ for (const int i : r_vertex_map.index_range()) {
+ if (vertex_selection[i] != invert) {
+ r_vertex_map[i] = num_selected_vertices;
+ num_selected_vertices++;
+ }
+ else {
+ r_vertex_map[i] = -1;
+ }
+ }
+
+ *r_num_selected_vertices = num_selected_vertices;
+}
+
+static void compute_selected_edges_from_vertex_selection(const Mesh &mesh,
+ const Span<bool> vertex_selection,
+ const bool invert,
+ MutableSpan<int> r_edge_map,
+ int *r_num_selected_edges)
+{
+ BLI_assert(mesh.totedge == r_edge_map.size());
+
+ int num_selected_edges = 0;
+ for (const int i : IndexRange(mesh.totedge)) {
+ const MEdge &edge = mesh.medge[i];
+
+ /* Only add the edge if both vertices will be in the new mesh. */
+ if (vertex_selection[edge.v1] != invert && vertex_selection[edge.v2] != invert) {
+ r_edge_map[i] = num_selected_edges;
+ num_selected_edges++;
+ }
+ else {
+ r_edge_map[i] = -1;
+ }
+ }
+
+ *r_num_selected_edges = num_selected_edges;
+}
+
+static void compute_selected_polygons_from_vertex_selection(const Mesh &mesh,
+ const Span<bool> vertex_selection,
+ const bool invert,
+ Vector<int> &r_selected_poly_indices,
+ Vector<int> &r_loop_starts,
+ int *r_num_selected_polys,
+ int *r_num_selected_loops)
+{
+ BLI_assert(mesh.totvert == vertex_selection.size());
+
+ r_selected_poly_indices.reserve(mesh.totpoly);
+ r_loop_starts.reserve(mesh.totloop);
+
+ int num_selected_loops = 0;
+ for (const int i : IndexRange(mesh.totpoly)) {
+ const MPoly &poly_src = mesh.mpoly[i];
+
+ bool all_verts_in_selection = true;
+ Span<MLoop> loops_src(&mesh.mloop[poly_src.loopstart], poly_src.totloop);
+ for (const MLoop &loop : loops_src) {
+ if (vertex_selection[loop.v] == invert) {
+ all_verts_in_selection = false;
+ break;
+ }
+ }
+
+ if (all_verts_in_selection) {
+ r_selected_poly_indices.append_unchecked(i);
+ r_loop_starts.append_unchecked(num_selected_loops);
+ num_selected_loops += poly_src.totloop;
+ }
+ }
+
+ *r_num_selected_polys = r_selected_poly_indices.size();
+ *r_num_selected_loops = num_selected_loops;
+}
+
+/**
+ * Checks for every edge if it is in `edge_selection`. If it is, then the two vertices of the
+ * edge are kept along with the edge.
+ */
+static void compute_selected_vertices_and_edges_from_edge_selection(
+ const Mesh &mesh,
+ const Span<bool> edge_selection,
+ const bool invert,
+ MutableSpan<int> r_vertex_map,
+ MutableSpan<int> r_edge_map,
+ int *r_num_selected_vertices,
+ int *r_num_selected_edges)
+{
+ BLI_assert(mesh.totedge == edge_selection.size());
+
+ int num_selected_edges = 0;
+ int num_selected_vertices = 0;
+ for (const int i : IndexRange(mesh.totedge)) {
+ const MEdge &edge = mesh.medge[i];
+ if (edge_selection[i] != invert) {
+ r_edge_map[i] = num_selected_edges;
+ num_selected_edges++;
+ if (r_vertex_map[edge.v1] == -1) {
+ r_vertex_map[edge.v1] = num_selected_vertices;
+ num_selected_vertices++;
+ }
+ if (r_vertex_map[edge.v2] == -1) {
+ r_vertex_map[edge.v2] = num_selected_vertices;
+ num_selected_vertices++;
+ }
+ }
+ else {
+ r_edge_map[i] = -1;
+ }
+ }
+
+ *r_num_selected_vertices = num_selected_vertices;
+ *r_num_selected_edges = num_selected_edges;
+}
+
+/**
+ * Checks for every edge if it is in `edge_selection`.
+ */
+static void compute_selected_edges_from_edge_selection(const Mesh &mesh,
+ const Span<bool> edge_selection,
+ const bool invert,
+ MutableSpan<int> r_edge_map,
+ int *r_num_selected_edges)
+{
+ BLI_assert(mesh.totedge == edge_selection.size());
+
+ int num_selected_edges = 0;
+ for (const int i : IndexRange(mesh.totedge)) {
+ if (edge_selection[i] != invert) {
+ r_edge_map[i] = num_selected_edges;
+ num_selected_edges++;
+ }
+ else {
+ r_edge_map[i] = -1;
+ }
+ }
+
+ *r_num_selected_edges = num_selected_edges;
+}
+
+/**
+ * Checks for every polygon if all the edges are in `edge_selection`. If they are, then that
+ * polygon is kept.
+ */
+static void compute_selected_polygons_from_edge_selection(const Mesh &mesh,
+ const Span<bool> edge_selection,
+ const bool invert,
+ Vector<int> &r_selected_poly_indices,
+ Vector<int> &r_loop_starts,
+ int *r_num_selected_polys,
+ int *r_num_selected_loops)
+{
+ r_selected_poly_indices.reserve(mesh.totpoly);
+ r_loop_starts.reserve(mesh.totloop);
+
+ int num_selected_loops = 0;
+ for (const int i : IndexRange(mesh.totpoly)) {
+ const MPoly &poly_src = mesh.mpoly[i];
+
+ bool all_edges_in_selection = true;
+ Span<MLoop> loops_src(&mesh.mloop[poly_src.loopstart], poly_src.totloop);
+ for (const MLoop &loop : loops_src) {
+ if (edge_selection[loop.e] == invert) {
+ all_edges_in_selection = false;
+ break;
+ }
+ }
+
+ if (all_edges_in_selection) {
+ r_selected_poly_indices.append_unchecked(i);
+ r_loop_starts.append_unchecked(num_selected_loops);
+ num_selected_loops += poly_src.totloop;
+ }
+ }
+
+ *r_num_selected_polys = r_selected_poly_indices.size();
+ *r_num_selected_loops = num_selected_loops;
+}
+
+/**
+ * Checks for every edge and polygon if all its vertices are in `vertex_selection`.
+ */
+static void compute_selected_mesh_data_from_vertex_selection_edge_face(
+ const Mesh &mesh,
+ const Span<bool> vertex_selection,
+ const bool invert,
+ MutableSpan<int> r_edge_map,
+ Vector<int> &r_selected_poly_indices,
+ Vector<int> &r_loop_starts,
+ int *r_num_selected_edges,
+ int *r_num_selected_polys,
+ int *r_num_selected_loops)
+{
+
+ compute_selected_edges_from_vertex_selection(
+ mesh, vertex_selection, invert, r_edge_map, r_num_selected_edges);
+
+ compute_selected_polygons_from_vertex_selection(mesh,
+ vertex_selection,
+ invert,
+ r_selected_poly_indices,
+ r_loop_starts,
+ r_num_selected_polys,
+ r_num_selected_loops);
+}
+
+/**
+ * Checks for every vertex if it is in `vertex_selection`. The polygons and edges are kept if all
+ * vertices of that polygon or edge are in the selection.
+ */
+static void compute_selected_mesh_data_from_vertex_selection(const Mesh &mesh,
+ const Span<bool> vertex_selection,
+ const bool invert,
+ MutableSpan<int> r_vertex_map,
+ MutableSpan<int> r_edge_map,
+ Vector<int> &r_selected_poly_indices,
+ Vector<int> &r_loop_starts,
+ int *r_num_selected_vertices,
+ int *r_num_selected_edges,
+ int *r_num_selected_polys,
+ int *r_num_selected_loops)
+{
+ compute_selected_vertices_from_vertex_selection(
+ vertex_selection, invert, r_vertex_map, r_num_selected_vertices);
+
+ compute_selected_edges_from_vertex_selection(
+ mesh, vertex_selection, invert, r_edge_map, r_num_selected_edges);
+
+ compute_selected_polygons_from_vertex_selection(mesh,
+ vertex_selection,
+ invert,
+ r_selected_poly_indices,
+ r_loop_starts,
+ r_num_selected_polys,
+ r_num_selected_loops);
+}
+
+/**
+ * Checks for every edge if it is in `edge_selection`. The polygons are kept if all edges are in
+ * the selection.
+ */
+static void compute_selected_mesh_data_from_edge_selection_edge_face(
+ const Mesh &mesh,
+ const Span<bool> edge_selection,
+ const bool invert,
+ MutableSpan<int> r_edge_map,
+ Vector<int> &r_selected_poly_indices,
+ Vector<int> &r_loop_starts,
+ int *r_num_selected_edges,
+ int *r_num_selected_polys,
+ int *r_num_selected_loops)
+{
+ compute_selected_edges_from_edge_selection(
+ mesh, edge_selection, invert, r_edge_map, r_num_selected_edges);
+ compute_selected_polygons_from_edge_selection(mesh,
+ edge_selection,
+ invert,
+ r_selected_poly_indices,
+ r_loop_starts,
+ r_num_selected_polys,
+ r_num_selected_loops);
+}
+
+/**
+ * Checks for every edge if it is in `edge_selection`. If it is, the vertices belonging to
+ * that edge are kept as well. The polygons are kept if all edges are in the selection.
+ */
+static void compute_selected_mesh_data_from_edge_selection(const Mesh &mesh,
+ const Span<bool> edge_selection,
+ const bool invert,
+ MutableSpan<int> r_vertex_map,
+ MutableSpan<int> r_edge_map,
+ Vector<int> &r_selected_poly_indices,
+ Vector<int> &r_loop_starts,
+ int *r_num_selected_vertices,
+ int *r_num_selected_edges,
+ int *r_num_selected_polys,
+ int *r_num_selected_loops)
+{
+ r_vertex_map.fill(-1);
+ compute_selected_vertices_and_edges_from_edge_selection(mesh,
+ edge_selection,
+ invert,
+ r_vertex_map,
+ r_edge_map,
+ r_num_selected_vertices,
+ r_num_selected_edges);
+ compute_selected_polygons_from_edge_selection(mesh,
+ edge_selection,
+ invert,
+ r_selected_poly_indices,
+ r_loop_starts,
+ r_num_selected_polys,
+ r_num_selected_loops);
+}
+
+/**
+ * Checks for every polygon if it is in `poly_selection`.
+ */
+static void compute_selected_polygons_from_poly_selection(const Mesh &mesh,
+ const Span<bool> poly_selection,
+ const bool invert,
+ Vector<int> &r_selected_poly_indices,
+ Vector<int> &r_loop_starts,
+ int *r_num_selected_polys,
+ int *r_num_selected_loops)
+{
+ BLI_assert(mesh.totpoly == poly_selection.size());
+
+ r_selected_poly_indices.reserve(mesh.totpoly);
+ r_loop_starts.reserve(mesh.totloop);
+
+ int num_selected_loops = 0;
+ for (const int i : IndexRange(mesh.totpoly)) {
+ const MPoly &poly_src = mesh.mpoly[i];
+ /* We keep this one. */
+ if (poly_selection[i] != invert) {
+ r_selected_poly_indices.append_unchecked(i);
+ r_loop_starts.append_unchecked(num_selected_loops);
+ num_selected_loops += poly_src.totloop;
+ }
+ }
+ *r_num_selected_polys = r_selected_poly_indices.size();
+ *r_num_selected_loops = num_selected_loops;
+}
+/**
+ * Checks for every polygon if it is in `poly_selection`. If it is, the edges
+ * belonging to that polygon are kept as well.
+ */
+static void compute_selected_mesh_data_from_poly_selection_edge_face(
+ const Mesh &mesh,
+ const Span<bool> poly_selection,
+ const bool invert,
+ MutableSpan<int> r_edge_map,
+ Vector<int> &r_selected_poly_indices,
+ Vector<int> &r_loop_starts,
+ int *r_num_selected_edges,
+ int *r_num_selected_polys,
+ int *r_num_selected_loops)
+{
+ BLI_assert(mesh.totpoly == poly_selection.size());
+ BLI_assert(mesh.totedge == r_edge_map.size());
+ r_edge_map.fill(-1);
+
+ r_selected_poly_indices.reserve(mesh.totpoly);
+ r_loop_starts.reserve(mesh.totloop);
+
+ int num_selected_loops = 0;
+ int num_selected_edges = 0;
+ for (const int i : IndexRange(mesh.totpoly)) {
+ const MPoly &poly_src = mesh.mpoly[i];
+ /* We keep this one. */
+ if (poly_selection[i] != invert) {
+ r_selected_poly_indices.append_unchecked(i);
+ r_loop_starts.append_unchecked(num_selected_loops);
+ num_selected_loops += poly_src.totloop;
+
+ /* Add the vertices and the edges. */
+ Span<MLoop> loops_src(&mesh.mloop[poly_src.loopstart], poly_src.totloop);
+ for (const MLoop &loop : loops_src) {
+ /* Check first if it has not yet been added. */
+ if (r_edge_map[loop.e] == -1) {
+ r_edge_map[loop.e] = num_selected_edges;
+ num_selected_edges++;
+ }
+ }
+ }
+ }
+ *r_num_selected_edges = num_selected_edges;
+ *r_num_selected_polys = r_selected_poly_indices.size();
+ *r_num_selected_loops = num_selected_loops;
+}
+
+/**
+ * Checks for every polygon if it is in `poly_selection`. If it is, the edges and vertices
+ * belonging to that polygon are kept as well.
+ */
+static void compute_selected_mesh_data_from_poly_selection(const Mesh &mesh,
+ const Span<bool> poly_selection,
+ const bool invert,
+ MutableSpan<int> r_vertex_map,
+ MutableSpan<int> r_edge_map,
+ Vector<int> &r_selected_poly_indices,
+ Vector<int> &r_loop_starts,
+ int *r_num_selected_vertices,
+ int *r_num_selected_edges,
+ int *r_num_selected_polys,
+ int *r_num_selected_loops)
+{
+ BLI_assert(mesh.totpoly == poly_selection.size());
+ BLI_assert(mesh.totedge == r_edge_map.size());
+ r_vertex_map.fill(-1);
+ r_edge_map.fill(-1);
+
+ r_selected_poly_indices.reserve(mesh.totpoly);
+ r_loop_starts.reserve(mesh.totloop);
+
+ int num_selected_loops = 0;
+ int num_selected_vertices = 0;
+ int num_selected_edges = 0;
+ for (const int i : IndexRange(mesh.totpoly)) {
+ const MPoly &poly_src = mesh.mpoly[i];
+ /* We keep this one. */
+ if (poly_selection[i] != invert) {
+ r_selected_poly_indices.append_unchecked(i);
+ r_loop_starts.append_unchecked(num_selected_loops);
+ num_selected_loops += poly_src.totloop;
+
+ /* Add the vertices and the edges. */
+ Span<MLoop> loops_src(&mesh.mloop[poly_src.loopstart], poly_src.totloop);
+ for (const MLoop &loop : loops_src) {
+ /* Check first if it has not yet been added. */
+ if (r_vertex_map[loop.v] == -1) {
+ r_vertex_map[loop.v] = num_selected_vertices;
+ num_selected_vertices++;
+ }
+ if (r_edge_map[loop.e] == -1) {
+ r_edge_map[loop.e] = num_selected_edges;
+ num_selected_edges++;
+ }
+ }
+ }
+ }
+ *r_num_selected_vertices = num_selected_vertices;
+ *r_num_selected_edges = num_selected_edges;
+ *r_num_selected_polys = r_selected_poly_indices.size();
+ *r_num_selected_loops = num_selected_loops;
+}
+
+/**
+ * Keep the parts of the mesh that are in the selection.
+ */
+static void do_mesh_separation(GeometrySet &geometry_set,
+ const MeshComponent &in_component,
+ const VArray_Span<bool> &selection,
+ const bool invert,
+ const AttributeDomain domain,
+ const GeometryNodeDeleteGeometryMode mode)
+{
+ /* Needed in all cases. */
+ Vector<int> selected_poly_indices;
+ Vector<int> new_loop_starts;
+ int num_selected_polys = 0;
+ int num_selected_loops = 0;
+
+ const Mesh &mesh_in = *in_component.get_for_read();
+ Mesh *mesh_out;
+ MeshComponent out_component;
+
+ Map<AttributeIDRef, AttributeKind> attributes;
+ geometry_set.gather_attributes_for_propagation(
+ {GEO_COMPONENT_TYPE_MESH}, GEO_COMPONENT_TYPE_MESH, false, attributes);
+
+ switch (mode) {
+ case GEO_NODE_DELETE_GEOMETRY_MODE_ALL: {
+ Array<int> vertex_map(mesh_in.totvert);
+ int num_selected_vertices = 0;
+
+ Array<int> edge_map(mesh_in.totedge);
+ int num_selected_edges = 0;
+
+ /* Fill all the maps based on the selection. */
+ switch (domain) {
+ case ATTR_DOMAIN_POINT:
+ compute_selected_mesh_data_from_vertex_selection(mesh_in,
+ selection,
+ invert,
+ vertex_map,
+ edge_map,
+ selected_poly_indices,
+ new_loop_starts,
+ &num_selected_vertices,
+ &num_selected_edges,
+ &num_selected_polys,
+ &num_selected_loops);
+ break;
+ case ATTR_DOMAIN_EDGE:
+ compute_selected_mesh_data_from_edge_selection(mesh_in,
+ selection,
+ invert,
+ vertex_map,
+ edge_map,
+ selected_poly_indices,
+ new_loop_starts,
+ &num_selected_vertices,
+ &num_selected_edges,
+ &num_selected_polys,
+ &num_selected_loops);
+ break;
+ case ATTR_DOMAIN_FACE:
+ compute_selected_mesh_data_from_poly_selection(mesh_in,
+ selection,
+ invert,
+ vertex_map,
+ edge_map,
+ selected_poly_indices,
+ new_loop_starts,
+ &num_selected_vertices,
+ &num_selected_edges,
+ &num_selected_polys,
+ &num_selected_loops);
+ break;
+ default:
+ BLI_assert_unreachable();
+ break;
+ }
+ mesh_out = BKE_mesh_new_nomain_from_template(&mesh_in,
+ num_selected_vertices,
+ num_selected_edges,
+ 0,
+ num_selected_loops,
+ num_selected_polys);
+ out_component.replace(mesh_out, GeometryOwnershipType::Editable);
+
+ /* Copy the selected parts of the mesh over to the new mesh. */
+ copy_masked_vertices_to_new_mesh(mesh_in, *mesh_out, vertex_map);
+ copy_masked_edges_to_new_mesh(mesh_in, *mesh_out, vertex_map, edge_map);
+ copy_masked_polys_to_new_mesh(
+ mesh_in, *mesh_out, vertex_map, edge_map, selected_poly_indices, new_loop_starts);
+
+ /* Copy attributes. */
+ copy_attributes_based_on_map(
+ attributes, in_component, out_component, ATTR_DOMAIN_POINT, vertex_map);
+ copy_attributes_based_on_map(
+ attributes, in_component, out_component, ATTR_DOMAIN_EDGE, edge_map);
+ copy_attributes_based_on_mask(attributes,
+ in_component,
+ out_component,
+ ATTR_DOMAIN_FACE,
+ IndexMask(Vector<int64_t>(selected_poly_indices.as_span())));
+ copy_face_corner_attributes(attributes,
+ in_component,
+ out_component,
+ num_selected_loops,
+ selected_poly_indices,
+ mesh_in);
+ break;
+ }
+ case GEO_NODE_DELETE_GEOMETRY_MODE_EDGE_FACE: {
+ Array<int> edge_map(mesh_in.totedge);
+ int num_selected_edges = 0;
+
+ /* Fill all the maps based on the selection. */
+ switch (domain) {
+ case ATTR_DOMAIN_POINT:
+ compute_selected_mesh_data_from_vertex_selection_edge_face(mesh_in,
+ selection,
+ invert,
+ edge_map,
+ selected_poly_indices,
+ new_loop_starts,
+ &num_selected_edges,
+ &num_selected_polys,
+ &num_selected_loops);
+ break;
+ case ATTR_DOMAIN_EDGE:
+ compute_selected_mesh_data_from_edge_selection_edge_face(mesh_in,
+ selection,
+ invert,
+ edge_map,
+ selected_poly_indices,
+ new_loop_starts,
+ &num_selected_edges,
+ &num_selected_polys,
+ &num_selected_loops);
+ break;
+ case ATTR_DOMAIN_FACE:
+ compute_selected_mesh_data_from_poly_selection_edge_face(mesh_in,
+ selection,
+ invert,
+ edge_map,
+ selected_poly_indices,
+ new_loop_starts,
+ &num_selected_edges,
+ &num_selected_polys,
+ &num_selected_loops);
+ break;
+ default:
+ BLI_assert_unreachable();
+ break;
+ }
+ mesh_out = BKE_mesh_new_nomain_from_template(&mesh_in,
+ mesh_in.totvert,
+ num_selected_edges,
+ 0,
+ num_selected_loops,
+ num_selected_polys);
+ out_component.replace(mesh_out, GeometryOwnershipType::Editable);
+
+ /* Copy the selected parts of the mesh over to the new mesh. */
+ memcpy(mesh_out->mvert, mesh_in.mvert, mesh_in.totvert * sizeof(MVert));
+ copy_masked_edges_to_new_mesh(mesh_in, *mesh_out, edge_map);
+ copy_masked_polys_to_new_mesh(
+ mesh_in, *mesh_out, edge_map, selected_poly_indices, new_loop_starts);
+
+ /* Copy attributes. */
+ copy_attributes(attributes, in_component, out_component, {ATTR_DOMAIN_POINT});
+ copy_attributes_based_on_map(
+ attributes, in_component, out_component, ATTR_DOMAIN_EDGE, edge_map);
+ copy_attributes_based_on_mask(attributes,
+ in_component,
+ out_component,
+ ATTR_DOMAIN_FACE,
+ IndexMask(Vector<int64_t>(selected_poly_indices.as_span())));
+ copy_face_corner_attributes(attributes,
+ in_component,
+ out_component,
+ num_selected_loops,
+ selected_poly_indices,
+ mesh_in);
+ break;
+ }
+ case GEO_NODE_DELETE_GEOMETRY_MODE_ONLY_FACE: {
+ /* Fill all the maps based on the selection. */
+ switch (domain) {
+ case ATTR_DOMAIN_POINT:
+ compute_selected_polygons_from_vertex_selection(mesh_in,
+ selection,
+ invert,
+ selected_poly_indices,
+ new_loop_starts,
+ &num_selected_polys,
+ &num_selected_loops);
+ break;
+ case ATTR_DOMAIN_EDGE:
+ compute_selected_polygons_from_edge_selection(mesh_in,
+ selection,
+ invert,
+ selected_poly_indices,
+ new_loop_starts,
+ &num_selected_polys,
+ &num_selected_loops);
+ break;
+ case ATTR_DOMAIN_FACE:
+ compute_selected_polygons_from_poly_selection(mesh_in,
+ selection,
+ invert,
+ selected_poly_indices,
+ new_loop_starts,
+ &num_selected_polys,
+ &num_selected_loops);
+ break;
+ default:
+ BLI_assert_unreachable();
+ break;
+ }
+ mesh_out = BKE_mesh_new_nomain_from_template(
+ &mesh_in, mesh_in.totvert, mesh_in.totedge, 0, num_selected_loops, num_selected_polys);
+ out_component.replace(mesh_out, GeometryOwnershipType::Editable);
+
+ /* Copy the selected parts of the mesh over to the new mesh. */
+ memcpy(mesh_out->mvert, mesh_in.mvert, mesh_in.totvert * sizeof(MVert));
+ memcpy(mesh_out->medge, mesh_in.medge, mesh_in.totedge * sizeof(MEdge));
+ copy_masked_polys_to_new_mesh(mesh_in, *mesh_out, selected_poly_indices, new_loop_starts);
+
+ /* Copy attributes. */
+ copy_attributes(
+ attributes, in_component, out_component, {ATTR_DOMAIN_POINT, ATTR_DOMAIN_EDGE});
+ copy_attributes_based_on_mask(attributes,
+ in_component,
+ out_component,
+ ATTR_DOMAIN_FACE,
+ IndexMask(Vector<int64_t>(selected_poly_indices.as_span())));
+ copy_face_corner_attributes(attributes,
+ in_component,
+ out_component,
+ num_selected_loops,
+ selected_poly_indices,
+ mesh_in);
+ break;
+ }
+ }
+
+ BKE_mesh_calc_edges_loose(mesh_out);
+ /* Tag to recalculate normals later. */
+ BKE_mesh_normals_tag_dirty(mesh_out);
+ geometry_set.replace_mesh(mesh_out);
+}
+
+static void separate_mesh_selection(GeometrySet &geometry_set,
+ const Field<bool> &selection_field,
+ const AttributeDomain selection_domain,
+ const GeometryNodeDeleteGeometryMode mode,
+ const bool invert)
+{
+ const MeshComponent &src_component = *geometry_set.get_component_for_read<MeshComponent>();
+ GeometryComponentFieldContext field_context{src_component, selection_domain};
+
+ fn::FieldEvaluator selection_evaluator{field_context,
+ src_component.attribute_domain_size(selection_domain)};
+ selection_evaluator.add(selection_field);
+ selection_evaluator.evaluate();
+ const VArray_Span<bool> &selection = selection_evaluator.get_evaluated<bool>(0);
+
+ /* Check if there is anything to delete. */
+ bool delete_nothing = true;
+ for (const int i : selection.index_range()) {
+ if (selection[i] == invert) {
+ delete_nothing = false;
+ break;
+ }
+ }
+ if (delete_nothing) {
+ return;
+ }
+
+ do_mesh_separation(geometry_set, src_component, selection, invert, selection_domain, mode);
+}
+
+} // namespace blender::nodes::node_geo_delete_geometry_cc
+
+namespace blender::nodes {
+
+void separate_geometry(GeometrySet &geometry_set,
+ const AttributeDomain domain,
+ const GeometryNodeDeleteGeometryMode mode,
+ const Field<bool> &selection_field,
+ const bool invert,
+ bool &r_is_error)
+{
+ namespace file_ns = blender::nodes::node_geo_delete_geometry_cc;
+
+ bool some_valid_domain = false;
+ if (geometry_set.has_pointcloud()) {
+ if (domain == ATTR_DOMAIN_POINT) {
+ file_ns::separate_point_cloud_selection(geometry_set, selection_field, invert);
+ some_valid_domain = true;
+ }
+ }
+ if (geometry_set.has_mesh()) {
+ if (ELEM(domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_EDGE, ATTR_DOMAIN_FACE, ATTR_DOMAIN_CORNER)) {
+ file_ns::separate_mesh_selection(geometry_set, selection_field, domain, mode, invert);
+ some_valid_domain = true;
+ }
+ }
+ if (geometry_set.has_curve()) {
+ if (ELEM(domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE)) {
+ file_ns::separate_curve_selection(geometry_set, selection_field, domain, invert);
+ some_valid_domain = true;
+ }
+ }
+ if (geometry_set.has_instances()) {
+ if (domain == ATTR_DOMAIN_INSTANCE) {
+ file_ns::separate_instance_selection(geometry_set, selection_field, invert);
+ some_valid_domain = true;
+ }
+ }
+ r_is_error = !some_valid_domain && geometry_set.has_realized_data();
+}
+
+} // namespace blender::nodes
+
+namespace blender::nodes::node_geo_delete_geometry_cc {
+
+NODE_STORAGE_FUNCS(NodeGeometryDeleteGeometry)
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::Bool>(N_("Selection"))
+ .default_value(true)
+ .hide_value()
+ .supports_field()
+ .description(N_("The parts of the geometry to be deleted"));
+ b.add_output<decl::Geometry>(N_("Geometry"));
+}
+
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ const bNode *node = static_cast<bNode *>(ptr->data);
+ const NodeGeometryDeleteGeometry &storage = node_storage(*node);
+ const AttributeDomain domain = static_cast<AttributeDomain>(storage.domain);
+
+ uiItemR(layout, ptr, "domain", 0, "", ICON_NONE);
+ /* Only show the mode when it is relevant. */
+ if (ELEM(domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_EDGE, ATTR_DOMAIN_FACE)) {
+ uiItemR(layout, ptr, "mode", 0, "", ICON_NONE);
+ }
+}
+
+static void node_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometryDeleteGeometry *data = MEM_cnew<NodeGeometryDeleteGeometry>(__func__);
+ data->domain = ATTR_DOMAIN_POINT;
+ data->mode = GEO_NODE_DELETE_GEOMETRY_MODE_ALL;
+
+ node->storage = data;
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+ const Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
+
+ const NodeGeometryDeleteGeometry &storage = node_storage(params.node());
+ const AttributeDomain domain = static_cast<AttributeDomain>(storage.domain);
+ const GeometryNodeDeleteGeometryMode mode = (GeometryNodeDeleteGeometryMode)storage.mode;
+
+ if (domain == ATTR_DOMAIN_INSTANCE) {
+ bool is_error;
+ separate_geometry(geometry_set, domain, mode, selection_field, true, is_error);
+ }
+ else {
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ bool is_error;
+ /* Invert here because we want to keep the things not in the selection. */
+ separate_geometry(geometry_set, domain, mode, selection_field, true, is_error);
+ });
+ }
+
+ params.set_output("Geometry", std::move(geometry_set));
+}
+
+} // namespace blender::nodes::node_geo_delete_geometry_cc
+
+void register_node_type_geo_delete_geometry()
+{
+ namespace file_ns = blender::nodes::node_geo_delete_geometry_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_DELETE_GEOMETRY, "Delete Geometry", NODE_CLASS_GEOMETRY);
+
+ node_type_storage(&ntype,
+ "NodeGeometryDeleteGeometry",
+ node_free_standard_storage,
+ node_copy_standard_storage);
+
+ node_type_init(&ntype, file_ns::node_init);
+
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.draw_buttons = file_ns::node_layout;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc
index 1a4c5d84dbf..d17657bfa3a 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc
@@ -36,51 +36,48 @@
#include "node_geometry_util.hh"
-using blender::bke::GeometryInstanceGroup;
+namespace blender::nodes::node_geo_distribute_points_on_faces_cc {
-namespace blender::nodes {
-
-static void geo_node_point_distribute_points_on_faces_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Float>("Distance Min").min(0.0f).subtype(PROP_DISTANCE);
- b.add_input<decl::Float>("Density Max").default_value(10.0f).min(0.0f);
- b.add_input<decl::Float>("Density").default_value(10.0f).supports_field();
- b.add_input<decl::Float>("Density Factor")
+ b.add_input<decl::Geometry>(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH);
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field();
+ b.add_input<decl::Float>(N_("Distance Min")).min(0.0f).subtype(PROP_DISTANCE);
+ b.add_input<decl::Float>(N_("Density Max")).default_value(10.0f).min(0.0f);
+ b.add_input<decl::Float>(N_("Density")).default_value(10.0f).min(0.0f).supports_field();
+ b.add_input<decl::Float>(N_("Density Factor"))
.default_value(1.0f)
.min(0.0f)
.max(1.0f)
.subtype(PROP_FACTOR)
.supports_field();
- b.add_input<decl::Int>("Seed");
- b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field();
+ b.add_input<decl::Int>(N_("Seed"));
- b.add_output<decl::Geometry>("Points");
- b.add_output<decl::Vector>("Normal").field_source();
- b.add_output<decl::Vector>("Rotation").subtype(PROP_EULER).field_source();
- b.add_output<decl::Int>("Stable ID").field_source();
+ b.add_output<decl::Geometry>(N_("Points"));
+ b.add_output<decl::Vector>(N_("Normal")).field_source();
+ b.add_output<decl::Vector>(N_("Rotation")).subtype(PROP_EULER).field_source();
}
-static void geo_node_point_distribute_points_on_faces_layout(uiLayout *layout,
- bContext *UNUSED(C),
- PointerRNA *ptr)
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiItemR(layout, ptr, "distribute_method", 0, "", ICON_NONE);
}
-static void node_point_distribute_points_on_faces_update(bNodeTree *UNUSED(ntree), bNode *node)
+static void node_point_distribute_points_on_faces_update(bNodeTree *ntree, bNode *node)
{
- bNodeSocket *sock_distance_min = (bNodeSocket *)BLI_findlink(&node->inputs, 1);
+ bNodeSocket *sock_distance_min = (bNodeSocket *)BLI_findlink(&node->inputs, 2);
bNodeSocket *sock_density_max = (bNodeSocket *)sock_distance_min->next;
bNodeSocket *sock_density = sock_density_max->next;
bNodeSocket *sock_density_factor = sock_density->next;
- nodeSetSocketAvailability(sock_distance_min,
- node->custom1 == GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_POISSON);
- nodeSetSocketAvailability(sock_density_max,
+ nodeSetSocketAvailability(ntree,
+ sock_distance_min,
node->custom1 == GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_POISSON);
- nodeSetSocketAvailability(sock_density,
- node->custom1 == GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_RANDOM);
- nodeSetSocketAvailability(sock_density_factor,
+ nodeSetSocketAvailability(
+ ntree, sock_density_max, node->custom1 == GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_POISSON);
+ nodeSetSocketAvailability(
+ ntree, sock_density, node->custom1 == GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_RANDOM);
+ nodeSetSocketAvailability(ntree,
+ sock_density_factor,
node->custom1 == GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_POISSON);
}
@@ -255,18 +252,26 @@ BLI_NOINLINE static void interpolate_attribute(const Mesh &mesh,
{
switch (source_domain) {
case ATTR_DOMAIN_POINT: {
- bke::mesh_surface_sample::sample_point_attribute(
- mesh, looptri_indices, bary_coords, source_data, output_data);
+ bke::mesh_surface_sample::sample_point_attribute(mesh,
+ looptri_indices,
+ bary_coords,
+ source_data,
+ IndexMask(output_data.size()),
+ output_data);
break;
}
case ATTR_DOMAIN_CORNER: {
- bke::mesh_surface_sample::sample_corner_attribute(
- mesh, looptri_indices, bary_coords, source_data, output_data);
+ bke::mesh_surface_sample::sample_corner_attribute(mesh,
+ looptri_indices,
+ bary_coords,
+ source_data,
+ IndexMask(output_data.size()),
+ output_data);
break;
}
case ATTR_DOMAIN_FACE: {
bke::mesh_surface_sample::sample_face_attribute(
- mesh, looptri_indices, source_data, output_data);
+ mesh, looptri_indices, source_data, IndexMask(output_data.size()), output_data);
break;
}
default: {
@@ -288,6 +293,12 @@ BLI_NOINLINE static void propagate_existing_attributes(
for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
const AttributeIDRef attribute_id = entry.key;
const CustomDataType output_data_type = entry.value.data_type;
+
+ ReadAttributeLookup source_attribute = mesh_component.attribute_try_get_for_read(attribute_id);
+ if (!source_attribute) {
+ continue;
+ }
+
/* The output domain is always #ATTR_DOMAIN_POINT, since we are creating a point cloud. */
OutputAttribute attribute_out = point_component.attribute_try_get_for_output_only(
attribute_id, ATTR_DOMAIN_POINT, output_data_type);
@@ -296,23 +307,12 @@ BLI_NOINLINE static void propagate_existing_attributes(
}
GMutableSpan out_span = attribute_out.as_span();
-
- std::optional<AttributeMetaData> attribute_info = point_component.attribute_get_meta_data(
- attribute_id);
- if (!attribute_info) {
- continue;
- }
-
- const AttributeDomain source_domain = attribute_info->domain;
- GVArrayPtr source_attribute = mesh_component.attribute_get_for_read(
- attribute_id, source_domain, output_data_type, nullptr);
- if (!source_attribute) {
- continue;
- }
-
- interpolate_attribute(
- mesh, bary_coords, looptri_indices, source_domain, *source_attribute, out_span);
-
+ interpolate_attribute(mesh,
+ bary_coords,
+ looptri_indices,
+ source_attribute.domain,
+ source_attribute.varray,
+ out_span);
attribute_out.save();
}
}
@@ -321,7 +321,6 @@ namespace {
struct AttributeOutputs {
StrongAnonymousAttributeID normal_id;
StrongAnonymousAttributeID rotation_id;
- StrongAnonymousAttributeID stable_id_id;
};
} // namespace
@@ -331,28 +330,25 @@ BLI_NOINLINE static void compute_attribute_outputs(const MeshComponent &mesh_com
const Span<int> looptri_indices,
const AttributeOutputs &attribute_outputs)
{
- std::optional<OutputAttribute_Typed<int>> id_attribute;
- std::optional<OutputAttribute_Typed<float3>> normal_attribute;
- std::optional<OutputAttribute_Typed<float3>> rotation_attribute;
+ OutputAttribute_Typed<int> id_attribute = point_component.attribute_try_get_for_output_only<int>(
+ "id", ATTR_DOMAIN_POINT);
+ MutableSpan<int> ids = id_attribute.as_span();
+
+ OutputAttribute_Typed<float3> normal_attribute;
+ OutputAttribute_Typed<float3> rotation_attribute;
- MutableSpan<int> ids;
MutableSpan<float3> normals;
MutableSpan<float3> rotations;
- if (attribute_outputs.stable_id_id) {
- id_attribute.emplace(point_component.attribute_try_get_for_output_only<int>(
- attribute_outputs.stable_id_id.get(), ATTR_DOMAIN_POINT));
- ids = id_attribute->as_span();
- }
if (attribute_outputs.normal_id) {
- normal_attribute.emplace(point_component.attribute_try_get_for_output_only<float3>(
- attribute_outputs.normal_id.get(), ATTR_DOMAIN_POINT));
- normals = normal_attribute->as_span();
+ normal_attribute = point_component.attribute_try_get_for_output_only<float3>(
+ attribute_outputs.normal_id.get(), ATTR_DOMAIN_POINT);
+ normals = normal_attribute.as_span();
}
if (attribute_outputs.rotation_id) {
- rotation_attribute.emplace(point_component.attribute_try_get_for_output_only<float3>(
- attribute_outputs.rotation_id.get(), ATTR_DOMAIN_POINT));
- rotations = rotation_attribute->as_span();
+ rotation_attribute = point_component.attribute_try_get_for_output_only<float3>(
+ attribute_outputs.rotation_id.get(), ATTR_DOMAIN_POINT);
+ rotations = rotation_attribute.as_span();
}
const Mesh &mesh = *mesh_component.get_for_read();
@@ -371,9 +367,8 @@ BLI_NOINLINE static void compute_attribute_outputs(const MeshComponent &mesh_com
const float3 v1_pos = float3(mesh.mvert[v1_index].co);
const float3 v2_pos = float3(mesh.mvert[v2_index].co);
- if (!ids.is_empty()) {
- ids[i] = noise::hash(noise::hash_float(bary_coord), looptri_index);
- }
+ ids[i] = noise::hash(noise::hash_float(bary_coord), looptri_index);
+
float3 normal;
if (!normals.is_empty() || !rotations.is_empty()) {
normal_tri_v3(normal, v0_pos, v1_pos, v2_pos);
@@ -386,14 +381,13 @@ BLI_NOINLINE static void compute_attribute_outputs(const MeshComponent &mesh_com
}
}
- if (id_attribute) {
- id_attribute->save();
- }
+ id_attribute.save();
+
if (normal_attribute) {
- normal_attribute->save();
+ normal_attribute.save();
}
if (rotation_attribute) {
- rotation_attribute->save();
+ rotation_attribute.save();
}
}
@@ -405,16 +399,12 @@ static Array<float> calc_full_density_factors_with_selection(const MeshComponent
GeometryComponentFieldContext field_context{component, attribute_domain};
const int domain_size = component.attribute_domain_size(attribute_domain);
- fn::FieldEvaluator selection_evaluator{field_context, domain_size};
- selection_evaluator.add(selection_field);
- selection_evaluator.evaluate();
- const IndexMask selection_mask = selection_evaluator.get_evaluated_as_mask(0);
-
Array<float> densities(domain_size, 0.0f);
- fn::FieldEvaluator density_evaluator{field_context, &selection_mask};
- density_evaluator.add_with_destination(density_field, densities.as_mutable_span());
- density_evaluator.evaluate();
+ fn::FieldEvaluator evaluator{field_context, domain_size};
+ evaluator.set_selection(selection_field);
+ evaluator.add_with_destination(density_field, densities.as_mutable_span());
+ evaluator.evaluate();
return densities;
}
@@ -504,6 +494,10 @@ static void point_distribution_calculate(GeometrySet &geometry_set,
}
}
+ if (positions.is_empty()) {
+ return;
+ }
+
PointCloud *pointcloud = BKE_pointcloud_new_nomain(positions.size());
memcpy(pointcloud->co, positions.data(), sizeof(float3) * positions.size());
uninitialized_fill_n(pointcloud->radius, pointcloud->totpoint, 0.05f);
@@ -526,9 +520,9 @@ static void point_distribution_calculate(GeometrySet &geometry_set,
mesh_component, point_component, bary_coords, looptri_indices, attribute_outputs);
}
-static void geo_node_point_distribute_points_on_faces_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
- GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh");
const GeometryNodeDistributePointsOnFacesMode method =
static_cast<GeometryNodeDistributePointsOnFacesMode>(params.node().custom1);
@@ -538,13 +532,10 @@ static void geo_node_point_distribute_points_on_faces_exec(GeoNodeExecParams par
AttributeOutputs attribute_outputs;
if (params.output_is_required("Normal")) {
- attribute_outputs.normal_id = StrongAnonymousAttributeID("normal");
+ attribute_outputs.normal_id = StrongAnonymousAttributeID("Normal");
}
if (params.output_is_required("Rotation")) {
- attribute_outputs.rotation_id = StrongAnonymousAttributeID("rotation");
- }
- if (params.output_is_required("Stable ID")) {
- attribute_outputs.stable_id_id = StrongAnonymousAttributeID("stable id");
+ attribute_outputs.rotation_id = StrongAnonymousAttributeID("Rotation");
}
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
@@ -560,35 +551,33 @@ static void geo_node_point_distribute_points_on_faces_exec(GeoNodeExecParams par
if (attribute_outputs.normal_id) {
params.set_output(
"Normal",
- AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.normal_id)));
+ AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.normal_id),
+ params.attribute_producer_name()));
}
if (attribute_outputs.rotation_id) {
params.set_output(
"Rotation",
- AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.rotation_id)));
- }
- if (attribute_outputs.stable_id_id) {
- params.set_output(
- "Stable ID",
- AnonymousAttributeFieldInput::Create<int>(std::move(attribute_outputs.stable_id_id)));
+ AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.rotation_id),
+ params.attribute_producer_name()));
}
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_distribute_points_on_faces_cc
void register_node_type_geo_distribute_points_on_faces()
{
+ namespace file_ns = blender::nodes::node_geo_distribute_points_on_faces_cc;
+
static bNodeType ntype;
geo_node_type_base(&ntype,
GEO_NODE_DISTRIBUTE_POINTS_ON_FACES,
"Distribute Points on Faces",
- NODE_CLASS_GEOMETRY,
- 0);
- node_type_update(&ntype, blender::nodes::node_point_distribute_points_on_faces_update);
+ NODE_CLASS_GEOMETRY);
+ node_type_update(&ntype, file_ns::node_point_distribute_points_on_faces_update);
node_type_size(&ntype, 170, 100, 320);
- ntype.declare = blender::nodes::geo_node_point_distribute_points_on_faces_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_point_distribute_points_on_faces_exec;
- ntype.draw_buttons = blender::nodes::geo_node_point_distribute_points_on_faces_layout;
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.draw_buttons = file_ns::node_layout;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc
new file mode 100644
index 00000000000..f6be6c1e7fb
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc
@@ -0,0 +1,931 @@
+/*
+ * 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 "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_attribute_math.hh"
+#include "BKE_mesh.h"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_dual_mesh_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>("Mesh").supported_type(GEO_COMPONENT_TYPE_MESH);
+ b.add_input<decl::Bool>("Keep Boundaries")
+ .default_value(false)
+ .description(
+ "Keep non-manifold boundaries of the input mesh in place by avoiding the dual "
+ "transformation there");
+ b.add_output<decl::Geometry>("Dual Mesh");
+}
+
+enum class EdgeType : int8_t {
+ Loose = 0, /* No polygons connected to it. */
+ Boundary = 1, /* An edge connected to exactly one polygon. */
+ Normal = 2, /* A normal edge (connected to two polygons). */
+ NonManifold = 3, /* An edge connected to more than two polygons. */
+};
+
+static EdgeType get_edge_type_with_added_neighbor(EdgeType old_type)
+{
+ switch (old_type) {
+ case EdgeType::Loose:
+ return EdgeType::Boundary;
+ case EdgeType::Boundary:
+ return EdgeType::Normal;
+ case EdgeType::Normal:
+ case EdgeType::NonManifold:
+ return EdgeType::NonManifold;
+ }
+ BLI_assert_unreachable();
+ return EdgeType::Loose;
+}
+
+enum class VertexType : int8_t {
+ Loose = 0, /* Either no edges connected or only loose edges connected. */
+ Normal = 1, /* A normal vertex. */
+ Boundary = 2, /* A vertex on a boundary edge. */
+ NonManifold = 3, /* A vertex on a non-manifold edge. */
+};
+
+static VertexType get_vertex_type_with_added_neighbor(VertexType old_type)
+{
+ switch (old_type) {
+ case VertexType::Loose:
+ return VertexType::Normal;
+ case VertexType::Normal:
+ return VertexType::Boundary;
+ case VertexType::Boundary:
+ case VertexType::NonManifold:
+ return VertexType::NonManifold;
+ }
+ BLI_assert_unreachable();
+ return VertexType::Loose;
+}
+
+/* Copy only where vertex_types is 'normal'. If keep boundaries is selected, also copy from
+ * boundary vertices. */
+template<typename T>
+static void copy_data_based_on_vertex_types(Span<T> data,
+ MutableSpan<T> r_data,
+ const Span<VertexType> vertex_types,
+ const bool keep_boundaries)
+{
+ if (keep_boundaries) {
+ int out_i = 0;
+ for (const int i : data.index_range()) {
+ if (ELEM(vertex_types[i], VertexType::Normal, VertexType::Boundary)) {
+ r_data[out_i] = data[i];
+ out_i++;
+ }
+ }
+ }
+ else {
+ int out_i = 0;
+ for (const int i : data.index_range()) {
+ if (vertex_types[i] == VertexType::Normal) {
+ r_data[out_i] = data[i];
+ out_i++;
+ }
+ }
+ }
+}
+
+template<typename T>
+static void copy_data_based_on_pairs(Span<T> data,
+ MutableSpan<T> r_data,
+ const Span<std::pair<int, int>> new_to_old_map)
+{
+ for (const std::pair<int, int> &pair : new_to_old_map) {
+ r_data[pair.first] = data[pair.second];
+ }
+}
+
+/* Copy using the map. */
+template<typename T>
+static void copy_data_based_on_new_to_old_map(Span<T> data,
+ MutableSpan<T> r_data,
+ const Span<int> new_to_old_map)
+{
+ for (const int i : r_data.index_range()) {
+ const int old_i = new_to_old_map[i];
+ r_data[i] = data[old_i];
+ }
+}
+
+/**
+ * Transfers the attributes from the original mesh to the new mesh using the following logic:
+ * - If the attribute was on the face domain it is now on the point domain, and this is true
+ * for all faces, so we can just copy these.
+ * - If the attribute was on the vertex domain there are three cases:
+ * - It was a 'bad' vertex so it is not in the dual mesh, and we can just ignore it
+ * - It was a normal vertex so it has a corresponding face in the dual mesh to which we can
+ * transfer.
+ * - It was a boundary vertex so it has a corresponding face, if keep_boundaries is true.
+ * Otherwise we can just ignore it.
+ * - If the attribute was on the edge domain we lookup for the new edges which edge it originated
+ * from using `new_to_old_edges_map`. We have to do it in this reverse order, because there can
+ * be more edges in the new mesh if keep boundaries is on.
+ * - We do the same thing for face corners as we do for edges.
+ *
+ * Some of the vertices (on the boundary) in the dual mesh don't come from faces, but from edges or
+ * vertices. For these the `boundary_vertex_to_relevant_face_map` is used, which maps them to the
+ * closest face.
+ */
+static void transfer_attributes(
+ const Map<AttributeIDRef, AttributeKind> &attributes,
+ const Span<VertexType> vertex_types,
+ const bool keep_boundaries,
+ const Span<int> new_to_old_edges_map,
+ const Span<int> new_to_old_face_corners_map,
+ const Span<std::pair<int, int>> boundary_vertex_to_relevant_face_map,
+ const GeometryComponent &src_component,
+ GeometryComponent &dst_component)
+{
+ for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
+ const AttributeIDRef attribute_id = entry.key;
+ ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read(attribute_id);
+ if (!src_attribute) {
+ continue;
+ }
+
+ AttributeDomain out_domain;
+ if (src_attribute.domain == ATTR_DOMAIN_FACE) {
+ out_domain = ATTR_DOMAIN_POINT;
+ }
+ else if (src_attribute.domain == ATTR_DOMAIN_POINT) {
+ out_domain = ATTR_DOMAIN_FACE;
+ }
+ else {
+ /* Edges and Face Corners. */
+ out_domain = src_attribute.domain;
+ }
+ const CustomDataType data_type = bke::cpp_type_to_custom_data_type(
+ src_attribute.varray.type());
+ OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
+ attribute_id, out_domain, data_type);
+
+ if (!dst_attribute) {
+ continue;
+ }
+
+ attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ using T = decltype(dummy);
+ VArray_Span<T> span{src_attribute.varray.typed<T>()};
+ MutableSpan<T> dst_span = dst_attribute.as_span<T>();
+ if (src_attribute.domain == ATTR_DOMAIN_FACE) {
+ dst_span.take_front(span.size()).copy_from(span);
+ if (keep_boundaries) {
+ copy_data_based_on_pairs(span, dst_span, boundary_vertex_to_relevant_face_map);
+ }
+ }
+ else if (src_attribute.domain == ATTR_DOMAIN_POINT) {
+ copy_data_based_on_vertex_types(span, dst_span, vertex_types, keep_boundaries);
+ }
+ else if (src_attribute.domain == ATTR_DOMAIN_EDGE) {
+ copy_data_based_on_new_to_old_map(span, dst_span, new_to_old_edges_map);
+ }
+ else {
+ copy_data_based_on_new_to_old_map(span, dst_span, new_to_old_face_corners_map);
+ }
+ });
+ dst_attribute.save();
+ }
+}
+
+/**
+ * Calculates the boundaries of the mesh. Boundary polygons are not computed since we don't need
+ * them later on. We use the following definitions:
+ * - An edge is on a boundary if it is connected to only one polygon.
+ * - A vertex is on a boundary if it is on an edge on a boundary.
+ */
+static void calc_boundaries(const Mesh &mesh,
+ MutableSpan<VertexType> r_vertex_types,
+ MutableSpan<EdgeType> r_edge_types)
+{
+ BLI_assert(r_vertex_types.size() == mesh.totvert);
+ BLI_assert(r_edge_types.size() == mesh.totedge);
+ r_vertex_types.fill(VertexType::Loose);
+ r_edge_types.fill(EdgeType::Loose);
+
+ /* Add up the number of polys connected to each edge. */
+ for (const int i : IndexRange(mesh.totpoly)) {
+ const MPoly &poly = mesh.mpoly[i];
+ for (const MLoop &loop : Span<MLoop>(&mesh.mloop[poly.loopstart], poly.totloop)) {
+ r_edge_types[loop.e] = get_edge_type_with_added_neighbor(r_edge_types[loop.e]);
+ }
+ }
+
+ /* Update vertices. */
+ for (const int i : IndexRange(mesh.totedge)) {
+ const EdgeType edge_type = r_edge_types[i];
+ if (edge_type == EdgeType::Loose) {
+ continue;
+ }
+ const MEdge &edge = mesh.medge[i];
+ if (edge_type == EdgeType::Boundary) {
+ r_vertex_types[edge.v1] = get_vertex_type_with_added_neighbor(r_vertex_types[edge.v1]);
+ r_vertex_types[edge.v2] = get_vertex_type_with_added_neighbor(r_vertex_types[edge.v2]);
+ }
+ else if (edge_type >= EdgeType::NonManifold) {
+ r_vertex_types[edge.v1] = VertexType::NonManifold;
+ r_vertex_types[edge.v2] = VertexType::NonManifold;
+ }
+ }
+
+ /* Normal verts are on a normal edge, and not on boundary edges or non-manifold edges. */
+ for (const int i : IndexRange(mesh.totedge)) {
+ const EdgeType edge_type = r_edge_types[i];
+ if (edge_type == EdgeType::Normal) {
+ const MEdge &edge = mesh.medge[i];
+ if (r_vertex_types[edge.v1] == VertexType::Loose) {
+ r_vertex_types[edge.v1] = VertexType::Normal;
+ }
+ if (r_vertex_types[edge.v2] == VertexType::Loose) {
+ r_vertex_types[edge.v2] = VertexType::Normal;
+ }
+ }
+ }
+}
+
+/**
+ * Stores the indices of the polygons connected to each vertex.
+ */
+static void create_vertex_poly_map(const Mesh &mesh,
+ MutableSpan<Vector<int>> r_vertex_poly_indices)
+{
+ for (const int i : IndexRange(mesh.totpoly)) {
+ const MPoly &poly = mesh.mpoly[i];
+ for (const MLoop &loop : Span<MLoop>(&mesh.mloop[poly.loopstart], poly.totloop)) {
+ r_vertex_poly_indices[loop.v].append(i);
+ }
+ }
+}
+
+/**
+ * Sorts the polygons connected to the given vertex based on polygon adjacency. The ordering is
+ * so such that the normals point in the same way as the original mesh. If the vertex is a
+ * boundary vertex, the first and last polygon have a boundary edge connected to the vertex. The
+ * `r_shared_edges` array at index i is set to the index of the shared edge between the i-th and
+ * `(i+1)-th` sorted polygon. Similarly the `r_sorted_corners` array at index i is set to the
+ * corner in the i-th sorted polygon.
+ *
+ * How the faces are sorted (see diagrams below):
+ * (For this explanation we'll assume all faces are oriented clockwise)
+ * (The vertex whose connected polygons we need to sort is "v0")
+ *
+ * \code{.unparsed}
+ * Normal case: Boundary Vertex case:
+ * v1 ----- v2 ----- v3 | | |
+ * | f3 | f0 | v2 ---- v4 --------- v3---
+ * | | | | / ,-' |
+ * v8 ----- v0 ----- v4 | f0 / f1 ,-' |
+ * | f2 | f1 | | / ,-' |
+ * | | | | / ,-' |
+ * v7 ----- v6 ----- v5 | / ,-' f2 |
+ * | /,-' |
+ * v0 ------------------ v1---
+ * \endcode
+ *
+ * - First we get the two corners of each face that have an edge which contains v0. A corner is
+ * simply a vertex followed by an edge. In this case for the face "f0" for example, we'd end up
+ * with the corners (v: v4, e: v4<->v0) and (v: v0, e: v0<->v2). Note that if the face was
+ * oriented counter-clockwise we'd end up with the corners (v: v0, e: v0<->v4) and (v: v2, e:
+ * v0<->v2) instead.
+ * - Then we need to choose one polygon as our first. If "v0" is not on a boundary we can just
+ * choose any polygon. If it is on a boundary some more care needs to be taken. Here we need to
+ * pick a polygon which lies on the boundary (in the diagram either f0 or f2). To choose between
+ * the two we need the next step.
+ * - In the normal case we use this polygon to set `shared_edge_i` which indicates the index of the
+ * shared edge between this polygon and the next one. There are two possible choices: v0<->v4 and
+ * v2<->v0. To choose we look at the corners. Since the edge v0<->v2 lies on the corner which has
+ * v0, we set `shared_edge_i` to the other edge (v0<->v4), such that the next face will be "f1"
+ * which is the next face in clockwise order.
+ * - In the boundary vertex case, we do something similar, but we are also forced to choose the
+ * edge which is not on the boundary. If this doesn't line up with orientation of the polygon, we
+ * know we'll need to choose the other boundary polygon as our first polygon. If the orientations
+ * don't line up there as well, it means that the mesh normals are not consistent, and we just
+ * have to force an orientation for ourselves. (Imagine if f0 is oriented counter-clockwise and
+ * f2 is oriented clockwise for example)
+ * - Next comes a loop where we look at the other faces and find the one which has the shared
+ * edge. Then we set the next shared edge to the other edge on the polygon connected to "v0", and
+ * continue. Because of the way we've chosen the first shared edge the order of the faces will
+ * have the same orientation as that of the first polygon.
+ * (In this case we'd have f0 -> f1 -> f2 -> f3 which also goes around clockwise).
+ * - Every time we determine a shared edge, we can also add a corner to `r_sorted_corners`. This
+ * will simply be the corner which doesn't contain the shared edge.
+ * - Finally if we are in the normal case we also need to add the last "shared edge" to close the
+ * loop.
+ */
+static void sort_vertex_polys(const Mesh &mesh,
+ const int vertex_index,
+ const bool boundary_vertex,
+ const Span<EdgeType> edge_types,
+ MutableSpan<int> connected_polygons,
+ MutableSpan<int> r_shared_edges,
+ MutableSpan<int> r_sorted_corners)
+{
+ if (connected_polygons.size() <= 2 && (!boundary_vertex || connected_polygons.size() == 0)) {
+ return;
+ }
+
+ /* For each polygon store the two corners whose edge contains the vertex. */
+ Array<std::pair<int, int>> poly_vertex_corners(connected_polygons.size());
+ for (const int i : connected_polygons.index_range()) {
+ const MPoly &poly = mesh.mpoly[connected_polygons[i]];
+ bool first_edge_done = false;
+ for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
+ const MLoop &loop = mesh.mloop[loop_index];
+ if (mesh.medge[loop.e].v1 == vertex_index || mesh.medge[loop.e].v2 == vertex_index) {
+ if (!first_edge_done) {
+ poly_vertex_corners[i].first = loop_index;
+ first_edge_done = true;
+ }
+ else {
+ poly_vertex_corners[i].second = loop_index;
+ break;
+ }
+ }
+ }
+ }
+
+ int shared_edge_i = -1;
+ /* Determine first polygon and orientation. For now the orientation of the whole loop depends
+ * on the one polygon we chose as first. It's probably not worth it to check every polygon in
+ * the loop to determine the 'average' orientation. */
+ if (boundary_vertex) {
+ /* Our first polygon needs to be one which has a boundary edge. */
+ for (const int i : connected_polygons.index_range()) {
+ const MLoop &first_loop = mesh.mloop[poly_vertex_corners[i].first];
+ const MLoop &second_loop = mesh.mloop[poly_vertex_corners[i].second];
+ if (edge_types[first_loop.e] == EdgeType::Boundary && first_loop.v == vertex_index) {
+ shared_edge_i = second_loop.e;
+ r_sorted_corners[0] = poly_vertex_corners[i].first;
+ std::swap(connected_polygons[i], connected_polygons[0]);
+ std::swap(poly_vertex_corners[i], poly_vertex_corners[0]);
+ break;
+ }
+ if (edge_types[second_loop.e] == EdgeType::Boundary && second_loop.v == vertex_index) {
+ shared_edge_i = first_loop.e;
+ r_sorted_corners[0] = poly_vertex_corners[i].second;
+ std::swap(connected_polygons[i], connected_polygons[0]);
+ std::swap(poly_vertex_corners[i], poly_vertex_corners[0]);
+ break;
+ }
+ }
+ if (shared_edge_i == -1) {
+ /* The rotation is inconsistent between the two polygons on the boundary. Just choose one
+ * of the polygon's orientation. */
+ for (const int i : connected_polygons.index_range()) {
+ const MLoop &first_loop = mesh.mloop[poly_vertex_corners[i].first];
+ const MLoop &second_loop = mesh.mloop[poly_vertex_corners[i].second];
+ if (edge_types[first_loop.e] == EdgeType::Boundary) {
+ shared_edge_i = second_loop.e;
+ r_sorted_corners[0] = poly_vertex_corners[i].first;
+ std::swap(connected_polygons[i], connected_polygons[0]);
+ std::swap(poly_vertex_corners[i], poly_vertex_corners[0]);
+ break;
+ }
+ if (edge_types[second_loop.e] == EdgeType::Boundary) {
+ shared_edge_i = first_loop.e;
+ r_sorted_corners[0] = poly_vertex_corners[i].second;
+ std::swap(connected_polygons[i], connected_polygons[0]);
+ std::swap(poly_vertex_corners[i], poly_vertex_corners[0]);
+ break;
+ }
+ }
+ }
+ }
+ else {
+ /* Any polygon can be the first. Just need to check the orientation. */
+ const MLoop &first_loop = mesh.mloop[poly_vertex_corners[0].first];
+ const MLoop &second_loop = mesh.mloop[poly_vertex_corners[0].second];
+ if (first_loop.v == vertex_index) {
+ shared_edge_i = second_loop.e;
+ r_sorted_corners[0] = poly_vertex_corners[0].first;
+ }
+ else {
+ r_sorted_corners[0] = poly_vertex_corners[0].second;
+ shared_edge_i = first_loop.e;
+ }
+ }
+ BLI_assert(shared_edge_i != -1);
+
+ for (const int i : IndexRange(connected_polygons.size() - 1)) {
+ r_shared_edges[i] = shared_edge_i;
+
+ /* Look at the other polys to see if it has this shared edge. */
+ int j = i + 1;
+ for (; j < connected_polygons.size(); ++j) {
+ const MLoop &first_loop = mesh.mloop[poly_vertex_corners[j].first];
+ const MLoop &second_loop = mesh.mloop[poly_vertex_corners[j].second];
+ if (first_loop.e == shared_edge_i) {
+ r_sorted_corners[i + 1] = poly_vertex_corners[j].first;
+ shared_edge_i = second_loop.e;
+ break;
+ }
+ if (second_loop.e == shared_edge_i) {
+ r_sorted_corners[i + 1] = poly_vertex_corners[j].second;
+ shared_edge_i = first_loop.e;
+ break;
+ }
+ }
+
+ BLI_assert(j != connected_polygons.size());
+
+ std::swap(connected_polygons[i + 1], connected_polygons[j]);
+ std::swap(poly_vertex_corners[i + 1], poly_vertex_corners[j]);
+ }
+
+ if (!boundary_vertex) {
+ /* Shared edge between first and last polygon. */
+ r_shared_edges.last() = shared_edge_i;
+ }
+}
+
+/**
+ * Get the edge on the poly that contains the given vertex and is a boundary edge.
+ */
+static void boundary_edge_on_poly(const MPoly &poly,
+ const Mesh &mesh,
+ const int vertex_index,
+ const Span<EdgeType> edge_types,
+ int &r_edge)
+{
+ for (const MLoop &loop : Span<MLoop>(&mesh.mloop[poly.loopstart], poly.totloop)) {
+ if (edge_types[loop.e] == EdgeType::Boundary) {
+ const MEdge &edge = mesh.medge[loop.e];
+ if (edge.v1 == vertex_index || edge.v2 == vertex_index) {
+ r_edge = loop.e;
+ return;
+ }
+ }
+ }
+}
+
+/**
+ * Get the two edges on the poly that contain the given vertex and are boundary edges. The
+ * orientation of the poly is taken into account.
+ */
+static void boundary_edges_on_poly(const MPoly &poly,
+ const Mesh &mesh,
+ const int vertex_index,
+ const Span<EdgeType> edge_types,
+ int &r_edge1,
+ int &r_edge2)
+{
+ bool edge1_done = false;
+ /* This is set to true if the order in which we encounter the two edges is inconsistent with the
+ * orientation of the polygon. */
+ bool needs_swap = false;
+ for (const MLoop &loop : Span<MLoop>(&mesh.mloop[poly.loopstart], poly.totloop)) {
+ if (edge_types[loop.e] == EdgeType::Boundary) {
+ const MEdge &edge = mesh.medge[loop.e];
+ if (edge.v1 == vertex_index || edge.v2 == vertex_index) {
+ if (edge1_done) {
+ if (needs_swap) {
+ r_edge2 = r_edge1;
+ r_edge1 = loop.e;
+ }
+ else {
+ r_edge2 = loop.e;
+ }
+ return;
+ }
+ r_edge1 = loop.e;
+ edge1_done = true;
+ if (loop.v == vertex_index) {
+ needs_swap = true;
+ }
+ }
+ }
+ }
+}
+
+static void add_edge(const Mesh &mesh,
+ const int old_edge_i,
+ const int v1,
+ const int v2,
+ Vector<int> &new_to_old_edges_map,
+ Vector<MEdge> &new_edges,
+ Vector<int> &loop_edges)
+{
+ MEdge new_edge = MEdge(mesh.medge[old_edge_i]);
+ new_edge.v1 = v1;
+ new_edge.v2 = v2;
+ const int new_edge_i = new_edges.size();
+ new_to_old_edges_map.append(old_edge_i);
+ new_edges.append(new_edge);
+ loop_edges.append(new_edge_i);
+}
+
+/* Returns true if the vertex is connected only to the two polygons and is not on the boundary. */
+static bool vertex_needs_dissolving(const int vertex,
+ const int first_poly_index,
+ const int second_poly_index,
+ const Span<VertexType> vertex_types,
+ const Span<Vector<int>> vertex_poly_indices)
+{
+ /* Order is guaranteed to be the same because 2poly verts that are not on the boundary are
+ * ignored in `sort_vertex_polys`. */
+ return (vertex_types[vertex] != VertexType::Boundary &&
+ vertex_poly_indices[vertex].size() == 2 &&
+ vertex_poly_indices[vertex][0] == first_poly_index &&
+ vertex_poly_indices[vertex][1] == second_poly_index);
+}
+
+/**
+ * Finds 'normal' vertices which are connected to only two polygons and marks them to not be
+ * used in the data-structures derived from the mesh. For each pair of polygons which has such a
+ * vertex, an edge is created for the dual mesh between the centers of those two polygons. All
+ * edges in the input mesh which contain such a vertex are marked as 'done' to prevent duplicate
+ * edges being created. (See T94144)
+ */
+static void dissolve_redundant_verts(const Mesh &mesh,
+ const Span<Vector<int>> vertex_poly_indices,
+ MutableSpan<VertexType> vertex_types,
+ MutableSpan<int> old_to_new_edges_map,
+ Vector<MEdge> &new_edges,
+ Vector<int> &new_to_old_edges_map)
+{
+ for (const int vert_i : IndexRange(mesh.totvert)) {
+ if (vertex_poly_indices[vert_i].size() != 2 || vertex_types[vert_i] != VertexType::Normal) {
+ continue;
+ }
+ const int first_poly_index = vertex_poly_indices[vert_i][0];
+ const int second_poly_index = vertex_poly_indices[vert_i][1];
+ const int new_edge_index = new_edges.size();
+ bool edge_created = false;
+ const MPoly &poly = mesh.mpoly[first_poly_index];
+ for (const MLoop &loop : Span<MLoop>(&mesh.mloop[poly.loopstart], poly.totloop)) {
+ const MEdge &edge = mesh.medge[loop.e];
+ const int v1 = edge.v1;
+ const int v2 = edge.v2;
+ bool mark_edge = false;
+ if (vertex_needs_dissolving(
+ v1, first_poly_index, second_poly_index, vertex_types, vertex_poly_indices)) {
+ /* This vertex is now 'removed' and should be ignored elsewhere. */
+ vertex_types[v1] = VertexType::Loose;
+ mark_edge = true;
+ }
+ if (vertex_needs_dissolving(
+ v2, first_poly_index, second_poly_index, vertex_types, vertex_poly_indices)) {
+ /* This vertex is now 'removed' and should be ignored elsewhere. */
+ vertex_types[v2] = VertexType::Loose;
+ mark_edge = true;
+ }
+ if (mark_edge) {
+ if (!edge_created) {
+ MEdge new_edge = MEdge(edge);
+ /* The vertex indices in the dual mesh are the polygon indices of the input mesh. */
+ new_edge.v1 = first_poly_index;
+ new_edge.v2 = second_poly_index;
+ new_to_old_edges_map.append(loop.e);
+ new_edges.append(new_edge);
+ edge_created = true;
+ }
+ old_to_new_edges_map[loop.e] = new_edge_index;
+ }
+ }
+ }
+}
+
+/**
+ * Calculate the barycentric dual of a mesh. The dual is only "dual" in terms of connectivity,
+ * i.e. applying the function twice will give the same vertices, edges, and faces, but not the
+ * same positions. When the option "Keep Boundaries" is selected the connectivity is no
+ * longer dual.
+ *
+ * For the dual mesh of a manifold input mesh:
+ * - The vertices are at the centers of the faces of the input mesh.
+ * - The edges connect the two vertices created from the two faces next to the edge in the input
+ * mesh.
+ * - The faces are at the vertices of the input mesh.
+ *
+ * Some special cases are needed for boundaries and non-manifold geometry.
+ */
+static void calc_dual_mesh(GeometrySet &geometry_set,
+ const MeshComponent &in_component,
+ const bool keep_boundaries)
+{
+ const Mesh &mesh_in = *in_component.get_for_read();
+
+ Map<AttributeIDRef, AttributeKind> attributes;
+ geometry_set.gather_attributes_for_propagation(
+ {GEO_COMPONENT_TYPE_MESH}, GEO_COMPONENT_TYPE_MESH, false, attributes);
+
+ Array<VertexType> vertex_types(mesh_in.totvert);
+ Array<EdgeType> edge_types(mesh_in.totedge);
+ calc_boundaries(mesh_in, vertex_types, edge_types);
+ /* Stores the indices of the polygons connected to the vertex. Because the polygons are looped
+ * over in order of their indices, the polygon's indices will be sorted in ascending order.
+ (This can change once they are sorted using `sort_vertex_polys`). */
+ Array<Vector<int>> vertex_poly_indices(mesh_in.totvert);
+ Array<Array<int>> vertex_shared_edges(mesh_in.totvert);
+ Array<Array<int>> vertex_corners(mesh_in.totvert);
+ create_vertex_poly_map(mesh_in, vertex_poly_indices);
+ threading::parallel_for(vertex_poly_indices.index_range(), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ if (vertex_types[i] == VertexType::Loose || vertex_types[i] >= VertexType::NonManifold ||
+ (!keep_boundaries && vertex_types[i] == VertexType::Boundary)) {
+ /* Bad vertex that we can't work with. */
+ continue;
+ }
+ MutableSpan<int> loop_indices = vertex_poly_indices[i];
+ Array<int> sorted_corners(loop_indices.size());
+ if (vertex_types[i] == VertexType::Normal) {
+ Array<int> shared_edges(loop_indices.size());
+ sort_vertex_polys(
+ mesh_in, i, false, edge_types, loop_indices, shared_edges, sorted_corners);
+ vertex_shared_edges[i] = shared_edges;
+ }
+ else {
+ Array<int> shared_edges(loop_indices.size() - 1);
+ sort_vertex_polys(
+ mesh_in, i, true, edge_types, loop_indices, shared_edges, sorted_corners);
+ vertex_shared_edges[i] = shared_edges;
+ }
+ vertex_corners[i] = sorted_corners;
+ }
+ });
+
+ Vector<float3> vertex_positions(mesh_in.totpoly);
+ for (const int i : IndexRange(mesh_in.totpoly)) {
+ const MPoly poly = mesh_in.mpoly[i];
+ BKE_mesh_calc_poly_center(
+ &poly, &mesh_in.mloop[poly.loopstart], mesh_in.mvert, vertex_positions[i]);
+ }
+
+ Array<int> boundary_edge_midpoint_index;
+ if (keep_boundaries) {
+ /* Only initialize when we actually need it. */
+ boundary_edge_midpoint_index.reinitialize(mesh_in.totedge);
+ /* We need to add vertices at the centers of boundary edges. */
+ for (const int i : IndexRange(mesh_in.totedge)) {
+ if (edge_types[i] == EdgeType::Boundary) {
+ float3 mid;
+ const MEdge &edge = mesh_in.medge[i];
+ mid_v3_v3v3(mid, mesh_in.mvert[edge.v1].co, mesh_in.mvert[edge.v2].co);
+ boundary_edge_midpoint_index[i] = vertex_positions.size();
+ vertex_positions.append(mid);
+ }
+ }
+ }
+
+ Vector<int> loop_lengths;
+ Vector<int> loops;
+ Vector<int> loop_edges;
+ Vector<MEdge> new_edges;
+ /* These are used to transfer attributes. */
+ Vector<int> new_to_old_face_corners_map;
+ Vector<int> new_to_old_edges_map;
+ /* Stores the index of the vertex in the dual and the face it should get the attribute from. */
+ Vector<std::pair<int, int>> boundary_vertex_to_relevant_face_map;
+ /* Since each edge in the dual (except the ones created with keep boundaries) comes from
+ * exactly one edge in the original, we can use this array to keep track of whether it still
+ * needs to be created or not. If it's not -1 it gives the index in `new_edges` of the dual
+ * edge. The edges coming from preserving the boundaries only get added once anyway, so we
+ * don't need a hash-map for that. */
+ Array<int> old_to_new_edges_map(mesh_in.totedge);
+ old_to_new_edges_map.fill(-1);
+
+ /* This is necessary to prevent duplicate edges from being created, but will likely not do
+ * anything for most meshes. */
+ dissolve_redundant_verts(mesh_in,
+ vertex_poly_indices,
+ vertex_types,
+ old_to_new_edges_map,
+ new_edges,
+ new_to_old_edges_map);
+
+ for (const int i : IndexRange(mesh_in.totvert)) {
+ if (vertex_types[i] == VertexType::Loose || vertex_types[i] >= VertexType::NonManifold ||
+ (!keep_boundaries && vertex_types[i] == VertexType::Boundary)) {
+ /* Bad vertex that we can't work with. */
+ continue;
+ }
+
+ Vector<int> loop_indices = vertex_poly_indices[i];
+ Span<int> shared_edges = vertex_shared_edges[i];
+ Span<int> sorted_corners = vertex_corners[i];
+ if (vertex_types[i] == VertexType::Normal) {
+ if (loop_indices.size() <= 2) {
+ /* We can't make a polygon from 2 vertices. */
+ continue;
+ }
+
+ /* Add edges in the loop. */
+ for (const int i : shared_edges.index_range()) {
+ const int old_edge_i = shared_edges[i];
+ if (old_to_new_edges_map[old_edge_i] == -1) {
+ /* This edge has not been created yet. */
+ MEdge new_edge = MEdge(mesh_in.medge[old_edge_i]);
+ new_edge.v1 = loop_indices[i];
+ new_edge.v2 = loop_indices[(i + 1) % loop_indices.size()];
+ new_to_old_edges_map.append(old_edge_i);
+ old_to_new_edges_map[old_edge_i] = new_edges.size();
+ new_edges.append(new_edge);
+ }
+ loop_edges.append(old_to_new_edges_map[old_edge_i]);
+ }
+
+ new_to_old_face_corners_map.extend(sorted_corners);
+ }
+ else {
+ /**
+ * The code handles boundary vertices like the vertex marked "V" in the diagram below.
+ * The first thing that happens is ordering the faces f1,f2 and f3 (stored in
+ * loop_indices), together with their shared edges e3 and e4 (which get stored in
+ * shared_edges). The ordering could end up being clockwise or counterclockwise, for this
+ * we'll assume that the ordering f1->f2->f3 is chosen. After that we add the edges in
+ * between the polygons, in this case the edges f1--f2, and f2--f3. Now we need to merge
+ * these with the boundary edges e1 and e2. To do this we create an edge from f3 to the
+ * midpoint of e2 (computed in a previous step), from this midpoint to V, from V to the
+ * midpoint of e1 and from the midpoint of e1 to f1.
+ *
+ * \code{.unparsed}
+ * | | | | | |
+ * v2 ---- v3 --------- v4--- v2 ---- v3 -------- v4---
+ * | f3 / ,-' | | / ,-'|
+ * | / f2 ,-' | | / ,-' |
+ * e2 | /e3 ,-' e4 | ====> M1-f3-/--f2-.,-' |
+ * | / ,-' | ====> | / ,-'\ |
+ * | / ,-' f1 | | / ,-' f1 |
+ * | /,-' | | /,-' | |
+ * V-------------------- v5--- V------------M2----- v5---
+ * \endcode
+ */
+
+ /* Add the edges in between the polys. */
+ for (const int i : shared_edges.index_range()) {
+ const int old_edge_i = shared_edges[i];
+ if (old_to_new_edges_map[old_edge_i] == -1) {
+ /* This edge has not been created yet. */
+ MEdge new_edge = MEdge(mesh_in.medge[old_edge_i]);
+ new_edge.v1 = loop_indices[i];
+ new_edge.v2 = loop_indices[i + 1];
+ new_to_old_edges_map.append(old_edge_i);
+ old_to_new_edges_map[old_edge_i] = new_edges.size();
+ new_edges.append(new_edge);
+ }
+ loop_edges.append(old_to_new_edges_map[old_edge_i]);
+ }
+
+ new_to_old_face_corners_map.extend(sorted_corners);
+
+ /* Add the vertex and the midpoints of the two boundary edges to the loop. */
+
+ /* Get the boundary edges. */
+ int edge1;
+ int edge2;
+ if (loop_indices.size() >= 2) {
+ /* The first boundary edge is at the end of the chain of polygons. */
+ boundary_edge_on_poly(mesh_in.mpoly[loop_indices.last()], mesh_in, i, edge_types, edge1);
+ boundary_edge_on_poly(mesh_in.mpoly[loop_indices.first()], mesh_in, i, edge_types, edge2);
+ }
+ else {
+ /* If there is only one polygon both edges are in that polygon. */
+ boundary_edges_on_poly(
+ mesh_in.mpoly[loop_indices[0]], mesh_in, i, edge_types, edge1, edge2);
+ }
+
+ const int last_face_center = loop_indices.last();
+ loop_indices.append(boundary_edge_midpoint_index[edge1]);
+ new_to_old_face_corners_map.append(sorted_corners.last());
+ const int first_midpoint = loop_indices.last();
+ if (old_to_new_edges_map[edge1] == -1) {
+ add_edge(mesh_in,
+ edge1,
+ last_face_center,
+ first_midpoint,
+ new_to_old_edges_map,
+ new_edges,
+ loop_edges);
+ old_to_new_edges_map[edge1] = new_edges.size() - 1;
+ boundary_vertex_to_relevant_face_map.append(std::pair(first_midpoint, last_face_center));
+ }
+ else {
+ loop_edges.append(old_to_new_edges_map[edge1]);
+ }
+ loop_indices.append(vertex_positions.size());
+ /* This is sort of arbitrary, but interpolating would be a lot harder to do. */
+ new_to_old_face_corners_map.append(sorted_corners.first());
+ boundary_vertex_to_relevant_face_map.append(
+ std::pair(loop_indices.last(), last_face_center));
+ vertex_positions.append(mesh_in.mvert[i].co);
+ const int boundary_vertex = loop_indices.last();
+ add_edge(mesh_in,
+ edge1,
+ first_midpoint,
+ boundary_vertex,
+ new_to_old_edges_map,
+ new_edges,
+ loop_edges);
+
+ loop_indices.append(boundary_edge_midpoint_index[edge2]);
+ new_to_old_face_corners_map.append(sorted_corners.first());
+ const int second_midpoint = loop_indices.last();
+ add_edge(mesh_in,
+ edge2,
+ boundary_vertex,
+ second_midpoint,
+ new_to_old_edges_map,
+ new_edges,
+ loop_edges);
+
+ if (old_to_new_edges_map[edge2] == -1) {
+ const int first_face_center = loop_indices.first();
+ add_edge(mesh_in,
+ edge2,
+ second_midpoint,
+ first_face_center,
+ new_to_old_edges_map,
+ new_edges,
+ loop_edges);
+ old_to_new_edges_map[edge2] = new_edges.size() - 1;
+ boundary_vertex_to_relevant_face_map.append(std::pair(second_midpoint, first_face_center));
+ }
+ else {
+ loop_edges.append(old_to_new_edges_map[edge2]);
+ }
+ }
+
+ loop_lengths.append(loop_indices.size());
+ for (const int j : loop_indices) {
+ loops.append(j);
+ }
+ }
+ Mesh *mesh_out = BKE_mesh_new_nomain(
+ vertex_positions.size(), new_edges.size(), 0, loops.size(), loop_lengths.size());
+ MeshComponent out_component;
+ out_component.replace(mesh_out, GeometryOwnershipType::Editable);
+ transfer_attributes(attributes,
+ vertex_types,
+ keep_boundaries,
+ new_to_old_edges_map,
+ new_to_old_face_corners_map,
+ boundary_vertex_to_relevant_face_map,
+ in_component,
+ out_component);
+
+ int loop_start = 0;
+ for (const int i : IndexRange(mesh_out->totpoly)) {
+ mesh_out->mpoly[i].loopstart = loop_start;
+ mesh_out->mpoly[i].totloop = loop_lengths[i];
+ loop_start += loop_lengths[i];
+ }
+ for (const int i : IndexRange(mesh_out->totloop)) {
+ mesh_out->mloop[i].v = loops[i];
+ mesh_out->mloop[i].e = loop_edges[i];
+ }
+ for (const int i : IndexRange(mesh_out->totvert)) {
+ copy_v3_v3(mesh_out->mvert[i].co, vertex_positions[i]);
+ }
+ memcpy(mesh_out->medge, new_edges.data(), sizeof(MEdge) * new_edges.size());
+ BKE_mesh_normals_tag_dirty(mesh_out);
+ geometry_set.replace_mesh(mesh_out);
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh");
+ const bool keep_boundaries = params.extract_input<bool>("Keep Boundaries");
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ if (geometry_set.has_mesh()) {
+ const MeshComponent &component = *geometry_set.get_component_for_read<MeshComponent>();
+ calc_dual_mesh(geometry_set, component, keep_boundaries);
+ }
+ });
+ params.set_output("Dual Mesh", std::move(geometry_set));
+}
+
+} // namespace blender::nodes::node_geo_dual_mesh_cc
+
+void register_node_type_geo_dual_mesh()
+{
+ namespace file_ns = blender::nodes::node_geo_dual_mesh_cc;
+
+ static bNodeType ntype;
+ geo_node_type_base(&ntype, GEO_NODE_DUAL_MESH, "Dual Mesh", NODE_CLASS_GEOMETRY);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc
new file mode 100644
index 00000000000..9376789cf2c
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc
@@ -0,0 +1,97 @@
+/*
+ * 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 "BKE_mesh.h"
+#include "BKE_mesh_runtime.h"
+
+#include "bmesh.h"
+#include "bmesh_tools.h"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_edge_split_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH);
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field();
+ b.add_output<decl::Geometry>(N_("Mesh"));
+}
+
+static Mesh *mesh_edge_split(const Mesh &mesh, const IndexMask selection)
+{
+ BMeshCreateParams bmesh_create_params{};
+ bmesh_create_params.use_toolflags = true;
+ const BMAllocTemplate allocsize = {0, 0, 0, 0};
+ BMesh *bm = BM_mesh_create(&allocsize, &bmesh_create_params);
+
+ BMeshFromMeshParams bmesh_from_mesh_params{};
+ BM_mesh_bm_from_me(bm, &mesh, &bmesh_from_mesh_params);
+
+ BM_mesh_elem_table_ensure(bm, BM_EDGE);
+ for (const int i : selection) {
+ BMEdge *edge = BM_edge_at_index(bm, i);
+ BM_elem_flag_enable(edge, BM_ELEM_TAG);
+ }
+
+ BM_mesh_edgesplit(bm, false, true, false);
+
+ Mesh *result = BKE_mesh_from_bmesh_for_eval_nomain(bm, nullptr, &mesh);
+ BM_mesh_free(bm);
+
+ BKE_mesh_normals_tag_dirty(result);
+
+ return result;
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh");
+
+ const Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
+
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ if (!geometry_set.has_mesh()) {
+ return;
+ }
+
+ const MeshComponent &mesh_component = *geometry_set.get_component_for_read<MeshComponent>();
+ GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_EDGE};
+ const int domain_size = mesh_component.attribute_domain_size(ATTR_DOMAIN_EDGE);
+ fn::FieldEvaluator selection_evaluator{field_context, domain_size};
+ selection_evaluator.add(selection_field);
+ selection_evaluator.evaluate();
+ const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0);
+
+ geometry_set.replace_mesh(mesh_edge_split(*mesh_component.get_for_read(), selection));
+ });
+
+ params.set_output("Mesh", std::move(geometry_set));
+}
+
+} // namespace blender::nodes::node_geo_edge_split_cc
+
+void register_node_type_geo_edge_split()
+{
+ namespace file_ns = blender::nodes::node_geo_edge_split_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_SPLIT_EDGES, "Split Edges", NODE_CLASS_GEOMETRY);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.declare = file_ns::node_declare;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc
new file mode 100644
index 00000000000..1d1c5bd2285
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc
@@ -0,0 +1,1365 @@
+/*
+ * 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_disjoint_set.hh"
+#include "BLI_task.hh"
+#include "BLI_vector_set.hh"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_attribute_math.hh"
+#include "BKE_mesh.h"
+#include "BKE_mesh_runtime.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_extrude_mesh_cc {
+
+NODE_STORAGE_FUNCS(NodeGeometryExtrudeMesh)
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>("Mesh").supported_type(GEO_COMPONENT_TYPE_MESH);
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).supports_field().hide_value();
+ b.add_input<decl::Vector>(N_("Offset")).subtype(PROP_TRANSLATION).implicit_field().hide_value();
+ b.add_input<decl::Float>(N_("Offset Scale")).default_value(1.0f).min(0.0f).supports_field();
+ b.add_input<decl::Bool>(N_("Individual")).default_value(true);
+ b.add_output<decl::Geometry>("Mesh");
+ b.add_output<decl::Bool>(N_("Top")).field_source();
+ b.add_output<decl::Bool>(N_("Side")).field_source();
+}
+
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
+ uiItemR(layout, ptr, "mode", 0, "", ICON_NONE);
+}
+
+static void node_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometryExtrudeMesh *data = MEM_cnew<NodeGeometryExtrudeMesh>(__func__);
+ data->mode = GEO_NODE_EXTRUDE_MESH_FACES;
+ node->storage = data;
+}
+
+static void node_update(bNodeTree *ntree, bNode *node)
+{
+ const NodeGeometryExtrudeMesh &storage = node_storage(*node);
+ const GeometryNodeExtrudeMeshMode mode = static_cast<GeometryNodeExtrudeMeshMode>(storage.mode);
+
+ bNodeSocket *individual_socket = (bNodeSocket *)node->inputs.last;
+
+ nodeSetSocketAvailability(ntree, individual_socket, mode == GEO_NODE_EXTRUDE_MESH_FACES);
+}
+
+struct AttributeOutputs {
+ StrongAnonymousAttributeID top_id;
+ StrongAnonymousAttributeID side_id;
+};
+
+static void save_selection_as_attribute(MeshComponent &component,
+ const AnonymousAttributeID *id,
+ const AttributeDomain domain,
+ const IndexMask selection)
+{
+ BLI_assert(!component.attribute_exists(id));
+
+ OutputAttribute_Typed<bool> attribute = component.attribute_try_get_for_output_only<bool>(
+ id, domain);
+ /* Rely on the new attribute being zeroed by default. */
+ BLI_assert(!attribute.as_span().as_span().contains(true));
+
+ if (selection.is_range()) {
+ attribute.as_span().slice(selection.as_range()).fill(true);
+ }
+ else {
+ attribute.as_span().fill_indices(selection, true);
+ }
+
+ attribute.save();
+}
+
+static MutableSpan<MVert> mesh_verts(Mesh &mesh)
+{
+ return {mesh.mvert, mesh.totvert};
+}
+static MutableSpan<MEdge> mesh_edges(Mesh &mesh)
+{
+ return {mesh.medge, mesh.totedge};
+}
+static Span<MPoly> mesh_polys(const Mesh &mesh)
+{
+ return {mesh.mpoly, mesh.totpoly};
+}
+static MutableSpan<MPoly> mesh_polys(Mesh &mesh)
+{
+ return {mesh.mpoly, mesh.totpoly};
+}
+static Span<MLoop> mesh_loops(const Mesh &mesh)
+{
+ return {mesh.mloop, mesh.totloop};
+}
+static MutableSpan<MLoop> mesh_loops(Mesh &mesh)
+{
+ return {mesh.mloop, mesh.totloop};
+}
+
+/**
+ * \note: Some areas in this file rely on the new sections of attributes from #CustomData_realloc
+ * to be zeroed.
+ */
+static void expand_mesh(Mesh &mesh,
+ const int vert_expand,
+ const int edge_expand,
+ const int poly_expand,
+ const int loop_expand)
+{
+ if (vert_expand != 0) {
+ CustomData_duplicate_referenced_layers(&mesh.vdata, mesh.totvert);
+ mesh.totvert += vert_expand;
+ CustomData_realloc(&mesh.vdata, mesh.totvert);
+ }
+ else {
+ /* Even when the number of vertices is not changed, the mesh can still be deformed. */
+ CustomData_duplicate_referenced_layer(&mesh.vdata, CD_MVERT, mesh.totvert);
+ }
+ if (edge_expand != 0) {
+ CustomData_duplicate_referenced_layers(&mesh.edata, mesh.totedge);
+ mesh.totedge += edge_expand;
+ CustomData_realloc(&mesh.edata, mesh.totedge);
+ }
+ if (poly_expand != 0) {
+ CustomData_duplicate_referenced_layers(&mesh.pdata, mesh.totpoly);
+ mesh.totpoly += poly_expand;
+ CustomData_realloc(&mesh.pdata, mesh.totpoly);
+ }
+ if (loop_expand != 0) {
+ CustomData_duplicate_referenced_layers(&mesh.ldata, mesh.totloop);
+ mesh.totloop += loop_expand;
+ CustomData_realloc(&mesh.ldata, mesh.totloop);
+ }
+ BKE_mesh_update_customdata_pointers(&mesh, false);
+}
+
+static MEdge new_edge(const int v1, const int v2)
+{
+ MEdge edge;
+ edge.v1 = v1;
+ edge.v2 = v2;
+ edge.flag = (ME_EDGEDRAW | ME_EDGERENDER);
+ return edge;
+}
+
+static MEdge new_loose_edge(const int v1, const int v2)
+{
+ MEdge edge;
+ edge.v1 = v1;
+ edge.v2 = v2;
+ edge.flag = ME_LOOSEEDGE;
+ return edge;
+}
+
+static MPoly new_poly(const int loopstart, const int totloop)
+{
+ MPoly poly;
+ poly.loopstart = loopstart;
+ poly.totloop = totloop;
+ poly.flag = 0;
+ return poly;
+}
+
+template<typename T> void copy_with_indices(MutableSpan<T> dst, Span<T> src, Span<int> indices)
+{
+ BLI_assert(dst.size() == indices.size());
+ for (const int i : dst.index_range()) {
+ dst[i] = src[indices[i]];
+ }
+}
+
+template<typename T> void copy_with_mask(MutableSpan<T> dst, Span<T> src, IndexMask mask)
+{
+ BLI_assert(dst.size() == mask.size());
+ threading::parallel_for(mask.index_range(), 512, [&](const IndexRange range) {
+ for (const int i : range) {
+ dst[i] = src[mask[i]];
+ }
+ });
+}
+
+/**
+ * \param get_mix_indices_fn: Returns a Span of indices of the source points to mix for every
+ * result point.
+ */
+template<typename T, typename GetMixIndicesFn>
+void copy_with_mixing(MutableSpan<T> dst, Span<T> src, GetMixIndicesFn get_mix_indices_fn)
+{
+ threading::parallel_for(dst.index_range(), 512, [&](const IndexRange range) {
+ attribute_math::DefaultPropatationMixer<T> mixer{dst.slice(range)};
+ for (const int i_dst : IndexRange(range.size())) {
+ for (const int i_src : get_mix_indices_fn(range[i_dst])) {
+ mixer.mix_in(i_dst, src[i_src]);
+ }
+ }
+ mixer.finalize();
+ });
+}
+
+static Array<Vector<int>> create_vert_to_edge_map(const int vert_size,
+ Span<MEdge> edges,
+ const int vert_offset = 0)
+{
+ Array<Vector<int>> vert_to_edge_map(vert_size);
+ for (const int i : edges.index_range()) {
+ vert_to_edge_map[edges[i].v1 - vert_offset].append(i);
+ vert_to_edge_map[edges[i].v2 - vert_offset].append(i);
+ }
+ return vert_to_edge_map;
+}
+
+static void extrude_mesh_vertices(MeshComponent &component,
+ const Field<bool> &selection_field,
+ const Field<float3> &offset_field,
+ const AttributeOutputs &attribute_outputs)
+{
+ Mesh &mesh = *component.get_for_write();
+ const int orig_vert_size = mesh.totvert;
+ const int orig_edge_size = mesh.totedge;
+
+ GeometryComponentFieldContext context{component, ATTR_DOMAIN_POINT};
+ FieldEvaluator evaluator{context, mesh.totvert};
+ evaluator.add(offset_field);
+ evaluator.set_selection(selection_field);
+ evaluator.evaluate();
+ const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
+ const VArray<float3> offsets = evaluator.get_evaluated<float3>(0);
+
+ /* This allows parallelizing attribute mixing for new edges. */
+ Array<Vector<int>> vert_to_edge_map = create_vert_to_edge_map(orig_vert_size, mesh_edges(mesh));
+
+ expand_mesh(mesh, selection.size(), selection.size(), 0, 0);
+
+ const IndexRange new_vert_range{orig_vert_size, selection.size()};
+ const IndexRange new_edge_range{orig_edge_size, selection.size()};
+
+ MutableSpan<MVert> new_verts = mesh_verts(mesh).slice(new_vert_range);
+ MutableSpan<MEdge> new_edges = mesh_edges(mesh).slice(new_edge_range);
+
+ for (const int i_selection : selection.index_range()) {
+ new_edges[i_selection] = new_loose_edge(selection[i_selection], new_vert_range[i_selection]);
+ }
+
+ component.attribute_foreach([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
+ if (!ELEM(meta_data.domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_EDGE)) {
+ return true;
+ }
+ OutputAttribute attribute = component.attribute_try_get_for_output(
+ id, meta_data.domain, meta_data.data_type);
+ attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) {
+ using T = decltype(dummy);
+ MutableSpan<T> data = attribute.as_span().typed<T>();
+ switch (attribute.domain()) {
+ case ATTR_DOMAIN_POINT: {
+ /* New vertices copy the attribute values from their source vertex. */
+ copy_with_mask(data.slice(new_vert_range), data.as_span(), selection);
+ break;
+ }
+ case ATTR_DOMAIN_EDGE: {
+ /* New edge values are mixed from of all the edges connected to the source vertex. */
+ copy_with_mixing(data.slice(new_edge_range), data.as_span(), [&](const int i) {
+ return vert_to_edge_map[selection[i]].as_span();
+ });
+ break;
+ }
+ default:
+ BLI_assert_unreachable();
+ }
+ });
+
+ attribute.save();
+ return true;
+ });
+
+ devirtualize_varray(offsets, [&](const auto offsets) {
+ threading::parallel_for(selection.index_range(), 1024, [&](const IndexRange range) {
+ for (const int i : range) {
+ const float3 offset = offsets[selection[i]];
+ add_v3_v3(new_verts[i].co, offset);
+ }
+ });
+ });
+
+ if (attribute_outputs.top_id) {
+ save_selection_as_attribute(
+ component, attribute_outputs.top_id.get(), ATTR_DOMAIN_POINT, new_vert_range);
+ }
+ if (attribute_outputs.side_id) {
+ save_selection_as_attribute(
+ component, attribute_outputs.side_id.get(), ATTR_DOMAIN_EDGE, new_edge_range);
+ }
+
+ BKE_mesh_runtime_clear_cache(&mesh);
+ BKE_mesh_normals_tag_dirty(&mesh);
+}
+
+static Array<Vector<int, 2>> mesh_calculate_polys_of_edge(const Mesh &mesh)
+{
+ Span<MPoly> polys = mesh_polys(mesh);
+ Span<MLoop> loops = mesh_loops(mesh);
+ Array<Vector<int, 2>> polys_of_edge(mesh.totedge);
+
+ for (const int i_poly : polys.index_range()) {
+ const MPoly &poly = polys[i_poly];
+ for (const MLoop &loop : loops.slice(poly.loopstart, poly.totloop)) {
+ polys_of_edge[loop.e].append(i_poly);
+ }
+ }
+
+ return polys_of_edge;
+}
+
+static void fill_quad_consistent_direction(Span<MLoop> other_poly_loops,
+ MutableSpan<MLoop> new_loops,
+ const int vert_connected_to_poly_1,
+ const int vert_connected_to_poly_2,
+ const int vert_across_from_poly_1,
+ const int vert_across_from_poly_2,
+ const int edge_connected_to_poly,
+ const int connecting_edge_1,
+ const int edge_across_from_poly,
+ const int connecting_edge_2)
+{
+ /* Find the loop on the polygon connected to the new quad that uses the duplicate edge. */
+ bool start_with_connecting_edge = true;
+ for (const MLoop &loop : other_poly_loops) {
+ if (loop.e == edge_connected_to_poly) {
+ start_with_connecting_edge = loop.v == vert_connected_to_poly_1;
+ break;
+ }
+ }
+ if (start_with_connecting_edge) {
+ new_loops[0].v = vert_connected_to_poly_1;
+ new_loops[0].e = connecting_edge_1;
+ new_loops[1].v = vert_across_from_poly_1;
+ new_loops[1].e = edge_across_from_poly;
+ new_loops[2].v = vert_across_from_poly_2;
+ new_loops[2].e = connecting_edge_2;
+ new_loops[3].v = vert_connected_to_poly_2;
+ new_loops[3].e = edge_connected_to_poly;
+ }
+ else {
+ new_loops[0].v = vert_connected_to_poly_1;
+ new_loops[0].e = edge_connected_to_poly;
+ new_loops[1].v = vert_connected_to_poly_2;
+ new_loops[1].e = connecting_edge_2;
+ new_loops[2].v = vert_across_from_poly_2;
+ new_loops[2].e = edge_across_from_poly;
+ new_loops[3].v = vert_across_from_poly_1;
+ new_loops[3].e = connecting_edge_1;
+ }
+}
+
+template<typename T>
+static VectorSet<int> vert_indices_from_edges(const Mesh &mesh, const Span<T> edge_indices)
+{
+ static_assert(is_same_any_v<T, int, int64_t>);
+
+ VectorSet<int> vert_indices;
+ vert_indices.reserve(edge_indices.size());
+ for (const T i_edge : edge_indices) {
+ const MEdge &edge = mesh.medge[i_edge];
+ vert_indices.add(edge.v1);
+ vert_indices.add(edge.v2);
+ }
+ return vert_indices;
+}
+
+static void extrude_mesh_edges(MeshComponent &component,
+ const Field<bool> &selection_field,
+ const Field<float3> &offset_field,
+ const AttributeOutputs &attribute_outputs)
+{
+ Mesh &mesh = *component.get_for_write();
+ const int orig_vert_size = mesh.totvert;
+ Span<MEdge> orig_edges = mesh_edges(mesh);
+ Span<MPoly> orig_polys = mesh_polys(mesh);
+ const int orig_loop_size = mesh.totloop;
+
+ GeometryComponentFieldContext edge_context{component, ATTR_DOMAIN_EDGE};
+ FieldEvaluator edge_evaluator{edge_context, mesh.totedge};
+ edge_evaluator.set_selection(selection_field);
+ edge_evaluator.add(offset_field);
+ edge_evaluator.evaluate();
+ const IndexMask edge_selection = edge_evaluator.get_evaluated_selection_as_mask();
+ const VArray<float3> &edge_offsets = edge_evaluator.get_evaluated<float3>(0);
+ if (edge_selection.is_empty()) {
+ return;
+ }
+
+ const Array<Vector<int, 2>> edge_to_poly_map = mesh_calculate_polys_of_edge(mesh);
+
+ /* Find the offsets on the vertex domain for translation. This must be done before the mesh's
+ * custom data layers are reallocated, in case the virtual array references on of them. */
+ Array<float3> vert_offsets;
+ if (!edge_offsets.is_single()) {
+ vert_offsets.reinitialize(orig_vert_size);
+ attribute_math::DefaultPropatationMixer<float3> mixer(vert_offsets);
+ for (const int i_edge : edge_selection) {
+ const MEdge &edge = orig_edges[i_edge];
+ const float3 offset = edge_offsets[i_edge];
+ mixer.mix_in(edge.v1, offset);
+ mixer.mix_in(edge.v2, offset);
+ }
+ mixer.finalize();
+ }
+
+ const VectorSet<int> new_vert_indices = vert_indices_from_edges(mesh, edge_selection.indices());
+
+ const IndexRange new_vert_range{orig_vert_size, new_vert_indices.size()};
+ /* The extruded edges connect the original and duplicate edges. */
+ const IndexRange connect_edge_range{orig_edges.size(), new_vert_range.size()};
+ /* The duplicate edges are extruded copies of the selected edges. */
+ const IndexRange duplicate_edge_range = connect_edge_range.after(edge_selection.size());
+ /* There is a new polygon for every selected edge. */
+ const IndexRange new_poly_range{orig_polys.size(), edge_selection.size()};
+ /* Every new polygon is a quad with four corners. */
+ const IndexRange new_loop_range{orig_loop_size, new_poly_range.size() * 4};
+
+ expand_mesh(mesh,
+ new_vert_range.size(),
+ connect_edge_range.size() + duplicate_edge_range.size(),
+ new_poly_range.size(),
+ new_loop_range.size());
+
+ MutableSpan<MVert> new_verts = mesh_verts(mesh).slice(new_vert_range);
+ MutableSpan<MEdge> connect_edges = mesh_edges(mesh).slice(connect_edge_range);
+ MutableSpan<MEdge> duplicate_edges = mesh_edges(mesh).slice(duplicate_edge_range);
+ MutableSpan<MPoly> polys = mesh_polys(mesh);
+ MutableSpan<MPoly> new_polys = polys.slice(new_poly_range);
+ MutableSpan<MLoop> loops = mesh_loops(mesh);
+ MutableSpan<MLoop> new_loops = loops.slice(new_loop_range);
+
+ for (const int i : connect_edges.index_range()) {
+ connect_edges[i] = new_edge(new_vert_indices[i], new_vert_range[i]);
+ }
+
+ for (const int i : duplicate_edges.index_range()) {
+ const MEdge &orig_edge = mesh.medge[edge_selection[i]];
+ const int i_new_vert_1 = new_vert_indices.index_of(orig_edge.v1);
+ const int i_new_vert_2 = new_vert_indices.index_of(orig_edge.v2);
+ duplicate_edges[i] = new_edge(new_vert_range[i_new_vert_1], new_vert_range[i_new_vert_2]);
+ }
+
+ for (const int i : new_polys.index_range()) {
+ new_polys[i] = new_poly(new_loop_range[i * 4], 4);
+ }
+
+ for (const int i : edge_selection.index_range()) {
+ const int orig_edge_index = edge_selection[i];
+
+ const MEdge &duplicate_edge = duplicate_edges[i];
+ const int new_vert_1 = duplicate_edge.v1;
+ const int new_vert_2 = duplicate_edge.v2;
+ const int extrude_index_1 = new_vert_1 - orig_vert_size;
+ const int extrude_index_2 = new_vert_2 - orig_vert_size;
+
+ Span<int> connected_polys = edge_to_poly_map[orig_edge_index];
+
+ /* When there was a single polygon connected to the new polygon, we can use the old one to keep
+ * the face direction consistent. When there is more than one connected edge, the new face
+ * direction is totally arbitrary and the only goal for the behavior is to be deterministic. */
+ Span<MLoop> connected_poly_loops = {};
+ if (connected_polys.size() == 1) {
+ const MPoly &connected_poly = polys[connected_polys.first()];
+ connected_poly_loops = loops.slice(connected_poly.loopstart, connected_poly.totloop);
+ }
+ fill_quad_consistent_direction(connected_poly_loops,
+ new_loops.slice(4 * i, 4),
+ new_vert_indices[extrude_index_1],
+ new_vert_indices[extrude_index_2],
+ new_vert_1,
+ new_vert_2,
+ orig_edge_index,
+ connect_edge_range[extrude_index_1],
+ duplicate_edge_range[i],
+ connect_edge_range[extrude_index_2]);
+ }
+
+ /* Create a map of indices in the extruded vertices array to all of the indices of edges
+ * in the duplicate edges array that connect to that vertex. This can be used to simplify the
+ * mixing of attribute data for the connecting edges. */
+ const Array<Vector<int>> new_vert_to_duplicate_edge_map = create_vert_to_edge_map(
+ new_vert_range.size(), duplicate_edges, orig_vert_size);
+
+ component.attribute_foreach([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
+ OutputAttribute attribute = component.attribute_try_get_for_output(
+ id, meta_data.domain, meta_data.data_type);
+ if (!attribute) {
+ return true; /* Impossible to write the "normal" attribute. */
+ }
+
+ attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) {
+ using T = decltype(dummy);
+ MutableSpan<T> data = attribute.as_span().typed<T>();
+ switch (attribute.domain()) {
+ case ATTR_DOMAIN_POINT: {
+ /* New vertices copy the attribute values from their source vertex. */
+ copy_with_indices(data.slice(new_vert_range), data.as_span(), new_vert_indices);
+ break;
+ }
+ case ATTR_DOMAIN_EDGE: {
+ /* Edges parallel to original edges copy the edge attributes from the original edges. */
+ MutableSpan<T> duplicate_data = data.slice(duplicate_edge_range);
+ copy_with_mask(duplicate_data, data.as_span(), edge_selection);
+
+ /* Edges connected to original vertices mix values of selected connected edges. */
+ MutableSpan<T> connect_data = data.slice(connect_edge_range);
+ copy_with_mixing(connect_data, duplicate_data.as_span(), [&](const int i_new_vert) {
+ return new_vert_to_duplicate_edge_map[i_new_vert].as_span();
+ });
+ break;
+ }
+ case ATTR_DOMAIN_FACE: {
+ /* Attribute values for new faces are a mix of the values of faces connected to the its
+ * original edge. */
+ copy_with_mixing(data.slice(new_poly_range), data.as_span(), [&](const int i) {
+ return edge_to_poly_map[edge_selection[i]].as_span();
+ });
+
+ break;
+ }
+ case ATTR_DOMAIN_CORNER: {
+ /* New corners get the average value of all adjacent corners on original faces connected
+ * to the original edge of their face. */
+ MutableSpan<T> new_data = data.slice(new_loop_range);
+ threading::parallel_for(edge_selection.index_range(), 256, [&](const IndexRange range) {
+ for (const int i_edge_selection : range) {
+ const int orig_edge_index = edge_selection[i_edge_selection];
+
+ Span<int> connected_polys = edge_to_poly_map[orig_edge_index];
+ if (connected_polys.is_empty()) {
+ /* If there are no connected polygons, there is no corner data to
+ * interpolate. */
+ new_data.slice(4 * i_edge_selection, 4).fill(T());
+ continue;
+ }
+
+ /* Both corners on each vertical edge of the side polygon get the same value,
+ * so there are only two unique values to mix. */
+ Array<T> side_poly_corner_data(2);
+ attribute_math::DefaultPropatationMixer<T> mixer{side_poly_corner_data};
+
+ const MEdge &duplicate_edge = duplicate_edges[i_edge_selection];
+ const int new_vert_1 = duplicate_edge.v1;
+ const int new_vert_2 = duplicate_edge.v2;
+ const int orig_vert_1 = new_vert_indices[new_vert_1 - orig_vert_size];
+ const int orig_vert_2 = new_vert_indices[new_vert_2 - orig_vert_size];
+
+ /* Average the corner data from the corners that share a vertex from the
+ * polygons that share an edge with the extruded edge. */
+ for (const int i_connected_poly : connected_polys.index_range()) {
+ const MPoly &connected_poly = polys[connected_polys[i_connected_poly]];
+ for (const int i_loop :
+ IndexRange(connected_poly.loopstart, connected_poly.totloop)) {
+ const MLoop &loop = loops[i_loop];
+ if (loop.v == orig_vert_1) {
+ mixer.mix_in(0, data[i_loop]);
+ }
+ if (loop.v == orig_vert_2) {
+ mixer.mix_in(1, data[i_loop]);
+ }
+ }
+ }
+
+ mixer.finalize();
+
+ /* Instead of replicating the order in #fill_quad_consistent_direction here, it's
+ * simpler (though probably slower) to just match the corner data based on the vertex
+ * indices. */
+ for (const int i : IndexRange(4 * i_edge_selection, 4)) {
+ if (ELEM(new_loops[i].v, new_vert_1, orig_vert_1)) {
+ new_data[i] = side_poly_corner_data.first();
+ }
+ else if (ELEM(new_loops[i].v, new_vert_2, orig_vert_2)) {
+ new_data[i] = side_poly_corner_data.last();
+ }
+ }
+ }
+ });
+ break;
+ }
+ default:
+ BLI_assert_unreachable();
+ }
+ });
+
+ attribute.save();
+ return true;
+ });
+
+ if (edge_offsets.is_single()) {
+ const float3 offset = edge_offsets.get_internal_single();
+ threading::parallel_for(new_verts.index_range(), 1024, [&](const IndexRange range) {
+ for (const int i : range) {
+ add_v3_v3(new_verts[i].co, offset);
+ }
+ });
+ }
+ else {
+ threading::parallel_for(new_verts.index_range(), 1024, [&](const IndexRange range) {
+ for (const int i : range) {
+ add_v3_v3(new_verts[i].co, vert_offsets[new_vert_indices[i]]);
+ }
+ });
+ }
+
+ if (attribute_outputs.top_id) {
+ save_selection_as_attribute(
+ component, attribute_outputs.top_id.get(), ATTR_DOMAIN_EDGE, duplicate_edge_range);
+ }
+ if (attribute_outputs.side_id) {
+ save_selection_as_attribute(
+ component, attribute_outputs.side_id.get(), ATTR_DOMAIN_FACE, new_poly_range);
+ }
+
+ BKE_mesh_runtime_clear_cache(&mesh);
+ BKE_mesh_normals_tag_dirty(&mesh);
+}
+
+/**
+ * Edges connected to one selected face are on the boundary of a region and will be duplicated into
+ * a "side face". Edges inside a region will be duplicated to leave any original faces unchanged.
+ */
+static void extrude_mesh_face_regions(MeshComponent &component,
+ const Field<bool> &selection_field,
+ const Field<float3> &offset_field,
+ const AttributeOutputs &attribute_outputs)
+{
+ Mesh &mesh = *component.get_for_write();
+ const int orig_vert_size = mesh.totvert;
+ Span<MEdge> orig_edges = mesh_edges(mesh);
+ Span<MPoly> orig_polys = mesh_polys(mesh);
+ Span<MLoop> orig_loops = mesh_loops(mesh);
+
+ GeometryComponentFieldContext poly_context{component, ATTR_DOMAIN_FACE};
+ FieldEvaluator poly_evaluator{poly_context, mesh.totpoly};
+ poly_evaluator.set_selection(selection_field);
+ poly_evaluator.add(offset_field);
+ poly_evaluator.evaluate();
+ const IndexMask poly_selection = poly_evaluator.get_evaluated_selection_as_mask();
+ const VArray<float3> &poly_offsets = poly_evaluator.get_evaluated<float3>(0);
+ if (poly_selection.is_empty()) {
+ return;
+ }
+
+ Array<bool> poly_selection_array(orig_polys.size(), false);
+ for (const int i_poly : poly_selection) {
+ poly_selection_array[i_poly] = true;
+ }
+
+ /* Mix the offsets from the face domain to the vertex domain. Evaluate on the face domain above
+ * in order to be consistent with the selection, and to use the face normals rather than vertex
+ * normals as an offset, for example. */
+ Array<float3> vert_offsets;
+ if (!poly_offsets.is_single()) {
+ vert_offsets.reinitialize(orig_vert_size);
+ attribute_math::DefaultPropatationMixer<float3> mixer(vert_offsets);
+ for (const int i_poly : poly_selection) {
+ const MPoly &poly = orig_polys[i_poly];
+ const float3 offset = poly_offsets[i_poly];
+ for (const MLoop &loop : orig_loops.slice(poly.loopstart, poly.totloop)) {
+ mixer.mix_in(loop.v, offset);
+ }
+ }
+ mixer.finalize();
+ }
+
+ /* All of the faces (selected and deselected) connected to each edge. */
+ const Array<Vector<int, 2>> edge_to_poly_map = mesh_calculate_polys_of_edge(mesh);
+
+ /* All vertices that are connected to the selected polygons.
+ * Start the size at one vert per poly to reduce unnecessary reallocation. */
+ VectorSet<int> all_selected_verts;
+ all_selected_verts.reserve(orig_polys.size());
+ for (const int i_poly : poly_selection) {
+ const MPoly &poly = orig_polys[i_poly];
+ for (const MLoop &loop : orig_loops.slice(poly.loopstart, poly.totloop)) {
+ all_selected_verts.add(loop.v);
+ }
+ }
+
+ /* Edges inside of an extruded region that are also attached to deselected edges. They must be
+ * duplicated in order to leave the old edge attached to the unchanged deselected faces. */
+ VectorSet<int> new_inner_edge_indices;
+ /* Edges inside of an extruded region. Their vertices should be translated
+ * with the offset, but the edges themselves should not be duplicated. */
+ Vector<int> inner_edge_indices;
+ /* The extruded face corresponding to each boundary edge (and each boundary face). */
+ Vector<int> edge_extruded_face_indices;
+ /* Edges on the outside of selected regions, either because there are no
+ * other connected faces, or because all of the other faces aren't selected. */
+ VectorSet<int> boundary_edge_indices;
+ for (const int i_edge : orig_edges.index_range()) {
+ Span<int> polys = edge_to_poly_map[i_edge];
+
+ int i_selected_poly = -1;
+ int deselected_poly_count = 0;
+ int selected_poly_count = 0;
+ for (const int i_other_poly : polys) {
+ if (poly_selection_array[i_other_poly]) {
+ selected_poly_count++;
+ i_selected_poly = i_other_poly;
+ }
+ else {
+ deselected_poly_count++;
+ }
+ }
+
+ if (selected_poly_count == 1) {
+ /* If there is only one selected polygon connected to the edge,
+ * the edge should be extruded to form a "side face". */
+ boundary_edge_indices.add_new(i_edge);
+ edge_extruded_face_indices.append(i_selected_poly);
+ }
+ else if (selected_poly_count > 1) {
+ /* The edge is inside an extruded region of faces. */
+ if (deselected_poly_count > 0) {
+ /* Add edges that are also connected to deselected edges to a separate list. */
+ new_inner_edge_indices.add_new(i_edge);
+ }
+ else {
+ /* Otherwise, just keep track of edges inside the region so that
+ * we can reattach them to duplicated vertices if necessary. */
+ inner_edge_indices.append(i_edge);
+ }
+ }
+ }
+
+ VectorSet<int> new_vert_indices = vert_indices_from_edges(mesh, boundary_edge_indices.as_span());
+ /* Before adding the rest of the new vertices from the new inner edges, store the number
+ * of new vertices from the boundary edges, since this is the number of connecting edges. */
+ const int extruded_vert_size = new_vert_indices.size();
+
+ /* The vertices attached to duplicate inner edges also have to be duplicated. */
+ for (const int i_edge : new_inner_edge_indices) {
+ const MEdge &edge = mesh.medge[i_edge];
+ new_vert_indices.add(edge.v1);
+ new_vert_indices.add(edge.v2);
+ }
+
+ /* New vertices forming the duplicated boundary edges and the ends of the new inner edges. */
+ const IndexRange new_vert_range{orig_vert_size, new_vert_indices.size()};
+ /* One edge connects each selected vertex to a new vertex on the extruded polygons. */
+ const IndexRange connect_edge_range{orig_edges.size(), extruded_vert_size};
+ /* Each selected edge is duplicated to form a single edge on the extrusion. */
+ const IndexRange boundary_edge_range = connect_edge_range.after(boundary_edge_indices.size());
+ /* Duplicated edges inside regions that were connected to deselected faces. */
+ const IndexRange new_inner_edge_range = boundary_edge_range.after(new_inner_edge_indices.size());
+ /* Each edge selected for extrusion is extruded into a single face. */
+ const IndexRange side_poly_range{orig_polys.size(), boundary_edge_indices.size()};
+ /* The loops that form the new side faces. */
+ const IndexRange side_loop_range{orig_loops.size(), side_poly_range.size() * 4};
+
+ expand_mesh(mesh,
+ new_vert_range.size(),
+ connect_edge_range.size() + boundary_edge_range.size() + new_inner_edge_range.size(),
+ side_poly_range.size(),
+ side_loop_range.size());
+
+ MutableSpan<MEdge> edges = mesh_edges(mesh);
+ MutableSpan<MEdge> connect_edges = edges.slice(connect_edge_range);
+ MutableSpan<MEdge> boundary_edges = edges.slice(boundary_edge_range);
+ MutableSpan<MEdge> new_inner_edges = edges.slice(new_inner_edge_range);
+ MutableSpan<MPoly> polys = mesh_polys(mesh);
+ MutableSpan<MPoly> new_polys = polys.slice(side_poly_range);
+ MutableSpan<MLoop> loops = mesh_loops(mesh);
+ MutableSpan<MLoop> new_loops = loops.slice(side_loop_range);
+
+ /* Initialize the edges that form the sides of the extrusion. */
+ for (const int i : connect_edges.index_range()) {
+ connect_edges[i] = new_edge(new_vert_indices[i], new_vert_range[i]);
+ }
+
+ /* Initialize the edges that form the top of the extrusion. */
+ for (const int i : boundary_edges.index_range()) {
+ const MEdge &orig_edge = edges[boundary_edge_indices[i]];
+ const int i_new_vert_1 = new_vert_indices.index_of(orig_edge.v1);
+ const int i_new_vert_2 = new_vert_indices.index_of(orig_edge.v2);
+ boundary_edges[i] = new_edge(new_vert_range[i_new_vert_1], new_vert_range[i_new_vert_2]);
+ }
+
+ /* Initialize the new edges inside of extrude regions. */
+ for (const int i : new_inner_edge_indices.index_range()) {
+ const MEdge &orig_edge = edges[new_inner_edge_indices[i]];
+ const int i_new_vert_1 = new_vert_indices.index_of(orig_edge.v1);
+ const int i_new_vert_2 = new_vert_indices.index_of(orig_edge.v2);
+ new_inner_edges[i] = new_edge(new_vert_range[i_new_vert_1], new_vert_range[i_new_vert_2]);
+ }
+
+ /* Initialize the new side polygons. */
+ for (const int i : new_polys.index_range()) {
+ new_polys[i] = new_poly(side_loop_range[i * 4], 4);
+ }
+
+ /* Connect original edges inside face regions to any new vertices, if necessary. */
+ for (const int i : inner_edge_indices) {
+ MEdge &edge = edges[i];
+ const int i_new_vert_1 = new_vert_indices.index_of_try(edge.v1);
+ const int i_new_vert_2 = new_vert_indices.index_of_try(edge.v2);
+ if (i_new_vert_1 != -1) {
+ edge.v1 = new_vert_range[i_new_vert_1];
+ }
+ if (i_new_vert_2 != -1) {
+ edge.v2 = new_vert_range[i_new_vert_2];
+ }
+ }
+
+ /* Connect the selected faces to the extruded or duplicated edges and the new vertices. */
+ for (const int i_poly : poly_selection) {
+ const MPoly &poly = polys[i_poly];
+ for (MLoop &loop : loops.slice(poly.loopstart, poly.totloop)) {
+ const int i_new_vert = new_vert_indices.index_of_try(loop.v);
+ if (i_new_vert != -1) {
+ loop.v = new_vert_range[i_new_vert];
+ }
+ const int i_boundary_edge = boundary_edge_indices.index_of_try(loop.e);
+ if (i_boundary_edge != -1) {
+ loop.e = boundary_edge_range[i_boundary_edge];
+ /* Skip the next check, an edge cannot be both a boundary edge and an inner edge. */
+ continue;
+ }
+ const int i_new_inner_edge = new_inner_edge_indices.index_of_try(loop.e);
+ if (i_new_inner_edge != -1) {
+ loop.e = new_inner_edge_range[i_new_inner_edge];
+ }
+ }
+ }
+
+ /* Create the faces on the sides of extruded regions. */
+ for (const int i : boundary_edge_indices.index_range()) {
+ const MEdge &boundary_edge = boundary_edges[i];
+ const int new_vert_1 = boundary_edge.v1;
+ const int new_vert_2 = boundary_edge.v2;
+ const int extrude_index_1 = new_vert_1 - orig_vert_size;
+ const int extrude_index_2 = new_vert_2 - orig_vert_size;
+
+ const MPoly &extrude_poly = polys[edge_extruded_face_indices[i]];
+
+ fill_quad_consistent_direction(loops.slice(extrude_poly.loopstart, extrude_poly.totloop),
+ new_loops.slice(4 * i, 4),
+ new_vert_1,
+ new_vert_2,
+ new_vert_indices[extrude_index_1],
+ new_vert_indices[extrude_index_2],
+ boundary_edge_range[i],
+ connect_edge_range[extrude_index_1],
+ boundary_edge_indices[i],
+ connect_edge_range[extrude_index_2]);
+ }
+
+ /* Create a map of indices in the extruded vertices array to all of the indices of edges
+ * in the duplicate edges array that connect to that vertex. This can be used to simplify the
+ * mixing of attribute data for the connecting edges. */
+ const Array<Vector<int>> new_vert_to_duplicate_edge_map = create_vert_to_edge_map(
+ new_vert_range.size(), boundary_edges, orig_vert_size);
+
+ component.attribute_foreach([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
+ OutputAttribute attribute = component.attribute_try_get_for_output(
+ id, meta_data.domain, meta_data.data_type);
+ if (!attribute) {
+ return true; /* Impossible to write the "normal" attribute. */
+ }
+
+ attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) {
+ using T = decltype(dummy);
+ MutableSpan<T> data = attribute.as_span().typed<T>();
+ switch (attribute.domain()) {
+ case ATTR_DOMAIN_POINT: {
+ /* New vertices copy the attributes from their original vertices. */
+ copy_with_indices(data.slice(new_vert_range), data.as_span(), new_vert_indices);
+ break;
+ }
+ case ATTR_DOMAIN_EDGE: {
+ /* Edges parallel to original edges copy the edge attributes from the original edges. */
+ MutableSpan<T> boundary_data = data.slice(boundary_edge_range);
+ copy_with_indices(boundary_data, data.as_span(), boundary_edge_indices);
+
+ /* Edges inside of face regions also just duplicate their source data. */
+ MutableSpan<T> new_inner_data = data.slice(new_inner_edge_range);
+ copy_with_indices(new_inner_data, data.as_span(), new_inner_edge_indices);
+
+ /* Edges connected to original vertices mix values of selected connected edges. */
+ MutableSpan<T> connect_data = data.slice(connect_edge_range);
+ copy_with_mixing(connect_data, boundary_data.as_span(), [&](const int i) {
+ return new_vert_to_duplicate_edge_map[i].as_span();
+ });
+ break;
+ }
+ case ATTR_DOMAIN_FACE: {
+ /* New faces on the side of extrusions get the values from the corresponding selected
+ * face. */
+ copy_with_indices(
+ data.slice(side_poly_range), data.as_span(), edge_extruded_face_indices);
+ break;
+ }
+ case ATTR_DOMAIN_CORNER: {
+ /* New corners get the values from the corresponding corner on the extruded face. */
+ MutableSpan<T> new_data = data.slice(side_loop_range);
+ threading::parallel_for(
+ boundary_edge_indices.index_range(), 256, [&](const IndexRange range) {
+ for (const int i_boundary_edge : range) {
+ const MPoly &poly = polys[edge_extruded_face_indices[i_boundary_edge]];
+
+ const MEdge &boundary_edge = boundary_edges[i_boundary_edge];
+ const int new_vert_1 = boundary_edge.v1;
+ const int new_vert_2 = boundary_edge.v2;
+ const int orig_vert_1 = new_vert_indices[new_vert_1 - orig_vert_size];
+ const int orig_vert_2 = new_vert_indices[new_vert_2 - orig_vert_size];
+
+ /* Retrieve the data for the first two sides of the quad from the extruded
+ * polygon, which we generally expect to have just a small amount of sides. This
+ * loop could be eliminated by adding a cache of connected loops (which would
+ * also simplify some of the other code to find the correct loops on the extruded
+ * face). */
+ T data_1;
+ T data_2;
+ for (const int i_loop : IndexRange(poly.loopstart, poly.totloop)) {
+ if (loops[i_loop].v == new_vert_1) {
+ data_1 = data[i_loop];
+ }
+ if (loops[i_loop].v == new_vert_2) {
+ data_2 = data[i_loop];
+ }
+ }
+
+ /* Instead of replicating the order in #fill_quad_consistent_direction here, it's
+ * simpler (though probably slower) to just match the corner data based on the
+ * vertex indices. */
+ for (const int i : IndexRange(4 * i_boundary_edge, 4)) {
+ if (ELEM(new_loops[i].v, new_vert_1, orig_vert_1)) {
+ new_data[i] = data_1;
+ }
+ else if (ELEM(new_loops[i].v, new_vert_2, orig_vert_2)) {
+ new_data[i] = data_2;
+ }
+ }
+ }
+ });
+ break;
+ }
+ default:
+ BLI_assert_unreachable();
+ }
+ });
+
+ attribute.save();
+ return true;
+ });
+
+ /* Translate vertices based on the offset. If the vertex is used by a selected edge, it will
+ * have been duplicated and only the new vertex should use the offset. Otherwise the vertex might
+ * still need an offset, but it was reused on the inside of a region of extruded faces. */
+ if (poly_offsets.is_single()) {
+ const float3 offset = poly_offsets.get_internal_single();
+ threading::parallel_for(
+ IndexRange(all_selected_verts.size()), 1024, [&](const IndexRange range) {
+ for (const int i_orig : all_selected_verts.as_span().slice(range)) {
+ const int i_new = new_vert_indices.index_of_try(i_orig);
+ MVert &vert = mesh_verts(mesh)[(i_new == -1) ? i_orig : new_vert_range[i_new]];
+ add_v3_v3(vert.co, offset);
+ }
+ });
+ }
+ else {
+ threading::parallel_for(
+ IndexRange(all_selected_verts.size()), 1024, [&](const IndexRange range) {
+ for (const int i_orig : all_selected_verts.as_span().slice(range)) {
+ const int i_new = new_vert_indices.index_of_try(i_orig);
+ const float3 offset = vert_offsets[i_orig];
+ MVert &vert = mesh_verts(mesh)[(i_new == -1) ? i_orig : new_vert_range[i_new]];
+ add_v3_v3(vert.co, offset);
+ }
+ });
+ }
+
+ if (attribute_outputs.top_id) {
+ save_selection_as_attribute(
+ component, attribute_outputs.top_id.get(), ATTR_DOMAIN_FACE, poly_selection);
+ }
+ if (attribute_outputs.side_id) {
+ save_selection_as_attribute(
+ component, attribute_outputs.side_id.get(), ATTR_DOMAIN_FACE, side_poly_range);
+ }
+
+ BKE_mesh_runtime_clear_cache(&mesh);
+ BKE_mesh_normals_tag_dirty(&mesh);
+}
+
+/* Get the range into an array of extruded corners, edges, or vertices for a particular polygon. */
+static IndexRange selected_corner_range(Span<int> offsets, const int index)
+{
+ const int offset = offsets[index];
+ const int next_offset = offsets[index + 1];
+ return IndexRange(offset, next_offset - offset);
+}
+
+static void extrude_individual_mesh_faces(MeshComponent &component,
+ const Field<bool> &selection_field,
+ const Field<float3> &offset_field,
+ const AttributeOutputs &attribute_outputs)
+{
+ Mesh &mesh = *component.get_for_write();
+ const int orig_vert_size = mesh.totvert;
+ const int orig_edge_size = mesh.totedge;
+ Span<MPoly> orig_polys = mesh_polys(mesh);
+ Span<MLoop> orig_loops = mesh_loops(mesh);
+
+ /* Use a mesh for the result of the evaluation because the mesh is reallocated before
+ * the vertices are moved, and the evaluated result might reference an attribute. */
+ Array<float3> poly_offset(orig_polys.size());
+ GeometryComponentFieldContext poly_context{component, ATTR_DOMAIN_FACE};
+ FieldEvaluator poly_evaluator{poly_context, mesh.totpoly};
+ poly_evaluator.set_selection(selection_field);
+ poly_evaluator.add_with_destination(offset_field, poly_offset.as_mutable_span());
+ poly_evaluator.evaluate();
+ const IndexMask poly_selection = poly_evaluator.get_evaluated_selection_as_mask();
+
+ /* Build an array of offsets into the new data for each polygon. This is used to facilitate
+ * parallelism later on by avoiding the need to keep track of an offset when iterating through
+ * all polygons. */
+ int extrude_corner_size = 0;
+ Array<int> index_offsets(poly_selection.size() + 1);
+ for (const int i_selection : poly_selection.index_range()) {
+ const MPoly &poly = orig_polys[poly_selection[i_selection]];
+ index_offsets[i_selection] = extrude_corner_size;
+ extrude_corner_size += poly.totloop;
+ }
+ index_offsets.last() = extrude_corner_size;
+
+ const IndexRange new_vert_range{orig_vert_size, extrude_corner_size};
+ /* One edge connects each selected vertex to a new vertex on the extruded polygons. */
+ const IndexRange connect_edge_range{orig_edge_size, extrude_corner_size};
+ /* Each selected edge is duplicated to form a single edge on the extrusion. */
+ const IndexRange duplicate_edge_range = connect_edge_range.after(extrude_corner_size);
+ /* Each edge selected for extrusion is extruded into a single face. */
+ const IndexRange side_poly_range{orig_polys.size(), duplicate_edge_range.size()};
+ const IndexRange side_loop_range{orig_loops.size(), side_poly_range.size() * 4};
+
+ expand_mesh(mesh,
+ new_vert_range.size(),
+ connect_edge_range.size() + duplicate_edge_range.size(),
+ side_poly_range.size(),
+ side_loop_range.size());
+
+ MutableSpan<MVert> new_verts = mesh_verts(mesh).slice(new_vert_range);
+ MutableSpan<MEdge> edges{mesh.medge, mesh.totedge};
+ MutableSpan<MEdge> connect_edges = edges.slice(connect_edge_range);
+ MutableSpan<MEdge> duplicate_edges = edges.slice(duplicate_edge_range);
+ MutableSpan<MPoly> polys{mesh.mpoly, mesh.totpoly};
+ MutableSpan<MPoly> new_polys = polys.slice(side_poly_range);
+ MutableSpan<MLoop> loops{mesh.mloop, mesh.totloop};
+
+ /* For every selected polygon, build the faces that form the sides of the extrusion. Filling some
+ * of this data like the new edges or polygons could be easily split into separate loops, which
+ * may or may not be faster, and would involve more duplication. */
+ threading::parallel_for(poly_selection.index_range(), 256, [&](const IndexRange range) {
+ for (const int i_selection : range) {
+ const IndexRange poly_corner_range = selected_corner_range(index_offsets, i_selection);
+
+ const MPoly &poly = polys[poly_selection[i_selection]];
+ Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop);
+
+ for (const int i : IndexRange(poly.totloop)) {
+ const int i_next = (i == poly.totloop - 1) ? 0 : i + 1;
+ const MLoop &orig_loop = poly_loops[i];
+ const MLoop &orig_loop_next = poly_loops[i_next];
+
+ const int i_extrude = poly_corner_range[i];
+ const int i_extrude_next = poly_corner_range[i_next];
+
+ const int i_duplicate_edge = duplicate_edge_range[i_extrude];
+ const int new_vert = new_vert_range[i_extrude];
+ const int new_vert_next = new_vert_range[i_extrude_next];
+
+ const int orig_edge = orig_loop.e;
+
+ const int orig_vert = orig_loop.v;
+ const int orig_vert_next = orig_loop_next.v;
+
+ duplicate_edges[i_extrude] = new_edge(new_vert, new_vert_next);
+
+ new_polys[i_extrude] = new_poly(side_loop_range[i_extrude * 4], 4);
+
+ MutableSpan<MLoop> side_loops = loops.slice(side_loop_range[i_extrude * 4], 4);
+ side_loops[0].v = new_vert_next;
+ side_loops[0].e = i_duplicate_edge;
+ side_loops[1].v = new_vert;
+ side_loops[1].e = connect_edge_range[i_extrude];
+ side_loops[2].v = orig_vert;
+ side_loops[2].e = orig_edge;
+ side_loops[3].v = orig_vert_next;
+ side_loops[3].e = connect_edge_range[i_extrude_next];
+
+ connect_edges[i_extrude] = new_edge(orig_vert, new_vert);
+ }
+ }
+ });
+
+ component.attribute_foreach([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
+ OutputAttribute attribute = component.attribute_try_get_for_output(
+ id, meta_data.domain, meta_data.data_type);
+ if (!attribute) {
+ return true; /* Impossible to write the "normal" attribute. */
+ }
+
+ attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) {
+ using T = decltype(dummy);
+ MutableSpan<T> data = attribute.as_span().typed<T>();
+ switch (attribute.domain()) {
+ case ATTR_DOMAIN_POINT: {
+ /* New vertices copy the attributes from their original vertices. */
+ MutableSpan<T> new_data = data.slice(new_vert_range);
+
+ threading::parallel_for(poly_selection.index_range(), 1024, [&](const IndexRange range) {
+ for (const int i_selection : range) {
+ const MPoly &poly = polys[poly_selection[i_selection]];
+ Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop);
+
+ const int corner_offset = index_offsets[i_selection];
+ for (const int i : poly_loops.index_range()) {
+ const int orig_index = poly_loops[i].v;
+ new_data[corner_offset + i] = data[orig_index];
+ }
+ }
+ });
+ break;
+ }
+ case ATTR_DOMAIN_EDGE: {
+ MutableSpan<T> duplicate_data = data.slice(duplicate_edge_range);
+ MutableSpan<T> connect_data = data.slice(connect_edge_range);
+
+ threading::parallel_for(poly_selection.index_range(), 512, [&](const IndexRange range) {
+ for (const int i_selection : range) {
+ const MPoly &poly = polys[poly_selection[i_selection]];
+ Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop);
+
+ const IndexRange poly_corner_range = selected_corner_range(index_offsets,
+ i_selection);
+
+ /* The data for the duplicate edge is simply a copy of the original edge's data. */
+ for (const int i : poly_loops.index_range()) {
+ const int orig_index = poly_loops[i].e;
+ duplicate_data[poly_corner_range[i]] = data[orig_index];
+ }
+
+ /* For the extruded edges, mix the data from the two neighboring original edges of
+ * the extruded polygon. */
+ for (const int i : poly_loops.index_range()) {
+ const int i_loop_prev = (i == 0) ? poly.totloop - 1 : i - 1;
+ const int orig_index = poly_loops[i].e;
+ const int orig_index_prev = poly_loops[i_loop_prev].e;
+ if constexpr (std::is_same_v<T, bool>) {
+ /* Propagate selections with "or" instead of "at least half". */
+ connect_data[poly_corner_range[i]] = data[orig_index] || data[orig_index_prev];
+ }
+ else {
+ connect_data[poly_corner_range[i]] = attribute_math::mix2(
+ 0.5f, data[orig_index], data[orig_index_prev]);
+ }
+ }
+ }
+ });
+ break;
+ }
+ case ATTR_DOMAIN_FACE: {
+ /* Each side face gets the values from the corresponding new face. */
+ MutableSpan<T> new_data = data.slice(side_poly_range);
+ threading::parallel_for(poly_selection.index_range(), 1024, [&](const IndexRange range) {
+ for (const int i_selection : range) {
+ const int poly_index = poly_selection[i_selection];
+ const IndexRange poly_corner_range = selected_corner_range(index_offsets,
+ i_selection);
+ new_data.slice(poly_corner_range).fill(data[poly_index]);
+ }
+ });
+ break;
+ }
+ case ATTR_DOMAIN_CORNER: {
+ /* Each corner on a side face gets its value from the matching corner on an extruded
+ * face. */
+ MutableSpan<T> new_data = data.slice(side_loop_range);
+ threading::parallel_for(poly_selection.index_range(), 256, [&](const IndexRange range) {
+ for (const int i_selection : range) {
+ const MPoly &poly = polys[poly_selection[i_selection]];
+ Span<T> poly_loop_data = data.slice(poly.loopstart, poly.totloop);
+ const IndexRange poly_corner_range = selected_corner_range(index_offsets,
+ i_selection);
+
+ for (const int i : IndexRange(poly.totloop)) {
+ const int i_next = (i == poly.totloop - 1) ? 0 : i + 1;
+ const int i_extrude = poly_corner_range[i];
+
+ MutableSpan<T> side_loop_data = new_data.slice(i_extrude * 4, 4);
+
+ /* The two corners on each side of the side polygon get the data from the matching
+ * corners of the extruded polygon. This order depends on the loop filling the loop
+ * indices. */
+ side_loop_data[0] = poly_loop_data[i_next];
+ side_loop_data[1] = poly_loop_data[i];
+ side_loop_data[2] = poly_loop_data[i];
+ side_loop_data[3] = poly_loop_data[i_next];
+ }
+ }
+ });
+ break;
+ }
+ default:
+ BLI_assert_unreachable();
+ }
+ });
+
+ attribute.save();
+ return true;
+ });
+
+ /* Offset the new vertices. */
+ threading::parallel_for(poly_selection.index_range(), 1024, [&](const IndexRange range) {
+ for (const int i_selection : range) {
+ const IndexRange poly_corner_range = selected_corner_range(index_offsets, i_selection);
+ for (MVert &vert : new_verts.slice(poly_corner_range)) {
+ add_v3_v3(vert.co, poly_offset[poly_selection[i_selection]]);
+ }
+ }
+ });
+
+ /* Finally update each extruded polygon's loops to point to the new edges and vertices.
+ * This must be done last, because they were used to find original indices for attribute
+ * interpolation before. Alternatively an original index array could be built for each domain. */
+ threading::parallel_for(poly_selection.index_range(), 256, [&](const IndexRange range) {
+ for (const int i_selection : range) {
+ const IndexRange poly_corner_range = selected_corner_range(index_offsets, i_selection);
+
+ const MPoly &poly = polys[poly_selection[i_selection]];
+ MutableSpan<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop);
+
+ for (const int i : IndexRange(poly.totloop)) {
+ MLoop &loop = poly_loops[i];
+ loop.v = new_vert_range[poly_corner_range[i]];
+ loop.e = duplicate_edge_range[poly_corner_range[i]];
+ }
+ }
+ });
+
+ if (attribute_outputs.top_id) {
+ save_selection_as_attribute(
+ component, attribute_outputs.top_id.get(), ATTR_DOMAIN_FACE, poly_selection);
+ }
+ if (attribute_outputs.side_id) {
+ save_selection_as_attribute(
+ component, attribute_outputs.side_id.get(), ATTR_DOMAIN_FACE, side_poly_range);
+ }
+
+ BKE_mesh_runtime_clear_cache(&mesh);
+ BKE_mesh_normals_tag_dirty(&mesh);
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh");
+ Field<bool> selection = params.extract_input<Field<bool>>("Selection");
+ Field<float3> offset_field = params.extract_input<Field<float3>>("Offset");
+ Field<float> scale_field = params.extract_input<Field<float>>("Offset Scale");
+ const NodeGeometryExtrudeMesh &storage = node_storage(params.node());
+ GeometryNodeExtrudeMeshMode mode = static_cast<GeometryNodeExtrudeMeshMode>(storage.mode);
+
+ /* Create a combined field from the offset and the scale so the field evaluator
+ * can take care of the multiplication and to simplify each extrude function. */
+ static fn::CustomMF_SI_SI_SO<float3, float, float3> multiply_fn{
+ "Scale", [](const float3 &offset, const float scale) { return offset * scale; }};
+ std::shared_ptr<FieldOperation> multiply_op = std::make_shared<FieldOperation>(
+ FieldOperation(multiply_fn, {std::move(offset_field), std::move(scale_field)}));
+ const Field<float3> final_offset{std::move(multiply_op)};
+
+ AttributeOutputs attribute_outputs;
+ if (params.output_is_required("Top")) {
+ attribute_outputs.top_id = StrongAnonymousAttributeID("Top");
+ }
+ if (params.output_is_required("Side")) {
+ attribute_outputs.side_id = StrongAnonymousAttributeID("Side");
+ }
+
+ const bool extrude_individual = mode == GEO_NODE_EXTRUDE_MESH_FACES &&
+ params.extract_input<bool>("Individual");
+
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ if (geometry_set.has_mesh()) {
+ MeshComponent &component = geometry_set.get_component_for_write<MeshComponent>();
+ switch (mode) {
+ case GEO_NODE_EXTRUDE_MESH_VERTICES:
+ extrude_mesh_vertices(component, selection, final_offset, attribute_outputs);
+ break;
+ case GEO_NODE_EXTRUDE_MESH_EDGES:
+ extrude_mesh_edges(component, selection, final_offset, attribute_outputs);
+ break;
+ case GEO_NODE_EXTRUDE_MESH_FACES: {
+ if (extrude_individual) {
+ extrude_individual_mesh_faces(component, selection, final_offset, attribute_outputs);
+ }
+ else {
+ extrude_mesh_face_regions(component, selection, final_offset, attribute_outputs);
+ }
+ break;
+ }
+ }
+
+ BLI_assert(BKE_mesh_is_valid(component.get_for_write()));
+ }
+ });
+
+ params.set_output("Mesh", std::move(geometry_set));
+ if (attribute_outputs.top_id) {
+ params.set_output("Top",
+ AnonymousAttributeFieldInput::Create<bool>(
+ std::move(attribute_outputs.top_id), params.attribute_producer_name()));
+ }
+ if (attribute_outputs.side_id) {
+ params.set_output("Side",
+ AnonymousAttributeFieldInput::Create<bool>(
+ std::move(attribute_outputs.side_id), params.attribute_producer_name()));
+ }
+}
+
+} // namespace blender::nodes::node_geo_extrude_mesh_cc
+
+void register_node_type_geo_extrude_mesh()
+{
+ namespace file_ns = blender::nodes::node_geo_extrude_mesh_cc;
+
+ static bNodeType ntype;
+ geo_node_type_base(&ntype, GEO_NODE_EXTRUDE_MESH, "Extrude Mesh", NODE_CLASS_GEOMETRY);
+ ntype.declare = file_ns::node_declare;
+ node_type_init(&ntype, file_ns::node_init);
+ node_type_update(&ntype, file_ns::node_update);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ node_type_storage(
+ &ntype, "NodeGeometryExtrudeMesh", node_free_standard_storage, node_copy_standard_storage);
+ ntype.draw_buttons = file_ns::node_layout;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc b/source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc
new file mode 100644
index 00000000000..9512323834c
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc
@@ -0,0 +1,193 @@
+/*
+ * 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 "UI_interface.h"
+#include "UI_resources.h"
+
+#include "BKE_attribute_math.hh"
+
+#include "BLI_task.hh"
+
+namespace blender::nodes::node_geo_field_at_index_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Int>(N_("Index")).min(0).supports_field();
+
+ b.add_input<decl::Float>(N_("Value"), "Value_Float").supports_field();
+ b.add_input<decl::Int>(N_("Value"), "Value_Int").supports_field();
+ b.add_input<decl::Vector>(N_("Value"), "Value_Vector").supports_field();
+ b.add_input<decl::Color>(N_("Value"), "Value_Color").supports_field();
+ b.add_input<decl::Bool>(N_("Value"), "Value_Bool").supports_field();
+
+ b.add_output<decl::Float>(N_("Value"), "Value_Float").field_source();
+ b.add_output<decl::Int>(N_("Value"), "Value_Int").field_source();
+ b.add_output<decl::Vector>(N_("Value"), "Value_Vector").field_source();
+ b.add_output<decl::Color>(N_("Value"), "Value_Color").field_source();
+ b.add_output<decl::Bool>(N_("Value"), "Value_Bool").field_source();
+}
+
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE);
+ uiItemR(layout, ptr, "domain", 0, "", ICON_NONE);
+}
+
+static void node_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ node->custom1 = ATTR_DOMAIN_POINT;
+ node->custom2 = CD_PROP_FLOAT;
+}
+
+static void node_update(bNodeTree *ntree, bNode *node)
+{
+ const CustomDataType data_type = static_cast<CustomDataType>(node->custom2);
+
+ bNodeSocket *sock_index = static_cast<bNodeSocket *>(node->inputs.first);
+ bNodeSocket *sock_in_float = sock_index->next;
+ bNodeSocket *sock_in_int = sock_in_float->next;
+ bNodeSocket *sock_in_vector = sock_in_int->next;
+ bNodeSocket *sock_in_color = sock_in_vector->next;
+ bNodeSocket *sock_in_bool = sock_in_color->next;
+
+ bNodeSocket *sock_out_float = static_cast<bNodeSocket *>(node->outputs.first);
+ bNodeSocket *sock_out_int = sock_out_float->next;
+ bNodeSocket *sock_out_vector = sock_out_int->next;
+ bNodeSocket *sock_out_color = sock_out_vector->next;
+ bNodeSocket *sock_out_bool = sock_out_color->next;
+
+ nodeSetSocketAvailability(ntree, sock_in_float, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(ntree, sock_in_int, data_type == CD_PROP_INT32);
+ nodeSetSocketAvailability(ntree, sock_in_vector, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(ntree, sock_in_color, data_type == CD_PROP_COLOR);
+ nodeSetSocketAvailability(ntree, sock_in_bool, data_type == CD_PROP_BOOL);
+
+ nodeSetSocketAvailability(ntree, sock_out_float, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(ntree, sock_out_int, data_type == CD_PROP_INT32);
+ nodeSetSocketAvailability(ntree, sock_out_vector, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(ntree, sock_out_color, data_type == CD_PROP_COLOR);
+ nodeSetSocketAvailability(ntree, sock_out_bool, data_type == CD_PROP_BOOL);
+}
+
+class FieldAtIndex final : public GeometryFieldInput {
+ private:
+ Field<int> index_field_;
+ GField value_field_;
+ AttributeDomain value_field_domain_;
+
+ public:
+ FieldAtIndex(Field<int> index_field, GField value_field, AttributeDomain value_field_domain)
+ : GeometryFieldInput(value_field.cpp_type(), "Field at Index"),
+ index_field_(std::move(index_field)),
+ value_field_(std::move(value_field)),
+ value_field_domain_(value_field_domain)
+ {
+ }
+
+ GVArray get_varray_for_context(const GeometryComponent &component,
+ const AttributeDomain domain,
+ IndexMask mask) const final
+ {
+ const GeometryComponentFieldContext value_field_context{component, value_field_domain_};
+ FieldEvaluator value_evaluator{value_field_context,
+ component.attribute_domain_size(value_field_domain_)};
+ value_evaluator.add(value_field_);
+ value_evaluator.evaluate();
+ const GVArray &values = value_evaluator.get_evaluated(0);
+
+ const GeometryComponentFieldContext index_field_context{component, domain};
+ FieldEvaluator index_evaluator{index_field_context, &mask};
+ index_evaluator.add(index_field_);
+ index_evaluator.evaluate();
+ const VArray<int> &indices = index_evaluator.get_evaluated<int>(0);
+
+ GVArray output_array;
+ attribute_math::convert_to_static_type(*type_, [&](auto dummy) {
+ using T = decltype(dummy);
+ Array<T> dst_array(mask.min_array_size());
+ VArray<T> src_values = values.typed<T>();
+ threading::parallel_for(mask.index_range(), 1024, [&](const IndexRange range) {
+ for (const int i : mask.slice(range)) {
+ const int index = indices[i];
+ if (index >= 0 && index < src_values.size()) {
+ dst_array[i] = src_values[index];
+ }
+ else {
+ dst_array[i] = {};
+ }
+ }
+ });
+ output_array = VArray<T>::ForContainer(std::move(dst_array));
+ });
+
+ return output_array;
+ }
+};
+
+static StringRefNull identifier_suffix(CustomDataType data_type)
+{
+ switch (data_type) {
+ case CD_PROP_BOOL:
+ return "Bool";
+ case CD_PROP_FLOAT:
+ return "Float";
+ case CD_PROP_INT32:
+ return "Int";
+ case CD_PROP_COLOR:
+ return "Color";
+ case CD_PROP_FLOAT3:
+ return "Vector";
+ default:
+ BLI_assert_unreachable();
+ return "";
+ }
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ const bNode &node = params.node();
+ const AttributeDomain domain = static_cast<AttributeDomain>(node.custom1);
+ const CustomDataType data_type = static_cast<CustomDataType>(node.custom2);
+
+ Field<int> index_field = params.extract_input<Field<int>>("Index");
+ attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ using T = decltype(dummy);
+ static const std::string identifier = "Value_" + identifier_suffix(data_type);
+ Field<T> value_field = params.extract_input<Field<T>>(identifier);
+ Field<T> output_field{
+ std::make_shared<FieldAtIndex>(std::move(index_field), std::move(value_field), domain)};
+ params.set_output(identifier, std::move(output_field));
+ });
+}
+
+} // namespace blender::nodes::node_geo_field_at_index_cc
+
+void register_node_type_geo_field_at_index()
+{
+ namespace file_ns = blender::nodes::node_geo_field_at_index_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_FIELD_AT_INDEX, "Field at Index", NODE_CLASS_CONVERTER);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.declare = file_ns::node_declare;
+ ntype.draw_buttons = file_ns::node_layout;
+ ntype.initfunc = file_ns::node_init;
+ ntype.updatefunc = file_ns::node_update;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc
new file mode 100644
index 00000000000..41970d75dfe
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc
@@ -0,0 +1,114 @@
+/*
+ * 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 "BKE_mesh.h"
+#include "BKE_mesh_runtime.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_attribute_math.hh"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_flip_faces_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH);
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field();
+ b.add_output<decl::Geometry>(N_("Mesh"));
+}
+
+static void mesh_flip_faces(MeshComponent &component, const Field<bool> &selection_field)
+{
+ GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_FACE};
+ const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE);
+ if (domain_size == 0) {
+ return;
+ }
+ fn::FieldEvaluator evaluator{field_context, domain_size};
+ evaluator.add(selection_field);
+ evaluator.evaluate();
+ const IndexMask selection = evaluator.get_evaluated_as_mask(0);
+
+ Mesh *mesh = component.get_for_write();
+
+ mesh->mloop = (MLoop *)CustomData_duplicate_referenced_layer(
+ &mesh->ldata, CD_MLOOP, mesh->totloop);
+ Span<MPoly> polys{mesh->mpoly, mesh->totpoly};
+ MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop};
+
+ for (const int i : selection.index_range()) {
+ const MPoly &poly = polys[selection[i]];
+ int start = poly.loopstart;
+ for (const int j : IndexRange(poly.totloop / 2)) {
+ const int index1 = start + j + 1;
+ const int index2 = start + poly.totloop - j - 1;
+ std::swap(loops[index1].v, loops[index2].v);
+ std::swap(loops[index1 - 1].e, loops[index2].e);
+ }
+ }
+
+ component.attribute_foreach(
+ [&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
+ if (meta_data.domain == ATTR_DOMAIN_CORNER) {
+ OutputAttribute attribute = component.attribute_try_get_for_output(
+ attribute_id, ATTR_DOMAIN_CORNER, meta_data.data_type, nullptr);
+ attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) {
+ using T = decltype(dummy);
+ MutableSpan<T> dst_span = attribute.as_span<T>();
+ for (const int j : selection.index_range()) {
+ const MPoly &poly = polys[selection[j]];
+ dst_span.slice(poly.loopstart + 1, poly.totloop - 1).reverse();
+ }
+ });
+ attribute.save();
+ }
+ return true;
+ });
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh");
+
+ const Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
+
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ if (!geometry_set.has_mesh()) {
+ return;
+ }
+ MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
+ mesh_flip_faces(mesh_component, selection_field);
+ });
+
+ params.set_output("Mesh", std::move(geometry_set));
+}
+
+} // namespace blender::nodes::node_geo_flip_faces_cc
+
+void register_node_type_geo_flip_faces()
+{
+ namespace file_ns = blender::nodes::node_geo_flip_faces_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_FLIP_FACES, "Flip Faces", NODE_CLASS_GEOMETRY);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.declare = file_ns::node_declare;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_geometry_to_instance.cc b/source/blender/nodes/geometry/nodes/node_geo_geometry_to_instance.cc
new file mode 100644
index 00000000000..f65af5b6737
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_geometry_to_instance.cc
@@ -0,0 +1,55 @@
+/*
+ * 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"
+
+namespace blender::nodes::node_geo_geometry_to_instance_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>(N_("Geometry")).multi_input();
+ b.add_output<decl::Geometry>(N_("Instances"));
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ Vector<GeometrySet> geometries = params.extract_multi_input<GeometrySet>("Geometry");
+ GeometrySet instances_geometry;
+ InstancesComponent &instances_component =
+ instances_geometry.get_component_for_write<InstancesComponent>();
+ for (GeometrySet &geometry : geometries) {
+ geometry.ensure_owns_direct_data();
+ const int handle = instances_component.add_reference(std::move(geometry));
+ instances_component.add_instance(handle, float4x4::identity());
+ }
+ params.set_output("Instances", std::move(instances_geometry));
+}
+
+} // namespace blender::nodes::node_geo_geometry_to_instance_cc
+
+void register_node_type_geo_geometry_to_instance()
+{
+ namespace file_ns = blender::nodes::node_geo_geometry_to_instance_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_GEOMETRY_TO_INSTANCE, "Geometry to Instance", NODE_CLASS_GEOMETRY);
+ node_type_size(&ntype, 160, 100, 300);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.declare = file_ns::node_declare;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_image_texture.cc b/source/blender/nodes/geometry/nodes/node_geo_image_texture.cc
new file mode 100644
index 00000000000..624a8b6b0f6
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_image_texture.cc
@@ -0,0 +1,428 @@
+/*
+ * 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.
+ *
+ * The Original Code is Copyright (C) 2005 Blender Foundation.
+ * All rights reserved.
+ */
+
+#include "node_geometry_util.hh"
+
+#include "BKE_image.h"
+
+#include "BLI_math_vec_types.hh"
+#include "BLI_threads.h"
+#include "BLI_timeit.hh"
+
+#include "IMB_colormanagement.h"
+#include "IMB_imbuf.h"
+#include "IMB_imbuf_types.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+namespace blender::nodes::node_geo_image_texture_cc {
+
+NODE_STORAGE_FUNCS(NodeGeometryImageTexture)
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Image>(N_("Image")).hide_label();
+ b.add_input<decl::Vector>(N_("Vector"))
+ .implicit_field()
+ .description(("Texture coordinates from 0 to 1"));
+ b.add_input<decl::Int>(N_("Frame")).min(0).max(MAXFRAMEF);
+ b.add_output<decl::Color>(N_("Color")).no_muted_links().dependent_field();
+ b.add_output<decl::Float>(N_("Alpha")).no_muted_links().dependent_field();
+}
+
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "interpolation", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
+ uiItemR(layout, ptr, "extension", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
+}
+
+static void node_init(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeGeometryImageTexture *tex = MEM_cnew<NodeGeometryImageTexture>(__func__);
+ node->storage = tex;
+}
+
+class ImageFieldsFunction : public fn::MultiFunction {
+ private:
+ const int interpolation_;
+ const int extension_;
+ Image &image_;
+ ImageUser image_user_;
+ void *image_lock_;
+ ImBuf *image_buffer_;
+
+ public:
+ ImageFieldsFunction(const int interpolation,
+ const int extension,
+ Image &image,
+ ImageUser image_user)
+ : interpolation_(interpolation),
+ extension_(extension),
+ image_(image),
+ image_user_(image_user)
+ {
+ static fn::MFSignature signature = create_signature();
+ this->set_signature(&signature);
+
+ image_buffer_ = BKE_image_acquire_ibuf(&image_, &image_user_, &image_lock_);
+ if (image_buffer_ == nullptr) {
+ throw std::runtime_error("cannot acquire image buffer");
+ }
+
+ if (image_buffer_->rect_float == nullptr) {
+ BLI_thread_lock(LOCK_IMAGE);
+ if (!image_buffer_->rect_float) {
+ IMB_float_from_rect(image_buffer_);
+ }
+ BLI_thread_unlock(LOCK_IMAGE);
+ }
+
+ if (image_buffer_->rect_float == nullptr) {
+ BKE_image_release_ibuf(&image_, image_buffer_, image_lock_);
+ throw std::runtime_error("cannot get float buffer");
+ }
+ }
+
+ ~ImageFieldsFunction() override
+ {
+ BKE_image_release_ibuf(&image_, image_buffer_, image_lock_);
+ }
+
+ static fn::MFSignature create_signature()
+ {
+ fn::MFSignatureBuilder signature{"ImageFunction"};
+ signature.single_input<float3>("Vector");
+ signature.single_output<ColorGeometry4f>("Color");
+ signature.single_output<float>("Alpha");
+ return signature.build();
+ }
+
+ static int wrap_periodic(int x, const int width)
+ {
+ x %= width;
+ if (x < 0) {
+ x += width;
+ }
+ return x;
+ }
+
+ static int wrap_clamp(const int x, const int width)
+ {
+ return std::clamp(x, 0, width - 1);
+ }
+
+ static float4 image_pixel_lookup(const ImBuf *ibuf, const int px, const int py)
+ {
+ if (px < 0 || py < 0 || px >= ibuf->x || py >= ibuf->y) {
+ return float4(0.0f, 0.0f, 0.0f, 0.0f);
+ }
+ return ((const float4 *)ibuf->rect_float)[px + py * ibuf->x];
+ }
+
+ static float frac(const float x, int *ix)
+ {
+ const int i = (int)x - ((x < 0.0f) ? 1 : 0);
+ *ix = i;
+ return x - (float)i;
+ }
+
+ static float4 image_cubic_texture_lookup(const ImBuf *ibuf,
+ const float px,
+ const float py,
+ const int extension)
+ {
+ const int width = ibuf->x;
+ const int height = ibuf->y;
+ int pix, piy, nix, niy;
+ const float tx = frac(px * (float)width - 0.5f, &pix);
+ const float ty = frac(py * (float)height - 0.5f, &piy);
+ int ppix, ppiy, nnix, nniy;
+
+ switch (extension) {
+ case SHD_IMAGE_EXTENSION_REPEAT: {
+ pix = wrap_periodic(pix, width);
+ piy = wrap_periodic(piy, height);
+ ppix = wrap_periodic(pix - 1, width);
+ ppiy = wrap_periodic(piy - 1, height);
+ nix = wrap_periodic(pix + 1, width);
+ niy = wrap_periodic(piy + 1, height);
+ nnix = wrap_periodic(pix + 2, width);
+ nniy = wrap_periodic(piy + 2, height);
+ break;
+ }
+ case SHD_IMAGE_EXTENSION_CLIP: {
+ ppix = pix - 1;
+ ppiy = piy - 1;
+ nix = pix + 1;
+ niy = piy + 1;
+ nnix = pix + 2;
+ nniy = piy + 2;
+ break;
+ }
+ case SHD_IMAGE_EXTENSION_EXTEND: {
+ ppix = wrap_clamp(pix - 1, width);
+ ppiy = wrap_clamp(piy - 1, height);
+ nix = wrap_clamp(pix + 1, width);
+ niy = wrap_clamp(piy + 1, height);
+ nnix = wrap_clamp(pix + 2, width);
+ nniy = wrap_clamp(piy + 2, height);
+ pix = wrap_clamp(pix, width);
+ piy = wrap_clamp(piy, height);
+ break;
+ }
+ default:
+ return float4(0.0f, 0.0f, 0.0f, 0.0f);
+ }
+
+ const int xc[4] = {ppix, pix, nix, nnix};
+ const int yc[4] = {ppiy, piy, niy, nniy};
+ float u[4], v[4];
+
+ u[0] = (((-1.0f / 6.0f) * tx + 0.5f) * tx - 0.5f) * tx + (1.0f / 6.0f);
+ u[1] = ((0.5f * tx - 1.0f) * tx) * tx + (2.0f / 3.0f);
+ u[2] = ((-0.5f * tx + 0.5f) * tx + 0.5f) * tx + (1.0f / 6.0f);
+ u[3] = (1.0f / 6.0f) * tx * tx * tx;
+
+ v[0] = (((-1.0f / 6.0f) * ty + 0.5f) * ty - 0.5f) * ty + (1.0f / 6.0f);
+ v[1] = ((0.5f * ty - 1.0f) * ty) * ty + (2.0f / 3.0f);
+ v[2] = ((-0.5f * ty + 0.5f) * ty + 0.5f) * ty + (1.0f / 6.0f);
+ v[3] = (1.0f / 6.0f) * ty * ty * ty;
+
+ return (v[0] * (u[0] * (image_pixel_lookup(ibuf, xc[0], yc[0])) +
+ u[1] * (image_pixel_lookup(ibuf, xc[1], yc[0])) +
+ u[2] * (image_pixel_lookup(ibuf, xc[2], yc[0])) +
+ u[3] * (image_pixel_lookup(ibuf, xc[3], yc[0])))) +
+ (v[1] * (u[0] * (image_pixel_lookup(ibuf, xc[0], yc[1])) +
+ u[1] * (image_pixel_lookup(ibuf, xc[1], yc[1])) +
+ u[2] * (image_pixel_lookup(ibuf, xc[2], yc[1])) +
+ u[3] * (image_pixel_lookup(ibuf, xc[3], yc[1])))) +
+ (v[2] * (u[0] * (image_pixel_lookup(ibuf, xc[0], yc[2])) +
+ u[1] * (image_pixel_lookup(ibuf, xc[1], yc[2])) +
+ u[2] * (image_pixel_lookup(ibuf, xc[2], yc[2])) +
+ u[3] * (image_pixel_lookup(ibuf, xc[3], yc[2])))) +
+ (v[3] * (u[0] * (image_pixel_lookup(ibuf, xc[0], yc[3])) +
+ u[1] * (image_pixel_lookup(ibuf, xc[1], yc[3])) +
+ u[2] * (image_pixel_lookup(ibuf, xc[2], yc[3])) +
+ u[3] * (image_pixel_lookup(ibuf, xc[3], yc[3]))));
+ }
+
+ static float4 image_linear_texture_lookup(const ImBuf *ibuf,
+ const float px,
+ const float py,
+ const int extension)
+ {
+ const int width = ibuf->x;
+ const int height = ibuf->y;
+ int pix, piy, nix, niy;
+ const float nfx = frac(px * (float)width - 0.5f, &pix);
+ const float nfy = frac(py * (float)height - 0.5f, &piy);
+
+ switch (extension) {
+ case SHD_IMAGE_EXTENSION_CLIP: {
+ nix = pix + 1;
+ niy = piy + 1;
+ break;
+ }
+ case SHD_IMAGE_EXTENSION_EXTEND: {
+ nix = wrap_clamp(pix + 1, width);
+ niy = wrap_clamp(piy + 1, height);
+ pix = wrap_clamp(pix, width);
+ piy = wrap_clamp(piy, height);
+ break;
+ }
+ default:
+ case SHD_IMAGE_EXTENSION_REPEAT:
+ pix = wrap_periodic(pix, width);
+ piy = wrap_periodic(piy, height);
+ nix = wrap_periodic(pix + 1, width);
+ niy = wrap_periodic(piy + 1, height);
+ break;
+ }
+
+ const float ptx = 1.0f - nfx;
+ const float pty = 1.0f - nfy;
+
+ return image_pixel_lookup(ibuf, pix, piy) * ptx * pty +
+ image_pixel_lookup(ibuf, nix, piy) * nfx * pty +
+ image_pixel_lookup(ibuf, pix, niy) * ptx * nfy +
+ image_pixel_lookup(ibuf, nix, niy) * nfx * nfy;
+ }
+
+ static float4 image_closest_texture_lookup(const ImBuf *ibuf,
+ const float px,
+ const float py,
+ const int extension)
+ {
+ const int width = ibuf->x;
+ const int height = ibuf->y;
+ int ix, iy;
+ const float tx = frac(px * (float)width - 0.5f, &ix);
+ const float ty = frac(py * (float)height - 0.5f, &iy);
+
+ switch (extension) {
+ case SHD_IMAGE_EXTENSION_REPEAT: {
+ ix = wrap_periodic(ix, width);
+ iy = wrap_periodic(iy, height);
+ return image_pixel_lookup(ibuf, ix, iy);
+ }
+ case SHD_IMAGE_EXTENSION_CLIP: {
+ if (tx < 0.0f || ty < 0.0f || tx > 1.0f || ty > 1.0f) {
+ return float4(0.0f, 0.0f, 0.0f, 0.0f);
+ }
+ if (ix < 0 || iy < 0 || ix > width || iy > height) {
+ return float4(0.0f, 0.0f, 0.0f, 0.0f);
+ }
+ ATTR_FALLTHROUGH;
+ }
+ case SHD_IMAGE_EXTENSION_EXTEND: {
+ ix = wrap_clamp(ix, width);
+ iy = wrap_clamp(iy, height);
+ return image_pixel_lookup(ibuf, ix, iy);
+ }
+ default:
+ return float4(0.0f, 0.0f, 0.0f, 0.0f);
+ }
+ }
+
+ void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override
+ {
+ const VArray<float3> &vectors = params.readonly_single_input<float3>(0, "Vector");
+ MutableSpan<ColorGeometry4f> r_color = params.uninitialized_single_output<ColorGeometry4f>(
+ 1, "Color");
+ MutableSpan<float> r_alpha = params.uninitialized_single_output_if_required<float>(2, "Alpha");
+
+ MutableSpan<float4> color_data{(float4 *)r_color.data(), r_color.size()};
+
+ /* Sample image texture. */
+ switch (interpolation_) {
+ case SHD_INTERP_LINEAR:
+ for (const int64_t i : mask) {
+ const float3 p = vectors[i];
+ color_data[i] = image_linear_texture_lookup(image_buffer_, p.x, p.y, extension_);
+ }
+ break;
+ case SHD_INTERP_CLOSEST:
+ for (const int64_t i : mask) {
+ const float3 p = vectors[i];
+ color_data[i] = image_closest_texture_lookup(image_buffer_, p.x, p.y, extension_);
+ }
+ break;
+ case SHD_INTERP_CUBIC:
+ case SHD_INTERP_SMART:
+ for (const int64_t i : mask) {
+ const float3 p = vectors[i];
+ color_data[i] = image_cubic_texture_lookup(image_buffer_, p.x, p.y, extension_);
+ }
+ break;
+ }
+
+ int alpha_mode = image_.alpha_mode;
+ if (IMB_colormanagement_space_name_is_data(image_.colorspace_settings.name)) {
+ alpha_mode = IMA_ALPHA_CHANNEL_PACKED;
+ }
+
+ switch (alpha_mode) {
+ case IMA_ALPHA_STRAIGHT: {
+ /* #ColorGeometry expects premultiplied alpha, so convert from straight to that. */
+ for (int64_t i : mask) {
+ straight_to_premul_v4(color_data[i]);
+ }
+ break;
+ }
+ case IMA_ALPHA_PREMUL: {
+ /* Alpha is premultiplied already, nothing to do. */
+ break;
+ }
+ case IMA_ALPHA_CHANNEL_PACKED: {
+ /* Color and alpha channels shouldn't interact with each other, nothing to do. */
+ break;
+ }
+ case IMA_ALPHA_IGNORE: {
+ /* The image should be treated as being opaque. */
+ for (int64_t i : mask) {
+ color_data[i].w = 1.0f;
+ }
+ break;
+ }
+ }
+
+ if (!r_alpha.is_empty()) {
+ for (int64_t i : mask) {
+ r_alpha[i] = r_color[i].a;
+ }
+ }
+ }
+};
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ Image *image = params.get_input<Image *>("Image");
+ if (image == nullptr) {
+ params.set_default_remaining_outputs();
+ return;
+ }
+
+ const NodeGeometryImageTexture &storage = node_storage(params.node());
+
+ ImageUser image_user;
+ BKE_imageuser_default(&image_user);
+ image_user.cycl = false;
+ image_user.frames = INT_MAX;
+ image_user.sfra = 1;
+ image_user.framenr = BKE_image_is_animated(image) ? params.get_input<int>("Frame") : 0;
+
+ std::unique_ptr<ImageFieldsFunction> image_fn;
+ try {
+ image_fn = std::make_unique<ImageFieldsFunction>(
+ storage.interpolation, storage.extension, *image, image_user);
+ }
+ catch (const std::runtime_error &) {
+ params.set_default_remaining_outputs();
+ return;
+ }
+
+ Field<float3> vector_field = params.extract_input<Field<float3>>("Vector");
+
+ auto image_op = std::make_shared<FieldOperation>(
+ FieldOperation(std::move(image_fn), {std::move(vector_field)}));
+
+ params.set_output("Color", Field<ColorGeometry4f>(image_op, 0));
+ params.set_output("Alpha", Field<float>(image_op, 1));
+}
+
+} // namespace blender::nodes::node_geo_image_texture_cc
+
+void register_node_type_geo_image_texture()
+{
+ namespace file_ns = blender::nodes::node_geo_image_texture_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_IMAGE_TEXTURE, "Image Texture", NODE_CLASS_TEXTURE);
+ ntype.declare = file_ns::node_declare;
+ ntype.draw_buttons = file_ns::node_layout;
+ node_type_init(&ntype, file_ns::node_init);
+ node_type_storage(
+ &ntype, "NodeGeometryImageTexture", node_free_standard_storage, node_copy_standard_storage);
+ node_type_size_preset(&ntype, NODE_SIZE_LARGE);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc
new file mode 100644
index 00000000000..b1144b58c37
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc
@@ -0,0 +1,128 @@
+/*
+ * 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 "BKE_spline.hh"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_input_curve_handles_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Bool>(N_("Relative"))
+ .default_value(false)
+ .supports_field()
+ .description(N_("Output the handle positions relative to the corresponding control point "
+ "instead of in the local space of the geometry"));
+ b.add_output<decl::Vector>(N_("Left")).field_source();
+ b.add_output<decl::Vector>(N_("Right")).field_source();
+}
+
+class HandlePositionFieldInput final : public GeometryFieldInput {
+ Field<bool> relative_;
+ bool left_;
+
+ public:
+ HandlePositionFieldInput(Field<bool> relative, bool left)
+ : GeometryFieldInput(CPPType::get<float3>(), "Handle"), relative_(relative), left_(left)
+ {
+ }
+
+ GVArray get_varray_for_context(const GeometryComponent &component,
+ const AttributeDomain domain,
+ IndexMask mask) const final
+ {
+ if (component.type() != GEO_COMPONENT_TYPE_CURVE) {
+ return {};
+ }
+
+ GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
+ fn::FieldEvaluator evaluator(field_context, &mask);
+ evaluator.add(relative_);
+ evaluator.evaluate();
+ const VArray<bool> &relative = evaluator.get_evaluated<bool>(0);
+
+ VArray<float3> positions = component.attribute_get_for_read<float3>(
+ "position", ATTR_DOMAIN_POINT, {0, 0, 0});
+
+ StringRef side = left_ ? "handle_left" : "handle_right";
+ VArray<float3> handles = component.attribute_get_for_read<float3>(
+ side, ATTR_DOMAIN_POINT, {0, 0, 0});
+
+ if (relative.is_single()) {
+ if (relative.get_internal_single()) {
+ Array<float3> output(positions.size());
+ for (const int i : positions.index_range()) {
+ output[i] = handles[i] - positions[i];
+ }
+ return component.attribute_try_adapt_domain<float3>(
+ VArray<float3>::ForContainer(std::move(output)), ATTR_DOMAIN_POINT, domain);
+ }
+ return component.attribute_try_adapt_domain<float3>(handles, ATTR_DOMAIN_POINT, domain);
+ }
+
+ Array<float3> output(positions.size());
+ for (const int i : positions.index_range()) {
+ if (relative[i]) {
+ output[i] = handles[i] - positions[i];
+ }
+ else {
+ output[i] = handles[i];
+ }
+ }
+ return component.attribute_try_adapt_domain<float3>(
+ VArray<float3>::ForContainer(std::move(output)), ATTR_DOMAIN_POINT, domain);
+ }
+
+ uint64_t hash() const override
+ {
+ return get_default_hash_2(relative_, left_);
+ }
+
+ bool is_equal_to(const fn::FieldNode &other) const override
+ {
+ if (const HandlePositionFieldInput *other_handle =
+ dynamic_cast<const HandlePositionFieldInput *>(&other)) {
+ return relative_ == other_handle->relative_ && left_ == other_handle->left_;
+ }
+ return false;
+ }
+};
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ Field<bool> relative = params.extract_input<Field<bool>>("Relative");
+ Field<float3> left_field{std::make_shared<HandlePositionFieldInput>(relative, true)};
+ Field<float3> right_field{std::make_shared<HandlePositionFieldInput>(relative, false)};
+
+ params.set_output("Left", std::move(left_field));
+ params.set_output("Right", std::move(right_field));
+}
+
+} // namespace blender::nodes::node_geo_input_curve_handles_cc
+
+void register_node_type_geo_input_curve_handles()
+{
+ namespace file_ns = blender::nodes::node_geo_input_curve_handles_cc;
+
+ static bNodeType ntype;
+ geo_node_type_base(
+ &ntype, GEO_NODE_INPUT_CURVE_HANDLES, "Curve Handle Positions", NODE_CLASS_INPUT);
+ node_type_size_preset(&ntype, NODE_SIZE_MIDDLE);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.declare = file_ns::node_declare;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_curve_tilt.cc b/source/blender/nodes/geometry/nodes/node_geo_input_curve_tilt.cc
new file mode 100644
index 00000000000..61b4b6bb9e9
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_curve_tilt.cc
@@ -0,0 +1,44 @@
+/*
+ * 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"
+
+namespace blender::nodes::node_geo_input_curve_tilt_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_output<decl::Float>(N_("Tilt")).field_source();
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ Field<float> tilt_field = AttributeFieldInput::Create<float>("tilt");
+ params.set_output("Tilt", std::move(tilt_field));
+}
+
+} // namespace blender::nodes::node_geo_input_curve_tilt_cc
+
+void register_node_type_geo_input_curve_tilt()
+{
+ namespace file_ns = blender::nodes::node_geo_input_curve_tilt_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_INPUT_CURVE_TILT, "Curve Tilt", NODE_CLASS_INPUT);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.declare = file_ns::node_declare;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_id.cc b/source/blender/nodes/geometry/nodes/node_geo_input_id.cc
new file mode 100644
index 00000000000..3fe0588a46d
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_id.cc
@@ -0,0 +1,44 @@
+/*
+ * 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"
+
+namespace blender::nodes::node_geo_input_id_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_output<decl::Int>(N_("ID")).field_source();
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ Field<int> position_field{std::make_shared<bke::IDAttributeFieldInput>()};
+ params.set_output("ID", std::move(position_field));
+}
+
+} // namespace blender::nodes::node_geo_input_id_cc
+
+void register_node_type_geo_input_id()
+{
+ namespace file_ns = blender::nodes::node_geo_input_id_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_INPUT_ID, "ID", NODE_CLASS_INPUT);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.declare = file_ns::node_declare;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_index.cc b/source/blender/nodes/geometry/nodes/node_geo_input_index.cc
index 7fcbaf429dd..98c2c9d58f0 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_index.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_index.cc
@@ -16,27 +16,29 @@
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_input_index_cc {
-static void geo_node_input_index_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_output<decl::Int>("Index").field_source();
+ b.add_output<decl::Int>(N_("Index")).field_source();
}
-static void geo_node_input_index_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
Field<int> index_field{std::make_shared<fn::IndexFieldInput>()};
params.set_output("Index", std::move(index_field));
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_input_index_cc
void register_node_type_geo_input_index()
{
+ namespace file_ns = blender::nodes::node_geo_input_index_cc;
+
static bNodeType ntype;
- geo_node_type_base(&ntype, GEO_NODE_INPUT_INDEX, "Index", NODE_CLASS_INPUT, 0);
- ntype.geometry_node_execute = blender::nodes::geo_node_input_index_exec;
- ntype.declare = blender::nodes::geo_node_input_index_declare;
+ geo_node_type_base(&ntype, GEO_NODE_INPUT_INDEX, "Index", NODE_CLASS_INPUT);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.declare = file_ns::node_declare;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_material.cc b/source/blender/nodes/geometry/nodes/node_geo_input_material.cc
index 8e805bd1359..a1c905fccaa 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_material.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_material.cc
@@ -19,33 +19,35 @@
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_input_material_cc {
-static void geo_node_input_material_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_output<decl::Material>("Material");
+ b.add_output<decl::Material>(N_("Material"));
}
-static void geo_node_input_material_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiItemR(layout, ptr, "material", 0, "", ICON_NONE);
}
-static void geo_node_input_material_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
Material *material = (Material *)params.node().id;
params.set_output("Material", material);
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_input_material_cc
void register_node_type_geo_input_material()
{
+ namespace file_ns = blender::nodes::node_geo_input_material_cc;
+
static bNodeType ntype;
- geo_node_type_base(&ntype, GEO_NODE_INPUT_MATERIAL, "Material", NODE_CLASS_INPUT, 0);
- ntype.draw_buttons = blender::nodes::geo_node_input_material_layout;
- ntype.declare = blender::nodes::geo_node_input_material_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_input_material_exec;
+ geo_node_type_base(&ntype, GEO_NODE_INPUT_MATERIAL, "Material", NODE_CLASS_INPUT);
+ ntype.draw_buttons = file_ns::node_layout;
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_material_index.cc b/source/blender/nodes/geometry/nodes/node_geo_input_material_index.cc
new file mode 100644
index 00000000000..fca29feb73c
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_material_index.cc
@@ -0,0 +1,44 @@
+/*
+ * 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"
+
+namespace blender::nodes::node_geo_input_material_index_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_output<decl::Int>(N_("Material Index")).field_source();
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ Field<int> material_index_field = AttributeFieldInput::Create<int>("material_index");
+ params.set_output("Material Index", std::move(material_index_field));
+}
+
+} // namespace blender::nodes::node_geo_input_material_index_cc
+
+void register_node_type_geo_input_material_index()
+{
+ namespace file_ns = blender::nodes::node_geo_input_material_index_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_INPUT_MATERIAL_INDEX, "Material Index", NODE_CLASS_INPUT);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.declare = file_ns::node_declare;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_angle.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_angle.cc
new file mode 100644
index 00000000000..4b6ed7b77b7
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_angle.cc
@@ -0,0 +1,222 @@
+/*
+ * 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 "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_mesh.h"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_input_mesh_edge_angle_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_output<decl::Float>(N_("Unsigned Angle"))
+ .field_source()
+ .description(
+ "The shortest angle in radians between two faces where they meet at an edge. Flat edges "
+ "and Non-manifold edges have an angle of zero. Computing this value is faster than the "
+ "signed angle");
+ b.add_output<decl::Float>(N_("Signed Angle"))
+ .field_source()
+ .description(
+ "The signed angle in radians between two faces where they meet at an edge. Flat edges "
+ "and Non-manifold edges have an angle of zero. Concave angles are positive and convex "
+ "angles are negative. Computing this value is slower than the unsigned angle");
+}
+
+struct EdgeMapEntry {
+ int face_count;
+ int face_index_1;
+ int face_index_2;
+};
+
+static Array<EdgeMapEntry> create_edge_map(const Span<MPoly> polys,
+ const Span<MLoop> loops,
+ const int total_edges)
+{
+ Array<EdgeMapEntry> edge_map(total_edges, {0, 0, 0});
+
+ for (const int i_poly : polys.index_range()) {
+ const MPoly &mpoly = polys[i_poly];
+ for (const MLoop &loop : loops.slice(mpoly.loopstart, mpoly.totloop)) {
+ EdgeMapEntry &entry = edge_map[loop.e];
+ if (entry.face_count == 0) {
+ entry.face_index_1 = i_poly;
+ }
+ else if (entry.face_count == 1) {
+ entry.face_index_2 = i_poly;
+ }
+ entry.face_count++;
+ }
+ }
+ return edge_map;
+}
+
+class AngleFieldInput final : public GeometryFieldInput {
+ public:
+ AngleFieldInput() : GeometryFieldInput(CPPType::get<float>(), "Unsigned Angle Field")
+ {
+ category_ = Category::Generated;
+ }
+
+ GVArray get_varray_for_context(const GeometryComponent &component,
+ const AttributeDomain domain,
+ IndexMask UNUSED(mask)) const final
+ {
+ if (component.type() != GEO_COMPONENT_TYPE_MESH) {
+ return {};
+ }
+
+ const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
+ const Mesh *mesh = mesh_component.get_for_read();
+ if (mesh == nullptr) {
+ return {};
+ }
+
+ Span<MPoly> polys{mesh->mpoly, mesh->totpoly};
+ Span<MLoop> loops{mesh->mloop, mesh->totloop};
+ Array<EdgeMapEntry> edge_map = create_edge_map(polys, loops, mesh->totedge);
+
+ auto angle_fn = [edge_map, polys, loops, mesh](const int i) -> float {
+ if (edge_map[i].face_count != 2) {
+ return 0.0f;
+ }
+ const MPoly &mpoly_1 = polys[edge_map[i].face_index_1];
+ const MPoly &mpoly_2 = polys[edge_map[i].face_index_2];
+ float3 normal_1, normal_2;
+ BKE_mesh_calc_poly_normal(&mpoly_1, &loops[mpoly_1.loopstart], mesh->mvert, normal_1);
+ BKE_mesh_calc_poly_normal(&mpoly_2, &loops[mpoly_2.loopstart], mesh->mvert, normal_2);
+ return angle_normalized_v3v3(normal_1, normal_2);
+ };
+
+ VArray<float> angles = VArray<float>::ForFunc(mesh->totedge, angle_fn);
+ return component.attribute_try_adapt_domain<float>(
+ std::move(angles), ATTR_DOMAIN_EDGE, domain);
+ }
+
+ uint64_t hash() const override
+ {
+ /* Some random constant hash. */
+ return 32426725235;
+ }
+
+ bool is_equal_to(const fn::FieldNode &other) const override
+ {
+ return dynamic_cast<const AngleFieldInput *>(&other) != nullptr;
+ }
+};
+
+class SignedAngleFieldInput final : public GeometryFieldInput {
+ public:
+ SignedAngleFieldInput() : GeometryFieldInput(CPPType::get<float>(), "Signed Angle Field")
+ {
+ category_ = Category::Generated;
+ }
+
+ GVArray get_varray_for_context(const GeometryComponent &component,
+ const AttributeDomain domain,
+ IndexMask UNUSED(mask)) const final
+ {
+ if (component.type() != GEO_COMPONENT_TYPE_MESH) {
+ return {};
+ }
+
+ const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
+ const Mesh *mesh = mesh_component.get_for_read();
+ if (mesh == nullptr) {
+ return {};
+ }
+
+ Span<MPoly> polys{mesh->mpoly, mesh->totpoly};
+ Span<MLoop> loops{mesh->mloop, mesh->totloop};
+ Array<EdgeMapEntry> edge_map = create_edge_map(polys, loops, mesh->totedge);
+
+ auto angle_fn = [edge_map, polys, loops, mesh](const int i) -> float {
+ if (edge_map[i].face_count != 2) {
+ return 0.0f;
+ }
+ const MPoly &mpoly_1 = polys[edge_map[i].face_index_1];
+ const MPoly &mpoly_2 = polys[edge_map[i].face_index_2];
+
+ /* Find the normals of the 2 polys. */
+ float3 poly_1_normal, poly_2_normal;
+ BKE_mesh_calc_poly_normal(&mpoly_1, &loops[mpoly_1.loopstart], mesh->mvert, poly_1_normal);
+ BKE_mesh_calc_poly_normal(&mpoly_2, &loops[mpoly_2.loopstart], mesh->mvert, poly_2_normal);
+
+ /* Find the centerpoint of the axis edge */
+ const float3 edge_centerpoint = (float3(mesh->mvert[mesh->medge[i].v1].co) +
+ float3(mesh->mvert[mesh->medge[i].v2].co)) *
+ 0.5f;
+
+ /* Get the centerpoint of poly 2 and subtract the edge centerpoint to get a tangent
+ * normal for poly 2. */
+ float3 poly_center_2;
+ BKE_mesh_calc_poly_center(&mpoly_2, &loops[mpoly_2.loopstart], mesh->mvert, poly_center_2);
+ const float3 poly_2_tangent = math::normalize(poly_center_2 - edge_centerpoint);
+ const float concavity = math::dot(poly_1_normal, poly_2_tangent);
+
+ /* Get the unsigned angle between the two polys */
+ const float angle = angle_normalized_v3v3(poly_1_normal, poly_2_normal);
+
+ if (angle == 0.0f || angle == 2.0f * M_PI || concavity < 0) {
+ return angle;
+ }
+ return -angle;
+ };
+
+ VArray<float> angles = VArray<float>::ForFunc(mesh->totedge, angle_fn);
+ return component.attribute_try_adapt_domain<float>(
+ std::move(angles), ATTR_DOMAIN_EDGE, domain);
+ }
+
+ uint64_t hash() const override
+ {
+ /* Some random constant hash. */
+ return 68465416863;
+ }
+
+ bool is_equal_to(const fn::FieldNode &other) const override
+ {
+ return dynamic_cast<const SignedAngleFieldInput *>(&other) != nullptr;
+ }
+};
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ if (params.output_is_required("Unsigned Angle")) {
+ Field<float> angle_field{std::make_shared<AngleFieldInput>()};
+ params.set_output("Unsigned Angle", std::move(angle_field));
+ }
+ if (params.output_is_required("Signed Angle")) {
+ Field<float> angle_field{std::make_shared<SignedAngleFieldInput>()};
+ params.set_output("Signed Angle", std::move(angle_field));
+ }
+}
+
+} // namespace blender::nodes::node_geo_input_mesh_edge_angle_cc
+
+void register_node_type_geo_input_mesh_edge_angle()
+{
+ namespace file_ns = blender::nodes::node_geo_input_mesh_edge_angle_cc;
+
+ static bNodeType ntype;
+ geo_node_type_base(&ntype, GEO_NODE_INPUT_MESH_EDGE_ANGLE, "Edge Angle", NODE_CLASS_INPUT);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_neighbors.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_neighbors.cc
new file mode 100644
index 00000000000..ddeb3ded511
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_neighbors.cc
@@ -0,0 +1,93 @@
+/*
+ * 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 "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_mesh.h"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_input_mesh_edge_neighbors_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_output<decl::Int>(N_("Face Count"))
+ .field_source()
+ .description(N_("Number of faces that contain the edge"));
+}
+
+class EdgeNeighborCountFieldInput final : public GeometryFieldInput {
+ public:
+ EdgeNeighborCountFieldInput()
+ : GeometryFieldInput(CPPType::get<int>(), "Edge Neighbor Count Field")
+ {
+ category_ = Category::Generated;
+ }
+
+ GVArray get_varray_for_context(const GeometryComponent &component,
+ const AttributeDomain domain,
+ IndexMask UNUSED(mask)) const final
+ {
+ if (component.type() == GEO_COMPONENT_TYPE_MESH) {
+ const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
+ const Mesh *mesh = mesh_component.get_for_read();
+ if (mesh == nullptr) {
+ return {};
+ }
+
+ Array<int> face_count(mesh->totedge, 0);
+ for (const int i : IndexRange(mesh->totloop)) {
+ face_count[mesh->mloop[i].e]++;
+ }
+
+ return mesh_component.attribute_try_adapt_domain<int>(
+ VArray<int>::ForContainer(std::move(face_count)), ATTR_DOMAIN_EDGE, domain);
+ }
+ return {};
+ }
+
+ uint64_t hash() const override
+ {
+ /* Some random constant hash. */
+ return 985671075;
+ }
+
+ bool is_equal_to(const fn::FieldNode &other) const override
+ {
+ return dynamic_cast<const EdgeNeighborCountFieldInput *>(&other) != nullptr;
+ }
+};
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ Field<int> neighbor_count_field{std::make_shared<EdgeNeighborCountFieldInput>()};
+ params.set_output("Face Count", std::move(neighbor_count_field));
+}
+
+} // namespace blender::nodes::node_geo_input_mesh_edge_neighbors_cc
+
+void register_node_type_geo_input_mesh_edge_neighbors()
+{
+ namespace file_ns = blender::nodes::node_geo_input_mesh_edge_neighbors_cc;
+
+ static bNodeType ntype;
+ geo_node_type_base(
+ &ntype, GEO_NODE_INPUT_MESH_EDGE_NEIGHBORS, "Edge Neighbors", NODE_CLASS_INPUT);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_vertices.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_vertices.cc
new file mode 100644
index 00000000000..f54c92fea7b
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_vertices.cc
@@ -0,0 +1,186 @@
+/*
+ * 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 "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_mesh.h"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_input_mesh_edge_vertices_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_output<decl::Int>(N_("Vertex Index 1"))
+ .field_source()
+ .description(N_("The index of the first vertex in the edge"));
+ b.add_output<decl::Int>(N_("Vertex Index 2"))
+ .field_source()
+ .description(N_("The index of the second vertex in the edge"));
+ b.add_output<decl::Vector>(N_("Position 1"))
+ .field_source()
+ .description(N_("The position of the first vertex in the edge"));
+ b.add_output<decl::Vector>(N_("Position 2"))
+ .field_source()
+ .description(N_("The position of the second vertex in the edge"));
+}
+
+enum VertexNumber { VERTEX_ONE, VERTEX_TWO };
+
+static VArray<int> construct_edge_vertices_gvarray(const MeshComponent &component,
+ const VertexNumber vertex,
+ const AttributeDomain domain)
+{
+ const Mesh *mesh = component.get_for_read();
+ if (mesh == nullptr) {
+ return {};
+ }
+ if (domain == ATTR_DOMAIN_EDGE) {
+ if (vertex == VERTEX_ONE) {
+ return VArray<int>::ForFunc(mesh->totedge,
+ [mesh](const int i) -> int { return mesh->medge[i].v1; });
+ }
+ return VArray<int>::ForFunc(mesh->totedge,
+ [mesh](const int i) -> int { return mesh->medge[i].v2; });
+ }
+ return {};
+}
+
+class EdgeVerticesFieldInput final : public GeometryFieldInput {
+ private:
+ VertexNumber vertex_;
+
+ public:
+ EdgeVerticesFieldInput(VertexNumber vertex)
+ : GeometryFieldInput(CPPType::get<int>(), "Edge Vertices Field"), vertex_(vertex)
+ {
+ category_ = Category::Generated;
+ }
+
+ GVArray get_varray_for_context(const GeometryComponent &component,
+ const AttributeDomain domain,
+ IndexMask UNUSED(mask)) const final
+ {
+ if (component.type() == GEO_COMPONENT_TYPE_MESH) {
+ const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
+ return construct_edge_vertices_gvarray(mesh_component, vertex_, domain);
+ }
+ return {};
+ }
+
+ uint64_t hash() const override
+ {
+ return vertex_ == VERTEX_ONE ? 23847562893465 : 92384598734567;
+ }
+
+ bool is_equal_to(const fn::FieldNode &other) const override
+ {
+ if (const EdgeVerticesFieldInput *other_field = dynamic_cast<const EdgeVerticesFieldInput *>(
+ &other)) {
+ return vertex_ == other_field->vertex_;
+ }
+ return false;
+ }
+};
+
+static VArray<float3> construct_edge_positions_gvarray(const MeshComponent &component,
+ const VertexNumber vertex,
+ const AttributeDomain domain)
+{
+ const Mesh *mesh = component.get_for_read();
+ if (mesh == nullptr) {
+ return {};
+ }
+
+ if (vertex == VERTEX_ONE) {
+ return component.attribute_try_adapt_domain<float3>(
+ VArray<float3>::ForFunc(
+ mesh->totedge,
+ [mesh](const int i) { return float3(mesh->mvert[mesh->medge[i].v1].co); }),
+ ATTR_DOMAIN_EDGE,
+ domain);
+ }
+ return component.attribute_try_adapt_domain<float3>(
+ VArray<float3>::ForFunc(
+ mesh->totedge,
+ [mesh](const int i) { return float3(mesh->mvert[mesh->medge[i].v2].co); }),
+ ATTR_DOMAIN_EDGE,
+ domain);
+}
+
+class EdgePositionFieldInput final : public GeometryFieldInput {
+ private:
+ VertexNumber vertex_;
+
+ public:
+ EdgePositionFieldInput(VertexNumber vertex)
+ : GeometryFieldInput(CPPType::get<float3>(), "Edge Position Field"), vertex_(vertex)
+ {
+ category_ = Category::Generated;
+ }
+
+ GVArray get_varray_for_context(const GeometryComponent &component,
+ const AttributeDomain domain,
+ IndexMask UNUSED(mask)) const final
+ {
+ if (component.type() == GEO_COMPONENT_TYPE_MESH) {
+ const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
+ return construct_edge_positions_gvarray(mesh_component, vertex_, domain);
+ }
+ return {};
+ }
+
+ uint64_t hash() const override
+ {
+ return vertex_ == VERTEX_ONE ? 987456978362 : 374587679866;
+ }
+
+ bool is_equal_to(const fn::FieldNode &other) const override
+ {
+ if (const EdgePositionFieldInput *other_field = dynamic_cast<const EdgePositionFieldInput *>(
+ &other)) {
+ return vertex_ == other_field->vertex_;
+ }
+ return false;
+ }
+};
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ Field<int> vertex_field_1{std::make_shared<EdgeVerticesFieldInput>(VERTEX_ONE)};
+ Field<int> vertex_field_2{std::make_shared<EdgeVerticesFieldInput>(VERTEX_TWO)};
+ Field<float3> position_field_1{std::make_shared<EdgePositionFieldInput>(VERTEX_ONE)};
+ Field<float3> position_field_2{std::make_shared<EdgePositionFieldInput>(VERTEX_TWO)};
+
+ params.set_output("Vertex Index 1", std::move(vertex_field_1));
+ params.set_output("Vertex Index 2", std::move(vertex_field_2));
+ params.set_output("Position 1", std::move(position_field_1));
+ params.set_output("Position 2", std::move(position_field_2));
+}
+
+} // namespace blender::nodes::node_geo_input_mesh_edge_vertices_cc
+
+void register_node_type_geo_input_mesh_edge_vertices()
+{
+ namespace file_ns = blender::nodes::node_geo_input_mesh_edge_vertices_cc;
+
+ static bNodeType ntype;
+ geo_node_type_base(&ntype, GEO_NODE_INPUT_MESH_EDGE_VERTICES, "Edge Vertices", NODE_CLASS_INPUT);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_area.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_area.cc
new file mode 100644
index 00000000000..ef8adff48f1
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_area.cc
@@ -0,0 +1,96 @@
+/*
+ * 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 "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_mesh.h"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_input_mesh_face_area_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_output<decl::Float>(N_("Area"))
+ .field_source()
+ .description(N_("The surface area of each of the mesh's faces"));
+}
+
+static VArray<float> construct_face_area_gvarray(const MeshComponent &component,
+ const AttributeDomain domain)
+{
+ const Mesh *mesh = component.get_for_read();
+ if (mesh == nullptr) {
+ return {};
+ }
+
+ auto area_fn = [mesh](const int i) -> float {
+ const MPoly *mp = &mesh->mpoly[i];
+ return BKE_mesh_calc_poly_area(mp, &mesh->mloop[mp->loopstart], mesh->mvert);
+ };
+
+ return component.attribute_try_adapt_domain<float>(
+ VArray<float>::ForFunc(mesh->totpoly, area_fn), ATTR_DOMAIN_FACE, domain);
+}
+
+class FaceAreaFieldInput final : public GeometryFieldInput {
+ public:
+ FaceAreaFieldInput() : GeometryFieldInput(CPPType::get<float>(), "Face Area Field")
+ {
+ category_ = Category::Generated;
+ }
+
+ GVArray get_varray_for_context(const GeometryComponent &component,
+ const AttributeDomain domain,
+ IndexMask UNUSED(mask)) const final
+ {
+ if (component.type() == GEO_COMPONENT_TYPE_MESH) {
+ const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
+ return construct_face_area_gvarray(mesh_component, domain);
+ }
+ return {};
+ }
+
+ uint64_t hash() const override
+ {
+ /* Some random constant hash. */
+ return 1346334523;
+ }
+
+ bool is_equal_to(const fn::FieldNode &other) const override
+ {
+ return dynamic_cast<const FaceAreaFieldInput *>(&other) != nullptr;
+ }
+};
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ params.set_output("Area", Field<float>(std::make_shared<FaceAreaFieldInput>()));
+}
+
+} // namespace blender::nodes::node_geo_input_mesh_face_area_cc
+
+void register_node_type_geo_input_mesh_face_area()
+{
+ namespace file_ns = blender::nodes::node_geo_input_mesh_face_area_cc;
+
+ static bNodeType ntype;
+ geo_node_type_base(&ntype, GEO_NODE_INPUT_MESH_FACE_AREA, "Face Area", NODE_CLASS_INPUT);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_neighbors.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_neighbors.cc
new file mode 100644
index 00000000000..8d196e5f8dd
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_neighbors.cc
@@ -0,0 +1,158 @@
+/*
+ * 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 "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_mesh.h"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_input_mesh_face_neighbors_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_output<decl::Int>(N_("Vertex Count"))
+ .field_source()
+ .description(N_("Number of edges or points in the face"));
+ b.add_output<decl::Int>(N_("Face Count"))
+ .field_source()
+ .description(N_("Number of faces which share an edge with the face"));
+}
+
+static VArray<int> construct_neighbor_count_gvarray(const MeshComponent &component,
+ const AttributeDomain domain)
+{
+ const Mesh *mesh = component.get_for_read();
+ if (mesh == nullptr) {
+ return {};
+ }
+
+ Array<int> edge_count(mesh->totedge, 0);
+ for (const int i : IndexRange(mesh->totloop)) {
+ edge_count[mesh->mloop[i].e]++;
+ }
+
+ Array<int> poly_count(mesh->totpoly, 0);
+ for (const int poly_num : IndexRange(mesh->totpoly)) {
+ MPoly &poly = mesh->mpoly[poly_num];
+ for (const int loop_num : IndexRange(poly.loopstart, poly.totloop)) {
+ poly_count[poly_num] += edge_count[mesh->mloop[loop_num].e] - 1;
+ }
+ }
+
+ return component.attribute_try_adapt_domain<int>(
+ VArray<int>::ForContainer(std::move(poly_count)), ATTR_DOMAIN_FACE, domain);
+}
+
+class FaceNeighborCountFieldInput final : public GeometryFieldInput {
+ public:
+ FaceNeighborCountFieldInput()
+ : GeometryFieldInput(CPPType::get<int>(), "Face Neighbor Count Field")
+ {
+ category_ = Category::Generated;
+ }
+
+ GVArray get_varray_for_context(const GeometryComponent &component,
+ const AttributeDomain domain,
+ IndexMask UNUSED(mask)) const final
+ {
+ if (component.type() == GEO_COMPONENT_TYPE_MESH) {
+ const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
+ return construct_neighbor_count_gvarray(mesh_component, domain);
+ }
+ return {};
+ }
+
+ uint64_t hash() const override
+ {
+ /* Some random constant hash. */
+ return 823543774;
+ }
+
+ bool is_equal_to(const fn::FieldNode &other) const override
+ {
+ return dynamic_cast<const FaceNeighborCountFieldInput *>(&other) != nullptr;
+ }
+};
+
+static VArray<int> construct_vertex_count_gvarray(const MeshComponent &component,
+ const AttributeDomain domain)
+{
+ const Mesh *mesh = component.get_for_read();
+ if (mesh == nullptr) {
+ return {};
+ }
+
+ return component.attribute_try_adapt_domain<int>(
+ VArray<int>::ForFunc(mesh->totpoly,
+ [mesh](const int i) -> float { return mesh->mpoly[i].totloop; }),
+ ATTR_DOMAIN_FACE,
+ domain);
+}
+
+class FaceVertexCountFieldInput final : public GeometryFieldInput {
+ public:
+ FaceVertexCountFieldInput() : GeometryFieldInput(CPPType::get<int>(), "Vertex Count Field")
+ {
+ category_ = Category::Generated;
+ }
+
+ GVArray get_varray_for_context(const GeometryComponent &component,
+ const AttributeDomain domain,
+ IndexMask UNUSED(mask)) const final
+ {
+ if (component.type() == GEO_COMPONENT_TYPE_MESH) {
+ const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
+ return construct_vertex_count_gvarray(mesh_component, domain);
+ }
+ return {};
+ }
+
+ uint64_t hash() const override
+ {
+ /* Some random constant hash. */
+ return 236235463634;
+ }
+
+ bool is_equal_to(const fn::FieldNode &other) const override
+ {
+ return dynamic_cast<const FaceVertexCountFieldInput *>(&other) != nullptr;
+ }
+};
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ Field<int> vertex_count_field{std::make_shared<FaceVertexCountFieldInput>()};
+ Field<int> neighbor_count_field{std::make_shared<FaceNeighborCountFieldInput>()};
+ params.set_output("Vertex Count", std::move(vertex_count_field));
+ params.set_output("Face Count", std::move(neighbor_count_field));
+}
+
+} // namespace blender::nodes::node_geo_input_mesh_face_neighbors_cc
+
+void register_node_type_geo_input_mesh_face_neighbors()
+{
+ namespace file_ns = blender::nodes::node_geo_input_mesh_face_neighbors_cc;
+
+ static bNodeType ntype;
+ geo_node_type_base(
+ &ntype, GEO_NODE_INPUT_MESH_FACE_NEIGHBORS, "Face Neighbors", NODE_CLASS_INPUT);
+ node_type_size_preset(&ntype, NODE_SIZE_MIDDLE);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc
new file mode 100644
index 00000000000..68bb93bbb64
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc
@@ -0,0 +1,157 @@
+/*
+ * 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 "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_mesh.h"
+
+#include "BLI_disjoint_set.hh"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_input_mesh_island_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_output<decl::Int>(N_("Island Index"))
+ .field_source()
+ .description(N_("Island indices are based on the order of the lowest-numbered vertex "
+ "contained in each island"));
+ b.add_output<decl::Int>(N_("Island Count"))
+ .field_source()
+ .description(N_("The total number of mesh islands"));
+}
+
+class IslandFieldInput final : public GeometryFieldInput {
+ public:
+ IslandFieldInput() : GeometryFieldInput(CPPType::get<int>(), "Island Index")
+ {
+ category_ = Category::Generated;
+ }
+
+ GVArray get_varray_for_context(const GeometryComponent &component,
+ const AttributeDomain domain,
+ IndexMask UNUSED(mask)) const final
+ {
+ if (component.type() != GEO_COMPONENT_TYPE_MESH) {
+ return {};
+ }
+ const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
+ const Mesh *mesh = mesh_component.get_for_read();
+ if (mesh == nullptr) {
+ return {};
+ }
+
+ DisjointSet islands(mesh->totvert);
+ for (const int i : IndexRange(mesh->totedge)) {
+ islands.join(mesh->medge[i].v1, mesh->medge[i].v2);
+ }
+
+ Array<int> output(mesh->totvert);
+ VectorSet<int> ordered_roots;
+ for (const int i : IndexRange(mesh->totvert)) {
+ const int64_t root = islands.find_root(i);
+ output[i] = ordered_roots.index_of_or_add(root);
+ }
+
+ return mesh_component.attribute_try_adapt_domain<int>(
+ VArray<int>::ForContainer(std::move(output)), ATTR_DOMAIN_POINT, domain);
+ }
+
+ uint64_t hash() const override
+ {
+ /* Some random constant hash. */
+ return 635467354;
+ }
+
+ bool is_equal_to(const fn::FieldNode &other) const override
+ {
+ return dynamic_cast<const IslandFieldInput *>(&other) != nullptr;
+ }
+};
+
+class IslandCountFieldInput final : public GeometryFieldInput {
+ public:
+ IslandCountFieldInput() : GeometryFieldInput(CPPType::get<int>(), "Island Count")
+ {
+ category_ = Category::Generated;
+ }
+
+ GVArray get_varray_for_context(const GeometryComponent &component,
+ const AttributeDomain domain,
+ IndexMask UNUSED(mask)) const final
+ {
+ if (component.type() != GEO_COMPONENT_TYPE_MESH) {
+ return {};
+ }
+ const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
+ const Mesh *mesh = mesh_component.get_for_read();
+ if (mesh == nullptr) {
+ return {};
+ }
+
+ DisjointSet islands(mesh->totvert);
+ for (const int i : IndexRange(mesh->totedge)) {
+ islands.join(mesh->medge[i].v1, mesh->medge[i].v2);
+ }
+
+ Set<int> island_list;
+ for (const int i_vert : IndexRange(mesh->totvert)) {
+ const int64_t root = islands.find_root(i_vert);
+ island_list.add(root);
+ }
+
+ return VArray<int>::ForSingle(island_list.size(),
+ mesh_component.attribute_domain_size(domain));
+ }
+
+ uint64_t hash() const override
+ {
+ /* Some random hash. */
+ return 45634572457;
+ }
+
+ bool is_equal_to(const fn::FieldNode &other) const override
+ {
+ return dynamic_cast<const IslandCountFieldInput *>(&other) != nullptr;
+ }
+};
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ if (params.output_is_required("Island Index")) {
+ Field<int> field{std::make_shared<IslandFieldInput>()};
+ params.set_output("Island Index", std::move(field));
+ }
+ if (params.output_is_required("Island Count")) {
+ Field<int> field{std::make_shared<IslandCountFieldInput>()};
+ params.set_output("Island Count", std::move(field));
+ }
+}
+
+} // namespace blender::nodes::node_geo_input_mesh_island_cc
+
+void register_node_type_geo_input_mesh_island()
+{
+ namespace file_ns = blender::nodes::node_geo_input_mesh_island_cc;
+
+ static bNodeType ntype;
+ geo_node_type_base(&ntype, GEO_NODE_INPUT_MESH_ISLAND, "Mesh Island", NODE_CLASS_INPUT);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_vertex_neighbors.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_vertex_neighbors.cc
new file mode 100644
index 00000000000..7d79164634d
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_vertex_neighbors.cc
@@ -0,0 +1,155 @@
+/*
+ * 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 "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_mesh.h"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_input_mesh_vertex_neighbors_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_output<decl::Int>(N_("Vertex Count"))
+ .field_source()
+ .description(N_("Vertex count and edge count are equal"));
+ b.add_output<decl::Int>(N_("Face Count"))
+ .field_source()
+ .description(N_("Number of faces that contain the vertex"));
+}
+
+static VArray<int> construct_vertex_count_gvarray(const MeshComponent &component,
+ const AttributeDomain domain)
+{
+ const Mesh *mesh = component.get_for_read();
+ if (mesh == nullptr) {
+ return {};
+ }
+
+ if (domain == ATTR_DOMAIN_POINT) {
+ Array<int> vertices(mesh->totvert, 0);
+ for (const int i : IndexRange(mesh->totedge)) {
+ vertices[mesh->medge[i].v1]++;
+ vertices[mesh->medge[i].v2]++;
+ }
+ return VArray<int>::ForContainer(std::move(vertices));
+ }
+ return {};
+}
+
+class VertexCountFieldInput final : public GeometryFieldInput {
+ public:
+ VertexCountFieldInput() : GeometryFieldInput(CPPType::get<int>(), "Vertex Count Field")
+ {
+ category_ = Category::Generated;
+ }
+
+ GVArray get_varray_for_context(const GeometryComponent &component,
+ const AttributeDomain domain,
+ IndexMask UNUSED(mask)) const final
+ {
+ if (component.type() == GEO_COMPONENT_TYPE_MESH) {
+ const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
+ return construct_vertex_count_gvarray(mesh_component, domain);
+ }
+ return {};
+ }
+
+ uint64_t hash() const override
+ {
+ /* Some random constant hash. */
+ return 23574528465;
+ }
+
+ bool is_equal_to(const fn::FieldNode &other) const override
+ {
+ return dynamic_cast<const VertexCountFieldInput *>(&other) != nullptr;
+ }
+};
+
+static VArray<int> construct_face_count_gvarray(const MeshComponent &component,
+ const AttributeDomain domain)
+{
+ const Mesh *mesh = component.get_for_read();
+ if (mesh == nullptr) {
+ return {};
+ }
+
+ if (domain == ATTR_DOMAIN_POINT) {
+ Array<int> vertices(mesh->totvert, 0);
+ for (const int i : IndexRange(mesh->totloop)) {
+ int vertex = mesh->mloop[i].v;
+ vertices[vertex]++;
+ }
+ return VArray<int>::ForContainer(std::move(vertices));
+ }
+ return {};
+}
+
+class VertexFaceCountFieldInput final : public GeometryFieldInput {
+ public:
+ VertexFaceCountFieldInput() : GeometryFieldInput(CPPType::get<int>(), "Vertex Face Count Field")
+ {
+ category_ = Category::Generated;
+ }
+
+ GVArray get_varray_for_context(const GeometryComponent &component,
+ const AttributeDomain domain,
+ IndexMask UNUSED(mask)) const final
+ {
+ if (component.type() == GEO_COMPONENT_TYPE_MESH) {
+ const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
+ return construct_face_count_gvarray(mesh_component, domain);
+ }
+ return {};
+ }
+
+ uint64_t hash() const override
+ {
+ /* Some random constant hash. */
+ return 3462374322;
+ }
+
+ bool is_equal_to(const fn::FieldNode &other) const override
+ {
+ return dynamic_cast<const VertexFaceCountFieldInput *>(&other) != nullptr;
+ }
+};
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ Field<int> vertex_field{std::make_shared<VertexCountFieldInput>()};
+ Field<int> face_field{std::make_shared<VertexFaceCountFieldInput>()};
+
+ params.set_output("Vertex Count", std::move(vertex_field));
+ params.set_output("Face Count", std::move(face_field));
+}
+
+} // namespace blender::nodes::node_geo_input_mesh_vertex_neighbors_cc
+
+void register_node_type_geo_input_mesh_vertex_neighbors()
+{
+ namespace file_ns = blender::nodes::node_geo_input_mesh_vertex_neighbors_cc;
+
+ static bNodeType ntype;
+ geo_node_type_base(
+ &ntype, GEO_NODE_INPUT_MESH_VERTEX_NEIGHBORS, "Vertex Neighbors", NODE_CLASS_INPUT);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc b/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc
index 5a2495afb9e..120ae0e9bd1 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc
@@ -24,280 +24,29 @@
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_input_normal_cc {
-static void geo_node_input_normal_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_output<decl::Vector>("Normal").field_source();
+ b.add_output<decl::Vector>(N_("Normal")).field_source();
}
-static GVArrayPtr mesh_face_normals(const Mesh &mesh,
- const Span<MVert> verts,
- const Span<MPoly> polys,
- const Span<MLoop> loops,
- const IndexMask mask)
+static void node_geo_exec(GeoNodeExecParams params)
{
- /* Use existing normals to avoid unnecessarily recalculating them, if possible. */
- if (!(mesh.runtime.cd_dirty_poly & CD_MASK_NORMAL) &&
- CustomData_has_layer(&mesh.pdata, CD_NORMAL)) {
- const void *data = CustomData_get_layer(&mesh.pdata, CD_NORMAL);
-
- return std::make_unique<fn::GVArray_For_Span<float3>>(
- Span<float3>((const float3 *)data, polys.size()));
- }
-
- auto normal_fn = [verts, polys, loops](const int i) -> float3 {
- float3 normal;
- const MPoly &poly = polys[i];
- BKE_mesh_calc_poly_normal(&poly, &loops[poly.loopstart], verts.data(), normal);
- return normal;
- };
-
- return std::make_unique<
- fn::GVArray_For_EmbeddedVArray<float3, VArray_For_Func<float3, decltype(normal_fn)>>>(
- mask.min_array_size(), mask.min_array_size(), normal_fn);
-}
-
-static GVArrayPtr mesh_vertex_normals(const Mesh &mesh,
- const Span<MVert> verts,
- const Span<MPoly> polys,
- const Span<MLoop> loops,
- const IndexMask mask)
-{
- /* Use existing normals to avoid unnecessarily recalculating them, if possible. */
- if (!(mesh.runtime.cd_dirty_vert & CD_MASK_NORMAL) &&
- CustomData_has_layer(&mesh.vdata, CD_NORMAL)) {
- const void *data = CustomData_get_layer(&mesh.pdata, CD_NORMAL);
-
- return std::make_unique<fn::GVArray_For_Span<float3>>(
- Span<float3>((const float3 *)data, mesh.totvert));
- }
-
- /* If the normals are dirty, they must be recalculated for the output of this node's field
- * source. Ideally vertex normals could be calculated lazily on a const mesh, but that's not
- * possible at the moment, so we take ownership of the results. Sadly we must also create a copy
- * of MVert to use the mesh normals API. This can be improved by adding mutex-protected lazy
- * calculation of normals on meshes.
- *
- * Use mask.min_array_size() to avoid calculating a final chunk of data if possible. */
- Array<MVert> temp_verts(verts);
- Array<float3> normals(verts.size()); /* Use full size for accumulation from faces. */
- BKE_mesh_calc_normals_poly_and_vertex(temp_verts.data(),
- mask.min_array_size(),
- loops.data(),
- loops.size(),
- polys.data(),
- polys.size(),
- nullptr,
- (float(*)[3])normals.data());
-
- return std::make_unique<fn::GVArray_For_ArrayContainer<Array<float3>>>(std::move(normals));
-}
-
-static const GVArray *construct_mesh_normals_gvarray(const MeshComponent &mesh_component,
- const Mesh &mesh,
- const IndexMask mask,
- const AttributeDomain domain,
- ResourceScope &scope)
-{
- Span<MVert> verts{mesh.mvert, mesh.totvert};
- Span<MEdge> edges{mesh.medge, mesh.totedge};
- Span<MPoly> polys{mesh.mpoly, mesh.totpoly};
- Span<MLoop> loops{mesh.mloop, mesh.totloop};
-
- switch (domain) {
- case ATTR_DOMAIN_FACE: {
- return scope.add_value(mesh_face_normals(mesh, verts, polys, loops, mask)).get();
- }
- case ATTR_DOMAIN_POINT: {
- return scope.add_value(mesh_vertex_normals(mesh, verts, polys, loops, mask)).get();
- }
- case ATTR_DOMAIN_EDGE: {
- /* In this case, start with vertex normals and convert to the edge domain, since the
- * conversion from edges to vertices is very simple. Use the full mask since the edges
- * might use the vertex normal from any index. */
- GVArrayPtr vert_normals = mesh_vertex_normals(
- mesh, verts, polys, loops, IndexRange(verts.size()));
- Span<float3> vert_normals_span = vert_normals->get_internal_span().typed<float3>();
- Array<float3> edge_normals(mask.min_array_size());
-
- /* Use "manual" domain interpolation instead of the GeometryComponent API to avoid
- * calculating unnecessary values and to allow normalizing the result much more simply. */
- for (const int i : mask) {
- const MEdge &edge = edges[i];
- edge_normals[i] = float3::interpolate(
- vert_normals_span[edge.v1], vert_normals_span[edge.v2], 0.5f)
- .normalized();
- }
-
- return &scope.construct<fn::GVArray_For_ArrayContainer<Array<float3>>>(
- std::move(edge_normals));
- }
- case ATTR_DOMAIN_CORNER: {
- /* The normals on corners are just the mesh's face normals, so start with the face normal
- * array and copy the face normal for each of its corners. */
- GVArrayPtr face_normals = mesh_face_normals(
- mesh, verts, polys, loops, IndexRange(polys.size()));
-
- /* In this case using the mesh component's generic domain interpolation is fine, the data
- * will still be normalized, since the face normal is just copied to every corner. */
- GVArrayPtr loop_normals = mesh_component.attribute_try_adapt_domain(
- std::move(face_normals), ATTR_DOMAIN_FACE, ATTR_DOMAIN_CORNER);
- return scope.add_value(std::move(loop_normals)).get();
- }
- default:
- return nullptr;
- }
-}
-
-static void calculate_bezier_normals(const BezierSpline &spline, MutableSpan<float3> normals)
-{
- Span<int> offsets = spline.control_point_offsets();
- Span<float3> evaluated_normals = spline.evaluated_normals();
- for (const int i : IndexRange(spline.size())) {
- normals[i] = evaluated_normals[offsets[i]];
- }
-}
-
-static void calculate_poly_normals(const PolySpline &spline, MutableSpan<float3> normals)
-{
- normals.copy_from(spline.evaluated_normals());
-}
-
-/**
- * Because NURBS control points are not necessarily on the path, the normal at the control points
- * is not well defined, so create a temporary poly spline to find the normals. This requires extra
- * copying currently, but may be more efficient in the future if attributes have some form of CoW.
- */
-static void calculate_nurbs_normals(const NURBSpline &spline, MutableSpan<float3> normals)
-{
- PolySpline poly_spline;
- poly_spline.resize(spline.size());
- poly_spline.positions().copy_from(spline.positions());
- normals.copy_from(poly_spline.evaluated_normals());
-}
-
-static Array<float3> curve_normal_point_domain(const CurveEval &curve)
-{
- Span<SplinePtr> splines = curve.splines();
- Array<int> offsets = curve.control_point_offsets();
- const int total_size = offsets.last();
- Array<float3> normals(total_size);
-
- threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) {
- for (const int i : range) {
- const Spline &spline = *splines[i];
- MutableSpan spline_normals{normals.as_mutable_span().slice(offsets[i], spline.size())};
- switch (splines[i]->type()) {
- case Spline::Type::Bezier:
- calculate_bezier_normals(static_cast<const BezierSpline &>(spline), spline_normals);
- break;
- case Spline::Type::Poly:
- calculate_poly_normals(static_cast<const PolySpline &>(spline), spline_normals);
- break;
- case Spline::Type::NURBS:
- calculate_nurbs_normals(static_cast<const NURBSpline &>(spline), spline_normals);
- break;
- }
- }
- });
- return normals;
-}
-
-static const GVArray *construct_curve_normal_gvarray(const CurveComponent &component,
- const AttributeDomain domain,
- ResourceScope &scope)
-{
- const CurveEval *curve = component.get_for_read();
- if (curve == nullptr) {
- return nullptr;
- }
-
- if (domain == ATTR_DOMAIN_POINT) {
- const Span<SplinePtr> splines = curve->splines();
-
- /* Use a reference to evaluated normals if possible to avoid an allocation and a copy.
- * This is only possible when there is only one poly spline. */
- if (splines.size() == 1 && splines.first()->type() == Spline::Type::Poly) {
- const PolySpline &spline = static_cast<PolySpline &>(*splines.first());
- return &scope.construct<fn::GVArray_For_Span<float3>>(spline.evaluated_normals());
- }
-
- Array<float3> normals = curve_normal_point_domain(*curve);
- return &scope.construct<fn::GVArray_For_ArrayContainer<Array<float3>>>(std::move(normals));
- }
-
- if (domain == ATTR_DOMAIN_CURVE) {
- Array<float3> point_normals = curve_normal_point_domain(*curve);
- GVArrayPtr gvarray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<float3>>>(
- std::move(point_normals));
- GVArrayPtr spline_normals = component.attribute_try_adapt_domain(
- std::move(gvarray), ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE);
- return scope.add_value(std::move(spline_normals)).get();
- }
-
- return nullptr;
-}
-
-class NormalFieldInput final : public fn::FieldInput {
- public:
- NormalFieldInput() : fn::FieldInput(CPPType::get<float3>(), "Normal")
- {
- }
-
- const GVArray *get_varray_for_context(const fn::FieldContext &context,
- IndexMask mask,
- ResourceScope &scope) const final
- {
- if (const GeometryComponentFieldContext *geometry_context =
- dynamic_cast<const GeometryComponentFieldContext *>(&context)) {
-
- const GeometryComponent &component = geometry_context->geometry_component();
- const AttributeDomain domain = geometry_context->domain();
-
- if (component.type() == GEO_COMPONENT_TYPE_MESH) {
- const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
- const Mesh *mesh = mesh_component.get_for_read();
- if (mesh == nullptr) {
- return nullptr;
- }
-
- return construct_mesh_normals_gvarray(mesh_component, *mesh, mask, domain, scope);
- }
- if (component.type() == GEO_COMPONENT_TYPE_CURVE) {
- const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
- return construct_curve_normal_gvarray(curve_component, domain, scope);
- }
- }
- return nullptr;
- }
-
- uint64_t hash() const override
- {
- /* Some random constant hash. */
- return 669605641;
- }
-
- bool is_equal_to(const fn::FieldNode &other) const override
- {
- return dynamic_cast<const NormalFieldInput *>(&other) != nullptr;
- }
-};
-
-static void geo_node_input_normal_exec(GeoNodeExecParams params)
-{
- Field<float3> normal_field{std::make_shared<NormalFieldInput>()};
+ Field<float3> normal_field{std::make_shared<bke::NormalFieldInput>()};
params.set_output("Normal", std::move(normal_field));
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_input_normal_cc
void register_node_type_geo_input_normal()
{
+ namespace file_ns = blender::nodes::node_geo_input_normal_cc;
+
static bNodeType ntype;
- geo_node_type_base(&ntype, GEO_NODE_INPUT_NORMAL, "Normal", NODE_CLASS_INPUT, 0);
- ntype.geometry_node_execute = blender::nodes::geo_node_input_normal_exec;
- ntype.declare = blender::nodes::geo_node_input_normal_declare;
+ geo_node_type_base(&ntype, GEO_NODE_INPUT_NORMAL, "Normal", NODE_CLASS_INPUT);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.declare = file_ns::node_declare;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_position.cc b/source/blender/nodes/geometry/nodes/node_geo_input_position.cc
index 44874259e20..beb528d2fd8 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_position.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_position.cc
@@ -16,27 +16,29 @@
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_input_position_cc {
-static void geo_node_input_position_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_output<decl::Vector>("Position").field_source();
+ b.add_output<decl::Vector>(N_("Position")).field_source();
}
-static void geo_node_input_position_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
Field<float3> position_field{AttributeFieldInput::Create<float3>("position")};
params.set_output("Position", std::move(position_field));
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_input_position_cc
void register_node_type_geo_input_position()
{
+ namespace file_ns = blender::nodes::node_geo_input_position_cc;
+
static bNodeType ntype;
- geo_node_type_base(&ntype, GEO_NODE_INPUT_POSITION, "Position", NODE_CLASS_INPUT, 0);
- ntype.geometry_node_execute = blender::nodes::geo_node_input_position_exec;
- ntype.declare = blender::nodes::geo_node_input_position_declare;
+ geo_node_type_base(&ntype, GEO_NODE_INPUT_POSITION, "Position", NODE_CLASS_INPUT);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.declare = file_ns::node_declare;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_radius.cc b/source/blender/nodes/geometry/nodes/node_geo_input_radius.cc
new file mode 100644
index 00000000000..c7777da08c6
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_radius.cc
@@ -0,0 +1,44 @@
+/*
+ * 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"
+
+namespace blender::nodes::node_geo_input_radius_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_output<decl::Float>(N_("Radius")).default_value(1.0f).min(0.0f).field_source();
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ Field<float> radius_field = AttributeFieldInput::Create<float>("radius");
+ params.set_output("Radius", std::move(radius_field));
+}
+
+} // namespace blender::nodes::node_geo_input_radius_cc
+
+void register_node_type_geo_input_radius()
+{
+ namespace file_ns = blender::nodes::node_geo_input_radius_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_INPUT_RADIUS, "Radius", NODE_CLASS_INPUT);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.declare = file_ns::node_declare;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_scene_time.cc b/source/blender/nodes/geometry/nodes/node_geo_input_scene_time.cc
new file mode 100644
index 00000000000..4ed65e99a1c
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_scene_time.cc
@@ -0,0 +1,50 @@
+/*
+ * 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 "BKE_scene.h"
+
+#include "DEG_depsgraph_query.h"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_input_scene_time_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_output<decl::Float>(N_("Seconds"));
+ b.add_output<decl::Float>(N_("Frame"));
+}
+
+static void node_exec(GeoNodeExecParams params)
+{
+ const Scene *scene = DEG_get_input_scene(params.depsgraph());
+ const float scene_ctime = BKE_scene_ctime_get(scene);
+ const double frame_rate = (((double)scene->r.frs_sec) / (double)scene->r.frs_sec_base);
+ params.set_output("Seconds", float(scene_ctime / frame_rate));
+ params.set_output("Frame", scene_ctime);
+}
+
+} // namespace blender::nodes::node_geo_input_scene_time_cc
+
+void register_node_type_geo_input_scene_time()
+{
+ static bNodeType ntype;
+ namespace file_ns = blender::nodes::node_geo_input_scene_time_cc;
+ geo_node_type_base(&ntype, GEO_NODE_INPUT_SCENE_TIME, "Scene Time", NODE_CLASS_INPUT);
+ ntype.geometry_node_execute = file_ns::node_exec;
+ ntype.declare = file_ns::node_declare;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_shade_smooth.cc b/source/blender/nodes/geometry/nodes/node_geo_input_shade_smooth.cc
new file mode 100644
index 00000000000..b27ab097223
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_shade_smooth.cc
@@ -0,0 +1,44 @@
+/*
+ * 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"
+
+namespace blender::nodes::node_geo_input_shade_smooth_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_output<decl::Bool>(N_("Smooth")).field_source();
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ Field<bool> shade_smooth_field = AttributeFieldInput::Create<bool>("shade_smooth");
+ params.set_output("Smooth", std::move(shade_smooth_field));
+}
+
+} // namespace blender::nodes::node_geo_input_shade_smooth_cc
+
+void register_node_type_geo_input_shade_smooth()
+{
+ namespace file_ns = blender::nodes::node_geo_input_shade_smooth_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_INPUT_SHADE_SMOOTH, "Is Shade Smooth", NODE_CLASS_INPUT);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.declare = file_ns::node_declare;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_spline_cyclic.cc b/source/blender/nodes/geometry/nodes/node_geo_input_spline_cyclic.cc
new file mode 100644
index 00000000000..2db00a1ae68
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_spline_cyclic.cc
@@ -0,0 +1,44 @@
+/*
+ * 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"
+
+namespace blender::nodes::node_geo_input_spline_cyclic_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_output<decl::Bool>(N_("Cyclic")).field_source();
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ Field<bool> cyclic_field = AttributeFieldInput::Create<bool>("cyclic");
+ params.set_output("Cyclic", std::move(cyclic_field));
+}
+
+} // namespace blender::nodes::node_geo_input_spline_cyclic_cc
+
+void register_node_type_geo_input_spline_cyclic()
+{
+ namespace file_ns = blender::nodes::node_geo_input_spline_cyclic_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_INPUT_SPLINE_CYCLIC, "Is Spline Cyclic", NODE_CLASS_INPUT);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.declare = file_ns::node_declare;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc b/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc
new file mode 100644
index 00000000000..b8c8ce840eb
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc
@@ -0,0 +1,163 @@
+/*
+ * 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 "BKE_spline.hh"
+
+namespace blender::nodes::node_geo_input_spline_length_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_output<decl::Float>(N_("Length")).field_source();
+ b.add_output<decl::Int>(N_("Point Count")).field_source();
+}
+
+/* --------------------------------------------------------------------
+ * Spline Length
+ */
+
+static VArray<float> construct_spline_length_gvarray(const CurveComponent &component,
+ const AttributeDomain domain)
+{
+ const CurveEval *curve = component.get_for_read();
+ if (curve == nullptr) {
+ return {};
+ }
+
+ Span<SplinePtr> splines = curve->splines();
+ auto length_fn = [splines](int i) { return splines[i]->length(); };
+
+ if (domain == ATTR_DOMAIN_CURVE) {
+ return VArray<float>::ForFunc(splines.size(), length_fn);
+ }
+ if (domain == ATTR_DOMAIN_POINT) {
+ VArray<float> length = VArray<float>::ForFunc(splines.size(), length_fn);
+ return component.attribute_try_adapt_domain<float>(
+ std::move(length), ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT);
+ }
+
+ return {};
+}
+
+class SplineLengthFieldInput final : public GeometryFieldInput {
+ public:
+ SplineLengthFieldInput() : GeometryFieldInput(CPPType::get<float>(), "Spline Length node")
+ {
+ category_ = Category::Generated;
+ }
+
+ GVArray get_varray_for_context(const GeometryComponent &component,
+ const AttributeDomain domain,
+ IndexMask UNUSED(mask)) const final
+ {
+ if (component.type() == GEO_COMPONENT_TYPE_CURVE) {
+ const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
+ return construct_spline_length_gvarray(curve_component, domain);
+ }
+ return {};
+ }
+
+ uint64_t hash() const override
+ {
+ /* Some random constant hash. */
+ return 3549623580;
+ }
+
+ bool is_equal_to(const fn::FieldNode &other) const override
+ {
+ return dynamic_cast<const SplineLengthFieldInput *>(&other) != nullptr;
+ }
+};
+
+/* --------------------------------------------------------------------
+ * Spline Count
+ */
+
+static VArray<int> construct_spline_count_gvarray(const CurveComponent &component,
+ const AttributeDomain domain)
+{
+ const CurveEval *curve = component.get_for_read();
+ if (curve == nullptr) {
+ return {};
+ }
+
+ Span<SplinePtr> splines = curve->splines();
+ auto count_fn = [splines](int i) { return splines[i]->size(); };
+
+ if (domain == ATTR_DOMAIN_CURVE) {
+ return VArray<int>::ForFunc(splines.size(), count_fn);
+ }
+ if (domain == ATTR_DOMAIN_POINT) {
+ VArray<int> count = VArray<int>::ForFunc(splines.size(), count_fn);
+ return component.attribute_try_adapt_domain<int>(
+ std::move(count), ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT);
+ }
+
+ return {};
+}
+
+class SplineCountFieldInput final : public GeometryFieldInput {
+ public:
+ SplineCountFieldInput() : GeometryFieldInput(CPPType::get<int>(), "Spline Point Count")
+ {
+ category_ = Category::Generated;
+ }
+
+ GVArray get_varray_for_context(const GeometryComponent &component,
+ const AttributeDomain domain,
+ IndexMask UNUSED(mask)) const final
+ {
+ if (component.type() == GEO_COMPONENT_TYPE_CURVE) {
+ const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
+ return construct_spline_count_gvarray(curve_component, domain);
+ }
+ return {};
+ }
+
+ uint64_t hash() const override
+ {
+ /* Some random constant hash. */
+ return 456364322625;
+ }
+
+ bool is_equal_to(const fn::FieldNode &other) const override
+ {
+ return dynamic_cast<const SplineCountFieldInput *>(&other) != nullptr;
+ }
+};
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ Field<float> spline_length_field{std::make_shared<SplineLengthFieldInput>()};
+ Field<int> spline_count_field{std::make_shared<SplineCountFieldInput>()};
+
+ params.set_output("Length", std::move(spline_length_field));
+ params.set_output("Point Count", std::move(spline_count_field));
+}
+
+} // namespace blender::nodes::node_geo_input_spline_length_cc
+
+void register_node_type_geo_input_spline_length()
+{
+ namespace file_ns = blender::nodes::node_geo_input_spline_length_cc;
+
+ static bNodeType ntype;
+ geo_node_type_base(&ntype, GEO_NODE_INPUT_SPLINE_LENGTH, "Spline Length", NODE_CLASS_INPUT);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.declare = file_ns::node_declare;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_spline_resolution.cc b/source/blender/nodes/geometry/nodes/node_geo_input_spline_resolution.cc
new file mode 100644
index 00000000000..d79f2ffd64d
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_spline_resolution.cc
@@ -0,0 +1,45 @@
+/*
+ * 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"
+
+namespace blender::nodes::node_geo_input_spline_resolution_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_output<decl::Int>(N_("Resolution")).field_source();
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ Field<int> resolution_field = AttributeFieldInput::Create<int>("resolution");
+ params.set_output("Resolution", std::move(resolution_field));
+}
+
+} // namespace blender::nodes::node_geo_input_spline_resolution_cc
+
+void register_node_type_geo_input_spline_resolution()
+{
+ namespace file_ns = blender::nodes::node_geo_input_spline_resolution_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_INPUT_SPLINE_RESOLUTION, "Spline Resolution", NODE_CLASS_INPUT);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.declare = file_ns::node_declare;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc b/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc
index d690642373a..f80fdfbf334 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc
@@ -20,11 +20,11 @@
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_input_tangent_cc {
-static void geo_node_input_tangent_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_output<decl::Vector>("Tangent").field_source();
+ b.add_output<decl::Vector>(N_("Tangent")).field_source();
}
static void calculate_bezier_tangents(const BezierSpline &spline, MutableSpan<float3> tangents)
@@ -84,9 +84,8 @@ static Array<float3> curve_tangent_point_domain(const CurveEval &curve)
return tangents;
}
-static const GVArray *construct_curve_tangent_gvarray(const CurveComponent &component,
- const AttributeDomain domain,
- ResourceScope &scope)
+static VArray<float3> construct_curve_tangent_gvarray(const CurveComponent &component,
+ const AttributeDomain domain)
{
const CurveEval *curve = component.get_for_read();
if (curve == nullptr) {
@@ -100,47 +99,40 @@ static const GVArray *construct_curve_tangent_gvarray(const CurveComponent &comp
* This is only possible when there is only one poly spline. */
if (splines.size() == 1 && splines.first()->type() == Spline::Type::Poly) {
const PolySpline &spline = static_cast<PolySpline &>(*splines.first());
- return &scope.construct<fn::GVArray_For_Span<float3>>(spline.evaluated_tangents());
+ return VArray<float3>::ForSpan(spline.evaluated_tangents());
}
Array<float3> tangents = curve_tangent_point_domain(*curve);
- return &scope.construct<fn::GVArray_For_ArrayContainer<Array<float3>>>(std::move(tangents));
+ return VArray<float3>::ForContainer(std::move(tangents));
}
if (domain == ATTR_DOMAIN_CURVE) {
Array<float3> point_tangents = curve_tangent_point_domain(*curve);
- GVArrayPtr gvarray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<float3>>>(
- std::move(point_tangents));
- GVArrayPtr spline_tangents = component.attribute_try_adapt_domain(
- std::move(gvarray), ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE);
- return scope.add_value(std::move(spline_tangents)).get();
+ return component.attribute_try_adapt_domain<float3>(
+ VArray<float3>::ForContainer(std::move(point_tangents)),
+ ATTR_DOMAIN_POINT,
+ ATTR_DOMAIN_CURVE);
}
return nullptr;
}
-class TangentFieldInput final : public fn::FieldInput {
+class TangentFieldInput final : public GeometryFieldInput {
public:
- TangentFieldInput() : fn::FieldInput(CPPType::get<float3>(), "Tangent")
+ TangentFieldInput() : GeometryFieldInput(CPPType::get<float3>(), "Tangent node")
{
+ category_ = Category::Generated;
}
- const GVArray *get_varray_for_context(const fn::FieldContext &context,
- IndexMask UNUSED(mask),
- ResourceScope &scope) const final
+ GVArray get_varray_for_context(const GeometryComponent &component,
+ const AttributeDomain domain,
+ IndexMask UNUSED(mask)) const final
{
- if (const GeometryComponentFieldContext *geometry_context =
- dynamic_cast<const GeometryComponentFieldContext *>(&context)) {
-
- const GeometryComponent &component = geometry_context->geometry_component();
- const AttributeDomain domain = geometry_context->domain();
-
- if (component.type() == GEO_COMPONENT_TYPE_CURVE) {
- const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
- return construct_curve_tangent_gvarray(curve_component, domain, scope);
- }
+ if (component.type() == GEO_COMPONENT_TYPE_CURVE) {
+ const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
+ return construct_curve_tangent_gvarray(curve_component, domain);
}
- return nullptr;
+ return {};
}
uint64_t hash() const override
@@ -155,20 +147,22 @@ class TangentFieldInput final : public fn::FieldInput {
}
};
-static void geo_node_input_tangent_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
Field<float3> tangent_field{std::make_shared<TangentFieldInput>()};
params.set_output("Tangent", std::move(tangent_field));
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_input_tangent_cc
void register_node_type_geo_input_tangent()
{
+ namespace file_ns = blender::nodes::node_geo_input_tangent_cc;
+
static bNodeType ntype;
- geo_node_type_base(&ntype, GEO_NODE_INPUT_TANGENT, "Curve Tangent", NODE_CLASS_INPUT, 0);
- ntype.geometry_node_execute = blender::nodes::geo_node_input_tangent_exec;
- ntype.declare = blender::nodes::geo_node_input_tangent_declare;
+ geo_node_type_base(&ntype, GEO_NODE_INPUT_TANGENT, "Curve Tangent", NODE_CLASS_INPUT);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.declare = file_ns::node_declare;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc
index 8c0c0763be8..71256a7f781 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc
@@ -22,72 +22,80 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "BKE_attribute_math.hh"
+
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_instance_on_points_cc {
-static void geo_node_instance_on_points_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Points").description("Points to instance on");
- b.add_input<decl::Geometry>("Instance").description("Geometry that is instanced on the points");
- b.add_input<decl::Bool>("Pick Instance")
+ b.add_input<decl::Geometry>(N_("Points")).description(N_("Points to instance on"));
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).supports_field().hide_value();
+ b.add_input<decl::Geometry>(N_("Instance"))
+ .description(N_("Geometry that is instanced on the points"));
+ b.add_input<decl::Bool>(N_("Pick Instance"))
.supports_field()
- .description("Place different instances on different points");
- b.add_input<decl::Int>("Instance Index")
+ .description(N_("Place different instances on different points"));
+ b.add_input<decl::Int>(N_("Instance Index"))
.implicit_field()
- .description(
+ .description(N_(
"Index of the instance that used for each point. This is only used when Pick Instances "
- "is on. By default the point index is used");
- b.add_input<decl::Vector>("Rotation")
+ "is on. By default the point index is used"));
+ b.add_input<decl::Vector>(N_("Rotation"))
.subtype(PROP_EULER)
.supports_field()
- .description("Rotation of the instances");
- b.add_input<decl::Vector>("Scale")
+ .description(N_("Rotation of the instances"));
+ b.add_input<decl::Vector>(N_("Scale"))
.default_value({1.0f, 1.0f, 1.0f})
+ .subtype(PROP_XYZ)
.supports_field()
- .description("Scale of the instances");
- b.add_input<decl::Int>("Stable ID")
- .supports_field()
- .description(
- "ID for every instance that is used to identify it over time even when the number of "
- "instances changes. Used for example for motion blur");
+ .description(N_("Scale of the instances"));
- b.add_output<decl::Geometry>("Instances");
+ b.add_output<decl::Geometry>(N_("Instances"));
}
-static void add_instances_from_component(InstancesComponent &dst_component,
- const GeometryComponent &src_component,
- const GeometrySet &instance,
- const GeoNodeExecParams &params)
+static void add_instances_from_component(
+ InstancesComponent &dst_component,
+ const GeometryComponent &src_component,
+ const GeometrySet &instance,
+ const GeoNodeExecParams &params,
+ const Map<AttributeIDRef, AttributeKind> &attributes_to_propagate)
{
const AttributeDomain domain = ATTR_DOMAIN_POINT;
const int domain_size = src_component.attribute_domain_size(domain);
+ VArray<bool> pick_instance;
+ VArray<int> indices;
+ VArray<float3> rotations;
+ VArray<float3> scales;
+
+ GeometryComponentFieldContext field_context{src_component, domain};
+ const Field<bool> selection_field = params.get_input<Field<bool>>("Selection");
+ fn::FieldEvaluator evaluator{field_context, domain_size};
+ evaluator.set_selection(selection_field);
+ /* The evaluator could use the component's stable IDs as a destination directly, but only the
+ * selected indices should be copied. */
+ evaluator.add(params.get_input<Field<bool>>("Pick Instance"), &pick_instance);
+ evaluator.add(params.get_input<Field<int>>("Instance Index"), &indices);
+ evaluator.add(params.get_input<Field<float3>>("Rotation"), &rotations);
+ evaluator.add(params.get_input<Field<float3>>("Scale"), &scales);
+ evaluator.evaluate();
+
+ const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
+
/* The initial size of the component might be non-zero when this function is called for multiple
* component types. */
const int start_len = dst_component.instances_amount();
- dst_component.resize(start_len + domain_size);
+ const int select_len = selection.index_range().size();
+ dst_component.resize(start_len + select_len);
+
MutableSpan<int> dst_handles = dst_component.instance_reference_handles().slice(start_len,
- domain_size);
+ select_len);
MutableSpan<float4x4> dst_transforms = dst_component.instance_transforms().slice(start_len,
- domain_size);
- MutableSpan<int> dst_stable_ids = dst_component.instance_ids().slice(start_len, domain_size);
+ select_len);
- GeometryComponentFieldContext field_context{src_component, domain};
- FieldEvaluator field_evaluator{field_context, domain_size};
-
- const VArray<bool> *pick_instance = nullptr;
- const VArray<int> *indices = nullptr;
- const VArray<float3> *rotations = nullptr;
- const VArray<float3> *scales = nullptr;
- field_evaluator.add(params.get_input<Field<bool>>("Pick Instance"), &pick_instance);
- field_evaluator.add(params.get_input<Field<int>>("Instance Index"), &indices);
- field_evaluator.add(params.get_input<Field<float3>>("Rotation"), &rotations);
- field_evaluator.add(params.get_input<Field<float3>>("Scale"), &scales);
- field_evaluator.add_with_destination(params.get_input<Field<int>>("Stable ID"), dst_stable_ids);
- field_evaluator.evaluate();
-
- GVArray_Typed<float3> positions = src_component.attribute_get_for_read<float3>(
+ VArray<float3> positions = src_component.attribute_get_for_read<float3>(
"position", domain, {0, 0, 0});
const InstancesComponent *src_instances = instance.get_component_for_read<InstancesComponent>();
@@ -96,7 +104,7 @@ static void add_instances_from_component(InstancesComponent &dst_component,
Array<int> handle_mapping;
/* Only fill #handle_mapping when it may be used below. */
if (src_instances != nullptr &&
- (!pick_instance->is_single() || pick_instance->get_internal_single())) {
+ (!pick_instance.is_single() || pick_instance.get_internal_single())) {
Span<InstanceReference> src_references = src_instances->references();
handle_mapping.reinitialize(src_references.size());
for (const int src_instance_handle : src_references.index_range()) {
@@ -110,23 +118,24 @@ static void add_instances_from_component(InstancesComponent &dst_component,
/* Add this reference last, because it is the most likely one to be removed later on. */
const int empty_reference_handle = dst_component.add_reference(InstanceReference());
- threading::parallel_for(IndexRange(domain_size), 1024, [&](IndexRange range) {
- for (const int i : range) {
+ threading::parallel_for(selection.index_range(), 1024, [&](IndexRange selection_range) {
+ for (const int range_i : selection_range) {
+ const int64_t i = selection[range_i];
+
/* Compute base transform for every instances. */
- float4x4 &dst_transform = dst_transforms[i];
- dst_transform = float4x4::from_loc_eul_scale(
- positions[i], rotations->get(i), scales->get(i));
+ float4x4 &dst_transform = dst_transforms[range_i];
+ dst_transform = float4x4::from_loc_eul_scale(positions[i], rotations[i], scales[i]);
/* Reference that will be used by this new instance. */
int dst_handle = empty_reference_handle;
- const bool use_individual_instance = pick_instance->get(i);
+ const bool use_individual_instance = pick_instance[i];
if (use_individual_instance) {
if (src_instances != nullptr) {
const int src_instances_amount = src_instances->instances_amount();
- const int original_index = indices->get(i);
- /* Use #mod_i instead of `%` to get the desirable wrap around behavior where -1 refers to
- * the last element. */
+ const int original_index = indices[i];
+ /* Use #mod_i instead of `%` to get the desirable wrap around behavior where -1
+ * refers to the last element. */
const int index = mod_i(original_index, std::max(src_instances_amount, 1));
if (index < src_instances_amount) {
/* Get the reference to the source instance. */
@@ -144,12 +153,12 @@ static void add_instances_from_component(InstancesComponent &dst_component,
dst_handle = full_instance_handle;
}
/* Set properties of new instance. */
- dst_handles[i] = dst_handle;
+ dst_handles[range_i] = dst_handle;
}
});
- if (pick_instance->is_single()) {
- if (pick_instance->get_internal_single()) {
+ if (pick_instance.is_single()) {
+ if (pick_instance.get_internal_single()) {
if (instance.has_realized_data()) {
params.error_message_add(
NodeWarningType::Info,
@@ -157,9 +166,40 @@ static void add_instances_from_component(InstancesComponent &dst_component,
}
}
}
+
+ bke::CustomDataAttributes &instance_attributes = dst_component.attributes();
+ for (const auto item : attributes_to_propagate.items()) {
+ const AttributeIDRef &attribute_id = item.key;
+ const AttributeKind attribute_kind = item.value;
+
+ const GVArray src_attribute = src_component.attribute_get_for_read(
+ attribute_id, ATTR_DOMAIN_POINT, attribute_kind.data_type);
+ BLI_assert(src_attribute);
+ std::optional<GMutableSpan> dst_attribute_opt = instance_attributes.get_for_write(
+ attribute_id);
+ if (!dst_attribute_opt) {
+ if (!instance_attributes.create(attribute_id, attribute_kind.data_type)) {
+ continue;
+ }
+ dst_attribute_opt = instance_attributes.get_for_write(attribute_id);
+ }
+ BLI_assert(dst_attribute_opt);
+ const GMutableSpan dst_attribute = dst_attribute_opt->slice(start_len, select_len);
+ threading::parallel_for(selection.index_range(), 1024, [&](IndexRange selection_range) {
+ attribute_math::convert_to_static_type(attribute_kind.data_type, [&](auto dummy) {
+ using T = decltype(dummy);
+ VArray<T> src = src_attribute.typed<T>();
+ MutableSpan<T> dst = dst_attribute.typed<T>();
+ for (const int range_i : selection_range) {
+ const int i = selection[range_i];
+ dst[range_i] = src[i];
+ }
+ });
+ });
+ }
}
-static void geo_node_instance_on_points_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Points");
GeometrySet instance = params.get_input<GeometrySet>("Instance");
@@ -168,40 +208,59 @@ static void geo_node_instance_on_points_exec(GeoNodeExecParams params)
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>();
+ Map<AttributeIDRef, AttributeKind> attributes_to_propagate;
+ geometry_set.gather_attributes_for_propagation(
+ {GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE},
+ GEO_COMPONENT_TYPE_INSTANCES,
+ false,
+ attributes_to_propagate);
+ attributes_to_propagate.remove("position");
+
if (geometry_set.has<MeshComponent>()) {
- add_instances_from_component(
- instances, *geometry_set.get_component_for_read<MeshComponent>(), instance, params);
- geometry_set.remove(GEO_COMPONENT_TYPE_MESH);
+ add_instances_from_component(instances,
+ *geometry_set.get_component_for_read<MeshComponent>(),
+ instance,
+ params,
+ attributes_to_propagate);
}
if (geometry_set.has<PointCloudComponent>()) {
add_instances_from_component(instances,
*geometry_set.get_component_for_read<PointCloudComponent>(),
instance,
- params);
- geometry_set.remove(GEO_COMPONENT_TYPE_POINT_CLOUD);
+ params,
+ attributes_to_propagate);
}
if (geometry_set.has<CurveComponent>()) {
- add_instances_from_component(
- instances, *geometry_set.get_component_for_read<CurveComponent>(), instance, params);
- geometry_set.remove(GEO_COMPONENT_TYPE_CURVE);
+ add_instances_from_component(instances,
+ *geometry_set.get_component_for_read<CurveComponent>(),
+ instance,
+ params,
+ attributes_to_propagate);
}
- /* Unused references may have been added above. Remove those now so that other nodes don't
- * process them needlessly. */
- instances.remove_unused_references();
+ geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
});
+ /* Unused references may have been added above. Remove those now so that other nodes don't
+ * process them needlessly.
+ * This should eventually be moved into the loop above, but currently this is quite tricky
+ * because it might remove references that the loop still wants to iterate over. */
+ InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>();
+ instances.remove_unused_references();
+
params.set_output("Instances", std::move(geometry_set));
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_instance_on_points_cc
void register_node_type_geo_instance_on_points()
{
+ namespace file_ns = blender::nodes::node_geo_instance_on_points_cc;
+
static bNodeType ntype;
geo_node_type_base(
- &ntype, GEO_NODE_INSTANCE_ON_POINTS, "Instance on Points", NODE_CLASS_GEOMETRY, 0);
- ntype.declare = blender::nodes::geo_node_instance_on_points_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_instance_on_points_exec;
+ &ntype, GEO_NODE_INSTANCE_ON_POINTS, "Instance on Points", NODE_CLASS_GEOMETRY);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc
new file mode 100644
index 00000000000..f9beed956bb
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc
@@ -0,0 +1,137 @@
+/*
+ * 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 "DNA_pointcloud_types.h"
+
+#include "BKE_attribute_math.hh"
+#include "BKE_pointcloud.h"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_instances_to_points_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>(N_("Instances")).only_instances();
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field();
+ b.add_input<decl::Vector>(N_("Position")).implicit_field();
+ b.add_input<decl::Float>(N_("Radius"))
+ .default_value(0.05f)
+ .min(0.0f)
+ .subtype(PROP_DISTANCE)
+ .supports_field();
+ b.add_output<decl::Geometry>(N_("Points"));
+}
+
+template<typename T>
+static void copy_attribute_to_points(const VArray<T> &src,
+ const IndexMask mask,
+ MutableSpan<T> dst)
+{
+ for (const int i : mask.index_range()) {
+ dst[i] = src[mask[i]];
+ }
+}
+
+static void convert_instances_to_points(GeometrySet &geometry_set,
+ Field<float3> position_field,
+ Field<float> radius_field,
+ const Field<bool> selection_field)
+{
+ const InstancesComponent &instances = *geometry_set.get_component_for_read<InstancesComponent>();
+
+ GeometryComponentFieldContext field_context{instances, ATTR_DOMAIN_INSTANCE};
+ const int domain_size = instances.attribute_domain_size(ATTR_DOMAIN_INSTANCE);
+
+ fn::FieldEvaluator evaluator{field_context, domain_size};
+ evaluator.set_selection(std::move(selection_field));
+ evaluator.add(std::move(position_field));
+ evaluator.add(std::move(radius_field));
+ evaluator.evaluate();
+ const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
+ if (selection.is_empty()) {
+ return;
+ }
+
+ PointCloud *pointcloud = BKE_pointcloud_new_nomain(selection.size());
+ geometry_set.replace_pointcloud(pointcloud);
+
+ PointCloudComponent &points = geometry_set.get_component_for_write<PointCloudComponent>();
+
+ const VArray<float3> &positions = evaluator.get_evaluated<float3>(0);
+ copy_attribute_to_points(positions, selection, {(float3 *)pointcloud->co, pointcloud->totpoint});
+ const VArray<float> &radii = evaluator.get_evaluated<float>(1);
+ copy_attribute_to_points(radii, selection, {pointcloud->radius, pointcloud->totpoint});
+
+ Map<AttributeIDRef, AttributeKind> attributes_to_propagate;
+ geometry_set.gather_attributes_for_propagation({GEO_COMPONENT_TYPE_INSTANCES},
+ GEO_COMPONENT_TYPE_POINT_CLOUD,
+ false,
+ attributes_to_propagate);
+ /* These two attributes are added by the implicit inputs above. */
+ attributes_to_propagate.remove("position");
+ attributes_to_propagate.remove("radius");
+
+ for (const auto item : attributes_to_propagate.items()) {
+ const AttributeIDRef &attribute_id = item.key;
+ const AttributeKind attribute_kind = item.value;
+
+ const GVArray src = instances.attribute_get_for_read(
+ attribute_id, ATTR_DOMAIN_INSTANCE, attribute_kind.data_type);
+ BLI_assert(src);
+ OutputAttribute dst = points.attribute_try_get_for_output_only(
+ attribute_id, ATTR_DOMAIN_POINT, attribute_kind.data_type);
+ BLI_assert(dst);
+
+ attribute_math::convert_to_static_type(attribute_kind.data_type, [&](auto dummy) {
+ using T = decltype(dummy);
+ copy_attribute_to_points(src.typed<T>(), selection, dst.as_span().typed<T>());
+ });
+ dst.save();
+ }
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Instances");
+
+ if (geometry_set.has_instances()) {
+ convert_instances_to_points(geometry_set,
+ params.extract_input<Field<float3>>("Position"),
+ params.extract_input<Field<float>>("Radius"),
+ params.extract_input<Field<bool>>("Selection"));
+ geometry_set.keep_only({GEO_COMPONENT_TYPE_POINT_CLOUD});
+ params.set_output("Points", std::move(geometry_set));
+ }
+ else {
+ params.set_default_remaining_outputs();
+ }
+}
+
+} // namespace blender::nodes::node_geo_instances_to_points_cc
+
+void register_node_type_geo_instances_to_points()
+{
+ namespace file_ns = blender::nodes::node_geo_instances_to_points_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_INSTANCES_TO_POINTS, "Instances to Points", NODE_CLASS_GEOMETRY);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_is_viewport.cc b/source/blender/nodes/geometry/nodes/node_geo_is_viewport.cc
index f8a1c764f61..c97bbad4665 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_is_viewport.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_is_viewport.cc
@@ -18,14 +18,14 @@
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_is_viewport_cc {
-static void geo_node_is_viewport_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_output<decl::Bool>("Is Viewport");
+ b.add_output<decl::Bool>(N_("Is Viewport"));
}
-static void geo_node_is_viewport_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
const Depsgraph *depsgraph = params.depsgraph();
const eEvaluationMode mode = DEG_get_mode(depsgraph);
@@ -34,14 +34,16 @@ static void geo_node_is_viewport_exec(GeoNodeExecParams params)
params.set_output("Is Viewport", is_viewport);
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_is_viewport_cc
void register_node_type_geo_is_viewport()
{
+ namespace file_ns = blender::nodes::node_geo_is_viewport_cc;
+
static bNodeType ntype;
- geo_node_type_base(&ntype, GEO_NODE_IS_VIEWPORT, "Is Viewport", NODE_CLASS_INPUT, 0);
- ntype.geometry_node_execute = blender::nodes::geo_node_is_viewport_exec;
- ntype.declare = blender::nodes::geo_node_is_viewport_declare;
+ geo_node_type_base(&ntype, GEO_NODE_IS_VIEWPORT, "Is Viewport", NODE_CLASS_INPUT);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.declare = file_ns::node_declare;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
index 93643298f92..1e521af6b13 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
@@ -19,127 +19,21 @@
#include "BKE_mesh_runtime.h"
#include "BKE_pointcloud.h"
#include "BKE_spline.hh"
+#include "BKE_type_conversions.hh"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
-#include "NOD_type_conversions.hh"
+#include "GEO_realize_instances.hh"
#include "node_geometry_util.hh"
-using blender::fn::GVArray_For_GSpan;
+namespace blender::nodes::node_geo_join_geometry_cc {
-namespace blender::nodes {
-
-static void geo_node_join_geometry_declare(NodeDeclarationBuilder &b)
-{
- b.add_input<decl::Geometry>("Geometry").multi_input();
- b.add_output<decl::Geometry>("Geometry");
-}
-
-static Mesh *join_mesh_topology_and_builtin_attributes(Span<const MeshComponent *> src_components)
+static void node_declare(NodeDeclarationBuilder &b)
{
- int totverts = 0;
- int totloops = 0;
- int totedges = 0;
- int totpolys = 0;
-
- int64_t cd_dirty_vert = 0;
- int64_t cd_dirty_poly = 0;
- int64_t cd_dirty_edge = 0;
- int64_t cd_dirty_loop = 0;
-
- VectorSet<Material *> materials;
-
- for (const MeshComponent *mesh_component : src_components) {
- const Mesh *mesh = mesh_component->get_for_read();
- totverts += mesh->totvert;
- totloops += mesh->totloop;
- totedges += mesh->totedge;
- totpolys += mesh->totpoly;
- cd_dirty_vert |= mesh->runtime.cd_dirty_vert;
- cd_dirty_poly |= mesh->runtime.cd_dirty_poly;
- cd_dirty_edge |= mesh->runtime.cd_dirty_edge;
- cd_dirty_loop |= mesh->runtime.cd_dirty_loop;
-
- for (const int slot_index : IndexRange(mesh->totcol)) {
- Material *material = mesh->mat[slot_index];
- materials.add(material);
- }
- }
-
- const Mesh *first_input_mesh = src_components[0]->get_for_read();
- Mesh *new_mesh = BKE_mesh_new_nomain(totverts, totedges, 0, totloops, totpolys);
- BKE_mesh_copy_parameters_for_eval(new_mesh, first_input_mesh);
-
- for (const int i : IndexRange(materials.size())) {
- Material *material = materials[i];
- BKE_id_material_eval_assign(&new_mesh->id, i + 1, material);
- }
-
- new_mesh->runtime.cd_dirty_vert = cd_dirty_vert;
- new_mesh->runtime.cd_dirty_poly = cd_dirty_poly;
- new_mesh->runtime.cd_dirty_edge = cd_dirty_edge;
- new_mesh->runtime.cd_dirty_loop = cd_dirty_loop;
-
- int vert_offset = 0;
- int loop_offset = 0;
- int edge_offset = 0;
- int poly_offset = 0;
- for (const MeshComponent *mesh_component : src_components) {
- const Mesh *mesh = mesh_component->get_for_read();
- if (mesh == nullptr) {
- continue;
- }
-
- Array<int> material_index_map(mesh->totcol);
- for (const int i : IndexRange(mesh->totcol)) {
- Material *material = mesh->mat[i];
- const int new_material_index = materials.index_of(material);
- material_index_map[i] = new_material_index;
- }
-
- for (const int i : IndexRange(mesh->totvert)) {
- const MVert &old_vert = mesh->mvert[i];
- MVert &new_vert = new_mesh->mvert[vert_offset + i];
- new_vert = old_vert;
- }
-
- for (const int i : IndexRange(mesh->totedge)) {
- const MEdge &old_edge = mesh->medge[i];
- MEdge &new_edge = new_mesh->medge[edge_offset + i];
- new_edge = old_edge;
- new_edge.v1 += vert_offset;
- new_edge.v2 += vert_offset;
- }
- for (const int i : IndexRange(mesh->totloop)) {
- const MLoop &old_loop = mesh->mloop[i];
- MLoop &new_loop = new_mesh->mloop[loop_offset + i];
- new_loop = old_loop;
- new_loop.v += vert_offset;
- new_loop.e += edge_offset;
- }
- for (const int i : IndexRange(mesh->totpoly)) {
- const MPoly &old_poly = mesh->mpoly[i];
- MPoly &new_poly = new_mesh->mpoly[poly_offset + i];
- new_poly = old_poly;
- new_poly.loopstart += loop_offset;
- if (old_poly.mat_nr >= 0 && old_poly.mat_nr < mesh->totcol) {
- new_poly.mat_nr = material_index_map[new_poly.mat_nr];
- }
- else {
- /* The material index was invalid before. */
- new_poly.mat_nr = 0;
- }
- }
-
- vert_offset += mesh->totvert;
- loop_offset += mesh->totloop;
- edge_offset += mesh->totedge;
- poly_offset += mesh->totpoly;
- }
-
- return new_mesh;
+ b.add_input<decl::Geometry>(N_("Geometry")).multi_input();
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
template<typename Component>
@@ -190,10 +84,10 @@ static void fill_new_attribute(Span<const GeometryComponent *> src_components,
if (domain_size == 0) {
continue;
}
- GVArrayPtr read_attribute = component->attribute_get_for_read(
+ GVArray read_attribute = component->attribute_get_for_read(
attribute_id, domain, data_type, nullptr);
- GVArray_GSpan src_span{*read_attribute};
+ GVArray_GSpan src_span{read_attribute};
const void *src_buffer = src_span.data();
void *dst_buffer = dst_span[offset];
cpp_type->copy_assign_n(src_buffer, dst_buffer, domain_size);
@@ -209,7 +103,7 @@ static void join_attributes(Span<const GeometryComponent *> src_components,
const Map<AttributeIDRef, AttributeMetaData> info = get_final_attribute_info(src_components,
ignored_attributes);
- for (const Map<AttributeIDRef, AttributeMetaData>::Item &item : info.items()) {
+ for (const Map<AttributeIDRef, AttributeMetaData>::Item item : info.items()) {
const AttributeIDRef attribute_id = item.key;
const AttributeMetaData &meta_data = item.value;
@@ -225,33 +119,6 @@ static void join_attributes(Span<const GeometryComponent *> src_components,
}
}
-static void join_components(Span<const MeshComponent *> src_components, GeometrySet &result)
-{
- Mesh *new_mesh = join_mesh_topology_and_builtin_attributes(src_components);
-
- MeshComponent &dst_component = result.get_component_for_write<MeshComponent>();
- dst_component.replace(new_mesh);
-
- /* Don't copy attributes that are stored directly in the mesh data structs. */
- join_attributes(to_base_components(src_components),
- dst_component,
- {"position", "material_index", "normal", "shade_smooth", "crease"});
-}
-
-static void join_components(Span<const PointCloudComponent *> src_components, GeometrySet &result)
-{
- int totpoints = 0;
- for (const PointCloudComponent *pointcloud_component : src_components) {
- totpoints += pointcloud_component->attribute_domain_size(ATTR_DOMAIN_POINT);
- }
-
- PointCloudComponent &dst_component = result.get_component_for_write<PointCloudComponent>();
- PointCloud *pointcloud = BKE_pointcloud_new_nomain(totpoints);
- dst_component.replace(pointcloud);
-
- join_attributes(to_base_components(src_components), dst_component);
-}
-
static void join_components(Span<const InstancesComponent *> src_components, GeometrySet &result)
{
InstancesComponent &dst_component = result.get_component_for_write<InstancesComponent>();
@@ -270,17 +137,16 @@ static void join_components(Span<const InstancesComponent *> src_components, Geo
}
Span<float4x4> src_transforms = src_component->instance_transforms();
- Span<int> src_ids = src_component->instance_ids();
Span<int> src_reference_handles = src_component->instance_reference_handles();
for (const int i : src_transforms.index_range()) {
const int src_handle = src_reference_handles[i];
const int dst_handle = handle_map[src_handle];
const float4x4 &transform = src_transforms[i];
- const int id = src_ids[i];
- dst_component.add_instance(dst_handle, transform, id);
+ dst_component.add_instance(dst_handle, transform);
}
}
+ join_attributes(to_base_components(src_components), dst_component, {"position"});
}
static void join_components(Span<const VolumeComponent *> src_components, GeometrySet &result)
@@ -291,169 +157,6 @@ static void join_components(Span<const VolumeComponent *> src_components, Geomet
UNUSED_VARS(src_components, dst_component);
}
-/**
- * \note This takes advantage of the fact that creating attributes on joined curves never
- * changes a point attribute into a spline attribute; it is always the other way around.
- */
-static void ensure_control_point_attribute(const AttributeIDRef &attribute_id,
- const CustomDataType data_type,
- Span<CurveComponent *> src_components,
- CurveEval &result)
-{
- MutableSpan<SplinePtr> splines = result.splines();
- const CPPType &type = *bke::custom_data_type_to_cpp_type(data_type);
-
- /* In order to fill point attributes with spline domain attribute values where necessary, keep
- * track of the curve each spline came from while iterating over the splines in the result. */
- int src_component_index = 0;
- int spline_index_in_component = 0;
- const CurveEval *current_curve = src_components[src_component_index]->get_for_read();
-
- for (SplinePtr &spline : splines) {
- std::optional<GSpan> attribute = spline->attributes.get_for_read(attribute_id);
-
- if (attribute) {
- if (attribute->type() != type) {
- /* In this case, the attribute exists, but it has the wrong type. So create a buffer
- * for the converted values, do the conversion, and then replace the attribute. */
- void *converted_buffer = MEM_mallocN_aligned(
- spline->size() * type.size(), type.alignment(), __func__);
-
- const DataTypeConversions &conversions = blender::nodes::get_implicit_type_conversions();
- conversions.try_convert(std::make_unique<GVArray_For_GSpan>(*attribute), type)
- ->materialize(converted_buffer);
-
- spline->attributes.remove(attribute_id);
- spline->attributes.create_by_move(attribute_id, data_type, converted_buffer);
- }
- }
- else {
- spline->attributes.create(attribute_id, data_type);
-
- if (current_curve->attributes.get_for_read(attribute_id)) {
- /* In this case the attribute did not exist, but there is a spline domain attribute
- * we can retrieve a value from, as a spline to point domain conversion. So fill the
- * new attribute with the value for this spline. */
- GVArrayPtr current_curve_attribute = current_curve->attributes.get_for_read(
- attribute_id, data_type, nullptr);
-
- BLI_assert(spline->attributes.get_for_read(attribute_id));
- std::optional<GMutableSpan> new_attribute = spline->attributes.get_for_write(attribute_id);
-
- BUFFER_FOR_CPP_TYPE_VALUE(type, buffer);
- current_curve_attribute->get(spline_index_in_component, buffer);
- type.fill_assign_n(buffer, new_attribute->data(), new_attribute->size());
- }
- }
-
- /* Move to the next spline and maybe the next input component. */
- spline_index_in_component++;
- if (spline != splines.last() && spline_index_in_component >= current_curve->splines().size()) {
- src_component_index++;
- spline_index_in_component = 0;
-
- current_curve = src_components[src_component_index]->get_for_read();
- }
- }
-}
-
-/**
- * Fill data for an attribute on the new curve based on all source curves.
- */
-static void ensure_spline_attribute(const AttributeIDRef &attribute_id,
- const CustomDataType data_type,
- Span<CurveComponent *> src_components,
- CurveEval &result)
-{
- const CPPType &type = *bke::custom_data_type_to_cpp_type(data_type);
-
- result.attributes.create(attribute_id, data_type);
- GMutableSpan result_attribute = *result.attributes.get_for_write(attribute_id);
-
- int offset = 0;
- for (const CurveComponent *component : src_components) {
- const CurveEval &curve = *component->get_for_read();
- const int size = curve.splines().size();
- if (size == 0) {
- continue;
- }
- GVArrayPtr read_attribute = curve.attributes.get_for_read(attribute_id, data_type, nullptr);
- GVArray_GSpan src_span{*read_attribute};
-
- const void *src_buffer = src_span.data();
- type.copy_assign_n(src_buffer, result_attribute[offset], size);
-
- offset += size;
- }
-}
-
-/**
- * Special handling for copying spline attributes. This is necessary because we move the splines
- * out of the source components instead of copying them, meaning we can no longer access point
- * domain attributes on the source components.
- *
- * \warning Splines have been moved out of the source components at this point, so it
- * is important to only read curve-level data (spline domain attributes) from them.
- */
-static void join_curve_attributes(const Map<AttributeIDRef, AttributeMetaData> &info,
- Span<CurveComponent *> src_components,
- CurveEval &result)
-{
- for (const Map<AttributeIDRef, AttributeMetaData>::Item &item : info.items()) {
- const AttributeIDRef attribute_id = item.key;
- const AttributeMetaData meta_data = item.value;
-
- if (meta_data.domain == ATTR_DOMAIN_CURVE) {
- ensure_spline_attribute(attribute_id, meta_data.data_type, src_components, result);
- }
- else {
- ensure_control_point_attribute(attribute_id, meta_data.data_type, src_components, result);
- }
- }
-}
-
-static void join_curve_components(MutableSpan<GeometrySet> src_geometry_sets, GeometrySet &result)
-{
- Vector<CurveComponent *> src_components;
- for (GeometrySet &geometry_set : src_geometry_sets) {
- if (geometry_set.has_curve()) {
- /* Retrieving with write access seems counterintuitive, but it can allow avoiding a copy
- * in the case where the input spline has no other users, because the splines can be
- * moved from the source curve rather than copied from a read-only source. Retrieving
- * the curve for write will make a copy only when it has a user elsewhere. */
- CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>();
- src_components.append(&component);
- }
- }
-
- if (src_components.size() == 0) {
- return;
- }
- if (src_components.size() == 1) {
- result.add(*src_components[0]);
- return;
- }
-
- /* Retrieve attribute info before moving the splines out of the input components. */
- const Map<AttributeIDRef, AttributeMetaData> info = get_final_attribute_info(
- {(const GeometryComponent **)src_components.data(), src_components.size()},
- {"position", "radius", "tilt", "cyclic", "resolution"});
-
- CurveComponent &dst_component = result.get_component_for_write<CurveComponent>();
- CurveEval *dst_curve = new CurveEval();
- for (CurveComponent *component : src_components) {
- CurveEval *src_curve = component->get_for_write();
- for (SplinePtr &spline : src_curve->splines()) {
- dst_curve->add_spline(std::move(spline));
- }
- }
- dst_curve->attributes.reallocate(dst_curve->splines().size());
-
- join_curve_attributes(info, src_components, *dst_curve);
-
- dst_component.replace(dst_curve);
-}
-
template<typename Component>
static void join_component_type(Span<GeometrySet> src_geometry_sets, GeometrySet &result)
{
@@ -472,10 +175,31 @@ static void join_component_type(Span<GeometrySet> src_geometry_sets, GeometrySet
result.add(*components[0]);
return;
}
- join_components(components, result);
+
+ GeometrySet instances_geometry_set;
+ InstancesComponent &instances =
+ instances_geometry_set.get_component_for_write<InstancesComponent>();
+
+ if constexpr (is_same_any_v<Component, InstancesComponent, VolumeComponent>) {
+ join_components(components, result);
+ }
+ else {
+ for (const Component *component : components) {
+ GeometrySet tmp_geo;
+ tmp_geo.add(*component);
+ const int handle = instances.add_reference(InstanceReference{tmp_geo});
+ instances.add_instance(handle, float4x4::identity());
+ }
+
+ geometry::RealizeInstancesOptions options;
+ options.keep_original_ids = true;
+ options.realize_instance_attributes = false;
+ GeometrySet joined_components = geometry::realize_instances(instances_geometry_set, options);
+ result.add(joined_components.get_component_for_write<Component>());
+ }
}
-static void geo_node_join_geometry_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
Vector<GeometrySet> geometry_sets = params.extract_multi_input<GeometrySet>("Geometry");
@@ -484,18 +208,20 @@ static void geo_node_join_geometry_exec(GeoNodeExecParams params)
join_component_type<PointCloudComponent>(geometry_sets, geometry_set_result);
join_component_type<InstancesComponent>(geometry_sets, geometry_set_result);
join_component_type<VolumeComponent>(geometry_sets, geometry_set_result);
- join_curve_components(geometry_sets, geometry_set_result);
+ join_component_type<CurveComponent>(geometry_sets, geometry_set_result);
params.set_output("Geometry", std::move(geometry_set_result));
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_join_geometry_cc
void register_node_type_geo_join_geometry()
{
+ namespace file_ns = blender::nodes::node_geo_join_geometry_cc;
+
static bNodeType ntype;
- geo_node_type_base(&ntype, GEO_NODE_JOIN_GEOMETRY, "Join Geometry", NODE_CLASS_GEOMETRY, 0);
- ntype.geometry_node_execute = blender::nodes::geo_node_join_geometry_exec;
- ntype.declare = blender::nodes::geo_node_join_geometry_declare;
+ geo_node_type_base(&ntype, GEO_NODE_JOIN_GEOMETRY, "Join Geometry", NODE_CLASS_GEOMETRY);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.declare = file_ns::node_declare;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc b/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc
deleted file mode 100644
index 780994996ae..00000000000
--- a/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * 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 "UI_interface.h"
-#include "UI_resources.h"
-
-#include "DNA_mesh_types.h"
-#include "DNA_meshdata_types.h"
-
-#include "BKE_material.h"
-
-namespace blender::nodes {
-
-static void geo_node_material_assign_declare(NodeDeclarationBuilder &b)
-{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Material>("Material").hide_label();
- b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field();
- b.add_output<decl::Geometry>("Geometry");
-}
-
-static void assign_material_to_faces(Mesh &mesh, const IndexMask selection, Material *material)
-{
- int new_material_index = -1;
- for (const int i : IndexRange(mesh.totcol)) {
- Material *other_material = mesh.mat[i];
- if (other_material == material) {
- new_material_index = i;
- break;
- }
- }
- if (new_material_index == -1) {
- /* Append a new material index. */
- new_material_index = mesh.totcol;
- BKE_id_material_eval_assign(&mesh.id, new_material_index + 1, material);
- }
-
- mesh.mpoly = (MPoly *)CustomData_duplicate_referenced_layer(&mesh.pdata, CD_MPOLY, mesh.totpoly);
- for (const int i : selection) {
- MPoly &poly = mesh.mpoly[i];
- poly.mat_nr = new_material_index;
- }
-}
-
-static void geo_node_material_assign_exec(GeoNodeExecParams params)
-{
- Material *material = params.extract_input<Material *>("Material");
- const Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
-
- GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
-
- geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
- if (geometry_set.has<MeshComponent>()) {
- MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
- Mesh *mesh = mesh_component.get_for_write();
- if (mesh != nullptr) {
- GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_FACE};
-
- fn::FieldEvaluator selection_evaluator{field_context, mesh->totpoly};
- selection_evaluator.add(selection_field);
- selection_evaluator.evaluate();
- const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0);
-
- assign_material_to_faces(*mesh, selection, material);
- }
- }
- });
-
- params.set_output("Geometry", std::move(geometry_set));
-}
-
-} // namespace blender::nodes
-
-void register_node_type_geo_material_assign()
-{
- static bNodeType ntype;
-
- geo_node_type_base(&ntype, GEO_NODE_MATERIAL_ASSIGN, "Material Assign", NODE_CLASS_GEOMETRY, 0);
- ntype.declare = blender::nodes::geo_node_material_assign_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_material_assign_exec;
- nodeRegisterType(&ntype);
-}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc b/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc
index a917434fa00..0309121db74 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc
@@ -24,17 +24,17 @@
#include "BKE_material.h"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_material_replace_cc {
-static void geo_node_material_replace_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Material>("Old");
- b.add_input<decl::Material>("New");
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry")).supported_type(GEO_COMPONENT_TYPE_MESH);
+ b.add_input<decl::Material>(N_("Old"));
+ b.add_input<decl::Material>(N_("New"));
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
-static void geo_node_material_replace_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
Material *old_material = params.extract_input<Material *>("Old");
Material *new_material = params.extract_input<Material *>("New");
@@ -55,15 +55,16 @@ static void geo_node_material_replace_exec(GeoNodeExecParams params)
params.set_output("Geometry", std::move(geometry_set));
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_material_replace_cc
void register_node_type_geo_material_replace()
{
+ namespace file_ns = blender::nodes::node_geo_material_replace_cc;
+
static bNodeType ntype;
- geo_node_type_base(
- &ntype, GEO_NODE_MATERIAL_REPLACE, "Material Replace", NODE_CLASS_GEOMETRY, 0);
- ntype.declare = blender::nodes::geo_node_material_replace_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_material_replace_exec;
+ geo_node_type_base(&ntype, GEO_NODE_REPLACE_MATERIAL, "Replace Material", NODE_CLASS_GEOMETRY);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc
index 337bd88c6e6..0b5f0bf34c5 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc
@@ -26,12 +26,12 @@
#include "BKE_material.h"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_material_selection_cc {
-static void geo_node_material_selection_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Material>("Material").hide_label(true);
- b.add_output<decl::Bool>("Selection").field_source();
+ b.add_input<decl::Material>(N_("Material")).hide_label(true);
+ b.add_output<decl::Bool>(N_("Selection")).field_source();
}
static void select_mesh_by_material(const Mesh &mesh,
@@ -54,78 +54,76 @@ static void select_mesh_by_material(const Mesh &mesh,
});
}
-class MaterialSelectionFieldInput final : public fn::FieldInput {
+class MaterialSelectionFieldInput final : public GeometryFieldInput {
Material *material_;
public:
MaterialSelectionFieldInput(Material *material)
- : fn::FieldInput(CPPType::get<bool>(), "Material Selection"), material_(material)
+ : GeometryFieldInput(CPPType::get<bool>(), "Material Selection node"), material_(material)
{
+ category_ = Category::Generated;
}
- const GVArray *get_varray_for_context(const fn::FieldContext &context,
- IndexMask mask,
- ResourceScope &scope) const final
+ GVArray get_varray_for_context(const GeometryComponent &component,
+ const AttributeDomain domain,
+ IndexMask mask) const final
{
- if (const GeometryComponentFieldContext *geometry_context =
- dynamic_cast<const GeometryComponentFieldContext *>(&context)) {
- const GeometryComponent &component = geometry_context->geometry_component();
- const AttributeDomain domain = geometry_context->domain();
- if (component.type() != GEO_COMPONENT_TYPE_MESH) {
- return nullptr;
- }
- const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
- const Mesh *mesh = mesh_component.get_for_read();
- if (mesh == nullptr) {
- return nullptr;
- }
-
- if (domain == ATTR_DOMAIN_FACE) {
- Array<bool> selection(mask.min_array_size());
- select_mesh_by_material(*mesh, material_, mask, selection);
- return &scope.construct<fn::GVArray_For_ArrayContainer<Array<bool>>>(std::move(selection));
- }
-
- Array<bool> selection(mesh->totpoly);
- select_mesh_by_material(*mesh, material_, IndexMask(mesh->totpoly), selection);
- GVArrayPtr face_selection = std::make_unique<fn::GVArray_For_ArrayContainer<Array<bool>>>(
- std::move(selection));
- GVArrayPtr final_selection = mesh_component.attribute_try_adapt_domain(
- std::move(face_selection), ATTR_DOMAIN_FACE, domain);
- return scope.add_value(std::move(final_selection)).get();
+ if (component.type() != GEO_COMPONENT_TYPE_MESH) {
+ return {};
+ }
+ const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
+ const Mesh *mesh = mesh_component.get_for_read();
+ if (mesh == nullptr) {
+ return {};
+ }
+
+ if (domain == ATTR_DOMAIN_FACE) {
+ Array<bool> selection(mask.min_array_size());
+ select_mesh_by_material(*mesh, material_, mask, selection);
+ return VArray<bool>::ForContainer(std::move(selection));
}
+ Array<bool> selection(mesh->totpoly);
+ select_mesh_by_material(*mesh, material_, IndexMask(mesh->totpoly), selection);
+ return mesh_component.attribute_try_adapt_domain<bool>(
+ VArray<bool>::ForContainer(std::move(selection)), ATTR_DOMAIN_FACE, domain);
+
return nullptr;
}
uint64_t hash() const override
{
- /* Some random constant hash. */
- return 91619626;
+ return get_default_hash(material_);
}
bool is_equal_to(const fn::FieldNode &other) const override
{
- return dynamic_cast<const MaterialSelectionFieldInput *>(&other) != nullptr;
+ if (const MaterialSelectionFieldInput *other_material_selection =
+ dynamic_cast<const MaterialSelectionFieldInput *>(&other)) {
+ return material_ == other_material_selection->material_;
+ }
+ return false;
}
};
-static void geo_node_material_selection_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
Material *material = params.extract_input<Material *>("Material");
Field<bool> material_field{std::make_shared<MaterialSelectionFieldInput>(material)};
params.set_output("Selection", std::move(material_field));
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_material_selection_cc
void register_node_type_geo_material_selection()
{
+ namespace file_ns = blender::nodes::node_geo_material_selection_cc;
+
static bNodeType ntype;
geo_node_type_base(
- &ntype, GEO_NODE_MATERIAL_SELECTION, "Material Selection", NODE_CLASS_GEOMETRY, 0);
- ntype.declare = blender::nodes::geo_node_material_selection_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_material_selection_exec;
+ &ntype, GEO_NODE_MATERIAL_SELECTION, "Material Selection", NODE_CLASS_GEOMETRY);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc b/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc
new file mode 100644
index 00000000000..deb149fd0f0
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc
@@ -0,0 +1,108 @@
+/*
+ * 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 "GEO_mesh_merge_by_distance.hh"
+#include "GEO_point_merge_by_distance.hh"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_merge_by_distance_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>(N_("Geometry"))
+ .supported_type({GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_MESH});
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field();
+ b.add_input<decl::Float>(N_("Distance")).default_value(0.1f).min(0.0f).subtype(PROP_DISTANCE);
+ b.add_output<decl::Geometry>(N_("Geometry"));
+}
+
+static PointCloud *pointcloud_merge_by_distance(const PointCloudComponent &src_points,
+ const float merge_distance,
+ const Field<bool> &selection_field)
+{
+ const int src_size = src_points.attribute_domain_size(ATTR_DOMAIN_POINT);
+ GeometryComponentFieldContext context{src_points, ATTR_DOMAIN_POINT};
+ FieldEvaluator evaluator{context, src_size};
+ evaluator.add(selection_field);
+ evaluator.evaluate();
+
+ const IndexMask selection = evaluator.get_evaluated_as_mask(0);
+ if (selection.is_empty()) {
+ return nullptr;
+ }
+
+ return geometry::point_merge_by_distance(src_points, merge_distance, selection);
+}
+
+static std::optional<Mesh *> mesh_merge_by_distance(const MeshComponent &mesh_component,
+ const float merge_distance,
+ const Field<bool> &selection_field)
+{
+ const int src_size = mesh_component.attribute_domain_size(ATTR_DOMAIN_POINT);
+ GeometryComponentFieldContext context{mesh_component, ATTR_DOMAIN_POINT};
+ FieldEvaluator evaluator{context, src_size};
+ evaluator.add(selection_field);
+ evaluator.evaluate();
+
+ const IndexMask selection = evaluator.get_evaluated_as_mask(0);
+ if (selection.is_empty()) {
+ return nullptr;
+ }
+
+ const Mesh &mesh = *mesh_component.get_for_read();
+ return geometry::mesh_merge_by_distance_all(mesh, selection, merge_distance);
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+ const Field<bool> selection = params.extract_input<Field<bool>>("Selection");
+ const float merge_distance = params.extract_input<float>("Distance");
+
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ if (geometry_set.has_pointcloud()) {
+ PointCloud *result = pointcloud_merge_by_distance(
+ *geometry_set.get_component_for_read<PointCloudComponent>(), merge_distance, selection);
+ geometry_set.replace_pointcloud(result);
+ }
+ if (geometry_set.has_mesh()) {
+ std::optional<Mesh *> result = mesh_merge_by_distance(
+ *geometry_set.get_component_for_read<MeshComponent>(), merge_distance, selection);
+ if (result) {
+ geometry_set.replace_mesh(*result);
+ }
+ }
+ });
+
+ params.set_output("Geometry", std::move(geometry_set));
+}
+
+} // namespace blender::nodes::node_geo_merge_by_distance_cc
+
+void register_node_type_geo_merge_by_distance()
+{
+ namespace file_ns = blender::nodes::node_geo_merge_by_distance_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_MERGE_BY_DISTANCE, "Merge by Distance", NODE_CLASS_GEOMETRY);
+
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc
index 9c477c639a2..b3b11b2e0e9 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc
@@ -25,28 +25,34 @@
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_mesh_primitive_circle_cc {
-static void geo_node_mesh_primitive_circle_declare(NodeDeclarationBuilder &b)
+NODE_STORAGE_FUNCS(NodeGeometryMeshCircle)
+
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Int>("Vertices").default_value(32).min(3);
- b.add_input<decl::Float>("Radius").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE);
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Int>(N_("Vertices"))
+ .default_value(32)
+ .min(3)
+ .description(N_("Number of vertices on the circle"));
+ b.add_input<decl::Float>(N_("Radius"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .subtype(PROP_DISTANCE)
+ .description(N_("Distance of the vertices from the origin"));
+ b.add_output<decl::Geometry>(N_("Mesh"));
}
-static void geo_node_mesh_primitive_circle_layout(uiLayout *layout,
- bContext *UNUSED(C),
- PointerRNA *ptr)
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
uiItemR(layout, ptr, "fill_type", 0, nullptr, ICON_NONE);
}
-static void geo_node_mesh_primitive_circle_init(bNodeTree *UNUSED(ntree), bNode *node)
+static void node_init(bNodeTree *UNUSED(ntree), bNode *node)
{
- NodeGeometryMeshCircle *node_storage = (NodeGeometryMeshCircle *)MEM_callocN(
- sizeof(NodeGeometryMeshCircle), __func__);
+ NodeGeometryMeshCircle *node_storage = MEM_cnew<NodeGeometryMeshCircle>(__func__);
node_storage->fill_type = GEO_NODE_MESH_CIRCLE_FILL_NONE;
@@ -132,12 +138,6 @@ static Mesh *create_circle_mesh(const float radius,
copy_v3_v3(verts.last().co, float3(0));
}
- /* Point all vertex normals in the up direction. */
- const short up_normal[3] = {0, 0, SHRT_MAX};
- for (MVert &vert : verts) {
- copy_v3_v3_short(vert.no, up_normal);
- }
-
/* Create outer edges. */
const short edge_flag = (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NONE) ?
ME_LOOSEEDGE :
@@ -192,42 +192,38 @@ static Mesh *create_circle_mesh(const float radius,
return mesh;
}
-static void geo_node_mesh_primitive_circle_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
- const bNode &node = params.node();
- const NodeGeometryMeshCircle &storage = *(const NodeGeometryMeshCircle *)node.storage;
-
- const GeometryNodeMeshCircleFillType fill_type = (const GeometryNodeMeshCircleFillType)
- storage.fill_type;
+ const NodeGeometryMeshCircle &storage = node_storage(params.node());
+ const GeometryNodeMeshCircleFillType fill = (GeometryNodeMeshCircleFillType)storage.fill_type;
const float radius = params.extract_input<float>("Radius");
const int verts_num = params.extract_input<int>("Vertices");
if (verts_num < 3) {
params.error_message_add(NodeWarningType::Info, TIP_("Vertices must be at least 3"));
- params.set_output("Geometry", GeometrySet());
+ params.set_default_remaining_outputs();
return;
}
- Mesh *mesh = create_circle_mesh(radius, verts_num, fill_type);
-
- BLI_assert(BKE_mesh_is_valid(mesh));
+ Mesh *mesh = create_circle_mesh(radius, verts_num, fill);
- params.set_output("Geometry", GeometrySet::create_with_mesh(mesh));
+ params.set_output("Mesh", GeometrySet::create_with_mesh(mesh));
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_mesh_primitive_circle_cc
void register_node_type_geo_mesh_primitive_circle()
{
+ namespace file_ns = blender::nodes::node_geo_mesh_primitive_circle_cc;
+
static bNodeType ntype;
- geo_node_type_base(
- &ntype, GEO_NODE_MESH_PRIMITIVE_CIRCLE, "Mesh Circle", NODE_CLASS_GEOMETRY, 0);
- node_type_init(&ntype, blender::nodes::geo_node_mesh_primitive_circle_init);
+ geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_CIRCLE, "Mesh Circle", NODE_CLASS_GEOMETRY);
+ node_type_init(&ntype, file_ns::node_init);
node_type_storage(
&ntype, "NodeGeometryMeshCircle", node_free_standard_storage, node_copy_standard_storage);
- ntype.geometry_node_execute = blender::nodes::geo_node_mesh_primitive_circle_exec;
- ntype.draw_buttons = blender::nodes::geo_node_mesh_primitive_circle_layout;
- ntype.declare = blender::nodes::geo_node_mesh_primitive_circle_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.draw_buttons = file_ns::node_layout;
+ ntype.declare = file_ns::node_declare;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc
index 0d58476fc58..e0923344421 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc
@@ -25,529 +25,797 @@
#include "node_geometry_util.hh"
+#include <cmath>
+
namespace blender::nodes {
-static void geo_node_mesh_primitive_cone_declare(NodeDeclarationBuilder &b)
-{
- b.add_input<decl::Int>("Vertices").default_value(32).min(3);
- b.add_input<decl::Float>("Radius Top").min(0.0f).subtype(PROP_DISTANCE);
- b.add_input<decl::Float>("Radius Bottom").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE);
- b.add_input<decl::Float>("Depth").default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE);
- b.add_output<decl::Geometry>("Geometry");
-}
+struct ConeConfig {
+ float radius_top;
+ float radius_bottom;
+ float height;
+ int circle_segments;
+ int side_segments;
+ int fill_segments;
+ GeometryNodeMeshCircleFillType fill_type;
+
+ bool top_is_point;
+ bool bottom_is_point;
+ /* The cone tip and a triangle fan filling are topologically identical.
+ * This simplifies the logic in some cases. */
+ bool top_has_center_vert;
+ bool bottom_has_center_vert;
+
+ /* Helpful quantities. */
+ int tot_quad_rings;
+ int tot_edge_rings;
+ int tot_verts;
+ int tot_edges;
+ int tot_corners;
+ int tot_faces;
+
+ /* Helpful vertex indices. */
+ int first_vert;
+ int first_ring_verts_start;
+ int last_ring_verts_start;
+ int last_vert;
+
+ /* Helpful edge indices. */
+ int first_ring_edges_start;
+ int last_ring_edges_start;
+ int last_fan_edges_start;
+ int last_edge;
+
+ /* Helpful face indices. */
+ int top_faces_start;
+ int top_faces_len;
+ int side_faces_start;
+ int side_faces_len;
+ int bottom_faces_start;
+ int bottom_faces_len;
+
+ ConeConfig(float radius_top,
+ float radius_bottom,
+ float depth,
+ int circle_segments,
+ int side_segments,
+ int fill_segments,
+ GeometryNodeMeshCircleFillType fill_type)
+ : radius_top(radius_top),
+ radius_bottom(radius_bottom),
+ height(0.5f * depth),
+ circle_segments(circle_segments),
+ side_segments(side_segments),
+ fill_segments(fill_segments),
+ fill_type(fill_type)
+ {
+ this->top_is_point = this->radius_top == 0.0f;
+ this->bottom_is_point = this->radius_bottom == 0.0f;
+ this->top_has_center_vert = this->top_is_point ||
+ this->fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN;
+ this->bottom_has_center_vert = this->bottom_is_point ||
+ this->fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN;
+
+ this->tot_quad_rings = this->calculate_total_quad_rings();
+ this->tot_edge_rings = this->calculate_total_edge_rings();
+ this->tot_verts = this->calculate_total_verts();
+ this->tot_edges = this->calculate_total_edges();
+ this->tot_corners = this->calculate_total_corners();
+
+ this->first_vert = 0;
+ this->first_ring_verts_start = this->top_has_center_vert ? 1 : first_vert;
+ this->last_vert = this->tot_verts - 1;
+ this->last_ring_verts_start = this->last_vert - this->circle_segments;
+
+ this->first_ring_edges_start = this->top_has_center_vert ? this->circle_segments : 0;
+ this->last_ring_edges_start = this->first_ring_edges_start +
+ this->tot_quad_rings * this->circle_segments * 2;
+ this->last_fan_edges_start = this->tot_edges - this->circle_segments;
+ this->last_edge = this->tot_edges - 1;
+
+ this->top_faces_start = 0;
+ if (!this->top_is_point) {
+ this->top_faces_len = (fill_segments - 1) * circle_segments;
+ this->top_faces_len += this->top_has_center_vert ? circle_segments : 0;
+ this->top_faces_len += this->fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON ? 1 : 0;
+ }
+ else {
+ this->top_faces_len = 0;
+ }
-static void geo_node_mesh_primitive_cone_layout(uiLayout *layout,
- bContext *UNUSED(C),
- PointerRNA *ptr)
-{
- uiLayoutSetPropSep(layout, true);
- uiLayoutSetPropDecorate(layout, false);
- uiItemR(layout, ptr, "fill_type", 0, nullptr, ICON_NONE);
-}
+ this->side_faces_start = this->top_faces_len;
+ if (this->top_is_point && this->bottom_is_point) {
+ this->side_faces_len = 0;
+ }
+ else {
+ this->side_faces_len = side_segments * circle_segments;
+ }
-static void geo_node_mesh_primitive_cone_init(bNodeTree *UNUSED(ntree), bNode *node)
-{
- NodeGeometryMeshCone *node_storage = (NodeGeometryMeshCone *)MEM_callocN(
- sizeof(NodeGeometryMeshCone), __func__);
+ if (!this->bottom_is_point) {
+ this->bottom_faces_len = (fill_segments - 1) * circle_segments;
+ this->bottom_faces_len += this->bottom_has_center_vert ? circle_segments : 0;
+ this->bottom_faces_len += this->fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON ? 1 : 0;
+ }
+ else {
+ this->bottom_faces_len = 0;
+ }
+ this->bottom_faces_start = this->side_faces_start + this->side_faces_len;
- node_storage->fill_type = GEO_NODE_MESH_CIRCLE_FILL_NGON;
+ this->tot_faces = this->top_faces_len + this->side_faces_len + this->bottom_faces_len;
+ }
- node->storage = node_storage;
-}
+ private:
+ int calculate_total_quad_rings();
+ int calculate_total_edge_rings();
+ int calculate_total_verts();
+ int calculate_total_edges();
+ int calculate_total_corners();
+};
-static int vert_total(const GeometryNodeMeshCircleFillType fill_type,
- const int verts_num,
- const bool top_is_point,
- const bool bottom_is_point)
+int ConeConfig::calculate_total_quad_rings()
{
- int vert_total = 0;
- if (!top_is_point) {
- vert_total += verts_num;
- if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
- vert_total++;
- }
+ if (top_is_point && bottom_is_point) {
+ return 0;
}
- else {
- vert_total++;
+
+ int quad_rings = 0;
+
+ if (!top_is_point) {
+ quad_rings += fill_segments - 1;
}
+
+ quad_rings += (!top_is_point && !bottom_is_point) ? side_segments : (side_segments - 1);
+
if (!bottom_is_point) {
- vert_total += verts_num;
- if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
- vert_total++;
- }
- }
- else {
- vert_total++;
+ quad_rings += fill_segments - 1;
}
- return vert_total;
+ return quad_rings;
}
-static int edge_total(const GeometryNodeMeshCircleFillType fill_type,
- const int verts_num,
- const bool top_is_point,
- const bool bottom_is_point)
+int ConeConfig::calculate_total_edge_rings()
{
if (top_is_point && bottom_is_point) {
- return 1;
+ return 0;
}
- int edge_total = 0;
+ int edge_rings = 0;
+
if (!top_is_point) {
- edge_total += verts_num;
- if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
- edge_total += verts_num;
- }
+ edge_rings += fill_segments;
}
- edge_total += verts_num;
+ edge_rings += side_segments - 1;
if (!bottom_is_point) {
- edge_total += verts_num;
- if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
- edge_total += verts_num;
- }
+ edge_rings += fill_segments;
}
- return edge_total;
+ return edge_rings;
}
-static int corner_total(const GeometryNodeMeshCircleFillType fill_type,
- const int verts_num,
- const bool top_is_point,
- const bool bottom_is_point)
+int ConeConfig::calculate_total_verts()
{
if (top_is_point && bottom_is_point) {
- return 0;
+ return side_segments + 1;
+ }
+
+ int vert_total = 0;
+
+ if (top_has_center_vert) {
+ vert_total++;
}
- int corner_total = 0;
if (!top_is_point) {
- if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
- corner_total += verts_num;
- }
- else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
- corner_total += verts_num * 3;
- }
+ vert_total += circle_segments * fill_segments;
}
- if (!top_is_point && !bottom_is_point) {
- corner_total += verts_num * 4;
+ vert_total += circle_segments * (side_segments - 1);
+
+ if (!bottom_is_point) {
+ vert_total += circle_segments * fill_segments;
}
- else {
- corner_total += verts_num * 3;
+
+ if (bottom_has_center_vert) {
+ vert_total++;
}
- if (!bottom_is_point) {
- if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
- corner_total += verts_num;
- }
- else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
- corner_total += verts_num * 3;
- }
+ return vert_total;
+}
+
+int ConeConfig::calculate_total_edges()
+{
+ if (top_is_point && bottom_is_point) {
+ return side_segments;
}
- return corner_total;
+ int edge_total = 0;
+ if (top_has_center_vert) {
+ edge_total += circle_segments;
+ }
+
+ edge_total += circle_segments * (tot_quad_rings * 2 + 1);
+
+ if (bottom_has_center_vert) {
+ edge_total += circle_segments;
+ }
+
+ return edge_total;
}
-static int face_total(const GeometryNodeMeshCircleFillType fill_type,
- const int verts_num,
- const bool top_is_point,
- const bool bottom_is_point)
+int ConeConfig::calculate_total_corners()
{
if (top_is_point && bottom_is_point) {
return 0;
}
- int face_total = 0;
- if (!top_is_point) {
- if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
- face_total++;
- }
- else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
- face_total += verts_num;
- }
+ int corner_total = 0;
+
+ if (top_has_center_vert) {
+ corner_total += (circle_segments * 3);
+ }
+ else if (!top_is_point && fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
+ corner_total += circle_segments;
}
- face_total += verts_num;
+ corner_total += tot_quad_rings * (circle_segments * 4);
- if (!bottom_is_point) {
- if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
- face_total++;
- }
- else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
- face_total += verts_num;
- }
+ if (bottom_has_center_vert) {
+ corner_total += (circle_segments * 3);
+ }
+ else if (!bottom_is_point && fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
+ corner_total += circle_segments;
}
- return face_total;
+ return corner_total;
}
-static void calculate_uvs(Mesh *mesh,
- const bool top_is_point,
- const bool bottom_is_point,
- const int verts_num,
- const GeometryNodeMeshCircleFillType fill_type)
+static void calculate_cone_vertices(const MutableSpan<MVert> &verts, const ConeConfig &config)
{
- MeshComponent mesh_component;
- mesh_component.replace(mesh, GeometryOwnershipType::Editable);
- OutputAttribute_Typed<float2> uv_attribute =
- mesh_component.attribute_try_get_for_output_only<float2>("uv_map", ATTR_DOMAIN_CORNER);
- MutableSpan<float2> uvs = uv_attribute.as_span();
-
- Array<float2> circle(verts_num);
+ Array<float2> circle(config.circle_segments);
+ const float angle_delta = 2.0f * (M_PI / static_cast<float>(config.circle_segments));
float angle = 0.0f;
- const float angle_delta = 2.0f * M_PI / static_cast<float>(verts_num);
- for (const int i : IndexRange(verts_num)) {
- circle[i].x = std::cos(angle) * 0.225f + 0.25f;
- circle[i].y = std::sin(angle) * 0.225f + 0.25f;
+ for (const int i : IndexRange(config.circle_segments)) {
+ circle[i].x = std::cos(angle);
+ circle[i].y = std::sin(angle);
angle += angle_delta;
}
- int loop_index = 0;
- if (!top_is_point) {
- if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
- for (const int i : IndexRange(verts_num)) {
- uvs[loop_index++] = circle[i];
- }
- }
- else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
- for (const int i : IndexRange(verts_num)) {
- uvs[loop_index++] = circle[i];
- uvs[loop_index++] = circle[(i + 1) % verts_num];
- uvs[loop_index++] = float2(0.25f, 0.25f);
- }
- }
- }
+ int vert_index = 0;
- /* Create side corners and faces. */
- if (!top_is_point && !bottom_is_point) {
- const float bottom = (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NONE) ? 0.0f : 0.5f;
- /* Quads connect the top and bottom. */
- for (const int i : IndexRange(verts_num)) {
- const float vert = static_cast<float>(i);
- uvs[loop_index++] = float2(vert / verts_num, bottom);
- uvs[loop_index++] = float2(vert / verts_num, 1.0f);
- uvs[loop_index++] = float2((vert + 1.0f) / verts_num, 1.0f);
- uvs[loop_index++] = float2((vert + 1.0f) / verts_num, bottom);
- }
+ /* Top cone tip or triangle fan center. */
+ if (config.top_has_center_vert) {
+ copy_v3_fl3(verts[vert_index++].co, 0.0f, 0.0f, config.height);
}
- else {
- /* Triangles connect the top and bottom section. */
- if (!top_is_point) {
- for (const int i : IndexRange(verts_num)) {
- uvs[loop_index++] = circle[i] + float2(0.5f, 0.0f);
- uvs[loop_index++] = float2(0.75f, 0.25f);
- uvs[loop_index++] = circle[(i + 1) % verts_num] + float2(0.5f, 0.0f);
- }
- }
- else {
- BLI_assert(!bottom_is_point);
- for (const int i : IndexRange(verts_num)) {
- uvs[loop_index++] = circle[i];
- uvs[loop_index++] = circle[(i + 1) % verts_num];
- uvs[loop_index++] = float2(0.25f, 0.25f);
+
+ /* Top fill including the outer edge of the fill. */
+ if (!config.top_is_point) {
+ const float top_fill_radius_delta = config.radius_top /
+ static_cast<float>(config.fill_segments);
+ for (const int i : IndexRange(config.fill_segments)) {
+ const float top_fill_radius = top_fill_radius_delta * (i + 1);
+ for (const int j : IndexRange(config.circle_segments)) {
+ const float x = circle[j].x * top_fill_radius;
+ const float y = circle[j].y * top_fill_radius;
+ copy_v3_fl3(verts[vert_index++].co, x, y, config.height);
}
}
}
- /* Create bottom corners and faces. */
- if (!bottom_is_point) {
- if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
- for (const int i : IndexRange(verts_num)) {
- /* Go backwards because of reversed face normal. */
- uvs[loop_index++] = circle[verts_num - 1 - i] + float2(0.5f, 0.0f);
- }
+ /* Rings along the side. */
+ const float side_radius_delta = (config.radius_bottom - config.radius_top) /
+ static_cast<float>(config.side_segments);
+ const float height_delta = 2.0f * config.height / static_cast<float>(config.side_segments);
+ for (const int i : IndexRange(config.side_segments - 1)) {
+ const float ring_radius = config.radius_top + (side_radius_delta * (i + 1));
+ const float ring_height = config.height - (height_delta * (i + 1));
+ for (const int j : IndexRange(config.circle_segments)) {
+ const float x = circle[j].x * ring_radius;
+ const float y = circle[j].y * ring_radius;
+ copy_v3_fl3(verts[vert_index++].co, x, y, ring_height);
}
- else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
- for (const int i : IndexRange(verts_num)) {
- uvs[loop_index++] = circle[i] + float2(0.5f, 0.0f);
- uvs[loop_index++] = float2(0.75f, 0.25f);
- uvs[loop_index++] = circle[(i + 1) % verts_num] + float2(0.5f, 0.0f);
+ }
+
+ /* Bottom fill including the outer edge of the fill. */
+ if (!config.bottom_is_point) {
+ const float bottom_fill_radius_delta = config.radius_bottom /
+ static_cast<float>(config.fill_segments);
+ for (const int i : IndexRange(config.fill_segments)) {
+ const float bottom_fill_radius = config.radius_bottom - (i * bottom_fill_radius_delta);
+ for (const int j : IndexRange(config.circle_segments)) {
+ const float x = circle[j].x * bottom_fill_radius;
+ const float y = circle[j].y * bottom_fill_radius;
+ copy_v3_fl3(verts[vert_index++].co, x, y, -config.height);
}
}
}
- uv_attribute.save();
+ /* Bottom cone tip or triangle fan center. */
+ if (config.bottom_has_center_vert) {
+ copy_v3_fl3(verts[vert_index++].co, 0.0f, 0.0f, -config.height);
+ }
}
-Mesh *create_cylinder_or_cone_mesh(const float radius_top,
- const float radius_bottom,
- const float depth,
- const int verts_num,
- const GeometryNodeMeshCircleFillType fill_type)
+static void calculate_cone_edges(const MutableSpan<MEdge> &edges, const ConeConfig &config)
{
- const bool top_is_point = radius_top == 0.0f;
- const bool bottom_is_point = radius_bottom == 0.0f;
- const float height = depth * 0.5f;
- /* Handle the case of a line / single point before everything else to avoid
- * the need to check for it later. */
- if (top_is_point && bottom_is_point) {
- const bool single_vertex = height == 0.0f;
- Mesh *mesh = BKE_mesh_new_nomain(single_vertex ? 1 : 2, single_vertex ? 0 : 1, 0, 0, 0);
- copy_v3_v3(mesh->mvert[0].co, float3(0.0f, 0.0f, height));
- if (single_vertex) {
- const short up[3] = {0, 0, SHRT_MAX};
- copy_v3_v3_short(mesh->mvert[0].no, up);
- return mesh;
- }
- copy_v3_v3(mesh->mvert[1].co, float3(0.0f, 0.0f, -height));
- mesh->medge[0].v1 = 0;
- mesh->medge[0].v2 = 1;
- mesh->medge[0].flag |= ME_LOOSEEDGE;
- BKE_mesh_normals_tag_dirty(mesh);
- return mesh;
- }
-
- Mesh *mesh = BKE_mesh_new_nomain(
- vert_total(fill_type, verts_num, top_is_point, bottom_is_point),
- edge_total(fill_type, verts_num, top_is_point, bottom_is_point),
- 0,
- corner_total(fill_type, verts_num, top_is_point, bottom_is_point),
- face_total(fill_type, verts_num, top_is_point, bottom_is_point));
- BKE_id_material_eval_ensure_default_slot(&mesh->id);
- MutableSpan<MVert> verts{mesh->mvert, mesh->totvert};
- MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop};
- MutableSpan<MEdge> edges{mesh->medge, mesh->totedge};
- MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly};
-
- /* Calculate vertex positions. */
- const int top_verts_start = 0;
- const int bottom_verts_start = top_verts_start + (!top_is_point ? verts_num : 1);
- const float angle_delta = 2.0f * (M_PI / static_cast<float>(verts_num));
- for (const int i : IndexRange(verts_num)) {
- const float angle = i * angle_delta;
- const float x = std::cos(angle);
- const float y = std::sin(angle);
- if (!top_is_point) {
- copy_v3_v3(verts[top_verts_start + i].co, float3(x * radius_top, y * radius_top, height));
- }
- if (!bottom_is_point) {
- copy_v3_v3(verts[bottom_verts_start + i].co,
- float3(x * radius_bottom, y * radius_bottom, -height));
+ int edge_index = 0;
+
+ /* Edges for top cone tip or triangle fan */
+ if (config.top_has_center_vert) {
+ for (const int i : IndexRange(config.circle_segments)) {
+ MEdge &edge = edges[edge_index++];
+ edge.v1 = config.first_vert;
+ edge.v2 = config.first_ring_verts_start + i;
+ edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
}
}
- if (top_is_point) {
- copy_v3_v3(verts[top_verts_start].co, float3(0.0f, 0.0f, height));
- }
- if (bottom_is_point) {
- copy_v3_v3(verts[bottom_verts_start].co, float3(0.0f, 0.0f, -height));
- }
- /* Add center vertices for the triangle fans at the end. */
- const int top_center_vert_index = bottom_verts_start + (bottom_is_point ? 1 : verts_num);
- const int bottom_center_vert_index = top_center_vert_index + (top_is_point ? 0 : 1);
- if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
- if (!top_is_point) {
- copy_v3_v3(verts[top_center_vert_index].co, float3(0.0f, 0.0f, height));
+ /* Rings and connecting edges between the rings. */
+ for (const int i : IndexRange(config.tot_edge_rings)) {
+ const int this_ring_vert_start = config.first_ring_verts_start + (i * config.circle_segments);
+ const int next_ring_vert_start = this_ring_vert_start + config.circle_segments;
+ /* Edge rings. */
+ for (const int j : IndexRange(config.circle_segments)) {
+ MEdge &edge = edges[edge_index++];
+ edge.v1 = this_ring_vert_start + j;
+ edge.v2 = this_ring_vert_start + ((j + 1) % config.circle_segments);
+ edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
}
- if (!bottom_is_point) {
- copy_v3_v3(verts[bottom_center_vert_index].co, float3(0.0f, 0.0f, -height));
+ if (i == config.tot_edge_rings - 1) {
+ /* There is one fewer ring of connecting edges. */
+ break;
}
- }
-
- /* Create top edges. */
- const int top_edges_start = 0;
- const int top_fan_edges_start = (!top_is_point &&
- fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) ?
- top_edges_start + verts_num :
- top_edges_start;
- if (!top_is_point) {
- for (const int i : IndexRange(verts_num)) {
- MEdge &edge = edges[top_edges_start + i];
- edge.v1 = top_verts_start + i;
- edge.v2 = top_verts_start + (i + 1) % verts_num;
+ /* Connecting edges. */
+ for (const int j : IndexRange(config.circle_segments)) {
+ MEdge &edge = edges[edge_index++];
+ edge.v1 = this_ring_vert_start + j;
+ edge.v2 = next_ring_vert_start + j;
edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
}
- if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
- for (const int i : IndexRange(verts_num)) {
- MEdge &edge = edges[top_fan_edges_start + i];
- edge.v1 = top_center_vert_index;
- edge.v2 = top_verts_start + i;
- edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
- }
- }
- }
-
- /* Create connecting edges. */
- const int connecting_edges_start = top_fan_edges_start + (!top_is_point ? verts_num : 0);
- for (const int i : IndexRange(verts_num)) {
- MEdge &edge = edges[connecting_edges_start + i];
- edge.v1 = top_verts_start + (!top_is_point ? i : 0);
- edge.v2 = bottom_verts_start + (!bottom_is_point ? i : 0);
- edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
}
- /* Create bottom edges. */
- const int bottom_edges_start = connecting_edges_start + verts_num;
- const int bottom_fan_edges_start = (!bottom_is_point &&
- fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) ?
- bottom_edges_start + verts_num :
- bottom_edges_start;
- if (!bottom_is_point) {
- for (const int i : IndexRange(verts_num)) {
- MEdge &edge = edges[bottom_edges_start + i];
- edge.v1 = bottom_verts_start + i;
- edge.v2 = bottom_verts_start + (i + 1) % verts_num;
+ /* Edges for bottom triangle fan or tip. */
+ if (config.bottom_has_center_vert) {
+ for (const int i : IndexRange(config.circle_segments)) {
+ MEdge &edge = edges[edge_index++];
+ edge.v1 = config.last_ring_verts_start + i;
+ edge.v2 = config.last_vert;
edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
}
- if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
- for (const int i : IndexRange(verts_num)) {
- MEdge &edge = edges[bottom_fan_edges_start + i];
- edge.v1 = bottom_center_vert_index;
- edge.v2 = bottom_verts_start + i;
- edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
- }
- }
}
+}
- /* Create top corners and faces. */
+static void calculate_cone_faces(const MutableSpan<MLoop> &loops,
+ const MutableSpan<MPoly> &polys,
+ const ConeConfig &config)
+{
int loop_index = 0;
int poly_index = 0;
- if (!top_is_point) {
- if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
+
+ if (config.top_has_center_vert) {
+ /* Top cone tip or center triangle fan in the fill. */
+ const int top_center_vert = 0;
+ const int top_fan_edges_start = 0;
+
+ for (const int i : IndexRange(config.circle_segments)) {
MPoly &poly = polys[poly_index++];
poly.loopstart = loop_index;
- poly.totloop = verts_num;
+ poly.totloop = 3;
- for (const int i : IndexRange(verts_num)) {
- MLoop &loop = loops[loop_index++];
- loop.v = top_verts_start + i;
- loop.e = top_edges_start + i;
- }
+ MLoop &loop_a = loops[loop_index++];
+ loop_a.v = config.first_ring_verts_start + i;
+ loop_a.e = config.first_ring_edges_start + i;
+ MLoop &loop_b = loops[loop_index++];
+ loop_b.v = config.first_ring_verts_start + ((i + 1) % config.circle_segments);
+ loop_b.e = top_fan_edges_start + ((i + 1) % config.circle_segments);
+ MLoop &loop_c = loops[loop_index++];
+ loop_c.v = top_center_vert;
+ loop_c.e = top_fan_edges_start + i;
+ }
+ }
+ else if (config.fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
+ /* Center n-gon in the fill. */
+ MPoly &poly = polys[poly_index++];
+ poly.loopstart = loop_index;
+ poly.totloop = config.circle_segments;
+ for (const int i : IndexRange(config.circle_segments)) {
+ MLoop &loop = loops[loop_index++];
+ loop.v = i;
+ loop.e = i;
}
- else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
- for (const int i : IndexRange(verts_num)) {
+ }
+
+ /* Quads connect one edge ring to the next one. */
+ if (config.tot_quad_rings > 0) {
+ for (const int i : IndexRange(config.tot_quad_rings)) {
+ const int this_ring_vert_start = config.first_ring_verts_start +
+ (i * config.circle_segments);
+ const int next_ring_vert_start = this_ring_vert_start + config.circle_segments;
+
+ const int this_ring_edges_start = config.first_ring_edges_start +
+ (i * 2 * config.circle_segments);
+ const int next_ring_edges_start = this_ring_edges_start + (2 * config.circle_segments);
+ const int ring_connections_start = this_ring_edges_start + config.circle_segments;
+
+ for (const int j : IndexRange(config.circle_segments)) {
MPoly &poly = polys[poly_index++];
poly.loopstart = loop_index;
- poly.totloop = 3;
+ poly.totloop = 4;
MLoop &loop_a = loops[loop_index++];
- loop_a.v = top_verts_start + i;
- loop_a.e = top_edges_start + i;
+ loop_a.v = this_ring_vert_start + j;
+ loop_a.e = ring_connections_start + j;
MLoop &loop_b = loops[loop_index++];
- loop_b.v = top_verts_start + (i + 1) % verts_num;
- loop_b.e = top_fan_edges_start + (i + 1) % verts_num;
+ loop_b.v = next_ring_vert_start + j;
+ loop_b.e = next_ring_edges_start + j;
MLoop &loop_c = loops[loop_index++];
- loop_c.v = top_center_vert_index;
- loop_c.e = top_fan_edges_start + i;
+ loop_c.v = next_ring_vert_start + ((j + 1) % config.circle_segments);
+ loop_c.e = ring_connections_start + ((j + 1) % config.circle_segments);
+ MLoop &loop_d = loops[loop_index++];
+ loop_d.v = this_ring_vert_start + ((j + 1) % config.circle_segments);
+ loop_d.e = this_ring_edges_start + j;
}
}
}
- /* Create side corners and faces. */
- if (!top_is_point && !bottom_is_point) {
- /* Quads connect the top and bottom. */
- for (const int i : IndexRange(verts_num)) {
+ if (config.bottom_has_center_vert) {
+ /* Bottom cone tip or center triangle fan in the fill. */
+ for (const int i : IndexRange(config.circle_segments)) {
MPoly &poly = polys[poly_index++];
poly.loopstart = loop_index;
- poly.totloop = 4;
+ poly.totloop = 3;
MLoop &loop_a = loops[loop_index++];
- loop_a.v = top_verts_start + i;
- loop_a.e = connecting_edges_start + i;
+ loop_a.v = config.last_ring_verts_start + i;
+ loop_a.e = config.last_fan_edges_start + i;
MLoop &loop_b = loops[loop_index++];
- loop_b.v = bottom_verts_start + i;
- loop_b.e = bottom_edges_start + i;
+ loop_b.v = config.last_vert;
+ loop_b.e = config.last_fan_edges_start + (i + 1) % config.circle_segments;
MLoop &loop_c = loops[loop_index++];
- loop_c.v = bottom_verts_start + (i + 1) % verts_num;
- loop_c.e = connecting_edges_start + (i + 1) % verts_num;
- MLoop &loop_d = loops[loop_index++];
- loop_d.v = top_verts_start + (i + 1) % verts_num;
- loop_d.e = top_edges_start + i;
+ loop_c.v = config.last_ring_verts_start + (i + 1) % config.circle_segments;
+ loop_c.e = config.last_ring_edges_start + i;
}
}
- else {
- /* Triangles connect the top and bottom section. */
- if (!top_is_point) {
- for (const int i : IndexRange(verts_num)) {
- MPoly &poly = polys[poly_index++];
- poly.loopstart = loop_index;
- poly.totloop = 3;
+ else if (config.fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
+ /* Center n-gon in the fill. */
+ MPoly &poly = polys[poly_index++];
+ poly.loopstart = loop_index;
+ poly.totloop = config.circle_segments;
- MLoop &loop_a = loops[loop_index++];
- loop_a.v = top_verts_start + i;
- loop_a.e = connecting_edges_start + i;
- MLoop &loop_b = loops[loop_index++];
- loop_b.v = bottom_verts_start;
- loop_b.e = connecting_edges_start + (i + 1) % verts_num;
- MLoop &loop_c = loops[loop_index++];
- loop_c.v = top_verts_start + (i + 1) % verts_num;
- loop_c.e = top_edges_start + i;
- }
+ for (const int i : IndexRange(config.circle_segments)) {
+ /* Go backwards to reverse surface normal. */
+ MLoop &loop = loops[loop_index++];
+ loop.v = config.last_vert - i;
+ loop.e = config.last_edge - ((i + 1) % config.circle_segments);
+ }
+ }
+}
+
+static void calculate_selection_outputs(Mesh *mesh,
+ const ConeConfig &config,
+ ConeAttributeOutputs &attribute_outputs)
+{
+ MeshComponent mesh_component;
+ mesh_component.replace(mesh, GeometryOwnershipType::Editable);
+
+ /* Populate "Top" selection output. */
+ if (attribute_outputs.top_id) {
+ const bool face = !config.top_is_point && config.fill_type != GEO_NODE_MESH_CIRCLE_FILL_NONE;
+ OutputAttribute_Typed<bool> attribute = mesh_component.attribute_try_get_for_output_only<bool>(
+ attribute_outputs.top_id.get(), face ? ATTR_DOMAIN_FACE : ATTR_DOMAIN_POINT);
+ MutableSpan<bool> selection = attribute.as_span();
+
+ if (config.top_is_point) {
+ selection[config.first_vert] = true;
}
else {
- BLI_assert(!bottom_is_point);
- for (const int i : IndexRange(verts_num)) {
- MPoly &poly = polys[poly_index++];
- poly.loopstart = loop_index;
- poly.totloop = 3;
+ selection.slice(0, face ? config.top_faces_len : config.circle_segments).fill(true);
+ }
+ attribute.save();
+ }
- MLoop &loop_a = loops[loop_index++];
- loop_a.v = bottom_verts_start + i;
- loop_a.e = bottom_edges_start + i;
- MLoop &loop_b = loops[loop_index++];
- loop_b.v = bottom_verts_start + (i + 1) % verts_num;
- loop_b.e = connecting_edges_start + (i + 1) % verts_num;
- MLoop &loop_c = loops[loop_index++];
- loop_c.v = top_verts_start;
- loop_c.e = connecting_edges_start + i;
+ /* Populate "Bottom" selection output. */
+ if (attribute_outputs.bottom_id) {
+ const bool face = !config.bottom_is_point &&
+ config.fill_type != GEO_NODE_MESH_CIRCLE_FILL_NONE;
+ OutputAttribute_Typed<bool> attribute = mesh_component.attribute_try_get_for_output_only<bool>(
+ attribute_outputs.bottom_id.get(), face ? ATTR_DOMAIN_FACE : ATTR_DOMAIN_POINT);
+ MutableSpan<bool> selection = attribute.as_span();
+
+ if (config.bottom_is_point) {
+ selection[config.last_vert] = true;
+ }
+ else {
+ selection
+ .slice(config.bottom_faces_start,
+ face ? config.bottom_faces_len : config.circle_segments)
+ .fill(true);
+ }
+ attribute.save();
+ }
+
+ /* Populate "Side" selection output. */
+ if (attribute_outputs.side_id) {
+ OutputAttribute_Typed<bool> attribute = mesh_component.attribute_try_get_for_output_only<bool>(
+ attribute_outputs.side_id.get(), ATTR_DOMAIN_FACE);
+ MutableSpan<bool> selection = attribute.as_span();
+
+ selection.slice(config.side_faces_start, config.side_faces_len).fill(true);
+ attribute.save();
+ }
+}
+
+/**
+ * If the top is the cone tip or has a fill, it is unwrapped into a circle in the
+ * lower left quadrant of the UV.
+ * Likewise, if the bottom is the cone tip or has a fill, it is unwrapped into a circle
+ * in the lower right quadrant of the UV.
+ * If the mesh is a truncated cone or a cylinder, the side faces are unwrapped into
+ * a rectangle that fills the top half of the UV (or the entire UV, if there are no fills).
+ */
+static void calculate_cone_uvs(Mesh *mesh, const ConeConfig &config)
+{
+ MeshComponent mesh_component;
+ mesh_component.replace(mesh, GeometryOwnershipType::Editable);
+ OutputAttribute_Typed<float2> uv_attribute =
+ mesh_component.attribute_try_get_for_output_only<float2>("uv_map", ATTR_DOMAIN_CORNER);
+ MutableSpan<float2> uvs = uv_attribute.as_span();
+
+ Array<float2> circle(config.circle_segments);
+ float angle = 0.0f;
+ const float angle_delta = 2.0f * M_PI / static_cast<float>(config.circle_segments);
+ for (const int i : IndexRange(config.circle_segments)) {
+ circle[i].x = std::cos(angle) * 0.225f;
+ circle[i].y = std::sin(angle) * 0.225f;
+ angle += angle_delta;
+ }
+
+ int loop_index = 0;
+
+ /* Left circle of the UV representing the top fill or top cone tip. */
+ if (config.top_is_point || config.fill_type != GEO_NODE_MESH_CIRCLE_FILL_NONE) {
+ const float2 center_left(0.25f, 0.25f);
+ const float radius_factor_delta = 1.0f / (config.top_is_point ?
+ static_cast<float>(config.side_segments) :
+ static_cast<float>(config.fill_segments));
+ const int left_circle_segment_count = config.top_is_point ? config.side_segments :
+ config.fill_segments;
+
+ if (config.top_has_center_vert) {
+ /* Cone tip itself or triangle fan center of the fill. */
+ for (const int i : IndexRange(config.circle_segments)) {
+ uvs[loop_index++] = radius_factor_delta * circle[i] + center_left;
+ uvs[loop_index++] = radius_factor_delta * circle[(i + 1) % config.circle_segments] +
+ center_left;
+ uvs[loop_index++] = center_left;
+ }
+ }
+ else if (!config.top_is_point && config.fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
+ /* N-gon at the center of the fill. */
+ for (const int i : IndexRange(config.circle_segments)) {
+ uvs[loop_index++] = radius_factor_delta * circle[i] + center_left;
+ }
+ }
+ /* The rest of the top fill is made out of quad rings. */
+ for (const int i : IndexRange(1, left_circle_segment_count - 1)) {
+ const float inner_radius_factor = i * radius_factor_delta;
+ const float outer_radius_factor = (i + 1) * radius_factor_delta;
+ for (const int j : IndexRange(config.circle_segments)) {
+ uvs[loop_index++] = inner_radius_factor * circle[j] + center_left;
+ uvs[loop_index++] = outer_radius_factor * circle[j] + center_left;
+ uvs[loop_index++] = outer_radius_factor * circle[(j + 1) % config.circle_segments] +
+ center_left;
+ uvs[loop_index++] = inner_radius_factor * circle[(j + 1) % config.circle_segments] +
+ center_left;
}
}
}
- /* Create bottom corners and faces. */
- if (!bottom_is_point) {
- if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
- MPoly &poly = polys[poly_index++];
- poly.loopstart = loop_index;
- poly.totloop = verts_num;
+ if (!config.top_is_point && !config.bottom_is_point) {
+ /* Mesh is a truncated cone or cylinder. The sides are unwrapped into a rectangle. */
+ const float bottom = (config.fill_type == GEO_NODE_MESH_CIRCLE_FILL_NONE) ? 0.0f : 0.5f;
+ const float x_delta = 1.0f / static_cast<float>(config.circle_segments);
+ const float y_delta = (1.0f - bottom) / static_cast<float>(config.side_segments);
- for (const int i : IndexRange(verts_num)) {
- /* Go backwards to reverse surface normal. */
- MLoop &loop = loops[loop_index++];
- loop.v = bottom_verts_start + verts_num - 1 - i;
- loop.e = bottom_edges_start + verts_num - 1 - (i + 1) % verts_num;
+ for (const int i : IndexRange(config.side_segments)) {
+ for (const int j : IndexRange(config.circle_segments)) {
+ uvs[loop_index++] = float2(j * x_delta, i * y_delta + bottom);
+ uvs[loop_index++] = float2(j * x_delta, (i + 1) * y_delta + bottom);
+ uvs[loop_index++] = float2((j + 1) * x_delta, (i + 1) * y_delta + bottom);
+ uvs[loop_index++] = float2((j + 1) * x_delta, i * y_delta + bottom);
}
}
- else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
- for (const int i : IndexRange(verts_num)) {
- MPoly &poly = polys[poly_index++];
- poly.loopstart = loop_index;
- poly.totloop = 3;
+ }
- MLoop &loop_a = loops[loop_index++];
- loop_a.v = bottom_verts_start + i;
- loop_a.e = bottom_fan_edges_start + i;
- MLoop &loop_b = loops[loop_index++];
- loop_b.v = bottom_center_vert_index;
- loop_b.e = bottom_fan_edges_start + (i + 1) % verts_num;
- MLoop &loop_c = loops[loop_index++];
- loop_c.v = bottom_verts_start + (i + 1) % verts_num;
- loop_c.e = bottom_edges_start + i;
+ /* Right circle of the UV representing the bottom fill or bottom cone tip. */
+ if (config.bottom_is_point || config.fill_type != GEO_NODE_MESH_CIRCLE_FILL_NONE) {
+ const float2 center_right(0.75f, 0.25f);
+ const float radius_factor_delta = 1.0f / (config.bottom_is_point ?
+ static_cast<float>(config.side_segments) :
+ static_cast<float>(config.fill_segments));
+ const int right_circle_segment_count = config.bottom_is_point ? config.side_segments :
+ config.fill_segments;
+
+ /* The bottom circle has to be created outside in to match the loop order. */
+ for (const int i : IndexRange(right_circle_segment_count - 1)) {
+ const float outer_radius_factor = 1.0f - i * radius_factor_delta;
+ const float inner_radius_factor = 1.0f - (i + 1) * radius_factor_delta;
+ for (const int j : IndexRange(config.circle_segments)) {
+ uvs[loop_index++] = outer_radius_factor * circle[j] + center_right;
+ uvs[loop_index++] = inner_radius_factor * circle[j] + center_right;
+ uvs[loop_index++] = inner_radius_factor * circle[(j + 1) % config.circle_segments] +
+ center_right;
+ uvs[loop_index++] = outer_radius_factor * circle[(j + 1) % config.circle_segments] +
+ center_right;
+ }
+ }
+
+ if (config.bottom_has_center_vert) {
+ /* Cone tip itself or triangle fan center of the fill. */
+ for (const int i : IndexRange(config.circle_segments)) {
+ uvs[loop_index++] = radius_factor_delta * circle[i] + center_right;
+ uvs[loop_index++] = center_right;
+ uvs[loop_index++] = radius_factor_delta * circle[(i + 1) % config.circle_segments] +
+ center_right;
+ }
+ }
+ else if (!config.bottom_is_point && config.fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
+ /* N-gon at the center of the fill. */
+ for (const int i : IndexRange(config.circle_segments)) {
+ /* Go backwards because of reversed face normal. */
+ uvs[loop_index++] = radius_factor_delta * circle[config.circle_segments - 1 - i] +
+ center_right;
}
}
}
- BKE_mesh_normals_tag_dirty(mesh);
+ uv_attribute.save();
+}
+
+static Mesh *create_vertex_mesh()
+{
+ /* Returns a mesh with a single vertex at the origin. */
+ Mesh *mesh = BKE_mesh_new_nomain(1, 0, 0, 0, 0);
+ copy_v3_fl3(mesh->mvert[0].co, 0.0f, 0.0f, 0.0f);
+ return mesh;
+}
- calculate_uvs(mesh, top_is_point, bottom_is_point, verts_num, fill_type);
+Mesh *create_cylinder_or_cone_mesh(const float radius_top,
+ const float radius_bottom,
+ const float depth,
+ const int circle_segments,
+ const int side_segments,
+ const int fill_segments,
+ const GeometryNodeMeshCircleFillType fill_type,
+ ConeAttributeOutputs &attribute_outputs)
+{
+ const ConeConfig config(
+ radius_top, radius_bottom, depth, circle_segments, side_segments, fill_segments, fill_type);
+
+ /* Handle the case of a line / single point before everything else to avoid
+ * the need to check for it later. */
+ if (config.top_is_point && config.bottom_is_point) {
+ if (config.height == 0.0f) {
+ return create_vertex_mesh();
+ }
+ const float z_delta = -2.0f * config.height / static_cast<float>(config.side_segments);
+ const float3 start(0.0f, 0.0f, config.height);
+ const float3 delta(0.0f, 0.0f, z_delta);
+ return create_line_mesh(start, delta, config.tot_verts);
+ }
+
+ Mesh *mesh = BKE_mesh_new_nomain(
+ config.tot_verts, config.tot_edges, 0, config.tot_corners, config.tot_faces);
+ BKE_id_material_eval_ensure_default_slot(&mesh->id);
+
+ MutableSpan<MVert> verts{mesh->mvert, mesh->totvert};
+ MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop};
+ MutableSpan<MEdge> edges{mesh->medge, mesh->totedge};
+ MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly};
+
+ calculate_cone_vertices(verts, config);
+ calculate_cone_edges(edges, config);
+ calculate_cone_faces(loops, polys, config);
+ calculate_cone_uvs(mesh, config);
+ calculate_selection_outputs(mesh, config, attribute_outputs);
return mesh;
}
-static void geo_node_mesh_primitive_cone_exec(GeoNodeExecParams params)
+} // namespace blender::nodes
+
+namespace blender::nodes::node_geo_mesh_primitive_cone_cc {
+
+NODE_STORAGE_FUNCS(NodeGeometryMeshCone)
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Int>(N_("Vertices"))
+ .default_value(32)
+ .min(3)
+ .max(512)
+ .description(N_("Number of points on the circle at the top and bottom"));
+ b.add_input<decl::Int>(N_("Side Segments"))
+ .default_value(1)
+ .min(1)
+ .max(512)
+ .description(N_("The number of edges running vertically along the side of the cone"));
+ b.add_input<decl::Int>(N_("Fill Segments"))
+ .default_value(1)
+ .min(1)
+ .max(512)
+ .description(N_("Number of concentric rings used to fill the round face"));
+ b.add_input<decl::Float>(N_("Radius Top"))
+ .min(0.0f)
+ .subtype(PROP_DISTANCE)
+ .description(N_("Radius of the top circle of the cone"));
+ b.add_input<decl::Float>(N_("Radius Bottom"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .subtype(PROP_DISTANCE)
+ .description(N_("Radius of the bottom circle of the cone"));
+ b.add_input<decl::Float>(N_("Depth"))
+ .default_value(2.0f)
+ .min(0.0f)
+ .subtype(PROP_DISTANCE)
+ .description(N_("Height of the generated cone"));
+ b.add_output<decl::Geometry>(N_("Mesh"));
+ b.add_output<decl::Bool>(N_("Top")).field_source();
+ b.add_output<decl::Bool>(N_("Bottom")).field_source();
+ b.add_output<decl::Bool>(N_("Side")).field_source();
+}
+
+static void node_init(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeGeometryMeshCone *node_storage = MEM_cnew<NodeGeometryMeshCone>(__func__);
+
+ node_storage->fill_type = GEO_NODE_MESH_CIRCLE_FILL_NGON;
+
+ node->storage = node_storage;
+}
+
+static void node_update(bNodeTree *ntree, bNode *node)
{
- const bNode &node = params.node();
- const NodeGeometryMeshCone &storage = *(const NodeGeometryMeshCone *)node.storage;
+ bNodeSocket *vertices_socket = (bNodeSocket *)node->inputs.first;
+ bNodeSocket *rings_socket = vertices_socket->next;
+ bNodeSocket *fill_subdiv_socket = rings_socket->next;
+
+ const NodeGeometryMeshCone &storage = node_storage(*node);
+ const GeometryNodeMeshCircleFillType fill = (GeometryNodeMeshCircleFillType)storage.fill_type;
+ const bool has_fill = fill != GEO_NODE_MESH_CIRCLE_FILL_NONE;
+ nodeSetSocketAvailability(ntree, fill_subdiv_socket, has_fill);
+}
- const GeometryNodeMeshCircleFillType fill_type = (const GeometryNodeMeshCircleFillType)
- storage.fill_type;
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
+ uiItemR(layout, ptr, "fill_type", 0, nullptr, ICON_NONE);
+}
- const int verts_num = params.extract_input<int>("Vertices");
- if (verts_num < 3) {
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ const NodeGeometryMeshCone &storage = node_storage(params.node());
+ const GeometryNodeMeshCircleFillType fill = (GeometryNodeMeshCircleFillType)storage.fill_type;
+
+ const int circle_segments = params.extract_input<int>("Vertices");
+ if (circle_segments < 3) {
params.error_message_add(NodeWarningType::Info, TIP_("Vertices must be at least 3"));
- params.set_output("Geometry", GeometrySet());
+ params.set_default_remaining_outputs();
+ return;
+ }
+
+ const int side_segments = params.extract_input<int>("Side Segments");
+ if (side_segments < 1) {
+ params.error_message_add(NodeWarningType::Info, TIP_("Side Segments must be at least 1"));
+ params.set_default_remaining_outputs();
+ return;
+ }
+
+ const bool no_fill = fill == GEO_NODE_MESH_CIRCLE_FILL_NONE;
+ const int fill_segments = no_fill ? 1 : params.extract_input<int>("Fill Segments");
+ if (fill_segments < 1) {
+ params.error_message_add(NodeWarningType::Info, TIP_("Fill Segments must be at least 1"));
+ params.set_default_remaining_outputs();
return;
}
@@ -555,27 +823,64 @@ static void geo_node_mesh_primitive_cone_exec(GeoNodeExecParams params)
const float radius_bottom = params.extract_input<float>("Radius Bottom");
const float depth = params.extract_input<float>("Depth");
- Mesh *mesh = create_cylinder_or_cone_mesh(
- radius_top, radius_bottom, depth, verts_num, fill_type);
+ ConeAttributeOutputs attribute_outputs;
+ if (params.output_is_required("Top")) {
+ attribute_outputs.top_id = StrongAnonymousAttributeID("top_selection");
+ }
+ if (params.output_is_required("Bottom")) {
+ attribute_outputs.bottom_id = StrongAnonymousAttributeID("bottom_selection");
+ }
+ if (params.output_is_required("Side")) {
+ attribute_outputs.side_id = StrongAnonymousAttributeID("side_selection");
+ }
+
+ Mesh *mesh = create_cylinder_or_cone_mesh(radius_top,
+ radius_bottom,
+ depth,
+ circle_segments,
+ side_segments,
+ fill_segments,
+ fill,
+ attribute_outputs);
/* Transform the mesh so that the base of the cone is at the origin. */
BKE_mesh_translate(mesh, float3(0.0f, 0.0f, depth * 0.5f), false);
- params.set_output("Geometry", GeometrySet::create_with_mesh(mesh));
+ if (attribute_outputs.top_id) {
+ params.set_output("Top",
+ AnonymousAttributeFieldInput::Create<bool>(
+ std::move(attribute_outputs.top_id), params.attribute_producer_name()));
+ }
+ if (attribute_outputs.bottom_id) {
+ params.set_output(
+ "Bottom",
+ AnonymousAttributeFieldInput::Create<bool>(std::move(attribute_outputs.bottom_id),
+ params.attribute_producer_name()));
+ }
+ if (attribute_outputs.side_id) {
+ params.set_output("Side",
+ AnonymousAttributeFieldInput::Create<bool>(
+ std::move(attribute_outputs.side_id), params.attribute_producer_name()));
+ }
+
+ params.set_output("Mesh", GeometrySet::create_with_mesh(mesh));
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_mesh_primitive_cone_cc
void register_node_type_geo_mesh_primitive_cone()
{
+ namespace file_ns = blender::nodes::node_geo_mesh_primitive_cone_cc;
+
static bNodeType ntype;
- geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_CONE, "Cone", NODE_CLASS_GEOMETRY, 0);
- node_type_init(&ntype, blender::nodes::geo_node_mesh_primitive_cone_init);
+ geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_CONE, "Cone", NODE_CLASS_GEOMETRY);
+ node_type_init(&ntype, file_ns::node_init);
+ node_type_update(&ntype, file_ns::node_update);
node_type_storage(
&ntype, "NodeGeometryMeshCone", node_free_standard_storage, node_copy_standard_storage);
- ntype.geometry_node_execute = blender::nodes::geo_node_mesh_primitive_cone_exec;
- ntype.draw_buttons = blender::nodes::geo_node_mesh_primitive_cone_layout;
- ntype.declare = blender::nodes::geo_node_mesh_primitive_cone_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.draw_buttons = file_ns::node_layout;
+ ntype.declare = file_ns::node_declare;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc
index af8ce02b3c1..5b67258a947 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc
@@ -24,15 +24,6 @@
namespace blender::nodes {
-static void geo_node_mesh_primitive_cube_declare(NodeDeclarationBuilder &b)
-{
- b.add_input<decl::Vector>("Size").default_value(float3(1)).min(0.0f).subtype(PROP_TRANSLATION);
- b.add_input<decl::Int>("Vertices X").default_value(2).min(2).max(1000);
- b.add_input<decl::Int>("Vertices Y").default_value(2).min(2).max(1000);
- b.add_input<decl::Int>("Vertices Z").default_value(2).min(2).max(1000);
- b.add_output<decl::Geometry>("Geometry");
-}
-
struct CuboidConfig {
float3 size;
int verts_x;
@@ -86,23 +77,37 @@ static void calculate_vertices(const CuboidConfig &config, MutableSpan<MVert> ve
int vert_index = 0;
- /* Though looping over all possible coordinates inside the cube only to skip them may be slow,
- * the alternative is similar complexity to below in the poly index calculation. If this loop
- * becomes a problem in the future it could be optimized, though only after proper performance
- * testing. */
for (const int z : IndexRange(config.verts_z)) {
- for (const int y : IndexRange(config.verts_y)) {
- for (const int x : IndexRange(config.verts_x)) {
- /* Only plot vertices on the surface of the cuboid. */
- if (ELEM(z, 0, config.edges_z) || ELEM(x, 0, config.edges_x) ||
- ELEM(y, 0, config.edges_y)) {
-
+ if (ELEM(z, 0, config.edges_z)) {
+ /* Fill bottom and top. */
+ const float z_pos = z_bottom + z_delta * z;
+ for (const int y : IndexRange(config.verts_y)) {
+ const float y_pos = y_front + y_delta * y;
+ for (const int x : IndexRange(config.verts_x)) {
const float x_pos = x_left + x_delta * x;
+ copy_v3_v3(verts[vert_index++].co, float3(x_pos, y_pos, z_pos));
+ }
+ }
+ }
+ else {
+ for (const int y : IndexRange(config.verts_y)) {
+ if (ELEM(y, 0, config.edges_y)) {
+ /* Fill y-sides. */
const float y_pos = y_front + y_delta * y;
const float z_pos = z_bottom + z_delta * z;
- copy_v3_v3(verts[vert_index].co, float3(x_pos, y_pos, z_pos));
-
- vert_index++;
+ for (const int x : IndexRange(config.verts_x)) {
+ const float x_pos = x_left + x_delta * x;
+ copy_v3_v3(verts[vert_index++].co, float3(x_pos, y_pos, z_pos));
+ }
+ }
+ else {
+ /* Fill x-sides. */
+ const float x_pos = x_left;
+ const float y_pos = y_front + y_delta * y;
+ const float z_pos = z_bottom + z_delta * z;
+ copy_v3_v3(verts[vert_index++].co, float3(x_pos, y_pos, z_pos));
+ const float x_pos2 = x_left + x_delta * config.edges_x;
+ copy_v3_v3(verts[vert_index++].co, float3(x_pos2, y_pos, z_pos));
}
}
}
@@ -150,7 +155,7 @@ static void calculate_polys(const CuboidConfig &config,
/* Calculate polys for Bottom faces. */
int vert_1_start = 0;
- for (const int UNUSED(y) : IndexRange(config.edges_y)) {
+ for ([[maybe_unused]] const int y : IndexRange(config.edges_y)) {
for (const int x : IndexRange(config.edges_x)) {
const int vert_1 = vert_1_start + x;
const int vert_2 = vert_1_start + config.verts_x + x;
@@ -168,7 +173,7 @@ static void calculate_polys(const CuboidConfig &config,
vert_1_start = 0;
int vert_2_start = config.verts_x * config.verts_y;
- for (const int UNUSED(z) : IndexRange(config.edges_z)) {
+ for ([[maybe_unused]] const int z : IndexRange(config.edges_z)) {
for (const int x : IndexRange(config.edges_x)) {
define_quad(polys,
loops,
@@ -191,7 +196,7 @@ static void calculate_polys(const CuboidConfig &config,
(config.verts_x - 2) * (config.verts_y - 2));
vert_2_start = vert_1_start + config.verts_x;
- for (const int UNUSED(y) : IndexRange(config.edges_y)) {
+ for ([[maybe_unused]] const int y : IndexRange(config.edges_y)) {
for (const int x : IndexRange(config.edges_x)) {
define_quad(polys,
loops,
@@ -423,6 +428,35 @@ Mesh *create_cuboid_mesh(const float3 size,
return mesh;
}
+} // namespace blender::nodes
+
+namespace blender::nodes::node_geo_mesh_primitive_cube_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Vector>(N_("Size"))
+ .default_value(float3(1))
+ .min(0.0f)
+ .subtype(PROP_TRANSLATION)
+ .description(N_("Side length along each axis"));
+ b.add_input<decl::Int>(N_("Vertices X"))
+ .default_value(2)
+ .min(2)
+ .max(1000)
+ .description(N_("Number of vertices for the X side of the shape"));
+ b.add_input<decl::Int>(N_("Vertices Y"))
+ .default_value(2)
+ .min(2)
+ .max(1000)
+ .description(N_("Number of vertices for the Y side of the shape"));
+ b.add_input<decl::Int>(N_("Vertices Z"))
+ .default_value(2)
+ .min(2)
+ .max(1000)
+ .description(N_("Number of vertices for the Z side of the shape"));
+ b.add_output<decl::Geometry>(N_("Mesh"));
+}
+
static Mesh *create_cube_mesh(const float3 size,
const int verts_x,
const int verts_y,
@@ -456,19 +490,19 @@ static Mesh *create_cube_mesh(const float3 size,
}
if (verts_y == 1) { /* XZ plane. */
Mesh *mesh = create_grid_mesh(verts_x, verts_z, size.x, size.z);
- transform_mesh(mesh, float3(0), float3(M_PI_2, 0.0f, 0.0f), float3(1));
+ transform_mesh(*mesh, float3(0), float3(M_PI_2, 0.0f, 0.0f), float3(1));
return mesh;
}
/* YZ plane. */
Mesh *mesh = create_grid_mesh(verts_z, verts_y, size.z, size.y);
- transform_mesh(mesh, float3(0), float3(0.0f, M_PI_2, 0.0f), float3(1));
+ transform_mesh(*mesh, float3(0), float3(0.0f, M_PI_2, 0.0f), float3(1));
return mesh;
}
return create_cuboid_mesh(size, verts_x, verts_y, verts_z);
}
-static void geo_node_mesh_primitive_cube_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
const float3 size = params.extract_input<float3>("Size");
const int verts_x = params.extract_input<int>("Vertices X");
@@ -476,23 +510,25 @@ static void geo_node_mesh_primitive_cube_exec(GeoNodeExecParams params)
const int verts_z = params.extract_input<int>("Vertices Z");
if (verts_x < 1 || verts_y < 1 || verts_z < 1) {
params.error_message_add(NodeWarningType::Info, TIP_("Vertices must be at least 1"));
- params.set_output("Geometry", GeometrySet());
+ params.set_default_remaining_outputs();
return;
}
Mesh *mesh = create_cube_mesh(size, verts_x, verts_y, verts_z);
- params.set_output("Geometry", GeometrySet::create_with_mesh(mesh));
+ params.set_output("Mesh", GeometrySet::create_with_mesh(mesh));
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_mesh_primitive_cube_cc
void register_node_type_geo_mesh_primitive_cube()
{
+ namespace file_ns = blender::nodes::node_geo_mesh_primitive_cube_cc;
+
static bNodeType ntype;
- geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_CUBE, "Cube", NODE_CLASS_GEOMETRY, 0);
- ntype.declare = blender::nodes::geo_node_mesh_primitive_cube_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_mesh_primitive_cube_exec;
+ geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_CUBE, "Cube", NODE_CLASS_GEOMETRY);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc
index 8c4defc3ca3..73f21cf31fa 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc
@@ -25,81 +25,155 @@
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_mesh_primitive_cylinder_cc {
-static void geo_node_mesh_primitive_cylinder_declare(NodeDeclarationBuilder &b)
+NODE_STORAGE_FUNCS(NodeGeometryMeshCylinder)
+
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Int>("Vertices")
+ b.add_input<decl::Int>(N_("Vertices"))
.default_value(32)
.min(3)
- .max(4096)
- .description("The number of vertices around the circumference");
- b.add_input<decl::Float>("Radius")
+ .max(512)
+ .description(N_("The number of vertices on the top and bottom circles"));
+ b.add_input<decl::Int>(N_("Side Segments"))
+ .default_value(1)
+ .min(1)
+ .max(512)
+ .description(N_("The number of rectangular segments along each side"));
+ b.add_input<decl::Int>(N_("Fill Segments"))
+ .default_value(1)
+ .min(1)
+ .max(512)
+ .description(N_("The number of concentric rings used to fill the round faces"));
+ b.add_input<decl::Float>(N_("Radius"))
.default_value(1.0f)
.min(0.0f)
.subtype(PROP_DISTANCE)
- .description("The radius of the cylinder");
- b.add_input<decl::Float>("Depth")
+ .description(N_("The radius of the cylinder"));
+ b.add_input<decl::Float>(N_("Depth"))
.default_value(2.0f)
.min(0.0f)
.subtype(PROP_DISTANCE)
- .description("The height of the cylinder on the Z axis");
- b.add_output<decl::Geometry>("Geometry");
+ .description(N_("The height of the cylinder"));
+ b.add_output<decl::Geometry>(N_("Mesh"));
+ b.add_output<decl::Bool>(N_("Top")).field_source();
+ b.add_output<decl::Bool>(N_("Side")).field_source();
+ b.add_output<decl::Bool>(N_("Bottom")).field_source();
}
-static void geo_node_mesh_primitive_cylinder_layout(uiLayout *layout,
- bContext *UNUSED(C),
- PointerRNA *ptr)
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
uiItemR(layout, ptr, "fill_type", 0, nullptr, ICON_NONE);
}
-static void geo_node_mesh_primitive_cylinder_init(bNodeTree *UNUSED(ntree), bNode *node)
+static void node_init(bNodeTree *UNUSED(ntree), bNode *node)
{
- NodeGeometryMeshCylinder *node_storage = (NodeGeometryMeshCylinder *)MEM_callocN(
- sizeof(NodeGeometryMeshCylinder), __func__);
+ NodeGeometryMeshCylinder *node_storage = MEM_cnew<NodeGeometryMeshCylinder>(__func__);
node_storage->fill_type = GEO_NODE_MESH_CIRCLE_FILL_NGON;
node->storage = node_storage;
}
-static void geo_node_mesh_primitive_cylinder_exec(GeoNodeExecParams params)
+static void node_update(bNodeTree *ntree, bNode *node)
{
- const bNode &node = params.node();
- const NodeGeometryMeshCylinder &storage = *(const NodeGeometryMeshCylinder *)node.storage;
+ bNodeSocket *vertices_socket = (bNodeSocket *)node->inputs.first;
+ bNodeSocket *rings_socket = vertices_socket->next;
+ bNodeSocket *fill_subdiv_socket = rings_socket->next;
+
+ const NodeGeometryMeshCylinder &storage = node_storage(*node);
+ const GeometryNodeMeshCircleFillType fill = (GeometryNodeMeshCircleFillType)storage.fill_type;
+ const bool has_fill = fill != GEO_NODE_MESH_CIRCLE_FILL_NONE;
+ nodeSetSocketAvailability(ntree, fill_subdiv_socket, has_fill);
+}
- const GeometryNodeMeshCircleFillType fill_type = (const GeometryNodeMeshCircleFillType)
- storage.fill_type;
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ const NodeGeometryMeshCylinder &storage = node_storage(params.node());
+ const GeometryNodeMeshCircleFillType fill = (GeometryNodeMeshCircleFillType)storage.fill_type;
const float radius = params.extract_input<float>("Radius");
const float depth = params.extract_input<float>("Depth");
- const int verts_num = params.extract_input<int>("Vertices");
- if (verts_num < 3) {
+ const int circle_segments = params.extract_input<int>("Vertices");
+ if (circle_segments < 3) {
params.error_message_add(NodeWarningType::Info, TIP_("Vertices must be at least 3"));
- params.set_output("Geometry", GeometrySet());
+ params.set_default_remaining_outputs();
+ return;
+ }
+
+ const int side_segments = params.extract_input<int>("Side Segments");
+ if (side_segments < 1) {
+ params.error_message_add(NodeWarningType::Info, TIP_("Side Segments must be at least 1"));
+ params.set_default_remaining_outputs();
+ return;
+ }
+
+ const bool no_fill = fill == GEO_NODE_MESH_CIRCLE_FILL_NONE;
+ const int fill_segments = no_fill ? 1 : params.extract_input<int>("Fill Segments");
+ if (fill_segments < 1) {
+ params.error_message_add(NodeWarningType::Info, TIP_("Fill Segments must be at least 1"));
+ params.set_default_remaining_outputs();
return;
}
+ ConeAttributeOutputs attribute_outputs;
+ if (params.output_is_required("Top")) {
+ attribute_outputs.top_id = StrongAnonymousAttributeID("top_selection");
+ }
+ if (params.output_is_required("Bottom")) {
+ attribute_outputs.bottom_id = StrongAnonymousAttributeID("bottom_selection");
+ }
+ if (params.output_is_required("Side")) {
+ attribute_outputs.side_id = StrongAnonymousAttributeID("side_selection");
+ }
+
/* The cylinder is a special case of the cone mesh where the top and bottom radius are equal. */
- Mesh *mesh = create_cylinder_or_cone_mesh(radius, radius, depth, verts_num, fill_type);
+ Mesh *mesh = create_cylinder_or_cone_mesh(radius,
+ radius,
+ depth,
+ circle_segments,
+ side_segments,
+ fill_segments,
+ fill,
+ attribute_outputs);
+
+ if (attribute_outputs.top_id) {
+ params.set_output("Top",
+ AnonymousAttributeFieldInput::Create<bool>(
+ std::move(attribute_outputs.top_id), params.attribute_producer_name()));
+ }
+ if (attribute_outputs.bottom_id) {
+ params.set_output(
+ "Bottom",
+ AnonymousAttributeFieldInput::Create<bool>(std::move(attribute_outputs.bottom_id),
+ params.attribute_producer_name()));
+ }
+ if (attribute_outputs.side_id) {
+ params.set_output("Side",
+ AnonymousAttributeFieldInput::Create<bool>(
+ std::move(attribute_outputs.side_id), params.attribute_producer_name()));
+ }
- params.set_output("Geometry", GeometrySet::create_with_mesh(mesh));
+ params.set_output("Mesh", GeometrySet::create_with_mesh(mesh));
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_mesh_primitive_cylinder_cc
void register_node_type_geo_mesh_primitive_cylinder()
{
+ namespace file_ns = blender::nodes::node_geo_mesh_primitive_cylinder_cc;
+
static bNodeType ntype;
- geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_CYLINDER, "Cylinder", NODE_CLASS_GEOMETRY, 0);
- node_type_init(&ntype, blender::nodes::geo_node_mesh_primitive_cylinder_init);
+ geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_CYLINDER, "Cylinder", NODE_CLASS_GEOMETRY);
+ node_type_init(&ntype, file_ns::node_init);
+ node_type_update(&ntype, file_ns::node_update);
node_type_storage(
&ntype, "NodeGeometryMeshCylinder", node_free_standard_storage, node_copy_standard_storage);
- ntype.declare = blender::nodes::geo_node_mesh_primitive_cylinder_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_mesh_primitive_cylinder_exec;
- ntype.draw_buttons = blender::nodes::geo_node_mesh_primitive_cylinder_layout;
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.draw_buttons = file_ns::node_layout;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc
index 858ef8648f8..ecb3c785212 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc
@@ -14,6 +14,8 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#include "BLI_task.hh"
+
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
@@ -27,15 +29,6 @@
namespace blender::nodes {
-static void geo_node_mesh_primitive_grid_declare(NodeDeclarationBuilder &b)
-{
- b.add_input<decl::Float>("Size X").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE);
- b.add_input<decl::Float>("Size Y").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE);
- b.add_input<decl::Int>("Vertices X").default_value(3).min(2).max(1000);
- b.add_input<decl::Int>("Vertices Y").default_value(3).min(2).max(1000);
- b.add_output<decl::Geometry>("Geometry");
-}
-
static void calculate_uvs(
Mesh *mesh, Span<MVert> verts, Span<MLoop> loops, const float size_x, const float size_y)
{
@@ -47,11 +40,13 @@ static void calculate_uvs(
const float dx = (size_x == 0.0f) ? 0.0f : 1.0f / size_x;
const float dy = (size_y == 0.0f) ? 0.0f : 1.0f / size_y;
- for (const int i : loops.index_range()) {
- const float3 &co = verts[loops[i].v].co;
- uvs[i].x = (co.x + size_x * 0.5f) * dx;
- uvs[i].y = (co.y + size_y * 0.5f) * dy;
- }
+ threading::parallel_for(loops.index_range(), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ const float3 &co = verts[loops[i].v].co;
+ uvs[i].x = (co.x + size_x * 0.5f) * dx;
+ uvs[i].y = (co.y + size_y * 0.5f) * dy;
+ }
+ });
uv_attribute.save();
}
@@ -79,72 +74,87 @@ Mesh *create_grid_mesh(const int verts_x,
const float dy = edges_y == 0 ? 0.0f : size_y / edges_y;
const float x_shift = edges_x / 2.0f;
const float y_shift = edges_y / 2.0f;
- for (const int x_index : IndexRange(verts_x)) {
- for (const int y_index : IndexRange(verts_y)) {
- const int vert_index = x_index * verts_y + y_index;
- verts[vert_index].co[0] = (x_index - x_shift) * dx;
- verts[vert_index].co[1] = (y_index - y_shift) * dy;
- verts[vert_index].co[2] = 0.0f;
+ threading::parallel_for(IndexRange(verts_x), 512, [&](IndexRange x_range) {
+ for (const int x : x_range) {
+ const int y_offset = x * verts_y;
+ threading::parallel_for(IndexRange(verts_y), 512, [&](IndexRange y_range) {
+ for (const int y : y_range) {
+ const int vert_index = y_offset + y;
+ verts[vert_index].co[0] = (x - x_shift) * dx;
+ verts[vert_index].co[1] = (y - y_shift) * dy;
+ verts[vert_index].co[2] = 0.0f;
+ }
+ });
}
- }
- }
-
- /* Point all vertex normals in the up direction. */
- const short up_normal[3] = {0, 0, SHRT_MAX};
- for (MVert &vert : verts) {
- copy_v3_v3_short(vert.no, up_normal);
+ });
}
- /* Build the horizontal edges in the X direction. */
const int y_edges_start = 0;
+ const int x_edges_start = verts_x * edges_y;
const short edge_flag = (edges_x == 0 || edges_y == 0) ? ME_LOOSEEDGE :
ME_EDGEDRAW | ME_EDGERENDER;
- int edge_index = 0;
- for (const int x : IndexRange(verts_x)) {
- for (const int y : IndexRange(edges_y)) {
- const int vert_index = x * verts_y + y;
- MEdge &edge = edges[edge_index++];
- edge.v1 = vert_index;
- edge.v2 = vert_index + 1;
- edge.flag = edge_flag;
+
+ /* Build the horizontal edges in the X direction. */
+ threading::parallel_for(IndexRange(verts_x), 512, [&](IndexRange x_range) {
+ for (const int x : x_range) {
+ const int y_vert_offset = x * verts_y;
+ const int y_edge_offset = y_edges_start + x * edges_y;
+ threading::parallel_for(IndexRange(edges_y), 512, [&](IndexRange y_range) {
+ for (const int y : y_range) {
+ const int vert_index = y_vert_offset + y;
+ MEdge &edge = edges[y_edge_offset + y];
+ edge.v1 = vert_index;
+ edge.v2 = vert_index + 1;
+ edge.flag = edge_flag;
+ }
+ });
}
- }
+ });
/* Build the vertical edges in the Y direction. */
- const int x_edges_start = edge_index;
- for (const int y : IndexRange(verts_y)) {
- for (const int x : IndexRange(edges_x)) {
- const int vert_index = x * verts_y + y;
- MEdge &edge = edges[edge_index++];
- edge.v1 = vert_index;
- edge.v2 = vert_index + verts_y;
- edge.flag = edge_flag;
+ threading::parallel_for(IndexRange(verts_y), 512, [&](IndexRange y_range) {
+ for (const int y : y_range) {
+ const int x_edge_offset = x_edges_start + y * edges_x;
+ threading::parallel_for(IndexRange(edges_x), 512, [&](IndexRange x_range) {
+ for (const int x : x_range) {
+ const int vert_index = x * verts_y + y;
+ MEdge &edge = edges[x_edge_offset + x];
+ edge.v1 = vert_index;
+ edge.v2 = vert_index + verts_y;
+ edge.flag = edge_flag;
+ }
+ });
}
- }
-
- int loop_index = 0;
- int poly_index = 0;
- for (const int x : IndexRange(edges_x)) {
- for (const int y : IndexRange(edges_y)) {
- MPoly &poly = polys[poly_index++];
- poly.loopstart = loop_index;
- poly.totloop = 4;
- const int vert_index = x * verts_y + y;
-
- MLoop &loop_a = loops[loop_index++];
- loop_a.v = vert_index;
- loop_a.e = x_edges_start + edges_x * y + x;
- MLoop &loop_b = loops[loop_index++];
- loop_b.v = vert_index + verts_y;
- loop_b.e = y_edges_start + edges_y * (x + 1) + y;
- MLoop &loop_c = loops[loop_index++];
- loop_c.v = vert_index + verts_y + 1;
- loop_c.e = x_edges_start + edges_x * (y + 1) + x;
- MLoop &loop_d = loops[loop_index++];
- loop_d.v = vert_index + 1;
- loop_d.e = y_edges_start + edges_y * x + y;
+ });
+
+ threading::parallel_for(IndexRange(edges_x), 512, [&](IndexRange x_range) {
+ for (const int x : x_range) {
+ const int y_offset = x * edges_y;
+ threading::parallel_for(IndexRange(edges_y), 512, [&](IndexRange y_range) {
+ for (const int y : y_range) {
+ const int poly_index = y_offset + y;
+ const int loop_index = poly_index * 4;
+ MPoly &poly = polys[poly_index];
+ poly.loopstart = loop_index;
+ poly.totloop = 4;
+ const int vert_index = x * verts_y + y;
+
+ MLoop &loop_a = loops[loop_index];
+ loop_a.v = vert_index;
+ loop_a.e = x_edges_start + edges_x * y + x;
+ MLoop &loop_b = loops[loop_index + 1];
+ loop_b.v = vert_index + verts_y;
+ loop_b.e = y_edges_start + edges_y * (x + 1) + y;
+ MLoop &loop_c = loops[loop_index + 2];
+ loop_c.v = vert_index + verts_y + 1;
+ loop_c.e = x_edges_start + edges_x * (y + 1) + x;
+ MLoop &loop_d = loops[loop_index + 3];
+ loop_d.v = vert_index + 1;
+ loop_d.e = y_edges_start + edges_y * x + y;
+ }
+ });
}
- }
+ });
if (mesh->totpoly != 0) {
calculate_uvs(mesh, verts, loops, size_x, size_y);
@@ -153,32 +163,62 @@ Mesh *create_grid_mesh(const int verts_x,
return mesh;
}
-static void geo_node_mesh_primitive_grid_exec(GeoNodeExecParams params)
+} // namespace blender::nodes
+
+namespace blender::nodes::node_geo_mesh_primitive_grid_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Float>(N_("Size X"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .subtype(PROP_DISTANCE)
+ .description(N_("Side length of the plane in the X direction"));
+ b.add_input<decl::Float>(N_("Size Y"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .subtype(PROP_DISTANCE)
+ .description(N_("Side length of the plane in the Y direction"));
+ b.add_input<decl::Int>(N_("Vertices X"))
+ .default_value(3)
+ .min(2)
+ .max(1000)
+ .description(N_("Number of vertices in the X direction"));
+ b.add_input<decl::Int>(N_("Vertices Y"))
+ .default_value(3)
+ .min(2)
+ .max(1000)
+ .description(N_("Number of vertices in the Y direction"));
+ b.add_output<decl::Geometry>(N_("Mesh"));
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
{
const float size_x = params.extract_input<float>("Size X");
const float size_y = params.extract_input<float>("Size Y");
const int verts_x = params.extract_input<int>("Vertices X");
const int verts_y = params.extract_input<int>("Vertices Y");
if (verts_x < 1 || verts_y < 1) {
- params.set_output("Geometry", GeometrySet());
+ params.set_default_remaining_outputs();
return;
}
Mesh *mesh = create_grid_mesh(verts_x, verts_y, size_x, size_y);
- BLI_assert(BKE_mesh_is_valid(mesh));
BKE_id_material_eval_ensure_default_slot(&mesh->id);
- params.set_output("Geometry", GeometrySet::create_with_mesh(mesh));
+ params.set_output("Mesh", GeometrySet::create_with_mesh(mesh));
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_mesh_primitive_grid_cc
void register_node_type_geo_mesh_primitive_grid()
{
+ namespace file_ns = blender::nodes::node_geo_mesh_primitive_grid_cc;
+
static bNodeType ntype;
- geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_GRID, "Grid", NODE_CLASS_GEOMETRY, 0);
- ntype.declare = blender::nodes::geo_node_mesh_primitive_grid_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_mesh_primitive_grid_exec;
+ geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_GRID, "Grid", NODE_CLASS_GEOMETRY);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc
index 5ea7165ac31..28a505c5bb8 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc
@@ -24,22 +24,31 @@
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_mesh_primitive_ico_sphere_cc {
-static void geo_node_mesh_primitive_ico_sphere_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>("Radius").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE);
- b.add_input<decl::Int>("Subdivisions").default_value(1).min(1).max(7);
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Float>(N_("Radius"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .subtype(PROP_DISTANCE)
+ .description(N_("Distance from the generated points to the origin"));
+ b.add_input<decl::Int>(N_("Subdivisions"))
+ .default_value(1)
+ .min(1)
+ .max(7)
+ .description(N_("Number of subdivisions on top of the basic icosahedron"));
+ b.add_output<decl::Geometry>(N_("Mesh"));
}
static Mesh *create_ico_sphere_mesh(const int subdivisions, const float radius)
{
const float4x4 transform = float4x4::identity();
- const BMeshCreateParams bmcp = {true};
+ BMeshCreateParams bmesh_create_params{};
+ bmesh_create_params.use_toolflags = true;
const BMAllocTemplate allocsize = {0, 0, 0, 0};
- BMesh *bm = BM_mesh_create(&allocsize, &bmcp);
+ BMesh *bm = BM_mesh_create(&allocsize, &bmesh_create_params);
BM_data_layer_add_named(bm, &bm->ldata, CD_MLOOPUV, nullptr);
BMO_op_callf(bm,
@@ -60,24 +69,26 @@ static Mesh *create_ico_sphere_mesh(const int subdivisions, const float radius)
return mesh;
}
-static void geo_node_mesh_primitive_ico_sphere_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
const int subdivisions = std::min(params.extract_input<int>("Subdivisions"), 10);
const float radius = params.extract_input<float>("Radius");
Mesh *mesh = create_ico_sphere_mesh(subdivisions, radius);
- params.set_output("Geometry", GeometrySet::create_with_mesh(mesh));
+ params.set_output("Mesh", GeometrySet::create_with_mesh(mesh));
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_mesh_primitive_ico_sphere_cc
void register_node_type_geo_mesh_primitive_ico_sphere()
{
+ namespace file_ns = blender::nodes::node_geo_mesh_primitive_ico_sphere_cc;
+
static bNodeType ntype;
geo_node_type_base(
- &ntype, GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE, "Ico Sphere", NODE_CLASS_GEOMETRY, 0);
- ntype.declare = blender::nodes::geo_node_mesh_primitive_ico_sphere_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_mesh_primitive_ico_sphere_exec;
+ &ntype, GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE, "Ico Sphere", NODE_CLASS_GEOMETRY);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc
index 031223b5ca6..691267bccb8 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc
@@ -23,22 +23,39 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "NOD_socket_search_link.hh"
+
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_mesh_primitive_line_cc {
-static void geo_node_mesh_primitive_line_declare(NodeDeclarationBuilder &b)
+NODE_STORAGE_FUNCS(NodeGeometryMeshLine)
+
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Int>("Count").default_value(10).min(1).max(10000);
- b.add_input<decl::Float>("Resolution").default_value(1.0f).min(0.1f).subtype(PROP_DISTANCE);
- b.add_input<decl::Vector>("Start Location").subtype(PROP_TRANSLATION);
- b.add_input<decl::Vector>("Offset").default_value({0.0f, 0.0f, 1.0f}).subtype(PROP_TRANSLATION);
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Int>(N_("Count"))
+ .default_value(10)
+ .min(1)
+ .max(10000)
+ .description(N_("Number of vertices on the line"));
+ b.add_input<decl::Float>(N_("Resolution"))
+ .default_value(1.0f)
+ .min(0.1f)
+ .subtype(PROP_DISTANCE)
+ .description(N_("Length of each individual edge"));
+ b.add_input<decl::Vector>(N_("Start Location"))
+ .subtype(PROP_TRANSLATION)
+ .description(N_("Position of the first vertex"));
+ b.add_input<decl::Vector>(N_("Offset"))
+ .default_value({0.0f, 0.0f, 1.0f})
+ .subtype(PROP_TRANSLATION)
+ .description(N_(
+ "In offset mode, the distance between each socket on each axis. In end points mode, the "
+ "position of the final vertex"));
+ b.add_output<decl::Geometry>(N_("Mesh"));
}
-static void geo_node_mesh_primitive_line_layout(uiLayout *layout,
- bContext *UNUSED(C),
- PointerRNA *ptr)
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
@@ -48,10 +65,9 @@ static void geo_node_mesh_primitive_line_layout(uiLayout *layout,
}
}
-static void geo_node_mesh_primitive_line_init(bNodeTree *UNUSED(ntree), bNode *node)
+static void node_init(bNodeTree *UNUSED(ntree), bNode *node)
{
- NodeGeometryMeshLine *node_storage = (NodeGeometryMeshLine *)MEM_callocN(
- sizeof(NodeGeometryMeshLine), __func__);
+ NodeGeometryMeshLine *node_storage = MEM_cnew<NodeGeometryMeshLine>(__func__);
node_storage->mode = GEO_NODE_MESH_LINE_MODE_OFFSET;
node_storage->count_mode = GEO_NODE_MESH_LINE_COUNT_TOTAL;
@@ -59,68 +75,74 @@ static void geo_node_mesh_primitive_line_init(bNodeTree *UNUSED(ntree), bNode *n
node->storage = node_storage;
}
-static void geo_node_mesh_primitive_line_update(bNodeTree *UNUSED(tree), bNode *node)
+static void node_update(bNodeTree *ntree, bNode *node)
{
bNodeSocket *count_socket = (bNodeSocket *)node->inputs.first;
bNodeSocket *resolution_socket = count_socket->next;
bNodeSocket *start_socket = resolution_socket->next;
bNodeSocket *end_and_offset_socket = start_socket->next;
- const NodeGeometryMeshLine &storage = *(const NodeGeometryMeshLine *)node->storage;
- const GeometryNodeMeshLineMode mode = (const GeometryNodeMeshLineMode)storage.mode;
- const GeometryNodeMeshLineCountMode count_mode = (const GeometryNodeMeshLineCountMode)
+ const NodeGeometryMeshLine &storage = node_storage(*node);
+ const GeometryNodeMeshLineMode mode = (GeometryNodeMeshLineMode)storage.mode;
+ const GeometryNodeMeshLineCountMode count_mode = (GeometryNodeMeshLineCountMode)
storage.count_mode;
node_sock_label(end_and_offset_socket,
(mode == GEO_NODE_MESH_LINE_MODE_END_POINTS) ? N_("End Location") :
N_("Offset"));
- nodeSetSocketAvailability(resolution_socket,
+ nodeSetSocketAvailability(ntree,
+ resolution_socket,
mode == GEO_NODE_MESH_LINE_MODE_END_POINTS &&
count_mode == GEO_NODE_MESH_LINE_COUNT_RESOLUTION);
- nodeSetSocketAvailability(count_socket,
+ nodeSetSocketAvailability(ntree,
+ count_socket,
mode == GEO_NODE_MESH_LINE_MODE_OFFSET ||
count_mode == GEO_NODE_MESH_LINE_COUNT_TOTAL);
}
-static void fill_edge_data(MutableSpan<MEdge> edges)
+static void node_gather_link_searches(GatherLinkSearchOpParams &params)
{
- for (const int i : edges.index_range()) {
- edges[i].v1 = i;
- edges[i].v2 = i + 1;
- edges[i].flag |= ME_LOOSEEDGE;
+ const NodeDeclaration &declaration = *params.node_type().fixed_declaration;
+ if (params.in_out() == SOCK_OUT) {
+ search_link_ops_for_declarations(params, declaration.outputs());
+ return;
}
-}
-
-Mesh *create_line_mesh(const float3 start, const float3 delta, const int count)
-{
- if (count < 1) {
- return nullptr;
+ else if (params.node_tree().typeinfo->validate_link(
+ static_cast<eNodeSocketDatatype>(params.other_socket().type), SOCK_FLOAT)) {
+ params.add_item(IFACE_("Count"), [](LinkSearchOpParams &params) {
+ bNode &node = params.add_node("GeometryNodeMeshLine");
+ node_storage(node).mode = GEO_NODE_MESH_LINE_MODE_OFFSET;
+ params.connect_available_socket(node, "Count");
+ });
+ params.add_item(IFACE_("Resolution"), [](LinkSearchOpParams &params) {
+ bNode &node = params.add_node("GeometryNodeMeshLine");
+ node_storage(node).mode = GEO_NODE_MESH_LINE_MODE_OFFSET;
+ node_storage(node).count_mode = GEO_NODE_MESH_LINE_COUNT_RESOLUTION;
+ params.connect_available_socket(node, "Resolution");
+ });
+ params.add_item(IFACE_("Start Location"), [](LinkSearchOpParams &params) {
+ bNode &node = params.add_node("GeometryNodeMeshLine");
+ params.connect_available_socket(node, "Start Location");
+ });
+ params.add_item(IFACE_("Offset"), [](LinkSearchOpParams &params) {
+ bNode &node = params.add_node("GeometryNodeMeshLine");
+ params.connect_available_socket(node, "Offset");
+ });
+ /* The last socket is reused in end points mode. */
+ params.add_item(IFACE_("End Location"), [](LinkSearchOpParams &params) {
+ bNode &node = params.add_node("GeometryNodeMeshLine");
+ node_storage(node).mode = GEO_NODE_MESH_LINE_MODE_END_POINTS;
+ params.connect_available_socket(node, "Offset");
+ });
}
-
- Mesh *mesh = BKE_mesh_new_nomain(count, count - 1, 0, 0, 0);
- BKE_id_material_eval_ensure_default_slot(&mesh->id);
- MutableSpan<MVert> verts{mesh->mvert, mesh->totvert};
- MutableSpan<MEdge> edges{mesh->medge, mesh->totedge};
-
- short normal[3];
- normal_float_to_short_v3(normal, delta.normalized());
-
- for (const int i : verts.index_range()) {
- copy_v3_v3(verts[i].co, start + delta * i);
- copy_v3_v3_short(verts[i].no, normal);
- }
-
- fill_edge_data(edges);
-
- return mesh;
}
-static void geo_node_mesh_primitive_line_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
- const NodeGeometryMeshLine &storage = *(const NodeGeometryMeshLine *)params.node().storage;
- const GeometryNodeMeshLineMode mode = (const GeometryNodeMeshLineMode)storage.mode;
- const GeometryNodeMeshLineCountMode count_mode = (const GeometryNodeMeshLineCountMode)
+ const NodeGeometryMeshLine &storage = node_storage(params.node());
+ const GeometryNodeMeshLineMode mode = (GeometryNodeMeshLineMode)storage.mode;
+ const GeometryNodeMeshLineCountMode count_mode = (GeometryNodeMeshLineCountMode)
storage.count_mode;
Mesh *mesh = nullptr;
@@ -133,8 +155,8 @@ static void geo_node_mesh_primitive_line_exec(GeoNodeExecParams params)
if (count_mode == GEO_NODE_MESH_LINE_COUNT_RESOLUTION) {
/* Don't allow asymptotic count increase for low resolution values. */
const float resolution = std::max(params.extract_input<float>("Resolution"), 0.0001f);
- const int count = total_delta.length() / resolution + 1;
- const float3 delta = total_delta.normalized() * resolution;
+ const int count = math::length(total_delta) / resolution + 1;
+ const float3 delta = math::normalize(total_delta) * resolution;
mesh = create_line_mesh(start, delta, count);
}
else if (count_mode == GEO_NODE_MESH_LINE_COUNT_TOTAL) {
@@ -154,22 +176,58 @@ static void geo_node_mesh_primitive_line_exec(GeoNodeExecParams params)
mesh = create_line_mesh(start, delta, count);
}
- params.set_output("Geometry", GeometrySet::create_with_mesh(mesh));
+ params.set_output("Mesh", GeometrySet::create_with_mesh(mesh));
+}
+
+} // namespace blender::nodes::node_geo_mesh_primitive_line_cc
+
+namespace blender::nodes {
+
+static void fill_edge_data(MutableSpan<MEdge> edges)
+{
+ for (const int i : edges.index_range()) {
+ edges[i].v1 = i;
+ edges[i].v2 = i + 1;
+ edges[i].flag |= ME_LOOSEEDGE;
+ }
+}
+
+Mesh *create_line_mesh(const float3 start, const float3 delta, const int count)
+{
+ if (count < 1) {
+ return nullptr;
+ }
+
+ Mesh *mesh = BKE_mesh_new_nomain(count, count - 1, 0, 0, 0);
+ BKE_id_material_eval_ensure_default_slot(&mesh->id);
+ MutableSpan<MVert> verts{mesh->mvert, mesh->totvert};
+ MutableSpan<MEdge> edges{mesh->medge, mesh->totedge};
+
+ for (const int i : verts.index_range()) {
+ copy_v3_v3(verts[i].co, start + delta * i);
+ }
+
+ fill_edge_data(edges);
+
+ return mesh;
}
} // namespace blender::nodes
void register_node_type_geo_mesh_primitive_line()
{
+ namespace file_ns = blender::nodes::node_geo_mesh_primitive_line_cc;
+
static bNodeType ntype;
- geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_LINE, "Mesh Line", NODE_CLASS_GEOMETRY, 0);
- ntype.declare = blender::nodes::geo_node_mesh_primitive_line_declare;
- node_type_init(&ntype, blender::nodes::geo_node_mesh_primitive_line_init);
- node_type_update(&ntype, blender::nodes::geo_node_mesh_primitive_line_update);
+ geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_LINE, "Mesh Line", NODE_CLASS_GEOMETRY);
+ ntype.declare = file_ns::node_declare;
+ node_type_init(&ntype, file_ns::node_init);
+ node_type_update(&ntype, file_ns::node_update);
node_type_storage(
&ntype, "NodeGeometryMeshLine", node_free_standard_storage, node_copy_standard_storage);
- ntype.geometry_node_execute = blender::nodes::geo_node_mesh_primitive_line_exec;
- ntype.draw_buttons = blender::nodes::geo_node_mesh_primitive_line_layout;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.draw_buttons = file_ns::node_layout;
+ ntype.gather_link_search_ops = file_ns::node_gather_link_searches;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc
index 6fd6cdf5747..751cf917f6f 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc
@@ -25,14 +25,26 @@
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_mesh_primitive_uv_sphere_cc {
-static void geo_node_mesh_primitive_uv_shpere_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Int>("Segments").default_value(32).min(3).max(1024);
- b.add_input<decl::Int>("Rings").default_value(16).min(2).max(1024);
- b.add_input<decl::Float>("Radius").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE);
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Int>(N_("Segments"))
+ .default_value(32)
+ .min(3)
+ .max(1024)
+ .description(N_("Horizontal resolution of the sphere"));
+ b.add_input<decl::Int>(N_("Rings"))
+ .default_value(16)
+ .min(2)
+ .max(1024)
+ .description(N_("The number of horizontal rings"));
+ b.add_input<decl::Float>(N_("Radius"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .subtype(PROP_DISTANCE)
+ .description(N_("Distance from the generated points to the origin"));
+ b.add_output<decl::Geometry>(N_("Mesh"));
}
static int sphere_vert_total(const int segments, const int rings)
@@ -59,7 +71,12 @@ static int sphere_face_total(const int segments, const int rings)
return quads + triangles;
}
+/**
+ * Also calculate vertex normals here, since the calculation is trivial, and it allows avoiding the
+ * calculation later, if it's necessary. The vertex normals are just the normalized positions.
+ */
static void calculate_sphere_vertex_data(MutableSpan<MVert> verts,
+ MutableSpan<float3> vert_normals,
const float radius,
const int segments,
const int rings)
@@ -68,7 +85,7 @@ static void calculate_sphere_vertex_data(MutableSpan<MVert> verts,
const float delta_phi = (2.0f * M_PI) / segments;
copy_v3_v3(verts[0].co, float3(0.0f, 0.0f, radius));
- normal_float_to_short_v3(verts[0].no, float3(0.0f, 0.0f, 1.0f));
+ vert_normals.first() = float3(0.0f, 0.0f, 1.0f);
int vert_index = 1;
for (const int ring : IndexRange(1, rings - 1)) {
@@ -80,13 +97,13 @@ static void calculate_sphere_vertex_data(MutableSpan<MVert> verts,
const float x = sin_theta * std::cos(phi);
const float y = sin_theta * std::sin(phi);
copy_v3_v3(verts[vert_index].co, float3(x, y, z) * radius);
- normal_float_to_short_v3(verts[vert_index].no, float3(x, y, z));
+ vert_normals[vert_index] = float3(x, y, z);
vert_index++;
}
}
copy_v3_v3(verts.last().co, float3(0.0f, 0.0f, -radius));
- normal_float_to_short_v3(verts.last().no, float3(0.0f, 0.0f, -1.0f));
+ vert_normals.last() = float3(0.0f, 0.0f, -1.0f);
}
static void calculate_sphere_edge_indices(MutableSpan<MEdge> edges,
@@ -166,7 +183,7 @@ static void calculate_sphere_faces(MutableSpan<MLoop> loops,
int ring_vert_index_start = 1;
int ring_edge_index_start = segments;
- for (const int UNUSED(ring) : IndexRange(1, rings - 2)) {
+ for ([[maybe_unused]] const int ring : IndexRange(1, rings - 2)) {
const int next_ring_vert_index_start = ring_vert_index_start + segments;
const int next_ring_edge_index_start = ring_edge_index_start + segments * 2;
const int ring_vertical_edge_index_start = ring_edge_index_start + segments;
@@ -267,7 +284,9 @@ static Mesh *create_uv_sphere_mesh(const float radius, const int segments, const
MutableSpan<MEdge> edges{mesh->medge, mesh->totedge};
MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly};
- calculate_sphere_vertex_data(verts, radius, segments, rings);
+ MutableSpan vert_normals{(float3 *)BKE_mesh_vertex_normals_for_write(mesh), mesh->totvert};
+ calculate_sphere_vertex_data(verts, vert_normals, radius, segments, rings);
+ BKE_mesh_vertex_normals_clear_dirty(mesh);
calculate_sphere_edge_indices(edges, segments, rings);
@@ -275,12 +294,10 @@ static Mesh *create_uv_sphere_mesh(const float radius, const int segments, const
calculate_sphere_uvs(mesh, segments, rings);
- BLI_assert(BKE_mesh_is_valid(mesh));
-
return mesh;
}
-static void geo_node_mesh_primitive_uv_sphere_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
const int segments_num = params.extract_input<int>("Segments");
const int rings_num = params.extract_input<int>("Rings");
@@ -291,25 +308,26 @@ static void geo_node_mesh_primitive_uv_sphere_exec(GeoNodeExecParams params)
if (rings_num < 3) {
params.error_message_add(NodeWarningType::Info, TIP_("Rings must be at least 3"));
}
- params.set_output("Geometry", GeometrySet());
+ params.set_default_remaining_outputs();
return;
}
const float radius = params.extract_input<float>("Radius");
Mesh *mesh = create_uv_sphere_mesh(radius, segments_num, rings_num);
- params.set_output("Geometry", GeometrySet::create_with_mesh(mesh));
+ params.set_output("Mesh", GeometrySet::create_with_mesh(mesh));
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_mesh_primitive_uv_sphere_cc
void register_node_type_geo_mesh_primitive_uv_sphere()
{
+ namespace file_ns = blender::nodes::node_geo_mesh_primitive_uv_sphere_cc;
+
static bNodeType ntype;
- geo_node_type_base(
- &ntype, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, "UV Sphere", NODE_CLASS_GEOMETRY, 0);
- ntype.declare = blender::nodes::geo_node_mesh_primitive_uv_shpere_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_mesh_primitive_uv_sphere_exec;
+ geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, "UV Sphere", NODE_CLASS_GEOMETRY);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc
index c436f5bd480..6d8a2fac8ad 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc
@@ -23,13 +23,13 @@
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_mesh_subdivide_cc {
-static void geo_node_mesh_subdivide_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Int>("Level").default_value(1).min(0).max(6);
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH);
+ b.add_input<decl::Int>(N_("Level")).default_value(1).min(0).max(6);
+ b.add_output<decl::Geometry>(N_("Mesh"));
}
static void geometry_set_mesh_subdivide(GeometrySet &geometry_set, const int level)
@@ -72,14 +72,14 @@ static void geometry_set_mesh_subdivide(GeometrySet &geometry_set, const int lev
BKE_subdiv_free(subdiv);
}
-static void geo_node_mesh_subdivide_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
- GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh");
#ifndef WITH_OPENSUBDIV
params.error_message_add(NodeWarningType::Error,
TIP_("Disabled, Blender was compiled without OpenSubdiv"));
- params.set_output("Geometry", std::move(geometry_set));
+ params.set_default_remaining_outputs();
return;
#endif
@@ -87,24 +87,26 @@ static void geo_node_mesh_subdivide_exec(GeoNodeExecParams params)
const int subdiv_level = clamp_i(params.extract_input<int>("Level"), 0, 11);
if (subdiv_level == 0) {
- params.set_output("Geometry", std::move(geometry_set));
+ params.set_output("Mesh", std::move(geometry_set));
return;
}
geometry_set.modify_geometry_sets(
[&](GeometrySet &geometry_set) { geometry_set_mesh_subdivide(geometry_set, subdiv_level); });
- params.set_output("Geometry", std::move(geometry_set));
+ params.set_output("Mesh", std::move(geometry_set));
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_mesh_subdivide_cc
void register_node_type_geo_mesh_subdivide()
{
+ namespace file_ns = blender::nodes::node_geo_mesh_subdivide_cc;
+
static bNodeType ntype;
- geo_node_type_base(&ntype, GEO_NODE_MESH_SUBDIVIDE, "Mesh Subdivide", NODE_CLASS_GEOMETRY, 0);
- ntype.declare = blender::nodes::geo_node_mesh_subdivide_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_mesh_subdivide_exec;
+ geo_node_type_base(&ntype, GEO_NODE_SUBDIVIDE_MESH, "Subdivide Mesh", NODE_CLASS_GEOMETRY);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc
new file mode 100644
index 00000000000..0f0fb3c230a
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc
@@ -0,0 +1,71 @@
+/*
+ * 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 "GEO_mesh_to_curve.hh"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_mesh_to_curve_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH);
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field();
+ b.add_output<decl::Geometry>(N_("Curve"));
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh");
+
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ if (!geometry_set.has_mesh()) {
+ geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ return;
+ }
+
+ const MeshComponent &component = *geometry_set.get_component_for_read<MeshComponent>();
+ GeometryComponentFieldContext context{component, ATTR_DOMAIN_EDGE};
+ fn::FieldEvaluator evaluator{context, component.attribute_domain_size(ATTR_DOMAIN_EDGE)};
+ evaluator.add(params.get_input<Field<bool>>("Selection"));
+ evaluator.evaluate();
+ const IndexMask selection = evaluator.get_evaluated_as_mask(0);
+ if (selection.size() == 0) {
+ geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ return;
+ }
+
+ std::unique_ptr<CurveEval> curve = geometry::mesh_to_curve_convert(component, selection);
+ geometry_set.replace_curve(curve.release());
+ geometry_set.keep_only({GEO_COMPONENT_TYPE_CURVE, GEO_COMPONENT_TYPE_INSTANCES});
+ });
+
+ params.set_output("Curve", std::move(geometry_set));
+}
+
+} // namespace blender::nodes::node_geo_mesh_to_curve_cc
+
+void register_node_type_geo_mesh_to_curve()
+{
+ namespace file_ns = blender::nodes::node_geo_mesh_to_curve_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_MESH_TO_CURVE, "Mesh to Curve", NODE_CLASS_GEOMETRY);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc
index 2f59a3c968b..d0546cd2583 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc
@@ -26,31 +26,32 @@
using blender::Array;
-namespace blender::nodes {
+namespace blender::nodes::node_geo_mesh_to_points_cc {
-static void geo_node_mesh_to_points_declare(NodeDeclarationBuilder &b)
+NODE_STORAGE_FUNCS(NodeGeometryMeshToPoints)
+
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Mesh");
- b.add_input<decl::Vector>("Position").implicit_field();
- b.add_input<decl::Float>("Radius")
+ b.add_input<decl::Geometry>(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH);
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).supports_field().hide_value();
+ b.add_input<decl::Vector>(N_("Position")).implicit_field();
+ b.add_input<decl::Float>(N_("Radius"))
.default_value(0.05f)
.min(0.0f)
.subtype(PROP_DISTANCE)
.supports_field();
- b.add_input<decl::Bool>("Selection").default_value(true).supports_field().hide_value();
- b.add_output<decl::Geometry>("Points");
+ b.add_output<decl::Geometry>(N_("Points"));
}
-static void geo_node_mesh_to_points_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiItemR(layout, ptr, "mode", 0, "", ICON_NONE);
}
-static void geo_node_mesh_to_points_init(bNodeTree *UNUSED(tree), bNode *node)
+static void node_init(bNodeTree *UNUSED(tree), bNode *node)
{
- NodeGeometryMeshToPoints *data = (NodeGeometryMeshToPoints *)MEM_callocN(
- sizeof(NodeGeometryMeshToPoints), __func__);
- data->mode = GEO_NODE_MESH_TO_POINTS_FACES;
+ NodeGeometryMeshToPoints *data = MEM_cnew<NodeGeometryMeshToPoints>(__func__);
+ data->mode = GEO_NODE_MESH_TO_POINTS_VERTICES;
node->storage = data;
}
@@ -81,10 +82,15 @@ static void geometry_set_mesh_to_points(GeometrySet &geometry_set,
geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
return;
}
- fn::FieldEvaluator selection_evaluator{field_context, domain_size};
- selection_evaluator.add(selection_field);
- selection_evaluator.evaluate();
- const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0);
+ fn::FieldEvaluator evaluator{field_context, domain_size};
+ evaluator.set_selection(selection_field);
+ /* Evaluating directly into the point cloud doesn't work because we are not using the full
+ * "min_array_size" array but compressing the selected elements into the final array with no
+ * gaps. */
+ evaluator.add(position_field);
+ evaluator.add(radius_field);
+ evaluator.evaluate();
+ const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
PointCloud *pointcloud = BKE_pointcloud_new_nomain(selection.size());
uninitialized_fill_n(pointcloud->radius, pointcloud->totpoint, 0.05f);
@@ -92,13 +98,6 @@ static void geometry_set_mesh_to_points(GeometrySet &geometry_set,
PointCloudComponent &point_component =
geometry_set.get_component_for_write<PointCloudComponent>();
- /* Evaluating directly into the point cloud doesn't work because we are not using the full
- * "min_array_size" array but compressing the selected elements into the final array with no
- * gaps. */
- fn::FieldEvaluator evaluator{field_context, &selection};
- evaluator.add(position_field);
- evaluator.add(radius_field);
- evaluator.evaluate();
copy_attribute_to_points(evaluator.get_evaluated<float3>(0),
selection,
{(float3 *)pointcloud->co, pointcloud->totpoint});
@@ -113,14 +112,14 @@ static void geometry_set_mesh_to_points(GeometrySet &geometry_set,
for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
const AttributeIDRef attribute_id = entry.key;
const CustomDataType data_type = entry.value.data_type;
- GVArrayPtr src = mesh_component->attribute_get_for_read(attribute_id, domain, data_type);
+ GVArray src = mesh_component->attribute_get_for_read(attribute_id, domain, data_type);
OutputAttribute dst = point_component.attribute_try_get_for_output_only(
attribute_id, ATTR_DOMAIN_POINT, data_type);
if (dst && src) {
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
using T = decltype(dummy);
- GVArray_Typed<T> src_typed{*src};
- copy_attribute_to_points(*src_typed, selection, dst.as_span().typed<T>());
+ VArray<T> src_typed = src.typed<T>();
+ copy_attribute_to_points(src_typed, selection, dst.as_span().typed<T>());
});
dst.save();
}
@@ -129,7 +128,7 @@ static void geometry_set_mesh_to_points(GeometrySet &geometry_set,
geometry_set.keep_only({GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_INSTANCES});
}
-static void geo_node_mesh_to_points_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh");
Field<float3> position = params.extract_input<Field<float3>>("Position");
@@ -144,8 +143,7 @@ static void geo_node_mesh_to_points_exec(GeoNodeExecParams params)
FieldOperation(max_zero_fn, {std::move(radius)}));
Field<float> positive_radius(std::move(max_zero_op), 0);
- const NodeGeometryMeshToPoints &storage =
- *(const NodeGeometryMeshToPoints *)params.node().storage;
+ const NodeGeometryMeshToPoints &storage = node_storage(params.node());
const GeometryNodeMeshToPointsMode mode = (GeometryNodeMeshToPointsMode)storage.mode;
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
@@ -172,17 +170,19 @@ static void geo_node_mesh_to_points_exec(GeoNodeExecParams params)
params.set_output("Points", std::move(geometry_set));
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_mesh_to_points_cc
void register_node_type_geo_mesh_to_points()
{
+ namespace file_ns = blender::nodes::node_geo_mesh_to_points_cc;
+
static bNodeType ntype;
- geo_node_type_base(&ntype, GEO_NODE_MESH_TO_POINTS, "Mesh to Points", NODE_CLASS_GEOMETRY, 0);
- ntype.declare = blender::nodes::geo_node_mesh_to_points_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_mesh_to_points_exec;
- node_type_init(&ntype, blender::nodes::geo_node_mesh_to_points_init);
- ntype.draw_buttons = blender::nodes::geo_node_mesh_to_points_layout;
+ geo_node_type_base(&ntype, GEO_NODE_MESH_TO_POINTS, "Mesh to Points", NODE_CLASS_GEOMETRY);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ node_type_init(&ntype, file_ns::node_init);
+ ntype.draw_buttons = file_ns::node_layout;
node_type_storage(
&ntype, "NodeGeometryMeshToPoints", node_free_standard_storage, node_copy_standard_storage);
nodeRegisterType(&ntype);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_object_info.cc b/source/blender/nodes/geometry/nodes/node_geo_object_info.cc
index 389acc40f0f..d32875d2627 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_object_info.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_object_info.cc
@@ -21,92 +21,106 @@
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_object_info_cc {
-static void geo_node_object_info_declare(NodeDeclarationBuilder &b)
+NODE_STORAGE_FUNCS(NodeGeometryObjectInfo)
+
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Object>("Object").hide_label();
- b.add_output<decl::Vector>("Location");
- b.add_output<decl::Vector>("Rotation");
- b.add_output<decl::Vector>("Scale");
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Object>(N_("Object")).hide_label();
+ b.add_input<decl::Bool>(N_("As Instance"))
+ .description(N_("Output the entire object as single instance. "
+ "This allows instancing non-geometry object types"));
+ b.add_output<decl::Vector>(N_("Location"));
+ b.add_output<decl::Vector>(N_("Rotation"));
+ b.add_output<decl::Vector>(N_("Scale"));
+ b.add_output<decl::Geometry>(N_("Geometry"));
}
-static void geo_node_object_info_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiItemR(layout, ptr, "transform_space", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
}
-static void geo_node_object_info_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
- const bNode &bnode = params.node();
- NodeGeometryObjectInfo *node_storage = (NodeGeometryObjectInfo *)bnode.storage;
- const bool transform_space_relative = (node_storage->transform_space ==
+ const NodeGeometryObjectInfo &storage = node_storage(params.node());
+ const bool transform_space_relative = (storage.transform_space ==
GEO_NODE_TRANSFORM_SPACE_RELATIVE);
Object *object = params.get_input<Object *>("Object");
- float3 location = {0, 0, 0};
- float3 rotation = {0, 0, 0};
- float3 scale = {0, 0, 0};
- GeometrySet geometry_set;
-
const Object *self_object = params.self_object();
+ if (object == nullptr) {
+ params.set_default_remaining_outputs();
+ return;
+ }
- if (object != nullptr) {
- float transform[4][4];
- mul_m4_m4m4(transform, self_object->imat, object->obmat);
+ const float4x4 &object_matrix = object->obmat;
+ const float4x4 transform = float4x4(self_object->imat) * object_matrix;
- float quaternion[4];
- if (transform_space_relative) {
- mat4_decompose(location, quaternion, scale, transform);
- }
- else {
- mat4_decompose(location, quaternion, scale, object->obmat);
+ if (transform_space_relative) {
+ params.set_output("Location", transform.translation());
+ params.set_output("Rotation", transform.to_euler());
+ params.set_output("Scale", transform.scale());
+ }
+ else {
+ params.set_output("Location", object_matrix.translation());
+ params.set_output("Rotation", object_matrix.to_euler());
+ params.set_output("Scale", object_matrix.scale());
+ }
+
+ if (params.output_is_required("Geometry")) {
+ if (object == self_object) {
+ params.error_message_add(NodeWarningType::Error,
+ TIP_("Geometry cannot be retrieved from the modifier object"));
+ params.set_default_remaining_outputs();
+ return;
}
- quat_to_eul(rotation, quaternion);
- if (object != self_object) {
+ GeometrySet geometry_set;
+ if (params.get_input<bool>("As Instance")) {
InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>();
const int handle = instances.add_reference(*object);
-
if (transform_space_relative) {
instances.add_instance(handle, transform);
}
else {
- float unit_transform[4][4];
- unit_m4(unit_transform);
- instances.add_instance(handle, unit_transform);
+ instances.add_instance(handle, float4x4::identity());
+ }
+ }
+ else {
+ geometry_set = bke::object_get_evaluated_geometry_set(*object);
+ if (transform_space_relative) {
+ transform_geometry_set(geometry_set, transform, *params.depsgraph());
}
}
- }
- params.set_output("Location", location);
- params.set_output("Rotation", rotation);
- params.set_output("Scale", scale);
- params.set_output("Geometry", geometry_set);
+ params.set_output("Geometry", geometry_set);
+ }
}
-static void geo_node_object_info_node_init(bNodeTree *UNUSED(tree), bNode *node)
+static void node_node_init(bNodeTree *UNUSED(tree), bNode *node)
{
- NodeGeometryObjectInfo *data = (NodeGeometryObjectInfo *)MEM_callocN(
- sizeof(NodeGeometryObjectInfo), __func__);
+ NodeGeometryObjectInfo *data = MEM_cnew<NodeGeometryObjectInfo>(__func__);
data->transform_space = GEO_NODE_TRANSFORM_SPACE_ORIGINAL;
node->storage = data;
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_object_info_cc
void register_node_type_geo_object_info()
{
+ namespace file_ns = blender::nodes::node_geo_object_info_cc;
+
static bNodeType ntype;
- geo_node_type_base(&ntype, GEO_NODE_OBJECT_INFO, "Object Info", NODE_CLASS_INPUT, 0);
- node_type_init(&ntype, blender::nodes::geo_node_object_info_node_init);
+ geo_node_type_base(&ntype, GEO_NODE_OBJECT_INFO, "Object Info", NODE_CLASS_INPUT);
+ node_type_init(&ntype, file_ns::node_node_init);
node_type_storage(
&ntype, "NodeGeometryObjectInfo", node_free_standard_storage, node_copy_standard_storage);
- ntype.geometry_node_execute = blender::nodes::geo_node_object_info_exec;
- ntype.draw_buttons = blender::nodes::geo_node_object_info_layout;
- ntype.declare = blender::nodes::geo_node_object_info_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.draw_buttons = file_ns::node_layout;
+ ntype.declare = file_ns::node_declare;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc
index afd0ced6360..f3da591f684 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc
@@ -21,15 +21,15 @@
#include "node_geometry_util.hh"
-using blender::Array;
+namespace blender::nodes::node_geo_points_to_vertices_cc {
-namespace blender::nodes {
+using blender::Array;
-static void geo_node_points_to_vertices_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Points");
- b.add_input<decl::Bool>("Selection").default_value(true).supports_field().hide_value();
- b.add_output<decl::Geometry>("Mesh");
+ b.add_input<decl::Geometry>(N_("Points")).supported_type(GEO_COMPONENT_TYPE_POINT_CLOUD);
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).supports_field().hide_value();
+ b.add_output<decl::Geometry>(N_("Mesh"));
}
template<typename T>
@@ -74,15 +74,15 @@ static void geometry_set_points_to_vertices(GeometrySet &geometry_set,
for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
const AttributeIDRef attribute_id = entry.key;
const CustomDataType data_type = entry.value.data_type;
- GVArrayPtr src = point_component->attribute_get_for_read(
+ GVArray src = point_component->attribute_get_for_read(
attribute_id, ATTR_DOMAIN_POINT, data_type);
OutputAttribute dst = mesh_component.attribute_try_get_for_output_only(
attribute_id, ATTR_DOMAIN_POINT, data_type);
if (dst && src) {
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
using T = decltype(dummy);
- GVArray_Typed<T> src_typed{*src};
- VArray_Span<T> src_typed_span{*src_typed};
+ VArray<T> src_typed = src.typed<T>();
+ VArray_Span<T> src_typed_span{src_typed};
copy_attribute_to_vertices(src_typed_span, selection, dst.as_span().typed<T>());
});
dst.save();
@@ -92,7 +92,7 @@ static void geometry_set_points_to_vertices(GeometrySet &geometry_set,
geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES});
}
-static void geo_node_points_to_vertices_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Points");
Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
@@ -104,15 +104,17 @@ static void geo_node_points_to_vertices_exec(GeoNodeExecParams params)
params.set_output("Mesh", std::move(geometry_set));
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_points_to_vertices_cc
void register_node_type_geo_points_to_vertices()
{
+ namespace file_ns = blender::nodes::node_geo_points_to_vertices_cc;
+
static bNodeType ntype;
geo_node_type_base(
- &ntype, GEO_NODE_POINTS_TO_VERTICES, "Points to Vertices", NODE_CLASS_GEOMETRY, 0);
- ntype.declare = blender::nodes::geo_node_points_to_vertices_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_points_to_vertices_exec;
+ &ntype, GEO_NODE_POINTS_TO_VERTICES, "Points to Vertices", NODE_CLASS_GEOMETRY);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc
new file mode 100644
index 00000000000..c165bcf8e35
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc
@@ -0,0 +1,284 @@
+/*
+ * 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.
+ */
+
+#ifdef WITH_OPENVDB
+# include <openvdb/openvdb.h>
+# include <openvdb/tools/LevelSetUtil.h>
+# include <openvdb/tools/ParticlesToLevelSet.h>
+#endif
+
+#include "node_geometry_util.hh"
+
+#include "BKE_lib_id.h"
+#include "BKE_volume.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+namespace blender::nodes::node_geo_points_to_volume_cc {
+
+NODE_STORAGE_FUNCS(NodeGeometryPointsToVolume)
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>(N_("Points"));
+ b.add_input<decl::Float>(N_("Density")).default_value(1.0f).min(0.0f);
+ b.add_input<decl::Float>(N_("Voxel Size"))
+ .default_value(0.3f)
+ .min(0.01f)
+ .subtype(PROP_DISTANCE)
+ .make_available([](bNode &node) {
+ node_storage(node).resolution_mode = GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_SIZE;
+ });
+ b.add_input<decl::Float>(N_("Voxel Amount"))
+ .default_value(64.0f)
+ .min(0.0f)
+ .make_available([](bNode &node) {
+ node_storage(node).resolution_mode = GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_AMOUNT;
+ });
+ b.add_input<decl::Float>(N_("Radius"))
+ .default_value(0.5f)
+ .min(0.0f)
+ .subtype(PROP_DISTANCE)
+ .supports_field();
+ b.add_output<decl::Geometry>(N_("Volume"));
+}
+
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
+ uiItemR(layout, ptr, "resolution_mode", 0, IFACE_("Resolution"), ICON_NONE);
+}
+
+static void node_init(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeGeometryPointsToVolume *data = MEM_cnew<NodeGeometryPointsToVolume>(__func__);
+ data->resolution_mode = GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_AMOUNT;
+ node->storage = data;
+}
+
+static void node_update(bNodeTree *ntree, bNode *node)
+{
+ const NodeGeometryPointsToVolume &storage = node_storage(*node);
+ bNodeSocket *voxel_size_socket = nodeFindSocket(node, SOCK_IN, "Voxel Size");
+ bNodeSocket *voxel_amount_socket = nodeFindSocket(node, SOCK_IN, "Voxel Amount");
+ nodeSetSocketAvailability(ntree,
+ voxel_amount_socket,
+ storage.resolution_mode ==
+ GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_AMOUNT);
+ nodeSetSocketAvailability(ntree,
+ voxel_size_socket,
+ storage.resolution_mode ==
+ GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_SIZE);
+}
+
+#ifdef WITH_OPENVDB
+namespace {
+/* Implements the interface required by #openvdb::tools::ParticlesToLevelSet. */
+struct ParticleList {
+ using PosType = openvdb::Vec3R;
+
+ Span<float3> positions;
+ Span<float> radii;
+
+ size_t size() const
+ {
+ return (size_t)positions.size();
+ }
+
+ void getPos(size_t n, openvdb::Vec3R &xyz) const
+ {
+ xyz = &positions[n].x;
+ }
+
+ void getPosRad(size_t n, openvdb::Vec3R &xyz, openvdb::Real &radius) const
+ {
+ xyz = &positions[n].x;
+ radius = radii[n];
+ }
+};
+} // namespace
+
+static openvdb::FloatGrid::Ptr generate_volume_from_points(const Span<float3> positions,
+ const Span<float> radii,
+ const float density)
+{
+ /* Create a new grid that will be filled. #ParticlesToLevelSet requires the background value to
+ * be positive. It will be set to zero later on. */
+ openvdb::FloatGrid::Ptr new_grid = openvdb::FloatGrid::create(1.0f);
+
+ /* Create a narrow-band level set grid based on the positions and radii. */
+ openvdb::tools::ParticlesToLevelSet op{*new_grid};
+ /* Don't ignore particles based on their radius. */
+ op.setRmin(0.0f);
+ op.setRmax(FLT_MAX);
+ ParticleList particles{positions, radii};
+ op.rasterizeSpheres(particles);
+ op.finalize();
+
+ /* Convert the level set to a fog volume. This also sets the background value to zero. Inside the
+ * fog there will be a density of 1. */
+ openvdb::tools::sdfToFogVolume(*new_grid);
+
+ /* Take the desired density into account. */
+ openvdb::tools::foreach (new_grid->beginValueOn(),
+ [&](const openvdb::FloatGrid::ValueOnIter &iter) {
+ iter.modifyValue([&](float &value) { value *= density; });
+ });
+ return new_grid;
+}
+
+static float compute_voxel_size(const GeoNodeExecParams &params,
+ Span<float3> positions,
+ const float radius)
+{
+ const NodeGeometryPointsToVolume &storage = node_storage(params.node());
+
+ if (storage.resolution_mode == GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_SIZE) {
+ return params.get_input<float>("Voxel Size");
+ }
+
+ if (positions.is_empty()) {
+ return 0.0f;
+ }
+
+ float3 min, max;
+ INIT_MINMAX(min, max);
+ minmax_v3v3_v3_array(min, max, (float(*)[3])positions.data(), positions.size());
+
+ const float voxel_amount = params.get_input<float>("Voxel Amount");
+ if (voxel_amount <= 1) {
+ return 0.0f;
+ }
+
+ /* The voxel size adapts to the final size of the volume. */
+ const float diagonal = math::distance(min, max);
+ const float extended_diagonal = diagonal + 2.0f * radius;
+ const float voxel_size = extended_diagonal / voxel_amount;
+ return voxel_size;
+}
+
+static void gather_point_data_from_component(GeoNodeExecParams &params,
+ const GeometryComponent &component,
+ Vector<float3> &r_positions,
+ Vector<float> &r_radii)
+{
+ VArray<float3> positions = component.attribute_get_for_read<float3>(
+ "position", ATTR_DOMAIN_POINT, {0, 0, 0});
+
+ Field<float> radius_field = params.get_input<Field<float>>("Radius");
+ GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
+ const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT);
+
+ r_positions.resize(r_positions.size() + domain_size);
+ positions.materialize(r_positions.as_mutable_span().take_back(domain_size));
+
+ r_radii.resize(r_radii.size() + domain_size);
+ fn::FieldEvaluator evaluator{field_context, domain_size};
+ evaluator.add_with_destination(radius_field, r_radii.as_mutable_span().take_back(domain_size));
+ evaluator.evaluate();
+}
+
+static void convert_to_grid_index_space(const float voxel_size,
+ MutableSpan<float3> positions,
+ MutableSpan<float> radii)
+{
+ const float voxel_size_inv = 1.0f / voxel_size;
+ for (const int i : positions.index_range()) {
+ positions[i] *= voxel_size_inv;
+ /* Better align generated grid with source points. */
+ positions[i] -= float3(0.5f);
+ radii[i] *= voxel_size_inv;
+ }
+}
+
+static void initialize_volume_component_from_points(GeoNodeExecParams &params,
+ GeometrySet &r_geometry_set)
+{
+ Vector<float3> positions;
+ Vector<float> radii;
+
+ if (r_geometry_set.has<MeshComponent>()) {
+ gather_point_data_from_component(
+ params, *r_geometry_set.get_component_for_read<MeshComponent>(), positions, radii);
+ }
+ if (r_geometry_set.has<PointCloudComponent>()) {
+ gather_point_data_from_component(
+ params, *r_geometry_set.get_component_for_read<PointCloudComponent>(), positions, radii);
+ }
+ if (r_geometry_set.has<CurveComponent>()) {
+ gather_point_data_from_component(
+ params, *r_geometry_set.get_component_for_read<CurveComponent>(), positions, radii);
+ }
+
+ const float max_radius = *std::max_element(radii.begin(), radii.end());
+ const float voxel_size = compute_voxel_size(params, positions, max_radius);
+ if (voxel_size == 0.0f || positions.is_empty()) {
+ return;
+ }
+
+ Volume *volume = (Volume *)BKE_id_new_nomain(ID_VO, nullptr);
+ BKE_volume_init_grids(volume);
+
+ const float density = params.get_input<float>("Density");
+ convert_to_grid_index_space(voxel_size, positions, radii);
+ openvdb::FloatGrid::Ptr new_grid = generate_volume_from_points(positions, radii, density);
+ new_grid->transform().postScale(voxel_size);
+ BKE_volume_grid_add_vdb(*volume, "density", std::move(new_grid));
+
+ r_geometry_set.keep_only({GEO_COMPONENT_TYPE_VOLUME, GEO_COMPONENT_TYPE_INSTANCES});
+ r_geometry_set.replace_volume(volume);
+}
+#endif
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Points");
+
+#ifdef WITH_OPENVDB
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ initialize_volume_component_from_points(params, geometry_set);
+ });
+ params.set_output("Volume", std::move(geometry_set));
+#else
+ params.error_message_add(NodeWarningType::Error,
+ TIP_("Disabled, Blender was compiled without OpenVDB"));
+ params.set_default_remaining_outputs();
+#endif
+}
+
+} // namespace blender::nodes::node_geo_points_to_volume_cc
+
+void register_node_type_geo_points_to_volume()
+{
+ namespace file_ns = blender::nodes::node_geo_points_to_volume_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_POINTS_TO_VOLUME, "Points to Volume", NODE_CLASS_GEOMETRY);
+ node_type_storage(&ntype,
+ "NodeGeometryPointsToVolume",
+ node_free_standard_storage,
+ node_copy_standard_storage);
+ node_type_size(&ntype, 170, 120, 700);
+ node_type_init(&ntype, file_ns::node_init);
+ node_type_update(&ntype, file_ns::node_update);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.draw_buttons = file_ns::node_layout;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_proximity.cc b/source/blender/nodes/geometry/nodes/node_geo_proximity.cc
index 2b1de5fbf95..3f509942f7c 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_proximity.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_proximity.cc
@@ -27,30 +27,33 @@
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_proximity_cc {
-static void geo_node_proximity_declare(NodeDeclarationBuilder &b)
+NODE_STORAGE_FUNCS(NodeGeometryProximity)
+
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Vector>("Source Position").implicit_field();
- b.add_input<decl::Geometry>("Target");
- b.add_output<decl::Vector>("Position").dependent_field();
- b.add_output<decl::Float>("Distance").dependent_field();
+ b.add_input<decl::Geometry>(N_("Target"))
+ .only_realized_data()
+ .supported_type({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD});
+ b.add_input<decl::Vector>(N_("Source Position")).implicit_field();
+ b.add_output<decl::Vector>(N_("Position")).dependent_field();
+ b.add_output<decl::Float>(N_("Distance")).dependent_field();
}
-static void geo_node_proximity_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiItemR(layout, ptr, "target_element", 0, "", ICON_NONE);
}
static void geo_proximity_init(bNodeTree *UNUSED(ntree), bNode *node)
{
- NodeGeometryProximity *node_storage = (NodeGeometryProximity *)MEM_callocN(
- sizeof(NodeGeometryProximity), __func__);
+ NodeGeometryProximity *node_storage = MEM_cnew<NodeGeometryProximity>(__func__);
node_storage->target_element = GEO_NODE_PROX_TARGET_FACES;
node->storage = node_storage;
}
-static void calculate_mesh_proximity(const VArray<float3> &positions,
+static bool calculate_mesh_proximity(const VArray<float3> &positions,
const IndexMask mask,
const Mesh &mesh,
const GeometryNodeProximityTargetType type,
@@ -71,7 +74,7 @@ static void calculate_mesh_proximity(const VArray<float3> &positions,
}
if (bvh_data.tree == nullptr) {
- return;
+ return false;
}
threading::parallel_for(mask.index_range(), 512, [&](IndexRange range) {
@@ -82,7 +85,7 @@ static void calculate_mesh_proximity(const VArray<float3> &positions,
for (int i : range) {
const int index = mask[i];
/* Use the distance to the last found point as upper bound to speedup the bvh lookup. */
- nearest.dist_sq = float3::distance_squared(nearest.co, positions[index]);
+ nearest.dist_sq = math::distance_squared(float3(nearest.co), positions[index]);
BLI_bvhtree_find_nearest(
bvh_data.tree, positions[index], &nearest, bvh_data.nearest_callback, &bvh_data);
@@ -97,18 +100,19 @@ static void calculate_mesh_proximity(const VArray<float3> &positions,
});
free_bvhtree_from_mesh(&bvh_data);
+ return true;
}
-static void calculate_pointcloud_proximity(const VArray<float3> &positions,
+static bool calculate_pointcloud_proximity(const VArray<float3> &positions,
const IndexMask mask,
const PointCloud &pointcloud,
- const MutableSpan<float> r_distances,
- const MutableSpan<float3> r_locations)
+ MutableSpan<float> r_distances,
+ MutableSpan<float3> r_locations)
{
BVHTreeFromPointCloud bvh_data;
BKE_bvhtree_from_pointcloud_get(&bvh_data, &pointcloud, 2);
if (bvh_data.tree == nullptr) {
- return;
+ return false;
}
threading::parallel_for(mask.index_range(), 512, [&](IndexRange range) {
@@ -136,6 +140,7 @@ static void calculate_pointcloud_proximity(const VArray<float3> &positions,
});
free_bvhtree_from_pointcloud(&bvh_data);
+ return true;
}
class ProximityFunction : public fn::MultiFunction {
@@ -172,18 +177,29 @@ class ProximityFunction : public fn::MultiFunction {
* comparison per vertex, so it's likely not worth it. */
MutableSpan<float> distances = params.uninitialized_single_output<float>(2, "Distance");
- distances.fill(FLT_MAX);
+ distances.fill_indices(mask, FLT_MAX);
+ bool success = false;
if (target_.has_mesh()) {
- calculate_mesh_proximity(
+ success |= calculate_mesh_proximity(
src_positions, mask, *target_.get_mesh_for_read(), type_, distances, positions);
}
if (target_.has_pointcloud() && type_ == GEO_NODE_PROX_TARGET_POINTS) {
- calculate_pointcloud_proximity(
+ success |= calculate_pointcloud_proximity(
src_positions, mask, *target_.get_pointcloud_for_read(), distances, positions);
}
+ if (!success) {
+ if (!positions.is_empty()) {
+ positions.fill_indices(mask, float3(0));
+ }
+ if (!distances.is_empty()) {
+ distances.fill_indices(mask, 0.0f);
+ }
+ return;
+ }
+
if (params.single_output_is_required(2, "Distance")) {
threading::parallel_for(mask.index_range(), 2048, [&](IndexRange range) {
for (const int i : range) {
@@ -195,17 +211,17 @@ class ProximityFunction : public fn::MultiFunction {
}
};
-static void geo_node_proximity_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set_target = params.extract_input<GeometrySet>("Target");
+ geometry_set_target.ensure_owns_direct_data();
if (!geometry_set_target.has_mesh() && !geometry_set_target.has_pointcloud()) {
- params.set_output("Position", fn::make_constant_field<float3>({0.0f, 0.0f, 0.0f}));
- params.set_output("Distance", fn::make_constant_field<float>({0.0f}));
+ params.set_default_remaining_outputs();
return;
}
- const NodeGeometryProximity &storage = *(const NodeGeometryProximity *)params.node().storage;
+ const NodeGeometryProximity &storage = node_storage(params.node());
Field<float3> position_field = params.extract_input<Field<float3>>("Source Position");
auto proximity_fn = std::make_unique<ProximityFunction>(
@@ -218,18 +234,20 @@ static void geo_node_proximity_exec(GeoNodeExecParams params)
params.set_output("Distance", Field<float>(proximity_op, 1));
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_proximity_cc
void register_node_type_geo_proximity()
{
+ namespace file_ns = blender::nodes::node_geo_proximity_cc;
+
static bNodeType ntype;
- geo_node_type_base(&ntype, GEO_NODE_PROXIMITY, "Geometry Proximity", NODE_CLASS_GEOMETRY, 0);
- node_type_init(&ntype, blender::nodes::geo_proximity_init);
+ geo_node_type_base(&ntype, GEO_NODE_PROXIMITY, "Geometry Proximity", NODE_CLASS_GEOMETRY);
+ node_type_init(&ntype, file_ns::geo_proximity_init);
node_type_storage(
&ntype, "NodeGeometryProximity", node_free_standard_storage, node_copy_standard_storage);
- ntype.declare = blender::nodes::geo_node_proximity_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_proximity_exec;
- ntype.draw_buttons = blender::nodes::geo_node_proximity_layout;
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.draw_buttons = file_ns::node_layout;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc
new file mode 100644
index 00000000000..c38503f688c
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc
@@ -0,0 +1,462 @@
+/*
+ * 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 "DNA_mesh_types.h"
+
+#include "BKE_attribute_math.hh"
+#include "BKE_bvhutils.h"
+#include "BKE_mesh_sample.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "NOD_socket_search_link.hh"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_raycast_cc {
+
+using namespace blender::bke::mesh_surface_sample;
+
+NODE_STORAGE_FUNCS(NodeGeometryRaycast)
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>(N_("Target Geometry"))
+ .only_realized_data()
+ .supported_type(GEO_COMPONENT_TYPE_MESH);
+
+ b.add_input<decl::Vector>(N_("Attribute")).hide_value().supports_field();
+ b.add_input<decl::Float>(N_("Attribute"), "Attribute_001").hide_value().supports_field();
+ b.add_input<decl::Color>(N_("Attribute"), "Attribute_002").hide_value().supports_field();
+ b.add_input<decl::Bool>(N_("Attribute"), "Attribute_003").hide_value().supports_field();
+ b.add_input<decl::Int>(N_("Attribute"), "Attribute_004").hide_value().supports_field();
+
+ b.add_input<decl::Vector>(N_("Source Position")).implicit_field();
+ b.add_input<decl::Vector>(N_("Ray Direction"))
+ .default_value({0.0f, 0.0f, -1.0f})
+ .supports_field();
+ b.add_input<decl::Float>(N_("Ray Length"))
+ .default_value(100.0f)
+ .min(0.0f)
+ .subtype(PROP_DISTANCE)
+ .supports_field();
+
+ b.add_output<decl::Bool>(N_("Is Hit")).dependent_field();
+ b.add_output<decl::Vector>(N_("Hit Position")).dependent_field();
+ b.add_output<decl::Vector>(N_("Hit Normal")).dependent_field();
+ b.add_output<decl::Float>(N_("Hit Distance")).dependent_field();
+
+ b.add_output<decl::Vector>(N_("Attribute")).dependent_field({1, 2, 3, 4, 5, 6});
+ b.add_output<decl::Float>(N_("Attribute"), "Attribute_001").dependent_field({1, 2, 3, 4, 5, 6});
+ b.add_output<decl::Color>(N_("Attribute"), "Attribute_002").dependent_field({1, 2, 3, 4, 5, 6});
+ b.add_output<decl::Bool>(N_("Attribute"), "Attribute_003").dependent_field({1, 2, 3, 4, 5, 6});
+ b.add_output<decl::Int>(N_("Attribute"), "Attribute_004").dependent_field({1, 2, 3, 4, 5, 6});
+}
+
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE);
+ uiItemR(layout, ptr, "mapping", 0, "", ICON_NONE);
+}
+
+static void node_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometryRaycast *data = MEM_cnew<NodeGeometryRaycast>(__func__);
+ data->mapping = GEO_NODE_RAYCAST_INTERPOLATED;
+ data->data_type = CD_PROP_FLOAT;
+ node->storage = data;
+}
+
+static void node_update(bNodeTree *ntree, bNode *node)
+{
+ const NodeGeometryRaycast &storage = node_storage(*node);
+ const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type);
+
+ bNodeSocket *socket_vector = (bNodeSocket *)BLI_findlink(&node->inputs, 1);
+ bNodeSocket *socket_float = socket_vector->next;
+ bNodeSocket *socket_color4f = socket_float->next;
+ bNodeSocket *socket_boolean = socket_color4f->next;
+ bNodeSocket *socket_int32 = socket_boolean->next;
+
+ nodeSetSocketAvailability(ntree, socket_vector, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(ntree, socket_float, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(ntree, socket_color4f, data_type == CD_PROP_COLOR);
+ nodeSetSocketAvailability(ntree, socket_boolean, data_type == CD_PROP_BOOL);
+ nodeSetSocketAvailability(ntree, socket_int32, data_type == CD_PROP_INT32);
+
+ bNodeSocket *out_socket_vector = (bNodeSocket *)BLI_findlink(&node->outputs, 4);
+ bNodeSocket *out_socket_float = out_socket_vector->next;
+ bNodeSocket *out_socket_color4f = out_socket_float->next;
+ bNodeSocket *out_socket_boolean = out_socket_color4f->next;
+ bNodeSocket *out_socket_int32 = out_socket_boolean->next;
+
+ nodeSetSocketAvailability(ntree, out_socket_vector, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(ntree, out_socket_float, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(ntree, out_socket_color4f, data_type == CD_PROP_COLOR);
+ nodeSetSocketAvailability(ntree, out_socket_boolean, data_type == CD_PROP_BOOL);
+ nodeSetSocketAvailability(ntree, out_socket_int32, data_type == CD_PROP_INT32);
+}
+
+static void node_gather_link_searches(GatherLinkSearchOpParams &params)
+{
+ const NodeDeclaration &declaration = *params.node_type().fixed_declaration;
+ search_link_ops_for_declarations(params, declaration.inputs().take_front(1));
+ search_link_ops_for_declarations(params, declaration.inputs().take_back(3));
+ search_link_ops_for_declarations(params, declaration.outputs().take_front(4));
+
+ const std::optional<CustomDataType> type = node_data_type_to_custom_data_type(
+ (eNodeSocketDatatype)params.other_socket().type);
+ if (type && *type != CD_PROP_STRING) {
+ /* The input and output sockets have the same name. */
+ params.add_item(IFACE_("Attribute"), [type](LinkSearchOpParams &params) {
+ bNode &node = params.add_node("GeometryNodeRaycast");
+ node_storage(node).data_type = *type;
+ params.update_and_connect_available_socket(node, "Attribute");
+ });
+ }
+}
+
+static eAttributeMapMode get_map_mode(GeometryNodeRaycastMapMode map_mode)
+{
+ switch (map_mode) {
+ case GEO_NODE_RAYCAST_INTERPOLATED:
+ return eAttributeMapMode::INTERPOLATED;
+ default:
+ case GEO_NODE_RAYCAST_NEAREST:
+ return eAttributeMapMode::NEAREST;
+ }
+}
+
+static void raycast_to_mesh(IndexMask mask,
+ const Mesh &mesh,
+ const VArray<float3> &ray_origins,
+ const VArray<float3> &ray_directions,
+ const VArray<float> &ray_lengths,
+ const MutableSpan<bool> r_hit,
+ const MutableSpan<int> r_hit_indices,
+ const MutableSpan<float3> r_hit_positions,
+ const MutableSpan<float3> r_hit_normals,
+ const MutableSpan<float> r_hit_distances,
+ int &hit_count)
+{
+ BVHTreeFromMesh tree_data;
+ BKE_bvhtree_from_mesh_get(&tree_data, &mesh, BVHTREE_FROM_LOOPTRI, 4);
+ if (tree_data.tree == nullptr) {
+ free_bvhtree_from_mesh(&tree_data);
+ return;
+ }
+
+ for (const int i : mask) {
+ const float ray_length = ray_lengths[i];
+ const float3 ray_origin = ray_origins[i];
+ const float3 ray_direction = math::normalize(ray_directions[i]);
+
+ BVHTreeRayHit hit;
+ hit.index = -1;
+ hit.dist = ray_length;
+ if (BLI_bvhtree_ray_cast(tree_data.tree,
+ ray_origin,
+ ray_direction,
+ 0.0f,
+ &hit,
+ tree_data.raycast_callback,
+ &tree_data) != -1) {
+ hit_count++;
+ if (!r_hit.is_empty()) {
+ r_hit[i] = hit.index >= 0;
+ }
+ if (!r_hit_indices.is_empty()) {
+ /* The caller must be able to handle invalid indices anyway, so don't clamp this value. */
+ r_hit_indices[i] = hit.index;
+ }
+ if (!r_hit_positions.is_empty()) {
+ r_hit_positions[i] = hit.co;
+ }
+ if (!r_hit_normals.is_empty()) {
+ r_hit_normals[i] = hit.no;
+ }
+ if (!r_hit_distances.is_empty()) {
+ r_hit_distances[i] = hit.dist;
+ }
+ }
+ else {
+ if (!r_hit.is_empty()) {
+ r_hit[i] = false;
+ }
+ if (!r_hit_indices.is_empty()) {
+ r_hit_indices[i] = -1;
+ }
+ if (!r_hit_positions.is_empty()) {
+ r_hit_positions[i] = float3(0.0f, 0.0f, 0.0f);
+ }
+ if (!r_hit_normals.is_empty()) {
+ r_hit_normals[i] = float3(0.0f, 0.0f, 0.0f);
+ }
+ if (!r_hit_distances.is_empty()) {
+ r_hit_distances[i] = ray_length;
+ }
+ }
+ }
+
+ /* We shouldn't be rebuilding the BVH tree when calling this function in parallel. */
+ BLI_assert(tree_data.cached);
+ free_bvhtree_from_mesh(&tree_data);
+}
+
+class RaycastFunction : public fn::MultiFunction {
+ private:
+ GeometrySet target_;
+ GeometryNodeRaycastMapMode mapping_;
+
+ /** The field for data evaluated on the target geometry. */
+ std::optional<GeometryComponentFieldContext> target_context_;
+ std::unique_ptr<FieldEvaluator> target_evaluator_;
+ const GVArray *target_data_ = nullptr;
+
+ /* Always evaluate the target domain data on the face corner domain because it contains the most
+ * information. Eventually this could be exposed as an option or determined automatically from
+ * the field inputs for better performance. */
+ const AttributeDomain domain_ = ATTR_DOMAIN_CORNER;
+
+ fn::MFSignature signature_;
+
+ public:
+ RaycastFunction(GeometrySet target, GField src_field, GeometryNodeRaycastMapMode mapping)
+ : target_(std::move(target)), mapping_((GeometryNodeRaycastMapMode)mapping)
+ {
+ target_.ensure_owns_direct_data();
+ this->evaluate_target_field(std::move(src_field));
+ signature_ = create_signature();
+ this->set_signature(&signature_);
+ }
+
+ fn::MFSignature create_signature()
+ {
+ blender::fn::MFSignatureBuilder signature{"Geometry Proximity"};
+ signature.single_input<float3>("Source Position");
+ signature.single_input<float3>("Ray Direction");
+ signature.single_input<float>("Ray Length");
+ signature.single_output<bool>("Is Hit");
+ signature.single_output<float3>("Hit Position");
+ signature.single_output<float3>("Hit Normal");
+ signature.single_output<float>("Distance");
+ if (target_data_) {
+ signature.single_output("Attribute", target_data_->type());
+ }
+ return signature.build();
+ }
+
+ void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override
+ {
+ /* Hit positions are always necessary for retrieving the attribute from the target if that
+ * output is required, so always retrieve a span from the evaluator in that case (it's
+ * expected that the evaluator is more likely to have a spare buffer that could be used). */
+ MutableSpan<float3> hit_positions =
+ (target_data_) ? params.uninitialized_single_output<float3>(4, "Hit Position") :
+ params.uninitialized_single_output_if_required<float3>(4, "Hit Position");
+
+ Array<int> hit_indices;
+ if (target_data_) {
+ hit_indices.reinitialize(mask.min_array_size());
+ }
+
+ BLI_assert(target_.has_mesh());
+ const Mesh &mesh = *target_.get_mesh_for_read();
+
+ int hit_count = 0;
+ raycast_to_mesh(mask,
+ mesh,
+ params.readonly_single_input<float3>(0, "Source Position"),
+ params.readonly_single_input<float3>(1, "Ray Direction"),
+ params.readonly_single_input<float>(2, "Ray Length"),
+ params.uninitialized_single_output_if_required<bool>(3, "Is Hit"),
+ hit_indices,
+ hit_positions,
+ params.uninitialized_single_output_if_required<float3>(5, "Hit Normal"),
+ params.uninitialized_single_output_if_required<float>(6, "Distance"),
+ hit_count);
+
+ if (target_data_) {
+ IndexMask hit_mask;
+ Vector<int64_t> hit_mask_indices;
+ if (hit_count < mask.size()) {
+ /* Not all rays hit the target. Create a corrected mask to avoid transferring attribute
+ * data to invalid indices. An alternative would be handling -1 indices in a separate case
+ * in #MeshAttributeInterpolator, but since it already has an IndexMask in its constructor,
+ * it's simpler to use that. */
+ hit_mask_indices.reserve(hit_count);
+ for (const int64_t i : mask) {
+ if (hit_indices[i] != -1) {
+ hit_mask_indices.append(i);
+ }
+ hit_mask = IndexMask(hit_mask_indices);
+ }
+ }
+ else {
+ hit_mask = mask;
+ }
+
+ GMutableSpan result = params.uninitialized_single_output_if_required(7, "Attribute");
+ if (!result.is_empty()) {
+ MeshAttributeInterpolator interp(&mesh, hit_mask, hit_positions, hit_indices);
+ result.type().fill_assign_indices(result.type().default_value(), result.data(), mask);
+ interp.sample_data(*target_data_, domain_, get_map_mode(mapping_), result);
+ }
+ }
+ }
+
+ private:
+ void evaluate_target_field(GField src_field)
+ {
+ if (!src_field) {
+ return;
+ }
+ const MeshComponent &mesh_component = *target_.get_component_for_read<MeshComponent>();
+ target_context_.emplace(GeometryComponentFieldContext{mesh_component, domain_});
+ const int domain_size = mesh_component.attribute_domain_size(domain_);
+ target_evaluator_ = std::make_unique<FieldEvaluator>(*target_context_, domain_size);
+ target_evaluator_->add(std::move(src_field));
+ target_evaluator_->evaluate();
+ target_data_ = &target_evaluator_->get_evaluated(0);
+ }
+};
+
+static GField get_input_attribute_field(GeoNodeExecParams &params, const CustomDataType data_type)
+{
+ switch (data_type) {
+ case CD_PROP_FLOAT:
+ if (params.output_is_required("Attribute_001")) {
+ return params.extract_input<Field<float>>("Attribute_001");
+ }
+ break;
+ case CD_PROP_FLOAT3:
+ if (params.output_is_required("Attribute")) {
+ return params.extract_input<Field<float3>>("Attribute");
+ }
+ break;
+ case CD_PROP_COLOR:
+ if (params.output_is_required("Attribute_002")) {
+ return params.extract_input<Field<ColorGeometry4f>>("Attribute_002");
+ }
+ break;
+ case CD_PROP_BOOL:
+ if (params.output_is_required("Attribute_003")) {
+ return params.extract_input<Field<bool>>("Attribute_003");
+ }
+ break;
+ case CD_PROP_INT32:
+ if (params.output_is_required("Attribute_004")) {
+ return params.extract_input<Field<int>>("Attribute_004");
+ }
+ break;
+ default:
+ BLI_assert_unreachable();
+ }
+ return {};
+}
+
+static void output_attribute_field(GeoNodeExecParams &params, GField field)
+{
+ switch (bke::cpp_type_to_custom_data_type(field.cpp_type())) {
+ case CD_PROP_FLOAT: {
+ params.set_output("Attribute_001", Field<float>(field));
+ break;
+ }
+ case CD_PROP_FLOAT3: {
+ params.set_output("Attribute", Field<float3>(field));
+ break;
+ }
+ case CD_PROP_COLOR: {
+ params.set_output("Attribute_002", Field<ColorGeometry4f>(field));
+ break;
+ }
+ case CD_PROP_BOOL: {
+ params.set_output("Attribute_003", Field<bool>(field));
+ break;
+ }
+ case CD_PROP_INT32: {
+ params.set_output("Attribute_004", Field<int>(field));
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ GeometrySet target = params.extract_input<GeometrySet>("Target Geometry");
+ const NodeGeometryRaycast &storage = node_storage(params.node());
+ const GeometryNodeRaycastMapMode mapping = (GeometryNodeRaycastMapMode)storage.mapping;
+ const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type);
+
+ if (target.is_empty()) {
+ params.set_default_remaining_outputs();
+ return;
+ }
+
+ if (!target.has_mesh()) {
+ params.set_default_remaining_outputs();
+ return;
+ }
+
+ if (target.get_mesh_for_read()->totpoly == 0) {
+ params.error_message_add(NodeWarningType::Error, TIP_("The target mesh must have faces"));
+ params.set_default_remaining_outputs();
+ return;
+ }
+
+ GField field = get_input_attribute_field(params, data_type);
+ const bool do_attribute_transfer = bool(field);
+ Field<float3> position_field = params.extract_input<Field<float3>>("Source Position");
+ Field<float3> direction_field = params.extract_input<Field<float3>>("Ray Direction");
+ Field<float> length_field = params.extract_input<Field<float>>("Ray Length");
+
+ auto fn = std::make_unique<RaycastFunction>(std::move(target), std::move(field), mapping);
+ auto op = std::make_shared<FieldOperation>(FieldOperation(
+ std::move(fn),
+ {std::move(position_field), std::move(direction_field), std::move(length_field)}));
+
+ params.set_output("Is Hit", Field<bool>(op, 0));
+ params.set_output("Hit Position", Field<float3>(op, 1));
+ params.set_output("Hit Normal", Field<float3>(op, 2));
+ params.set_output("Hit Distance", Field<float>(op, 3));
+ if (do_attribute_transfer) {
+ output_attribute_field(params, GField(op, 4));
+ }
+}
+
+} // namespace blender::nodes::node_geo_raycast_cc
+
+void register_node_type_geo_raycast()
+{
+ namespace file_ns = blender::nodes::node_geo_raycast_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_RAYCAST, "Raycast", NODE_CLASS_GEOMETRY);
+ node_type_size_preset(&ntype, NODE_SIZE_MIDDLE);
+ node_type_init(&ntype, file_ns::node_init);
+ node_type_update(&ntype, file_ns::node_update);
+ node_type_storage(
+ &ntype, "NodeGeometryRaycast", node_free_standard_storage, node_copy_standard_storage);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.draw_buttons = file_ns::node_layout;
+ ntype.gather_link_search_ops = file_ns::node_gather_link_searches;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_realize_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_realize_instances.cc
index 3be79d5ba3b..48b88705ed2 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_realize_instances.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_realize_instances.cc
@@ -16,33 +16,47 @@
#include "node_geometry_util.hh"
+#include "GEO_realize_instances.hh"
+
#include "UI_interface.h"
#include "UI_resources.h"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_realize_instances_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_output<decl::Geometry>(N_("Geometry"));
+}
-static void geo_node_realize_instances_declare(NodeDeclarationBuilder &b)
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_output<decl::Geometry>("Geometry");
+ uiItemR(layout, ptr, "legacy_behavior", 0, nullptr, ICON_NONE);
}
-static void geo_node_realize_instances_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
+ const bool legacy_behavior = params.node().custom1 & GEO_NODE_REALIZE_INSTANCES_LEGACY_BEHAVIOR;
+
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = bke::geometry_set_realize_instances(geometry_set);
+ geometry::RealizeInstancesOptions options;
+ options.keep_original_ids = legacy_behavior;
+ options.realize_instance_attributes = !legacy_behavior;
+ geometry_set = geometry::realize_instances(geometry_set, options);
params.set_output("Geometry", std::move(geometry_set));
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_realize_instances_cc
void register_node_type_geo_realize_instances()
{
+ namespace file_ns = blender::nodes::node_geo_realize_instances_cc;
+
static bNodeType ntype;
- geo_node_type_base(
- &ntype, GEO_NODE_REALIZE_INSTANCES, "Realize Instances", NODE_CLASS_GEOMETRY, 0);
- ntype.declare = blender::nodes::geo_node_realize_instances_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_realize_instances_exec;
+ geo_node_type_base(&ntype, GEO_NODE_REALIZE_INSTANCES, "Realize Instances", NODE_CLASS_GEOMETRY);
+ ntype.declare = file_ns::node_declare;
+ ntype.draw_buttons_ex = file_ns::node_layout;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc
new file mode 100644
index 00000000000..7d5c5b77ffd
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc
@@ -0,0 +1,119 @@
+/*
+ * 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 "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_rotate_instances_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>(N_("Instances")).only_instances();
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field();
+ b.add_input<decl::Vector>(N_("Rotation")).subtype(PROP_EULER).supports_field();
+ b.add_input<decl::Vector>(N_("Pivot Point")).subtype(PROP_TRANSLATION).supports_field();
+ b.add_input<decl::Bool>(N_("Local Space")).default_value(true).supports_field();
+ b.add_output<decl::Geometry>(N_("Instances"));
+}
+
+static void rotate_instances(GeoNodeExecParams &params, InstancesComponent &instances_component)
+{
+ GeometryComponentFieldContext field_context{instances_component, ATTR_DOMAIN_INSTANCE};
+ const int domain_size = instances_component.instances_amount();
+
+ fn::FieldEvaluator evaluator{field_context, domain_size};
+ evaluator.set_selection(params.extract_input<Field<bool>>("Selection"));
+ evaluator.add(params.extract_input<Field<float3>>("Rotation"));
+ evaluator.add(params.extract_input<Field<float3>>("Pivot Point"));
+ evaluator.add(params.extract_input<Field<bool>>("Local Space"));
+ evaluator.evaluate();
+
+ const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
+ const VArray<float3> &rotations = evaluator.get_evaluated<float3>(0);
+ const VArray<float3> &pivots = evaluator.get_evaluated<float3>(1);
+ const VArray<bool> &local_spaces = evaluator.get_evaluated<bool>(2);
+
+ MutableSpan<float4x4> instance_transforms = instances_component.instance_transforms();
+
+ threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) {
+ for (const int i_selection : range) {
+ const int i = selection[i_selection];
+ const float3 pivot = pivots[i];
+ const float3 euler = rotations[i];
+ float4x4 &instance_transform = instance_transforms[i];
+
+ float4x4 rotation_matrix;
+ float3 used_pivot;
+
+ if (local_spaces[i]) {
+ /* Find rotation axis from the matrix. This should work even if the instance is skewed. */
+ const float3 rotation_axis_x = instance_transform.values[0];
+ const float3 rotation_axis_y = instance_transform.values[1];
+ const float3 rotation_axis_z = instance_transform.values[2];
+
+ /* Create rotations around the individual axis. This could be optimized to skip some axis
+ * when the angle is zero. */
+ float rotation_x[3][3], rotation_y[3][3], rotation_z[3][3];
+ axis_angle_to_mat3(rotation_x, rotation_axis_x, euler.x);
+ axis_angle_to_mat3(rotation_y, rotation_axis_y, euler.y);
+ axis_angle_to_mat3(rotation_z, rotation_axis_z, euler.z);
+
+ /* Combine the previously computed rotations into the final rotation matrix. */
+ float rotation[3][3];
+ mul_m3_series(rotation, rotation_z, rotation_y, rotation_x);
+ copy_m4_m3(rotation_matrix.values, rotation);
+
+ /* Transform the passed in pivot into the local space of the instance. */
+ used_pivot = instance_transform * pivot;
+ }
+ else {
+ used_pivot = pivot;
+ eul_to_mat4(rotation_matrix.values, euler);
+ }
+ /* Move the pivot to the origin so that we can rotate around it. */
+ sub_v3_v3(instance_transform.values[3], used_pivot);
+ /* Perform the actual rotation. */
+ mul_m4_m4_pre(instance_transform.values, rotation_matrix.values);
+ /* Undo the pivot shifting done before. */
+ add_v3_v3(instance_transform.values[3], used_pivot);
+ }
+ });
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Instances");
+ if (geometry_set.has_instances()) {
+ InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>();
+ rotate_instances(params, instances);
+ }
+ params.set_output("Instances", std::move(geometry_set));
+}
+
+} // namespace blender::nodes::node_geo_rotate_instances_cc
+
+void register_node_type_geo_rotate_instances()
+{
+ namespace file_ns = blender::nodes::node_geo_rotate_instances_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_ROTATE_INSTANCES, "Rotate Instances", NODE_CLASS_GEOMETRY);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.declare = file_ns::node_declare;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_scale_elements.cc b/source/blender/nodes/geometry/nodes/node_geo_scale_elements.cc
new file mode 100644
index 00000000000..aaa2c156442
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_scale_elements.cc
@@ -0,0 +1,485 @@
+/*
+ * 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_array.hh"
+#include "BLI_disjoint_set.hh"
+#include "BLI_task.hh"
+#include "BLI_vector.hh"
+#include "BLI_vector_set.hh"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "BKE_mesh.h"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_scale_elements_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>(N_("Geometry")).supported_type(GEO_COMPONENT_TYPE_MESH);
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field();
+ b.add_input<decl::Float>(N_("Scale"), "Scale").default_value(1.0f).min(0.0f).supports_field();
+ b.add_input<decl::Vector>(N_("Center"))
+ .subtype(PROP_TRANSLATION)
+ .implicit_field()
+ .description(N_("Origin of the scaling for each element. If multiple elements are "
+ "connected, their center is averaged"));
+ b.add_input<decl::Vector>(N_("Axis"))
+ .default_value({1.0f, 0.0f, 0.0f})
+ .supports_field()
+ .description(N_("Direction in which to scale the element"));
+ b.add_output<decl::Geometry>(N_("Geometry"));
+};
+
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "domain", 0, "", ICON_NONE);
+ uiItemR(layout, ptr, "scale_mode", 0, "", ICON_NONE);
+}
+
+static void node_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ node->custom1 = ATTR_DOMAIN_FACE;
+ node->custom2 = GEO_NODE_SCALE_ELEMENTS_UNIFORM;
+}
+
+static void node_update(bNodeTree *ntree, bNode *node)
+{
+ bNodeSocket *geometry_socket = static_cast<bNodeSocket *>(node->inputs.first);
+ bNodeSocket *selection_socket = geometry_socket->next;
+ bNodeSocket *scale_float_socket = selection_socket->next;
+ bNodeSocket *center_socket = scale_float_socket->next;
+ bNodeSocket *axis_socket = center_socket->next;
+
+ const GeometryNodeScaleElementsMode mode = static_cast<GeometryNodeScaleElementsMode>(
+ node->custom2);
+ const bool use_single_axis = mode == GEO_NODE_SCALE_ELEMENTS_SINGLE_AXIS;
+
+ nodeSetSocketAvailability(ntree, axis_socket, use_single_axis);
+}
+
+struct UniformScaleFields {
+ Field<bool> selection;
+ Field<float> scale;
+ Field<float3> center;
+};
+
+struct UniformScaleParams {
+ IndexMask selection;
+ VArray<float> scales;
+ VArray<float3> centers;
+};
+
+struct AxisScaleFields {
+ Field<bool> selection;
+ Field<float> scale;
+ Field<float3> center;
+ Field<float3> axis;
+};
+
+struct AxisScaleParams {
+ IndexMask selection;
+ VArray<float> scales;
+ VArray<float3> centers;
+ VArray<float3> axis_vectors;
+};
+
+/**
+ * When multiple elements share the same vertices, they are scaled together.
+ */
+struct ElementIsland {
+ /* Either face or edge indices. */
+ Vector<int> element_indices;
+};
+
+static float3 transform_with_uniform_scale(const float3 &position,
+ const float3 &center,
+ const float scale)
+{
+ const float3 diff = position - center;
+ const float3 scaled_diff = scale * diff;
+ const float3 new_position = center + scaled_diff;
+ return new_position;
+}
+
+static float4x4 create_single_axis_transform(const float3 &center,
+ const float3 &axis,
+ const float scale)
+{
+ /* Scale along x axis. The other axis need to be orthogonal, but their specific value does not
+ * matter. */
+ const float3 x_axis = math::normalize(axis);
+ float3 y_axis = math::cross(x_axis, float3(0.0f, 0.0f, 1.0f));
+ if (math::is_zero(y_axis)) {
+ y_axis = math::cross(x_axis, float3(0.0f, 1.0f, 0.0f));
+ }
+ y_axis = math::normalize(y_axis);
+ const float3 z_axis = math::cross(x_axis, y_axis);
+
+ float4x4 transform = float4x4::identity();
+
+ /* Move scaling center to the origin. */
+ sub_v3_v3(transform.values[3], center);
+
+ /* `base_change` and `base_change_inv` are used to rotate space so that scaling along the
+ * provided axis is the same as scaling along the x axis. */
+ float4x4 base_change = float4x4::identity();
+ copy_v3_v3(base_change.values[0], x_axis);
+ copy_v3_v3(base_change.values[1], y_axis);
+ copy_v3_v3(base_change.values[2], z_axis);
+
+ /* Can invert by transposing, because the matrix is orthonormal. */
+ float4x4 base_change_inv = base_change.transposed();
+
+ float4x4 scale_transform = float4x4::identity();
+ scale_transform.values[0][0] = scale;
+
+ transform = base_change * scale_transform * base_change_inv * transform;
+
+ /* Move scaling center back to where it was. */
+ add_v3_v3(transform.values[3], center);
+
+ return transform;
+}
+
+using GetVertexIndicesFn =
+ FunctionRef<void(const Mesh &mesh, int element_index, VectorSet<int> &r_vertex_indices)>;
+
+static void scale_vertex_islands_uniformly(Mesh &mesh,
+ const Span<ElementIsland> islands,
+ const UniformScaleParams &params,
+ const GetVertexIndicesFn get_vertex_indices)
+{
+ threading::parallel_for(islands.index_range(), 256, [&](const IndexRange range) {
+ for (const int island_index : range) {
+ const ElementIsland &island = islands[island_index];
+
+ float scale = 0.0f;
+ float3 center = {0.0f, 0.0f, 0.0f};
+
+ VectorSet<int> vertex_indices;
+ for (const int poly_index : island.element_indices) {
+ get_vertex_indices(mesh, poly_index, vertex_indices);
+ center += params.centers[poly_index];
+ scale += params.scales[poly_index];
+ }
+
+ /* Divide by number of elements to get the average. */
+ const float f = 1.0f / island.element_indices.size();
+ scale *= f;
+ center *= f;
+
+ for (const int vert_index : vertex_indices) {
+ MVert &vert = mesh.mvert[vert_index];
+ const float3 old_position = vert.co;
+ const float3 new_position = transform_with_uniform_scale(old_position, center, scale);
+ copy_v3_v3(vert.co, new_position);
+ }
+ }
+ });
+
+ /* Positions have changed, so the normals will have to be recomputed. */
+ BKE_mesh_normals_tag_dirty(&mesh);
+}
+
+static void scale_vertex_islands_on_axis(Mesh &mesh,
+ const Span<ElementIsland> islands,
+ const AxisScaleParams &params,
+ const GetVertexIndicesFn get_vertex_indices)
+{
+ threading::parallel_for(islands.index_range(), 256, [&](const IndexRange range) {
+ for (const int island_index : range) {
+ const ElementIsland &island = islands[island_index];
+
+ float scale = 0.0f;
+ float3 center = {0.0f, 0.0f, 0.0f};
+ float3 axis = {0.0f, 0.0f, 0.0f};
+
+ VectorSet<int> vertex_indices;
+ for (const int poly_index : island.element_indices) {
+ get_vertex_indices(mesh, poly_index, vertex_indices);
+ center += params.centers[poly_index];
+ scale += params.scales[poly_index];
+ axis += params.axis_vectors[poly_index];
+ }
+
+ /* Divide by number of elements to get the average. */
+ const float f = 1.0f / island.element_indices.size();
+ scale *= f;
+ center *= f;
+ axis *= f;
+
+ if (math::is_zero(axis)) {
+ axis = float3(1.0f, 0.0f, 0.0f);
+ }
+
+ const float4x4 transform = create_single_axis_transform(center, axis, scale);
+ for (const int vert_index : vertex_indices) {
+ MVert &vert = mesh.mvert[vert_index];
+ const float3 old_position = vert.co;
+ const float3 new_position = transform * old_position;
+ copy_v3_v3(vert.co, new_position);
+ }
+ }
+ });
+
+ /* Positions have changed, so the normals will have to be recomputed. */
+ BKE_mesh_normals_tag_dirty(&mesh);
+}
+
+static Vector<ElementIsland> prepare_face_islands(const Mesh &mesh, const IndexMask face_selection)
+{
+ /* Use the disjoint set data structure to determine which vertices have to be scaled together. */
+ DisjointSet disjoint_set(mesh.totvert);
+ for (const int poly_index : face_selection) {
+ const MPoly &poly = mesh.mpoly[poly_index];
+ const Span<MLoop> poly_loops{mesh.mloop + poly.loopstart, poly.totloop};
+ for (const int loop_index : IndexRange(poly.totloop - 1)) {
+ const int v1 = poly_loops[loop_index].v;
+ const int v2 = poly_loops[loop_index + 1].v;
+ disjoint_set.join(v1, v2);
+ }
+ disjoint_set.join(poly_loops.first().v, poly_loops.last().v);
+ }
+
+ VectorSet<int> island_ids;
+ Vector<ElementIsland> islands;
+ /* There are at most as many islands as there are selected faces. */
+ islands.reserve(face_selection.size());
+
+ /* Gather all of the face indices in each island into separate vectors. */
+ for (const int poly_index : face_selection) {
+ const MPoly &poly = mesh.mpoly[poly_index];
+ const Span<MLoop> poly_loops{mesh.mloop + poly.loopstart, poly.totloop};
+ const int island_id = disjoint_set.find_root(poly_loops[0].v);
+ const int island_index = island_ids.index_of_or_add(island_id);
+ if (island_index == islands.size()) {
+ islands.append_as();
+ }
+ ElementIsland &island = islands[island_index];
+ island.element_indices.append(poly_index);
+ }
+
+ return islands;
+}
+
+static void get_face_vertices(const Mesh &mesh, int face_index, VectorSet<int> &r_vertex_indices)
+{
+ const MPoly &poly = mesh.mpoly[face_index];
+ const Span<MLoop> poly_loops{mesh.mloop + poly.loopstart, poly.totloop};
+ for (const MLoop &loop : poly_loops) {
+ r_vertex_indices.add(loop.v);
+ }
+}
+
+static AxisScaleParams evaluate_axis_scale_fields(FieldEvaluator &evaluator,
+ const AxisScaleFields &fields)
+{
+ AxisScaleParams out;
+ evaluator.set_selection(fields.selection);
+ evaluator.add(fields.scale, &out.scales);
+ evaluator.add(fields.center, &out.centers);
+ evaluator.add(fields.axis, &out.axis_vectors);
+ evaluator.evaluate();
+ out.selection = evaluator.get_evaluated_selection_as_mask();
+ return out;
+}
+
+static void scale_faces_on_axis(MeshComponent &mesh_component, const AxisScaleFields &fields)
+{
+ Mesh &mesh = *mesh_component.get_for_write();
+ mesh.mvert = static_cast<MVert *>(
+ CustomData_duplicate_referenced_layer(&mesh.vdata, CD_MVERT, mesh.totvert));
+
+ GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_FACE};
+ FieldEvaluator evaluator{field_context, mesh.totpoly};
+ AxisScaleParams params = evaluate_axis_scale_fields(evaluator, fields);
+
+ Vector<ElementIsland> island = prepare_face_islands(mesh, params.selection);
+ scale_vertex_islands_on_axis(mesh, island, params, get_face_vertices);
+}
+
+static UniformScaleParams evaluate_uniform_scale_fields(FieldEvaluator &evaluator,
+ const UniformScaleFields &fields)
+{
+ UniformScaleParams out;
+ evaluator.set_selection(fields.selection);
+ evaluator.add(fields.scale, &out.scales);
+ evaluator.add(fields.center, &out.centers);
+ evaluator.evaluate();
+ out.selection = evaluator.get_evaluated_selection_as_mask();
+ return out;
+}
+
+static void scale_faces_uniformly(MeshComponent &mesh_component, const UniformScaleFields &fields)
+{
+ Mesh &mesh = *mesh_component.get_for_write();
+ mesh.mvert = static_cast<MVert *>(
+ CustomData_duplicate_referenced_layer(&mesh.vdata, CD_MVERT, mesh.totvert));
+
+ GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_FACE};
+ FieldEvaluator evaluator{field_context, mesh.totpoly};
+ UniformScaleParams params = evaluate_uniform_scale_fields(evaluator, fields);
+
+ Vector<ElementIsland> island = prepare_face_islands(mesh, params.selection);
+ scale_vertex_islands_uniformly(mesh, island, params, get_face_vertices);
+}
+
+static Vector<ElementIsland> prepare_edge_islands(const Mesh &mesh, const IndexMask edge_selection)
+{
+ /* Use the disjoint set data structure to determine which vertices have to be scaled together. */
+ DisjointSet disjoint_set(mesh.totvert);
+ for (const int edge_index : edge_selection) {
+ const MEdge &edge = mesh.medge[edge_index];
+ disjoint_set.join(edge.v1, edge.v2);
+ }
+
+ VectorSet<int> island_ids;
+ Vector<ElementIsland> islands;
+ /* There are at most as many islands as there are selected edges. */
+ islands.reserve(edge_selection.size());
+
+ /* Gather all of the edge indices in each island into separate vectors. */
+ for (const int edge_index : edge_selection) {
+ const MEdge &edge = mesh.medge[edge_index];
+ const int island_id = disjoint_set.find_root(edge.v1);
+ const int island_index = island_ids.index_of_or_add(island_id);
+ if (island_index == islands.size()) {
+ islands.append_as();
+ }
+ ElementIsland &island = islands[island_index];
+ island.element_indices.append(edge_index);
+ }
+
+ return islands;
+}
+
+static void get_edge_vertices(const Mesh &mesh, int edge_index, VectorSet<int> &r_vertex_indices)
+{
+ const MEdge &edge = mesh.medge[edge_index];
+ r_vertex_indices.add(edge.v1);
+ r_vertex_indices.add(edge.v2);
+}
+
+static void scale_edges_uniformly(MeshComponent &mesh_component, const UniformScaleFields &fields)
+{
+ Mesh &mesh = *mesh_component.get_for_write();
+ mesh.mvert = static_cast<MVert *>(
+ CustomData_duplicate_referenced_layer(&mesh.vdata, CD_MVERT, mesh.totvert));
+
+ GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_EDGE};
+ FieldEvaluator evaluator{field_context, mesh.totedge};
+ UniformScaleParams params = evaluate_uniform_scale_fields(evaluator, fields);
+
+ Vector<ElementIsland> island = prepare_edge_islands(mesh, params.selection);
+ scale_vertex_islands_uniformly(mesh, island, params, get_edge_vertices);
+}
+
+static void scale_edges_on_axis(MeshComponent &mesh_component, const AxisScaleFields &fields)
+{
+ Mesh &mesh = *mesh_component.get_for_write();
+ mesh.mvert = static_cast<MVert *>(
+ CustomData_duplicate_referenced_layer(&mesh.vdata, CD_MVERT, mesh.totvert));
+
+ GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_EDGE};
+ FieldEvaluator evaluator{field_context, mesh.totedge};
+ AxisScaleParams params = evaluate_axis_scale_fields(evaluator, fields);
+
+ Vector<ElementIsland> island = prepare_edge_islands(mesh, params.selection);
+ scale_vertex_islands_on_axis(mesh, island, params, get_edge_vertices);
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ const bNode &node = params.node();
+ const AttributeDomain domain = static_cast<AttributeDomain>(node.custom1);
+ const GeometryNodeScaleElementsMode scale_mode = static_cast<GeometryNodeScaleElementsMode>(
+ node.custom2);
+
+ GeometrySet geometry = params.extract_input<GeometrySet>("Geometry");
+
+ Field<bool> selection_field = params.get_input<Field<bool>>("Selection");
+ Field<float> scale_field = params.get_input<Field<float>>("Scale");
+ Field<float3> center_field = params.get_input<Field<float3>>("Center");
+ Field<float3> axis_field;
+ if (scale_mode == GEO_NODE_SCALE_ELEMENTS_SINGLE_AXIS) {
+ axis_field = params.get_input<Field<float3>>("Axis");
+ }
+
+ geometry.modify_geometry_sets([&](GeometrySet &geometry) {
+ if (!geometry.has_mesh()) {
+ return;
+ }
+ MeshComponent &mesh_component = geometry.get_component_for_write<MeshComponent>();
+ switch (domain) {
+ case ATTR_DOMAIN_FACE: {
+ switch (scale_mode) {
+ case GEO_NODE_SCALE_ELEMENTS_UNIFORM: {
+ scale_faces_uniformly(mesh_component, {selection_field, scale_field, center_field});
+ break;
+ }
+ case GEO_NODE_SCALE_ELEMENTS_SINGLE_AXIS: {
+ scale_faces_on_axis(mesh_component,
+ {selection_field, scale_field, center_field, axis_field});
+ break;
+ }
+ }
+ break;
+ }
+ case ATTR_DOMAIN_EDGE: {
+ switch (scale_mode) {
+ case GEO_NODE_SCALE_ELEMENTS_UNIFORM: {
+ scale_edges_uniformly(mesh_component, {selection_field, scale_field, center_field});
+ break;
+ }
+ case GEO_NODE_SCALE_ELEMENTS_SINGLE_AXIS: {
+ scale_edges_on_axis(mesh_component,
+ {selection_field, scale_field, center_field, axis_field});
+ break;
+ }
+ }
+ break;
+ }
+ default:
+ BLI_assert_unreachable();
+ break;
+ }
+ });
+
+ params.set_output("Geometry", std::move(geometry));
+}
+
+} // namespace blender::nodes::node_geo_scale_elements_cc
+
+void register_node_type_geo_scale_elements()
+{
+ namespace file_ns = blender::nodes::node_geo_scale_elements_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_SCALE_ELEMENTS, "Scale Elements", NODE_CLASS_GEOMETRY);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.declare = file_ns::node_declare;
+ ntype.draw_buttons = file_ns::node_layout;
+ ntype.initfunc = file_ns::node_init;
+ ntype.updatefunc = file_ns::node_update;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc
new file mode 100644
index 00000000000..5bd2028ff41
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc
@@ -0,0 +1,98 @@
+/*
+ * 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 "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_scale_instances_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>(N_("Instances")).only_instances();
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field();
+ b.add_input<decl::Vector>(N_("Scale"))
+ .subtype(PROP_XYZ)
+ .default_value({1, 1, 1})
+ .supports_field();
+ b.add_input<decl::Vector>(N_("Center")).subtype(PROP_TRANSLATION).supports_field();
+ b.add_input<decl::Bool>(N_("Local Space")).default_value(true).supports_field();
+ b.add_output<decl::Geometry>(N_("Instances"));
+}
+
+static void scale_instances(GeoNodeExecParams &params, InstancesComponent &instances_component)
+{
+ GeometryComponentFieldContext field_context{instances_component, ATTR_DOMAIN_INSTANCE};
+
+ fn::FieldEvaluator evaluator{field_context, instances_component.instances_amount()};
+ evaluator.set_selection(params.extract_input<Field<bool>>("Selection"));
+ evaluator.add(params.extract_input<Field<float3>>("Scale"));
+ evaluator.add(params.extract_input<Field<float3>>("Center"));
+ evaluator.add(params.extract_input<Field<bool>>("Local Space"));
+ evaluator.evaluate();
+
+ const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
+ const VArray<float3> &scales = evaluator.get_evaluated<float3>(0);
+ const VArray<float3> &pivots = evaluator.get_evaluated<float3>(1);
+ const VArray<bool> &local_spaces = evaluator.get_evaluated<bool>(2);
+
+ MutableSpan<float4x4> instance_transforms = instances_component.instance_transforms();
+
+ threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) {
+ for (const int i_selection : range) {
+ const int i = selection[i_selection];
+ const float3 pivot = pivots[i];
+ float4x4 &instance_transform = instance_transforms[i];
+
+ if (local_spaces[i]) {
+ instance_transform *= float4x4::from_location(pivot);
+ rescale_m4(instance_transform.values, scales[i]);
+ instance_transform *= float4x4::from_location(-pivot);
+ }
+ else {
+ const float4x4 original_transform = instance_transform;
+ instance_transform = float4x4::from_location(pivot);
+ rescale_m4(instance_transform.values, scales[i]);
+ instance_transform *= float4x4::from_location(-pivot);
+ instance_transform *= original_transform;
+ }
+ }
+ });
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Instances");
+ if (geometry_set.has_instances()) {
+ InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>();
+ scale_instances(params, instances);
+ }
+ params.set_output("Instances", std::move(geometry_set));
+}
+
+} // namespace blender::nodes::node_geo_scale_instances_cc
+
+void register_node_type_geo_scale_instances()
+{
+ namespace file_ns = blender::nodes::node_geo_scale_instances_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_SCALE_INSTANCES, "Scale Instances", NODE_CLASS_GEOMETRY);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.declare = file_ns::node_declare;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_separate_components.cc b/source/blender/nodes/geometry/nodes/node_geo_separate_components.cc
index dafd10cee2d..3e34378d3e0 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_separate_components.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_separate_components.cc
@@ -16,19 +16,19 @@
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_separate_components_cc {
-static void geo_node_join_geometry_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_output<decl::Geometry>("Mesh");
- b.add_output<decl::Geometry>("Point Cloud");
- b.add_output<decl::Geometry>("Curve");
- b.add_output<decl::Geometry>("Volume");
- b.add_output<decl::Geometry>("Instances");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_output<decl::Geometry>(N_("Mesh"));
+ b.add_output<decl::Geometry>(N_("Point Cloud"));
+ b.add_output<decl::Geometry>(N_("Curve"));
+ b.add_output<decl::Geometry>(N_("Volume"));
+ b.add_output<decl::Geometry>(N_("Instances"));
}
-static void geo_node_separate_components_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
@@ -61,15 +61,17 @@ static void geo_node_separate_components_exec(GeoNodeExecParams params)
params.set_output("Instances", instances);
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_separate_components_cc
void register_node_type_geo_separate_components()
{
+ namespace file_ns = blender::nodes::node_geo_separate_components_cc;
+
static bNodeType ntype;
geo_node_type_base(
- &ntype, GEO_NODE_SEPARATE_COMPONENTS, "Separate Components", NODE_CLASS_GEOMETRY, 0);
- ntype.declare = blender::nodes::geo_node_join_geometry_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_separate_components_exec;
+ &ntype, GEO_NODE_SEPARATE_COMPONENTS, "Separate Components", NODE_CLASS_GEOMETRY);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_separate_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_separate_geometry.cc
new file mode 100644
index 00000000000..fec1ac1363e
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_separate_geometry.cc
@@ -0,0 +1,117 @@
+/*
+ * 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 "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_separate_geometry_cc {
+
+NODE_STORAGE_FUNCS(NodeGeometrySeparateGeometry)
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::Bool>(N_("Selection"))
+ .default_value(true)
+ .hide_value()
+ .supports_field()
+ .description(N_("The parts of the geometry that go into the first output"));
+ b.add_output<decl::Geometry>(N_("Selection"))
+ .description(N_("The parts of the geometry in the selection"));
+ b.add_output<decl::Geometry>(N_("Inverted"))
+ .description(N_("The parts of the geometry not in the selection"));
+}
+
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "domain", 0, "", ICON_NONE);
+}
+
+static void node_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometrySeparateGeometry *data = MEM_cnew<NodeGeometrySeparateGeometry>(__func__);
+ data->domain = ATTR_DOMAIN_POINT;
+
+ node->storage = data;
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+ const Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
+
+ const NodeGeometrySeparateGeometry &storage = node_storage(params.node());
+ const AttributeDomain domain = static_cast<AttributeDomain>(storage.domain);
+
+ auto separate_geometry_maybe_recursively = [&](GeometrySet &geometry_set, bool invert) {
+ bool is_error;
+ if (domain == ATTR_DOMAIN_INSTANCE) {
+ /* Only delete top level instances. */
+ separate_geometry(geometry_set,
+ domain,
+ GEO_NODE_DELETE_GEOMETRY_MODE_ALL,
+ selection_field,
+ invert,
+ is_error);
+ }
+ else {
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ separate_geometry(geometry_set,
+ domain,
+ GEO_NODE_DELETE_GEOMETRY_MODE_ALL,
+ selection_field,
+ invert,
+ is_error);
+ });
+ }
+ };
+
+ GeometrySet second_set(geometry_set);
+ if (params.output_is_required("Selection")) {
+ separate_geometry_maybe_recursively(geometry_set, false);
+ params.set_output("Selection", std::move(geometry_set));
+ }
+ if (params.output_is_required("Inverted")) {
+ separate_geometry_maybe_recursively(second_set, true);
+ params.set_output("Inverted", std::move(second_set));
+ }
+}
+
+} // namespace blender::nodes::node_geo_separate_geometry_cc
+
+void register_node_type_geo_separate_geometry()
+{
+ namespace file_ns = blender::nodes::node_geo_separate_geometry_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_SEPARATE_GEOMETRY, "Separate Geometry", NODE_CLASS_GEOMETRY);
+
+ node_type_storage(&ntype,
+ "NodeGeometrySeparateGeometry",
+ node_free_standard_storage,
+ node_copy_standard_storage);
+
+ node_type_init(&ntype, file_ns::node_init);
+
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.draw_buttons = file_ns::node_layout;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc
new file mode 100644
index 00000000000..82d09bbc208
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc
@@ -0,0 +1,181 @@
+/*
+ * 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 "BKE_spline.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_set_curve_handles_cc {
+
+NODE_STORAGE_FUNCS(NodeGeometrySetCurveHandlePositions)
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE);
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field();
+ b.add_input<decl::Vector>(N_("Position")).implicit_field();
+ b.add_input<decl::Vector>(N_("Offset")).default_value(float3(0.0f, 0.0f, 0.0f)).supports_field();
+ b.add_output<decl::Geometry>(N_("Curve"));
+}
+
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
+}
+
+static void node_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometrySetCurveHandlePositions *data = MEM_cnew<NodeGeometrySetCurveHandlePositions>(
+ __func__);
+
+ data->mode = GEO_NODE_CURVE_HANDLE_LEFT;
+ node->storage = data;
+}
+
+static void set_position_in_component(const GeometryNodeCurveHandleMode mode,
+ GeometryComponent &component,
+ const Field<bool> &selection_field,
+ const Field<float3> &position_field,
+ const Field<float3> &offset_field)
+{
+ GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
+ const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT);
+ if (domain_size == 0) {
+ return;
+ }
+
+ fn::FieldEvaluator evaluator{field_context, domain_size};
+ evaluator.set_selection(selection_field);
+ evaluator.add(position_field);
+ evaluator.add(offset_field);
+ evaluator.evaluate();
+ const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
+
+ CurveComponent *curve_component = static_cast<CurveComponent *>(&component);
+ CurveEval *curve = curve_component->get_for_write();
+
+ StringRef side = mode & GEO_NODE_CURVE_HANDLE_LEFT ? "handle_left" : "handle_right";
+
+ int current_point = 0;
+ int current_mask = 0;
+
+ for (const SplinePtr &spline : curve->splines()) {
+ if (spline->type() == Spline::Type::Bezier) {
+ BezierSpline &bezier = static_cast<BezierSpline &>(*spline);
+ for (int i : bezier.positions().index_range()) {
+ if (current_mask < selection.size() && selection[current_mask] == current_point) {
+ if (mode & GEO_NODE_CURVE_HANDLE_LEFT) {
+ if (bezier.handle_types_left()[i] == BezierSpline::HandleType::Vector) {
+ bezier.ensure_auto_handles();
+ bezier.handle_types_left()[i] = BezierSpline::HandleType::Free;
+ }
+ else if (bezier.handle_types_left()[i] == BezierSpline::HandleType::Auto) {
+ bezier.ensure_auto_handles();
+ bezier.handle_types_left()[i] = BezierSpline::HandleType::Align;
+ }
+ }
+ else {
+ if (bezier.handle_types_right()[i] == BezierSpline::HandleType::Vector) {
+ bezier.ensure_auto_handles();
+ bezier.handle_types_right()[i] = BezierSpline::HandleType::Free;
+ }
+ else if (bezier.handle_types_right()[i] == BezierSpline::HandleType::Auto) {
+ bezier.ensure_auto_handles();
+ bezier.handle_types_right()[i] = BezierSpline::HandleType::Align;
+ }
+ }
+ current_mask++;
+ }
+ current_point++;
+ }
+ }
+ else {
+ for ([[maybe_unused]] int i : spline->positions().index_range()) {
+ if (current_mask < selection.size() && selection[current_mask] == current_point) {
+ current_mask++;
+ }
+ current_point++;
+ }
+ }
+ }
+
+ const VArray<float3> &positions_input = evaluator.get_evaluated<float3>(0);
+ const VArray<float3> &offsets_input = evaluator.get_evaluated<float3>(1);
+
+ OutputAttribute_Typed<float3> positions = component.attribute_try_get_for_output<float3>(
+ side, ATTR_DOMAIN_POINT, {0, 0, 0});
+ MutableSpan<float3> position_mutable = positions.as_span();
+
+ for (int i : selection) {
+ position_mutable[i] = positions_input[i] + offsets_input[i];
+ }
+
+ positions.save();
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ const NodeGeometrySetCurveHandlePositions &storage = node_storage(params.node());
+ const GeometryNodeCurveHandleMode mode = (GeometryNodeCurveHandleMode)storage.mode;
+
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
+ Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
+ Field<float3> position_field = params.extract_input<Field<float3>>("Position");
+ Field<float3> offset_field = params.extract_input<Field<float3>>("Offset");
+
+ bool has_bezier = false;
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ if (geometry_set.has_curve() &&
+ geometry_set.get_curve_for_read()->has_spline_with_type(Spline::Type::Bezier)) {
+ has_bezier = true;
+ set_position_in_component(mode,
+ geometry_set.get_component_for_write<CurveComponent>(),
+ selection_field,
+ position_field,
+ offset_field);
+ }
+ });
+ if (!has_bezier) {
+ params.error_message_add(NodeWarningType::Info,
+ TIP_("The input geometry does not contain a Bezier spline"));
+ }
+ params.set_output("Curve", std::move(geometry_set));
+}
+
+} // namespace blender::nodes::node_geo_set_curve_handles_cc
+
+void register_node_type_geo_set_curve_handles()
+{
+ namespace file_ns = blender::nodes::node_geo_set_curve_handles_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_SET_CURVE_HANDLES, "Set Handle Positions", NODE_CLASS_GEOMETRY);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.declare = file_ns::node_declare;
+ ntype.minwidth = 100.0f;
+ node_type_init(&ntype, file_ns::node_init);
+ node_type_storage(&ntype,
+ "NodeGeometrySetCurveHandlePositions",
+ node_free_standard_storage,
+ node_copy_standard_storage);
+ ntype.draw_buttons = file_ns::node_layout;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc
new file mode 100644
index 00000000000..06fe4427520
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc
@@ -0,0 +1,82 @@
+/*
+ * 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"
+
+namespace blender::nodes::node_geo_set_curve_radius_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE);
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field();
+ b.add_input<decl::Float>(N_("Radius"))
+ .min(0.0f)
+ .default_value(1.0f)
+ .supports_field()
+ .subtype(PROP_DISTANCE);
+ b.add_output<decl::Geometry>(N_("Curve"));
+}
+
+static void set_radius_in_component(GeometryComponent &component,
+ const Field<bool> &selection_field,
+ const Field<float> &radius_field)
+{
+ GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
+ const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT);
+ if (domain_size == 0) {
+ return;
+ }
+
+ OutputAttribute_Typed<float> radii = component.attribute_try_get_for_output_only<float>(
+ "radius", ATTR_DOMAIN_POINT);
+
+ fn::FieldEvaluator evaluator{field_context, domain_size};
+ evaluator.set_selection(selection_field);
+ evaluator.add_with_destination(radius_field, radii.varray());
+ evaluator.evaluate();
+
+ radii.save();
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
+ Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
+ Field<float> radii_field = params.extract_input<Field<float>>("Radius");
+
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ if (geometry_set.has_curve()) {
+ set_radius_in_component(
+ geometry_set.get_component_for_write<CurveComponent>(), selection_field, radii_field);
+ }
+ });
+
+ params.set_output("Curve", std::move(geometry_set));
+}
+
+} // namespace blender::nodes::node_geo_set_curve_radius_cc
+
+void register_node_type_geo_set_curve_radius()
+{
+ namespace file_ns = blender::nodes::node_geo_set_curve_radius_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_SET_CURVE_RADIUS, "Set Curve Radius", NODE_CLASS_GEOMETRY);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.declare = file_ns::node_declare;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc
new file mode 100644
index 00000000000..0854d0a4549
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc
@@ -0,0 +1,78 @@
+/*
+ * 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"
+
+namespace blender::nodes::node_geo_set_curve_tilt_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE);
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field();
+ b.add_input<decl::Float>(N_("Tilt")).subtype(PROP_ANGLE).supports_field();
+ b.add_output<decl::Geometry>(N_("Curve"));
+}
+
+static void set_tilt_in_component(GeometryComponent &component,
+ const Field<bool> &selection_field,
+ const Field<float> &tilt_field)
+{
+ GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
+ const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT);
+ if (domain_size == 0) {
+ return;
+ }
+
+ OutputAttribute_Typed<float> tilts = component.attribute_try_get_for_output_only<float>(
+ "tilt", ATTR_DOMAIN_POINT);
+
+ fn::FieldEvaluator evaluator{field_context, domain_size};
+ evaluator.set_selection(selection_field);
+ evaluator.add_with_destination(tilt_field, tilts.varray());
+ evaluator.evaluate();
+
+ tilts.save();
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
+ Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
+ Field<float> tilt_field = params.extract_input<Field<float>>("Tilt");
+
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ if (geometry_set.has_curve()) {
+ set_tilt_in_component(
+ geometry_set.get_component_for_write<CurveComponent>(), selection_field, tilt_field);
+ }
+ });
+
+ params.set_output("Curve", std::move(geometry_set));
+}
+
+} // namespace blender::nodes::node_geo_set_curve_tilt_cc
+
+void register_node_type_geo_set_curve_tilt()
+{
+ namespace file_ns = blender::nodes::node_geo_set_curve_tilt_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_SET_CURVE_TILT, "Set Curve Tilt", NODE_CLASS_GEOMETRY);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.declare = file_ns::node_declare;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_id.cc b/source/blender/nodes/geometry/nodes/node_geo_set_id.cc
new file mode 100644
index 00000000000..110b8206944
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_id.cc
@@ -0,0 +1,94 @@
+/*
+ * 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"
+
+namespace blender::nodes::node_geo_set_id_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field();
+ b.add_input<decl::Int>(N_("ID")).implicit_field();
+ b.add_output<decl::Geometry>(N_("Geometry"));
+}
+
+static void set_id_in_component(GeometryComponent &component,
+ const Field<bool> &selection_field,
+ const Field<int> &id_field)
+{
+ GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
+ const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT);
+ if (domain_size == 0) {
+ return;
+ }
+
+ fn::FieldEvaluator evaluator{field_context, domain_size};
+ evaluator.set_selection(selection_field);
+
+ /* Since adding the ID attribute can change the result of the field evaluation (the random value
+ * node uses the index if the ID is unavailable), make sure that it isn't added before evaluating
+ * the field. However, as an optimization, use a faster code path when it already exists. */
+ if (component.attribute_exists("id")) {
+ OutputAttribute_Typed<int> id_attribute = component.attribute_try_get_for_output_only<int>(
+ "id", ATTR_DOMAIN_POINT);
+ evaluator.add_with_destination(id_field, id_attribute.varray());
+ evaluator.evaluate();
+ id_attribute.save();
+ }
+ else {
+ evaluator.add(id_field);
+ evaluator.evaluate();
+ const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
+ const VArray<int> &result_ids = evaluator.get_evaluated<int>(0);
+ OutputAttribute_Typed<int> id_attribute = component.attribute_try_get_for_output_only<int>(
+ "id", ATTR_DOMAIN_POINT);
+ result_ids.materialize(selection, id_attribute.as_span());
+ id_attribute.save();
+ }
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
+ Field<int> id_field = params.extract_input<Field<int>>("ID");
+
+ for (const GeometryComponentType type : {GEO_COMPONENT_TYPE_INSTANCES,
+ GEO_COMPONENT_TYPE_MESH,
+ GEO_COMPONENT_TYPE_POINT_CLOUD,
+ GEO_COMPONENT_TYPE_CURVE}) {
+ if (geometry_set.has(type)) {
+ set_id_in_component(geometry_set.get_component_for_write(type), selection_field, id_field);
+ }
+ }
+
+ params.set_output("Geometry", std::move(geometry_set));
+}
+
+} // namespace blender::nodes::node_geo_set_id_cc
+
+void register_node_type_geo_set_id()
+{
+ namespace file_ns = blender::nodes::node_geo_set_id_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_SET_ID, "Set ID", NODE_CLASS_GEOMETRY);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.declare = file_ns::node_declare;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_material.cc b/source/blender/nodes/geometry/nodes/node_geo_set_material.cc
new file mode 100644
index 00000000000..ab2c778d6fc
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_material.cc
@@ -0,0 +1,134 @@
+/*
+ * 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 "UI_interface.h"
+#include "UI_resources.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_pointcloud_types.h"
+#include "DNA_volume_types.h"
+
+#include "BKE_material.h"
+
+namespace blender::nodes::node_geo_set_material_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>(N_("Geometry"))
+ .supported_type(
+ {GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_VOLUME, GEO_COMPONENT_TYPE_POINT_CLOUD});
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field();
+ b.add_input<decl::Material>(N_("Material")).hide_label();
+ b.add_output<decl::Geometry>(N_("Geometry"));
+}
+
+static void assign_material_to_faces(Mesh &mesh, const IndexMask selection, Material *material)
+{
+ if (selection.size() != mesh.totpoly) {
+ /* If the entire mesh isn't selected, and there is no material slot yet, add an empty
+ * slot so that the faces that aren't selected can still refer to the default material. */
+ BKE_id_material_eval_ensure_default_slot(&mesh.id);
+ }
+
+ int new_material_index = -1;
+ for (const int i : IndexRange(mesh.totcol)) {
+ Material *other_material = mesh.mat[i];
+ if (other_material == material) {
+ new_material_index = i;
+ break;
+ }
+ }
+ if (new_material_index == -1) {
+ /* Append a new material index. */
+ new_material_index = mesh.totcol;
+ BKE_id_material_eval_assign(&mesh.id, new_material_index + 1, material);
+ }
+
+ mesh.mpoly = (MPoly *)CustomData_duplicate_referenced_layer(&mesh.pdata, CD_MPOLY, mesh.totpoly);
+ for (const int i : selection) {
+ MPoly &poly = mesh.mpoly[i];
+ poly.mat_nr = new_material_index;
+ }
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ Material *material = params.extract_input<Material *>("Material");
+ const Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
+
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+ /* Only add the warnings once, even if there are many unique instances. */
+ bool point_selection_warning = false;
+ bool volume_selection_warning = false;
+
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ if (geometry_set.has_mesh()) {
+ MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
+ Mesh &mesh = *mesh_component.get_for_write();
+ GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_FACE};
+
+ fn::FieldEvaluator selection_evaluator{field_context, mesh.totpoly};
+ selection_evaluator.add(selection_field);
+ selection_evaluator.evaluate();
+ const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0);
+
+ assign_material_to_faces(mesh, selection, material);
+ }
+ if (Volume *volume = geometry_set.get_volume_for_write()) {
+ BKE_id_material_eval_assign(&volume->id, 1, material);
+ if (selection_field.node().depends_on_input()) {
+ volume_selection_warning = true;
+ }
+ }
+ if (PointCloud *pointcloud = geometry_set.get_pointcloud_for_write()) {
+ BKE_id_material_eval_assign(&pointcloud->id, 1, material);
+ if (selection_field.node().depends_on_input()) {
+ point_selection_warning = true;
+ }
+ }
+ });
+
+ if (volume_selection_warning) {
+ params.error_message_add(
+ NodeWarningType::Info,
+ TIP_("Volumes only support a single material; selection input can not be a field"));
+ }
+ if (point_selection_warning) {
+ params.error_message_add(
+ NodeWarningType::Info,
+ TIP_("Point clouds only support a single material; selection input can not be a field"));
+ }
+
+ params.set_output("Geometry", std::move(geometry_set));
+}
+
+} // namespace blender::nodes::node_geo_set_material_cc
+
+void register_node_type_geo_set_material()
+{
+ namespace file_ns = blender::nodes::node_geo_set_material_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_SET_MATERIAL, "Set Material", NODE_CLASS_GEOMETRY);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc b/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc
new file mode 100644
index 00000000000..ca6d78adc80
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc
@@ -0,0 +1,77 @@
+/*
+ * 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"
+
+namespace blender::nodes::node_geo_set_material_index_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>(N_("Geometry")).supported_type(GEO_COMPONENT_TYPE_MESH);
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field();
+ b.add_input<decl::Int>(N_("Material Index")).supports_field().min(0);
+ b.add_output<decl::Geometry>(N_("Geometry"));
+}
+
+static void set_material_index_in_component(GeometryComponent &component,
+ const Field<bool> &selection_field,
+ const Field<int> &index_field)
+{
+ GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_FACE};
+ const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE);
+ if (domain_size == 0) {
+ return;
+ }
+
+ OutputAttribute_Typed<int> indices = component.attribute_try_get_for_output_only<int>(
+ "material_index", ATTR_DOMAIN_FACE);
+
+ fn::FieldEvaluator evaluator{field_context, domain_size};
+ evaluator.set_selection(selection_field);
+ evaluator.add_with_destination(index_field, indices.varray());
+ evaluator.evaluate();
+ indices.save();
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
+ Field<int> index_field = params.extract_input<Field<int>>("Material Index");
+
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ if (geometry_set.has_mesh()) {
+ set_material_index_in_component(
+ geometry_set.get_component_for_write<MeshComponent>(), selection_field, index_field);
+ }
+ });
+ params.set_output("Geometry", std::move(geometry_set));
+}
+
+} // namespace blender::nodes::node_geo_set_material_index_cc
+
+void register_node_type_geo_set_material_index()
+{
+ namespace file_ns = blender::nodes::node_geo_set_material_index_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_SET_MATERIAL_INDEX, "Set Material Index", NODE_CLASS_GEOMETRY);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.declare = file_ns::node_declare;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc b/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc
new file mode 100644
index 00000000000..b7dd091da44
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc
@@ -0,0 +1,83 @@
+/*
+ * 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"
+
+namespace blender::nodes::node_geo_set_point_radius_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>(N_("Points")).supported_type(GEO_COMPONENT_TYPE_POINT_CLOUD);
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field();
+ b.add_input<decl::Float>(N_("Radius"))
+ .default_value(0.05f)
+ .min(0.0f)
+ .supports_field()
+ .subtype(PROP_DISTANCE);
+ b.add_output<decl::Geometry>(N_("Points"));
+}
+
+static void set_radius_in_component(GeometryComponent &component,
+ const Field<bool> &selection_field,
+ const Field<float> &radius_field)
+{
+ GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
+ const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT);
+ if (domain_size == 0) {
+ return;
+ }
+
+ OutputAttribute_Typed<float> radii = component.attribute_try_get_for_output_only<float>(
+ "radius", ATTR_DOMAIN_POINT);
+
+ fn::FieldEvaluator evaluator{field_context, domain_size};
+ evaluator.set_selection(selection_field);
+ evaluator.add_with_destination(radius_field, radii.varray());
+ evaluator.evaluate();
+
+ radii.save();
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Points");
+ Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
+ Field<float> radii_field = params.extract_input<Field<float>>("Radius");
+
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ if (geometry_set.has_pointcloud()) {
+ set_radius_in_component(geometry_set.get_component_for_write<PointCloudComponent>(),
+ selection_field,
+ radii_field);
+ }
+ });
+
+ params.set_output("Points", std::move(geometry_set));
+}
+
+} // namespace blender::nodes::node_geo_set_point_radius_cc
+
+void register_node_type_geo_set_point_radius()
+{
+ namespace file_ns = blender::nodes::node_geo_set_point_radius_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_SET_POINT_RADIUS, "Set Point Radius", NODE_CLASS_GEOMETRY);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.declare = file_ns::node_declare;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc
index 8caf961fc04..4a8e4e6eab8 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc
@@ -16,45 +16,126 @@
#include "DEG_depsgraph_query.h"
+#include "BLI_task.hh"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_set_position_cc {
-static void geo_node_set_position_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Vector>("Position").implicit_field();
- b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field();
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field();
+ b.add_input<decl::Vector>(N_("Position")).implicit_field();
+ b.add_input<decl::Vector>(N_("Offset")).supports_field().subtype(PROP_TRANSLATION);
+ b.add_output<decl::Geometry>(N_("Geometry"));
+}
+
+static void set_computed_position_and_offset(GeometryComponent &component,
+ const VArray<float3> &in_positions,
+ const VArray<float3> &in_offsets,
+ const AttributeDomain domain,
+ const IndexMask selection)
+{
+
+ OutputAttribute_Typed<float3> positions = component.attribute_try_get_for_output<float3>(
+ "position", domain, {0, 0, 0});
+
+ const int grain_size = 10000;
+
+ switch (component.type()) {
+ case GEO_COMPONENT_TYPE_MESH: {
+ Mesh *mesh = static_cast<MeshComponent &>(component).get_for_write();
+ MutableSpan<MVert> mverts{mesh->mvert, mesh->totvert};
+ if (in_positions.is_same(positions.varray())) {
+ devirtualize_varray(in_offsets, [&](const auto in_offsets) {
+ threading::parallel_for(
+ selection.index_range(), grain_size, [&](const IndexRange range) {
+ for (const int i : selection.slice(range)) {
+ const float3 offset = in_offsets[i];
+ add_v3_v3(mverts[i].co, offset);
+ }
+ });
+ });
+ }
+ else {
+ devirtualize_varray2(
+ in_positions, in_offsets, [&](const auto in_positions, const auto in_offsets) {
+ threading::parallel_for(
+ selection.index_range(), grain_size, [&](const IndexRange range) {
+ for (const int i : selection.slice(range)) {
+ const float3 new_position = in_positions[i] + in_offsets[i];
+ copy_v3_v3(mverts[i].co, new_position);
+ }
+ });
+ });
+ }
+ break;
+ }
+ default: {
+ MutableSpan<float3> out_positions_span = positions.as_span();
+ if (in_positions.is_same(positions.varray())) {
+ devirtualize_varray(in_offsets, [&](const auto in_offsets) {
+ threading::parallel_for(
+ selection.index_range(), grain_size, [&](const IndexRange range) {
+ for (const int i : selection.slice(range)) {
+ out_positions_span[i] += in_offsets[i];
+ }
+ });
+ });
+ }
+ else {
+ devirtualize_varray2(
+ in_positions, in_offsets, [&](const auto in_positions, const auto in_offsets) {
+ threading::parallel_for(
+ selection.index_range(), grain_size, [&](const IndexRange range) {
+ for (const int i : selection.slice(range)) {
+ out_positions_span[i] = in_positions[i] + in_offsets[i];
+ }
+ });
+ });
+ }
+ break;
+ }
+ }
+
+ positions.save();
}
static void set_position_in_component(GeometryComponent &component,
const Field<bool> &selection_field,
- const Field<float3> &position_field)
+ const Field<float3> &position_field,
+ const Field<float3> &offset_field)
{
- GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
- const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT);
+ AttributeDomain domain = component.type() == GEO_COMPONENT_TYPE_INSTANCES ?
+ ATTR_DOMAIN_INSTANCE :
+ ATTR_DOMAIN_POINT;
+ GeometryComponentFieldContext field_context{component, domain};
+ const int domain_size = component.attribute_domain_size(domain);
if (domain_size == 0) {
return;
}
- fn::FieldEvaluator selection_evaluator{field_context, domain_size};
- selection_evaluator.add(selection_field);
- selection_evaluator.evaluate();
- const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0);
+ fn::FieldEvaluator evaluator{field_context, domain_size};
+ evaluator.set_selection(selection_field);
+ evaluator.add(position_field);
+ evaluator.add(offset_field);
+ evaluator.evaluate();
- OutputAttribute_Typed<float3> positions = component.attribute_try_get_for_output<float3>(
- "position", ATTR_DOMAIN_POINT, {0, 0, 0});
- fn::FieldEvaluator position_evaluator{field_context, &selection};
- position_evaluator.add_with_destination(position_field, positions.varray());
- position_evaluator.evaluate();
- positions.save();
+ const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
+ const VArray<float3> &positions_input = evaluator.get_evaluated<float3>(0);
+ const VArray<float3> &offsets_input = evaluator.get_evaluated<float3>(1);
+ set_computed_position_and_offset(component, positions_input, offsets_input, domain, selection);
}
-static void geo_node_set_position_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry = params.extract_input<GeometrySet>("Geometry");
Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
+ Field<float3> offset_field = params.extract_input<Field<float3>>("Offset");
Field<float3> position_field = params.extract_input<Field<float3>>("Position");
for (const GeometryComponentType type : {GEO_COMPONENT_TYPE_MESH,
@@ -63,21 +144,23 @@ static void geo_node_set_position_exec(GeoNodeExecParams params)
GEO_COMPONENT_TYPE_INSTANCES}) {
if (geometry.has(type)) {
set_position_in_component(
- geometry.get_component_for_write(type), selection_field, position_field);
+ geometry.get_component_for_write(type), selection_field, position_field, offset_field);
}
}
params.set_output("Geometry", std::move(geometry));
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_set_position_cc
void register_node_type_geo_set_position()
{
+ namespace file_ns = blender::nodes::node_geo_set_position_cc;
+
static bNodeType ntype;
- geo_node_type_base(&ntype, GEO_NODE_SET_POSITION, "Set Position", NODE_CLASS_GEOMETRY, 0);
- ntype.geometry_node_execute = blender::nodes::geo_node_set_position_exec;
- ntype.declare = blender::nodes::geo_node_set_position_declare;
+ geo_node_type_base(&ntype, GEO_NODE_SET_POSITION, "Set Position", NODE_CLASS_GEOMETRY);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.declare = file_ns::node_declare;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc b/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc
new file mode 100644
index 00000000000..d442cd37e81
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc
@@ -0,0 +1,77 @@
+/*
+ * 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"
+
+namespace blender::nodes::node_geo_set_shade_smooth_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>(N_("Geometry")).supported_type(GEO_COMPONENT_TYPE_MESH);
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field();
+ b.add_input<decl::Bool>(N_("Shade Smooth")).supports_field().default_value(true);
+ b.add_output<decl::Geometry>(N_("Geometry"));
+}
+
+static void set_smooth_in_component(GeometryComponent &component,
+ const Field<bool> &selection_field,
+ const Field<bool> &shade_field)
+{
+ GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_FACE};
+ const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE);
+ if (domain_size == 0) {
+ return;
+ }
+
+ OutputAttribute_Typed<bool> shades = component.attribute_try_get_for_output_only<bool>(
+ "shade_smooth", ATTR_DOMAIN_FACE);
+
+ fn::FieldEvaluator evaluator{field_context, domain_size};
+ evaluator.set_selection(selection_field);
+ evaluator.add_with_destination(shade_field, shades.varray());
+ evaluator.evaluate();
+
+ shades.save();
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
+ Field<bool> shade_field = params.extract_input<Field<bool>>("Shade Smooth");
+
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ if (geometry_set.has_mesh()) {
+ set_smooth_in_component(
+ geometry_set.get_component_for_write<MeshComponent>(), selection_field, shade_field);
+ }
+ });
+ params.set_output("Geometry", std::move(geometry_set));
+}
+
+} // namespace blender::nodes::node_geo_set_shade_smooth_cc
+
+void register_node_type_geo_set_shade_smooth()
+{
+ namespace file_ns = blender::nodes::node_geo_set_shade_smooth_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_SET_SHADE_SMOOTH, "Set Shade Smooth", NODE_CLASS_GEOMETRY);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.declare = file_ns::node_declare;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc b/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc
new file mode 100644
index 00000000000..13230e185a3
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc
@@ -0,0 +1,78 @@
+/*
+ * 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"
+
+namespace blender::nodes::node_geo_set_spline_cyclic_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>(N_("Geometry")).supported_type(GEO_COMPONENT_TYPE_CURVE);
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field();
+ b.add_input<decl::Bool>(N_("Cyclic")).supports_field();
+ b.add_output<decl::Geometry>(N_("Geometry"));
+}
+
+static void set_cyclic_in_component(GeometryComponent &component,
+ const Field<bool> &selection_field,
+ const Field<bool> &cyclic_field)
+{
+ GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE};
+ const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE);
+ if (domain_size == 0) {
+ return;
+ }
+
+ OutputAttribute_Typed<bool> cyclics = component.attribute_try_get_for_output_only<bool>(
+ "cyclic", ATTR_DOMAIN_CURVE);
+
+ fn::FieldEvaluator evaluator{field_context, domain_size};
+ evaluator.set_selection(selection_field);
+ evaluator.add_with_destination(cyclic_field, cyclics.varray());
+ evaluator.evaluate();
+
+ cyclics.save();
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
+ Field<bool> cyclic_field = params.extract_input<Field<bool>>("Cyclic");
+
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ if (geometry_set.has_curve()) {
+ set_cyclic_in_component(
+ geometry_set.get_component_for_write<CurveComponent>(), selection_field, cyclic_field);
+ }
+ });
+
+ params.set_output("Geometry", std::move(geometry_set));
+}
+
+} // namespace blender::nodes::node_geo_set_spline_cyclic_cc
+
+void register_node_type_geo_set_spline_cyclic()
+{
+ namespace file_ns = blender::nodes::node_geo_set_spline_cyclic_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_SET_SPLINE_CYCLIC, "Set Spline Cyclic", NODE_CLASS_GEOMETRY);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.declare = file_ns::node_declare;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc b/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc
new file mode 100644
index 00000000000..e472e14671c
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc
@@ -0,0 +1,95 @@
+/*
+ * 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 "BKE_spline.hh"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_set_spline_resolution_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>(N_("Geometry")).supported_type(GEO_COMPONENT_TYPE_CURVE);
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field();
+ b.add_input<decl::Int>(N_("Resolution")).min(1).default_value(12).supports_field();
+ b.add_output<decl::Geometry>(N_("Geometry"));
+}
+
+static void set_resolution_in_component(GeometryComponent &component,
+ const Field<bool> &selection_field,
+ const Field<int> &resolution_field)
+{
+ GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE};
+ const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE);
+ if (domain_size == 0) {
+ return;
+ }
+
+ OutputAttribute_Typed<int> resolutions = component.attribute_try_get_for_output_only<int>(
+ "resolution", ATTR_DOMAIN_CURVE);
+
+ fn::FieldEvaluator evaluator{field_context, domain_size};
+ evaluator.set_selection(selection_field);
+ evaluator.add_with_destination(resolution_field, resolutions.varray());
+ evaluator.evaluate();
+
+ resolutions.save();
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
+ Field<int> resolution_field = params.extract_input<Field<int>>("Resolution");
+
+ bool only_poly = true;
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ if (geometry_set.has_curve()) {
+ if (only_poly) {
+ for (const SplinePtr &spline : geometry_set.get_curve_for_read()->splines()) {
+ if (ELEM(spline->type(), Spline::Type::Bezier, Spline::Type::NURBS)) {
+ only_poly = false;
+ break;
+ }
+ }
+ }
+ set_resolution_in_component(geometry_set.get_component_for_write<CurveComponent>(),
+ selection_field,
+ resolution_field);
+ }
+ });
+
+ if (only_poly) {
+ params.error_message_add(NodeWarningType::Warning,
+ TIP_("Input geometry does not contain a Bezier or NURB spline"));
+ }
+ params.set_output("Geometry", std::move(geometry_set));
+}
+
+} // namespace blender::nodes::node_geo_set_spline_resolution_cc
+
+void register_node_type_geo_set_spline_resolution()
+{
+ namespace file_ns = blender::nodes::node_geo_set_spline_resolution_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_SET_SPLINE_RESOLUTION, "Set Spline Resolution", NODE_CLASS_GEOMETRY);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.declare = file_ns::node_declare;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_string_join.cc b/source/blender/nodes/geometry/nodes/node_geo_string_join.cc
index 1e4a4d1f68b..176fcf3178a 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_string_join.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_string_join.cc
@@ -16,16 +16,16 @@
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_string_join_cc {
-static void geo_node_string_join_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::String>("Delimiter");
- b.add_input<decl::String>("Strings").multi_input().hide_value();
- b.add_output<decl::String>("String");
-};
+ b.add_input<decl::String>(N_("Delimiter"));
+ b.add_input<decl::String>(N_("Strings")).multi_input().hide_value();
+ b.add_output<decl::String>(N_("String"));
+}
-static void geo_node_string_join_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
Vector<std::string> strings = params.extract_multi_input<std::string>("Strings");
const std::string delim = params.extract_input<std::string>("Delimiter");
@@ -40,14 +40,16 @@ static void geo_node_string_join_exec(GeoNodeExecParams params)
params.set_output("String", std::move(output));
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_string_join_cc
void register_node_type_geo_string_join()
{
+ namespace file_ns = blender::nodes::node_geo_string_join_cc;
+
static bNodeType ntype;
- geo_node_type_base(&ntype, GEO_NODE_STRING_JOIN, "String Join", NODE_CLASS_CONVERTER, 0);
- ntype.geometry_node_execute = blender::nodes::geo_node_string_join_exec;
- ntype.declare = blender::nodes::geo_node_string_join_declare;
+ geo_node_type_base(&ntype, GEO_NODE_STRING_JOIN, "Join Strings", NODE_CLASS_CONVERTER);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.declare = file_ns::node_declare;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc
index 5e2f03806c3..10c0d61ccb6 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc
@@ -18,8 +18,8 @@
#include "DNA_vfont_types.h"
#include "BKE_curve.h"
-#include "BKE_font.h"
#include "BKE_spline.hh"
+#include "BKE_vfont.h"
#include "BLI_hash.h"
#include "BLI_string_utf8.h"
@@ -30,25 +30,44 @@
#include "node_geometry_util.hh"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_string_to_curves_cc {
-static void geo_node_string_to_curves_declare(NodeDeclarationBuilder &b)
+NODE_STORAGE_FUNCS(NodeGeometryStringToCurves)
+
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::String>("String");
- b.add_input<decl::Float>("Size").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE);
- b.add_input<decl::Float>("Character Spacing")
+ b.add_input<decl::String>(N_("String"));
+ b.add_input<decl::Float>(N_("Size")).default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE);
+ b.add_input<decl::Float>(N_("Character Spacing"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .subtype(PROP_DISTANCE);
+ b.add_input<decl::Float>(N_("Word Spacing"))
.default_value(1.0f)
.min(0.0f)
.subtype(PROP_DISTANCE);
- b.add_input<decl::Float>("Word Spacing").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE);
- b.add_input<decl::Float>("Line Spacing").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE);
- b.add_input<decl::Float>("Text Box Width").default_value(0.0f).min(0.0f).subtype(PROP_DISTANCE);
- b.add_input<decl::Float>("Text Box Height").default_value(0.0f).min(0.0f).subtype(PROP_DISTANCE);
- b.add_output<decl::Geometry>("Curves");
- b.add_output<decl::String>("Remainder");
+ b.add_input<decl::Float>(N_("Line Spacing"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .subtype(PROP_DISTANCE);
+ b.add_input<decl::Float>(N_("Text Box Width"))
+ .default_value(0.0f)
+ .min(0.0f)
+ .subtype(PROP_DISTANCE);
+ b.add_input<decl::Float>(N_("Text Box Height"))
+ .default_value(0.0f)
+ .min(0.0f)
+ .subtype(PROP_DISTANCE)
+ .make_available([](bNode &node) {
+ node_storage(node).overflow = GEO_NODE_STRING_TO_CURVES_MODE_SCALE_TO_FIT;
+ });
+ b.add_output<decl::Geometry>(N_("Curve Instances"));
+ b.add_output<decl::String>(N_("Remainder")).make_available([](bNode &node) {
+ node_storage(node).overflow = GEO_NODE_STRING_TO_CURVES_MODE_TRUNCATE;
+ });
}
-static void geo_node_string_to_curves_layout(uiLayout *layout, struct bContext *C, PointerRNA *ptr)
+static void node_layout(uiLayout *layout, struct bContext *C, PointerRNA *ptr)
{
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
@@ -67,10 +86,9 @@ static void geo_node_string_to_curves_layout(uiLayout *layout, struct bContext *
uiItemR(layout, ptr, "align_y", 0, "", ICON_NONE);
}
-static void geo_node_string_to_curves_init(bNodeTree *UNUSED(ntree), bNode *node)
+static void node_init(bNodeTree *UNUSED(ntree), bNode *node)
{
- NodeGeometryStringToCurves *data = (NodeGeometryStringToCurves *)MEM_callocN(
- sizeof(NodeGeometryStringToCurves), __func__);
+ NodeGeometryStringToCurves *data = MEM_cnew<NodeGeometryStringToCurves>(__func__);
data->overflow = GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW;
data->align_x = GEO_NODE_STRING_TO_CURVES_ALIGN_X_LEFT;
@@ -79,17 +97,19 @@ static void geo_node_string_to_curves_init(bNodeTree *UNUSED(ntree), bNode *node
node->id = (ID *)BKE_vfont_builtin_get();
}
-static void geo_node_string_to_curves_update(bNodeTree *UNUSED(ntree), bNode *node)
+static void node_update(bNodeTree *ntree, bNode *node)
{
- const NodeGeometryStringToCurves *storage = (const NodeGeometryStringToCurves *)node->storage;
+ const NodeGeometryStringToCurves &storage = node_storage(*node);
const GeometryNodeStringToCurvesOverflowMode overflow = (GeometryNodeStringToCurvesOverflowMode)
- storage->overflow;
+ storage.overflow;
bNodeSocket *socket_remainder = ((bNodeSocket *)node->outputs.first)->next;
- nodeSetSocketAvailability(socket_remainder, overflow == GEO_NODE_STRING_TO_CURVES_MODE_TRUNCATE);
+ nodeSetSocketAvailability(
+ ntree, socket_remainder, overflow == GEO_NODE_STRING_TO_CURVES_MODE_TRUNCATE);
bNodeSocket *height_socket = (bNodeSocket *)node->inputs.last;
bNodeSocket *width_socket = height_socket->prev;
- nodeSetSocketAvailability(height_socket, overflow != GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW);
+ nodeSetSocketAvailability(
+ ntree, height_socket, overflow != GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW);
node_sock_label(width_socket,
overflow == GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW ? N_("Max Width") :
N_("Text Box Width"));
@@ -117,8 +137,7 @@ static TextLayout get_text_layout(GeoNodeExecParams &params)
return {};
}
- const NodeGeometryStringToCurves &storage =
- *(const NodeGeometryStringToCurves *)params.node().storage;
+ const NodeGeometryStringToCurves &storage = node_storage(params.node());
const GeometryNodeStringToCurvesOverflowMode overflow = (GeometryNodeStringToCurvesOverflowMode)
storage.overflow;
const GeometryNodeStringToCurvesAlignXMode align_x = (GeometryNodeStringToCurvesAlignXMode)
@@ -136,7 +155,7 @@ static TextLayout get_text_layout(GeoNodeExecParams &params)
params.extract_input<float>("Text Box Height");
VFont *vfont = (VFont *)params.node().id;
- Curve cu = {nullptr};
+ Curve cu = {{nullptr}};
cu.type = OB_FONT;
/* Set defaults */
cu.resolu = 12;
@@ -214,7 +233,7 @@ static Map<int, int> create_curve_instances(GeoNodeExecParams &params,
if (handles.contains(charcodes[i])) {
continue;
}
- Curve cu = {nullptr};
+ Curve cu = {{nullptr}};
cu.type = OB_FONT;
cu.resolu = 12;
cu.vfont = vfont;
@@ -242,18 +261,16 @@ static void add_instances_from_handles(InstancesComponent &instances,
instances.resize(positions.size());
MutableSpan<int> handles = instances.instance_reference_handles();
MutableSpan<float4x4> transforms = instances.instance_transforms();
- MutableSpan<int> instance_ids = instances.instance_ids();
threading::parallel_for(IndexRange(positions.size()), 256, [&](IndexRange range) {
for (const int i : range) {
handles[i] = char_handles.lookup(charcodes[i]);
transforms[i] = float4x4::from_location({positions[i].x, positions[i].y, 0});
- instance_ids[i] = i;
}
});
}
-static void geo_node_string_to_curves_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
TextLayout layout = get_text_layout(params);
@@ -264,15 +281,16 @@ static void geo_node_string_to_curves_exec(GeoNodeExecParams params)
}
if (layout.positions.size() == 0) {
- params.set_output("Curves", GeometrySet());
+ params.set_output("Curve Instances", GeometrySet());
return;
}
/* Convert UTF-8 encoded string to UTF-32. */
size_t len_bytes;
size_t len_chars = BLI_strlen_utf8_ex(layout.text.c_str(), &len_bytes);
- Array<char32_t> char_codes(len_chars + 1);
- BLI_str_utf8_as_utf32(char_codes.data(), layout.text.c_str(), len_chars + 1);
+ Array<char32_t> char_codes_with_null(len_chars + 1);
+ BLI_str_utf8_as_utf32(char_codes_with_null.data(), layout.text.c_str(), len_chars + 1);
+ const Span<char32_t> char_codes = char_codes_with_null.as_span().drop_back(1);
/* Create and add instances. */
GeometrySet geometry_set_out;
@@ -281,26 +299,27 @@ static void geo_node_string_to_curves_exec(GeoNodeExecParams params)
params, layout.final_font_size, char_codes, instances);
add_instances_from_handles(instances, char_handles, char_codes, layout.positions);
- params.set_output("Curves", std::move(geometry_set_out));
+ params.set_output("Curve Instances", std::move(geometry_set_out));
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_string_to_curves_cc
void register_node_type_geo_string_to_curves()
{
+ namespace file_ns = blender::nodes::node_geo_string_to_curves_cc;
+
static bNodeType ntype;
- geo_node_type_base(
- &ntype, GEO_NODE_STRING_TO_CURVES, "String to Curves", NODE_CLASS_GEOMETRY, 0);
- ntype.declare = blender::nodes::geo_node_string_to_curves_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_string_to_curves_exec;
- node_type_init(&ntype, blender::nodes::geo_node_string_to_curves_init);
- node_type_update(&ntype, blender::nodes::geo_node_string_to_curves_update);
+ geo_node_type_base(&ntype, GEO_NODE_STRING_TO_CURVES, "String to Curves", NODE_CLASS_GEOMETRY);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ node_type_init(&ntype, file_ns::node_init);
+ node_type_update(&ntype, file_ns::node_update);
node_type_size(&ntype, 190, 120, 700);
node_type_storage(&ntype,
"NodeGeometryStringToCurves",
node_free_standard_storage,
node_copy_standard_storage);
- ntype.draw_buttons = blender::nodes::geo_node_string_to_curves_layout;
+ ntype.draw_buttons = file_ns::node_layout;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc
new file mode 100644
index 00000000000..eb1a5496845
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc
@@ -0,0 +1,166 @@
+/*
+ * 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 "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_modifier_types.h"
+
+#include "BKE_mesh.h"
+#include "BKE_subdiv.h"
+#include "BKE_subdiv_mesh.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_subdivision_surface_cc {
+
+NODE_STORAGE_FUNCS(NodeGeometrySubdivisionSurface)
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH);
+ b.add_input<decl::Int>(N_("Level")).default_value(1).min(0).max(6);
+ b.add_input<decl::Float>(N_("Crease"))
+ .default_value(0.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .supports_field()
+ .subtype(PROP_FACTOR);
+ b.add_output<decl::Geometry>(N_("Mesh"));
+}
+
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "uv_smooth", 0, "", ICON_NONE);
+ uiItemR(layout, ptr, "boundary_smooth", 0, "", ICON_NONE);
+}
+
+static void node_init(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeGeometrySubdivisionSurface *data = MEM_cnew<NodeGeometrySubdivisionSurface>(__func__);
+ data->uv_smooth = SUBSURF_UV_SMOOTH_PRESERVE_BOUNDARIES;
+ data->boundary_smooth = SUBSURF_BOUNDARY_SMOOTH_ALL;
+ node->storage = data;
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh");
+#ifndef WITH_OPENSUBDIV
+ params.error_message_add(NodeWarningType::Error,
+ TIP_("Disabled, Blender was compiled without OpenSubdiv"));
+#else
+ Field<float> crease_field = params.extract_input<Field<float>>("Crease");
+
+ const NodeGeometrySubdivisionSurface &storage = node_storage(params.node());
+ const int uv_smooth = storage.uv_smooth;
+ const int boundary_smooth = storage.boundary_smooth;
+ const int subdiv_level = clamp_i(params.extract_input<int>("Level"), 0, 30);
+
+ /* Only process subdivision if level is greater than 0. */
+ if (subdiv_level == 0) {
+ params.set_output("Mesh", std::move(geometry_set));
+ return;
+ }
+
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ if (!geometry_set.has_mesh()) {
+ return;
+ }
+
+ MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
+ AttributeDomain domain = ATTR_DOMAIN_EDGE;
+ GeometryComponentFieldContext field_context{mesh_component, domain};
+ const int domain_size = mesh_component.attribute_domain_size(domain);
+
+ if (domain_size == 0) {
+ return;
+ }
+
+ FieldEvaluator evaluator(field_context, domain_size);
+ evaluator.add(crease_field);
+ evaluator.evaluate();
+ const VArray<float> &creases = evaluator.get_evaluated<float>(0);
+
+ OutputAttribute_Typed<float> crease = mesh_component.attribute_try_get_for_output_only<float>(
+ "crease", domain);
+ MutableSpan<float> crease_span = crease.as_span();
+ for (auto i : creases.index_range()) {
+ crease_span[i] = std::clamp(creases[i], 0.0f, 1.0f);
+ }
+ crease.save();
+
+ /* Initialize mesh settings. */
+ SubdivToMeshSettings mesh_settings;
+ mesh_settings.resolution = (1 << subdiv_level) + 1;
+ mesh_settings.use_optimal_display = false;
+
+ /* Initialize subdivision settings. */
+ SubdivSettings subdiv_settings;
+ subdiv_settings.is_simple = false;
+ subdiv_settings.is_adaptive = false;
+ subdiv_settings.use_creases = !(creases.is_single() && creases.get_internal_single() == 0.0f);
+ subdiv_settings.level = subdiv_level;
+
+ subdiv_settings.vtx_boundary_interpolation =
+ BKE_subdiv_vtx_boundary_interpolation_from_subsurf(boundary_smooth);
+ subdiv_settings.fvar_linear_interpolation = BKE_subdiv_fvar_interpolation_from_uv_smooth(
+ uv_smooth);
+
+ Mesh *mesh_in = mesh_component.get_for_write();
+
+ /* Apply subdivision to mesh. */
+ Subdiv *subdiv = BKE_subdiv_update_from_mesh(nullptr, &subdiv_settings, mesh_in);
+
+ /* In case of bad topology, skip to input mesh. */
+ if (subdiv == nullptr) {
+ return;
+ }
+
+ Mesh *mesh_out = BKE_subdiv_to_mesh(subdiv, &mesh_settings, mesh_in);
+ BKE_mesh_normals_tag_dirty(mesh_out);
+
+ mesh_component.replace(mesh_out);
+
+ BKE_subdiv_free(subdiv);
+ });
+#endif
+ params.set_output("Mesh", std::move(geometry_set));
+}
+
+} // namespace blender::nodes::node_geo_subdivision_surface_cc
+
+void register_node_type_geo_subdivision_surface()
+{
+ namespace file_ns = blender::nodes::node_geo_subdivision_surface_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_SUBDIVISION_SURFACE, "Subdivision Surface", NODE_CLASS_GEOMETRY);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.draw_buttons = file_ns::node_layout;
+ node_type_init(&ntype, file_ns::node_init);
+ node_type_size_preset(&ntype, NODE_SIZE_MIDDLE);
+ node_type_storage(&ntype,
+ "NodeGeometrySubdivisionSurface",
+ node_free_standard_storage,
+ node_copy_standard_storage);
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_switch.cc b/source/blender/nodes/geometry/nodes/node_geo_switch.cc
index ca857c4d2e3..a2f05677310 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_switch.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_switch.cc
@@ -19,149 +19,298 @@
#include "UI_interface.h"
#include "UI_resources.h"
-namespace blender::nodes {
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
-static void geo_node_switch_declare(NodeDeclarationBuilder &b)
+#include "BKE_material.h"
+
+#include "NOD_socket_search_link.hh"
+
+#include "FN_multi_function_signature.hh"
+
+namespace blender::nodes::node_geo_switch_cc {
+
+NODE_STORAGE_FUNCS(NodeSwitch)
+
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Bool>("Switch");
-
- b.add_input<decl::Float>("False");
- b.add_input<decl::Float>("True");
- b.add_input<decl::Int>("False", "False_001").min(-100000).max(100000);
- b.add_input<decl::Int>("True", "True_001").min(-100000).max(100000);
- b.add_input<decl::Bool>("False", "False_002");
- b.add_input<decl::Bool>("True", "True_002");
- b.add_input<decl::Vector>("False", "False_003");
- b.add_input<decl::Vector>("True", "True_003");
- b.add_input<decl::Color>("False", "False_004").default_value({0.8f, 0.8f, 0.8f, 1.0f});
- b.add_input<decl::Color>("True", "True_004").default_value({0.8f, 0.8f, 0.8f, 1.0f});
- b.add_input<decl::String>("False", "False_005");
- b.add_input<decl::String>("True", "True_005");
- b.add_input<decl::Geometry>("False", "False_006");
- b.add_input<decl::Geometry>("True", "True_006");
- b.add_input<decl::Object>("False", "False_007");
- b.add_input<decl::Object>("True", "True_007");
- b.add_input<decl::Collection>("False", "False_008");
- b.add_input<decl::Collection>("True", "True_008");
- b.add_input<decl::Texture>("False", "False_009");
- b.add_input<decl::Texture>("True", "True_009");
- b.add_input<decl::Material>("False", "False_010");
- b.add_input<decl::Material>("True", "True_010");
-
- b.add_output<decl::Float>("Output");
- b.add_output<decl::Int>("Output", "Output_001");
- b.add_output<decl::Bool>("Output", "Output_002");
- b.add_output<decl::Vector>("Output", "Output_003");
- b.add_output<decl::Color>("Output", "Output_004");
- b.add_output<decl::String>("Output", "Output_005");
- b.add_output<decl::Geometry>("Output", "Output_006");
- b.add_output<decl::Object>("Output", "Output_007");
- b.add_output<decl::Collection>("Output", "Output_008");
- b.add_output<decl::Texture>("Output", "Output_009");
- b.add_output<decl::Material>("Output", "Output_010");
+ b.add_input<decl::Bool>(N_("Switch")).default_value(false).supports_field();
+ b.add_input<decl::Bool>(N_("Switch"), "Switch_001").default_value(false);
+
+ b.add_input<decl::Float>(N_("False")).supports_field();
+ b.add_input<decl::Float>(N_("True")).supports_field();
+ b.add_input<decl::Int>(N_("False"), "False_001").min(-100000).max(100000).supports_field();
+ b.add_input<decl::Int>(N_("True"), "True_001").min(-100000).max(100000).supports_field();
+ b.add_input<decl::Bool>(N_("False"), "False_002")
+ .default_value(false)
+ .hide_value()
+ .supports_field();
+ b.add_input<decl::Bool>(N_("True"), "True_002")
+ .default_value(true)
+ .hide_value()
+ .supports_field();
+ b.add_input<decl::Vector>(N_("False"), "False_003").supports_field();
+ b.add_input<decl::Vector>(N_("True"), "True_003").supports_field();
+ b.add_input<decl::Color>(N_("False"), "False_004")
+ .default_value({0.8f, 0.8f, 0.8f, 1.0f})
+ .supports_field();
+ b.add_input<decl::Color>(N_("True"), "True_004")
+ .default_value({0.8f, 0.8f, 0.8f, 1.0f})
+ .supports_field();
+ b.add_input<decl::String>(N_("False"), "False_005").supports_field();
+ b.add_input<decl::String>(N_("True"), "True_005").supports_field();
+
+ b.add_input<decl::Geometry>(N_("False"), "False_006");
+ b.add_input<decl::Geometry>(N_("True"), "True_006");
+ b.add_input<decl::Object>(N_("False"), "False_007");
+ b.add_input<decl::Object>(N_("True"), "True_007");
+ b.add_input<decl::Collection>(N_("False"), "False_008");
+ b.add_input<decl::Collection>(N_("True"), "True_008");
+ b.add_input<decl::Texture>(N_("False"), "False_009");
+ b.add_input<decl::Texture>(N_("True"), "True_009");
+ b.add_input<decl::Material>(N_("False"), "False_010");
+ b.add_input<decl::Material>(N_("True"), "True_010");
+ b.add_input<decl::Image>(N_("False"), "False_011");
+ b.add_input<decl::Image>(N_("True"), "True_011");
+
+ b.add_output<decl::Float>(N_("Output")).dependent_field();
+ b.add_output<decl::Int>(N_("Output"), "Output_001").dependent_field();
+ b.add_output<decl::Bool>(N_("Output"), "Output_002").dependent_field();
+ b.add_output<decl::Vector>(N_("Output"), "Output_003").dependent_field();
+ b.add_output<decl::Color>(N_("Output"), "Output_004").dependent_field();
+ b.add_output<decl::String>(N_("Output"), "Output_005").dependent_field();
+ b.add_output<decl::Geometry>(N_("Output"), "Output_006");
+ b.add_output<decl::Object>(N_("Output"), "Output_007");
+ b.add_output<decl::Collection>(N_("Output"), "Output_008");
+ b.add_output<decl::Texture>(N_("Output"), "Output_009");
+ b.add_output<decl::Material>(N_("Output"), "Output_010");
+ b.add_output<decl::Image>(N_("Output"), "Output_011");
}
-static void geo_node_switch_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiItemR(layout, ptr, "input_type", 0, "", ICON_NONE);
}
-static void geo_node_switch_init(bNodeTree *UNUSED(tree), bNode *node)
+static void node_init(bNodeTree *UNUSED(tree), bNode *node)
{
- NodeSwitch *data = (NodeSwitch *)MEM_callocN(sizeof(NodeSwitch), __func__);
+ NodeSwitch *data = MEM_cnew<NodeSwitch>(__func__);
data->input_type = SOCK_GEOMETRY;
node->storage = data;
}
-static void geo_node_switch_update(bNodeTree *UNUSED(ntree), bNode *node)
+static void node_update(bNodeTree *ntree, bNode *node)
{
- NodeSwitch *node_storage = (NodeSwitch *)node->storage;
+ const NodeSwitch &storage = node_storage(*node);
int index = 0;
- LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) {
- nodeSetSocketAvailability(
- socket, index == 0 || socket->type == (eNodeSocketDatatype)node_storage->input_type);
- index++;
+ bNodeSocket *field_switch = (bNodeSocket *)node->inputs.first;
+ bNodeSocket *non_field_switch = (bNodeSocket *)field_switch->next;
+
+ const bool fields_type = ELEM(
+ storage.input_type, SOCK_FLOAT, SOCK_INT, SOCK_BOOLEAN, SOCK_VECTOR, SOCK_RGBA, SOCK_STRING);
+
+ nodeSetSocketAvailability(ntree, field_switch, fields_type);
+ nodeSetSocketAvailability(ntree, non_field_switch, !fields_type);
+
+ LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, &node->inputs, index) {
+ if (index <= 1) {
+ continue;
+ }
+ nodeSetSocketAvailability(ntree, socket, socket->type == storage.input_type);
}
+
LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) {
- nodeSetSocketAvailability(socket,
- socket->type == (eNodeSocketDatatype)node_storage->input_type);
+ nodeSetSocketAvailability(ntree, socket, socket->type == storage.input_type);
}
}
-template<typename T>
-static void output_input(GeoNodeExecParams &params,
- const bool input,
- const StringRef input_suffix,
- const StringRef output_identifier)
+static void node_gather_link_searches(GatherLinkSearchOpParams &params)
{
- const std::string name_a = "False" + input_suffix;
- const std::string name_b = "True" + input_suffix;
- if (input) {
- params.set_input_unused(name_a);
- if (params.lazy_require_input(name_b)) {
+ if (params.in_out() == SOCK_OUT) {
+ params.add_item(IFACE_("Output"), [](LinkSearchOpParams &params) {
+ bNode &node = params.add_node("GeometryNodeSwitch");
+ node_storage(node).input_type = params.socket.type;
+ params.update_and_connect_available_socket(node, "Output");
+ });
+ }
+ else {
+ if (params.other_socket().type == SOCK_BOOLEAN) {
+ params.add_item(IFACE_("Switch"), [](LinkSearchOpParams &params) {
+ bNode &node = params.add_node("GeometryNodeSwitch");
+ params.connect_available_socket(node, "Start");
+ });
+ }
+ params.add_item(IFACE_("False"), [](LinkSearchOpParams &params) {
+ bNode &node = params.add_node("GeometryNodeSwitch");
+ node_storage(node).input_type = params.socket.type;
+ params.update_and_connect_available_socket(node, "False");
+ });
+ params.add_item(IFACE_("True"), [](LinkSearchOpParams &params) {
+ bNode &node = params.add_node("GeometryNodeSwitch");
+ node_storage(node).input_type = params.socket.type;
+ params.update_and_connect_available_socket(node, "True");
+ });
+ }
+}
+
+template<typename T> class SwitchFieldsFunction : public fn::MultiFunction {
+ public:
+ SwitchFieldsFunction()
+ {
+ static fn::MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ }
+ static fn::MFSignature create_signature()
+ {
+ fn::MFSignatureBuilder signature{"Switch"};
+ signature.single_input<bool>("Switch");
+ signature.single_input<T>("False");
+ signature.single_input<T>("True");
+ signature.single_output<T>("Output");
+ return signature.build();
+ }
+
+ void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override
+ {
+ const VArray<bool> &switches = params.readonly_single_input<bool>(0, "Switch");
+ const VArray<T> &falses = params.readonly_single_input<T>(1, "False");
+ const VArray<T> &trues = params.readonly_single_input<T>(2, "True");
+ MutableSpan<T> values = params.uninitialized_single_output_if_required<T>(3, "Output");
+ for (int64_t i : mask) {
+ new (&values[i]) T(switches[i] ? trues[i] : falses[i]);
+ }
+ }
+};
+
+template<typename T> void switch_fields(GeoNodeExecParams &params, const StringRef suffix)
+{
+ if (params.lazy_require_input("Switch")) {
+ return;
+ }
+
+ const std::string name_false = "False" + suffix;
+ const std::string name_true = "True" + suffix;
+ const std::string name_output = "Output" + suffix;
+
+ Field<bool> switches_field = params.get_input<Field<bool>>("Switch");
+ if (switches_field.node().depends_on_input()) {
+ /* The switch has to be incorporated into the field. Both inputs have to be evaluated. */
+ const bool require_false = params.lazy_require_input(name_false);
+ const bool require_true = params.lazy_require_input(name_true);
+ if (require_false | require_true) {
return;
}
- params.set_output(output_identifier, params.extract_input<T>(name_b));
+
+ Field<T> falses_field = params.extract_input<Field<T>>(name_false);
+ Field<T> trues_field = params.extract_input<Field<T>>(name_true);
+
+ auto switch_fn = std::make_unique<SwitchFieldsFunction<T>>();
+ auto switch_op = std::make_shared<FieldOperation>(FieldOperation(
+ std::move(switch_fn),
+ {std::move(switches_field), std::move(falses_field), std::move(trues_field)}));
+
+ params.set_output(name_output, Field<T>(switch_op, 0));
}
else {
- params.set_input_unused(name_b);
- if (params.lazy_require_input(name_a)) {
- return;
+ /* The switch input is constant, so just evaluate and forward one of the inputs. */
+ const bool switch_value = fn::evaluate_constant_field(switches_field);
+ if (switch_value) {
+ params.set_input_unused(name_false);
+ if (params.lazy_require_input(name_true)) {
+ return;
+ }
+ params.set_output(name_output, params.extract_input<Field<T>>(name_true));
+ }
+ else {
+ params.set_input_unused(name_true);
+ if (params.lazy_require_input(name_false)) {
+ return;
+ }
+ params.set_output(name_output, params.extract_input<Field<T>>(name_false));
}
- params.set_output(output_identifier, params.extract_input<T>(name_a));
}
}
-static void geo_node_switch_exec(GeoNodeExecParams params)
+template<typename T> void switch_no_fields(GeoNodeExecParams &params, const StringRef suffix)
{
- if (params.lazy_require_input("Switch")) {
+ if (params.lazy_require_input("Switch_001")) {
return;
}
- const NodeSwitch &storage = *(const NodeSwitch *)params.node().storage;
- const bool input = params.get_input<bool>("Switch");
- switch ((eNodeSocketDatatype)storage.input_type) {
+ bool switch_value = params.get_input<bool>("Switch_001");
+
+ const std::string name_false = "False" + suffix;
+ const std::string name_true = "True" + suffix;
+ const std::string name_output = "Output" + suffix;
+
+ if (switch_value) {
+ params.set_input_unused(name_false);
+ if (params.lazy_require_input(name_true)) {
+ return;
+ }
+ params.set_output(name_output, params.extract_input<T>(name_true));
+ }
+ else {
+ params.set_input_unused(name_true);
+ if (params.lazy_require_input(name_false)) {
+ return;
+ }
+ params.set_output(name_output, params.extract_input<T>(name_false));
+ }
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ const NodeSwitch &storage = node_storage(params.node());
+ const eNodeSocketDatatype data_type = static_cast<eNodeSocketDatatype>(storage.input_type);
+
+ switch (data_type) {
+
case SOCK_FLOAT: {
- output_input<float>(params, input, "", "Output");
+ switch_fields<float>(params, "");
break;
}
case SOCK_INT: {
- output_input<int>(params, input, "_001", "Output_001");
+ switch_fields<int>(params, "_001");
break;
}
case SOCK_BOOLEAN: {
- output_input<bool>(params, input, "_002", "Output_002");
+ switch_fields<bool>(params, "_002");
break;
}
case SOCK_VECTOR: {
- output_input<float3>(params, input, "_003", "Output_003");
+ switch_fields<float3>(params, "_003");
break;
}
case SOCK_RGBA: {
- output_input<ColorGeometry4f>(params, input, "_004", "Output_004");
+ switch_fields<ColorGeometry4f>(params, "_004");
break;
}
case SOCK_STRING: {
- output_input<std::string>(params, input, "_005", "Output_005");
+ switch_fields<std::string>(params, "_005");
break;
}
case SOCK_GEOMETRY: {
- output_input<GeometrySet>(params, input, "_006", "Output_006");
+ switch_no_fields<GeometrySet>(params, "_006");
break;
}
case SOCK_OBJECT: {
- output_input<Object *>(params, input, "_007", "Output_007");
+ switch_no_fields<Object *>(params, "_007");
break;
}
case SOCK_COLLECTION: {
- output_input<Collection *>(params, input, "_008", "Output_008");
+ switch_no_fields<Collection *>(params, "_008");
break;
}
case SOCK_TEXTURE: {
- output_input<Tex *>(params, input, "_009", "Output_009");
+ switch_no_fields<Tex *>(params, "_009");
break;
}
case SOCK_MATERIAL: {
- output_input<Material *>(params, input, "_010", "Output_010");
+ switch_no_fields<Material *>(params, "_010");
+ break;
+ }
+ case SOCK_IMAGE: {
+ switch_no_fields<Image *>(params, "_011");
break;
}
default:
@@ -170,19 +319,22 @@ static void geo_node_switch_exec(GeoNodeExecParams params)
}
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_switch_cc
void register_node_type_geo_switch()
{
+ namespace file_ns = blender::nodes::node_geo_switch_cc;
+
static bNodeType ntype;
- geo_node_type_base(&ntype, GEO_NODE_SWITCH, "Switch", NODE_CLASS_CONVERTER, 0);
- ntype.declare = blender::nodes::geo_node_switch_declare;
- node_type_init(&ntype, blender::nodes::geo_node_switch_init);
- node_type_update(&ntype, blender::nodes::geo_node_switch_update);
+ geo_node_type_base(&ntype, GEO_NODE_SWITCH, "Switch", NODE_CLASS_CONVERTER);
+ ntype.declare = file_ns::node_declare;
+ node_type_init(&ntype, file_ns::node_init);
+ node_type_update(&ntype, file_ns::node_update);
node_type_storage(&ntype, "NodeSwitch", node_free_standard_storage, node_copy_standard_storage);
- ntype.geometry_node_execute = blender::nodes::geo_node_switch_exec;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
ntype.geometry_node_execute_supports_laziness = true;
- ntype.draw_buttons = blender::nodes::geo_node_switch_layout;
+ ntype.gather_link_search_ops = file_ns::node_gather_link_searches;
+ ntype.draw_buttons = file_ns::node_layout;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc
new file mode 100644
index 00000000000..5a8d9ab470d
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc
@@ -0,0 +1,842 @@
+/*
+ * 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_kdopbvh.h"
+#include "BLI_task.hh"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_pointcloud_types.h"
+
+#include "BKE_attribute_math.hh"
+#include "BKE_bvhutils.h"
+#include "BKE_mesh_runtime.h"
+#include "BKE_mesh_sample.hh"
+
+#include "FN_generic_array.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "NOD_socket_search_link.hh"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_transfer_attribute_cc {
+
+using namespace blender::bke::mesh_surface_sample;
+using blender::fn::GArray;
+
+NODE_STORAGE_FUNCS(NodeGeometryTransferAttribute)
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>(N_("Source"))
+ .supported_type({GEO_COMPONENT_TYPE_MESH,
+ GEO_COMPONENT_TYPE_POINT_CLOUD,
+ GEO_COMPONENT_TYPE_CURVE,
+ GEO_COMPONENT_TYPE_INSTANCES});
+
+ b.add_input<decl::Vector>(N_("Attribute")).hide_value().supports_field();
+ b.add_input<decl::Float>(N_("Attribute"), "Attribute_001").hide_value().supports_field();
+ b.add_input<decl::Color>(N_("Attribute"), "Attribute_002").hide_value().supports_field();
+ b.add_input<decl::Bool>(N_("Attribute"), "Attribute_003").hide_value().supports_field();
+ b.add_input<decl::Int>(N_("Attribute"), "Attribute_004").hide_value().supports_field();
+
+ b.add_input<decl::Vector>(N_("Source Position"))
+ .implicit_field()
+ .make_available([](bNode &node) {
+ node_storage(node).mode = GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED;
+ });
+ b.add_input<decl::Int>(N_("Index")).implicit_field().make_available([](bNode &node) {
+ node_storage(node).mode = GEO_NODE_ATTRIBUTE_TRANSFER_INDEX;
+ });
+
+ b.add_output<decl::Vector>(N_("Attribute")).dependent_field({6, 7});
+ b.add_output<decl::Float>(N_("Attribute"), "Attribute_001").dependent_field({6, 7});
+ b.add_output<decl::Color>(N_("Attribute"), "Attribute_002").dependent_field({6, 7});
+ b.add_output<decl::Bool>(N_("Attribute"), "Attribute_003").dependent_field({6, 7});
+ b.add_output<decl::Int>(N_("Attribute"), "Attribute_004").dependent_field({6, 7});
+}
+
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ const bNode &node = *static_cast<const bNode *>(ptr->data);
+ const NodeGeometryTransferAttribute &storage = node_storage(node);
+ const GeometryNodeAttributeTransferMode mapping = (GeometryNodeAttributeTransferMode)
+ storage.mode;
+
+ uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE);
+ uiItemR(layout, ptr, "mapping", 0, "", ICON_NONE);
+ if (mapping != GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED) {
+ uiItemR(layout, ptr, "domain", 0, "", ICON_NONE);
+ }
+}
+
+static void node_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometryTransferAttribute *data = MEM_cnew<NodeGeometryTransferAttribute>(__func__);
+ data->data_type = CD_PROP_FLOAT;
+ data->mode = GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED;
+ node->storage = data;
+}
+
+static void node_update(bNodeTree *ntree, bNode *node)
+{
+ const NodeGeometryTransferAttribute &storage = node_storage(*node);
+ const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type);
+ const GeometryNodeAttributeTransferMode mapping = (GeometryNodeAttributeTransferMode)
+ storage.mode;
+
+ bNodeSocket *socket_geometry = (bNodeSocket *)node->inputs.first;
+ bNodeSocket *socket_vector = socket_geometry->next;
+ bNodeSocket *socket_float = socket_vector->next;
+ bNodeSocket *socket_color4f = socket_float->next;
+ bNodeSocket *socket_boolean = socket_color4f->next;
+ bNodeSocket *socket_int32 = socket_boolean->next;
+
+ bNodeSocket *socket_positions = socket_int32->next;
+ bNodeSocket *socket_indices = socket_positions->next;
+
+ nodeSetSocketAvailability(ntree, socket_vector, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(ntree, socket_float, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(ntree, socket_color4f, data_type == CD_PROP_COLOR);
+ nodeSetSocketAvailability(ntree, socket_boolean, data_type == CD_PROP_BOOL);
+ nodeSetSocketAvailability(ntree, socket_int32, data_type == CD_PROP_INT32);
+
+ nodeSetSocketAvailability(ntree, socket_positions, mapping != GEO_NODE_ATTRIBUTE_TRANSFER_INDEX);
+ nodeSetSocketAvailability(ntree, socket_indices, mapping == GEO_NODE_ATTRIBUTE_TRANSFER_INDEX);
+
+ bNodeSocket *out_socket_vector = (bNodeSocket *)node->outputs.first;
+ bNodeSocket *out_socket_float = out_socket_vector->next;
+ bNodeSocket *out_socket_color4f = out_socket_float->next;
+ bNodeSocket *out_socket_boolean = out_socket_color4f->next;
+ bNodeSocket *out_socket_int32 = out_socket_boolean->next;
+
+ nodeSetSocketAvailability(ntree, out_socket_vector, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(ntree, out_socket_float, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(ntree, out_socket_color4f, data_type == CD_PROP_COLOR);
+ nodeSetSocketAvailability(ntree, out_socket_boolean, data_type == CD_PROP_BOOL);
+ nodeSetSocketAvailability(ntree, out_socket_int32, data_type == CD_PROP_INT32);
+}
+
+static void node_gather_link_searches(GatherLinkSearchOpParams &params)
+{
+ const NodeDeclaration &declaration = *params.node_type().fixed_declaration;
+ search_link_ops_for_declarations(params, declaration.inputs().take_back(2));
+ search_link_ops_for_declarations(params, declaration.inputs().take_front(1));
+
+ const std::optional<CustomDataType> type = node_data_type_to_custom_data_type(
+ (eNodeSocketDatatype)params.other_socket().type);
+ if (type && *type != CD_PROP_STRING) {
+ /* The input and output sockets have the same name. */
+ params.add_item(IFACE_("Attribute"), [type](LinkSearchOpParams &params) {
+ bNode &node = params.add_node("GeometryNodeAttributeTransfer");
+ node_storage(node).data_type = *type;
+ params.update_and_connect_available_socket(node, "Attribute");
+ });
+ }
+}
+
+static void get_closest_in_bvhtree(BVHTreeFromMesh &tree_data,
+ const VArray<float3> &positions,
+ const IndexMask mask,
+ const MutableSpan<int> r_indices,
+ const MutableSpan<float> r_distances_sq,
+ const MutableSpan<float3> r_positions)
+{
+ BLI_assert(positions.size() >= r_indices.size());
+ BLI_assert(positions.size() >= r_distances_sq.size());
+ BLI_assert(positions.size() >= r_positions.size());
+
+ for (const int i : mask) {
+ BVHTreeNearest nearest;
+ nearest.dist_sq = FLT_MAX;
+ const float3 position = positions[i];
+ BLI_bvhtree_find_nearest(
+ tree_data.tree, position, &nearest, tree_data.nearest_callback, &tree_data);
+ if (!r_indices.is_empty()) {
+ r_indices[i] = nearest.index;
+ }
+ if (!r_distances_sq.is_empty()) {
+ r_distances_sq[i] = nearest.dist_sq;
+ }
+ if (!r_positions.is_empty()) {
+ r_positions[i] = nearest.co;
+ }
+ }
+}
+
+static void get_closest_pointcloud_points(const PointCloud &pointcloud,
+ const VArray<float3> &positions,
+ const IndexMask mask,
+ const MutableSpan<int> r_indices,
+ const MutableSpan<float> r_distances_sq)
+{
+ BLI_assert(positions.size() >= r_indices.size());
+ BLI_assert(pointcloud.totpoint > 0);
+
+ BVHTreeFromPointCloud tree_data;
+ BKE_bvhtree_from_pointcloud_get(&tree_data, &pointcloud, 2);
+
+ for (const int i : mask) {
+ BVHTreeNearest nearest;
+ nearest.dist_sq = FLT_MAX;
+ const float3 position = positions[i];
+ BLI_bvhtree_find_nearest(
+ tree_data.tree, position, &nearest, tree_data.nearest_callback, &tree_data);
+ r_indices[i] = nearest.index;
+ if (!r_distances_sq.is_empty()) {
+ r_distances_sq[i] = nearest.dist_sq;
+ }
+ }
+
+ free_bvhtree_from_pointcloud(&tree_data);
+}
+
+static void get_closest_mesh_points(const Mesh &mesh,
+ const VArray<float3> &positions,
+ const IndexMask mask,
+ const MutableSpan<int> r_point_indices,
+ const MutableSpan<float> r_distances_sq,
+ const MutableSpan<float3> r_positions)
+{
+ BLI_assert(mesh.totvert > 0);
+ BVHTreeFromMesh tree_data;
+ BKE_bvhtree_from_mesh_get(&tree_data, &mesh, BVHTREE_FROM_VERTS, 2);
+ get_closest_in_bvhtree(tree_data, positions, mask, r_point_indices, r_distances_sq, r_positions);
+ free_bvhtree_from_mesh(&tree_data);
+}
+
+static void get_closest_mesh_edges(const Mesh &mesh,
+ const VArray<float3> &positions,
+ const IndexMask mask,
+ const MutableSpan<int> r_edge_indices,
+ const MutableSpan<float> r_distances_sq,
+ const MutableSpan<float3> r_positions)
+{
+ BLI_assert(mesh.totedge > 0);
+ BVHTreeFromMesh tree_data;
+ BKE_bvhtree_from_mesh_get(&tree_data, &mesh, BVHTREE_FROM_EDGES, 2);
+ get_closest_in_bvhtree(tree_data, positions, mask, r_edge_indices, r_distances_sq, r_positions);
+ free_bvhtree_from_mesh(&tree_data);
+}
+
+static void get_closest_mesh_looptris(const Mesh &mesh,
+ const VArray<float3> &positions,
+ const IndexMask mask,
+ const MutableSpan<int> r_looptri_indices,
+ const MutableSpan<float> r_distances_sq,
+ const MutableSpan<float3> r_positions)
+{
+ BLI_assert(mesh.totpoly > 0);
+ BVHTreeFromMesh tree_data;
+ BKE_bvhtree_from_mesh_get(&tree_data, &mesh, BVHTREE_FROM_LOOPTRI, 2);
+ get_closest_in_bvhtree(
+ tree_data, positions, mask, r_looptri_indices, r_distances_sq, r_positions);
+ free_bvhtree_from_mesh(&tree_data);
+}
+
+static void get_closest_mesh_polygons(const Mesh &mesh,
+ const VArray<float3> &positions,
+ const IndexMask mask,
+ const MutableSpan<int> r_poly_indices,
+ const MutableSpan<float> r_distances_sq,
+ const MutableSpan<float3> r_positions)
+{
+ BLI_assert(mesh.totpoly > 0);
+
+ Array<int> looptri_indices(positions.size());
+ get_closest_mesh_looptris(mesh, positions, mask, looptri_indices, r_distances_sq, r_positions);
+
+ const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&mesh),
+ BKE_mesh_runtime_looptri_len(&mesh)};
+
+ for (const int i : mask) {
+ const MLoopTri &looptri = looptris[looptri_indices[i]];
+ r_poly_indices[i] = looptri.poly;
+ }
+}
+
+/* The closest corner is defined to be the closest corner on the closest face. */
+static void get_closest_mesh_corners(const Mesh &mesh,
+ const VArray<float3> &positions,
+ const IndexMask mask,
+ const MutableSpan<int> r_corner_indices,
+ const MutableSpan<float> r_distances_sq,
+ const MutableSpan<float3> r_positions)
+{
+ BLI_assert(mesh.totloop > 0);
+ Array<int> poly_indices(positions.size());
+ get_closest_mesh_polygons(mesh, positions, mask, poly_indices, {}, {});
+
+ for (const int i : mask) {
+ const float3 position = positions[i];
+ const int poly_index = poly_indices[i];
+ const MPoly &poly = mesh.mpoly[poly_index];
+
+ /* Find the closest vertex in the polygon. */
+ float min_distance_sq = FLT_MAX;
+ const MVert *closest_mvert;
+ int closest_loop_index = 0;
+ for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
+ const MLoop &loop = mesh.mloop[loop_index];
+ const int vertex_index = loop.v;
+ const MVert &mvert = mesh.mvert[vertex_index];
+ const float distance_sq = math::distance_squared(position, float3(mvert.co));
+ if (distance_sq < min_distance_sq) {
+ min_distance_sq = distance_sq;
+ closest_loop_index = loop_index;
+ closest_mvert = &mvert;
+ }
+ }
+ if (!r_corner_indices.is_empty()) {
+ r_corner_indices[i] = closest_loop_index;
+ }
+ if (!r_positions.is_empty()) {
+ r_positions[i] = closest_mvert->co;
+ }
+ if (!r_distances_sq.is_empty()) {
+ r_distances_sq[i] = min_distance_sq;
+ }
+ }
+}
+
+template<typename T>
+void copy_with_indices(const VArray<T> &src,
+ const IndexMask mask,
+ const Span<int> indices,
+ const MutableSpan<T> dst)
+{
+ if (src.is_empty()) {
+ return;
+ }
+ for (const int i : mask) {
+ dst[i] = src[indices[i]];
+ }
+}
+
+template<typename T>
+void copy_with_indices_clamped(const VArray<T> &src,
+ const IndexMask mask,
+ const VArray<int> &indices,
+ const MutableSpan<T> dst)
+{
+ if (src.is_empty()) {
+ return;
+ }
+ const int max_index = src.size() - 1;
+ threading::parallel_for(mask.index_range(), 4096, [&](IndexRange range) {
+ for (const int i : range) {
+ const int index = mask[i];
+ dst[index] = src[std::clamp(indices[index], 0, max_index)];
+ }
+ });
+}
+
+template<typename T>
+void copy_with_indices_and_comparison(const VArray<T> &src_1,
+ const VArray<T> &src_2,
+ const Span<float> distances_1,
+ const Span<float> distances_2,
+ const IndexMask mask,
+ const Span<int> indices_1,
+ const Span<int> indices_2,
+ const MutableSpan<T> dst)
+{
+ if (src_1.is_empty() || src_2.is_empty()) {
+ return;
+ }
+ for (const int i : mask) {
+ if (distances_1[i] < distances_2[i]) {
+ dst[i] = src_1[indices_1[i]];
+ }
+ else {
+ dst[i] = src_2[indices_2[i]];
+ }
+ }
+}
+
+static bool component_is_available(const GeometrySet &geometry,
+ const GeometryComponentType type,
+ const AttributeDomain domain)
+{
+ if (!geometry.has(type)) {
+ return false;
+ }
+ const GeometryComponent &component = *geometry.get_component_for_read(type);
+ if (component.is_empty()) {
+ return false;
+ }
+ return component.attribute_domain_size(domain) != 0;
+}
+
+/**
+ * \note Multi-threading for this function is provided by the field evaluator. Since the #call
+ * function could be called many times, calculate the data from the target geometry once and store
+ * it for later.
+ */
+class NearestInterpolatedTransferFunction : public fn::MultiFunction {
+ GeometrySet target_;
+ GField src_field_;
+
+ /**
+ * This function is meant to sample the surface of a mesh rather than take the value from
+ * individual elements, so use the most complex domain, ensuring no information is lost. In the
+ * future, it should be possible to use the most complex domain required by the field inputs, to
+ * simplify sampling and avoid domain conversions.
+ */
+ AttributeDomain domain_ = ATTR_DOMAIN_CORNER;
+
+ fn::MFSignature signature_;
+
+ std::optional<GeometryComponentFieldContext> target_context_;
+ std::unique_ptr<FieldEvaluator> target_evaluator_;
+ const GVArray *target_data_;
+
+ public:
+ NearestInterpolatedTransferFunction(GeometrySet geometry, GField src_field)
+ : target_(std::move(geometry)), src_field_(std::move(src_field))
+ {
+ target_.ensure_owns_direct_data();
+ signature_ = this->create_signature();
+ this->set_signature(&signature_);
+ this->evaluate_target_field();
+ }
+
+ fn::MFSignature create_signature()
+ {
+ blender::fn::MFSignatureBuilder signature{"Attribute Transfer Nearest Interpolated"};
+ signature.single_input<float3>("Position");
+ signature.single_output("Attribute", src_field_.cpp_type());
+ return signature.build();
+ }
+
+ void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override
+ {
+ const VArray<float3> &positions = params.readonly_single_input<float3>(0, "Position");
+ GMutableSpan dst = params.uninitialized_single_output_if_required(1, "Attribute");
+
+ const MeshComponent &mesh_component = *target_.get_component_for_read<MeshComponent>();
+ BLI_assert(mesh_component.has_mesh());
+ const Mesh &mesh = *mesh_component.get_for_read();
+ BLI_assert(mesh.totpoly > 0);
+
+ /* Find closest points on the mesh surface. */
+ Array<int> looptri_indices(mask.min_array_size());
+ Array<float3> sampled_positions(mask.min_array_size());
+ get_closest_mesh_looptris(mesh, positions, mask, looptri_indices, {}, sampled_positions);
+
+ MeshAttributeInterpolator interp(&mesh, mask, sampled_positions, looptri_indices);
+ interp.sample_data(*target_data_, domain_, eAttributeMapMode::INTERPOLATED, dst);
+ }
+
+ private:
+ void evaluate_target_field()
+ {
+ const MeshComponent &mesh_component = *target_.get_component_for_read<MeshComponent>();
+ target_context_.emplace(GeometryComponentFieldContext{mesh_component, domain_});
+ const int domain_size = mesh_component.attribute_domain_size(domain_);
+ target_evaluator_ = std::make_unique<FieldEvaluator>(*target_context_, domain_size);
+ target_evaluator_->add(src_field_);
+ target_evaluator_->evaluate();
+ target_data_ = &target_evaluator_->get_evaluated(0);
+ }
+};
+
+/**
+ * \note Multi-threading for this function is provided by the field evaluator. Since the #call
+ * function could be called many times, calculate the data from the target geometry once and store
+ * it for later.
+ */
+class NearestTransferFunction : public fn::MultiFunction {
+ GeometrySet target_;
+ GField src_field_;
+ AttributeDomain domain_;
+
+ fn::MFSignature signature_;
+
+ bool use_mesh_;
+ bool use_points_;
+
+ /* Store data from the target as a virtual array, since we may only access a few indices. */
+ std::optional<GeometryComponentFieldContext> mesh_context_;
+ std::unique_ptr<FieldEvaluator> mesh_evaluator_;
+ const GVArray *mesh_data_;
+
+ std::optional<GeometryComponentFieldContext> point_context_;
+ std::unique_ptr<FieldEvaluator> point_evaluator_;
+ const GVArray *point_data_;
+
+ public:
+ NearestTransferFunction(GeometrySet geometry, GField src_field, AttributeDomain domain)
+ : target_(std::move(geometry)), src_field_(std::move(src_field)), domain_(domain)
+ {
+ target_.ensure_owns_direct_data();
+ signature_ = this->create_signature();
+ this->set_signature(&signature_);
+
+ this->use_mesh_ = component_is_available(target_, GEO_COMPONENT_TYPE_MESH, domain_);
+ this->use_points_ = component_is_available(target_, GEO_COMPONENT_TYPE_POINT_CLOUD, domain_);
+
+ this->evaluate_target_field();
+ }
+
+ fn::MFSignature create_signature()
+ {
+ blender::fn::MFSignatureBuilder signature{"Attribute Transfer Nearest"};
+ signature.single_input<float3>("Position");
+ signature.single_output("Attribute", src_field_.cpp_type());
+ return signature.build();
+ }
+
+ void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override
+ {
+ const VArray<float3> &positions = params.readonly_single_input<float3>(0, "Position");
+ GMutableSpan dst = params.uninitialized_single_output_if_required(1, "Attribute");
+
+ if (!use_mesh_ && !use_points_) {
+ dst.type().fill_construct_indices(dst.type().default_value(), dst.data(), mask);
+ return;
+ }
+
+ const Mesh *mesh = use_mesh_ ? target_.get_mesh_for_read() : nullptr;
+ const PointCloud *pointcloud = use_points_ ? target_.get_pointcloud_for_read() : nullptr;
+
+ const int tot_samples = mask.min_array_size();
+
+ Array<int> point_indices;
+ Array<float> point_distances;
+
+ /* Depending on where what domain the source attribute lives, these indices are either vertex,
+ * corner, edge or polygon indices. */
+ Array<int> mesh_indices;
+ Array<float> mesh_distances;
+
+ /* If there is a point cloud, find the closest points. */
+ if (use_points_) {
+ point_indices.reinitialize(tot_samples);
+ if (use_mesh_) {
+ point_distances.reinitialize(tot_samples);
+ }
+ get_closest_pointcloud_points(*pointcloud, positions, mask, point_indices, point_distances);
+ }
+
+ /* If there is a mesh, find the closest mesh elements. */
+ if (use_mesh_) {
+ mesh_indices.reinitialize(tot_samples);
+ if (use_points_) {
+ mesh_distances.reinitialize(tot_samples);
+ }
+ switch (domain_) {
+ case ATTR_DOMAIN_POINT: {
+ get_closest_mesh_points(*mesh, positions, mask, mesh_indices, mesh_distances, {});
+ break;
+ }
+ case ATTR_DOMAIN_EDGE: {
+ get_closest_mesh_edges(*mesh, positions, mask, mesh_indices, mesh_distances, {});
+ break;
+ }
+ case ATTR_DOMAIN_FACE: {
+ get_closest_mesh_polygons(*mesh, positions, mask, mesh_indices, mesh_distances, {});
+ break;
+ }
+ case ATTR_DOMAIN_CORNER: {
+ get_closest_mesh_corners(*mesh, positions, mask, mesh_indices, mesh_distances, {});
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+ }
+
+ attribute_math::convert_to_static_type(dst.type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ if (use_mesh_ && use_points_) {
+ VArray<T> src_mesh = mesh_data_->typed<T>();
+ VArray<T> src_point = point_data_->typed<T>();
+ copy_with_indices_and_comparison(src_mesh,
+ src_point,
+ mesh_distances,
+ point_distances,
+ mask,
+ mesh_indices,
+ point_indices,
+ dst.typed<T>());
+ }
+ else if (use_points_) {
+ VArray<T> src_point = point_data_->typed<T>();
+ copy_with_indices(src_point, mask, point_indices, dst.typed<T>());
+ }
+ else if (use_mesh_) {
+ VArray<T> src_mesh = mesh_data_->typed<T>();
+ copy_with_indices(src_mesh, mask, mesh_indices, dst.typed<T>());
+ }
+ });
+ }
+
+ private:
+ void evaluate_target_field()
+ {
+ if (use_mesh_) {
+ const MeshComponent &mesh = *target_.get_component_for_read<MeshComponent>();
+ const int domain_size = mesh.attribute_domain_size(domain_);
+ mesh_context_.emplace(GeometryComponentFieldContext(mesh, domain_));
+ mesh_evaluator_ = std::make_unique<FieldEvaluator>(*mesh_context_, domain_size);
+ mesh_evaluator_->add(src_field_);
+ mesh_evaluator_->evaluate();
+ mesh_data_ = &mesh_evaluator_->get_evaluated(0);
+ }
+
+ if (use_points_) {
+ const PointCloudComponent &points = *target_.get_component_for_read<PointCloudComponent>();
+ const int domain_size = points.attribute_domain_size(domain_);
+ point_context_.emplace(GeometryComponentFieldContext(points, domain_));
+ point_evaluator_ = std::make_unique<FieldEvaluator>(*point_context_, domain_size);
+ point_evaluator_->add(src_field_);
+ point_evaluator_->evaluate();
+ point_data_ = &point_evaluator_->get_evaluated(0);
+ }
+ }
+};
+
+static const GeometryComponent *find_target_component(const GeometrySet &geometry,
+ const AttributeDomain domain)
+{
+ /* Choose the other component based on a consistent order, rather than some more complicated
+ * heuristic. This is the same order visible in the spreadsheet and used in the ray-cast node. */
+ static const Array<GeometryComponentType> supported_types = {GEO_COMPONENT_TYPE_MESH,
+ GEO_COMPONENT_TYPE_POINT_CLOUD,
+ GEO_COMPONENT_TYPE_CURVE,
+ GEO_COMPONENT_TYPE_INSTANCES};
+ for (const GeometryComponentType src_type : supported_types) {
+ if (component_is_available(geometry, src_type, domain)) {
+ return geometry.get_component_for_read(src_type);
+ }
+ }
+
+ return nullptr;
+}
+
+/**
+ * The index-based transfer theoretically does not need realized data when there is only one
+ * instance geometry set in the target. A future optimization could be removing that limitation
+ * internally.
+ */
+class IndexTransferFunction : public fn::MultiFunction {
+ GeometrySet src_geometry_;
+ GField src_field_;
+ AttributeDomain domain_;
+
+ fn::MFSignature signature_;
+
+ std::optional<GeometryComponentFieldContext> geometry_context_;
+ std::unique_ptr<FieldEvaluator> evaluator_;
+ const GVArray *src_data_ = nullptr;
+
+ public:
+ IndexTransferFunction(GeometrySet geometry, GField src_field, const AttributeDomain domain)
+ : src_geometry_(std::move(geometry)), src_field_(std::move(src_field)), domain_(domain)
+ {
+ src_geometry_.ensure_owns_direct_data();
+
+ signature_ = this->create_signature();
+ this->set_signature(&signature_);
+
+ this->evaluate_field();
+ }
+
+ fn::MFSignature create_signature()
+ {
+ fn::MFSignatureBuilder signature{"Attribute Transfer Index"};
+ signature.single_input<int>("Index");
+ signature.single_output("Attribute", src_field_.cpp_type());
+ return signature.build();
+ }
+
+ void evaluate_field()
+ {
+ const GeometryComponent *component = find_target_component(src_geometry_, domain_);
+ if (component == nullptr) {
+ return;
+ }
+ const int domain_size = component->attribute_domain_size(domain_);
+ geometry_context_.emplace(GeometryComponentFieldContext(*component, domain_));
+ evaluator_ = std::make_unique<FieldEvaluator>(*geometry_context_, domain_size);
+ evaluator_->add(src_field_);
+ evaluator_->evaluate();
+ src_data_ = &evaluator_->get_evaluated(0);
+ }
+
+ void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override
+ {
+ const VArray<int> &indices = params.readonly_single_input<int>(0, "Index");
+ GMutableSpan dst = params.uninitialized_single_output(1, "Attribute");
+
+ const CPPType &type = dst.type();
+ if (src_data_ == nullptr) {
+ type.fill_construct_indices(type.default_value(), dst.data(), mask);
+ return;
+ }
+
+ attribute_math::convert_to_static_type(type, [&](auto dummy) {
+ using T = decltype(dummy);
+ copy_with_indices_clamped(src_data_->typed<T>(), mask, indices, dst.typed<T>());
+ });
+ }
+};
+
+static GField get_input_attribute_field(GeoNodeExecParams &params, const CustomDataType data_type)
+{
+ switch (data_type) {
+ case CD_PROP_FLOAT:
+ return params.extract_input<Field<float>>("Attribute_001");
+ case CD_PROP_FLOAT3:
+ return params.extract_input<Field<float3>>("Attribute");
+ case CD_PROP_COLOR:
+ return params.extract_input<Field<ColorGeometry4f>>("Attribute_002");
+ case CD_PROP_BOOL:
+ return params.extract_input<Field<bool>>("Attribute_003");
+ case CD_PROP_INT32:
+ return params.extract_input<Field<int>>("Attribute_004");
+ default:
+ BLI_assert_unreachable();
+ }
+ return {};
+}
+
+static void output_attribute_field(GeoNodeExecParams &params, GField field)
+{
+ switch (bke::cpp_type_to_custom_data_type(field.cpp_type())) {
+ case CD_PROP_FLOAT: {
+ params.set_output("Attribute_001", Field<float>(field));
+ break;
+ }
+ case CD_PROP_FLOAT3: {
+ params.set_output("Attribute", Field<float3>(field));
+ break;
+ }
+ case CD_PROP_COLOR: {
+ params.set_output("Attribute_002", Field<ColorGeometry4f>(field));
+ break;
+ }
+ case CD_PROP_BOOL: {
+ params.set_output("Attribute_003", Field<bool>(field));
+ break;
+ }
+ case CD_PROP_INT32: {
+ params.set_output("Attribute_004", Field<int>(field));
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry = params.extract_input<GeometrySet>("Source");
+ const NodeGeometryTransferAttribute &storage = node_storage(params.node());
+ const GeometryNodeAttributeTransferMode mapping = (GeometryNodeAttributeTransferMode)
+ storage.mode;
+ const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type);
+ const AttributeDomain domain = static_cast<AttributeDomain>(storage.domain);
+
+ GField field = get_input_attribute_field(params, data_type);
+
+ auto return_default = [&]() {
+ attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ using T = decltype(dummy);
+ output_attribute_field(params, fn::make_constant_field<T>(T()));
+ });
+ };
+
+ GField output_field;
+ switch (mapping) {
+ case GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED: {
+ const Mesh *mesh = geometry.get_mesh_for_read();
+ if (mesh == nullptr) {
+ if (!geometry.is_empty()) {
+ params.error_message_add(NodeWarningType::Error,
+ TIP_("The target geometry must contain a mesh"));
+ }
+ return return_default();
+ }
+ if (mesh->totpoly == 0) {
+ /* Don't add a warning for empty meshes. */
+ if (mesh->totvert != 0) {
+ params.error_message_add(NodeWarningType::Error,
+ TIP_("The target mesh must have faces"));
+ }
+ return return_default();
+ }
+ auto fn = std::make_unique<NearestInterpolatedTransferFunction>(std::move(geometry),
+ std::move(field));
+ auto op = std::make_shared<FieldOperation>(
+ FieldOperation(std::move(fn), {params.extract_input<Field<float3>>("Source Position")}));
+ output_field = GField(std::move(op));
+ break;
+ }
+ case GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST: {
+ if (geometry.has_curve() && !geometry.has_mesh() && !geometry.has_pointcloud()) {
+ params.error_message_add(NodeWarningType::Error,
+ TIP_("The target geometry must contain a mesh or a point cloud"));
+ return return_default();
+ }
+ auto fn = std::make_unique<NearestTransferFunction>(
+ std::move(geometry), std::move(field), domain);
+ auto op = std::make_shared<FieldOperation>(
+ FieldOperation(std::move(fn), {params.extract_input<Field<float3>>("Source Position")}));
+ output_field = GField(std::move(op));
+ break;
+ }
+ case GEO_NODE_ATTRIBUTE_TRANSFER_INDEX: {
+ Field<int> indices = params.extract_input<Field<int>>("Index");
+ auto fn = std::make_unique<IndexTransferFunction>(
+ std::move(geometry), std::move(field), domain);
+ auto op = std::make_shared<FieldOperation>(
+ FieldOperation(std::move(fn), {std::move(indices)}));
+ output_field = GField(std::move(op));
+ break;
+ }
+ }
+
+ output_attribute_field(params, std::move(output_field));
+}
+
+} // namespace blender::nodes::node_geo_transfer_attribute_cc
+
+void register_node_type_geo_transfer_attribute()
+{
+ namespace file_ns = blender::nodes::node_geo_transfer_attribute_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_TRANSFER_ATTRIBUTE, "Transfer Attribute", NODE_CLASS_ATTRIBUTE);
+ node_type_init(&ntype, file_ns::node_init);
+ node_type_update(&ntype, file_ns::node_update);
+ node_type_storage(&ntype,
+ "NodeGeometryTransferAttribute",
+ node_free_standard_storage,
+ node_copy_standard_storage);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.draw_buttons = file_ns::node_layout;
+ ntype.gather_link_search_ops = file_ns::node_gather_link_searches;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_transform.cc b/source/blender/nodes/geometry/nodes/node_geo_transform.cc
index d5eb067cad0..6187a2eacf9 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_transform.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_transform.cc
@@ -25,6 +25,7 @@
#include "DNA_volume_types.h"
#include "BKE_mesh.h"
+#include "BKE_pointcloud.h"
#include "BKE_spline.hh"
#include "BKE_volume.h"
@@ -34,18 +35,9 @@
namespace blender::nodes {
-static void geo_node_transform_declare(NodeDeclarationBuilder &b)
-{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Vector>("Translation").subtype(PROP_TRANSLATION);
- b.add_input<decl::Vector>("Rotation").subtype(PROP_EULER);
- b.add_input<decl::Vector>("Scale").default_value({1, 1, 1}).subtype(PROP_XYZ);
- b.add_output<decl::Geometry>("Geometry");
-}
-
static bool use_translate(const float3 rotation, const float3 scale)
{
- if (compare_ff(rotation.length_squared(), 0.0f, 1e-9f) != 1) {
+ if (compare_ff(math::length_squared(rotation), 0.0f, 1e-9f) != 1) {
return false;
}
if (compare_ff(scale.x, 1.0f, 1e-9f) != 1 || compare_ff(scale.y, 1.0f, 1e-9f) != 1 ||
@@ -55,153 +47,187 @@ static bool use_translate(const float3 rotation, const float3 scale)
return true;
}
-void transform_mesh(Mesh *mesh,
- const float3 translation,
- const float3 rotation,
- const float3 scale)
+static void translate_mesh(Mesh &mesh, const float3 translation)
{
- /* Use only translation if rotation and scale are zero. */
- if (use_translate(rotation, scale)) {
- if (!translation.is_zero()) {
- BKE_mesh_translate(mesh, translation, false);
- }
- }
- else {
- const float4x4 matrix = float4x4::from_loc_eul_scale(translation, rotation, scale);
- BKE_mesh_transform(mesh, matrix.values, false);
- BKE_mesh_normals_tag_dirty(mesh);
+ if (!math::is_zero(translation)) {
+ BKE_mesh_translate(&mesh, translation, false);
}
}
-static void transform_pointcloud(PointCloud *pointcloud,
- const float3 translation,
- const float3 rotation,
- const float3 scale)
+static void transform_mesh(Mesh &mesh, const float4x4 &transform)
{
- /* Use only translation if rotation and scale don't apply. */
- if (use_translate(rotation, scale)) {
- for (const int i : IndexRange(pointcloud->totpoint)) {
- add_v3_v3(pointcloud->co[i], translation);
- }
+ BKE_mesh_transform(&mesh, transform.values, false);
+ BKE_mesh_normals_tag_dirty(&mesh);
+}
+
+static void translate_pointcloud(PointCloud &pointcloud, const float3 translation)
+{
+ CustomData_duplicate_referenced_layer(&pointcloud.pdata, CD_PROP_FLOAT3, pointcloud.totpoint);
+ BKE_pointcloud_update_customdata_pointers(&pointcloud);
+ for (const int i : IndexRange(pointcloud.totpoint)) {
+ add_v3_v3(pointcloud.co[i], translation);
}
- else {
- const float4x4 matrix = float4x4::from_loc_eul_scale(translation, rotation, scale);
- for (const int i : IndexRange(pointcloud->totpoint)) {
- float3 &co = *(float3 *)pointcloud->co[i];
- co = matrix * co;
- }
+}
+
+static void transform_pointcloud(PointCloud &pointcloud, const float4x4 &transform)
+{
+ CustomData_duplicate_referenced_layer(&pointcloud.pdata, CD_PROP_FLOAT3, pointcloud.totpoint);
+ BKE_pointcloud_update_customdata_pointers(&pointcloud);
+ for (const int i : IndexRange(pointcloud.totpoint)) {
+ float3 &co = *(float3 *)pointcloud.co[i];
+ co = transform * co;
}
}
-static void transform_instances(InstancesComponent &instances,
- const float3 translation,
- const float3 rotation,
- const float3 scale)
+static void translate_instances(InstancesComponent &instances, const float3 translation)
{
MutableSpan<float4x4> transforms = instances.instance_transforms();
-
- /* Use only translation if rotation and scale don't apply. */
- if (use_translate(rotation, scale)) {
- for (float4x4 &transform : transforms) {
- add_v3_v3(transform.ptr()[3], translation);
- }
+ for (float4x4 &transform : transforms) {
+ add_v3_v3(transform.ptr()[3], translation);
}
- else {
- const float4x4 matrix = float4x4::from_loc_eul_scale(translation, rotation, scale);
- for (float4x4 &transform : transforms) {
- transform = matrix * transform;
- }
+}
+
+static void transform_instances(InstancesComponent &instances, const float4x4 &transform)
+{
+ MutableSpan<float4x4> instance_transforms = instances.instance_transforms();
+ for (float4x4 &instance_transform : instance_transforms) {
+ instance_transform = transform * instance_transform;
}
}
-static void transform_volume(Volume *volume,
- const float3 translation,
- const float3 rotation,
- const float3 scale,
- GeoNodeExecParams &params)
+static void transform_volume(Volume &volume, const float4x4 &transform, const Depsgraph &depsgraph)
{
#ifdef WITH_OPENVDB
/* Scaling an axis to zero is not supported for volumes. */
+ const float3 translation = transform.translation();
+ const float3 rotation = transform.to_euler();
+ const float3 scale = transform.scale();
const float3 limited_scale = {
(scale.x == 0.0f) ? FLT_EPSILON : scale.x,
(scale.y == 0.0f) ? FLT_EPSILON : scale.y,
(scale.z == 0.0f) ? FLT_EPSILON : scale.z,
};
+ const float4x4 scale_limited_transform = float4x4::from_loc_eul_scale(
+ translation, rotation, limited_scale);
- const Main *bmain = DEG_get_bmain(params.depsgraph());
- BKE_volume_load(volume, bmain);
-
- const float4x4 matrix = float4x4::from_loc_eul_scale(translation, rotation, limited_scale);
+ const Main *bmain = DEG_get_bmain(&depsgraph);
+ BKE_volume_load(&volume, bmain);
openvdb::Mat4s vdb_matrix;
- memcpy(vdb_matrix.asPointer(), matrix, sizeof(float[4][4]));
+ memcpy(vdb_matrix.asPointer(), &scale_limited_transform, sizeof(float[4][4]));
openvdb::Mat4d vdb_matrix_d{vdb_matrix};
- const int num_grids = BKE_volume_num_grids(volume);
+ const int num_grids = BKE_volume_num_grids(&volume);
for (const int i : IndexRange(num_grids)) {
- VolumeGrid *volume_grid = BKE_volume_grid_get_for_write(volume, i);
+ VolumeGrid *volume_grid = BKE_volume_grid_get_for_write(&volume, i);
- openvdb::GridBase::Ptr grid = BKE_volume_grid_openvdb_for_write(volume, volume_grid, false);
+ openvdb::GridBase::Ptr grid = BKE_volume_grid_openvdb_for_write(&volume, volume_grid, false);
openvdb::math::Transform &grid_transform = grid->transform();
grid_transform.postMult(vdb_matrix_d);
}
#else
- UNUSED_VARS(volume, translation, rotation, scale, params);
+ UNUSED_VARS(volume, transform, depsgraph);
#endif
}
-static void transform_curve(CurveEval &curve,
- const float3 translation,
- const float3 rotation,
- const float3 scale)
+static void translate_volume(Volume &volume, const float3 translation, const Depsgraph &depsgraph)
{
- if (use_translate(rotation, scale)) {
- curve.translate(translation);
+ transform_volume(volume, float4x4::from_location(translation), depsgraph);
+}
+
+static void translate_geometry_set(GeometrySet &geometry,
+ const float3 translation,
+ const Depsgraph &depsgraph)
+{
+ if (CurveEval *curve = geometry.get_curve_for_write()) {
+ curve->translate(translation);
}
- else {
- const float4x4 matrix = float4x4::from_loc_eul_scale(translation, rotation, scale);
- curve.transform(matrix);
+ if (Mesh *mesh = geometry.get_mesh_for_write()) {
+ translate_mesh(*mesh, translation);
+ }
+ if (PointCloud *pointcloud = geometry.get_pointcloud_for_write()) {
+ translate_pointcloud(*pointcloud, translation);
+ }
+ if (Volume *volume = geometry.get_volume_for_write()) {
+ translate_volume(*volume, translation, depsgraph);
+ }
+ if (geometry.has_instances()) {
+ translate_instances(geometry.get_component_for_write<InstancesComponent>(), translation);
+ }
+}
+
+void transform_geometry_set(GeometrySet &geometry,
+ const float4x4 &transform,
+ const Depsgraph &depsgraph)
+{
+ if (CurveEval *curve = geometry.get_curve_for_write()) {
+ curve->transform(transform);
+ }
+ if (Mesh *mesh = geometry.get_mesh_for_write()) {
+ transform_mesh(*mesh, transform);
+ }
+ if (PointCloud *pointcloud = geometry.get_pointcloud_for_write()) {
+ transform_pointcloud(*pointcloud, transform);
+ }
+ if (Volume *volume = geometry.get_volume_for_write()) {
+ transform_volume(*volume, transform, depsgraph);
+ }
+ if (geometry.has_instances()) {
+ transform_instances(geometry.get_component_for_write<InstancesComponent>(), transform);
}
}
-static void geo_node_transform_exec(GeoNodeExecParams params)
+void transform_mesh(Mesh &mesh,
+ const float3 translation,
+ const float3 rotation,
+ const float3 scale)
+{
+ const float4x4 matrix = float4x4::from_loc_eul_scale(translation, rotation, scale);
+ transform_mesh(mesh, matrix);
+}
+
+} // namespace blender::nodes
+
+namespace blender::nodes::node_geo_transform_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::Vector>(N_("Translation")).subtype(PROP_TRANSLATION);
+ b.add_input<decl::Vector>(N_("Rotation")).subtype(PROP_EULER);
+ b.add_input<decl::Vector>(N_("Scale")).default_value({1, 1, 1}).subtype(PROP_XYZ);
+ b.add_output<decl::Geometry>(N_("Geometry"));
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
const float3 translation = params.extract_input<float3>("Translation");
const float3 rotation = params.extract_input<float3>("Rotation");
const float3 scale = params.extract_input<float3>("Scale");
- if (geometry_set.has_mesh()) {
- Mesh *mesh = geometry_set.get_mesh_for_write();
- transform_mesh(mesh, translation, rotation, scale);
- }
- if (geometry_set.has_pointcloud()) {
- PointCloud *pointcloud = geometry_set.get_pointcloud_for_write();
- transform_pointcloud(pointcloud, translation, rotation, scale);
- }
- if (geometry_set.has_instances()) {
- InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>();
- transform_instances(instances, translation, rotation, scale);
- }
- if (geometry_set.has_volume()) {
- Volume *volume = geometry_set.get_volume_for_write();
- transform_volume(volume, translation, rotation, scale, params);
+ /* Use only translation if rotation and scale don't apply. */
+ if (use_translate(rotation, scale)) {
+ translate_geometry_set(geometry_set, translation, *params.depsgraph());
}
- if (geometry_set.has_curve()) {
- CurveEval *curve = geometry_set.get_curve_for_write();
- transform_curve(*curve, translation, rotation, scale);
+ else {
+ transform_geometry_set(geometry_set,
+ float4x4::from_loc_eul_scale(translation, rotation, scale),
+ *params.depsgraph());
}
params.set_output("Geometry", std::move(geometry_set));
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_transform_cc
void register_node_type_geo_transform()
{
+ namespace file_ns = blender::nodes::node_geo_transform_cc;
+
static bNodeType ntype;
- geo_node_type_base(&ntype, GEO_NODE_TRANSFORM, "Transform", NODE_CLASS_GEOMETRY, 0);
- ntype.declare = blender::nodes::geo_node_transform_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_transform_exec;
+ geo_node_type_base(&ntype, GEO_NODE_TRANSFORM, "Transform", NODE_CLASS_GEOMETRY);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc
new file mode 100644
index 00000000000..91c503ff047
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc
@@ -0,0 +1,84 @@
+/*
+ * 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 "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_translate_instances_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>(N_("Instances")).only_instances();
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field();
+ b.add_input<decl::Vector>(N_("Translation")).subtype(PROP_TRANSLATION).supports_field();
+ b.add_input<decl::Bool>(N_("Local Space")).default_value(true).supports_field();
+ b.add_output<decl::Geometry>(N_("Instances"));
+}
+
+static void translate_instances(GeoNodeExecParams &params, InstancesComponent &instances_component)
+{
+ GeometryComponentFieldContext field_context{instances_component, ATTR_DOMAIN_INSTANCE};
+
+ fn::FieldEvaluator evaluator{field_context, instances_component.instances_amount()};
+ evaluator.set_selection(params.extract_input<Field<bool>>("Selection"));
+ evaluator.add(params.extract_input<Field<float3>>("Translation"));
+ evaluator.add(params.extract_input<Field<bool>>("Local Space"));
+ evaluator.evaluate();
+
+ const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
+ const VArray<float3> &translations = evaluator.get_evaluated<float3>(0);
+ const VArray<bool> &local_spaces = evaluator.get_evaluated<bool>(1);
+
+ MutableSpan<float4x4> instance_transforms = instances_component.instance_transforms();
+
+ threading::parallel_for(selection.index_range(), 1024, [&](IndexRange range) {
+ for (const int i_selection : range) {
+ const int i = selection[i_selection];
+ if (local_spaces[i]) {
+ instance_transforms[i] *= float4x4::from_location(translations[i]);
+ }
+ else {
+ add_v3_v3(instance_transforms[i].values[3], translations[i]);
+ }
+ }
+ });
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Instances");
+ if (geometry_set.has_instances()) {
+ InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>();
+ translate_instances(params, instances);
+ }
+ params.set_output("Instances", std::move(geometry_set));
+}
+
+} // namespace blender::nodes::node_geo_translate_instances_cc
+
+void register_node_type_geo_translate_instances()
+{
+ namespace file_ns = blender::nodes::node_geo_translate_instances_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_TRANSLATE_INSTANCES, "Translate Instances", NODE_CLASS_GEOMETRY);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.declare = file_ns::node_declare;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc
index 7ef0913622c..e78c4d7bc35 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc
@@ -14,29 +14,30 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#include "BKE_customdata.h"
+#include "BKE_mesh.h"
+
+#include "bmesh.h"
+#include "bmesh_tools.h"
+
+#include "DNA_mesh_types.h"
+
#include "UI_interface.h"
#include "UI_resources.h"
#include "node_geometry_util.hh"
-extern "C" {
-Mesh *triangulate_mesh(Mesh *mesh,
- const int quad_method,
- const int ngon_method,
- const int min_vertices,
- const int flag);
-}
+namespace blender::nodes::node_geo_triangulate_cc {
-namespace blender::nodes {
-
-static void geo_node_triangulate_declare(NodeDeclarationBuilder &b)
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Int>("Minimum Vertices").default_value(4).min(4).max(10000);
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH);
+ b.add_input<decl::Bool>(N_("Selection")).default_value(true).supports_field().hide_value();
+ b.add_input<decl::Int>(N_("Minimum Vertices")).default_value(4).min(4).max(10000);
+ b.add_output<decl::Geometry>(N_("Mesh"));
}
-static void geo_node_triangulate_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiItemR(layout, ptr, "quad_method", 0, "", ICON_NONE);
uiItemR(layout, ptr, "ngon_method", 0, "", ICON_NONE);
@@ -48,9 +49,35 @@ static void geo_triangulate_init(bNodeTree *UNUSED(ntree), bNode *node)
node->custom2 = GEO_NODE_TRIANGULATE_NGON_BEAUTY;
}
-static void geo_node_triangulate_exec(GeoNodeExecParams params)
+static Mesh *triangulate_mesh_selection(const Mesh &mesh,
+ const int quad_method,
+ const int ngon_method,
+ const IndexMask selection,
+ const int min_vertices)
{
- GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ CustomData_MeshMasks cd_mask_extra = {
+ CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX, 0, CD_MASK_ORIGINDEX};
+ BMeshCreateParams create_params{0};
+ BMeshFromMeshParams from_mesh_params{true, 1, 1, 1, cd_mask_extra};
+ BMesh *bm = BKE_mesh_to_bmesh_ex(&mesh, &create_params, &from_mesh_params);
+
+ /* Tag faces to be triangulated from the selection mask. */
+ BM_mesh_elem_table_ensure(bm, BM_FACE);
+ for (int i_face : selection) {
+ BM_elem_flag_set(BM_face_at_index(bm, i_face), BM_ELEM_TAG, true);
+ }
+
+ BM_mesh_triangulate(bm, quad_method, ngon_method, min_vertices, true, NULL, NULL, NULL);
+ Mesh *result = BKE_mesh_from_bmesh_for_eval_nomain(bm, &cd_mask_extra, &mesh);
+ BM_mesh_free(bm);
+ BKE_mesh_normals_tag_dirty(result);
+ return result;
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh");
+ Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
const int min_vertices = std::max(params.extract_input<int>("Minimum Vertices"), 4);
GeometryNodeTriangulateQuads quad_method = static_cast<GeometryNodeTriangulateQuads>(
@@ -59,26 +86,38 @@ static void geo_node_triangulate_exec(GeoNodeExecParams params)
params.node().custom2);
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
- /* #triangulate_mesh might modify the input mesh currently. */
- Mesh *mesh_in = geometry_set.get_mesh_for_write();
- if (mesh_in != nullptr) {
- Mesh *mesh_out = triangulate_mesh(mesh_in, quad_method, ngon_method, min_vertices, 0);
- geometry_set.replace_mesh(mesh_out);
+ if (!geometry_set.has_mesh()) {
+ return;
}
+ GeometryComponent &component = geometry_set.get_component_for_write<MeshComponent>();
+ const Mesh &mesh_in = *geometry_set.get_mesh_for_read();
+
+ const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE);
+ GeometryComponentFieldContext context{component, ATTR_DOMAIN_FACE};
+ FieldEvaluator evaluator{context, domain_size};
+ evaluator.add(selection_field);
+ evaluator.evaluate();
+ const IndexMask selection = evaluator.get_evaluated_as_mask(0);
+
+ Mesh *mesh_out = triangulate_mesh_selection(
+ mesh_in, quad_method, ngon_method, selection, min_vertices);
+ geometry_set.replace_mesh(mesh_out);
});
- params.set_output("Geometry", std::move(geometry_set));
+ params.set_output("Mesh", std::move(geometry_set));
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_triangulate_cc
void register_node_type_geo_triangulate()
{
+ namespace file_ns = blender::nodes::node_geo_triangulate_cc;
+
static bNodeType ntype;
- geo_node_type_base(&ntype, GEO_NODE_TRIANGULATE, "Triangulate", NODE_CLASS_GEOMETRY, 0);
- ntype.declare = blender::nodes::geo_node_triangulate_declare;
- node_type_init(&ntype, blender::nodes::geo_triangulate_init);
- ntype.geometry_node_execute = blender::nodes::geo_node_triangulate_exec;
- ntype.draw_buttons = blender::nodes::geo_node_triangulate_layout;
+ geo_node_type_base(&ntype, GEO_NODE_TRIANGULATE, "Triangulate", NODE_CLASS_GEOMETRY);
+ ntype.declare = file_ns::node_declare;
+ node_type_init(&ntype, file_ns::geo_triangulate_init);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.draw_buttons = file_ns::node_layout;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_viewer.cc b/source/blender/nodes/geometry/nodes/node_geo_viewer.cc
index 3331962341f..c717d90f7cc 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_viewer.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_viewer.cc
@@ -14,20 +14,139 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#include "BKE_context.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "ED_node.h"
+#include "ED_spreadsheet.h"
+
+#include "NOD_socket_search_link.hh"
+
#include "node_geometry_util.hh"
-namespace blender::nodes {
-static void geo_node_viewer_declare(NodeDeclarationBuilder &b)
+namespace blender::nodes::node_geo_viewer_cc {
+
+NODE_STORAGE_FUNCS(NodeGeometryViewer)
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>(N_("Geometry"));
+ b.add_input<decl::Float>(N_("Value")).supports_field().hide_value();
+ b.add_input<decl::Vector>(N_("Value"), "Value_001").supports_field().hide_value();
+ b.add_input<decl::Color>(N_("Value"), "Value_002").supports_field().hide_value();
+ b.add_input<decl::Int>(N_("Value"), "Value_003").supports_field().hide_value();
+ b.add_input<decl::Bool>(N_("Value"), "Value_004").supports_field().hide_value();
+}
+
+static void node_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometryViewer *data = MEM_cnew<NodeGeometryViewer>(__func__);
+ data->data_type = CD_PROP_FLOAT;
+
+ node->storage = data;
+}
+
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE);
+}
+
+static eNodeSocketDatatype custom_data_type_to_socket_type(const CustomDataType type)
+{
+ switch (type) {
+ case CD_PROP_FLOAT:
+ return SOCK_FLOAT;
+ case CD_PROP_INT32:
+ return SOCK_INT;
+ case CD_PROP_FLOAT3:
+ return SOCK_VECTOR;
+ case CD_PROP_BOOL:
+ return SOCK_BOOLEAN;
+ case CD_PROP_COLOR:
+ return SOCK_RGBA;
+ default:
+ BLI_assert_unreachable();
+ return SOCK_FLOAT;
+ }
+}
+
+static void node_update(bNodeTree *ntree, bNode *node)
+{
+ const NodeGeometryViewer &storage = node_storage(*node);
+ const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type);
+ const eNodeSocketDatatype socket_type = custom_data_type_to_socket_type(data_type);
+
+ LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) {
+ if (socket->type == SOCK_GEOMETRY) {
+ continue;
+ }
+ nodeSetSocketAvailability(ntree, socket, socket->type == socket_type);
+ }
+}
+
+static void node_gather_link_searches(GatherLinkSearchOpParams &params)
{
- b.add_input<decl::Geometry>("Geometry");
+ auto set_active_fn = [](LinkSearchOpParams &params, bNode &viewer_node) {
+ /* Set this new viewer node active in spreadsheet editors. */
+ SpaceNode *snode = CTX_wm_space_node(&params.C);
+ Main *bmain = CTX_data_main(&params.C);
+ ED_node_set_active(bmain, snode, &params.node_tree, &viewer_node, nullptr);
+ ED_spreadsheet_context_paths_set_geometry_node(bmain, snode, &viewer_node);
+ };
+
+ const std::optional<CustomDataType> type = node_socket_to_custom_data_type(
+ params.other_socket());
+ if (params.in_out() == SOCK_OUT) {
+ /* The viewer node only has inputs. */
+ return;
+ }
+ if (params.other_socket().type == SOCK_GEOMETRY) {
+ params.add_item(IFACE_("Geometry"), [set_active_fn](LinkSearchOpParams &params) {
+ bNode &node = params.add_node("GeometryNodeViewer");
+ params.connect_available_socket(node, "Geometry");
+ set_active_fn(params, node);
+ });
+ }
+ if (type &&
+ ELEM(type, CD_PROP_FLOAT, CD_PROP_BOOL, CD_PROP_INT32, CD_PROP_FLOAT3, CD_PROP_COLOR)) {
+ params.add_item(IFACE_("Value"), [type, set_active_fn](LinkSearchOpParams &params) {
+ bNode &node = params.add_node("GeometryNodeViewer");
+ node_storage(node).data_type = *type;
+ params.update_and_connect_available_socket(node, "Value");
+
+ /* If the source node has a geometry socket, connect it to the new viewer node as well. */
+ LISTBASE_FOREACH (bNodeSocket *, socket, &params.node.outputs) {
+ if (socket->type == SOCK_GEOMETRY && !(socket->flag & (SOCK_UNAVAIL | SOCK_HIDDEN))) {
+ nodeAddLink(&params.node_tree,
+ &params.node,
+ socket,
+ &node,
+ static_cast<bNodeSocket *>(node.inputs.first));
+ }
+ }
+
+ set_active_fn(params, node);
+ });
+ }
}
-} // namespace blender::nodes
+
+} // namespace blender::nodes::node_geo_viewer_cc
void register_node_type_geo_viewer()
{
+ namespace file_ns = blender::nodes::node_geo_viewer_cc;
+
static bNodeType ntype;
- geo_node_type_base(&ntype, GEO_NODE_VIEWER, "Viewer", NODE_CLASS_OUTPUT, 0);
- ntype.declare = blender::nodes::geo_node_viewer_declare;
+ geo_node_type_base(&ntype, GEO_NODE_VIEWER, "Viewer", NODE_CLASS_OUTPUT);
+ node_type_storage(
+ &ntype, "NodeGeometryViewer", node_free_standard_storage, node_copy_standard_storage);
+ node_type_update(&ntype, file_ns::node_update);
+ node_type_init(&ntype, file_ns::node_init);
+ ntype.declare = file_ns::node_declare;
+ ntype.draw_buttons_ex = file_ns::node_layout;
+ ntype.gather_link_search_ops = file_ns::node_gather_link_searches;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc
index 229a35e0007..c7dc73f8a91 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc
@@ -35,138 +35,193 @@
#include "UI_interface.h"
#include "UI_resources.h"
-namespace blender::nodes {
+namespace blender::nodes::node_geo_volume_to_mesh_cc {
-static void geo_node_volume_to_mesh_declare(NodeDeclarationBuilder &b)
+NODE_STORAGE_FUNCS(NodeGeometryVolumeToMesh)
+
+static void node_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::String>("Density");
- b.add_input<decl::Float>("Voxel Size").default_value(0.3f).min(0.01f).subtype(PROP_DISTANCE);
- b.add_input<decl::Float>("Voxel Amount").default_value(64.0f).min(0.0f);
- b.add_input<decl::Float>("Threshold").default_value(0.1f).min(0.0f);
- b.add_input<decl::Float>("Adaptivity").min(0.0f).max(1.0f).subtype(PROP_FACTOR);
- b.add_output<decl::Geometry>("Geometry");
+ b.add_input<decl::Geometry>(N_("Volume")).supported_type(GEO_COMPONENT_TYPE_VOLUME);
+ b.add_input<decl::Float>(N_("Voxel Size"))
+ .default_value(0.3f)
+ .min(0.01f)
+ .subtype(PROP_DISTANCE)
+ .make_available([](bNode &node) {
+ node_storage(node).resolution_mode = VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_SIZE;
+ });
+ b.add_input<decl::Float>(N_("Voxel Amount"))
+ .default_value(64.0f)
+ .min(0.0f)
+ .make_available([](bNode &node) {
+ node_storage(node).resolution_mode = VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_AMOUNT;
+ });
+ b.add_input<decl::Float>(N_("Threshold")).default_value(0.1f).min(0.0f);
+ b.add_input<decl::Float>(N_("Adaptivity")).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
+ b.add_output<decl::Geometry>(N_("Mesh"));
}
-static void geo_node_volume_to_mesh_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
uiItemR(layout, ptr, "resolution_mode", 0, IFACE_("Resolution"), ICON_NONE);
}
-static void geo_node_volume_to_mesh_init(bNodeTree *UNUSED(ntree), bNode *node)
+static void node_init(bNodeTree *UNUSED(ntree), bNode *node)
{
- NodeGeometryVolumeToMesh *data = (NodeGeometryVolumeToMesh *)MEM_callocN(
- sizeof(NodeGeometryVolumeToMesh), __func__);
+ NodeGeometryVolumeToMesh *data = MEM_cnew<NodeGeometryVolumeToMesh>(__func__);
data->resolution_mode = VOLUME_TO_MESH_RESOLUTION_MODE_GRID;
-
- bNodeSocket *grid_socket = nodeFindSocket(node, SOCK_IN, "Density");
- bNodeSocketValueString *grid_socket_value = (bNodeSocketValueString *)grid_socket->default_value;
- STRNCPY(grid_socket_value->value, "density");
-
node->storage = data;
}
-static void geo_node_volume_to_mesh_update(bNodeTree *UNUSED(ntree), bNode *node)
+static void node_update(bNodeTree *ntree, bNode *node)
{
- NodeGeometryVolumeToMesh *data = (NodeGeometryVolumeToMesh *)node->storage;
+ const NodeGeometryVolumeToMesh &storage = node_storage(*node);
bNodeSocket *voxel_size_socket = nodeFindSocket(node, SOCK_IN, "Voxel Size");
bNodeSocket *voxel_amount_socket = nodeFindSocket(node, SOCK_IN, "Voxel Amount");
- nodeSetSocketAvailability(voxel_amount_socket,
- data->resolution_mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_AMOUNT);
- nodeSetSocketAvailability(voxel_size_socket,
- data->resolution_mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_SIZE);
+ nodeSetSocketAvailability(ntree,
+ voxel_amount_socket,
+ storage.resolution_mode ==
+ VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_AMOUNT);
+ nodeSetSocketAvailability(ntree,
+ voxel_size_socket,
+ storage.resolution_mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_SIZE);
}
#ifdef WITH_OPENVDB
-static void create_mesh_from_volume(GeometrySet &geometry_set_in,
- GeometrySet &geometry_set_out,
- GeoNodeExecParams &params)
+static bke::VolumeToMeshResolution get_resolution_param(const GeoNodeExecParams &params)
{
- if (!geometry_set_in.has<VolumeComponent>()) {
- return;
- }
-
- const NodeGeometryVolumeToMesh &storage =
- *(const NodeGeometryVolumeToMesh *)params.node().storage;
+ const NodeGeometryVolumeToMesh &storage = node_storage(params.node());
bke::VolumeToMeshResolution resolution;
resolution.mode = (VolumeToMeshResolutionMode)storage.resolution_mode;
if (resolution.mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_AMOUNT) {
- resolution.settings.voxel_amount = params.get_input<float>("Voxel Amount");
- if (resolution.settings.voxel_amount <= 0.0f) {
- return;
- }
+ resolution.settings.voxel_amount = std::max(params.get_input<float>("Voxel Amount"), 0.0f);
}
else if (resolution.mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_SIZE) {
- resolution.settings.voxel_size = params.get_input<float>("Voxel Size");
- if (resolution.settings.voxel_size <= 0.0f) {
- return;
- }
+ resolution.settings.voxel_size = std::max(params.get_input<float>("Voxel Size"), 0.0f);
+ }
+
+ return resolution;
+}
+
+static Mesh *create_mesh_from_volume_grids(Span<openvdb::GridBase::ConstPtr> grids,
+ const float threshold,
+ const float adaptivity,
+ const bke::VolumeToMeshResolution &resolution)
+{
+ Array<bke::OpenVDBMeshData> mesh_data(grids.size());
+ for (const int i : grids.index_range()) {
+ mesh_data[i] = bke::volume_to_mesh_data(*grids[i], resolution, threshold, adaptivity);
+ }
+
+ int vert_offset = 0;
+ int poly_offset = 0;
+ int loop_offset = 0;
+ Array<int> vert_offsets(mesh_data.size());
+ Array<int> poly_offsets(mesh_data.size());
+ Array<int> loop_offsets(mesh_data.size());
+ for (const int i : grids.index_range()) {
+ const bke::OpenVDBMeshData &data = mesh_data[i];
+ vert_offsets[i] = vert_offset;
+ poly_offsets[i] = poly_offset;
+ loop_offsets[i] = loop_offset;
+ vert_offset += data.verts.size();
+ poly_offset += (data.tris.size() + data.quads.size());
+ loop_offset += (3 * data.tris.size() + 4 * data.quads.size());
}
- const VolumeComponent *component = geometry_set_in.get_component_for_read<VolumeComponent>();
- const Volume *volume = component->get_for_read();
+ Mesh *mesh = BKE_mesh_new_nomain(vert_offset, 0, 0, loop_offset, poly_offset);
+ BKE_id_material_eval_ensure_default_slot(&mesh->id);
+ MutableSpan<MVert> verts{mesh->mvert, mesh->totvert};
+ MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop};
+ MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly};
+
+ for (const int i : grids.index_range()) {
+ const bke::OpenVDBMeshData &data = mesh_data[i];
+ bke::fill_mesh_from_openvdb_data(data.verts,
+ data.tris,
+ data.quads,
+ vert_offsets[i],
+ poly_offsets[i],
+ loop_offsets[i],
+ verts,
+ polys,
+ loops);
+ }
+
+ BKE_mesh_calc_edges(mesh, false, false);
+ BKE_mesh_normals_tag_dirty(mesh);
+
+ return mesh;
+}
+
+static Mesh *create_mesh_from_volume(GeometrySet &geometry_set, GeoNodeExecParams &params)
+{
+ const Volume *volume = geometry_set.get_volume_for_read();
if (volume == nullptr) {
- return;
+ return nullptr;
}
+ const bke::VolumeToMeshResolution resolution = get_resolution_param(params);
const Main *bmain = DEG_get_bmain(params.depsgraph());
BKE_volume_load(volume, bmain);
- const std::string grid_name = params.get_input<std::string>("Density");
- const VolumeGrid *volume_grid = BKE_volume_grid_find_for_read(volume, grid_name.c_str());
- if (volume_grid == nullptr) {
- return;
+ Vector<openvdb::GridBase::ConstPtr> grids;
+ for (const int i : IndexRange(BKE_volume_num_grids(volume))) {
+ const VolumeGrid *volume_grid = BKE_volume_grid_get_for_read(volume, i);
+ openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid);
+ grids.append(std::move(grid));
}
- float threshold = params.get_input<float>("Threshold");
- float adaptivity = params.get_input<float>("Adaptivity");
-
- const openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid);
- Mesh *mesh = bke::volume_to_mesh(*grid, resolution, threshold, adaptivity);
- if (mesh == nullptr) {
- return;
+ if (grids.is_empty()) {
+ return nullptr;
}
- BKE_id_material_eval_ensure_default_slot(&mesh->id);
- MeshComponent &dst_component = geometry_set_out.get_component_for_write<MeshComponent>();
- dst_component.replace(mesh);
+
+ return create_mesh_from_volume_grids(grids,
+ params.get_input<float>("Threshold"),
+ params.get_input<float>("Adaptivity"),
+ resolution);
}
#endif /* WITH_OPENVDB */
-static void geo_node_volume_to_mesh_exec(GeoNodeExecParams params)
+static void node_geo_exec(GeoNodeExecParams params)
{
- GeometrySet geometry_set_in = params.extract_input<GeometrySet>("Geometry");
- GeometrySet geometry_set_out;
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Volume");
#ifdef WITH_OPENVDB
- create_mesh_from_volume(geometry_set_in, geometry_set_out, params);
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ Mesh *mesh = create_mesh_from_volume(geometry_set, params);
+ geometry_set.replace_mesh(mesh);
+ geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES});
+ });
#else
params.error_message_add(NodeWarningType::Error,
TIP_("Disabled, Blender was compiled without OpenVDB"));
#endif
- params.set_output("Geometry", geometry_set_out);
+ params.set_output("Mesh", std::move(geometry_set));
}
-} // namespace blender::nodes
+} // namespace blender::nodes::node_geo_volume_to_mesh_cc
void register_node_type_geo_volume_to_mesh()
{
+ namespace file_ns = blender::nodes::node_geo_volume_to_mesh_cc;
+
static bNodeType ntype;
- geo_node_type_base(&ntype, GEO_NODE_VOLUME_TO_MESH, "Volume to Mesh", NODE_CLASS_GEOMETRY, 0);
- ntype.declare = blender::nodes::geo_node_volume_to_mesh_declare;
+ geo_node_type_base(&ntype, GEO_NODE_VOLUME_TO_MESH, "Volume to Mesh", NODE_CLASS_GEOMETRY);
+ ntype.declare = file_ns::node_declare;
node_type_storage(
&ntype, "NodeGeometryVolumeToMesh", node_free_standard_storage, node_copy_standard_storage);
- node_type_size(&ntype, 200, 120, 700);
- node_type_init(&ntype, blender::nodes::geo_node_volume_to_mesh_init);
- node_type_update(&ntype, blender::nodes::geo_node_volume_to_mesh_update);
- ntype.geometry_node_execute = blender::nodes::geo_node_volume_to_mesh_exec;
- ntype.draw_buttons = blender::nodes::geo_node_volume_to_mesh_layout;
+ node_type_size(&ntype, 170, 120, 700);
+ node_type_init(&ntype, file_ns::node_init);
+ node_type_update(&ntype, file_ns::node_update);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.draw_buttons = file_ns::node_layout;
nodeRegisterType(&ntype);
}