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')
-rw-r--r--source/blender/nodes/CMakeLists.txt3
-rw-r--r--source/blender/nodes/NOD_composite.h2
-rw-r--r--source/blender/nodes/NOD_derived_node_tree.hh172
-rw-r--r--source/blender/nodes/NOD_geometry.h14
-rw-r--r--source/blender/nodes/NOD_geometry_exec.hh26
-rw-r--r--source/blender/nodes/NOD_geometry_nodes_eval_log.hh5
-rw-r--r--source/blender/nodes/NOD_multi_function.hh18
-rw-r--r--source/blender/nodes/NOD_node_declaration.hh37
-rw-r--r--source/blender/nodes/NOD_node_tree_ref.hh760
-rw-r--r--source/blender/nodes/NOD_shader.h1
-rw-r--r--source/blender/nodes/NOD_static_types.h459
-rw-r--r--source/blender/nodes/composite/CMakeLists.txt11
-rw-r--r--source/blender/nodes/composite/node_composite_tree.cc6
-rw-r--r--source/blender/nodes/composite/node_composite_util.hh12
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_alpha_over.cc68
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_antialiasing.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_bilateralblur.cc73
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_blur.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_bokehblur.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_bokehimage.cc66
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_boxmask.cc99
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_brightness.cc45
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_channel_matte.cc96
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_chroma_matte.cc63
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_color_matte.cc62
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_color_spill.cc112
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_colorbalance.cc69
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc81
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_composite.cc129
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_convert_color_space.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_cornerpin.cc21
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_crop.cc168
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc58
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_curves.cc248
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_defocus.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_denoise.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_despeckle.cc72
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_diff_matte.cc56
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_dilate.cc486
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_directionalblur.cc140
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_displace.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_distance_matte.cc72
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_double_edge_mask.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_ellipsemask.cc99
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_exposure.cc31
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_filter.cc130
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_flip.cc64
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_gamma.cc32
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_glare.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_hue_sat_val.cc49
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_huecorrect.cc67
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_id_mask.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_image.cc277
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_inpaint.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_invert.cc55
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_keying.cc22
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_keyingscreen.cc42
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_lensdist.cc204
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_levels.cc21
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_luma_matte.cc55
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_map_range.cc67
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_map_uv.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_map_value.cc60
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_mask.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_math.cc67
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_mixrgb.cc125
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_movieclip.cc190
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_moviedistortion.cc21
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_normal.cc42
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_normalize.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_output_file.cc27
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_pixelate.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_planetrackdeform.cc53
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_posterize.cc35
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_premulkey.cc39
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_rgb.cc30
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_rotate.cc54
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_scale.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_scene_time.cc36
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_sepcomb_color.cc124
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_sepcomb_hsva.cc77
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_sepcomb_rgba.cc77
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_sepcomb_xyz.cc63
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_sepcomb_ycca.cc126
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_sepcomb_yuva.cc76
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_setalpha.cc42
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_split_viewer.cc71
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_stabilize2d.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_sunbeams.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_switch.cc28
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_switchview.cc22
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_texture.cc21
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_tonemap.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_trackpos.cc53
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_transform.cc77
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_translate.cc72
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_val_to_rgb.cc152
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_value.cc26
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_vec_blur.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_viewer.cc129
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_zcombine.cc21
-rw-r--r--source/blender/nodes/function/node_function_util.hh2
-rw-r--r--source/blender/nodes/function/nodes/node_fn_align_euler_to_vector.cc4
-rw-r--r--source/blender/nodes/function/nodes/node_fn_boolean_math.cc2
-rw-r--r--source/blender/nodes/function/nodes/node_fn_combine_color.cc2
-rw-r--r--source/blender/nodes/function/nodes/node_fn_compare.cc2
-rw-r--r--source/blender/nodes/function/nodes/node_fn_float_to_int.cc2
-rw-r--r--source/blender/nodes/function/nodes/node_fn_input_bool.cc2
-rw-r--r--source/blender/nodes/function/nodes/node_fn_input_color.cc2
-rw-r--r--source/blender/nodes/function/nodes/node_fn_input_int.cc2
-rw-r--r--source/blender/nodes/function/nodes/node_fn_input_string.cc2
-rw-r--r--source/blender/nodes/function/nodes/node_fn_input_vector.cc2
-rw-r--r--source/blender/nodes/function/nodes/node_fn_rotate_euler.cc2
-rw-r--r--source/blender/nodes/function/nodes/node_fn_separate_color.cc41
-rw-r--r--source/blender/nodes/geometry/CMakeLists.txt10
-rw-r--r--source/blender/nodes/geometry/node_geometry_tree.cc4
-rw-r--r--source/blender/nodes/geometry/node_geometry_util.hh5
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc62
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc19
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc42
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc24
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_boolean.cc15
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc86
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc32
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc8
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc595
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc17
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc12
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc37
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc12
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc318
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_set_handle_type.cc37
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc85
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc684
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc323
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc5
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc25
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc21
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc424
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc459
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc154
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc207
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc391
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_curves.cc110
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_selection.cc134
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_edge_split.cc23
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc217
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc49
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc34
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc30
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_instance_rotation.cc22
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_instance_scale.cc22
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_angle.cc78
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_neighbors.cc28
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_vertices.cc64
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_area.cc32
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_is_planar.cc50
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_neighbors.cc66
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc52
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_mesh_vertex_neighbors.cc58
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_shortest_edge_paths.cc236
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc23
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc25
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc15
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc46
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_interpolate_domain.cc167
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc19
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_material_selection.cc38
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc40
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc8
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc56
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc23
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc5
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc164
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc19
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc44
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_to_volume.cc187
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_object_info.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_points.cc101
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc47
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc11
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_raycast.cc10
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_remove_attribute.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc12
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_scale_elements.cc164
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc11
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc41
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc32
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc30
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_id.cc28
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_material.cc13
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc25
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc27
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_position.cc65
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc29
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc30
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc34
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc76
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc75
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc32
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc64
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_transform.cc129
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc9
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_triangulate.cc9
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_uv_pack_islands.cc146
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_uv_unwrap.cc194
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_volume_cube.cc204
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc22
-rw-r--r--source/blender/nodes/intern/derived_node_tree.cc139
-rw-r--r--source/blender/nodes/intern/geometry_nodes_eval_log.cc32
-rw-r--r--source/blender/nodes/intern/node_common.cc15
-rw-r--r--source/blender/nodes/intern/node_geometry_exec.cc51
-rw-r--r--source/blender/nodes/intern/node_multi_function.cc8
-rw-r--r--source/blender/nodes/intern/node_tree_ref.cc678
-rw-r--r--source/blender/nodes/intern/node_util.c2
-rw-r--r--source/blender/nodes/shader/CMakeLists.txt1
-rw-r--r--source/blender/nodes/shader/node_shader_tree.cc33
-rw-r--r--source/blender/nodes/shader/node_shader_util.hh2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_attribute.cc16
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_bsdf_hair_principled.cc3
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc21
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_color_ramp.cc2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_curves.cc6
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_geometry.cc5
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_hair_info.cc4
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_math.cc7
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_mix.cc443
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_mix_rgb.cc33
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_output_light.cc18
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_rgb.cc7
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_sepcomb_hsv.cc2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_sepcomb_rgb.cc2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_sepcomb_xyz.cc38
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_brick.cc2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_coord.cc8
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_environment.cc6
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_gradient.cc2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_magic.cc2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_wave.cc2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_value.cc7
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_vector_math.cc2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_vector_transform.cc2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_vertex_color.cc7
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_volume_info.cc2
-rw-r--r--source/blender/nodes/texture/CMakeLists.txt1
-rw-r--r--source/blender/nodes/texture/node_texture_tree.c12
-rw-r--r--source/blender/nodes/texture/nodes/node_texture_output.c17
-rw-r--r--source/blender/nodes/texture/nodes/node_texture_proc.c39
-rw-r--r--source/blender/nodes/texture/nodes/node_texture_texture.c2
253 files changed, 11270 insertions, 5924 deletions
diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt
index 386e5fe14c9..ff8bd27f8d7 100644
--- a/source/blender/nodes/CMakeLists.txt
+++ b/source/blender/nodes/CMakeLists.txt
@@ -29,7 +29,6 @@ set(INC
../makesrna
../render
../windowmanager
- ../../../intern/glew-mx
../../../intern/guardedalloc
# dna_type_offsets.h
@@ -50,7 +49,6 @@ set(SRC
intern/node_multi_function.cc
intern/node_socket.cc
intern/node_socket_declarations.cc
- intern/node_tree_ref.cc
intern/node_util.c
intern/socket_search_link.cc
@@ -64,7 +62,6 @@ set(SRC
NOD_math_functions.hh
NOD_multi_function.hh
NOD_node_declaration.hh
- NOD_node_tree_ref.hh
NOD_shader.h
NOD_socket.h
NOD_socket_declarations.hh
diff --git a/source/blender/nodes/NOD_composite.h b/source/blender/nodes/NOD_composite.h
index 5d782674f16..58126c5722d 100644
--- a/source/blender/nodes/NOD_composite.h
+++ b/source/blender/nodes/NOD_composite.h
@@ -169,7 +169,7 @@ void ntreeCompositClearTags(struct bNodeTree *ntree);
struct bNodeSocket *ntreeCompositOutputFileAddSocket(struct bNodeTree *ntree,
struct bNode *node,
const char *name,
- struct ImageFormatData *im_format);
+ const struct ImageFormatData *im_format);
int ntreeCompositOutputFileRemoveActiveSocket(struct bNodeTree *ntree, struct bNode *node);
void ntreeCompositOutputFileSetPath(struct bNode *node,
diff --git a/source/blender/nodes/NOD_derived_node_tree.hh b/source/blender/nodes/NOD_derived_node_tree.hh
index b0799d90dcd..b3775e729da 100644
--- a/source/blender/nodes/NOD_derived_node_tree.hh
+++ b/source/blender/nodes/NOD_derived_node_tree.hh
@@ -5,16 +5,17 @@
/** \file
* \ingroup nodes
*
- * DerivedNodeTree builds on top of NodeTreeRef and makes working with (nested) node groups more
- * convenient and safe. It does so by pairing nodes and sockets with a context. The context
- * contains information about the current "instance" of the node or socket. A node might be
- * "instanced" multiple times when it is in a node group that is used multiple times.
+ * DerivedNodeTree makes working with (nested) node groups more convenient and safe. It does so by
+ * pairing nodes and sockets with a context. The context contains information about the current
+ * "instance" of the node or socket. A node might be "instanced" multiple times when it is in a
+ * node group that is used multiple times.
*/
#include "BLI_function_ref.hh"
+#include "BLI_linear_allocator.hh"
#include "BLI_vector_set.hh"
-#include "NOD_node_tree_ref.hh"
+#include "BKE_node_runtime.hh"
namespace blender::nodes {
@@ -40,20 +41,20 @@ class DTreeContext {
DTreeContext *parent_context_;
/* Null when this context is for the root node group. Otherwise it points to the group node in
* the parent node group that contains this context. */
- const NodeRef *parent_node_;
+ const bNode *parent_node_;
/* The current node tree. */
- const NodeTreeRef *tree_;
+ const bNodeTree *btree_;
/* All the children contexts of this context. */
- Map<const NodeRef *, DTreeContext *> children_;
+ Map<const bNode *, DTreeContext *> children_;
DerivedNodeTree *derived_tree_;
friend DerivedNodeTree;
public:
- const NodeTreeRef &tree() const;
+ const bNodeTree &btree() const;
const DTreeContext *parent_context() const;
- const NodeRef *parent_node() const;
- const DTreeContext *child_context(const NodeRef &node) const;
+ const bNode *parent_node() const;
+ const DTreeContext *child_context(const bNode &node) const;
const DerivedNodeTree &derived_tree() const;
bool is_root() const;
};
@@ -65,15 +66,16 @@ class DTreeContext {
class DNode {
private:
const DTreeContext *context_ = nullptr;
- const NodeRef *node_ref_ = nullptr;
+ const bNode *bnode_ = nullptr;
public:
DNode() = default;
- DNode(const DTreeContext *context, const NodeRef *node);
+ DNode(const DTreeContext *context, const bNode *node);
const DTreeContext *context() const;
- const NodeRef *node_ref() const;
- const NodeRef *operator->() const;
+ const bNode *bnode() const;
+ const bNode *operator->() const;
+ const bNode &operator*() const;
friend bool operator==(const DNode &a, const DNode &b);
friend bool operator!=(const DNode &a, const DNode &b);
@@ -98,17 +100,18 @@ class DNode {
class DSocket {
protected:
const DTreeContext *context_ = nullptr;
- const SocketRef *socket_ref_ = nullptr;
+ const bNodeSocket *bsocket_ = nullptr;
public:
DSocket() = default;
- DSocket(const DTreeContext *context, const SocketRef *socket);
+ DSocket(const DTreeContext *context, const bNodeSocket *socket);
DSocket(const DInputSocket &input_socket);
DSocket(const DOutputSocket &output_socket);
const DTreeContext *context() const;
- const SocketRef *socket_ref() const;
- const SocketRef *operator->() const;
+ const bNodeSocket *bsocket() const;
+ const bNodeSocket *operator->() const;
+ const bNodeSocket &operator*() const;
friend bool operator==(const DSocket &a, const DSocket &b);
friend bool operator!=(const DSocket &a, const DSocket &b);
@@ -123,12 +126,9 @@ class DSocket {
class DInputSocket : public DSocket {
public:
DInputSocket() = default;
- DInputSocket(const DTreeContext *context, const InputSocketRef *socket);
+ DInputSocket(const DTreeContext *context, const bNodeSocket *socket);
explicit DInputSocket(const DSocket &base_socket);
- const InputSocketRef *socket_ref() const;
- const InputSocketRef *operator->() const;
-
DOutputSocket get_corresponding_group_node_output() const;
Vector<DOutputSocket, 4> get_corresponding_group_input_sockets() const;
@@ -144,12 +144,9 @@ class DInputSocket : public DSocket {
class DOutputSocket : public DSocket {
public:
DOutputSocket() = default;
- DOutputSocket(const DTreeContext *context, const OutputSocketRef *socket);
+ DOutputSocket(const DTreeContext *context, const bNodeSocket *socket);
explicit DOutputSocket(const DSocket &base_socket);
- const OutputSocketRef *socket_ref() const;
- const OutputSocketRef *operator->() const;
-
DInputSocket get_corresponding_group_node_input() const;
DInputSocket get_active_corresponding_group_output_socket() const;
@@ -177,7 +174,7 @@ class DerivedNodeTree {
private:
LinearAllocator<> allocator_;
DTreeContext *root_context_;
- VectorSet<const NodeTreeRef *> used_node_tree_refs_;
+ VectorSet<const bNodeTree *> used_btrees_;
public:
/**
@@ -186,11 +183,11 @@ class DerivedNodeTree {
* has to make sure that the node tree refs added to #node_tree_refs live at least as long as the
* derived node tree.
*/
- DerivedNodeTree(bNodeTree &btree, NodeTreeRefMap &node_tree_refs);
+ DerivedNodeTree(const bNodeTree &btree);
~DerivedNodeTree();
const DTreeContext &root_context() const;
- Span<const NodeTreeRef *> used_node_tree_refs() const;
+ Span<const bNodeTree *> used_btrees() const;
/**
* \return True when there is a link cycle. Unavailable sockets are ignored.
@@ -205,9 +202,8 @@ class DerivedNodeTree {
private:
DTreeContext &construct_context_recursively(DTreeContext *parent_context,
- const NodeRef *parent_node,
- bNodeTree &btree,
- NodeTreeRefMap &node_tree_refs);
+ const bNode *parent_node,
+ const bNodeTree &btree);
void destruct_context_recursively(DTreeContext *context);
void foreach_node_in_context_recursive(const DTreeContext &context,
@@ -215,7 +211,6 @@ class DerivedNodeTree {
};
namespace derived_node_tree_types {
-using namespace node_tree_ref_types;
using nodes::DerivedNodeTree;
using nodes::DInputSocket;
using nodes::DNode;
@@ -228,9 +223,9 @@ using nodes::DTreeContext;
/** \name #DTreeContext Inline Methods
* \{ */
-inline const NodeTreeRef &DTreeContext::tree() const
+inline const bNodeTree &DTreeContext::btree() const
{
- return *tree_;
+ return *btree_;
}
inline const DTreeContext *DTreeContext::parent_context() const
@@ -238,12 +233,12 @@ inline const DTreeContext *DTreeContext::parent_context() const
return parent_context_;
}
-inline const NodeRef *DTreeContext::parent_node() const
+inline const bNode *DTreeContext::parent_node() const
{
return parent_node_;
}
-inline const DTreeContext *DTreeContext::child_context(const NodeRef &node) const
+inline const DTreeContext *DTreeContext::child_context(const bNode &node) const
{
return children_.lookup_default(&node, nullptr);
}
@@ -264,10 +259,10 @@ inline bool DTreeContext::is_root() const
/** \name #DNode Inline Methods
* \{ */
-inline DNode::DNode(const DTreeContext *context, const NodeRef *node_ref)
- : context_(context), node_ref_(node_ref)
+inline DNode::DNode(const DTreeContext *context, const bNode *bnode)
+ : context_(context), bnode_(bnode)
{
- BLI_assert(node_ref == nullptr || &node_ref->tree() == &context->tree());
+ BLI_assert(bnode == nullptr || bnode->runtime->owner_tree == &context->btree());
}
inline const DTreeContext *DNode::context() const
@@ -275,14 +270,14 @@ inline const DTreeContext *DNode::context() const
return context_;
}
-inline const NodeRef *DNode::node_ref() const
+inline const bNode *DNode::bnode() const
{
- return node_ref_;
+ return bnode_;
}
inline bool operator==(const DNode &a, const DNode &b)
{
- return a.context_ == b.context_ && a.node_ref_ == b.node_ref_;
+ return a.context_ == b.context_ && a.bnode_ == b.bnode_;
}
inline bool operator!=(const DNode &a, const DNode &b)
@@ -292,37 +287,43 @@ inline bool operator!=(const DNode &a, const DNode &b)
inline DNode::operator bool() const
{
- return node_ref_ != nullptr;
+ return bnode_ != nullptr;
+}
+
+inline const bNode *DNode::operator->() const
+{
+ return bnode_;
}
-inline const NodeRef *DNode::operator->() const
+inline const bNode &DNode::operator*() const
{
- return node_ref_;
+ BLI_assert(bnode_ != nullptr);
+ return *bnode_;
}
inline uint64_t DNode::hash() const
{
- return get_default_hash_2(context_, node_ref_);
+ return get_default_hash_2(context_, bnode_);
}
inline DInputSocket DNode::input(int index) const
{
- return {context_, &node_ref_->input(index)};
+ return {context_, &bnode_->input_socket(index)};
}
inline DOutputSocket DNode::output(int index) const
{
- return {context_, &node_ref_->output(index)};
+ return {context_, &bnode_->output_socket(index)};
}
inline DInputSocket DNode::input_by_identifier(StringRef identifier) const
{
- return {context_, &node_ref_->input_by_identifier(identifier)};
+ return {context_, &bnode_->input_by_identifier(identifier)};
}
inline DOutputSocket DNode::output_by_identifier(StringRef identifier) const
{
- return {context_, &node_ref_->output_by_identifier(identifier)};
+ return {context_, &bnode_->output_by_identifier(identifier)};
}
/** \} */
@@ -331,19 +332,20 @@ inline DOutputSocket DNode::output_by_identifier(StringRef identifier) const
/** \name #DSocket Inline Methods
* \{ */
-inline DSocket::DSocket(const DTreeContext *context, const SocketRef *socket_ref)
- : context_(context), socket_ref_(socket_ref)
+inline DSocket::DSocket(const DTreeContext *context, const bNodeSocket *bsocket)
+ : context_(context), bsocket_(bsocket)
{
- BLI_assert(socket_ref == nullptr || &socket_ref->tree() == &context->tree());
+ BLI_assert(bsocket == nullptr ||
+ bsocket->runtime->owner_node->runtime->owner_tree == &context->btree());
}
inline DSocket::DSocket(const DInputSocket &input_socket)
- : DSocket(input_socket.context_, input_socket.socket_ref_)
+ : DSocket(input_socket.context_, input_socket.bsocket_)
{
}
inline DSocket::DSocket(const DOutputSocket &output_socket)
- : DSocket(output_socket.context_, output_socket.socket_ref_)
+ : DSocket(output_socket.context_, output_socket.bsocket_)
{
}
@@ -352,14 +354,14 @@ inline const DTreeContext *DSocket::context() const
return context_;
}
-inline const SocketRef *DSocket::socket_ref() const
+inline const bNodeSocket *DSocket::bsocket() const
{
- return socket_ref_;
+ return bsocket_;
}
inline bool operator==(const DSocket &a, const DSocket &b)
{
- return a.context_ == b.context_ && a.socket_ref_ == b.socket_ref_;
+ return a.context_ == b.context_ && a.bsocket_ == b.bsocket_;
}
inline bool operator!=(const DSocket &a, const DSocket &b)
@@ -369,23 +371,29 @@ inline bool operator!=(const DSocket &a, const DSocket &b)
inline DSocket::operator bool() const
{
- return socket_ref_ != nullptr;
+ return bsocket_ != nullptr;
}
-inline const SocketRef *DSocket::operator->() const
+inline const bNodeSocket *DSocket::operator->() const
{
- return socket_ref_;
+ return bsocket_;
+}
+
+inline const bNodeSocket &DSocket::operator*() const
+{
+ BLI_assert(bsocket_ != nullptr);
+ return *bsocket_;
}
inline uint64_t DSocket::hash() const
{
- return get_default_hash_2(context_, socket_ref_);
+ return get_default_hash_2(context_, bsocket_);
}
inline DNode DSocket::node() const
{
- BLI_assert(socket_ref_ != nullptr);
- return {context_, &socket_ref_->node()};
+ BLI_assert(bsocket_ != nullptr);
+ return {context_, bsocket_->runtime->owner_node};
}
/** \} */
@@ -394,8 +402,8 @@ inline DNode DSocket::node() const
/** \name #DInputSocket Inline Methods
* \{ */
-inline DInputSocket::DInputSocket(const DTreeContext *context, const InputSocketRef *socket_ref)
- : DSocket(context, socket_ref)
+inline DInputSocket::DInputSocket(const DTreeContext *context, const bNodeSocket *bsocket)
+ : DSocket(context, bsocket)
{
}
@@ -404,24 +412,14 @@ inline DInputSocket::DInputSocket(const DSocket &base_socket) : DSocket(base_soc
BLI_assert(base_socket->is_input());
}
-inline const InputSocketRef *DInputSocket::socket_ref() const
-{
- return (const InputSocketRef *)socket_ref_;
-}
-
-inline const InputSocketRef *DInputSocket::operator->() const
-{
- return (const InputSocketRef *)socket_ref_;
-}
-
/** \} */
/* -------------------------------------------------------------------- */
/** \name #DOutputSocket Inline Methods
* \{ */
-inline DOutputSocket::DOutputSocket(const DTreeContext *context, const OutputSocketRef *socket_ref)
- : DSocket(context, socket_ref)
+inline DOutputSocket::DOutputSocket(const DTreeContext *context, const bNodeSocket *bsocket)
+ : DSocket(context, bsocket)
{
}
@@ -430,16 +428,6 @@ inline DOutputSocket::DOutputSocket(const DSocket &base_socket) : DSocket(base_s
BLI_assert(base_socket->is_output());
}
-inline const OutputSocketRef *DOutputSocket::socket_ref() const
-{
- return (const OutputSocketRef *)socket_ref_;
-}
-
-inline const OutputSocketRef *DOutputSocket::operator->() const
-{
- return (const OutputSocketRef *)socket_ref_;
-}
-
/** \} */
/* -------------------------------------------------------------------- */
@@ -451,9 +439,9 @@ inline const DTreeContext &DerivedNodeTree::root_context() const
return *root_context_;
}
-inline Span<const NodeTreeRef *> DerivedNodeTree::used_node_tree_refs() const
+inline Span<const bNodeTree *> DerivedNodeTree::used_btrees() const
{
- return used_node_tree_refs_;
+ return used_btrees_;
}
/** \} */
diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h
index dc90073ec31..e16cd7a253f 100644
--- a/source/blender/nodes/NOD_geometry.h
+++ b/source/blender/nodes/NOD_geometry.h
@@ -47,17 +47,19 @@ void register_node_type_geo_curve_subdivide(void);
void register_node_type_geo_curve_to_mesh(void);
void register_node_type_geo_curve_to_points(void);
void register_node_type_geo_curve_trim(void);
+void register_node_type_geo_deform_curves_on_surface(void);
void register_node_type_geo_delete_geometry(void);
-void register_node_type_geo_duplicate_elements(void);
void register_node_type_geo_distribute_points_on_faces(void);
void register_node_type_geo_dual_mesh(void);
+void register_node_type_geo_duplicate_elements(void);
+void register_node_type_geo_edge_paths_to_curves(void);
+void register_node_type_geo_edge_paths_to_selection(void);
void register_node_type_geo_edge_split(void);
void register_node_type_geo_extrude_mesh(void);
void register_node_type_geo_field_at_index(void);
void register_node_type_geo_flip_faces(void);
void register_node_type_geo_geometry_to_instance(void);
void register_node_type_geo_image_texture(void);
-void register_node_type_geo_input_named_attribute(void);
void register_node_type_geo_input_curve_handles(void);
void register_node_type_geo_input_curve_tilt(void);
void register_node_type_geo_input_id(void);
@@ -74,17 +76,20 @@ void register_node_type_geo_input_mesh_face_is_planar(void);
void register_node_type_geo_input_mesh_face_neighbors(void);
void register_node_type_geo_input_mesh_island(void);
void register_node_type_geo_input_mesh_vertex_neighbors(void);
+void register_node_type_geo_input_named_attribute(void);
void register_node_type_geo_input_normal(void);
void register_node_type_geo_input_position(void);
void register_node_type_geo_input_radius(void);
void register_node_type_geo_input_scene_time(void);
void register_node_type_geo_input_shade_smooth(void);
+void register_node_type_geo_input_shortest_edge_paths(void);
void register_node_type_geo_input_spline_cyclic(void);
void register_node_type_geo_input_spline_length(void);
void register_node_type_geo_input_spline_resolution(void);
void register_node_type_geo_input_tangent(void);
void register_node_type_geo_instance_on_points(void);
void register_node_type_geo_instances_to_points(void);
+void register_node_type_geo_interpolate_domain(void);
void register_node_type_geo_is_viewport(void);
void register_node_type_geo_join_geometry(void);
void register_node_type_geo_material_replace(void);
@@ -101,9 +106,11 @@ void register_node_type_geo_mesh_primitive_uv_sphere(void);
void register_node_type_geo_mesh_subdivide(void);
void register_node_type_geo_mesh_to_curve(void);
void register_node_type_geo_mesh_to_points(void);
+void register_node_type_geo_mesh_to_volume(void);
void register_node_type_geo_object_info(void);
void register_node_type_geo_points_to_vertices(void);
void register_node_type_geo_points_to_volume(void);
+void register_node_type_geo_points(void);
void register_node_type_geo_proximity(void);
void register_node_type_geo_raycast(void);
void register_node_type_geo_realize_instances(void);
@@ -134,7 +141,10 @@ void register_node_type_geo_transfer_attribute(void);
void register_node_type_geo_transform(void);
void register_node_type_geo_translate_instances(void);
void register_node_type_geo_triangulate(void);
+void register_node_type_geo_uv_pack_islands(void);
+void register_node_type_geo_uv_unwrap(void);
void register_node_type_geo_viewer(void);
+void register_node_type_geo_volume_cube(void);
void register_node_type_geo_volume_to_mesh(void);
#ifdef __cplusplus
diff --git a/source/blender/nodes/NOD_geometry_exec.hh b/source/blender/nodes/NOD_geometry_exec.hh
index b82c05f33be..b5ffd3a317c 100644
--- a/source/blender/nodes/NOD_geometry_exec.hh
+++ b/source/blender/nodes/NOD_geometry_exec.hh
@@ -5,34 +5,34 @@
#include "FN_field.hh"
#include "FN_multi_function_builder.hh"
-#include "BKE_attribute_access.hh"
#include "BKE_geometry_fields.hh"
#include "BKE_geometry_set.hh"
-#include "BKE_geometry_set_instances.hh"
#include "DNA_node_types.h"
#include "NOD_derived_node_tree.hh"
#include "NOD_geometry_nodes_eval_log.hh"
-#include "GEO_realize_instances.hh"
-
struct Depsgraph;
struct ModifierData;
namespace blender::nodes {
using bke::AnonymousAttributeFieldInput;
+using bke::AttributeAccessor;
using bke::AttributeFieldInput;
using bke::AttributeIDRef;
-using bke::GeometryComponentFieldContext;
-using bke::GeometryFieldInput;
-using bke::OutputAttribute;
-using bke::OutputAttribute_Typed;
-using bke::ReadAttributeLookup;
+using bke::AttributeKind;
+using bke::AttributeMetaData;
+using bke::AttributeReader;
+using bke::AttributeWriter;
+using bke::GAttributeReader;
+using bke::GAttributeWriter;
+using bke::GSpanAttributeWriter;
+using bke::MutableAttributeAccessor;
+using bke::SpanAttributeWriter;
using bke::StrongAnonymousAttributeID;
using bke::WeakAnonymousAttributeID;
-using bke::WriteAttributeLookup;
using fn::Field;
using fn::FieldContext;
using fn::FieldEvaluator;
@@ -161,6 +161,7 @@ class GeoNodeExecParams {
}
void check_input_geometry_set(StringRef identifier, const GeometrySet &geometry_set) const;
+ void check_output_geometry_set(const GeometrySet &geometry_set) const;
/**
* Get input as vector for multi input socket with the given identifier.
@@ -229,6 +230,9 @@ class GeoNodeExecParams {
#ifdef DEBUG
this->check_output_access(identifier, type);
#endif
+ if constexpr (std::is_same_v<StoredT, GeometrySet>) {
+ this->check_output_geometry_set(value);
+ }
GMutablePointer gvalue = provider_->alloc_output_value(type);
new (gvalue.get()) StoredT(std::forward<T>(value));
provider_->set_output(identifier, gvalue);
@@ -279,7 +283,7 @@ class GeoNodeExecParams {
*/
const bNode &node() const
{
- return *provider_->dnode->bnode();
+ return *provider_->dnode;
}
const Object *self_object() const
diff --git a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh b/source/blender/nodes/NOD_geometry_nodes_eval_log.hh
index 05c97c3903d..46ba72d14d8 100644
--- a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh
+++ b/source/blender/nodes/NOD_geometry_nodes_eval_log.hh
@@ -115,11 +115,16 @@ class GeometryValueLog : public ValueLog {
struct InstancesInfo {
int instances_num;
};
+ struct EditDataInfo {
+ bool has_deformed_positions;
+ bool has_deform_matrices;
+ };
std::optional<MeshInfo> mesh_info;
std::optional<CurveInfo> curve_info;
std::optional<PointCloudInfo> pointcloud_info;
std::optional<InstancesInfo> instances_info;
+ std::optional<EditDataInfo> edit_data_info;
GeometryValueLog(const GeometrySet &geometry_set, bool log_full_geometry = false);
diff --git a/source/blender/nodes/NOD_multi_function.hh b/source/blender/nodes/NOD_multi_function.hh
index b6d51578b1c..21a94d9192b 100644
--- a/source/blender/nodes/NOD_multi_function.hh
+++ b/source/blender/nodes/NOD_multi_function.hh
@@ -19,15 +19,15 @@ class NodeMultiFunctions;
*/
class NodeMultiFunctionBuilder : NonCopyable, NonMovable {
private:
- bNode &node_;
- bNodeTree &tree_;
+ const bNode &node_;
+ const bNodeTree &tree_;
std::shared_ptr<MultiFunction> owned_built_fn_;
const MultiFunction *built_fn_ = nullptr;
friend NodeMultiFunctions;
public:
- NodeMultiFunctionBuilder(bNode &node, bNodeTree &tree);
+ NodeMultiFunctionBuilder(const bNode &node, const bNodeTree &tree);
/**
* Assign a multi-function for the current node. The input and output parameters of the function
@@ -42,8 +42,8 @@ class NodeMultiFunctionBuilder : NonCopyable, NonMovable {
*/
template<typename T, typename... Args> void construct_and_set_matching_fn(Args &&...args);
- bNode &node();
- bNodeTree &tree();
+ const bNode &node();
+ const bNodeTree &tree();
};
/**
@@ -69,17 +69,17 @@ class NodeMultiFunctions {
/** \name #NodeMultiFunctionBuilder Inline Methods
* \{ */
-inline NodeMultiFunctionBuilder::NodeMultiFunctionBuilder(bNode &node, bNodeTree &tree)
+inline NodeMultiFunctionBuilder::NodeMultiFunctionBuilder(const bNode &node, const bNodeTree &tree)
: node_(node), tree_(tree)
{
}
-inline bNode &NodeMultiFunctionBuilder::node()
+inline const bNode &NodeMultiFunctionBuilder::node()
{
return node_;
}
-inline bNodeTree &NodeMultiFunctionBuilder::tree()
+inline const bNodeTree &NodeMultiFunctionBuilder::tree()
{
return tree_;
}
@@ -110,7 +110,7 @@ inline void NodeMultiFunctionBuilder::construct_and_set_matching_fn(Args &&...ar
inline const NodeMultiFunctions::Item &NodeMultiFunctions::try_get(const DNode &node) const
{
static Item empty_item;
- const Item *item = map_.lookup_ptr(node->bnode());
+ const Item *item = map_.lookup_ptr(node.bnode());
if (item == nullptr) {
return empty_item;
}
diff --git a/source/blender/nodes/NOD_node_declaration.hh b/source/blender/nodes/NOD_node_declaration.hh
index 4e78f6c1142..d8b8c354230 100644
--- a/source/blender/nodes/NOD_node_declaration.hh
+++ b/source/blender/nodes/NOD_node_declaration.hh
@@ -88,6 +88,14 @@ class SocketDeclaration {
InputSocketFieldType input_field_type_ = InputSocketFieldType::None;
OutputFieldDependency output_field_dependency_;
+ /** The priority of the input for determining the domain of the node. See
+ * realtime_compositor::InputDescriptor for more information. */
+ int compositor_domain_priority_ = 0;
+
+ /** This input expects a single value and can't operate on non-single values. See
+ * realtime_compositor::InputDescriptor for more information. */
+ bool compositor_expects_single_value_ = false;
+
/** Utility method to make the socket available if there is a straightforward way to do so. */
std::function<void(bNode &)> make_available_fn_;
@@ -124,6 +132,9 @@ class SocketDeclaration {
InputSocketFieldType input_field_type() const;
const OutputFieldDependency &output_field_dependency() const;
+ int compositor_domain_priority() const;
+ bool compositor_expects_single_value() const;
+
protected:
void set_common_flags(bNodeSocket &socket) const;
bool matches_common_data(const bNodeSocket &socket) const;
@@ -238,6 +249,22 @@ class SocketDeclarationBuilder : public BaseSocketDeclarationBuilder {
return *(Self *)this;
}
+ /** The priority of the input for determining the domain of the node. See
+ * realtime_compositor::InputDescriptor for more information. */
+ Self &compositor_domain_priority(int priority)
+ {
+ decl_->compositor_domain_priority_ = priority;
+ return *(Self *)this;
+ }
+
+ /** This input expects a single value and can't operate on non-single values. See
+ * realtime_compositor::InputDescriptor for more information. */
+ Self &compositor_expects_single_value(bool value = true)
+ {
+ decl_->compositor_expects_single_value_ = value;
+ return *(Self *)this;
+ }
+
/**
* Pass a function that sets properties on the node required to make the corresponding socket
* available, if it is not available on the default state of the node. The function is allowed to
@@ -428,6 +455,16 @@ inline const OutputFieldDependency &SocketDeclaration::output_field_dependency()
return output_field_dependency_;
}
+inline int SocketDeclaration::compositor_domain_priority() const
+{
+ return compositor_domain_priority_;
+}
+
+inline bool SocketDeclaration::compositor_expects_single_value() const
+{
+ return compositor_expects_single_value_;
+}
+
inline void SocketDeclaration::make_available(bNode &node) const
{
if (make_available_fn_) {
diff --git a/source/blender/nodes/NOD_node_tree_ref.hh b/source/blender/nodes/NOD_node_tree_ref.hh
deleted file mode 100644
index 257aa5f4110..00000000000
--- a/source/blender/nodes/NOD_node_tree_ref.hh
+++ /dev/null
@@ -1,760 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-
-#pragma once
-
-/** \file
- * \ingroup nodes
- *
- * NodeTreeRef makes querying information about a bNodeTree more efficient. It is an immutable data
- * structure. It should not be used after anymore, after the underlying node tree changed.
- *
- * The following queries are supported efficiently:
- * - socket -> index of socket
- * - socket -> directly linked sockets
- * - socket -> directly linked links
- * - socket -> linked sockets when skipping reroutes
- * - socket -> node
- * - socket/node -> rna pointer
- * - node -> inputs/outputs
- * - node -> tree
- * - tree -> all nodes
- * - tree -> all (input/output) sockets
- * - idname -> nodes
- *
- * Every socket has an id. The id-space is shared between input and output sockets.
- * When storing data per socket, it is often better to use the id as index into an array, instead
- * of a hash table.
- *
- * Every node has an id as well. The same rule regarding hash tables applies.
- *
- * There is an utility to export this data structure as graph in dot format.
- */
-
-#include "BLI_array.hh"
-#include "BLI_function_ref.hh"
-#include "BLI_linear_allocator.hh"
-#include "BLI_map.hh"
-#include "BLI_multi_value_map.hh"
-#include "BLI_string_ref.hh"
-#include "BLI_timeit.hh"
-#include "BLI_utility_mixins.hh"
-#include "BLI_vector.hh"
-
-#include "BKE_node.h"
-#include "BKE_node_runtime.hh"
-
-#include "DNA_node_types.h"
-
-#include "RNA_access.h"
-
-namespace blender::nodes {
-
-class SocketRef;
-class InputSocketRef;
-class OutputSocketRef;
-class NodeRef;
-class NodeTreeRef;
-class LinkRef;
-class InternalLinkRef;
-
-using SocketIndexByIdentifierMap = Map<std::string, int>;
-
-class SocketRef : NonCopyable, NonMovable {
- protected:
- NodeRef *node_;
- bNodeSocket *bsocket_;
- bool is_input_;
- int id_;
- int index_;
- Vector<LinkRef *> directly_linked_links_;
-
- /* These sockets are linked directly, i.e. with a single link in between. */
- MutableSpan<const SocketRef *> directly_linked_sockets_;
- /* These sockets are linked when reroutes, muted links and muted nodes have been taken into
- * account. */
- MutableSpan<const SocketRef *> logically_linked_sockets_;
- /* These are the sockets that have been skipped when searching for logically linked sockets.
- * That includes for example the input and output socket of an intermediate reroute node. */
- MutableSpan<const SocketRef *> logically_linked_skipped_sockets_;
-
- friend NodeTreeRef;
-
- public:
- Span<const SocketRef *> logically_linked_sockets() const;
- Span<const SocketRef *> logically_linked_skipped_sockets() const;
- Span<const SocketRef *> directly_linked_sockets() const;
- Span<const LinkRef *> directly_linked_links() const;
-
- bool is_directly_linked() const;
- bool is_logically_linked() const;
-
- const NodeRef &node() const;
- const NodeTreeRef &tree() const;
-
- int id() const;
- int index() const;
-
- bool is_input() const;
- bool is_output() const;
-
- const SocketRef &as_base() const;
- const InputSocketRef &as_input() const;
- const OutputSocketRef &as_output() const;
-
- PointerRNA rna() const;
-
- StringRefNull idname() const;
- StringRefNull name() const;
- StringRefNull identifier() const;
- bNodeSocketType *typeinfo() const;
-
- bNodeSocket *bsocket() const;
- bNode *bnode() const;
- bNodeTree *btree() const;
-
- bool is_available() const;
- bool is_undefined() const;
-
- void *default_value() const;
- template<typename T> T *default_value() const;
-};
-
-class InputSocketRef final : public SocketRef {
- public:
- friend NodeTreeRef;
-
- Span<const OutputSocketRef *> logically_linked_sockets() const;
- Span<const OutputSocketRef *> directly_linked_sockets() const;
-
- bool is_multi_input_socket() const;
-
- private:
- void foreach_logical_origin(FunctionRef<void(const OutputSocketRef &)> origin_fn,
- FunctionRef<void(const SocketRef &)> skipped_fn,
- bool only_follow_first_input_link,
- Vector<const InputSocketRef *> &seen_sockets_stack) const;
-};
-
-class OutputSocketRef final : public SocketRef {
- public:
- friend NodeTreeRef;
-
- Span<const InputSocketRef *> logically_linked_sockets() const;
- Span<const InputSocketRef *> directly_linked_sockets() const;
-
- private:
- void foreach_logical_target(FunctionRef<void(const InputSocketRef &)> target_fn,
- FunctionRef<void(const SocketRef &)> skipped_fn,
- Vector<const OutputSocketRef *> &seen_sockets_stack) const;
-};
-
-class NodeRef : NonCopyable, NonMovable {
- private:
- NodeTreeRef *tree_;
- bNode *bnode_;
- int id_;
- Vector<InputSocketRef *> inputs_;
- Vector<OutputSocketRef *> outputs_;
- Vector<InternalLinkRef *> internal_links_;
- SocketIndexByIdentifierMap *input_index_by_identifier_;
- SocketIndexByIdentifierMap *output_index_by_identifier_;
-
- friend NodeTreeRef;
-
- public:
- const NodeTreeRef &tree() const;
-
- Span<const InputSocketRef *> inputs() const;
- Span<const OutputSocketRef *> outputs() const;
- Span<const InternalLinkRef *> internal_links() const;
- Span<const SocketRef *> sockets(eNodeSocketInOut in_out) const;
-
- const InputSocketRef &input(int index) const;
- const OutputSocketRef &output(int index) const;
-
- const InputSocketRef &input_by_identifier(StringRef identifier) const;
- const OutputSocketRef &output_by_identifier(StringRef identifier) const;
-
- bool any_input_is_directly_linked() const;
- bool any_output_is_directly_linked() const;
- bool any_socket_is_directly_linked(eNodeSocketInOut in_out) const;
-
- bNode *bnode() const;
- bNodeTree *btree() const;
-
- PointerRNA rna() const;
- StringRefNull idname() const;
- StringRefNull name() const;
- StringRefNull label() const;
- StringRefNull label_or_name() const;
- bNodeType *typeinfo() const;
- const NodeDeclaration *declaration() const;
-
- int id() const;
-
- bool is_reroute_node() const;
- bool is_group_node() const;
- bool is_group_input_node() const;
- bool is_group_output_node() const;
- bool is_muted() const;
- bool is_frame() const;
- bool is_undefined() const;
-
- void *storage() const;
- template<typename T> T *storage() const;
-};
-
-class LinkRef : NonCopyable, NonMovable {
- private:
- OutputSocketRef *from_;
- InputSocketRef *to_;
- bNodeLink *blink_;
-
- friend NodeTreeRef;
-
- public:
- const OutputSocketRef &from() const;
- const InputSocketRef &to() const;
-
- bNodeLink *blink() const;
-
- bool is_muted() const;
-};
-
-class InternalLinkRef : NonCopyable, NonMovable {
- private:
- InputSocketRef *from_;
- OutputSocketRef *to_;
- bNodeLink *blink_;
-
- friend NodeTreeRef;
-
- public:
- const InputSocketRef &from() const;
- const OutputSocketRef &to() const;
-
- bNodeLink *blink() const;
-};
-
-class NodeTreeRef : NonCopyable, NonMovable {
- private:
- LinearAllocator<> allocator_;
- bNodeTree *btree_;
- Vector<NodeRef *> nodes_by_id_;
- Vector<SocketRef *> sockets_by_id_;
- Vector<InputSocketRef *> input_sockets_;
- Vector<OutputSocketRef *> output_sockets_;
- Vector<LinkRef *> links_;
- MultiValueMap<const bNodeType *, NodeRef *> nodes_by_type_;
- Vector<std::unique_ptr<SocketIndexByIdentifierMap>> owned_identifier_maps_;
- const NodeRef *group_output_node_ = nullptr;
-
- public:
- NodeTreeRef(bNodeTree *btree);
- ~NodeTreeRef();
-
- Span<const NodeRef *> nodes() const;
- Span<const NodeRef *> nodes_by_type(StringRefNull idname) const;
- Span<const NodeRef *> nodes_by_type(const bNodeType *nodetype) const;
-
- Span<const SocketRef *> sockets() const;
- Span<const InputSocketRef *> input_sockets() const;
- Span<const OutputSocketRef *> output_sockets() const;
-
- Span<const LinkRef *> links() const;
-
- const NodeRef *find_node(const bNode &bnode) const;
-
- /**
- * This is the active group output node if there are multiple.
- */
- const NodeRef *group_output_node() const;
-
- /**
- * \return True when there is a link cycle. Unavailable sockets are ignored.
- */
- bool has_link_cycles() const;
- bool has_undefined_nodes_or_sockets() const;
-
- enum class ToposortDirection {
- LeftToRight,
- RightToLeft,
- };
-
- struct ToposortResult {
- Vector<const NodeRef *> sorted_nodes;
- /**
- * There can't be a correct topological sort of the nodes when there is a cycle. The nodes will
- * still be sorted to some degree. The caller has to decide whether it can handle non-perfect
- * sorts or not.
- */
- bool has_cycle = false;
- };
-
- /**
- * Sort nodes topologically from left to right or right to left.
- * In the future the result if this could be cached on #NodeTreeRef.
- */
- ToposortResult toposort(ToposortDirection direction) const;
-
- bNodeTree *btree() const;
- StringRefNull name() const;
-
- std::string to_dot() const;
-
- private:
- /* Utility functions used during construction. */
- InputSocketRef &find_input_socket(Map<bNode *, NodeRef *> &node_mapping,
- bNode *bnode,
- bNodeSocket *bsocket);
- OutputSocketRef &find_output_socket(Map<bNode *, NodeRef *> &node_mapping,
- bNode *bnode,
- bNodeSocket *bsocket);
-
- void create_linked_socket_caches();
- void create_socket_identifier_maps();
-};
-
-using NodeTreeRefMap = Map<bNodeTree *, std::unique_ptr<const NodeTreeRef>>;
-
-const NodeTreeRef &get_tree_ref_from_map(NodeTreeRefMap &node_tree_refs, bNodeTree &btree);
-
-namespace node_tree_ref_types {
-using nodes::InputSocketRef;
-using nodes::NodeRef;
-using nodes::NodeTreeRef;
-using nodes::NodeTreeRefMap;
-using nodes::OutputSocketRef;
-using nodes::SocketRef;
-} // namespace node_tree_ref_types
-
-/* -------------------------------------------------------------------- */
-/** \name #SocketRef Inline Methods
- * \{ */
-
-inline Span<const SocketRef *> SocketRef::logically_linked_sockets() const
-{
- return logically_linked_sockets_;
-}
-
-inline Span<const SocketRef *> SocketRef::logically_linked_skipped_sockets() const
-{
- return logically_linked_skipped_sockets_;
-}
-
-inline Span<const SocketRef *> SocketRef::directly_linked_sockets() const
-{
- return directly_linked_sockets_;
-}
-
-inline Span<const LinkRef *> SocketRef::directly_linked_links() const
-{
- return directly_linked_links_;
-}
-
-inline bool SocketRef::is_directly_linked() const
-{
- return directly_linked_sockets_.size() > 0;
-}
-
-inline bool SocketRef::is_logically_linked() const
-{
- return logically_linked_sockets_.size() > 0;
-}
-
-inline const NodeRef &SocketRef::node() const
-{
- return *node_;
-}
-
-inline const NodeTreeRef &SocketRef::tree() const
-{
- return node_->tree();
-}
-
-inline int SocketRef::id() const
-{
- return id_;
-}
-
-inline int SocketRef::index() const
-{
- return index_;
-}
-
-inline bool SocketRef::is_input() const
-{
- return is_input_;
-}
-
-inline bool SocketRef::is_output() const
-{
- return !is_input_;
-}
-
-inline const SocketRef &SocketRef::as_base() const
-{
- return *this;
-}
-
-inline const InputSocketRef &SocketRef::as_input() const
-{
- BLI_assert(this->is_input());
- return static_cast<const InputSocketRef &>(*this);
-}
-
-inline const OutputSocketRef &SocketRef::as_output() const
-{
- BLI_assert(this->is_output());
- return static_cast<const OutputSocketRef &>(*this);
-}
-
-inline StringRefNull SocketRef::idname() const
-{
- return bsocket_->idname;
-}
-
-inline StringRefNull SocketRef::name() const
-{
- return bsocket_->name;
-}
-
-inline StringRefNull SocketRef::identifier() const
-{
- return bsocket_->identifier;
-}
-
-inline bNodeSocketType *SocketRef::typeinfo() const
-{
- return bsocket_->typeinfo;
-}
-
-inline bNodeSocket *SocketRef::bsocket() const
-{
- return bsocket_;
-}
-
-inline bNode *SocketRef::bnode() const
-{
- return node_->bnode();
-}
-
-inline bNodeTree *SocketRef::btree() const
-{
- return node_->btree();
-}
-
-inline bool SocketRef::is_available() const
-{
- return (bsocket_->flag & SOCK_UNAVAIL) == 0;
-}
-
-inline bool SocketRef::is_undefined() const
-{
- return bsocket_->typeinfo == &NodeSocketTypeUndefined;
-}
-
-inline void *SocketRef::default_value() const
-{
- return bsocket_->default_value;
-}
-
-template<typename T> inline T *SocketRef::default_value() const
-{
- return (T *)bsocket_->default_value;
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name #InputSocketRef Inline Methods
- * \{ */
-
-inline Span<const OutputSocketRef *> InputSocketRef::logically_linked_sockets() const
-{
- return logically_linked_sockets_.as_span().cast<const OutputSocketRef *>();
-}
-
-inline Span<const OutputSocketRef *> InputSocketRef::directly_linked_sockets() const
-{
- return directly_linked_sockets_.cast<const OutputSocketRef *>();
-}
-
-inline bool InputSocketRef::is_multi_input_socket() const
-{
- return bsocket_->flag & SOCK_MULTI_INPUT;
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name #OutputSocketRef Inline Methods
- * \{ */
-
-inline Span<const InputSocketRef *> OutputSocketRef::logically_linked_sockets() const
-{
- return logically_linked_sockets_.as_span().cast<const InputSocketRef *>();
-}
-
-inline Span<const InputSocketRef *> OutputSocketRef::directly_linked_sockets() const
-{
- return directly_linked_sockets_.cast<const InputSocketRef *>();
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name #NodeRef Inline Methods
- * \{ */
-
-inline const NodeTreeRef &NodeRef::tree() const
-{
- return *tree_;
-}
-
-inline Span<const InputSocketRef *> NodeRef::inputs() const
-{
- return inputs_;
-}
-
-inline Span<const OutputSocketRef *> NodeRef::outputs() const
-{
- return outputs_;
-}
-
-inline Span<const SocketRef *> NodeRef::sockets(const eNodeSocketInOut in_out) const
-{
- return in_out == SOCK_IN ? inputs_.as_span().cast<const SocketRef *>() :
- outputs_.as_span().cast<const SocketRef *>();
-}
-
-inline Span<const InternalLinkRef *> NodeRef::internal_links() const
-{
- return internal_links_;
-}
-
-inline const InputSocketRef &NodeRef::input(int index) const
-{
- return *inputs_[index];
-}
-
-inline const OutputSocketRef &NodeRef::output(int index) const
-{
- return *outputs_[index];
-}
-
-inline const InputSocketRef &NodeRef::input_by_identifier(StringRef identifier) const
-{
- const int index = input_index_by_identifier_->lookup_as(identifier);
- return this->input(index);
-}
-
-inline const OutputSocketRef &NodeRef::output_by_identifier(StringRef identifier) const
-{
- const int index = output_index_by_identifier_->lookup_as(identifier);
- return this->output(index);
-}
-
-inline bNode *NodeRef::bnode() const
-{
- return bnode_;
-}
-
-inline bNodeTree *NodeRef::btree() const
-{
- return tree_->btree();
-}
-
-inline StringRefNull NodeRef::idname() const
-{
- return bnode_->idname;
-}
-
-inline StringRefNull NodeRef::name() const
-{
- return bnode_->name;
-}
-
-inline StringRefNull NodeRef::label() const
-{
- return bnode_->label;
-}
-
-inline StringRefNull NodeRef::label_or_name() const
-{
- const StringRefNull label = this->label();
- if (!label.is_empty()) {
- return label;
- }
- return this->name();
-}
-
-inline bNodeType *NodeRef::typeinfo() const
-{
- return bnode_->typeinfo;
-}
-
-/* Returns a pointer because not all nodes have declarations currently. */
-inline const NodeDeclaration *NodeRef::declaration() const
-{
- nodeDeclarationEnsure(this->tree().btree(), bnode_);
- return bnode_->runtime->declaration;
-}
-
-inline int NodeRef::id() const
-{
- return id_;
-}
-
-inline bool NodeRef::is_reroute_node() const
-{
- return bnode_->type == NODE_REROUTE;
-}
-
-inline bool NodeRef::is_group_node() const
-{
- return bnode_->type == NODE_GROUP || bnode_->type == NODE_CUSTOM_GROUP;
-}
-
-inline bool NodeRef::is_group_input_node() const
-{
- return bnode_->type == NODE_GROUP_INPUT;
-}
-
-inline bool NodeRef::is_group_output_node() const
-{
- return bnode_->type == NODE_GROUP_OUTPUT;
-}
-
-inline bool NodeRef::is_frame() const
-{
- return bnode_->type == NODE_FRAME;
-}
-
-inline bool NodeRef::is_undefined() const
-{
- return bnode_->typeinfo == &NodeTypeUndefined;
-}
-
-inline bool NodeRef::is_muted() const
-{
- return (bnode_->flag & NODE_MUTED) != 0;
-}
-
-inline void *NodeRef::storage() const
-{
- return bnode_->storage;
-}
-
-template<typename T> inline T *NodeRef::storage() const
-{
- return (T *)bnode_->storage;
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name #LinkRef Inline Methods
- * \{ */
-
-inline const OutputSocketRef &LinkRef::from() const
-{
- return *from_;
-}
-
-inline const InputSocketRef &LinkRef::to() const
-{
- return *to_;
-}
-
-inline bNodeLink *LinkRef::blink() const
-{
- return blink_;
-}
-
-inline bool LinkRef::is_muted() const
-{
- return blink_->flag & NODE_LINK_MUTED;
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name #InternalLinkRef Inline Methods
- * \{ */
-
-inline const InputSocketRef &InternalLinkRef::from() const
-{
- return *from_;
-}
-
-inline const OutputSocketRef &InternalLinkRef::to() const
-{
- return *to_;
-}
-
-inline bNodeLink *InternalLinkRef::blink() const
-{
- return blink_;
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name #NodeTreeRef Inline Methods
- * \{ */
-
-inline Span<const NodeRef *> NodeTreeRef::nodes() const
-{
- return nodes_by_id_;
-}
-
-inline Span<const NodeRef *> NodeTreeRef::nodes_by_type(StringRefNull idname) const
-{
- const bNodeType *nodetype = nodeTypeFind(idname.c_str());
- return this->nodes_by_type(nodetype);
-}
-
-inline Span<const NodeRef *> NodeTreeRef::nodes_by_type(const bNodeType *nodetype) const
-{
- return nodes_by_type_.lookup(nodetype);
-}
-
-inline Span<const SocketRef *> NodeTreeRef::sockets() const
-{
- return sockets_by_id_;
-}
-
-inline Span<const InputSocketRef *> NodeTreeRef::input_sockets() const
-{
- return input_sockets_;
-}
-
-inline Span<const OutputSocketRef *> NodeTreeRef::output_sockets() const
-{
- return output_sockets_;
-}
-
-inline Span<const LinkRef *> NodeTreeRef::links() const
-{
- return links_;
-}
-
-inline const NodeRef *NodeTreeRef::group_output_node() const
-{
- return group_output_node_;
-}
-
-inline bNodeTree *NodeTreeRef::btree() const
-{
- return btree_;
-}
-
-inline StringRefNull NodeTreeRef::name() const
-{
- return btree_->id.name + 2;
-}
-
-/** \} */
-
-} // namespace blender::nodes
diff --git a/source/blender/nodes/NOD_shader.h b/source/blender/nodes/NOD_shader.h
index 1d1310360b8..8fe77bffaad 100644
--- a/source/blender/nodes/NOD_shader.h
+++ b/source/blender/nodes/NOD_shader.h
@@ -26,6 +26,7 @@ void register_node_type_sh_camera(void);
void register_node_type_sh_value(void);
void register_node_type_sh_rgb(void);
void register_node_type_sh_mix_rgb(void);
+void register_node_type_sh_mix(void);
void register_node_type_sh_valtorgb(void);
void register_node_type_sh_rgbtobw(void);
void register_node_type_sh_shadertorgb(void);
diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h
index 7ca013bf792..d587da823f1 100644
--- a/source/blender/nodes/NOD_static_types.h
+++ b/source/blender/nodes/NOD_static_types.h
@@ -17,111 +17,112 @@
/* WARNING! If you edit those strings, please do the same in relevant nodes files (under blender/nodes/...)! */
/* Tree type Node ID RNA def function Enum name Struct name UI Name UI Description */
-DefNode(Node, NODE_FRAME, def_frame, "FRAME", Frame, "Frame", "" )
-DefNode(Node, NODE_GROUP, def_group, "GROUP", Group, "Group", "" )
-DefNode(Node, NODE_GROUP_INPUT, def_group_input, "GROUP_INPUT", GroupInput, "Group Input", "" )
-DefNode(Node, NODE_GROUP_OUTPUT, def_group_output, "GROUP_OUTPUT", GroupOutput, "Group Output", "" )
-DefNode(Node, NODE_REROUTE, 0, "REROUTE", Reroute, "Reroute", "" )
+DefNode(Node, NODE_FRAME, def_frame, "FRAME", Frame, "Frame", "Collect related nodes together in a common area. Useful for organization when the re-usability of a node group is not required")
+DefNode(Node, NODE_GROUP, def_group, "GROUP", Group, "Group", "")
+DefNode(Node, NODE_GROUP_INPUT, def_group_input, "GROUP_INPUT", GroupInput, "Group Input", "Expose connected data from inside a node group as inputs to its interface")
+DefNode(Node, NODE_GROUP_OUTPUT, def_group_output, "GROUP_OUTPUT", GroupOutput, "Group Output", "Output data from inside of a node group")
+DefNode(Node, NODE_REROUTE, 0, "REROUTE", Reroute, "Reroute", "A single-socket organization tool that supports one input and multiple outputs")
-DefNode(ShaderNode, SH_NODE_RGB, 0, "RGB", RGB, "RGB", "" )
-DefNode(ShaderNode, SH_NODE_VALUE, 0, "VALUE", Value, "Value", "" )
-DefNode(ShaderNode, SH_NODE_MIX_RGB, def_mix_rgb, "MIX_RGB", MixRGB, "MixRGB", "" )
-DefNode(ShaderNode, SH_NODE_VALTORGB, def_colorramp, "VALTORGB", ValToRGB, "ColorRamp", "" )
-DefNode(ShaderNode, SH_NODE_RGBTOBW, 0, "RGBTOBW", RGBToBW, "RGB to BW", "" )
-DefNode(ShaderNode, SH_NODE_SHADERTORGB, 0, "SHADERTORGB", ShaderToRGB, "Shader to RGB", "" )
-DefNode(ShaderNode, SH_NODE_NORMAL, 0, "NORMAL", Normal, "Normal", "" )
-DefNode(ShaderNode, SH_NODE_GAMMA, 0, "GAMMA", Gamma, "Gamma", "" )
-DefNode(ShaderNode, SH_NODE_BRIGHTCONTRAST, 0, "BRIGHTCONTRAST", BrightContrast, "Bright Contrast", "" )
-DefNode(ShaderNode, SH_NODE_MAPPING, def_sh_mapping, "MAPPING", Mapping, "Mapping", "" )
-DefNode(ShaderNode, SH_NODE_CURVE_VEC, def_vector_curve, "CURVE_VEC", VectorCurve, "Vector Curves", "" )
-DefNode(ShaderNode, SH_NODE_CURVE_RGB, def_rgb_curve, "CURVE_RGB", RGBCurve, "RGB Curves", "" )
-DefNode(ShaderNode, SH_NODE_CAMERA, 0, "CAMERA", CameraData, "Camera Data", "" )
-DefNode(ShaderNode, SH_NODE_MAP_RANGE, def_map_range, "MAP_RANGE", MapRange, "Map Range", "" )
-DefNode(ShaderNode, SH_NODE_CLAMP, def_clamp, "CLAMP", Clamp, "Clamp", "" )
-DefNode(ShaderNode, SH_NODE_MATH, def_math, "MATH", Math, "Math", "" )
-DefNode(ShaderNode, SH_NODE_VECTOR_MATH, def_vector_math, "VECT_MATH", VectorMath, "Vector Math", "" )
-DefNode(ShaderNode, SH_NODE_SQUEEZE, 0, "SQUEEZE", Squeeze, "Squeeze Value", "" )
-DefNode(ShaderNode, SH_NODE_INVERT, 0, "INVERT", Invert, "Invert", "" )
-DefNode(ShaderNode, SH_NODE_SEPRGB_LEGACY, 0, "SEPRGB", SeparateRGB, "Separate RGB", "" )
-DefNode(ShaderNode, SH_NODE_COMBRGB_LEGACY, 0, "COMBRGB", CombineRGB, "Combine RGB", "" )
-DefNode(ShaderNode, SH_NODE_HUE_SAT, 0, "HUE_SAT", HueSaturation, "Hue Saturation Value", "" )
+DefNode(ShaderNode, SH_NODE_RGB, 0, "RGB", RGB, "RGB", "A color picker")
+DefNode(ShaderNode, SH_NODE_VALUE, 0, "VALUE", Value, "Value", "Used to Input numerical values to other nodes in the tree")
+DefNode(ShaderNode, SH_NODE_MIX_RGB_LEGACY, def_mix_rgb, "MIX_RGB", MixRGB, "MixRGB", "Mix two input colors")
+DefNode(ShaderNode, SH_NODE_VALTORGB, def_colorramp, "VALTORGB", ValToRGB, "ColorRamp", "Map values to colors with the use of a gradient")
+DefNode(ShaderNode, SH_NODE_RGBTOBW, 0, "RGBTOBW", RGBToBW, "RGB to BW", "Convert a color's luminance to a grayscale value")
+DefNode(ShaderNode, SH_NODE_SHADERTORGB, 0, "SHADERTORGB", ShaderToRGB, "Shader to RGB", "Convert rendering effect (such as light and shadow) to color. Typically used for non-photorealistic rendering, to apply additional effects on the output of BSDFs.\nNote: only supported for Eevee")
+DefNode(ShaderNode, SH_NODE_NORMAL, 0, "NORMAL", Normal, "Normal", "Generate a normal vector and a dot product")
+DefNode(ShaderNode, SH_NODE_GAMMA, 0, "GAMMA", Gamma, "Gamma", "Apply a gamma correction")
+DefNode(ShaderNode, SH_NODE_BRIGHTCONTRAST, 0, "BRIGHTCONTRAST", BrightContrast, "Bright Contrast", "Control the brightness and contrast of the input color")
+DefNode(ShaderNode, SH_NODE_MAPPING, def_sh_mapping, "MAPPING", Mapping, "Mapping", "Transform the input vector by applying translation, rotation, and scale")
+DefNode(ShaderNode, SH_NODE_CURVE_VEC, def_vector_curve, "CURVE_VEC", VectorCurve, "Vector Curves", "Map an input vectors to curves, used to fine-tune the interpolation of the input")
+DefNode(ShaderNode, SH_NODE_CURVE_RGB, def_rgb_curve, "CURVE_RGB", RGBCurve, "RGB Curves", "Apply color corrections for each color channel")
+DefNode(ShaderNode, SH_NODE_CAMERA, 0, "CAMERA", CameraData, "Camera Data", "Retrieve information about the camera and how it relates to the current shading point's position")
+DefNode(ShaderNode, SH_NODE_MAP_RANGE, def_map_range, "MAP_RANGE", MapRange, "Map Range", "Remap a value from a range to a target range")
+DefNode(ShaderNode, SH_NODE_CLAMP, def_clamp, "CLAMP", Clamp, "Clamp", "Clamp a value between a minimum and a maximum")
+DefNode(ShaderNode, SH_NODE_MATH, def_math, "MATH", Math, "Math", "Perform math operations")
+DefNode(ShaderNode, SH_NODE_VECTOR_MATH, def_vector_math, "VECT_MATH", VectorMath, "Vector Math", "Perform vector math operation")
+DefNode(ShaderNode, SH_NODE_SQUEEZE, 0, "SQUEEZE", Squeeze, "Squeeze Value", "")
+DefNode(ShaderNode, SH_NODE_INVERT, 0, "INVERT", Invert, "Invert", "Invert a color, producing a negative")
+DefNode(ShaderNode, SH_NODE_SEPRGB_LEGACY, 0, "SEPRGB", SeparateRGB, "Separate RGB", "Split a color into its red, green, and blue channels (Deprecated)")
+DefNode(ShaderNode, SH_NODE_COMBRGB_LEGACY, 0, "COMBRGB", CombineRGB, "Combine RGB", "Generate a color from its red, green, and blue channels (Deprecated)")
+DefNode(ShaderNode, SH_NODE_HUE_SAT, 0, "HUE_SAT", HueSaturation, "Hue Saturation Value","Apply a color transformation in the HSV color model")
-DefNode(ShaderNode, SH_NODE_OUTPUT_MATERIAL, def_sh_output, "OUTPUT_MATERIAL", OutputMaterial, "Material Output", "" )
-DefNode(ShaderNode, SH_NODE_EEVEE_SPECULAR, 0, "EEVEE_SPECULAR", EeveeSpecular, "Specular BSDF", "" )
-DefNode(ShaderNode, SH_NODE_OUTPUT_LIGHT, def_sh_output, "OUTPUT_LIGHT", OutputLight, "Light Output", "" )
-DefNode(ShaderNode, SH_NODE_OUTPUT_WORLD, def_sh_output, "OUTPUT_WORLD", OutputWorld, "World Output", "" )
-DefNode(ShaderNode, SH_NODE_OUTPUT_LINESTYLE, def_sh_output_linestyle,"OUTPUT_LINESTYLE", OutputLineStyle, "Line Style Output", "" )
-DefNode(ShaderNode, SH_NODE_FRESNEL, 0, "FRESNEL", Fresnel, "Fresnel", "" )
-DefNode(ShaderNode, SH_NODE_LAYER_WEIGHT, 0, "LAYER_WEIGHT", LayerWeight, "Layer Weight", "" )
-DefNode(ShaderNode, SH_NODE_MIX_SHADER, 0, "MIX_SHADER", MixShader, "Mix Shader", "" )
-DefNode(ShaderNode, SH_NODE_ADD_SHADER, 0, "ADD_SHADER", AddShader, "Add Shader", "" )
-DefNode(ShaderNode, SH_NODE_ATTRIBUTE, def_sh_attribute, "ATTRIBUTE", Attribute, "Attribute", "" )
-DefNode(ShaderNode, SH_NODE_AMBIENT_OCCLUSION, def_sh_ambient_occlusion,"AMBIENT_OCCLUSION", AmbientOcclusion, "Ambient Occlusion", "" )
-DefNode(ShaderNode, SH_NODE_BACKGROUND, 0, "BACKGROUND", Background, "Background", "" )
-DefNode(ShaderNode, SH_NODE_HOLDOUT, 0, "HOLDOUT", Holdout, "Holdout", "" )
-DefNode(ShaderNode, SH_NODE_BSDF_ANISOTROPIC, def_anisotropic, "BSDF_ANISOTROPIC", BsdfAnisotropic, "Anisotropic BSDF", "" )
-DefNode(ShaderNode, SH_NODE_BSDF_DIFFUSE, 0, "BSDF_DIFFUSE", BsdfDiffuse, "Diffuse BSDF", "" )
-DefNode(ShaderNode, SH_NODE_BSDF_PRINCIPLED, def_principled, "BSDF_PRINCIPLED", BsdfPrincipled, "Principled BSDF", "" )
-DefNode(ShaderNode, SH_NODE_BSDF_GLOSSY, def_glossy, "BSDF_GLOSSY", BsdfGlossy, "Glossy BSDF", "" )
-DefNode(ShaderNode, SH_NODE_BSDF_GLASS, def_glass, "BSDF_GLASS", BsdfGlass, "Glass BSDF", "" )
-DefNode(ShaderNode, SH_NODE_BSDF_REFRACTION, def_refraction, "BSDF_REFRACTION", BsdfRefraction, "Refraction BSDF", "" )
-DefNode(ShaderNode, SH_NODE_BSDF_TRANSLUCENT, 0, "BSDF_TRANSLUCENT", BsdfTranslucent, "Translucent BSDF", "" )
-DefNode(ShaderNode, SH_NODE_BSDF_TRANSPARENT, 0, "BSDF_TRANSPARENT", BsdfTransparent, "Transparent BSDF", "" )
-DefNode(ShaderNode, SH_NODE_BSDF_VELVET, 0, "BSDF_VELVET", BsdfVelvet, "Velvet BSDF", "" )
-DefNode(ShaderNode, SH_NODE_BSDF_TOON, def_toon, "BSDF_TOON", BsdfToon, "Toon BSDF", "" )
-DefNode(ShaderNode, SH_NODE_BSDF_HAIR, def_hair, "BSDF_HAIR", BsdfHair, "Hair BSDF", "" )
-DefNode(ShaderNode, SH_NODE_BSDF_HAIR_PRINCIPLED, def_hair_principled, "BSDF_HAIR_PRINCIPLED", BsdfHairPrincipled, "Principled Hair BSDF", "")
-DefNode(ShaderNode, SH_NODE_SUBSURFACE_SCATTERING, def_sh_subsurface, "SUBSURFACE_SCATTERING",SubsurfaceScattering,"Subsurface Scattering","")
-DefNode(ShaderNode, SH_NODE_VOLUME_ABSORPTION, 0, "VOLUME_ABSORPTION", VolumeAbsorption, "Volume Absorption", "" )
-DefNode(ShaderNode, SH_NODE_VOLUME_SCATTER, 0, "VOLUME_SCATTER", VolumeScatter, "Volume Scatter", "" )
-DefNode(ShaderNode, SH_NODE_VOLUME_PRINCIPLED, 0, "PRINCIPLED_VOLUME", VolumePrincipled, "Principled Volume", "" )
-DefNode(ShaderNode, SH_NODE_EMISSION, 0, "EMISSION", Emission, "Emission", "" )
-DefNode(ShaderNode, SH_NODE_NEW_GEOMETRY, 0, "NEW_GEOMETRY", NewGeometry, "Geometry", "" )
-DefNode(ShaderNode, SH_NODE_LIGHT_PATH, 0, "LIGHT_PATH", LightPath, "Light Path", "" )
-DefNode(ShaderNode, SH_NODE_LIGHT_FALLOFF, 0, "LIGHT_FALLOFF", LightFalloff, "Light Falloff", "" )
-DefNode(ShaderNode, SH_NODE_OBJECT_INFO, 0, "OBJECT_INFO", ObjectInfo, "Object Info", "" )
-DefNode(ShaderNode, SH_NODE_PARTICLE_INFO, 0, "PARTICLE_INFO", ParticleInfo, "Particle Info", "" )
-DefNode(ShaderNode, SH_NODE_HAIR_INFO, 0, "HAIR_INFO", HairInfo, "Curves Info", "" )
-DefNode(ShaderNode, SH_NODE_POINT_INFO, 0, "POINT_INFO", PointInfo, "Point Info", "" )
-DefNode(ShaderNode, SH_NODE_VOLUME_INFO, 0, "VOLUME_INFO", VolumeInfo, "Volume Info", "" )
-DefNode(ShaderNode, SH_NODE_WIREFRAME, def_sh_tex_wireframe, "WIREFRAME", Wireframe, "Wireframe", "" )
-DefNode(ShaderNode, SH_NODE_WAVELENGTH, 0, "WAVELENGTH", Wavelength, "Wavelength", "" )
-DefNode(ShaderNode, SH_NODE_BLACKBODY, 0, "BLACKBODY", Blackbody, "Blackbody", "" )
-DefNode(ShaderNode, SH_NODE_BUMP, def_sh_bump, "BUMP", Bump, "Bump", "" )
-DefNode(ShaderNode, SH_NODE_NORMAL_MAP, def_sh_normal_map, "NORMAL_MAP", NormalMap, "Normal Map", "" )
-DefNode(ShaderNode, SH_NODE_TANGENT, def_sh_tangent, "TANGENT", Tangent, "Tangent", "" )
-DefNode(ShaderNode, SH_NODE_SCRIPT, def_sh_script, "SCRIPT", Script, "Script", "" )
-DefNode(ShaderNode, SH_NODE_TEX_IMAGE, def_sh_tex_image, "TEX_IMAGE", TexImage, "Image Texture", "" )
-DefNode(ShaderNode, SH_NODE_TEX_ENVIRONMENT, def_sh_tex_environment, "TEX_ENVIRONMENT", TexEnvironment, "Environment Texture","" )
-DefNode(ShaderNode, SH_NODE_TEX_SKY, def_sh_tex_sky, "TEX_SKY", TexSky, "Sky Texture", "" )
-DefNode(ShaderNode, SH_NODE_TEX_GRADIENT, def_sh_tex_gradient, "TEX_GRADIENT", TexGradient, "Gradient Texture", "" )
-DefNode(ShaderNode, SH_NODE_TEX_NOISE, def_sh_tex_noise, "TEX_NOISE", TexNoise, "Noise Texture", "" )
-DefNode(ShaderNode, SH_NODE_TEX_MAGIC, def_sh_tex_magic, "TEX_MAGIC", TexMagic, "Magic Texture", "" )
-DefNode(ShaderNode, SH_NODE_TEX_WAVE, def_sh_tex_wave, "TEX_WAVE", TexWave, "Wave Texture", "" )
-DefNode(ShaderNode, SH_NODE_TEX_MUSGRAVE, def_sh_tex_musgrave, "TEX_MUSGRAVE", TexMusgrave, "Musgrave Texture", "" )
-DefNode(ShaderNode, SH_NODE_TEX_VORONOI, def_sh_tex_voronoi, "TEX_VORONOI", TexVoronoi, "Voronoi Texture", "" )
-DefNode(ShaderNode, SH_NODE_TEX_CHECKER, def_sh_tex_checker, "TEX_CHECKER", TexChecker, "Checker Texture", "" )
-DefNode(ShaderNode, SH_NODE_TEX_BRICK, def_sh_tex_brick, "TEX_BRICK", TexBrick, "Brick Texture", "" )
-DefNode(ShaderNode, SH_NODE_TEX_POINTDENSITY, def_sh_tex_pointdensity,"TEX_POINTDENSITY", TexPointDensity, "Point Density", "" )
-DefNode(ShaderNode, SH_NODE_TEX_COORD, def_sh_tex_coord, "TEX_COORD", TexCoord, "Texture Coordinate","" )
-DefNode(ShaderNode, SH_NODE_VECTOR_ROTATE, def_sh_vector_rotate, "VECTOR_ROTATE", VectorRotate, "Vector Rotate", "" )
-DefNode(ShaderNode, SH_NODE_VECT_TRANSFORM, def_sh_vect_transform, "VECT_TRANSFORM", VectorTransform, "Vector Transform", "" )
-DefNode(ShaderNode, SH_NODE_SEPHSV_LEGACY, 0, "SEPHSV", SeparateHSV, "Separate HSV", "" )
-DefNode(ShaderNode, SH_NODE_COMBHSV_LEGACY, 0, "COMBHSV", CombineHSV, "Combine HSV", "" )
-DefNode(ShaderNode, SH_NODE_UVMAP, def_sh_uvmap, "UVMAP", UVMap, "UV Map", "" )
-DefNode(ShaderNode, SH_NODE_VERTEX_COLOR, def_sh_vertex_color, "VERTEX_COLOR", VertexColor, "Color Attribute", "" )
-DefNode(ShaderNode, SH_NODE_UVALONGSTROKE, def_sh_uvalongstroke, "UVALONGSTROKE", UVAlongStroke, "UV Along Stroke", "" )
-DefNode(ShaderNode, SH_NODE_SEPXYZ, 0, "SEPXYZ", SeparateXYZ, "Separate XYZ", "" )
-DefNode(ShaderNode, SH_NODE_COMBXYZ, 0, "COMBXYZ", CombineXYZ, "Combine XYZ", "" )
-DefNode(ShaderNode, SH_NODE_BEVEL, def_sh_bevel, "BEVEL", Bevel, "Bevel", "" )
-DefNode(ShaderNode, SH_NODE_DISPLACEMENT, def_sh_displacement, "DISPLACEMENT", Displacement, "Displacement", "" )
-DefNode(ShaderNode, SH_NODE_VECTOR_DISPLACEMENT,def_sh_vector_displacement,"VECTOR_DISPLACEMENT",VectorDisplacement,"Vector Displacement","" )
-DefNode(ShaderNode, SH_NODE_TEX_IES, def_sh_tex_ies, "TEX_IES", TexIES, "IES Texture", "" )
-DefNode(ShaderNode, SH_NODE_TEX_WHITE_NOISE, def_sh_tex_white_noise, "TEX_WHITE_NOISE", TexWhiteNoise, "White Noise", "" )
-DefNode(ShaderNode, SH_NODE_OUTPUT_AOV, def_sh_output_aov, "OUTPUT_AOV", OutputAOV, "AOV Output", "" )
-DefNode(ShaderNode, SH_NODE_CURVE_FLOAT, def_float_curve, "CURVE_FLOAT", FloatCurve, "Float Curve", "" )
-DefNode(ShaderNode, SH_NODE_COMBINE_COLOR, def_sh_combsep_color, "COMBINE_COLOR", CombineColor, "Combine Color", "" )
-DefNode(ShaderNode, SH_NODE_SEPARATE_COLOR, def_sh_combsep_color, "SEPARATE_COLOR", SeparateColor, "Separate Color", "" )
+DefNode(ShaderNode, SH_NODE_OUTPUT_MATERIAL, def_sh_output, "OUTPUT_MATERIAL", OutputMaterial, "Material Output", "Output surface material information for use in rendering")
+DefNode(ShaderNode, SH_NODE_EEVEE_SPECULAR, 0, "EEVEE_SPECULAR", EeveeSpecular, "Specular BSDF", "Similar to the Principled BSDF node but uses the specular workflow instead of metallic, which functions by specifying the facing (along normal) reflection color. Energy is not conserved, so the result may not be physically accurate")
+DefNode(ShaderNode, SH_NODE_OUTPUT_LIGHT, def_sh_output, "OUTPUT_LIGHT", OutputLight, "Light Output", "Output light information to a light object")
+DefNode(ShaderNode, SH_NODE_OUTPUT_WORLD, def_sh_output, "OUTPUT_WORLD", OutputWorld, "World Output", "Output light color information to the scene's World")
+DefNode(ShaderNode, SH_NODE_OUTPUT_LINESTYLE, def_sh_output_linestyle,"OUTPUT_LINESTYLE", OutputLineStyle, "Line Style Output", "")
+DefNode(ShaderNode, SH_NODE_FRESNEL, 0, "FRESNEL", Fresnel, "Fresnel", "Produce a blending factor depending on the angle between the surface normal and the view direction using Fresnel equations.\nTypically used for mixing reflections at grazing angles")
+DefNode(ShaderNode, SH_NODE_LAYER_WEIGHT, 0, "LAYER_WEIGHT", LayerWeight, "Layer Weight", "Produce a blending factor depending on the angle between the surface normal and the view direction.\nTypically used for layering shaders with the Mix Shader node")
+DefNode(ShaderNode, SH_NODE_MIX_SHADER, 0, "MIX_SHADER", MixShader, "Mix Shader", "Mix two shaders together. Typically used for material layering")
+DefNode(ShaderNode, SH_NODE_ADD_SHADER, 0, "ADD_SHADER", AddShader, "Add Shader", "Add two Shaders together")
+DefNode(ShaderNode, SH_NODE_ATTRIBUTE, def_sh_attribute, "ATTRIBUTE", Attribute, "Attribute", "Retrieve attributes attached to objects or geometry")
+DefNode(ShaderNode, SH_NODE_AMBIENT_OCCLUSION, def_sh_ambient_occlusion,"AMBIENT_OCCLUSION", AmbientOcclusion, "Ambient Occlusion", "Compute how much the hemisphere above the shading point is occluded, for example to add weathering effects to corners.\nNote: For Cycles, this may slow down renders significantly")
+DefNode(ShaderNode, SH_NODE_BACKGROUND, 0, "BACKGROUND", Background, "Background", "Add background light emission.\nNote: This node should only be used for the world surface output")
+DefNode(ShaderNode, SH_NODE_HOLDOUT, 0, "HOLDOUT", Holdout, "Holdout", "Create a \"hole\" in the image with zero alpha transparency, which is useful for compositing.\nNote: the holdout shader can only create alpha when transparency is enabled in the film settings")
+DefNode(ShaderNode, SH_NODE_BSDF_ANISOTROPIC, def_anisotropic, "BSDF_ANISOTROPIC", BsdfAnisotropic, "Anisotropic BSDF", "Glossy reflection with separate control over U and V direction roughness")
+DefNode(ShaderNode, SH_NODE_BSDF_DIFFUSE, 0, "BSDF_DIFFUSE", BsdfDiffuse, "Diffuse BSDF", "Lambertian and Oren-Nayar diffuse reflection")
+DefNode(ShaderNode, SH_NODE_BSDF_PRINCIPLED, def_principled, "BSDF_PRINCIPLED", BsdfPrincipled, "Principled BSDF", "Physically-based, easy-to-use shader for rendering surface materials, based on the Disney principled model also known as the \"PBR\" shader")
+DefNode(ShaderNode, SH_NODE_BSDF_GLOSSY, def_glossy, "BSDF_GLOSSY", BsdfGlossy, "Glossy BSDF", "Reflection with microfacet distribution, used for materials such as metal or mirrors")
+DefNode(ShaderNode, SH_NODE_BSDF_GLASS, def_glass, "BSDF_GLASS", BsdfGlass, "Glass BSDF", "Glass-like shader mixing refraction and reflection at grazing angles")
+DefNode(ShaderNode, SH_NODE_BSDF_REFRACTION, def_refraction, "BSDF_REFRACTION", BsdfRefraction, "Refraction BSDF", "Glossy refraction with sharp or microfacet distribution, typically used for materials that transmit light")
+DefNode(ShaderNode, SH_NODE_BSDF_TRANSLUCENT, 0, "BSDF_TRANSLUCENT", BsdfTranslucent, "Translucent BSDF", "Lambertian diffuse transmission")
+DefNode(ShaderNode, SH_NODE_BSDF_TRANSPARENT, 0, "BSDF_TRANSPARENT", BsdfTransparent, "Transparent BSDF", "Transparency without refraction, passing straight through the surface as if there were no geometry")
+DefNode(ShaderNode, SH_NODE_BSDF_VELVET, 0, "BSDF_VELVET", BsdfVelvet, "Velvet BSDF", "Reflection for materials such as cloth.\nTypically mixed with other shaders (such as a Diffuse Shader) and is not particularly useful on its own")
+DefNode(ShaderNode, SH_NODE_BSDF_TOON, def_toon, "BSDF_TOON", BsdfToon, "Toon BSDF", "Diffuse and Glossy shaders with cartoon light effects")
+DefNode(ShaderNode, SH_NODE_BSDF_HAIR, def_hair, "BSDF_HAIR", BsdfHair, "Hair BSDF", "Reflection and transmission shaders optimized for hair rendering")
+DefNode(ShaderNode, SH_NODE_BSDF_HAIR_PRINCIPLED, def_hair_principled, "BSDF_HAIR_PRINCIPLED", BsdfHairPrincipled, "Principled Hair BSDF", "Physically-based, easy-to-use shader for rendering hair and fur")
+DefNode(ShaderNode, SH_NODE_SUBSURFACE_SCATTERING, def_sh_subsurface, "SUBSURFACE_SCATTERING",SubsurfaceScattering,"Subsurface Scattering","Subsurface multiple scattering shader to simulate light entering the surface and bouncing internally.\nTypically used for materials such as skin, wax, marble or milk")
+DefNode(ShaderNode, SH_NODE_VOLUME_ABSORPTION, 0, "VOLUME_ABSORPTION", VolumeAbsorption, "Volume Absorption", "Absorb light as it passes through the volume")
+DefNode(ShaderNode, SH_NODE_VOLUME_SCATTER, 0, "VOLUME_SCATTER", VolumeScatter, "Volume Scatter", "Scatter light as it passes through the volume, often used to add fog to a scene")
+DefNode(ShaderNode, SH_NODE_VOLUME_PRINCIPLED, 0, "PRINCIPLED_VOLUME", VolumePrincipled, "Principled Volume", "Combine all volume shading components into a single easy to use node")
+DefNode(ShaderNode, SH_NODE_EMISSION, 0, "EMISSION", Emission, "Emission", "Lambertian emission shader")
+DefNode(ShaderNode, SH_NODE_NEW_GEOMETRY, 0, "NEW_GEOMETRY", NewGeometry, "Geometry", "Retrieve geometric information about the current shading point")
+DefNode(ShaderNode, SH_NODE_LIGHT_PATH, 0, "LIGHT_PATH", LightPath, "Light Path", "Retrieve the type of incoming ray for which the shader is being executed.\nTypically used for non-physically-based tricks")
+DefNode(ShaderNode, SH_NODE_LIGHT_FALLOFF, 0, "LIGHT_FALLOFF", LightFalloff, "Light Falloff", "Manipulate how light intensity decreases over distance. Typically used for non-physically-based effects; in reality light always falls off quadratically")
+DefNode(ShaderNode, SH_NODE_OBJECT_INFO, 0, "OBJECT_INFO", ObjectInfo, "Object Info", "Retrieve information about the object instance")
+DefNode(ShaderNode, SH_NODE_PARTICLE_INFO, 0, "PARTICLE_INFO", ParticleInfo, "Particle Info", "Retrieve the data of the particle that spawned the object instance, for example to give variation to multiple instances of an object")
+DefNode(ShaderNode, SH_NODE_HAIR_INFO, 0, "HAIR_INFO", HairInfo, "Curves Info", "Retrieve hair curve information")
+DefNode(ShaderNode, SH_NODE_POINT_INFO, 0, "POINT_INFO", PointInfo, "Point Info", "Retrieve information about points in a point cloud")
+DefNode(ShaderNode, SH_NODE_VOLUME_INFO, 0, "VOLUME_INFO", VolumeInfo, "Volume Info", "Read volume data attributes from volume grids")
+DefNode(ShaderNode, SH_NODE_WIREFRAME, def_sh_tex_wireframe, "WIREFRAME", Wireframe, "Wireframe", "Retrieve the edges of an object as it appears to Cycles.\nNote: as meshes are triangulated before being processed by Cycles, topology will always appear triangulated")
+DefNode(ShaderNode, SH_NODE_WAVELENGTH, 0, "WAVELENGTH", Wavelength, "Wavelength", "Convert a wavelength value to an RGB value")
+DefNode(ShaderNode, SH_NODE_BLACKBODY, 0, "BLACKBODY", Blackbody, "Blackbody", "Convert a blackbody temperature to an RGB value")
+DefNode(ShaderNode, SH_NODE_BUMP, def_sh_bump, "BUMP", Bump, "Bump", "Generate a perturbed normal from a height texture for bump mapping. Typically used for faking highly detailed surfaces")
+DefNode(ShaderNode, SH_NODE_NORMAL_MAP, def_sh_normal_map, "NORMAL_MAP", NormalMap, "Normal Map", "Generate a perturbed normal from an RGB normal map image. Typically used for faking highly detailed surfaces")
+DefNode(ShaderNode, SH_NODE_TANGENT, def_sh_tangent, "TANGENT", Tangent, "Tangent", "Generate a tangent direction for the Anisotropic BSDF")
+DefNode(ShaderNode, SH_NODE_SCRIPT, def_sh_script, "SCRIPT", Script, "Script", "Generate an OSL shader from a file or text data-block.\nNote: OSL shaders are not supported on the GPU")
+DefNode(ShaderNode, SH_NODE_TEX_IMAGE, def_sh_tex_image, "TEX_IMAGE", TexImage, "Image Texture", "Sample an image file as a texture")
+DefNode(ShaderNode, SH_NODE_TEX_ENVIRONMENT, def_sh_tex_environment, "TEX_ENVIRONMENT", TexEnvironment, "Environment Texture","Sample an image file as an environment texture. Typically used to light the scene with the background node")
+DefNode(ShaderNode, SH_NODE_TEX_SKY, def_sh_tex_sky, "TEX_SKY", TexSky, "Sky Texture", "Generate a procedural sky texture")
+DefNode(ShaderNode, SH_NODE_TEX_GRADIENT, def_sh_tex_gradient, "TEX_GRADIENT", TexGradient, "Gradient Texture", "Generate interpolated color and intensity values based on the input vector")
+DefNode(ShaderNode, SH_NODE_TEX_NOISE, def_sh_tex_noise, "TEX_NOISE", TexNoise, "Noise Texture", "Generate fractal Perlin noise")
+DefNode(ShaderNode, SH_NODE_TEX_MAGIC, def_sh_tex_magic, "TEX_MAGIC", TexMagic, "Magic Texture", "Generate a psychedelic color texture")
+DefNode(ShaderNode, SH_NODE_TEX_WAVE, def_sh_tex_wave, "TEX_WAVE", TexWave, "Wave Texture", "Generate procedural bands or rings with noise")
+DefNode(ShaderNode, SH_NODE_TEX_MUSGRAVE, def_sh_tex_musgrave, "TEX_MUSGRAVE", TexMusgrave, "Musgrave Texture", "Generate fractal Perlin noise. Allows for greater control over how octaves are combined than the Noise Texture node")
+DefNode(ShaderNode, SH_NODE_TEX_VORONOI, def_sh_tex_voronoi, "TEX_VORONOI", TexVoronoi, "Voronoi Texture", "Generate Worley noise based on the distance to random points. Typically used to generate textures such as stones, water, or biological cells")
+DefNode(ShaderNode, SH_NODE_TEX_CHECKER, def_sh_tex_checker, "TEX_CHECKER", TexChecker, "Checker Texture", "Generate a checkerboard texture")
+DefNode(ShaderNode, SH_NODE_TEX_BRICK, def_sh_tex_brick, "TEX_BRICK", TexBrick, "Brick Texture", "Generate a procedural texture producing bricks")
+DefNode(ShaderNode, SH_NODE_TEX_POINTDENSITY, def_sh_tex_pointdensity,"TEX_POINTDENSITY", TexPointDensity, "Point Density", "Generate a volumetric point for each particle or vertex of another object")
+DefNode(ShaderNode, SH_NODE_TEX_COORD, def_sh_tex_coord, "TEX_COORD", TexCoord, "Texture Coordinate","Retrieve multiple types of texture coordinates.\nTypically used as inputs for texture nodes")
+DefNode(ShaderNode, SH_NODE_VECTOR_ROTATE, def_sh_vector_rotate, "VECTOR_ROTATE", VectorRotate, "Vector Rotate", "Rotate a vector around a pivot point (center)")
+DefNode(ShaderNode, SH_NODE_VECT_TRANSFORM, def_sh_vect_transform, "VECT_TRANSFORM", VectorTransform, "Vector Transform", "Convert a vector, point, or normal between world, camera, and object coordinate space")
+DefNode(ShaderNode, SH_NODE_SEPHSV_LEGACY, 0, "SEPHSV", SeparateHSV, "Separate HSV", "Split a color into its hue, saturation, and value channels")
+DefNode(ShaderNode, SH_NODE_COMBHSV_LEGACY, 0, "COMBHSV", CombineHSV, "Combine HSV", "Create a color from its hue, saturation, and value channels")
+DefNode(ShaderNode, SH_NODE_UVMAP, def_sh_uvmap, "UVMAP", UVMap, "UV Map", "Retrieve a UV map from the geometry, or the default fallback if none is specified")
+DefNode(ShaderNode, SH_NODE_VERTEX_COLOR, def_sh_vertex_color, "VERTEX_COLOR", VertexColor, "Color Attribute", "Retrieve a color attribute, or the default fallback if none is specified")
+DefNode(ShaderNode, SH_NODE_UVALONGSTROKE, def_sh_uvalongstroke, "UVALONGSTROKE", UVAlongStroke, "UV Along Stroke", "")
+DefNode(ShaderNode, SH_NODE_SEPXYZ, 0, "SEPXYZ", SeparateXYZ, "Separate XYZ", "Split a vector into its X, Y, and Z components")
+DefNode(ShaderNode, SH_NODE_COMBXYZ, 0, "COMBXYZ", CombineXYZ, "Combine XYZ", "Create a vector from X, Y, and Z components")
+DefNode(ShaderNode, SH_NODE_BEVEL, def_sh_bevel, "BEVEL", Bevel, "Bevel", "Generates normals with round corners.\nNote: only supported in Cycles, and may slow down renders")
+DefNode(ShaderNode, SH_NODE_DISPLACEMENT, def_sh_displacement, "DISPLACEMENT", Displacement, "Displacement", "Displace the surface along the surface normal")
+DefNode(ShaderNode, SH_NODE_VECTOR_DISPLACEMENT,def_sh_vector_displacement,"VECTOR_DISPLACEMENT",VectorDisplacement,"Vector Displacement","Displace the surface along an arbitrary direction")
+DefNode(ShaderNode, SH_NODE_TEX_IES, def_sh_tex_ies, "TEX_IES", TexIES, "IES Texture", "Used to match real world lights with IES files, which store the directional intensity distribution of light sources")
+DefNode(ShaderNode, SH_NODE_TEX_WHITE_NOISE, def_sh_tex_white_noise, "TEX_WHITE_NOISE", TexWhiteNoise, "White Noise", "Return a random value or color based on an input seed")
+DefNode(ShaderNode, SH_NODE_OUTPUT_AOV, def_sh_output_aov, "OUTPUT_AOV", OutputAOV, "AOV Output", "Arbitrary Output Variables.\nProvide custom render passes for arbitrary shader node outputs")
+DefNode(ShaderNode, SH_NODE_CURVE_FLOAT, def_float_curve, "CURVE_FLOAT", FloatCurve, "Float Curve", "Map an input float to a curve and outputs a float value")
+DefNode(ShaderNode, SH_NODE_COMBINE_COLOR, def_sh_combsep_color, "COMBINE_COLOR", CombineColor, "Combine Color", "Create a color from individual components using multiple models")
+DefNode(ShaderNode, SH_NODE_SEPARATE_COLOR, def_sh_combsep_color, "SEPARATE_COLOR", SeparateColor, "Separate Color", "Split a color into its individual components using multiple models")
+DefNode(ShaderNode, SH_NODE_MIX, def_sh_mix, "MIX", Mix, "Mix", "Mix values by a factor")
DefNode(CompositorNode, CMP_NODE_VIEWER, def_cmp_viewer, "VIEWER", Viewer, "Viewer", "" )
DefNode(CompositorNode, CMP_NODE_RGB, 0, "RGB", RGB, "RGB", "" )
@@ -260,8 +261,8 @@ DefNode(TextureNode, TEX_NODE_PROC+TEX_NOISE, 0, "TEX_NO
DefNode(TextureNode, TEX_NODE_PROC+TEX_STUCCI, 0, "TEX_STUCCI", TexStucci, "Stucci", "" )
DefNode(TextureNode, TEX_NODE_PROC+TEX_DISTNOISE, 0, "TEX_DISTNOISE", TexDistNoise, "Distorted Noise", "" )
-DefNode(FunctionNode, FN_NODE_ALIGN_EULER_TO_VECTOR, def_fn_align_euler_to_vector, "ALIGN_EULER_TO_VECTOR", AlignEulerToVector, "Align Euler To Vector", "")
-DefNode(FunctionNode, FN_NODE_BOOLEAN_MATH, def_boolean_math, "BOOLEAN_MATH", BooleanMath, "Boolean Math", "")
+DefNode(FunctionNode, FN_NODE_ALIGN_EULER_TO_VECTOR, def_fn_align_euler_to_vector, "ALIGN_EULER_TO_VECTOR", AlignEulerToVector, "Align Euler to Vector", "")
+DefNode(FunctionNode, FN_NODE_BOOLEAN_MATH, def_boolean_math, "BOOLEAN_MATH", BooleanMath, "Boolean Math", "")
DefNode(FunctionNode, FN_NODE_COMBINE_COLOR, def_fn_combsep_color, "COMBINE_COLOR", CombineColor, "Combine Color", "")
DefNode(FunctionNode, FN_NODE_COMPARE, def_compare, "COMPARE", Compare, "Compare", "")
DefNode(FunctionNode, FN_NODE_FLOAT_TO_INT, def_float_to_int, "FLOAT_TO_INT", FloatToInt, "Float to Integer", "")
@@ -279,125 +280,135 @@ DefNode(FunctionNode, FN_NODE_SLICE_STRING, 0, "SLICE_STRING", SliceString, "Sli
DefNode(FunctionNode, FN_NODE_STRING_LENGTH, 0, "STRING_LENGTH", StringLength, "String Length", "")
DefNode(FunctionNode, FN_NODE_VALUE_TO_STRING, 0, "VALUE_TO_STRING", ValueToString, "Value to String", "")
-DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_DOMAIN_SIZE, def_geo_attribute_domain_size, "ATTRIBUTE_DOMAIN_SIZE", AttributeDomainSize, "Domain Size", "")
-DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_STATISTIC, def_geo_attribute_statistic, "ATTRIBUTE_STATISTIC", AttributeStatistic, "Attribute Statistic", "")
-DefNode(GeometryNode, GEO_NODE_BOUNDING_BOX, 0, "BOUNDING_BOX", BoundBox, "Bounding Box", "")
-DefNode(GeometryNode, GEO_NODE_CAPTURE_ATTRIBUTE, def_geo_attribute_capture, "CAPTURE_ATTRIBUTE", CaptureAttribute, "Capture Attribute", "")
-DefNode(GeometryNode, GEO_NODE_COLLECTION_INFO, def_geo_collection_info, "COLLECTION_INFO", CollectionInfo, "Collection Info", "")
-DefNode(GeometryNode, GEO_NODE_CONVEX_HULL, 0, "CONVEX_HULL", ConvexHull, "Convex Hull", "")
-DefNode(GeometryNode, GEO_NODE_CURVE_ENDPOINT_SELECTION, 0, "CURVE_ENDPOINT_SELECTION", CurveEndpointSelection, "Endpoint Selection", "")
-DefNode(GeometryNode, GEO_NODE_CURVE_HANDLE_TYPE_SELECTION, def_geo_curve_handle_type_selection, "CURVE_HANDLE_TYPE_SELECTION", CurveHandleTypeSelection, "Handle Type Selection", "")
-DefNode(GeometryNode, GEO_NODE_CURVE_LENGTH, 0, "CURVE_LENGTH", CurveLength, "Curve Length", "")
-DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_ARC, def_geo_curve_primitive_arc, "CURVE_PRIMITIVE_ARC", CurveArc, "Arc", "")
-DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_BEZIER_SEGMENT, def_geo_curve_primitive_bezier_segment, "CURVE_PRIMITIVE_BEZIER_SEGMENT", CurvePrimitiveBezierSegment, "Bezier Segment", "")
-DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_CIRCLE, def_geo_curve_primitive_circle, "CURVE_PRIMITIVE_CIRCLE", CurvePrimitiveCircle, "Curve Circle", "")
-DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_LINE, def_geo_curve_primitive_line, "CURVE_PRIMITIVE_LINE", CurvePrimitiveLine, "Curve Line", "")
-DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_QUADRATIC_BEZIER, 0, "CURVE_PRIMITIVE_QUADRATIC_BEZIER", CurveQuadraticBezier, "Quadratic Bezier", "")
-DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_QUADRILATERAL, def_geo_curve_primitive_quadrilateral, "CURVE_PRIMITIVE_QUADRILATERAL", CurvePrimitiveQuadrilateral, "Quadrilateral", "")
-DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_SPIRAL, 0, "CURVE_PRIMITIVE_SPIRAL", CurveSpiral, "Curve Spiral", "")
-DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_STAR, 0, "CURVE_PRIMITIVE_STAR", CurveStar, "Star", "")
-DefNode(GeometryNode, GEO_NODE_CURVE_SET_HANDLE_TYPE, def_geo_curve_set_handle_type, "CURVE_SET_HANDLES", CurveSetHandles, "Set Handle Type", "")
-DefNode(GeometryNode, GEO_NODE_CURVE_SPLINE_PARAMETER, 0, "SPLINE_PARAMETER", SplineParameter, "Spline Parameter", "")
-DefNode(GeometryNode, GEO_NODE_CURVE_SPLINE_TYPE, def_geo_curve_spline_type, "CURVE_SPLINE_TYPE", CurveSplineType, "Set Spline Type", "")
-DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, "Curve to Mesh", "")
-DefNode(GeometryNode, GEO_NODE_CURVE_TO_POINTS, def_geo_curve_to_points, "CURVE_TO_POINTS", CurveToPoints, "Curve to Points", "")
-DefNode(GeometryNode, GEO_NODE_DELETE_GEOMETRY, def_geo_delete_geometry, "DELETE_GEOMETRY", DeleteGeometry, "Delete Geometry", "")
-DefNode(GeometryNode, GEO_NODE_DUPLICATE_ELEMENTS, def_geo_duplicate_elements, "DUPLICATE_ELEMENTS", DuplicateElements, "Duplicate Elements", "")
-DefNode(GeometryNode, GEO_NODE_DISTRIBUTE_POINTS_ON_FACES, def_geo_distribute_points_on_faces, "DISTRIBUTE_POINTS_ON_FACES", DistributePointsOnFaces, "Distribute Points on Faces", "")
-DefNode(GeometryNode, GEO_NODE_ACCUMULATE_FIELD, def_geo_accumulate_field, "ACCUMULATE_FIELD", AccumulateField, "Accumulate Field", "")
-DefNode(GeometryNode, GEO_NODE_DUAL_MESH, 0, "DUAL_MESH", DualMesh, "Dual Mesh", "")
-DefNode(GeometryNode, GEO_NODE_EXTRUDE_MESH, def_geo_extrude_mesh, "EXTRUDE_MESH", ExtrudeMesh, "Extrude Mesh", "")
-DefNode(GeometryNode, GEO_NODE_FIELD_AT_INDEX, def_geo_field_at_index, "FIELD_AT_INDEX", FieldAtIndex, "Field at Index", "")
-DefNode(GeometryNode, GEO_NODE_FILL_CURVE, def_geo_curve_fill, "FILL_CURVE", FillCurve, "Fill Curve", "")
-DefNode(GeometryNode, GEO_NODE_FILLET_CURVE, def_geo_curve_fillet, "FILLET_CURVE", FilletCurve, "Fillet Curve", "")
-DefNode(GeometryNode, GEO_NODE_FLIP_FACES, 0, "FLIP_FACES", FlipFaces, "Flip Faces", "")
-DefNode(GeometryNode, GEO_NODE_GEOMETRY_TO_INSTANCE, 0, "GEOMETRY_TO_INSTANCE", GeometryToInstance, "Geometry to Instance", "")
-DefNode(GeometryNode, GEO_NODE_IMAGE_TEXTURE, def_geo_image_texture, "IMAGE_TEXTURE", ImageTexture, "Image Texture", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_NAMED_ATTRIBUTE, def_geo_input_named_attribute, "INPUT_ATTRIBUTE", InputNamedAttribute, "Named Attribute", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_CURVE_HANDLES, 0, "INPUT_CURVE_HANDLES", InputCurveHandlePositions, "Curve Handle Positions", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_CURVE_TILT, 0, "INPUT_CURVE_TILT", InputCurveTilt, "Curve Tilt", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_ID, 0, "INPUT_ID", InputID, "ID", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_INDEX, 0, "INDEX", InputIndex, "Index", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_INSTANCE_ROTATION, 0, "INPUT_INSTANCE_ROTATION", InputInstanceRotation, "Instance Rotation", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_INSTANCE_SCALE, 0, "INPUT_INSTANCE_SCALE", InputInstanceScale, "Instance Scale", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL_INDEX, 0, "INPUT_MATERIAL_INDEX", InputMaterialIndex, "Material Index", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL, def_geo_input_material, "INPUT_MATERIAL", InputMaterial, "Material", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_MESH_EDGE_ANGLE, 0, "MESH_EDGE_ANGLE", InputMeshEdgeAngle, "Edge Angle", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_MESH_EDGE_NEIGHBORS, 0, "MESH_EDGE_NEIGHBORS", InputMeshEdgeNeighbors, "Edge Neighbors", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_MESH_EDGE_VERTICES, 0, "MESH_EDGE_VERTICES", InputMeshEdgeVertices, "Edge Vertices", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_MESH_FACE_AREA, 0, "MESH_FACE_AREA", InputMeshFaceArea, "Face Area", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_MESH_FACE_IS_PLANAR, 0, "MESH_FACE_IS_PLANAR", InputMeshFaceIsPlanar, "Face is Planar", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_MESH_FACE_NEIGHBORS, 0, "MESH_FACE_NEIGHBORS", InputMeshFaceNeighbors, "Face Neighbors", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_MESH_ISLAND, 0, "MESH_ISLAND", InputMeshIsland, "Mesh Island", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_MESH_VERTEX_NEIGHBORS, 0, "MESH_VERTEX_NEIGHBORS", InputMeshVertexNeighbors, "Vertex Neighbors", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_NORMAL, 0, "INPUT_NORMAL", InputNormal, "Normal", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_POSITION, 0, "POSITION", InputPosition, "Position", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_RADIUS, 0, "INPUT_RADIUS", InputRadius, "Radius", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_SCENE_TIME, 0, "INPUT_SCENE_TIME", InputSceneTime, "Scene Time", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_SHADE_SMOOTH, 0, "INPUT_SHADE_SMOOTH", InputShadeSmooth, "Is Shade Smooth", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_SPLINE_CYCLIC, 0, "INPUT_SPLINE_CYCLIC", InputSplineCyclic, "Is Spline Cyclic", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_SPLINE_LENGTH, 0, "SPLINE_LENGTH", SplineLength, "Spline Length", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_SPLINE_RESOLUTION, 0, "INPUT_SPLINE_RESOLUTION", InputSplineResolution, "Spline Resolution", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_TANGENT, 0, "INPUT_TANGENT", InputTangent, "Curve Tangent", "")
-DefNode(GeometryNode, GEO_NODE_INSTANCE_ON_POINTS, 0, "INSTANCE_ON_POINTS", InstanceOnPoints, "Instance on Points", "")
-DefNode(GeometryNode, GEO_NODE_INSTANCES_TO_POINTS, 0, "INSTANCES_TO_POINTS", InstancesToPoints, "Instances to Points", "")
-DefNode(GeometryNode, GEO_NODE_IS_VIEWPORT, 0, "IS_VIEWPORT", IsViewport, "Is Viewport", "")
-DefNode(GeometryNode, GEO_NODE_JOIN_GEOMETRY, 0, "JOIN_GEOMETRY", JoinGeometry, "Join Geometry", "")
-DefNode(GeometryNode, GEO_NODE_MATERIAL_SELECTION, 0, "MATERIAL_SELECTION", MaterialSelection, "Material Selection", "")
-DefNode(GeometryNode, GEO_NODE_MERGE_BY_DISTANCE, def_geo_merge_by_distance, "MERGE_BY_DISTANCE", MergeByDistance, "Merge by Distance", "")
-DefNode(GeometryNode, GEO_NODE_MESH_BOOLEAN, def_geo_boolean, "MESH_BOOLEAN", MeshBoolean, "Mesh Boolean", "")
-DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CIRCLE, def_geo_mesh_circle, "MESH_PRIMITIVE_CIRCLE", MeshCircle, "Mesh Circle", "")
-DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CONE, def_geo_mesh_cone, "MESH_PRIMITIVE_CONE", MeshCone, "Cone", "")
-DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CUBE, 0, "MESH_PRIMITIVE_CUBE", MeshCube, "Cube", "")
-DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CYLINDER, def_geo_mesh_cylinder, "MESH_PRIMITIVE_CYLINDER", MeshCylinder, "Cylinder", "")
-DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_GRID, 0, "MESH_PRIMITIVE_GRID", MeshGrid, "Grid", "")
-DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE, 0, "MESH_PRIMITIVE_ICO_SPHERE", MeshIcoSphere, "Ico Sphere", "")
-DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_LINE, def_geo_mesh_line, "MESH_PRIMITIVE_LINE", MeshLine, "Mesh Line", "")
-DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, 0, "MESH_PRIMITIVE_UV_SPHERE", MeshUVSphere, "UV Sphere", "")
-DefNode(GeometryNode, GEO_NODE_MESH_TO_CURVE, 0, "MESH_TO_CURVE", MeshToCurve, "Mesh to Curve", "")
-DefNode(GeometryNode, GEO_NODE_MESH_TO_POINTS, def_geo_mesh_to_points, "MESH_TO_POINTS", MeshToPoints, "Mesh to Points", "")
-DefNode(GeometryNode, GEO_NODE_OBJECT_INFO, def_geo_object_info, "OBJECT_INFO", ObjectInfo, "Object Info", "")
-DefNode(GeometryNode, GEO_NODE_POINTS_TO_VERTICES, 0, "POINTS_TO_VERTICES", PointsToVertices, "Points to Vertices", "")
-DefNode(GeometryNode, GEO_NODE_POINTS_TO_VOLUME, def_geo_points_to_volume, "POINTS_TO_VOLUME", PointsToVolume, "Points to Volume", "")
-DefNode(GeometryNode, GEO_NODE_PROXIMITY, def_geo_proximity, "PROXIMITY", Proximity, "Geometry Proximity", "")
-DefNode(GeometryNode, GEO_NODE_RAYCAST, def_geo_raycast, "RAYCAST", Raycast, "Raycast", "")
-DefNode(GeometryNode, GEO_NODE_REMOVE_ATTRIBUTE, 0, "REMOVE_ATTRIBUTE", RemoveAttribute, "Remove Named Attribute", "")
-DefNode(GeometryNode, GEO_NODE_REALIZE_INSTANCES, def_geo_realize_instances, "REALIZE_INSTANCES", RealizeInstances, "Realize Instances", "")
-DefNode(GeometryNode, GEO_NODE_REPLACE_MATERIAL, 0, "REPLACE_MATERIAL", ReplaceMaterial, "Replace Material", "")
-DefNode(GeometryNode, GEO_NODE_RESAMPLE_CURVE, def_geo_curve_resample, "RESAMPLE_CURVE", ResampleCurve, "Resample Curve", "")
-DefNode(GeometryNode, GEO_NODE_REVERSE_CURVE, 0, "REVERSE_CURVE", ReverseCurve, "Reverse Curve", "")
-DefNode(GeometryNode, GEO_NODE_ROTATE_INSTANCES, 0, "ROTATE_INSTANCES", RotateInstances, "Rotate Instances", "")
-DefNode(GeometryNode, GEO_NODE_SAMPLE_CURVE, def_geo_curve_sample, "SAMPLE_CURVE", SampleCurve, "Sample Curve", "")
-DefNode(GeometryNode, GEO_NODE_SCALE_ELEMENTS, def_geo_scale_elements, "SCALE_ELEMENTS", ScaleElements, "Scale Elements", "")
-DefNode(GeometryNode, GEO_NODE_SCALE_INSTANCES, 0, "SCALE_INSTANCES", ScaleInstances, "Scale Instances", "")
-DefNode(GeometryNode, GEO_NODE_SEPARATE_COMPONENTS, 0, "SEPARATE_COMPONENTS", SeparateComponents, "Separate Components", "")
-DefNode(GeometryNode, GEO_NODE_SEPARATE_GEOMETRY, def_geo_separate_geometry, "SEPARATE_GEOMETRY", SeparateGeometry, "Separate Geometry", "")
-DefNode(GeometryNode, GEO_NODE_SET_CURVE_HANDLES, def_geo_curve_set_handle_positions, "SET_CURVE_HANDLES", SetCurveHandlePositions, "Set Handle Positions", "")
-DefNode(GeometryNode, GEO_NODE_SET_CURVE_RADIUS, 0, "SET_CURVE_RADIUS", SetCurveRadius, "Set Curve Radius", "")
-DefNode(GeometryNode, GEO_NODE_SET_CURVE_TILT, 0, "SET_CURVE_TILT", SetCurveTilt, "Set Curve Tilt", "")
-DefNode(GeometryNode, GEO_NODE_SET_ID, 0, "SET_ID", SetID, "Set ID", "")
-DefNode(GeometryNode, GEO_NODE_SET_MATERIAL_INDEX, 0, "SET_MATERIAL_INDEX", SetMaterialIndex, "Set Material Index", "")
-DefNode(GeometryNode, GEO_NODE_SET_MATERIAL, 0, "SET_MATERIAL", SetMaterial, "Set Material", "")
-DefNode(GeometryNode, GEO_NODE_SET_POINT_RADIUS, 0, "SET_POINT_RADIUS", SetPointRadius, "Set Point Radius", "")
-DefNode(GeometryNode, GEO_NODE_SET_POSITION, 0, "SET_POSITION", SetPosition, "Set Position", "")
-DefNode(GeometryNode, GEO_NODE_SET_SHADE_SMOOTH, 0, "SET_SHADE_SMOOTH", SetShadeSmooth, "Set Shade Smooth", "")
-DefNode(GeometryNode, GEO_NODE_SET_SPLINE_CYCLIC, 0, "SET_SPLINE_CYCLIC", SetSplineCyclic, "Set Spline Cyclic", "")
-DefNode(GeometryNode, GEO_NODE_SET_SPLINE_RESOLUTION, 0, "SET_SPLINE_RESOLUTION", SetSplineResolution, "Set Spline Resolution", "")
-DefNode(GeometryNode, GEO_NODE_SPLIT_EDGES, 0, "SPLIT_EDGES", SplitEdges, "Split Edges", "")
-DefNode(GeometryNode, GEO_NODE_STORE_NAMED_ATTRIBUTE, def_geo_store_named_attribute, "STORE_NAMED_ATTRIBUTE", StoreNamedAttribute, "Store Named Attribute", "")
-DefNode(GeometryNode, GEO_NODE_STRING_JOIN, 0, "STRING_JOIN", StringJoin, "Join Strings", "")
-DefNode(GeometryNode, GEO_NODE_STRING_TO_CURVES, def_geo_string_to_curves, "STRING_TO_CURVES", StringToCurves, "String to Curves", "")
-DefNode(GeometryNode, GEO_NODE_SUBDIVIDE_CURVE, 0, "SUBDIVIDE_CURVE", SubdivideCurve, "Subdivide Curve", "")
-DefNode(GeometryNode, GEO_NODE_SUBDIVIDE_MESH, 0, "SUBDIVIDE_MESH", SubdivideMesh, "Subdivide Mesh", "")
-DefNode(GeometryNode, GEO_NODE_SUBDIVISION_SURFACE, def_geo_subdivision_surface, "SUBDIVISION_SURFACE", SubdivisionSurface, "Subdivision Surface", "")
-DefNode(GeometryNode, GEO_NODE_SWITCH, def_geo_switch, "SWITCH", Switch, "Switch", "")
-DefNode(GeometryNode, GEO_NODE_TRANSFER_ATTRIBUTE, def_geo_transfer_attribute, "ATTRIBUTE_TRANSFER", AttributeTransfer, "Transfer Attribute", "")
-DefNode(GeometryNode, GEO_NODE_TRANSFORM, 0, "TRANSFORM", Transform, "Transform", "")
-DefNode(GeometryNode, GEO_NODE_TRANSLATE_INSTANCES, 0, "TRANSLATE_INSTANCES", TranslateInstances, "Translate Instances", "")
-DefNode(GeometryNode, GEO_NODE_TRIANGULATE, def_geo_triangulate, "TRIANGULATE", Triangulate, "Triangulate", "")
-DefNode(GeometryNode, GEO_NODE_TRIM_CURVE, def_geo_curve_trim, "TRIM_CURVE", TrimCurve, "Trim Curve", "")
-DefNode(GeometryNode, GEO_NODE_VIEWER, def_geo_viewer, "VIEWER", Viewer, "Viewer", "")
-DefNode(GeometryNode, GEO_NODE_VOLUME_TO_MESH, def_geo_volume_to_mesh, "VOLUME_TO_MESH", VolumeToMesh, "Volume to Mesh", "")
+DefNode(GeometryNode, GEO_NODE_ACCUMULATE_FIELD, def_geo_accumulate_field, "ACCUMULATE_FIELD", AccumulateField, "Accumulate Field", "Add the values of an evaluated field together and output the running total for each element")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_DOMAIN_SIZE, def_geo_attribute_domain_size, "ATTRIBUTE_DOMAIN_SIZE", AttributeDomainSize, "Domain Size", "Retrieve the number of elements in a geometry for each attribute domain")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_STATISTIC, def_geo_attribute_statistic, "ATTRIBUTE_STATISTIC",AttributeStatistic, "Attribute Statistic","Calculate statistics about a data set from a field evaluated on a geometry")
+DefNode(GeometryNode, GEO_NODE_BOUNDING_BOX, 0, "BOUNDING_BOX", BoundBox, "Bounding Box", "Calculate the limits of a geometry's positions and generate a box mesh with those dimensions")
+DefNode(GeometryNode, GEO_NODE_CAPTURE_ATTRIBUTE, def_geo_attribute_capture,"CAPTURE_ATTRIBUTE", CaptureAttribute, "Capture Attribute", "Store the result of a field on a geometry and output the data as a node socket. Allows remembering or interpolating data as the geometry changes, such as positions before deformation")
+DefNode(GeometryNode, GEO_NODE_COLLECTION_INFO, def_geo_collection_info, "COLLECTION_INFO", CollectionInfo, "Collection Info", "Retrieve geometry from a collection")
+DefNode(GeometryNode, GEO_NODE_CONVEX_HULL, 0, "CONVEX_HULL", ConvexHull, "Convex Hull", "Create a mesh that encloses all points in the input geometry with the smallest number of points")
+DefNode(GeometryNode, GEO_NODE_CURVE_ENDPOINT_SELECTION, 0, "CURVE_ENDPOINT_SELECTION", CurveEndpointSelection, "Endpoint Selection", "Provide a selection for an arbitrary number of endpoints in each spline")
+DefNode(GeometryNode, GEO_NODE_CURVE_HANDLE_TYPE_SELECTION, def_geo_curve_handle_type_selection, "CURVE_HANDLE_TYPE_SELECTION", CurveHandleTypeSelection, "Handle Type Selection", "Provide a selection based on the handle types of Bézier control points")
+DefNode(GeometryNode, GEO_NODE_CURVE_LENGTH, 0, "CURVE_LENGTH", CurveLength, "Curve Length", "Retrieve the length of all splines added together")
+DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_ARC, def_geo_curve_primitive_arc, "CURVE_PRIMITIVE_ARC",CurveArc, "Arc", "Generate a poly spline arc")
+DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_BEZIER_SEGMENT, def_geo_curve_primitive_bezier_segment, "CURVE_PRIMITIVE_BEZIER_SEGMENT", CurvePrimitiveBezierSegment, "Bezier Segment", "Generate a 2D Bézier spline from the given control points and handles")
+DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_CIRCLE,def_geo_curve_primitive_circle, "CURVE_PRIMITIVE_CIRCLE", CurvePrimitiveCircle, "Curve Circle", "Generate a poly spline circle")
+DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_LINE, def_geo_curve_primitive_line, "CURVE_PRIMITIVE_LINE", CurvePrimitiveLine, "Curve Line", "Generate a poly spline line with two points")
+DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_QUADRATIC_BEZIER, 0, "CURVE_PRIMITIVE_QUADRATIC_BEZIER", CurveQuadraticBezier, "Quadratic Bezier", "Generate a poly spline in a parabola shape with control points positions")
+DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_QUADRILATERAL, def_geo_curve_primitive_quadrilateral, "CURVE_PRIMITIVE_QUADRILATERAL", CurvePrimitiveQuadrilateral, "Quadrilateral", "Generate a polygon with four points")
+DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_SPIRAL,0, "CURVE_PRIMITIVE_SPIRAL", CurveSpiral, "Curve Spiral", "Generate a poly spline in a spiral shape")
+DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_STAR, 0, "CURVE_PRIMITIVE_STAR", CurveStar, "Star", "Generate a poly spline in a star pattern by connecting alternating points of two circles")
+DefNode(GeometryNode, GEO_NODE_CURVE_SET_HANDLE_TYPE, def_geo_curve_set_handle_type, "CURVE_SET_HANDLES", CurveSetHandles, "Set Handle Type", "Set the handle type for the control points of a Bézier curve")
+DefNode(GeometryNode, GEO_NODE_CURVE_SPLINE_PARAMETER,0, "SPLINE_PARAMETER", SplineParameter, "Spline Parameter", "Retrieve how far along each spline a control point is")
+DefNode(GeometryNode, GEO_NODE_CURVE_SPLINE_TYPE, def_geo_curve_spline_type,"CURVE_SPLINE_TYPE", CurveSplineType, "Set Spline Type", "Change the type of curves")
+DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, "Curve to Mesh", "Convert curves into a mesh, optionally with a custom profile shape defined by curves")
+DefNode(GeometryNode, GEO_NODE_CURVE_TO_POINTS, def_geo_curve_to_points, "CURVE_TO_POINTS", CurveToPoints, "Curve to Points", "Generate a point cloud by sampling positions along curves")
+DefNode(GeometryNode, GEO_NODE_DEFORM_CURVES_ON_SURFACE, 0, "DEFORM_CURVES_ON_SURFACE", DeformCurvesOnSurface, "Deform Curves on Surface", "Translate and rotate curves based on changes between the object's original and evaluated surface mesh")
+DefNode(GeometryNode, GEO_NODE_DELETE_GEOMETRY, def_geo_delete_geometry, "DELETE_GEOMETRY", DeleteGeometry, "Delete Geometry", "Remove selected elements of a geometry")
+DefNode(GeometryNode, GEO_NODE_DISTRIBUTE_POINTS_ON_FACES, def_geo_distribute_points_on_faces, "DISTRIBUTE_POINTS_ON_FACES", DistributePointsOnFaces, "Distribute Points on Faces", "Generate points spread out on the surface of a mesh")
+DefNode(GeometryNode, GEO_NODE_DUAL_MESH, 0, "DUAL_MESH", DualMesh, "Dual Mesh", "Convert Faces into vertices and vertices into faces")
+DefNode(GeometryNode, GEO_NODE_DUPLICATE_ELEMENTS, def_geo_duplicate_elements, "DUPLICATE_ELEMENTS", DuplicateElements, "Duplicate Elements", "Generate an arbitrary number copies of each selected input element")
+DefNode(GeometryNode, GEO_NODE_EDGE_PATHS_TO_CURVES, 0, "EDGE_PATHS_TO_CURVES", EdgePathsToCurves, "Edge Paths to Curves", "")
+DefNode(GeometryNode, GEO_NODE_EDGE_PATHS_TO_SELECTION, 0, "EDGE_PATHS_TO_SELECTION", EdgePathsToSelection, "Edge Paths to Selection", "")
+DefNode(GeometryNode, GEO_NODE_EXTRUDE_MESH, def_geo_extrude_mesh, "EXTRUDE_MESH", ExtrudeMesh, "Extrude Mesh", "Generate new vertices, edges, or faces from selected elements and move them based on an offset while keeping them connected by their boundary")
+DefNode(GeometryNode, GEO_NODE_FIELD_AT_INDEX, def_geo_field_at_index, "FIELD_AT_INDEX", FieldAtIndex, "Field at Index", "Retrieve data of other elements in the context's geometry")
+DefNode(GeometryNode, GEO_NODE_FILL_CURVE, def_geo_curve_fill, "FILL_CURVE", FillCurve, "Fill Curve", "Generate a mesh on the XY plane with faces on the inside of input curves")
+DefNode(GeometryNode, GEO_NODE_FILLET_CURVE, def_geo_curve_fillet, "FILLET_CURVE", FilletCurve, "Fillet Curve", "Round corners by generating circular arcs on each control point")
+DefNode(GeometryNode, GEO_NODE_FLIP_FACES, 0, "FLIP_FACES", FlipFaces, "Flip Faces", "Reverse the order of the vertices and edges of selected faces, flipping their normal direction")
+DefNode(GeometryNode, GEO_NODE_GEOMETRY_TO_INSTANCE, 0, "GEOMETRY_TO_INSTANCE", GeometryToInstance, "Geometry to Instance", "Convert each input geometry into an instance, which can be much faster than the Join Geometry node when the inputs are large")
+DefNode(GeometryNode, GEO_NODE_IMAGE_TEXTURE, def_geo_image_texture, "IMAGE_TEXTURE", ImageTexture, "Image Texture", "Sample values from an image texture")
+DefNode(GeometryNode, GEO_NODE_INPUT_CURVE_HANDLES, 0, "INPUT_CURVE_HANDLES",InputCurveHandlePositions,"Curve Handle Positions", "Retrieve the position of each Bézier control point's handles")
+DefNode(GeometryNode, GEO_NODE_INPUT_CURVE_TILT, 0, "INPUT_CURVE_TILT", InputCurveTilt, "Curve Tilt", "Retrieve the angle at each control point used to twist the curve's normal around its tangent")
+DefNode(GeometryNode, GEO_NODE_INPUT_ID, 0, "INPUT_ID", InputID, "ID", "Retrieve a stable random identifier value from the \"id\" attribute on the point domain, or the index if the attribute does not exist")
+DefNode(GeometryNode, GEO_NODE_INPUT_INDEX, 0, "INDEX", InputIndex, "Index", "Retrieve an integer value indicating the position of each element in the list, starting at zero")
+DefNode(GeometryNode, GEO_NODE_INPUT_INSTANCE_ROTATION, 0, "INPUT_INSTANCE_ROTATION", InputInstanceRotation, "Instance Rotation", "Retrieve the rotation of each instance in the geometry")
+DefNode(GeometryNode, GEO_NODE_INPUT_INSTANCE_SCALE, 0, "INPUT_INSTANCE_SCALE", InputInstanceScale, "Instance Scale", "Retrieve the scale of each instance in the geometry")
+DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL_INDEX, 0, "INPUT_MATERIAL_INDEX", InputMaterialIndex, "Material Index", "Retrieve the index of the material used for each element in the geometry's list of materials")
+DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL, def_geo_input_material, "INPUT_MATERIAL", InputMaterial, "Material", "Output a single material")
+DefNode(GeometryNode, GEO_NODE_INPUT_MESH_EDGE_ANGLE, 0, "MESH_EDGE_ANGLE", InputMeshEdgeAngle, "Edge Angle", "Calculate the surface area of each face in a mesh")
+DefNode(GeometryNode, GEO_NODE_INPUT_MESH_EDGE_NEIGHBORS, 0, "MESH_EDGE_NEIGHBORS",InputMeshEdgeNeighbors, "Edge Neighbors", "Retrieve the number of faces that use each edge as one of their sides")
+DefNode(GeometryNode, GEO_NODE_INPUT_MESH_EDGE_VERTICES, 0, "MESH_EDGE_VERTICES", InputMeshEdgeVertices, "Edge Vertices", "Retrieve topology information relating to each edge of a mesh")
+DefNode(GeometryNode, GEO_NODE_INPUT_MESH_FACE_AREA, 0, "MESH_FACE_AREA", InputMeshFaceArea, "Face Area", "Calculate the surface area of a mesh's faces")
+DefNode(GeometryNode, GEO_NODE_INPUT_MESH_FACE_IS_PLANAR, 0, "MESH_FACE_IS_PLANAR",InputMeshFaceIsPlanar, "Is Face Planar", "Retrieve whether all triangles in a face are on the same plane, i.e. whether have the same normal")
+DefNode(GeometryNode, GEO_NODE_INPUT_MESH_FACE_NEIGHBORS, 0, "MESH_FACE_NEIGHBORS",InputMeshFaceNeighbors, "Face Neighbors", "Retrieve topology information relating to each face of a mesh")
+DefNode(GeometryNode, GEO_NODE_INPUT_MESH_ISLAND, 0, "MESH_ISLAND", InputMeshIsland, "Mesh Island", "Retrieve information about separate connected regions in a mesh")
+DefNode(GeometryNode, GEO_NODE_INPUT_MESH_VERTEX_NEIGHBORS, 0, "MESH_VERTEX_NEIGHBORS", InputMeshVertexNeighbors, "Vertex Neighbors", "Retrieve topology information relating to each vertex of a mesh")
+DefNode(GeometryNode, GEO_NODE_INPUT_NAMED_ATTRIBUTE, def_geo_input_named_attribute, "INPUT_ATTRIBUTE", InputNamedAttribute, "Named Attribute", "Retrieve the data of a specified attribute")
+DefNode(GeometryNode, GEO_NODE_INPUT_NORMAL, 0, "INPUT_NORMAL", InputNormal, "Normal", "Retrieve a unit length vector indicating the direction pointing away from the geometry at each element")
+DefNode(GeometryNode, GEO_NODE_INPUT_POSITION, 0, "POSITION", InputPosition, "Position", "Retrieve a vector indicating the location of each element")
+DefNode(GeometryNode, GEO_NODE_INPUT_RADIUS, 0, "INPUT_RADIUS", InputRadius, "Radius", "Retrieve the radius at each point on curve or point cloud geometry")
+DefNode(GeometryNode, GEO_NODE_INPUT_SCENE_TIME, 0, "INPUT_SCENE_TIME", InputSceneTime, "Scene Time", "Retrieve the current time in the scene's animation in units of seconds or frames")
+DefNode(GeometryNode, GEO_NODE_INPUT_SHADE_SMOOTH, 0, "INPUT_SHADE_SMOOTH", InputShadeSmooth, "Is Shade Smooth", "Retrieve whether each face is marked for smooth shading")
+DefNode(GeometryNode, GEO_NODE_INPUT_SHORTEST_EDGE_PATHS, 0, "SHORTEST_EDGE_PATHS", InputShortestEdgePaths, "Shortest Edge Paths", "")
+DefNode(GeometryNode, GEO_NODE_INPUT_SPLINE_CYCLIC, 0, "INPUT_SPLINE_CYCLIC",InputSplineCyclic, "Is Spline Cyclic", "Retrieve whether each spline endpoint connects to the beginning")
+DefNode(GeometryNode, GEO_NODE_INPUT_SPLINE_LENGTH, 0, "SPLINE_LENGTH", SplineLength, "Spline Length", "Retrieve the total length of each spline, as a distance or as a number of points")
+DefNode(GeometryNode, GEO_NODE_INPUT_SPLINE_RESOLUTION, 0, "INPUT_SPLINE_RESOLUTION", InputSplineResolution, "Spline Resolution", "Retrieve the number of evaluated points that will be generated for every control point on curves")
+DefNode(GeometryNode, GEO_NODE_INPUT_TANGENT, 0, "INPUT_TANGENT", InputTangent, "Curve Tangent", "Retrieve the direction of curves at each control point")
+DefNode(GeometryNode, GEO_NODE_INSTANCE_ON_POINTS, 0, "INSTANCE_ON_POINTS", InstanceOnPoints, "Instance on Points", "Generate a reference to geometry at each of the input points, without duplicating its underlying data")
+DefNode(GeometryNode, GEO_NODE_INSTANCES_TO_POINTS, 0, "INSTANCES_TO_POINTS",InstancesToPoints, "Instances to Points","Generate points at the origins of instances.\nNote: Nested instances are not affected by this node")
+DefNode(GeometryNode, GEO_NODE_INTERPOLATE_DOMAIN, def_geo_interpolate_domain, "FIELD_ON_DOMAIN", FieldOnDomain, "Interpolate Domain", "Retrieve values from a field on a different domain besides the domain from the context")
+DefNode(GeometryNode, GEO_NODE_IS_VIEWPORT, 0, "IS_VIEWPORT", IsViewport, "Is Viewport", "Retrieve whether the nodes are being evaluated for the viewport rather than the final render")
+DefNode(GeometryNode, GEO_NODE_JOIN_GEOMETRY, 0, "JOIN_GEOMETRY", JoinGeometry, "Join Geometry", "Merge separately generated geometries into a single one")
+DefNode(GeometryNode, GEO_NODE_MATERIAL_SELECTION, 0, "MATERIAL_SELECTION", MaterialSelection, "Material Selection", "Provide a selection of faces that use the specified material")
+DefNode(GeometryNode, GEO_NODE_MERGE_BY_DISTANCE, def_geo_merge_by_distance,"MERGE_BY_DISTANCE", MergeByDistance, "Merge by Distance", "Merge vertices or points within a given distance")
+DefNode(GeometryNode, GEO_NODE_MESH_BOOLEAN, def_geo_boolean, "MESH_BOOLEAN", MeshBoolean, "Mesh Boolean", "Cut, subtract, or join multiple mesh inputs")
+DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CIRCLE, def_geo_mesh_circle, "MESH_PRIMITIVE_CIRCLE", MeshCircle, "Mesh Circle", "Generate a circular ring of edges")
+DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CONE, def_geo_mesh_cone, "MESH_PRIMITIVE_CONE",MeshCone, "Cone", "Generate a cone mesh")
+DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CUBE, 0, "MESH_PRIMITIVE_CUBE",MeshCube, "Cube", "Generate a cuboid mesh with variable side lengths and subdivisions")
+DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CYLINDER, def_geo_mesh_cylinder, "MESH_PRIMITIVE_CYLINDER", MeshCylinder, "Cylinder", "Generate a cylinder mesh")
+DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_GRID, 0, "MESH_PRIMITIVE_GRID",MeshGrid, "Grid", "Generate a planar mesh on the XY plane")
+DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE, 0, "MESH_PRIMITIVE_ICO_SPHERE", MeshIcoSphere, "Ico Sphere", "Generate a spherical mesh that consists of equally sized triangles")
+DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_LINE, def_geo_mesh_line, "MESH_PRIMITIVE_LINE",MeshLine, "Mesh Line", "Generate vertices in a line and connect them with edges")
+DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, 0, "MESH_PRIMITIVE_UV_SPHERE", MeshUVSphere, "UV Sphere", "Generate a spherical mesh with quads, except for triangles at the top and bottom")
+DefNode(GeometryNode, GEO_NODE_MESH_TO_CURVE, 0, "MESH_TO_CURVE", MeshToCurve, "Mesh to Curve", "Generate a curve from a mesh")
+DefNode(GeometryNode, GEO_NODE_MESH_TO_POINTS, def_geo_mesh_to_points, "MESH_TO_POINTS", MeshToPoints, "Mesh to Points", "Generate a point cloud from a mesh's vertices")
+DefNode(GeometryNode, GEO_NODE_MESH_TO_VOLUME, def_geo_mesh_to_volume, "MESH_TO_VOLUME", MeshToVolume, "Mesh to Volume", "Create a fog volume with the shape of the input mesh's surface")
+DefNode(GeometryNode, GEO_NODE_OBJECT_INFO, def_geo_object_info, "OBJECT_INFO", ObjectInfo, "Object Info", "Retrieve information from an object")
+DefNode(GeometryNode, GEO_NODE_POINTS_TO_VERTICES, 0, "POINTS_TO_VERTICES", PointsToVertices, "Points to Vertices", "Generate a mesh vertex for each point cloud point")
+DefNode(GeometryNode, GEO_NODE_POINTS_TO_VOLUME, def_geo_points_to_volume, "POINTS_TO_VOLUME", PointsToVolume, "Points to Volume", "Generate a fog volume sphere around every point")
+DefNode(GeometryNode, GEO_NODE_POINTS, 0, "POINTS", Points, "Points", "Generate a point cloud with positions and radii defined by fields")
+DefNode(GeometryNode, GEO_NODE_PROXIMITY, def_geo_proximity, "PROXIMITY", Proximity, "Geometry Proximity", "Compute the closest location on the target geometry")
+DefNode(GeometryNode, GEO_NODE_RAYCAST, def_geo_raycast, "RAYCAST", Raycast, "Raycast", "Cast rays from the context geometry onto a target geometry, and retrieve information from each hit point")
+DefNode(GeometryNode, GEO_NODE_REALIZE_INSTANCES, def_geo_realize_instances,"REALIZE_INSTANCES", RealizeInstances, "Realize Instances", "Change the direction of the curve by swapping each spline's start and end data")
+DefNode(GeometryNode, GEO_NODE_REMOVE_ATTRIBUTE, 0, "REMOVE_ATTRIBUTE", RemoveAttribute, "Remove Named Attribute", "Delete an attribute with a specified name from a geometry. Typically used to optimize performance")
+DefNode(GeometryNode, GEO_NODE_REPLACE_MATERIAL, 0, "REPLACE_MATERIAL", ReplaceMaterial, "Replace Material", "Swap one material with another")
+DefNode(GeometryNode, GEO_NODE_RESAMPLE_CURVE, def_geo_curve_resample, "RESAMPLE_CURVE", ResampleCurve, "Resample Curve", "Generate a poly spline for each input spline")
+DefNode(GeometryNode, GEO_NODE_REVERSE_CURVE, 0, "REVERSE_CURVE", ReverseCurve, "Reverse Curve", "Swap the start and end of splines")
+DefNode(GeometryNode, GEO_NODE_ROTATE_INSTANCES, 0, "ROTATE_INSTANCES", RotateInstances, "Rotate Instances", "Rotate geometry instances in local or global space")
+DefNode(GeometryNode, GEO_NODE_SAMPLE_CURVE, def_geo_curve_sample, "SAMPLE_CURVE", SampleCurve, "Sample Curve", "Retrieve data from a point on a curve at a certain distance from its start")
+DefNode(GeometryNode, GEO_NODE_SCALE_ELEMENTS, def_geo_scale_elements, "SCALE_ELEMENTS", ScaleElements, "Scale Elements", "Scale groups of connected edges and faces")
+DefNode(GeometryNode, GEO_NODE_SCALE_INSTANCES, 0, "SCALE_INSTANCES", ScaleInstances, "Scale Instances", "Scale geometry instances in local or global space")
+DefNode(GeometryNode, GEO_NODE_SEPARATE_COMPONENTS, 0, "SEPARATE_COMPONENTS",SeparateComponents, "Separate Components","Split a geometry into a separate output for each type of data in the geometry")
+DefNode(GeometryNode, GEO_NODE_SEPARATE_GEOMETRY, def_geo_separate_geometry,"SEPARATE_GEOMETRY", SeparateGeometry, "Separate Geometry", "Split a geometry into two geometry outputs based on a selection")
+DefNode(GeometryNode, GEO_NODE_SET_CURVE_HANDLES, def_geo_curve_set_handle_positions, "SET_CURVE_HANDLES", SetCurveHandlePositions, "Set Handle Positions", "Set the positions for the handles of Bézier curves")
+DefNode(GeometryNode, GEO_NODE_SET_CURVE_RADIUS, 0, "SET_CURVE_RADIUS", SetCurveRadius, "Set Curve Radius", "Set the radius of the curve at each control point")
+DefNode(GeometryNode, GEO_NODE_SET_CURVE_TILT, 0, "SET_CURVE_TILT", SetCurveTilt, "Set Curve Tilt", "Set the tilt angle at each curve control point")
+DefNode(GeometryNode, GEO_NODE_SET_ID, 0, "SET_ID", SetID, "Set ID", "Set the id attribute on the input geometry, mainly used internally for randomizing")
+DefNode(GeometryNode, GEO_NODE_SET_MATERIAL_INDEX, 0, "SET_MATERIAL_INDEX", SetMaterialIndex, "Set Material Index", "Set the material index for each selected geometry element")
+DefNode(GeometryNode, GEO_NODE_SET_MATERIAL, 0, "SET_MATERIAL", SetMaterial, "Set Material", "Assign a material to geometry elements")
+DefNode(GeometryNode, GEO_NODE_SET_POINT_RADIUS, 0, "SET_POINT_RADIUS", SetPointRadius, "Set Point Radius", "Set the display size of point cloud points")
+DefNode(GeometryNode, GEO_NODE_SET_POSITION, 0, "SET_POSITION", SetPosition, "Set Position", "Set the location of each point")
+DefNode(GeometryNode, GEO_NODE_SET_SHADE_SMOOTH, 0, "SET_SHADE_SMOOTH", SetShadeSmooth, "Set Shade Smooth", "Control the smoothness of mesh normals around each face by changing the \"shade smooth\" attribute")
+DefNode(GeometryNode, GEO_NODE_SET_SPLINE_CYCLIC, 0, "SET_SPLINE_CYCLIC", SetSplineCyclic, "Set Spline Cyclic", "Control whether each spline loops back on itself by changing the \"cyclic\" attribute")
+DefNode(GeometryNode, GEO_NODE_SET_SPLINE_RESOLUTION, 0, "SET_SPLINE_RESOLUTION", SetSplineResolution, "Set Spline Resolution", "Control how many evaluated points should be generated on every curve segment")
+DefNode(GeometryNode, GEO_NODE_SPLIT_EDGES, 0, "SPLIT_EDGES", SplitEdges, "Split Edges", "Duplicate mesh edges and break connections with the surrounding faces")
+DefNode(GeometryNode, GEO_NODE_STORE_NAMED_ATTRIBUTE, def_geo_store_named_attribute, "STORE_NAMED_ATTRIBUTE", StoreNamedAttribute, "Store Named Attribute", "Store the result of a field on a geometry as an attribute with the specified name")
+DefNode(GeometryNode, GEO_NODE_STRING_JOIN, 0, "STRING_JOIN", StringJoin, "Join Strings", "Combine any number of input strings")
+DefNode(GeometryNode, GEO_NODE_STRING_TO_CURVES, def_geo_string_to_curves, "STRING_TO_CURVES", StringToCurves, "String to Curves", "Generate a paragraph of text with a specific font, using a curve instance to store each character")
+DefNode(GeometryNode, GEO_NODE_SUBDIVIDE_CURVE, 0, "SUBDIVIDE_CURVE", SubdivideCurve, "Subdivide Curve", "Dividing each curve segment into a specified number of pieces")
+DefNode(GeometryNode, GEO_NODE_SUBDIVIDE_MESH, 0, "SUBDIVIDE_MESH", SubdivideMesh, "Subdivide Mesh", "Divide mesh faces into smaller ones without changing the shape or volume, using linear interpolation to place the new vertices")
+DefNode(GeometryNode, GEO_NODE_SUBDIVISION_SURFACE, def_geo_subdivision_surface, "SUBDIVISION_SURFACE",SubdivisionSurface, "Subdivision Surface","Divide mesh faces to form a smooth surface, using the Catmull-Clark subdivision method")
+DefNode(GeometryNode, GEO_NODE_SWITCH, def_geo_switch, "SWITCH", Switch, "Switch", "Switch between two inputs")
+DefNode(GeometryNode, GEO_NODE_TRANSFER_ATTRIBUTE, def_geo_transfer_attribute, "ATTRIBUTE_TRANSFER", AttributeTransfer, "Transfer Attribute", "Retrieve values from a source geometry and provides them as a field by interpolating them with the context geometry")
+DefNode(GeometryNode, GEO_NODE_TRANSFORM, 0, "TRANSFORM", Transform, "Transform", "Translate, rotate or scale the geometry")
+DefNode(GeometryNode, GEO_NODE_TRANSLATE_INSTANCES, 0, "TRANSLATE_INSTANCES",TranslateInstances, "Translate Instances","Move top-level geometry instances in local or global space")
+DefNode(GeometryNode, GEO_NODE_TRIANGULATE, def_geo_triangulate, "TRIANGULATE", Triangulate, "Triangulate", "Convert all faces in a mesh to triangular faces")
+DefNode(GeometryNode, GEO_NODE_TRIM_CURVE, def_geo_curve_trim, "TRIM_CURVE", TrimCurve, "Trim Curve", "Shorten curves by removing portions at the start or end")
+DefNode(GeometryNode, GEO_NODE_UV_PACK_ISLANDS, 0, "UV_PACK_ISLANDS", UVPackIslands, "Pack UV Islands", "Scale islands of a UV map and move them so they fill the UV space as much as possible")
+DefNode(GeometryNode, GEO_NODE_UV_UNWRAP, def_geo_uv_unwrap, "UV_UNWRAP", UVUnwrap, "UV Unwrap", "Generate a UV map based on seam edges")
+DefNode(GeometryNode, GEO_NODE_VIEWER, def_geo_viewer, "VIEWER", Viewer, "Viewer", "Display the input data in the Spreadsheet Editor")
+DefNode(GeometryNode, GEO_NODE_VOLUME_CUBE, 0, "VOLUME_CUBE", VolumeCube, "Volume Cube", "Generate a dense volume with a field that controls the density at each grid voxel based on its position")
+DefNode(GeometryNode, GEO_NODE_VOLUME_TO_MESH, def_geo_volume_to_mesh, "VOLUME_TO_MESH", VolumeToMesh, "Volume to Mesh", "Generate a mesh on the \"surface\" of a volume")
/* undefine macros */
#undef DefNode
diff --git a/source/blender/nodes/composite/CMakeLists.txt b/source/blender/nodes/composite/CMakeLists.txt
index c0100d77889..2537e8e93cc 100644
--- a/source/blender/nodes/composite/CMakeLists.txt
+++ b/source/blender/nodes/composite/CMakeLists.txt
@@ -10,11 +10,14 @@ set(INC
../../blenlib
../../blentranslation
../../depsgraph
+ ../../functions
+ ../../gpu
../../imbuf
../../makesdna
../../makesrna
../../render
../../windowmanager
+ ../../compositor/realtime_compositor
../../../../intern/guardedalloc
# dna_type_offsets.h
@@ -120,15 +123,19 @@ set(SRC
node_composite_util.hh
)
+set(LIB
+ bf_realtime_compositor
+)
+
if(WITH_IMAGE_OPENEXR)
add_definitions(-DWITH_OPENEXR)
endif()
-if(WITH_COMPOSITOR)
+if(WITH_COMPOSITOR_CPU)
list(APPEND INC
../../compositor
)
- add_definitions(-DWITH_COMPOSITOR)
+ add_definitions(-DWITH_COMPOSITOR_CPU)
endif()
if(WITH_OPENIMAGEDENOISE)
diff --git a/source/blender/nodes/composite/node_composite_tree.cc b/source/blender/nodes/composite/node_composite_tree.cc
index 0b75dd9cef3..9792c55b590 100644
--- a/source/blender/nodes/composite/node_composite_tree.cc
+++ b/source/blender/nodes/composite/node_composite_tree.cc
@@ -15,6 +15,7 @@
#include "BKE_context.h"
#include "BKE_global.h"
+#include "BKE_image.h"
#include "BKE_main.h"
#include "BKE_node.h"
#include "BKE_node_tree_update.h"
@@ -31,7 +32,7 @@
#include "NOD_composite.h"
#include "node_composite_util.hh"
-#ifdef WITH_COMPOSITOR
+#ifdef WITH_COMPOSITOR_CPU
# include "COM_compositor.h"
#endif
@@ -182,6 +183,7 @@ void register_node_tree_type_cmp()
tt->type = NTREE_COMPOSIT;
strcpy(tt->idname, "CompositorNodeTree");
+ strcpy(tt->group_idname, "CompositorNodeGroup");
strcpy(tt->ui_name, N_("Compositor"));
tt->ui_icon = ICON_NODE_COMPOSITING;
strcpy(tt->ui_description, N_("Compositing nodes"));
@@ -208,7 +210,7 @@ void ntreeCompositExecTree(Scene *scene,
int do_preview,
const char *view_name)
{
-#ifdef WITH_COMPOSITOR
+#ifdef WITH_COMPOSITOR_CPU
COM_execute(rd, scene, ntree, rendering, view_name);
#else
UNUSED_VARS(scene, ntree, rd, rendering, view_name);
diff --git a/source/blender/nodes/composite/node_composite_util.hh b/source/blender/nodes/composite/node_composite_util.hh
index 3e9c43aa7d2..14210cedc95 100644
--- a/source/blender/nodes/composite/node_composite_util.hh
+++ b/source/blender/nodes/composite/node_composite_util.hh
@@ -8,24 +8,12 @@
#pragma once
#include "DNA_ID.h"
-#include "DNA_movieclip_types.h"
#include "DNA_node_types.h"
#include "BLT_translation.h"
-#include "BKE_colorband.h"
-#include "BKE_colortools.h"
-#include "BKE_image.h"
-#include "BKE_texture.h"
-#include "BKE_tracking.h"
-
#include "node_util.h"
-#include "IMB_imbuf.h"
-#include "IMB_imbuf_types.h"
-
-#include "RE_pipeline.h"
-
#include "NOD_composite.h"
#include "NOD_socket.h"
#include "NOD_socket_declarations.hh"
diff --git a/source/blender/nodes/composite/nodes/node_composite_alpha_over.cc b/source/blender/nodes/composite/nodes/node_composite_alpha_over.cc
index d392b810bc1..12f81da3d1c 100644
--- a/source/blender/nodes/composite/nodes/node_composite_alpha_over.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_alpha_over.cc
@@ -8,17 +8,32 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** ALPHAOVER ******************** */
namespace blender::nodes::node_composite_alpha_over_cc {
+NODE_STORAGE_FUNCS(NodeTwoFloats)
+
static void cmp_node_alphaover_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Color>(N_("Image"), "Image_001").default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Float>(N_("Fac"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(2);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Color>(N_("Image"), "Image_001")
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(1);
b.add_output<decl::Color>(N_("Image"));
}
@@ -36,6 +51,52 @@ static void node_composit_buts_alphaover(uiLayout *layout, bContext *UNUSED(C),
uiItemR(col, ptr, "premul", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class AlphaOverShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ const float premultiply_factor = get_premultiply_factor();
+ if (premultiply_factor != 0.0f) {
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_alpha_over_mixed",
+ inputs,
+ outputs,
+ GPU_uniform(&premultiply_factor));
+ return;
+ }
+
+ if (get_use_premultiply()) {
+ GPU_stack_link(material, &bnode(), "node_composite_alpha_over_key", inputs, outputs);
+ return;
+ }
+
+ GPU_stack_link(material, &bnode(), "node_composite_alpha_over_premultiply", inputs, outputs);
+ }
+
+ bool get_use_premultiply()
+ {
+ return bnode().custom1;
+ }
+
+ float get_premultiply_factor()
+ {
+ return node_storage(bnode()).x;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new AlphaOverShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_alpha_over_cc
void register_node_type_cmp_alphaover()
@@ -50,6 +111,7 @@ void register_node_type_cmp_alphaover()
node_type_init(&ntype, file_ns::node_alphaover_init);
node_type_storage(
&ntype, "NodeTwoFloats", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_antialiasing.cc b/source/blender/nodes/composite/nodes/node_composite_antialiasing.cc
index f45b678fc50..55fe3366526 100644
--- a/source/blender/nodes/composite/nodes/node_composite_antialiasing.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_antialiasing.cc
@@ -8,6 +8,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Anti-Aliasing (SMAA 1x) ******************** */
@@ -42,6 +44,23 @@ static void node_composit_buts_antialiasing(uiLayout *layout, bContext *UNUSED(C
uiItemR(col, ptr, "corner_rounding", 0, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class AntiAliasingOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new AntiAliasingOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_antialiasing_cc
void register_node_type_cmp_antialiasing()
@@ -58,6 +77,7 @@ void register_node_type_cmp_antialiasing()
node_type_init(&ntype, file_ns::node_composit_init_antialiasing);
node_type_storage(
&ntype, "NodeAntiAliasingData", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_bilateralblur.cc b/source/blender/nodes/composite/nodes/node_composite_bilateralblur.cc
index ad4a1f701d6..ac9a6c89aa4 100644
--- a/source/blender/nodes/composite/nodes/node_composite_bilateralblur.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_bilateralblur.cc
@@ -5,19 +5,32 @@
* \ingroup cmpnodes
*/
+#include "BLI_math_base.hh"
+
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_shader.h"
+
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
#include "node_composite_util.hh"
/* **************** BILATERALBLUR ******************** */
namespace blender::nodes::node_composite_bilateralblur_cc {
+NODE_STORAGE_FUNCS(NodeBilateralBlurData)
+
static void cmp_node_bilateralblur_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Color>(N_("Determinator")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Color>(N_("Determinator"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(1);
b.add_output<decl::Color>(N_("Image"));
}
@@ -42,6 +55,61 @@ static void node_composit_buts_bilateralblur(uiLayout *layout,
uiItemR(col, ptr, "sigma_space", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class BilateralBlurOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ const Result &input_image = get_input("Image");
+ /* Single value inputs can't be blurred and are returned as is. */
+ if (input_image.is_single_value()) {
+ get_input("Image").pass_through(get_result("Image"));
+ return;
+ }
+
+ GPUShader *shader = shader_manager().get("compositor_bilateral_blur");
+ GPU_shader_bind(shader);
+
+ GPU_shader_uniform_1i(shader, "radius", get_blur_radius());
+ GPU_shader_uniform_1f(shader, "threshold", get_threshold());
+
+ input_image.bind_as_texture(shader, "input_tx");
+
+ const Result &determinator_image = get_input("Determinator");
+ determinator_image.bind_as_texture(shader, "determinator_tx");
+
+ const Domain domain = compute_domain();
+ Result &output_image = get_result("Image");
+ output_image.allocate_texture(domain);
+ output_image.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ GPU_shader_unbind();
+ output_image.unbind_as_image();
+ input_image.unbind_as_texture();
+ determinator_image.unbind_as_texture();
+ }
+
+ int get_blur_radius()
+ {
+ return math::ceil(node_storage(bnode()).iter + node_storage(bnode()).sigma_space);
+ }
+
+ float get_threshold()
+ {
+ return node_storage(bnode()).sigma_color;
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new BilateralBlurOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_bilateralblur_cc
void register_node_type_cmp_bilateralblur()
@@ -56,6 +124,7 @@ void register_node_type_cmp_bilateralblur()
node_type_init(&ntype, file_ns::node_composit_init_bilateralblur);
node_type_storage(
&ntype, "NodeBilateralBlurData", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_blur.cc b/source/blender/nodes/composite/nodes/node_composite_blur.cc
index 7beffe15c8e..cb1d93fe10b 100644
--- a/source/blender/nodes/composite/nodes/node_composite_blur.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_blur.cc
@@ -10,6 +10,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** BLUR ******************** */
@@ -71,6 +73,23 @@ static void node_composit_buts_blur(uiLayout *layout, bContext *UNUSED(C), Point
uiItemR(col, ptr, "use_extended_bounds", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class BlurOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new BlurOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_blur_cc
void register_node_type_cmp_blur()
@@ -86,6 +105,7 @@ void register_node_type_cmp_blur()
node_type_init(&ntype, file_ns::node_composit_init_blur);
node_type_storage(
&ntype, "NodeBlurData", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc b/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc
index a936bafe671..538f00af12d 100644
--- a/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc
@@ -8,6 +8,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** BLUR ******************** */
@@ -37,6 +39,23 @@ static void node_composit_buts_bokehblur(uiLayout *layout, bContext *UNUSED(C),
uiItemR(layout, ptr, "use_extended_bounds", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class BokehBlurOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new BokehBlurOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_bokehblur_cc
void register_node_type_cmp_bokehblur()
@@ -49,6 +68,7 @@ void register_node_type_cmp_bokehblur()
ntype.declare = file_ns::cmp_node_bokehblur_declare;
ntype.draw_buttons = file_ns::node_composit_buts_bokehblur;
node_type_init(&ntype, file_ns::node_composit_init_bokehblur);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc b/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc
index 8330c56736a..81cc8990d35 100644
--- a/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc
@@ -5,15 +5,25 @@
* \ingroup cmpnodes
*/
+#include "BLI_math_base.h"
+#include "BLI_math_vec_types.hh"
+
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_shader.h"
+
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
#include "node_composite_util.hh"
/* **************** Bokeh image Tools ******************** */
namespace blender::nodes::node_composite_bokehimage_cc {
+NODE_STORAGE_FUNCS(NodeBokehImage)
+
static void cmp_node_bokehimage_declare(NodeDeclarationBuilder &b)
{
b.add_output<decl::Color>(N_("Image"));
@@ -45,6 +55,61 @@ static void node_composit_buts_bokehimage(uiLayout *layout, bContext *UNUSED(C),
uiItemR(layout, ptr, "shift", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class BokehImageOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ GPUShader *shader = shader_manager().get("compositor_bokeh_image");
+ GPU_shader_bind(shader);
+
+ GPU_shader_uniform_1f(shader, "exterior_angle", get_exterior_angle());
+ GPU_shader_uniform_1f(shader, "rotation", get_rotation());
+ GPU_shader_uniform_1f(shader, "roundness", node_storage(bnode()).rounding);
+ GPU_shader_uniform_1f(shader, "catadioptric", node_storage(bnode()).catadioptric);
+ GPU_shader_uniform_1f(shader, "lens_shift", node_storage(bnode()).lensshift);
+
+ Result &output = get_result("Image");
+ const Domain domain = compute_domain();
+ output.allocate_texture(domain);
+ output.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ output.unbind_as_image();
+ GPU_shader_unbind();
+ }
+
+ Domain compute_domain() override
+ {
+ return Domain(int2(512));
+ }
+
+ /* The exterior angle is the angle between each two consecutive vertices of the regular polygon
+ * from its center. */
+ float get_exterior_angle()
+ {
+ return (M_PI * 2.0f) / node_storage(bnode()).flaps;
+ }
+
+ float get_rotation()
+ {
+ /* Offset the rotation such that the second vertex of the regular polygon lies on the positive
+ * y axis, which is 90 degrees minus the angle that it makes with the positive x axis assuming
+ * the first vertex lies on the positive x axis. */
+ const float offset = M_PI_2 - get_exterior_angle();
+ return node_storage(bnode()).angle - offset;
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new BokehImageOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_bokehimage_cc
void register_node_type_cmp_bokehimage()
@@ -60,6 +125,7 @@ void register_node_type_cmp_bokehimage()
node_type_init(&ntype, file_ns::node_composit_init_bokehimage);
node_type_storage(
&ntype, "NodeBokehImage", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_boxmask.cc b/source/blender/nodes/composite/nodes/node_composite_boxmask.cc
index f39b69c63f2..3cf0932e1b3 100644
--- a/source/blender/nodes/composite/nodes/node_composite_boxmask.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_boxmask.cc
@@ -5,15 +5,26 @@
* \ingroup cmpnodes
*/
+#include <cmath>
+
+#include "BLI_math_vec_types.hh"
+
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_shader.h"
+
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
#include "node_composite_util.hh"
/* **************** SCALAR MATH ******************** */
namespace blender::nodes::node_composite_boxmask_cc {
+NODE_STORAGE_FUNCS(NodeBoxMask)
+
static void cmp_node_boxmask_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Float>(N_("Mask")).default_value(0.0f).min(0.0f).max(1.0f);
@@ -48,6 +59,93 @@ static void node_composit_buts_boxmask(uiLayout *layout, bContext *UNUSED(C), Po
uiItemR(layout, ptr, "mask_type", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class BoxMaskOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ GPUShader *shader = shader_manager().get(get_shader_name());
+ GPU_shader_bind(shader);
+
+ const Domain domain = compute_domain();
+
+ GPU_shader_uniform_2iv(shader, "domain_size", domain.size);
+
+ GPU_shader_uniform_2fv(shader, "location", get_location());
+ GPU_shader_uniform_2fv(shader, "size", get_size() / 2.0f);
+ GPU_shader_uniform_1f(shader, "cos_angle", std::cos(get_angle()));
+ GPU_shader_uniform_1f(shader, "sin_angle", std::sin(get_angle()));
+
+ const Result &input_mask = get_input("Mask");
+ input_mask.bind_as_texture(shader, "base_mask_tx");
+
+ const Result &value = get_input("Value");
+ value.bind_as_texture(shader, "mask_value_tx");
+
+ Result &output_mask = get_result("Mask");
+ output_mask.allocate_texture(domain);
+ output_mask.bind_as_image(shader, "output_mask_img");
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ input_mask.unbind_as_texture();
+ value.unbind_as_texture();
+ output_mask.unbind_as_image();
+ GPU_shader_unbind();
+ }
+
+ Domain compute_domain() override
+ {
+ if (get_input("Mask").is_single_value()) {
+ return Domain(context().get_output_size());
+ }
+ return get_input("Mask").domain();
+ }
+
+ CMPNodeMaskType get_mask_type()
+ {
+ return (CMPNodeMaskType)bnode().custom1;
+ }
+
+ const char *get_shader_name()
+ {
+ switch (get_mask_type()) {
+ default:
+ case CMP_NODE_MASKTYPE_ADD:
+ return "compositor_box_mask_add";
+ case CMP_NODE_MASKTYPE_SUBTRACT:
+ return "compositor_box_mask_subtract";
+ case CMP_NODE_MASKTYPE_MULTIPLY:
+ return "compositor_box_mask_multiply";
+ case CMP_NODE_MASKTYPE_NOT:
+ return "compositor_box_mask_not";
+ }
+ }
+
+ float2 get_location()
+ {
+ return float2(node_storage(bnode()).x, node_storage(bnode()).y);
+ }
+
+ float2 get_size()
+ {
+ return float2(node_storage(bnode()).width, node_storage(bnode()).height);
+ }
+
+ float get_angle()
+ {
+ return node_storage(bnode()).rotation;
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new BoxMaskOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_boxmask_cc
void register_node_type_cmp_boxmask()
@@ -61,6 +159,7 @@ void register_node_type_cmp_boxmask()
ntype.draw_buttons = file_ns::node_composit_buts_boxmask;
node_type_init(&ntype, file_ns::node_composit_init_boxmask);
node_type_storage(&ntype, "NodeBoxMask", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_brightness.cc b/source/blender/nodes/composite/nodes/node_composite_brightness.cc
index 65ed2885d9b..fa22f551de6 100644
--- a/source/blender/nodes/composite/nodes/node_composite_brightness.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_brightness.cc
@@ -8,6 +8,10 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** Bright and Contrast ******************** */
@@ -16,9 +20,11 @@ namespace blender::nodes::node_composite_brightness_cc {
static void cmp_node_brightcontrast_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Float>(N_("Bright")).min(-100.0f).max(100.0f);
- b.add_input<decl::Float>(N_("Contrast")).min(-100.0f).max(100.0f);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("Bright")).min(-100.0f).max(100.0f).compositor_domain_priority(1);
+ b.add_input<decl::Float>(N_("Contrast")).min(-100.0f).max(100.0f).compositor_domain_priority(2);
b.add_output<decl::Color>(N_("Image"));
}
@@ -34,6 +40,38 @@ static void node_composit_buts_brightcontrast(uiLayout *layout,
uiItemR(layout, ptr, "use_premultiply", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class BrightContrastShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ const float use_premultiply = get_use_premultiply();
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_bright_contrast",
+ inputs,
+ outputs,
+ GPU_constant(&use_premultiply));
+ }
+
+ bool get_use_premultiply()
+ {
+ return bnode().custom1;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new BrightContrastShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_brightness_cc
void register_node_type_cmp_brightcontrast()
@@ -46,6 +84,7 @@ void register_node_type_cmp_brightcontrast()
ntype.declare = file_ns::cmp_node_brightcontrast_declare;
ntype.draw_buttons = file_ns::node_composit_buts_brightcontrast;
node_type_init(&ntype, file_ns::node_composit_init_brightcontrast);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_channel_matte.cc b/source/blender/nodes/composite/nodes/node_composite_channel_matte.cc
index 627f07fdfce..3b825017da8 100644
--- a/source/blender/nodes/composite/nodes/node_composite_channel_matte.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_channel_matte.cc
@@ -10,15 +10,23 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* ******************* Channel Matte Node ********************************* */
namespace blender::nodes::node_composite_channel_matte_cc {
+NODE_STORAGE_FUNCS(NodeChroma)
+
static void cmp_node_channel_matte_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Color>(N_("Image"));
b.add_output<decl::Float>(N_("Matte"));
}
@@ -79,6 +87,91 @@ static void node_composit_buts_channel_matte(uiLayout *layout,
col, ptr, "limit_min", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class ChannelMatteShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ const float color_space = get_color_space();
+ const float matte_channel = get_matte_channel();
+ float limit_channels[2];
+ get_limit_channels(limit_channels);
+ const float max_limit = get_max_limit();
+ const float min_limit = get_min_limit();
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_channel_matte",
+ inputs,
+ outputs,
+ GPU_constant(&color_space),
+ GPU_constant(&matte_channel),
+ GPU_constant(limit_channels),
+ GPU_uniform(&max_limit),
+ GPU_uniform(&min_limit));
+ }
+
+ /* 1 -> CMP_NODE_CHANNEL_MATTE_CS_RGB
+ * 2 -> CMP_NODE_CHANNEL_MATTE_CS_HSV
+ * 3 -> CMP_NODE_CHANNEL_MATTE_CS_YUV
+ * 4 -> CMP_NODE_CHANNEL_MATTE_CS_YCC */
+ int get_color_space()
+ {
+ return bnode().custom1;
+ }
+
+ /* Get the index of the channel used to generate the matte. */
+ int get_matte_channel()
+ {
+ return bnode().custom2 - 1;
+ }
+
+ /* Get the index of the channel used to compute the limit value. */
+ int get_limit_channel()
+ {
+ return node_storage(bnode()).channel - 1;
+ }
+
+ /* Get the indices of the channels used to compute the limit value. We always assume the limit
+ * algorithm is Max, if it is a single limit channel, store it in both limit channels, because
+ * the maximum of two identical values is the same value. */
+ void get_limit_channels(float limit_channels[2])
+ {
+ if (node_storage(bnode()).algorithm == CMP_NODE_CHANNEL_MATTE_LIMIT_ALGORITHM_MAX) {
+ /* If the algorithm is Max, store the indices of the other two channels other than the matte
+ * channel. */
+ limit_channels[0] = (get_matte_channel() + 1) % 3;
+ limit_channels[1] = (get_matte_channel() + 2) % 3;
+ }
+ else {
+ /* If the algorithm is Single, store the index of the limit channel in both channels. */
+ limit_channels[0] = get_limit_channel();
+ limit_channels[1] = get_limit_channel();
+ }
+ }
+
+ float get_max_limit()
+ {
+ return node_storage(bnode()).t1;
+ }
+
+ float get_min_limit()
+ {
+ return node_storage(bnode()).t2;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new ChannelMatteShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_channel_matte_cc
void register_node_type_cmp_channel_matte()
@@ -93,6 +186,7 @@ void register_node_type_cmp_channel_matte()
ntype.flag |= NODE_PREVIEW;
node_type_init(&ntype, file_ns::node_composit_init_channel_matte);
node_type_storage(&ntype, "NodeChroma", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_chroma_matte.cc b/source/blender/nodes/composite/nodes/node_composite_chroma_matte.cc
index 69319c6825d..e5ce87169d4 100644
--- a/source/blender/nodes/composite/nodes/node_composite_chroma_matte.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_chroma_matte.cc
@@ -5,21 +5,33 @@
* \ingroup cmpnodes
*/
+#include <cmath>
+
#include "BLI_math_rotation.h"
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* ******************* Chroma Key ********************************************************** */
namespace blender::nodes::node_composite_chroma_matte_cc {
+NODE_STORAGE_FUNCS(NodeChroma)
+
static void cmp_node_chroma_matte_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Color>(N_("Key Color")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Color>(N_("Key Color"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(1);
b.add_output<decl::Color>(N_("Image"));
b.add_output<decl::Float>(N_("Matte"));
}
@@ -51,6 +63,52 @@ static void node_composit_buts_chroma_matte(uiLayout *layout, bContext *UNUSED(C
// uiItemR(col, ptr, "shadow_adjust", UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class ChromaMatteShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ const float acceptance = get_acceptance();
+ const float cutoff = get_cutoff();
+ const float falloff = get_falloff();
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_chroma_matte",
+ inputs,
+ outputs,
+ GPU_uniform(&acceptance),
+ GPU_uniform(&cutoff),
+ GPU_uniform(&falloff));
+ }
+
+ float get_acceptance()
+ {
+ return std::tan(node_storage(bnode()).t1) / 2.0f;
+ }
+
+ float get_cutoff()
+ {
+ return node_storage(bnode()).t2;
+ }
+
+ float get_falloff()
+ {
+ return node_storage(bnode()).fstrength;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new ChromaMatteShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_chroma_matte_cc
void register_node_type_cmp_chroma_matte()
@@ -65,6 +123,7 @@ void register_node_type_cmp_chroma_matte()
ntype.flag |= NODE_PREVIEW;
node_type_init(&ntype, file_ns::node_composit_init_chroma_matte);
node_type_storage(&ntype, "NodeChroma", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_color_matte.cc b/source/blender/nodes/composite/nodes/node_composite_color_matte.cc
index 474fb1b72f2..08329601f14 100644
--- a/source/blender/nodes/composite/nodes/node_composite_color_matte.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_color_matte.cc
@@ -8,16 +8,26 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* ******************* Color Matte ********************************************************** */
namespace blender::nodes::node_composite_color_matte_cc {
+NODE_STORAGE_FUNCS(NodeChroma)
+
static void cmp_node_color_matte_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Color>(N_("Key Color")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Color>(N_("Key Color"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(1);
b.add_output<decl::Color>(N_("Image"));
b.add_output<decl::Float>(N_("Matte"));
}
@@ -50,6 +60,53 @@ static void node_composit_buts_color_matte(uiLayout *layout, bContext *UNUSED(C)
col, ptr, "color_value", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class ColorMatteShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ const float hue_epsilon = get_hue_epsilon();
+ const float saturation_epsilon = get_saturation_epsilon();
+ const float value_epsilon = get_value_epsilon();
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_color_matte",
+ inputs,
+ outputs,
+ GPU_uniform(&hue_epsilon),
+ GPU_uniform(&saturation_epsilon),
+ GPU_uniform(&value_epsilon));
+ }
+
+ float get_hue_epsilon()
+ {
+ /* Divide by 2 because the hue wraps around. */
+ return node_storage(bnode()).t1 / 2.0f;
+ }
+
+ float get_saturation_epsilon()
+ {
+ return node_storage(bnode()).t2;
+ }
+
+ float get_value_epsilon()
+ {
+ return node_storage(bnode()).t3;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new ColorMatteShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_color_matte_cc
void register_node_type_cmp_color_matte()
@@ -64,6 +121,7 @@ void register_node_type_cmp_color_matte()
ntype.flag |= NODE_PREVIEW;
node_type_init(&ntype, file_ns::node_composit_init_color_matte);
node_type_storage(&ntype, "NodeChroma", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_color_spill.cc b/source/blender/nodes/composite/nodes/node_composite_color_spill.cc
index 9ad5dfbaeb2..29401d7b20f 100644
--- a/source/blender/nodes/composite/nodes/node_composite_color_spill.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_color_spill.cc
@@ -10,16 +10,29 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* ******************* Color Spill Suppression ********************************* */
namespace blender::nodes::node_composite_color_spill_cc {
+NODE_STORAGE_FUNCS(NodeColorspill)
+
static void cmp_node_color_spill_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("Fac"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(1);
b.add_output<decl::Color>(N_("Image"));
}
@@ -27,8 +40,8 @@ static void node_composit_init_color_spill(bNodeTree *UNUSED(ntree), bNode *node
{
NodeColorspill *ncs = MEM_cnew<NodeColorspill>(__func__);
node->storage = ncs;
+ node->custom2 = CMP_NODE_COLOR_SPILL_LIMIT_ALGORITHM_SINGLE;
node->custom1 = 2; /* green channel */
- node->custom2 = 0; /* simple limit algorithm */
ncs->limchan = 0; /* limit by red */
ncs->limscale = 1.0f; /* limit scaling factor */
ncs->unspill = 0; /* do not use unspill */
@@ -80,6 +93,98 @@ static void node_composit_buts_color_spill(uiLayout *layout, bContext *UNUSED(C)
}
}
+using namespace blender::realtime_compositor;
+
+class ColorSpillShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ const float spill_channel = get_spill_channel();
+ float spill_scale[3];
+ get_spill_scale(spill_scale);
+ float limit_channels[2];
+ get_limit_channels(limit_channels);
+ const float limit_scale = get_limit_scale();
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_color_spill",
+ inputs,
+ outputs,
+ GPU_constant(&spill_channel),
+ GPU_uniform(spill_scale),
+ GPU_constant(limit_channels),
+ GPU_uniform(&limit_scale));
+ }
+
+ /* Get the index of the channel used for spilling. */
+ int get_spill_channel()
+ {
+ return bnode().custom1 - 1;
+ }
+
+ CMPNodeColorSpillLimitAlgorithm get_limit_algorithm()
+ {
+ return (CMPNodeColorSpillLimitAlgorithm)bnode().custom2;
+ }
+
+ void get_spill_scale(float spill_scale[3])
+ {
+ const NodeColorspill &node_color_spill = node_storage(bnode());
+ if (node_color_spill.unspill) {
+ spill_scale[0] = node_color_spill.uspillr;
+ spill_scale[1] = node_color_spill.uspillg;
+ spill_scale[2] = node_color_spill.uspillb;
+ spill_scale[get_spill_channel()] *= -1.0f;
+ }
+ else {
+ spill_scale[0] = 0.0f;
+ spill_scale[1] = 0.0f;
+ spill_scale[2] = 0.0f;
+ spill_scale[get_spill_channel()] = -1.0f;
+ }
+ }
+
+ /* Get the index of the channel used for limiting. */
+ int get_limit_channel()
+ {
+ return node_storage(bnode()).limchan;
+ }
+
+ /* Get the indices of the channels used to compute the limit value. We always assume the limit
+ * algorithm is Average, if it is a single limit channel, store it in both limit channels,
+ * because the average of two identical values is the same value. */
+ void get_limit_channels(float limit_channels[2])
+ {
+ if (get_limit_algorithm() == CMP_NODE_COLOR_SPILL_LIMIT_ALGORITHM_AVERAGE) {
+ /* If the algorithm is Average, store the indices of the other two channels other than the
+ * spill channel. */
+ limit_channels[0] = (get_spill_channel() + 1) % 3;
+ limit_channels[1] = (get_spill_channel() + 2) % 3;
+ }
+ else {
+ /* If the algorithm is Single, store the index of the limit channel in both channels. */
+ limit_channels[0] = get_limit_channel();
+ limit_channels[1] = get_limit_channel();
+ }
+ }
+
+ float get_limit_scale()
+ {
+ return node_storage(bnode()).limscale;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new ColorSpillShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_color_spill_cc
void register_node_type_cmp_color_spill()
@@ -94,6 +199,7 @@ void register_node_type_cmp_color_spill()
node_type_init(&ntype, file_ns::node_composit_init_color_spill);
node_type_storage(
&ntype, "NodeColorspill", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc b/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc
index dd081c8fc12..e05fbf00a25 100644
--- a/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc
@@ -10,6 +10,10 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* ******************* Color Balance ********************************* */
@@ -44,10 +48,19 @@ void ntreeCompositColorBalanceSyncFromCDL(bNodeTree *UNUSED(ntree), bNode *node)
namespace blender::nodes::node_composite_colorbalance_cc {
+NODE_STORAGE_FUNCS(NodeColorBalance)
+
static void cmp_node_colorbalance_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Float>(N_("Fac"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(1);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Color>(N_("Image"));
}
@@ -71,7 +84,7 @@ static void node_composit_buts_colorbalance(uiLayout *layout, bContext *UNUSED(C
uiItemR(layout, ptr, "correction_method", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
- if (RNA_enum_get(ptr, "correction_method") == 0) {
+ if (RNA_enum_get(ptr, "correction_method") == CMP_NODE_COLOR_BALANCE_LGG) {
split = uiLayoutSplit(layout, 0.0f, false);
col = uiLayoutColumn(split, false);
@@ -116,7 +129,7 @@ static void node_composit_buts_colorbalance_ex(uiLayout *layout,
{
uiItemR(layout, ptr, "correction_method", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
- if (RNA_enum_get(ptr, "correction_method") == 0) {
+ if (RNA_enum_get(ptr, "correction_method") == CMP_NODE_COLOR_BALANCE_LGG) {
uiTemplateColorPicker(layout, ptr, "lift", true, true, false, true);
uiItemR(layout, ptr, "lift", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
@@ -139,6 +152,53 @@ static void node_composit_buts_colorbalance_ex(uiLayout *layout,
}
}
+using namespace blender::realtime_compositor;
+
+class ColorBalanceShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ const NodeColorBalance &node_color_balance = node_storage(bnode());
+
+ if (get_color_balance_method() == CMP_NODE_COLOR_BALANCE_LGG) {
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_color_balance_lgg",
+ inputs,
+ outputs,
+ GPU_uniform(node_color_balance.lift),
+ GPU_uniform(node_color_balance.gamma),
+ GPU_uniform(node_color_balance.gain));
+ return;
+ }
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_color_balance_asc_cdl",
+ inputs,
+ outputs,
+ GPU_uniform(node_color_balance.offset),
+ GPU_uniform(node_color_balance.power),
+ GPU_uniform(node_color_balance.slope),
+ GPU_uniform(&node_color_balance.offset_basis));
+ }
+
+ CMPNodeColorBalanceMethod get_color_balance_method()
+ {
+ return (CMPNodeColorBalanceMethod)bnode().custom1;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new ColorBalanceShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_colorbalance_cc
void register_node_type_cmp_colorbalance()
@@ -155,6 +215,7 @@ void register_node_type_cmp_colorbalance()
node_type_init(&ntype, file_ns::node_composit_init_colorbalance);
node_type_storage(
&ntype, "NodeColorBalance", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc b/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc
index 39ecd277cec..92b10fc1877 100644
--- a/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc
@@ -5,19 +5,33 @@
* \ingroup cmpnodes
*/
+#include "IMB_colormanagement.h"
+
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* ******************* Color Correction ********************************* */
namespace blender::nodes::node_composite_colorcorrection_cc {
+NODE_STORAGE_FUNCS(NodeColorCorrection)
+
static void cmp_node_colorcorrection_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Float>(N_("Mask")).default_value(1.0f).min(0.0f).max(1.0f);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("Mask"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .compositor_domain_priority(1);
b.add_output<decl::Color>(N_("Image"));
}
@@ -266,6 +280,68 @@ static void node_composit_buts_colorcorrection_ex(uiLayout *layout,
uiItemR(row, ptr, "midtones_end", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class ColorCorrectionShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ float enabled_channels[3];
+ get_enabled_channels(enabled_channels);
+ float luminance_coefficients[3];
+ IMB_colormanagement_get_luminance_coefficients(luminance_coefficients);
+
+ const NodeColorCorrection &node_color_correction = node_storage(bnode());
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_color_correction",
+ inputs,
+ outputs,
+ GPU_constant(enabled_channels),
+ GPU_uniform(&node_color_correction.startmidtones),
+ GPU_uniform(&node_color_correction.endmidtones),
+ GPU_uniform(&node_color_correction.master.saturation),
+ GPU_uniform(&node_color_correction.master.contrast),
+ GPU_uniform(&node_color_correction.master.gamma),
+ GPU_uniform(&node_color_correction.master.gain),
+ GPU_uniform(&node_color_correction.master.lift),
+ GPU_uniform(&node_color_correction.shadows.saturation),
+ GPU_uniform(&node_color_correction.shadows.contrast),
+ GPU_uniform(&node_color_correction.shadows.gamma),
+ GPU_uniform(&node_color_correction.shadows.gain),
+ GPU_uniform(&node_color_correction.shadows.lift),
+ GPU_uniform(&node_color_correction.midtones.saturation),
+ GPU_uniform(&node_color_correction.midtones.contrast),
+ GPU_uniform(&node_color_correction.midtones.gamma),
+ GPU_uniform(&node_color_correction.midtones.gain),
+ GPU_uniform(&node_color_correction.midtones.lift),
+ GPU_uniform(&node_color_correction.highlights.saturation),
+ GPU_uniform(&node_color_correction.highlights.contrast),
+ GPU_uniform(&node_color_correction.highlights.gamma),
+ GPU_uniform(&node_color_correction.highlights.gain),
+ GPU_uniform(&node_color_correction.highlights.lift),
+ GPU_constant(luminance_coefficients));
+ }
+
+ void get_enabled_channels(float enabled_channels[3])
+ {
+ for (int i = 0; i < 3; i++) {
+ enabled_channels[i] = (bnode().custom1 & (1 << i)) ? 1.0f : 0.0f;
+ }
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new ColorCorrectionShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_colorcorrection_cc
void register_node_type_cmp_colorcorrection()
@@ -282,6 +358,7 @@ void register_node_type_cmp_colorcorrection()
node_type_init(&ntype, file_ns::node_composit_init_colorcorrection);
node_type_storage(
&ntype, "NodeColorCorrection", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_composite.cc b/source/blender/nodes/composite/nodes/node_composite_composite.cc
index d35ce7dc11a..68061bb434d 100644
--- a/source/blender/nodes/composite/nodes/node_composite_composite.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_composite.cc
@@ -5,9 +5,18 @@
* \ingroup cmpnodes
*/
+#include "BLI_math_vec_types.hh"
+
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_shader.h"
+#include "GPU_state.h"
+#include "GPU_texture.h"
+
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
#include "node_composite_util.hh"
/* **************** COMPOSITE ******************** */
@@ -26,6 +35,125 @@ static void node_composit_buts_composite(uiLayout *layout, bContext *UNUSED(C),
uiItemR(layout, ptr, "use_alpha", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class CompositeOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ const Result &image = get_input("Image");
+ const Result &alpha = get_input("Alpha");
+
+ if (image.is_single_value() && alpha.is_single_value()) {
+ execute_clear();
+ }
+ else if (ignore_alpha()) {
+ execute_ignore_alpha();
+ }
+ else if (!node().input_by_identifier("Alpha")->is_logically_linked()) {
+ execute_copy();
+ }
+ else {
+ execute_set_alpha();
+ }
+ }
+
+ /* Executes when all inputs are single values, in which case, the output texture can just be
+ * cleared to the appropriate color. */
+ void execute_clear()
+ {
+ const Result &image = get_input("Image");
+ const Result &alpha = get_input("Alpha");
+
+ float4 color = image.get_color_value();
+ if (ignore_alpha()) {
+ color.w = 1.0f;
+ }
+ else if (node().input_by_identifier("Alpha")->is_logically_linked()) {
+ color.w = alpha.get_float_value();
+ }
+
+ GPU_texture_clear(context().get_output_texture(), GPU_DATA_FLOAT, color);
+ }
+
+ /* Executes when the alpha channel of the image is ignored. */
+ void execute_ignore_alpha()
+ {
+ GPUShader *shader = shader_manager().get("compositor_convert_color_to_opaque");
+ GPU_shader_bind(shader);
+
+ const Result &image = get_input("Image");
+ image.bind_as_texture(shader, "input_tx");
+
+ GPUTexture *output_texture = context().get_output_texture();
+ const int image_unit = GPU_shader_get_texture_binding(shader, "output_img");
+ GPU_texture_image_bind(output_texture, image_unit);
+
+ compute_dispatch_threads_at_least(shader, compute_domain().size);
+
+ image.unbind_as_texture();
+ GPU_texture_image_unbind(output_texture);
+ GPU_shader_unbind();
+ }
+
+ /* Executes when the image texture is written with no adjustments and can thus be copied directly
+ * to the output texture. */
+ void execute_copy()
+ {
+ const Result &image = get_input("Image");
+
+ /* Make sure any prior writes to the texture are reflected before copying it. */
+ GPU_memory_barrier(GPU_BARRIER_TEXTURE_UPDATE);
+
+ GPU_texture_copy(context().get_output_texture(), image.texture());
+ }
+
+ /* Executes when the alpha channel of the image is set as the value of the input alpha. */
+ void execute_set_alpha()
+ {
+ GPUShader *shader = shader_manager().get("compositor_set_alpha");
+ GPU_shader_bind(shader);
+
+ const Result &image = get_input("Image");
+ image.bind_as_texture(shader, "image_tx");
+
+ const Result &alpha = get_input("Alpha");
+ alpha.bind_as_texture(shader, "alpha_tx");
+
+ GPUTexture *output_texture = context().get_output_texture();
+ const int image_unit = GPU_shader_get_texture_binding(shader, "output_img");
+ GPU_texture_image_bind(output_texture, image_unit);
+
+ compute_dispatch_threads_at_least(shader, compute_domain().size);
+
+ image.unbind_as_texture();
+ alpha.unbind_as_texture();
+ GPU_texture_image_unbind(output_texture);
+ GPU_shader_unbind();
+ }
+
+ /* If true, the alpha channel of the image is set to 1, that is, it becomes opaque. If false, the
+ * alpha channel of the image is retained, but only if the alpha input is not linked. If the
+ * alpha input is linked, it the value of that input will be used as the alpha of the image. */
+ bool ignore_alpha()
+ {
+ return bnode().custom2 & CMP_NODE_OUTPUT_IGNORE_ALPHA;
+ }
+
+ /* The operation domain have the same dimensions of the output without any transformations. */
+ Domain compute_domain() override
+ {
+ return Domain(context().get_output_size());
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new CompositeOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_composite_cc
void register_node_type_cmp_composite()
@@ -37,6 +165,7 @@ void register_node_type_cmp_composite()
cmp_node_type_base(&ntype, CMP_NODE_COMPOSITE, "Composite", NODE_CLASS_OUTPUT);
ntype.declare = file_ns::cmp_node_composite_declare;
ntype.draw_buttons = file_ns::node_composit_buts_composite;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
ntype.flag |= NODE_PREVIEW;
ntype.no_muting = true;
diff --git a/source/blender/nodes/composite/nodes/node_composite_convert_color_space.cc b/source/blender/nodes/composite/nodes/node_composite_convert_color_space.cc
index 303248c3852..e36da39cca1 100644
--- a/source/blender/nodes/composite/nodes/node_composite_convert_color_space.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_convert_color_space.cc
@@ -14,6 +14,8 @@
#include "IMB_colormanagement.h"
+#include "COM_node_operation.hh"
+
namespace blender::nodes::node_composite_convert_color_space_cc {
static void CMP_NODE_CONVERT_COLOR_SPACE_declare(NodeDeclarationBuilder &b)
@@ -47,6 +49,23 @@ static void node_composit_buts_convert_colorspace(uiLayout *layout,
uiItemR(layout, ptr, "to_color_space", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class ConvertColorSpaceOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new ConvertColorSpaceOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_convert_color_space_cc
void register_node_type_cmp_convert_color_space(void)
@@ -62,6 +81,7 @@ void register_node_type_cmp_convert_color_space(void)
node_type_init(&ntype, file_ns::node_composit_init_convert_colorspace);
node_type_storage(
&ntype, "NodeConvertColorSpace", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_cornerpin.cc b/source/blender/nodes/composite/nodes/node_composite_cornerpin.cc
index 07da0da0be1..9679701a7cf 100644
--- a/source/blender/nodes/composite/nodes/node_composite_cornerpin.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_cornerpin.cc
@@ -5,6 +5,8 @@
* \ingroup cmpnodes
*/
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
namespace blender::nodes::node_composite_cornerpin_cc {
@@ -32,6 +34,24 @@ static void cmp_node_cornerpin_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Float>(N_("Plane"));
}
+using namespace blender::realtime_compositor;
+
+class CornerPinOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ get_result("Plane").allocate_invalid();
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new CornerPinOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_cornerpin_cc
void register_node_type_cmp_cornerpin()
@@ -42,6 +62,7 @@ void register_node_type_cmp_cornerpin()
cmp_node_type_base(&ntype, CMP_NODE_CORNERPIN, "Corner Pin", NODE_CLASS_DISTORT);
ntype.declare = file_ns::cmp_node_cornerpin_declare;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_crop.cc b/source/blender/nodes/composite/nodes/node_composite_crop.cc
index 823e1052dd0..13d02a707be 100644
--- a/source/blender/nodes/composite/nodes/node_composite_crop.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_crop.cc
@@ -5,20 +5,35 @@
* \ingroup cmpnodes
*/
+#include "BLI_math_base.h"
+#include "BLI_math_vec_types.hh"
+
+#include "DNA_node_types.h"
+
#include "RNA_access.h"
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_shader.h"
+#include "GPU_texture.h"
+
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
#include "node_composite_util.hh"
/* **************** Crop ******************** */
namespace blender::nodes::node_composite_crop_cc {
+NODE_STORAGE_FUNCS(NodeTwoXYs)
+
static void cmp_node_crop_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Color>(N_("Image"));
}
@@ -54,6 +69,156 @@ static void node_composit_buts_crop(uiLayout *layout, bContext *UNUSED(C), Point
}
}
+using namespace blender::realtime_compositor;
+
+class CropOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ /* The operation does nothing, so just pass the input through. */
+ if (is_identity()) {
+ get_input("Image").pass_through(get_result("Image"));
+ return;
+ }
+
+ if (get_is_image_crop()) {
+ execute_image_crop();
+ }
+ else {
+ execute_alpha_crop();
+ }
+ }
+
+ /* Crop by replacing areas outside of the cropping bounds with zero alpha. The output have the
+ * same domain as the input image. */
+ void execute_alpha_crop()
+ {
+ GPUShader *shader = shader_manager().get("compositor_alpha_crop");
+ GPU_shader_bind(shader);
+
+ int2 lower_bound, upper_bound;
+ compute_cropping_bounds(lower_bound, upper_bound);
+ GPU_shader_uniform_2iv(shader, "lower_bound", lower_bound);
+ GPU_shader_uniform_2iv(shader, "upper_bound", upper_bound);
+
+ const Result &input_image = get_input("Image");
+ input_image.bind_as_texture(shader, "input_tx");
+
+ const Domain domain = compute_domain();
+
+ Result &output_image = get_result("Image");
+ output_image.allocate_texture(domain);
+ output_image.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ input_image.unbind_as_texture();
+ output_image.unbind_as_image();
+ GPU_shader_unbind();
+ }
+
+ /* Crop the image into a new size that matches the cropping bounds. */
+ void execute_image_crop()
+ {
+ int2 lower_bound, upper_bound;
+ compute_cropping_bounds(lower_bound, upper_bound);
+
+ /* The image is cropped into nothing, so just return a single zero value. */
+ if (lower_bound.x == upper_bound.x || lower_bound.y == upper_bound.y) {
+ Result &result = get_result("Image");
+ result.allocate_invalid();
+ return;
+ }
+
+ GPUShader *shader = shader_manager().get("compositor_image_crop");
+ GPU_shader_bind(shader);
+
+ GPU_shader_uniform_2iv(shader, "lower_bound", lower_bound);
+
+ const Result &input_image = get_input("Image");
+ input_image.bind_as_texture(shader, "input_tx");
+
+ const int2 size = upper_bound - lower_bound;
+
+ Result &output_image = get_result("Image");
+ output_image.allocate_texture(Domain(size, compute_domain().transformation));
+ output_image.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, size);
+
+ input_image.unbind_as_texture();
+ output_image.unbind_as_image();
+ GPU_shader_unbind();
+ }
+
+ /* If true, the image should actually be cropped into a new size. Otherwise, if false, the region
+ * outside of the cropping bounds will be set to a zero alpha value. */
+ bool get_is_image_crop()
+ {
+ return bnode().custom1;
+ }
+
+ bool get_is_relative()
+ {
+ return bnode().custom2;
+ }
+
+ /* Returns true if the operation does nothing and the input can be passed through. */
+ bool is_identity()
+ {
+ const Result &input = get_input("Image");
+ /* Single value inputs can't be cropped and are returned as is. */
+ if (input.is_single_value()) {
+ return true;
+ }
+
+ int2 lower_bound, upper_bound;
+ compute_cropping_bounds(lower_bound, upper_bound);
+ const int2 input_size = input.domain().size;
+ /* The cropping bounds cover the whole image, so no cropping happens. */
+ if (lower_bound == int2(0) && upper_bound == input_size) {
+ return true;
+ }
+
+ return false;
+ }
+
+ void compute_cropping_bounds(int2 &lower_bound, int2 &upper_bound)
+ {
+ const NodeTwoXYs &node_two_xys = node_storage(bnode());
+ const int2 input_size = get_input("Image").domain().size;
+
+ if (get_is_relative()) {
+ /* The cropping bounds are relative to the image size. The factors are in the [0, 1] range,
+ * so it is guaranteed that they won't go over the input image size. */
+ lower_bound.x = input_size.x * node_two_xys.fac_x1;
+ lower_bound.y = input_size.y * node_two_xys.fac_y2;
+ upper_bound.x = input_size.x * node_two_xys.fac_x2;
+ upper_bound.y = input_size.y * node_two_xys.fac_y1;
+ }
+ else {
+ /* Make sure the bounds don't go over the input image size. */
+ lower_bound.x = min_ii(node_two_xys.x1, input_size.x);
+ lower_bound.y = min_ii(node_two_xys.y2, input_size.y);
+ upper_bound.x = min_ii(node_two_xys.x2, input_size.x);
+ upper_bound.y = min_ii(node_two_xys.y1, input_size.y);
+ }
+
+ /* Make sure upper bound is actually higher than the lower bound. */
+ lower_bound.x = min_ii(lower_bound.x, upper_bound.x);
+ lower_bound.y = min_ii(lower_bound.y, upper_bound.y);
+ upper_bound.x = max_ii(lower_bound.x, upper_bound.x);
+ upper_bound.y = max_ii(lower_bound.y, upper_bound.y);
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new CropOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_crop_cc
void register_node_type_cmp_crop()
@@ -67,6 +232,7 @@ void register_node_type_cmp_crop()
ntype.draw_buttons = file_ns::node_composit_buts_crop;
node_type_init(&ntype, file_ns::node_composit_init_crop);
node_type_storage(&ntype, "NodeTwoXYs", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc
index 9193f91087a..7e5544381a4 100644
--- a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc
@@ -17,12 +17,17 @@
#include "BKE_context.h"
#include "BKE_cryptomatte.hh"
#include "BKE_global.h"
+#include "BKE_image.h"
#include "BKE_lib_id.h"
#include "BKE_library.h"
#include "BKE_main.h"
#include "MEM_guardedalloc.h"
+#include "RE_pipeline.h"
+
+#include "COM_node_operation.hh"
+
#include <optional>
/* -------------------------------------------------------------------- */
@@ -102,7 +107,6 @@ static blender::bke::cryptomatte::CryptomatteSessionPtr cryptomatte_init_from_no
return session;
}
-extern "C" {
static CryptomatteEntry *cryptomatte_find(const NodeCryptomatte &n, float encoded_hash)
{
LISTBASE_FOREACH (CryptomatteEntry *, entry, &n.entries) {
@@ -296,6 +300,25 @@ static bool node_poll_cryptomatte(bNodeType *UNUSED(ntype),
return false;
}
+using namespace blender::realtime_compositor;
+
+class CryptoMatteOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ get_result("Matte").allocate_invalid();
+ get_result("Pick").allocate_invalid();
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new CryptoMatteOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_cryptomatte_cc
void register_node_type_cmp_cryptomatte()
@@ -313,6 +336,8 @@ void register_node_type_cmp_cryptomatte()
ntype.poll = file_ns::node_poll_cryptomatte;
node_type_storage(
&ntype, "NodeCryptomatte", file_ns::node_free_cryptomatte, file_ns::node_copy_cryptomatte);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
+
nodeRegisterType(&ntype);
}
@@ -347,7 +372,7 @@ int ntreeCompositCryptomatteRemoveSocket(bNodeTree *ntree, bNode *node)
return 1;
}
-namespace blender::nodes::node_composite_cryptomatte_cc {
+namespace blender::nodes::node_composite_legacy_cryptomatte_cc {
static void node_init_cryptomatte_legacy(bNodeTree *ntree, bNode *node)
{
@@ -362,22 +387,43 @@ static void node_init_cryptomatte_legacy(bNodeTree *ntree, bNode *node)
ntreeCompositCryptomatteAddSocket(ntree, node);
}
-} // namespace blender::nodes::node_composite_cryptomatte_cc
+using namespace blender::realtime_compositor;
+
+class CryptoMatteOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("image").pass_through(get_result("Image"));
+ get_result("Matte").allocate_invalid();
+ get_result("Pick").allocate_invalid();
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new CryptoMatteOperation(context, node);
+}
+
+} // namespace blender::nodes::node_composite_legacy_cryptomatte_cc
void register_node_type_cmp_cryptomatte_legacy()
{
- namespace legacy_file_ns = blender::nodes::node_composite_cryptomatte_cc;
+ namespace legacy_file_ns = blender::nodes::node_composite_legacy_cryptomatte_cc;
namespace file_ns = blender::nodes::node_composite_cryptomatte_cc;
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_CRYPTOMATTE_LEGACY, "Cryptomatte", NODE_CLASS_MATTE);
node_type_socket_templates(&ntype, nullptr, file_ns::cmp_node_cryptomatte_out);
- node_type_init(&ntype, file_ns::node_init_cryptomatte_legacy);
+ node_type_init(&ntype, legacy_file_ns::node_init_cryptomatte_legacy);
node_type_storage(
&ntype, "NodeCryptomatte", file_ns::node_free_cryptomatte, file_ns::node_copy_cryptomatte);
+ ntype.gather_link_search_ops = nullptr;
+ ntype.get_compositor_operation = legacy_file_ns::get_compositor_operation;
+
nodeRegisterType(&ntype);
}
/** \} */
-}
diff --git a/source/blender/nodes/composite/nodes/node_composite_curves.cc b/source/blender/nodes/composite/nodes/node_composite_curves.cc
index fff0d467f75..bf45e219730 100644
--- a/source/blender/nodes/composite/nodes/node_composite_curves.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_curves.cc
@@ -5,14 +5,23 @@
* \ingroup cmpnodes
*/
+#include "BLI_math_base.h"
+
+#include "BKE_colortools.h"
+
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_node_operation.hh"
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** CURVE Time ******************** */
-namespace blender::nodes::node_composite_curves_cc {
+namespace blender::nodes::node_composite_time_curves_cc {
static void cmp_node_time_declare(NodeDeclarationBuilder &b)
{
@@ -27,11 +36,65 @@ static void node_composit_init_curves_time(bNodeTree *UNUSED(ntree), bNode *node
node->storage = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
}
-} // namespace blender::nodes::node_composite_curves_cc
+using namespace blender::realtime_compositor;
+
+class TimeCurveOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ Result &result = get_result("Fac");
+ result.allocate_single_value();
+
+ CurveMapping *curve_mapping = const_cast<CurveMapping *>(get_curve_mapping());
+ BKE_curvemapping_init(curve_mapping);
+ const float time = BKE_curvemapping_evaluateF(curve_mapping, 0, compute_normalized_time());
+ result.set_float_value(clamp_f(time, 0.0f, 1.0f));
+ }
+
+ const CurveMapping *get_curve_mapping()
+ {
+ return static_cast<const CurveMapping *>(bnode().storage);
+ }
+
+ int get_start_time()
+ {
+ return bnode().custom1;
+ }
+
+ int get_end_time()
+ {
+ return bnode().custom2;
+ }
+
+ float compute_normalized_time()
+ {
+ const int frame_number = context().get_frame_number();
+ if (frame_number < get_start_time()) {
+ return 0.0f;
+ }
+ if (frame_number > get_end_time()) {
+ return 1.0f;
+ }
+ if (get_start_time() == get_end_time()) {
+ return 0.0f;
+ }
+ return static_cast<float>(frame_number - get_start_time()) /
+ static_cast<float>(get_end_time() - get_start_time());
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new TimeCurveOperation(context, node);
+}
+
+} // namespace blender::nodes::node_composite_time_curves_cc
void register_node_type_cmp_curve_time()
{
- namespace file_ns = blender::nodes::node_composite_curves_cc;
+ namespace file_ns = blender::nodes::node_composite_time_curves_cc;
static bNodeType ntype;
@@ -40,17 +103,22 @@ void register_node_type_cmp_curve_time()
node_type_size(&ntype, 200, 140, 320);
node_type_init(&ntype, file_ns::node_composit_init_curves_time);
node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
/* **************** CURVE VEC ******************** */
-namespace blender::nodes::node_composite_curves_cc {
+namespace blender::nodes::node_composite_vector_curves_cc {
static void cmp_node_curve_vec_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Vector>(N_("Vector")).default_value({0.0f, 0.0f, 0.0f}).min(-1.0f).max(1.0f);
+ b.add_input<decl::Vector>(N_("Vector"))
+ .default_value({0.0f, 0.0f, 0.0f})
+ .min(-1.0f)
+ .max(1.0f)
+ .compositor_domain_priority(0);
b.add_output<decl::Vector>(N_("Vector"));
}
@@ -64,11 +132,63 @@ static void node_buts_curvevec(uiLayout *layout, bContext *UNUSED(C), PointerRNA
uiTemplateCurveMapping(layout, ptr, "mapping", 'v', false, false, false, false);
}
-} // namespace blender::nodes::node_composite_curves_cc
+using namespace blender::realtime_compositor;
+
+class VectorCurvesShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ CurveMapping *curve_mapping = const_cast<CurveMapping *>(get_curve_mapping());
+
+ BKE_curvemapping_init(curve_mapping);
+ float *band_values;
+ int band_size;
+ BKE_curvemapping_table_RGBA(curve_mapping, &band_values, &band_size);
+ float band_layer;
+ GPUNodeLink *band_texture = GPU_color_band(material, band_size, band_values, &band_layer);
+
+ float start_slopes[CM_TOT];
+ float end_slopes[CM_TOT];
+ BKE_curvemapping_compute_slopes(curve_mapping, start_slopes, end_slopes);
+ float range_minimums[CM_TOT];
+ BKE_curvemapping_get_range_minimums(curve_mapping, range_minimums);
+ float range_dividers[CM_TOT];
+ BKE_curvemapping_compute_range_dividers(curve_mapping, range_dividers);
+
+ GPU_stack_link(material,
+ &bnode(),
+ "curves_vector",
+ inputs,
+ outputs,
+ band_texture,
+ GPU_constant(&band_layer),
+ GPU_uniform(range_minimums),
+ GPU_uniform(range_dividers),
+ GPU_uniform(start_slopes),
+ GPU_uniform(end_slopes));
+ }
+
+ const CurveMapping *get_curve_mapping()
+ {
+ return static_cast<const CurveMapping *>(bnode().storage);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new VectorCurvesShaderNode(node);
+}
+
+} // namespace blender::nodes::node_composite_vector_curves_cc
void register_node_type_cmp_curve_vec()
{
- namespace file_ns = blender::nodes::node_composite_curves_cc;
+ namespace file_ns = blender::nodes::node_composite_vector_curves_cc;
static bNodeType ntype;
@@ -78,19 +198,26 @@ void register_node_type_cmp_curve_vec()
node_type_size(&ntype, 200, 140, 320);
node_type_init(&ntype, file_ns::node_composit_init_curve_vec);
node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
/* **************** CURVE RGB ******************** */
-namespace blender::nodes::node_composite_curves_cc {
+namespace blender::nodes::node_composite_rgb_curves_cc {
static void cmp_node_rgbcurves_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(-1.0f).max(1.0f).subtype(
- PROP_FACTOR);
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Float>(N_("Fac"))
+ .default_value(1.0f)
+ .min(-1.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(1);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_input<decl::Color>(N_("Black Level")).default_value({0.0f, 0.0f, 0.0f, 1.0f});
b.add_input<decl::Color>(N_("White Level")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
b.add_output<decl::Color>(N_("Image"));
@@ -101,11 +228,105 @@ static void node_composit_init_curve_rgb(bNodeTree *UNUSED(ntree), bNode *node)
node->storage = BKE_curvemapping_add(4, 0.0f, 0.0f, 1.0f, 1.0f);
}
-} // namespace blender::nodes::node_composite_curves_cc
+using namespace blender::realtime_compositor;
+
+class RGBCurvesShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ CurveMapping *curve_mapping = const_cast<CurveMapping *>(get_curve_mapping());
+
+ BKE_curvemapping_init(curve_mapping);
+ float *band_values;
+ int band_size;
+ BKE_curvemapping_table_RGBA(curve_mapping, &band_values, &band_size);
+ float band_layer;
+ GPUNodeLink *band_texture = GPU_color_band(material, band_size, band_values, &band_layer);
+
+ float start_slopes[CM_TOT];
+ float end_slopes[CM_TOT];
+ BKE_curvemapping_compute_slopes(curve_mapping, start_slopes, end_slopes);
+ float range_minimums[CM_TOT];
+ BKE_curvemapping_get_range_minimums(curve_mapping, range_minimums);
+ float range_dividers[CM_TOT];
+ BKE_curvemapping_compute_range_dividers(curve_mapping, range_dividers);
+
+ if (curve_mapping->tone == CURVE_TONE_FILMLIKE) {
+ GPU_stack_link(material,
+ &bnode(),
+ "curves_film_like",
+ inputs,
+ outputs,
+ band_texture,
+ GPU_constant(&band_layer),
+ GPU_uniform(&range_minimums[3]),
+ GPU_uniform(&range_dividers[3]),
+ GPU_uniform(&start_slopes[3]),
+ GPU_uniform(&end_slopes[3]));
+ return;
+ }
+
+ const float min = 0.0f;
+ const float max = 1.0f;
+ GPU_link(material,
+ "clamp_value",
+ get_input_link("Fac"),
+ GPU_constant(&min),
+ GPU_constant(&max),
+ &get_input("Fac").link);
+
+ /* If the RGB curves do nothing, use a function that skips RGB computations. */
+ if (BKE_curvemapping_is_map_identity(curve_mapping, 0) &&
+ BKE_curvemapping_is_map_identity(curve_mapping, 1) &&
+ BKE_curvemapping_is_map_identity(curve_mapping, 2)) {
+ GPU_stack_link(material,
+ &bnode(),
+ "curves_combined_only",
+ inputs,
+ outputs,
+ band_texture,
+ GPU_constant(&band_layer),
+ GPU_uniform(&range_minimums[3]),
+ GPU_uniform(&range_dividers[3]),
+ GPU_uniform(&start_slopes[3]),
+ GPU_uniform(&end_slopes[3]));
+ return;
+ }
+
+ GPU_stack_link(material,
+ &bnode(),
+ "curves_combined_rgb",
+ inputs,
+ outputs,
+ band_texture,
+ GPU_constant(&band_layer),
+ GPU_uniform(range_minimums),
+ GPU_uniform(range_dividers),
+ GPU_uniform(start_slopes),
+ GPU_uniform(end_slopes));
+ }
+
+ const CurveMapping *get_curve_mapping()
+ {
+ return static_cast<const CurveMapping *>(bnode().storage);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new RGBCurvesShaderNode(node);
+}
+
+} // namespace blender::nodes::node_composite_rgb_curves_cc
void register_node_type_cmp_curve_rgb()
{
- namespace file_ns = blender::nodes::node_composite_curves_cc;
+ namespace file_ns = blender::nodes::node_composite_rgb_curves_cc;
static bNodeType ntype;
@@ -114,6 +335,7 @@ void register_node_type_cmp_curve_rgb()
node_type_size(&ntype, 200, 140, 320);
node_type_init(&ntype, file_ns::node_composit_init_curve_rgb);
node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_defocus.cc b/source/blender/nodes/composite/nodes/node_composite_defocus.cc
index 83dd397ff1f..94b4908a1bd 100644
--- a/source/blender/nodes/composite/nodes/node_composite_defocus.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_defocus.cc
@@ -12,6 +12,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* ************ Defocus Node ****************** */
@@ -81,6 +83,23 @@ static void node_composit_buts_defocus(uiLayout *layout, bContext *C, PointerRNA
uiItemR(sub, ptr, "z_scale", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class DefocusOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new DefocusOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_defocus_cc
void register_node_type_cmp_defocus()
@@ -94,6 +113,7 @@ void register_node_type_cmp_defocus()
ntype.draw_buttons = file_ns::node_composit_buts_defocus;
node_type_init(&ntype, file_ns::node_composit_init_defocus);
node_type_storage(&ntype, "NodeDefocus", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_denoise.cc b/source/blender/nodes/composite/nodes/node_composite_denoise.cc
index 051a2580ef9..0452e7cd943 100644
--- a/source/blender/nodes/composite/nodes/node_composite_denoise.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_denoise.cc
@@ -10,6 +10,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
namespace blender::nodes::node_composite_denoise_cc {
@@ -52,6 +54,23 @@ static void node_composit_buts_denoise(uiLayout *layout, bContext *UNUSED(C), Po
uiItemR(layout, ptr, "use_hdr", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class DenoiseOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new DenoiseOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_denoise_cc
void register_node_type_cmp_denoise()
@@ -65,6 +84,7 @@ void register_node_type_cmp_denoise()
ntype.draw_buttons = file_ns::node_composit_buts_denoise;
node_type_init(&ntype, file_ns::node_composit_init_denonise);
node_type_storage(&ntype, "NodeDenoise", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_despeckle.cc b/source/blender/nodes/composite/nodes/node_composite_despeckle.cc
index 66a18cfa369..aa6725b8750 100644
--- a/source/blender/nodes/composite/nodes/node_composite_despeckle.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_despeckle.cc
@@ -8,6 +8,11 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_shader.h"
+
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
#include "node_composite_util.hh"
/* **************** FILTER ******************** */
@@ -16,8 +21,15 @@ namespace blender::nodes::node_composite_despeckle_cc {
static void cmp_node_despeckle_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Float>(N_("Fac"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(1);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Color>(N_("Image"));
}
@@ -36,6 +48,61 @@ static void node_composit_buts_despeckle(uiLayout *layout, bContext *UNUSED(C),
uiItemR(col, ptr, "threshold_neighbor", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class DespeckleOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ const Result &input_image = get_input("Image");
+ /* Single value inputs can't be despeckled and are returned as is. */
+ if (input_image.is_single_value()) {
+ get_input("Image").pass_through(get_result("Image"));
+ return;
+ }
+
+ GPUShader *shader = shader_manager().get("compositor_despeckle");
+ GPU_shader_bind(shader);
+
+ GPU_shader_uniform_1f(shader, "threshold", get_threshold());
+ GPU_shader_uniform_1f(shader, "neighbor_threshold", get_neighbor_threshold());
+
+ input_image.bind_as_texture(shader, "input_tx");
+
+ const Result &factor_image = get_input("Fac");
+ factor_image.bind_as_texture(shader, "factor_tx");
+
+ const Domain domain = compute_domain();
+ Result &output_image = get_result("Image");
+ output_image.allocate_texture(domain);
+ output_image.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ GPU_shader_unbind();
+ output_image.unbind_as_image();
+ input_image.unbind_as_texture();
+ factor_image.unbind_as_texture();
+ }
+
+ float get_threshold()
+ {
+ return bnode().custom3;
+ }
+
+ float get_neighbor_threshold()
+ {
+ return bnode().custom4;
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new DespeckleOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_despeckle_cc
void register_node_type_cmp_despeckle()
@@ -49,6 +116,7 @@ void register_node_type_cmp_despeckle()
ntype.draw_buttons = file_ns::node_composit_buts_despeckle;
ntype.flag |= NODE_PREVIEW;
node_type_init(&ntype, file_ns::node_composit_init_despeckle);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_diff_matte.cc b/source/blender/nodes/composite/nodes/node_composite_diff_matte.cc
index 20dd61a9725..8912d00a9be 100644
--- a/source/blender/nodes/composite/nodes/node_composite_diff_matte.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_diff_matte.cc
@@ -8,18 +8,28 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* ******************* channel Difference Matte ********************************* */
namespace blender::nodes::node_composite_diff_matte_cc {
+NODE_STORAGE_FUNCS(NodeChroma)
+
static void cmp_node_diff_matte_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image 1")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Color>(N_("Image 2")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image 1"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Color>(N_("Image 2"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(1);
b.add_output<decl::Color>(N_("Image"));
- b.add_output<decl::Color>(N_("Matte"));
+ b.add_output<decl::Float>(N_("Matte"));
}
static void node_composit_init_diff_matte(bNodeTree *UNUSED(ntree), bNode *node)
@@ -40,6 +50,45 @@ static void node_composit_buts_diff_matte(uiLayout *layout, bContext *UNUSED(C),
uiItemR(col, ptr, "falloff", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class DifferenceMatteShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ const float tolerance = get_tolerance();
+ const float falloff = get_falloff();
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_difference_matte",
+ inputs,
+ outputs,
+ GPU_uniform(&tolerance),
+ GPU_uniform(&falloff));
+ }
+
+ float get_tolerance()
+ {
+ return node_storage(bnode()).t1;
+ }
+
+ float get_falloff()
+ {
+ return node_storage(bnode()).t2;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new DifferenceMatteShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_diff_matte_cc
void register_node_type_cmp_diff_matte()
@@ -54,6 +103,7 @@ void register_node_type_cmp_diff_matte()
ntype.flag |= NODE_PREVIEW;
node_type_init(&ntype, file_ns::node_composit_init_diff_matte);
node_type_storage(&ntype, "NodeChroma", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_dilate.cc b/source/blender/nodes/composite/nodes/node_composite_dilate.cc
index 9bdb9ae0837..551dfacb276 100644
--- a/source/blender/nodes/composite/nodes/node_composite_dilate.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_dilate.cc
@@ -5,17 +5,36 @@
* \ingroup cmpnodes
*/
+#include <cmath>
+
+#include "BLI_array.hh"
+#include "BLI_assert.h"
+#include "BLI_math_base.hh"
+
+#include "DNA_scene_types.h"
+
#include "RNA_access.h"
#include "UI_interface.h"
#include "UI_resources.h"
+#include "RE_pipeline.h"
+
+#include "GPU_shader.h"
+#include "GPU_state.h"
+#include "GPU_texture.h"
+
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
#include "node_composite_util.hh"
/* **************** Dilate/Erode ******************** */
namespace blender::nodes::node_composite_dilate_cc {
+NODE_STORAGE_FUNCS(NodeDilateErode)
+
static void cmp_node_dilate_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Float>(N_("Mask")).default_value(0.0f).min(0.0f).max(1.0f);
@@ -34,15 +53,477 @@ static void node_composit_buts_dilateerode(uiLayout *layout, bContext *UNUSED(C)
uiItemR(layout, ptr, "mode", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
uiItemR(layout, ptr, "distance", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
switch (RNA_enum_get(ptr, "mode")) {
- case CMP_NODE_DILATEERODE_DISTANCE_THRESH:
+ case CMP_NODE_DILATE_ERODE_DISTANCE_THRESHOLD:
uiItemR(layout, ptr, "edge", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
break;
- case CMP_NODE_DILATEERODE_DISTANCE_FEATHER:
+ case CMP_NODE_DILATE_ERODE_DISTANCE_FEATHER:
uiItemR(layout, ptr, "falloff", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
break;
}
}
+using namespace blender::realtime_compositor;
+
+/* Computes a falloff that is equal to 1 at an input of zero and decrease to zero at an input of 1,
+ * with the rate of decrease depending on the falloff type. */
+static float compute_distance_falloff(float x, int falloff_type)
+{
+ x = 1.0f - x;
+
+ switch (falloff_type) {
+ case PROP_SMOOTH:
+ return 3.0f * x * x - 2.0f * x * x * x;
+ case PROP_SPHERE:
+ return std::sqrt(2.0f * x - x * x);
+ case PROP_ROOT:
+ return std::sqrt(x);
+ case PROP_SHARP:
+ return x * x;
+ case PROP_INVSQUARE:
+ return x * (2.0f - x);
+ case PROP_LIN:
+ return x;
+ default:
+ BLI_assert_unreachable();
+ return x;
+ }
+}
+
+/* A helper class that computes and caches 1D GPU textures containing the weights of the separable
+ * Gaussian filter of the given radius as well as an inverse distance falloff of the given type and
+ * radius. The weights and falloffs are symmetric, because the Gaussian and falloff functions are
+ * all even functions. Consequently, only the positive half of the filter is computed and the
+ * shader takes that into consideration. */
+class SymmetricSeparableMorphologicalDistanceFeatherWeights {
+ private:
+ int radius_ = 1;
+ int falloff_type_ = PROP_SMOOTH;
+ GPUTexture *weights_texture_ = nullptr;
+ GPUTexture *distance_falloffs_texture_ = nullptr;
+
+ public:
+ ~SymmetricSeparableMorphologicalDistanceFeatherWeights()
+ {
+ if (weights_texture_) {
+ GPU_texture_free(weights_texture_);
+ }
+
+ if (distance_falloffs_texture_) {
+ GPU_texture_free(distance_falloffs_texture_);
+ }
+ }
+
+ /* Check if textures containing the weights and distance falloffs were already computed for the
+ * given distance falloff type and radius. If such textures exists, do nothing, otherwise, free
+ * the already computed textures and recompute it with the given distance falloff type and
+ * radius. */
+ void update(int radius, int falloff_type)
+ {
+ if (weights_texture_ && distance_falloffs_texture_ && falloff_type == falloff_type_ &&
+ radius == radius_) {
+ return;
+ }
+
+ radius_ = radius;
+ falloff_type_ = falloff_type;
+
+ compute_weights();
+ compute_distance_falloffs();
+ }
+
+ void compute_weights()
+ {
+ if (weights_texture_) {
+ GPU_texture_free(weights_texture_);
+ }
+
+ /* The size of filter is double the radius plus 1, but since the filter is symmetric, we only
+ * compute half of it and no doubling happens. We add 1 to make sure the filter size is always
+ * odd and there is a center weight. */
+ const int size = radius_ + 1;
+ Array<float> weights(size);
+
+ float sum = 0.0f;
+
+ /* First, compute the center weight. */
+ const float center_weight = RE_filter_value(R_FILTER_GAUSS, 0.0f);
+ weights[0] = center_weight;
+ sum += center_weight;
+
+ /* Second, compute the other weights in the positive direction, making sure to add double the
+ * weight to the sum of weights because the filter is symmetric and we only loop over half of
+ * it. Skip the center weight already computed by dropping the front index. */
+ const float scale = radius_ > 0.0f ? 1.0f / radius_ : 0.0f;
+ for (const int i : weights.index_range().drop_front(1)) {
+ const float weight = RE_filter_value(R_FILTER_GAUSS, i * scale);
+ weights[i] = weight;
+ sum += weight * 2.0f;
+ }
+
+ /* Finally, normalize the weights. */
+ for (const int i : weights.index_range()) {
+ weights[i] /= sum;
+ }
+
+ weights_texture_ = GPU_texture_create_1d("Weights", size, 1, GPU_R16F, weights.data());
+ }
+
+ void compute_distance_falloffs()
+ {
+ if (distance_falloffs_texture_) {
+ GPU_texture_free(distance_falloffs_texture_);
+ }
+
+ /* The size of the distance falloffs is double the radius plus 1, but since the falloffs are
+ * symmetric, we only compute half of them and no doubling happens. We add 1 to make sure the
+ * falloffs size is always odd and there is a center falloff. */
+ const int size = radius_ + 1;
+ Array<float> falloffs(size);
+
+ /* Compute the distance falloffs in the positive direction only, because the falloffs are
+ * symmetric. */
+ const float scale = radius_ > 0.0f ? 1.0f / radius_ : 0.0f;
+ for (const int i : falloffs.index_range()) {
+ falloffs[i] = compute_distance_falloff(i * scale, falloff_type_);
+ }
+
+ distance_falloffs_texture_ = GPU_texture_create_1d(
+ "Distance Factors", size, 1, GPU_R16F, falloffs.data());
+ }
+
+ void bind_weights_as_texture(GPUShader *shader, const char *texture_name)
+ {
+ const int texture_image_unit = GPU_shader_get_texture_binding(shader, texture_name);
+ GPU_texture_bind(weights_texture_, texture_image_unit);
+ }
+
+ void unbind_weights_as_texture()
+ {
+ GPU_texture_unbind(weights_texture_);
+ }
+
+ void bind_distance_falloffs_as_texture(GPUShader *shader, const char *texture_name)
+ {
+ const int texture_image_unit = GPU_shader_get_texture_binding(shader, texture_name);
+ GPU_texture_bind(distance_falloffs_texture_, texture_image_unit);
+ }
+
+ void unbind_distance_falloffs_as_texture()
+ {
+ GPU_texture_unbind(distance_falloffs_texture_);
+ }
+};
+
+class DilateErodeOperation : public NodeOperation {
+ private:
+ /* Cached symmetric blur weights and distance falloffs for the distance feature method. */
+ SymmetricSeparableMorphologicalDistanceFeatherWeights distance_feather_weights_;
+
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ if (is_identity()) {
+ get_input("Mask").pass_through(get_result("Mask"));
+ return;
+ }
+
+ switch (get_method()) {
+ case CMP_NODE_DILATE_ERODE_STEP:
+ execute_step();
+ return;
+ case CMP_NODE_DILATE_ERODE_DISTANCE:
+ execute_distance();
+ return;
+ case CMP_NODE_DILATE_ERODE_DISTANCE_THRESHOLD:
+ execute_distance_threshold();
+ return;
+ case CMP_NODE_DILATE_ERODE_DISTANCE_FEATHER:
+ execute_distance_feather();
+ return;
+ default:
+ BLI_assert_unreachable();
+ return;
+ }
+ }
+
+ /* ----------------------------
+ * Step Morphological Operator.
+ * ---------------------------- */
+
+ void execute_step()
+ {
+ GPUTexture *horizontal_pass_result = execute_step_horizontal_pass();
+ execute_step_vertical_pass(horizontal_pass_result);
+ }
+
+ GPUTexture *execute_step_horizontal_pass()
+ {
+ GPUShader *shader = shader_manager().get(get_morphological_step_shader_name());
+ GPU_shader_bind(shader);
+
+ /* Pass the absolute value of the distance. We have specialized shaders for each sign. */
+ GPU_shader_uniform_1i(shader, "radius", math::abs(get_distance()));
+
+ const Result &input_mask = get_input("Mask");
+ input_mask.bind_as_texture(shader, "input_tx");
+
+ /* We allocate an output image of a transposed size, that is, with a height equivalent to the
+ * width of the input and vice versa. This is done as a performance optimization. The shader
+ * will process the image horizontally and write it to the intermediate output transposed. Then
+ * the vertical pass will execute the same horizontal pass shader, but since its input is
+ * transposed, it will effectively do a vertical pass and write to the output transposed,
+ * effectively undoing the transposition in the horizontal pass. This is done to improve
+ * spatial cache locality in the shader and to avoid having two separate shaders for each of
+ * the passes. */
+ const Domain domain = compute_domain();
+ const int2 transposed_domain = int2(domain.size.y, domain.size.x);
+
+ GPUTexture *horizontal_pass_result = texture_pool().acquire_color(transposed_domain);
+ const int image_unit = GPU_shader_get_texture_binding(shader, "output_img");
+ GPU_texture_image_bind(horizontal_pass_result, image_unit);
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ GPU_shader_unbind();
+ input_mask.unbind_as_texture();
+ GPU_texture_image_unbind(horizontal_pass_result);
+
+ return horizontal_pass_result;
+ }
+
+ void execute_step_vertical_pass(GPUTexture *horizontal_pass_result)
+ {
+ GPUShader *shader = shader_manager().get(get_morphological_step_shader_name());
+ GPU_shader_bind(shader);
+
+ /* Pass the absolute value of the distance. We have specialized shaders for each sign. */
+ GPU_shader_uniform_1i(shader, "radius", math::abs(get_distance()));
+
+ GPU_memory_barrier(GPU_BARRIER_TEXTURE_FETCH);
+ const int texture_image_unit = GPU_shader_get_texture_binding(shader, "input_tx");
+ GPU_texture_bind(horizontal_pass_result, texture_image_unit);
+
+ const Domain domain = compute_domain();
+ Result &output_mask = get_result("Mask");
+ output_mask.allocate_texture(domain);
+ output_mask.bind_as_image(shader, "output_img");
+
+ /* Notice that the domain is transposed, see the note on the horizontal pass method for more
+ * information on the reasoning behind this. */
+ compute_dispatch_threads_at_least(shader, int2(domain.size.y, domain.size.x));
+
+ GPU_shader_unbind();
+ output_mask.unbind_as_image();
+ GPU_texture_unbind(horizontal_pass_result);
+ }
+
+ const char *get_morphological_step_shader_name()
+ {
+ if (get_distance() > 0) {
+ return "compositor_morphological_step_dilate";
+ }
+ return "compositor_morphological_step_erode";
+ }
+
+ /* --------------------------------
+ * Distance Morphological Operator.
+ * -------------------------------- */
+
+ void execute_distance()
+ {
+ GPUShader *shader = shader_manager().get(get_morphological_distance_shader_name());
+ GPU_shader_bind(shader);
+
+ /* Pass the absolute value of the distance. We have specialized shaders for each sign. */
+ GPU_shader_uniform_1i(shader, "radius", math::abs(get_distance()));
+
+ const Result &input_mask = get_input("Mask");
+ input_mask.bind_as_texture(shader, "input_tx");
+
+ const Domain domain = compute_domain();
+ Result &output_mask = get_result("Mask");
+ output_mask.allocate_texture(domain);
+ output_mask.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ GPU_shader_unbind();
+ output_mask.unbind_as_image();
+ input_mask.unbind_as_texture();
+ }
+
+ const char *get_morphological_distance_shader_name()
+ {
+ if (get_distance() > 0) {
+ return "compositor_morphological_distance_dilate";
+ }
+ return "compositor_morphological_distance_erode";
+ }
+
+ /* ------------------------------------------
+ * Distance Threshold Morphological Operator.
+ * ------------------------------------------ */
+
+ void execute_distance_threshold()
+ {
+ GPUShader *shader = shader_manager().get("compositor_morphological_distance_threshold");
+ GPU_shader_bind(shader);
+
+ GPU_shader_uniform_1f(shader, "inset", get_inset());
+ GPU_shader_uniform_1i(shader, "radius", get_morphological_distance_threshold_radius());
+ GPU_shader_uniform_1i(shader, "distance", get_distance());
+
+ const Result &input_mask = get_input("Mask");
+ input_mask.bind_as_texture(shader, "input_tx");
+
+ const Domain domain = compute_domain();
+ Result &output_mask = get_result("Mask");
+ output_mask.allocate_texture(domain);
+ output_mask.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ GPU_shader_unbind();
+ output_mask.unbind_as_image();
+ input_mask.unbind_as_texture();
+ }
+
+ /* See the discussion in the implementation for more information. */
+ int get_morphological_distance_threshold_radius()
+ {
+ return static_cast<int>(math::ceil(get_inset())) + math::abs(get_distance());
+ }
+
+ /* ----------------------------------------
+ * Distance Feather Morphological Operator.
+ * ---------------------------------------- */
+
+ void execute_distance_feather()
+ {
+ GPUTexture *horizontal_pass_result = execute_distance_feather_horizontal_pass();
+ execute_distance_feather_vertical_pass(horizontal_pass_result);
+ }
+
+ GPUTexture *execute_distance_feather_horizontal_pass()
+ {
+ GPUShader *shader = shader_manager().get(get_morphological_distance_feather_shader_name());
+ GPU_shader_bind(shader);
+
+ const Result &input_image = get_input("Mask");
+ input_image.bind_as_texture(shader, "input_tx");
+
+ distance_feather_weights_.update(math::abs(get_distance()), node_storage(bnode()).falloff);
+ distance_feather_weights_.bind_weights_as_texture(shader, "weights_tx");
+ distance_feather_weights_.bind_distance_falloffs_as_texture(shader, "falloffs_tx");
+
+ /* We allocate an output image of a transposed size, that is, with a height equivalent to the
+ * width of the input and vice versa. This is done as a performance optimization. The shader
+ * will process the image horizontally and write it to the intermediate output transposed. Then
+ * the vertical pass will execute the same horizontal pass shader, but since its input is
+ * transposed, it will effectively do a vertical pass and write to the output transposed,
+ * effectively undoing the transposition in the horizontal pass. This is done to improve
+ * spatial cache locality in the shader and to avoid having two separate shaders for each of
+ * the passes. */
+ const Domain domain = compute_domain();
+ const int2 transposed_domain = int2(domain.size.y, domain.size.x);
+
+ GPUTexture *horizontal_pass_result = texture_pool().acquire_color(transposed_domain);
+ const int image_unit = GPU_shader_get_texture_binding(shader, "output_img");
+ GPU_texture_image_bind(horizontal_pass_result, image_unit);
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ GPU_shader_unbind();
+ input_image.unbind_as_texture();
+ distance_feather_weights_.unbind_weights_as_texture();
+ distance_feather_weights_.unbind_distance_falloffs_as_texture();
+ GPU_texture_image_unbind(horizontal_pass_result);
+
+ return horizontal_pass_result;
+ }
+
+ void execute_distance_feather_vertical_pass(GPUTexture *horizontal_pass_result)
+ {
+ GPUShader *shader = shader_manager().get(get_morphological_distance_feather_shader_name());
+ GPU_shader_bind(shader);
+
+ GPU_memory_barrier(GPU_BARRIER_TEXTURE_FETCH);
+ const int texture_image_unit = GPU_shader_get_texture_binding(shader, "input_tx");
+ GPU_texture_bind(horizontal_pass_result, texture_image_unit);
+
+ distance_feather_weights_.update(math::abs(get_distance()), node_storage(bnode()).falloff);
+ distance_feather_weights_.bind_weights_as_texture(shader, "weights_tx");
+ distance_feather_weights_.bind_distance_falloffs_as_texture(shader, "falloffs_tx");
+
+ const Domain domain = compute_domain();
+ Result &output_image = get_result("Mask");
+ output_image.allocate_texture(domain);
+ output_image.bind_as_image(shader, "output_img");
+
+ /* Notice that the domain is transposed, see the note on the horizontal pass method for more
+ * information on the reasoning behind this. */
+ compute_dispatch_threads_at_least(shader, int2(domain.size.y, domain.size.x));
+
+ GPU_shader_unbind();
+ output_image.unbind_as_image();
+ distance_feather_weights_.unbind_weights_as_texture();
+ distance_feather_weights_.unbind_distance_falloffs_as_texture();
+ GPU_texture_unbind(horizontal_pass_result);
+ }
+
+ const char *get_morphological_distance_feather_shader_name()
+ {
+ if (get_distance() > 0) {
+ return "compositor_morphological_distance_feather_dilate";
+ }
+ return "compositor_morphological_distance_feather_erode";
+ }
+
+ /* ---------------
+ * Common Methods.
+ * --------------- */
+
+ bool is_identity()
+ {
+ const Result &input = get_input("Mask");
+ if (input.is_single_value()) {
+ return true;
+ }
+
+ if (get_method() == CMP_NODE_DILATE_ERODE_DISTANCE_THRESHOLD && get_inset() != 0.0f) {
+ return false;
+ }
+
+ if (get_distance() == 0) {
+ return true;
+ }
+
+ return false;
+ }
+
+ int get_distance()
+ {
+ return bnode().custom2;
+ }
+
+ float get_inset()
+ {
+ return bnode().custom3;
+ }
+
+ CMPNodeDilateErodeMethod get_method()
+ {
+ return (CMPNodeDilateErodeMethod)bnode().custom1;
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new DilateErodeOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_dilate_cc
void register_node_type_cmp_dilateerode()
@@ -57,6 +538,7 @@ void register_node_type_cmp_dilateerode()
node_type_init(&ntype, file_ns::node_composit_init_dilateerode);
node_type_storage(
&ntype, "NodeDilateErode", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_directionalblur.cc b/source/blender/nodes/composite/nodes/node_composite_directionalblur.cc
index 3d82ab04fc9..6e6bec70283 100644
--- a/source/blender/nodes/composite/nodes/node_composite_directionalblur.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_directionalblur.cc
@@ -5,16 +5,30 @@
* \ingroup cmpnodes
*/
+#include "BLI_float3x3.hh"
+#include "BLI_math_base.hh"
+#include "BLI_math_vec_types.hh"
+#include "BLI_math_vector.hh"
+
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_shader.h"
+
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
#include "node_composite_util.hh"
namespace blender::nodes::node_composite_directionalblur_cc {
+NODE_STORAGE_FUNCS(NodeDBlurData)
+
static void cmp_node_directional_blur_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Color>(N_("Image"));
}
@@ -51,6 +65,129 @@ static void node_composit_buts_dblur(uiLayout *layout, bContext *UNUSED(C), Poin
uiItemR(layout, ptr, "zoom", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class DirectionalBlurOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ if (is_identity()) {
+ get_input("Image").pass_through(get_result("Image"));
+ return;
+ }
+
+ GPUShader *shader = shader_manager().get("compositor_directional_blur");
+ GPU_shader_bind(shader);
+
+ /* The number of iterations does not cover the original image, that is, the image with no
+ * transformation. So add an extra iteration for the original image and put that into
+ * consideration in the shader. */
+ GPU_shader_uniform_1i(shader, "iterations", get_iterations() + 1);
+ GPU_shader_uniform_mat3_as_mat4(shader, "inverse_transformation", get_transformation().ptr());
+
+ const Result &input_image = get_input("Image");
+ input_image.bind_as_texture(shader, "input_tx");
+
+ GPU_texture_filter_mode(input_image.texture(), true);
+ GPU_texture_wrap_mode(input_image.texture(), false, false);
+
+ const Domain domain = compute_domain();
+ Result &output_image = get_result("Image");
+ output_image.allocate_texture(domain);
+ output_image.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ GPU_shader_unbind();
+ output_image.unbind_as_image();
+ input_image.unbind_as_texture();
+ }
+
+ /* Get the amount of translation that will be applied on each iteration. The translation is in
+ * the negative x direction rotated in the clock-wise direction, hence the negative sign for the
+ * rotation and translation vector. */
+ float2 get_translation()
+ {
+ const float diagonal_length = math::length(float2(get_input("Image").domain().size));
+ const float translation_amount = diagonal_length * node_storage(bnode()).distance;
+ const float3x3 rotation = float3x3::from_rotation(-node_storage(bnode()).angle);
+ return rotation * float2(-translation_amount / get_iterations(), 0.0f);
+ }
+
+ /* Get the amount of rotation that will be applied on each iteration. */
+ float get_rotation()
+ {
+ return node_storage(bnode()).spin / get_iterations();
+ }
+
+ /* Get the amount of scale that will be applied on each iteration. The scale is identity when the
+ * user supplies 0, so we add 1. */
+ float2 get_scale()
+ {
+ return float2(1.0f + node_storage(bnode()).zoom / get_iterations());
+ }
+
+ float2 get_origin()
+ {
+ const float2 center = float2(node_storage(bnode()).center_x, node_storage(bnode()).center_y);
+ return float2(get_input("Image").domain().size) * center;
+ }
+
+ float3x3 get_transformation()
+ {
+ /* Construct the transformation that will be applied on each iteration. */
+ const float3x3 transformation = float3x3::from_translation_rotation_scale(
+ get_translation(), get_rotation(), get_scale());
+ /* Change the origin of the transformation to the user-specified origin. */
+ const float3x3 origin_transformation = float3x3::from_origin_transformation(transformation,
+ get_origin());
+ /* The shader will transform the coordinates, not the image itself, so take the inverse. */
+ return origin_transformation.inverted();
+ }
+
+ /* The actual number of iterations is 2 to the power of the user supplied iterations. The power
+ * is implemented using a bit shift. But also make sure it doesn't exceed the upper limit which
+ * is the number of diagonal pixels. */
+ int get_iterations()
+ {
+ const int iterations = 2 << (node_storage(bnode()).iter - 1);
+ const int upper_limit = math::ceil(math::length(float2(get_input("Image").domain().size)));
+ return math::min(iterations, upper_limit);
+ }
+
+ /* Returns true if the operation does nothing and the input can be passed through. */
+ bool is_identity()
+ {
+ const Result &input = get_input("Image");
+ /* Single value inputs can't be blurred and are returned as is. */
+ if (input.is_single_value()) {
+ return true;
+ }
+
+ /* If any of the following options are non-zero, then the operation is not an identity. */
+ if (node_storage(bnode()).distance != 0.0f) {
+ return false;
+ }
+
+ if (node_storage(bnode()).spin != 0.0f) {
+ return false;
+ }
+
+ if (node_storage(bnode()).zoom != 0.0f) {
+ return false;
+ }
+
+ return true;
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new DirectionalBlurOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_directionalblur_cc
void register_node_type_cmp_dblur()
@@ -65,6 +202,7 @@ void register_node_type_cmp_dblur()
node_type_init(&ntype, file_ns::node_composit_init_dblur);
node_type_storage(
&ntype, "NodeDBlurData", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_displace.cc b/source/blender/nodes/composite/nodes/node_composite_displace.cc
index 0b0d42cbb08..1049f2fa4a9 100644
--- a/source/blender/nodes/composite/nodes/node_composite_displace.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_displace.cc
@@ -5,6 +5,8 @@
* \ingroup cmpnodes
*/
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Displace ******************** */
@@ -24,6 +26,23 @@ static void cmp_node_displace_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Color>(N_("Image"));
}
+using namespace blender::realtime_compositor;
+
+class DisplaceOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new DisplaceOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_displace_cc
void register_node_type_cmp_displace()
@@ -34,6 +53,7 @@ void register_node_type_cmp_displace()
cmp_node_type_base(&ntype, CMP_NODE_DISPLACE, "Displace", NODE_CLASS_DISTORT);
ntype.declare = file_ns::cmp_node_displace_declare;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_distance_matte.cc b/source/blender/nodes/composite/nodes/node_composite_distance_matte.cc
index a8646d8498e..6a786571f43 100644
--- a/source/blender/nodes/composite/nodes/node_composite_distance_matte.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_distance_matte.cc
@@ -8,16 +8,26 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* ******************* channel Distance Matte ********************************* */
namespace blender::nodes::node_composite_distance_matte_cc {
+NODE_STORAGE_FUNCS(NodeChroma)
+
static void cmp_node_distance_matte_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Color>(N_("Key Color")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Color>(N_("Key Color"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(1);
b.add_output<decl::Color>(N_("Image"));
b.add_output<decl::Float>(N_("Matte"));
}
@@ -26,7 +36,7 @@ static void node_composit_init_distance_matte(bNodeTree *UNUSED(ntree), bNode *n
{
NodeChroma *c = MEM_cnew<NodeChroma>(__func__);
node->storage = c;
- c->channel = 1;
+ c->channel = CMP_NODE_DISTANCE_MATTE_COLOR_SPACE_RGBA;
c->t1 = 0.1f;
c->t2 = 0.1f;
}
@@ -48,6 +58,61 @@ static void node_composit_buts_distance_matte(uiLayout *layout,
uiItemR(col, ptr, "falloff", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class DistanceMatteShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ const float tolerance = get_tolerance();
+ const float falloff = get_falloff();
+
+ if (get_color_space() == CMP_NODE_DISTANCE_MATTE_COLOR_SPACE_RGBA) {
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_distance_matte_rgba",
+ inputs,
+ outputs,
+ GPU_uniform(&tolerance),
+ GPU_uniform(&falloff));
+ return;
+ }
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_distance_matte_ycca",
+ inputs,
+ outputs,
+ GPU_uniform(&tolerance),
+ GPU_uniform(&falloff));
+ }
+
+ CMPNodeDistanceMatteColorSpace get_color_space()
+ {
+ return (CMPNodeDistanceMatteColorSpace)node_storage(bnode()).channel;
+ }
+
+ float get_tolerance()
+ {
+ return node_storage(bnode()).t1;
+ }
+
+ float get_falloff()
+ {
+ return node_storage(bnode()).t2;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new DistanceMatteShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_distance_matte_cc
void register_node_type_cmp_distance_matte()
@@ -62,6 +127,7 @@ void register_node_type_cmp_distance_matte()
ntype.flag |= NODE_PREVIEW;
node_type_init(&ntype, file_ns::node_composit_init_distance_matte);
node_type_storage(&ntype, "NodeChroma", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_double_edge_mask.cc b/source/blender/nodes/composite/nodes/node_composite_double_edge_mask.cc
index 9dc2b223618..fec7879ed78 100644
--- a/source/blender/nodes/composite/nodes/node_composite_double_edge_mask.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_double_edge_mask.cc
@@ -8,6 +8,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Double Edge Mask ******************** */
@@ -35,6 +37,23 @@ static void node_composit_buts_double_edge_mask(uiLayout *layout,
uiItemR(col, ptr, "edge_mode", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class DoubleEdgeMaskOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Inner Mask").pass_through(get_result("Mask"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new DoubleEdgeMaskOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_double_edge_mask_cc
void register_node_type_cmp_doubleedgemask()
@@ -46,6 +65,7 @@ void register_node_type_cmp_doubleedgemask()
cmp_node_type_base(&ntype, CMP_NODE_DOUBLEEDGEMASK, "Double Edge Mask", NODE_CLASS_MATTE);
ntype.declare = file_ns::cmp_node_double_edge_mask_declare;
ntype.draw_buttons = file_ns::node_composit_buts_double_edge_mask;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_ellipsemask.cc b/source/blender/nodes/composite/nodes/node_composite_ellipsemask.cc
index 4da6a0a442e..7c031b354e5 100644
--- a/source/blender/nodes/composite/nodes/node_composite_ellipsemask.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_ellipsemask.cc
@@ -5,15 +5,26 @@
* \ingroup cmpnodes
*/
+#include <cmath>
+
+#include "BLI_math_vec_types.hh"
+
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_shader.h"
+
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
#include "node_composite_util.hh"
/* **************** SCALAR MATH ******************** */
namespace blender::nodes::node_composite_ellipsemask_cc {
+NODE_STORAGE_FUNCS(NodeEllipseMask)
+
static void cmp_node_ellipsemask_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Float>(N_("Mask")).default_value(0.0f).min(0.0f).max(1.0f);
@@ -46,6 +57,93 @@ static void node_composit_buts_ellipsemask(uiLayout *layout, bContext *UNUSED(C)
uiItemR(layout, ptr, "mask_type", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class EllipseMaskOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ GPUShader *shader = shader_manager().get(get_shader_name());
+ GPU_shader_bind(shader);
+
+ const Domain domain = compute_domain();
+
+ GPU_shader_uniform_2iv(shader, "domain_size", domain.size);
+
+ GPU_shader_uniform_2fv(shader, "location", get_location());
+ GPU_shader_uniform_2fv(shader, "radius", get_size() / 2.0f);
+ GPU_shader_uniform_1f(shader, "cos_angle", std::cos(get_angle()));
+ GPU_shader_uniform_1f(shader, "sin_angle", std::sin(get_angle()));
+
+ const Result &input_mask = get_input("Mask");
+ input_mask.bind_as_texture(shader, "base_mask_tx");
+
+ const Result &value = get_input("Value");
+ value.bind_as_texture(shader, "mask_value_tx");
+
+ Result &output_mask = get_result("Mask");
+ output_mask.allocate_texture(domain);
+ output_mask.bind_as_image(shader, "output_mask_img");
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ input_mask.unbind_as_texture();
+ value.unbind_as_texture();
+ output_mask.unbind_as_image();
+ GPU_shader_unbind();
+ }
+
+ Domain compute_domain() override
+ {
+ if (get_input("Mask").is_single_value()) {
+ return Domain(context().get_output_size());
+ }
+ return get_input("Mask").domain();
+ }
+
+ CMPNodeMaskType get_mask_type()
+ {
+ return (CMPNodeMaskType)bnode().custom1;
+ }
+
+ const char *get_shader_name()
+ {
+ switch (get_mask_type()) {
+ default:
+ case CMP_NODE_MASKTYPE_ADD:
+ return "compositor_ellipse_mask_add";
+ case CMP_NODE_MASKTYPE_SUBTRACT:
+ return "compositor_ellipse_mask_subtract";
+ case CMP_NODE_MASKTYPE_MULTIPLY:
+ return "compositor_ellipse_mask_multiply";
+ case CMP_NODE_MASKTYPE_NOT:
+ return "compositor_ellipse_mask_not";
+ }
+ }
+
+ float2 get_location()
+ {
+ return float2(node_storage(bnode()).x, node_storage(bnode()).y);
+ }
+
+ float2 get_size()
+ {
+ return float2(node_storage(bnode()).width, node_storage(bnode()).height);
+ }
+
+ float get_angle()
+ {
+ return node_storage(bnode()).rotation;
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new EllipseMaskOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_ellipsemask_cc
void register_node_type_cmp_ellipsemask()
@@ -61,6 +159,7 @@ void register_node_type_cmp_ellipsemask()
node_type_init(&ntype, file_ns::node_composit_init_ellipsemask);
node_type_storage(
&ntype, "NodeEllipseMask", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_exposure.cc b/source/blender/nodes/composite/nodes/node_composite_exposure.cc
index 881cfc11058..19b93680ff6 100644
--- a/source/blender/nodes/composite/nodes/node_composite_exposure.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_exposure.cc
@@ -5,6 +5,10 @@
* \ingroup cmpnodes
*/
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** Exposure ******************** */
@@ -13,11 +17,33 @@ namespace blender::nodes::node_composite_exposure_cc {
static void cmp_node_exposure_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Float>(N_("Exposure")).min(-10.0f).max(10.0f);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("Exposure")).min(-10.0f).max(10.0f).compositor_domain_priority(1);
b.add_output<decl::Color>(N_("Image"));
}
+using namespace blender::realtime_compositor;
+
+class ExposureShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), "node_composite_exposure", inputs, outputs);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new ExposureShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_exposure_cc
void register_node_type_cmp_exposure()
@@ -28,6 +54,7 @@ void register_node_type_cmp_exposure()
cmp_node_type_base(&ntype, CMP_NODE_EXPOSURE, "Exposure", NODE_CLASS_OP_COLOR);
ntype.declare = file_ns::cmp_node_exposure_declare;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_filter.cc b/source/blender/nodes/composite/nodes/node_composite_filter.cc
index c343c21feb2..bd7b443e17e 100644
--- a/source/blender/nodes/composite/nodes/node_composite_filter.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_filter.cc
@@ -5,9 +5,14 @@
* \ingroup cmpnodes
*/
+#include "BLI_float3x3.hh"
+
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
#include "node_composite_util.hh"
/* **************** FILTER ******************** */
@@ -16,8 +21,15 @@ namespace blender::nodes::node_composite_filter_cc {
static void cmp_node_filter_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Float>(N_("Fac"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(1);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Color>(N_("Image"));
}
@@ -26,6 +38,119 @@ static void node_composit_buts_filter(uiLayout *layout, bContext *UNUSED(C), Poi
uiItemR(layout, ptr, "filter_type", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class FilterOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ GPUShader *shader = shader_manager().get(get_shader_name());
+ GPU_shader_bind(shader);
+
+ GPU_shader_uniform_mat3_as_mat4(shader, "kernel", get_filter_kernel().ptr());
+
+ const Result &input_image = get_input("Image");
+ input_image.bind_as_texture(shader, "input_tx");
+
+ const Result &factor = get_input("Fac");
+ factor.bind_as_texture(shader, "factor_tx");
+
+ const Domain domain = compute_domain();
+
+ Result &output_image = get_result("Image");
+ output_image.allocate_texture(domain);
+ output_image.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ input_image.unbind_as_texture();
+ factor.unbind_as_texture();
+ output_image.unbind_as_image();
+ GPU_shader_unbind();
+ }
+
+ CMPNodeFilterMethod get_filter_method()
+ {
+ return (CMPNodeFilterMethod)bnode().custom1;
+ }
+
+ float3x3 get_filter_kernel()
+ {
+ /* Initialize the kernels as arrays of rows with the top row first. Edge detection kernels
+ * return the kernel in the X direction, while the kernel in the Y direction will be computed
+ * inside the shader by transposing the kernel in the X direction. */
+ switch (get_filter_method()) {
+ case CMP_NODE_FILTER_SOFT: {
+ const float kernel[3][3] = {{1.0f / 16.0f, 2.0f / 16.0f, 1.0f / 16.0f},
+ {2.0f / 16.0f, 4.0f / 16.0f, 2.0f / 16.0f},
+ {1.0f / 16.0f, 2.0f / 16.0f, 1.0f / 16.0f}};
+ return float3x3(kernel);
+ }
+ case CMP_NODE_FILTER_SHARP_BOX: {
+ const float kernel[3][3] = {
+ {-1.0f, -1.0f, -1.0f}, {-1.0f, 9.0f, -1.0f}, {-1.0f, -1.0f, -1.0f}};
+ return float3x3(kernel);
+ }
+ case CMP_NODE_FILTER_LAPLACE: {
+ const float kernel[3][3] = {{-1.0f / 8.0f, -1.0f / 8.0f, -1.0f / 8.0f},
+ {-1.0f / 8.0f, 1.0f, -1.0f / 8.0f},
+ {-1.0f / 8.0f, -1.0f / 8.0f, -1.0f / 8.0f}};
+ return float3x3(kernel);
+ }
+ case CMP_NODE_FILTER_SOBEL: {
+ const float kernel[3][3] = {{1.0f, 0.0f, -1.0f}, {2.0f, 0.0f, -2.0f}, {1.0f, 0.0f, -1.0f}};
+ return float3x3(kernel);
+ }
+ case CMP_NODE_FILTER_PREWITT: {
+ const float kernel[3][3] = {{1.0f, 0.0f, -1.0f}, {1.0f, 0.0f, -1.0f}, {1.0f, 0.0f, -1.0f}};
+ return float3x3(kernel);
+ }
+ case CMP_NODE_FILTER_KIRSCH: {
+ const float kernel[3][3] = {
+ {5.0f, -3.0f, -2.0f}, {5.0f, -3.0f, -2.0f}, {5.0f, -3.0f, -2.0f}};
+ return float3x3(kernel);
+ }
+ case CMP_NODE_FILTER_SHADOW: {
+ const float kernel[3][3] = {{1.0f, 2.0f, 1.0f}, {0.0f, 1.0f, 0.0f}, {-1.0f, -2.0f, -1.0f}};
+ return float3x3(kernel);
+ }
+ case CMP_NODE_FILTER_SHARP_DIAMOND: {
+ const float kernel[3][3] = {
+ {0.0f, -1.0f, 0.0f}, {-1.0f, 5.0f, -1.0f}, {0.0f, -1.0f, 0.0f}};
+ return float3x3(kernel);
+ }
+ default: {
+ const float kernel[3][3] = {{0.0f, 0.0f, 0.0f}, {0.0f, 1.0f, 0.0f}, {0.0f, 0.0f, 0.0f}};
+ return float3x3(kernel);
+ }
+ }
+ }
+
+ const char *get_shader_name()
+ {
+ switch (get_filter_method()) {
+ case CMP_NODE_FILTER_LAPLACE:
+ case CMP_NODE_FILTER_SOBEL:
+ case CMP_NODE_FILTER_PREWITT:
+ case CMP_NODE_FILTER_KIRSCH:
+ return "compositor_edge_filter";
+ case CMP_NODE_FILTER_SOFT:
+ case CMP_NODE_FILTER_SHARP_BOX:
+ case CMP_NODE_FILTER_SHADOW:
+ case CMP_NODE_FILTER_SHARP_DIAMOND:
+ default:
+ return "compositor_filter";
+ }
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new FilterOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_filter_cc
void register_node_type_cmp_filter()
@@ -39,6 +164,7 @@ void register_node_type_cmp_filter()
ntype.draw_buttons = file_ns::node_composit_buts_filter;
ntype.labelfunc = node_filter_label;
ntype.flag |= NODE_PREVIEW;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_flip.cc b/source/blender/nodes/composite/nodes/node_composite_flip.cc
index 37b9a2d020d..aaa2b565ed2 100644
--- a/source/blender/nodes/composite/nodes/node_composite_flip.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_flip.cc
@@ -5,9 +5,18 @@
* \ingroup cmpnodes
*/
+#include "BLI_assert.h"
+#include "BLI_utildefines.h"
+
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_shader.h"
+#include "GPU_texture.h"
+
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
#include "node_composite_util.hh"
/* **************** Flip ******************** */
@@ -16,7 +25,9 @@ namespace blender::nodes::node_composite_flip_cc {
static void cmp_node_flip_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Color>(N_("Image"));
}
@@ -25,6 +36,56 @@ static void node_composit_buts_flip(uiLayout *layout, bContext *UNUSED(C), Point
uiItemR(layout, ptr, "axis", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class FlipOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ Result &input = get_input("Image");
+ Result &result = get_result("Image");
+
+ /* Can't flip a single value, pass it through to the output. */
+ if (input.is_single_value()) {
+ input.pass_through(result);
+ return;
+ }
+
+ GPUShader *shader = shader_manager().get("compositor_flip");
+ GPU_shader_bind(shader);
+
+ GPU_shader_uniform_1b(
+ shader, "flip_x", ELEM(get_flip_mode(), CMP_NODE_FLIP_X, CMP_NODE_FLIP_X_Y));
+ GPU_shader_uniform_1b(
+ shader, "flip_y", ELEM(get_flip_mode(), CMP_NODE_FLIP_Y, CMP_NODE_FLIP_X_Y));
+
+ input.bind_as_texture(shader, "input_tx");
+
+ const Domain domain = compute_domain();
+
+ result.allocate_texture(domain);
+ result.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ input.unbind_as_texture();
+ result.unbind_as_image();
+ GPU_shader_unbind();
+ }
+
+ CMPNodeFlipMode get_flip_mode()
+ {
+ return (CMPNodeFlipMode)bnode().custom1;
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new FlipOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_flip_cc
void register_node_type_cmp_flip()
@@ -36,6 +97,7 @@ void register_node_type_cmp_flip()
cmp_node_type_base(&ntype, CMP_NODE_FLIP, "Flip", NODE_CLASS_DISTORT);
ntype.declare = file_ns::cmp_node_flip_declare;
ntype.draw_buttons = file_ns::node_composit_buts_flip;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_gamma.cc b/source/blender/nodes/composite/nodes/node_composite_gamma.cc
index b4b8502e915..660d8068231 100644
--- a/source/blender/nodes/composite/nodes/node_composite_gamma.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_gamma.cc
@@ -5,6 +5,10 @@
* \ingroup cmpnodes
*/
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** Gamma Tools ******************** */
@@ -13,15 +17,38 @@ namespace blender::nodes::node_composite_gamma_cc {
static void cmp_node_gamma_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_input<decl::Float>(N_("Gamma"))
.default_value(1.0f)
.min(0.001f)
.max(10.0f)
- .subtype(PROP_UNSIGNED);
+ .subtype(PROP_UNSIGNED)
+ .compositor_domain_priority(1);
b.add_output<decl::Color>(N_("Image"));
}
+using namespace blender::realtime_compositor;
+
+class GammaShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), "node_composite_gamma", inputs, outputs);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new GammaShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_gamma_cc
void register_node_type_cmp_gamma()
@@ -32,6 +59,7 @@ void register_node_type_cmp_gamma()
cmp_node_type_base(&ntype, CMP_NODE_GAMMA, "Gamma", NODE_CLASS_OP_COLOR);
ntype.declare = file_ns::cmp_node_gamma_declare;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_glare.cc b/source/blender/nodes/composite/nodes/node_composite_glare.cc
index 7f21d30cfa6..33577d5caf8 100644
--- a/source/blender/nodes/composite/nodes/node_composite_glare.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_glare.cc
@@ -10,6 +10,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
namespace blender::nodes::node_composite_glare_cc {
@@ -75,6 +77,23 @@ static void node_composit_buts_glare(uiLayout *layout, bContext *UNUSED(C), Poin
}
}
+using namespace blender::realtime_compositor;
+
+class GlareOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new GlareOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_glare_cc
void register_node_type_cmp_glare()
@@ -88,6 +107,7 @@ void register_node_type_cmp_glare()
ntype.draw_buttons = file_ns::node_composit_buts_glare;
node_type_init(&ntype, file_ns::node_composit_init_glare);
node_type_storage(&ntype, "NodeGlare", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_hue_sat_val.cc b/source/blender/nodes/composite/nodes/node_composite_hue_sat_val.cc
index 08a048829df..091864a06f7 100644
--- a/source/blender/nodes/composite/nodes/node_composite_hue_sat_val.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_hue_sat_val.cc
@@ -5,6 +5,10 @@
* \ingroup cmpnodes
*/
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** Hue Saturation ******************** */
@@ -13,22 +17,56 @@ namespace blender::nodes::node_composite_hue_sat_val_cc {
static void cmp_node_huesatval_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Float>(N_("Hue")).default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("Hue"))
+ .default_value(0.5f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(1);
b.add_input<decl::Float>(N_("Saturation"))
.default_value(1.0f)
.min(0.0f)
.max(2.0f)
- .subtype(PROP_FACTOR);
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(2);
b.add_input<decl::Float>(N_("Value"))
.default_value(1.0f)
.min(0.0f)
.max(2.0f)
- .subtype(PROP_FACTOR);
- b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(3);
+ b.add_input<decl::Float>(N_("Fac"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(4);
b.add_output<decl::Color>(N_("Image"));
}
+using namespace blender::realtime_compositor;
+
+class HueSaturationValueShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), "node_composite_hue_saturation_value", inputs, outputs);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new HueSaturationValueShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_hue_sat_val_cc
void register_node_type_cmp_hue_sat()
@@ -39,6 +77,7 @@ void register_node_type_cmp_hue_sat()
cmp_node_type_base(&ntype, CMP_NODE_HUE_SAT, "Hue Saturation Value", NODE_CLASS_OP_COLOR);
ntype.declare = file_ns::cmp_node_huesatval_declare;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc b/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc
index bb5e6bf06a8..6333860a19b 100644
--- a/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc
@@ -5,14 +5,29 @@
* \ingroup cmpnodes
*/
+#include "BKE_colortools.h"
+
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
+#include "BKE_colortools.h"
+
namespace blender::nodes::node_composite_huecorrect_cc {
static void cmp_node_huecorrect_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Float>(N_("Fac"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(1);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Color>(N_("Image"));
}
@@ -33,6 +48,53 @@ static void node_composit_init_huecorrect(bNodeTree *UNUSED(ntree), bNode *node)
cumapping->cur = 1;
}
+using namespace blender::realtime_compositor;
+
+class HueCorrectShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ CurveMapping *curve_mapping = const_cast<CurveMapping *>(get_curve_mapping());
+
+ BKE_curvemapping_init(curve_mapping);
+ float *band_values;
+ int band_size;
+ BKE_curvemapping_table_RGBA(curve_mapping, &band_values, &band_size);
+ float band_layer;
+ GPUNodeLink *band_texture = GPU_color_band(material, band_size, band_values, &band_layer);
+
+ float range_minimums[CM_TOT];
+ BKE_curvemapping_get_range_minimums(curve_mapping, range_minimums);
+ float range_dividers[CM_TOT];
+ BKE_curvemapping_compute_range_dividers(curve_mapping, range_dividers);
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_hue_correct",
+ inputs,
+ outputs,
+ band_texture,
+ GPU_constant(&band_layer),
+ GPU_uniform(range_minimums),
+ GPU_uniform(range_dividers));
+ }
+
+ const CurveMapping *get_curve_mapping()
+ {
+ return static_cast<const CurveMapping *>(bnode().storage);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new HueCorrectShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_huecorrect_cc
void register_node_type_cmp_huecorrect()
@@ -46,6 +108,7 @@ void register_node_type_cmp_huecorrect()
node_type_size(&ntype, 320, 140, 500);
node_type_init(&ntype, file_ns::node_composit_init_huecorrect);
node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_id_mask.cc b/source/blender/nodes/composite/nodes/node_composite_id_mask.cc
index 25ab9aa63fc..ac8456cb931 100644
--- a/source/blender/nodes/composite/nodes/node_composite_id_mask.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_id_mask.cc
@@ -8,6 +8,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** ID Mask ******************** */
@@ -26,6 +28,23 @@ static void node_composit_buts_id_mask(uiLayout *layout, bContext *UNUSED(C), Po
uiItemR(layout, ptr, "use_antialiasing", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class IDMaskOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("ID value").pass_through(get_result("Alpha"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new IDMaskOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_id_mask_cc
void register_node_type_cmp_idmask()
@@ -37,6 +56,7 @@ void register_node_type_cmp_idmask()
cmp_node_type_base(&ntype, CMP_NODE_ID_MASK, "ID Mask", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::cmp_node_idmask_declare;
ntype.draw_buttons = file_ns::node_composit_buts_id_mask;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_image.cc b/source/blender/nodes/composite/nodes/node_composite_image.cc
index d071e9f13db..4d1eff0b940 100644
--- a/source/blender/nodes/composite/nodes/node_composite_image.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_image.cc
@@ -8,23 +8,34 @@
#include "node_composite_util.hh"
#include "BLI_linklist.h"
+#include "BLI_math_vec_types.hh"
#include "BLI_utildefines.h"
#include "BKE_context.h"
#include "BKE_global.h"
+#include "BKE_image.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
#include "BKE_scene.h"
+#include "DEG_depsgraph_query.h"
+
#include "DNA_scene_types.h"
#include "RE_engine.h"
+#include "RE_pipeline.h"
#include "RNA_access.h"
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_shader.h"
+#include "GPU_texture.h"
+
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
/* **************** IMAGE (and RenderResult, multilayer image) ******************** */
static bNodeSocketTemplate cmp_node_rlayers_out[] = {
@@ -431,6 +442,215 @@ static void node_composit_copy_image(bNodeTree *UNUSED(dest_ntree),
}
}
+using namespace blender::realtime_compositor;
+
+class ImageOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ if (!is_valid()) {
+ allocate_invalid();
+ return;
+ }
+
+ update_image_frame_number();
+
+ for (const bNodeSocket *output : this->node()->output_sockets()) {
+ compute_output(output->identifier);
+ }
+ }
+
+ /* Returns true if the node results can be computed, otherwise, returns false. */
+ bool is_valid()
+ {
+ Image *image = get_image();
+ ImageUser *image_user = get_image_user();
+ if (!image || !image_user) {
+ return false;
+ }
+
+ if (BKE_image_is_multilayer(image)) {
+ if (!image->rr) {
+ return false;
+ }
+
+ RenderLayer *render_layer = get_render_layer();
+ if (!render_layer) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /* Allocate all needed outputs as invalid. This should be called when is_valid returns false. */
+ void allocate_invalid()
+ {
+ for (const bNodeSocket *output : this->node()->output_sockets()) {
+ if (!should_compute_output(output->identifier)) {
+ continue;
+ }
+
+ Result &result = get_result(output->identifier);
+ result.allocate_invalid();
+ }
+ }
+
+ /* Compute the effective frame number of the image if it was animated and invalidate the cached
+ * GPU texture if the computed frame number is different. */
+ void update_image_frame_number()
+ {
+ BKE_image_user_frame_calc(get_image(), get_image_user(), context().get_frame_number());
+ }
+
+ void compute_output(StringRef identifier)
+ {
+ if (!should_compute_output(identifier)) {
+ return;
+ }
+
+ ImageUser image_user = compute_image_user_for_output(identifier);
+ GPUTexture *image_texture = BKE_image_get_gpu_texture(get_image(), &image_user, nullptr);
+
+ const int2 size = int2(GPU_texture_width(image_texture), GPU_texture_height(image_texture));
+ Result &result = get_result(identifier);
+ result.allocate_texture(Domain(size));
+
+ GPUShader *shader = shader_manager().get(get_shader_name(identifier));
+ GPU_shader_bind(shader);
+
+ const int input_unit = GPU_shader_get_texture_binding(shader, "input_tx");
+ GPU_texture_bind(image_texture, input_unit);
+
+ result.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, size);
+
+ GPU_shader_unbind();
+ GPU_texture_unbind(image_texture);
+ result.unbind_as_image();
+ }
+
+ /* Get a copy of the image user that is appropriate to retrieve the image buffer for the output
+ * with the given identifier. This essentially sets the appropriate pass and view indices that
+ * corresponds to the output. */
+ ImageUser compute_image_user_for_output(StringRef identifier)
+ {
+ ImageUser image_user = *get_image_user();
+
+ /* Set the needed view. */
+ image_user.view = get_view_index();
+
+ /* Set the needed pass. */
+ if (BKE_image_is_multilayer(get_image())) {
+ image_user.pass = get_pass_index(get_pass_name(identifier));
+ BKE_image_multilayer_index(get_image()->rr, &image_user);
+ }
+ else {
+ BKE_image_multiview_index(get_image(), &image_user);
+ }
+
+ return image_user;
+ }
+
+ /* Get the shader that should be used to compute the output with the given identifier. The
+ * shaders just copy the retrieved image textures into the results except for the alpha output,
+ * which extracts the alpha and writes it to the result instead. Note that a call to a host
+ * texture copy doesn't work because results are stored in a different half float formats. */
+ const char *get_shader_name(StringRef identifier)
+ {
+ if (identifier == "Alpha") {
+ return "compositor_extract_alpha_from_color";
+ }
+ else if (get_result(identifier).type() == ResultType::Color) {
+ return "compositor_convert_color_to_half_color";
+ }
+ else {
+ return "compositor_convert_float_to_half_float";
+ }
+ }
+
+ Image *get_image()
+ {
+ return (Image *)bnode().id;
+ }
+
+ ImageUser *get_image_user()
+ {
+ return static_cast<ImageUser *>(bnode().storage);
+ }
+
+ /* Get the render layer selected in the node assuming the image is a multilayer image. */
+ RenderLayer *get_render_layer()
+ {
+ const ListBase *layers = &get_image()->rr->layers;
+ return static_cast<RenderLayer *>(BLI_findlink(layers, get_image_user()->layer));
+ }
+
+ /* Get the name of the pass corresponding to the output with the given identifier assuming the
+ * image is a multilayer image. */
+ const char *get_pass_name(StringRef identifier)
+ {
+ DOutputSocket output = node().output_by_identifier(identifier);
+ return static_cast<NodeImageLayer *>(output->storage)->pass_name;
+ }
+
+ /* Get the index of the pass with the given name in the selected render layer's passes list
+ * assuming the image is a multilayer image. */
+ int get_pass_index(const char *name)
+ {
+ return BLI_findstringindex(&get_render_layer()->passes, name, offsetof(RenderPass, name));
+ }
+
+ /* Get the index of the view selected in the node. If the image is not a multi-view image or only
+ * has a single view, then zero is returned. Otherwise, if the image is a multi-view image, the
+ * index of the selected view is returned. However, note that the value of the view member of the
+ * image user is not the actual index of the view. More specifically, the index 0 is reserved to
+ * denote the special mode of operation "All", which dynamically selects the view whose name
+ * matches the view currently being rendered. It follows that the views are then indexed starting
+ * from 1. So for non zero view values, the actual index of the view is the value of the view
+ * member of the image user minus 1. */
+ int get_view_index()
+ {
+ /* The image is not a multi-view image, so just return zero. */
+ if (!BKE_image_is_multiview(get_image())) {
+ return 0;
+ }
+
+ const ListBase *views = &get_image()->rr->views;
+ /* There is only one view and its index is 0. */
+ if (BLI_listbase_count_at_most(views, 2) < 2) {
+ return 0;
+ }
+
+ const int view = get_image_user()->view;
+ /* The view is not zero, which means it is manually specified and the actual index is then the
+ * view value minus 1. */
+ if (view != 0) {
+ return view - 1;
+ }
+
+ /* Otherwise, the view value is zero, denoting the special mode of operation "All", which finds
+ * the index of the view whose name matches the view currently being rendered. */
+ const char *view_name = context().get_view_name().data();
+ const int matched_view = BLI_findstringindex(views, view_name, offsetof(RenderView, name));
+
+ /* No view matches the view currently being rendered, so fallback to the first view. */
+ if (matched_view == -1) {
+ return 0;
+ }
+
+ return matched_view;
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new ImageOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_image_cc
void register_node_type_cmp_image()
@@ -444,6 +664,7 @@ void register_node_type_cmp_image()
node_type_storage(
&ntype, "ImageUser", file_ns::node_composit_free_image, file_ns::node_composit_copy_image);
node_type_update(&ntype, file_ns::cmp_node_image_update);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
ntype.labelfunc = node_image_label;
ntype.flag |= NODE_PREVIEW;
@@ -467,7 +688,7 @@ const char *node_cmp_rlayers_sock_to_pass(int sock_index)
return (STREQ(name, "Alpha")) ? RE_PASSNAME_COMBINED : name;
}
-namespace blender::nodes::node_composite_image_cc {
+namespace blender::nodes::node_composite_render_layer_cc {
static void node_composit_init_rlayers(const bContext *C, PointerRNA *ptr)
{
@@ -593,11 +814,60 @@ static void node_composit_buts_viewlayers(uiLayout *layout, bContext *C, Pointer
RNA_string_set(&op_ptr, "scene", scene_name);
}
-} // namespace blender::nodes::node_composite_image_cc
+using namespace blender::realtime_compositor;
+
+class RenderLayerOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ const int view_layer = bnode().custom1;
+ GPUTexture *pass_texture = context().get_input_texture(view_layer, SCE_PASS_COMBINED);
+ const int2 size = int2(GPU_texture_width(pass_texture), GPU_texture_height(pass_texture));
+
+ /* Compute image output. */
+ Result &image_result = get_result("Image");
+ image_result.allocate_texture(Domain(size));
+ GPU_texture_copy(image_result.texture(), pass_texture);
+
+ /* Compute alpha output. */
+ Result &alpha_result = get_result("Alpha");
+ alpha_result.allocate_texture(Domain(size));
+
+ GPUShader *shader = shader_manager().get("compositor_extract_alpha_from_color");
+ GPU_shader_bind(shader);
+
+ const int input_unit = GPU_shader_get_texture_binding(shader, "input_tx");
+ GPU_texture_bind(pass_texture, input_unit);
+
+ alpha_result.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, size);
+
+ GPU_shader_unbind();
+ GPU_texture_unbind(pass_texture);
+ alpha_result.unbind_as_image();
+
+ /* Other output passes are not supported for now, so allocate them as invalid. */
+ for (const bNodeSocket *output : this->node()->output_sockets()) {
+ if (!STREQ(output->identifier, "Image") && !STREQ(output->identifier, "Alpha")) {
+ get_result(output->identifier).allocate_invalid();
+ }
+ }
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new RenderLayerOperation(context, node);
+}
+
+} // namespace blender::nodes::node_composite_render_layer_cc
void register_node_type_cmp_rlayers()
{
- namespace file_ns = blender::nodes::node_composite_image_cc;
+ namespace file_ns = blender::nodes::node_composite_render_layer_cc;
static bNodeType ntype;
@@ -606,6 +876,7 @@ void register_node_type_cmp_rlayers()
ntype.draw_buttons = file_ns::node_composit_buts_viewlayers;
ntype.initfunc_api = file_ns::node_composit_init_rlayers;
ntype.poll = file_ns::node_composit_poll_rlayers;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
ntype.flag |= NODE_PREVIEW;
node_type_storage(
&ntype, nullptr, file_ns::node_composit_free_rlayers, file_ns::node_composit_copy_rlayers);
diff --git a/source/blender/nodes/composite/nodes/node_composite_inpaint.cc b/source/blender/nodes/composite/nodes/node_composite_inpaint.cc
index 2958d1b2869..f6e46bef299 100644
--- a/source/blender/nodes/composite/nodes/node_composite_inpaint.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_inpaint.cc
@@ -8,6 +8,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Inpaint/ ******************** */
@@ -25,6 +27,23 @@ static void node_composit_buts_inpaint(uiLayout *layout, bContext *UNUSED(C), Po
uiItemR(layout, ptr, "distance", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class InpaintOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new InpaintOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_inpaint_cc
void register_node_type_cmp_inpaint()
@@ -36,6 +55,7 @@ void register_node_type_cmp_inpaint()
cmp_node_type_base(&ntype, CMP_NODE_INPAINT, "Inpaint", NODE_CLASS_OP_FILTER);
ntype.declare = file_ns::cmp_node_inpaint_declare;
ntype.draw_buttons = file_ns::node_composit_buts_inpaint;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_invert.cc b/source/blender/nodes/composite/nodes/node_composite_invert.cc
index 6dff043537a..4bfcc7b6b9c 100644
--- a/source/blender/nodes/composite/nodes/node_composite_invert.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_invert.cc
@@ -8,6 +8,10 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** INVERT ******************** */
@@ -16,8 +20,15 @@ namespace blender::nodes::node_composite_invert_cc {
static void cmp_node_invert_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
- b.add_input<decl::Color>(N_("Color")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Float>(N_("Fac"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(1);
+ b.add_input<decl::Color>(N_("Color"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Color>(N_("Color"));
}
@@ -35,6 +46,45 @@ static void node_composit_buts_invert(uiLayout *layout, bContext *UNUSED(C), Poi
uiItemR(col, ptr, "invert_alpha", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class InvertShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ const float do_rgb = get_do_rgb();
+ const float do_alpha = get_do_alpha();
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_invert",
+ inputs,
+ outputs,
+ GPU_constant(&do_rgb),
+ GPU_constant(&do_alpha));
+ }
+
+ bool get_do_rgb()
+ {
+ return bnode().custom1 & CMP_CHAN_RGB;
+ }
+
+ bool get_do_alpha()
+ {
+ return bnode().custom1 & CMP_CHAN_A;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new InvertShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_invert_cc
void register_node_type_cmp_invert()
@@ -47,6 +97,7 @@ void register_node_type_cmp_invert()
ntype.declare = file_ns::cmp_node_invert_declare;
ntype.draw_buttons = file_ns::node_composit_buts_invert;
node_type_init(&ntype, file_ns::node_composit_init_invert);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_keying.cc b/source/blender/nodes/composite/nodes/node_composite_keying.cc
index fbfdf2ad3c6..8b584e216cd 100644
--- a/source/blender/nodes/composite/nodes/node_composite_keying.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_keying.cc
@@ -14,6 +14,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Keying ******************** */
@@ -63,6 +65,25 @@ static void node_composit_buts_keying(uiLayout *layout, bContext *UNUSED(C), Poi
uiItemR(layout, ptr, "blur_post", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class KeyingOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ get_result("Matte").allocate_invalid();
+ get_result("Edges").allocate_invalid();
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new KeyingOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_keying_cc
void register_node_type_cmp_keying()
@@ -77,6 +98,7 @@ void register_node_type_cmp_keying()
node_type_init(&ntype, file_ns::node_composit_init_keying);
node_type_storage(
&ntype, "NodeKeyingData", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_keyingscreen.cc b/source/blender/nodes/composite/nodes/node_composite_keyingscreen.cc
index c3ed5cd7aa8..9eec705b6ca 100644
--- a/source/blender/nodes/composite/nodes/node_composite_keyingscreen.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_keyingscreen.cc
@@ -6,16 +6,23 @@
*/
#include "DNA_movieclip_types.h"
+#include "DNA_tracking_types.h"
#include "BLI_math_base.h"
#include "BLI_math_color.h"
+#include "BKE_context.h"
+#include "BKE_lib_id.h"
+#include "BKE_tracking.h"
+
#include "RNA_access.h"
#include "RNA_prototypes.h"
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Keying Screen ******************** */
@@ -27,10 +34,23 @@ static void cmp_node_keyingscreen_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Color>(N_("Screen"));
}
-static void node_composit_init_keyingscreen(bNodeTree *UNUSED(ntree), bNode *node)
+static void node_composit_init_keyingscreen(const bContext *C, PointerRNA *ptr)
{
+ bNode *node = (bNode *)ptr->data;
+
NodeKeyingScreenData *data = MEM_cnew<NodeKeyingScreenData>(__func__);
node->storage = data;
+
+ const Scene *scene = CTX_data_scene(C);
+ if (scene->clip) {
+ MovieClip *clip = scene->clip;
+
+ node->id = &clip->id;
+ id_us_plus(&clip->id);
+
+ const MovieTrackingObject *tracking_object = BKE_tracking_object_get_active(&clip->tracking);
+ BLI_strncpy(data->tracking_object, tracking_object->name, sizeof(data->tracking_object));
+ }
}
static void node_composit_buts_keyingscreen(uiLayout *layout, bContext *C, PointerRNA *ptr)
@@ -60,6 +80,23 @@ static void node_composit_buts_keyingscreen(uiLayout *layout, bContext *C, Point
}
}
+using namespace blender::realtime_compositor;
+
+class KeyingScreenOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_result("Screen").allocate_invalid();
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new KeyingScreenOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_keyingscreen_cc
void register_node_type_cmp_keyingscreen()
@@ -71,9 +108,10 @@ void register_node_type_cmp_keyingscreen()
cmp_node_type_base(&ntype, CMP_NODE_KEYINGSCREEN, "Keying Screen", NODE_CLASS_MATTE);
ntype.declare = file_ns::cmp_node_keyingscreen_declare;
ntype.draw_buttons = file_ns::node_composit_buts_keyingscreen;
- node_type_init(&ntype, file_ns::node_composit_init_keyingscreen);
+ ntype.initfunc_api = file_ns::node_composit_init_keyingscreen;
node_type_storage(
&ntype, "NodeKeyingScreenData", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_lensdist.cc b/source/blender/nodes/composite/nodes/node_composite_lensdist.cc
index 593b7cc9b71..260fccf66d0 100644
--- a/source/blender/nodes/composite/nodes/node_composite_lensdist.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_lensdist.cc
@@ -5,20 +5,50 @@
* \ingroup cmpnodes
*/
+#include "BLI_math_base.h"
+#include "BLI_math_vec_types.hh"
+
#include "RNA_access.h"
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_shader.h"
+#include "GPU_texture.h"
+
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
#include "node_composite_util.hh"
+/* Distortion can't be exactly -1.0 as it will cause infinite pincushion distortion. */
+#define MINIMUM_DISTORTION -0.999f
+/* Arbitrary scaling factor for the dispersion input in projector distortion mode. */
+#define PROJECTOR_DISPERSION_SCALE 5.0f
+/* Arbitrary scaling factor for the dispersion input in screen distortion mode. */
+#define SCREEN_DISPERSION_SCALE 4.0f
+/* Arbitrary scaling factor for the distortion input. */
+#define DISTORTION_SCALE 4.0f
+
namespace blender::nodes::node_composite_lensdist_cc {
+NODE_STORAGE_FUNCS(NodeLensDist)
+
static void cmp_node_lensdist_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Float>(N_("Distort")).default_value(0.0f).min(-0.999f).max(1.0f);
- b.add_input<decl::Float>(N_("Dispersion")).default_value(0.0f).min(0.0f).max(1.0f);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("Distort"))
+ .default_value(0.0f)
+ .min(MINIMUM_DISTORTION)
+ .max(1.0f)
+ .compositor_expects_single_value();
+ b.add_input<decl::Float>(N_("Dispersion"))
+ .default_value(0.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .compositor_expects_single_value();
b.add_output<decl::Color>(N_("Image"));
}
@@ -42,6 +72,173 @@ static void node_composit_buts_lensdist(uiLayout *layout, bContext *UNUSED(C), P
uiItemR(col, ptr, "use_fit", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class LensDistortionOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ if (is_identity()) {
+ get_input("Image").pass_through(get_result("Image"));
+ return;
+ }
+
+ if (get_is_projector()) {
+ execute_projector_distortion();
+ }
+ else {
+ execute_screen_distortion();
+ }
+ }
+
+ void execute_projector_distortion()
+ {
+ GPUShader *shader = shader_manager().get("compositor_projector_lens_distortion");
+ GPU_shader_bind(shader);
+
+ const Result &input_image = get_input("Image");
+ input_image.bind_as_texture(shader, "input_tx");
+
+ GPU_texture_filter_mode(input_image.texture(), true);
+ GPU_texture_wrap_mode(input_image.texture(), false, false);
+
+ const Domain domain = compute_domain();
+
+ const float dispersion = (get_dispersion() * PROJECTOR_DISPERSION_SCALE) / domain.size.x;
+ GPU_shader_uniform_1f(shader, "dispersion", dispersion);
+
+ Result &output_image = get_result("Image");
+ output_image.allocate_texture(domain);
+ output_image.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ input_image.unbind_as_texture();
+ output_image.unbind_as_image();
+ GPU_shader_unbind();
+ }
+
+ void execute_screen_distortion()
+ {
+ GPUShader *shader = shader_manager().get(get_screen_distortion_shader());
+ GPU_shader_bind(shader);
+
+ const Result &input_image = get_input("Image");
+ input_image.bind_as_texture(shader, "input_tx");
+
+ GPU_texture_filter_mode(input_image.texture(), true);
+ GPU_texture_wrap_mode(input_image.texture(), false, false);
+
+ const Domain domain = compute_domain();
+
+ const float3 chromatic_distortion = compute_chromatic_distortion();
+ GPU_shader_uniform_3fv(shader, "chromatic_distortion", chromatic_distortion);
+
+ GPU_shader_uniform_1f(shader, "scale", compute_scale());
+
+ Result &output_image = get_result("Image");
+ output_image.allocate_texture(domain);
+ output_image.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ input_image.unbind_as_texture();
+ output_image.unbind_as_image();
+ GPU_shader_unbind();
+ }
+
+ const char *get_screen_distortion_shader()
+ {
+ if (get_is_jitter()) {
+ return "compositor_screen_lens_distortion_jitter";
+ }
+ return "compositor_screen_lens_distortion";
+ }
+
+ float get_distortion()
+ {
+ const Result &input = get_input("Distort");
+ return clamp_f(input.get_float_value_default(0.0f), MINIMUM_DISTORTION, 1.0f);
+ }
+
+ float get_dispersion()
+ {
+ const Result &input = get_input("Dispersion");
+ return clamp_f(input.get_float_value_default(0.0f), 0.0f, 1.0f);
+ }
+
+ /* Get the distortion amount for each channel. The green channel has a distortion amount that
+ * matches that specified in the node inputs, while the red and blue channels have higher and
+ * lower distortion amounts respectively based on the dispersion value. */
+ float3 compute_chromatic_distortion()
+ {
+ const float green_distortion = get_distortion();
+ const float dispersion = get_dispersion() / SCREEN_DISPERSION_SCALE;
+ const float red_distortion = clamp_f(green_distortion + dispersion, MINIMUM_DISTORTION, 1.0f);
+ const float blue_distortion = clamp_f(green_distortion - dispersion, MINIMUM_DISTORTION, 1.0f);
+ return float3(red_distortion, green_distortion, blue_distortion) * DISTORTION_SCALE;
+ }
+
+ /* The distortion model will distort the image in such a way that the result will no longer
+ * fit the domain of the original image, so we scale the image to account for that. If get_is_fit
+ * is false, then the scaling factor will be such that the furthest pixels horizontally and
+ * vertically are at the boundary of the image. Otherwise, if get_is_fit is true, the scaling
+ * factor will be such that the furthest pixels diagonally are at the corner of the image. */
+ float compute_scale()
+ {
+ const float3 distortion = compute_chromatic_distortion() / DISTORTION_SCALE;
+ const float maximum_distortion = max_fff(distortion[0], distortion[1], distortion[2]);
+
+ if (get_is_fit() && (maximum_distortion > 0.0f)) {
+ return 1.0f / (1.0f + 2.0f * maximum_distortion);
+ }
+ return 1.0f / (1.0f + maximum_distortion);
+ }
+
+ bool get_is_projector()
+ {
+ return node_storage(bnode()).proj;
+ }
+
+ bool get_is_jitter()
+ {
+ return node_storage(bnode()).jit;
+ }
+
+ bool get_is_fit()
+ {
+ return node_storage(bnode()).fit;
+ }
+
+ /* Returns true if the operation does nothing and the input can be passed through. */
+ bool is_identity()
+ {
+ /* The input is a single value and the operation does nothing. */
+ if (get_input("Image").is_single_value()) {
+ return true;
+ }
+
+ /* Projector have zero dispersion and does nothing. */
+ if (get_is_projector() && get_dispersion() == 0.0f) {
+ return true;
+ }
+
+ /* Both distortion and dispersion are zero and the operation does nothing. */
+ if (get_distortion() == 0.0f && get_dispersion() == 0.0f) {
+ return true;
+ }
+
+ return false;
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new LensDistortionOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_lensdist_cc
void register_node_type_cmp_lensdist()
@@ -56,6 +253,7 @@ void register_node_type_cmp_lensdist()
node_type_init(&ntype, file_ns::node_composit_init_lensdist);
node_type_storage(
&ntype, "NodeLensDist", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_levels.cc b/source/blender/nodes/composite/nodes/node_composite_levels.cc
index a30567672f0..2f1ebeb79b5 100644
--- a/source/blender/nodes/composite/nodes/node_composite_levels.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_levels.cc
@@ -8,6 +8,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** LEVELS ******************** */
@@ -31,6 +33,24 @@ static void node_composit_buts_view_levels(uiLayout *layout, bContext *UNUSED(C)
uiItemR(layout, ptr, "channel", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class LevelsOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_result("Mean").allocate_invalid();
+ get_result("Std Dev").allocate_invalid();
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new LevelsOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_levels_cc
void register_node_type_cmp_view_levels()
@@ -44,6 +64,7 @@ void register_node_type_cmp_view_levels()
ntype.draw_buttons = file_ns::node_composit_buts_view_levels;
ntype.flag |= NODE_PREVIEW;
node_type_init(&ntype, file_ns::node_composit_init_view_levels);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_luma_matte.cc b/source/blender/nodes/composite/nodes/node_composite_luma_matte.cc
index 94697a2aafd..59ae62ec411 100644
--- a/source/blender/nodes/composite/nodes/node_composite_luma_matte.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_luma_matte.cc
@@ -5,18 +5,28 @@
* \ingroup cmpnodes
*/
+#include "IMB_colormanagement.h"
+
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* ******************* Luma Matte Node ********************************* */
namespace blender::nodes::node_composite_luma_matte_cc {
+NODE_STORAGE_FUNCS(NodeChroma)
+
static void cmp_node_luma_matte_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Color>(N_("Image"));
b.add_output<decl::Float>(N_("Matte"));
}
@@ -40,6 +50,48 @@ static void node_composit_buts_luma_matte(uiLayout *layout, bContext *UNUSED(C),
col, ptr, "limit_min", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class LuminanceMatteShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ const float high = get_high();
+ const float low = get_low();
+ float luminance_coefficients[3];
+ IMB_colormanagement_get_luminance_coefficients(luminance_coefficients);
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_luminance_matte",
+ inputs,
+ outputs,
+ GPU_uniform(&high),
+ GPU_uniform(&low),
+ GPU_constant(luminance_coefficients));
+ }
+
+ float get_high()
+ {
+ return node_storage(bnode()).t1;
+ }
+
+ float get_low()
+ {
+ return node_storage(bnode()).t2;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new LuminanceMatteShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_luma_matte_cc
void register_node_type_cmp_luma_matte()
@@ -54,6 +106,7 @@ void register_node_type_cmp_luma_matte()
ntype.flag |= NODE_PREVIEW;
node_type_init(&ntype, file_ns::node_composit_init_luma_matte);
node_type_storage(&ntype, "NodeChroma", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_map_range.cc b/source/blender/nodes/composite/nodes/node_composite_map_range.cc
index e52c6d096b9..e72869efa93 100644
--- a/source/blender/nodes/composite/nodes/node_composite_map_range.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_map_range.cc
@@ -8,6 +8,10 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** Map Range ******************** */
@@ -16,11 +20,31 @@ namespace blender::nodes::node_composite_map_range_cc {
static void cmp_node_map_range_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Value")).default_value(1.0f).min(0.0f).max(1.0f);
- b.add_input<decl::Float>(N_("From Min")).default_value(0.0f).min(-10000.0f).max(10000.0f);
- b.add_input<decl::Float>(N_("From Max")).default_value(1.0f).min(-10000.0f).max(10000.0f);
- b.add_input<decl::Float>(N_("To Min")).default_value(0.0f).min(-10000.0f).max(10000.0f);
- b.add_input<decl::Float>(N_("To Max")).default_value(1.0f).min(-10000.0f).max(10000.0f);
+ b.add_input<decl::Float>(N_("Value"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("From Min"))
+ .default_value(0.0f)
+ .min(-10000.0f)
+ .max(10000.0f)
+ .compositor_domain_priority(1);
+ b.add_input<decl::Float>(N_("From Max"))
+ .default_value(1.0f)
+ .min(-10000.0f)
+ .max(10000.0f)
+ .compositor_domain_priority(2);
+ b.add_input<decl::Float>(N_("To Min"))
+ .default_value(0.0f)
+ .min(-10000.0f)
+ .max(10000.0f)
+ .compositor_domain_priority(3);
+ b.add_input<decl::Float>(N_("To Max"))
+ .default_value(1.0f)
+ .min(-10000.0f)
+ .max(10000.0f)
+ .compositor_domain_priority(4);
b.add_output<decl::Float>(N_("Value"));
}
@@ -32,6 +56,38 @@ static void node_composit_buts_map_range(uiLayout *layout, bContext *UNUSED(C),
uiItemR(col, ptr, "use_clamp", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class MapRangeShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ const float should_clamp = get_should_clamp();
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_map_range",
+ inputs,
+ outputs,
+ GPU_constant(&should_clamp));
+ }
+
+ bool get_should_clamp()
+ {
+ return bnode().custom1;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new MapRangeShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_map_range_cc
void register_node_type_cmp_map_range()
@@ -43,6 +99,7 @@ void register_node_type_cmp_map_range()
cmp_node_type_base(&ntype, CMP_NODE_MAP_RANGE, "Map Range", NODE_CLASS_OP_VECTOR);
ntype.declare = file_ns::cmp_node_map_range_declare;
ntype.draw_buttons = file_ns::node_composit_buts_map_range;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_map_uv.cc b/source/blender/nodes/composite/nodes/node_composite_map_uv.cc
index 31961f07ea4..4f660d62c3b 100644
--- a/source/blender/nodes/composite/nodes/node_composite_map_uv.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_map_uv.cc
@@ -8,6 +8,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Map UV ******************** */
@@ -26,6 +28,23 @@ static void node_composit_buts_map_uv(uiLayout *layout, bContext *UNUSED(C), Poi
uiItemR(layout, ptr, "alpha", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class MapUVOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new MapUVOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_map_uv_cc
void register_node_type_cmp_mapuv()
@@ -37,6 +56,7 @@ void register_node_type_cmp_mapuv()
cmp_node_type_base(&ntype, CMP_NODE_MAP_UV, "Map UV", NODE_CLASS_DISTORT);
ntype.declare = file_ns::cmp_node_map_uv_declare;
ntype.draw_buttons = file_ns::node_composit_buts_map_uv;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_map_value.cc b/source/blender/nodes/composite/nodes/node_composite_map_value.cc
index b069cce93fc..e30de39605d 100644
--- a/source/blender/nodes/composite/nodes/node_composite_map_value.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_map_value.cc
@@ -5,20 +5,32 @@
* \ingroup cmpnodes
*/
+#include "BKE_texture.h"
+
#include "RNA_access.h"
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** MAP VALUE ******************** */
namespace blender::nodes::node_composite_map_value_cc {
+NODE_STORAGE_FUNCS(TexMapping)
+
static void cmp_node_map_value_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Value")).default_value(1.0f).min(0.0f).max(1.0f);
+ b.add_input<decl::Float>(N_("Value"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .compositor_domain_priority(0);
b.add_output<decl::Float>(N_("Value"));
}
@@ -48,6 +60,51 @@ static void node_composit_buts_map_value(uiLayout *layout, bContext *UNUSED(C),
uiItemR(sub, ptr, "max", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class MapValueShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ const TexMapping &texture_mapping = node_storage(bnode());
+
+ const float use_min = get_use_min();
+ const float use_max = get_use_max();
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_map_value",
+ inputs,
+ outputs,
+ GPU_uniform(texture_mapping.loc),
+ GPU_uniform(texture_mapping.size),
+ GPU_constant(&use_min),
+ GPU_uniform(texture_mapping.min),
+ GPU_constant(&use_max),
+ GPU_uniform(texture_mapping.max));
+ }
+
+ bool get_use_min()
+ {
+ return node_storage(bnode()).flag & TEXMAP_CLIP_MIN;
+ }
+
+ bool get_use_max()
+ {
+ return node_storage(bnode()).flag & TEXMAP_CLIP_MAX;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new MapValueShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_map_value_cc
void register_node_type_cmp_map_value()
@@ -61,6 +118,7 @@ void register_node_type_cmp_map_value()
ntype.draw_buttons = file_ns::node_composit_buts_map_value;
node_type_init(&ntype, file_ns::node_composit_init_map_value);
node_type_storage(&ntype, "TexMapping", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_mask.cc b/source/blender/nodes/composite/nodes/node_composite_mask.cc
index 5b8fac5d1c0..2372dbae3f2 100644
--- a/source/blender/nodes/composite/nodes/node_composite_mask.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_mask.cc
@@ -10,6 +10,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Mask ******************** */
@@ -74,6 +76,23 @@ static void node_composit_buts_mask(uiLayout *layout, bContext *C, PointerRNA *p
}
}
+using namespace blender::realtime_compositor;
+
+class MaskOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_result("Mask").allocate_invalid();
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new MaskOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_mask_cc
void register_node_type_cmp_mask()
@@ -87,6 +106,7 @@ void register_node_type_cmp_mask()
ntype.draw_buttons = file_ns::node_composit_buts_mask;
node_type_init(&ntype, file_ns::node_composit_init_mask);
ntype.labelfunc = file_ns::node_mask_label;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
node_type_storage(&ntype, "NodeMask", node_free_standard_storage, node_copy_standard_storage);
diff --git a/source/blender/nodes/composite/nodes/node_composite_math.cc b/source/blender/nodes/composite/nodes/node_composite_math.cc
index 7b2eadef2cb..4baf057913e 100644
--- a/source/blender/nodes/composite/nodes/node_composite_math.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_math.cc
@@ -5,6 +5,12 @@
* \ingroup cmpnodes
*/
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
+#include "NOD_math_functions.hh"
+
#include "node_composite_util.hh"
/* **************** SCALAR MATH ******************** */
@@ -13,18 +19,72 @@ namespace blender::nodes::node_composite_math_cc {
static void cmp_node_math_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Value")).default_value(0.5f).min(-10000.0f).max(10000.0f);
+ b.add_input<decl::Float>(N_("Value"))
+ .default_value(0.5f)
+ .min(-10000.0f)
+ .max(10000.0f)
+ .compositor_domain_priority(0);
b.add_input<decl::Float>(N_("Value"), "Value_001")
.default_value(0.5f)
.min(-10000.0f)
- .max(10000.0f);
+ .max(10000.0f)
+ .compositor_domain_priority(1);
b.add_input<decl::Float>(N_("Value"), "Value_002")
.default_value(0.5f)
.min(-10000.0f)
- .max(10000.0f);
+ .max(10000.0f)
+ .compositor_domain_priority(2);
b.add_output<decl::Float>(N_("Value"));
}
+using namespace blender::realtime_compositor;
+
+class MathShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), get_shader_function_name(), inputs, outputs);
+
+ if (!get_should_clamp()) {
+ return;
+ }
+
+ const float min = 0.0f;
+ const float max = 1.0f;
+ GPU_link(material,
+ "clamp_value",
+ get_output("Value").link,
+ GPU_constant(&min),
+ GPU_constant(&max),
+ &get_output("Value").link);
+ }
+
+ NodeMathOperation get_operation()
+ {
+ return (NodeMathOperation)bnode().custom1;
+ }
+
+ const char *get_shader_function_name()
+ {
+ return get_float_math_operation_info(get_operation())->shader_name.c_str();
+ }
+
+ bool get_should_clamp()
+ {
+ return bnode().custom2 & SHD_MATH_CLAMP;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new MathShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_math_cc
void register_node_type_cmp_math()
@@ -37,6 +97,7 @@ void register_node_type_cmp_math()
ntype.declare = file_ns::cmp_node_math_declare;
ntype.labelfunc = node_math_label;
node_type_update(&ntype, node_math_update);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_mixrgb.cc b/source/blender/nodes/composite/nodes/node_composite_mixrgb.cc
index fc11aa188b0..a1fbbfe7d40 100644
--- a/source/blender/nodes/composite/nodes/node_composite_mixrgb.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_mixrgb.cc
@@ -5,6 +5,14 @@
* \ingroup cmpnodes
*/
+#include "BLI_assert.h"
+
+#include "DNA_material_types.h"
+
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** MIX RGB ******************** */
@@ -13,12 +21,122 @@ namespace blender::nodes::node_composite_mixrgb_cc {
static void cmp_node_mixrgb_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Color>(N_("Image"), "Image_001").default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Float>(N_("Fac"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(2);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Color>(N_("Image"), "Image_001")
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(1);
b.add_output<decl::Color>(N_("Image"));
}
+using namespace blender::realtime_compositor;
+
+class MixRGBShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ if (get_use_alpha()) {
+ GPU_link(material,
+ "multiply_by_alpha",
+ get_input_link("Fac"),
+ get_input_link("Image_001"),
+ &get_input("Fac").link);
+ }
+
+ GPU_stack_link(material, &bnode(), get_shader_function_name(), inputs, outputs);
+
+ if (!get_should_clamp()) {
+ return;
+ }
+
+ const float min[4] = {0.0f, 0.0f, 0.0f, 0.0f};
+ const float max[4] = {1.0f, 1.0f, 1.0f, 1.0f};
+ GPU_link(material,
+ "clamp_color",
+ get_output("Image").link,
+ GPU_constant(min),
+ GPU_constant(max),
+ &get_output("Image").link);
+ }
+
+ int get_mode()
+ {
+ return bnode().custom1;
+ }
+
+ const char *get_shader_function_name()
+ {
+ switch (get_mode()) {
+ case MA_RAMP_BLEND:
+ return "mix_blend";
+ case MA_RAMP_ADD:
+ return "mix_add";
+ case MA_RAMP_MULT:
+ return "mix_mult";
+ case MA_RAMP_SUB:
+ return "mix_sub";
+ case MA_RAMP_SCREEN:
+ return "mix_screen";
+ case MA_RAMP_DIV:
+ return "mix_div";
+ case MA_RAMP_DIFF:
+ return "mix_diff";
+ case MA_RAMP_DARK:
+ return "mix_dark";
+ case MA_RAMP_LIGHT:
+ return "mix_light";
+ case MA_RAMP_OVERLAY:
+ return "mix_overlay";
+ case MA_RAMP_DODGE:
+ return "mix_dodge";
+ case MA_RAMP_BURN:
+ return "mix_burn";
+ case MA_RAMP_HUE:
+ return "mix_hue";
+ case MA_RAMP_SAT:
+ return "mix_sat";
+ case MA_RAMP_VAL:
+ return "mix_val";
+ case MA_RAMP_COLOR:
+ return "mix_color";
+ case MA_RAMP_SOFT:
+ return "mix_soft";
+ case MA_RAMP_LINEAR:
+ return "mix_linear";
+ }
+
+ BLI_assert_unreachable();
+ return nullptr;
+ }
+
+ bool get_use_alpha()
+ {
+ return bnode().custom2 & SHD_MIXRGB_USE_ALPHA;
+ }
+
+ bool get_should_clamp()
+ {
+ return bnode().custom2 & SHD_MIXRGB_CLAMP;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new MixRGBShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_mixrgb_cc
void register_node_type_cmp_mix_rgb()
@@ -31,6 +149,7 @@ void register_node_type_cmp_mix_rgb()
ntype.flag |= NODE_PREVIEW;
ntype.declare = file_ns::cmp_node_mixrgb_declare;
ntype.labelfunc = node_blend_label;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_movieclip.cc b/source/blender/nodes/composite/nodes/node_composite_movieclip.cc
index a4d5f294fe0..b9d9620a214 100644
--- a/source/blender/nodes/composite/nodes/node_composite_movieclip.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_movieclip.cc
@@ -5,8 +5,13 @@
* \ingroup cmpnodes
*/
+#include "BLI_math_vec_types.hh"
+
#include "BKE_context.h"
#include "BKE_lib_id.h"
+#include "BKE_movieclip.h"
+#include "BKE_tracking.h"
+
#include "DNA_defaults.h"
#include "RNA_access.h"
@@ -14,6 +19,12 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_shader.h"
+#include "GPU_texture.h"
+
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
#include "node_composite_util.hh"
namespace blender::nodes::node_composite_movieclip_cc {
@@ -79,6 +90,184 @@ static void node_composit_buts_movieclip_ex(uiLayout *layout, bContext *C, Point
uiTemplateColorspaceSettings(layout, &clipptr, "colorspace_settings");
}
+using namespace blender::realtime_compositor;
+
+class MovieClipOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ GPUTexture *movie_clip_texture = get_movie_clip_texture();
+
+ compute_image(movie_clip_texture);
+ compute_alpha(movie_clip_texture);
+ compute_stabilization_data(movie_clip_texture);
+
+ free_movie_clip_texture();
+ }
+
+ void compute_image(GPUTexture *movie_clip_texture)
+ {
+ if (!should_compute_output("Image")) {
+ return;
+ }
+
+ Result &result = get_result("Image");
+
+ /* The movie clip texture is invalid or missing, set an appropriate fallback value. */
+ if (!movie_clip_texture) {
+ result.allocate_invalid();
+ return;
+ }
+
+ const int2 size = int2(GPU_texture_width(movie_clip_texture),
+ GPU_texture_height(movie_clip_texture));
+ result.allocate_texture(Domain(size));
+
+ GPUShader *shader = shader_manager().get("compositor_convert_color_to_half_color");
+ GPU_shader_bind(shader);
+
+ const int input_unit = GPU_shader_get_texture_binding(shader, "input_tx");
+ GPU_texture_bind(movie_clip_texture, input_unit);
+
+ result.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, size);
+
+ GPU_shader_unbind();
+ GPU_texture_unbind(movie_clip_texture);
+ result.unbind_as_image();
+ }
+
+ void compute_alpha(GPUTexture *movie_clip_texture)
+ {
+ if (!should_compute_output("Alpha")) {
+ return;
+ }
+
+ Result &result = get_result("Alpha");
+
+ /* The movie clip texture is invalid or missing, set an appropriate fallback value. */
+ if (!movie_clip_texture) {
+ result.allocate_single_value();
+ result.set_float_value(1.0f);
+ return;
+ }
+
+ const int2 size = int2(GPU_texture_width(movie_clip_texture),
+ GPU_texture_height(movie_clip_texture));
+ result.allocate_texture(Domain(size));
+
+ GPUShader *shader = shader_manager().get("compositor_extract_alpha_from_color");
+ GPU_shader_bind(shader);
+
+ const int input_unit = GPU_shader_get_texture_binding(shader, "input_tx");
+ GPU_texture_bind(movie_clip_texture, input_unit);
+
+ result.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, size);
+
+ GPU_shader_unbind();
+ GPU_texture_unbind(movie_clip_texture);
+ result.unbind_as_image();
+ }
+
+ void compute_stabilization_data(GPUTexture *movie_clip_texture)
+ {
+ /* The movie clip texture is invalid or missing, set appropriate fallback values. */
+ if (!movie_clip_texture) {
+ if (should_compute_output("Offset X")) {
+ Result &result = get_result("Offset X");
+ result.allocate_single_value();
+ result.set_float_value(0.0f);
+ }
+ if (should_compute_output("Offset Y")) {
+ Result &result = get_result("Offset Y");
+ result.allocate_single_value();
+ result.set_float_value(0.0f);
+ }
+ if (should_compute_output("Scale")) {
+ Result &result = get_result("Scale");
+ result.allocate_single_value();
+ result.set_float_value(1.0f);
+ }
+ if (should_compute_output("Angle")) {
+ Result &result = get_result("Angle");
+ result.allocate_single_value();
+ result.set_float_value(0.0f);
+ }
+ return;
+ }
+
+ MovieClip *movie_clip = get_movie_clip();
+ const int frame_number = BKE_movieclip_remap_scene_to_clip_frame(movie_clip,
+ context().get_frame_number());
+ const int width = GPU_texture_width(movie_clip_texture);
+ const int height = GPU_texture_height(movie_clip_texture);
+
+ /* If the movie clip has no stabilization data, it will initialize the given values with
+ * fallback values regardless, so no need to handle that case. */
+ float2 offset;
+ float scale, angle;
+ BKE_tracking_stabilization_data_get(
+ movie_clip, frame_number, width, height, offset, &scale, &angle);
+
+ if (should_compute_output("Offset X")) {
+ Result &result = get_result("Offset X");
+ result.allocate_single_value();
+ result.set_float_value(offset.x);
+ }
+ if (should_compute_output("Offset Y")) {
+ Result &result = get_result("Offset Y");
+ result.allocate_single_value();
+ result.set_float_value(offset.y);
+ }
+ if (should_compute_output("Scale")) {
+ Result &result = get_result("Scale");
+ result.allocate_single_value();
+ result.set_float_value(scale);
+ }
+ if (should_compute_output("Angle")) {
+ Result &result = get_result("Angle");
+ result.allocate_single_value();
+ result.set_float_value(angle);
+ }
+ }
+
+ GPUTexture *get_movie_clip_texture()
+ {
+ MovieClip *movie_clip = get_movie_clip();
+ MovieClipUser *movie_clip_user = get_movie_clip_user();
+ BKE_movieclip_user_set_frame(movie_clip_user, context().get_frame_number());
+ return BKE_movieclip_get_gpu_texture(movie_clip, movie_clip_user);
+ }
+
+ void free_movie_clip_texture()
+ {
+ MovieClip *movie_clip = get_movie_clip();
+ if (movie_clip) {
+ BKE_movieclip_free_gputexture(movie_clip);
+ }
+ }
+
+ MovieClip *get_movie_clip()
+ {
+ return (MovieClip *)bnode().id;
+ }
+
+ MovieClipUser *get_movie_clip_user()
+ {
+ return static_cast<MovieClipUser *>(bnode().storage);
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new MovieClipOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_movieclip_cc
void register_node_type_cmp_movieclip()
@@ -91,6 +280,7 @@ void register_node_type_cmp_movieclip()
ntype.declare = file_ns::cmp_node_movieclip_declare;
ntype.draw_buttons = file_ns::node_composit_buts_movieclip;
ntype.draw_buttons_ex = file_ns::node_composit_buts_movieclip_ex;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
ntype.initfunc_api = file_ns::init;
ntype.flag |= NODE_PREVIEW;
node_type_storage(
diff --git a/source/blender/nodes/composite/nodes/node_composite_moviedistortion.cc b/source/blender/nodes/composite/nodes/node_composite_moviedistortion.cc
index 9c6c6a40b2c..88638586594 100644
--- a/source/blender/nodes/composite/nodes/node_composite_moviedistortion.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_moviedistortion.cc
@@ -7,10 +7,13 @@
#include "BKE_context.h"
#include "BKE_lib_id.h"
+#include "BKE_tracking.h"
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Translate ******************** */
@@ -80,6 +83,23 @@ static void node_composit_buts_moviedistortion(uiLayout *layout, bContext *C, Po
uiItemR(layout, ptr, "distortion_type", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class MovieDistortionOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new MovieDistortionOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_moviedistortion_cc
void register_node_type_cmp_moviedistortion()
@@ -94,6 +114,7 @@ void register_node_type_cmp_moviedistortion()
ntype.labelfunc = file_ns::label;
ntype.initfunc_api = file_ns::init;
node_type_storage(&ntype, nullptr, file_ns::storage_free, file_ns::storage_copy);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_normal.cc b/source/blender/nodes/composite/nodes/node_composite_normal.cc
index b4dd0bbacd0..a1a6303e21b 100644
--- a/source/blender/nodes/composite/nodes/node_composite_normal.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_normal.cc
@@ -5,6 +5,10 @@
* \ingroup cmpnodes
*/
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** NORMAL ******************** */
@@ -17,7 +21,8 @@ static void cmp_node_normal_declare(NodeDeclarationBuilder &b)
.default_value({0.0f, 0.0f, 1.0f})
.min(-1.0f)
.max(1.0f)
- .subtype(PROP_DIRECTION);
+ .subtype(PROP_DIRECTION)
+ .compositor_domain_priority(0);
b.add_output<decl::Vector>(N_("Normal"))
.default_value({0.0f, 0.0f, 1.0f})
.min(-1.0f)
@@ -26,6 +31,40 @@ static void cmp_node_normal_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Float>(N_("Dot"));
}
+using namespace blender::realtime_compositor;
+
+class NormalShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_normal",
+ inputs,
+ outputs,
+ GPU_uniform(get_vector_value()));
+ }
+
+ /* The vector value is stored in the default value of the output socket. */
+ const float *get_vector_value()
+ {
+ return node()
+ .output_by_identifier("Normal")
+ ->default_value_typed<bNodeSocketValueVector>()
+ ->value;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new NormalShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_normal_cc
void register_node_type_cmp_normal()
@@ -36,6 +75,7 @@ void register_node_type_cmp_normal()
cmp_node_type_base(&ntype, CMP_NODE_NORMAL, "Normal", NODE_CLASS_OP_VECTOR);
ntype.declare = file_ns::cmp_node_normal_declare;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_normalize.cc b/source/blender/nodes/composite/nodes/node_composite_normalize.cc
index 49318279bdb..21765825468 100644
--- a/source/blender/nodes/composite/nodes/node_composite_normalize.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_normalize.cc
@@ -5,6 +5,8 @@
* \ingroup cmpnodes
*/
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** NORMALIZE single channel, useful for Z buffer ******************** */
@@ -17,6 +19,23 @@ static void cmp_node_normalize_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Float>(N_("Value"));
}
+using namespace blender::realtime_compositor;
+
+class NormalizeOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Value").pass_through(get_result("Value"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new NormalizeOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_normalize_cc
void register_node_type_cmp_normalize()
@@ -27,6 +46,7 @@ void register_node_type_cmp_normalize()
cmp_node_type_base(&ntype, CMP_NODE_NORMALIZE, "Normalize", NODE_CLASS_OP_VECTOR);
ntype.declare = file_ns::cmp_node_normalize_declare;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_output_file.cc b/source/blender/nodes/composite/nodes/node_composite_output_file.cc
index 1fd6e62b4c5..5ed383977a5 100644
--- a/source/blender/nodes/composite/nodes/node_composite_output_file.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_output_file.cc
@@ -24,6 +24,8 @@
#include "IMB_openexr.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** OUTPUT FILE ******************** */
@@ -114,7 +116,7 @@ void ntreeCompositOutputFileUniqueLayer(ListBase *list,
bNodeSocket *ntreeCompositOutputFileAddSocket(bNodeTree *ntree,
bNode *node,
const char *name,
- ImageFormatData *im_format)
+ const ImageFormatData *im_format)
{
NodeImageMultiFile *nimf = (NodeImageMultiFile *)node->storage;
bNodeSocket *sock = nodeAddStaticSocket(
@@ -130,7 +132,8 @@ bNodeSocket *ntreeCompositOutputFileAddSocket(bNodeTree *ntree,
ntreeCompositOutputFileUniqueLayer(&node->inputs, sock, name, '_');
if (im_format) {
- sockdata->format = *im_format;
+ BKE_image_format_copy(&sockdata->format, im_format);
+ sockdata->format.color_management = R_IMF_COLOR_MANAGEMENT_FOLLOW_SCENE;
if (BKE_imtype_is_movie(sockdata->format.imtype)) {
sockdata->format.imtype = R_IMF_IMTYPE_OPENEXR;
}
@@ -198,7 +201,8 @@ static void init_output_file(const bContext *C, PointerRNA *ptr)
RenderData *rd = &scene->r;
BLI_strncpy(nimf->base_path, rd->pic, sizeof(nimf->base_path));
- nimf->format = rd->im_format;
+ BKE_image_format_copy(&nimf->format, &rd->im_format);
+ nimf->format.color_management = R_IMF_COLOR_MANAGEMENT_FOLLOW_SCENE;
if (BKE_imtype_is_movie(nimf->format.imtype)) {
nimf->format.imtype = R_IMF_IMTYPE_OPENEXR;
}
@@ -437,6 +441,22 @@ static void node_composit_buts_file_output_ex(uiLayout *layout, bContext *C, Poi
}
}
+using namespace blender::realtime_compositor;
+
+class OutputFileOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new OutputFileOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_output_file_cc
void register_node_type_cmp_output_file()
@@ -453,6 +473,7 @@ void register_node_type_cmp_output_file()
node_type_storage(
&ntype, "NodeImageMultiFile", file_ns::free_output_file, file_ns::copy_output_file);
node_type_update(&ntype, file_ns::update_output_file);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_pixelate.cc b/source/blender/nodes/composite/nodes/node_composite_pixelate.cc
index 529aa0f84de..4567464a547 100644
--- a/source/blender/nodes/composite/nodes/node_composite_pixelate.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_pixelate.cc
@@ -5,6 +5,8 @@
* \ingroup cmpnodes
*/
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Pixelate ******************** */
@@ -17,6 +19,23 @@ static void cmp_node_pixelate_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Color>(N_("Color"));
}
+using namespace blender::realtime_compositor;
+
+class PixelateOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Color").pass_through(get_result("Color"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new PixelateOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_pixelate_cc
void register_node_type_cmp_pixelate()
@@ -27,6 +46,7 @@ void register_node_type_cmp_pixelate()
cmp_node_type_base(&ntype, CMP_NODE_PIXELATE, "Pixelate", NODE_CLASS_OP_FILTER);
ntype.declare = file_ns::cmp_node_pixelate_declare;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_planetrackdeform.cc b/source/blender/nodes/composite/nodes/node_composite_planetrackdeform.cc
index fb0c03579a2..68dc020a02e 100644
--- a/source/blender/nodes/composite/nodes/node_composite_planetrackdeform.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_planetrackdeform.cc
@@ -5,12 +5,21 @@
* \ingroup cmpnodes
*/
+#include "DNA_movieclip_types.h"
+#include "DNA_tracking_types.h"
+
+#include "BKE_context.h"
+#include "BKE_lib_id.h"
+#include "BKE_tracking.h"
+
#include "RNA_access.h"
#include "RNA_prototypes.h"
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
namespace blender::nodes::node_composite_planetrackdeform_cc {
@@ -22,12 +31,33 @@ static void cmp_node_planetrackdeform_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Float>(N_("Plane"));
}
-static void init(bNodeTree *UNUSED(ntree), bNode *node)
+static void init(const bContext *C, PointerRNA *ptr)
{
+ bNode *node = (bNode *)ptr->data;
+
NodePlaneTrackDeformData *data = MEM_cnew<NodePlaneTrackDeformData>(__func__);
data->motion_blur_samples = 16;
data->motion_blur_shutter = 0.5f;
node->storage = data;
+
+ const Scene *scene = CTX_data_scene(C);
+ if (scene->clip) {
+ MovieClip *clip = scene->clip;
+ MovieTracking *tracking = &clip->tracking;
+
+ node->id = &clip->id;
+ id_us_plus(&clip->id);
+
+ const MovieTrackingObject *tracking_object = BKE_tracking_object_get_active(tracking);
+ BLI_strncpy(data->tracking_object, tracking_object->name, sizeof(data->tracking_object));
+
+ const MovieTrackingPlaneTrack *active_plane_track = BKE_tracking_plane_track_get_active(
+ tracking);
+ if (active_plane_track) {
+ BLI_strncpy(
+ data->plane_track_name, active_plane_track->name, sizeof(data->plane_track_name));
+ }
+ }
}
static void node_composit_buts_planetrackdeform(uiLayout *layout, bContext *C, PointerRNA *ptr)
@@ -79,6 +109,24 @@ static void node_composit_buts_planetrackdeform(uiLayout *layout, bContext *C, P
}
}
+using namespace blender::realtime_compositor;
+
+class PlaneTrackDeformOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ get_result("Plane").allocate_invalid();
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new PlaneTrackDeformOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_planetrackdeform_cc
void register_node_type_cmp_planetrackdeform()
@@ -90,9 +138,10 @@ void register_node_type_cmp_planetrackdeform()
cmp_node_type_base(&ntype, CMP_NODE_PLANETRACKDEFORM, "Plane Track Deform", NODE_CLASS_DISTORT);
ntype.declare = file_ns::cmp_node_planetrackdeform_declare;
ntype.draw_buttons = file_ns::node_composit_buts_planetrackdeform;
- node_type_init(&ntype, file_ns::init);
+ ntype.initfunc_api = file_ns::init;
node_type_storage(
&ntype, "NodePlaneTrackDeformData", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_posterize.cc b/source/blender/nodes/composite/nodes/node_composite_posterize.cc
index c97035d55ea..1268219e7e2 100644
--- a/source/blender/nodes/composite/nodes/node_composite_posterize.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_posterize.cc
@@ -5,6 +5,10 @@
* \ingroup cmpnodes
*/
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** Posterize ******************** */
@@ -13,11 +17,37 @@ namespace blender::nodes::node_composite_posterize_cc {
static void cmp_node_posterize_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Float>(N_("Steps")).default_value(8.0f).min(2.0f).max(1024.0f);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("Steps"))
+ .default_value(8.0f)
+ .min(2.0f)
+ .max(1024.0f)
+ .compositor_domain_priority(1);
b.add_output<decl::Color>(N_("Image"));
}
+using namespace blender::realtime_compositor;
+
+class PosterizeShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), "node_composite_posterize", inputs, outputs);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new PosterizeShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_posterize_cc
void register_node_type_cmp_posterize()
@@ -28,6 +58,7 @@ void register_node_type_cmp_posterize()
cmp_node_type_base(&ntype, CMP_NODE_POSTERIZE, "Posterize", NODE_CLASS_OP_COLOR);
ntype.declare = file_ns::cmp_node_posterize_declare;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_premulkey.cc b/source/blender/nodes/composite/nodes/node_composite_premulkey.cc
index 000cc9df90a..c814ea5f738 100644
--- a/source/blender/nodes/composite/nodes/node_composite_premulkey.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_premulkey.cc
@@ -8,6 +8,10 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** Premul and Key Alpha Convert ******************** */
@@ -16,7 +20,9 @@ namespace blender::nodes::node_composite_premulkey_cc {
static void cmp_node_premulkey_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Color>(N_("Image"));
}
@@ -25,6 +31,36 @@ static void node_composit_buts_premulkey(uiLayout *layout, bContext *UNUSED(C),
uiItemR(layout, ptr, "mapping", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class AlphaConvertShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ if (get_mode() == 0) {
+ GPU_stack_link(material, &bnode(), "color_alpha_premultiply", inputs, outputs);
+ return;
+ }
+
+ GPU_stack_link(material, &bnode(), "color_alpha_unpremultiply", inputs, outputs);
+ }
+
+ CMPNodeAlphaConvertMode get_mode()
+ {
+ return (CMPNodeAlphaConvertMode)bnode().custom1;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new AlphaConvertShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_premulkey_cc
void register_node_type_cmp_premulkey()
@@ -36,6 +72,7 @@ void register_node_type_cmp_premulkey()
cmp_node_type_base(&ntype, CMP_NODE_PREMULKEY, "Alpha Convert", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::cmp_node_premulkey_declare;
ntype.draw_buttons = file_ns::node_composit_buts_premulkey;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_rgb.cc b/source/blender/nodes/composite/nodes/node_composite_rgb.cc
index 5bc4c67dd8e..f107961f301 100644
--- a/source/blender/nodes/composite/nodes/node_composite_rgb.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_rgb.cc
@@ -5,6 +5,12 @@
* \ingroup cmpnodes
*/
+#include "BLI_math_vec_types.hh"
+
+#include "DNA_node_types.h"
+
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** RGB ******************** */
@@ -16,6 +22,29 @@ static void cmp_node_rgb_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Color>(N_("RGBA")).default_value({0.5f, 0.5f, 0.5f, 1.0f});
}
+using namespace blender::realtime_compositor;
+
+class RGBOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ Result &result = get_result("RGBA");
+ result.allocate_single_value();
+
+ const bNodeSocket *socket = static_cast<const bNodeSocket *>(bnode().outputs.first);
+ float4 color = float4(static_cast<const bNodeSocketValueRGBA *>(socket->default_value)->value);
+
+ result.set_color_value(color);
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new RGBOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_rgb_cc
void register_node_type_cmp_rgb()
@@ -27,6 +56,7 @@ void register_node_type_cmp_rgb()
cmp_node_type_base(&ntype, CMP_NODE_RGB, "RGB", NODE_CLASS_INPUT);
ntype.declare = file_ns::cmp_node_rgb_declare;
node_type_size_preset(&ntype, NODE_SIZE_SMALL);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_rotate.cc b/source/blender/nodes/composite/nodes/node_composite_rotate.cc
index a083bc1837b..35caa3cd242 100644
--- a/source/blender/nodes/composite/nodes/node_composite_rotate.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_rotate.cc
@@ -5,9 +5,14 @@
* \ingroup cmpnodes
*/
+#include "BLI_assert.h"
+#include "BLI_float3x3.hh"
+
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Rotate ******************** */
@@ -16,12 +21,15 @@ namespace blender::nodes::node_composite_rotate_cc {
static void cmp_node_rotate_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_input<decl::Float>(N_("Degr"))
.default_value(0.0f)
.min(-10000.0f)
.max(10000.0f)
- .subtype(PROP_ANGLE);
+ .subtype(PROP_ANGLE)
+ .compositor_expects_single_value();
b.add_output<decl::Color>(N_("Image"));
}
@@ -35,6 +43,47 @@ static void node_composit_buts_rotate(uiLayout *layout, bContext *UNUSED(C), Poi
uiItemR(layout, ptr, "filter_type", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class RotateOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ Result &input = get_input("Image");
+ Result &result = get_result("Image");
+ input.pass_through(result);
+
+ const float rotation = get_input("Degr").get_float_value_default(0.0f);
+
+ const float3x3 transformation = float3x3::from_rotation(rotation);
+
+ result.transform(transformation);
+ result.get_realization_options().interpolation = get_interpolation();
+ }
+
+ Interpolation get_interpolation()
+ {
+ switch (bnode().custom1) {
+ case 0:
+ return Interpolation::Nearest;
+ case 1:
+ return Interpolation::Bilinear;
+ case 2:
+ return Interpolation::Bicubic;
+ }
+
+ BLI_assert_unreachable();
+ return Interpolation::Nearest;
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new RotateOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_rotate_cc
void register_node_type_cmp_rotate()
@@ -47,6 +96,7 @@ void register_node_type_cmp_rotate()
ntype.declare = file_ns::cmp_node_rotate_declare;
ntype.draw_buttons = file_ns::node_composit_buts_rotate;
node_type_init(&ntype, file_ns::node_composit_init_rotate);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_scale.cc b/source/blender/nodes/composite/nodes/node_composite_scale.cc
index b2b42a3613c..8b43ae8c9ca 100644
--- a/source/blender/nodes/composite/nodes/node_composite_scale.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_scale.cc
@@ -10,6 +10,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Scale ******************** */
@@ -55,6 +57,23 @@ static void node_composit_buts_scale(uiLayout *layout, bContext *UNUSED(C), Poin
}
}
+using namespace blender::realtime_compositor;
+
+class ScaleOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new ScaleOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_scale_cc
void register_node_type_cmp_scale()
@@ -67,6 +86,7 @@ void register_node_type_cmp_scale()
ntype.declare = file_ns::cmp_node_scale_declare;
ntype.draw_buttons = file_ns::node_composit_buts_scale;
node_type_update(&ntype, file_ns::node_composite_update_scale);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_scene_time.cc b/source/blender/nodes/composite/nodes/node_composite_scene_time.cc
index 20bafb0d3d4..1f5317378bb 100644
--- a/source/blender/nodes/composite/nodes/node_composite_scene_time.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_scene_time.cc
@@ -3,6 +3,8 @@
* \ingroup cmpnodes
*/
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
namespace blender::nodes {
@@ -13,6 +15,38 @@ static void cmp_node_scene_time_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Float>(N_("Frame"));
}
+using namespace blender::realtime_compositor;
+
+class SceneTimeOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ execute_seconds();
+ execute_frame();
+ }
+
+ void execute_seconds()
+ {
+ Result &result = get_result("Seconds");
+ result.allocate_single_value();
+ result.set_float_value(context().get_time());
+ }
+
+ void execute_frame()
+ {
+ Result &result = get_result("Frame");
+ result.allocate_single_value();
+ result.set_float_value(static_cast<float>(context().get_frame_number()));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new SceneTimeOperation(context, node);
+}
+
} // namespace blender::nodes
void register_node_type_cmp_scene_time()
@@ -21,5 +55,7 @@ void register_node_type_cmp_scene_time()
cmp_node_type_base(&ntype, CMP_NODE_SCENE_TIME, "Scene Time", NODE_CLASS_INPUT);
ntype.declare = blender::nodes::cmp_node_scene_time_declare;
+ ntype.get_compositor_operation = blender::nodes::get_compositor_operation;
+
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_color.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_color.cc
index b253656a628..f6792d7ce61 100644
--- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_color.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_color.cc
@@ -1,5 +1,11 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include "BLI_assert.h"
+
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
static void node_cmp_combsep_color_init(bNodeTree *UNUSED(ntree), bNode *node)
@@ -56,9 +62,13 @@ static void node_cmp_combsep_color_label(const ListBase *sockets, CMPNodeCombSep
namespace blender::nodes::node_composite_separate_color_cc {
+NODE_STORAGE_FUNCS(NodeCMPCombSepColor)
+
static void cmp_node_separate_color_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Float>(N_("Red"));
b.add_output<decl::Float>(N_("Green"));
b.add_output<decl::Float>(N_("Blue"));
@@ -71,6 +81,52 @@ static void cmp_node_separate_color_update(bNodeTree *UNUSED(ntree), bNode *node
node_cmp_combsep_color_label(&node->outputs, (CMPNodeCombSepColorMode)storage->mode);
}
+using namespace blender::realtime_compositor;
+
+class SeparateColorShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), get_shader_function_name(), inputs, outputs);
+ }
+
+ const char *get_shader_function_name()
+ {
+ switch (node_storage(bnode()).mode) {
+ case CMP_NODE_COMBSEP_COLOR_RGB:
+ return "node_composite_separate_rgba";
+ case CMP_NODE_COMBSEP_COLOR_HSV:
+ return "node_composite_separate_hsva";
+ case CMP_NODE_COMBSEP_COLOR_HSL:
+ return "node_composite_separate_hsla";
+ case CMP_NODE_COMBSEP_COLOR_YUV:
+ return "node_composite_separate_yuva_itu_709";
+ case CMP_NODE_COMBSEP_COLOR_YCC:
+ switch (node_storage(bnode()).ycc_mode) {
+ case BLI_YCC_ITU_BT601:
+ return "node_composite_separate_ycca_itu_601";
+ case BLI_YCC_ITU_BT709:
+ return "node_composite_separate_ycca_itu_709";
+ case BLI_YCC_JFIF_0_255:
+ return "node_composite_separate_ycca_jpeg";
+ }
+ }
+
+ BLI_assert_unreachable();
+ return nullptr;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new SeparateColorShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_separate_color_cc
void register_node_type_cmp_separate_color()
@@ -85,6 +141,7 @@ void register_node_type_cmp_separate_color()
node_type_storage(
&ntype, "NodeCMPCombSepColor", node_free_standard_storage, node_copy_standard_storage);
node_type_update(&ntype, file_ns::cmp_node_separate_color_update);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
@@ -93,24 +150,34 @@ void register_node_type_cmp_separate_color()
namespace blender::nodes::node_composite_combine_color_cc {
+NODE_STORAGE_FUNCS(NodeCMPCombSepColor)
+
static void cmp_node_combine_color_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Red")).default_value(0.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
+ b.add_input<decl::Float>(N_("Red"))
+ .default_value(0.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(0);
b.add_input<decl::Float>(N_("Green"))
.default_value(0.0f)
.min(0.0f)
.max(1.0f)
- .subtype(PROP_FACTOR);
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(1);
b.add_input<decl::Float>(N_("Blue"))
.default_value(0.0f)
.min(0.0f)
.max(1.0f)
- .subtype(PROP_FACTOR);
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(2);
b.add_input<decl::Float>(N_("Alpha"))
.default_value(1.0f)
.min(0.0f)
.max(1.0f)
- .subtype(PROP_FACTOR);
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(3);
b.add_output<decl::Color>(N_("Image"));
}
@@ -120,6 +187,52 @@ static void cmp_node_combine_color_update(bNodeTree *UNUSED(ntree), bNode *node)
node_cmp_combsep_color_label(&node->inputs, (CMPNodeCombSepColorMode)storage->mode);
}
+using namespace blender::realtime_compositor;
+
+class CombineColorShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), get_shader_function_name(), inputs, outputs);
+ }
+
+ const char *get_shader_function_name()
+ {
+ switch (node_storage(bnode()).mode) {
+ case CMP_NODE_COMBSEP_COLOR_RGB:
+ return "node_composite_combine_rgba";
+ case CMP_NODE_COMBSEP_COLOR_HSV:
+ return "node_composite_combine_hsva";
+ case CMP_NODE_COMBSEP_COLOR_HSL:
+ return "node_composite_combine_hsla";
+ case CMP_NODE_COMBSEP_COLOR_YUV:
+ return "node_composite_combine_yuva_itu_709";
+ case CMP_NODE_COMBSEP_COLOR_YCC:
+ switch (node_storage(bnode()).ycc_mode) {
+ case BLI_YCC_ITU_BT601:
+ return "node_composite_combine_ycca_itu_601";
+ case BLI_YCC_ITU_BT709:
+ return "node_composite_combine_ycca_itu_709";
+ case BLI_YCC_JFIF_0_255:
+ return "node_composite_combine_ycca_jpeg";
+ }
+ }
+
+ BLI_assert_unreachable();
+ return nullptr;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new CombineColorShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_combine_color_cc
void register_node_type_cmp_combine_color()
@@ -134,6 +247,7 @@ void register_node_type_cmp_combine_color()
node_type_storage(
&ntype, "NodeCMPCombSepColor", node_free_standard_storage, node_copy_standard_storage);
node_type_update(&ntype, file_ns::cmp_node_combine_color_update);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_hsva.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_hsva.cc
index a0d2485ea5a..b655c0db106 100644
--- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_hsva.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_hsva.cc
@@ -5,57 +5,112 @@
* \ingroup cmpnodes
*/
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** SEPARATE HSVA ******************** */
-namespace blender::nodes::node_composite_sepcomb_hsva_cc {
+namespace blender::nodes::node_composite_separate_hsva_cc {
static void cmp_node_sephsva_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Float>(N_("H"));
b.add_output<decl::Float>(N_("S"));
b.add_output<decl::Float>(N_("V"));
b.add_output<decl::Float>(N_("A"));
}
-} // namespace blender::nodes::node_composite_sepcomb_hsva_cc
+using namespace blender::realtime_compositor;
+
+class SeparateHSVAShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), "node_composite_separate_hsva", inputs, outputs);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new SeparateHSVAShaderNode(node);
+}
+
+} // namespace blender::nodes::node_composite_separate_hsva_cc
void register_node_type_cmp_sephsva()
{
- namespace file_ns = blender::nodes::node_composite_sepcomb_hsva_cc;
+ namespace file_ns = blender::nodes::node_composite_separate_hsva_cc;
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_SEPHSVA_LEGACY, "Separate HSVA", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::cmp_node_sephsva_declare;
+ ntype.gather_link_search_ops = nullptr;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
+
nodeRegisterType(&ntype);
}
/* **************** COMBINE HSVA ******************** */
-namespace blender::nodes::node_composite_sepcomb_hsva_cc {
+namespace blender::nodes::node_composite_combine_hsva_cc {
static void cmp_node_combhsva_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("H")).min(0.0f).max(1.0f);
- b.add_input<decl::Float>(N_("S")).min(0.0f).max(1.0f);
- b.add_input<decl::Float>(N_("V")).min(0.0f).max(1.0f);
- b.add_input<decl::Float>(N_("A")).default_value(1.0f).min(0.0f).max(1.0f);
+ b.add_input<decl::Float>(N_("H")).min(0.0f).max(1.0f).compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("S")).min(0.0f).max(1.0f).compositor_domain_priority(1);
+ b.add_input<decl::Float>(N_("V")).min(0.0f).max(1.0f).compositor_domain_priority(2);
+ b.add_input<decl::Float>(N_("A"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .compositor_domain_priority(3);
b.add_output<decl::Color>(N_("Image"));
}
-} // namespace blender::nodes::node_composite_sepcomb_hsva_cc
+using namespace blender::realtime_compositor;
+
+class CombineHSVAShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), "node_composite_combine_hsva", inputs, outputs);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new CombineHSVAShaderNode(node);
+}
+
+} // namespace blender::nodes::node_composite_combine_hsva_cc
void register_node_type_cmp_combhsva()
{
- namespace file_ns = blender::nodes::node_composite_sepcomb_hsva_cc;
+ namespace file_ns = blender::nodes::node_composite_combine_hsva_cc;
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_COMBHSVA_LEGACY, "Combine HSVA", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::cmp_node_combhsva_declare;
+ ntype.gather_link_search_ops = nullptr;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_rgba.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_rgba.cc
index ae46681b0f4..1f4c9fd153f 100644
--- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_rgba.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_rgba.cc
@@ -5,57 +5,112 @@
* \ingroup cmpnodes
*/
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** SEPARATE RGBA ******************** */
-namespace blender::nodes::node_composite_sepcomb_rgba_cc {
+
+namespace blender::nodes::node_composite_separate_rgba_cc {
static void cmp_node_seprgba_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Float>(N_("R"));
b.add_output<decl::Float>(N_("G"));
b.add_output<decl::Float>(N_("B"));
b.add_output<decl::Float>(N_("A"));
}
-} // namespace blender::nodes::node_composite_sepcomb_rgba_cc
+using namespace blender::realtime_compositor;
+
+class SeparateRGBAShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), "node_composite_separate_rgba", inputs, outputs);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new SeparateRGBAShaderNode(node);
+}
+
+} // namespace blender::nodes::node_composite_separate_rgba_cc
void register_node_type_cmp_seprgba()
{
- namespace file_ns = blender::nodes::node_composite_sepcomb_rgba_cc;
+ namespace file_ns = blender::nodes::node_composite_separate_rgba_cc;
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_SEPRGBA_LEGACY, "Separate RGBA", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::cmp_node_seprgba_declare;
+ ntype.gather_link_search_ops = nullptr;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
/* **************** COMBINE RGBA ******************** */
-namespace blender::nodes::node_composite_sepcomb_rgba_cc {
+namespace blender::nodes::node_composite_combine_rgba_cc {
static void cmp_node_combrgba_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("R")).min(0.0f).max(1.0f);
- b.add_input<decl::Float>(N_("G")).min(0.0f).max(1.0f);
- b.add_input<decl::Float>(N_("B")).min(0.0f).max(1.0f);
- b.add_input<decl::Float>(N_("A")).default_value(1.0f).min(0.0f).max(1.0f);
+ b.add_input<decl::Float>(N_("R")).min(0.0f).max(1.0f).compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("G")).min(0.0f).max(1.0f).compositor_domain_priority(1);
+ b.add_input<decl::Float>(N_("B")).min(0.0f).max(1.0f).compositor_domain_priority(2);
+ b.add_input<decl::Float>(N_("A"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .compositor_domain_priority(3);
b.add_output<decl::Color>(N_("Image"));
}
-} // namespace blender::nodes::node_composite_sepcomb_rgba_cc
+using namespace blender::realtime_compositor;
+
+class CombineRGBAShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), "node_composite_combine_rgba", inputs, outputs);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new CombineRGBAShaderNode(node);
+}
+
+} // namespace blender::nodes::node_composite_combine_rgba_cc
void register_node_type_cmp_combrgba()
{
- namespace file_ns = blender::nodes::node_composite_sepcomb_rgba_cc;
+ namespace file_ns = blender::nodes::node_composite_combine_rgba_cc;
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_COMBRGBA_LEGACY, "Combine RGBA", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::cmp_node_combrgba_declare;
+ ntype.gather_link_search_ops = nullptr;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_xyz.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_xyz.cc
index 4979c376cab..e288e698808 100644
--- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_xyz.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_xyz.cc
@@ -5,10 +5,15 @@
* \ingroup cmpnodes
*/
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** SEPARATE XYZ ******************** */
-namespace blender::nodes {
+
+namespace blender::nodes::node_composite_separate_xyz_cc {
static void cmp_node_separate_xyz_declare(NodeDeclarationBuilder &b)
{
@@ -18,21 +23,44 @@ static void cmp_node_separate_xyz_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Float>("Z");
}
-} // namespace blender::nodes
+using namespace blender::realtime_compositor;
+
+class SeparateXYZShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), "node_composite_separate_xyz", inputs, outputs);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new SeparateXYZShaderNode(node);
+}
+
+} // namespace blender::nodes::node_composite_separate_xyz_cc
void register_node_type_cmp_separate_xyz()
{
+ namespace file_ns = blender::nodes::node_composite_separate_xyz_cc;
+
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_SEPARATE_XYZ, "Separate XYZ", NODE_CLASS_CONVERTER);
- ntype.declare = blender::nodes::cmp_node_separate_xyz_declare;
+ ntype.declare = file_ns::cmp_node_separate_xyz_declare;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
/* **************** COMBINE XYZ ******************** */
-namespace blender::nodes {
+namespace blender::nodes::node_composite_combine_xyz_cc {
static void cmp_node_combine_xyz_declare(NodeDeclarationBuilder &b)
{
@@ -42,14 +70,37 @@ static void cmp_node_combine_xyz_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Vector>("Vector");
}
-} // namespace blender::nodes
+using namespace blender::realtime_compositor;
+
+class CombineXYZShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), "node_composite_combine_xyz", inputs, outputs);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new CombineXYZShaderNode(node);
+}
+
+} // namespace blender::nodes::node_composite_combine_xyz_cc
void register_node_type_cmp_combine_xyz()
{
+ namespace file_ns = blender::nodes::node_composite_combine_xyz_cc;
+
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_COMBINE_XYZ, "Combine XYZ", NODE_CLASS_CONVERTER);
- ntype.declare = blender::nodes::cmp_node_combine_xyz_declare;
+ ntype.declare = file_ns::cmp_node_combine_xyz_declare;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_ycca.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_ycca.cc
index a3c40b61e64..bebe6abe115 100644
--- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_ycca.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_ycca.cc
@@ -5,15 +5,23 @@
* \ingroup cmpnodes
*/
+#include "BLI_assert.h"
+
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** SEPARATE YCCA ******************** */
-namespace blender::nodes::node_composite_sepcomb_ycca_cc {
+namespace blender::nodes::node_composite_separate_ycca_cc {
static void cmp_node_sepycca_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Float>(N_("Y"));
b.add_output<decl::Float>(N_("Cb"));
b.add_output<decl::Float>(N_("Cr"));
@@ -25,31 +33,85 @@ static void node_composit_init_mode_sepycca(bNodeTree *UNUSED(ntree), bNode *nod
node->custom1 = 1; /* BLI_YCC_ITU_BT709 */
}
-} // namespace blender::nodes::node_composite_sepcomb_ycca_cc
+using namespace blender::realtime_compositor;
+
+class SeparateYCCAShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), get_shader_function_name(), inputs, outputs);
+ }
+
+ int get_mode()
+ {
+ return bnode().custom1;
+ }
+
+ const char *get_shader_function_name()
+ {
+ switch (get_mode()) {
+ case BLI_YCC_ITU_BT601:
+ return "node_composite_separate_ycca_itu_601";
+ case BLI_YCC_ITU_BT709:
+ return "node_composite_separate_ycca_itu_709";
+ case BLI_YCC_JFIF_0_255:
+ return "node_composite_separate_ycca_jpeg";
+ }
+
+ BLI_assert_unreachable();
+ return nullptr;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new SeparateYCCAShaderNode(node);
+}
+
+} // namespace blender::nodes::node_composite_separate_ycca_cc
void register_node_type_cmp_sepycca()
{
- namespace file_ns = blender::nodes::node_composite_sepcomb_ycca_cc;
+ namespace file_ns = blender::nodes::node_composite_separate_ycca_cc;
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_SEPYCCA_LEGACY, "Separate YCbCrA", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::cmp_node_sepycca_declare;
node_type_init(&ntype, file_ns::node_composit_init_mode_sepycca);
+ ntype.gather_link_search_ops = nullptr;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
/* **************** COMBINE YCCA ******************** */
-namespace blender::nodes::node_composite_sepcomb_ycca_cc {
+namespace blender::nodes::node_composite_combine_ycca_cc {
static void cmp_node_combycca_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Y")).min(0.0f).max(1.0f);
- b.add_input<decl::Float>(N_("Cb")).default_value(0.5f).min(0.0f).max(1.0f);
- b.add_input<decl::Float>(N_("Cr")).default_value(0.5f).min(0.0f).max(1.0f);
- b.add_input<decl::Float>(N_("A")).default_value(1.0f).min(0.0f).max(1.0f);
+ b.add_input<decl::Float>(N_("Y")).min(0.0f).max(1.0f).compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("Cb"))
+ .default_value(0.5f)
+ .min(0.0f)
+ .max(1.0f)
+ .compositor_domain_priority(1);
+ b.add_input<decl::Float>(N_("Cr"))
+ .default_value(0.5f)
+ .min(0.0f)
+ .max(1.0f)
+ .compositor_domain_priority(2);
+ b.add_input<decl::Float>(N_("A"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .compositor_domain_priority(3);
b.add_output<decl::Color>(N_("Image"));
}
@@ -58,17 +120,59 @@ static void node_composit_init_mode_combycca(bNodeTree *UNUSED(ntree), bNode *no
node->custom1 = 1; /* BLI_YCC_ITU_BT709 */
}
-} // namespace blender::nodes::node_composite_sepcomb_ycca_cc
+using namespace blender::realtime_compositor;
+
+class CombineYCCAShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), get_shader_function_name(), inputs, outputs);
+ }
+
+ int get_mode()
+ {
+ return bnode().custom1;
+ }
+
+ const char *get_shader_function_name()
+ {
+ switch (get_mode()) {
+ case BLI_YCC_ITU_BT601:
+ return "node_composite_combine_ycca_itu_601";
+ case BLI_YCC_ITU_BT709:
+ return "node_composite_combine_ycca_itu_709";
+ case BLI_YCC_JFIF_0_255:
+ return "node_composite_combine_ycca_jpeg";
+ }
+
+ BLI_assert_unreachable();
+ return nullptr;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new CombineYCCAShaderNode(node);
+}
+
+} // namespace blender::nodes::node_composite_combine_ycca_cc
void register_node_type_cmp_combycca()
{
- namespace file_ns = blender::nodes::node_composite_sepcomb_ycca_cc;
+ namespace file_ns = blender::nodes::node_composite_combine_ycca_cc;
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_COMBYCCA_LEGACY, "Combine YCbCrA", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::cmp_node_combycca_declare;
node_type_init(&ntype, file_ns::node_composit_init_mode_combycca);
+ ntype.gather_link_search_ops = nullptr;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_yuva.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_yuva.cc
index 7fdece5904d..1f0eb04cfc3 100644
--- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_yuva.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_yuva.cc
@@ -5,58 +5,112 @@
* \ingroup cmpnodes
*/
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** SEPARATE YUVA ******************** */
-namespace blender::nodes::node_composite_sepcomb_yuva_cc {
+namespace blender::nodes::node_composite_separate_yuva_cc {
static void cmp_node_sepyuva_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Float>(N_("Y"));
b.add_output<decl::Float>(N_("U"));
b.add_output<decl::Float>(N_("V"));
b.add_output<decl::Float>(N_("A"));
}
-} // namespace blender::nodes::node_composite_sepcomb_yuva_cc
+using namespace blender::realtime_compositor;
+
+class SeparateYUVAShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), "node_composite_separate_yuva_itu_709", inputs, outputs);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new SeparateYUVAShaderNode(node);
+}
+
+} // namespace blender::nodes::node_composite_separate_yuva_cc
void register_node_type_cmp_sepyuva()
{
- namespace file_ns = blender::nodes::node_composite_sepcomb_yuva_cc;
+ namespace file_ns = blender::nodes::node_composite_separate_yuva_cc;
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_SEPYUVA_LEGACY, "Separate YUVA", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::cmp_node_sepyuva_declare;
+ ntype.gather_link_search_ops = nullptr;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
/* **************** COMBINE YUVA ******************** */
-namespace blender::nodes::node_composite_sepcomb_yuva_cc {
+namespace blender::nodes::node_composite_combine_yuva_cc {
static void cmp_node_combyuva_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Y")).min(0.0f).max(1.0f);
- b.add_input<decl::Float>(N_("U")).min(0.0f).max(1.0f);
- b.add_input<decl::Float>(N_("V")).min(0.0f).max(1.0f);
- b.add_input<decl::Float>(N_("A")).default_value(1.0f).min(0.0f).max(1.0f);
+ b.add_input<decl::Float>(N_("Y")).min(0.0f).max(1.0f).compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("U")).min(0.0f).max(1.0f).compositor_domain_priority(1);
+ b.add_input<decl::Float>(N_("V")).min(0.0f).max(1.0f).compositor_domain_priority(2);
+ b.add_input<decl::Float>(N_("A"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .compositor_domain_priority(3);
b.add_output<decl::Color>(N_("Image"));
}
-} // namespace blender::nodes::node_composite_sepcomb_yuva_cc
+using namespace blender::realtime_compositor;
+
+class CombineYUVAShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), "node_composite_combine_yuva_itu_709", inputs, outputs);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new CombineYUVAShaderNode(node);
+}
+
+} // namespace blender::nodes::node_composite_combine_yuva_cc
void register_node_type_cmp_combyuva()
{
- namespace file_ns = blender::nodes::node_composite_sepcomb_yuva_cc;
+ namespace file_ns = blender::nodes::node_composite_combine_yuva_cc;
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_COMBYUVA_LEGACY, "Combine YUVA", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::cmp_node_combyuva_declare;
+ ntype.gather_link_search_ops = nullptr;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_setalpha.cc b/source/blender/nodes/composite/nodes/node_composite_setalpha.cc
index 8aeaafbbf67..df3aca2c665 100644
--- a/source/blender/nodes/composite/nodes/node_composite_setalpha.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_setalpha.cc
@@ -8,16 +8,28 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** SET ALPHA ******************** */
namespace blender::nodes::node_composite_setalpha_cc {
+NODE_STORAGE_FUNCS(NodeSetAlpha)
+
static void cmp_node_setalpha_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Float>(N_("Alpha")).default_value(1.0f).min(0.0f).max(1.0f);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("Alpha"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .compositor_domain_priority(1);
b.add_output<decl::Color>(N_("Image"));
}
@@ -33,6 +45,31 @@ static void node_composit_buts_set_alpha(uiLayout *layout, bContext *UNUSED(C),
uiItemR(layout, ptr, "mode", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class SetAlphaShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ if (node_storage(bnode()).mode == CMP_NODE_SETALPHA_MODE_APPLY) {
+ GPU_stack_link(material, &bnode(), "node_composite_set_alpha_apply", inputs, outputs);
+ return;
+ }
+
+ GPU_stack_link(material, &bnode(), "node_composite_set_alpha_replace", inputs, outputs);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new SetAlphaShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_setalpha_cc
void register_node_type_cmp_setalpha()
@@ -47,6 +84,7 @@ void register_node_type_cmp_setalpha()
node_type_init(&ntype, file_ns::node_composit_init_setalpha);
node_type_storage(
&ntype, "NodeSetAlpha", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_split_viewer.cc b/source/blender/nodes/composite/nodes/node_composite_split_viewer.cc
index ab325c4559f..085de69e63e 100644
--- a/source/blender/nodes/composite/nodes/node_composite_split_viewer.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_split_viewer.cc
@@ -11,6 +11,12 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_shader.h"
+#include "GPU_texture.h"
+
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
#include "node_composite_util.hh"
/* **************** SPLIT VIEWER ******************** */
@@ -43,6 +49,70 @@ static void node_composit_buts_splitviewer(uiLayout *layout, bContext *UNUSED(C)
uiItemR(col, ptr, "factor", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class ViewerOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ GPUShader *shader = get_split_viewer_shader();
+ GPU_shader_bind(shader);
+
+ const int2 size = compute_domain().size;
+
+ GPU_shader_uniform_1f(shader, "split_ratio", get_split_ratio());
+ GPU_shader_uniform_2iv(shader, "view_size", size);
+
+ const Result &first_image = get_input("Image");
+ first_image.bind_as_texture(shader, "first_image_tx");
+ const Result &second_image = get_input("Image_001");
+ second_image.bind_as_texture(shader, "second_image_tx");
+
+ GPUTexture *output_texture = context().get_output_texture();
+ const int image_unit = GPU_shader_get_texture_binding(shader, "output_img");
+ GPU_texture_image_bind(output_texture, image_unit);
+
+ compute_dispatch_threads_at_least(shader, size);
+
+ first_image.unbind_as_texture();
+ second_image.unbind_as_texture();
+ GPU_texture_image_unbind(output_texture);
+ GPU_shader_unbind();
+ }
+
+ /* The operation domain have the same dimensions of the output without any transformations. */
+ Domain compute_domain() override
+ {
+ return Domain(context().get_output_size());
+ }
+
+ GPUShader *get_split_viewer_shader()
+ {
+ if (get_split_axis() == CMP_NODE_SPLIT_VIEWER_HORIZONTAL) {
+ return shader_manager().get("compositor_split_viewer_horizontal");
+ }
+
+ return shader_manager().get("compositor_split_viewer_vertical");
+ }
+
+ CMPNodeSplitViewerAxis get_split_axis()
+ {
+ return (CMPNodeSplitViewerAxis)bnode().custom2;
+ }
+
+ float get_split_ratio()
+ {
+ return bnode().custom1 / 100.0f;
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new ViewerOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_split_viewer_cc
void register_node_type_cmp_splitviewer()
@@ -57,6 +127,7 @@ void register_node_type_cmp_splitviewer()
ntype.flag |= NODE_PREVIEW;
node_type_init(&ntype, file_ns::node_composit_init_splitviewer);
node_type_storage(&ntype, "ImageUser", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
ntype.no_muting = true;
diff --git a/source/blender/nodes/composite/nodes/node_composite_stabilize2d.cc b/source/blender/nodes/composite/nodes/node_composite_stabilize2d.cc
index 63d00a0864b..75a96a05863 100644
--- a/source/blender/nodes/composite/nodes/node_composite_stabilize2d.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_stabilize2d.cc
@@ -11,6 +11,8 @@
#include "BKE_context.h"
#include "BKE_lib_id.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Stabilize 2D ******************** */
@@ -58,6 +60,23 @@ static void node_composit_buts_stabilize2d(uiLayout *layout, bContext *C, Pointe
uiItemR(layout, ptr, "invert", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class Stabilize2DOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new Stabilize2DOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_stabilize2d_cc
void register_node_type_cmp_stabilize2d()
@@ -70,6 +89,7 @@ void register_node_type_cmp_stabilize2d()
ntype.declare = file_ns::cmp_node_stabilize2d_declare;
ntype.draw_buttons = file_ns::node_composit_buts_stabilize2d;
ntype.initfunc_api = file_ns::init;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_sunbeams.cc b/source/blender/nodes/composite/nodes/node_composite_sunbeams.cc
index 766f26745ef..4b9264d7e35 100644
--- a/source/blender/nodes/composite/nodes/node_composite_sunbeams.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_sunbeams.cc
@@ -8,6 +8,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
namespace blender::nodes::node_composite_sunbeams_cc {
@@ -38,6 +40,23 @@ static void node_composit_buts_sunbeams(uiLayout *layout, bContext *UNUSED(C), P
ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class SunBeamsOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new SunBeamsOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_sunbeams_cc
void register_node_type_cmp_sunbeams()
@@ -52,6 +71,7 @@ void register_node_type_cmp_sunbeams()
node_type_init(&ntype, file_ns::init);
node_type_storage(
&ntype, "NodeSunBeams", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_switch.cc b/source/blender/nodes/composite/nodes/node_composite_switch.cc
index bda490572e9..767802cc442 100644
--- a/source/blender/nodes/composite/nodes/node_composite_switch.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_switch.cc
@@ -8,6 +8,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Switch ******************** */
@@ -26,6 +28,30 @@ static void node_composit_buts_switch(uiLayout *layout, bContext *UNUSED(C), Poi
uiItemR(layout, ptr, "check", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class SwitchOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ Result &input = get_input(get_condition() ? "On" : "Off");
+ Result &result = get_result("Image");
+ input.pass_through(result);
+ }
+
+ bool get_condition()
+ {
+ return bnode().custom1;
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new SwitchOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_switch_cc
void register_node_type_cmp_switch()
@@ -38,5 +64,7 @@ void register_node_type_cmp_switch()
ntype.declare = file_ns::cmp_node_switch_declare;
ntype.draw_buttons = file_ns::node_composit_buts_switch;
node_type_size_preset(&ntype, NODE_SIZE_SMALL);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
+
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_switchview.cc b/source/blender/nodes/composite/nodes/node_composite_switchview.cc
index 2cf3da03a05..e74c3b6007a 100644
--- a/source/blender/nodes/composite/nodes/node_composite_switchview.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_switchview.cc
@@ -11,6 +11,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** SWITCH VIEW ******************** */
@@ -140,6 +142,25 @@ static void node_composit_buts_switch_view_ex(uiLayout *layout,
nullptr);
}
+using namespace blender::realtime_compositor;
+
+class SwitchViewOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ Result &input = get_input(context().get_view_name());
+ Result &result = get_result("Image");
+ input.pass_through(result);
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new SwitchViewOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_switchview_cc
void register_node_type_cmp_switch_view()
@@ -153,6 +174,7 @@ void register_node_type_cmp_switch_view()
ntype.draw_buttons_ex = file_ns::node_composit_buts_switch_view_ex;
ntype.initfunc_api = file_ns::init_switch_view;
node_type_update(&ntype, file_ns::cmp_node_switch_view_update);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_texture.cc b/source/blender/nodes/composite/nodes/node_composite_texture.cc
index 7571e97a2cd..5a628aae7a7 100644
--- a/source/blender/nodes/composite/nodes/node_composite_texture.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_texture.cc
@@ -5,6 +5,8 @@
* \ingroup cmpnodes
*/
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** TEXTURE ******************** */
@@ -23,6 +25,24 @@ static void cmp_node_texture_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Color>(N_("Color"));
}
+using namespace blender::realtime_compositor;
+
+class TextureOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_result("Value").allocate_invalid();
+ get_result("Color").allocate_invalid();
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new TextureOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_texture_cc
void register_node_type_cmp_texture()
@@ -34,6 +54,7 @@ void register_node_type_cmp_texture()
cmp_node_type_base(&ntype, CMP_NODE_TEXTURE, "Texture", NODE_CLASS_INPUT);
ntype.declare = file_ns::cmp_node_texture_declare;
ntype.flag |= NODE_PREVIEW;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_tonemap.cc b/source/blender/nodes/composite/nodes/node_composite_tonemap.cc
index cdfe97b038d..4cc3d4f32a3 100644
--- a/source/blender/nodes/composite/nodes/node_composite_tonemap.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_tonemap.cc
@@ -10,6 +10,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
namespace blender::nodes::node_composite_tonemap_cc {
@@ -58,6 +60,23 @@ static void node_composit_buts_tonemap(uiLayout *layout, bContext *UNUSED(C), Po
}
}
+using namespace blender::realtime_compositor;
+
+class ToneMapOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new ToneMapOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_tonemap_cc
void register_node_type_cmp_tonemap()
@@ -71,6 +90,7 @@ void register_node_type_cmp_tonemap()
ntype.draw_buttons = file_ns::node_composit_buts_tonemap;
node_type_init(&ntype, file_ns::node_composit_init_tonemap);
node_type_storage(&ntype, "NodeTonemap", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_trackpos.cc b/source/blender/nodes/composite/nodes/node_composite_trackpos.cc
index 17a086f306f..0e9bd800f44 100644
--- a/source/blender/nodes/composite/nodes/node_composite_trackpos.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_trackpos.cc
@@ -5,12 +5,21 @@
* \ingroup cmpnodes
*/
+#include "DNA_movieclip_types.h"
+#include "DNA_tracking_types.h"
+
+#include "BKE_context.h"
+#include "BKE_lib_id.h"
+#include "BKE_tracking.h"
+
#include "RNA_access.h"
#include "RNA_prototypes.h"
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
namespace blender::nodes::node_composite_trackpos_cc {
@@ -22,11 +31,29 @@ static void cmp_node_trackpos_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Vector>(N_("Speed")).subtype(PROP_VELOCITY);
}
-static void init(bNodeTree *UNUSED(ntree), bNode *node)
+static void init(const bContext *C, PointerRNA *ptr)
{
- NodeTrackPosData *data = MEM_cnew<NodeTrackPosData>(__func__);
+ bNode *node = (bNode *)ptr->data;
+ NodeTrackPosData *data = MEM_cnew<NodeTrackPosData>(__func__);
node->storage = data;
+
+ const Scene *scene = CTX_data_scene(C);
+ if (scene->clip) {
+ MovieClip *clip = scene->clip;
+ MovieTracking *tracking = &clip->tracking;
+
+ node->id = &clip->id;
+ id_us_plus(&clip->id);
+
+ const MovieTrackingObject *tracking_object = BKE_tracking_object_get_active(tracking);
+ BLI_strncpy(data->tracking_object, tracking_object->name, sizeof(data->tracking_object));
+
+ const MovieTrackingTrack *active_track = BKE_tracking_track_get_active(tracking);
+ if (active_track) {
+ BLI_strncpy(data->track_name, active_track->name, sizeof(data->track_name));
+ }
+ }
}
static void node_composit_buts_trackpos(uiLayout *layout, bContext *C, PointerRNA *ptr)
@@ -77,6 +104,25 @@ static void node_composit_buts_trackpos(uiLayout *layout, bContext *C, PointerRN
}
}
+using namespace blender::realtime_compositor;
+
+class TrackPositionOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_result("X").allocate_invalid();
+ get_result("Y").allocate_invalid();
+ get_result("Speed").allocate_invalid();
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new TrackPositionOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_trackpos_cc
void register_node_type_cmp_trackpos()
@@ -88,9 +134,10 @@ void register_node_type_cmp_trackpos()
cmp_node_type_base(&ntype, CMP_NODE_TRACKPOS, "Track Position", NODE_CLASS_INPUT);
ntype.declare = file_ns::cmp_node_trackpos_declare;
ntype.draw_buttons = file_ns::node_composit_buts_trackpos;
- node_type_init(&ntype, file_ns::init);
+ ntype.initfunc_api = file_ns::init;
node_type_storage(
&ntype, "NodeTrackPosData", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_transform.cc b/source/blender/nodes/composite/nodes/node_composite_transform.cc
index fe72f5e33ca..7c5866d2d06 100644
--- a/source/blender/nodes/composite/nodes/node_composite_transform.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_transform.cc
@@ -5,9 +5,15 @@
* \ingroup cmpnodes
*/
+#include "BLI_assert.h"
+#include "BLI_float3x3.hh"
+#include "BLI_math_vector.h"
+
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Transform ******************** */
@@ -16,15 +22,30 @@ namespace blender::nodes::node_composite_transform_cc {
static void cmp_node_transform_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({0.8f, 0.8f, 0.8f, 1.0f});
- b.add_input<decl::Float>(N_("X")).default_value(0.0f).min(-10000.0f).max(10000.0f);
- b.add_input<decl::Float>(N_("Y")).default_value(0.0f).min(-10000.0f).max(10000.0f);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({0.8f, 0.8f, 0.8f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("X"))
+ .default_value(0.0f)
+ .min(-10000.0f)
+ .max(10000.0f)
+ .compositor_expects_single_value();
+ b.add_input<decl::Float>(N_("Y"))
+ .default_value(0.0f)
+ .min(-10000.0f)
+ .max(10000.0f)
+ .compositor_expects_single_value();
b.add_input<decl::Float>(N_("Angle"))
.default_value(0.0f)
.min(-10000.0f)
.max(10000.0f)
- .subtype(PROP_ANGLE);
- b.add_input<decl::Float>(N_("Scale")).default_value(1.0f).min(0.0001f).max(CMP_SCALE_MAX);
+ .subtype(PROP_ANGLE)
+ .compositor_expects_single_value();
+ b.add_input<decl::Float>(N_("Scale"))
+ .default_value(1.0f)
+ .min(0.0001f)
+ .max(CMP_SCALE_MAX)
+ .compositor_expects_single_value();
b.add_output<decl::Color>(N_("Image"));
}
@@ -33,6 +54,51 @@ static void node_composit_buts_transform(uiLayout *layout, bContext *UNUSED(C),
uiItemR(layout, ptr, "filter_type", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class TransformOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ Result &input = get_input("Image");
+ Result &result = get_result("Image");
+ input.pass_through(result);
+
+ const float2 translation = float2(get_input("X").get_float_value_default(0.0f),
+ get_input("Y").get_float_value_default(0.0f));
+ const float rotation = get_input("Angle").get_float_value_default(0.0f);
+ const float2 scale = float2(get_input("Scale").get_float_value_default(1.0f));
+
+ const float3x3 transformation = float3x3::from_translation_rotation_scale(
+ translation, rotation, scale);
+
+ result.transform(transformation);
+ result.get_realization_options().interpolation = get_interpolation();
+ }
+
+ Interpolation get_interpolation()
+ {
+ switch (bnode().custom1) {
+ case 0:
+ return Interpolation::Nearest;
+ case 1:
+ return Interpolation::Bilinear;
+ case 2:
+ return Interpolation::Bicubic;
+ }
+
+ BLI_assert_unreachable();
+ return Interpolation::Nearest;
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new TransformOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_transform_cc
void register_node_type_cmp_transform()
@@ -44,6 +110,7 @@ void register_node_type_cmp_transform()
cmp_node_type_base(&ntype, CMP_NODE_TRANSFORM, "Transform", NODE_CLASS_DISTORT);
ntype.declare = file_ns::cmp_node_transform_declare;
ntype.draw_buttons = file_ns::node_composit_buts_transform;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_translate.cc b/source/blender/nodes/composite/nodes/node_composite_translate.cc
index bbdc8ca4d31..e0f87ff604a 100644
--- a/source/blender/nodes/composite/nodes/node_composite_translate.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_translate.cc
@@ -5,20 +5,37 @@
* \ingroup cmpnodes
*/
+#include "BLI_float3x3.hh"
+#include "BLI_math_vec_types.hh"
+
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Translate ******************** */
namespace blender::nodes::node_composite_translate_cc {
+NODE_STORAGE_FUNCS(NodeTranslateData)
+
static void cmp_node_translate_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Float>(N_("X")).default_value(0.0f).min(-10000.0f).max(10000.0f);
- b.add_input<decl::Float>(N_("Y")).default_value(0.0f).min(-10000.0f).max(10000.0f);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("X"))
+ .default_value(0.0f)
+ .min(-10000.0f)
+ .max(10000.0f)
+ .compositor_expects_single_value();
+ b.add_input<decl::Float>(N_("Y"))
+ .default_value(0.0f)
+ .min(-10000.0f)
+ .max(10000.0f)
+ .compositor_expects_single_value();
b.add_output<decl::Color>(N_("Image"));
}
@@ -34,6 +51,54 @@ static void node_composit_buts_translate(uiLayout *layout, bContext *UNUSED(C),
uiItemR(layout, ptr, "wrap_axis", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class TranslateOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ Result &input = get_input("Image");
+ Result &result = get_result("Image");
+ input.pass_through(result);
+
+ float x = get_input("X").get_float_value_default(0.0f);
+ float y = get_input("Y").get_float_value_default(0.0f);
+ if (get_use_relative()) {
+ x *= input.domain().size.x;
+ y *= input.domain().size.y;
+ }
+
+ const float2 translation = float2(x, y);
+ const float3x3 transformation = float3x3::from_translation(translation);
+
+ result.transform(transformation);
+ result.get_realization_options().repeat_x = get_repeat_x();
+ result.get_realization_options().repeat_y = get_repeat_y();
+ }
+
+ bool get_use_relative()
+ {
+ return node_storage(bnode()).relative;
+ }
+
+ bool get_repeat_x()
+ {
+ return ELEM(node_storage(bnode()).wrap_axis, CMP_NODE_WRAP_X, CMP_NODE_WRAP_XY);
+ }
+
+ bool get_repeat_y()
+ {
+ return ELEM(node_storage(bnode()).wrap_axis, CMP_NODE_WRAP_Y, CMP_NODE_WRAP_XY);
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new TranslateOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_translate_cc
void register_node_type_cmp_translate()
@@ -48,6 +113,7 @@ void register_node_type_cmp_translate()
node_type_init(&ntype, file_ns::node_composit_init_translate);
node_type_storage(
&ntype, "NodeTranslateData", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_val_to_rgb.cc b/source/blender/nodes/composite/nodes/node_composite_val_to_rgb.cc
index f71028bf8c1..03a7bc61924 100644
--- a/source/blender/nodes/composite/nodes/node_composite_val_to_rgb.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_val_to_rgb.cc
@@ -5,16 +5,33 @@
* \ingroup cmpnodes
*/
+#include "BLI_assert.h"
+
+#include "IMB_colormanagement.h"
+
+#include "BKE_colorband.h"
+
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
+#include "BKE_colorband.h"
+
/* **************** VALTORGB ******************** */
-namespace blender::nodes::node_composite_val_to_rgb_cc {
+namespace blender::nodes::node_composite_color_ramp_cc {
static void cmp_node_valtorgb_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Fac")).default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
- b.add_output<decl::Color>(N_("Image"));
+ b.add_input<decl::Float>(N_("Fac"))
+ .default_value(0.5f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(1);
+ b.add_output<decl::Color>(N_("Image")).compositor_domain_priority(0);
b.add_output<decl::Float>(N_("Alpha"));
}
@@ -23,11 +40,94 @@ static void node_composit_init_valtorgb(bNodeTree *UNUSED(ntree), bNode *node)
node->storage = BKE_colorband_add(true);
}
-} // namespace blender::nodes::node_composite_val_to_rgb_cc
+using namespace blender::realtime_compositor;
+
+class ColorRampShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ struct ColorBand *color_band = get_color_band();
+
+ /* Common / easy case optimization. */
+ if ((color_band->tot <= 2) && (color_band->color_mode == COLBAND_BLEND_RGB)) {
+ float mul_bias[2];
+ switch (color_band->ipotype) {
+ case COLBAND_INTERP_LINEAR:
+ mul_bias[0] = 1.0f / (color_band->data[1].pos - color_band->data[0].pos);
+ mul_bias[1] = -mul_bias[0] * color_band->data[0].pos;
+ GPU_stack_link(material,
+ &bnode(),
+ "valtorgb_opti_linear",
+ inputs,
+ outputs,
+ GPU_uniform(mul_bias),
+ GPU_uniform(&color_band->data[0].r),
+ GPU_uniform(&color_band->data[1].r));
+ return;
+ case COLBAND_INTERP_CONSTANT:
+ mul_bias[1] = max_ff(color_band->data[0].pos, color_band->data[1].pos);
+ GPU_stack_link(material,
+ &bnode(),
+ "valtorgb_opti_constant",
+ inputs,
+ outputs,
+ GPU_uniform(&mul_bias[1]),
+ GPU_uniform(&color_band->data[0].r),
+ GPU_uniform(&color_band->data[1].r));
+ return;
+ case COLBAND_INTERP_EASE:
+ mul_bias[0] = 1.0f / (color_band->data[1].pos - color_band->data[0].pos);
+ mul_bias[1] = -mul_bias[0] * color_band->data[0].pos;
+ GPU_stack_link(material,
+ &bnode(),
+ "valtorgb_opti_ease",
+ inputs,
+ outputs,
+ GPU_uniform(mul_bias),
+ GPU_uniform(&color_band->data[0].r),
+ GPU_uniform(&color_band->data[1].r));
+ return;
+ default:
+ BLI_assert_unreachable();
+ return;
+ }
+ }
+
+ float *array, layer;
+ int size;
+ BKE_colorband_evaluate_table_rgba(color_band, &array, &size);
+ GPUNodeLink *tex = GPU_color_band(material, size, array, &layer);
+
+ if (color_band->ipotype == COLBAND_INTERP_CONSTANT) {
+ GPU_stack_link(
+ material, &bnode(), "valtorgb_nearest", inputs, outputs, tex, GPU_constant(&layer));
+ return;
+ }
+
+ GPU_stack_link(material, &bnode(), "valtorgb", inputs, outputs, tex, GPU_constant(&layer));
+ }
+
+ struct ColorBand *get_color_band()
+ {
+ return static_cast<struct ColorBand *>(bnode().storage);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new ColorRampShaderNode(node);
+}
+
+} // namespace blender::nodes::node_composite_color_ramp_cc
void register_node_type_cmp_valtorgb()
{
- namespace file_ns = blender::nodes::node_composite_val_to_rgb_cc;
+ namespace file_ns = blender::nodes::node_composite_color_ramp_cc;
static bNodeType ntype;
@@ -36,31 +136,63 @@ void register_node_type_cmp_valtorgb()
node_type_size(&ntype, 240, 200, 320);
node_type_init(&ntype, file_ns::node_composit_init_valtorgb);
node_type_storage(&ntype, "ColorBand", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
/* **************** RGBTOBW ******************** */
-namespace blender::nodes::node_composite_val_to_rgb_cc {
+namespace blender::nodes::node_composite_rgb_to_bw_cc {
static void cmp_node_rgbtobw_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({0.8f, 0.8f, 0.8f, 1.0f});
- b.add_output<decl::Color>(N_("Val"));
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({0.8f, 0.8f, 0.8f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_output<decl::Float>(N_("Val"));
+}
+
+using namespace blender::realtime_compositor;
+
+class RGBToBWShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ float luminance_coefficients[3];
+ IMB_colormanagement_get_luminance_coefficients(luminance_coefficients);
+
+ GPU_stack_link(material,
+ &bnode(),
+ "color_to_luminance",
+ inputs,
+ outputs,
+ GPU_constant(luminance_coefficients));
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new RGBToBWShaderNode(node);
}
-} // namespace blender::nodes::node_composite_val_to_rgb_cc
+} // namespace blender::nodes::node_composite_rgb_to_bw_cc
void register_node_type_cmp_rgbtobw()
{
- namespace file_ns = blender::nodes::node_composite_val_to_rgb_cc;
+ namespace file_ns = blender::nodes::node_composite_rgb_to_bw_cc;
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_RGBTOBW, "RGB to BW", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::cmp_node_rgbtobw_declare;
node_type_size_preset(&ntype, NODE_SIZE_SMALL);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_value.cc b/source/blender/nodes/composite/nodes/node_composite_value.cc
index a3269d3d1c2..a96e1db14ad 100644
--- a/source/blender/nodes/composite/nodes/node_composite_value.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_value.cc
@@ -5,6 +5,8 @@
* \ingroup cmpnodes
*/
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** VALUE ******************** */
@@ -16,6 +18,29 @@ static void cmp_node_value_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Float>(N_("Value")).default_value(0.5f);
}
+using namespace blender::realtime_compositor;
+
+class ValueOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ Result &result = get_result("Value");
+ result.allocate_single_value();
+
+ const bNodeSocket *socket = static_cast<bNodeSocket *>(bnode().outputs.first);
+ float value = static_cast<bNodeSocketValueFloat *>(socket->default_value)->value;
+
+ result.set_float_value(value);
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new ValueOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_value_cc
void register_node_type_cmp_value()
@@ -27,6 +52,7 @@ void register_node_type_cmp_value()
cmp_node_type_base(&ntype, CMP_NODE_VALUE, "Value", NODE_CLASS_INPUT);
ntype.declare = file_ns::cmp_node_value_declare;
node_type_size_preset(&ntype, NODE_SIZE_SMALL);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_vec_blur.cc b/source/blender/nodes/composite/nodes/node_composite_vec_blur.cc
index 741f2e0e816..515478da75d 100644
--- a/source/blender/nodes/composite/nodes/node_composite_vec_blur.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_vec_blur.cc
@@ -8,6 +8,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** VECTOR BLUR ******************** */
@@ -51,6 +53,23 @@ static void node_composit_buts_vecblur(uiLayout *layout, bContext *UNUSED(C), Po
uiItemR(layout, ptr, "use_curved", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class VectorBlurOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new VectorBlurOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_vec_blur_cc
void register_node_type_cmp_vecblur()
@@ -65,6 +84,7 @@ void register_node_type_cmp_vecblur()
node_type_init(&ntype, file_ns::node_composit_init_vecblur);
node_type_storage(
&ntype, "NodeBlurData", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_viewer.cc b/source/blender/nodes/composite/nodes/node_composite_viewer.cc
index 05f395183b5..4e82b31ca47 100644
--- a/source/blender/nodes/composite/nodes/node_composite_viewer.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_viewer.cc
@@ -5,6 +5,8 @@
* \ingroup cmpnodes
*/
+#include "BLI_math_vec_types.hh"
+
#include "BKE_global.h"
#include "BKE_image.h"
@@ -13,6 +15,13 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_shader.h"
+#include "GPU_state.h"
+#include "GPU_texture.h"
+
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
#include "node_composite_util.hh"
/* **************** VIEWER ******************** */
@@ -55,6 +64,125 @@ static void node_composit_buts_viewer_ex(uiLayout *layout, bContext *UNUSED(C),
}
}
+using namespace blender::realtime_compositor;
+
+class ViewerOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ const Result &image = get_input("Image");
+ const Result &alpha = get_input("Alpha");
+
+ if (image.is_single_value() && alpha.is_single_value()) {
+ execute_clear();
+ }
+ else if (ignore_alpha()) {
+ execute_ignore_alpha();
+ }
+ else if (!node().input_by_identifier("Alpha")->is_logically_linked()) {
+ execute_copy();
+ }
+ else {
+ execute_set_alpha();
+ }
+ }
+
+ /* Executes when all inputs are single values, in which case, the output texture can just be
+ * cleared to the appropriate color. */
+ void execute_clear()
+ {
+ const Result &image = get_input("Image");
+ const Result &alpha = get_input("Alpha");
+
+ float4 color = image.get_color_value();
+ if (ignore_alpha()) {
+ color.w = 1.0f;
+ }
+ else if (node().input_by_identifier("Alpha")->is_logically_linked()) {
+ color.w = alpha.get_float_value();
+ }
+
+ GPU_texture_clear(context().get_output_texture(), GPU_DATA_FLOAT, color);
+ }
+
+ /* Executes when the alpha channel of the image is ignored. */
+ void execute_ignore_alpha()
+ {
+ GPUShader *shader = shader_manager().get("compositor_convert_color_to_opaque");
+ GPU_shader_bind(shader);
+
+ const Result &image = get_input("Image");
+ image.bind_as_texture(shader, "input_tx");
+
+ GPUTexture *output_texture = context().get_output_texture();
+ const int image_unit = GPU_shader_get_texture_binding(shader, "output_img");
+ GPU_texture_image_bind(output_texture, image_unit);
+
+ compute_dispatch_threads_at_least(shader, compute_domain().size);
+
+ image.unbind_as_texture();
+ GPU_texture_image_unbind(output_texture);
+ GPU_shader_unbind();
+ }
+
+ /* Executes when the image texture is written with no adjustments and can thus be copied directly
+ * to the output texture. */
+ void execute_copy()
+ {
+ const Result &image = get_input("Image");
+
+ /* Make sure any prior writes to the texture are reflected before copying it. */
+ GPU_memory_barrier(GPU_BARRIER_TEXTURE_UPDATE);
+
+ GPU_texture_copy(context().get_output_texture(), image.texture());
+ }
+
+ /* Executes when the alpha channel of the image is set as the value of the input alpha. */
+ void execute_set_alpha()
+ {
+ GPUShader *shader = shader_manager().get("compositor_set_alpha");
+ GPU_shader_bind(shader);
+
+ const Result &image = get_input("Image");
+ image.bind_as_texture(shader, "image_tx");
+
+ const Result &alpha = get_input("Alpha");
+ alpha.bind_as_texture(shader, "alpha_tx");
+
+ GPUTexture *output_texture = context().get_output_texture();
+ const int image_unit = GPU_shader_get_texture_binding(shader, "output_img");
+ GPU_texture_image_bind(output_texture, image_unit);
+
+ compute_dispatch_threads_at_least(shader, compute_domain().size);
+
+ image.unbind_as_texture();
+ alpha.unbind_as_texture();
+ GPU_texture_image_unbind(output_texture);
+ GPU_shader_unbind();
+ }
+
+ /* If true, the alpha channel of the image is set to 1, that is, it becomes opaque. If false, the
+ * alpha channel of the image is retained, but only if the alpha input is not linked. If the
+ * alpha input is linked, it the value of that input will be used as the alpha of the image. */
+ bool ignore_alpha()
+ {
+ return bnode().custom2 & CMP_NODE_OUTPUT_IGNORE_ALPHA;
+ }
+
+ /* The operation domain have the same dimensions of the output without any transformations. */
+ Domain compute_domain() override
+ {
+ return Domain(context().get_output_size());
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new ViewerOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_viewer_cc
void register_node_type_cmp_viewer()
@@ -70,6 +198,7 @@ void register_node_type_cmp_viewer()
ntype.flag |= NODE_PREVIEW;
node_type_init(&ntype, file_ns::node_composit_init_viewer);
node_type_storage(&ntype, "ImageUser", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
ntype.no_muting = true;
diff --git a/source/blender/nodes/composite/nodes/node_composite_zcombine.cc b/source/blender/nodes/composite/nodes/node_composite_zcombine.cc
index be90aeb7acc..e5f460099e9 100644
--- a/source/blender/nodes/composite/nodes/node_composite_zcombine.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_zcombine.cc
@@ -8,6 +8,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Z COMBINE ******************** */
@@ -33,6 +35,24 @@ static void node_composit_buts_zcombine(uiLayout *layout, bContext *UNUSED(C), P
uiItemR(col, ptr, "use_antialias_z", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class ZCombineOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ get_result("Z").allocate_invalid();
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new ZCombineOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_zcombine_cc
void register_node_type_cmp_zcombine()
@@ -44,6 +64,7 @@ void register_node_type_cmp_zcombine()
cmp_node_type_base(&ntype, CMP_NODE_ZCOMBINE, "Z Combine", NODE_CLASS_OP_COLOR);
ntype.declare = file_ns::cmp_node_zcombine_declare;
ntype.draw_buttons = file_ns::node_composit_buts_zcombine;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/function/node_function_util.hh b/source/blender/nodes/function/node_function_util.hh
index fd0b6c31b1d..059b2f9bc17 100644
--- a/source/blender/nodes/function/node_function_util.hh
+++ b/source/blender/nodes/function/node_function_util.hh
@@ -23,4 +23,6 @@
#include "FN_multi_function_builder.hh"
+#include "RNA_access.h"
+
void fn_node_type_base(struct bNodeType *ntype, int type, const char *name, short nclass);
diff --git a/source/blender/nodes/function/nodes/node_fn_align_euler_to_vector.cc b/source/blender/nodes/function/nodes/node_fn_align_euler_to_vector.cc
index 2cb455832e5..e5c89567d44 100644
--- a/source/blender/nodes/function/nodes/node_fn_align_euler_to_vector.cc
+++ b/source/blender/nodes/function/nodes/node_fn_align_euler_to_vector.cc
@@ -150,7 +150,7 @@ class MF_AlignEulerToVector : public fn::MultiFunction {
static fn::MFSignature create_signature()
{
- fn::MFSignatureBuilder signature{"Align Euler To Vector"};
+ fn::MFSignatureBuilder signature{"Align Euler to Vector"};
signature.single_input<float3>("Rotation");
signature.single_input<float>("Factor");
signature.single_input<float3>("Vector");
@@ -190,7 +190,7 @@ class MF_AlignEulerToVector : public fn::MultiFunction {
static void fn_node_align_euler_to_vector_build_multi_function(NodeMultiFunctionBuilder &builder)
{
- bNode &node = builder.node();
+ const bNode &node = builder.node();
builder.construct_and_set_matching_fn<MF_AlignEulerToVector>(node.custom1, node.custom2);
}
diff --git a/source/blender/nodes/function/nodes/node_fn_boolean_math.cc b/source/blender/nodes/function/nodes/node_fn_boolean_math.cc
index b6d7e6c9a5f..5fc28509a49 100644
--- a/source/blender/nodes/function/nodes/node_fn_boolean_math.cc
+++ b/source/blender/nodes/function/nodes/node_fn_boolean_math.cc
@@ -68,7 +68,7 @@ static void node_gather_link_searches(GatherLinkSearchOpParams &params)
}
}
-static const fn::MultiFunction *get_multi_function(bNode &bnode)
+static const fn::MultiFunction *get_multi_function(const bNode &bnode)
{
static auto exec_preset = fn::CustomMF_presets::AllSpanOrSingle();
static fn::CustomMF_SI_SI_SO<bool, bool, bool> and_fn{
diff --git a/source/blender/nodes/function/nodes/node_fn_combine_color.cc b/source/blender/nodes/function/nodes/node_fn_combine_color.cc
index c5fd3ce38a1..450cd166e78 100644
--- a/source/blender/nodes/function/nodes/node_fn_combine_color.cc
+++ b/source/blender/nodes/function/nodes/node_fn_combine_color.cc
@@ -49,7 +49,7 @@ static void fn_node_combine_color_init(bNodeTree *UNUSED(tree), bNode *node)
node->storage = data;
}
-static const fn::MultiFunction *get_multi_function(bNode &bnode)
+static const fn::MultiFunction *get_multi_function(const bNode &bnode)
{
const NodeCombSepColor &storage = node_storage(bnode);
diff --git a/source/blender/nodes/function/nodes/node_fn_compare.cc b/source/blender/nodes/function/nodes/node_fn_compare.cc
index e3f13dc7d6b..122d1a3c93e 100644
--- a/source/blender/nodes/function/nodes/node_fn_compare.cc
+++ b/source/blender/nodes/function/nodes/node_fn_compare.cc
@@ -167,7 +167,7 @@ static float component_average(float3 a)
return (a.x + a.y + a.z) / 3.0f;
}
-static const fn::MultiFunction *get_multi_function(bNode &node)
+static const fn::MultiFunction *get_multi_function(const bNode &node)
{
const NodeFunctionCompare *data = (NodeFunctionCompare *)node.storage;
diff --git a/source/blender/nodes/function/nodes/node_fn_float_to_int.cc b/source/blender/nodes/function/nodes/node_fn_float_to_int.cc
index 9c9d8620a7e..aad2f532d20 100644
--- a/source/blender/nodes/function/nodes/node_fn_float_to_int.cc
+++ b/source/blender/nodes/function/nodes/node_fn_float_to_int.cc
@@ -39,7 +39,7 @@ static void node_float_to_int_label(const bNodeTree *UNUSED(ntree),
BLI_strncpy(label, IFACE_(name), maxlen);
}
-static const fn::MultiFunction *get_multi_function(bNode &bnode)
+static const fn::MultiFunction *get_multi_function(const bNode &bnode)
{
static auto exec_preset = fn::CustomMF_presets::AllSpanOrSingle();
static fn::CustomMF_SI_SO<float, int> round_fn{
diff --git a/source/blender/nodes/function/nodes/node_fn_input_bool.cc b/source/blender/nodes/function/nodes/node_fn_input_bool.cc
index 5ced719627f..717f4d1ac6b 100644
--- a/source/blender/nodes/function/nodes/node_fn_input_bool.cc
+++ b/source/blender/nodes/function/nodes/node_fn_input_bool.cc
@@ -22,7 +22,7 @@ static void fn_node_input_bool_layout(uiLayout *layout, bContext *UNUSED(C), Poi
static void fn_node_input_bool_build_multi_function(NodeMultiFunctionBuilder &builder)
{
- bNode &bnode = builder.node();
+ const bNode &bnode = builder.node();
NodeInputBool *node_storage = static_cast<NodeInputBool *>(bnode.storage);
builder.construct_and_set_matching_fn<fn::CustomMF_Constant<bool>>(node_storage->boolean);
}
diff --git a/source/blender/nodes/function/nodes/node_fn_input_color.cc b/source/blender/nodes/function/nodes/node_fn_input_color.cc
index 46787f7575d..cdad1542c66 100644
--- a/source/blender/nodes/function/nodes/node_fn_input_color.cc
+++ b/source/blender/nodes/function/nodes/node_fn_input_color.cc
@@ -23,7 +23,7 @@ static void fn_node_input_color_layout(uiLayout *layout, bContext *UNUSED(C), Po
static void fn_node_input_color_build_multi_function(
blender::nodes::NodeMultiFunctionBuilder &builder)
{
- bNode &bnode = builder.node();
+ const bNode &bnode = builder.node();
NodeInputColor *node_storage = static_cast<NodeInputColor *>(bnode.storage);
blender::ColorGeometry4f color = (ColorGeometry4f)node_storage->color;
builder.construct_and_set_matching_fn<blender::fn::CustomMF_Constant<ColorGeometry4f>>(color);
diff --git a/source/blender/nodes/function/nodes/node_fn_input_int.cc b/source/blender/nodes/function/nodes/node_fn_input_int.cc
index 1e5dcd5ae7a..16506b5f9b8 100644
--- a/source/blender/nodes/function/nodes/node_fn_input_int.cc
+++ b/source/blender/nodes/function/nodes/node_fn_input_int.cc
@@ -22,7 +22,7 @@ static void fn_node_input_int_layout(uiLayout *layout, bContext *UNUSED(C), Poin
static void fn_node_input_int_build_multi_function(NodeMultiFunctionBuilder &builder)
{
- bNode &bnode = builder.node();
+ const bNode &bnode = builder.node();
NodeInputInt *node_storage = static_cast<NodeInputInt *>(bnode.storage);
builder.construct_and_set_matching_fn<fn::CustomMF_Constant<int>>(node_storage->integer);
}
diff --git a/source/blender/nodes/function/nodes/node_fn_input_string.cc b/source/blender/nodes/function/nodes/node_fn_input_string.cc
index 124a8572f78..129d19f4f04 100644
--- a/source/blender/nodes/function/nodes/node_fn_input_string.cc
+++ b/source/blender/nodes/function/nodes/node_fn_input_string.cc
@@ -20,7 +20,7 @@ static void fn_node_input_string_layout(uiLayout *layout, bContext *UNUSED(C), P
static void fn_node_input_string_build_multi_function(NodeMultiFunctionBuilder &builder)
{
- bNode &bnode = builder.node();
+ const bNode &bnode = builder.node();
NodeInputString *node_storage = static_cast<NodeInputString *>(bnode.storage);
std::string string = std::string((node_storage->string) ? node_storage->string : "");
builder.construct_and_set_matching_fn<fn::CustomMF_Constant<std::string>>(std::move(string));
diff --git a/source/blender/nodes/function/nodes/node_fn_input_vector.cc b/source/blender/nodes/function/nodes/node_fn_input_vector.cc
index 898c19e92f0..de894a4038d 100644
--- a/source/blender/nodes/function/nodes/node_fn_input_vector.cc
+++ b/source/blender/nodes/function/nodes/node_fn_input_vector.cc
@@ -22,7 +22,7 @@ static void fn_node_input_vector_layout(uiLayout *layout, bContext *UNUSED(C), P
static void fn_node_input_vector_build_multi_function(NodeMultiFunctionBuilder &builder)
{
- bNode &bnode = builder.node();
+ const bNode &bnode = builder.node();
NodeInputVector *node_storage = static_cast<NodeInputVector *>(bnode.storage);
float3 vector(node_storage->vector);
builder.construct_and_set_matching_fn<fn::CustomMF_Constant<float3>>(vector);
diff --git a/source/blender/nodes/function/nodes/node_fn_rotate_euler.cc b/source/blender/nodes/function/nodes/node_fn_rotate_euler.cc
index a4fc1a6bfd1..299c0f7a932 100644
--- a/source/blender/nodes/function/nodes/node_fn_rotate_euler.cc
+++ b/source/blender/nodes/function/nodes/node_fn_rotate_euler.cc
@@ -52,7 +52,7 @@ static void fn_node_rotate_euler_layout(uiLayout *layout, bContext *UNUSED(C), P
uiItemR(layout, ptr, "space", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
}
-static const fn::MultiFunction *get_multi_function(bNode &bnode)
+static const fn::MultiFunction *get_multi_function(const bNode &bnode)
{
static fn::CustomMF_SI_SI_SO<float3, float3, float3> obj_euler_rot{
"Rotate Euler by Euler/Object", [](const float3 &input, const float3 &rotation) {
diff --git a/source/blender/nodes/function/nodes/node_fn_separate_color.cc b/source/blender/nodes/function/nodes/node_fn_separate_color.cc
index 1701dfdc6fa..19613427835 100644
--- a/source/blender/nodes/function/nodes/node_fn_separate_color.cc
+++ b/source/blender/nodes/function/nodes/node_fn_separate_color.cc
@@ -60,22 +60,41 @@ class SeparateRGBAFunction : public fn::MultiFunction {
{
const VArray<ColorGeometry4f> &colors = params.readonly_single_input<ColorGeometry4f>(0,
"Color");
- MutableSpan<float> red = params.uninitialized_single_output<float>(1, "Red");
- MutableSpan<float> green = params.uninitialized_single_output<float>(2, "Green");
- MutableSpan<float> blue = params.uninitialized_single_output<float>(3, "Blue");
+
+ MutableSpan<float> red = params.uninitialized_single_output_if_required<float>(1, "Red");
+ MutableSpan<float> green = params.uninitialized_single_output_if_required<float>(2, "Green");
+ MutableSpan<float> blue = params.uninitialized_single_output_if_required<float>(3, "Blue");
MutableSpan<float> alpha = params.uninitialized_single_output_if_required<float>(4, "Alpha");
- for (int64_t i : mask) {
- red[i] = colors[i].r;
- green[i] = colors[i].g;
- blue[i] = colors[i].b;
+ std::array<MutableSpan<float>, 4> outputs = {red, green, blue, alpha};
+ Vector<int> used_outputs;
+ if (!red.is_empty()) {
+ used_outputs.append(0);
+ }
+ if (!green.is_empty()) {
+ used_outputs.append(1);
+ }
+ if (!blue.is_empty()) {
+ used_outputs.append(2);
}
-
if (!alpha.is_empty()) {
- for (int64_t i : mask) {
- alpha[i] = colors[i].a;
- }
+ used_outputs.append(3);
}
+
+ devirtualize_varray(colors, [&](auto colors) {
+ mask.to_best_mask_type([&](auto mask) {
+ const int used_outputs_num = used_outputs.size();
+ const int *used_outputs_data = used_outputs.data();
+
+ for (const int64_t i : mask) {
+ const ColorGeometry4f &color = colors[i];
+ for (const int out_i : IndexRange(used_outputs_num)) {
+ const int channel = used_outputs_data[out_i];
+ outputs[channel][i] = color[channel];
+ }
+ }
+ });
+ });
}
};
diff --git a/source/blender/nodes/geometry/CMakeLists.txt b/source/blender/nodes/geometry/CMakeLists.txt
index 54fda328ca8..31c00cc6b82 100644
--- a/source/blender/nodes/geometry/CMakeLists.txt
+++ b/source/blender/nodes/geometry/CMakeLists.txt
@@ -57,10 +57,13 @@ set(SRC
nodes/node_geo_curve_to_mesh.cc
nodes/node_geo_curve_to_points.cc
nodes/node_geo_curve_trim.cc
+ nodes/node_geo_deform_curves_on_surface.cc
nodes/node_geo_delete_geometry.cc
nodes/node_geo_distribute_points_on_faces.cc
nodes/node_geo_dual_mesh.cc
nodes/node_geo_duplicate_elements.cc
+ nodes/node_geo_edge_paths_to_curves.cc
+ nodes/node_geo_edge_paths_to_selection.cc
nodes/node_geo_edge_split.cc
nodes/node_geo_extrude_mesh.cc
nodes/node_geo_field_at_index.cc
@@ -89,12 +92,14 @@ set(SRC
nodes/node_geo_input_radius.cc
nodes/node_geo_input_scene_time.cc
nodes/node_geo_input_shade_smooth.cc
+ nodes/node_geo_input_shortest_edge_paths.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_interpolate_domain.cc
nodes/node_geo_is_viewport.cc
nodes/node_geo_join_geometry.cc
nodes/node_geo_material_replace.cc
@@ -111,7 +116,9 @@ set(SRC
nodes/node_geo_mesh_subdivide.cc
nodes/node_geo_mesh_to_curve.cc
nodes/node_geo_mesh_to_points.cc
+ nodes/node_geo_mesh_to_volume.cc
nodes/node_geo_object_info.cc
+ nodes/node_geo_points.cc
nodes/node_geo_points_to_vertices.cc
nodes/node_geo_points_to_volume.cc
nodes/node_geo_proximity.cc
@@ -143,7 +150,10 @@ set(SRC
nodes/node_geo_transform.cc
nodes/node_geo_translate_instances.cc
nodes/node_geo_triangulate.cc
+ nodes/node_geo_uv_pack_islands.cc
+ nodes/node_geo_uv_unwrap.cc
nodes/node_geo_viewer.cc
+ nodes/node_geo_volume_cube.cc
nodes/node_geo_volume_to_mesh.cc
node_geometry_exec.cc
diff --git a/source/blender/nodes/geometry/node_geometry_tree.cc b/source/blender/nodes/geometry/node_geometry_tree.cc
index 38e914b9a9f..c6914bbcb0c 100644
--- a/source/blender/nodes/geometry/node_geometry_tree.cc
+++ b/source/blender/nodes/geometry/node_geometry_tree.cc
@@ -7,6 +7,7 @@
#include "NOD_geometry.h"
#include "BKE_context.h"
+#include "BKE_layer.h"
#include "BKE_node.h"
#include "BKE_object.h"
@@ -32,7 +33,7 @@ static void geometry_node_tree_get_from_context(const bContext *C,
ID **r_from)
{
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *ob = OBACT(view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
if (ob == nullptr) {
return;
@@ -109,6 +110,7 @@ void register_node_tree_type_geo()
MEM_callocN(sizeof(bNodeTreeType), "geometry node tree type"));
tt->type = NTREE_GEOMETRY;
strcpy(tt->idname, "GeometryNodeTree");
+ strcpy(tt->group_idname, "GeometryNodeGroup");
strcpy(tt->ui_name, N_("Geometry Node Editor"));
tt->ui_icon = ICON_GEOMETRY_NODES;
strcpy(tt->ui_description, N_("Geometry nodes"));
diff --git a/source/blender/nodes/geometry/node_geometry_util.hh b/source/blender/nodes/geometry/node_geometry_util.hh
index efb7efaf1cc..4db4d8bb097 100644
--- a/source/blender/nodes/geometry/node_geometry_util.hh
+++ b/source/blender/nodes/geometry/node_geometry_util.hh
@@ -20,6 +20,8 @@
#include "NOD_socket_declarations.hh"
#include "NOD_socket_declarations_geometry.hh"
+#include "RNA_access.h"
+
#include "node_util.h"
void geo_node_type_base(struct bNodeType *ntype, int type, const char *name, short nclass);
@@ -34,7 +36,8 @@ void transform_mesh(Mesh &mesh,
const float3 rotation,
const float3 scale);
-void transform_geometry_set(GeometrySet &geometry,
+void transform_geometry_set(GeoNodeExecParams &params,
+ GeometrySet &geometry,
const float4x4 &transform,
const Depsgraph &depsgraph);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc b/source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc
index a7404af8564..13a9cdc8600 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc
@@ -192,7 +192,7 @@ static void node_gather_link_searches(GatherLinkSearchOpParams &params)
}
}
-template<typename T> class AccumulateFieldInput final : public GeometryFieldInput {
+template<typename T> class AccumulateFieldInput final : public bke::GeometryFieldInput {
private:
Field<T> input_;
Field<int> group_index_;
@@ -204,7 +204,7 @@ template<typename T> class AccumulateFieldInput final : public GeometryFieldInpu
Field<T> input,
Field<int> group_index,
AccumulationMode accumulation_mode)
- : GeometryFieldInput(CPPType::get<T>(), "Accumulation"),
+ : bke::GeometryFieldInput(CPPType::get<T>(), "Accumulation"),
input_(input),
group_index_(group_index),
source_domain_(source_domain),
@@ -212,21 +212,25 @@ template<typename T> class AccumulateFieldInput final : public GeometryFieldInpu
{
}
- GVArray get_varray_for_context(const GeometryComponent &component,
- const eAttrDomain domain,
- IndexMask UNUSED(mask)) const final
+ GVArray get_varray_for_context(const bke::GeometryFieldContext &context,
+ const IndexMask /*mask*/) const final
{
- const GeometryComponentFieldContext field_context{component, source_domain_};
- const int domain_num = component.attribute_domain_num(field_context.domain());
+ const AttributeAccessor attributes = *context.attributes();
+ const int domain_size = attributes.domain_size(source_domain_);
+ if (domain_size == 0) {
+ return {};
+ }
- fn::FieldEvaluator evaluator{field_context, domain_num};
+ const bke::GeometryFieldContext source_context{
+ context.geometry(), context.type(), source_domain_};
+ fn::FieldEvaluator evaluator{source_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);
+ const VArray<T> values = evaluator.get_evaluated<T>(0);
+ const VArray<int> group_indices = evaluator.get_evaluated<int>(1);
- Array<T> accumulations_out(domain_num);
+ Array<T> accumulations_out(domain_size);
if (group_indices.is_single()) {
T accumulation = T();
@@ -261,8 +265,8 @@ template<typename T> class AccumulateFieldInput final : public GeometryFieldInpu
}
}
- return component.attribute_try_adapt_domain<T>(
- VArray<T>::ForContainer(std::move(accumulations_out)), source_domain_, domain);
+ return attributes.adapt_domain<T>(
+ VArray<T>::ForContainer(std::move(accumulations_out)), source_domain_, context.domain());
}
uint64_t hash() const override
@@ -283,7 +287,7 @@ template<typename T> class AccumulateFieldInput final : public GeometryFieldInpu
}
};
-template<typename T> class TotalFieldInput final : public GeometryFieldInput {
+template<typename T> class TotalFieldInput final : public bke::GeometryFieldInput {
private:
Field<T> input_;
Field<int> group_index_;
@@ -291,36 +295,40 @@ template<typename T> class TotalFieldInput final : public GeometryFieldInput {
public:
TotalFieldInput(const eAttrDomain source_domain, Field<T> input, Field<int> group_index)
- : GeometryFieldInput(CPPType::get<T>(), "Total Value"),
+ : bke::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 eAttrDomain domain,
- IndexMask UNUSED(mask)) const final
+ GVArray get_varray_for_context(const bke::GeometryFieldContext &context,
+ IndexMask /*mask*/) const final
{
- const GeometryComponentFieldContext field_context{component, source_domain_};
- const int domain_num = component.attribute_domain_num(field_context.domain());
+ const AttributeAccessor attributes = *context.attributes();
+ const int domain_size = attributes.domain_size(source_domain_);
+ if (domain_size == 0) {
+ return {};
+ }
- fn::FieldEvaluator evaluator{field_context, domain_num};
+ const bke::GeometryFieldContext source_context{
+ context.geometry(), context.type(), source_domain_};
+ fn::FieldEvaluator evaluator{source_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);
+ 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_num);
+ return VArray<T>::ForSingle(accumulation, domain_size);
}
- Array<T> accumulations_out(domain_num);
+ 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]);
@@ -330,8 +338,8 @@ template<typename T> class TotalFieldInput final : public GeometryFieldInput {
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);
+ return attributes.adapt_domain<T>(
+ VArray<T>::ForContainer(std::move(accumulations_out)), source_domain_, context.domain());
}
uint64_t hash() const override
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 496fb081d6b..8c11288efdd 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc
@@ -111,19 +111,26 @@ static void try_capture_field_on_geometry(GeometryComponent &component,
const eAttrDomain domain,
const GField &field)
{
- GeometryComponentFieldContext field_context{component, domain};
- const int domain_num = component.attribute_domain_num(domain);
- const IndexMask mask{IndexMask(domain_num)};
+ const int domain_size = component.attribute_domain_size(domain);
+ if (domain_size == 0) {
+ return;
+ }
+ bke::GeometryFieldContext field_context{component, domain};
+ MutableAttributeAccessor attributes = *component.attributes_for_write();
+ const IndexMask mask{IndexMask(domain_size)};
const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(field.cpp_type());
- OutputAttribute output_attribute = component.attribute_try_get_for_output_only(
+ GAttributeWriter output_attribute = attributes.lookup_or_add_for_write(
attribute_id, domain, data_type);
+ if (!output_attribute) {
+ return;
+ }
fn::FieldEvaluator evaluator{field_context, &mask};
- evaluator.add_with_destination(field, output_attribute.varray());
+ evaluator.add_with_destination(field, output_attribute.varray);
evaluator.evaluate();
- output_attribute.save();
+ output_attribute.finish();
}
static StringRefNull identifier_suffix(eCustomDataType data_type)
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
index 8ab0eb678e7..f6ea6073459 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc
@@ -65,18 +65,17 @@ static void node_update(bNodeTree *ntree, bNode *node)
static void node_geo_exec(GeoNodeExecParams params)
{
- GeometryComponentType component = (GeometryComponentType)params.node().custom1;
- GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ const GeometryComponentType component = (GeometryComponentType)params.node().custom1;
+ const 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_num(ATTR_DOMAIN_POINT));
- params.set_output("Edge Count", component->attribute_domain_num(ATTR_DOMAIN_EDGE));
- params.set_output("Face Count", component->attribute_domain_num(ATTR_DOMAIN_FACE));
- params.set_output("Face Corner Count",
- component->attribute_domain_num(ATTR_DOMAIN_CORNER));
+ if (const MeshComponent *component = geometry_set.get_component_for_read<MeshComponent>()) {
+ const AttributeAccessor attributes = *component->attributes();
+ params.set_output("Point Count", attributes.domain_size(ATTR_DOMAIN_POINT));
+ params.set_output("Edge Count", attributes.domain_size(ATTR_DOMAIN_EDGE));
+ params.set_output("Face Count", attributes.domain_size(ATTR_DOMAIN_FACE));
+ params.set_output("Face Corner Count", attributes.domain_size(ATTR_DOMAIN_CORNER));
}
else {
params.set_default_remaining_outputs();
@@ -84,10 +83,11 @@ static void node_geo_exec(GeoNodeExecParams params)
break;
}
case GEO_COMPONENT_TYPE_CURVE: {
- if (geometry_set.has_curves()) {
- const CurveComponent *component = geometry_set.get_component_for_read<CurveComponent>();
- params.set_output("Point Count", component->attribute_domain_num(ATTR_DOMAIN_POINT));
- params.set_output("Spline Count", component->attribute_domain_num(ATTR_DOMAIN_CURVE));
+ if (const CurveComponent *component =
+ geometry_set.get_component_for_read<CurveComponent>()) {
+ const AttributeAccessor attributes = *component->attributes();
+ params.set_output("Point Count", attributes.domain_size(ATTR_DOMAIN_POINT));
+ params.set_output("Spline Count", attributes.domain_size(ATTR_DOMAIN_CURVE));
}
else {
params.set_default_remaining_outputs();
@@ -95,10 +95,10 @@ static void node_geo_exec(GeoNodeExecParams params)
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_num(ATTR_DOMAIN_POINT));
+ if (const PointCloudComponent *component =
+ geometry_set.get_component_for_read<PointCloudComponent>()) {
+ const AttributeAccessor attributes = *component->attributes();
+ params.set_output("Point Count", attributes.domain_size(ATTR_DOMAIN_POINT));
}
else {
params.set_default_remaining_outputs();
@@ -106,10 +106,10 @@ static void node_geo_exec(GeoNodeExecParams params)
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_num(ATTR_DOMAIN_INSTANCE));
+ if (const InstancesComponent *component =
+ geometry_set.get_component_for_read<InstancesComponent>()) {
+ const AttributeAccessor attributes = *component->attributes();
+ params.set_output("Instance Count", attributes.domain_size(ATTR_DOMAIN_INSTANCE));
}
else {
params.set_default_remaining_outputs();
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 35404725998..af0007c2fa4 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc
@@ -195,15 +195,19 @@ static void node_geo_exec(GeoNodeExecParams params)
const Field<float> input_field = params.get_input<Field<float>>("Attribute");
Vector<float> data;
for (const GeometryComponent *component : components) {
- if (component->attribute_domain_supported(domain)) {
- GeometryComponentFieldContext field_context{*component, domain};
- const int domain_num = component->attribute_domain_num(domain);
+ const std::optional<AttributeAccessor> attributes = component->attributes();
+ if (!attributes.has_value()) {
+ continue;
+ }
+ if (attributes->domain_supported(domain)) {
+ bke::GeometryFieldContext field_context{*component, domain};
+ const int domain_num = attributes->domain_size(domain);
fn::FieldEvaluator data_evaluator{field_context, domain_num};
data_evaluator.add(input_field);
data_evaluator.set_selection(selection_field);
data_evaluator.evaluate();
- const VArray<float> &component_data = data_evaluator.get_evaluated<float>(0);
+ 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();
@@ -273,15 +277,19 @@ static void node_geo_exec(GeoNodeExecParams params)
const Field<float3> input_field = params.get_input<Field<float3>>("Attribute_001");
Vector<float3> data;
for (const GeometryComponent *component : components) {
- if (component->attribute_domain_supported(domain)) {
- GeometryComponentFieldContext field_context{*component, domain};
- const int domain_num = component->attribute_domain_num(domain);
+ const std::optional<AttributeAccessor> attributes = component->attributes();
+ if (!attributes.has_value()) {
+ continue;
+ }
+ if (attributes->domain_supported(domain)) {
+ bke::GeometryFieldContext field_context{*component, domain};
+ const int domain_num = attributes->domain_size(domain);
fn::FieldEvaluator data_evaluator{field_context, domain_num};
data_evaluator.add(input_field);
data_evaluator.set_selection(selection_field);
data_evaluator.evaluate();
- const VArray<float3> &component_data = data_evaluator.get_evaluated<float3>(0);
+ 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();
diff --git a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc
index daeca311e08..a6af74645b6 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc
@@ -2,6 +2,7 @@
#include "DNA_mesh_types.h"
+#include "BKE_geometry_set_instances.hh"
#include "BKE_mesh_boolean_convert.hh"
#include "UI_interface.h"
@@ -153,17 +154,15 @@ static void node_geo_exec(GeoNodeExecParams params)
/* Store intersecting edges in attribute. */
if (attribute_outputs.intersecting_edges_id) {
- MeshComponent mesh_component;
- mesh_component.replace(result, GeometryOwnershipType::Editable);
- OutputAttribute_Typed<bool> attribute = mesh_component.attribute_try_get_for_output_only<bool>(
+ MutableAttributeAccessor attributes = result->attributes_for_write();
+ SpanAttributeWriter<bool> selection = attributes.lookup_or_add_for_write_only_span<bool>(
attribute_outputs.intersecting_edges_id.get(), ATTR_DOMAIN_EDGE);
- MutableSpan<bool> selection = attribute.as_span();
- selection.fill(false);
+
+ selection.span.fill(false);
for (const int i : intersecting_edges) {
- selection[i] = true;
+ selection.span[i] = true;
}
-
- attribute.save();
+ selection.finish();
params.set_output(
"Intersecting Edges",
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 00b10cc8a2f..75c198f7bdd 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc
@@ -50,7 +50,7 @@ static void node_geo_exec(GeoNodeExecParams params)
}
if (sub_min == float3(FLT_MAX)) {
- sub_geometry.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ sub_geometry.remove_geometry_during_modify();
}
else {
const float3 scale = sub_max - sub_min;
@@ -58,7 +58,7 @@ static void node_geo_exec(GeoNodeExecParams params)
Mesh *mesh = geometry::create_cuboid_mesh(scale, 2, 2, 2, "uv_map");
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});
+ sub_geometry.keep_only_during_modify({GEO_COMPONENT_TYPE_MESH});
}
});
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 31f706c497c..c8b692651e9 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc
@@ -22,8 +22,6 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Geometry>(N_("Convex Hull"));
}
-using bke::GeometryInstanceGroup;
-
#ifdef WITH_BULLET
static Mesh *hull_from_bullet(const Mesh *mesh, Span<float3> coords)
@@ -48,6 +46,7 @@ static Mesh *hull_from_bullet(const Mesh *mesh, Span<float3> coords)
}
/* Copy vertices. */
+ MutableSpan<MVert> dst_verts = result->verts_for_write();
for (const int i : IndexRange(verts_num)) {
float co[3];
int original_index;
@@ -61,7 +60,7 @@ static Mesh *hull_from_bullet(const Mesh *mesh, Span<float3> coords)
}
# endif
/* Copy the position of the original point. */
- copy_v3_v3(result->mvert[i].co, co);
+ copy_v3_v3(dst_verts[i].co, co);
}
else {
BLI_assert_msg(0, "Unexpected new vertex in hull output");
@@ -75,6 +74,8 @@ static Mesh *hull_from_bullet(const Mesh *mesh, Span<float3> coords)
* to a loop and edges need to be created from that. */
Array<MLoop> mloop_src(loops_num);
uint edge_index = 0;
+ MutableSpan<MEdge> edges = result->edges_for_write();
+
for (const int i : IndexRange(loops_num)) {
int v_from;
int v_to;
@@ -83,7 +84,7 @@ static Mesh *hull_from_bullet(const Mesh *mesh, Span<float3> coords)
mloop_src[i].v = (uint)v_from;
/* Add edges for ascending order loops only. */
if (v_from < v_to) {
- MEdge &edge = result->medge[edge_index];
+ MEdge &edge = edges[edge_index];
edge.v1 = v_from;
edge.v2 = v_to;
edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
@@ -97,7 +98,7 @@ static Mesh *hull_from_bullet(const Mesh *mesh, Span<float3> coords)
}
if (edges_num == 1) {
/* In this case there are no loops. */
- MEdge &edge = result->medge[0];
+ MEdge &edge = edges[0];
edge.v1 = 0;
edge.v2 = 1;
edge.flag |= ME_EDGEDRAW | ME_EDGERENDER | ME_LOOSEEDGE;
@@ -108,7 +109,10 @@ static Mesh *hull_from_bullet(const Mesh *mesh, Span<float3> coords)
/* Copy faces. */
Array<int> loops;
int j = 0;
- MLoop *loop = result->mloop;
+ MutableSpan<MPoly> polys = result->polys_for_write();
+ MutableSpan<MLoop> mesh_loops = result->loops_for_write();
+ MLoop *loop = mesh_loops.data();
+
for (const int i : IndexRange(faces_num)) {
const int len = plConvexHullGetFaceSize(hull, i);
@@ -118,7 +122,7 @@ static Mesh *hull_from_bullet(const Mesh *mesh, Span<float3> coords)
loops.reinitialize(len);
plConvexHullGetFaceLoops(hull, i, loops.data());
- MPoly &face = result->mpoly[i];
+ MPoly &face = polys[i];
face.loopstart = j;
face.totloop = len;
for (const int k : IndexRange(len)) {
@@ -142,28 +146,35 @@ static Mesh *compute_hull(const GeometrySet &geometry_set)
Span<float3> positions_span;
- if (geometry_set.has_mesh()) {
+ if (const MeshComponent *component = geometry_set.get_component_for_read<MeshComponent>()) {
count++;
- const MeshComponent *component = geometry_set.get_component_for_read<MeshComponent>();
- total_num += component->attribute_domain_num(ATTR_DOMAIN_POINT);
+ if (const VArray<float3> positions = component->attributes()->lookup<float3>(
+ "position", ATTR_DOMAIN_POINT)) {
+ if (positions.is_span()) {
+ span_count++;
+ positions_span = positions.get_internal_span();
+ }
+ total_num += positions.size();
+ }
}
- if (geometry_set.has_pointcloud()) {
+ if (const PointCloudComponent *component =
+ geometry_set.get_component_for_read<PointCloudComponent>()) {
count++;
- span_count++;
- const PointCloudComponent *component =
- geometry_set.get_component_for_read<PointCloudComponent>();
- VArray<float3> varray = component->attribute_get_for_read<float3>(
- "position", ATTR_DOMAIN_POINT, {0, 0, 0});
- total_num += varray.size();
- positions_span = varray.get_internal_span();
+ if (const VArray<float3> positions = component->attributes()->lookup<float3>(
+ "position", ATTR_DOMAIN_POINT)) {
+ if (positions.is_span()) {
+ span_count++;
+ positions_span = positions.get_internal_span();
+ }
+ total_num += positions.size();
+ }
}
- if (geometry_set.has_curves()) {
+ if (const Curves *curves_id = geometry_set.get_curves_for_read()) {
count++;
span_count++;
- const Curves &curves_id = *geometry_set.get_curves_for_read();
- const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
+ const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry);
positions_span = curves.evaluated_positions();
total_num += positions_span.size();
}
@@ -181,26 +192,25 @@ static Mesh *compute_hull(const GeometrySet &geometry_set)
Array<float3> positions(total_num);
int offset = 0;
- if (geometry_set.has_mesh()) {
- const MeshComponent *component = geometry_set.get_component_for_read<MeshComponent>();
- 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()));
- offset += varray.size();
+ if (const MeshComponent *component = geometry_set.get_component_for_read<MeshComponent>()) {
+ if (const VArray<float3> varray = component->attributes()->lookup<float3>("position",
+ ATTR_DOMAIN_POINT)) {
+ 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>();
- 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()));
- offset += varray.size();
+ if (const PointCloudComponent *component =
+ geometry_set.get_component_for_read<PointCloudComponent>()) {
+ if (const VArray<float3> varray = component->attributes()->lookup<float3>("position",
+ ATTR_DOMAIN_POINT)) {
+ varray.materialize(positions.as_mutable_span().slice(offset, varray.size()));
+ offset += varray.size();
+ }
}
- if (geometry_set.has_curves()) {
- const Curves &curves_id = *geometry_set.get_curves_for_read();
- const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
+ if (const Curves *curves_id = geometry_set.get_curves_for_read()) {
+ const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry);
Span<float3> array = curves.evaluated_positions();
positions.as_mutable_span().slice(offset, array.size()).copy_from(array);
offset += array.size();
@@ -220,7 +230,7 @@ static void node_geo_exec(GeoNodeExecParams params)
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
Mesh *mesh = compute_hull(geometry_set);
geometry_set.replace_mesh(mesh);
- geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES});
+ geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_MESH});
});
params.set_output("Convex Hull", std::move(geometry_set));
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
index b52bf2571b5..28d979facac 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc
@@ -27,57 +27,49 @@ static void node_declare(NodeDeclarationBuilder &b)
N_("The selection from the start and end of the splines based on the input sizes"));
}
-class EndpointFieldInput final : public GeometryFieldInput {
+class EndpointFieldInput final : public bke::CurvesFieldInput {
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"),
+ : bke::CurvesFieldInput(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,
+ GVArray get_varray_for_context(const bke::CurvesGeometry &curves,
const eAttrDomain domain,
IndexMask UNUSED(mask)) const final
{
- if (component.type() != GEO_COMPONENT_TYPE_CURVE || domain != ATTR_DOMAIN_POINT) {
- return nullptr;
+ if (domain != ATTR_DOMAIN_POINT) {
+ return {};
}
-
- const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
- if (!curve_component.has_curves()) {
- return nullptr;
- }
-
- const Curves &curves_id = *curve_component.get_for_read();
- const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
if (curves.points_num() == 0) {
- return nullptr;
+ return {};
}
- GeometryComponentFieldContext size_context{curve_component, ATTR_DOMAIN_CURVE};
+ bke::CurvesFieldContext size_context{curves, ATTR_DOMAIN_CURVE};
fn::FieldEvaluator evaluator{size_context, curves.curves_num()};
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 VArray<int> start_size = evaluator.get_evaluated<int>(0);
+ const VArray<int> end_size = evaluator.get_evaluated<int>(1);
Array<bool> selection(curves.points_num(), false);
MutableSpan<bool> selection_span = selection.as_mutable_span();
devirtualize_varray2(start_size, end_size, [&](const auto &start_size, const auto &end_size) {
threading::parallel_for(curves.curves_range(), 1024, [&](IndexRange curves_range) {
for (const int i : curves_range) {
- const IndexRange range = curves.points_for_curve(i);
+ const IndexRange points = curves.points_for_curve(i);
const int start = std::max(start_size[i], 0);
const int end = std::max(end_size[i], 0);
- selection_span.slice(range.take_front(start)).fill(true);
- selection_span.slice(range.take_back(end)).fill(true);
+ selection_span.slice(points.take_front(start)).fill(true);
+ selection_span.slice(points.take_back(end)).fill(true);
}
});
});
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 c9a8dba55b2..c675ee472cd 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc
@@ -79,10 +79,10 @@ static Mesh *cdt_to_mesh(const meshintersect::CDT_result<double> &result)
}
Mesh *mesh = BKE_mesh_new_nomain(vert_len, edge_len, 0, loop_len, poly_len);
- MutableSpan<MVert> verts{mesh->mvert, mesh->totvert};
- MutableSpan<MEdge> edges{mesh->medge, mesh->totedge};
- MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop};
- MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly};
+ MutableSpan<MVert> verts = mesh->verts_for_write();
+ MutableSpan<MEdge> edges = mesh->edges_for_write();
+ MutableSpan<MPoly> polys = mesh->polys_for_write();
+ MutableSpan<MLoop> loops = mesh->loops_for_write();
for (const int i : IndexRange(result.vert.size())) {
copy_v3_v3(verts[i].co, float3((float)result.vert[i].x, (float)result.vert[i].y, 0.0f));
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 fb8a488ddae..4586bb24464 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc
@@ -1,16 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
-#include "BLI_task.hh"
-
#include "UI_interface.h"
#include "UI_resources.h"
-#include "DNA_node_types.h"
+#include "GEO_fillet_curves.hh"
#include "node_geometry_util.hh"
-#include "BKE_spline.hh"
-
namespace blender::nodes::node_geo_curve_fillet_cc {
NODE_STORAGE_FUNCS(NodeGeometryCurveFillet)
@@ -44,571 +40,18 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
static void node_init(bNodeTree *UNUSED(tree), bNode *node)
{
NodeGeometryCurveFillet *data = MEM_cnew<NodeGeometryCurveFillet>(__func__);
-
data->mode = GEO_NODE_CURVE_FILLET_BEZIER;
node->storage = data;
}
-struct FilletParam {
- GeometryNodeCurveFilletMode mode;
-
- /* Number of points to be added. */
- VArray<int> counts;
-
- /* Radii for fillet arc at all vertices. */
- VArray<float> radii;
-
- /* Whether or not fillets are allowed to overlap. */
- bool limit_radius;
-};
-
-/* A data structure used to store fillet data about all vertices to be filleted. */
-struct FilletData {
- Span<float3> positions;
- Array<float3> directions, axes;
- Array<float> radii, angles;
- Array<int> counts;
-};
-
static void node_update(bNodeTree *ntree, bNode *node)
{
const NodeGeometryCurveFillet &storage = node_storage(*node);
const GeometryNodeCurveFilletMode mode = (GeometryNodeCurveFilletMode)storage.mode;
-
bNodeSocket *poly_socket = ((bNodeSocket *)node->inputs.first)->next;
-
nodeSetSocketAvailability(ntree, poly_socket, mode == GEO_NODE_CURVE_FILLET_POLY);
}
-/* Function to get the center of a fillet. */
-static float3 get_center(const float3 vec_pos2prev,
- const float3 pos,
- const float3 axis,
- const float angle)
-{
- float3 vec_pos2center;
- rotate_normalized_v3_v3v3fl(vec_pos2center, vec_pos2prev, axis, M_PI_2 - angle / 2.0f);
- vec_pos2center *= 1.0f / sinf(angle / 2.0f);
-
- return vec_pos2center + pos;
-}
-
-/* Function to get the center of the fillet using fillet data */
-static float3 get_center(const float3 vec_pos2prev, const FilletData &fd, const int index)
-{
- const float angle = fd.angles[index];
- const float3 axis = fd.axes[index];
- const float3 pos = fd.positions[index];
-
- return get_center(vec_pos2prev, pos, axis, angle);
-}
-
-/* Calculate the direction vectors from each vertex to their previous vertex. */
-static Array<float3> calculate_directions(const Span<float3> positions)
-{
- const int num = positions.size();
- Array<float3> directions(num);
-
- for (const int i : IndexRange(num - 1)) {
- directions[i] = math::normalize(positions[i + 1] - positions[i]);
- }
- directions[num - 1] = math::normalize(positions[0] - positions[num - 1]);
-
- return directions;
-}
-
-/* Calculate the axes around which the fillet is built. */
-static Array<float3> calculate_axes(const Span<float3> directions)
-{
- const int num = directions.size();
- Array<float3> axes(num);
-
- axes[0] = math::normalize(math::cross(-directions[num - 1], directions[0]));
- for (const int i : IndexRange(1, num - 1)) {
- axes[i] = math::normalize(math::cross(-directions[i - 1], directions[i]));
- }
-
- return axes;
-}
-
-/* Calculate the angle of the arc formed by the fillet. */
-static Array<float> calculate_angles(const Span<float3> directions)
-{
- const int num = directions.size();
- Array<float> angles(num);
-
- angles[0] = M_PI - angle_v3v3(-directions[num - 1], directions[0]);
- for (const int i : IndexRange(1, num - 1)) {
- angles[i] = M_PI - angle_v3v3(-directions[i - 1], directions[i]);
- }
-
- return angles;
-}
-
-/* Calculate the segment count in each filleted arc. */
-static Array<int> calculate_counts(const FilletParam &fillet_param,
- const int num,
- const int spline_offset,
- const bool cyclic)
-{
- Array<int> counts(num, 1);
- if (fillet_param.mode == GEO_NODE_CURVE_FILLET_POLY) {
- for (const int i : IndexRange(num)) {
- counts[i] = fillet_param.counts[spline_offset + i];
- }
- }
- if (!cyclic) {
- counts[0] = counts[num - 1] = 0;
- }
-
- return counts;
-}
-
-/* Calculate the radii for the vertices to be filleted. */
-static Array<float> calculate_radii(const FilletParam &fillet_param,
- const int num,
- const int spline_offset)
-{
- Array<float> radii(num, 0.0f);
- if (fillet_param.limit_radius) {
- for (const int i : IndexRange(num)) {
- radii[i] = std::max(fillet_param.radii[spline_offset + i], 0.0f);
- }
- }
- else {
- for (const int i : IndexRange(num)) {
- radii[i] = fillet_param.radii[spline_offset + i];
- }
- }
-
- return radii;
-}
-
-/* Calculate the number of vertices added per vertex on the source spline. */
-static int calculate_point_counts(MutableSpan<int> point_counts,
- const Span<float> radii,
- const Span<int> counts)
-{
- int added_count = 0;
- for (const int i : IndexRange(point_counts.size())) {
- /* Calculate number of points to be added for the vertex. */
- if (radii[i] != 0.0f) {
- added_count += counts[i];
- point_counts[i] = counts[i] + 1;
- }
- }
-
- return added_count;
-}
-
-static FilletData calculate_fillet_data(const Spline &spline,
- const FilletParam &fillet_param,
- int &added_count,
- MutableSpan<int> point_counts,
- const int spline_offset)
-{
- const int num = spline.size();
-
- FilletData fd;
- fd.directions = calculate_directions(spline.positions());
- fd.positions = spline.positions();
- fd.axes = calculate_axes(fd.directions);
- fd.angles = calculate_angles(fd.directions);
- fd.counts = calculate_counts(fillet_param, num, spline_offset, spline.is_cyclic());
- fd.radii = calculate_radii(fillet_param, num, spline_offset);
-
- added_count = calculate_point_counts(point_counts, fd.radii, fd.counts);
-
- return fd;
-}
-
-/* Limit the radius based on angle and radii to prevent overlapping. */
-static void limit_radii(FilletData &fd, const bool cyclic)
-{
- MutableSpan<float> radii(fd.radii);
- Span<float> angles(fd.angles);
- Span<float3> positions(fd.positions);
-
- const int num = radii.size();
- const int fillet_count = cyclic ? num : num - 2;
- const int start = cyclic ? 0 : 1;
- Array<float> max_radii(num, FLT_MAX);
-
- if (cyclic) {
- /* Calculate lengths between adjacent control points. */
- const float len_prev = math::distance(positions[0], positions[num - 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);
- const float tan_len_prev = radii[num - 1] * tan(angles[num - 1] / 2.0f);
- const float tan_len_next = radii[1] * tan(angles[1] / 2.0f);
-
- float factor_prev = 1.0f, factor_next = 1.0f;
- if (tan_len + tan_len_prev > len_prev) {
- factor_prev = len_prev / (tan_len + tan_len_prev);
- }
- if (tan_len + tan_len_next > len_next) {
- factor_next = len_next / (tan_len + tan_len_next);
- }
-
- /* Scale max radii by calculated factors. */
- max_radii[0] = radii[0] * std::min(factor_next, factor_prev);
- max_radii[1] = radii[1] * factor_next;
- max_radii[num - 1] = radii[num - 1] * factor_prev;
- }
-
- /* Initialize max_radii to largest possible radii. */
- float prev_dist = math::distance(positions[1], positions[0]);
- for (const int i : IndexRange(1, num - 2)) {
- 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 = 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);
-
- /* Scale down radii if too large for segment. */
- float factor = 1.0f;
- if (tan_len + tan_len_next > len_next) {
- factor = len_next / (tan_len + tan_len_next);
- }
- max_radii[i] = std::min(max_radii[i], radii[i] * factor);
- max_radii[i + 1] = std::min(max_radii[i + 1], radii[i + 1] * factor);
- }
-
- /* Assign the max_radii to the fillet data's radii. */
- for (const int i : IndexRange(num)) {
- radii[i] = std::min(radii[i], max_radii[i]);
- }
-}
-
-/*
- * Create a mapping from each vertex in the destination spline to that of the source spline.
- * Used for copying the data from the source spline.
- */
-static Array<int> create_dst_to_src_map(const Span<int> point_counts, const int total_points)
-{
- Array<int> map(total_points);
- MutableSpan<int> map_span{map};
- int index = 0;
-
- for (const int i : point_counts.index_range()) {
- map_span.slice(index, point_counts[i]).fill(i);
- index += point_counts[i];
- }
-
- BLI_assert(index == total_points);
-
- return map;
-}
-
-template<typename T>
-static void copy_attribute_by_mapping(const Span<T> src,
- MutableSpan<T> dst,
- const Span<int> mapping)
-{
- for (const int i : dst.index_range()) {
- dst[i] = src[mapping[i]];
- }
-}
-
-/* Copy radii and tilts from source spline to destination. Positions are handled later in update
- * positions methods. */
-static void copy_common_attributes_by_mapping(const Spline &src,
- Spline &dst,
- const Span<int> mapping)
-{
- copy_attribute_by_mapping(src.radii(), dst.radii(), mapping);
- copy_attribute_by_mapping(src.tilts(), dst.tilts(), mapping);
-
- 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) {
- 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;
- }
- }
- BLI_assert_unreachable();
- return false;
- },
- ATTR_DOMAIN_POINT);
-}
-
-/* Update the vertex positions and handle positions of a Bezier spline based on fillet data. */
-static void update_bezier_positions(const FilletData &fd,
- BezierSpline &dst_spline,
- const BezierSpline &src_spline,
- const Span<int> point_counts)
-{
- Span<float> radii(fd.radii);
- Span<float> angles(fd.angles);
- Span<float3> axes(fd.axes);
- Span<float3> positions(fd.positions);
- Span<float3> directions(fd.directions);
-
- const int num = radii.size();
-
- int i_dst = 0;
- for (const int i_src : IndexRange(num)) {
- const int count = point_counts[i_src];
-
- /* Skip if the point count for the vertex is 1. */
- if (count == 1) {
- dst_spline.positions()[i_dst] = src_spline.positions()[i_src];
- dst_spline.handle_types_left()[i_dst] = src_spline.handle_types_left()[i_src];
- dst_spline.handle_types_right()[i_dst] = src_spline.handle_types_right()[i_src];
- dst_spline.handle_positions_left()[i_dst] = src_spline.handle_positions_left()[i_src];
- dst_spline.handle_positions_right()[i_dst] = src_spline.handle_positions_right()[i_src];
- i_dst++;
- continue;
- }
-
- /* Calculate the angle to be formed between any 2 adjacent vertices within the fillet. */
- const float segment_angle = angles[i_src] / (count - 1);
- /* Calculate the handle length for each added vertex. Equation: L = 4R/3 * tan(A/4) */
- const float handle_length = 4.0f * radii[i_src] / 3.0f * tan(segment_angle / 4.0f);
- /* Calculate the distance by which each vertex should be displaced from their initial position.
- */
- const float displacement = radii[i_src] * tan(angles[i_src] / 2.0f);
-
- /* Position the end points of the arc and their handles. */
- const int end_i = i_dst + count - 1;
- const float3 prev_dir = i_src == 0 ? -directions[num - 1] : -directions[i_src - 1];
- const float3 next_dir = directions[i_src];
- dst_spline.positions()[i_dst] = positions[i_src] + displacement * prev_dir;
- dst_spline.positions()[end_i] = positions[i_src] + displacement * next_dir;
- dst_spline.handle_positions_right()[i_dst] = dst_spline.positions()[i_dst] -
- handle_length * prev_dir;
- dst_spline.handle_positions_left()[end_i] = dst_spline.positions()[end_i] -
- handle_length * next_dir;
- dst_spline.handle_types_right()[i_dst] = dst_spline.handle_types_left()[end_i] =
- BEZIER_HANDLE_ALIGN;
- dst_spline.handle_types_left()[i_dst] = dst_spline.handle_types_right()[end_i] =
- BEZIER_HANDLE_VECTOR;
- dst_spline.mark_cache_invalid();
-
- /* Calculate the center of the radius to be formed. */
- 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;
- float radius;
- radius_vec = math::normalize_and_get_length(radius_vec, radius);
-
- dst_spline.handle_types_right().slice(1, count - 2).fill(BEZIER_HANDLE_ALIGN);
- dst_spline.handle_types_left().slice(1, count - 2).fill(BEZIER_HANDLE_ALIGN);
-
- /* For each of the vertices in between the end points. */
- for (const int j : IndexRange(1, count - 2)) {
- int index = i_dst + j;
- /* Rotate the radius by the segment angle and determine its tangent (used for getting handle
- * directions). */
- float3 new_radius_vec, tangent_vec;
- rotate_normalized_v3_v3v3fl(new_radius_vec, radius_vec, -axes[i_src], segment_angle);
- rotate_normalized_v3_v3v3fl(tangent_vec, new_radius_vec, axes[i_src], M_PI_2);
- radius_vec = new_radius_vec;
- tangent_vec *= handle_length;
-
- /* Adjust the positions of the respective vertex and its handles. */
- dst_spline.positions()[index] = center + new_radius_vec * radius;
- dst_spline.handle_positions_left()[index] = dst_spline.positions()[index] + tangent_vec;
- dst_spline.handle_positions_right()[index] = dst_spline.positions()[index] - tangent_vec;
- }
-
- i_dst += count;
- }
-}
-
-/* Update the vertex positions of a Poly spline based on fillet data. */
-static void update_poly_positions(const FilletData &fd,
- Spline &dst_spline,
- const Spline &src_spline,
- const Span<int> point_counts)
-{
- Span<float> radii(fd.radii);
- Span<float> angles(fd.angles);
- Span<float3> axes(fd.axes);
- Span<float3> positions(fd.positions);
- Span<float3> directions(fd.directions);
-
- const int num = radii.size();
-
- int i_dst = 0;
- for (const int i_src : IndexRange(num)) {
- const int count = point_counts[i_src];
-
- /* Skip if the point count for the vertex is 1. */
- if (count == 1) {
- dst_spline.positions()[i_dst] = src_spline.positions()[i_src];
- i_dst++;
- continue;
- }
-
- const float segment_angle = angles[i_src] / (count - 1);
- const float displacement = radii[i_src] * tan(angles[i_src] / 2.0f);
-
- /* Position the end points of the arc. */
- const int end_i = i_dst + count - 1;
- const float3 prev_dir = i_src == 0 ? -directions[num - 1] : -directions[i_src - 1];
- const float3 next_dir = directions[i_src];
- dst_spline.positions()[i_dst] = positions[i_src] + displacement * prev_dir;
- dst_spline.positions()[end_i] = positions[i_src] + displacement * next_dir;
-
- /* Calculate the center of the radius to be formed. */
- 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;
-
- for (const int j : IndexRange(1, count - 2)) {
- /* Rotate the radius by the segment angle */
- float3 new_radius_vec;
- rotate_normalized_v3_v3v3fl(new_radius_vec, radius_vec, -axes[i_src], segment_angle);
- radius_vec = new_radius_vec;
-
- dst_spline.positions()[i_dst + j] = center + new_radius_vec;
- }
-
- i_dst += count;
- }
-}
-
-static SplinePtr fillet_spline(const Spline &spline,
- const FilletParam &fillet_param,
- const int spline_offset)
-{
- const int num = spline.size();
- const bool cyclic = spline.is_cyclic();
-
- if (num < 3) {
- return spline.copy();
- }
-
- /* Initialize the point_counts with 1s (at least one vertex on dst for each vertex on src). */
- Array<int> point_counts(num, 1);
-
- int added_count = 0;
- /* Update point_counts array and added_count. */
- FilletData fd = calculate_fillet_data(
- spline, fillet_param, added_count, point_counts, spline_offset);
- if (fillet_param.limit_radius) {
- limit_radii(fd, cyclic);
- }
-
- const int total_points = added_count + num;
- const Array<int> dst_to_src = create_dst_to_src_map(point_counts, total_points);
- SplinePtr dst_spline_ptr = spline.copy_only_settings();
- (*dst_spline_ptr).resize(total_points);
- copy_common_attributes_by_mapping(spline, *dst_spline_ptr, dst_to_src);
-
- switch (spline.type()) {
- case CURVE_TYPE_BEZIER: {
- const BezierSpline &src_spline = static_cast<const BezierSpline &>(spline);
- BezierSpline &dst_spline = static_cast<BezierSpline &>(*dst_spline_ptr);
- if (fillet_param.mode == GEO_NODE_CURVE_FILLET_POLY) {
- dst_spline.handle_types_left().fill(BEZIER_HANDLE_VECTOR);
- dst_spline.handle_types_right().fill(BEZIER_HANDLE_VECTOR);
- update_poly_positions(fd, dst_spline, src_spline, point_counts);
- }
- else {
- update_bezier_positions(fd, dst_spline, src_spline, point_counts);
- }
- break;
- }
- case CURVE_TYPE_POLY: {
- update_poly_positions(fd, *dst_spline_ptr, spline, point_counts);
- break;
- }
- case CURVE_TYPE_NURBS: {
- const NURBSpline &src_spline = static_cast<const NURBSpline &>(spline);
- NURBSpline &dst_spline = static_cast<NURBSpline &>(*dst_spline_ptr);
- copy_attribute_by_mapping(src_spline.weights(), dst_spline.weights(), dst_to_src);
- update_poly_positions(fd, dst_spline, src_spline, point_counts);
- break;
- }
- case CURVE_TYPE_CATMULL_ROM: {
- BLI_assert_unreachable();
- break;
- }
- }
-
- return dst_spline_ptr;
-}
-
-static std::unique_ptr<CurveEval> fillet_curve(const CurveEval &input_curve,
- const FilletParam &fillet_param)
-{
- Span<SplinePtr> input_splines = input_curve.splines();
-
- std::unique_ptr<CurveEval> output_curve = std::make_unique<CurveEval>();
- const int splines_num = input_splines.size();
- output_curve->resize(splines_num);
- MutableSpan<SplinePtr> output_splines = output_curve->splines();
- Array<int> spline_offsets = input_curve.control_point_offsets();
-
- threading::parallel_for(input_splines.index_range(), 128, [&](IndexRange range) {
- for (const int i : range) {
- output_splines[i] = fillet_spline(*input_splines[i], fillet_param, spline_offsets[i]);
- }
- });
- output_curve->attributes = input_curve.attributes;
-
- return output_curve;
-}
-
-static void calculate_curve_fillet(GeometrySet &geometry_set,
- const GeometryNodeCurveFilletMode mode,
- const Field<float> &radius_field,
- const std::optional<Field<int>> &count_field,
- const bool limit_radius)
-{
- if (!geometry_set.has_curves()) {
- return;
- }
-
- FilletParam fillet_param;
- fillet_param.mode = mode;
-
- CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>();
- GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
- const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_POINT);
- fn::FieldEvaluator field_evaluator{field_context, domain_num};
-
- field_evaluator.add(radius_field);
-
- if (mode == GEO_NODE_CURVE_FILLET_POLY) {
- field_evaluator.add(*count_field);
- }
-
- 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) {
- return;
- }
-
- if (mode == GEO_NODE_CURVE_FILLET_POLY) {
- fillet_param.counts = field_evaluator.get_evaluated<int>(1);
- }
-
- fillet_param.limit_radius = limit_radius;
-
- const std::unique_ptr<CurveEval> input_curve = curves_to_curve_eval(*component.get_for_read());
- std::unique_ptr<CurveEval> output_curve = fillet_curve(*input_curve, fillet_param);
-
- geometry_set.replace_curves(curve_eval_to_curves(*output_curve));
-}
-
static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
@@ -625,7 +68,41 @@ static void node_geo_exec(GeoNodeExecParams params)
}
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
- calculate_curve_fillet(geometry_set, mode, radius_field, count_field, limit_radius);
+ if (!geometry_set.has_curves()) {
+ return;
+ }
+
+ const Curves &curves_id = *geometry_set.get_curves_for_read();
+ const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
+ bke::CurvesFieldContext context{curves, ATTR_DOMAIN_POINT};
+ fn::FieldEvaluator evaluator{context, curves.points_num()};
+ evaluator.add(radius_field);
+
+ switch (mode) {
+ case GEO_NODE_CURVE_FILLET_BEZIER: {
+ evaluator.evaluate();
+ bke::CurvesGeometry dst_curves = geometry::fillet_curves_bezier(
+ curves, curves.curves_range(), evaluator.get_evaluated<float>(0), limit_radius);
+ Curves *dst_curves_id = bke::curves_new_nomain(std::move(dst_curves));
+ bke::curves_copy_parameters(curves_id, *dst_curves_id);
+ geometry_set.replace_curves(dst_curves_id);
+ break;
+ }
+ case GEO_NODE_CURVE_FILLET_POLY: {
+ evaluator.add(*count_field);
+ evaluator.evaluate();
+ bke::CurvesGeometry dst_curves = geometry::fillet_curves_poly(
+ curves,
+ curves.curves_range(),
+ evaluator.get_evaluated<float>(0),
+ evaluator.get_evaluated<int>(1),
+ limit_radius);
+ Curves *dst_curves_id = bke::curves_new_nomain(std::move(dst_curves));
+ bke::curves_copy_parameters(curves_id, *dst_curves_id);
+ geometry_set.replace_curves(dst_curves_id);
+ break;
+ }
+ }
});
params.set_output("Curve", std::move(geometry_set));
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
index 5ef20f03f28..b34b22e995d 100644
--- 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
@@ -70,35 +70,28 @@ static void select_by_handle_type(const bke::CurvesGeometry &curves,
}
}
-class HandleTypeFieldInput final : public GeometryFieldInput {
+class HandleTypeFieldInput final : public bke::CurvesFieldInput {
HandleType type_;
GeometryNodeCurveHandleMode mode_;
public:
HandleTypeFieldInput(HandleType type, GeometryNodeCurveHandleMode mode)
- : GeometryFieldInput(CPPType::get<bool>(), "Handle Type Selection node"),
+ : bke::CurvesFieldInput(CPPType::get<bool>(), "Handle Type Selection node"),
type_(type),
mode_(mode)
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const bke::CurvesGeometry &curves,
const eAttrDomain domain,
IndexMask mask) const final
{
- if (component.type() != GEO_COMPONENT_TYPE_CURVE || domain != ATTR_DOMAIN_POINT) {
+ if (domain != ATTR_DOMAIN_POINT) {
return {};
}
-
- const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
- const Curves *curves_id = curve_component.get_for_read();
- if (curves_id == nullptr) {
- return {};
- }
-
Array<bool> selection(mask.min_array_size());
- select_by_handle_type(bke::CurvesGeometry::wrap(curves_id->geometry), type_, mode_, selection);
+ select_by_handle_type(curves, type_, mode_, selection);
return VArray<bool>::ForContainer(std::move(selection));
}
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 91ba5f2845f..286d9993d0e 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
@@ -61,13 +61,13 @@ static Curves *create_star_curve(const float inner_radius,
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;
+ SpanAttributeWriter<bool> selection =
+ component.attributes_for_write()->lookup_or_add_for_write_only_span<bool>(r_attribute.get(),
+ ATTR_DOMAIN_POINT);
+ for (int i : selection.span.index_range()) {
+ selection.span[i] = i % 2 == 0;
}
- attribute.save();
+ selection.finish();
}
static void node_geo_exec(GeoNodeExecParams params)
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 78a132064ed..41eafe2a741 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc
@@ -60,14 +60,20 @@ static void node_geo_exec(GeoNodeExecParams params)
const Field<bool> selection = params.extract_input<Field<bool>>("Selection");
+ GeometryComponentEditData::remember_deformed_curve_positions_if_necessary(geometry_set);
+
switch (mode) {
case GEO_NODE_CURVE_RESAMPLE_COUNT: {
Field<int> count = params.extract_input<Field<int>>("Count");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry) {
- if (const CurveComponent *component = geometry.get_component_for_read<CurveComponent>()) {
- if (!component->is_empty()) {
- geometry.replace_curves(geometry::resample_to_count(*component, selection, count));
- }
+ if (const Curves *src_curves_id = geometry.get_curves_for_read()) {
+ const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(
+ src_curves_id->geometry);
+ bke::CurvesGeometry dst_curves = geometry::resample_to_count(
+ src_curves, selection, count);
+ Curves *dst_curves_id = bke::curves_new_nomain(std::move(dst_curves));
+ bke::curves_copy_parameters(*src_curves_id, *dst_curves_id);
+ geometry.replace_curves(dst_curves_id);
}
});
break;
@@ -75,20 +81,27 @@ static void node_geo_exec(GeoNodeExecParams params)
case GEO_NODE_CURVE_RESAMPLE_LENGTH: {
Field<float> length = params.extract_input<Field<float>>("Length");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry) {
- if (const CurveComponent *component = geometry.get_component_for_read<CurveComponent>()) {
- if (!component->is_empty()) {
- geometry.replace_curves(geometry::resample_to_length(*component, selection, length));
- }
+ if (const Curves *src_curves_id = geometry.get_curves_for_read()) {
+ const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(
+ src_curves_id->geometry);
+ bke::CurvesGeometry dst_curves = geometry::resample_to_length(
+ src_curves, selection, length);
+ Curves *dst_curves_id = bke::curves_new_nomain(std::move(dst_curves));
+ bke::curves_copy_parameters(*src_curves_id, *dst_curves_id);
+ geometry.replace_curves(dst_curves_id);
}
});
break;
}
case GEO_NODE_CURVE_RESAMPLE_EVALUATED:
geometry_set.modify_geometry_sets([&](GeometrySet &geometry) {
- if (const CurveComponent *component = geometry.get_component_for_read<CurveComponent>()) {
- if (!component->is_empty()) {
- geometry.replace_curves(geometry::resample_to_evaluated(*component, selection));
- }
+ if (const Curves *src_curves_id = geometry.get_curves_for_read()) {
+ const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(
+ src_curves_id->geometry);
+ bke::CurvesGeometry dst_curves = geometry::resample_to_evaluated(src_curves, selection);
+ Curves *dst_curves_id = bke::curves_new_nomain(std::move(dst_curves));
+ bke::curves_copy_parameters(*src_curves_id, *dst_curves_id);
+ geometry.replace_curves(dst_curves_id);
}
});
break;
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 64a174df599..0169ead5bd2 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc
@@ -23,14 +23,12 @@ static void node_geo_exec(GeoNodeExecParams params)
if (!geometry_set.has_curves()) {
return;
}
+ const Curves &src_curves_id = *geometry_set.get_curves_for_read();
+ const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(src_curves_id.geometry);
- Field<bool> selection_field = params.get_input<Field<bool>>("Selection");
- const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>();
- GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE};
- const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_CURVE);
-
- fn::FieldEvaluator selection_evaluator{field_context, domain_num};
- selection_evaluator.add(selection_field);
+ bke::CurvesFieldContext field_context{src_curves, ATTR_DOMAIN_CURVE};
+ fn::FieldEvaluator selection_evaluator{field_context, src_curves.curves_num()};
+ selection_evaluator.add(params.get_input<Field<bool>>("Selection"));
selection_evaluator.evaluate();
const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0);
if (selection.is_empty()) {
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 9077b59254c..e80b600a740 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc
@@ -1,8 +1,9 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
-#include "BLI_task.hh"
+#include "BLI_devirtualize_parameters.hh"
+#include "BLI_length_parameterize.hh"
-#include "BKE_spline.hh"
+#include "BKE_curves.hh"
#include "UI_interface.h"
#include "UI_resources.h"
@@ -58,34 +59,103 @@ static void node_update(bNodeTree *ntree, bNode *node)
nodeSetSocketAvailability(ntree, length, mode == GEO_NODE_CURVE_SAMPLE_LENGTH);
}
-template<typename T> static T sample_with_lookup(const Spline::LookupResult lookup, Span<T> data)
+static void sample_indices_and_lengths(const Span<float> accumulated_lengths,
+ const Span<float> sample_lengths,
+ const IndexMask mask,
+ MutableSpan<int> r_segment_indices,
+ MutableSpan<float> r_length_in_segment)
{
- return attribute_math::mix2(
- lookup.factor, data[lookup.evaluated_index], data[lookup.next_evaluated_index]);
+ const float total_length = accumulated_lengths.last();
+ length_parameterize::SampleSegmentHint hint;
+
+ mask.to_best_mask_type([&](const auto mask) {
+ for (const int64_t i : mask) {
+ int segment_i;
+ float factor_in_segment;
+ length_parameterize::sample_at_length(accumulated_lengths,
+ std::clamp(sample_lengths[i], 0.0f, total_length),
+ segment_i,
+ factor_in_segment,
+ &hint);
+ const float segment_start = segment_i == 0 ? 0.0f : accumulated_lengths[segment_i - 1];
+ const float segment_end = accumulated_lengths[segment_i];
+ const float segment_length = segment_end - segment_start;
+
+ r_segment_indices[i] = segment_i;
+ r_length_in_segment[i] = factor_in_segment * segment_length;
+ }
+ });
+}
+
+static void sample_indices_and_factors_to_compressed(const Span<float> accumulated_lengths,
+ const Span<float> sample_lengths,
+ const IndexMask mask,
+ MutableSpan<int> r_segment_indices,
+ MutableSpan<float> r_factor_in_segment)
+{
+ const float total_length = accumulated_lengths.last();
+ length_parameterize::SampleSegmentHint hint;
+
+ mask.to_best_mask_type([&](const auto mask) {
+ for (const int64_t i : IndexRange(mask.size())) {
+ const float length = sample_lengths[mask[i]];
+ length_parameterize::sample_at_length(accumulated_lengths,
+ std::clamp(length, 0.0f, total_length),
+ r_segment_indices[i],
+ r_factor_in_segment[i],
+ &hint);
+ }
+ });
}
+/**
+ * Given an array of accumulated lengths, find the segment indices that
+ * sample lengths lie on, and how far along the segment they are.
+ */
+class SampleFloatSegmentsFunction : public fn::MultiFunction {
+ private:
+ Array<float> accumulated_lengths_;
+
+ public:
+ SampleFloatSegmentsFunction(Array<float> accumulated_lengths)
+ : accumulated_lengths_(std::move(accumulated_lengths))
+ {
+ static fn::MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ }
+
+ static fn::MFSignature create_signature()
+ {
+ fn::MFSignatureBuilder signature{"Sample Curve Index"};
+ signature.single_input<float>("Length");
+
+ signature.single_output<int>("Curve Index");
+ signature.single_output<float>("Length in Curve");
+ return signature.build();
+ }
+
+ void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override
+ {
+ const VArraySpan<float> lengths = params.readonly_single_input<float>(0, "Length");
+ MutableSpan<int> indices = params.uninitialized_single_output<int>(1, "Curve Index");
+ MutableSpan<float> lengths_in_segments = params.uninitialized_single_output<float>(
+ 2, "Length in Curve");
+
+ sample_indices_and_lengths(accumulated_lengths_, lengths, mask, indices, lengths_in_segments);
+ }
+};
+
class SampleCurveFunction : public fn::MultiFunction {
private:
/**
- * The function holds a geometry set instead of a curve or a curve component in order to
- * maintain a reference to the geometry while the field tree is being built, so that the
- * curve is not freed before the function can execute.
+ * The function holds a geometry set instead of curves or a curve component reference in order
+ * to maintain a ownership of the geometry while the field tree is being built and used, so
+ * that the curve is not freed before the function can execute.
*/
GeometrySet geometry_set_;
- /**
- * To support factor inputs, the node adds another field operation before this one to multiply by
- * the curve's total length. Since that must calculate the spline lengths anyway, store them to
- * reuse the calculation.
- */
- Array<float> spline_lengths_;
- /** The last member of #spline_lengths_, extracted for convenience. */
- const float total_length_;
public:
- SampleCurveFunction(GeometrySet geometry_set, Array<float> spline_lengths)
- : geometry_set_(std::move(geometry_set)),
- spline_lengths_(std::move(spline_lengths)),
- total_length_(spline_lengths_.last())
+ SampleCurveFunction(GeometrySet geometry_set) : geometry_set_(std::move(geometry_set))
{
static fn::MFSignature signature = create_signature();
this->set_signature(&signature);
@@ -93,7 +163,8 @@ class SampleCurveFunction : public fn::MultiFunction {
static fn::MFSignature create_signature()
{
- blender::fn::MFSignatureBuilder signature{"Curve Sample"};
+ blender::fn::MFSignatureBuilder signature{"Sample Curve"};
+ signature.single_input<int>("Curve Index");
signature.single_input<float>("Length");
signature.single_output<float3>("Position");
signature.single_output<float3>("Tangent");
@@ -104,11 +175,11 @@ class SampleCurveFunction : public fn::MultiFunction {
void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override
{
MutableSpan<float3> sampled_positions = params.uninitialized_single_output_if_required<float3>(
- 1, "Position");
+ 2, "Position");
MutableSpan<float3> sampled_tangents = params.uninitialized_single_output_if_required<float3>(
- 2, "Tangent");
+ 3, "Tangent");
MutableSpan<float3> sampled_normals = params.uninitialized_single_output_if_required<float3>(
- 3, "Normal");
+ 4, "Normal");
auto return_default = [&]() {
if (!sampled_positions.is_empty()) {
@@ -126,61 +197,78 @@ class SampleCurveFunction : public fn::MultiFunction {
return return_default();
}
- const CurveComponent *curve_component = geometry_set_.get_component_for_read<CurveComponent>();
- const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(
- *curve_component->get_for_read());
- Span<SplinePtr> splines = curve->splines();
- if (splines.is_empty()) {
+ const Curves &curves_id = *geometry_set_.get_curves_for_read();
+ const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
+ if (curves.points_num() == 0) {
return return_default();
}
-
- const VArray<float> &lengths_varray = params.readonly_single_input<float>(0, "Length");
- const VArray_Span lengths{lengths_varray};
-#ifdef DEBUG
- for (const float length : lengths) {
- /* Lengths must be in range of the curve's total length. This is ensured in
- * #get_length_input_field by adding another multi-function before this one
- * to clamp the lengths. */
- BLI_assert(length >= 0.0f && length <= total_length_);
- }
-#endif
-
- Array<int> spline_indices(mask.min_array_size());
- for (const int i : mask) {
- const float *offset = std::lower_bound(
- spline_lengths_.begin(), spline_lengths_.end(), lengths[i]);
- const int index = offset - spline_lengths_.data() - 1;
- spline_indices[i] = std::max(index, 0);
+ Span<float3> evaluated_positions = curves.evaluated_positions();
+ Span<float3> evaluated_tangents;
+ Span<float3> evaluated_normals;
+ if (!sampled_tangents.is_empty()) {
+ evaluated_tangents = curves.evaluated_tangents();
}
-
- /* Storing lookups in an array is unnecessary but will simplify custom attribute transfer. */
- Array<Spline::LookupResult> lookups(mask.min_array_size());
- for (const int i : mask) {
- const float length_in_spline = lengths[i] - spline_lengths_[spline_indices[i]];
- lookups[i] = splines[spline_indices[i]]->lookup_evaluated_length(length_in_spline);
+ if (!sampled_normals.is_empty()) {
+ evaluated_normals = curves.evaluated_normals();
}
- if (!sampled_positions.is_empty()) {
- for (const int i : mask) {
- const Spline::LookupResult &lookup = lookups[i];
- const Span<float3> evaluated_positions = splines[spline_indices[i]]->evaluated_positions();
- sampled_positions[i] = sample_with_lookup(lookup, evaluated_positions);
+ const VArray<int> curve_indices = params.readonly_single_input<int>(0, "Curve Index");
+ const VArraySpan<float> lengths = params.readonly_single_input<float>(1, "Length");
+ const VArray<bool> cyclic = curves.cyclic();
+
+ Array<int> indices;
+ Array<float> factors;
+
+ auto sample_curve = [&](const int curve_i, const IndexMask mask) {
+ /* Store the sampled indices and factors in arrays the size of the mask.
+ * Then, during interpolation, move the results back to the masked indices. */
+ indices.reinitialize(mask.size());
+ factors.reinitialize(mask.size());
+ sample_indices_and_factors_to_compressed(
+ curves.evaluated_lengths_for_curve(curve_i, cyclic[curve_i]),
+ lengths,
+ mask,
+ indices,
+ factors);
+
+ const IndexRange evaluated_points = curves.evaluated_points_for_curve(curve_i);
+ if (!sampled_positions.is_empty()) {
+ length_parameterize::interpolate_to_masked<float3>(
+ evaluated_positions.slice(evaluated_points),
+ indices,
+ factors,
+ mask,
+ sampled_positions);
}
- }
-
- if (!sampled_tangents.is_empty()) {
- 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] = math::normalize(sample_with_lookup(lookup, evaluated_tangents));
+ if (!sampled_tangents.is_empty()) {
+ length_parameterize::interpolate_to_masked<float3>(
+ evaluated_tangents.slice(evaluated_points), indices, factors, mask, sampled_tangents);
+ for (const int64_t i : mask) {
+ sampled_tangents[i] = math::normalize(sampled_tangents[i]);
+ }
}
- }
+ if (!sampled_normals.is_empty()) {
+ length_parameterize::interpolate_to_masked<float3>(
+ evaluated_normals.slice(evaluated_points), indices, factors, mask, sampled_normals);
+ for (const int64_t i : mask) {
+ sampled_normals[i] = math::normalize(sampled_normals[i]);
+ }
+ }
+ };
- if (!sampled_normals.is_empty()) {
- 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] = math::normalize(sample_with_lookup(lookup, evaluated_normals));
+ if (curve_indices.is_single()) {
+ sample_curve(curve_indices.get_internal_single(), mask);
+ }
+ else {
+ MultiValueMap<int, int64_t> indices_per_curve;
+ devirtualize_varray(curve_indices, [&](const auto curve_indices) {
+ for (const int64_t i : mask) {
+ indices_per_curve.add(curve_indices[i], i);
+ }
+ });
+
+ for (const int curve_i : indices_per_curve.keys()) {
+ sample_curve(curve_i, IndexMask(indices_per_curve.lookup(curve_i)));
}
}
}
@@ -188,82 +276,82 @@ class SampleCurveFunction : public fn::MultiFunction {
/**
* Pre-process the lengths or factors used for the sampling, turning factors into lengths, and
- * clamping between zero and the total length of the curve. Do this as a separate operation in the
+ * clamping between zero and the total length of the curves. Do this as a separate operation in the
* field tree to make the sampling simpler, and to let the evaluator optimize better.
*
* \todo Use a mutable single input instead when they are supported.
*/
-static Field<float> get_length_input_field(const GeoNodeExecParams &params,
- const float curve_total_length)
+static Field<float> get_length_input_field(GeoNodeExecParams params,
+ const GeometryNodeCurveSampleMode mode,
+ const float curves_total_length)
{
- 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. */
- Field<float> length_field = params.get_input<Field<float>>("Length");
- auto clamp_fn = std::make_unique<fn::CustomMF_SI_SO<float, float>>(
- __func__,
- [curve_total_length](float length) {
- return std::clamp(length, 0.0f, curve_total_length);
- },
- fn::CustomMF_presets::AllSpanOrSingle());
- auto clamp_op = std::make_shared<FieldOperation>(
- FieldOperation(std::move(clamp_fn), {std::move(length_field)}));
-
- return Field<float>(std::move(clamp_op), 0);
+ return params.extract_input<Field<float>>("Length");
}
- /* Convert the factor to a length and clamp it to the bounds of the curve. */
+ /* Convert the factor to a length. */
Field<float> factor_field = params.get_input<Field<float>>("Factor");
auto clamp_fn = std::make_unique<fn::CustomMF_SI_SO<float, float>>(
__func__,
- [curve_total_length](float factor) {
- const float length = factor * curve_total_length;
- return std::clamp(length, 0.0f, curve_total_length);
- },
+ [curves_total_length](float factor) { return factor * curves_total_length; },
fn::CustomMF_presets::AllSpanOrSingle());
- auto process_op = std::make_shared<FieldOperation>(
- FieldOperation(std::move(clamp_fn), {std::move(factor_field)}));
- return Field<float>(std::move(process_op), 0);
+ return Field<float>(FieldOperation::Create(std::move(clamp_fn), {std::move(factor_field)}), 0);
}
-static void node_geo_exec(GeoNodeExecParams params)
+static Array<float> curve_accumulated_lengths(const bke::CurvesGeometry &curves)
{
- GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
-
- const CurveComponent *component = geometry_set.get_component_for_read<CurveComponent>();
- if (component == nullptr) {
- params.set_default_remaining_outputs();
- return;
+ curves.ensure_evaluated_lengths();
+
+ Array<float> curve_lengths(curves.curves_num());
+ const VArray<bool> cyclic = curves.cyclic();
+ float length = 0.0f;
+ for (const int i : curves.curves_range()) {
+ length += curves.evaluated_length_total_for_curve(i, cyclic[i]);
+ curve_lengths[i] = length;
}
+ return curve_lengths;
+}
- if (!component->has_curves()) {
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
+ if (!geometry_set.has_curves()) {
params.set_default_remaining_outputs();
return;
}
- const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*component->get_for_read());
-
- if (curve->splines().is_empty()) {
+ const Curves &curves_id = *geometry_set.get_curves_for_read();
+ const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
+ if (curves.points_num() == 0) {
params.set_default_remaining_outputs();
return;
}
- Array<float> spline_lengths = curve->accumulated_spline_lengths();
- const float total_length = spline_lengths.last();
+ Array<float> curve_lengths = curve_accumulated_lengths(curves);
+ const float total_length = curve_lengths.last();
if (total_length == 0.0f) {
params.set_default_remaining_outputs();
return;
}
- Field<float> length_field = get_length_input_field(params, total_length);
+ const NodeGeometryCurveSample &storage = node_storage(params.node());
+ const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)storage.mode;
+ Field<float> length_field = get_length_input_field(params, mode, total_length);
+
+ auto sample_fn = std::make_unique<SampleCurveFunction>(std::move(geometry_set));
- auto sample_fn = std::make_unique<SampleCurveFunction>(std::move(geometry_set),
- std::move(spline_lengths));
- auto sample_op = std::make_shared<FieldOperation>(
- FieldOperation(std::move(sample_fn), {length_field}));
+ std::shared_ptr<FieldOperation> sample_op;
+ if (curves.curves_num() == 1) {
+ sample_op = FieldOperation::Create(std::move(sample_fn),
+ {fn::make_constant_field<int>(0), std::move(length_field)});
+ }
+ else {
+ auto index_fn = std::make_unique<SampleFloatSegmentsFunction>(std::move(curve_lengths));
+ auto index_op = FieldOperation::Create(std::move(index_fn), {std::move(length_field)});
+ sample_op = FieldOperation::Create(std::move(sample_fn),
+ {Field<int>(index_op, 0), Field<float>(index_op, 1)});
+ }
params.set_output("Position", Field<float3>(sample_op, 0));
params.set_output("Tangent", Field<float3>(sample_op, 1));
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_set_handle_type.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_set_handle_type.cc
index 469d8d8d13b..8bb24821064 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_set_handle_type.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_set_handle_type.cc
@@ -51,15 +51,12 @@ static HandleType handle_type_from_input_type(GeometryNodeCurveHandleType type)
return BEZIER_HANDLE_AUTO;
}
-static void set_type_in_component(CurveComponent &component,
- const GeometryNodeCurveHandleMode mode,
- const HandleType new_handle_type,
- const Field<bool> &selection_field)
+static void set_handle_type(bke::CurvesGeometry &curves,
+ const GeometryNodeCurveHandleMode mode,
+ const HandleType new_handle_type,
+ const Field<bool> &selection_field)
{
- Curves &curves_id = *component.get_for_write();
- bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
-
- GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
+ bke::CurvesFieldContext field_context{curves, ATTR_DOMAIN_POINT};
fn::FieldEvaluator evaluator{field_context, curves.points_num()};
evaluator.set_selection(selection_field);
evaluator.evaluate();
@@ -93,21 +90,17 @@ static void node_geo_exec(GeoNodeExecParams params)
std::atomic<bool> has_bezier = false;
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
- if (!geometry_set.has_curves()) {
- return;
- }
- has_curves = true;
- const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>();
- if (!component.attribute_exists("handle_type_left") ||
- !component.attribute_exists("handle_type_right")) {
- return;
+ if (Curves *curves_id = geometry_set.get_curves_for_write()) {
+ bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry);
+ has_curves = true;
+ const AttributeAccessor attributes = curves.attributes();
+ if (!attributes.contains("handle_type_left") || !attributes.contains("handle_type_right")) {
+ return;
+ }
+ has_bezier = true;
+
+ set_handle_type(curves, mode, new_handle_type, selection_field);
}
- has_bezier = true;
-
- set_type_in_component(geometry_set.get_component_for_write<CurveComponent>(),
- mode,
- new_handle_type,
- selection_field);
});
if (has_curves && !has_bezier) {
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
index ae2b4fd779d..b5d8d1f020a 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc
@@ -119,10 +119,20 @@ static VArray<float> construct_curve_parameter_varray(const bke::CurvesGeometry
for (const int i_curve : range) {
const float total_length = curves.evaluated_length_total_for_curve(i_curve,
cyclic[i_curve]);
- const float factor = total_length == 0.0f ? 0.0f : 1.0f / total_length;
MutableSpan<float> curve_lengths = lengths.slice(curves.points_for_curve(i_curve));
- for (float &value : curve_lengths) {
- value *= factor;
+ if (total_length > 0.0f) {
+ const float factor = 1.0f / total_length;
+ for (float &value : curve_lengths) {
+ value *= factor;
+ }
+ }
+ else {
+ /* It is arbitrary what to do in those rare cases when all the points are
+ * in the same position. In this case we are just arbitrarily giving a valid
+ * value in the range based on the point index. */
+ for (const int i : curve_lengths.index_range()) {
+ curve_lengths[i] = i / (curve_lengths.size() - 1.0f);
+ }
}
}
});
@@ -133,11 +143,21 @@ static VArray<float> construct_curve_parameter_varray(const bke::CurvesGeometry
Array<float> lengths = accumulated_lengths_curve_domain(curves);
const int last_index = curves.curves_num() - 1;
- const int total_length = lengths.last() + curves.evaluated_length_total_for_curve(
- last_index, cyclic[last_index]);
- const float factor = total_length == 0.0f ? 0.0f : 1.0f / total_length;
- for (float &value : lengths) {
- value *= factor;
+ const float total_length = lengths.last() + curves.evaluated_length_total_for_curve(
+ last_index, cyclic[last_index]);
+ if (total_length > 0.0f) {
+ const float factor = 1.0f / total_length;
+ for (float &value : lengths) {
+ value *= factor;
+ }
+ }
+ else {
+ /* It is arbitrary what to do in those rare cases when all the points are
+ * in the same position. In this case we are just arbitrarily giving a valid
+ * value in the range based on the curve index. */
+ for (const int i : lengths.index_range()) {
+ lengths[i] = i / (lengths.size() - 1.0f);
+ }
}
return VArray<float>::ForContainer(std::move(lengths));
}
@@ -183,26 +203,18 @@ static VArray<int> construct_index_on_spline_varray(const bke::CurvesGeometry &c
return {};
}
-class CurveParameterFieldInput final : public GeometryFieldInput {
+class CurveParameterFieldInput final : public bke::CurvesFieldInput {
public:
- CurveParameterFieldInput() : GeometryFieldInput(CPPType::get<float>(), "Curve Parameter node")
+ CurveParameterFieldInput() : bke::CurvesFieldInput(CPPType::get<float>(), "Curve Parameter node")
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const bke::CurvesGeometry &curves,
const eAttrDomain domain,
IndexMask mask) const final
{
- if (component.type() == GEO_COMPONENT_TYPE_CURVE) {
- const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
- if (curve_component.has_curves()) {
- const Curves &curves_id = *curve_component.get_for_read();
- const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
- return construct_curve_parameter_varray(curves, mask, domain);
- }
- }
- return {};
+ return construct_curve_parameter_varray(curves, mask, domain);
}
uint64_t hash() const override
@@ -217,26 +229,19 @@ class CurveParameterFieldInput final : public GeometryFieldInput {
}
};
-class CurveLengthParameterFieldInput final : public GeometryFieldInput {
+class CurveLengthParameterFieldInput final : public bke::CurvesFieldInput {
public:
- CurveLengthParameterFieldInput() : GeometryFieldInput(CPPType::get<float>(), "Curve Length node")
+ CurveLengthParameterFieldInput()
+ : bke::CurvesFieldInput(CPPType::get<float>(), "Curve Length node")
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const bke::CurvesGeometry &curves,
const eAttrDomain domain,
IndexMask mask) const final
{
- if (component.type() == GEO_COMPONENT_TYPE_CURVE) {
- const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
- if (curve_component.has_curves()) {
- const Curves &curves_id = *curve_component.get_for_read();
- const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
- return construct_curve_length_parameter_varray(curves, mask, domain);
- }
- }
- return {};
+ return construct_curve_length_parameter_varray(curves, mask, domain);
}
uint64_t hash() const override
@@ -251,26 +256,18 @@ class CurveLengthParameterFieldInput final : public GeometryFieldInput {
}
};
-class IndexOnSplineFieldInput final : public GeometryFieldInput {
+class IndexOnSplineFieldInput final : public bke::CurvesFieldInput {
public:
- IndexOnSplineFieldInput() : GeometryFieldInput(CPPType::get<int>(), "Spline Index")
+ IndexOnSplineFieldInput() : bke::CurvesFieldInput(CPPType::get<int>(), "Spline Index")
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const bke::CurvesGeometry &curves,
const eAttrDomain domain,
IndexMask mask) const final
{
- if (component.type() == GEO_COMPONENT_TYPE_CURVE) {
- const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
- if (curve_component.has_curves()) {
- const Curves &curves_id = *curve_component.get_for_read();
- const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
- return construct_index_on_spline_varray(curves, mask, domain);
- }
- }
- return {};
+ return construct_index_on_spline_varray(curves, mask, domain);
}
uint64_t hash() const override
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
index 8479d61a890..4d7e5f13969 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc
@@ -1,16 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
-#include <numeric>
-
-#include "BKE_attribute_math.hh"
#include "BKE_curves.hh"
-#include "BKE_curves_utils.hh"
-
-#include "BLI_task.hh"
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GEO_set_curve_type.hh"
+
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_curve_spline_type_cc {
@@ -37,631 +33,6 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node)
node->storage = data;
}
-/**
- * This function answers the question about possible conversion method for NURBS-to-Bezier. In
- * general for 3rd degree NURBS curves there is one-to-one relation with 3rd degree Bezier curves
- * that can be exploit for conversion - Bezier handles sit on NURBS hull segments and in the middle
- * between those handles are Bezier anchor points.
- */
-static bool is_nurbs_to_bezier_one_to_one(const KnotsMode knots_mode)
-{
- if (ELEM(knots_mode, NURBS_KNOT_MODE_NORMAL, NURBS_KNOT_MODE_ENDPOINT)) {
- return true;
- }
- return false;
-}
-
-/**
- * As an optimization, just change the types on a mutable curves data-block when the conversion is
- * simple. This could be expanded to more cases where the number of points doesn't change in the
- * future, though that might require properly initializing some attributes, or removing others.
- */
-static bool conversion_can_change_point_num(const CurveType dst_type)
-{
- if (ELEM(dst_type, CURVE_TYPE_CATMULL_ROM, CURVE_TYPE_POLY)) {
- /* The conversion to Catmull Rom or Poly should never change the number of points, no matter
- * the source type (Bezier to Catmull Rom conversion cannot maintain the same shape anyway). */
- return false;
- }
- return true;
-}
-
-template<typename T>
-static void scale_input_assign(const Span<T> src,
- const int scale,
- const int offset,
- MutableSpan<T> dst)
-{
- for (const int i : dst.index_range()) {
- dst[i] = src[i * scale + offset];
- }
-}
-
-/**
- * The Bezier control point and its handles become three control points on the NURBS curve,
- * so each attribute value is duplicated three times.
- */
-template<typename T> static void bezier_generic_to_nurbs(const Span<T> src, MutableSpan<T> dst)
-{
- for (const int i : src.index_range()) {
- dst[i * 3] = src[i];
- dst[i * 3 + 1] = src[i];
- dst[i * 3 + 2] = src[i];
- }
-}
-
-static void bezier_generic_to_nurbs(const GSpan src, GMutableSpan dst)
-{
- attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
- using T = decltype(dummy);
- bezier_generic_to_nurbs(src.typed<T>(), dst.typed<T>());
- });
-}
-
-static void bezier_positions_to_nurbs(const Span<float3> src_positions,
- const Span<float3> src_handles_l,
- const Span<float3> src_handles_r,
- MutableSpan<float3> dst_positions)
-{
- for (const int i : src_positions.index_range()) {
- dst_positions[i * 3] = src_handles_l[i];
- dst_positions[i * 3 + 1] = src_positions[i];
- dst_positions[i * 3 + 2] = src_handles_r[i];
- }
-}
-
-static void catmull_rom_to_bezier_handles(const Span<float3> src_positions,
- const bool cyclic,
- MutableSpan<float3> dst_handles_l,
- MutableSpan<float3> dst_handles_r)
-{
- /* Catmull Rom curves are the same as Bezier curves with automatically defined handle positions.
- * This constant defines the portion of the distance between the next/previous points to use for
- * the length of the handles. */
- constexpr float handle_scale = 1.0f / 6.0f;
-
- if (src_positions.size() == 1) {
- dst_handles_l.first() = src_positions.first();
- dst_handles_r.first() = src_positions.first();
- return;
- }
-
- const float3 first_offset = cyclic ? src_positions[1] - src_positions.last() :
- src_positions[1] - src_positions[0];
- dst_handles_r.first() = src_positions.first() + first_offset * handle_scale;
- dst_handles_l.first() = src_positions.first() - first_offset * handle_scale;
-
- const float3 last_offset = cyclic ? src_positions.first() - src_positions.last(1) :
- src_positions.last() - src_positions.last(1);
- dst_handles_l.last() = src_positions.last() - last_offset * handle_scale;
- dst_handles_r.last() = src_positions.last() + last_offset * handle_scale;
-
- for (const int i : src_positions.index_range().drop_front(1).drop_back(1)) {
- const float3 left_offset = src_positions[i - 1] - src_positions[i + 1];
- dst_handles_l[i] = src_positions[i] + left_offset * handle_scale;
-
- const float3 right_offset = src_positions[i + 1] - src_positions[i - 1];
- dst_handles_r[i] = src_positions[i] + right_offset * handle_scale;
- }
-}
-
-static void catmull_rom_to_nurbs_positions(const Span<float3> src_positions,
- const bool cyclic,
- MutableSpan<float3> dst_positions)
-{
- /* Convert the Catmull Rom position data to Bezier handles in order to reuse the Bezier to
- * NURBS positions assignment. If this becomes a bottleneck, this step could be avoided. */
- Array<float3, 32> bezier_handles_l(src_positions.size());
- Array<float3, 32> bezier_handles_r(src_positions.size());
- catmull_rom_to_bezier_handles(src_positions, cyclic, bezier_handles_l, bezier_handles_r);
- bezier_positions_to_nurbs(src_positions, bezier_handles_l, bezier_handles_r, dst_positions);
-}
-
-template<typename T>
-static void nurbs_to_bezier_assign(const Span<T> src,
- const MutableSpan<T> dst,
- const KnotsMode knots_mode)
-{
- switch (knots_mode) {
- case NURBS_KNOT_MODE_NORMAL:
- for (const int i : dst.index_range()) {
- dst[i] = src[(i + 1) % src.size()];
- }
- break;
- case NURBS_KNOT_MODE_ENDPOINT:
- for (const int i : dst.index_range().drop_back(1).drop_front(1)) {
- dst[i] = src[i + 1];
- }
- dst.first() = src.first();
- dst.last() = src.last();
- break;
- default:
- /* Every 3rd NURBS position (starting from index 1) should have its attributes transferred.
- */
- scale_input_assign<T>(src, 3, 1, dst);
- }
-}
-
-static void nurbs_to_bezier_assign(const GSpan src, const KnotsMode knots_mode, 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);
- });
-}
-
-static Vector<float3> create_nurbs_to_bezier_handles(const Span<float3> nurbs_positions,
- const KnotsMode knots_mode)
-{
- const int nurbs_positions_num = nurbs_positions.size();
- Vector<float3> handle_positions;
-
- if (is_nurbs_to_bezier_one_to_one(knots_mode)) {
- const bool is_periodic = knots_mode == NURBS_KNOT_MODE_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]);
- }
-
- /* Place Bezier handles on interior NURBS hull segments. Those handles can be either placed on
- * endpoints, midpoints or 1/3 of the distance of a hull segment. */
- const int segments_num = nurbs_positions_num - 1;
- const bool ignore_interior_segment = segments_num == 3 && is_periodic == false;
- if (ignore_interior_segment == false) {
- const float mid_offset = (float)(segments_num - 1) / 2.0f;
- for (const int i : IndexRange(1, segments_num - 2)) {
- /* Divisor can have values: 1, 2 or 3. */
- 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_num - 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]);
- }
- }
- else {
- for (const int i : IndexRange(nurbs_positions_num)) {
- if (i % 3 == 1) {
- continue;
- }
- handle_positions.append(nurbs_positions[i]);
- }
- if (nurbs_positions_num % 3 == 1) {
- handle_positions.pop_last();
- }
- else if (nurbs_positions_num % 3 == 2) {
- const int last_index = nurbs_positions_num - 1;
- handle_positions.append(2 * nurbs_positions[last_index] - nurbs_positions[last_index - 1]);
- }
- }
-
- return handle_positions;
-}
-
-static void create_nurbs_to_bezier_positions(const Span<float3> nurbs_positions,
- const Span<float3> handle_positions,
- const KnotsMode knots_mode,
- MutableSpan<float3> bezier_positions)
-{
- if (is_nurbs_to_bezier_one_to_one(knots_mode)) {
- for (const int i : bezier_positions.index_range()) {
- bezier_positions[i] = math::interpolate(
- handle_positions[i * 2], handle_positions[i * 2 + 1], 0.5f);
- }
- }
- else {
- /* Every 3rd NURBS position (starting from index 1) should be converted to Bezier position. */
- scale_input_assign(nurbs_positions, 3, 1, bezier_positions);
- }
-}
-
-static int to_bezier_size(const CurveType src_type,
- const bool cyclic,
- const KnotsMode knots_mode,
- const int src_size)
-{
- switch (src_type) {
- case CURVE_TYPE_NURBS: {
- if (is_nurbs_to_bezier_one_to_one(knots_mode)) {
- return cyclic ? src_size : src_size - 2;
- }
- return (src_size + 1) / 3;
- }
- default:
- return src_size;
- }
-}
-
-static int to_nurbs_size(const CurveType src_type, const int src_size)
-{
- switch (src_type) {
- case CURVE_TYPE_BEZIER:
- case CURVE_TYPE_CATMULL_ROM:
- return src_size * 3;
- default:
- return src_size;
- }
-}
-
-static void retrieve_curve_sizes(const bke::CurvesGeometry &curves, MutableSpan<int> sizes)
-{
- threading::parallel_for(curves.curves_range(), 4096, [&](IndexRange range) {
- for (const int i : range) {
- sizes[i] = curves.points_for_curve(i).size();
- }
- });
-}
-
-struct GenericAttributes : NonCopyable, NonMovable {
- Vector<GSpan> src;
- Vector<GMutableSpan> dst;
-
- Vector<OutputAttribute> attributes;
-};
-
-static void retrieve_generic_point_attributes(const CurveComponent &src_component,
- CurveComponent &dst_component,
- GenericAttributes &attributes)
-{
- src_component.attribute_foreach(
- [&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
- if (meta_data.domain != ATTR_DOMAIN_POINT) {
- /* Curve domain attributes are all copied directly to the result in one step. */
- return true;
- }
- if (src_component.attribute_is_builtin(id)) {
- if (!(id.is_named() && ELEM(id, "tilt", "radius"))) {
- return true;
- }
- }
-
- GVArray src_attribute = src_component.attribute_try_get_for_read(id, ATTR_DOMAIN_POINT);
- BLI_assert(src_attribute);
- attributes.src.append(src_attribute.get_internal_span());
-
- OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
- id, ATTR_DOMAIN_POINT, meta_data.data_type);
- attributes.dst.append(dst_attribute.as_span());
- attributes.attributes.append(std::move(dst_attribute));
-
- return true;
- });
-}
-
-static void convert_to_bezier(const CurveComponent &src_component,
- const bke::CurvesGeometry &src_curves,
- const IndexMask selection,
- CurveComponent &dst_component,
- bke::CurvesGeometry &dst_curves)
-{
- const VArray<int8_t> src_knot_modes = src_curves.nurbs_knots_modes();
- const VArray<int8_t> src_types = src_curves.curve_types();
- const VArray<bool> src_cyclic = src_curves.cyclic();
- const Span<float3> src_positions = src_curves.positions();
-
- MutableSpan<int> dst_offsets = dst_curves.offsets_for_write();
- retrieve_curve_sizes(src_curves, dst_curves.offsets_for_write());
- threading::parallel_for(selection.index_range(), 1024, [&](IndexRange range) {
- for (const int i : selection.slice(range)) {
- dst_offsets[i] = to_bezier_size(
- CurveType(src_types[i]), src_cyclic[i], KnotsMode(src_knot_modes[i]), dst_offsets[i]);
- }
- });
- bke::curves::accumulate_counts_to_offsets(dst_offsets);
- dst_curves.resize(dst_offsets.last(), dst_curves.curves_num());
-
- GenericAttributes attributes;
- retrieve_generic_point_attributes(src_component, dst_component, attributes);
-
- MutableSpan<float3> dst_positions = dst_curves.positions_for_write();
- MutableSpan<float3> dst_handles_l = dst_curves.handle_positions_left_for_write();
- MutableSpan<float3> dst_handles_r = dst_curves.handle_positions_right_for_write();
- MutableSpan<int8_t> dst_types_l = dst_curves.handle_types_left_for_write();
- MutableSpan<int8_t> dst_types_r = dst_curves.handle_types_right_for_write();
- MutableSpan<float> dst_weights = dst_curves.nurbs_weights_for_write();
-
- auto catmull_rom_to_bezier = [&](IndexMask selection) {
- bke::curves::fill_points<int8_t>(dst_curves, selection, BEZIER_HANDLE_ALIGN, dst_types_l);
- bke::curves::fill_points<int8_t>(dst_curves, selection, BEZIER_HANDLE_ALIGN, dst_types_r);
- bke::curves::copy_point_data(src_curves, dst_curves, selection, src_positions, dst_positions);
-
- threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) {
- for (const int i : selection.slice(range)) {
- const IndexRange src_points = src_curves.points_for_curve(i);
- const IndexRange dst_points = dst_curves.points_for_curve(i);
- catmull_rom_to_bezier_handles(src_positions.slice(src_points),
- src_cyclic[i],
- dst_handles_l.slice(dst_points),
- dst_handles_r.slice(dst_points));
- }
- });
-
- for (const int i : attributes.src.index_range()) {
- bke::curves::copy_point_data(
- src_curves, dst_curves, selection, attributes.src[i], attributes.dst[i]);
- }
- };
-
- auto poly_to_bezier = [&](IndexMask selection) {
- bke::curves::copy_point_data(src_curves, dst_curves, selection, src_positions, dst_positions);
- bke::curves::fill_points<int8_t>(dst_curves, selection, BEZIER_HANDLE_VECTOR, dst_types_l);
- bke::curves::fill_points<int8_t>(dst_curves, selection, BEZIER_HANDLE_VECTOR, dst_types_r);
- dst_curves.calculate_bezier_auto_handles();
- for (const int i : attributes.src.index_range()) {
- bke::curves::copy_point_data(
- src_curves, dst_curves, selection, attributes.src[i], attributes.dst[i]);
- }
- };
-
- auto bezier_to_bezier = [&](IndexMask selection) {
- const VArray_Span<int8_t> src_types_l = src_curves.handle_types_left();
- const VArray_Span<int8_t> src_types_r = src_curves.handle_types_right();
- const Span<float3> src_handles_l = src_curves.handle_positions_left();
- const Span<float3> src_handles_r = src_curves.handle_positions_right();
-
- bke::curves::copy_point_data(src_curves, dst_curves, selection, src_positions, dst_positions);
- bke::curves::copy_point_data(src_curves, dst_curves, selection, src_handles_l, dst_handles_l);
- bke::curves::copy_point_data(src_curves, dst_curves, selection, src_handles_r, dst_handles_r);
- bke::curves::copy_point_data(src_curves, dst_curves, selection, src_types_l, dst_types_l);
- bke::curves::copy_point_data(src_curves, dst_curves, selection, src_types_r, dst_types_r);
-
- dst_curves.calculate_bezier_auto_handles();
-
- for (const int i : attributes.src.index_range()) {
- bke::curves::copy_point_data(
- src_curves, dst_curves, selection, attributes.src[i], attributes.dst[i]);
- }
- };
-
- auto nurbs_to_bezier = [&](IndexMask selection) {
- bke::curves::fill_points<int8_t>(dst_curves, selection, BEZIER_HANDLE_ALIGN, dst_types_l);
- bke::curves::fill_points<int8_t>(dst_curves, selection, BEZIER_HANDLE_ALIGN, dst_types_r);
- bke::curves::fill_points<float>(dst_curves, selection, 0.0f, dst_weights);
-
- threading::parallel_for(selection.index_range(), 64, [&](IndexRange range) {
- for (const int i : selection.slice(range)) {
- const IndexRange src_points = src_curves.points_for_curve(i);
- const IndexRange dst_points = dst_curves.points_for_curve(i);
- const Span<float3> src_curve_positions = src_positions.slice(src_points);
-
- KnotsMode knots_mode = KnotsMode(src_knot_modes[i]);
- Span<float3> nurbs_positions = src_curve_positions;
- Vector<float3> nurbs_positions_vector;
- if (src_cyclic[i] && is_nurbs_to_bezier_one_to_one(knots_mode)) {
- /* For conversion treat this as periodic closed curve. Extend NURBS hull to first and
- * second point which will act as a skeleton for placing Bezier handles. */
- nurbs_positions_vector.extend(src_curve_positions);
- nurbs_positions_vector.append(src_curve_positions[0]);
- nurbs_positions_vector.append(src_curve_positions[1]);
- nurbs_positions = nurbs_positions_vector;
- knots_mode = NURBS_KNOT_MODE_NORMAL;
- }
-
- const Vector<float3> handle_positions = create_nurbs_to_bezier_handles(nurbs_positions,
- knots_mode);
-
- scale_input_assign(handle_positions.as_span(), 2, 0, dst_handles_l.slice(dst_points));
- scale_input_assign(handle_positions.as_span(), 2, 1, dst_handles_r.slice(dst_points));
-
- create_nurbs_to_bezier_positions(
- nurbs_positions, handle_positions, knots_mode, dst_positions.slice(dst_points));
- }
- });
-
- for (const int i_attribute : attributes.src.index_range()) {
- threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) {
- for (const int i : selection.slice(range)) {
- const IndexRange src_points = src_curves.points_for_curve(i);
- const IndexRange dst_points = dst_curves.points_for_curve(i);
- nurbs_to_bezier_assign(attributes.src[i_attribute].slice(src_points),
- KnotsMode(src_knot_modes[i]),
- attributes.dst[i_attribute].slice(dst_points));
- }
- });
- }
- };
-
- bke::curves::foreach_curve_by_type(src_curves.curve_types(),
- src_curves.curve_type_counts(),
- selection,
- catmull_rom_to_bezier,
- poly_to_bezier,
- bezier_to_bezier,
- nurbs_to_bezier);
-
- const Vector<IndexRange> unselected_ranges = selection.extract_ranges_invert(
- src_curves.curves_range());
-
- for (const int i : attributes.src.index_range()) {
- bke::curves::copy_point_data(
- src_curves, dst_curves, unselected_ranges, attributes.src[i], attributes.dst[i]);
- }
-
- for (OutputAttribute &attribute : attributes.attributes) {
- attribute.save();
- }
-}
-
-static void convert_to_nurbs(const CurveComponent &src_component,
- const bke::CurvesGeometry &src_curves,
- const IndexMask selection,
- CurveComponent &dst_component,
- bke::CurvesGeometry &dst_curves)
-{
- const VArray<int8_t> src_types = src_curves.curve_types();
- const VArray<bool> src_cyclic = src_curves.cyclic();
- const Span<float3> src_positions = src_curves.positions();
-
- MutableSpan<int> dst_offsets = dst_curves.offsets_for_write();
- retrieve_curve_sizes(src_curves, dst_curves.offsets_for_write());
- threading::parallel_for(selection.index_range(), 1024, [&](IndexRange range) {
- for (const int i : selection.slice(range)) {
- dst_offsets[i] = to_nurbs_size(CurveType(src_types[i]), dst_offsets[i]);
- }
- });
- bke::curves::accumulate_counts_to_offsets(dst_offsets);
- dst_curves.resize(dst_offsets.last(), dst_curves.curves_num());
-
- GenericAttributes attributes;
- retrieve_generic_point_attributes(src_component, dst_component, attributes);
-
- MutableSpan<float3> dst_positions = dst_curves.positions_for_write();
-
- auto fill_weights_if_necessary = [&](const IndexMask selection) {
- if (!src_curves.nurbs_weights().is_empty()) {
- bke::curves::fill_points(dst_curves, selection, 1.0f, dst_curves.nurbs_weights_for_write());
- }
- };
-
- auto catmull_rom_to_nurbs = [&](IndexMask selection) {
- dst_curves.nurbs_orders_for_write().fill_indices(selection, 4);
- dst_curves.nurbs_knots_modes_for_write().fill_indices(selection, NURBS_KNOT_MODE_BEZIER);
- fill_weights_if_necessary(selection);
-
- threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) {
- for (const int i : selection.slice(range)) {
- const IndexRange src_points = src_curves.points_for_curve(i);
- const IndexRange dst_points = dst_curves.points_for_curve(i);
- catmull_rom_to_nurbs_positions(
- src_positions.slice(src_points), src_cyclic[i], dst_positions.slice(dst_points));
- }
- });
-
- for (const int i_attribute : attributes.src.index_range()) {
- threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) {
- for (const int i : selection.slice(range)) {
- const IndexRange src_points = src_curves.points_for_curve(i);
- const IndexRange dst_points = dst_curves.points_for_curve(i);
- bezier_generic_to_nurbs(attributes.src[i_attribute].slice(src_points),
- attributes.dst[i_attribute].slice(dst_points));
- }
- });
- }
- };
-
- auto poly_to_nurbs = [&](IndexMask selection) {
- dst_curves.nurbs_orders_for_write().fill_indices(selection, 4);
- bke::curves::copy_point_data(src_curves, dst_curves, selection, src_positions, dst_positions);
- fill_weights_if_necessary(selection);
-
- /* Avoid using "Endpoint" knots modes for cyclic curves, since it adds a sharp point at the
- * start/end. */
- if (src_cyclic.is_single()) {
- dst_curves.nurbs_knots_modes_for_write().fill_indices(
- selection,
- src_cyclic.get_internal_single() ? NURBS_KNOT_MODE_NORMAL : NURBS_KNOT_MODE_ENDPOINT);
- }
- else {
- VArray_Span<bool> cyclic{src_cyclic};
- MutableSpan<int8_t> knots_modes = dst_curves.nurbs_knots_modes_for_write();
- threading::parallel_for(selection.index_range(), 1024, [&](IndexRange range) {
- for (const int i : selection.slice(range)) {
- knots_modes[i] = cyclic[i] ? NURBS_KNOT_MODE_NORMAL : NURBS_KNOT_MODE_ENDPOINT;
- }
- });
- }
-
- for (const int i_attribute : attributes.src.index_range()) {
- bke::curves::copy_point_data(src_curves,
- dst_curves,
- selection,
- attributes.src[i_attribute],
- attributes.dst[i_attribute]);
- }
- };
-
- auto bezier_to_nurbs = [&](IndexMask selection) {
- const Span<float3> src_handles_l = src_curves.handle_positions_left();
- const Span<float3> src_handles_r = src_curves.handle_positions_right();
-
- dst_curves.nurbs_orders_for_write().fill_indices(selection, 4);
- dst_curves.nurbs_knots_modes_for_write().fill_indices(selection, NURBS_KNOT_MODE_BEZIER);
- fill_weights_if_necessary(selection);
-
- threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) {
- for (const int i : selection.slice(range)) {
- const IndexRange src_points = src_curves.points_for_curve(i);
- const IndexRange dst_points = dst_curves.points_for_curve(i);
- bezier_positions_to_nurbs(src_positions.slice(src_points),
- src_handles_l.slice(src_points),
- src_handles_r.slice(src_points),
- dst_positions.slice(dst_points));
- }
- });
-
- for (const int i_attribute : attributes.src.index_range()) {
- threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) {
- for (const int i : selection.slice(range)) {
- const IndexRange src_points = src_curves.points_for_curve(i);
- const IndexRange dst_points = dst_curves.points_for_curve(i);
- bezier_generic_to_nurbs(attributes.src[i_attribute].slice(src_points),
- attributes.dst[i_attribute].slice(dst_points));
- }
- });
- }
- };
-
- auto nurbs_to_nurbs = [&](IndexMask selection) {
- bke::curves::copy_point_data(src_curves, dst_curves, selection, src_positions, dst_positions);
-
- if (!src_curves.nurbs_weights().is_empty()) {
- bke::curves::copy_point_data(src_curves,
- dst_curves,
- selection,
- src_curves.nurbs_weights(),
- dst_curves.nurbs_weights_for_write());
- }
-
- for (const int i_attribute : attributes.src.index_range()) {
- bke::curves::copy_point_data(src_curves,
- dst_curves,
- selection,
- attributes.src[i_attribute],
- attributes.dst[i_attribute]);
- }
- };
-
- bke::curves::foreach_curve_by_type(src_curves.curve_types(),
- src_curves.curve_type_counts(),
- selection,
- catmull_rom_to_nurbs,
- poly_to_nurbs,
- bezier_to_nurbs,
- nurbs_to_nurbs);
-
- const Vector<IndexRange> unselected_ranges = selection.extract_ranges_invert(
- src_curves.curves_range());
-
- for (const int i : attributes.src.index_range()) {
- bke::curves::copy_point_data(
- src_curves, dst_curves, unselected_ranges, attributes.src[i], attributes.dst[i]);
- }
-
- for (OutputAttribute &attribute : attributes.attributes) {
- attribute.save();
- }
-}
-
static void node_geo_exec(GeoNodeExecParams params)
{
const NodeGeometryCurveSplineType &storage = node_storage(params.node());
@@ -674,57 +45,32 @@ static void node_geo_exec(GeoNodeExecParams params)
if (!geometry_set.has_curves()) {
return;
}
- const CurveComponent &src_component = *geometry_set.get_component_for_read<CurveComponent>();
- const Curves &src_curves_id = *src_component.get_for_read();
+ const Curves &src_curves_id = *geometry_set.get_curves_for_read();
const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(src_curves_id.geometry);
if (src_curves.is_single_type(dst_type)) {
return;
}
- GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_CURVE};
- const int domain_size = src_component.attribute_domain_num(ATTR_DOMAIN_CURVE);
-
- fn::FieldEvaluator evaluator{field_context, domain_size};
+ bke::CurvesFieldContext field_context{src_curves, ATTR_DOMAIN_CURVE};
+ fn::FieldEvaluator evaluator{field_context, src_curves.curves_num()};
evaluator.set_selection(selection_field);
evaluator.evaluate();
const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
- if (!conversion_can_change_point_num(dst_type)) {
- CurveComponent &dst_component = geometry_set.get_component_for_write<CurveComponent>();
- Curves &curves_id = *dst_component.get_for_write();
- bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
- curves.fill_curve_types(selection, dst_type);
- curves.remove_attributes_based_on_types();
+ if (selection.is_empty()) {
return;
}
- Curves *dst_curves_id = bke::curves_new_nomain(0, src_curves.curves_num());
- bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap(dst_curves_id->geometry);
- CurveComponent dst_component;
- dst_component.replace(dst_curves_id, GeometryOwnershipType::Editable);
- /* Directly copy curve attributes, since they stay the same (except for curve types). */
- CustomData_copy(&src_curves.curve_data,
- &dst_curves.curve_data,
- CD_MASK_ALL,
- CD_DUPLICATE,
- src_curves.curves_num());
-
- dst_curves.fill_curve_types(selection, dst_type);
-
- switch (dst_type) {
- case CURVE_TYPE_CATMULL_ROM:
- case CURVE_TYPE_POLY:
- /* Converting to Catmull Rom curves or poly curves should be handled
- * above by the optimization to avoid changing the point count. */
- BLI_assert_unreachable();
- break;
- case CURVE_TYPE_BEZIER:
- convert_to_bezier(src_component, src_curves, selection, dst_component, dst_curves);
- break;
- case CURVE_TYPE_NURBS:
- convert_to_nurbs(src_component, src_curves, selection, dst_component, dst_curves);
- break;
+ if (geometry::try_curves_conversion_in_place(
+ selection, dst_type, [&]() -> bke::CurvesGeometry & {
+ return bke::CurvesGeometry::wrap(geometry_set.get_curves_for_write()->geometry);
+ })) {
+ return;
}
+ bke::CurvesGeometry dst_curves = geometry::convert_curves(src_curves, selection, dst_type);
+
+ Curves *dst_curves_id = bke::curves_new_nomain(std::move(dst_curves));
+ bke::curves_copy_parameters(src_curves_id, *dst_curves_id);
geometry_set.replace_curves(dst_curves_id);
});
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc
index 4d8745bf79e..919d0056bca 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc
@@ -1,10 +1,8 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
-#include "BLI_task.hh"
-#include "BLI_timeit.hh"
+#include "BKE_curves.hh"
-#include "BKE_attribute_math.hh"
-#include "BKE_spline.hh"
+#include "GEO_subdivide_curves.hh"
#include "UI_interface.h"
#include "UI_resources.h"
@@ -26,302 +24,6 @@ static void node_declare(NodeDeclarationBuilder &b)
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_num() + 1);
- int offset = 0;
- for (const int i : IndexRange(spline.segments_num())) {
- 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_num = src.size();
- threading::parallel_for(IndexRange(src_num - 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_num - 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_num,
- 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<int8_t> dst_type_left,
- MutableSpan<int8_t> 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() = BEZIER_HANDLE_VECTOR;
- }
- dst_type_left.slice(offset + 1, result_num).fill(BEZIER_HANDLE_VECTOR);
- dst_type_right.slice(offset, result_num).fill(BEZIER_HANDLE_VECTOR);
-
- const float factor_delta = 1.0f / result_num;
- for (const int cut : IndexRange(result_num)) {
- 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() = BEZIER_HANDLE_FREE;
- }
- dst_type_left.slice(offset + 1, result_num).fill(BEZIER_HANDLE_FREE);
- dst_type_right.slice(offset, result_num).fill(BEZIER_HANDLE_FREE);
-
- const int i_segment_last = is_last_cyclic_segment ? 0 : offset + result_num;
-
- /* 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_num - 1)) {
- const float parameter = 1.0f / (result_num - 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_num - 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<int8_t> dst_type_left = dst.handle_types_left();
- MutableSpan<int8_t> 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();
- dst_type_left.first() = src.handle_types_left().first();
- dst_type_right.last() = src.handle_types_right().last();
- dst_handles_left.first() = src_handles_left.first();
- dst_handles_right.last() = src_handles_right.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 CURVE_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 CURVE_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 CURVE_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;
- }
- case CURVE_TYPE_CATMULL_ROM: {
- BLI_assert_unreachable();
- 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_num = offsets.last() + int(!spline.is_cyclic());
- SplinePtr new_spline = spline.copy_only_settings();
- new_spline->resize(result_num);
- 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");
@@ -332,21 +34,24 @@ static void node_geo_exec(GeoNodeExecParams params)
return;
}
- const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>();
- GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
- const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_POINT);
+ const Curves &src_curves_id = *geometry_set.get_curves_for_read();
+ const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(src_curves_id.geometry);
- fn::FieldEvaluator evaluator{field_context, domain_num};
+ bke::CurvesFieldContext field_context{src_curves, ATTR_DOMAIN_POINT};
+ fn::FieldEvaluator evaluator{field_context, src_curves.points_num()};
evaluator.add(cuts_field);
evaluator.evaluate();
- const VArray<int> &cuts = evaluator.get_evaluated<int>(0);
-
+ 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(
- *curves_to_curve_eval(*component.get_for_read()), cuts);
- geometry_set.replace_curves(curve_eval_to_curves(*output_curve));
+
+ bke::CurvesGeometry dst_curves = geometry::subdivide_curves(
+ src_curves, src_curves.curves_range(), cuts);
+
+ Curves *dst_curves_id = bke::curves_new_nomain(std::move(dst_curves));
+ bke::curves_copy_parameters(src_curves_id, *dst_curves_id);
+ geometry_set.replace_curves(dst_curves_id);
});
params.set_output("Curve", geometry_set);
}
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 903a5e7c1d7..bc319ce905a 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
@@ -28,9 +28,10 @@ static void geometry_set_curve_to_mesh(GeometrySet &geometry_set,
const bool fill_caps)
{
const Curves &curves = *geometry_set.get_curves_for_read();
-
const Curves *profile_curves = profile_set.get_curves_for_read();
+ GeometryComponentEditData::remember_deformed_curve_positions_if_necessary(geometry_set);
+
if (profile_curves == nullptr) {
Mesh *mesh = bke::curve_to_wire_mesh(bke::CurvesGeometry::wrap(curves.geometry));
geometry_set.replace_mesh(mesh);
@@ -55,7 +56,7 @@ static void node_geo_exec(GeoNodeExecParams params)
has_curves = true;
geometry_set_curve_to_mesh(geometry_set, profile_set, fill_caps);
}
- geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES});
+ geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_MESH});
});
params.set_output("Mesh", std::move(curve_set));
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
index 7d83b4b3ecb..0ddf06dc8c7 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc
@@ -126,19 +126,22 @@ static GMutableSpan ensure_point_attribute(PointCloudComponent &points,
const AttributeIDRef &attribute_id,
const eCustomDataType 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();
+ GAttributeWriter attribute = points.attributes_for_write()->lookup_or_add_for_write(
+ attribute_id, ATTR_DOMAIN_POINT, data_type);
+ GMutableSpan span = attribute.varray.get_internal_span();
+ attribute.finish();
+ return 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>();
+ AttributeWriter<T> attribute = points.attributes_for_write()->lookup_or_add_for_write<T>(
+ attribute_id, ATTR_DOMAIN_POINT);
+ MutableSpan<T> span = attribute.varray.get_internal_span();
+ attribute.finish();
+ return span;
}
namespace {
@@ -317,9 +320,11 @@ static void node_geo_exec(GeoNodeExecParams params)
attribute_outputs.normal_id = StrongAnonymousAttributeID("Normal");
attribute_outputs.rotation_id = StrongAnonymousAttributeID("Rotation");
+ GeometryComponentEditData::remember_deformed_curve_positions_if_necessary(geometry_set);
+
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
if (!geometry_set.has_curves()) {
- geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ geometry_set.remove_geometry_during_modify();
return;
}
const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(
@@ -330,7 +335,7 @@ static void node_geo_exec(GeoNodeExecParams params)
const Array<int> offsets = calculate_spline_point_offsets(params, mode, *curve, splines);
const int total_num = offsets.last();
if (total_num == 0) {
- geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ geometry_set.remove_geometry_during_modify();
return;
}
@@ -356,7 +361,7 @@ static void node_geo_exec(GeoNodeExecParams params)
point_attributes.tangents, point_attributes.normals, point_attributes.rotations);
}
- geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES, GEO_COMPONENT_TYPE_POINT_CLOUD});
+ geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_POINT_CLOUD});
});
params.set_output("Points", std::move(geometry_set));
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 c993a3d305d..443f67be421 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc
@@ -1,5 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include "BKE_curves.hh"
#include "BKE_spline.hh"
#include "BLI_task.hh"
@@ -503,19 +504,18 @@ static void geometry_set_curve_trim(GeometrySet &geometry_set,
if (!geometry_set.has_curves()) {
return;
}
+ const Curves &src_curves_id = *geometry_set.get_curves_for_read();
+ const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(src_curves_id.geometry);
- CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>();
- GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE};
- const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_CURVE);
-
- fn::FieldEvaluator evaluator{field_context, domain_num};
+ bke::CurvesFieldContext field_context{curves, ATTR_DOMAIN_CURVE};
+ fn::FieldEvaluator evaluator{field_context, curves.curves_num()};
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);
+ const VArray<float> starts = evaluator.get_evaluated<float>(0);
+ const VArray<float> ends = evaluator.get_evaluated<float>(1);
- std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*geometry_set.get_curves_for_read());
+ std::unique_ptr<CurveEval> curve = curves_to_curve_eval(src_curves_id);
MutableSpan<SplinePtr> splines = curve->splines();
threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) {
@@ -566,7 +566,9 @@ static void geometry_set_curve_trim(GeometrySet &geometry_set,
}
});
- geometry_set.replace_curves(curve_eval_to_curves(*curve));
+ Curves *dst_curves_id = curve_eval_to_curves(*curve);
+ bke::curves_copy_parameters(src_curves_id, *dst_curves_id);
+ geometry_set.replace_curves(dst_curves_id);
}
static void node_geo_exec(GeoNodeExecParams params)
@@ -575,6 +577,7 @@ static void node_geo_exec(GeoNodeExecParams params)
const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)storage.mode;
GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
+ GeometryComponentEditData::remember_deformed_curve_positions_if_necessary(geometry_set);
if (mode == GEO_NODE_CURVE_SAMPLE_FACTOR) {
Field<float> start_field = params.extract_input<Field<float>>("Start");
diff --git a/source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc
new file mode 100644
index 00000000000..3f6155460ed
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc
@@ -0,0 +1,424 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "BKE_attribute_math.hh"
+#include "BKE_curves.hh"
+#include "BKE_editmesh.h"
+#include "BKE_lib_id.h"
+#include "BKE_mesh.h"
+#include "BKE_mesh_runtime.h"
+#include "BKE_mesh_wrapper.h"
+#include "BKE_modifier.h"
+#include "BKE_type_conversions.hh"
+
+#include "BLI_float3x3.hh"
+#include "BLI_task.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "NOD_socket_search_link.hh"
+
+#include "GEO_reverse_uv_sampler.hh"
+
+#include "DEG_depsgraph_query.h"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_deform_curves_on_surface_cc {
+
+using attribute_math::mix3;
+using bke::CurvesGeometry;
+using geometry::ReverseUVSampler;
+
+NODE_STORAGE_FUNCS(NodeGeometryCurveTrim)
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>(N_("Curves")).supported_type(GEO_COMPONENT_TYPE_CURVE);
+ b.add_output<decl::Geometry>(N_("Curves"));
+}
+
+static void deform_curves(const CurvesGeometry &curves,
+ const Mesh &surface_mesh_old,
+ const Mesh &surface_mesh_new,
+ const Span<float2> curve_attachment_uvs,
+ const ReverseUVSampler &reverse_uv_sampler_old,
+ const ReverseUVSampler &reverse_uv_sampler_new,
+ const Span<float3> corner_normals_old,
+ const Span<float3> corner_normals_new,
+ const Span<float3> rest_positions,
+ const float4x4 &surface_to_curves,
+ MutableSpan<float3> r_positions,
+ MutableSpan<float3x3> r_rotations,
+ std::atomic<int> &r_invalid_uv_count)
+{
+ /* Find attachment points on old and new mesh. */
+ const int curves_num = curves.curves_num();
+ Array<ReverseUVSampler::Result> surface_samples_old(curves_num);
+ Array<ReverseUVSampler::Result> surface_samples_new(curves_num);
+ threading::parallel_invoke(
+ 1024 < curves_num,
+ [&]() { reverse_uv_sampler_old.sample_many(curve_attachment_uvs, surface_samples_old); },
+ [&]() { reverse_uv_sampler_new.sample_many(curve_attachment_uvs, surface_samples_new); });
+
+ const float4x4 curves_to_surface = surface_to_curves.inverted();
+
+ const Span<MVert> surface_verts_old = surface_mesh_old.verts();
+ const Span<MLoop> surface_loops_old = surface_mesh_old.loops();
+
+ const Span<MVert> surface_verts_new = surface_mesh_new.verts();
+ const Span<MLoop> surface_loops_new = surface_mesh_new.loops();
+
+ threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange range) {
+ for (const int curve_i : range) {
+ const ReverseUVSampler::Result &surface_sample_old = surface_samples_old[curve_i];
+ if (surface_sample_old.type != ReverseUVSampler::ResultType::Ok) {
+ r_invalid_uv_count++;
+ continue;
+ }
+ const ReverseUVSampler::Result &surface_sample_new = surface_samples_new[curve_i];
+ if (surface_sample_new.type != ReverseUVSampler::ResultType::Ok) {
+ r_invalid_uv_count++;
+ continue;
+ }
+
+ const MLoopTri &looptri_old = *surface_sample_old.looptri;
+ const MLoopTri &looptri_new = *surface_sample_new.looptri;
+ const float3 &bary_weights_old = surface_sample_old.bary_weights;
+ const float3 &bary_weights_new = surface_sample_new.bary_weights;
+
+ const int corner_0_old = looptri_old.tri[0];
+ const int corner_1_old = looptri_old.tri[1];
+ const int corner_2_old = looptri_old.tri[2];
+
+ const int corner_0_new = looptri_new.tri[0];
+ const int corner_1_new = looptri_new.tri[1];
+ const int corner_2_new = looptri_new.tri[2];
+
+ const int vert_0_old = surface_loops_old[corner_0_old].v;
+ const int vert_1_old = surface_loops_old[corner_1_old].v;
+ const int vert_2_old = surface_loops_old[corner_2_old].v;
+
+ const int vert_0_new = surface_loops_new[corner_0_new].v;
+ const int vert_1_new = surface_loops_new[corner_1_new].v;
+ const int vert_2_new = surface_loops_new[corner_2_new].v;
+
+ const float3 &normal_0_old = corner_normals_old[corner_0_old];
+ const float3 &normal_1_old = corner_normals_old[corner_1_old];
+ const float3 &normal_2_old = corner_normals_old[corner_2_old];
+ const float3 normal_old = math::normalize(
+ mix3(bary_weights_old, normal_0_old, normal_1_old, normal_2_old));
+
+ const float3 &normal_0_new = corner_normals_new[corner_0_new];
+ const float3 &normal_1_new = corner_normals_new[corner_1_new];
+ const float3 &normal_2_new = corner_normals_new[corner_2_new];
+ const float3 normal_new = math::normalize(
+ mix3(bary_weights_new, normal_0_new, normal_1_new, normal_2_new));
+
+ const float3 &pos_0_old = surface_verts_old[vert_0_old].co;
+ const float3 &pos_1_old = surface_verts_old[vert_1_old].co;
+ const float3 &pos_2_old = surface_verts_old[vert_2_old].co;
+ const float3 pos_old = mix3(bary_weights_old, pos_0_old, pos_1_old, pos_2_old);
+
+ const float3 &pos_0_new = surface_verts_new[vert_0_new].co;
+ const float3 &pos_1_new = surface_verts_new[vert_1_new].co;
+ const float3 &pos_2_new = surface_verts_new[vert_2_new].co;
+ const float3 pos_new = mix3(bary_weights_new, pos_0_new, pos_1_new, pos_2_new);
+
+ /* The translation is just the difference between the old and new position on the surface. */
+ const float3 translation = pos_new - pos_old;
+
+ const float3 &rest_pos_0 = rest_positions[vert_0_new];
+ const float3 &rest_pos_1 = rest_positions[vert_1_new];
+
+ /* The tangent reference direction is used to determine the rotation of the surface point
+ * around its normal axis. It's important that the old and new tangent reference are computed
+ * in a consistent way. If the surface has not been rotated, the old and new tangent
+ * reference have to have the same direction. For that reason, the old tangent reference is
+ * computed based on the rest position attribute instead of positions on the old mesh. This
+ * way the old and new tangent reference use the same topology.
+ *
+ * TODO: Figure out if this can be smoothly interpolated across the surface as well.
+ * Currently, this is a source of discontinuity in the deformation, because the vector
+ * changes instantly from one triangle to the next. */
+ const float3 tangent_reference_dir_old = rest_pos_1 - rest_pos_0;
+ const float3 tangent_reference_dir_new = pos_1_new - pos_0_new;
+
+ /* Compute first local tangent based on the (potentially smoothed) normal and the tangent
+ * reference. */
+ const float3 tangent_x_old = math::normalize(
+ math::cross(normal_old, tangent_reference_dir_old));
+ const float3 tangent_x_new = math::normalize(
+ math::cross(normal_new, tangent_reference_dir_new));
+
+ /* The second tangent defined by the normal and first tangent. */
+ const float3 tangent_y_old = math::normalize(math::cross(normal_old, tangent_x_old));
+ const float3 tangent_y_new = math::normalize(math::cross(normal_new, tangent_x_new));
+
+ /* Construct rotation matrix that encodes the orientation of the old surface position. */
+ float3x3 rotation_old;
+ copy_v3_v3(rotation_old.values[0], tangent_x_old);
+ copy_v3_v3(rotation_old.values[1], tangent_y_old);
+ copy_v3_v3(rotation_old.values[2], normal_old);
+
+ /* Construct rotation matrix that encodes the orientation of the new surface position. */
+ float3x3 rotation_new;
+ copy_v3_v3(rotation_new.values[0], tangent_x_new);
+ copy_v3_v3(rotation_new.values[1], tangent_y_new);
+ copy_v3_v3(rotation_new.values[2], normal_new);
+
+ /* Can use transpose instead of inverse because the matrix is orthonormal. In the case of
+ * zero-area triangles, the matrix would not be orthonormal, but in this case, none of this
+ * works anyway. */
+ const float3x3 rotation_old_inv = rotation_old.transposed();
+
+ /* Compute a rotation matrix that rotates points from the old to the new surface
+ * orientation. */
+ const float3x3 rotation = rotation_new * rotation_old_inv;
+ float4x4 rotation_4x4;
+ copy_m4_m3(rotation_4x4.values, rotation.values);
+
+ /* Construction transformation matrix for this surface position that includes rotation and
+ * translation. */
+ float4x4 surface_transform = float4x4::identity();
+ /* Subtract and add #pos_old, so that the rotation origin is the position on the surface. */
+ sub_v3_v3(surface_transform.values[3], pos_old);
+ mul_m4_m4_pre(surface_transform.values, rotation_4x4.values);
+ add_v3_v3(surface_transform.values[3], pos_old);
+ add_v3_v3(surface_transform.values[3], translation);
+
+ /* Change the basis of the transformation so to that it can be applied in the local space of
+ * the curves. */
+ const float4x4 curve_transform = surface_to_curves * surface_transform * curves_to_surface;
+
+ /* Actually transform all points. */
+ const IndexRange points = curves.points_for_curve(curve_i);
+ for (const int point_i : points) {
+ const float3 old_point_pos = r_positions[point_i];
+ const float3 new_point_pos = curve_transform * old_point_pos;
+ r_positions[point_i] = new_point_pos;
+ }
+
+ if (!r_rotations.is_empty()) {
+ for (const int point_i : points) {
+ r_rotations[point_i] = rotation * r_rotations[point_i];
+ }
+ }
+ }
+ });
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ GeometrySet curves_geometry = params.extract_input<GeometrySet>("Curves");
+
+ Mesh *surface_mesh_orig = nullptr;
+ bool free_suface_mesh_orig = false;
+ BLI_SCOPED_DEFER([&]() {
+ if (free_suface_mesh_orig) {
+ BKE_id_free(nullptr, surface_mesh_orig);
+ }
+ });
+
+ auto pass_through_input = [&]() { params.set_output("Curves", std::move(curves_geometry)); };
+
+ const Object *self_ob_eval = params.self_object();
+ if (self_ob_eval == nullptr || self_ob_eval->type != OB_CURVES) {
+ pass_through_input();
+ params.error_message_add(NodeWarningType::Error, TIP_("Node only works for curves objects"));
+ return;
+ }
+ const Curves *self_curves_eval = static_cast<const Curves *>(self_ob_eval->data);
+ if (self_curves_eval->surface_uv_map == nullptr || self_curves_eval->surface_uv_map[0] == '\0') {
+ pass_through_input();
+ params.error_message_add(NodeWarningType::Error, TIP_("Surface UV map not defined"));
+ return;
+ }
+ /* Take surface information from self-object. */
+ Object *surface_ob_eval = self_curves_eval->surface;
+ const StringRefNull uv_map_name = self_curves_eval->surface_uv_map;
+ const StringRefNull rest_position_name = "rest_position";
+
+ if (!curves_geometry.has_curves()) {
+ pass_through_input();
+ return;
+ }
+ if (surface_ob_eval == nullptr || surface_ob_eval->type != OB_MESH) {
+ pass_through_input();
+ params.error_message_add(NodeWarningType::Error, TIP_("Curves not attached to a surface"));
+ return;
+ }
+ Object *surface_ob_orig = DEG_get_original_object(surface_ob_eval);
+ Mesh &surface_object_data = *static_cast<Mesh *>(surface_ob_orig->data);
+
+ if (BMEditMesh *em = surface_object_data.edit_mesh) {
+ surface_mesh_orig = BKE_mesh_from_bmesh_for_eval_nomain(em->bm, nullptr, &surface_object_data);
+ free_suface_mesh_orig = true;
+ }
+ else {
+ surface_mesh_orig = &surface_object_data;
+ }
+ Mesh *surface_mesh_eval = BKE_modifier_get_evaluated_mesh_from_evaluated_object(surface_ob_eval);
+ if (surface_mesh_eval == nullptr) {
+ pass_through_input();
+ params.error_message_add(NodeWarningType::Error, TIP_("Surface has no mesh"));
+ return;
+ }
+
+ BKE_mesh_wrapper_ensure_mdata(surface_mesh_eval);
+
+ const AttributeAccessor mesh_attributes_eval = surface_mesh_eval->attributes();
+ const AttributeAccessor mesh_attributes_orig = surface_mesh_orig->attributes();
+
+ Curves &curves_id = *curves_geometry.get_curves_for_write();
+ CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry);
+
+ if (!mesh_attributes_eval.contains(uv_map_name)) {
+ pass_through_input();
+ char *message = BLI_sprintfN(TIP_("Evaluated surface missing UV map: \"%s\""),
+ uv_map_name.c_str());
+ params.error_message_add(NodeWarningType::Error, message);
+ MEM_freeN(message);
+ return;
+ }
+ if (!mesh_attributes_orig.contains(uv_map_name)) {
+ pass_through_input();
+ char *message = BLI_sprintfN(TIP_("Original surface missing UV map: \"%s\""),
+ uv_map_name.c_str());
+ params.error_message_add(NodeWarningType::Error, message);
+ MEM_freeN(message);
+ return;
+ }
+ if (!mesh_attributes_eval.contains(rest_position_name)) {
+ pass_through_input();
+ params.error_message_add(NodeWarningType::Error,
+ TIP_("Evaluated surface missing attribute: \"rest_position\""));
+ return;
+ }
+ if (curves.surface_uv_coords().is_empty() && curves.curves_num() > 0) {
+ pass_through_input();
+ params.error_message_add(NodeWarningType::Error,
+ TIP_("Curves are not attached to any UV map"));
+ return;
+ }
+ const VArraySpan<float2> uv_map_orig = mesh_attributes_orig.lookup<float2>(uv_map_name,
+ ATTR_DOMAIN_CORNER);
+ const VArraySpan<float2> uv_map_eval = mesh_attributes_eval.lookup<float2>(uv_map_name,
+ ATTR_DOMAIN_CORNER);
+ const VArraySpan<float3> rest_positions = mesh_attributes_eval.lookup<float3>(rest_position_name,
+ ATTR_DOMAIN_POINT);
+ const Span<float2> surface_uv_coords = curves.surface_uv_coords();
+
+ const Span<MLoopTri> looptris_orig{BKE_mesh_runtime_looptri_ensure(surface_mesh_orig),
+ BKE_mesh_runtime_looptri_len(surface_mesh_orig)};
+ const Span<MLoopTri> looptris_eval{BKE_mesh_runtime_looptri_ensure(surface_mesh_eval),
+ BKE_mesh_runtime_looptri_len(surface_mesh_eval)};
+ const ReverseUVSampler reverse_uv_sampler_orig{uv_map_orig, looptris_orig};
+ const ReverseUVSampler reverse_uv_sampler_eval{uv_map_eval, looptris_eval};
+
+ /* Retrieve face corner normals from each mesh. It's necessary to use face corner normals
+ * because face normals or vertex normals may lose information (custom normals, auto smooth) in
+ * some cases. It isn't yet possible to retrieve lazily calculated face corner normals from a
+ * const mesh, so they are calculated here every time. */
+ Array<float3> corner_normals_orig(surface_mesh_orig->totloop);
+ Array<float3> corner_normals_eval(surface_mesh_eval->totloop);
+ BKE_mesh_calc_normals_split_ex(
+ surface_mesh_orig, nullptr, reinterpret_cast<float(*)[3]>(corner_normals_orig.data()));
+ BKE_mesh_calc_normals_split_ex(
+ surface_mesh_eval, nullptr, reinterpret_cast<float(*)[3]>(corner_normals_eval.data()));
+
+ std::atomic<int> invalid_uv_count = 0;
+
+ const bke::CurvesSurfaceTransforms transforms{*self_ob_eval, surface_ob_eval};
+
+ bke::CurvesEditHints *edit_hints = curves_geometry.get_curve_edit_hints_for_write();
+ MutableSpan<float3> edit_hint_positions;
+ MutableSpan<float3x3> edit_hint_rotations;
+ if (edit_hints != nullptr) {
+ if (edit_hints->positions.has_value()) {
+ edit_hint_positions = *edit_hints->positions;
+ }
+ if (!edit_hints->deform_mats.has_value()) {
+ edit_hints->deform_mats.emplace(edit_hints->curves_id_orig.geometry.point_num,
+ float3x3::identity());
+ edit_hints->deform_mats->fill(float3x3::identity());
+ }
+ edit_hint_rotations = *edit_hints->deform_mats;
+ }
+
+ if (edit_hint_positions.is_empty()) {
+ deform_curves(curves,
+ *surface_mesh_orig,
+ *surface_mesh_eval,
+ surface_uv_coords,
+ reverse_uv_sampler_orig,
+ reverse_uv_sampler_eval,
+ corner_normals_orig,
+ corner_normals_eval,
+ rest_positions,
+ transforms.surface_to_curves,
+ curves.positions_for_write(),
+ edit_hint_rotations,
+ invalid_uv_count);
+ }
+ else {
+ /* First deform the actual curves in the input geometry. */
+ deform_curves(curves,
+ *surface_mesh_orig,
+ *surface_mesh_eval,
+ surface_uv_coords,
+ reverse_uv_sampler_orig,
+ reverse_uv_sampler_eval,
+ corner_normals_orig,
+ corner_normals_eval,
+ rest_positions,
+ transforms.surface_to_curves,
+ curves.positions_for_write(),
+ {},
+ invalid_uv_count);
+ /* Then also deform edit curve information for use in sculpt mode. */
+ const CurvesGeometry &curves_orig = CurvesGeometry::wrap(edit_hints->curves_id_orig.geometry);
+ deform_curves(curves_orig,
+ *surface_mesh_orig,
+ *surface_mesh_eval,
+ surface_uv_coords,
+ reverse_uv_sampler_orig,
+ reverse_uv_sampler_eval,
+ corner_normals_orig,
+ corner_normals_eval,
+ rest_positions,
+ transforms.surface_to_curves,
+ edit_hint_positions,
+ edit_hint_rotations,
+ invalid_uv_count);
+ }
+
+ curves.tag_positions_changed();
+
+ if (invalid_uv_count) {
+ char *message = BLI_sprintfN(TIP_("Invalid surface UVs on %d curves"),
+ invalid_uv_count.load());
+ params.error_message_add(NodeWarningType::Warning, message);
+ MEM_freeN(message);
+ }
+
+ params.set_output("Curves", curves_geometry);
+}
+
+} // namespace blender::nodes::node_geo_deform_curves_on_surface_cc
+
+void register_node_type_geo_deform_curves_on_surface()
+{
+ namespace file_ns = blender::nodes::node_geo_deform_curves_on_surface_cc;
+
+ static bNodeType ntype;
+ geo_node_type_base(
+ &ntype, GEO_NODE_DEFORM_CURVES_ON_SURFACE, "Deform Curves on Surface", NODE_CLASS_GEOMETRY);
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ ntype.declare = file_ns::node_declare;
+ node_type_size(&ntype, 170, 120, 700);
+ 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
index 352411dd8f5..851ca622d6b 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc
@@ -7,6 +7,7 @@
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
+#include "DNA_pointcloud_types.h"
#include "BKE_attribute_math.hh"
#include "BKE_curves.hh"
@@ -43,13 +44,13 @@ static void copy_data_based_on_map(Span<T> src, MutableSpan<T> dst, Span<int> in
* 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 bke::AttributeAccessor src_attributes,
+ bke::MutableAttributeAccessor dst_attributes,
const Span<eAttrDomain> 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);
+ GAttributeReader attribute = src_attributes.lookup(attribute_id);
if (!attribute) {
continue;
}
@@ -60,7 +61,7 @@ static void copy_attributes(const Map<AttributeIDRef, AttributeKind> &attributes
}
const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(attribute.varray.type());
- OutputAttribute result_attribute = result_component.attribute_try_get_for_output_only(
+ GSpanAttributeWriter result_attribute = dst_attributes.lookup_or_add_for_write_only_span(
attribute_id, attribute.domain, data_type);
if (!result_attribute) {
@@ -69,11 +70,11 @@ static void copy_attributes(const Map<AttributeIDRef, AttributeKind> &attributes
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>();
+ VArraySpan<T> span{attribute.varray.typed<T>()};
+ MutableSpan<T> out_span = result_attribute.span.typed<T>();
out_span.copy_from(span);
});
- result_attribute.save();
+ result_attribute.finish();
}
}
@@ -82,14 +83,14 @@ static void copy_attributes(const Map<AttributeIDRef, AttributeKind> &attributes
* 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 bke::AttributeAccessor src_attributes,
+ bke::MutableAttributeAccessor dst_attributes,
const eAttrDomain 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);
+ GAttributeReader attribute = src_attributes.lookup(attribute_id);
if (!attribute) {
continue;
}
@@ -100,7 +101,7 @@ static void copy_attributes_based_on_mask(const Map<AttributeIDRef, AttributeKin
}
const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(attribute.varray.type());
- OutputAttribute result_attribute = result_component.attribute_try_get_for_output_only(
+ GSpanAttributeWriter result_attribute = dst_attributes.lookup_or_add_for_write_only_span(
attribute_id, attribute.domain, data_type);
if (!result_attribute) {
@@ -109,23 +110,23 @@ static void copy_attributes_based_on_mask(const Map<AttributeIDRef, AttributeKin
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>();
+ VArraySpan<T> span{attribute.varray.typed<T>()};
+ MutableSpan<T> out_span = result_attribute.span.typed<T>();
copy_data_based_on_mask(span, out_span, mask);
});
- result_attribute.save();
+ result_attribute.finish();
}
}
static void copy_attributes_based_on_map(const Map<AttributeIDRef, AttributeKind> &attributes,
- const GeometryComponent &in_component,
- GeometryComponent &result_component,
+ const bke::AttributeAccessor src_attributes,
+ bke::MutableAttributeAccessor dst_attributes,
const eAttrDomain 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);
+ GAttributeReader attribute = src_attributes.lookup(attribute_id);
if (!attribute) {
continue;
}
@@ -136,7 +137,7 @@ static void copy_attributes_based_on_map(const Map<AttributeIDRef, AttributeKind
}
const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(attribute.varray.type());
- OutputAttribute result_attribute = result_component.attribute_try_get_for_output_only(
+ GSpanAttributeWriter result_attribute = dst_attributes.lookup_or_add_for_write_only_span(
attribute_id, attribute.domain, data_type);
if (!result_attribute) {
@@ -145,25 +146,26 @@ static void copy_attributes_based_on_map(const Map<AttributeIDRef, AttributeKind
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>();
+ VArraySpan<T> span{attribute.varray.typed<T>()};
+ MutableSpan<T> out_span = result_attribute.span.typed<T>();
copy_data_based_on_map(span, out_span, index_map);
});
- result_attribute.save();
+ result_attribute.finish();
}
}
static void copy_face_corner_attributes(const Map<AttributeIDRef, AttributeKind> &attributes,
- const GeometryComponent &in_component,
- GeometryComponent &out_component,
+ const bke::AttributeAccessor src_attributes,
+ bke::MutableAttributeAccessor dst_attributes,
const int selected_loops_num,
const Span<int> selected_poly_indices,
const Mesh &mesh_in)
{
+ const Span<MPoly> polys = mesh_in.polys();
Vector<int64_t> indices;
indices.reserve(selected_loops_num);
for (const int src_poly_index : selected_poly_indices) {
- const MPoly &src_poly = mesh_in.mpoly[src_poly_index];
+ const MPoly &src_poly = polys[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)) {
@@ -171,42 +173,38 @@ static void copy_face_corner_attributes(const Map<AttributeIDRef, AttributeKind>
}
}
copy_attributes_based_on_mask(
- attributes, in_component, out_component, ATTR_DOMAIN_CORNER, IndexMask(indices));
+ attributes, src_attributes, dst_attributes, ATTR_DOMAIN_CORNER, IndexMask(indices));
}
-static void copy_masked_vertices_to_new_mesh(const Mesh &src_mesh,
- Mesh &dst_mesh,
- Span<int> vertex_map)
+static void copy_masked_verts_to_new_mesh(const Mesh &src_mesh,
+ Mesh &dst_mesh,
+ Span<int> vertex_map)
{
BLI_assert(src_mesh.totvert == vertex_map.size());
+ const Span<MVert> src_verts = src_mesh.verts();
+ MutableSpan<MVert> dst_verts = dst_mesh.verts_for_write();
+
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;
+ dst_verts[i_dst] = src_verts[i_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());
+ const Span<MEdge> src_edges = src_mesh.edges();
+ MutableSpan<MEdge> dst_edges = dst_mesh.edges_for_write();
+
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;
+ dst_edges[i_dst] = src_edges[i_src];
}
}
@@ -217,14 +215,16 @@ static void copy_masked_edges_to_new_mesh(const Mesh &src_mesh,
{
BLI_assert(src_mesh.totvert == vertex_map.size());
BLI_assert(src_mesh.totedge == edge_map.size());
+ const Span<MEdge> src_edges = src_mesh.edges();
+ MutableSpan<MEdge> dst_edges = dst_mesh.edges_for_write();
+
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];
+ const MEdge &e_src = src_edges[i_src];
+ MEdge &e_dst = dst_edges[i_dst];
e_dst = e_src;
e_dst.v1 = vertex_map[e_src.v1];
@@ -239,16 +239,21 @@ static void copy_masked_polys_to_new_mesh(const Mesh &src_mesh,
Span<int> masked_poly_indices,
Span<int> new_loop_starts)
{
+ const Span<MPoly> src_polys = src_mesh.polys();
+ const Span<MLoop> src_loops = src_mesh.loops();
+ MutableSpan<MPoly> dst_polys = dst_mesh.polys_for_write();
+ MutableSpan<MLoop> dst_loops = dst_mesh.loops_for_write();
+
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 MPoly &mp_src = src_polys[i_src];
+ MPoly &mp_dst = dst_polys[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;
+ const MLoop *ml_src = &src_loops[i_ml_src];
+ MLoop *ml_dst = &dst_loops[i_ml_dst];
mp_dst = mp_src;
mp_dst.loopstart = i_ml_dst;
@@ -265,16 +270,21 @@ static void copy_masked_polys_to_new_mesh(const Mesh &src_mesh,
Span<int> masked_poly_indices,
Span<int> new_loop_starts)
{
+ const Span<MPoly> src_polys = src_mesh.polys();
+ const Span<MLoop> src_loops = src_mesh.loops();
+ MutableSpan<MPoly> dst_polys = dst_mesh.polys_for_write();
+ MutableSpan<MLoop> dst_loops = dst_mesh.loops_for_write();
+
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 MPoly &mp_src = src_polys[i_src];
+ MPoly &mp_dst = dst_polys[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;
+ const MLoop *ml_src = &src_loops[i_ml_src];
+ MLoop *ml_dst = &dst_loops[i_ml_dst];
mp_dst = mp_src;
mp_dst.loopstart = i_ml_dst;
@@ -292,16 +302,21 @@ static void copy_masked_polys_to_new_mesh(const Mesh &src_mesh,
Span<int> masked_poly_indices,
Span<int> new_loop_starts)
{
+ const Span<MPoly> src_polys = src_mesh.polys();
+ const Span<MLoop> src_loops = src_mesh.loops();
+ MutableSpan<MPoly> dst_polys = dst_mesh.polys_for_write();
+ MutableSpan<MLoop> dst_loops = dst_mesh.loops_for_write();
+
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 MPoly &mp_src = src_polys[i_src];
+ MPoly &mp_dst = dst_polys[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;
+ const MLoop *ml_src = &src_loops[i_ml_src];
+ MLoop *ml_dst = &dst_loops[i_ml_dst];
mp_dst = mp_src;
mp_dst.loopstart = i_ml_dst;
@@ -316,18 +331,19 @@ static void delete_curves_selection(GeometrySet &geometry_set,
const Field<bool> &selection_field,
const eAttrDomain selection_domain)
{
- const CurveComponent &src_component = *geometry_set.get_component_for_read<CurveComponent>();
- GeometryComponentFieldContext field_context{src_component, selection_domain};
+ const Curves &src_curves_id = *geometry_set.get_curves_for_read();
+ const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(src_curves_id.geometry);
- const int domain_num = src_component.attribute_domain_num(selection_domain);
- fn::FieldEvaluator evaluator{field_context, domain_num};
+ const int domain_size = src_curves.attributes().domain_size(selection_domain);
+ bke::CurvesFieldContext field_context{src_curves, selection_domain};
+ fn::FieldEvaluator evaluator{field_context, domain_size};
evaluator.set_selection(selection_field);
evaluator.evaluate();
const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
if (selection.is_empty()) {
return;
}
- if (selection.size() == domain_num) {
+ if (selection.size() == domain_size) {
geometry_set.remove<CurveComponent>();
return;
}
@@ -347,11 +363,10 @@ static void delete_curves_selection(GeometrySet &geometry_set,
static void separate_point_cloud_selection(GeometrySet &geometry_set,
const Field<bool> &selection_field)
{
- const PointCloudComponent &src_points =
- *geometry_set.get_component_for_read<PointCloudComponent>();
- GeometryComponentFieldContext field_context{src_points, ATTR_DOMAIN_POINT};
+ const PointCloud &src_pointcloud = *geometry_set.get_pointcloud_for_read();
- fn::FieldEvaluator evaluator{field_context, src_points.attribute_domain_num(ATTR_DOMAIN_POINT)};
+ bke::PointCloudFieldContext field_context{src_pointcloud};
+ fn::FieldEvaluator evaluator{field_context, src_pointcloud.totpoint};
evaluator.set_selection(selection_field);
evaluator.evaluate();
const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
@@ -362,14 +377,15 @@ static void separate_point_cloud_selection(GeometrySet &geometry_set,
PointCloud *pointcloud = BKE_pointcloud_new_nomain(selection.size());
- 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, selection);
+ copy_attributes_based_on_mask(attributes,
+ src_pointcloud.attributes(),
+ pointcloud->attributes_for_write(),
+ ATTR_DOMAIN_POINT,
+ selection);
geometry_set.replace_pointcloud(pointcloud);
}
@@ -377,10 +393,9 @@ static void delete_selected_instances(GeometrySet &geometry_set,
const Field<bool> &selection_field)
{
InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>();
- GeometryComponentFieldContext field_context{instances, ATTR_DOMAIN_INSTANCE};
+ bke::GeometryFieldContext field_context{instances, ATTR_DOMAIN_INSTANCE};
- fn::FieldEvaluator evaluator{field_context,
- instances.attribute_domain_num(ATTR_DOMAIN_INSTANCE)};
+ fn::FieldEvaluator evaluator{field_context, instances.instances_num()};
evaluator.set_selection(selection_field);
evaluator.evaluate();
const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
@@ -392,9 +407,9 @@ static void delete_selected_instances(GeometrySet &geometry_set,
instances.remove_instances(selection);
}
-static void compute_selected_vertices_from_vertex_selection(const Span<bool> vertex_selection,
- MutableSpan<int> r_vertex_map,
- int *r_selected_vertices_num)
+static void compute_selected_verts_from_vertex_selection(const Span<bool> vertex_selection,
+ MutableSpan<int> r_vertex_map,
+ int *r_selected_verts_num)
{
BLI_assert(vertex_selection.size() == r_vertex_map.size());
@@ -409,7 +424,7 @@ static void compute_selected_vertices_from_vertex_selection(const Span<bool> ver
}
}
- *r_selected_vertices_num = selected_verts_num;
+ *r_selected_verts_num = selected_verts_num;
}
static void compute_selected_edges_from_vertex_selection(const Mesh &mesh,
@@ -418,10 +433,11 @@ static void compute_selected_edges_from_vertex_selection(const Mesh &mesh,
int *r_selected_edges_num)
{
BLI_assert(mesh.totedge == r_edge_map.size());
+ const Span<MEdge> edges = mesh.edges();
int selected_edges_num = 0;
for (const int i : IndexRange(mesh.totedge)) {
- const MEdge &edge = mesh.medge[i];
+ const MEdge &edge = edges[i];
/* Only add the edge if both vertices will be in the new mesh. */
if (vertex_selection[edge.v1] && vertex_selection[edge.v2]) {
@@ -436,25 +452,27 @@ static void compute_selected_edges_from_vertex_selection(const Mesh &mesh,
*r_selected_edges_num = selected_edges_num;
}
-static void compute_selected_polygons_from_vertex_selection(const Mesh &mesh,
- const Span<bool> vertex_selection,
- Vector<int> &r_selected_poly_indices,
- Vector<int> &r_loop_starts,
- int *r_selected_polys_num,
- int *r_selected_loops_num)
+static void compute_selected_polys_from_vertex_selection(const Mesh &mesh,
+ const Span<bool> vertex_selection,
+ Vector<int> &r_selected_poly_indices,
+ Vector<int> &r_loop_starts,
+ int *r_selected_polys_num,
+ int *r_selected_loops_num)
{
BLI_assert(mesh.totvert == vertex_selection.size());
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
r_selected_poly_indices.reserve(mesh.totpoly);
r_loop_starts.reserve(mesh.totloop);
int selected_loops_num = 0;
- for (const int i : IndexRange(mesh.totpoly)) {
- const MPoly &poly_src = mesh.mpoly[i];
+ for (const int i : polys.index_range()) {
+ const MPoly &poly_src = polys[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) {
+ const Span<MLoop> poly_loops = loops.slice(poly_src.loopstart, poly_src.totloop);
+ for (const MLoop &loop : poly_loops) {
if (!vertex_selection[loop.v]) {
all_verts_in_selection = false;
break;
@@ -476,20 +494,20 @@ static void compute_selected_polygons_from_vertex_selection(const Mesh &mesh,
* 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,
- MutableSpan<int> r_vertex_map,
- MutableSpan<int> r_edge_map,
- int *r_selected_vertices_num,
- int *r_selected_edges_num)
+static void compute_selected_verts_and_edges_from_edge_selection(const Mesh &mesh,
+ const Span<bool> edge_selection,
+ MutableSpan<int> r_vertex_map,
+ MutableSpan<int> r_edge_map,
+ int *r_selected_verts_num,
+ int *r_selected_edges_num)
{
BLI_assert(mesh.totedge == edge_selection.size());
+ const Span<MEdge> edges = mesh.edges();
int selected_edges_num = 0;
int selected_verts_num = 0;
for (const int i : IndexRange(mesh.totedge)) {
- const MEdge &edge = mesh.medge[i];
+ const MEdge &edge = edges[i];
if (edge_selection[i]) {
r_edge_map[i] = selected_edges_num;
selected_edges_num++;
@@ -507,7 +525,7 @@ static void compute_selected_vertices_and_edges_from_edge_selection(
}
}
- *r_selected_vertices_num = selected_verts_num;
+ *r_selected_verts_num = selected_verts_num;
*r_selected_edges_num = selected_edges_num;
}
@@ -539,23 +557,26 @@ static void compute_selected_edges_from_edge_selection(const Mesh &mesh,
* 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,
- Vector<int> &r_selected_poly_indices,
- Vector<int> &r_loop_starts,
- int *r_selected_polys_num,
- int *r_selected_loops_num)
+static void compute_selected_polys_from_edge_selection(const Mesh &mesh,
+ const Span<bool> edge_selection,
+ Vector<int> &r_selected_poly_indices,
+ Vector<int> &r_loop_starts,
+ int *r_selected_polys_num,
+ int *r_selected_loops_num)
{
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
+
r_selected_poly_indices.reserve(mesh.totpoly);
r_loop_starts.reserve(mesh.totloop);
int selected_loops_num = 0;
- for (const int i : IndexRange(mesh.totpoly)) {
- const MPoly &poly_src = mesh.mpoly[i];
+ for (const int i : polys.index_range()) {
+ const MPoly &poly_src = polys[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) {
+ const Span<MLoop> poly_loops = loops.slice(poly_src.loopstart, poly_src.totloop);
+ for (const MLoop &loop : poly_loops) {
if (!edge_selection[loop.e]) {
all_edges_in_selection = false;
break;
@@ -590,12 +611,12 @@ static void compute_selected_mesh_data_from_vertex_selection_edge_face(
compute_selected_edges_from_vertex_selection(
mesh, vertex_selection, r_edge_map, r_selected_edges_num);
- compute_selected_polygons_from_vertex_selection(mesh,
- vertex_selection,
- r_selected_poly_indices,
- r_loop_starts,
- r_selected_polys_num,
- r_selected_loops_num);
+ compute_selected_polys_from_vertex_selection(mesh,
+ vertex_selection,
+ r_selected_poly_indices,
+ r_loop_starts,
+ r_selected_polys_num,
+ r_selected_loops_num);
}
/**
@@ -608,23 +629,23 @@ static void compute_selected_mesh_data_from_vertex_selection(const Mesh &mesh,
MutableSpan<int> r_edge_map,
Vector<int> &r_selected_poly_indices,
Vector<int> &r_loop_starts,
- int *r_selected_vertices_num,
+ int *r_selected_verts_num,
int *r_selected_edges_num,
int *r_selected_polys_num,
int *r_selected_loops_num)
{
- compute_selected_vertices_from_vertex_selection(
- vertex_selection, r_vertex_map, r_selected_vertices_num);
+ compute_selected_verts_from_vertex_selection(
+ vertex_selection, r_vertex_map, r_selected_verts_num);
compute_selected_edges_from_vertex_selection(
mesh, vertex_selection, r_edge_map, r_selected_edges_num);
- compute_selected_polygons_from_vertex_selection(mesh,
- vertex_selection,
- r_selected_poly_indices,
- r_loop_starts,
- r_selected_polys_num,
- r_selected_loops_num);
+ compute_selected_polys_from_vertex_selection(mesh,
+ vertex_selection,
+ r_selected_poly_indices,
+ r_loop_starts,
+ r_selected_polys_num,
+ r_selected_loops_num);
}
/**
@@ -643,17 +664,17 @@ static void compute_selected_mesh_data_from_edge_selection_edge_face(
{
compute_selected_edges_from_edge_selection(
mesh, edge_selection, r_edge_map, r_selected_edges_num);
- compute_selected_polygons_from_edge_selection(mesh,
- edge_selection,
- r_selected_poly_indices,
- r_loop_starts,
- r_selected_polys_num,
- r_selected_loops_num);
+ compute_selected_polys_from_edge_selection(mesh,
+ edge_selection,
+ r_selected_poly_indices,
+ r_loop_starts,
+ r_selected_polys_num,
+ r_selected_loops_num);
}
/**
* 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.
+ * that edge are kept as well. The polys 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,
@@ -661,44 +682,41 @@ static void compute_selected_mesh_data_from_edge_selection(const Mesh &mesh,
MutableSpan<int> r_edge_map,
Vector<int> &r_selected_poly_indices,
Vector<int> &r_loop_starts,
- int *r_selected_vertices_num,
+ int *r_selected_verts_num,
int *r_selected_edges_num,
int *r_selected_polys_num,
int *r_selected_loops_num)
{
r_vertex_map.fill(-1);
- compute_selected_vertices_and_edges_from_edge_selection(mesh,
- edge_selection,
- r_vertex_map,
- r_edge_map,
- r_selected_vertices_num,
- r_selected_edges_num);
- compute_selected_polygons_from_edge_selection(mesh,
- edge_selection,
- r_selected_poly_indices,
- r_loop_starts,
- r_selected_polys_num,
- r_selected_loops_num);
+ compute_selected_verts_and_edges_from_edge_selection(
+ mesh, edge_selection, r_vertex_map, r_edge_map, r_selected_verts_num, r_selected_edges_num);
+ compute_selected_polys_from_edge_selection(mesh,
+ edge_selection,
+ r_selected_poly_indices,
+ r_loop_starts,
+ r_selected_polys_num,
+ r_selected_loops_num);
}
/**
* 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,
- Vector<int> &r_selected_poly_indices,
- Vector<int> &r_loop_starts,
- int *r_selected_polys_num,
- int *r_selected_loops_num)
+static void compute_selected_polys_from_poly_selection(const Mesh &mesh,
+ const Span<bool> poly_selection,
+ Vector<int> &r_selected_poly_indices,
+ Vector<int> &r_loop_starts,
+ int *r_selected_polys_num,
+ int *r_selected_loops_num)
{
BLI_assert(mesh.totpoly == poly_selection.size());
+ const Span<MPoly> polys = mesh.polys();
r_selected_poly_indices.reserve(mesh.totpoly);
r_loop_starts.reserve(mesh.totloop);
int selected_loops_num = 0;
- for (const int i : IndexRange(mesh.totpoly)) {
- const MPoly &poly_src = mesh.mpoly[i];
+ for (const int i : polys.index_range()) {
+ const MPoly &poly_src = polys[i];
/* We keep this one. */
if (poly_selection[i]) {
r_selected_poly_indices.append_unchecked(i);
@@ -725,6 +743,9 @@ static void compute_selected_mesh_data_from_poly_selection_edge_face(
{
BLI_assert(mesh.totpoly == poly_selection.size());
BLI_assert(mesh.totedge == r_edge_map.size());
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
+
r_edge_map.fill(-1);
r_selected_poly_indices.reserve(mesh.totpoly);
@@ -732,8 +753,8 @@ static void compute_selected_mesh_data_from_poly_selection_edge_face(
int selected_loops_num = 0;
int selected_edges_num = 0;
- for (const int i : IndexRange(mesh.totpoly)) {
- const MPoly &poly_src = mesh.mpoly[i];
+ for (const int i : polys.index_range()) {
+ const MPoly &poly_src = polys[i];
/* We keep this one. */
if (poly_selection[i]) {
r_selected_poly_indices.append_unchecked(i);
@@ -741,8 +762,8 @@ static void compute_selected_mesh_data_from_poly_selection_edge_face(
selected_loops_num += 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) {
+ const Span<MLoop> poly_loops = loops.slice(poly_src.loopstart, poly_src.totloop);
+ for (const MLoop &loop : poly_loops) {
/* Check first if it has not yet been added. */
if (r_edge_map[loop.e] == -1) {
r_edge_map[loop.e] = selected_edges_num;
@@ -766,13 +787,16 @@ static void compute_selected_mesh_data_from_poly_selection(const Mesh &mesh,
MutableSpan<int> r_edge_map,
Vector<int> &r_selected_poly_indices,
Vector<int> &r_loop_starts,
- int *r_selected_vertices_num,
+ int *r_selected_verts_num,
int *r_selected_edges_num,
int *r_selected_polys_num,
int *r_selected_loops_num)
{
BLI_assert(mesh.totpoly == poly_selection.size());
BLI_assert(mesh.totedge == r_edge_map.size());
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
+
r_vertex_map.fill(-1);
r_edge_map.fill(-1);
@@ -782,8 +806,8 @@ static void compute_selected_mesh_data_from_poly_selection(const Mesh &mesh,
int selected_loops_num = 0;
int selected_verts_num = 0;
int selected_edges_num = 0;
- for (const int i : IndexRange(mesh.totpoly)) {
- const MPoly &poly_src = mesh.mpoly[i];
+ for (const int i : polys.index_range()) {
+ const MPoly &poly_src = polys[i];
/* We keep this one. */
if (poly_selection[i]) {
r_selected_poly_indices.append_unchecked(i);
@@ -791,8 +815,8 @@ static void compute_selected_mesh_data_from_poly_selection(const Mesh &mesh,
selected_loops_num += 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) {
+ const Span<MLoop> poly_loops = loops.slice(poly_src.loopstart, poly_src.totloop);
+ for (const MLoop &loop : poly_loops) {
/* Check first if it has not yet been added. */
if (r_vertex_map[loop.v] == -1) {
r_vertex_map[loop.v] = selected_verts_num;
@@ -805,7 +829,7 @@ static void compute_selected_mesh_data_from_poly_selection(const Mesh &mesh,
}
}
}
- *r_selected_vertices_num = selected_verts_num;
+ *r_selected_verts_num = selected_verts_num;
*r_selected_edges_num = selected_edges_num;
*r_selected_polys_num = r_selected_poly_indices.size();
*r_selected_loops_num = selected_loops_num;
@@ -815,7 +839,7 @@ static void compute_selected_mesh_data_from_poly_selection(const Mesh &mesh,
* Keep the parts of the mesh that are in the selection.
*/
static void do_mesh_separation(GeometrySet &geometry_set,
- const MeshComponent &in_component,
+ const Mesh &mesh_in,
const Span<bool> selection,
const eAttrDomain domain,
const GeometryNodeDeleteGeometryMode mode)
@@ -826,9 +850,7 @@ static void do_mesh_separation(GeometrySet &geometry_set,
int selected_polys_num = 0;
int selected_loops_num = 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(
@@ -890,27 +912,32 @@ static void do_mesh_separation(GeometrySet &geometry_set,
0,
selected_loops_num,
selected_polys_num);
- 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_verts_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_map(attributes,
+ mesh_in.attributes(),
+ mesh_out->attributes_for_write(),
+ ATTR_DOMAIN_POINT,
+ vertex_map);
+ copy_attributes_based_on_map(attributes,
+ mesh_in.attributes(),
+ mesh_out->attributes_for_write(),
+ ATTR_DOMAIN_EDGE,
+ edge_map);
copy_attributes_based_on_mask(attributes,
- in_component,
- out_component,
+ mesh_in.attributes(),
+ mesh_out->attributes_for_write(),
ATTR_DOMAIN_FACE,
IndexMask(Vector<int64_t>(selected_poly_indices.as_span())));
copy_face_corner_attributes(attributes,
- in_component,
- out_component,
+ mesh_in.attributes(),
+ mesh_out->attributes_for_write(),
selected_loops_num,
selected_poly_indices,
mesh_in);
@@ -962,26 +989,29 @@ static void do_mesh_separation(GeometrySet &geometry_set,
0,
selected_loops_num,
selected_polys_num);
- 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));
+ mesh_out->verts_for_write().copy_from(mesh_in.verts());
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(
+ attributes, mesh_in.attributes(), mesh_out->attributes_for_write(), {ATTR_DOMAIN_POINT});
+ copy_attributes_based_on_map(attributes,
+ mesh_in.attributes(),
+ mesh_out->attributes_for_write(),
+ ATTR_DOMAIN_EDGE,
+ edge_map);
copy_attributes_based_on_mask(attributes,
- in_component,
- out_component,
+ mesh_in.attributes(),
+ mesh_out->attributes_for_write(),
ATTR_DOMAIN_FACE,
IndexMask(Vector<int64_t>(selected_poly_indices.as_span())));
copy_face_corner_attributes(attributes,
- in_component,
- out_component,
+ mesh_in.attributes(),
+ mesh_out->attributes_for_write(),
selected_loops_num,
selected_poly_indices,
mesh_in);
@@ -991,28 +1021,28 @@ static void do_mesh_separation(GeometrySet &geometry_set,
/* Fill all the maps based on the selection. */
switch (domain) {
case ATTR_DOMAIN_POINT:
- compute_selected_polygons_from_vertex_selection(mesh_in,
- selection,
- selected_poly_indices,
- new_loop_starts,
- &selected_polys_num,
- &selected_loops_num);
+ compute_selected_polys_from_vertex_selection(mesh_in,
+ selection,
+ selected_poly_indices,
+ new_loop_starts,
+ &selected_polys_num,
+ &selected_loops_num);
break;
case ATTR_DOMAIN_EDGE:
- compute_selected_polygons_from_edge_selection(mesh_in,
- selection,
- selected_poly_indices,
- new_loop_starts,
- &selected_polys_num,
- &selected_loops_num);
+ compute_selected_polys_from_edge_selection(mesh_in,
+ selection,
+ selected_poly_indices,
+ new_loop_starts,
+ &selected_polys_num,
+ &selected_loops_num);
break;
case ATTR_DOMAIN_FACE:
- compute_selected_polygons_from_poly_selection(mesh_in,
- selection,
- selected_poly_indices,
- new_loop_starts,
- &selected_polys_num,
- &selected_loops_num);
+ compute_selected_polys_from_poly_selection(mesh_in,
+ selection,
+ selected_poly_indices,
+ new_loop_starts,
+ &selected_polys_num,
+ &selected_loops_num);
break;
default:
BLI_assert_unreachable();
@@ -1020,24 +1050,25 @@ static void do_mesh_separation(GeometrySet &geometry_set,
}
mesh_out = BKE_mesh_new_nomain_from_template(
&mesh_in, mesh_in.totvert, mesh_in.totedge, 0, selected_loops_num, selected_polys_num);
- 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));
+ mesh_out->verts_for_write().copy_from(mesh_in.verts());
+ mesh_out->edges_for_write().copy_from(mesh_in.edges());
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(attributes,
+ mesh_in.attributes(),
+ mesh_out->attributes_for_write(),
+ {ATTR_DOMAIN_POINT, ATTR_DOMAIN_EDGE});
copy_attributes_based_on_mask(attributes,
- in_component,
- out_component,
+ mesh_in.attributes(),
+ mesh_out->attributes_for_write(),
ATTR_DOMAIN_FACE,
IndexMask(Vector<int64_t>(selected_poly_indices.as_span())));
copy_face_corner_attributes(attributes,
- in_component,
- out_component,
+ mesh_in.attributes(),
+ mesh_out->attributes_for_write(),
selected_loops_num,
selected_poly_indices,
mesh_in);
@@ -1054,22 +1085,20 @@ static void separate_mesh_selection(GeometrySet &geometry_set,
const eAttrDomain selection_domain,
const GeometryNodeDeleteGeometryMode mode)
{
- const MeshComponent &src_component = *geometry_set.get_component_for_read<MeshComponent>();
- GeometryComponentFieldContext field_context{src_component, selection_domain};
-
- fn::FieldEvaluator evaluator{field_context,
- src_component.attribute_domain_num(selection_domain)};
+ const Mesh &src_mesh = *geometry_set.get_mesh_for_read();
+ bke::MeshFieldContext field_context{src_mesh, selection_domain};
+ fn::FieldEvaluator evaluator{field_context, src_mesh.attributes().domain_size(selection_domain)};
evaluator.add(selection_field);
evaluator.evaluate();
const VArray<bool> selection = evaluator.get_evaluated<bool>(0);
/* Check if there is anything to delete. */
- if (selection.is_single() && selection.get_internal_single()) {
+ if (selection.is_empty() || (selection.is_single() && selection.get_internal_single())) {
return;
}
- const VArray_Span<bool> selection_span{selection};
+ const VArraySpan<bool> selection_span{selection};
- do_mesh_separation(geometry_set, src_component, selection_span, selection_domain, mode);
+ do_mesh_separation(geometry_set, src_mesh, selection_span, selection_domain, mode);
}
} // namespace blender::nodes::node_geo_delete_geometry_cc
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 f95601813a3..b84ee33e26f 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
@@ -105,6 +105,8 @@ static void sample_mesh_surface(const Mesh &mesh,
Vector<float3> &r_bary_coords,
Vector<int> &r_looptri_indices)
{
+ const Span<MVert> verts = mesh.verts();
+ const Span<MLoop> loops = mesh.loops();
const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&mesh),
BKE_mesh_runtime_looptri_len(&mesh)};
@@ -113,12 +115,12 @@ static void sample_mesh_surface(const Mesh &mesh,
const int v0_loop = looptri.tri[0];
const int v1_loop = looptri.tri[1];
const int v2_loop = looptri.tri[2];
- const int v0_index = mesh.mloop[v0_loop].v;
- const int v1_index = mesh.mloop[v1_loop].v;
- const int v2_index = mesh.mloop[v2_loop].v;
- const float3 v0_pos = float3(mesh.mvert[v0_index].co);
- const float3 v1_pos = float3(mesh.mvert[v1_index].co);
- const float3 v2_pos = float3(mesh.mvert[v2_index].co);
+ const int v0_index = loops[v0_loop].v;
+ const int v1_index = loops[v1_loop].v;
+ const int v2_index = loops[v2_loop].v;
+ const float3 v0_pos = verts[v0_index].co;
+ const float3 v1_pos = verts[v1_index].co;
+ const float3 v2_pos = verts[v2_index].co;
float looptri_density_factor = 1.0f;
if (!density_factors.is_empty()) {
@@ -220,11 +222,11 @@ BLI_NOINLINE static void update_elimination_mask_based_on_density_factors(
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]);
- const float probablity = v0_density_factor * bary_coord.x + v1_density_factor * bary_coord.y +
- v2_density_factor * bary_coord.z;
+ const float probability = v0_density_factor * bary_coord.x + v1_density_factor * bary_coord.y +
+ v2_density_factor * bary_coord.z;
const float hash = noise::hash_float_to_float(bary_coord);
- if (hash > probablity) {
+ if (hash > probability) {
elimination_mask[i] = true;
}
}
@@ -283,38 +285,38 @@ BLI_NOINLINE static void interpolate_attribute(const Mesh &mesh,
}
BLI_NOINLINE static void propagate_existing_attributes(
- const MeshComponent &mesh_component,
+ const Mesh &mesh,
const Map<AttributeIDRef, AttributeKind> &attributes,
- GeometryComponent &point_component,
+ PointCloud &points,
const Span<float3> bary_coords,
const Span<int> looptri_indices)
{
- const Mesh &mesh = *mesh_component.get_for_read();
+ const AttributeAccessor mesh_attributes = mesh.attributes();
+ MutableAttributeAccessor point_attributes = points.attributes_for_write();
for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
const AttributeIDRef attribute_id = entry.key;
const eCustomDataType output_data_type = entry.value.data_type;
- ReadAttributeLookup source_attribute = mesh_component.attribute_try_get_for_read(attribute_id);
+ GAttributeReader source_attribute = mesh_attributes.lookup(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(
+ GSpanAttributeWriter attribute_out = point_attributes.lookup_or_add_for_write_only_span(
attribute_id, ATTR_DOMAIN_POINT, output_data_type);
if (!attribute_out) {
continue;
}
- GMutableSpan out_span = attribute_out.as_span();
interpolate_attribute(mesh,
bary_coords,
looptri_indices,
source_attribute.domain,
source_attribute.varray,
- out_span);
- attribute_out.save();
+ attribute_out.span);
+ attribute_out.finish();
}
}
@@ -325,34 +327,31 @@ struct AttributeOutputs {
};
} // namespace
-BLI_NOINLINE static void compute_attribute_outputs(const MeshComponent &mesh_component,
- PointCloudComponent &point_component,
+BLI_NOINLINE static void compute_attribute_outputs(const Mesh &mesh,
+ PointCloud &points,
const Span<float3> bary_coords,
const Span<int> looptri_indices,
const AttributeOutputs &attribute_outputs)
{
- 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();
+ MutableAttributeAccessor point_attributes = points.attributes_for_write();
- OutputAttribute_Typed<float3> normal_attribute;
- OutputAttribute_Typed<float3> rotation_attribute;
+ SpanAttributeWriter<int> ids = point_attributes.lookup_or_add_for_write_only_span<int>(
+ "id", ATTR_DOMAIN_POINT);
- MutableSpan<float3> normals;
- MutableSpan<float3> rotations;
+ SpanAttributeWriter<float3> normals;
+ SpanAttributeWriter<float3> rotations;
if (attribute_outputs.normal_id) {
- normal_attribute = point_component.attribute_try_get_for_output_only<float3>(
+ normals = point_attributes.lookup_or_add_for_write_only_span<float3>(
attribute_outputs.normal_id.get(), ATTR_DOMAIN_POINT);
- normals = normal_attribute.as_span();
}
if (attribute_outputs.rotation_id) {
- rotation_attribute = point_component.attribute_try_get_for_output_only<float3>(
+ rotations = point_attributes.lookup_or_add_for_write_only_span<float3>(
attribute_outputs.rotation_id.get(), ATTR_DOMAIN_POINT);
- rotations = rotation_attribute.as_span();
}
- const Mesh &mesh = *mesh_component.get_for_read();
+ const Span<MVert> verts = mesh.verts();
+ const Span<MLoop> loops = mesh.loops();
const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&mesh),
BKE_mesh_runtime_looptri_len(&mesh)};
@@ -361,55 +360,54 @@ BLI_NOINLINE static void compute_attribute_outputs(const MeshComponent &mesh_com
const MLoopTri &looptri = looptris[looptri_index];
const float3 &bary_coord = bary_coords[i];
- const int v0_index = mesh.mloop[looptri.tri[0]].v;
- const int v1_index = mesh.mloop[looptri.tri[1]].v;
- const int v2_index = mesh.mloop[looptri.tri[2]].v;
- const float3 v0_pos = float3(mesh.mvert[v0_index].co);
- const float3 v1_pos = float3(mesh.mvert[v1_index].co);
- const float3 v2_pos = float3(mesh.mvert[v2_index].co);
+ const int v0_index = loops[looptri.tri[0]].v;
+ const int v1_index = loops[looptri.tri[1]].v;
+ const int v2_index = loops[looptri.tri[2]].v;
+ const float3 v0_pos = verts[v0_index].co;
+ const float3 v1_pos = verts[v1_index].co;
+ const float3 v2_pos = verts[v2_index].co;
- ids[i] = noise::hash(noise::hash_float(bary_coord), looptri_index);
+ ids.span[i] = noise::hash(noise::hash_float(bary_coord), looptri_index);
float3 normal;
- if (!normals.is_empty() || !rotations.is_empty()) {
+ if (!normals.span.is_empty() || !rotations.span.is_empty()) {
normal_tri_v3(normal, v0_pos, v1_pos, v2_pos);
}
- if (!normals.is_empty()) {
- normals[i] = normal;
+ if (!normals.span.is_empty()) {
+ normals.span[i] = normal;
}
- if (!rotations.is_empty()) {
- rotations[i] = normal_to_euler_rotation(normal);
+ if (!rotations.span.is_empty()) {
+ rotations.span[i] = normal_to_euler_rotation(normal);
}
}
- id_attribute.save();
+ ids.finish();
- if (normal_attribute) {
- normal_attribute.save();
+ if (normals) {
+ normals.finish();
}
- if (rotation_attribute) {
- rotation_attribute.save();
+ if (rotations) {
+ rotations.finish();
}
}
-static Array<float> calc_full_density_factors_with_selection(const MeshComponent &component,
+static Array<float> calc_full_density_factors_with_selection(const Mesh &mesh,
const Field<float> &density_field,
const Field<bool> &selection_field)
{
- const eAttrDomain attribute_domain = ATTR_DOMAIN_CORNER;
- GeometryComponentFieldContext field_context{component, attribute_domain};
- const int domain_num = component.attribute_domain_num(attribute_domain);
+ const eAttrDomain domain = ATTR_DOMAIN_CORNER;
+ const int domain_size = mesh.attributes().domain_size(domain);
+ Array<float> densities(domain_size, 0.0f);
- Array<float> densities(domain_num, 0.0f);
-
- fn::FieldEvaluator evaluator{field_context, domain_num};
+ bke::MeshFieldContext field_context{mesh, domain};
+ 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;
}
-static void distribute_points_random(const MeshComponent &component,
+static void distribute_points_random(const Mesh &mesh,
const Field<float> &density_field,
const Field<bool> &selection_field,
const int seed,
@@ -418,12 +416,11 @@ static void distribute_points_random(const MeshComponent &component,
Vector<int> &looptri_indices)
{
const Array<float> densities = calc_full_density_factors_with_selection(
- component, density_field, selection_field);
- const Mesh &mesh = *component.get_for_read();
+ mesh, density_field, selection_field);
sample_mesh_surface(mesh, 1.0f, densities, seed, positions, bary_coords, looptri_indices);
}
-static void distribute_points_poisson_disk(const MeshComponent &mesh_component,
+static void distribute_points_poisson_disk(const Mesh &mesh,
const float minimum_distance,
const float max_density,
const Field<float> &density_factor_field,
@@ -433,14 +430,13 @@ static void distribute_points_poisson_disk(const MeshComponent &mesh_component,
Vector<float3> &bary_coords,
Vector<int> &looptri_indices)
{
- const Mesh &mesh = *mesh_component.get_for_read();
sample_mesh_surface(mesh, max_density, {}, seed, positions, bary_coords, looptri_indices);
Array<bool> elimination_mask(positions.size(), false);
update_elimination_mask_for_close_points(positions, minimum_distance, elimination_mask);
const Array<float> density_factors = calc_full_density_factors_with_selection(
- mesh_component, density_factor_field, selection_field);
+ mesh, density_factor_field, selection_field);
update_elimination_mask_based_on_density_factors(
mesh, density_factors, bary_coords, looptri_indices, elimination_mask.as_mutable_span());
@@ -460,7 +456,7 @@ static void point_distribution_calculate(GeometrySet &geometry_set,
return;
}
- const MeshComponent &mesh_component = *geometry_set.get_component_for_read<MeshComponent>();
+ const Mesh &mesh = *geometry_set.get_mesh_for_read();
Vector<float3> positions;
Vector<float3> bary_coords;
@@ -469,20 +465,15 @@ static void point_distribution_calculate(GeometrySet &geometry_set,
switch (method) {
case GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_RANDOM: {
const Field<float> density_field = params.get_input<Field<float>>("Density");
- distribute_points_random(mesh_component,
- density_field,
- selection_field,
- seed,
- positions,
- bary_coords,
- looptri_indices);
+ distribute_points_random(
+ mesh, density_field, selection_field, seed, positions, bary_coords, looptri_indices);
break;
}
case GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_POISSON: {
const float minimum_distance = params.get_input<float>("Distance Min");
const float density_max = params.get_input<float>("Density Max");
const Field<float> density_factors_field = params.get_input<Field<float>>("Density Factor");
- distribute_points_poisson_disk(mesh_component,
+ distribute_points_poisson_disk(mesh,
minimum_distance,
density_max,
density_factors_field,
@@ -500,12 +491,17 @@ static void point_distribution_calculate(GeometrySet &geometry_set,
}
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);
- geometry_set.replace_pointcloud(pointcloud);
+ bke::MutableAttributeAccessor point_attributes = pointcloud->attributes_for_write();
+ bke::SpanAttributeWriter<float3> point_positions =
+ point_attributes.lookup_or_add_for_write_only_span<float3>("position", ATTR_DOMAIN_POINT);
+ bke::SpanAttributeWriter<float> point_radii =
+ point_attributes.lookup_or_add_for_write_only_span<float>("radius", ATTR_DOMAIN_POINT);
+ point_positions.span.copy_from(positions);
+ point_radii.span.fill(0.05f);
+ point_positions.finish();
+ point_radii.finish();
- PointCloudComponent &point_component =
- geometry_set.get_component_for_write<PointCloudComponent>();
+ geometry_set.replace_pointcloud(pointcloud);
Map<AttributeIDRef, AttributeKind> attributes;
geometry_set.gather_attributes_for_propagation(
@@ -514,11 +510,9 @@ static void point_distribution_calculate(GeometrySet &geometry_set,
/* Position is set separately. */
attributes.remove("position");
- propagate_existing_attributes(
- mesh_component, attributes, point_component, bary_coords, looptri_indices);
+ propagate_existing_attributes(mesh, attributes, *pointcloud, bary_coords, looptri_indices);
- compute_attribute_outputs(
- mesh_component, point_component, bary_coords, looptri_indices, attribute_outputs);
+ compute_attribute_outputs(mesh, *pointcloud, bary_coords, looptri_indices, attribute_outputs);
}
static void node_geo_exec(GeoNodeExecParams params)
@@ -544,7 +538,7 @@ static void node_geo_exec(GeoNodeExecParams params)
geometry_set, selection_field, method, seed, attribute_outputs, params);
/* Keep instances because the original geometry set may contain instances that are processed as
* well. */
- geometry_set.keep_only({GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_INSTANCES});
+ geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_POINT_CLOUD});
});
params.set_output("Points", std::move(geometry_set));
diff --git a/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc
index 52156b59c51..84e63845b84 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc
@@ -143,12 +143,12 @@ static void transfer_attributes(
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)
+ const AttributeAccessor src_attributes,
+ MutableAttributeAccessor dst_attributes)
{
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);
+ GAttributeReader src_attribute = src_attributes.lookup(attribute_id);
if (!src_attribute) {
continue;
}
@@ -166,7 +166,7 @@ static void transfer_attributes(
}
const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(
src_attribute.varray.type());
- OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
+ GSpanAttributeWriter dst_attribute = dst_attributes.lookup_or_add_for_write_only_span(
attribute_id, out_domain, data_type);
if (!dst_attribute) {
@@ -175,8 +175,8 @@ static void transfer_attributes(
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>();
+ VArraySpan<T> span{src_attribute.varray.typed<T>()};
+ MutableSpan<T> dst_span = dst_attribute.span.typed<T>();
if (src_attribute.domain == ATTR_DOMAIN_FACE) {
dst_span.take_front(span.size()).copy_from(span);
if (keep_boundaries) {
@@ -193,7 +193,7 @@ static void transfer_attributes(
copy_data_based_on_new_to_old_map(span, dst_span, new_to_old_face_corners_map);
}
});
- dst_attribute.save();
+ dst_attribute.finish();
}
}
@@ -209,13 +209,18 @@ static void calc_boundaries(const Mesh &mesh,
{
BLI_assert(r_vertex_types.size() == mesh.totvert);
BLI_assert(r_edge_types.size() == mesh.totedge);
+ const Span<MEdge> edges = mesh.edges();
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
+
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)) {
+ const MPoly &poly = polys[i];
+ const Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop);
+ for (const MLoop &loop : poly_loops) {
r_edge_types[loop.e] = get_edge_type_with_added_neighbor(r_edge_types[loop.e]);
}
}
@@ -226,7 +231,7 @@ static void calc_boundaries(const Mesh &mesh,
if (edge_type == EdgeType::Loose) {
continue;
}
- const MEdge &edge = mesh.medge[i];
+ const MEdge &edge = edges[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]);
@@ -241,7 +246,7 @@ static void calc_boundaries(const Mesh &mesh,
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];
+ const MEdge &edge = edges[i];
if (r_vertex_types[edge.v1] == VertexType::Loose) {
r_vertex_types[edge.v1] = VertexType::Normal;
}
@@ -258,9 +263,12 @@ static void calc_boundaries(const Mesh &mesh,
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)) {
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
+ for (const int i : polys.index_range()) {
+ const MPoly &poly = polys[i];
+ const Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop);
+ for (const MLoop &loop : poly_loops) {
r_vertex_poly_indices[loop.v].append(i);
}
}
@@ -321,26 +329,28 @@ static void create_vertex_poly_map(const Mesh &mesh,
* - Finally if we are in the normal case we also need to add the last "shared edge" to close the
* loop.
*/
-static bool sort_vertex_polys(const Mesh &mesh,
+static bool sort_vertex_polys(const Span<MEdge> edges,
+ const Span<MPoly> polys,
+ const Span<MLoop> loops,
const int vertex_index,
const bool boundary_vertex,
const Span<EdgeType> edge_types,
- MutableSpan<int> connected_polygons,
+ MutableSpan<int> connected_polys,
MutableSpan<int> r_shared_edges,
MutableSpan<int> r_sorted_corners)
{
- if (connected_polygons.size() <= 2 && (!boundary_vertex || connected_polygons.size() == 0)) {
+ if (connected_polys.size() <= 2 && (!boundary_vertex || connected_polys.size() == 0)) {
return true;
}
/* 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]];
+ Array<std::pair<int, int>> poly_vertex_corners(connected_polys.size());
+ for (const int i : connected_polys.index_range()) {
+ const MPoly &poly = polys[connected_polys[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) {
+ const MLoop &loop = loops[loop_index];
+ if (edges[loop.e].v1 == vertex_index || edges[loop.e].v2 == vertex_index) {
if (!first_edge_done) {
poly_vertex_corners[i].first = loop_index;
first_edge_done = true;
@@ -359,20 +369,20 @@ static bool sort_vertex_polys(const Mesh &mesh,
* 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];
+ for (const int i : connected_polys.index_range()) {
+ const MLoop &first_loop = loops[poly_vertex_corners[i].first];
+ const MLoop &second_loop = loops[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(connected_polys[i], connected_polys[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(connected_polys[i], connected_polys[0]);
std::swap(poly_vertex_corners[i], poly_vertex_corners[0]);
break;
}
@@ -380,20 +390,20 @@ static bool sort_vertex_polys(const Mesh &mesh,
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];
+ for (const int i : connected_polys.index_range()) {
+ const MLoop &first_loop = loops[poly_vertex_corners[i].first];
+ const MLoop &second_loop = loops[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(connected_polys[i], connected_polys[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(connected_polys[i], connected_polys[0]);
std::swap(poly_vertex_corners[i], poly_vertex_corners[0]);
break;
}
@@ -402,8 +412,8 @@ static bool sort_vertex_polys(const Mesh &mesh,
}
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];
+ const MLoop &first_loop = loops[poly_vertex_corners[0].first];
+ const MLoop &second_loop = loops[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;
@@ -415,14 +425,14 @@ static bool sort_vertex_polys(const Mesh &mesh,
}
BLI_assert(shared_edge_i != -1);
- for (const int i : IndexRange(connected_polygons.size() - 1)) {
+ for (const int i : IndexRange(connected_polys.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];
+ for (; j < connected_polys.size(); ++j) {
+ const MLoop &first_loop = loops[poly_vertex_corners[j].first];
+ const MLoop &second_loop = loops[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;
@@ -434,13 +444,13 @@ static bool sort_vertex_polys(const Mesh &mesh,
break;
}
}
- if (j == connected_polygons.size()) {
+ if (j == connected_polys.size()) {
/* The vertex is not manifold because the polygons around the vertex don't form a loop, and
* hence can't be sorted. */
return false;
}
- std::swap(connected_polygons[i + 1], connected_polygons[j]);
+ std::swap(connected_polys[i + 1], connected_polys[j]);
std::swap(poly_vertex_corners[i + 1], poly_vertex_corners[j]);
}
@@ -455,14 +465,16 @@ static bool sort_vertex_polys(const Mesh &mesh,
* 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 Span<MEdge> edges,
+ const Span<MLoop> loops,
const int vertex_index,
const Span<EdgeType> edge_types,
int &r_edge)
{
- for (const MLoop &loop : Span<MLoop>(&mesh.mloop[poly.loopstart], poly.totloop)) {
+ const Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop);
+ for (const MLoop &loop : poly_loops) {
if (edge_types[loop.e] == EdgeType::Boundary) {
- const MEdge &edge = mesh.medge[loop.e];
+ const MEdge &edge = edges[loop.e];
if (edge.v1 == vertex_index || edge.v2 == vertex_index) {
r_edge = loop.e;
return;
@@ -476,7 +488,8 @@ static void boundary_edge_on_poly(const MPoly &poly,
* orientation of the poly is taken into account.
*/
static void boundary_edges_on_poly(const MPoly &poly,
- const Mesh &mesh,
+ const Span<MEdge> edges,
+ const Span<MLoop> loops,
const int vertex_index,
const Span<EdgeType> edge_types,
int &r_edge1,
@@ -486,9 +499,10 @@ static void boundary_edges_on_poly(const MPoly &poly,
/* 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)) {
+ const Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop);
+ for (const MLoop &loop : poly_loops) {
if (edge_types[loop.e] == EdgeType::Boundary) {
- const MEdge &edge = mesh.medge[loop.e];
+ const MEdge &edge = edges[loop.e];
if (edge.v1 == vertex_index || edge.v2 == vertex_index) {
if (edge1_done) {
if (needs_swap) {
@@ -510,7 +524,7 @@ static void boundary_edges_on_poly(const MPoly &poly,
}
}
-static void add_edge(const Mesh &mesh,
+static void add_edge(const Span<MEdge> src_edges,
const int old_edge_i,
const int v1,
const int v2,
@@ -518,7 +532,7 @@ static void add_edge(const Mesh &mesh,
Vector<MEdge> &new_edges,
Vector<int> &loop_edges)
{
- MEdge new_edge = MEdge(mesh.medge[old_edge_i]);
+ MEdge new_edge = src_edges[old_edge_i];
new_edge.v1 = v1;
new_edge.v2 = v2;
const int new_edge_i = new_edges.size();
@@ -549,14 +563,17 @@ static bool vertex_needs_dissolving(const int vertex,
* 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,
+static void dissolve_redundant_verts(const Span<MEdge> edges,
+ const Span<MPoly> polys,
+ const Span<MLoop> loops,
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)) {
+ const int vertex_num = vertex_types.size();
+ for (const int vert_i : IndexRange(vertex_num)) {
if (vertex_poly_indices[vert_i].size() != 2 || vertex_types[vert_i] != VertexType::Normal) {
continue;
}
@@ -564,9 +581,10 @@ static void dissolve_redundant_verts(const Mesh &mesh,
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 MPoly &poly = polys[first_poly_index];
+ const Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop);
+ for (const MLoop &loop : poly_loops) {
+ const MEdge &edge = edges[loop.e];
const int v1 = edge.v1;
const int v2 = edge.v2;
bool mark_edge = false;
@@ -617,6 +635,10 @@ static void calc_dual_mesh(GeometrySet &geometry_set,
const bool keep_boundaries)
{
const Mesh &mesh_in = *in_component.get_for_read();
+ const Span<MVert> src_verts = mesh_in.verts();
+ const Span<MEdge> src_edges = mesh_in.edges();
+ const Span<MPoly> src_polys = mesh_in.polys();
+ const Span<MLoop> src_loops = mesh_in.loops();
Map<AttributeIDRef, AttributeKind> attributes;
geometry_set.gather_attributes_for_propagation(
@@ -644,14 +666,28 @@ static void calc_dual_mesh(GeometrySet &geometry_set,
bool vertex_ok = true;
if (vertex_types[i] == VertexType::Normal) {
Array<int> shared_edges(loop_indices.size());
- vertex_ok = sort_vertex_polys(
- mesh_in, i, false, edge_types, loop_indices, shared_edges, sorted_corners);
+ vertex_ok = sort_vertex_polys(src_edges,
+ src_polys,
+ src_loops,
+ i,
+ false,
+ edge_types,
+ loop_indices,
+ shared_edges,
+ sorted_corners);
vertex_shared_edges[i] = std::move(shared_edges);
}
else {
Array<int> shared_edges(loop_indices.size() - 1);
- vertex_ok = sort_vertex_polys(
- mesh_in, i, true, edge_types, loop_indices, shared_edges, sorted_corners);
+ vertex_ok = sort_vertex_polys(src_edges,
+ src_polys,
+ src_loops,
+ i,
+ true,
+ edge_types,
+ loop_indices,
+ shared_edges,
+ sorted_corners);
vertex_shared_edges[i] = std::move(shared_edges);
}
if (!vertex_ok) {
@@ -666,9 +702,9 @@ static void calc_dual_mesh(GeometrySet &geometry_set,
Vector<float3> vertex_positions(mesh_in.totpoly);
for (const int i : IndexRange(mesh_in.totpoly)) {
- const MPoly poly = mesh_in.mpoly[i];
+ const MPoly &poly = src_polys[i];
BKE_mesh_calc_poly_center(
- &poly, &mesh_in.mloop[poly.loopstart], mesh_in.mvert, vertex_positions[i]);
+ &poly, &src_loops[poly.loopstart], src_verts.data(), vertex_positions[i]);
}
Array<int> boundary_edge_midpoint_index;
@@ -679,8 +715,8 @@ static void calc_dual_mesh(GeometrySet &geometry_set,
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);
+ const MEdge &edge = src_edges[i];
+ mid_v3_v3v3(mid, src_verts[edge.v1].co, src_verts[edge.v2].co);
boundary_edge_midpoint_index[i] = vertex_positions.size();
vertex_positions.append(mid);
}
@@ -706,7 +742,9 @@ static void calc_dual_mesh(GeometrySet &geometry_set,
/* This is necessary to prevent duplicate edges from being created, but will likely not do
* anything for most meshes. */
- dissolve_redundant_verts(mesh_in,
+ dissolve_redundant_verts(src_edges,
+ src_polys,
+ src_loops,
vertex_poly_indices,
vertex_types,
old_to_new_edges_map,
@@ -734,7 +772,7 @@ static void calc_dual_mesh(GeometrySet &geometry_set,
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]);
+ MEdge new_edge = src_edges[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);
@@ -776,7 +814,7 @@ static void calc_dual_mesh(GeometrySet &geometry_set,
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]);
+ MEdge new_edge = src_edges[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);
@@ -795,13 +833,15 @@ static void calc_dual_mesh(GeometrySet &geometry_set,
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);
+ boundary_edge_on_poly(
+ src_polys[loop_indices.last()], src_edges, src_loops, i, edge_types, edge1);
+ boundary_edge_on_poly(
+ src_polys[loop_indices.first()], src_edges, src_loops, 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);
+ src_polys[loop_indices[0]], src_edges, src_loops, i, edge_types, edge1, edge2);
}
const int last_face_center = loop_indices.last();
@@ -809,7 +849,7 @@ static void calc_dual_mesh(GeometrySet &geometry_set,
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,
+ add_edge(src_edges,
edge1,
last_face_center,
first_midpoint,
@@ -827,9 +867,9 @@ static void calc_dual_mesh(GeometrySet &geometry_set,
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);
+ vertex_positions.append(src_verts[i].co);
const int boundary_vertex = loop_indices.last();
- add_edge(mesh_in,
+ add_edge(src_edges,
edge1,
first_midpoint,
boundary_vertex,
@@ -840,7 +880,7 @@ static void calc_dual_mesh(GeometrySet &geometry_set,
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,
+ add_edge(src_edges,
edge2,
boundary_vertex,
second_midpoint,
@@ -850,7 +890,7 @@ static void calc_dual_mesh(GeometrySet &geometry_set,
if (old_to_new_edges_map[edge2] == -1) {
const int first_face_center = loop_indices.first();
- add_edge(mesh_in,
+ add_edge(src_edges,
edge2,
second_midpoint,
first_face_center,
@@ -872,31 +912,34 @@ static void calc_dual_mesh(GeometrySet &geometry_set,
}
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);
+ mesh_in.attributes(),
+ mesh_out->attributes_for_write());
+
+ MutableSpan<MVert> dst_verts = mesh_out->verts_for_write();
+ MutableSpan<MEdge> dst_edges = mesh_out->edges_for_write();
+ MutableSpan<MPoly> dst_polys = mesh_out->polys_for_write();
+ MutableSpan<MLoop> dst_loops = mesh_out->loops_for_write();
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];
+ dst_polys[i].loopstart = loop_start;
+ dst_polys[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];
+ dst_loops[i].v = loops[i];
+ dst_loops[i].e = loop_edges[i];
}
for (const int i : IndexRange(mesh_out->totvert)) {
- copy_v3_v3(mesh_out->mvert[i].co, vertex_positions[i]);
+ copy_v3_v3(dst_verts[i].co, vertex_positions[i]);
}
- memcpy(mesh_out->medge, new_edges.data(), sizeof(MEdge) * new_edges.size());
+ dst_edges.copy_from(new_edges);
geometry_set.replace_mesh(mesh_out);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc b/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc
index 691f341b518..d2a3c339301 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc
@@ -61,13 +61,11 @@ struct IndexAttributes {
* \{ */
static Map<AttributeIDRef, AttributeKind> gather_attributes_without_id(
- const GeometrySet &geometry_set,
- const GeometryComponentType component_type,
- const bool include_instances)
+ const GeometrySet &geometry_set, const GeometryComponentType component_type)
{
Map<AttributeIDRef, AttributeKind> attributes;
geometry_set.gather_attributes_for_propagation(
- {component_type}, component_type, include_instances, attributes);
+ {component_type}, component_type, false, attributes);
attributes.remove("id");
return attributes;
};
@@ -143,23 +141,22 @@ static void threaded_id_offset_copy(const Span<int> offsets,
}
/** Create the copy indices for the duplication domain. */
-static void create_duplicate_index_attribute(GeometryComponent &component,
+static void create_duplicate_index_attribute(bke::MutableAttributeAccessor attributes,
const eAttrDomain output_domain,
const IndexMask selection,
const IndexAttributes &attribute_outputs,
const Span<int> offsets)
{
- OutputAttribute_Typed<int> copy_attribute = component.attribute_try_get_for_output_only<int>(
+ SpanAttributeWriter<int> duplicate_indices = attributes.lookup_or_add_for_write_only_span<int>(
attribute_outputs.duplicate_index.get(), output_domain);
- MutableSpan<int> duplicate_indices = copy_attribute.as_span();
for (const int i : IndexRange(selection.size())) {
const IndexRange range = range_for_offsets_index(offsets, i);
- MutableSpan<int> indices = duplicate_indices.slice(range);
+ MutableSpan<int> indices = duplicate_indices.span.slice(range);
for (const int i : indices.index_range()) {
indices[i] = i;
}
}
- copy_attribute.save();
+ duplicate_indices.finish();
}
/**
@@ -167,62 +164,57 @@ static void create_duplicate_index_attribute(GeometryComponent &component,
* and the duplicate number. This function is used for the point domain elements.
*/
static void copy_stable_id_point(const Span<int> offsets,
- const GeometryComponent &src_component,
- GeometryComponent &dst_component)
+ const bke::AttributeAccessor src_attributes,
+ bke::MutableAttributeAccessor dst_attributes)
{
- ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read("id");
+ GAttributeReader src_attribute = src_attributes.lookup("id");
if (!src_attribute) {
return;
}
- OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
+ GSpanAttributeWriter dst_attribute = dst_attributes.lookup_or_add_for_write_only_span(
"id", ATTR_DOMAIN_POINT, CD_PROP_INT32);
if (!dst_attribute) {
return;
}
- VArray_Span<int> src{src_attribute.varray.typed<int>()};
- MutableSpan<int> dst = dst_attribute.as_span<int>();
+ VArraySpan<int> src{src_attribute.varray.typed<int>()};
+ MutableSpan<int> dst = dst_attribute.span.typed<int>();
threaded_id_offset_copy(offsets, src, dst);
- dst_attribute.save();
+ dst_attribute.finish();
}
-/* The attributes for the point (also instance) duplicated elements are stored sequentially
- * (1,1,1,2,2,2,3,3,3,etc) They can be copied by using a simple offset array. For each domain, if
- * elements are ordered differently a custom function is called to copy the attributes.
- */
-
-static void copy_point_attributes_without_id(GeometrySet &geometry_set,
- const GeometryComponentType component_type,
- const bool include_instances,
- const Span<int> offsets,
- const IndexMask selection,
- const GeometryComponent &src_component,
- GeometryComponent &dst_component)
+static void copy_attributes_without_id(GeometrySet &geometry_set,
+ const GeometryComponentType component_type,
+ const eAttrDomain domain,
+ const Span<int> offsets,
+ const IndexMask selection,
+ const bke::AttributeAccessor src_attributes,
+ bke::MutableAttributeAccessor dst_attributes)
{
- Map<AttributeIDRef, AttributeKind> attributes = gather_attributes_without_id(
- geometry_set, component_type, include_instances);
+ const Map<AttributeIDRef, AttributeKind> attributes = gather_attributes_without_id(
+ geometry_set, component_type);
for (const 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 || src_attribute.domain != ATTR_DOMAIN_POINT) {
+ GAttributeReader src_attribute = src_attributes.lookup(attribute_id);
+ if (!src_attribute || src_attribute.domain != domain) {
continue;
}
eAttrDomain out_domain = src_attribute.domain;
const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(
src_attribute.varray.type());
- OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
+ GSpanAttributeWriter dst_attribute = dst_attributes.lookup_or_add_for_write_only_span(
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> src = src_attribute.varray.typed<T>();
- MutableSpan<T> dst = dst_attribute.as_span<T>();
+ VArraySpan<T> src = src_attribute.varray.typed<T>();
+ MutableSpan<T> dst = dst_attribute.span.typed<T>();
threaded_slice_fill<T>(offsets, selection, src, dst);
});
- dst_attribute.save();
+ dst_attribute.finish();
}
}
@@ -237,19 +229,17 @@ static void copy_point_attributes_without_id(GeometrySet &geometry_set,
* copied with an offset fill, otherwise a mapping is used.
*/
static void copy_curve_attributes_without_id(const GeometrySet &geometry_set,
- const CurveComponent &src_component,
const bke::CurvesGeometry &src_curves,
const IndexMask selection,
const Span<int> curve_offsets,
- bke::CurvesGeometry &dst_curves,
- CurveComponent &dst_component)
+ bke::CurvesGeometry &dst_curves)
{
Map<AttributeIDRef, AttributeKind> attributes = gather_attributes_without_id(
- geometry_set, GEO_COMPONENT_TYPE_CURVE, false);
+ geometry_set, GEO_COMPONENT_TYPE_CURVE);
for (const 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);
+ GAttributeReader src_attribute = src_curves.attributes().lookup(attribute_id);
if (!src_attribute) {
continue;
}
@@ -257,16 +247,17 @@ static void copy_curve_attributes_without_id(const GeometrySet &geometry_set,
eAttrDomain out_domain = src_attribute.domain;
const eCustomDataType 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);
+ GSpanAttributeWriter dst_attribute =
+ dst_curves.attributes_for_write().lookup_or_add_for_write_only_span(
+ 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> src{src_attribute.varray.typed<T>()};
- MutableSpan<T> dst = dst_attribute.as_span<T>();
+ VArraySpan<T> src{src_attribute.varray.typed<T>()};
+ MutableSpan<T> dst = dst_attribute.span.typed<T>();
switch (out_domain) {
case ATTR_DOMAIN_CURVE:
@@ -287,7 +278,7 @@ static void copy_curve_attributes_without_id(const GeometrySet &geometry_set,
break;
}
});
- dst_attribute.save();
+ dst_attribute.finish();
}
}
@@ -300,22 +291,21 @@ static void copy_curve_attributes_without_id(const GeometrySet &geometry_set,
static void copy_stable_id_curves(const bke::CurvesGeometry &src_curves,
const IndexMask selection,
const Span<int> curve_offsets,
- const CurveComponent &src_component,
- bke::CurvesGeometry &dst_curves,
- CurveComponent &dst_component)
+ bke::CurvesGeometry &dst_curves)
{
- ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read("id");
+ GAttributeReader src_attribute = src_curves.attributes().lookup("id");
if (!src_attribute) {
return;
}
- OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
- "id", ATTR_DOMAIN_POINT, CD_PROP_INT32);
+ GSpanAttributeWriter dst_attribute =
+ dst_curves.attributes_for_write().lookup_or_add_for_write_only_span(
+ "id", ATTR_DOMAIN_POINT, CD_PROP_INT32);
if (!dst_attribute) {
return;
}
- VArray_Span<int> src{src_attribute.varray.typed<int>()};
- MutableSpan<int> dst = dst_attribute.as_span<int>();
+ VArraySpan<int> src{src_attribute.varray.typed<int>()};
+ MutableSpan<int> dst = dst_attribute.span.typed<int>();
threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) {
for (const int i_selection : range) {
@@ -329,7 +319,7 @@ static void copy_stable_id_curves(const bke::CurvesGeometry &src_curves,
}
}
});
- dst_attribute.save();
+ dst_attribute.finish();
}
static void duplicate_curves(GeometrySet &geometry_set,
@@ -338,16 +328,16 @@ static void duplicate_curves(GeometrySet &geometry_set,
const IndexAttributes &attribute_outputs)
{
if (!geometry_set.has_curves()) {
- geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ geometry_set.remove_geometry_during_modify();
return;
}
- geometry_set.keep_only({GEO_COMPONENT_TYPE_CURVE, GEO_COMPONENT_TYPE_INSTANCES});
+ geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_CURVE});
+ GeometryComponentEditData::remember_deformed_curve_positions_if_necessary(geometry_set);
- const CurveComponent &src_component = *geometry_set.get_component_for_read<CurveComponent>();
- const Curves &curves_id = *src_component.get_for_read();
+ const Curves &curves_id = *geometry_set.get_curves_for_read();
const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
- GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_CURVE};
+ bke::CurvesFieldContext field_context{curves, ATTR_DOMAIN_CURVE};
FieldEvaluator evaluator{field_context, curves.curves_num()};
evaluator.add(count_field);
evaluator.set_selection(selection_field);
@@ -372,6 +362,7 @@ static void duplicate_curves(GeometrySet &geometry_set,
point_offsets.last() = dst_points_num;
Curves *new_curves_id = bke::curves_new_nomain(dst_points_num, dst_curves_num);
+ bke::curves_copy_parameters(curves_id, *new_curves_id);
bke::CurvesGeometry &new_curves = bke::CurvesGeometry::wrap(new_curves_id->geometry);
MutableSpan<int> all_dst_offsets = new_curves.offsets_for_write();
@@ -389,18 +380,16 @@ static void duplicate_curves(GeometrySet &geometry_set,
});
all_dst_offsets.last() = dst_points_num;
- CurveComponent dst_component;
- dst_component.replace(new_curves_id, GeometryOwnershipType::Editable);
-
- copy_curve_attributes_without_id(
- geometry_set, src_component, curves, selection, curve_offsets, new_curves, dst_component);
+ copy_curve_attributes_without_id(geometry_set, curves, selection, curve_offsets, new_curves);
- copy_stable_id_curves(
- curves, selection, curve_offsets, src_component, new_curves, dst_component);
+ copy_stable_id_curves(curves, selection, curve_offsets, new_curves);
if (attribute_outputs.duplicate_index) {
- create_duplicate_index_attribute(
- dst_component, ATTR_DOMAIN_CURVE, selection, attribute_outputs, curve_offsets);
+ create_duplicate_index_attribute(new_curves.attributes_for_write(),
+ ATTR_DOMAIN_CURVE,
+ selection,
+ attribute_outputs,
+ curve_offsets);
}
geometry_set.replace_curves(new_curves_id);
@@ -422,15 +411,15 @@ static void copy_face_attributes_without_id(GeometrySet &geometry_set,
const Span<int> loop_mapping,
const Span<int> offsets,
const IndexMask selection,
- const GeometryComponent &src_component,
- GeometryComponent &dst_component)
+ const bke::AttributeAccessor src_attributes,
+ bke::MutableAttributeAccessor dst_attributes)
{
Map<AttributeIDRef, AttributeKind> attributes = gather_attributes_without_id(
- geometry_set, GEO_COMPONENT_TYPE_MESH, false);
+ geometry_set, GEO_COMPONENT_TYPE_MESH);
for (const 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);
+ GAttributeReader src_attribute = src_attributes.lookup(attribute_id);
if (!src_attribute) {
continue;
}
@@ -438,7 +427,7 @@ static void copy_face_attributes_without_id(GeometrySet &geometry_set,
eAttrDomain out_domain = src_attribute.domain;
const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(
src_attribute.varray.type());
- OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
+ GSpanAttributeWriter dst_attribute = dst_attributes.lookup_or_add_for_write_only_span(
attribute_id, out_domain, data_type);
if (!dst_attribute) {
continue;
@@ -446,8 +435,8 @@ static void copy_face_attributes_without_id(GeometrySet &geometry_set,
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
using T = decltype(dummy);
- VArray_Span<T> src{src_attribute.varray.typed<T>()};
- MutableSpan<T> dst = dst_attribute.as_span<T>();
+ VArraySpan<T> src{src_attribute.varray.typed<T>()};
+ MutableSpan<T> dst = dst_attribute.span.typed<T>();
switch (out_domain) {
case ATTR_DOMAIN_FACE:
@@ -466,7 +455,7 @@ static void copy_face_attributes_without_id(GeometrySet &geometry_set,
break;
}
});
- dst_attribute.save();
+ dst_attribute.finish();
}
}
@@ -481,23 +470,23 @@ static void copy_stable_id_faces(const Mesh &mesh,
const IndexMask selection,
const Span<int> poly_offsets,
const Span<int> vert_mapping,
- const MeshComponent &src_component,
- MeshComponent &dst_component)
+ const bke::AttributeAccessor src_attributes,
+ bke::MutableAttributeAccessor dst_attributes)
{
- ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read("id");
+ GAttributeReader src_attribute = src_attributes.lookup("id");
if (!src_attribute) {
return;
}
- OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
+ GSpanAttributeWriter dst_attribute = dst_attributes.lookup_or_add_for_write_only_span(
"id", ATTR_DOMAIN_POINT, CD_PROP_INT32);
if (!dst_attribute) {
return;
}
- VArray_Span<int> src{src_attribute.varray.typed<int>()};
- MutableSpan<int> dst = dst_attribute.as_span<int>();
+ VArraySpan<int> src{src_attribute.varray.typed<int>()};
+ MutableSpan<int> dst = dst_attribute.span.typed<int>();
- Span<MPoly> polys(mesh.mpoly, mesh.totpoly);
+ const Span<MPoly> polys = mesh.polys();
int loop_index = 0;
for (const int i_poly : selection.index_range()) {
const IndexRange range = range_for_offsets_index(poly_offsets, i_poly);
@@ -518,7 +507,7 @@ static void copy_stable_id_faces(const Mesh &mesh,
}
}
- dst_attribute.save();
+ dst_attribute.finish();
}
static void duplicate_faces(GeometrySet &geometry_set,
@@ -527,19 +516,18 @@ static void duplicate_faces(GeometrySet &geometry_set,
const IndexAttributes &attribute_outputs)
{
if (!geometry_set.has_mesh()) {
- geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ geometry_set.remove_geometry_during_modify();
return;
}
- geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES});
+ geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_MESH});
- const MeshComponent &src_component = *geometry_set.get_component_for_read<MeshComponent>();
- const Mesh &mesh = *src_component.get_for_read();
- 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);
+ const Mesh &mesh = *geometry_set.get_mesh_for_read();
+ const Span<MVert> verts = mesh.verts();
+ const Span<MEdge> edges = mesh.edges();
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
- GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_FACE};
+ bke::MeshFieldContext field_context{mesh, ATTR_DOMAIN_FACE};
FieldEvaluator evaluator(field_context, polys.size());
evaluator.add(count_field);
evaluator.set_selection(selection_field);
@@ -559,10 +547,10 @@ static void duplicate_faces(GeometrySet &geometry_set,
offsets[selection.size()] = total_polys;
Mesh *new_mesh = BKE_mesh_new_nomain(total_loops, total_loops, 0, total_loops, total_polys);
- MutableSpan<MVert> new_verts(new_mesh->mvert, new_mesh->totvert);
- MutableSpan<MEdge> new_edges(new_mesh->medge, new_mesh->totedge);
- MutableSpan<MLoop> new_loops(new_mesh->mloop, new_mesh->totloop);
- MutableSpan<MPoly> new_poly(new_mesh->mpoly, new_mesh->totpoly);
+ MutableSpan<MVert> new_verts = new_mesh->verts_for_write();
+ MutableSpan<MEdge> new_edges = new_mesh->edges_for_write();
+ MutableSpan<MPoly> new_polys = new_mesh->polys_for_write();
+ MutableSpan<MLoop> new_loops = new_mesh->loops_for_write();
Array<int> vert_mapping(new_verts.size());
Array<int> edge_mapping(new_edges.size());
@@ -575,8 +563,8 @@ static void duplicate_faces(GeometrySet &geometry_set,
const MPoly &source = polys[selection[i_selection]];
for ([[maybe_unused]] const int i_duplicate : IndexRange(poly_range.size())) {
- new_poly[poly_index] = source;
- new_poly[poly_index].loopstart = loop_index;
+ new_polys[poly_index] = source;
+ new_polys[poly_index].loopstart = loop_index;
for (const int i_loops : IndexRange(source.totloop)) {
const MLoop &current_loop = loops[source.loopstart + i_loops];
loop_mapping[loop_index] = source.loopstart + i_loops;
@@ -589,7 +577,7 @@ static void duplicate_faces(GeometrySet &geometry_set,
new_edges[loop_index].v2 = loop_index + 1;
}
else {
- new_edges[loop_index].v2 = new_poly[poly_index].loopstart;
+ new_edges[loop_index].v2 = new_polys[poly_index].loopstart;
}
new_loops[loop_index].v = loop_index;
new_loops[loop_index].e = loop_index;
@@ -599,23 +587,21 @@ static void duplicate_faces(GeometrySet &geometry_set,
}
}
- MeshComponent dst_component;
- dst_component.replace(new_mesh, GeometryOwnershipType::Editable);
-
copy_face_attributes_without_id(geometry_set,
edge_mapping,
vert_mapping,
loop_mapping,
offsets,
selection,
- src_component,
- dst_component);
+ mesh.attributes(),
+ new_mesh->attributes_for_write());
- copy_stable_id_faces(mesh, selection, offsets, vert_mapping, src_component, dst_component);
+ copy_stable_id_faces(
+ mesh, selection, offsets, vert_mapping, mesh.attributes(), new_mesh->attributes_for_write());
if (attribute_outputs.duplicate_index) {
create_duplicate_index_attribute(
- dst_component, ATTR_DOMAIN_FACE, selection, attribute_outputs, offsets);
+ new_mesh->attributes_for_write(), ATTR_DOMAIN_FACE, selection, attribute_outputs, offsets);
}
geometry_set.replace_mesh(new_mesh);
@@ -635,15 +621,15 @@ static void copy_edge_attributes_without_id(GeometrySet &geometry_set,
const Span<int> point_mapping,
const Span<int> offsets,
const IndexMask selection,
- const GeometryComponent &src_component,
- GeometryComponent &dst_component)
+ const bke::AttributeAccessor src_attributes,
+ bke::MutableAttributeAccessor dst_attributes)
{
Map<AttributeIDRef, AttributeKind> attributes = gather_attributes_without_id(
- geometry_set, GEO_COMPONENT_TYPE_MESH, false);
+ geometry_set, GEO_COMPONENT_TYPE_MESH);
for (const 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);
+ GAttributeReader src_attribute = src_attributes.lookup(attribute_id);
if (!src_attribute) {
continue;
}
@@ -651,15 +637,15 @@ static void copy_edge_attributes_without_id(GeometrySet &geometry_set,
const eAttrDomain out_domain = src_attribute.domain;
const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(
src_attribute.varray.type());
- OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
+ GSpanAttributeWriter dst_attribute = dst_attributes.lookup_or_add_for_write_only_span(
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> src{src_attribute.varray.typed<T>()};
- MutableSpan<T> dst = dst_attribute.as_span<T>();
+ VArraySpan<T> src{src_attribute.varray.typed<T>()};
+ MutableSpan<T> dst = dst_attribute.span.typed<T>();
switch (out_domain) {
case ATTR_DOMAIN_EDGE:
@@ -672,7 +658,7 @@ static void copy_edge_attributes_without_id(GeometrySet &geometry_set,
break;
}
});
- dst_attribute.save();
+ dst_attribute.finish();
}
}
@@ -683,23 +669,23 @@ static void copy_edge_attributes_without_id(GeometrySet &geometry_set,
static void copy_stable_id_edges(const Mesh &mesh,
const IndexMask selection,
const Span<int> edge_offsets,
- const MeshComponent &src_component,
- MeshComponent &dst_component)
+ const bke::AttributeAccessor src_attributes,
+ bke::MutableAttributeAccessor dst_attributes)
{
- ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read("id");
+ GAttributeReader src_attribute = src_attributes.lookup("id");
if (!src_attribute) {
return;
}
- OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
+ GSpanAttributeWriter dst_attribute = dst_attributes.lookup_or_add_for_write_only_span(
"id", ATTR_DOMAIN_POINT, CD_PROP_INT32);
if (!dst_attribute) {
return;
}
- Span<MEdge> edges(mesh.medge, mesh.totedge);
+ const Span<MEdge> edges = mesh.edges();
- VArray_Span<int> src{src_attribute.varray.typed<int>()};
- MutableSpan<int> dst = dst_attribute.as_span<int>();
+ VArraySpan<int> src{src_attribute.varray.typed<int>()};
+ MutableSpan<int> dst = dst_attribute.span.typed<int>();
threading::parallel_for(IndexRange(selection.size()), 1024, [&](IndexRange range) {
for (const int i_selection : range) {
const IndexRange edge_range = range_for_offsets_index(edge_offsets, i_selection);
@@ -717,7 +703,7 @@ static void copy_stable_id_edges(const Mesh &mesh,
}
}
});
- dst_attribute.save();
+ dst_attribute.finish();
}
static void duplicate_edges(GeometrySet &geometry_set,
@@ -726,15 +712,13 @@ static void duplicate_edges(GeometrySet &geometry_set,
const IndexAttributes &attribute_outputs)
{
if (!geometry_set.has_mesh()) {
- geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ geometry_set.remove_geometry_during_modify();
return;
};
- const MeshComponent &src_component = *geometry_set.get_component_for_read<MeshComponent>();
- const Mesh &mesh = *src_component.get_for_read();
- Span<MVert> verts(mesh.mvert, mesh.totvert);
- Span<MEdge> edges(mesh.medge, mesh.totedge);
+ const Mesh &mesh = *geometry_set.get_mesh_for_read();
+ const Span<MEdge> edges = mesh.edges();
- GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_EDGE};
+ bke::MeshFieldContext field_context{mesh, ATTR_DOMAIN_EDGE};
FieldEvaluator evaluator{field_context, edges.size()};
evaluator.add(count_field);
evaluator.set_selection(selection_field);
@@ -745,8 +729,7 @@ static void duplicate_edges(GeometrySet &geometry_set,
Array<int> edge_offsets = accumulate_counts_to_offsets(selection, counts);
Mesh *new_mesh = BKE_mesh_new_nomain(edge_offsets.last() * 2, edge_offsets.last(), 0, 0, 0);
- MutableSpan<MVert> new_verts(new_mesh->mvert, new_mesh->totvert);
- MutableSpan<MEdge> new_edges(new_mesh->medge, new_mesh->totedge);
+ MutableSpan<MEdge> new_edges = new_mesh->edges_for_write();
Array<int> vert_orig_indices(edge_offsets.last() * 2);
threading::parallel_for(selection.index_range(), 1024, [&](IndexRange range) {
@@ -775,17 +758,22 @@ static void duplicate_edges(GeometrySet &geometry_set,
}
});
- MeshComponent dst_component;
- dst_component.replace(new_mesh, GeometryOwnershipType::Editable);
-
- copy_edge_attributes_without_id(
- geometry_set, vert_orig_indices, edge_offsets, selection, src_component, dst_component);
+ copy_edge_attributes_without_id(geometry_set,
+ vert_orig_indices,
+ edge_offsets,
+ selection,
+ mesh.attributes(),
+ new_mesh->attributes_for_write());
- copy_stable_id_edges(mesh, selection, edge_offsets, src_component, dst_component);
+ copy_stable_id_edges(
+ mesh, selection, edge_offsets, mesh.attributes(), new_mesh->attributes_for_write());
if (attribute_outputs.duplicate_index) {
- create_duplicate_index_attribute(
- dst_component, ATTR_DOMAIN_EDGE, selection, attribute_outputs, edge_offsets);
+ create_duplicate_index_attribute(new_mesh->attributes_for_write(),
+ ATTR_DOMAIN_EDGE,
+ selection,
+ attribute_outputs,
+ edge_offsets);
}
geometry_set.replace_mesh(new_mesh);
@@ -802,14 +790,13 @@ static void duplicate_points_curve(GeometrySet &geometry_set,
const Field<bool> &selection_field,
const IndexAttributes &attribute_outputs)
{
- const CurveComponent &src_component = *geometry_set.get_component_for_read<CurveComponent>();
- const Curves &src_curves_id = *src_component.get_for_read();
+ const Curves &src_curves_id = *geometry_set.get_curves_for_read();
const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(src_curves_id.geometry);
if (src_curves.points_num() == 0) {
return;
}
- GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_POINT};
+ bke::CurvesFieldContext field_context{src_curves, ATTR_DOMAIN_POINT};
FieldEvaluator evaluator{field_context, src_curves.points_num()};
evaluator.add(count_field);
evaluator.set_selection(selection_field);
@@ -829,6 +816,7 @@ static void duplicate_points_curve(GeometrySet &geometry_set,
});
Curves *new_curves_id = bke::curves_new_nomain(dst_num, dst_num);
+ bke::curves_copy_parameters(src_curves_id, *new_curves_id);
bke::CurvesGeometry &new_curves = bke::CurvesGeometry::wrap(new_curves_id->geometry);
MutableSpan<int> new_curve_offsets = new_curves.offsets_for_write();
for (const int i : new_curves.curves_range()) {
@@ -836,15 +824,12 @@ static void duplicate_points_curve(GeometrySet &geometry_set,
}
new_curve_offsets.last() = dst_num;
- CurveComponent dst_component;
- dst_component.replace(new_curves_id, GeometryOwnershipType::Editable);
-
Map<AttributeIDRef, AttributeKind> attributes = gather_attributes_without_id(
- geometry_set, GEO_COMPONENT_TYPE_CURVE, false);
+ geometry_set, GEO_COMPONENT_TYPE_CURVE);
for (const 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);
+ GAttributeReader src_attribute = src_curves.attributes().lookup(attribute_id);
if (!src_attribute) {
continue;
}
@@ -852,16 +837,17 @@ static void duplicate_points_curve(GeometrySet &geometry_set,
eAttrDomain domain = src_attribute.domain;
const eCustomDataType 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, domain, data_type);
+ GSpanAttributeWriter dst_attribute =
+ new_curves.attributes_for_write().lookup_or_add_for_write_only_span(
+ attribute_id, domain, data_type);
if (!dst_attribute) {
continue;
}
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
using T = decltype(dummy);
- VArray_Span<T> src{src_attribute.varray.typed<T>()};
- MutableSpan<T> dst = dst_attribute.as_span<T>();
+ VArraySpan<T> src{src_attribute.varray.typed<T>()};
+ MutableSpan<T> dst = dst_attribute.span.typed<T>();
switch (domain) {
case ATTR_DOMAIN_CURVE:
@@ -880,14 +866,17 @@ static void duplicate_points_curve(GeometrySet &geometry_set,
break;
}
});
- dst_attribute.save();
+ dst_attribute.finish();
}
- copy_stable_id_point(offsets, src_component, dst_component);
+ copy_stable_id_point(offsets, src_curves.attributes(), new_curves.attributes_for_write());
if (attribute_outputs.duplicate_index) {
- create_duplicate_index_attribute(
- dst_component, ATTR_DOMAIN_POINT, selection, attribute_outputs, offsets.as_span());
+ create_duplicate_index_attribute(new_curves.attributes_for_write(),
+ ATTR_DOMAIN_POINT,
+ selection,
+ attribute_outputs,
+ offsets.as_span());
}
geometry_set.replace_curves(new_curves_id);
@@ -904,11 +893,10 @@ static void duplicate_points_mesh(GeometrySet &geometry_set,
const Field<bool> &selection_field,
const IndexAttributes &attribute_outputs)
{
- const MeshComponent &src_component = *geometry_set.get_component_for_read<MeshComponent>();
const Mesh &mesh = *geometry_set.get_mesh_for_read();
- Span<MVert> src_verts(mesh.mvert, mesh.totvert);
+ const Span<MVert> src_verts = mesh.verts();
- GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_POINT};
+ bke::MeshFieldContext field_context{mesh, ATTR_DOMAIN_POINT};
FieldEvaluator evaluator{field_context, src_verts.size()};
evaluator.add(count_field);
evaluator.set_selection(selection_field);
@@ -919,25 +907,26 @@ static void duplicate_points_mesh(GeometrySet &geometry_set,
Array<int> offsets = accumulate_counts_to_offsets(selection, counts);
Mesh *new_mesh = BKE_mesh_new_nomain(offsets.last(), 0, 0, 0, 0);
- MutableSpan<MVert> dst_verts(new_mesh->mvert, new_mesh->totvert);
+ MutableSpan<MVert> dst_verts = new_mesh->verts_for_write();
threaded_slice_fill(offsets.as_span(), selection, src_verts, dst_verts);
- MeshComponent dst_component;
- dst_component.replace(new_mesh, GeometryOwnershipType::Editable);
- copy_point_attributes_without_id(geometry_set,
- GEO_COMPONENT_TYPE_MESH,
- false,
- offsets,
- selection,
- src_component,
- dst_component);
+ copy_attributes_without_id(geometry_set,
+ GEO_COMPONENT_TYPE_MESH,
+ ATTR_DOMAIN_POINT,
+ offsets,
+ selection,
+ mesh.attributes(),
+ new_mesh->attributes_for_write());
- copy_stable_id_point(offsets, src_component, dst_component);
+ copy_stable_id_point(offsets, mesh.attributes(), new_mesh->attributes_for_write());
if (attribute_outputs.duplicate_index) {
- create_duplicate_index_attribute(
- dst_component, ATTR_DOMAIN_POINT, selection, attribute_outputs, offsets.as_span());
+ create_duplicate_index_attribute(new_mesh->attributes_for_write(),
+ ATTR_DOMAIN_POINT,
+ selection,
+ attribute_outputs,
+ offsets.as_span());
}
geometry_set.replace_mesh(new_mesh);
@@ -954,12 +943,10 @@ static void duplicate_points_pointcloud(GeometrySet &geometry_set,
const Field<bool> &selection_field,
const IndexAttributes &attribute_outputs)
{
- const PointCloudComponent &src_points =
- *geometry_set.get_component_for_read<PointCloudComponent>();
- const int point_num = src_points.attribute_domain_num(ATTR_DOMAIN_POINT);
+ const PointCloud &src_points = *geometry_set.get_pointcloud_for_read();
- GeometryComponentFieldContext field_context{src_points, ATTR_DOMAIN_POINT};
- FieldEvaluator evaluator{field_context, point_num};
+ bke::PointCloudFieldContext field_context{src_points};
+ FieldEvaluator evaluator{field_context, src_points.totpoint};
evaluator.add(count_field);
evaluator.set_selection(selection_field);
evaluator.evaluate();
@@ -969,22 +956,23 @@ static void duplicate_points_pointcloud(GeometrySet &geometry_set,
Array<int> offsets = accumulate_counts_to_offsets(selection, counts);
PointCloud *pointcloud = BKE_pointcloud_new_nomain(offsets.last());
- PointCloudComponent dst_component;
- dst_component.replace(pointcloud, GeometryOwnershipType::Editable);
- copy_point_attributes_without_id(geometry_set,
- GEO_COMPONENT_TYPE_POINT_CLOUD,
- false,
- offsets,
- selection,
- src_points,
- dst_component);
+ copy_attributes_without_id(geometry_set,
+ GEO_COMPONENT_TYPE_POINT_CLOUD,
+ ATTR_DOMAIN_POINT,
+ offsets,
+ selection,
+ src_points.attributes(),
+ pointcloud->attributes_for_write());
- copy_stable_id_point(offsets, src_points, dst_component);
+ copy_stable_id_point(offsets, src_points.attributes(), pointcloud->attributes_for_write());
if (attribute_outputs.duplicate_index) {
- create_duplicate_index_attribute(
- dst_component, ATTR_DOMAIN_POINT, selection, attribute_outputs, offsets);
+ create_duplicate_index_attribute(pointcloud->attributes_for_write(),
+ ATTR_DOMAIN_POINT,
+ selection,
+ attribute_outputs,
+ offsets);
}
geometry_set.replace_pointcloud(pointcloud);
}
@@ -1024,7 +1012,7 @@ static void duplicate_points(GeometrySet &geometry_set,
}
}
component_types.append(GEO_COMPONENT_TYPE_INSTANCES);
- geometry_set.keep_only(component_types);
+ geometry_set.keep_only_during_modify(component_types);
}
/** \} */
@@ -1046,7 +1034,7 @@ static void duplicate_instances(GeometrySet &geometry_set,
const InstancesComponent &src_instances =
*geometry_set.get_component_for_read<InstancesComponent>();
- GeometryComponentFieldContext field_context{src_instances, ATTR_DOMAIN_INSTANCE};
+ bke::GeometryFieldContext field_context{src_instances, ATTR_DOMAIN_INSTANCE};
FieldEvaluator evaluator{field_context, src_instances.instances_num()};
evaluator.add(count_field);
evaluator.set_selection(selection_field);
@@ -1076,17 +1064,20 @@ static void duplicate_instances(GeometrySet &geometry_set,
dst_instances.instance_reference_handles().slice(range).fill(new_handle);
}
- copy_point_attributes_without_id(geometry_set,
- GEO_COMPONENT_TYPE_INSTANCES,
- true,
- offsets,
- selection,
- src_instances,
- dst_instances);
+ copy_attributes_without_id(geometry_set,
+ GEO_COMPONENT_TYPE_INSTANCES,
+ ATTR_DOMAIN_INSTANCE,
+ offsets,
+ selection,
+ *src_instances.attributes(),
+ *dst_instances.attributes_for_write());
if (attribute_outputs.duplicate_index) {
- create_duplicate_index_attribute(
- dst_instances, ATTR_DOMAIN_INSTANCE, selection, attribute_outputs, offsets);
+ create_duplicate_index_attribute(*dst_instances.attributes_for_write(),
+ ATTR_DOMAIN_INSTANCE,
+ selection,
+ attribute_outputs,
+ offsets);
}
geometry_set = std::move(dst_geometry);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_curves.cc b/source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_curves.cc
new file mode 100644
index 00000000000..ba09acf0bf0
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_curves.cc
@@ -0,0 +1,110 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "BKE_curves.hh"
+
+#include "DNA_mesh_types.h"
+
+#include "GEO_mesh_to_curve.hh"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_edge_paths_to_curves_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_("Start Vertices")).default_value(true).hide_value().supports_field();
+ b.add_input<decl::Int>(N_("Next Vertex Index")).default_value(-1).hide_value().supports_field();
+ b.add_output<decl::Geometry>(N_("Curves"));
+}
+
+static Curves *edge_paths_to_curves_convert(const Mesh &mesh,
+ const IndexMask start_verts_mask,
+ const Span<int> next_indices)
+{
+ Vector<int> vert_indices;
+ Vector<int> curve_offsets;
+ Array<bool> visited(mesh.totvert, false);
+ for (const int first_vert : start_verts_mask) {
+ const int second_vert = next_indices[first_vert];
+ if (first_vert == second_vert) {
+ continue;
+ }
+ if (second_vert < 0 || second_vert >= mesh.totvert) {
+ continue;
+ }
+
+ curve_offsets.append(vert_indices.size());
+
+ /* Iterate through path defined by #next_indices. */
+ int current_vert = first_vert;
+ while (!visited[current_vert]) {
+ visited[current_vert] = true;
+ vert_indices.append(current_vert);
+ const int next_vert = next_indices[current_vert];
+ if (next_vert < 0 || next_vert >= mesh.totvert) {
+ break;
+ }
+ current_vert = next_vert;
+ }
+
+ /* Reset visited status. */
+ const int points_in_curve_num = vert_indices.size() - curve_offsets.last();
+ for (const int vert_in_curve : vert_indices.as_span().take_back(points_in_curve_num)) {
+ visited[vert_in_curve] = false;
+ }
+ }
+
+ if (vert_indices.is_empty()) {
+ return nullptr;
+ }
+ Curves *curves_id = bke::curves_new_nomain(
+ geometry::create_curve_from_vert_indices(mesh, vert_indices, curve_offsets, IndexRange(0)));
+ return curves_id;
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh");
+
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ const Mesh *mesh = geometry_set.get_mesh_for_read();
+ if (mesh == nullptr) {
+ geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ return;
+ }
+
+ bke::MeshFieldContext context{*mesh, ATTR_DOMAIN_POINT};
+ fn::FieldEvaluator evaluator{context, mesh->totvert};
+ evaluator.add(params.get_input<Field<int>>("Next Vertex Index"));
+ evaluator.add(params.get_input<Field<bool>>("Start Vertices"));
+ evaluator.evaluate();
+ const VArraySpan<int> next_vert = evaluator.get_evaluated<int>(0);
+ IndexMask start_verts = evaluator.get_evaluated_as_mask(1);
+
+ if (start_verts.is_empty()) {
+ geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ return;
+ }
+
+ geometry_set.replace_curves(edge_paths_to_curves_convert(*mesh, start_verts, next_vert));
+ geometry_set.keep_only({GEO_COMPONENT_TYPE_CURVE, GEO_COMPONENT_TYPE_INSTANCES});
+ });
+
+ params.set_output("Curves", std::move(geometry_set));
+}
+
+} // namespace blender::nodes::node_geo_edge_paths_to_curves_cc
+
+void register_node_type_geo_edge_paths_to_curves()
+{
+ namespace file_ns = blender::nodes::node_geo_edge_paths_to_curves_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_EDGE_PATHS_TO_CURVES, "Edge Paths to Curves", 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_paths_to_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_selection.cc
new file mode 100644
index 00000000000..9ef9ee8ad6e
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_selection.cc
@@ -0,0 +1,134 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "BKE_attribute_math.hh"
+#include "BKE_mesh.h"
+
+#include "BLI_map.hh"
+#include "BLI_set.hh"
+#include "BLI_task.hh"
+
+#include "node_geometry_util.hh"
+
+#include <set>
+
+namespace blender::nodes::node_geo_edge_paths_to_selection_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Bool>(N_("Start Vertices")).default_value(true).hide_value().supports_field();
+ b.add_input<decl::Int>(N_("Next Vertex Index")).default_value(-1).hide_value().supports_field();
+ b.add_output<decl::Bool>(N_("Selection")).field_source();
+}
+
+static void edge_paths_to_selection(const Mesh &src_mesh,
+ const IndexMask start_selection,
+ const Span<int> next_indices,
+ MutableSpan<bool> r_selection)
+{
+ const Span<MEdge> edges = src_mesh.edges();
+
+ Array<bool> selection(src_mesh.totvert, false);
+
+ for (const int start_vert : start_selection) {
+ selection[start_vert] = true;
+ }
+
+ for (const int start_i : start_selection) {
+ int iter = start_i;
+ while (iter != next_indices[iter] && !selection[next_indices[iter]]) {
+ if (next_indices[iter] < 0 || next_indices[iter] >= src_mesh.totvert) {
+ break;
+ }
+ selection[next_indices[iter]] = true;
+ iter = next_indices[iter];
+ }
+ }
+
+ for (const int i : edges.index_range()) {
+ const MEdge &edge = edges[i];
+ if ((selection[edge.v1] && selection[edge.v2]) &&
+ (edge.v1 == next_indices[edge.v2] || edge.v2 == next_indices[edge.v1])) {
+ r_selection[i] = true;
+ }
+ }
+}
+
+class PathToEdgeSelectionFieldInput final : public bke::MeshFieldInput {
+ private:
+ Field<bool> start_vertices_;
+ Field<int> next_vertex_;
+
+ public:
+ PathToEdgeSelectionFieldInput(Field<bool> start_verts, Field<int> next_vertex)
+ : bke::MeshFieldInput(CPPType::get<bool>(), "Edge Selection"),
+ start_vertices_(start_verts),
+ next_vertex_(next_vertex)
+ {
+ category_ = Category::Generated;
+ }
+
+ GVArray get_varray_for_context(const Mesh &mesh,
+ const eAttrDomain domain,
+ const IndexMask /*mask*/) const final
+ {
+ bke::MeshFieldContext context{mesh, ATTR_DOMAIN_POINT};
+ fn::FieldEvaluator evaluator{context, mesh.totvert};
+ evaluator.add(next_vertex_);
+ evaluator.add(start_vertices_);
+ evaluator.evaluate();
+ const VArraySpan<int> next_vert = evaluator.get_evaluated<int>(0);
+ const IndexMask start_verts = evaluator.get_evaluated_as_mask(1);
+
+ if (start_verts.is_empty()) {
+ return {};
+ }
+
+ Array<bool> selection(mesh.totedge, false);
+ MutableSpan<bool> selection_span = selection.as_mutable_span();
+
+ edge_paths_to_selection(mesh, start_verts, next_vert, selection_span);
+
+ return mesh.attributes().adapt_domain<bool>(
+ VArray<bool>::ForContainer(std::move(selection)), ATTR_DOMAIN_EDGE, domain);
+ }
+
+ uint64_t hash() const override
+ {
+ return get_default_hash_2(start_vertices_, next_vertex_);
+ }
+
+ bool is_equal_to(const fn::FieldNode &other) const override
+ {
+ if (const PathToEdgeSelectionFieldInput *other_field =
+ dynamic_cast<const PathToEdgeSelectionFieldInput *>(&other)) {
+ return other_field->start_vertices_ == start_vertices_ &&
+ other_field->next_vertex_ == next_vertex_;
+ }
+ return false;
+ }
+};
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ Field<bool> start_verts = params.extract_input<Field<bool>>("Start Vertices");
+ Field<int> next_vertex = params.extract_input<Field<int>>("Next Vertex Index");
+ Field<bool> selection_field{
+ std::make_shared<PathToEdgeSelectionFieldInput>(start_verts, next_vertex)};
+ params.set_output("Selection", std::move(selection_field));
+}
+
+} // namespace blender::nodes::node_geo_edge_paths_to_selection_cc
+
+void register_node_type_geo_edge_paths_to_selection()
+{
+ namespace file_ns = blender::nodes::node_geo_edge_paths_to_selection_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_EDGE_PATHS_TO_SELECTION, "Edge Paths to Selection", NODE_CLASS_INPUT);
+ ntype.declare = file_ns::node_declare;
+ node_type_size(&ntype, 150, 100, 300);
+ 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
index 94fbec66bfe..0b4d5bd53f3 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc
@@ -1,5 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include "DNA_mesh_types.h"
+
#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
@@ -51,19 +53,18 @@ static void node_geo_exec(GeoNodeExecParams params)
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;
- }
+ if (const Mesh *mesh = geometry_set.get_mesh_for_write()) {
- const MeshComponent &mesh_component = *geometry_set.get_component_for_read<MeshComponent>();
- GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_EDGE};
- const int domain_num = mesh_component.attribute_domain_num(ATTR_DOMAIN_EDGE);
- fn::FieldEvaluator selection_evaluator{field_context, domain_num};
- selection_evaluator.add(selection_field);
- selection_evaluator.evaluate();
- const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0);
+ bke::MeshFieldContext field_context{*mesh, ATTR_DOMAIN_EDGE};
+ fn::FieldEvaluator selection_evaluator{field_context, mesh->totedge};
+ 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));
+ Mesh *result = mesh_edge_split(*mesh, selection);
+
+ geometry_set.replace_mesh(result);
+ }
});
params.set_output("Mesh", std::move(geometry_set));
diff --git a/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc
index 3eca92e37a3..9224e9d55f3 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc
@@ -61,51 +61,26 @@ struct AttributeOutputs {
StrongAnonymousAttributeID side_id;
};
-static void save_selection_as_attribute(MeshComponent &component,
+static void save_selection_as_attribute(Mesh &mesh,
const AnonymousAttributeID *id,
const eAttrDomain domain,
const IndexMask selection)
{
- BLI_assert(!component.attribute_exists(id));
+ MutableAttributeAccessor attributes = mesh.attributes_for_write();
+ BLI_assert(!attributes.contains(id));
- OutputAttribute_Typed<bool> attribute = component.attribute_try_get_for_output_only<bool>(
- id, domain);
+ SpanAttributeWriter<bool> attribute = attributes.lookup_or_add_for_write_span<bool>(id, domain);
/* Rely on the new attribute being zeroed by default. */
- BLI_assert(!attribute.as_span().as_span().contains(true));
+ BLI_assert(!attribute.span.as_span().contains(true));
if (selection.is_range()) {
- attribute.as_span().slice(selection.as_range()).fill(true);
+ attribute.span.slice(selection.as_range()).fill(true);
}
else {
- attribute.as_span().fill_indices(selection, true);
+ attribute.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};
+ attribute.finish();
}
/**
@@ -142,7 +117,6 @@ static void expand_mesh(Mesh &mesh,
mesh.totloop += loop_expand;
CustomData_realloc(&mesh.ldata, mesh.totloop);
}
- BKE_mesh_update_customdata_pointers(&mesh, false);
}
static CustomData &get_customdata(Mesh &mesh, const eAttrDomain domain)
@@ -164,11 +138,10 @@ static CustomData &get_customdata(Mesh &mesh, const eAttrDomain domain)
static MutableSpan<int> get_orig_index_layer(Mesh &mesh, const eAttrDomain domain)
{
- MeshComponent component;
- component.replace(&mesh, GeometryOwnershipType::ReadOnly);
+ const bke::AttributeAccessor attributes = mesh.attributes();
CustomData &custom_data = get_customdata(mesh, domain);
if (int *orig_indices = static_cast<int *>(CustomData_get_layer(&custom_data, CD_ORIGINDEX))) {
- return {orig_indices, component.attribute_domain_num(domain)};
+ return {orig_indices, attributes.domain_size(domain)};
}
return {};
}
@@ -226,7 +199,7 @@ 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)};
+ attribute_math::DefaultPropagationMixer<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]);
@@ -248,16 +221,15 @@ static Array<Vector<int>> create_vert_to_edge_map(const int vert_size,
return vert_to_edge_map;
}
-static void extrude_mesh_vertices(MeshComponent &component,
+static void extrude_mesh_vertices(Mesh &mesh,
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};
+ bke::MeshFieldContext context{mesh, ATTR_DOMAIN_POINT};
FieldEvaluator evaluator{context, mesh.totvert};
evaluator.add(offset_field);
evaluator.set_selection(selection_field);
@@ -266,30 +238,32 @@ static void extrude_mesh_vertices(MeshComponent &component,
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));
+ Array<Vector<int>> vert_to_edge_map = create_vert_to_edge_map(orig_vert_size, mesh.edges());
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);
+ MutableSpan<MVert> new_verts = mesh.verts_for_write().slice(new_vert_range);
+ MutableSpan<MEdge> new_edges = mesh.edges_for_write().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) {
+ MutableAttributeAccessor attributes = mesh.attributes_for_write();
+
+ attributes.for_all([&](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(
+ GSpanAttributeWriter attribute = attributes.lookup_or_add_for_write_span(
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()) {
+ MutableSpan<T> data = attribute.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);
@@ -307,7 +281,7 @@ static void extrude_mesh_vertices(MeshComponent &component,
}
});
- attribute.save();
+ attribute.finish();
return true;
});
@@ -325,11 +299,11 @@ static void extrude_mesh_vertices(MeshComponent &component,
if (attribute_outputs.top_id) {
save_selection_as_attribute(
- component, attribute_outputs.top_id.get(), ATTR_DOMAIN_POINT, new_vert_range);
+ mesh, 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);
+ mesh, attribute_outputs.side_id.get(), ATTR_DOMAIN_EDGE, new_edge_range);
}
BKE_mesh_runtime_clear_cache(&mesh);
@@ -337,8 +311,8 @@ static void extrude_mesh_vertices(MeshComponent &component,
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);
+ Span<MPoly> polys = mesh.polys();
+ Span<MLoop> loops = mesh.loops();
Array<Vector<int, 2>> polys_of_edge(mesh.totedge);
for (const int i_poly : polys.index_range()) {
@@ -396,35 +370,35 @@ 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>);
+ const Span<MEdge> edges = mesh.edges();
VectorSet<int> vert_indices;
vert_indices.reserve(edge_indices.size());
for (const T i_edge : edge_indices) {
- const MEdge &edge = mesh.medge[i_edge];
+ const MEdge &edge = edges[i_edge];
vert_indices.add(edge.v1);
vert_indices.add(edge.v2);
}
return vert_indices;
}
-static void extrude_mesh_edges(MeshComponent &component,
+static void extrude_mesh_edges(Mesh &mesh,
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<MEdge> orig_edges = mesh.edges();
+ Span<MPoly> orig_polys = mesh.polys();
const int orig_loop_size = mesh.totloop;
- GeometryComponentFieldContext edge_context{component, ATTR_DOMAIN_EDGE};
+ bke::MeshFieldContext edge_context{mesh, 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);
+ const VArray<float3> edge_offsets = edge_evaluator.get_evaluated<float3>(0);
if (edge_selection.is_empty()) {
return;
}
@@ -436,7 +410,7 @@ static void extrude_mesh_edges(MeshComponent &component,
Array<float3> vert_offsets;
if (!edge_offsets.is_single()) {
vert_offsets.reinitialize(orig_vert_size);
- attribute_math::DefaultPropatationMixer<float3> mixer(vert_offsets);
+ attribute_math::DefaultPropagationMixer<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];
@@ -464,12 +438,12 @@ static void extrude_mesh_edges(MeshComponent &component,
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<MEdge> edges = mesh.edges_for_write();
+ MutableSpan<MEdge> connect_edges = edges.slice(connect_edge_range);
+ MutableSpan<MEdge> duplicate_edges = edges.slice(duplicate_edge_range);
+ MutableSpan<MPoly> polys = mesh.polys_for_write();
MutableSpan<MPoly> new_polys = polys.slice(new_poly_range);
- MutableSpan<MLoop> loops = mesh_loops(mesh);
+ MutableSpan<MLoop> loops = mesh.loops_for_write();
MutableSpan<MLoop> new_loops = loops.slice(new_loop_range);
for (const int i : connect_edges.index_range()) {
@@ -477,7 +451,7 @@ static void extrude_mesh_edges(MeshComponent &component,
}
for (const int i : duplicate_edges.index_range()) {
- const MEdge &orig_edge = mesh.medge[edge_selection[i]];
+ const MEdge &orig_edge = edges[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]);
@@ -524,8 +498,10 @@ static void extrude_mesh_edges(MeshComponent &component,
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(
+ MutableAttributeAccessor attributes = mesh.attributes_for_write();
+
+ attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
+ GSpanAttributeWriter attribute = attributes.lookup_or_add_for_write_span(
id, meta_data.domain, meta_data.data_type);
if (!attribute) {
return true; /* Impossible to write the "normal" attribute. */
@@ -533,8 +509,8 @@ static void extrude_mesh_edges(MeshComponent &component,
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()) {
+ MutableSpan<T> data = attribute.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);
@@ -580,7 +556,7 @@ static void extrude_mesh_edges(MeshComponent &component,
/* 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};
+ attribute_math::DefaultPropagationMixer<T> mixer{side_poly_corner_data};
const MEdge &duplicate_edge = duplicate_edges[i_edge_selection];
const int new_vert_1 = duplicate_edge.v1;
@@ -626,10 +602,11 @@ static void extrude_mesh_edges(MeshComponent &component,
}
});
- attribute.save();
+ attribute.finish();
return true;
});
+ MutableSpan<MVert> new_verts = mesh.verts_for_write().slice(new_vert_range);
if (edge_offsets.is_single()) {
const float3 offset = edge_offsets.get_internal_single();
threading::parallel_for(new_verts.index_range(), 1024, [&](const IndexRange range) {
@@ -655,11 +632,11 @@ static void extrude_mesh_edges(MeshComponent &component,
if (attribute_outputs.top_id) {
save_selection_as_attribute(
- component, attribute_outputs.top_id.get(), ATTR_DOMAIN_EDGE, duplicate_edge_range);
+ mesh, 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);
+ mesh, attribute_outputs.side_id.get(), ATTR_DOMAIN_FACE, new_poly_range);
}
BKE_mesh_runtime_clear_cache(&mesh);
@@ -669,24 +646,23 @@ static void extrude_mesh_edges(MeshComponent &component,
* 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,
+static void extrude_mesh_face_regions(Mesh &mesh,
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);
+ Span<MEdge> orig_edges = mesh.edges();
+ Span<MPoly> orig_polys = mesh.polys();
+ Span<MLoop> orig_loops = mesh.loops();
- GeometryComponentFieldContext poly_context{component, ATTR_DOMAIN_FACE};
+ bke::MeshFieldContext poly_context{mesh, 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);
+ const VArray<float3> poly_offsets = poly_evaluator.get_evaluated<float3>(0);
if (poly_selection.is_empty()) {
return;
}
@@ -702,7 +678,7 @@ static void extrude_mesh_face_regions(MeshComponent &component,
Array<float3> vert_offsets;
if (!poly_offsets.is_single()) {
vert_offsets.reinitialize(orig_vert_size);
- attribute_math::DefaultPropatationMixer<float3> mixer(vert_offsets);
+ attribute_math::DefaultPropagationMixer<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];
@@ -781,7 +757,7 @@ static void extrude_mesh_face_regions(MeshComponent &component,
/* 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];
+ const MEdge &edge = orig_edges[i_edge];
new_vert_indices.add(edge.v1);
new_vert_indices.add(edge.v2);
}
@@ -805,13 +781,13 @@ static void extrude_mesh_face_regions(MeshComponent &component,
side_poly_range.size(),
side_loop_range.size());
- MutableSpan<MEdge> edges = mesh_edges(mesh);
+ MutableSpan<MEdge> edges = mesh.edges_for_write();
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> polys = mesh.polys_for_write();
MutableSpan<MPoly> new_polys = polys.slice(side_poly_range);
- MutableSpan<MLoop> loops = mesh_loops(mesh);
+ MutableSpan<MLoop> loops = mesh.loops_for_write();
MutableSpan<MLoop> new_loops = loops.slice(side_loop_range);
/* Initialize the edges that form the sides of the extrusion. */
@@ -902,8 +878,10 @@ static void extrude_mesh_face_regions(MeshComponent &component,
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(
+ MutableAttributeAccessor attributes = mesh.attributes_for_write();
+
+ attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
+ GSpanAttributeWriter attribute = attributes.lookup_or_add_for_write_span(
id, meta_data.domain, meta_data.data_type);
if (!attribute) {
return true; /* Impossible to write the "normal" attribute. */
@@ -911,8 +889,8 @@ static void extrude_mesh_face_regions(MeshComponent &component,
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()) {
+ MutableSpan<T> data = attribute.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);
@@ -991,20 +969,21 @@ static void extrude_mesh_face_regions(MeshComponent &component,
}
});
- attribute.save();
+ attribute.finish();
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. */
+ MutableSpan<MVert> verts = mesh.verts_for_write();
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]];
+ MVert &vert = verts[(i_new == -1) ? i_orig : new_vert_range[i_new]];
add_v3_v3(vert.co, offset);
}
});
@@ -1015,7 +994,7 @@ static void extrude_mesh_face_regions(MeshComponent &component,
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]];
+ MVert &vert = verts[(i_new == -1) ? i_orig : new_vert_range[i_new]];
add_v3_v3(vert.co, offset);
}
});
@@ -1034,11 +1013,11 @@ static void extrude_mesh_face_regions(MeshComponent &component,
if (attribute_outputs.top_id) {
save_selection_as_attribute(
- component, attribute_outputs.top_id.get(), ATTR_DOMAIN_FACE, poly_selection);
+ mesh, 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);
+ mesh, attribute_outputs.side_id.get(), ATTR_DOMAIN_FACE, side_poly_range);
}
BKE_mesh_runtime_clear_cache(&mesh);
@@ -1052,21 +1031,20 @@ static IndexRange selected_corner_range(Span<int> offsets, const int index)
return IndexRange(offset, next_offset - offset);
}
-static void extrude_individual_mesh_faces(MeshComponent &component,
+static void extrude_individual_mesh_faces(Mesh &mesh,
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);
+ Span<MPoly> orig_polys = mesh.polys();
+ Span<MLoop> orig_loops = mesh.loops();
/* 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};
+ bke::MeshFieldContext poly_context{mesh, 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());
@@ -1100,13 +1078,13 @@ static void extrude_individual_mesh_faces(MeshComponent &component,
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<MVert> new_verts = mesh.verts_for_write().slice(new_vert_range);
+ MutableSpan<MEdge> edges = mesh.edges_for_write();
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> polys = mesh.polys_for_write();
MutableSpan<MPoly> new_polys = polys.slice(side_poly_range);
- MutableSpan<MLoop> loops{mesh.mloop, mesh.totloop};
+ MutableSpan<MLoop> loops = mesh.loops_for_write();
/* 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
@@ -1154,8 +1132,10 @@ static void extrude_individual_mesh_faces(MeshComponent &component,
}
});
- component.attribute_foreach([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
- OutputAttribute attribute = component.attribute_try_get_for_output(
+ MutableAttributeAccessor attributes = mesh.attributes_for_write();
+
+ attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
+ GSpanAttributeWriter attribute = attributes.lookup_or_add_for_write_span(
id, meta_data.domain, meta_data.data_type);
if (!attribute) {
return true; /* Impossible to write the "normal" attribute. */
@@ -1163,8 +1143,8 @@ static void extrude_individual_mesh_faces(MeshComponent &component,
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()) {
+ MutableSpan<T> data = attribute.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);
@@ -1267,7 +1247,7 @@ static void extrude_individual_mesh_faces(MeshComponent &component,
}
});
- attribute.save();
+ attribute.finish();
return true;
});
@@ -1311,11 +1291,11 @@ static void extrude_individual_mesh_faces(MeshComponent &component,
if (attribute_outputs.top_id) {
save_selection_as_attribute(
- component, attribute_outputs.top_id.get(), ATTR_DOMAIN_FACE, poly_selection);
+ mesh, 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);
+ mesh, attribute_outputs.side_id.get(), ATTR_DOMAIN_FACE, side_poly_range);
}
BKE_mesh_runtime_clear_cache(&mesh);
@@ -1352,27 +1332,26 @@ static void node_geo_exec(GeoNodeExecParams params)
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>();
+ if (Mesh *mesh = geometry_set.get_mesh_for_write()) {
switch (mode) {
case GEO_NODE_EXTRUDE_MESH_VERTICES:
- extrude_mesh_vertices(component, selection, final_offset, attribute_outputs);
+ extrude_mesh_vertices(*mesh, selection, final_offset, attribute_outputs);
break;
case GEO_NODE_EXTRUDE_MESH_EDGES:
- extrude_mesh_edges(component, selection, final_offset, attribute_outputs);
+ extrude_mesh_edges(*mesh, 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);
+ extrude_individual_mesh_faces(*mesh, selection, final_offset, attribute_outputs);
}
else {
- extrude_mesh_face_regions(component, selection, final_offset, attribute_outputs);
+ extrude_mesh_face_regions(*mesh, selection, final_offset, attribute_outputs);
}
break;
}
}
- BLI_assert(BKE_mesh_is_valid(component.get_for_write()));
+ BLI_assert(BKE_mesh_is_valid(mesh));
}
});
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
index 58281df43d3..c8df5785fed 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc
@@ -9,17 +9,19 @@
#include "BLI_task.hh"
+#include "NOD_socket_search_link.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_input<decl::Float>(N_("Value"), "Value_Float").hide_value().supports_field();
+ b.add_input<decl::Int>(N_("Value"), "Value_Int").hide_value().supports_field();
+ b.add_input<decl::Vector>(N_("Value"), "Value_Vector").hide_value().supports_field();
+ b.add_input<decl::Color>(N_("Value"), "Value_Color").hide_value().supports_field();
+ b.add_input<decl::Bool>(N_("Value"), "Value_Bool").hide_value().supports_field();
b.add_output<decl::Float>(N_("Value"), "Value_Float").field_source();
b.add_output<decl::Int>(N_("Value"), "Value_Int").field_source();
@@ -70,7 +72,24 @@ static void node_update(bNodeTree *ntree, bNode *node)
nodeSetSocketAvailability(ntree, sock_out_bool, data_type == CD_PROP_BOOL);
}
-class FieldAtIndex final : public GeometryFieldInput {
+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));
+
+ const bNodeType &node_type = params.node_type();
+ const std::optional<eCustomDataType> type = node_data_type_to_custom_data_type(
+ (eNodeSocketDatatype)params.other_socket().type);
+ if (type && *type != CD_PROP_STRING) {
+ params.add_item(IFACE_("Value"), [node_type, type](LinkSearchOpParams &params) {
+ bNode &node = params.add_node(node_type);
+ node.custom2 = *type;
+ params.update_and_connect_available_socket(node, "Value");
+ });
+ }
+}
+
+class FieldAtIndex final : public bke::GeometryFieldInput {
private:
Field<int> index_field_;
GField value_field_;
@@ -78,29 +97,28 @@ class FieldAtIndex final : public GeometryFieldInput {
public:
FieldAtIndex(Field<int> index_field, GField value_field, eAttrDomain value_field_domain)
- : GeometryFieldInput(value_field.cpp_type(), "Field at Index"),
+ : bke::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 eAttrDomain domain,
- IndexMask mask) const final
+ GVArray get_varray_for_context(const bke::GeometryFieldContext &context,
+ const IndexMask mask) const final
{
- const GeometryComponentFieldContext value_field_context{component, value_field_domain_};
+ const bke::GeometryFieldContext value_field_context{
+ context.geometry(), context.type(), value_field_domain_};
FieldEvaluator value_evaluator{value_field_context,
- component.attribute_domain_num(value_field_domain_)};
+ context.attributes()->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};
+ FieldEvaluator index_evaluator{context, &mask};
index_evaluator.add(index_field_);
index_evaluator.evaluate();
- const VArray<int> &indices = index_evaluator.get_evaluated<int>(0);
+ const VArray<int> indices = index_evaluator.get_evaluated<int>(0);
GVArray output_array;
attribute_math::convert_to_static_type(*type_, [&](auto dummy) {
@@ -175,5 +193,6 @@ void register_node_type_geo_field_at_index()
ntype.draw_buttons = file_ns::node_layout;
ntype.initfunc = file_ns::node_init;
ntype.updatefunc = 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_flip_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc
index 0484017cf3b..613425716d4 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc
@@ -19,24 +19,19 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Geometry>(N_("Mesh"));
}
-static void mesh_flip_faces(MeshComponent &component, const Field<bool> &selection_field)
+static void mesh_flip_faces(Mesh &mesh, const Field<bool> &selection_field)
{
- GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_FACE};
- const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_FACE);
- if (domain_num == 0) {
+ if (mesh.totpoly == 0) {
return;
}
- fn::FieldEvaluator evaluator{field_context, domain_num};
+ bke::MeshFieldContext field_context{mesh, ATTR_DOMAIN_FACE};
+ fn::FieldEvaluator evaluator{field_context, mesh.totpoly};
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};
+ const Span<MPoly> polys = mesh.polys();
+ MutableSpan<MLoop> loops = mesh.loops_for_write();
for (const int i : selection.index_range()) {
const MPoly &poly = polys[selection[i]];
@@ -49,20 +44,21 @@ static void mesh_flip_faces(MeshComponent &component, const Field<bool> &selecti
}
}
- component.attribute_foreach(
+ MutableAttributeAccessor attributes = mesh.attributes_for_write();
+ attributes.for_all(
[&](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);
+ GSpanAttributeWriter attribute = attributes.lookup_or_add_for_write_span(
+ attribute_id, ATTR_DOMAIN_CORNER, meta_data.data_type);
attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) {
using T = decltype(dummy);
- MutableSpan<T> dst_span = attribute.as_span<T>();
+ MutableSpan<T> dst_span = attribute.span.typed<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();
+ attribute.finish();
}
return true;
});
@@ -75,11 +71,9 @@ static void node_geo_exec(GeoNodeExecParams params)
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;
+ if (Mesh *mesh = geometry_set.get_mesh_for_write()) {
+ mesh_flip_faces(*mesh, selection_field);
}
- 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));
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
index da249278867..bff2e7831c6 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc
@@ -1,5 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include "BKE_curves.hh"
+
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_input_curve_handles_cc {
@@ -15,35 +17,33 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Vector>(N_("Right")).field_source();
}
-class HandlePositionFieldInput final : public GeometryFieldInput {
+class HandlePositionFieldInput final : public bke::CurvesFieldInput {
Field<bool> relative_;
bool left_;
public:
HandlePositionFieldInput(Field<bool> relative, bool left)
- : GeometryFieldInput(CPPType::get<float3>(), "Handle"), relative_(relative), left_(left)
+ : bke::CurvesFieldInput(CPPType::get<float3>(), "Handle"), relative_(relative), left_(left)
{
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const bke::CurvesGeometry &curves,
const eAttrDomain domain,
- IndexMask mask) const final
+ const IndexMask mask) const final
{
- if (component.type() != GEO_COMPONENT_TYPE_CURVE) {
- return {};
- }
-
- GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
+ bke::CurvesFieldContext field_context{curves, ATTR_DOMAIN_POINT};
fn::FieldEvaluator evaluator(field_context, &mask);
evaluator.add(relative_);
evaluator.evaluate();
- const VArray<bool> &relative = evaluator.get_evaluated<bool>(0);
+ const VArray<bool> relative = evaluator.get_evaluated<bool>(0);
+
+ const AttributeAccessor attributes = curves.attributes();
- VArray<float3> positions = component.attribute_get_for_read<float3>(
+ VArray<float3> positions = attributes.lookup_or_default<float3>(
"position", ATTR_DOMAIN_POINT, {0, 0, 0});
StringRef side = left_ ? "handle_left" : "handle_right";
- VArray<float3> handles = component.attribute_get_for_read<float3>(
+ VArray<float3> handles = attributes.lookup_or_default<float3>(
side, ATTR_DOMAIN_POINT, {0, 0, 0});
if (relative.is_single()) {
@@ -52,10 +52,10 @@ class HandlePositionFieldInput final : public GeometryFieldInput {
for (const int i : positions.index_range()) {
output[i] = handles[i] - positions[i];
}
- return component.attribute_try_adapt_domain<float3>(
+ return attributes.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);
+ return attributes.adapt_domain<float3>(handles, ATTR_DOMAIN_POINT, domain);
}
Array<float3> output(positions.size());
@@ -67,7 +67,7 @@ class HandlePositionFieldInput final : public GeometryFieldInput {
output[i] = handles[i];
}
}
- return component.attribute_try_adapt_domain<float3>(
+ return attributes.adapt_domain<float3>(
VArray<float3>::ForContainer(std::move(output)), ATTR_DOMAIN_POINT, domain);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_instance_rotation.cc b/source/blender/nodes/geometry/nodes/node_geo_input_instance_rotation.cc
index 4c7a148a797..8c5a92904ab 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_instance_rotation.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_instance_rotation.cc
@@ -9,28 +9,20 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Vector>(N_("Rotation")).field_source();
}
-class VectorFieldInput final : public GeometryFieldInput {
+class InstanceRotationFieldInput final : public bke::InstancesFieldInput {
public:
- VectorFieldInput() : GeometryFieldInput(CPPType::get<float3>(), "Rotation")
+ InstanceRotationFieldInput() : bke::InstancesFieldInput(CPPType::get<float3>(), "Rotation")
{
}
- GVArray get_varray_for_context(const GeometryComponent &component,
- const eAttrDomain UNUSED(domain),
+ GVArray get_varray_for_context(const InstancesComponent &instances,
IndexMask UNUSED(mask)) const final
{
- if (component.type() != GEO_COMPONENT_TYPE_INSTANCES) {
- return {};
- }
-
- const InstancesComponent &instance_component = static_cast<const InstancesComponent &>(
- component);
-
auto rotation_fn = [&](const int i) -> float3 {
- return instance_component.instance_transforms()[i].to_euler();
+ return instances.instance_transforms()[i].to_euler();
};
- return VArray<float3>::ForFunc(instance_component.instances_num(), rotation_fn);
+ return VArray<float3>::ForFunc(instances.instances_num(), rotation_fn);
}
uint64_t hash() const override
@@ -40,13 +32,13 @@ class VectorFieldInput final : public GeometryFieldInput {
bool is_equal_to(const fn::FieldNode &other) const override
{
- return dynamic_cast<const VectorFieldInput *>(&other) != nullptr;
+ return dynamic_cast<const InstanceRotationFieldInput *>(&other) != nullptr;
}
};
static void node_geo_exec(GeoNodeExecParams params)
{
- Field<float3> rotation{std::make_shared<VectorFieldInput>()};
+ Field<float3> rotation{std::make_shared<InstanceRotationFieldInput>()};
params.set_output("Rotation", std::move(rotation));
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_instance_scale.cc b/source/blender/nodes/geometry/nodes/node_geo_input_instance_scale.cc
index b3a362fbf3e..b79e73915b7 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_instance_scale.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_instance_scale.cc
@@ -9,28 +9,20 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Vector>(N_("Scale")).field_source();
}
-class VectorFieldInput final : public GeometryFieldInput {
+class InstanceScaleFieldInput final : public bke::InstancesFieldInput {
public:
- VectorFieldInput() : GeometryFieldInput(CPPType::get<float3>(), "Scale")
+ InstanceScaleFieldInput() : bke::InstancesFieldInput(CPPType::get<float3>(), "Scale")
{
}
- GVArray get_varray_for_context(const GeometryComponent &component,
- const eAttrDomain UNUSED(domain),
+ GVArray get_varray_for_context(const InstancesComponent &instances,
IndexMask UNUSED(mask)) const final
{
- if (component.type() != GEO_COMPONENT_TYPE_INSTANCES) {
- return {};
- }
-
- const InstancesComponent &instance_component = static_cast<const InstancesComponent &>(
- component);
-
auto scale_fn = [&](const int i) -> float3 {
- return instance_component.instance_transforms()[i].scale();
+ return instances.instance_transforms()[i].scale();
};
- return VArray<float3>::ForFunc(instance_component.instances_num(), scale_fn);
+ return VArray<float3>::ForFunc(instances.instances_num(), scale_fn);
}
uint64_t hash() const override
@@ -40,13 +32,13 @@ class VectorFieldInput final : public GeometryFieldInput {
bool is_equal_to(const fn::FieldNode &other) const override
{
- return dynamic_cast<const VectorFieldInput *>(&other) != nullptr;
+ return dynamic_cast<const InstanceScaleFieldInput *>(&other) != nullptr;
}
};
static void node_geo_exec(GeoNodeExecParams params)
{
- Field<float3> scale{std::make_shared<VectorFieldInput>()};
+ Field<float3> scale{std::make_shared<InstanceScaleFieldInput>()};
params.set_output("Scale", std::move(scale));
}
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
index f27e0305c7d..f2e7379b3a2 100644
--- 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
@@ -53,46 +53,36 @@ static Array<EdgeMapEntry> create_edge_map(const Span<MPoly> polys,
return edge_map;
}
-class AngleFieldInput final : public GeometryFieldInput {
+class AngleFieldInput final : public bke::MeshFieldInput {
public:
- AngleFieldInput() : GeometryFieldInput(CPPType::get<float>(), "Unsigned Angle Field")
+ AngleFieldInput() : bke::MeshFieldInput(CPPType::get<float>(), "Unsigned Angle Field")
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const Mesh &mesh,
const eAttrDomain 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 {};
- }
+ const Span<MVert> verts = mesh.verts();
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
+ Array<EdgeMapEntry> edge_map = create_edge_map(polys, loops, mesh.totedge);
- 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 {
+ auto angle_fn = [edge_map = std::move(edge_map), verts, polys, loops](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);
+ BKE_mesh_calc_poly_normal(&mpoly_1, &loops[mpoly_1.loopstart], verts.data(), normal_1);
+ BKE_mesh_calc_poly_normal(&mpoly_2, &loops[mpoly_2.loopstart], verts.data(), 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);
+ VArray<float> angles = VArray<float>::ForFunc(mesh.totedge, angle_fn);
+ return mesh.attributes().adapt_domain<float>(std::move(angles), ATTR_DOMAIN_EDGE, domain);
}
uint64_t hash() const override
@@ -107,32 +97,25 @@ class AngleFieldInput final : public GeometryFieldInput {
}
};
-class SignedAngleFieldInput final : public GeometryFieldInput {
+class SignedAngleFieldInput final : public bke::MeshFieldInput {
public:
- SignedAngleFieldInput() : GeometryFieldInput(CPPType::get<float>(), "Signed Angle Field")
+ SignedAngleFieldInput() : bke::MeshFieldInput(CPPType::get<float>(), "Signed Angle Field")
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const Mesh &mesh,
const eAttrDomain 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 {
+ const Span<MVert> verts = mesh.verts();
+ const Span<MEdge> edges = mesh.edges();
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
+ Array<EdgeMapEntry> edge_map = create_edge_map(polys, loops, mesh.totedge);
+
+ auto angle_fn =
+ [edge_map = std::move(edge_map), verts, edges, polys, loops](const int i) -> float {
if (edge_map[i].face_count != 2) {
return 0.0f;
}
@@ -141,18 +124,18 @@ class SignedAngleFieldInput final : public GeometryFieldInput {
/* 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);
+ BKE_mesh_calc_poly_normal(&mpoly_1, &loops[mpoly_1.loopstart], verts.data(), poly_1_normal);
+ BKE_mesh_calc_poly_normal(&mpoly_2, &loops[mpoly_2.loopstart], verts.data(), 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)) *
+ const float3 edge_centerpoint = (float3(verts[edges[i].v1].co) +
+ float3(verts[edges[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);
+ BKE_mesh_calc_poly_center(&mpoly_2, &loops[mpoly_2.loopstart], verts.data(), 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);
@@ -165,9 +148,8 @@ class SignedAngleFieldInput final : public GeometryFieldInput {
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);
+ VArray<float> angles = VArray<float>::ForFunc(mesh.totedge, angle_fn);
+ return mesh.attributes().adapt_domain<float>(std::move(angles), ATTR_DOMAIN_EDGE, domain);
}
uint64_t hash() const override
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
index cbc2ebc3e68..bfe8753c039 100644
--- 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
@@ -16,34 +16,26 @@ static void node_declare(NodeDeclarationBuilder &b)
.description(N_("The number of faces that use each edge as one of their sides"));
}
-class EdgeNeighborCountFieldInput final : public GeometryFieldInput {
+class EdgeNeighborCountFieldInput final : public bke::MeshFieldInput {
public:
EdgeNeighborCountFieldInput()
- : GeometryFieldInput(CPPType::get<int>(), "Edge Neighbor Count Field")
+ : bke::MeshFieldInput(CPPType::get<int>(), "Edge Neighbor Count Field")
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const Mesh &mesh,
const eAttrDomain 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);
+ const Span<MLoop> loops = mesh.loops();
+ Array<int> face_count(mesh.totedge, 0);
+ for (const MLoop &loop : loops) {
+ face_count[loop.e]++;
}
- return {};
+
+ return mesh.attributes().adapt_domain<int>(
+ VArray<int>::ForContainer(std::move(face_count)), ATTR_DOMAIN_EDGE, domain);
}
uint64_t hash() const override
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
index 6201ad26bfb..c8ceae239a4 100644
--- 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
@@ -27,45 +27,37 @@ static void node_declare(NodeDeclarationBuilder &b)
enum VertexNumber { VERTEX_ONE, VERTEX_TWO };
-static VArray<int> construct_edge_vertices_gvarray(const MeshComponent &component,
- const VertexNumber vertex,
- const eAttrDomain domain)
+static VArray<int> construct_edge_verts_gvarray(const Mesh &mesh,
+ const VertexNumber vertex,
+ const eAttrDomain domain)
{
- const Mesh *mesh = component.get_for_read();
- if (mesh == nullptr) {
- return {};
- }
+ const Span<MEdge> edges = mesh.edges();
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(edges.size(),
+ [edges](const int i) -> int { return edges[i].v1; });
}
- return VArray<int>::ForFunc(mesh->totedge,
- [mesh](const int i) -> int { return mesh->medge[i].v2; });
+ return VArray<int>::ForFunc(edges.size(), [edges](const int i) -> int { return edges[i].v2; });
}
return {};
}
-class EdgeVerticesFieldInput final : public GeometryFieldInput {
+class EdgeVerticesFieldInput final : public bke::MeshFieldInput {
private:
VertexNumber vertex_;
public:
EdgeVerticesFieldInput(VertexNumber vertex)
- : GeometryFieldInput(CPPType::get<int>(), "Edge Vertices Field"), vertex_(vertex)
+ : bke::MeshFieldInput(CPPType::get<int>(), "Edge Vertices Field"), vertex_(vertex)
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const Mesh &mesh,
const eAttrDomain 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 {};
+ return construct_edge_verts_gvarray(mesh, vertex_, domain);
}
uint64_t hash() const override
@@ -83,51 +75,43 @@ class EdgeVerticesFieldInput final : public GeometryFieldInput {
}
};
-static VArray<float3> construct_edge_positions_gvarray(const MeshComponent &component,
+static VArray<float3> construct_edge_positions_gvarray(const Mesh &mesh,
const VertexNumber vertex,
const eAttrDomain domain)
{
- const Mesh *mesh = component.get_for_read();
- if (mesh == nullptr) {
- return {};
- }
+ const Span<MVert> verts = mesh.verts();
+ const Span<MEdge> edges = mesh.edges();
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); }),
+ return mesh.attributes().adapt_domain<float3>(
+ VArray<float3>::ForFunc(edges.size(),
+ [verts, edges](const int i) { return verts[edges[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); }),
+ return mesh.attributes().adapt_domain<float3>(
+ VArray<float3>::ForFunc(edges.size(),
+ [verts, edges](const int i) { return verts[edges[i].v2].co; }),
ATTR_DOMAIN_EDGE,
domain);
}
-class EdgePositionFieldInput final : public GeometryFieldInput {
+class EdgePositionFieldInput final : public bke::MeshFieldInput {
private:
VertexNumber vertex_;
public:
EdgePositionFieldInput(VertexNumber vertex)
- : GeometryFieldInput(CPPType::get<float3>(), "Edge Position Field"), vertex_(vertex)
+ : bke::MeshFieldInput(CPPType::get<float3>(), "Edge Position Field"), vertex_(vertex)
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const Mesh &mesh,
const eAttrDomain 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 {};
+ return construct_edge_positions_gvarray(mesh, vertex_, domain);
}
uint64_t hash() const override
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
index 7a0e3e37a65..be921c1f1c5 100644
--- 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
@@ -16,39 +16,33 @@ static void node_declare(NodeDeclarationBuilder &b)
.description(N_("The surface area of each of the mesh's faces"));
}
-static VArray<float> construct_face_area_gvarray(const MeshComponent &component,
- const eAttrDomain domain)
+static VArray<float> construct_face_area_varray(const Mesh &mesh, const eAttrDomain domain)
{
- const Mesh *mesh = component.get_for_read();
- if (mesh == nullptr) {
- return {};
- }
+ const Span<MVert> verts = mesh.verts();
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
- 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);
+ auto area_fn = [verts, polys, loops](const int i) -> float {
+ const MPoly &poly = polys[i];
+ return BKE_mesh_calc_poly_area(&poly, &loops[poly.loopstart], verts.data());
};
- return component.attribute_try_adapt_domain<float>(
- VArray<float>::ForFunc(mesh->totpoly, area_fn), ATTR_DOMAIN_FACE, domain);
+ return mesh.attributes().adapt_domain<float>(
+ VArray<float>::ForFunc(polys.size(), area_fn), ATTR_DOMAIN_FACE, domain);
}
-class FaceAreaFieldInput final : public GeometryFieldInput {
+class FaceAreaFieldInput final : public bke::MeshFieldInput {
public:
- FaceAreaFieldInput() : GeometryFieldInput(CPPType::get<float>(), "Face Area Field")
+ FaceAreaFieldInput() : bke::MeshFieldInput(CPPType::get<float>(), "Face Area Field")
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const Mesh &mesh,
const eAttrDomain 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 {};
+ return construct_face_area_varray(mesh, domain);
}
uint64_t hash() const override
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_is_planar.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_is_planar.cc
index 532c3dc81e5..72c45de7b0f 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_is_planar.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_is_planar.cc
@@ -22,53 +22,45 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Bool>("Planar").field_source();
}
-class PlanarFieldInput final : public GeometryFieldInput {
+class PlanarFieldInput final : public bke::MeshFieldInput {
private:
Field<float> threshold_;
public:
PlanarFieldInput(Field<float> threshold)
- : GeometryFieldInput(CPPType::get<bool>(), "Planar"), threshold_(threshold)
+ : bke::MeshFieldInput(CPPType::get<bool>(), "Planar"), threshold_(threshold)
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const Mesh &mesh,
const eAttrDomain domain,
- [[maybe_unused]] IndexMask mask) const final
+ IndexMask /*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 {};
- }
-
- GeometryComponentFieldContext context{mesh_component, ATTR_DOMAIN_FACE};
- fn::FieldEvaluator evaluator{context, mesh->totpoly};
+ const Span<MVert> verts = mesh.verts();
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
+ const Span<float3> poly_normals{(float3 *)BKE_mesh_poly_normals_ensure(&mesh), mesh.totpoly};
+
+ bke::MeshFieldContext context{mesh, ATTR_DOMAIN_FACE};
+ fn::FieldEvaluator evaluator{context, polys.size()};
evaluator.add(threshold_);
evaluator.evaluate();
- const VArray<float> &thresholds = evaluator.get_evaluated<float>(0);
-
- Span<float3> poly_normals{(float3 *)BKE_mesh_poly_normals_ensure(mesh), mesh->totpoly};
+ const VArray<float> thresholds = evaluator.get_evaluated<float>(0);
- auto planar_fn = [mesh, thresholds, poly_normals](const int i_poly) -> bool {
- if (mesh->mpoly[i_poly].totloop <= 3) {
+ auto planar_fn = [verts, polys, loops, thresholds, poly_normals](const int i) -> bool {
+ const MPoly &poly = polys[i];
+ if (poly.totloop <= 3) {
return true;
}
- const int loopstart = mesh->mpoly[i_poly].loopstart;
- const int loops = mesh->mpoly[i_poly].totloop;
- Span<MLoop> poly_loops(&mesh->mloop[loopstart], loops);
- float3 reference_normal = poly_normals[i_poly];
+ const Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop);
+ const float3 &reference_normal = poly_normals[i];
float min = FLT_MAX;
float max = -FLT_MAX;
for (const int i_loop : poly_loops.index_range()) {
- const float3 vert = mesh->mvert[poly_loops[i_loop].v].co;
+ const float3 vert = verts[poly_loops[i_loop].v].co;
float dot = math::dot(reference_normal, vert);
if (dot > max) {
max = dot;
@@ -77,11 +69,11 @@ class PlanarFieldInput final : public GeometryFieldInput {
min = dot;
}
}
- return max - min < thresholds[i_poly] / 2.0f;
+ return max - min < thresholds[i] / 2.0f;
};
- return component.attribute_try_adapt_domain<bool>(
- VArray<bool>::ForFunc(mesh->totpoly, planar_fn), ATTR_DOMAIN_FACE, domain);
+ return mesh.attributes().adapt_domain<bool>(
+ VArray<bool>::ForFunc(polys.size(), planar_fn), ATTR_DOMAIN_FACE, domain);
}
uint64_t hash() const override
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
index 67a21cb06f0..9e85eae3a31 100644
--- 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
@@ -19,48 +19,41 @@ static void node_declare(NodeDeclarationBuilder &b)
.description(N_("Number of faces which share an edge with the face"));
}
-static VArray<int> construct_neighbor_count_gvarray(const MeshComponent &component,
- const eAttrDomain domain)
+static VArray<int> construct_neighbor_count_varray(const Mesh &mesh, const eAttrDomain domain)
{
- const Mesh *mesh = component.get_for_read();
- if (mesh == nullptr) {
- return {};
- }
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
- Array<int> edge_count(mesh->totedge, 0);
- for (const int i : IndexRange(mesh->totloop)) {
- edge_count[mesh->mloop[i].e]++;
+ Array<int> edge_count(mesh.totedge, 0);
+ for (const MLoop &loop : loops) {
+ edge_count[loop.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;
+ Array<int> poly_count(polys.size(), 0);
+ for (const int poly_index : polys.index_range()) {
+ const MPoly &poly = polys[poly_index];
+ for (const MLoop &loop : loops.slice(poly.loopstart, poly.totloop)) {
+ poly_count[poly_index] += edge_count[loop.e] - 1;
}
}
- return component.attribute_try_adapt_domain<int>(
+ return mesh.attributes().adapt_domain<int>(
VArray<int>::ForContainer(std::move(poly_count)), ATTR_DOMAIN_FACE, domain);
}
-class FaceNeighborCountFieldInput final : public GeometryFieldInput {
+class FaceNeighborCountFieldInput final : public bke::MeshFieldInput {
public:
FaceNeighborCountFieldInput()
- : GeometryFieldInput(CPPType::get<int>(), "Face Neighbor Count Field")
+ : bke::MeshFieldInput(CPPType::get<int>(), "Face Neighbor Count Field")
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const Mesh &mesh,
const eAttrDomain 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 {};
+ return construct_neighbor_count_varray(mesh, domain);
}
uint64_t hash() const override
@@ -75,37 +68,28 @@ class FaceNeighborCountFieldInput final : public GeometryFieldInput {
}
};
-static VArray<int> construct_vertex_count_gvarray(const MeshComponent &component,
- const eAttrDomain domain)
+static VArray<int> construct_vertex_count_varray(const Mesh &mesh, const eAttrDomain 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; }),
+ const Span<MPoly> polys = mesh.polys();
+ return mesh.attributes().adapt_domain<int>(
+ VArray<int>::ForFunc(polys.size(),
+ [polys](const int i) -> float { return polys[i].totloop; }),
ATTR_DOMAIN_FACE,
domain);
}
-class FaceVertexCountFieldInput final : public GeometryFieldInput {
+class FaceVertexCountFieldInput final : public bke::MeshFieldInput {
public:
- FaceVertexCountFieldInput() : GeometryFieldInput(CPPType::get<int>(), "Vertex Count Field")
+ FaceVertexCountFieldInput() : bke::MeshFieldInput(CPPType::get<int>(), "Vertex Count Field")
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const Mesh &mesh,
const eAttrDomain 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 {};
+ return construct_vertex_count_varray(mesh, domain);
}
uint64_t hash() const override
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
index bd57924d685..9d7735e707d 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc
@@ -22,39 +22,32 @@ static void node_declare(NodeDeclarationBuilder &b)
.description(N_("The total number of mesh islands"));
}
-class IslandFieldInput final : public GeometryFieldInput {
+class IslandFieldInput final : public bke::MeshFieldInput {
public:
- IslandFieldInput() : GeometryFieldInput(CPPType::get<int>(), "Island Index")
+ IslandFieldInput() : bke::MeshFieldInput(CPPType::get<int>(), "Island Index")
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const Mesh &mesh,
const eAttrDomain 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 {};
- }
+ const Span<MEdge> edges = mesh.edges();
- DisjointSet islands(mesh->totvert);
- for (const int i : IndexRange(mesh->totedge)) {
- islands.join(mesh->medge[i].v1, mesh->medge[i].v2);
+ DisjointSet islands(mesh.totvert);
+ for (const int i : edges.index_range()) {
+ islands.join(edges[i].v1, edges[i].v2);
}
- Array<int> output(mesh->totvert);
+ Array<int> output(mesh.totvert);
VectorSet<int> ordered_roots;
- for (const int i : IndexRange(mesh->totvert)) {
+ 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>(
+ return mesh.attributes().adapt_domain<int>(
VArray<int>::ForContainer(std::move(output)), ATTR_DOMAIN_POINT, domain);
}
@@ -70,38 +63,31 @@ class IslandFieldInput final : public GeometryFieldInput {
}
};
-class IslandCountFieldInput final : public GeometryFieldInput {
+class IslandCountFieldInput final : public bke::MeshFieldInput {
public:
- IslandCountFieldInput() : GeometryFieldInput(CPPType::get<int>(), "Island Count")
+ IslandCountFieldInput() : bke::MeshFieldInput(CPPType::get<int>(), "Island Count")
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const Mesh &mesh,
const eAttrDomain 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 {};
- }
+ const Span<MEdge> edges = mesh.edges();
- DisjointSet islands(mesh->totvert);
- for (const int i : IndexRange(mesh->totedge)) {
- islands.join(mesh->medge[i].v1, mesh->medge[i].v2);
+ DisjointSet islands(mesh.totvert);
+ for (const int i : edges.index_range()) {
+ islands.join(edges[i].v1, edges[i].v2);
}
Set<int> island_list;
- for (const int i_vert : IndexRange(mesh->totvert)) {
+ 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_num(domain));
+ return VArray<int>::ForSingle(island_list.size(), mesh.attributes().domain_size(domain));
}
uint64_t hash() const override
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
index 62b3f9d0e92..ab44a6c8515 100644
--- 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
@@ -20,41 +20,32 @@ static void node_declare(NodeDeclarationBuilder &b)
.description(N_("Number of faces that contain the vertex"));
}
-static VArray<int> construct_vertex_count_gvarray(const MeshComponent &component,
- const eAttrDomain domain)
+static VArray<int> construct_vertex_count_gvarray(const Mesh &mesh, const eAttrDomain domain)
{
- const Mesh *mesh = component.get_for_read();
- if (mesh == nullptr) {
- return {};
- }
-
+ const Span<MEdge> edges = mesh.edges();
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]++;
+ Array<int> counts(mesh.totvert, 0);
+ for (const int i : edges.index_range()) {
+ counts[edges[i].v1]++;
+ counts[edges[i].v2]++;
}
- return VArray<int>::ForContainer(std::move(vertices));
+ return VArray<int>::ForContainer(std::move(counts));
}
return {};
}
-class VertexCountFieldInput final : public GeometryFieldInput {
+class VertexCountFieldInput final : public bke::MeshFieldInput {
public:
- VertexCountFieldInput() : GeometryFieldInput(CPPType::get<int>(), "Vertex Count Field")
+ VertexCountFieldInput() : bke::MeshFieldInput(CPPType::get<int>(), "Vertex Count Field")
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const Mesh &mesh,
const eAttrDomain 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 {};
+ return construct_vertex_count_gvarray(mesh, domain);
}
uint64_t hash() const override
@@ -69,18 +60,13 @@ class VertexCountFieldInput final : public GeometryFieldInput {
}
};
-static VArray<int> construct_face_count_gvarray(const MeshComponent &component,
- const eAttrDomain domain)
+static VArray<int> construct_face_count_gvarray(const Mesh &mesh, const eAttrDomain domain)
{
- const Mesh *mesh = component.get_for_read();
- if (mesh == nullptr) {
- return {};
- }
-
+ const Span<MLoop> loops = mesh.loops();
if (domain == ATTR_DOMAIN_POINT) {
- Array<int> vertices(mesh->totvert, 0);
- for (const int i : IndexRange(mesh->totloop)) {
- int vertex = mesh->mloop[i].v;
+ Array<int> vertices(mesh.totvert, 0);
+ for (const int i : loops.index_range()) {
+ int vertex = loops[i].v;
vertices[vertex]++;
}
return VArray<int>::ForContainer(std::move(vertices));
@@ -88,22 +74,18 @@ static VArray<int> construct_face_count_gvarray(const MeshComponent &component,
return {};
}
-class VertexFaceCountFieldInput final : public GeometryFieldInput {
+class VertexFaceCountFieldInput final : public bke::MeshFieldInput {
public:
- VertexFaceCountFieldInput() : GeometryFieldInput(CPPType::get<int>(), "Vertex Face Count Field")
+ VertexFaceCountFieldInput() : bke::MeshFieldInput(CPPType::get<int>(), "Vertex Face Count Field")
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const Mesh &mesh,
const eAttrDomain 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 {};
+ return construct_face_count_gvarray(mesh, domain);
}
uint64_t hash() const override
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_shortest_edge_paths.cc b/source/blender/nodes/geometry/nodes/node_geo_input_shortest_edge_paths.cc
new file mode 100644
index 00000000000..a54daabde3b
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_shortest_edge_paths.cc
@@ -0,0 +1,236 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include <queue>
+
+#include "BLI_map.hh"
+#include "BLI_math_vec_types.hh"
+#include "BLI_set.hh"
+#include "BLI_task.hh"
+
+#include "BKE_mesh.h"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_input_shortest_edge_paths_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Bool>(N_("End Vertex")).default_value(false).hide_value().supports_field();
+ b.add_input<decl::Float>(N_("Edge Cost")).default_value(1.0f).hide_value().supports_field();
+ b.add_output<decl::Int>(N_("Next Vertex Index")).field_source();
+ b.add_output<decl::Float>(N_("Total Cost")).field_source();
+}
+
+typedef std::pair<float, int> VertPriority;
+
+struct EdgeVertMap {
+ Array<Vector<int>> edges_by_vertex_map;
+
+ EdgeVertMap(const Mesh &mesh)
+ {
+ const Span<MEdge> edges = mesh.edges();
+ edges_by_vertex_map.reinitialize(mesh.totvert);
+ for (const int edge_i : edges.index_range()) {
+ const MEdge &edge = edges[edge_i];
+ edges_by_vertex_map[edge.v1].append(edge_i);
+ edges_by_vertex_map[edge.v2].append(edge_i);
+ }
+ }
+};
+
+static void shortest_paths(const Mesh &mesh,
+ EdgeVertMap &maps,
+ const IndexMask end_selection,
+ const VArray<float> &input_cost,
+ MutableSpan<int> r_next_index,
+ MutableSpan<float> r_cost)
+{
+ const Span<MEdge> edges = mesh.edges();
+ Array<bool> visited(mesh.totvert, false);
+
+ std::priority_queue<VertPriority, std::vector<VertPriority>, std::greater<VertPriority>> queue;
+
+ for (const int start_vert_i : end_selection) {
+ r_cost[start_vert_i] = 0.0f;
+ queue.emplace(0.0f, start_vert_i);
+ }
+
+ while (!queue.empty()) {
+ const float cost_i = queue.top().first;
+ const int vert_i = queue.top().second;
+ queue.pop();
+ if (visited[vert_i]) {
+ continue;
+ }
+ visited[vert_i] = true;
+ const Span<int> incident_edge_indices = maps.edges_by_vertex_map[vert_i];
+ for (const int edge_i : incident_edge_indices) {
+ const MEdge &edge = edges[edge_i];
+ const int neighbor_vert_i = edge.v1 + edge.v2 - vert_i;
+ if (visited[neighbor_vert_i]) {
+ continue;
+ }
+ const float edge_cost = std::max(0.0f, input_cost[edge_i]);
+ const float new_neighbour_cost = cost_i + edge_cost;
+ if (new_neighbour_cost < r_cost[neighbor_vert_i]) {
+ r_cost[neighbor_vert_i] = new_neighbour_cost;
+ r_next_index[neighbor_vert_i] = vert_i;
+ queue.emplace(new_neighbour_cost, neighbor_vert_i);
+ }
+ }
+ }
+}
+
+class ShortestEdgePathsNextVertFieldInput final : public bke::MeshFieldInput {
+ private:
+ Field<bool> end_selection_;
+ Field<float> cost_;
+
+ public:
+ ShortestEdgePathsNextVertFieldInput(Field<bool> end_selection, Field<float> cost)
+ : bke::MeshFieldInput(CPPType::get<int>(), "Shortest Edge Paths Next Vertex Field"),
+ end_selection_(end_selection),
+ cost_(cost)
+ {
+ category_ = Category::Generated;
+ }
+
+ GVArray get_varray_for_context(const Mesh &mesh,
+ const eAttrDomain domain,
+ const IndexMask /*mask*/) const final
+ {
+ bke::MeshFieldContext edge_context{mesh, ATTR_DOMAIN_EDGE};
+ fn::FieldEvaluator edge_evaluator{edge_context, mesh.totedge};
+ edge_evaluator.add(cost_);
+ edge_evaluator.evaluate();
+ const VArray<float> input_cost = edge_evaluator.get_evaluated<float>(0);
+
+ bke::MeshFieldContext point_context{mesh, ATTR_DOMAIN_POINT};
+ fn::FieldEvaluator point_evaluator{point_context, mesh.totvert};
+ point_evaluator.add(end_selection_);
+ point_evaluator.evaluate();
+ const IndexMask end_selection = point_evaluator.get_evaluated_as_mask(0);
+
+ Array<int> next_index(mesh.totvert, -1);
+ Array<float> cost(mesh.totvert, FLT_MAX);
+
+ if (!end_selection.is_empty()) {
+ EdgeVertMap maps(mesh);
+ shortest_paths(mesh, maps, end_selection, input_cost, next_index, cost);
+ }
+ threading::parallel_for(next_index.index_range(), 1024, [&](const IndexRange range) {
+ for (const int i : range) {
+ if (next_index[i] == -1) {
+ next_index[i] = i;
+ }
+ }
+ });
+ return mesh.attributes().adapt_domain<int>(
+ VArray<int>::ForContainer(std::move(next_index)), ATTR_DOMAIN_POINT, domain);
+ }
+
+ uint64_t hash() const override
+ {
+ /* Some random constant hash. */
+ return 8466507837;
+ }
+
+ bool is_equal_to(const fn::FieldNode &other) const override
+ {
+ if (const ShortestEdgePathsNextVertFieldInput *other_field =
+ dynamic_cast<const ShortestEdgePathsNextVertFieldInput *>(&other)) {
+ return other_field->end_selection_ == end_selection_ && other_field->cost_ == cost_;
+ }
+ return false;
+ }
+};
+
+class ShortestEdgePathsCostFieldInput final : public bke::MeshFieldInput {
+ private:
+ Field<bool> end_selection_;
+ Field<float> cost_;
+
+ public:
+ ShortestEdgePathsCostFieldInput(Field<bool> end_selection, Field<float> cost)
+ : bke::MeshFieldInput(CPPType::get<float>(), "Shortest Edge Paths Cost Field"),
+ end_selection_(end_selection),
+ cost_(cost)
+ {
+ category_ = Category::Generated;
+ }
+
+ GVArray get_varray_for_context(const Mesh &mesh,
+ const eAttrDomain domain,
+ const IndexMask /*mask*/) const final
+ {
+ bke::MeshFieldContext edge_context{mesh, ATTR_DOMAIN_EDGE};
+ fn::FieldEvaluator edge_evaluator{edge_context, mesh.totedge};
+ edge_evaluator.add(cost_);
+ edge_evaluator.evaluate();
+ const VArray<float> input_cost = edge_evaluator.get_evaluated<float>(0);
+
+ bke::MeshFieldContext point_context{mesh, ATTR_DOMAIN_POINT};
+ fn::FieldEvaluator point_evaluator{point_context, mesh.totvert};
+ point_evaluator.add(end_selection_);
+ point_evaluator.evaluate();
+ const IndexMask end_selection = point_evaluator.get_evaluated_as_mask(0);
+
+ Array<int> next_index(mesh.totvert, -1);
+ Array<float> cost(mesh.totvert, FLT_MAX);
+
+ if (!end_selection.is_empty()) {
+ EdgeVertMap maps(mesh);
+ shortest_paths(mesh, maps, end_selection, input_cost, next_index, cost);
+ }
+ threading::parallel_for(cost.index_range(), 1024, [&](const IndexRange range) {
+ for (const int i : range) {
+ if (cost[i] == FLT_MAX) {
+ cost[i] = 0;
+ }
+ }
+ });
+ return mesh.attributes().adapt_domain<float>(
+ VArray<float>::ForContainer(std::move(cost)), ATTR_DOMAIN_POINT, domain);
+ }
+
+ uint64_t hash() const override
+ {
+ return get_default_hash_2(end_selection_, cost_);
+ }
+
+ bool is_equal_to(const fn::FieldNode &other) const override
+ {
+ if (const ShortestEdgePathsCostFieldInput *other_field =
+ dynamic_cast<const ShortestEdgePathsCostFieldInput *>(&other)) {
+ return other_field->end_selection_ == end_selection_ && other_field->cost_ == cost_;
+ }
+ return false;
+ }
+};
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ Field<bool> end_selection = params.extract_input<Field<bool>>("End Vertex");
+ Field<float> cost = params.extract_input<Field<float>>("Edge Cost");
+
+ Field<int> next_vert_field{
+ std::make_shared<ShortestEdgePathsNextVertFieldInput>(end_selection, cost)};
+ Field<float> cost_field{std::make_shared<ShortestEdgePathsCostFieldInput>(end_selection, cost)};
+ params.set_output("Next Vertex Index", std::move(next_vert_field));
+ params.set_output("Total Cost", std::move(cost_field));
+}
+
+} // namespace blender::nodes::node_geo_input_shortest_edge_paths_cc
+
+void register_node_type_geo_input_shortest_edge_paths()
+{
+ namespace file_ns = blender::nodes::node_geo_input_shortest_edge_paths_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_INPUT_SHORTEST_EDGE_PATHS, "Shortest Edge Paths", 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_spline_length.cc b/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc
index def82eefca5..07dc158ff48 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc
@@ -16,15 +16,9 @@ static void node_declare(NodeDeclarationBuilder &b)
* Spline Count
*/
-static VArray<int> construct_curve_point_count_gvarray(const CurveComponent &component,
+static VArray<int> construct_curve_point_count_gvarray(const bke::CurvesGeometry &curves,
const eAttrDomain domain)
{
- if (!component.has_curves()) {
- return {};
- }
- const Curves &curves_id = *component.get_for_read();
- const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
-
auto count_fn = [curves](int64_t i) { return curves.points_for_curve(i).size(); };
if (domain == ATTR_DOMAIN_CURVE) {
@@ -32,29 +26,24 @@ static VArray<int> construct_curve_point_count_gvarray(const CurveComponent &com
}
if (domain == ATTR_DOMAIN_POINT) {
VArray<int> count = VArray<int>::ForFunc(curves.curves_num(), count_fn);
- return component.attribute_try_adapt_domain<int>(
- std::move(count), ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT);
+ return curves.adapt_domain<int>(std::move(count), ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT);
}
return {};
}
-class SplineCountFieldInput final : public GeometryFieldInput {
+class SplineCountFieldInput final : public bke::CurvesFieldInput {
public:
- SplineCountFieldInput() : GeometryFieldInput(CPPType::get<int>(), "Spline Point Count")
+ SplineCountFieldInput() : bke::CurvesFieldInput(CPPType::get<int>(), "Spline Point Count")
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const bke::CurvesGeometry &curves,
const eAttrDomain domain,
IndexMask UNUSED(mask)) const final
{
- if (component.type() == GEO_COMPONENT_TYPE_CURVE) {
- const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
- return construct_curve_point_count_gvarray(curve_component, domain);
- }
- return {};
+ return construct_curve_point_count_gvarray(curves, domain);
}
uint64_t hash() const override
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 f5831941094..ea3d060f03c 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc
@@ -63,19 +63,12 @@ static Array<float3> curve_tangent_point_domain(const bke::CurvesGeometry &curve
return results;
}
-static VArray<float3> construct_curve_tangent_gvarray(const CurveComponent &component,
+static VArray<float3> construct_curve_tangent_gvarray(const bke::CurvesGeometry &curves,
const eAttrDomain domain)
{
- if (!component.has_curves()) {
- return {};
- }
-
- const Curves &curves_id = *component.get_for_read();
- const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
-
const VArray<int8_t> types = curves.curve_types();
if (curves.is_single_type(CURVE_TYPE_POLY)) {
- return component.attribute_try_adapt_domain<float3>(
+ return curves.adapt_domain<float3>(
VArray<float3>::ForSpan(curves.evaluated_tangents()), ATTR_DOMAIN_POINT, domain);
}
@@ -86,29 +79,25 @@ static VArray<float3> construct_curve_tangent_gvarray(const CurveComponent &comp
}
if (domain == ATTR_DOMAIN_CURVE) {
- return component.attribute_try_adapt_domain<float3>(
+ return curves.adapt_domain<float3>(
VArray<float3>::ForContainer(std::move(tangents)), ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE);
}
return nullptr;
}
-class TangentFieldInput final : public GeometryFieldInput {
+class TangentFieldInput final : public bke::CurvesFieldInput {
public:
- TangentFieldInput() : GeometryFieldInput(CPPType::get<float3>(), "Tangent node")
+ TangentFieldInput() : bke::CurvesFieldInput(CPPType::get<float3>(), "Tangent node")
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const bke::CurvesGeometry &curves,
const eAttrDomain domain,
IndexMask UNUSED(mask)) const final
{
- 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 {};
+ return construct_curve_tangent_gvarray(curves, domain);
}
uint64_t hash() const override
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 21ef8765e43..d54d082311f 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
@@ -50,14 +50,14 @@ static void add_instances_from_component(
const Map<AttributeIDRef, AttributeKind> &attributes_to_propagate)
{
const eAttrDomain domain = ATTR_DOMAIN_POINT;
- const int domain_num = src_component.attribute_domain_num(domain);
+ const int domain_num = 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};
+ bke::GeometryFieldContext field_context{src_component, domain};
const Field<bool> selection_field = params.get_input<Field<bool>>("Selection");
fn::FieldEvaluator evaluator{field_context, domain_num};
evaluator.set_selection(selection_field);
@@ -70,6 +70,9 @@ static void add_instances_from_component(
evaluator.evaluate();
const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
+ if (selection.is_empty()) {
+ return;
+ }
/* The initial size of the component might be non-zero when this function is called for multiple
* component types. */
@@ -82,7 +85,7 @@ static void add_instances_from_component(
MutableSpan<float4x4> dst_transforms = dst_component.instance_transforms().slice(start_len,
select_len);
- VArray<float3> positions = src_component.attribute_get_for_read<float3>(
+ VArray<float3> positions = src_component.attributes()->lookup_or_default<float3>(
"position", domain, {0, 0, 0});
const InstancesComponent *src_instances = instance.get_component_for_read<InstancesComponent>();
@@ -154,12 +157,12 @@ static void add_instances_from_component(
}
}
- bke::CustomDataAttributes &instance_attributes = dst_component.attributes();
+ bke::CustomDataAttributes &instance_attributes = dst_component.instance_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(
+ const GVArray src_attribute = src_component.attributes()->lookup_or_default(
attribute_id, ATTR_DOMAIN_POINT, attribute_kind.data_type);
BLI_assert(src_attribute);
std::optional<GMutableSpan> dst_attribute_opt = instance_attributes.get_for_write(
@@ -213,7 +216,7 @@ static void node_geo_exec(GeoNodeExecParams params)
}
}
- geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ geometry_set.remove_geometry_during_modify();
});
/* Unused references may have been added above. Remove those now so that other nodes don't
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
index 2126a5cc329..ec2f1b00e6c 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc
@@ -22,16 +22,6 @@ static void node_declare(NodeDeclarationBuilder &b)
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,
@@ -39,10 +29,8 @@ static void convert_instances_to_points(GeometrySet &geometry_set,
{
const InstancesComponent &instances = *geometry_set.get_component_for_read<InstancesComponent>();
- GeometryComponentFieldContext field_context{instances, ATTR_DOMAIN_INSTANCE};
- const int domain_num = instances.attribute_domain_num(ATTR_DOMAIN_INSTANCE);
-
- fn::FieldEvaluator evaluator{field_context, domain_num};
+ const bke::InstancesFieldContext context{instances};
+ fn::FieldEvaluator evaluator{context, instances.instances_num()};
evaluator.set_selection(std::move(selection_field));
evaluator.add(std::move(position_field));
evaluator.add(std::move(radius_field));
@@ -51,16 +39,23 @@ static void convert_instances_to_points(GeometrySet &geometry_set,
if (selection.is_empty()) {
return;
}
+ const VArray<float3> &positions = evaluator.get_evaluated<float3>(0);
+ const VArray<float> radii = evaluator.get_evaluated<float>(1);
PointCloud *pointcloud = BKE_pointcloud_new_nomain(selection.size());
geometry_set.replace_pointcloud(pointcloud);
- PointCloudComponent &points = geometry_set.get_component_for_write<PointCloudComponent>();
+ bke::MutableAttributeAccessor point_attributes = pointcloud->attributes_for_write();
- 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});
+ bke::SpanAttributeWriter<float3> point_positions =
+ point_attributes.lookup_or_add_for_write_only_span<float3>("position", ATTR_DOMAIN_POINT);
+ bke::SpanAttributeWriter<float> point_radii =
+ point_attributes.lookup_or_add_for_write_only_span<float>("radius", ATTR_DOMAIN_POINT);
+
+ positions.materialize_compressed_to_uninitialized(selection, point_positions.span);
+ radii.materialize_compressed_to_uninitialized(selection, point_radii.span);
+ point_positions.finish();
+ point_radii.finish();
Map<AttributeIDRef, AttributeKind> attributes_to_propagate;
geometry_set.gather_attributes_for_propagation({GEO_COMPONENT_TYPE_INSTANCES},
@@ -75,18 +70,15 @@ static void convert_instances_to_points(GeometrySet &geometry_set,
const AttributeIDRef &attribute_id = item.key;
const AttributeKind attribute_kind = item.value;
- const GVArray src = instances.attribute_get_for_read(
+ const GVArray src = instances.attributes()->lookup_or_default(
attribute_id, ATTR_DOMAIN_INSTANCE, attribute_kind.data_type);
BLI_assert(src);
- OutputAttribute dst = points.attribute_try_get_for_output_only(
+ GSpanAttributeWriter dst = point_attributes.lookup_or_add_for_write_only_span(
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();
+ src.materialize_compressed_to_uninitialized(selection, dst.span.data());
+ dst.finish();
}
}
@@ -99,7 +91,7 @@ static void node_geo_exec(GeoNodeExecParams params)
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});
+ geometry_set.keep_only({GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_EDIT});
params.set_output("Points", std::move(geometry_set));
}
else {
diff --git a/source/blender/nodes/geometry/nodes/node_geo_interpolate_domain.cc b/source/blender/nodes/geometry/nodes/node_geo_interpolate_domain.cc
new file mode 100644
index 00000000000..8e38ef14aba
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_interpolate_domain.cc
@@ -0,0 +1,167 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "node_geometry_util.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "BKE_attribute_math.hh"
+
+#include "BLI_task.hh"
+
+#include "NOD_socket_search_link.hh"
+
+namespace blender::nodes::node_geo_interpolate_domain_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ 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 eCustomDataType data_type = static_cast<eCustomDataType>(node->custom2);
+
+ bNodeSocket *sock_in_float = static_cast<bNodeSocket *>(node->inputs.first);
+ 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);
+}
+
+static void node_gather_link_searches(GatherLinkSearchOpParams &params)
+{
+ const bNodeType &node_type = params.node_type();
+ const std::optional<eCustomDataType> type = node_data_type_to_custom_data_type(
+ (eNodeSocketDatatype)params.other_socket().type);
+ if (type && *type != CD_PROP_STRING) {
+ params.add_item(IFACE_("Value"), [node_type, type](LinkSearchOpParams &params) {
+ bNode &node = params.add_node(node_type);
+ node.custom2 = *type;
+ params.update_and_connect_available_socket(node, "Value");
+ });
+ }
+}
+
+class InterpolateDomain final : public bke::GeometryFieldInput {
+ private:
+ GField src_field_;
+ eAttrDomain src_domain_;
+
+ public:
+ InterpolateDomain(GField field, eAttrDomain domain)
+ : bke::GeometryFieldInput(field.cpp_type(), "Interpolate Domain"),
+ src_field_(std::move(field)),
+ src_domain_(domain)
+ {
+ }
+
+ GVArray get_varray_for_context(const bke::GeometryFieldContext &context,
+ IndexMask /*mask*/) const final
+ {
+ const bke::AttributeAccessor attributes = *context.attributes();
+
+ const bke::GeometryFieldContext other_domain_context{
+ context.geometry(), context.type(), src_domain_};
+ const int64_t src_domain_size = attributes.domain_size(src_domain_);
+ GArray values(src_field_.cpp_type(), src_domain_size);
+ FieldEvaluator value_evaluator{other_domain_context, src_domain_size};
+ value_evaluator.add_with_destination(src_field_, values.as_mutable_span());
+ value_evaluator.evaluate();
+ return attributes.adapt_domain(
+ GVArray::ForGArray(std::move(values)), src_domain_, context.domain());
+ }
+};
+
+static StringRefNull identifier_suffix(eCustomDataType 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 eAttrDomain domain = static_cast<eAttrDomain>(node.custom1);
+ const eCustomDataType data_type = static_cast<eCustomDataType>(node.custom2);
+
+ 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> src_field = params.extract_input<Field<T>>(identifier);
+ Field<T> dst_field{std::make_shared<InterpolateDomain>(std::move(src_field), domain)};
+ params.set_output(identifier, std::move(dst_field));
+ });
+}
+
+} // namespace blender::nodes::node_geo_interpolate_domain_cc
+
+void register_node_type_geo_interpolate_domain()
+{
+ namespace file_ns = blender::nodes::node_geo_interpolate_domain_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_INTERPOLATE_DOMAIN, "Interpolate Domain", 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;
+ ntype.gather_link_search_ops = file_ns::node_gather_link_searches;
+ 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 0c56b0e9804..023d7a32a61 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
@@ -24,7 +24,7 @@ static Map<AttributeIDRef, AttributeMetaData> get_final_attribute_info(
Map<AttributeIDRef, AttributeMetaData> info;
for (const GeometryComponent *component : components) {
- component->attribute_foreach(
+ component->attributes()->for_all(
[&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
if (attribute_id.is_named() && ignored_attributes.contains(attribute_id.name())) {
return true;
@@ -56,14 +56,14 @@ static void fill_new_attribute(Span<const GeometryComponent *> src_components,
int offset = 0;
for (const GeometryComponent *component : src_components) {
- const int domain_num = component->attribute_domain_num(domain);
+ const int domain_num = component->attribute_domain_size(domain);
if (domain_num == 0) {
continue;
}
- GVArray read_attribute = component->attribute_get_for_read(
+ GVArray read_attribute = component->attributes()->lookup_or_default(
attribute_id, domain, data_type, nullptr);
- GVArray_GSpan src_span{read_attribute};
+ GVArraySpan 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_num);
@@ -83,15 +83,15 @@ static void join_attributes(Span<const GeometryComponent *> src_components,
const AttributeIDRef attribute_id = item.key;
const AttributeMetaData &meta_data = item.value;
- OutputAttribute write_attribute = result.attribute_try_get_for_output_only(
- attribute_id, meta_data.domain, meta_data.data_type);
+ GSpanAttributeWriter write_attribute =
+ result.attributes_for_write()->lookup_or_add_for_write_only_span(
+ attribute_id, meta_data.domain, meta_data.data_type);
if (!write_attribute) {
continue;
}
- GMutableSpan dst_span = write_attribute.as_span();
fill_new_attribute(
- src_components, attribute_id, meta_data.data_type, meta_data.domain, dst_span);
- write_attribute.save();
+ src_components, attribute_id, meta_data.data_type, meta_data.domain, write_attribute.span);
+ write_attribute.finish();
}
}
@@ -185,6 +185,7 @@ static void node_geo_exec(GeoNodeExecParams params)
join_component_type<InstancesComponent>(geometry_sets, geometry_set_result);
join_component_type<VolumeComponent>(geometry_sets, geometry_set_result);
join_component_type<CurveComponent>(geometry_sets, geometry_set_result);
+ join_component_type<GeometryComponentEditData>(geometry_sets, geometry_set_result);
params.set_output("Geometry", std::move(geometry_set_result));
}
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 5875606da97..628688f3b47 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc
@@ -23,46 +23,56 @@ static void node_declare(NodeDeclarationBuilder &b)
static void select_mesh_by_material(const Mesh &mesh,
const Material *material,
const IndexMask mask,
- const MutableSpan<bool> r_selection)
+ MutableSpan<bool> r_selection)
{
BLI_assert(mesh.totpoly >= r_selection.size());
- Vector<int> material_indices;
+ Vector<int> slots;
for (const int i : IndexRange(mesh.totcol)) {
if (mesh.mat[i] == material) {
- material_indices.append(i);
+ slots.append(i);
}
}
+ const AttributeAccessor attributes = mesh.attributes();
+ const VArray<int> material_indices = attributes.lookup_or_default<int>(
+ "material_index", ATTR_DOMAIN_FACE, 0);
+ if (material != nullptr && material_indices.is_single() &&
+ material_indices.get_internal_single() == 0) {
+ r_selection.fill_indices(mask, false);
+ return;
+ }
+
+ const VArraySpan<int> material_indices_span(material_indices);
+
threading::parallel_for(mask.index_range(), 1024, [&](IndexRange range) {
for (const int i : range) {
const int face_index = mask[i];
- r_selection[i] = material_indices.contains(mesh.mpoly[face_index].mat_nr);
+ r_selection[i] = slots.contains(material_indices_span[face_index]);
}
});
}
-class MaterialSelectionFieldInput final : public GeometryFieldInput {
+class MaterialSelectionFieldInput final : public bke::GeometryFieldInput {
Material *material_;
public:
MaterialSelectionFieldInput(Material *material)
- : GeometryFieldInput(CPPType::get<bool>(), "Material Selection node"), material_(material)
+ : bke::GeometryFieldInput(CPPType::get<bool>(), "Material Selection node"),
+ material_(material)
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
- const eAttrDomain domain,
- IndexMask mask) const final
+ GVArray get_varray_for_context(const bke::GeometryFieldContext &context,
+ const IndexMask mask) const final
{
- if (component.type() != GEO_COMPONENT_TYPE_MESH) {
+ if (context.type() != GEO_COMPONENT_TYPE_MESH) {
return {};
}
- const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
- const Mesh *mesh = mesh_component.get_for_read();
+ const Mesh *mesh = context.mesh();
if (mesh == nullptr) {
return {};
}
-
+ const eAttrDomain domain = context.domain();
if (domain == ATTR_DOMAIN_FACE) {
Array<bool> selection(mask.min_array_size());
select_mesh_by_material(*mesh, material_, mask, selection);
@@ -71,7 +81,7 @@ class MaterialSelectionFieldInput final : public GeometryFieldInput {
Array<bool> selection(mesh->totpoly);
select_mesh_by_material(*mesh, material_, IndexMask(mesh->totpoly), selection);
- return mesh_component.attribute_try_adapt_domain<bool>(
+ return mesh->attributes().adapt_domain<bool>(
VArray<bool>::ForContainer(std::move(selection)), ATTR_DOMAIN_FACE, domain);
return nullptr;
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
index 1def4089115..f64f997810e 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc
@@ -1,5 +1,8 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include "DNA_mesh_types.h"
+#include "DNA_pointcloud_types.h"
+
#include "GEO_mesh_merge_by_distance.hh"
#include "GEO_point_merge_by_distance.hh"
@@ -35,13 +38,12 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node)
node->storage = data;
}
-static PointCloud *pointcloud_merge_by_distance(const PointCloudComponent &src_points,
+static PointCloud *pointcloud_merge_by_distance(const PointCloud &src_points,
const float merge_distance,
const Field<bool> &selection_field)
{
- const int src_num = src_points.attribute_domain_num(ATTR_DOMAIN_POINT);
- GeometryComponentFieldContext context{src_points, ATTR_DOMAIN_POINT};
- FieldEvaluator evaluator{context, src_num};
+ bke::PointCloudFieldContext context{src_points};
+ FieldEvaluator evaluator{context, src_points.totpoint};
evaluator.add(selection_field);
evaluator.evaluate();
@@ -53,28 +55,25 @@ static PointCloud *pointcloud_merge_by_distance(const PointCloudComponent &src_p
return geometry::point_merge_by_distance(src_points, merge_distance, selection);
}
-static std::optional<Mesh *> mesh_merge_by_distance_connected(const MeshComponent &mesh_component,
+static std::optional<Mesh *> mesh_merge_by_distance_connected(const Mesh &mesh,
const float merge_distance,
const Field<bool> &selection_field)
{
- const int src_num = mesh_component.attribute_domain_num(ATTR_DOMAIN_POINT);
- Array<bool> selection(src_num);
- GeometryComponentFieldContext context{mesh_component, ATTR_DOMAIN_POINT};
- FieldEvaluator evaluator{context, src_num};
+ Array<bool> selection(mesh.totvert);
+ bke::MeshFieldContext context{mesh, ATTR_DOMAIN_POINT};
+ FieldEvaluator evaluator{context, mesh.totvert};
evaluator.add_with_destination(selection_field, selection.as_mutable_span());
evaluator.evaluate();
- const Mesh &mesh = *mesh_component.get_for_read();
return geometry::mesh_merge_by_distance_connected(mesh, selection, merge_distance, false);
}
-static std::optional<Mesh *> mesh_merge_by_distance_all(const MeshComponent &mesh_component,
+static std::optional<Mesh *> mesh_merge_by_distance_all(const Mesh &mesh,
const float merge_distance,
const Field<bool> &selection_field)
{
- const int src_num = mesh_component.attribute_domain_num(ATTR_DOMAIN_POINT);
- GeometryComponentFieldContext context{mesh_component, ATTR_DOMAIN_POINT};
- FieldEvaluator evaluator{context, src_num};
+ bke::MeshFieldContext context{mesh, ATTR_DOMAIN_POINT};
+ FieldEvaluator evaluator{context, mesh.totvert};
evaluator.add(selection_field);
evaluator.evaluate();
@@ -83,7 +82,6 @@ static std::optional<Mesh *> mesh_merge_by_distance_all(const MeshComponent &mes
return std::nullopt;
}
- const Mesh &mesh = *mesh_component.get_for_read();
return geometry::mesh_merge_by_distance_all(mesh, selection, merge_distance);
}
@@ -98,22 +96,20 @@ static void node_geo_exec(GeoNodeExecParams params)
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);
+ if (const PointCloud *pointcloud = geometry_set.get_pointcloud_for_read()) {
+ PointCloud *result = pointcloud_merge_by_distance(*pointcloud, merge_distance, selection);
if (result) {
geometry_set.replace_pointcloud(result);
}
}
- if (geometry_set.has_mesh()) {
- const MeshComponent &component = *geometry_set.get_component_for_read<MeshComponent>();
+ if (const Mesh *mesh = geometry_set.get_mesh_for_read()) {
std::optional<Mesh *> result;
switch (mode) {
case GEO_NODE_MERGE_BY_DISTANCE_MODE_ALL:
- result = mesh_merge_by_distance_all(component, merge_distance, selection);
+ result = mesh_merge_by_distance_all(*mesh, merge_distance, selection);
break;
case GEO_NODE_MERGE_BY_DISTANCE_MODE_CONNECTED:
- result = mesh_merge_by_distance_connected(component, merge_distance, selection);
+ result = mesh_merge_by_distance_connected(*mesh, merge_distance, selection);
break;
default:
BLI_assert_unreachable();
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 9e85547315c..801b3c78060 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
@@ -109,10 +109,10 @@ static Mesh *create_circle_mesh(const float radius,
circle_corner_total(fill_type, verts_num),
circle_face_total(fill_type, verts_num));
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};
+ MutableSpan<MVert> verts = mesh->verts_for_write();
+ MutableSpan<MEdge> edges = mesh->edges_for_write();
+ MutableSpan<MPoly> polys = mesh->polys_for_write();
+ MutableSpan<MLoop> loops = mesh->loops_for_write();
/* Assign vertex coordinates. */
const float angle_delta = 2.0f * (M_PI / static_cast<float>(verts_num));
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 b882d4bdf09..edf14f664c5 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
@@ -255,7 +255,7 @@ int ConeConfig::calculate_total_corners()
return corner_total;
}
-static void calculate_cone_vertices(const MutableSpan<MVert> &verts, const ConeConfig &config)
+static void calculate_cone_verts(const MutableSpan<MVert> &verts, const ConeConfig &config)
{
Array<float2> circle(config.circle_segments);
const float angle_delta = 2.0f * (M_PI / static_cast<float>(config.circle_segments));
@@ -480,53 +480,49 @@ static void calculate_selection_outputs(Mesh *mesh,
const ConeConfig &config,
ConeAttributeOutputs &attribute_outputs)
{
- MeshComponent mesh_component;
- mesh_component.replace(mesh, GeometryOwnershipType::Editable);
+ MutableAttributeAccessor attributes = mesh->attributes_for_write();
/* 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>(
+ SpanAttributeWriter<bool> selection = attributes.lookup_or_add_for_write_only_span<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;
+ selection.span[config.first_vert] = true;
}
else {
- selection.slice(0, face ? config.top_faces_len : config.circle_segments).fill(true);
+ selection.span.slice(0, face ? config.top_faces_len : config.circle_segments).fill(true);
}
- attribute.save();
+ selection.finish();
}
/* 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>(
+ SpanAttributeWriter<bool> selection = attributes.lookup_or_add_for_write_only_span<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;
+ selection.span[config.last_vert] = true;
}
else if (face) {
- selection.slice(config.bottom_faces_start, config.bottom_faces_len).fill(true);
+ selection.span.slice(config.bottom_faces_start, config.bottom_faces_len).fill(true);
}
else {
- selection.slice(config.last_ring_verts_start + 1, config.circle_segments).fill(true);
+ selection.span.slice(config.last_ring_verts_start + 1, config.circle_segments).fill(true);
}
- attribute.save();
+ selection.finish();
}
/* Populate "Side" selection output. */
if (attribute_outputs.side_id) {
- OutputAttribute_Typed<bool> attribute = mesh_component.attribute_try_get_for_output_only<bool>(
+ SpanAttributeWriter<bool> selection = attributes.lookup_or_add_for_write_only_span<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();
+ selection.span.slice(config.side_faces_start, config.side_faces_len).fill(true);
+ selection.finish();
}
}
@@ -540,11 +536,11 @@ static void calculate_selection_outputs(Mesh *mesh,
*/
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();
+ MutableAttributeAccessor attributes = mesh->attributes_for_write();
+
+ SpanAttributeWriter<float2> uv_attribute = attributes.lookup_or_add_for_write_only_span<float2>(
+ "uv_map", ATTR_DOMAIN_CORNER);
+ MutableSpan<float2> uvs = uv_attribute.span;
Array<float2> circle(config.circle_segments);
float angle = 0.0f;
@@ -654,14 +650,14 @@ static void calculate_cone_uvs(Mesh *mesh, const ConeConfig &config)
}
}
- uv_attribute.save();
+ uv_attribute.finish();
}
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);
+ copy_v3_fl3(mesh->verts_for_write().first().co, 0.0f, 0.0f, 0.0f);
return mesh;
}
@@ -693,12 +689,12 @@ Mesh *create_cylinder_or_cone_mesh(const float radius_top,
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};
+ MutableSpan<MVert> verts = mesh->verts_for_write();
+ MutableSpan<MEdge> edges = mesh->edges_for_write();
+ MutableSpan<MPoly> polys = mesh->polys_for_write();
+ MutableSpan<MLoop> loops = mesh->loops_for_write();
- calculate_cone_vertices(verts, config);
+ calculate_cone_verts(verts, config);
calculate_cone_edges(edges, config);
calculate_cone_faces(loops, polys, config);
calculate_cone_uvs(mesh, config);
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 523dbd5dac2..6f0b8283b72 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
@@ -18,23 +18,22 @@ namespace blender::nodes {
static void calculate_uvs(
Mesh *mesh, Span<MVert> verts, Span<MLoop> loops, const float size_x, const float size_y)
{
- 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();
+ MutableAttributeAccessor attributes = mesh->attributes_for_write();
+
+ SpanAttributeWriter<float2> uv_attribute = attributes.lookup_or_add_for_write_only_span<float2>(
+ "uv_map", ATTR_DOMAIN_CORNER);
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;
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.span[i].x = (co.x + size_x * 0.5f) * dx;
+ uv_attribute.span[i].y = (co.y + size_y * 0.5f) * dy;
}
});
- uv_attribute.save();
+ uv_attribute.finish();
}
Mesh *create_grid_mesh(const int verts_x,
@@ -50,10 +49,10 @@ Mesh *create_grid_mesh(const int verts_x,
0,
edges_x * edges_y * 4,
edges_x * edges_y);
- 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};
+ MutableSpan<MVert> verts = mesh->verts_for_write();
+ MutableSpan<MEdge> edges = mesh->edges_for_write();
+ MutableSpan<MPoly> polys = mesh->polys_for_write();
+ MutableSpan<MLoop> loops = mesh->loops_for_write();
{
const float dx = edges_x == 0 ? 0.0f : size_x / edges_x;
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 2af86b4b1d2..4fd6399f4eb 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
@@ -179,10 +179,11 @@ Mesh *create_line_mesh(const float3 start, const float3 delta, const int count)
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};
+ MutableSpan<MVert> verts = mesh->verts_for_write();
+ MutableSpan<MEdge> edges = mesh->edges_for_write();
threading::parallel_invoke(
+ 1024 < count,
[&]() {
threading::parallel_for(verts.index_range(), 4096, [&](IndexRange range) {
for (const int i : range) {
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 4e0e5c7c912..d39e72b7f0a 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
@@ -1,5 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include "BLI_task.hh"
+
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
@@ -61,15 +63,26 @@ static int sphere_face_total(const int segments, const int rings)
* 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)
+BLI_NOINLINE static void calculate_sphere_vertex_data(MutableSpan<MVert> verts,
+ MutableSpan<float3> vert_normals,
+ const float radius,
+ const int segments,
+ const int rings)
{
const float delta_theta = M_PI / rings;
const float delta_phi = (2.0f * M_PI) / segments;
+ Array<float, 64> segment_cosines(segments + 1);
+ for (const int segment : IndexRange(1, segments)) {
+ const float phi = segment * delta_phi;
+ segment_cosines[segment] = std::cos(phi);
+ }
+ Array<float, 64> segment_sines(segments + 1);
+ for (const int segment : IndexRange(1, segments)) {
+ const float phi = segment * delta_phi;
+ segment_sines[segment] = std::sin(phi);
+ }
+
copy_v3_v3(verts[0].co, float3(0.0f, 0.0f, radius));
vert_normals.first() = float3(0.0f, 0.0f, 1.0f);
@@ -79,9 +92,8 @@ static void calculate_sphere_vertex_data(MutableSpan<MVert> verts,
const float sin_theta = std::sin(theta);
const float z = std::cos(theta);
for (const int segment : IndexRange(1, segments)) {
- const float phi = segment * delta_phi;
- const float x = sin_theta * std::cos(phi);
- const float y = sin_theta * std::sin(phi);
+ const float x = sin_theta * segment_cosines[segment];
+ const float y = sin_theta * segment_sines[segment];
copy_v3_v3(verts[vert_index].co, float3(x, y, z) * radius);
vert_normals[vert_index] = float3(x, y, z);
vert_index++;
@@ -92,9 +104,9 @@ static void calculate_sphere_vertex_data(MutableSpan<MVert> verts,
vert_normals.last() = float3(0.0f, 0.0f, -1.0f);
}
-static void calculate_sphere_edge_indices(MutableSpan<MEdge> edges,
- const int segments,
- const int rings)
+BLI_NOINLINE static void calculate_sphere_edge_indices(MutableSpan<MEdge> edges,
+ const int segments,
+ const int rings)
{
int edge_index = 0;
@@ -142,20 +154,46 @@ static void calculate_sphere_edge_indices(MutableSpan<MEdge> edges,
}
}
-static void calculate_sphere_faces(MutableSpan<MLoop> loops,
- MutableSpan<MPoly> polys,
- const int segments,
- const int rings)
+BLI_NOINLINE static void calculate_sphere_faces(MutableSpan<MPoly> polys, const int segments)
{
int loop_index = 0;
- int poly_index = 0;
/* Add the triangles connected to the top vertex. */
- const int first_vert_ring_index_start = 1;
- for (const int segment : IndexRange(segments)) {
- MPoly &poly = polys[poly_index++];
+ for (MPoly &poly : polys.take_front(segments)) {
+ poly.loopstart = loop_index;
+ poly.totloop = 3;
+ loop_index += 3;
+ }
+
+ /* Add the middle quads. */
+ for (MPoly &poly : polys.drop_front(segments).drop_back(segments)) {
+ poly.loopstart = loop_index;
+ poly.totloop = 4;
+ loop_index += 4;
+ }
+
+ /* Add the triangles connected to the bottom vertex. */
+ for (MPoly &poly : polys.take_back(segments)) {
poly.loopstart = loop_index;
poly.totloop = 3;
+ loop_index += 3;
+ }
+}
+
+BLI_NOINLINE static void calculate_sphere_corners(MutableSpan<MLoop> loops,
+ const int segments,
+ const int rings)
+{
+ int loop_index = 0;
+ auto segment_next_or_first = [&](const int segment) {
+ return segment == segments - 1 ? 0 : segment + 1;
+ };
+
+ /* Add the triangles connected to the top vertex. */
+ const int first_vert_ring_index_start = 1;
+ for (const int segment : IndexRange(segments)) {
+ const int segment_next = segment_next_or_first(segment);
+
MLoop &loop_a = loops[loop_index++];
loop_a.v = 0;
loop_a.e = segment;
@@ -163,8 +201,8 @@ static void calculate_sphere_faces(MutableSpan<MLoop> loops,
loop_b.v = first_vert_ring_index_start + segment;
loop_b.e = segments + segment;
MLoop &loop_c = loops[loop_index++];
- loop_c.v = first_vert_ring_index_start + (segment + 1) % segments;
- loop_c.e = (segment + 1) % segments;
+ loop_c.v = first_vert_ring_index_start + segment_next;
+ loop_c.e = segment_next;
}
int ring_vert_index_start = 1;
@@ -175,9 +213,7 @@ static void calculate_sphere_faces(MutableSpan<MLoop> loops,
const int ring_vertical_edge_index_start = ring_edge_index_start + segments;
for (const int segment : IndexRange(segments)) {
- MPoly &poly = polys[poly_index++];
- poly.loopstart = loop_index;
- poly.totloop = 4;
+ const int segment_next = segment_next_or_first(segment);
MLoop &loop_a = loops[loop_index++];
loop_a.v = ring_vert_index_start + segment;
@@ -186,10 +222,10 @@ static void calculate_sphere_faces(MutableSpan<MLoop> loops,
loop_b.v = next_ring_vert_index_start + segment;
loop_b.e = next_ring_edge_index_start + segment;
MLoop &loop_c = loops[loop_index++];
- loop_c.v = next_ring_vert_index_start + (segment + 1) % segments;
- loop_c.e = ring_vertical_edge_index_start + (segment + 1) % segments;
+ loop_c.v = next_ring_vert_index_start + segment_next;
+ loop_c.e = ring_vertical_edge_index_start + segment_next;
MLoop &loop_d = loops[loop_index++];
- loop_d.v = ring_vert_index_start + (segment + 1) % segments;
+ loop_d.v = ring_vert_index_start + segment_next;
loop_d.e = ring_edge_index_start + segment;
}
ring_vert_index_start += segments;
@@ -202,15 +238,13 @@ static void calculate_sphere_faces(MutableSpan<MLoop> loops,
const int last_vert_index = sphere_vert_total(segments, rings) - 1;
const int last_vert_ring_start = last_vert_index - segments;
for (const int segment : IndexRange(segments)) {
- MPoly &poly = polys[poly_index++];
- poly.loopstart = loop_index;
- poly.totloop = 3;
+ const int segment_next = segment_next_or_first(segment);
MLoop &loop_a = loops[loop_index++];
loop_a.v = last_vert_index;
- loop_a.e = bottom_edge_fan_start + (segment + 1) % segments;
+ loop_a.e = bottom_edge_fan_start + segment_next;
MLoop &loop_b = loops[loop_index++];
- loop_b.v = last_vert_ring_start + (segment + 1) % segments;
+ loop_b.v = last_vert_ring_start + segment_next;
loop_b.e = last_edge_ring_start + segment;
MLoop &loop_c = loops[loop_index++];
loop_c.v = last_vert_ring_start + segment;
@@ -218,43 +252,45 @@ static void calculate_sphere_faces(MutableSpan<MLoop> loops,
}
}
-static void calculate_sphere_uvs(Mesh *mesh, const float segments, const float rings)
+BLI_NOINLINE static void calculate_sphere_uvs(Mesh *mesh, const float segments, const float rings)
{
- 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();
+ MutableAttributeAccessor attributes = mesh->attributes_for_write();
+
+ SpanAttributeWriter<float2> uv_attribute = attributes.lookup_or_add_for_write_only_span<float2>(
+ "uv_map", ATTR_DOMAIN_CORNER);
+ MutableSpan<float2> uvs = uv_attribute.span;
int loop_index = 0;
const float dy = 1.0f / rings;
+ const float segments_inv = 1.0f / segments;
+
for (const int i_segment : IndexRange(segments)) {
const float segment = static_cast<float>(i_segment);
- uvs[loop_index++] = float2((segment + 0.5f) / segments, 0.0f);
- uvs[loop_index++] = float2(segment / segments, dy);
- uvs[loop_index++] = float2((segment + 1.0f) / segments, dy);
+ uvs[loop_index++] = float2((segment + 0.5f) * segments_inv, 0.0f);
+ uvs[loop_index++] = float2(segment * segments_inv, dy);
+ uvs[loop_index++] = float2((segment + 1.0f) * segments_inv, dy);
}
for (const int i_ring : IndexRange(1, rings - 2)) {
const float ring = static_cast<float>(i_ring);
for (const int i_segment : IndexRange(segments)) {
const float segment = static_cast<float>(i_segment);
- uvs[loop_index++] = float2(segment / segments, ring / rings);
- uvs[loop_index++] = float2(segment / segments, (ring + 1.0f) / rings);
- uvs[loop_index++] = float2((segment + 1.0f) / segments, (ring + 1.0f) / rings);
- uvs[loop_index++] = float2((segment + 1.0f) / segments, ring / rings);
+ uvs[loop_index++] = float2(segment * segments_inv, ring / rings);
+ uvs[loop_index++] = float2(segment * segments_inv, (ring + 1.0f) / rings);
+ uvs[loop_index++] = float2((segment + 1.0f) * segments_inv, (ring + 1.0f) / rings);
+ uvs[loop_index++] = float2((segment + 1.0f) * segments_inv, ring / rings);
}
}
for (const int i_segment : IndexRange(segments)) {
const float segment = static_cast<float>(i_segment);
- uvs[loop_index++] = float2((segment + 0.5f) / segments, 1.0f);
- uvs[loop_index++] = float2((segment + 1.0f) / segments, 1.0f - dy);
- uvs[loop_index++] = float2(segment / segments, 1.0f - dy);
+ uvs[loop_index++] = float2((segment + 0.5f) * segments_inv, 1.0f);
+ uvs[loop_index++] = float2((segment + 1.0f) * segments_inv, 1.0f - dy);
+ uvs[loop_index++] = float2(segment * segments_inv, 1.0f - dy);
}
- uv_attribute.save();
+ uv_attribute.finish();
}
static Mesh *create_uv_sphere_mesh(const float radius, const int segments, const int rings)
@@ -265,20 +301,22 @@ static Mesh *create_uv_sphere_mesh(const float radius, const int segments, const
sphere_corner_total(segments, rings),
sphere_face_total(segments, rings));
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};
-
- 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);
-
- calculate_sphere_faces(loops, polys, segments, rings);
-
- calculate_sphere_uvs(mesh, segments, rings);
+ MutableSpan<MVert> verts = mesh->verts_for_write();
+ MutableSpan<MEdge> edges = mesh->edges_for_write();
+ MutableSpan<MPoly> polys = mesh->polys_for_write();
+ MutableSpan<MLoop> loops = mesh->loops_for_write();
+
+ threading::parallel_invoke(
+ 1024 < 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); },
+ [&]() { calculate_sphere_faces(polys, segments); },
+ [&]() { calculate_sphere_corners(loops, segments, rings); },
+ [&]() { calculate_sphere_uvs(mesh, segments, rings); });
return mesh;
}
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
index ec6acf55dd8..4d08fa40a29 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc
@@ -1,5 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include "DNA_mesh_types.h"
+
#include "GEO_mesh_to_curve.hh"
#include "node_geometry_util.hh"
@@ -18,24 +20,25 @@ 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});
+ const Mesh *mesh = geometry_set.get_mesh_for_read();
+ if (mesh == nullptr) {
+ geometry_set.remove_geometry_during_modify();
return;
}
- const MeshComponent &component = *geometry_set.get_component_for_read<MeshComponent>();
- GeometryComponentFieldContext context{component, ATTR_DOMAIN_EDGE};
- fn::FieldEvaluator evaluator{context, component.attribute_domain_num(ATTR_DOMAIN_EDGE)};
+ bke::MeshFieldContext context{*mesh, ATTR_DOMAIN_EDGE};
+ fn::FieldEvaluator evaluator{context, mesh->totedge};
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});
+ geometry_set.remove_geometry_during_modify();
return;
}
- geometry_set.replace_curves(geometry::mesh_to_curve_convert(component, selection));
- geometry_set.keep_only({GEO_COMPONENT_TYPE_CURVE, GEO_COMPONENT_TYPE_INSTANCES});
+ bke::CurvesGeometry curves = geometry::mesh_to_curve_convert(*mesh, selection);
+ geometry_set.replace_curves(bke::curves_new_nomain(std::move(curves)));
+ geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_CURVE});
});
params.set_output("Curve", std::move(geometry_set));
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 7463eb01471..a1d6695b33b 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
@@ -2,6 +2,7 @@
#include "BLI_task.hh"
+#include "DNA_mesh_types.h"
#include "DNA_pointcloud_types.h"
#include "BKE_attribute_math.hh"
@@ -60,18 +61,18 @@ static void geometry_set_mesh_to_points(GeometrySet &geometry_set,
Field<bool> &selection_field,
const eAttrDomain domain)
{
- const MeshComponent *mesh_component = geometry_set.get_component_for_read<MeshComponent>();
- if (mesh_component == nullptr) {
- geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ const Mesh *mesh = geometry_set.get_mesh_for_read();
+ if (mesh == nullptr) {
+ geometry_set.remove_geometry_during_modify();
return;
}
- GeometryComponentFieldContext field_context{*mesh_component, domain};
- const int domain_num = mesh_component->attribute_domain_num(domain);
- if (domain_num == 0) {
- geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ const int domain_size = mesh->attributes().domain_size(domain);
+ if (domain_size == 0) {
+ geometry_set.remove_geometry_during_modify();
return;
}
- fn::FieldEvaluator evaluator{field_context, domain_num};
+ bke::MeshFieldContext field_context{*mesh, domain};
+ 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
@@ -83,39 +84,40 @@ static void geometry_set_mesh_to_points(GeometrySet &geometry_set,
PointCloud *pointcloud = BKE_pointcloud_new_nomain(selection.size());
geometry_set.replace_pointcloud(pointcloud);
- PointCloudComponent &point_component =
- geometry_set.get_component_for_write<PointCloudComponent>();
+ MutableAttributeAccessor dst_attributes = pointcloud->attributes_for_write();
- OutputAttribute position = point_component.attribute_try_get_for_output_only(
+ GSpanAttributeWriter position = dst_attributes.lookup_or_add_for_write_only_span(
"position", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3);
materialize_compressed_to_uninitialized_threaded(
- evaluator.get_evaluated(0), selection, position.as_span());
- position.save();
+ evaluator.get_evaluated(0), selection, position.span);
+ position.finish();
- OutputAttribute radius = point_component.attribute_try_get_for_output_only(
+ GSpanAttributeWriter radius = dst_attributes.lookup_or_add_for_write_only_span(
"radius", ATTR_DOMAIN_POINT, CD_PROP_FLOAT);
materialize_compressed_to_uninitialized_threaded(
- evaluator.get_evaluated(1), selection, radius.as_span());
- radius.save();
+ evaluator.get_evaluated(1), selection, radius.span);
+ radius.finish();
Map<AttributeIDRef, AttributeKind> attributes;
geometry_set.gather_attributes_for_propagation(
{GEO_COMPONENT_TYPE_MESH}, GEO_COMPONENT_TYPE_POINT_CLOUD, false, attributes);
attributes.remove("position");
+ const AttributeAccessor src_attributes = mesh->attributes();
+
for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
const AttributeIDRef attribute_id = entry.key;
const eCustomDataType data_type = entry.value.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(
+ GVArray src = src_attributes.lookup_or_default(attribute_id, domain, data_type);
+ GSpanAttributeWriter dst = dst_attributes.lookup_or_add_for_write_only_span(
attribute_id, ATTR_DOMAIN_POINT, data_type);
if (dst && src) {
- materialize_compressed_to_uninitialized_threaded(src, selection, dst.as_span());
- dst.save();
+ materialize_compressed_to_uninitialized_threaded(src, selection, dst.span);
+ dst.finish();
}
}
- geometry_set.keep_only({GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_INSTANCES});
+ geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_POINT_CLOUD});
}
static void node_geo_exec(GeoNodeExecParams params)
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_volume.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_volume.cc
new file mode 100644
index 00000000000..92814a8bc5e
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_volume.cc
@@ -0,0 +1,187 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "DEG_depsgraph_query.h"
+#include "node_geometry_util.hh"
+
+#include "BKE_lib_id.h"
+#include "BKE_mesh.h"
+#include "BKE_mesh_runtime.h"
+#include "BKE_mesh_wrapper.h"
+#include "BKE_object.h"
+#include "BKE_volume.h"
+
+#include "GEO_mesh_to_volume.hh"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+namespace blender::nodes::node_geo_mesh_to_volume_cc {
+
+NODE_STORAGE_FUNCS(NodeGeometryMeshToVolume)
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH);
+ b.add_input<decl::Float>(N_("Density")).default_value(1.0f).min(0.01f).max(FLT_MAX);
+ b.add_input<decl::Float>(N_("Voxel Size"))
+ .default_value(0.3f)
+ .min(0.01f)
+ .max(FLT_MAX)
+ .subtype(PROP_DISTANCE);
+ b.add_input<decl::Float>(N_("Voxel Amount")).default_value(64.0f).min(0.0f).max(FLT_MAX);
+ b.add_input<decl::Float>(N_("Exterior Band Width"))
+ .default_value(0.1f)
+ .min(0.0f)
+ .max(FLT_MAX)
+ .subtype(PROP_DISTANCE)
+ .description(N_("Width of the volume outside of the mesh"));
+ b.add_input<decl::Float>(N_("Interior Band Width"))
+ .default_value(0.0f)
+ .min(0.0f)
+ .max(FLT_MAX)
+ .subtype(PROP_DISTANCE)
+ .description(N_("Width of the volume inside of the mesh"));
+ b.add_input<decl::Bool>(N_("Fill Volume"))
+ .default_value(true)
+ .description(N_("Initialize the density grid in every cell inside the enclosed volume"));
+ 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(tree), bNode *node)
+{
+ NodeGeometryMeshToVolume *data = (NodeGeometryMeshToVolume *)MEM_callocN(
+ sizeof(NodeGeometryMeshToVolume), __func__);
+ data->resolution_mode = MESH_TO_VOLUME_RESOLUTION_MODE_VOXEL_AMOUNT;
+ node->storage = data;
+}
+
+static void node_update(bNodeTree *ntree, bNode *node)
+{
+ NodeGeometryMeshToVolume *data = (NodeGeometryMeshToVolume *)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 == MESH_TO_VOLUME_RESOLUTION_MODE_VOXEL_AMOUNT);
+ nodeSetSocketAvailability(ntree,
+ voxel_size_socket,
+ data->resolution_mode == MESH_TO_VOLUME_RESOLUTION_MODE_VOXEL_SIZE);
+}
+
+#ifdef WITH_OPENVDB
+
+static Volume *create_volume_from_mesh(const Mesh &mesh, GeoNodeExecParams &params)
+{
+ const NodeGeometryMeshToVolume &storage =
+ *(const NodeGeometryMeshToVolume *)params.node().storage;
+
+ const float density = params.get_input<float>("Density");
+ const float exterior_band_width = params.get_input<float>("Exterior Band Width");
+ const float interior_band_width = params.get_input<float>("Interior Band Width");
+ const bool fill_volume = params.get_input<bool>("Fill Volume");
+
+ geometry::MeshToVolumeResolution resolution;
+ resolution.mode = (MeshToVolumeModifierResolutionMode)storage.resolution_mode;
+ if (resolution.mode == MESH_TO_VOLUME_RESOLUTION_MODE_VOXEL_AMOUNT) {
+ resolution.settings.voxel_amount = params.get_input<float>("Voxel Amount");
+ if (resolution.settings.voxel_amount <= 0.0f) {
+ return nullptr;
+ }
+ }
+ else if (resolution.mode == MESH_TO_VOLUME_RESOLUTION_MODE_VOXEL_SIZE) {
+ resolution.settings.voxel_size = params.get_input<float>("Voxel Size");
+ if (resolution.settings.voxel_size <= 0.0f) {
+ return nullptr;
+ }
+ }
+
+ if (mesh.totvert == 0 || mesh.totpoly == 0) {
+ return nullptr;
+ }
+
+ const float4x4 mesh_to_volume_space_transform = float4x4::identity();
+
+ auto bounds_fn = [&](float3 &r_min, float3 &r_max) {
+ float3 min{std::numeric_limits<float>::max()};
+ float3 max{-std::numeric_limits<float>::max()};
+ BKE_mesh_wrapper_minmax(&mesh, min, max);
+ r_min = min;
+ r_max = max;
+ };
+
+ const float voxel_size = geometry::volume_compute_voxel_size(params.depsgraph(),
+ bounds_fn,
+ resolution,
+ exterior_band_width,
+ mesh_to_volume_space_transform);
+
+ Volume *volume = (Volume *)BKE_id_new_nomain(ID_VO, nullptr);
+ BKE_volume_init_grids(volume);
+
+ /* Convert mesh to grid and add to volume. */
+ geometry::volume_grid_add_from_mesh(volume,
+ "density",
+ &mesh,
+ mesh_to_volume_space_transform,
+ voxel_size,
+ fill_volume,
+ exterior_band_width,
+ interior_band_width,
+ density);
+
+ return volume;
+}
+
+#endif /* WITH_OPENVDB */
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+#ifdef WITH_OPENVDB
+ GeometrySet geometry_set(params.extract_input<GeometrySet>("Mesh"));
+
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ if (geometry_set.has_mesh()) {
+ Volume *volume = create_volume_from_mesh(*geometry_set.get_mesh_for_read(), params);
+ geometry_set.replace_volume(volume);
+ geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_VOLUME});
+ }
+ });
+ 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();
+ return;
+#endif
+}
+
+} // namespace blender::nodes::node_geo_mesh_to_volume_cc
+
+void register_node_type_geo_mesh_to_volume()
+{
+ namespace file_ns = blender::nodes::node_geo_mesh_to_volume_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_MESH_TO_VOLUME, "Mesh to Volume", NODE_CLASS_GEOMETRY);
+ ntype.declare = file_ns::node_declare;
+ node_type_size(&ntype, 200, 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;
+ node_type_storage(
+ &ntype, "NodeGeometryMeshToVolume", 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 f4d91d7496b..691988249df 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_object_info.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_object_info.cc
@@ -2,6 +2,8 @@
#include "BLI_math_matrix.h"
+#include "BKE_geometry_set_instances.hh"
+
#include "UI_interface.h"
#include "UI_resources.h"
@@ -78,7 +80,7 @@ static void node_geo_exec(GeoNodeExecParams params)
else {
geometry_set = bke::object_get_evaluated_geometry_set(*object);
if (transform_space_relative) {
- transform_geometry_set(geometry_set, transform, *params.depsgraph());
+ transform_geometry_set(params, geometry_set, transform, *params.depsgraph());
}
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_points.cc b/source/blender/nodes/geometry/nodes/node_geo_points.cc
new file mode 100644
index 00000000000..4a294076834
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_points.cc
@@ -0,0 +1,101 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "BKE_pointcloud.h"
+#include "DNA_pointcloud_types.h"
+
+#include "BLI_task.hh"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_points_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Int>(N_("Count"))
+ .default_value(1)
+ .description(N_("The number of points to create"))
+ .min(0);
+ b.add_input<decl::Vector>(N_("Position"))
+ .supports_field()
+ .default_value(float3(0.0f))
+ .description(N_("The positions of the new points"));
+ b.add_input<decl::Float>(N_("Radius"))
+ .supports_field()
+ .subtype(PROP_DISTANCE)
+ .default_value(float(0.1f))
+ .description(N_("The radii of the new points"));
+ b.add_output<decl::Geometry>(N_("Geometry"));
+}
+
+class PointsFieldContext : public FieldContext {
+ private:
+ int points_num_;
+
+ public:
+ PointsFieldContext(const int points_num) : points_num_(points_num)
+ {
+ }
+
+ int64_t points_num() const
+ {
+ return points_num_;
+ }
+
+ GVArray get_varray_for_input(const FieldInput &field_input,
+ const IndexMask mask,
+ ResourceScope &UNUSED(scope)) const
+ {
+ const bke::IDAttributeFieldInput *id_field_input =
+ dynamic_cast<const bke::IDAttributeFieldInput *>(&field_input);
+
+ const fn::IndexFieldInput *index_field_input = dynamic_cast<const fn::IndexFieldInput *>(
+ &field_input);
+
+ if (id_field_input == nullptr && index_field_input == nullptr) {
+ return {};
+ }
+
+ return fn::IndexFieldInput::get_index_varray(mask);
+ }
+};
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ const int count = params.extract_input<int>("Count");
+ if (count <= 0) {
+ params.set_default_remaining_outputs();
+ return;
+ }
+
+ Field<float3> position_field = params.extract_input<Field<float3>>("Position");
+ Field<float> radius_field = params.extract_input<Field<float>>("Radius");
+
+ PointCloud *points = BKE_pointcloud_new_nomain(count);
+ MutableAttributeAccessor attributes = points->attributes_for_write();
+ AttributeWriter<float3> output_position = attributes.lookup_or_add_for_write<float3>(
+ "position", ATTR_DOMAIN_POINT);
+ AttributeWriter<float> output_radii = attributes.lookup_or_add_for_write<float>(
+ "radius", ATTR_DOMAIN_POINT);
+
+ PointsFieldContext context{count};
+ fn::FieldEvaluator evaluator{context, count};
+ evaluator.add_with_destination(position_field, output_position.varray);
+ evaluator.add_with_destination(radius_field, output_radii.varray);
+ evaluator.evaluate();
+
+ output_position.finish();
+ output_radii.finish();
+ params.set_output("Geometry", GeometrySet::create_with_pointcloud(points));
+}
+
+} // namespace blender::nodes::node_geo_points_cc
+
+void register_node_type_geo_points()
+{
+ namespace file_ns = blender::nodes::node_geo_points_cc;
+ static bNodeType ntype;
+ geo_node_type_base(&ntype, GEO_NODE_POINTS, "Points", 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_points_to_vertices.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc
index 00b3d167755..4ac3bf712f7 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
@@ -2,6 +2,8 @@
#include "BLI_task.hh"
+#include "DNA_pointcloud_types.h"
+
#include "BKE_attribute_math.hh"
#include "BKE_mesh.h"
@@ -18,33 +20,22 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Geometry>(N_("Mesh"));
}
-template<typename T>
-static void copy_attribute_to_vertices(const Span<T> src, const IndexMask mask, MutableSpan<T> dst)
-{
- for (const int i : mask.index_range()) {
- dst[i] = src[mask[i]];
- }
-}
-
/* One improvement would be to move the attribute arrays directly to the mesh when possible. */
static void geometry_set_points_to_vertices(GeometrySet &geometry_set,
Field<bool> &selection_field)
{
- const PointCloudComponent *point_component =
- geometry_set.get_component_for_read<PointCloudComponent>();
- if (point_component == nullptr) {
- geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ const PointCloud *points = geometry_set.get_pointcloud_for_read();
+ if (points == nullptr) {
+ geometry_set.remove_geometry_during_modify();
return;
}
-
- GeometryComponentFieldContext field_context{*point_component, ATTR_DOMAIN_POINT};
- const int domain_num = point_component->attribute_domain_num(ATTR_DOMAIN_POINT);
- if (domain_num == 0) {
- geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ if (points->totpoint == 0) {
+ geometry_set.remove_geometry_during_modify();
return;
}
- fn::FieldEvaluator selection_evaluator{field_context, domain_num};
+ bke::PointCloudFieldContext field_context{*points};
+ fn::FieldEvaluator selection_evaluator{field_context, points->totpoint};
selection_evaluator.add(selection_field);
selection_evaluator.evaluate();
const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0);
@@ -55,27 +46,23 @@ static void geometry_set_points_to_vertices(GeometrySet &geometry_set,
Mesh *mesh = BKE_mesh_new_nomain(selection.size(), 0, 0, 0, 0);
geometry_set.replace_mesh(mesh);
- MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
+
+ const AttributeAccessor src_attributes = points->attributes();
+ MutableAttributeAccessor dst_attributes = mesh->attributes_for_write();
for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
const AttributeIDRef attribute_id = entry.key;
const eCustomDataType data_type = entry.value.data_type;
- 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(
+ GVArray src = src_attributes.lookup_or_default(attribute_id, ATTR_DOMAIN_POINT, data_type);
+ GSpanAttributeWriter dst = dst_attributes.lookup_or_add_for_write_only_span(
attribute_id, ATTR_DOMAIN_POINT, data_type);
if (dst && src) {
- attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
- using T = decltype(dummy);
- 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();
+ src.materialize_compressed_to_uninitialized(selection, dst.span.data());
+ dst.finish();
}
}
- geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES});
+ geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_MESH});
}
static void node_geo_exec(GeoNodeExecParams params)
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
index 42cee4c0efe..ba6bd40a6b6 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc
@@ -163,12 +163,15 @@ static void gather_point_data_from_component(GeoNodeExecParams &params,
Vector<float3> &r_positions,
Vector<float> &r_radii)
{
- VArray<float3> positions = component.attribute_get_for_read<float3>(
+ if (component.is_empty()) {
+ return;
+ }
+ VArray<float3> positions = component.attributes()->lookup_or_default<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_num = component.attribute_domain_num(ATTR_DOMAIN_POINT);
+ bke::GeometryFieldContext field_context{component, ATTR_DOMAIN_POINT};
+ const int domain_num = component.attribute_domain_size(ATTR_DOMAIN_POINT);
r_positions.resize(r_positions.size() + domain_num);
positions.materialize(r_positions.as_mutable_span().take_back(domain_num));
@@ -221,7 +224,7 @@ static void initialize_volume_component_from_points(GeoNodeExecParams &params,
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.keep_only_during_modify({GEO_COMPONENT_TYPE_VOLUME});
r_geometry_set.replace_volume(volume);
}
#endif
diff --git a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc
index a92cee2d066..f657b128c51 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc
@@ -208,7 +208,7 @@ class RaycastFunction : public fn::MultiFunction {
GeometryNodeRaycastMapMode mapping_;
/** The field for data evaluated on the target geometry. */
- std::optional<GeometryComponentFieldContext> target_context_;
+ std::optional<bke::MeshFieldContext> target_context_;
std::unique_ptr<FieldEvaluator> target_evaluator_;
const GVArray *target_data_ = nullptr;
@@ -310,10 +310,10 @@ class RaycastFunction : public fn::MultiFunction {
if (!src_field) {
return;
}
- const MeshComponent &mesh_component = *target_.get_component_for_read<MeshComponent>();
- target_context_.emplace(GeometryComponentFieldContext{mesh_component, domain_});
- const int domain_num = mesh_component.attribute_domain_num(domain_);
- target_evaluator_ = std::make_unique<FieldEvaluator>(*target_context_, domain_num);
+ const Mesh &mesh = *target_.get_mesh_for_read();
+ target_context_.emplace(bke::MeshFieldContext{mesh, domain_});
+ const int domain_size = mesh.attributes().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);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_remove_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_remove_attribute.cc
index da42b8c5ee0..ee279ba58f9 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_remove_attribute.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_remove_attribute.cc
@@ -39,7 +39,7 @@ static void node_geo_exec(GeoNodeExecParams params)
/* First check if the attribute exists before getting write access,
* to avoid potentially expensive unnecessary copies. */
const GeometryComponent &read_only_component = *geometry_set.get_component_for_read(type);
- if (read_only_component.attribute_exists(name)) {
+ if (read_only_component.attributes()->contains(name)) {
attribute_exists = true;
}
else {
@@ -47,7 +47,7 @@ static void node_geo_exec(GeoNodeExecParams params)
}
GeometryComponent &component = geometry_set.get_component_for_write(type);
- if (!component.attribute_try_delete(name)) {
+ if (!component.attributes_for_write()->remove(name)) {
cannot_delete = true;
}
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc
index 59e203afd08..4ed94e67e74 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc
@@ -18,10 +18,8 @@ static void node_declare(NodeDeclarationBuilder &b)
static void rotate_instances(GeoNodeExecParams &params, InstancesComponent &instances_component)
{
- GeometryComponentFieldContext field_context{instances_component, ATTR_DOMAIN_INSTANCE};
- const int domain_num = instances_component.instances_num();
-
- fn::FieldEvaluator evaluator{field_context, domain_num};
+ const bke::InstancesFieldContext context{instances_component};
+ fn::FieldEvaluator evaluator{context, instances_component.instances_num()};
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"));
@@ -29,9 +27,9 @@ static void rotate_instances(GeoNodeExecParams &params, InstancesComponent &inst
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);
+ 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();
diff --git a/source/blender/nodes/geometry/nodes/node_geo_scale_elements.cc b/source/blender/nodes/geometry/nodes/node_geo_scale_elements.cc
index 698efa8865d..2ebbf88b8ad 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_scale_elements.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_scale_elements.cc
@@ -147,14 +147,22 @@ static float4x4 create_single_axis_transform(const float3 &center,
return transform;
}
-using GetVertexIndicesFn =
- FunctionRef<void(const Mesh &mesh, int element_index, VectorSet<int> &r_vertex_indices)>;
+using GetVertexIndicesFn = FunctionRef<void(Span<MEdge> edges,
+ Span<MPoly> polys,
+ Span<MLoop> loops,
+ 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)
{
+ MutableSpan<MVert> verts = mesh.verts_for_write();
+ const Span<MEdge> edges = mesh.edges();
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
+
threading::parallel_for(islands.index_range(), 256, [&](const IndexRange range) {
for (const int island_index : range) {
const ElementIsland &island = islands[island_index];
@@ -164,7 +172,7 @@ static void scale_vertex_islands_uniformly(Mesh &mesh,
VectorSet<int> vertex_indices;
for (const int poly_index : island.element_indices) {
- get_vertex_indices(mesh, poly_index, vertex_indices);
+ get_vertex_indices(edges, polys, loops, poly_index, vertex_indices);
center += params.centers[poly_index];
scale += params.scales[poly_index];
}
@@ -175,7 +183,7 @@ static void scale_vertex_islands_uniformly(Mesh &mesh,
center *= f;
for (const int vert_index : vertex_indices) {
- MVert &vert = mesh.mvert[vert_index];
+ MVert &vert = verts[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);
@@ -183,8 +191,7 @@ static void scale_vertex_islands_uniformly(Mesh &mesh,
}
});
- /* Positions have changed, so the normals will have to be recomputed. */
- BKE_mesh_normals_tag_dirty(&mesh);
+ BKE_mesh_tag_coords_changed(&mesh);
}
static void scale_vertex_islands_on_axis(Mesh &mesh,
@@ -192,6 +199,11 @@ static void scale_vertex_islands_on_axis(Mesh &mesh,
const AxisScaleParams &params,
const GetVertexIndicesFn get_vertex_indices)
{
+ MutableSpan<MVert> verts = mesh.verts_for_write();
+ const Span<MEdge> edges = mesh.edges();
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
+
threading::parallel_for(islands.index_range(), 256, [&](const IndexRange range) {
for (const int island_index : range) {
const ElementIsland &island = islands[island_index];
@@ -202,7 +214,7 @@ static void scale_vertex_islands_on_axis(Mesh &mesh,
VectorSet<int> vertex_indices;
for (const int poly_index : island.element_indices) {
- get_vertex_indices(mesh, poly_index, vertex_indices);
+ get_vertex_indices(edges, polys, loops, poly_index, vertex_indices);
center += params.centers[poly_index];
scale += params.scales[poly_index];
axis += params.axis_vectors[poly_index];
@@ -220,7 +232,7 @@ static void scale_vertex_islands_on_axis(Mesh &mesh,
const float4x4 transform = create_single_axis_transform(center, axis, scale);
for (const int vert_index : vertex_indices) {
- MVert &vert = mesh.mvert[vert_index];
+ MVert &vert = verts[vert_index];
const float3 old_position = vert.co;
const float3 new_position = transform * old_position;
copy_v3_v3(vert.co, new_position);
@@ -228,17 +240,19 @@ static void scale_vertex_islands_on_axis(Mesh &mesh,
}
});
- /* Positions have changed, so the normals will have to be recomputed. */
- BKE_mesh_normals_tag_dirty(&mesh);
+ BKE_mesh_tag_coords_changed(&mesh);
}
static Vector<ElementIsland> prepare_face_islands(const Mesh &mesh, const IndexMask face_selection)
{
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
+
/* 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};
+ const MPoly &poly = polys[poly_index];
+ const Span<MLoop> poly_loops = loops.slice(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;
@@ -254,8 +268,8 @@ static Vector<ElementIsland> prepare_face_islands(const Mesh &mesh, const IndexM
/* 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 MPoly &poly = polys[poly_index];
+ const Span<MLoop> poly_loops = loops.slice(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()) {
@@ -268,10 +282,14 @@ static Vector<ElementIsland> prepare_face_islands(const Mesh &mesh, const IndexM
return islands;
}
-static void get_face_vertices(const Mesh &mesh, int face_index, VectorSet<int> &r_vertex_indices)
+static void get_face_verts(const Span<MEdge> /*edges*/,
+ const Span<MPoly> polys,
+ const Span<MLoop> loops,
+ 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};
+ const MPoly &poly = polys[face_index];
+ const Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop);
for (const MLoop &loop : poly_loops) {
r_vertex_indices.add(loop.v);
}
@@ -290,18 +308,14 @@ static AxisScaleParams evaluate_axis_scale_fields(FieldEvaluator &evaluator,
return out;
}
-static void scale_faces_on_axis(MeshComponent &mesh_component, const AxisScaleFields &fields)
+static void scale_faces_on_axis(Mesh &mesh, 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};
+ bke::MeshFieldContext field_context{mesh, 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);
+ scale_vertex_islands_on_axis(mesh, island, params, get_face_verts);
}
static UniformScaleParams evaluate_uniform_scale_fields(FieldEvaluator &evaluator,
@@ -316,26 +330,24 @@ static UniformScaleParams evaluate_uniform_scale_fields(FieldEvaluator &evaluato
return out;
}
-static void scale_faces_uniformly(MeshComponent &mesh_component, const UniformScaleFields &fields)
+static void scale_faces_uniformly(Mesh &mesh, 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};
+ bke::MeshFieldContext field_context{mesh, 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);
+ scale_vertex_islands_uniformly(mesh, island, params, get_face_verts);
}
static Vector<ElementIsland> prepare_edge_islands(const Mesh &mesh, const IndexMask edge_selection)
{
+ const Span<MEdge> edges = mesh.edges();
+
/* 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];
+ const MEdge &edge = edges[edge_index];
disjoint_set.join(edge.v1, edge.v2);
}
@@ -346,7 +358,7 @@ static Vector<ElementIsland> prepare_edge_islands(const Mesh &mesh, const IndexM
/* 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 MEdge &edge = edges[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()) {
@@ -359,39 +371,35 @@ static Vector<ElementIsland> prepare_edge_islands(const Mesh &mesh, const IndexM
return islands;
}
-static void get_edge_vertices(const Mesh &mesh, int edge_index, VectorSet<int> &r_vertex_indices)
+static void get_edge_verts(const Span<MEdge> edges,
+ const Span<MPoly> /*polys*/,
+ const Span<MLoop> /*loops*/,
+ int edge_index,
+ VectorSet<int> &r_vertex_indices)
{
- const MEdge &edge = mesh.medge[edge_index];
+ const MEdge &edge = edges[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)
+static void scale_edges_uniformly(Mesh &mesh, 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};
+ bke::MeshFieldContext field_context{mesh, 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);
+ scale_vertex_islands_uniformly(mesh, island, params, get_edge_verts);
}
-static void scale_edges_on_axis(MeshComponent &mesh_component, const AxisScaleFields &fields)
+static void scale_edges_on_axis(Mesh &mesh, 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};
+ bke::MeshFieldContext field_context{mesh, 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);
+ scale_vertex_islands_on_axis(mesh, island, params, get_edge_verts);
}
static void node_geo_exec(GeoNodeExecParams params)
@@ -412,42 +420,38 @@ static void node_geo_exec(GeoNodeExecParams params)
}
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;
+ if (Mesh *mesh = geometry.get_mesh_for_write()) {
+ switch (domain) {
+ case ATTR_DOMAIN_FACE: {
+ switch (scale_mode) {
+ case GEO_NODE_SCALE_ELEMENTS_UNIFORM: {
+ scale_faces_uniformly(*mesh, {selection_field, scale_field, center_field});
+ break;
+ }
+ case GEO_NODE_SCALE_ELEMENTS_SINGLE_AXIS: {
+ scale_faces_on_axis(*mesh, {selection_field, scale_field, center_field, axis_field});
+ break;
+ }
}
+ 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;
+ case ATTR_DOMAIN_EDGE: {
+ switch (scale_mode) {
+ case GEO_NODE_SCALE_ELEMENTS_UNIFORM: {
+ scale_edges_uniformly(*mesh, {selection_field, scale_field, center_field});
+ break;
+ }
+ case GEO_NODE_SCALE_ELEMENTS_SINGLE_AXIS: {
+ scale_edges_on_axis(*mesh, {selection_field, scale_field, center_field, axis_field});
+ break;
+ }
}
+ break;
}
- break;
+ default:
+ BLI_assert_unreachable();
+ break;
}
- default:
- BLI_assert_unreachable();
- break;
}
});
diff --git a/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc
index d4716a6b6f0..21fe724e194 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc
@@ -21,9 +21,8 @@ static void node_declare(NodeDeclarationBuilder &b)
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_num()};
+ const bke::InstancesFieldContext context{instances_component};
+ fn::FieldEvaluator evaluator{context, instances_component.instances_num()};
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"));
@@ -31,9 +30,9 @@ static void scale_instances(GeoNodeExecParams &params, InstancesComponent &insta
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);
+ 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();
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
index d2082924fa7..e529ddddabe 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc
@@ -68,29 +68,25 @@ static void update_handle_types_for_movement(int8_t &type, int8_t &other)
}
}
-static void set_position_in_component(CurveComponent &component,
+static void set_position_in_component(bke::CurvesGeometry &curves,
const GeometryNodeCurveHandleMode mode,
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_num = component.attribute_domain_num(ATTR_DOMAIN_POINT);
- if (domain_num == 0) {
+ if (curves.points_num() == 0) {
return;
}
- fn::FieldEvaluator evaluator{field_context, domain_num};
+ bke::CurvesFieldContext field_context{curves, ATTR_DOMAIN_POINT};
+ fn::FieldEvaluator evaluator{field_context, curves.points_num()};
evaluator.set_selection(selection_field);
evaluator.add(position_field);
evaluator.add(offset_field);
evaluator.evaluate();
const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
- const VArray<float3> &new_positions = evaluator.get_evaluated<float3>(0);
- const VArray<float3> &new_offsets = evaluator.get_evaluated<float3>(1);
-
- Curves &curves_id = *component.get_for_write();
- bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
+ const VArray<float3> new_positions = evaluator.get_evaluated<float3>(0);
+ const VArray<float3> new_offsets = evaluator.get_evaluated<float3>(1);
Span<float3> positions = curves.positions();
@@ -141,22 +137,17 @@ static void node_geo_exec(GeoNodeExecParams params)
std::atomic<bool> has_bezier = false;
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
- if (!geometry_set.has_curves()) {
- return;
- }
- has_curves = true;
- const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>();
- if (!component.attribute_exists("handle_left") ||
- !component.attribute_exists("handle_right")) {
- return;
- }
- has_bezier = true;
+ if (Curves *curves_id = geometry_set.get_curves_for_write()) {
+ bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry);
+ has_curves = true;
+ const AttributeAccessor attributes = curves.attributes();
+ if (!attributes.contains("handle_left") || !attributes.contains("handle_right")) {
+ return;
+ }
+ has_bezier = true;
- set_position_in_component(geometry_set.get_component_for_write<CurveComponent>(),
- mode,
- selection_field,
- position_field,
- offset_field);
+ set_position_in_component(curves, mode, selection_field, position_field, offset_field);
+ }
});
if (has_curves && !has_bezier) {
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
index 4c84093bfcb..0d361090068 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc
@@ -1,5 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include "BKE_curves.hh"
+
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_set_curve_radius_cc {
@@ -10,31 +12,30 @@ static void node_declare(NodeDeclarationBuilder &b)
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)
+ .default_value(0.005f)
.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)
+static void set_radius(bke::CurvesGeometry &curves,
+ const Field<bool> &selection_field,
+ const Field<float> &radius_field)
{
- GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
- const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_POINT);
- if (domain_num == 0) {
+ if (curves.points_num() == 0) {
return;
}
+ MutableAttributeAccessor attributes = curves.attributes_for_write();
+ AttributeWriter<float> radii = attributes.lookup_or_add_for_write<float>("radius",
+ ATTR_DOMAIN_POINT);
- OutputAttribute_Typed<float> radii = component.attribute_try_get_for_output_only<float>(
- "radius", ATTR_DOMAIN_POINT);
-
- fn::FieldEvaluator evaluator{field_context, domain_num};
+ bke::CurvesFieldContext field_context{curves, ATTR_DOMAIN_POINT};
+ fn::FieldEvaluator evaluator{field_context, curves.points_num()};
evaluator.set_selection(selection_field);
- evaluator.add_with_destination(radius_field, radii.varray());
+ evaluator.add_with_destination(radius_field, radii.varray);
evaluator.evaluate();
- radii.save();
+ radii.finish();
}
static void node_geo_exec(GeoNodeExecParams params)
@@ -44,9 +45,8 @@ static void node_geo_exec(GeoNodeExecParams params)
Field<float> radii_field = params.extract_input<Field<float>>("Radius");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
- if (geometry_set.has_curves()) {
- set_radius_in_component(
- geometry_set.get_component_for_write<CurveComponent>(), selection_field, radii_field);
+ if (Curves *curves_id = geometry_set.get_curves_for_write()) {
+ set_radius(bke::CurvesGeometry::wrap(curves_id->geometry), selection_field, radii_field);
}
});
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
index 8b1e5935a61..8c1fb883f46 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc
@@ -1,5 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include "BKE_curves.hh"
+
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_set_curve_tilt_cc {
@@ -12,25 +14,24 @@ static void node_declare(NodeDeclarationBuilder &b)
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)
+static void set_tilt(bke::CurvesGeometry &curves,
+ const Field<bool> &selection_field,
+ const Field<float> &tilt_field)
{
- GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
- const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_POINT);
- if (domain_num == 0) {
+ if (curves.points_num() == 0) {
return;
}
+ MutableAttributeAccessor attributes = curves.attributes_for_write();
+ AttributeWriter<float> tilts = attributes.lookup_or_add_for_write<float>("tilt",
+ ATTR_DOMAIN_POINT);
- OutputAttribute_Typed<float> tilts = component.attribute_try_get_for_output_only<float>(
- "tilt", ATTR_DOMAIN_POINT);
-
- fn::FieldEvaluator evaluator{field_context, domain_num};
+ bke::CurvesFieldContext field_context{curves, ATTR_DOMAIN_POINT};
+ fn::FieldEvaluator evaluator{field_context, curves.points_num()};
evaluator.set_selection(selection_field);
- evaluator.add_with_destination(tilt_field, tilts.varray());
+ evaluator.add_with_destination(tilt_field, tilts.varray);
evaluator.evaluate();
- tilts.save();
+ tilts.finish();
}
static void node_geo_exec(GeoNodeExecParams params)
@@ -40,9 +41,8 @@ static void node_geo_exec(GeoNodeExecParams params)
Field<float> tilt_field = params.extract_input<Field<float>>("Tilt");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
- if (geometry_set.has_curves()) {
- set_tilt_in_component(
- geometry_set.get_component_for_write<CurveComponent>(), selection_field, tilt_field);
+ if (Curves *curves_id = geometry_set.get_curves_for_write()) {
+ set_tilt(bke::CurvesGeometry::wrap(curves_id->geometry), selection_field, tilt_field);
}
});
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_id.cc b/source/blender/nodes/geometry/nodes/node_geo_set_id.cc
index 87d48daddea..5864401223b 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_id.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_id.cc
@@ -19,34 +19,34 @@ static void set_id_in_component(GeometryComponent &component,
const eAttrDomain domain = (component.type() == GEO_COMPONENT_TYPE_INSTANCES) ?
ATTR_DOMAIN_INSTANCE :
ATTR_DOMAIN_POINT;
- GeometryComponentFieldContext field_context{component, domain};
- const int domain_num = component.attribute_domain_num(domain);
- if (domain_num == 0) {
+ const int domain_size = component.attribute_domain_size(domain);
+ if (domain_size == 0) {
return;
}
+ MutableAttributeAccessor attributes = *component.attributes_for_write();
+ bke::GeometryFieldContext field_context{component, domain};
- fn::FieldEvaluator evaluator{field_context, domain_num};
+ 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", domain);
- evaluator.add_with_destination(id_field, id_attribute.varray());
+ if (attributes.contains("id")) {
+ AttributeWriter<int> id_attribute = attributes.lookup_or_add_for_write<int>("id", domain);
+ evaluator.add_with_destination(id_field, id_attribute.varray);
evaluator.evaluate();
- id_attribute.save();
+ id_attribute.finish();
}
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", domain);
- result_ids.materialize(selection, id_attribute.as_span());
- id_attribute.save();
+ const VArray<int> result_ids = evaluator.get_evaluated<int>(0);
+ SpanAttributeWriter<int> id_attribute = attributes.lookup_or_add_for_write_span<int>("id",
+ domain);
+ result_ids.materialize(selection, id_attribute.span);
+ id_attribute.finish();
}
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_material.cc b/source/blender/nodes/geometry/nodes/node_geo_set_material.cc
index 507c6e81b1f..8d00d82664b 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_material.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_material.cc
@@ -12,6 +12,7 @@
#include "DNA_volume_types.h"
#include "BKE_material.h"
+#include "BKE_mesh.h"
namespace blender::nodes::node_geo_set_material_cc {
@@ -49,11 +50,11 @@ static void assign_material_to_faces(Mesh &mesh, const IndexMask selection, Mate
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;
- }
+ MutableAttributeAccessor attributes = mesh.attributes_for_write();
+ SpanAttributeWriter<int> material_indices = attributes.lookup_or_add_for_write_span<int>(
+ "material_index", ATTR_DOMAIN_FACE);
+ material_indices.span.fill_indices(selection, new_material_index);
+ material_indices.finish();
}
static void node_geo_exec(GeoNodeExecParams params)
@@ -72,8 +73,8 @@ static void node_geo_exec(GeoNodeExecParams params)
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};
+ bke::MeshFieldContext field_context{mesh, ATTR_DOMAIN_FACE};
fn::FieldEvaluator selection_evaluator{field_context, mesh.totpoly};
selection_evaluator.add(selection_field);
selection_evaluator.evaluate();
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
index 58613dae832..f6dded56315 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc
@@ -14,22 +14,23 @@ static void node_declare(NodeDeclarationBuilder &b)
static void set_material_index_in_component(GeometryComponent &component,
const Field<bool> &selection_field,
- const Field<int> &index_field)
+ const Field<int> &index_field,
+ const eAttrDomain domain)
{
- GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_FACE};
- const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_FACE);
- if (domain_num == 0) {
+ const int domain_size = component.attribute_domain_size(domain);
+ if (domain_size == 0) {
return;
}
+ MutableAttributeAccessor attributes = *component.attributes_for_write();
+ bke::GeometryFieldContext field_context{component, domain};
- OutputAttribute_Typed<int> indices = component.attribute_try_get_for_output_only<int>(
- "material_index", ATTR_DOMAIN_FACE);
+ AttributeWriter<int> indices = attributes.lookup_or_add_for_write<int>("material_index", domain);
- fn::FieldEvaluator evaluator{field_context, domain_num};
+ fn::FieldEvaluator evaluator{field_context, domain_size};
evaluator.set_selection(selection_field);
- evaluator.add_with_destination(index_field, indices.varray());
+ evaluator.add_with_destination(index_field, indices.varray);
evaluator.evaluate();
- indices.save();
+ indices.finish();
}
static void node_geo_exec(GeoNodeExecParams params)
@@ -40,8 +41,10 @@ static void node_geo_exec(GeoNodeExecParams params)
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);
+ set_material_index_in_component(geometry_set.get_component_for_write<MeshComponent>(),
+ selection_field,
+ index_field,
+ ATTR_DOMAIN_FACE);
}
});
params.set_output("Geometry", std::move(geometry_set));
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
index 571bead9743..28d07b31218 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc
@@ -1,5 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include "DNA_pointcloud_types.h"
+
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_set_point_radius_cc {
@@ -16,25 +18,24 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Geometry>(N_("Points"));
}
-static void set_radius_in_component(GeometryComponent &component,
+static void set_radius_in_component(PointCloud &pointcloud,
const Field<bool> &selection_field,
const Field<float> &radius_field)
{
- GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
- const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_POINT);
- if (domain_num == 0) {
+ if (pointcloud.totpoint == 0) {
return;
}
+ MutableAttributeAccessor attributes = pointcloud.attributes_for_write();
+ AttributeWriter<float> radii = attributes.lookup_or_add_for_write<float>("radius",
+ ATTR_DOMAIN_POINT);
- OutputAttribute_Typed<float> radii = component.attribute_try_get_for_output_only<float>(
- "radius", ATTR_DOMAIN_POINT);
-
- fn::FieldEvaluator evaluator{field_context, domain_num};
+ bke::PointCloudFieldContext field_context{pointcloud};
+ fn::FieldEvaluator evaluator{field_context, pointcloud.totpoint};
evaluator.set_selection(selection_field);
- evaluator.add_with_destination(radius_field, radii.varray());
+ evaluator.add_with_destination(radius_field, radii.varray);
evaluator.evaluate();
- radii.save();
+ radii.finish();
}
static void node_geo_exec(GeoNodeExecParams params)
@@ -44,10 +45,8 @@ static void node_geo_exec(GeoNodeExecParams params)
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);
+ if (PointCloud *pointcloud = geometry_set.get_pointcloud_for_write()) {
+ set_radius_in_component(*pointcloud, selection_field, radii_field);
}
});
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 e9ed87e552f..613f140ff0a 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc
@@ -8,6 +8,7 @@
#include "DNA_meshdata_types.h"
#include "BKE_curves.hh"
+#include "BKE_mesh.h"
#include "node_geometry_util.hh"
@@ -25,26 +26,24 @@ static void node_declare(NodeDeclarationBuilder &b)
static void set_computed_position_and_offset(GeometryComponent &component,
const VArray<float3> &in_positions,
const VArray<float3> &in_offsets,
- const eAttrDomain domain,
const IndexMask selection)
{
-
- OutputAttribute_Typed<float3> positions = component.attribute_try_get_for_output<float3>(
- "position", domain, {0, 0, 0});
+ MutableAttributeAccessor attributes = *component.attributes_for_write();
+ AttributeWriter<float3> positions = attributes.lookup_for_write<float3>("position");
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())) {
+ MutableSpan<MVert> verts = mesh->verts_for_write();
+ 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);
+ add_v3_v3(verts[i].co, offset);
}
});
});
@@ -56,7 +55,7 @@ static void set_computed_position_and_offset(GeometryComponent &component,
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);
+ copy_v3_v3(verts[i].co, new_position);
}
});
});
@@ -67,18 +66,13 @@ static void set_computed_position_and_offset(GeometryComponent &component,
CurveComponent &curve_component = static_cast<CurveComponent &>(component);
Curves &curves_id = *curve_component.get_for_write();
bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
- if (component.attribute_exists("handle_right") &&
- component.attribute_exists("handle_left")) {
- OutputAttribute_Typed<float3> handle_right_attribute =
- component.attribute_try_get_for_output<float3>(
- "handle_right", ATTR_DOMAIN_POINT, {0, 0, 0});
- OutputAttribute_Typed<float3> handle_left_attribute =
- component.attribute_try_get_for_output<float3>(
- "handle_left", ATTR_DOMAIN_POINT, {0, 0, 0});
- MutableSpan<float3> handle_right = handle_right_attribute.as_span();
- MutableSpan<float3> handle_left = handle_left_attribute.as_span();
-
- MutableSpan<float3> out_positions_span = positions.as_span();
+ if (attributes.contains("handle_right") && attributes.contains("handle_left")) {
+ SpanAttributeWriter<float3> handle_right_attribute =
+ attributes.lookup_or_add_for_write_span<float3>("handle_right", ATTR_DOMAIN_POINT);
+ SpanAttributeWriter<float3> handle_left_attribute =
+ attributes.lookup_or_add_for_write_span<float3>("handle_left", ATTR_DOMAIN_POINT);
+
+ MutableVArraySpan<float3> out_positions_span = positions.varray;
devirtualize_varray2(
in_positions, in_offsets, [&](const auto in_positions, const auto in_offsets) {
threading::parallel_for(
@@ -86,15 +80,16 @@ static void set_computed_position_and_offset(GeometryComponent &component,
for (const int i : selection.slice(range)) {
const float3 new_position = in_positions[i] + in_offsets[i];
const float3 delta = new_position - out_positions_span[i];
- handle_right[i] += delta;
- handle_left[i] += delta;
+ handle_right_attribute.span[i] += delta;
+ handle_left_attribute.span[i] += delta;
out_positions_span[i] = new_position;
}
});
});
- handle_right_attribute.save();
- handle_left_attribute.save();
+ out_positions_span.save();
+ handle_right_attribute.finish();
+ handle_left_attribute.finish();
/* Automatic Bezier handles must be recalculated based on the new positions. */
curves.calculate_bezier_auto_handles();
@@ -105,8 +100,8 @@ static void set_computed_position_and_offset(GeometryComponent &component,
}
}
default: {
- MutableSpan<float3> out_positions_span = positions.as_span();
- if (in_positions.is_same(positions.varray())) {
+ MutableVArraySpan<float3> out_positions_span = positions.varray;
+ 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) {
@@ -127,11 +122,12 @@ static void set_computed_position_and_offset(GeometryComponent &component,
});
});
}
+ out_positions_span.save();
break;
}
}
- positions.save();
+ positions.finish();
}
static void set_position_in_component(GeometryComponent &component,
@@ -141,22 +137,23 @@ static void set_position_in_component(GeometryComponent &component,
{
eAttrDomain domain = component.type() == GEO_COMPONENT_TYPE_INSTANCES ? ATTR_DOMAIN_INSTANCE :
ATTR_DOMAIN_POINT;
- GeometryComponentFieldContext field_context{component, domain};
- const int domain_num = component.attribute_domain_num(domain);
- if (domain_num == 0) {
+ bke::GeometryFieldContext field_context{component, domain};
+ const int domain_size = component.attribute_domain_size(domain);
+ if (domain_size == 0) {
return;
}
- fn::FieldEvaluator evaluator{field_context, domain_num};
+ 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();
- 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);
+
+ 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, selection);
}
static void node_geo_exec(GeoNodeExecParams params)
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
index b98fbd0a0fe..0df51e49827 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc
@@ -1,5 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include "DNA_mesh_types.h"
+
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_set_shade_smooth_cc {
@@ -12,25 +14,25 @@ static void node_declare(NodeDeclarationBuilder &b)
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)
+static void set_smooth(Mesh &mesh,
+ const Field<bool> &selection_field,
+ const Field<bool> &shade_field)
{
- GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_FACE};
- const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_FACE);
- if (domain_num == 0) {
+ if (mesh.totpoly == 0) {
return;
}
- OutputAttribute_Typed<bool> shades = component.attribute_try_get_for_output_only<bool>(
- "shade_smooth", ATTR_DOMAIN_FACE);
+ MutableAttributeAccessor attributes = mesh.attributes_for_write();
+ AttributeWriter<bool> smooth = attributes.lookup_or_add_for_write<bool>("shade_smooth",
+ ATTR_DOMAIN_FACE);
- fn::FieldEvaluator evaluator{field_context, domain_num};
+ bke::MeshFieldContext field_context{mesh, ATTR_DOMAIN_FACE};
+ fn::FieldEvaluator evaluator{field_context, mesh.totpoly};
evaluator.set_selection(selection_field);
- evaluator.add_with_destination(shade_field, shades.varray());
+ evaluator.add_with_destination(shade_field, smooth.varray);
evaluator.evaluate();
- shades.save();
+ smooth.finish();
}
static void node_geo_exec(GeoNodeExecParams params)
@@ -40,9 +42,8 @@ static void node_geo_exec(GeoNodeExecParams params)
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);
+ if (Mesh *mesh = geometry_set.get_mesh_for_write()) {
+ set_smooth(*mesh, selection_field, shade_field);
}
});
params.set_output("Geometry", std::move(geometry_set));
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
index 976857883f0..d8faa154477 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc
@@ -1,5 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include "BKE_curves.hh"
+
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_set_spline_cyclic_cc {
@@ -12,25 +14,24 @@ static void node_declare(NodeDeclarationBuilder &b)
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)
+static void set_cyclic(bke::CurvesGeometry &curves,
+ const Field<bool> &selection_field,
+ const Field<bool> &cyclic_field)
{
- GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE};
- const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_CURVE);
- if (domain_num == 0) {
+ if (curves.curves_num() == 0) {
return;
}
+ MutableAttributeAccessor attributes = curves.attributes_for_write();
+ AttributeWriter<bool> cyclics = attributes.lookup_or_add_for_write<bool>("cyclic",
+ ATTR_DOMAIN_CURVE);
- OutputAttribute_Typed<bool> cyclics = component.attribute_try_get_for_output_only<bool>(
- "cyclic", ATTR_DOMAIN_CURVE);
-
- fn::FieldEvaluator evaluator{field_context, domain_num};
+ bke::CurvesFieldContext field_context{curves, ATTR_DOMAIN_CURVE};
+ fn::FieldEvaluator evaluator{field_context, curves.curves_num()};
evaluator.set_selection(selection_field);
- evaluator.add_with_destination(cyclic_field, cyclics.varray());
+ evaluator.add_with_destination(cyclic_field, cyclics.varray);
evaluator.evaluate();
- cyclics.save();
+ cyclics.finish();
}
static void node_geo_exec(GeoNodeExecParams params)
@@ -40,9 +41,8 @@ static void node_geo_exec(GeoNodeExecParams params)
Field<bool> cyclic_field = params.extract_input<Field<bool>>("Cyclic");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
- if (geometry_set.has_curves()) {
- set_cyclic_in_component(
- geometry_set.get_component_for_write<CurveComponent>(), selection_field, cyclic_field);
+ if (Curves *curves_id = geometry_set.get_curves_for_write()) {
+ set_cyclic(bke::CurvesGeometry::wrap(curves_id->geometry), selection_field, cyclic_field);
}
});
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
index 8b665376c01..d46ceac92ba 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc
@@ -1,5 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include "BKE_curves.hh"
+
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_set_spline_resolution_cc {
@@ -12,36 +14,36 @@ static void node_declare(NodeDeclarationBuilder &b)
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)
+static void set_resolution(bke::CurvesGeometry &curves,
+ const Field<bool> &selection_field,
+ const Field<int> &resolution_field)
{
- GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE};
- const int domain_num = component.attribute_domain_num(ATTR_DOMAIN_CURVE);
- if (domain_num == 0) {
+ if (curves.curves_num() == 0) {
return;
}
+ MutableAttributeAccessor attributes = curves.attributes_for_write();
+ AttributeWriter<int> resolutions = attributes.lookup_or_add_for_write<int>("resolution",
+ ATTR_DOMAIN_CURVE);
- OutputAttribute_Typed<int> resolutions = component.attribute_try_get_for_output_only<int>(
- "resolution", ATTR_DOMAIN_CURVE);
-
- fn::FieldEvaluator evaluator{field_context, domain_num};
+ bke::CurvesFieldContext field_context{curves, ATTR_DOMAIN_CURVE};
+ fn::FieldEvaluator evaluator{field_context, curves.curves_num()};
evaluator.set_selection(selection_field);
- evaluator.add_with_destination(resolution_field, resolutions.varray());
+ evaluator.add_with_destination(resolution_field, resolutions.varray);
evaluator.evaluate();
- resolutions.save();
+ resolutions.finish();
}
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");
+ Field<bool> selection = params.extract_input<Field<bool>>("Selection");
+ Field<int> resolution = params.extract_input<Field<int>>("Resolution");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
- set_resolution_in_component(
- geometry_set.get_component_for_write<CurveComponent>(), selection_field, resolution_field);
+ if (Curves *curves_id = geometry_set.get_curves_for_write()) {
+ set_resolution(bke::CurvesGeometry::wrap(curves_id->geometry), selection, resolution);
+ }
});
params.set_output("Geometry", std::move(geometry_set));
diff --git a/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc
index 7ccdae2e5a6..c2d6f57ce8a 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc
@@ -1,8 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include <atomic>
+
#include "UI_interface.h"
#include "UI_resources.h"
+#include "RNA_enum_types.h"
+
#include "NOD_socket_search_link.hh"
#include "BKE_type_conversions.hh"
@@ -68,7 +72,7 @@ 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(2));
- if (params.in_out() == SOCK_OUT) {
+ if (params.in_out() == SOCK_IN) {
const std::optional<eCustomDataType> type = node_data_type_to_custom_data_type(
static_cast<eNodeSocketDatatype>(params.other_socket().type));
if (type && *type != CD_PROP_STRING) {
@@ -85,41 +89,49 @@ static void node_gather_link_searches(GatherLinkSearchOpParams &params)
static void try_capture_field_on_geometry(GeometryComponent &component,
const StringRef name,
const eAttrDomain domain,
- const GField &field)
+ const GField &field,
+ std::atomic<bool> &r_failure)
{
- GeometryComponentFieldContext field_context{component, domain};
- const int domain_num = component.attribute_domain_num(domain);
- const IndexMask mask{IndexMask(domain_num)};
+ MutableAttributeAccessor attributes = *component.attributes_for_write();
+ const int domain_size = attributes.domain_size(domain);
+ if (domain_size == 0) {
+ return;
+ }
+
+ bke::GeometryFieldContext field_context{component, domain};
+ const IndexMask mask{IndexMask(domain_size)};
const CPPType &type = field.cpp_type();
const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(type);
/* Could avoid allocating a new buffer if:
- * - We are writing to an attribute that exists already.
+ * - We are writing to an attribute that exists already with the correct domain and type.
* - The field does not depend on that attribute (we can't easily check for that yet). */
- void *buffer = MEM_mallocN(type.size() * domain_num, __func__);
+ void *buffer = MEM_mallocN(type.size() * domain_size, __func__);
fn::FieldEvaluator evaluator{field_context, &mask};
- evaluator.add_with_destination(field, GMutableSpan{type, buffer, domain_num});
+ evaluator.add_with_destination(field, GMutableSpan{type, buffer, domain_size});
evaluator.evaluate();
- component.attribute_try_delete(name);
- if (component.attribute_exists(name)) {
- WriteAttributeLookup write_attribute = component.attribute_try_get_for_write(name);
- if (write_attribute && write_attribute.domain == domain &&
- write_attribute.varray.type() == type) {
- write_attribute.varray.set_all(buffer);
- write_attribute.tag_modified_fn();
- }
- else {
- /* Cannot change type of built-in attribute. */
+ if (GAttributeWriter attribute = attributes.lookup_for_write(name)) {
+ if (attribute.domain == domain && attribute.varray.type() == type) {
+ attribute.varray.set_all(buffer);
+ attribute.finish();
+ type.destruct_n(buffer, domain_size);
+ MEM_freeN(buffer);
+ return;
}
- type.destruct_n(buffer, domain_num);
- MEM_freeN(buffer);
}
- else {
- component.attribute_try_create(name, domain, data_type, AttributeInitMove{buffer});
+ attributes.remove(name);
+ if (attributes.add(name, domain, data_type, bke::AttributeInitMoveArray{buffer})) {
+ return;
}
+
+ /* If the name corresponds to a builtin attribute, removing the attribute might fail if
+ * it's required, and adding the attribute might fail if the domain or type is incorrect. */
+ type.destruct_n(buffer, domain_size);
+ MEM_freeN(buffer);
+ r_failure = true;
}
static void node_geo_exec(GeoNodeExecParams params)
@@ -170,12 +182,14 @@ static void node_geo_exec(GeoNodeExecParams params)
break;
}
+ std::atomic<bool> failure = false;
+
/* 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, name, domain, field);
+ try_capture_field_on_geometry(component, name, domain, field, failure);
}
}
else {
@@ -184,12 +198,26 @@ static void node_geo_exec(GeoNodeExecParams params)
{GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE}) {
if (geometry_set.has(type)) {
GeometryComponent &component = geometry_set.get_component_for_write(type);
- try_capture_field_on_geometry(component, name, domain, field);
+ try_capture_field_on_geometry(component, name, domain, field, failure);
}
}
});
}
+ if (failure) {
+ const char *domain_name = nullptr;
+ RNA_enum_name_from_value(rna_enum_attribute_domain_items, domain, &domain_name);
+ const char *type_name = nullptr;
+ RNA_enum_name_from_value(rna_enum_attribute_type_items, data_type, &type_name);
+ char *message = BLI_sprintfN(
+ TIP_("Failed to write to attribute \"%s\" with domain \"%s\" and type \"%s\""),
+ name.c_str(),
+ TIP_(domain_name),
+ TIP_(type_name));
+ params.error_message_add(NodeWarningType::Warning, message);
+ MEM_freeN(message);
+ }
+
params.set_output("Geometry", std::move(geometry_set));
}
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 33f5eccd719..afd7db6604d 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
@@ -4,7 +4,8 @@
#include "DNA_vfont_types.h"
#include "BKE_curve.h"
-#include "BKE_spline.hh"
+#include "BKE_curve_legacy_convert.hh"
+#include "BKE_curves.hh"
#include "BKE_vfont.h"
#include "BLI_hash.h"
@@ -101,7 +102,7 @@ static void node_update(bNodeTree *ntree, bNode *node)
ntree, height_socket, overflow != GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW);
}
-static float3 get_pivot_point(GeoNodeExecParams &params, CurveEval &curve)
+static float3 get_pivot_point(GeoNodeExecParams &params, bke::CurvesGeometry &curves)
{
const NodeGeometryStringToCurves &storage = node_storage(params.node());
const GeometryNodeStringToCurvesPivotMode pivot_mode = (GeometryNodeStringToCurvesPivotMode)
@@ -110,7 +111,7 @@ static float3 get_pivot_point(GeoNodeExecParams &params, CurveEval &curve)
float3 min(FLT_MAX), max(FLT_MIN);
/* Check if curve is empty. */
- if (!curve.bounds_min_max(min, max, false)) {
+ if (!curves.bounds_min_max(min, max)) {
return {0.0f, 0.0f, 0.0f};
}
@@ -156,12 +157,18 @@ struct TextLayout {
float final_font_size;
};
-static TextLayout get_text_layout(GeoNodeExecParams &params)
+static std::optional<TextLayout> get_text_layout(GeoNodeExecParams &params)
{
+ VFont *vfont = reinterpret_cast<VFont *>(params.node().id);
+ if (!vfont) {
+ params.error_message_add(NodeWarningType::Error, TIP_("Font not specified"));
+ return std::nullopt;
+ }
+
TextLayout layout;
layout.text = params.extract_input<std::string>("String");
if (layout.text.empty()) {
- return {};
+ return std::nullopt;
}
const NodeGeometryStringToCurves &storage = node_storage(params.node());
@@ -180,7 +187,6 @@ static TextLayout get_text_layout(GeoNodeExecParams &params)
const float textbox_h = overflow == GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW ?
0.0f :
params.extract_input<float>("Text Box Height");
- VFont *vfont = (VFont *)params.node().id;
Curve cu = dna::shallow_zero_initialize();
cu.type = OB_FONT;
@@ -264,7 +270,7 @@ static TextLayout get_text_layout(GeoNodeExecParams &params)
/* Returns a mapping of UTF-32 character code to instance handle. */
static Map<int, int> create_curve_instances(GeoNodeExecParams &params,
TextLayout &layout,
- InstancesComponent &instance_component)
+ InstancesComponent &instances)
{
VFont *vfont = (VFont *)params.node().id;
Map<int, int> handles;
@@ -282,22 +288,29 @@ static Map<int, int> create_curve_instances(GeoNodeExecParams &params,
charinfo.mat_nr = 1;
BKE_vfont_build_char(&cu, &cu.nurb, layout.char_codes[i], &charinfo, 0, 0, 0, i, 1);
- std::unique_ptr<CurveEval> curve_eval = curve_eval_from_dna_curve(cu);
+ Curves *curves_id = bke::curve_legacy_to_curves(cu);
+ if (curves_id == nullptr) {
+ if (pivot_required) {
+ layout.pivot_points.add_new(layout.char_codes[i], float3(0));
+ }
+ handles.add_new(layout.char_codes[i], instances.add_reference({}));
+ continue;
+ }
+
+ bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry);
BKE_nurbList_free(&cu.nurb);
float4x4 size_matrix = float4x4::identity();
size_matrix.apply_scale(layout.final_font_size);
- curve_eval->transform(size_matrix);
+ curves.transform(size_matrix);
if (pivot_required) {
- float3 pivot_point = get_pivot_point(params, *curve_eval);
+ float3 pivot_point = get_pivot_point(params, curves);
layout.pivot_points.add_new(layout.char_codes[i], pivot_point);
}
- GeometrySet geometry_set_curve = GeometrySet::create_with_curves(
- curve_eval_to_curves(*curve_eval));
- handles.add_new(layout.char_codes[i],
- instance_component.add_reference(std::move(geometry_set_curve)));
+ GeometrySet geometry_set = GeometrySet::create_with_curves(curves_id);
+ handles.add_new(layout.char_codes[i], instances.add_reference(std::move(geometry_set)));
}
return handles;
}
@@ -322,13 +335,14 @@ static void create_attributes(GeoNodeExecParams &params,
const TextLayout &layout,
InstancesComponent &instances)
{
+ MutableAttributeAccessor attributes = *instances.attributes_for_write();
+
if (params.output_is_required("Line")) {
StrongAnonymousAttributeID line_id = StrongAnonymousAttributeID("Line");
- OutputAttribute_Typed<int> line_attribute = instances.attribute_try_get_for_output_only<int>(
+ SpanAttributeWriter<int> line_attribute = attributes.lookup_or_add_for_write_only_span<int>(
line_id.get(), ATTR_DOMAIN_INSTANCE);
- MutableSpan<int> lines = line_attribute.as_span();
- lines.copy_from(layout.line_numbers);
- line_attribute.save();
+ line_attribute.span.copy_from(layout.line_numbers);
+ line_attribute.finish();
params.set_output("Line",
AnonymousAttributeFieldInput::Create<int>(std::move(line_id),
params.attribute_producer_name()));
@@ -336,15 +350,14 @@ static void create_attributes(GeoNodeExecParams &params,
if (params.output_is_required("Pivot Point")) {
StrongAnonymousAttributeID pivot_id = StrongAnonymousAttributeID("Pivot");
- OutputAttribute_Typed<float3> pivot_attribute =
- instances.attribute_try_get_for_output_only<float3>(pivot_id.get(), ATTR_DOMAIN_INSTANCE);
- MutableSpan<float3> pivots = pivot_attribute.as_span();
+ SpanAttributeWriter<float3> pivot_attribute =
+ attributes.lookup_or_add_for_write_only_span<float3>(pivot_id.get(), ATTR_DOMAIN_INSTANCE);
for (const int i : layout.char_codes.index_range()) {
- pivots[i] = layout.pivot_points.lookup(layout.char_codes[i]);
+ pivot_attribute.span[i] = layout.pivot_points.lookup(layout.char_codes[i]);
}
- pivot_attribute.save();
+ pivot_attribute.finish();
params.set_output("Pivot Point",
AnonymousAttributeFieldInput::Create<float3>(
std::move(pivot_id), params.attribute_producer_name()));
@@ -353,15 +366,19 @@ static void create_attributes(GeoNodeExecParams &params,
static void node_geo_exec(GeoNodeExecParams params)
{
- TextLayout layout = get_text_layout(params);
+ std::optional<TextLayout> layout = get_text_layout(params);
+ if (!layout) {
+ params.set_default_remaining_outputs();
+ return;
+ }
const NodeGeometryStringToCurves &storage =
*(const NodeGeometryStringToCurves *)params.node().storage;
if (storage.overflow == GEO_NODE_STRING_TO_CURVES_MODE_TRUNCATE) {
- params.set_output("Remainder", std::move(layout.truncated_text));
+ params.set_output("Remainder", std::move(layout->truncated_text));
}
- if (layout.positions.size() == 0) {
+ if (layout->positions.size() == 0) {
params.set_output("Curve Instances", GeometrySet());
params.set_default_remaining_outputs();
return;
@@ -370,9 +387,9 @@ static void node_geo_exec(GeoNodeExecParams params)
/* Create and add instances. */
GeometrySet geometry_set_out;
InstancesComponent &instances = geometry_set_out.get_component_for_write<InstancesComponent>();
- Map<int, int> char_handles = create_curve_instances(params, layout, instances);
- add_instances_from_handles(instances, char_handles, layout);
- create_attributes(params, layout, instances);
+ Map<int, int> char_handles = create_curve_instances(params, *layout, instances);
+ add_instances_from_handles(instances, char_handles, *layout);
+ create_attributes(params, *layout, instances);
params.set_output("Curve Instances", std::move(geometry_set_out));
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc
index 9eda5bb34ff..60c8a89a6bf 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc
@@ -6,6 +6,7 @@
#include "DNA_meshdata_types.h"
#include "DNA_modifier_types.h"
+#include "BKE_attribute.hh"
#include "BKE_mesh.h"
#include "BKE_subdiv.h"
#include "BKE_subdiv_mesh.h"
@@ -72,17 +73,18 @@ static void write_vertex_creases(Mesh &mesh, const VArray<float> &crease_varray)
}
else {
crease = static_cast<float *>(
- CustomData_add_layer(&mesh.vdata, CD_CREASE, CD_DEFAULT, nullptr, mesh.totvert));
+ CustomData_add_layer(&mesh.vdata, CD_CREASE, CD_CONSTRUCT, nullptr, mesh.totvert));
}
materialize_and_clamp_creases(crease_varray, {crease, mesh.totvert});
}
static void write_edge_creases(MeshComponent &mesh, const VArray<float> &crease_varray)
{
- OutputAttribute_Typed<float> attribute = mesh.attribute_try_get_for_output_only<float>(
- "crease", ATTR_DOMAIN_EDGE);
- materialize_and_clamp_creases(crease_varray, attribute.as_span());
- attribute.save();
+ bke::SpanAttributeWriter<float> attribute =
+ mesh.attributes_for_write()->lookup_or_add_for_write_only_span<float>("crease",
+ ATTR_DOMAIN_EDGE);
+ materialize_and_clamp_creases(crease_varray, attribute.span);
+ attribute.finish();
}
static bool varray_is_nonzero(const VArray<float> &varray)
@@ -117,21 +119,19 @@ static void node_geo_exec(GeoNodeExecParams params)
return;
}
- const MeshComponent &mesh_component = *geometry_set.get_component_for_read<MeshComponent>();
- const int verts_num = mesh_component.attribute_domain_num(ATTR_DOMAIN_POINT);
- const int edges_num = mesh_component.attribute_domain_num(ATTR_DOMAIN_EDGE);
- if (verts_num == 0 || edges_num == 0) {
+ const Mesh &mesh = *geometry_set.get_mesh_for_read();
+ if (mesh.totvert == 0 || mesh.totedge == 0) {
return;
}
- GeometryComponentFieldContext point_context{mesh_component, ATTR_DOMAIN_POINT};
- FieldEvaluator point_evaluator(point_context, verts_num);
+ bke::MeshFieldContext point_context{mesh, ATTR_DOMAIN_POINT};
+ FieldEvaluator point_evaluator(point_context, mesh.totvert);
point_evaluator.add(vertex_crease_field);
point_evaluator.evaluate();
const VArray<float> vertex_creases = point_evaluator.get_evaluated<float>(0);
- GeometryComponentFieldContext edge_context{mesh_component, ATTR_DOMAIN_EDGE};
- FieldEvaluator edge_evaluator(edge_context, edges_num);
+ bke::MeshFieldContext edge_context{mesh, ATTR_DOMAIN_EDGE};
+ FieldEvaluator edge_evaluator(edge_context, mesh.totedge);
edge_evaluator.add(edge_crease_field);
edge_evaluator.evaluate();
const VArray<float> edge_creases = edge_evaluator.get_evaluated<float>(0);
@@ -160,17 +160,15 @@ static void node_geo_exec(GeoNodeExecParams params)
subdiv_settings.fvar_linear_interpolation = BKE_subdiv_fvar_interpolation_from_uv_smooth(
uv_smooth);
- const Mesh &mesh_in = *geometry_set.get_mesh_for_read();
-
/* Apply subdivision to mesh. */
- Subdiv *subdiv = BKE_subdiv_update_from_mesh(nullptr, &subdiv_settings, &mesh_in);
+ Subdiv *subdiv = BKE_subdiv_update_from_mesh(nullptr, &subdiv_settings, &mesh);
/* 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);
+ Mesh *mesh_out = BKE_subdiv_to_mesh(subdiv, &mesh_settings, &mesh);
geometry_set.replace_mesh(mesh_out);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc
index 0af6c76feaf..afc492c40e4 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc
@@ -10,6 +10,7 @@
#include "BKE_attribute_math.hh"
#include "BKE_bvhutils.h"
+#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
#include "BKE_mesh_sample.hh"
@@ -234,12 +235,12 @@ static void get_closest_mesh_looptris(const Mesh &mesh,
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)
+static void get_closest_mesh_polys(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);
@@ -263,23 +264,27 @@ static void get_closest_mesh_corners(const Mesh &mesh,
const MutableSpan<float> r_distances_sq,
const MutableSpan<float3> r_positions)
{
+ const Span<MVert> verts = mesh.verts();
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
+
BLI_assert(mesh.totloop > 0);
Array<int> poly_indices(positions.size());
- get_closest_mesh_polygons(mesh, positions, mask, poly_indices, {}, {});
+ get_closest_mesh_polys(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];
+ const MPoly &poly = polys[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 MLoop &loop = loops[loop_index];
const int vertex_index = loop.v;
- const MVert &mvert = mesh.mvert[vertex_index];
+ const MVert &mvert = verts[vertex_index];
const float distance_sq = math::distance_squared(position, float3(mvert.co));
if (distance_sq < min_distance_sq) {
min_distance_sq = distance_sq;
@@ -365,7 +370,7 @@ static bool component_is_available(const GeometrySet &geometry,
if (component.is_empty()) {
return false;
}
- return component.attribute_domain_num(domain) != 0;
+ return component.attribute_domain_size(domain) != 0;
}
/**
@@ -387,7 +392,7 @@ class NearestInterpolatedTransferFunction : public fn::MultiFunction {
fn::MFSignature signature_;
- std::optional<GeometryComponentFieldContext> source_context_;
+ std::optional<bke::MeshFieldContext> source_context_;
std::unique_ptr<FieldEvaluator> source_evaluator_;
const GVArray *source_data_;
@@ -431,10 +436,10 @@ class NearestInterpolatedTransferFunction : public fn::MultiFunction {
private:
void evaluate_source_field()
{
- const MeshComponent &mesh_component = *source_.get_component_for_read<MeshComponent>();
- source_context_.emplace(GeometryComponentFieldContext{mesh_component, domain_});
- const int domain_num = mesh_component.attribute_domain_num(domain_);
- source_evaluator_ = std::make_unique<FieldEvaluator>(*source_context_, domain_num);
+ const Mesh &mesh = *source_.get_mesh_for_read();
+ source_context_.emplace(bke::MeshFieldContext{mesh, domain_});
+ const int domain_size = mesh.attributes().domain_size(domain_);
+ source_evaluator_ = std::make_unique<FieldEvaluator>(*source_context_, domain_size);
source_evaluator_->add(src_field_);
source_evaluator_->evaluate();
source_data_ = &source_evaluator_->get_evaluated(0);
@@ -457,11 +462,11 @@ class NearestTransferFunction : public fn::MultiFunction {
bool use_points_;
/* Store data from the source as a virtual array, since we may only access a few indices. */
- std::optional<GeometryComponentFieldContext> mesh_context_;
+ std::optional<bke::MeshFieldContext> mesh_context_;
std::unique_ptr<FieldEvaluator> mesh_evaluator_;
const GVArray *mesh_data_;
- std::optional<GeometryComponentFieldContext> point_context_;
+ std::optional<bke::PointCloudFieldContext> point_context_;
std::unique_ptr<FieldEvaluator> point_evaluator_;
const GVArray *point_data_;
@@ -535,7 +540,7 @@ class NearestTransferFunction : public fn::MultiFunction {
break;
}
case ATTR_DOMAIN_FACE: {
- get_closest_mesh_polygons(*mesh, positions, mask, mesh_indices, mesh_distances, {});
+ get_closest_mesh_polys(*mesh, positions, mask, mesh_indices, mesh_distances, {});
break;
}
case ATTR_DOMAIN_CORNER: {
@@ -577,20 +582,19 @@ class NearestTransferFunction : public fn::MultiFunction {
void evaluate_source_field()
{
if (use_mesh_) {
- const MeshComponent &mesh = *source_.get_component_for_read<MeshComponent>();
- const int domain_num = mesh.attribute_domain_num(domain_);
- mesh_context_.emplace(GeometryComponentFieldContext(mesh, domain_));
- mesh_evaluator_ = std::make_unique<FieldEvaluator>(*mesh_context_, domain_num);
+ const Mesh &mesh = *source_.get_mesh_for_read();
+ const int domain_size = mesh.attributes().domain_size(domain_);
+ mesh_context_.emplace(bke::MeshFieldContext(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 = *source_.get_component_for_read<PointCloudComponent>();
- const int domain_num = points.attribute_domain_num(domain_);
- point_context_.emplace(GeometryComponentFieldContext(points, domain_));
- point_evaluator_ = std::make_unique<FieldEvaluator>(*point_context_, domain_num);
+ const PointCloud &points = *source_.get_pointcloud_for_read();
+ point_context_.emplace(bke::PointCloudFieldContext(points));
+ point_evaluator_ = std::make_unique<FieldEvaluator>(*point_context_, points.totpoint);
point_evaluator_->add(src_field_);
point_evaluator_->evaluate();
point_data_ = &point_evaluator_->get_evaluated(0);
@@ -628,7 +632,7 @@ class IndexTransferFunction : public fn::MultiFunction {
fn::MFSignature signature_;
- std::optional<GeometryComponentFieldContext> geometry_context_;
+ std::optional<bke::GeometryFieldContext> geometry_context_;
std::unique_ptr<FieldEvaluator> evaluator_;
const GVArray *src_data_ = nullptr;
@@ -658,8 +662,8 @@ class IndexTransferFunction : public fn::MultiFunction {
if (component == nullptr) {
return;
}
- const int domain_num = component->attribute_domain_num(domain_);
- geometry_context_.emplace(GeometryComponentFieldContext(*component, domain_));
+ const int domain_num = component->attribute_domain_size(domain_);
+ geometry_context_.emplace(bke::GeometryFieldContext(*component, domain_));
evaluator_ = std::make_unique<FieldEvaluator>(*geometry_context_, domain_num);
evaluator_->add(src_field_);
evaluator_->evaluate();
diff --git a/source/blender/nodes/geometry/nodes/node_geo_transform.cc b/source/blender/nodes/geometry/nodes/node_geo_transform.cc
index e95db205920..4130cad3bda 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_transform.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_transform.cc
@@ -47,21 +47,24 @@ static void transform_mesh(Mesh &mesh, const float4x4 &transform)
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);
+ MutableAttributeAccessor attributes = pointcloud.attributes_for_write();
+ SpanAttributeWriter position = attributes.lookup_or_add_for_write_span<float3>(
+ "position", ATTR_DOMAIN_POINT);
+ for (const int i : position.span.index_range()) {
+ position.span[i] += translation;
}
+ position.finish();
}
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;
+ MutableAttributeAccessor attributes = pointcloud.attributes_for_write();
+ SpanAttributeWriter position = attributes.lookup_or_add_for_write_span<float3>(
+ "position", ATTR_DOMAIN_POINT);
+ for (const int i : position.span.index_range()) {
+ position.span[i] = transform * position.span[i];
}
+ position.finish();
}
static void translate_instances(InstancesComponent &instances, const float3 translation)
@@ -80,47 +83,93 @@ static void transform_instances(InstancesComponent &instances, const float4x4 &t
}
}
-static void transform_volume(Volume &volume, const float4x4 &transform, const Depsgraph &depsgraph)
+static void transform_volume(GeoNodeExecParams &params,
+ 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(&depsgraph);
BKE_volume_load(&volume, bmain);
openvdb::Mat4s vdb_matrix;
- memcpy(vdb_matrix.asPointer(), &scale_limited_transform, sizeof(float[4][4]));
+ memcpy(vdb_matrix.asPointer(), &transform, sizeof(float[4][4]));
openvdb::Mat4d vdb_matrix_d{vdb_matrix};
+ bool found_too_small_scale = false;
const int grids_num = BKE_volume_num_grids(&volume);
for (const int i : IndexRange(grids_num)) {
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::math::Transform &grid_transform = grid->transform();
- grid_transform.postMult(vdb_matrix_d);
+ float4x4 grid_matrix;
+ BKE_volume_grid_transform_matrix(volume_grid, grid_matrix.values);
+ mul_m4_m4_pre(grid_matrix.values, transform.values);
+ const float determinant = determinant_m4(grid_matrix.values);
+ if (!BKE_volume_grid_determinant_valid(determinant)) {
+ found_too_small_scale = true;
+ /* Clear the tree because it is too small. */
+ BKE_volume_grid_clear_tree(volume, *volume_grid);
+ if (determinant == 0) {
+ /* Reset rotation and scale. */
+ copy_v3_fl3(grid_matrix.values[0], 1, 0, 0);
+ copy_v3_fl3(grid_matrix.values[1], 0, 1, 0);
+ copy_v3_fl3(grid_matrix.values[2], 0, 0, 1);
+ }
+ else {
+ /* Keep rotation but reset scale. */
+ normalize_v3(grid_matrix.values[0]);
+ normalize_v3(grid_matrix.values[1]);
+ normalize_v3(grid_matrix.values[2]);
+ }
+ }
+ BKE_volume_grid_transform_matrix_set(volume_grid, grid_matrix.values);
+ }
+ if (found_too_small_scale) {
+ params.error_message_add(NodeWarningType::Warning,
+ TIP_("Volume scale is lower than permitted by OpenVDB"));
}
#else
- UNUSED_VARS(volume, transform, depsgraph);
+ UNUSED_VARS(params, volume, transform, depsgraph);
#endif
}
-static void translate_volume(Volume &volume, const float3 translation, const Depsgraph &depsgraph)
+static void translate_volume(GeoNodeExecParams &params,
+ Volume &volume,
+ const float3 translation,
+ const Depsgraph &depsgraph)
+{
+ transform_volume(params, volume, float4x4::from_location(translation), depsgraph);
+}
+
+static void transform_curve_edit_hints(bke::CurvesEditHints &edit_hints, const float4x4 &transform)
+{
+ if (edit_hints.positions.has_value()) {
+ for (float3 &pos : *edit_hints.positions) {
+ pos = transform * pos;
+ }
+ }
+ float3x3 deform_mat;
+ copy_m3_m4(deform_mat.values, transform.values);
+ if (edit_hints.deform_mats.has_value()) {
+ for (float3x3 &mat : *edit_hints.deform_mats) {
+ mat = deform_mat * mat;
+ }
+ }
+ else {
+ edit_hints.deform_mats.emplace(edit_hints.curves_id_orig.geometry.point_num, deform_mat);
+ }
+}
+
+static void translate_curve_edit_hints(bke::CurvesEditHints &edit_hints, const float3 &translation)
{
- transform_volume(volume, float4x4::from_location(translation), depsgraph);
+ if (edit_hints.positions.has_value()) {
+ for (float3 &pos : *edit_hints.positions) {
+ pos += translation;
+ }
+ }
}
-static void translate_geometry_set(GeometrySet &geometry,
+static void translate_geometry_set(GeoNodeExecParams &params,
+ GeometrySet &geometry,
const float3 translation,
const Depsgraph &depsgraph)
{
@@ -134,14 +183,18 @@ static void translate_geometry_set(GeometrySet &geometry,
translate_pointcloud(*pointcloud, translation);
}
if (Volume *volume = geometry.get_volume_for_write()) {
- translate_volume(*volume, translation, depsgraph);
+ translate_volume(params, *volume, translation, depsgraph);
}
if (geometry.has_instances()) {
translate_instances(geometry.get_component_for_write<InstancesComponent>(), translation);
}
+ if (bke::CurvesEditHints *curve_edit_hints = geometry.get_curve_edit_hints_for_write()) {
+ translate_curve_edit_hints(*curve_edit_hints, translation);
+ }
}
-void transform_geometry_set(GeometrySet &geometry,
+void transform_geometry_set(GeoNodeExecParams &params,
+ GeometrySet &geometry,
const float4x4 &transform,
const Depsgraph &depsgraph)
{
@@ -155,11 +208,14 @@ void transform_geometry_set(GeometrySet &geometry,
transform_pointcloud(*pointcloud, transform);
}
if (Volume *volume = geometry.get_volume_for_write()) {
- transform_volume(*volume, transform, depsgraph);
+ transform_volume(params, *volume, transform, depsgraph);
}
if (geometry.has_instances()) {
transform_instances(geometry.get_component_for_write<InstancesComponent>(), transform);
}
+ if (bke::CurvesEditHints *curve_edit_hints = geometry.get_curve_edit_hints_for_write()) {
+ transform_curve_edit_hints(*curve_edit_hints, transform);
+ }
}
void transform_mesh(Mesh &mesh,
@@ -193,10 +249,11 @@ static void node_geo_exec(GeoNodeExecParams params)
/* Use only translation if rotation and scale don't apply. */
if (use_translate(rotation, scale)) {
- translate_geometry_set(geometry_set, translation, *params.depsgraph());
+ translate_geometry_set(params, geometry_set, translation, *params.depsgraph());
}
else {
- transform_geometry_set(geometry_set,
+ transform_geometry_set(params,
+ geometry_set,
float4x4::from_loc_eul_scale(translation, rotation, scale),
*params.depsgraph());
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc
index 258c1ac3fba..3e9fe99adb0 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc
@@ -17,17 +17,16 @@ static void node_declare(NodeDeclarationBuilder &b)
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_num()};
+ const bke::InstancesFieldContext context{instances_component};
+ fn::FieldEvaluator evaluator{context, instances_component.instances_num()};
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);
+ 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();
diff --git a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc
index e47dc22da04..57487059437 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc
@@ -47,9 +47,6 @@ static Mesh *triangulate_mesh_selection(const Mesh &mesh,
BMeshFromMeshParams from_mesh_params{};
from_mesh_params.calc_face_normal = true;
from_mesh_params.calc_vert_normal = true;
- from_mesh_params.add_key_index = true;
- from_mesh_params.use_shapekey = true;
- from_mesh_params.active_shapekey = 1;
from_mesh_params.cd_mask_extra = cd_mask_extra;
BMesh *bm = BKE_mesh_to_bmesh_ex(&mesh, &create_params, &from_mesh_params);
@@ -80,12 +77,10 @@ static void node_geo_exec(GeoNodeExecParams params)
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_num = component.attribute_domain_num(ATTR_DOMAIN_FACE);
- GeometryComponentFieldContext context{component, ATTR_DOMAIN_FACE};
- FieldEvaluator evaluator{context, domain_num};
+ bke::MeshFieldContext context{mesh_in, ATTR_DOMAIN_FACE};
+ FieldEvaluator evaluator{context, mesh_in.totpoly};
evaluator.add(selection_field);
evaluator.evaluate();
const IndexMask selection = evaluator.get_evaluated_as_mask(0);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_uv_pack_islands.cc b/source/blender/nodes/geometry/nodes/node_geo_uv_pack_islands.cc
new file mode 100644
index 00000000000..ccb489f6e29
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_uv_pack_islands.cc
@@ -0,0 +1,146 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "GEO_uv_parametrizer.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_mesh.h"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_uv_pack_islands_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Vector>(N_("UV")).hide_value().supports_field();
+ b.add_input<decl::Bool>(N_("Selection"))
+ .default_value(true)
+ .hide_value()
+ .supports_field()
+ .description(N_("Faces to consider when packing islands"));
+ b.add_input<decl::Float>(N_("Margin"))
+ .default_value(0.001f)
+ .min(0.0f)
+ .max(1.0f)
+ .description(N_("Space between islands"));
+ b.add_input<decl::Bool>(N_("Rotate"))
+ .default_value(true)
+ .description(N_("Rotate islands for best fit"));
+ b.add_output<decl::Vector>(N_("UV")).field_source();
+}
+
+static VArray<float3> construct_uv_gvarray(const Mesh &mesh,
+ const Field<bool> selection_field,
+ const Field<float3> uv_field,
+ const bool rotate,
+ const float margin,
+ const eAttrDomain domain)
+{
+ const Span<MVert> verts = mesh.verts();
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
+
+ bke::MeshFieldContext face_context{mesh, ATTR_DOMAIN_FACE};
+ FieldEvaluator face_evaluator{face_context, polys.size()};
+ face_evaluator.add(selection_field);
+ face_evaluator.evaluate();
+ const IndexMask selection = face_evaluator.get_evaluated_as_mask(0);
+ if (selection.is_empty()) {
+ return {};
+ }
+
+ bke::MeshFieldContext corner_context{mesh, ATTR_DOMAIN_CORNER};
+ FieldEvaluator evaluator{corner_context, mesh.totloop};
+ Array<float3> uv(mesh.totloop);
+ evaluator.add_with_destination(uv_field, uv.as_mutable_span());
+ evaluator.evaluate();
+
+ ParamHandle *handle = GEO_uv_parametrizer_construct_begin();
+ for (const int mp_index : selection) {
+ const MPoly &mp = polys[mp_index];
+ Array<ParamKey, 16> mp_vkeys(mp.totloop);
+ Array<bool, 16> mp_pin(mp.totloop);
+ Array<bool, 16> mp_select(mp.totloop);
+ Array<const float *, 16> mp_co(mp.totloop);
+ Array<float *, 16> mp_uv(mp.totloop);
+ for (const int i : IndexRange(mp.totloop)) {
+ const MLoop &ml = loops[mp.loopstart + i];
+ mp_vkeys[i] = ml.v;
+ mp_co[i] = verts[ml.v].co;
+ mp_uv[i] = uv[mp.loopstart + i];
+ mp_pin[i] = false;
+ mp_select[i] = false;
+ }
+ GEO_uv_parametrizer_face_add(handle,
+ mp_index,
+ mp.totloop,
+ mp_vkeys.data(),
+ mp_co.data(),
+ mp_uv.data(),
+ mp_pin.data(),
+ mp_select.data());
+ }
+ GEO_uv_parametrizer_construct_end(handle, true, true, nullptr);
+
+ GEO_uv_parametrizer_pack(handle, margin, rotate, true);
+ GEO_uv_parametrizer_flush(handle);
+ GEO_uv_parametrizer_delete(handle);
+
+ return mesh.attributes().adapt_domain<float3>(
+ VArray<float3>::ForContainer(std::move(uv)), ATTR_DOMAIN_CORNER, domain);
+}
+
+class PackIslandsFieldInput final : public bke::MeshFieldInput {
+ private:
+ const Field<bool> selection_field;
+ const Field<float3> uv_field;
+ const bool rotate;
+ const float margin;
+
+ public:
+ PackIslandsFieldInput(const Field<bool> selection_field,
+ const Field<float3> uv_field,
+ const bool rotate,
+ const float margin)
+ : bke::MeshFieldInput(CPPType::get<float3>(), "Pack UV Islands Field"),
+ selection_field(selection_field),
+ uv_field(uv_field),
+ rotate(rotate),
+ margin(margin)
+ {
+ category_ = Category::Generated;
+ }
+
+ GVArray get_varray_for_context(const Mesh &mesh,
+ const eAttrDomain domain,
+ IndexMask UNUSED(mask)) const final
+ {
+ return construct_uv_gvarray(mesh, selection_field, uv_field, rotate, margin, domain);
+ }
+};
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ const Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
+ const Field<float3> uv_field = params.extract_input<Field<float3>>("UV");
+ const bool rotate = params.extract_input<bool>("Rotate");
+ const float margin = params.extract_input<float>("Margin");
+ params.set_output("UV",
+ Field<float3>(std::make_shared<PackIslandsFieldInput>(
+ selection_field, uv_field, rotate, margin)));
+}
+
+} // namespace blender::nodes::node_geo_uv_pack_islands_cc
+
+void register_node_type_geo_uv_pack_islands()
+{
+ namespace file_ns = blender::nodes::node_geo_uv_pack_islands_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_UV_PACK_ISLANDS, "Pack UV Islands", NODE_CLASS_CONVERTER);
+ 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_uv_unwrap.cc b/source/blender/nodes/geometry/nodes/node_geo_uv_unwrap.cc
new file mode 100644
index 00000000000..801bc3f4642
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_uv_unwrap.cc
@@ -0,0 +1,194 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "GEO_uv_parametrizer.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_mesh.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_uv_unwrap_cc {
+
+NODE_STORAGE_FUNCS(NodeGeometryUVUnwrap)
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Bool>(N_("Selection"))
+ .default_value(true)
+ .hide_value()
+ .supports_field()
+ .description(N_("Faces to participate in the unwrap operation"));
+ b.add_input<decl::Bool>(N_("Seam"))
+ .hide_value()
+ .supports_field()
+ .description(N_("Edges to mark where the mesh is \"cut\" for the purposes of unwrapping"));
+ b.add_input<decl::Float>(N_("Margin"))
+ .default_value(0.001f)
+ .min(0.0f)
+ .max(1.0f)
+ .description(N_("Space between islands"));
+ b.add_input<decl::Bool>(N_("Fill Holes"))
+ .default_value(true)
+ .description(N_("Virtually fill holes in mesh before unwrapping, to better avoid overlaps "
+ "and preserve symmetry"));
+ b.add_output<decl::Vector>(N_("UV")).field_source().description(
+ N_("UV coordinates between 0 and 1 for each face corner in the selected faces"));
+}
+
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
+ uiItemR(layout, ptr, "method", 0, "", ICON_NONE);
+}
+
+static void node_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometryUVUnwrap *data = MEM_cnew<NodeGeometryUVUnwrap>(__func__);
+ data->method = GEO_NODE_UV_UNWRAP_METHOD_ANGLE_BASED;
+ node->storage = data;
+}
+
+static VArray<float3> construct_uv_gvarray(const Mesh &mesh,
+ const Field<bool> selection_field,
+ const Field<bool> seam_field,
+ const bool fill_holes,
+ const float margin,
+ const GeometryNodeUVUnwrapMethod method,
+ const eAttrDomain domain)
+{
+ const Span<MVert> verts = mesh.verts();
+ const Span<MEdge> edges = mesh.edges();
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
+
+ bke::MeshFieldContext face_context{mesh, ATTR_DOMAIN_FACE};
+ FieldEvaluator face_evaluator{face_context, polys.size()};
+ face_evaluator.add(selection_field);
+ face_evaluator.evaluate();
+ const IndexMask selection = face_evaluator.get_evaluated_as_mask(0);
+ if (selection.is_empty()) {
+ return {};
+ }
+
+ bke::MeshFieldContext edge_context{mesh, ATTR_DOMAIN_EDGE};
+ FieldEvaluator edge_evaluator{edge_context, edges.size()};
+ edge_evaluator.add(seam_field);
+ edge_evaluator.evaluate();
+ const IndexMask seam = edge_evaluator.get_evaluated_as_mask(0);
+
+ Array<float3> uv(loops.size(), float3(0));
+
+ ParamHandle *handle = GEO_uv_parametrizer_construct_begin();
+ for (const int mp_index : selection) {
+ const MPoly &mp = polys[mp_index];
+ Array<ParamKey, 16> mp_vkeys(mp.totloop);
+ Array<bool, 16> mp_pin(mp.totloop);
+ Array<bool, 16> mp_select(mp.totloop);
+ Array<const float *, 16> mp_co(mp.totloop);
+ Array<float *, 16> mp_uv(mp.totloop);
+ for (const int i : IndexRange(mp.totloop)) {
+ const MLoop &ml = loops[mp.loopstart + i];
+ mp_vkeys[i] = ml.v;
+ mp_co[i] = verts[ml.v].co;
+ mp_uv[i] = uv[mp.loopstart + i];
+ mp_pin[i] = false;
+ mp_select[i] = false;
+ }
+ GEO_uv_parametrizer_face_add(handle,
+ mp_index,
+ mp.totloop,
+ mp_vkeys.data(),
+ mp_co.data(),
+ mp_uv.data(),
+ mp_pin.data(),
+ mp_select.data());
+ }
+ for (const int i : seam) {
+ const MEdge &edge = edges[i];
+ ParamKey vkeys[2]{edge.v1, edge.v2};
+ GEO_uv_parametrizer_edge_set_seam(handle, vkeys);
+ }
+ /* TODO: once field input nodes are able to emit warnings (T94039), emit a
+ * warning if we fail to solve an island. */
+ GEO_uv_parametrizer_construct_end(handle, fill_holes, false, nullptr);
+
+ GEO_uv_parametrizer_lscm_begin(handle, false, method == GEO_NODE_UV_UNWRAP_METHOD_ANGLE_BASED);
+ GEO_uv_parametrizer_lscm_solve(handle, nullptr, nullptr);
+ GEO_uv_parametrizer_lscm_end(handle);
+ GEO_uv_parametrizer_average(handle, true, false, false);
+ GEO_uv_parametrizer_pack(handle, margin, true, true);
+ GEO_uv_parametrizer_flush(handle);
+ GEO_uv_parametrizer_delete(handle);
+
+ return mesh.attributes().adapt_domain<float3>(
+ VArray<float3>::ForContainer(std::move(uv)), ATTR_DOMAIN_CORNER, domain);
+}
+
+class UnwrapFieldInput final : public bke::MeshFieldInput {
+ private:
+ const Field<bool> selection;
+ const Field<bool> seam;
+ const bool fill_holes;
+ const float margin;
+ const GeometryNodeUVUnwrapMethod method;
+
+ public:
+ UnwrapFieldInput(const Field<bool> selection,
+ const Field<bool> seam,
+ const bool fill_holes,
+ const float margin,
+ const GeometryNodeUVUnwrapMethod method)
+ : bke::MeshFieldInput(CPPType::get<float3>(), "UV Unwrap Field"),
+ selection(selection),
+ seam(seam),
+ fill_holes(fill_holes),
+ margin(margin),
+ method(method)
+ {
+ category_ = Category::Generated;
+ }
+
+ GVArray get_varray_for_context(const Mesh &mesh,
+ const eAttrDomain domain,
+ IndexMask UNUSED(mask)) const final
+ {
+ return construct_uv_gvarray(mesh, selection, seam, fill_holes, margin, method, domain);
+ }
+};
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ const NodeGeometryUVUnwrap &storage = node_storage(params.node());
+ const GeometryNodeUVUnwrapMethod method = (GeometryNodeUVUnwrapMethod)storage.method;
+ const Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
+ const Field<bool> seam_field = params.extract_input<Field<bool>>("Seam");
+ const bool fill_holes = params.extract_input<bool>("Fill Holes");
+ const float margin = params.extract_input<float>("Margin");
+ params.set_output("UV",
+ Field<float3>(std::make_shared<UnwrapFieldInput>(
+ selection_field, seam_field, fill_holes, margin, method)));
+}
+
+} // namespace blender::nodes::node_geo_uv_unwrap_cc
+
+void register_node_type_geo_uv_unwrap()
+{
+ namespace file_ns = blender::nodes::node_geo_uv_unwrap_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_UV_UNWRAP, "UV Unwrap", NODE_CLASS_CONVERTER);
+ node_type_init(&ntype, file_ns::node_init);
+ node_type_storage(
+ &ntype, "NodeGeometryUVUnwrap", 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_volume_cube.cc b/source/blender/nodes/geometry/nodes/node_geo_volume_cube.cc
new file mode 100644
index 00000000000..e964bf03ed2
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_volume_cube.cc
@@ -0,0 +1,204 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#ifdef WITH_OPENVDB
+# include <openvdb/openvdb.h>
+# include <openvdb/tools/Dense.h>
+# include <openvdb/tools/LevelSetUtil.h>
+# include <openvdb/tools/ParticlesToLevelSet.h>
+#endif
+
+#include "node_geometry_util.hh"
+
+#include "DNA_mesh_types.h"
+
+#include "BLI_task.hh"
+
+#include "BKE_geometry_set.hh"
+#include "BKE_lib_id.h"
+#include "BKE_mesh.h"
+#include "BKE_volume.h"
+
+namespace blender::nodes::node_geo_volume_cube_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Float>(N_("Density"))
+ .description(N_("Volume density per voxel"))
+ .supports_field()
+ .default_value(1.0f);
+ b.add_input<decl::Float>(N_("Background"))
+ .description(N_("Value for voxels outside of the cube"));
+
+ b.add_input<decl::Vector>(N_("Min"))
+ .description(N_("Minimum boundary of volume"))
+ .default_value(float3(-1.0f));
+ b.add_input<decl::Vector>(N_("Max"))
+ .description(N_("Maximum boundary of volume"))
+ .default_value(float3(1.0f));
+
+ b.add_input<decl::Int>(N_("Resolution X"))
+ .description(N_("Number of voxels in the X axis"))
+ .default_value(32)
+ .min(2);
+ b.add_input<decl::Int>(N_("Resolution Y"))
+ .description(N_("Number of voxels in the Y axis"))
+ .default_value(32)
+ .min(2);
+ b.add_input<decl::Int>(N_("Resolution Z"))
+ .description(N_("Number of voxels in the Z axis"))
+ .default_value(32)
+ .min(2);
+
+ b.add_output<decl::Geometry>(N_("Volume"));
+}
+
+static float map(const float x,
+ const float in_min,
+ const float in_max,
+ const float out_min,
+ const float out_max)
+{
+ return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
+}
+
+class Grid3DFieldContext : public FieldContext {
+ private:
+ int3 resolution_;
+ float3 bounds_min_;
+ float3 bounds_max_;
+
+ public:
+ Grid3DFieldContext(const int3 resolution, const float3 bounds_min, const float3 bounds_max)
+ : resolution_(resolution), bounds_min_(bounds_min), bounds_max_(bounds_max)
+ {
+ }
+
+ int64_t points_num() const
+ {
+ return static_cast<int64_t>(resolution_.x) * static_cast<int64_t>(resolution_.y) *
+ static_cast<int64_t>(resolution_.z);
+ }
+
+ GVArray get_varray_for_input(const FieldInput &field_input,
+ const IndexMask UNUSED(mask),
+ ResourceScope &UNUSED(scope)) const
+ {
+ const bke::AttributeFieldInput *attribute_field_input =
+ dynamic_cast<const bke::AttributeFieldInput *>(&field_input);
+ if (attribute_field_input == nullptr) {
+ return {};
+ }
+ if (attribute_field_input->attribute_name() != "position") {
+ return {};
+ }
+
+ Array<float3> positions(this->points_num());
+
+ threading::parallel_for(IndexRange(resolution_.x), 1, [&](const IndexRange x_range) {
+ /* Start indexing at current X slice. */
+ int64_t index = x_range.start() * resolution_.y * resolution_.z;
+ for (const int64_t x_i : x_range) {
+ const float x = map(x_i, 0.0f, resolution_.x - 1, bounds_min_.x, bounds_max_.x);
+ for (const int64_t y_i : IndexRange(resolution_.y)) {
+ const float y = map(y_i, 0.0f, resolution_.y - 1, bounds_min_.y, bounds_max_.y);
+ for (const int64_t z_i : IndexRange(resolution_.z)) {
+ const float z = map(z_i, 0.0f, resolution_.z - 1, bounds_min_.z, bounds_max_.z);
+ positions[index] = float3(x, y, z);
+ index++;
+ }
+ }
+ }
+ });
+ return VArray<float3>::ForContainer(std::move(positions));
+ }
+};
+
+#ifdef WITH_OPENVDB
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ const float3 bounds_min = params.extract_input<float3>("Min");
+ const float3 bounds_max = params.extract_input<float3>("Max");
+
+ const int3 resolution = int3(params.extract_input<int>("Resolution X"),
+ params.extract_input<int>("Resolution Y"),
+ params.extract_input<int>("Resolution Z"));
+
+ if (resolution.x < 2 || resolution.y < 2 || resolution.z < 2) {
+ params.error_message_add(NodeWarningType::Error, TIP_("Resolution must be greater than 1"));
+ params.set_default_remaining_outputs();
+ return;
+ }
+
+ if (bounds_min.x == bounds_max.x || bounds_min.y == bounds_max.y ||
+ bounds_min.z == bounds_max.z) {
+ params.error_message_add(NodeWarningType::Error,
+ TIP_("Bounding box volume must be greater than 0"));
+ params.set_default_remaining_outputs();
+ return;
+ }
+
+ const double3 scale_fac = double3(bounds_max - bounds_min) / double3(resolution - 1);
+ if (!BKE_volume_grid_determinant_valid(scale_fac.x * scale_fac.y * scale_fac.z)) {
+ params.error_message_add(NodeWarningType::Warning,
+ TIP_("Volume scale is lower than permitted by OpenVDB"));
+ params.set_default_remaining_outputs();
+ return;
+ }
+
+ Field<float> input_field = params.extract_input<Field<float>>("Density");
+
+ /* Evaluate input field on a 3D grid. */
+ Grid3DFieldContext context(resolution, bounds_min, bounds_max);
+ FieldEvaluator evaluator(context, context.points_num());
+ Array<float> densities(context.points_num());
+ evaluator.add_with_destination(std::move(input_field), densities.as_mutable_span());
+ evaluator.evaluate();
+
+ /* Store resulting values in openvdb grid. */
+ const float background = params.extract_input<float>("Background");
+ openvdb::FloatGrid::Ptr grid = openvdb::FloatGrid::create(background);
+ grid->setGridClass(openvdb::GRID_FOG_VOLUME);
+
+ openvdb::tools::Dense<float, openvdb::tools::LayoutZYX> dense_grid{
+ openvdb::math::CoordBBox({0, 0, 0}, {resolution.x - 1, resolution.y - 1, resolution.z - 1}),
+ densities.data()};
+ openvdb::tools::copyFromDense(dense_grid, *grid, 0.0f);
+
+ grid->transform().preTranslate(openvdb::math::Vec3<float>(-0.5f));
+ grid->transform().postScale(openvdb::math::Vec3<double>(scale_fac.x, scale_fac.y, scale_fac.z));
+ grid->transform().postTranslate(
+ openvdb::math::Vec3<float>(bounds_min.x, bounds_min.y, bounds_min.z));
+
+ Volume *volume = (Volume *)BKE_id_new_nomain(ID_VO, nullptr);
+ BKE_volume_init_grids(volume);
+
+ BKE_volume_grid_add_vdb(*volume, "density", std::move(grid));
+
+ GeometrySet r_geometry_set;
+ r_geometry_set.replace_volume(volume);
+ params.set_output("Volume", r_geometry_set);
+}
+
+#else
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ params.error_message_add(NodeWarningType::Error,
+ TIP_("Disabled, Blender was compiled without OpenVDB"));
+ params.set_default_remaining_outputs();
+}
+#endif /* WITH_OPENVDB */
+
+} // namespace blender::nodes::node_geo_volume_cube_cc
+
+void register_node_type_geo_volume_cube()
+{
+ namespace file_ns = blender::nodes::node_geo_volume_cube_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_VOLUME_CUBE, "Volume 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_volume_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc
index e5827c24320..46708f53087 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
@@ -41,7 +41,9 @@ static void node_declare(NodeDeclarationBuilder &b)
.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_("Threshold"))
+ .default_value(0.1f)
+ .description(N_("Values larger than the threshold are inside the generated mesh"));
b.add_input<decl::Float>(N_("Adaptivity")).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
b.add_output<decl::Geometry>(N_("Mesh"));
}
@@ -121,9 +123,9 @@ static Mesh *create_mesh_from_volume_grids(Span<openvdb::GridBase::ConstPtr> gri
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};
+ MutableSpan<MVert> verts = mesh->verts_for_write();
+ MutableSpan<MPoly> polys = mesh->polys_for_write();
+ MutableSpan<MLoop> loops = mesh->loops_for_write();
for (const int i : grids.index_range()) {
const bke::OpenVDBMeshData &data = mesh_data[i];
@@ -151,6 +153,16 @@ static Mesh *create_mesh_from_volume(GeometrySet &geometry_set, GeoNodeExecParam
}
const bke::VolumeToMeshResolution resolution = get_resolution_param(params);
+
+ if (resolution.mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_SIZE &&
+ resolution.settings.voxel_size <= 0.0f) {
+ return nullptr;
+ }
+ if (resolution.mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_AMOUNT &&
+ resolution.settings.voxel_amount <= 0) {
+ return nullptr;
+ }
+
const Main *bmain = DEG_get_bmain(params.depsgraph());
BKE_volume_load(volume, bmain);
@@ -181,7 +193,7 @@ static void node_geo_exec(GeoNodeExecParams 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});
+ geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_MESH});
});
#else
params.error_message_add(NodeWarningType::Error,
diff --git a/source/blender/nodes/intern/derived_node_tree.cc b/source/blender/nodes/intern/derived_node_tree.cc
index e589da09b16..e8e0f0fa61c 100644
--- a/source/blender/nodes/intern/derived_node_tree.cc
+++ b/source/blender/nodes/intern/derived_node_tree.cc
@@ -2,38 +2,38 @@
#include "NOD_derived_node_tree.hh"
+#include "BKE_node.h"
+
#include "BLI_dot_export.hh"
namespace blender::nodes {
-DerivedNodeTree::DerivedNodeTree(bNodeTree &btree, NodeTreeRefMap &node_tree_refs)
+DerivedNodeTree::DerivedNodeTree(const bNodeTree &btree)
{
/* Construct all possible contexts immediately. This is significantly cheaper than inlining all
* node groups. If it still becomes a performance issue in the future, contexts could be
* constructed lazily when they are needed. */
- root_context_ = &this->construct_context_recursively(nullptr, nullptr, btree, node_tree_refs);
+ root_context_ = &this->construct_context_recursively(nullptr, nullptr, btree);
}
DTreeContext &DerivedNodeTree::construct_context_recursively(DTreeContext *parent_context,
- const NodeRef *parent_node,
- bNodeTree &btree,
- NodeTreeRefMap &node_tree_refs)
+ const bNode *parent_node,
+ const bNodeTree &btree)
{
+ btree.ensure_topology_cache();
DTreeContext &context = *allocator_.construct<DTreeContext>().release();
context.parent_context_ = parent_context;
context.parent_node_ = parent_node;
context.derived_tree_ = this;
- context.tree_ = &get_tree_ref_from_map(node_tree_refs, btree);
- used_node_tree_refs_.add(context.tree_);
+ context.btree_ = &btree;
+ used_btrees_.add(context.btree_);
- for (const NodeRef *node : context.tree_->nodes()) {
- if (node->is_group_node()) {
- bNode *bnode = node->bnode();
+ for (const bNode *bnode : context.btree_->all_nodes()) {
+ if (bnode->is_group()) {
bNodeTree *child_btree = reinterpret_cast<bNodeTree *>(bnode->id);
if (child_btree != nullptr) {
- DTreeContext &child = this->construct_context_recursively(
- &context, node, *child_btree, node_tree_refs);
- context.children_.add_new(node, &child);
+ DTreeContext &child = this->construct_context_recursively(&context, bnode, *child_btree);
+ context.children_.add_new(bnode, &child);
}
}
}
@@ -57,8 +57,8 @@ void DerivedNodeTree::destruct_context_recursively(DTreeContext *context)
bool DerivedNodeTree::has_link_cycles() const
{
- for (const NodeTreeRef *tree_ref : used_node_tree_refs_) {
- if (tree_ref->has_link_cycles()) {
+ for (const bNodeTree *btree : used_btrees_) {
+ if (btree->has_link_cycle()) {
return true;
}
}
@@ -67,8 +67,8 @@ bool DerivedNodeTree::has_link_cycles() const
bool DerivedNodeTree::has_undefined_nodes_or_sockets() const
{
- for (const NodeTreeRef *tree_ref : used_node_tree_refs_) {
- if (tree_ref->has_undefined_nodes_or_sockets()) {
+ for (const bNodeTree *btree : used_btrees_) {
+ if (btree->has_undefined_nodes_or_sockets()) {
return true;
}
}
@@ -83,8 +83,8 @@ void DerivedNodeTree::foreach_node(FunctionRef<void(DNode)> callback) const
void DerivedNodeTree::foreach_node_in_context_recursive(const DTreeContext &context,
FunctionRef<void(DNode)> callback) const
{
- for (const NodeRef *node_ref : context.tree_->nodes()) {
- callback(DNode(&context, node_ref));
+ for (const bNode *bnode : context.btree_->all_nodes()) {
+ callback(DNode(&context, bnode));
}
for (const DTreeContext *child_context : context.children_.values()) {
this->foreach_node_in_context_recursive(*child_context, callback);
@@ -94,32 +94,32 @@ void DerivedNodeTree::foreach_node_in_context_recursive(const DTreeContext &cont
DOutputSocket DInputSocket::get_corresponding_group_node_output() const
{
BLI_assert(*this);
- BLI_assert(socket_ref_->node().is_group_output_node());
- BLI_assert(socket_ref_->index() < socket_ref_->node().inputs().size() - 1);
+ BLI_assert(bsocket_->owner_node().is_group_output());
+ BLI_assert(bsocket_->index() < bsocket_->owner_node().input_sockets().size() - 1);
const DTreeContext *parent_context = context_->parent_context();
- const NodeRef *parent_node = context_->parent_node();
+ const bNode *parent_node = context_->parent_node();
BLI_assert(parent_context != nullptr);
BLI_assert(parent_node != nullptr);
- const int socket_index = socket_ref_->index();
- return {parent_context, &parent_node->output(socket_index)};
+ const int socket_index = bsocket_->index();
+ return {parent_context, &parent_node->output_socket(socket_index)};
}
Vector<DOutputSocket> DInputSocket::get_corresponding_group_input_sockets() const
{
BLI_assert(*this);
- BLI_assert(socket_ref_->node().is_group_node());
+ BLI_assert(bsocket_->owner_node().is_group());
- const DTreeContext *child_context = context_->child_context(socket_ref_->node());
+ const DTreeContext *child_context = context_->child_context(bsocket_->owner_node());
BLI_assert(child_context != nullptr);
- const NodeTreeRef &child_tree = child_context->tree();
- Span<const NodeRef *> group_input_nodes = child_tree.nodes_by_type("NodeGroupInput");
- const int socket_index = socket_ref_->index();
+ const bNodeTree &child_tree = child_context->btree();
+ Span<const bNode *> group_input_nodes = child_tree.nodes_by_type("NodeGroupInput");
+ const int socket_index = bsocket_->index();
Vector<DOutputSocket> sockets;
- for (const NodeRef *group_input_node : group_input_nodes) {
- sockets.append(DOutputSocket(child_context, &group_input_node->output(socket_index)));
+ for (const bNode *group_input_node : group_input_nodes) {
+ sockets.append(DOutputSocket(child_context, &group_input_node->output_socket(socket_index)));
}
return sockets;
}
@@ -127,36 +127,36 @@ Vector<DOutputSocket> DInputSocket::get_corresponding_group_input_sockets() cons
DInputSocket DOutputSocket::get_corresponding_group_node_input() const
{
BLI_assert(*this);
- BLI_assert(socket_ref_->node().is_group_input_node());
- BLI_assert(socket_ref_->index() < socket_ref_->node().outputs().size() - 1);
+ BLI_assert(bsocket_->owner_node().is_group_input());
+ BLI_assert(bsocket_->index() < bsocket_->owner_node().output_sockets().size() - 1);
const DTreeContext *parent_context = context_->parent_context();
- const NodeRef *parent_node = context_->parent_node();
+ const bNode *parent_node = context_->parent_node();
BLI_assert(parent_context != nullptr);
BLI_assert(parent_node != nullptr);
- const int socket_index = socket_ref_->index();
- return {parent_context, &parent_node->input(socket_index)};
+ const int socket_index = bsocket_->index();
+ return {parent_context, &parent_node->input_socket(socket_index)};
}
DInputSocket DOutputSocket::get_active_corresponding_group_output_socket() const
{
BLI_assert(*this);
- BLI_assert(socket_ref_->node().is_group_node());
+ BLI_assert(bsocket_->owner_node().is_group());
- const DTreeContext *child_context = context_->child_context(socket_ref_->node());
+ const DTreeContext *child_context = context_->child_context(bsocket_->owner_node());
if (child_context == nullptr) {
/* Can happen when the group node references a non-existent group (e.g. when the group is
* linked but the original file is not found). */
return {};
}
- const NodeTreeRef &child_tree = child_context->tree();
- Span<const NodeRef *> group_output_nodes = child_tree.nodes_by_type("NodeGroupOutput");
- const int socket_index = socket_ref_->index();
- for (const NodeRef *group_output_node : group_output_nodes) {
- if (group_output_node->bnode()->flag & NODE_DO_OUTPUT || group_output_nodes.size() == 1) {
- return {child_context, &group_output_node->input(socket_index)};
+ const bNodeTree &child_tree = child_context->btree();
+ Span<const bNode *> group_output_nodes = child_tree.nodes_by_type("NodeGroupOutput");
+ const int socket_index = bsocket_->index();
+ for (const bNode *group_output_node : group_output_nodes) {
+ if (group_output_node->flag & NODE_DO_OUTPUT || group_output_nodes.size() == 1) {
+ return {child_context, &group_output_node->input_socket(socket_index)};
}
}
return {};
@@ -165,11 +165,11 @@ DInputSocket DOutputSocket::get_active_corresponding_group_output_socket() const
void DInputSocket::foreach_origin_socket(FunctionRef<void(DSocket)> origin_fn) const
{
BLI_assert(*this);
- for (const OutputSocketRef *linked_socket : socket_ref_->as_input().logically_linked_sockets()) {
- const NodeRef &linked_node = linked_socket->node();
+ for (const bNodeSocket *linked_socket : bsocket_->logically_linked_sockets()) {
+ const bNode &linked_node = linked_socket->owner_node();
DOutputSocket linked_dsocket{context_, linked_socket};
- if (linked_node.is_group_input_node()) {
+ if (linked_node.is_group_input()) {
if (context_->is_root()) {
/* This is a group input in the root node group. */
origin_fn(linked_dsocket);
@@ -187,7 +187,7 @@ void DInputSocket::foreach_origin_socket(FunctionRef<void(DSocket)> origin_fn) c
}
}
}
- else if (linked_node.is_group_node()) {
+ else if (linked_node.is_group()) {
DInputSocket socket_in_group = linked_dsocket.get_active_corresponding_group_output_socket();
if (socket_in_group) {
if (socket_in_group->is_logically_linked()) {
@@ -217,16 +217,16 @@ void DOutputSocket::foreach_target_socket(ForeachTargetSocketFn target_fn) const
void DOutputSocket::foreach_target_socket(ForeachTargetSocketFn target_fn,
TargetSocketPathInfo &path_info) const
{
- for (const LinkRef *link : socket_ref_->as_output().directly_linked_links()) {
+ for (const bNodeLink *link : bsocket_->directly_linked_links()) {
if (link->is_muted()) {
continue;
}
- const DInputSocket &linked_socket{context_, &link->to()};
+ const DInputSocket &linked_socket{context_, link->tosock};
if (!linked_socket->is_available()) {
continue;
}
const DNode linked_node = linked_socket.node();
- if (linked_node->is_reroute_node()) {
+ if (linked_node->is_reroute()) {
const DInputSocket reroute_input = linked_socket;
const DOutputSocket reroute_output = linked_node.output(0);
path_info.sockets.append(reroute_input);
@@ -236,18 +236,18 @@ void DOutputSocket::foreach_target_socket(ForeachTargetSocketFn target_fn,
path_info.sockets.pop_last();
}
else if (linked_node->is_muted()) {
- for (const InternalLinkRef *internal_link : linked_node->internal_links()) {
- if (&internal_link->from() != linked_socket.socket_ref()) {
+ for (const bNodeLink *internal_link : linked_node->internal_links_span()) {
+ if (internal_link->fromsock != linked_socket.bsocket()) {
continue;
}
/* The internal link only forwards the first incoming link. */
- if (linked_socket->is_multi_input_socket()) {
+ if (linked_socket->is_multi_input()) {
if (linked_socket->directly_linked_links()[0] != link) {
continue;
}
}
const DInputSocket mute_input = linked_socket;
- const DOutputSocket mute_output{context_, &internal_link->to()};
+ const DOutputSocket mute_output{context_, internal_link->tosock};
path_info.sockets.append(mute_input);
path_info.sockets.append(mute_output);
mute_output.foreach_target_socket(target_fn, path_info);
@@ -255,8 +255,8 @@ void DOutputSocket::foreach_target_socket(ForeachTargetSocketFn target_fn,
path_info.sockets.pop_last();
}
}
- else if (linked_node->is_group_output_node()) {
- if (linked_node.node_ref() != context_->tree().group_output_node()) {
+ else if (linked_node->is_group_output()) {
+ if (linked_node.bnode() != context_->btree().group_output_node()) {
continue;
}
if (context_->is_root()) {
@@ -276,7 +276,7 @@ void DOutputSocket::foreach_target_socket(ForeachTargetSocketFn target_fn,
path_info.sockets.pop_last();
}
}
- else if (linked_node->is_group_node()) {
+ else if (linked_node->is_group()) {
/* Follow the links within the nested node group. */
path_info.sockets.append(linked_socket);
const Vector<DOutputSocket> sockets_in_group =
@@ -310,7 +310,8 @@ static dot::Cluster *get_dot_cluster_for_context(
}
dot::Cluster *parent_cluster = get_dot_cluster_for_context(
digraph, parent_context, dot_clusters);
- std::string cluster_name = context->tree().name() + " / " + context->parent_node()->name();
+ std::string cluster_name = StringRef(context->btree().id.name + 2) + " / " +
+ context->parent_node()->name;
dot::Cluster &cluster = digraph.new_cluster(cluster_name);
cluster.set_parent_cluster(parent_cluster);
return &cluster;
@@ -328,11 +329,11 @@ std::string DerivedNodeTree::to_dot() const
this->foreach_node([&](DNode node) {
/* Ignore nodes that should not show up in the final output. */
- if (node->is_muted() || node->is_group_node() || node->is_reroute_node() || node->is_frame()) {
+ if (node->is_muted() || node->is_group() || node->is_reroute() || node->is_frame()) {
return;
}
if (!node.context()->is_root()) {
- if (node->is_group_input_node() || node->is_group_output_node()) {
+ if (node->is_group_input() || node->is_group_output()) {
return;
}
}
@@ -345,22 +346,22 @@ std::string DerivedNodeTree::to_dot() const
Vector<std::string> input_names;
Vector<std::string> output_names;
- for (const InputSocketRef *socket : node->inputs()) {
+ for (const bNodeSocket *socket : node->input_sockets()) {
if (socket->is_available()) {
- input_names.append(socket->name());
+ input_names.append(socket->name);
}
}
- for (const OutputSocketRef *socket : node->outputs()) {
+ for (const bNodeSocket *socket : node->output_sockets()) {
if (socket->is_available()) {
- output_names.append(socket->name());
+ output_names.append(socket->name);
}
}
dot::NodeWithSocketsRef dot_node_with_sockets = dot::NodeWithSocketsRef(
- dot_node, node->name(), input_names, output_names);
+ dot_node, node->name, input_names, output_names);
int input_index = 0;
- for (const InputSocketRef *socket : node->inputs()) {
+ for (const bNodeSocket *socket : node->input_sockets()) {
if (socket->is_available()) {
dot_input_sockets.add_new(DInputSocket{node.context(), socket},
dot_node_with_sockets.input(input_index));
@@ -368,7 +369,7 @@ std::string DerivedNodeTree::to_dot() const
}
}
int output_index = 0;
- for (const OutputSocketRef *socket : node->outputs()) {
+ for (const bNodeSocket *socket : node->output_sockets()) {
if (socket->is_available()) {
dot_output_sockets.add_new(DOutputSocket{node.context(), socket},
dot_node_with_sockets.output(output_index));
@@ -392,7 +393,7 @@ std::string DerivedNodeTree::to_dot() const
}
}
dot::Node &dot_node = *dot_floating_inputs.lookup_or_add_cb(from_socket, [&]() {
- dot::Node &dot_node = digraph.new_node(from_socket->name());
+ dot::Node &dot_node = digraph.new_node(from_socket->name);
dot_node.set_background_color("white");
dot_node.set_shape(dot::Attr_shape::Ellipse);
dot_node.set_parent_cluster(
diff --git a/source/blender/nodes/intern/geometry_nodes_eval_log.cc b/source/blender/nodes/intern/geometry_nodes_eval_log.cc
index 85dfdf03b82..89bfa5834e8 100644
--- a/source/blender/nodes/intern/geometry_nodes_eval_log.cc
+++ b/source/blender/nodes/intern/geometry_nodes_eval_log.cc
@@ -2,6 +2,7 @@
#include "NOD_geometry_nodes_eval_log.hh"
+#include "BKE_curves.hh"
#include "BKE_geometry_set_instances.hh"
#include "DNA_modifier_types.h"
@@ -88,17 +89,17 @@ TreeLog &ModifierLog::lookup_or_add_tree_log(LogByTreeContext &log_by_tree_conte
destruct_ptr<TreeLog> owned_tree_log = allocator_.construct<TreeLog>();
tree_log = owned_tree_log.get();
log_by_tree_context.add_new(&tree_context, tree_log);
- parent_log.child_logs_.add_new(tree_context.parent_node()->name(), std::move(owned_tree_log));
+ parent_log.child_logs_.add_new(tree_context.parent_node()->name, std::move(owned_tree_log));
return *tree_log;
}
NodeLog &ModifierLog::lookup_or_add_node_log(LogByTreeContext &log_by_tree_context, DNode node)
{
TreeLog &tree_log = this->lookup_or_add_tree_log(log_by_tree_context, *node.context());
- NodeLog &node_log = *tree_log.node_logs_.lookup_or_add_cb(node->name(), [&]() {
+ NodeLog &node_log = *tree_log.node_logs_.lookup_or_add_cb(node->name, [&]() {
destruct_ptr<NodeLog> node_log = allocator_.construct<NodeLog>();
- node_log->input_logs_.resize(node->inputs().size());
- node_log->output_logs_.resize(node->outputs().size());
+ node_log->input_logs_.resize(node->input_sockets().size());
+ node_log->output_logs_.resize(node->output_sockets().size());
return node_log;
});
return node_log;
@@ -228,7 +229,7 @@ GeometryValueLog::GeometryValueLog(const GeometrySet &geometry_set, bool log_ful
all_component_types,
true,
[&](const bke::AttributeIDRef &attribute_id,
- const AttributeMetaData &meta_data,
+ const bke::AttributeMetaData &meta_data,
const GeometryComponent &UNUSED(component)) {
if (attribute_id.is_named() && names.add(attribute_id.name())) {
this->attributes_.append({attribute_id.name(), meta_data.domain, meta_data.data_type});
@@ -241,21 +242,21 @@ GeometryValueLog::GeometryValueLog(const GeometrySet &geometry_set, bool log_ful
case GEO_COMPONENT_TYPE_MESH: {
const MeshComponent &mesh_component = *(const MeshComponent *)component;
MeshInfo &info = this->mesh_info.emplace();
- info.verts_num = mesh_component.attribute_domain_num(ATTR_DOMAIN_POINT);
- info.edges_num = mesh_component.attribute_domain_num(ATTR_DOMAIN_EDGE);
- info.faces_num = mesh_component.attribute_domain_num(ATTR_DOMAIN_FACE);
+ info.verts_num = mesh_component.attribute_domain_size(ATTR_DOMAIN_POINT);
+ info.edges_num = mesh_component.attribute_domain_size(ATTR_DOMAIN_EDGE);
+ info.faces_num = mesh_component.attribute_domain_size(ATTR_DOMAIN_FACE);
break;
}
case GEO_COMPONENT_TYPE_CURVE: {
const CurveComponent &curve_component = *(const CurveComponent *)component;
CurveInfo &info = this->curve_info.emplace();
- info.splines_num = curve_component.attribute_domain_num(ATTR_DOMAIN_CURVE);
+ info.splines_num = curve_component.attribute_domain_size(ATTR_DOMAIN_CURVE);
break;
}
case GEO_COMPONENT_TYPE_POINT_CLOUD: {
const PointCloudComponent &pointcloud_component = *(const PointCloudComponent *)component;
PointCloudInfo &info = this->pointcloud_info.emplace();
- info.points_num = pointcloud_component.attribute_domain_num(ATTR_DOMAIN_POINT);
+ info.points_num = pointcloud_component.attribute_domain_size(ATTR_DOMAIN_POINT);
break;
}
case GEO_COMPONENT_TYPE_INSTANCES: {
@@ -264,6 +265,17 @@ GeometryValueLog::GeometryValueLog(const GeometrySet &geometry_set, bool log_ful
info.instances_num = instances_component.instances_num();
break;
}
+ case GEO_COMPONENT_TYPE_EDIT: {
+ const GeometryComponentEditData &edit_component = *(
+ const GeometryComponentEditData *)component;
+ if (const bke::CurvesEditHints *curve_edit_hints =
+ edit_component.curves_edit_hints_.get()) {
+ EditDataInfo &info = this->edit_data_info.emplace();
+ info.has_deform_matrices = curve_edit_hints->deform_mats.has_value();
+ info.has_deformed_positions = curve_edit_hints->positions.has_value();
+ }
+ break;
+ }
case GEO_COMPONENT_TYPE_VOLUME: {
break;
}
diff --git a/source/blender/nodes/intern/node_common.cc b/source/blender/nodes/intern/node_common.cc
index b7c5f9570e4..6402ec3f3d6 100644
--- a/source/blender/nodes/intern/node_common.cc
+++ b/source/blender/nodes/intern/node_common.cc
@@ -37,6 +37,7 @@ using blender::MultiValueMap;
using blender::Set;
using blender::Stack;
using blender::StringRef;
+using blender::Vector;
/* -------------------------------------------------------------------- */
/** \name Node Group
@@ -160,6 +161,7 @@ static void group_verify_socket_list(bNodeTree &node_tree,
const bool ensure_extend_socket_exists)
{
ListBase old_sockets = verify_lb;
+ Vector<bNodeSocket *> ordered_old_sockets = old_sockets;
BLI_listbase_clear(&verify_lb);
LISTBASE_FOREACH (const bNodeSocket *, interface_socket, &interface_sockets) {
@@ -193,6 +195,19 @@ static void group_verify_socket_list(bNodeTree &node_tree,
LISTBASE_FOREACH_MUTABLE (bNodeSocket *, unused_socket, &old_sockets) {
nodeRemoveSocket(&node_tree, &node, unused_socket);
}
+
+ {
+ /* Check if new sockets match the old sockets. */
+ int index;
+ LISTBASE_FOREACH_INDEX (bNodeSocket *, new_socket, &verify_lb, index) {
+ if (index < ordered_old_sockets.size()) {
+ if (ordered_old_sockets[index] != new_socket) {
+ BKE_ntree_update_tag_interface(&node_tree);
+ break;
+ }
+ }
+ }
+ }
}
void node_group_update(struct bNodeTree *ntree, struct bNode *node)
diff --git a/source/blender/nodes/intern/node_geometry_exec.cc b/source/blender/nodes/intern/node_geometry_exec.cc
index 56e9c9f0496..953dce035c2 100644
--- a/source/blender/nodes/intern/node_geometry_exec.cc
+++ b/source/blender/nodes/intern/node_geometry_exec.cc
@@ -4,6 +4,7 @@
#include "DEG_depsgraph_query.h"
+#include "BKE_curves.hh"
#include "BKE_type_conversions.hh"
#include "NOD_geometry_exec.hh"
@@ -37,7 +38,7 @@ void GeoNodeExecParams::check_input_geometry_set(StringRef identifier,
const GeometrySet &geometry_set) const
{
const SocketDeclaration &decl =
- *provider_->dnode->input_by_identifier(identifier).bsocket()->runtime->declaration;
+ *provider_->dnode->input_by_identifier(identifier).runtime->declaration;
const decl::Geometry *geo_decl = dynamic_cast<const decl::Geometry *>(&decl);
if (geo_decl == nullptr) {
return;
@@ -94,16 +95,32 @@ void GeoNodeExecParams::check_input_geometry_set(StringRef identifier,
message += TIP_("Curve");
break;
}
+ case GEO_COMPONENT_TYPE_EDIT: {
+ continue;
+ }
}
this->error_message_add(NodeWarningType::Info, std::move(message));
}
}
+void GeoNodeExecParams::check_output_geometry_set(const GeometrySet &geometry_set) const
+{
+ UNUSED_VARS_NDEBUG(geometry_set);
+#ifdef DEBUG
+ if (const bke::CurvesEditHints *curve_edit_hints =
+ geometry_set.get_curve_edit_hints_for_read()) {
+ /* If this is not valid, it's likely that the number of stored deformed points does not match
+ * the number of points in the original data. */
+ BLI_assert(curve_edit_hints->is_valid());
+ }
+#endif
+}
+
const bNodeSocket *GeoNodeExecParams::find_available_socket(const StringRef name) const
{
- for (const InputSocketRef *socket : provider_->dnode->inputs()) {
- if (socket->is_available() && socket->name() == name) {
- return socket->bsocket();
+ for (const bNodeSocket *socket : provider_->dnode->runtime->inputs) {
+ if (socket->is_available() && socket->name == name) {
+ return socket;
}
}
@@ -123,10 +140,10 @@ void GeoNodeExecParams::set_default_remaining_outputs()
void GeoNodeExecParams::check_input_access(StringRef identifier,
const CPPType *requested_type) const
{
- bNodeSocket *found_socket = nullptr;
- for (const InputSocketRef *socket : provider_->dnode->inputs()) {
- if (socket->identifier() == identifier) {
- found_socket = socket->bsocket();
+ const bNodeSocket *found_socket = nullptr;
+ for (const bNodeSocket *socket : provider_->dnode->input_sockets()) {
+ if (socket->identifier == identifier) {
+ found_socket = socket;
break;
}
}
@@ -134,9 +151,9 @@ void GeoNodeExecParams::check_input_access(StringRef identifier,
if (found_socket == nullptr) {
std::cout << "Did not find an input socket with the identifier '" << identifier << "'.\n";
std::cout << "Possible identifiers are: ";
- for (const InputSocketRef *socket : provider_->dnode->inputs()) {
+ for (const bNodeSocket *socket : provider_->dnode->input_sockets()) {
if (socket->is_available()) {
- std::cout << "'" << socket->identifier() << "', ";
+ std::cout << "'" << socket->identifier << "', ";
}
}
std::cout << "\n";
@@ -165,10 +182,10 @@ void GeoNodeExecParams::check_input_access(StringRef identifier,
void GeoNodeExecParams::check_output_access(StringRef identifier, const CPPType &value_type) const
{
- bNodeSocket *found_socket = nullptr;
- for (const OutputSocketRef *socket : provider_->dnode->outputs()) {
- if (socket->identifier() == identifier) {
- found_socket = socket->bsocket();
+ const bNodeSocket *found_socket = nullptr;
+ for (const bNodeSocket *socket : provider_->dnode->output_sockets()) {
+ if (socket->identifier == identifier) {
+ found_socket = socket;
break;
}
}
@@ -176,9 +193,9 @@ void GeoNodeExecParams::check_output_access(StringRef identifier, const CPPType
if (found_socket == nullptr) {
std::cout << "Did not find an output socket with the identifier '" << identifier << "'.\n";
std::cout << "Possible identifiers are: ";
- for (const OutputSocketRef *socket : provider_->dnode->outputs()) {
- if (socket->is_available()) {
- std::cout << "'" << socket->identifier() << "', ";
+ for (const bNodeSocket *socket : provider_->dnode->output_sockets()) {
+ if (!(socket->flag & SOCK_UNAVAIL)) {
+ std::cout << "'" << socket->identifier << "', ";
}
}
std::cout << "\n";
diff --git a/source/blender/nodes/intern/node_multi_function.cc b/source/blender/nodes/intern/node_multi_function.cc
index 13bfdfbfac1..1f8397923e9 100644
--- a/source/blender/nodes/intern/node_multi_function.cc
+++ b/source/blender/nodes/intern/node_multi_function.cc
@@ -2,14 +2,14 @@
#include "NOD_multi_function.hh"
+#include "BKE_node.h"
+
namespace blender::nodes {
NodeMultiFunctions::NodeMultiFunctions(const DerivedNodeTree &tree)
{
- for (const NodeTreeRef *tree_ref : tree.used_node_tree_refs()) {
- bNodeTree *btree = tree_ref->btree();
- for (const NodeRef *node : tree_ref->nodes()) {
- bNode *bnode = node->bnode();
+ for (const bNodeTree *btree : tree.used_btrees()) {
+ for (const bNode *bnode : btree->all_nodes()) {
if (bnode->typeinfo->build_multi_function == nullptr) {
continue;
}
diff --git a/source/blender/nodes/intern/node_tree_ref.cc b/source/blender/nodes/intern/node_tree_ref.cc
deleted file mode 100644
index 64a8690a869..00000000000
--- a/source/blender/nodes/intern/node_tree_ref.cc
+++ /dev/null
@@ -1,678 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-
-#include <mutex>
-
-#include "NOD_node_tree_ref.hh"
-
-#include "BLI_dot_export.hh"
-#include "BLI_stack.hh"
-
-#include "RNA_prototypes.h"
-
-namespace blender::nodes {
-
-NodeTreeRef::NodeTreeRef(bNodeTree *btree) : btree_(btree)
-{
- Map<bNode *, NodeRef *> node_mapping;
-
- LISTBASE_FOREACH (bNode *, bnode, &btree->nodes) {
- NodeRef &node = *allocator_.construct<NodeRef>().release();
-
- node.tree_ = this;
- node.bnode_ = bnode;
- node.id_ = nodes_by_id_.append_and_get_index(&node);
-
- LISTBASE_FOREACH (bNodeSocket *, bsocket, &bnode->inputs) {
- InputSocketRef &socket = *allocator_.construct<InputSocketRef>().release();
- socket.node_ = &node;
- socket.index_ = node.inputs_.append_and_get_index(&socket);
- socket.is_input_ = true;
- socket.bsocket_ = bsocket;
- socket.id_ = sockets_by_id_.append_and_get_index(&socket);
- }
-
- LISTBASE_FOREACH (bNodeSocket *, bsocket, &bnode->outputs) {
- OutputSocketRef &socket = *allocator_.construct<OutputSocketRef>().release();
- socket.node_ = &node;
- socket.index_ = node.outputs_.append_and_get_index(&socket);
- socket.is_input_ = false;
- socket.bsocket_ = bsocket;
- socket.id_ = sockets_by_id_.append_and_get_index(&socket);
- }
-
- LISTBASE_FOREACH (bNodeLink *, blink, &bnode->internal_links) {
- InternalLinkRef &internal_link = *allocator_.construct<InternalLinkRef>().release();
- internal_link.blink_ = blink;
- for (InputSocketRef *socket_ref : node.inputs_) {
- if (socket_ref->bsocket_ == blink->fromsock) {
- internal_link.from_ = socket_ref;
- break;
- }
- }
- for (OutputSocketRef *socket_ref : node.outputs_) {
- if (socket_ref->bsocket_ == blink->tosock) {
- internal_link.to_ = socket_ref;
- break;
- }
- }
- BLI_assert(internal_link.from_ != nullptr);
- BLI_assert(internal_link.to_ != nullptr);
- node.internal_links_.append(&internal_link);
- }
-
- input_sockets_.extend(node.inputs_.as_span());
- output_sockets_.extend(node.outputs_.as_span());
-
- node_mapping.add_new(bnode, &node);
- }
-
- LISTBASE_FOREACH (bNodeLink *, blink, &btree->links) {
- OutputSocketRef &from_socket = this->find_output_socket(
- node_mapping, blink->fromnode, blink->fromsock);
- InputSocketRef &to_socket = this->find_input_socket(
- node_mapping, blink->tonode, blink->tosock);
-
- LinkRef &link = *allocator_.construct<LinkRef>().release();
- link.from_ = &from_socket;
- link.to_ = &to_socket;
- link.blink_ = blink;
-
- links_.append(&link);
-
- from_socket.directly_linked_links_.append(&link);
- to_socket.directly_linked_links_.append(&link);
- }
-
- for (InputSocketRef *input_socket : input_sockets_) {
- if (input_socket->is_multi_input_socket()) {
- std::sort(input_socket->directly_linked_links_.begin(),
- input_socket->directly_linked_links_.end(),
- [&](const LinkRef *a, const LinkRef *b) -> bool {
- int index_a = a->blink()->multi_input_socket_index;
- int index_b = b->blink()->multi_input_socket_index;
- return index_a > index_b;
- });
- }
- }
-
- this->create_socket_identifier_maps();
- this->create_linked_socket_caches();
-
- for (NodeRef *node : nodes_by_id_) {
- const bNodeType *nodetype = node->bnode_->typeinfo;
- nodes_by_type_.add(nodetype, node);
- }
-
- const Span<const NodeRef *> group_output_nodes = this->nodes_by_type("NodeGroupOutput");
- if (group_output_nodes.is_empty()) {
- group_output_node_ = nullptr;
- }
- else if (group_output_nodes.size() == 1) {
- group_output_node_ = group_output_nodes.first();
- }
- else {
- for (const NodeRef *group_output : group_output_nodes) {
- if (group_output->bnode_->flag & NODE_DO_OUTPUT) {
- group_output_node_ = group_output;
- break;
- }
- }
- }
-}
-
-NodeTreeRef::~NodeTreeRef()
-{
- /* The destructor has to be called manually, because these types are allocated in a linear
- * allocator. */
- for (NodeRef *node : nodes_by_id_) {
- node->~NodeRef();
- }
- for (InputSocketRef *socket : input_sockets_) {
- socket->~InputSocketRef();
- }
- for (OutputSocketRef *socket : output_sockets_) {
- socket->~OutputSocketRef();
- }
- for (LinkRef *link : links_) {
- link->~LinkRef();
- }
-}
-
-InputSocketRef &NodeTreeRef::find_input_socket(Map<bNode *, NodeRef *> &node_mapping,
- bNode *bnode,
- bNodeSocket *bsocket)
-{
- NodeRef *node = node_mapping.lookup(bnode);
- for (InputSocketRef *socket : node->inputs_) {
- if (socket->bsocket_ == bsocket) {
- return *socket;
- }
- }
- BLI_assert_unreachable();
- return *node->inputs_[0];
-}
-
-OutputSocketRef &NodeTreeRef::find_output_socket(Map<bNode *, NodeRef *> &node_mapping,
- bNode *bnode,
- bNodeSocket *bsocket)
-{
- NodeRef *node = node_mapping.lookup(bnode);
- for (OutputSocketRef *socket : node->outputs_) {
- if (socket->bsocket_ == bsocket) {
- return *socket;
- }
- }
- BLI_assert_unreachable();
- return *node->outputs_[0];
-}
-
-void NodeTreeRef::create_linked_socket_caches()
-{
- for (InputSocketRef *socket : input_sockets_) {
- /* Find directly linked socket based on incident links. */
- Vector<const SocketRef *> directly_linked_sockets;
- for (LinkRef *link : socket->directly_linked_links_) {
- directly_linked_sockets.append(link->from_);
- }
- socket->directly_linked_sockets_ = allocator_.construct_array_copy(
- directly_linked_sockets.as_span());
-
- /* Find logically linked sockets. */
- Vector<const SocketRef *> logically_linked_sockets;
- Vector<const SocketRef *> logically_linked_skipped_sockets;
- Vector<const InputSocketRef *> seen_sockets_stack;
- socket->foreach_logical_origin(
- [&](const OutputSocketRef &origin) { logically_linked_sockets.append(&origin); },
- [&](const SocketRef &socket) { logically_linked_skipped_sockets.append(&socket); },
- false,
- seen_sockets_stack);
- if (logically_linked_sockets == directly_linked_sockets) {
- socket->logically_linked_sockets_ = socket->directly_linked_sockets_;
- }
- else {
- socket->logically_linked_sockets_ = allocator_.construct_array_copy(
- logically_linked_sockets.as_span());
- }
- socket->logically_linked_skipped_sockets_ = allocator_.construct_array_copy(
- logically_linked_skipped_sockets.as_span());
- }
-
- for (OutputSocketRef *socket : output_sockets_) {
- /* Find directly linked socket based on incident links. */
- Vector<const SocketRef *> directly_linked_sockets;
- for (LinkRef *link : socket->directly_linked_links_) {
- directly_linked_sockets.append(link->to_);
- }
- socket->directly_linked_sockets_ = allocator_.construct_array_copy(
- directly_linked_sockets.as_span());
-
- /* Find logically linked sockets. */
- Vector<const SocketRef *> logically_linked_sockets;
- Vector<const SocketRef *> logically_linked_skipped_sockets;
- Vector<const OutputSocketRef *> handled_sockets;
- socket->foreach_logical_target(
- [&](const InputSocketRef &target) { logically_linked_sockets.append(&target); },
- [&](const SocketRef &socket) { logically_linked_skipped_sockets.append(&socket); },
- handled_sockets);
- if (logically_linked_sockets == directly_linked_sockets) {
- socket->logically_linked_sockets_ = socket->directly_linked_sockets_;
- }
- else {
- socket->logically_linked_sockets_ = allocator_.construct_array_copy(
- logically_linked_sockets.as_span());
- }
- socket->logically_linked_skipped_sockets_ = allocator_.construct_array_copy(
- logically_linked_skipped_sockets.as_span());
- }
-}
-
-void InputSocketRef::foreach_logical_origin(
- FunctionRef<void(const OutputSocketRef &)> origin_fn,
- FunctionRef<void(const SocketRef &)> skipped_fn,
- bool only_follow_first_input_link,
- Vector<const InputSocketRef *> &seen_sockets_stack) const
-{
- /* Protect against loops. */
- if (seen_sockets_stack.contains(this)) {
- return;
- }
- seen_sockets_stack.append(this);
-
- Span<const LinkRef *> links_to_check = this->directly_linked_links();
- if (only_follow_first_input_link) {
- links_to_check = links_to_check.take_front(1);
- }
- for (const LinkRef *link : links_to_check) {
- if (link->is_muted()) {
- continue;
- }
- const OutputSocketRef &origin = link->from();
- const NodeRef &origin_node = origin.node();
- if (!origin.is_available()) {
- /* Non available sockets are ignored. */
- }
- else if (origin_node.is_reroute_node()) {
- const InputSocketRef &reroute_input = origin_node.input(0);
- const OutputSocketRef &reroute_output = origin_node.output(0);
- skipped_fn.call_safe(reroute_input);
- skipped_fn.call_safe(reroute_output);
- reroute_input.foreach_logical_origin(origin_fn, skipped_fn, false, seen_sockets_stack);
- }
- else if (origin_node.is_muted()) {
- for (const InternalLinkRef *internal_link : origin_node.internal_links()) {
- if (&internal_link->to() == &origin) {
- const InputSocketRef &mute_input = internal_link->from();
- skipped_fn.call_safe(origin);
- skipped_fn.call_safe(mute_input);
- mute_input.foreach_logical_origin(origin_fn, skipped_fn, true, seen_sockets_stack);
- }
- }
- }
- else {
- origin_fn(origin);
- }
- }
-
- seen_sockets_stack.pop_last();
-}
-
-void OutputSocketRef::foreach_logical_target(
- FunctionRef<void(const InputSocketRef &)> target_fn,
- FunctionRef<void(const SocketRef &)> skipped_fn,
- Vector<const OutputSocketRef *> &seen_sockets_stack) const
-{
- /* Protect against loops. */
- if (seen_sockets_stack.contains(this)) {
- return;
- }
- seen_sockets_stack.append(this);
-
- for (const LinkRef *link : this->directly_linked_links()) {
- if (link->is_muted()) {
- continue;
- }
- const InputSocketRef &target = link->to();
- const NodeRef &target_node = target.node();
- if (!target.is_available()) {
- /* Non available sockets are ignored. */
- }
- else if (target_node.is_reroute_node()) {
- const OutputSocketRef &reroute_output = target_node.output(0);
- skipped_fn.call_safe(target);
- skipped_fn.call_safe(reroute_output);
- reroute_output.foreach_logical_target(target_fn, skipped_fn, seen_sockets_stack);
- }
- else if (target_node.is_muted()) {
- skipped_fn.call_safe(target);
- for (const InternalLinkRef *internal_link : target_node.internal_links()) {
- if (&internal_link->from() == &target) {
- /* The internal link only forwards the first incoming link. */
- if (target.is_multi_input_socket()) {
- if (target.directly_linked_links()[0] != link) {
- continue;
- }
- }
- const OutputSocketRef &mute_output = internal_link->to();
- skipped_fn.call_safe(target);
- skipped_fn.call_safe(mute_output);
- mute_output.foreach_logical_target(target_fn, skipped_fn, seen_sockets_stack);
- }
- }
- }
- else {
- target_fn(target);
- }
- }
-
- seen_sockets_stack.pop_last();
-}
-
-namespace {
-struct SocketByIdentifierMap {
- SocketIndexByIdentifierMap *map = nullptr;
- std::unique_ptr<SocketIndexByIdentifierMap> owned_map;
-};
-} // namespace
-
-static std::unique_ptr<SocketIndexByIdentifierMap> create_identifier_map(const ListBase &sockets)
-{
- std::unique_ptr<SocketIndexByIdentifierMap> map = std::make_unique<SocketIndexByIdentifierMap>();
- int index;
- LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, &sockets, index) {
- map->add_new(socket->identifier, index);
- }
- return map;
-}
-
-/* This function is not threadsafe. */
-static SocketByIdentifierMap get_or_create_identifier_map(
- const bNode &node, const ListBase &sockets, const bNodeSocketTemplate *sockets_template)
-{
- SocketByIdentifierMap map;
- if (sockets_template == nullptr) {
- if (BLI_listbase_is_empty(&sockets)) {
- static SocketIndexByIdentifierMap empty_map;
- map.map = &empty_map;
- }
- else if (node.type == NODE_REROUTE) {
- if (&node.inputs == &sockets) {
- static SocketIndexByIdentifierMap reroute_input_map = [] {
- SocketIndexByIdentifierMap map;
- map.add_new("Input", 0);
- return map;
- }();
- map.map = &reroute_input_map;
- }
- else {
- static SocketIndexByIdentifierMap reroute_output_map = [] {
- SocketIndexByIdentifierMap map;
- map.add_new("Output", 0);
- return map;
- }();
- map.map = &reroute_output_map;
- }
- }
- else {
- /* The node has a dynamic amount of sockets. Therefore we need to create a new map. */
- map.owned_map = create_identifier_map(sockets);
- map.map = &*map.owned_map;
- }
- }
- else {
- /* Cache only one map for nodes that have the same sockets. */
- static Map<const bNodeSocketTemplate *, std::unique_ptr<SocketIndexByIdentifierMap>> maps;
- map.map = &*maps.lookup_or_add_cb(sockets_template,
- [&]() { return create_identifier_map(sockets); });
- }
- return map;
-}
-
-void NodeTreeRef::create_socket_identifier_maps()
-{
- /* `get_or_create_identifier_map` is not threadsafe, therefore we have to hold a lock here. */
- static std::mutex mutex;
- std::lock_guard lock{mutex};
-
- for (NodeRef *node : nodes_by_id_) {
- bNode &bnode = *node->bnode_;
- SocketByIdentifierMap inputs_map = get_or_create_identifier_map(
- bnode, bnode.inputs, bnode.typeinfo->inputs);
- SocketByIdentifierMap outputs_map = get_or_create_identifier_map(
- bnode, bnode.outputs, bnode.typeinfo->outputs);
- node->input_index_by_identifier_ = inputs_map.map;
- node->output_index_by_identifier_ = outputs_map.map;
- if (inputs_map.owned_map) {
- owned_identifier_maps_.append(std::move(inputs_map.owned_map));
- }
- if (outputs_map.owned_map) {
- owned_identifier_maps_.append(std::move(outputs_map.owned_map));
- }
- }
-}
-
-static bool has_link_cycles_recursive(const NodeRef &node,
- MutableSpan<bool> visited,
- MutableSpan<bool> is_in_stack)
-{
- const int node_id = node.id();
- if (is_in_stack[node_id]) {
- return true;
- }
- if (visited[node_id]) {
- return false;
- }
-
- visited[node_id] = true;
- is_in_stack[node_id] = true;
-
- for (const OutputSocketRef *from_socket : node.outputs()) {
- if (!from_socket->is_available()) {
- continue;
- }
- for (const InputSocketRef *to_socket : from_socket->directly_linked_sockets()) {
- if (!to_socket->is_available()) {
- continue;
- }
- const NodeRef &to_node = to_socket->node();
- if (has_link_cycles_recursive(to_node, visited, is_in_stack)) {
- return true;
- }
- }
- }
-
- is_in_stack[node_id] = false;
- return false;
-}
-
-bool NodeTreeRef::has_link_cycles() const
-{
- const int node_amount = nodes_by_id_.size();
- Array<bool> visited(node_amount, false);
- Array<bool> is_in_stack(node_amount, false);
-
- for (const NodeRef *node : nodes_by_id_) {
- if (has_link_cycles_recursive(*node, visited, is_in_stack)) {
- return true;
- }
- }
- return false;
-}
-
-bool NodeTreeRef::has_undefined_nodes_or_sockets() const
-{
- for (const NodeRef *node : nodes_by_id_) {
- if (node->is_undefined()) {
- return true;
- }
- }
- for (const SocketRef *socket : sockets_by_id_) {
- if (socket->is_undefined()) {
- return true;
- }
- }
- return false;
-}
-
-bool NodeRef::any_input_is_directly_linked() const
-{
- for (const SocketRef *socket : inputs_) {
- if (!socket->directly_linked_sockets().is_empty()) {
- return true;
- }
- }
- return false;
-}
-
-bool NodeRef::any_output_is_directly_linked() const
-{
- for (const SocketRef *socket : outputs_) {
- if (!socket->directly_linked_sockets().is_empty()) {
- return true;
- }
- }
- return false;
-}
-
-bool NodeRef::any_socket_is_directly_linked(eNodeSocketInOut in_out) const
-{
- if (in_out == SOCK_IN) {
- return this->any_input_is_directly_linked();
- }
- return this->any_output_is_directly_linked();
-}
-
-struct ToposortNodeState {
- bool is_done = false;
- bool is_in_stack = false;
-};
-
-static void toposort_from_start_node(const NodeTreeRef::ToposortDirection direction,
- const NodeRef &start_node,
- MutableSpan<ToposortNodeState> node_states,
- NodeTreeRef::ToposortResult &result)
-{
- struct Item {
- const NodeRef *node;
- /* Index of the next socket that is checked in the depth-first search. */
- int socket_index = 0;
- /* Link index in the next socket that is checked in the depth-first search. */
- int link_index = 0;
- };
-
- /* Do a depth-first search to sort nodes topologically. */
- Stack<Item, 64> nodes_to_check;
- nodes_to_check.push({&start_node});
- while (!nodes_to_check.is_empty()) {
- Item &item = nodes_to_check.peek();
- const NodeRef &node = *item.node;
- const Span<const SocketRef *> sockets = node.sockets(
- direction == NodeTreeRef::ToposortDirection::LeftToRight ? SOCK_IN : SOCK_OUT);
-
- while (true) {
- if (item.socket_index == sockets.size()) {
- /* All sockets have already been visited. */
- break;
- }
- const SocketRef &socket = *sockets[item.socket_index];
- const Span<const SocketRef *> linked_sockets = socket.directly_linked_sockets();
- if (item.link_index == linked_sockets.size()) {
- /* All links connected to this socket have already been visited. */
- item.socket_index++;
- item.link_index = 0;
- continue;
- }
- const SocketRef &linked_socket = *linked_sockets[item.link_index];
- const NodeRef &linked_node = linked_socket.node();
- ToposortNodeState &linked_node_state = node_states[linked_node.id()];
- if (linked_node_state.is_done) {
- /* The linked node has already been visited. */
- item.link_index++;
- continue;
- }
- if (linked_node_state.is_in_stack) {
- result.has_cycle = true;
- }
- else {
- nodes_to_check.push({&linked_node});
- linked_node_state.is_in_stack = true;
- }
- break;
- }
-
- /* If no other element has been pushed, the current node can be pushed to the sorted list. */
- if (&item == &nodes_to_check.peek()) {
- ToposortNodeState &node_state = node_states[node.id()];
- node_state.is_done = true;
- node_state.is_in_stack = false;
- result.sorted_nodes.append(&node);
- nodes_to_check.pop();
- }
- }
-}
-
-NodeTreeRef::ToposortResult NodeTreeRef::toposort(const ToposortDirection direction) const
-{
- ToposortResult result;
- result.sorted_nodes.reserve(nodes_by_id_.size());
-
- Array<ToposortNodeState> node_states(nodes_by_id_.size());
-
- for (const NodeRef *node : nodes_by_id_) {
- if (node_states[node->id()].is_done) {
- /* Ignore nodes that are done already. */
- continue;
- }
- if (node->any_socket_is_directly_linked(
- direction == ToposortDirection::LeftToRight ? SOCK_OUT : SOCK_IN)) {
- /* Ignore non-start nodes. */
- continue;
- }
-
- toposort_from_start_node(direction, *node, node_states, result);
- }
-
- /* Check if the loop above forgot some nodes because there is a cycle. */
- if (result.sorted_nodes.size() < nodes_by_id_.size()) {
- result.has_cycle = true;
- for (const NodeRef *node : nodes_by_id_) {
- if (node_states[node->id()].is_done) {
- /* Ignore nodes that are done already. */
- continue;
- }
- /* Start toposort at this node which is somewhere in the middle of a loop. */
- toposort_from_start_node(direction, *node, node_states, result);
- }
- }
-
- BLI_assert(result.sorted_nodes.size() == nodes_by_id_.size());
- return result;
-}
-
-const NodeRef *NodeTreeRef::find_node(const bNode &bnode) const
-{
- for (const NodeRef *node : this->nodes_by_type(bnode.typeinfo)) {
- if (node->bnode_ == &bnode) {
- return node;
- }
- }
- return nullptr;
-}
-
-std::string NodeTreeRef::to_dot() const
-{
- dot::DirectedGraph digraph;
- digraph.set_rankdir(dot::Attr_rankdir::LeftToRight);
-
- Map<const NodeRef *, dot::NodeWithSocketsRef> dot_nodes;
-
- for (const NodeRef *node : nodes_by_id_) {
- dot::Node &dot_node = digraph.new_node("");
- dot_node.set_background_color("white");
-
- Vector<std::string> input_names;
- Vector<std::string> output_names;
- for (const InputSocketRef *socket : node->inputs()) {
- input_names.append(socket->name());
- }
- for (const OutputSocketRef *socket : node->outputs()) {
- output_names.append(socket->name());
- }
-
- dot_nodes.add_new(node,
- dot::NodeWithSocketsRef(dot_node, node->name(), input_names, output_names));
- }
-
- for (const OutputSocketRef *from_socket : output_sockets_) {
- for (const InputSocketRef *to_socket : from_socket->directly_linked_sockets()) {
- dot::NodeWithSocketsRef &from_dot_node = dot_nodes.lookup(&from_socket->node());
- dot::NodeWithSocketsRef &to_dot_node = dot_nodes.lookup(&to_socket->node());
-
- digraph.new_edge(from_dot_node.output(from_socket->index()),
- to_dot_node.input(to_socket->index()));
- }
- }
-
- return digraph.to_dot_string();
-}
-
-const NodeTreeRef &get_tree_ref_from_map(NodeTreeRefMap &node_tree_refs, bNodeTree &btree)
-{
- return *node_tree_refs.lookup_or_add_cb(&btree,
- [&]() { return std::make_unique<NodeTreeRef>(&btree); });
-}
-
-PointerRNA NodeRef::rna() const
-{
- PointerRNA rna;
- RNA_pointer_create(&tree_->btree()->id, &RNA_Node, bnode_, &rna);
- return rna;
-}
-
-PointerRNA SocketRef::rna() const
-{
- PointerRNA rna;
- RNA_pointer_create(&this->tree().btree()->id, &RNA_NodeSocket, bsocket_, &rna);
- return rna;
-}
-
-} // namespace blender::nodes
diff --git a/source/blender/nodes/intern/node_util.c b/source/blender/nodes/intern/node_util.c
index e8be093c606..ddab455509d 100644
--- a/source/blender/nodes/intern/node_util.c
+++ b/source/blender/nodes/intern/node_util.c
@@ -200,7 +200,7 @@ void node_math_label(const bNodeTree *UNUSED(ntree), const bNode *node, char *la
if (!enum_label) {
name = "Unknown";
}
- BLI_strncpy(label, IFACE_(name), maxlen);
+ BLI_strncpy(label, CTX_IFACE_(BLT_I18NCONTEXT_ID_NODETREE, name), maxlen);
}
void node_vector_math_label(const bNodeTree *UNUSED(ntree),
diff --git a/source/blender/nodes/shader/CMakeLists.txt b/source/blender/nodes/shader/CMakeLists.txt
index 3e90f185168..7885ad78225 100644
--- a/source/blender/nodes/shader/CMakeLists.txt
+++ b/source/blender/nodes/shader/CMakeLists.txt
@@ -67,6 +67,7 @@ set(SRC
nodes/node_shader_map_range.cc
nodes/node_shader_mapping.cc
nodes/node_shader_math.cc
+ nodes/node_shader_mix.cc
nodes/node_shader_mix_rgb.cc
nodes/node_shader_mix_shader.cc
nodes/node_shader_normal.cc
diff --git a/source/blender/nodes/shader/node_shader_tree.cc b/source/blender/nodes/shader/node_shader_tree.cc
index f107ec73c60..57772522299 100644
--- a/source/blender/nodes/shader/node_shader_tree.cc
+++ b/source/blender/nodes/shader/node_shader_tree.cc
@@ -26,6 +26,7 @@
#include "BLT_translation.h"
#include "BKE_context.h"
+#include "BKE_layer.h"
#include "BKE_lib_id.h"
#include "BKE_linestyle.h"
#include "BKE_node.h"
@@ -71,7 +72,7 @@ static void shader_get_from_context(const bContext *C,
SpaceNode *snode = CTX_wm_space_node(C);
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *ob = OBACT(view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
if (snode->shaderfrom == SNODE_SHADER_OBJECT) {
if (ob) {
@@ -166,6 +167,7 @@ void register_node_tree_type_sh()
tt->type = NTREE_SHADER;
strcpy(tt->idname, "ShaderNodeTree");
+ strcpy(tt->group_idname, "ShaderNodeGroup");
strcpy(tt->ui_name, N_("Shader Editor"));
tt->ui_icon = ICON_NODE_MATERIAL;
strcpy(tt->ui_description, N_("Shader nodes"));
@@ -616,7 +618,7 @@ static bool ntree_shader_implicit_closure_cast(bNodeTree *ntree)
bool modified = false;
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree->links) {
if ((link->fromsock->type != SOCK_SHADER) && (link->tosock->type == SOCK_SHADER)) {
- bNode *emission_node = nodeAddStaticNode(NULL, ntree, SH_NODE_EMISSION);
+ bNode *emission_node = nodeAddStaticNode(nullptr, ntree, SH_NODE_EMISSION);
bNodeSocket *in_sock = ntree_shader_node_find_input(emission_node, "Color");
bNodeSocket *out_sock = ntree_shader_node_find_output(emission_node, "Emission");
nodeAddLink(ntree, link->fromnode, link->fromsock, emission_node, in_sock);
@@ -644,7 +646,7 @@ static void ntree_weight_tree_merge_weight(bNodeTree *ntree,
bNode **tonode,
bNodeSocket **tosock)
{
- bNode *addnode = nodeAddStaticNode(NULL, ntree, SH_NODE_MATH);
+ bNode *addnode = nodeAddStaticNode(nullptr, ntree, SH_NODE_MATH);
addnode->custom1 = NODE_MATH_ADD;
addnode->tmp_flag = -2; /* Copy */
bNodeSocket *addsock_out = ntree_shader_node_output_get(addnode, 0);
@@ -682,19 +684,19 @@ static bool ntree_weight_tree_tag_nodes(bNode *fromnode, bNode *tonode, void *us
* with their respective weights. */
static void ntree_shader_weight_tree_invert(bNodeTree *ntree, bNode *output_node)
{
- bNodeLink *displace_link = NULL;
+ bNodeLink *displace_link = nullptr;
bNodeSocket *displace_output = ntree_shader_node_find_input(output_node, "Displacement");
if (displace_output && displace_output->link) {
/* Remove any displacement link to avoid tagging it later on. */
displace_link = displace_output->link;
- displace_output->link = NULL;
+ displace_output->link = nullptr;
}
- bNodeLink *thickness_link = NULL;
+ bNodeLink *thickness_link = nullptr;
bNodeSocket *thickness_output = ntree_shader_node_find_input(output_node, "Thickness");
if (thickness_output && thickness_output->link) {
/* Remove any thickness link to avoid tagging it later on. */
thickness_link = thickness_output->link;
- thickness_output->link = NULL;
+ thickness_output->link = nullptr;
}
/* Init tmp flag. */
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
@@ -712,10 +714,11 @@ static void ntree_shader_weight_tree_invert(bNodeTree *ntree, bNode *output_node
switch (node->type) {
case SH_NODE_SHADERTORGB:
+ case SH_NODE_OUTPUT_LIGHT:
case SH_NODE_OUTPUT_WORLD:
case SH_NODE_OUTPUT_MATERIAL: {
/* Start the tree with full weight. */
- nodes_copy[id] = nodeAddStaticNode(NULL, ntree, SH_NODE_VALUE);
+ nodes_copy[id] = nodeAddStaticNode(nullptr, ntree, SH_NODE_VALUE);
nodes_copy[id]->tmp_flag = -2; /* Copy */
((bNodeSocketValueFloat *)ntree_shader_node_output_get(nodes_copy[id], 0)->default_value)
->value = 1.0f;
@@ -724,7 +727,7 @@ static void ntree_shader_weight_tree_invert(bNodeTree *ntree, bNode *output_node
case SH_NODE_ADD_SHADER: {
/* Simple passthrough node. Each original inputs will get the same weight. */
/* TODO(fclem): Better use some kind of reroute node? */
- nodes_copy[id] = nodeAddStaticNode(NULL, ntree, SH_NODE_MATH);
+ nodes_copy[id] = nodeAddStaticNode(nullptr, ntree, SH_NODE_MATH);
nodes_copy[id]->custom1 = NODE_MATH_ADD;
nodes_copy[id]->tmp_flag = -2; /* Copy */
((bNodeSocketValueFloat *)ntree_shader_node_input_get(nodes_copy[id], 0)->default_value)
@@ -737,17 +740,17 @@ static void ntree_shader_weight_tree_invert(bNodeTree *ntree, bNode *output_node
bNodeSocket *fromsock, *tosock;
int id_start = id;
/* output = (factor * input_weight) */
- nodes_copy[id] = nodeAddStaticNode(NULL, ntree, SH_NODE_MATH);
+ nodes_copy[id] = nodeAddStaticNode(nullptr, ntree, SH_NODE_MATH);
nodes_copy[id]->custom1 = NODE_MATH_MULTIPLY;
nodes_copy[id]->tmp_flag = -2; /* Copy */
id++;
/* output = ((1.0 - factor) * input_weight) <=> (input_weight - factor * input_weight) */
- nodes_copy[id] = nodeAddStaticNode(NULL, ntree, SH_NODE_MATH);
+ nodes_copy[id] = nodeAddStaticNode(nullptr, ntree, SH_NODE_MATH);
nodes_copy[id]->custom1 = NODE_MATH_SUBTRACT;
nodes_copy[id]->tmp_flag = -2; /* Copy */
id++;
/* Node sanitizes the input mix factor by clamping it. */
- nodes_copy[id] = nodeAddStaticNode(NULL, ntree, SH_NODE_MATH);
+ nodes_copy[id] = nodeAddStaticNode(nullptr, ntree, SH_NODE_MATH);
nodes_copy[id]->custom1 = NODE_MATH_ADD;
nodes_copy[id]->custom2 = SHD_MATH_CLAMP;
nodes_copy[id]->tmp_flag = -2; /* Copy */
@@ -763,7 +766,7 @@ static void ntree_shader_weight_tree_invert(bNodeTree *ntree, bNode *output_node
id++;
/* Reroute the weight input to the 3 processing nodes. Simplify linking later-on. */
/* TODO(fclem): Better use some kind of reroute node? */
- nodes_copy[id] = nodeAddStaticNode(NULL, ntree, SH_NODE_MATH);
+ nodes_copy[id] = nodeAddStaticNode(nullptr, ntree, SH_NODE_MATH);
nodes_copy[id]->custom1 = NODE_MATH_ADD;
nodes_copy[id]->tmp_flag = -2; /* Copy */
((bNodeSocketValueFloat *)ntree_shader_node_input_get(nodes_copy[id], 0)->default_value)
@@ -810,6 +813,7 @@ static void ntree_shader_weight_tree_invert(bNodeTree *ntree, bNode *output_node
switch (node->type) {
case SH_NODE_SHADERTORGB:
+ case SH_NODE_OUTPUT_LIGHT:
case SH_NODE_OUTPUT_WORLD:
case SH_NODE_OUTPUT_MATERIAL:
case SH_NODE_ADD_SHADER: {
@@ -1007,6 +1011,7 @@ static void ntree_shader_pruned_unused(bNodeTree *ntree, bNode *output_node)
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->type == SH_NODE_OUTPUT_AOV) {
+ node->tmp_flag = 1;
nodeChainIterBackwards(ntree, node, ntree_branch_node_tag, nullptr, 0);
}
}
@@ -1036,7 +1041,7 @@ void ntreeGPUMaterialNodes(bNodeTree *localtree, GPUMaterial *mat)
/* Tree is valid if it contains no undefined implicit socket type cast. */
bool valid_tree = ntree_shader_implicit_closure_cast(localtree);
- if (valid_tree && output != NULL) {
+ if (valid_tree && output != nullptr) {
ntree_shader_pruned_unused(localtree, output);
ntree_shader_shader_to_rgba_branch(localtree, output);
ntree_shader_weight_tree_invert(localtree, output);
diff --git a/source/blender/nodes/shader/node_shader_util.hh b/source/blender/nodes/shader/node_shader_util.hh
index d5f54d9cac9..38220634695 100644
--- a/source/blender/nodes/shader/node_shader_util.hh
+++ b/source/blender/nodes/shader/node_shader_util.hh
@@ -59,6 +59,8 @@
#include "RE_pipeline.h"
#include "RE_texture.h"
+#include "RNA_access.h"
+
bool sh_node_poll_default(struct bNodeType *ntype,
struct bNodeTree *ntree,
const char **r_disabled_hint);
diff --git a/source/blender/nodes/shader/nodes/node_shader_attribute.cc b/source/blender/nodes/shader/nodes/node_shader_attribute.cc
index d01271c15d3..65d053e6379 100644
--- a/source/blender/nodes/shader/nodes/node_shader_attribute.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_attribute.cc
@@ -36,6 +36,7 @@ static int node_shader_gpu_attribute(GPUMaterial *mat,
{
NodeShaderAttribute *attr = static_cast<NodeShaderAttribute *>(node->storage);
bool is_varying = attr->type == SHD_ATTRIBUTE_GEOMETRY;
+ float attr_hash = 0.0f;
GPUNodeLink *cd_attr;
@@ -43,7 +44,12 @@ static int node_shader_gpu_attribute(GPUMaterial *mat,
cd_attr = GPU_attribute(mat, CD_AUTO_FROM_NAME, attr->name);
}
else {
- cd_attr = GPU_uniform_attribute(mat, attr->name, attr->type == SHD_ATTRIBUTE_INSTANCER);
+ cd_attr = GPU_uniform_attribute(mat,
+ attr->name,
+ attr->type == SHD_ATTRIBUTE_INSTANCER,
+ reinterpret_cast<uint32_t *>(&attr_hash));
+
+ GPU_link(mat, "node_attribute_uniform", cd_attr, GPU_constant(&attr_hash), &cd_attr);
}
if (STREQ(attr->name, "color")) {
@@ -55,9 +61,11 @@ static int node_shader_gpu_attribute(GPUMaterial *mat,
GPU_stack_link(mat, node, "node_attribute", in, out, cd_attr);
- int i;
- LISTBASE_FOREACH_INDEX (bNodeSocket *, sock, &node->outputs, i) {
- node_shader_gpu_bump_tex_coord(mat, node, &out[i].link);
+ if (is_varying) {
+ int i;
+ LISTBASE_FOREACH_INDEX (bNodeSocket *, sock, &node->outputs, i) {
+ node_shader_gpu_bump_tex_coord(mat, node, &out[i].link);
+ }
}
return 1;
diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_hair_principled.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_hair_principled.cc
index 6495dcfffba..a0579372a15 100644
--- a/source/blender/nodes/shader/nodes/node_shader_bsdf_hair_principled.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_hair_principled.cc
@@ -42,8 +42,7 @@ static void node_declare(NodeDeclarationBuilder &b)
.min(0.0f)
.max(1.0f)
.subtype(PROP_FACTOR);
- b.add_input<decl::Float>(N_("IOR")).default_value(1.55f).min(0.0f).max(1000.0f).subtype(
- PROP_FACTOR);
+ b.add_input<decl::Float>(N_("IOR")).default_value(1.55f).min(0.0f).max(1000.0f);
b.add_input<decl::Float>(N_("Offset"))
.default_value(2.0f * ((float)M_PI) / 180.0f)
.min(-M_PI_2)
diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc
index a63c7aede04..7b72d4b9be4 100644
--- a/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc
@@ -167,6 +167,27 @@ static int node_shader_gpu_bsdf_principled(GPUMaterial *mat,
if (use_transparency) {
flag |= GPU_MATFLAG_TRANSPARENT;
}
+ if (use_clear) {
+ flag |= GPU_MATFLAG_CLEARCOAT;
+ }
+
+ /* Ref. T98190: Defines are optimizations for old compilers.
+ * Might become unnecessary with EEVEE-Next. */
+ if (use_diffuse == false && use_refract == false && use_clear == true) {
+ flag |= GPU_MATFLAG_PRINCIPLED_CLEARCOAT;
+ }
+ else if (use_diffuse == false && use_refract == false && use_clear == false) {
+ flag |= GPU_MATFLAG_PRINCIPLED_METALLIC;
+ }
+ else if (use_diffuse == true && use_refract == false && use_clear == false) {
+ flag |= GPU_MATFLAG_PRINCIPLED_DIELECTRIC;
+ }
+ else if (use_diffuse == false && use_refract == true && use_clear == false) {
+ flag |= GPU_MATFLAG_PRINCIPLED_GLASS;
+ }
+ else {
+ flag |= GPU_MATFLAG_PRINCIPLED_ANY;
+ }
if (use_subsurf) {
bNodeSocket *socket = (bNodeSocket *)BLI_findlink(&node->original->inputs, 2);
diff --git a/source/blender/nodes/shader/nodes/node_shader_color_ramp.cc b/source/blender/nodes/shader/nodes/node_shader_color_ramp.cc
index 3723480ffa3..d73ffd89288 100644
--- a/source/blender/nodes/shader/nodes/node_shader_color_ramp.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_color_ramp.cc
@@ -125,7 +125,7 @@ class ColorBandFunction : public fn::MultiFunction {
static void sh_node_valtorgb_build_multi_function(nodes::NodeMultiFunctionBuilder &builder)
{
- bNode &bnode = builder.node();
+ const bNode &bnode = builder.node();
const ColorBand *color_band = (const ColorBand *)bnode.storage;
builder.construct_and_set_matching_fn<ColorBandFunction>(*color_band);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_curves.cc b/source/blender/nodes/shader/nodes/node_shader_curves.cc
index eb47059063d..4725aef5991 100644
--- a/source/blender/nodes/shader/nodes/node_shader_curves.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_curves.cc
@@ -95,7 +95,7 @@ class CurveVecFunction : public fn::MultiFunction {
static void sh_node_curve_vec_build_multi_function(NodeMultiFunctionBuilder &builder)
{
- bNode &bnode = builder.node();
+ const bNode &bnode = builder.node();
CurveMapping *cumap = (CurveMapping *)bnode.storage;
BKE_curvemapping_init(cumap);
builder.construct_and_set_matching_fn<CurveVecFunction>(*cumap);
@@ -237,7 +237,7 @@ class CurveRGBFunction : public fn::MultiFunction {
static void sh_node_curve_rgb_build_multi_function(NodeMultiFunctionBuilder &builder)
{
- bNode &bnode = builder.node();
+ const bNode &bnode = builder.node();
CurveMapping *cumap = (CurveMapping *)bnode.storage;
BKE_curvemapping_init(cumap);
builder.construct_and_set_matching_fn<CurveRGBFunction>(*cumap);
@@ -356,7 +356,7 @@ class CurveFloatFunction : public fn::MultiFunction {
static void sh_node_curve_float_build_multi_function(NodeMultiFunctionBuilder &builder)
{
- bNode &bnode = builder.node();
+ const bNode &bnode = builder.node();
CurveMapping *cumap = (CurveMapping *)bnode.storage;
BKE_curvemapping_init(cumap);
builder.construct_and_set_matching_fn<CurveFloatFunction>(*cumap);
diff --git a/source/blender/nodes/shader/nodes/node_shader_geometry.cc b/source/blender/nodes/shader/nodes/node_shader_geometry.cc
index 47df932f9d4..d23561de7ff 100644
--- a/source/blender/nodes/shader/nodes/node_shader_geometry.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_geometry.cc
@@ -29,10 +29,9 @@ static int node_shader_gpu_geometry(GPUMaterial *mat,
if (out[5].hasoutput) {
GPU_material_flag_set(mat, GPU_MATFLAG_BARYCENTRIC);
}
- /* Opti: don't request orco if not needed. */
+ /* Optimization: don't request orco if not needed. */
const float val[4] = {0.0f, 0.0f, 0.0f, 0.0f};
- GPUNodeLink *orco_link = (!out[2].hasoutput) ? GPU_constant(val) :
- GPU_attribute(mat, CD_ORCO, "");
+ GPUNodeLink *orco_link = out[2].hasoutput ? GPU_attribute(mat, CD_ORCO, "") : GPU_constant(val);
const bool success = GPU_stack_link(mat, node, "node_geometry", in, out, orco_link);
diff --git a/source/blender/nodes/shader/nodes/node_shader_hair_info.cc b/source/blender/nodes/shader/nodes/node_shader_hair_info.cc
index 11d23e47735..f46556291ce 100644
--- a/source/blender/nodes/shader/nodes/node_shader_hair_info.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_hair_info.cc
@@ -23,8 +23,8 @@ static int node_shader_gpu_hair_info(GPUMaterial *mat,
{
/* Length: don't request length if not needed. */
static const float zero = 0;
- GPUNodeLink *length_link = (!out[2].hasoutput) ? GPU_constant(&zero) :
- GPU_attribute(mat, CD_HAIRLENGTH, "");
+ GPUNodeLink *length_link = out[2].hasoutput ? GPU_attribute(mat, CD_HAIRLENGTH, "") :
+ GPU_constant(&zero);
return GPU_stack_link(mat, node, "node_hair_info", in, out, length_link);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_math.cc b/source/blender/nodes/shader/nodes/node_shader_math.cc
index 8a2b18d7d76..bd83f8dac37 100644
--- a/source/blender/nodes/shader/nodes/node_shader_math.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_math.cc
@@ -61,8 +61,9 @@ static void sh_node_math_gather_link_searches(GatherLinkSearchOpParams &params)
ELEM(item->value, NODE_MATH_COMPARE, NODE_MATH_GREATER_THAN, NODE_MATH_LESS_THAN)) ?
-1 :
weight;
- params.add_item(
- IFACE_(item->name), SocketSearchOp{"Value", (NodeMathOperation)item->value}, gn_weight);
+ params.add_item(CTX_IFACE_(BLT_I18NCONTEXT_ID_NODETREE, item->name),
+ SocketSearchOp{"Value", (NodeMathOperation)item->value},
+ gn_weight);
}
}
}
@@ -101,7 +102,7 @@ static int gpu_shader_math(GPUMaterial *mat,
return 0;
}
-static const fn::MultiFunction *get_base_multi_function(bNode &node)
+static const fn::MultiFunction *get_base_multi_function(const bNode &node)
{
const int mode = node.custom1;
const fn::MultiFunction *base_fn = nullptr;
diff --git a/source/blender/nodes/shader/nodes/node_shader_mix.cc b/source/blender/nodes/shader/nodes/node_shader_mix.cc
new file mode 100644
index 00000000000..f785e32832e
--- /dev/null
+++ b/source/blender/nodes/shader/nodes/node_shader_mix.cc
@@ -0,0 +1,443 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2005 Blender Foundation. All rights reserved. */
+
+/** \file
+ * \ingroup shdnodes
+ */
+
+#include <algorithm>
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_shader_util.hh"
+
+#include "NOD_socket_search_link.hh"
+#include "RNA_enum_types.h"
+
+namespace blender::nodes::node_sh_mix_cc {
+
+NODE_STORAGE_FUNCS(NodeShaderMix)
+
+static void sh_node_mix_declare(NodeDeclarationBuilder &b)
+{
+ b.is_function_node();
+ b.add_input<decl::Float>(N_("Factor"), "Factor_Float")
+ .default_value(0.5f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR);
+ b.add_input<decl::Vector>(N_("Factor"), "Factor_Vector")
+ .default_value(float3(0.5f))
+ .subtype(PROP_FACTOR);
+
+ b.add_input<decl::Float>(N_("A"), "A_Float")
+ .min(-10000.0f)
+ .max(10000.0f)
+ .is_default_link_socket();
+ b.add_input<decl::Float>(N_("B"), "B_Float").min(-10000.0f).max(10000.0f);
+
+ b.add_input<decl::Vector>(N_("A"), "A_Vector").is_default_link_socket();
+ b.add_input<decl::Vector>(N_("B"), "B_Vector");
+
+ b.add_input<decl::Color>(N_("A"), "A_Color")
+ .default_value({0.5f, 0.5f, 0.5f, 1.0f})
+ .is_default_link_socket();
+ b.add_input<decl::Color>(N_("B"), "B_Color").default_value({0.5f, 0.5f, 0.5f, 1.0f});
+
+ b.add_output<decl::Float>(N_("Result"), "Result_Float");
+ b.add_output<decl::Vector>(N_("Result"), "Result_Vector");
+ b.add_output<decl::Color>(N_("Result"), "Result_Color");
+};
+
+static void sh_node_mix_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ const NodeShaderMix &data = node_storage(*static_cast<const bNode *>(ptr->data));
+ uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE);
+ if (data.data_type == SOCK_VECTOR) {
+ uiItemR(layout, ptr, "factor_mode", 0, "", ICON_NONE);
+ }
+ if (data.data_type == SOCK_RGBA) {
+ uiItemR(layout, ptr, "blend_type", 0, "", ICON_NONE);
+ uiItemR(layout, ptr, "clamp_result", 0, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "clamp_factor", 0, nullptr, ICON_NONE);
+ }
+ else {
+ uiItemR(layout, ptr, "clamp_factor", 0, nullptr, ICON_NONE);
+ }
+}
+
+static void sh_node_mix_label(const bNodeTree *UNUSED(ntree),
+ const bNode *node,
+ char *label,
+ int maxlen)
+{
+ const NodeShaderMix &storage = node_storage(*node);
+ if (storage.data_type == SOCK_RGBA) {
+ const char *name;
+ bool enum_label = RNA_enum_name(rna_enum_ramp_blend_items, storage.blend_type, &name);
+ if (!enum_label) {
+ name = "Unknown";
+ }
+ BLI_strncpy(label, IFACE_(name), maxlen);
+ }
+}
+
+static int sh_node_mix_ui_class(const bNode *node)
+{
+ const NodeShaderMix &storage = node_storage(*node);
+ const eNodeSocketDatatype data_type = static_cast<eNodeSocketDatatype>(storage.data_type);
+
+ switch (data_type) {
+ case SOCK_VECTOR:
+ return NODE_CLASS_OP_VECTOR;
+ case SOCK_RGBA:
+ return NODE_CLASS_OP_COLOR;
+ default:
+ return NODE_CLASS_CONVERTER;
+ }
+}
+
+static void sh_node_mix_update(bNodeTree *ntree, bNode *node)
+{
+ const NodeShaderMix &storage = node_storage(*node);
+ const eNodeSocketDatatype data_type = static_cast<eNodeSocketDatatype>(storage.data_type);
+
+ LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) {
+ nodeSetSocketAvailability(ntree, socket, socket->type == data_type);
+ }
+
+ LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) {
+ nodeSetSocketAvailability(ntree, socket, socket->type == data_type);
+ }
+
+ bool use_vector_factor = data_type == SOCK_VECTOR &&
+ storage.factor_mode != NODE_MIX_MODE_UNIFORM;
+
+ bNodeSocket *sock_factor = (bNodeSocket *)BLI_findlink(&node->inputs, 0);
+ nodeSetSocketAvailability(ntree, sock_factor, !use_vector_factor);
+
+ bNodeSocket *sock_factor_vec = (bNodeSocket *)BLI_findlink(&node->inputs, 1);
+ nodeSetSocketAvailability(ntree, sock_factor_vec, use_vector_factor);
+}
+
+static void node_mix_gather_link_searches(GatherLinkSearchOpParams &params)
+{
+ const eNodeSocketDatatype sock_type = static_cast<eNodeSocketDatatype>(
+ params.other_socket().type);
+
+ if (ELEM(sock_type, SOCK_BOOLEAN, SOCK_FLOAT, SOCK_RGBA, SOCK_VECTOR, SOCK_INT)) {
+ const eNodeSocketDatatype type = ELEM(sock_type, SOCK_BOOLEAN, SOCK_INT) ? SOCK_FLOAT :
+ sock_type;
+
+ if (params.in_out() == SOCK_OUT) {
+ params.add_item(IFACE_("Result"), [type](LinkSearchOpParams &params) {
+ bNode &node = params.add_node("ShaderNodeMix");
+ node_storage(node).data_type = type;
+ params.update_and_connect_available_socket(node, "Result");
+ });
+ }
+ else {
+ if (ELEM(sock_type, SOCK_VECTOR, SOCK_RGBA)) {
+ params.add_item(IFACE_("Factor (Non-Uniform)"), [](LinkSearchOpParams &params) {
+ bNode &node = params.add_node("ShaderNodeMix");
+ node_storage(node).data_type = SOCK_VECTOR;
+ node_storage(node).factor_mode = NODE_MIX_MODE_NON_UNIFORM;
+ params.update_and_connect_available_socket(node, "Factor");
+ });
+ }
+ params.add_item(IFACE_("Factor"), [type](LinkSearchOpParams &params) {
+ bNode &node = params.add_node("ShaderNodeMix");
+ node_storage(node).data_type = type;
+ params.update_and_connect_available_socket(node, "Factor");
+ });
+ params.add_item(IFACE_("A"), [type](LinkSearchOpParams &params) {
+ bNode &node = params.add_node("ShaderNodeMix");
+ node_storage(node).data_type = type;
+ params.update_and_connect_available_socket(node, "A");
+ });
+ params.add_item(IFACE_("B"), [type](LinkSearchOpParams &params) {
+ bNode &node = params.add_node("ShaderNodeMix");
+ node_storage(node).data_type = type;
+ params.update_and_connect_available_socket(node, "B");
+ });
+ }
+ }
+}
+
+static void node_mix_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeShaderMix *data = MEM_cnew<NodeShaderMix>(__func__);
+ data->data_type = SOCK_FLOAT;
+ data->factor_mode = NODE_MIX_MODE_UNIFORM;
+ data->clamp_factor = 1;
+ data->clamp_result = 0;
+ data->blend_type = MA_RAMP_BLEND;
+ node->storage = data;
+}
+
+static const char *gpu_shader_get_name(eNodeSocketDatatype data_type,
+ const bool non_uniform,
+ const int blend_type)
+{
+ switch (data_type) {
+ case SOCK_FLOAT:
+ return "node_mix_float";
+ case SOCK_VECTOR:
+ return (non_uniform) ? "node_mix_vector_non_uniform" : "node_mix_vector";
+ case SOCK_RGBA:
+ switch (blend_type) {
+ case MA_RAMP_BLEND:
+ return "node_mix_blend";
+ case MA_RAMP_ADD:
+ return "node_mix_add";
+ case MA_RAMP_MULT:
+ return "node_mix_mult";
+ case MA_RAMP_SUB:
+ return "node_mix_sub";
+ case MA_RAMP_SCREEN:
+ return "node_mix_screen";
+ case MA_RAMP_DIV:
+ return "node_mix_div_fallback";
+ case MA_RAMP_DIFF:
+ return "node_mix_diff";
+ case MA_RAMP_DARK:
+ return "node_mix_dark";
+ case MA_RAMP_LIGHT:
+ return "node_mix_light";
+ case MA_RAMP_OVERLAY:
+ return "node_mix_overlay";
+ case MA_RAMP_DODGE:
+ return "node_mix_dodge";
+ case MA_RAMP_BURN:
+ return "node_mix_burn";
+ case MA_RAMP_HUE:
+ return "node_mix_hue";
+ case MA_RAMP_SAT:
+ return "node_mix_sat";
+ case MA_RAMP_VAL:
+ return "node_mix_val";
+ case MA_RAMP_COLOR:
+ return "node_mix_color";
+ case MA_RAMP_SOFT:
+ return "node_mix_soft";
+ case MA_RAMP_LINEAR:
+ return "node_mix_linear";
+ default:
+ BLI_assert_unreachable();
+ return nullptr;
+ }
+ default:
+ BLI_assert_unreachable();
+ return nullptr;
+ }
+}
+
+static int gpu_shader_mix(GPUMaterial *mat,
+ bNode *node,
+ bNodeExecData *UNUSED(execdata),
+ GPUNodeStack *in,
+ GPUNodeStack *out)
+{
+ const NodeShaderMix &storage = node_storage(*node);
+ const bool is_non_uniform = storage.factor_mode == NODE_MIX_MODE_NON_UNIFORM;
+ const bool is_color_mode = storage.data_type == SOCK_RGBA;
+ const bool is_vector_mode = storage.data_type == SOCK_VECTOR;
+ const int blend_type = storage.blend_type;
+ const char *name = gpu_shader_get_name(
+ (eNodeSocketDatatype)storage.data_type, is_non_uniform, blend_type);
+
+ if (name == nullptr) {
+ return 0;
+ }
+
+ if (storage.clamp_factor) {
+ if (is_non_uniform && is_vector_mode) {
+ const float min[3] = {0.0f, 0.0f, 0.0f};
+ const float max[3] = {1.0f, 1.0f, 1.0f};
+ const GPUNodeLink *factor_link = in[1].link ? in[1].link : GPU_uniform(in[1].vec);
+ GPU_link(mat,
+ "node_mix_clamp_vector",
+ factor_link,
+ GPU_constant(min),
+ GPU_constant(max),
+ &in[1].link);
+ }
+ else {
+ const float min = 0.0f;
+ const float max = 1.0f;
+ const GPUNodeLink *factor_link = in[0].link ? in[0].link : GPU_uniform(in[0].vec);
+ GPU_link(mat,
+ "node_mix_clamp_value",
+ factor_link,
+ GPU_constant(&min),
+ GPU_constant(&max),
+ &in[0].link);
+ }
+ }
+
+ int ret = GPU_stack_link(mat, node, name, in, out);
+
+ if (ret && is_color_mode && storage.clamp_result) {
+ const float min[3] = {0.0f, 0.0f, 0.0f};
+ const float max[3] = {1.0f, 1.0f, 1.0f};
+ GPU_link(mat,
+ "node_mix_clamp_vector",
+ out[2].link,
+ GPU_constant(min),
+ GPU_constant(max),
+ &out[2].link);
+ }
+ return ret;
+}
+
+class MixColorFunction : public fn::MultiFunction {
+ private:
+ const bool clamp_factor_;
+ const bool clamp_result_;
+ const int blend_type_;
+
+ public:
+ MixColorFunction(const bool clamp_factor, const bool clamp_result, const int blend_type)
+ : clamp_factor_(clamp_factor), clamp_result_(clamp_result), blend_type_(blend_type)
+ {
+ static fn::MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ }
+
+ static fn::MFSignature create_signature()
+ {
+ fn::MFSignatureBuilder signature{"MixColor"};
+ signature.single_input<float>("Factor");
+ signature.single_input<ColorGeometry4f>("A");
+ signature.single_input<ColorGeometry4f>("B");
+ signature.single_output<ColorGeometry4f>("Result");
+ return signature.build();
+ }
+
+ void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override
+ {
+ const VArray<float> &fac = params.readonly_single_input<float>(0, "Factor");
+ const VArray<ColorGeometry4f> &col1 = params.readonly_single_input<ColorGeometry4f>(1, "A");
+ const VArray<ColorGeometry4f> &col2 = params.readonly_single_input<ColorGeometry4f>(2, "B");
+ MutableSpan<ColorGeometry4f> results = params.uninitialized_single_output<ColorGeometry4f>(
+ 3, "Result");
+
+ if (clamp_factor_) {
+ for (int64_t i : mask) {
+ results[i] = col1[i];
+ ramp_blend(blend_type_, results[i], std::clamp(fac[i], 0.0f, 1.0f), col2[i]);
+ }
+ }
+ else {
+ for (int64_t i : mask) {
+ results[i] = col1[i];
+ ramp_blend(blend_type_, results[i], fac[i], col2[i]);
+ }
+ }
+
+ if (clamp_result_) {
+ for (int64_t i : mask) {
+ clamp_v3(results[i], 0.0f, 1.0f);
+ }
+ }
+ }
+};
+
+static const fn::MultiFunction *get_multi_function(const bNode &node)
+{
+ const NodeShaderMix *data = (NodeShaderMix *)node.storage;
+ bool uniform_factor = data->factor_mode == NODE_MIX_MODE_UNIFORM;
+ const bool clamp_factor = data->clamp_factor;
+ switch (data->data_type) {
+ case SOCK_FLOAT: {
+ if (clamp_factor) {
+ static fn::CustomMF_SI_SI_SI_SO<float, float, float, float> fn{
+ "Clamp Mix Float", [](float t, const float a, const float b) {
+ return math::interpolate(a, b, std::clamp(t, 0.0f, 1.0f));
+ }};
+ return &fn;
+ }
+ else {
+ static fn::CustomMF_SI_SI_SI_SO<float, float, float, float> fn{
+ "Mix Float", [](const float t, const float a, const float b) {
+ return math::interpolate(a, b, t);
+ }};
+ return &fn;
+ }
+ }
+ case SOCK_VECTOR: {
+ if (clamp_factor) {
+ if (uniform_factor) {
+ static fn::CustomMF_SI_SI_SI_SO<float, float3, float3, float3> fn{
+ "Clamp Mix Vector", [](const float t, const float3 a, const float3 b) {
+ return math::interpolate(a, b, std::clamp(t, 0.0f, 1.0f));
+ }};
+ return &fn;
+ }
+ else {
+ static fn::CustomMF_SI_SI_SI_SO<float3, float3, float3, float3> fn{
+ "Clamp Mix Vector Non Uniform", [](float3 t, const float3 a, const float3 b) {
+ t = math::clamp(t, 0.0f, 1.0f);
+ return a * (float3(1.0f) - t) + b * t;
+ }};
+ return &fn;
+ }
+ }
+ else {
+ if (uniform_factor) {
+ static fn::CustomMF_SI_SI_SI_SO<float, float3, float3, float3> fn{
+ "Mix Vector", [](const float t, const float3 a, const float3 b) {
+ return math::interpolate(a, b, t);
+ }};
+ return &fn;
+ }
+ else {
+ static fn::CustomMF_SI_SI_SI_SO<float3, float3, float3, float3> fn{
+ "Mix Vector Non Uniform", [](const float3 t, const float3 a, const float3 b) {
+ return a * (float3(1.0f) - t) + b * t;
+ }};
+ return &fn;
+ }
+ }
+ }
+ }
+ BLI_assert_unreachable();
+ return nullptr;
+}
+
+static void sh_node_mix_build_multi_function(NodeMultiFunctionBuilder &builder)
+{
+ const NodeShaderMix &storage = node_storage(builder.node());
+
+ if (storage.data_type == SOCK_RGBA) {
+ builder.construct_and_set_matching_fn<MixColorFunction>(
+ storage.clamp_factor, storage.clamp_result, storage.blend_type);
+ }
+ else {
+ const fn::MultiFunction *fn = get_multi_function(builder.node());
+ builder.set_matching_fn(fn);
+ }
+}
+
+} // namespace blender::nodes::node_sh_mix_cc
+
+void register_node_type_sh_mix()
+{
+ namespace file_ns = blender::nodes::node_sh_mix_cc;
+
+ static bNodeType ntype;
+ sh_fn_node_type_base(&ntype, SH_NODE_MIX, "Mix", NODE_CLASS_CONVERTER);
+ ntype.declare = file_ns::sh_node_mix_declare;
+ ntype.ui_class = file_ns::sh_node_mix_ui_class;
+ node_type_gpu(&ntype, file_ns::gpu_shader_mix);
+ node_type_update(&ntype, file_ns::sh_node_mix_update);
+ node_type_init(&ntype, file_ns::node_mix_init);
+ node_type_storage(
+ &ntype, "NodeShaderMix", node_free_standard_storage, node_copy_standard_storage);
+ ntype.build_multi_function = file_ns::sh_node_mix_build_multi_function;
+ ntype.draw_buttons = file_ns::sh_node_mix_layout;
+ ntype.labelfunc = file_ns::sh_node_mix_label;
+ ntype.gather_link_search_ops = file_ns::node_mix_gather_link_searches;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/shader/nodes/node_shader_mix_rgb.cc b/source/blender/nodes/shader/nodes/node_shader_mix_rgb.cc
index 12707623049..46ac8f05803 100644
--- a/source/blender/nodes/shader/nodes/node_shader_mix_rgb.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_mix_rgb.cc
@@ -32,7 +32,7 @@ static const char *gpu_shader_get_name(int mode)
case MA_RAMP_SCREEN:
return "mix_screen";
case MA_RAMP_DIV:
- return "mix_div";
+ return "mix_div_fallback";
case MA_RAMP_DIFF:
return "mix_diff";
case MA_RAMP_DARK:
@@ -70,18 +70,23 @@ static int gpu_shader_mix_rgb(GPUMaterial *mat,
{
const char *name = gpu_shader_get_name(node->custom1);
- if (name != nullptr) {
- int ret = GPU_stack_link(mat, node, name, in, out);
- if (ret && node->custom2 & SHD_MIXRGB_CLAMP) {
- const float min[3] = {0.0f, 0.0f, 0.0f};
- const float max[3] = {1.0f, 1.0f, 1.0f};
- GPU_link(
- mat, "clamp_color", out[0].link, GPU_constant(min), GPU_constant(max), &out[0].link);
- }
- return ret;
+ if (name == nullptr) {
+ return 0;
}
- return 0;
+ const float min = 0.0f;
+ const float max = 1.0f;
+ const GPUNodeLink *factor_link = in[0].link ? in[0].link : GPU_uniform(in[0].vec);
+ GPU_link(mat, "clamp_value", factor_link, GPU_constant(&min), GPU_constant(&max), &in[0].link);
+
+ int ret = GPU_stack_link(mat, node, name, in, out);
+
+ if (ret && node->custom2 & SHD_MIXRGB_CLAMP) {
+ const float min[3] = {0.0f, 0.0f, 0.0f};
+ const float max[3] = {1.0f, 1.0f, 1.0f};
+ GPU_link(mat, "clamp_color", out[0].link, GPU_constant(min), GPU_constant(max), &out[0].link);
+ }
+ return ret;
}
class MixRGBFunction : public fn::MultiFunction {
@@ -131,7 +136,7 @@ class MixRGBFunction : public fn::MultiFunction {
static void sh_node_mix_rgb_build_multi_function(NodeMultiFunctionBuilder &builder)
{
- bNode &node = builder.node();
+ const bNode &node = builder.node();
bool clamp = node.custom2 & SHD_MIXRGB_CLAMP;
int mix_type = node.custom1;
builder.construct_and_set_matching_fn<MixRGBFunction>(clamp, mix_type);
@@ -145,11 +150,11 @@ void register_node_type_sh_mix_rgb()
static bNodeType ntype;
- sh_fn_node_type_base(&ntype, SH_NODE_MIX_RGB, "Mix", NODE_CLASS_OP_COLOR);
+ sh_fn_node_type_base(&ntype, SH_NODE_MIX_RGB_LEGACY, "Mix", NODE_CLASS_OP_COLOR);
ntype.declare = file_ns::sh_node_mix_rgb_declare;
ntype.labelfunc = node_blend_label;
node_type_gpu(&ntype, file_ns::gpu_shader_mix_rgb);
ntype.build_multi_function = file_ns::sh_node_mix_rgb_build_multi_function;
-
+ ntype.gather_link_search_ops = nullptr;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_output_light.cc b/source/blender/nodes/shader/nodes/node_shader_output_light.cc
index 0eb8a60fdaa..3707806841e 100644
--- a/source/blender/nodes/shader/nodes/node_shader_output_light.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_output_light.cc
@@ -10,6 +10,22 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_input<decl::Shader>(N_("Surface"));
}
+static int node_shader_gpu_output_light(GPUMaterial *mat,
+ bNode *UNUSED(node),
+ bNodeExecData *UNUSED(execdata),
+ GPUNodeStack *in,
+ GPUNodeStack *UNUSED(out))
+{
+ GPUNodeLink *outlink_surface;
+ /* Passthrough node in order to do the right socket conversions. */
+ if (in[0].link) {
+ /* Reuse material output. */
+ GPU_link(mat, "node_output_material_surface", in[0].link, &outlink_surface);
+ GPU_material_output_surface(mat, outlink_surface);
+ }
+ return true;
+}
+
} // namespace blender::nodes::node_shader_output_light_cc
/* node type definition */
@@ -21,6 +37,8 @@ void register_node_type_sh_output_light()
sh_node_type_base(&ntype, SH_NODE_OUTPUT_LIGHT, "Light Output", NODE_CLASS_OUTPUT);
ntype.declare = file_ns::node_declare;
+ node_type_gpu(&ntype, file_ns::node_shader_gpu_output_light);
+
ntype.no_muting = true;
nodeRegisterType(&ntype);
diff --git a/source/blender/nodes/shader/nodes/node_shader_rgb.cc b/source/blender/nodes/shader/nodes/node_shader_rgb.cc
index 38acfab322f..3d28f5278a2 100644
--- a/source/blender/nodes/shader/nodes/node_shader_rgb.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_rgb.cc
@@ -17,11 +17,12 @@ static void node_declare(NodeDeclarationBuilder &b)
static int gpu_shader_rgb(GPUMaterial *mat,
bNode *node,
bNodeExecData *UNUSED(execdata),
- GPUNodeStack *in,
+ GPUNodeStack * /*in*/,
GPUNodeStack *out)
{
- GPUNodeLink *link = GPU_uniformbuf_link_out(mat, node, out, 0);
- return GPU_stack_link(mat, node, "set_rgba", in, out, link);
+ const bNodeSocket *socket = static_cast<bNodeSocket *>(node->outputs.first);
+ float *value = static_cast<bNodeSocketValueRGBA *>(socket->default_value)->value;
+ return GPU_link(mat, "set_rgba", GPU_uniform(value), &out->link);
}
} // namespace blender::nodes::node_shader_rgb_cc
diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcomb_hsv.cc b/source/blender/nodes/shader/nodes/node_shader_sepcomb_hsv.cc
index 6dfabe48292..1c02c5ba074 100644
--- a/source/blender/nodes/shader/nodes/node_shader_sepcomb_hsv.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_sepcomb_hsv.cc
@@ -39,6 +39,7 @@ void register_node_type_sh_sephsv()
sh_node_type_base(&ntype, SH_NODE_SEPHSV_LEGACY, "Separate HSV", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::node_declare_sephsv;
node_type_gpu(&ntype, file_ns::gpu_shader_sephsv);
+ ntype.gather_link_search_ops = nullptr;
nodeRegisterType(&ntype);
}
@@ -75,6 +76,7 @@ void register_node_type_sh_combhsv()
sh_node_type_base(&ntype, SH_NODE_COMBHSV_LEGACY, "Combine HSV", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::node_declare_combhsv;
node_type_gpu(&ntype, file_ns::gpu_shader_combhsv);
+ ntype.gather_link_search_ops = nullptr;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcomb_rgb.cc b/source/blender/nodes/shader/nodes/node_shader_sepcomb_rgb.cc
index 28b55047633..3fe76b05b5d 100644
--- a/source/blender/nodes/shader/nodes/node_shader_sepcomb_rgb.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_sepcomb_rgb.cc
@@ -80,6 +80,7 @@ void register_node_type_sh_seprgb()
ntype.declare = file_ns::sh_node_seprgb_declare;
node_type_gpu(&ntype, file_ns::gpu_shader_seprgb);
ntype.build_multi_function = file_ns::sh_node_seprgb_build_multi_function;
+ ntype.gather_link_search_ops = nullptr;
nodeRegisterType(&ntype);
}
@@ -123,6 +124,7 @@ void register_node_type_sh_combrgb()
ntype.declare = file_ns::sh_node_combrgb_declare;
node_type_gpu(&ntype, file_ns::gpu_shader_combrgb);
ntype.build_multi_function = file_ns::sh_node_combrgb_build_multi_function;
+ ntype.gather_link_search_ops = nullptr;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcomb_xyz.cc b/source/blender/nodes/shader/nodes/node_shader_sepcomb_xyz.cc
index 94a6febe92e..d4413036555 100644
--- a/source/blender/nodes/shader/nodes/node_shader_sepcomb_xyz.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_sepcomb_xyz.cc
@@ -48,16 +48,36 @@ class MF_SeparateXYZ : public fn::MultiFunction {
void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override
{
const VArray<float3> &vectors = params.readonly_single_input<float3>(0, "XYZ");
- MutableSpan<float> xs = params.uninitialized_single_output<float>(1, "X");
- MutableSpan<float> ys = params.uninitialized_single_output<float>(2, "Y");
- MutableSpan<float> zs = params.uninitialized_single_output<float>(3, "Z");
-
- for (int64_t i : mask) {
- float3 xyz = vectors[i];
- xs[i] = xyz.x;
- ys[i] = xyz.y;
- zs[i] = xyz.z;
+ MutableSpan<float> xs = params.uninitialized_single_output_if_required<float>(1, "X");
+ MutableSpan<float> ys = params.uninitialized_single_output_if_required<float>(2, "Y");
+ MutableSpan<float> zs = params.uninitialized_single_output_if_required<float>(3, "Z");
+
+ std::array<MutableSpan<float>, 3> outputs = {xs, ys, zs};
+ Vector<int> used_outputs;
+ if (!xs.is_empty()) {
+ used_outputs.append(0);
}
+ if (!ys.is_empty()) {
+ used_outputs.append(1);
+ }
+ if (!zs.is_empty()) {
+ used_outputs.append(2);
+ }
+
+ devirtualize_varray(vectors, [&](auto vectors) {
+ mask.to_best_mask_type([&](auto mask) {
+ const int used_outputs_num = used_outputs.size();
+ const int *used_outputs_data = used_outputs.data();
+
+ for (const int64_t i : mask) {
+ const float3 &vector = vectors[i];
+ for (const int out_i : IndexRange(used_outputs_num)) {
+ const int coordinate = used_outputs_data[out_i];
+ outputs[coordinate][i] = vector[coordinate];
+ }
+ }
+ });
+ });
}
};
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_brick.cc b/source/blender/nodes/shader/nodes/node_shader_tex_brick.cc
index cad9e1b33f2..a1c51a440d0 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_brick.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_brick.cc
@@ -261,7 +261,7 @@ class BrickFunction : public fn::MultiFunction {
static void sh_node_brick_build_multi_function(NodeMultiFunctionBuilder &builder)
{
- bNode &node = builder.node();
+ const bNode &node = builder.node();
NodeTexBrick *tex = (NodeTexBrick *)node.storage;
builder.construct_and_set_matching_fn<BrickFunction>(
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_coord.cc b/source/blender/nodes/shader/nodes/node_shader_tex_coord.cc
index fb5971021fc..9727a6089a6 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_coord.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_coord.cc
@@ -38,12 +38,12 @@ static int node_shader_gpu_tex_coord(GPUMaterial *mat,
/* Use special matrix to let the shader branch to using the render object's matrix. */
float dummy_matrix[4][4];
dummy_matrix[3][3] = 0.0f;
- GPUNodeLink *inv_obmat = (ob != NULL) ? GPU_uniform(&ob->imat[0][0]) :
- GPU_uniform(&dummy_matrix[0][0]);
+ GPUNodeLink *inv_obmat = (ob != nullptr) ? GPU_uniform(&ob->imat[0][0]) :
+ GPU_uniform(&dummy_matrix[0][0]);
- /* Opti: don't request orco if not needed. */
+ /* Optimization: don't request orco if not needed. */
float4 zero(0.0f);
- GPUNodeLink *orco = (!out[0].hasoutput) ? GPU_constant(zero) : GPU_attribute(mat, CD_ORCO, "");
+ GPUNodeLink *orco = out[0].hasoutput ? GPU_attribute(mat, CD_ORCO, "") : GPU_constant(zero);
GPUNodeLink *mtface = GPU_attribute(mat, CD_AUTO_FROM_NAME, "");
GPU_stack_link(mat, node, "node_tex_coord", in, out, inv_obmat, orco, mtface);
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_environment.cc b/source/blender/nodes/shader/nodes/node_shader_tex_environment.cc
index df5fbac3ab5..2739a75ed21 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_environment.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_environment.cc
@@ -50,7 +50,11 @@ static int node_shader_gpu_tex_environment(GPUMaterial *mat,
return GPU_stack_link(mat, node, "node_tex_environment_empty", in, out);
}
- node_shader_gpu_default_tex_coord(mat, node, &in[0].link);
+ if (!in[0].link) {
+ GPU_link(mat, "node_tex_coord_position", &in[0].link);
+ node_shader_gpu_bump_tex_coord(mat, node, &in[0].link);
+ }
+
node_shader_gpu_tex_mapping(mat, node, in, out);
/* Compute texture coordinate. */
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_gradient.cc b/source/blender/nodes/shader/nodes/node_shader_tex_gradient.cc
index 8478cbd406b..37c72ec1f17 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_gradient.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_gradient.cc
@@ -139,7 +139,7 @@ class GradientFunction : public fn::MultiFunction {
static void sh_node_gradient_tex_build_multi_function(NodeMultiFunctionBuilder &builder)
{
- bNode &node = builder.node();
+ const bNode &node = builder.node();
NodeTexGradient *tex = (NodeTexGradient *)node.storage;
builder.construct_and_set_matching_fn<GradientFunction>(tex->gradient_type);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_magic.cc b/source/blender/nodes/shader/nodes/node_shader_tex_magic.cc
index 95c4a8b8e46..205d3b89016 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_magic.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_magic.cc
@@ -161,7 +161,7 @@ class MagicFunction : public fn::MultiFunction {
static void sh_node_magic_tex_build_multi_function(NodeMultiFunctionBuilder &builder)
{
- bNode &node = builder.node();
+ const bNode &node = builder.node();
NodeTexMagic *tex = (NodeTexMagic *)node.storage;
builder.construct_and_set_matching_fn<MagicFunction>(tex->depth);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc b/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc
index c13ce3c3df3..a2241c2327f 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc
@@ -516,7 +516,7 @@ class MusgraveFunction : public fn::MultiFunction {
static void sh_node_musgrave_build_multi_function(NodeMultiFunctionBuilder &builder)
{
- bNode &node = builder.node();
+ const bNode &node = builder.node();
NodeTexMusgrave *tex = (NodeTexMusgrave *)node.storage;
builder.construct_and_set_matching_fn<MusgraveFunction>(tex->dimensions, tex->musgrave_type);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_wave.cc b/source/blender/nodes/shader/nodes/node_shader_tex_wave.cc
index ad24224dc7f..8475101dbaf 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_wave.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_wave.cc
@@ -206,7 +206,7 @@ class WaveFunction : public fn::MultiFunction {
static void sh_node_wave_tex_build_multi_function(NodeMultiFunctionBuilder &builder)
{
- bNode &node = builder.node();
+ const bNode &node = builder.node();
NodeTexWave *tex = (NodeTexWave *)node.storage;
builder.construct_and_set_matching_fn<WaveFunction>(
tex->wave_type, tex->bands_direction, tex->rings_direction, tex->wave_profile);
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc b/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc
index 6d4c491046b..64075a903ab 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc
@@ -176,7 +176,7 @@ class WhiteNoiseFunction : public fn::MultiFunction {
static void sh_node_noise_build_multi_function(NodeMultiFunctionBuilder &builder)
{
- bNode &node = builder.node();
+ const bNode &node = builder.node();
builder.construct_and_set_matching_fn<WhiteNoiseFunction>((int)node.custom1);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_value.cc b/source/blender/nodes/shader/nodes/node_shader_value.cc
index 362cdf58052..14dbb3b25eb 100644
--- a/source/blender/nodes/shader/nodes/node_shader_value.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_value.cc
@@ -17,11 +17,12 @@ static void sh_node_value_declare(NodeDeclarationBuilder &b)
static int gpu_shader_value(GPUMaterial *mat,
bNode *node,
bNodeExecData *UNUSED(execdata),
- GPUNodeStack *in,
+ GPUNodeStack * /*in*/,
GPUNodeStack *out)
{
- GPUNodeLink *link = GPU_uniformbuf_link_out(mat, node, out, 0);
- return GPU_stack_link(mat, node, "set_value", in, out, link);
+ const bNodeSocket *socket = static_cast<bNodeSocket *>(node->outputs.first);
+ float value = static_cast<bNodeSocketValueFloat *>(socket->default_value)->value;
+ return GPU_link(mat, "set_value", GPU_uniform(&value), &out->link);
}
static void sh_node_value_build_multi_function(NodeMultiFunctionBuilder &builder)
diff --git a/source/blender/nodes/shader/nodes/node_shader_vector_math.cc b/source/blender/nodes/shader/nodes/node_shader_vector_math.cc
index 21f5c44c640..d01e03f9d71 100644
--- a/source/blender/nodes/shader/nodes/node_shader_vector_math.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_vector_math.cc
@@ -225,7 +225,7 @@ static void node_shader_update_vector_math(bNodeTree *ntree, bNode *node)
}
}
-static const fn::MultiFunction *get_multi_function(bNode &node)
+static const fn::MultiFunction *get_multi_function(const bNode &node)
{
NodeVectorMathOperation operation = NodeVectorMathOperation(node.custom1);
diff --git a/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc b/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc
index b35f686e331..a036fc52d84 100644
--- a/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc
@@ -96,7 +96,7 @@ static float3 sh_node_vector_rotate_euler(const float3 &vector,
return result + center;
}
-static const fn::MultiFunction *get_multi_function(bNode &node)
+static const fn::MultiFunction *get_multi_function(const bNode &node)
{
bool invert = node.custom2;
const int mode = node.custom1;
diff --git a/source/blender/nodes/shader/nodes/node_shader_vector_transform.cc b/source/blender/nodes/shader/nodes/node_shader_vector_transform.cc
index de588f9005f..159bd238212 100644
--- a/source/blender/nodes/shader/nodes/node_shader_vector_transform.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_vector_transform.cc
@@ -83,7 +83,7 @@ static const char *get_gpufn_name_from_to(short from, short to, bool is_directio
}
break;
}
- return NULL;
+ return nullptr;
}
static int gpu_shader_vect_transform(GPUMaterial *mat,
diff --git a/source/blender/nodes/shader/nodes/node_shader_vertex_color.cc b/source/blender/nodes/shader/nodes/node_shader_vertex_color.cc
index 830f02d8df1..c636cc976e6 100644
--- a/source/blender/nodes/shader/nodes/node_shader_vertex_color.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_vertex_color.cc
@@ -42,9 +42,8 @@ static int node_shader_gpu_vertex_color(GPUMaterial *mat,
GPUNodeStack *out)
{
NodeShaderVertexColor *vertexColor = (NodeShaderVertexColor *)node->storage;
- /* NOTE: using CD_AUTO_FROM_NAME instead of CD_MCOL or CD_PROP_COLOR for named attributes
- * as geometry nodes may overwrite data which will also change the eCustomDataType.
- * This will also make EEVEE and Cycles
+ /* NOTE: Using #CD_AUTO_FROM_NAME is necessary because there are multiple color attribute types,
+ * and the type may change during evaluation anyway. This will also make EEVEE and Cycles
* consistent. See T93179. */
GPUNodeLink *vertexColorLink;
@@ -53,7 +52,7 @@ static int node_shader_gpu_vertex_color(GPUMaterial *mat,
vertexColorLink = GPU_attribute(mat, CD_AUTO_FROM_NAME, vertexColor->layer_name);
}
else { /* Fall back on active render color attribute. */
- vertexColorLink = GPU_attribute(mat, CD_MCOL, vertexColor->layer_name);
+ vertexColorLink = GPU_attribute_default_color(mat);
}
return GPU_stack_link(mat, node, "node_vertex_color", in, out, vertexColorLink);
diff --git a/source/blender/nodes/shader/nodes/node_shader_volume_info.cc b/source/blender/nodes/shader/nodes/node_shader_volume_info.cc
index a202312f8d8..1f31e9c605f 100644
--- a/source/blender/nodes/shader/nodes/node_shader_volume_info.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_volume_info.cc
@@ -25,9 +25,11 @@ static int node_shader_gpu_volume_info(GPUMaterial *mat,
}
if (out[1].hasoutput) {
out[1].link = GPU_attribute(mat, CD_AUTO_FROM_NAME, "density");
+ GPU_link(mat, "node_attribute_density", out[1].link, &out[1].link);
}
if (out[2].hasoutput) {
out[2].link = GPU_attribute(mat, CD_AUTO_FROM_NAME, "flame");
+ GPU_link(mat, "node_attribute_flame", out[2].link, &out[2].link);
}
if (out[3].hasoutput) {
out[3].link = GPU_attribute(mat, CD_AUTO_FROM_NAME, "temperature");
diff --git a/source/blender/nodes/texture/CMakeLists.txt b/source/blender/nodes/texture/CMakeLists.txt
index 5bed54ebfd7..2ccdf4c0bc9 100644
--- a/source/blender/nodes/texture/CMakeLists.txt
+++ b/source/blender/nodes/texture/CMakeLists.txt
@@ -15,6 +15,7 @@ set(INC
../../render
../../windowmanager
../../../../intern/guardedalloc
+ ../../bmesh
# RNA_prototypes.h
${CMAKE_BINARY_DIR}/source/blender/makesrna
)
diff --git a/source/blender/nodes/texture/node_texture_tree.c b/source/blender/nodes/texture/node_texture_tree.c
index 903e293a962..3f8ff85306d 100644
--- a/source/blender/nodes/texture/node_texture_tree.c
+++ b/source/blender/nodes/texture/node_texture_tree.c
@@ -18,6 +18,7 @@
#include "BLT_translation.h"
#include "BKE_context.h"
+#include "BKE_layer.h"
#include "BKE_linestyle.h"
#include "BKE_node.h"
#include "BKE_paint.h"
@@ -46,7 +47,7 @@ static void texture_get_from_context(const bContext *C,
SpaceNode *snode = CTX_wm_space_node(C);
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *ob = OBACT(view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
Tex *tx = NULL;
if (snode->texfrom == SNODE_TEX_BRUSH) {
@@ -140,6 +141,7 @@ void register_node_tree_type_tex(void)
tt->type = NTREE_TEXTURE;
strcpy(tt->idname, "TextureNodeTree");
+ strcpy(tt->group_idname, "TextureNodeGroup");
strcpy(tt->ui_name, N_("Texture Node Editor"));
tt->ui_icon = ICON_NODE_TEXTURE; /* Defined in `drawnode.c`. */
strcpy(tt->ui_description, N_("Texture nodes"));
@@ -324,7 +326,6 @@ int ntreeTexExecTree(bNodeTree *ntree,
MTex *mtex)
{
TexCallData data;
- float *nor = target->nor;
int retval = TEX_INT;
bNodeThreadStack *nts = NULL;
bNodeTreeExec *exec = ntree->execdata;
@@ -356,14 +357,7 @@ int ntreeTexExecTree(bNodeTree *ntree,
ntreeExecThreadNodes(exec, nts, &data, thread);
ntreeReleaseThreadStack(nts);
- if (target->nor) {
- retval |= TEX_NOR;
- }
retval |= TEX_RGB;
- /* confusing stuff; the texture output node sets this to NULL to indicate no normal socket was
- * set however, the texture code checks this for other reasons
- * (namely, a normal is required for material). */
- target->nor = nor;
return retval;
}
diff --git a/source/blender/nodes/texture/nodes/node_texture_output.c b/source/blender/nodes/texture/nodes/node_texture_output.c
index cf5e32cb486..b300ba9ef77 100644
--- a/source/blender/nodes/texture/nodes/node_texture_output.c
+++ b/source/blender/nodes/texture/nodes/node_texture_output.c
@@ -13,7 +13,6 @@
/* **************** COMPOSITE ******************** */
static bNodeSocketTemplate inputs[] = {
{SOCK_RGBA, N_("Color"), 0.0f, 0.0f, 0.0f, 1.0f},
- {SOCK_VECTOR, N_("Normal"), 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, PROP_DIRECTION},
{-1, ""},
};
@@ -32,12 +31,7 @@ static void exec(void *data,
TexParams params;
params_from_cdata(&params, cdata);
- if (in[1] && in[1]->hasinput && !in[0]->hasinput) {
- tex_input_rgba(target->trgba, in[1], &params, cdata->thread);
- }
- else {
- tex_input_rgba(target->trgba, in[0], &params, cdata->thread);
- }
+ tex_input_rgba(target->trgba, in[0], &params, cdata->thread);
}
else {
/* 0 means don't care, so just use first */
@@ -49,15 +43,6 @@ static void exec(void *data,
target->tin = (target->trgba[0] + target->trgba[1] + target->trgba[2]) / 3.0f;
target->talpha = true;
-
- if (target->nor) {
- if (in[1] && in[1]->hasinput) {
- tex_input_vec(target->nor, in[1], &params, cdata->thread);
- }
- else {
- target->nor = NULL;
- }
- }
}
}
}
diff --git a/source/blender/nodes/texture/nodes/node_texture_proc.c b/source/blender/nodes/texture/nodes/node_texture_proc.c
index fd7e6fdfc7f..d925c9f3554 100644
--- a/source/blender/nodes/texture/nodes/node_texture_proc.c
+++ b/source/blender/nodes/texture/nodes/node_texture_proc.c
@@ -14,10 +14,8 @@
* In this file: wrappers to use procedural textures as nodes
*/
-static bNodeSocketTemplate outputs_both[] = {
- {SOCK_RGBA, N_("Color"), 1.0f, 0.0f, 0.0f, 1.0f},
- {SOCK_VECTOR, N_("Normal"), 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, PROP_DIRECTION},
- {-1, ""}};
+static bNodeSocketTemplate outputs_both[] = {{SOCK_RGBA, N_("Color"), 1.0f, 0.0f, 0.0f, 1.0f},
+ {-1, ""}};
static bNodeSocketTemplate outputs_color_only[] = {{SOCK_RGBA, N_("Color")}, {-1, ""}};
/* Inputs common to all, #defined because nodes will need their own inputs too */
@@ -34,27 +32,15 @@ static void do_proc(float *result,
TexParams *p,
const float col1[4],
const float col2[4],
- char is_normal,
Tex *tex,
const short thread)
{
TexResult texres;
int textype;
- if (is_normal) {
- texres.nor = result;
- }
- else {
- texres.nor = NULL;
- }
-
textype = multitex_nodes(
tex, p->co, p->dxt, p->dyt, p->osatex, &texres, thread, 0, p->mtex, NULL);
- if (is_normal) {
- return;
- }
-
if (textype & TEX_RGB) {
copy_v4_v4(result, texres.trgba);
}
@@ -66,13 +52,8 @@ static void do_proc(float *result,
typedef void (*MapFn)(Tex *tex, bNodeStack **in, TexParams *p, const short thread);
-static void texfn(float *result,
- TexParams *p,
- bNode *node,
- bNodeStack **in,
- char is_normal,
- MapFn map_inputs,
- short thread)
+static void texfn(
+ float *result, TexParams *p, bNode *node, bNodeStack **in, MapFn map_inputs, short thread)
{
Tex tex = *((Tex *)(node->storage));
float col1[4], col2[4];
@@ -81,7 +62,7 @@ static void texfn(float *result,
map_inputs(&tex, in, p, thread);
- do_proc(result, p, col1, col2, is_normal, &tex, thread);
+ do_proc(result, p, col1, col2, &tex, thread);
}
static int count_outputs(bNode *node)
@@ -106,12 +87,7 @@ static int count_outputs(bNode *node)
static void name##_colorfn( \
float *result, TexParams *p, bNode *node, bNodeStack **in, short thread) \
{ \
- texfn(result, p, node, in, 0, &name##_map_inputs, thread); \
- } \
- static void name##_normalfn( \
- float *result, TexParams *p, bNode *node, bNodeStack **in, short thread) \
- { \
- texfn(result, p, node, in, 1, &name##_map_inputs, thread); \
+ texfn(result, p, node, in, &name##_map_inputs, thread); \
} \
static void name##_exec(void *data, \
int UNUSED(thread), \
@@ -124,9 +100,6 @@ static int count_outputs(bNode *node)
if (outs >= 1) { \
tex_output(node, execdata, in, out[0], &name##_colorfn, data); \
} \
- if (outs >= 2) { \
- tex_output(node, execdata, in, out[1], &name##_normalfn, data); \
- } \
}
/* --- VORONOI -- */
diff --git a/source/blender/nodes/texture/nodes/node_texture_texture.c b/source/blender/nodes/texture/nodes/node_texture_texture.c
index 2d2b4e06665..79cd8bbb1df 100644
--- a/source/blender/nodes/texture/nodes/node_texture_texture.c
+++ b/source/blender/nodes/texture/nodes/node_texture_texture.c
@@ -45,13 +45,11 @@ static void colorfn(float *out, TexParams *p, bNode *node, bNodeStack **in, shor
else if (nodetex) {
TexResult texres;
int textype;
- float nor[] = {0, 0, 0};
float col1[4], col2[4];
tex_input_rgba(col1, in[0], p, thread);
tex_input_rgba(col2, in[1], p, thread);
- texres.nor = nor;
textype = multitex_nodes(nodetex, co, dxt, dyt, p->osatex, &texres, thread, 0, p->mtex, NULL);
if (textype & TEX_RGB) {