diff options
Diffstat (limited to 'source/blender/nodes')
467 files changed, 35300 insertions, 13272 deletions
diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 8eaf88a6f1e..402a71af27f 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -18,6 +18,12 @@ # All rights reserved. # ***** END GPL LICENSE BLOCK ***** +add_subdirectory(composite) +add_subdirectory(function) +add_subdirectory(geometry) +add_subdirectory(shader) +add_subdirectory(texture) + set(INC . composite @@ -33,347 +39,26 @@ set(INC ../bmesh ../depsgraph ../functions + ../geometry ../gpu ../imbuf ../makesdna ../makesrna ../render + ../windowmanager ../../../intern/glew-mx ../../../intern/guardedalloc - ../../../intern/sky/include + + # dna_type_offsets.h + ${CMAKE_CURRENT_BINARY_DIR}/../makesdna/intern ) set(SRC - composite/nodes/node_composite_alphaOver.cc - composite/nodes/node_composite_antialiasing.cc - composite/nodes/node_composite_bilateralblur.cc - composite/nodes/node_composite_blur.cc - composite/nodes/node_composite_bokehblur.cc - composite/nodes/node_composite_bokehimage.cc - composite/nodes/node_composite_boxmask.cc - composite/nodes/node_composite_brightness.cc - composite/nodes/node_composite_channelMatte.cc - composite/nodes/node_composite_chromaMatte.cc - composite/nodes/node_composite_colorMatte.cc - composite/nodes/node_composite_colorSpill.cc - composite/nodes/node_composite_colorbalance.cc - composite/nodes/node_composite_colorcorrection.cc - composite/nodes/node_composite_common.cc - composite/nodes/node_composite_composite.cc - composite/nodes/node_composite_cornerpin.cc - composite/nodes/node_composite_crop.cc - composite/nodes/node_composite_cryptomatte.cc - composite/nodes/node_composite_curves.cc - composite/nodes/node_composite_defocus.cc - composite/nodes/node_composite_denoise.cc - composite/nodes/node_composite_despeckle.cc - composite/nodes/node_composite_diffMatte.cc - composite/nodes/node_composite_dilate.cc - composite/nodes/node_composite_directionalblur.cc - composite/nodes/node_composite_displace.cc - composite/nodes/node_composite_distanceMatte.cc - composite/nodes/node_composite_doubleEdgeMask.cc - composite/nodes/node_composite_ellipsemask.cc - composite/nodes/node_composite_exposure.cc - composite/nodes/node_composite_filter.cc - composite/nodes/node_composite_flip.cc - composite/nodes/node_composite_gamma.cc - composite/nodes/node_composite_glare.cc - composite/nodes/node_composite_hueSatVal.cc - composite/nodes/node_composite_huecorrect.cc - composite/nodes/node_composite_idMask.cc - composite/nodes/node_composite_image.cc - composite/nodes/node_composite_inpaint.cc - composite/nodes/node_composite_invert.cc - composite/nodes/node_composite_keying.cc - composite/nodes/node_composite_keyingscreen.cc - composite/nodes/node_composite_lensdist.cc - composite/nodes/node_composite_levels.cc - composite/nodes/node_composite_lummaMatte.cc - composite/nodes/node_composite_mapRange.cc - composite/nodes/node_composite_mapUV.cc - composite/nodes/node_composite_mapValue.cc - composite/nodes/node_composite_mask.cc - composite/nodes/node_composite_math.cc - composite/nodes/node_composite_mixrgb.cc - composite/nodes/node_composite_movieclip.cc - composite/nodes/node_composite_moviedistortion.cc - composite/nodes/node_composite_normal.cc - composite/nodes/node_composite_normalize.cc - composite/nodes/node_composite_outputFile.cc - composite/nodes/node_composite_pixelate.cc - composite/nodes/node_composite_planetrackdeform.cc - composite/nodes/node_composite_posterize.cc - composite/nodes/node_composite_premulkey.cc - composite/nodes/node_composite_rgb.cc - composite/nodes/node_composite_rotate.cc - composite/nodes/node_composite_scale.cc - composite/nodes/node_composite_sepcombHSVA.cc - composite/nodes/node_composite_sepcombRGBA.cc - composite/nodes/node_composite_sepcombYCCA.cc - composite/nodes/node_composite_sepcombYUVA.cc - composite/nodes/node_composite_setalpha.cc - composite/nodes/node_composite_splitViewer.cc - composite/nodes/node_composite_stabilize2d.cc - composite/nodes/node_composite_sunbeams.cc - composite/nodes/node_composite_switch.cc - composite/nodes/node_composite_switchview.cc - composite/nodes/node_composite_texture.cc - composite/nodes/node_composite_tonemap.cc - composite/nodes/node_composite_trackpos.cc - composite/nodes/node_composite_transform.cc - composite/nodes/node_composite_translate.cc - composite/nodes/node_composite_valToRgb.cc - composite/nodes/node_composite_value.cc - composite/nodes/node_composite_vecBlur.cc - composite/nodes/node_composite_viewer.cc - composite/nodes/node_composite_zcombine.cc - - composite/node_composite_tree.cc - composite/node_composite_util.cc - - function/nodes/legacy/node_fn_random_float.cc - - function/nodes/node_fn_boolean_math.cc - function/nodes/node_fn_float_compare.cc - function/nodes/node_fn_float_to_int.cc - function/nodes/node_fn_input_special_characters.cc - function/nodes/node_fn_input_string.cc - function/nodes/node_fn_input_vector.cc - function/nodes/node_fn_random_value.cc - function/nodes/node_fn_string_length.cc - function/nodes/node_fn_string_substring.cc - function/nodes/node_fn_value_to_string.cc - function/node_function_util.cc - - geometry/nodes/legacy/node_geo_align_rotation_to_vector.cc - geometry/nodes/legacy/node_geo_attribute_clamp.cc - geometry/nodes/legacy/node_geo_attribute_color_ramp.cc - geometry/nodes/legacy/node_geo_attribute_combine_xyz.cc - geometry/nodes/legacy/node_geo_attribute_compare.cc - geometry/nodes/legacy/node_geo_attribute_convert.cc - geometry/nodes/legacy/node_geo_attribute_curve_map.cc - geometry/nodes/legacy/node_geo_attribute_fill.cc - geometry/nodes/legacy/node_geo_attribute_map_range.cc - geometry/nodes/legacy/node_geo_attribute_math.cc - geometry/nodes/legacy/node_geo_attribute_mix.cc - geometry/nodes/legacy/node_geo_attribute_proximity.cc - geometry/nodes/legacy/node_geo_attribute_randomize.cc - geometry/nodes/legacy/node_geo_attribute_sample_texture.cc - geometry/nodes/legacy/node_geo_attribute_separate_xyz.cc - geometry/nodes/legacy/node_geo_attribute_transfer.cc - geometry/nodes/legacy/node_geo_attribute_vector_math.cc - geometry/nodes/legacy/node_geo_attribute_vector_rotate.cc - geometry/nodes/legacy/node_geo_curve_endpoints.cc - geometry/nodes/legacy/node_geo_curve_reverse.cc - geometry/nodes/legacy/node_geo_curve_select_by_handle_type.cc - geometry/nodes/legacy/node_geo_curve_set_handles.cc - geometry/nodes/legacy/node_geo_curve_spline_type.cc - geometry/nodes/legacy/node_geo_curve_subdivide.cc - geometry/nodes/legacy/node_geo_curve_to_points.cc - geometry/nodes/legacy/node_geo_delete_geometry.cc - geometry/nodes/legacy/node_geo_edge_split.cc - geometry/nodes/legacy/node_geo_material_assign.cc - geometry/nodes/legacy/node_geo_mesh_to_curve.cc - geometry/nodes/legacy/node_geo_point_distribute.cc - geometry/nodes/legacy/node_geo_point_instance.cc - geometry/nodes/legacy/node_geo_point_rotate.cc - geometry/nodes/legacy/node_geo_point_scale.cc - geometry/nodes/legacy/node_geo_point_separate.cc - geometry/nodes/legacy/node_geo_point_translate.cc - geometry/nodes/legacy/node_geo_points_to_volume.cc - geometry/nodes/legacy/node_geo_raycast.cc - geometry/nodes/legacy/node_geo_select_by_material.cc - geometry/nodes/legacy/node_geo_subdivision_surface.cc - - geometry/nodes/node_geo_attribute_capture.cc - geometry/nodes/node_geo_attribute_remove.cc - geometry/nodes/node_geo_attribute_statistic.cc - geometry/nodes/node_geo_boolean.cc - geometry/nodes/node_geo_bounding_box.cc - geometry/nodes/node_geo_collection_info.cc - geometry/nodes/node_geo_common.cc - geometry/nodes/node_geo_convex_hull.cc - geometry/nodes/node_geo_curve_fill.cc - geometry/nodes/node_geo_curve_fillet.cc - geometry/nodes/node_geo_curve_length.cc - geometry/nodes/node_geo_curve_parameter.cc - geometry/nodes/node_geo_curve_primitive_bezier_segment.cc - geometry/nodes/node_geo_curve_primitive_circle.cc - geometry/nodes/node_geo_curve_primitive_line.cc - geometry/nodes/node_geo_curve_primitive_quadratic_bezier.cc - geometry/nodes/node_geo_curve_primitive_quadrilateral.cc - geometry/nodes/node_geo_curve_primitive_spiral.cc - geometry/nodes/node_geo_curve_primitive_star.cc - geometry/nodes/node_geo_curve_resample.cc - geometry/nodes/node_geo_curve_reverse.cc - geometry/nodes/node_geo_curve_sample.cc - geometry/nodes/node_geo_curve_to_mesh.cc - geometry/nodes/node_geo_curve_trim.cc - geometry/nodes/node_geo_distribute_points_on_faces.cc - geometry/nodes/node_geo_input_index.cc - geometry/nodes/node_geo_input_material.cc - geometry/nodes/node_geo_input_normal.cc - geometry/nodes/node_geo_input_position.cc - geometry/nodes/node_geo_input_tangent.cc - geometry/nodes/node_geo_instance_on_points.cc - geometry/nodes/node_geo_is_viewport.cc - geometry/nodes/node_geo_join_geometry.cc - geometry/nodes/node_geo_material_assign.cc - geometry/nodes/node_geo_material_replace.cc - geometry/nodes/node_geo_material_selection.cc - geometry/nodes/node_geo_mesh_primitive_circle.cc - geometry/nodes/node_geo_mesh_primitive_cone.cc - geometry/nodes/node_geo_mesh_primitive_cube.cc - geometry/nodes/node_geo_mesh_primitive_cylinder.cc - geometry/nodes/node_geo_mesh_primitive_grid.cc - geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc - geometry/nodes/node_geo_mesh_primitive_line.cc - geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc - geometry/nodes/node_geo_mesh_subdivide.cc - geometry/nodes/node_geo_mesh_to_points.cc - geometry/nodes/node_geo_object_info.cc - geometry/nodes/node_geo_points_to_vertices.cc - geometry/nodes/node_geo_proximity.cc - geometry/nodes/node_geo_realize_instances.cc - geometry/nodes/node_geo_separate_components.cc - geometry/nodes/node_geo_set_position.cc - geometry/nodes/node_geo_string_join.cc - geometry/nodes/node_geo_string_to_curves.cc - geometry/nodes/node_geo_switch.cc - geometry/nodes/node_geo_transform.cc - geometry/nodes/node_geo_triangulate.cc - geometry/nodes/node_geo_viewer.cc - geometry/nodes/node_geo_volume_to_mesh.cc - - geometry/node_geometry_exec.cc - geometry/node_geometry_tree.cc - geometry/node_geometry_util.cc - - shader/nodes/node_shader_add_shader.c - shader/nodes/node_shader_ambient_occlusion.c - shader/nodes/node_shader_attribute.c - shader/nodes/node_shader_background.c - shader/nodes/node_shader_bevel.c - shader/nodes/node_shader_blackbody.c - shader/nodes/node_shader_brightness.c - shader/nodes/node_shader_bsdf_anisotropic.c - shader/nodes/node_shader_bsdf_diffuse.c - shader/nodes/node_shader_bsdf_glass.c - shader/nodes/node_shader_bsdf_glossy.c - shader/nodes/node_shader_bsdf_hair.c - shader/nodes/node_shader_bsdf_hair_principled.c - shader/nodes/node_shader_bsdf_principled.c - shader/nodes/node_shader_bsdf_refraction.c - shader/nodes/node_shader_bsdf_toon.c - shader/nodes/node_shader_bsdf_translucent.c - shader/nodes/node_shader_bsdf_transparent.c - shader/nodes/node_shader_bsdf_velvet.c - shader/nodes/node_shader_bump.c - shader/nodes/node_shader_camera.c - shader/nodes/node_shader_clamp.cc - shader/nodes/node_shader_common.c - shader/nodes/node_shader_curves.cc - shader/nodes/node_shader_displacement.c - shader/nodes/node_shader_eevee_specular.c - shader/nodes/node_shader_emission.c - shader/nodes/node_shader_fresnel.c - shader/nodes/node_shader_gamma.c - shader/nodes/node_shader_geometry.c - shader/nodes/node_shader_hair_info.c - shader/nodes/node_shader_holdout.c - shader/nodes/node_shader_hueSatVal.c - shader/nodes/node_shader_ies_light.c - shader/nodes/node_shader_invert.c - shader/nodes/node_shader_layer_weight.c - shader/nodes/node_shader_light_falloff.c - shader/nodes/node_shader_light_path.c - shader/nodes/node_shader_map_range.cc - shader/nodes/node_shader_mapping.c - shader/nodes/node_shader_math.cc - shader/nodes/node_shader_mixRgb.cc - shader/nodes/node_shader_mix_shader.c - shader/nodes/node_shader_normal.c - shader/nodes/node_shader_normal_map.c - shader/nodes/node_shader_object_info.c - shader/nodes/node_shader_output_aov.c - shader/nodes/node_shader_output_light.c - shader/nodes/node_shader_output_linestyle.c - shader/nodes/node_shader_output_material.c - shader/nodes/node_shader_output_world.c - shader/nodes/node_shader_particle_info.c - shader/nodes/node_shader_rgb.c - shader/nodes/node_shader_script.c - shader/nodes/node_shader_sepcombHSV.c - shader/nodes/node_shader_sepcombRGB.cc - shader/nodes/node_shader_sepcombXYZ.cc - shader/nodes/node_shader_shaderToRgb.c - shader/nodes/node_shader_squeeze.c - shader/nodes/node_shader_subsurface_scattering.c - shader/nodes/node_shader_tangent.c - shader/nodes/node_shader_tex_brick.c - shader/nodes/node_shader_tex_checker.c - shader/nodes/node_shader_tex_coord.c - shader/nodes/node_shader_tex_environment.c - shader/nodes/node_shader_tex_gradient.cc - shader/nodes/node_shader_tex_image.c - shader/nodes/node_shader_tex_magic.c - shader/nodes/node_shader_tex_musgrave.cc - shader/nodes/node_shader_tex_noise.cc - shader/nodes/node_shader_tex_pointdensity.c - shader/nodes/node_shader_tex_sky.c - shader/nodes/node_shader_tex_voronoi.cc - shader/nodes/node_shader_tex_wave.c - shader/nodes/node_shader_tex_white_noise.cc - shader/nodes/node_shader_uvAlongStroke.c - shader/nodes/node_shader_uvmap.c - shader/nodes/node_shader_valToRgb.cc - shader/nodes/node_shader_value.cc - shader/nodes/node_shader_vectTransform.c - shader/nodes/node_shader_vector_displacement.c - shader/nodes/node_shader_vector_math.cc - shader/nodes/node_shader_vector_rotate.cc - shader/nodes/node_shader_vertex_color.c - shader/nodes/node_shader_volume_absorption.c - shader/nodes/node_shader_volume_info.c - shader/nodes/node_shader_volume_principled.c - shader/nodes/node_shader_volume_scatter.c - shader/nodes/node_shader_wavelength.c - shader/nodes/node_shader_wireframe.c - shader/node_shader_tree.c - shader/node_shader_util.c - - texture/nodes/node_texture_at.c - texture/nodes/node_texture_bricks.c - texture/nodes/node_texture_checker.c - texture/nodes/node_texture_common.c - texture/nodes/node_texture_compose.c - texture/nodes/node_texture_coord.c - texture/nodes/node_texture_curves.c - texture/nodes/node_texture_decompose.c - texture/nodes/node_texture_distance.c - texture/nodes/node_texture_hueSatVal.c - texture/nodes/node_texture_image.c - texture/nodes/node_texture_invert.c - texture/nodes/node_texture_math.c - texture/nodes/node_texture_mixRgb.c - texture/nodes/node_texture_output.c - texture/nodes/node_texture_proc.c - texture/nodes/node_texture_rotate.c - texture/nodes/node_texture_scale.c - texture/nodes/node_texture_texture.c - texture/nodes/node_texture_translate.c - texture/nodes/node_texture_valToNor.c - texture/nodes/node_texture_valToRgb.c - texture/nodes/node_texture_viewer.c - texture/node_texture_tree.c - texture/node_texture_util.c - intern/derived_node_tree.cc intern/geometry_nodes_eval_log.cc intern/math_functions.cc - intern/node_common.c + intern/node_common.cc intern/node_declaration.cc intern/node_exec.cc intern/node_geometry_exec.cc @@ -382,13 +67,7 @@ set(SRC intern/node_socket_declarations.cc intern/node_tree_ref.cc intern/node_util.c - intern/type_conversions.cc - - composite/node_composite_util.hh - function/node_function_util.hh - shader/node_shader_util.h - geometry/node_geometry_util.hh - texture/node_texture_util.h + intern/socket_search_link.cc NOD_common.h NOD_composite.h @@ -404,9 +83,10 @@ set(SRC NOD_shader.h NOD_socket.h NOD_socket_declarations.hh + NOD_socket_declarations_geometry.hh + NOD_socket_search_link.hh NOD_static_types.h NOD_texture.h - NOD_type_conversions.hh intern/node_common.h intern/node_exec.h intern/node_util.h @@ -415,18 +95,22 @@ set(SRC set(LIB bf_bmesh bf_functions - bf_intern_sky + bf_nodes_composite + bf_nodes_function + bf_nodes_geometry + bf_nodes_shader + bf_nodes_texture ) if(WITH_BULLET) list(APPEND INC_SYS ${BULLET_INCLUDE_DIRS} - "../../../intern/rigidbody/" + ../../../intern/rigidbody ) if(NOT WITH_SYSTEM_BULLET) list(APPEND LIB extern_bullet - ) + ) endif() list(APPEND LIB @@ -469,13 +153,6 @@ if(WITH_IMAGE_OPENEXR) add_definitions(-DWITH_OPENEXR) endif() -if(WITH_COMPOSITOR) - list(APPEND INC - ../compositor - ) - add_definitions(-DWITH_COMPOSITOR) -endif() - if(WITH_FREESTYLE) add_definitions(-DWITH_FREESTYLE) endif() @@ -504,3 +181,6 @@ if(WITH_OPENVDB) endif() blender_add_lib(bf_nodes "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") + +# Needed so we can use dna_type_offsets.h for defaults initialization. +add_dependencies(bf_nodes bf_dna) diff --git a/source/blender/nodes/NOD_common.h b/source/blender/nodes/NOD_common.h index 50ed992dcb6..e488352170b 100644 --- a/source/blender/nodes/NOD_common.h +++ b/source/blender/nodes/NOD_common.h @@ -35,9 +35,11 @@ void register_node_type_reroute(void); void register_node_type_group_input(void); void register_node_type_group_output(void); -/* internal functions for editor */ +/* Internal functions for editor. */ + struct bNodeSocket *node_group_find_input_socket(struct bNode *groupnode, const char *identifier); struct bNodeSocket *node_group_find_output_socket(struct bNode *groupnode, const char *identifier); +/** Make sure all group node in ntree, which use ngroup, are sync'd. */ void node_group_update(struct bNodeTree *ntree, struct bNode *node); struct bNodeSocket *node_group_input_find_socket(struct bNode *node, const char *identifier); @@ -45,6 +47,8 @@ struct bNodeSocket *node_group_output_find_socket(struct bNode *node, const char void node_group_input_update(struct bNodeTree *ntree, struct bNode *node); void node_group_output_update(struct bNodeTree *ntree, struct bNode *node); +void node_internal_links_create(struct bNodeTree *ntree, struct bNode *node); + #ifdef __cplusplus } #endif diff --git a/source/blender/nodes/NOD_composite.h b/source/blender/nodes/NOD_composite.h index d243577f68d..82faccc2c2d 100644 --- a/source/blender/nodes/NOD_composite.h +++ b/source/blender/nodes/NOD_composite.h @@ -43,6 +43,7 @@ void register_node_type_cmp_texture(void); void register_node_type_cmp_value(void); void register_node_type_cmp_rgb(void); void register_node_type_cmp_curve_time(void); +void register_node_type_cmp_scene_time(void); void register_node_type_cmp_movieclip(void); void register_node_type_cmp_composite(void); @@ -62,6 +63,7 @@ void register_node_type_cmp_alphaover(void); void register_node_type_cmp_zcombine(void); void register_node_type_cmp_colorbalance(void); void register_node_type_cmp_huecorrect(void); +void register_node_type_cmp_convert_color_space(void); void register_node_type_cmp_normal(void); void register_node_type_cmp_curve_vec(void); @@ -150,6 +152,75 @@ const char *node_cmp_rlayers_sock_to_pass(int sock_index); void register_node_type_cmp_custom_group(bNodeType *ntype); +void ntreeCompositExecTree(struct Scene *scene, + struct bNodeTree *ntree, + struct RenderData *rd, + int rendering, + int do_previews, + const struct ColorManagedViewSettings *view_settings, + const struct ColorManagedDisplaySettings *display_settings, + const char *view_name); + +/** + * Called from render pipeline, to tag render input and output. + * need to do all scenes, to prevent errors when you re-render 1 scene. + */ +void ntreeCompositTagRender(struct Scene *scene); + +/** + * Update the outputs of the render layer nodes. + * Since the outputs depend on the render engine, this part is a bit complex: + * - #ntreeCompositUpdateRLayers is called and loops over all render layer nodes. + * - Each render layer node calls the update function of the + * render engine that's used for its scene. + * - The render engine calls RE_engine_register_pass for each pass. + * - #RE_engine_register_pass calls #node_cmp_rlayers_register_pass. + */ +void ntreeCompositUpdateRLayers(struct bNodeTree *ntree); + +void ntreeCompositClearTags(struct bNodeTree *ntree); + +struct bNodeSocket *ntreeCompositOutputFileAddSocket(struct bNodeTree *ntree, + struct bNode *node, + const char *name, + struct ImageFormatData *im_format); + +int ntreeCompositOutputFileRemoveActiveSocket(struct bNodeTree *ntree, struct bNode *node); +void ntreeCompositOutputFileSetPath(struct bNode *node, + struct bNodeSocket *sock, + const char *name); +void ntreeCompositOutputFileSetLayer(struct bNode *node, + struct bNodeSocket *sock, + const char *name); +/* needed in do_versions */ +void ntreeCompositOutputFileUniquePath(struct ListBase *list, + struct bNodeSocket *sock, + const char defname[], + char delim); +void ntreeCompositOutputFileUniqueLayer(struct ListBase *list, + struct bNodeSocket *sock, + const char defname[], + char delim); + +void ntreeCompositColorBalanceSyncFromLGG(bNodeTree *ntree, bNode *node); +void ntreeCompositColorBalanceSyncFromCDL(bNodeTree *ntree, bNode *node); + +void ntreeCompositCryptomatteSyncFromAdd(const Scene *scene, bNode *node); +void ntreeCompositCryptomatteSyncFromRemove(bNode *node); +bNodeSocket *ntreeCompositCryptomatteAddSocket(bNodeTree *ntree, bNode *node); +int ntreeCompositCryptomatteRemoveSocket(bNodeTree *ntree, bNode *node); +void ntreeCompositCryptomatteLayerPrefix(const Scene *scene, + const bNode *node, + char *r_prefix, + size_t prefix_len); + +/** + * Update the runtime layer names with the crypto-matte layer names of the references render layer + * or image. + */ +void ntreeCompositCryptomatteUpdateLayerNames(const Scene *scene, bNode *node); +struct CryptomatteSession *ntreeCompositCryptomatteSession(const Scene *scene, bNode *node); + #ifdef __cplusplus } #endif diff --git a/source/blender/nodes/NOD_derived_node_tree.hh b/source/blender/nodes/NOD_derived_node_tree.hh index 60d84463a82..dc619deb5d2 100644 --- a/source/blender/nodes/NOD_derived_node_tree.hh +++ b/source/blender/nodes/NOD_derived_node_tree.hh @@ -72,8 +72,10 @@ class DTreeContext { bool is_root() const; }; -/* A (nullable) reference to a node and the context it is in. It is unique within an entire nested - * node group hierarchy. This type is small and can be passed around by value. */ +/** + * A (nullable) reference to a node and the context it is in. It is unique within an entire nested + * node group hierarchy. This type is small and can be passed around by value. + */ class DNode { private: const DTreeContext *context_ = nullptr; @@ -100,11 +102,13 @@ class DNode { DOutputSocket output_by_identifier(StringRef identifier) const; }; -/* A (nullable) reference to a socket and the context it is in. It is unique within an entire +/** + * A (nullable) reference to a socket and the context it is in. It is unique within an entire * nested node group hierarchy. This type is small and can be passed around by value. * * A #DSocket can represent an input or an output socket. If the type of a socket is known at - * compile time is preferable to use #DInputSocket or #DOutputSocket instead. */ + * compile time is preferable to use #DInputSocket or #DOutputSocket instead. + */ class DSocket { protected: const DTreeContext *context_ = nullptr; @@ -129,7 +133,7 @@ class DSocket { DNode node() const; }; -/* A (nullable) reference to an input socket and the context it is in. */ +/** A (nullable) reference to an input socket and the context it is in. */ class DInputSocket : public DSocket { public: DInputSocket() = default; @@ -142,10 +146,15 @@ class DInputSocket : public DSocket { DOutputSocket get_corresponding_group_node_output() const; Vector<DOutputSocket, 4> get_corresponding_group_input_sockets() const; + /** + * Call `origin_fn` for every "real" origin socket. "Real" means that reroutes, muted nodes + * and node groups are handled by this function. Origin sockets are ones where a node gets its + * inputs from. + */ void foreach_origin_socket(FunctionRef<void(DSocket)> origin_fn) const; }; -/* A (nullable) reference to an output socket and the context it is in. */ +/** A (nullable) reference to an output socket and the context it is in. */ class DOutputSocket : public DSocket { public: DOutputSocket() = default; @@ -158,8 +167,24 @@ class DOutputSocket : public DSocket { DInputSocket get_corresponding_group_node_input() const; DInputSocket get_active_corresponding_group_output_socket() const; - void foreach_target_socket(FunctionRef<void(DInputSocket)> target_fn, - FunctionRef<void(DSocket)> skipped_fn) const; + struct TargetSocketPathInfo { + /** All sockets on the path from the current to the final target sockets, excluding `this`. */ + Vector<DSocket, 16> sockets; + }; + + using ForeachTargetSocketFn = + FunctionRef<void(DInputSocket, const TargetSocketPathInfo &path_info)>; + + /** + * Calls `target_fn` for every "real" target socket. "Real" means that reroutes, muted nodes + * and node groups are handled by this function. Target sockets are on the nodes that use the + * value from this socket. + */ + void foreach_target_socket(ForeachTargetSocketFn target_fn) const; + + private: + void foreach_target_socket(ForeachTargetSocketFn target_fn, + TargetSocketPathInfo &path_info) const; }; class DerivedNodeTree { @@ -169,16 +194,27 @@ class DerivedNodeTree { VectorSet<const NodeTreeRef *> used_node_tree_refs_; public: + /** + * Construct a new derived node tree for a given root node tree. The generated derived node tree + * does not own the used node tree refs (so that those can be used by others as well). The caller + * 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 DTreeContext &root_context() const; Span<const NodeTreeRef *> used_node_tree_refs() 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; + /** Calls the given callback on all nodes in the (possibly nested) derived node tree. */ void foreach_node(FunctionRef<void(DNode)> callback) const; + /** Generates a graph in dot format. The generated graph has all node groups inlined. */ std::string to_dot() const; private: @@ -202,9 +238,9 @@ using nodes::DSocket; using nodes::DTreeContext; } // namespace derived_node_tree_types -/* -------------------------------------------------------------------- - * DTreeContext inline methods. - */ +/* -------------------------------------------------------------------- */ +/** \name #DTreeContext Inline Methods + * \{ */ inline const NodeTreeRef &DTreeContext::tree() const { @@ -236,9 +272,11 @@ inline bool DTreeContext::is_root() const return parent_context_ == nullptr; } -/* -------------------------------------------------------------------- - * DNode inline methods. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #DNode Inline Methods + * \{ */ inline DNode::DNode(const DTreeContext *context, const NodeRef *node_ref) : context_(context), node_ref_(node_ref) @@ -301,9 +339,11 @@ inline DOutputSocket DNode::output_by_identifier(StringRef identifier) const return {context_, &node_ref_->output_by_identifier(identifier)}; } -/* -------------------------------------------------------------------- - * DSocket inline methods. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #DSocket Inline Methods + * \{ */ inline DSocket::DSocket(const DTreeContext *context, const SocketRef *socket_ref) : context_(context), socket_ref_(socket_ref) @@ -362,9 +402,11 @@ inline DNode DSocket::node() const return {context_, &socket_ref_->node()}; } -/* -------------------------------------------------------------------- - * DInputSocket inline methods. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #DInputSocket Inline Methods + * \{ */ inline DInputSocket::DInputSocket(const DTreeContext *context, const InputSocketRef *socket_ref) : DSocket(context, socket_ref) @@ -386,9 +428,11 @@ inline const InputSocketRef *DInputSocket::operator->() const return (const InputSocketRef *)socket_ref_; } -/* -------------------------------------------------------------------- - * DOutputSocket inline methods. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #DOutputSocket Inline Methods + * \{ */ inline DOutputSocket::DOutputSocket(const DTreeContext *context, const OutputSocketRef *socket_ref) : DSocket(context, socket_ref) @@ -410,9 +454,11 @@ inline const OutputSocketRef *DOutputSocket::operator->() const return (const OutputSocketRef *)socket_ref_; } -/* -------------------------------------------------------------------- - * DerivedNodeTree inline methods. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #DerivedNodeTree Inline Methods + * \{ */ inline const DTreeContext &DerivedNodeTree::root_context() const { @@ -424,4 +470,6 @@ inline Span<const NodeTreeRef *> DerivedNodeTree::used_node_tree_refs() const return used_node_tree_refs_; } +/** \} */ + } // namespace blender::nodes diff --git a/source/blender/nodes/NOD_function.h b/source/blender/nodes/NOD_function.h index 9aa4c04000e..be3998a916c 100644 --- a/source/blender/nodes/NOD_function.h +++ b/source/blender/nodes/NOD_function.h @@ -22,15 +22,21 @@ extern "C" { void register_node_type_fn_legacy_random_float(void); +void register_node_type_fn_align_euler_to_vector(void); void register_node_type_fn_boolean_math(void); -void register_node_type_fn_float_compare(void); +void register_node_type_fn_compare(void); void register_node_type_fn_float_to_int(void); +void register_node_type_fn_input_bool(void); +void register_node_type_fn_input_color(void); +void register_node_type_fn_input_int(void); void register_node_type_fn_input_special_characters(void); void register_node_type_fn_input_string(void); void register_node_type_fn_input_vector(void); void register_node_type_fn_random_value(void); +void register_node_type_fn_replace_string(void); +void register_node_type_fn_rotate_euler(void); +void register_node_type_fn_slice_string(void); void register_node_type_fn_string_length(void); -void register_node_type_fn_string_substring(void); void register_node_type_fn_value_to_string(void); #ifdef __cplusplus diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index af8661d9b8d..609d92c09df 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -16,12 +16,12 @@ #pragma once +#include "BKE_node.h" + #ifdef __cplusplus extern "C" { #endif -#include "BKE_node.h" - extern struct bNodeTreeType *ntreeType_Geometry; void register_node_tree_type_geo(void); @@ -31,10 +31,25 @@ void register_node_type_geo_custom_group(bNodeType *ntype); void register_node_type_geo_legacy_attribute_proximity(void); void register_node_type_geo_legacy_attribute_randomize(void); +void register_node_type_geo_legacy_attribute_transfer(void); +void register_node_type_geo_legacy_curve_endpoints(void); +void register_node_type_geo_legacy_curve_reverse(void); +void register_node_type_geo_legacy_curve_set_handles(void); +void register_node_type_geo_legacy_curve_spline_type(void); +void register_node_type_geo_legacy_curve_subdivide(void); +void register_node_type_geo_legacy_curve_to_points(void); +void register_node_type_geo_legacy_delete_geometry(void); +void register_node_type_geo_legacy_edge_split(void); void register_node_type_geo_legacy_material_assign(void); +void register_node_type_geo_legacy_mesh_to_curve(void); +void register_node_type_geo_legacy_points_to_volume(void); +void register_node_type_geo_legacy_raycast(void); +void register_node_type_geo_legacy_select_by_handle_type(void); void register_node_type_geo_legacy_select_by_material(void); -void register_node_type_geo_legacy_curve_reverse(void); +void register_node_type_geo_legacy_subdivision_surface(void); +void register_node_type_geo_legacy_volume_to_mesh(void); +void register_node_type_geo_accumulate_field(void); void register_node_type_geo_align_rotation_to_vector(void); void register_node_type_geo_attribute_capture(void); void register_node_type_geo_attribute_clamp(void); @@ -43,6 +58,7 @@ void register_node_type_geo_attribute_combine_xyz(void); void register_node_type_geo_attribute_compare(void); void register_node_type_geo_attribute_convert(void); void register_node_type_geo_attribute_curve_map(void); +void register_node_type_geo_attribute_domain_size(void); void register_node_type_geo_attribute_fill(void); void register_node_type_geo_attribute_map_range(void); void register_node_type_geo_attribute_math(void); @@ -50,18 +66,18 @@ void register_node_type_geo_attribute_mix(void); void register_node_type_geo_attribute_remove(void); void register_node_type_geo_attribute_separate_xyz(void); void register_node_type_geo_attribute_statistic(void); -void register_node_type_geo_attribute_transfer(void); void register_node_type_geo_attribute_vector_math(void); void register_node_type_geo_attribute_vector_rotate(void); void register_node_type_geo_boolean(void); void register_node_type_geo_bounding_box(void); void register_node_type_geo_collection_info(void); void register_node_type_geo_convex_hull(void); -void register_node_type_geo_curve_endpoints(void); +void register_node_type_geo_curve_endpoint_selection(void); void register_node_type_geo_curve_fill(void); void register_node_type_geo_curve_fillet(void); +void register_node_type_geo_curve_handle_type_selection(void); void register_node_type_geo_curve_length(void); -void register_node_type_geo_curve_parameter(void); +void register_node_type_geo_curve_primitive_arc(void); void register_node_type_geo_curve_primitive_bezier_segment(void); void register_node_type_geo_curve_primitive_circle(void); void register_node_type_geo_curve_primitive_line(void); @@ -73,6 +89,7 @@ void register_node_type_geo_curve_resample(void); void register_node_type_geo_curve_reverse(void); void register_node_type_geo_curve_sample(void); void register_node_type_geo_curve_set_handles(void); +void register_node_type_geo_curve_spline_parameter(void); void register_node_type_geo_curve_spline_type(void); void register_node_type_geo_curve_subdivide(void); void register_node_type_geo_curve_to_mesh(void); @@ -80,18 +97,42 @@ void register_node_type_geo_curve_to_points(void); void register_node_type_geo_curve_trim(void); void register_node_type_geo_delete_geometry(void); void register_node_type_geo_distribute_points_on_faces(void); +void register_node_type_geo_dual_mesh(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_curve_handles(void); +void register_node_type_geo_input_curve_tilt(void); +void register_node_type_geo_input_id(void); void register_node_type_geo_input_index(void); +void register_node_type_geo_input_material_index(void); void register_node_type_geo_input_material(void); +void register_node_type_geo_input_mesh_edge_angle(void); +void register_node_type_geo_input_mesh_edge_neighbors(void); +void register_node_type_geo_input_mesh_edge_vertices(void); +void register_node_type_geo_input_mesh_face_area(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_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_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_is_viewport(void); void register_node_type_geo_join_geometry(void); -void register_node_type_geo_material_assign(void); void register_node_type_geo_material_replace(void); void register_node_type_geo_material_selection(void); +void register_node_type_geo_merge_by_distance(void); void register_node_type_geo_mesh_primitive_circle(void); void register_node_type_geo_mesh_primitive_cone(void); void register_node_type_geo_mesh_primitive_cube(void); @@ -115,15 +156,31 @@ void register_node_type_geo_points_to_volume(void); void register_node_type_geo_proximity(void); void register_node_type_geo_raycast(void); void register_node_type_geo_realize_instances(void); +void register_node_type_geo_rotate_instances(void); void register_node_type_geo_sample_texture(void); +void register_node_type_geo_scale_elements(void); +void register_node_type_geo_scale_instances(void); void register_node_type_geo_select_by_handle_type(void); void register_node_type_geo_separate_components(void); +void register_node_type_geo_separate_geometry(void); +void register_node_type_geo_set_curve_handles(void); +void register_node_type_geo_set_curve_radius(void); +void register_node_type_geo_set_curve_tilt(void); +void register_node_type_geo_set_id(void); +void register_node_type_geo_set_material_index(void); +void register_node_type_geo_set_material(void); +void register_node_type_geo_set_point_radius(void); void register_node_type_geo_set_position(void); +void register_node_type_geo_set_shade_smooth(void); +void register_node_type_geo_set_spline_cyclic(void); +void register_node_type_geo_set_spline_resolution(void); void register_node_type_geo_string_join(void); void register_node_type_geo_string_to_curves(void); void register_node_type_geo_subdivision_surface(void); void register_node_type_geo_switch(void); +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_viewer(void); void register_node_type_geo_volume_to_mesh(void); diff --git a/source/blender/nodes/NOD_geometry_exec.hh b/source/blender/nodes/NOD_geometry_exec.hh index 962e1c3c48f..7060760237a 100644 --- a/source/blender/nodes/NOD_geometry_exec.hh +++ b/source/blender/nodes/NOD_geometry_exec.hh @@ -28,6 +28,8 @@ #include "NOD_derived_node_tree.hh" #include "NOD_geometry_nodes_eval_log.hh" +#include "GEO_realize_instances.hh" + struct Depsgraph; struct ModifierData; @@ -36,8 +38,8 @@ namespace blender::nodes { using bke::AnonymousAttributeFieldInput; using bke::AttributeFieldInput; using bke::AttributeIDRef; -using bke::geometry_set_realize_instances; using bke::GeometryComponentFieldContext; +using bke::GeometryFieldInput; using bke::OutputAttribute; using bke::OutputAttribute_Typed; using bke::ReadAttributeLookup; @@ -57,13 +59,9 @@ using fn::GPointer; using fn::GSpan; using fn::GVArray; using fn::GVArray_GSpan; -using fn::GVArray_Span; -using fn::GVArray_Typed; -using fn::GVArrayPtr; using fn::GVMutableArray; using fn::GVMutableArray_GSpan; -using fn::GVMutableArray_Typed; -using fn::GVMutableArrayPtr; +using fn::ValueOrField; using geometry_nodes_eval_log::NodeWarningType; /** @@ -122,6 +120,8 @@ class GeoNodeExecParamsProvider { virtual bool output_is_required(StringRef identifier) const = 0; virtual bool lazy_require_input(StringRef identifier) = 0; virtual bool lazy_output_is_required(StringRef identifier) const = 0; + + virtual void set_default_remaining_outputs() = 0; }; class GeoNodeExecParams { @@ -134,12 +134,8 @@ class GeoNodeExecParams { } template<typename T> - static inline constexpr bool is_stored_as_field_v = std::is_same_v<T, float> || - std::is_same_v<T, int> || - std::is_same_v<T, bool> || - std::is_same_v<T, ColorGeometry4f> || - std::is_same_v<T, float3> || - std::is_same_v<T, std::string>; + static inline constexpr bool is_field_base_type_v = + is_same_any_v<T, float, int, bool, ColorGeometry4f, float3, std::string>; /** * Get the input value for the input socket with the given identifier. @@ -162,19 +158,31 @@ class GeoNodeExecParams { */ template<typename T> T extract_input(StringRef identifier) { - if constexpr (is_stored_as_field_v<T>) { - Field<T> field = this->extract_input<Field<T>>(identifier); - return fn::evaluate_constant_field(field); + if constexpr (is_field_base_type_v<T>) { + ValueOrField<T> value_or_field = this->extract_input<ValueOrField<T>>(identifier); + return value_or_field.as_value(); + } + else if constexpr (fn::is_field_v<T>) { + using BaseType = typename T::base_type; + ValueOrField<BaseType> value_or_field = this->extract_input<ValueOrField<BaseType>>( + identifier); + return value_or_field.as_field(); } else { #ifdef DEBUG this->check_input_access(identifier, &CPPType::get<T>()); #endif GMutablePointer gvalue = this->extract_input(identifier); - return gvalue.relocate_out<T>(); + T value = gvalue.relocate_out<T>(); + if constexpr (std::is_same_v<T, GeometrySet>) { + this->check_input_geometry_set(identifier, value); + } + return value; } } + void check_input_geometry_set(StringRef identifier, const GeometrySet &geometry_set) const; + /** * Get input as vector for multi input socket with the given identifier. * @@ -185,9 +193,9 @@ class GeoNodeExecParams { Vector<GMutablePointer> gvalues = provider_->extract_multi_input(identifier); Vector<T> values; for (GMutablePointer gvalue : gvalues) { - if constexpr (is_stored_as_field_v<T>) { - const Field<T> field = gvalue.relocate_out<Field<T>>(); - values.append(fn::evaluate_constant_field(field)); + if constexpr (is_field_base_type_v<T>) { + const ValueOrField<T> value_or_field = gvalue.relocate_out<ValueOrField<T>>(); + values.append(value_or_field.as_value()); } else { values.append(gvalue.relocate_out<T>()); @@ -199,11 +207,16 @@ class GeoNodeExecParams { /** * Get the input value for the input socket with the given identifier. */ - template<typename T> const T get_input(StringRef identifier) const + template<typename T> T get_input(StringRef identifier) const { - if constexpr (is_stored_as_field_v<T>) { - const Field<T> &field = this->get_input<Field<T>>(identifier); - return fn::evaluate_constant_field(field); + if constexpr (is_field_base_type_v<T>) { + ValueOrField<T> value_or_field = this->get_input<ValueOrField<T>>(identifier); + return value_or_field.as_value(); + } + else if constexpr (fn::is_field_v<T>) { + using BaseType = typename T::base_type; + ValueOrField<BaseType> value_or_field = this->get_input<ValueOrField<BaseType>>(identifier); + return value_or_field.as_field(); } else { #ifdef DEBUG @@ -211,7 +224,11 @@ class GeoNodeExecParams { #endif GPointer gvalue = provider_->get_input(identifier); BLI_assert(gvalue.is_type<T>()); - return *(const T *)gvalue.get(); + const T &value = *(const T *)gvalue.get(); + if constexpr (std::is_same_v<T, GeometrySet>) { + this->check_input_geometry_set(identifier, value); + } + return value; } } @@ -221,9 +238,12 @@ class GeoNodeExecParams { template<typename T> void set_output(StringRef identifier, T &&value) { using StoredT = std::decay_t<T>; - if constexpr (is_stored_as_field_v<StoredT>) { - this->set_output<Field<StoredT>>(identifier, - fn::make_constant_field<StoredT>(std::forward<T>(value))); + if constexpr (is_field_base_type_v<StoredT>) { + this->set_output(identifier, ValueOrField<StoredT>(std::forward<T>(value))); + } + else if constexpr (fn::is_field_v<StoredT>) { + using BaseType = typename StoredT::base_type; + this->set_output(identifier, ValueOrField<BaseType>(std::forward<T>(value))); } else { const CPPType &type = CPPType::get<StoredT>(); @@ -306,21 +326,21 @@ class GeoNodeExecParams { * \note This will add an error message if the string socket is active and * the input attribute does not exist. */ - GVArrayPtr get_input_attribute(const StringRef name, - const GeometryComponent &component, - const AttributeDomain domain, - const CustomDataType type, - const void *default_value) const; + GVArray get_input_attribute(const StringRef name, + const GeometryComponent &component, + AttributeDomain domain, + const CustomDataType type, + const void *default_value) const; template<typename T> - GVArray_Typed<T> get_input_attribute(const StringRef name, - const GeometryComponent &component, - const AttributeDomain domain, - const T &default_value) const + VArray<T> get_input_attribute(const StringRef name, + const GeometryComponent &component, + const AttributeDomain domain, + const T &default_value) const { const CustomDataType type = bke::cpp_type_to_custom_data_type(CPPType::get<T>()); - GVArrayPtr varray = this->get_input_attribute(name, component, domain, type, &default_value); - return GVArray_Typed<T>(std::move(varray)); + GVArray varray = this->get_input_attribute(name, component, domain, type, &default_value); + return varray.typed<T>(); } /** @@ -331,9 +351,18 @@ class GeoNodeExecParams { const GeometryComponent &component, const CustomDataType default_type) const; + /** + * If any of the corresponding input sockets are attributes instead of single values, + * use the highest priority attribute domain from among them. + * Otherwise return the default domain. + */ AttributeDomain get_highest_priority_input_domain(Span<std::string> names, const GeometryComponent &component, - const AttributeDomain default_domain) const; + AttributeDomain default_domain) const; + + std::string attribute_producer_name() const; + + void set_default_remaining_outputs(); private: /* Utilities for detecting common errors at when using this class. */ diff --git a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh b/source/blender/nodes/NOD_geometry_nodes_eval_log.hh index ff8e137e341..ac2c29b4ec2 100644 --- a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh +++ b/source/blender/nodes/NOD_geometry_nodes_eval_log.hh @@ -41,6 +41,8 @@ #include "NOD_derived_node_tree.hh" +#include <chrono> + struct SpaceNode; struct SpaceSpreadsheet; @@ -76,6 +78,31 @@ class GenericValueLog : public ValueLog { } }; +class GFieldValueLog : public ValueLog { + private: + fn::GField field_; + const fn::CPPType &type_; + Vector<std::string> input_tooltips_; + + public: + GFieldValueLog(fn::GField field, bool log_full_field); + + const fn::GField &field() const + { + return field_; + } + + Span<std::string> input_tooltips() const + { + return input_tooltips_; + } + + const fn::CPPType &type() const + { + return type_; + } +}; + struct GeometryAttributeInfo { std::string name; AttributeDomain domain; @@ -109,7 +136,7 @@ class GeometryValueLog : public ValueLog { std::optional<PointCloudInfo> pointcloud_info; std::optional<InstancesInfo> instances_info; - GeometryValueLog(const GeometrySet &geometry_set, bool log_full_geometry); + GeometryValueLog(const GeometrySet &geometry_set, bool log_full_geometry = false); Span<GeometryAttributeInfo> attributes() const { @@ -144,6 +171,16 @@ struct NodeWithWarning { NodeWarning warning; }; +struct NodeWithExecutionTime { + DNode node; + std::chrono::microseconds exec_time; +}; + +struct NodeWithDebugMessage { + DNode node; + std::string message; +}; + /** The same value can be referenced by multiple sockets when they are linked. */ struct ValueOfSockets { Span<DSocket> sockets; @@ -164,6 +201,8 @@ class LocalGeoLogger { std::unique_ptr<LinearAllocator<>> allocator_; Vector<ValueOfSockets> values_; Vector<NodeWithWarning> node_warnings_; + Vector<NodeWithExecutionTime> node_exec_times_; + Vector<NodeWithDebugMessage> node_debug_messages_; friend ModifierLog; @@ -176,26 +215,49 @@ class LocalGeoLogger { void log_value_for_sockets(Span<DSocket> sockets, GPointer value); void log_multi_value_socket(DSocket socket, Span<GPointer> values); void log_node_warning(DNode node, NodeWarningType type, std::string message); + void log_execution_time(DNode node, std::chrono::microseconds exec_time); + /** + * Log a message that will be displayed in the node editor next to the node. + * This should only be used for debugging purposes and not to display information to users. + */ + void log_debug_message(DNode node, std::string message); }; /** The root logger class. */ class GeoLogger { private: - /** The entire geometry of sockets in this set should be cached, because e.g. the spreadsheet - * displays the data. We don't log the entire geometry at all places, because that would require - * way too much memory. */ - Set<DSocket> log_full_geometry_sockets_; + /** + * Log the entire value for these sockets, because they may be inspected afterwards. + * We don't log everything, because that would take up too much memory and cause significant + * slowdowns. + */ + Set<DSocket> log_full_sockets_; threading::EnumerableThreadSpecific<LocalGeoLogger> threadlocals_; + /* These are only optional since they don't have a default constructor. */ + std::unique_ptr<GeometryValueLog> input_geometry_log_; + std::unique_ptr<GeometryValueLog> output_geometry_log_; + friend LocalGeoLogger; + friend ModifierLog; public: - GeoLogger(Set<DSocket> log_full_geometry_sockets) - : log_full_geometry_sockets_(std::move(log_full_geometry_sockets)), + GeoLogger(Set<DSocket> log_full_sockets) + : log_full_sockets_(std::move(log_full_sockets)), threadlocals_([this]() { return LocalGeoLogger(*this); }) { } + void log_input_geometry(const GeometrySet &geometry) + { + input_geometry_log_ = std::make_unique<GeometryValueLog>(geometry); + } + + void log_output_geometry(const GeometrySet &geometry) + { + output_geometry_log_ = std::make_unique<GeometryValueLog>(geometry); + } + LocalGeoLogger &local() { return threadlocals_.local(); @@ -232,12 +294,15 @@ class NodeLog { Vector<SocketLog> input_logs_; Vector<SocketLog> output_logs_; Vector<NodeWarning, 0> warnings_; + Vector<std::string, 0> debug_messages_; + std::chrono::microseconds exec_time_; friend ModifierLog; public: const SocketLog *lookup_socket_log(eNodeSocketInOut in_out, int index) const; const SocketLog *lookup_socket_log(const bNode &node, const bNodeSocket &socket) const; + void execution_time(std::chrono::microseconds exec_time); Span<SocketLog> input_logs() const { @@ -254,6 +319,16 @@ class NodeLog { return warnings_; } + Span<std::string> debug_messages() const + { + return debug_messages_; + } + + std::chrono::microseconds execution_time() const + { + return exec_time_; + } + Vector<const GeometryAttributeInfo *> lookup_available_attributes() const; }; @@ -281,6 +356,9 @@ class ModifierLog { destruct_ptr<TreeLog> root_tree_logs_; Vector<destruct_ptr<ValueLog>> logged_values_; + std::unique_ptr<GeometryValueLog> input_geometry_log_; + std::unique_ptr<GeometryValueLog> output_geometry_log_; + public: ModifierLog(GeoLogger &logger); @@ -301,6 +379,9 @@ class ModifierLog { const SpaceSpreadsheet &sspreadsheet); void foreach_node_log(FunctionRef<void(const NodeLog &)> fn) const; + const GeometryValueLog *input_geometry_log() const; + const GeometryValueLog *output_geometry_log() const; + private: using LogByTreeContext = Map<const DTreeContext *, TreeLog *>; diff --git a/source/blender/nodes/NOD_math_functions.hh b/source/blender/nodes/NOD_math_functions.hh index 9443be820d1..6ea89beee2e 100644 --- a/source/blender/nodes/NOD_math_functions.hh +++ b/source/blender/nodes/NOD_math_functions.hh @@ -18,9 +18,9 @@ #include "DNA_node_types.h" -#include "BLI_float3.hh" #include "BLI_math_base_safe.h" #include "BLI_math_rotation.h" +#include "BLI_math_vec_types.hh" #include "BLI_string_ref.hh" namespace blender::nodes { @@ -36,9 +36,9 @@ struct FloatMathOperationInfo { } }; -const FloatMathOperationInfo *get_float_math_operation_info(const int operation); -const FloatMathOperationInfo *get_float3_math_operation_info(const int operation); -const FloatMathOperationInfo *get_float_compare_operation_info(const int operation); +const FloatMathOperationInfo *get_float_math_operation_info(int operation); +const FloatMathOperationInfo *get_float3_math_operation_info(int operation); +const FloatMathOperationInfo *get_float_compare_operation_info(int operation); /** * This calls the `callback` with two arguments: @@ -193,7 +193,7 @@ inline bool try_dispatch_float_math_fl_fl_fl_to_fl(const int operation, Callback case NODE_MATH_SMOOTH_MIN: return dispatch([](float a, float b, float c) { return smoothminf(a, b, c); }); case NODE_MATH_SMOOTH_MAX: - return dispatch([](float a, float b, float c) { return -smoothminf(-a, -b, -c); }); + return dispatch([](float a, float b, float c) { return -smoothminf(-a, -b, c); }); case NODE_MATH_WRAP: return dispatch([](float a, float b, float c) { return wrapf(a, b, c); }); } @@ -204,7 +204,7 @@ inline bool try_dispatch_float_math_fl_fl_fl_to_fl(const int operation, Callback * This is similar to try_dispatch_float_math_fl_to_fl, just with a different callback signature. */ template<typename Callback> -inline bool try_dispatch_float_math_fl_fl_to_bool(const FloatCompareOperation operation, +inline bool try_dispatch_float_math_fl_fl_to_bool(const NodeCompareOperation operation, Callback &&callback) { const FloatMathOperationInfo *info = get_float_compare_operation_info(operation); @@ -219,13 +219,13 @@ inline bool try_dispatch_float_math_fl_fl_to_bool(const FloatCompareOperation op }; switch (operation) { - case NODE_FLOAT_COMPARE_LESS_THAN: + case NODE_COMPARE_LESS_THAN: return dispatch([](float a, float b) { return a < b; }); - case NODE_FLOAT_COMPARE_LESS_EQUAL: + case NODE_COMPARE_LESS_EQUAL: return dispatch([](float a, float b) { return a <= b; }); - case NODE_FLOAT_COMPARE_GREATER_THAN: + case NODE_COMPARE_GREATER_THAN: return dispatch([](float a, float b) { return a > b; }); - case NODE_FLOAT_COMPARE_GREATER_EQUAL: + case NODE_COMPARE_GREATER_EQUAL: return dispatch([](float a, float b) { return a >= b; }); default: return false; @@ -240,6 +240,8 @@ template<typename Callback> inline bool try_dispatch_float_math_fl3_fl3_to_fl3(const NodeVectorMathOperation operation, Callback &&callback) { + using namespace blender::math; + const FloatMathOperationInfo *info = get_float3_math_operation_info(operation); if (info == nullptr) { return false; @@ -259,40 +261,21 @@ inline bool try_dispatch_float_math_fl3_fl3_to_fl3(const NodeVectorMathOperation case NODE_VECTOR_MATH_MULTIPLY: return dispatch([](float3 a, float3 b) { return a * b; }); case NODE_VECTOR_MATH_DIVIDE: - return dispatch([](float3 a, float3 b) { - return float3(safe_divide(a.x, b.x), safe_divide(a.y, b.y), safe_divide(a.z, b.z)); - }); + return dispatch([](float3 a, float3 b) { return safe_divide(a, b); }); case NODE_VECTOR_MATH_CROSS_PRODUCT: - return dispatch([](float3 a, float3 b) { return float3::cross_high_precision(a, b); }); + return dispatch([](float3 a, float3 b) { return cross_high_precision(a, b); }); case NODE_VECTOR_MATH_PROJECT: - return dispatch([](float3 a, float3 b) { - float length_squared = b.length_squared(); - return (length_squared != 0.0) ? (float3::dot(a, b) / length_squared) * b : float3(0.0f); - }); + return dispatch([](float3 a, float3 b) { return project(a, b); }); case NODE_VECTOR_MATH_REFLECT: - return dispatch([](float3 a, float3 b) { - b.normalize(); - return a.reflected(b); - }); + return dispatch([](float3 a, float3 b) { return reflect(a, normalize(b)); }); case NODE_VECTOR_MATH_SNAP: - return dispatch([](float3 a, float3 b) { - return float3(floor(safe_divide(a.x, b.x)), - floor(safe_divide(a.y, b.y)), - floor(safe_divide(a.z, b.z))) * - b; - }); + return dispatch([](float3 a, float3 b) { return floor(safe_divide(a, b)) * b; }); case NODE_VECTOR_MATH_MODULO: - return dispatch([](float3 a, float3 b) { - return float3(safe_modf(a.x, b.x), safe_modf(a.y, b.y), safe_modf(a.z, b.z)); - }); + return dispatch([](float3 a, float3 b) { return mod(a, b); }); case NODE_VECTOR_MATH_MINIMUM: - return dispatch([](float3 a, float3 b) { - return float3(min_ff(a.x, b.x), min_ff(a.y, b.y), min_ff(a.z, b.z)); - }); + return dispatch([](float3 a, float3 b) { return min(a, b); }); case NODE_VECTOR_MATH_MAXIMUM: - return dispatch([](float3 a, float3 b) { - return float3(max_ff(a.x, b.x), max_ff(a.y, b.y), max_ff(a.z, b.z)); - }); + return dispatch([](float3 a, float3 b) { return max(a, b); }); default: return false; } @@ -306,6 +289,8 @@ template<typename Callback> inline bool try_dispatch_float_math_fl3_fl3_to_fl(const NodeVectorMathOperation operation, Callback &&callback) { + using namespace blender::math; + const FloatMathOperationInfo *info = get_float3_math_operation_info(operation); if (info == nullptr) { return false; @@ -319,9 +304,9 @@ inline bool try_dispatch_float_math_fl3_fl3_to_fl(const NodeVectorMathOperation switch (operation) { case NODE_VECTOR_MATH_DOT_PRODUCT: - return dispatch([](float3 a, float3 b) { return float3::dot(a, b); }); + return dispatch([](float3 a, float3 b) { return dot(a, b); }); case NODE_VECTOR_MATH_DISTANCE: - return dispatch([](float3 a, float3 b) { return float3::distance(a, b); }); + return dispatch([](float3 a, float3 b) { return distance(a, b); }); default: return false; } @@ -335,6 +320,8 @@ template<typename Callback> inline bool try_dispatch_float_math_fl3_fl3_fl3_to_fl3(const NodeVectorMathOperation operation, Callback &&callback) { + using namespace blender::math; + const FloatMathOperationInfo *info = get_float3_math_operation_info(operation); if (info == nullptr) { return false; @@ -354,7 +341,7 @@ inline bool try_dispatch_float_math_fl3_fl3_fl3_to_fl3(const NodeVectorMathOpera return float3(wrapf(a.x, b.x, c.x), wrapf(a.y, b.y, c.y), wrapf(a.z, b.z, c.z)); }); case NODE_VECTOR_MATH_FACEFORWARD: - return dispatch([](float3 a, float3 b, float3 c) { return float3::faceforward(a, b, c); }); + return dispatch([](float3 a, float3 b, float3 c) { return faceforward(a, b, c); }); default: return false; } @@ -368,6 +355,8 @@ template<typename Callback> inline bool try_dispatch_float_math_fl3_fl3_fl_to_fl3(const NodeVectorMathOperation operation, Callback &&callback) { + using namespace blender::math; + const FloatMathOperationInfo *info = get_float3_math_operation_info(operation); if (info == nullptr) { return false; @@ -381,8 +370,7 @@ inline bool try_dispatch_float_math_fl3_fl3_fl_to_fl3(const NodeVectorMathOperat switch (operation) { case NODE_VECTOR_MATH_REFRACT: - return dispatch( - [](float3 a, float3 b, float c) { return float3::refract(a, b.normalized(), c); }); + return dispatch([](float3 a, float3 b, float c) { return refract(a, normalize(b), c); }); default: return false; } @@ -396,6 +384,8 @@ template<typename Callback> inline bool try_dispatch_float_math_fl3_to_fl(const NodeVectorMathOperation operation, Callback &&callback) { + using namespace blender::math; + const FloatMathOperationInfo *info = get_float3_math_operation_info(operation); if (info == nullptr) { return false; @@ -409,7 +399,7 @@ inline bool try_dispatch_float_math_fl3_to_fl(const NodeVectorMathOperation oper switch (operation) { case NODE_VECTOR_MATH_LENGTH: - return dispatch([](float3 in) { return in.length(); }); + return dispatch([](float3 in) { return length(in); }); default: return false; } @@ -450,6 +440,8 @@ template<typename Callback> inline bool try_dispatch_float_math_fl3_to_fl3(const NodeVectorMathOperation operation, Callback &&callback) { + using namespace blender::math; + const FloatMathOperationInfo *info = get_float3_math_operation_info(operation); if (info == nullptr) { return false; @@ -463,20 +455,15 @@ inline bool try_dispatch_float_math_fl3_to_fl3(const NodeVectorMathOperation ope switch (operation) { case NODE_VECTOR_MATH_NORMALIZE: - return dispatch([](float3 in) { - float3 out = in; - out.normalize(); - return out; - }); /* Should be safe. */ + return dispatch([](float3 in) { return normalize(in); }); /* Should be safe. */ case NODE_VECTOR_MATH_FLOOR: - return dispatch([](float3 in) { return float3(floor(in.x), floor(in.y), floor(in.z)); }); + return dispatch([](float3 in) { return floor(in); }); case NODE_VECTOR_MATH_CEIL: - return dispatch([](float3 in) { return float3(ceil(in.x), ceil(in.y), ceil(in.z)); }); + return dispatch([](float3 in) { return ceil(in); }); case NODE_VECTOR_MATH_FRACTION: - return dispatch( - [](float3 in) { return in - float3(floor(in.x), floor(in.y), floor(in.z)); }); + return dispatch([](float3 in) { return fract(in); }); case NODE_VECTOR_MATH_ABSOLUTE: - return dispatch([](float3 in) { return float3::abs(in); }); + return dispatch([](float3 in) { return abs(in); }); case NODE_VECTOR_MATH_SINE: return dispatch([](float3 in) { return float3(sinf(in.x), sinf(in.y), sinf(in.z)); }); case NODE_VECTOR_MATH_COSINE: diff --git a/source/blender/nodes/NOD_multi_function.hh b/source/blender/nodes/NOD_multi_function.hh index 58816544dc1..367ceaab9f7 100644 --- a/source/blender/nodes/NOD_multi_function.hh +++ b/source/blender/nodes/NOD_multi_function.hh @@ -33,15 +33,15 @@ class NodeMultiFunctions; */ class NodeMultiFunctionBuilder : NonCopyable, NonMovable { private: - ResourceScope &resource_scope_; bNode &node_; bNodeTree &tree_; + std::shared_ptr<MultiFunction> owned_built_fn_; const MultiFunction *built_fn_ = nullptr; friend NodeMultiFunctions; public: - NodeMultiFunctionBuilder(ResourceScope &resource_scope, bNode &node, bNodeTree &tree); + NodeMultiFunctionBuilder(bNode &node, bNodeTree &tree); /** * Assign a multi-function for the current node. The input and output parameters of the function @@ -58,31 +58,33 @@ class NodeMultiFunctionBuilder : NonCopyable, NonMovable { bNode &node(); bNodeTree &tree(); - - ResourceScope &resource_scope(); }; /** * Gives access to multi-functions for all nodes in a node tree that support them. */ class NodeMultiFunctions { + public: + struct Item { + const MultiFunction *fn = nullptr; + std::shared_ptr<MultiFunction> owned_fn; + }; + private: - Map<const bNode *, const MultiFunction *> map_; + Map<const bNode *, Item> map_; public: - NodeMultiFunctions(const DerivedNodeTree &tree, ResourceScope &resource_scope); + NodeMultiFunctions(const DerivedNodeTree &tree); - const MultiFunction *try_get(const DNode &node) const; + const Item &try_get(const DNode &node) const; }; -/* -------------------------------------------------------------------- - * NodeMultiFunctionBuilder inline methods. - */ +/* -------------------------------------------------------------------- */ +/** \name #NodeMultiFunctionBuilder Inline Methods + * \{ */ -inline NodeMultiFunctionBuilder::NodeMultiFunctionBuilder(ResourceScope &resource_scope, - bNode &node, - bNodeTree &tree) - : resource_scope_(resource_scope), node_(node), tree_(tree) +inline NodeMultiFunctionBuilder::NodeMultiFunctionBuilder(bNode &node, bNodeTree &tree) + : node_(node), tree_(tree) { } @@ -96,11 +98,6 @@ inline bNodeTree &NodeMultiFunctionBuilder::tree() return tree_; } -inline ResourceScope &NodeMultiFunctionBuilder::resource_scope() -{ - return resource_scope_; -} - inline void NodeMultiFunctionBuilder::set_matching_fn(const MultiFunction *fn) { built_fn_ = fn; @@ -108,23 +105,32 @@ inline void NodeMultiFunctionBuilder::set_matching_fn(const MultiFunction *fn) inline void NodeMultiFunctionBuilder::set_matching_fn(const MultiFunction &fn) { - this->set_matching_fn(&fn); + built_fn_ = &fn; } template<typename T, typename... Args> inline void NodeMultiFunctionBuilder::construct_and_set_matching_fn(Args &&...args) { - const T &fn = resource_scope_.construct<T>(std::forward<Args>(args)...); - this->set_matching_fn(&fn); + owned_built_fn_ = std::make_shared<T>(std::forward<Args>(args)...); + built_fn_ = &*owned_built_fn_; } -/* -------------------------------------------------------------------- - * NodeMultiFunctions inline methods. - */ +/** \} */ -inline const MultiFunction *NodeMultiFunctions::try_get(const DNode &node) const +/* -------------------------------------------------------------------- */ +/** \name #NodeMultiFunctions Inline Methods + * \{ */ + +inline const NodeMultiFunctions::Item &NodeMultiFunctions::try_get(const DNode &node) const { - return map_.lookup_default(node->bnode(), nullptr); + static Item empty_item; + const Item *item = map_.lookup_ptr(node->bnode()); + if (item == nullptr) { + return empty_item; + } + return *item; } +/** \} */ + } // namespace blender::nodes diff --git a/source/blender/nodes/NOD_node_declaration.hh b/source/blender/nodes/NOD_node_declaration.hh index 07d4e05cda8..113e8ffc93d 100644 --- a/source/blender/nodes/NOD_node_declaration.hh +++ b/source/blender/nodes/NOD_node_declaration.hh @@ -16,6 +16,7 @@ #pragma once +#include <functional> #include <type_traits> #include "BLI_string_ref.hh" @@ -23,6 +24,8 @@ #include "DNA_node_types.h" +struct bNode; + namespace blender::nodes { class NodeDeclarationBuilder; @@ -57,59 +60,15 @@ class OutputFieldDependency { Vector<int> linked_input_indices_; public: - static OutputFieldDependency ForFieldSource() - { - OutputFieldDependency field_dependency; - field_dependency.type_ = OutputSocketFieldType::FieldSource; - return field_dependency; - } - - static OutputFieldDependency ForDataSource() - { - OutputFieldDependency field_dependency; - field_dependency.type_ = OutputSocketFieldType::None; - return field_dependency; - } - - static OutputFieldDependency ForPartiallyDependentField(Vector<int> indices) - { - OutputFieldDependency field_dependency; - if (indices.is_empty()) { - field_dependency.type_ = OutputSocketFieldType::None; - } - else { - field_dependency.type_ = OutputSocketFieldType::PartiallyDependent; - field_dependency.linked_input_indices_ = std::move(indices); - } - return field_dependency; - } - - static OutputFieldDependency ForDependentField() - { - OutputFieldDependency field_dependency; - field_dependency.type_ = OutputSocketFieldType::DependentField; - return field_dependency; - } + static OutputFieldDependency ForFieldSource(); + static OutputFieldDependency ForDataSource(); + static OutputFieldDependency ForDependentField(); + static OutputFieldDependency ForPartiallyDependentField(Vector<int> indices); - OutputSocketFieldType field_type() const - { - return type_; - } + OutputSocketFieldType field_type() const; + Span<int> linked_input_indices() const; - Span<int> linked_input_indices() const - { - return linked_input_indices_; - } - - friend bool operator==(const OutputFieldDependency &a, const OutputFieldDependency &b) - { - return a.type_ == b.type_ && a.linked_input_indices_ == b.linked_input_indices_; - } - - friend bool operator!=(const OutputFieldDependency &a, const OutputFieldDependency &b) - { - return !(a == b); - } + friend bool operator==(const OutputFieldDependency &a, const OutputFieldDependency &b); }; /** @@ -118,16 +77,6 @@ class OutputFieldDependency { struct FieldInferencingInterface { Vector<InputSocketFieldType> inputs; Vector<OutputFieldDependency> outputs; - - friend bool operator==(const FieldInferencingInterface &a, const FieldInferencingInterface &b) - { - return a.inputs == b.inputs && a.outputs == b.outputs; - } - - friend bool operator!=(const FieldInferencingInterface &a, const FieldInferencingInterface &b) - { - return !(a == b); - } }; /** @@ -138,27 +87,53 @@ class SocketDeclaration { std::string name_; std::string identifier_; std::string description_; + /** Defined by whether the socket is part of the node's input or + * output socket declaration list. Included here for convenience. */ + eNodeSocketInOut in_out_; bool hide_label_ = false; bool hide_value_ = false; + bool compact_ = false; bool is_multi_input_ = false; bool no_mute_links_ = false; + bool is_unavailable_ = false; + bool is_attribute_name_ = false; + bool is_default_link_socket_ = false; InputSocketFieldType input_field_type_ = InputSocketFieldType::None; OutputFieldDependency output_field_dependency_; + /** Utility method to make the socket available if there is a straightforward way to do so. */ + std::function<void(bNode &)> make_available_fn_; + friend NodeDeclarationBuilder; template<typename SocketDecl> friend class SocketDeclarationBuilder; public: virtual ~SocketDeclaration() = default; - virtual bNodeSocket &build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const = 0; + virtual bNodeSocket &build(bNodeTree &ntree, bNode &node) const = 0; virtual bool matches(const bNodeSocket &socket) const = 0; virtual bNodeSocket &update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &socket) const; + /** + * Determine if a new socket described by this declaration could have a valid connection + * the other socket. + */ + virtual bool can_connect(const bNodeSocket &socket) const = 0; + + /** + * Change the node such that the socket will become visible. The node type's update method + * should be called afterwards. + * \note Note that this is not necessarily implemented for all node types. + */ + void make_available(bNode &node) const; + StringRefNull name() const; StringRefNull description() const; StringRefNull identifier() const; + eNodeSocketInOut in_out() const; + bool is_attribute_name() const; + bool is_default_link_socket() const; InputSocketFieldType input_field_type() const; const OutputFieldDependency &output_field_dependency() const; @@ -211,12 +186,35 @@ class SocketDeclarationBuilder : public BaseSocketDeclarationBuilder { decl_->description_ = std::move(value); return *(Self *)this; } + Self &no_muted_links(bool value = true) { decl_->no_mute_links_ = value; return *(Self *)this; } + /** + * Used for sockets that are always unavailable and should not be seen by the user. + * Ideally, no new calls to this method should be added over time. + */ + Self &unavailable(bool value = true) + { + decl_->is_unavailable_ = value; + return *(Self *)this; + } + + Self &is_attribute_name(bool value = true) + { + decl_->is_attribute_name_ = value; + return *(Self *)this; + } + + Self &is_default_link_socket(bool value = true) + { + decl_->is_default_link_socket_ = value; + return *(Self *)this; + } + /** The input socket allows passing in a field. */ Self &supports_field() { @@ -251,6 +249,19 @@ class SocketDeclarationBuilder : public BaseSocketDeclarationBuilder { { decl_->output_field_dependency_ = OutputFieldDependency::ForPartiallyDependentField( std::move(input_dependencies)); + 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 + * make other sockets unavailable, since it is meant to be called when the node is first added. + * The node type's update function is called afterwards. + */ + Self &make_available(std::function<void(bNode &)> fn) + { + decl_->make_available_fn_ = std::move(fn); + return *(Self *)this; } }; @@ -265,11 +276,11 @@ class NodeDeclaration { friend NodeDeclarationBuilder; public: - void build(bNodeTree &ntree, bNode &node) const; bool matches(const bNode &node) const; Span<SocketDeclarationPtr> inputs() const; Span<SocketDeclarationPtr> outputs() const; + Span<SocketDeclarationPtr> sockets(eNodeSocketInOut in_out) const; bool is_function_node() const { @@ -289,10 +300,13 @@ class NodeDeclarationBuilder { /** * All inputs support fields, and all outputs are fields if any of the inputs is a field. - * Calling field status definitions on each socket is unnecessary. + * Calling field status definitions on each socket is unnecessary. Must be called before adding + * any sockets. */ void is_function_node(bool value = true) { + BLI_assert_msg(declaration_.inputs().is_empty() && declaration_.outputs().is_empty(), + "is_function_node() must be called before any socket is created"); declaration_.is_function_node_ = value; } @@ -305,12 +319,88 @@ class NodeDeclarationBuilder { template<typename DeclType> typename DeclType::Builder &add_socket(StringRef name, StringRef identifier, - Vector<SocketDeclarationPtr> &r_decls); + eNodeSocketInOut in_out); }; -/* -------------------------------------------------------------------- - * SocketDeclaration inline methods. - */ +/* -------------------------------------------------------------------- */ +/** \name #OutputFieldDependency Inline Methods + * \{ */ + +inline OutputFieldDependency OutputFieldDependency::ForFieldSource() +{ + OutputFieldDependency field_dependency; + field_dependency.type_ = OutputSocketFieldType::FieldSource; + return field_dependency; +} + +inline OutputFieldDependency OutputFieldDependency::ForDataSource() +{ + OutputFieldDependency field_dependency; + field_dependency.type_ = OutputSocketFieldType::None; + return field_dependency; +} + +inline OutputFieldDependency OutputFieldDependency::ForDependentField() +{ + OutputFieldDependency field_dependency; + field_dependency.type_ = OutputSocketFieldType::DependentField; + return field_dependency; +} + +inline OutputFieldDependency OutputFieldDependency::ForPartiallyDependentField(Vector<int> indices) +{ + OutputFieldDependency field_dependency; + if (indices.is_empty()) { + field_dependency.type_ = OutputSocketFieldType::None; + } + else { + field_dependency.type_ = OutputSocketFieldType::PartiallyDependent; + field_dependency.linked_input_indices_ = std::move(indices); + } + return field_dependency; +} + +inline OutputSocketFieldType OutputFieldDependency::field_type() const +{ + return type_; +} + +inline Span<int> OutputFieldDependency::linked_input_indices() const +{ + return linked_input_indices_; +} + +inline bool operator==(const OutputFieldDependency &a, const OutputFieldDependency &b) +{ + return a.type_ == b.type_ && a.linked_input_indices_ == b.linked_input_indices_; +} + +inline bool operator!=(const OutputFieldDependency &a, const OutputFieldDependency &b) +{ + return !(a == b); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #FieldInferencingInterface Inline Methods + * \{ */ + +inline bool operator==(const FieldInferencingInterface &a, const FieldInferencingInterface &b) +{ + return a.inputs == b.inputs && a.outputs == b.outputs; +} + +inline bool operator!=(const FieldInferencingInterface &a, const FieldInferencingInterface &b) +{ + return !(a == b); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #SocketDeclaration Inline Methods + * \{ */ inline StringRefNull SocketDeclaration::name() const { @@ -322,10 +412,26 @@ inline StringRefNull SocketDeclaration::identifier() const return identifier_; } +inline eNodeSocketInOut SocketDeclaration::in_out() const +{ + return in_out_; +} + inline StringRefNull SocketDeclaration::description() const { return description_; } + +inline bool SocketDeclaration::is_attribute_name() const +{ + return is_attribute_name_; +} + +inline bool SocketDeclaration::is_default_link_socket() const +{ + return is_default_link_socket_; +} + inline InputSocketFieldType SocketDeclaration::input_field_type() const { return input_field_type_; @@ -336,9 +442,18 @@ inline const OutputFieldDependency &SocketDeclaration::output_field_dependency() return output_field_dependency_; } -/* -------------------------------------------------------------------- - * NodeDeclarationBuilder inline methods. - */ +inline void SocketDeclaration::make_available(bNode &node) const +{ + if (make_available_fn_) { + make_available_fn_(node); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #NodeDeclarationBuilder Inline Methods + * \{ */ inline NodeDeclarationBuilder::NodeDeclarationBuilder(NodeDeclaration &declaration) : declaration_(declaration) @@ -349,36 +464,48 @@ template<typename DeclType> inline typename DeclType::Builder &NodeDeclarationBuilder::add_input(StringRef name, StringRef identifier) { - return this->add_socket<DeclType>(name, identifier, declaration_.inputs_); + return this->add_socket<DeclType>(name, identifier, SOCK_IN); } template<typename DeclType> inline typename DeclType::Builder &NodeDeclarationBuilder::add_output(StringRef name, StringRef identifier) { - return this->add_socket<DeclType>(name, identifier, declaration_.outputs_); + return this->add_socket<DeclType>(name, identifier, SOCK_OUT); } template<typename DeclType> -inline typename DeclType::Builder &NodeDeclarationBuilder::add_socket( - StringRef name, StringRef identifier, Vector<SocketDeclarationPtr> &r_decls) +inline typename DeclType::Builder &NodeDeclarationBuilder::add_socket(StringRef name, + StringRef identifier, + eNodeSocketInOut in_out) { static_assert(std::is_base_of_v<SocketDeclaration, DeclType>); using Builder = typename DeclType::Builder; + + Vector<SocketDeclarationPtr> &declarations = in_out == SOCK_IN ? declaration_.inputs_ : + declaration_.outputs_; + std::unique_ptr<DeclType> socket_decl = std::make_unique<DeclType>(); std::unique_ptr<Builder> socket_decl_builder = std::make_unique<Builder>(); socket_decl_builder->decl_ = &*socket_decl; socket_decl->name_ = name; socket_decl->identifier_ = identifier.is_empty() ? name : identifier; - r_decls.append(std::move(socket_decl)); + socket_decl->in_out_ = in_out; + if (declaration_.is_function_node()) { + socket_decl->input_field_type_ = InputSocketFieldType::IsSupported; + socket_decl->output_field_dependency_ = OutputFieldDependency::ForDependentField(); + } + declarations.append(std::move(socket_decl)); Builder &socket_decl_builder_ref = *socket_decl_builder; builders_.append(std::move(socket_decl_builder)); return socket_decl_builder_ref; } -/* -------------------------------------------------------------------- - * NodeDeclaration inline methods. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #NodeDeclaration Inline Methods + * \{ */ inline Span<SocketDeclarationPtr> NodeDeclaration::inputs() const { @@ -390,4 +517,14 @@ inline Span<SocketDeclarationPtr> NodeDeclaration::outputs() const return outputs_; } +inline Span<SocketDeclarationPtr> NodeDeclaration::sockets(eNodeSocketInOut in_out) const +{ + if (in_out == SOCK_IN) { + return inputs_; + } + return outputs_; +} + +/** \} */ + } // namespace blender::nodes diff --git a/source/blender/nodes/NOD_node_tree_ref.hh b/source/blender/nodes/NOD_node_tree_ref.hh index 1da42fb6425..ebbec20a139 100644 --- a/source/blender/nodes/NOD_node_tree_ref.hh +++ b/source/blender/nodes/NOD_node_tree_ref.hh @@ -200,6 +200,8 @@ class NodeRef : NonCopyable, NonMovable { PointerRNA *rna() const; StringRefNull idname() const; StringRefNull name() const; + StringRefNull label() const; + StringRefNull label_or_name() const; bNodeType *typeinfo() const; const NodeDeclaration *declaration() const; @@ -260,6 +262,7 @@ class NodeTreeRef : NonCopyable, NonMovable { 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); @@ -275,6 +278,16 @@ class NodeTreeRef : NonCopyable, NonMovable { 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; @@ -283,7 +296,21 @@ class NodeTreeRef : NonCopyable, NonMovable { RightToLeft, }; - Vector<const NodeRef *> toposort(ToposortDirection direction) const; + 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; @@ -316,9 +343,9 @@ using nodes::OutputSocketRef; using nodes::SocketRef; } // namespace node_tree_ref_types -/* -------------------------------------------------------------------- - * SocketRef inline methods. - */ +/* -------------------------------------------------------------------- */ +/** \name #SocketRef Inline Methods + * \{ */ inline Span<const SocketRef *> SocketRef::logically_linked_sockets() const { @@ -457,9 +484,11 @@ template<typename T> inline T *SocketRef::default_value() const return (T *)bsocket_->default_value; } -/* -------------------------------------------------------------------- - * InputSocketRef inline methods. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #InputSocketRef Inline Methods + * \{ */ inline Span<const OutputSocketRef *> InputSocketRef::logically_linked_sockets() const { @@ -476,9 +505,11 @@ inline bool InputSocketRef::is_multi_input_socket() const return bsocket_->flag & SOCK_MULTI_INPUT; } -/* -------------------------------------------------------------------- - * OutputSocketRef inline methods. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #OutputSocketRef Inline Methods + * \{ */ inline Span<const InputSocketRef *> OutputSocketRef::logically_linked_sockets() const { @@ -490,9 +521,11 @@ inline Span<const InputSocketRef *> OutputSocketRef::directly_linked_sockets() c return directly_linked_sockets_.cast<const InputSocketRef *>(); } -/* -------------------------------------------------------------------- - * NodeRef inline methods. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #NodeRef Inline Methods + * \{ */ inline const NodeTreeRef &NodeRef::tree() const { @@ -567,6 +600,20 @@ 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; @@ -629,9 +676,11 @@ template<typename T> inline T *NodeRef::storage() const return (T *)bnode_->storage; } -/* -------------------------------------------------------------------- - * LinkRef inline methods. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #LinkRef Inline Methods + * \{ */ inline const OutputSocketRef &LinkRef::from() const { @@ -653,9 +702,11 @@ inline bool LinkRef::is_muted() const return blink_->flag & NODE_LINK_MUTED; } -/* -------------------------------------------------------------------- - * InternalLinkRef inline methods. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #InternalLinkRef Inline Methods + * \{ */ inline const InputSocketRef &InternalLinkRef::from() const { @@ -672,9 +723,11 @@ inline bNodeLink *InternalLinkRef::blink() const return blink_; } -/* -------------------------------------------------------------------- - * NodeTreeRef inline methods. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #NodeTreeRef Inline Methods + * \{ */ inline Span<const NodeRef *> NodeTreeRef::nodes() const { @@ -712,6 +765,11 @@ 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_; @@ -722,4 +780,6 @@ 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 2911e0fbea6..57740ce3ad9 100644 --- a/source/blender/nodes/NOD_shader.h +++ b/source/blender/nodes/NOD_shader.h @@ -49,6 +49,7 @@ void register_node_type_sh_normal(void); void register_node_type_sh_gamma(void); void register_node_type_sh_brightcontrast(void); void register_node_type_sh_mapping(void); +void register_node_type_sh_curve_float(void); void register_node_type_sh_curve_vec(void); void register_node_type_sh_curve_rgb(void); void register_node_type_sh_map_range(void); @@ -84,6 +85,7 @@ void register_node_type_sh_layer_weight(void); void register_node_type_sh_tex_coord(void); void register_node_type_sh_particle_info(void); void register_node_type_sh_hair_info(void); +void register_node_type_sh_point_info(void); void register_node_type_sh_volume_info(void); void register_node_type_sh_script(void); void register_node_type_sh_normal_map(void); @@ -142,6 +144,27 @@ void register_node_type_sh_tex_white_noise(void); void register_node_type_sh_custom_group(bNodeType *ntype); +struct bNodeTreeExec *ntreeShaderBeginExecTree(struct bNodeTree *ntree); +void ntreeShaderEndExecTree(struct bNodeTreeExec *exec); + +/** + * Find an output node of the shader tree. + * + * \note it will only return output which is NOT in the group, which isn't how + * render engines works but it's how the GPU shader compilation works. This we + * can change in the future and make it a generic function, but for now it stays + * private here. + */ +struct bNode *ntreeShaderOutputNode(struct bNodeTree *ntree, int target); + +/** + * This one needs to work on a local tree. + */ +void ntreeGPUMaterialNodes(struct bNodeTree *localtree, + struct GPUMaterial *mat, + bool *has_surface_output, + bool *has_volume_output); + #ifdef __cplusplus } #endif diff --git a/source/blender/nodes/NOD_socket_declarations.hh b/source/blender/nodes/NOD_socket_declarations.hh index 3d0cfdb5d5d..a1972c66ca2 100644 --- a/source/blender/nodes/NOD_socket_declarations.hh +++ b/source/blender/nodes/NOD_socket_declarations.hh @@ -21,7 +21,7 @@ #include "RNA_types.h" #include "BLI_color.hh" -#include "BLI_float3.hh" +#include "BLI_math_vec_types.hh" namespace blender::nodes::decl { @@ -39,36 +39,18 @@ class Float : public SocketDeclaration { public: using Builder = FloatBuilder; - bNodeSocket &build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const override; + bNodeSocket &build(bNodeTree &ntree, bNode &node) const override; bool matches(const bNodeSocket &socket) const override; bNodeSocket &update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &socket) const override; + bool can_connect(const bNodeSocket &socket) const override; }; class FloatBuilder : public SocketDeclarationBuilder<Float> { public: - FloatBuilder &min(const float value) - { - decl_->soft_min_value_ = value; - return *this; - } - - FloatBuilder &max(const float value) - { - decl_->soft_max_value_ = value; - return *this; - } - - FloatBuilder &default_value(const float value) - { - decl_->default_value_ = value; - return *this; - } - - FloatBuilder &subtype(PropertySubType subtype) - { - decl_->subtype_ = subtype; - return *this; - } + FloatBuilder &min(float value); + FloatBuilder &max(float value); + FloatBuilder &default_value(float value); + FloatBuilder &subtype(PropertySubType subtype); }; class IntBuilder; @@ -85,36 +67,18 @@ class Int : public SocketDeclaration { public: using Builder = IntBuilder; - bNodeSocket &build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const override; + bNodeSocket &build(bNodeTree &ntree, bNode &node) const override; bool matches(const bNodeSocket &socket) const override; bNodeSocket &update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &socket) const override; + bool can_connect(const bNodeSocket &socket) const override; }; class IntBuilder : public SocketDeclarationBuilder<Int> { public: - IntBuilder &min(const int value) - { - decl_->soft_min_value_ = value; - return *this; - } - - IntBuilder &max(const int value) - { - decl_->soft_max_value_ = value; - return *this; - } - - IntBuilder &default_value(const int value) - { - decl_->default_value_ = value; - return *this; - } - - IntBuilder &subtype(PropertySubType subtype) - { - decl_->subtype_ = subtype; - return *this; - } + IntBuilder &min(int value); + IntBuilder &max(int value); + IntBuilder &default_value(int value); + IntBuilder &subtype(PropertySubType subtype); }; class VectorBuilder; @@ -131,36 +95,19 @@ class Vector : public SocketDeclaration { public: using Builder = VectorBuilder; - bNodeSocket &build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const override; + bNodeSocket &build(bNodeTree &ntree, bNode &node) const override; bool matches(const bNodeSocket &socket) const override; bNodeSocket &update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &socket) const override; + bool can_connect(const bNodeSocket &socket) const override; }; class VectorBuilder : public SocketDeclarationBuilder<Vector> { public: - VectorBuilder &default_value(const float3 value) - { - decl_->default_value_ = value; - return *this; - } - - VectorBuilder &subtype(PropertySubType subtype) - { - decl_->subtype_ = subtype; - return *this; - } - - VectorBuilder &min(const float min) - { - decl_->soft_min_value_ = min; - return *this; - } - - VectorBuilder &max(const float max) - { - decl_->soft_max_value_ = max; - return *this; - } + VectorBuilder &default_value(const float3 value); + VectorBuilder &subtype(PropertySubType subtype); + VectorBuilder &min(float min); + VectorBuilder &max(float max); + VectorBuilder &compact(); }; class BoolBuilder; @@ -173,17 +120,14 @@ class Bool : public SocketDeclaration { public: using Builder = BoolBuilder; - bNodeSocket &build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const override; + bNodeSocket &build(bNodeTree &ntree, bNode &node) const override; bool matches(const bNodeSocket &socket) const override; + bool can_connect(const bNodeSocket &socket) const override; }; class BoolBuilder : public SocketDeclarationBuilder<Bool> { public: - BoolBuilder &default_value(const bool value) - { - decl_->default_value_ = value; - return *this; - } + BoolBuilder &default_value(bool value); }; class ColorBuilder; @@ -197,25 +141,35 @@ class Color : public SocketDeclaration { public: using Builder = ColorBuilder; - bNodeSocket &build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const override; + bNodeSocket &build(bNodeTree &ntree, bNode &node) const override; bool matches(const bNodeSocket &socket) const override; + bool can_connect(const bNodeSocket &socket) const override; }; class ColorBuilder : public SocketDeclarationBuilder<Color> { public: - ColorBuilder &default_value(const ColorGeometry4f value) - { - decl_->default_value_ = value; - return *this; - } + ColorBuilder &default_value(const ColorGeometry4f value); }; +class StringBuilder; + class String : public SocketDeclaration { + private: + std::string default_value_; + + friend StringBuilder; + public: - using Builder = SocketDeclarationBuilder<String>; + using Builder = StringBuilder; - bNodeSocket &build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const override; + bNodeSocket &build(bNodeTree &ntree, bNode &node) const override; bool matches(const bNodeSocket &socket) const override; + bool can_connect(const bNodeSocket &socket) const override; +}; + +class StringBuilder : public SocketDeclarationBuilder<String> { + public: + StringBuilder &default_value(const std::string value); }; class IDSocketDeclaration : public SocketDeclaration { @@ -223,57 +177,226 @@ class IDSocketDeclaration : public SocketDeclaration { const char *idname_; public: - IDSocketDeclaration(const char *idname) : idname_(idname) - { - } + IDSocketDeclaration(const char *idname); - bNodeSocket &build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const override; + bNodeSocket &build(bNodeTree &ntree, bNode &node) const override; bool matches(const bNodeSocket &socket) const override; bNodeSocket &update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &socket) const override; + bool can_connect(const bNodeSocket &socket) const override; }; class Object : public IDSocketDeclaration { public: using Builder = SocketDeclarationBuilder<Object>; - Object() : IDSocketDeclaration("NodeSocketObject") - { - } + Object(); }; class Material : public IDSocketDeclaration { public: using Builder = SocketDeclarationBuilder<Material>; - Material() : IDSocketDeclaration("NodeSocketMaterial") - { - } + Material(); }; class Collection : public IDSocketDeclaration { public: using Builder = SocketDeclarationBuilder<Collection>; - Collection() : IDSocketDeclaration("NodeSocketCollection") - { - } + Collection(); }; class Texture : public IDSocketDeclaration { public: using Builder = SocketDeclarationBuilder<Texture>; - Texture() : IDSocketDeclaration("NodeSocketTexture") - { - } + Texture(); }; -class Geometry : public SocketDeclaration { +class Image : public IDSocketDeclaration { public: - using Builder = SocketDeclarationBuilder<Geometry>; + using Builder = SocketDeclarationBuilder<Image>; + + Image(); +}; - bNodeSocket &build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const override; +class ShaderBuilder; + +class Shader : public SocketDeclaration { + private: + friend ShaderBuilder; + + public: + using Builder = ShaderBuilder; + + bNodeSocket &build(bNodeTree &ntree, bNode &node) const override; bool matches(const bNodeSocket &socket) const override; + bool can_connect(const bNodeSocket &socket) const override; +}; + +class ShaderBuilder : public SocketDeclarationBuilder<Shader> { }; +/* -------------------------------------------------------------------- */ +/** \name #FloatBuilder Inline Methods + * \{ */ + +inline FloatBuilder &FloatBuilder::min(const float value) +{ + decl_->soft_min_value_ = value; + return *this; +} + +inline FloatBuilder &FloatBuilder::max(const float value) +{ + decl_->soft_max_value_ = value; + return *this; +} + +inline FloatBuilder &FloatBuilder::default_value(const float value) +{ + decl_->default_value_ = value; + return *this; +} + +inline FloatBuilder &FloatBuilder::subtype(PropertySubType subtype) +{ + decl_->subtype_ = subtype; + return *this; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #IntBuilder Inline Methods + * \{ */ + +inline IntBuilder &IntBuilder::min(const int value) +{ + decl_->soft_min_value_ = value; + return *this; +} + +inline IntBuilder &IntBuilder::max(const int value) +{ + decl_->soft_max_value_ = value; + return *this; +} + +inline IntBuilder &IntBuilder::default_value(const int value) +{ + decl_->default_value_ = value; + return *this; +} + +inline IntBuilder &IntBuilder::subtype(PropertySubType subtype) +{ + decl_->subtype_ = subtype; + return *this; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #VectorBuilder Inline Methods + * \{ */ + +inline VectorBuilder &VectorBuilder::default_value(const float3 value) +{ + decl_->default_value_ = value; + return *this; +} + +inline VectorBuilder &VectorBuilder::subtype(PropertySubType subtype) +{ + decl_->subtype_ = subtype; + return *this; +} + +inline VectorBuilder &VectorBuilder::min(const float min) +{ + decl_->soft_min_value_ = min; + return *this; +} + +inline VectorBuilder &VectorBuilder::max(const float max) +{ + decl_->soft_max_value_ = max; + return *this; +} + +inline VectorBuilder &VectorBuilder::compact() +{ + decl_->compact_ = true; + return *this; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #BoolBuilder Inline Methods + * \{ */ + +inline BoolBuilder &BoolBuilder::default_value(const bool value) +{ + decl_->default_value_ = value; + return *this; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #ColorBuilder Inline Methods + * \{ */ + +inline ColorBuilder &ColorBuilder::default_value(const ColorGeometry4f value) +{ + decl_->default_value_ = value; + return *this; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #StringBuilder Inline Methods + * \{ */ + +inline StringBuilder &StringBuilder::default_value(std::string value) +{ + decl_->default_value_ = std::move(value); + return *this; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #IDSocketDeclaration and Children Inline Methods + * \{ */ + +inline IDSocketDeclaration::IDSocketDeclaration(const char *idname) : idname_(idname) +{ +} + +inline Object::Object() : IDSocketDeclaration("NodeSocketObject") +{ +} + +inline Material::Material() : IDSocketDeclaration("NodeSocketMaterial") +{ +} + +inline Collection::Collection() : IDSocketDeclaration("NodeSocketCollection") +{ +} + +inline Texture::Texture() : IDSocketDeclaration("NodeSocketTexture") +{ +} + +inline Image::Image() : IDSocketDeclaration("NodeSocketImage") +{ +} + +/** \} */ + } // namespace blender::nodes::decl diff --git a/source/blender/nodes/NOD_socket_declarations_geometry.hh b/source/blender/nodes/NOD_socket_declarations_geometry.hh new file mode 100644 index 00000000000..0ce07da22ff --- /dev/null +++ b/source/blender/nodes/NOD_socket_declarations_geometry.hh @@ -0,0 +1,55 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma once + +#include "BKE_geometry_set.hh" + +#include "NOD_socket_declarations.hh" + +namespace blender::nodes::decl { + +class GeometryBuilder; + +class Geometry : public SocketDeclaration { + private: + blender::Vector<GeometryComponentType> supported_types_; + bool only_realized_data_ = false; + bool only_instances_ = false; + + friend GeometryBuilder; + + public: + using Builder = GeometryBuilder; + + bNodeSocket &build(bNodeTree &ntree, bNode &node) const override; + bool matches(const bNodeSocket &socket) const override; + bool can_connect(const bNodeSocket &socket) const override; + + Span<GeometryComponentType> supported_types() const; + bool only_realized_data() const; + bool only_instances() const; +}; + +class GeometryBuilder : public SocketDeclarationBuilder<Geometry> { + public: + GeometryBuilder &supported_type(GeometryComponentType supported_type); + GeometryBuilder &supported_type(blender::Vector<GeometryComponentType> supported_types); + GeometryBuilder &only_realized_data(bool value = true); + GeometryBuilder &only_instances(bool value = true); +}; + +} // namespace blender::nodes::decl diff --git a/source/blender/nodes/NOD_socket_search_link.hh b/source/blender/nodes/NOD_socket_search_link.hh new file mode 100644 index 00000000000..b7594561dc4 --- /dev/null +++ b/source/blender/nodes/NOD_socket_search_link.hh @@ -0,0 +1,151 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma once + +#include <functional> + +#include "BLI_string_ref.hh" +#include "BLI_vector.hh" + +#include "DNA_node_types.h" /* Necessary for eNodeSocketInOut. */ + +#include "NOD_node_declaration.hh" + +struct bContext; + +namespace blender::nodes { + +/** + * Parameters for the operation operation of adding a node after the link drag search menu closes. + */ +class LinkSearchOpParams { + private: + /** + * Keep track of the nodes added by the callback, so they can be selected or moved afterwards. + */ + Vector<bNode *> &added_nodes_; + + public: + const bContext &C; + bNodeTree &node_tree; + /** + * The node that contains the #socket. + */ + bNode &node; + /** + * The existing socket to connect any added nodes to. Might be an input or output socket. + */ + bNodeSocket &socket; + + LinkSearchOpParams(const bContext &C, + bNodeTree &node_tree, + bNode &node, + bNodeSocket &socket, + Vector<bNode *> &added_nodes) + : added_nodes_(added_nodes), C(C), node_tree(node_tree), node(node), socket(socket) + { + } + + bNode &add_node(StringRef idname); + bNode &add_node(const bNodeType &type); + /** + * Find a socket with the given name (correctly checks for inputs and outputs) + * and connect it to the socket the link drag started from (#socket). + */ + void connect_available_socket(bNode &new_node, StringRef socket_name); + /** + * Like #connect_available_socket, but also calls the node's update function. + */ + void update_and_connect_available_socket(bNode &new_node, StringRef socket_name); +}; + +struct SocketLinkOperation { + using LinkSocketFn = std::function<void(LinkSearchOpParams &link_params)>; + + std::string name; + LinkSocketFn fn; + int weight = 0; +}; + +class GatherLinkSearchOpParams { + /** The current node type. */ + const bNodeType &node_type_; + + const bNodeTree &node_tree_; + + const bNodeSocket &other_socket_; + + /* The operations currently being built. Owned by the caller. */ + Vector<SocketLinkOperation> &items_; + + public: + GatherLinkSearchOpParams(const bNodeType &node_type, + const bNodeTree &node_tree, + const bNodeSocket &other_socket, + Vector<SocketLinkOperation> &items) + : node_type_(node_type), node_tree_(node_tree), other_socket_(other_socket), items_(items) + { + } + + /** + * The node on the other side of the dragged link. + */ + const bNodeSocket &other_socket() const; + + /** + * The node tree the user is editing when the search menu is created. + */ + const bNodeTree &node_tree() const; + + /** + * The type of the node in the current callback. + */ + const bNodeType &node_type() const; + + /** + * Whether to list the input or output sockets of the node. + */ + eNodeSocketInOut in_out() const; + + /** + * \param weight: Used to customize the order when multiple search items match. + * + * \warning When creating lambdas for the #fn argument, be careful not to capture this class + * itself, since it is temporary. That is why we tend to use the same variable name for this + * class (`params`) that we do for the argument to `LinkSocketFn`. + */ + void add_item(std::string socket_name, SocketLinkOperation::LinkSocketFn fn, int weight = 0); +}; + +/** + * This callback can be used for a node type when a few things are true about its inputs. + * To avoid creating more boilerplate, it is the default callback for node types. + * - Either all declared sockets are visible in the default state of the node, *OR* the node's + * type's declaration has been extended with #make_available functions for those sockets. + * + * If a node type does not meet these criteria, the function will do nothing in a release build. + * In a debug build, an assert will most likely be hit. + * + * \note For nodes with the deprecated #bNodeSocketTemplate instead of a declaration, + * these criteria do not apply and the function just tries its best without asserting. + */ +void search_link_ops_for_basic_node(GatherLinkSearchOpParams ¶ms); + +void search_link_ops_for_declarations(GatherLinkSearchOpParams ¶ms, + Span<SocketDeclarationPtr> declarations); + +} // namespace blender::nodes diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 3ee3faf6122..8cbde6adcad 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -96,6 +96,7 @@ DefNode(ShaderNode, SH_NODE_LIGHT_FALLOFF, 0, "LIG 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, "Hair 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", "" ) @@ -132,6 +133,7 @@ DefNode(ShaderNode, SH_NODE_VECTOR_DISPLACEMENT,def_sh_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(CompositorNode, CMP_NODE_VIEWER, def_cmp_viewer, "VIEWER", Viewer, "Viewer", "" ) DefNode(CompositorNode, CMP_NODE_RGB, 0, "RGB", RGB, "RGB", "" ) @@ -147,7 +149,7 @@ DefNode(CompositorNode, CMP_NODE_BLUR, def_cmp_blur, "BLUR", DefNode(CompositorNode, CMP_NODE_FILTER, def_cmp_filter, "FILTER", Filter, "Filter", "" ) DefNode(CompositorNode, CMP_NODE_MAP_VALUE, def_cmp_map_value, "MAP_VALUE", MapValue, "Map Value", "" ) DefNode(CompositorNode, CMP_NODE_MAP_RANGE, def_cmp_map_range, "MAP_RANGE", MapRange, "Map Range", "" ) -DefNode(CompositorNode, CMP_NODE_TIME, def_time, "TIME", Time, "Time", "" ) +DefNode(CompositorNode, CMP_NODE_TIME, def_time, "TIME", Time, "Time Curve", "" ) DefNode(CompositorNode, CMP_NODE_VECBLUR, def_cmp_vector_blur, "VECBLUR", VecBlur, "Vector Blur", "" ) DefNode(CompositorNode, CMP_NODE_SEPRGBA, 0, "SEPRGBA", SepRGBA, "Separate RGBA", "" ) DefNode(CompositorNode, CMP_NODE_SEPHSVA, 0, "SEPHSVA", SepHSVA, "Separate HSVA", "" ) @@ -226,6 +228,8 @@ DefNode(CompositorNode, CMP_NODE_DENOISE, def_cmp_denoise, "DENOIS DefNode(CompositorNode, CMP_NODE_EXPOSURE, 0, "EXPOSURE", Exposure, "Exposure", "" ) DefNode(CompositorNode, CMP_NODE_ANTIALIASING, def_cmp_antialiasing, "ANTIALIASING", AntiAliasing, "Anti-Aliasing", "" ) DefNode(CompositorNode, CMP_NODE_POSTERIZE, 0, "POSTERIZE", Posterize, "Posterize", "" ) +DefNode(CompositorNode, CMP_NODE_CONVERT_COLOR_SPACE,def_cmp_convert_color_space, "CONVERT_COLORSPACE", ConvertColorSpace, "Color Space","" ) +DefNode(CompositorNode, CMP_NODE_SCENE_TIME, 0, "SCENE_TIME", SceneTime, "Scene Time", "" ) DefNode(TextureNode, TEX_NODE_OUTPUT, def_tex_output, "OUTPUT", Output, "Output", "" ) DefNode(TextureNode, TEX_NODE_CHECKER, 0, "CHECKER", Checker, "Checker", "" ) @@ -264,16 +268,22 @@ DefNode(TextureNode, TEX_NODE_PROC+TEX_DISTNOISE, 0, "TEX_DI DefNode(FunctionNode, FN_NODE_LEGACY_RANDOM_FLOAT, 0, "LEGACY_RANDOM_FLOAT", LegacyRandomFloat, "Random Float", "") +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_FLOAT_COMPARE, def_float_compare, "FLOAT_COMPARE", FloatCompare, "Float Compare", "") +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", "") +DefNode(FunctionNode, FN_NODE_INPUT_BOOL, def_fn_input_bool, "INPUT_BOOL", InputBool, "Boolean", "") +DefNode(FunctionNode, FN_NODE_INPUT_COLOR, def_fn_input_color, "INPUT_COLOR", InputColor, "Color", "") +DefNode(FunctionNode, FN_NODE_INPUT_INT, def_fn_input_int, "INPUT_INT", InputInt, "Integer", "") DefNode(FunctionNode, FN_NODE_INPUT_SPECIAL_CHARACTERS, 0, "INPUT_SPECIAL_CHARACTERS", InputSpecialCharacters, "Special Characters", "") DefNode(FunctionNode, FN_NODE_INPUT_STRING, def_fn_input_string, "INPUT_STRING", InputString, "String", "") DefNode(FunctionNode, FN_NODE_INPUT_VECTOR, def_fn_input_vector, "INPUT_VECTOR", InputVector, "Vector", "") DefNode(FunctionNode, FN_NODE_RANDOM_VALUE, def_fn_random_value, "RANDOM_VALUE", RandomValue, "Random Value", "") -DefNode(FunctionNode, FN_NODE_VALUE_TO_STRING, 0, "VALUE_TO_STRING", ValueToString, "Value to String", "") +DefNode(FunctionNode, FN_NODE_REPLACE_STRING, 0, "REPLACE_STRING", ReplaceString, "Replace String", "") +DefNode(FunctionNode, FN_NODE_ROTATE_EULER, def_fn_rotate_euler, "ROTATE_EULER", RotateEuler, "Rotate Euler", "") +DefNode(FunctionNode, FN_NODE_SLICE_STRING, 0, "SLICE_STRING", SliceString, "Slice String", "") DefNode(FunctionNode, FN_NODE_STRING_LENGTH, 0, "STRING_LENGTH", StringLength, "String Length", "") -DefNode(FunctionNode, FN_NODE_STRING_SUBSTRING, 0, "STRING_SUBSTRING", StringSubstring, "String Substring", "") +DefNode(FunctionNode, FN_NODE_VALUE_TO_STRING, 0, "VALUE_TO_STRING", ValueToString, "Value to String", "") DefNode(GeometryNode, GEO_NODE_LEGACY_ALIGN_ROTATION_TO_VECTOR, def_geo_align_rotation_to_vector, "LEGACY_ALIGN_ROTATION_TO_VECTOR", LegacyAlignRotationToVector, "Align Rotation to Vector", "") DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_CLAMP, def_geo_attribute_clamp, "LEGACY_ATTRIBUTE_CLAMP", LegacyAttributeClamp, "Attribute Clamp", "") @@ -296,10 +306,10 @@ DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_VECTOR_ROTATE, def_geo_attribute DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_ENDPOINTS, 0, "LEGACY_CURVE_ENDPOINTS", LegacyCurveEndpoints, "Curve Endpoints", "") DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_REVERSE, 0, "LEGACY_CURVE_REVERSE", LegacyCurveReverse, "Curve Reverse", "") DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_SELECT_HANDLES, def_geo_curve_select_handles, "LEGACY_CURVE_SELECT_HANDLES", LegacyCurveSelectHandles, "Select by Handle Type", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_SET_HANDLES, def_geo_curve_set_handles, "LEGACY_CURVE_SET_HANDLES", LegacyCurveSetHandles, "Set Handle Type", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_SET_HANDLES, def_geo_legacy_curve_set_handles, "LEGACY_CURVE_SET_HANDLES", LegacyCurveSetHandles, "Set Handle Type", "") DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_SPLINE_TYPE, def_geo_curve_spline_type, "LEGACY_CURVE_SPLINE_TYPE", LegacyCurveSplineType, "Set Spline Type", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_SUBDIVIDE, def_geo_curve_subdivide, "LEGACY_CURVE_SUBDIVIDE", LegacyCurveSubdivide, "Curve Subdivide", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_TO_POINTS, def_geo_curve_to_points, "LEGACY_CURVE_TO_POINTS", LegacyCurveToPoints, "Curve to Points", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_SUBDIVIDE, def_geo_legacy_curve_subdivide, "LEGACY_CURVE_SUBDIVIDE", LegacyCurveSubdivide, "Curve Subdivide", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_TO_POINTS, def_geo_legacy_curve_to_points, "LEGACY_CURVE_TO_POINTS", LegacyCurveToPoints, "Curve to Points", "") DefNode(GeometryNode, GEO_NODE_LEGACY_DELETE_GEOMETRY, 0, "LEGACY_DELETE_GEOMETRY", LegacyDeleteGeometry, "Delete Geometry", "") DefNode(GeometryNode, GEO_NODE_LEGACY_EDGE_SPLIT, 0, "LEGACY_EDGE_SPLIT", LegacyEdgeSplit, "Edge Split", "") DefNode(GeometryNode, GEO_NODE_LEGACY_MATERIAL_ASSIGN, 0, "LEGACY_MATERIAL_ASSIGN", LegacyMaterialAssign, "Material Assign", "") @@ -310,22 +320,23 @@ DefNode(GeometryNode, GEO_NODE_LEGACY_POINT_ROTATE, def_geo_point_rotate, "LEGAC DefNode(GeometryNode, GEO_NODE_LEGACY_POINT_SCALE, def_geo_point_scale, "LEGACY_POINT_SCALE", LegacyPointScale, "Point Scale", "") DefNode(GeometryNode, GEO_NODE_LEGACY_POINT_SEPARATE, 0, "LEGACY_POINT_SEPARATE", LegacyPointSeparate, "Point Separate", "") DefNode(GeometryNode, GEO_NODE_LEGACY_POINT_TRANSLATE, def_geo_point_translate, "LEGACY_POINT_TRANSLATE", LegacyPointTranslate, "Point Translate", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_POINTS_TO_VOLUME, def_geo_points_to_volume, "LEGACY_POINTS_TO_VOLUME", LegacyPointsToVolume, "Points to Volume", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_RAYCAST, def_geo_raycast, "LEGACY_RAYCAST", LegacyRaycast, "Raycast", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_POINTS_TO_VOLUME, def_geo_legacy_points_to_volume, "LEGACY_POINTS_TO_VOLUME", LegacyPointsToVolume, "Points to Volume", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_RAYCAST, def_geo_legacy_raycast, "LEGACY_RAYCAST", LegacyRaycast, "Raycast", "") DefNode(GeometryNode, GEO_NODE_LEGACY_SELECT_BY_MATERIAL, 0, "LEGACY_SELECT_BY_MATERIAL", LegacySelectByMaterial, "Select by Material", "") DefNode(GeometryNode, GEO_NODE_LEGACY_SUBDIVISION_SURFACE, def_geo_subdivision_surface, "LEGACY_SUBDIVISION_SURFACE", LegacySubdivisionSurface, "Subdivision Surface", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_VOLUME_TO_MESH, def_geo_volume_to_mesh, "LEGACY_VOLUME_TO_MESH", LegacyVolumeToMesh, "Volume to Mesh", "") -DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_CAPTURE, def_geo_attribute_capture, "ATTRIBUTE_CAPTURE", AttributeCapture, "Attribute Capture", "") +DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_DOMAIN_SIZE, def_geo_attribute_domain_size, "ATTRIBUTE_DOMAIN_SIZE", AttributeDomainSize, "Domain Size", "") DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_REMOVE, 0, "ATTRIBUTE_REMOVE", AttributeRemove, "Attribute Remove", "") DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_STATISTIC, def_geo_attribute_statistic, "ATTRIBUTE_STATISTIC", AttributeStatistic, "Attribute Statistic", "") -DefNode(GeometryNode, GEO_NODE_BOOLEAN, def_geo_boolean, "BOOLEAN", Boolean, "Boolean", "") DefNode(GeometryNode, GEO_NODE_BOUNDING_BOX, 0, "BOUNDING_BOX", BoundBox, "Bounding Box", "") +DefNode(GeometryNode, GEO_NODE_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_FILL, def_geo_curve_fill, "CURVE_FILL", CurveFill, "Curve Fill", "") -DefNode(GeometryNode, GEO_NODE_CURVE_FILLET, def_geo_curve_fillet, "CURVE_FILLET", CurveFillet, "Curve Fillet", "") +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_PARAMETER, 0, "CURVE_PARAMETER", CurveParameter, "Curve Parameter", "") +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", "") @@ -333,23 +344,51 @@ DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_QUADRATIC_BEZIER, 0, "CURVE_PRIMI 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_RESAMPLE, def_geo_curve_resample, "CURVE_RESAMPLE", CurveResample, "Resample Curve", "") -DefNode(GeometryNode, GEO_NODE_CURVE_REVERSE, 0, "CURVE_REVERSE", CurveReverse, "Curve Reverse", "") -DefNode(GeometryNode, GEO_NODE_CURVE_SAMPLE, def_geo_curve_sample, "CURVE_SAMPLE", CurveSample, "Curve Sample", "") +DefNode(GeometryNode, GEO_NODE_CURVE_SET_HANDLES, def_geo_curve_set_handles, "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_TRIM, def_geo_curve_trim, "CURVE_TRIM", CurveTrim, "Curve Trim", "") +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_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_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_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_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_ASSIGN, 0, "MATERIAL_ASSIGN", MaterialAssign, "Material Assign", "") -DefNode(GeometryNode, GEO_NODE_MATERIAL_REPLACE, 0, "MATERIAL_REPLACE", MaterialReplace, "Material Replace", "") DefNode(GeometryNode, GEO_NODE_MATERIAL_SELECTION, 0, "MATERIAL_SELECTION", MaterialSelection, "Material Selection", "") +DefNode(GeometryNode, GEO_NODE_MERGE_BY_DISTANCE, 0, "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", "") @@ -358,20 +397,47 @@ DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_GRID, 0, "MESH_PRIMITIVE_GRID", Me 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_SUBDIVIDE, 0, "MESH_SUBDIVIDE", MeshSubdivide, "Mesh Subdivide", "") +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_REALIZE_INSTANCES, 0, "REALIZE_INSTANCES", RealizeInstances, "Realize Instances", "") +DefNode(GeometryNode, GEO_NODE_RAYCAST, def_geo_raycast, "RAYCAST", Raycast, "Raycast", "") +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_STRING_JOIN, 0, "STRING_JOIN", StringJoin, "String Join", "") +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_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_VIEWER, 0, "VIEWER", Viewer, "Viewer", "") +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", "") /* undefine macros */ diff --git a/source/blender/nodes/NOD_texture.h b/source/blender/nodes/NOD_texture.h index af59fefd925..c08bc814915 100644 --- a/source/blender/nodes/NOD_texture.h +++ b/source/blender/nodes/NOD_texture.h @@ -74,6 +74,22 @@ void register_node_type_tex_proc_noise(void); void register_node_type_tex_proc_stucci(void); void register_node_type_tex_proc_distnoise(void); +void ntreeTexCheckCyclics(struct bNodeTree *ntree); +struct bNodeTreeExec *ntreeTexBeginExecTree(struct bNodeTree *ntree); +void ntreeTexEndExecTree(struct bNodeTreeExec *exec); +int ntreeTexExecTree(struct bNodeTree *ntree, + struct TexResult *target, + const float co[3], + float dxt[3], + float dyt[3], + int osatex, + short thread, + const struct Tex *tex, + short which_output, + int cfra, + int preview, + struct MTex *mtex); + #ifdef __cplusplus } #endif diff --git a/source/blender/nodes/NOD_type_conversions.hh b/source/blender/nodes/NOD_type_conversions.hh deleted file mode 100644 index ec4859f0657..00000000000 --- a/source/blender/nodes/NOD_type_conversions.hh +++ /dev/null @@ -1,83 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#pragma once - -#include "FN_multi_function.hh" - -namespace blender::nodes { - -using fn::CPPType; -using fn::GVArray; - -struct ConversionFunctions { - const fn::MultiFunction *multi_function; - void (*convert_single_to_initialized)(const void *src, void *dst); - void (*convert_single_to_uninitialized)(const void *src, void *dst); -}; - -class DataTypeConversions { - private: - Map<std::pair<fn::MFDataType, fn::MFDataType>, ConversionFunctions> conversions_; - - public: - void add(fn::MFDataType from_type, - fn::MFDataType to_type, - const fn::MultiFunction &fn, - void (*convert_single_to_initialized)(const void *src, void *dst), - void (*convert_single_to_uninitialized)(const void *src, void *dst)) - { - conversions_.add_new({from_type, to_type}, - {&fn, convert_single_to_initialized, convert_single_to_uninitialized}); - } - - const ConversionFunctions *get_conversion_functions(fn::MFDataType from, fn::MFDataType to) const - { - return conversions_.lookup_ptr({from, to}); - } - - const ConversionFunctions *get_conversion_functions(const CPPType &from, const CPPType &to) const - { - return this->get_conversion_functions(fn::MFDataType::ForSingle(from), - fn::MFDataType::ForSingle(to)); - } - - const fn::MultiFunction *get_conversion_multi_function(fn::MFDataType from, - fn::MFDataType to) const - { - const ConversionFunctions *functions = this->get_conversion_functions(from, to); - return functions ? functions->multi_function : nullptr; - } - - bool is_convertible(const CPPType &from_type, const CPPType &to_type) const - { - return conversions_.contains( - {fn::MFDataType::ForSingle(from_type), fn::MFDataType::ForSingle(to_type)}); - } - - void convert_to_uninitialized(const CPPType &from_type, - const CPPType &to_type, - const void *from_value, - void *to_value) const; - - fn::GVArrayPtr try_convert(fn::GVArrayPtr varray, const CPPType &to_type) const; - - fn::GVMutableArrayPtr try_convert(fn::GVMutableArrayPtr varray, const CPPType &to_type) const; -}; - -const DataTypeConversions &get_implicit_type_conversions(); - -} // namespace blender::nodes diff --git a/source/blender/nodes/composite/CMakeLists.txt b/source/blender/nodes/composite/CMakeLists.txt new file mode 100644 index 00000000000..8d2b2befd1a --- /dev/null +++ b/source/blender/nodes/composite/CMakeLists.txt @@ -0,0 +1,163 @@ +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# The Original Code is Copyright (C) 2021, Blender Foundation +# All rights reserved. +# ***** END GPL LICENSE BLOCK ***** + +set(INC + . + .. + ../intern + ../../editors/include + ../../blenkernel + ../../blenlib + ../../blentranslation + ../../depsgraph + ../../imbuf + ../../makesdna + ../../makesrna + ../../render + ../../windowmanager + ../../../../intern/guardedalloc + + # dna_type_offsets.h + ${CMAKE_CURRENT_BINARY_DIR}/../../makesdna/intern +) + + +set(SRC + nodes/node_composite_alpha_over.cc + nodes/node_composite_antialiasing.cc + nodes/node_composite_bilateralblur.cc + nodes/node_composite_blur.cc + nodes/node_composite_bokehblur.cc + nodes/node_composite_bokehimage.cc + nodes/node_composite_boxmask.cc + nodes/node_composite_brightness.cc + nodes/node_composite_channel_matte.cc + nodes/node_composite_chroma_matte.cc + nodes/node_composite_color_matte.cc + nodes/node_composite_color_spill.cc + nodes/node_composite_colorbalance.cc + nodes/node_composite_colorcorrection.cc + nodes/node_composite_common.cc + nodes/node_composite_composite.cc + nodes/node_composite_convert_color_space.cc + nodes/node_composite_cornerpin.cc + nodes/node_composite_crop.cc + nodes/node_composite_cryptomatte.cc + nodes/node_composite_curves.cc + nodes/node_composite_defocus.cc + nodes/node_composite_denoise.cc + nodes/node_composite_despeckle.cc + nodes/node_composite_diff_matte.cc + nodes/node_composite_dilate.cc + nodes/node_composite_directionalblur.cc + nodes/node_composite_displace.cc + nodes/node_composite_distance_matte.cc + nodes/node_composite_double_edge_mask.cc + nodes/node_composite_ellipsemask.cc + nodes/node_composite_exposure.cc + nodes/node_composite_filter.cc + nodes/node_composite_flip.cc + nodes/node_composite_gamma.cc + nodes/node_composite_glare.cc + nodes/node_composite_hue_sat_val.cc + nodes/node_composite_huecorrect.cc + nodes/node_composite_id_mask.cc + nodes/node_composite_image.cc + nodes/node_composite_inpaint.cc + nodes/node_composite_invert.cc + nodes/node_composite_keying.cc + nodes/node_composite_keyingscreen.cc + nodes/node_composite_lensdist.cc + nodes/node_composite_levels.cc + nodes/node_composite_luma_matte.cc + nodes/node_composite_map_range.cc + nodes/node_composite_map_uv.cc + nodes/node_composite_map_value.cc + nodes/node_composite_mask.cc + nodes/node_composite_math.cc + nodes/node_composite_mixrgb.cc + nodes/node_composite_movieclip.cc + nodes/node_composite_moviedistortion.cc + nodes/node_composite_normal.cc + nodes/node_composite_normalize.cc + nodes/node_composite_output_file.cc + nodes/node_composite_pixelate.cc + nodes/node_composite_planetrackdeform.cc + nodes/node_composite_posterize.cc + nodes/node_composite_premulkey.cc + nodes/node_composite_rgb.cc + nodes/node_composite_rotate.cc + nodes/node_composite_scale.cc + nodes/node_composite_scene_time.cc + nodes/node_composite_sepcomb_hsva.cc + nodes/node_composite_sepcomb_rgba.cc + nodes/node_composite_sepcomb_ycca.cc + nodes/node_composite_sepcomb_yuva.cc + nodes/node_composite_setalpha.cc + nodes/node_composite_split_viewer.cc + nodes/node_composite_stabilize2d.cc + nodes/node_composite_sunbeams.cc + nodes/node_composite_switch.cc + nodes/node_composite_switchview.cc + nodes/node_composite_texture.cc + nodes/node_composite_tonemap.cc + nodes/node_composite_trackpos.cc + nodes/node_composite_transform.cc + nodes/node_composite_translate.cc + nodes/node_composite_val_to_rgb.cc + nodes/node_composite_value.cc + nodes/node_composite_vec_blur.cc + nodes/node_composite_viewer.cc + nodes/node_composite_zcombine.cc + + node_composite_tree.cc + node_composite_util.cc + + node_composite_util.hh +) + +if(WITH_INTERNATIONAL) + add_definitions(-DWITH_INTERNATIONAL) +endif() + +if(WITH_IMAGE_OPENEXR) + add_definitions(-DWITH_OPENEXR) +endif() + +if(WITH_COMPOSITOR) + list(APPEND INC + ../../compositor + ) + add_definitions(-DWITH_COMPOSITOR) +endif() + +if(WITH_OPENIMAGEDENOISE) + add_definitions(-DWITH_OPENIMAGEDENOISE) +endif() + +blender_add_lib(bf_nodes_composite "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") + +if(WITH_UNITY_BUILD) + set_target_properties(bf_nodes_composite PROPERTIES UNITY_BUILD ON) + set_target_properties(bf_nodes_composite PROPERTIES UNITY_BUILD_BATCH_SIZE 10) +endif() + +# Needed so we can use dna_type_offsets.h for defaults initialization. +add_dependencies(bf_nodes_composite bf_dna) diff --git a/source/blender/nodes/composite/node_composite_tree.cc b/source/blender/nodes/composite/node_composite_tree.cc index ea54673faee..c54382cc1ad 100644 --- a/source/blender/nodes/composite/node_composite_tree.cc +++ b/source/blender/nodes/composite/node_composite_tree.cc @@ -33,8 +33,11 @@ #include "BKE_global.h" #include "BKE_main.h" #include "BKE_node.h" +#include "BKE_node_tree_update.h" #include "BKE_tracking.h" +#include "UI_resources.h" + #include "node_common.h" #include "node_util.h" @@ -104,7 +107,7 @@ static void localize(bNodeTree *localtree, bNodeTree *ntree) local_node->original = node; /* move over the compbufs */ - /* right after ntreeCopyTree() oldsock pointers are valid */ + /* right after #ntreeCopyTree() `oldsock` pointers are valid */ if (ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) { if (node->id) { @@ -117,29 +120,11 @@ static void localize(bNodeTree *localtree, bNodeTree *ntree) } } - bNodeSocket *output_sock = (bNodeSocket *)node->outputs.first; - bNodeSocket *local_output_sock = (bNodeSocket *)local_node->outputs.first; - while (output_sock != nullptr) { - local_output_sock->cache = output_sock->cache; - output_sock->cache = nullptr; - /* This is actually link to original: someone was just lazy enough and tried to save few - * bytes in the cost of readability. */ - local_output_sock->new_sock = output_sock; - - output_sock = output_sock->next; - local_output_sock = local_output_sock->next; - } - node = node->next; local_node = local_node->next; } } -static void local_sync(bNodeTree *localtree, bNodeTree *ntree) -{ - BKE_node_preview_sync_tree(ntree, localtree); -} - static void local_merge(Main *bmain, bNodeTree *localtree, bNodeTree *ntree) { bNode *lnode; @@ -149,11 +134,11 @@ static void local_merge(Main *bmain, bNodeTree *localtree, bNodeTree *ntree) BKE_node_preview_merge_tree(ntree, localtree, true); for (lnode = (bNode *)localtree->nodes.first; lnode; lnode = lnode->next) { - if (ntreeNodeExists(ntree, lnode->new_node)) { + if (bNode *orig_node = nodeFindNodebyName(ntree, lnode->name)) { if (ELEM(lnode->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) { if (lnode->id && (lnode->flag & NODE_DO_OUTPUT)) { /* image_merge does sanity check for pointers */ - BKE_image_merge(bmain, (Image *)lnode->new_node->id, (Image *)lnode->id); + BKE_image_merge(bmain, (Image *)orig_node->id, (Image *)lnode->id); } } else if (lnode->type == CMP_NODE_MOVIEDISTORTION) { @@ -161,20 +146,19 @@ static void local_merge(Main *bmain, bNodeTree *localtree, bNodeTree *ntree) * and to achieve much better performance on further calls this context should be * copied back to original node */ if (lnode->storage) { - if (lnode->new_node->storage) { - BKE_tracking_distortion_free((MovieDistortion *)lnode->new_node->storage); + if (orig_node->storage) { + BKE_tracking_distortion_free((MovieDistortion *)orig_node->storage); } - lnode->new_node->storage = BKE_tracking_distortion_copy( - (MovieDistortion *)lnode->storage); + orig_node->storage = BKE_tracking_distortion_copy((MovieDistortion *)lnode->storage); } } for (lsock = (bNodeSocket *)lnode->outputs.first; lsock; lsock = lsock->next) { - if (ntreeOutputExists(lnode->new_node, lsock->new_sock)) { - lsock->new_sock->cache = lsock->cache; + if (bNodeSocket *orig_socket = nodeFindSocket(orig_node, SOCK_OUT, lsock->identifier)) { + orig_socket->cache = lsock->cache; lsock->cache = nullptr; - lsock->new_sock = nullptr; + orig_socket = nullptr; } } } @@ -186,11 +170,6 @@ static void update(bNodeTree *ntree) ntreeSetOutput(ntree); ntree_update_reroute_nodes(ntree); - - if (ntree->update & NTREE_UPDATE_NODES) { - /* clean up preview cache, in case nodes have been removed */ - BKE_node_preview_remove_unused(ntree); - } } static void composite_node_add_init(bNodeTree *UNUSED(bnodetree), bNode *bnode) @@ -212,22 +191,20 @@ static bool composite_node_tree_socket_type_valid(bNodeTreeType *UNUSED(ntreetyp bNodeTreeType *ntreeType_Composite; -void register_node_tree_type_cmp(void) +void register_node_tree_type_cmp() { - bNodeTreeType *tt = ntreeType_Composite = (bNodeTreeType *)MEM_callocN( - sizeof(bNodeTreeType), "compositor node tree type"); + bNodeTreeType *tt = ntreeType_Composite = MEM_cnew<bNodeTreeType>(__func__); tt->type = NTREE_COMPOSIT; strcpy(tt->idname, "CompositorNodeTree"); strcpy(tt->ui_name, N_("Compositor")); - tt->ui_icon = 0; /* defined in drawnode.c */ + tt->ui_icon = ICON_NODE_COMPOSITING; strcpy(tt->ui_description, N_("Compositing nodes")); tt->free_cache = free_cache; tt->free_node_cache = free_node_cache; tt->foreach_nodeclass = foreach_nodeclass; tt->localize = localize; - tt->local_sync = local_sync; tt->local_merge = local_merge; tt->update = update; tt->get_from_context = composite_get_from_context; @@ -259,16 +236,6 @@ void ntreeCompositExecTree(Scene *scene, /* *********************************************** */ -/** - * Update the outputs of the render layer nodes. - * Since the outputs depend on the render engine, this part is a bit complex: - * - #ntreeCompositUpdateRLayers is called and loops over all render layer nodes. - * - Each render layer node calls the update function of the - * render engine that's used for its scene. - * - The render engine calls RE_engine_register_pass for each pass. - * - #RE_engine_register_pass calls #ntreeCompositRegisterPass, - * which calls #node_cmp_rlayers_register_pass for every render layer node. - */ void ntreeCompositUpdateRLayers(bNodeTree *ntree) { if (ntree == nullptr) { @@ -282,25 +249,6 @@ void ntreeCompositUpdateRLayers(bNodeTree *ntree) } } -void ntreeCompositRegisterPass(bNodeTree *ntree, - Scene *scene, - ViewLayer *view_layer, - const char *name, - eNodeSocketDatatype type) -{ - if (ntree == nullptr) { - return; - } - - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (node->type == CMP_NODE_R_LAYERS) { - node_cmp_rlayers_register_pass(ntree, node, scene, view_layer, name, type); - } - } -} - -/* called from render pipeline, to tag render input and output */ -/* need to do all scenes, to prevent errors when you re-render 1 scene */ void ntreeCompositTagRender(Scene *scene) { /* XXX Think using G_MAIN here is valid, since you want to update current file's scene nodes, @@ -313,14 +261,15 @@ void ntreeCompositTagRender(Scene *scene) if (sce_iter->nodetree) { LISTBASE_FOREACH (bNode *, node, &sce_iter->nodetree->nodes) { if (node->id == (ID *)scene || node->type == CMP_NODE_COMPOSITE) { - nodeUpdate(sce_iter->nodetree, node); + BKE_ntree_update_tag_node_property(sce_iter->nodetree, node); } else if (node->type == CMP_NODE_TEXTURE) /* uses scene size_x/size_y */ { - nodeUpdate(sce_iter->nodetree, node); + BKE_ntree_update_tag_node_property(sce_iter->nodetree, node); } } } } + BKE_ntree_update_main(G_MAIN, nullptr); } /* XXX after render animation system gets a refresh, this call allows composite to end clean */ diff --git a/source/blender/nodes/composite/node_composite_util.cc b/source/blender/nodes/composite/node_composite_util.cc index 86aaec61bc3..1f892c1c9e2 100644 --- a/source/blender/nodes/composite/node_composite_util.cc +++ b/source/blender/nodes/composite/node_composite_util.cc @@ -21,6 +21,8 @@ * \ingroup nodes */ +#include "NOD_socket_search_link.hh" + #include "node_composite_util.hh" bool cmp_node_poll_default(bNodeType *UNUSED(ntype), @@ -28,7 +30,7 @@ bool cmp_node_poll_default(bNodeType *UNUSED(ntype), const char **r_disabled_hint) { if (!STREQ(ntree->idname, "CompositorNodeTree")) { - *r_disabled_hint = "Not a compositor node tree"; + *r_disabled_hint = TIP_("Not a compositor node tree"); return false; } return true; @@ -45,12 +47,12 @@ void cmp_node_update_default(bNodeTree *UNUSED(ntree), bNode *node) node->need_exec = 1; } -void cmp_node_type_base(bNodeType *ntype, int type, const char *name, short nclass, short flag) +void cmp_node_type_base(bNodeType *ntype, int type, const char *name, short nclass) { - node_type_base(ntype, type, name, nclass, flag); + node_type_base(ntype, type, name, nclass); ntype->poll = cmp_node_poll_default; ntype->updatefunc = cmp_node_update_default; ntype->insert_link = node_insert_link_default; - ntype->update_internal_links = node_update_internal_links_default; + ntype->gather_link_search_ops = blender::nodes::search_link_ops_for_basic_node; } diff --git a/source/blender/nodes/composite/node_composite_util.hh b/source/blender/nodes/composite/node_composite_util.hh index 6fd82ffc93f..65dbc2065ef 100644 --- a/source/blender/nodes/composite/node_composite_util.hh +++ b/source/blender/nodes/composite/node_composite_util.hh @@ -27,9 +27,6 @@ #include "DNA_movieclip_types.h" #include "DNA_node_types.h" -#include "BLI_blenlib.h" -#include "BLI_math.h" - #include "BLT_translation.h" #include "BKE_colorband.h" @@ -45,8 +42,8 @@ #include "RE_pipeline.h" -/* only for forward declarations */ #include "NOD_composite.h" +#include "NOD_socket.h" #include "NOD_socket_declarations.hh" #define CMP_SCALE_MAX 12000 @@ -55,5 +52,4 @@ bool cmp_node_poll_default(struct bNodeType *ntype, struct bNodeTree *ntree, const char **r_disabled_hint); void cmp_node_update_default(struct bNodeTree *ntree, struct bNode *node); -void cmp_node_type_base( - struct bNodeType *ntype, int type, const char *name, short nclass, short flag); +void cmp_node_type_base(struct bNodeType *ntype, int type, const char *name, short nclass); diff --git a/source/blender/nodes/composite/nodes/node_composite_alphaOver.cc b/source/blender/nodes/composite/nodes/node_composite_alpha_over.cc index 6210d946bc7..a080b7d4840 100644 --- a/source/blender/nodes/composite/nodes/node_composite_alphaOver.cc +++ b/source/blender/nodes/composite/nodes/node_composite_alpha_over.cc @@ -21,34 +21,49 @@ * \ingroup cmpnodes */ +#include "UI_interface.h" +#include "UI_resources.h" + #include "node_composite_util.hh" /* **************** ALPHAOVER ******************** */ -namespace blender::nodes { +namespace blender::nodes::node_composite_alpha_over_cc { static void cmp_node_alphaover_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Float>("Fac").default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input<decl::Color>("Image", "Image_001").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_output<decl::Color>("Image"); + 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_output<decl::Color>(N_("Image")); } -} // namespace blender::nodes - static void node_alphaover_init(bNodeTree *UNUSED(ntree), bNode *node) { - node->storage = MEM_callocN(sizeof(NodeTwoFloats), "NodeTwoFloats"); + node->storage = MEM_cnew<NodeTwoFloats>(__func__); +} + +static void node_composit_buts_alphaover(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayout *col; + + col = uiLayoutColumn(layout, true); + uiItemR(col, ptr, "use_premultiply", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(col, ptr, "premul", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } -void register_node_type_cmp_alphaover(void) +} // namespace blender::nodes::node_composite_alpha_over_cc + +void register_node_type_cmp_alphaover() { + namespace file_ns = blender::nodes::node_composite_alpha_over_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_ALPHAOVER, "Alpha Over", NODE_CLASS_OP_COLOR, 0); - ntype.declare = blender::nodes::cmp_node_alphaover_declare; - node_type_init(&ntype, node_alphaover_init); + cmp_node_type_base(&ntype, CMP_NODE_ALPHAOVER, "Alpha Over", NODE_CLASS_OP_COLOR); + ntype.declare = file_ns::cmp_node_alphaover_declare; + ntype.draw_buttons = file_ns::node_composit_buts_alphaover; + node_type_init(&ntype, file_ns::node_alphaover_init); node_type_storage( &ntype, "NodeTwoFloats", node_free_standard_storage, node_copy_standard_storage); diff --git a/source/blender/nodes/composite/nodes/node_composite_antialiasing.cc b/source/blender/nodes/composite/nodes/node_composite_antialiasing.cc index 23e63b9a53b..fcc04a85b38 100644 --- a/source/blender/nodes/composite/nodes/node_composite_antialiasing.cc +++ b/source/blender/nodes/composite/nodes/node_composite_antialiasing.cc @@ -23,19 +23,24 @@ * \ingroup cmpnodes */ +#include "UI_interface.h" +#include "UI_resources.h" + #include "node_composite_util.hh" /* **************** Anti-Aliasing (SMAA 1x) ******************** */ -static bNodeSocketTemplate cmp_node_antialiasing_in[] = { - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, {-1, ""}}; +namespace blender::nodes::node_composite_antialiasing_cc { -static bNodeSocketTemplate cmp_node_antialiasing_out[] = {{SOCK_RGBA, N_("Image")}, {-1, ""}}; +static void cmp_node_antialiasing_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output<decl::Color>(N_("Image")); +} static void node_composit_init_antialiasing(bNodeTree *UNUSED(ntree), bNode *node) { - NodeAntiAliasingData *data = (NodeAntiAliasingData *)MEM_callocN(sizeof(NodeAntiAliasingData), - "node antialiasing data"); + NodeAntiAliasingData *data = MEM_cnew<NodeAntiAliasingData>(__func__); data->threshold = CMP_DEFAULT_SMAA_THRESHOLD; data->contrast_limit = CMP_DEFAULT_SMAA_CONTRAST_LIMIT; @@ -44,15 +49,31 @@ static void node_composit_init_antialiasing(bNodeTree *UNUSED(ntree), bNode *nod node->storage = data; } -void register_node_type_cmp_antialiasing(void) +static void node_composit_buts_antialiasing(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayout *col; + + col = uiLayoutColumn(layout, false); + + uiItemR(col, ptr, "threshold", 0, nullptr, ICON_NONE); + uiItemR(col, ptr, "contrast_limit", 0, nullptr, ICON_NONE); + uiItemR(col, ptr, "corner_rounding", 0, nullptr, ICON_NONE); +} + +} // namespace blender::nodes::node_composite_antialiasing_cc + +void register_node_type_cmp_antialiasing() { + namespace file_ns = blender::nodes::node_composite_antialiasing_cc; + static bNodeType ntype; - cmp_node_type_base( - &ntype, CMP_NODE_ANTIALIASING, "Anti-Aliasing", NODE_CLASS_OP_FILTER, NODE_PREVIEW); - node_type_socket_templates(&ntype, cmp_node_antialiasing_in, cmp_node_antialiasing_out); + cmp_node_type_base(&ntype, CMP_NODE_ANTIALIASING, "Anti-Aliasing", NODE_CLASS_OP_FILTER); + ntype.declare = file_ns::cmp_node_antialiasing_declare; + ntype.draw_buttons = file_ns::node_composit_buts_antialiasing; + ntype.flag |= NODE_PREVIEW; node_type_size(&ntype, 170, 140, 200); - node_type_init(&ntype, node_composit_init_antialiasing); + node_type_init(&ntype, file_ns::node_composit_init_antialiasing); node_type_storage( &ntype, "NodeAntiAliasingData", node_free_standard_storage, node_copy_standard_storage); diff --git a/source/blender/nodes/composite/nodes/node_composite_bilateralblur.cc b/source/blender/nodes/composite/nodes/node_composite_bilateralblur.cc index 3e724d17a10..1c3303103f8 100644 --- a/source/blender/nodes/composite/nodes/node_composite_bilateralblur.cc +++ b/source/blender/nodes/composite/nodes/node_composite_bilateralblur.cc @@ -21,36 +21,55 @@ * \ingroup cmpnodes */ +#include "UI_interface.h" +#include "UI_resources.h" + #include "node_composite_util.hh" /* **************** BILATERALBLUR ******************** */ -static bNodeSocketTemplate cmp_node_bilateralblur_in[] = { - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, - {SOCK_RGBA, N_("Determinator"), 1.0f, 1.0f, 1.0f, 1.0f}, - {-1, ""}}; -static bNodeSocketTemplate cmp_node_bilateralblur_out[] = { - {SOCK_RGBA, N_("Image")}, - {-1, ""}, -}; +namespace blender::nodes::node_composite_bilateralblur_cc { + +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_output<decl::Color>(N_("Image")); +} static void node_composit_init_bilateralblur(bNodeTree *UNUSED(ntree), bNode *node) { - NodeBilateralBlurData *nbbd = (NodeBilateralBlurData *)MEM_callocN(sizeof(NodeBilateralBlurData), - "node bilateral blur data"); + NodeBilateralBlurData *nbbd = MEM_cnew<NodeBilateralBlurData>(__func__); node->storage = nbbd; nbbd->iter = 1; nbbd->sigma_color = 0.3; nbbd->sigma_space = 5.0; } -void register_node_type_cmp_bilateralblur(void) +static void node_composit_buts_bilateralblur(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiLayout *col; + + col = uiLayoutColumn(layout, true); + uiItemR(col, ptr, "iterations", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(col, ptr, "sigma_color", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(col, ptr, "sigma_space", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); +} + +} // namespace blender::nodes::node_composite_bilateralblur_cc + +void register_node_type_cmp_bilateralblur() { + namespace file_ns = blender::nodes::node_composite_bilateralblur_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_BILATERALBLUR, "Bilateral Blur", NODE_CLASS_OP_FILTER, 0); - node_type_socket_templates(&ntype, cmp_node_bilateralblur_in, cmp_node_bilateralblur_out); - node_type_init(&ntype, node_composit_init_bilateralblur); + cmp_node_type_base(&ntype, CMP_NODE_BILATERALBLUR, "Bilateral Blur", NODE_CLASS_OP_FILTER); + ntype.declare = file_ns::cmp_node_bilateralblur_declare; + ntype.draw_buttons = file_ns::node_composit_buts_bilateralblur; + node_type_init(&ntype, file_ns::node_composit_init_bilateralblur); node_type_storage( &ntype, "NodeBilateralBlurData", node_free_standard_storage, node_copy_standard_storage); diff --git a/source/blender/nodes/composite/nodes/node_composite_blur.cc b/source/blender/nodes/composite/nodes/node_composite_blur.cc index c5c0c21929e..dd0a6db74c1 100644 --- a/source/blender/nodes/composite/nodes/node_composite_blur.cc +++ b/source/blender/nodes/composite/nodes/node_composite_blur.cc @@ -22,29 +22,85 @@ * \ingroup cmpnodes */ +#include "RNA_access.h" + +#include "UI_interface.h" +#include "UI_resources.h" + #include "node_composite_util.hh" /* **************** BLUR ******************** */ -static bNodeSocketTemplate cmp_node_blur_in[] = { - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, - {SOCK_FLOAT, N_("Size"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE}, - {-1, ""}}; -static bNodeSocketTemplate cmp_node_blur_out[] = {{SOCK_RGBA, N_("Image")}, {-1, ""}}; + +namespace blender::nodes::node_composite_blur_cc { + +static void cmp_node_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::Float>(N_("Size")).default_value(1.0f).min(0.0f).max(1.0f); + b.add_output<decl::Color>(N_("Image")); +} static void node_composit_init_blur(bNodeTree *UNUSED(ntree), bNode *node) { - NodeBlurData *data = (NodeBlurData *)MEM_callocN(sizeof(NodeBlurData), "node blur data"); + NodeBlurData *data = MEM_cnew<NodeBlurData>(__func__); data->filtertype = R_FILTER_GAUSS; node->storage = data; } -void register_node_type_cmp_blur(void) +static void node_composit_buts_blur(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayout *col, *row; + + col = uiLayoutColumn(layout, false); + const int filter = RNA_enum_get(ptr, "filter_type"); + const int reference = RNA_boolean_get(ptr, "use_variable_size"); + + uiItemR(col, ptr, "filter_type", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); + if (filter != R_FILTER_FAST_GAUSS) { + uiItemR(col, ptr, "use_variable_size", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + if (!reference) { + uiItemR(col, ptr, "use_bokeh", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + } + uiItemR(col, ptr, "use_gamma_correction", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + } + + uiItemR(col, ptr, "use_relative", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + + if (RNA_boolean_get(ptr, "use_relative")) { + uiItemL(col, IFACE_("Aspect Correction"), ICON_NONE); + row = uiLayoutRow(layout, true); + uiItemR(row, + ptr, + "aspect_correction", + UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_EXPAND, + nullptr, + ICON_NONE); + + col = uiLayoutColumn(layout, true); + uiItemR(col, ptr, "factor_x", UI_ITEM_R_SPLIT_EMPTY_NAME, IFACE_("X"), ICON_NONE); + uiItemR(col, ptr, "factor_y", UI_ITEM_R_SPLIT_EMPTY_NAME, IFACE_("Y"), ICON_NONE); + } + else { + col = uiLayoutColumn(layout, true); + uiItemR(col, ptr, "size_x", UI_ITEM_R_SPLIT_EMPTY_NAME, IFACE_("X"), ICON_NONE); + uiItemR(col, ptr, "size_y", UI_ITEM_R_SPLIT_EMPTY_NAME, IFACE_("Y"), ICON_NONE); + } + uiItemR(col, ptr, "use_extended_bounds", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); +} + +} // namespace blender::nodes::node_composite_blur_cc + +void register_node_type_cmp_blur() { + namespace file_ns = blender::nodes::node_composite_blur_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_BLUR, "Blur", NODE_CLASS_OP_FILTER, NODE_PREVIEW); - node_type_socket_templates(&ntype, cmp_node_blur_in, cmp_node_blur_out); - node_type_init(&ntype, node_composit_init_blur); + cmp_node_type_base(&ntype, CMP_NODE_BLUR, "Blur", NODE_CLASS_OP_FILTER); + ntype.declare = file_ns::cmp_node_blur_declare; + ntype.draw_buttons = file_ns::node_composit_buts_blur; + ntype.flag |= NODE_PREVIEW; + node_type_init(&ntype, file_ns::node_composit_init_blur); node_type_storage( &ntype, "NodeBlurData", node_free_standard_storage, node_copy_standard_storage); diff --git a/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc b/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc index f130a642e20..282328b5e10 100644 --- a/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc +++ b/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc @@ -22,18 +22,23 @@ * \ingroup cmpnodes */ -#include "../node_composite_util.hh" +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_composite_util.hh" /* **************** BLUR ******************** */ -static bNodeSocketTemplate cmp_node_bokehblur_in[] = { - {SOCK_RGBA, N_("Image"), 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f}, - {SOCK_RGBA, N_("Bokeh"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Size"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 10.0f}, - {SOCK_FLOAT, N_("Bounding box"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f}, - {-1, ""}}; -static bNodeSocketTemplate cmp_node_bokehblur_out[] = { - {SOCK_RGBA, N_("Image"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f}, {-1, ""}}; +namespace blender::nodes::node_composite_bokehblur_cc { + +static void cmp_node_bokehblur_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Color>(N_("Image")).default_value({0.8f, 0.8f, 0.8f, 1.0f}); + b.add_input<decl::Color>(N_("Bokeh")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Float>(N_("Size")).default_value(1.0f).min(0.0f).max(10.0f); + b.add_input<decl::Float>(N_("Bounding box")).default_value(1.0f).min(0.0f).max(1.0f); + b.add_output<decl::Color>(N_("Image")); +} static void node_composit_init_bokehblur(bNodeTree *UNUSED(ntree), bNode *node) { @@ -41,13 +46,26 @@ static void node_composit_init_bokehblur(bNodeTree *UNUSED(ntree), bNode *node) node->custom4 = 16.0f; } -void register_node_type_cmp_bokehblur(void) +static void node_composit_buts_bokehblur(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { + uiItemR(layout, ptr, "use_variable_size", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + // uiItemR(layout, ptr, "f_stop", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); /* UNUSED */ + uiItemR(layout, ptr, "blur_max", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(layout, ptr, "use_extended_bounds", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); +} + +} // namespace blender::nodes::node_composite_bokehblur_cc + +void register_node_type_cmp_bokehblur() +{ + namespace file_ns = blender::nodes::node_composite_bokehblur_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_BOKEHBLUR, "Bokeh Blur", NODE_CLASS_OP_FILTER, 0); - node_type_socket_templates(&ntype, cmp_node_bokehblur_in, cmp_node_bokehblur_out); - node_type_init(&ntype, node_composit_init_bokehblur); + cmp_node_type_base(&ntype, CMP_NODE_BOKEHBLUR, "Bokeh Blur", NODE_CLASS_OP_FILTER); + 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); 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 3a4bf94d256..df502bc625f 100644 --- a/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc +++ b/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc @@ -21,22 +21,23 @@ * \ingroup cmpnodes */ -#include "../node_composite_util.hh" +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_composite_util.hh" /* **************** Bokeh image Tools ******************** */ -namespace blender::nodes { +namespace blender::nodes::node_composite_bokehimage_cc { static void cmp_node_bokehimage_declare(NodeDeclarationBuilder &b) { - b.add_output<decl::Color>("Image"); + b.add_output<decl::Color>(N_("Image")); } -} // namespace blender::nodes - static void node_composit_init_bokehimage(bNodeTree *UNUSED(ntree), bNode *node) { - NodeBokehImage *data = (NodeBokehImage *)MEM_callocN(sizeof(NodeBokehImage), "NodeBokehImage"); + NodeBokehImage *data = MEM_cnew<NodeBokehImage>(__func__); data->angle = 0.0f; data->flaps = 5; data->rounding = 0.0f; @@ -45,13 +46,34 @@ static void node_composit_init_bokehimage(bNodeTree *UNUSED(ntree), bNode *node) node->storage = data; } -void register_node_type_cmp_bokehimage(void) +static void node_composit_buts_bokehimage(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { + uiItemR(layout, ptr, "flaps", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(layout, ptr, "angle", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR( + layout, ptr, "rounding", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(layout, + ptr, + "catadioptric", + UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, + nullptr, + ICON_NONE); + uiItemR(layout, ptr, "shift", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); +} + +} // namespace blender::nodes::node_composite_bokehimage_cc + +void register_node_type_cmp_bokehimage() +{ + namespace file_ns = blender::nodes::node_composite_bokehimage_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_BOKEHIMAGE, "Bokeh Image", NODE_CLASS_INPUT, NODE_PREVIEW); - ntype.declare = blender::nodes::cmp_node_bokehimage_declare; - node_type_init(&ntype, node_composit_init_bokehimage); + cmp_node_type_base(&ntype, CMP_NODE_BOKEHIMAGE, "Bokeh Image", NODE_CLASS_INPUT); + ntype.declare = file_ns::cmp_node_bokehimage_declare; + ntype.draw_buttons = file_ns::node_composit_buts_bokehimage; + ntype.flag |= NODE_PREVIEW; + node_type_init(&ntype, file_ns::node_composit_init_bokehimage); node_type_storage( &ntype, "NodeBokehImage", node_free_standard_storage, node_copy_standard_storage); diff --git a/source/blender/nodes/composite/nodes/node_composite_boxmask.cc b/source/blender/nodes/composite/nodes/node_composite_boxmask.cc index cdf96065f97..499942725c2 100644 --- a/source/blender/nodes/composite/nodes/node_composite_boxmask.cc +++ b/source/blender/nodes/composite/nodes/node_composite_boxmask.cc @@ -21,20 +21,25 @@ * \ingroup cmpnodes */ -#include "../node_composite_util.hh" +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_composite_util.hh" /* **************** SCALAR MATH ******************** */ -static bNodeSocketTemplate cmp_node_boxmask_in[] = { - {SOCK_FLOAT, N_("Mask"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Value"), 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f}, - {-1, ""}}; -static bNodeSocketTemplate cmp_node_boxmask_out[] = { - {SOCK_FLOAT, N_("Mask"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f}, {-1, ""}}; +namespace blender::nodes::node_composite_boxmask_cc { + +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); + b.add_input<decl::Float>(N_("Value")).default_value(1.0f).min(0.0f).max(1.0f); + b.add_output<decl::Float>(N_("Mask")); +} static void node_composit_init_boxmask(bNodeTree *UNUSED(ntree), bNode *node) { - NodeBoxMask *data = (NodeBoxMask *)MEM_callocN(sizeof(NodeBoxMask), "NodeBoxMask"); + NodeBoxMask *data = MEM_cnew<NodeBoxMask>(__func__); data->x = 0.5; data->y = 0.5; data->width = 0.2; @@ -43,13 +48,34 @@ static void node_composit_init_boxmask(bNodeTree *UNUSED(ntree), bNode *node) node->storage = data; } -void register_node_type_cmp_boxmask(void) +static void node_composit_buts_boxmask(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayout *row; + + row = uiLayoutRow(layout, true); + uiItemR(row, ptr, "x", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(row, ptr, "y", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + + row = uiLayoutRow(layout, true); + uiItemR(row, ptr, "width", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(row, ptr, "height", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + + uiItemR(layout, ptr, "rotation", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(layout, ptr, "mask_type", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); +} + +} // namespace blender::nodes::node_composite_boxmask_cc + +void register_node_type_cmp_boxmask() { + namespace file_ns = blender::nodes::node_composite_boxmask_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_MASK_BOX, "Box Mask", NODE_CLASS_MATTE, 0); - node_type_socket_templates(&ntype, cmp_node_boxmask_in, cmp_node_boxmask_out); - node_type_init(&ntype, node_composit_init_boxmask); + cmp_node_type_base(&ntype, CMP_NODE_MASK_BOX, "Box Mask", NODE_CLASS_MATTE); + ntype.declare = file_ns::cmp_node_boxmask_declare; + 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); 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 ad4b09c69d0..7f60187dddf 100644 --- a/source/blender/nodes/composite/nodes/node_composite_brightness.cc +++ b/source/blender/nodes/composite/nodes/node_composite_brightness.cc @@ -21,34 +21,47 @@ * \ingroup cmpnodes */ +#include "UI_interface.h" +#include "UI_resources.h" + #include "node_composite_util.hh" /* **************** Bright and Contrast ******************** */ -namespace blender::nodes { +namespace blender::nodes::node_composite_brightness_cc { static void cmp_node_brightcontrast_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input<decl::Float>("Bright").min(-100.0f).max(100.0f); - b.add_input<decl::Float>("Contrast").min(-100.0f).max(100.0f); - b.add_output<decl::Color>("Image"); + 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_output<decl::Color>(N_("Image")); } -} // namespace blender::nodes - static void node_composit_init_brightcontrast(bNodeTree *UNUSED(ntree), bNode *node) { node->custom1 = 1; } -void register_node_type_cmp_brightcontrast(void) +static void node_composit_buts_brightcontrast(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) { + uiItemR(layout, ptr, "use_premultiply", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); +} + +} // namespace blender::nodes::node_composite_brightness_cc + +void register_node_type_cmp_brightcontrast() +{ + namespace file_ns = blender::nodes::node_composite_brightness_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_BRIGHTCONTRAST, "Bright/Contrast", NODE_CLASS_OP_COLOR, 0); - ntype.declare = blender::nodes::cmp_node_brightcontrast_declare; - node_type_init(&ntype, node_composit_init_brightcontrast); + cmp_node_type_base(&ntype, CMP_NODE_BRIGHTCONTRAST, "Bright/Contrast", NODE_CLASS_OP_COLOR); + 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); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_channelMatte.cc b/source/blender/nodes/composite/nodes/node_composite_channelMatte.cc deleted file mode 100644 index e211bc45b17..00000000000 --- a/source/blender/nodes/composite/nodes/node_composite_channelMatte.cc +++ /dev/null @@ -1,64 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2006 Blender Foundation. - * All rights reserved. - */ - -/** \file - * \ingroup cmpnodes - */ - -#include "node_composite_util.hh" - -/* ******************* Channel Matte Node ********************************* */ -static bNodeSocketTemplate cmp_node_channel_matte_in[] = { - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, - {-1, ""}, -}; - -static bNodeSocketTemplate cmp_node_channel_matte_out[] = { - {SOCK_RGBA, N_("Image")}, - {SOCK_FLOAT, N_("Matte")}, - {-1, ""}, -}; - -static void node_composit_init_channel_matte(bNodeTree *UNUSED(ntree), bNode *node) -{ - NodeChroma *c = (NodeChroma *)MEM_callocN(sizeof(NodeChroma), "node chroma"); - node->storage = c; - c->t1 = 1.0f; - c->t2 = 0.0f; - c->t3 = 0.0f; - c->fsize = 0.0f; - c->fstrength = 0.0f; - c->algorithm = 1; /* Max channel limiting. */ - c->channel = 1; /* Limit by red. */ - node->custom1 = 1; /* RGB channel. */ - node->custom2 = 2; /* Green Channel. */ -} - -void register_node_type_cmp_channel_matte(void) -{ - static bNodeType ntype; - - cmp_node_type_base( - &ntype, CMP_NODE_CHANNEL_MATTE, "Channel Key", NODE_CLASS_MATTE, NODE_PREVIEW); - node_type_socket_templates(&ntype, cmp_node_channel_matte_in, cmp_node_channel_matte_out); - node_type_init(&ntype, node_composit_init_channel_matte); - node_type_storage(&ntype, "NodeChroma", node_free_standard_storage, node_copy_standard_storage); - - 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 new file mode 100644 index 00000000000..a53e6a97dae --- /dev/null +++ b/source/blender/nodes/composite/nodes/node_composite_channel_matte.cc @@ -0,0 +1,114 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2006 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup cmpnodes + */ + +#include "RNA_access.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_composite_util.hh" + +/* ******************* Channel Matte Node ********************************* */ + +namespace blender::nodes::node_composite_channel_matte_cc { + +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_output<decl::Color>(N_("Image")); + b.add_output<decl::Float>(N_("Matte")); +} + +static void node_composit_init_channel_matte(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeChroma *c = MEM_cnew<NodeChroma>(__func__); + node->storage = c; + c->t1 = 1.0f; + c->t2 = 0.0f; + c->t3 = 0.0f; + c->fsize = 0.0f; + c->fstrength = 0.0f; + c->algorithm = 1; /* Max channel limiting. */ + c->channel = 1; /* Limit by red. */ + node->custom1 = 1; /* RGB channel. */ + node->custom2 = 2; /* Green Channel. */ +} + +static void node_composit_buts_channel_matte(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiLayout *col, *row; + + uiItemL(layout, IFACE_("Color Space:"), ICON_NONE); + row = uiLayoutRow(layout, false); + uiItemR( + row, ptr, "color_space", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_EXPAND, nullptr, ICON_NONE); + + col = uiLayoutColumn(layout, false); + uiItemL(col, IFACE_("Key Channel:"), ICON_NONE); + row = uiLayoutRow(col, false); + uiItemR(row, + ptr, + "matte_channel", + UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_EXPAND, + nullptr, + ICON_NONE); + + col = uiLayoutColumn(layout, false); + + uiItemR(col, ptr, "limit_method", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + if (RNA_enum_get(ptr, "limit_method") == 0) { + uiItemL(col, IFACE_("Limiting Channel:"), ICON_NONE); + row = uiLayoutRow(col, false); + uiItemR(row, + ptr, + "limit_channel", + UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_EXPAND, + nullptr, + ICON_NONE); + } + + uiItemR( + col, ptr, "limit_max", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR( + col, ptr, "limit_min", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); +} + +} // namespace blender::nodes::node_composite_channel_matte_cc + +void register_node_type_cmp_channel_matte() +{ + namespace file_ns = blender::nodes::node_composite_channel_matte_cc; + + static bNodeType ntype; + + cmp_node_type_base(&ntype, CMP_NODE_CHANNEL_MATTE, "Channel Key", NODE_CLASS_MATTE); + ntype.declare = file_ns::cmp_node_channel_matte_declare; + ntype.draw_buttons = file_ns::node_composit_buts_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); + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/composite/nodes/node_composite_chromaMatte.cc b/source/blender/nodes/composite/nodes/node_composite_chromaMatte.cc deleted file mode 100644 index 990778160df..00000000000 --- a/source/blender/nodes/composite/nodes/node_composite_chromaMatte.cc +++ /dev/null @@ -1,60 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2006 Blender Foundation. - * All rights reserved. - */ - -/** \file - * \ingroup cmpnodes - */ - -#include "node_composite_util.hh" - -/* ******************* Chroma Key ********************************************************** */ -static bNodeSocketTemplate cmp_node_chroma_in[] = { - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, - {SOCK_RGBA, N_("Key Color"), 1.0f, 1.0f, 1.0f, 1.0f}, - {-1, ""}, -}; - -static bNodeSocketTemplate cmp_node_chroma_out[] = { - {SOCK_RGBA, N_("Image")}, - {SOCK_FLOAT, N_("Matte")}, - {-1, ""}, -}; - -static void node_composit_init_chroma_matte(bNodeTree *UNUSED(ntree), bNode *node) -{ - NodeChroma *c = (NodeChroma *)MEM_callocN(sizeof(NodeChroma), "node chroma"); - node->storage = c; - c->t1 = DEG2RADF(30.0f); - c->t2 = DEG2RADF(10.0f); - c->t3 = 0.0f; - c->fsize = 0.0f; - c->fstrength = 1.0f; -} - -void register_node_type_cmp_chroma_matte(void) -{ - static bNodeType ntype; - - cmp_node_type_base(&ntype, CMP_NODE_CHROMA_MATTE, "Chroma Key", NODE_CLASS_MATTE, NODE_PREVIEW); - node_type_socket_templates(&ntype, cmp_node_chroma_in, cmp_node_chroma_out); - node_type_init(&ntype, node_composit_init_chroma_matte); - node_type_storage(&ntype, "NodeChroma", node_free_standard_storage, node_copy_standard_storage); - - 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 new file mode 100644 index 00000000000..a85cdd05b14 --- /dev/null +++ b/source/blender/nodes/composite/nodes/node_composite_chroma_matte.cc @@ -0,0 +1,84 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2006 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup cmpnodes + */ + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_composite_util.hh" + +/* ******************* Chroma Key ********************************************************** */ + +namespace blender::nodes::node_composite_chroma_matte_cc { + +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_output<decl::Color>(N_("Image")); + b.add_output<decl::Float>(N_("Matte")); +} + +static void node_composit_init_chroma_matte(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeChroma *c = MEM_cnew<NodeChroma>(__func__); + node->storage = c; + c->t1 = DEG2RADF(30.0f); + c->t2 = DEG2RADF(10.0f); + c->t3 = 0.0f; + c->fsize = 0.0f; + c->fstrength = 1.0f; +} + +static void node_composit_buts_chroma_matte(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayout *col; + + col = uiLayoutColumn(layout, false); + uiItemR(col, ptr, "tolerance", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(col, ptr, "threshold", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + + col = uiLayoutColumn(layout, true); + /* Removed for now. */ + // uiItemR(col, ptr, "lift", UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(col, ptr, "gain", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + /* Removed for now. */ + // uiItemR(col, ptr, "shadow_adjust", UI_ITEM_R_SLIDER, nullptr, ICON_NONE); +} + +} // namespace blender::nodes::node_composite_chroma_matte_cc + +void register_node_type_cmp_chroma_matte() +{ + namespace file_ns = blender::nodes::node_composite_chroma_matte_cc; + + static bNodeType ntype; + + cmp_node_type_base(&ntype, CMP_NODE_CHROMA_MATTE, "Chroma Key", NODE_CLASS_MATTE); + ntype.declare = file_ns::cmp_node_chroma_matte_declare; + ntype.draw_buttons = file_ns::node_composit_buts_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); + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/composite/nodes/node_composite_colorMatte.cc b/source/blender/nodes/composite/nodes/node_composite_colorMatte.cc deleted file mode 100644 index fc9a0075b14..00000000000 --- a/source/blender/nodes/composite/nodes/node_composite_colorMatte.cc +++ /dev/null @@ -1,60 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2006 Blender Foundation. - * All rights reserved. - */ - -/** \file - * \ingroup cmpnodes - */ - -#include "node_composite_util.hh" - -/* ******************* Color Key ********************************************************** */ -static bNodeSocketTemplate cmp_node_color_in[] = { - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, - {SOCK_RGBA, N_("Key Color"), 1.0f, 1.0f, 1.0f, 1.0f}, - {-1, ""}, -}; - -static bNodeSocketTemplate cmp_node_color_out[] = { - {SOCK_RGBA, N_("Image")}, - {SOCK_FLOAT, N_("Matte")}, - {-1, ""}, -}; - -static void node_composit_init_color_matte(bNodeTree *UNUSED(ntree), bNode *node) -{ - NodeChroma *c = (NodeChroma *)MEM_callocN(sizeof(NodeChroma), "node color"); - node->storage = c; - c->t1 = 0.01f; - c->t2 = 0.1f; - c->t3 = 0.1f; - c->fsize = 0.0f; - c->fstrength = 1.0f; -} - -void register_node_type_cmp_color_matte(void) -{ - static bNodeType ntype; - - cmp_node_type_base(&ntype, CMP_NODE_COLOR_MATTE, "Color Key", NODE_CLASS_MATTE, NODE_PREVIEW); - node_type_socket_templates(&ntype, cmp_node_color_in, cmp_node_color_out); - node_type_init(&ntype, node_composit_init_color_matte); - node_type_storage(&ntype, "NodeChroma", node_free_standard_storage, node_copy_standard_storage); - - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/composite/nodes/node_composite_colorSpill.cc b/source/blender/nodes/composite/nodes/node_composite_colorSpill.cc deleted file mode 100644 index 7bdc2e8289e..00000000000 --- a/source/blender/nodes/composite/nodes/node_composite_colorSpill.cc +++ /dev/null @@ -1,60 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2006 Blender Foundation. - * All rights reserved. - */ - -/** \file - * \ingroup cmpnodes - */ - -#include "node_composite_util.hh" - -/* ******************* Color Spill Suppression ********************************* */ -static bNodeSocketTemplate cmp_node_color_spill_in[] = { - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, - {SOCK_FLOAT, N_("Fac"), 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_FACTOR}, - {-1, ""}, -}; - -static bNodeSocketTemplate cmp_node_color_spill_out[] = { - {SOCK_RGBA, N_("Image")}, - {-1, ""}, -}; - -static void node_composit_init_color_spill(bNodeTree *UNUSED(ntree), bNode *node) -{ - NodeColorspill *ncs = (NodeColorspill *)MEM_callocN(sizeof(NodeColorspill), "node colorspill"); - node->storage = ncs; - 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 */ -} - -void register_node_type_cmp_color_spill(void) -{ - static bNodeType ntype; - - cmp_node_type_base(&ntype, CMP_NODE_COLOR_SPILL, "Color Spill", NODE_CLASS_MATTE, 0); - node_type_socket_templates(&ntype, cmp_node_color_spill_in, cmp_node_color_spill_out); - node_type_init(&ntype, node_composit_init_color_spill); - node_type_storage( - &ntype, "NodeColorspill", node_free_standard_storage, node_copy_standard_storage); - - 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 new file mode 100644 index 00000000000..58bd0bd7d69 --- /dev/null +++ b/source/blender/nodes/composite/nodes/node_composite_color_matte.cc @@ -0,0 +1,85 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2006 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup cmpnodes + */ + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_composite_util.hh" + +/* ******************* Color Matte ********************************************************** */ + +namespace blender::nodes::node_composite_color_matte_cc { + +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_output<decl::Color>(N_("Image")); + b.add_output<decl::Float>(N_("Matte")); +} + +static void node_composit_init_color_matte(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeChroma *c = MEM_cnew<NodeChroma>(__func__); + node->storage = c; + c->t1 = 0.01f; + c->t2 = 0.1f; + c->t3 = 0.1f; + c->fsize = 0.0f; + c->fstrength = 1.0f; +} + +static void node_composit_buts_color_matte(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayout *col; + + col = uiLayoutColumn(layout, true); + uiItemR( + col, ptr, "color_hue", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(col, + ptr, + "color_saturation", + UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, + nullptr, + ICON_NONE); + uiItemR( + col, ptr, "color_value", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); +} + +} // namespace blender::nodes::node_composite_color_matte_cc + +void register_node_type_cmp_color_matte() +{ + namespace file_ns = blender::nodes::node_composite_color_matte_cc; + + static bNodeType ntype; + + cmp_node_type_base(&ntype, CMP_NODE_COLOR_MATTE, "Color Key", NODE_CLASS_MATTE); + ntype.declare = file_ns::cmp_node_color_matte_declare; + ntype.draw_buttons = file_ns::node_composit_buts_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); + + 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 new file mode 100644 index 00000000000..1ee7686a8b1 --- /dev/null +++ b/source/blender/nodes/composite/nodes/node_composite_color_spill.cc @@ -0,0 +1,115 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2006 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup cmpnodes + */ + +#include "RNA_access.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_composite_util.hh" + +/* ******************* Color Spill Suppression ********************************* */ + +namespace blender::nodes::node_composite_color_spill_cc { + +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_output<decl::Color>(N_("Image")); +} + +static void node_composit_init_color_spill(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeColorspill *ncs = MEM_cnew<NodeColorspill>(__func__); + node->storage = ncs; + 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 */ +} + +static void node_composit_buts_color_spill(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayout *row, *col; + + uiItemL(layout, IFACE_("Despill Channel:"), ICON_NONE); + row = uiLayoutRow(layout, false); + uiItemR(row, ptr, "channel", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_EXPAND, nullptr, ICON_NONE); + + col = uiLayoutColumn(layout, false); + uiItemR(col, ptr, "limit_method", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + + if (RNA_enum_get(ptr, "limit_method") == 0) { + uiItemL(col, IFACE_("Limiting Channel:"), ICON_NONE); + row = uiLayoutRow(col, false); + uiItemR(row, + ptr, + "limit_channel", + UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_EXPAND, + nullptr, + ICON_NONE); + } + + uiItemR(col, ptr, "ratio", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(col, ptr, "use_unspill", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + if (RNA_boolean_get(ptr, "use_unspill") == true) { + uiItemR(col, + ptr, + "unspill_red", + UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, + nullptr, + ICON_NONE); + uiItemR(col, + ptr, + "unspill_green", + UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, + nullptr, + ICON_NONE); + uiItemR(col, + ptr, + "unspill_blue", + UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, + nullptr, + ICON_NONE); + } +} + +} // namespace blender::nodes::node_composite_color_spill_cc + +void register_node_type_cmp_color_spill() +{ + namespace file_ns = blender::nodes::node_composite_color_spill_cc; + + static bNodeType ntype; + + cmp_node_type_base(&ntype, CMP_NODE_COLOR_SPILL, "Color Spill", NODE_CLASS_MATTE); + ntype.declare = file_ns::cmp_node_color_spill_declare; + ntype.draw_buttons = file_ns::node_composit_buts_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); + + 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 440e37fe741..809641ec147 100644 --- a/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc +++ b/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc @@ -21,26 +21,19 @@ * \ingroup cmpnodes */ -#include "node_composite_util.hh" - -/* ******************* Color Balance ********************************* */ +#include "RNA_access.h" -namespace blender::nodes { +#include "UI_interface.h" +#include "UI_resources.h" -static void cmp_node_colorbalance_declare(NodeDeclarationBuilder &b) -{ - b.add_input<decl::Float>("Fac").default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_output<decl::Color>("Image"); -} +#include "node_composite_util.hh" -} // namespace blender::nodes +/* ******************* Color Balance ********************************* */ /* Sync functions update formula parameters for other modes, such that the result is comparable. * Note that the results are not exactly the same due to differences in color handling * (sRGB conversion happens for LGG), - * but this keeps settings comparable. - */ + * but this keeps settings comparable. */ void ntreeCompositColorBalanceSyncFromLGG(bNodeTree *UNUSED(ntree), bNode *node) { @@ -65,10 +58,18 @@ void ntreeCompositColorBalanceSyncFromCDL(bNodeTree *UNUSED(ntree), bNode *node) } } +namespace blender::nodes::node_composite_colorbalance_cc { + +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_output<decl::Color>(N_("Image")); +} + static void node_composit_init_colorbalance(bNodeTree *UNUSED(ntree), bNode *node) { - NodeColorBalance *n = (NodeColorBalance *)MEM_callocN(sizeof(NodeColorBalance), - "node colorbalance"); + NodeColorBalance *n = MEM_cnew<NodeColorBalance>(__func__); n->lift[0] = n->lift[1] = n->lift[2] = 1.0f; n->gamma[0] = n->gamma[1] = n->gamma[2] = 1.0f; @@ -80,14 +81,94 @@ static void node_composit_init_colorbalance(bNodeTree *UNUSED(ntree), bNode *nod node->storage = n; } -void register_node_type_cmp_colorbalance(void) +static void node_composit_buts_colorbalance(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { + uiLayout *split, *col, *row; + + uiItemR(layout, ptr, "correction_method", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + + if (RNA_enum_get(ptr, "correction_method") == 0) { + + split = uiLayoutSplit(layout, 0.0f, false); + col = uiLayoutColumn(split, false); + uiTemplateColorPicker(col, ptr, "lift", true, true, false, true); + row = uiLayoutRow(col, false); + uiItemR(row, ptr, "lift", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + + col = uiLayoutColumn(split, false); + uiTemplateColorPicker(col, ptr, "gamma", true, true, true, true); + row = uiLayoutRow(col, false); + uiItemR(row, ptr, "gamma", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + + col = uiLayoutColumn(split, false); + uiTemplateColorPicker(col, ptr, "gain", true, true, true, true); + row = uiLayoutRow(col, false); + uiItemR(row, ptr, "gain", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + } + else { + + split = uiLayoutSplit(layout, 0.0f, false); + col = uiLayoutColumn(split, false); + uiTemplateColorPicker(col, ptr, "offset", true, true, false, true); + row = uiLayoutRow(col, false); + uiItemR(row, ptr, "offset", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(col, ptr, "offset_basis", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + + col = uiLayoutColumn(split, false); + uiTemplateColorPicker(col, ptr, "power", true, true, false, true); + row = uiLayoutRow(col, false); + uiItemR(row, ptr, "power", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + + col = uiLayoutColumn(split, false); + uiTemplateColorPicker(col, ptr, "slope", true, true, false, true); + row = uiLayoutRow(col, false); + uiItemR(row, ptr, "slope", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + } +} + +static void node_composit_buts_colorbalance_ex(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiItemR(layout, ptr, "correction_method", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + + if (RNA_enum_get(ptr, "correction_method") == 0) { + + uiTemplateColorPicker(layout, ptr, "lift", true, true, false, true); + uiItemR(layout, ptr, "lift", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + + uiTemplateColorPicker(layout, ptr, "gamma", true, true, true, true); + uiItemR(layout, ptr, "gamma", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + + uiTemplateColorPicker(layout, ptr, "gain", true, true, true, true); + uiItemR(layout, ptr, "gain", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + } + else { + uiTemplateColorPicker(layout, ptr, "offset", true, true, false, true); + uiItemR(layout, ptr, "offset", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + + uiTemplateColorPicker(layout, ptr, "power", true, true, false, true); + uiItemR(layout, ptr, "power", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + + uiTemplateColorPicker(layout, ptr, "slope", true, true, false, true); + uiItemR(layout, ptr, "slope", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + } +} + +} // namespace blender::nodes::node_composite_colorbalance_cc + +void register_node_type_cmp_colorbalance() +{ + namespace file_ns = blender::nodes::node_composite_colorbalance_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_COLORBALANCE, "Color Balance", NODE_CLASS_OP_COLOR, 0); - ntype.declare = blender::nodes::cmp_node_colorbalance_declare; + cmp_node_type_base(&ntype, CMP_NODE_COLORBALANCE, "Color Balance", NODE_CLASS_OP_COLOR); + ntype.declare = file_ns::cmp_node_colorbalance_declare; + ntype.draw_buttons = file_ns::node_composit_buts_colorbalance; + ntype.draw_buttons_ex = file_ns::node_composit_buts_colorbalance_ex; node_type_size(&ntype, 400, 200, 400); - node_type_init(&ntype, node_composit_init_colorbalance); + node_type_init(&ntype, file_ns::node_composit_init_colorbalance); node_type_storage( &ntype, "NodeColorBalance", node_free_standard_storage, node_copy_standard_storage); diff --git a/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc b/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc index 0682c66f1e8..9f1dcf24de9 100644 --- a/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc +++ b/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc @@ -21,25 +21,25 @@ * \ingroup cmpnodes */ +#include "UI_interface.h" +#include "UI_resources.h" + #include "node_composite_util.hh" /* ******************* Color Correction ********************************* */ -namespace blender::nodes { +namespace blender::nodes::node_composite_colorcorrection_cc { static void cmp_node_colorcorrection_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input<decl::Float>("Mask").default_value(1.0f).min(0.0f).max(1.0f); - b.add_output<decl::Color>("Image"); + 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_output<decl::Color>(N_("Image")); } -} // namespace blender::nodes - static void node_composit_init_colorcorrection(bNodeTree *UNUSED(ntree), bNode *node) { - NodeColorCorrection *n = (NodeColorCorrection *)MEM_callocN(sizeof(NodeColorCorrection), - "node colorcorrection"); + NodeColorCorrection *n = MEM_cnew<NodeColorCorrection>(__func__); n->startmidtones = 0.2f; n->endmidtones = 0.7f; n->master.contrast = 1.0f; @@ -66,14 +66,236 @@ static void node_composit_init_colorcorrection(bNodeTree *UNUSED(ntree), bNode * node->storage = n; } -void register_node_type_cmp_colorcorrection(void) +static void node_composit_buts_colorcorrection(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiLayout *row; + + row = uiLayoutRow(layout, false); + uiItemR(row, ptr, "red", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(row, ptr, "green", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(row, ptr, "blue", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + + row = uiLayoutRow(layout, false); + uiItemL(row, "", ICON_NONE); + uiItemL(row, IFACE_("Saturation"), ICON_NONE); + uiItemL(row, IFACE_("Contrast"), ICON_NONE); + uiItemL(row, IFACE_("Gamma"), ICON_NONE); + uiItemL(row, IFACE_("Gain"), ICON_NONE); + uiItemL(row, IFACE_("Lift"), ICON_NONE); + + row = uiLayoutRow(layout, false); + uiItemL(row, IFACE_("Master"), ICON_NONE); + uiItemR( + row, ptr, "master_saturation", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, "", ICON_NONE); + uiItemR( + row, ptr, "master_contrast", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, "", ICON_NONE); + uiItemR(row, ptr, "master_gamma", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, "", ICON_NONE); + uiItemR(row, ptr, "master_gain", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, "", ICON_NONE); + uiItemR(row, ptr, "master_lift", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, "", ICON_NONE); + + row = uiLayoutRow(layout, false); + uiItemL(row, IFACE_("Highlights"), ICON_NONE); + uiItemR(row, + ptr, + "highlights_saturation", + UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, + "", + ICON_NONE); + uiItemR(row, + ptr, + "highlights_contrast", + UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, + "", + ICON_NONE); + uiItemR( + row, ptr, "highlights_gamma", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, "", ICON_NONE); + uiItemR( + row, ptr, "highlights_gain", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, "", ICON_NONE); + uiItemR( + row, ptr, "highlights_lift", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, "", ICON_NONE); + + row = uiLayoutRow(layout, false); + uiItemL(row, IFACE_("Midtones"), ICON_NONE); + uiItemR(row, + ptr, + "midtones_saturation", + UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, + "", + ICON_NONE); + uiItemR( + row, ptr, "midtones_contrast", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, "", ICON_NONE); + uiItemR( + row, ptr, "midtones_gamma", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, "", ICON_NONE); + uiItemR(row, ptr, "midtones_gain", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, "", ICON_NONE); + uiItemR(row, ptr, "midtones_lift", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, "", ICON_NONE); + + row = uiLayoutRow(layout, false); + uiItemL(row, IFACE_("Shadows"), ICON_NONE); + uiItemR(row, + ptr, + "shadows_saturation", + UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, + "", + ICON_NONE); + uiItemR( + row, ptr, "shadows_contrast", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, "", ICON_NONE); + uiItemR(row, ptr, "shadows_gamma", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, "", ICON_NONE); + uiItemR(row, ptr, "shadows_gain", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, "", ICON_NONE); + uiItemR(row, ptr, "shadows_lift", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, "", ICON_NONE); + + row = uiLayoutRow(layout, false); + uiItemR(row, + ptr, + "midtones_start", + UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, + nullptr, + ICON_NONE); + uiItemR( + row, ptr, "midtones_end", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); +} + +static void node_composit_buts_colorcorrection_ex(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) { + uiLayout *row; + + row = uiLayoutRow(layout, false); + uiItemR(row, ptr, "red", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(row, ptr, "green", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(row, ptr, "blue", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + row = layout; + uiItemL(row, IFACE_("Saturation"), ICON_NONE); + uiItemR(row, + ptr, + "master_saturation", + UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, + nullptr, + ICON_NONE); + uiItemR(row, + ptr, + "highlights_saturation", + UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, + nullptr, + ICON_NONE); + uiItemR(row, + ptr, + "midtones_saturation", + UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, + nullptr, + ICON_NONE); + uiItemR(row, + ptr, + "shadows_saturation", + UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, + nullptr, + ICON_NONE); + + uiItemL(row, IFACE_("Contrast"), ICON_NONE); + uiItemR(row, + ptr, + "master_contrast", + UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, + nullptr, + ICON_NONE); + uiItemR(row, + ptr, + "highlights_contrast", + UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, + nullptr, + ICON_NONE); + uiItemR(row, + ptr, + "midtones_contrast", + UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, + nullptr, + ICON_NONE); + uiItemR(row, + ptr, + "shadows_contrast", + UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, + nullptr, + ICON_NONE); + + uiItemL(row, IFACE_("Gamma"), ICON_NONE); + uiItemR( + row, ptr, "master_gamma", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(row, + ptr, + "highlights_gamma", + UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, + nullptr, + ICON_NONE); + uiItemR(row, + ptr, + "midtones_gamma", + UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, + nullptr, + ICON_NONE); + uiItemR(row, + ptr, + "shadows_gamma", + UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, + nullptr, + ICON_NONE); + + uiItemL(row, IFACE_("Gain"), ICON_NONE); + uiItemR( + row, ptr, "master_gain", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(row, + ptr, + "highlights_gain", + UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, + nullptr, + ICON_NONE); + uiItemR(row, + ptr, + "midtones_gain", + UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, + nullptr, + ICON_NONE); + uiItemR( + row, ptr, "shadows_gain", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + + uiItemL(row, IFACE_("Lift"), ICON_NONE); + uiItemR( + row, ptr, "master_lift", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(row, + ptr, + "highlights_lift", + UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, + nullptr, + ICON_NONE); + uiItemR(row, + ptr, + "midtones_lift", + UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, + nullptr, + ICON_NONE); + uiItemR( + row, ptr, "shadows_lift", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + + row = uiLayoutRow(layout, false); + uiItemR(row, ptr, "midtones_start", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(row, ptr, "midtones_end", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); +} + +} // namespace blender::nodes::node_composite_colorcorrection_cc + +void register_node_type_cmp_colorcorrection() +{ + namespace file_ns = blender::nodes::node_composite_colorcorrection_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_COLORCORRECTION, "Color Correction", NODE_CLASS_OP_COLOR, 0); - ntype.declare = blender::nodes::cmp_node_colorcorrection_declare; + cmp_node_type_base(&ntype, CMP_NODE_COLORCORRECTION, "Color Correction", NODE_CLASS_OP_COLOR); + ntype.declare = file_ns::cmp_node_colorcorrection_declare; + ntype.draw_buttons = file_ns::node_composit_buts_colorcorrection; + ntype.draw_buttons_ex = file_ns::node_composit_buts_colorcorrection_ex; node_type_size(&ntype, 400, 200, 600); - node_type_init(&ntype, node_composit_init_colorcorrection); + node_type_init(&ntype, file_ns::node_composit_init_colorcorrection); node_type_storage( &ntype, "NodeColorCorrection", node_free_standard_storage, node_copy_standard_storage); diff --git a/source/blender/nodes/composite/nodes/node_composite_common.cc b/source/blender/nodes/composite/nodes/node_composite_common.cc index fecf6795ef7..d5f7279398e 100644 --- a/source/blender/nodes/composite/nodes/node_composite_common.cc +++ b/source/blender/nodes/composite/nodes/node_composite_common.cc @@ -32,26 +32,23 @@ #include "RNA_access.h" -void register_node_type_cmp_group(void) +void register_node_type_cmp_group() { static bNodeType ntype; /* NOTE: Cannot use sh_node_type_base for node group, because it would map the node type * to the shared NODE_GROUP integer type id. */ - node_type_base_custom( - &ntype, "CompositorNodeGroup", "Group", NODE_CLASS_GROUP, NODE_CONST_OUTPUT); + node_type_base_custom(&ntype, "CompositorNodeGroup", "Group", NODE_CLASS_GROUP); ntype.type = NODE_GROUP; ntype.poll = cmp_node_poll_default; ntype.poll_instance = node_group_poll_instance; ntype.insert_link = node_insert_link_default; - ntype.update_internal_links = node_update_internal_links_default; ntype.rna_ext.srna = RNA_struct_find("CompositorNodeGroup"); BLI_assert(ntype.rna_ext.srna != nullptr); RNA_struct_blender_type_set(ntype.rna_ext.srna, &ntype); - node_type_socket_templates(&ntype, nullptr, nullptr); node_type_size(&ntype, 140, 60, 400); - node_type_label(&ntype, node_group_label); + ntype.labelfunc = node_group_label; node_type_group_update(&ntype, node_group_update); nodeRegisterType(&ntype); @@ -66,7 +63,4 @@ void register_node_type_cmp_custom_group(bNodeType *ntype) if (ntype->insert_link == nullptr) { ntype->insert_link = node_insert_link_default; } - if (ntype->update_internal_links == nullptr) { - ntype->update_internal_links = node_update_internal_links_default; - } } diff --git a/source/blender/nodes/composite/nodes/node_composite_composite.cc b/source/blender/nodes/composite/nodes/node_composite_composite.cc index 170fecb251c..b3b0e5bf432 100644 --- a/source/blender/nodes/composite/nodes/node_composite_composite.cc +++ b/source/blender/nodes/composite/nodes/node_composite_composite.cc @@ -21,30 +21,40 @@ * \ingroup cmpnodes */ +#include "UI_interface.h" +#include "UI_resources.h" + #include "node_composite_util.hh" /* **************** COMPOSITE ******************** */ -namespace blender::nodes { +namespace blender::nodes::node_composite_composite_cc { static void cmp_node_composite_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>("Image").default_value({0.0f, 0.0f, 0.0f, 1.0f}); - b.add_input<decl::Float>("Alpha").default_value(1.0f).min(0.0f).max(1.0f); - b.add_input<decl::Float>("Z").default_value(1.0f).min(0.0f).max(1.0f); + b.add_input<decl::Color>(N_("Image")).default_value({0.0f, 0.0f, 0.0f, 1.0f}); + b.add_input<decl::Float>(N_("Alpha")).default_value(1.0f).min(0.0f).max(1.0f); + b.add_input<decl::Float>(N_("Z")).default_value(1.0f).min(0.0f).max(1.0f); +} + +static void node_composit_buts_composite(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "use_alpha", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } -} // namespace blender::nodes +} // namespace blender::nodes::node_composite_composite_cc -void register_node_type_cmp_composite(void) +void register_node_type_cmp_composite() { - static bNodeType ntype; + namespace file_ns = blender::nodes::node_composite_composite_cc; - cmp_node_type_base(&ntype, CMP_NODE_COMPOSITE, "Composite", NODE_CLASS_OUTPUT, NODE_PREVIEW); - ntype.declare = blender::nodes::cmp_node_composite_declare; + static bNodeType ntype; - /* Do not allow muting for this node. */ - node_type_internal_links(&ntype, nullptr); + 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.flag |= NODE_PREVIEW; + ntype.no_muting = true; nodeRegisterType(&ntype); } 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 new file mode 100644 index 00000000000..75af21ab9a2 --- /dev/null +++ b/source/blender/nodes/composite/nodes/node_composite_convert_color_space.cc @@ -0,0 +1,83 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2021 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup cmpnodes + */ + +#include "node_composite_util.hh" + +#include "RNA_access.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "IMB_colormanagement.h" + +namespace blender::nodes::node_composite_convert_color_space_cc { + +static void CMP_NODE_CONVERT_COLOR_SPACE_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output<decl::Color>(N_("Image")); +} + +static void node_composit_init_convert_colorspace(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeConvertColorSpace *ncs = static_cast<NodeConvertColorSpace *>( + MEM_callocN(sizeof(NodeConvertColorSpace), "node colorspace")); + const char *first_colorspace = IMB_colormanagement_role_colorspace_name_get( + COLOR_ROLE_SCENE_LINEAR); + if (first_colorspace && first_colorspace[0]) { + STRNCPY(ncs->from_color_space, first_colorspace); + STRNCPY(ncs->to_color_space, first_colorspace); + } + else { + ncs->from_color_space[0] = 0; + ncs->to_color_space[0] = 0; + } + node->storage = ncs; +} + +static void node_composit_buts_convert_colorspace(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiItemR(layout, ptr, "from_color_space", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(layout, ptr, "to_color_space", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); +} + +} // namespace blender::nodes::node_composite_convert_color_space_cc + +void register_node_type_cmp_convert_color_space(void) +{ + namespace file_ns = blender::nodes::node_composite_convert_color_space_cc; + static bNodeType ntype; + + cmp_node_type_base( + &ntype, CMP_NODE_CONVERT_COLOR_SPACE, "Convert Colorspace", NODE_CLASS_CONVERTER); + ntype.declare = file_ns::CMP_NODE_CONVERT_COLOR_SPACE_declare; + ntype.draw_buttons = file_ns::node_composit_buts_convert_colorspace; + node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); + node_type_init(&ntype, file_ns::node_composit_init_convert_colorspace); + node_type_storage( + &ntype, "NodeConvertColorSpace", node_free_standard_storage, node_copy_standard_storage); + + 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 b5ca1fb015e..e4abc8106e2 100644 --- a/source/blender/nodes/composite/nodes/node_composite_cornerpin.cc +++ b/source/blender/nodes/composite/nodes/node_composite_cornerpin.cc @@ -23,27 +23,41 @@ #include "node_composite_util.hh" -static bNodeSocketTemplate inputs[] = { - {SOCK_RGBA, N_("Image"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {SOCK_VECTOR, N_("Upper Left"), 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {SOCK_VECTOR, N_("Upper Right"), 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {SOCK_VECTOR, N_("Lower Left"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {SOCK_VECTOR, N_("Lower Right"), 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {-1, ""}, -}; - -static bNodeSocketTemplate outputs[] = { - {SOCK_RGBA, N_("Image")}, - {SOCK_FLOAT, N_("Plane")}, - {-1, ""}, -}; - -void register_node_type_cmp_cornerpin(void) +namespace blender::nodes::node_composite_cornerpin_cc { + +static void cmp_node_cornerpin_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Vector>(N_("Upper Left")) + .default_value({0.0f, 1.0f, 0.0f}) + .min(0.0f) + .max(1.0f); + b.add_input<decl::Vector>(N_("Upper Right")) + .default_value({1.0f, 1.0f, 0.0f}) + .min(0.0f) + .max(1.0f); + b.add_input<decl::Vector>(N_("Lower Left")) + .default_value({0.0f, 0.0f, 0.0f}) + .min(0.0f) + .max(1.0f); + b.add_input<decl::Vector>(N_("Lower Right")) + .default_value({1.0f, 0.0f, 0.0f}) + .min(0.0f) + .max(1.0f); + b.add_output<decl::Color>(N_("Image")); + b.add_output<decl::Float>(N_("Plane")); +} + +} // namespace blender::nodes::node_composite_cornerpin_cc + +void register_node_type_cmp_cornerpin() { + namespace file_ns = blender::nodes::node_composite_cornerpin_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_CORNERPIN, "Corner Pin", NODE_CLASS_DISTORT, 0); - node_type_socket_templates(&ntype, inputs, outputs); + cmp_node_type_base(&ntype, CMP_NODE_CORNERPIN, "Corner Pin", NODE_CLASS_DISTORT); + ntype.declare = file_ns::cmp_node_cornerpin_declare; 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 f07dba8a74b..e14b7d04ea6 100644 --- a/source/blender/nodes/composite/nodes/node_composite_crop.cc +++ b/source/blender/nodes/composite/nodes/node_composite_crop.cc @@ -21,22 +21,26 @@ * \ingroup cmpnodes */ +#include "RNA_access.h" + +#include "UI_interface.h" +#include "UI_resources.h" + #include "node_composite_util.hh" /* **************** Crop ******************** */ -static bNodeSocketTemplate cmp_node_crop_in[] = { - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_crop_out[] = { - {SOCK_RGBA, N_("Image")}, - {-1, ""}, -}; +namespace blender::nodes::node_composite_crop_cc { + +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_output<decl::Color>(N_("Image")); +} static void node_composit_init_crop(bNodeTree *UNUSED(ntree), bNode *node) { - NodeTwoXYs *nxy = (NodeTwoXYs *)MEM_callocN(sizeof(NodeTwoXYs), "node xy data"); + NodeTwoXYs *nxy = MEM_cnew<NodeTwoXYs>(__func__); node->storage = nxy; nxy->x1 = 0; nxy->x2 = 0; @@ -44,13 +48,40 @@ static void node_composit_init_crop(bNodeTree *UNUSED(ntree), bNode *node) nxy->y2 = 0; } -void register_node_type_cmp_crop(void) +static void node_composit_buts_crop(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayout *col; + + uiItemR(layout, ptr, "use_crop_size", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(layout, ptr, "relative", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + + col = uiLayoutColumn(layout, true); + if (RNA_boolean_get(ptr, "relative")) { + uiItemR(col, ptr, "rel_min_x", UI_ITEM_R_SPLIT_EMPTY_NAME, IFACE_("Left"), ICON_NONE); + uiItemR(col, ptr, "rel_max_x", UI_ITEM_R_SPLIT_EMPTY_NAME, IFACE_("Right"), ICON_NONE); + uiItemR(col, ptr, "rel_min_y", UI_ITEM_R_SPLIT_EMPTY_NAME, IFACE_("Up"), ICON_NONE); + uiItemR(col, ptr, "rel_max_y", UI_ITEM_R_SPLIT_EMPTY_NAME, IFACE_("Down"), ICON_NONE); + } + else { + uiItemR(col, ptr, "min_x", UI_ITEM_R_SPLIT_EMPTY_NAME, IFACE_("Left"), ICON_NONE); + uiItemR(col, ptr, "max_x", UI_ITEM_R_SPLIT_EMPTY_NAME, IFACE_("Right"), ICON_NONE); + uiItemR(col, ptr, "min_y", UI_ITEM_R_SPLIT_EMPTY_NAME, IFACE_("Up"), ICON_NONE); + uiItemR(col, ptr, "max_y", UI_ITEM_R_SPLIT_EMPTY_NAME, IFACE_("Down"), ICON_NONE); + } +} + +} // namespace blender::nodes::node_composite_crop_cc + +void register_node_type_cmp_crop() { + namespace file_ns = blender::nodes::node_composite_crop_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_CROP, "Crop", NODE_CLASS_DISTORT, 0); - node_type_socket_templates(&ntype, cmp_node_crop_in, cmp_node_crop_out); - node_type_init(&ntype, node_composit_init_crop); + cmp_node_type_base(&ntype, CMP_NODE_CROP, "Crop", NODE_CLASS_DISTORT); + ntype.declare = file_ns::cmp_node_crop_declare; + 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); 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 6657267b016..40b467d608a 100644 --- a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc +++ b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc @@ -38,8 +38,10 @@ #include <optional> +/* -------------------------------------------------------------------- */ /** \name Cryptomatte * \{ */ + static blender::bke::cryptomatte::CryptomatteSessionPtr cryptomatte_init_from_node_render( const bNode &node, const bool use_meta_data) { @@ -134,8 +136,7 @@ static void cryptomatte_add(const Scene &scene, return; } - CryptomatteEntry *entry = static_cast<CryptomatteEntry *>( - MEM_callocN(sizeof(CryptomatteEntry), __func__)); + CryptomatteEntry *entry = MEM_cnew<CryptomatteEntry>(__func__); entry->encoded_hash = encoded_hash; blender::bke::cryptomatte::CryptomatteSessionPtr session = cryptomatte_init_from_node( scene, node, true); @@ -156,16 +157,6 @@ static void cryptomatte_remove(NodeCryptomatte &n, float encoded_hash) MEM_freeN(entry); } -static bNodeSocketTemplate cmp_node_cryptomatte_in[] = { - {SOCK_RGBA, N_("Image"), 0.0f, 0.0f, 0.0f, 1.0f}, {-1, ""}}; - -static bNodeSocketTemplate cmp_node_cryptomatte_out[] = { - {SOCK_RGBA, N_("Image")}, - {SOCK_FLOAT, N_("Matte")}, - {SOCK_RGBA, N_("Pick")}, - {-1, ""}, -}; - void ntreeCompositCryptomatteSyncFromAdd(const Scene *scene, bNode *node) { BLI_assert(ELEM(node->type, CMP_NODE_CRYPTOMATTE, CMP_NODE_CRYPTOMATTE_LEGACY)); @@ -197,8 +188,7 @@ void ntreeCompositCryptomatteUpdateLayerNames(const Scene *scene, bNode *node) if (session) { for (blender::StringRef layer_name : blender::bke::cryptomatte::BKE_cryptomatte_layer_names_get(*session)) { - CryptomatteLayer *layer = static_cast<CryptomatteLayer *>( - MEM_callocN(sizeof(CryptomatteLayer), __func__)); + CryptomatteLayer *layer = MEM_cnew<CryptomatteLayer>(__func__); layer_name.copy(layer->name); BLI_addtail(&n->runtime.layers, layer); } @@ -241,10 +231,21 @@ CryptomatteSession *ntreeCompositCryptomatteSession(const Scene *scene, bNode *n return session_ptr.release(); } +namespace blender::nodes::node_composite_cryptomatte_cc { + +static bNodeSocketTemplate cmp_node_cryptomatte_in[] = { + {SOCK_RGBA, N_("Image"), 0.0f, 0.0f, 0.0f, 1.0f}, {-1, ""}}; + +static bNodeSocketTemplate cmp_node_cryptomatte_out[] = { + {SOCK_RGBA, N_("Image")}, + {SOCK_FLOAT, N_("Matte")}, + {SOCK_RGBA, N_("Pick")}, + {-1, ""}, +}; + static void node_init_cryptomatte(bNodeTree *UNUSED(ntree), bNode *node) { - NodeCryptomatte *user = static_cast<NodeCryptomatte *>( - MEM_callocN(sizeof(NodeCryptomatte), __func__)); + NodeCryptomatte *user = MEM_cnew<NodeCryptomatte>(__func__); node->storage = user; } @@ -297,44 +298,40 @@ static bool node_poll_cryptomatte(bNodeType *UNUSED(ntype), } if (scene == nullptr) { - *r_disabled_hint = - "The node tree must be the compositing node tree of any scene in the file"; + *r_disabled_hint = TIP_( + "The node tree must be the compositing node tree of any scene in the file"); } return scene != nullptr; } - *r_disabled_hint = "Not a compositor node tree"; + *r_disabled_hint = TIP_("Not a compositor node tree"); return false; } -void register_node_type_cmp_cryptomatte(void) +} // namespace blender::nodes::node_composite_cryptomatte_cc + +void register_node_type_cmp_cryptomatte() { + namespace file_ns = blender::nodes::node_composite_cryptomatte_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_CRYPTOMATTE, "Cryptomatte", NODE_CLASS_MATTE, 0); - node_type_socket_templates(&ntype, cmp_node_cryptomatte_in, cmp_node_cryptomatte_out); + cmp_node_type_base(&ntype, CMP_NODE_CRYPTOMATTE, "Cryptomatte", NODE_CLASS_MATTE); + node_type_socket_templates( + &ntype, file_ns::cmp_node_cryptomatte_in, file_ns::cmp_node_cryptomatte_out); node_type_size(&ntype, 240, 100, 700); - node_type_init(&ntype, node_init_cryptomatte); - ntype.initfunc_api = node_init_api_cryptomatte; - ntype.poll = node_poll_cryptomatte; - node_type_storage(&ntype, "NodeCryptomatte", node_free_cryptomatte, node_copy_cryptomatte); + node_type_init(&ntype, file_ns::node_init_cryptomatte); + ntype.initfunc_api = file_ns::node_init_api_cryptomatte; + ntype.poll = file_ns::node_poll_cryptomatte; + node_type_storage( + &ntype, "NodeCryptomatte", file_ns::node_free_cryptomatte, file_ns::node_copy_cryptomatte); nodeRegisterType(&ntype); } /** \} */ +/* -------------------------------------------------------------------- */ /** \name Cryptomatte Legacy * \{ */ -static void node_init_cryptomatte_legacy(bNodeTree *ntree, bNode *node) -{ - node_init_cryptomatte(ntree, node); - - nodeAddStaticSocket(ntree, node, SOCK_IN, SOCK_RGBA, PROP_NONE, "image", "Image"); - - /* Add three inputs by default, as recommended by the Cryptomatte specification. */ - ntreeCompositCryptomatteAddSocket(ntree, node); - ntreeCompositCryptomatteAddSocket(ntree, node); - ntreeCompositCryptomatteAddSocket(ntree, node); -} bNodeSocket *ntreeCompositCryptomatteAddSocket(bNodeTree *ntree, bNode *node) { @@ -361,14 +358,35 @@ int ntreeCompositCryptomatteRemoveSocket(bNodeTree *ntree, bNode *node) return 1; } -void register_node_type_cmp_cryptomatte_legacy(void) +namespace blender::nodes::node_composite_cryptomatte_cc { + +static void node_init_cryptomatte_legacy(bNodeTree *ntree, bNode *node) +{ + namespace file_ns = blender::nodes::node_composite_cryptomatte_cc; + file_ns::node_init_cryptomatte(ntree, node); + + nodeAddStaticSocket(ntree, node, SOCK_IN, SOCK_RGBA, PROP_NONE, "image", "Image"); + + /* Add three inputs by default, as recommended by the Cryptomatte specification. */ + ntreeCompositCryptomatteAddSocket(ntree, node); + ntreeCompositCryptomatteAddSocket(ntree, node); + ntreeCompositCryptomatteAddSocket(ntree, node); +} + +} // namespace blender::nodes::node_composite_cryptomatte_cc + +void register_node_type_cmp_cryptomatte_legacy() { + namespace legacy_file_ns = blender::nodes::node_composite_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, 0); - node_type_socket_templates(&ntype, nullptr, cmp_node_cryptomatte_out); - node_type_init(&ntype, node_init_cryptomatte_legacy); - node_type_storage(&ntype, "NodeCryptomatte", node_free_cryptomatte, node_copy_cryptomatte); + 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_storage( + &ntype, "NodeCryptomatte", file_ns::node_free_cryptomatte, file_ns::node_copy_cryptomatte); 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 88d96e1ca4a..12390a8549d 100644 --- a/source/blender/nodes/composite/nodes/node_composite_curves.cc +++ b/source/blender/nodes/composite/nodes/node_composite_curves.cc @@ -21,19 +21,20 @@ * \ingroup cmpnodes */ +#include "UI_interface.h" +#include "UI_resources.h" + #include "node_composite_util.hh" /* **************** CURVE Time ******************** */ -namespace blender::nodes { +namespace blender::nodes::node_composite_curves_cc { static void cmp_node_time_declare(NodeDeclarationBuilder &b) { - b.add_output<decl::Float>("Fac"); + b.add_output<decl::Float>(N_("Fac")); } -} // namespace blender::nodes - /* custom1 = start_frame, custom2 = end_frame */ static void node_composit_init_curves_time(bNodeTree *UNUSED(ntree), bNode *node) { @@ -42,43 +43,56 @@ 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); } -void register_node_type_cmp_curve_time(void) +} // namespace blender::nodes::node_composite_curves_cc + +void register_node_type_cmp_curve_time() { + namespace file_ns = blender::nodes::node_composite_curves_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_TIME, "Time", NODE_CLASS_INPUT, 0); - ntype.declare = blender::nodes::cmp_node_time_declare; - node_type_size(&ntype, 140, 100, 320); - node_type_init(&ntype, node_composit_init_curves_time); + cmp_node_type_base(&ntype, CMP_NODE_TIME, "Time Curve", NODE_CLASS_INPUT); + ntype.declare = file_ns::cmp_node_time_declare; + 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); nodeRegisterType(&ntype); } /* **************** CURVE VEC ******************** */ -static bNodeSocketTemplate cmp_node_curve_vec_in[] = { - {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, PROP_NONE}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_curve_vec_out[] = { - {SOCK_VECTOR, N_("Vector")}, - {-1, ""}, -}; +namespace blender::nodes::node_composite_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_output<decl::Vector>(N_("Vector")); +} static void node_composit_init_curve_vec(bNodeTree *UNUSED(ntree), bNode *node) { node->storage = BKE_curvemapping_add(3, -1.0f, -1.0f, 1.0f, 1.0f); } -void register_node_type_cmp_curve_vec(void) +static void node_buts_curvevec(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { + uiTemplateCurveMapping(layout, ptr, "mapping", 'v', false, false, false, false); +} + +} // namespace blender::nodes::node_composite_curves_cc + +void register_node_type_cmp_curve_vec() +{ + namespace file_ns = blender::nodes::node_composite_curves_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_CURVE_VEC, "Vector Curves", NODE_CLASS_OP_VECTOR, 0); - node_type_socket_templates(&ntype, cmp_node_curve_vec_in, cmp_node_curve_vec_out); + cmp_node_type_base(&ntype, CMP_NODE_CURVE_VEC, "Vector Curves", NODE_CLASS_OP_VECTOR); + ntype.declare = file_ns::cmp_node_curve_vec_declare; + ntype.draw_buttons = file_ns::node_buts_curvevec; node_type_size(&ntype, 200, 140, 320); - node_type_init(&ntype, node_composit_init_curve_vec); + node_type_init(&ntype, file_ns::node_composit_init_curve_vec); node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves); nodeRegisterType(&ntype); @@ -86,32 +100,35 @@ void register_node_type_cmp_curve_vec(void) /* **************** CURVE RGB ******************** */ -namespace blender::nodes { +namespace blender::nodes::node_composite_curves_cc { static void cmp_node_rgbcurves_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Float>("Fac").default_value(1.0f).min(-1.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input<decl::Color>("Black Level").default_value({0.0f, 0.0f, 0.0f, 1.0f}); - b.add_input<decl::Color>("White Level").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_output<decl::Color>("Image"); + 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::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")); } -} // namespace blender::nodes - 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); } -void register_node_type_cmp_curve_rgb(void) +} // namespace blender::nodes::node_composite_curves_cc + +void register_node_type_cmp_curve_rgb() { + namespace file_ns = blender::nodes::node_composite_curves_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_CURVE_RGB, "RGB Curves", NODE_CLASS_OP_COLOR, 0); - ntype.declare = blender::nodes::cmp_node_rgbcurves_declare; + cmp_node_type_base(&ntype, CMP_NODE_CURVE_RGB, "RGB Curves", NODE_CLASS_OP_COLOR); + ntype.declare = file_ns::cmp_node_rgbcurves_declare; node_type_size(&ntype, 200, 140, 320); - node_type_init(&ntype, node_composit_init_curve_rgb); + node_type_init(&ntype, file_ns::node_composit_init_curve_rgb); node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves); 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 1103aff4366..41200c97b06 100644 --- a/source/blender/nodes/composite/nodes/node_composite_defocus.cc +++ b/source/blender/nodes/composite/nodes/node_composite_defocus.cc @@ -21,25 +21,30 @@ * \ingroup cmpnodes */ -#include "node_composite_util.hh" - #include <climits> +#include "RNA_access.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_composite_util.hh" + /* ************ Defocus Node ****************** */ -static bNodeSocketTemplate cmp_node_defocus_in[] = { - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, - {SOCK_FLOAT, N_("Z"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_defocus_out[] = { - {SOCK_RGBA, N_("Image")}, - {-1, ""}, -}; + +namespace blender::nodes::node_composite_defocus_cc { + +static void cmp_node_defocus_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_("Z")).default_value(1.0f).min(0.0f).max(1.0f); + b.add_output<decl::Color>(N_("Image")); +} static void node_composit_init_defocus(bNodeTree *UNUSED(ntree), bNode *node) { /* defocus node */ - NodeDefocus *nbd = (NodeDefocus *)MEM_callocN(sizeof(NodeDefocus), "node defocus data"); + NodeDefocus *nbd = MEM_cnew<NodeDefocus>(__func__); nbd->bktype = 0; nbd->rotation = 0.0f; nbd->preview = 1; @@ -53,13 +58,57 @@ static void node_composit_init_defocus(bNodeTree *UNUSED(ntree), bNode *node) node->storage = nbd; } -void register_node_type_cmp_defocus(void) +static void node_composit_buts_defocus(uiLayout *layout, bContext *C, PointerRNA *ptr) +{ + uiLayout *sub, *col; + + col = uiLayoutColumn(layout, false); + uiItemL(col, IFACE_("Bokeh Type:"), ICON_NONE); + uiItemR(col, ptr, "bokeh", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); + uiItemR(col, ptr, "angle", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + + uiItemR(layout, ptr, "use_gamma_correction", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + + col = uiLayoutColumn(layout, false); + uiLayoutSetActive(col, RNA_boolean_get(ptr, "use_zbuffer") == true); + uiItemR(col, ptr, "f_stop", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + + uiItemR(layout, ptr, "blur_max", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(layout, ptr, "threshold", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + + col = uiLayoutColumn(layout, false); + uiItemR(col, ptr, "use_preview", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + + uiTemplateID(layout, + C, + ptr, + "scene", + nullptr, + nullptr, + nullptr, + UI_TEMPLATE_ID_FILTER_ALL, + false, + nullptr); + + col = uiLayoutColumn(layout, false); + uiItemR(col, ptr, "use_zbuffer", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + sub = uiLayoutColumn(col, false); + uiLayoutSetActive(sub, RNA_boolean_get(ptr, "use_zbuffer") == false); + uiItemR(sub, ptr, "z_scale", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); +} + +} // namespace blender::nodes::node_composite_defocus_cc + +void register_node_type_cmp_defocus() { + namespace file_ns = blender::nodes::node_composite_defocus_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_DEFOCUS, "Defocus", NODE_CLASS_OP_FILTER, 0); - node_type_socket_templates(&ntype, cmp_node_defocus_in, cmp_node_defocus_out); - node_type_init(&ntype, node_composit_init_defocus); + cmp_node_type_base(&ntype, CMP_NODE_DEFOCUS, "Defocus", NODE_CLASS_OP_FILTER); + ntype.declare = file_ns::cmp_node_defocus_declare; + 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); 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 ec085794462..d407bcbde63 100644 --- a/source/blender/nodes/composite/nodes/node_composite_denoise.cc +++ b/source/blender/nodes/composite/nodes/node_composite_denoise.cc @@ -23,30 +23,65 @@ * \ingroup cmpnodes */ +#include "BLI_system.h" + +#include "UI_interface.h" +#include "UI_resources.h" + #include "node_composite_util.hh" -static bNodeSocketTemplate cmp_node_denoise_in[] = { - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f}, - {SOCK_VECTOR, N_("Normal"), 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, - {SOCK_RGBA, N_("Albedo"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, - {-1, ""}}; -static bNodeSocketTemplate cmp_node_denoise_out[] = {{SOCK_RGBA, N_("Image")}, {-1, ""}}; +namespace blender::nodes::node_composite_denoise_cc { + +static void cmp_node_denoise_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Vector>(N_("Normal")) + .default_value({0.0f, 0.0f, 0.0f}) + .min(-1.0f) + .max(1.0f) + .hide_value(); + b.add_input<decl::Color>(N_("Albedo")).default_value({1.0f, 1.0f, 1.0f, 1.0f}).hide_value(); + b.add_output<decl::Color>(N_("Image")); +} static void node_composit_init_denonise(bNodeTree *UNUSED(ntree), bNode *node) { - NodeDenoise *ndg = (NodeDenoise *)MEM_callocN(sizeof(NodeDenoise), "node denoise data"); + NodeDenoise *ndg = MEM_cnew<NodeDenoise>(__func__); ndg->hdr = true; ndg->prefilter = CMP_NODE_DENOISE_PREFILTER_ACCURATE; node->storage = ndg; } -void register_node_type_cmp_denoise(void) +static void node_composit_buts_denoise(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { +#ifndef WITH_OPENIMAGEDENOISE + uiItemL(layout, IFACE_("Disabled, built without OpenImageDenoise"), ICON_ERROR); +#else + /* Always supported through Accelerate framework BNNS on macOS. */ +# ifndef __APPLE__ + if (!BLI_cpu_support_sse41()) { + uiItemL(layout, IFACE_("Disabled, CPU with SSE4.1 is required"), ICON_ERROR); + } +# endif +#endif + + uiItemL(layout, IFACE_("Prefilter:"), ICON_NONE); + uiItemR(layout, ptr, "prefilter", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(layout, ptr, "use_hdr", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); +} + +} // namespace blender::nodes::node_composite_denoise_cc + +void register_node_type_cmp_denoise() +{ + namespace file_ns = blender::nodes::node_composite_denoise_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_DENOISE, "Denoise", NODE_CLASS_OP_FILTER, 0); - node_type_socket_templates(&ntype, cmp_node_denoise_in, cmp_node_denoise_out); - node_type_init(&ntype, node_composit_init_denonise); + cmp_node_type_base(&ntype, CMP_NODE_DENOISE, "Denoise", NODE_CLASS_OP_FILTER); + ntype.declare = file_ns::cmp_node_denoise_declare; + 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); 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 52d91dabeb1..ef9c760622c 100644 --- a/source/blender/nodes/composite/nodes/node_composite_despeckle.cc +++ b/source/blender/nodes/composite/nodes/node_composite_despeckle.cc @@ -21,18 +21,21 @@ * \ingroup cmpnodes */ +#include "UI_interface.h" +#include "UI_resources.h" + #include "node_composite_util.hh" /* **************** FILTER ******************** */ -static bNodeSocketTemplate cmp_node_despeckle_in[] = { - {SOCK_FLOAT, N_("Fac"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_despeckle_out[] = { - {SOCK_RGBA, N_("Image")}, - {-1, ""}, -}; + +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_output<decl::Color>(N_("Image")); +} static void node_composit_init_despeckle(bNodeTree *UNUSED(ntree), bNode *node) { @@ -40,13 +43,28 @@ static void node_composit_init_despeckle(bNodeTree *UNUSED(ntree), bNode *node) node->custom4 = 0.5f; } -void register_node_type_cmp_despeckle(void) +static void node_composit_buts_despeckle(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { + uiLayout *col; + + col = uiLayoutColumn(layout, false); + uiItemR(col, ptr, "threshold", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(col, ptr, "threshold_neighbor", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); +} + +} // namespace blender::nodes::node_composite_despeckle_cc + +void register_node_type_cmp_despeckle() +{ + namespace file_ns = blender::nodes::node_composite_despeckle_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_DESPECKLE, "Despeckle", NODE_CLASS_OP_FILTER, NODE_PREVIEW); - node_type_socket_templates(&ntype, cmp_node_despeckle_in, cmp_node_despeckle_out); - node_type_init(&ntype, node_composit_init_despeckle); + cmp_node_type_base(&ntype, CMP_NODE_DESPECKLE, "Despeckle", NODE_CLASS_OP_FILTER); + ntype.declare = file_ns::cmp_node_despeckle_declare; + ntype.draw_buttons = file_ns::node_composit_buts_despeckle; + ntype.flag |= NODE_PREVIEW; + node_type_init(&ntype, file_ns::node_composit_init_despeckle); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_diffMatte.cc b/source/blender/nodes/composite/nodes/node_composite_diffMatte.cc deleted file mode 100644 index 1e1a48381b7..00000000000 --- a/source/blender/nodes/composite/nodes/node_composite_diffMatte.cc +++ /dev/null @@ -1,58 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2006 Blender Foundation. - * All rights reserved. - */ - -/** \file - * \ingroup cmpnodes - */ - -#include "node_composite_util.hh" - -/* ******************* channel Difference Matte ********************************* */ -static bNodeSocketTemplate cmp_node_diff_matte_in[] = { - {SOCK_RGBA, N_("Image 1"), 1.0f, 1.0f, 1.0f, 1.0f}, - {SOCK_RGBA, N_("Image 2"), 1.0f, 1.0f, 1.0f, 1.0f}, - {-1, ""}, -}; - -static bNodeSocketTemplate cmp_node_diff_matte_out[] = { - {SOCK_RGBA, N_("Image")}, - {SOCK_FLOAT, N_("Matte")}, - {-1, ""}, -}; - -static void node_composit_init_diff_matte(bNodeTree *UNUSED(ntree), bNode *node) -{ - NodeChroma *c = (NodeChroma *)MEM_callocN(sizeof(NodeChroma), "node chroma"); - node->storage = c; - c->t1 = 0.1f; - c->t2 = 0.1f; -} - -void register_node_type_cmp_diff_matte(void) -{ - static bNodeType ntype; - - cmp_node_type_base( - &ntype, CMP_NODE_DIFF_MATTE, "Difference Key", NODE_CLASS_MATTE, NODE_PREVIEW); - node_type_socket_templates(&ntype, cmp_node_diff_matte_in, cmp_node_diff_matte_out); - node_type_init(&ntype, node_composit_init_diff_matte); - node_type_storage(&ntype, "NodeChroma", node_free_standard_storage, node_copy_standard_storage); - - 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 new file mode 100644 index 00000000000..9b3360c9e7d --- /dev/null +++ b/source/blender/nodes/composite/nodes/node_composite_diff_matte.cc @@ -0,0 +1,75 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2006 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup cmpnodes + */ + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_composite_util.hh" + +/* ******************* channel Difference Matte ********************************* */ + +namespace blender::nodes::node_composite_diff_matte_cc { + +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_output<decl::Color>(N_("Image")); + b.add_output<decl::Color>(N_("Matte")); +} + +static void node_composit_init_diff_matte(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeChroma *c = MEM_cnew<NodeChroma>(__func__); + node->storage = c; + c->t1 = 0.1f; + c->t2 = 0.1f; +} + +static void node_composit_buts_diff_matte(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayout *col; + + col = uiLayoutColumn(layout, true); + uiItemR( + col, ptr, "tolerance", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(col, ptr, "falloff", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); +} + +} // namespace blender::nodes::node_composite_diff_matte_cc + +void register_node_type_cmp_diff_matte() +{ + namespace file_ns = blender::nodes::node_composite_diff_matte_cc; + + static bNodeType ntype; + + cmp_node_type_base(&ntype, CMP_NODE_DIFF_MATTE, "Difference Key", NODE_CLASS_MATTE); + ntype.declare = file_ns::cmp_node_diff_matte_declare; + ntype.draw_buttons = file_ns::node_composit_buts_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); + + 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 57884a299da..efd06ce8fd4 100644 --- a/source/blender/nodes/composite/nodes/node_composite_dilate.cc +++ b/source/blender/nodes/composite/nodes/node_composite_dilate.cc @@ -21,29 +21,56 @@ * \ingroup cmpnodes */ +#include "RNA_access.h" + +#include "UI_interface.h" +#include "UI_resources.h" + #include "node_composite_util.hh" /* **************** Dilate/Erode ******************** */ -static bNodeSocketTemplate cmp_node_dilateerode_in[] = { - {SOCK_FLOAT, N_("Mask"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, {-1, ""}}; -static bNodeSocketTemplate cmp_node_dilateerode_out[] = {{SOCK_FLOAT, N_("Mask")}, {-1, ""}}; +namespace blender::nodes::node_composite_dilate_cc { + +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); + b.add_output<decl::Float>(N_("Mask")); +} static void node_composit_init_dilateerode(bNodeTree *UNUSED(ntree), bNode *node) { - NodeDilateErode *data = (NodeDilateErode *)MEM_callocN(sizeof(NodeDilateErode), - "NodeDilateErode"); + NodeDilateErode *data = MEM_cnew<NodeDilateErode>(__func__); data->falloff = PROP_SMOOTH; node->storage = data; } -void register_node_type_cmp_dilateerode(void) +static void node_composit_buts_dilateerode(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { + 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: + uiItemR(layout, ptr, "edge", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + break; + case CMP_NODE_DILATEERODE_DISTANCE_FEATHER: + uiItemR(layout, ptr, "falloff", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + break; + } +} + +} // namespace blender::nodes::node_composite_dilate_cc + +void register_node_type_cmp_dilateerode() +{ + namespace file_ns = blender::nodes::node_composite_dilate_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_DILATEERODE, "Dilate/Erode", NODE_CLASS_OP_FILTER, 0); - node_type_socket_templates(&ntype, cmp_node_dilateerode_in, cmp_node_dilateerode_out); - node_type_init(&ntype, node_composit_init_dilateerode); + cmp_node_type_base(&ntype, CMP_NODE_DILATEERODE, "Dilate/Erode", NODE_CLASS_OP_FILTER); + ntype.draw_buttons = file_ns::node_composit_buts_dilateerode; + ntype.declare = file_ns::cmp_node_dilate_declare; + node_type_init(&ntype, file_ns::node_composit_init_dilateerode); node_type_storage( &ntype, "NodeDilateErode", node_free_standard_storage, node_copy_standard_storage); diff --git a/source/blender/nodes/composite/nodes/node_composite_directionalblur.cc b/source/blender/nodes/composite/nodes/node_composite_directionalblur.cc index d9f82ba5009..2215e62659b 100644 --- a/source/blender/nodes/composite/nodes/node_composite_directionalblur.cc +++ b/source/blender/nodes/composite/nodes/node_composite_directionalblur.cc @@ -21,29 +21,64 @@ * \ingroup cmpnodes */ +#include "UI_interface.h" +#include "UI_resources.h" + #include "node_composite_util.hh" -static bNodeSocketTemplate cmp_node_dblur_in[] = {{SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, - {-1, ""}}; +namespace blender::nodes::node_composite_directionalblur_cc { -static bNodeSocketTemplate cmp_node_dblur_out[] = {{SOCK_RGBA, N_("Image")}, {-1, ""}}; +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_output<decl::Color>(N_("Image")); +} static void node_composit_init_dblur(bNodeTree *UNUSED(ntree), bNode *node) { - NodeDBlurData *ndbd = (NodeDBlurData *)MEM_callocN(sizeof(NodeDBlurData), "node dblur data"); + NodeDBlurData *ndbd = MEM_cnew<NodeDBlurData>(__func__); node->storage = ndbd; ndbd->iter = 1; ndbd->center_x = 0.5; ndbd->center_y = 0.5; } -void register_node_type_cmp_dblur(void) +static void node_composit_buts_dblur(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayout *col; + + uiItemR(layout, ptr, "iterations", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(layout, ptr, "use_wrap", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + + col = uiLayoutColumn(layout, true); + uiItemL(col, IFACE_("Center:"), ICON_NONE); + uiItemR(col, ptr, "center_x", UI_ITEM_R_SPLIT_EMPTY_NAME, IFACE_("X"), ICON_NONE); + uiItemR(col, ptr, "center_y", UI_ITEM_R_SPLIT_EMPTY_NAME, IFACE_("Y"), ICON_NONE); + + uiItemS(layout); + + col = uiLayoutColumn(layout, true); + uiItemR(col, ptr, "distance", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(col, ptr, "angle", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + + uiItemS(layout); + + uiItemR(layout, ptr, "spin", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(layout, ptr, "zoom", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); +} + +} // namespace blender::nodes::node_composite_directionalblur_cc + +void register_node_type_cmp_dblur() { + namespace file_ns = blender::nodes::node_composite_directionalblur_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_DBLUR, "Directional Blur", NODE_CLASS_OP_FILTER, 0); - node_type_socket_templates(&ntype, cmp_node_dblur_in, cmp_node_dblur_out); - node_type_init(&ntype, node_composit_init_dblur); + cmp_node_type_base(&ntype, CMP_NODE_DBLUR, "Directional Blur", NODE_CLASS_OP_FILTER); + ntype.declare = file_ns::cmp_node_directional_blur_declare; + ntype.draw_buttons = file_ns::node_composit_buts_dblur; + node_type_init(&ntype, file_ns::node_composit_init_dblur); node_type_storage( &ntype, "NodeDBlurData", node_free_standard_storage, node_copy_standard_storage); diff --git a/source/blender/nodes/composite/nodes/node_composite_displace.cc b/source/blender/nodes/composite/nodes/node_composite_displace.cc index b1ed7f05794..819680e1967 100644 --- a/source/blender/nodes/composite/nodes/node_composite_displace.cc +++ b/source/blender/nodes/composite/nodes/node_composite_displace.cc @@ -25,24 +25,31 @@ /* **************** Displace ******************** */ -static bNodeSocketTemplate cmp_node_displace_in[] = { - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, - {SOCK_VECTOR, N_("Vector"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_TRANSLATION}, - {SOCK_FLOAT, N_("X Scale"), 0.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f, PROP_NONE}, - {SOCK_FLOAT, N_("Y Scale"), 0.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f, PROP_NONE}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_displace_out[] = { - {SOCK_RGBA, N_("Image")}, - {-1, ""}, -}; - -void register_node_type_cmp_displace(void) +namespace blender::nodes::node_composite_displace_cc { + +static void cmp_node_displace_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Vector>(N_("Vector")) + .default_value({1.0f, 1.0f, 1.0f}) + .min(0.0f) + .max(1.0f) + .subtype(PROP_TRANSLATION); + b.add_input<decl::Float>(N_("X Scale")).default_value(0.0f).min(-1000.0f).max(1000.0f); + b.add_input<decl::Float>(N_("Y Scale")).default_value(0.0f).min(-1000.0f).max(1000.0f); + b.add_output<decl::Color>(N_("Image")); +} + +} // namespace blender::nodes::node_composite_displace_cc + +void register_node_type_cmp_displace() { + namespace file_ns = blender::nodes::node_composite_displace_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_DISPLACE, "Displace", NODE_CLASS_DISTORT, 0); - node_type_socket_templates(&ntype, cmp_node_displace_in, cmp_node_displace_out); + cmp_node_type_base(&ntype, CMP_NODE_DISPLACE, "Displace", NODE_CLASS_DISTORT); + ntype.declare = file_ns::cmp_node_displace_declare; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_distanceMatte.cc b/source/blender/nodes/composite/nodes/node_composite_distanceMatte.cc deleted file mode 100644 index 3f8767ecd08..00000000000 --- a/source/blender/nodes/composite/nodes/node_composite_distanceMatte.cc +++ /dev/null @@ -1,58 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2006 Blender Foundation. - * All rights reserved. - */ - -/** \file - * \ingroup cmpnodes - */ - -#include "node_composite_util.hh" - -/* ******************* channel Distance Matte ********************************* */ -static bNodeSocketTemplate cmp_node_distance_matte_in[] = { - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, - {SOCK_RGBA, N_("Key Color"), 1.0f, 1.0f, 1.0f, 1.0f}, - {-1, ""}, -}; - -static bNodeSocketTemplate cmp_node_distance_matte_out[] = { - {SOCK_RGBA, N_("Image")}, - {SOCK_FLOAT, N_("Matte")}, - {-1, ""}, -}; - -static void node_composit_init_distance_matte(bNodeTree *UNUSED(ntree), bNode *node) -{ - NodeChroma *c = (NodeChroma *)MEM_callocN(sizeof(NodeChroma), "node chroma"); - node->storage = c; - c->channel = 1; - c->t1 = 0.1f; - c->t2 = 0.1f; -} - -void register_node_type_cmp_distance_matte(void) -{ - static bNodeType ntype; - - cmp_node_type_base(&ntype, CMP_NODE_DIST_MATTE, "Distance Key", NODE_CLASS_MATTE, NODE_PREVIEW); - node_type_socket_templates(&ntype, cmp_node_distance_matte_in, cmp_node_distance_matte_out); - node_type_init(&ntype, node_composit_init_distance_matte); - node_type_storage(&ntype, "NodeChroma", node_free_standard_storage, node_copy_standard_storage); - - 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 new file mode 100644 index 00000000000..82781588c9d --- /dev/null +++ b/source/blender/nodes/composite/nodes/node_composite_distance_matte.cc @@ -0,0 +1,83 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2006 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup cmpnodes + */ + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_composite_util.hh" + +/* ******************* channel Distance Matte ********************************* */ + +namespace blender::nodes::node_composite_distance_matte_cc { + +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_output<decl::Color>(N_("Image")); + b.add_output<decl::Float>(N_("Matte")); +} + +static void node_composit_init_distance_matte(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeChroma *c = MEM_cnew<NodeChroma>(__func__); + node->storage = c; + c->channel = 1; + c->t1 = 0.1f; + c->t2 = 0.1f; +} + +static void node_composit_buts_distance_matte(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiLayout *col, *row; + + col = uiLayoutColumn(layout, true); + + uiItemL(layout, IFACE_("Color Space:"), ICON_NONE); + row = uiLayoutRow(layout, false); + uiItemR(row, ptr, "channel", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_EXPAND, nullptr, ICON_NONE); + + uiItemR( + col, ptr, "tolerance", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(col, ptr, "falloff", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); +} + +} // namespace blender::nodes::node_composite_distance_matte_cc + +void register_node_type_cmp_distance_matte() +{ + namespace file_ns = blender::nodes::node_composite_distance_matte_cc; + + static bNodeType ntype; + + cmp_node_type_base(&ntype, CMP_NODE_DIST_MATTE, "Distance Key", NODE_CLASS_MATTE); + ntype.declare = file_ns::cmp_node_distance_matte_declare; + ntype.draw_buttons = file_ns::node_composit_buts_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); + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/composite/nodes/node_composite_doubleEdgeMask.cc b/source/blender/nodes/composite/nodes/node_composite_doubleEdgeMask.cc deleted file mode 100644 index 7c9a48efc2d..00000000000 --- a/source/blender/nodes/composite/nodes/node_composite_doubleEdgeMask.cc +++ /dev/null @@ -1,50 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2011 Blender Foundation. - * All rights reserved. - */ - -/** \file - * \ingroup cmpnodes - */ -#include "node_composite_util.hh" -/* **************** Double Edge Mask ******************** */ - -static bNodeSocketTemplate cmp_node_doubleedgemask_in[] = { - /* Inner mask socket definition. */ - {SOCK_FLOAT, "Inner Mask", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - /* Outer mask socket definition. */ - {SOCK_FLOAT, "Outer Mask", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - /* Input socket array terminator. */ - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_doubleedgemask_out[] = { - /* Output socket definition. */ - {SOCK_FLOAT, "Mask"}, - /* Output socket array terminator. */ - {-1, ""}, -}; - -void register_node_type_cmp_doubleedgemask(void) -{ - static bNodeType ntype; /* Allocate a node type data structure. */ - - cmp_node_type_base(&ntype, CMP_NODE_DOUBLEEDGEMASK, "Double Edge Mask", NODE_CLASS_MATTE, 0); - node_type_socket_templates(&ntype, cmp_node_doubleedgemask_in, cmp_node_doubleedgemask_out); - node_type_socket_templates(&ntype, cmp_node_doubleedgemask_in, cmp_node_doubleedgemask_out); - - 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 new file mode 100644 index 00000000000..959d4ecf69e --- /dev/null +++ b/source/blender/nodes/composite/nodes/node_composite_double_edge_mask.cc @@ -0,0 +1,67 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2011 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup cmpnodes + */ + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_composite_util.hh" + +/* **************** Double Edge Mask ******************** */ + +namespace blender::nodes::node_composite_double_edge_mask_cc { + +static void cmp_node_double_edge_mask_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Float>(N_("Inner Mask")).default_value(0.8f).min(0.0f).max(1.0f); + b.add_input<decl::Float>(N_("Outer Mask")).default_value(0.8f).min(0.0f).max(1.0f); + b.add_output<decl::Float>(N_("Mask")); +} + +static void node_composit_buts_double_edge_mask(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiLayout *col; + + col = uiLayoutColumn(layout, false); + + uiItemL(col, IFACE_("Inner Edge:"), ICON_NONE); + uiItemR(col, ptr, "inner_mode", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); + uiItemL(col, IFACE_("Buffer Edge:"), ICON_NONE); + uiItemR(col, ptr, "edge_mode", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); +} + +} // namespace blender::nodes::node_composite_double_edge_mask_cc + +void register_node_type_cmp_doubleedgemask() +{ + namespace file_ns = blender::nodes::node_composite_double_edge_mask_cc; + + static bNodeType ntype; /* Allocate a node type data structure. */ + + 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; + + 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 67196fb0d35..b2be8abf0cd 100644 --- a/source/blender/nodes/composite/nodes/node_composite_ellipsemask.cc +++ b/source/blender/nodes/composite/nodes/node_composite_ellipsemask.cc @@ -21,21 +21,25 @@ * \ingroup cmpnodes */ -#include "../node_composite_util.hh" +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_composite_util.hh" /* **************** SCALAR MATH ******************** */ -static bNodeSocketTemplate cmp_node_ellipsemask_in[] = { - {SOCK_FLOAT, N_("Mask"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Value"), 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f}, - {-1, ""}}; -static bNodeSocketTemplate cmp_node_ellipsemask_out[] = { - {SOCK_FLOAT, N_("Mask"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f}, {-1, ""}}; +namespace blender::nodes::node_composite_ellipsemask_cc { + +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); + b.add_input<decl::Float>(N_("Value")).default_value(1.0f).min(0.0f).max(1.0f); + b.add_output<decl::Float>(N_("Mask")); +} static void node_composit_init_ellipsemask(bNodeTree *UNUSED(ntree), bNode *node) { - NodeEllipseMask *data = (NodeEllipseMask *)MEM_callocN(sizeof(NodeEllipseMask), - "NodeEllipseMask"); + NodeEllipseMask *data = MEM_cnew<NodeEllipseMask>(__func__); data->x = 0.5; data->y = 0.5; data->width = 0.2; @@ -44,14 +48,33 @@ static void node_composit_init_ellipsemask(bNodeTree *UNUSED(ntree), bNode *node node->storage = data; } -void register_node_type_cmp_ellipsemask(void) +static void node_composit_buts_ellipsemask(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayout *row; + row = uiLayoutRow(layout, true); + uiItemR(row, ptr, "x", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(row, ptr, "y", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + row = uiLayoutRow(layout, true); + uiItemR(row, ptr, "width", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(row, ptr, "height", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + + uiItemR(layout, ptr, "rotation", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(layout, ptr, "mask_type", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); +} + +} // namespace blender::nodes::node_composite_ellipsemask_cc + +void register_node_type_cmp_ellipsemask() { + namespace file_ns = blender::nodes::node_composite_ellipsemask_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_MASK_ELLIPSE, "Ellipse Mask", NODE_CLASS_MATTE, 0); - node_type_socket_templates(&ntype, cmp_node_ellipsemask_in, cmp_node_ellipsemask_out); + cmp_node_type_base(&ntype, CMP_NODE_MASK_ELLIPSE, "Ellipse Mask", NODE_CLASS_MATTE); + ntype.declare = file_ns::cmp_node_ellipsemask_declare; + ntype.draw_buttons = file_ns::node_composit_buts_ellipsemask; node_type_size(&ntype, 260, 110, 320); - node_type_init(&ntype, node_composit_init_ellipsemask); + node_type_init(&ntype, file_ns::node_composit_init_ellipsemask); node_type_storage( &ntype, "NodeEllipseMask", node_free_standard_storage, node_copy_standard_storage); diff --git a/source/blender/nodes/composite/nodes/node_composite_exposure.cc b/source/blender/nodes/composite/nodes/node_composite_exposure.cc index fd959376afe..2798f4a46e8 100644 --- a/source/blender/nodes/composite/nodes/node_composite_exposure.cc +++ b/source/blender/nodes/composite/nodes/node_composite_exposure.cc @@ -25,23 +25,25 @@ /* **************** Exposure ******************** */ -namespace blender::nodes { +namespace blender::nodes::node_composite_exposure_cc { static void cmp_node_exposure_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input<decl::Float>("Exposure").min(-10.0f).max(10.0f); - b.add_output<decl::Color>("Image"); + 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_output<decl::Color>(N_("Image")); } -} // namespace blender::nodes +} // namespace blender::nodes::node_composite_exposure_cc -void register_node_type_cmp_exposure(void) +void register_node_type_cmp_exposure() { + namespace file_ns = blender::nodes::node_composite_exposure_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_EXPOSURE, "Exposure", NODE_CLASS_OP_COLOR, 0); - ntype.declare = blender::nodes::cmp_node_exposure_declare; + cmp_node_type_base(&ntype, CMP_NODE_EXPOSURE, "Exposure", NODE_CLASS_OP_COLOR); + ntype.declare = file_ns::cmp_node_exposure_declare; 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 f07619877f4..cd51e0d3c85 100644 --- a/source/blender/nodes/composite/nodes/node_composite_filter.cc +++ b/source/blender/nodes/composite/nodes/node_composite_filter.cc @@ -21,26 +21,40 @@ * \ingroup cmpnodes */ +#include "UI_interface.h" +#include "UI_resources.h" + #include "node_composite_util.hh" /* **************** FILTER ******************** */ -static bNodeSocketTemplate cmp_node_filter_in[] = { - {SOCK_FLOAT, N_("Fac"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_filter_out[] = { - {SOCK_RGBA, N_("Image")}, - {-1, ""}, -}; - -void register_node_type_cmp_filter(void) + +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_output<decl::Color>(N_("Image")); +} + +static void node_composit_buts_filter(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { + uiItemR(layout, ptr, "filter_type", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); +} + +} // namespace blender::nodes::node_composite_filter_cc + +void register_node_type_cmp_filter() +{ + namespace file_ns = blender::nodes::node_composite_filter_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_FILTER, "Filter", NODE_CLASS_OP_FILTER, NODE_PREVIEW); - node_type_socket_templates(&ntype, cmp_node_filter_in, cmp_node_filter_out); - node_type_label(&ntype, node_filter_label); + cmp_node_type_base(&ntype, CMP_NODE_FILTER, "Filter", NODE_CLASS_OP_FILTER); + ntype.declare = file_ns::cmp_node_filter_declare; + ntype.draw_buttons = file_ns::node_composit_buts_filter; + ntype.labelfunc = node_filter_label; + ntype.flag |= NODE_PREVIEW; 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 42aa3141f5c..dba98a42e93 100644 --- a/source/blender/nodes/composite/nodes/node_composite_flip.cc +++ b/source/blender/nodes/composite/nodes/node_composite_flip.cc @@ -21,25 +21,37 @@ * \ingroup cmpnodes */ +#include "UI_interface.h" +#include "UI_resources.h" + #include "node_composite_util.hh" /* **************** Flip ******************** */ -static bNodeSocketTemplate cmp_node_flip_in[] = { - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_flip_out[] = { - {SOCK_RGBA, N_("Image")}, - {-1, ""}, -}; +namespace blender::nodes::node_composite_flip_cc { -void register_node_type_cmp_flip(void) +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_output<decl::Color>(N_("Image")); +} + +static void node_composit_buts_flip(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "axis", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); +} + +} // namespace blender::nodes::node_composite_flip_cc + +void register_node_type_cmp_flip() +{ + namespace file_ns = blender::nodes::node_composite_flip_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_FLIP, "Flip", NODE_CLASS_DISTORT, 0); - node_type_socket_templates(&ntype, cmp_node_flip_in, cmp_node_flip_out); + 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; 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 a29a001688a..aafd558f0df 100644 --- a/source/blender/nodes/composite/nodes/node_composite_gamma.cc +++ b/source/blender/nodes/composite/nodes/node_composite_gamma.cc @@ -25,24 +25,29 @@ /* **************** Gamma Tools ******************** */ -namespace blender::nodes { +namespace blender::nodes::node_composite_gamma_cc { static void cmp_node_gamma_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input<decl::Float>("Gamma").default_value(1.0f).min(0.001f).max(10.0f).subtype( - PROP_UNSIGNED); - b.add_output<decl::Color>("Image"); + b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Float>(N_("Gamma")) + .default_value(1.0f) + .min(0.001f) + .max(10.0f) + .subtype(PROP_UNSIGNED); + b.add_output<decl::Color>(N_("Image")); } -} // namespace blender::nodes +} // namespace blender::nodes::node_composite_gamma_cc -void register_node_type_cmp_gamma(void) +void register_node_type_cmp_gamma() { + namespace file_ns = blender::nodes::node_composite_gamma_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_GAMMA, "Gamma", NODE_CLASS_OP_COLOR, 0); - ntype.declare = blender::nodes::cmp_node_gamma_declare; + cmp_node_type_base(&ntype, CMP_NODE_GAMMA, "Gamma", NODE_CLASS_OP_COLOR); + ntype.declare = file_ns::cmp_node_gamma_declare; 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 8a2fd1e1584..479eeef3808 100644 --- a/source/blender/nodes/composite/nodes/node_composite_glare.cc +++ b/source/blender/nodes/composite/nodes/node_composite_glare.cc @@ -21,20 +21,24 @@ * \ingroup cmpnodes */ +#include "RNA_access.h" + +#include "UI_interface.h" +#include "UI_resources.h" + #include "node_composite_util.hh" -static bNodeSocketTemplate cmp_node_glare_in[] = { - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_glare_out[] = { - {SOCK_RGBA, N_("Image")}, - {-1, ""}, -}; +namespace blender::nodes::node_composite_glare_cc { + +static void cmp_node_glare_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output<decl::Color>(N_("Image")); +} static void node_composit_init_glare(bNodeTree *UNUSED(ntree), bNode *node) { - NodeGlare *ndg = (NodeGlare *)MEM_callocN(sizeof(NodeGlare), "node glare data"); + NodeGlare *ndg = MEM_cnew<NodeGlare>(__func__); ndg->quality = 1; ndg->type = 2; ndg->iter = 3; @@ -49,13 +53,56 @@ static void node_composit_init_glare(bNodeTree *UNUSED(ntree), bNode *node) node->storage = ndg; } -void register_node_type_cmp_glare(void) +static void node_composit_buts_glare(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { + uiItemR(layout, ptr, "glare_type", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); + uiItemR(layout, ptr, "quality", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); + + if (RNA_enum_get(ptr, "glare_type") != 1) { + uiItemR(layout, ptr, "iterations", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + + if (RNA_enum_get(ptr, "glare_type") != 0) { + uiItemR(layout, + ptr, + "color_modulation", + UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, + nullptr, + ICON_NONE); + } + } + + uiItemR(layout, ptr, "mix", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(layout, ptr, "threshold", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + + if (RNA_enum_get(ptr, "glare_type") == 2) { + uiItemR(layout, ptr, "streaks", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(layout, ptr, "angle_offset", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + } + if (RNA_enum_get(ptr, "glare_type") == 0 || RNA_enum_get(ptr, "glare_type") == 2) { + uiItemR( + layout, ptr, "fade", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + + if (RNA_enum_get(ptr, "glare_type") == 0) { + uiItemR(layout, ptr, "use_rotate_45", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + } + } + if (RNA_enum_get(ptr, "glare_type") == 1) { + uiItemR(layout, ptr, "size", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + } +} + +} // namespace blender::nodes::node_composite_glare_cc + +void register_node_type_cmp_glare() +{ + namespace file_ns = blender::nodes::node_composite_glare_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_GLARE, "Glare", NODE_CLASS_OP_FILTER, 0); - node_type_socket_templates(&ntype, cmp_node_glare_in, cmp_node_glare_out); - node_type_init(&ntype, node_composit_init_glare); + cmp_node_type_base(&ntype, CMP_NODE_GLARE, "Glare", NODE_CLASS_OP_FILTER); + ntype.declare = file_ns::cmp_node_glare_declare; + 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); nodeRegisterType(&ntype); diff --git a/source/blender/nodes/composite/nodes/node_composite_hueSatVal.cc b/source/blender/nodes/composite/nodes/node_composite_hue_sat_val.cc index 07746918a94..c98cfcdba02 100644 --- a/source/blender/nodes/composite/nodes/node_composite_hueSatVal.cc +++ b/source/blender/nodes/composite/nodes/node_composite_hue_sat_val.cc @@ -25,30 +25,36 @@ /* **************** Hue Saturation ******************** */ -namespace blender::nodes { +namespace blender::nodes::node_composite_hue_sat_val_cc { static void cmp_node_huesatval_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input<decl::Float>("Hue").default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_input<decl::Float>("Saturation") + 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::Float>(N_("Saturation")) .default_value(1.0f) .min(0.0f) .max(2.0f) .subtype(PROP_FACTOR); - b.add_input<decl::Float>("Value").default_value(1.0f).min(0.0f).max(2.0f).subtype(PROP_FACTOR); - b.add_input<decl::Float>("Fac").default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_output<decl::Color>("Image"); + 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); + b.add_output<decl::Color>(N_("Image")); } -} // namespace blender::nodes +} // namespace blender::nodes::node_composite_hue_sat_val_cc -void register_node_type_cmp_hue_sat(void) +void register_node_type_cmp_hue_sat() { + namespace file_ns = blender::nodes::node_composite_hue_sat_val_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_HUE_SAT, "Hue Saturation Value", NODE_CLASS_OP_COLOR, 0); - ntype.declare = blender::nodes::cmp_node_huesatval_declare; + cmp_node_type_base(&ntype, CMP_NODE_HUE_SAT, "Hue Saturation Value", NODE_CLASS_OP_COLOR); + ntype.declare = file_ns::cmp_node_huesatval_declare; 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 39014896a7b..347c1588af3 100644 --- a/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc +++ b/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc @@ -23,17 +23,15 @@ #include "node_composite_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_composite_huecorrect_cc { static void cmp_node_huecorrect_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Float>("Fac").default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_output<decl::Color>("Image"); + 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_output<decl::Color>(N_("Image")); } -} // namespace blender::nodes - static void node_composit_init_huecorrect(bNodeTree *UNUSED(ntree), bNode *node) { node->storage = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); @@ -51,14 +49,18 @@ static void node_composit_init_huecorrect(bNodeTree *UNUSED(ntree), bNode *node) cumapping->cur = 1; } -void register_node_type_cmp_huecorrect(void) +} // namespace blender::nodes::node_composite_huecorrect_cc + +void register_node_type_cmp_huecorrect() { + namespace file_ns = blender::nodes::node_composite_huecorrect_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_HUECORRECT, "Hue Correct", NODE_CLASS_OP_COLOR, 0); - ntype.declare = blender::nodes::cmp_node_huecorrect_declare; + cmp_node_type_base(&ntype, CMP_NODE_HUECORRECT, "Hue Correct", NODE_CLASS_OP_COLOR); + ntype.declare = file_ns::cmp_node_huecorrect_declare; node_type_size(&ntype, 320, 140, 500); - node_type_init(&ntype, node_composit_init_huecorrect); + node_type_init(&ntype, file_ns::node_composit_init_huecorrect); node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves); 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 new file mode 100644 index 00000000000..4b7674286dd --- /dev/null +++ b/source/blender/nodes/composite/nodes/node_composite_id_mask.cc @@ -0,0 +1,58 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2006 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup cmpnodes + */ + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_composite_util.hh" + +/* **************** ID Mask ******************** */ + +namespace blender::nodes::node_composite_id_mask_cc { + +static void cmp_node_idmask_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Float>(N_("ID value")).default_value(1.0f).min(0.0f).max(1.0f); + b.add_output<decl::Float>(N_("Alpha")); +} + +static void node_composit_buts_id_mask(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "index", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(layout, ptr, "use_antialiasing", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); +} + +} // namespace blender::nodes::node_composite_id_mask_cc + +void register_node_type_cmp_idmask() +{ + namespace file_ns = blender::nodes::node_composite_id_mask_cc; + + static bNodeType ntype; + + 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; + + 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 3cef3a7625f..f2b9fbc2215 100644 --- a/source/blender/nodes/composite/nodes/node_composite_image.cc +++ b/source/blender/nodes/composite/nodes/node_composite_image.cc @@ -26,16 +26,21 @@ #include "BLI_linklist.h" #include "BLI_utildefines.h" -#include "DNA_scene_types.h" - -#include "RE_engine.h" - #include "BKE_context.h" #include "BKE_global.h" #include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_scene.h" +#include "DNA_scene_types.h" + +#include "RE_engine.h" + +#include "RNA_access.h" + +#include "UI_interface.h" +#include "UI_resources.h" + /* **************** IMAGE (and RenderResult, multilayer image) ******************** */ static bNodeSocketTemplate cmp_node_rlayers_out[] = { @@ -103,8 +108,7 @@ static void cmp_node_image_add_pass_output(bNodeTree *ntree, sock = nodeAddStaticSocket(ntree, node, SOCK_OUT, type, PROP_NONE, name, name); } /* extra socket info */ - NodeImageLayer *sockdata = (NodeImageLayer *)MEM_callocN(sizeof(NodeImageLayer), - "node image layer"); + NodeImageLayer *sockdata = MEM_cnew<NodeImageLayer>(__func__); sock->storage = sockdata; } @@ -141,7 +145,6 @@ static void cmp_node_image_create_outputs(bNodeTree *ntree, * So we manually construct image user to be sure first * image from sequence (that one which is set as filename * for image data-block) is used for sockets detection. */ - load_iuser.ok = 1; load_iuser.framenr = offset; /* make sure ima->type is correct */ @@ -266,7 +269,12 @@ void node_cmp_rlayers_register_pass(bNodeTree *ntree, } } -static void cmp_node_rlayer_create_outputs_cb(void *UNUSED(userdata), +struct CreateOutputUserData { + bNodeTree &ntree; + bNode &node; +}; + +static void cmp_node_rlayer_create_outputs_cb(void *userdata, Scene *scene, ViewLayer *view_layer, const char *name, @@ -274,18 +282,8 @@ static void cmp_node_rlayer_create_outputs_cb(void *UNUSED(userdata), const char *UNUSED(chanid), eNodeSocketDatatype type) { - /* Register the pass in all scenes that have a render layer node for this layer. - * Since multiple scenes can be used in the compositor, the code must loop over all scenes - * and check whether their nodetree has a node that needs to be updated. */ - /* NOTE: using G_MAIN seems valid here, - * unless we want to register that for every other temp Main we could generate??? */ - ntreeCompositRegisterPass(scene->nodetree, scene, view_layer, name, type); - - for (Scene *sce = (Scene *)G_MAIN->scenes.first; sce; sce = (Scene *)sce->id.next) { - if (sce->nodetree && sce != scene) { - ntreeCompositRegisterPass(sce->nodetree, scene, view_layer, name, type); - } - } + CreateOutputUserData &data = *(CreateOutputUserData *)userdata; + node_cmp_rlayers_register_pass(&data.ntree, &data.node, scene, view_layer, name, type); } static void cmp_node_rlayer_create_outputs(bNodeTree *ntree, @@ -305,14 +303,17 @@ static void cmp_node_rlayer_create_outputs(bNodeTree *ntree, data->prev_index = -1; node->storage = data; + CreateOutputUserData userdata = {*ntree, *node}; + RenderEngine *engine = RE_engine_create(engine_type); RE_engine_update_render_passes( - engine, scene, view_layer, cmp_node_rlayer_create_outputs_cb, nullptr); + engine, scene, view_layer, cmp_node_rlayer_create_outputs_cb, &userdata); RE_engine_free(engine); if ((scene->r.mode & R_EDGE_FRS) && (view_layer->freestyle_config.flags & FREESTYLE_AS_RENDER_PASS)) { - ntreeCompositRegisterPass(ntree, scene, view_layer, RE_PASSNAME_FREESTYLE, SOCK_RGBA); + node_cmp_rlayers_register_pass( + ntree, node, scene, view_layer, RE_PASSNAME_FREESTYLE, SOCK_RGBA); } MEM_freeN(data); @@ -373,7 +374,8 @@ static void cmp_node_image_verify_outputs(bNodeTree *ntree, bNode *node, bool rl for (sock = (bNodeSocket *)node->outputs.first; sock; sock = sock_next, sock_index++) { sock_next = sock->next; if (BLI_linklist_index(available_sockets.list, sock) >= 0) { - sock->flag &= ~(SOCK_UNAVAIL | SOCK_HIDDEN); + sock->flag &= ~SOCK_HIDDEN; + nodeSetSocketAvailability(ntree, sock, true); } else { bNodeLink *link; @@ -387,7 +389,7 @@ static void cmp_node_image_verify_outputs(bNodeTree *ntree, bNode *node, bool rl nodeRemoveSocket(ntree, node, sock); } else { - sock->flag |= SOCK_UNAVAIL; + nodeSetSocketAvailability(ntree, sock, false); } } } @@ -395,6 +397,8 @@ static void cmp_node_image_verify_outputs(bNodeTree *ntree, bNode *node, bool rl BLI_linklist_free(available_sockets.list, nullptr); } +namespace blender::nodes::node_composite_image_cc { + static void cmp_node_image_update(bNodeTree *ntree, bNode *node) { /* avoid unnecessary updates, only changes to the image/image user data are of interest */ @@ -407,11 +411,10 @@ static void cmp_node_image_update(bNodeTree *ntree, bNode *node) static void node_composit_init_image(bNodeTree *ntree, bNode *node) { - ImageUser *iuser = (ImageUser *)MEM_callocN(sizeof(ImageUser), "node image user"); + ImageUser *iuser = MEM_cnew<ImageUser>(__func__); node->storage = iuser; iuser->frames = 1; iuser->sfra = 1; - iuser->ok = 1; iuser->flag |= IMA_ANIM_ALWAYS; /* setup initial outputs */ @@ -444,15 +447,21 @@ static void node_composit_copy_image(bNodeTree *UNUSED(dest_ntree), } } -void register_node_type_cmp_image(void) +} // namespace blender::nodes::node_composite_image_cc + +void register_node_type_cmp_image() { + namespace file_ns = blender::nodes::node_composite_image_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_IMAGE, "Image", NODE_CLASS_INPUT, NODE_PREVIEW); - node_type_init(&ntype, node_composit_init_image); - node_type_storage(&ntype, "ImageUser", node_composit_free_image, node_composit_copy_image); - node_type_update(&ntype, cmp_node_image_update); - node_type_label(&ntype, node_image_label); + cmp_node_type_base(&ntype, CMP_NODE_IMAGE, "Image", NODE_CLASS_INPUT); + node_type_init(&ntype, file_ns::node_composit_init_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.labelfunc = node_image_label; + ntype.flag |= NODE_PREVIEW; nodeRegisterType(&ntype); } @@ -474,6 +483,8 @@ 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 { + static void node_composit_init_rlayers(const bContext *C, PointerRNA *ptr) { Scene *scene = CTX_data_scene(C); @@ -485,8 +496,7 @@ static void node_composit_init_rlayers(const bContext *C, PointerRNA *ptr) for (bNodeSocket *sock = (bNodeSocket *)node->outputs.first; sock; sock = sock->next, sock_index++) { - NodeImageLayer *sockdata = (NodeImageLayer *)MEM_callocN(sizeof(NodeImageLayer), - "node image layer"); + NodeImageLayer *sockdata = MEM_cnew<NodeImageLayer>(__func__); sock->storage = sockdata; BLI_strncpy(sockdata->pass_name, @@ -500,7 +510,7 @@ static bool node_composit_poll_rlayers(bNodeType *UNUSED(ntype), const char **r_disabled_hint) { if (!STREQ(ntree->idname, "CompositorNodeTree")) { - *r_disabled_hint = "Not a compositor node tree"; + *r_disabled_hint = TIP_("Not a compositor node tree"); return false; } @@ -517,7 +527,8 @@ static bool node_composit_poll_rlayers(bNodeType *UNUSED(ntype), } if (scene == nullptr) { - *r_disabled_hint = "The node tree must be the compositing node tree of any scene in the file"; + *r_disabled_hint = TIP_( + "The node tree must be the compositing node tree of any scene in the file"); return false; } return true; @@ -555,16 +566,66 @@ static void cmp_node_rlayers_update(bNodeTree *ntree, bNode *node) cmp_node_update_default(ntree, node); } -void register_node_type_cmp_rlayers(void) +static void node_composit_buts_viewlayers(uiLayout *layout, bContext *C, PointerRNA *ptr) +{ + bNode *node = (bNode *)ptr->data; + uiLayout *col, *row; + + uiTemplateID(layout, + C, + ptr, + "scene", + nullptr, + nullptr, + nullptr, + UI_TEMPLATE_ID_FILTER_ALL, + false, + nullptr); + + if (!node->id) { + return; + } + + col = uiLayoutColumn(layout, false); + row = uiLayoutRow(col, true); + uiItemR(row, ptr, "layer", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); + + PropertyRNA *prop = RNA_struct_find_property(ptr, "layer"); + const char *layer_name; + if (!(RNA_property_enum_identifier( + C, ptr, prop, RNA_property_enum_get(ptr, prop), &layer_name))) { + return; + } + + PointerRNA scn_ptr; + char scene_name[MAX_ID_NAME - 2]; + scn_ptr = RNA_pointer_get(ptr, "scene"); + RNA_string_get(&scn_ptr, "name", scene_name); + + PointerRNA op_ptr; + uiItemFullO( + row, "RENDER_OT_render", "", ICON_RENDER_STILL, nullptr, WM_OP_INVOKE_DEFAULT, 0, &op_ptr); + RNA_string_set(&op_ptr, "layer", layer_name); + RNA_string_set(&op_ptr, "scene", scene_name); +} + +} // namespace blender::nodes::node_composite_image_cc + +void register_node_type_cmp_rlayers() { + namespace file_ns = blender::nodes::node_composite_image_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_R_LAYERS, "Render Layers", NODE_CLASS_INPUT, NODE_PREVIEW); + cmp_node_type_base(&ntype, CMP_NODE_R_LAYERS, "Render Layers", NODE_CLASS_INPUT); node_type_socket_templates(&ntype, nullptr, cmp_node_rlayers_out); - ntype.initfunc_api = node_composit_init_rlayers; - ntype.poll = node_composit_poll_rlayers; - node_type_storage(&ntype, nullptr, node_composit_free_rlayers, node_composit_copy_rlayers); - node_type_update(&ntype, cmp_node_rlayers_update); + 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.flag |= NODE_PREVIEW; + node_type_storage( + &ntype, nullptr, file_ns::node_composit_free_rlayers, file_ns::node_composit_copy_rlayers); + node_type_update(&ntype, file_ns::cmp_node_rlayers_update); node_type_init(&ntype, node_cmp_rlayers_outputs); node_type_size_preset(&ntype, NODE_SIZE_LARGE); diff --git a/source/blender/nodes/composite/nodes/node_composite_inpaint.cc b/source/blender/nodes/composite/nodes/node_composite_inpaint.cc index d0ff97a2ca0..f470038ad39 100644 --- a/source/blender/nodes/composite/nodes/node_composite_inpaint.cc +++ b/source/blender/nodes/composite/nodes/node_composite_inpaint.cc @@ -21,20 +21,37 @@ * \ingroup cmpnodes */ +#include "UI_interface.h" +#include "UI_resources.h" + #include "node_composite_util.hh" /* **************** Inpaint/ ******************** */ -static bNodeSocketTemplate cmp_node_inpaint_in[] = { - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, {-1, ""}}; -static bNodeSocketTemplate cmp_node_inpaint_out[] = {{SOCK_RGBA, N_("Image")}, {-1, ""}}; +namespace blender::nodes::node_composite_inpaint_cc { -void register_node_type_cmp_inpaint(void) +static void cmp_node_inpaint_declare(NodeDeclarationBuilder &b) { + b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output<decl::Color>(N_("Image")); +} + +static void node_composit_buts_inpaint(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "distance", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); +} + +} // namespace blender::nodes::node_composite_inpaint_cc + +void register_node_type_cmp_inpaint() +{ + namespace file_ns = blender::nodes::node_composite_inpaint_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_INPAINT, "Inpaint", NODE_CLASS_OP_FILTER, 0); - node_type_socket_templates(&ntype, cmp_node_inpaint_in, cmp_node_inpaint_out); + 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; 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 57b7ed36ccd..c5435f5fb92 100644 --- a/source/blender/nodes/composite/nodes/node_composite_invert.cc +++ b/source/blender/nodes/composite/nodes/node_composite_invert.cc @@ -21,34 +21,48 @@ * \ingroup cmpnodes */ +#include "UI_interface.h" +#include "UI_resources.h" + #include "node_composite_util.hh" /* **************** INVERT ******************** */ -namespace blender::nodes { +namespace blender::nodes::node_composite_invert_cc { static void cmp_node_invert_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Float>("Fac").default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_input<decl::Color>("Color").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_output<decl::Color>("Color"); + 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_output<decl::Color>(N_("Color")); } -} // namespace blender::nodes - static void node_composit_init_invert(bNodeTree *UNUSED(ntree), bNode *node) { node->custom1 |= CMP_CHAN_RGB; } -/* custom1 = mix type */ -void register_node_type_cmp_invert(void) +static void node_composit_buts_invert(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayout *col; + + col = uiLayoutColumn(layout, false); + uiItemR(col, ptr, "invert_rgb", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(col, ptr, "invert_alpha", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); +} + +} // namespace blender::nodes::node_composite_invert_cc + +void register_node_type_cmp_invert() { + namespace file_ns = blender::nodes::node_composite_invert_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_INVERT, "Invert", NODE_CLASS_OP_COLOR, 0); - ntype.declare = blender::nodes::cmp_node_invert_declare; - node_type_init(&ntype, node_composit_init_invert); + cmp_node_type_base(&ntype, CMP_NODE_INVERT, "Invert", NODE_CLASS_OP_COLOR); + 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); 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 d5547161069..26a7297acd5 100644 --- a/source/blender/nodes/composite/nodes/node_composite_keying.cc +++ b/source/blender/nodes/composite/nodes/node_composite_keying.cc @@ -21,34 +21,35 @@ * \ingroup cmpnodes */ +#include "BLI_math_base.h" + #include "BLT_translation.h" #include "DNA_movieclip_types.h" -#include "BLI_math_base.h" +#include "UI_interface.h" +#include "UI_resources.h" #include "node_composite_util.hh" -/* **************** Translate ******************** */ +/* **************** Keying ******************** */ -static bNodeSocketTemplate cmp_node_keying_in[] = { - {SOCK_RGBA, "Image", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f}, - {SOCK_RGBA, "Key Color", 1.0f, 1.0f, 1.0f, 1.0f}, - {SOCK_FLOAT, "Garbage Matte", 0.0f, 1.0f, 1.0f, 1.0f}, - {SOCK_FLOAT, "Core Matte", 0.0f, 1.0f, 1.0f, 1.0f}, - {-1, ""}, -}; +namespace blender::nodes::node_composite_keying_cc { -static bNodeSocketTemplate cmp_node_keying_out[] = { - {SOCK_RGBA, "Image"}, - {SOCK_FLOAT, "Matte"}, - {SOCK_FLOAT, "Edges"}, - {-1, ""}, -}; +static void cmp_node_keying_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Color>(N_("Image")).default_value({0.8f, 0.8f, 0.8f, 1.0f}); + b.add_input<decl::Color>(N_("Key Color")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Float>(N_("Garbage Matte")).hide_value(); + b.add_input<decl::Float>(N_("Core Matte")).hide_value(); + b.add_output<decl::Color>(N_("Image")); + b.add_output<decl::Float>(N_("Matte")); + b.add_output<decl::Float>(N_("Edges")); +} static void node_composit_init_keying(bNodeTree *UNUSED(ntree), bNode *node) { - NodeKeyingData *data = (NodeKeyingData *)MEM_callocN(sizeof(NodeKeyingData), "node keying data"); + NodeKeyingData *data = MEM_cnew<NodeKeyingData>(__func__); data->screen_balance = 0.5f; data->despill_balance = 0.5f; @@ -60,13 +61,36 @@ static void node_composit_init_keying(bNodeTree *UNUSED(ntree), bNode *node) node->storage = data; } -void register_node_type_cmp_keying(void) +static void node_composit_buts_keying(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { + /* bNode *node = (bNode*)ptr->data; */ /* UNUSED */ + + uiItemR(layout, ptr, "blur_pre", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(layout, ptr, "screen_balance", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(layout, ptr, "despill_factor", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(layout, ptr, "despill_balance", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(layout, ptr, "edge_kernel_radius", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(layout, ptr, "edge_kernel_tolerance", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(layout, ptr, "clip_black", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(layout, ptr, "clip_white", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(layout, ptr, "dilate_distance", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(layout, ptr, "feather_falloff", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(layout, ptr, "feather_distance", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(layout, ptr, "blur_post", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); +} + +} // namespace blender::nodes::node_composite_keying_cc + +void register_node_type_cmp_keying() +{ + namespace file_ns = blender::nodes::node_composite_keying_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_KEYING, "Keying", NODE_CLASS_MATTE, 0); - node_type_socket_templates(&ntype, cmp_node_keying_in, cmp_node_keying_out); - node_type_init(&ntype, node_composit_init_keying); + cmp_node_type_base(&ntype, CMP_NODE_KEYING, "Keying", NODE_CLASS_MATTE); + ntype.declare = file_ns::cmp_node_keying_declare; + ntype.draw_buttons = file_ns::node_composit_buts_keying; + node_type_init(&ntype, file_ns::node_composit_init_keying); node_type_storage( &ntype, "NodeKeyingData", node_free_standard_storage, node_copy_standard_storage); diff --git a/source/blender/nodes/composite/nodes/node_composite_keyingscreen.cc b/source/blender/nodes/composite/nodes/node_composite_keyingscreen.cc index c976a92b92d..4862fcaa2e0 100644 --- a/source/blender/nodes/composite/nodes/node_composite_keyingscreen.cc +++ b/source/blender/nodes/composite/nodes/node_composite_keyingscreen.cc @@ -26,29 +26,67 @@ #include "BLI_math_base.h" #include "BLI_math_color.h" +#include "RNA_access.h" + +#include "UI_interface.h" +#include "UI_resources.h" + #include "node_composite_util.hh" -/* **************** Translate ******************** */ +/* **************** Keying Screen ******************** */ -static bNodeSocketTemplate cmp_node_keyingscreen_out[] = { - {SOCK_RGBA, "Screen"}, - {-1, ""}, -}; +namespace blender::nodes::node_composite_keyingscreen_cc { + +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) { - NodeKeyingScreenData *data = (NodeKeyingScreenData *)MEM_callocN(sizeof(NodeKeyingScreenData), - "node keyingscreen data"); + NodeKeyingScreenData *data = MEM_cnew<NodeKeyingScreenData>(__func__); node->storage = data; } -void register_node_type_cmp_keyingscreen(void) +static void node_composit_buts_keyingscreen(uiLayout *layout, bContext *C, PointerRNA *ptr) +{ + bNode *node = (bNode *)ptr->data; + + uiTemplateID(layout, + C, + ptr, + "clip", + nullptr, + nullptr, + nullptr, + UI_TEMPLATE_ID_FILTER_ALL, + false, + nullptr); + + if (node->id) { + MovieClip *clip = (MovieClip *)node->id; + uiLayout *col; + PointerRNA tracking_ptr; + + RNA_pointer_create(&clip->id, &RNA_MovieTracking, &clip->tracking, &tracking_ptr); + + col = uiLayoutColumn(layout, true); + uiItemPointerR(col, ptr, "tracking_object", &tracking_ptr, "objects", "", ICON_OBJECT_DATA); + } +} + +} // namespace blender::nodes::node_composite_keyingscreen_cc + +void register_node_type_cmp_keyingscreen() { + namespace file_ns = blender::nodes::node_composite_keyingscreen_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_KEYINGSCREEN, "Keying Screen", NODE_CLASS_MATTE, 0); - node_type_socket_templates(&ntype, nullptr, cmp_node_keyingscreen_out); - node_type_init(&ntype, node_composit_init_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); node_type_storage( &ntype, "NodeKeyingScreenData", node_free_standard_storage, node_copy_standard_storage); diff --git a/source/blender/nodes/composite/nodes/node_composite_lensdist.cc b/source/blender/nodes/composite/nodes/node_composite_lensdist.cc index 2a8dc035792..2d85e53016d 100644 --- a/source/blender/nodes/composite/nodes/node_composite_lensdist.cc +++ b/source/blender/nodes/composite/nodes/node_composite_lensdist.cc @@ -21,33 +21,55 @@ * \ingroup cmpnodes */ +#include "RNA_access.h" + +#include "UI_interface.h" +#include "UI_resources.h" + #include "node_composite_util.hh" -static bNodeSocketTemplate cmp_node_lensdist_in[] = { - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, - {SOCK_FLOAT, N_("Distort"), 0.0f, 0.0f, 0.0f, 0.0f, -0.999f, 1.0f, PROP_NONE}, - {SOCK_FLOAT, N_("Dispersion"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_lensdist_out[] = { - {SOCK_RGBA, N_("Image")}, - {-1, ""}, -}; +namespace blender::nodes::node_composite_lensdist_cc { + +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_output<decl::Color>(N_("Image")); +} static void node_composit_init_lensdist(bNodeTree *UNUSED(ntree), bNode *node) { - NodeLensDist *nld = (NodeLensDist *)MEM_callocN(sizeof(NodeLensDist), "node lensdist data"); + NodeLensDist *nld = MEM_cnew<NodeLensDist>(__func__); nld->jit = nld->proj = nld->fit = 0; node->storage = nld; } -void register_node_type_cmp_lensdist(void) +static void node_composit_buts_lensdist(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayout *col; + + col = uiLayoutColumn(layout, false); + uiItemR(col, ptr, "use_projector", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + + col = uiLayoutColumn(col, false); + uiLayoutSetActive(col, RNA_boolean_get(ptr, "use_projector") == false); + uiItemR(col, ptr, "use_jitter", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(col, ptr, "use_fit", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); +} + +} // namespace blender::nodes::node_composite_lensdist_cc + +void register_node_type_cmp_lensdist() { + namespace file_ns = blender::nodes::node_composite_lensdist_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_LENSDIST, "Lens Distortion", NODE_CLASS_DISTORT, 0); - node_type_socket_templates(&ntype, cmp_node_lensdist_in, cmp_node_lensdist_out); - node_type_init(&ntype, node_composit_init_lensdist); + cmp_node_type_base(&ntype, CMP_NODE_LENSDIST, "Lens Distortion", NODE_CLASS_DISTORT); + ntype.declare = file_ns::cmp_node_lensdist_declare; + ntype.draw_buttons = file_ns::node_composit_buts_lensdist; + node_type_init(&ntype, file_ns::node_composit_init_lensdist); node_type_storage( &ntype, "NodeLensDist", node_free_standard_storage, node_copy_standard_storage); diff --git a/source/blender/nodes/composite/nodes/node_composite_levels.cc b/source/blender/nodes/composite/nodes/node_composite_levels.cc index aaab8dcc874..57202d95cb7 100644 --- a/source/blender/nodes/composite/nodes/node_composite_levels.cc +++ b/source/blender/nodes/composite/nodes/node_composite_levels.cc @@ -21,33 +21,45 @@ * \ingroup cmpnodes */ +#include "UI_interface.h" +#include "UI_resources.h" + #include "node_composite_util.hh" /* **************** LEVELS ******************** */ -namespace blender::nodes { +namespace blender::nodes::node_composite_levels_cc { static void cmp_node_levels_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>("Image").default_value({0.0f, 0.0f, 0.0f, 1.0f}); - b.add_output<decl::Float>("Mean"); - b.add_output<decl::Float>("Std Dev"); + b.add_input<decl::Color>(N_("Image")).default_value({0.0f, 0.0f, 0.0f, 1.0f}); + b.add_output<decl::Float>(N_("Mean")); + b.add_output<decl::Float>(N_("Std Dev")); } -} // namespace blender::nodes - static void node_composit_init_view_levels(bNodeTree *UNUSED(ntree), bNode *node) { node->custom1 = 1; /* All channels. */ } -void register_node_type_cmp_view_levels(void) +static void node_composit_buts_view_levels(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { + uiItemR(layout, ptr, "channel", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); +} + +} // namespace blender::nodes::node_composite_levels_cc + +void register_node_type_cmp_view_levels() +{ + namespace file_ns = blender::nodes::node_composite_levels_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_VIEW_LEVELS, "Levels", NODE_CLASS_OUTPUT, NODE_PREVIEW); - ntype.declare = blender::nodes::cmp_node_levels_declare; - node_type_init(&ntype, node_composit_init_view_levels); + cmp_node_type_base(&ntype, CMP_NODE_VIEW_LEVELS, "Levels", NODE_CLASS_OUTPUT); + ntype.declare = file_ns::cmp_node_levels_declare; + 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); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_lummaMatte.cc b/source/blender/nodes/composite/nodes/node_composite_luma_matte.cc index 600406d22b9..851b218b617 100644 --- a/source/blender/nodes/composite/nodes/node_composite_lummaMatte.cc +++ b/source/blender/nodes/composite/nodes/node_composite_luma_matte.cc @@ -21,35 +21,54 @@ * \ingroup cmpnodes */ +#include "UI_interface.h" +#include "UI_resources.h" + #include "node_composite_util.hh" /* ******************* Luma Matte Node ********************************* */ -static bNodeSocketTemplate cmp_node_luma_matte_in[] = { - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_luma_matte_out[] = { - {SOCK_RGBA, N_("Image")}, - {SOCK_FLOAT, N_("Matte")}, - {-1, ""}, -}; +namespace blender::nodes::node_composite_luma_matte_cc { + +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_output<decl::Color>(N_("Image")); + b.add_output<decl::Float>(N_("Matte")); +} static void node_composit_init_luma_matte(bNodeTree *UNUSED(ntree), bNode *node) { - NodeChroma *c = (NodeChroma *)MEM_callocN(sizeof(NodeChroma), "node chroma"); + NodeChroma *c = MEM_cnew<NodeChroma>(__func__); node->storage = c; c->t1 = 1.0f; c->t2 = 0.0f; } -void register_node_type_cmp_luma_matte(void) +static void node_composit_buts_luma_matte(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayout *col; + + col = uiLayoutColumn(layout, true); + uiItemR( + col, ptr, "limit_max", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR( + col, ptr, "limit_min", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); +} + +} // namespace blender::nodes::node_composite_luma_matte_cc + +void register_node_type_cmp_luma_matte() { + namespace file_ns = blender::nodes::node_composite_luma_matte_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_LUMA_MATTE, "Luminance Key", NODE_CLASS_MATTE, NODE_PREVIEW); - node_type_socket_templates(&ntype, cmp_node_luma_matte_in, cmp_node_luma_matte_out); - node_type_init(&ntype, node_composit_init_luma_matte); + cmp_node_type_base(&ntype, CMP_NODE_LUMA_MATTE, "Luminance Key", NODE_CLASS_MATTE); + ntype.declare = file_ns::cmp_node_luma_matte_declare; + ntype.draw_buttons = file_ns::node_composit_buts_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); nodeRegisterType(&ntype); diff --git a/source/blender/nodes/composite/nodes/node_composite_mapRange.cc b/source/blender/nodes/composite/nodes/node_composite_mapRange.cc deleted file mode 100644 index 808ad538e55..00000000000 --- a/source/blender/nodes/composite/nodes/node_composite_mapRange.cc +++ /dev/null @@ -1,48 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2006 Blender Foundation. - * All rights reserved. - */ - -/** \file - * \ingroup cmpnodes - */ - -#include "node_composite_util.hh" - -/* **************** MAP VALUE ******************** */ -static bNodeSocketTemplate cmp_node_map_range_in[] = { - {SOCK_FLOAT, N_("Value"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {SOCK_FLOAT, N_("From Min"), 0.0f, 1.0f, 1.0f, 1.0f, -10000.0f, 10000.0f, PROP_NONE}, - {SOCK_FLOAT, N_("From Max"), 1.0f, 1.0f, 1.0f, 1.0f, -10000.0f, 10000.0f, PROP_NONE}, - {SOCK_FLOAT, N_("To Min"), 0.0f, 1.0f, 1.0f, 1.0f, -10000.0f, 10000.0f, PROP_NONE}, - {SOCK_FLOAT, N_("To Max"), 1.0f, 1.0f, 1.0f, 1.0f, -10000.0f, 10000.0f, PROP_NONE}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_map_range_out[] = { - {SOCK_FLOAT, N_("Value")}, - {-1, ""}, -}; - -void register_node_type_cmp_map_range(void) -{ - static bNodeType ntype; - - cmp_node_type_base(&ntype, CMP_NODE_MAP_RANGE, "Map Range", NODE_CLASS_OP_VECTOR, 0); - node_type_socket_templates(&ntype, cmp_node_map_range_in, cmp_node_map_range_out); - - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/composite/nodes/node_composite_mapValue.cc b/source/blender/nodes/composite/nodes/node_composite_mapValue.cc deleted file mode 100644 index 25c00c2ba13..00000000000 --- a/source/blender/nodes/composite/nodes/node_composite_mapValue.cc +++ /dev/null @@ -1,51 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2006 Blender Foundation. - * All rights reserved. - */ - -/** \file - * \ingroup cmpnodes - */ - -#include "node_composite_util.hh" - -/* **************** MAP VALUE ******************** */ -static bNodeSocketTemplate cmp_node_map_value_in[] = { - {SOCK_FLOAT, N_("Value"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_map_value_out[] = { - {SOCK_FLOAT, N_("Value")}, - {-1, ""}, -}; - -static void node_composit_init_map_value(bNodeTree *UNUSED(ntree), bNode *node) -{ - node->storage = BKE_texture_mapping_add(TEXMAP_TYPE_POINT); -} - -void register_node_type_cmp_map_value(void) -{ - static bNodeType ntype; - - cmp_node_type_base(&ntype, CMP_NODE_MAP_VALUE, "Map Value", NODE_CLASS_OP_VECTOR, 0); - node_type_socket_templates(&ntype, cmp_node_map_value_in, cmp_node_map_value_out); - node_type_init(&ntype, node_composit_init_map_value); - node_type_storage(&ntype, "TexMapping", node_free_standard_storage, node_copy_standard_storage); - - 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 new file mode 100644 index 00000000000..93622236c73 --- /dev/null +++ b/source/blender/nodes/composite/nodes/node_composite_map_range.cc @@ -0,0 +1,64 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2006 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup cmpnodes + */ + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_composite_util.hh" + +/* **************** Map Range ******************** */ + +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(0.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(0.0f).min(-10000.0f).max(10000.0f); + b.add_output<decl::Float>(N_("Value")); +} + +static void node_composit_buts_map_range(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayout *col; + + col = uiLayoutColumn(layout, true); + uiItemR(col, ptr, "use_clamp", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); +} + +} // namespace blender::nodes::node_composite_map_range_cc + +void register_node_type_cmp_map_range() +{ + namespace file_ns = blender::nodes::node_composite_map_range_cc; + + static bNodeType ntype; + + 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; + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/composite/nodes/node_composite_mapUV.cc b/source/blender/nodes/composite/nodes/node_composite_map_uv.cc index 99032bd042e..92573a362e4 100644 --- a/source/blender/nodes/composite/nodes/node_composite_mapUV.cc +++ b/source/blender/nodes/composite/nodes/node_composite_map_uv.cc @@ -21,26 +21,38 @@ * \ingroup cmpnodes */ +#include "UI_interface.h" +#include "UI_resources.h" + #include "node_composite_util.hh" /* **************** Map UV ******************** */ -static bNodeSocketTemplate cmp_node_mapuv_in[] = { - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, - {SOCK_VECTOR, N_("UV"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_mapuv_out[] = { - {SOCK_RGBA, N_("Image")}, - {-1, ""}, -}; - -void register_node_type_cmp_mapuv(void) +namespace blender::nodes::node_composite_map_uv_cc { + +static void cmp_node_map_uv_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Vector>(N_("UV")).default_value({1.0f, 0.0f, 0.0f}).min(0.0f).max(1.0f); + b.add_output<decl::Color>(N_("Image")); +} + +static void node_composit_buts_map_uv(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { + uiItemR(layout, ptr, "alpha", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); +} + +} // namespace blender::nodes::node_composite_map_uv_cc + +void register_node_type_cmp_mapuv() +{ + namespace file_ns = blender::nodes::node_composite_map_uv_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_MAP_UV, "Map UV", NODE_CLASS_DISTORT, 0); - node_type_socket_templates(&ntype, cmp_node_mapuv_in, cmp_node_mapuv_out); + 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; 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 new file mode 100644 index 00000000000..79f25dcee5f --- /dev/null +++ b/source/blender/nodes/composite/nodes/node_composite_map_value.cc @@ -0,0 +1,82 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2006 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup cmpnodes + */ + +#include "RNA_access.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_composite_util.hh" + +/* **************** MAP VALUE ******************** */ + +namespace blender::nodes::node_composite_map_value_cc { + +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_output<decl::Float>(N_("Value")); +} + +static void node_composit_init_map_value(bNodeTree *UNUSED(ntree), bNode *node) +{ + node->storage = BKE_texture_mapping_add(TEXMAP_TYPE_POINT); +} + +static void node_composit_buts_map_value(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayout *sub, *col; + + col = uiLayoutColumn(layout, true); + uiItemR(col, ptr, "offset", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(col, ptr, "size", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + + col = uiLayoutColumn(layout, true); + uiItemR(col, ptr, "use_min", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + sub = uiLayoutColumn(col, false); + uiLayoutSetActive(sub, RNA_boolean_get(ptr, "use_min")); + uiItemR(sub, ptr, "min", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); + + col = uiLayoutColumn(layout, true); + uiItemR(col, ptr, "use_max", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + sub = uiLayoutColumn(col, false); + uiLayoutSetActive(sub, RNA_boolean_get(ptr, "use_max")); + uiItemR(sub, ptr, "max", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); +} + +} // namespace blender::nodes::node_composite_map_value_cc + +void register_node_type_cmp_map_value() +{ + namespace file_ns = blender::nodes::node_composite_map_value_cc; + + static bNodeType ntype; + + cmp_node_type_base(&ntype, CMP_NODE_MAP_VALUE, "Map Value", NODE_CLASS_OP_VECTOR); + ntype.declare = file_ns::cmp_node_map_value_declare; + 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); + + 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 8b415bb8b63..b74c7341ce3 100644 --- a/source/blender/nodes/composite/nodes/node_composite_mask.cc +++ b/source/blender/nodes/composite/nodes/node_composite_mask.cc @@ -23,22 +23,23 @@ #include "DNA_mask_types.h" +#include "UI_interface.h" +#include "UI_resources.h" + #include "node_composite_util.hh" /* **************** Mask ******************** */ -namespace blender::nodes { +namespace blender::nodes::node_composite_mask_cc { static void cmp_node_mask_declare(NodeDeclarationBuilder &b) { - b.add_output<decl::Float>("Mask"); + b.add_output<decl::Float>(N_("Mask")); } -} // namespace blender::nodes - static void node_composit_init_mask(bNodeTree *UNUSED(ntree), bNode *node) { - NodeMask *data = (NodeMask *)MEM_callocN(sizeof(NodeMask), "NodeMask"); + NodeMask *data = MEM_cnew<NodeMask>(__func__); data->size_x = data->size_y = 256; node->storage = data; @@ -46,7 +47,10 @@ static void node_composit_init_mask(bNodeTree *UNUSED(ntree), bNode *node) node->custom3 = 0.5f; /* shutter */ } -static void node_mask_label(bNodeTree *UNUSED(ntree), bNode *node, char *label, int maxlen) +static void node_mask_label(const bNodeTree *UNUSED(ntree), + const bNode *node, + char *label, + int maxlen) { if (node->id != nullptr) { BLI_strncpy(label, node->id->name + 2, maxlen); @@ -56,14 +60,49 @@ static void node_mask_label(bNodeTree *UNUSED(ntree), bNode *node, char *label, } } -void register_node_type_cmp_mask(void) +static void node_composit_buts_mask(uiLayout *layout, bContext *C, PointerRNA *ptr) { + bNode *node = (bNode *)ptr->data; + + uiTemplateID(layout, + C, + ptr, + "mask", + nullptr, + nullptr, + nullptr, + UI_TEMPLATE_ID_FILTER_ALL, + false, + nullptr); + uiItemR(layout, ptr, "use_feather", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + + uiItemR(layout, ptr, "size_source", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); + + if (node->custom1 & (CMP_NODEFLAG_MASK_FIXED | CMP_NODEFLAG_MASK_FIXED_SCENE)) { + uiItemR(layout, ptr, "size_x", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(layout, ptr, "size_y", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + } + + uiItemR(layout, ptr, "use_motion_blur", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + if (node->custom1 & CMP_NODEFLAG_MASK_MOTION_BLUR) { + uiItemR(layout, ptr, "motion_blur_samples", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(layout, ptr, "motion_blur_shutter", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + } +} + +} // namespace blender::nodes::node_composite_mask_cc + +void register_node_type_cmp_mask() +{ + namespace file_ns = blender::nodes::node_composite_mask_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_MASK, "Mask", NODE_CLASS_INPUT, 0); - ntype.declare = blender::nodes::cmp_node_mask_declare; - node_type_init(&ntype, node_composit_init_mask); - node_type_label(&ntype, node_mask_label); + cmp_node_type_base(&ntype, CMP_NODE_MASK, "Mask", NODE_CLASS_INPUT); + ntype.declare = file_ns::cmp_node_mask_declare; + 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; 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 ecddcc2ad32..1083d85ba7c 100644 --- a/source/blender/nodes/composite/nodes/node_composite_math.cc +++ b/source/blender/nodes/composite/nodes/node_composite_math.cc @@ -24,21 +24,34 @@ #include "node_composite_util.hh" /* **************** SCALAR MATH ******************** */ -static bNodeSocketTemplate cmp_node_math_in[] = { - {SOCK_FLOAT, N_("Value"), 0.5f, 0.5f, 0.5f, 1.0f, -10000.0f, 10000.0f, PROP_NONE}, - {SOCK_FLOAT, N_("Value"), 0.5f, 0.5f, 0.5f, 1.0f, -10000.0f, 10000.0f, PROP_NONE}, - {SOCK_FLOAT, N_("Value"), 0.0f, 0.5f, 0.5f, 1.0f, -10000.0f, 10000.0f, PROP_NONE}, - {-1, ""}}; -static bNodeSocketTemplate cmp_node_math_out[] = {{SOCK_FLOAT, N_("Value")}, {-1, ""}}; +namespace blender::nodes::node_composite_math_cc { -void register_node_type_cmp_math(void) +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"), "Value_001") + .default_value(0.5f) + .min(-10000.0f) + .max(10000.0f); + b.add_input<decl::Float>(N_("Value"), "Value_002") + .default_value(0.5f) + .min(-10000.0f) + .max(10000.0f); + b.add_output<decl::Float>(N_("Value")); +} + +} // namespace blender::nodes::node_composite_math_cc + +void register_node_type_cmp_math() +{ + namespace file_ns = blender::nodes::node_composite_math_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_MATH, "Math", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, cmp_node_math_in, cmp_node_math_out); - node_type_label(&ntype, node_math_label); + cmp_node_type_base(&ntype, CMP_NODE_MATH, "Math", NODE_CLASS_CONVERTER); + ntype.declare = file_ns::cmp_node_math_declare; + ntype.labelfunc = node_math_label; node_type_update(&ntype, node_math_update); 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 4f2a9c2717c..7a10fa599de 100644 --- a/source/blender/nodes/composite/nodes/node_composite_mixrgb.cc +++ b/source/blender/nodes/composite/nodes/node_composite_mixrgb.cc @@ -25,26 +25,28 @@ /* **************** MIX RGB ******************** */ -namespace blender::nodes { +namespace blender::nodes::node_composite_mixrgb_cc { static void cmp_node_mixrgb_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Float>("Fac").default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input<decl::Color>("Image", "Image_001").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_output<decl::Color>("Image"); + 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_output<decl::Color>(N_("Image")); } -} // namespace blender::nodes +} // namespace blender::nodes::node_composite_mixrgb_cc -/* custom1 = mix type */ -void register_node_type_cmp_mix_rgb(void) +void register_node_type_cmp_mix_rgb() { + namespace file_ns = blender::nodes::node_composite_mixrgb_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_MIX_RGB, "Mix", NODE_CLASS_OP_COLOR, NODE_PREVIEW); - ntype.declare = blender::nodes::cmp_node_mixrgb_declare; - node_type_label(&ntype, node_blend_label); + cmp_node_type_base(&ntype, CMP_NODE_MIX_RGB, "Mix", NODE_CLASS_OP_COLOR); + ntype.flag |= NODE_PREVIEW; + ntype.declare = file_ns::cmp_node_mixrgb_declare; + ntype.labelfunc = node_blend_label; 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 ae91212f811..f6f00864839 100644 --- a/source/blender/nodes/composite/nodes/node_composite_movieclip.cc +++ b/source/blender/nodes/composite/nodes/node_composite_movieclip.cc @@ -21,31 +21,34 @@ * \ingroup cmpnodes */ -#include "node_composite_util.hh" - #include "BKE_context.h" #include "BKE_lib_id.h" +#include "DNA_defaults.h" + +#include "RNA_access.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_composite_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_composite_movieclip_cc { static void cmp_node_movieclip_declare(NodeDeclarationBuilder &b) { - b.add_output<decl::Color>("Image"); - b.add_output<decl::Float>("Alpha"); - b.add_output<decl::Float>("Offset X"); - b.add_output<decl::Float>("Offset Y"); - b.add_output<decl::Float>("Scale"); - b.add_output<decl::Float>("Angle"); + b.add_output<decl::Color>(N_("Image")); + b.add_output<decl::Float>(N_("Alpha")); + b.add_output<decl::Float>(N_("Offset X")); + b.add_output<decl::Float>(N_("Offset Y")); + b.add_output<decl::Float>(N_("Scale")); + b.add_output<decl::Float>(N_("Angle")); } -} // namespace blender::nodes - static void init(const bContext *C, PointerRNA *ptr) { bNode *node = (bNode *)ptr->data; Scene *scene = CTX_data_scene(C); - MovieClipUser *user = (MovieClipUser *)MEM_callocN(sizeof(MovieClipUser), - "node movie clip user"); + MovieClipUser *user = DNA_struct_default_alloc(MovieClipUser); node->id = (ID *)scene->clip; id_us_plus(node->id); @@ -53,13 +56,59 @@ static void init(const bContext *C, PointerRNA *ptr) user->framenr = 1; } -void register_node_type_cmp_movieclip(void) +static void node_composit_buts_movieclip(uiLayout *layout, bContext *C, PointerRNA *ptr) +{ + uiTemplateID(layout, + C, + ptr, + "clip", + nullptr, + "CLIP_OT_open", + nullptr, + UI_TEMPLATE_ID_FILTER_ALL, + false, + nullptr); +} + +static void node_composit_buts_movieclip_ex(uiLayout *layout, bContext *C, PointerRNA *ptr) { + bNode *node = (bNode *)ptr->data; + PointerRNA clipptr; + + uiTemplateID(layout, + C, + ptr, + "clip", + nullptr, + "CLIP_OT_open", + nullptr, + UI_TEMPLATE_ID_FILTER_ALL, + false, + nullptr); + + if (!node->id) { + return; + } + + clipptr = RNA_pointer_get(ptr, "clip"); + + uiTemplateColorspaceSettings(layout, &clipptr, "colorspace_settings"); +} + +} // namespace blender::nodes::node_composite_movieclip_cc + +void register_node_type_cmp_movieclip() +{ + namespace file_ns = blender::nodes::node_composite_movieclip_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_MOVIECLIP, "Movie Clip", NODE_CLASS_INPUT, NODE_PREVIEW); - ntype.declare = blender::nodes::cmp_node_movieclip_declare; - ntype.initfunc_api = init; + cmp_node_type_base(&ntype, CMP_NODE_MOVIECLIP, "Movie Clip", NODE_CLASS_INPUT); + 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.initfunc_api = file_ns::init; + ntype.flag |= NODE_PREVIEW; node_type_storage( &ntype, "MovieClipUser", node_free_standard_storage, node_copy_standard_storage); diff --git a/source/blender/nodes/composite/nodes/node_composite_moviedistortion.cc b/source/blender/nodes/composite/nodes/node_composite_moviedistortion.cc index 2bac30cc152..396c6fa7a13 100644 --- a/source/blender/nodes/composite/nodes/node_composite_moviedistortion.cc +++ b/source/blender/nodes/composite/nodes/node_composite_moviedistortion.cc @@ -21,24 +21,25 @@ * \ingroup cmpnodes */ -#include "node_composite_util.hh" - #include "BKE_context.h" #include "BKE_lib_id.h" +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_composite_util.hh" + /* **************** Translate ******************** */ -static bNodeSocketTemplate cmp_node_moviedistortion_in[] = { - {SOCK_RGBA, N_("Image"), 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f}, - {-1, ""}, -}; +namespace blender::nodes::node_composite_moviedistortion_cc { -static bNodeSocketTemplate cmp_node_moviedistortion_out[] = { - {SOCK_RGBA, N_("Image")}, - {-1, ""}, -}; +static void cmp_node_moviedistortion_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_("Image")); +} -static void label(bNodeTree *UNUSED(ntree), bNode *node, char *label, int maxlen) +static void label(const bNodeTree *UNUSED(ntree), const bNode *node, char *label, int maxlen) { if (node->custom1 == 0) { BLI_strncpy(label, IFACE_("Undistortion"), maxlen); @@ -73,16 +74,42 @@ static void storage_copy(bNodeTree *UNUSED(dest_ntree), bNode *dest_node, const } } -void register_node_type_cmp_moviedistortion(void) +static void node_composit_buts_moviedistortion(uiLayout *layout, bContext *C, PointerRNA *ptr) { - static bNodeType ntype; + bNode *node = (bNode *)ptr->data; - cmp_node_type_base(&ntype, CMP_NODE_MOVIEDISTORTION, "Movie Distortion", NODE_CLASS_DISTORT, 0); - node_type_socket_templates(&ntype, cmp_node_moviedistortion_in, cmp_node_moviedistortion_out); - node_type_label(&ntype, label); + uiTemplateID(layout, + C, + ptr, + "clip", + nullptr, + "CLIP_OT_open", + nullptr, + UI_TEMPLATE_ID_FILTER_ALL, + false, + nullptr); + + if (!node->id) { + return; + } + + uiItemR(layout, ptr, "distortion_type", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); +} + +} // namespace blender::nodes::node_composite_moviedistortion_cc + +void register_node_type_cmp_moviedistortion() +{ + namespace file_ns = blender::nodes::node_composite_moviedistortion_cc; + + static bNodeType ntype; - ntype.initfunc_api = init; - node_type_storage(&ntype, nullptr, storage_free, storage_copy); + cmp_node_type_base(&ntype, CMP_NODE_MOVIEDISTORTION, "Movie Distortion", NODE_CLASS_DISTORT); + ntype.declare = file_ns::cmp_node_moviedistortion_declare; + ntype.draw_buttons = file_ns::node_composit_buts_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); 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 7531025daa5..da44d355966 100644 --- a/source/blender/nodes/composite/nodes/node_composite_normal.cc +++ b/source/blender/nodes/composite/nodes/node_composite_normal.cc @@ -24,23 +24,30 @@ #include "node_composite_util.hh" /* **************** NORMAL ******************** */ -static bNodeSocketTemplate cmp_node_normal_in[] = { - {SOCK_VECTOR, N_("Normal"), 0.0f, 0.0f, 1.0f, 0.0f, -1.0f, 1.0f, PROP_DIRECTION}, - {-1, ""}, -}; - -static bNodeSocketTemplate cmp_node_normal_out[] = { - {SOCK_VECTOR, N_("Normal"), 0.0f, 0.0f, 1.0f, 0.0f, -1.0f, 1.0f, PROP_DIRECTION}, - {SOCK_FLOAT, N_("Dot")}, - {-1, ""}, -}; - -void register_node_type_cmp_normal(void) + +namespace blender::nodes::node_composite_normal_cc { + +static void cmp_node_normal_declare(NodeDeclarationBuilder &b) { + b.add_input<decl::Vector>(N_("Normal")) + .default_value({1.0f, 1.0f, 1.0f}) + .min(-1.0f) + .max(1.0f) + .subtype(PROP_DIRECTION); + b.add_output<decl::Vector>(N_("Normal")); + b.add_output<decl::Float>(N_("Dot")); +} + +} // namespace blender::nodes::node_composite_normal_cc + +void register_node_type_cmp_normal() +{ + namespace file_ns = blender::nodes::node_composite_normal_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_NORMAL, "Normal", NODE_CLASS_OP_VECTOR, 0); - node_type_socket_templates(&ntype, cmp_node_normal_in, cmp_node_normal_out); + cmp_node_type_base(&ntype, CMP_NODE_NORMAL, "Normal", NODE_CLASS_OP_VECTOR); + ntype.declare = file_ns::cmp_node_normal_declare; 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 7cc54e4eed6..3d25187e5e6 100644 --- a/source/blender/nodes/composite/nodes/node_composite_normalize.cc +++ b/source/blender/nodes/composite/nodes/node_composite_normalize.cc @@ -24,16 +24,25 @@ #include "node_composite_util.hh" /* **************** NORMALIZE single channel, useful for Z buffer ******************** */ -static bNodeSocketTemplate cmp_node_normalize_in[] = { - {SOCK_FLOAT, N_("Value"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, {-1, ""}}; -static bNodeSocketTemplate cmp_node_normalize_out[] = {{SOCK_FLOAT, N_("Value")}, {-1, ""}}; -void register_node_type_cmp_normalize(void) +namespace blender::nodes::node_composite_normalize_cc { + +static void cmp_node_normalize_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Float>(N_("Value")).default_value(1.0f).min(0.0f).max(1.0f); + b.add_output<decl::Float>(N_("Value")); +} + +} // namespace blender::nodes::node_composite_normalize_cc + +void register_node_type_cmp_normalize() { + namespace file_ns = blender::nodes::node_composite_normalize_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_NORMALIZE, "Normalize", NODE_CLASS_OP_VECTOR, 0); - node_type_socket_templates(&ntype, cmp_node_normalize_in, cmp_node_normalize_out); + cmp_node_type_base(&ntype, CMP_NODE_NORMALIZE, "Normalize", NODE_CLASS_OP_VECTOR); + ntype.declare = file_ns::cmp_node_normalize_declare; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_outputFile.cc b/source/blender/nodes/composite/nodes/node_composite_output_file.cc index a372d2f7419..544c8437394 100644 --- a/source/blender/nodes/composite/nodes/node_composite_outputFile.cc +++ b/source/blender/nodes/composite/nodes/node_composite_output_file.cc @@ -21,14 +21,21 @@ * \ingroup cmpnodes */ +#include <cstring> + +#include "BLI_string_utf8.h" #include "BLI_string_utils.h" #include "BLI_utildefines.h" -#include <cstring> #include "BKE_context.h" #include "RNA_access.h" +#include "UI_interface.h" +#include "UI_resources.h" + +#include "WM_api.h" + #include "node_composite_util.hh" #include "intern/openexr/openexr_multi.h" @@ -128,8 +135,7 @@ bNodeSocket *ntreeCompositOutputFileAddSocket(bNodeTree *ntree, ntree, node, SOCK_IN, SOCK_RGBA, PROP_NONE, nullptr, name); /* create format data for the input socket */ - NodeImageMultiFileSocket *sockdata = (NodeImageMultiFileSocket *)MEM_callocN( - sizeof(NodeImageMultiFileSocket), "socket image format"); + NodeImageMultiFileSocket *sockdata = MEM_cnew<NodeImageMultiFileSocket>(__func__); sock->storage = sockdata; BLI_strncpy_utf8(sockdata->path, name, sizeof(sockdata->path)); @@ -190,14 +196,15 @@ void ntreeCompositOutputFileSetLayer(bNode *node, bNodeSocket *sock, const char ntreeCompositOutputFileUniqueLayer(&node->inputs, sock, name, '_'); } +namespace blender::nodes::node_composite_output_file_cc { + /* XXX uses initfunc_api callback, regular initfunc does not support context yet */ static void init_output_file(const bContext *C, PointerRNA *ptr) { Scene *scene = CTX_data_scene(C); bNodeTree *ntree = (bNodeTree *)ptr->owner_id; bNode *node = (bNode *)ptr->data; - NodeImageMultiFile *nimf = (NodeImageMultiFile *)MEM_callocN(sizeof(NodeImageMultiFile), - "node image multi file"); + NodeImageMultiFile *nimf = MEM_cnew<NodeImageMultiFile>(__func__); ImageFormatData *format = nullptr; node->storage = nimf; @@ -275,15 +282,177 @@ static void update_output_file(bNodeTree *ntree, bNode *node) } } -void register_node_type_cmp_output_file(void) +static void node_composit_buts_file_output(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + PointerRNA imfptr = RNA_pointer_get(ptr, "format"); + const bool multilayer = RNA_enum_get(&imfptr, "file_format") == R_IMF_IMTYPE_MULTILAYER; + + if (multilayer) { + uiItemL(layout, IFACE_("Path:"), ICON_NONE); + } + else { + uiItemL(layout, IFACE_("Base Path:"), ICON_NONE); + } + uiItemR(layout, ptr, "base_path", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); +} + +static void node_composit_buts_file_output_ex(uiLayout *layout, bContext *C, PointerRNA *ptr) { + Scene *scene = CTX_data_scene(C); + PointerRNA imfptr = RNA_pointer_get(ptr, "format"); + PointerRNA active_input_ptr, op_ptr; + uiLayout *row, *col; + const bool multilayer = RNA_enum_get(&imfptr, "file_format") == R_IMF_IMTYPE_MULTILAYER; + const bool is_exr = RNA_enum_get(&imfptr, "file_format") == R_IMF_IMTYPE_OPENEXR; + const bool is_multiview = (scene->r.scemode & R_MULTIVIEW) != 0; + + node_composit_buts_file_output(layout, C, ptr); + uiTemplateImageSettings(layout, &imfptr, false); + + /* disable stereo output for multilayer, too much work for something that no one will use */ + /* if someone asks for that we can implement it */ + if (is_multiview) { + uiTemplateImageFormatViews(layout, &imfptr, nullptr); + } + + uiItemS(layout); + + uiItemO(layout, IFACE_("Add Input"), ICON_ADD, "NODE_OT_output_file_add_socket"); + + row = uiLayoutRow(layout, false); + col = uiLayoutColumn(row, true); + + const int active_index = RNA_int_get(ptr, "active_input_index"); + /* using different collection properties if multilayer format is enabled */ + if (multilayer) { + uiTemplateList(col, + C, + "UI_UL_list", + "file_output_node", + ptr, + "layer_slots", + ptr, + "active_input_index", + nullptr, + 0, + 0, + 0, + 0, + UI_TEMPLATE_LIST_FLAG_NONE); + RNA_property_collection_lookup_int( + ptr, RNA_struct_find_property(ptr, "layer_slots"), active_index, &active_input_ptr); + } + else { + uiTemplateList(col, + C, + "UI_UL_list", + "file_output_node", + ptr, + "file_slots", + ptr, + "active_input_index", + nullptr, + 0, + 0, + 0, + 0, + UI_TEMPLATE_LIST_FLAG_NONE); + RNA_property_collection_lookup_int( + ptr, RNA_struct_find_property(ptr, "file_slots"), active_index, &active_input_ptr); + } + /* XXX collection lookup does not return the ID part of the pointer, + * setting this manually here */ + active_input_ptr.owner_id = ptr->owner_id; + + col = uiLayoutColumn(row, true); + wmOperatorType *ot = WM_operatortype_find("NODE_OT_output_file_move_active_socket", false); + uiItemFullO_ptr(col, ot, "", ICON_TRIA_UP, nullptr, WM_OP_INVOKE_DEFAULT, 0, &op_ptr); + RNA_enum_set(&op_ptr, "direction", 1); + uiItemFullO_ptr(col, ot, "", ICON_TRIA_DOWN, nullptr, WM_OP_INVOKE_DEFAULT, 0, &op_ptr); + RNA_enum_set(&op_ptr, "direction", 2); + + if (active_input_ptr.data) { + if (multilayer) { + col = uiLayoutColumn(layout, true); + + uiItemL(col, IFACE_("Layer:"), ICON_NONE); + row = uiLayoutRow(col, false); + uiItemR(row, &active_input_ptr, "name", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); + uiItemFullO(row, + "NODE_OT_output_file_remove_active_socket", + "", + ICON_X, + nullptr, + WM_OP_EXEC_DEFAULT, + UI_ITEM_R_ICON_ONLY, + nullptr); + } + else { + col = uiLayoutColumn(layout, true); + + uiItemL(col, IFACE_("File Subpath:"), ICON_NONE); + row = uiLayoutRow(col, false); + uiItemR(row, &active_input_ptr, "path", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); + uiItemFullO(row, + "NODE_OT_output_file_remove_active_socket", + "", + ICON_X, + nullptr, + WM_OP_EXEC_DEFAULT, + UI_ITEM_R_ICON_ONLY, + nullptr); + + /* format details for individual files */ + imfptr = RNA_pointer_get(&active_input_ptr, "format"); + + col = uiLayoutColumn(layout, true); + uiItemL(col, IFACE_("Format:"), ICON_NONE); + uiItemR(col, + &active_input_ptr, + "use_node_format", + UI_ITEM_R_SPLIT_EMPTY_NAME, + nullptr, + ICON_NONE); + + const bool is_socket_exr = RNA_enum_get(&imfptr, "file_format") == R_IMF_IMTYPE_OPENEXR; + const bool use_node_format = RNA_boolean_get(&active_input_ptr, "use_node_format"); + + if ((!is_exr && use_node_format) || (!is_socket_exr && !use_node_format)) { + uiItemR(col, + &active_input_ptr, + "save_as_render", + UI_ITEM_R_SPLIT_EMPTY_NAME, + nullptr, + ICON_NONE); + } + + col = uiLayoutColumn(layout, false); + uiLayoutSetActive(col, use_node_format == false); + uiTemplateImageSettings(col, &imfptr, false); + + if (is_multiview) { + uiTemplateImageFormatViews(layout, &imfptr, nullptr); + } + } + } +} + +} // namespace blender::nodes::node_composite_output_file_cc + +void register_node_type_cmp_output_file() +{ + namespace file_ns = blender::nodes::node_composite_output_file_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_OUTPUT_FILE, "File Output", NODE_CLASS_OUTPUT, NODE_PREVIEW); - node_type_socket_templates(&ntype, nullptr, nullptr); - ntype.initfunc_api = init_output_file; - node_type_storage(&ntype, "NodeImageMultiFile", free_output_file, copy_output_file); - node_type_update(&ntype, update_output_file); + cmp_node_type_base(&ntype, CMP_NODE_OUTPUT_FILE, "File Output", NODE_CLASS_OUTPUT); + ntype.draw_buttons = file_ns::node_composit_buts_file_output; + ntype.draw_buttons_ex = file_ns::node_composit_buts_file_output_ex; + ntype.initfunc_api = file_ns::init_output_file; + ntype.flag |= NODE_PREVIEW; + node_type_storage( + &ntype, "NodeImageMultiFile", file_ns::free_output_file, file_ns::copy_output_file); + node_type_update(&ntype, file_ns::update_output_file); 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 19975c21a0b..baded2186d3 100644 --- a/source/blender/nodes/composite/nodes/node_composite_pixelate.cc +++ b/source/blender/nodes/composite/nodes/node_composite_pixelate.cc @@ -25,16 +25,24 @@ /* **************** Pixelate ******************** */ -static bNodeSocketTemplate cmp_node_pixelate_in[] = { - {SOCK_RGBA, N_("Color"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, {-1, ""}}; -static bNodeSocketTemplate cmp_node_pixelate_out[] = {{SOCK_RGBA, N_("Color")}, {-1, ""}}; +namespace blender::nodes::node_composite_pixelate_cc { -void register_node_type_cmp_pixelate(void) +static void cmp_node_pixelate_declare(NodeDeclarationBuilder &b) { + b.add_input<decl::Color>(N_("Color")); + b.add_output<decl::Color>(N_("Color")); +} + +} // namespace blender::nodes::node_composite_pixelate_cc + +void register_node_type_cmp_pixelate() +{ + namespace file_ns = blender::nodes::node_composite_pixelate_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_PIXELATE, "Pixelate", NODE_CLASS_OP_FILTER, 0); - node_type_socket_templates(&ntype, cmp_node_pixelate_in, cmp_node_pixelate_out); + cmp_node_type_base(&ntype, CMP_NODE_PIXELATE, "Pixelate", NODE_CLASS_OP_FILTER); + ntype.declare = file_ns::cmp_node_pixelate_declare; 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 e122b710b7b..453501b6ab1 100644 --- a/source/blender/nodes/composite/nodes/node_composite_planetrackdeform.cc +++ b/source/blender/nodes/composite/nodes/node_composite_planetrackdeform.cc @@ -21,34 +21,91 @@ * \ingroup cmpnodes */ +#include "RNA_access.h" + +#include "UI_interface.h" +#include "UI_resources.h" + #include "node_composite_util.hh" -static bNodeSocketTemplate cmp_node_planetrackdeform_in[] = { - {SOCK_RGBA, N_("Image"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, {-1, ""}}; +namespace blender::nodes::node_composite_planetrackdeform_cc { -static bNodeSocketTemplate cmp_node_planetrackdeform_out[] = { - {SOCK_RGBA, N_("Image")}, - {SOCK_FLOAT, N_("Plane")}, - {-1, ""}, -}; +static void cmp_node_planetrackdeform_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Color>(N_("Image")); + b.add_output<decl::Color>(N_("Image")); + b.add_output<decl::Float>(N_("Plane")); +} static void init(bNodeTree *UNUSED(ntree), bNode *node) { - NodePlaneTrackDeformData *data = (NodePlaneTrackDeformData *)MEM_callocN( - sizeof(NodePlaneTrackDeformData), "node plane track deform data"); + NodePlaneTrackDeformData *data = MEM_cnew<NodePlaneTrackDeformData>(__func__); data->motion_blur_samples = 16; data->motion_blur_shutter = 0.5f; node->storage = data; } -void register_node_type_cmp_planetrackdeform(void) +static void node_composit_buts_planetrackdeform(uiLayout *layout, bContext *C, PointerRNA *ptr) { + bNode *node = (bNode *)ptr->data; + NodePlaneTrackDeformData *data = (NodePlaneTrackDeformData *)node->storage; + + uiTemplateID(layout, + C, + ptr, + "clip", + nullptr, + "CLIP_OT_open", + nullptr, + UI_TEMPLATE_ID_FILTER_ALL, + false, + nullptr); + + if (node->id) { + MovieClip *clip = (MovieClip *)node->id; + MovieTracking *tracking = &clip->tracking; + MovieTrackingObject *object; + uiLayout *col; + PointerRNA tracking_ptr; + + RNA_pointer_create(&clip->id, &RNA_MovieTracking, tracking, &tracking_ptr); + + col = uiLayoutColumn(layout, false); + uiItemPointerR(col, ptr, "tracking_object", &tracking_ptr, "objects", "", ICON_OBJECT_DATA); + + object = BKE_tracking_object_get_named(tracking, data->tracking_object); + if (object) { + PointerRNA object_ptr; + + RNA_pointer_create(&clip->id, &RNA_MovieTrackingObject, object, &object_ptr); + + uiItemPointerR( + col, ptr, "plane_track_name", &object_ptr, "plane_tracks", "", ICON_ANIM_DATA); + } + else { + uiItemR(layout, ptr, "plane_track_name", 0, "", ICON_ANIM_DATA); + } + } + + uiItemR(layout, ptr, "use_motion_blur", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + if (data->flag & CMP_NODEFLAG_PLANETRACKDEFORM_MOTION_BLUR) { + uiItemR(layout, ptr, "motion_blur_samples", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(layout, ptr, "motion_blur_shutter", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + } +} + +} // namespace blender::nodes::node_composite_planetrackdeform_cc + +void register_node_type_cmp_planetrackdeform() +{ + namespace file_ns = blender::nodes::node_composite_planetrackdeform_cc; + static bNodeType ntype; - cmp_node_type_base( - &ntype, CMP_NODE_PLANETRACKDEFORM, "Plane Track Deform", NODE_CLASS_DISTORT, 0); - node_type_socket_templates(&ntype, cmp_node_planetrackdeform_in, cmp_node_planetrackdeform_out); - node_type_init(&ntype, init); + 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); node_type_storage( &ntype, "NodePlaneTrackDeformData", node_free_standard_storage, node_copy_standard_storage); diff --git a/source/blender/nodes/composite/nodes/node_composite_posterize.cc b/source/blender/nodes/composite/nodes/node_composite_posterize.cc index 45a98e68b4b..f05ed3d80b7 100644 --- a/source/blender/nodes/composite/nodes/node_composite_posterize.cc +++ b/source/blender/nodes/composite/nodes/node_composite_posterize.cc @@ -25,22 +25,25 @@ /* **************** Posterize ******************** */ -static bNodeSocketTemplate cmp_node_posterize_in[] = { - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, - {SOCK_FLOAT, N_("Steps"), 8.0f, 8.0f, 8.0f, 8.0f, 2.0f, 1024.0f, PROP_NONE}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_posterize_out[] = { - {SOCK_RGBA, N_("Image")}, - {-1, ""}, -}; - -void register_node_type_cmp_posterize(void) +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_output<decl::Color>(N_("Image")); +} + +} // namespace blender::nodes::node_composite_posterize_cc + +void register_node_type_cmp_posterize() { + namespace file_ns = blender::nodes::node_composite_posterize_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_POSTERIZE, "Posterize", NODE_CLASS_OP_COLOR, 0); - node_type_socket_templates(&ntype, cmp_node_posterize_in, cmp_node_posterize_out); + cmp_node_type_base(&ntype, CMP_NODE_POSTERIZE, "Posterize", NODE_CLASS_OP_COLOR); + ntype.declare = file_ns::cmp_node_posterize_declare; 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 68716ee53b5..aec005ee25a 100644 --- a/source/blender/nodes/composite/nodes/node_composite_premulkey.cc +++ b/source/blender/nodes/composite/nodes/node_composite_premulkey.cc @@ -21,25 +21,37 @@ * \ingroup cmpnodes */ +#include "UI_interface.h" +#include "UI_resources.h" + #include "node_composite_util.hh" /* **************** Premul and Key Alpha Convert ******************** */ -static bNodeSocketTemplate cmp_node_premulkey_in[] = { - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_premulkey_out[] = { - {SOCK_RGBA, N_("Image")}, - {-1, ""}, -}; +namespace blender::nodes::node_composite_premulkey_cc { -void register_node_type_cmp_premulkey(void) +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_output<decl::Color>(N_("Image")); +} + +static void node_composit_buts_premulkey(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "mapping", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); +} + +} // namespace blender::nodes::node_composite_premulkey_cc + +void register_node_type_cmp_premulkey() +{ + namespace file_ns = blender::nodes::node_composite_premulkey_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_PREMULKEY, "Alpha Convert", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, cmp_node_premulkey_in, cmp_node_premulkey_out); + 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; 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 332e56e26b1..feaa3ee8c19 100644 --- a/source/blender/nodes/composite/nodes/node_composite_rgb.cc +++ b/source/blender/nodes/composite/nodes/node_composite_rgb.cc @@ -25,21 +25,23 @@ /* **************** RGB ******************** */ -namespace blender::nodes { +namespace blender::nodes::node_composite_rgb_cc { static void cmp_node_rgb_declare(NodeDeclarationBuilder &b) { - b.add_output<decl::Color>("RGBA").default_value({0.5f, 0.5f, 0.5f, 1.0f}); + b.add_output<decl::Color>(N_("RGBA")).default_value({0.5f, 0.5f, 0.5f, 1.0f}); } -} // namespace blender::nodes +} // namespace blender::nodes::node_composite_rgb_cc -void register_node_type_cmp_rgb(void) +void register_node_type_cmp_rgb() { + namespace file_ns = blender::nodes::node_composite_rgb_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_RGB, "RGB", NODE_CLASS_INPUT, 0); - ntype.declare = blender::nodes::cmp_node_rgb_declare; + 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); 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 d28b35ec9fb..96ac4f3db5b 100644 --- a/source/blender/nodes/composite/nodes/node_composite_rotate.cc +++ b/source/blender/nodes/composite/nodes/node_composite_rotate.cc @@ -21,32 +21,48 @@ * \ingroup cmpnodes */ +#include "UI_interface.h" +#include "UI_resources.h" + #include "node_composite_util.hh" /* **************** Rotate ******************** */ -static bNodeSocketTemplate cmp_node_rotate_in[] = { - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, - {SOCK_FLOAT, N_("Degr"), 0.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f, PROP_ANGLE}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_rotate_out[] = { - {SOCK_RGBA, N_("Image")}, - {-1, ""}, -}; +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::Float>(N_("Degr")) + .default_value(0.0f) + .min(-10000.0f) + .max(10000.0f) + .subtype(PROP_ANGLE); + b.add_output<decl::Color>(N_("Image")); +} static void node_composit_init_rotate(bNodeTree *UNUSED(ntree), bNode *node) { node->custom1 = 1; /* Bilinear Filter. */ } -void register_node_type_cmp_rotate(void) +static void node_composit_buts_rotate(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { + uiItemR(layout, ptr, "filter_type", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); +} + +} // namespace blender::nodes::node_composite_rotate_cc + +void register_node_type_cmp_rotate() +{ + namespace file_ns = blender::nodes::node_composite_rotate_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_ROTATE, "Rotate", NODE_CLASS_DISTORT, 0); - node_type_socket_templates(&ntype, cmp_node_rotate_in, cmp_node_rotate_out); - node_type_init(&ntype, node_composit_init_rotate); + cmp_node_type_base(&ntype, CMP_NODE_ROTATE, "Rotate", NODE_CLASS_DISTORT); + 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); 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 3972fc0d949..dba08dd7eb1 100644 --- a/source/blender/nodes/composite/nodes/node_composite_scale.cc +++ b/source/blender/nodes/composite/nodes/node_composite_scale.cc @@ -21,18 +21,26 @@ * \ingroup cmpnodes */ +#include "RNA_access.h" + +#include "UI_interface.h" +#include "UI_resources.h" + #include "node_composite_util.hh" /* **************** Scale ******************** */ -static bNodeSocketTemplate cmp_node_scale_in[] = { - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, - {SOCK_FLOAT, N_("X"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0001f, CMP_SCALE_MAX, PROP_NONE}, - {SOCK_FLOAT, N_("Y"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0001f, CMP_SCALE_MAX, PROP_NONE}, - {-1, ""}}; -static bNodeSocketTemplate cmp_node_scale_out[] = {{SOCK_RGBA, N_("Image")}, {-1, ""}}; +namespace blender::nodes::node_composite_scale_cc { + +static void cmp_node_scale_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(1.0f).min(0.0001f).max(CMP_SCALE_MAX); + b.add_input<decl::Float>(N_("Y")).default_value(1.0f).min(0.0001f).max(CMP_SCALE_MAX); + b.add_output<decl::Color>(N_("Image")); +} -static void node_composite_update_scale(bNodeTree *UNUSED(ntree), bNode *node) +static void node_composite_update_scale(bNodeTree *ntree, bNode *node) { bNodeSocket *sock; bool use_xy_scale = ELEM(node->custom1, CMP_SCALE_RELATIVE, CMP_SCALE_ABSOLUTE); @@ -40,23 +48,41 @@ static void node_composite_update_scale(bNodeTree *UNUSED(ntree), bNode *node) /* Only show X/Y scale factor inputs for modes using them! */ for (sock = (bNodeSocket *)node->inputs.first; sock; sock = sock->next) { if (STR_ELEM(sock->name, "X", "Y")) { - if (use_xy_scale) { - sock->flag &= ~SOCK_UNAVAIL; - } - else { - sock->flag |= SOCK_UNAVAIL; - } + nodeSetSocketAvailability(ntree, sock, use_xy_scale); } } } -void register_node_type_cmp_scale(void) +static void node_composit_buts_scale(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "space", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); + + if (RNA_enum_get(ptr, "space") == CMP_SCALE_RENDERPERCENT) { + uiLayout *row; + uiItemR(layout, + ptr, + "frame_method", + UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_EXPAND, + nullptr, + ICON_NONE); + row = uiLayoutRow(layout, true); + uiItemR(row, ptr, "offset_x", UI_ITEM_R_SPLIT_EMPTY_NAME, "X", ICON_NONE); + uiItemR(row, ptr, "offset_y", UI_ITEM_R_SPLIT_EMPTY_NAME, "Y", ICON_NONE); + } +} + +} // namespace blender::nodes::node_composite_scale_cc + +void register_node_type_cmp_scale() { + namespace file_ns = blender::nodes::node_composite_scale_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_SCALE, "Scale", NODE_CLASS_DISTORT, 0); - node_type_socket_templates(&ntype, cmp_node_scale_in, cmp_node_scale_out); - node_type_update(&ntype, node_composite_update_scale); + cmp_node_type_base(&ntype, CMP_NODE_SCALE, "Scale", NODE_CLASS_DISTORT); + 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); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_idMask.cc b/source/blender/nodes/composite/nodes/node_composite_scene_time.cc index 13e3536f9ee..5c53c1df237 100644 --- a/source/blender/nodes/composite/nodes/node_composite_idMask.cc +++ b/source/blender/nodes/composite/nodes/node_composite_scene_time.cc @@ -12,34 +12,28 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2006 Blender Foundation. - * All rights reserved. */ - /** \file * \ingroup cmpnodes */ #include "node_composite_util.hh" -/* **************** ID Mask ******************** */ +namespace blender::nodes { + +static void cmp_node_scene_time_declare(NodeDeclarationBuilder &b) +{ + b.add_output<decl::Float>(N_("Seconds")); + b.add_output<decl::Float>(N_("Frame")); +} -static bNodeSocketTemplate cmp_node_idmask_in[] = { - {SOCK_FLOAT, N_("ID value"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_idmask_out[] = { - {SOCK_FLOAT, N_("Alpha")}, - {-1, ""}, -}; +} // namespace blender::nodes -void register_node_type_cmp_idmask(void) +void register_node_type_cmp_scene_time() { static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_ID_MASK, "ID Mask", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, cmp_node_idmask_in, cmp_node_idmask_out); - + cmp_node_type_base(&ntype, CMP_NODE_SCENE_TIME, "Scene Time", NODE_CLASS_INPUT); + ntype.declare = blender::nodes::cmp_node_scene_time_declare; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcombHSVA.cc b/source/blender/nodes/composite/nodes/node_composite_sepcombHSVA.cc deleted file mode 100644 index 17b1ab9ac59..00000000000 --- a/source/blender/nodes/composite/nodes/node_composite_sepcombHSVA.cc +++ /dev/null @@ -1,70 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2006 Blender Foundation. - * All rights reserved. - */ - -/** \file - * \ingroup cmpnodes - */ - -#include "node_composite_util.hh" - -/* **************** SEPARATE HSVA ******************** */ -static bNodeSocketTemplate cmp_node_sephsva_in[] = { - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_sephsva_out[] = { - {SOCK_FLOAT, N_("H")}, - {SOCK_FLOAT, N_("S")}, - {SOCK_FLOAT, N_("V")}, - {SOCK_FLOAT, N_("A")}, - {-1, ""}, -}; - -void register_node_type_cmp_sephsva(void) -{ - static bNodeType ntype; - - cmp_node_type_base(&ntype, CMP_NODE_SEPHSVA, "Separate HSVA", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, cmp_node_sephsva_in, cmp_node_sephsva_out); - - nodeRegisterType(&ntype); -} - -/* **************** COMBINE HSVA ******************** */ -static bNodeSocketTemplate cmp_node_combhsva_in[] = { - {SOCK_FLOAT, N_("H"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {SOCK_FLOAT, N_("S"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {SOCK_FLOAT, N_("V"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {SOCK_FLOAT, N_("A"), 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_combhsva_out[] = { - {SOCK_RGBA, N_("Image")}, - {-1, ""}, -}; - -void register_node_type_cmp_combhsva(void) -{ - static bNodeType ntype; - - cmp_node_type_base(&ntype, CMP_NODE_COMBHSVA, "Combine HSVA", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, cmp_node_combhsva_in, cmp_node_combhsva_out); - - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcombRGBA.cc b/source/blender/nodes/composite/nodes/node_composite_sepcombRGBA.cc deleted file mode 100644 index d3a021ed7ba..00000000000 --- a/source/blender/nodes/composite/nodes/node_composite_sepcombRGBA.cc +++ /dev/null @@ -1,70 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2006 Blender Foundation. - * All rights reserved. - */ - -/** \file - * \ingroup cmpnodes - */ - -#include "node_composite_util.hh" - -/* **************** SEPARATE RGBA ******************** */ -static bNodeSocketTemplate cmp_node_seprgba_in[] = { - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_seprgba_out[] = { - {SOCK_FLOAT, N_("R")}, - {SOCK_FLOAT, N_("G")}, - {SOCK_FLOAT, N_("B")}, - {SOCK_FLOAT, N_("A")}, - {-1, ""}, -}; - -void register_node_type_cmp_seprgba(void) -{ - static bNodeType ntype; - - cmp_node_type_base(&ntype, CMP_NODE_SEPRGBA, "Separate RGBA", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, cmp_node_seprgba_in, cmp_node_seprgba_out); - - nodeRegisterType(&ntype); -} - -/* **************** COMBINE RGBA ******************** */ -static bNodeSocketTemplate cmp_node_combrgba_in[] = { - {SOCK_FLOAT, N_("R"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {SOCK_FLOAT, N_("G"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {SOCK_FLOAT, N_("B"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {SOCK_FLOAT, N_("A"), 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_combrgba_out[] = { - {SOCK_RGBA, N_("Image")}, - {-1, ""}, -}; - -void register_node_type_cmp_combrgba(void) -{ - static bNodeType ntype; - - cmp_node_type_base(&ntype, CMP_NODE_COMBRGBA, "Combine RGBA", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, cmp_node_combrgba_in, cmp_node_combrgba_out); - - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcombYUVA.cc b/source/blender/nodes/composite/nodes/node_composite_sepcombYUVA.cc deleted file mode 100644 index 59982b81468..00000000000 --- a/source/blender/nodes/composite/nodes/node_composite_sepcombYUVA.cc +++ /dev/null @@ -1,68 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2006 Blender Foundation. - * All rights reserved. - */ - -/** \file - * \ingroup cmpnodes - */ - -#include "node_composite_util.hh" - -/* **************** SEPARATE YUVA ******************** */ -static bNodeSocketTemplate cmp_node_sepyuva_in[] = { - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, {-1, ""}}; -static bNodeSocketTemplate cmp_node_sepyuva_out[] = { - {SOCK_FLOAT, N_("Y")}, - {SOCK_FLOAT, N_("U")}, - {SOCK_FLOAT, N_("V")}, - {SOCK_FLOAT, N_("A")}, - {-1, ""}, -}; - -void register_node_type_cmp_sepyuva(void) -{ - static bNodeType ntype; - - cmp_node_type_base(&ntype, CMP_NODE_SEPYUVA, "Separate YUVA", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, cmp_node_sepyuva_in, cmp_node_sepyuva_out); - - nodeRegisterType(&ntype); -} - -/* **************** COMBINE YUVA ******************** */ -static bNodeSocketTemplate cmp_node_combyuva_in[] = { - {SOCK_FLOAT, N_("Y"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {SOCK_FLOAT, N_("U"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {SOCK_FLOAT, N_("V"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {SOCK_FLOAT, N_("A"), 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_combyuva_out[] = { - {SOCK_RGBA, N_("Image")}, - {-1, ""}, -}; - -void register_node_type_cmp_combyuva(void) -{ - static bNodeType ntype; - - cmp_node_type_base(&ntype, CMP_NODE_COMBYUVA, "Combine YUVA", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, cmp_node_combyuva_in, cmp_node_combyuva_out); - - 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 new file mode 100644 index 00000000000..41d0668dbf2 --- /dev/null +++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_hsva.cc @@ -0,0 +1,77 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2006 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup cmpnodes + */ + +#include "node_composite_util.hh" + +/* **************** SEPARATE HSVA ******************** */ + +namespace blender::nodes::node_composite_sepcomb_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_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 + +void register_node_type_cmp_sephsva() +{ + namespace file_ns = blender::nodes::node_composite_sepcomb_hsva_cc; + + static bNodeType ntype; + + cmp_node_type_base(&ntype, CMP_NODE_SEPHSVA, "Separate HSVA", NODE_CLASS_CONVERTER); + ntype.declare = file_ns::cmp_node_sephsva_declare; + nodeRegisterType(&ntype); +} + +/* **************** COMBINE HSVA ******************** */ + +namespace blender::nodes::node_composite_sepcomb_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_output<decl::Color>(N_("Image")); +} + +} // namespace blender::nodes::node_composite_sepcomb_hsva_cc + +void register_node_type_cmp_combhsva() +{ + namespace file_ns = blender::nodes::node_composite_sepcomb_hsva_cc; + + static bNodeType ntype; + + cmp_node_type_base(&ntype, CMP_NODE_COMBHSVA, "Combine HSVA", NODE_CLASS_CONVERTER); + ntype.declare = file_ns::cmp_node_combhsva_declare; + + 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 new file mode 100644 index 00000000000..bd200b0886f --- /dev/null +++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_rgba.cc @@ -0,0 +1,77 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2006 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup cmpnodes + */ + +#include "node_composite_util.hh" + +/* **************** SEPARATE RGBA ******************** */ +namespace blender::nodes::node_composite_sepcomb_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_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 + +void register_node_type_cmp_seprgba() +{ + namespace file_ns = blender::nodes::node_composite_sepcomb_rgba_cc; + + static bNodeType ntype; + + cmp_node_type_base(&ntype, CMP_NODE_SEPRGBA, "Separate RGBA", NODE_CLASS_CONVERTER); + ntype.declare = file_ns::cmp_node_seprgba_declare; + + nodeRegisterType(&ntype); +} + +/* **************** COMBINE RGBA ******************** */ + +namespace blender::nodes::node_composite_sepcomb_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_output<decl::Color>(N_("Image")); +} + +} // namespace blender::nodes::node_composite_sepcomb_rgba_cc + +void register_node_type_cmp_combrgba() +{ + namespace file_ns = blender::nodes::node_composite_sepcomb_rgba_cc; + + static bNodeType ntype; + + cmp_node_type_base(&ntype, CMP_NODE_COMBRGBA, "Combine RGBA", NODE_CLASS_CONVERTER); + ntype.declare = file_ns::cmp_node_combrgba_declare; + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcombYCCA.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_ycca.cc index 2090841b3b9..79cb379806c 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcombYCCA.cc +++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_ycca.cc @@ -24,57 +24,67 @@ #include "node_composite_util.hh" /* **************** SEPARATE YCCA ******************** */ -static bNodeSocketTemplate cmp_node_sepycca_in[] = { - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, {-1, ""}}; -static bNodeSocketTemplate cmp_node_sepycca_out[] = { - {SOCK_FLOAT, N_("Y")}, - {SOCK_FLOAT, N_("Cb")}, - {SOCK_FLOAT, N_("Cr")}, - {SOCK_FLOAT, N_("A")}, - {-1, ""}, -}; + +namespace blender::nodes::node_composite_sepcomb_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_output<decl::Float>(N_("Y")); + b.add_output<decl::Float>(N_("Cb")); + b.add_output<decl::Float>(N_("Cr")); + b.add_output<decl::Float>(N_("A")); +} static void node_composit_init_mode_sepycca(bNodeTree *UNUSED(ntree), bNode *node) { node->custom1 = 1; /* BLI_YCC_ITU_BT709 */ } -void register_node_type_cmp_sepycca(void) +} // namespace blender::nodes::node_composite_sepcomb_ycca_cc + +void register_node_type_cmp_sepycca() { + namespace file_ns = blender::nodes::node_composite_sepcomb_ycca_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_SEPYCCA, "Separate YCbCrA", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, cmp_node_sepycca_in, cmp_node_sepycca_out); - node_type_init(&ntype, node_composit_init_mode_sepycca); + cmp_node_type_base(&ntype, CMP_NODE_SEPYCCA, "Separate YCbCrA", NODE_CLASS_CONVERTER); + ntype.declare = file_ns::cmp_node_sepycca_declare; + node_type_init(&ntype, file_ns::node_composit_init_mode_sepycca); nodeRegisterType(&ntype); } /* **************** COMBINE YCCA ******************** */ -static bNodeSocketTemplate cmp_node_combycca_in[] = { - {SOCK_FLOAT, N_("Y"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {SOCK_FLOAT, N_("Cb"), 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {SOCK_FLOAT, N_("Cr"), 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {SOCK_FLOAT, N_("A"), 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_combycca_out[] = { - {SOCK_RGBA, N_("Image")}, - {-1, ""}, -}; + +namespace blender::nodes::node_composite_sepcomb_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_output<decl::Color>(N_("Image")); +} static void node_composit_init_mode_combycca(bNodeTree *UNUSED(ntree), bNode *node) { node->custom1 = 1; /* BLI_YCC_ITU_BT709 */ } -void register_node_type_cmp_combycca(void) +} // namespace blender::nodes::node_composite_sepcomb_ycca_cc + +void register_node_type_cmp_combycca() { + namespace file_ns = blender::nodes::node_composite_sepcomb_ycca_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_COMBYCCA, "Combine YCbCrA", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, cmp_node_combycca_in, cmp_node_combycca_out); - node_type_init(&ntype, node_composit_init_mode_combycca); + cmp_node_type_base(&ntype, CMP_NODE_COMBYCCA, "Combine YCbCrA", NODE_CLASS_CONVERTER); + ntype.declare = file_ns::cmp_node_combycca_declare; + node_type_init(&ntype, file_ns::node_composit_init_mode_combycca); 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 new file mode 100644 index 00000000000..29c0f753e00 --- /dev/null +++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_yuva.cc @@ -0,0 +1,78 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2006 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup cmpnodes + */ + +#include "node_composite_util.hh" + +/* **************** SEPARATE YUVA ******************** */ + +namespace blender::nodes::node_composite_sepcomb_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_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 + +void register_node_type_cmp_sepyuva() +{ + namespace file_ns = blender::nodes::node_composite_sepcomb_yuva_cc; + + static bNodeType ntype; + + cmp_node_type_base(&ntype, CMP_NODE_SEPYUVA, "Separate YUVA", NODE_CLASS_CONVERTER); + ntype.declare = file_ns::cmp_node_sepyuva_declare; + + nodeRegisterType(&ntype); +} + +/* **************** COMBINE YUVA ******************** */ + +namespace blender::nodes::node_composite_sepcomb_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_output<decl::Color>(N_("Image")); +} + +} // namespace blender::nodes::node_composite_sepcomb_yuva_cc + +void register_node_type_cmp_combyuva() +{ + namespace file_ns = blender::nodes::node_composite_sepcomb_yuva_cc; + + static bNodeType ntype; + + cmp_node_type_base(&ntype, CMP_NODE_COMBYUVA, "Combine YUVA", NODE_CLASS_CONVERTER); + ntype.declare = file_ns::cmp_node_combyuva_declare; + + 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 a2089bd0913..18f8fe45833 100644 --- a/source/blender/nodes/composite/nodes/node_composite_setalpha.cc +++ b/source/blender/nodes/composite/nodes/node_composite_setalpha.cc @@ -21,33 +21,46 @@ * \ingroup cmpnodes */ +#include "UI_interface.h" +#include "UI_resources.h" + #include "node_composite_util.hh" /* **************** SET ALPHA ******************** */ -static bNodeSocketTemplate cmp_node_setalpha_in[] = { - {SOCK_RGBA, N_("Image"), 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Alpha"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_setalpha_out[] = { - {SOCK_RGBA, N_("Image")}, - {-1, ""}, -}; + +namespace blender::nodes::node_composite_setalpha_cc { + +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_output<decl::Color>(N_("Image")); +} static void node_composit_init_setalpha(bNodeTree *UNUSED(ntree), bNode *node) { - NodeSetAlpha *settings = (NodeSetAlpha *)MEM_callocN(sizeof(NodeSetAlpha), __func__); + NodeSetAlpha *settings = MEM_cnew<NodeSetAlpha>(__func__); node->storage = settings; settings->mode = CMP_NODE_SETALPHA_MODE_APPLY; } -void register_node_type_cmp_setalpha(void) +static void node_composit_buts_set_alpha(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { + uiItemR(layout, ptr, "mode", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); +} + +} // namespace blender::nodes::node_composite_setalpha_cc + +void register_node_type_cmp_setalpha() +{ + namespace file_ns = blender::nodes::node_composite_setalpha_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_SETALPHA, "Set Alpha", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, cmp_node_setalpha_in, cmp_node_setalpha_out); - node_type_init(&ntype, node_composit_init_setalpha); + cmp_node_type_base(&ntype, CMP_NODE_SETALPHA, "Set Alpha", NODE_CLASS_CONVERTER); + ntype.declare = file_ns::cmp_node_setalpha_declare; + ntype.draw_buttons = file_ns::node_composit_buts_set_alpha; + node_type_init(&ntype, file_ns::node_composit_init_setalpha); node_type_storage( &ntype, "NodeSetAlpha", node_free_standard_storage, node_copy_standard_storage); diff --git a/source/blender/nodes/composite/nodes/node_composite_splitViewer.cc b/source/blender/nodes/composite/nodes/node_composite_split_viewer.cc index 27fbb0fafd4..68a6c8c2943 100644 --- a/source/blender/nodes/composite/nodes/node_composite_splitViewer.cc +++ b/source/blender/nodes/composite/nodes/node_composite_split_viewer.cc @@ -21,46 +21,60 @@ * \ingroup cmpnodes */ -#include "node_composite_util.hh" - #include "BKE_global.h" #include "BKE_image.h" +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_composite_util.hh" + /* **************** SPLIT VIEWER ******************** */ -namespace blender::nodes { +namespace blender::nodes::node_composite_split_viewer_cc { -static void cmp_node_splitviewer_declare(NodeDeclarationBuilder &b) +static void cmp_node_split_viewer_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>("Image"); - b.add_input<decl::Color>("Image"); + b.add_input<decl::Color>(N_("Image")); + b.add_input<decl::Color>(N_("Image"), "Image_001"); } -} // namespace blender::nodes - static void node_composit_init_splitviewer(bNodeTree *UNUSED(ntree), bNode *node) { - ImageUser *iuser = (ImageUser *)MEM_callocN(sizeof(ImageUser), "node image user"); + ImageUser *iuser = MEM_cnew<ImageUser>(__func__); node->storage = iuser; iuser->sfra = 1; - iuser->ok = 1; node->custom1 = 50; /* default 50% split */ node->id = (ID *)BKE_image_ensure_viewer(G.main, IMA_TYPE_COMPOSITE, "Viewer Node"); } -void register_node_type_cmp_splitviewer(void) +static void node_composit_buts_splitviewer(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { + uiLayout *row, *col; + + col = uiLayoutColumn(layout, false); + row = uiLayoutRow(col, false); + uiItemR(row, ptr, "axis", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_EXPAND, nullptr, ICON_NONE); + uiItemR(col, ptr, "factor", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); +} + +} // namespace blender::nodes::node_composite_split_viewer_cc + +void register_node_type_cmp_splitviewer() +{ + namespace file_ns = blender::nodes::node_composite_split_viewer_cc; + static bNodeType ntype; - cmp_node_type_base( - &ntype, CMP_NODE_SPLITVIEWER, "Split Viewer", NODE_CLASS_OUTPUT, NODE_PREVIEW); - ntype.declare = blender::nodes::cmp_node_splitviewer_declare; - node_type_init(&ntype, node_composit_init_splitviewer); + cmp_node_type_base(&ntype, CMP_NODE_SPLITVIEWER, "Split Viewer", NODE_CLASS_OUTPUT); + ntype.declare = file_ns::cmp_node_split_viewer_declare; + ntype.draw_buttons = file_ns::node_composit_buts_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); - /* Do not allow muting for this node. */ - node_type_internal_links(&ntype, nullptr); + ntype.no_muting = true; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_stabilize2d.cc b/source/blender/nodes/composite/nodes/node_composite_stabilize2d.cc index e5ce2e8ceb9..5fe057f4f2e 100644 --- a/source/blender/nodes/composite/nodes/node_composite_stabilize2d.cc +++ b/source/blender/nodes/composite/nodes/node_composite_stabilize2d.cc @@ -21,22 +21,23 @@ * \ingroup cmpnodes */ -#include "node_composite_util.hh" +#include "UI_interface.h" +#include "UI_resources.h" #include "BKE_context.h" #include "BKE_lib_id.h" -/* **************** Translate ******************** */ +#include "node_composite_util.hh" -static bNodeSocketTemplate cmp_node_stabilize2d_in[] = { - {SOCK_RGBA, N_("Image"), 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f}, - {-1, ""}, -}; +/* **************** Stabilize 2D ******************** */ -static bNodeSocketTemplate cmp_node_stabilize2d_out[] = { - {SOCK_RGBA, N_("Image")}, - {-1, ""}, -}; +namespace blender::nodes::node_composite_stabilize2d_cc { + +static void cmp_node_stabilize2d_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_("Image")); +} static void init(const bContext *C, PointerRNA *ptr) { @@ -50,13 +51,41 @@ static void init(const bContext *C, PointerRNA *ptr) node->custom1 = 1; } -void register_node_type_cmp_stabilize2d(void) +static void node_composit_buts_stabilize2d(uiLayout *layout, bContext *C, PointerRNA *ptr) +{ + bNode *node = (bNode *)ptr->data; + + uiTemplateID(layout, + C, + ptr, + "clip", + nullptr, + "CLIP_OT_open", + nullptr, + UI_TEMPLATE_ID_FILTER_ALL, + false, + nullptr); + + if (!node->id) { + return; + } + + uiItemR(layout, ptr, "filter_type", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); + uiItemR(layout, ptr, "invert", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); +} + +} // namespace blender::nodes::node_composite_stabilize2d_cc + +void register_node_type_cmp_stabilize2d() { + namespace file_ns = blender::nodes::node_composite_stabilize2d_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_STABILIZE2D, "Stabilize 2D", NODE_CLASS_DISTORT, 0); - node_type_socket_templates(&ntype, cmp_node_stabilize2d_in, cmp_node_stabilize2d_out); - ntype.initfunc_api = init; + cmp_node_type_base(&ntype, CMP_NODE_STABILIZE2D, "Stabilize 2D", NODE_CLASS_DISTORT); + ntype.declare = file_ns::cmp_node_stabilize2d_declare; + ntype.draw_buttons = file_ns::node_composit_buts_stabilize2d; + ntype.initfunc_api = file_ns::init; 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 73907d2e27f..34433e2bf40 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sunbeams.cc +++ b/source/blender/nodes/composite/nodes/node_composite_sunbeams.cc @@ -21,33 +21,51 @@ * \ingroup cmpnodes */ +#include "UI_interface.h" +#include "UI_resources.h" + #include "node_composite_util.hh" -static bNodeSocketTemplate inputs[] = { - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, - {-1, ""}, -}; -static bNodeSocketTemplate outputs[] = { - {SOCK_RGBA, N_("Image")}, - {-1, ""}, -}; +namespace blender::nodes::node_composite_sunbeams_cc { + +static void cmp_node_sunbeams_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output<decl::Color>(N_("Image")); +} static void init(bNodeTree *UNUSED(ntree), bNode *node) { - NodeSunBeams *data = (NodeSunBeams *)MEM_callocN(sizeof(NodeSunBeams), "sun beams node"); + NodeSunBeams *data = MEM_cnew<NodeSunBeams>(__func__); data->source[0] = 0.5f; data->source[1] = 0.5f; node->storage = data; } -void register_node_type_cmp_sunbeams(void) +static void node_composit_buts_sunbeams(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { + uiItemR(layout, ptr, "source", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_EXPAND, "", ICON_NONE); + uiItemR(layout, + ptr, + "ray_length", + UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, + nullptr, + ICON_NONE); +} + +} // namespace blender::nodes::node_composite_sunbeams_cc + +void register_node_type_cmp_sunbeams() +{ + namespace file_ns = blender::nodes::node_composite_sunbeams_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_SUNBEAMS, "Sun Beams", NODE_CLASS_OP_FILTER, 0); - node_type_socket_templates(&ntype, inputs, outputs); - node_type_init(&ntype, init); + cmp_node_type_base(&ntype, CMP_NODE_SUNBEAMS, "Sun Beams", NODE_CLASS_OP_FILTER); + ntype.declare = file_ns::cmp_node_sunbeams_declare; + ntype.draw_buttons = file_ns::node_composit_buts_sunbeams; + node_type_init(&ntype, file_ns::init); node_type_storage( &ntype, "NodeSunBeams", node_free_standard_storage, node_copy_standard_storage); diff --git a/source/blender/nodes/composite/nodes/node_composite_switch.cc b/source/blender/nodes/composite/nodes/node_composite_switch.cc index 71226a6da0b..342813f8d67 100644 --- a/source/blender/nodes/composite/nodes/node_composite_switch.cc +++ b/source/blender/nodes/composite/nodes/node_composite_switch.cc @@ -21,27 +21,38 @@ * \ingroup cmpnodes */ -#include "../node_composite_util.hh" - -/* **************** MIX RGB ******************** */ -static bNodeSocketTemplate cmp_node_switch_in[] = { - {SOCK_RGBA, N_("Off"), 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f}, - {SOCK_RGBA, N_("On"), 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f}, - {-1, ""}, -}; - -static bNodeSocketTemplate cmp_node_switch_out[] = { - {SOCK_RGBA, N_("Image"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f}, - {-1, ""}, -}; - -/* custom1 = mix type */ -void register_node_type_cmp_switch(void) +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_composite_util.hh" + +/* **************** Switch ******************** */ + +namespace blender::nodes::node_composite_switch_cc { + +static void cmp_node_switch_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Color>(N_("Off")).default_value({0.8f, 0.8f, 0.8f, 1.0f}); + b.add_input<decl::Color>(N_("On")).default_value({0.8f, 0.8f, 0.8f, 1.0f}); + b.add_output<decl::Color>(N_("Image")); +} + +static void node_composit_buts_switch(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "check", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); +} + +} // namespace blender::nodes::node_composite_switch_cc + +void register_node_type_cmp_switch() { + namespace file_ns = blender::nodes::node_composite_switch_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_SWITCH, "Switch", NODE_CLASS_LAYOUT, 0); - node_type_socket_templates(&ntype, cmp_node_switch_in, cmp_node_switch_out); + cmp_node_type_base(&ntype, CMP_NODE_SWITCH, "Switch", NODE_CLASS_LAYOUT); + 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); 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 a61712f7f8d..678d7fe1a9b 100644 --- a/source/blender/nodes/composite/nodes/node_composite_switchview.cc +++ b/source/blender/nodes/composite/nodes/node_composite_switchview.cc @@ -25,9 +25,15 @@ #include "BKE_context.h" #include "BKE_lib_id.h" -#include "../node_composite_util.hh" +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_composite_util.hh" /* **************** SWITCH VIEW ******************** */ + +namespace blender::nodes::node_composite_switchview_cc { + static bNodeSocketTemplate cmp_node_switch_view_out[] = { {SOCK_RGBA, N_("Image"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f}, {-1, ""}, @@ -137,16 +143,33 @@ static void init_switch_view(const bContext *C, PointerRNA *ptr) cmp_node_switch_view_sanitycheck(ntree, node); } -void register_node_type_cmp_switch_view(void) +static void node_composit_buts_switch_view_ex(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *UNUSED(ptr)) { - static bNodeType ntype; + uiItemFullO(layout, + "NODE_OT_switch_view_update", + "Update Views", + ICON_FILE_REFRESH, + nullptr, + WM_OP_INVOKE_DEFAULT, + 0, + nullptr); +} + +} // namespace blender::nodes::node_composite_switchview_cc - cmp_node_type_base(&ntype, CMP_NODE_SWITCH_VIEW, "Switch View", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, nullptr, cmp_node_switch_view_out); +void register_node_type_cmp_switch_view() +{ + namespace file_ns = blender::nodes::node_composite_switchview_cc; - ntype.initfunc_api = init_switch_view; + static bNodeType ntype; - node_type_update(&ntype, cmp_node_switch_view_update); + cmp_node_type_base(&ntype, CMP_NODE_SWITCH_VIEW, "Switch View", NODE_CLASS_CONVERTER); + node_type_socket_templates(&ntype, nullptr, file_ns::cmp_node_switch_view_out); + 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); 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 eff008b4b41..5c2446d4f2c 100644 --- a/source/blender/nodes/composite/nodes/node_composite_texture.cc +++ b/source/blender/nodes/composite/nodes/node_composite_texture.cc @@ -25,28 +25,31 @@ /* **************** TEXTURE ******************** */ -namespace blender::nodes { +namespace blender::nodes::node_composite_texture_cc { static void cmp_node_texture_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Vector>("Offset").min(-2.0f).max(2.0f).subtype(PROP_TRANSLATION); - b.add_input<decl::Vector>("Scale") + b.add_input<decl::Vector>(N_("Offset")).min(-2.0f).max(2.0f).subtype(PROP_TRANSLATION); + b.add_input<decl::Vector>(N_("Scale")) .default_value({1.0f, 1.0f, 1.0f}) .min(-10.0f) .max(10.0f) .subtype(PROP_XYZ); - b.add_output<decl::Float>("Value"); - b.add_output<decl::Color>("Color"); + b.add_output<decl::Float>(N_("Value")); + b.add_output<decl::Color>(N_("Color")); } -} // namespace blender::nodes +} // namespace blender::nodes::node_composite_texture_cc -void register_node_type_cmp_texture(void) +void register_node_type_cmp_texture() { + namespace file_ns = blender::nodes::node_composite_texture_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_TEXTURE, "Texture", NODE_CLASS_INPUT, NODE_PREVIEW); - ntype.declare = blender::nodes::cmp_node_texture_declare; + cmp_node_type_base(&ntype, CMP_NODE_TEXTURE, "Texture", NODE_CLASS_INPUT); + ntype.declare = file_ns::cmp_node_texture_declare; + ntype.flag |= NODE_PREVIEW; 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 85fd240ce2e..08ec998dfa2 100644 --- a/source/blender/nodes/composite/nodes/node_composite_tonemap.cc +++ b/source/blender/nodes/composite/nodes/node_composite_tonemap.cc @@ -21,21 +21,24 @@ * \ingroup cmpnodes */ +#include "RNA_access.h" + +#include "UI_interface.h" +#include "UI_resources.h" + #include "node_composite_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_composite_tonemap_cc { static void cmp_node_tonemap_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_output<decl::Color>("Image"); + b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output<decl::Color>(N_("Image")); } -} // namespace blender::nodes - static void node_composit_init_tonemap(bNodeTree *UNUSED(ntree), bNode *node) { - NodeTonemap *ntm = (NodeTonemap *)MEM_callocN(sizeof(NodeTonemap), "node tonemap data"); + NodeTonemap *ntm = MEM_cnew<NodeTonemap>(__func__); ntm->type = 1; ntm->key = 0.18; ntm->offset = 1; @@ -49,13 +52,40 @@ static void node_composit_init_tonemap(bNodeTree *UNUSED(ntree), bNode *node) node->storage = ntm; } -void register_node_type_cmp_tonemap(void) +static void node_composit_buts_tonemap(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { + uiLayout *col; + + col = uiLayoutColumn(layout, false); + uiItemR(col, ptr, "tonemap_type", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); + if (RNA_enum_get(ptr, "tonemap_type") == 0) { + uiItemR(col, ptr, "key", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR(col, ptr, "offset", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(col, ptr, "gamma", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + } + else { + uiItemR(col, ptr, "intensity", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR( + col, ptr, "contrast", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR( + col, ptr, "adaptation", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + uiItemR( + col, ptr, "correction", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); + } +} + +} // namespace blender::nodes::node_composite_tonemap_cc + +void register_node_type_cmp_tonemap() +{ + namespace file_ns = blender::nodes::node_composite_tonemap_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_TONEMAP, "Tonemap", NODE_CLASS_OP_COLOR, 0); - ntype.declare = blender::nodes::cmp_node_tonemap_declare; - node_type_init(&ntype, node_composit_init_tonemap); + cmp_node_type_base(&ntype, CMP_NODE_TONEMAP, "Tonemap", NODE_CLASS_OP_COLOR); + ntype.declare = file_ns::cmp_node_tonemap_declare; + 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); 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 cb5c9468daa..3a45319d0ad 100644 --- a/source/blender/nodes/composite/nodes/node_composite_trackpos.cc +++ b/source/blender/nodes/composite/nodes/node_composite_trackpos.cc @@ -21,34 +21,89 @@ * \ingroup cmpnodes */ +#include "RNA_access.h" + +#include "UI_interface.h" +#include "UI_resources.h" + #include "node_composite_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_composite_trackpos_cc { static void cmp_node_trackpos_declare(NodeDeclarationBuilder &b) { - b.add_output<decl::Float>("X"); - b.add_output<decl::Float>("Y"); - b.add_output<decl::Vector>("Speed").subtype(PROP_VELOCITY); + b.add_output<decl::Float>(N_("X")); + b.add_output<decl::Float>(N_("Y")); + b.add_output<decl::Vector>(N_("Speed")).subtype(PROP_VELOCITY); } -} // namespace blender::nodes - static void init(bNodeTree *UNUSED(ntree), bNode *node) { - NodeTrackPosData *data = (NodeTrackPosData *)MEM_callocN(sizeof(NodeTrackPosData), - "node track position data"); + NodeTrackPosData *data = MEM_cnew<NodeTrackPosData>(__func__); node->storage = data; } -void register_node_type_cmp_trackpos(void) +static void node_composit_buts_trackpos(uiLayout *layout, bContext *C, PointerRNA *ptr) { + bNode *node = (bNode *)ptr->data; + + uiTemplateID(layout, + C, + ptr, + "clip", + nullptr, + "CLIP_OT_open", + nullptr, + UI_TEMPLATE_ID_FILTER_ALL, + false, + nullptr); + + if (node->id) { + MovieClip *clip = (MovieClip *)node->id; + MovieTracking *tracking = &clip->tracking; + MovieTrackingObject *object; + uiLayout *col; + PointerRNA tracking_ptr; + NodeTrackPosData *data = (NodeTrackPosData *)node->storage; + + RNA_pointer_create(&clip->id, &RNA_MovieTracking, tracking, &tracking_ptr); + + col = uiLayoutColumn(layout, false); + uiItemPointerR(col, ptr, "tracking_object", &tracking_ptr, "objects", "", ICON_OBJECT_DATA); + + object = BKE_tracking_object_get_named(tracking, data->tracking_object); + if (object) { + PointerRNA object_ptr; + + RNA_pointer_create(&clip->id, &RNA_MovieTrackingObject, object, &object_ptr); + + uiItemPointerR(col, ptr, "track_name", &object_ptr, "tracks", "", ICON_ANIM_DATA); + } + else { + uiItemR(layout, ptr, "track_name", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_ANIM_DATA); + } + + uiItemR(layout, ptr, "position", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + + if (ELEM(node->custom1, CMP_TRACKPOS_RELATIVE_FRAME, CMP_TRACKPOS_ABSOLUTE_FRAME)) { + uiItemR(layout, ptr, "frame_relative", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + } + } +} + +} // namespace blender::nodes::node_composite_trackpos_cc + +void register_node_type_cmp_trackpos() +{ + namespace file_ns = blender::nodes::node_composite_trackpos_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_TRACKPOS, "Track Position", NODE_CLASS_INPUT, 0); - ntype.declare = blender::nodes::cmp_node_trackpos_declare; - node_type_init(&ntype, init); + 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); node_type_storage( &ntype, "NodeTrackPosData", node_free_standard_storage, node_copy_standard_storage); diff --git a/source/blender/nodes/composite/nodes/node_composite_transform.cc b/source/blender/nodes/composite/nodes/node_composite_transform.cc index 1695101cdbf..6afc173df04 100644 --- a/source/blender/nodes/composite/nodes/node_composite_transform.cc +++ b/source/blender/nodes/composite/nodes/node_composite_transform.cc @@ -21,30 +21,45 @@ * \ingroup cmpnodes */ +#include "UI_interface.h" +#include "UI_resources.h" + #include "node_composite_util.hh" /* **************** Transform ******************** */ -static bNodeSocketTemplate cmp_node_transform_in[] = { - {SOCK_RGBA, N_("Image"), 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("X"), 0.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f}, - {SOCK_FLOAT, N_("Y"), 0.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f}, - {SOCK_FLOAT, N_("Angle"), 0.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f, PROP_ANGLE}, - {SOCK_FLOAT, N_("Scale"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0001f, CMP_SCALE_MAX}, - {-1, ""}, -}; - -static bNodeSocketTemplate cmp_node_transform_out[] = { - {SOCK_RGBA, N_("Image")}, - {-1, ""}, -}; - -void register_node_type_cmp_transform(void) +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::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); + b.add_output<decl::Color>(N_("Image")); +} + +static void node_composit_buts_transform(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { + uiItemR(layout, ptr, "filter_type", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); +} + +} // namespace blender::nodes::node_composite_transform_cc + +void register_node_type_cmp_transform() +{ + namespace file_ns = blender::nodes::node_composite_transform_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_TRANSFORM, "Transform", NODE_CLASS_DISTORT, 0); - node_type_socket_templates(&ntype, cmp_node_transform_in, cmp_node_transform_out); + 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; 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 0ee8a41a5ea..6666d161b4a 100644 --- a/source/blender/nodes/composite/nodes/node_composite_translate.cc +++ b/source/blender/nodes/composite/nodes/node_composite_translate.cc @@ -21,35 +21,47 @@ * \ingroup cmpnodes */ +#include "UI_interface.h" +#include "UI_resources.h" + #include "node_composite_util.hh" -/* **************** Translate ******************** */ +/* **************** Translate ******************** */ -static bNodeSocketTemplate cmp_node_translate_in[] = { - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, - {SOCK_FLOAT, N_("X"), 0.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f, PROP_NONE}, - {SOCK_FLOAT, N_("Y"), 0.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f, PROP_NONE}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_translate_out[] = { - {SOCK_RGBA, N_("Image")}, - {-1, ""}, -}; +namespace blender::nodes::node_composite_translate_cc { + +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_output<decl::Color>(N_("Image")); +} static void node_composit_init_translate(bNodeTree *UNUSED(ntree), bNode *node) { - NodeTranslateData *data = (NodeTranslateData *)MEM_callocN(sizeof(NodeTranslateData), - "node translate data"); + NodeTranslateData *data = MEM_cnew<NodeTranslateData>(__func__); node->storage = data; } -void register_node_type_cmp_translate(void) +static void node_composit_buts_translate(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { + uiItemR(layout, ptr, "use_relative", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(layout, ptr, "wrap_axis", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); +} + +} // namespace blender::nodes::node_composite_translate_cc + +void register_node_type_cmp_translate() +{ + namespace file_ns = blender::nodes::node_composite_translate_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_TRANSLATE, "Translate", NODE_CLASS_DISTORT, 0); - node_type_socket_templates(&ntype, cmp_node_translate_in, cmp_node_translate_out); - node_type_init(&ntype, node_composit_init_translate); + cmp_node_type_base(&ntype, CMP_NODE_TRANSLATE, "Translate", NODE_CLASS_DISTORT); + ntype.declare = file_ns::cmp_node_translate_declare; + ntype.draw_buttons = file_ns::node_composit_buts_translate; + node_type_init(&ntype, file_ns::node_composit_init_translate); node_type_storage( &ntype, "NodeTranslateData", node_free_standard_storage, node_copy_standard_storage); diff --git a/source/blender/nodes/composite/nodes/node_composite_valToRgb.cc b/source/blender/nodes/composite/nodes/node_composite_val_to_rgb.cc index ece52dea269..797bd931577 100644 --- a/source/blender/nodes/composite/nodes/node_composite_valToRgb.cc +++ b/source/blender/nodes/composite/nodes/node_composite_val_to_rgb.cc @@ -24,50 +24,58 @@ #include "node_composite_util.hh" /* **************** VALTORGB ******************** */ -static bNodeSocketTemplate cmp_node_valtorgb_in[] = { - {SOCK_FLOAT, N_("Fac"), 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_valtorgb_out[] = { - {SOCK_RGBA, N_("Image")}, - {SOCK_FLOAT, N_("Alpha")}, - {-1, ""}, -}; + +namespace blender::nodes::node_composite_val_to_rgb_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_output<decl::Float>(N_("Alpha")); +} static void node_composit_init_valtorgb(bNodeTree *UNUSED(ntree), bNode *node) { node->storage = BKE_colorband_add(true); } -void register_node_type_cmp_valtorgb(void) +} // namespace blender::nodes::node_composite_val_to_rgb_cc + +void register_node_type_cmp_valtorgb() { + namespace file_ns = blender::nodes::node_composite_val_to_rgb_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_VALTORGB, "ColorRamp", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, cmp_node_valtorgb_in, cmp_node_valtorgb_out); + cmp_node_type_base(&ntype, CMP_NODE_VALTORGB, "ColorRamp", NODE_CLASS_CONVERTER); + ntype.declare = file_ns::cmp_node_valtorgb_declare; node_type_size(&ntype, 240, 200, 320); - node_type_init(&ntype, node_composit_init_valtorgb); + node_type_init(&ntype, file_ns::node_composit_init_valtorgb); node_type_storage(&ntype, "ColorBand", node_free_standard_storage, node_copy_standard_storage); nodeRegisterType(&ntype); } /* **************** RGBTOBW ******************** */ -static bNodeSocketTemplate cmp_node_rgbtobw_in[] = { - {SOCK_RGBA, N_("Image"), 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_rgbtobw_out[] = { - {SOCK_FLOAT, N_("Val"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f}, - {-1, ""}, -}; - -void register_node_type_cmp_rgbtobw(void) + +namespace blender::nodes::node_composite_val_to_rgb_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")); +} + +} // namespace blender::nodes::node_composite_val_to_rgb_cc + +void register_node_type_cmp_rgbtobw() { + namespace file_ns = blender::nodes::node_composite_val_to_rgb_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_RGBTOBW, "RGB to BW", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, cmp_node_rgbtobw_in, cmp_node_rgbtobw_out); + 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); 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 5459801bcc7..076adc1c01c 100644 --- a/source/blender/nodes/composite/nodes/node_composite_value.cc +++ b/source/blender/nodes/composite/nodes/node_composite_value.cc @@ -25,21 +25,23 @@ /* **************** VALUE ******************** */ -namespace blender::nodes { +namespace blender::nodes::node_composite_value_cc { static void cmp_node_value_declare(NodeDeclarationBuilder &b) { - b.add_output<decl::Float>("Value").default_value(0.5f); + b.add_output<decl::Float>(N_("Value")).default_value(0.5f); } -} // namespace blender::nodes +} // namespace blender::nodes::node_composite_value_cc -void register_node_type_cmp_value(void) +void register_node_type_cmp_value() { + namespace file_ns = blender::nodes::node_composite_value_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_VALUE, "Value", NODE_CLASS_INPUT, 0); - ntype.declare = blender::nodes::cmp_node_value_declare; + 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); nodeRegisterType(&ntype); diff --git a/source/blender/nodes/composite/nodes/node_composite_vecBlur.cc b/source/blender/nodes/composite/nodes/node_composite_vecBlur.cc deleted file mode 100644 index ce6ba659609..00000000000 --- a/source/blender/nodes/composite/nodes/node_composite_vecBlur.cc +++ /dev/null @@ -1,54 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2006 Blender Foundation. - * All rights reserved. - */ - -/** \file - * \ingroup cmpnodes - */ - -#include "node_composite_util.hh" - -/* **************** VECTOR BLUR ******************** */ -static bNodeSocketTemplate cmp_node_vecblur_in[] = { - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, - {SOCK_FLOAT, N_("Z"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE}, - {SOCK_VECTOR, N_("Speed"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_VELOCITY}, - {-1, ""}}; -static bNodeSocketTemplate cmp_node_vecblur_out[] = {{SOCK_RGBA, N_("Image")}, {-1, ""}}; - -static void node_composit_init_vecblur(bNodeTree *UNUSED(ntree), bNode *node) -{ - NodeBlurData *nbd = (NodeBlurData *)MEM_callocN(sizeof(NodeBlurData), "node blur data"); - node->storage = nbd; - nbd->samples = 32; - nbd->fac = 1.0f; -} - -/* custom1: iterations, custom2: max_speed (0 = no_limit). */ -void register_node_type_cmp_vecblur(void) -{ - static bNodeType ntype; - - cmp_node_type_base(&ntype, CMP_NODE_VECBLUR, "Vector Blur", NODE_CLASS_OP_FILTER, 0); - node_type_socket_templates(&ntype, cmp_node_vecblur_in, cmp_node_vecblur_out); - node_type_init(&ntype, node_composit_init_vecblur); - node_type_storage( - &ntype, "NodeBlurData", node_free_standard_storage, node_copy_standard_storage); - - 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 new file mode 100644 index 00000000000..a461688641d --- /dev/null +++ b/source/blender/nodes/composite/nodes/node_composite_vec_blur.cc @@ -0,0 +1,86 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2006 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup cmpnodes + */ + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_composite_util.hh" + +/* **************** VECTOR BLUR ******************** */ + +namespace blender::nodes::node_composite_vec_blur_cc { + +static void cmp_node_vec_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::Float>(N_("Z")).default_value(0.0f).min(0.0f).max(1.0f); + b.add_input<decl::Vector>(N_("Speed")) + .default_value({0.0f, 0.0f, 0.0f}) + .min(0.0f) + .max(1.0f) + .subtype(PROP_VELOCITY); + b.add_output<decl::Color>(N_("Image")); +} + +/* custom1: iterations, custom2: max_speed (0 = no_limit). */ +static void node_composit_init_vecblur(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeBlurData *nbd = MEM_cnew<NodeBlurData>(__func__); + node->storage = nbd; + nbd->samples = 32; + nbd->fac = 1.0f; +} + +static void node_composit_buts_vecblur(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayout *col; + + col = uiLayoutColumn(layout, false); + uiItemR(col, ptr, "samples", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(col, ptr, "factor", UI_ITEM_R_SPLIT_EMPTY_NAME, IFACE_("Blur"), ICON_NONE); + + col = uiLayoutColumn(layout, true); + uiItemL(col, IFACE_("Speed:"), ICON_NONE); + uiItemR(col, ptr, "speed_min", UI_ITEM_R_SPLIT_EMPTY_NAME, IFACE_("Min"), ICON_NONE); + uiItemR(col, ptr, "speed_max", UI_ITEM_R_SPLIT_EMPTY_NAME, IFACE_("Max"), ICON_NONE); + + uiItemR(layout, ptr, "use_curved", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); +} + +} // namespace blender::nodes::node_composite_vec_blur_cc + +void register_node_type_cmp_vecblur() +{ + namespace file_ns = blender::nodes::node_composite_vec_blur_cc; + + static bNodeType ntype; + + cmp_node_type_base(&ntype, CMP_NODE_VECBLUR, "Vector Blur", NODE_CLASS_OP_FILTER); + ntype.declare = file_ns::cmp_node_vec_blur_declare; + ntype.draw_buttons = file_ns::node_composit_buts_vecblur; + node_type_init(&ntype, file_ns::node_composit_init_vecblur); + node_type_storage( + &ntype, "NodeBlurData", node_free_standard_storage, node_copy_standard_storage); + + 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 7234d4d8eb2..a57f1fbb8ef 100644 --- a/source/blender/nodes/composite/nodes/node_composite_viewer.cc +++ b/source/blender/nodes/composite/nodes/node_composite_viewer.cc @@ -21,46 +21,73 @@ * \ingroup cmpnodes */ -#include "node_composite_util.hh" - #include "BKE_global.h" #include "BKE_image.h" +#include "RNA_access.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_composite_util.hh" + /* **************** VIEWER ******************** */ -namespace blender::nodes { +namespace blender::nodes::node_composite_viewer_cc { static void cmp_node_viewer_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>("Image").default_value({0.0f, 0.0f, 0.0f, 1.0f}); - b.add_input<decl::Float>("Alpha").default_value(1.0f).min(0.0f).max(1.0f); - b.add_input<decl::Float>("Z").default_value(1.0f).min(0.0f).max(1.0f); + b.add_input<decl::Color>(N_("Image")).default_value({0.0f, 0.0f, 0.0f, 1.0f}); + b.add_input<decl::Float>(N_("Alpha")).default_value(1.0f).min(0.0f).max(1.0f); + b.add_input<decl::Float>(N_("Z")).default_value(1.0f).min(0.0f).max(1.0f); } -} // namespace blender::nodes - static void node_composit_init_viewer(bNodeTree *UNUSED(ntree), bNode *node) { - ImageUser *iuser = (ImageUser *)MEM_callocN(sizeof(ImageUser), "node image user"); + ImageUser *iuser = MEM_cnew<ImageUser>(__func__); node->storage = iuser; iuser->sfra = 1; - iuser->ok = 1; node->custom3 = 0.5f; node->custom4 = 0.5f; node->id = (ID *)BKE_image_ensure_viewer(G.main, IMA_TYPE_COMPOSITE, "Viewer Node"); } -void register_node_type_cmp_viewer(void) +static void node_composit_buts_viewer(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "use_alpha", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); +} + +static void node_composit_buts_viewer_ex(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayout *col; + + uiItemR(layout, ptr, "use_alpha", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(layout, ptr, "tile_order", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + if (RNA_enum_get(ptr, "tile_order") == 0) { + col = uiLayoutColumn(layout, true); + uiItemR(col, ptr, "center_x", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(col, ptr, "center_y", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + } +} + +} // namespace blender::nodes::node_composite_viewer_cc + +void register_node_type_cmp_viewer() { + namespace file_ns = blender::nodes::node_composite_viewer_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_VIEWER, "Viewer", NODE_CLASS_OUTPUT, NODE_PREVIEW); - ntype.declare = blender::nodes::cmp_node_viewer_declare; - node_type_init(&ntype, node_composit_init_viewer); + cmp_node_type_base(&ntype, CMP_NODE_VIEWER, "Viewer", NODE_CLASS_OUTPUT); + ntype.declare = file_ns::cmp_node_viewer_declare; + ntype.draw_buttons = file_ns::node_composit_buts_viewer; + ntype.draw_buttons_ex = file_ns::node_composit_buts_viewer_ex; + 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); - node_type_internal_links(&ntype, nullptr); + ntype.no_muting = true; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_zcombine.cc b/source/blender/nodes/composite/nodes/node_composite_zcombine.cc index 79e4d449159..7a6d5b3af5f 100644 --- a/source/blender/nodes/composite/nodes/node_composite_zcombine.cc +++ b/source/blender/nodes/composite/nodes/node_composite_zcombine.cc @@ -21,31 +21,45 @@ * \ingroup cmpnodes */ +#include "UI_interface.h" +#include "UI_resources.h" + #include "node_composite_util.hh" /* **************** Z COMBINE ******************** */ -namespace blender::nodes { +namespace blender::nodes::node_composite_zcombine_cc { static void cmp_node_zcombine_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>("Image").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input<decl::Float>("Z").default_value(1.0f).min(0.0f).max(10000.0f); - b.add_input<decl::Color>("Image", "Image_001").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input<decl::Float>("Z", "Z_001").default_value(1.0f).min(0.0f).max(10000.0f); - b.add_output<decl::Color>("Image"); - b.add_output<decl::Float>("Z"); + b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Float>(N_("Z")).default_value(1.0f).min(0.0f).max(10000.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_("Z"), "Z_001").default_value(1.0f).min(0.0f).max(10000.0f); + b.add_output<decl::Color>(N_("Image")); + b.add_output<decl::Float>(N_("Z")); +} + +static void node_composit_buts_zcombine(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayout *col; + + col = uiLayoutColumn(layout, true); + uiItemR(col, ptr, "use_alpha", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(col, ptr, "use_antialias_z", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } -} // namespace blender::nodes +} // namespace blender::nodes::node_composite_zcombine_cc -/* lazy coder NOTE: node->custom2 is abused to send signal. */ -void register_node_type_cmp_zcombine(void) +void register_node_type_cmp_zcombine() { + namespace file_ns = blender::nodes::node_composite_zcombine_cc; + static bNodeType ntype; - cmp_node_type_base(&ntype, CMP_NODE_ZCOMBINE, "Z Combine", NODE_CLASS_OP_COLOR, 0); - ntype.declare = blender::nodes::cmp_node_zcombine_declare; + 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; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/function/CMakeLists.txt b/source/blender/nodes/function/CMakeLists.txt new file mode 100644 index 00000000000..0c3c6a34995 --- /dev/null +++ b/source/blender/nodes/function/CMakeLists.txt @@ -0,0 +1,75 @@ +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# The Original Code is Copyright (C) 2021, Blender Foundation +# All rights reserved. +# ***** END GPL LICENSE BLOCK ***** + +set(INC + . + .. + ../intern + ../../blenkernel + ../../blenlib + ../../blentranslation + ../../editors/include + ../../functions + ../../makesdna + ../../makesrna + ../../windowmanager + ../../../../intern/guardedalloc +) + + +set(SRC + nodes/legacy/node_fn_random_float.cc + + nodes/node_fn_align_euler_to_vector.cc + nodes/node_fn_boolean_math.cc + nodes/node_fn_compare.cc + nodes/node_fn_float_to_int.cc + nodes/node_fn_input_bool.cc + nodes/node_fn_input_color.cc + nodes/node_fn_input_int.cc + nodes/node_fn_input_special_characters.cc + nodes/node_fn_input_string.cc + nodes/node_fn_input_vector.cc + nodes/node_fn_random_value.cc + nodes/node_fn_replace_string.cc + nodes/node_fn_rotate_euler.cc + nodes/node_fn_slice_string.cc + nodes/node_fn_string_length.cc + nodes/node_fn_value_to_string.cc + + node_function_util.cc + + node_function_util.hh +) + +set(LIB + bf_functions +) + +if(WITH_INTERNATIONAL) + add_definitions(-DWITH_INTERNATIONAL) +endif() + +blender_add_lib(bf_nodes_function "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") + +if(WITH_UNITY_BUILD) + set_target_properties(bf_nodes_function PROPERTIES UNITY_BUILD ON) + set_target_properties(bf_nodes_function PROPERTIES UNITY_BUILD_BATCH_SIZE 10) +endif() diff --git a/source/blender/nodes/function/node_function_util.cc b/source/blender/nodes/function/node_function_util.cc index 8ff8b416310..0137b298f45 100644 --- a/source/blender/nodes/function/node_function_util.cc +++ b/source/blender/nodes/function/node_function_util.cc @@ -17,22 +17,24 @@ #include "node_function_util.hh" #include "node_util.h" +#include "NOD_socket_search_link.hh" + static bool fn_node_poll_default(bNodeType *UNUSED(ntype), bNodeTree *ntree, const char **r_disabled_hint) { /* Function nodes are only supported in simulation node trees so far. */ if (!STREQ(ntree->idname, "GeometryNodeTree")) { - *r_disabled_hint = "Not a geometry node tree"; + *r_disabled_hint = TIP_("Not a geometry node tree"); return false; } return true; } -void fn_node_type_base(bNodeType *ntype, int type, const char *name, short nclass, short flag) +void fn_node_type_base(bNodeType *ntype, int type, const char *name, short nclass) { - node_type_base(ntype, type, name, nclass, flag); + node_type_base(ntype, type, name, nclass); ntype->poll = fn_node_poll_default; - ntype->update_internal_links = node_update_internal_links_default; ntype->insert_link = node_insert_link_default; + ntype->gather_link_search_ops = blender::nodes::search_link_ops_for_basic_node; } diff --git a/source/blender/nodes/function/node_function_util.hh b/source/blender/nodes/function/node_function_util.hh index 46b485298e3..69c617b4f01 100644 --- a/source/blender/nodes/function/node_function_util.hh +++ b/source/blender/nodes/function/node_function_util.hh @@ -18,7 +18,7 @@ #include <string.h> -#include "BLI_float3.hh" +#include "BLI_math_vec_types.hh" #include "BLI_utildefines.h" #include "MEM_guardedalloc.h" @@ -37,5 +37,4 @@ #include "FN_multi_function_builder.hh" -void fn_node_type_base( - struct bNodeType *ntype, int type, const char *name, short nclass, short flag); +void fn_node_type_base(struct bNodeType *ntype, int type, const char *name, short nclass); diff --git a/source/blender/nodes/function/nodes/legacy/node_fn_random_float.cc b/source/blender/nodes/function/nodes/legacy/node_fn_random_float.cc index 7f6f554ba93..9470b82a8eb 100644 --- a/source/blender/nodes/function/nodes/legacy/node_fn_random_float.cc +++ b/source/blender/nodes/function/nodes/legacy/node_fn_random_float.cc @@ -18,18 +18,16 @@ #include "BLI_hash.h" -namespace blender::nodes { +namespace blender::nodes::node_fn_random_float_cc { static void fn_node_legacy_random_float_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input<decl::Float>("Min").min(-10000.0f).max(10000.0f); - b.add_input<decl::Float>("Max").default_value(1.0f).min(-10000.0f).max(10000.0f); - b.add_input<decl::Int>("Seed").min(-10000).max(10000); - b.add_output<decl::Float>("Value"); -}; - -} // namespace blender::nodes + b.add_input<decl::Float>(N_("Min")).min(-10000.0f).max(10000.0f); + b.add_input<decl::Float>(N_("Max")).default_value(1.0f).min(-10000.0f).max(10000.0f); + b.add_input<decl::Int>(N_("Seed")).min(-10000).max(10000); + b.add_output<decl::Float>(N_("Value")); +} class RandomFloatFunction : public blender::fn::MultiFunction { public: @@ -75,12 +73,16 @@ static void fn_node_legacy_random_float_build_multi_function( builder.set_matching_fn(fn); } +} // namespace blender::nodes::node_fn_random_float_cc + void register_node_type_fn_legacy_random_float() { + namespace file_ns = blender::nodes::node_fn_random_float_cc; + static bNodeType ntype; - fn_node_type_base(&ntype, FN_NODE_LEGACY_RANDOM_FLOAT, "Random Float", 0, 0); - ntype.declare = blender::nodes::fn_node_legacy_random_float_declare; - ntype.build_multi_function = fn_node_legacy_random_float_build_multi_function; + fn_node_type_base(&ntype, FN_NODE_LEGACY_RANDOM_FLOAT, "Random Float", 0); + ntype.declare = file_ns::fn_node_legacy_random_float_declare; + ntype.build_multi_function = file_ns::fn_node_legacy_random_float_build_multi_function; nodeRegisterType(&ntype); } 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 new file mode 100644 index 00000000000..bcc035e6ede --- /dev/null +++ b/source/blender/nodes/function/nodes/node_fn_align_euler_to_vector.cc @@ -0,0 +1,224 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_task.hh" + +#include "RNA_enum_types.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_function_util.hh" + +namespace blender::nodes::node_fn_align_euler_to_vector_cc { + +static void fn_node_align_euler_to_vector_declare(NodeDeclarationBuilder &b) +{ + b.is_function_node(); + b.add_input<decl::Vector>(N_("Rotation")).subtype(PROP_EULER).hide_value(); + b.add_input<decl::Float>(N_("Factor")) + .default_value(1.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Vector>(N_("Vector")).default_value({0.0, 0.0, 1.0}); + b.add_output<decl::Vector>(N_("Rotation")).subtype(PROP_EULER); +} + +static void fn_node_align_euler_to_vector_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiItemR(layout, ptr, "axis", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + uiItemR(layout, ptr, "pivot_axis", 0, IFACE_("Pivot"), ICON_NONE); +} + +static void align_rotations_auto_pivot(IndexMask mask, + const VArray<float3> &input_rotations, + const VArray<float3> &vectors, + const VArray<float> &factors, + const float3 local_main_axis, + const MutableSpan<float3> output_rotations) +{ + threading::parallel_for(mask.index_range(), 512, [&](IndexRange mask_range) { + for (const int maski : mask_range) { + const int64_t i = mask[maski]; + const float3 vector = vectors[i]; + if (is_zero_v3(vector)) { + output_rotations[i] = input_rotations[i]; + continue; + } + + float old_rotation[3][3]; + eul_to_mat3(old_rotation, input_rotations[i]); + float3 old_axis; + mul_v3_m3v3(old_axis, old_rotation, local_main_axis); + + const float3 new_axis = math::normalize(vector); + float3 rotation_axis = math::cross_high_precision(old_axis, new_axis); + if (is_zero_v3(rotation_axis)) { + /* The vectors are linearly dependent, so we fall back to another axis. */ + rotation_axis = math::cross_high_precision(old_axis, float3(1, 0, 0)); + if (is_zero_v3(rotation_axis)) { + /* This is now guaranteed to not be zero. */ + rotation_axis = math::cross_high_precision(old_axis, float3(0, 1, 0)); + } + } + + const float full_angle = angle_normalized_v3v3(old_axis, new_axis); + const float angle = factors[i] * full_angle; + + float rotation[3][3]; + axis_angle_to_mat3(rotation, rotation_axis, angle); + + float new_rotation_matrix[3][3]; + mul_m3_m3m3(new_rotation_matrix, rotation, old_rotation); + + float3 new_rotation; + mat3_to_eul(new_rotation, new_rotation_matrix); + + output_rotations[i] = new_rotation; + } + }); +} + +static void align_rotations_fixed_pivot(IndexMask mask, + const VArray<float3> &input_rotations, + const VArray<float3> &vectors, + const VArray<float> &factors, + const float3 local_main_axis, + const float3 local_pivot_axis, + const MutableSpan<float3> output_rotations) +{ + threading::parallel_for(mask.index_range(), 512, [&](IndexRange mask_range) { + for (const int64_t maski : mask_range) { + const int64_t i = mask[maski]; + if (local_main_axis == local_pivot_axis) { + /* Can't compute any meaningful rotation angle in this case. */ + output_rotations[i] = input_rotations[i]; + continue; + } + + const float3 vector = vectors[i]; + if (is_zero_v3(vector)) { + output_rotations[i] = input_rotations[i]; + continue; + } + + float old_rotation[3][3]; + eul_to_mat3(old_rotation, input_rotations[i]); + float3 old_axis; + mul_v3_m3v3(old_axis, old_rotation, local_main_axis); + float3 pivot_axis; + mul_v3_m3v3(pivot_axis, old_rotation, local_pivot_axis); + + float full_angle = angle_signed_on_axis_v3v3_v3(vector, old_axis, pivot_axis); + if (full_angle > M_PI) { + /* Make sure the point is rotated as little as possible. */ + full_angle -= 2.0f * M_PI; + } + const float angle = factors[i] * full_angle; + + float rotation[3][3]; + axis_angle_to_mat3(rotation, pivot_axis, angle); + + float new_rotation_matrix[3][3]; + mul_m3_m3m3(new_rotation_matrix, rotation, old_rotation); + + float3 new_rotation; + mat3_to_eul(new_rotation, new_rotation_matrix); + + output_rotations[i] = new_rotation; + } + }); +} + +class MF_AlignEulerToVector : public fn::MultiFunction { + private: + int main_axis_mode_; + int pivot_axis_mode_; + + public: + MF_AlignEulerToVector(int main_axis_mode, int pivot_axis_mode) + : main_axis_mode_(main_axis_mode), pivot_axis_mode_(pivot_axis_mode) + { + static fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static fn::MFSignature create_signature() + { + fn::MFSignatureBuilder signature{"Align Euler To Vector"}; + signature.single_input<float3>("Rotation"); + signature.single_input<float>("Factor"); + signature.single_input<float3>("Vector"); + + signature.single_output<float3>("Rotation"); + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + const VArray<float3> &input_rotations = params.readonly_single_input<float3>(0, "Rotation"); + const VArray<float> &factors = params.readonly_single_input<float>(1, "Factor"); + const VArray<float3> &vectors = params.readonly_single_input<float3>(2, "Vector"); + + auto output_rotations = params.uninitialized_single_output<float3>(3, "Rotation"); + + float3 local_main_axis = {0.0f, 0.0f, 0.0f}; + local_main_axis[main_axis_mode_] = 1; + + if (pivot_axis_mode_ == FN_NODE_ALIGN_EULER_TO_VECTOR_PIVOT_AXIS_AUTO) { + align_rotations_auto_pivot( + mask, input_rotations, vectors, factors, local_main_axis, output_rotations); + } + else { + float3 local_pivot_axis = {0.0f, 0.0f, 0.0f}; + local_pivot_axis[pivot_axis_mode_ - 1] = 1; + align_rotations_fixed_pivot(mask, + input_rotations, + vectors, + factors, + local_main_axis, + local_pivot_axis, + output_rotations); + } + } +}; + +static void fn_node_align_euler_to_vector_build_multi_function(NodeMultiFunctionBuilder &builder) +{ + bNode &node = builder.node(); + builder.construct_and_set_matching_fn<MF_AlignEulerToVector>(node.custom1, node.custom2); +} + +} // namespace blender::nodes::node_fn_align_euler_to_vector_cc + +void register_node_type_fn_align_euler_to_vector() +{ + namespace file_ns = blender::nodes::node_fn_align_euler_to_vector_cc; + + static bNodeType ntype; + + fn_node_type_base( + &ntype, FN_NODE_ALIGN_EULER_TO_VECTOR, "Align Euler to Vector", NODE_CLASS_CONVERTER); + ntype.declare = file_ns::fn_node_align_euler_to_vector_declare; + ntype.draw_buttons = file_ns::fn_node_align_euler_to_vector_layout; + ntype.build_multi_function = file_ns::fn_node_align_euler_to_vector_build_multi_function; + nodeRegisterType(&ntype); +} 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 d10490bb2ee..9425c4ff328 100644 --- a/source/blender/nodes/function/nodes/node_fn_boolean_math.cc +++ b/source/blender/nodes/function/nodes/node_fn_boolean_math.cc @@ -22,34 +22,36 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "NOD_socket_search_link.hh" + #include "node_function_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_fn_boolean_math_cc { static void fn_node_boolean_math_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input<decl::Bool>("Boolean", "Boolean"); - b.add_input<decl::Bool>("Boolean", "Boolean_001"); - b.add_output<decl::Bool>("Boolean"); -}; - -} // namespace blender::nodes + b.add_input<decl::Bool>(N_("Boolean"), "Boolean"); + b.add_input<decl::Bool>(N_("Boolean"), "Boolean_001"); + b.add_output<decl::Bool>(N_("Boolean")); +} static void fn_node_boolean_math_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "operation", 0, "", ICON_NONE); } -static void node_boolean_math_update(bNodeTree *UNUSED(ntree), bNode *node) +static void node_boolean_math_update(bNodeTree *ntree, bNode *node) { bNodeSocket *sockB = (bNodeSocket *)BLI_findlink(&node->inputs, 1); - nodeSetSocketAvailability(sockB, - ELEM(node->custom1, NODE_BOOLEAN_MATH_AND, NODE_BOOLEAN_MATH_OR)); + nodeSetSocketAvailability(ntree, sockB, !ELEM(node->custom1, NODE_BOOLEAN_MATH_NOT)); } -static void node_boolean_math_label(bNodeTree *UNUSED(ntree), bNode *node, char *label, int maxlen) +static void node_boolean_math_label(const bNodeTree *UNUSED(ntree), + const bNode *node, + char *label, + int maxlen) { const char *name; bool enum_label = RNA_enum_name(rna_enum_node_boolean_math_items, node->custom1, &name); @@ -59,13 +61,46 @@ static void node_boolean_math_label(bNodeTree *UNUSED(ntree), bNode *node, char BLI_strncpy(label, IFACE_(name), maxlen); } -static const blender::fn::MultiFunction *get_multi_function(bNode &bnode) +static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) { - static blender::fn::CustomMF_SI_SI_SO<bool, bool, bool> and_fn{ - "And", [](bool a, bool b) { return a && b; }}; - static blender::fn::CustomMF_SI_SI_SO<bool, bool, bool> or_fn{ - "Or", [](bool a, bool b) { return a || b; }}; - static blender::fn::CustomMF_SI_SO<bool, bool> not_fn{"Not", [](bool a) { return !a; }}; + if (!params.node_tree().typeinfo->validate_link( + static_cast<eNodeSocketDatatype>(params.other_socket().type), SOCK_BOOLEAN)) { + return; + } + + for (const EnumPropertyItem *item = rna_enum_node_boolean_math_items; + item->identifier != nullptr; + item++) { + if (item->name != nullptr && item->identifier[0] != '\0') { + NodeBooleanMathOperation operation = static_cast<NodeBooleanMathOperation>(item->value); + params.add_item(IFACE_(item->name), [operation](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node("FunctionNodeBooleanMath"); + node.custom1 = operation; + params.update_and_connect_available_socket(node, "Boolean"); + }); + } + } +} + +static const fn::MultiFunction *get_multi_function(bNode &bnode) +{ + static fn::CustomMF_SI_SI_SO<bool, bool, bool> and_fn{"And", + [](bool a, bool b) { return a && b; }}; + static fn::CustomMF_SI_SI_SO<bool, bool, bool> or_fn{"Or", + [](bool a, bool b) { return a || b; }}; + static fn::CustomMF_SI_SO<bool, bool> not_fn{"Not", [](bool a) { return !a; }}; + static fn::CustomMF_SI_SI_SO<bool, bool, bool> nand_fn{"Not And", + [](bool a, bool b) { return !(a && b); }}; + static fn::CustomMF_SI_SI_SO<bool, bool, bool> nor_fn{"Nor", + [](bool a, bool b) { return !(a || b); }}; + static fn::CustomMF_SI_SI_SO<bool, bool, bool> xnor_fn{"Equal", + [](bool a, bool b) { return a == b; }}; + static fn::CustomMF_SI_SI_SO<bool, bool, bool> xor_fn{"Not Equal", + [](bool a, bool b) { return a != b; }}; + static fn::CustomMF_SI_SI_SO<bool, bool, bool> imply_fn{"Imply", + [](bool a, bool b) { return !a || b; }}; + static fn::CustomMF_SI_SI_SO<bool, bool, bool> nimply_fn{"Subtract", + [](bool a, bool b) { return a && !b; }}; switch (bnode.custom1) { case NODE_BOOLEAN_MATH_AND: @@ -74,28 +109,44 @@ static const blender::fn::MultiFunction *get_multi_function(bNode &bnode) return &or_fn; case NODE_BOOLEAN_MATH_NOT: return ¬_fn; + case NODE_BOOLEAN_MATH_NAND: + return &nand_fn; + case NODE_BOOLEAN_MATH_NOR: + return &nor_fn; + case NODE_BOOLEAN_MATH_XNOR: + return &xnor_fn; + case NODE_BOOLEAN_MATH_XOR: + return &xor_fn; + case NODE_BOOLEAN_MATH_IMPLY: + return &imply_fn; + case NODE_BOOLEAN_MATH_NIMPLY: + return &nimply_fn; } BLI_assert_unreachable(); return nullptr; } -static void fn_node_boolean_math_build_multi_function( - blender::nodes::NodeMultiFunctionBuilder &builder) +static void fn_node_boolean_math_build_multi_function(NodeMultiFunctionBuilder &builder) { - const blender::fn::MultiFunction *fn = get_multi_function(builder.node()); + const fn::MultiFunction *fn = get_multi_function(builder.node()); builder.set_matching_fn(fn); } +} // namespace blender::nodes::node_fn_boolean_math_cc + void register_node_type_fn_boolean_math() { + namespace file_ns = blender::nodes::node_fn_boolean_math_cc; + static bNodeType ntype; - fn_node_type_base(&ntype, FN_NODE_BOOLEAN_MATH, "Boolean Math", NODE_CLASS_CONVERTER, 0); - ntype.declare = blender::nodes::fn_node_boolean_math_declare; - node_type_label(&ntype, node_boolean_math_label); - node_type_update(&ntype, node_boolean_math_update); - ntype.build_multi_function = fn_node_boolean_math_build_multi_function; - ntype.draw_buttons = fn_node_boolean_math_layout; + fn_node_type_base(&ntype, FN_NODE_BOOLEAN_MATH, "Boolean Math", NODE_CLASS_CONVERTER); + ntype.declare = file_ns::fn_node_boolean_math_declare; + ntype.labelfunc = file_ns::node_boolean_math_label; + node_type_update(&ntype, file_ns::node_boolean_math_update); + ntype.build_multi_function = file_ns::fn_node_boolean_math_build_multi_function; + ntype.draw_buttons = file_ns::fn_node_boolean_math_layout; + ntype.gather_link_search_ops = file_ns::node_gather_link_searches; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/function/nodes/node_fn_compare.cc b/source/blender/nodes/function/nodes/node_fn_compare.cc new file mode 100644 index 00000000000..13a7ff86624 --- /dev/null +++ b/source/blender/nodes/function/nodes/node_fn_compare.cc @@ -0,0 +1,552 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <cmath> + +#include "BLI_listbase.h" +#include "BLI_string.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "RNA_enum_types.h" + +#include "node_function_util.hh" + +#include "NOD_socket_search_link.hh" + +namespace blender::nodes::node_fn_compare_cc { + +NODE_STORAGE_FUNCS(NodeFunctionCompare) + +static void fn_node_compare_declare(NodeDeclarationBuilder &b) +{ + b.is_function_node(); + b.add_input<decl::Float>(N_("A")).min(-10000.0f).max(10000.0f); + b.add_input<decl::Float>(N_("B")).min(-10000.0f).max(10000.0f); + + b.add_input<decl::Int>(N_("A"), "A_INT"); + b.add_input<decl::Int>(N_("B"), "B_INT"); + + b.add_input<decl::Vector>(N_("A"), "A_VEC3"); + b.add_input<decl::Vector>(N_("B"), "B_VEC3"); + + b.add_input<decl::Color>(N_("A"), "A_COL"); + b.add_input<decl::Color>(N_("B"), "B_COL"); + + b.add_input<decl::String>(N_("A"), "A_STR"); + b.add_input<decl::String>(N_("B"), "B_STR"); + + b.add_input<decl::Float>(N_("C")).default_value(0.9f); + b.add_input<decl::Float>(N_("Angle")).default_value(0.0872665f).subtype(PROP_ANGLE); + b.add_input<decl::Float>(N_("Epsilon")).default_value(0.001).min(-10000.0f).max(10000.0f); + + b.add_output<decl::Bool>(N_("Result")); +} + +static void geo_node_compare_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + const NodeFunctionCompare &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, "mode", 0, "", ICON_NONE); + } + uiItemR(layout, ptr, "operation", 0, "", ICON_NONE); +} + +static void node_compare_update(bNodeTree *ntree, bNode *node) +{ + NodeFunctionCompare *data = (NodeFunctionCompare *)node->storage; + + bNodeSocket *sock_comp = (bNodeSocket *)BLI_findlink(&node->inputs, 10); + bNodeSocket *sock_angle = (bNodeSocket *)BLI_findlink(&node->inputs, 11); + bNodeSocket *sock_epsilon = (bNodeSocket *)BLI_findlink(&node->inputs, 12); + + LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) { + nodeSetSocketAvailability(ntree, socket, socket->type == (eNodeSocketDatatype)data->data_type); + } + + nodeSetSocketAvailability(ntree, + sock_epsilon, + ELEM(data->operation, NODE_COMPARE_EQUAL, NODE_COMPARE_NOT_EQUAL) && + !ELEM(data->data_type, SOCK_INT, SOCK_STRING)); + + nodeSetSocketAvailability(ntree, + sock_comp, + ELEM(data->mode, NODE_COMPARE_MODE_DOT_PRODUCT) && + data->data_type == SOCK_VECTOR); + + nodeSetSocketAvailability(ntree, + sock_angle, + ELEM(data->mode, NODE_COMPARE_MODE_DIRECTION) && + data->data_type == SOCK_VECTOR); +} + +static void node_compare_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeFunctionCompare *data = MEM_cnew<NodeFunctionCompare>(__func__); + data->operation = NODE_COMPARE_GREATER_THAN; + data->data_type = SOCK_FLOAT; + data->mode = NODE_COMPARE_MODE_ELEMENT; + node->storage = data; +} + +class SocketSearchOp { + public: + std::string socket_name; + eNodeSocketDatatype data_type; + NodeCompareOperation operation; + NodeCompareMode mode = NODE_COMPARE_MODE_ELEMENT; + void operator()(LinkSearchOpParams ¶ms) + { + bNode &node = params.add_node("FunctionNodeCompare"); + node_storage(node).data_type = data_type; + node_storage(node).operation = operation; + node_storage(node).mode = mode; + params.update_and_connect_available_socket(node, socket_name); + } +}; + +static void node_compare_gather_link_searches(GatherLinkSearchOpParams ¶ms) +{ + const eNodeSocketDatatype type = static_cast<eNodeSocketDatatype>(params.other_socket().type); + if (!ELEM(type, SOCK_BOOLEAN, SOCK_FLOAT, SOCK_RGBA, SOCK_VECTOR, SOCK_INT, SOCK_STRING)) { + return; + } + + const eNodeSocketDatatype mode_type = (type == SOCK_BOOLEAN) ? SOCK_INT : type; + const bool string_type = (type == SOCK_STRING); + + const std::string socket_name = params.in_out() == SOCK_IN ? "A" : "Result"; + + for (const EnumPropertyItem *item = rna_enum_node_compare_operation_items; + item->identifier != nullptr; + item++) { + if (item->name != nullptr && item->identifier[0] != '\0') { + if (!string_type && + ELEM(item->value, NODE_COMPARE_COLOR_BRIGHTER, NODE_COMPARE_COLOR_DARKER)) { + params.add_item(IFACE_(item->name), + SocketSearchOp{socket_name, + SOCK_RGBA, + static_cast<NodeCompareOperation>(item->value)}); + } + else if ((!string_type) || + (string_type && ELEM(item->value, NODE_COMPARE_EQUAL, NODE_COMPARE_NOT_EQUAL))) { + params.add_item(IFACE_(item->name), + SocketSearchOp{socket_name, + mode_type, + static_cast<NodeCompareOperation>(item->value)}); + } + } + } + /* Add Angle socket. */ + if (!string_type && params.in_out() == SOCK_IN) { + params.add_item( + IFACE_("Angle"), + SocketSearchOp{ + "Angle", SOCK_VECTOR, NODE_COMPARE_GREATER_THAN, NODE_COMPARE_MODE_DIRECTION}); + } +} + +static void node_compare_label(const bNodeTree *UNUSED(ntree), + const bNode *node, + char *label, + int maxlen) +{ + const NodeFunctionCompare *data = (NodeFunctionCompare *)node->storage; + const char *name; + bool enum_label = RNA_enum_name(rna_enum_node_compare_operation_items, data->operation, &name); + if (!enum_label) { + name = "Unknown"; + } + BLI_strncpy(label, IFACE_(name), maxlen); +} + +static float component_average(float3 a) +{ + return (a.x + a.y + a.z) / 3.0f; +} + +static const fn::MultiFunction *get_multi_function(bNode &node) +{ + const NodeFunctionCompare *data = (NodeFunctionCompare *)node.storage; + + switch (data->data_type) { + case SOCK_FLOAT: + switch (data->operation) { + case NODE_COMPARE_LESS_THAN: { + static fn::CustomMF_SI_SI_SO<float, float, bool> fn{ + "Less Than", [](float a, float b) { return a < b; }}; + return &fn; + } + case NODE_COMPARE_LESS_EQUAL: { + static fn::CustomMF_SI_SI_SO<float, float, bool> fn{ + "Less Equal", [](float a, float b) { return a <= b; }}; + return &fn; + } + case NODE_COMPARE_GREATER_THAN: { + static fn::CustomMF_SI_SI_SO<float, float, bool> fn{ + "Greater Than", [](float a, float b) { return a > b; }}; + return &fn; + } + case NODE_COMPARE_GREATER_EQUAL: { + static fn::CustomMF_SI_SI_SO<float, float, bool> fn{ + "Greater Equal", [](float a, float b) { return a >= b; }}; + return &fn; + } + case NODE_COMPARE_EQUAL: { + static fn::CustomMF_SI_SI_SI_SO<float, float, float, bool> fn{ + "Equal", [](float a, float b, float epsilon) { return std::abs(a - b) <= epsilon; }}; + return &fn; + } + case NODE_COMPARE_NOT_EQUAL: + static fn::CustomMF_SI_SI_SI_SO<float, float, float, bool> fn{ + "Not Equal", + [](float a, float b, float epsilon) { return std::abs(a - b) > epsilon; }}; + return &fn; + } + break; + case SOCK_INT: + switch (data->operation) { + case NODE_COMPARE_LESS_THAN: { + static fn::CustomMF_SI_SI_SO<int, int, bool> fn{"Less Than", + [](int a, int b) { return a < b; }}; + return &fn; + } + case NODE_COMPARE_LESS_EQUAL: { + static fn::CustomMF_SI_SI_SO<int, int, bool> fn{"Less Equal", + [](int a, int b) { return a <= b; }}; + return &fn; + } + case NODE_COMPARE_GREATER_THAN: { + static fn::CustomMF_SI_SI_SO<int, int, bool> fn{"Greater Than", + [](int a, int b) { return a > b; }}; + return &fn; + } + case NODE_COMPARE_GREATER_EQUAL: { + static fn::CustomMF_SI_SI_SO<int, int, bool> fn{"Greater Equal", + [](int a, int b) { return a >= b; }}; + return &fn; + } + case NODE_COMPARE_EQUAL: { + static fn::CustomMF_SI_SI_SO<int, int, bool> fn{"Equal", + [](int a, int b) { return a == b; }}; + return &fn; + } + case NODE_COMPARE_NOT_EQUAL: { + static fn::CustomMF_SI_SI_SO<int, int, bool> fn{"Not Equal", + [](int a, int b) { return a != b; }}; + return &fn; + } + } + break; + case SOCK_VECTOR: + switch (data->operation) { + case NODE_COMPARE_LESS_THAN: + switch (data->mode) { + case NODE_COMPARE_MODE_AVERAGE: { + static fn::CustomMF_SI_SI_SO<float3, float3, bool> fn{ + "Less Than - Average", + [](float3 a, float3 b) { return component_average(a) < component_average(b); }}; + return &fn; + } + case NODE_COMPARE_MODE_DOT_PRODUCT: { + static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, bool> fn{ + "Less Than - Dot Product", + [](float3 a, float3 b, float comp) { return math::dot(a, b) < comp; }}; + return &fn; + } + case NODE_COMPARE_MODE_DIRECTION: { + static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, bool> fn{ + "Less Than - Direction", + [](float3 a, float3 b, float angle) { return angle_v3v3(a, b) < angle; }}; + return &fn; + } + case NODE_COMPARE_MODE_ELEMENT: { + static fn::CustomMF_SI_SI_SO<float3, float3, bool> fn{ + "Less Than - Element-wise", + [](float3 a, float3 b) { return a.x < b.x && a.y < b.y && a.z < b.z; }}; + return &fn; + } + case NODE_COMPARE_MODE_LENGTH: { + static fn::CustomMF_SI_SI_SO<float3, float3, bool> fn{ + "Less Than - Length", + [](float3 a, float3 b) { return math::length(a) < math::length(b); }}; + return &fn; + } + } + break; + case NODE_COMPARE_LESS_EQUAL: + switch (data->mode) { + case NODE_COMPARE_MODE_AVERAGE: { + static fn::CustomMF_SI_SI_SO<float3, float3, bool> fn{ + "Less Equal - Average", + [](float3 a, float3 b) { return component_average(a) <= component_average(b); }}; + return &fn; + } + case NODE_COMPARE_MODE_DOT_PRODUCT: { + static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, bool> fn{ + "Less Equal - Dot Product", + [](float3 a, float3 b, float comp) { return math::dot(a, b) <= comp; }}; + return &fn; + } + case NODE_COMPARE_MODE_DIRECTION: { + static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, bool> fn{ + "Less Equal - Direction", + [](float3 a, float3 b, float angle) { return angle_v3v3(a, b) <= angle; }}; + return &fn; + } + case NODE_COMPARE_MODE_ELEMENT: { + static fn::CustomMF_SI_SI_SO<float3, float3, bool> fn{ + "Less Equal - Element-wise", + [](float3 a, float3 b) { return a.x <= b.x && a.y <= b.y && a.z <= b.z; }}; + return &fn; + } + case NODE_COMPARE_MODE_LENGTH: { + static fn::CustomMF_SI_SI_SO<float3, float3, bool> fn{ + "Less Equal - Length", + [](float3 a, float3 b) { return math::length(a) <= math::length(b); }}; + return &fn; + } + } + break; + case NODE_COMPARE_GREATER_THAN: + switch (data->mode) { + case NODE_COMPARE_MODE_AVERAGE: { + static fn::CustomMF_SI_SI_SO<float3, float3, bool> fn{ + "Greater Than - Average", + [](float3 a, float3 b) { return component_average(a) > component_average(b); }}; + return &fn; + } + case NODE_COMPARE_MODE_DOT_PRODUCT: { + static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, bool> fn{ + "Greater Than - Dot Product", + [](float3 a, float3 b, float comp) { return math::dot(a, b) > comp; }}; + return &fn; + } + case NODE_COMPARE_MODE_DIRECTION: { + static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, bool> fn{ + "Greater Than - Direction", + [](float3 a, float3 b, float angle) { return angle_v3v3(a, b) > angle; }}; + return &fn; + } + case NODE_COMPARE_MODE_ELEMENT: { + static fn::CustomMF_SI_SI_SO<float3, float3, bool> fn{ + "Greater Than - Element-wise", + [](float3 a, float3 b) { return a.x > b.x && a.y > b.y && a.z > b.z; }}; + return &fn; + } + case NODE_COMPARE_MODE_LENGTH: { + static fn::CustomMF_SI_SI_SO<float3, float3, bool> fn{ + "Greater Than - Length", + [](float3 a, float3 b) { return math::length(a) > math::length(b); }}; + return &fn; + } + } + break; + case NODE_COMPARE_GREATER_EQUAL: + switch (data->mode) { + case NODE_COMPARE_MODE_AVERAGE: { + static fn::CustomMF_SI_SI_SO<float3, float3, bool> fn{ + "Greater Equal - Average", + [](float3 a, float3 b) { return component_average(a) >= component_average(b); }}; + return &fn; + } + case NODE_COMPARE_MODE_DOT_PRODUCT: { + static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, bool> fn{ + "Greater Equal - Dot Product", + [](float3 a, float3 b, float comp) { return math::dot(a, b) >= comp; }}; + return &fn; + } + case NODE_COMPARE_MODE_DIRECTION: { + static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, bool> fn{ + "Greater Equal - Direction", + [](float3 a, float3 b, float angle) { return angle_v3v3(a, b) >= angle; }}; + return &fn; + } + case NODE_COMPARE_MODE_ELEMENT: { + static fn::CustomMF_SI_SI_SO<float3, float3, bool> fn{ + "Greater Equal - Element-wise", + [](float3 a, float3 b) { return a.x >= b.x && a.y >= b.y && a.z >= b.z; }}; + return &fn; + } + case NODE_COMPARE_MODE_LENGTH: { + static fn::CustomMF_SI_SI_SO<float3, float3, bool> fn{ + "Greater Equal - Length", + [](float3 a, float3 b) { return math::length(a) >= math::length(b); }}; + return &fn; + } + } + break; + case NODE_COMPARE_EQUAL: + switch (data->mode) { + case NODE_COMPARE_MODE_AVERAGE: { + static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, bool> fn{ + "Equal - Average", [](float3 a, float3 b, float epsilon) { + return abs(component_average(a) - component_average(b)) <= epsilon; + }}; + return &fn; + } + case NODE_COMPARE_MODE_DOT_PRODUCT: { + static fn::CustomMF_SI_SI_SI_SI_SO<float3, float3, float, float, bool> fn{ + "Equal - Dot Product", [](float3 a, float3 b, float comp, float epsilon) { + return abs(math::dot(a, b) - comp) <= epsilon; + }}; + return &fn; + } + case NODE_COMPARE_MODE_DIRECTION: { + static fn::CustomMF_SI_SI_SI_SI_SO<float3, float3, float, float, bool> fn{ + "Equal - Direction", [](float3 a, float3 b, float angle, float epsilon) { + return abs(angle_v3v3(a, b) - angle) <= epsilon; + }}; + return &fn; + } + case NODE_COMPARE_MODE_ELEMENT: { + static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, bool> fn{ + "Equal - Element-wise", [](float3 a, float3 b, float epsilon) { + return abs(a.x - b.x) <= epsilon && abs(a.y - b.y) <= epsilon && + abs(a.z - b.z) <= epsilon; + }}; + return &fn; + } + case NODE_COMPARE_MODE_LENGTH: { + static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, bool> fn{ + "Equal - Length", [](float3 a, float3 b, float epsilon) { + return abs(math::length(a) - math::length(b)) <= epsilon; + }}; + return &fn; + } + } + break; + case NODE_COMPARE_NOT_EQUAL: + switch (data->mode) { + case NODE_COMPARE_MODE_AVERAGE: { + static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, bool> fn{ + "Not Equal - Average", [](float3 a, float3 b, float epsilon) { + return abs(component_average(a) - component_average(b)) > epsilon; + }}; + return &fn; + } + case NODE_COMPARE_MODE_DOT_PRODUCT: { + static fn::CustomMF_SI_SI_SI_SI_SO<float3, float3, float, float, bool> fn{ + "Not Equal - Dot Product", [](float3 a, float3 b, float comp, float epsilon) { + return abs(math::dot(a, b) - comp) >= epsilon; + }}; + return &fn; + } + case NODE_COMPARE_MODE_DIRECTION: { + static fn::CustomMF_SI_SI_SI_SI_SO<float3, float3, float, float, bool> fn{ + "Not Equal - Direction", [](float3 a, float3 b, float angle, float epsilon) { + return abs(angle_v3v3(a, b) - angle) > epsilon; + }}; + return &fn; + } + case NODE_COMPARE_MODE_ELEMENT: { + static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, bool> fn{ + "Not Equal - Element-wise", [](float3 a, float3 b, float epsilon) { + return abs(a.x - b.x) > epsilon && abs(a.y - b.y) > epsilon && + abs(a.z - b.z) > epsilon; + }}; + return &fn; + } + case NODE_COMPARE_MODE_LENGTH: { + static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, bool> fn{ + "Not Equal - Length", [](float3 a, float3 b, float epsilon) { + return abs(math::length(a) - math::length(b)) > epsilon; + }}; + return &fn; + } + } + break; + } + break; + case SOCK_RGBA: + switch (data->operation) { + case NODE_COMPARE_EQUAL: { + static fn::CustomMF_SI_SI_SI_SO<ColorGeometry4f, ColorGeometry4f, float, bool> fn{ + "Equal", [](ColorGeometry4f a, ColorGeometry4f b, float epsilon) { + return abs(a.r - b.r) <= epsilon && abs(a.g - b.g) <= epsilon && + abs(a.b - b.b) <= epsilon; + }}; + return &fn; + } + case NODE_COMPARE_NOT_EQUAL: { + static fn::CustomMF_SI_SI_SI_SO<ColorGeometry4f, ColorGeometry4f, float, bool> fn{ + "Not Equal", [](ColorGeometry4f a, ColorGeometry4f b, float epsilon) { + return abs(a.r - b.r) > epsilon && abs(a.g - b.g) > epsilon && + abs(a.b - b.b) > epsilon; + }}; + return &fn; + } + case NODE_COMPARE_COLOR_BRIGHTER: { + static fn::CustomMF_SI_SI_SO<ColorGeometry4f, ColorGeometry4f, bool> fn{ + "Brighter", [](ColorGeometry4f a, ColorGeometry4f b) { + return rgb_to_grayscale(a) > rgb_to_grayscale(b); + }}; + return &fn; + } + case NODE_COMPARE_COLOR_DARKER: { + static fn::CustomMF_SI_SI_SO<ColorGeometry4f, ColorGeometry4f, bool> fn{ + "Darker", [](ColorGeometry4f a, ColorGeometry4f b) { + return rgb_to_grayscale(a) < rgb_to_grayscale(b); + }}; + return &fn; + } + } + break; + case SOCK_STRING: + switch (data->operation) { + case NODE_COMPARE_EQUAL: { + static fn::CustomMF_SI_SI_SO<std::string, std::string, bool> fn{ + "Equal", [](std::string a, std::string b) { return a == b; }}; + return &fn; + } + case NODE_COMPARE_NOT_EQUAL: { + static fn::CustomMF_SI_SI_SO<std::string, std::string, bool> fn{ + "Not Equal", [](std::string a, std::string b) { return a != b; }}; + return &fn; + } + } + break; + } + return nullptr; +} + +static void fn_node_compare_build_multi_function(NodeMultiFunctionBuilder &builder) +{ + const fn::MultiFunction *fn = get_multi_function(builder.node()); + builder.set_matching_fn(fn); +} + +} // namespace blender::nodes::node_fn_compare_cc + +void register_node_type_fn_compare() +{ + namespace file_ns = blender::nodes::node_fn_compare_cc; + + static bNodeType ntype; + fn_node_type_base(&ntype, FN_NODE_COMPARE, "Compare", NODE_CLASS_CONVERTER); + ntype.declare = file_ns::fn_node_compare_declare; + ntype.labelfunc = file_ns::node_compare_label; + node_type_update(&ntype, file_ns::node_compare_update); + node_type_init(&ntype, file_ns::node_compare_init); + node_type_storage( + &ntype, "NodeFunctionCompare", node_free_standard_storage, node_copy_standard_storage); + ntype.build_multi_function = file_ns::fn_node_compare_build_multi_function; + ntype.draw_buttons = file_ns::geo_node_compare_layout; + ntype.gather_link_search_ops = file_ns::node_compare_gather_link_searches; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/function/nodes/node_fn_float_compare.cc b/source/blender/nodes/function/nodes/node_fn_float_compare.cc deleted file mode 100644 index 9736c52e895..00000000000 --- a/source/blender/nodes/function/nodes/node_fn_float_compare.cc +++ /dev/null @@ -1,120 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include <cmath> - -#include "BLI_listbase.h" -#include "BLI_string.h" - -#include "RNA_enum_types.h" - -#include "UI_interface.h" -#include "UI_resources.h" - -#include "node_function_util.hh" - -namespace blender::nodes { - -static void fn_node_float_compare_declare(NodeDeclarationBuilder &b) -{ - b.is_function_node(); - b.add_input<decl::Float>("A").min(-10000.0f).max(10000.0f); - b.add_input<decl::Float>("B").min(-10000.0f).max(10000.0f); - b.add_input<decl::Float>("Epsilon").default_value(0.001f).min(-10000.0f).max(10000.0f); - b.add_output<decl::Bool>("Result"); -}; - -} // namespace blender::nodes - -static void geo_node_float_compare_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiItemR(layout, ptr, "operation", 0, "", ICON_NONE); -} - -static void node_float_compare_update(bNodeTree *UNUSED(ntree), bNode *node) -{ - bNodeSocket *sockEpsilon = (bNodeSocket *)BLI_findlink(&node->inputs, 2); - - nodeSetSocketAvailability( - sockEpsilon, ELEM(node->custom1, NODE_FLOAT_COMPARE_EQUAL, NODE_FLOAT_COMPARE_NOT_EQUAL)); -} - -static void node_float_compare_label(bNodeTree *UNUSED(ntree), - bNode *node, - char *label, - int maxlen) -{ - const char *name; - bool enum_label = RNA_enum_name(rna_enum_node_float_compare_items, node->custom1, &name); - if (!enum_label) { - name = "Unknown"; - } - BLI_strncpy(label, IFACE_(name), maxlen); -} - -static const blender::fn::MultiFunction *get_multi_function(bNode &node) -{ - static blender::fn::CustomMF_SI_SI_SO<float, float, bool> less_than_fn{ - "Less Than", [](float a, float b) { return a < b; }}; - static blender::fn::CustomMF_SI_SI_SO<float, float, bool> less_equal_fn{ - "Less Equal", [](float a, float b) { return a <= b; }}; - static blender::fn::CustomMF_SI_SI_SO<float, float, bool> greater_than_fn{ - "Greater Than", [](float a, float b) { return a > b; }}; - static blender::fn::CustomMF_SI_SI_SO<float, float, bool> greater_equal_fn{ - "Greater Equal", [](float a, float b) { return a >= b; }}; - static blender::fn::CustomMF_SI_SI_SI_SO<float, float, float, bool> equal_fn{ - "Equal", [](float a, float b, float epsilon) { return std::abs(a - b) <= epsilon; }}; - static blender::fn::CustomMF_SI_SI_SI_SO<float, float, float, bool> not_equal_fn{ - "Not Equal", [](float a, float b, float epsilon) { return std::abs(a - b) > epsilon; }}; - - switch (node.custom1) { - case NODE_FLOAT_COMPARE_LESS_THAN: - return &less_than_fn; - case NODE_FLOAT_COMPARE_LESS_EQUAL: - return &less_equal_fn; - case NODE_FLOAT_COMPARE_GREATER_THAN: - return &greater_than_fn; - case NODE_FLOAT_COMPARE_GREATER_EQUAL: - return &greater_equal_fn; - case NODE_FLOAT_COMPARE_EQUAL: - return &equal_fn; - case NODE_FLOAT_COMPARE_NOT_EQUAL: - return ¬_equal_fn; - } - - BLI_assert_unreachable(); - return nullptr; -} - -static void fn_node_float_compare_build_multi_function( - blender::nodes::NodeMultiFunctionBuilder &builder) -{ - const blender::fn::MultiFunction *fn = get_multi_function(builder.node()); - builder.set_matching_fn(fn); -} - -void register_node_type_fn_float_compare() -{ - static bNodeType ntype; - - fn_node_type_base(&ntype, FN_NODE_FLOAT_COMPARE, "Float Compare", NODE_CLASS_CONVERTER, 0); - ntype.declare = blender::nodes::fn_node_float_compare_declare; - node_type_label(&ntype, node_float_compare_label); - node_type_update(&ntype, node_float_compare_update); - ntype.build_multi_function = fn_node_float_compare_build_multi_function; - ntype.draw_buttons = geo_node_float_compare_layout; - nodeRegisterType(&ntype); -} 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 8bb5dafff8a..488787980dc 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 @@ -25,23 +25,24 @@ #include "node_function_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_fn_float_to_int_cc { static void fn_node_float_to_int_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input<decl::Float>("Float"); - b.add_output<decl::Int>("Integer"); -}; - -} // namespace blender::nodes + b.add_input<decl::Float>(N_("Float")); + b.add_output<decl::Int>(N_("Integer")); +} static void fn_node_float_to_int_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "rounding_mode", 0, "", ICON_NONE); } -static void node_float_to_int_label(bNodeTree *UNUSED(ntree), bNode *node, char *label, int maxlen) +static void node_float_to_int_label(const bNodeTree *UNUSED(ntree), + const bNode *node, + char *label, + int maxlen) { const char *name; bool enum_label = RNA_enum_name(rna_enum_node_float_to_int_items, node->custom1, &name); @@ -51,16 +52,13 @@ static void node_float_to_int_label(bNodeTree *UNUSED(ntree), bNode *node, char BLI_strncpy(label, IFACE_(name), maxlen); } -static const blender::fn::MultiFunction *get_multi_function(bNode &bnode) +static const fn::MultiFunction *get_multi_function(bNode &bnode) { - static blender::fn::CustomMF_SI_SO<float, int> round_fn{"Round", - [](float a) { return (int)round(a); }}; - static blender::fn::CustomMF_SI_SO<float, int> floor_fn{"Floor", - [](float a) { return (int)floor(a); }}; - static blender::fn::CustomMF_SI_SO<float, int> ceil_fn{"Ceiling", - [](float a) { return (int)ceil(a); }}; - static blender::fn::CustomMF_SI_SO<float, int> trunc_fn{"Truncate", - [](float a) { return (int)trunc(a); }}; + static fn::CustomMF_SI_SO<float, int> round_fn{"Round", [](float a) { return (int)round(a); }}; + static fn::CustomMF_SI_SO<float, int> floor_fn{"Floor", [](float a) { return (int)floor(a); }}; + static fn::CustomMF_SI_SO<float, int> ceil_fn{"Ceiling", [](float a) { return (int)ceil(a); }}; + static fn::CustomMF_SI_SO<float, int> trunc_fn{"Truncate", + [](float a) { return (int)trunc(a); }}; switch (static_cast<FloatToIntRoundingMode>(bnode.custom1)) { case FN_NODE_FLOAT_TO_INT_ROUND: @@ -77,21 +75,24 @@ static const blender::fn::MultiFunction *get_multi_function(bNode &bnode) return nullptr; } -static void fn_node_float_to_int_build_multi_function( - blender::nodes::NodeMultiFunctionBuilder &builder) +static void fn_node_float_to_int_build_multi_function(NodeMultiFunctionBuilder &builder) { - const blender::fn::MultiFunction *fn = get_multi_function(builder.node()); + const fn::MultiFunction *fn = get_multi_function(builder.node()); builder.set_matching_fn(fn); } +} // namespace blender::nodes::node_fn_float_to_int_cc + void register_node_type_fn_float_to_int() { + namespace file_ns = blender::nodes::node_fn_float_to_int_cc; + static bNodeType ntype; - fn_node_type_base(&ntype, FN_NODE_FLOAT_TO_INT, "Float to Integer", NODE_CLASS_CONVERTER, 0); - ntype.declare = blender::nodes::fn_node_float_to_int_declare; - node_type_label(&ntype, node_float_to_int_label); - ntype.build_multi_function = fn_node_float_to_int_build_multi_function; - ntype.draw_buttons = fn_node_float_to_int_layout; + fn_node_type_base(&ntype, FN_NODE_FLOAT_TO_INT, "Float to Integer", NODE_CLASS_CONVERTER); + ntype.declare = file_ns::fn_node_float_to_int_declare; + ntype.labelfunc = file_ns::node_float_to_int_label; + ntype.build_multi_function = file_ns::fn_node_float_to_int_build_multi_function; + ntype.draw_buttons = file_ns::fn_node_float_to_int_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/function/nodes/node_fn_input_bool.cc b/source/blender/nodes/function/nodes/node_fn_input_bool.cc new file mode 100644 index 00000000000..583570effd9 --- /dev/null +++ b/source/blender/nodes/function/nodes/node_fn_input_bool.cc @@ -0,0 +1,66 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_function_util.hh" + +#include "BLI_hash.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes::node_fn_input_bool_cc { + +static void fn_node_input_bool_declare(NodeDeclarationBuilder &b) +{ + b.add_output<decl::Bool>(N_("Boolean")); +} + +static void fn_node_input_bool_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayout *col = uiLayoutColumn(layout, true); + uiItemR(col, ptr, "boolean", UI_ITEM_R_EXPAND, IFACE_("Value"), ICON_NONE); +} + +static void fn_node_input_bool_build_multi_function(NodeMultiFunctionBuilder &builder) +{ + 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); +} + +static void fn_node_input_bool_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeInputBool *data = MEM_cnew<NodeInputBool>(__func__); + node->storage = data; +} + +} // namespace blender::nodes::node_fn_input_bool_cc + +void register_node_type_fn_input_bool() +{ + namespace file_ns = blender::nodes::node_fn_input_bool_cc; + + static bNodeType ntype; + + fn_node_type_base(&ntype, FN_NODE_INPUT_BOOL, "Boolean", 0); + ntype.declare = file_ns::fn_node_input_bool_declare; + node_type_init(&ntype, file_ns::fn_node_input_bool_init); + node_type_storage( + &ntype, "NodeInputBool", node_free_standard_storage, node_copy_standard_storage); + ntype.build_multi_function = file_ns::fn_node_input_bool_build_multi_function; + ntype.draw_buttons = file_ns::fn_node_input_bool_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/function/nodes/node_fn_input_color.cc b/source/blender/nodes/function/nodes/node_fn_input_color.cc new file mode 100644 index 00000000000..1fad5b2f5f4 --- /dev/null +++ b/source/blender/nodes/function/nodes/node_fn_input_color.cc @@ -0,0 +1,67 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_function_util.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes::node_fn_input_color_cc { + +static void fn_node_input_color_declare(NodeDeclarationBuilder &b) +{ + b.add_output<decl::Color>(N_("Color")); +} + +static void fn_node_input_color_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiTemplateColorPicker(layout, ptr, "color", true, false, false, true); + uiItemR(layout, ptr, "color", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); +} + +static void fn_node_input_color_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) +{ + 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); +} + +static void fn_node_input_color_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeInputColor *data = MEM_cnew<NodeInputColor>(__func__); + copy_v4_fl4(data->color, 0.5f, 0.5f, 0.5f, 1.0f); + node->storage = data; +} + +} // namespace blender::nodes::node_fn_input_color_cc + +void register_node_type_fn_input_color() +{ + namespace file_ns = blender::nodes::node_fn_input_color_cc; + + static bNodeType ntype; + + fn_node_type_base(&ntype, FN_NODE_INPUT_COLOR, "Color", NODE_CLASS_INPUT); + ntype.declare = file_ns::fn_node_input_color_declare; + node_type_init(&ntype, file_ns::fn_node_input_color_init); + node_type_storage( + &ntype, "NodeInputColor", node_free_standard_storage, node_copy_standard_storage); + ntype.build_multi_function = file_ns::fn_node_input_color_build_multi_function; + ntype.draw_buttons = file_ns::fn_node_input_color_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/function/nodes/node_fn_input_int.cc b/source/blender/nodes/function/nodes/node_fn_input_int.cc new file mode 100644 index 00000000000..dc30ecb253c --- /dev/null +++ b/source/blender/nodes/function/nodes/node_fn_input_int.cc @@ -0,0 +1,66 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_function_util.hh" + +#include "BLI_hash.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes::node_fn_input_int_cc { + +static void fn_node_input_int_declare(NodeDeclarationBuilder &b) +{ + b.add_output<decl::Int>(N_("Integer")); +} + +static void fn_node_input_int_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayout *col = uiLayoutColumn(layout, true); + uiItemR(col, ptr, "integer", UI_ITEM_R_EXPAND, "", ICON_NONE); +} + +static void fn_node_input_int_build_multi_function(NodeMultiFunctionBuilder &builder) +{ + 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); +} + +static void fn_node_input_int_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeInputInt *data = MEM_cnew<NodeInputInt>(__func__); + node->storage = data; +} + +} // namespace blender::nodes::node_fn_input_int_cc + +void register_node_type_fn_input_int() +{ + namespace file_ns = blender::nodes::node_fn_input_int_cc; + + static bNodeType ntype; + + fn_node_type_base(&ntype, FN_NODE_INPUT_INT, "Integer", 0); + ntype.declare = file_ns::fn_node_input_int_declare; + node_type_init(&ntype, file_ns::fn_node_input_int_init); + node_type_storage( + &ntype, "NodeInputInt", node_free_standard_storage, node_copy_standard_storage); + ntype.build_multi_function = file_ns::fn_node_input_int_build_multi_function; + ntype.draw_buttons = file_ns::fn_node_input_int_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/function/nodes/node_fn_input_special_characters.cc b/source/blender/nodes/function/nodes/node_fn_input_special_characters.cc index 11c64d3f694..681bc16a301 100644 --- a/source/blender/nodes/function/nodes/node_fn_input_special_characters.cc +++ b/source/blender/nodes/function/nodes/node_fn_input_special_characters.cc @@ -16,13 +16,13 @@ #include "node_function_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_fn_input_special_characters_cc { static void fn_node_input_special_characters_declare(NodeDeclarationBuilder &b) { - b.add_output<decl::String>("Line Break"); - b.add_output<decl::String>("Tab"); -}; + b.add_output<decl::String>(N_("Line Break")); + b.add_output<decl::String>(N_("Tab")); +} class MF_SpecialCharacters : public fn::MultiFunction { public: @@ -59,16 +59,17 @@ static void fn_node_input_special_characters_build_multi_function( builder.set_matching_fn(special_characters_fn); } -} // namespace blender::nodes +} // namespace blender::nodes::node_fn_input_special_characters_cc void register_node_type_fn_input_special_characters() { + namespace file_ns = blender::nodes::node_fn_input_special_characters_cc; + static bNodeType ntype; fn_node_type_base( - &ntype, FN_NODE_INPUT_SPECIAL_CHARACTERS, "Special Characters", NODE_CLASS_INPUT, 0); - ntype.declare = blender::nodes::fn_node_input_special_characters_declare; - ntype.build_multi_function = - blender::nodes::fn_node_input_special_characters_build_multi_function; + &ntype, FN_NODE_INPUT_SPECIAL_CHARACTERS, "Special Characters", NODE_CLASS_INPUT); + ntype.declare = file_ns::fn_node_input_special_characters_declare; + ntype.build_multi_function = file_ns::fn_node_input_special_characters_build_multi_function; nodeRegisterType(&ntype); } 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 704ae9d900c..4abb352d802 100644 --- a/source/blender/nodes/function/nodes/node_fn_input_string.cc +++ b/source/blender/nodes/function/nodes/node_fn_input_string.cc @@ -19,29 +19,25 @@ #include "UI_interface.h" #include "UI_resources.h" -namespace blender::nodes { +namespace blender::nodes::node_fn_input_string_cc { static void fn_node_input_string_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_output<decl::String>("String"); -}; - -} // namespace blender::nodes + b.add_output<decl::String>(N_("String")); +} static void fn_node_input_string_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "string", 0, "", ICON_NONE); } -static void fn_node_input_string_build_multi_function( - blender::nodes::NodeMultiFunctionBuilder &builder) +static void fn_node_input_string_build_multi_function(NodeMultiFunctionBuilder &builder) { 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<blender::fn::CustomMF_Constant<std::string>>( - std::move(string)); + builder.construct_and_set_matching_fn<fn::CustomMF_Constant<std::string>>(std::move(string)); } static void fn_node_input_string_init(bNodeTree *UNUSED(ntree), bNode *node) @@ -75,15 +71,20 @@ static void fn_node_string_copy(bNodeTree *UNUSED(dest_ntree), dest_node->storage = destination_storage; } +} // namespace blender::nodes::node_fn_input_string_cc + void register_node_type_fn_input_string() { + namespace file_ns = blender::nodes::node_fn_input_string_cc; + static bNodeType ntype; - fn_node_type_base(&ntype, FN_NODE_INPUT_STRING, "String", NODE_CLASS_INPUT, 0); - ntype.declare = blender::nodes::fn_node_input_string_declare; - node_type_init(&ntype, fn_node_input_string_init); - node_type_storage(&ntype, "NodeInputString", fn_node_input_string_free, fn_node_string_copy); - ntype.build_multi_function = fn_node_input_string_build_multi_function; - ntype.draw_buttons = fn_node_input_string_layout; + fn_node_type_base(&ntype, FN_NODE_INPUT_STRING, "String", NODE_CLASS_INPUT); + ntype.declare = file_ns::fn_node_input_string_declare; + node_type_init(&ntype, file_ns::fn_node_input_string_init); + node_type_storage( + &ntype, "NodeInputString", file_ns::fn_node_input_string_free, file_ns::fn_node_string_copy); + ntype.build_multi_function = file_ns::fn_node_input_string_build_multi_function; + ntype.draw_buttons = file_ns::fn_node_input_string_layout; nodeRegisterType(&ntype); } 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 9548df7b423..ba9600b461c 100644 --- a/source/blender/nodes/function/nodes/node_fn_input_vector.cc +++ b/source/blender/nodes/function/nodes/node_fn_input_vector.cc @@ -21,14 +21,12 @@ #include "UI_interface.h" #include "UI_resources.h" -namespace blender::nodes { +namespace blender::nodes::node_fn_input_vector_cc { static void fn_node_input_vector_declare(NodeDeclarationBuilder &b) { - b.add_output<decl::Vector>("Vector"); -}; - -} // namespace blender::nodes + b.add_output<decl::Vector>(N_("Vector")); +} static void fn_node_input_vector_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { @@ -36,31 +34,34 @@ static void fn_node_input_vector_layout(uiLayout *layout, bContext *UNUSED(C), P uiItemR(col, ptr, "vector", UI_ITEM_R_EXPAND, "", ICON_NONE); } -static void fn_node_vector_input_build_multi_function( - blender::nodes::NodeMultiFunctionBuilder &builder) +static void fn_node_input_vector_build_multi_function(NodeMultiFunctionBuilder &builder) { bNode &bnode = builder.node(); NodeInputVector *node_storage = static_cast<NodeInputVector *>(bnode.storage); - blender::float3 vector(node_storage->vector); - builder.construct_and_set_matching_fn<blender::fn::CustomMF_Constant<blender::float3>>(vector); + float3 vector(node_storage->vector); + builder.construct_and_set_matching_fn<fn::CustomMF_Constant<float3>>(vector); } + static void fn_node_input_vector_init(bNodeTree *UNUSED(ntree), bNode *node) { - NodeInputVector *data = (NodeInputVector *)MEM_callocN(sizeof(NodeInputVector), - "input vector node"); + NodeInputVector *data = MEM_cnew<NodeInputVector>(__func__); node->storage = data; } +} // namespace blender::nodes::node_fn_input_vector_cc + void register_node_type_fn_input_vector() { + namespace file_ns = blender::nodes::node_fn_input_vector_cc; + static bNodeType ntype; - fn_node_type_base(&ntype, FN_NODE_INPUT_VECTOR, "Vector", 0, 0); - ntype.declare = blender::nodes::fn_node_input_vector_declare; - node_type_init(&ntype, fn_node_input_vector_init); + fn_node_type_base(&ntype, FN_NODE_INPUT_VECTOR, "Vector", 0); + ntype.declare = file_ns::fn_node_input_vector_declare; + node_type_init(&ntype, file_ns::fn_node_input_vector_init); node_type_storage( &ntype, "NodeInputVector", node_free_standard_storage, node_copy_standard_storage); - ntype.build_multi_function = fn_node_vector_input_build_multi_function; - ntype.draw_buttons = fn_node_input_vector_layout; + ntype.build_multi_function = file_ns::fn_node_input_vector_build_multi_function; + ntype.draw_buttons = file_ns::fn_node_input_vector_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/function/nodes/node_fn_random_value.cc b/source/blender/nodes/function/nodes/node_fn_random_value.cc index 53ca77aab0c..ceea6246cb0 100644 --- a/source/blender/nodes/function/nodes/node_fn_random_value.cc +++ b/source/blender/nodes/function/nodes/node_fn_random_value.cc @@ -19,36 +19,41 @@ #include "node_function_util.hh" +#include "NOD_socket_search_link.hh" + #include "UI_interface.h" #include "UI_resources.h" -namespace blender::nodes { +namespace blender::nodes::node_fn_random_value_cc { + +NODE_STORAGE_FUNCS(NodeRandomValue) static void fn_node_random_value_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Vector>("Min").supports_field(); - b.add_input<decl::Vector>("Max").default_value({1.0f, 1.0f, 1.0f}).supports_field(); - b.add_input<decl::Float>("Min", "Min_001").supports_field(); - b.add_input<decl::Float>("Max", "Max_001").default_value(1.0f).supports_field(); - b.add_input<decl::Int>("Min", "Min_002").min(-100000).max(100000).supports_field(); - b.add_input<decl::Int>("Max", "Max_002") + b.add_input<decl::Vector>(N_("Min")).supports_field(); + b.add_input<decl::Vector>(N_("Max")).default_value({1.0f, 1.0f, 1.0f}).supports_field(); + b.add_input<decl::Float>(N_("Min"), "Min_001").supports_field(); + b.add_input<decl::Float>(N_("Max"), "Max_001").default_value(1.0f).supports_field(); + b.add_input<decl::Int>(N_("Min"), "Min_002").min(-100000).max(100000).supports_field(); + b.add_input<decl::Int>(N_("Max"), "Max_002") .default_value(100) .min(-100000) .max(100000) .supports_field(); - b.add_input<decl::Float>("Probability") + b.add_input<decl::Float>(N_("Probability")) .min(0.0f) .max(1.0f) .default_value(0.5f) .subtype(PROP_FACTOR) - .supports_field(); - b.add_input<decl::Int>("ID").implicit_field(); - b.add_input<decl::Int>("Seed").default_value(0).min(-10000).max(10000).supports_field(); - - b.add_output<decl::Vector>("Value").dependent_field(); - b.add_output<decl::Float>("Value", "Value_001").dependent_field(); - b.add_output<decl::Int>("Value", "Value_002").dependent_field(); - b.add_output<decl::Bool>("Value", "Value_003").dependent_field(); + .supports_field() + .make_available([](bNode &node) { node_storage(node).data_type = CD_PROP_BOOL; }); + b.add_input<decl::Int>(N_("ID")).implicit_field(); + b.add_input<decl::Int>(N_("Seed")).default_value(0).min(-10000).max(10000).supports_field(); + + b.add_output<decl::Vector>(N_("Value")).dependent_field(); + b.add_output<decl::Float>(N_("Value"), "Value_001").dependent_field(); + b.add_output<decl::Int>(N_("Value"), "Value_002").dependent_field(); + b.add_output<decl::Bool>(N_("Value"), "Value_003").dependent_field(); } static void fn_node_random_value_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -58,14 +63,14 @@ static void fn_node_random_value_layout(uiLayout *layout, bContext *UNUSED(C), P static void fn_node_random_value_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeRandomValue *data = (NodeRandomValue *)MEM_callocN(sizeof(NodeRandomValue), __func__); + NodeRandomValue *data = MEM_cnew<NodeRandomValue>(__func__); data->data_type = CD_PROP_FLOAT; node->storage = data; } -static void fn_node_random_value_update(bNodeTree *UNUSED(ntree), bNode *node) +static void fn_node_random_value_update(bNodeTree *ntree, bNode *node) { - const NodeRandomValue &storage = *(const NodeRandomValue *)node->storage; + const NodeRandomValue &storage = node_storage(*node); const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type); bNodeSocket *sock_min_vector = (bNodeSocket *)node->inputs.first; @@ -81,18 +86,66 @@ static void fn_node_random_value_update(bNodeTree *UNUSED(ntree), bNode *node) bNodeSocket *sock_out_int = sock_out_float->next; bNodeSocket *sock_out_bool = sock_out_int->next; - nodeSetSocketAvailability(sock_min_vector, data_type == CD_PROP_FLOAT3); - nodeSetSocketAvailability(sock_max_vector, data_type == CD_PROP_FLOAT3); - nodeSetSocketAvailability(sock_min_float, data_type == CD_PROP_FLOAT); - nodeSetSocketAvailability(sock_max_float, data_type == CD_PROP_FLOAT); - nodeSetSocketAvailability(sock_min_int, data_type == CD_PROP_INT32); - nodeSetSocketAvailability(sock_max_int, data_type == CD_PROP_INT32); - nodeSetSocketAvailability(sock_probability, data_type == CD_PROP_BOOL); - - nodeSetSocketAvailability(sock_out_vector, data_type == CD_PROP_FLOAT3); - nodeSetSocketAvailability(sock_out_float, data_type == CD_PROP_FLOAT); - nodeSetSocketAvailability(sock_out_int, data_type == CD_PROP_INT32); - nodeSetSocketAvailability(sock_out_bool, data_type == CD_PROP_BOOL); + nodeSetSocketAvailability(ntree, sock_min_vector, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(ntree, sock_max_vector, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(ntree, sock_min_float, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(ntree, sock_max_float, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(ntree, sock_min_int, data_type == CD_PROP_INT32); + nodeSetSocketAvailability(ntree, sock_max_int, data_type == CD_PROP_INT32); + nodeSetSocketAvailability(ntree, sock_probability, data_type == CD_PROP_BOOL); + + nodeSetSocketAvailability(ntree, sock_out_vector, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(ntree, sock_out_float, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(ntree, sock_out_int, data_type == CD_PROP_INT32); + nodeSetSocketAvailability(ntree, sock_out_bool, data_type == CD_PROP_BOOL); +} + +static std::optional<CustomDataType> node_type_from_other_socket(const bNodeSocket &socket) +{ + switch (socket.type) { + case SOCK_FLOAT: + return CD_PROP_FLOAT; + case SOCK_BOOLEAN: + return CD_PROP_BOOL; + case SOCK_INT: + return CD_PROP_INT32; + case SOCK_VECTOR: + case SOCK_RGBA: + return CD_PROP_FLOAT3; + default: + return {}; + } +} + +static void fn_node_random_value_gather_link_search(GatherLinkSearchOpParams ¶ms) +{ + const NodeDeclaration &declaration = *params.node_type().fixed_declaration; + const std::optional<CustomDataType> type = node_type_from_other_socket(params.other_socket()); + if (!type) { + return; + } + if (params.in_out() == SOCK_IN) { + if (ELEM(*type, CD_PROP_INT32, CD_PROP_FLOAT3, CD_PROP_FLOAT)) { + params.add_item(IFACE_("Min"), [type](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node("FunctionNodeRandomValue"); + node_storage(node).data_type = *type; + params.update_and_connect_available_socket(node, "Min"); + }); + params.add_item(IFACE_("Max"), [type](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node("FunctionNodeRandomValue"); + node_storage(node).data_type = *type; + params.update_and_connect_available_socket(node, "Max"); + }); + } + search_link_ops_for_declarations(params, declaration.inputs().take_back(3)); + } + else { + params.add_item(IFACE_("Value"), [type](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node("FunctionNodeRandomValue"); + node_storage(node).data_type = *type; + params.update_and_connect_available_socket(node, "Value"); + }); + } } class RandomVectorFunction : public fn::MultiFunction { @@ -203,14 +256,16 @@ class RandomIntFunction : public fn::MultiFunction { const VArray<int> &seeds = params.readonly_single_input<int>(3, "Seed"); MutableSpan<int> values = params.uninitialized_single_output<int>(4, "Value"); + /* Add one to the maximum and use floor to produce an even + * distribution for the first and last values (See T93591). */ for (int64_t i : mask) { const float min_value = min_values[i]; - const float max_value = max_values[i]; + const float max_value = max_values[i] + 1.0f; const int seed = seeds[i]; const int id = ids[i]; const float value = noise::hash_to_float(id, seed); - values[i] = round_fl_to_int(value * (max_value - min_value) + min_value); + values[i] = floor(value * (max_value - min_value) + min_value); } } }; @@ -251,7 +306,7 @@ class RandomBoolFunction : public fn::MultiFunction { static void fn_node_random_value_build_multi_function(NodeMultiFunctionBuilder &builder) { - const NodeRandomValue &storage = *(const NodeRandomValue *)builder.node().storage; + const NodeRandomValue &storage = node_storage(builder.node()); const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type); switch (data_type) { @@ -282,17 +337,21 @@ static void fn_node_random_value_build_multi_function(NodeMultiFunctionBuilder & } } -} // namespace blender::nodes +} // namespace blender::nodes::node_fn_random_value_cc void register_node_type_fn_random_value() { + namespace file_ns = blender::nodes::node_fn_random_value_cc; + static bNodeType ntype; - fn_node_type_base(&ntype, FN_NODE_RANDOM_VALUE, "Random Value", NODE_CLASS_CONVERTER, 0); - node_type_init(&ntype, blender::nodes::fn_node_random_value_init); - node_type_update(&ntype, blender::nodes::fn_node_random_value_update); - ntype.draw_buttons = blender::nodes::fn_node_random_value_layout; - ntype.declare = blender::nodes::fn_node_random_value_declare; - ntype.build_multi_function = blender::nodes::fn_node_random_value_build_multi_function; + + fn_node_type_base(&ntype, FN_NODE_RANDOM_VALUE, "Random Value", NODE_CLASS_CONVERTER); + node_type_init(&ntype, file_ns::fn_node_random_value_init); + node_type_update(&ntype, file_ns::fn_node_random_value_update); + ntype.draw_buttons = file_ns::fn_node_random_value_layout; + ntype.declare = file_ns::fn_node_random_value_declare; + ntype.build_multi_function = file_ns::fn_node_random_value_build_multi_function; + ntype.gather_link_search_ops = file_ns::fn_node_random_value_gather_link_search; node_type_storage( &ntype, "NodeRandomValue", node_free_standard_storage, node_copy_standard_storage); nodeRegisterType(&ntype); diff --git a/source/blender/nodes/function/nodes/node_fn_replace_string.cc b/source/blender/nodes/function/nodes/node_fn_replace_string.cc new file mode 100644 index 00000000000..afe516a5214 --- /dev/null +++ b/source/blender/nodes/function/nodes/node_fn_replace_string.cc @@ -0,0 +1,68 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_string_utf8.h" + +#include "node_function_util.hh" + +namespace blender::nodes::node_fn_replace_string_cc { + +static void fn_node_replace_string_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::String>(N_("String")); + b.add_input<decl::String>(N_("Find")).description(N_("The string to find in the input string")); + b.add_input<decl::String>(N_("Replace")) + .description(N_("The string to replace each match with")); + b.add_output<decl::String>(N_("String")); +} + +static std::string replace_all(std::string str, const std::string &from, const std::string &to) +{ + if (from.length() <= 0) { + return str; + } + const size_t step = to.length() > 0 ? to.length() : 1; + + size_t offset = 0; + while ((offset = str.find(from, offset)) != std::string::npos) { + str.replace(offset, from.length(), to); + offset += step; + } + return str; +} + +static void fn_node_replace_string_build_multi_function(NodeMultiFunctionBuilder &builder) +{ + static fn::CustomMF_SI_SI_SI_SO<std::string, std::string, std::string, std::string> substring_fn{ + "Replace", [](const std::string &str, const std::string &find, const std::string &replace) { + return replace_all(str, find, replace); + }}; + builder.set_matching_fn(&substring_fn); +} + +} // namespace blender::nodes::node_fn_replace_string_cc + +void register_node_type_fn_replace_string() +{ + namespace file_ns = blender::nodes::node_fn_replace_string_cc; + + static bNodeType ntype; + + fn_node_type_base(&ntype, FN_NODE_REPLACE_STRING, "Replace String", NODE_CLASS_CONVERTER); + ntype.declare = file_ns::fn_node_replace_string_declare; + ntype.build_multi_function = file_ns::fn_node_replace_string_build_multi_function; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/function/nodes/node_fn_rotate_euler.cc b/source/blender/nodes/function/nodes/node_fn_rotate_euler.cc new file mode 100644 index 00000000000..3140aeac975 --- /dev/null +++ b/source/blender/nodes/function/nodes/node_fn_rotate_euler.cc @@ -0,0 +1,142 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_listbase.h" +#include "BLI_string.h" + +#include "RNA_enum_types.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_function_util.hh" + +namespace blender::nodes::node_fn_rotate_euler_cc { + +static void fn_node_rotate_euler_declare(NodeDeclarationBuilder &b) +{ + b.is_function_node(); + b.add_input<decl::Vector>(N_("Rotation")).subtype(PROP_EULER).hide_value(); + b.add_input<decl::Vector>(N_("Rotate By")).subtype(PROP_EULER); + b.add_input<decl::Vector>(N_("Axis")).default_value({0.0, 0.0, 1.0}).subtype(PROP_XYZ); + b.add_input<decl::Float>(N_("Angle")).subtype(PROP_ANGLE); + b.add_output<decl::Vector>(N_("Rotation")); +} + +static void fn_node_rotate_euler_update(bNodeTree *ntree, bNode *node) +{ + bNodeSocket *rotate_by_socket = static_cast<bNodeSocket *>(BLI_findlink(&node->inputs, 1)); + bNodeSocket *axis_socket = static_cast<bNodeSocket *>(BLI_findlink(&node->inputs, 2)); + bNodeSocket *angle_socket = static_cast<bNodeSocket *>(BLI_findlink(&node->inputs, 3)); + + nodeSetSocketAvailability( + ntree, rotate_by_socket, ELEM(node->custom1, FN_NODE_ROTATE_EULER_TYPE_EULER)); + nodeSetSocketAvailability( + ntree, axis_socket, ELEM(node->custom1, FN_NODE_ROTATE_EULER_TYPE_AXIS_ANGLE)); + nodeSetSocketAvailability( + ntree, angle_socket, ELEM(node->custom1, FN_NODE_ROTATE_EULER_TYPE_AXIS_ANGLE)); +} + +static void fn_node_rotate_euler_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "type", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); + uiItemR(layout, ptr, "space", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); +} + +static const fn::MultiFunction *get_multi_function(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) { + float input_mat[3][3]; + eul_to_mat3(input_mat, input); + float rot_mat[3][3]; + eul_to_mat3(rot_mat, rotation); + float mat_res[3][3]; + mul_m3_m3m3(mat_res, rot_mat, input_mat); + float3 result; + mat3_to_eul(result, mat_res); + return result; + }}; + static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> obj_AA_rot{ + "Rotate Euler by AxisAngle/Object", + [](const float3 &input, const float3 &axis, float angle) { + float input_mat[3][3]; + eul_to_mat3(input_mat, input); + float rot_mat[3][3]; + axis_angle_to_mat3(rot_mat, axis, angle); + float mat_res[3][3]; + mul_m3_m3m3(mat_res, rot_mat, input_mat); + float3 result; + mat3_to_eul(result, mat_res); + return result; + }}; + static fn::CustomMF_SI_SI_SO<float3, float3, float3> local_euler_rot{ + "Rotate Euler by Euler/Local", [](const float3 &input, const float3 &rotation) { + float input_mat[3][3]; + eul_to_mat3(input_mat, input); + float rot_mat[3][3]; + eul_to_mat3(rot_mat, rotation); + float mat_res[3][3]; + mul_m3_m3m3(mat_res, input_mat, rot_mat); + float3 result; + mat3_to_eul(result, mat_res); + return result; + }}; + static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> local_AA_rot{ + "Rotate Euler by AxisAngle/Local", [](const float3 &input, const float3 &axis, float angle) { + float input_mat[3][3]; + eul_to_mat3(input_mat, input); + float rot_mat[3][3]; + axis_angle_to_mat3(rot_mat, axis, angle); + float mat_res[3][3]; + mul_m3_m3m3(mat_res, input_mat, rot_mat); + float3 result; + mat3_to_eul(result, mat_res); + return result; + }}; + short type = bnode.custom1; + short space = bnode.custom2; + if (type == FN_NODE_ROTATE_EULER_TYPE_AXIS_ANGLE) { + return space == FN_NODE_ROTATE_EULER_SPACE_OBJECT ? &obj_AA_rot : &local_AA_rot; + } + if (type == FN_NODE_ROTATE_EULER_TYPE_EULER) { + return space == FN_NODE_ROTATE_EULER_SPACE_OBJECT ? &obj_euler_rot : &local_euler_rot; + } + BLI_assert_unreachable(); + return nullptr; +} + +static void fn_node_rotate_euler_build_multi_function(NodeMultiFunctionBuilder &builder) +{ + const fn::MultiFunction *fn = get_multi_function(builder.node()); + builder.set_matching_fn(fn); +} + +} // namespace blender::nodes::node_fn_rotate_euler_cc + +void register_node_type_fn_rotate_euler() +{ + namespace file_ns = blender::nodes::node_fn_rotate_euler_cc; + + static bNodeType ntype; + + fn_node_type_base(&ntype, FN_NODE_ROTATE_EULER, "Rotate Euler", NODE_CLASS_CONVERTER); + ntype.declare = file_ns::fn_node_rotate_euler_declare; + ntype.draw_buttons = file_ns::fn_node_rotate_euler_layout; + node_type_update(&ntype, file_ns::fn_node_rotate_euler_update); + ntype.build_multi_function = file_ns::fn_node_rotate_euler_build_multi_function; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/function/nodes/node_fn_string_substring.cc b/source/blender/nodes/function/nodes/node_fn_slice_string.cc index b91171923d6..4055495ec1f 100644 --- a/source/blender/nodes/function/nodes/node_fn_string_substring.cc +++ b/source/blender/nodes/function/nodes/node_fn_slice_string.cc @@ -18,38 +18,38 @@ #include "node_function_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_fn_slice_string_cc { -static void fn_node_string_substring_declare(NodeDeclarationBuilder &b) +static void fn_node_slice_string_declare(NodeDeclarationBuilder &b) { - b.is_function_node(); - b.add_input<decl::String>("String"); - b.add_input<decl::Int>("Position"); - b.add_input<decl::Int>("Length").min(0); - b.add_output<decl::String>("String"); -}; - -} // namespace blender::nodes + b.add_input<decl::String>(N_("String")); + b.add_input<decl::Int>(N_("Position")); + b.add_input<decl::Int>(N_("Length")).min(0).default_value(10); + b.add_output<decl::String>(N_("String")); +} -static void fn_node_string_substring_build_multi_function( - blender::nodes::NodeMultiFunctionBuilder &builder) +static void fn_node_slice_string_build_multi_function(NodeMultiFunctionBuilder &builder) { - static blender::fn::CustomMF_SI_SI_SI_SO<std::string, int, int, std::string> substring_fn{ - "Substring", [](const std::string &str, int a, int b) { + static blender::fn::CustomMF_SI_SI_SI_SO<std::string, int, int, std::string> slice_fn{ + "Slice", [](const std::string &str, int a, int b) { const int len = BLI_strlen_utf8(str.c_str()); const int start = BLI_str_utf8_offset_from_index(str.c_str(), std::clamp(a, 0, len)); const int end = BLI_str_utf8_offset_from_index(str.c_str(), std::clamp(a + b, 0, len)); return str.substr(start, std::max<int>(end - start, 0)); }}; - builder.set_matching_fn(&substring_fn); + builder.set_matching_fn(&slice_fn); } -void register_node_type_fn_string_substring() +} // namespace blender::nodes::node_fn_slice_string_cc + +void register_node_type_fn_slice_string() { + namespace file_ns = blender::nodes::node_fn_slice_string_cc; + static bNodeType ntype; - fn_node_type_base(&ntype, FN_NODE_STRING_SUBSTRING, "String Substring", NODE_CLASS_CONVERTER, 0); - ntype.declare = blender::nodes::fn_node_string_substring_declare; - ntype.build_multi_function = fn_node_string_substring_build_multi_function; + fn_node_type_base(&ntype, FN_NODE_SLICE_STRING, "Slice String", NODE_CLASS_CONVERTER); + ntype.declare = file_ns::fn_node_slice_string_declare; + ntype.build_multi_function = file_ns::fn_node_slice_string_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/function/nodes/node_fn_string_length.cc b/source/blender/nodes/function/nodes/node_fn_string_length.cc index 89038629c3c..bed434c8f2c 100644 --- a/source/blender/nodes/function/nodes/node_fn_string_length.cc +++ b/source/blender/nodes/function/nodes/node_fn_string_length.cc @@ -20,31 +20,31 @@ #include "node_function_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_fn_string_length_cc { static void fn_node_string_length_declare(NodeDeclarationBuilder &b) { - b.is_function_node(); - b.add_input<decl::String>("String"); - b.add_output<decl::Int>("Length"); -}; - -} // namespace blender::nodes + b.add_input<decl::String>(N_("String")); + b.add_output<decl::Int>(N_("Length")); +} -static void fn_node_string_length_build_multi_function( - blender::nodes::NodeMultiFunctionBuilder &builder) +static void fn_node_string_length_build_multi_function(NodeMultiFunctionBuilder &builder) { - static blender::fn::CustomMF_SI_SO<std::string, int> str_len_fn{ + static fn::CustomMF_SI_SO<std::string, int> str_len_fn{ "String Length", [](const std::string &a) { return BLI_strlen_utf8(a.c_str()); }}; builder.set_matching_fn(&str_len_fn); } +} // namespace blender::nodes::node_fn_string_length_cc + void register_node_type_fn_string_length() { + namespace file_ns = blender::nodes::node_fn_string_length_cc; + static bNodeType ntype; - fn_node_type_base(&ntype, FN_NODE_STRING_LENGTH, "String Length", NODE_CLASS_CONVERTER, 0); - ntype.declare = blender::nodes::fn_node_string_length_declare; - ntype.build_multi_function = fn_node_string_length_build_multi_function; + fn_node_type_base(&ntype, FN_NODE_STRING_LENGTH, "String Length", NODE_CLASS_CONVERTER); + ntype.declare = file_ns::fn_node_string_length_declare; + ntype.build_multi_function = file_ns::fn_node_string_length_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/function/nodes/node_fn_value_to_string.cc b/source/blender/nodes/function/nodes/node_fn_value_to_string.cc index 56206af2eb2..ee0613de9bd 100644 --- a/source/blender/nodes/function/nodes/node_fn_value_to_string.cc +++ b/source/blender/nodes/function/nodes/node_fn_value_to_string.cc @@ -17,22 +17,18 @@ #include "node_function_util.hh" #include <iomanip> -namespace blender::nodes { +namespace blender::nodes::node_fn_value_to_string_cc { static void fn_node_value_to_string_declare(NodeDeclarationBuilder &b) { - b.is_function_node(); - b.add_input<decl::Float>("Value"); - b.add_input<decl::Int>("Decimals").min(0); - b.add_output<decl::String>("String"); -}; - -} // namespace blender::nodes + b.add_input<decl::Float>(N_("Value")); + b.add_input<decl::Int>(N_("Decimals")).min(0); + b.add_output<decl::String>(N_("String")); +} -static void fn_node_value_to_string_build_multi_function( - blender::nodes::NodeMultiFunctionBuilder &builder) +static void fn_node_value_to_string_build_multi_function(NodeMultiFunctionBuilder &builder) { - static blender::fn::CustomMF_SI_SI_SO<float, int, std::string> to_str_fn{ + static fn::CustomMF_SI_SI_SO<float, int, std::string> to_str_fn{ "Value To String", [](float a, int b) { std::stringstream stream; stream << std::fixed << std::setprecision(std::max(0, b)) << a; @@ -41,12 +37,16 @@ static void fn_node_value_to_string_build_multi_function( builder.set_matching_fn(&to_str_fn); } +} // namespace blender::nodes::node_fn_value_to_string_cc + void register_node_type_fn_value_to_string() { + namespace file_ns = blender::nodes::node_fn_value_to_string_cc; + static bNodeType ntype; - fn_node_type_base(&ntype, FN_NODE_VALUE_TO_STRING, "Value to String", NODE_CLASS_CONVERTER, 0); - ntype.declare = blender::nodes::fn_node_value_to_string_declare; - ntype.build_multi_function = fn_node_value_to_string_build_multi_function; + fn_node_type_base(&ntype, FN_NODE_VALUE_TO_STRING, "Value to String", NODE_CLASS_CONVERTER); + ntype.declare = file_ns::fn_node_value_to_string_declare; + ntype.build_multi_function = file_ns::fn_node_value_to_string_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/CMakeLists.txt b/source/blender/nodes/geometry/CMakeLists.txt new file mode 100644 index 00000000000..b4add633b0c --- /dev/null +++ b/source/blender/nodes/geometry/CMakeLists.txt @@ -0,0 +1,290 @@ +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ***** END GPL LICENSE BLOCK ***** + +set(INC + . + .. + ../intern + ../../editors/include + ../../blenkernel + ../../blenlib + ../../blentranslation + ../../bmesh + ../../depsgraph + ../../functions + ../../geometry + ../../gpu + ../../imbuf + ../../makesdna + ../../makesrna + ../../render + ../../windowmanager + ../../../../intern/guardedalloc +) + + +set(SRC + nodes/legacy/node_geo_legacy_align_rotation_to_vector.cc + nodes/legacy/node_geo_legacy_attribute_clamp.cc + nodes/legacy/node_geo_legacy_attribute_color_ramp.cc + nodes/legacy/node_geo_legacy_attribute_combine_xyz.cc + nodes/legacy/node_geo_legacy_attribute_compare.cc + nodes/legacy/node_geo_legacy_attribute_convert.cc + nodes/legacy/node_geo_legacy_attribute_curve_map.cc + nodes/legacy/node_geo_legacy_attribute_fill.cc + nodes/legacy/node_geo_legacy_attribute_map_range.cc + nodes/legacy/node_geo_legacy_attribute_math.cc + nodes/legacy/node_geo_legacy_attribute_mix.cc + nodes/legacy/node_geo_legacy_attribute_proximity.cc + nodes/legacy/node_geo_legacy_attribute_randomize.cc + nodes/legacy/node_geo_legacy_attribute_sample_texture.cc + nodes/legacy/node_geo_legacy_attribute_separate_xyz.cc + nodes/legacy/node_geo_legacy_attribute_transfer.cc + nodes/legacy/node_geo_legacy_attribute_vector_math.cc + nodes/legacy/node_geo_legacy_attribute_vector_rotate.cc + nodes/legacy/node_geo_legacy_curve_endpoints.cc + nodes/legacy/node_geo_legacy_curve_reverse.cc + nodes/legacy/node_geo_legacy_curve_select_by_handle_type.cc + nodes/legacy/node_geo_legacy_curve_set_handles.cc + nodes/legacy/node_geo_legacy_curve_spline_type.cc + nodes/legacy/node_geo_legacy_curve_subdivide.cc + nodes/legacy/node_geo_legacy_curve_to_points.cc + nodes/legacy/node_geo_legacy_delete_geometry.cc + nodes/legacy/node_geo_legacy_edge_split.cc + nodes/legacy/node_geo_legacy_material_assign.cc + nodes/legacy/node_geo_legacy_mesh_to_curve.cc + nodes/legacy/node_geo_legacy_point_distribute.cc + nodes/legacy/node_geo_legacy_point_instance.cc + nodes/legacy/node_geo_legacy_point_rotate.cc + nodes/legacy/node_geo_legacy_point_scale.cc + nodes/legacy/node_geo_legacy_point_separate.cc + nodes/legacy/node_geo_legacy_point_translate.cc + nodes/legacy/node_geo_legacy_points_to_volume.cc + nodes/legacy/node_geo_legacy_raycast.cc + nodes/legacy/node_geo_legacy_select_by_material.cc + nodes/legacy/node_geo_legacy_subdivision_surface.cc + nodes/legacy/node_geo_legacy_volume_to_mesh.cc + + nodes/node_geo_accumulate_field.cc + nodes/node_geo_attribute_capture.cc + nodes/node_geo_attribute_domain_size.cc + nodes/node_geo_attribute_remove.cc + nodes/node_geo_attribute_statistic.cc + nodes/node_geo_boolean.cc + nodes/node_geo_bounding_box.cc + nodes/node_geo_collection_info.cc + nodes/node_geo_common.cc + nodes/node_geo_convex_hull.cc + nodes/node_geo_curve_endpoint_selection.cc + nodes/node_geo_curve_fill.cc + nodes/node_geo_curve_fillet.cc + nodes/node_geo_curve_handle_type_selection.cc + nodes/node_geo_curve_length.cc + nodes/node_geo_curve_primitive_arc.cc + nodes/node_geo_curve_primitive_bezier_segment.cc + nodes/node_geo_curve_primitive_circle.cc + nodes/node_geo_curve_primitive_line.cc + nodes/node_geo_curve_primitive_quadratic_bezier.cc + nodes/node_geo_curve_primitive_quadrilateral.cc + nodes/node_geo_curve_primitive_spiral.cc + nodes/node_geo_curve_primitive_star.cc + nodes/node_geo_curve_resample.cc + nodes/node_geo_curve_reverse.cc + nodes/node_geo_curve_sample.cc + nodes/node_geo_curve_set_handles.cc + nodes/node_geo_curve_spline_parameter.cc + nodes/node_geo_curve_spline_type.cc + nodes/node_geo_curve_subdivide.cc + nodes/node_geo_curve_to_mesh.cc + nodes/node_geo_curve_to_points.cc + nodes/node_geo_curve_trim.cc + nodes/node_geo_delete_geometry.cc + nodes/node_geo_distribute_points_on_faces.cc + nodes/node_geo_dual_mesh.cc + nodes/node_geo_edge_split.cc + nodes/node_geo_extrude_mesh.cc + nodes/node_geo_field_at_index.cc + nodes/node_geo_flip_faces.cc + nodes/node_geo_geometry_to_instance.cc + nodes/node_geo_image_texture.cc + nodes/node_geo_input_curve_handles.cc + nodes/node_geo_input_curve_tilt.cc + nodes/node_geo_input_id.cc + nodes/node_geo_input_index.cc + nodes/node_geo_input_material.cc + nodes/node_geo_input_material_index.cc + nodes/node_geo_input_mesh_edge_angle.cc + nodes/node_geo_input_mesh_edge_neighbors.cc + nodes/node_geo_input_mesh_edge_vertices.cc + nodes/node_geo_input_mesh_face_area.cc + nodes/node_geo_input_mesh_face_neighbors.cc + nodes/node_geo_input_mesh_island.cc + nodes/node_geo_input_mesh_vertex_neighbors.cc + nodes/node_geo_input_normal.cc + nodes/node_geo_input_position.cc + nodes/node_geo_input_radius.cc + nodes/node_geo_input_scene_time.cc + nodes/node_geo_input_shade_smooth.cc + nodes/node_geo_input_spline_cyclic.cc + nodes/node_geo_input_spline_length.cc + nodes/node_geo_input_spline_resolution.cc + nodes/node_geo_input_tangent.cc + nodes/node_geo_instance_on_points.cc + nodes/node_geo_instances_to_points.cc + nodes/node_geo_is_viewport.cc + nodes/node_geo_join_geometry.cc + nodes/node_geo_material_replace.cc + nodes/node_geo_material_selection.cc + nodes/node_geo_merge_by_distance.cc + nodes/node_geo_mesh_primitive_circle.cc + nodes/node_geo_mesh_primitive_cone.cc + nodes/node_geo_mesh_primitive_cube.cc + nodes/node_geo_mesh_primitive_cylinder.cc + nodes/node_geo_mesh_primitive_grid.cc + nodes/node_geo_mesh_primitive_ico_sphere.cc + nodes/node_geo_mesh_primitive_line.cc + nodes/node_geo_mesh_primitive_uv_sphere.cc + nodes/node_geo_mesh_subdivide.cc + nodes/node_geo_mesh_to_curve.cc + nodes/node_geo_mesh_to_points.cc + nodes/node_geo_object_info.cc + nodes/node_geo_points_to_vertices.cc + nodes/node_geo_points_to_volume.cc + nodes/node_geo_proximity.cc + nodes/node_geo_raycast.cc + nodes/node_geo_realize_instances.cc + nodes/node_geo_rotate_instances.cc + nodes/node_geo_scale_elements.cc + nodes/node_geo_scale_instances.cc + nodes/node_geo_separate_components.cc + nodes/node_geo_separate_geometry.cc + nodes/node_geo_set_curve_handles.cc + nodes/node_geo_set_curve_radius.cc + nodes/node_geo_set_curve_tilt.cc + nodes/node_geo_set_id.cc + nodes/node_geo_set_material.cc + nodes/node_geo_set_material_index.cc + nodes/node_geo_set_point_radius.cc + nodes/node_geo_set_position.cc + nodes/node_geo_set_shade_smooth.cc + nodes/node_geo_set_spline_cyclic.cc + nodes/node_geo_set_spline_resolution.cc + nodes/node_geo_string_join.cc + nodes/node_geo_string_to_curves.cc + nodes/node_geo_subdivision_surface.cc + nodes/node_geo_switch.cc + nodes/node_geo_transfer_attribute.cc + nodes/node_geo_transform.cc + nodes/node_geo_translate_instances.cc + nodes/node_geo_triangulate.cc + nodes/node_geo_viewer.cc + nodes/node_geo_volume_to_mesh.cc + + node_geometry_exec.cc + node_geometry_tree.cc + node_geometry_util.cc + + node_geometry_util.hh +) + +set(LIB + bf_bmesh + bf_functions + bf_geometry +) + +if(WITH_BULLET) + list(APPEND INC_SYS + ${BULLET_INCLUDE_DIRS} + ../../../../intern/rigidbody + ) + if(NOT WITH_SYSTEM_BULLET) + list(APPEND LIB + extern_bullet + ) + endif() + + list(APPEND LIB + ${BULLET_LIBRARIES} + ) + add_definitions(-DWITH_BULLET) +endif() + +if(WITH_PYTHON) + list(APPEND INC + ../../python + ) + list(APPEND INC_SYS + ${PYTHON_INCLUDE_DIRS} + ) + list(APPEND LIB + ${PYTHON_LINKFLAGS} + ${PYTHON_LIBRARIES} + ) + add_definitions(-DWITH_PYTHON) +endif() + +if(WITH_INTERNATIONAL) + add_definitions(-DWITH_INTERNATIONAL) +endif() + +if(WITH_TBB) + list(APPEND INC_SYS + ${TBB_INCLUDE_DIRS} + ) + add_definitions(-DWITH_TBB) + if(WIN32) + # TBB includes Windows.h which will define min/max macros + # that will collide with the stl versions. + add_definitions(-DNOMINMAX) + endif() +endif() + +if(WITH_IMAGE_OPENEXR) + add_definitions(-DWITH_OPENEXR) +endif() + +if(WITH_OPENSUBDIV) + add_definitions(-DWITH_OPENSUBDIV) +endif() + +if(WITH_GMP) + add_definitions(-DWITH_GMP) + + list(APPEND INC_SYS + ${GMP_INCLUDE_DIRS} + ) + + list(APPEND LIB + ${GMP_LIBRARIES} + ) +endif() + +if(WITH_OPENVDB) + list(APPEND INC_SYS + ${OPENVDB_INCLUDE_DIRS} + ) + add_definitions(-DWITH_OPENVDB ${OPENVDB_DEFINITIONS}) +endif() + +blender_add_lib(bf_nodes_geometry "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") + +if(WITH_UNITY_BUILD) + set_target_properties(bf_nodes_geometry PROPERTIES UNITY_BUILD ON) + set_target_properties(bf_nodes_geometry PROPERTIES UNITY_BUILD_BATCH_SIZE 10) +endif() diff --git a/source/blender/nodes/geometry/node_geometry_tree.cc b/source/blender/nodes/geometry/node_geometry_tree.cc index d6b23c38ee4..a6dec71ed06 100644 --- a/source/blender/nodes/geometry/node_geometry_tree.cc +++ b/source/blender/nodes/geometry/node_geometry_tree.cc @@ -32,6 +32,8 @@ #include "RNA_access.h" +#include "UI_resources.h" + #include "node_common.h" bNodeTreeType *ntreeType_Geometry; @@ -84,15 +86,16 @@ static void foreach_nodeclass(Scene *UNUSED(scene), void *calldata, bNodeClassCa func(calldata, NODE_CLASS_LAYOUT, N_("Layout")); } -static bool geometry_node_tree_validate_link(bNodeTree *UNUSED(ntree), bNodeLink *link) +static bool geometry_node_tree_validate_link(eNodeSocketDatatype type_a, + eNodeSocketDatatype type_b) { /* Geometry, string, object, material, texture and collection sockets can only be connected to * themselves. The other types can be converted between each other. */ - if (ELEM(link->fromsock->type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_BOOLEAN, SOCK_INT) && - ELEM(link->tosock->type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_BOOLEAN, SOCK_INT)) { + if (ELEM(type_a, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_BOOLEAN, SOCK_INT) && + ELEM(type_b, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_BOOLEAN, SOCK_INT)) { return true; } - return (link->tosock->type == link->fromsock->type); + return type_a == type_b; } static bool geometry_node_tree_socket_type_valid(bNodeTreeType *UNUSED(ntreetype), @@ -109,17 +112,18 @@ static bool geometry_node_tree_socket_type_valid(bNodeTreeType *UNUSED(ntreetype SOCK_GEOMETRY, SOCK_COLLECTION, SOCK_TEXTURE, + SOCK_IMAGE, SOCK_MATERIAL); } -void register_node_tree_type_geo(void) +void register_node_tree_type_geo() { bNodeTreeType *tt = ntreeType_Geometry = static_cast<bNodeTreeType *>( MEM_callocN(sizeof(bNodeTreeType), "geometry node tree type")); tt->type = NTREE_GEOMETRY; strcpy(tt->idname, "GeometryNodeTree"); strcpy(tt->ui_name, N_("Geometry Node Editor")); - tt->ui_icon = 0; /* defined in drawnode.c */ + tt->ui_icon = ICON_NODETREE; strcpy(tt->ui_description, N_("Geometry nodes")); tt->rna_ext.srna = &RNA_GeometryNodeTree; tt->update = geometry_node_tree_update; diff --git a/source/blender/nodes/geometry/node_geometry_util.cc b/source/blender/nodes/geometry/node_geometry_util.cc index 46e9d36c09c..ceb9a7e1467 100644 --- a/source/blender/nodes/geometry/node_geometry_util.cc +++ b/source/blender/nodes/geometry/node_geometry_util.cc @@ -24,18 +24,14 @@ #include "BKE_mesh_runtime.h" #include "BKE_pointcloud.h" +#include "NOD_socket_search_link.hh" + namespace blender::nodes { using bke::GeometryInstanceGroup; -/** - * Update the availability of a group of input sockets with the same name, - * used for switching between attribute inputs or single values. - * - * \param mode: Controls which socket of the group to make available. - * \param name_is_available: If false, make all sockets with this name unavailable. - */ -void update_attribute_input_socket_availabilities(bNode &node, +void update_attribute_input_socket_availabilities(bNodeTree &ntree, + bNode &node, const StringRef name, const GeometryNodeAttributeInputMode mode, const bool name_is_available) @@ -50,11 +46,36 @@ void update_attribute_input_socket_availabilities(bNode &node, (socket->type == SOCK_INT && mode_ == GEO_NODE_ATTRIBUTE_INPUT_INTEGER) || (socket->type == SOCK_VECTOR && mode_ == GEO_NODE_ATTRIBUTE_INPUT_VECTOR) || (socket->type == SOCK_RGBA && mode_ == GEO_NODE_ATTRIBUTE_INPUT_COLOR)); - nodeSetSocketAvailability(socket, socket_is_available); + nodeSetSocketAvailability(&ntree, socket, socket_is_available); } } } +std::optional<CustomDataType> node_data_type_to_custom_data_type(const eNodeSocketDatatype type) +{ + switch (type) { + case SOCK_FLOAT: + return CD_PROP_FLOAT; + case SOCK_VECTOR: + return CD_PROP_FLOAT3; + case SOCK_RGBA: + return CD_PROP_COLOR; + case SOCK_BOOLEAN: + return CD_PROP_BOOL; + case SOCK_INT: + return CD_PROP_INT32; + case SOCK_STRING: + return CD_PROP_STRING; + default: + return {}; + } +} + +std::optional<CustomDataType> node_socket_to_custom_data_type(const bNodeSocket &socket) +{ + return node_data_type_to_custom_data_type(static_cast<eNodeSocketDatatype>(socket.type)); +} + } // namespace blender::nodes bool geo_node_poll_default(bNodeType *UNUSED(ntype), @@ -62,16 +83,16 @@ bool geo_node_poll_default(bNodeType *UNUSED(ntype), const char **r_disabled_hint) { if (!STREQ(ntree->idname, "GeometryNodeTree")) { - *r_disabled_hint = "Not a geometry node tree"; + *r_disabled_hint = TIP_("Not a geometry node tree"); return false; } return true; } -void geo_node_type_base(bNodeType *ntype, int type, const char *name, short nclass, short flag) +void geo_node_type_base(bNodeType *ntype, int type, const char *name, short nclass) { - node_type_base(ntype, type, name, nclass, flag); + node_type_base(ntype, type, name, nclass); ntype->poll = geo_node_poll_default; - ntype->update_internal_links = node_update_internal_links_default; ntype->insert_link = node_insert_link_default; + ntype->gather_link_search_ops = blender::nodes::search_link_ops_for_basic_node; } diff --git a/source/blender/nodes/geometry/node_geometry_util.hh b/source/blender/nodes/geometry/node_geometry_util.hh index 015ac0de002..dddc3527124 100644 --- a/source/blender/nodes/geometry/node_geometry_util.hh +++ b/source/blender/nodes/geometry/node_geometry_util.hh @@ -18,7 +18,7 @@ #include <string.h> -#include "BLI_float3.hh" +#include "BLI_math_vec_types.hh" #include "BLI_utildefines.h" #include "MEM_guardedalloc.h" @@ -32,41 +32,59 @@ #include "NOD_geometry.h" #include "NOD_geometry_exec.hh" #include "NOD_socket_declarations.hh" +#include "NOD_socket_declarations_geometry.hh" #include "node_util.h" -void geo_node_type_base( - struct bNodeType *ntype, int type, const char *name, short nclass, short flag); +void geo_node_type_base(struct bNodeType *ntype, int type, const char *name, short nclass); bool geo_node_poll_default(struct bNodeType *ntype, struct bNodeTree *ntree, const char **r_disabled_hint); namespace blender::nodes { -void update_attribute_input_socket_availabilities(bNode &node, +/** + * Update the availability of a group of input sockets with the same name, + * used for switching between attribute inputs or single values. + * + * \param mode: Controls which socket of the group to make available. + * \param name_is_available: If false, make all sockets with this name unavailable. + */ +void update_attribute_input_socket_availabilities(bNodeTree &ntree, + bNode &node, const StringRef name, - const GeometryNodeAttributeInputMode mode, - const bool name_is_available = true); + GeometryNodeAttributeInputMode mode, + bool name_is_available = true); Array<uint32_t> get_geometry_element_ids_as_uints(const GeometryComponent &component, - const AttributeDomain domain); + AttributeDomain domain); -void transform_mesh(Mesh *mesh, +void transform_mesh(Mesh &mesh, const float3 translation, const float3 rotation, const float3 scale); -Mesh *create_line_mesh(const float3 start, const float3 delta, const int count); +void transform_geometry_set(GeometrySet &geometry, + const float4x4 &transform, + const Depsgraph &depsgraph); -Mesh *create_grid_mesh(const int verts_x, - const int verts_y, - const float size_x, - const float size_y); +Mesh *create_line_mesh(const float3 start, const float3 delta, int count); -Mesh *create_cylinder_or_cone_mesh(const float radius_top, - const float radius_bottom, - const float depth, - const int verts_num, - const GeometryNodeMeshCircleFillType fill_type); +Mesh *create_grid_mesh(int verts_x, int verts_y, float size_x, float size_y); + +struct ConeAttributeOutputs { + StrongAnonymousAttributeID top_id; + StrongAnonymousAttributeID bottom_id; + StrongAnonymousAttributeID side_id; +}; + +Mesh *create_cylinder_or_cone_mesh(float radius_top, + float radius_bottom, + float depth, + int circle_segments, + int side_segments, + int fill_segments, + GeometryNodeMeshCircleFillType fill_type, + ConeAttributeOutputs &attribute_outputs); Mesh *create_cuboid_mesh(float3 size, int verts_x, int verts_y, int verts_z); @@ -76,7 +94,18 @@ Mesh *create_cuboid_mesh(float3 size, int verts_x, int verts_y, int verts_z); void copy_point_attributes_based_on_mask(const GeometryComponent &in_component, GeometryComponent &result_component, Span<bool> masks, - const bool invert); + bool invert); +/** + * Returns the parts of the geometry that are on the selection for the given domain. If the domain + * is not applicable for the component, e.g. face domain for point cloud, nothing happens to that + * component. If no component can work with the domain, then `error_message` is set to true. + */ +void separate_geometry(GeometrySet &geometry_set, + AttributeDomain domain, + GeometryNodeDeleteGeometryMode mode, + const Field<bool> &selection_field, + bool invert, + bool &r_is_error); struct CurveToPointsResults { int result_size; @@ -100,4 +129,7 @@ void curve_create_default_rotation_attribute(Span<float3> tangents, Span<float3> normals, MutableSpan<float3> rotations); +std::optional<CustomDataType> node_data_type_to_custom_data_type(eNodeSocketDatatype type); +std::optional<CustomDataType> node_socket_to_custom_data_type(const bNodeSocket &socket); + } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_align_rotation_to_vector.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_align_rotation_to_vector.cc index d0bb906e8af..1d064586238 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_align_rotation_to_vector.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_align_rotation_to_vector.cc @@ -22,27 +22,25 @@ #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_legacy_align_rotation_to_vector_cc { -static void geo_node_align_rotation_to_vector_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::String>("Factor"); - b.add_input<decl::Float>("Factor", "Factor_001") + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::String>(N_("Factor")); + b.add_input<decl::Float>(N_("Factor"), "Factor_001") .default_value(1.0f) .min(0.0f) .max(1.0f) .subtype(PROP_FACTOR); - b.add_input<decl::String>("Vector"); - b.add_input<decl::Vector>("Vector", "Vector_001") + b.add_input<decl::String>(N_("Vector")); + b.add_input<decl::Vector>(N_("Vector"), "Vector_001") .default_value({0.0, 0.0, 1.0}) .subtype(PROP_ANGLE); - b.add_output<decl::Geometry>("Geometry"); + b.add_output<decl::Geometry>(N_("Geometry")); } -static void geo_node_align_rotation_to_vector_layout(uiLayout *layout, - bContext *UNUSED(C), - PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "axis", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); uiLayoutSetPropSep(layout, true); @@ -53,10 +51,10 @@ static void geo_node_align_rotation_to_vector_layout(uiLayout *layout, uiItemR(col, ptr, "input_type_vector", 0, IFACE_("Vector"), ICON_NONE); } -static void geo_node_align_rotation_to_vector_init(bNodeTree *UNUSED(ntree), bNode *node) +static void node_init(bNodeTree *UNUSED(ntree), bNode *node) { - NodeGeometryAlignRotationToVector *node_storage = (NodeGeometryAlignRotationToVector *) - MEM_callocN(sizeof(NodeGeometryAlignRotationToVector), __func__); + NodeGeometryAlignRotationToVector *node_storage = MEM_cnew<NodeGeometryAlignRotationToVector>( + __func__); node_storage->axis = GEO_NODE_ALIGN_ROTATION_TO_VECTOR_AXIS_X; node_storage->input_type_factor = GEO_NODE_ATTRIBUTE_INPUT_FLOAT; @@ -65,14 +63,14 @@ static void geo_node_align_rotation_to_vector_init(bNodeTree *UNUSED(ntree), bNo node->storage = node_storage; } -static void geo_node_align_rotation_to_vector_update(bNodeTree *UNUSED(ntree), bNode *node) +static void node_update(bNodeTree *ntree, bNode *node) { NodeGeometryAlignRotationToVector *node_storage = (NodeGeometryAlignRotationToVector *) node->storage; update_attribute_input_socket_availabilities( - *node, "Factor", (GeometryNodeAttributeInputMode)node_storage->input_type_factor); + *ntree, *node, "Factor", (GeometryNodeAttributeInputMode)node_storage->input_type_factor); update_attribute_input_socket_availabilities( - *node, "Vector", (GeometryNodeAttributeInputMode)node_storage->input_type_vector); + *ntree, *node, "Vector", (GeometryNodeAttributeInputMode)node_storage->input_type_vector); } static void align_rotations_auto_pivot(const VArray<float3> &vectors, @@ -92,14 +90,14 @@ static void align_rotations_auto_pivot(const VArray<float3> &vectors, float3 old_axis; mul_v3_m3v3(old_axis, old_rotation, local_main_axis); - const float3 new_axis = vector.normalized(); - float3 rotation_axis = float3::cross_high_precision(old_axis, new_axis); + const float3 new_axis = math::normalize(vector); + float3 rotation_axis = math::cross_high_precision(old_axis, new_axis); if (is_zero_v3(rotation_axis)) { /* The vectors are linearly dependent, so we fall back to another axis. */ - rotation_axis = float3::cross_high_precision(old_axis, float3(1, 0, 0)); + rotation_axis = math::cross_high_precision(old_axis, float3(1, 0, 0)); if (is_zero_v3(rotation_axis)) { /* This is now guaranteed to not be zero. */ - rotation_axis = float3::cross_high_precision(old_axis, float3(0, 1, 0)); + rotation_axis = math::cross_high_precision(old_axis, float3(0, 1, 0)); } } @@ -179,9 +177,9 @@ static void align_rotations_on_component(GeometryComponent &component, return; } - GVArray_Typed<float> factors = params.get_input_attribute<float>( + VArray<float> factors = params.get_input_attribute<float>( "Factor", component, ATTR_DOMAIN_POINT, 1.0f); - GVArray_Typed<float3> vectors = params.get_input_attribute<float3>( + VArray<float3> vectors = params.get_input_attribute<float3>( "Vector", component, ATTR_DOMAIN_POINT, {0, 0, 1}); float3 local_main_axis{0, 0, 0}; @@ -199,11 +197,11 @@ static void align_rotations_on_component(GeometryComponent &component, rotations.save(); } -static void geo_node_align_rotation_to_vector_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - geometry_set = geometry_set_realize_instances(geometry_set); + geometry_set = geometry::realize_instances_legacy(geometry_set); if (geometry_set.has<MeshComponent>()) { align_rotations_on_component(geometry_set.get_component_for_write<MeshComponent>(), params); @@ -219,25 +217,26 @@ static void geo_node_align_rotation_to_vector_exec(GeoNodeExecParams params) params.set_output("Geometry", geometry_set); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_legacy_align_rotation_to_vector_cc void register_node_type_geo_align_rotation_to_vector() { + namespace file_ns = blender::nodes::node_geo_legacy_align_rotation_to_vector_cc; + static bNodeType ntype; geo_node_type_base(&ntype, GEO_NODE_LEGACY_ALIGN_ROTATION_TO_VECTOR, "Align Rotation to Vector", - NODE_CLASS_GEOMETRY, - 0); - node_type_init(&ntype, blender::nodes::geo_node_align_rotation_to_vector_init); - node_type_update(&ntype, blender::nodes::geo_node_align_rotation_to_vector_update); + NODE_CLASS_GEOMETRY); + node_type_init(&ntype, file_ns::node_init); + node_type_update(&ntype, file_ns::node_update); node_type_storage(&ntype, "NodeGeometryAlignRotationToVector", node_free_standard_storage, node_copy_standard_storage); - ntype.declare = blender::nodes::geo_node_align_rotation_to_vector_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_align_rotation_to_vector_exec; - ntype.draw_buttons = blender::nodes::geo_node_align_rotation_to_vector_layout; + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.draw_buttons = file_ns::node_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_clamp.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_clamp.cc index 2e931a2da98..cac2a90a76c 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_clamp.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_clamp.cc @@ -20,40 +20,39 @@ #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_legacy_attribute_clamp_cc { -static void geo_node_attribute_clamp_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::String>("Attribute"); - b.add_input<decl::String>("Result"); - b.add_input<decl::Vector>("Min"); - b.add_input<decl::Vector>("Max").default_value({1.0f, 1.0f, 1.0f}); - b.add_input<decl::Float>("Min", "Min_001"); - b.add_input<decl::Float>("Max", "Max_001").default_value(1.0f); - b.add_input<decl::Int>("Min", "Min_002").min(-100000).max(100000); - b.add_input<decl::Int>("Max", "Max_002").default_value(100).min(-100000).max(100000); - b.add_input<decl::Color>("Min", "Min_003").default_value({0.5f, 0.5f, 0.5f, 1.0f}); - b.add_input<decl::Color>("Max", "Max_003").default_value({0.5f, 0.5f, 0.5f, 1.0f}); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::String>(N_("Attribute")); + b.add_input<decl::String>(N_("Result")); + b.add_input<decl::Vector>(N_("Min")); + b.add_input<decl::Vector>(N_("Max")).default_value({1.0f, 1.0f, 1.0f}); + b.add_input<decl::Float>(N_("Min"), "Min_001"); + b.add_input<decl::Float>(N_("Max"), "Max_001").default_value(1.0f); + b.add_input<decl::Int>(N_("Min"), "Min_002").min(-100000).max(100000); + b.add_input<decl::Int>(N_("Max"), "Max_002").default_value(100).min(-100000).max(100000); + b.add_input<decl::Color>(N_("Min"), "Min_003").default_value({0.5f, 0.5f, 0.5f, 1.0f}); + b.add_input<decl::Color>(N_("Max"), "Max_003").default_value({0.5f, 0.5f, 0.5f, 1.0f}); + b.add_output<decl::Geometry>(N_("Geometry")); } -static void geo_node_attribute_clamp_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); uiItemR(layout, ptr, "operation", 0, "", ICON_NONE); } -static void geo_node_attribute_clamp_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeAttributeClamp *data = (NodeAttributeClamp *)MEM_callocN(sizeof(NodeAttributeClamp), - __func__); + NodeAttributeClamp *data = MEM_cnew<NodeAttributeClamp>(__func__); data->data_type = CD_PROP_FLOAT; data->operation = NODE_CLAMP_MINMAX; node->storage = data; } -static void geo_node_attribute_clamp_update(bNodeTree *UNUSED(ntree), bNode *node) +static void node_update(bNodeTree *ntree, bNode *node) { bNodeSocket *sock_min_vector = (bNodeSocket *)BLI_findlink(&node->inputs, 3); bNodeSocket *sock_max_vector = sock_min_vector->next; @@ -66,14 +65,14 @@ static void geo_node_attribute_clamp_update(bNodeTree *UNUSED(ntree), bNode *nod const NodeAttributeClamp &storage = *(const NodeAttributeClamp *)node->storage; const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type); - nodeSetSocketAvailability(sock_min_vector, data_type == CD_PROP_FLOAT3); - nodeSetSocketAvailability(sock_max_vector, data_type == CD_PROP_FLOAT3); - nodeSetSocketAvailability(sock_min_float, data_type == CD_PROP_FLOAT); - nodeSetSocketAvailability(sock_max_float, data_type == CD_PROP_FLOAT); - nodeSetSocketAvailability(sock_min_int, data_type == CD_PROP_INT32); - nodeSetSocketAvailability(sock_max_int, data_type == CD_PROP_INT32); - nodeSetSocketAvailability(sock_min_color, data_type == CD_PROP_COLOR); - nodeSetSocketAvailability(sock_max_color, data_type == CD_PROP_COLOR); + nodeSetSocketAvailability(ntree, sock_min_vector, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(ntree, sock_max_vector, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(ntree, sock_min_float, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(ntree, sock_max_float, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(ntree, sock_min_int, data_type == CD_PROP_INT32); + nodeSetSocketAvailability(ntree, sock_max_int, data_type == CD_PROP_INT32); + nodeSetSocketAvailability(ntree, sock_min_color, data_type == CD_PROP_COLOR); + nodeSetSocketAvailability(ntree, sock_max_color, data_type == CD_PROP_COLOR); } template<typename T> T clamp_value(const T val, const T min, const T max); @@ -156,7 +155,7 @@ static void clamp_attribute(GeometryComponent &component, const GeoNodeExecParam const AttributeDomain domain = get_result_domain(component, attribute_name, result_name); const int operation = static_cast<int>(storage.operation); - GVArrayPtr attribute_input = component.attribute_try_get_for_read( + GVArray attribute_input = component.attribute_try_get_for_read( attribute_name, domain, data_type); OutputAttribute attribute_result = component.attribute_try_get_for_output_only( @@ -185,7 +184,7 @@ static void clamp_attribute(GeometryComponent &component, const GeoNodeExecParam } } MutableSpan<float3> results = attribute_result.as_span<float3>(); - clamp_attribute<float3>(attribute_input->typed<float3>(), results, min, max); + clamp_attribute<float3>(attribute_input.typed<float3>(), results, min, max); break; } case CD_PROP_FLOAT: { @@ -193,10 +192,10 @@ static void clamp_attribute(GeometryComponent &component, const GeoNodeExecParam const float max = params.get_input<float>("Max_001"); MutableSpan<float> results = attribute_result.as_span<float>(); if (operation == NODE_CLAMP_RANGE && min > max) { - clamp_attribute<float>(attribute_input->typed<float>(), results, max, min); + clamp_attribute<float>(attribute_input.typed<float>(), results, max, min); } else { - clamp_attribute<float>(attribute_input->typed<float>(), results, min, max); + clamp_attribute<float>(attribute_input.typed<float>(), results, min, max); } break; } @@ -205,10 +204,10 @@ static void clamp_attribute(GeometryComponent &component, const GeoNodeExecParam const int max = params.get_input<int>("Max_002"); MutableSpan<int> results = attribute_result.as_span<int>(); if (operation == NODE_CLAMP_RANGE && min > max) { - clamp_attribute<int>(attribute_input->typed<int>(), results, max, min); + clamp_attribute<int>(attribute_input.typed<int>(), results, max, min); } else { - clamp_attribute<int>(attribute_input->typed<int>(), results, min, max); + clamp_attribute<int>(attribute_input.typed<int>(), results, min, max); } break; } @@ -231,7 +230,7 @@ static void clamp_attribute(GeometryComponent &component, const GeoNodeExecParam } MutableSpan<ColorGeometry4f> results = attribute_result.as_span<ColorGeometry4f>(); clamp_attribute<ColorGeometry4f>( - attribute_input->typed<ColorGeometry4f>(), results, min, max); + attribute_input.typed<ColorGeometry4f>(), results, min, max); break; } default: { @@ -243,11 +242,11 @@ static void clamp_attribute(GeometryComponent &component, const GeoNodeExecParam attribute_result.save(); } -static void geo_node_attribute_clamp_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - geometry_set = geometry_set_realize_instances(geometry_set); + geometry_set = geometry::realize_instances_legacy(geometry_set); if (geometry_set.has<MeshComponent>()) { clamp_attribute(geometry_set.get_component_for_write<MeshComponent>(), params); @@ -262,19 +261,21 @@ static void geo_node_attribute_clamp_exec(GeoNodeExecParams params) params.set_output("Geometry", geometry_set); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_legacy_attribute_clamp_cc void register_node_type_geo_attribute_clamp() { + namespace file_ns = blender::nodes::node_geo_legacy_attribute_clamp_cc; + static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_LEGACY_ATTRIBUTE_CLAMP, "Attribute Clamp", NODE_CLASS_ATTRIBUTE, 0); - node_type_init(&ntype, blender::nodes::geo_node_attribute_clamp_init); - node_type_update(&ntype, blender::nodes::geo_node_attribute_clamp_update); - ntype.declare = blender::nodes::geo_node_attribute_clamp_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_attribute_clamp_exec; - ntype.draw_buttons = blender::nodes::geo_node_attribute_clamp_layout; + &ntype, GEO_NODE_LEGACY_ATTRIBUTE_CLAMP, "Attribute Clamp", NODE_CLASS_ATTRIBUTE); + node_type_init(&ntype, file_ns::node_init); + node_type_update(&ntype, file_ns::node_update); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.draw_buttons = file_ns::node_layout; node_type_storage( &ntype, "NodeAttributeClamp", node_free_standard_storage, node_copy_standard_storage); nodeRegisterType(&ntype); diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_color_ramp.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_color_ramp.cc index aa054af3acd..ec57422a531 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_color_ramp.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_color_ramp.cc @@ -23,27 +23,24 @@ #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_legacy_attribute_color_ramp_cc { -static void geo_node_attribute_color_ramp_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::String>("Attribute"); - b.add_input<decl::String>("Result"); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::String>(N_("Attribute")); + b.add_input<decl::String>(N_("Result")); + b.add_output<decl::Geometry>(N_("Geometry")); } -static void geo_node_attribute_color_ramp_layout(uiLayout *layout, - bContext *UNUSED(C), - PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiTemplateColorRamp(layout, ptr, "color_ramp", false); } -static void geo_node_attribute_color_ramp_init(bNodeTree *UNUSED(ntree), bNode *node) +static void node_init(bNodeTree *UNUSED(ntree), bNode *node) { - NodeAttributeColorRamp *node_storage = (NodeAttributeColorRamp *)MEM_callocN( - sizeof(NodeAttributeColorRamp), __func__); + NodeAttributeColorRamp *node_storage = MEM_cnew<NodeAttributeColorRamp>(__func__); BKE_colorband_init(&node_storage->color_ramp, true); node->storage = node_storage; } @@ -85,7 +82,7 @@ static void execute_on_component(const GeoNodeExecParams ¶ms, GeometryCompon return; } - GVArray_Typed<float> attribute_in = component.attribute_get_for_read<float>( + VArray<float> attribute_in = component.attribute_get_for_read<float>( input_name, result_domain, 0.0f); MutableSpan<ColorGeometry4f> results = attribute_result.as_span(); @@ -100,11 +97,11 @@ static void execute_on_component(const GeoNodeExecParams ¶ms, GeometryCompon attribute_result.save(); } -static void geo_node_attribute_color_ramp_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - geometry_set = geometry_set_realize_instances(geometry_set); + geometry_set = geometry::realize_instances_legacy(geometry_set); if (geometry_set.has<MeshComponent>()) { execute_on_component(params, geometry_set.get_component_for_write<MeshComponent>()); @@ -119,23 +116,22 @@ static void geo_node_attribute_color_ramp_exec(GeoNodeExecParams params) params.set_output("Geometry", std::move(geometry_set)); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_legacy_attribute_color_ramp_cc void register_node_type_geo_attribute_color_ramp() { + namespace file_ns = blender::nodes::node_geo_legacy_attribute_color_ramp_cc; + static bNodeType ntype; - geo_node_type_base(&ntype, - GEO_NODE_LEGACY_ATTRIBUTE_COLOR_RAMP, - "Attribute Color Ramp", - NODE_CLASS_ATTRIBUTE, - 0); + geo_node_type_base( + &ntype, GEO_NODE_LEGACY_ATTRIBUTE_COLOR_RAMP, "Attribute Color Ramp", NODE_CLASS_ATTRIBUTE); node_type_storage( &ntype, "NodeAttributeColorRamp", node_free_standard_storage, node_copy_standard_storage); - node_type_init(&ntype, blender::nodes::geo_node_attribute_color_ramp_init); + node_type_init(&ntype, file_ns::node_init); node_type_size_preset(&ntype, NODE_SIZE_LARGE); - ntype.declare = blender::nodes::geo_node_attribute_color_ramp_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_attribute_color_ramp_exec; - ntype.draw_buttons = blender::nodes::geo_node_attribute_color_ramp_layout; + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.draw_buttons = file_ns::node_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_combine_xyz.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_combine_xyz.cc index 569d5a824ca..403b9446f75 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_combine_xyz.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_combine_xyz.cc @@ -19,24 +19,22 @@ #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_legacy_attribute_combine_xyz_cc { -static void geo_node_attribute_combine_xyz_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::String>("X"); - b.add_input<decl::Float>("X", "X_001"); - b.add_input<decl::String>("Y"); - b.add_input<decl::Float>("Y", "Y_001"); - b.add_input<decl::String>("Z"); - b.add_input<decl::Float>("Z", "Z_001"); - b.add_input<decl::String>("Result"); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::String>(N_("X")); + b.add_input<decl::Float>(N_("X"), "X_001"); + b.add_input<decl::String>(N_("Y")); + b.add_input<decl::Float>(N_("Y"), "Y_001"); + b.add_input<decl::String>(N_("Z")); + b.add_input<decl::Float>(N_("Z"), "Z_001"); + b.add_input<decl::String>(N_("Result")); + b.add_output<decl::Geometry>(N_("Geometry")); } -static void geo_node_attribute_combine_xyz_layout(uiLayout *layout, - bContext *UNUSED(C), - PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); @@ -46,10 +44,9 @@ static void geo_node_attribute_combine_xyz_layout(uiLayout *layout, uiItemR(col, ptr, "input_type_z", 0, IFACE_("Z"), ICON_NONE); } -static void geo_node_attribute_combine_xyz_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeAttributeCombineXYZ *data = (NodeAttributeCombineXYZ *)MEM_callocN( - sizeof(NodeAttributeCombineXYZ), __func__); + NodeAttributeCombineXYZ *data = MEM_cnew<NodeAttributeCombineXYZ>(__func__); data->input_type_x = GEO_NODE_ATTRIBUTE_INPUT_FLOAT; data->input_type_y = GEO_NODE_ATTRIBUTE_INPUT_FLOAT; @@ -57,15 +54,15 @@ static void geo_node_attribute_combine_xyz_init(bNodeTree *UNUSED(tree), bNode * node->storage = data; } -static void geo_node_attribute_combine_xyz_update(bNodeTree *UNUSED(ntree), bNode *node) +static void node_update(bNodeTree *ntree, bNode *node) { NodeAttributeCombineXYZ *node_storage = (NodeAttributeCombineXYZ *)node->storage; update_attribute_input_socket_availabilities( - *node, "X", (GeometryNodeAttributeInputMode)node_storage->input_type_x); + *ntree, *node, "X", (GeometryNodeAttributeInputMode)node_storage->input_type_x); update_attribute_input_socket_availabilities( - *node, "Y", (GeometryNodeAttributeInputMode)node_storage->input_type_y); + *ntree, *node, "Y", (GeometryNodeAttributeInputMode)node_storage->input_type_y); update_attribute_input_socket_availabilities( - *node, "Z", (GeometryNodeAttributeInputMode)node_storage->input_type_z); + *ntree, *node, "Z", (GeometryNodeAttributeInputMode)node_storage->input_type_z); } static AttributeDomain get_result_domain(const GeometryComponent &component, @@ -95,11 +92,11 @@ static void combine_attributes(GeometryComponent &component, const GeoNodeExecPa if (!attribute_result) { return; } - GVArray_Typed<float> attribute_x = params.get_input_attribute<float>( + VArray<float> attribute_x = params.get_input_attribute<float>( "X", component, result_domain, 0.0f); - GVArray_Typed<float> attribute_y = params.get_input_attribute<float>( + VArray<float> attribute_y = params.get_input_attribute<float>( "Y", component, result_domain, 0.0f); - GVArray_Typed<float> attribute_z = params.get_input_attribute<float>( + VArray<float> attribute_z = params.get_input_attribute<float>( "Z", component, result_domain, 0.0f); for (const int i : IndexRange(attribute_result->size())) { @@ -111,11 +108,11 @@ static void combine_attributes(GeometryComponent &component, const GeoNodeExecPa attribute_result.save(); } -static void geo_node_attribute_combine_xyz_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - geometry_set = geometry_set_realize_instances(geometry_set); + geometry_set = geometry::realize_instances_legacy(geometry_set); if (geometry_set.has<MeshComponent>()) { combine_attributes(geometry_set.get_component_for_write<MeshComponent>(), params); @@ -130,24 +127,25 @@ static void geo_node_attribute_combine_xyz_exec(GeoNodeExecParams params) params.set_output("Geometry", geometry_set); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_legacy_attribute_combine_xyz_cc void register_node_type_geo_attribute_combine_xyz() { + namespace file_ns = blender::nodes::node_geo_legacy_attribute_combine_xyz_cc; + static bNodeType ntype; geo_node_type_base(&ntype, GEO_NODE_LEGACY_ATTRIBUTE_COMBINE_XYZ, "Attribute Combine XYZ", - NODE_CLASS_ATTRIBUTE, - 0); - node_type_init(&ntype, blender::nodes::geo_node_attribute_combine_xyz_init); - node_type_update(&ntype, blender::nodes::geo_node_attribute_combine_xyz_update); + NODE_CLASS_ATTRIBUTE); + node_type_init(&ntype, file_ns::node_init); + node_type_update(&ntype, file_ns::node_update); node_type_storage( &ntype, "NodeAttributeCombineXYZ", node_free_standard_storage, node_copy_standard_storage); - ntype.declare = blender::nodes::geo_node_attribute_combine_xyz_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_attribute_combine_xyz_exec; - ntype.draw_buttons = blender::nodes::geo_node_attribute_combine_xyz_layout; + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.draw_buttons = file_ns::node_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_compare.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_compare.cc index 0b9708dae14..6cec73d76a2 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_compare.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_compare.cc @@ -21,27 +21,25 @@ #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_legacy_attribute_compare_cc { -static void geo_node_attribute_compare_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::String>("A"); - b.add_input<decl::Float>("A", "A_001"); - b.add_input<decl::Vector>("A", "A_002"); - b.add_input<decl::Color>("A", "A_003").default_value({0.5, 0.5, 0.5, 1.0}); - b.add_input<decl::String>("B"); - b.add_input<decl::Float>("B", "B_001"); - b.add_input<decl::Vector>("B", "B_002"); - b.add_input<decl::Color>("B", "B_003").default_value({0.5, 0.5, 0.5, 1.0}); - b.add_input<decl::Float>("Threshold").default_value(0.01f).min(0.0f); - b.add_input<decl::String>("Result"); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::String>(N_("A")); + b.add_input<decl::Float>(N_("A"), "A_001"); + b.add_input<decl::Vector>(N_("A"), "A_002"); + b.add_input<decl::Color>(N_("A"), "A_003").default_value({0.5, 0.5, 0.5, 1.0}); + b.add_input<decl::String>(N_("B")); + b.add_input<decl::Float>(N_("B"), "B_001"); + b.add_input<decl::Vector>(N_("B"), "B_002"); + b.add_input<decl::Color>(N_("B"), "B_003").default_value({0.5, 0.5, 0.5, 1.0}); + b.add_input<decl::Float>(N_("Threshold")).default_value(0.01f).min(0.0f); + b.add_input<decl::String>(N_("Result")); + b.add_output<decl::Geometry>(N_("Geometry")); } -static void geo_node_attribute_compare_layout(uiLayout *layout, - bContext *UNUSED(C), - PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "operation", 0, "", ICON_NONE); uiLayoutSetPropSep(layout, true); @@ -50,11 +48,10 @@ static void geo_node_attribute_compare_layout(uiLayout *layout, uiItemR(layout, ptr, "input_type_b", 0, IFACE_("B"), ICON_NONE); } -static void geo_node_attribute_compare_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeAttributeCompare *data = (NodeAttributeCompare *)MEM_callocN(sizeof(NodeAttributeCompare), - __func__); - data->operation = NODE_FLOAT_COMPARE_GREATER_THAN; + NodeAttributeCompare *data = MEM_cnew<NodeAttributeCompare>(__func__); + data->operation = NODE_COMPARE_GREATER_THAN; data->input_type_a = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE; data->input_type_b = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE; node->storage = data; @@ -62,24 +59,24 @@ static void geo_node_attribute_compare_init(bNodeTree *UNUSED(tree), bNode *node static bool operation_tests_equality(const NodeAttributeCompare &node_storage) { - return ELEM(node_storage.operation, NODE_FLOAT_COMPARE_EQUAL, NODE_FLOAT_COMPARE_NOT_EQUAL); + return ELEM(node_storage.operation, NODE_COMPARE_EQUAL, NODE_COMPARE_NOT_EQUAL); } -static void geo_node_attribute_compare_update(bNodeTree *UNUSED(ntree), bNode *node) +static void node_update(bNodeTree *ntree, bNode *node) { NodeAttributeCompare *node_storage = (NodeAttributeCompare *)node->storage; update_attribute_input_socket_availabilities( - *node, "A", (GeometryNodeAttributeInputMode)node_storage->input_type_a); + *ntree, *node, "A", (GeometryNodeAttributeInputMode)node_storage->input_type_a); update_attribute_input_socket_availabilities( - *node, "B", (GeometryNodeAttributeInputMode)node_storage->input_type_b); + *ntree, *node, "B", (GeometryNodeAttributeInputMode)node_storage->input_type_b); bNodeSocket *socket_threshold = (bNodeSocket *)BLI_findlink(&node->inputs, 9); - nodeSetSocketAvailability(socket_threshold, operation_tests_equality(*node_storage)); + nodeSetSocketAvailability(ntree, socket_threshold, operation_tests_equality(*node_storage)); } static void do_math_operation(const VArray<float> &input_a, const VArray<float> &input_b, - const FloatCompareOperation operation, + const NodeCompareOperation operation, MutableSpan<bool> span_result) { const int size = input_a.size(); @@ -243,7 +240,7 @@ static void attribute_compare_calc(GeometryComponent &component, const GeoNodeEx { const bNode &node = params.node(); NodeAttributeCompare *node_storage = (NodeAttributeCompare *)node.storage; - const FloatCompareOperation operation = static_cast<FloatCompareOperation>( + const NodeCompareOperation operation = static_cast<NodeCompareOperation>( node_storage->operation); const std::string result_name = params.get_input<std::string>("Result"); @@ -257,9 +254,9 @@ static void attribute_compare_calc(GeometryComponent &component, const GeoNodeEx const CustomDataType input_data_type = get_data_type(component, params, *node_storage); - GVArrayPtr attribute_a = params.get_input_attribute( + GVArray attribute_a = params.get_input_attribute( "A", component, result_domain, input_data_type, nullptr); - GVArrayPtr attribute_b = params.get_input_attribute( + GVArray attribute_b = params.get_input_attribute( "B", component, result_domain, input_data_type, nullptr); if (!attribute_a || !attribute_b) { @@ -273,60 +270,60 @@ static void attribute_compare_calc(GeometryComponent &component, const GeoNodeEx * conversions and float comparison. In other words, the comparison is not element-wise. */ if (operation_tests_equality(*node_storage)) { const float threshold = params.get_input<float>("Threshold"); - if (operation == NODE_FLOAT_COMPARE_EQUAL) { + if (operation == NODE_COMPARE_EQUAL) { if (input_data_type == CD_PROP_FLOAT) { do_equal_operation_float( - attribute_a->typed<float>(), attribute_b->typed<float>(), threshold, result_span); + attribute_a.typed<float>(), attribute_b.typed<float>(), threshold, result_span); } else if (input_data_type == CD_PROP_FLOAT3) { do_equal_operation_float3( - attribute_a->typed<float3>(), attribute_b->typed<float3>(), threshold, result_span); + attribute_a.typed<float3>(), attribute_b.typed<float3>(), threshold, result_span); } else if (input_data_type == CD_PROP_COLOR) { - do_equal_operation_color4f(attribute_a->typed<ColorGeometry4f>(), - attribute_b->typed<ColorGeometry4f>(), + do_equal_operation_color4f(attribute_a.typed<ColorGeometry4f>(), + attribute_b.typed<ColorGeometry4f>(), threshold, result_span); } else if (input_data_type == CD_PROP_BOOL) { do_equal_operation_bool( - attribute_a->typed<bool>(), attribute_b->typed<bool>(), threshold, result_span); + attribute_a.typed<bool>(), attribute_b.typed<bool>(), threshold, result_span); } } - else if (operation == NODE_FLOAT_COMPARE_NOT_EQUAL) { + else if (operation == NODE_COMPARE_NOT_EQUAL) { if (input_data_type == CD_PROP_FLOAT) { do_not_equal_operation_float( - attribute_a->typed<float>(), attribute_b->typed<float>(), threshold, result_span); + attribute_a.typed<float>(), attribute_b.typed<float>(), threshold, result_span); } else if (input_data_type == CD_PROP_FLOAT3) { do_not_equal_operation_float3( - attribute_a->typed<float3>(), attribute_b->typed<float3>(), threshold, result_span); + attribute_a.typed<float3>(), attribute_b.typed<float3>(), threshold, result_span); } else if (input_data_type == CD_PROP_COLOR) { - do_not_equal_operation_color4f(attribute_a->typed<ColorGeometry4f>(), - attribute_b->typed<ColorGeometry4f>(), + do_not_equal_operation_color4f(attribute_a.typed<ColorGeometry4f>(), + attribute_b.typed<ColorGeometry4f>(), threshold, result_span); } else if (input_data_type == CD_PROP_BOOL) { do_not_equal_operation_bool( - attribute_a->typed<bool>(), attribute_b->typed<bool>(), threshold, result_span); + attribute_a.typed<bool>(), attribute_b.typed<bool>(), threshold, result_span); } } } else { do_math_operation( - attribute_a->typed<float>(), attribute_b->typed<float>(), operation, result_span); + attribute_a.typed<float>(), attribute_b.typed<float>(), operation, result_span); } attribute_result.save(); } -static void geo_node_attribute_compare_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - geometry_set = geometry_set_realize_instances(geometry_set); + geometry_set = geometry::realize_instances_legacy(geometry_set); if (geometry_set.has<MeshComponent>()) { attribute_compare_calc(geometry_set.get_component_for_write<MeshComponent>(), params); @@ -341,20 +338,22 @@ static void geo_node_attribute_compare_exec(GeoNodeExecParams params) params.set_output("Geometry", geometry_set); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_legacy_attribute_compare_cc void register_node_type_geo_attribute_compare() { + namespace file_ns = blender::nodes::node_geo_legacy_attribute_compare_cc; + static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_LEGACY_ATTRIBUTE_COMPARE, "Attribute Compare", NODE_CLASS_ATTRIBUTE, 0); - ntype.declare = blender::nodes::geo_node_attribute_compare_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_attribute_compare_exec; - ntype.draw_buttons = blender::nodes::geo_node_attribute_compare_layout; - node_type_update(&ntype, blender::nodes::geo_node_attribute_compare_update); + &ntype, GEO_NODE_LEGACY_ATTRIBUTE_COMPARE, "Attribute Compare", NODE_CLASS_ATTRIBUTE); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.draw_buttons = file_ns::node_layout; + node_type_update(&ntype, file_ns::node_update); node_type_storage( &ntype, "NodeAttributeCompare", node_free_standard_storage, node_copy_standard_storage); - node_type_init(&ntype, blender::nodes::geo_node_attribute_compare_init); + node_type_init(&ntype, file_ns::node_init); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_convert.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_convert.cc index a2382aa9d25..2b13f57e990 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_convert.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_convert.cc @@ -19,19 +19,17 @@ #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_legacy_attribute_convert_cc { -static void geo_node_attribute_convert_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::String>("Attribute"); - b.add_input<decl::String>("Result"); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::String>(N_("Attribute")); + b.add_input<decl::String>(N_("Result")); + b.add_output<decl::Geometry>(N_("Geometry")); } -static void geo_node_attribute_convert_layout(uiLayout *layout, - bContext *UNUSED(C), - PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); @@ -39,10 +37,9 @@ static void geo_node_attribute_convert_layout(uiLayout *layout, uiItemR(layout, ptr, "data_type", 0, IFACE_("Type"), ICON_NONE); } -static void geo_node_attribute_convert_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeAttributeConvert *data = (NodeAttributeConvert *)MEM_callocN(sizeof(NodeAttributeConvert), - __func__); + NodeAttributeConvert *data = MEM_cnew<NodeAttributeConvert>(__func__); data->data_type = CD_AUTO_FROM_NAME; data->domain = ATTR_DOMAIN_AUTO; @@ -104,7 +101,7 @@ static void attribute_convert_calc(GeometryComponent &component, return; } - GVArrayPtr source_attribute = component.attribute_try_get_for_read( + GVArray source_attribute = component.attribute_try_get_for_read( source_name, result_domain, result_type); if (!source_attribute) { params.error_message_add(NodeWarningType::Error, @@ -118,7 +115,7 @@ static void attribute_convert_calc(GeometryComponent &component, return; } - GVArray_GSpan source_span{*source_attribute}; + GVArray_GSpan source_span{source_attribute}; GMutableSpan result_span = result_attribute.as_span(); BLI_assert(source_span.size() == result_span.size()); @@ -130,11 +127,11 @@ static void attribute_convert_calc(GeometryComponent &component, result_attribute.save(); } -static void geo_node_attribute_convert_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - geometry_set = geometry_set_realize_instances(geometry_set); + geometry_set = geometry::realize_instances_legacy(geometry_set); const std::string result_name = params.extract_input<std::string>("Result"); const std::string source_name = params.extract_input<std::string>("Attribute"); @@ -143,7 +140,7 @@ static void geo_node_attribute_convert_exec(GeoNodeExecParams params) const AttributeDomain domain = static_cast<AttributeDomain>(node_storage.domain); if (result_name.empty()) { - params.set_output("Geometry", geometry_set); + params.set_default_remaining_outputs(); return; } @@ -175,18 +172,20 @@ static void geo_node_attribute_convert_exec(GeoNodeExecParams params) params.set_output("Geometry", geometry_set); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_legacy_attribute_convert_cc void register_node_type_geo_attribute_convert() { + namespace file_ns = blender::nodes::node_geo_legacy_attribute_convert_cc; + static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_LEGACY_ATTRIBUTE_CONVERT, "Attribute Convert", NODE_CLASS_ATTRIBUTE, 0); - ntype.declare = blender::nodes::geo_node_attribute_convert_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_attribute_convert_exec; - ntype.draw_buttons = blender::nodes::geo_node_attribute_convert_layout; - node_type_init(&ntype, blender::nodes::geo_node_attribute_convert_init); + &ntype, GEO_NODE_LEGACY_ATTRIBUTE_CONVERT, "Attribute Convert", NODE_CLASS_ATTRIBUTE); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.draw_buttons = file_ns::node_layout; + node_type_init(&ntype, file_ns::node_init); node_type_storage( &ntype, "NodeAttributeConvert", node_free_standard_storage, node_copy_standard_storage); diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_curve_map.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_curve_map.cc index b9621b4ae92..8e1e763f1ad 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_curve_map.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_curve_map.cc @@ -24,19 +24,17 @@ #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_legacy_attribute_curve_map_cc { -static void geo_node_attribute_curve_map_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::String>("Attribute"); - b.add_input<decl::String>("Result"); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::String>(N_("Attribute")); + b.add_input<decl::String>(N_("Result")); + b.add_output<decl::Geometry>(N_("Geometry")); } -static void geo_node_attribute_curve_map_layout(uiLayout *layout, - bContext *UNUSED(C), - PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); bNode *node = (bNode *)ptr->data; @@ -54,7 +52,7 @@ static void geo_node_attribute_curve_map_layout(uiLayout *layout, } } -static void geo_node_attribute_curve_map_free_storage(bNode *node) +static void node_free_storage(bNode *node) { if (node->storage) { NodeAttributeCurveMap *data = (NodeAttributeCurveMap *)node->storage; @@ -64,9 +62,9 @@ static void geo_node_attribute_curve_map_free_storage(bNode *node) } } -static void geo_node_attribute_curve_map_copy_storage(bNodeTree *UNUSED(dest_ntree), - bNode *dest_node, - const bNode *src_node) +static void node_copy_storage(bNodeTree *UNUSED(dest_ntree), + bNode *dest_node, + const bNode *src_node) { dest_node->storage = MEM_dupallocN(src_node->storage); NodeAttributeCurveMap *src_data = (NodeAttributeCurveMap *)src_node->storage; @@ -75,10 +73,9 @@ static void geo_node_attribute_curve_map_copy_storage(bNodeTree *UNUSED(dest_ntr dest_data->curve_rgb = BKE_curvemapping_copy(src_data->curve_rgb); } -static void geo_node_attribute_curve_map_init(bNodeTree *UNUSED(ntree), bNode *node) +static void node_init(bNodeTree *UNUSED(ntree), bNode *node) { - NodeAttributeCurveMap *data = (NodeAttributeCurveMap *)MEM_callocN(sizeof(NodeAttributeCurveMap), - __func__); + NodeAttributeCurveMap *data = MEM_cnew<NodeAttributeCurveMap>(__func__); data->data_type = CD_PROP_FLOAT; data->curve_vec = BKE_curvemapping_add(4, -1.0f, -1.0f, 1.0f, 1.0f); @@ -87,7 +84,7 @@ static void geo_node_attribute_curve_map_init(bNodeTree *UNUSED(ntree), bNode *n node->storage = data; } -static void geo_node_attribute_curve_map_update(bNodeTree *UNUSED(ntree), bNode *node) +static void node_update(bNodeTree *UNUSED(ntree), bNode *node) { /* Set the active curve when data type is changed. */ NodeAttributeCurveMap *data = (NodeAttributeCurveMap *)node->storage; @@ -136,10 +133,10 @@ static void execute_on_component(const GeoNodeExecParams ¶ms, GeometryCompon switch (result_type) { case CD_PROP_FLOAT: { const CurveMapping *cumap = (CurveMapping *)node_storage.curve_vec; - GVArray_Typed<float> attribute_in = component.attribute_get_for_read<float>( + VArray<float> attribute_in = component.attribute_get_for_read<float>( input_name, result_domain, float(0.0f)); MutableSpan<float> results = attribute_result.as_span<float>(); - threading::parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) { + threading::parallel_for(attribute_in.index_range(), 512, [&](IndexRange range) { for (const int i : range) { results[i] = BKE_curvemapping_evaluateF(cumap, 3, attribute_in[i]); } @@ -148,10 +145,10 @@ static void execute_on_component(const GeoNodeExecParams ¶ms, GeometryCompon } case CD_PROP_FLOAT3: { const CurveMapping *cumap = (CurveMapping *)node_storage.curve_vec; - GVArray_Typed<float3> attribute_in = component.attribute_get_for_read<float3>( + VArray<float3> attribute_in = component.attribute_get_for_read<float3>( input_name, result_domain, float3(0.0f)); MutableSpan<float3> results = attribute_result.as_span<float3>(); - threading::parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) { + threading::parallel_for(attribute_in.index_range(), 512, [&](IndexRange range) { for (const int i : range) { BKE_curvemapping_evaluate3F(cumap, results[i], attribute_in[i]); } @@ -160,11 +157,10 @@ static void execute_on_component(const GeoNodeExecParams ¶ms, GeometryCompon } case CD_PROP_COLOR: { const CurveMapping *cumap = (CurveMapping *)node_storage.curve_rgb; - GVArray_Typed<ColorGeometry4f> attribute_in = - component.attribute_get_for_read<ColorGeometry4f>( - input_name, result_domain, ColorGeometry4f(0.0f, 0.0f, 0.0f, 1.0f)); + VArray<ColorGeometry4f> attribute_in = component.attribute_get_for_read<ColorGeometry4f>( + input_name, result_domain, ColorGeometry4f(0.0f, 0.0f, 0.0f, 1.0f)); MutableSpan<ColorGeometry4f> results = attribute_result.as_span<ColorGeometry4f>(); - threading::parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) { + threading::parallel_for(attribute_in.index_range(), 512, [&](IndexRange range) { for (const int i : range) { BKE_curvemapping_evaluateRGBF(cumap, results[i], attribute_in[i]); } @@ -180,7 +176,7 @@ static void execute_on_component(const GeoNodeExecParams ¶ms, GeometryCompon attribute_result.save(); } -static void geo_node_attribute_curve_map_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { const bNode &bnode = params.node(); NodeAttributeCurveMap *data = (NodeAttributeCurveMap *)bnode.storage; @@ -189,7 +185,7 @@ static void geo_node_attribute_curve_map_exec(GeoNodeExecParams params) GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - geometry_set = geometry_set_realize_instances(geometry_set); + geometry_set = geometry::realize_instances_legacy(geometry_set); if (geometry_set.has<MeshComponent>()) { execute_on_component(params, geometry_set.get_component_for_write<MeshComponent>()); @@ -204,23 +200,23 @@ static void geo_node_attribute_curve_map_exec(GeoNodeExecParams params) params.set_output("Geometry", std::move(geometry_set)); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_legacy_attribute_curve_map_cc void register_node_type_geo_attribute_curve_map() { + namespace file_ns = blender::nodes::node_geo_legacy_attribute_curve_map_cc; + static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_LEGACY_ATTRIBUTE_CURVE_MAP, "Attribute Curve Map", NODE_CLASS_ATTRIBUTE, 0); - node_type_update(&ntype, blender::nodes::geo_node_attribute_curve_map_update); - node_type_init(&ntype, blender::nodes::geo_node_attribute_curve_map_init); + &ntype, GEO_NODE_LEGACY_ATTRIBUTE_CURVE_MAP, "Attribute Curve Map", NODE_CLASS_ATTRIBUTE); + node_type_update(&ntype, file_ns::node_update); + node_type_init(&ntype, file_ns::node_init); node_type_size_preset(&ntype, NODE_SIZE_LARGE); - node_type_storage(&ntype, - "NodeAttributeCurveMap", - blender::nodes::geo_node_attribute_curve_map_free_storage, - blender::nodes::geo_node_attribute_curve_map_copy_storage); - ntype.declare = blender::nodes::geo_node_attribute_curve_map_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_attribute_curve_map_exec; - ntype.draw_buttons = blender::nodes::geo_node_attribute_curve_map_layout; + node_type_storage( + &ntype, "NodeAttributeCurveMap", file_ns::node_free_storage, file_ns::node_copy_storage); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.draw_buttons = file_ns::node_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_fill.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_fill.cc index 3c50ae5c837..a32e3b7412f 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_fill.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_fill.cc @@ -19,21 +19,21 @@ #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_legacy_attribute_fill_cc { -static void geo_node_attribute_fill_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::String>("Attribute"); - b.add_input<decl::Vector>("Value", "Value"); - b.add_input<decl::Float>("Value", "Value_001"); - b.add_input<decl::Color>("Value", "Value_002"); - b.add_input<decl::Bool>("Value", "Value_003"); - b.add_input<decl::Int>("Value", "Value_004"); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::String>(N_("Attribute")).is_attribute_name(); + b.add_input<decl::Vector>(N_("Value"), "Value"); + b.add_input<decl::Float>(N_("Value"), "Value_001"); + b.add_input<decl::Color>(N_("Value"), "Value_002"); + b.add_input<decl::Bool>(N_("Value"), "Value_003"); + b.add_input<decl::Int>(N_("Value"), "Value_004"); + b.add_output<decl::Geometry>(N_("Geometry")); } -static void geo_node_attribute_fill_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); @@ -41,13 +41,13 @@ static void geo_node_attribute_fill_layout(uiLayout *layout, bContext *UNUSED(C) uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); } -static void geo_node_attribute_fill_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree *UNUSED(tree), bNode *node) { node->custom1 = CD_PROP_FLOAT; node->custom2 = ATTR_DOMAIN_AUTO; } -static void geo_node_attribute_fill_update(bNodeTree *UNUSED(ntree), bNode *node) +static void node_update(bNodeTree *ntree, bNode *node) { bNodeSocket *socket_value_vector = (bNodeSocket *)BLI_findlink(&node->inputs, 2); bNodeSocket *socket_value_float = socket_value_vector->next; @@ -57,11 +57,11 @@ static void geo_node_attribute_fill_update(bNodeTree *UNUSED(ntree), bNode *node const CustomDataType data_type = static_cast<CustomDataType>(node->custom1); - nodeSetSocketAvailability(socket_value_vector, data_type == CD_PROP_FLOAT3); - nodeSetSocketAvailability(socket_value_float, data_type == CD_PROP_FLOAT); - nodeSetSocketAvailability(socket_value_color4f, data_type == CD_PROP_COLOR); - nodeSetSocketAvailability(socket_value_boolean, data_type == CD_PROP_BOOL); - nodeSetSocketAvailability(socket_value_int32, data_type == CD_PROP_INT32); + nodeSetSocketAvailability(ntree, socket_value_vector, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(ntree, socket_value_float, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(ntree, socket_value_color4f, data_type == CD_PROP_COLOR); + nodeSetSocketAvailability(ntree, socket_value_boolean, data_type == CD_PROP_BOOL); + nodeSetSocketAvailability(ntree, socket_value_int32, data_type == CD_PROP_INT32); } static AttributeDomain get_result_domain(const GeometryComponent &component, const StringRef name) @@ -127,11 +127,11 @@ static void fill_attribute(GeometryComponent &component, const GeoNodeExecParams attribute.save(); } -static void geo_node_attribute_fill_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - geometry_set = geometry_set_realize_instances(geometry_set); + geometry_set = geometry::realize_instances_legacy(geometry_set); if (geometry_set.has<MeshComponent>()) { fill_attribute(geometry_set.get_component_for_write<MeshComponent>(), params); @@ -146,18 +146,20 @@ static void geo_node_attribute_fill_exec(GeoNodeExecParams params) params.set_output("Geometry", geometry_set); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_legacy_attribute_fill_cc void register_node_type_geo_attribute_fill() { + namespace file_ns = blender::nodes::node_geo_legacy_attribute_fill_cc; + static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_LEGACY_ATTRIBUTE_FILL, "Attribute Fill", NODE_CLASS_ATTRIBUTE, 0); - node_type_init(&ntype, blender::nodes::geo_node_attribute_fill_init); - node_type_update(&ntype, blender::nodes::geo_node_attribute_fill_update); - ntype.geometry_node_execute = blender::nodes::geo_node_attribute_fill_exec; - ntype.draw_buttons = blender::nodes::geo_node_attribute_fill_layout; - ntype.declare = blender::nodes::geo_node_attribute_fill_declare; + &ntype, GEO_NODE_LEGACY_ATTRIBUTE_FILL, "Attribute Fill", NODE_CLASS_ATTRIBUTE); + node_type_init(&ntype, file_ns::node_init); + node_type_update(&ntype, file_ns::node_update); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.draw_buttons = file_ns::node_layout; + ntype.declare = file_ns::node_declare; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_map_range.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_map_range.cc index 0ea3bbe1e45..398af087499 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_map_range.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_map_range.cc @@ -22,25 +22,25 @@ #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_legacy_attribute_map_range_cc { -static void geo_node_attribute_map_range_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::String>("Attribute"); - b.add_input<decl::String>("Result"); - b.add_input<decl::Float>("From Min"); - b.add_input<decl::Float>("From Max").default_value(1.0f); - b.add_input<decl::Float>("To Min"); - b.add_input<decl::Float>("To Max").default_value(1.0f); - b.add_input<decl::Float>("Steps").default_value(4.0f); - b.add_input<decl::Vector>("From Min", "From Min_001"); - b.add_input<decl::Vector>("From Max", "From Max_001").default_value({1.0f, 1.0f, 1.0f}); - b.add_input<decl::Vector>("To Min", "To Min_001"); - b.add_input<decl::Vector>("To Max", "To Max_001").default_value({1.0f, 1.0f, 1.0f}); - b.add_input<decl::Vector>("Steps", "Steps_001").default_value({4.0f, 4.0f, 4.0f}); - b.add_input<decl::Bool>("Clamp"); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::String>(N_("Attribute")); + b.add_input<decl::String>(N_("Result")); + b.add_input<decl::Float>(N_("From Min")); + b.add_input<decl::Float>(N_("From Max")).default_value(1.0f); + b.add_input<decl::Float>(N_("To Min")); + b.add_input<decl::Float>(N_("To Max")).default_value(1.0f); + b.add_input<decl::Float>(N_("Steps")).default_value(4.0f); + b.add_input<decl::Vector>(N_("From Min"), "From Min_001"); + b.add_input<decl::Vector>(N_("From Max"), "From Max_001").default_value({1.0f, 1.0f, 1.0f}); + b.add_input<decl::Vector>(N_("To Min"), "To Min_001"); + b.add_input<decl::Vector>(N_("To Max"), "To Max_001").default_value({1.0f, 1.0f, 1.0f}); + b.add_input<decl::Vector>(N_("Steps"), "Steps_001").default_value({4.0f, 4.0f, 4.0f}); + b.add_input<decl::Bool>(N_("Clamp")); + b.add_output<decl::Geometry>(N_("Geometry")); } static void fn_attribute_map_range_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -49,16 +49,15 @@ static void fn_attribute_map_range_layout(uiLayout *layout, bContext *UNUSED(C), uiItemR(layout, ptr, "interpolation_type", 0, "", ICON_NONE); } -static void geo_node_attribute_map_range_init(bNodeTree *UNUSED(ntree), bNode *node) +static void node_init(bNodeTree *UNUSED(ntree), bNode *node) { - NodeAttributeMapRange *data = (NodeAttributeMapRange *)MEM_callocN(sizeof(NodeAttributeMapRange), - __func__); + NodeAttributeMapRange *data = MEM_cnew<NodeAttributeMapRange>(__func__); data->data_type = CD_PROP_FLOAT; data->interpolation_type = NODE_MAP_RANGE_LINEAR; node->storage = data; } -static void geo_node_attribute_map_range_update(bNodeTree *UNUSED(ntree), bNode *node) +static void node_update(bNodeTree *ntree, bNode *node) { NodeAttributeMapRange &node_storage = *(NodeAttributeMapRange *)node->storage; @@ -78,23 +77,26 @@ static void geo_node_attribute_map_range_update(bNodeTree *UNUSED(ntree), bNode const CustomDataType data_type = static_cast<CustomDataType>(node_storage.data_type); - nodeSetSocketAvailability(sock_clamp, + nodeSetSocketAvailability(ntree, + sock_clamp, node_storage.interpolation_type == NODE_MAP_RANGE_LINEAR || node_storage.interpolation_type == NODE_MAP_RANGE_STEPPED); - nodeSetSocketAvailability(sock_from_min_float, data_type == CD_PROP_FLOAT); - nodeSetSocketAvailability(sock_from_max_float, data_type == CD_PROP_FLOAT); - nodeSetSocketAvailability(sock_to_min_float, data_type == CD_PROP_FLOAT); - nodeSetSocketAvailability(sock_to_max_float, data_type == CD_PROP_FLOAT); - nodeSetSocketAvailability(sock_steps_float, + nodeSetSocketAvailability(ntree, sock_from_min_float, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(ntree, sock_from_max_float, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(ntree, sock_to_min_float, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(ntree, sock_to_max_float, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(ntree, + sock_steps_float, data_type == CD_PROP_FLOAT && node_storage.interpolation_type == NODE_MAP_RANGE_STEPPED); - nodeSetSocketAvailability(sock_from_min_vector, data_type == CD_PROP_FLOAT3); - nodeSetSocketAvailability(sock_from_max_vector, data_type == CD_PROP_FLOAT3); - nodeSetSocketAvailability(sock_to_min_vector, data_type == CD_PROP_FLOAT3); - nodeSetSocketAvailability(sock_to_max_vector, data_type == CD_PROP_FLOAT3); - nodeSetSocketAvailability(sock_steps_vector, + nodeSetSocketAvailability(ntree, sock_from_min_vector, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(ntree, sock_from_max_vector, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(ntree, sock_to_min_vector, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(ntree, sock_to_max_vector, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(ntree, + sock_steps_vector, data_type == CD_PROP_FLOAT3 && node_storage.interpolation_type == NODE_MAP_RANGE_STEPPED); } @@ -362,7 +364,7 @@ static void map_range_attribute(GeometryComponent &component, const GeoNodeExecP const AttributeDomain domain = get_result_domain(component, input_name, result_name); - GVArrayPtr attribute_input = component.attribute_try_get_for_read(input_name, domain, data_type); + GVArray attribute_input = component.attribute_try_get_for_read(input_name, domain, data_type); if (!attribute_input) { params.error_message_add(NodeWarningType::Error, @@ -381,12 +383,12 @@ static void map_range_attribute(GeometryComponent &component, const GeoNodeExecP switch (data_type) { case CD_PROP_FLOAT: { - map_range_float(attribute_input->typed<float>(), attribute_result.as_span<float>(), params); + map_range_float(attribute_input.typed<float>(), attribute_result.as_span<float>(), params); break; } case CD_PROP_FLOAT3: { map_range_float3( - attribute_input->typed<float3>(), attribute_result.as_span<float3>(), params); + attribute_input.typed<float3>(), attribute_result.as_span<float3>(), params); break; } default: @@ -396,7 +398,7 @@ static void map_range_attribute(GeometryComponent &component, const GeoNodeExecP attribute_result.save(); } -static void geo_node_attribute_map_range_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); @@ -413,20 +415,22 @@ static void geo_node_attribute_map_range_exec(GeoNodeExecParams params) params.set_output("Geometry", geometry_set); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_legacy_attribute_map_range_cc void register_node_type_geo_attribute_map_range() { + namespace file_ns = blender::nodes::node_geo_legacy_attribute_map_range_cc; + static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_LEGACY_ATTRIBUTE_MAP_RANGE, "Attribute Map Range", NODE_CLASS_ATTRIBUTE, 0); - ntype.geometry_node_execute = blender::nodes::geo_node_attribute_map_range_exec; - node_type_init(&ntype, blender::nodes::geo_node_attribute_map_range_init); - node_type_update(&ntype, blender::nodes::geo_node_attribute_map_range_update); + &ntype, GEO_NODE_LEGACY_ATTRIBUTE_MAP_RANGE, "Attribute Map Range", NODE_CLASS_ATTRIBUTE); + ntype.geometry_node_execute = file_ns::node_geo_exec; + node_type_init(&ntype, file_ns::node_init); + node_type_update(&ntype, file_ns::node_update); node_type_storage( &ntype, "NodeAttributeMapRange", node_free_standard_storage, node_copy_standard_storage); - ntype.declare = blender::nodes::geo_node_attribute_map_range_declare; - ntype.draw_buttons = blender::nodes::fn_attribute_map_range_layout; + ntype.declare = file_ns::node_declare; + ntype.draw_buttons = file_ns::fn_attribute_map_range_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_math.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_math.cc index efa09215b45..3257d2d7358 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_math.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_math.cc @@ -25,19 +25,19 @@ #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_legacy_attribute_math_cc { -static void geo_node_attribute_math_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::String>("A"); - b.add_input<decl::Float>("A", "A_001"); - b.add_input<decl::String>("B"); - b.add_input<decl::Float>("B", "B_001"); - b.add_input<decl::String>("C"); - b.add_input<decl::Float>("C", "C_001"); - b.add_input<decl::String>("Result"); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::String>(N_("A")); + b.add_input<decl::Float>(N_("A"), "A_001"); + b.add_input<decl::String>(N_("B")); + b.add_input<decl::Float>(N_("B"), "B_001"); + b.add_input<decl::String>(N_("C")); + b.add_input<decl::Float>(N_("C"), "C_001"); + b.add_input<decl::String>(N_("Result")); + b.add_output<decl::Geometry>(N_("Geometry")); } static bool operation_use_input_c(const NodeMathOperation operation) @@ -100,7 +100,7 @@ static bool operation_use_input_b(const NodeMathOperation operation) return false; } -static void geo_node_attribute_math_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { bNode *node = (bNode *)ptr->data; NodeAttributeMath *node_storage = (NodeAttributeMath *)node->storage; @@ -119,9 +119,9 @@ static void geo_node_attribute_math_layout(uiLayout *layout, bContext *UNUSED(C) } } -static void geo_node_attribute_math_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeAttributeMath *data = (NodeAttributeMath *)MEM_callocN(sizeof(NodeAttributeMath), __func__); + NodeAttributeMath *data = MEM_cnew<NodeAttributeMath>(__func__); data->operation = NODE_MATH_ADD; data->input_type_a = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE; @@ -130,7 +130,10 @@ static void geo_node_attribute_math_init(bNodeTree *UNUSED(tree), bNode *node) node->storage = data; } -static void geo_node_math_label(bNodeTree *UNUSED(ntree), bNode *node, char *label, int maxlen) +static void geo_node_math_label(const bNodeTree *UNUSED(ntree), + const bNode *node, + char *label, + int maxlen) { NodeAttributeMath &node_storage = *(NodeAttributeMath *)node->storage; const char *name; @@ -141,19 +144,21 @@ static void geo_node_math_label(bNodeTree *UNUSED(ntree), bNode *node, char *lab BLI_strncpy(label, IFACE_(name), maxlen); } -static void geo_node_attribute_math_update(bNodeTree *UNUSED(ntree), bNode *node) +static void node_update(bNodeTree *ntree, bNode *node) { NodeAttributeMath &node_storage = *(NodeAttributeMath *)node->storage; NodeMathOperation operation = static_cast<NodeMathOperation>(node_storage.operation); update_attribute_input_socket_availabilities( - *node, "A", (GeometryNodeAttributeInputMode)node_storage.input_type_a); + *ntree, *node, "A", (GeometryNodeAttributeInputMode)node_storage.input_type_a); update_attribute_input_socket_availabilities( + *ntree, *node, "B", (GeometryNodeAttributeInputMode)node_storage.input_type_b, operation_use_input_b(operation)); update_attribute_input_socket_availabilities( + *ntree, *node, "C", (GeometryNodeAttributeInputMode)node_storage.input_type_c, @@ -250,7 +255,7 @@ static void attribute_math_calc(GeometryComponent &component, const GeoNodeExecP return; } - GVArray_Typed<float> attribute_a = params.get_input_attribute<float>( + VArray<float> attribute_a = params.get_input_attribute<float>( "A", component, result_domain, 0.0f); MutableSpan<float> result_span = attribute_result.as_span(); @@ -258,10 +263,10 @@ static void attribute_math_calc(GeometryComponent &component, const GeoNodeExecP /* Note that passing the data with `get_internal_span<float>()` works * because the attributes were accessed with #CD_PROP_FLOAT. */ if (operation_use_input_b(operation)) { - GVArray_Typed<float> attribute_b = params.get_input_attribute<float>( + VArray<float> attribute_b = params.get_input_attribute<float>( "B", component, result_domain, 0.0f); if (operation_use_input_c(operation)) { - GVArray_Typed<float> attribute_c = params.get_input_attribute<float>( + VArray<float> attribute_c = params.get_input_attribute<float>( "C", component, result_domain, 0.0f); do_math_operation(attribute_a, attribute_b, attribute_c, result_span, operation); } @@ -276,11 +281,11 @@ static void attribute_math_calc(GeometryComponent &component, const GeoNodeExecP attribute_result.save(); } -static void geo_node_attribute_math_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - geometry_set = geometry_set_realize_instances(geometry_set); + geometry_set = geometry::realize_instances_legacy(geometry_set); if (geometry_set.has<MeshComponent>()) { attribute_math_calc(geometry_set.get_component_for_write<MeshComponent>(), params); @@ -295,20 +300,22 @@ static void geo_node_attribute_math_exec(GeoNodeExecParams params) params.set_output("Geometry", geometry_set); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_legacy_attribute_math_cc void register_node_type_geo_attribute_math() { + namespace file_ns = blender::nodes::node_geo_legacy_attribute_math_cc; + static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_LEGACY_ATTRIBUTE_MATH, "Attribute Math", NODE_CLASS_ATTRIBUTE, 0); - ntype.declare = blender::nodes::geo_node_attribute_math_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_attribute_math_exec; - ntype.draw_buttons = blender::nodes::geo_node_attribute_math_layout; - node_type_label(&ntype, blender::nodes::geo_node_math_label); - node_type_update(&ntype, blender::nodes::geo_node_attribute_math_update); - node_type_init(&ntype, blender::nodes::geo_node_attribute_math_init); + &ntype, GEO_NODE_LEGACY_ATTRIBUTE_MATH, "Attribute Math", NODE_CLASS_ATTRIBUTE); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.draw_buttons = file_ns::node_layout; + ntype.labelfunc = file_ns::geo_node_math_label; + node_type_update(&ntype, file_ns::node_update); + node_type_init(&ntype, file_ns::node_init); node_type_storage( &ntype, "NodeAttributeMath", node_free_standard_storage, node_copy_standard_storage); nodeRegisterType(&ntype); diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_mix.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_mix.cc index 74e05cb997d..c0c30898584 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_mix.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_mix.cc @@ -25,30 +25,30 @@ #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_legacy_attribute_mix_cc { -static void geo_node_mix_attribute_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::String>("Factor"); - b.add_input<decl::Float>("Factor", "Factor_001") + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::String>(N_("Factor")); + b.add_input<decl::Float>(N_("Factor"), "Factor_001") .default_value(0.5f) .min(0.0f) .max(1.0f) .subtype(PROP_FACTOR); - b.add_input<decl::String>("A"); - b.add_input<decl::Float>("A", "A_001"); - b.add_input<decl::Vector>("A", "A_002"); - b.add_input<decl::Color>("A", "A_003").default_value({0.5f, 0.5f, 0.5f, 1.0f}); - b.add_input<decl::String>("B"); - b.add_input<decl::Float>("B", "B_001"); - b.add_input<decl::Vector>("B", "B_002"); - b.add_input<decl::Color>("B", "B_003").default_value({0.5f, 0.5f, 0.5f, 1.0f}); - b.add_input<decl::String>("Result"); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::String>(N_("A")); + b.add_input<decl::Float>(N_("A"), "A_001"); + b.add_input<decl::Vector>(N_("A"), "A_002"); + b.add_input<decl::Color>(N_("A"), "A_003").default_value({0.5f, 0.5f, 0.5f, 1.0f}); + b.add_input<decl::String>(N_("B")); + b.add_input<decl::Float>(N_("B"), "B_001"); + b.add_input<decl::Vector>(N_("B"), "B_002"); + b.add_input<decl::Color>(N_("B"), "B_003").default_value({0.5f, 0.5f, 0.5f, 1.0f}); + b.add_input<decl::String>(N_("Result")); + b.add_output<decl::Geometry>(N_("Geometry")); } -static void geo_node_attribute_mix_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); @@ -59,10 +59,9 @@ static void geo_node_attribute_mix_layout(uiLayout *layout, bContext *UNUSED(C), uiItemR(col, ptr, "input_type_b", 0, IFACE_("B"), ICON_NONE); } -static void geo_node_attribute_mix_init(bNodeTree *UNUSED(ntree), bNode *node) +static void node_init(bNodeTree *UNUSED(ntree), bNode *node) { - NodeAttributeMix *data = (NodeAttributeMix *)MEM_callocN(sizeof(NodeAttributeMix), - "attribute mix node"); + NodeAttributeMix *data = MEM_cnew<NodeAttributeMix>("attribute mix node"); data->blend_type = MA_RAMP_BLEND; data->input_type_factor = GEO_NODE_ATTRIBUTE_INPUT_FLOAT; data->input_type_a = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE; @@ -70,15 +69,15 @@ static void geo_node_attribute_mix_init(bNodeTree *UNUSED(ntree), bNode *node) node->storage = data; } -static void geo_node_attribute_mix_update(bNodeTree *UNUSED(ntree), bNode *node) +static void node_update(bNodeTree *ntree, bNode *node) { NodeAttributeMix *node_storage = (NodeAttributeMix *)node->storage; update_attribute_input_socket_availabilities( - *node, "Factor", (GeometryNodeAttributeInputMode)node_storage->input_type_factor); + *ntree, *node, "Factor", (GeometryNodeAttributeInputMode)node_storage->input_type_factor); update_attribute_input_socket_availabilities( - *node, "A", (GeometryNodeAttributeInputMode)node_storage->input_type_a); + *ntree, *node, "A", (GeometryNodeAttributeInputMode)node_storage->input_type_a); update_attribute_input_socket_availabilities( - *node, "B", (GeometryNodeAttributeInputMode)node_storage->input_type_b); + *ntree, *node, "B", (GeometryNodeAttributeInputMode)node_storage->input_type_b); } static void do_mix_operation_float(const int blend_mode, @@ -144,25 +143,28 @@ static void do_mix_operation(const CustomDataType result_type, GVMutableArray &attribute_result) { if (result_type == CD_PROP_FLOAT) { + VMutableArray<float> result = attribute_result.typed<float>(); do_mix_operation_float(blend_mode, attribute_factor, attribute_a.typed<float>(), attribute_b.typed<float>(), - attribute_result.typed<float>()); + result); } else if (result_type == CD_PROP_FLOAT3) { + VMutableArray<float3> result = attribute_result.typed<float3>(); do_mix_operation_float3(blend_mode, attribute_factor, attribute_a.typed<float3>(), attribute_b.typed<float3>(), - attribute_result.typed<float3>()); + result); } else if (result_type == CD_PROP_COLOR) { + VMutableArray<ColorGeometry4f> result = attribute_result.typed<ColorGeometry4f>(); do_mix_operation_color4f(blend_mode, attribute_factor, attribute_a.typed<ColorGeometry4f>(), attribute_b.typed<ColorGeometry4f>(), - attribute_result.typed<ColorGeometry4f>()); + result); } } @@ -203,27 +205,27 @@ static void attribute_mix_calc(GeometryComponent &component, const GeoNodeExecPa return; } - GVArray_Typed<float> attribute_factor = params.get_input_attribute<float>( + VArray<float> attribute_factor = params.get_input_attribute<float>( "Factor", component, result_domain, 0.5f); - GVArrayPtr attribute_a = params.get_input_attribute( + GVArray attribute_a = params.get_input_attribute( "A", component, result_domain, result_type, nullptr); - GVArrayPtr attribute_b = params.get_input_attribute( + GVArray attribute_b = params.get_input_attribute( "B", component, result_domain, result_type, nullptr); do_mix_operation(result_type, node_storage->blend_type, attribute_factor, - *attribute_a, - *attribute_b, - *attribute_result); + attribute_a, + attribute_b, + attribute_result.varray()); attribute_result.save(); } -static void geo_node_attribute_mix_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - geometry_set = geometry_set_realize_instances(geometry_set); + geometry_set = geometry::realize_instances_legacy(geometry_set); if (geometry_set.has<MeshComponent>()) { attribute_mix_calc(geometry_set.get_component_for_write<MeshComponent>(), params); @@ -238,19 +240,20 @@ static void geo_node_attribute_mix_exec(GeoNodeExecParams params) params.set_output("Geometry", geometry_set); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_legacy_attribute_mix_cc void register_node_type_geo_attribute_mix() { + namespace file_ns = blender::nodes::node_geo_legacy_attribute_mix_cc; + static bNodeType ntype; - geo_node_type_base( - &ntype, GEO_NODE_LEGACY_ATTRIBUTE_MIX, "Attribute Mix", NODE_CLASS_ATTRIBUTE, 0); - node_type_init(&ntype, blender::nodes::geo_node_attribute_mix_init); - node_type_update(&ntype, blender::nodes::geo_node_attribute_mix_update); - ntype.declare = blender::nodes::geo_node_mix_attribute_declare; - ntype.draw_buttons = blender::nodes::geo_node_attribute_mix_layout; + geo_node_type_base(&ntype, GEO_NODE_LEGACY_ATTRIBUTE_MIX, "Attribute Mix", NODE_CLASS_ATTRIBUTE); + node_type_init(&ntype, file_ns::node_init); + node_type_update(&ntype, file_ns::node_update); + ntype.declare = file_ns::node_declare; + ntype.draw_buttons = file_ns::node_layout; node_type_storage( &ntype, "NodeAttributeMix", node_free_standard_storage, node_copy_standard_storage); - ntype.geometry_node_execute = blender::nodes::geo_node_attribute_mix_exec; + ntype.geometry_node_execute = file_ns::node_geo_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_proximity.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_proximity.cc index 6120118f611..20f500b1bd8 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_proximity.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_proximity.cc @@ -26,28 +26,26 @@ #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_legacy_attribute_proximity_cc { -static void geo_node_attribute_proximity_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Geometry>("Target"); - b.add_input<decl::String>("Distance"); - b.add_input<decl::String>("Position"); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::Geometry>(N_("Target")); + b.add_input<decl::String>(N_("Distance")); + b.add_input<decl::String>(N_("Position")); + b.add_output<decl::Geometry>(N_("Geometry")); } -static void geo_node_attribute_proximity_layout(uiLayout *layout, - bContext *UNUSED(C), - PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "target_geometry_element", 0, "", ICON_NONE); } -static void geo_attribute_proximity_init(bNodeTree *UNUSED(ntree), bNode *node) +static void node_init(bNodeTree *UNUSED(ntree), bNode *node) { - NodeGeometryAttributeProximity *node_storage = (NodeGeometryAttributeProximity *)MEM_callocN( - sizeof(NodeGeometryAttributeProximity), __func__); + NodeGeometryAttributeProximity *node_storage = MEM_cnew<NodeGeometryAttributeProximity>( + __func__); node_storage->target_geometry_element = GEO_NODE_PROXIMITY_TARGET_FACES; node->storage = node_storage; @@ -83,7 +81,7 @@ static void calculate_mesh_proximity(const VArray<float3> &positions, for (int i : range) { /* Use the distance to the last found point as upper bound to speedup the bvh lookup. */ - nearest.dist_sq = float3::distance_squared(nearest.co, positions[i]); + nearest.dist_sq = math::distance_squared(float3(nearest.co), positions[i]); BLI_bvhtree_find_nearest( bvh_data.tree, positions[i], &nearest, bvh_data.nearest_callback, &bvh_data); @@ -153,7 +151,7 @@ static void attribute_calc_proximity(GeometryComponent &component, if (!position_attribute || (!distance_attribute && !location_attribute)) { return; } - GVArray_Typed<float3> positions{*position_attribute.varray}; + VArray<float3> positions = position_attribute.varray.typed<float3>(); const NodeGeometryAttributeProximity &storage = *(const NodeGeometryAttributeProximity *)params.node().storage; @@ -203,16 +201,16 @@ static void attribute_calc_proximity(GeometryComponent &component, } } -static void geo_node_attribute_proximity_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); GeometrySet geometry_set_target = params.extract_input<GeometrySet>("Target"); - geometry_set = geometry_set_realize_instances(geometry_set); + geometry_set = geometry::realize_instances_legacy(geometry_set); /* This isn't required. This node should be rewritten to handle instances * for the target geometry set. However, the generic BVH API complicates this. */ - geometry_set_target = geometry_set_realize_instances(geometry_set_target); + geometry_set_target = geometry::realize_instances_legacy(geometry_set_target); if (geometry_set.has<MeshComponent>()) { attribute_calc_proximity( @@ -230,22 +228,24 @@ static void geo_node_attribute_proximity_exec(GeoNodeExecParams params) params.set_output("Geometry", geometry_set); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_legacy_attribute_proximity_cc void register_node_type_geo_legacy_attribute_proximity() { + namespace file_ns = blender::nodes::node_geo_legacy_attribute_proximity_cc; + static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_LEGACY_ATTRIBUTE_PROXIMITY, "Attribute Proximity", NODE_CLASS_ATTRIBUTE, 0); - node_type_init(&ntype, blender::nodes::geo_attribute_proximity_init); + &ntype, GEO_NODE_LEGACY_ATTRIBUTE_PROXIMITY, "Attribute Proximity", NODE_CLASS_ATTRIBUTE); + node_type_init(&ntype, file_ns::node_init); node_type_storage(&ntype, "NodeGeometryAttributeProximity", node_free_standard_storage, node_copy_standard_storage); - ntype.declare = blender::nodes::geo_node_attribute_proximity_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_attribute_proximity_exec; - ntype.draw_buttons = blender::nodes::geo_node_attribute_proximity_layout; + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.draw_buttons = file_ns::node_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_randomize.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_randomize.cc index 2e6ba456725..92a946b225b 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_randomize.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_randomize.cc @@ -25,39 +25,70 @@ namespace blender::nodes { -static void geo_node_legacy_attribute_randomize_declare(NodeDeclarationBuilder &b) +Array<uint32_t> get_geometry_element_ids_as_uints(const GeometryComponent &component, + const AttributeDomain domain) +{ + const int domain_size = component.attribute_domain_size(domain); + + /* Hash the reserved name attribute "id" as a (hopefully) stable seed for each point. */ + GVArray hash_attribute = component.attribute_try_get_for_read("id", domain); + Array<uint32_t> hashes(domain_size); + if (hash_attribute) { + BLI_assert(hashes.size() == hash_attribute.size()); + const CPPType &cpp_type = hash_attribute.type(); + BLI_assert(cpp_type.is_hashable()); + GVArray_GSpan items{hash_attribute}; + threading::parallel_for(hashes.index_range(), 512, [&](IndexRange range) { + for (const int i : range) { + hashes[i] = cpp_type.hash(items[i]); + } + }); + } + else { + /* If there is no "id" attribute for per-point variation, just create it here. */ + RandomNumberGenerator rng(0); + for (const int i : hashes.index_range()) { + hashes[i] = rng.get_uint32(); + } + } + + return hashes; +} + +} // namespace blender::nodes + +namespace blender::nodes::node_geo_legacy_attribute_randomize_cc { + +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::String>("Attribute"); - b.add_input<decl::Vector>("Min"); - b.add_input<decl::Vector>("Max").default_value({1.0f, 1.0f, 1.0f}); - b.add_input<decl::Float>("Min", "Min_001"); - b.add_input<decl::Float>("Max", "Max_001").default_value(1.0f); - b.add_input<decl::Int>("Min", "Min_002").min(-100000).max(100000); - b.add_input<decl::Int>("Max", "Max_002").default_value(100).min(-100000).max(100000); - b.add_input<decl::Int>("Seed").min(-10000).max(10000); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::String>(N_("Attribute")); + b.add_input<decl::Vector>(N_("Min")); + b.add_input<decl::Vector>(N_("Max")).default_value({1.0f, 1.0f, 1.0f}); + b.add_input<decl::Float>(N_("Min"), "Min_001"); + b.add_input<decl::Float>(N_("Max"), "Max_001").default_value(1.0f); + b.add_input<decl::Int>(N_("Min"), "Min_002").min(-100000).max(100000); + b.add_input<decl::Int>(N_("Max"), "Max_002").default_value(100).min(-100000).max(100000); + b.add_input<decl::Int>(N_("Seed")).min(-10000).max(10000); + b.add_output<decl::Geometry>(N_("Geometry")); } -static void geo_node_legacy_attribute_random_layout(uiLayout *layout, - bContext *UNUSED(C), - PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); uiItemR(layout, ptr, "operation", 0, "", ICON_NONE); } -static void geo_node_legacy_attribute_randomize_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeAttributeRandomize *data = (NodeAttributeRandomize *)MEM_callocN( - sizeof(NodeAttributeRandomize), __func__); + NodeAttributeRandomize *data = MEM_cnew<NodeAttributeRandomize>(__func__); data->data_type = CD_PROP_FLOAT; data->domain = ATTR_DOMAIN_POINT; data->operation = GEO_NODE_ATTRIBUTE_RANDOMIZE_REPLACE_CREATE; node->storage = data; } -static void geo_node_legacy_attribute_randomize_update(bNodeTree *UNUSED(ntree), bNode *node) +static void node_update(bNodeTree *ntree, bNode *node) { bNodeSocket *sock_min_vector = (bNodeSocket *)BLI_findlink(&node->inputs, 2); bNodeSocket *sock_max_vector = sock_min_vector->next; @@ -68,12 +99,12 @@ static void geo_node_legacy_attribute_randomize_update(bNodeTree *UNUSED(ntree), const NodeAttributeRandomize &storage = *(const NodeAttributeRandomize *)node->storage; const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type); - nodeSetSocketAvailability(sock_min_vector, data_type == CD_PROP_FLOAT3); - nodeSetSocketAvailability(sock_max_vector, data_type == CD_PROP_FLOAT3); - nodeSetSocketAvailability(sock_min_float, data_type == CD_PROP_FLOAT); - nodeSetSocketAvailability(sock_max_float, data_type == CD_PROP_FLOAT); - nodeSetSocketAvailability(sock_min_int, data_type == CD_PROP_INT32); - nodeSetSocketAvailability(sock_max_int, data_type == CD_PROP_INT32); + nodeSetSocketAvailability(ntree, sock_min_vector, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(ntree, sock_max_vector, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(ntree, sock_min_float, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(ntree, sock_max_float, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(ntree, sock_min_int, data_type == CD_PROP_INT32); + nodeSetSocketAvailability(ntree, sock_max_int, data_type == CD_PROP_INT32); } template<typename T> @@ -174,36 +205,6 @@ static void randomize_attribute_bool(MutableSpan<bool> span, }); } -Array<uint32_t> get_geometry_element_ids_as_uints(const GeometryComponent &component, - const AttributeDomain domain) -{ - const int domain_size = component.attribute_domain_size(domain); - - /* Hash the reserved name attribute "id" as a (hopefully) stable seed for each point. */ - GVArrayPtr hash_attribute = component.attribute_try_get_for_read("id", domain); - Array<uint32_t> hashes(domain_size); - if (hash_attribute) { - BLI_assert(hashes.size() == hash_attribute->size()); - const CPPType &cpp_type = hash_attribute->type(); - BLI_assert(cpp_type.is_hashable()); - GVArray_GSpan items{*hash_attribute}; - threading::parallel_for(hashes.index_range(), 512, [&](IndexRange range) { - for (const int i : range) { - hashes[i] = cpp_type.hash(items[i]); - } - }); - } - else { - /* If there is no "id" attribute for per-point variation, just create it here. */ - RandomNumberGenerator rng(0); - for (const int i : hashes.index_range()) { - hashes[i] = rng.get_uint32(); - } - } - - return hashes; -} - static AttributeDomain get_result_domain(const GeometryComponent &component, const GeoNodeExecParams ¶ms, const StringRef name) @@ -280,12 +281,12 @@ static void randomize_attribute_on_component(GeometryComponent &component, attribute.save(); } -static void geo_node_legacy_random_attribute_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); const std::string attribute_name = params.get_input<std::string>("Attribute"); if (attribute_name.empty()) { - params.set_output("Geometry", geometry_set); + params.set_default_remaining_outputs(); return; } const int seed = params.get_input<int>("Seed"); @@ -294,7 +295,7 @@ static void geo_node_legacy_random_attribute_exec(GeoNodeExecParams params) const GeometryNodeAttributeRandomizeMode operation = static_cast<GeometryNodeAttributeRandomizeMode>(storage.operation); - geometry_set = geometry_set_realize_instances(geometry_set); + geometry_set = geometry::realize_instances_legacy(geometry_set); if (geometry_set.has<MeshComponent>()) { randomize_attribute_on_component(geometry_set.get_component_for_write<MeshComponent>(), @@ -324,20 +325,22 @@ static void geo_node_legacy_random_attribute_exec(GeoNodeExecParams params) params.set_output("Geometry", geometry_set); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_legacy_attribute_randomize_cc void register_node_type_geo_legacy_attribute_randomize() { + namespace file_ns = blender::nodes::node_geo_legacy_attribute_randomize_cc; + static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_LEGACY_ATTRIBUTE_RANDOMIZE, "Attribute Randomize", NODE_CLASS_ATTRIBUTE, 0); - node_type_init(&ntype, blender::nodes::geo_node_legacy_attribute_randomize_init); - node_type_update(&ntype, blender::nodes::geo_node_legacy_attribute_randomize_update); + &ntype, GEO_NODE_LEGACY_ATTRIBUTE_RANDOMIZE, "Attribute Randomize", NODE_CLASS_ATTRIBUTE); + node_type_init(&ntype, file_ns::node_init); + node_type_update(&ntype, file_ns::node_update); - ntype.declare = blender::nodes::geo_node_legacy_attribute_randomize_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_legacy_random_attribute_exec; - ntype.draw_buttons = blender::nodes::geo_node_legacy_attribute_random_layout; + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.draw_buttons = file_ns::node_layout; node_type_storage( &ntype, "NodeAttributeRandomize", node_free_standard_storage, node_copy_standard_storage); nodeRegisterType(&ntype); diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_sample_texture.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_sample_texture.cc index 52f97475941..ae034d152be 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_sample_texture.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_sample_texture.cc @@ -28,15 +28,15 @@ #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_legacy_attribute_sample_texture_cc { -static void geo_node_attribute_sample_texture_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Texture>("Texture").hide_label(); - b.add_input<decl::String>("Mapping"); - b.add_input<decl::String>("Result"); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::Texture>(N_("Texture")).hide_label(); + b.add_input<decl::String>(N_("Mapping")); + b.add_input<decl::String>(N_("Result")); + b.add_output<decl::Geometry>(N_("Geometry")); } static AttributeDomain get_result_domain(const GeometryComponent &component, @@ -82,7 +82,7 @@ static void execute_on_component(GeometryComponent &component, const GeoNodeExec return; } - GVArray_Typed<float3> mapping_attribute = component.attribute_get_for_read<float3>( + VArray<float3> mapping_attribute = component.attribute_get_for_read<float3>( mapping_name, result_domain, {0, 0, 0}); MutableSpan<ColorGeometry4f> colors = attribute_out.as_span(); @@ -100,11 +100,11 @@ static void execute_on_component(GeometryComponent &component, const GeoNodeExec attribute_out.save(); } -static void geo_node_attribute_sample_texture_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - geometry_set = geometry_set_realize_instances(geometry_set); + geometry_set = geometry::realize_instances_legacy(geometry_set); if (geometry_set.has<MeshComponent>()) { execute_on_component(geometry_set.get_component_for_write<MeshComponent>(), params); @@ -119,19 +119,20 @@ static void geo_node_attribute_sample_texture_exec(GeoNodeExecParams params) params.set_output("Geometry", geometry_set); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_legacy_attribute_sample_texture_cc void register_node_type_geo_sample_texture() { + namespace file_ns = blender::nodes::node_geo_legacy_attribute_sample_texture_cc; + static bNodeType ntype; geo_node_type_base(&ntype, GEO_NODE_LEGACY_ATTRIBUTE_SAMPLE_TEXTURE, "Attribute Sample Texture", - NODE_CLASS_ATTRIBUTE, - 0); + NODE_CLASS_ATTRIBUTE); node_type_size_preset(&ntype, NODE_SIZE_LARGE); - ntype.declare = blender::nodes::geo_node_attribute_sample_texture_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_attribute_sample_texture_exec; + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_separate_xyz.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_separate_xyz.cc index de0090406c6..960ec540556 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_separate_xyz.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_separate_xyz.cc @@ -19,41 +19,38 @@ #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_legacy_attribute_separate_xyz_cc { -static void geo_node_attribute_separate_xyz_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::String>("Vector"); - b.add_input<decl::Vector>("Vector", "Vector_001"); - b.add_input<decl::String>("Result X"); - b.add_input<decl::String>("Result Y"); - b.add_input<decl::String>("Result Z"); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::String>(N_("Vector")); + b.add_input<decl::Vector>(N_("Vector"), "Vector_001"); + b.add_input<decl::String>(N_("Result X")); + b.add_input<decl::String>(N_("Result Y")); + b.add_input<decl::String>(N_("Result Z")); + b.add_output<decl::Geometry>(N_("Geometry")); } -static void geo_node_attribute_separate_xyz_layout(uiLayout *layout, - bContext *UNUSED(C), - PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); uiItemR(layout, ptr, "input_type", 0, IFACE_("Type"), ICON_NONE); } -static void geo_node_attribute_separate_xyz_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeAttributeSeparateXYZ *data = (NodeAttributeSeparateXYZ *)MEM_callocN( - sizeof(NodeAttributeSeparateXYZ), __func__); + NodeAttributeSeparateXYZ *data = MEM_cnew<NodeAttributeSeparateXYZ>(__func__); data->input_type = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE; node->storage = data; } -static void geo_node_attribute_separate_xyz_update(bNodeTree *UNUSED(ntree), bNode *node) +static void node_update(bNodeTree *ntree, bNode *node) { NodeAttributeSeparateXYZ *node_storage = (NodeAttributeSeparateXYZ *)node->storage; update_attribute_input_socket_availabilities( - *node, "Vector", (GeometryNodeAttributeInputMode)node_storage->input_type); + *ntree, *node, "Vector", (GeometryNodeAttributeInputMode)node_storage->input_type); } static void extract_input(const int index, const Span<float3> &input, MutableSpan<float> result) @@ -106,9 +103,9 @@ static void separate_attribute(GeometryComponent &component, const GeoNodeExecPa const AttributeDomain result_domain = get_result_domain( component, params, result_name_x, result_name_y, result_name_z); - GVArray_Typed<float3> attribute_input = params.get_input_attribute<float3>( + VArray<float3> attribute_input = params.get_input_attribute<float3>( "Vector", component, result_domain, {0, 0, 0}); - VArray_Span<float3> input_span{*attribute_input}; + VArray_Span<float3> input_span{attribute_input}; OutputAttribute_Typed<float> attribute_result_x = component.attribute_try_get_for_output_only<float>(result_name_x, result_domain); @@ -132,11 +129,11 @@ static void separate_attribute(GeometryComponent &component, const GeoNodeExecPa } } -static void geo_node_attribute_separate_xyz_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - geometry_set = geometry_set_realize_instances(geometry_set); + geometry_set = geometry::realize_instances_legacy(geometry_set); if (geometry_set.has<MeshComponent>()) { separate_attribute(geometry_set.get_component_for_write<MeshComponent>(), params); @@ -151,23 +148,24 @@ static void geo_node_attribute_separate_xyz_exec(GeoNodeExecParams params) params.set_output("Geometry", geometry_set); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_legacy_attribute_separate_xyz_cc void register_node_type_geo_attribute_separate_xyz() { + namespace file_ns = blender::nodes::node_geo_legacy_attribute_separate_xyz_cc; + static bNodeType ntype; geo_node_type_base(&ntype, GEO_NODE_LEGACY_ATTRIBUTE_SEPARATE_XYZ, "Attribute Separate XYZ", - NODE_CLASS_ATTRIBUTE, - 0); - ntype.declare = blender::nodes::geo_node_attribute_separate_xyz_declare; - node_type_init(&ntype, blender::nodes::geo_node_attribute_separate_xyz_init); - node_type_update(&ntype, blender::nodes::geo_node_attribute_separate_xyz_update); + NODE_CLASS_ATTRIBUTE); + ntype.declare = file_ns::node_declare; + node_type_init(&ntype, file_ns::node_init); + node_type_update(&ntype, file_ns::node_update); node_type_storage( &ntype, "NodeAttributeSeparateXYZ", node_free_standard_storage, node_copy_standard_storage); - ntype.geometry_node_execute = blender::nodes::geo_node_attribute_separate_xyz_exec; - ntype.draw_buttons = blender::nodes::geo_node_attribute_separate_xyz_layout; + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.draw_buttons = file_ns::node_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_transfer.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_transfer.cc index f187ee39b94..a85a7c56cb9 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_transfer.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_transfer.cc @@ -29,20 +29,18 @@ #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_legacy_attribute_transfer_cc { -static void geo_node_attribute_transfer_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Geometry>("Source Geometry"); - b.add_input<decl::String>("Source"); - b.add_input<decl::String>("Destination"); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::Geometry>(N_("Source Geometry")); + b.add_input<decl::String>(N_("Source")); + b.add_input<decl::String>(N_("Destination")); + b.add_output<decl::Geometry>(N_("Geometry")); } -static void geo_node_attribute_transfer_layout(uiLayout *layout, - bContext *UNUSED(C), - PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); @@ -50,10 +48,9 @@ static void geo_node_attribute_transfer_layout(uiLayout *layout, uiItemR(layout, ptr, "mapping", 0, IFACE_("Mapping"), ICON_NONE); } -static void geo_node_attribute_transfer_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeGeometryAttributeTransfer *data = (NodeGeometryAttributeTransfer *)MEM_callocN( - sizeof(NodeGeometryAttributeTransfer), __func__); + NodeGeometryAttributeTransfer *data = MEM_cnew<NodeGeometryAttributeTransfer>(__func__); data->domain = ATTR_DOMAIN_AUTO; node->storage = data; } @@ -232,7 +229,7 @@ static void get_closest_mesh_corners(const Mesh &mesh, const MLoop &loop = mesh.mloop[loop_index]; const int vertex_index = loop.v; const MVert &mvert = mesh.mvert[vertex_index]; - const float distance_sq = float3::distance_squared(position, mvert.co); + const float distance_sq = math::distance_squared(position, float3(mvert.co)); if (distance_sq < min_distance_sq) { min_distance_sq = distance_sq; closest_loop_index = loop_index; @@ -284,7 +281,8 @@ static void transfer_attribute_nearest_face_interpolated(const GeometrySet &src_ Array<float3> positions(tot_samples); get_closest_mesh_looptris(*mesh, dst_positions, looptri_indices, {}, positions); - bke::mesh_surface_sample::MeshAttributeInterpolator interp(mesh, positions, looptri_indices); + bke::mesh_surface_sample::MeshAttributeInterpolator interp( + mesh, IndexMask(tot_samples), positions, looptri_indices); interp.sample_attribute( src_attribute, dst_attribute, bke::mesh_surface_sample::eAttributeMapMode::INTERPOLATED); @@ -404,15 +402,15 @@ static void transfer_attribute_nearest(const GeometrySet &src_geometry, data_type); for (const int i : IndexRange(tot_samples)) { if (pointcloud_distances_sq[i] < mesh_distances_sq[i]) { - /* Point-cloud point is closer. */ + /* Point cloud point is closer. */ const int index = pointcloud_indices[i]; - pointcloud_src_attribute.varray->get(index, buffer); + pointcloud_src_attribute.varray.get(index, buffer); dst_attribute->set_by_relocate(i, buffer); } else { /* Mesh element is closer. */ const int index = mesh_indices[i]; - mesh_src_attribute.varray->get(index, buffer); + mesh_src_attribute.varray.get(index, buffer); dst_attribute->set_by_relocate(i, buffer); } } @@ -423,7 +421,7 @@ static void transfer_attribute_nearest(const GeometrySet &src_geometry, src_name, data_type); for (const int i : IndexRange(tot_samples)) { const int index = pointcloud_indices[i]; - src_attribute.varray->get(index, buffer); + src_attribute.varray.get(index, buffer); dst_attribute->set_by_relocate(i, buffer); } } @@ -433,7 +431,7 @@ static void transfer_attribute_nearest(const GeometrySet &src_geometry, data_type); for (const int i : IndexRange(tot_samples)) { const int index = mesh_indices[i]; - src_attribute.varray->get(index, buffer); + src_attribute.varray.get(index, buffer); dst_attribute->set_by_relocate(i, buffer); } } @@ -459,16 +457,16 @@ static void transfer_attribute(const GeoNodeExecParams ¶ms, const AttributeDomain dst_domain = (input_domain == ATTR_DOMAIN_AUTO) ? auto_domain : input_domain; - GVArray_Typed<float3> dst_positions = dst_component.attribute_get_for_read<float3>( + VArray<float3> dst_positions = dst_component.attribute_get_for_read<float3>( "position", dst_domain, {0, 0, 0}); switch (mapping) { - case GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED: { + case GEO_NODE_LEGACY_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED: { transfer_attribute_nearest_face_interpolated( src_geometry, dst_component, dst_positions, dst_domain, data_type, src_name, dst_name); break; } - case GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST: { + case GEO_NODE_LEGACY_ATTRIBUTE_TRANSFER_NEAREST: { transfer_attribute_nearest( src_geometry, dst_component, dst_positions, dst_domain, data_type, src_name, dst_name); break; @@ -476,7 +474,7 @@ static void transfer_attribute(const GeoNodeExecParams ¶ms, } } -static void geo_node_attribute_transfer_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { GeometrySet dst_geometry_set = params.extract_input<GeometrySet>("Geometry"); GeometrySet src_geometry_set = params.extract_input<GeometrySet>("Source Geometry"); @@ -484,12 +482,12 @@ static void geo_node_attribute_transfer_exec(GeoNodeExecParams params) const std::string dst_attribute_name = params.extract_input<std::string>("Destination"); if (src_attribute_name.empty() || dst_attribute_name.empty()) { - params.set_output("Geometry", dst_geometry_set); + params.set_default_remaining_outputs(); return; } - dst_geometry_set = bke::geometry_set_realize_instances(dst_geometry_set); - src_geometry_set = bke::geometry_set_realize_instances(src_geometry_set); + dst_geometry_set = geometry::realize_instances_legacy(dst_geometry_set); + src_geometry_set = geometry::realize_instances_legacy(src_geometry_set); if (dst_geometry_set.has<MeshComponent>()) { transfer_attribute(params, @@ -509,21 +507,23 @@ static void geo_node_attribute_transfer_exec(GeoNodeExecParams params) params.set_output("Geometry", dst_geometry_set); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_legacy_attribute_transfer_cc -void register_node_type_geo_attribute_transfer() +void register_node_type_geo_legacy_attribute_transfer() { + namespace file_ns = blender::nodes::node_geo_legacy_attribute_transfer_cc; + static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_LEGACY_ATTRIBUTE_TRANSFER, "Attribute Transfer", NODE_CLASS_ATTRIBUTE, 0); - node_type_init(&ntype, blender::nodes::geo_node_attribute_transfer_init); + &ntype, GEO_NODE_LEGACY_ATTRIBUTE_TRANSFER, "Attribute Transfer", NODE_CLASS_ATTRIBUTE); + node_type_init(&ntype, file_ns::node_init); node_type_storage(&ntype, "NodeGeometryAttributeTransfer", node_free_standard_storage, node_copy_standard_storage); - ntype.declare = blender::nodes::geo_node_attribute_transfer_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_attribute_transfer_exec; - ntype.draw_buttons = blender::nodes::geo_node_attribute_transfer_layout; + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.draw_buttons = file_ns::node_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_vector_math.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_vector_math.cc index 59903050f88..5b3c3c05a6a 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_vector_math.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_vector_math.cc @@ -26,21 +26,21 @@ #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_legacy_attribute_vector_math_cc { -static void geo_node_attribute_vector_math_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::String>("A"); - b.add_input<decl::Vector>("A", "A_001"); - b.add_input<decl::String>("B"); - b.add_input<decl::Vector>("B", "B_001"); - b.add_input<decl::Float>("B", "B_002"); - b.add_input<decl::String>("C"); - b.add_input<decl::Vector>("C", "C_001"); - b.add_input<decl::Float>("C", "C_002"); - b.add_input<decl::String>("Result"); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::String>(N_("A")); + b.add_input<decl::Vector>(N_("A"), "A_001"); + b.add_input<decl::String>(N_("B")); + b.add_input<decl::Vector>(N_("B"), "B_001"); + b.add_input<decl::Float>(N_("B"), "B_002"); + b.add_input<decl::String>(N_("C")); + b.add_input<decl::Vector>(N_("C"), "C_001"); + b.add_input<decl::Float>(N_("C"), "C_002"); + b.add_input<decl::String>(N_("Result")); + b.add_output<decl::Geometry>(N_("Geometry")); } static bool operation_use_input_b(const NodeVectorMathOperation operation) @@ -66,9 +66,7 @@ static bool operation_use_input_c(const NodeVectorMathOperation operation) NODE_VECTOR_MATH_MULTIPLY_ADD); } -static void geo_node_attribute_vector_math_layout(uiLayout *layout, - bContext *UNUSED(C), - PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { bNode *node = (bNode *)ptr->data; const NodeAttributeVectorMath &node_storage = *(NodeAttributeVectorMath *)node->storage; @@ -103,10 +101,9 @@ static CustomDataType operation_get_read_type_c(const NodeVectorMathOperation op return CD_PROP_FLOAT3; } -static void geo_node_attribute_vector_math_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeAttributeVectorMath *data = (NodeAttributeVectorMath *)MEM_callocN( - sizeof(NodeAttributeVectorMath), __func__); + NodeAttributeVectorMath *data = MEM_cnew<NodeAttributeVectorMath>(__func__); data->operation = NODE_VECTOR_MATH_ADD; data->input_type_a = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE; @@ -152,8 +149,8 @@ static CustomDataType operation_get_result_type(const NodeVectorMathOperation op return CD_PROP_FLOAT3; } -static void geo_node_vector_math_label(bNodeTree *UNUSED(ntree), - bNode *node, +static void geo_node_vector_math_label(const bNodeTree *UNUSED(ntree), + const bNode *node, char *label, int maxlen) { @@ -166,19 +163,21 @@ static void geo_node_vector_math_label(bNodeTree *UNUSED(ntree), BLI_snprintf(label, maxlen, IFACE_("Vector %s"), IFACE_(name)); } -static void geo_node_attribute_vector_math_update(bNodeTree *UNUSED(ntree), bNode *node) +static void node_update(bNodeTree *ntree, bNode *node) { const NodeAttributeVectorMath *node_storage = (NodeAttributeVectorMath *)node->storage; const NodeVectorMathOperation operation = (const NodeVectorMathOperation)node_storage->operation; update_attribute_input_socket_availabilities( - *node, "A", (GeometryNodeAttributeInputMode)node_storage->input_type_a); + *ntree, *node, "A", (GeometryNodeAttributeInputMode)node_storage->input_type_a); update_attribute_input_socket_availabilities( + *ntree, *node, "B", (GeometryNodeAttributeInputMode)node_storage->input_type_b, operation_use_input_b(operation)); update_attribute_input_socket_availabilities( + *ntree, *node, "C", (GeometryNodeAttributeInputMode)node_storage->input_type_c, @@ -187,7 +186,7 @@ static void geo_node_attribute_vector_math_update(bNodeTree *UNUSED(ntree), bNod static void do_math_operation_fl3_fl3_to_fl3(const VArray<float3> &input_a, const VArray<float3> &input_b, - VMutableArray<float3> &result, + const VMutableArray<float3> &result, const NodeVectorMathOperation operation) { const int size = input_a.size(); @@ -218,7 +217,7 @@ static void do_math_operation_fl3_fl3_to_fl3(const VArray<float3> &input_a, static void do_math_operation_fl3_fl3_fl3_to_fl3(const VArray<float3> &input_a, const VArray<float3> &input_b, const VArray<float3> &input_c, - VMutableArray<float3> &result, + const VMutableArray<float3> &result, const NodeVectorMathOperation operation) { const int size = input_a.size(); @@ -251,7 +250,7 @@ static void do_math_operation_fl3_fl3_fl3_to_fl3(const VArray<float3> &input_a, static void do_math_operation_fl3_fl3_fl_to_fl3(const VArray<float3> &input_a, const VArray<float3> &input_b, const VArray<float> &input_c, - VMutableArray<float3> &result, + const VMutableArray<float3> &result, const NodeVectorMathOperation operation) { const int size = input_a.size(); @@ -283,7 +282,7 @@ static void do_math_operation_fl3_fl3_fl_to_fl3(const VArray<float3> &input_a, static void do_math_operation_fl3_fl3_to_fl(const VArray<float3> &input_a, const VArray<float3> &input_b, - VMutableArray<float> &result, + const VMutableArray<float> &result, const NodeVectorMathOperation operation) { const int size = input_a.size(); @@ -313,7 +312,7 @@ static void do_math_operation_fl3_fl3_to_fl(const VArray<float3> &input_a, static void do_math_operation_fl3_fl_to_fl3(const VArray<float3> &input_a, const VArray<float> &input_b, - VMutableArray<float3> &result, + const VMutableArray<float3> &result, const NodeVectorMathOperation operation) { const int size = input_a.size(); @@ -342,7 +341,7 @@ static void do_math_operation_fl3_fl_to_fl3(const VArray<float3> &input_a, } static void do_math_operation_fl3_to_fl3(const VArray<float3> &input_a, - VMutableArray<float3> &result, + const VMutableArray<float3> &result, const NodeVectorMathOperation operation) { const int size = input_a.size(); @@ -369,7 +368,7 @@ static void do_math_operation_fl3_to_fl3(const VArray<float3> &input_a, } static void do_math_operation_fl3_to_fl(const VArray<float3> &input_a, - VMutableArray<float> &result, + const VMutableArray<float> &result, const NodeVectorMathOperation operation) { const int size = input_a.size(); @@ -437,13 +436,13 @@ static void attribute_vector_math_calc(GeometryComponent &component, const AttributeDomain result_domain = get_result_domain( component, params, operation, result_name); - GVArrayPtr attribute_a = params.get_input_attribute( + GVArray attribute_a = params.get_input_attribute( "A", component, result_domain, read_type_a, nullptr); if (!attribute_a) { return; } - GVArrayPtr attribute_b; - GVArrayPtr attribute_c; + GVArray attribute_b; + GVArray attribute_c; if (use_input_b) { attribute_b = params.get_input_attribute("B", component, result_domain, read_type_b, nullptr); if (!attribute_b) { @@ -476,26 +475,26 @@ static void attribute_vector_math_calc(GeometryComponent &component, case NODE_VECTOR_MATH_MODULO: case NODE_VECTOR_MATH_MINIMUM: case NODE_VECTOR_MATH_MAXIMUM: - do_math_operation_fl3_fl3_to_fl3(attribute_a->typed<float3>(), - attribute_b->typed<float3>(), - attribute_result->typed<float3>(), + do_math_operation_fl3_fl3_to_fl3(attribute_a.typed<float3>(), + attribute_b.typed<float3>(), + attribute_result.varray().typed<float3>(), operation); break; case NODE_VECTOR_MATH_DOT_PRODUCT: case NODE_VECTOR_MATH_DISTANCE: - do_math_operation_fl3_fl3_to_fl(attribute_a->typed<float3>(), - attribute_b->typed<float3>(), - attribute_result->typed<float>(), + do_math_operation_fl3_fl3_to_fl(attribute_a.typed<float3>(), + attribute_b.typed<float3>(), + attribute_result.varray().typed<float>(), operation); break; case NODE_VECTOR_MATH_LENGTH: do_math_operation_fl3_to_fl( - attribute_a->typed<float3>(), attribute_result->typed<float>(), operation); + attribute_a.typed<float3>(), attribute_result.varray().typed<float>(), operation); break; case NODE_VECTOR_MATH_SCALE: - do_math_operation_fl3_fl_to_fl3(attribute_a->typed<float3>(), - attribute_b->typed<float>(), - attribute_result->typed<float3>(), + do_math_operation_fl3_fl_to_fl3(attribute_a.typed<float3>(), + attribute_b.typed<float>(), + attribute_result.varray().typed<float3>(), operation); break; case NODE_VECTOR_MATH_NORMALIZE: @@ -507,33 +506,33 @@ static void attribute_vector_math_calc(GeometryComponent &component, case NODE_VECTOR_MATH_COSINE: case NODE_VECTOR_MATH_TANGENT: do_math_operation_fl3_to_fl3( - attribute_a->typed<float3>(), attribute_result->typed<float3>(), operation); + attribute_a.typed<float3>(), attribute_result.varray().typed<float3>(), operation); break; case NODE_VECTOR_MATH_WRAP: case NODE_VECTOR_MATH_FACEFORWARD: case NODE_VECTOR_MATH_MULTIPLY_ADD: - do_math_operation_fl3_fl3_fl3_to_fl3(attribute_a->typed<float3>(), - attribute_b->typed<float3>(), - attribute_c->typed<float3>(), - attribute_result->typed<float3>(), + do_math_operation_fl3_fl3_fl3_to_fl3(attribute_a.typed<float3>(), + attribute_b.typed<float3>(), + attribute_c.typed<float3>(), + attribute_result.varray().typed<float3>(), operation); break; case NODE_VECTOR_MATH_REFRACT: - do_math_operation_fl3_fl3_fl_to_fl3(attribute_a->typed<float3>(), - attribute_b->typed<float3>(), - attribute_c->typed<float>(), - attribute_result->typed<float3>(), + do_math_operation_fl3_fl3_fl_to_fl3(attribute_a.typed<float3>(), + attribute_b.typed<float3>(), + attribute_c.typed<float>(), + attribute_result.varray().typed<float3>(), operation); break; } attribute_result.save(); } -static void geo_node_attribute_vector_math_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - geometry_set = geometry_set_realize_instances(geometry_set); + geometry_set = geometry::realize_instances_legacy(geometry_set); if (geometry_set.has<MeshComponent>()) { attribute_vector_math_calc(geometry_set.get_component_for_write<MeshComponent>(), params); @@ -549,23 +548,24 @@ static void geo_node_attribute_vector_math_exec(GeoNodeExecParams params) params.set_output("Geometry", geometry_set); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_legacy_attribute_vector_math_cc void register_node_type_geo_attribute_vector_math() { + namespace file_ns = blender::nodes::node_geo_legacy_attribute_vector_math_cc; + static bNodeType ntype; geo_node_type_base(&ntype, GEO_NODE_LEGACY_ATTRIBUTE_VECTOR_MATH, "Attribute Vector Math", - NODE_CLASS_ATTRIBUTE, - 0); - ntype.declare = blender::nodes::geo_node_attribute_vector_math_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_attribute_vector_math_exec; - ntype.draw_buttons = blender::nodes::geo_node_attribute_vector_math_layout; - node_type_label(&ntype, blender::nodes::geo_node_vector_math_label); - node_type_update(&ntype, blender::nodes::geo_node_attribute_vector_math_update); - node_type_init(&ntype, blender::nodes::geo_node_attribute_vector_math_init); + NODE_CLASS_ATTRIBUTE); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.draw_buttons = file_ns::node_layout; + ntype.labelfunc = file_ns::geo_node_vector_math_label; + node_type_update(&ntype, file_ns::node_update); + node_type_init(&ntype, file_ns::node_init); node_type_storage( &ntype, "NodeAttributeVectorMath", node_free_standard_storage, node_copy_standard_storage); diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_vector_rotate.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_vector_rotate.cc index 0c515fa63fb..3738c4ad14d 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_vector_rotate.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_attribute_vector_rotate.cc @@ -21,30 +21,28 @@ #include "UI_interface.h" #include "UI_resources.h" -namespace blender::nodes { +namespace blender::nodes::node_geo_legacy_attribute_vector_rotate_cc { -static void geo_node_attribute_vector_rotate_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::String>("Vector"); - b.add_input<decl::Vector>("Vector", "Vector_001").min(0.0f).max(1.0f).hide_value(); - b.add_input<decl::String>("Center"); - b.add_input<decl::Vector>("Center", "Center_001").subtype(PROP_XYZ); - b.add_input<decl::String>("Axis"); - b.add_input<decl::Vector>("Axis", "Axis_001").min(-1.0f).max(1.0f).subtype(PROP_XYZ); - b.add_input<decl::String>("Angle"); - b.add_input<decl::Float>("Angle", "Angle_001").subtype(PROP_ANGLE); - b.add_input<decl::String>("Rotation"); - b.add_input<decl::Vector>("Rotation", "Rotation_001").subtype(PROP_EULER); - b.add_input<decl::Bool>("Invert"); - b.add_input<decl::String>("Result"); - - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::String>(N_("Vector")); + b.add_input<decl::Vector>(N_("Vector"), "Vector_001").min(0.0f).max(1.0f).hide_value(); + b.add_input<decl::String>(N_("Center")); + b.add_input<decl::Vector>(N_("Center"), "Center_001").subtype(PROP_XYZ); + b.add_input<decl::String>(N_("Axis")); + b.add_input<decl::Vector>(N_("Axis"), "Axis_001").min(-1.0f).max(1.0f).subtype(PROP_XYZ); + b.add_input<decl::String>(N_("Angle")); + b.add_input<decl::Float>(N_("Angle"), "Angle_001").subtype(PROP_ANGLE); + b.add_input<decl::String>(N_("Rotation")); + b.add_input<decl::Vector>(N_("Rotation"), "Rotation_001").subtype(PROP_EULER); + b.add_input<decl::Bool>(N_("Invert")); + b.add_input<decl::String>(N_("Result")); + + b.add_output<decl::Geometry>(N_("Geometry")); } -static void geo_node_attribute_vector_rotate_layout(uiLayout *layout, - bContext *UNUSED(C), - PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { bNode *node = (bNode *)ptr->data; const NodeAttributeVectorRotate &node_storage = *(NodeAttributeVectorRotate *)node->storage; @@ -70,27 +68,30 @@ static void geo_node_attribute_vector_rotate_layout(uiLayout *layout, } } -static void geo_node_attribute_vector_rotate_update(bNodeTree *UNUSED(ntree), bNode *node) +static void node_update(bNodeTree *ntree, bNode *node) { const NodeAttributeVectorRotate *node_storage = (NodeAttributeVectorRotate *)node->storage; const GeometryNodeAttributeVectorRotateMode mode = (const GeometryNodeAttributeVectorRotateMode) node_storage->mode; update_attribute_input_socket_availabilities( - *node, "Vector", (GeometryNodeAttributeInputMode)node_storage->input_type_vector); + *ntree, *node, "Vector", (GeometryNodeAttributeInputMode)node_storage->input_type_vector); update_attribute_input_socket_availabilities( - *node, "Center", (GeometryNodeAttributeInputMode)node_storage->input_type_center); + *ntree, *node, "Center", (GeometryNodeAttributeInputMode)node_storage->input_type_center); update_attribute_input_socket_availabilities( + *ntree, *node, "Axis", (GeometryNodeAttributeInputMode)node_storage->input_type_axis, (mode == GEO_NODE_VECTOR_ROTATE_TYPE_AXIS)); update_attribute_input_socket_availabilities( + *ntree, *node, "Angle", (GeometryNodeAttributeInputMode)node_storage->input_type_angle, (mode != GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ)); update_attribute_input_socket_availabilities( + *ntree, *node, "Rotation", (GeometryNodeAttributeInputMode)node_storage->input_type_rotation, @@ -109,10 +110,9 @@ static float3 vector_rotate_around_axis(const float3 vector, return result + center; } -static void geo_node_attribute_vector_rotate_init(bNodeTree *UNUSED(ntree), bNode *node) +static void node_init(bNodeTree *UNUSED(ntree), bNode *node) { - NodeAttributeVectorRotate *node_storage = (NodeAttributeVectorRotate *)MEM_callocN( - sizeof(NodeAttributeVectorRotate), __func__); + NodeAttributeVectorRotate *node_storage = MEM_cnew<NodeAttributeVectorRotate>(__func__); node_storage->mode = GEO_NODE_VECTOR_ROTATE_TYPE_AXIS; node_storage->input_type_vector = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE; @@ -220,12 +220,12 @@ static void execute_on_component(const GeoNodeExecParams ¶ms, GeometryCompon const AttributeDomain result_domain = get_result_domain(component, params, result_name); const bool invert = params.get_input<bool>("Invert"); - GVArrayPtr attribute_vector = params.get_input_attribute( + GVArray attribute_vector = params.get_input_attribute( "Vector", component, result_domain, CD_PROP_FLOAT3, nullptr); if (!attribute_vector) { return; } - GVArrayPtr attribute_center = params.get_input_attribute( + GVArray attribute_center = params.get_input_attribute( "Center", component, result_domain, CD_PROP_FLOAT3, nullptr); if (!attribute_center) { return; @@ -238,21 +238,21 @@ static void execute_on_component(const GeoNodeExecParams ¶ms, GeometryCompon } if (mode == GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ) { - GVArrayPtr attribute_rotation = params.get_input_attribute( + GVArray attribute_rotation = params.get_input_attribute( "Rotation", component, result_domain, CD_PROP_FLOAT3, nullptr); if (!attribute_rotation) { return; } - do_vector_rotate_euler(attribute_vector->typed<float3>(), - attribute_center->typed<float3>(), - attribute_rotation->typed<float3>(), + do_vector_rotate_euler(attribute_vector.typed<float3>(), + attribute_center.typed<float3>(), + attribute_rotation.typed<float3>(), attribute_result.as_span<float3>(), invert); attribute_result.save(); return; } - GVArrayPtr attribute_angle = params.get_input_attribute( + GVArray attribute_angle = params.get_input_attribute( "Angle", component, result_domain, CD_PROP_FLOAT, nullptr); if (!attribute_angle) { return; @@ -260,40 +260,40 @@ static void execute_on_component(const GeoNodeExecParams ¶ms, GeometryCompon switch (mode) { case GEO_NODE_VECTOR_ROTATE_TYPE_AXIS: { - GVArrayPtr attribute_axis = params.get_input_attribute( + GVArray attribute_axis = params.get_input_attribute( "Axis", component, result_domain, CD_PROP_FLOAT3, nullptr); if (!attribute_axis) { return; } - do_vector_rotate_around_axis(attribute_vector->typed<float3>(), - attribute_center->typed<float3>(), - attribute_axis->typed<float3>(), - attribute_angle->typed<float>(), + do_vector_rotate_around_axis(attribute_vector.typed<float3>(), + attribute_center.typed<float3>(), + attribute_axis.typed<float3>(), + attribute_angle.typed<float>(), attribute_result.as_span<float3>(), invert); } break; case GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_X: - do_vector_rotate_around_fixed_axis(attribute_vector->typed<float3>(), - attribute_center->typed<float3>(), + do_vector_rotate_around_fixed_axis(attribute_vector.typed<float3>(), + attribute_center.typed<float3>(), float3(1.0f, 0.0f, 0.0f), - attribute_angle->typed<float>(), + attribute_angle.typed<float>(), attribute_result.as_span<float3>(), invert); break; case GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_Y: - do_vector_rotate_around_fixed_axis(attribute_vector->typed<float3>(), - attribute_center->typed<float3>(), + do_vector_rotate_around_fixed_axis(attribute_vector.typed<float3>(), + attribute_center.typed<float3>(), float3(0.0f, 1.0f, 0.0f), - attribute_angle->typed<float>(), + attribute_angle.typed<float>(), attribute_result.as_span<float3>(), invert); break; case GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_Z: - do_vector_rotate_around_fixed_axis(attribute_vector->typed<float3>(), - attribute_center->typed<float3>(), + do_vector_rotate_around_fixed_axis(attribute_vector.typed<float3>(), + attribute_center.typed<float3>(), float3(0.0f, 0.0f, 1.0f), - attribute_angle->typed<float>(), + attribute_angle.typed<float>(), attribute_result.as_span<float3>(), invert); @@ -306,11 +306,11 @@ static void execute_on_component(const GeoNodeExecParams ¶ms, GeometryCompon attribute_result.save(); } -static void geo_node_attribute_vector_rotate_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - geometry_set = geometry_set_realize_instances(geometry_set); + geometry_set = geometry::realize_instances_legacy(geometry_set); if (geometry_set.has<MeshComponent>()) { execute_on_component(params, geometry_set.get_component_for_write<MeshComponent>()); @@ -325,24 +325,25 @@ static void geo_node_attribute_vector_rotate_exec(GeoNodeExecParams params) params.set_output("Geometry", std::move(geometry_set)); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_legacy_attribute_vector_rotate_cc void register_node_type_geo_attribute_vector_rotate() { + namespace file_ns = blender::nodes::node_geo_legacy_attribute_vector_rotate_cc; + static bNodeType ntype; geo_node_type_base(&ntype, GEO_NODE_LEGACY_ATTRIBUTE_VECTOR_ROTATE, "Attribute Vector Rotate", - NODE_CLASS_ATTRIBUTE, - 0); - node_type_update(&ntype, blender::nodes::geo_node_attribute_vector_rotate_update); - node_type_init(&ntype, blender::nodes::geo_node_attribute_vector_rotate_init); + NODE_CLASS_ATTRIBUTE); + node_type_update(&ntype, file_ns::node_update); + node_type_init(&ntype, file_ns::node_init); node_type_size(&ntype, 165, 100, 600); node_type_storage( &ntype, "NodeAttributeVectorRotate", node_free_standard_storage, node_copy_standard_storage); - ntype.geometry_node_execute = blender::nodes::geo_node_attribute_vector_rotate_exec; - ntype.draw_buttons = blender::nodes::geo_node_attribute_vector_rotate_layout; - ntype.declare = blender::nodes::geo_node_attribute_vector_rotate_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.draw_buttons = file_ns::node_layout; + ntype.declare = file_ns::node_declare; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_endpoints.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_endpoints.cc index 65d22eca39c..51564d8d200 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_endpoints.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_endpoints.cc @@ -25,13 +25,13 @@ #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_legacy_curve_endpoints_cc { -static void geo_node_curve_endpoints_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_output<decl::Geometry>("Start Points"); - b.add_output<decl::Geometry>("End Points"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_output<decl::Geometry>(N_("Start Points")); + b.add_output<decl::Geometry>(N_("End Points")); } /** @@ -61,7 +61,7 @@ static void copy_spline_domain_attributes(const CurveComponent &curve_component, if (meta_data.domain != ATTR_DOMAIN_CURVE) { return true; } - GVArrayPtr spline_attribute = curve_component.attribute_get_for_read( + GVArray spline_attribute = curve_component.attribute_get_for_read( attribute_id, ATTR_DOMAIN_CURVE, meta_data.data_type); OutputAttribute result_attribute = points.attribute_try_get_for_output_only( @@ -70,7 +70,7 @@ static void copy_spline_domain_attributes(const CurveComponent &curve_component, /* Only copy the attributes of splines in the offsets. */ for (const int i : offsets.index_range()) { - spline_attribute->get(offsets[i], result[i]); + spline_attribute.get(offsets[i], result[i]); } result_attribute.save(); @@ -124,36 +124,35 @@ static void copy_endpoint_attributes(Span<SplinePtr> splines, end_data.tilts[i] = spline.tilts().last(); /* Copy the point attribute data over. */ - for (const auto &item : start_data.point_attributes.items()) { + for (const auto item : start_data.point_attributes.items()) { const AttributeIDRef attribute_id = item.key; GMutableSpan point_span = item.value; BLI_assert(spline.attributes.get_for_read(attribute_id)); GSpan spline_span = *spline.attributes.get_for_read(attribute_id); - blender::fn::GVArray_For_GSpan(spline_span).get(0, point_span[i]); + spline_span.type().copy_assign(spline_span[0], point_span[i]); } - for (const auto &item : end_data.point_attributes.items()) { + for (const auto item : end_data.point_attributes.items()) { const AttributeIDRef attribute_id = item.key; GMutableSpan point_span = item.value; BLI_assert(spline.attributes.get_for_read(attribute_id)); GSpan spline_span = *spline.attributes.get_for_read(attribute_id); - blender::fn::GVArray_For_GSpan(spline_span).get(spline.size() - 1, point_span[i]); + spline_span.type().copy_assign(spline_span[spline.size() - 1], point_span[i]); } } }); } -static void geo_node_curve_endpoints_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - geometry_set = bke::geometry_set_realize_instances(geometry_set); + geometry_set = geometry::realize_instances_legacy(geometry_set); if (!geometry_set.has_curve()) { - params.set_output("Start Points", GeometrySet()); - params.set_output("End Points", GeometrySet()); + params.set_default_remaining_outputs(); return; } @@ -168,8 +167,7 @@ static void geo_node_curve_endpoints_exec(GeoNodeExecParams params) const int total_size = offsets.size(); if (total_size == 0) { - params.set_output("Start Points", GeometrySet()); - params.set_output("End Points", GeometrySet()); + params.set_default_remaining_outputs(); return; } @@ -206,16 +204,18 @@ static void geo_node_curve_endpoints_exec(GeoNodeExecParams params) params.set_output("End Points", std::move(end_result)); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_legacy_curve_endpoints_cc -void register_node_type_geo_curve_endpoints() +void register_node_type_geo_legacy_curve_endpoints() { + namespace file_ns = blender::nodes::node_geo_legacy_curve_endpoints_cc; + static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_LEGACY_CURVE_ENDPOINTS, "Curve Endpoints", NODE_CLASS_GEOMETRY, 0); - ntype.declare = blender::nodes::geo_node_curve_endpoints_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_curve_endpoints_exec; + &ntype, GEO_NODE_LEGACY_CURVE_ENDPOINTS, "Curve Endpoints", NODE_CLASS_GEOMETRY); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_reverse.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_reverse.cc index d1c81333c30..844baa53962 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_reverse.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_reverse.cc @@ -20,19 +20,19 @@ #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_legacy_curve_reverse_cc { -static void geo_node_curve_reverse_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Curve"); - b.add_input<decl::String>("Selection"); - b.add_output<decl::Geometry>("Curve"); + b.add_input<decl::Geometry>(N_("Curve")); + b.add_input<decl::String>(N_("Selection")); + b.add_output<decl::Geometry>(N_("Curve")); } -static void geo_node_curve_reverse_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); - geometry_set = bke::geometry_set_realize_instances(geometry_set); + geometry_set = geometry::realize_instances_legacy(geometry_set); if (!geometry_set.has_curve()) { params.set_output("Curve", geometry_set); return; @@ -44,7 +44,7 @@ static void geo_node_curve_reverse_exec(GeoNodeExecParams params) MutableSpan<SplinePtr> splines = curve.splines(); const std::string selection_name = params.extract_input<std::string>("Selection"); - GVArray_Typed<bool> selection = curve_component.attribute_get_for_read( + VArray<bool> selection = curve_component.attribute_get_for_read( selection_name, ATTR_DOMAIN_CURVE, true); threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) { @@ -58,14 +58,15 @@ static void geo_node_curve_reverse_exec(GeoNodeExecParams params) params.set_output("Curve", geometry_set); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_legacy_curve_reverse_cc void register_node_type_geo_legacy_curve_reverse() { + namespace file_ns = blender::nodes::node_geo_legacy_curve_reverse_cc; + static bNodeType ntype; - geo_node_type_base( - &ntype, GEO_NODE_LEGACY_CURVE_REVERSE, "Curve Reverse", NODE_CLASS_GEOMETRY, 0); - ntype.declare = blender::nodes::geo_node_curve_reverse_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_curve_reverse_exec; + geo_node_type_base(&ntype, GEO_NODE_LEGACY_CURVE_REVERSE, "Curve Reverse", NODE_CLASS_GEOMETRY); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_select_by_handle_type.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_select_by_handle_type.cc index dfcae2e65b0..780756bcbca 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_select_by_handle_type.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_select_by_handle_type.cc @@ -23,27 +23,24 @@ #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_legacy_curve_select_by_handle_type_cc { -static void geo_node_select_by_handle_type_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::String>("Selection"); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::String>(N_("Selection")); + b.add_output<decl::Geometry>(N_("Geometry")); } -static void geo_node_curve_select_by_handle_type_layout(uiLayout *layout, - bContext *UNUSED(C), - PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); uiItemR(layout, ptr, "handle_type", 0, "", ICON_NONE); } -static void geo_node_curve_select_by_handle_type_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeGeometryCurveSelectHandles *data = (NodeGeometryCurveSelectHandles *)MEM_callocN( - sizeof(NodeGeometryCurveSelectHandles), __func__); + NodeGeometryCurveSelectHandles *data = MEM_cnew<NodeGeometryCurveSelectHandles>(__func__); data->handle_type = GEO_NODE_CURVE_HANDLE_AUTO; data->mode = GEO_NODE_CURVE_HANDLE_LEFT | GEO_NODE_CURVE_HANDLE_RIGHT; @@ -94,7 +91,7 @@ static void select_curve_by_handle_type(const CurveEval &curve, }); } -static void geo_node_select_by_handle_type_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { const NodeGeometryCurveSelectHandles *storage = (const NodeGeometryCurveSelectHandles *)params.node().storage; @@ -103,7 +100,7 @@ static void geo_node_select_by_handle_type_exec(GeoNodeExecParams params) const GeometryNodeCurveHandleMode mode = (GeometryNodeCurveHandleMode)storage->mode; GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - geometry_set = bke::geometry_set_realize_instances(geometry_set); + geometry_set = geometry::realize_instances_legacy(geometry_set); CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>(); const CurveEval *curve = curve_component.get_for_read(); @@ -121,25 +118,24 @@ static void geo_node_select_by_handle_type_exec(GeoNodeExecParams params) params.set_output("Geometry", std::move(geometry_set)); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_legacy_curve_select_by_handle_type_cc -void register_node_type_geo_select_by_handle_type() +void register_node_type_geo_legacy_select_by_handle_type() { + namespace file_ns = blender::nodes::node_geo_legacy_curve_select_by_handle_type_cc; + static bNodeType ntype; - geo_node_type_base(&ntype, - GEO_NODE_LEGACY_CURVE_SELECT_HANDLES, - "Select by Handle Type", - NODE_CLASS_GEOMETRY, - 0); - ntype.declare = blender::nodes::geo_node_select_by_handle_type_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_select_by_handle_type_exec; - node_type_init(&ntype, blender::nodes::geo_node_curve_select_by_handle_type_init); + geo_node_type_base( + &ntype, GEO_NODE_LEGACY_CURVE_SELECT_HANDLES, "Select by Handle Type", NODE_CLASS_GEOMETRY); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + node_type_init(&ntype, file_ns::node_init); node_type_storage(&ntype, "NodeGeometryCurveSelectHandles", node_free_standard_storage, node_copy_standard_storage); - ntype.draw_buttons = blender::nodes::geo_node_curve_select_by_handle_type_layout; + ntype.draw_buttons = file_ns::node_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_set_handles.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_set_handles.cc index 31c13134f79..a82b917e817 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_set_handles.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_set_handles.cc @@ -21,27 +21,24 @@ #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_legacy_curve_set_handles_cc { -static void geo_node_curve_set_handles_decalre(NodeDeclarationBuilder &b) +static void node_decalre(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Curve"); - b.add_input<decl::String>("Selection"); - b.add_output<decl::Geometry>("Curve"); + b.add_input<decl::Geometry>(N_("Curve")); + b.add_input<decl::String>(N_("Selection")); + b.add_output<decl::Geometry>(N_("Curve")); } -static void geo_node_curve_set_handles_layout(uiLayout *layout, - bContext *UNUSED(C), - PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); uiItemR(layout, ptr, "handle_type", 0, "", ICON_NONE); } -static void geo_node_curve_set_handles_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeGeometryCurveSetHandles *data = (NodeGeometryCurveSetHandles *)MEM_callocN( - sizeof(NodeGeometryCurveSetHandles), __func__); + NodeGeometryCurveSetHandles *data = MEM_cnew<NodeGeometryCurveSetHandles>(__func__); data->handle_type = GEO_NODE_CURVE_HANDLE_AUTO; data->mode = GEO_NODE_CURVE_HANDLE_LEFT | GEO_NODE_CURVE_HANDLE_RIGHT; @@ -64,7 +61,7 @@ static BezierSpline::HandleType handle_type_from_input_type(GeometryNodeCurveHan return BezierSpline::HandleType::Auto; } -static void geo_node_curve_set_handles_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { const NodeGeometryCurveSetHandles *node_storage = (NodeGeometryCurveSetHandles *)params.node().storage; @@ -72,7 +69,7 @@ static void geo_node_curve_set_handles_exec(GeoNodeExecParams params) const GeometryNodeCurveHandleMode mode = (GeometryNodeCurveHandleMode)node_storage->mode; GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); - geometry_set = bke::geometry_set_realize_instances(geometry_set); + geometry_set = geometry::realize_instances_legacy(geometry_set); if (!geometry_set.has_curve()) { params.set_output("Curve", geometry_set); return; @@ -84,7 +81,7 @@ static void geo_node_curve_set_handles_exec(GeoNodeExecParams params) MutableSpan<SplinePtr> splines = curve.splines(); const std::string selection_name = params.extract_input<std::string>("Selection"); - GVArray_Typed<bool> selection = curve_component.attribute_get_for_read( + VArray<bool> selection = curve_component.attribute_get_for_read( selection_name, ATTR_DOMAIN_POINT, true); const BezierSpline::HandleType new_handle_type = handle_type_from_input_type(type); @@ -124,21 +121,23 @@ static void geo_node_curve_set_handles_exec(GeoNodeExecParams params) params.set_output("Curve", geometry_set); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_legacy_curve_set_handles_cc -void register_node_type_geo_curve_set_handles() +void register_node_type_geo_legacy_curve_set_handles() { + namespace file_ns = blender::nodes::node_geo_legacy_curve_set_handles_cc; + static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_LEGACY_CURVE_SET_HANDLES, "Set Handle Type", NODE_CLASS_GEOMETRY, 0); - ntype.declare = blender::nodes::geo_node_curve_set_handles_decalre; - ntype.geometry_node_execute = blender::nodes::geo_node_curve_set_handles_exec; - node_type_init(&ntype, blender::nodes::geo_node_curve_set_handles_init); + &ntype, GEO_NODE_LEGACY_CURVE_SET_HANDLES, "Set Handle Type", NODE_CLASS_GEOMETRY); + ntype.declare = file_ns::node_decalre; + ntype.geometry_node_execute = file_ns::node_geo_exec; + node_type_init(&ntype, file_ns::node_init); node_type_storage(&ntype, "NodeGeometryCurveSetHandles", node_free_standard_storage, node_copy_standard_storage); - ntype.draw_buttons = blender::nodes::geo_node_curve_set_handles_layout; + ntype.draw_buttons = file_ns::node_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_spline_type.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_spline_type.cc index 0ef107fd8a4..6fd82e6a1bb 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_spline_type.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_spline_type.cc @@ -23,26 +23,23 @@ #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_legacy_curve_spline_type_cc { -static void geo_node_curve_spline_type_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Curve"); - b.add_input<decl::String>("Selection"); - b.add_output<decl::Geometry>("Curve"); + b.add_input<decl::Geometry>(N_("Curve")); + b.add_input<decl::String>(N_("Selection")); + b.add_output<decl::Geometry>(N_("Curve")); } -static void geo_node_curve_spline_type_layout(uiLayout *layout, - bContext *UNUSED(C), - PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "spline_type", 0, "", ICON_NONE); } -static void geo_node_curve_spline_type_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeGeometryCurveSplineType *data = (NodeGeometryCurveSplineType *)MEM_callocN( - sizeof(NodeGeometryCurveSplineType), __func__); + NodeGeometryCurveSplineType *data = MEM_cnew<NodeGeometryCurveSplineType>(__func__); data->spline_type = GEO_NODE_SPLINE_TYPE_POLY; node->storage = data; @@ -238,14 +235,14 @@ static SplinePtr convert_to_nurbs(const Spline &input) return {}; } -static void geo_node_curve_spline_type_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { const NodeGeometryCurveSplineType *storage = (const NodeGeometryCurveSplineType *)params.node().storage; const GeometryNodeSplineType output_type = (const GeometryNodeSplineType)storage->spline_type; GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); - geometry_set = bke::geometry_set_realize_instances(geometry_set); + geometry_set = geometry::realize_instances_legacy(geometry_set); if (!geometry_set.has_curve()) { params.set_output("Curve", geometry_set); return; @@ -255,7 +252,7 @@ static void geo_node_curve_spline_type_exec(GeoNodeExecParams params) const CurveEval &curve = *curve_component->get_for_read(); const std::string selection_name = params.extract_input<std::string>("Selection"); - GVArray_Typed<bool> selection = curve_component->attribute_get_for_read( + VArray<bool> selection = curve_component->attribute_get_for_read( selection_name, ATTR_DOMAIN_CURVE, true); std::unique_ptr<CurveEval> new_curve = std::make_unique<CurveEval>(); @@ -282,21 +279,23 @@ static void geo_node_curve_spline_type_exec(GeoNodeExecParams params) params.set_output("Curve", GeometrySet::create_with_curve(new_curve.release())); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_legacy_curve_spline_type_cc -void register_node_type_geo_curve_spline_type() +void register_node_type_geo_legacy_curve_spline_type() { + namespace file_ns = blender::nodes::node_geo_legacy_curve_spline_type_cc; + static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_LEGACY_CURVE_SPLINE_TYPE, "Set Spline Type", NODE_CLASS_GEOMETRY, 0); - ntype.declare = blender::nodes::geo_node_curve_spline_type_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_curve_spline_type_exec; - node_type_init(&ntype, blender::nodes::geo_node_curve_spline_type_init); + &ntype, GEO_NODE_LEGACY_CURVE_SPLINE_TYPE, "Set Spline Type", NODE_CLASS_GEOMETRY); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + node_type_init(&ntype, file_ns::node_init); node_type_storage(&ntype, "NodeGeometryCurveSplineType", node_free_standard_storage, node_copy_standard_storage); - ntype.draw_buttons = blender::nodes::geo_node_curve_spline_type_layout; + ntype.draw_buttons = file_ns::node_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_subdivide.cc index 0522f2b8981..4621a1656aa 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_subdivide.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_subdivide.cc @@ -25,42 +25,37 @@ #include "node_geometry_util.hh" -using blender::fn::GVArray_For_GSpan; -using blender::fn::GVArray_For_Span; -using blender::fn::GVArray_Typed; +namespace blender::nodes::node_geo_legacy_curve_subdivide_cc { -namespace blender::nodes { - -static void geo_node_curve_subdivide_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::String>("Cuts"); - b.add_input<decl::Int>("Cuts", "Cuts_001").default_value(1).min(0).max(1000); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::String>(N_("Cuts")); + b.add_input<decl::Int>(N_("Cuts"), "Cuts_001").default_value(1).min(0).max(1000); + b.add_output<decl::Geometry>(N_("Geometry")); } -static void geo_node_curve_subdivide_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); uiItemR(layout, ptr, "cuts_type", 0, IFACE_("Cuts"), ICON_NONE); } -static void geo_node_curve_subdivide_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeGeometryCurveSubdivide *data = (NodeGeometryCurveSubdivide *)MEM_callocN( - sizeof(NodeGeometryCurveSubdivide), __func__); + NodeGeometryCurveSubdivide *data = MEM_cnew<NodeGeometryCurveSubdivide>(__func__); data->cuts_type = GEO_NODE_ATTRIBUTE_INPUT_INTEGER; node->storage = data; } -static void geo_node_curve_subdivide_update(bNodeTree *UNUSED(ntree), bNode *node) +static void node_update(bNodeTree *ntree, bNode *node) { NodeGeometryPointTranslate &node_storage = *(NodeGeometryPointTranslate *)node->storage; update_attribute_input_socket_availabilities( - *node, "Cuts", (GeometryNodeAttributeInputMode)node_storage.input_type); + *ntree, *node, "Cuts", (GeometryNodeAttributeInputMode)node_storage.input_type); } static Array<int> get_subdivided_offsets(const Spline &spline, @@ -308,8 +303,12 @@ static SplinePtr subdivide_spline(const Spline &spline, const VArray<int> &cuts, const int spline_offset) { - /* Since we expect to access each value many times, it should be worth it to make sure the - * attribute is a real span (especially considering the note below). Using the offset at each + if (spline.size() <= 1) { + return spline.copy(); + } + + /* Since we expect to access each value many times, it should be worth it to make sure count + * of cuts is a real span (especially considering the note below). Using the offset at each * point facilitates subdividing in parallel later. */ Array<int> offsets = get_subdivided_offsets(spline, cuts, spline_offset); const int result_size = offsets.last() + int(!spline.is_cyclic()); @@ -347,11 +346,11 @@ static std::unique_ptr<CurveEval> subdivide_curve(const CurveEval &input_curve, return output_curve; } -static void geo_node_subdivide_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - geometry_set = bke::geometry_set_realize_instances(geometry_set); + geometry_set = geometry::realize_instances_legacy(geometry_set); if (!geometry_set.has_curve()) { params.set_output("Geometry", geometry_set); @@ -359,34 +358,35 @@ static void geo_node_subdivide_exec(GeoNodeExecParams params) } const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>(); - GVArray_Typed<int> cuts = params.get_input_attribute<int>( - "Cuts", component, ATTR_DOMAIN_POINT, 0); - if (cuts->is_single() && cuts->get_internal_single() < 1) { + VArray<int> cuts = params.get_input_attribute<int>("Cuts", component, ATTR_DOMAIN_POINT, 0); + if (cuts.is_single() && cuts.get_internal_single() < 1) { params.set_output("Geometry", geometry_set); return; } - std::unique_ptr<CurveEval> output_curve = subdivide_curve(*component.get_for_read(), *cuts); + std::unique_ptr<CurveEval> output_curve = subdivide_curve(*component.get_for_read(), cuts); params.set_output("Geometry", GeometrySet::create_with_curve(output_curve.release())); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_legacy_curve_subdivide_cc -void register_node_type_geo_curve_subdivide() +void register_node_type_geo_legacy_curve_subdivide() { + namespace file_ns = blender::nodes::node_geo_legacy_curve_subdivide_cc; + static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_LEGACY_CURVE_SUBDIVIDE, "Curve Subdivide", NODE_CLASS_GEOMETRY, 0); - ntype.declare = blender::nodes::geo_node_curve_subdivide_declare; - ntype.draw_buttons = blender::nodes::geo_node_curve_subdivide_layout; + &ntype, GEO_NODE_LEGACY_CURVE_SUBDIVIDE, "Curve Subdivide", NODE_CLASS_GEOMETRY); + ntype.declare = file_ns::node_declare; + ntype.draw_buttons = file_ns::node_layout; node_type_storage(&ntype, "NodeGeometryCurveSubdivide", node_free_standard_storage, node_copy_standard_storage); - node_type_init(&ntype, blender::nodes::geo_node_curve_subdivide_init); - node_type_update(&ntype, blender::nodes::geo_node_curve_subdivide_update); - ntype.geometry_node_execute = blender::nodes::geo_node_subdivide_exec; + node_type_init(&ntype, file_ns::node_init); + node_type_update(&ntype, file_ns::node_update); + ntype.geometry_node_execute = file_ns::node_geo_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_to_points.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_to_points.cc index 0c435d69991..1e6b7f92a77 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_to_points.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_to_points.cc @@ -28,29 +28,80 @@ namespace blender::nodes { -static void geo_node_curve_to_points_declare(NodeDeclarationBuilder &b) +static GMutableSpan create_attribute_and_retrieve_span(PointCloudComponent &points, + const AttributeIDRef &attribute_id, + const CustomDataType data_type) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Int>("Count").default_value(10).min(2).max(100000); - b.add_input<decl::Float>("Length").default_value(0.1f).min(0.001f).subtype(PROP_DISTANCE); - b.add_output<decl::Geometry>("Geometry"); + points.attribute_try_create(attribute_id, ATTR_DOMAIN_POINT, data_type, AttributeInitDefault()); + WriteAttributeLookup attribute = points.attribute_try_get_for_write(attribute_id); + BLI_assert(attribute); + return attribute.varray.get_internal_span(); } -static void geo_node_curve_to_points_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +template<typename T> +static MutableSpan<T> create_attribute_and_retrieve_span(PointCloudComponent &points, + const AttributeIDRef &attribute_id) +{ + GMutableSpan attribute = create_attribute_and_retrieve_span( + points, attribute_id, bke::cpp_type_to_custom_data_type(CPPType::get<T>())); + return attribute.typed<T>(); +} + +CurveToPointsResults curve_to_points_create_result_attributes(PointCloudComponent &points, + const CurveEval &curve) +{ + CurveToPointsResults attributes; + + attributes.result_size = points.attribute_domain_size(ATTR_DOMAIN_POINT); + + attributes.positions = create_attribute_and_retrieve_span<float3>(points, "position"); + attributes.radii = create_attribute_and_retrieve_span<float>(points, "radius"); + attributes.tilts = create_attribute_and_retrieve_span<float>(points, "tilt"); + + /* Because of the invariants of the curve component, we use the attributes of the + * first spline as a representative for the attribute meta data all splines. */ + curve.splines().first()->attributes.foreach_attribute( + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + attributes.point_attributes.add_new( + attribute_id, + create_attribute_and_retrieve_span(points, attribute_id, meta_data.data_type)); + return true; + }, + ATTR_DOMAIN_POINT); + + attributes.tangents = create_attribute_and_retrieve_span<float3>(points, "tangent"); + attributes.normals = create_attribute_and_retrieve_span<float3>(points, "normal"); + attributes.rotations = create_attribute_and_retrieve_span<float3>(points, "rotation"); + + return attributes; +} + +} // namespace blender::nodes + +namespace blender::nodes::node_geo_legacy_curve_to_points_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::Int>(N_("Count")).default_value(10).min(2).max(100000); + b.add_input<decl::Float>(N_("Length")).default_value(0.1f).min(0.001f).subtype(PROP_DISTANCE); + b.add_output<decl::Geometry>(N_("Geometry")); +} + +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "mode", 0, "", ICON_NONE); } -static void geo_node_curve_to_points_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeGeometryCurveToPoints *data = (NodeGeometryCurveToPoints *)MEM_callocN( - sizeof(NodeGeometryCurveToPoints), __func__); + NodeGeometryCurveToPoints *data = MEM_cnew<NodeGeometryCurveToPoints>(__func__); data->mode = GEO_NODE_CURVE_RESAMPLE_COUNT; node->storage = data; } -static void geo_node_curve_to_points_update(bNodeTree *UNUSED(ntree), bNode *node) +static void node_update(bNodeTree *ntree, bNode *node) { NodeGeometryCurveToPoints &node_storage = *(NodeGeometryCurveToPoints *)node->storage; const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)node_storage.mode; @@ -58,8 +109,8 @@ static void geo_node_curve_to_points_update(bNodeTree *UNUSED(ntree), bNode *nod bNodeSocket *count_socket = ((bNodeSocket *)node->inputs.first)->next; bNodeSocket *length_socket = count_socket->next; - nodeSetSocketAvailability(count_socket, mode == GEO_NODE_CURVE_RESAMPLE_COUNT); - nodeSetSocketAvailability(length_socket, mode == GEO_NODE_CURVE_RESAMPLE_LENGTH); + nodeSetSocketAvailability(ntree, count_socket, mode == GEO_NODE_CURVE_RESAMPLE_COUNT); + nodeSetSocketAvailability(ntree, length_socket, mode == GEO_NODE_CURVE_RESAMPLE_LENGTH); } /** @@ -114,54 +165,6 @@ static Array<int> calculate_spline_point_offsets(GeoNodeExecParams ¶ms, return {0}; } -static GMutableSpan create_attribute_and_retrieve_span(PointCloudComponent &points, - const AttributeIDRef &attribute_id, - const CustomDataType data_type) -{ - points.attribute_try_create(attribute_id, ATTR_DOMAIN_POINT, data_type, AttributeInitDefault()); - WriteAttributeLookup attribute = points.attribute_try_get_for_write(attribute_id); - BLI_assert(attribute); - return attribute.varray->get_internal_span(); -} - -template<typename T> -static MutableSpan<T> create_attribute_and_retrieve_span(PointCloudComponent &points, - const AttributeIDRef &attribute_id) -{ - GMutableSpan attribute = create_attribute_and_retrieve_span( - points, attribute_id, bke::cpp_type_to_custom_data_type(CPPType::get<T>())); - return attribute.typed<T>(); -} - -CurveToPointsResults curve_to_points_create_result_attributes(PointCloudComponent &points, - const CurveEval &curve) -{ - CurveToPointsResults attributes; - - attributes.result_size = points.attribute_domain_size(ATTR_DOMAIN_POINT); - - attributes.positions = create_attribute_and_retrieve_span<float3>(points, "position"); - attributes.radii = create_attribute_and_retrieve_span<float>(points, "radius"); - attributes.tilts = create_attribute_and_retrieve_span<float>(points, "tilt"); - - /* Because of the invariants of the curve component, we use the attributes of the - * first spline as a representative for the attribute meta data all splines. */ - curve.splines().first()->attributes.foreach_attribute( - [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { - attributes.point_attributes.add_new( - attribute_id, - create_attribute_and_retrieve_span(points, attribute_id, meta_data.data_type)); - return true; - }, - ATTR_DOMAIN_POINT); - - attributes.tangents = create_attribute_and_retrieve_span<float3>(points, "tangent"); - attributes.normals = create_attribute_and_retrieve_span<float3>(points, "normal"); - attributes.rotations = create_attribute_and_retrieve_span<float3>(points, "rotation"); - - return attributes; -} - /** * TODO: For non-poly splines, this has double copies that could be avoided as part * of a general look at optimizing uses of #Spline::interpolate_to_evaluated. @@ -177,10 +180,10 @@ static void copy_evaluated_point_attributes(Span<SplinePtr> splines, const int size = offsets[i + 1] - offsets[i]; data.positions.slice(offset, size).copy_from(spline.evaluated_positions()); - spline.interpolate_to_evaluated(spline.radii())->materialize(data.radii.slice(offset, size)); - spline.interpolate_to_evaluated(spline.tilts())->materialize(data.tilts.slice(offset, size)); + spline.interpolate_to_evaluated(spline.radii()).materialize(data.radii.slice(offset, size)); + spline.interpolate_to_evaluated(spline.tilts()).materialize(data.tilts.slice(offset, size)); - for (const Map<AttributeIDRef, GMutableSpan>::Item &item : data.point_attributes.items()) { + for (const Map<AttributeIDRef, GMutableSpan>::Item item : data.point_attributes.items()) { const AttributeIDRef attribute_id = item.key; GMutableSpan point_span = item.value; @@ -188,7 +191,7 @@ static void copy_evaluated_point_attributes(Span<SplinePtr> splines, GSpan spline_span = *spline.attributes.get_for_read(attribute_id); spline.interpolate_to_evaluated(spline_span) - ->materialize(point_span.slice(offset, size).data()); + .materialize(point_span.slice(offset, size).data()); } data.tangents.slice(offset, size).copy_from(spline.evaluated_tangents()); @@ -223,14 +226,14 @@ static void copy_uniform_sample_point_attributes(Span<SplinePtr> splines, uniform_samples, data.tilts.slice(offset, size)); - for (const Map<AttributeIDRef, GMutableSpan>::Item &item : data.point_attributes.items()) { + for (const Map<AttributeIDRef, GMutableSpan>::Item item : data.point_attributes.items()) { const AttributeIDRef attribute_id = item.key; GMutableSpan point_span = item.value; BLI_assert(spline.attributes.get_for_read(attribute_id)); GSpan spline_span = *spline.attributes.get_for_read(attribute_id); - spline.sample_with_index_factors(*spline.interpolate_to_evaluated(spline_span), + spline.sample_with_index_factors(spline.interpolate_to_evaluated(spline_span), uniform_samples, point_span.slice(offset, size)); } @@ -238,13 +241,13 @@ static void copy_uniform_sample_point_attributes(Span<SplinePtr> splines, spline.sample_with_index_factors<float3>( spline.evaluated_tangents(), uniform_samples, data.tangents.slice(offset, size)); for (float3 &tangent : data.tangents) { - tangent.normalize(); + tangent = math::normalize(tangent); } spline.sample_with_index_factors<float3>( spline.evaluated_normals(), uniform_samples, data.normals.slice(offset, size)); for (float3 &normals : data.normals) { - normals.normalize(); + normals = math::normalize(normals); } } }); @@ -263,20 +266,20 @@ static void copy_spline_domain_attributes(const CurveComponent &curve_component, if (meta_data.domain != ATTR_DOMAIN_CURVE) { return true; } - GVArrayPtr spline_attribute = curve_component.attribute_get_for_read( + GVArray spline_attribute = curve_component.attribute_get_for_read( attribute_id, ATTR_DOMAIN_CURVE, meta_data.data_type); - const CPPType &type = spline_attribute->type(); + const CPPType &type = spline_attribute.type(); OutputAttribute result_attribute = points.attribute_try_get_for_output_only( attribute_id, ATTR_DOMAIN_POINT, meta_data.data_type); GMutableSpan result = result_attribute.as_span(); - for (const int i : IndexRange(spline_attribute->size())) { + for (const int i : spline_attribute.index_range()) { const int offset = offsets[i]; const int size = offsets[i + 1] - offsets[i]; if (size != 0) { BUFFER_FOR_CPP_TYPE_VALUE(type, buffer); - spline_attribute->get(i, buffer); + spline_attribute.get(i, buffer); type.fill_assign_n(buffer, result[offset], size); } } @@ -286,25 +289,13 @@ static void copy_spline_domain_attributes(const CurveComponent &curve_component, }); } -void curve_create_default_rotation_attribute(Span<float3> tangents, - Span<float3> normals, - MutableSpan<float3> rotations) -{ - threading::parallel_for(IndexRange(rotations.size()), 512, [&](IndexRange range) { - for (const int i : range) { - rotations[i] = - float4x4::from_normalized_axis_data({0, 0, 0}, normals[i], tangents[i]).to_euler(); - } - }); -} - -static void geo_node_curve_to_points_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { NodeGeometryCurveToPoints &node_storage = *(NodeGeometryCurveToPoints *)params.node().storage; const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)node_storage.mode; GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - geometry_set = bke::geometry_set_realize_instances(geometry_set); + geometry_set = geometry::realize_instances_legacy(geometry_set); if (!geometry_set.has_curve()) { params.set_output("Geometry", GeometrySet()); @@ -352,21 +343,23 @@ static void geo_node_curve_to_points_exec(GeoNodeExecParams params) params.set_output("Geometry", std::move(result)); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_legacy_curve_to_points_cc -void register_node_type_geo_curve_to_points() +void register_node_type_geo_legacy_curve_to_points() { + namespace file_ns = blender::nodes::node_geo_legacy_curve_to_points_cc; + static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_LEGACY_CURVE_TO_POINTS, "Curve to Points", NODE_CLASS_GEOMETRY, 0); - ntype.declare = blender::nodes::geo_node_curve_to_points_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_curve_to_points_exec; - ntype.draw_buttons = blender::nodes::geo_node_curve_to_points_layout; + &ntype, GEO_NODE_LEGACY_CURVE_TO_POINTS, "Curve to Points", NODE_CLASS_GEOMETRY); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.draw_buttons = file_ns::node_layout; node_type_storage( &ntype, "NodeGeometryCurveToPoints", node_free_standard_storage, node_copy_standard_storage); - node_type_init(&ntype, blender::nodes::geo_node_curve_to_points_init); - node_type_update(&ntype, blender::nodes::geo_node_curve_to_points_update); + node_type_init(&ntype, file_ns::node_init); + node_type_update(&ntype, file_ns::node_update); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_delete_geometry.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_delete_geometry.cc index 1e2f652cd78..f7fd12d775a 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_delete_geometry.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_delete_geometry.cc @@ -26,31 +26,16 @@ #include "node_geometry_util.hh" -using blender::bke::CustomDataAttributes; - -/* Code from the mask modifier in MOD_mask.cc. */ -extern void copy_masked_vertices_to_new_mesh(const Mesh &src_mesh, - Mesh &dst_mesh, - blender::Span<int> vertex_map); -extern void copy_masked_edges_to_new_mesh(const Mesh &src_mesh, - Mesh &dst_mesh, - blender::Span<int> vertex_map, - blender::Span<int> edge_map); -extern void copy_masked_polys_to_new_mesh(const Mesh &src_mesh, - Mesh &dst_mesh, - blender::Span<int> vertex_map, - blender::Span<int> edge_map, - blender::Span<int> masked_poly_indices, - blender::Span<int> new_loop_starts); +namespace blender::nodes::node_geo_legacy_delete_geometry_cc { -namespace blender::nodes { +using blender::bke::CustomDataAttributes; -static void geo_node_delete_geometry_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::String>("Selection"); - b.add_input<decl::Bool>("Invert"); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::String>(N_("Selection")); + b.add_input<decl::Bool>(N_("Invert")); + b.add_output<decl::Geometry>(N_("Geometry")); } template<typename T> static void copy_data(Span<T> data, MutableSpan<T> r_data, IndexMask mask) @@ -60,6 +45,78 @@ template<typename T> static void copy_data(Span<T> data, MutableSpan<T> r_data, } } +static void copy_masked_vertices_to_new_mesh(const Mesh &src_mesh, + Mesh &dst_mesh, + Span<int> vertex_map) +{ + BLI_assert(src_mesh.totvert == vertex_map.size()); + for (const int i_src : vertex_map.index_range()) { + const int i_dst = vertex_map[i_src]; + if (i_dst == -1) { + continue; + } + + const MVert &v_src = src_mesh.mvert[i_src]; + MVert &v_dst = dst_mesh.mvert[i_dst]; + + v_dst = v_src; + CustomData_copy_data(&src_mesh.vdata, &dst_mesh.vdata, i_src, i_dst, 1); + } +} + +static void copy_masked_edges_to_new_mesh(const Mesh &src_mesh, + Mesh &dst_mesh, + Span<int> vertex_map, + Span<int> edge_map) +{ + BLI_assert(src_mesh.totvert == vertex_map.size()); + BLI_assert(src_mesh.totedge == edge_map.size()); + for (const int i_src : IndexRange(src_mesh.totedge)) { + const int i_dst = edge_map[i_src]; + if (ELEM(i_dst, -1, -2)) { + continue; + } + + const MEdge &e_src = src_mesh.medge[i_src]; + MEdge &e_dst = dst_mesh.medge[i_dst]; + + CustomData_copy_data(&src_mesh.edata, &dst_mesh.edata, i_src, i_dst, 1); + e_dst = e_src; + e_dst.v1 = vertex_map[e_src.v1]; + e_dst.v2 = vertex_map[e_src.v2]; + } +} + +static void copy_masked_polys_to_new_mesh(const Mesh &src_mesh, + Mesh &dst_mesh, + Span<int> vertex_map, + Span<int> edge_map, + Span<int> masked_poly_indices, + Span<int> new_loop_starts) +{ + for (const int i_dst : masked_poly_indices.index_range()) { + const int i_src = masked_poly_indices[i_dst]; + + const MPoly &mp_src = src_mesh.mpoly[i_src]; + MPoly &mp_dst = dst_mesh.mpoly[i_dst]; + const int i_ml_src = mp_src.loopstart; + const int i_ml_dst = new_loop_starts[i_dst]; + + CustomData_copy_data(&src_mesh.pdata, &dst_mesh.pdata, i_src, i_dst, 1); + CustomData_copy_data(&src_mesh.ldata, &dst_mesh.ldata, i_ml_src, i_ml_dst, mp_src.totloop); + + const MLoop *ml_src = src_mesh.mloop + i_ml_src; + MLoop *ml_dst = dst_mesh.mloop + i_ml_dst; + + mp_dst = mp_src; + mp_dst.loopstart = i_ml_dst; + for (int i : IndexRange(mp_src.totloop)) { + ml_dst[i].v = vertex_map[ml_src[i].v]; + ml_dst[i].e = edge_map[ml_src[i].e]; + } + } +} + static void spline_copy_builtin_attributes(const Spline &spline, Spline &r_spline, const IndexMask mask) @@ -137,7 +194,7 @@ static std::unique_ptr<CurveEval> curve_delete(const CurveEval &input_curve, Vector<int64_t> copied_splines; if (input_curve.attributes.get_for_read(name)) { - GVArray_Typed<bool> selection = input_curve.attributes.get_for_read<bool>(name, false); + VArray<bool> selection = input_curve.attributes.get_for_read<bool>(name, false); for (const int i : input_splines.index_range()) { if (selection[i] == invert) { output_curve->add_spline(input_splines[i]->copy()); @@ -151,7 +208,7 @@ static std::unique_ptr<CurveEval> curve_delete(const CurveEval &input_curve, for (const int i : input_splines.index_range()) { const Spline &spline = *input_splines[i]; - GVArray_Typed<bool> selection = spline.attributes.get_for_read<bool>(name, false); + VArray<bool> selection = spline.attributes.get_for_read<bool>(name, false); indices_to_copy.clear(); for (const int i_point : IndexRange(spline.size())) { @@ -202,7 +259,7 @@ static void delete_point_cloud_selection(const PointCloudComponent &in_component const StringRef selection_name, const bool invert) { - const GVArray_Typed<bool> selection_attribute = in_component.attribute_get_for_read<bool>( + const VArray<bool> selection_attribute = in_component.attribute_get_for_read<bool>( selection_name, ATTR_DOMAIN_POINT, false); VArray_Span<bool> selection{selection_attribute}; @@ -590,7 +647,7 @@ static void delete_mesh_selection(MeshComponent &component, const AttributeDomain selection_domain = get_mesh_selection_domain(component, selection_name); /* This already checks if the attribute exists, and displays a warning in that case. */ - GVArray_Typed<bool> selection = component.attribute_get_for_read<bool>( + VArray<bool> selection = component.attribute_get_for_read<bool>( selection_name, selection_domain, false); /* Check if there is anything to delete. */ @@ -627,10 +684,10 @@ static void delete_mesh_selection(MeshComponent &component, component.replace(mesh_out); } -static void geo_node_delete_geometry_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - geometry_set = bke::geometry_set_realize_instances(geometry_set); + geometry_set = geometry::realize_instances_legacy(geometry_set); const bool invert = params.extract_input<bool>("Invert"); const std::string selection_name = params.extract_input<std::string>("Selection"); @@ -662,16 +719,18 @@ static void geo_node_delete_geometry_exec(GeoNodeExecParams params) params.set_output("Geometry", std::move(out_set)); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_legacy_delete_geometry_cc -void register_node_type_geo_delete_geometry() +void register_node_type_geo_legacy_delete_geometry() { + namespace file_ns = blender::nodes::node_geo_legacy_delete_geometry_cc; + static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_LEGACY_DELETE_GEOMETRY, "Delete Geometry", NODE_CLASS_GEOMETRY, 0); + &ntype, GEO_NODE_LEGACY_DELETE_GEOMETRY, "Delete Geometry", NODE_CLASS_GEOMETRY); - ntype.declare = blender::nodes::geo_node_delete_geometry_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_delete_geometry_exec; + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_edge_split.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_edge_split.cc index 2ea6516996d..e628edb7e17 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_edge_split.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_edge_split.cc @@ -22,26 +22,26 @@ extern "C" { Mesh *doEdgeSplit(const Mesh *mesh, EdgeSplitModifierData *emd); } -namespace blender::nodes { +namespace blender::nodes::node_geo_legacy_edge_split_cc { -static void geo_node_edge_split_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Bool>("Edge Angle").default_value(true); - b.add_input<decl::Float>("Angle") + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::Bool>(N_("Edge Angle")).default_value(true); + b.add_input<decl::Float>(N_("Angle")) .default_value(DEG2RADF(30.0f)) .min(0.0f) .max(DEG2RADF(180.0f)) .subtype(PROP_ANGLE); - b.add_input<decl::Bool>("Sharp Edges"); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Bool>(N_("Sharp Edges")); + b.add_output<decl::Geometry>(N_("Geometry")); } -static void geo_node_edge_split_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - geometry_set = geometry_set_realize_instances(geometry_set); + geometry_set = geometry::realize_instances_legacy(geometry_set); if (!geometry_set.has_mesh()) { params.set_output("Geometry", std::move(geometry_set)); @@ -76,14 +76,16 @@ static void geo_node_edge_split_exec(GeoNodeExecParams params) params.set_output("Geometry", std::move(geometry_set)); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_legacy_edge_split_cc -void register_node_type_geo_edge_split() +void register_node_type_geo_legacy_edge_split() { + namespace file_ns = blender::nodes::node_geo_legacy_edge_split_cc; + static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_LEGACY_EDGE_SPLIT, "Edge Split", NODE_CLASS_GEOMETRY, 0); - ntype.geometry_node_execute = blender::nodes::geo_node_edge_split_exec; - ntype.declare = blender::nodes::geo_node_edge_split_declare; + geo_node_type_base(&ntype, GEO_NODE_LEGACY_EDGE_SPLIT, "Edge Split", NODE_CLASS_GEOMETRY); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.declare = file_ns::node_declare; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_material_assign.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_material_assign.cc index 7d3481c1067..8fd6b1e299f 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_material_assign.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_material_assign.cc @@ -24,14 +24,14 @@ #include "BKE_material.h" -namespace blender::nodes { +namespace blender::nodes::node_geo_legacy_material_assign_cc { -static void geo_node_legacy_material_assign_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Material>("Material").hide_label(true); - b.add_input<decl::String>("Selection"); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::Material>(N_("Material")).hide_label(true); + b.add_input<decl::String>(N_("Selection")); + b.add_output<decl::Geometry>(N_("Geometry")); } static void assign_material_to_faces(Mesh &mesh, const VArray<bool> &face_mask, Material *material) @@ -59,20 +59,20 @@ static void assign_material_to_faces(Mesh &mesh, const VArray<bool> &face_mask, } } -static void geo_node_legacy_material_assign_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { Material *material = params.extract_input<Material *>("Material"); const std::string mask_name = params.extract_input<std::string>("Selection"); GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - geometry_set = geometry_set_realize_instances(geometry_set); + geometry_set = geometry::realize_instances_legacy(geometry_set); if (geometry_set.has<MeshComponent>()) { MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>(); Mesh *mesh = mesh_component.get_for_write(); if (mesh != nullptr) { - GVArray_Typed<bool> face_mask = mesh_component.attribute_get_for_read<bool>( + VArray<bool> face_mask = mesh_component.attribute_get_for_read<bool>( mask_name, ATTR_DOMAIN_FACE, true); assign_material_to_faces(*mesh, face_mask, material); } @@ -81,15 +81,17 @@ static void geo_node_legacy_material_assign_exec(GeoNodeExecParams params) params.set_output("Geometry", std::move(geometry_set)); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_legacy_material_assign_cc void register_node_type_geo_legacy_material_assign() { + namespace file_ns = blender::nodes::node_geo_legacy_material_assign_cc; + static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_LEGACY_MATERIAL_ASSIGN, "Material Assign", NODE_CLASS_GEOMETRY, 0); - ntype.declare = blender::nodes::geo_node_legacy_material_assign_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_legacy_material_assign_exec; + &ntype, GEO_NODE_LEGACY_MATERIAL_ASSIGN, "Material Assign", NODE_CLASS_GEOMETRY); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_mesh_to_curve.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_mesh_to_curve.cc new file mode 100644 index 00000000000..d026fff003f --- /dev/null +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_mesh_to_curve.cc @@ -0,0 +1,80 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "GEO_mesh_to_curve.hh" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_legacy_mesh_to_curve_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>(N_("Mesh")); + b.add_input<decl::String>(N_("Selection")); + b.add_output<decl::Geometry>(N_("Curve")); +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh"); + + geometry_set = geometry::realize_instances_legacy(geometry_set); + + if (!geometry_set.has_mesh()) { + params.set_default_remaining_outputs(); + return; + } + + const MeshComponent &component = *geometry_set.get_component_for_read<MeshComponent>(); + const std::string selection_name = params.extract_input<std::string>("Selection"); + if (!selection_name.empty() && !component.attribute_exists(selection_name)) { + params.error_message_add(NodeWarningType::Error, + TIP_("No attribute with name \"") + selection_name + "\""); + } + VArray<bool> selection = component.attribute_get_for_read<bool>( + selection_name, ATTR_DOMAIN_EDGE, true); + + Vector<int64_t> selected_edge_indices; + for (const int64_t i : IndexRange(component.attribute_domain_size(ATTR_DOMAIN_EDGE))) { + if (selection[i]) { + selected_edge_indices.append(i); + } + } + + if (selected_edge_indices.size() == 0) { + params.set_default_remaining_outputs(); + return; + } + + std::unique_ptr<CurveEval> curve = geometry::mesh_to_curve_convert( + component, IndexMask(selected_edge_indices)); + + params.set_output("Curve", GeometrySet::create_with_curve(curve.release())); +} + +} // namespace blender::nodes::node_geo_legacy_mesh_to_curve_cc + +void register_node_type_geo_legacy_mesh_to_curve() +{ + namespace file_ns = blender::nodes::node_geo_legacy_mesh_to_curve_cc; + + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_LEGACY_MESH_TO_CURVE, "Mesh to Curve", NODE_CLASS_GEOMETRY); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_distribute.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_distribute.cc index f95b0da86ed..29eff373d15 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_distribute.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_distribute.cc @@ -36,36 +36,35 @@ #include "node_geometry_util.hh" -using blender::bke::GeometryInstanceGroup; +namespace blender::nodes::node_geo_legacy_point_distribute_cc { -namespace blender::nodes { +using blender::bke::GeometryInstanceGroup; -static void geo_node_point_distribute_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Float>("Distance Min").min(0.0f).max(100000.0f).subtype(PROP_DISTANCE); - b.add_input<decl::Float>("Density Max") + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::Float>(N_("Distance Min")).min(0.0f).max(100000.0f).subtype(PROP_DISTANCE); + b.add_input<decl::Float>(N_("Density Max")) .default_value(1.0f) .min(0.0f) .max(100000.0f) .subtype(PROP_NONE); - b.add_input<decl::String>("Density Attribute"); - b.add_input<decl::Int>("Seed").min(-10000).max(10000); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::String>(N_("Density Attribute")); + b.add_input<decl::Int>(N_("Seed")).min(-10000).max(10000); + b.add_output<decl::Geometry>(N_("Geometry")); } -static void geo_node_point_distribute_layout(uiLayout *layout, - bContext *UNUSED(C), - PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "distribute_method", 0, "", ICON_NONE); } -static void node_point_distribute_update(bNodeTree *UNUSED(ntree), bNode *node) +static void node_point_distribute_update(bNodeTree *ntree, bNode *node) { bNodeSocket *sock_min_dist = (bNodeSocket *)BLI_findlink(&node->inputs, 1); - nodeSetSocketAvailability(sock_min_dist, ELEM(node->custom1, GEO_NODE_POINT_DISTRIBUTE_POISSON)); + nodeSetSocketAvailability( + ntree, sock_min_dist, ELEM(node->custom1, GEO_NODE_POINT_DISTRIBUTE_POISSON)); } /** @@ -106,9 +105,9 @@ static void sample_mesh_surface(const Mesh &mesh, float looptri_density_factor = 1.0f; if (density_factors != nullptr) { - const float v0_density_factor = std::max(0.0f, density_factors->get(v0_loop)); - const float v1_density_factor = std::max(0.0f, density_factors->get(v1_loop)); - const float v2_density_factor = std::max(0.0f, density_factors->get(v2_loop)); + const float v0_density_factor = std::max(0.0f, (*density_factors)[v0_loop]); + const float v1_density_factor = std::max(0.0f, (*density_factors)[v1_loop]); + const float v2_density_factor = std::max(0.0f, (*density_factors)[v2_loop]); looptri_density_factor = (v0_density_factor + v1_density_factor + v2_density_factor) / 3.0f; } const float area = area_tri_v3(v0_pos, v1_pos, v2_pos); @@ -252,18 +251,26 @@ BLI_NOINLINE static void interpolate_attribute(const Mesh &mesh, { switch (source_domain) { case ATTR_DOMAIN_POINT: { - bke::mesh_surface_sample::sample_point_attribute( - mesh, looptri_indices, bary_coords, source_data, output_data); + bke::mesh_surface_sample::sample_point_attribute(mesh, + looptri_indices, + bary_coords, + source_data, + IndexMask(output_data.size()), + output_data); break; } case ATTR_DOMAIN_CORNER: { - bke::mesh_surface_sample::sample_corner_attribute( - mesh, looptri_indices, bary_coords, source_data, output_data); + bke::mesh_surface_sample::sample_corner_attribute(mesh, + looptri_indices, + bary_coords, + source_data, + IndexMask(output_data.size()), + output_data); break; } case ATTR_DOMAIN_FACE: { bke::mesh_surface_sample::sample_face_attribute( - mesh, looptri_indices, source_data, output_data); + mesh, looptri_indices, source_data, IndexMask(output_data.size()), output_data); break; } default: { @@ -307,21 +314,21 @@ BLI_NOINLINE static void interpolate_existing_attributes( } const AttributeDomain source_domain = attribute_info->domain; - GVArrayPtr source_attribute = source_component.attribute_get_for_read( + GVArray source_attribute = source_component.attribute_get_for_read( attribute_id, source_domain, output_data_type, nullptr); if (!source_attribute) { i_instance += set_group.transforms.size(); continue; } - for (const int UNUSED(i_set_instance) : set_group.transforms.index_range()) { + for ([[maybe_unused]] const int i_set_instance : set_group.transforms.index_range()) { const int offset = instance_start_offsets[i_instance]; Span<float3> bary_coords = bary_coords_array[i_instance]; Span<int> looptri_indices = looptri_indices_array[i_instance]; GMutableSpan instance_span = out_span.slice(offset, bary_coords.size()); interpolate_attribute( - mesh, bary_coords, looptri_indices, source_domain, *source_attribute, instance_span); + mesh, bary_coords, looptri_indices, source_domain, source_attribute, instance_span); i_instance++; } @@ -329,7 +336,7 @@ BLI_NOINLINE static void interpolate_existing_attributes( attribute_math::convert_to_static_type(output_data_type, [&](auto dummy) { using T = decltype(dummy); - GVArray_Span<T> source_span{*source_attribute}; + VArray_Span source_span{source_attribute.typed<T>()}; }); } @@ -437,7 +444,7 @@ static void distribute_points_random(Span<GeometryInstanceGroup> set_groups, for (const GeometryInstanceGroup &set_group : set_groups) { const GeometrySet &set = set_group.geometry_set; const MeshComponent &component = *set.get_component_for_read<MeshComponent>(); - GVArray_Typed<float> density_factors = component.attribute_get_for_read<float>( + VArray<float> density_factors = component.attribute_get_for_read<float>( density_attribute_name, ATTR_DOMAIN_CORNER, use_one_default ? 1.0f : 0.0f); const Mesh &mesh = *component.get_for_read(); for (const float4x4 &transform : set_group.transforms) { @@ -447,7 +454,7 @@ static void distribute_points_random(Span<GeometryInstanceGroup> set_groups, sample_mesh_surface(mesh, transform, density, - &*density_factors, + &density_factors, seed, positions, bary_coords, @@ -506,10 +513,10 @@ static void distribute_points_poisson_disk(Span<GeometryInstanceGroup> set_group const GeometrySet &set = set_group.geometry_set; const MeshComponent &component = *set.get_component_for_read<MeshComponent>(); const Mesh &mesh = *component.get_for_read(); - const GVArray_Typed<float> density_factors = component.attribute_get_for_read<float>( + const VArray<float> density_factors = component.attribute_get_for_read<float>( density_attribute_name, ATTR_DOMAIN_CORNER, use_one_default ? 1.0f : 0.0f); - for (const int UNUSED(i_set_instance) : set_group.transforms.index_range()) { + for ([[maybe_unused]] const int i_set_instance : set_group.transforms.index_range()) { Vector<float3> &positions = positions_all[i_instance]; Vector<float3> &bary_coords = bary_coords_all[i_instance]; Vector<int> &looptri_indices = looptri_indices_all[i_instance]; @@ -532,7 +539,7 @@ static void distribute_points_poisson_disk(Span<GeometryInstanceGroup> set_group } } -static void geo_node_point_distribute_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); @@ -545,14 +552,14 @@ static void geo_node_point_distribute_exec(GeoNodeExecParams params) "Density Attribute"); if (density <= 0.0f) { - params.set_output("Geometry", GeometrySet()); + params.set_default_remaining_outputs(); return; } Vector<GeometryInstanceGroup> set_groups; geometry_set_gather_instances(geometry_set, set_groups); if (set_groups.is_empty()) { - params.set_output("Geometry", GeometrySet()); + params.set_default_remaining_outputs(); return; } @@ -566,7 +573,7 @@ static void geo_node_point_distribute_exec(GeoNodeExecParams params) if (set_groups.is_empty()) { params.error_message_add(NodeWarningType::Error, TIP_("Input geometry must contain a mesh")); - params.set_output("Geometry", GeometrySet()); + params.set_default_remaining_outputs(); return; } @@ -615,6 +622,11 @@ static void geo_node_point_distribute_exec(GeoNodeExecParams params) final_points_len += positions.size(); } + if (final_points_len == 0) { + params.set_default_remaining_outputs(); + return; + } + PointCloud *pointcloud = BKE_pointcloud_new_nomain(final_points_len); for (const int instance_index : positions_all.index_range()) { const int offset = instance_start_offsets[instance_index]; @@ -641,17 +653,19 @@ static void geo_node_point_distribute_exec(GeoNodeExecParams params) params.set_output("Geometry", std::move(geometry_set_out)); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_legacy_point_distribute_cc void register_node_type_geo_point_distribute() { + namespace file_ns = blender::nodes::node_geo_legacy_point_distribute_cc; + static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_LEGACY_POINT_DISTRIBUTE, "Point Distribute", NODE_CLASS_GEOMETRY, 0); - node_type_update(&ntype, blender::nodes::node_point_distribute_update); - ntype.declare = blender::nodes::geo_node_point_distribute_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_point_distribute_exec; - ntype.draw_buttons = blender::nodes::geo_node_point_distribute_layout; + &ntype, GEO_NODE_LEGACY_POINT_DISTRIBUTE, "Point Distribute", NODE_CLASS_GEOMETRY); + node_type_update(&ntype, file_ns::node_point_distribute_update); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.draw_buttons = file_ns::node_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_instance.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_instance.cc index fb45c22ced4..faf0b1a5fe7 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_instance.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_instance.cc @@ -24,19 +24,19 @@ #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_legacy_point_instance_cc { -static void geo_node_point_instance_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Object>("Object").hide_label(); - b.add_input<decl::Collection>("Collection").hide_label(); - b.add_input<decl::Geometry>("Instance Geometry"); - b.add_input<decl::Int>("Seed").min(-10000).max(10000); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::Object>(N_("Object")).hide_label(); + b.add_input<decl::Collection>(N_("Collection")).hide_label(); + b.add_input<decl::Geometry>(N_("Instance Geometry")); + b.add_input<decl::Int>(N_("Seed")).min(-10000).max(10000); + b.add_output<decl::Geometry>(N_("Geometry")); } -static void geo_node_point_instance_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "instance_type", 0, "", ICON_NONE); if (RNA_enum_get(ptr, "instance_type") == GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION) { @@ -44,16 +44,15 @@ static void geo_node_point_instance_layout(uiLayout *layout, bContext *UNUSED(C) } } -static void geo_node_point_instance_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeGeometryPointInstance *data = (NodeGeometryPointInstance *)MEM_callocN( - sizeof(NodeGeometryPointInstance), __func__); + NodeGeometryPointInstance *data = MEM_cnew<NodeGeometryPointInstance>(__func__); data->instance_type = GEO_NODE_POINT_INSTANCE_TYPE_OBJECT; data->flag |= GEO_NODE_POINT_INSTANCE_WHOLE_COLLECTION; node->storage = data; } -static void geo_node_point_instance_update(bNodeTree *UNUSED(tree), bNode *node) +static void node_update(bNodeTree *ntree, bNode *node) { bNodeSocket *object_socket = (bNodeSocket *)BLI_findlink(&node->inputs, 1); bNodeSocket *collection_socket = object_socket->next; @@ -65,12 +64,15 @@ static void geo_node_point_instance_update(bNodeTree *UNUSED(tree), bNode *node) const bool use_whole_collection = (node_storage->flag & GEO_NODE_POINT_INSTANCE_WHOLE_COLLECTION) != 0; - nodeSetSocketAvailability(object_socket, type == GEO_NODE_POINT_INSTANCE_TYPE_OBJECT); - nodeSetSocketAvailability(collection_socket, type == GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION); - nodeSetSocketAvailability(instance_geometry_socket, - type == GEO_NODE_POINT_INSTANCE_TYPE_GEOMETRY); + nodeSetSocketAvailability(ntree, object_socket, type == GEO_NODE_POINT_INSTANCE_TYPE_OBJECT); nodeSetSocketAvailability( - seed_socket, type == GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION && !use_whole_collection); + ntree, collection_socket, type == GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION); + nodeSetSocketAvailability( + ntree, instance_geometry_socket, type == GEO_NODE_POINT_INSTANCE_TYPE_GEOMETRY); + nodeSetSocketAvailability(ntree, + seed_socket, + type == GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION && + !use_whole_collection); } static Vector<InstanceReference> get_instance_references__object(GeoNodeExecParams ¶ms) @@ -170,21 +172,25 @@ static void add_instances_from_component(InstancesComponent &instances, const AttributeDomain domain = ATTR_DOMAIN_POINT; const int domain_size = src_geometry.attribute_domain_size(domain); + if (domain_size == 0) { + return; + } - GVArray_Typed<float3> positions = src_geometry.attribute_get_for_read<float3>( + VArray<float3> positions = src_geometry.attribute_get_for_read<float3>( "position", domain, {0, 0, 0}); - GVArray_Typed<float3> rotations = src_geometry.attribute_get_for_read<float3>( + VArray<float3> rotations = src_geometry.attribute_get_for_read<float3>( "rotation", domain, {0, 0, 0}); - GVArray_Typed<float3> scales = src_geometry.attribute_get_for_read<float3>( - "scale", domain, {1, 1, 1}); - GVArray_Typed<int> id_attribute = src_geometry.attribute_get_for_read<int>("id", domain, -1); + VArray<float3> scales = src_geometry.attribute_get_for_read<float3>("scale", domain, {1, 1, 1}); + VArray<int> id_attribute = src_geometry.attribute_get_for_read<int>("id", domain, -1); /* The initial size of the component might be non-zero if there are two component types. */ const int start_len = instances.instances_amount(); instances.resize(start_len + domain_size); MutableSpan<int> handles = instances.instance_reference_handles().slice(start_len, domain_size); MutableSpan<float4x4> transforms = instances.instance_transforms().slice(start_len, domain_size); - MutableSpan<int> instance_ids = instances.instance_ids().slice(start_len, domain_size); + OutputAttribute_Typed<int> instance_id_attribute = + instances.attribute_try_get_for_output_only<int>("id", ATTR_DOMAIN_INSTANCE); + MutableSpan<int> instance_ids = instance_id_attribute.as_span(); /* Skip all of the randomness handling if there is only a single possible instance * (anything except for collection mode with "Whole Collection" turned off). */ @@ -211,16 +217,18 @@ static void add_instances_from_component(InstancesComponent &instances, } }); } + + instance_id_attribute.save(); } -static void geo_node_point_instance_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); GeometrySet geometry_set_out; /* TODO: This node should be able to instance on the input instances component * rather than making the entire input geometry set real. */ - geometry_set = geometry_set_realize_instances(geometry_set); + geometry_set = geometry::realize_instances_legacy(geometry_set); const Vector<InstanceReference> possible_references = get_instance_references(params); if (possible_references.is_empty()) { @@ -253,20 +261,22 @@ static void geo_node_point_instance_exec(GeoNodeExecParams params) params.set_output("Geometry", std::move(geometry_set_out)); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_legacy_point_instance_cc void register_node_type_geo_point_instance() { + namespace file_ns = blender::nodes::node_geo_legacy_point_instance_cc; + static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_LEGACY_POINT_INSTANCE, "Point Instance", NODE_CLASS_GEOMETRY, 0); - node_type_init(&ntype, blender::nodes::geo_node_point_instance_init); + &ntype, GEO_NODE_LEGACY_POINT_INSTANCE, "Point Instance", NODE_CLASS_GEOMETRY); + node_type_init(&ntype, file_ns::node_init); node_type_storage( &ntype, "NodeGeometryPointInstance", node_free_standard_storage, node_copy_standard_storage); - ntype.declare = blender::nodes::geo_node_point_instance_declare; - ntype.draw_buttons = blender::nodes::geo_node_point_instance_layout; - node_type_update(&ntype, blender::nodes::geo_node_point_instance_update); - ntype.geometry_node_execute = blender::nodes::geo_node_point_instance_exec; + ntype.declare = file_ns::node_declare; + ntype.draw_buttons = file_ns::node_layout; + node_type_update(&ntype, file_ns::node_update); + ntype.geometry_node_execute = file_ns::node_geo_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_rotate.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_rotate.cc index 60c82360007..ad87ec5541b 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_rotate.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_rotate.cc @@ -21,21 +21,23 @@ #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_legacy_point_rotate_cc { -static void geo_node_point_rotate_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::String>("Axis"); - b.add_input<decl::Vector>("Axis", "Axis_001").default_value({0.0, 0.0, 1.0}).subtype(PROP_XYZ); - b.add_input<decl::String>("Angle"); - b.add_input<decl::Float>("Angle", "Angle_001").subtype(PROP_ANGLE); - b.add_input<decl::String>("Rotation"); - b.add_input<decl::Vector>("Rotation", "Rotation_001").subtype(PROP_EULER); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::String>(N_("Axis")); + b.add_input<decl::Vector>(N_("Axis"), "Axis_001") + .default_value({0.0, 0.0, 1.0}) + .subtype(PROP_XYZ); + b.add_input<decl::String>(N_("Angle")); + b.add_input<decl::Float>(N_("Angle"), "Angle_001").subtype(PROP_ANGLE); + b.add_input<decl::String>(N_("Rotation")); + b.add_input<decl::Vector>(N_("Rotation"), "Rotation_001").subtype(PROP_EULER); + b.add_output<decl::Geometry>(N_("Geometry")); } -static void geo_node_point_rotate_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { NodeGeometryRotatePoints *storage = (NodeGeometryRotatePoints *)((bNode *)ptr->data)->storage; @@ -55,10 +57,9 @@ static void geo_node_point_rotate_layout(uiLayout *layout, bContext *UNUSED(C), } } -static void geo_node_point_rotate_init(bNodeTree *UNUSED(ntree), bNode *node) +static void node_init(bNodeTree *UNUSED(ntree), bNode *node) { - NodeGeometryRotatePoints *node_storage = (NodeGeometryRotatePoints *)MEM_callocN( - sizeof(NodeGeometryRotatePoints), __func__); + NodeGeometryRotatePoints *node_storage = MEM_cnew<NodeGeometryRotatePoints>(__func__); node_storage->type = GEO_NODE_POINT_ROTATE_TYPE_EULER; node_storage->space = GEO_NODE_POINT_ROTATE_SPACE_OBJECT; @@ -69,20 +70,23 @@ static void geo_node_point_rotate_init(bNodeTree *UNUSED(ntree), bNode *node) node->storage = node_storage; } -static void geo_node_point_rotate_update(bNodeTree *UNUSED(ntree), bNode *node) +static void node_update(bNodeTree *ntree, bNode *node) { NodeGeometryRotatePoints *node_storage = (NodeGeometryRotatePoints *)node->storage; update_attribute_input_socket_availabilities( + *ntree, *node, "Axis", (GeometryNodeAttributeInputMode)node_storage->input_type_axis, node_storage->type == GEO_NODE_POINT_ROTATE_TYPE_AXIS_ANGLE); update_attribute_input_socket_availabilities( + *ntree, *node, "Angle", (GeometryNodeAttributeInputMode)node_storage->input_type_angle, node_storage->type == GEO_NODE_POINT_ROTATE_TYPE_AXIS_ANGLE); update_attribute_input_socket_availabilities( + *ntree, *node, "Rotation", (GeometryNodeAttributeInputMode)node_storage->input_type_rotation, @@ -167,9 +171,9 @@ static void point_rotate_on_component(GeometryComponent &component, const int domain_size = rotations.size(); if (storage.type == GEO_NODE_POINT_ROTATE_TYPE_AXIS_ANGLE) { - GVArray_Typed<float3> axis = params.get_input_attribute<float3>( + VArray<float3> axis = params.get_input_attribute<float3>( "Axis", component, ATTR_DOMAIN_POINT, {0, 0, 1}); - GVArray_Typed<float> angles = params.get_input_attribute<float>( + VArray<float> angles = params.get_input_attribute<float>( "Angle", component, ATTR_DOMAIN_POINT, 0); if (storage.space == GEO_NODE_POINT_ROTATE_SPACE_OBJECT) { @@ -180,7 +184,7 @@ static void point_rotate_on_component(GeometryComponent &component, } } else { - GVArray_Typed<float3> eulers = params.get_input_attribute<float3>( + VArray<float3> eulers = params.get_input_attribute<float3>( "Rotation", component, ATTR_DOMAIN_POINT, {0, 0, 0}); if (storage.space == GEO_NODE_POINT_ROTATE_SPACE_OBJECT) { @@ -194,11 +198,11 @@ static void point_rotate_on_component(GeometryComponent &component, rotation_attribute.save(); } -static void geo_node_point_rotate_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - geometry_set = geometry_set_realize_instances(geometry_set); + geometry_set = geometry::realize_instances_legacy(geometry_set); if (geometry_set.has<MeshComponent>()) { point_rotate_on_component(geometry_set.get_component_for_write<MeshComponent>(), params); @@ -213,19 +217,21 @@ static void geo_node_point_rotate_exec(GeoNodeExecParams params) params.set_output("Geometry", geometry_set); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_legacy_point_rotate_cc void register_node_type_geo_point_rotate() { + namespace file_ns = blender::nodes::node_geo_legacy_point_rotate_cc; + static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_LEGACY_POINT_ROTATE, "Point Rotate", NODE_CLASS_GEOMETRY, 0); - node_type_init(&ntype, blender::nodes::geo_node_point_rotate_init); - node_type_update(&ntype, blender::nodes::geo_node_point_rotate_update); + geo_node_type_base(&ntype, GEO_NODE_LEGACY_POINT_ROTATE, "Point Rotate", NODE_CLASS_GEOMETRY); + node_type_init(&ntype, file_ns::node_init); + node_type_update(&ntype, file_ns::node_update); node_type_storage( &ntype, "NodeGeometryRotatePoints", node_free_standard_storage, node_copy_standard_storage); - ntype.declare = blender::nodes::geo_node_point_rotate_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_point_rotate_exec; - ntype.draw_buttons = blender::nodes::geo_node_point_rotate_layout; + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.draw_buttons = file_ns::node_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_scale.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_scale.cc index 99adce149e9..69e69a24e29 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_scale.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_scale.cc @@ -21,41 +21,40 @@ #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_legacy_point_scale_cc { -static void geo_node_point_scale_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::String>("Factor"); - b.add_input<decl::Vector>("Factor", "Factor_001") + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::String>(N_("Factor")); + b.add_input<decl::Vector>(N_("Factor"), "Factor_001") .default_value({1.0f, 1.0f, 1.0f}) .subtype(PROP_XYZ); - b.add_input<decl::Float>("Factor", "Factor_002").default_value(1.0f).min(0.0f); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Float>(N_("Factor"), "Factor_002").default_value(1.0f).min(0.0f); + b.add_output<decl::Geometry>(N_("Geometry")); } -static void geo_node_point_scale_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); uiItemR(layout, ptr, "input_type", 0, IFACE_("Type"), ICON_NONE); } -static void geo_node_point_scale_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeGeometryPointScale *data = (NodeGeometryPointScale *)MEM_callocN( - sizeof(NodeGeometryPointScale), __func__); + NodeGeometryPointScale *data = MEM_cnew<NodeGeometryPointScale>(__func__); data->input_type = GEO_NODE_ATTRIBUTE_INPUT_VECTOR; node->storage = data; } -static void geo_node_point_scale_update(bNodeTree *UNUSED(ntree), bNode *node) +static void node_update(bNodeTree *ntree, bNode *node) { NodeGeometryPointScale &node_storage = *(NodeGeometryPointScale *)node->storage; update_attribute_input_socket_availabilities( - *node, "Factor", (GeometryNodeAttributeInputMode)node_storage.input_type); + *ntree, *node, "Factor", (GeometryNodeAttributeInputMode)node_storage.input_type); } static void execute_on_component(GeoNodeExecParams params, GeometryComponent &component) @@ -78,7 +77,7 @@ static void execute_on_component(GeoNodeExecParams params, GeometryComponent &co const CustomDataType data_type = (input_type == GEO_NODE_ATTRIBUTE_INPUT_FLOAT) ? CD_PROP_FLOAT : CD_PROP_FLOAT3; - GVArrayPtr attribute = params.get_input_attribute( + GVArray attribute = params.get_input_attribute( "Factor", component, ATTR_DOMAIN_POINT, data_type, nullptr); if (!attribute) { return; @@ -86,13 +85,13 @@ static void execute_on_component(GeoNodeExecParams params, GeometryComponent &co MutableSpan<float3> scale_span = scale_attribute.as_span(); if (data_type == CD_PROP_FLOAT) { - GVArray_Typed<float> factors{*attribute}; + VArray<float> factors = attribute.typed<float>(); for (const int i : scale_span.index_range()) { scale_span[i] = scale_span[i] * factors[i]; } } else if (data_type == CD_PROP_FLOAT3) { - GVArray_Typed<float3> factors{*attribute}; + VArray<float3> factors = attribute.typed<float3>(); for (const int i : scale_span.index_range()) { scale_span[i] = scale_span[i] * factors[i]; } @@ -101,11 +100,11 @@ static void execute_on_component(GeoNodeExecParams params, GeometryComponent &co scale_attribute.save(); } -static void geo_node_point_scale_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - geometry_set = geometry_set_realize_instances(geometry_set); + geometry_set = geometry::realize_instances_legacy(geometry_set); if (geometry_set.has<MeshComponent>()) { execute_on_component(params, geometry_set.get_component_for_write<MeshComponent>()); @@ -120,20 +119,22 @@ static void geo_node_point_scale_exec(GeoNodeExecParams params) params.set_output("Geometry", std::move(geometry_set)); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_legacy_point_scale_cc void register_node_type_geo_point_scale() { + namespace file_ns = blender::nodes::node_geo_legacy_point_scale_cc; + static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_LEGACY_POINT_SCALE, "Point Scale", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base(&ntype, GEO_NODE_LEGACY_POINT_SCALE, "Point Scale", NODE_CLASS_GEOMETRY); - ntype.declare = blender::nodes::geo_node_point_scale_declare; - node_type_init(&ntype, blender::nodes::geo_node_point_scale_init); - node_type_update(&ntype, blender::nodes::geo_node_point_scale_update); + ntype.declare = file_ns::node_declare; + node_type_init(&ntype, file_ns::node_init); + node_type_update(&ntype, file_ns::node_update); node_type_storage( &ntype, "NodeGeometryPointScale", node_free_standard_storage, node_copy_standard_storage); - ntype.geometry_node_execute = blender::nodes::geo_node_point_scale_exec; - ntype.draw_buttons = blender::nodes::geo_node_point_scale_layout; + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.draw_buttons = file_ns::node_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_separate.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_separate.cc index 48b6676c1dd..b9760587706 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_separate.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_separate.cc @@ -25,14 +25,6 @@ namespace blender::nodes { -static void geo_node_point_instance_declare(NodeDeclarationBuilder &b) -{ - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::String>("Mask"); - b.add_output<decl::Geometry>("Geometry 1"); - b.add_output<decl::Geometry>("Geometry 2"); -} - template<typename T> static void copy_data_based_on_mask(Span<T> data, Span<bool> masks, @@ -55,7 +47,7 @@ void copy_point_attributes_based_on_mask(const GeometryComponent &in_component, { for (const AttributeIDRef &attribute_id : in_component.attribute_ids()) { ReadAttributeLookup attribute = in_component.attribute_try_get_for_read(attribute_id); - const CustomDataType data_type = bke::cpp_type_to_custom_data_type(attribute.varray->type()); + const CustomDataType data_type = bke::cpp_type_to_custom_data_type(attribute.varray.type()); /* Only copy point attributes. Theoretically this could interpolate attributes on other * domains to the point domain, but that would conflict with attributes that are built-in @@ -69,7 +61,7 @@ void copy_point_attributes_based_on_mask(const GeometryComponent &in_component, attribute_math::convert_to_static_type(data_type, [&](auto dummy) { using T = decltype(dummy); - GVArray_Span<T> span{*attribute.varray}; + VArray_Span span{attribute.varray.typed<T>()}; MutableSpan<T> out_span = result_attribute.as_span<T>(); copy_data_based_on_mask(span, masks, invert, out_span); }); @@ -78,6 +70,18 @@ void copy_point_attributes_based_on_mask(const GeometryComponent &in_component, } } +} // namespace blender::nodes + +namespace blender::nodes::node_geo_legacy_point_separate_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::String>(N_("Mask")); + b.add_output<decl::Geometry>(N_("Geometry 1")); + b.add_output<decl::Geometry>(N_("Geometry 2")); +} + static void create_component_points(GeometryComponent &component, const int total) { switch (component.type()) { @@ -103,7 +107,7 @@ static void separate_points_from_component(const GeometryComponent &in_component return; } - const GVArray_Typed<bool> mask_attribute = in_component.attribute_get_for_read<bool>( + const VArray<bool> mask_attribute = in_component.attribute_get_for_read<bool>( mask_name, ATTR_DOMAIN_POINT, false); VArray_Span<bool> masks{mask_attribute}; @@ -133,7 +137,7 @@ static GeometrySet separate_geometry_set(const GeometrySet &set_in, return set_out; } -static void geo_node_point_separate_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { bool wait_for_inputs = false; wait_for_inputs |= params.lazy_require_input("Geometry"); @@ -146,7 +150,7 @@ static void geo_node_point_separate_exec(GeoNodeExecParams params) /* TODO: This is not necessary-- the input geometry set can be read only, * but it must be rewritten to handle instance groups. */ - geometry_set = geometry_set_realize_instances(geometry_set); + geometry_set = geometry::realize_instances_legacy(geometry_set); if (params.lazy_output_is_required("Geometry 1")) { params.set_output("Geometry 1", @@ -158,16 +162,18 @@ static void geo_node_point_separate_exec(GeoNodeExecParams params) } } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_legacy_point_separate_cc void register_node_type_geo_point_separate() { + namespace file_ns = blender::nodes::node_geo_legacy_point_separate_cc; + static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_LEGACY_POINT_SEPARATE, "Point Separate", NODE_CLASS_GEOMETRY, 0); - ntype.declare = blender::nodes::geo_node_point_instance_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_point_separate_exec; + &ntype, GEO_NODE_LEGACY_POINT_SEPARATE, "Point Separate", NODE_CLASS_GEOMETRY); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; ntype.geometry_node_execute_supports_laziness = true; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_translate.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_translate.cc index f2fce45c57b..385c3d9f22d 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_point_translate.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_point_translate.cc @@ -19,17 +19,17 @@ #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_legacy_point_translate_cc { -static void geo_node_point_translate_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::String>("Translation"); - b.add_input<decl::Vector>("Translation", "Translation_001").subtype(PROP_TRANSLATION); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::String>(N_("Translation")); + b.add_input<decl::Vector>(N_("Translation"), "Translation_001").subtype(PROP_TRANSLATION); + b.add_output<decl::Geometry>(N_("Geometry")); } -static void geo_node_point_translate_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); @@ -43,21 +43,21 @@ static void execute_on_component(GeoNodeExecParams params, GeometryComponent &co if (!position_attribute) { return; } - GVArray_Typed<float3> attribute = params.get_input_attribute<float3>( + VArray<float3> attribute = params.get_input_attribute<float3>( "Translation", component, ATTR_DOMAIN_POINT, {0, 0, 0}); - for (const int i : IndexRange(attribute.size())) { + for (const int i : attribute.index_range()) { position_attribute->set(i, position_attribute->get(i) + attribute[i]); } position_attribute.save(); } -static void geo_node_point_translate_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - geometry_set = geometry_set_realize_instances(geometry_set); + geometry_set = geometry::realize_instances_legacy(geometry_set); if (geometry_set.has<MeshComponent>()) { execute_on_component(params, geometry_set.get_component_for_write<MeshComponent>()); @@ -72,39 +72,40 @@ static void geo_node_point_translate_exec(GeoNodeExecParams params) params.set_output("Geometry", std::move(geometry_set)); } -static void geo_node_point_translate_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeGeometryPointTranslate *data = (NodeGeometryPointTranslate *)MEM_callocN( - sizeof(NodeGeometryPointTranslate), __func__); + NodeGeometryPointTranslate *data = MEM_cnew<NodeGeometryPointTranslate>(__func__); data->input_type = GEO_NODE_ATTRIBUTE_INPUT_VECTOR; node->storage = data; } -static void geo_node_point_translate_update(bNodeTree *UNUSED(ntree), bNode *node) +static void node_update(bNodeTree *ntree, bNode *node) { NodeGeometryPointTranslate &node_storage = *(NodeGeometryPointTranslate *)node->storage; update_attribute_input_socket_availabilities( - *node, "Translation", (GeometryNodeAttributeInputMode)node_storage.input_type); + *ntree, *node, "Translation", (GeometryNodeAttributeInputMode)node_storage.input_type); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_legacy_point_translate_cc void register_node_type_geo_point_translate() { + namespace file_ns = blender::nodes::node_geo_legacy_point_translate_cc; + static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_LEGACY_POINT_TRANSLATE, "Point Translate", NODE_CLASS_GEOMETRY, 0); - node_type_init(&ntype, blender::nodes::geo_node_point_translate_init); - node_type_update(&ntype, blender::nodes::geo_node_point_translate_update); + &ntype, GEO_NODE_LEGACY_POINT_TRANSLATE, "Point Translate", NODE_CLASS_GEOMETRY); + node_type_init(&ntype, file_ns::node_init); + node_type_update(&ntype, file_ns::node_update); node_type_storage(&ntype, "NodeGeometryPointTranslate", node_free_standard_storage, node_copy_standard_storage); - ntype.declare = blender::nodes::geo_node_point_translate_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_point_translate_exec; - ntype.draw_buttons = blender::nodes::geo_node_point_translate_layout; + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.draw_buttons = file_ns::node_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_points_to_volume.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_points_to_volume.cc index d920c8de9f0..f54ffc53a6e 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_points_to_volume.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_points_to_volume.cc @@ -28,22 +28,20 @@ #include "UI_interface.h" #include "UI_resources.h" -namespace blender::nodes { +namespace blender::nodes::node_geo_legacy_points_to_volume_cc { -static void geo_node_points_to_volume_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Float>("Density").default_value(1.0f).min(0.0f); - b.add_input<decl::Float>("Voxel Size").default_value(0.3f).min(0.01f).subtype(PROP_DISTANCE); - b.add_input<decl::Float>("Voxel Amount").default_value(64.0f).min(0.0f); - b.add_input<decl::String>("Radius"); - b.add_input<decl::Float>("Radius", "Radius_001").default_value(0.5f).min(0.0f); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::Float>(N_("Density")).default_value(1.0f).min(0.0f); + b.add_input<decl::Float>(N_("Voxel Size")).default_value(0.3f).min(0.01f).subtype(PROP_DISTANCE); + b.add_input<decl::Float>(N_("Voxel Amount")).default_value(64.0f).min(0.0f); + b.add_input<decl::String>(N_("Radius")); + b.add_input<decl::Float>(N_("Radius"), "Radius_001").default_value(0.5f).min(0.0f); + b.add_output<decl::Geometry>(N_("Geometry")); } -static void geo_node_points_to_volume_layout(uiLayout *layout, - bContext *UNUSED(C), - PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); @@ -51,10 +49,9 @@ static void geo_node_points_to_volume_layout(uiLayout *layout, uiItemR(layout, ptr, "input_type_radius", 0, IFACE_("Radius"), ICON_NONE); } -static void geo_node_points_to_volume_init(bNodeTree *UNUSED(ntree), bNode *node) +static void node_init(bNodeTree *UNUSED(ntree), bNode *node) { - NodeGeometryPointsToVolume *data = (NodeGeometryPointsToVolume *)MEM_callocN( - sizeof(NodeGeometryPointsToVolume), __func__); + NodeGeometryPointsToVolume *data = MEM_cnew<NodeGeometryPointsToVolume>(__func__); data->resolution_mode = GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_AMOUNT; data->input_type_radius = GEO_NODE_ATTRIBUTE_INPUT_FLOAT; node->storage = data; @@ -65,19 +62,22 @@ static void geo_node_points_to_volume_init(bNodeTree *UNUSED(ntree), bNode *node STRNCPY(radius_attribute_socket_value->value, "radius"); } -static void geo_node_points_to_volume_update(bNodeTree *UNUSED(ntree), bNode *node) +static void node_update(bNodeTree *ntree, bNode *node) { NodeGeometryPointsToVolume *data = (NodeGeometryPointsToVolume *)node->storage; bNodeSocket *voxel_size_socket = nodeFindSocket(node, SOCK_IN, "Voxel Size"); bNodeSocket *voxel_amount_socket = nodeFindSocket(node, SOCK_IN, "Voxel Amount"); - nodeSetSocketAvailability(voxel_amount_socket, + nodeSetSocketAvailability(ntree, + voxel_amount_socket, data->resolution_mode == GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_AMOUNT); - nodeSetSocketAvailability( - voxel_size_socket, data->resolution_mode == GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_SIZE); + nodeSetSocketAvailability(ntree, + voxel_size_socket, + data->resolution_mode == + GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_SIZE); update_attribute_input_socket_availabilities( - *node, "Radius", (GeometryNodeAttributeInputMode)data->input_type_radius); + *ntree, *node, "Radius", (GeometryNodeAttributeInputMode)data->input_type_radius); } #ifdef WITH_OPENVDB @@ -161,7 +161,7 @@ static float compute_voxel_size(const GeoNodeExecParams ¶ms, } /* The voxel size adapts to the final size of the volume. */ - const float diagonal = float3::distance(min, max); + const float diagonal = math::distance(min, max); const float extended_diagonal = diagonal + 2.0f * radius; const float voxel_size = extended_diagonal / voxel_amount; return voxel_size; @@ -172,12 +172,12 @@ static void gather_point_data_from_component(const GeoNodeExecParams ¶ms, Vector<float3> &r_positions, Vector<float> &r_radii) { - GVArray_Typed<float3> positions = component.attribute_get_for_read<float3>( + VArray<float3> positions = component.attribute_get_for_read<float3>( "position", ATTR_DOMAIN_POINT, {0, 0, 0}); - GVArray_Typed<float> radii = params.get_input_attribute<float>( + VArray<float> radii = params.get_input_attribute<float>( "Radius", component, ATTR_DOMAIN_POINT, 0.0f); - for (const int i : IndexRange(positions.size())) { + for (const int i : positions.index_range()) { r_positions.append(positions[i]); r_radii.append(radii[i]); } @@ -241,13 +241,13 @@ static void initialize_volume_component_from_points(const GeometrySet &geometry_ } #endif -static void geo_node_points_to_volume_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set_in = params.extract_input<GeometrySet>("Geometry"); GeometrySet geometry_set_out; /* TODO: Read-only access to instances should be supported here, for now they are made real. */ - geometry_set_in = geometry_set_realize_instances(geometry_set_in); + geometry_set_in = geometry::realize_instances_legacy(geometry_set_in); #ifdef WITH_OPENVDB initialize_volume_component_from_points(geometry_set_in, geometry_set_out, params); @@ -256,23 +256,25 @@ static void geo_node_points_to_volume_exec(GeoNodeExecParams params) params.set_output("Geometry", std::move(geometry_set_out)); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_legacy_points_to_volume_cc -void register_node_type_geo_points_to_volume() +void register_node_type_geo_legacy_points_to_volume() { + namespace file_ns = blender::nodes::node_geo_legacy_points_to_volume_cc; + static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_LEGACY_POINTS_TO_VOLUME, "Points to Volume", NODE_CLASS_GEOMETRY, 0); + &ntype, GEO_NODE_LEGACY_POINTS_TO_VOLUME, "Points to Volume", NODE_CLASS_GEOMETRY); node_type_storage(&ntype, "NodeGeometryPointsToVolume", node_free_standard_storage, node_copy_standard_storage); node_type_size(&ntype, 170, 120, 700); - node_type_init(&ntype, blender::nodes::geo_node_points_to_volume_init); - node_type_update(&ntype, blender::nodes::geo_node_points_to_volume_update); - ntype.declare = blender::nodes::geo_node_points_to_volume_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_points_to_volume_exec; - ntype.draw_buttons = blender::nodes::geo_node_points_to_volume_layout; + node_type_init(&ntype, file_ns::node_init); + node_type_update(&ntype, file_ns::node_update); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.draw_buttons = file_ns::node_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_raycast.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_raycast.cc index 401a478f04c..cfae88e0625 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_raycast.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_raycast.cc @@ -24,30 +24,30 @@ #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_legacy_raycast_cc { -static void geo_node_raycast_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Geometry>("Target Geometry"); - b.add_input<decl::String>("Ray Direction"); - b.add_input<decl::Vector>("Ray Direction", "Ray Direction_001") + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::Geometry>(N_("Target Geometry")); + b.add_input<decl::String>(N_("Ray Direction")); + b.add_input<decl::Vector>(N_("Ray Direction"), "Ray Direction_001") .default_value({0.0f, 0.0f, 1.0f}); - b.add_input<decl::String>("Ray Length"); - b.add_input<decl::Float>("Ray Length", "Ray Length_001") + b.add_input<decl::String>(N_("Ray Length")); + b.add_input<decl::Float>(N_("Ray Length"), "Ray Length_001") .default_value(100.0f) .min(0.0f) .subtype(PROP_DISTANCE); - b.add_input<decl::String>("Target Attribute"); - b.add_input<decl::String>("Is Hit"); - b.add_input<decl::String>("Hit Position"); - b.add_input<decl::String>("Hit Normal"); - b.add_input<decl::String>("Hit Distance"); - b.add_input<decl::String>("Hit Attribute"); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::String>(N_("Target Attribute")); + b.add_input<decl::String>(N_("Is Hit")); + b.add_input<decl::String>(N_("Hit Position")); + b.add_input<decl::String>(N_("Hit Normal")); + b.add_input<decl::String>(N_("Hit Distance")); + b.add_input<decl::String>(N_("Hit Attribute")); + b.add_output<decl::Geometry>(N_("Geometry")); } -static void geo_node_raycast_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); @@ -56,24 +56,27 @@ static void geo_node_raycast_layout(uiLayout *layout, bContext *UNUSED(C), Point uiItemR(layout, ptr, "input_type_ray_length", 0, IFACE_("Ray Length"), ICON_NONE); } -static void geo_node_raycast_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeGeometryRaycast *data = (NodeGeometryRaycast *)MEM_callocN(sizeof(NodeGeometryRaycast), - __func__); + NodeGeometryRaycast *data = MEM_cnew<NodeGeometryRaycast>(__func__); data->input_type_ray_direction = GEO_NODE_ATTRIBUTE_INPUT_VECTOR; data->input_type_ray_length = GEO_NODE_ATTRIBUTE_INPUT_FLOAT; node->storage = data; } -static void geo_node_raycast_update(bNodeTree *UNUSED(ntree), bNode *node) +static void node_update(bNodeTree *ntree, bNode *node) { NodeGeometryRaycast *node_storage = (NodeGeometryRaycast *)node->storage; update_attribute_input_socket_availabilities( + *ntree, *node, "Ray Direction", (GeometryNodeAttributeInputMode)node_storage->input_type_ray_direction); update_attribute_input_socket_availabilities( - *node, "Ray Length", (GeometryNodeAttributeInputMode)node_storage->input_type_ray_length); + *ntree, + *node, + "Ray Length", + (GeometryNodeAttributeInputMode)node_storage->input_type_ray_length); } static void raycast_to_mesh(const Mesh &mesh, @@ -104,7 +107,7 @@ static void raycast_to_mesh(const Mesh &mesh, for (const int i : ray_origins.index_range()) { const float ray_length = ray_lengths[i]; const float3 ray_origin = ray_origins[i]; - const float3 ray_direction = ray_directions[i].normalized(); + const float3 ray_direction = math::normalize(ray_directions[i]); BVHTreeRayHit hit; hit.index = -1; @@ -197,11 +200,11 @@ static void raycast_from_points(const GeoNodeExecParams ¶ms, (GeometryNodeRaycastMapMode)storage.mapping); const AttributeDomain result_domain = ATTR_DOMAIN_POINT; - GVArray_Typed<float3> ray_origins = dst_component.attribute_get_for_read<float3>( + VArray<float3> ray_origins = dst_component.attribute_get_for_read<float3>( "position", result_domain, {0, 0, 0}); - GVArray_Typed<float3> ray_directions = params.get_input_attribute<float3>( + VArray<float3> ray_directions = params.get_input_attribute<float3>( "Ray Direction", dst_component, result_domain, {0, 0, 0}); - GVArray_Typed<float> ray_lengths = params.get_input_attribute<float>( + VArray<float> ray_lengths = params.get_input_attribute<float>( "Ray Length", dst_component, result_domain, 0); OutputAttribute_Typed<bool> hit_attribute = @@ -218,10 +221,10 @@ static void raycast_from_points(const GeoNodeExecParams ¶ms, Array<int> hit_indices; Array<float3> hit_positions_internal; if (!hit_attribute_names.is_empty()) { - hit_indices.reinitialize(ray_origins->size()); + hit_indices.reinitialize(ray_origins.size()); if (!hit_position_attribute) { - hit_positions_internal.reinitialize(ray_origins->size()); + hit_positions_internal.reinitialize(ray_origins.size()); } } const MutableSpan<bool> is_hit = hit_attribute ? hit_attribute.as_span() : MutableSpan<bool>(); @@ -250,7 +253,8 @@ static void raycast_from_points(const GeoNodeExecParams ¶ms, hit_distance_attribute.save(); /* Custom interpolated attributes */ - bke::mesh_surface_sample::MeshAttributeInterpolator interp(src_mesh, hit_positions, hit_indices); + bke::mesh_surface_sample::MeshAttributeInterpolator interp( + src_mesh, IndexMask(ray_origins.size()), hit_positions, hit_indices); for (const int i : hit_attribute_names.index_range()) { const std::optional<AttributeMetaData> meta_data = src_mesh_component->attribute_get_meta_data( hit_attribute_names[i]); @@ -267,7 +271,7 @@ static void raycast_from_points(const GeoNodeExecParams ¶ms, } } -static void geo_node_raycast_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); GeometrySet target_geometry_set = params.extract_input<GeometrySet>("Target Geometry"); @@ -280,8 +284,8 @@ static void geo_node_raycast_exec(GeoNodeExecParams params) const Array<std::string> hit_names = {params.extract_input<std::string>("Target Attribute")}; const Array<std::string> hit_output_names = {params.extract_input<std::string>("Hit Attribute")}; - geometry_set = bke::geometry_set_realize_instances(geometry_set); - target_geometry_set = bke::geometry_set_realize_instances(target_geometry_set); + geometry_set = geometry::realize_instances_legacy(geometry_set); + target_geometry_set = geometry::realize_instances_legacy(target_geometry_set); static const Array<GeometryComponentType> types = { GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE}; @@ -302,20 +306,22 @@ static void geo_node_raycast_exec(GeoNodeExecParams params) params.set_output("Geometry", geometry_set); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_legacy_raycast_cc -void register_node_type_geo_raycast() +void register_node_type_geo_legacy_raycast() { + namespace file_ns = blender::nodes::node_geo_legacy_raycast_cc; + static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_LEGACY_RAYCAST, "Raycast", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base(&ntype, GEO_NODE_LEGACY_RAYCAST, "Raycast", NODE_CLASS_GEOMETRY); node_type_size_preset(&ntype, NODE_SIZE_LARGE); - node_type_init(&ntype, blender::nodes::geo_node_raycast_init); - node_type_update(&ntype, blender::nodes::geo_node_raycast_update); + node_type_init(&ntype, file_ns::node_init); + node_type_update(&ntype, file_ns::node_update); node_type_storage( &ntype, "NodeGeometryRaycast", node_free_standard_storage, node_copy_standard_storage); - ntype.declare = blender::nodes::geo_node_raycast_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_raycast_exec; - ntype.draw_buttons = blender::nodes::geo_node_raycast_layout; + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.draw_buttons = file_ns::node_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_select_by_material.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_select_by_material.cc index eabdd2bcd5a..59ac697b658 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_select_by_material.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_select_by_material.cc @@ -26,14 +26,14 @@ #include "BKE_material.h" -namespace blender::nodes { +namespace blender::nodes::node_geo_legacy_select_by_material_cc { -static void geo_node_legacy_select_by_material_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Material>("Material").hide_label(); - b.add_input<decl::String>("Selection"); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::Material>(N_("Material")).hide_label(); + b.add_input<decl::String>(N_("Selection")); + b.add_output<decl::Geometry>(N_("Geometry")); } static void select_mesh_by_material(const Mesh &mesh, @@ -54,13 +54,13 @@ static void select_mesh_by_material(const Mesh &mesh, }); } -static void geo_node_legacy_select_by_material_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { Material *material = params.extract_input<Material *>("Material"); const std::string selection_name = params.extract_input<std::string>("Selection"); GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - geometry_set = geometry_set_realize_instances(geometry_set); + geometry_set = geometry::realize_instances_legacy(geometry_set); if (geometry_set.has<MeshComponent>()) { MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>(); @@ -78,15 +78,17 @@ static void geo_node_legacy_select_by_material_exec(GeoNodeExecParams params) params.set_output("Geometry", std::move(geometry_set)); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_legacy_select_by_material_cc void register_node_type_geo_legacy_select_by_material() { + namespace file_ns = blender::nodes::node_geo_legacy_select_by_material_cc; + static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_LEGACY_SELECT_BY_MATERIAL, "Select by Material", NODE_CLASS_GEOMETRY, 0); - ntype.declare = blender::nodes::geo_node_legacy_select_by_material_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_legacy_select_by_material_exec; + &ntype, GEO_NODE_LEGACY_SELECT_BY_MATERIAL, "Select by Material", NODE_CLASS_GEOMETRY); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_subdivision_surface.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_subdivision_surface.cc index 07d3f89bdb7..7c5553cb5e4 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_subdivision_surface.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_subdivision_surface.cc @@ -23,19 +23,17 @@ #include "UI_resources.h" #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_legacy_subdivision_surface_cc { -static void geo_node_subdivision_surface_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Int>("Level").default_value(1).min(0).max(6); - b.add_input<decl::Bool>("Use Creases"); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::Int>(N_("Level")).default_value(1).min(0).max(6); + b.add_input<decl::Bool>(N_("Use Creases")); + b.add_output<decl::Geometry>(N_("Geometry")); } -static void geo_node_subdivision_surface_layout(uiLayout *layout, - bContext *UNUSED(C), - PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { #ifdef WITH_OPENSUBDIV uiLayoutSetPropSep(layout, true); @@ -47,20 +45,19 @@ static void geo_node_subdivision_surface_layout(uiLayout *layout, #endif } -static void geo_node_subdivision_surface_init(bNodeTree *UNUSED(ntree), bNode *node) +static void node_init(bNodeTree *UNUSED(ntree), bNode *node) { - NodeGeometrySubdivisionSurface *data = (NodeGeometrySubdivisionSurface *)MEM_callocN( - sizeof(NodeGeometrySubdivisionSurface), __func__); + NodeGeometrySubdivisionSurface *data = MEM_cnew<NodeGeometrySubdivisionSurface>(__func__); data->uv_smooth = SUBSURF_UV_SMOOTH_PRESERVE_BOUNDARIES; data->boundary_smooth = SUBSURF_BOUNDARY_SMOOTH_ALL; node->storage = data; } -static void geo_node_subdivision_surface_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - geometry_set = geometry_set_realize_instances(geometry_set); + geometry_set = geometry::realize_instances_legacy(geometry_set); if (!geometry_set.has_mesh()) { params.set_output("Geometry", geometry_set); @@ -126,18 +123,20 @@ static void geo_node_subdivision_surface_exec(GeoNodeExecParams params) params.set_output("Geometry", std::move(geometry_set)); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_legacy_subdivision_surface_cc -void register_node_type_geo_subdivision_surface() +void register_node_type_geo_legacy_subdivision_surface() { + namespace file_ns = blender::nodes::node_geo_legacy_subdivision_surface_cc; + static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_LEGACY_SUBDIVISION_SURFACE, "Subdivision Surface", NODE_CLASS_GEOMETRY, 0); - ntype.declare = blender::nodes::geo_node_subdivision_surface_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_subdivision_surface_exec; - ntype.draw_buttons = blender::nodes::geo_node_subdivision_surface_layout; - node_type_init(&ntype, blender::nodes::geo_node_subdivision_surface_init); + &ntype, GEO_NODE_LEGACY_SUBDIVISION_SURFACE, "Subdivision Surface", NODE_CLASS_GEOMETRY); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.draw_buttons = file_ns::node_layout; + node_type_init(&ntype, file_ns::node_init); node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); node_type_storage(&ntype, "NodeGeometrySubdivisionSurface", diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_volume_to_mesh.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_volume_to_mesh.cc new file mode 100644 index 00000000000..42fbc49ed4b --- /dev/null +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_volume_to_mesh.cc @@ -0,0 +1,176 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "DEG_depsgraph_query.h" +#ifdef WITH_OPENVDB +# include <openvdb/tools/GridTransformer.h> +# include <openvdb/tools/VolumeToMesh.h> +#endif + +#include "node_geometry_util.hh" + +#include "BKE_lib_id.h" +#include "BKE_material.h" +#include "BKE_mesh.h" +#include "BKE_mesh_runtime.h" +#include "BKE_volume.h" +#include "BKE_volume_to_mesh.hh" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes::node_geo_legacy_volume_to_mesh_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::String>(N_("Density")); + b.add_input<decl::Float>(N_("Voxel Size")).default_value(0.3f).min(0.01f).subtype(PROP_DISTANCE); + b.add_input<decl::Float>(N_("Voxel Amount")).default_value(64.0f).min(0.0f); + b.add_input<decl::Float>(N_("Threshold")).default_value(0.1f).min(0.0f); + b.add_input<decl::Float>(N_("Adaptivity")).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_output<decl::Geometry>(N_("Geometry")); +} + +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + uiItemR(layout, ptr, "resolution_mode", 0, IFACE_("Resolution"), ICON_NONE); +} + +static void node_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryVolumeToMesh *data = MEM_cnew<NodeGeometryVolumeToMesh>(__func__); + data->resolution_mode = VOLUME_TO_MESH_RESOLUTION_MODE_GRID; + + bNodeSocket *grid_socket = nodeFindSocket(node, SOCK_IN, "Density"); + bNodeSocketValueString *grid_socket_value = (bNodeSocketValueString *)grid_socket->default_value; + STRNCPY(grid_socket_value->value, "density"); + + node->storage = data; +} + +static void node_update(bNodeTree *ntree, bNode *node) +{ + NodeGeometryVolumeToMesh *data = (NodeGeometryVolumeToMesh *)node->storage; + + bNodeSocket *voxel_size_socket = nodeFindSocket(node, SOCK_IN, "Voxel Size"); + bNodeSocket *voxel_amount_socket = nodeFindSocket(node, SOCK_IN, "Voxel Amount"); + nodeSetSocketAvailability(ntree, + voxel_amount_socket, + data->resolution_mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_AMOUNT); + nodeSetSocketAvailability(ntree, + voxel_size_socket, + data->resolution_mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_SIZE); +} + +#ifdef WITH_OPENVDB + +static void create_mesh_from_volume(GeometrySet &geometry_set_in, + GeometrySet &geometry_set_out, + GeoNodeExecParams ¶ms) +{ + if (!geometry_set_in.has<VolumeComponent>()) { + return; + } + + const NodeGeometryVolumeToMesh &storage = + *(const NodeGeometryVolumeToMesh *)params.node().storage; + + bke::VolumeToMeshResolution resolution; + resolution.mode = (VolumeToMeshResolutionMode)storage.resolution_mode; + if (resolution.mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_AMOUNT) { + resolution.settings.voxel_amount = params.get_input<float>("Voxel Amount"); + if (resolution.settings.voxel_amount <= 0.0f) { + return; + } + } + else if (resolution.mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_SIZE) { + resolution.settings.voxel_size = params.get_input<float>("Voxel Size"); + if (resolution.settings.voxel_size <= 0.0f) { + return; + } + } + + const VolumeComponent *component = geometry_set_in.get_component_for_read<VolumeComponent>(); + const Volume *volume = component->get_for_read(); + if (volume == nullptr) { + return; + } + + const Main *bmain = DEG_get_bmain(params.depsgraph()); + BKE_volume_load(volume, bmain); + + const std::string grid_name = params.get_input<std::string>("Density"); + const VolumeGrid *volume_grid = BKE_volume_grid_find_for_read(volume, grid_name.c_str()); + if (volume_grid == nullptr) { + return; + } + + float threshold = params.get_input<float>("Threshold"); + float adaptivity = params.get_input<float>("Adaptivity"); + + const openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid); + Mesh *mesh = bke::volume_to_mesh(*grid, resolution, threshold, adaptivity); + if (mesh == nullptr) { + return; + } + BKE_id_material_eval_ensure_default_slot(&mesh->id); + MeshComponent &dst_component = geometry_set_out.get_component_for_write<MeshComponent>(); + dst_component.replace(mesh); +} + +#endif /* WITH_OPENVDB */ + +static void node_geo_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set_in = params.extract_input<GeometrySet>("Geometry"); + GeometrySet geometry_set_out; + +#ifdef WITH_OPENVDB + create_mesh_from_volume(geometry_set_in, geometry_set_out, params); +#else + params.error_message_add(NodeWarningType::Error, + TIP_("Disabled, Blender was compiled without OpenVDB")); +#endif + + params.set_output("Geometry", geometry_set_out); +} + +} // namespace blender::nodes::node_geo_legacy_volume_to_mesh_cc + +void register_node_type_geo_legacy_volume_to_mesh() +{ + namespace file_ns = blender::nodes::node_geo_legacy_volume_to_mesh_cc; + + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_LEGACY_VOLUME_TO_MESH, "Volume to Mesh", NODE_CLASS_GEOMETRY); + ntype.declare = file_ns::node_declare; + node_type_storage( + &ntype, "NodeGeometryVolumeToMesh", node_free_standard_storage, node_copy_standard_storage); + node_type_size(&ntype, 170, 120, 700); + node_type_init(&ntype, file_ns::node_init); + node_type_update(&ntype, file_ns::node_update); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.draw_buttons = file_ns::node_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_mesh_to_curve.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_mesh_to_curve.cc deleted file mode 100644 index 11349dc7d42..00000000000 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_mesh_to_curve.cc +++ /dev/null @@ -1,314 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include "BLI_array.hh" -#include "BLI_task.hh" - -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" - -#include "BKE_attribute_math.hh" -#include "BKE_spline.hh" - -#include "node_geometry_util.hh" - -using blender::Array; - -namespace blender::nodes { - -static void geo_node_mesh_to_curve_declare(NodeDeclarationBuilder &b) -{ - b.add_input<decl::Geometry>("Mesh"); - b.add_input<decl::String>("Selection"); - b.add_output<decl::Geometry>("Curve"); -} - -template<typename T> -static void copy_attribute_to_points(const VArray<T> &source_data, - Span<int> map, - MutableSpan<T> dest_data) -{ - for (const int point_index : map.index_range()) { - const int vert_index = map[point_index]; - dest_data[point_index] = source_data[vert_index]; - } -} - -static void copy_attributes_to_points(CurveEval &curve, - const MeshComponent &mesh_component, - Span<Vector<int>> point_to_vert_maps) -{ - MutableSpan<SplinePtr> splines = curve.splines(); - Set<AttributeIDRef> source_attribute_ids = mesh_component.attribute_ids(); - - /* Copy builtin control point attributes. */ - if (source_attribute_ids.contains("tilt")) { - const GVArray_Typed<float> tilt_attribute = mesh_component.attribute_get_for_read<float>( - "tilt", ATTR_DOMAIN_POINT, 0.0f); - threading::parallel_for(splines.index_range(), 256, [&](IndexRange range) { - for (const int i : range) { - copy_attribute_to_points<float>( - *tilt_attribute, point_to_vert_maps[i], splines[i]->tilts()); - } - }); - source_attribute_ids.remove_contained("tilt"); - } - if (source_attribute_ids.contains("radius")) { - const GVArray_Typed<float> radius_attribute = mesh_component.attribute_get_for_read<float>( - "radius", ATTR_DOMAIN_POINT, 1.0f); - threading::parallel_for(splines.index_range(), 256, [&](IndexRange range) { - for (const int i : range) { - copy_attribute_to_points<float>( - *radius_attribute, point_to_vert_maps[i], splines[i]->radii()); - } - }); - source_attribute_ids.remove_contained("radius"); - } - - /* Don't copy other builtin control point attributes. */ - source_attribute_ids.remove("position"); - - /* Copy dynamic control point attributes. */ - for (const AttributeIDRef &attribute_id : source_attribute_ids) { - const GVArrayPtr mesh_attribute = mesh_component.attribute_try_get_for_read(attribute_id, - ATTR_DOMAIN_POINT); - /* Some attributes might not exist if they were builtin attribute on domains that don't - * have any elements, i.e. a face attribute on the output of the line primitive node. */ - if (!mesh_attribute) { - continue; - } - - const CustomDataType data_type = bke::cpp_type_to_custom_data_type(mesh_attribute->type()); - - threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) { - for (const int i : range) { - /* Create attribute on the spline points. */ - splines[i]->attributes.create(attribute_id, data_type); - std::optional<GMutableSpan> spline_attribute = splines[i]->attributes.get_for_write( - attribute_id); - BLI_assert(spline_attribute); - - /* Copy attribute based on the map for this spline. */ - attribute_math::convert_to_static_type(mesh_attribute->type(), [&](auto dummy) { - using T = decltype(dummy); - copy_attribute_to_points<T>( - mesh_attribute->typed<T>(), point_to_vert_maps[i], spline_attribute->typed<T>()); - }); - } - }); - } - - curve.assert_valid_point_attributes(); -} - -struct CurveFromEdgesOutput { - std::unique_ptr<CurveEval> curve; - Vector<Vector<int>> point_to_vert_maps; -}; - -static CurveFromEdgesOutput mesh_to_curve(Span<MVert> verts, Span<std::pair<int, int>> edges) -{ - std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>(); - Vector<Vector<int>> point_to_vert_maps; - - /* Compute the number of edges connecting to each vertex. */ - Array<int> neighbor_count(verts.size(), 0); - for (const std::pair<int, int> &edge : edges) { - neighbor_count[edge.first]++; - neighbor_count[edge.second]++; - } - - /* Compute an offset into the array of neighbor edges based on the counts. */ - Array<int> neighbor_offsets(verts.size()); - int start = 0; - for (const int i : verts.index_range()) { - neighbor_offsets[i] = start; - start += neighbor_count[i]; - } - - /* Use as an index into the "neighbor group" for each vertex. */ - Array<int> used_slots(verts.size(), 0); - /* Calculate the indices of each vertex's neighboring edges. */ - Array<int> neighbors(edges.size() * 2); - for (const int i : edges.index_range()) { - const int v1 = edges[i].first; - const int v2 = edges[i].second; - neighbors[neighbor_offsets[v1] + used_slots[v1]] = v2; - neighbors[neighbor_offsets[v2] + used_slots[v2]] = v1; - used_slots[v1]++; - used_slots[v2]++; - } - - /* Now use the neighbor group offsets calculated above as a count used edges at each vertex. */ - Array<int> unused_edges = std::move(used_slots); - - for (const int start_vert : verts.index_range()) { - /* The vertex will be part of a cyclic spline. */ - if (neighbor_count[start_vert] == 2) { - continue; - } - - /* The vertex has no connected edges, or they were already used. */ - if (unused_edges[start_vert] == 0) { - continue; - } - - for (const int i : IndexRange(neighbor_count[start_vert])) { - int current_vert = start_vert; - int next_vert = neighbors[neighbor_offsets[current_vert] + i]; - - if (unused_edges[next_vert] == 0) { - continue; - } - - std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); - Vector<int> point_to_vert_map; - - spline->add_point(verts[current_vert].co, 1.0f, 0.0f); - point_to_vert_map.append(current_vert); - - /* Follow connected edges until we read a vertex with more than two connected edges. */ - while (true) { - int last_vert = current_vert; - current_vert = next_vert; - - spline->add_point(verts[current_vert].co, 1.0f, 0.0f); - point_to_vert_map.append(current_vert); - unused_edges[current_vert]--; - unused_edges[last_vert]--; - - if (neighbor_count[current_vert] != 2) { - break; - } - - const int offset = neighbor_offsets[current_vert]; - const int next_a = neighbors[offset]; - const int next_b = neighbors[offset + 1]; - next_vert = (last_vert == next_a) ? next_b : next_a; - } - - spline->attributes.reallocate(spline->size()); - curve->add_spline(std::move(spline)); - point_to_vert_maps.append(std::move(point_to_vert_map)); - } - } - - /* All remaining edges are part of cyclic splines (we skipped vertices with two edges before). */ - for (const int start_vert : verts.index_range()) { - if (unused_edges[start_vert] != 2) { - continue; - } - - int current_vert = start_vert; - int next_vert = neighbors[neighbor_offsets[current_vert]]; - - std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); - Vector<int> point_to_vert_map; - spline->set_cyclic(true); - - spline->add_point(verts[current_vert].co, 1.0f, 0.0f); - point_to_vert_map.append(current_vert); - - /* Follow connected edges until we loop back to the start vertex. */ - while (next_vert != start_vert) { - const int last_vert = current_vert; - current_vert = next_vert; - - spline->add_point(verts[current_vert].co, 1.0f, 0.0f); - point_to_vert_map.append(current_vert); - unused_edges[current_vert]--; - unused_edges[last_vert]--; - - const int offset = neighbor_offsets[current_vert]; - const int next_a = neighbors[offset]; - const int next_b = neighbors[offset + 1]; - next_vert = (last_vert == next_a) ? next_b : next_a; - } - - spline->attributes.reallocate(spline->size()); - curve->add_spline(std::move(spline)); - point_to_vert_maps.append(std::move(point_to_vert_map)); - } - - curve->attributes.reallocate(curve->splines().size()); - return {std::move(curve), std::move(point_to_vert_maps)}; -} - -/** - * Get a separate array of the indices for edges in a selection (a boolean attribute). - * This helps to make the above algorithm simpler by removing the need to check for selection - * in many places. - */ -static Vector<std::pair<int, int>> get_selected_edges(GeoNodeExecParams params, - const MeshComponent &component) -{ - const Mesh &mesh = *component.get_for_read(); - const std::string selection_name = params.extract_input<std::string>("Selection"); - if (!selection_name.empty() && !component.attribute_exists(selection_name)) { - params.error_message_add(NodeWarningType::Error, - TIP_("No attribute with name \"") + selection_name + "\""); - } - GVArray_Typed<bool> selection = component.attribute_get_for_read<bool>( - selection_name, ATTR_DOMAIN_EDGE, true); - - Vector<std::pair<int, int>> selected_edges; - for (const int i : IndexRange(mesh.totedge)) { - if (selection[i]) { - selected_edges.append({mesh.medge[i].v1, mesh.medge[i].v2}); - } - } - - return selected_edges; -} - -static void geo_node_mesh_to_curve_exec(GeoNodeExecParams params) -{ - GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh"); - - geometry_set = bke::geometry_set_realize_instances(geometry_set); - - if (!geometry_set.has_mesh()) { - params.set_output("Curve", GeometrySet()); - return; - } - - const MeshComponent &component = *geometry_set.get_component_for_read<MeshComponent>(); - const Mesh &mesh = *component.get_for_read(); - Span<MVert> verts = Span{mesh.mvert, mesh.totvert}; - Vector<std::pair<int, int>> selected_edges = get_selected_edges(params, component); - if (selected_edges.size() == 0) { - params.set_output("Curve", GeometrySet()); - return; - } - - CurveFromEdgesOutput output = mesh_to_curve(verts, selected_edges); - copy_attributes_to_points(*output.curve, component, output.point_to_vert_maps); - - params.set_output("Curve", GeometrySet::create_with_curve(output.curve.release())); -} - -} // namespace blender::nodes - -void register_node_type_geo_mesh_to_curve() -{ - static bNodeType ntype; - - geo_node_type_base( - &ntype, GEO_NODE_LEGACY_MESH_TO_CURVE, "Mesh to Curve", NODE_CLASS_GEOMETRY, 0); - ntype.declare = blender::nodes::geo_node_mesh_to_curve_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_mesh_to_curve_exec; - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc b/source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc new file mode 100644 index 00000000000..6c2e72cf14f --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc @@ -0,0 +1,430 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BKE_attribute_math.hh" + +#include "NOD_socket_search_link.hh" + +#include "node_geometry_util.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes::node_geo_accumulate_field_cc { + +NODE_STORAGE_FUNCS(NodeAccumulateField) + +static void node_declare(NodeDeclarationBuilder &b) +{ + std::string value_in_description = "The values to be accumulated"; + std::string leading_out_description = + "The running total of values in the corresponding group, starting at the first value"; + std::string trailing_out_description = + "The running total of values in the corresponding group, starting at zero"; + std::string total_out_description = "The total of all of the values in the corresponding group"; + + b.add_input<decl::Vector>(N_("Value"), "Value Vector") + .default_value({1.0f, 1.0f, 1.0f}) + .supports_field() + .description(N_(value_in_description)); + b.add_input<decl::Float>(N_("Value"), "Value Float") + .default_value(1.0f) + .supports_field() + .description(N_(value_in_description)); + b.add_input<decl::Int>(N_("Value"), "Value Int") + .default_value(1) + .supports_field() + .description(N_(value_in_description)); + b.add_input<decl::Int>(N_("Group Index")) + .supports_field() + .description( + N_("An index used to group values together for multiple separate accumulations")); + + b.add_output<decl::Vector>(N_("Leading"), "Leading Vector") + .field_source() + .description(N_(leading_out_description)); + b.add_output<decl::Float>(N_("Leading"), "Leading Float") + .field_source() + .description(N_(leading_out_description)); + b.add_output<decl::Int>(N_("Leading"), "Leading Int") + .field_source() + .description(N_(leading_out_description)); + + b.add_output<decl::Vector>(N_("Trailing"), "Trailing Vector") + .field_source() + .description(N_(trailing_out_description)); + b.add_output<decl::Float>(N_("Trailing"), "Trailing Float") + .field_source() + .description(N_(trailing_out_description)); + b.add_output<decl::Int>(N_("Trailing"), "Trailing Int") + .field_source() + .description(N_(trailing_out_description)); + + b.add_output<decl::Vector>(N_("Total"), "Total Vector") + .field_source() + .description(N_(total_out_description)); + b.add_output<decl::Float>(N_("Total"), "Total Float") + .field_source() + .description(N_(total_out_description)); + b.add_output<decl::Int>(N_("Total"), "Total Int") + .field_source() + .description(N_(total_out_description)); +} + +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); + uiItemR(layout, ptr, "domain", 0, "", ICON_NONE); +} + +static void node_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeAccumulateField *data = MEM_cnew<NodeAccumulateField>(__func__); + data->data_type = CD_PROP_FLOAT; + data->domain = ATTR_DOMAIN_POINT; + node->storage = data; +} + +static void node_update(bNodeTree *ntree, bNode *node) +{ + const NodeAccumulateField &storage = node_storage(*node); + const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type); + + bNodeSocket *sock_in_vector = (bNodeSocket *)node->inputs.first; + bNodeSocket *sock_in_float = sock_in_vector->next; + bNodeSocket *sock_in_int = sock_in_float->next; + + bNodeSocket *sock_out_vector = (bNodeSocket *)node->outputs.first; + bNodeSocket *sock_out_float = sock_out_vector->next; + bNodeSocket *sock_out_int = sock_out_float->next; + + bNodeSocket *sock_out_first_vector = sock_out_int->next; + bNodeSocket *sock_out_first_float = sock_out_first_vector->next; + bNodeSocket *sock_out_first_int = sock_out_first_float->next; + bNodeSocket *sock_out_total_vector = sock_out_first_int->next; + bNodeSocket *sock_out_total_float = sock_out_total_vector->next; + bNodeSocket *sock_out_total_int = sock_out_total_float->next; + + nodeSetSocketAvailability(ntree, sock_in_vector, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(ntree, sock_in_float, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(ntree, sock_in_int, data_type == CD_PROP_INT32); + + nodeSetSocketAvailability(ntree, sock_out_vector, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(ntree, sock_out_float, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(ntree, sock_out_int, data_type == CD_PROP_INT32); + + nodeSetSocketAvailability(ntree, sock_out_first_vector, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(ntree, sock_out_first_float, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(ntree, sock_out_first_int, data_type == CD_PROP_INT32); + + nodeSetSocketAvailability(ntree, sock_out_total_vector, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(ntree, sock_out_total_float, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(ntree, sock_out_total_int, data_type == CD_PROP_INT32); +} + +enum class AccumulationMode { Leading = 0, Trailing = 1 }; + +static std::optional<CustomDataType> node_type_from_other_socket(const bNodeSocket &socket) +{ + switch (socket.type) { + case SOCK_FLOAT: + return CD_PROP_FLOAT; + case SOCK_BOOLEAN: + case SOCK_INT: + return CD_PROP_INT32; + case SOCK_VECTOR: + case SOCK_RGBA: + return CD_PROP_FLOAT3; + default: + return {}; + } +} + +static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) +{ + const std::optional<CustomDataType> type = node_type_from_other_socket(params.other_socket()); + if (!type) { + return; + } + if (params.in_out() == SOCK_OUT) { + params.add_item( + IFACE_("Leading"), + [type](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node("GeometryNodeAccumulateField"); + node_storage(node).data_type = *type; + params.update_and_connect_available_socket(node, "Leading"); + }, + 0); + params.add_item( + IFACE_("Trailing"), + [type](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node("GeometryNodeAccumulateField"); + node_storage(node).data_type = *type; + params.update_and_connect_available_socket(node, "Trailing"); + }, + -1); + params.add_item( + IFACE_("Total"), + [type](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node("GeometryNodeAccumulateField"); + node_storage(node).data_type = *type; + params.update_and_connect_available_socket(node, "Total"); + }, + -2); + } + else { + params.add_item( + IFACE_("Value"), + [type](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node("GeometryNodeAccumulateField"); + node_storage(node).data_type = *type; + params.update_and_connect_available_socket(node, "Value"); + }, + 0); + + params.add_item( + IFACE_("Group Index"), + [type](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node("GeometryNodeAccumulateField"); + node_storage(node).data_type = *type; + params.update_and_connect_available_socket(node, "Group Index"); + }, + -1); + } +} + +template<typename T> class AccumulateFieldInput final : public GeometryFieldInput { + private: + Field<T> input_; + Field<int> group_index_; + AttributeDomain source_domain_; + AccumulationMode accumulation_mode_; + + public: + AccumulateFieldInput(const AttributeDomain source_domain, + Field<T> input, + Field<int> group_index, + AccumulationMode accumulation_mode) + : GeometryFieldInput(CPPType::get<T>(), "Accumulation"), + input_(input), + group_index_(group_index), + source_domain_(source_domain), + accumulation_mode_(accumulation_mode) + { + } + + GVArray get_varray_for_context(const GeometryComponent &component, + const AttributeDomain domain, + IndexMask UNUSED(mask)) const final + { + const GeometryComponentFieldContext field_context{component, source_domain_}; + const int domain_size = component.attribute_domain_size(field_context.domain()); + + fn::FieldEvaluator evaluator{field_context, domain_size}; + evaluator.add(input_); + evaluator.add(group_index_); + evaluator.evaluate(); + const VArray<T> &values = evaluator.get_evaluated<T>(0); + const VArray<int> &group_indices = evaluator.get_evaluated<int>(1); + + Array<T> accumulations_out(domain_size); + + if (group_indices.is_single()) { + T accumulation = T(); + if (accumulation_mode_ == AccumulationMode::Leading) { + for (const int i : values.index_range()) { + accumulation = values[i] + accumulation; + accumulations_out[i] = accumulation; + } + } + else { + for (const int i : values.index_range()) { + accumulations_out[i] = accumulation; + accumulation = values[i] + accumulation; + } + } + } + else { + Map<int, T> accumulations; + if (accumulation_mode_ == AccumulationMode::Leading) { + for (const int i : values.index_range()) { + T &accumulation_value = accumulations.lookup_or_add_default(group_indices[i]); + accumulation_value += values[i]; + accumulations_out[i] = accumulation_value; + } + } + else { + for (const int i : values.index_range()) { + T &accumulation_value = accumulations.lookup_or_add_default(group_indices[i]); + accumulations_out[i] = accumulation_value; + accumulation_value += values[i]; + } + } + } + + return component.attribute_try_adapt_domain<T>( + VArray<T>::ForContainer(std::move(accumulations_out)), source_domain_, domain); + } + + uint64_t hash() const override + { + return get_default_hash_4(input_, group_index_, source_domain_, accumulation_mode_); + } + + bool is_equal_to(const fn::FieldNode &other) const override + { + if (const AccumulateFieldInput *other_accumulate = dynamic_cast<const AccumulateFieldInput *>( + &other)) { + return input_ == other_accumulate->input_ && + group_index_ == other_accumulate->group_index_ && + source_domain_ == other_accumulate->source_domain_ && + accumulation_mode_ == other_accumulate->accumulation_mode_; + } + return false; + } +}; + +template<typename T> class TotalFieldInput final : public GeometryFieldInput { + private: + Field<T> input_; + Field<int> group_index_; + AttributeDomain source_domain_; + + public: + TotalFieldInput(const AttributeDomain source_domain, Field<T> input, Field<int> group_index) + : GeometryFieldInput(CPPType::get<T>(), "Total Value"), + input_(input), + group_index_(group_index), + source_domain_(source_domain) + { + } + + GVArray get_varray_for_context(const GeometryComponent &component, + const AttributeDomain domain, + IndexMask UNUSED(mask)) const final + { + const GeometryComponentFieldContext field_context{component, source_domain_}; + const int domain_size = component.attribute_domain_size(field_context.domain()); + + fn::FieldEvaluator evaluator{field_context, domain_size}; + evaluator.add(input_); + evaluator.add(group_index_); + evaluator.evaluate(); + const VArray<T> &values = evaluator.get_evaluated<T>(0); + const VArray<int> &group_indices = evaluator.get_evaluated<int>(1); + + if (group_indices.is_single()) { + T accumulation = T(); + for (const int i : values.index_range()) { + accumulation = values[i] + accumulation; + } + return VArray<T>::ForSingle(accumulation, domain_size); + } + + Array<T> accumulations_out(domain_size); + Map<int, T> accumulations; + for (const int i : values.index_range()) { + T &value = accumulations.lookup_or_add_default(group_indices[i]); + value = value + values[i]; + } + for (const int i : values.index_range()) { + accumulations_out[i] = accumulations.lookup(group_indices[i]); + } + + return component.attribute_try_adapt_domain<T>( + VArray<T>::ForContainer(std::move(accumulations_out)), source_domain_, domain); + } + + uint64_t hash() const override + { + return get_default_hash_3(input_, group_index_, source_domain_); + } + + bool is_equal_to(const fn::FieldNode &other) const override + { + if (const TotalFieldInput *other_field = dynamic_cast<const TotalFieldInput *>(&other)) { + return input_ == other_field->input_ && group_index_ == other_field->group_index_ && + source_domain_ == other_field->source_domain_; + } + return false; + } +}; + +template<typename T> std::string identifier_suffix() +{ + if constexpr (std::is_same_v<T, int>) { + return "Int"; + } + if constexpr (std::is_same_v<T, float>) { + return "Float"; + } + if constexpr (std::is_same_v<T, float3>) { + return "Vector"; + } +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + const NodeAccumulateField &storage = node_storage(params.node()); + const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type); + const AttributeDomain source_domain = static_cast<AttributeDomain>(storage.domain); + + Field<int> group_index_field = params.extract_input<Field<int>>("Group Index"); + attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + using T = decltype(dummy); + if constexpr (std::is_same_v<T, int> || std::is_same_v<T, float> || + std::is_same_v<T, float3>) { + const std::string suffix = " " + identifier_suffix<T>(); + Field<T> input_field = params.extract_input<Field<T>>("Value" + suffix); + if (params.output_is_required("Leading" + suffix)) { + params.set_output( + "Leading" + suffix, + Field<T>{std::make_shared<AccumulateFieldInput<T>>( + source_domain, input_field, group_index_field, AccumulationMode::Leading)}); + } + if (params.output_is_required("Trailing" + suffix)) { + params.set_output( + "Trailing" + suffix, + Field<T>{std::make_shared<AccumulateFieldInput<T>>( + source_domain, input_field, group_index_field, AccumulationMode::Trailing)}); + } + if (params.output_is_required("Total" + suffix)) { + params.set_output("Total" + suffix, + Field<T>{std::make_shared<TotalFieldInput<T>>( + source_domain, input_field, group_index_field)}); + } + } + }); +} +} // namespace blender::nodes::node_geo_accumulate_field_cc + +void register_node_type_geo_accumulate_field() +{ + namespace file_ns = blender::nodes::node_geo_accumulate_field_cc; + + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_ACCUMULATE_FIELD, "Accumulate Field", NODE_CLASS_CONVERTER); + ntype.geometry_node_execute = file_ns::node_geo_exec; + node_type_init(&ntype, file_ns::node_init); + node_type_update(&ntype, file_ns::node_update); + ntype.draw_buttons = file_ns::node_layout; + ntype.declare = file_ns::node_declare; + ntype.gather_link_search_ops = file_ns::node_gather_link_searches; + node_type_storage( + &ntype, "NodeAccumulateField", node_free_standard_storage, node_copy_standard_storage); + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc index 43fb00a482c..840dfd2fbd3 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc @@ -19,51 +19,51 @@ #include "BKE_attribute_math.hh" +#include "NOD_socket_search_link.hh" + #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_attribute_capture_cc { + +NODE_STORAGE_FUNCS(NodeGeometryAttributeCapture) -static void geo_node_attribute_capture_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Vector>("Value").supports_field(); - b.add_input<decl::Float>("Value", "Value_001").supports_field(); - b.add_input<decl::Color>("Value", "Value_002").supports_field(); - b.add_input<decl::Bool>("Value", "Value_003").supports_field(); - b.add_input<decl::Int>("Value", "Value_004").supports_field(); - - b.add_output<decl::Geometry>("Geometry"); - b.add_output<decl::Vector>("Attribute").field_source(); - b.add_output<decl::Float>("Attribute", "Attribute_001").field_source(); - b.add_output<decl::Color>("Attribute", "Attribute_002").field_source(); - b.add_output<decl::Bool>("Attribute", "Attribute_003").field_source(); - b.add_output<decl::Int>("Attribute", "Attribute_004").field_source(); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::Vector>(N_("Value")).supports_field(); + b.add_input<decl::Float>(N_("Value"), "Value_001").supports_field(); + b.add_input<decl::Color>(N_("Value"), "Value_002").supports_field(); + b.add_input<decl::Bool>(N_("Value"), "Value_003").supports_field(); + b.add_input<decl::Int>(N_("Value"), "Value_004").supports_field(); + + b.add_output<decl::Geometry>(N_("Geometry")); + b.add_output<decl::Vector>(N_("Attribute")).field_source(); + b.add_output<decl::Float>(N_("Attribute"), "Attribute_001").field_source(); + b.add_output<decl::Color>(N_("Attribute"), "Attribute_002").field_source(); + b.add_output<decl::Bool>(N_("Attribute"), "Attribute_003").field_source(); + b.add_output<decl::Int>(N_("Attribute"), "Attribute_004").field_source(); } -static void geo_node_attribute_capture_layout(uiLayout *layout, - bContext *UNUSED(C), - PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); - uiItemR(layout, ptr, "domain", 0, "", ICON_NONE); uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); + uiItemR(layout, ptr, "domain", 0, "", ICON_NONE); } -static void geo_node_attribute_capture_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeGeometryAttributeCapture *data = (NodeGeometryAttributeCapture *)MEM_callocN( - sizeof(NodeGeometryAttributeCapture), __func__); + NodeGeometryAttributeCapture *data = MEM_cnew<NodeGeometryAttributeCapture>(__func__); data->data_type = CD_PROP_FLOAT; data->domain = ATTR_DOMAIN_POINT; node->storage = data; } -static void geo_node_attribute_capture_update(bNodeTree *UNUSED(ntree), bNode *node) +static void node_update(bNodeTree *ntree, bNode *node) { - const NodeGeometryAttributeCapture &storage = *(const NodeGeometryAttributeCapture *) - node->storage; + const NodeGeometryAttributeCapture &storage = node_storage(*node); const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type); bNodeSocket *socket_value_geometry = (bNodeSocket *)node->inputs.first; @@ -73,11 +73,11 @@ static void geo_node_attribute_capture_update(bNodeTree *UNUSED(ntree), bNode *n bNodeSocket *socket_value_boolean = socket_value_color4f->next; bNodeSocket *socket_value_int32 = socket_value_boolean->next; - nodeSetSocketAvailability(socket_value_vector, data_type == CD_PROP_FLOAT3); - nodeSetSocketAvailability(socket_value_float, data_type == CD_PROP_FLOAT); - nodeSetSocketAvailability(socket_value_color4f, data_type == CD_PROP_COLOR); - nodeSetSocketAvailability(socket_value_boolean, data_type == CD_PROP_BOOL); - nodeSetSocketAvailability(socket_value_int32, data_type == CD_PROP_INT32); + nodeSetSocketAvailability(ntree, socket_value_vector, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(ntree, socket_value_float, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(ntree, socket_value_color4f, data_type == CD_PROP_COLOR); + nodeSetSocketAvailability(ntree, socket_value_boolean, data_type == CD_PROP_BOOL); + nodeSetSocketAvailability(ntree, socket_value_int32, data_type == CD_PROP_INT32); bNodeSocket *out_socket_value_geometry = (bNodeSocket *)node->outputs.first; bNodeSocket *out_socket_value_vector = out_socket_value_geometry->next; @@ -86,11 +86,38 @@ static void geo_node_attribute_capture_update(bNodeTree *UNUSED(ntree), bNode *n bNodeSocket *out_socket_value_boolean = out_socket_value_color4f->next; bNodeSocket *out_socket_value_int32 = out_socket_value_boolean->next; - nodeSetSocketAvailability(out_socket_value_vector, data_type == CD_PROP_FLOAT3); - nodeSetSocketAvailability(out_socket_value_float, data_type == CD_PROP_FLOAT); - nodeSetSocketAvailability(out_socket_value_color4f, data_type == CD_PROP_COLOR); - nodeSetSocketAvailability(out_socket_value_boolean, data_type == CD_PROP_BOOL); - nodeSetSocketAvailability(out_socket_value_int32, data_type == CD_PROP_INT32); + nodeSetSocketAvailability(ntree, out_socket_value_vector, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(ntree, out_socket_value_float, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(ntree, out_socket_value_color4f, data_type == CD_PROP_COLOR); + nodeSetSocketAvailability(ntree, out_socket_value_boolean, data_type == CD_PROP_BOOL); + nodeSetSocketAvailability(ntree, out_socket_value_int32, data_type == CD_PROP_INT32); +} + +static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) +{ + const NodeDeclaration &declaration = *params.node_type().fixed_declaration; + search_link_ops_for_declarations(params, declaration.inputs().take_front(1)); + search_link_ops_for_declarations(params, declaration.outputs().take_front(1)); + + const bNodeType &node_type = params.node_type(); + const std::optional<CustomDataType> type = node_data_type_to_custom_data_type( + (eNodeSocketDatatype)params.other_socket().type); + if (type && *type != CD_PROP_STRING) { + if (params.in_out() == SOCK_OUT) { + params.add_item(IFACE_("Attribute"), [node_type, type](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node(node_type); + node_storage(node).data_type = *type; + params.update_and_connect_available_socket(node, "Attribute"); + }); + } + else { + params.add_item(IFACE_("Value"), [node_type, type](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node(node_type); + node_storage(node).data_type = *type; + params.update_and_connect_available_socket(node, "Value"); + }); + } + } } static void try_capture_field_on_geometry(GeometryComponent &component, @@ -113,13 +140,11 @@ static void try_capture_field_on_geometry(GeometryComponent &component, output_attribute.save(); } -static void geo_node_attribute_capture_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - const bNode &node = params.node(); - const NodeGeometryAttributeCapture &storage = *(const NodeGeometryAttributeCapture *) - node.storage; + const NodeGeometryAttributeCapture &storage = node_storage(params.node()); const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type); const AttributeDomain domain = static_cast<AttributeDomain>(storage.domain); @@ -144,20 +169,33 @@ static void geo_node_attribute_capture_exec(GeoNodeExecParams params) break; } - WeakAnonymousAttributeID anonymous_id{"Attribute Capture"}; + WeakAnonymousAttributeID anonymous_id{"Attribute"}; const CPPType &type = field.cpp_type(); - static const Array<GeometryComponentType> types = { - GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE}; - for (const GeometryComponentType type : types) { - if (geometry_set.has(type)) { - GeometryComponent &component = geometry_set.get_component_for_write(type); + /* Run on the instances component separately to only affect the top level of instances. */ + if (domain == ATTR_DOMAIN_INSTANCE) { + if (geometry_set.has_instances()) { + GeometryComponent &component = geometry_set.get_component_for_write( + GEO_COMPONENT_TYPE_INSTANCES); try_capture_field_on_geometry(component, anonymous_id.get(), domain, field); } } + else { + static const Array<GeometryComponentType> types = { + GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE}; + + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + for (const GeometryComponentType type : types) { + if (geometry_set.has(type)) { + GeometryComponent &component = geometry_set.get_component_for_write(type); + try_capture_field_on_geometry(component, anonymous_id.get(), domain, field); + } + } + }); + } - GField output_field{ - std::make_shared<bke::AnonymousAttributeFieldInput>(std::move(anonymous_id), type)}; + GField output_field{std::make_shared<bke::AnonymousAttributeFieldInput>( + std::move(anonymous_id), type, params.attribute_producer_name())}; switch (data_type) { case CD_PROP_FLOAT: { @@ -187,22 +225,25 @@ static void geo_node_attribute_capture_exec(GeoNodeExecParams params) params.set_output("Geometry", geometry_set); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_attribute_capture_cc void register_node_type_geo_attribute_capture() { + namespace file_ns = blender::nodes::node_geo_attribute_capture_cc; + static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_ATTRIBUTE_CAPTURE, "Attribute Capture", NODE_CLASS_ATTRIBUTE, 0); + &ntype, GEO_NODE_CAPTURE_ATTRIBUTE, "Capture Attribute", NODE_CLASS_ATTRIBUTE); node_type_storage(&ntype, "NodeGeometryAttributeCapture", node_free_standard_storage, node_copy_standard_storage); - node_type_init(&ntype, blender::nodes::geo_node_attribute_capture_init); - node_type_update(&ntype, blender::nodes::geo_node_attribute_capture_update); - ntype.declare = blender::nodes::geo_node_attribute_capture_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_attribute_capture_exec; - ntype.draw_buttons = blender::nodes::geo_node_attribute_capture_layout; + node_type_init(&ntype, file_ns::node_init); + node_type_update(&ntype, file_ns::node_update); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.draw_buttons = file_ns::node_layout; + ntype.gather_link_search_ops = file_ns::node_gather_link_searches; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc new file mode 100644 index 00000000000..609ef39eb3f --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc @@ -0,0 +1,154 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_attribute_domain_size_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Geometry"); + b.add_output<decl::Int>("Point Count").make_available([](bNode &node) { + node.custom1 = GEO_COMPONENT_TYPE_MESH; + }); + b.add_output<decl::Int>("Edge Count").make_available([](bNode &node) { + node.custom1 = GEO_COMPONENT_TYPE_MESH; + }); + b.add_output<decl::Int>("Face Count").make_available([](bNode &node) { + node.custom1 = GEO_COMPONENT_TYPE_MESH; + }); + b.add_output<decl::Int>("Face Corner Count").make_available([](bNode &node) { + node.custom1 = GEO_COMPONENT_TYPE_MESH; + }); + b.add_output<decl::Int>("Spline Count").make_available([](bNode &node) { + node.custom1 = GEO_COMPONENT_TYPE_CURVE; + }); + b.add_output<decl::Int>("Instance Count").make_available([](bNode &node) { + node.custom1 = GEO_COMPONENT_TYPE_INSTANCES; + }); +} + +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "component", 0, "", ICON_NONE); +} + +static void node_init(bNodeTree *UNUSED(tree), bNode *node) +{ + node->custom1 = GEO_COMPONENT_TYPE_MESH; +} + +static void node_update(bNodeTree *ntree, bNode *node) +{ + bNodeSocket *point_socket = (bNodeSocket *)node->outputs.first; + bNodeSocket *edge_socket = point_socket->next; + bNodeSocket *face_socket = edge_socket->next; + bNodeSocket *face_corner_socket = face_socket->next; + bNodeSocket *spline_socket = face_corner_socket->next; + bNodeSocket *instances_socket = spline_socket->next; + + nodeSetSocketAvailability(ntree, + point_socket, + ELEM(node->custom1, + GEO_COMPONENT_TYPE_MESH, + GEO_COMPONENT_TYPE_CURVE, + GEO_COMPONENT_TYPE_POINT_CLOUD)); + nodeSetSocketAvailability(ntree, edge_socket, node->custom1 == GEO_COMPONENT_TYPE_MESH); + nodeSetSocketAvailability(ntree, face_socket, node->custom1 == GEO_COMPONENT_TYPE_MESH); + nodeSetSocketAvailability(ntree, face_corner_socket, node->custom1 == GEO_COMPONENT_TYPE_MESH); + nodeSetSocketAvailability(ntree, spline_socket, node->custom1 == GEO_COMPONENT_TYPE_CURVE); + nodeSetSocketAvailability( + ntree, instances_socket, node->custom1 == GEO_COMPONENT_TYPE_INSTANCES); +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + GeometryComponentType component = (GeometryComponentType)params.node().custom1; + GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + + switch (component) { + case GEO_COMPONENT_TYPE_MESH: { + if (geometry_set.has_mesh()) { + const MeshComponent *component = geometry_set.get_component_for_read<MeshComponent>(); + params.set_output("Point Count", component->attribute_domain_size(ATTR_DOMAIN_POINT)); + params.set_output("Edge Count", component->attribute_domain_size(ATTR_DOMAIN_EDGE)); + params.set_output("Face Count", component->attribute_domain_size(ATTR_DOMAIN_FACE)); + params.set_output("Face Corner Count", + component->attribute_domain_size(ATTR_DOMAIN_CORNER)); + } + else { + params.set_default_remaining_outputs(); + } + break; + } + case GEO_COMPONENT_TYPE_CURVE: { + if (geometry_set.has_curve()) { + const CurveComponent *component = geometry_set.get_component_for_read<CurveComponent>(); + params.set_output("Point Count", component->attribute_domain_size(ATTR_DOMAIN_POINT)); + params.set_output("Spline Count", component->attribute_domain_size(ATTR_DOMAIN_CURVE)); + } + else { + params.set_default_remaining_outputs(); + } + break; + } + case GEO_COMPONENT_TYPE_POINT_CLOUD: { + if (geometry_set.has_pointcloud()) { + const PointCloudComponent *component = + geometry_set.get_component_for_read<PointCloudComponent>(); + params.set_output("Point Count", component->attribute_domain_size(ATTR_DOMAIN_POINT)); + } + else { + params.set_default_remaining_outputs(); + } + break; + } + case GEO_COMPONENT_TYPE_INSTANCES: { + if (geometry_set.has_instances()) { + const InstancesComponent *component = + geometry_set.get_component_for_read<InstancesComponent>(); + params.set_output("Instance Count", + component->attribute_domain_size(ATTR_DOMAIN_INSTANCE)); + } + else { + params.set_default_remaining_outputs(); + } + break; + } + default: + BLI_assert_unreachable(); + } +} + +} // namespace blender::nodes::node_geo_attribute_domain_size_cc + +void register_node_type_geo_attribute_domain_size() +{ + namespace file_ns = blender::nodes::node_geo_attribute_domain_size_cc; + + static bNodeType ntype; + geo_node_type_base(&ntype, GEO_NODE_ATTRIBUTE_DOMAIN_SIZE, "Domain Size", NODE_CLASS_ATTRIBUTE); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.declare = file_ns::node_declare; + ntype.draw_buttons = file_ns::node_layout; + node_type_init(&ntype, file_ns::node_init); + ntype.updatefunc = file_ns::node_update; + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc index f93ef6f1db3..8ed50b2cc75 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc @@ -16,13 +16,13 @@ #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_attribute_remove_cc { -static void geo_node_attribute_remove_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::String>("Attribute").multi_input(); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::String>(N_("Attribute")).multi_input(); + b.add_output<decl::Geometry>(N_("Geometry")); } static void remove_attribute(GeometryComponent &component, @@ -42,7 +42,7 @@ static void remove_attribute(GeometryComponent &component, } } -static void geo_node_attribute_remove_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); Vector<std::string> attribute_names = params.extract_multi_input<std::string>("Attribute"); @@ -59,18 +59,23 @@ static void geo_node_attribute_remove_exec(GeoNodeExecParams params) remove_attribute( geometry_set.get_component_for_write<CurveComponent>(), params, attribute_names); } + if (geometry_set.has<InstancesComponent>()) { + remove_attribute( + geometry_set.get_component_for_write<InstancesComponent>(), params, attribute_names); + } params.set_output("Geometry", geometry_set); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_attribute_remove_cc void register_node_type_geo_attribute_remove() { + namespace file_ns = blender::nodes::node_geo_attribute_remove_cc; + static bNodeType ntype; - geo_node_type_base( - &ntype, GEO_NODE_ATTRIBUTE_REMOVE, "Attribute Remove", NODE_CLASS_ATTRIBUTE, 0); - ntype.geometry_node_execute = blender::nodes::geo_node_attribute_remove_exec; - ntype.declare = blender::nodes::geo_node_attribute_remove_declare; + geo_node_type_base(&ntype, GEO_NODE_ATTRIBUTE_REMOVE, "Attribute Remove", NODE_CLASS_ATTRIBUTE); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.declare = file_ns::node_declare; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc index 1b7d2fe28a1..7df032b150b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc @@ -22,53 +22,55 @@ #include "BLI_math_base_safe.h" +#include "NOD_socket_search_link.hh" + #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_attribute_statistic_cc { -static void geo_node_attribute_statistic_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Float>("Attribute").hide_value().supports_field(); - b.add_input<decl::Vector>("Attribute", "Attribute_001").hide_value().supports_field(); - - b.add_output<decl::Float>("Mean"); - b.add_output<decl::Float>("Median"); - b.add_output<decl::Float>("Sum"); - b.add_output<decl::Float>("Min"); - b.add_output<decl::Float>("Max"); - b.add_output<decl::Float>("Range"); - b.add_output<decl::Float>("Standard Deviation"); - b.add_output<decl::Float>("Variance"); - - b.add_output<decl::Vector>("Mean", "Mean_001"); - b.add_output<decl::Vector>("Median", "Median_001"); - b.add_output<decl::Vector>("Sum", "Sum_001"); - b.add_output<decl::Vector>("Min", "Min_001"); - b.add_output<decl::Vector>("Max", "Max_001"); - b.add_output<decl::Vector>("Range", "Range_001"); - b.add_output<decl::Vector>("Standard Deviation", "Standard Deviation_001"); - b.add_output<decl::Vector>("Variance", "Variance_001"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).supports_field().hide_value(); + b.add_input<decl::Float>(N_("Attribute")).hide_value().supports_field(); + b.add_input<decl::Vector>(N_("Attribute"), "Attribute_001").hide_value().supports_field(); + + b.add_output<decl::Float>(N_("Mean")); + b.add_output<decl::Float>(N_("Median")); + b.add_output<decl::Float>(N_("Sum")); + b.add_output<decl::Float>(N_("Min")); + b.add_output<decl::Float>(N_("Max")); + b.add_output<decl::Float>(N_("Range")); + b.add_output<decl::Float>(N_("Standard Deviation")); + b.add_output<decl::Float>(N_("Variance")); + + b.add_output<decl::Vector>(N_("Mean"), "Mean_001"); + b.add_output<decl::Vector>(N_("Median"), "Median_001"); + b.add_output<decl::Vector>(N_("Sum"), "Sum_001"); + b.add_output<decl::Vector>(N_("Min"), "Min_001"); + b.add_output<decl::Vector>(N_("Max"), "Max_001"); + b.add_output<decl::Vector>(N_("Range"), "Range_001"); + b.add_output<decl::Vector>(N_("Standard Deviation"), "Standard Deviation_001"); + b.add_output<decl::Vector>(N_("Variance"), "Variance_001"); } -static void geo_node_attribute_statistic_layout(uiLayout *layout, - bContext *UNUSED(C), - PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); uiItemR(layout, ptr, "domain", 0, "", ICON_NONE); } -static void geo_node_attribute_statistic_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree *UNUSED(tree), bNode *node) { node->custom1 = CD_PROP_FLOAT; node->custom2 = ATTR_DOMAIN_POINT; } -static void geo_node_attribute_statistic_update(bNodeTree *UNUSED(ntree), bNode *node) +static void node_update(bNodeTree *ntree, bNode *node) { bNodeSocket *socket_geo = (bNodeSocket *)node->inputs.first; - bNodeSocket *socket_float_attr = socket_geo->next; + bNodeSocket *socket_selection = socket_geo->next; + bNodeSocket *socket_float_attr = socket_selection->next; bNodeSocket *socket_float3_attr = socket_float_attr->next; bNodeSocket *socket_float_mean = (bNodeSocket *)node->outputs.first; @@ -91,25 +93,70 @@ static void geo_node_attribute_statistic_update(bNodeTree *UNUSED(ntree), bNode const CustomDataType data_type = static_cast<CustomDataType>(node->custom1); - nodeSetSocketAvailability(socket_float_attr, data_type == CD_PROP_FLOAT); - nodeSetSocketAvailability(socket_float_mean, data_type == CD_PROP_FLOAT); - nodeSetSocketAvailability(socket_float_median, data_type == CD_PROP_FLOAT); - nodeSetSocketAvailability(socket_float_sum, data_type == CD_PROP_FLOAT); - nodeSetSocketAvailability(socket_float_min, data_type == CD_PROP_FLOAT); - nodeSetSocketAvailability(socket_float_max, data_type == CD_PROP_FLOAT); - nodeSetSocketAvailability(socket_float_range, data_type == CD_PROP_FLOAT); - nodeSetSocketAvailability(socket_float_std, data_type == CD_PROP_FLOAT); - nodeSetSocketAvailability(socket_float_variance, data_type == CD_PROP_FLOAT); - - nodeSetSocketAvailability(socket_float3_attr, data_type == CD_PROP_FLOAT3); - nodeSetSocketAvailability(socket_vector_mean, data_type == CD_PROP_FLOAT3); - nodeSetSocketAvailability(socket_vector_median, data_type == CD_PROP_FLOAT3); - nodeSetSocketAvailability(socket_vector_sum, data_type == CD_PROP_FLOAT3); - nodeSetSocketAvailability(socket_vector_min, data_type == CD_PROP_FLOAT3); - nodeSetSocketAvailability(socket_vector_max, data_type == CD_PROP_FLOAT3); - nodeSetSocketAvailability(socket_vector_range, data_type == CD_PROP_FLOAT3); - nodeSetSocketAvailability(socket_vector_std, data_type == CD_PROP_FLOAT3); - nodeSetSocketAvailability(socket_vector_variance, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(ntree, socket_float_attr, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(ntree, socket_float_mean, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(ntree, socket_float_median, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(ntree, socket_float_sum, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(ntree, socket_float_min, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(ntree, socket_float_max, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(ntree, socket_float_range, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(ntree, socket_float_std, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(ntree, socket_float_variance, data_type == CD_PROP_FLOAT); + + nodeSetSocketAvailability(ntree, socket_float3_attr, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(ntree, socket_vector_mean, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(ntree, socket_vector_median, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(ntree, socket_vector_sum, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(ntree, socket_vector_min, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(ntree, socket_vector_max, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(ntree, socket_vector_range, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(ntree, socket_vector_std, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(ntree, socket_vector_variance, data_type == CD_PROP_FLOAT3); +} + +static std::optional<CustomDataType> node_type_from_other_socket(const bNodeSocket &socket) +{ + switch (socket.type) { + case SOCK_FLOAT: + case SOCK_BOOLEAN: + case SOCK_INT: + return CD_PROP_FLOAT; + case SOCK_VECTOR: + case SOCK_RGBA: + return CD_PROP_FLOAT3; + default: + return {}; + } +} + +static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) +{ + const bNodeType &node_type = params.node_type(); + const NodeDeclaration &declaration = *params.node_type().fixed_declaration; + search_link_ops_for_declarations(params, declaration.inputs().take_front(2)); + + const std::optional<CustomDataType> type = node_type_from_other_socket(params.other_socket()); + if (!type) { + return; + } + + if (params.in_out() == SOCK_IN) { + params.add_item(IFACE_("Attribute"), [node_type, type](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node(node_type); + node.custom1 = *type; + params.update_and_connect_available_socket(node, "Attribute"); + }); + } + else { + for (const StringRefNull name : + {"Mean", "Median", "Sum", "Min", "Max", "Range", "Standard Deviation", "Variance"}) { + params.add_item(IFACE_(name.c_str()), [node_type, name, type](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node(node_type); + node.custom1 = *type; + params.update_and_connect_available_socket(node, name); + }); + } + } } template<typename T> static T compute_sum(const Span<T> data) @@ -146,65 +193,40 @@ static float median_of_sorted_span(const Span<float> data) } return median; } -static void set_empty(CustomDataType data_type, GeoNodeExecParams ¶ms) -{ - if (data_type == CD_PROP_FLOAT) { - params.set_output("Mean", 0.0f); - params.set_output("Median", 0.0f); - params.set_output("Sum", 0.0f); - params.set_output("Min", 0.0f); - params.set_output("Max", 0.0f); - params.set_output("Range", 0.0f); - params.set_output("Standard Deviation", 0.0f); - params.set_output("Variance", 0.0f); - } - else if (data_type == CD_PROP_FLOAT3) { - params.set_output("Mean_001", float3{0.0f, 0.0f, 0.0f}); - params.set_output("Median_001", float3{0.0f, 0.0f, 0.0f}); - params.set_output("Sum_001", float3{0.0f, 0.0f, 0.0f}); - params.set_output("Min_001", float3{0.0f, 0.0f, 0.0f}); - params.set_output("Max_001", float3{0.0f, 0.0f, 0.0f}); - params.set_output("Range_001", float3{0.0f, 0.0f, 0.0f}); - params.set_output("Standard Deviation_001", float3{0.0f, 0.0f, 0.0f}); - params.set_output("Variance_001", float3{0.0f, 0.0f, 0.0f}); - } -} -static void geo_node_attribute_statistic_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.get_input<GeometrySet>("Geometry"); - const bNode &node = params.node(); const CustomDataType data_type = static_cast<CustomDataType>(node.custom1); const AttributeDomain domain = static_cast<AttributeDomain>(node.custom2); - - int64_t total_size = 0; Vector<const GeometryComponent *> components = geometry_set.get_components_for_read(); - for (const GeometryComponent *component : components) { - if (component->attribute_domain_supported(domain)) { - total_size += component->attribute_domain_size(domain); - } - } - if (total_size == 0) { - set_empty(data_type, params); - return; - } + const Field<bool> selection_field = params.get_input<Field<bool>>("Selection"); switch (data_type) { case CD_PROP_FLOAT: { const Field<float> input_field = params.get_input<Field<float>>("Attribute"); - Array<float> data = Array<float>(total_size); - int offset = 0; + Vector<float> data; for (const GeometryComponent *component : components) { if (component->attribute_domain_supported(domain)) { GeometryComponentFieldContext field_context{*component, domain}; const int domain_size = component->attribute_domain_size(domain); + fn::FieldEvaluator data_evaluator{field_context, domain_size}; - MutableSpan<float> component_result = data.as_mutable_span().slice(offset, domain_size); - data_evaluator.add_with_destination(input_field, component_result); + data_evaluator.add(input_field); + data_evaluator.set_selection(selection_field); data_evaluator.evaluate(); - offset += domain_size; + const VArray<float> &component_data = data_evaluator.get_evaluated<float>(0); + const IndexMask selection = data_evaluator.get_evaluated_selection_as_mask(); + + const int next_data_index = data.size(); + data.resize(next_data_index + selection.size()); + MutableSpan<float> selected_data = data.as_mutable_span().slice(next_data_index, + selection.size()); + for (const int i : selection.index_range()) { + selected_data[i] = component_data[selection[i]]; + } } } @@ -225,7 +247,7 @@ static void geo_node_attribute_statistic_exec(GeoNodeExecParams params) const bool variance_required = params.output_is_required("Standard Deviation") || params.output_is_required("Variance"); - if (total_size != 0) { + if (data.size() != 0) { if (sort_required) { std::sort(data.begin(), data.end()); median = median_of_sorted_span(data); @@ -236,7 +258,7 @@ static void geo_node_attribute_statistic_exec(GeoNodeExecParams params) } if (sum_required || variance_required) { sum = compute_sum<float>(data); - mean = sum / total_size; + mean = sum / data.size(); if (variance_required) { variance = compute_variance(data, mean); @@ -263,18 +285,26 @@ static void geo_node_attribute_statistic_exec(GeoNodeExecParams params) } case CD_PROP_FLOAT3: { const Field<float3> input_field = params.get_input<Field<float3>>("Attribute_001"); - - Array<float3> data = Array<float3>(total_size); - int offset = 0; + Vector<float3> data; for (const GeometryComponent *component : components) { if (component->attribute_domain_supported(domain)) { GeometryComponentFieldContext field_context{*component, domain}; const int domain_size = component->attribute_domain_size(domain); + fn::FieldEvaluator data_evaluator{field_context, domain_size}; - MutableSpan<float3> component_result = data.as_mutable_span().slice(offset, domain_size); - data_evaluator.add_with_destination(input_field, component_result); + data_evaluator.add(input_field); + data_evaluator.set_selection(selection_field); data_evaluator.evaluate(); - offset += domain_size; + const VArray<float3> &component_data = data_evaluator.get_evaluated<float3>(0); + const IndexMask selection = data_evaluator.get_evaluated_selection_as_mask(); + + const int next_data_index = data.size(); + data.resize(data.size() + selection.size()); + MutableSpan<float3> selected_data = data.as_mutable_span().slice(next_data_index, + selection.size()); + for (const int i : selection.index_range()) { + selected_data[i] = component_data[selection[i]]; + } } } @@ -299,9 +329,9 @@ static void geo_node_attribute_statistic_exec(GeoNodeExecParams params) Array<float> data_y; Array<float> data_z; if (sort_required || variance_required) { - data_x.reinitialize(total_size); - data_y.reinitialize(total_size); - data_z.reinitialize(total_size); + data_x.reinitialize(data.size()); + data_y.reinitialize(data.size()); + data_z.reinitialize(data.size()); for (const int i : data.index_range()) { data_x[i] = data[i].x; data_y[i] = data[i].y; @@ -309,7 +339,7 @@ static void geo_node_attribute_statistic_exec(GeoNodeExecParams params) } } - if (total_size != 0) { + if (data.size() != 0) { if (sort_required) { std::sort(data_x.begin(), data_x.end()); std::sort(data_y.begin(), data_y.end()); @@ -326,7 +356,7 @@ static void geo_node_attribute_statistic_exec(GeoNodeExecParams params) } if (sum_required || variance_required) { sum = compute_sum(data.as_span()); - mean = sum / total_size; + mean = sum / data.size(); if (variance_required) { const float x_variance = compute_variance(data_x, mean.x); @@ -360,19 +390,22 @@ static void geo_node_attribute_statistic_exec(GeoNodeExecParams params) } } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_attribute_statistic_cc void register_node_type_geo_attribute_statistic() { + namespace file_ns = blender::nodes::node_geo_attribute_statistic_cc; + static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_ATTRIBUTE_STATISTIC, "Attribute Statistic", NODE_CLASS_ATTRIBUTE, 0); - - ntype.declare = blender::nodes::geo_node_attribute_statistic_declare; - node_type_init(&ntype, blender::nodes::geo_node_attribute_statistic_init); - node_type_update(&ntype, blender::nodes::geo_node_attribute_statistic_update); - ntype.geometry_node_execute = blender::nodes::geo_node_attribute_statistic_exec; - ntype.draw_buttons = blender::nodes::geo_node_attribute_statistic_layout; + &ntype, GEO_NODE_ATTRIBUTE_STATISTIC, "Attribute Statistic", NODE_CLASS_ATTRIBUTE); + + ntype.declare = file_ns::node_declare; + node_type_init(&ntype, file_ns::node_init); + node_type_update(&ntype, file_ns::node_update); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.draw_buttons = file_ns::node_layout; + ntype.gather_link_search_ops = file_ns::node_gather_link_searches; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc index 21b425c0ed4..a9158e0ef7a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc @@ -23,23 +23,25 @@ #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_boolean_cc { -static void geo_node_boolean_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry 1"); - b.add_input<decl::Geometry>("Geometry 2").multi_input(); - b.add_input<decl::Bool>("Self Intersection"); - b.add_input<decl::Bool>("Hole Tolerant"); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Mesh 1")) + .only_realized_data() + .supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input<decl::Geometry>(N_("Mesh 2")).multi_input().supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input<decl::Bool>(N_("Self Intersection")); + b.add_input<decl::Bool>(N_("Hole Tolerant")); + b.add_output<decl::Geometry>(N_("Mesh")); } -static void geo_node_boolean_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "operation", 0, "", ICON_NONE); } -static void geo_node_boolean_update(bNodeTree *UNUSED(ntree), bNode *node) +static void node_update(bNodeTree *ntree, bNode *node) { GeometryNodeBooleanOperation operation = (GeometryNodeBooleanOperation)node->custom1; @@ -49,24 +51,24 @@ static void geo_node_boolean_update(bNodeTree *UNUSED(ntree), bNode *node) switch (operation) { case GEO_NODE_BOOLEAN_INTERSECT: case GEO_NODE_BOOLEAN_UNION: - nodeSetSocketAvailability(geometry_1_socket, false); - nodeSetSocketAvailability(geometry_2_socket, true); - node_sock_label(geometry_2_socket, N_("Geometry")); + nodeSetSocketAvailability(ntree, geometry_1_socket, false); + nodeSetSocketAvailability(ntree, geometry_2_socket, true); + node_sock_label(geometry_2_socket, N_("Mesh")); break; case GEO_NODE_BOOLEAN_DIFFERENCE: - nodeSetSocketAvailability(geometry_1_socket, true); - nodeSetSocketAvailability(geometry_2_socket, true); - node_sock_label(geometry_2_socket, N_("Geometry 2")); + nodeSetSocketAvailability(ntree, geometry_1_socket, true); + nodeSetSocketAvailability(ntree, geometry_2_socket, true); + node_sock_label(geometry_2_socket, N_("Mesh 2")); break; } } -static void geo_node_boolean_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree *UNUSED(tree), bNode *node) { node->custom1 = GEO_NODE_BOOLEAN_DIFFERENCE; } -static void geo_node_boolean_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { GeometryNodeBooleanOperation operation = (GeometryNodeBooleanOperation)params.node().custom1; const bool use_self = params.get_input<bool>("Self Intersection"); @@ -82,12 +84,7 @@ static void geo_node_boolean_exec(GeoNodeExecParams params) GeometrySet set_a; if (operation == GEO_NODE_BOOLEAN_DIFFERENCE) { - set_a = params.extract_input<GeometrySet>("Geometry 1"); - if (set_a.has_instances()) { - params.error_message_add( - NodeWarningType::Info, - TIP_("Instances are not supported for the first geometry input, and will not be used")); - } + set_a = params.extract_input<GeometrySet>("Mesh 1"); /* Note that it technically wouldn't be necessary to realize the instances for the first * geometry input, but the boolean code expects the first shape for the difference operation * to be a single mesh. */ @@ -101,7 +98,7 @@ static void geo_node_boolean_exec(GeoNodeExecParams params) /* The instance transform matrices are owned by the instance group, so we have to * keep all of them around for use during the boolean operation. */ Vector<bke::GeometryInstanceGroup> set_groups; - Vector<GeometrySet> geometry_sets = params.extract_multi_input<GeometrySet>("Geometry 2"); + Vector<GeometrySet> geometry_sets = params.extract_multi_input<GeometrySet>("Mesh 2"); for (const GeometrySet &geometry_set : geometry_sets) { bke::geometry_set_gather_instances(geometry_set, set_groups); } @@ -119,20 +116,22 @@ static void geo_node_boolean_exec(GeoNodeExecParams params) Mesh *result = blender::meshintersect::direct_mesh_boolean( meshes, transforms, float4x4::identity(), {}, use_self, hole_tolerant, operation); - params.set_output("Geometry", GeometrySet::create_with_mesh(result)); + params.set_output("Mesh", GeometrySet::create_with_mesh(result)); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_boolean_cc void register_node_type_geo_boolean() { + namespace file_ns = blender::nodes::node_geo_boolean_cc; + static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_BOOLEAN, "Boolean", NODE_CLASS_GEOMETRY, 0); - ntype.declare = blender::nodes::geo_node_boolean_declare; - ntype.draw_buttons = blender::nodes::geo_node_boolean_layout; - ntype.updatefunc = blender::nodes::geo_node_boolean_update; - node_type_init(&ntype, blender::nodes::geo_node_boolean_init); - ntype.geometry_node_execute = blender::nodes::geo_node_boolean_exec; + geo_node_type_base(&ntype, GEO_NODE_MESH_BOOLEAN, "Mesh Boolean", NODE_CLASS_GEOMETRY); + ntype.declare = file_ns::node_declare; + ntype.draw_buttons = file_ns::node_layout; + ntype.updatefunc = file_ns::node_update; + node_type_init(&ntype, file_ns::node_init); + ntype.geometry_node_execute = file_ns::node_geo_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc b/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc index fdc6b12095c..465bd72b57a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc @@ -14,160 +14,80 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include "BKE_spline.hh" -#include "BKE_volume.h" - #include "node_geometry_util.hh" -namespace blender::nodes { - -static void geo_node_bounding_box_declare(NodeDeclarationBuilder &b) -{ - b.add_input<decl::Geometry>("Geometry"); - b.add_output<decl::Geometry>("Bounding Box"); - b.add_output<decl::Vector>("Min"); - b.add_output<decl::Vector>("Max"); -} - -using bke::GeometryInstanceGroup; +namespace blender::nodes::node_geo_bounding_box_cc { -static void compute_min_max_from_position_and_transform(const GeometryComponent &component, - Span<float4x4> transforms, - float3 &r_min, - float3 &r_max) +static void node_declare(NodeDeclarationBuilder &b) { - GVArray_Typed<float3> positions = component.attribute_get_for_read<float3>( - "position", ATTR_DOMAIN_POINT, {0, 0, 0}); - - for (const float4x4 &transform : transforms) { - for (const int i : positions.index_range()) { - const float3 position = positions[i]; - const float3 transformed_position = transform * position; - minmax_v3v3_v3(r_min, r_max, transformed_position); - } - } -} - -static void compute_min_max_from_volume_and_transforms(const VolumeComponent &volume_component, - Span<float4x4> transforms, - float3 &r_min, - float3 &r_max) -{ -#ifdef WITH_OPENVDB - const Volume *volume = volume_component.get_for_read(); - if (volume == nullptr) { - return; - } - for (const int i : IndexRange(BKE_volume_num_grids(volume))) { - const VolumeGrid *volume_grid = BKE_volume_grid_get_for_read(volume, i); - openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid); - - for (const float4x4 &transform : transforms) { - openvdb::GridBase::ConstPtr instance_grid = BKE_volume_grid_shallow_transform(grid, - transform); - float3 grid_min = float3(FLT_MAX); - float3 grid_max = float3(-FLT_MAX); - if (BKE_volume_grid_bounds(instance_grid, grid_min, grid_max)) { - DO_MIN(grid_min, r_min); - DO_MAX(grid_max, r_max); - } - } - } -#else - UNUSED_VARS(volume_component, transforms, r_min, r_max); -#endif -} - -static void compute_min_max_from_curve_and_transforms(const CurveComponent &curve_component, - Span<float4x4> transforms, - float3 &r_min, - float3 &r_max) -{ - const CurveEval *curve = curve_component.get_for_read(); - if (curve == nullptr) { - return; - } - for (const SplinePtr &spline : curve->splines()) { - Span<float3> positions = spline->evaluated_positions(); - - for (const float4x4 &transform : transforms) { - for (const int i : positions.index_range()) { - const float3 position = positions[i]; - const float3 transformed_position = transform * position; - minmax_v3v3_v3(r_min, r_max, transformed_position); - } - } - } + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_output<decl::Geometry>(N_("Bounding Box")); + b.add_output<decl::Vector>(N_("Min")); + b.add_output<decl::Vector>(N_("Max")); } -static void compute_geometry_set_instances_boundbox(const GeometrySet &geometry_set, - float3 &r_min, - float3 &r_max) -{ - Vector<GeometryInstanceGroup> set_groups; - bke::geometry_set_gather_instances(geometry_set, set_groups); - - for (const GeometryInstanceGroup &set_group : set_groups) { - const GeometrySet &set = set_group.geometry_set; - Span<float4x4> transforms = set_group.transforms; - - if (set.has<PointCloudComponent>()) { - compute_min_max_from_position_and_transform( - *set.get_component_for_read<PointCloudComponent>(), transforms, r_min, r_max); - } - if (set.has<MeshComponent>()) { - compute_min_max_from_position_and_transform( - *set.get_component_for_read<MeshComponent>(), transforms, r_min, r_max); - } - if (set.has<VolumeComponent>()) { - compute_min_max_from_volume_and_transforms( - *set.get_component_for_read<VolumeComponent>(), transforms, r_min, r_max); - } - if (set.has<CurveComponent>()) { - compute_min_max_from_curve_and_transforms( - *set.get_component_for_read<CurveComponent>(), transforms, r_min, r_max); - } - } -} - -static void geo_node_bounding_box_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + /* Compute the min and max of all realized geometry for the two + * vector outputs, which are only meant to consider real geometry. */ float3 min = float3(FLT_MAX); float3 max = float3(-FLT_MAX); - - if (geometry_set.has_instances()) { - compute_geometry_set_instances_boundbox(geometry_set, min, max); - } - else { - geometry_set.compute_boundbox_without_instances(&min, &max); - } - + geometry_set.compute_boundbox_without_instances(&min, &max); if (min == float3(FLT_MAX)) { - params.set_output("Bounding Box", GeometrySet()); params.set_output("Min", float3(0)); params.set_output("Max", float3(0)); } else { - const float3 scale = max - min; - const float3 center = min + scale / 2.0f; - Mesh *mesh = create_cuboid_mesh(scale, 2, 2, 2); - transform_mesh(mesh, center, float3(0), float3(1)); - params.set_output("Bounding Box", GeometrySet::create_with_mesh(mesh)); params.set_output("Min", min); params.set_output("Max", max); } + + /* Generate the bounding box meshes inside each unique geometry set (including individually for + * every instance). Because geometry components are reference counted anyway, we can just + * repurpose the original geometry sets for the output. */ + if (params.output_is_required("Bounding Box")) { + geometry_set.modify_geometry_sets([&](GeometrySet &sub_geometry) { + float3 sub_min = float3(FLT_MAX); + float3 sub_max = float3(-FLT_MAX); + + /* Reuse the min and max calculation if this is the main "real" geometry set. */ + if (&sub_geometry == &geometry_set) { + sub_min = min; + sub_max = max; + } + else { + sub_geometry.compute_boundbox_without_instances(&sub_min, &sub_max); + } + + if (sub_min == float3(FLT_MAX)) { + sub_geometry.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); + } + else { + const float3 scale = sub_max - sub_min; + const float3 center = sub_min + scale / 2.0f; + Mesh *mesh = create_cuboid_mesh(scale, 2, 2, 2); + transform_mesh(*mesh, center, float3(0), float3(1)); + sub_geometry.replace_mesh(mesh); + sub_geometry.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES}); + } + }); + + params.set_output("Bounding Box", std::move(geometry_set)); + } } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_bounding_box_cc void register_node_type_geo_bounding_box() { + namespace file_ns = blender::nodes::node_geo_bounding_box_cc; + static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_BOUNDING_BOX, "Bounding Box", NODE_CLASS_GEOMETRY, 0); - ntype.declare = blender::nodes::geo_node_bounding_box_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_bounding_box_exec; + geo_node_type_base(&ntype, GEO_NODE_BOUNDING_BOX, "Bounding Box", NODE_CLASS_GEOMETRY); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc b/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc index d03221703f0..43816b8d8dc 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc @@ -25,53 +25,67 @@ #include "node_geometry_util.hh" -namespace blender::nodes { +#include <algorithm> -static void geo_node_collection_info_declare(NodeDeclarationBuilder &b) +namespace blender::nodes::node_geo_collection_info_cc { + +NODE_STORAGE_FUNCS(NodeGeometryCollectionInfo) + +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Collection>("Collection").hide_label(); - b.add_input<decl::Bool>("Separate Children") - .description("Output each child of the collection as a separate instance"); - b.add_input<decl::Bool>("Reset Children") + b.add_input<decl::Collection>(N_("Collection")).hide_label(); + b.add_input<decl::Bool>(N_("Separate Children")) .description( - "Reset the transforms of every child instance in the output. Only used when Separate " - "Children is enabled"); - b.add_output<decl::Geometry>("Geometry"); + N_("Output each child of the collection as a separate instance, sorted alphabetically")); + b.add_input<decl::Bool>(N_("Reset Children")) + .description( + N_("Reset the transforms of every child instance in the output. Only used when Separate " + "Children is enabled")); + b.add_output<decl::Geometry>(N_("Geometry")); } -static void geo_node_collection_info_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "transform_space", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); } -static void geo_node_collection_info_node_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeGeometryCollectionInfo *data = (NodeGeometryCollectionInfo *)MEM_callocN( - sizeof(NodeGeometryCollectionInfo), __func__); + NodeGeometryCollectionInfo *data = MEM_cnew<NodeGeometryCollectionInfo>(__func__); data->transform_space = GEO_NODE_TRANSFORM_SPACE_ORIGINAL; node->storage = data; } -static void geo_node_collection_info_exec(GeoNodeExecParams params) +struct InstanceListEntry { + int handle; + char *name; + float4x4 transform; +}; + +static void node_geo_exec(GeoNodeExecParams params) { Collection *collection = params.get_input<Collection *>("Collection"); - GeometrySet geometry_set_out; - if (collection == nullptr) { - params.set_output("Geometry", geometry_set_out); + params.set_default_remaining_outputs(); + return; + } + const Object *self_object = params.self_object(); + const bool is_recursive = BKE_collection_has_object_recursive_instanced(collection, + (Object *)self_object); + if (is_recursive) { + params.error_message_add(NodeWarningType::Error, "Collection contains current object"); + params.set_default_remaining_outputs(); return; } - const bNode &bnode = params.node(); - NodeGeometryCollectionInfo *node_storage = (NodeGeometryCollectionInfo *)bnode.storage; - const bool use_relative_transform = (node_storage->transform_space == + const NodeGeometryCollectionInfo &storage = node_storage(params.node()); + const bool use_relative_transform = (storage.transform_space == GEO_NODE_TRANSFORM_SPACE_RELATIVE); + GeometrySet geometry_set_out; InstancesComponent &instances = geometry_set_out.get_component_for_write<InstancesComponent>(); - const Object *self_object = params.self_object(); - const bool separate_children = params.get_input<bool>("Separate Children"); if (separate_children) { const bool reset_children = params.get_input<bool>("Reset Children"); @@ -85,6 +99,8 @@ static void geo_node_collection_info_exec(GeoNodeExecParams params) } instances.reserve(children_collections.size() + children_objects.size()); + Vector<InstanceListEntry> entries; + entries.reserve(children_collections.size() + children_objects.size()); for (Collection *child_collection : children_collections) { float4x4 transform = float4x4::identity(); @@ -98,7 +114,7 @@ static void geo_node_collection_info_exec(GeoNodeExecParams params) } } const int handle = instances.add_reference(*child_collection); - instances.add_instance(handle, transform); + entries.append({handle, &(child_collection->id.name[2]), transform}); } for (Object *child_object : children_objects) { const int handle = instances.add_reference(*child_object); @@ -112,7 +128,16 @@ static void geo_node_collection_info_exec(GeoNodeExecParams params) } mul_m4_m4_post(transform.values, child_object->obmat); } - instances.add_instance(handle, transform); + entries.append({handle, &(child_object->id.name[2]), transform}); + } + + std::sort(entries.begin(), + entries.end(), + [](const InstanceListEntry &a, const InstanceListEntry &b) { + return BLI_strcasecmp_natural(a.name, b.name) <= 0; + }); + for (const InstanceListEntry &entry : entries) { + instances.add_instance(entry.handle, entry.transform); } } else { @@ -129,20 +154,22 @@ static void geo_node_collection_info_exec(GeoNodeExecParams params) params.set_output("Geometry", geometry_set_out); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_collection_info_cc void register_node_type_geo_collection_info() { + namespace file_ns = blender::nodes::node_geo_collection_info_cc; + static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_COLLECTION_INFO, "Collection Info", NODE_CLASS_INPUT, 0); - ntype.declare = blender::nodes::geo_node_collection_info_declare; - node_type_init(&ntype, blender::nodes::geo_node_collection_info_node_init); + geo_node_type_base(&ntype, GEO_NODE_COLLECTION_INFO, "Collection Info", NODE_CLASS_INPUT); + ntype.declare = file_ns::node_declare; + node_type_init(&ntype, file_ns::node_node_init); node_type_storage(&ntype, "NodeGeometryCollectionInfo", node_free_standard_storage, node_copy_standard_storage); - ntype.geometry_node_execute = blender::nodes::geo_node_collection_info_exec; - ntype.draw_buttons = blender::nodes::geo_node_collection_info_layout; + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.draw_buttons = file_ns::node_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_common.cc b/source/blender/nodes/geometry/nodes/node_geo_common.cc index e2bb7e9f939..093b4450657 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_common.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_common.cc @@ -22,23 +22,21 @@ #include "node_common.h" #include "node_geometry_util.hh" -void register_node_type_geo_group(void) +void register_node_type_geo_group() { static bNodeType ntype; - node_type_base_custom(&ntype, "GeometryNodeGroup", "Group", NODE_CLASS_GROUP, 0); + node_type_base_custom(&ntype, "GeometryNodeGroup", "Group", NODE_CLASS_GROUP); ntype.type = NODE_GROUP; ntype.poll = geo_node_poll_default; ntype.poll_instance = node_group_poll_instance; ntype.insert_link = node_insert_link_default; - ntype.update_internal_links = node_update_internal_links_default; ntype.rna_ext.srna = RNA_struct_find("GeometryNodeGroup"); BLI_assert(ntype.rna_ext.srna != nullptr); RNA_struct_blender_type_set(ntype.rna_ext.srna, &ntype); - node_type_socket_templates(&ntype, nullptr, nullptr); node_type_size(&ntype, 140, 60, 400); - node_type_label(&ntype, node_group_label); + ntype.labelfunc = node_group_label; node_type_group_update(&ntype, node_group_update); nodeRegisterType(&ntype); @@ -53,7 +51,4 @@ void register_node_type_geo_custom_group(bNodeType *ntype) if (ntype->insert_link == nullptr) { ntype->insert_link = node_insert_link_default; } - if (ntype->update_internal_links == nullptr) { - ntype->update_internal_links = node_update_internal_links_default; - } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc index 4377d32210d..11bb8a61df5 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc @@ -28,12 +28,12 @@ # include "RBI_hull_api.h" #endif -namespace blender::nodes { +namespace blender::nodes::node_geo_convex_hull_cc { -static void geo_node_convex_hull_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_output<decl::Geometry>("Convex Hull"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_output<decl::Geometry>(N_("Convex Hull")); } using bke::GeometryInstanceGroup; @@ -169,10 +169,10 @@ static Mesh *compute_hull(const GeometrySet &geometry_set) span_count++; const PointCloudComponent *component = geometry_set.get_component_for_read<PointCloudComponent>(); - GVArray_Typed<float3> varray = component->attribute_get_for_read<float3>( + VArray<float3> varray = component->attribute_get_for_read<float3>( "position", ATTR_DOMAIN_POINT, {0, 0, 0}); - total_size += varray->size(); - positions_span = varray->get_internal_span(); + total_size += varray.size(); + positions_span = varray.get_internal_span(); } if (geometry_set.has_curve()) { @@ -200,18 +200,18 @@ static Mesh *compute_hull(const GeometrySet &geometry_set) if (geometry_set.has_mesh()) { const MeshComponent *component = geometry_set.get_component_for_read<MeshComponent>(); - GVArray_Typed<float3> varray = component->attribute_get_for_read<float3>( + VArray<float3> varray = component->attribute_get_for_read<float3>( "position", ATTR_DOMAIN_POINT, {0, 0, 0}); - varray->materialize(positions.as_mutable_span().slice(offset, varray.size())); + varray.materialize(positions.as_mutable_span().slice(offset, varray.size())); offset += varray.size(); } if (geometry_set.has_pointcloud()) { const PointCloudComponent *component = geometry_set.get_component_for_read<PointCloudComponent>(); - GVArray_Typed<float3> varray = component->attribute_get_for_read<float3>( + VArray<float3> varray = component->attribute_get_for_read<float3>( "position", ATTR_DOMAIN_POINT, {0, 0, 0}); - varray->materialize(positions.as_mutable_span().slice(offset, varray.size())); + varray.materialize(positions.as_mutable_span().slice(offset, varray.size())); offset += varray.size(); } @@ -235,16 +235,16 @@ static void read_positions(const GeometryComponent &component, Span<float4x4> transforms, Vector<float3> *r_coords) { - GVArray_Typed<float3> positions = component.attribute_get_for_read<float3>( + VArray<float3> positions = component.attribute_get_for_read<float3>( "position", ATTR_DOMAIN_POINT, {0, 0, 0}); /* NOTE: could use convex hull operation here to * cut out some vertices, before accumulating, * but can also be done by the user beforehand. */ - r_coords->reserve(r_coords->size() + positions.size() * transforms.size()); + r_coords->reserve(r_coords->size() + positions->size() * transforms.size()); for (const float4x4 &transform : transforms) { - for (const int i : positions.index_range()) { + for (const int i : positions->index_range()) { const float3 position = positions[i]; const float3 transformed_position = transform * position; r_coords->append(transformed_position); @@ -296,7 +296,7 @@ static Mesh *convex_hull_from_instances(const GeometrySet &geometry_set) #endif /* WITH_BULLET */ -static void geo_node_convex_hull_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); @@ -312,18 +312,20 @@ static void geo_node_convex_hull_exec(GeoNodeExecParams params) #else params.error_message_add(NodeWarningType::Error, TIP_("Disabled, Blender was compiled without Bullet")); - params.set_output("Convex Hull", geometry_set); + params.set_default_remaining_outputs(); #endif /* WITH_BULLET */ } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_convex_hull_cc void register_node_type_geo_convex_hull() { + namespace file_ns = blender::nodes::node_geo_convex_hull_cc; + static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_CONVEX_HULL, "Convex Hull", NODE_CLASS_GEOMETRY, 0); - ntype.declare = blender::nodes::geo_node_convex_hull_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_convex_hull_exec; + geo_node_type_base(&ntype, GEO_NODE_CONVEX_HULL, "Convex Hull", NODE_CLASS_GEOMETRY); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc new file mode 100644 index 00000000000..666100ffd7f --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc @@ -0,0 +1,147 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BKE_spline.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_curve_endpoint_selection_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Int>(N_("Start Size")) + .min(0) + .default_value(1) + .supports_field() + .description(N_("The amount of points to select from the start of each spline")); + b.add_input<decl::Int>(N_("End Size")) + .min(0) + .default_value(1) + .supports_field() + .description(N_("The amount of points to select from the end of each spline")); + b.add_output<decl::Bool>(N_("Selection")) + .field_source() + .description( + N_("The selection from the start and end of the splines based on the input sizes")); +} + +static void select_by_spline(const int start, const int end, MutableSpan<bool> r_selection) +{ + const int size = r_selection.size(); + const int start_use = std::min(start, size); + const int end_use = std::min(end, size); + + r_selection.slice(0, start_use).fill(true); + r_selection.slice(size - end_use, end_use).fill(true); +} + +class EndpointFieldInput final : public GeometryFieldInput { + Field<int> start_size_; + Field<int> end_size_; + + public: + EndpointFieldInput(Field<int> start_size, Field<int> end_size) + : GeometryFieldInput(CPPType::get<bool>(), "Endpoint Selection node"), + start_size_(start_size), + end_size_(end_size) + { + category_ = Category::Generated; + } + + GVArray get_varray_for_context(const GeometryComponent &component, + const AttributeDomain domain, + IndexMask UNUSED(mask)) const final + { + if (component.type() != GEO_COMPONENT_TYPE_CURVE || domain != ATTR_DOMAIN_POINT) { + return nullptr; + } + + const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); + const CurveEval *curve = curve_component.get_for_read(); + + Array<int> control_point_offsets = curve->control_point_offsets(); + + if (curve == nullptr || control_point_offsets.last() == 0) { + return nullptr; + } + + GeometryComponentFieldContext size_context{curve_component, ATTR_DOMAIN_CURVE}; + fn::FieldEvaluator evaluator{size_context, curve->splines().size()}; + evaluator.add(start_size_); + evaluator.add(end_size_); + evaluator.evaluate(); + const VArray<int> &start_size = evaluator.get_evaluated<int>(0); + const VArray<int> &end_size = evaluator.get_evaluated<int>(1); + + const int point_size = control_point_offsets.last(); + Array<bool> selection(point_size, false); + int current_point = 0; + MutableSpan<bool> selection_span = selection.as_mutable_span(); + for (int i : IndexRange(curve->splines().size())) { + const SplinePtr &spline = curve->splines()[i]; + if (start_size[i] <= 0 && end_size[i] <= 0) { + selection_span.slice(current_point, spline->size()).fill(false); + } + else { + int start_use = std::max(start_size[i], 0); + int end_use = std::max(end_size[i], 0); + select_by_spline(start_use, end_use, selection_span.slice(current_point, spline->size())); + } + current_point += spline->size(); + } + return VArray<bool>::ForContainer(std::move(selection)); + }; + + uint64_t hash() const override + { + return get_default_hash_2(start_size_, end_size_); + } + + bool is_equal_to(const fn::FieldNode &other) const override + { + if (const EndpointFieldInput *other_endpoint = dynamic_cast<const EndpointFieldInput *>( + &other)) { + return start_size_ == other_endpoint->start_size_ && end_size_ == other_endpoint->end_size_; + } + return false; + } +}; + +static void node_geo_exec(GeoNodeExecParams params) +{ + Field<int> start_size = params.extract_input<Field<int>>("Start Size"); + Field<int> end_size = params.extract_input<Field<int>>("End Size"); + Field<bool> selection_field{std::make_shared<EndpointFieldInput>(start_size, end_size)}; + params.set_output("Selection", std::move(selection_field)); +} +} // namespace blender::nodes::node_geo_curve_endpoint_selection_cc + +void register_node_type_geo_curve_endpoint_selection() +{ + namespace file_ns = blender::nodes::node_geo_curve_endpoint_selection_cc; + + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_CURVE_ENDPOINT_SELECTION, "Endpoint Selection", NODE_CLASS_INPUT); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc index c30741cf786..929d9046f98 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc @@ -16,7 +16,7 @@ #include "BLI_array.hh" #include "BLI_delaunay_2d.h" -#include "BLI_double2.hh" +#include "BLI_math_vec_types.hh" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" @@ -31,23 +31,24 @@ #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_curve_fill_cc { -static void geo_node_curve_fill_declare(NodeDeclarationBuilder &b) +NODE_STORAGE_FUNCS(NodeGeometryCurveFill) + +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Curve"); - b.add_output<decl::Geometry>("Mesh"); + b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_output<decl::Geometry>(N_("Mesh")); } -static void geo_node_curve_fill_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); } -static void geo_node_curve_fill_init(bNodeTree *UNUSED(ntree), bNode *node) +static void node_init(bNodeTree *UNUSED(ntree), bNode *node) { - NodeGeometryCurveFill *data = (NodeGeometryCurveFill *)MEM_callocN(sizeof(NodeGeometryCurveFill), - __func__); + NodeGeometryCurveFill *data = MEM_cnew<NodeGeometryCurveFill>(__func__); data->mode = GEO_NODE_CURVE_FILL_MODE_TRIANGULATED; node->storage = data; @@ -147,11 +148,11 @@ static void curve_fill_calculate(GeometrySet &geometry_set, const GeometryNodeCu geometry_set.replace_curve(nullptr); } -static void geo_node_curve_fill_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); - const NodeGeometryCurveFill &storage = *(const NodeGeometryCurveFill *)params.node().storage; + const NodeGeometryCurveFill &storage = node_storage(params.node()); const GeometryNodeCurveFillMode mode = (GeometryNodeCurveFillMode)storage.mode; geometry_set.modify_geometry_sets( @@ -160,19 +161,21 @@ static void geo_node_curve_fill_exec(GeoNodeExecParams params) params.set_output("Mesh", std::move(geometry_set)); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_curve_fill_cc void register_node_type_geo_curve_fill() { + namespace file_ns = blender::nodes::node_geo_curve_fill_cc; + static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_CURVE_FILL, "Curve Fill", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base(&ntype, GEO_NODE_FILL_CURVE, "Fill Curve", NODE_CLASS_GEOMETRY); - node_type_init(&ntype, blender::nodes::geo_node_curve_fill_init); + node_type_init(&ntype, file_ns::node_init); node_type_storage( &ntype, "NodeGeometryCurveFill", node_free_standard_storage, node_copy_standard_storage); - ntype.declare = blender::nodes::geo_node_curve_fill_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_curve_fill_exec; - ntype.draw_buttons = blender::nodes::geo_node_curve_fill_layout; + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.draw_buttons = file_ns::node_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc index 67ce20efd9d..68b609f8045 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc @@ -25,30 +25,37 @@ #include "BKE_spline.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_curve_fillet_cc { -static void geo_node_curve_fillet_declare(NodeDeclarationBuilder &b) +NODE_STORAGE_FUNCS(NodeGeometryCurveFillet) + +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Curve"); - b.add_input<decl::Int>("Count").default_value(1).min(1).max(1000); - b.add_input<decl::Float>("Radius") + b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input<decl::Int>(N_("Count")) + .default_value(1) + .min(1) + .max(1000) + .supports_field() + .make_available([](bNode &node) { node_storage(node).mode = GEO_NODE_CURVE_FILLET_POLY; }); + b.add_input<decl::Float>(N_("Radius")) .min(0.0f) .max(FLT_MAX) .subtype(PropertySubType::PROP_DISTANCE) - .default_value(0.25f); - b.add_input<decl::Bool>("Limit Radius"); - b.add_output<decl::Geometry>("Curve"); + .default_value(0.25f) + .supports_field(); + b.add_input<decl::Bool>(N_("Limit Radius")); + b.add_output<decl::Geometry>(N_("Curve")); } -static void geo_node_curve_fillet_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); } -static void geo_node_curve_fillet_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeGeometryCurveFillet *data = (NodeGeometryCurveFillet *)MEM_callocN( - sizeof(NodeGeometryCurveFillet), __func__); + NodeGeometryCurveFillet *data = MEM_cnew<NodeGeometryCurveFillet>(__func__); data->mode = GEO_NODE_CURVE_FILLET_BEZIER; node->storage = data; @@ -58,10 +65,10 @@ struct FilletParam { GeometryNodeCurveFilletMode mode; /* Number of points to be added. */ - const VArray<int> *counts; + VArray<int> counts; /* Radii for fillet arc at all vertices. */ - const VArray<float> *radii; + VArray<float> radii; /* Whether or not fillets are allowed to overlap. */ bool limit_radius; @@ -75,14 +82,14 @@ struct FilletData { Array<int> counts; }; -static void geo_node_curve_fillet_update(bNodeTree *UNUSED(ntree), bNode *node) +static void node_update(bNodeTree *ntree, bNode *node) { - NodeGeometryCurveFillet &node_storage = *(NodeGeometryCurveFillet *)node->storage; - const GeometryNodeCurveFilletMode mode = (GeometryNodeCurveFilletMode)node_storage.mode; + const NodeGeometryCurveFillet &storage = node_storage(*node); + const GeometryNodeCurveFilletMode mode = (GeometryNodeCurveFilletMode)storage.mode; bNodeSocket *poly_socket = ((bNodeSocket *)node->inputs.first)->next; - nodeSetSocketAvailability(poly_socket, mode == GEO_NODE_CURVE_FILLET_POLY); + nodeSetSocketAvailability(ntree, poly_socket, mode == GEO_NODE_CURVE_FILLET_POLY); } /* Function to get the center of a fillet. */ @@ -115,9 +122,9 @@ static Array<float3> calculate_directions(const Span<float3> positions) Array<float3> directions(size); for (const int i : IndexRange(size - 1)) { - directions[i] = (positions[i + 1] - positions[i]).normalized(); + directions[i] = math::normalize(positions[i + 1] - positions[i]); } - directions[size - 1] = (positions[0] - positions[size - 1]).normalized(); + directions[size - 1] = math::normalize(positions[0] - positions[size - 1]); return directions; } @@ -128,9 +135,9 @@ static Array<float3> calculate_axes(const Span<float3> directions) const int size = directions.size(); Array<float3> axes(size); - axes[0] = float3::cross(-directions[size - 1], directions[0]).normalized(); + axes[0] = math::normalize(math::cross(-directions[size - 1], directions[0])); for (const int i : IndexRange(1, size - 1)) { - axes[i] = float3::cross(-directions[i - 1], directions[i]).normalized(); + axes[i] = math::normalize(math::cross(-directions[i - 1], directions[i])); } return axes; @@ -159,7 +166,7 @@ static Array<int> calculate_counts(const FilletParam &fillet_param, Array<int> counts(size, 1); if (fillet_param.mode == GEO_NODE_CURVE_FILLET_POLY) { for (const int i : IndexRange(size)) { - counts[i] = (*fillet_param.counts)[spline_offset + i]; + counts[i] = fillet_param.counts[spline_offset + i]; } } if (!cyclic) { @@ -177,12 +184,12 @@ static Array<float> calculate_radii(const FilletParam &fillet_param, Array<float> radii(size, 0.0f); if (fillet_param.limit_radius) { for (const int i : IndexRange(size)) { - radii[i] = std::max((*fillet_param.radii)[spline_offset + i], 0.0f); + radii[i] = std::max(fillet_param.radii[spline_offset + i], 0.0f); } } else { for (const int i : IndexRange(size)) { - radii[i] = (*fillet_param.radii)[spline_offset + i]; + radii[i] = fillet_param.radii[spline_offset + i]; } } @@ -241,8 +248,8 @@ static void limit_radii(FilletData &fd, const bool cyclic) if (cyclic) { /* Calculate lengths between adjacent control points. */ - const float len_prev = float3::distance(positions[0], positions[size - 1]); - const float len_next = float3::distance(positions[0], positions[1]); + const float len_prev = math::distance(positions[0], positions[size - 1]); + const float len_next = math::distance(positions[0], positions[1]); /* Calculate tangent lengths of fillets in control points. */ const float tan_len = radii[0] * tan(angles[0] / 2.0f); @@ -264,16 +271,16 @@ static void limit_radii(FilletData &fd, const bool cyclic) } /* Initialize max_radii to largest possible radii. */ - float prev_dist = float3::distance(positions[1], positions[0]); + float prev_dist = math::distance(positions[1], positions[0]); for (const int i : IndexRange(1, size - 2)) { - const float temp_dist = float3::distance(positions[i], positions[i + 1]); + const float temp_dist = math::distance(positions[i], positions[i + 1]); max_radii[i] = std::min(prev_dist, temp_dist) / tan(angles[i] / 2.0f); prev_dist = temp_dist; } /* Max radii calculations for each index. */ for (const int i : IndexRange(start, fillet_count - 1)) { - const float len_next = float3::distance(positions[i], positions[i + 1]); + const float len_next = math::distance(positions[i], positions[i + 1]); const float tan_len = radii[i] * tan(angles[i] / 2.0f); const float tan_len_next = radii[i + 1] * tan(angles[i + 1] / 2.0f); @@ -331,14 +338,17 @@ static void copy_common_attributes_by_mapping(const Spline &src, copy_attribute_by_mapping(src.radii(), dst.radii(), mapping); copy_attribute_by_mapping(src.tilts(), dst.tilts(), mapping); - dst.attributes.reallocate(1); src.attributes.foreach_attribute( [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { std::optional<GSpan> src_attribute = src.attributes.get_for_read(attribute_id); if (dst.attributes.create(attribute_id, meta_data.data_type)) { std::optional<GMutableSpan> dst_attribute = dst.attributes.get_for_write(attribute_id); if (dst_attribute) { - src_attribute->type().copy_assign(src_attribute->data(), dst_attribute->data()); + attribute_math::convert_to_static_type(dst_attribute->type(), [&](auto dummy) { + using T = decltype(dummy); + copy_attribute_by_mapping( + src_attribute->typed<T>(), dst_attribute->typed<T>(), mapping); + }); return true; } } @@ -405,7 +415,8 @@ static void update_bezier_positions(const FilletData &fd, const float3 center = get_center(dst_spline.positions()[i_dst] - positions[i_src], fd, i_src); /* Calculate the vector of the radius formed by the first vertex. */ float3 radius_vec = dst_spline.positions()[i_dst] - center; - const float radius = radius_vec.normalize_and_get_length(); + float radius; + radius_vec = math::normalize_and_get_length(radius_vec, radius); dst_spline.handle_types_right().slice(1, count - 2).fill(BezierSpline::HandleType::Align); dst_spline.handle_types_left().slice(1, count - 2).fill(BezierSpline::HandleType::Align); @@ -589,13 +600,13 @@ static void calculate_curve_fillet(GeometrySet &geometry_set, field_evaluator.evaluate(); - fillet_param.radii = &field_evaluator.get_evaluated<float>(0); - if (fillet_param.radii->is_single() && fillet_param.radii->get_internal_single() < 0.0f) { + fillet_param.radii = field_evaluator.get_evaluated<float>(0); + if (fillet_param.radii.is_single() && fillet_param.radii.get_internal_single() < 0.0f) { return; } if (mode == GEO_NODE_CURVE_FILLET_POLY) { - fillet_param.counts = &field_evaluator.get_evaluated<int>(1); + fillet_param.counts = field_evaluator.get_evaluated<int>(1); } fillet_param.limit_radius = limit_radius; @@ -606,12 +617,12 @@ static void calculate_curve_fillet(GeometrySet &geometry_set, geometry_set.replace_curve(output_curve.release()); } -static void geo_node_fillet_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); - NodeGeometryCurveFillet &node_storage = *(NodeGeometryCurveFillet *)params.node().storage; - const GeometryNodeCurveFilletMode mode = (GeometryNodeCurveFilletMode)node_storage.mode; + const NodeGeometryCurveFillet &storage = node_storage(params.node()); + const GeometryNodeCurveFilletMode mode = (GeometryNodeCurveFilletMode)storage.mode; Field<float> radius_field = params.extract_input<Field<float>>("Radius"); const bool limit_radius = params.extract_input<bool>("Limit Radius"); @@ -628,19 +639,21 @@ static void geo_node_fillet_exec(GeoNodeExecParams params) params.set_output("Curve", std::move(geometry_set)); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_curve_fillet_cc void register_node_type_geo_curve_fillet() { + namespace file_ns = blender::nodes::node_geo_curve_fillet_cc; + static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_CURVE_FILLET, "Curve Fillet", NODE_CLASS_GEOMETRY, 0); - ntype.draw_buttons = blender::nodes::geo_node_curve_fillet_layout; + geo_node_type_base(&ntype, GEO_NODE_FILLET_CURVE, "Fillet Curve", NODE_CLASS_GEOMETRY); + ntype.draw_buttons = file_ns::node_layout; node_type_storage( &ntype, "NodeGeometryCurveFillet", node_free_standard_storage, node_copy_standard_storage); - ntype.declare = blender::nodes::geo_node_curve_fillet_declare; - node_type_init(&ntype, blender::nodes::geo_node_curve_fillet_init); - node_type_update(&ntype, blender::nodes::geo_node_curve_fillet_update); - ntype.geometry_node_execute = blender::nodes::geo_node_fillet_exec; + ntype.declare = file_ns::node_declare; + node_type_init(&ntype, file_ns::node_init); + node_type_update(&ntype, file_ns::node_update); + ntype.geometry_node_execute = file_ns::node_geo_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc new file mode 100644 index 00000000000..e4e87e519f7 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc @@ -0,0 +1,169 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BKE_spline.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_curve_handle_type_selection_cc { + +NODE_STORAGE_FUNCS(NodeGeometryCurveSelectHandles) + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_output<decl::Bool>(N_("Selection")).field_source(); +} + +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); + uiItemR(layout, ptr, "handle_type", 0, "", ICON_NONE); +} + +static void node_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryCurveSelectHandles *data = MEM_cnew<NodeGeometryCurveSelectHandles>(__func__); + + data->handle_type = GEO_NODE_CURVE_HANDLE_AUTO; + data->mode = GEO_NODE_CURVE_HANDLE_LEFT | GEO_NODE_CURVE_HANDLE_RIGHT; + node->storage = data; +} + +static BezierSpline::HandleType handle_type_from_input_type(const GeometryNodeCurveHandleType type) +{ + switch (type) { + case GEO_NODE_CURVE_HANDLE_AUTO: + return BezierSpline::HandleType::Auto; + case GEO_NODE_CURVE_HANDLE_ALIGN: + return BezierSpline::HandleType::Align; + case GEO_NODE_CURVE_HANDLE_FREE: + return BezierSpline::HandleType::Free; + case GEO_NODE_CURVE_HANDLE_VECTOR: + return BezierSpline::HandleType::Vector; + } + BLI_assert_unreachable(); + return BezierSpline::HandleType::Auto; +} + +static void select_by_handle_type(const CurveEval &curve, + const BezierSpline::HandleType type, + const GeometryNodeCurveHandleMode mode, + const MutableSpan<bool> r_selection) +{ + int offset = 0; + for (const SplinePtr &spline : curve.splines()) { + if (spline->type() != Spline::Type::Bezier) { + r_selection.slice(offset, spline->size()).fill(false); + offset += spline->size(); + } + else { + BezierSpline *b = static_cast<BezierSpline *>(spline.get()); + for (int i : IndexRange(b->size())) { + r_selection[offset++] = (mode & GEO_NODE_CURVE_HANDLE_LEFT && + b->handle_types_left()[i] == type) || + (mode & GEO_NODE_CURVE_HANDLE_RIGHT && + b->handle_types_right()[i] == type); + } + } + } +} + +class HandleTypeFieldInput final : public GeometryFieldInput { + BezierSpline::HandleType type_; + GeometryNodeCurveHandleMode mode_; + + public: + HandleTypeFieldInput(BezierSpline::HandleType type, GeometryNodeCurveHandleMode mode) + : GeometryFieldInput(CPPType::get<bool>(), "Handle Type Selection node"), + type_(type), + mode_(mode) + { + category_ = Category::Generated; + } + + GVArray get_varray_for_context(const GeometryComponent &component, + const AttributeDomain domain, + IndexMask mask) const final + { + if (component.type() != GEO_COMPONENT_TYPE_CURVE) { + return {}; + } + + const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); + const CurveEval *curve = curve_component.get_for_read(); + if (curve == nullptr) { + return {}; + } + + if (domain == ATTR_DOMAIN_POINT) { + Array<bool> selection(mask.min_array_size()); + select_by_handle_type(*curve, type_, mode_, selection); + return VArray<bool>::ForContainer(std::move(selection)); + } + return {}; + } + + uint64_t hash() const override + { + return get_default_hash_2((int)mode_, (int)type_); + } + + bool is_equal_to(const fn::FieldNode &other) const override + { + return dynamic_cast<const HandleTypeFieldInput *>(&other) != nullptr; + if (const HandleTypeFieldInput *other_handle_selection = + dynamic_cast<const HandleTypeFieldInput *>(&other)) { + return mode_ == other_handle_selection->mode_ && type_ == other_handle_selection->type_; + } + return false; + } +}; + +static void node_geo_exec(GeoNodeExecParams params) +{ + const NodeGeometryCurveSelectHandles &storage = node_storage(params.node()); + const BezierSpline::HandleType handle_type = handle_type_from_input_type( + (GeometryNodeCurveHandleType)storage.handle_type); + const GeometryNodeCurveHandleMode mode = (GeometryNodeCurveHandleMode)storage.mode; + + Field<bool> selection_field{std::make_shared<HandleTypeFieldInput>(handle_type, mode)}; + params.set_output("Selection", std::move(selection_field)); +} + +} // namespace blender::nodes::node_geo_curve_handle_type_selection_cc + +void register_node_type_geo_curve_handle_type_selection() +{ + namespace file_ns = blender::nodes::node_geo_curve_handle_type_selection_cc; + + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_CURVE_HANDLE_TYPE_SELECTION, "Handle Type Selection", NODE_CLASS_INPUT); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + node_type_init(&ntype, file_ns::node_init); + node_type_storage(&ntype, + "NodeGeometryCurveSelectHandles", + node_free_standard_storage, + node_copy_standard_storage); + ntype.draw_buttons = file_ns::node_layout; + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc index ac7df35bb72..21ae88a6852 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc @@ -17,19 +17,19 @@ #include "BKE_spline.hh" #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_curve_length_cc { -static void geo_node_curve_length_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Curve"); - b.add_output<decl::Float>("Length"); + b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_output<decl::Float>(N_("Length")); } -static void geo_node_curve_length_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { GeometrySet curve_set = params.extract_input<GeometrySet>("Curve"); if (!curve_set.has_curve()) { - params.set_output("Length", 0.0f); + params.set_default_remaining_outputs(); return; } const CurveEval &curve = *curve_set.get_curve_for_read(); @@ -40,14 +40,16 @@ static void geo_node_curve_length_exec(GeoNodeExecParams params) params.set_output("Length", length); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_curve_length_cc void register_node_type_geo_curve_length() { + namespace file_ns = blender::nodes::node_geo_curve_length_cc; + static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_CURVE_LENGTH, "Curve Length", NODE_CLASS_GEOMETRY, 0); - ntype.declare = blender::nodes::geo_node_curve_length_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_curve_length_exec; + geo_node_type_base(&ntype, GEO_NODE_CURVE_LENGTH, "Curve Length", NODE_CLASS_GEOMETRY); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_parameter.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_parameter.cc deleted file mode 100644 index 90853387ec7..00000000000 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_parameter.cc +++ /dev/null @@ -1,206 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include "BLI_task.hh" - -#include "BKE_spline.hh" - -#include "node_geometry_util.hh" - -namespace blender::nodes { - -static void geo_node_curve_parameter_declare(NodeDeclarationBuilder &b) -{ - b.add_output<decl::Float>("Factor").field_source(); -} - -/** - * A basic interpolation from the point domain to the spline domain would be useless, since the - * average parameter for each spline would just be 0.5, or close to it. Instead, the parameter for - * each spline is the portion of the total length at the start of the spline. - */ -static Array<float> curve_parameter_spline_domain(const CurveEval &curve, const IndexMask mask) -{ - Span<SplinePtr> splines = curve.splines(); - float length = 0.0f; - Array<float> parameters(splines.size()); - for (const int i : splines.index_range()) { - parameters[i] = length; - length += splines[i]->length(); - } - const float total_length_inverse = length == 0.0f ? 0.0f : 1.0f / length; - mask.foreach_index([&](const int64_t i) { parameters[i] *= total_length_inverse; }); - - return parameters; -} - -/** - * The parameter at each control point is the factor at the corresponding evaluated point. - */ -static void calculate_bezier_parameters(const BezierSpline &spline, MutableSpan<float> parameters) -{ - Span<int> offsets = spline.control_point_offsets(); - Span<float> lengths = spline.evaluated_lengths(); - const float total_length = spline.length(); - const float total_length_inverse = total_length == 0.0f ? 0.0f : 1.0f / total_length; - - for (const int i : IndexRange(1, spline.size() - 1)) { - parameters[i] = lengths[offsets[i] - 1] * total_length_inverse; - } -} - -/** - * The parameter for poly splines is simply the evaluated lengths divided by the total length. - */ -static void calculate_poly_parameters(const PolySpline &spline, MutableSpan<float> parameters) -{ - Span<float> lengths = spline.evaluated_lengths(); - const float total_length = spline.length(); - const float total_length_inverse = total_length == 0.0f ? 0.0f : 1.0f / total_length; - - for (const int i : IndexRange(1, spline.size() - 1)) { - parameters[i] = lengths[i - 1] * total_length_inverse; - } -} - -/** - * Since NURBS control points do not necessarily coincide with the evaluated curve's path, and - * each control point doesn't correspond well to a specific evaluated point, the parameter at - * each point is not well defined. So instead, treat the control points as if they were a poly - * spline. - */ -static void calculate_nurbs_parameters(const NURBSpline &spline, MutableSpan<float> parameters) -{ - Span<float3> positions = spline.positions(); - Array<float> control_point_lengths(spline.size()); - - float length = 0.0f; - for (const int i : IndexRange(positions.size() - 1)) { - parameters[i] = length; - length += float3::distance(positions[i], positions[i + 1]); - } - - const float total_length_inverse = length == 0.0f ? 0.0f : 1.0f / length; - for (float ¶meter : parameters) { - parameter *= total_length_inverse; - } -} - -static Array<float> curve_parameter_point_domain(const CurveEval &curve) -{ - Span<SplinePtr> splines = curve.splines(); - Array<int> offsets = curve.control_point_offsets(); - const int total_size = offsets.last(); - Array<float> parameters(total_size); - - threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) { - for (const int i : range) { - const Spline &spline = *splines[i]; - MutableSpan spline_factors{parameters.as_mutable_span().slice(offsets[i], spline.size())}; - spline_factors.first() = 0.0f; - switch (splines[i]->type()) { - case Spline::Type::Bezier: { - calculate_bezier_parameters(static_cast<const BezierSpline &>(spline), spline_factors); - break; - } - case Spline::Type::Poly: { - calculate_poly_parameters(static_cast<const PolySpline &>(spline), spline_factors); - break; - } - case Spline::Type::NURBS: { - calculate_nurbs_parameters(static_cast<const NURBSpline &>(spline), spline_factors); - break; - } - } - } - }); - return parameters; -} - -static const GVArray *construct_curve_parameter_gvarray(const CurveEval &curve, - const IndexMask mask, - const AttributeDomain domain, - ResourceScope &scope) -{ - if (domain == ATTR_DOMAIN_POINT) { - Array<float> parameters = curve_parameter_point_domain(curve); - return &scope.construct<fn::GVArray_For_ArrayContainer<Array<float>>>(std::move(parameters)); - } - - if (domain == ATTR_DOMAIN_CURVE) { - Array<float> parameters = curve_parameter_spline_domain(curve, mask); - return &scope.construct<fn::GVArray_For_ArrayContainer<Array<float>>>(std::move(parameters)); - } - - return nullptr; -} - -class CurveParameterFieldInput final : public fn::FieldInput { - public: - CurveParameterFieldInput() : fn::FieldInput(CPPType::get<float>(), "Curve Parameter") - { - } - - const GVArray *get_varray_for_context(const fn::FieldContext &context, - IndexMask mask, - ResourceScope &scope) const final - { - if (const GeometryComponentFieldContext *geometry_context = - dynamic_cast<const GeometryComponentFieldContext *>(&context)) { - - const GeometryComponent &component = geometry_context->geometry_component(); - const AttributeDomain domain = geometry_context->domain(); - - if (component.type() == GEO_COMPONENT_TYPE_CURVE) { - const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); - const CurveEval *curve = curve_component.get_for_read(); - if (curve) { - return construct_curve_parameter_gvarray(*curve, mask, domain, scope); - } - } - } - return nullptr; - } - - uint64_t hash() const override - { - /* Some random constant hash. */ - return 29837456298; - } - - bool is_equal_to(const fn::FieldNode &other) const override - { - return dynamic_cast<const CurveParameterFieldInput *>(&other) != nullptr; - } -}; - -static void geo_node_curve_parameter_exec(GeoNodeExecParams params) -{ - Field<float> parameter_field{std::make_shared<CurveParameterFieldInput>()}; - params.set_output("Factor", std::move(parameter_field)); -} - -} // namespace blender::nodes - -void register_node_type_geo_curve_parameter() -{ - static bNodeType ntype; - - geo_node_type_base(&ntype, GEO_NODE_CURVE_PARAMETER, "Curve Parameter", NODE_CLASS_INPUT, 0); - ntype.geometry_node_execute = blender::nodes::geo_node_curve_parameter_exec; - ntype.declare = blender::nodes::geo_node_curve_parameter_declare; - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_arc.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_arc.cc new file mode 100644 index 00000000000..3f6298168a2 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_arc.cc @@ -0,0 +1,391 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BKE_spline.hh" +#include "BLI_math_base_safe.h" +#include "UI_interface.h" +#include "UI_resources.h" +#include "node_geometry_util.hh" +#include <numeric> + +namespace blender::nodes::node_geo_curve_primitive_arc_cc { + +NODE_STORAGE_FUNCS(NodeGeometryCurvePrimitiveArc) + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Int>(N_("Resolution")) + .default_value(16) + .min(2) + .max(256) + .subtype(PROP_UNSIGNED) + .description(N_("The number of points on the arc")); + b.add_input<decl::Vector>(N_("Start")) + .default_value({-1.0f, 0.0f, 0.0f}) + .subtype(PROP_TRANSLATION) + .description(N_("Position of the first control point")); + b.add_input<decl::Vector>(N_("Middle")) + .default_value({0.0f, 2.0f, 0.0f}) + .subtype(PROP_TRANSLATION) + .description(N_("Position of the middle control point")); + b.add_input<decl::Vector>(N_("End")) + .default_value({1.0f, 0.0f, 0.0f}) + .subtype(PROP_TRANSLATION) + .description(N_("Position of the last control point")); + b.add_input<decl::Float>(N_("Radius")) + .default_value(1.0f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description(N_("Distance of the points from the origin")); + b.add_input<decl::Float>(N_("Start Angle")) + .default_value(0.0f) + .subtype(PROP_ANGLE) + .description(N_("Starting angle of the arc")); + b.add_input<decl::Float>(N_("Sweep Angle")) + .default_value(1.75f * M_PI) + .min(-2 * M_PI) + .max(2 * M_PI) + .subtype(PROP_ANGLE) + .description(N_("Length of the arc")); + b.add_input<decl::Float>(N_("Offset Angle")) + .default_value(0.0f) + .subtype(PROP_ANGLE) + .description(N_("Offset angle of the arc")); + b.add_input<decl::Bool>(N_("Connect Center")) + .default_value(false) + .description(N_("Connect the arc at the center")); + b.add_input<decl::Bool>(N_("Invert Arc")) + .default_value(false) + .description(N_("Invert and draw opposite arc")); + + b.add_output<decl::Geometry>(N_("Curve")); + b.add_output<decl::Vector>(N_("Center")) + .description(N_("The center of the circle described by the three points")) + .make_available( + [](bNode &node) { node_storage(node).mode = GEO_NODE_CURVE_PRIMITIVE_ARC_TYPE_POINTS; }); + b.add_output<decl::Vector>(N_("Normal")) + .description(N_("The normal direction of the plane described by the three points, pointing " + "towards the positive Z axis")) + .make_available( + [](bNode &node) { node_storage(node).mode = GEO_NODE_CURVE_PRIMITIVE_ARC_TYPE_POINTS; }); + b.add_output<decl::Float>(N_("Radius")) + .description(N_("The radius of the circle described by the three points")) + .make_available( + [](bNode &node) { node_storage(node).mode = GEO_NODE_CURVE_PRIMITIVE_ARC_TYPE_POINTS; }); +} + +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); +} + +static void node_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryCurvePrimitiveArc *data = MEM_cnew<NodeGeometryCurvePrimitiveArc>(__func__); + + data->mode = GEO_NODE_CURVE_PRIMITIVE_ARC_TYPE_RADIUS; + node->storage = data; +} + +static void node_update(bNodeTree *ntree, bNode *node) +{ + const NodeGeometryCurvePrimitiveArc &storage = node_storage(*node); + const GeometryNodeCurvePrimitiveArcMode mode = (GeometryNodeCurvePrimitiveArcMode)storage.mode; + + bNodeSocket *start_socket = ((bNodeSocket *)node->inputs.first)->next; + bNodeSocket *middle_socket = start_socket->next; + bNodeSocket *end_socket = middle_socket->next; + + bNodeSocket *radius_socket = end_socket->next; + bNodeSocket *start_angle_socket = radius_socket->next; + bNodeSocket *sweep_angle_socket = start_angle_socket->next; + + bNodeSocket *offset_angle_socket = sweep_angle_socket->next; + + bNodeSocket *center_out_socket = ((bNodeSocket *)node->outputs.first)->next; + bNodeSocket *normal_out_socket = center_out_socket->next; + bNodeSocket *radius_out_socket = normal_out_socket->next; + + const bool radius_mode = (mode == GEO_NODE_CURVE_PRIMITIVE_ARC_TYPE_RADIUS); + const bool points_mode = (mode == GEO_NODE_CURVE_PRIMITIVE_ARC_TYPE_POINTS); + + nodeSetSocketAvailability(ntree, start_socket, points_mode); + nodeSetSocketAvailability(ntree, middle_socket, points_mode); + nodeSetSocketAvailability(ntree, end_socket, points_mode); + + nodeSetSocketAvailability(ntree, radius_socket, radius_mode); + nodeSetSocketAvailability(ntree, start_angle_socket, radius_mode); + nodeSetSocketAvailability(ntree, sweep_angle_socket, radius_mode); + + nodeSetSocketAvailability(ntree, offset_angle_socket, points_mode); + + nodeSetSocketAvailability(ntree, center_out_socket, points_mode); + nodeSetSocketAvailability(ntree, normal_out_socket, points_mode); + nodeSetSocketAvailability(ntree, radius_out_socket, points_mode); +} + +static float3 rotate_vector_around_axis(const float3 vector, const float3 axis, const float angle) +{ + float3 result = vector; + float mat[3][3]; + axis_angle_to_mat3(mat, axis, angle); + mul_m3_v3(mat, result); + return result; +} + +static bool colinear_f3_f3_f3(const float3 p1, const float3 p2, const float3 p3) +{ + const float3 a = math::normalize(p2 - p1); + const float3 b = math::normalize(p3 - p1); + return (ELEM(a, b, b * -1.0f)); +} + +static std::unique_ptr<CurveEval> create_arc_curve_from_points(const int resolution, + const float3 a, + const float3 b, + const float3 c, + float angle_offset, + const bool connect_center, + const bool invert_arc, + float3 &r_center, + float3 &r_normal, + float &r_radius) +{ + std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>(); + std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); + + if (connect_center) { + spline->resize(resolution + 1); + } + else { + spline->resize(resolution); + } + + const int stepcount = resolution - 1; + const int centerpoint = resolution; + MutableSpan<float3> positions = spline->positions(); + spline->radii().fill(1.0f); + spline->tilts().fill(0.0f); + + const bool is_colinear = colinear_f3_f3_f3(a, b, c); + + float3 center; + float3 normal; + float radius; + const float3 mid_ac = math::midpoint(a, c); + normal_tri_v3(normal, a, c, b); + + if (is_colinear || a == c || a == b || b == c || resolution == 2) { + /* If colinear, generate a point line between points. */ + float3 p1, p2; + + /* Find the two points that are furthest away from each other. */ + const float ab = math::distance_squared(a, b); + const float ac = math::distance_squared(a, c); + const float bc = math::distance_squared(b, c); + if (ab > ac && ab > bc) { + p1 = a; + p2 = b; + } + else if (bc > ab && bc > ac) { + p1 = b; + p2 = c; + } + else { + p1 = a; + p2 = c; + } + + const float step = 1.0f / stepcount; + for (const int i : IndexRange(resolution)) { + const float factor = step * i; + positions[i] = math::interpolate(p1, p2, factor); + } + center = mid_ac; + radius = 0.0f; + } + else { + /* Midpoints of `A->B` and `B->C`. */ + const float3 mid_ab = math::midpoint(a, b); + const float3 mid_bc = math::midpoint(c, b); + + /* Normalized vectors of `A->B` and `B->C`. */ + const float3 nba = math::normalize(b - a); + const float3 ncb = math::normalize(c - b); + + /* Normal of plane of main 2 segments A->B and `B->C`. */ + const float3 nabc = math::normalize(math::cross(nba, ncb)); + + /* Determine center point from the intersection of 3 planes. */ + float plane_1[4], plane_2[4], plane_3[4]; + plane_from_point_normal_v3(plane_1, mid_ab, nabc); + plane_from_point_normal_v3(plane_2, mid_ab, nba); + plane_from_point_normal_v3(plane_3, mid_bc, ncb); + + /* If the 3 planes do not intersect at one point, just return empty geometry. */ + if (!isect_plane_plane_plane_v3(plane_1, plane_2, plane_3, center)) { + r_center = mid_ac; + r_normal = normal; + r_radius = 0.0f; + return nullptr; + } + + /* Radial vectors. */ + const float3 rad_a = math::normalize(a - center); + const float3 rad_b = math::normalize(b - center); + const float3 rad_c = math::normalize(c - center); + + /* Calculate angles. */ + radius = math::distance(center, b); + float angle_ab = angle_signed_on_axis_v3v3_v3(rad_a, rad_b, normal) + 2.0f * M_PI; + float angle_ac = angle_signed_on_axis_v3v3_v3(rad_a, rad_c, normal) + 2.0f * M_PI; + float angle = (angle_ac > angle_ab) ? angle_ac : angle_ab; + angle -= 2.0f * M_PI; + if (invert_arc) { + angle = -(2.0f * M_PI - angle); + } + + /* Create arc. */ + const float step = angle / stepcount; + for (const int i : IndexRange(resolution)) { + const float factor = step * i + angle_offset; + float3 out = rotate_vector_around_axis(rad_a, -normal, factor); + positions[i] = out * radius + center; + } + } + + if (connect_center) { + spline->set_cyclic(true); + positions[centerpoint] = center; + } + + /* Ensure normal is relative to Z-up. */ + if (math::dot(float3(0, 0, 1), normal) < 0) { + normal = -normal; + } + + curve->add_spline(std::move(spline)); + curve->attributes.reallocate(curve->splines().size()); + r_center = center; + r_radius = radius; + r_normal = normal; + return curve; +} + +static std::unique_ptr<CurveEval> create_arc_curve_from_radius(const int resolution, + const float radius, + const float start_angle, + const float sweep_angle, + const bool connect_center, + const bool invert_arc) +{ + std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>(); + std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); + + if (connect_center) { + spline->resize(resolution + 1); + } + else { + spline->resize(resolution); + } + + const int stepcount = resolution - 1; + const int centerpoint = resolution; + MutableSpan<float3> positions = spline->positions(); + spline->radii().fill(1.0f); + spline->tilts().fill(0.0f); + + const float sweep = (invert_arc) ? -(2.0f * M_PI - sweep_angle) : sweep_angle; + + const float theta_step = sweep / float(stepcount); + for (const int i : IndexRange(resolution)) { + const float theta = theta_step * i + start_angle; + const float x = radius * cos(theta); + const float y = radius * sin(theta); + positions[i] = float3(x, y, 0.0f); + } + + if (connect_center) { + spline->set_cyclic(true); + positions[centerpoint] = float3(0.0f, 0.0f, 0.0f); + } + + curve->add_spline(std::move(spline)); + curve->attributes.reallocate(curve->splines().size()); + return curve; +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + const NodeGeometryCurvePrimitiveArc &storage = node_storage(params.node()); + + const GeometryNodeCurvePrimitiveArcMode mode = (GeometryNodeCurvePrimitiveArcMode)storage.mode; + + switch (mode) { + case GEO_NODE_CURVE_PRIMITIVE_ARC_TYPE_POINTS: { + std::unique_ptr<CurveEval> curve; + float3 r_center, r_normal; + float r_radius; + curve = create_arc_curve_from_points(std::max(params.extract_input<int>("Resolution"), 2), + params.extract_input<float3>("Start"), + params.extract_input<float3>("Middle"), + params.extract_input<float3>("End"), + params.extract_input<float>("Offset Angle"), + params.extract_input<bool>("Connect Center"), + params.extract_input<bool>("Invert Arc"), + r_center, + r_normal, + r_radius); + params.set_output("Curve", GeometrySet::create_with_curve(curve.release())); + params.set_output("Center", r_center); + params.set_output("Normal", r_normal); + params.set_output("Radius", r_radius); + break; + } + case GEO_NODE_CURVE_PRIMITIVE_ARC_TYPE_RADIUS: { + std::unique_ptr<CurveEval> curve; + curve = create_arc_curve_from_radius(std::max(params.extract_input<int>("Resolution"), 2), + params.extract_input<float>("Radius"), + params.extract_input<float>("Start Angle"), + params.extract_input<float>("Sweep Angle"), + params.extract_input<bool>("Connect Center"), + params.extract_input<bool>("Invert Arc")); + + params.set_output("Curve", GeometrySet::create_with_curve(curve.release())); + break; + } + } +} + +} // namespace blender::nodes::node_geo_curve_primitive_arc_cc + +void register_node_type_geo_curve_primitive_arc() +{ + namespace file_ns = blender::nodes::node_geo_curve_primitive_arc_cc; + + static bNodeType ntype; + geo_node_type_base(&ntype, GEO_NODE_CURVE_PRIMITIVE_ARC, "Arc", NODE_CLASS_GEOMETRY); + node_type_init(&ntype, file_ns::node_init); + node_type_update(&ntype, file_ns::node_update); + node_type_storage(&ntype, + "NodeGeometryCurvePrimitiveArc", + node_free_standard_storage, + node_copy_standard_storage); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.draw_buttons = file_ns::node_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc index 313473e3442..7d84ddf9917 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc @@ -21,31 +21,49 @@ #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_curve_primitive_bezier_segment_cc { -static void geo_node_curve_primitive_bezier_segment_declare(NodeDeclarationBuilder &b) +NODE_STORAGE_FUNCS(NodeGeometryCurvePrimitiveBezierSegment) + +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Int>("Resolution").default_value(16).min(1).max(256).subtype(PROP_UNSIGNED); - b.add_input<decl::Vector>("Start").default_value({-1.0f, 0.0f, 0.0f}).subtype(PROP_TRANSLATION); - b.add_input<decl::Vector>("Start Handle") + b.add_input<decl::Int>(N_("Resolution")) + .default_value(16) + .min(1) + .max(256) + .subtype(PROP_UNSIGNED) + .description(N_("The number of evaluated points on the curve")); + b.add_input<decl::Vector>(N_("Start")) + .default_value({-1.0f, 0.0f, 0.0f}) + .subtype(PROP_TRANSLATION) + .description(N_("Position of the start control point of the curve")); + b.add_input<decl::Vector>(N_("Start Handle")) .default_value({-0.5f, 0.5f, 0.0f}) - .subtype(PROP_TRANSLATION); - b.add_input<decl::Vector>("End Handle").subtype(PROP_TRANSLATION); - b.add_input<decl::Vector>("End").default_value({1.0f, 0.0f, 0.0f}).subtype(PROP_TRANSLATION); - b.add_output<decl::Geometry>("Curve"); + .subtype(PROP_TRANSLATION) + .description( + N_("Position of the start handle used to define the shape of the curve. In Offset mode, " + "relative to Start point")); + b.add_input<decl::Vector>(N_("End Handle")) + .subtype(PROP_TRANSLATION) + .description( + N_("Position of the end handle used to define the shape of the curve. In Offset mode, " + "relative to End point")); + b.add_input<decl::Vector>(N_("End")) + .default_value({1.0f, 0.0f, 0.0f}) + .subtype(PROP_TRANSLATION) + .description(N_("Position of the end control point of the curve")); + b.add_output<decl::Geometry>(N_("Curve")); } -static void geo_node_curve_primitive_bezier_segment_layout(uiLayout *layout, - bContext *UNUSED(C), - PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); } -static void geo_node_curve_primitive_bezier_segment_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeGeometryCurvePrimitiveBezierSegment *data = (NodeGeometryCurvePrimitiveBezierSegment *) - MEM_callocN(sizeof(NodeGeometryCurvePrimitiveBezierSegment), __func__); + NodeGeometryCurvePrimitiveBezierSegment *data = + MEM_cnew<NodeGeometryCurvePrimitiveBezierSegment>(__func__); data->mode = GEO_NODE_CURVE_PRIMITIVE_BEZIER_SEGMENT_POSITION; node->storage = data; @@ -61,53 +79,37 @@ static std::unique_ptr<CurveEval> create_bezier_segment_curve( { std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>(); std::unique_ptr<BezierSpline> spline = std::make_unique<BezierSpline>(); + spline->set_resolution(resolution); + + spline->resize(2); + MutableSpan<float3> positions = spline->positions(); + spline->handle_types_left().fill(BezierSpline::HandleType::Align); + spline->handle_types_right().fill(BezierSpline::HandleType::Align); + spline->radii().fill(1.0f); + spline->tilts().fill(0.0f); + + positions.first() = start; + positions.last() = end; if (mode == GEO_NODE_CURVE_PRIMITIVE_BEZIER_SEGMENT_POSITION) { - spline->add_point(start, - BezierSpline::HandleType::Align, - 2.0f * start - start_handle_right, - BezierSpline::HandleType::Align, - start_handle_right, - 1.0f, - 0.0f); - spline->add_point(end, - BezierSpline::HandleType::Align, - end_handle_left, - BezierSpline::HandleType::Align, - 2.0f * end - end_handle_left, - 1.0f, - 0.0f); + spline->set_handle_position_right(0, start_handle_right); + spline->set_handle_position_left(1, end_handle_left); } else { - spline->add_point(start, - BezierSpline::HandleType::Align, - start - start_handle_right, - BezierSpline::HandleType::Align, - start + start_handle_right, - 1.0f, - 0.0f); - spline->add_point(end, - BezierSpline::HandleType::Align, - end + end_handle_left, - BezierSpline::HandleType::Align, - end - end_handle_left, - 1.0f, - 0.0f); + spline->set_handle_position_right(0, start + start_handle_right); + spline->set_handle_position_left(1, end + end_handle_left); } - spline->set_resolution(resolution); - spline->attributes.reallocate(spline->size()); curve->add_spline(std::move(spline)); - curve->attributes.reallocate(curve->splines().size()); + curve->attributes.reallocate(1); return curve; } -static void geo_node_curve_primitive_bezier_segment_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { - const NodeGeometryCurvePrimitiveBezierSegment *node_storage = - (NodeGeometryCurvePrimitiveBezierSegment *)params.node().storage; + const NodeGeometryCurvePrimitiveBezierSegment &storage = node_storage(params.node()); const GeometryNodeCurvePrimitiveBezierSegmentMode mode = - (const GeometryNodeCurvePrimitiveBezierSegmentMode)node_storage->mode; + (const GeometryNodeCurvePrimitiveBezierSegmentMode)storage.mode; std::unique_ptr<CurveEval> curve = create_bezier_segment_curve( params.extract_input<float3>("Start"), @@ -119,20 +121,22 @@ static void geo_node_curve_primitive_bezier_segment_exec(GeoNodeExecParams param params.set_output("Curve", GeometrySet::create_with_curve(curve.release())); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_curve_primitive_bezier_segment_cc void register_node_type_geo_curve_primitive_bezier_segment() { + namespace file_ns = blender::nodes::node_geo_curve_primitive_bezier_segment_cc; + static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_CURVE_PRIMITIVE_BEZIER_SEGMENT, "Bezier Segment", NODE_CLASS_GEOMETRY, 0); - node_type_init(&ntype, blender::nodes::geo_node_curve_primitive_bezier_segment_init); + &ntype, GEO_NODE_CURVE_PRIMITIVE_BEZIER_SEGMENT, "Bezier Segment", NODE_CLASS_GEOMETRY); + node_type_init(&ntype, file_ns::node_init); node_type_storage(&ntype, "NodeGeometryCurvePrimitiveBezierSegment", node_free_standard_storage, node_copy_standard_storage); - ntype.declare = blender::nodes::geo_node_curve_primitive_bezier_segment_declare; - ntype.draw_buttons = blender::nodes::geo_node_curve_primitive_bezier_segment_layout; - ntype.geometry_node_execute = blender::nodes::geo_node_curve_primitive_bezier_segment_exec; + ntype.declare = file_ns::node_declare; + ntype.draw_buttons = file_ns::node_layout; + ntype.geometry_node_execute = file_ns::node_geo_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc index f5eb83ea4fd..7b5d1a1dc80 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc @@ -21,43 +21,64 @@ #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_curve_primitive_circle_cc { -static void geo_node_curve_primitive_circle_declare(NodeDeclarationBuilder &b) +NODE_STORAGE_FUNCS(NodeGeometryCurvePrimitiveCircle) + +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Int>("Resolution").default_value(32).min(3).max(512); - b.add_input<decl::Vector>("Point 1") + b.add_input<decl::Int>(N_("Resolution")) + .default_value(32) + .min(3) + .max(512) + .description(N_("Number of points on the circle")); + b.add_input<decl::Vector>(N_("Point 1")) .default_value({-1.0f, 0.0f, 0.0f}) - .subtype(PROP_TRANSLATION); - b.add_input<decl::Vector>("Point 2").default_value({0.0f, 1.0f, 0.0f}).subtype(PROP_TRANSLATION); - b.add_input<decl::Vector>("Point 3").default_value({1.0f, 0.0f, 0.0f}).subtype(PROP_TRANSLATION); - b.add_input<decl::Float>("Radius").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_output<decl::Geometry>("Curve"); - b.add_output<decl::Vector>("Center"); + .subtype(PROP_TRANSLATION) + .description( + N_("One of the three points on the circle. The point order determines the circle's " + "direction")); + b.add_input<decl::Vector>(N_("Point 2")) + .default_value({0.0f, 1.0f, 0.0f}) + .subtype(PROP_TRANSLATION) + .description( + N_("One of the three points on the circle. The point order determines the circle's " + "direction")); + b.add_input<decl::Vector>(N_("Point 3")) + .default_value({1.0f, 0.0f, 0.0f}) + .subtype(PROP_TRANSLATION) + .description( + N_("One of the three points on the circle. The point order determines the circle's " + "direction")); + b.add_input<decl::Float>(N_("Radius")) + .default_value(1.0f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description(N_("Distance of the points from the origin")); + b.add_output<decl::Geometry>(N_("Curve")); + b.add_output<decl::Vector>(N_("Center")).make_available([](bNode &node) { + node_storage(node).mode = GEO_NODE_CURVE_PRIMITIVE_CIRCLE_TYPE_POINTS; + }); } -static void geo_node_curve_primitive_circle_layout(uiLayout *layout, - bContext *UNUSED(C), - PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); } -static void geo_node_curve_primitive_circle_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeGeometryCurvePrimitiveCircle *data = (NodeGeometryCurvePrimitiveCircle *)MEM_callocN( - sizeof(NodeGeometryCurvePrimitiveCircle), __func__); + NodeGeometryCurvePrimitiveCircle *data = MEM_cnew<NodeGeometryCurvePrimitiveCircle>(__func__); data->mode = GEO_NODE_CURVE_PRIMITIVE_CIRCLE_TYPE_RADIUS; node->storage = data; } -static void geo_node_curve_primitive_circle_update(bNodeTree *UNUSED(ntree), bNode *node) +static void node_update(bNodeTree *ntree, bNode *node) { - const NodeGeometryCurvePrimitiveCircle *node_storage = (NodeGeometryCurvePrimitiveCircle *) - node->storage; - const GeometryNodeCurvePrimitiveCircleMode mode = (const GeometryNodeCurvePrimitiveCircleMode) - node_storage->mode; + const NodeGeometryCurvePrimitiveCircle &storage = node_storage(*node); + const GeometryNodeCurvePrimitiveCircleMode mode = (GeometryNodeCurvePrimitiveCircleMode) + storage.mode; bNodeSocket *start_socket = ((bNodeSocket *)node->inputs.first)->next; bNodeSocket *middle_socket = start_socket->next; @@ -66,18 +87,23 @@ static void geo_node_curve_primitive_circle_update(bNodeTree *UNUSED(ntree), bNo bNodeSocket *center_socket = ((bNodeSocket *)node->outputs.first)->next; - nodeSetSocketAvailability(start_socket, mode == GEO_NODE_CURVE_PRIMITIVE_CIRCLE_TYPE_POINTS); - nodeSetSocketAvailability(middle_socket, mode == GEO_NODE_CURVE_PRIMITIVE_CIRCLE_TYPE_POINTS); - nodeSetSocketAvailability(end_socket, mode == GEO_NODE_CURVE_PRIMITIVE_CIRCLE_TYPE_POINTS); - nodeSetSocketAvailability(center_socket, mode == GEO_NODE_CURVE_PRIMITIVE_CIRCLE_TYPE_POINTS); - nodeSetSocketAvailability(radius_socket, mode == GEO_NODE_CURVE_PRIMITIVE_CIRCLE_TYPE_RADIUS); + nodeSetSocketAvailability( + ntree, start_socket, mode == GEO_NODE_CURVE_PRIMITIVE_CIRCLE_TYPE_POINTS); + nodeSetSocketAvailability( + ntree, middle_socket, mode == GEO_NODE_CURVE_PRIMITIVE_CIRCLE_TYPE_POINTS); + nodeSetSocketAvailability( + ntree, end_socket, mode == GEO_NODE_CURVE_PRIMITIVE_CIRCLE_TYPE_POINTS); + nodeSetSocketAvailability( + ntree, center_socket, mode == GEO_NODE_CURVE_PRIMITIVE_CIRCLE_TYPE_POINTS); + nodeSetSocketAvailability( + ntree, radius_socket, mode == GEO_NODE_CURVE_PRIMITIVE_CIRCLE_TYPE_RADIUS); } static bool colinear_f3_f3_f3(const float3 p1, const float3 p2, const float3 p3) { - const float3 a = (p2 - p1).normalized(); - const float3 b = (p3 - p1).normalized(); - return (a == b || a == b * -1.0f); + const float3 a = math::normalize(p2 - p1); + const float3 b = math::normalize(p3 - p1); + return (ELEM(a, b, b * -1.0f)); } static std::unique_ptr<CurveEval> create_point_circle_curve( @@ -96,18 +122,18 @@ static std::unique_ptr<CurveEval> create_point_circle_curve( float3 center; /* Midpoints of `P1->P2` and `P2->P3`. */ - const float3 q1 = float3::interpolate(p1, p2, 0.5f); - const float3 q2 = float3::interpolate(p2, p3, 0.5f); + const float3 q1 = math::interpolate(p1, p2, 0.5f); + const float3 q2 = math::interpolate(p2, p3, 0.5f); /* Normal Vectors of `P1->P2` and `P2->P3` */ - const float3 v1 = (p2 - p1).normalized(); - const float3 v2 = (p3 - p2).normalized(); + const float3 v1 = math::normalize(p2 - p1); + const float3 v2 = math::normalize(p3 - p2); /* Normal of plane of main 2 segments P1->P2 and `P2->P3`. */ - const float3 v3 = float3::cross(v1, v2).normalized(); + const float3 v3 = math::normalize(math::cross(v1, v2)); /* Normal of plane of first perpendicular bisector and `P1->P2`. */ - const float3 v4 = float3::cross(v3, v1).normalized(); + const float3 v4 = math::normalize(math::cross(v3, v1)); /* Determine Center-point from the intersection of 3 planes. */ float plane_1[4], plane_2[4], plane_3[4]; @@ -122,7 +148,7 @@ static std::unique_ptr<CurveEval> create_point_circle_curve( } /* Get the radius from the center-point to p1. */ - const float r = float3::distance(p1, center); + const float r = math::distance(p1, center); const float theta_step = ((2 * M_PI) / (float)resolution); for (const int i : IndexRange(resolution)) { @@ -132,7 +158,7 @@ static std::unique_ptr<CurveEval> create_point_circle_curve( */ const float theta = theta_step * i; - positions[i] = center + r * cos(theta) * v1 + r * sin(theta) * v4; + positions[i] = center + r * sin(theta) * v1 + r * cos(theta) * v4; } spline->radii().fill(1.0f); @@ -169,13 +195,11 @@ static std::unique_ptr<CurveEval> create_radius_circle_curve(const int resolutio return curve; } -static void geo_node_curve_primitive_circle_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { - const NodeGeometryCurvePrimitiveCircle *node_storage = - (NodeGeometryCurvePrimitiveCircle *)params.node().storage; - + const NodeGeometryCurvePrimitiveCircle &storage = node_storage(params.node()); const GeometryNodeCurvePrimitiveCircleMode mode = (GeometryNodeCurvePrimitiveCircleMode) - node_storage->mode; + storage.mode; std::unique_ptr<CurveEval> curve; if (mode == GEO_NODE_CURVE_PRIMITIVE_CIRCLE_TYPE_POINTS) { @@ -196,26 +220,27 @@ static void geo_node_curve_primitive_circle_exec(GeoNodeExecParams params) params.set_output("Curve", GeometrySet::create_with_curve(curve.release())); } else { - params.set_output("Curve", GeometrySet()); + params.set_default_remaining_outputs(); } } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_curve_primitive_circle_cc void register_node_type_geo_curve_primitive_circle() { + namespace file_ns = blender::nodes::node_geo_curve_primitive_circle_cc; + static bNodeType ntype; - geo_node_type_base( - &ntype, GEO_NODE_CURVE_PRIMITIVE_CIRCLE, "Curve Circle", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base(&ntype, GEO_NODE_CURVE_PRIMITIVE_CIRCLE, "Curve Circle", NODE_CLASS_GEOMETRY); - node_type_init(&ntype, blender::nodes::geo_node_curve_primitive_circle_init); - node_type_update(&ntype, blender::nodes::geo_node_curve_primitive_circle_update); + node_type_init(&ntype, file_ns::node_init); + node_type_update(&ntype, file_ns::node_update); node_type_storage(&ntype, "NodeGeometryCurvePrimitiveCircle", node_free_standard_storage, node_copy_standard_storage); - ntype.declare = blender::nodes::geo_node_curve_primitive_circle_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_curve_primitive_circle_exec; - ntype.draw_buttons = blender::nodes::geo_node_curve_primitive_circle_layout; + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.draw_buttons = file_ns::node_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc index a3d2ada612f..d35fa0a2fdc 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc @@ -21,48 +21,57 @@ #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_curve_primitive_line_cc { -static void geo_node_curve_primitive_line_declare(NodeDeclarationBuilder &b) +NODE_STORAGE_FUNCS(NodeGeometryCurvePrimitiveLine) + +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Vector>("Start").subtype(PROP_TRANSLATION); - b.add_input<decl::Vector>("End").default_value({0.0f, 0.0f, 1.0f}).subtype(PROP_TRANSLATION); - b.add_input<decl::Vector>("Direction").default_value({0.0f, 0.0f, 1.0f}); - b.add_input<decl::Float>("Length").default_value(1.0f).subtype(PROP_DISTANCE); - b.add_output<decl::Geometry>("Curve"); + b.add_input<decl::Vector>(N_("Start")) + .subtype(PROP_TRANSLATION) + .description(N_("Position of the first control point")); + b.add_input<decl::Vector>(N_("End")) + .default_value({0.0f, 0.0f, 1.0f}) + .subtype(PROP_TRANSLATION) + .description(N_("Position of the second control point")); + b.add_input<decl::Vector>(N_("Direction")) + .default_value({0.0f, 0.0f, 1.0f}) + .description( + N_("Direction the line is going in. The length of this vector does not matter")); + b.add_input<decl::Float>(N_("Length")) + .default_value(1.0f) + .subtype(PROP_DISTANCE) + .description(N_("Distance between the two points")); + b.add_output<decl::Geometry>(N_("Curve")); } -static void geo_node_curve_primitive_line_layout(uiLayout *layout, - bContext *UNUSED(C), - PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); } -static void geo_node_curve_primitive_line_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeGeometryCurvePrimitiveLine *data = (NodeGeometryCurvePrimitiveLine *)MEM_callocN( - sizeof(NodeGeometryCurvePrimitiveLine), __func__); + NodeGeometryCurvePrimitiveLine *data = MEM_cnew<NodeGeometryCurvePrimitiveLine>(__func__); data->mode = GEO_NODE_CURVE_PRIMITIVE_LINE_MODE_POINTS; node->storage = data; } -static void geo_node_curve_primitive_line_update(bNodeTree *UNUSED(ntree), bNode *node) +static void node_update(bNodeTree *ntree, bNode *node) { - const NodeGeometryCurvePrimitiveLine *node_storage = (NodeGeometryCurvePrimitiveLine *) - node->storage; - const GeometryNodeCurvePrimitiveLineMode mode = (const GeometryNodeCurvePrimitiveLineMode) - node_storage->mode; + const NodeGeometryCurvePrimitiveLine &storage = node_storage(*node); + const GeometryNodeCurvePrimitiveLineMode mode = (GeometryNodeCurvePrimitiveLineMode)storage.mode; bNodeSocket *p2_socket = ((bNodeSocket *)node->inputs.first)->next; bNodeSocket *direction_socket = p2_socket->next; bNodeSocket *length_socket = direction_socket->next; - nodeSetSocketAvailability(p2_socket, mode == GEO_NODE_CURVE_PRIMITIVE_LINE_MODE_POINTS); - nodeSetSocketAvailability(direction_socket, - mode == GEO_NODE_CURVE_PRIMITIVE_LINE_MODE_DIRECTION); - nodeSetSocketAvailability(length_socket, mode == GEO_NODE_CURVE_PRIMITIVE_LINE_MODE_DIRECTION); + nodeSetSocketAvailability(ntree, p2_socket, mode == GEO_NODE_CURVE_PRIMITIVE_LINE_MODE_POINTS); + nodeSetSocketAvailability( + ntree, direction_socket, mode == GEO_NODE_CURVE_PRIMITIVE_LINE_MODE_DIRECTION); + nodeSetSocketAvailability( + ntree, length_socket, mode == GEO_NODE_CURVE_PRIMITIVE_LINE_MODE_DIRECTION); } static std::unique_ptr<CurveEval> create_point_line_curve(const float3 start, const float3 end) @@ -91,7 +100,7 @@ static std::unique_ptr<CurveEval> create_direction_line_curve(const float3 start spline->resize(2); MutableSpan<float3> positions = spline->positions(); positions[0] = start; - positions[1] = direction.normalized() * length + start; + positions[1] = math::normalize(direction) * length + start; spline->radii().fill(1.0f); spline->tilts().fill(0.0f); @@ -100,13 +109,10 @@ static std::unique_ptr<CurveEval> create_direction_line_curve(const float3 start return curve; } -static void geo_node_curve_primitive_line_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { - - const NodeGeometryCurvePrimitiveLine *node_storage = - (NodeGeometryCurvePrimitiveLine *)params.node().storage; - - GeometryNodeCurvePrimitiveLineMode mode = (GeometryNodeCurvePrimitiveLineMode)node_storage->mode; + const NodeGeometryCurvePrimitiveLine &storage = node_storage(params.node()); + const GeometryNodeCurvePrimitiveLineMode mode = (GeometryNodeCurvePrimitiveLineMode)storage.mode; std::unique_ptr<CurveEval> curve; if (mode == GEO_NODE_CURVE_PRIMITIVE_LINE_MODE_POINTS) { @@ -122,20 +128,22 @@ static void geo_node_curve_primitive_line_exec(GeoNodeExecParams params) params.set_output("Curve", GeometrySet::create_with_curve(curve.release())); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_curve_primitive_line_cc void register_node_type_geo_curve_primitive_line() { + namespace file_ns = blender::nodes::node_geo_curve_primitive_line_cc; + static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_CURVE_PRIMITIVE_LINE, "Curve Line", NODE_CLASS_GEOMETRY, 0); - node_type_init(&ntype, blender::nodes::geo_node_curve_primitive_line_init); - node_type_update(&ntype, blender::nodes::geo_node_curve_primitive_line_update); + geo_node_type_base(&ntype, GEO_NODE_CURVE_PRIMITIVE_LINE, "Curve Line", NODE_CLASS_GEOMETRY); + node_type_init(&ntype, file_ns::node_init); + node_type_update(&ntype, file_ns::node_update); node_type_storage(&ntype, "NodeGeometryCurvePrimitiveLine", node_free_standard_storage, node_copy_standard_storage); - ntype.declare = blender::nodes::geo_node_curve_primitive_line_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_curve_primitive_line_exec; - ntype.draw_buttons = blender::nodes::geo_node_curve_primitive_line_layout; + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.draw_buttons = file_ns::node_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadratic_bezier.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadratic_bezier.cc index a54fd971ac4..885d92a111b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadratic_bezier.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadratic_bezier.cc @@ -17,15 +17,29 @@ #include "BKE_spline.hh" #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_curve_primitive_quadratic_bezier_cc { -static void geo_node_curve_primitive_quadratic_bezier_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Int>("Resolution").default_value(16).min(3).max(256).subtype(PROP_UNSIGNED); - b.add_input<decl::Vector>("Start").default_value({-1.0f, 0.0f, 0.0f}).subtype(PROP_TRANSLATION); - b.add_input<decl::Vector>("Middle").default_value({0.0f, 2.0f, 0.0f}).subtype(PROP_TRANSLATION); - b.add_input<decl::Vector>("End").default_value({1.0f, 0.0f, 0.0f}).subtype(PROP_TRANSLATION); - b.add_output<decl::Geometry>("Curve"); + b.add_input<decl::Int>(N_("Resolution")) + .default_value(16) + .min(3) + .max(256) + .subtype(PROP_UNSIGNED) + .description(N_("The number of edges on the curve")); + b.add_input<decl::Vector>(N_("Start")) + .default_value({-1.0f, 0.0f, 0.0f}) + .subtype(PROP_TRANSLATION) + .description(N_("Position of the first control point")); + b.add_input<decl::Vector>(N_("Middle")) + .default_value({0.0f, 2.0f, 0.0f}) + .subtype(PROP_TRANSLATION) + .description(N_("Position of the middle control point")); + b.add_input<decl::Vector>(N_("End")) + .default_value({1.0f, 0.0f, 0.0f}) + .subtype(PROP_TRANSLATION) + .description(N_("Position of the last control point")); + b.add_output<decl::Geometry>(N_("Curve")); } static std::unique_ptr<CurveEval> create_quadratic_bezier_curve(const float3 p1, @@ -36,21 +50,25 @@ static std::unique_ptr<CurveEval> create_quadratic_bezier_curve(const float3 p1, std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>(); std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); + spline->resize(resolution + 1); + MutableSpan<float3> positions = spline->positions(); + spline->radii().fill(1.0f); + spline->tilts().fill(0.0f); + const float step = 1.0f / resolution; - for (int i : IndexRange(resolution + 1)) { + for (const int i : IndexRange(resolution + 1)) { const float factor = step * i; - const float3 q1 = float3::interpolate(p1, p2, factor); - const float3 q2 = float3::interpolate(p2, p3, factor); - const float3 out = float3::interpolate(q1, q2, factor); - spline->add_point(out, 1.0f, 0.0f); + const float3 q1 = math::interpolate(p1, p2, factor); + const float3 q2 = math::interpolate(p2, p3, factor); + positions[i] = math::interpolate(q1, q2, factor); } - spline->attributes.reallocate(spline->size()); + curve->add_spline(std::move(spline)); curve->attributes.reallocate(curve->splines().size()); return curve; } -static void geo_node_curve_primitive_quadratic_bezier_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { std::unique_ptr<CurveEval> curve = create_quadratic_bezier_curve( params.extract_input<float3>("Start"), @@ -60,17 +78,16 @@ static void geo_node_curve_primitive_quadratic_bezier_exec(GeoNodeExecParams par params.set_output("Curve", GeometrySet::create_with_curve(curve.release())); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_curve_primitive_quadratic_bezier_cc void register_node_type_geo_curve_primitive_quadratic_bezier() { + namespace file_ns = blender::nodes::node_geo_curve_primitive_quadratic_bezier_cc; + static bNodeType ntype; - geo_node_type_base(&ntype, - GEO_NODE_CURVE_PRIMITIVE_QUADRATIC_BEZIER, - "Quadratic Bezier", - NODE_CLASS_GEOMETRY, - 0); - ntype.declare = blender::nodes::geo_node_curve_primitive_quadratic_bezier_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_curve_primitive_quadratic_bezier_exec; + geo_node_type_base( + &ntype, GEO_NODE_CURVE_PRIMITIVE_QUADRATIC_BEZIER, "Quadratic Bezier", NODE_CLASS_GEOMETRY); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc index 07ddaa8f61e..8ec42cb1c45 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc @@ -17,46 +17,88 @@ #include "BKE_spline.hh" #include "UI_interface.h" #include "UI_resources.h" + +#include "NOD_socket_search_link.hh" + #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_curve_primitive_quadrilateral_cc { -static void geo_node_curve_primitive_quadrilateral_declare(NodeDeclarationBuilder &b) +NODE_STORAGE_FUNCS(NodeGeometryCurvePrimitiveQuad) + +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Float>("Width").default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input<decl::Float>("Height").default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input<decl::Float>("Bottom Width").default_value(4.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input<decl::Float>("Top Width").default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input<decl::Float>("Offset").default_value(1.0f).subtype(PROP_DISTANCE); - b.add_input<decl::Float>("Bottom Height").default_value(3.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input<decl::Float>("Top Height").default_value(1.0f).subtype(PROP_DISTANCE); - b.add_input<decl::Vector>("Point 1").default_value({-1.0f, -1.0f, 0.0f}).subtype(PROP_DISTANCE); - b.add_input<decl::Vector>("Point 2").default_value({1.0f, -1.0f, 0.0f}).subtype(PROP_DISTANCE); - b.add_input<decl::Vector>("Point 3").default_value({1.0f, 1.0f, 0.0f}).subtype(PROP_DISTANCE); - b.add_input<decl::Vector>("Point 4").default_value({-1.0f, 1.0f, 0.0f}).subtype(PROP_DISTANCE); - b.add_output<decl::Geometry>("Curve"); + b.add_input<decl::Float>(N_("Width")) + .default_value(2.0f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description(N_("The X axis size of the shape")); + b.add_input<decl::Float>(N_("Height")) + .default_value(2.0f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description(N_("The Y axis size of the shape")); + b.add_input<decl::Float>(N_("Bottom Width")) + .default_value(4.0f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description(N_("The X axis size of the shape")); + b.add_input<decl::Float>(N_("Top Width")) + .default_value(2.0f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description(N_("The X axis size of the shape")); + b.add_input<decl::Float>(N_("Offset")) + .default_value(1.0f) + .subtype(PROP_DISTANCE) + .description( + N_("For Parallelogram, the relative X difference between the top and bottom edges. For " + "Trapezoid, the amount to move the top edge in the positive X axis")); + b.add_input<decl::Float>(N_("Bottom Height")) + .default_value(3.0f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description(N_("The distance between the bottom point and the X axis")); + b.add_input<decl::Float>(N_("Top Height")) + .default_value(1.0f) + .subtype(PROP_DISTANCE) + .description(N_("The distance between the top point and the X axis")); + b.add_input<decl::Vector>(N_("Point 1")) + .default_value({-1.0f, -1.0f, 0.0f}) + .subtype(PROP_DISTANCE) + .description(N_("The exact location of the point to use")); + b.add_input<decl::Vector>(N_("Point 2")) + .default_value({1.0f, -1.0f, 0.0f}) + .subtype(PROP_DISTANCE) + .description(N_("The exact location of the point to use")); + b.add_input<decl::Vector>(N_("Point 3")) + .default_value({1.0f, 1.0f, 0.0f}) + .subtype(PROP_DISTANCE) + .description(N_("The exact location of the point to use")); + b.add_input<decl::Vector>(N_("Point 4")) + .default_value({-1.0f, 1.0f, 0.0f}) + .subtype(PROP_DISTANCE) + .description(N_("The exact location of the point to use")); + b.add_output<decl::Geometry>(N_("Curve")); } -static void geo_node_curve_primitive_quadrilateral_layout(uiLayout *layout, - bContext *UNUSED(C), - PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "mode", 0, "", ICON_NONE); } -static void geo_node_curve_primitive_quadrilateral_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeGeometryCurvePrimitiveQuad *data = (NodeGeometryCurvePrimitiveQuad *)MEM_callocN( - sizeof(NodeGeometryCurvePrimitiveQuad), __func__); + NodeGeometryCurvePrimitiveQuad *data = MEM_cnew<NodeGeometryCurvePrimitiveQuad>(__func__); data->mode = GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_RECTANGLE; node->storage = data; } -static void geo_node_curve_primitive_quadrilateral_update(bNodeTree *UNUSED(ntree), bNode *node) +static void node_update(bNodeTree *ntree, bNode *node) { - NodeGeometryCurvePrimitiveQuad &node_storage = *(NodeGeometryCurvePrimitiveQuad *)node->storage; + const NodeGeometryCurvePrimitiveQuad &storage = node_storage(*node); GeometryNodeCurvePrimitiveQuadMode mode = static_cast<GeometryNodeCurvePrimitiveQuadMode>( - node_storage.mode); + storage.mode); bNodeSocket *width = ((bNodeSocket *)node->inputs.first); bNodeSocket *height = width->next; @@ -70,35 +112,61 @@ static void geo_node_curve_primitive_quadrilateral_update(bNodeTree *UNUSED(ntre bNodeSocket *p3 = p2->next; bNodeSocket *p4 = p3->next; - LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { - nodeSetSocketAvailability(sock, false); - } + Vector<bNodeSocket *> available_sockets; if (mode == GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_RECTANGLE) { - nodeSetSocketAvailability(width, true); - nodeSetSocketAvailability(height, true); + available_sockets.extend({width, height}); } else if (mode == GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_PARALLELOGRAM) { - nodeSetSocketAvailability(width, true); - nodeSetSocketAvailability(height, true); - nodeSetSocketAvailability(offset, true); + available_sockets.extend({width, height, offset}); } else if (mode == GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_TRAPEZOID) { - nodeSetSocketAvailability(bottom, true); - nodeSetSocketAvailability(top, true); - nodeSetSocketAvailability(offset, true); - nodeSetSocketAvailability(height, true); + available_sockets.extend({bottom, top, offset, height}); } else if (mode == GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_KITE) { - nodeSetSocketAvailability(width, true); - nodeSetSocketAvailability(bottom_height, true); - nodeSetSocketAvailability(top_height, true); + available_sockets.extend({width, bottom_height, top_height}); } else if (mode == GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_POINTS) { - nodeSetSocketAvailability(p1, true); - nodeSetSocketAvailability(p2, true); - nodeSetSocketAvailability(p3, true); - nodeSetSocketAvailability(p4, true); + available_sockets.extend({p1, p2, p3, p4}); + } + + LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { + nodeSetSocketAvailability(ntree, sock, available_sockets.contains(sock)); + } +} + +class SocketSearchOp { + public: + std::string socket_name; + GeometryNodeCurvePrimitiveQuadMode quad_mode; + void operator()(LinkSearchOpParams ¶ms) + { + bNode &node = params.add_node("GeometryNodeCurvePrimitiveQuadrilateral"); + node_storage(node).mode = quad_mode; + params.update_and_connect_available_socket(node, socket_name); + } +}; + +static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) +{ + const NodeDeclaration &declaration = *params.node_type().fixed_declaration; + if (params.in_out() == SOCK_OUT) { + search_link_ops_for_declarations(params, declaration.outputs()); + } + else if (params.node_tree().typeinfo->validate_link( + static_cast<eNodeSocketDatatype>(params.other_socket().type), SOCK_FLOAT)) { + params.add_item(IFACE_("Width"), + SocketSearchOp{"Width", GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_RECTANGLE}); + params.add_item(IFACE_("Height"), + SocketSearchOp{"Height", GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_RECTANGLE}); + params.add_item(IFACE_("Bottom Width"), + SocketSearchOp{"Bottom Width", GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_TRAPEZOID}); + params.add_item(IFACE_("Top Width"), + SocketSearchOp{"Top Width", GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_TRAPEZOID}); + params.add_item(IFACE_("Offset"), + SocketSearchOp{"Offset", GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_PARALLELOGRAM}); + params.add_item(IFACE_("Point 1"), + SocketSearchOp{"Point 1", GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_POINTS}); } } @@ -157,12 +225,10 @@ static void create_kite_curve(MutableSpan<float3> positions, positions[3] = float3(-width / 2.0f, 0, 0); } -static void geo_node_curve_primitive_quadrilateral_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { - const NodeGeometryCurvePrimitiveQuad &node_storage = - *(NodeGeometryCurvePrimitiveQuad *)(params.node()).storage; - const GeometryNodeCurvePrimitiveQuadMode mode = static_cast<GeometryNodeCurvePrimitiveQuadMode>( - node_storage.mode); + const NodeGeometryCurvePrimitiveQuad &storage = node_storage(params.node()); + const GeometryNodeCurvePrimitiveQuadMode mode = (GeometryNodeCurvePrimitiveQuadMode)storage.mode; std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>(); std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); @@ -206,7 +272,7 @@ static void geo_node_curve_primitive_quadrilateral_exec(GeoNodeExecParams params params.extract_input<float3>("Point 4")); break; default: - params.set_output("Curve", GeometrySet()); + params.set_default_remaining_outputs(); return; } @@ -215,21 +281,24 @@ static void geo_node_curve_primitive_quadrilateral_exec(GeoNodeExecParams params params.set_output("Curve", GeometrySet::create_with_curve(curve.release())); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_curve_primitive_quadrilateral_cc void register_node_type_geo_curve_primitive_quadrilateral() { + namespace file_ns = blender::nodes::node_geo_curve_primitive_quadrilateral_cc; + static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_CURVE_PRIMITIVE_QUADRILATERAL, "Quadrilateral", NODE_CLASS_GEOMETRY, 0); - ntype.declare = blender::nodes::geo_node_curve_primitive_quadrilateral_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_curve_primitive_quadrilateral_exec; - ntype.draw_buttons = blender::nodes::geo_node_curve_primitive_quadrilateral_layout; - node_type_update(&ntype, blender::nodes::geo_node_curve_primitive_quadrilateral_update); - node_type_init(&ntype, blender::nodes::geo_node_curve_primitive_quadrilateral_init); + &ntype, GEO_NODE_CURVE_PRIMITIVE_QUADRILATERAL, "Quadrilateral", NODE_CLASS_GEOMETRY); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.draw_buttons = file_ns::node_layout; + node_type_update(&ntype, file_ns::node_update); + node_type_init(&ntype, file_ns::node_init); node_type_storage(&ntype, "NodeGeometryCurvePrimitiveQuad", node_free_standard_storage, node_copy_standard_storage); + ntype.gather_link_search_ops = file_ns::node_gather_link_searches; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc index 0803d43e5c3..6aba65b5638 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc @@ -18,17 +18,35 @@ #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_curve_primitive_spiral_cc { -static void geo_node_curve_primitive_spiral_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Int>("Resolution").default_value(32).min(1).max(1024).subtype(PROP_UNSIGNED); - b.add_input<decl::Float>("Rotations").default_value(2.0f).min(0.0f); - b.add_input<decl::Float>("Start Radius").default_value(1.0f).subtype(PROP_DISTANCE); - b.add_input<decl::Float>("End Radius").default_value(2.0f).subtype(PROP_DISTANCE); - b.add_input<decl::Float>("Height").default_value(2.0f).subtype(PROP_DISTANCE); - b.add_input<decl::Bool>("Reverse"); - b.add_output<decl::Geometry>("Curve"); + b.add_input<decl::Int>(N_("Resolution")) + .default_value(32) + .min(1) + .max(1024) + .subtype(PROP_UNSIGNED) + .description(N_("Number of points in one rotation of the spiral")); + b.add_input<decl::Float>(N_("Rotations")) + .default_value(2.0f) + .min(0.0f) + .description(N_("Number of times the spiral makes a full rotation")); + b.add_input<decl::Float>(N_("Start Radius")) + .default_value(1.0f) + .subtype(PROP_DISTANCE) + .description(N_("Horizontal Distance from the Z axis at the start of the spiral")); + b.add_input<decl::Float>(N_("End Radius")) + .default_value(2.0f) + .subtype(PROP_DISTANCE) + .description(N_("Horizontal Distance from the Z axis at the end of the spiral")); + b.add_input<decl::Float>(N_("Height")) + .default_value(2.0f) + .subtype(PROP_DISTANCE) + .description(N_("The height perpendicular to the base of the spiral")); + b.add_input<decl::Bool>(N_("Reverse")) + .description(N_("Switch the direction from clockwise to counterclockwise")); + b.add_output<decl::Geometry>(N_("Curve")); } static std::unique_ptr<CurveEval> create_spiral_curve(const float rotations, @@ -43,39 +61,35 @@ static std::unique_ptr<CurveEval> create_spiral_curve(const float rotations, const int totalpoints = std::max(int(resolution * rotations), 1); const float delta_radius = (end_radius - start_radius) / (float)totalpoints; - float radius = start_radius; const float delta_height = height / (float)totalpoints; - const float delta_theta = (M_PI * 2 * rotations) / (float)totalpoints; - float theta = 0.0f; + const float delta_theta = (M_PI * 2 * rotations) / (float)totalpoints * + (direction ? 1.0f : -1.0f); + + spline->resize(totalpoints + 1); + MutableSpan<float3> positions = spline->positions(); + spline->radii().fill(1.0f); + spline->tilts().fill(0.0f); for (const int i : IndexRange(totalpoints + 1)) { + const float theta = i * delta_theta; + const float radius = start_radius + i * delta_radius; const float x = radius * cos(theta); const float y = radius * sin(theta); const float z = delta_height * i; - spline->add_point(float3(x, y, z), 1.0f, 0.0f); - - radius += delta_radius; - - if (direction) { - theta += delta_theta; - } - else { - theta -= delta_theta; - } + positions[i] = {x, y, z}; } - spline->attributes.reallocate(spline->size()); curve->add_spline(std::move(spline)); curve->attributes.reallocate(curve->splines().size()); return curve; } -static void geo_node_curve_primitive_spiral_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { const float rotations = std::max(params.extract_input<float>("Rotations"), 0.0f); if (rotations == 0.0f) { - params.set_output("Curve", GeometrySet()); + params.set_default_remaining_outputs(); return; } @@ -89,14 +103,16 @@ static void geo_node_curve_primitive_spiral_exec(GeoNodeExecParams params) params.set_output("Curve", GeometrySet::create_with_curve(curve.release())); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_curve_primitive_spiral_cc void register_node_type_geo_curve_primitive_spiral() { + namespace file_ns = blender::nodes::node_geo_curve_primitive_spiral_cc; + static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_CURVE_PRIMITIVE_SPIRAL, "Spiral", NODE_CLASS_GEOMETRY, 0); - ntype.declare = blender::nodes::geo_node_curve_primitive_spiral_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_curve_primitive_spiral_exec; + geo_node_type_base(&ntype, GEO_NODE_CURVE_PRIMITIVE_SPIRAL, "Spiral", NODE_CLASS_GEOMETRY); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc index 6261146562d..14517a79037 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc @@ -18,15 +18,33 @@ #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_curve_primitive_star_cc { -static void geo_node_curve_primitive_star_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Int>("Points").default_value(8).min(3).max(256).subtype(PROP_UNSIGNED); - b.add_input<decl::Float>("Inner Radius").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input<decl::Float>("Outer Radius").default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input<decl::Float>("Twist").subtype(PROP_ANGLE); - b.add_output<decl::Geometry>("Curve"); + b.add_input<decl::Int>(N_("Points")) + .default_value(8) + .min(3) + .max(256) + .subtype(PROP_UNSIGNED) + .description(N_("Number of points on each of the circles")); + b.add_input<decl::Float>(N_("Inner Radius")) + .default_value(1.0f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description(N_("Radius of the inner circle; can be larger than outer radius")); + b.add_input<decl::Float>(N_("Outer Radius")) + .default_value(2.0f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description(N_("Radius of the outer circle; can be smaller than inner radius")); + b.add_input<decl::Float>(N_("Twist")) + .subtype(PROP_ANGLE) + .description(N_("The counterclockwise rotation of the inner set of points")); + b.add_output<decl::Geometry>(N_("Curve")); + b.add_output<decl::Bool>(N_("Outer Points")) + .field_source() + .description(N_("An attribute field with a selection of the outer points")); } static std::unique_ptr<CurveEval> create_star_curve(const float inner_radius, @@ -36,41 +54,69 @@ static std::unique_ptr<CurveEval> create_star_curve(const float inner_radius, { std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>(); std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); + spline->set_cyclic(true); + + spline->resize(points * 2); + MutableSpan<float3> positions = spline->positions(); + spline->radii().fill(1.0f); + spline->tilts().fill(0.0f); const float theta_step = (2.0f * M_PI) / float(points); - for (int i : IndexRange(points)) { + for (const int i : IndexRange(points)) { const float x = outer_radius * cos(theta_step * i); const float y = outer_radius * sin(theta_step * i); - spline->add_point(float3(x, y, 0.0f), 1.0f, 0.0f); + positions[i * 2] = {x, y, 0.0f}; const float inner_x = inner_radius * cos(theta_step * i + theta_step * 0.5f + twist); const float inner_y = inner_radius * sin(theta_step * i + theta_step * 0.5f + twist); - spline->add_point(float3(inner_x, inner_y, 0.0f), 1.0f, 0.0f); + positions[i * 2 + 1] = {inner_x, inner_y, 0.0f}; } - spline->set_cyclic(true); - spline->attributes.reallocate(spline->size()); + curve->add_spline(std::move(spline)); curve->attributes.reallocate(curve->splines().size()); + return curve; } -static void geo_node_curve_primitive_star_exec(GeoNodeExecParams params) +static void create_selection_output(CurveComponent &component, + StrongAnonymousAttributeID &r_attribute) +{ + OutputAttribute_Typed<bool> attribute = component.attribute_try_get_for_output_only<bool>( + r_attribute.get(), ATTR_DOMAIN_POINT); + MutableSpan<bool> selection = attribute.as_span(); + for (int i : selection.index_range()) { + selection[i] = i % 2 == 0; + } + attribute.save(); +} + +static void node_geo_exec(GeoNodeExecParams params) { std::unique_ptr<CurveEval> curve = create_star_curve( std::max(params.extract_input<float>("Inner Radius"), 0.0f), std::max(params.extract_input<float>("Outer Radius"), 0.0f), params.extract_input<float>("Twist"), std::max(params.extract_input<int>("Points"), 3)); - params.set_output("Curve", GeometrySet::create_with_curve(curve.release())); -} + GeometrySet output = GeometrySet::create_with_curve(curve.release()); -} // namespace blender::nodes + if (params.output_is_required("Outer Points")) { + StrongAnonymousAttributeID attribute_output("Outer Points"); + create_selection_output(output.get_component_for_write<CurveComponent>(), attribute_output); + params.set_output("Outer Points", + AnonymousAttributeFieldInput::Create<bool>( + std::move(attribute_output), params.attribute_producer_name())); + } + params.set_output("Curve", std::move(output)); +} +} // namespace blender::nodes::node_geo_curve_primitive_star_cc void register_node_type_geo_curve_primitive_star() { + namespace file_ns = blender::nodes::node_geo_curve_primitive_star_cc; + static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_CURVE_PRIMITIVE_STAR, "Star", NODE_CLASS_GEOMETRY, 0); - ntype.declare = blender::nodes::geo_node_curve_primitive_star_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_curve_primitive_star_exec; + geo_node_type_base(&ntype, GEO_NODE_CURVE_PRIMITIVE_STAR, "Star", NODE_CLASS_GEOMETRY); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc index b8f62460069..8494b4868e8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc @@ -26,50 +26,53 @@ #include "node_geometry_util.hh" -using blender::fn::GVArray_For_GSpan; -using blender::fn::GVArray_For_Span; -using blender::fn::GVArray_Typed; +namespace blender::nodes::node_geo_curve_resample_cc { -namespace blender::nodes { +NODE_STORAGE_FUNCS(NodeGeometryCurveResample) -static void geo_node_curve_resample_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Int>("Count").default_value(10).min(1).max(100000); - b.add_input<decl::Float>("Length").default_value(0.1f).min(0.001f).subtype(PROP_DISTANCE); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).supports_field().hide_value(); + b.add_input<decl::Int>(N_("Count")).default_value(10).min(1).max(100000).supports_field(); + b.add_input<decl::Float>(N_("Length")) + .default_value(0.1f) + .min(0.001f) + .supports_field() + .subtype(PROP_DISTANCE); + b.add_output<decl::Geometry>(N_("Curve")); } -static void geo_node_curve_resample_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "mode", 0, "", ICON_NONE); } -static void geo_node_curve_resample_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeGeometryCurveResample *data = (NodeGeometryCurveResample *)MEM_callocN( - sizeof(NodeGeometryCurveResample), __func__); + NodeGeometryCurveResample *data = MEM_cnew<NodeGeometryCurveResample>(__func__); data->mode = GEO_NODE_CURVE_RESAMPLE_COUNT; node->storage = data; } -static void geo_node_curve_resample_update(bNodeTree *UNUSED(ntree), bNode *node) +static void node_update(bNodeTree *ntree, bNode *node) { - NodeGeometryCurveResample &node_storage = *(NodeGeometryCurveResample *)node->storage; - const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)node_storage.mode; + const NodeGeometryCurveResample &storage = node_storage(*node); + const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)storage.mode; - bNodeSocket *count_socket = ((bNodeSocket *)node->inputs.first)->next; + bNodeSocket *count_socket = ((bNodeSocket *)node->inputs.first)->next->next; bNodeSocket *length_socket = count_socket->next; - nodeSetSocketAvailability(count_socket, mode == GEO_NODE_CURVE_RESAMPLE_COUNT); - nodeSetSocketAvailability(length_socket, mode == GEO_NODE_CURVE_RESAMPLE_LENGTH); + nodeSetSocketAvailability(ntree, count_socket, mode == GEO_NODE_CURVE_RESAMPLE_COUNT); + nodeSetSocketAvailability(ntree, length_socket, mode == GEO_NODE_CURVE_RESAMPLE_LENGTH); } struct SampleModeParam { GeometryNodeCurveResampleMode mode; - std::optional<float> length; - std::optional<int> count; + std::optional<Field<float>> length; + std::optional<Field<int>> count; + Field<bool> selection; }; static SplinePtr resample_spline(const Spline &src, const int count) @@ -78,8 +81,11 @@ static SplinePtr resample_spline(const Spline &src, const int count) Spline::copy_base_settings(src, *dst); if (src.evaluated_edges_size() < 1 || count == 1) { - dst->add_point(src.positions().first(), src.tilts().first(), src.radii().first()); - dst->attributes.reallocate(1); + dst->resize(1); + dst->positions().first() = src.positions().first(); + dst->radii().first() = src.radii().first(); + dst->tilts().first() = src.tilts().first(); + src.attributes.foreach_attribute( [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { std::optional<GSpan> src_attribute = src.attributes.get_for_read(attribute_id); @@ -118,7 +124,7 @@ static SplinePtr resample_spline(const Spline &src, const int count) std::optional<GMutableSpan> output_attribute = dst->attributes.get_for_write( attribute_id); if (output_attribute) { - src.sample_with_index_factors(*src.interpolate_to_evaluated(*input_attribute), + src.sample_with_index_factors(src.interpolate_to_evaluated(*input_attribute), uniform_samples, *output_attribute); return true; @@ -141,8 +147,8 @@ static SplinePtr resample_spline_evaluated(const Spline &src) dst->positions().copy_from(src.evaluated_positions()); dst->positions().copy_from(src.evaluated_positions()); - src.interpolate_to_evaluated(src.radii())->materialize(dst->radii()); - src.interpolate_to_evaluated(src.tilts())->materialize(dst->tilts()); + src.interpolate_to_evaluated(src.radii()).materialize(dst->radii()); + src.interpolate_to_evaluated(src.tilts()).materialize(dst->tilts()); src.attributes.foreach_attribute( [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { @@ -150,7 +156,7 @@ static SplinePtr resample_spline_evaluated(const Spline &src) if (dst->attributes.create(attribute_id, meta_data.data_type)) { std::optional<GMutableSpan> dst_attribute = dst->attributes.get_for_write(attribute_id); if (dst_attribute) { - src.interpolate_to_evaluated(*src_attribute)->materialize(dst_attribute->data()); + src.interpolate_to_evaluated(*src_attribute).materialize(dst_attribute->data()); return true; } } @@ -163,42 +169,80 @@ static SplinePtr resample_spline_evaluated(const Spline &src) return dst; } -static std::unique_ptr<CurveEval> resample_curve(const CurveEval &input_curve, +static std::unique_ptr<CurveEval> resample_curve(const CurveComponent *component, const SampleModeParam &mode_param) { - Span<SplinePtr> input_splines = input_curve.splines(); + const CurveEval *input_curve = component->get_for_read(); + GeometryComponentFieldContext field_context{*component, ATTR_DOMAIN_CURVE}; + const int domain_size = component->attribute_domain_size(ATTR_DOMAIN_CURVE); + + Span<SplinePtr> input_splines = input_curve->splines(); std::unique_ptr<CurveEval> output_curve = std::make_unique<CurveEval>(); output_curve->resize(input_splines.size()); MutableSpan<SplinePtr> output_splines = output_curve->splines(); if (mode_param.mode == GEO_NODE_CURVE_RESAMPLE_COUNT) { + fn::FieldEvaluator evaluator{field_context, domain_size}; + evaluator.add(*mode_param.count); + evaluator.add(mode_param.selection); + evaluator.evaluate(); + const VArray<int> &cuts = evaluator.get_evaluated<int>(0); + const VArray<bool> &selections = evaluator.get_evaluated<bool>(1); + threading::parallel_for(input_splines.index_range(), 128, [&](IndexRange range) { for (const int i : range) { BLI_assert(mode_param.count); - output_splines[i] = resample_spline(*input_splines[i], *mode_param.count); + if (selections[i] && input_splines[i]->evaluated_points_size() > 0) { + output_splines[i] = resample_spline(*input_splines[i], std::max(cuts[i], 1)); + } + else { + output_splines[i] = input_splines[i]->copy(); + } } }); } else if (mode_param.mode == GEO_NODE_CURVE_RESAMPLE_LENGTH) { + fn::FieldEvaluator evaluator{field_context, domain_size}; + evaluator.add(*mode_param.length); + evaluator.add(mode_param.selection); + evaluator.evaluate(); + const VArray<float> &lengths = evaluator.get_evaluated<float>(0); + const VArray<bool> &selections = evaluator.get_evaluated<bool>(1); + threading::parallel_for(input_splines.index_range(), 128, [&](IndexRange range) { for (const int i : range) { - const float length = input_splines[i]->length(); - const int count = std::max(int(length / *mode_param.length) + 1, 1); - output_splines[i] = resample_spline(*input_splines[i], count); + if (selections[i] && input_splines[i]->evaluated_points_size() > 0) { + /* Don't allow asymptotic count increase for low resolution values. */ + const float divide_length = std::max(lengths[i], 0.0001f); + const float spline_length = input_splines[i]->length(); + const int count = std::max(int(spline_length / divide_length) + 1, 1); + output_splines[i] = resample_spline(*input_splines[i], count); + } + else { + output_splines[i] = input_splines[i]->copy(); + } } }); } else if (mode_param.mode == GEO_NODE_CURVE_RESAMPLE_EVALUATED) { + fn::FieldEvaluator evaluator{field_context, domain_size}; + evaluator.add(mode_param.selection); + evaluator.evaluate(); + const VArray<bool> &selections = evaluator.get_evaluated<bool>(0); + threading::parallel_for(input_splines.index_range(), 128, [&](IndexRange range) { for (const int i : range) { - output_splines[i] = resample_spline_evaluated(*input_splines[i]); + if (selections[i] && input_splines[i]->evaluated_points_size() > 0) { + output_splines[i] = resample_spline_evaluated(*input_splines[i]); + } + else { + output_splines[i] = input_splines[i]->copy(); + } } }); } - - output_curve->attributes = input_curve.attributes; - + output_curve->attributes = input_curve->attributes; return output_curve; } @@ -209,54 +253,57 @@ static void geometry_set_curve_resample(GeometrySet &geometry_set, return; } - const CurveEval &input_curve = *geometry_set.get_curve_for_read(); - std::unique_ptr<CurveEval> output_curve = resample_curve(input_curve, mode_param); + std::unique_ptr<CurveEval> output_curve = resample_curve( + geometry_set.get_component_for_read<CurveComponent>(), mode_param); geometry_set.replace_curve(output_curve.release()); } -static void geo_node_resample_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { - GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); - NodeGeometryCurveResample &node_storage = *(NodeGeometryCurveResample *)params.node().storage; - const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)node_storage.mode; + const NodeGeometryCurveResample &storage = node_storage(params.node()); + const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)storage.mode; SampleModeParam mode_param; mode_param.mode = mode; + mode_param.selection = params.extract_input<Field<bool>>("Selection"); + if (mode == GEO_NODE_CURVE_RESAMPLE_COUNT) { - const int count = params.extract_input<int>("Count"); + Field<int> count = params.extract_input<Field<int>>("Count"); if (count < 1) { - params.set_output("Geometry", GeometrySet()); + params.set_default_remaining_outputs(); return; } mode_param.count.emplace(count); } else if (mode == GEO_NODE_CURVE_RESAMPLE_LENGTH) { - /* Don't allow asymptotic count increase for low resolution values. */ - const float resolution = std::max(params.extract_input<float>("Length"), 0.0001f); + Field<float> resolution = params.extract_input<Field<float>>("Length"); mode_param.length.emplace(resolution); } geometry_set.modify_geometry_sets( [&](GeometrySet &geometry_set) { geometry_set_curve_resample(geometry_set, mode_param); }); - params.set_output("Geometry", std::move(geometry_set)); + params.set_output("Curve", std::move(geometry_set)); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_curve_resample_cc void register_node_type_geo_curve_resample() { + namespace file_ns = blender::nodes::node_geo_curve_resample_cc; + static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_CURVE_RESAMPLE, "Resample Curve", NODE_CLASS_GEOMETRY, 0); - ntype.declare = blender::nodes::geo_node_curve_resample_declare; - ntype.draw_buttons = blender::nodes::geo_node_curve_resample_layout; + geo_node_type_base(&ntype, GEO_NODE_RESAMPLE_CURVE, "Resample Curve", NODE_CLASS_GEOMETRY); + ntype.declare = file_ns::node_declare; + ntype.draw_buttons = file_ns::node_layout; node_type_storage( &ntype, "NodeGeometryCurveResample", node_free_standard_storage, node_copy_standard_storage); - node_type_init(&ntype, blender::nodes::geo_node_curve_resample_init); - node_type_update(&ntype, blender::nodes::geo_node_curve_resample_update); - ntype.geometry_node_execute = blender::nodes::geo_node_resample_exec; + node_type_init(&ntype, file_ns::node_init); + node_type_update(&ntype, file_ns::node_update); + ntype.geometry_node_execute = file_ns::node_geo_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc index b644faabedb..38974fafce7 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc @@ -20,16 +20,16 @@ #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_curve_reverse_cc { -static void geo_node_curve_reverse_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Curve"); - b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field(); - b.add_output<decl::Geometry>("Curve"); + b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_output<decl::Geometry>(N_("Curve")); } -static void geo_node_curve_reverse_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); @@ -38,7 +38,7 @@ static void geo_node_curve_reverse_exec(GeoNodeExecParams params) return; } - Field<bool> selection_field = params.extract_input<Field<bool>>("Selection"); + Field<bool> selection_field = params.get_input<Field<bool>>("Selection"); CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>(); GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE}; const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE); @@ -60,13 +60,15 @@ static void geo_node_curve_reverse_exec(GeoNodeExecParams params) params.set_output("Curve", std::move(geometry_set)); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_curve_reverse_cc void register_node_type_geo_curve_reverse() { + namespace file_ns = blender::nodes::node_geo_curve_reverse_cc; + static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_CURVE_REVERSE, "Curve Reverse", NODE_CLASS_GEOMETRY, 0); - ntype.declare = blender::nodes::geo_node_curve_reverse_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_curve_reverse_exec; + geo_node_type_base(&ntype, GEO_NODE_REVERSE_CURVE, "Reverse Curve", NODE_CLASS_GEOMETRY); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc index 1266f525861..56fbc50f033 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc @@ -23,42 +23,53 @@ #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_curve_sample_cc { -static void geo_node_curve_sample_declare(NodeDeclarationBuilder &b) -{ - b.add_input<decl::Geometry>("Curve"); - b.add_input<decl::Float>("Factor").min(0.0f).max(1.0f).subtype(PROP_FACTOR).supports_field(); - b.add_input<decl::Float>("Length").min(0.0f).subtype(PROP_DISTANCE).supports_field(); +NODE_STORAGE_FUNCS(NodeGeometryCurveSample) - b.add_output<decl::Vector>("Position").dependent_field(); - b.add_output<decl::Vector>("Tangent").dependent_field(); - b.add_output<decl::Vector>("Normal").dependent_field(); +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>(N_("Curve")) + .only_realized_data() + .supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input<decl::Float>(N_("Factor")) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR) + .supports_field() + .make_available([](bNode &node) { node_storage(node).mode = GEO_NODE_CURVE_SAMPLE_FACTOR; }); + b.add_input<decl::Float>(N_("Length")) + .min(0.0f) + .subtype(PROP_DISTANCE) + .supports_field() + .make_available([](bNode &node) { node_storage(node).mode = GEO_NODE_CURVE_SAMPLE_LENGTH; }); + b.add_output<decl::Vector>(N_("Position")).dependent_field(); + b.add_output<decl::Vector>(N_("Tangent")).dependent_field(); + b.add_output<decl::Vector>(N_("Normal")).dependent_field(); } -static void geo_node_curve_sample_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); } -static void geo_node_curve_sample_type_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_type_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeGeometryCurveSample *data = (NodeGeometryCurveSample *)MEM_callocN( - sizeof(NodeGeometryCurveSample), __func__); + NodeGeometryCurveSample *data = MEM_cnew<NodeGeometryCurveSample>(__func__); data->mode = GEO_NODE_CURVE_SAMPLE_LENGTH; node->storage = data; } -static void geo_node_curve_sample_update(bNodeTree *UNUSED(ntree), bNode *node) +static void node_update(bNodeTree *ntree, bNode *node) { - const NodeGeometryCurveSample &node_storage = *(NodeGeometryCurveSample *)node->storage; - const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode; + const NodeGeometryCurveSample &storage = node_storage(*node); + const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)storage.mode; bNodeSocket *factor = ((bNodeSocket *)node->inputs.first)->next; bNodeSocket *length = factor->next; - nodeSetSocketAvailability(factor, mode == GEO_NODE_CURVE_SAMPLE_FACTOR); - nodeSetSocketAvailability(length, mode == GEO_NODE_CURVE_SAMPLE_LENGTH); + nodeSetSocketAvailability(ntree, factor, mode == GEO_NODE_CURVE_SAMPLE_FACTOR); + nodeSetSocketAvailability(ntree, length, mode == GEO_NODE_CURVE_SAMPLE_LENGTH); } template<typename T> static T sample_with_lookup(const Spline::LookupResult lookup, Span<T> data) @@ -174,7 +185,7 @@ class SampleCurveFunction : public fn::MultiFunction { for (const int i : mask) { const Spline::LookupResult &lookup = lookups[i]; const Span<float3> evaluated_tangents = splines[spline_indices[i]]->evaluated_tangents(); - sampled_tangents[i] = sample_with_lookup(lookup, evaluated_tangents).normalized(); + sampled_tangents[i] = math::normalize(sample_with_lookup(lookup, evaluated_tangents)); } } @@ -182,7 +193,7 @@ class SampleCurveFunction : public fn::MultiFunction { for (const int i : mask) { const Spline::LookupResult &lookup = lookups[i]; const Span<float3> evaluated_normals = splines[spline_indices[i]]->evaluated_normals(); - sampled_normals[i] = sample_with_lookup(lookup, evaluated_normals).normalized(); + sampled_normals[i] = math::normalize(sample_with_lookup(lookup, evaluated_normals)); } } } @@ -198,8 +209,8 @@ class SampleCurveFunction : public fn::MultiFunction { static Field<float> get_length_input_field(const GeoNodeExecParams ¶ms, const float curve_total_length) { - const NodeGeometryCurveSample &node_storage = *(NodeGeometryCurveSample *)params.node().storage; - const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode; + const NodeGeometryCurveSample &storage = node_storage(params.node()); + const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)storage.mode; if (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) { /* Just make sure the length is in bounds of the curve. */ @@ -227,34 +238,32 @@ static Field<float> get_length_input_field(const GeoNodeExecParams ¶ms, return Field<float>(std::move(process_op), 0); } -static void geo_node_curve_sample_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); - auto return_default = [&]() { - params.set_output("Position", fn::make_constant_field<float3>({0.0f, 0.0f, 0.0f})); - params.set_output("Tangent", fn::make_constant_field<float3>({0.0f, 0.0f, 0.0f})); - params.set_output("Normal", fn::make_constant_field<float3>({0.0f, 0.0f, 0.0f})); - }; - const CurveComponent *component = geometry_set.get_component_for_read<CurveComponent>(); if (component == nullptr) { - return return_default(); + params.set_default_remaining_outputs(); + return; } const CurveEval *curve = component->get_for_read(); if (curve == nullptr) { - return return_default(); + params.set_default_remaining_outputs(); + return; } if (curve->splines().is_empty()) { - return return_default(); + params.set_default_remaining_outputs(); + return; } Array<float> spline_lengths = curve->accumulated_spline_lengths(); const float total_length = spline_lengths.last(); if (total_length == 0.0f) { - return return_default(); + params.set_default_remaining_outputs(); + return; } Field<float> length_field = get_length_input_field(params, total_length); @@ -269,20 +278,22 @@ static void geo_node_curve_sample_exec(GeoNodeExecParams params) params.set_output("Normal", Field<float3>(sample_op, 2)); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_curve_sample_cc void register_node_type_geo_curve_sample() { + namespace file_ns = blender::nodes::node_geo_curve_sample_cc; + static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_CURVE_SAMPLE, "Curve Sample", NODE_CLASS_GEOMETRY, 0); - ntype.geometry_node_execute = blender::nodes::geo_node_curve_sample_exec; - ntype.declare = blender::nodes::geo_node_curve_sample_declare; - node_type_init(&ntype, blender::nodes::geo_node_curve_sample_type_init); - node_type_update(&ntype, blender::nodes::geo_node_curve_sample_update); + geo_node_type_base(&ntype, GEO_NODE_SAMPLE_CURVE, "Sample Curve", NODE_CLASS_GEOMETRY); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.declare = file_ns::node_declare; + node_type_init(&ntype, file_ns::node_type_init); + node_type_update(&ntype, file_ns::node_update); node_type_storage( &ntype, "NodeGeometryCurveSample", node_free_standard_storage, node_copy_standard_storage); - ntype.draw_buttons = blender::nodes::geo_node_curve_sample_layout; + ntype.draw_buttons = file_ns::node_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc new file mode 100644 index 00000000000..74bdce4cef3 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc @@ -0,0 +1,149 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BKE_spline.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_curve_set_handles_cc { + +NODE_STORAGE_FUNCS(NodeGeometryCurveSetHandles) + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_output<decl::Geometry>(N_("Curve")); +} + +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); + uiItemR(layout, ptr, "handle_type", 0, "", ICON_NONE); +} + +static void node_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryCurveSetHandles *data = MEM_cnew<NodeGeometryCurveSetHandles>(__func__); + + data->handle_type = GEO_NODE_CURVE_HANDLE_AUTO; + data->mode = GEO_NODE_CURVE_HANDLE_LEFT | GEO_NODE_CURVE_HANDLE_RIGHT; + node->storage = data; +} + +static BezierSpline::HandleType handle_type_from_input_type(GeometryNodeCurveHandleType type) +{ + switch (type) { + case GEO_NODE_CURVE_HANDLE_AUTO: + return BezierSpline::HandleType::Auto; + case GEO_NODE_CURVE_HANDLE_ALIGN: + return BezierSpline::HandleType::Align; + case GEO_NODE_CURVE_HANDLE_FREE: + return BezierSpline::HandleType::Free; + case GEO_NODE_CURVE_HANDLE_VECTOR: + return BezierSpline::HandleType::Vector; + } + BLI_assert_unreachable(); + return BezierSpline::HandleType::Auto; +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + const NodeGeometryCurveSetHandles &storage = node_storage(params.node()); + const GeometryNodeCurveHandleType type = (GeometryNodeCurveHandleType)storage.handle_type; + const GeometryNodeCurveHandleMode mode = (GeometryNodeCurveHandleMode)storage.mode; + + GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); + Field<bool> selection_field = params.extract_input<Field<bool>>("Selection"); + + bool has_bezier_spline = false; + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (!geometry_set.has_curve()) { + return; + } + + /* Retrieve data for write access so we can avoid new allocations for the handles data. */ + CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>(); + CurveEval &curve = *curve_component.get_for_write(); + MutableSpan<SplinePtr> splines = curve.splines(); + + GeometryComponentFieldContext field_context{curve_component, ATTR_DOMAIN_POINT}; + const int domain_size = curve_component.attribute_domain_size(ATTR_DOMAIN_POINT); + + fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + selection_evaluator.add(selection_field); + selection_evaluator.evaluate(); + const VArray<bool> &selection = selection_evaluator.get_evaluated<bool>(0); + + const BezierSpline::HandleType new_handle_type = handle_type_from_input_type(type); + int point_index = 0; + + for (SplinePtr &spline : splines) { + if (spline->type() != Spline::Type::Bezier) { + point_index += spline->positions().size(); + continue; + } + + has_bezier_spline = true; + BezierSpline &bezier_spline = static_cast<BezierSpline &>(*spline); + if (ELEM(new_handle_type, BezierSpline::HandleType::Free, BezierSpline::HandleType::Align)) { + /* In this case the automatically calculated handle types need to be "baked", because + * they're possibly changing from a type that is calculated automatically to a type that + * is positioned manually. */ + bezier_spline.ensure_auto_handles(); + } + + for (int i_point : IndexRange(bezier_spline.size())) { + if (selection[point_index]) { + if (mode & GEO_NODE_CURVE_HANDLE_LEFT) { + bezier_spline.handle_types_left()[i_point] = new_handle_type; + } + if (mode & GEO_NODE_CURVE_HANDLE_RIGHT) { + bezier_spline.handle_types_right()[i_point] = new_handle_type; + } + } + point_index++; + } + bezier_spline.mark_cache_invalid(); + } + }); + if (!has_bezier_spline) { + params.error_message_add(NodeWarningType::Info, TIP_("No Bezier splines in input curve")); + } + params.set_output("Curve", std::move(geometry_set)); +} +} // namespace blender::nodes::node_geo_curve_set_handles_cc + +void register_node_type_geo_curve_set_handles() +{ + namespace file_ns = blender::nodes::node_geo_curve_set_handles_cc; + + static bNodeType ntype; + geo_node_type_base(&ntype, GEO_NODE_CURVE_SET_HANDLES, "Set Handle Type", NODE_CLASS_GEOMETRY); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + node_type_init(&ntype, file_ns::node_init); + node_type_storage(&ntype, + "NodeGeometryCurveSetHandles", + node_free_standard_storage, + node_copy_standard_storage); + ntype.draw_buttons = file_ns::node_layout; + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc new file mode 100644 index 00000000000..257a5b8df00 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc @@ -0,0 +1,326 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_task.hh" + +#include "BKE_spline.hh" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_curve_spline_parameter_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_output<decl::Float>(N_("Factor")) + .field_source() + .description( + N_("For points, the portion of the spline's total length at the control point. For " + "Splines, the factor of that spline within the entire curve")); + b.add_output<decl::Float>(N_("Length")) + .field_source() + .description( + N_("For points, the distance along the control point's spline, For splines, the " + "distance along the entire curve")); + b.add_output<decl::Int>(N_("Index")) + .field_source() + .description(N_("Each control point's index on its spline")); +} + +/** + * A basic interpolation from the point domain to the spline domain would be useless, since the + * average parameter for each spline would just be 0.5, or close to it. Instead, the parameter for + * each spline is the portion of the total length at the start of the spline. + */ +static Array<float> curve_length_spline_domain(const CurveEval &curve, + const IndexMask UNUSED(mask)) +{ + Span<SplinePtr> splines = curve.splines(); + float length = 0.0f; + Array<float> lengths(splines.size()); + for (const int i : splines.index_range()) { + lengths[i] = length; + length += splines[i]->length(); + } + return lengths; +} + +/** + * The parameter at each control point is the factor at the corresponding evaluated point. + */ +static void calculate_bezier_lengths(const BezierSpline &spline, MutableSpan<float> lengths) +{ + Span<int> offsets = spline.control_point_offsets(); + Span<float> lengths_eval = spline.evaluated_lengths(); + for (const int i : IndexRange(1, spline.size() - 1)) { + lengths[i] = lengths_eval[offsets[i] - 1]; + } +} + +/** + * The parameter for poly splines is simply the evaluated lengths divided by the total length. + */ +static void calculate_poly_length(const PolySpline &spline, MutableSpan<float> lengths) +{ + Span<float> lengths_eval = spline.evaluated_lengths(); + if (spline.is_cyclic()) { + lengths.drop_front(1).copy_from(lengths_eval.drop_back(1)); + } + else { + lengths.drop_front(1).copy_from(lengths_eval); + } +} + +/** + * Since NURBS control points do not necessarily coincide with the evaluated curve's path, and + * each control point doesn't correspond well to a specific evaluated point, the parameter at + * each point is not well defined. So instead, treat the control points as if they were a poly + * spline. + */ +static void calculate_nurbs_lengths(const NURBSpline &spline, MutableSpan<float> lengths) +{ + Span<float3> positions = spline.positions(); + Array<float> control_point_lengths(spline.size()); + float length = 0.0f; + for (const int i : IndexRange(positions.size() - 1)) { + lengths[i] = length; + length += math::distance(positions[i], positions[i + 1]); + } + lengths.last() = length; +} + +static Array<float> curve_length_point_domain(const CurveEval &curve) +{ + Span<SplinePtr> splines = curve.splines(); + Array<int> offsets = curve.control_point_offsets(); + const int total_size = offsets.last(); + Array<float> lengths(total_size); + + threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) { + for (const int i : range) { + const Spline &spline = *splines[i]; + MutableSpan spline_factors{lengths.as_mutable_span().slice(offsets[i], spline.size())}; + spline_factors.first() = 0.0f; + switch (splines[i]->type()) { + case Spline::Type::Bezier: { + calculate_bezier_lengths(static_cast<const BezierSpline &>(spline), spline_factors); + break; + } + case Spline::Type::Poly: { + calculate_poly_length(static_cast<const PolySpline &>(spline), spline_factors); + break; + } + case Spline::Type::NURBS: { + calculate_nurbs_lengths(static_cast<const NURBSpline &>(spline), spline_factors); + break; + } + } + } + }); + return lengths; +} + +static VArray<float> construct_curve_parameter_varray(const CurveEval &curve, + const IndexMask mask, + const AttributeDomain domain) +{ + if (domain == ATTR_DOMAIN_POINT) { + Span<SplinePtr> splines = curve.splines(); + Array<float> values = curve_length_point_domain(curve); + + const Array<int> offsets = curve.control_point_offsets(); + for (const int i_spline : curve.splines().index_range()) { + const Spline &spline = *splines[i_spline]; + const float spline_length = spline.length(); + const float spline_length_inv = spline_length == 0.0f ? 0.0f : 1.0f / spline_length; + for (const int i : IndexRange(spline.size())) { + values[offsets[i_spline] + i] *= spline_length_inv; + } + } + return VArray<float>::ForContainer(std::move(values)); + } + + if (domain == ATTR_DOMAIN_CURVE) { + Array<float> values = curve.accumulated_spline_lengths(); + const float total_length_inv = values.last() == 0.0f ? 0.0f : 1.0f / values.last(); + for (const int i : mask) { + values[i] *= total_length_inv; + } + return VArray<float>::ForContainer(std::move(values)); + } + return {}; +} + +static VArray<float> construct_curve_length_varray(const CurveEval &curve, + const IndexMask mask, + const AttributeDomain domain) +{ + if (domain == ATTR_DOMAIN_POINT) { + Array<float> lengths = curve_length_point_domain(curve); + return VArray<float>::ForContainer(std::move(lengths)); + } + + if (domain == ATTR_DOMAIN_CURVE) { + if (curve.splines().size() == 1) { + Array<float> lengths(1, 0.0f); + return VArray<float>::ForContainer(std::move(lengths)); + } + + Array<float> lengths = curve_length_spline_domain(curve, mask); + return VArray<float>::ForContainer(std::move(lengths)); + } + + return {}; +} + +static VArray<int> construct_index_on_spline_varray(const CurveEval &curve, + const IndexMask UNUSED(mask), + const AttributeDomain domain) +{ + if (domain == ATTR_DOMAIN_POINT) { + Array<int> output(curve.total_control_point_size()); + int output_index = 0; + for (int spline_index : curve.splines().index_range()) { + for (int point_index : IndexRange(curve.splines()[spline_index]->size())) { + output[output_index++] = point_index; + } + } + return VArray<int>::ForContainer(std::move(output)); + } + return {}; +} + +class CurveParameterFieldInput final : public GeometryFieldInput { + public: + CurveParameterFieldInput() : GeometryFieldInput(CPPType::get<float>(), "Curve Parameter node") + { + category_ = Category::Generated; + } + + GVArray get_varray_for_context(const GeometryComponent &component, + const AttributeDomain domain, + IndexMask mask) const final + { + if (component.type() == GEO_COMPONENT_TYPE_CURVE) { + const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); + const CurveEval *curve = curve_component.get_for_read(); + if (curve) { + return construct_curve_parameter_varray(*curve, mask, domain); + } + } + return {}; + } + + uint64_t hash() const override + { + /* Some random constant hash. */ + return 29837456298; + } + + bool is_equal_to(const fn::FieldNode &other) const override + { + return dynamic_cast<const CurveParameterFieldInput *>(&other) != nullptr; + } +}; + +class CurveLengthFieldInput final : public GeometryFieldInput { + public: + CurveLengthFieldInput() : GeometryFieldInput(CPPType::get<float>(), "Curve Length node") + { + category_ = Category::Generated; + } + + GVArray get_varray_for_context(const GeometryComponent &component, + const AttributeDomain domain, + IndexMask mask) const final + { + if (component.type() == GEO_COMPONENT_TYPE_CURVE) { + const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); + const CurveEval *curve = curve_component.get_for_read(); + if (curve) { + return construct_curve_length_varray(*curve, mask, domain); + } + } + return {}; + } + + uint64_t hash() const override + { + /* Some random constant hash. */ + return 345634563454; + } + + bool is_equal_to(const fn::FieldNode &other) const override + { + return dynamic_cast<const CurveLengthFieldInput *>(&other) != nullptr; + } +}; + +class IndexOnSplineFieldInput final : public GeometryFieldInput { + public: + IndexOnSplineFieldInput() : GeometryFieldInput(CPPType::get<int>(), "Spline Index") + { + category_ = Category::Generated; + } + + GVArray get_varray_for_context(const GeometryComponent &component, + const AttributeDomain domain, + IndexMask mask) const final + { + if (component.type() == GEO_COMPONENT_TYPE_CURVE) { + const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); + const CurveEval *curve = curve_component.get_for_read(); + if (curve) { + return construct_index_on_spline_varray(*curve, mask, domain); + } + } + return {}; + } + + uint64_t hash() const override + { + /* Some random constant hash. */ + return 4536246522; + } + + bool is_equal_to(const fn::FieldNode &other) const override + { + return dynamic_cast<const IndexOnSplineFieldInput *>(&other) != nullptr; + } +}; + +static void node_geo_exec(GeoNodeExecParams params) +{ + Field<float> parameter_field{std::make_shared<CurveParameterFieldInput>()}; + Field<float> length_field{std::make_shared<CurveLengthFieldInput>()}; + Field<int> index_on_spline_field{std::make_shared<IndexOnSplineFieldInput>()}; + params.set_output("Factor", std::move(parameter_field)); + params.set_output("Length", std::move(length_field)); + params.set_output("Index", std::move(index_on_spline_field)); +} + +} // namespace blender::nodes::node_geo_curve_spline_parameter_cc + +void register_node_type_geo_curve_spline_parameter() +{ + namespace file_ns = blender::nodes::node_geo_curve_spline_parameter_cc; + + static bNodeType ntype; + geo_node_type_base( + &ntype, GEO_NODE_CURVE_SPLINE_PARAMETER, "Spline Parameter", NODE_CLASS_INPUT); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.declare = file_ns::node_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc new file mode 100644 index 00000000000..b91ddd7bc7a --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc @@ -0,0 +1,434 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BKE_spline.hh" + +#include "BLI_task.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_curve_spline_type_cc { + +NODE_STORAGE_FUNCS(NodeGeometryCurveSplineType) + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_output<decl::Geometry>(N_("Curve")); +} + +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "spline_type", 0, "", ICON_NONE); +} + +static void node_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryCurveSplineType *data = MEM_cnew<NodeGeometryCurveSplineType>(__func__); + + data->spline_type = GEO_NODE_SPLINE_TYPE_POLY; + node->storage = data; +} + +template<class T> +static void scale_input_assign(const Span<T> input, + const int scale, + const int offset, + const MutableSpan<T> r_output) +{ + for (const int i : IndexRange(r_output.size())) { + r_output[i] = input[i * scale + offset]; + } +} + +template<class T> +static void scale_output_assign(const Span<T> input, + const int scale, + const int offset, + const MutableSpan<T> &r_output) +{ + for (const int i : IndexRange(input.size())) { + r_output[i * scale + offset] = input[i]; + } +} + +template<class T> +static void nurbs_to_bezier_assign(const Span<T> input, + const MutableSpan<T> r_output, + const NURBSpline::KnotsMode knotsMode) +{ + const int input_size = input.size(); + const int output_size = r_output.size(); + + switch (knotsMode) { + case NURBSpline::KnotsMode::Bezier: + scale_input_assign<T>(input, 3, 1, r_output); + break; + case NURBSpline::KnotsMode::Normal: + for (const int i : IndexRange(output_size)) { + r_output[i] = input[(i + 1) % input_size]; + } + break; + case NURBSpline::KnotsMode::EndPoint: + for (const int i : IndexRange(1, output_size - 2)) { + r_output[i] = input[i + 1]; + } + r_output.first() = input.first(); + r_output.last() = input.last(); + break; + } +} + +template<typename CopyFn> +static void copy_attributes(const Spline &input_spline, Spline &output_spline, CopyFn copy_fn) +{ + input_spline.attributes.foreach_attribute( + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + std::optional<GSpan> src = input_spline.attributes.get_for_read(attribute_id); + BLI_assert(src); + if (!output_spline.attributes.create(attribute_id, meta_data.data_type)) { + BLI_assert_unreachable(); + return false; + } + std::optional<GMutableSpan> dst = output_spline.attributes.get_for_write(attribute_id); + if (!dst) { + BLI_assert_unreachable(); + return false; + } + + copy_fn(*src, *dst); + + return true; + }, + ATTR_DOMAIN_POINT); +} + +static Vector<float3> create_nurbs_to_bezier_handles(const Span<float3> nurbs_positions, + const NURBSpline::KnotsMode knots_mode) +{ + const int nurbs_positions_size = nurbs_positions.size(); + Vector<float3> handle_positions; + if (knots_mode == NURBSpline::KnotsMode::Bezier) { + for (const int i : IndexRange(nurbs_positions_size)) { + if (i % 3 == 1) { + continue; + } + handle_positions.append(nurbs_positions[i]); + } + if (nurbs_positions_size % 3 == 1) { + handle_positions.pop_last(); + } + else if (nurbs_positions_size % 3 == 2) { + const int last_index = nurbs_positions_size - 1; + handle_positions.append(2 * nurbs_positions[last_index] - nurbs_positions[last_index - 1]); + } + } + else { + const bool is_periodic = knots_mode == NURBSpline::KnotsMode::Normal; + if (is_periodic) { + handle_positions.append(nurbs_positions[1] + + ((nurbs_positions[0] - nurbs_positions[1]) / 3)); + } + else { + handle_positions.append(2 * nurbs_positions[0] - nurbs_positions[1]); + handle_positions.append(nurbs_positions[1]); + } + const int segments_size = nurbs_positions_size - 1; + const bool ignore_interior_segment = segments_size == 3 && is_periodic == false; + if (ignore_interior_segment == false) { + const float mid_offset = (float)(segments_size - 1) / 2.0f; + for (const int i : IndexRange(1, segments_size - 2)) { + const int divisor = is_periodic ? + 3 : + std::min(3, (int)(-std::abs(i - mid_offset) + mid_offset + 1.0f)); + const float3 &p1 = nurbs_positions[i]; + const float3 &p2 = nurbs_positions[i + 1]; + const float3 displacement = (p2 - p1) / divisor; + const int num_handles_on_segment = divisor < 3 ? 1 : 2; + for (int j : IndexRange(1, num_handles_on_segment)) { + handle_positions.append(p1 + (displacement * j)); + } + } + } + const int last_index = nurbs_positions_size - 1; + if (is_periodic) { + handle_positions.append( + nurbs_positions[last_index - 1] + + ((nurbs_positions[last_index] - nurbs_positions[last_index - 1]) / 3)); + } + else { + handle_positions.append(nurbs_positions[last_index - 1]); + handle_positions.append(2 * nurbs_positions[last_index] - nurbs_positions[last_index - 1]); + } + } + return handle_positions; +} + +static Array<float3> create_nurbs_to_bezier_positions(const Span<float3> nurbs_positions, + const Span<float3> handle_positions, + const NURBSpline::KnotsMode knots_mode) +{ + if (knots_mode == NURBSpline::KnotsMode::Bezier) { + /* Every third NURBS position (starting from index 1) should be converted to Bezier position */ + const int scale = 3; + const int offset = 1; + Array<float3> bezier_positions((nurbs_positions.size() + offset) / scale); + scale_input_assign(nurbs_positions, scale, offset, bezier_positions.as_mutable_span()); + return bezier_positions; + } + + Array<float3> bezier_positions(handle_positions.size() / 2); + for (const int i : IndexRange(bezier_positions.size())) { + bezier_positions[i] = math::interpolate( + handle_positions[i * 2], handle_positions[i * 2 + 1], 0.5f); + } + return bezier_positions; +} + +static SplinePtr convert_to_poly_spline(const Spline &input) +{ + std::unique_ptr<PolySpline> output = std::make_unique<PolySpline>(); + output->resize(input.positions().size()); + output->positions().copy_from(input.positions()); + output->radii().copy_from(input.radii()); + output->tilts().copy_from(input.tilts()); + Spline::copy_base_settings(input, *output); + output->attributes = input.attributes; + return output; +} + +static SplinePtr poly_to_nurbs(const Spline &input) +{ + std::unique_ptr<NURBSpline> output = std::make_unique<NURBSpline>(); + output->resize(input.positions().size()); + output->positions().copy_from(input.positions()); + output->radii().copy_from(input.radii()); + output->tilts().copy_from(input.tilts()); + output->weights().fill(1.0f); + output->set_resolution(12); + output->set_order(4); + Spline::copy_base_settings(input, *output); + output->knots_mode = NURBSpline::KnotsMode::Bezier; + output->attributes = input.attributes; + return output; +} + +static SplinePtr bezier_to_nurbs(const Spline &input) +{ + const BezierSpline &bezier_spline = static_cast<const BezierSpline &>(input); + std::unique_ptr<NURBSpline> output = std::make_unique<NURBSpline>(); + output->resize(input.size() * 3); + + scale_output_assign(bezier_spline.handle_positions_left(), 3, 0, output->positions()); + scale_output_assign(input.radii(), 3, 0, output->radii()); + scale_output_assign(input.tilts(), 3, 0, output->tilts()); + + scale_output_assign(bezier_spline.positions(), 3, 1, output->positions()); + scale_output_assign(input.radii(), 3, 1, output->radii()); + scale_output_assign(input.tilts(), 3, 1, output->tilts()); + + scale_output_assign(bezier_spline.handle_positions_right(), 3, 2, output->positions()); + scale_output_assign(input.radii(), 3, 2, output->radii()); + scale_output_assign(input.tilts(), 3, 2, output->tilts()); + + Spline::copy_base_settings(input, *output); + output->weights().fill(1.0f); + output->set_resolution(12); + output->set_order(4); + output->set_cyclic(input.is_cyclic()); + output->knots_mode = NURBSpline::KnotsMode::Bezier; + output->attributes.reallocate(output->size()); + copy_attributes(input, *output, [](GSpan src, GMutableSpan dst) { + attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { + using T = decltype(dummy); + scale_output_assign<T>(src.typed<T>(), 3, 0, dst.typed<T>()); + scale_output_assign<T>(src.typed<T>(), 3, 1, dst.typed<T>()); + scale_output_assign<T>(src.typed<T>(), 3, 2, dst.typed<T>()); + }); + }); + return output; +} + +static SplinePtr poly_to_bezier(const Spline &input) +{ + std::unique_ptr<BezierSpline> output = std::make_unique<BezierSpline>(); + output->resize(input.size()); + output->positions().copy_from(input.positions()); + output->radii().copy_from(input.radii()); + output->tilts().copy_from(input.tilts()); + output->handle_types_left().fill(BezierSpline::HandleType::Vector); + output->handle_types_right().fill(BezierSpline::HandleType::Vector); + output->set_resolution(12); + Spline::copy_base_settings(input, *output); + output->attributes = input.attributes; + return output; +} + +static SplinePtr nurbs_to_bezier(const Spline &input) +{ + const NURBSpline &nurbs_spline = static_cast<const NURBSpline &>(input); + Span<float3> nurbs_positions; + Vector<float3> nurbs_positions_vector; + NURBSpline::KnotsMode knots_mode; + if (nurbs_spline.is_cyclic()) { + nurbs_positions_vector = nurbs_spline.positions(); + nurbs_positions_vector.append(nurbs_spline.positions()[0]); + nurbs_positions_vector.append(nurbs_spline.positions()[1]); + nurbs_positions = nurbs_positions_vector; + knots_mode = NURBSpline::KnotsMode::Normal; + } + else { + nurbs_positions = nurbs_spline.positions(); + knots_mode = nurbs_spline.knots_mode; + } + const Vector<float3> handle_positions = create_nurbs_to_bezier_handles(nurbs_positions, + knots_mode); + BLI_assert(handle_positions.size() % 2 == 0); + const Array<float3> bezier_positions = create_nurbs_to_bezier_positions( + nurbs_positions, handle_positions.as_span(), knots_mode); + BLI_assert(handle_positions.size() == bezier_positions.size() * 2); + + std::unique_ptr<BezierSpline> output = std::make_unique<BezierSpline>(); + output->resize(bezier_positions.size()); + output->positions().copy_from(bezier_positions); + nurbs_to_bezier_assign(nurbs_spline.radii(), output->radii(), knots_mode); + nurbs_to_bezier_assign(nurbs_spline.tilts(), output->tilts(), knots_mode); + scale_input_assign(handle_positions.as_span(), 2, 0, output->handle_positions_left()); + scale_input_assign(handle_positions.as_span(), 2, 1, output->handle_positions_right()); + output->handle_types_left().fill(BezierSpline::HandleType::Align); + output->handle_types_right().fill(BezierSpline::HandleType::Align); + output->set_resolution(nurbs_spline.resolution()); + Spline::copy_base_settings(nurbs_spline, *output); + output->attributes.reallocate(output->size()); + copy_attributes(nurbs_spline, *output, [knots_mode](GSpan src, GMutableSpan dst) { + attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { + using T = decltype(dummy); + nurbs_to_bezier_assign(src.typed<T>(), dst.typed<T>(), knots_mode); + }); + }); + return output; +} + +static SplinePtr convert_to_bezier(const Spline &input, GeoNodeExecParams params) +{ + switch (input.type()) { + case Spline::Type::Bezier: + return input.copy(); + case Spline::Type::Poly: + return poly_to_bezier(input); + case Spline::Type::NURBS: + if (input.size() < 4) { + params.error_message_add( + NodeWarningType::Info, + TIP_("NURBS must have minimum of 4 points for Bezier Conversion")); + return input.copy(); + } + return nurbs_to_bezier(input); + } + BLI_assert_unreachable(); + return {}; +} + +static SplinePtr convert_to_nurbs(const Spline &input) +{ + switch (input.type()) { + case Spline::Type::NURBS: + return input.copy(); + case Spline::Type::Bezier: + return bezier_to_nurbs(input); + case Spline::Type::Poly: + return poly_to_nurbs(input); + } + BLI_assert_unreachable(); + return {}; +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + const NodeGeometryCurveSplineType &storage = node_storage(params.node()); + const GeometryNodeSplineType output_type = (const GeometryNodeSplineType)storage.spline_type; + + GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); + Field<bool> selection_field = params.extract_input<Field<bool>>("Selection"); + + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (!geometry_set.has_curve()) { + return; + } + + const CurveComponent *curve_component = geometry_set.get_component_for_read<CurveComponent>(); + const CurveEval &curve = *curve_component->get_for_read(); + GeometryComponentFieldContext field_context{*curve_component, ATTR_DOMAIN_CURVE}; + const int domain_size = curve_component->attribute_domain_size(ATTR_DOMAIN_CURVE); + + fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + selection_evaluator.add(selection_field); + selection_evaluator.evaluate(); + const VArray<bool> &selection = selection_evaluator.get_evaluated<bool>(0); + + std::unique_ptr<CurveEval> new_curve = std::make_unique<CurveEval>(); + new_curve->resize(curve.splines().size()); + + threading::parallel_for(curve.splines().index_range(), 512, [&](IndexRange range) { + for (const int i : range) { + if (selection[i]) { + switch (output_type) { + case GEO_NODE_SPLINE_TYPE_POLY: + new_curve->splines()[i] = convert_to_poly_spline(*curve.splines()[i]); + break; + case GEO_NODE_SPLINE_TYPE_BEZIER: + new_curve->splines()[i] = convert_to_bezier(*curve.splines()[i], params); + break; + case GEO_NODE_SPLINE_TYPE_NURBS: + new_curve->splines()[i] = convert_to_nurbs(*curve.splines()[i]); + break; + } + } + else { + new_curve->splines()[i] = curve.splines()[i]->copy(); + } + } + }); + new_curve->attributes = curve.attributes; + geometry_set.replace_curve(new_curve.release()); + }); + + params.set_output("Curve", std::move(geometry_set)); +} + +} // namespace blender::nodes::node_geo_curve_spline_type_cc + +void register_node_type_geo_curve_spline_type() +{ + namespace file_ns = blender::nodes::node_geo_curve_spline_type_cc; + + static bNodeType ntype; + geo_node_type_base(&ntype, GEO_NODE_CURVE_SPLINE_TYPE, "Set Spline Type", NODE_CLASS_GEOMETRY); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + node_type_init(&ntype, file_ns::node_init); + node_type_storage(&ntype, + "NodeGeometryCurveSplineType", + node_free_standard_storage, + node_copy_standard_storage); + ntype.draw_buttons = file_ns::node_layout; + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc new file mode 100644 index 00000000000..ae282017e0c --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc @@ -0,0 +1,366 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_task.hh" +#include "BLI_timeit.hh" + +#include "BKE_attribute_math.hh" +#include "BKE_spline.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_curve_subdivide_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input<decl::Int>(N_("Cuts")).default_value(1).min(0).max(1000).supports_field(); + b.add_output<decl::Geometry>(N_("Curve")); +} + +static Array<int> get_subdivided_offsets(const Spline &spline, + const VArray<int> &cuts, + const int spline_offset) +{ + Array<int> offsets(spline.segments_size() + 1); + int offset = 0; + for (const int i : IndexRange(spline.segments_size())) { + offsets[i] = offset; + offset = offset + std::max(cuts[spline_offset + i], 0) + 1; + } + offsets.last() = offset; + return offsets; +} + +template<typename T> +static void subdivide_attribute(Span<T> src, + const Span<int> offsets, + const bool is_cyclic, + MutableSpan<T> dst) +{ + const int src_size = src.size(); + threading::parallel_for(IndexRange(src_size - 1), 1024, [&](IndexRange range) { + for (const int i : range) { + const int cuts = offsets[i + 1] - offsets[i]; + dst[offsets[i]] = src[i]; + const float factor_delta = cuts == 0 ? 1.0f : 1.0f / cuts; + for (const int cut : IndexRange(cuts)) { + const float factor = cut * factor_delta; + dst[offsets[i] + cut] = attribute_math::mix2(factor, src[i], src[i + 1]); + } + } + }); + + if (is_cyclic) { + const int i = src_size - 1; + const int cuts = offsets[i + 1] - offsets[i]; + dst[offsets[i]] = src.last(); + const float factor_delta = cuts == 0 ? 1.0f : 1.0f / cuts; + for (const int cut : IndexRange(cuts)) { + const float factor = cut * factor_delta; + dst[offsets[i] + cut] = attribute_math::mix2(factor, src.last(), src.first()); + } + } + else { + dst.last() = src.last(); + } +} + +/** + * In order to generate a Bezier spline with the same shape as the input spline, apply the + * De Casteljau algorithm iteratively for the provided number of cuts, constantly updating the + * previous result point's right handle and the left handle at the end of the segment. + * + * \note Non-vector segments in the result spline are given free handles. This could possibly be + * improved with another pass that sets handles to aligned where possible, but currently that does + * not provide much benefit for the increased complexity. + */ +static void subdivide_bezier_segment(const BezierSpline &src, + const int index, + const int offset, + const int result_size, + Span<float3> src_positions, + Span<float3> src_handles_left, + Span<float3> src_handles_right, + MutableSpan<float3> dst_positions, + MutableSpan<float3> dst_handles_left, + MutableSpan<float3> dst_handles_right, + MutableSpan<BezierSpline::HandleType> dst_type_left, + MutableSpan<BezierSpline::HandleType> dst_type_right) +{ + const bool is_last_cyclic_segment = index == (src.size() - 1); + const int next_index = is_last_cyclic_segment ? 0 : index + 1; + + /* The first point in the segment is always copied. */ + dst_positions[offset] = src_positions[index]; + + if (src.segment_is_vector(index)) { + if (is_last_cyclic_segment) { + dst_type_left.first() = BezierSpline::HandleType::Vector; + } + dst_type_left.slice(offset + 1, result_size).fill(BezierSpline::HandleType::Vector); + dst_type_right.slice(offset, result_size).fill(BezierSpline::HandleType::Vector); + + const float factor_delta = 1.0f / result_size; + for (const int cut : IndexRange(result_size)) { + const float factor = cut * factor_delta; + dst_positions[offset + cut] = attribute_math::mix2( + factor, src_positions[index], src_positions[next_index]); + } + } + else { + if (is_last_cyclic_segment) { + dst_type_left.first() = BezierSpline::HandleType::Free; + } + dst_type_left.slice(offset + 1, result_size).fill(BezierSpline::HandleType::Free); + dst_type_right.slice(offset, result_size).fill(BezierSpline::HandleType::Free); + + const int i_segment_last = is_last_cyclic_segment ? 0 : offset + result_size; + + /* Create a Bezier segment to update iteratively for every subdivision + * and references to the meaningful values for ease of use. */ + BezierSpline temp; + temp.resize(2); + float3 &segment_start = temp.positions().first(); + float3 &segment_end = temp.positions().last(); + float3 &handle_prev = temp.handle_positions_right().first(); + float3 &handle_next = temp.handle_positions_left().last(); + segment_start = src_positions[index]; + segment_end = src_positions[next_index]; + handle_prev = src_handles_right[index]; + handle_next = src_handles_left[next_index]; + + for (const int cut : IndexRange(result_size - 1)) { + const float parameter = 1.0f / (result_size - cut); + const BezierSpline::InsertResult insert = temp.calculate_segment_insertion(0, 1, parameter); + + /* Copy relevant temporary data to the result. */ + dst_handles_right[offset + cut] = insert.handle_prev; + dst_handles_left[offset + cut + 1] = insert.left_handle; + dst_positions[offset + cut + 1] = insert.position; + + /* Update the segment to prepare it for the next subdivision. */ + segment_start = insert.position; + handle_prev = insert.right_handle; + handle_next = insert.handle_next; + } + + /* Copy the handles for the last segment from the temporary spline. */ + dst_handles_right[offset + result_size - 1] = handle_prev; + dst_handles_left[i_segment_last] = handle_next; + } +} + +static void subdivide_bezier_spline(const BezierSpline &src, + const Span<int> offsets, + BezierSpline &dst) +{ + Span<float3> src_positions = src.positions(); + Span<float3> src_handles_left = src.handle_positions_left(); + Span<float3> src_handles_right = src.handle_positions_right(); + MutableSpan<float3> dst_positions = dst.positions(); + MutableSpan<float3> dst_handles_left = dst.handle_positions_left(); + MutableSpan<float3> dst_handles_right = dst.handle_positions_right(); + MutableSpan<BezierSpline::HandleType> dst_type_left = dst.handle_types_left(); + MutableSpan<BezierSpline::HandleType> dst_type_right = dst.handle_types_right(); + + threading::parallel_for(IndexRange(src.size() - 1), 512, [&](IndexRange range) { + for (const int i : range) { + subdivide_bezier_segment(src, + i, + offsets[i], + offsets[i + 1] - offsets[i], + src_positions, + src_handles_left, + src_handles_right, + dst_positions, + dst_handles_left, + dst_handles_right, + dst_type_left, + dst_type_right); + } + }); + + if (src.is_cyclic()) { + const int i_last = src.size() - 1; + subdivide_bezier_segment(src, + i_last, + offsets[i_last], + offsets.last() - offsets[i_last], + src_positions, + src_handles_left, + src_handles_right, + dst_positions, + dst_handles_left, + dst_handles_right, + dst_type_left, + dst_type_right); + } + else { + dst_positions.last() = src_positions.last(); + } +} + +static void subdivide_builtin_attributes(const Spline &src_spline, + const Span<int> offsets, + Spline &dst_spline) +{ + const bool is_cyclic = src_spline.is_cyclic(); + subdivide_attribute<float>(src_spline.radii(), offsets, is_cyclic, dst_spline.radii()); + subdivide_attribute<float>(src_spline.tilts(), offsets, is_cyclic, dst_spline.tilts()); + switch (src_spline.type()) { + case Spline::Type::Poly: { + const PolySpline &src = static_cast<const PolySpline &>(src_spline); + PolySpline &dst = static_cast<PolySpline &>(dst_spline); + subdivide_attribute<float3>(src.positions(), offsets, is_cyclic, dst.positions()); + break; + } + case Spline::Type::Bezier: { + const BezierSpline &src = static_cast<const BezierSpline &>(src_spline); + BezierSpline &dst = static_cast<BezierSpline &>(dst_spline); + subdivide_bezier_spline(src, offsets, dst); + dst.mark_cache_invalid(); + break; + } + case Spline::Type::NURBS: { + const NURBSpline &src = static_cast<const NURBSpline &>(src_spline); + NURBSpline &dst = static_cast<NURBSpline &>(dst_spline); + subdivide_attribute<float3>(src.positions(), offsets, is_cyclic, dst.positions()); + subdivide_attribute<float>(src.weights(), offsets, is_cyclic, dst.weights()); + break; + } + } +} + +static void subdivide_dynamic_attributes(const Spline &src_spline, + const Span<int> offsets, + Spline &dst_spline) +{ + const bool is_cyclic = src_spline.is_cyclic(); + src_spline.attributes.foreach_attribute( + [&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + std::optional<GSpan> src = src_spline.attributes.get_for_read(attribute_id); + BLI_assert(src); + + if (!dst_spline.attributes.create(attribute_id, meta_data.data_type)) { + /* Since the source spline of the same type had the attribute, adding it should work. */ + BLI_assert_unreachable(); + } + + std::optional<GMutableSpan> dst = dst_spline.attributes.get_for_write(attribute_id); + BLI_assert(dst); + + attribute_math::convert_to_static_type(dst->type(), [&](auto dummy) { + using T = decltype(dummy); + subdivide_attribute<T>(src->typed<T>(), offsets, is_cyclic, dst->typed<T>()); + }); + return true; + }, + ATTR_DOMAIN_POINT); +} + +static SplinePtr subdivide_spline(const Spline &spline, + const VArray<int> &cuts, + const int spline_offset) +{ + if (spline.size() <= 1) { + return spline.copy(); + } + + /* Since we expect to access each value many times, it should be worth it to make sure count + * of cuts is a real span (especially considering the note below). Using the offset at each + * point facilitates subdividing in parallel later. */ + Array<int> offsets = get_subdivided_offsets(spline, cuts, spline_offset); + const int result_size = offsets.last() + int(!spline.is_cyclic()); + SplinePtr new_spline = spline.copy_only_settings(); + new_spline->resize(result_size); + subdivide_builtin_attributes(spline, offsets, *new_spline); + subdivide_dynamic_attributes(spline, offsets, *new_spline); + return new_spline; +} + +/** + * \note Passing the virtual array for the entire spline is possibly quite inefficient here when + * the attribute was on the point domain and stored separately for each spline already, and it + * prevents some other optimizations like skipping splines with a single attribute value of < 1. + * However, it allows the node to access builtin attribute easily, so it the makes most sense this + * way until the attribute API is refactored. + */ +static std::unique_ptr<CurveEval> subdivide_curve(const CurveEval &input_curve, + const VArray<int> &cuts) +{ + const Array<int> control_point_offsets = input_curve.control_point_offsets(); + const Span<SplinePtr> input_splines = input_curve.splines(); + + std::unique_ptr<CurveEval> output_curve = std::make_unique<CurveEval>(); + output_curve->resize(input_splines.size()); + output_curve->attributes = input_curve.attributes; + MutableSpan<SplinePtr> output_splines = output_curve->splines(); + + threading::parallel_for(input_splines.index_range(), 128, [&](IndexRange range) { + for (const int i : range) { + output_splines[i] = subdivide_spline(*input_splines[i], cuts, control_point_offsets[i]); + } + }); + + return output_curve; +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); + Field<int> cuts_field = params.extract_input<Field<int>>("Cuts"); + + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (!geometry_set.has_curve()) { + return; + } + + const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>(); + GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; + const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); + + fn::FieldEvaluator evaluator{field_context, domain_size}; + evaluator.add(cuts_field); + evaluator.evaluate(); + const VArray<int> &cuts = evaluator.get_evaluated<int>(0); + + if (cuts.is_single() && cuts.get_internal_single() < 1) { + return; + } + + std::unique_ptr<CurveEval> output_curve = subdivide_curve(*component.get_for_read(), cuts); + geometry_set.replace_curve(output_curve.release()); + }); + params.set_output("Curve", geometry_set); +} + +} // namespace blender::nodes::node_geo_curve_subdivide_cc + +void register_node_type_geo_curve_subdivide() +{ + namespace file_ns = blender::nodes::node_geo_curve_subdivide_cc; + + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_SUBDIVIDE_CURVE, "Subdivide Curve", NODE_CLASS_GEOMETRY); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc index f7cef9bbf63..ef4fc51d1b3 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc @@ -23,71 +23,65 @@ #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_curve_to_mesh_cc { -static void geo_node_curve_to_mesh_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Curve"); - b.add_input<decl::Geometry>("Profile Curve"); - b.add_output<decl::Geometry>("Mesh"); + b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input<decl::Geometry>(N_("Profile Curve")) + .only_realized_data() + .supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input<decl::Bool>(N_("Fill Caps")) + .description( + N_("If the profile spline is cyclic, fill the ends of the generated mesh with N-gons")); + b.add_output<decl::Geometry>(N_("Mesh")); } static void geometry_set_curve_to_mesh(GeometrySet &geometry_set, const GeometrySet &profile_set, - const GeoNodeExecParams ¶ms) + const bool fill_caps) { - if (!geometry_set.has_curve()) { - if (!geometry_set.is_empty()) { - params.error_message_add(NodeWarningType::Warning, - TIP_("No curve data available in curve input")); - } - return; - } - + const CurveEval *curve = geometry_set.get_curve_for_read(); const CurveEval *profile_curve = profile_set.get_curve_for_read(); if (profile_curve == nullptr) { - Mesh *mesh = bke::curve_to_wire_mesh(*geometry_set.get_curve_for_read()); + Mesh *mesh = bke::curve_to_wire_mesh(*curve); geometry_set.replace_mesh(mesh); } else { - Mesh *mesh = bke::curve_to_mesh_sweep(*geometry_set.get_curve_for_read(), *profile_curve); + Mesh *mesh = bke::curve_to_mesh_sweep(*curve, *profile_curve, fill_caps); geometry_set.replace_mesh(mesh); } } -static void geo_node_curve_to_mesh_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { GeometrySet curve_set = params.extract_input<GeometrySet>("Curve"); GeometrySet profile_set = params.extract_input<GeometrySet>("Profile Curve"); + const bool fill_caps = params.extract_input<bool>("Fill Caps"); - if (profile_set.has_instances()) { - params.error_message_add(NodeWarningType::Error, - TIP_("Instances are not supported in the profile input")); - params.set_output("Mesh", GeometrySet()); - return; - } - - if (!profile_set.has_curve() && !profile_set.is_empty()) { - params.error_message_add(NodeWarningType::Warning, - TIP_("No curve data available in the profile input")); - } - + bool has_curve = false; curve_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - geometry_set_curve_to_mesh(geometry_set, profile_set, params); + if (geometry_set.has_curve()) { + has_curve = true; + geometry_set_curve_to_mesh(geometry_set, profile_set, fill_caps); + } + geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES}); }); params.set_output("Mesh", std::move(curve_set)); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_curve_to_mesh_cc void register_node_type_geo_curve_to_mesh() { + namespace file_ns = blender::nodes::node_geo_curve_to_mesh_cc; + static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_CURVE_TO_MESH, "Curve to Mesh", NODE_CLASS_GEOMETRY, 0); - ntype.declare = blender::nodes::geo_node_curve_to_mesh_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_curve_to_mesh_exec; + geo_node_type_base(&ntype, GEO_NODE_CURVE_TO_MESH, "Curve to Mesh", NODE_CLASS_GEOMETRY); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc new file mode 100644 index 00000000000..19efd4b7508 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc @@ -0,0 +1,414 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_array.hh" +#include "BLI_task.hh" +#include "BLI_timeit.hh" + +#include "BKE_pointcloud.h" +#include "BKE_spline.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes { +void curve_create_default_rotation_attribute(Span<float3> tangents, + Span<float3> normals, + MutableSpan<float3> rotations) +{ + threading::parallel_for(IndexRange(rotations.size()), 512, [&](IndexRange range) { + for (const int i : range) { + rotations[i] = + float4x4::from_normalized_axis_data({0, 0, 0}, normals[i], tangents[i]).to_euler(); + } + }); +} +} // namespace blender::nodes + +namespace blender::nodes::node_geo_curve_to_points_cc { + +NODE_STORAGE_FUNCS(NodeGeometryCurveToPoints) + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input<decl::Int>(N_("Count")) + .default_value(10) + .min(2) + .max(100000) + .make_available( + [](bNode &node) { node_storage(node).mode = GEO_NODE_CURVE_RESAMPLE_COUNT; }); + b.add_input<decl::Float>(N_("Length")) + .default_value(0.1f) + .min(0.001f) + .subtype(PROP_DISTANCE) + .make_available( + [](bNode &node) { node_storage(node).mode = GEO_NODE_CURVE_RESAMPLE_LENGTH; }); + b.add_output<decl::Geometry>(N_("Points")); + b.add_output<decl::Vector>(N_("Tangent")).field_source(); + b.add_output<decl::Vector>(N_("Normal")).field_source(); + b.add_output<decl::Vector>(N_("Rotation")).field_source(); +} + +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "mode", 0, "", ICON_NONE); +} + +static void node_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryCurveToPoints *data = MEM_cnew<NodeGeometryCurveToPoints>(__func__); + + data->mode = GEO_NODE_CURVE_RESAMPLE_COUNT; + node->storage = data; +} + +static void node_update(bNodeTree *ntree, bNode *node) +{ + const NodeGeometryCurveToPoints &storage = node_storage(*node); + const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)storage.mode; + + bNodeSocket *count_socket = ((bNodeSocket *)node->inputs.first)->next; + bNodeSocket *length_socket = count_socket->next; + + nodeSetSocketAvailability(ntree, count_socket, mode == GEO_NODE_CURVE_RESAMPLE_COUNT); + nodeSetSocketAvailability(ntree, length_socket, mode == GEO_NODE_CURVE_RESAMPLE_LENGTH); +} + +static Array<int> calculate_spline_point_offsets(GeoNodeExecParams ¶ms, + const GeometryNodeCurveResampleMode mode, + const CurveEval &curve, + const Span<SplinePtr> splines) +{ + const int size = curve.splines().size(); + switch (mode) { + case GEO_NODE_CURVE_RESAMPLE_COUNT: { + const int count = params.get_input<int>("Count"); + if (count < 1) { + return {0}; + } + Array<int> offsets(size + 1); + int offset = 0; + for (const int i : IndexRange(size)) { + offsets[i] = offset; + if (splines[i]->evaluated_points_size() > 0) { + offset += count; + } + } + offsets.last() = offset; + return offsets; + } + case GEO_NODE_CURVE_RESAMPLE_LENGTH: { + /* Don't allow asymptotic count increase for low resolution values. */ + const float resolution = std::max(params.get_input<float>("Length"), 0.0001f); + Array<int> offsets(size + 1); + int offset = 0; + for (const int i : IndexRange(size)) { + offsets[i] = offset; + if (splines[i]->evaluated_points_size() > 0) { + offset += splines[i]->length() / resolution + 1; + } + } + offsets.last() = offset; + return offsets; + } + case GEO_NODE_CURVE_RESAMPLE_EVALUATED: { + return curve.evaluated_point_offsets(); + } + } + BLI_assert_unreachable(); + return {0}; +} + +/** + * \note: Relies on the fact that all attributes on point clouds are stored contiguously. + */ +static GMutableSpan ensure_point_attribute(PointCloudComponent &points, + const AttributeIDRef &attribute_id, + const CustomDataType data_type) +{ + points.attribute_try_create(attribute_id, ATTR_DOMAIN_POINT, data_type, AttributeInitDefault()); + WriteAttributeLookup attribute = points.attribute_try_get_for_write(attribute_id); + BLI_assert(attribute); + return attribute.varray.get_internal_span(); +} + +template<typename T> +static MutableSpan<T> ensure_point_attribute(PointCloudComponent &points, + const AttributeIDRef &attribute_id) +{ + GMutableSpan attribute = ensure_point_attribute( + points, attribute_id, bke::cpp_type_to_custom_data_type(CPPType::get<T>())); + return attribute.typed<T>(); +} + +namespace { +struct AnonymousAttributeIDs { + StrongAnonymousAttributeID tangent_id; + StrongAnonymousAttributeID normal_id; + StrongAnonymousAttributeID rotation_id; +}; + +struct ResultAttributes { + MutableSpan<float3> positions; + MutableSpan<float> radii; + + Map<AttributeIDRef, GMutableSpan> point_attributes; + + MutableSpan<float3> tangents; + MutableSpan<float3> normals; + MutableSpan<float3> rotations; +}; +} // namespace + +static ResultAttributes create_attributes_for_transfer(PointCloudComponent &points, + const CurveEval &curve, + const AnonymousAttributeIDs &attributes) +{ + ResultAttributes outputs; + + outputs.positions = ensure_point_attribute<float3>(points, "position"); + outputs.radii = ensure_point_attribute<float>(points, "radius"); + + if (attributes.tangent_id) { + outputs.tangents = ensure_point_attribute<float3>(points, attributes.tangent_id.get()); + } + if (attributes.normal_id) { + outputs.normals = ensure_point_attribute<float3>(points, attributes.normal_id.get()); + } + if (attributes.rotation_id) { + outputs.rotations = ensure_point_attribute<float3>(points, attributes.rotation_id.get()); + } + + /* Because of the invariants of the curve component, we use the attributes of the first spline + * as a representative for the attribute meta data all splines. Attributes from the spline domain + * are handled separately. */ + curve.splines().first()->attributes.foreach_attribute( + [&](const AttributeIDRef &id, const AttributeMetaData &meta_data) { + if (id.should_be_kept()) { + outputs.point_attributes.add_new( + id, ensure_point_attribute(points, id, meta_data.data_type)); + } + return true; + }, + ATTR_DOMAIN_POINT); + + return outputs; +} + +/** + * TODO: For non-poly splines, this has double copies that could be avoided as part + * of a general look at optimizing uses of #Spline::interpolate_to_evaluated. + */ +static void copy_evaluated_point_attributes(const Span<SplinePtr> splines, + const Span<int> offsets, + ResultAttributes &data) +{ + threading::parallel_for(splines.index_range(), 64, [&](IndexRange range) { + for (const int i : range) { + const Spline &spline = *splines[i]; + const int offset = offsets[i]; + const int size = offsets[i + 1] - offsets[i]; + + data.positions.slice(offset, size).copy_from(spline.evaluated_positions()); + spline.interpolate_to_evaluated(spline.radii()).materialize(data.radii.slice(offset, size)); + + for (const Map<AttributeIDRef, GMutableSpan>::Item item : data.point_attributes.items()) { + const AttributeIDRef attribute_id = item.key; + const GMutableSpan dst = item.value; + + BLI_assert(spline.attributes.get_for_read(attribute_id)); + GSpan spline_span = *spline.attributes.get_for_read(attribute_id); + + spline.interpolate_to_evaluated(spline_span).materialize(dst.slice(offset, size).data()); + } + + if (!data.tangents.is_empty()) { + data.tangents.slice(offset, size).copy_from(spline.evaluated_tangents()); + } + if (!data.normals.is_empty()) { + data.normals.slice(offset, size).copy_from(spline.evaluated_normals()); + } + } + }); +} + +static void copy_uniform_sample_point_attributes(const Span<SplinePtr> splines, + const Span<int> offsets, + ResultAttributes &data) +{ + threading::parallel_for(splines.index_range(), 64, [&](IndexRange range) { + for (const int i : range) { + const Spline &spline = *splines[i]; + const int offset = offsets[i]; + const int size = offsets[i + 1] - offsets[i]; + if (size == 0) { + continue; + } + + const Array<float> uniform_samples = spline.sample_uniform_index_factors(size); + + spline.sample_with_index_factors<float3>( + spline.evaluated_positions(), uniform_samples, data.positions.slice(offset, size)); + spline.sample_with_index_factors<float>(spline.interpolate_to_evaluated(spline.radii()), + uniform_samples, + data.radii.slice(offset, size)); + + for (const Map<AttributeIDRef, GMutableSpan>::Item item : data.point_attributes.items()) { + const AttributeIDRef attribute_id = item.key; + const GMutableSpan dst = item.value; + + BLI_assert(spline.attributes.get_for_read(attribute_id)); + GSpan spline_span = *spline.attributes.get_for_read(attribute_id); + + spline.sample_with_index_factors(spline.interpolate_to_evaluated(spline_span), + uniform_samples, + dst.slice(offset, size)); + } + + if (!data.tangents.is_empty()) { + spline.sample_with_index_factors<float3>( + spline.evaluated_tangents(), uniform_samples, data.tangents.slice(offset, size)); + for (float3 &tangent : data.tangents) { + tangent = math::normalize(tangent); + } + } + + if (!data.normals.is_empty()) { + spline.sample_with_index_factors<float3>( + spline.evaluated_normals(), uniform_samples, data.normals.slice(offset, size)); + for (float3 &normals : data.normals) { + normals = math::normalize(normals); + } + } + } + }); +} + +static void copy_spline_domain_attributes(const CurveEval &curve, + const Span<int> offsets, + PointCloudComponent &points) +{ + curve.attributes.foreach_attribute( + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + const GSpan curve_attribute = *curve.attributes.get_for_read(attribute_id); + const CPPType &type = curve_attribute.type(); + const GMutableSpan dst = ensure_point_attribute(points, attribute_id, meta_data.data_type); + + for (const int i : curve.splines().index_range()) { + const int offset = offsets[i]; + const int size = offsets[i + 1] - offsets[i]; + type.fill_assign_n(curve_attribute[i], dst[offset], size); + } + + return true; + }, + ATTR_DOMAIN_CURVE); +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + const NodeGeometryCurveToPoints &storage = node_storage(params.node()); + const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)storage.mode; + GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); + + AnonymousAttributeIDs attribute_outputs; + attribute_outputs.tangent_id = StrongAnonymousAttributeID("Tangent"); + attribute_outputs.normal_id = StrongAnonymousAttributeID("Normal"); + attribute_outputs.rotation_id = StrongAnonymousAttributeID("Rotation"); + + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (!geometry_set.has_curve()) { + geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); + return; + } + const CurveEval &curve = *geometry_set.get_curve_for_read(); + const Span<SplinePtr> splines = curve.splines(); + curve.assert_valid_point_attributes(); + + const Array<int> offsets = calculate_spline_point_offsets(params, mode, curve, splines); + const int total_size = offsets.last(); + if (total_size == 0) { + geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); + return; + } + + geometry_set.replace_pointcloud(BKE_pointcloud_new_nomain(total_size)); + PointCloudComponent &points = geometry_set.get_component_for_write<PointCloudComponent>(); + ResultAttributes point_attributes = create_attributes_for_transfer( + points, curve, attribute_outputs); + + switch (mode) { + case GEO_NODE_CURVE_RESAMPLE_COUNT: + case GEO_NODE_CURVE_RESAMPLE_LENGTH: + copy_uniform_sample_point_attributes(splines, offsets, point_attributes); + break; + case GEO_NODE_CURVE_RESAMPLE_EVALUATED: + copy_evaluated_point_attributes(splines, offsets, point_attributes); + break; + } + + copy_spline_domain_attributes(curve, offsets, points); + + if (!point_attributes.rotations.is_empty()) { + curve_create_default_rotation_attribute( + point_attributes.tangents, point_attributes.normals, point_attributes.rotations); + } + + geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES, GEO_COMPONENT_TYPE_POINT_CLOUD}); + }); + + params.set_output("Points", std::move(geometry_set)); + if (attribute_outputs.tangent_id) { + params.set_output( + "Tangent", + AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.tangent_id), + params.attribute_producer_name())); + } + if (attribute_outputs.normal_id) { + params.set_output( + "Normal", + AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.normal_id), + params.attribute_producer_name())); + } + if (attribute_outputs.rotation_id) { + params.set_output( + "Rotation", + AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.rotation_id), + params.attribute_producer_name())); + } +} + +} // namespace blender::nodes::node_geo_curve_to_points_cc + +void register_node_type_geo_curve_to_points() +{ + namespace file_ns = blender::nodes::node_geo_curve_to_points_cc; + + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_CURVE_TO_POINTS, "Curve to Points", NODE_CLASS_GEOMETRY); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.draw_buttons = file_ns::node_layout; + node_type_storage( + &ntype, "NodeGeometryCurveToPoints", node_free_standard_storage, node_copy_standard_storage); + node_type_init(&ntype, file_ns::node_init); + node_type_update(&ntype, file_ns::node_update); + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc index 97043980899..359863d39e0 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc @@ -20,50 +20,105 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "NOD_socket_search_link.hh" + #include "node_geometry_util.hh" +namespace blender::nodes::node_geo_curve_trim_cc { + using blender::attribute_math::mix2; -namespace blender::nodes { +NODE_STORAGE_FUNCS(NodeGeometryCurveTrim) -static void geo_node_curve_trim_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Curve"); - b.add_input<decl::Float>("Start").min(0.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_input<decl::Float>("End").min(0.0f).max(1.0f).default_value(1.0f).subtype(PROP_FACTOR); - b.add_input<decl::Float>("Start", "Start_001").min(0.0f).subtype(PROP_DISTANCE); - b.add_input<decl::Float>("End", "End_001").min(0.0f).default_value(1.0f).subtype(PROP_DISTANCE); - b.add_output<decl::Geometry>("Curve"); + b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input<decl::Float>(N_("Start")) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR) + .make_available([](bNode &node) { node_storage(node).mode = GEO_NODE_CURVE_SAMPLE_FACTOR; }) + .supports_field(); + b.add_input<decl::Float>(N_("End")) + .min(0.0f) + .max(1.0f) + .default_value(1.0f) + .subtype(PROP_FACTOR) + .make_available([](bNode &node) { node_storage(node).mode = GEO_NODE_CURVE_SAMPLE_FACTOR; }) + .supports_field(); + b.add_input<decl::Float>(N_("Start"), "Start_001") + .min(0.0f) + .subtype(PROP_DISTANCE) + .make_available([](bNode &node) { node_storage(node).mode = GEO_NODE_CURVE_SAMPLE_LENGTH; }) + .supports_field(); + b.add_input<decl::Float>(N_("End"), "End_001") + .min(0.0f) + .default_value(1.0f) + .subtype(PROP_DISTANCE) + .make_available([](bNode &node) { node_storage(node).mode = GEO_NODE_CURVE_SAMPLE_LENGTH; }) + .supports_field(); + b.add_output<decl::Geometry>(N_("Curve")); } -static void geo_node_curve_trim_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); } -static void geo_node_curve_trim_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeGeometryCurveTrim *data = (NodeGeometryCurveTrim *)MEM_callocN(sizeof(NodeGeometryCurveTrim), - __func__); + NodeGeometryCurveTrim *data = MEM_cnew<NodeGeometryCurveTrim>(__func__); data->mode = GEO_NODE_CURVE_SAMPLE_FACTOR; node->storage = data; } -static void geo_node_curve_trim_update(bNodeTree *UNUSED(ntree), bNode *node) +static void node_update(bNodeTree *ntree, bNode *node) { - const NodeGeometryCurveTrim &node_storage = *(NodeGeometryCurveTrim *)node->storage; - const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode; + const NodeGeometryCurveTrim &storage = node_storage(*node); + const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)storage.mode; bNodeSocket *start_fac = ((bNodeSocket *)node->inputs.first)->next; bNodeSocket *end_fac = start_fac->next; bNodeSocket *start_len = end_fac->next; bNodeSocket *end_len = start_len->next; - nodeSetSocketAvailability(start_fac, mode == GEO_NODE_CURVE_SAMPLE_FACTOR); - nodeSetSocketAvailability(end_fac, mode == GEO_NODE_CURVE_SAMPLE_FACTOR); - nodeSetSocketAvailability(start_len, mode == GEO_NODE_CURVE_SAMPLE_LENGTH); - nodeSetSocketAvailability(end_len, mode == GEO_NODE_CURVE_SAMPLE_LENGTH); + nodeSetSocketAvailability(ntree, start_fac, mode == GEO_NODE_CURVE_SAMPLE_FACTOR); + nodeSetSocketAvailability(ntree, end_fac, mode == GEO_NODE_CURVE_SAMPLE_FACTOR); + nodeSetSocketAvailability(ntree, start_len, mode == GEO_NODE_CURVE_SAMPLE_LENGTH); + nodeSetSocketAvailability(ntree, end_len, mode == GEO_NODE_CURVE_SAMPLE_LENGTH); +} + +class SocketSearchOp { + public: + StringRef socket_name; + GeometryNodeCurveSampleMode mode; + void operator()(LinkSearchOpParams ¶ms) + { + bNode &node = params.add_node("GeometryNodeTrimCurve"); + node_storage(node).mode = mode; + params.update_and_connect_available_socket(node, socket_name); + } +}; + +static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) +{ + const NodeDeclaration &declaration = *params.node_type().fixed_declaration; + + search_link_ops_for_declarations(params, declaration.outputs()); + search_link_ops_for_declarations(params, declaration.inputs().take_front(1)); + + if (params.in_out() == SOCK_IN) { + if (params.node_tree().typeinfo->validate_link( + static_cast<eNodeSocketDatatype>(params.other_socket().type), SOCK_FLOAT)) { + params.add_item(IFACE_("Start (Factor)"), + SocketSearchOp{"Start", GEO_NODE_CURVE_SAMPLE_FACTOR}); + params.add_item(IFACE_("End (Factor)"), SocketSearchOp{"End", GEO_NODE_CURVE_SAMPLE_FACTOR}); + params.add_item(IFACE_("Start (Length)"), + SocketSearchOp{"Start", GEO_NODE_CURVE_SAMPLE_LENGTH}); + params.add_item(IFACE_("End (Length)"), SocketSearchOp{"End", GEO_NODE_CURVE_SAMPLE_LENGTH}); + } + } } struct TrimLocation { @@ -123,19 +178,19 @@ static void linear_trim_to_output_data(const TrimLocation &start, /* Look up the control points to the left and right of factor, and get the factor between them. */ static TrimLocation lookup_control_point_position(const Spline::LookupResult &lookup, - Span<int> control_point_offsets) + const BezierSpline &spline) { - const int *left_offset = std::lower_bound( - control_point_offsets.begin(), control_point_offsets.end(), lookup.evaluated_index); - const int index = left_offset - control_point_offsets.begin(); - const int left = control_point_offsets[index] > lookup.evaluated_index ? index - 1 : index; - const int right = left + 1; - - const float factor = std::clamp( - (lookup.evaluated_index + lookup.factor - control_point_offsets[left]) / - (control_point_offsets[right] - control_point_offsets[left]), - 0.0f, - 1.0f); + Span<int> offsets = spline.control_point_offsets(); + + const int *offset = std::lower_bound(offsets.begin(), offsets.end(), lookup.evaluated_index); + const int index = offset - offsets.begin(); + + const int left = offsets[index] > lookup.evaluated_index ? index - 1 : index; + const int right = left == (spline.size() - 1) ? 0 : left + 1; + + const float offset_in_segment = lookup.evaluated_index + lookup.factor - offsets[left]; + const int segment_eval_size = offsets[left + 1] - offsets[left]; + const float factor = std::clamp(offset_in_segment / segment_eval_size, 0.0f, 1.0f); return {left, right, factor}; } @@ -204,9 +259,9 @@ static PolySpline trim_nurbs_spline(const Spline &spline, attribute_math::convert_to_static_type(src->type(), [&](auto dummy) { using T = decltype(dummy); - GVArray_Typed<T> eval_data = spline.interpolate_to_evaluated<T>(src->typed<T>()); + VArray<T> eval_data = spline.interpolate_to_evaluated<T>(src->typed<T>()); linear_trim_to_output_data<T>( - start, end, eval_data->get_internal_span(), dst->typed<T>()); + start, end, eval_data.get_internal_span(), dst->typed<T>()); }); return true; }, @@ -215,13 +270,13 @@ static PolySpline trim_nurbs_spline(const Spline &spline, linear_trim_to_output_data<float3>( start, end, spline.evaluated_positions(), new_spline.positions()); - GVArray_Typed<float> evaluated_radii = spline.interpolate_to_evaluated(spline.radii()); + VArray<float> evaluated_radii = spline.interpolate_to_evaluated(spline.radii()); linear_trim_to_output_data<float>( - start, end, evaluated_radii->get_internal_span(), new_spline.radii()); + start, end, evaluated_radii.get_internal_span(), new_spline.radii()); - GVArray_Typed<float> evaluated_tilts = spline.interpolate_to_evaluated(spline.tilts()); + VArray<float> evaluated_tilts = spline.interpolate_to_evaluated(spline.tilts()); linear_trim_to_output_data<float>( - start, end, evaluated_tilts->get_internal_span(), new_spline.tilts()); + start, end, evaluated_tilts.get_internal_span(), new_spline.tilts()); return new_spline; } @@ -235,10 +290,11 @@ static void trim_bezier_spline(Spline &spline, const Spline::LookupResult &end_lookup) { BezierSpline &bezier_spline = static_cast<BezierSpline &>(spline); - Span<int> control_offsets = bezier_spline.control_point_offsets(); - const TrimLocation start = lookup_control_point_position(start_lookup, control_offsets); - TrimLocation end = lookup_control_point_position(end_lookup, control_offsets); + const TrimLocation start = lookup_control_point_position(start_lookup, bezier_spline); + TrimLocation end = lookup_control_point_position(end_lookup, bezier_spline); + + const Span<int> control_offsets = bezier_spline.control_point_offsets(); /* The number of control points in the resulting spline. */ const int size = end.right_index - start.left_index + 1; @@ -320,97 +376,245 @@ static void trim_bezier_spline(Spline &spline, bezier_spline.resize(size); } +static void trim_spline(SplinePtr &spline, + const Spline::LookupResult start, + const Spline::LookupResult end) +{ + switch (spline->type()) { + case Spline::Type::Bezier: + trim_bezier_spline(*spline, start, end); + break; + case Spline::Type::Poly: + trim_poly_spline(*spline, start, end); + break; + case Spline::Type::NURBS: + spline = std::make_unique<PolySpline>(trim_nurbs_spline(*spline, start, end)); + break; + } + spline->mark_cache_invalid(); +} + +template<typename T> +static void to_single_point_data(const TrimLocation &trim, MutableSpan<T> data) +{ + data.first() = mix2<T>(trim.factor, data[trim.left_index], data[trim.right_index]); +} +template<typename T> +static void to_single_point_data(const TrimLocation &trim, Span<T> src, MutableSpan<T> dst) +{ + dst.first() = mix2<T>(trim.factor, src[trim.left_index], src[trim.right_index]); +} + +static void to_single_point_bezier(Spline &spline, const Spline::LookupResult &lookup) +{ + BezierSpline &bezier = static_cast<BezierSpline &>(spline); + + const TrimLocation trim = lookup_control_point_position(lookup, bezier); + + const BezierSpline::InsertResult new_point = bezier.calculate_segment_insertion( + trim.left_index, trim.right_index, trim.factor); + bezier.positions().first() = new_point.position; + bezier.handle_types_left().first() = BezierSpline::HandleType::Free; + bezier.handle_types_right().first() = BezierSpline::HandleType::Free; + bezier.handle_positions_left().first() = new_point.left_handle; + bezier.handle_positions_right().first() = new_point.right_handle; + + to_single_point_data<float>(trim, bezier.radii()); + to_single_point_data<float>(trim, bezier.tilts()); + spline.attributes.foreach_attribute( + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &UNUSED(meta_data)) { + std::optional<GMutableSpan> data = spline.attributes.get_for_write(attribute_id); + attribute_math::convert_to_static_type(data->type(), [&](auto dummy) { + using T = decltype(dummy); + to_single_point_data<T>(trim, data->typed<T>()); + }); + return true; + }, + ATTR_DOMAIN_POINT); + spline.resize(1); +} + +static void to_single_point_poly(Spline &spline, const Spline::LookupResult &lookup) +{ + const TrimLocation trim{lookup.evaluated_index, lookup.next_evaluated_index, lookup.factor}; + + to_single_point_data<float3>(trim, spline.positions()); + to_single_point_data<float>(trim, spline.radii()); + to_single_point_data<float>(trim, spline.tilts()); + spline.attributes.foreach_attribute( + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &UNUSED(meta_data)) { + std::optional<GMutableSpan> data = spline.attributes.get_for_write(attribute_id); + attribute_math::convert_to_static_type(data->type(), [&](auto dummy) { + using T = decltype(dummy); + to_single_point_data<T>(trim, data->typed<T>()); + }); + return true; + }, + ATTR_DOMAIN_POINT); + spline.resize(1); +} + +static PolySpline to_single_point_nurbs(const Spline &spline, const Spline::LookupResult &lookup) +{ + /* Since this outputs a poly spline, the evaluated indices are the control point indices. */ + const TrimLocation trim{lookup.evaluated_index, lookup.next_evaluated_index, lookup.factor}; + + /* Create poly spline and copy trimmed data to it. */ + PolySpline new_spline; + new_spline.resize(1); + + spline.attributes.foreach_attribute( + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + new_spline.attributes.create(attribute_id, meta_data.data_type); + std::optional<GSpan> src = spline.attributes.get_for_read(attribute_id); + std::optional<GMutableSpan> dst = new_spline.attributes.get_for_write(attribute_id); + attribute_math::convert_to_static_type(src->type(), [&](auto dummy) { + using T = decltype(dummy); + VArray<T> eval_data = spline.interpolate_to_evaluated<T>(src->typed<T>()); + to_single_point_data<T>(trim, eval_data.get_internal_span(), dst->typed<T>()); + }); + return true; + }, + ATTR_DOMAIN_POINT); + + to_single_point_data<float3>(trim, spline.evaluated_positions(), new_spline.positions()); + + VArray<float> evaluated_radii = spline.interpolate_to_evaluated(spline.radii()); + to_single_point_data<float>(trim, evaluated_radii.get_internal_span(), new_spline.radii()); + + VArray<float> evaluated_tilts = spline.interpolate_to_evaluated(spline.tilts()); + to_single_point_data<float>(trim, evaluated_tilts.get_internal_span(), new_spline.tilts()); + + return new_spline; +} + +static void to_single_point_spline(SplinePtr &spline, const Spline::LookupResult &lookup) +{ + switch (spline->type()) { + case Spline::Type::Bezier: + to_single_point_bezier(*spline, lookup); + break; + case Spline::Type::Poly: + to_single_point_poly(*spline, lookup); + break; + case Spline::Type::NURBS: + spline = std::make_unique<PolySpline>(to_single_point_nurbs(*spline, lookup)); + break; + } +} + static void geometry_set_curve_trim(GeometrySet &geometry_set, const GeometryNodeCurveSampleMode mode, - const float start, - const float end) + Field<float> &start_field, + Field<float> &end_field) { if (!geometry_set.has_curve()) { return; } + CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>(); + GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE}; + const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE); + + fn::FieldEvaluator evaluator{field_context, domain_size}; + evaluator.add(start_field); + evaluator.add(end_field); + evaluator.evaluate(); + const blender::VArray<float> &starts = evaluator.get_evaluated<float>(0); + const blender::VArray<float> &ends = evaluator.get_evaluated<float>(1); + CurveEval &curve = *geometry_set.get_curve_for_write(); MutableSpan<SplinePtr> splines = curve.splines(); threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) { for (const int i : range) { - Spline &spline = *splines[i]; + SplinePtr &spline = splines[i]; - /* Currently this node doesn't support cyclic splines, it could in the future though. */ - if (spline.is_cyclic()) { + /* Currently trimming cyclic splines is not supported. It could be in the future though. */ + if (spline->is_cyclic()) { continue; } - /* Return a spline with one point instead of implicitly - * reversing the spline or switching the parameters. */ - if (end < start) { - spline.resize(1); + if (spline->evaluated_edges_size() == 0) { continue; } - const Spline::LookupResult start_lookup = - (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) ? - spline.lookup_evaluated_length(std::clamp(start, 0.0f, spline.length())) : - spline.lookup_evaluated_factor(std::clamp(start, 0.0f, 1.0f)); - const Spline::LookupResult end_lookup = - (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) ? - spline.lookup_evaluated_length(std::clamp(end, 0.0f, spline.length())) : - spline.lookup_evaluated_factor(std::clamp(end, 0.0f, 1.0f)); - - switch (spline.type()) { - case Spline::Type::Bezier: - trim_bezier_spline(spline, start_lookup, end_lookup); - break; - case Spline::Type::Poly: - trim_poly_spline(spline, start_lookup, end_lookup); - break; - case Spline::Type::NURBS: - splines[i] = std::make_unique<PolySpline>( - trim_nurbs_spline(spline, start_lookup, end_lookup)); - break; + const float length = spline->length(); + if (length == 0.0f) { + continue; + } + + const float start = starts[i]; + const float end = ends[i]; + + /* When the start and end samples are reversed, instead of implicitly reversing the spline + * or switching the parameters, create a single point spline with the end sample point. */ + if (end <= start) { + if (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) { + to_single_point_spline(spline, + spline->lookup_evaluated_length(std::clamp(start, 0.0f, length))); + } + else { + to_single_point_spline(spline, + spline->lookup_evaluated_factor(std::clamp(start, 0.0f, 1.0f))); + } + continue; + } + + if (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) { + trim_spline(spline, + spline->lookup_evaluated_length(std::clamp(start, 0.0f, length)), + spline->lookup_evaluated_length(std::clamp(end, 0.0f, length))); + } + else { + trim_spline(spline, + spline->lookup_evaluated_factor(std::clamp(start, 0.0f, 1.0f)), + spline->lookup_evaluated_factor(std::clamp(end, 0.0f, 1.0f))); } - splines[i]->mark_cache_invalid(); } }); } -static void geo_node_curve_trim_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { - const NodeGeometryCurveTrim &node_storage = *(NodeGeometryCurveTrim *)params.node().storage; - const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode; + const NodeGeometryCurveTrim &storage = node_storage(params.node()); + const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)storage.mode; GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); if (mode == GEO_NODE_CURVE_SAMPLE_FACTOR) { - const float start = params.extract_input<float>("Start"); - const float end = params.extract_input<float>("End"); + Field<float> start_field = params.extract_input<Field<float>>("Start"); + Field<float> end_field = params.extract_input<Field<float>>("End"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - geometry_set_curve_trim(geometry_set, mode, start, end); + geometry_set_curve_trim(geometry_set, mode, start_field, end_field); }); } else if (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) { - const float start = params.extract_input<float>("Start_001"); - const float end = params.extract_input<float>("End_001"); + Field<float> start_field = params.extract_input<Field<float>>("Start_001"); + Field<float> end_field = params.extract_input<Field<float>>("End_001"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - geometry_set_curve_trim(geometry_set, mode, start, end); + geometry_set_curve_trim(geometry_set, mode, start_field, end_field); }); } params.set_output("Curve", std::move(geometry_set)); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_curve_trim_cc void register_node_type_geo_curve_trim() { + namespace file_ns = blender::nodes::node_geo_curve_trim_cc; + static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_CURVE_TRIM, "Curve Trim", NODE_CLASS_GEOMETRY, 0); - ntype.geometry_node_execute = blender::nodes::geo_node_curve_trim_exec; - ntype.draw_buttons = blender::nodes::geo_node_curve_trim_layout; - ntype.declare = blender::nodes::geo_node_curve_trim_declare; + geo_node_type_base(&ntype, GEO_NODE_TRIM_CURVE, "Trim Curve", NODE_CLASS_GEOMETRY); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.draw_buttons = file_ns::node_layout; + ntype.declare = file_ns::node_declare; node_type_storage( &ntype, "NodeGeometryCurveTrim", node_free_standard_storage, node_copy_standard_storage); - node_type_init(&ntype, blender::nodes::geo_node_curve_trim_init); - node_type_update(&ntype, blender::nodes::geo_node_curve_trim_update); + node_type_init(&ntype, file_ns::node_init); + node_type_update(&ntype, file_ns::node_update); + ntype.gather_link_search_ops = file_ns::node_gather_link_searches; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc new file mode 100644 index 00000000000..8b762abd29b --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc @@ -0,0 +1,1399 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "BLI_array.hh" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_customdata.h" +#include "BKE_mesh.h" +#include "BKE_pointcloud.h" +#include "BKE_spline.hh" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_delete_geometry_cc { + +using blender::bke::CustomDataAttributes; + +template<typename T> +static void copy_data_based_on_mask(Span<T> data, MutableSpan<T> r_data, IndexMask mask) +{ + for (const int i_out : mask.index_range()) { + r_data[i_out] = data[mask[i_out]]; + } +} + +template<typename T> +static void copy_data_based_on_map(Span<T> src, MutableSpan<T> dst, Span<int> index_map) +{ + for (const int i_src : index_map.index_range()) { + const int i_dst = index_map[i_src]; + if (i_dst != -1) { + dst[i_dst] = src[i_src]; + } + } +} + +/** Utility function for making an IndexMask from a boolean selection. The indices vector should + * live at least as long as the returned IndexMask. + */ +static IndexMask index_mask_indices(Span<bool> mask, const bool invert, Vector<int64_t> &indices) +{ + for (const int i : mask.index_range()) { + if (mask[i] != invert) { + indices.append(i); + } + } + return IndexMask(indices); +} + +/** + * Copies the attributes with a domain in `domains` to `result_component`. + */ +static void copy_attributes(const Map<AttributeIDRef, AttributeKind> &attributes, + const GeometryComponent &in_component, + GeometryComponent &result_component, + const Span<AttributeDomain> domains) +{ + for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) { + const AttributeIDRef attribute_id = entry.key; + ReadAttributeLookup attribute = in_component.attribute_try_get_for_read(attribute_id); + if (!attribute) { + continue; + } + + /* Only copy if it is on a domain we want. */ + if (!domains.contains(attribute.domain)) { + continue; + } + const CustomDataType data_type = bke::cpp_type_to_custom_data_type(attribute.varray.type()); + + OutputAttribute result_attribute = result_component.attribute_try_get_for_output_only( + attribute_id, attribute.domain, data_type); + + if (!result_attribute) { + continue; + } + + attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + using T = decltype(dummy); + VArray_Span<T> span{attribute.varray.typed<T>()}; + MutableSpan<T> out_span = result_attribute.as_span<T>(); + out_span.copy_from(span); + }); + result_attribute.save(); + } +} + +/** + * For each attribute with a domain in `domains` it copies the parts of that attribute which lie in + * the mask to `result_component`. + */ +static void copy_attributes_based_on_mask(const Map<AttributeIDRef, AttributeKind> &attributes, + const GeometryComponent &in_component, + GeometryComponent &result_component, + const AttributeDomain domain, + const IndexMask mask) +{ + for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) { + const AttributeIDRef attribute_id = entry.key; + ReadAttributeLookup attribute = in_component.attribute_try_get_for_read(attribute_id); + if (!attribute) { + continue; + } + + /* Only copy if it is on a domain we want. */ + if (domain != attribute.domain) { + continue; + } + const CustomDataType data_type = bke::cpp_type_to_custom_data_type(attribute.varray.type()); + + OutputAttribute result_attribute = result_component.attribute_try_get_for_output_only( + attribute_id, attribute.domain, data_type); + + if (!result_attribute) { + continue; + } + + attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + using T = decltype(dummy); + VArray_Span<T> span{attribute.varray.typed<T>()}; + MutableSpan<T> out_span = result_attribute.as_span<T>(); + copy_data_based_on_mask(span, out_span, mask); + }); + result_attribute.save(); + } +} + +static void copy_attributes_based_on_map(const Map<AttributeIDRef, AttributeKind> &attributes, + const GeometryComponent &in_component, + GeometryComponent &result_component, + const AttributeDomain domain, + const Span<int> index_map) +{ + for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) { + const AttributeIDRef attribute_id = entry.key; + ReadAttributeLookup attribute = in_component.attribute_try_get_for_read(attribute_id); + if (!attribute) { + continue; + } + + /* Only copy if it is on a domain we want. */ + if (domain != attribute.domain) { + continue; + } + const CustomDataType data_type = bke::cpp_type_to_custom_data_type(attribute.varray.type()); + + OutputAttribute result_attribute = result_component.attribute_try_get_for_output_only( + attribute_id, attribute.domain, data_type); + + if (!result_attribute) { + continue; + } + + attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + using T = decltype(dummy); + VArray_Span<T> span{attribute.varray.typed<T>()}; + MutableSpan<T> out_span = result_attribute.as_span<T>(); + copy_data_based_on_map(span, out_span, index_map); + }); + result_attribute.save(); + } +} + +static void copy_face_corner_attributes(const Map<AttributeIDRef, AttributeKind> &attributes, + const GeometryComponent &in_component, + GeometryComponent &out_component, + const int num_selected_loops, + const Span<int> selected_poly_indices, + const Mesh &mesh_in) +{ + Vector<int64_t> indices; + indices.reserve(num_selected_loops); + for (const int src_poly_index : selected_poly_indices) { + const MPoly &src_poly = mesh_in.mpoly[src_poly_index]; + const int src_loop_start = src_poly.loopstart; + const int tot_loop = src_poly.totloop; + for (const int i : IndexRange(tot_loop)) { + indices.append_unchecked(src_loop_start + i); + } + } + copy_attributes_based_on_mask( + attributes, in_component, out_component, ATTR_DOMAIN_CORNER, IndexMask(indices)); +} + +static void copy_masked_vertices_to_new_mesh(const Mesh &src_mesh, + Mesh &dst_mesh, + Span<int> vertex_map) +{ + BLI_assert(src_mesh.totvert == vertex_map.size()); + for (const int i_src : vertex_map.index_range()) { + const int i_dst = vertex_map[i_src]; + if (i_dst == -1) { + continue; + } + + const MVert &v_src = src_mesh.mvert[i_src]; + MVert &v_dst = dst_mesh.mvert[i_dst]; + + v_dst = v_src; + } +} + +static void copy_masked_edges_to_new_mesh(const Mesh &src_mesh, Mesh &dst_mesh, Span<int> edge_map) +{ + BLI_assert(src_mesh.totedge == edge_map.size()); + for (const int i_src : IndexRange(src_mesh.totedge)) { + const int i_dst = edge_map[i_src]; + if (ELEM(i_dst, -1, -2)) { + continue; + } + + const MEdge &e_src = src_mesh.medge[i_src]; + MEdge &e_dst = dst_mesh.medge[i_dst]; + + e_dst = e_src; + e_dst.v1 = e_src.v1; + e_dst.v2 = e_src.v2; + } +} + +static void copy_masked_edges_to_new_mesh(const Mesh &src_mesh, + Mesh &dst_mesh, + Span<int> vertex_map, + Span<int> edge_map) +{ + BLI_assert(src_mesh.totvert == vertex_map.size()); + BLI_assert(src_mesh.totedge == edge_map.size()); + for (const int i_src : IndexRange(src_mesh.totedge)) { + const int i_dst = edge_map[i_src]; + if (i_dst == -1) { + continue; + } + + const MEdge &e_src = src_mesh.medge[i_src]; + MEdge &e_dst = dst_mesh.medge[i_dst]; + + e_dst = e_src; + e_dst.v1 = vertex_map[e_src.v1]; + e_dst.v2 = vertex_map[e_src.v2]; + } +} + +/* Faces and edges changed but vertices are the same. */ +static void copy_masked_polys_to_new_mesh(const Mesh &src_mesh, + Mesh &dst_mesh, + Span<int> edge_map, + Span<int> masked_poly_indices, + Span<int> new_loop_starts) +{ + for (const int i_dst : masked_poly_indices.index_range()) { + const int i_src = masked_poly_indices[i_dst]; + + const MPoly &mp_src = src_mesh.mpoly[i_src]; + MPoly &mp_dst = dst_mesh.mpoly[i_dst]; + const int i_ml_src = mp_src.loopstart; + const int i_ml_dst = new_loop_starts[i_dst]; + + const MLoop *ml_src = src_mesh.mloop + i_ml_src; + MLoop *ml_dst = dst_mesh.mloop + i_ml_dst; + + mp_dst = mp_src; + mp_dst.loopstart = i_ml_dst; + for (int i : IndexRange(mp_src.totloop)) { + ml_dst[i].v = ml_src[i].v; + ml_dst[i].e = edge_map[ml_src[i].e]; + } + } +} + +/* Only faces changed. */ +static void copy_masked_polys_to_new_mesh(const Mesh &src_mesh, + Mesh &dst_mesh, + Span<int> masked_poly_indices, + Span<int> new_loop_starts) +{ + for (const int i_dst : masked_poly_indices.index_range()) { + const int i_src = masked_poly_indices[i_dst]; + + const MPoly &mp_src = src_mesh.mpoly[i_src]; + MPoly &mp_dst = dst_mesh.mpoly[i_dst]; + const int i_ml_src = mp_src.loopstart; + const int i_ml_dst = new_loop_starts[i_dst]; + + const MLoop *ml_src = src_mesh.mloop + i_ml_src; + MLoop *ml_dst = dst_mesh.mloop + i_ml_dst; + + mp_dst = mp_src; + mp_dst.loopstart = i_ml_dst; + for (int i : IndexRange(mp_src.totloop)) { + ml_dst[i].v = ml_src[i].v; + ml_dst[i].e = ml_src[i].e; + } + } +} + +static void copy_masked_polys_to_new_mesh(const Mesh &src_mesh, + Mesh &dst_mesh, + Span<int> vertex_map, + Span<int> edge_map, + Span<int> masked_poly_indices, + Span<int> new_loop_starts) +{ + for (const int i_dst : masked_poly_indices.index_range()) { + const int i_src = masked_poly_indices[i_dst]; + + const MPoly &mp_src = src_mesh.mpoly[i_src]; + MPoly &mp_dst = dst_mesh.mpoly[i_dst]; + const int i_ml_src = mp_src.loopstart; + const int i_ml_dst = new_loop_starts[i_dst]; + + const MLoop *ml_src = src_mesh.mloop + i_ml_src; + MLoop *ml_dst = dst_mesh.mloop + i_ml_dst; + + mp_dst = mp_src; + mp_dst.loopstart = i_ml_dst; + for (int i : IndexRange(mp_src.totloop)) { + ml_dst[i].v = vertex_map[ml_src[i].v]; + ml_dst[i].e = edge_map[ml_src[i].e]; + } + } +} + +static void spline_copy_builtin_attributes(const Spline &spline, + Spline &r_spline, + const IndexMask mask) +{ + copy_data_based_on_mask(spline.positions(), r_spline.positions(), mask); + copy_data_based_on_mask(spline.radii(), r_spline.radii(), mask); + copy_data_based_on_mask(spline.tilts(), r_spline.tilts(), mask); + switch (spline.type()) { + case Spline::Type::Poly: + break; + case Spline::Type::Bezier: { + const BezierSpline &src = static_cast<const BezierSpline &>(spline); + BezierSpline &dst = static_cast<BezierSpline &>(r_spline); + copy_data_based_on_mask(src.handle_positions_left(), dst.handle_positions_left(), mask); + copy_data_based_on_mask(src.handle_positions_right(), dst.handle_positions_right(), mask); + copy_data_based_on_mask(src.handle_types_left(), dst.handle_types_left(), mask); + copy_data_based_on_mask(src.handle_types_right(), dst.handle_types_right(), mask); + break; + } + case Spline::Type::NURBS: { + const NURBSpline &src = static_cast<const NURBSpline &>(spline); + NURBSpline &dst = static_cast<NURBSpline &>(r_spline); + copy_data_based_on_mask(src.weights(), dst.weights(), mask); + break; + } + } +} + +static void copy_dynamic_attributes(const CustomDataAttributes &src, + CustomDataAttributes &dst, + const IndexMask mask) +{ + src.foreach_attribute( + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + std::optional<GSpan> src_attribute = src.get_for_read(attribute_id); + BLI_assert(src_attribute); + + if (!dst.create(attribute_id, meta_data.data_type)) { + /* Since the source spline of the same type had the attribute, adding it should work. + */ + BLI_assert_unreachable(); + } + + std::optional<GMutableSpan> new_attribute = dst.get_for_write(attribute_id); + BLI_assert(new_attribute); + + attribute_math::convert_to_static_type(new_attribute->type(), [&](auto dummy) { + using T = decltype(dummy); + copy_data_based_on_mask(src_attribute->typed<T>(), new_attribute->typed<T>(), mask); + }); + return true; + }, + ATTR_DOMAIN_POINT); +} + +/** + * Deletes points in the spline. Those not in the mask are deleted. The spline is not split into + * multiple newer splines. + */ +static SplinePtr spline_delete(const Spline &spline, const IndexMask mask) +{ + SplinePtr new_spline = spline.copy_only_settings(); + new_spline->resize(mask.size()); + + spline_copy_builtin_attributes(spline, *new_spline, mask); + copy_dynamic_attributes(spline.attributes, new_spline->attributes, mask); + + return new_spline; +} + +static std::unique_ptr<CurveEval> curve_separate(const CurveEval &input_curve, + const Span<bool> selection, + const AttributeDomain selection_domain, + const bool invert) +{ + Span<SplinePtr> input_splines = input_curve.splines(); + std::unique_ptr<CurveEval> output_curve = std::make_unique<CurveEval>(); + + /* Keep track of which splines were copied to the result to copy spline domain attributes. */ + Vector<int64_t> copied_splines; + + if (selection_domain == ATTR_DOMAIN_CURVE) { + /* Operates on each of the splines as a whole, i.e. not on the points in the splines + * themselves. */ + for (const int i : selection.index_range()) { + if (selection[i] != invert) { + output_curve->add_spline(input_splines[i]->copy()); + copied_splines.append(i); + } + } + } + else { + /* Operates on the points in the splines themselves. */ + + /* Reuse index vector for each spline. */ + Vector<int64_t> indices_to_copy; + + int selection_index = 0; + for (const int i : input_splines.index_range()) { + const Spline &spline = *input_splines[i]; + + indices_to_copy.clear(); + for (const int i_point : IndexRange(spline.size())) { + if (selection[selection_index] != invert) { + /* Append i_point instead of selection_index because we need indices local to the spline + * for copying. */ + indices_to_copy.append(i_point); + } + selection_index++; + } + + /* Avoid creating an empty spline. */ + if (indices_to_copy.is_empty()) { + continue; + } + + SplinePtr new_spline = spline_delete(spline, IndexMask(indices_to_copy)); + output_curve->add_spline(std::move(new_spline)); + copied_splines.append(i); + } + } + + if (copied_splines.is_empty()) { + return {}; + } + + output_curve->attributes.reallocate(output_curve->splines().size()); + copy_dynamic_attributes( + input_curve.attributes, output_curve->attributes, IndexMask(copied_splines)); + + return output_curve; +} + +static void separate_curve_selection(GeometrySet &geometry_set, + const Field<bool> &selection_field, + const AttributeDomain selection_domain, + const bool invert) +{ + const CurveComponent &src_component = *geometry_set.get_component_for_read<CurveComponent>(); + GeometryComponentFieldContext field_context{src_component, selection_domain}; + + fn::FieldEvaluator selection_evaluator{field_context, + src_component.attribute_domain_size(selection_domain)}; + selection_evaluator.add(selection_field); + selection_evaluator.evaluate(); + const VArray_Span<bool> &selection = selection_evaluator.get_evaluated<bool>(0); + std::unique_ptr<CurveEval> r_curve = curve_separate( + *src_component.get_for_read(), selection, selection_domain, invert); + if (r_curve) { + geometry_set.replace_curve(r_curve.release()); + } + else { + geometry_set.replace_curve(nullptr); + } +} + +static void separate_point_cloud_selection(GeometrySet &geometry_set, + const Field<bool> &selection_field, + const bool invert) +{ + const PointCloudComponent &src_points = + *geometry_set.get_component_for_read<PointCloudComponent>(); + GeometryComponentFieldContext field_context{src_points, ATTR_DOMAIN_POINT}; + + fn::FieldEvaluator selection_evaluator{field_context, + src_points.attribute_domain_size(ATTR_DOMAIN_POINT)}; + selection_evaluator.add(selection_field); + selection_evaluator.evaluate(); + const VArray_Span<bool> &selection = selection_evaluator.get_evaluated<bool>(0); + + Vector<int64_t> indices; + const IndexMask mask = index_mask_indices(selection, invert, indices); + const int total = mask.size(); + PointCloud *pointcloud = BKE_pointcloud_new_nomain(total); + + if (total == 0) { + geometry_set.replace_pointcloud(pointcloud); + return; + } + + PointCloudComponent dst_points; + dst_points.replace(pointcloud, GeometryOwnershipType::Editable); + + Map<AttributeIDRef, AttributeKind> attributes; + geometry_set.gather_attributes_for_propagation( + {GEO_COMPONENT_TYPE_POINT_CLOUD}, GEO_COMPONENT_TYPE_POINT_CLOUD, false, attributes); + + copy_attributes_based_on_mask(attributes, src_points, dst_points, ATTR_DOMAIN_POINT, mask); + geometry_set.replace_pointcloud(pointcloud); +} + +static void separate_instance_selection(GeometrySet &geometry_set, + const Field<bool> &selection_field, + const bool invert) +{ + InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>(); + GeometryComponentFieldContext field_context{instances, ATTR_DOMAIN_INSTANCE}; + + const int domain_size = instances.attribute_domain_size(ATTR_DOMAIN_INSTANCE); + fn::FieldEvaluator evaluator{field_context, domain_size}; + evaluator.add(selection_field); + evaluator.evaluate(); + const VArray_Span<bool> &selection = evaluator.get_evaluated<bool>(0); + + Vector<int64_t> indices; + const IndexMask mask = index_mask_indices(selection, invert, indices); + + if (mask.is_empty()) { + geometry_set.remove<InstancesComponent>(); + return; + } + + instances.remove_instances(mask); +} + +static void compute_selected_vertices_from_vertex_selection(const Span<bool> vertex_selection, + const bool invert, + MutableSpan<int> r_vertex_map, + int *r_num_selected_vertices) +{ + BLI_assert(vertex_selection.size() == r_vertex_map.size()); + + int num_selected_vertices = 0; + for (const int i : r_vertex_map.index_range()) { + if (vertex_selection[i] != invert) { + r_vertex_map[i] = num_selected_vertices; + num_selected_vertices++; + } + else { + r_vertex_map[i] = -1; + } + } + + *r_num_selected_vertices = num_selected_vertices; +} + +static void compute_selected_edges_from_vertex_selection(const Mesh &mesh, + const Span<bool> vertex_selection, + const bool invert, + MutableSpan<int> r_edge_map, + int *r_num_selected_edges) +{ + BLI_assert(mesh.totedge == r_edge_map.size()); + + int num_selected_edges = 0; + for (const int i : IndexRange(mesh.totedge)) { + const MEdge &edge = mesh.medge[i]; + + /* Only add the edge if both vertices will be in the new mesh. */ + if (vertex_selection[edge.v1] != invert && vertex_selection[edge.v2] != invert) { + r_edge_map[i] = num_selected_edges; + num_selected_edges++; + } + else { + r_edge_map[i] = -1; + } + } + + *r_num_selected_edges = num_selected_edges; +} + +static void compute_selected_polygons_from_vertex_selection(const Mesh &mesh, + const Span<bool> vertex_selection, + const bool invert, + Vector<int> &r_selected_poly_indices, + Vector<int> &r_loop_starts, + int *r_num_selected_polys, + int *r_num_selected_loops) +{ + BLI_assert(mesh.totvert == vertex_selection.size()); + + r_selected_poly_indices.reserve(mesh.totpoly); + r_loop_starts.reserve(mesh.totloop); + + int num_selected_loops = 0; + for (const int i : IndexRange(mesh.totpoly)) { + const MPoly &poly_src = mesh.mpoly[i]; + + bool all_verts_in_selection = true; + Span<MLoop> loops_src(&mesh.mloop[poly_src.loopstart], poly_src.totloop); + for (const MLoop &loop : loops_src) { + if (vertex_selection[loop.v] == invert) { + all_verts_in_selection = false; + break; + } + } + + if (all_verts_in_selection) { + r_selected_poly_indices.append_unchecked(i); + r_loop_starts.append_unchecked(num_selected_loops); + num_selected_loops += poly_src.totloop; + } + } + + *r_num_selected_polys = r_selected_poly_indices.size(); + *r_num_selected_loops = num_selected_loops; +} + +/** + * Checks for every edge if it is in `edge_selection`. If it is, then the two vertices of the + * edge are kept along with the edge. + */ +static void compute_selected_vertices_and_edges_from_edge_selection( + const Mesh &mesh, + const Span<bool> edge_selection, + const bool invert, + MutableSpan<int> r_vertex_map, + MutableSpan<int> r_edge_map, + int *r_num_selected_vertices, + int *r_num_selected_edges) +{ + BLI_assert(mesh.totedge == edge_selection.size()); + + int num_selected_edges = 0; + int num_selected_vertices = 0; + for (const int i : IndexRange(mesh.totedge)) { + const MEdge &edge = mesh.medge[i]; + if (edge_selection[i] != invert) { + r_edge_map[i] = num_selected_edges; + num_selected_edges++; + if (r_vertex_map[edge.v1] == -1) { + r_vertex_map[edge.v1] = num_selected_vertices; + num_selected_vertices++; + } + if (r_vertex_map[edge.v2] == -1) { + r_vertex_map[edge.v2] = num_selected_vertices; + num_selected_vertices++; + } + } + else { + r_edge_map[i] = -1; + } + } + + *r_num_selected_vertices = num_selected_vertices; + *r_num_selected_edges = num_selected_edges; +} + +/** + * Checks for every edge if it is in `edge_selection`. + */ +static void compute_selected_edges_from_edge_selection(const Mesh &mesh, + const Span<bool> edge_selection, + const bool invert, + MutableSpan<int> r_edge_map, + int *r_num_selected_edges) +{ + BLI_assert(mesh.totedge == edge_selection.size()); + + int num_selected_edges = 0; + for (const int i : IndexRange(mesh.totedge)) { + if (edge_selection[i] != invert) { + r_edge_map[i] = num_selected_edges; + num_selected_edges++; + } + else { + r_edge_map[i] = -1; + } + } + + *r_num_selected_edges = num_selected_edges; +} + +/** + * Checks for every polygon if all the edges are in `edge_selection`. If they are, then that + * polygon is kept. + */ +static void compute_selected_polygons_from_edge_selection(const Mesh &mesh, + const Span<bool> edge_selection, + const bool invert, + Vector<int> &r_selected_poly_indices, + Vector<int> &r_loop_starts, + int *r_num_selected_polys, + int *r_num_selected_loops) +{ + r_selected_poly_indices.reserve(mesh.totpoly); + r_loop_starts.reserve(mesh.totloop); + + int num_selected_loops = 0; + for (const int i : IndexRange(mesh.totpoly)) { + const MPoly &poly_src = mesh.mpoly[i]; + + bool all_edges_in_selection = true; + Span<MLoop> loops_src(&mesh.mloop[poly_src.loopstart], poly_src.totloop); + for (const MLoop &loop : loops_src) { + if (edge_selection[loop.e] == invert) { + all_edges_in_selection = false; + break; + } + } + + if (all_edges_in_selection) { + r_selected_poly_indices.append_unchecked(i); + r_loop_starts.append_unchecked(num_selected_loops); + num_selected_loops += poly_src.totloop; + } + } + + *r_num_selected_polys = r_selected_poly_indices.size(); + *r_num_selected_loops = num_selected_loops; +} + +/** + * Checks for every edge and polygon if all its vertices are in `vertex_selection`. + */ +static void compute_selected_mesh_data_from_vertex_selection_edge_face( + const Mesh &mesh, + const Span<bool> vertex_selection, + const bool invert, + MutableSpan<int> r_edge_map, + Vector<int> &r_selected_poly_indices, + Vector<int> &r_loop_starts, + int *r_num_selected_edges, + int *r_num_selected_polys, + int *r_num_selected_loops) +{ + + compute_selected_edges_from_vertex_selection( + mesh, vertex_selection, invert, r_edge_map, r_num_selected_edges); + + compute_selected_polygons_from_vertex_selection(mesh, + vertex_selection, + invert, + r_selected_poly_indices, + r_loop_starts, + r_num_selected_polys, + r_num_selected_loops); +} + +/** + * Checks for every vertex if it is in `vertex_selection`. The polygons and edges are kept if all + * vertices of that polygon or edge are in the selection. + */ +static void compute_selected_mesh_data_from_vertex_selection(const Mesh &mesh, + const Span<bool> vertex_selection, + const bool invert, + MutableSpan<int> r_vertex_map, + MutableSpan<int> r_edge_map, + Vector<int> &r_selected_poly_indices, + Vector<int> &r_loop_starts, + int *r_num_selected_vertices, + int *r_num_selected_edges, + int *r_num_selected_polys, + int *r_num_selected_loops) +{ + compute_selected_vertices_from_vertex_selection( + vertex_selection, invert, r_vertex_map, r_num_selected_vertices); + + compute_selected_edges_from_vertex_selection( + mesh, vertex_selection, invert, r_edge_map, r_num_selected_edges); + + compute_selected_polygons_from_vertex_selection(mesh, + vertex_selection, + invert, + r_selected_poly_indices, + r_loop_starts, + r_num_selected_polys, + r_num_selected_loops); +} + +/** + * Checks for every edge if it is in `edge_selection`. The polygons are kept if all edges are in + * the selection. + */ +static void compute_selected_mesh_data_from_edge_selection_edge_face( + const Mesh &mesh, + const Span<bool> edge_selection, + const bool invert, + MutableSpan<int> r_edge_map, + Vector<int> &r_selected_poly_indices, + Vector<int> &r_loop_starts, + int *r_num_selected_edges, + int *r_num_selected_polys, + int *r_num_selected_loops) +{ + compute_selected_edges_from_edge_selection( + mesh, edge_selection, invert, r_edge_map, r_num_selected_edges); + compute_selected_polygons_from_edge_selection(mesh, + edge_selection, + invert, + r_selected_poly_indices, + r_loop_starts, + r_num_selected_polys, + r_num_selected_loops); +} + +/** + * Checks for every edge if it is in `edge_selection`. If it is, the vertices belonging to + * that edge are kept as well. The polygons are kept if all edges are in the selection. + */ +static void compute_selected_mesh_data_from_edge_selection(const Mesh &mesh, + const Span<bool> edge_selection, + const bool invert, + MutableSpan<int> r_vertex_map, + MutableSpan<int> r_edge_map, + Vector<int> &r_selected_poly_indices, + Vector<int> &r_loop_starts, + int *r_num_selected_vertices, + int *r_num_selected_edges, + int *r_num_selected_polys, + int *r_num_selected_loops) +{ + r_vertex_map.fill(-1); + compute_selected_vertices_and_edges_from_edge_selection(mesh, + edge_selection, + invert, + r_vertex_map, + r_edge_map, + r_num_selected_vertices, + r_num_selected_edges); + compute_selected_polygons_from_edge_selection(mesh, + edge_selection, + invert, + r_selected_poly_indices, + r_loop_starts, + r_num_selected_polys, + r_num_selected_loops); +} + +/** + * Checks for every polygon if it is in `poly_selection`. + */ +static void compute_selected_polygons_from_poly_selection(const Mesh &mesh, + const Span<bool> poly_selection, + const bool invert, + Vector<int> &r_selected_poly_indices, + Vector<int> &r_loop_starts, + int *r_num_selected_polys, + int *r_num_selected_loops) +{ + BLI_assert(mesh.totpoly == poly_selection.size()); + + r_selected_poly_indices.reserve(mesh.totpoly); + r_loop_starts.reserve(mesh.totloop); + + int num_selected_loops = 0; + for (const int i : IndexRange(mesh.totpoly)) { + const MPoly &poly_src = mesh.mpoly[i]; + /* We keep this one. */ + if (poly_selection[i] != invert) { + r_selected_poly_indices.append_unchecked(i); + r_loop_starts.append_unchecked(num_selected_loops); + num_selected_loops += poly_src.totloop; + } + } + *r_num_selected_polys = r_selected_poly_indices.size(); + *r_num_selected_loops = num_selected_loops; +} +/** + * Checks for every polygon if it is in `poly_selection`. If it is, the edges + * belonging to that polygon are kept as well. + */ +static void compute_selected_mesh_data_from_poly_selection_edge_face( + const Mesh &mesh, + const Span<bool> poly_selection, + const bool invert, + MutableSpan<int> r_edge_map, + Vector<int> &r_selected_poly_indices, + Vector<int> &r_loop_starts, + int *r_num_selected_edges, + int *r_num_selected_polys, + int *r_num_selected_loops) +{ + BLI_assert(mesh.totpoly == poly_selection.size()); + BLI_assert(mesh.totedge == r_edge_map.size()); + r_edge_map.fill(-1); + + r_selected_poly_indices.reserve(mesh.totpoly); + r_loop_starts.reserve(mesh.totloop); + + int num_selected_loops = 0; + int num_selected_edges = 0; + for (const int i : IndexRange(mesh.totpoly)) { + const MPoly &poly_src = mesh.mpoly[i]; + /* We keep this one. */ + if (poly_selection[i] != invert) { + r_selected_poly_indices.append_unchecked(i); + r_loop_starts.append_unchecked(num_selected_loops); + num_selected_loops += poly_src.totloop; + + /* Add the vertices and the edges. */ + Span<MLoop> loops_src(&mesh.mloop[poly_src.loopstart], poly_src.totloop); + for (const MLoop &loop : loops_src) { + /* Check first if it has not yet been added. */ + if (r_edge_map[loop.e] == -1) { + r_edge_map[loop.e] = num_selected_edges; + num_selected_edges++; + } + } + } + } + *r_num_selected_edges = num_selected_edges; + *r_num_selected_polys = r_selected_poly_indices.size(); + *r_num_selected_loops = num_selected_loops; +} + +/** + * Checks for every polygon if it is in `poly_selection`. If it is, the edges and vertices + * belonging to that polygon are kept as well. + */ +static void compute_selected_mesh_data_from_poly_selection(const Mesh &mesh, + const Span<bool> poly_selection, + const bool invert, + MutableSpan<int> r_vertex_map, + MutableSpan<int> r_edge_map, + Vector<int> &r_selected_poly_indices, + Vector<int> &r_loop_starts, + int *r_num_selected_vertices, + int *r_num_selected_edges, + int *r_num_selected_polys, + int *r_num_selected_loops) +{ + BLI_assert(mesh.totpoly == poly_selection.size()); + BLI_assert(mesh.totedge == r_edge_map.size()); + r_vertex_map.fill(-1); + r_edge_map.fill(-1); + + r_selected_poly_indices.reserve(mesh.totpoly); + r_loop_starts.reserve(mesh.totloop); + + int num_selected_loops = 0; + int num_selected_vertices = 0; + int num_selected_edges = 0; + for (const int i : IndexRange(mesh.totpoly)) { + const MPoly &poly_src = mesh.mpoly[i]; + /* We keep this one. */ + if (poly_selection[i] != invert) { + r_selected_poly_indices.append_unchecked(i); + r_loop_starts.append_unchecked(num_selected_loops); + num_selected_loops += poly_src.totloop; + + /* Add the vertices and the edges. */ + Span<MLoop> loops_src(&mesh.mloop[poly_src.loopstart], poly_src.totloop); + for (const MLoop &loop : loops_src) { + /* Check first if it has not yet been added. */ + if (r_vertex_map[loop.v] == -1) { + r_vertex_map[loop.v] = num_selected_vertices; + num_selected_vertices++; + } + if (r_edge_map[loop.e] == -1) { + r_edge_map[loop.e] = num_selected_edges; + num_selected_edges++; + } + } + } + } + *r_num_selected_vertices = num_selected_vertices; + *r_num_selected_edges = num_selected_edges; + *r_num_selected_polys = r_selected_poly_indices.size(); + *r_num_selected_loops = num_selected_loops; +} + +/** + * Keep the parts of the mesh that are in the selection. + */ +static void do_mesh_separation(GeometrySet &geometry_set, + const MeshComponent &in_component, + const VArray_Span<bool> &selection, + const bool invert, + const AttributeDomain domain, + const GeometryNodeDeleteGeometryMode mode) +{ + /* Needed in all cases. */ + Vector<int> selected_poly_indices; + Vector<int> new_loop_starts; + int num_selected_polys = 0; + int num_selected_loops = 0; + + const Mesh &mesh_in = *in_component.get_for_read(); + Mesh *mesh_out; + MeshComponent out_component; + + Map<AttributeIDRef, AttributeKind> attributes; + geometry_set.gather_attributes_for_propagation( + {GEO_COMPONENT_TYPE_MESH}, GEO_COMPONENT_TYPE_MESH, false, attributes); + + switch (mode) { + case GEO_NODE_DELETE_GEOMETRY_MODE_ALL: { + Array<int> vertex_map(mesh_in.totvert); + int num_selected_vertices = 0; + + Array<int> edge_map(mesh_in.totedge); + int num_selected_edges = 0; + + /* Fill all the maps based on the selection. */ + switch (domain) { + case ATTR_DOMAIN_POINT: + compute_selected_mesh_data_from_vertex_selection(mesh_in, + selection, + invert, + vertex_map, + edge_map, + selected_poly_indices, + new_loop_starts, + &num_selected_vertices, + &num_selected_edges, + &num_selected_polys, + &num_selected_loops); + break; + case ATTR_DOMAIN_EDGE: + compute_selected_mesh_data_from_edge_selection(mesh_in, + selection, + invert, + vertex_map, + edge_map, + selected_poly_indices, + new_loop_starts, + &num_selected_vertices, + &num_selected_edges, + &num_selected_polys, + &num_selected_loops); + break; + case ATTR_DOMAIN_FACE: + compute_selected_mesh_data_from_poly_selection(mesh_in, + selection, + invert, + vertex_map, + edge_map, + selected_poly_indices, + new_loop_starts, + &num_selected_vertices, + &num_selected_edges, + &num_selected_polys, + &num_selected_loops); + break; + default: + BLI_assert_unreachable(); + break; + } + mesh_out = BKE_mesh_new_nomain_from_template(&mesh_in, + num_selected_vertices, + num_selected_edges, + 0, + num_selected_loops, + num_selected_polys); + out_component.replace(mesh_out, GeometryOwnershipType::Editable); + + /* Copy the selected parts of the mesh over to the new mesh. */ + copy_masked_vertices_to_new_mesh(mesh_in, *mesh_out, vertex_map); + copy_masked_edges_to_new_mesh(mesh_in, *mesh_out, vertex_map, edge_map); + copy_masked_polys_to_new_mesh( + mesh_in, *mesh_out, vertex_map, edge_map, selected_poly_indices, new_loop_starts); + + /* Copy attributes. */ + copy_attributes_based_on_map( + attributes, in_component, out_component, ATTR_DOMAIN_POINT, vertex_map); + copy_attributes_based_on_map( + attributes, in_component, out_component, ATTR_DOMAIN_EDGE, edge_map); + copy_attributes_based_on_mask(attributes, + in_component, + out_component, + ATTR_DOMAIN_FACE, + IndexMask(Vector<int64_t>(selected_poly_indices.as_span()))); + copy_face_corner_attributes(attributes, + in_component, + out_component, + num_selected_loops, + selected_poly_indices, + mesh_in); + break; + } + case GEO_NODE_DELETE_GEOMETRY_MODE_EDGE_FACE: { + Array<int> edge_map(mesh_in.totedge); + int num_selected_edges = 0; + + /* Fill all the maps based on the selection. */ + switch (domain) { + case ATTR_DOMAIN_POINT: + compute_selected_mesh_data_from_vertex_selection_edge_face(mesh_in, + selection, + invert, + edge_map, + selected_poly_indices, + new_loop_starts, + &num_selected_edges, + &num_selected_polys, + &num_selected_loops); + break; + case ATTR_DOMAIN_EDGE: + compute_selected_mesh_data_from_edge_selection_edge_face(mesh_in, + selection, + invert, + edge_map, + selected_poly_indices, + new_loop_starts, + &num_selected_edges, + &num_selected_polys, + &num_selected_loops); + break; + case ATTR_DOMAIN_FACE: + compute_selected_mesh_data_from_poly_selection_edge_face(mesh_in, + selection, + invert, + edge_map, + selected_poly_indices, + new_loop_starts, + &num_selected_edges, + &num_selected_polys, + &num_selected_loops); + break; + default: + BLI_assert_unreachable(); + break; + } + mesh_out = BKE_mesh_new_nomain_from_template(&mesh_in, + mesh_in.totvert, + num_selected_edges, + 0, + num_selected_loops, + num_selected_polys); + out_component.replace(mesh_out, GeometryOwnershipType::Editable); + + /* Copy the selected parts of the mesh over to the new mesh. */ + memcpy(mesh_out->mvert, mesh_in.mvert, mesh_in.totvert * sizeof(MVert)); + copy_masked_edges_to_new_mesh(mesh_in, *mesh_out, edge_map); + copy_masked_polys_to_new_mesh( + mesh_in, *mesh_out, edge_map, selected_poly_indices, new_loop_starts); + + /* Copy attributes. */ + copy_attributes(attributes, in_component, out_component, {ATTR_DOMAIN_POINT}); + copy_attributes_based_on_map( + attributes, in_component, out_component, ATTR_DOMAIN_EDGE, edge_map); + copy_attributes_based_on_mask(attributes, + in_component, + out_component, + ATTR_DOMAIN_FACE, + IndexMask(Vector<int64_t>(selected_poly_indices.as_span()))); + copy_face_corner_attributes(attributes, + in_component, + out_component, + num_selected_loops, + selected_poly_indices, + mesh_in); + break; + } + case GEO_NODE_DELETE_GEOMETRY_MODE_ONLY_FACE: { + /* Fill all the maps based on the selection. */ + switch (domain) { + case ATTR_DOMAIN_POINT: + compute_selected_polygons_from_vertex_selection(mesh_in, + selection, + invert, + selected_poly_indices, + new_loop_starts, + &num_selected_polys, + &num_selected_loops); + break; + case ATTR_DOMAIN_EDGE: + compute_selected_polygons_from_edge_selection(mesh_in, + selection, + invert, + selected_poly_indices, + new_loop_starts, + &num_selected_polys, + &num_selected_loops); + break; + case ATTR_DOMAIN_FACE: + compute_selected_polygons_from_poly_selection(mesh_in, + selection, + invert, + selected_poly_indices, + new_loop_starts, + &num_selected_polys, + &num_selected_loops); + break; + default: + BLI_assert_unreachable(); + break; + } + mesh_out = BKE_mesh_new_nomain_from_template( + &mesh_in, mesh_in.totvert, mesh_in.totedge, 0, num_selected_loops, num_selected_polys); + out_component.replace(mesh_out, GeometryOwnershipType::Editable); + + /* Copy the selected parts of the mesh over to the new mesh. */ + memcpy(mesh_out->mvert, mesh_in.mvert, mesh_in.totvert * sizeof(MVert)); + memcpy(mesh_out->medge, mesh_in.medge, mesh_in.totedge * sizeof(MEdge)); + copy_masked_polys_to_new_mesh(mesh_in, *mesh_out, selected_poly_indices, new_loop_starts); + + /* Copy attributes. */ + copy_attributes( + attributes, in_component, out_component, {ATTR_DOMAIN_POINT, ATTR_DOMAIN_EDGE}); + copy_attributes_based_on_mask(attributes, + in_component, + out_component, + ATTR_DOMAIN_FACE, + IndexMask(Vector<int64_t>(selected_poly_indices.as_span()))); + copy_face_corner_attributes(attributes, + in_component, + out_component, + num_selected_loops, + selected_poly_indices, + mesh_in); + break; + } + } + + BKE_mesh_calc_edges_loose(mesh_out); + /* Tag to recalculate normals later. */ + BKE_mesh_normals_tag_dirty(mesh_out); + geometry_set.replace_mesh(mesh_out); +} + +static void separate_mesh_selection(GeometrySet &geometry_set, + const Field<bool> &selection_field, + const AttributeDomain selection_domain, + const GeometryNodeDeleteGeometryMode mode, + const bool invert) +{ + const MeshComponent &src_component = *geometry_set.get_component_for_read<MeshComponent>(); + GeometryComponentFieldContext field_context{src_component, selection_domain}; + + fn::FieldEvaluator selection_evaluator{field_context, + src_component.attribute_domain_size(selection_domain)}; + selection_evaluator.add(selection_field); + selection_evaluator.evaluate(); + const VArray_Span<bool> &selection = selection_evaluator.get_evaluated<bool>(0); + + /* Check if there is anything to delete. */ + bool delete_nothing = true; + for (const int i : selection.index_range()) { + if (selection[i] == invert) { + delete_nothing = false; + break; + } + } + if (delete_nothing) { + return; + } + + do_mesh_separation(geometry_set, src_component, selection, invert, selection_domain, mode); +} + +} // namespace blender::nodes::node_geo_delete_geometry_cc + +namespace blender::nodes { + +void separate_geometry(GeometrySet &geometry_set, + const AttributeDomain domain, + const GeometryNodeDeleteGeometryMode mode, + const Field<bool> &selection_field, + const bool invert, + bool &r_is_error) +{ + namespace file_ns = blender::nodes::node_geo_delete_geometry_cc; + + bool some_valid_domain = false; + if (geometry_set.has_pointcloud()) { + if (domain == ATTR_DOMAIN_POINT) { + file_ns::separate_point_cloud_selection(geometry_set, selection_field, invert); + some_valid_domain = true; + } + } + if (geometry_set.has_mesh()) { + if (ELEM(domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_EDGE, ATTR_DOMAIN_FACE, ATTR_DOMAIN_CORNER)) { + file_ns::separate_mesh_selection(geometry_set, selection_field, domain, mode, invert); + some_valid_domain = true; + } + } + if (geometry_set.has_curve()) { + if (ELEM(domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE)) { + file_ns::separate_curve_selection(geometry_set, selection_field, domain, invert); + some_valid_domain = true; + } + } + if (geometry_set.has_instances()) { + if (domain == ATTR_DOMAIN_INSTANCE) { + file_ns::separate_instance_selection(geometry_set, selection_field, invert); + some_valid_domain = true; + } + } + r_is_error = !some_valid_domain && geometry_set.has_realized_data(); +} + +} // namespace blender::nodes + +namespace blender::nodes::node_geo_delete_geometry_cc { + +NODE_STORAGE_FUNCS(NodeGeometryDeleteGeometry) + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::Bool>(N_("Selection")) + .default_value(true) + .hide_value() + .supports_field() + .description(N_("The parts of the geometry to be deleted")); + b.add_output<decl::Geometry>(N_("Geometry")); +} + +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + const bNode *node = static_cast<bNode *>(ptr->data); + const NodeGeometryDeleteGeometry &storage = node_storage(*node); + const AttributeDomain domain = static_cast<AttributeDomain>(storage.domain); + + uiItemR(layout, ptr, "domain", 0, "", ICON_NONE); + /* Only show the mode when it is relevant. */ + if (ELEM(domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_EDGE, ATTR_DOMAIN_FACE)) { + uiItemR(layout, ptr, "mode", 0, "", ICON_NONE); + } +} + +static void node_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryDeleteGeometry *data = MEM_cnew<NodeGeometryDeleteGeometry>(__func__); + data->domain = ATTR_DOMAIN_POINT; + data->mode = GEO_NODE_DELETE_GEOMETRY_MODE_ALL; + + node->storage = data; +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + + const Field<bool> selection_field = params.extract_input<Field<bool>>("Selection"); + + const NodeGeometryDeleteGeometry &storage = node_storage(params.node()); + const AttributeDomain domain = static_cast<AttributeDomain>(storage.domain); + const GeometryNodeDeleteGeometryMode mode = (GeometryNodeDeleteGeometryMode)storage.mode; + + if (domain == ATTR_DOMAIN_INSTANCE) { + bool is_error; + separate_geometry(geometry_set, domain, mode, selection_field, true, is_error); + } + else { + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + bool is_error; + /* Invert here because we want to keep the things not in the selection. */ + separate_geometry(geometry_set, domain, mode, selection_field, true, is_error); + }); + } + + params.set_output("Geometry", std::move(geometry_set)); +} + +} // namespace blender::nodes::node_geo_delete_geometry_cc + +void register_node_type_geo_delete_geometry() +{ + namespace file_ns = blender::nodes::node_geo_delete_geometry_cc; + + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_DELETE_GEOMETRY, "Delete Geometry", NODE_CLASS_GEOMETRY); + + node_type_storage(&ntype, + "NodeGeometryDeleteGeometry", + node_free_standard_storage, + node_copy_standard_storage); + + node_type_init(&ntype, file_ns::node_init); + + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.draw_buttons = file_ns::node_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc index 1a4c5d84dbf..d17657bfa3a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc @@ -36,51 +36,48 @@ #include "node_geometry_util.hh" -using blender::bke::GeometryInstanceGroup; +namespace blender::nodes::node_geo_distribute_points_on_faces_cc { -namespace blender::nodes { - -static void geo_node_point_distribute_points_on_faces_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Float>("Distance Min").min(0.0f).subtype(PROP_DISTANCE); - b.add_input<decl::Float>("Density Max").default_value(10.0f).min(0.0f); - b.add_input<decl::Float>("Density").default_value(10.0f).supports_field(); - b.add_input<decl::Float>("Density Factor") + b.add_input<decl::Geometry>(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input<decl::Float>(N_("Distance Min")).min(0.0f).subtype(PROP_DISTANCE); + b.add_input<decl::Float>(N_("Density Max")).default_value(10.0f).min(0.0f); + b.add_input<decl::Float>(N_("Density")).default_value(10.0f).min(0.0f).supports_field(); + b.add_input<decl::Float>(N_("Density Factor")) .default_value(1.0f) .min(0.0f) .max(1.0f) .subtype(PROP_FACTOR) .supports_field(); - b.add_input<decl::Int>("Seed"); - b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field(); + b.add_input<decl::Int>(N_("Seed")); - b.add_output<decl::Geometry>("Points"); - b.add_output<decl::Vector>("Normal").field_source(); - b.add_output<decl::Vector>("Rotation").subtype(PROP_EULER).field_source(); - b.add_output<decl::Int>("Stable ID").field_source(); + b.add_output<decl::Geometry>(N_("Points")); + b.add_output<decl::Vector>(N_("Normal")).field_source(); + b.add_output<decl::Vector>(N_("Rotation")).subtype(PROP_EULER).field_source(); } -static void geo_node_point_distribute_points_on_faces_layout(uiLayout *layout, - bContext *UNUSED(C), - PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "distribute_method", 0, "", ICON_NONE); } -static void node_point_distribute_points_on_faces_update(bNodeTree *UNUSED(ntree), bNode *node) +static void node_point_distribute_points_on_faces_update(bNodeTree *ntree, bNode *node) { - bNodeSocket *sock_distance_min = (bNodeSocket *)BLI_findlink(&node->inputs, 1); + bNodeSocket *sock_distance_min = (bNodeSocket *)BLI_findlink(&node->inputs, 2); bNodeSocket *sock_density_max = (bNodeSocket *)sock_distance_min->next; bNodeSocket *sock_density = sock_density_max->next; bNodeSocket *sock_density_factor = sock_density->next; - nodeSetSocketAvailability(sock_distance_min, - node->custom1 == GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_POISSON); - nodeSetSocketAvailability(sock_density_max, + nodeSetSocketAvailability(ntree, + sock_distance_min, node->custom1 == GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_POISSON); - nodeSetSocketAvailability(sock_density, - node->custom1 == GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_RANDOM); - nodeSetSocketAvailability(sock_density_factor, + nodeSetSocketAvailability( + ntree, sock_density_max, node->custom1 == GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_POISSON); + nodeSetSocketAvailability( + ntree, sock_density, node->custom1 == GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_RANDOM); + nodeSetSocketAvailability(ntree, + sock_density_factor, node->custom1 == GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_POISSON); } @@ -255,18 +252,26 @@ BLI_NOINLINE static void interpolate_attribute(const Mesh &mesh, { switch (source_domain) { case ATTR_DOMAIN_POINT: { - bke::mesh_surface_sample::sample_point_attribute( - mesh, looptri_indices, bary_coords, source_data, output_data); + bke::mesh_surface_sample::sample_point_attribute(mesh, + looptri_indices, + bary_coords, + source_data, + IndexMask(output_data.size()), + output_data); break; } case ATTR_DOMAIN_CORNER: { - bke::mesh_surface_sample::sample_corner_attribute( - mesh, looptri_indices, bary_coords, source_data, output_data); + bke::mesh_surface_sample::sample_corner_attribute(mesh, + looptri_indices, + bary_coords, + source_data, + IndexMask(output_data.size()), + output_data); break; } case ATTR_DOMAIN_FACE: { bke::mesh_surface_sample::sample_face_attribute( - mesh, looptri_indices, source_data, output_data); + mesh, looptri_indices, source_data, IndexMask(output_data.size()), output_data); break; } default: { @@ -288,6 +293,12 @@ BLI_NOINLINE static void propagate_existing_attributes( for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) { const AttributeIDRef attribute_id = entry.key; const CustomDataType output_data_type = entry.value.data_type; + + ReadAttributeLookup source_attribute = mesh_component.attribute_try_get_for_read(attribute_id); + if (!source_attribute) { + continue; + } + /* The output domain is always #ATTR_DOMAIN_POINT, since we are creating a point cloud. */ OutputAttribute attribute_out = point_component.attribute_try_get_for_output_only( attribute_id, ATTR_DOMAIN_POINT, output_data_type); @@ -296,23 +307,12 @@ BLI_NOINLINE static void propagate_existing_attributes( } GMutableSpan out_span = attribute_out.as_span(); - - std::optional<AttributeMetaData> attribute_info = point_component.attribute_get_meta_data( - attribute_id); - if (!attribute_info) { - continue; - } - - const AttributeDomain source_domain = attribute_info->domain; - GVArrayPtr source_attribute = mesh_component.attribute_get_for_read( - attribute_id, source_domain, output_data_type, nullptr); - if (!source_attribute) { - continue; - } - - interpolate_attribute( - mesh, bary_coords, looptri_indices, source_domain, *source_attribute, out_span); - + interpolate_attribute(mesh, + bary_coords, + looptri_indices, + source_attribute.domain, + source_attribute.varray, + out_span); attribute_out.save(); } } @@ -321,7 +321,6 @@ namespace { struct AttributeOutputs { StrongAnonymousAttributeID normal_id; StrongAnonymousAttributeID rotation_id; - StrongAnonymousAttributeID stable_id_id; }; } // namespace @@ -331,28 +330,25 @@ BLI_NOINLINE static void compute_attribute_outputs(const MeshComponent &mesh_com const Span<int> looptri_indices, const AttributeOutputs &attribute_outputs) { - std::optional<OutputAttribute_Typed<int>> id_attribute; - std::optional<OutputAttribute_Typed<float3>> normal_attribute; - std::optional<OutputAttribute_Typed<float3>> rotation_attribute; + OutputAttribute_Typed<int> id_attribute = point_component.attribute_try_get_for_output_only<int>( + "id", ATTR_DOMAIN_POINT); + MutableSpan<int> ids = id_attribute.as_span(); + + OutputAttribute_Typed<float3> normal_attribute; + OutputAttribute_Typed<float3> rotation_attribute; - MutableSpan<int> ids; MutableSpan<float3> normals; MutableSpan<float3> rotations; - if (attribute_outputs.stable_id_id) { - id_attribute.emplace(point_component.attribute_try_get_for_output_only<int>( - attribute_outputs.stable_id_id.get(), ATTR_DOMAIN_POINT)); - ids = id_attribute->as_span(); - } if (attribute_outputs.normal_id) { - normal_attribute.emplace(point_component.attribute_try_get_for_output_only<float3>( - attribute_outputs.normal_id.get(), ATTR_DOMAIN_POINT)); - normals = normal_attribute->as_span(); + normal_attribute = point_component.attribute_try_get_for_output_only<float3>( + attribute_outputs.normal_id.get(), ATTR_DOMAIN_POINT); + normals = normal_attribute.as_span(); } if (attribute_outputs.rotation_id) { - rotation_attribute.emplace(point_component.attribute_try_get_for_output_only<float3>( - attribute_outputs.rotation_id.get(), ATTR_DOMAIN_POINT)); - rotations = rotation_attribute->as_span(); + rotation_attribute = point_component.attribute_try_get_for_output_only<float3>( + attribute_outputs.rotation_id.get(), ATTR_DOMAIN_POINT); + rotations = rotation_attribute.as_span(); } const Mesh &mesh = *mesh_component.get_for_read(); @@ -371,9 +367,8 @@ BLI_NOINLINE static void compute_attribute_outputs(const MeshComponent &mesh_com const float3 v1_pos = float3(mesh.mvert[v1_index].co); const float3 v2_pos = float3(mesh.mvert[v2_index].co); - if (!ids.is_empty()) { - ids[i] = noise::hash(noise::hash_float(bary_coord), looptri_index); - } + ids[i] = noise::hash(noise::hash_float(bary_coord), looptri_index); + float3 normal; if (!normals.is_empty() || !rotations.is_empty()) { normal_tri_v3(normal, v0_pos, v1_pos, v2_pos); @@ -386,14 +381,13 @@ BLI_NOINLINE static void compute_attribute_outputs(const MeshComponent &mesh_com } } - if (id_attribute) { - id_attribute->save(); - } + id_attribute.save(); + if (normal_attribute) { - normal_attribute->save(); + normal_attribute.save(); } if (rotation_attribute) { - rotation_attribute->save(); + rotation_attribute.save(); } } @@ -405,16 +399,12 @@ static Array<float> calc_full_density_factors_with_selection(const MeshComponent GeometryComponentFieldContext field_context{component, attribute_domain}; const int domain_size = component.attribute_domain_size(attribute_domain); - fn::FieldEvaluator selection_evaluator{field_context, domain_size}; - selection_evaluator.add(selection_field); - selection_evaluator.evaluate(); - const IndexMask selection_mask = selection_evaluator.get_evaluated_as_mask(0); - Array<float> densities(domain_size, 0.0f); - fn::FieldEvaluator density_evaluator{field_context, &selection_mask}; - density_evaluator.add_with_destination(density_field, densities.as_mutable_span()); - density_evaluator.evaluate(); + fn::FieldEvaluator evaluator{field_context, domain_size}; + evaluator.set_selection(selection_field); + evaluator.add_with_destination(density_field, densities.as_mutable_span()); + evaluator.evaluate(); return densities; } @@ -504,6 +494,10 @@ static void point_distribution_calculate(GeometrySet &geometry_set, } } + if (positions.is_empty()) { + return; + } + PointCloud *pointcloud = BKE_pointcloud_new_nomain(positions.size()); memcpy(pointcloud->co, positions.data(), sizeof(float3) * positions.size()); uninitialized_fill_n(pointcloud->radius, pointcloud->totpoint, 0.05f); @@ -526,9 +520,9 @@ static void point_distribution_calculate(GeometrySet &geometry_set, mesh_component, point_component, bary_coords, looptri_indices, attribute_outputs); } -static void geo_node_point_distribute_points_on_faces_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { - GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh"); const GeometryNodeDistributePointsOnFacesMode method = static_cast<GeometryNodeDistributePointsOnFacesMode>(params.node().custom1); @@ -538,13 +532,10 @@ static void geo_node_point_distribute_points_on_faces_exec(GeoNodeExecParams par AttributeOutputs attribute_outputs; if (params.output_is_required("Normal")) { - attribute_outputs.normal_id = StrongAnonymousAttributeID("normal"); + attribute_outputs.normal_id = StrongAnonymousAttributeID("Normal"); } if (params.output_is_required("Rotation")) { - attribute_outputs.rotation_id = StrongAnonymousAttributeID("rotation"); - } - if (params.output_is_required("Stable ID")) { - attribute_outputs.stable_id_id = StrongAnonymousAttributeID("stable id"); + attribute_outputs.rotation_id = StrongAnonymousAttributeID("Rotation"); } geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { @@ -560,35 +551,33 @@ static void geo_node_point_distribute_points_on_faces_exec(GeoNodeExecParams par if (attribute_outputs.normal_id) { params.set_output( "Normal", - AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.normal_id))); + AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.normal_id), + params.attribute_producer_name())); } if (attribute_outputs.rotation_id) { params.set_output( "Rotation", - AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.rotation_id))); - } - if (attribute_outputs.stable_id_id) { - params.set_output( - "Stable ID", - AnonymousAttributeFieldInput::Create<int>(std::move(attribute_outputs.stable_id_id))); + AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.rotation_id), + params.attribute_producer_name())); } } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_distribute_points_on_faces_cc void register_node_type_geo_distribute_points_on_faces() { + namespace file_ns = blender::nodes::node_geo_distribute_points_on_faces_cc; + static bNodeType ntype; geo_node_type_base(&ntype, GEO_NODE_DISTRIBUTE_POINTS_ON_FACES, "Distribute Points on Faces", - NODE_CLASS_GEOMETRY, - 0); - node_type_update(&ntype, blender::nodes::node_point_distribute_points_on_faces_update); + NODE_CLASS_GEOMETRY); + node_type_update(&ntype, file_ns::node_point_distribute_points_on_faces_update); node_type_size(&ntype, 170, 100, 320); - ntype.declare = blender::nodes::geo_node_point_distribute_points_on_faces_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_point_distribute_points_on_faces_exec; - ntype.draw_buttons = blender::nodes::geo_node_point_distribute_points_on_faces_layout; + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.draw_buttons = file_ns::node_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc new file mode 100644 index 00000000000..f6be6c1e7fb --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc @@ -0,0 +1,931 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_task.hh" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_attribute_math.hh" +#include "BKE_mesh.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_dual_mesh_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Mesh").supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input<decl::Bool>("Keep Boundaries") + .default_value(false) + .description( + "Keep non-manifold boundaries of the input mesh in place by avoiding the dual " + "transformation there"); + b.add_output<decl::Geometry>("Dual Mesh"); +} + +enum class EdgeType : int8_t { + Loose = 0, /* No polygons connected to it. */ + Boundary = 1, /* An edge connected to exactly one polygon. */ + Normal = 2, /* A normal edge (connected to two polygons). */ + NonManifold = 3, /* An edge connected to more than two polygons. */ +}; + +static EdgeType get_edge_type_with_added_neighbor(EdgeType old_type) +{ + switch (old_type) { + case EdgeType::Loose: + return EdgeType::Boundary; + case EdgeType::Boundary: + return EdgeType::Normal; + case EdgeType::Normal: + case EdgeType::NonManifold: + return EdgeType::NonManifold; + } + BLI_assert_unreachable(); + return EdgeType::Loose; +} + +enum class VertexType : int8_t { + Loose = 0, /* Either no edges connected or only loose edges connected. */ + Normal = 1, /* A normal vertex. */ + Boundary = 2, /* A vertex on a boundary edge. */ + NonManifold = 3, /* A vertex on a non-manifold edge. */ +}; + +static VertexType get_vertex_type_with_added_neighbor(VertexType old_type) +{ + switch (old_type) { + case VertexType::Loose: + return VertexType::Normal; + case VertexType::Normal: + return VertexType::Boundary; + case VertexType::Boundary: + case VertexType::NonManifold: + return VertexType::NonManifold; + } + BLI_assert_unreachable(); + return VertexType::Loose; +} + +/* Copy only where vertex_types is 'normal'. If keep boundaries is selected, also copy from + * boundary vertices. */ +template<typename T> +static void copy_data_based_on_vertex_types(Span<T> data, + MutableSpan<T> r_data, + const Span<VertexType> vertex_types, + const bool keep_boundaries) +{ + if (keep_boundaries) { + int out_i = 0; + for (const int i : data.index_range()) { + if (ELEM(vertex_types[i], VertexType::Normal, VertexType::Boundary)) { + r_data[out_i] = data[i]; + out_i++; + } + } + } + else { + int out_i = 0; + for (const int i : data.index_range()) { + if (vertex_types[i] == VertexType::Normal) { + r_data[out_i] = data[i]; + out_i++; + } + } + } +} + +template<typename T> +static void copy_data_based_on_pairs(Span<T> data, + MutableSpan<T> r_data, + const Span<std::pair<int, int>> new_to_old_map) +{ + for (const std::pair<int, int> &pair : new_to_old_map) { + r_data[pair.first] = data[pair.second]; + } +} + +/* Copy using the map. */ +template<typename T> +static void copy_data_based_on_new_to_old_map(Span<T> data, + MutableSpan<T> r_data, + const Span<int> new_to_old_map) +{ + for (const int i : r_data.index_range()) { + const int old_i = new_to_old_map[i]; + r_data[i] = data[old_i]; + } +} + +/** + * Transfers the attributes from the original mesh to the new mesh using the following logic: + * - If the attribute was on the face domain it is now on the point domain, and this is true + * for all faces, so we can just copy these. + * - If the attribute was on the vertex domain there are three cases: + * - It was a 'bad' vertex so it is not in the dual mesh, and we can just ignore it + * - It was a normal vertex so it has a corresponding face in the dual mesh to which we can + * transfer. + * - It was a boundary vertex so it has a corresponding face, if keep_boundaries is true. + * Otherwise we can just ignore it. + * - If the attribute was on the edge domain we lookup for the new edges which edge it originated + * from using `new_to_old_edges_map`. We have to do it in this reverse order, because there can + * be more edges in the new mesh if keep boundaries is on. + * - We do the same thing for face corners as we do for edges. + * + * Some of the vertices (on the boundary) in the dual mesh don't come from faces, but from edges or + * vertices. For these the `boundary_vertex_to_relevant_face_map` is used, which maps them to the + * closest face. + */ +static void transfer_attributes( + const Map<AttributeIDRef, AttributeKind> &attributes, + const Span<VertexType> vertex_types, + const bool keep_boundaries, + const Span<int> new_to_old_edges_map, + const Span<int> new_to_old_face_corners_map, + const Span<std::pair<int, int>> boundary_vertex_to_relevant_face_map, + const GeometryComponent &src_component, + GeometryComponent &dst_component) +{ + for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) { + const AttributeIDRef attribute_id = entry.key; + ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read(attribute_id); + if (!src_attribute) { + continue; + } + + AttributeDomain out_domain; + if (src_attribute.domain == ATTR_DOMAIN_FACE) { + out_domain = ATTR_DOMAIN_POINT; + } + else if (src_attribute.domain == ATTR_DOMAIN_POINT) { + out_domain = ATTR_DOMAIN_FACE; + } + else { + /* Edges and Face Corners. */ + out_domain = src_attribute.domain; + } + const CustomDataType data_type = bke::cpp_type_to_custom_data_type( + src_attribute.varray.type()); + OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( + attribute_id, out_domain, data_type); + + if (!dst_attribute) { + continue; + } + + attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + using T = decltype(dummy); + VArray_Span<T> span{src_attribute.varray.typed<T>()}; + MutableSpan<T> dst_span = dst_attribute.as_span<T>(); + if (src_attribute.domain == ATTR_DOMAIN_FACE) { + dst_span.take_front(span.size()).copy_from(span); + if (keep_boundaries) { + copy_data_based_on_pairs(span, dst_span, boundary_vertex_to_relevant_face_map); + } + } + else if (src_attribute.domain == ATTR_DOMAIN_POINT) { + copy_data_based_on_vertex_types(span, dst_span, vertex_types, keep_boundaries); + } + else if (src_attribute.domain == ATTR_DOMAIN_EDGE) { + copy_data_based_on_new_to_old_map(span, dst_span, new_to_old_edges_map); + } + else { + copy_data_based_on_new_to_old_map(span, dst_span, new_to_old_face_corners_map); + } + }); + dst_attribute.save(); + } +} + +/** + * Calculates the boundaries of the mesh. Boundary polygons are not computed since we don't need + * them later on. We use the following definitions: + * - An edge is on a boundary if it is connected to only one polygon. + * - A vertex is on a boundary if it is on an edge on a boundary. + */ +static void calc_boundaries(const Mesh &mesh, + MutableSpan<VertexType> r_vertex_types, + MutableSpan<EdgeType> r_edge_types) +{ + BLI_assert(r_vertex_types.size() == mesh.totvert); + BLI_assert(r_edge_types.size() == mesh.totedge); + r_vertex_types.fill(VertexType::Loose); + r_edge_types.fill(EdgeType::Loose); + + /* Add up the number of polys connected to each edge. */ + for (const int i : IndexRange(mesh.totpoly)) { + const MPoly &poly = mesh.mpoly[i]; + for (const MLoop &loop : Span<MLoop>(&mesh.mloop[poly.loopstart], poly.totloop)) { + r_edge_types[loop.e] = get_edge_type_with_added_neighbor(r_edge_types[loop.e]); + } + } + + /* Update vertices. */ + for (const int i : IndexRange(mesh.totedge)) { + const EdgeType edge_type = r_edge_types[i]; + if (edge_type == EdgeType::Loose) { + continue; + } + const MEdge &edge = mesh.medge[i]; + if (edge_type == EdgeType::Boundary) { + r_vertex_types[edge.v1] = get_vertex_type_with_added_neighbor(r_vertex_types[edge.v1]); + r_vertex_types[edge.v2] = get_vertex_type_with_added_neighbor(r_vertex_types[edge.v2]); + } + else if (edge_type >= EdgeType::NonManifold) { + r_vertex_types[edge.v1] = VertexType::NonManifold; + r_vertex_types[edge.v2] = VertexType::NonManifold; + } + } + + /* Normal verts are on a normal edge, and not on boundary edges or non-manifold edges. */ + for (const int i : IndexRange(mesh.totedge)) { + const EdgeType edge_type = r_edge_types[i]; + if (edge_type == EdgeType::Normal) { + const MEdge &edge = mesh.medge[i]; + if (r_vertex_types[edge.v1] == VertexType::Loose) { + r_vertex_types[edge.v1] = VertexType::Normal; + } + if (r_vertex_types[edge.v2] == VertexType::Loose) { + r_vertex_types[edge.v2] = VertexType::Normal; + } + } + } +} + +/** + * Stores the indices of the polygons connected to each vertex. + */ +static void create_vertex_poly_map(const Mesh &mesh, + MutableSpan<Vector<int>> r_vertex_poly_indices) +{ + for (const int i : IndexRange(mesh.totpoly)) { + const MPoly &poly = mesh.mpoly[i]; + for (const MLoop &loop : Span<MLoop>(&mesh.mloop[poly.loopstart], poly.totloop)) { + r_vertex_poly_indices[loop.v].append(i); + } + } +} + +/** + * Sorts the polygons connected to the given vertex based on polygon adjacency. The ordering is + * so such that the normals point in the same way as the original mesh. If the vertex is a + * boundary vertex, the first and last polygon have a boundary edge connected to the vertex. The + * `r_shared_edges` array at index i is set to the index of the shared edge between the i-th and + * `(i+1)-th` sorted polygon. Similarly the `r_sorted_corners` array at index i is set to the + * corner in the i-th sorted polygon. + * + * How the faces are sorted (see diagrams below): + * (For this explanation we'll assume all faces are oriented clockwise) + * (The vertex whose connected polygons we need to sort is "v0") + * + * \code{.unparsed} + * Normal case: Boundary Vertex case: + * v1 ----- v2 ----- v3 | | | + * | f3 | f0 | v2 ---- v4 --------- v3--- + * | | | | / ,-' | + * v8 ----- v0 ----- v4 | f0 / f1 ,-' | + * | f2 | f1 | | / ,-' | + * | | | | / ,-' | + * v7 ----- v6 ----- v5 | / ,-' f2 | + * | /,-' | + * v0 ------------------ v1--- + * \endcode + * + * - First we get the two corners of each face that have an edge which contains v0. A corner is + * simply a vertex followed by an edge. In this case for the face "f0" for example, we'd end up + * with the corners (v: v4, e: v4<->v0) and (v: v0, e: v0<->v2). Note that if the face was + * oriented counter-clockwise we'd end up with the corners (v: v0, e: v0<->v4) and (v: v2, e: + * v0<->v2) instead. + * - Then we need to choose one polygon as our first. If "v0" is not on a boundary we can just + * choose any polygon. If it is on a boundary some more care needs to be taken. Here we need to + * pick a polygon which lies on the boundary (in the diagram either f0 or f2). To choose between + * the two we need the next step. + * - In the normal case we use this polygon to set `shared_edge_i` which indicates the index of the + * shared edge between this polygon and the next one. There are two possible choices: v0<->v4 and + * v2<->v0. To choose we look at the corners. Since the edge v0<->v2 lies on the corner which has + * v0, we set `shared_edge_i` to the other edge (v0<->v4), such that the next face will be "f1" + * which is the next face in clockwise order. + * - In the boundary vertex case, we do something similar, but we are also forced to choose the + * edge which is not on the boundary. If this doesn't line up with orientation of the polygon, we + * know we'll need to choose the other boundary polygon as our first polygon. If the orientations + * don't line up there as well, it means that the mesh normals are not consistent, and we just + * have to force an orientation for ourselves. (Imagine if f0 is oriented counter-clockwise and + * f2 is oriented clockwise for example) + * - Next comes a loop where we look at the other faces and find the one which has the shared + * edge. Then we set the next shared edge to the other edge on the polygon connected to "v0", and + * continue. Because of the way we've chosen the first shared edge the order of the faces will + * have the same orientation as that of the first polygon. + * (In this case we'd have f0 -> f1 -> f2 -> f3 which also goes around clockwise). + * - Every time we determine a shared edge, we can also add a corner to `r_sorted_corners`. This + * will simply be the corner which doesn't contain the shared edge. + * - Finally if we are in the normal case we also need to add the last "shared edge" to close the + * loop. + */ +static void sort_vertex_polys(const Mesh &mesh, + const int vertex_index, + const bool boundary_vertex, + const Span<EdgeType> edge_types, + MutableSpan<int> connected_polygons, + MutableSpan<int> r_shared_edges, + MutableSpan<int> r_sorted_corners) +{ + if (connected_polygons.size() <= 2 && (!boundary_vertex || connected_polygons.size() == 0)) { + return; + } + + /* For each polygon store the two corners whose edge contains the vertex. */ + Array<std::pair<int, int>> poly_vertex_corners(connected_polygons.size()); + for (const int i : connected_polygons.index_range()) { + const MPoly &poly = mesh.mpoly[connected_polygons[i]]; + bool first_edge_done = false; + for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { + const MLoop &loop = mesh.mloop[loop_index]; + if (mesh.medge[loop.e].v1 == vertex_index || mesh.medge[loop.e].v2 == vertex_index) { + if (!first_edge_done) { + poly_vertex_corners[i].first = loop_index; + first_edge_done = true; + } + else { + poly_vertex_corners[i].second = loop_index; + break; + } + } + } + } + + int shared_edge_i = -1; + /* Determine first polygon and orientation. For now the orientation of the whole loop depends + * on the one polygon we chose as first. It's probably not worth it to check every polygon in + * the loop to determine the 'average' orientation. */ + if (boundary_vertex) { + /* Our first polygon needs to be one which has a boundary edge. */ + for (const int i : connected_polygons.index_range()) { + const MLoop &first_loop = mesh.mloop[poly_vertex_corners[i].first]; + const MLoop &second_loop = mesh.mloop[poly_vertex_corners[i].second]; + if (edge_types[first_loop.e] == EdgeType::Boundary && first_loop.v == vertex_index) { + shared_edge_i = second_loop.e; + r_sorted_corners[0] = poly_vertex_corners[i].first; + std::swap(connected_polygons[i], connected_polygons[0]); + std::swap(poly_vertex_corners[i], poly_vertex_corners[0]); + break; + } + if (edge_types[second_loop.e] == EdgeType::Boundary && second_loop.v == vertex_index) { + shared_edge_i = first_loop.e; + r_sorted_corners[0] = poly_vertex_corners[i].second; + std::swap(connected_polygons[i], connected_polygons[0]); + std::swap(poly_vertex_corners[i], poly_vertex_corners[0]); + break; + } + } + if (shared_edge_i == -1) { + /* The rotation is inconsistent between the two polygons on the boundary. Just choose one + * of the polygon's orientation. */ + for (const int i : connected_polygons.index_range()) { + const MLoop &first_loop = mesh.mloop[poly_vertex_corners[i].first]; + const MLoop &second_loop = mesh.mloop[poly_vertex_corners[i].second]; + if (edge_types[first_loop.e] == EdgeType::Boundary) { + shared_edge_i = second_loop.e; + r_sorted_corners[0] = poly_vertex_corners[i].first; + std::swap(connected_polygons[i], connected_polygons[0]); + std::swap(poly_vertex_corners[i], poly_vertex_corners[0]); + break; + } + if (edge_types[second_loop.e] == EdgeType::Boundary) { + shared_edge_i = first_loop.e; + r_sorted_corners[0] = poly_vertex_corners[i].second; + std::swap(connected_polygons[i], connected_polygons[0]); + std::swap(poly_vertex_corners[i], poly_vertex_corners[0]); + break; + } + } + } + } + else { + /* Any polygon can be the first. Just need to check the orientation. */ + const MLoop &first_loop = mesh.mloop[poly_vertex_corners[0].first]; + const MLoop &second_loop = mesh.mloop[poly_vertex_corners[0].second]; + if (first_loop.v == vertex_index) { + shared_edge_i = second_loop.e; + r_sorted_corners[0] = poly_vertex_corners[0].first; + } + else { + r_sorted_corners[0] = poly_vertex_corners[0].second; + shared_edge_i = first_loop.e; + } + } + BLI_assert(shared_edge_i != -1); + + for (const int i : IndexRange(connected_polygons.size() - 1)) { + r_shared_edges[i] = shared_edge_i; + + /* Look at the other polys to see if it has this shared edge. */ + int j = i + 1; + for (; j < connected_polygons.size(); ++j) { + const MLoop &first_loop = mesh.mloop[poly_vertex_corners[j].first]; + const MLoop &second_loop = mesh.mloop[poly_vertex_corners[j].second]; + if (first_loop.e == shared_edge_i) { + r_sorted_corners[i + 1] = poly_vertex_corners[j].first; + shared_edge_i = second_loop.e; + break; + } + if (second_loop.e == shared_edge_i) { + r_sorted_corners[i + 1] = poly_vertex_corners[j].second; + shared_edge_i = first_loop.e; + break; + } + } + + BLI_assert(j != connected_polygons.size()); + + std::swap(connected_polygons[i + 1], connected_polygons[j]); + std::swap(poly_vertex_corners[i + 1], poly_vertex_corners[j]); + } + + if (!boundary_vertex) { + /* Shared edge between first and last polygon. */ + r_shared_edges.last() = shared_edge_i; + } +} + +/** + * Get the edge on the poly that contains the given vertex and is a boundary edge. + */ +static void boundary_edge_on_poly(const MPoly &poly, + const Mesh &mesh, + const int vertex_index, + const Span<EdgeType> edge_types, + int &r_edge) +{ + for (const MLoop &loop : Span<MLoop>(&mesh.mloop[poly.loopstart], poly.totloop)) { + if (edge_types[loop.e] == EdgeType::Boundary) { + const MEdge &edge = mesh.medge[loop.e]; + if (edge.v1 == vertex_index || edge.v2 == vertex_index) { + r_edge = loop.e; + return; + } + } + } +} + +/** + * Get the two edges on the poly that contain the given vertex and are boundary edges. The + * orientation of the poly is taken into account. + */ +static void boundary_edges_on_poly(const MPoly &poly, + const Mesh &mesh, + const int vertex_index, + const Span<EdgeType> edge_types, + int &r_edge1, + int &r_edge2) +{ + bool edge1_done = false; + /* This is set to true if the order in which we encounter the two edges is inconsistent with the + * orientation of the polygon. */ + bool needs_swap = false; + for (const MLoop &loop : Span<MLoop>(&mesh.mloop[poly.loopstart], poly.totloop)) { + if (edge_types[loop.e] == EdgeType::Boundary) { + const MEdge &edge = mesh.medge[loop.e]; + if (edge.v1 == vertex_index || edge.v2 == vertex_index) { + if (edge1_done) { + if (needs_swap) { + r_edge2 = r_edge1; + r_edge1 = loop.e; + } + else { + r_edge2 = loop.e; + } + return; + } + r_edge1 = loop.e; + edge1_done = true; + if (loop.v == vertex_index) { + needs_swap = true; + } + } + } + } +} + +static void add_edge(const Mesh &mesh, + const int old_edge_i, + const int v1, + const int v2, + Vector<int> &new_to_old_edges_map, + Vector<MEdge> &new_edges, + Vector<int> &loop_edges) +{ + MEdge new_edge = MEdge(mesh.medge[old_edge_i]); + new_edge.v1 = v1; + new_edge.v2 = v2; + const int new_edge_i = new_edges.size(); + new_to_old_edges_map.append(old_edge_i); + new_edges.append(new_edge); + loop_edges.append(new_edge_i); +} + +/* Returns true if the vertex is connected only to the two polygons and is not on the boundary. */ +static bool vertex_needs_dissolving(const int vertex, + const int first_poly_index, + const int second_poly_index, + const Span<VertexType> vertex_types, + const Span<Vector<int>> vertex_poly_indices) +{ + /* Order is guaranteed to be the same because 2poly verts that are not on the boundary are + * ignored in `sort_vertex_polys`. */ + return (vertex_types[vertex] != VertexType::Boundary && + vertex_poly_indices[vertex].size() == 2 && + vertex_poly_indices[vertex][0] == first_poly_index && + vertex_poly_indices[vertex][1] == second_poly_index); +} + +/** + * Finds 'normal' vertices which are connected to only two polygons and marks them to not be + * used in the data-structures derived from the mesh. For each pair of polygons which has such a + * vertex, an edge is created for the dual mesh between the centers of those two polygons. All + * edges in the input mesh which contain such a vertex are marked as 'done' to prevent duplicate + * edges being created. (See T94144) + */ +static void dissolve_redundant_verts(const Mesh &mesh, + const Span<Vector<int>> vertex_poly_indices, + MutableSpan<VertexType> vertex_types, + MutableSpan<int> old_to_new_edges_map, + Vector<MEdge> &new_edges, + Vector<int> &new_to_old_edges_map) +{ + for (const int vert_i : IndexRange(mesh.totvert)) { + if (vertex_poly_indices[vert_i].size() != 2 || vertex_types[vert_i] != VertexType::Normal) { + continue; + } + const int first_poly_index = vertex_poly_indices[vert_i][0]; + const int second_poly_index = vertex_poly_indices[vert_i][1]; + const int new_edge_index = new_edges.size(); + bool edge_created = false; + const MPoly &poly = mesh.mpoly[first_poly_index]; + for (const MLoop &loop : Span<MLoop>(&mesh.mloop[poly.loopstart], poly.totloop)) { + const MEdge &edge = mesh.medge[loop.e]; + const int v1 = edge.v1; + const int v2 = edge.v2; + bool mark_edge = false; + if (vertex_needs_dissolving( + v1, first_poly_index, second_poly_index, vertex_types, vertex_poly_indices)) { + /* This vertex is now 'removed' and should be ignored elsewhere. */ + vertex_types[v1] = VertexType::Loose; + mark_edge = true; + } + if (vertex_needs_dissolving( + v2, first_poly_index, second_poly_index, vertex_types, vertex_poly_indices)) { + /* This vertex is now 'removed' and should be ignored elsewhere. */ + vertex_types[v2] = VertexType::Loose; + mark_edge = true; + } + if (mark_edge) { + if (!edge_created) { + MEdge new_edge = MEdge(edge); + /* The vertex indices in the dual mesh are the polygon indices of the input mesh. */ + new_edge.v1 = first_poly_index; + new_edge.v2 = second_poly_index; + new_to_old_edges_map.append(loop.e); + new_edges.append(new_edge); + edge_created = true; + } + old_to_new_edges_map[loop.e] = new_edge_index; + } + } + } +} + +/** + * Calculate the barycentric dual of a mesh. The dual is only "dual" in terms of connectivity, + * i.e. applying the function twice will give the same vertices, edges, and faces, but not the + * same positions. When the option "Keep Boundaries" is selected the connectivity is no + * longer dual. + * + * For the dual mesh of a manifold input mesh: + * - The vertices are at the centers of the faces of the input mesh. + * - The edges connect the two vertices created from the two faces next to the edge in the input + * mesh. + * - The faces are at the vertices of the input mesh. + * + * Some special cases are needed for boundaries and non-manifold geometry. + */ +static void calc_dual_mesh(GeometrySet &geometry_set, + const MeshComponent &in_component, + const bool keep_boundaries) +{ + const Mesh &mesh_in = *in_component.get_for_read(); + + Map<AttributeIDRef, AttributeKind> attributes; + geometry_set.gather_attributes_for_propagation( + {GEO_COMPONENT_TYPE_MESH}, GEO_COMPONENT_TYPE_MESH, false, attributes); + + Array<VertexType> vertex_types(mesh_in.totvert); + Array<EdgeType> edge_types(mesh_in.totedge); + calc_boundaries(mesh_in, vertex_types, edge_types); + /* Stores the indices of the polygons connected to the vertex. Because the polygons are looped + * over in order of their indices, the polygon's indices will be sorted in ascending order. + (This can change once they are sorted using `sort_vertex_polys`). */ + Array<Vector<int>> vertex_poly_indices(mesh_in.totvert); + Array<Array<int>> vertex_shared_edges(mesh_in.totvert); + Array<Array<int>> vertex_corners(mesh_in.totvert); + create_vertex_poly_map(mesh_in, vertex_poly_indices); + threading::parallel_for(vertex_poly_indices.index_range(), 512, [&](IndexRange range) { + for (const int i : range) { + if (vertex_types[i] == VertexType::Loose || vertex_types[i] >= VertexType::NonManifold || + (!keep_boundaries && vertex_types[i] == VertexType::Boundary)) { + /* Bad vertex that we can't work with. */ + continue; + } + MutableSpan<int> loop_indices = vertex_poly_indices[i]; + Array<int> sorted_corners(loop_indices.size()); + if (vertex_types[i] == VertexType::Normal) { + Array<int> shared_edges(loop_indices.size()); + sort_vertex_polys( + mesh_in, i, false, edge_types, loop_indices, shared_edges, sorted_corners); + vertex_shared_edges[i] = shared_edges; + } + else { + Array<int> shared_edges(loop_indices.size() - 1); + sort_vertex_polys( + mesh_in, i, true, edge_types, loop_indices, shared_edges, sorted_corners); + vertex_shared_edges[i] = shared_edges; + } + vertex_corners[i] = sorted_corners; + } + }); + + Vector<float3> vertex_positions(mesh_in.totpoly); + for (const int i : IndexRange(mesh_in.totpoly)) { + const MPoly poly = mesh_in.mpoly[i]; + BKE_mesh_calc_poly_center( + &poly, &mesh_in.mloop[poly.loopstart], mesh_in.mvert, vertex_positions[i]); + } + + Array<int> boundary_edge_midpoint_index; + if (keep_boundaries) { + /* Only initialize when we actually need it. */ + boundary_edge_midpoint_index.reinitialize(mesh_in.totedge); + /* We need to add vertices at the centers of boundary edges. */ + for (const int i : IndexRange(mesh_in.totedge)) { + if (edge_types[i] == EdgeType::Boundary) { + float3 mid; + const MEdge &edge = mesh_in.medge[i]; + mid_v3_v3v3(mid, mesh_in.mvert[edge.v1].co, mesh_in.mvert[edge.v2].co); + boundary_edge_midpoint_index[i] = vertex_positions.size(); + vertex_positions.append(mid); + } + } + } + + Vector<int> loop_lengths; + Vector<int> loops; + Vector<int> loop_edges; + Vector<MEdge> new_edges; + /* These are used to transfer attributes. */ + Vector<int> new_to_old_face_corners_map; + Vector<int> new_to_old_edges_map; + /* Stores the index of the vertex in the dual and the face it should get the attribute from. */ + Vector<std::pair<int, int>> boundary_vertex_to_relevant_face_map; + /* Since each edge in the dual (except the ones created with keep boundaries) comes from + * exactly one edge in the original, we can use this array to keep track of whether it still + * needs to be created or not. If it's not -1 it gives the index in `new_edges` of the dual + * edge. The edges coming from preserving the boundaries only get added once anyway, so we + * don't need a hash-map for that. */ + Array<int> old_to_new_edges_map(mesh_in.totedge); + old_to_new_edges_map.fill(-1); + + /* This is necessary to prevent duplicate edges from being created, but will likely not do + * anything for most meshes. */ + dissolve_redundant_verts(mesh_in, + vertex_poly_indices, + vertex_types, + old_to_new_edges_map, + new_edges, + new_to_old_edges_map); + + for (const int i : IndexRange(mesh_in.totvert)) { + if (vertex_types[i] == VertexType::Loose || vertex_types[i] >= VertexType::NonManifold || + (!keep_boundaries && vertex_types[i] == VertexType::Boundary)) { + /* Bad vertex that we can't work with. */ + continue; + } + + Vector<int> loop_indices = vertex_poly_indices[i]; + Span<int> shared_edges = vertex_shared_edges[i]; + Span<int> sorted_corners = vertex_corners[i]; + if (vertex_types[i] == VertexType::Normal) { + if (loop_indices.size() <= 2) { + /* We can't make a polygon from 2 vertices. */ + continue; + } + + /* Add edges in the loop. */ + for (const int i : shared_edges.index_range()) { + const int old_edge_i = shared_edges[i]; + if (old_to_new_edges_map[old_edge_i] == -1) { + /* This edge has not been created yet. */ + MEdge new_edge = MEdge(mesh_in.medge[old_edge_i]); + new_edge.v1 = loop_indices[i]; + new_edge.v2 = loop_indices[(i + 1) % loop_indices.size()]; + new_to_old_edges_map.append(old_edge_i); + old_to_new_edges_map[old_edge_i] = new_edges.size(); + new_edges.append(new_edge); + } + loop_edges.append(old_to_new_edges_map[old_edge_i]); + } + + new_to_old_face_corners_map.extend(sorted_corners); + } + else { + /** + * The code handles boundary vertices like the vertex marked "V" in the diagram below. + * The first thing that happens is ordering the faces f1,f2 and f3 (stored in + * loop_indices), together with their shared edges e3 and e4 (which get stored in + * shared_edges). The ordering could end up being clockwise or counterclockwise, for this + * we'll assume that the ordering f1->f2->f3 is chosen. After that we add the edges in + * between the polygons, in this case the edges f1--f2, and f2--f3. Now we need to merge + * these with the boundary edges e1 and e2. To do this we create an edge from f3 to the + * midpoint of e2 (computed in a previous step), from this midpoint to V, from V to the + * midpoint of e1 and from the midpoint of e1 to f1. + * + * \code{.unparsed} + * | | | | | | + * v2 ---- v3 --------- v4--- v2 ---- v3 -------- v4--- + * | f3 / ,-' | | / ,-'| + * | / f2 ,-' | | / ,-' | + * e2 | /e3 ,-' e4 | ====> M1-f3-/--f2-.,-' | + * | / ,-' | ====> | / ,-'\ | + * | / ,-' f1 | | / ,-' f1 | + * | /,-' | | /,-' | | + * V-------------------- v5--- V------------M2----- v5--- + * \endcode + */ + + /* Add the edges in between the polys. */ + for (const int i : shared_edges.index_range()) { + const int old_edge_i = shared_edges[i]; + if (old_to_new_edges_map[old_edge_i] == -1) { + /* This edge has not been created yet. */ + MEdge new_edge = MEdge(mesh_in.medge[old_edge_i]); + new_edge.v1 = loop_indices[i]; + new_edge.v2 = loop_indices[i + 1]; + new_to_old_edges_map.append(old_edge_i); + old_to_new_edges_map[old_edge_i] = new_edges.size(); + new_edges.append(new_edge); + } + loop_edges.append(old_to_new_edges_map[old_edge_i]); + } + + new_to_old_face_corners_map.extend(sorted_corners); + + /* Add the vertex and the midpoints of the two boundary edges to the loop. */ + + /* Get the boundary edges. */ + int edge1; + int edge2; + if (loop_indices.size() >= 2) { + /* The first boundary edge is at the end of the chain of polygons. */ + boundary_edge_on_poly(mesh_in.mpoly[loop_indices.last()], mesh_in, i, edge_types, edge1); + boundary_edge_on_poly(mesh_in.mpoly[loop_indices.first()], mesh_in, i, edge_types, edge2); + } + else { + /* If there is only one polygon both edges are in that polygon. */ + boundary_edges_on_poly( + mesh_in.mpoly[loop_indices[0]], mesh_in, i, edge_types, edge1, edge2); + } + + const int last_face_center = loop_indices.last(); + loop_indices.append(boundary_edge_midpoint_index[edge1]); + new_to_old_face_corners_map.append(sorted_corners.last()); + const int first_midpoint = loop_indices.last(); + if (old_to_new_edges_map[edge1] == -1) { + add_edge(mesh_in, + edge1, + last_face_center, + first_midpoint, + new_to_old_edges_map, + new_edges, + loop_edges); + old_to_new_edges_map[edge1] = new_edges.size() - 1; + boundary_vertex_to_relevant_face_map.append(std::pair(first_midpoint, last_face_center)); + } + else { + loop_edges.append(old_to_new_edges_map[edge1]); + } + loop_indices.append(vertex_positions.size()); + /* This is sort of arbitrary, but interpolating would be a lot harder to do. */ + new_to_old_face_corners_map.append(sorted_corners.first()); + boundary_vertex_to_relevant_face_map.append( + std::pair(loop_indices.last(), last_face_center)); + vertex_positions.append(mesh_in.mvert[i].co); + const int boundary_vertex = loop_indices.last(); + add_edge(mesh_in, + edge1, + first_midpoint, + boundary_vertex, + new_to_old_edges_map, + new_edges, + loop_edges); + + loop_indices.append(boundary_edge_midpoint_index[edge2]); + new_to_old_face_corners_map.append(sorted_corners.first()); + const int second_midpoint = loop_indices.last(); + add_edge(mesh_in, + edge2, + boundary_vertex, + second_midpoint, + new_to_old_edges_map, + new_edges, + loop_edges); + + if (old_to_new_edges_map[edge2] == -1) { + const int first_face_center = loop_indices.first(); + add_edge(mesh_in, + edge2, + second_midpoint, + first_face_center, + new_to_old_edges_map, + new_edges, + loop_edges); + old_to_new_edges_map[edge2] = new_edges.size() - 1; + boundary_vertex_to_relevant_face_map.append(std::pair(second_midpoint, first_face_center)); + } + else { + loop_edges.append(old_to_new_edges_map[edge2]); + } + } + + loop_lengths.append(loop_indices.size()); + for (const int j : loop_indices) { + loops.append(j); + } + } + Mesh *mesh_out = BKE_mesh_new_nomain( + vertex_positions.size(), new_edges.size(), 0, loops.size(), loop_lengths.size()); + MeshComponent out_component; + out_component.replace(mesh_out, GeometryOwnershipType::Editable); + transfer_attributes(attributes, + vertex_types, + keep_boundaries, + new_to_old_edges_map, + new_to_old_face_corners_map, + boundary_vertex_to_relevant_face_map, + in_component, + out_component); + + int loop_start = 0; + for (const int i : IndexRange(mesh_out->totpoly)) { + mesh_out->mpoly[i].loopstart = loop_start; + mesh_out->mpoly[i].totloop = loop_lengths[i]; + loop_start += loop_lengths[i]; + } + for (const int i : IndexRange(mesh_out->totloop)) { + mesh_out->mloop[i].v = loops[i]; + mesh_out->mloop[i].e = loop_edges[i]; + } + for (const int i : IndexRange(mesh_out->totvert)) { + copy_v3_v3(mesh_out->mvert[i].co, vertex_positions[i]); + } + memcpy(mesh_out->medge, new_edges.data(), sizeof(MEdge) * new_edges.size()); + BKE_mesh_normals_tag_dirty(mesh_out); + geometry_set.replace_mesh(mesh_out); +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh"); + const bool keep_boundaries = params.extract_input<bool>("Keep Boundaries"); + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (geometry_set.has_mesh()) { + const MeshComponent &component = *geometry_set.get_component_for_read<MeshComponent>(); + calc_dual_mesh(geometry_set, component, keep_boundaries); + } + }); + params.set_output("Dual Mesh", std::move(geometry_set)); +} + +} // namespace blender::nodes::node_geo_dual_mesh_cc + +void register_node_type_geo_dual_mesh() +{ + namespace file_ns = blender::nodes::node_geo_dual_mesh_cc; + + static bNodeType ntype; + geo_node_type_base(&ntype, GEO_NODE_DUAL_MESH, "Dual Mesh", NODE_CLASS_GEOMETRY); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc new file mode 100644 index 00000000000..9376789cf2c --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc @@ -0,0 +1,97 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BKE_mesh.h" +#include "BKE_mesh_runtime.h" + +#include "bmesh.h" +#include "bmesh_tools.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_edge_split_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_output<decl::Geometry>(N_("Mesh")); +} + +static Mesh *mesh_edge_split(const Mesh &mesh, const IndexMask selection) +{ + BMeshCreateParams bmesh_create_params{}; + bmesh_create_params.use_toolflags = true; + const BMAllocTemplate allocsize = {0, 0, 0, 0}; + BMesh *bm = BM_mesh_create(&allocsize, &bmesh_create_params); + + BMeshFromMeshParams bmesh_from_mesh_params{}; + BM_mesh_bm_from_me(bm, &mesh, &bmesh_from_mesh_params); + + BM_mesh_elem_table_ensure(bm, BM_EDGE); + for (const int i : selection) { + BMEdge *edge = BM_edge_at_index(bm, i); + BM_elem_flag_enable(edge, BM_ELEM_TAG); + } + + BM_mesh_edgesplit(bm, false, true, false); + + Mesh *result = BKE_mesh_from_bmesh_for_eval_nomain(bm, nullptr, &mesh); + BM_mesh_free(bm); + + BKE_mesh_normals_tag_dirty(result); + + return result; +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh"); + + const Field<bool> selection_field = params.extract_input<Field<bool>>("Selection"); + + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (!geometry_set.has_mesh()) { + return; + } + + const MeshComponent &mesh_component = *geometry_set.get_component_for_read<MeshComponent>(); + GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_EDGE}; + const int domain_size = mesh_component.attribute_domain_size(ATTR_DOMAIN_EDGE); + fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + selection_evaluator.add(selection_field); + selection_evaluator.evaluate(); + const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); + + geometry_set.replace_mesh(mesh_edge_split(*mesh_component.get_for_read(), selection)); + }); + + params.set_output("Mesh", std::move(geometry_set)); +} + +} // namespace blender::nodes::node_geo_edge_split_cc + +void register_node_type_geo_edge_split() +{ + namespace file_ns = blender::nodes::node_geo_edge_split_cc; + + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_SPLIT_EDGES, "Split Edges", NODE_CLASS_GEOMETRY); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.declare = file_ns::node_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc new file mode 100644 index 00000000000..1d1c5bd2285 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc @@ -0,0 +1,1365 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_disjoint_set.hh" +#include "BLI_task.hh" +#include "BLI_vector_set.hh" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_attribute_math.hh" +#include "BKE_mesh.h" +#include "BKE_mesh_runtime.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_extrude_mesh_cc { + +NODE_STORAGE_FUNCS(NodeGeometryExtrudeMesh) + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>("Mesh").supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).supports_field().hide_value(); + b.add_input<decl::Vector>(N_("Offset")).subtype(PROP_TRANSLATION).implicit_field().hide_value(); + b.add_input<decl::Float>(N_("Offset Scale")).default_value(1.0f).min(0.0f).supports_field(); + b.add_input<decl::Bool>(N_("Individual")).default_value(true); + b.add_output<decl::Geometry>("Mesh"); + b.add_output<decl::Bool>(N_("Top")).field_source(); + b.add_output<decl::Bool>(N_("Side")).field_source(); +} + +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + uiItemR(layout, ptr, "mode", 0, "", ICON_NONE); +} + +static void node_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryExtrudeMesh *data = MEM_cnew<NodeGeometryExtrudeMesh>(__func__); + data->mode = GEO_NODE_EXTRUDE_MESH_FACES; + node->storage = data; +} + +static void node_update(bNodeTree *ntree, bNode *node) +{ + const NodeGeometryExtrudeMesh &storage = node_storage(*node); + const GeometryNodeExtrudeMeshMode mode = static_cast<GeometryNodeExtrudeMeshMode>(storage.mode); + + bNodeSocket *individual_socket = (bNodeSocket *)node->inputs.last; + + nodeSetSocketAvailability(ntree, individual_socket, mode == GEO_NODE_EXTRUDE_MESH_FACES); +} + +struct AttributeOutputs { + StrongAnonymousAttributeID top_id; + StrongAnonymousAttributeID side_id; +}; + +static void save_selection_as_attribute(MeshComponent &component, + const AnonymousAttributeID *id, + const AttributeDomain domain, + const IndexMask selection) +{ + BLI_assert(!component.attribute_exists(id)); + + OutputAttribute_Typed<bool> attribute = component.attribute_try_get_for_output_only<bool>( + id, domain); + /* Rely on the new attribute being zeroed by default. */ + BLI_assert(!attribute.as_span().as_span().contains(true)); + + if (selection.is_range()) { + attribute.as_span().slice(selection.as_range()).fill(true); + } + else { + attribute.as_span().fill_indices(selection, true); + } + + attribute.save(); +} + +static MutableSpan<MVert> mesh_verts(Mesh &mesh) +{ + return {mesh.mvert, mesh.totvert}; +} +static MutableSpan<MEdge> mesh_edges(Mesh &mesh) +{ + return {mesh.medge, mesh.totedge}; +} +static Span<MPoly> mesh_polys(const Mesh &mesh) +{ + return {mesh.mpoly, mesh.totpoly}; +} +static MutableSpan<MPoly> mesh_polys(Mesh &mesh) +{ + return {mesh.mpoly, mesh.totpoly}; +} +static Span<MLoop> mesh_loops(const Mesh &mesh) +{ + return {mesh.mloop, mesh.totloop}; +} +static MutableSpan<MLoop> mesh_loops(Mesh &mesh) +{ + return {mesh.mloop, mesh.totloop}; +} + +/** + * \note: Some areas in this file rely on the new sections of attributes from #CustomData_realloc + * to be zeroed. + */ +static void expand_mesh(Mesh &mesh, + const int vert_expand, + const int edge_expand, + const int poly_expand, + const int loop_expand) +{ + if (vert_expand != 0) { + CustomData_duplicate_referenced_layers(&mesh.vdata, mesh.totvert); + mesh.totvert += vert_expand; + CustomData_realloc(&mesh.vdata, mesh.totvert); + } + else { + /* Even when the number of vertices is not changed, the mesh can still be deformed. */ + CustomData_duplicate_referenced_layer(&mesh.vdata, CD_MVERT, mesh.totvert); + } + if (edge_expand != 0) { + CustomData_duplicate_referenced_layers(&mesh.edata, mesh.totedge); + mesh.totedge += edge_expand; + CustomData_realloc(&mesh.edata, mesh.totedge); + } + if (poly_expand != 0) { + CustomData_duplicate_referenced_layers(&mesh.pdata, mesh.totpoly); + mesh.totpoly += poly_expand; + CustomData_realloc(&mesh.pdata, mesh.totpoly); + } + if (loop_expand != 0) { + CustomData_duplicate_referenced_layers(&mesh.ldata, mesh.totloop); + mesh.totloop += loop_expand; + CustomData_realloc(&mesh.ldata, mesh.totloop); + } + BKE_mesh_update_customdata_pointers(&mesh, false); +} + +static MEdge new_edge(const int v1, const int v2) +{ + MEdge edge; + edge.v1 = v1; + edge.v2 = v2; + edge.flag = (ME_EDGEDRAW | ME_EDGERENDER); + return edge; +} + +static MEdge new_loose_edge(const int v1, const int v2) +{ + MEdge edge; + edge.v1 = v1; + edge.v2 = v2; + edge.flag = ME_LOOSEEDGE; + return edge; +} + +static MPoly new_poly(const int loopstart, const int totloop) +{ + MPoly poly; + poly.loopstart = loopstart; + poly.totloop = totloop; + poly.flag = 0; + return poly; +} + +template<typename T> void copy_with_indices(MutableSpan<T> dst, Span<T> src, Span<int> indices) +{ + BLI_assert(dst.size() == indices.size()); + for (const int i : dst.index_range()) { + dst[i] = src[indices[i]]; + } +} + +template<typename T> void copy_with_mask(MutableSpan<T> dst, Span<T> src, IndexMask mask) +{ + BLI_assert(dst.size() == mask.size()); + threading::parallel_for(mask.index_range(), 512, [&](const IndexRange range) { + for (const int i : range) { + dst[i] = src[mask[i]]; + } + }); +} + +/** + * \param get_mix_indices_fn: Returns a Span of indices of the source points to mix for every + * result point. + */ +template<typename T, typename GetMixIndicesFn> +void copy_with_mixing(MutableSpan<T> dst, Span<T> src, GetMixIndicesFn get_mix_indices_fn) +{ + threading::parallel_for(dst.index_range(), 512, [&](const IndexRange range) { + attribute_math::DefaultPropatationMixer<T> mixer{dst.slice(range)}; + for (const int i_dst : IndexRange(range.size())) { + for (const int i_src : get_mix_indices_fn(range[i_dst])) { + mixer.mix_in(i_dst, src[i_src]); + } + } + mixer.finalize(); + }); +} + +static Array<Vector<int>> create_vert_to_edge_map(const int vert_size, + Span<MEdge> edges, + const int vert_offset = 0) +{ + Array<Vector<int>> vert_to_edge_map(vert_size); + for (const int i : edges.index_range()) { + vert_to_edge_map[edges[i].v1 - vert_offset].append(i); + vert_to_edge_map[edges[i].v2 - vert_offset].append(i); + } + return vert_to_edge_map; +} + +static void extrude_mesh_vertices(MeshComponent &component, + const Field<bool> &selection_field, + const Field<float3> &offset_field, + const AttributeOutputs &attribute_outputs) +{ + Mesh &mesh = *component.get_for_write(); + const int orig_vert_size = mesh.totvert; + const int orig_edge_size = mesh.totedge; + + GeometryComponentFieldContext context{component, ATTR_DOMAIN_POINT}; + FieldEvaluator evaluator{context, mesh.totvert}; + evaluator.add(offset_field); + evaluator.set_selection(selection_field); + evaluator.evaluate(); + const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); + const VArray<float3> offsets = evaluator.get_evaluated<float3>(0); + + /* This allows parallelizing attribute mixing for new edges. */ + Array<Vector<int>> vert_to_edge_map = create_vert_to_edge_map(orig_vert_size, mesh_edges(mesh)); + + expand_mesh(mesh, selection.size(), selection.size(), 0, 0); + + const IndexRange new_vert_range{orig_vert_size, selection.size()}; + const IndexRange new_edge_range{orig_edge_size, selection.size()}; + + MutableSpan<MVert> new_verts = mesh_verts(mesh).slice(new_vert_range); + MutableSpan<MEdge> new_edges = mesh_edges(mesh).slice(new_edge_range); + + for (const int i_selection : selection.index_range()) { + new_edges[i_selection] = new_loose_edge(selection[i_selection], new_vert_range[i_selection]); + } + + component.attribute_foreach([&](const AttributeIDRef &id, const AttributeMetaData meta_data) { + if (!ELEM(meta_data.domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_EDGE)) { + return true; + } + OutputAttribute attribute = component.attribute_try_get_for_output( + id, meta_data.domain, meta_data.data_type); + attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) { + using T = decltype(dummy); + MutableSpan<T> data = attribute.as_span().typed<T>(); + switch (attribute.domain()) { + case ATTR_DOMAIN_POINT: { + /* New vertices copy the attribute values from their source vertex. */ + copy_with_mask(data.slice(new_vert_range), data.as_span(), selection); + break; + } + case ATTR_DOMAIN_EDGE: { + /* New edge values are mixed from of all the edges connected to the source vertex. */ + copy_with_mixing(data.slice(new_edge_range), data.as_span(), [&](const int i) { + return vert_to_edge_map[selection[i]].as_span(); + }); + break; + } + default: + BLI_assert_unreachable(); + } + }); + + attribute.save(); + return true; + }); + + devirtualize_varray(offsets, [&](const auto offsets) { + threading::parallel_for(selection.index_range(), 1024, [&](const IndexRange range) { + for (const int i : range) { + const float3 offset = offsets[selection[i]]; + add_v3_v3(new_verts[i].co, offset); + } + }); + }); + + if (attribute_outputs.top_id) { + save_selection_as_attribute( + component, attribute_outputs.top_id.get(), ATTR_DOMAIN_POINT, new_vert_range); + } + if (attribute_outputs.side_id) { + save_selection_as_attribute( + component, attribute_outputs.side_id.get(), ATTR_DOMAIN_EDGE, new_edge_range); + } + + BKE_mesh_runtime_clear_cache(&mesh); + BKE_mesh_normals_tag_dirty(&mesh); +} + +static Array<Vector<int, 2>> mesh_calculate_polys_of_edge(const Mesh &mesh) +{ + Span<MPoly> polys = mesh_polys(mesh); + Span<MLoop> loops = mesh_loops(mesh); + Array<Vector<int, 2>> polys_of_edge(mesh.totedge); + + for (const int i_poly : polys.index_range()) { + const MPoly &poly = polys[i_poly]; + for (const MLoop &loop : loops.slice(poly.loopstart, poly.totloop)) { + polys_of_edge[loop.e].append(i_poly); + } + } + + return polys_of_edge; +} + +static void fill_quad_consistent_direction(Span<MLoop> other_poly_loops, + MutableSpan<MLoop> new_loops, + const int vert_connected_to_poly_1, + const int vert_connected_to_poly_2, + const int vert_across_from_poly_1, + const int vert_across_from_poly_2, + const int edge_connected_to_poly, + const int connecting_edge_1, + const int edge_across_from_poly, + const int connecting_edge_2) +{ + /* Find the loop on the polygon connected to the new quad that uses the duplicate edge. */ + bool start_with_connecting_edge = true; + for (const MLoop &loop : other_poly_loops) { + if (loop.e == edge_connected_to_poly) { + start_with_connecting_edge = loop.v == vert_connected_to_poly_1; + break; + } + } + if (start_with_connecting_edge) { + new_loops[0].v = vert_connected_to_poly_1; + new_loops[0].e = connecting_edge_1; + new_loops[1].v = vert_across_from_poly_1; + new_loops[1].e = edge_across_from_poly; + new_loops[2].v = vert_across_from_poly_2; + new_loops[2].e = connecting_edge_2; + new_loops[3].v = vert_connected_to_poly_2; + new_loops[3].e = edge_connected_to_poly; + } + else { + new_loops[0].v = vert_connected_to_poly_1; + new_loops[0].e = edge_connected_to_poly; + new_loops[1].v = vert_connected_to_poly_2; + new_loops[1].e = connecting_edge_2; + new_loops[2].v = vert_across_from_poly_2; + new_loops[2].e = edge_across_from_poly; + new_loops[3].v = vert_across_from_poly_1; + new_loops[3].e = connecting_edge_1; + } +} + +template<typename T> +static VectorSet<int> vert_indices_from_edges(const Mesh &mesh, const Span<T> edge_indices) +{ + static_assert(is_same_any_v<T, int, int64_t>); + + VectorSet<int> vert_indices; + vert_indices.reserve(edge_indices.size()); + for (const T i_edge : edge_indices) { + const MEdge &edge = mesh.medge[i_edge]; + vert_indices.add(edge.v1); + vert_indices.add(edge.v2); + } + return vert_indices; +} + +static void extrude_mesh_edges(MeshComponent &component, + const Field<bool> &selection_field, + const Field<float3> &offset_field, + const AttributeOutputs &attribute_outputs) +{ + Mesh &mesh = *component.get_for_write(); + const int orig_vert_size = mesh.totvert; + Span<MEdge> orig_edges = mesh_edges(mesh); + Span<MPoly> orig_polys = mesh_polys(mesh); + const int orig_loop_size = mesh.totloop; + + GeometryComponentFieldContext edge_context{component, ATTR_DOMAIN_EDGE}; + FieldEvaluator edge_evaluator{edge_context, mesh.totedge}; + edge_evaluator.set_selection(selection_field); + edge_evaluator.add(offset_field); + edge_evaluator.evaluate(); + const IndexMask edge_selection = edge_evaluator.get_evaluated_selection_as_mask(); + const VArray<float3> &edge_offsets = edge_evaluator.get_evaluated<float3>(0); + if (edge_selection.is_empty()) { + return; + } + + const Array<Vector<int, 2>> edge_to_poly_map = mesh_calculate_polys_of_edge(mesh); + + /* Find the offsets on the vertex domain for translation. This must be done before the mesh's + * custom data layers are reallocated, in case the virtual array references on of them. */ + Array<float3> vert_offsets; + if (!edge_offsets.is_single()) { + vert_offsets.reinitialize(orig_vert_size); + attribute_math::DefaultPropatationMixer<float3> mixer(vert_offsets); + for (const int i_edge : edge_selection) { + const MEdge &edge = orig_edges[i_edge]; + const float3 offset = edge_offsets[i_edge]; + mixer.mix_in(edge.v1, offset); + mixer.mix_in(edge.v2, offset); + } + mixer.finalize(); + } + + const VectorSet<int> new_vert_indices = vert_indices_from_edges(mesh, edge_selection.indices()); + + const IndexRange new_vert_range{orig_vert_size, new_vert_indices.size()}; + /* The extruded edges connect the original and duplicate edges. */ + const IndexRange connect_edge_range{orig_edges.size(), new_vert_range.size()}; + /* The duplicate edges are extruded copies of the selected edges. */ + const IndexRange duplicate_edge_range = connect_edge_range.after(edge_selection.size()); + /* There is a new polygon for every selected edge. */ + const IndexRange new_poly_range{orig_polys.size(), edge_selection.size()}; + /* Every new polygon is a quad with four corners. */ + const IndexRange new_loop_range{orig_loop_size, new_poly_range.size() * 4}; + + expand_mesh(mesh, + new_vert_range.size(), + connect_edge_range.size() + duplicate_edge_range.size(), + new_poly_range.size(), + new_loop_range.size()); + + MutableSpan<MVert> new_verts = mesh_verts(mesh).slice(new_vert_range); + MutableSpan<MEdge> connect_edges = mesh_edges(mesh).slice(connect_edge_range); + MutableSpan<MEdge> duplicate_edges = mesh_edges(mesh).slice(duplicate_edge_range); + MutableSpan<MPoly> polys = mesh_polys(mesh); + MutableSpan<MPoly> new_polys = polys.slice(new_poly_range); + MutableSpan<MLoop> loops = mesh_loops(mesh); + MutableSpan<MLoop> new_loops = loops.slice(new_loop_range); + + for (const int i : connect_edges.index_range()) { + connect_edges[i] = new_edge(new_vert_indices[i], new_vert_range[i]); + } + + for (const int i : duplicate_edges.index_range()) { + const MEdge &orig_edge = mesh.medge[edge_selection[i]]; + const int i_new_vert_1 = new_vert_indices.index_of(orig_edge.v1); + const int i_new_vert_2 = new_vert_indices.index_of(orig_edge.v2); + duplicate_edges[i] = new_edge(new_vert_range[i_new_vert_1], new_vert_range[i_new_vert_2]); + } + + for (const int i : new_polys.index_range()) { + new_polys[i] = new_poly(new_loop_range[i * 4], 4); + } + + for (const int i : edge_selection.index_range()) { + const int orig_edge_index = edge_selection[i]; + + const MEdge &duplicate_edge = duplicate_edges[i]; + const int new_vert_1 = duplicate_edge.v1; + const int new_vert_2 = duplicate_edge.v2; + const int extrude_index_1 = new_vert_1 - orig_vert_size; + const int extrude_index_2 = new_vert_2 - orig_vert_size; + + Span<int> connected_polys = edge_to_poly_map[orig_edge_index]; + + /* When there was a single polygon connected to the new polygon, we can use the old one to keep + * the face direction consistent. When there is more than one connected edge, the new face + * direction is totally arbitrary and the only goal for the behavior is to be deterministic. */ + Span<MLoop> connected_poly_loops = {}; + if (connected_polys.size() == 1) { + const MPoly &connected_poly = polys[connected_polys.first()]; + connected_poly_loops = loops.slice(connected_poly.loopstart, connected_poly.totloop); + } + fill_quad_consistent_direction(connected_poly_loops, + new_loops.slice(4 * i, 4), + new_vert_indices[extrude_index_1], + new_vert_indices[extrude_index_2], + new_vert_1, + new_vert_2, + orig_edge_index, + connect_edge_range[extrude_index_1], + duplicate_edge_range[i], + connect_edge_range[extrude_index_2]); + } + + /* Create a map of indices in the extruded vertices array to all of the indices of edges + * in the duplicate edges array that connect to that vertex. This can be used to simplify the + * mixing of attribute data for the connecting edges. */ + const Array<Vector<int>> new_vert_to_duplicate_edge_map = create_vert_to_edge_map( + new_vert_range.size(), duplicate_edges, orig_vert_size); + + component.attribute_foreach([&](const AttributeIDRef &id, const AttributeMetaData meta_data) { + OutputAttribute attribute = component.attribute_try_get_for_output( + id, meta_data.domain, meta_data.data_type); + if (!attribute) { + return true; /* Impossible to write the "normal" attribute. */ + } + + attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) { + using T = decltype(dummy); + MutableSpan<T> data = attribute.as_span().typed<T>(); + switch (attribute.domain()) { + case ATTR_DOMAIN_POINT: { + /* New vertices copy the attribute values from their source vertex. */ + copy_with_indices(data.slice(new_vert_range), data.as_span(), new_vert_indices); + break; + } + case ATTR_DOMAIN_EDGE: { + /* Edges parallel to original edges copy the edge attributes from the original edges. */ + MutableSpan<T> duplicate_data = data.slice(duplicate_edge_range); + copy_with_mask(duplicate_data, data.as_span(), edge_selection); + + /* Edges connected to original vertices mix values of selected connected edges. */ + MutableSpan<T> connect_data = data.slice(connect_edge_range); + copy_with_mixing(connect_data, duplicate_data.as_span(), [&](const int i_new_vert) { + return new_vert_to_duplicate_edge_map[i_new_vert].as_span(); + }); + break; + } + case ATTR_DOMAIN_FACE: { + /* Attribute values for new faces are a mix of the values of faces connected to the its + * original edge. */ + copy_with_mixing(data.slice(new_poly_range), data.as_span(), [&](const int i) { + return edge_to_poly_map[edge_selection[i]].as_span(); + }); + + break; + } + case ATTR_DOMAIN_CORNER: { + /* New corners get the average value of all adjacent corners on original faces connected + * to the original edge of their face. */ + MutableSpan<T> new_data = data.slice(new_loop_range); + threading::parallel_for(edge_selection.index_range(), 256, [&](const IndexRange range) { + for (const int i_edge_selection : range) { + const int orig_edge_index = edge_selection[i_edge_selection]; + + Span<int> connected_polys = edge_to_poly_map[orig_edge_index]; + if (connected_polys.is_empty()) { + /* If there are no connected polygons, there is no corner data to + * interpolate. */ + new_data.slice(4 * i_edge_selection, 4).fill(T()); + continue; + } + + /* Both corners on each vertical edge of the side polygon get the same value, + * so there are only two unique values to mix. */ + Array<T> side_poly_corner_data(2); + attribute_math::DefaultPropatationMixer<T> mixer{side_poly_corner_data}; + + const MEdge &duplicate_edge = duplicate_edges[i_edge_selection]; + const int new_vert_1 = duplicate_edge.v1; + const int new_vert_2 = duplicate_edge.v2; + const int orig_vert_1 = new_vert_indices[new_vert_1 - orig_vert_size]; + const int orig_vert_2 = new_vert_indices[new_vert_2 - orig_vert_size]; + + /* Average the corner data from the corners that share a vertex from the + * polygons that share an edge with the extruded edge. */ + for (const int i_connected_poly : connected_polys.index_range()) { + const MPoly &connected_poly = polys[connected_polys[i_connected_poly]]; + for (const int i_loop : + IndexRange(connected_poly.loopstart, connected_poly.totloop)) { + const MLoop &loop = loops[i_loop]; + if (loop.v == orig_vert_1) { + mixer.mix_in(0, data[i_loop]); + } + if (loop.v == orig_vert_2) { + mixer.mix_in(1, data[i_loop]); + } + } + } + + mixer.finalize(); + + /* Instead of replicating the order in #fill_quad_consistent_direction here, it's + * simpler (though probably slower) to just match the corner data based on the vertex + * indices. */ + for (const int i : IndexRange(4 * i_edge_selection, 4)) { + if (ELEM(new_loops[i].v, new_vert_1, orig_vert_1)) { + new_data[i] = side_poly_corner_data.first(); + } + else if (ELEM(new_loops[i].v, new_vert_2, orig_vert_2)) { + new_data[i] = side_poly_corner_data.last(); + } + } + } + }); + break; + } + default: + BLI_assert_unreachable(); + } + }); + + attribute.save(); + return true; + }); + + if (edge_offsets.is_single()) { + const float3 offset = edge_offsets.get_internal_single(); + threading::parallel_for(new_verts.index_range(), 1024, [&](const IndexRange range) { + for (const int i : range) { + add_v3_v3(new_verts[i].co, offset); + } + }); + } + else { + threading::parallel_for(new_verts.index_range(), 1024, [&](const IndexRange range) { + for (const int i : range) { + add_v3_v3(new_verts[i].co, vert_offsets[new_vert_indices[i]]); + } + }); + } + + if (attribute_outputs.top_id) { + save_selection_as_attribute( + component, attribute_outputs.top_id.get(), ATTR_DOMAIN_EDGE, duplicate_edge_range); + } + if (attribute_outputs.side_id) { + save_selection_as_attribute( + component, attribute_outputs.side_id.get(), ATTR_DOMAIN_FACE, new_poly_range); + } + + BKE_mesh_runtime_clear_cache(&mesh); + BKE_mesh_normals_tag_dirty(&mesh); +} + +/** + * Edges connected to one selected face are on the boundary of a region and will be duplicated into + * a "side face". Edges inside a region will be duplicated to leave any original faces unchanged. + */ +static void extrude_mesh_face_regions(MeshComponent &component, + const Field<bool> &selection_field, + const Field<float3> &offset_field, + const AttributeOutputs &attribute_outputs) +{ + Mesh &mesh = *component.get_for_write(); + const int orig_vert_size = mesh.totvert; + Span<MEdge> orig_edges = mesh_edges(mesh); + Span<MPoly> orig_polys = mesh_polys(mesh); + Span<MLoop> orig_loops = mesh_loops(mesh); + + GeometryComponentFieldContext poly_context{component, ATTR_DOMAIN_FACE}; + FieldEvaluator poly_evaluator{poly_context, mesh.totpoly}; + poly_evaluator.set_selection(selection_field); + poly_evaluator.add(offset_field); + poly_evaluator.evaluate(); + const IndexMask poly_selection = poly_evaluator.get_evaluated_selection_as_mask(); + const VArray<float3> &poly_offsets = poly_evaluator.get_evaluated<float3>(0); + if (poly_selection.is_empty()) { + return; + } + + Array<bool> poly_selection_array(orig_polys.size(), false); + for (const int i_poly : poly_selection) { + poly_selection_array[i_poly] = true; + } + + /* Mix the offsets from the face domain to the vertex domain. Evaluate on the face domain above + * in order to be consistent with the selection, and to use the face normals rather than vertex + * normals as an offset, for example. */ + Array<float3> vert_offsets; + if (!poly_offsets.is_single()) { + vert_offsets.reinitialize(orig_vert_size); + attribute_math::DefaultPropatationMixer<float3> mixer(vert_offsets); + for (const int i_poly : poly_selection) { + const MPoly &poly = orig_polys[i_poly]; + const float3 offset = poly_offsets[i_poly]; + for (const MLoop &loop : orig_loops.slice(poly.loopstart, poly.totloop)) { + mixer.mix_in(loop.v, offset); + } + } + mixer.finalize(); + } + + /* All of the faces (selected and deselected) connected to each edge. */ + const Array<Vector<int, 2>> edge_to_poly_map = mesh_calculate_polys_of_edge(mesh); + + /* All vertices that are connected to the selected polygons. + * Start the size at one vert per poly to reduce unnecessary reallocation. */ + VectorSet<int> all_selected_verts; + all_selected_verts.reserve(orig_polys.size()); + for (const int i_poly : poly_selection) { + const MPoly &poly = orig_polys[i_poly]; + for (const MLoop &loop : orig_loops.slice(poly.loopstart, poly.totloop)) { + all_selected_verts.add(loop.v); + } + } + + /* Edges inside of an extruded region that are also attached to deselected edges. They must be + * duplicated in order to leave the old edge attached to the unchanged deselected faces. */ + VectorSet<int> new_inner_edge_indices; + /* Edges inside of an extruded region. Their vertices should be translated + * with the offset, but the edges themselves should not be duplicated. */ + Vector<int> inner_edge_indices; + /* The extruded face corresponding to each boundary edge (and each boundary face). */ + Vector<int> edge_extruded_face_indices; + /* Edges on the outside of selected regions, either because there are no + * other connected faces, or because all of the other faces aren't selected. */ + VectorSet<int> boundary_edge_indices; + for (const int i_edge : orig_edges.index_range()) { + Span<int> polys = edge_to_poly_map[i_edge]; + + int i_selected_poly = -1; + int deselected_poly_count = 0; + int selected_poly_count = 0; + for (const int i_other_poly : polys) { + if (poly_selection_array[i_other_poly]) { + selected_poly_count++; + i_selected_poly = i_other_poly; + } + else { + deselected_poly_count++; + } + } + + if (selected_poly_count == 1) { + /* If there is only one selected polygon connected to the edge, + * the edge should be extruded to form a "side face". */ + boundary_edge_indices.add_new(i_edge); + edge_extruded_face_indices.append(i_selected_poly); + } + else if (selected_poly_count > 1) { + /* The edge is inside an extruded region of faces. */ + if (deselected_poly_count > 0) { + /* Add edges that are also connected to deselected edges to a separate list. */ + new_inner_edge_indices.add_new(i_edge); + } + else { + /* Otherwise, just keep track of edges inside the region so that + * we can reattach them to duplicated vertices if necessary. */ + inner_edge_indices.append(i_edge); + } + } + } + + VectorSet<int> new_vert_indices = vert_indices_from_edges(mesh, boundary_edge_indices.as_span()); + /* Before adding the rest of the new vertices from the new inner edges, store the number + * of new vertices from the boundary edges, since this is the number of connecting edges. */ + const int extruded_vert_size = new_vert_indices.size(); + + /* The vertices attached to duplicate inner edges also have to be duplicated. */ + for (const int i_edge : new_inner_edge_indices) { + const MEdge &edge = mesh.medge[i_edge]; + new_vert_indices.add(edge.v1); + new_vert_indices.add(edge.v2); + } + + /* New vertices forming the duplicated boundary edges and the ends of the new inner edges. */ + const IndexRange new_vert_range{orig_vert_size, new_vert_indices.size()}; + /* One edge connects each selected vertex to a new vertex on the extruded polygons. */ + const IndexRange connect_edge_range{orig_edges.size(), extruded_vert_size}; + /* Each selected edge is duplicated to form a single edge on the extrusion. */ + const IndexRange boundary_edge_range = connect_edge_range.after(boundary_edge_indices.size()); + /* Duplicated edges inside regions that were connected to deselected faces. */ + const IndexRange new_inner_edge_range = boundary_edge_range.after(new_inner_edge_indices.size()); + /* Each edge selected for extrusion is extruded into a single face. */ + const IndexRange side_poly_range{orig_polys.size(), boundary_edge_indices.size()}; + /* The loops that form the new side faces. */ + const IndexRange side_loop_range{orig_loops.size(), side_poly_range.size() * 4}; + + expand_mesh(mesh, + new_vert_range.size(), + connect_edge_range.size() + boundary_edge_range.size() + new_inner_edge_range.size(), + side_poly_range.size(), + side_loop_range.size()); + + MutableSpan<MEdge> edges = mesh_edges(mesh); + MutableSpan<MEdge> connect_edges = edges.slice(connect_edge_range); + MutableSpan<MEdge> boundary_edges = edges.slice(boundary_edge_range); + MutableSpan<MEdge> new_inner_edges = edges.slice(new_inner_edge_range); + MutableSpan<MPoly> polys = mesh_polys(mesh); + MutableSpan<MPoly> new_polys = polys.slice(side_poly_range); + MutableSpan<MLoop> loops = mesh_loops(mesh); + MutableSpan<MLoop> new_loops = loops.slice(side_loop_range); + + /* Initialize the edges that form the sides of the extrusion. */ + for (const int i : connect_edges.index_range()) { + connect_edges[i] = new_edge(new_vert_indices[i], new_vert_range[i]); + } + + /* Initialize the edges that form the top of the extrusion. */ + for (const int i : boundary_edges.index_range()) { + const MEdge &orig_edge = edges[boundary_edge_indices[i]]; + const int i_new_vert_1 = new_vert_indices.index_of(orig_edge.v1); + const int i_new_vert_2 = new_vert_indices.index_of(orig_edge.v2); + boundary_edges[i] = new_edge(new_vert_range[i_new_vert_1], new_vert_range[i_new_vert_2]); + } + + /* Initialize the new edges inside of extrude regions. */ + for (const int i : new_inner_edge_indices.index_range()) { + const MEdge &orig_edge = edges[new_inner_edge_indices[i]]; + const int i_new_vert_1 = new_vert_indices.index_of(orig_edge.v1); + const int i_new_vert_2 = new_vert_indices.index_of(orig_edge.v2); + new_inner_edges[i] = new_edge(new_vert_range[i_new_vert_1], new_vert_range[i_new_vert_2]); + } + + /* Initialize the new side polygons. */ + for (const int i : new_polys.index_range()) { + new_polys[i] = new_poly(side_loop_range[i * 4], 4); + } + + /* Connect original edges inside face regions to any new vertices, if necessary. */ + for (const int i : inner_edge_indices) { + MEdge &edge = edges[i]; + const int i_new_vert_1 = new_vert_indices.index_of_try(edge.v1); + const int i_new_vert_2 = new_vert_indices.index_of_try(edge.v2); + if (i_new_vert_1 != -1) { + edge.v1 = new_vert_range[i_new_vert_1]; + } + if (i_new_vert_2 != -1) { + edge.v2 = new_vert_range[i_new_vert_2]; + } + } + + /* Connect the selected faces to the extruded or duplicated edges and the new vertices. */ + for (const int i_poly : poly_selection) { + const MPoly &poly = polys[i_poly]; + for (MLoop &loop : loops.slice(poly.loopstart, poly.totloop)) { + const int i_new_vert = new_vert_indices.index_of_try(loop.v); + if (i_new_vert != -1) { + loop.v = new_vert_range[i_new_vert]; + } + const int i_boundary_edge = boundary_edge_indices.index_of_try(loop.e); + if (i_boundary_edge != -1) { + loop.e = boundary_edge_range[i_boundary_edge]; + /* Skip the next check, an edge cannot be both a boundary edge and an inner edge. */ + continue; + } + const int i_new_inner_edge = new_inner_edge_indices.index_of_try(loop.e); + if (i_new_inner_edge != -1) { + loop.e = new_inner_edge_range[i_new_inner_edge]; + } + } + } + + /* Create the faces on the sides of extruded regions. */ + for (const int i : boundary_edge_indices.index_range()) { + const MEdge &boundary_edge = boundary_edges[i]; + const int new_vert_1 = boundary_edge.v1; + const int new_vert_2 = boundary_edge.v2; + const int extrude_index_1 = new_vert_1 - orig_vert_size; + const int extrude_index_2 = new_vert_2 - orig_vert_size; + + const MPoly &extrude_poly = polys[edge_extruded_face_indices[i]]; + + fill_quad_consistent_direction(loops.slice(extrude_poly.loopstart, extrude_poly.totloop), + new_loops.slice(4 * i, 4), + new_vert_1, + new_vert_2, + new_vert_indices[extrude_index_1], + new_vert_indices[extrude_index_2], + boundary_edge_range[i], + connect_edge_range[extrude_index_1], + boundary_edge_indices[i], + connect_edge_range[extrude_index_2]); + } + + /* Create a map of indices in the extruded vertices array to all of the indices of edges + * in the duplicate edges array that connect to that vertex. This can be used to simplify the + * mixing of attribute data for the connecting edges. */ + const Array<Vector<int>> new_vert_to_duplicate_edge_map = create_vert_to_edge_map( + new_vert_range.size(), boundary_edges, orig_vert_size); + + component.attribute_foreach([&](const AttributeIDRef &id, const AttributeMetaData meta_data) { + OutputAttribute attribute = component.attribute_try_get_for_output( + id, meta_data.domain, meta_data.data_type); + if (!attribute) { + return true; /* Impossible to write the "normal" attribute. */ + } + + attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) { + using T = decltype(dummy); + MutableSpan<T> data = attribute.as_span().typed<T>(); + switch (attribute.domain()) { + case ATTR_DOMAIN_POINT: { + /* New vertices copy the attributes from their original vertices. */ + copy_with_indices(data.slice(new_vert_range), data.as_span(), new_vert_indices); + break; + } + case ATTR_DOMAIN_EDGE: { + /* Edges parallel to original edges copy the edge attributes from the original edges. */ + MutableSpan<T> boundary_data = data.slice(boundary_edge_range); + copy_with_indices(boundary_data, data.as_span(), boundary_edge_indices); + + /* Edges inside of face regions also just duplicate their source data. */ + MutableSpan<T> new_inner_data = data.slice(new_inner_edge_range); + copy_with_indices(new_inner_data, data.as_span(), new_inner_edge_indices); + + /* Edges connected to original vertices mix values of selected connected edges. */ + MutableSpan<T> connect_data = data.slice(connect_edge_range); + copy_with_mixing(connect_data, boundary_data.as_span(), [&](const int i) { + return new_vert_to_duplicate_edge_map[i].as_span(); + }); + break; + } + case ATTR_DOMAIN_FACE: { + /* New faces on the side of extrusions get the values from the corresponding selected + * face. */ + copy_with_indices( + data.slice(side_poly_range), data.as_span(), edge_extruded_face_indices); + break; + } + case ATTR_DOMAIN_CORNER: { + /* New corners get the values from the corresponding corner on the extruded face. */ + MutableSpan<T> new_data = data.slice(side_loop_range); + threading::parallel_for( + boundary_edge_indices.index_range(), 256, [&](const IndexRange range) { + for (const int i_boundary_edge : range) { + const MPoly &poly = polys[edge_extruded_face_indices[i_boundary_edge]]; + + const MEdge &boundary_edge = boundary_edges[i_boundary_edge]; + const int new_vert_1 = boundary_edge.v1; + const int new_vert_2 = boundary_edge.v2; + const int orig_vert_1 = new_vert_indices[new_vert_1 - orig_vert_size]; + const int orig_vert_2 = new_vert_indices[new_vert_2 - orig_vert_size]; + + /* Retrieve the data for the first two sides of the quad from the extruded + * polygon, which we generally expect to have just a small amount of sides. This + * loop could be eliminated by adding a cache of connected loops (which would + * also simplify some of the other code to find the correct loops on the extruded + * face). */ + T data_1; + T data_2; + for (const int i_loop : IndexRange(poly.loopstart, poly.totloop)) { + if (loops[i_loop].v == new_vert_1) { + data_1 = data[i_loop]; + } + if (loops[i_loop].v == new_vert_2) { + data_2 = data[i_loop]; + } + } + + /* Instead of replicating the order in #fill_quad_consistent_direction here, it's + * simpler (though probably slower) to just match the corner data based on the + * vertex indices. */ + for (const int i : IndexRange(4 * i_boundary_edge, 4)) { + if (ELEM(new_loops[i].v, new_vert_1, orig_vert_1)) { + new_data[i] = data_1; + } + else if (ELEM(new_loops[i].v, new_vert_2, orig_vert_2)) { + new_data[i] = data_2; + } + } + } + }); + break; + } + default: + BLI_assert_unreachable(); + } + }); + + attribute.save(); + return true; + }); + + /* Translate vertices based on the offset. If the vertex is used by a selected edge, it will + * have been duplicated and only the new vertex should use the offset. Otherwise the vertex might + * still need an offset, but it was reused on the inside of a region of extruded faces. */ + if (poly_offsets.is_single()) { + const float3 offset = poly_offsets.get_internal_single(); + threading::parallel_for( + IndexRange(all_selected_verts.size()), 1024, [&](const IndexRange range) { + for (const int i_orig : all_selected_verts.as_span().slice(range)) { + const int i_new = new_vert_indices.index_of_try(i_orig); + MVert &vert = mesh_verts(mesh)[(i_new == -1) ? i_orig : new_vert_range[i_new]]; + add_v3_v3(vert.co, offset); + } + }); + } + else { + threading::parallel_for( + IndexRange(all_selected_verts.size()), 1024, [&](const IndexRange range) { + for (const int i_orig : all_selected_verts.as_span().slice(range)) { + const int i_new = new_vert_indices.index_of_try(i_orig); + const float3 offset = vert_offsets[i_orig]; + MVert &vert = mesh_verts(mesh)[(i_new == -1) ? i_orig : new_vert_range[i_new]]; + add_v3_v3(vert.co, offset); + } + }); + } + + if (attribute_outputs.top_id) { + save_selection_as_attribute( + component, attribute_outputs.top_id.get(), ATTR_DOMAIN_FACE, poly_selection); + } + if (attribute_outputs.side_id) { + save_selection_as_attribute( + component, attribute_outputs.side_id.get(), ATTR_DOMAIN_FACE, side_poly_range); + } + + BKE_mesh_runtime_clear_cache(&mesh); + BKE_mesh_normals_tag_dirty(&mesh); +} + +/* Get the range into an array of extruded corners, edges, or vertices for a particular polygon. */ +static IndexRange selected_corner_range(Span<int> offsets, const int index) +{ + const int offset = offsets[index]; + const int next_offset = offsets[index + 1]; + return IndexRange(offset, next_offset - offset); +} + +static void extrude_individual_mesh_faces(MeshComponent &component, + const Field<bool> &selection_field, + const Field<float3> &offset_field, + const AttributeOutputs &attribute_outputs) +{ + Mesh &mesh = *component.get_for_write(); + const int orig_vert_size = mesh.totvert; + const int orig_edge_size = mesh.totedge; + Span<MPoly> orig_polys = mesh_polys(mesh); + Span<MLoop> orig_loops = mesh_loops(mesh); + + /* Use a mesh for the result of the evaluation because the mesh is reallocated before + * the vertices are moved, and the evaluated result might reference an attribute. */ + Array<float3> poly_offset(orig_polys.size()); + GeometryComponentFieldContext poly_context{component, ATTR_DOMAIN_FACE}; + FieldEvaluator poly_evaluator{poly_context, mesh.totpoly}; + poly_evaluator.set_selection(selection_field); + poly_evaluator.add_with_destination(offset_field, poly_offset.as_mutable_span()); + poly_evaluator.evaluate(); + const IndexMask poly_selection = poly_evaluator.get_evaluated_selection_as_mask(); + + /* Build an array of offsets into the new data for each polygon. This is used to facilitate + * parallelism later on by avoiding the need to keep track of an offset when iterating through + * all polygons. */ + int extrude_corner_size = 0; + Array<int> index_offsets(poly_selection.size() + 1); + for (const int i_selection : poly_selection.index_range()) { + const MPoly &poly = orig_polys[poly_selection[i_selection]]; + index_offsets[i_selection] = extrude_corner_size; + extrude_corner_size += poly.totloop; + } + index_offsets.last() = extrude_corner_size; + + const IndexRange new_vert_range{orig_vert_size, extrude_corner_size}; + /* One edge connects each selected vertex to a new vertex on the extruded polygons. */ + const IndexRange connect_edge_range{orig_edge_size, extrude_corner_size}; + /* Each selected edge is duplicated to form a single edge on the extrusion. */ + const IndexRange duplicate_edge_range = connect_edge_range.after(extrude_corner_size); + /* Each edge selected for extrusion is extruded into a single face. */ + const IndexRange side_poly_range{orig_polys.size(), duplicate_edge_range.size()}; + const IndexRange side_loop_range{orig_loops.size(), side_poly_range.size() * 4}; + + expand_mesh(mesh, + new_vert_range.size(), + connect_edge_range.size() + duplicate_edge_range.size(), + side_poly_range.size(), + side_loop_range.size()); + + MutableSpan<MVert> new_verts = mesh_verts(mesh).slice(new_vert_range); + MutableSpan<MEdge> edges{mesh.medge, mesh.totedge}; + MutableSpan<MEdge> connect_edges = edges.slice(connect_edge_range); + MutableSpan<MEdge> duplicate_edges = edges.slice(duplicate_edge_range); + MutableSpan<MPoly> polys{mesh.mpoly, mesh.totpoly}; + MutableSpan<MPoly> new_polys = polys.slice(side_poly_range); + MutableSpan<MLoop> loops{mesh.mloop, mesh.totloop}; + + /* For every selected polygon, build the faces that form the sides of the extrusion. Filling some + * of this data like the new edges or polygons could be easily split into separate loops, which + * may or may not be faster, and would involve more duplication. */ + threading::parallel_for(poly_selection.index_range(), 256, [&](const IndexRange range) { + for (const int i_selection : range) { + const IndexRange poly_corner_range = selected_corner_range(index_offsets, i_selection); + + const MPoly &poly = polys[poly_selection[i_selection]]; + Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop); + + for (const int i : IndexRange(poly.totloop)) { + const int i_next = (i == poly.totloop - 1) ? 0 : i + 1; + const MLoop &orig_loop = poly_loops[i]; + const MLoop &orig_loop_next = poly_loops[i_next]; + + const int i_extrude = poly_corner_range[i]; + const int i_extrude_next = poly_corner_range[i_next]; + + const int i_duplicate_edge = duplicate_edge_range[i_extrude]; + const int new_vert = new_vert_range[i_extrude]; + const int new_vert_next = new_vert_range[i_extrude_next]; + + const int orig_edge = orig_loop.e; + + const int orig_vert = orig_loop.v; + const int orig_vert_next = orig_loop_next.v; + + duplicate_edges[i_extrude] = new_edge(new_vert, new_vert_next); + + new_polys[i_extrude] = new_poly(side_loop_range[i_extrude * 4], 4); + + MutableSpan<MLoop> side_loops = loops.slice(side_loop_range[i_extrude * 4], 4); + side_loops[0].v = new_vert_next; + side_loops[0].e = i_duplicate_edge; + side_loops[1].v = new_vert; + side_loops[1].e = connect_edge_range[i_extrude]; + side_loops[2].v = orig_vert; + side_loops[2].e = orig_edge; + side_loops[3].v = orig_vert_next; + side_loops[3].e = connect_edge_range[i_extrude_next]; + + connect_edges[i_extrude] = new_edge(orig_vert, new_vert); + } + } + }); + + component.attribute_foreach([&](const AttributeIDRef &id, const AttributeMetaData meta_data) { + OutputAttribute attribute = component.attribute_try_get_for_output( + id, meta_data.domain, meta_data.data_type); + if (!attribute) { + return true; /* Impossible to write the "normal" attribute. */ + } + + attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) { + using T = decltype(dummy); + MutableSpan<T> data = attribute.as_span().typed<T>(); + switch (attribute.domain()) { + case ATTR_DOMAIN_POINT: { + /* New vertices copy the attributes from their original vertices. */ + MutableSpan<T> new_data = data.slice(new_vert_range); + + threading::parallel_for(poly_selection.index_range(), 1024, [&](const IndexRange range) { + for (const int i_selection : range) { + const MPoly &poly = polys[poly_selection[i_selection]]; + Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop); + + const int corner_offset = index_offsets[i_selection]; + for (const int i : poly_loops.index_range()) { + const int orig_index = poly_loops[i].v; + new_data[corner_offset + i] = data[orig_index]; + } + } + }); + break; + } + case ATTR_DOMAIN_EDGE: { + MutableSpan<T> duplicate_data = data.slice(duplicate_edge_range); + MutableSpan<T> connect_data = data.slice(connect_edge_range); + + threading::parallel_for(poly_selection.index_range(), 512, [&](const IndexRange range) { + for (const int i_selection : range) { + const MPoly &poly = polys[poly_selection[i_selection]]; + Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop); + + const IndexRange poly_corner_range = selected_corner_range(index_offsets, + i_selection); + + /* The data for the duplicate edge is simply a copy of the original edge's data. */ + for (const int i : poly_loops.index_range()) { + const int orig_index = poly_loops[i].e; + duplicate_data[poly_corner_range[i]] = data[orig_index]; + } + + /* For the extruded edges, mix the data from the two neighboring original edges of + * the extruded polygon. */ + for (const int i : poly_loops.index_range()) { + const int i_loop_prev = (i == 0) ? poly.totloop - 1 : i - 1; + const int orig_index = poly_loops[i].e; + const int orig_index_prev = poly_loops[i_loop_prev].e; + if constexpr (std::is_same_v<T, bool>) { + /* Propagate selections with "or" instead of "at least half". */ + connect_data[poly_corner_range[i]] = data[orig_index] || data[orig_index_prev]; + } + else { + connect_data[poly_corner_range[i]] = attribute_math::mix2( + 0.5f, data[orig_index], data[orig_index_prev]); + } + } + } + }); + break; + } + case ATTR_DOMAIN_FACE: { + /* Each side face gets the values from the corresponding new face. */ + MutableSpan<T> new_data = data.slice(side_poly_range); + threading::parallel_for(poly_selection.index_range(), 1024, [&](const IndexRange range) { + for (const int i_selection : range) { + const int poly_index = poly_selection[i_selection]; + const IndexRange poly_corner_range = selected_corner_range(index_offsets, + i_selection); + new_data.slice(poly_corner_range).fill(data[poly_index]); + } + }); + break; + } + case ATTR_DOMAIN_CORNER: { + /* Each corner on a side face gets its value from the matching corner on an extruded + * face. */ + MutableSpan<T> new_data = data.slice(side_loop_range); + threading::parallel_for(poly_selection.index_range(), 256, [&](const IndexRange range) { + for (const int i_selection : range) { + const MPoly &poly = polys[poly_selection[i_selection]]; + Span<T> poly_loop_data = data.slice(poly.loopstart, poly.totloop); + const IndexRange poly_corner_range = selected_corner_range(index_offsets, + i_selection); + + for (const int i : IndexRange(poly.totloop)) { + const int i_next = (i == poly.totloop - 1) ? 0 : i + 1; + const int i_extrude = poly_corner_range[i]; + + MutableSpan<T> side_loop_data = new_data.slice(i_extrude * 4, 4); + + /* The two corners on each side of the side polygon get the data from the matching + * corners of the extruded polygon. This order depends on the loop filling the loop + * indices. */ + side_loop_data[0] = poly_loop_data[i_next]; + side_loop_data[1] = poly_loop_data[i]; + side_loop_data[2] = poly_loop_data[i]; + side_loop_data[3] = poly_loop_data[i_next]; + } + } + }); + break; + } + default: + BLI_assert_unreachable(); + } + }); + + attribute.save(); + return true; + }); + + /* Offset the new vertices. */ + threading::parallel_for(poly_selection.index_range(), 1024, [&](const IndexRange range) { + for (const int i_selection : range) { + const IndexRange poly_corner_range = selected_corner_range(index_offsets, i_selection); + for (MVert &vert : new_verts.slice(poly_corner_range)) { + add_v3_v3(vert.co, poly_offset[poly_selection[i_selection]]); + } + } + }); + + /* Finally update each extruded polygon's loops to point to the new edges and vertices. + * This must be done last, because they were used to find original indices for attribute + * interpolation before. Alternatively an original index array could be built for each domain. */ + threading::parallel_for(poly_selection.index_range(), 256, [&](const IndexRange range) { + for (const int i_selection : range) { + const IndexRange poly_corner_range = selected_corner_range(index_offsets, i_selection); + + const MPoly &poly = polys[poly_selection[i_selection]]; + MutableSpan<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop); + + for (const int i : IndexRange(poly.totloop)) { + MLoop &loop = poly_loops[i]; + loop.v = new_vert_range[poly_corner_range[i]]; + loop.e = duplicate_edge_range[poly_corner_range[i]]; + } + } + }); + + if (attribute_outputs.top_id) { + save_selection_as_attribute( + component, attribute_outputs.top_id.get(), ATTR_DOMAIN_FACE, poly_selection); + } + if (attribute_outputs.side_id) { + save_selection_as_attribute( + component, attribute_outputs.side_id.get(), ATTR_DOMAIN_FACE, side_poly_range); + } + + BKE_mesh_runtime_clear_cache(&mesh); + BKE_mesh_normals_tag_dirty(&mesh); +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh"); + Field<bool> selection = params.extract_input<Field<bool>>("Selection"); + Field<float3> offset_field = params.extract_input<Field<float3>>("Offset"); + Field<float> scale_field = params.extract_input<Field<float>>("Offset Scale"); + const NodeGeometryExtrudeMesh &storage = node_storage(params.node()); + GeometryNodeExtrudeMeshMode mode = static_cast<GeometryNodeExtrudeMeshMode>(storage.mode); + + /* Create a combined field from the offset and the scale so the field evaluator + * can take care of the multiplication and to simplify each extrude function. */ + static fn::CustomMF_SI_SI_SO<float3, float, float3> multiply_fn{ + "Scale", [](const float3 &offset, const float scale) { return offset * scale; }}; + std::shared_ptr<FieldOperation> multiply_op = std::make_shared<FieldOperation>( + FieldOperation(multiply_fn, {std::move(offset_field), std::move(scale_field)})); + const Field<float3> final_offset{std::move(multiply_op)}; + + AttributeOutputs attribute_outputs; + if (params.output_is_required("Top")) { + attribute_outputs.top_id = StrongAnonymousAttributeID("Top"); + } + if (params.output_is_required("Side")) { + attribute_outputs.side_id = StrongAnonymousAttributeID("Side"); + } + + const bool extrude_individual = mode == GEO_NODE_EXTRUDE_MESH_FACES && + params.extract_input<bool>("Individual"); + + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (geometry_set.has_mesh()) { + MeshComponent &component = geometry_set.get_component_for_write<MeshComponent>(); + switch (mode) { + case GEO_NODE_EXTRUDE_MESH_VERTICES: + extrude_mesh_vertices(component, selection, final_offset, attribute_outputs); + break; + case GEO_NODE_EXTRUDE_MESH_EDGES: + extrude_mesh_edges(component, selection, final_offset, attribute_outputs); + break; + case GEO_NODE_EXTRUDE_MESH_FACES: { + if (extrude_individual) { + extrude_individual_mesh_faces(component, selection, final_offset, attribute_outputs); + } + else { + extrude_mesh_face_regions(component, selection, final_offset, attribute_outputs); + } + break; + } + } + + BLI_assert(BKE_mesh_is_valid(component.get_for_write())); + } + }); + + params.set_output("Mesh", std::move(geometry_set)); + if (attribute_outputs.top_id) { + params.set_output("Top", + AnonymousAttributeFieldInput::Create<bool>( + std::move(attribute_outputs.top_id), params.attribute_producer_name())); + } + if (attribute_outputs.side_id) { + params.set_output("Side", + AnonymousAttributeFieldInput::Create<bool>( + std::move(attribute_outputs.side_id), params.attribute_producer_name())); + } +} + +} // namespace blender::nodes::node_geo_extrude_mesh_cc + +void register_node_type_geo_extrude_mesh() +{ + namespace file_ns = blender::nodes::node_geo_extrude_mesh_cc; + + static bNodeType ntype; + geo_node_type_base(&ntype, GEO_NODE_EXTRUDE_MESH, "Extrude Mesh", NODE_CLASS_GEOMETRY); + ntype.declare = file_ns::node_declare; + node_type_init(&ntype, file_ns::node_init); + node_type_update(&ntype, file_ns::node_update); + ntype.geometry_node_execute = file_ns::node_geo_exec; + node_type_storage( + &ntype, "NodeGeometryExtrudeMesh", node_free_standard_storage, node_copy_standard_storage); + ntype.draw_buttons = file_ns::node_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc b/source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc new file mode 100644 index 00000000000..9512323834c --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc @@ -0,0 +1,193 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "BKE_attribute_math.hh" + +#include "BLI_task.hh" + +namespace blender::nodes::node_geo_field_at_index_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Int>(N_("Index")).min(0).supports_field(); + + b.add_input<decl::Float>(N_("Value"), "Value_Float").supports_field(); + b.add_input<decl::Int>(N_("Value"), "Value_Int").supports_field(); + b.add_input<decl::Vector>(N_("Value"), "Value_Vector").supports_field(); + b.add_input<decl::Color>(N_("Value"), "Value_Color").supports_field(); + b.add_input<decl::Bool>(N_("Value"), "Value_Bool").supports_field(); + + b.add_output<decl::Float>(N_("Value"), "Value_Float").field_source(); + b.add_output<decl::Int>(N_("Value"), "Value_Int").field_source(); + b.add_output<decl::Vector>(N_("Value"), "Value_Vector").field_source(); + b.add_output<decl::Color>(N_("Value"), "Value_Color").field_source(); + b.add_output<decl::Bool>(N_("Value"), "Value_Bool").field_source(); +} + +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); + uiItemR(layout, ptr, "domain", 0, "", ICON_NONE); +} + +static void node_init(bNodeTree *UNUSED(tree), bNode *node) +{ + node->custom1 = ATTR_DOMAIN_POINT; + node->custom2 = CD_PROP_FLOAT; +} + +static void node_update(bNodeTree *ntree, bNode *node) +{ + const CustomDataType data_type = static_cast<CustomDataType>(node->custom2); + + bNodeSocket *sock_index = static_cast<bNodeSocket *>(node->inputs.first); + bNodeSocket *sock_in_float = sock_index->next; + bNodeSocket *sock_in_int = sock_in_float->next; + bNodeSocket *sock_in_vector = sock_in_int->next; + bNodeSocket *sock_in_color = sock_in_vector->next; + bNodeSocket *sock_in_bool = sock_in_color->next; + + bNodeSocket *sock_out_float = static_cast<bNodeSocket *>(node->outputs.first); + bNodeSocket *sock_out_int = sock_out_float->next; + bNodeSocket *sock_out_vector = sock_out_int->next; + bNodeSocket *sock_out_color = sock_out_vector->next; + bNodeSocket *sock_out_bool = sock_out_color->next; + + nodeSetSocketAvailability(ntree, sock_in_float, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(ntree, sock_in_int, data_type == CD_PROP_INT32); + nodeSetSocketAvailability(ntree, sock_in_vector, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(ntree, sock_in_color, data_type == CD_PROP_COLOR); + nodeSetSocketAvailability(ntree, sock_in_bool, data_type == CD_PROP_BOOL); + + nodeSetSocketAvailability(ntree, sock_out_float, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(ntree, sock_out_int, data_type == CD_PROP_INT32); + nodeSetSocketAvailability(ntree, sock_out_vector, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(ntree, sock_out_color, data_type == CD_PROP_COLOR); + nodeSetSocketAvailability(ntree, sock_out_bool, data_type == CD_PROP_BOOL); +} + +class FieldAtIndex final : public GeometryFieldInput { + private: + Field<int> index_field_; + GField value_field_; + AttributeDomain value_field_domain_; + + public: + FieldAtIndex(Field<int> index_field, GField value_field, AttributeDomain value_field_domain) + : GeometryFieldInput(value_field.cpp_type(), "Field at Index"), + index_field_(std::move(index_field)), + value_field_(std::move(value_field)), + value_field_domain_(value_field_domain) + { + } + + GVArray get_varray_for_context(const GeometryComponent &component, + const AttributeDomain domain, + IndexMask mask) const final + { + const GeometryComponentFieldContext value_field_context{component, value_field_domain_}; + FieldEvaluator value_evaluator{value_field_context, + component.attribute_domain_size(value_field_domain_)}; + value_evaluator.add(value_field_); + value_evaluator.evaluate(); + const GVArray &values = value_evaluator.get_evaluated(0); + + const GeometryComponentFieldContext index_field_context{component, domain}; + FieldEvaluator index_evaluator{index_field_context, &mask}; + index_evaluator.add(index_field_); + index_evaluator.evaluate(); + const VArray<int> &indices = index_evaluator.get_evaluated<int>(0); + + GVArray output_array; + attribute_math::convert_to_static_type(*type_, [&](auto dummy) { + using T = decltype(dummy); + Array<T> dst_array(mask.min_array_size()); + VArray<T> src_values = values.typed<T>(); + threading::parallel_for(mask.index_range(), 1024, [&](const IndexRange range) { + for (const int i : mask.slice(range)) { + const int index = indices[i]; + if (index >= 0 && index < src_values.size()) { + dst_array[i] = src_values[index]; + } + else { + dst_array[i] = {}; + } + } + }); + output_array = VArray<T>::ForContainer(std::move(dst_array)); + }); + + return output_array; + } +}; + +static StringRefNull identifier_suffix(CustomDataType data_type) +{ + switch (data_type) { + case CD_PROP_BOOL: + return "Bool"; + case CD_PROP_FLOAT: + return "Float"; + case CD_PROP_INT32: + return "Int"; + case CD_PROP_COLOR: + return "Color"; + case CD_PROP_FLOAT3: + return "Vector"; + default: + BLI_assert_unreachable(); + return ""; + } +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + const bNode &node = params.node(); + const AttributeDomain domain = static_cast<AttributeDomain>(node.custom1); + const CustomDataType data_type = static_cast<CustomDataType>(node.custom2); + + Field<int> index_field = params.extract_input<Field<int>>("Index"); + attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + using T = decltype(dummy); + static const std::string identifier = "Value_" + identifier_suffix(data_type); + Field<T> value_field = params.extract_input<Field<T>>(identifier); + Field<T> output_field{ + std::make_shared<FieldAtIndex>(std::move(index_field), std::move(value_field), domain)}; + params.set_output(identifier, std::move(output_field)); + }); +} + +} // namespace blender::nodes::node_geo_field_at_index_cc + +void register_node_type_geo_field_at_index() +{ + namespace file_ns = blender::nodes::node_geo_field_at_index_cc; + + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_FIELD_AT_INDEX, "Field at Index", NODE_CLASS_CONVERTER); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.declare = file_ns::node_declare; + ntype.draw_buttons = file_ns::node_layout; + ntype.initfunc = file_ns::node_init; + ntype.updatefunc = file_ns::node_update; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc new file mode 100644 index 00000000000..41970d75dfe --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc @@ -0,0 +1,114 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BKE_mesh.h" +#include "BKE_mesh_runtime.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_attribute_math.hh" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_flip_faces_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_output<decl::Geometry>(N_("Mesh")); +} + +static void mesh_flip_faces(MeshComponent &component, const Field<bool> &selection_field) +{ + GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_FACE}; + const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE); + if (domain_size == 0) { + return; + } + fn::FieldEvaluator evaluator{field_context, domain_size}; + evaluator.add(selection_field); + evaluator.evaluate(); + const IndexMask selection = evaluator.get_evaluated_as_mask(0); + + Mesh *mesh = component.get_for_write(); + + mesh->mloop = (MLoop *)CustomData_duplicate_referenced_layer( + &mesh->ldata, CD_MLOOP, mesh->totloop); + Span<MPoly> polys{mesh->mpoly, mesh->totpoly}; + MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop}; + + for (const int i : selection.index_range()) { + const MPoly &poly = polys[selection[i]]; + int start = poly.loopstart; + for (const int j : IndexRange(poly.totloop / 2)) { + const int index1 = start + j + 1; + const int index2 = start + poly.totloop - j - 1; + std::swap(loops[index1].v, loops[index2].v); + std::swap(loops[index1 - 1].e, loops[index2].e); + } + } + + component.attribute_foreach( + [&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + if (meta_data.domain == ATTR_DOMAIN_CORNER) { + OutputAttribute attribute = component.attribute_try_get_for_output( + attribute_id, ATTR_DOMAIN_CORNER, meta_data.data_type, nullptr); + attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) { + using T = decltype(dummy); + MutableSpan<T> dst_span = attribute.as_span<T>(); + for (const int j : selection.index_range()) { + const MPoly &poly = polys[selection[j]]; + dst_span.slice(poly.loopstart + 1, poly.totloop - 1).reverse(); + } + }); + attribute.save(); + } + return true; + }); +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh"); + + const Field<bool> selection_field = params.extract_input<Field<bool>>("Selection"); + + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (!geometry_set.has_mesh()) { + return; + } + MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>(); + mesh_flip_faces(mesh_component, selection_field); + }); + + params.set_output("Mesh", std::move(geometry_set)); +} + +} // namespace blender::nodes::node_geo_flip_faces_cc + +void register_node_type_geo_flip_faces() +{ + namespace file_ns = blender::nodes::node_geo_flip_faces_cc; + + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_FLIP_FACES, "Flip Faces", NODE_CLASS_GEOMETRY); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.declare = file_ns::node_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_geometry_to_instance.cc b/source/blender/nodes/geometry/nodes/node_geo_geometry_to_instance.cc new file mode 100644 index 00000000000..f65af5b6737 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_geometry_to_instance.cc @@ -0,0 +1,55 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_geometry_to_instance_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>(N_("Geometry")).multi_input(); + b.add_output<decl::Geometry>(N_("Instances")); +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + Vector<GeometrySet> geometries = params.extract_multi_input<GeometrySet>("Geometry"); + GeometrySet instances_geometry; + InstancesComponent &instances_component = + instances_geometry.get_component_for_write<InstancesComponent>(); + for (GeometrySet &geometry : geometries) { + geometry.ensure_owns_direct_data(); + const int handle = instances_component.add_reference(std::move(geometry)); + instances_component.add_instance(handle, float4x4::identity()); + } + params.set_output("Instances", std::move(instances_geometry)); +} + +} // namespace blender::nodes::node_geo_geometry_to_instance_cc + +void register_node_type_geo_geometry_to_instance() +{ + namespace file_ns = blender::nodes::node_geo_geometry_to_instance_cc; + + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_GEOMETRY_TO_INSTANCE, "Geometry to Instance", NODE_CLASS_GEOMETRY); + node_type_size(&ntype, 160, 100, 300); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.declare = file_ns::node_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_image_texture.cc b/source/blender/nodes/geometry/nodes/node_geo_image_texture.cc new file mode 100644 index 00000000000..624a8b6b0f6 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_image_texture.cc @@ -0,0 +1,428 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2005 Blender Foundation. + * All rights reserved. + */ + +#include "node_geometry_util.hh" + +#include "BKE_image.h" + +#include "BLI_math_vec_types.hh" +#include "BLI_threads.h" +#include "BLI_timeit.hh" + +#include "IMB_colormanagement.h" +#include "IMB_imbuf.h" +#include "IMB_imbuf_types.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes::node_geo_image_texture_cc { + +NODE_STORAGE_FUNCS(NodeGeometryImageTexture) + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Image>(N_("Image")).hide_label(); + b.add_input<decl::Vector>(N_("Vector")) + .implicit_field() + .description(("Texture coordinates from 0 to 1")); + b.add_input<decl::Int>(N_("Frame")).min(0).max(MAXFRAMEF); + b.add_output<decl::Color>(N_("Color")).no_muted_links().dependent_field(); + b.add_output<decl::Float>(N_("Alpha")).no_muted_links().dependent_field(); +} + +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "interpolation", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); + uiItemR(layout, ptr, "extension", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); +} + +static void node_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryImageTexture *tex = MEM_cnew<NodeGeometryImageTexture>(__func__); + node->storage = tex; +} + +class ImageFieldsFunction : public fn::MultiFunction { + private: + const int interpolation_; + const int extension_; + Image &image_; + ImageUser image_user_; + void *image_lock_; + ImBuf *image_buffer_; + + public: + ImageFieldsFunction(const int interpolation, + const int extension, + Image &image, + ImageUser image_user) + : interpolation_(interpolation), + extension_(extension), + image_(image), + image_user_(image_user) + { + static fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + + image_buffer_ = BKE_image_acquire_ibuf(&image_, &image_user_, &image_lock_); + if (image_buffer_ == nullptr) { + throw std::runtime_error("cannot acquire image buffer"); + } + + if (image_buffer_->rect_float == nullptr) { + BLI_thread_lock(LOCK_IMAGE); + if (!image_buffer_->rect_float) { + IMB_float_from_rect(image_buffer_); + } + BLI_thread_unlock(LOCK_IMAGE); + } + + if (image_buffer_->rect_float == nullptr) { + BKE_image_release_ibuf(&image_, image_buffer_, image_lock_); + throw std::runtime_error("cannot get float buffer"); + } + } + + ~ImageFieldsFunction() override + { + BKE_image_release_ibuf(&image_, image_buffer_, image_lock_); + } + + static fn::MFSignature create_signature() + { + fn::MFSignatureBuilder signature{"ImageFunction"}; + signature.single_input<float3>("Vector"); + signature.single_output<ColorGeometry4f>("Color"); + signature.single_output<float>("Alpha"); + return signature.build(); + } + + static int wrap_periodic(int x, const int width) + { + x %= width; + if (x < 0) { + x += width; + } + return x; + } + + static int wrap_clamp(const int x, const int width) + { + return std::clamp(x, 0, width - 1); + } + + static float4 image_pixel_lookup(const ImBuf *ibuf, const int px, const int py) + { + if (px < 0 || py < 0 || px >= ibuf->x || py >= ibuf->y) { + return float4(0.0f, 0.0f, 0.0f, 0.0f); + } + return ((const float4 *)ibuf->rect_float)[px + py * ibuf->x]; + } + + static float frac(const float x, int *ix) + { + const int i = (int)x - ((x < 0.0f) ? 1 : 0); + *ix = i; + return x - (float)i; + } + + static float4 image_cubic_texture_lookup(const ImBuf *ibuf, + const float px, + const float py, + const int extension) + { + const int width = ibuf->x; + const int height = ibuf->y; + int pix, piy, nix, niy; + const float tx = frac(px * (float)width - 0.5f, &pix); + const float ty = frac(py * (float)height - 0.5f, &piy); + int ppix, ppiy, nnix, nniy; + + switch (extension) { + case SHD_IMAGE_EXTENSION_REPEAT: { + pix = wrap_periodic(pix, width); + piy = wrap_periodic(piy, height); + ppix = wrap_periodic(pix - 1, width); + ppiy = wrap_periodic(piy - 1, height); + nix = wrap_periodic(pix + 1, width); + niy = wrap_periodic(piy + 1, height); + nnix = wrap_periodic(pix + 2, width); + nniy = wrap_periodic(piy + 2, height); + break; + } + case SHD_IMAGE_EXTENSION_CLIP: { + ppix = pix - 1; + ppiy = piy - 1; + nix = pix + 1; + niy = piy + 1; + nnix = pix + 2; + nniy = piy + 2; + break; + } + case SHD_IMAGE_EXTENSION_EXTEND: { + ppix = wrap_clamp(pix - 1, width); + ppiy = wrap_clamp(piy - 1, height); + nix = wrap_clamp(pix + 1, width); + niy = wrap_clamp(piy + 1, height); + nnix = wrap_clamp(pix + 2, width); + nniy = wrap_clamp(piy + 2, height); + pix = wrap_clamp(pix, width); + piy = wrap_clamp(piy, height); + break; + } + default: + return float4(0.0f, 0.0f, 0.0f, 0.0f); + } + + const int xc[4] = {ppix, pix, nix, nnix}; + const int yc[4] = {ppiy, piy, niy, nniy}; + float u[4], v[4]; + + u[0] = (((-1.0f / 6.0f) * tx + 0.5f) * tx - 0.5f) * tx + (1.0f / 6.0f); + u[1] = ((0.5f * tx - 1.0f) * tx) * tx + (2.0f / 3.0f); + u[2] = ((-0.5f * tx + 0.5f) * tx + 0.5f) * tx + (1.0f / 6.0f); + u[3] = (1.0f / 6.0f) * tx * tx * tx; + + v[0] = (((-1.0f / 6.0f) * ty + 0.5f) * ty - 0.5f) * ty + (1.0f / 6.0f); + v[1] = ((0.5f * ty - 1.0f) * ty) * ty + (2.0f / 3.0f); + v[2] = ((-0.5f * ty + 0.5f) * ty + 0.5f) * ty + (1.0f / 6.0f); + v[3] = (1.0f / 6.0f) * ty * ty * ty; + + return (v[0] * (u[0] * (image_pixel_lookup(ibuf, xc[0], yc[0])) + + u[1] * (image_pixel_lookup(ibuf, xc[1], yc[0])) + + u[2] * (image_pixel_lookup(ibuf, xc[2], yc[0])) + + u[3] * (image_pixel_lookup(ibuf, xc[3], yc[0])))) + + (v[1] * (u[0] * (image_pixel_lookup(ibuf, xc[0], yc[1])) + + u[1] * (image_pixel_lookup(ibuf, xc[1], yc[1])) + + u[2] * (image_pixel_lookup(ibuf, xc[2], yc[1])) + + u[3] * (image_pixel_lookup(ibuf, xc[3], yc[1])))) + + (v[2] * (u[0] * (image_pixel_lookup(ibuf, xc[0], yc[2])) + + u[1] * (image_pixel_lookup(ibuf, xc[1], yc[2])) + + u[2] * (image_pixel_lookup(ibuf, xc[2], yc[2])) + + u[3] * (image_pixel_lookup(ibuf, xc[3], yc[2])))) + + (v[3] * (u[0] * (image_pixel_lookup(ibuf, xc[0], yc[3])) + + u[1] * (image_pixel_lookup(ibuf, xc[1], yc[3])) + + u[2] * (image_pixel_lookup(ibuf, xc[2], yc[3])) + + u[3] * (image_pixel_lookup(ibuf, xc[3], yc[3])))); + } + + static float4 image_linear_texture_lookup(const ImBuf *ibuf, + const float px, + const float py, + const int extension) + { + const int width = ibuf->x; + const int height = ibuf->y; + int pix, piy, nix, niy; + const float nfx = frac(px * (float)width - 0.5f, &pix); + const float nfy = frac(py * (float)height - 0.5f, &piy); + + switch (extension) { + case SHD_IMAGE_EXTENSION_CLIP: { + nix = pix + 1; + niy = piy + 1; + break; + } + case SHD_IMAGE_EXTENSION_EXTEND: { + nix = wrap_clamp(pix + 1, width); + niy = wrap_clamp(piy + 1, height); + pix = wrap_clamp(pix, width); + piy = wrap_clamp(piy, height); + break; + } + default: + case SHD_IMAGE_EXTENSION_REPEAT: + pix = wrap_periodic(pix, width); + piy = wrap_periodic(piy, height); + nix = wrap_periodic(pix + 1, width); + niy = wrap_periodic(piy + 1, height); + break; + } + + const float ptx = 1.0f - nfx; + const float pty = 1.0f - nfy; + + return image_pixel_lookup(ibuf, pix, piy) * ptx * pty + + image_pixel_lookup(ibuf, nix, piy) * nfx * pty + + image_pixel_lookup(ibuf, pix, niy) * ptx * nfy + + image_pixel_lookup(ibuf, nix, niy) * nfx * nfy; + } + + static float4 image_closest_texture_lookup(const ImBuf *ibuf, + const float px, + const float py, + const int extension) + { + const int width = ibuf->x; + const int height = ibuf->y; + int ix, iy; + const float tx = frac(px * (float)width - 0.5f, &ix); + const float ty = frac(py * (float)height - 0.5f, &iy); + + switch (extension) { + case SHD_IMAGE_EXTENSION_REPEAT: { + ix = wrap_periodic(ix, width); + iy = wrap_periodic(iy, height); + return image_pixel_lookup(ibuf, ix, iy); + } + case SHD_IMAGE_EXTENSION_CLIP: { + if (tx < 0.0f || ty < 0.0f || tx > 1.0f || ty > 1.0f) { + return float4(0.0f, 0.0f, 0.0f, 0.0f); + } + if (ix < 0 || iy < 0 || ix > width || iy > height) { + return float4(0.0f, 0.0f, 0.0f, 0.0f); + } + ATTR_FALLTHROUGH; + } + case SHD_IMAGE_EXTENSION_EXTEND: { + ix = wrap_clamp(ix, width); + iy = wrap_clamp(iy, height); + return image_pixel_lookup(ibuf, ix, iy); + } + default: + return float4(0.0f, 0.0f, 0.0f, 0.0f); + } + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + const VArray<float3> &vectors = params.readonly_single_input<float3>(0, "Vector"); + MutableSpan<ColorGeometry4f> r_color = params.uninitialized_single_output<ColorGeometry4f>( + 1, "Color"); + MutableSpan<float> r_alpha = params.uninitialized_single_output_if_required<float>(2, "Alpha"); + + MutableSpan<float4> color_data{(float4 *)r_color.data(), r_color.size()}; + + /* Sample image texture. */ + switch (interpolation_) { + case SHD_INTERP_LINEAR: + for (const int64_t i : mask) { + const float3 p = vectors[i]; + color_data[i] = image_linear_texture_lookup(image_buffer_, p.x, p.y, extension_); + } + break; + case SHD_INTERP_CLOSEST: + for (const int64_t i : mask) { + const float3 p = vectors[i]; + color_data[i] = image_closest_texture_lookup(image_buffer_, p.x, p.y, extension_); + } + break; + case SHD_INTERP_CUBIC: + case SHD_INTERP_SMART: + for (const int64_t i : mask) { + const float3 p = vectors[i]; + color_data[i] = image_cubic_texture_lookup(image_buffer_, p.x, p.y, extension_); + } + break; + } + + int alpha_mode = image_.alpha_mode; + if (IMB_colormanagement_space_name_is_data(image_.colorspace_settings.name)) { + alpha_mode = IMA_ALPHA_CHANNEL_PACKED; + } + + switch (alpha_mode) { + case IMA_ALPHA_STRAIGHT: { + /* #ColorGeometry expects premultiplied alpha, so convert from straight to that. */ + for (int64_t i : mask) { + straight_to_premul_v4(color_data[i]); + } + break; + } + case IMA_ALPHA_PREMUL: { + /* Alpha is premultiplied already, nothing to do. */ + break; + } + case IMA_ALPHA_CHANNEL_PACKED: { + /* Color and alpha channels shouldn't interact with each other, nothing to do. */ + break; + } + case IMA_ALPHA_IGNORE: { + /* The image should be treated as being opaque. */ + for (int64_t i : mask) { + color_data[i].w = 1.0f; + } + break; + } + } + + if (!r_alpha.is_empty()) { + for (int64_t i : mask) { + r_alpha[i] = r_color[i].a; + } + } + } +}; + +static void node_geo_exec(GeoNodeExecParams params) +{ + Image *image = params.get_input<Image *>("Image"); + if (image == nullptr) { + params.set_default_remaining_outputs(); + return; + } + + const NodeGeometryImageTexture &storage = node_storage(params.node()); + + ImageUser image_user; + BKE_imageuser_default(&image_user); + image_user.cycl = false; + image_user.frames = INT_MAX; + image_user.sfra = 1; + image_user.framenr = BKE_image_is_animated(image) ? params.get_input<int>("Frame") : 0; + + std::unique_ptr<ImageFieldsFunction> image_fn; + try { + image_fn = std::make_unique<ImageFieldsFunction>( + storage.interpolation, storage.extension, *image, image_user); + } + catch (const std::runtime_error &) { + params.set_default_remaining_outputs(); + return; + } + + Field<float3> vector_field = params.extract_input<Field<float3>>("Vector"); + + auto image_op = std::make_shared<FieldOperation>( + FieldOperation(std::move(image_fn), {std::move(vector_field)})); + + params.set_output("Color", Field<ColorGeometry4f>(image_op, 0)); + params.set_output("Alpha", Field<float>(image_op, 1)); +} + +} // namespace blender::nodes::node_geo_image_texture_cc + +void register_node_type_geo_image_texture() +{ + namespace file_ns = blender::nodes::node_geo_image_texture_cc; + + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_IMAGE_TEXTURE, "Image Texture", NODE_CLASS_TEXTURE); + ntype.declare = file_ns::node_declare; + ntype.draw_buttons = file_ns::node_layout; + node_type_init(&ntype, file_ns::node_init); + node_type_storage( + &ntype, "NodeGeometryImageTexture", node_free_standard_storage, node_copy_standard_storage); + node_type_size_preset(&ntype, NODE_SIZE_LARGE); + ntype.geometry_node_execute = file_ns::node_geo_exec; + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc new file mode 100644 index 00000000000..b1144b58c37 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc @@ -0,0 +1,128 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BKE_spline.hh" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_input_curve_handles_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Bool>(N_("Relative")) + .default_value(false) + .supports_field() + .description(N_("Output the handle positions relative to the corresponding control point " + "instead of in the local space of the geometry")); + b.add_output<decl::Vector>(N_("Left")).field_source(); + b.add_output<decl::Vector>(N_("Right")).field_source(); +} + +class HandlePositionFieldInput final : public GeometryFieldInput { + Field<bool> relative_; + bool left_; + + public: + HandlePositionFieldInput(Field<bool> relative, bool left) + : GeometryFieldInput(CPPType::get<float3>(), "Handle"), relative_(relative), left_(left) + { + } + + GVArray get_varray_for_context(const GeometryComponent &component, + const AttributeDomain domain, + IndexMask mask) const final + { + if (component.type() != GEO_COMPONENT_TYPE_CURVE) { + return {}; + } + + GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; + fn::FieldEvaluator evaluator(field_context, &mask); + evaluator.add(relative_); + evaluator.evaluate(); + const VArray<bool> &relative = evaluator.get_evaluated<bool>(0); + + VArray<float3> positions = component.attribute_get_for_read<float3>( + "position", ATTR_DOMAIN_POINT, {0, 0, 0}); + + StringRef side = left_ ? "handle_left" : "handle_right"; + VArray<float3> handles = component.attribute_get_for_read<float3>( + side, ATTR_DOMAIN_POINT, {0, 0, 0}); + + if (relative.is_single()) { + if (relative.get_internal_single()) { + Array<float3> output(positions.size()); + for (const int i : positions.index_range()) { + output[i] = handles[i] - positions[i]; + } + return component.attribute_try_adapt_domain<float3>( + VArray<float3>::ForContainer(std::move(output)), ATTR_DOMAIN_POINT, domain); + } + return component.attribute_try_adapt_domain<float3>(handles, ATTR_DOMAIN_POINT, domain); + } + + Array<float3> output(positions.size()); + for (const int i : positions.index_range()) { + if (relative[i]) { + output[i] = handles[i] - positions[i]; + } + else { + output[i] = handles[i]; + } + } + return component.attribute_try_adapt_domain<float3>( + VArray<float3>::ForContainer(std::move(output)), ATTR_DOMAIN_POINT, domain); + } + + uint64_t hash() const override + { + return get_default_hash_2(relative_, left_); + } + + bool is_equal_to(const fn::FieldNode &other) const override + { + if (const HandlePositionFieldInput *other_handle = + dynamic_cast<const HandlePositionFieldInput *>(&other)) { + return relative_ == other_handle->relative_ && left_ == other_handle->left_; + } + return false; + } +}; + +static void node_geo_exec(GeoNodeExecParams params) +{ + Field<bool> relative = params.extract_input<Field<bool>>("Relative"); + Field<float3> left_field{std::make_shared<HandlePositionFieldInput>(relative, true)}; + Field<float3> right_field{std::make_shared<HandlePositionFieldInput>(relative, false)}; + + params.set_output("Left", std::move(left_field)); + params.set_output("Right", std::move(right_field)); +} + +} // namespace blender::nodes::node_geo_input_curve_handles_cc + +void register_node_type_geo_input_curve_handles() +{ + namespace file_ns = blender::nodes::node_geo_input_curve_handles_cc; + + static bNodeType ntype; + geo_node_type_base( + &ntype, GEO_NODE_INPUT_CURVE_HANDLES, "Curve Handle Positions", NODE_CLASS_INPUT); + node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.declare = file_ns::node_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_curve_tilt.cc b/source/blender/nodes/geometry/nodes/node_geo_input_curve_tilt.cc new file mode 100644 index 00000000000..61b4b6bb9e9 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_input_curve_tilt.cc @@ -0,0 +1,44 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_input_curve_tilt_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_output<decl::Float>(N_("Tilt")).field_source(); +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + Field<float> tilt_field = AttributeFieldInput::Create<float>("tilt"); + params.set_output("Tilt", std::move(tilt_field)); +} + +} // namespace blender::nodes::node_geo_input_curve_tilt_cc + +void register_node_type_geo_input_curve_tilt() +{ + namespace file_ns = blender::nodes::node_geo_input_curve_tilt_cc; + + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_INPUT_CURVE_TILT, "Curve Tilt", NODE_CLASS_INPUT); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.declare = file_ns::node_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_id.cc b/source/blender/nodes/geometry/nodes/node_geo_input_id.cc new file mode 100644 index 00000000000..3fe0588a46d --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_input_id.cc @@ -0,0 +1,44 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_input_id_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_output<decl::Int>(N_("ID")).field_source(); +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + Field<int> position_field{std::make_shared<bke::IDAttributeFieldInput>()}; + params.set_output("ID", std::move(position_field)); +} + +} // namespace blender::nodes::node_geo_input_id_cc + +void register_node_type_geo_input_id() +{ + namespace file_ns = blender::nodes::node_geo_input_id_cc; + + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_INPUT_ID, "ID", NODE_CLASS_INPUT); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.declare = file_ns::node_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_index.cc b/source/blender/nodes/geometry/nodes/node_geo_input_index.cc index 7fcbaf429dd..98c2c9d58f0 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_index.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_index.cc @@ -16,27 +16,29 @@ #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_input_index_cc { -static void geo_node_input_index_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_output<decl::Int>("Index").field_source(); + b.add_output<decl::Int>(N_("Index")).field_source(); } -static void geo_node_input_index_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { Field<int> index_field{std::make_shared<fn::IndexFieldInput>()}; params.set_output("Index", std::move(index_field)); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_input_index_cc void register_node_type_geo_input_index() { + namespace file_ns = blender::nodes::node_geo_input_index_cc; + static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_INPUT_INDEX, "Index", NODE_CLASS_INPUT, 0); - ntype.geometry_node_execute = blender::nodes::geo_node_input_index_exec; - ntype.declare = blender::nodes::geo_node_input_index_declare; + geo_node_type_base(&ntype, GEO_NODE_INPUT_INDEX, "Index", NODE_CLASS_INPUT); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.declare = file_ns::node_declare; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_material.cc b/source/blender/nodes/geometry/nodes/node_geo_input_material.cc index 8e805bd1359..a1c905fccaa 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_material.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_material.cc @@ -19,33 +19,35 @@ #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_input_material_cc { -static void geo_node_input_material_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_output<decl::Material>("Material"); + b.add_output<decl::Material>(N_("Material")); } -static void geo_node_input_material_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "material", 0, "", ICON_NONE); } -static void geo_node_input_material_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { Material *material = (Material *)params.node().id; params.set_output("Material", material); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_input_material_cc void register_node_type_geo_input_material() { + namespace file_ns = blender::nodes::node_geo_input_material_cc; + static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_INPUT_MATERIAL, "Material", NODE_CLASS_INPUT, 0); - ntype.draw_buttons = blender::nodes::geo_node_input_material_layout; - ntype.declare = blender::nodes::geo_node_input_material_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_input_material_exec; + geo_node_type_base(&ntype, GEO_NODE_INPUT_MATERIAL, "Material", NODE_CLASS_INPUT); + ntype.draw_buttons = file_ns::node_layout; + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_material_index.cc b/source/blender/nodes/geometry/nodes/node_geo_input_material_index.cc new file mode 100644 index 00000000000..fca29feb73c --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_input_material_index.cc @@ -0,0 +1,44 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_input_material_index_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_output<decl::Int>(N_("Material Index")).field_source(); +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + Field<int> material_index_field = AttributeFieldInput::Create<int>("material_index"); + params.set_output("Material Index", std::move(material_index_field)); +} + +} // namespace blender::nodes::node_geo_input_material_index_cc + +void register_node_type_geo_input_material_index() +{ + namespace file_ns = blender::nodes::node_geo_input_material_index_cc; + + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_INPUT_MATERIAL_INDEX, "Material Index", NODE_CLASS_INPUT); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.declare = file_ns::node_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_angle.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_angle.cc new file mode 100644 index 00000000000..4b6ed7b77b7 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_angle.cc @@ -0,0 +1,222 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_mesh.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_input_mesh_edge_angle_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_output<decl::Float>(N_("Unsigned Angle")) + .field_source() + .description( + "The shortest angle in radians between two faces where they meet at an edge. Flat edges " + "and Non-manifold edges have an angle of zero. Computing this value is faster than the " + "signed angle"); + b.add_output<decl::Float>(N_("Signed Angle")) + .field_source() + .description( + "The signed angle in radians between two faces where they meet at an edge. Flat edges " + "and Non-manifold edges have an angle of zero. Concave angles are positive and convex " + "angles are negative. Computing this value is slower than the unsigned angle"); +} + +struct EdgeMapEntry { + int face_count; + int face_index_1; + int face_index_2; +}; + +static Array<EdgeMapEntry> create_edge_map(const Span<MPoly> polys, + const Span<MLoop> loops, + const int total_edges) +{ + Array<EdgeMapEntry> edge_map(total_edges, {0, 0, 0}); + + for (const int i_poly : polys.index_range()) { + const MPoly &mpoly = polys[i_poly]; + for (const MLoop &loop : loops.slice(mpoly.loopstart, mpoly.totloop)) { + EdgeMapEntry &entry = edge_map[loop.e]; + if (entry.face_count == 0) { + entry.face_index_1 = i_poly; + } + else if (entry.face_count == 1) { + entry.face_index_2 = i_poly; + } + entry.face_count++; + } + } + return edge_map; +} + +class AngleFieldInput final : public GeometryFieldInput { + public: + AngleFieldInput() : GeometryFieldInput(CPPType::get<float>(), "Unsigned Angle Field") + { + category_ = Category::Generated; + } + + GVArray get_varray_for_context(const GeometryComponent &component, + const AttributeDomain domain, + IndexMask UNUSED(mask)) const final + { + if (component.type() != GEO_COMPONENT_TYPE_MESH) { + return {}; + } + + const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); + const Mesh *mesh = mesh_component.get_for_read(); + if (mesh == nullptr) { + return {}; + } + + Span<MPoly> polys{mesh->mpoly, mesh->totpoly}; + Span<MLoop> loops{mesh->mloop, mesh->totloop}; + Array<EdgeMapEntry> edge_map = create_edge_map(polys, loops, mesh->totedge); + + auto angle_fn = [edge_map, polys, loops, mesh](const int i) -> float { + if (edge_map[i].face_count != 2) { + return 0.0f; + } + const MPoly &mpoly_1 = polys[edge_map[i].face_index_1]; + const MPoly &mpoly_2 = polys[edge_map[i].face_index_2]; + float3 normal_1, normal_2; + BKE_mesh_calc_poly_normal(&mpoly_1, &loops[mpoly_1.loopstart], mesh->mvert, normal_1); + BKE_mesh_calc_poly_normal(&mpoly_2, &loops[mpoly_2.loopstart], mesh->mvert, normal_2); + return angle_normalized_v3v3(normal_1, normal_2); + }; + + VArray<float> angles = VArray<float>::ForFunc(mesh->totedge, angle_fn); + return component.attribute_try_adapt_domain<float>( + std::move(angles), ATTR_DOMAIN_EDGE, domain); + } + + uint64_t hash() const override + { + /* Some random constant hash. */ + return 32426725235; + } + + bool is_equal_to(const fn::FieldNode &other) const override + { + return dynamic_cast<const AngleFieldInput *>(&other) != nullptr; + } +}; + +class SignedAngleFieldInput final : public GeometryFieldInput { + public: + SignedAngleFieldInput() : GeometryFieldInput(CPPType::get<float>(), "Signed Angle Field") + { + category_ = Category::Generated; + } + + GVArray get_varray_for_context(const GeometryComponent &component, + const AttributeDomain domain, + IndexMask UNUSED(mask)) const final + { + if (component.type() != GEO_COMPONENT_TYPE_MESH) { + return {}; + } + + const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); + const Mesh *mesh = mesh_component.get_for_read(); + if (mesh == nullptr) { + return {}; + } + + Span<MPoly> polys{mesh->mpoly, mesh->totpoly}; + Span<MLoop> loops{mesh->mloop, mesh->totloop}; + Array<EdgeMapEntry> edge_map = create_edge_map(polys, loops, mesh->totedge); + + auto angle_fn = [edge_map, polys, loops, mesh](const int i) -> float { + if (edge_map[i].face_count != 2) { + return 0.0f; + } + const MPoly &mpoly_1 = polys[edge_map[i].face_index_1]; + const MPoly &mpoly_2 = polys[edge_map[i].face_index_2]; + + /* Find the normals of the 2 polys. */ + float3 poly_1_normal, poly_2_normal; + BKE_mesh_calc_poly_normal(&mpoly_1, &loops[mpoly_1.loopstart], mesh->mvert, poly_1_normal); + BKE_mesh_calc_poly_normal(&mpoly_2, &loops[mpoly_2.loopstart], mesh->mvert, poly_2_normal); + + /* Find the centerpoint of the axis edge */ + const float3 edge_centerpoint = (float3(mesh->mvert[mesh->medge[i].v1].co) + + float3(mesh->mvert[mesh->medge[i].v2].co)) * + 0.5f; + + /* Get the centerpoint of poly 2 and subtract the edge centerpoint to get a tangent + * normal for poly 2. */ + float3 poly_center_2; + BKE_mesh_calc_poly_center(&mpoly_2, &loops[mpoly_2.loopstart], mesh->mvert, poly_center_2); + const float3 poly_2_tangent = math::normalize(poly_center_2 - edge_centerpoint); + const float concavity = math::dot(poly_1_normal, poly_2_tangent); + + /* Get the unsigned angle between the two polys */ + const float angle = angle_normalized_v3v3(poly_1_normal, poly_2_normal); + + if (angle == 0.0f || angle == 2.0f * M_PI || concavity < 0) { + return angle; + } + return -angle; + }; + + VArray<float> angles = VArray<float>::ForFunc(mesh->totedge, angle_fn); + return component.attribute_try_adapt_domain<float>( + std::move(angles), ATTR_DOMAIN_EDGE, domain); + } + + uint64_t hash() const override + { + /* Some random constant hash. */ + return 68465416863; + } + + bool is_equal_to(const fn::FieldNode &other) const override + { + return dynamic_cast<const SignedAngleFieldInput *>(&other) != nullptr; + } +}; + +static void node_geo_exec(GeoNodeExecParams params) +{ + if (params.output_is_required("Unsigned Angle")) { + Field<float> angle_field{std::make_shared<AngleFieldInput>()}; + params.set_output("Unsigned Angle", std::move(angle_field)); + } + if (params.output_is_required("Signed Angle")) { + Field<float> angle_field{std::make_shared<SignedAngleFieldInput>()}; + params.set_output("Signed Angle", std::move(angle_field)); + } +} + +} // namespace blender::nodes::node_geo_input_mesh_edge_angle_cc + +void register_node_type_geo_input_mesh_edge_angle() +{ + namespace file_ns = blender::nodes::node_geo_input_mesh_edge_angle_cc; + + static bNodeType ntype; + geo_node_type_base(&ntype, GEO_NODE_INPUT_MESH_EDGE_ANGLE, "Edge Angle", NODE_CLASS_INPUT); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_neighbors.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_neighbors.cc new file mode 100644 index 00000000000..ddeb3ded511 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_neighbors.cc @@ -0,0 +1,93 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_mesh.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_input_mesh_edge_neighbors_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_output<decl::Int>(N_("Face Count")) + .field_source() + .description(N_("Number of faces that contain the edge")); +} + +class EdgeNeighborCountFieldInput final : public GeometryFieldInput { + public: + EdgeNeighborCountFieldInput() + : GeometryFieldInput(CPPType::get<int>(), "Edge Neighbor Count Field") + { + category_ = Category::Generated; + } + + GVArray get_varray_for_context(const GeometryComponent &component, + const AttributeDomain domain, + IndexMask UNUSED(mask)) const final + { + if (component.type() == GEO_COMPONENT_TYPE_MESH) { + const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); + const Mesh *mesh = mesh_component.get_for_read(); + if (mesh == nullptr) { + return {}; + } + + Array<int> face_count(mesh->totedge, 0); + for (const int i : IndexRange(mesh->totloop)) { + face_count[mesh->mloop[i].e]++; + } + + return mesh_component.attribute_try_adapt_domain<int>( + VArray<int>::ForContainer(std::move(face_count)), ATTR_DOMAIN_EDGE, domain); + } + return {}; + } + + uint64_t hash() const override + { + /* Some random constant hash. */ + return 985671075; + } + + bool is_equal_to(const fn::FieldNode &other) const override + { + return dynamic_cast<const EdgeNeighborCountFieldInput *>(&other) != nullptr; + } +}; + +static void node_geo_exec(GeoNodeExecParams params) +{ + Field<int> neighbor_count_field{std::make_shared<EdgeNeighborCountFieldInput>()}; + params.set_output("Face Count", std::move(neighbor_count_field)); +} + +} // namespace blender::nodes::node_geo_input_mesh_edge_neighbors_cc + +void register_node_type_geo_input_mesh_edge_neighbors() +{ + namespace file_ns = blender::nodes::node_geo_input_mesh_edge_neighbors_cc; + + static bNodeType ntype; + geo_node_type_base( + &ntype, GEO_NODE_INPUT_MESH_EDGE_NEIGHBORS, "Edge Neighbors", NODE_CLASS_INPUT); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_vertices.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_vertices.cc new file mode 100644 index 00000000000..f54c92fea7b --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_vertices.cc @@ -0,0 +1,186 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_mesh.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_input_mesh_edge_vertices_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_output<decl::Int>(N_("Vertex Index 1")) + .field_source() + .description(N_("The index of the first vertex in the edge")); + b.add_output<decl::Int>(N_("Vertex Index 2")) + .field_source() + .description(N_("The index of the second vertex in the edge")); + b.add_output<decl::Vector>(N_("Position 1")) + .field_source() + .description(N_("The position of the first vertex in the edge")); + b.add_output<decl::Vector>(N_("Position 2")) + .field_source() + .description(N_("The position of the second vertex in the edge")); +} + +enum VertexNumber { VERTEX_ONE, VERTEX_TWO }; + +static VArray<int> construct_edge_vertices_gvarray(const MeshComponent &component, + const VertexNumber vertex, + const AttributeDomain domain) +{ + const Mesh *mesh = component.get_for_read(); + if (mesh == nullptr) { + return {}; + } + if (domain == ATTR_DOMAIN_EDGE) { + if (vertex == VERTEX_ONE) { + return VArray<int>::ForFunc(mesh->totedge, + [mesh](const int i) -> int { return mesh->medge[i].v1; }); + } + return VArray<int>::ForFunc(mesh->totedge, + [mesh](const int i) -> int { return mesh->medge[i].v2; }); + } + return {}; +} + +class EdgeVerticesFieldInput final : public GeometryFieldInput { + private: + VertexNumber vertex_; + + public: + EdgeVerticesFieldInput(VertexNumber vertex) + : GeometryFieldInput(CPPType::get<int>(), "Edge Vertices Field"), vertex_(vertex) + { + category_ = Category::Generated; + } + + GVArray get_varray_for_context(const GeometryComponent &component, + const AttributeDomain domain, + IndexMask UNUSED(mask)) const final + { + if (component.type() == GEO_COMPONENT_TYPE_MESH) { + const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); + return construct_edge_vertices_gvarray(mesh_component, vertex_, domain); + } + return {}; + } + + uint64_t hash() const override + { + return vertex_ == VERTEX_ONE ? 23847562893465 : 92384598734567; + } + + bool is_equal_to(const fn::FieldNode &other) const override + { + if (const EdgeVerticesFieldInput *other_field = dynamic_cast<const EdgeVerticesFieldInput *>( + &other)) { + return vertex_ == other_field->vertex_; + } + return false; + } +}; + +static VArray<float3> construct_edge_positions_gvarray(const MeshComponent &component, + const VertexNumber vertex, + const AttributeDomain domain) +{ + const Mesh *mesh = component.get_for_read(); + if (mesh == nullptr) { + return {}; + } + + if (vertex == VERTEX_ONE) { + return component.attribute_try_adapt_domain<float3>( + VArray<float3>::ForFunc( + mesh->totedge, + [mesh](const int i) { return float3(mesh->mvert[mesh->medge[i].v1].co); }), + ATTR_DOMAIN_EDGE, + domain); + } + return component.attribute_try_adapt_domain<float3>( + VArray<float3>::ForFunc( + mesh->totedge, + [mesh](const int i) { return float3(mesh->mvert[mesh->medge[i].v2].co); }), + ATTR_DOMAIN_EDGE, + domain); +} + +class EdgePositionFieldInput final : public GeometryFieldInput { + private: + VertexNumber vertex_; + + public: + EdgePositionFieldInput(VertexNumber vertex) + : GeometryFieldInput(CPPType::get<float3>(), "Edge Position Field"), vertex_(vertex) + { + category_ = Category::Generated; + } + + GVArray get_varray_for_context(const GeometryComponent &component, + const AttributeDomain domain, + IndexMask UNUSED(mask)) const final + { + if (component.type() == GEO_COMPONENT_TYPE_MESH) { + const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); + return construct_edge_positions_gvarray(mesh_component, vertex_, domain); + } + return {}; + } + + uint64_t hash() const override + { + return vertex_ == VERTEX_ONE ? 987456978362 : 374587679866; + } + + bool is_equal_to(const fn::FieldNode &other) const override + { + if (const EdgePositionFieldInput *other_field = dynamic_cast<const EdgePositionFieldInput *>( + &other)) { + return vertex_ == other_field->vertex_; + } + return false; + } +}; + +static void node_geo_exec(GeoNodeExecParams params) +{ + Field<int> vertex_field_1{std::make_shared<EdgeVerticesFieldInput>(VERTEX_ONE)}; + Field<int> vertex_field_2{std::make_shared<EdgeVerticesFieldInput>(VERTEX_TWO)}; + Field<float3> position_field_1{std::make_shared<EdgePositionFieldInput>(VERTEX_ONE)}; + Field<float3> position_field_2{std::make_shared<EdgePositionFieldInput>(VERTEX_TWO)}; + + params.set_output("Vertex Index 1", std::move(vertex_field_1)); + params.set_output("Vertex Index 2", std::move(vertex_field_2)); + params.set_output("Position 1", std::move(position_field_1)); + params.set_output("Position 2", std::move(position_field_2)); +} + +} // namespace blender::nodes::node_geo_input_mesh_edge_vertices_cc + +void register_node_type_geo_input_mesh_edge_vertices() +{ + namespace file_ns = blender::nodes::node_geo_input_mesh_edge_vertices_cc; + + static bNodeType ntype; + geo_node_type_base(&ntype, GEO_NODE_INPUT_MESH_EDGE_VERTICES, "Edge Vertices", NODE_CLASS_INPUT); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_area.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_area.cc new file mode 100644 index 00000000000..ef8adff48f1 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_area.cc @@ -0,0 +1,96 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_mesh.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_input_mesh_face_area_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_output<decl::Float>(N_("Area")) + .field_source() + .description(N_("The surface area of each of the mesh's faces")); +} + +static VArray<float> construct_face_area_gvarray(const MeshComponent &component, + const AttributeDomain domain) +{ + const Mesh *mesh = component.get_for_read(); + if (mesh == nullptr) { + return {}; + } + + auto area_fn = [mesh](const int i) -> float { + const MPoly *mp = &mesh->mpoly[i]; + return BKE_mesh_calc_poly_area(mp, &mesh->mloop[mp->loopstart], mesh->mvert); + }; + + return component.attribute_try_adapt_domain<float>( + VArray<float>::ForFunc(mesh->totpoly, area_fn), ATTR_DOMAIN_FACE, domain); +} + +class FaceAreaFieldInput final : public GeometryFieldInput { + public: + FaceAreaFieldInput() : GeometryFieldInput(CPPType::get<float>(), "Face Area Field") + { + category_ = Category::Generated; + } + + GVArray get_varray_for_context(const GeometryComponent &component, + const AttributeDomain domain, + IndexMask UNUSED(mask)) const final + { + if (component.type() == GEO_COMPONENT_TYPE_MESH) { + const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); + return construct_face_area_gvarray(mesh_component, domain); + } + return {}; + } + + uint64_t hash() const override + { + /* Some random constant hash. */ + return 1346334523; + } + + bool is_equal_to(const fn::FieldNode &other) const override + { + return dynamic_cast<const FaceAreaFieldInput *>(&other) != nullptr; + } +}; + +static void node_geo_exec(GeoNodeExecParams params) +{ + params.set_output("Area", Field<float>(std::make_shared<FaceAreaFieldInput>())); +} + +} // namespace blender::nodes::node_geo_input_mesh_face_area_cc + +void register_node_type_geo_input_mesh_face_area() +{ + namespace file_ns = blender::nodes::node_geo_input_mesh_face_area_cc; + + static bNodeType ntype; + geo_node_type_base(&ntype, GEO_NODE_INPUT_MESH_FACE_AREA, "Face Area", NODE_CLASS_INPUT); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_neighbors.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_neighbors.cc new file mode 100644 index 00000000000..8d196e5f8dd --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_neighbors.cc @@ -0,0 +1,158 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_mesh.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_input_mesh_face_neighbors_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_output<decl::Int>(N_("Vertex Count")) + .field_source() + .description(N_("Number of edges or points in the face")); + b.add_output<decl::Int>(N_("Face Count")) + .field_source() + .description(N_("Number of faces which share an edge with the face")); +} + +static VArray<int> construct_neighbor_count_gvarray(const MeshComponent &component, + const AttributeDomain domain) +{ + const Mesh *mesh = component.get_for_read(); + if (mesh == nullptr) { + return {}; + } + + Array<int> edge_count(mesh->totedge, 0); + for (const int i : IndexRange(mesh->totloop)) { + edge_count[mesh->mloop[i].e]++; + } + + Array<int> poly_count(mesh->totpoly, 0); + for (const int poly_num : IndexRange(mesh->totpoly)) { + MPoly &poly = mesh->mpoly[poly_num]; + for (const int loop_num : IndexRange(poly.loopstart, poly.totloop)) { + poly_count[poly_num] += edge_count[mesh->mloop[loop_num].e] - 1; + } + } + + return component.attribute_try_adapt_domain<int>( + VArray<int>::ForContainer(std::move(poly_count)), ATTR_DOMAIN_FACE, domain); +} + +class FaceNeighborCountFieldInput final : public GeometryFieldInput { + public: + FaceNeighborCountFieldInput() + : GeometryFieldInput(CPPType::get<int>(), "Face Neighbor Count Field") + { + category_ = Category::Generated; + } + + GVArray get_varray_for_context(const GeometryComponent &component, + const AttributeDomain domain, + IndexMask UNUSED(mask)) const final + { + if (component.type() == GEO_COMPONENT_TYPE_MESH) { + const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); + return construct_neighbor_count_gvarray(mesh_component, domain); + } + return {}; + } + + uint64_t hash() const override + { + /* Some random constant hash. */ + return 823543774; + } + + bool is_equal_to(const fn::FieldNode &other) const override + { + return dynamic_cast<const FaceNeighborCountFieldInput *>(&other) != nullptr; + } +}; + +static VArray<int> construct_vertex_count_gvarray(const MeshComponent &component, + const AttributeDomain domain) +{ + const Mesh *mesh = component.get_for_read(); + if (mesh == nullptr) { + return {}; + } + + return component.attribute_try_adapt_domain<int>( + VArray<int>::ForFunc(mesh->totpoly, + [mesh](const int i) -> float { return mesh->mpoly[i].totloop; }), + ATTR_DOMAIN_FACE, + domain); +} + +class FaceVertexCountFieldInput final : public GeometryFieldInput { + public: + FaceVertexCountFieldInput() : GeometryFieldInput(CPPType::get<int>(), "Vertex Count Field") + { + category_ = Category::Generated; + } + + GVArray get_varray_for_context(const GeometryComponent &component, + const AttributeDomain domain, + IndexMask UNUSED(mask)) const final + { + if (component.type() == GEO_COMPONENT_TYPE_MESH) { + const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); + return construct_vertex_count_gvarray(mesh_component, domain); + } + return {}; + } + + uint64_t hash() const override + { + /* Some random constant hash. */ + return 236235463634; + } + + bool is_equal_to(const fn::FieldNode &other) const override + { + return dynamic_cast<const FaceVertexCountFieldInput *>(&other) != nullptr; + } +}; + +static void node_geo_exec(GeoNodeExecParams params) +{ + Field<int> vertex_count_field{std::make_shared<FaceVertexCountFieldInput>()}; + Field<int> neighbor_count_field{std::make_shared<FaceNeighborCountFieldInput>()}; + params.set_output("Vertex Count", std::move(vertex_count_field)); + params.set_output("Face Count", std::move(neighbor_count_field)); +} + +} // namespace blender::nodes::node_geo_input_mesh_face_neighbors_cc + +void register_node_type_geo_input_mesh_face_neighbors() +{ + namespace file_ns = blender::nodes::node_geo_input_mesh_face_neighbors_cc; + + static bNodeType ntype; + geo_node_type_base( + &ntype, GEO_NODE_INPUT_MESH_FACE_NEIGHBORS, "Face Neighbors", NODE_CLASS_INPUT); + node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc new file mode 100644 index 00000000000..68bb93bbb64 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc @@ -0,0 +1,157 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_mesh.h" + +#include "BLI_disjoint_set.hh" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_input_mesh_island_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_output<decl::Int>(N_("Island Index")) + .field_source() + .description(N_("Island indices are based on the order of the lowest-numbered vertex " + "contained in each island")); + b.add_output<decl::Int>(N_("Island Count")) + .field_source() + .description(N_("The total number of mesh islands")); +} + +class IslandFieldInput final : public GeometryFieldInput { + public: + IslandFieldInput() : GeometryFieldInput(CPPType::get<int>(), "Island Index") + { + category_ = Category::Generated; + } + + GVArray get_varray_for_context(const GeometryComponent &component, + const AttributeDomain domain, + IndexMask UNUSED(mask)) const final + { + if (component.type() != GEO_COMPONENT_TYPE_MESH) { + return {}; + } + const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); + const Mesh *mesh = mesh_component.get_for_read(); + if (mesh == nullptr) { + return {}; + } + + DisjointSet islands(mesh->totvert); + for (const int i : IndexRange(mesh->totedge)) { + islands.join(mesh->medge[i].v1, mesh->medge[i].v2); + } + + Array<int> output(mesh->totvert); + VectorSet<int> ordered_roots; + for (const int i : IndexRange(mesh->totvert)) { + const int64_t root = islands.find_root(i); + output[i] = ordered_roots.index_of_or_add(root); + } + + return mesh_component.attribute_try_adapt_domain<int>( + VArray<int>::ForContainer(std::move(output)), ATTR_DOMAIN_POINT, domain); + } + + uint64_t hash() const override + { + /* Some random constant hash. */ + return 635467354; + } + + bool is_equal_to(const fn::FieldNode &other) const override + { + return dynamic_cast<const IslandFieldInput *>(&other) != nullptr; + } +}; + +class IslandCountFieldInput final : public GeometryFieldInput { + public: + IslandCountFieldInput() : GeometryFieldInput(CPPType::get<int>(), "Island Count") + { + category_ = Category::Generated; + } + + GVArray get_varray_for_context(const GeometryComponent &component, + const AttributeDomain domain, + IndexMask UNUSED(mask)) const final + { + if (component.type() != GEO_COMPONENT_TYPE_MESH) { + return {}; + } + const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); + const Mesh *mesh = mesh_component.get_for_read(); + if (mesh == nullptr) { + return {}; + } + + DisjointSet islands(mesh->totvert); + for (const int i : IndexRange(mesh->totedge)) { + islands.join(mesh->medge[i].v1, mesh->medge[i].v2); + } + + Set<int> island_list; + for (const int i_vert : IndexRange(mesh->totvert)) { + const int64_t root = islands.find_root(i_vert); + island_list.add(root); + } + + return VArray<int>::ForSingle(island_list.size(), + mesh_component.attribute_domain_size(domain)); + } + + uint64_t hash() const override + { + /* Some random hash. */ + return 45634572457; + } + + bool is_equal_to(const fn::FieldNode &other) const override + { + return dynamic_cast<const IslandCountFieldInput *>(&other) != nullptr; + } +}; + +static void node_geo_exec(GeoNodeExecParams params) +{ + if (params.output_is_required("Island Index")) { + Field<int> field{std::make_shared<IslandFieldInput>()}; + params.set_output("Island Index", std::move(field)); + } + if (params.output_is_required("Island Count")) { + Field<int> field{std::make_shared<IslandCountFieldInput>()}; + params.set_output("Island Count", std::move(field)); + } +} + +} // namespace blender::nodes::node_geo_input_mesh_island_cc + +void register_node_type_geo_input_mesh_island() +{ + namespace file_ns = blender::nodes::node_geo_input_mesh_island_cc; + + static bNodeType ntype; + geo_node_type_base(&ntype, GEO_NODE_INPUT_MESH_ISLAND, "Mesh Island", NODE_CLASS_INPUT); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_vertex_neighbors.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_vertex_neighbors.cc new file mode 100644 index 00000000000..7d79164634d --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_vertex_neighbors.cc @@ -0,0 +1,155 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_mesh.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_input_mesh_vertex_neighbors_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_output<decl::Int>(N_("Vertex Count")) + .field_source() + .description(N_("Vertex count and edge count are equal")); + b.add_output<decl::Int>(N_("Face Count")) + .field_source() + .description(N_("Number of faces that contain the vertex")); +} + +static VArray<int> construct_vertex_count_gvarray(const MeshComponent &component, + const AttributeDomain domain) +{ + const Mesh *mesh = component.get_for_read(); + if (mesh == nullptr) { + return {}; + } + + if (domain == ATTR_DOMAIN_POINT) { + Array<int> vertices(mesh->totvert, 0); + for (const int i : IndexRange(mesh->totedge)) { + vertices[mesh->medge[i].v1]++; + vertices[mesh->medge[i].v2]++; + } + return VArray<int>::ForContainer(std::move(vertices)); + } + return {}; +} + +class VertexCountFieldInput final : public GeometryFieldInput { + public: + VertexCountFieldInput() : GeometryFieldInput(CPPType::get<int>(), "Vertex Count Field") + { + category_ = Category::Generated; + } + + GVArray get_varray_for_context(const GeometryComponent &component, + const AttributeDomain domain, + IndexMask UNUSED(mask)) const final + { + if (component.type() == GEO_COMPONENT_TYPE_MESH) { + const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); + return construct_vertex_count_gvarray(mesh_component, domain); + } + return {}; + } + + uint64_t hash() const override + { + /* Some random constant hash. */ + return 23574528465; + } + + bool is_equal_to(const fn::FieldNode &other) const override + { + return dynamic_cast<const VertexCountFieldInput *>(&other) != nullptr; + } +}; + +static VArray<int> construct_face_count_gvarray(const MeshComponent &component, + const AttributeDomain domain) +{ + const Mesh *mesh = component.get_for_read(); + if (mesh == nullptr) { + return {}; + } + + if (domain == ATTR_DOMAIN_POINT) { + Array<int> vertices(mesh->totvert, 0); + for (const int i : IndexRange(mesh->totloop)) { + int vertex = mesh->mloop[i].v; + vertices[vertex]++; + } + return VArray<int>::ForContainer(std::move(vertices)); + } + return {}; +} + +class VertexFaceCountFieldInput final : public GeometryFieldInput { + public: + VertexFaceCountFieldInput() : GeometryFieldInput(CPPType::get<int>(), "Vertex Face Count Field") + { + category_ = Category::Generated; + } + + GVArray get_varray_for_context(const GeometryComponent &component, + const AttributeDomain domain, + IndexMask UNUSED(mask)) const final + { + if (component.type() == GEO_COMPONENT_TYPE_MESH) { + const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); + return construct_face_count_gvarray(mesh_component, domain); + } + return {}; + } + + uint64_t hash() const override + { + /* Some random constant hash. */ + return 3462374322; + } + + bool is_equal_to(const fn::FieldNode &other) const override + { + return dynamic_cast<const VertexFaceCountFieldInput *>(&other) != nullptr; + } +}; + +static void node_geo_exec(GeoNodeExecParams params) +{ + Field<int> vertex_field{std::make_shared<VertexCountFieldInput>()}; + Field<int> face_field{std::make_shared<VertexFaceCountFieldInput>()}; + + params.set_output("Vertex Count", std::move(vertex_field)); + params.set_output("Face Count", std::move(face_field)); +} + +} // namespace blender::nodes::node_geo_input_mesh_vertex_neighbors_cc + +void register_node_type_geo_input_mesh_vertex_neighbors() +{ + namespace file_ns = blender::nodes::node_geo_input_mesh_vertex_neighbors_cc; + + static bNodeType ntype; + geo_node_type_base( + &ntype, GEO_NODE_INPUT_MESH_VERTEX_NEIGHBORS, "Vertex Neighbors", NODE_CLASS_INPUT); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc b/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc index 5a2495afb9e..120ae0e9bd1 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc @@ -24,280 +24,29 @@ #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_input_normal_cc { -static void geo_node_input_normal_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_output<decl::Vector>("Normal").field_source(); + b.add_output<decl::Vector>(N_("Normal")).field_source(); } -static GVArrayPtr mesh_face_normals(const Mesh &mesh, - const Span<MVert> verts, - const Span<MPoly> polys, - const Span<MLoop> loops, - const IndexMask mask) +static void node_geo_exec(GeoNodeExecParams params) { - /* Use existing normals to avoid unnecessarily recalculating them, if possible. */ - if (!(mesh.runtime.cd_dirty_poly & CD_MASK_NORMAL) && - CustomData_has_layer(&mesh.pdata, CD_NORMAL)) { - const void *data = CustomData_get_layer(&mesh.pdata, CD_NORMAL); - - return std::make_unique<fn::GVArray_For_Span<float3>>( - Span<float3>((const float3 *)data, polys.size())); - } - - auto normal_fn = [verts, polys, loops](const int i) -> float3 { - float3 normal; - const MPoly &poly = polys[i]; - BKE_mesh_calc_poly_normal(&poly, &loops[poly.loopstart], verts.data(), normal); - return normal; - }; - - return std::make_unique< - fn::GVArray_For_EmbeddedVArray<float3, VArray_For_Func<float3, decltype(normal_fn)>>>( - mask.min_array_size(), mask.min_array_size(), normal_fn); -} - -static GVArrayPtr mesh_vertex_normals(const Mesh &mesh, - const Span<MVert> verts, - const Span<MPoly> polys, - const Span<MLoop> loops, - const IndexMask mask) -{ - /* Use existing normals to avoid unnecessarily recalculating them, if possible. */ - if (!(mesh.runtime.cd_dirty_vert & CD_MASK_NORMAL) && - CustomData_has_layer(&mesh.vdata, CD_NORMAL)) { - const void *data = CustomData_get_layer(&mesh.pdata, CD_NORMAL); - - return std::make_unique<fn::GVArray_For_Span<float3>>( - Span<float3>((const float3 *)data, mesh.totvert)); - } - - /* If the normals are dirty, they must be recalculated for the output of this node's field - * source. Ideally vertex normals could be calculated lazily on a const mesh, but that's not - * possible at the moment, so we take ownership of the results. Sadly we must also create a copy - * of MVert to use the mesh normals API. This can be improved by adding mutex-protected lazy - * calculation of normals on meshes. - * - * Use mask.min_array_size() to avoid calculating a final chunk of data if possible. */ - Array<MVert> temp_verts(verts); - Array<float3> normals(verts.size()); /* Use full size for accumulation from faces. */ - BKE_mesh_calc_normals_poly_and_vertex(temp_verts.data(), - mask.min_array_size(), - loops.data(), - loops.size(), - polys.data(), - polys.size(), - nullptr, - (float(*)[3])normals.data()); - - return std::make_unique<fn::GVArray_For_ArrayContainer<Array<float3>>>(std::move(normals)); -} - -static const GVArray *construct_mesh_normals_gvarray(const MeshComponent &mesh_component, - const Mesh &mesh, - const IndexMask mask, - const AttributeDomain domain, - ResourceScope &scope) -{ - Span<MVert> verts{mesh.mvert, mesh.totvert}; - Span<MEdge> edges{mesh.medge, mesh.totedge}; - Span<MPoly> polys{mesh.mpoly, mesh.totpoly}; - Span<MLoop> loops{mesh.mloop, mesh.totloop}; - - switch (domain) { - case ATTR_DOMAIN_FACE: { - return scope.add_value(mesh_face_normals(mesh, verts, polys, loops, mask)).get(); - } - case ATTR_DOMAIN_POINT: { - return scope.add_value(mesh_vertex_normals(mesh, verts, polys, loops, mask)).get(); - } - case ATTR_DOMAIN_EDGE: { - /* In this case, start with vertex normals and convert to the edge domain, since the - * conversion from edges to vertices is very simple. Use the full mask since the edges - * might use the vertex normal from any index. */ - GVArrayPtr vert_normals = mesh_vertex_normals( - mesh, verts, polys, loops, IndexRange(verts.size())); - Span<float3> vert_normals_span = vert_normals->get_internal_span().typed<float3>(); - Array<float3> edge_normals(mask.min_array_size()); - - /* Use "manual" domain interpolation instead of the GeometryComponent API to avoid - * calculating unnecessary values and to allow normalizing the result much more simply. */ - for (const int i : mask) { - const MEdge &edge = edges[i]; - edge_normals[i] = float3::interpolate( - vert_normals_span[edge.v1], vert_normals_span[edge.v2], 0.5f) - .normalized(); - } - - return &scope.construct<fn::GVArray_For_ArrayContainer<Array<float3>>>( - std::move(edge_normals)); - } - case ATTR_DOMAIN_CORNER: { - /* The normals on corners are just the mesh's face normals, so start with the face normal - * array and copy the face normal for each of its corners. */ - GVArrayPtr face_normals = mesh_face_normals( - mesh, verts, polys, loops, IndexRange(polys.size())); - - /* In this case using the mesh component's generic domain interpolation is fine, the data - * will still be normalized, since the face normal is just copied to every corner. */ - GVArrayPtr loop_normals = mesh_component.attribute_try_adapt_domain( - std::move(face_normals), ATTR_DOMAIN_FACE, ATTR_DOMAIN_CORNER); - return scope.add_value(std::move(loop_normals)).get(); - } - default: - return nullptr; - } -} - -static void calculate_bezier_normals(const BezierSpline &spline, MutableSpan<float3> normals) -{ - Span<int> offsets = spline.control_point_offsets(); - Span<float3> evaluated_normals = spline.evaluated_normals(); - for (const int i : IndexRange(spline.size())) { - normals[i] = evaluated_normals[offsets[i]]; - } -} - -static void calculate_poly_normals(const PolySpline &spline, MutableSpan<float3> normals) -{ - normals.copy_from(spline.evaluated_normals()); -} - -/** - * Because NURBS control points are not necessarily on the path, the normal at the control points - * is not well defined, so create a temporary poly spline to find the normals. This requires extra - * copying currently, but may be more efficient in the future if attributes have some form of CoW. - */ -static void calculate_nurbs_normals(const NURBSpline &spline, MutableSpan<float3> normals) -{ - PolySpline poly_spline; - poly_spline.resize(spline.size()); - poly_spline.positions().copy_from(spline.positions()); - normals.copy_from(poly_spline.evaluated_normals()); -} - -static Array<float3> curve_normal_point_domain(const CurveEval &curve) -{ - Span<SplinePtr> splines = curve.splines(); - Array<int> offsets = curve.control_point_offsets(); - const int total_size = offsets.last(); - Array<float3> normals(total_size); - - threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) { - for (const int i : range) { - const Spline &spline = *splines[i]; - MutableSpan spline_normals{normals.as_mutable_span().slice(offsets[i], spline.size())}; - switch (splines[i]->type()) { - case Spline::Type::Bezier: - calculate_bezier_normals(static_cast<const BezierSpline &>(spline), spline_normals); - break; - case Spline::Type::Poly: - calculate_poly_normals(static_cast<const PolySpline &>(spline), spline_normals); - break; - case Spline::Type::NURBS: - calculate_nurbs_normals(static_cast<const NURBSpline &>(spline), spline_normals); - break; - } - } - }); - return normals; -} - -static const GVArray *construct_curve_normal_gvarray(const CurveComponent &component, - const AttributeDomain domain, - ResourceScope &scope) -{ - const CurveEval *curve = component.get_for_read(); - if (curve == nullptr) { - return nullptr; - } - - if (domain == ATTR_DOMAIN_POINT) { - const Span<SplinePtr> splines = curve->splines(); - - /* Use a reference to evaluated normals if possible to avoid an allocation and a copy. - * This is only possible when there is only one poly spline. */ - if (splines.size() == 1 && splines.first()->type() == Spline::Type::Poly) { - const PolySpline &spline = static_cast<PolySpline &>(*splines.first()); - return &scope.construct<fn::GVArray_For_Span<float3>>(spline.evaluated_normals()); - } - - Array<float3> normals = curve_normal_point_domain(*curve); - return &scope.construct<fn::GVArray_For_ArrayContainer<Array<float3>>>(std::move(normals)); - } - - if (domain == ATTR_DOMAIN_CURVE) { - Array<float3> point_normals = curve_normal_point_domain(*curve); - GVArrayPtr gvarray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<float3>>>( - std::move(point_normals)); - GVArrayPtr spline_normals = component.attribute_try_adapt_domain( - std::move(gvarray), ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE); - return scope.add_value(std::move(spline_normals)).get(); - } - - return nullptr; -} - -class NormalFieldInput final : public fn::FieldInput { - public: - NormalFieldInput() : fn::FieldInput(CPPType::get<float3>(), "Normal") - { - } - - const GVArray *get_varray_for_context(const fn::FieldContext &context, - IndexMask mask, - ResourceScope &scope) const final - { - if (const GeometryComponentFieldContext *geometry_context = - dynamic_cast<const GeometryComponentFieldContext *>(&context)) { - - const GeometryComponent &component = geometry_context->geometry_component(); - const AttributeDomain domain = geometry_context->domain(); - - if (component.type() == GEO_COMPONENT_TYPE_MESH) { - const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); - const Mesh *mesh = mesh_component.get_for_read(); - if (mesh == nullptr) { - return nullptr; - } - - return construct_mesh_normals_gvarray(mesh_component, *mesh, mask, domain, scope); - } - if (component.type() == GEO_COMPONENT_TYPE_CURVE) { - const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); - return construct_curve_normal_gvarray(curve_component, domain, scope); - } - } - return nullptr; - } - - uint64_t hash() const override - { - /* Some random constant hash. */ - return 669605641; - } - - bool is_equal_to(const fn::FieldNode &other) const override - { - return dynamic_cast<const NormalFieldInput *>(&other) != nullptr; - } -}; - -static void geo_node_input_normal_exec(GeoNodeExecParams params) -{ - Field<float3> normal_field{std::make_shared<NormalFieldInput>()}; + Field<float3> normal_field{std::make_shared<bke::NormalFieldInput>()}; params.set_output("Normal", std::move(normal_field)); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_input_normal_cc void register_node_type_geo_input_normal() { + namespace file_ns = blender::nodes::node_geo_input_normal_cc; + static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_INPUT_NORMAL, "Normal", NODE_CLASS_INPUT, 0); - ntype.geometry_node_execute = blender::nodes::geo_node_input_normal_exec; - ntype.declare = blender::nodes::geo_node_input_normal_declare; + geo_node_type_base(&ntype, GEO_NODE_INPUT_NORMAL, "Normal", NODE_CLASS_INPUT); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.declare = file_ns::node_declare; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_position.cc b/source/blender/nodes/geometry/nodes/node_geo_input_position.cc index 44874259e20..beb528d2fd8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_position.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_position.cc @@ -16,27 +16,29 @@ #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_input_position_cc { -static void geo_node_input_position_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_output<decl::Vector>("Position").field_source(); + b.add_output<decl::Vector>(N_("Position")).field_source(); } -static void geo_node_input_position_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { Field<float3> position_field{AttributeFieldInput::Create<float3>("position")}; params.set_output("Position", std::move(position_field)); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_input_position_cc void register_node_type_geo_input_position() { + namespace file_ns = blender::nodes::node_geo_input_position_cc; + static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_INPUT_POSITION, "Position", NODE_CLASS_INPUT, 0); - ntype.geometry_node_execute = blender::nodes::geo_node_input_position_exec; - ntype.declare = blender::nodes::geo_node_input_position_declare; + geo_node_type_base(&ntype, GEO_NODE_INPUT_POSITION, "Position", NODE_CLASS_INPUT); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.declare = file_ns::node_declare; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_radius.cc b/source/blender/nodes/geometry/nodes/node_geo_input_radius.cc new file mode 100644 index 00000000000..c7777da08c6 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_input_radius.cc @@ -0,0 +1,44 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_input_radius_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_output<decl::Float>(N_("Radius")).default_value(1.0f).min(0.0f).field_source(); +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + Field<float> radius_field = AttributeFieldInput::Create<float>("radius"); + params.set_output("Radius", std::move(radius_field)); +} + +} // namespace blender::nodes::node_geo_input_radius_cc + +void register_node_type_geo_input_radius() +{ + namespace file_ns = blender::nodes::node_geo_input_radius_cc; + + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_INPUT_RADIUS, "Radius", NODE_CLASS_INPUT); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.declare = file_ns::node_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_scene_time.cc b/source/blender/nodes/geometry/nodes/node_geo_input_scene_time.cc new file mode 100644 index 00000000000..4ed65e99a1c --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_input_scene_time.cc @@ -0,0 +1,50 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BKE_scene.h" + +#include "DEG_depsgraph_query.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_input_scene_time_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_output<decl::Float>(N_("Seconds")); + b.add_output<decl::Float>(N_("Frame")); +} + +static void node_exec(GeoNodeExecParams params) +{ + const Scene *scene = DEG_get_input_scene(params.depsgraph()); + const float scene_ctime = BKE_scene_ctime_get(scene); + const double frame_rate = (((double)scene->r.frs_sec) / (double)scene->r.frs_sec_base); + params.set_output("Seconds", float(scene_ctime / frame_rate)); + params.set_output("Frame", scene_ctime); +} + +} // namespace blender::nodes::node_geo_input_scene_time_cc + +void register_node_type_geo_input_scene_time() +{ + static bNodeType ntype; + namespace file_ns = blender::nodes::node_geo_input_scene_time_cc; + geo_node_type_base(&ntype, GEO_NODE_INPUT_SCENE_TIME, "Scene Time", NODE_CLASS_INPUT); + ntype.geometry_node_execute = file_ns::node_exec; + ntype.declare = file_ns::node_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_shade_smooth.cc b/source/blender/nodes/geometry/nodes/node_geo_input_shade_smooth.cc new file mode 100644 index 00000000000..b27ab097223 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_input_shade_smooth.cc @@ -0,0 +1,44 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_input_shade_smooth_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_output<decl::Bool>(N_("Smooth")).field_source(); +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + Field<bool> shade_smooth_field = AttributeFieldInput::Create<bool>("shade_smooth"); + params.set_output("Smooth", std::move(shade_smooth_field)); +} + +} // namespace blender::nodes::node_geo_input_shade_smooth_cc + +void register_node_type_geo_input_shade_smooth() +{ + namespace file_ns = blender::nodes::node_geo_input_shade_smooth_cc; + + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_INPUT_SHADE_SMOOTH, "Is Shade Smooth", NODE_CLASS_INPUT); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.declare = file_ns::node_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_spline_cyclic.cc b/source/blender/nodes/geometry/nodes/node_geo_input_spline_cyclic.cc new file mode 100644 index 00000000000..2db00a1ae68 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_input_spline_cyclic.cc @@ -0,0 +1,44 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_input_spline_cyclic_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_output<decl::Bool>(N_("Cyclic")).field_source(); +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + Field<bool> cyclic_field = AttributeFieldInput::Create<bool>("cyclic"); + params.set_output("Cyclic", std::move(cyclic_field)); +} + +} // namespace blender::nodes::node_geo_input_spline_cyclic_cc + +void register_node_type_geo_input_spline_cyclic() +{ + namespace file_ns = blender::nodes::node_geo_input_spline_cyclic_cc; + + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_INPUT_SPLINE_CYCLIC, "Is Spline Cyclic", NODE_CLASS_INPUT); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.declare = file_ns::node_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc b/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc new file mode 100644 index 00000000000..b8c8ce840eb --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc @@ -0,0 +1,163 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +#include "BKE_spline.hh" + +namespace blender::nodes::node_geo_input_spline_length_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_output<decl::Float>(N_("Length")).field_source(); + b.add_output<decl::Int>(N_("Point Count")).field_source(); +} + +/* -------------------------------------------------------------------- + * Spline Length + */ + +static VArray<float> construct_spline_length_gvarray(const CurveComponent &component, + const AttributeDomain domain) +{ + const CurveEval *curve = component.get_for_read(); + if (curve == nullptr) { + return {}; + } + + Span<SplinePtr> splines = curve->splines(); + auto length_fn = [splines](int i) { return splines[i]->length(); }; + + if (domain == ATTR_DOMAIN_CURVE) { + return VArray<float>::ForFunc(splines.size(), length_fn); + } + if (domain == ATTR_DOMAIN_POINT) { + VArray<float> length = VArray<float>::ForFunc(splines.size(), length_fn); + return component.attribute_try_adapt_domain<float>( + std::move(length), ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT); + } + + return {}; +} + +class SplineLengthFieldInput final : public GeometryFieldInput { + public: + SplineLengthFieldInput() : GeometryFieldInput(CPPType::get<float>(), "Spline Length node") + { + category_ = Category::Generated; + } + + GVArray get_varray_for_context(const GeometryComponent &component, + const AttributeDomain domain, + IndexMask UNUSED(mask)) const final + { + if (component.type() == GEO_COMPONENT_TYPE_CURVE) { + const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); + return construct_spline_length_gvarray(curve_component, domain); + } + return {}; + } + + uint64_t hash() const override + { + /* Some random constant hash. */ + return 3549623580; + } + + bool is_equal_to(const fn::FieldNode &other) const override + { + return dynamic_cast<const SplineLengthFieldInput *>(&other) != nullptr; + } +}; + +/* -------------------------------------------------------------------- + * Spline Count + */ + +static VArray<int> construct_spline_count_gvarray(const CurveComponent &component, + const AttributeDomain domain) +{ + const CurveEval *curve = component.get_for_read(); + if (curve == nullptr) { + return {}; + } + + Span<SplinePtr> splines = curve->splines(); + auto count_fn = [splines](int i) { return splines[i]->size(); }; + + if (domain == ATTR_DOMAIN_CURVE) { + return VArray<int>::ForFunc(splines.size(), count_fn); + } + if (domain == ATTR_DOMAIN_POINT) { + VArray<int> count = VArray<int>::ForFunc(splines.size(), count_fn); + return component.attribute_try_adapt_domain<int>( + std::move(count), ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT); + } + + return {}; +} + +class SplineCountFieldInput final : public GeometryFieldInput { + public: + SplineCountFieldInput() : GeometryFieldInput(CPPType::get<int>(), "Spline Point Count") + { + category_ = Category::Generated; + } + + GVArray get_varray_for_context(const GeometryComponent &component, + const AttributeDomain domain, + IndexMask UNUSED(mask)) const final + { + if (component.type() == GEO_COMPONENT_TYPE_CURVE) { + const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); + return construct_spline_count_gvarray(curve_component, domain); + } + return {}; + } + + uint64_t hash() const override + { + /* Some random constant hash. */ + return 456364322625; + } + + bool is_equal_to(const fn::FieldNode &other) const override + { + return dynamic_cast<const SplineCountFieldInput *>(&other) != nullptr; + } +}; + +static void node_geo_exec(GeoNodeExecParams params) +{ + Field<float> spline_length_field{std::make_shared<SplineLengthFieldInput>()}; + Field<int> spline_count_field{std::make_shared<SplineCountFieldInput>()}; + + params.set_output("Length", std::move(spline_length_field)); + params.set_output("Point Count", std::move(spline_count_field)); +} + +} // namespace blender::nodes::node_geo_input_spline_length_cc + +void register_node_type_geo_input_spline_length() +{ + namespace file_ns = blender::nodes::node_geo_input_spline_length_cc; + + static bNodeType ntype; + geo_node_type_base(&ntype, GEO_NODE_INPUT_SPLINE_LENGTH, "Spline Length", NODE_CLASS_INPUT); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.declare = file_ns::node_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_spline_resolution.cc b/source/blender/nodes/geometry/nodes/node_geo_input_spline_resolution.cc new file mode 100644 index 00000000000..d79f2ffd64d --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_input_spline_resolution.cc @@ -0,0 +1,45 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_input_spline_resolution_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_output<decl::Int>(N_("Resolution")).field_source(); +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + Field<int> resolution_field = AttributeFieldInput::Create<int>("resolution"); + params.set_output("Resolution", std::move(resolution_field)); +} + +} // namespace blender::nodes::node_geo_input_spline_resolution_cc + +void register_node_type_geo_input_spline_resolution() +{ + namespace file_ns = blender::nodes::node_geo_input_spline_resolution_cc; + + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_INPUT_SPLINE_RESOLUTION, "Spline Resolution", NODE_CLASS_INPUT); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.declare = file_ns::node_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc b/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc index d690642373a..f80fdfbf334 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc @@ -20,11 +20,11 @@ #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_input_tangent_cc { -static void geo_node_input_tangent_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_output<decl::Vector>("Tangent").field_source(); + b.add_output<decl::Vector>(N_("Tangent")).field_source(); } static void calculate_bezier_tangents(const BezierSpline &spline, MutableSpan<float3> tangents) @@ -84,9 +84,8 @@ static Array<float3> curve_tangent_point_domain(const CurveEval &curve) return tangents; } -static const GVArray *construct_curve_tangent_gvarray(const CurveComponent &component, - const AttributeDomain domain, - ResourceScope &scope) +static VArray<float3> construct_curve_tangent_gvarray(const CurveComponent &component, + const AttributeDomain domain) { const CurveEval *curve = component.get_for_read(); if (curve == nullptr) { @@ -100,47 +99,40 @@ static const GVArray *construct_curve_tangent_gvarray(const CurveComponent &comp * This is only possible when there is only one poly spline. */ if (splines.size() == 1 && splines.first()->type() == Spline::Type::Poly) { const PolySpline &spline = static_cast<PolySpline &>(*splines.first()); - return &scope.construct<fn::GVArray_For_Span<float3>>(spline.evaluated_tangents()); + return VArray<float3>::ForSpan(spline.evaluated_tangents()); } Array<float3> tangents = curve_tangent_point_domain(*curve); - return &scope.construct<fn::GVArray_For_ArrayContainer<Array<float3>>>(std::move(tangents)); + return VArray<float3>::ForContainer(std::move(tangents)); } if (domain == ATTR_DOMAIN_CURVE) { Array<float3> point_tangents = curve_tangent_point_domain(*curve); - GVArrayPtr gvarray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<float3>>>( - std::move(point_tangents)); - GVArrayPtr spline_tangents = component.attribute_try_adapt_domain( - std::move(gvarray), ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE); - return scope.add_value(std::move(spline_tangents)).get(); + return component.attribute_try_adapt_domain<float3>( + VArray<float3>::ForContainer(std::move(point_tangents)), + ATTR_DOMAIN_POINT, + ATTR_DOMAIN_CURVE); } return nullptr; } -class TangentFieldInput final : public fn::FieldInput { +class TangentFieldInput final : public GeometryFieldInput { public: - TangentFieldInput() : fn::FieldInput(CPPType::get<float3>(), "Tangent") + TangentFieldInput() : GeometryFieldInput(CPPType::get<float3>(), "Tangent node") { + category_ = Category::Generated; } - const GVArray *get_varray_for_context(const fn::FieldContext &context, - IndexMask UNUSED(mask), - ResourceScope &scope) const final + GVArray get_varray_for_context(const GeometryComponent &component, + const AttributeDomain domain, + IndexMask UNUSED(mask)) const final { - if (const GeometryComponentFieldContext *geometry_context = - dynamic_cast<const GeometryComponentFieldContext *>(&context)) { - - const GeometryComponent &component = geometry_context->geometry_component(); - const AttributeDomain domain = geometry_context->domain(); - - if (component.type() == GEO_COMPONENT_TYPE_CURVE) { - const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); - return construct_curve_tangent_gvarray(curve_component, domain, scope); - } + if (component.type() == GEO_COMPONENT_TYPE_CURVE) { + const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); + return construct_curve_tangent_gvarray(curve_component, domain); } - return nullptr; + return {}; } uint64_t hash() const override @@ -155,20 +147,22 @@ class TangentFieldInput final : public fn::FieldInput { } }; -static void geo_node_input_tangent_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { Field<float3> tangent_field{std::make_shared<TangentFieldInput>()}; params.set_output("Tangent", std::move(tangent_field)); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_input_tangent_cc void register_node_type_geo_input_tangent() { + namespace file_ns = blender::nodes::node_geo_input_tangent_cc; + static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_INPUT_TANGENT, "Curve Tangent", NODE_CLASS_INPUT, 0); - ntype.geometry_node_execute = blender::nodes::geo_node_input_tangent_exec; - ntype.declare = blender::nodes::geo_node_input_tangent_declare; + geo_node_type_base(&ntype, GEO_NODE_INPUT_TANGENT, "Curve Tangent", NODE_CLASS_INPUT); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.declare = file_ns::node_declare; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc index 8c0c0763be8..71256a7f781 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc @@ -22,72 +22,80 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "BKE_attribute_math.hh" + #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_instance_on_points_cc { -static void geo_node_instance_on_points_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Points").description("Points to instance on"); - b.add_input<decl::Geometry>("Instance").description("Geometry that is instanced on the points"); - b.add_input<decl::Bool>("Pick Instance") + b.add_input<decl::Geometry>(N_("Points")).description(N_("Points to instance on")); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).supports_field().hide_value(); + b.add_input<decl::Geometry>(N_("Instance")) + .description(N_("Geometry that is instanced on the points")); + b.add_input<decl::Bool>(N_("Pick Instance")) .supports_field() - .description("Place different instances on different points"); - b.add_input<decl::Int>("Instance Index") + .description(N_("Place different instances on different points")); + b.add_input<decl::Int>(N_("Instance Index")) .implicit_field() - .description( + .description(N_( "Index of the instance that used for each point. This is only used when Pick Instances " - "is on. By default the point index is used"); - b.add_input<decl::Vector>("Rotation") + "is on. By default the point index is used")); + b.add_input<decl::Vector>(N_("Rotation")) .subtype(PROP_EULER) .supports_field() - .description("Rotation of the instances"); - b.add_input<decl::Vector>("Scale") + .description(N_("Rotation of the instances")); + b.add_input<decl::Vector>(N_("Scale")) .default_value({1.0f, 1.0f, 1.0f}) + .subtype(PROP_XYZ) .supports_field() - .description("Scale of the instances"); - b.add_input<decl::Int>("Stable ID") - .supports_field() - .description( - "ID for every instance that is used to identify it over time even when the number of " - "instances changes. Used for example for motion blur"); + .description(N_("Scale of the instances")); - b.add_output<decl::Geometry>("Instances"); + b.add_output<decl::Geometry>(N_("Instances")); } -static void add_instances_from_component(InstancesComponent &dst_component, - const GeometryComponent &src_component, - const GeometrySet &instance, - const GeoNodeExecParams ¶ms) +static void add_instances_from_component( + InstancesComponent &dst_component, + const GeometryComponent &src_component, + const GeometrySet &instance, + const GeoNodeExecParams ¶ms, + const Map<AttributeIDRef, AttributeKind> &attributes_to_propagate) { const AttributeDomain domain = ATTR_DOMAIN_POINT; const int domain_size = src_component.attribute_domain_size(domain); + VArray<bool> pick_instance; + VArray<int> indices; + VArray<float3> rotations; + VArray<float3> scales; + + GeometryComponentFieldContext field_context{src_component, domain}; + const Field<bool> selection_field = params.get_input<Field<bool>>("Selection"); + fn::FieldEvaluator evaluator{field_context, domain_size}; + evaluator.set_selection(selection_field); + /* The evaluator could use the component's stable IDs as a destination directly, but only the + * selected indices should be copied. */ + evaluator.add(params.get_input<Field<bool>>("Pick Instance"), &pick_instance); + evaluator.add(params.get_input<Field<int>>("Instance Index"), &indices); + evaluator.add(params.get_input<Field<float3>>("Rotation"), &rotations); + evaluator.add(params.get_input<Field<float3>>("Scale"), &scales); + evaluator.evaluate(); + + const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); + /* The initial size of the component might be non-zero when this function is called for multiple * component types. */ const int start_len = dst_component.instances_amount(); - dst_component.resize(start_len + domain_size); + const int select_len = selection.index_range().size(); + dst_component.resize(start_len + select_len); + MutableSpan<int> dst_handles = dst_component.instance_reference_handles().slice(start_len, - domain_size); + select_len); MutableSpan<float4x4> dst_transforms = dst_component.instance_transforms().slice(start_len, - domain_size); - MutableSpan<int> dst_stable_ids = dst_component.instance_ids().slice(start_len, domain_size); + select_len); - GeometryComponentFieldContext field_context{src_component, domain}; - FieldEvaluator field_evaluator{field_context, domain_size}; - - const VArray<bool> *pick_instance = nullptr; - const VArray<int> *indices = nullptr; - const VArray<float3> *rotations = nullptr; - const VArray<float3> *scales = nullptr; - field_evaluator.add(params.get_input<Field<bool>>("Pick Instance"), &pick_instance); - field_evaluator.add(params.get_input<Field<int>>("Instance Index"), &indices); - field_evaluator.add(params.get_input<Field<float3>>("Rotation"), &rotations); - field_evaluator.add(params.get_input<Field<float3>>("Scale"), &scales); - field_evaluator.add_with_destination(params.get_input<Field<int>>("Stable ID"), dst_stable_ids); - field_evaluator.evaluate(); - - GVArray_Typed<float3> positions = src_component.attribute_get_for_read<float3>( + VArray<float3> positions = src_component.attribute_get_for_read<float3>( "position", domain, {0, 0, 0}); const InstancesComponent *src_instances = instance.get_component_for_read<InstancesComponent>(); @@ -96,7 +104,7 @@ static void add_instances_from_component(InstancesComponent &dst_component, Array<int> handle_mapping; /* Only fill #handle_mapping when it may be used below. */ if (src_instances != nullptr && - (!pick_instance->is_single() || pick_instance->get_internal_single())) { + (!pick_instance.is_single() || pick_instance.get_internal_single())) { Span<InstanceReference> src_references = src_instances->references(); handle_mapping.reinitialize(src_references.size()); for (const int src_instance_handle : src_references.index_range()) { @@ -110,23 +118,24 @@ static void add_instances_from_component(InstancesComponent &dst_component, /* Add this reference last, because it is the most likely one to be removed later on. */ const int empty_reference_handle = dst_component.add_reference(InstanceReference()); - threading::parallel_for(IndexRange(domain_size), 1024, [&](IndexRange range) { - for (const int i : range) { + threading::parallel_for(selection.index_range(), 1024, [&](IndexRange selection_range) { + for (const int range_i : selection_range) { + const int64_t i = selection[range_i]; + /* Compute base transform for every instances. */ - float4x4 &dst_transform = dst_transforms[i]; - dst_transform = float4x4::from_loc_eul_scale( - positions[i], rotations->get(i), scales->get(i)); + float4x4 &dst_transform = dst_transforms[range_i]; + dst_transform = float4x4::from_loc_eul_scale(positions[i], rotations[i], scales[i]); /* Reference that will be used by this new instance. */ int dst_handle = empty_reference_handle; - const bool use_individual_instance = pick_instance->get(i); + const bool use_individual_instance = pick_instance[i]; if (use_individual_instance) { if (src_instances != nullptr) { const int src_instances_amount = src_instances->instances_amount(); - const int original_index = indices->get(i); - /* Use #mod_i instead of `%` to get the desirable wrap around behavior where -1 refers to - * the last element. */ + const int original_index = indices[i]; + /* Use #mod_i instead of `%` to get the desirable wrap around behavior where -1 + * refers to the last element. */ const int index = mod_i(original_index, std::max(src_instances_amount, 1)); if (index < src_instances_amount) { /* Get the reference to the source instance. */ @@ -144,12 +153,12 @@ static void add_instances_from_component(InstancesComponent &dst_component, dst_handle = full_instance_handle; } /* Set properties of new instance. */ - dst_handles[i] = dst_handle; + dst_handles[range_i] = dst_handle; } }); - if (pick_instance->is_single()) { - if (pick_instance->get_internal_single()) { + if (pick_instance.is_single()) { + if (pick_instance.get_internal_single()) { if (instance.has_realized_data()) { params.error_message_add( NodeWarningType::Info, @@ -157,9 +166,40 @@ static void add_instances_from_component(InstancesComponent &dst_component, } } } + + bke::CustomDataAttributes &instance_attributes = dst_component.attributes(); + for (const auto item : attributes_to_propagate.items()) { + const AttributeIDRef &attribute_id = item.key; + const AttributeKind attribute_kind = item.value; + + const GVArray src_attribute = src_component.attribute_get_for_read( + attribute_id, ATTR_DOMAIN_POINT, attribute_kind.data_type); + BLI_assert(src_attribute); + std::optional<GMutableSpan> dst_attribute_opt = instance_attributes.get_for_write( + attribute_id); + if (!dst_attribute_opt) { + if (!instance_attributes.create(attribute_id, attribute_kind.data_type)) { + continue; + } + dst_attribute_opt = instance_attributes.get_for_write(attribute_id); + } + BLI_assert(dst_attribute_opt); + const GMutableSpan dst_attribute = dst_attribute_opt->slice(start_len, select_len); + threading::parallel_for(selection.index_range(), 1024, [&](IndexRange selection_range) { + attribute_math::convert_to_static_type(attribute_kind.data_type, [&](auto dummy) { + using T = decltype(dummy); + VArray<T> src = src_attribute.typed<T>(); + MutableSpan<T> dst = dst_attribute.typed<T>(); + for (const int range_i : selection_range) { + const int i = selection[range_i]; + dst[range_i] = src[i]; + } + }); + }); + } } -static void geo_node_instance_on_points_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Points"); GeometrySet instance = params.get_input<GeometrySet>("Instance"); @@ -168,40 +208,59 @@ static void geo_node_instance_on_points_exec(GeoNodeExecParams params) geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>(); + Map<AttributeIDRef, AttributeKind> attributes_to_propagate; + geometry_set.gather_attributes_for_propagation( + {GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE}, + GEO_COMPONENT_TYPE_INSTANCES, + false, + attributes_to_propagate); + attributes_to_propagate.remove("position"); + if (geometry_set.has<MeshComponent>()) { - add_instances_from_component( - instances, *geometry_set.get_component_for_read<MeshComponent>(), instance, params); - geometry_set.remove(GEO_COMPONENT_TYPE_MESH); + add_instances_from_component(instances, + *geometry_set.get_component_for_read<MeshComponent>(), + instance, + params, + attributes_to_propagate); } if (geometry_set.has<PointCloudComponent>()) { add_instances_from_component(instances, *geometry_set.get_component_for_read<PointCloudComponent>(), instance, - params); - geometry_set.remove(GEO_COMPONENT_TYPE_POINT_CLOUD); + params, + attributes_to_propagate); } if (geometry_set.has<CurveComponent>()) { - add_instances_from_component( - instances, *geometry_set.get_component_for_read<CurveComponent>(), instance, params); - geometry_set.remove(GEO_COMPONENT_TYPE_CURVE); + add_instances_from_component(instances, + *geometry_set.get_component_for_read<CurveComponent>(), + instance, + params, + attributes_to_propagate); } - /* Unused references may have been added above. Remove those now so that other nodes don't - * process them needlessly. */ - instances.remove_unused_references(); + geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); }); + /* Unused references may have been added above. Remove those now so that other nodes don't + * process them needlessly. + * This should eventually be moved into the loop above, but currently this is quite tricky + * because it might remove references that the loop still wants to iterate over. */ + InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>(); + instances.remove_unused_references(); + params.set_output("Instances", std::move(geometry_set)); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_instance_on_points_cc void register_node_type_geo_instance_on_points() { + namespace file_ns = blender::nodes::node_geo_instance_on_points_cc; + static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_INSTANCE_ON_POINTS, "Instance on Points", NODE_CLASS_GEOMETRY, 0); - ntype.declare = blender::nodes::geo_node_instance_on_points_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_instance_on_points_exec; + &ntype, GEO_NODE_INSTANCE_ON_POINTS, "Instance on Points", NODE_CLASS_GEOMETRY); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc new file mode 100644 index 00000000000..f9beed956bb --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc @@ -0,0 +1,137 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "DNA_pointcloud_types.h" + +#include "BKE_attribute_math.hh" +#include "BKE_pointcloud.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_instances_to_points_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>(N_("Instances")).only_instances(); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input<decl::Vector>(N_("Position")).implicit_field(); + b.add_input<decl::Float>(N_("Radius")) + .default_value(0.05f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .supports_field(); + b.add_output<decl::Geometry>(N_("Points")); +} + +template<typename T> +static void copy_attribute_to_points(const VArray<T> &src, + const IndexMask mask, + MutableSpan<T> dst) +{ + for (const int i : mask.index_range()) { + dst[i] = src[mask[i]]; + } +} + +static void convert_instances_to_points(GeometrySet &geometry_set, + Field<float3> position_field, + Field<float> radius_field, + const Field<bool> selection_field) +{ + const InstancesComponent &instances = *geometry_set.get_component_for_read<InstancesComponent>(); + + GeometryComponentFieldContext field_context{instances, ATTR_DOMAIN_INSTANCE}; + const int domain_size = instances.attribute_domain_size(ATTR_DOMAIN_INSTANCE); + + fn::FieldEvaluator evaluator{field_context, domain_size}; + evaluator.set_selection(std::move(selection_field)); + evaluator.add(std::move(position_field)); + evaluator.add(std::move(radius_field)); + evaluator.evaluate(); + const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); + if (selection.is_empty()) { + return; + } + + PointCloud *pointcloud = BKE_pointcloud_new_nomain(selection.size()); + geometry_set.replace_pointcloud(pointcloud); + + PointCloudComponent &points = geometry_set.get_component_for_write<PointCloudComponent>(); + + const VArray<float3> &positions = evaluator.get_evaluated<float3>(0); + copy_attribute_to_points(positions, selection, {(float3 *)pointcloud->co, pointcloud->totpoint}); + const VArray<float> &radii = evaluator.get_evaluated<float>(1); + copy_attribute_to_points(radii, selection, {pointcloud->radius, pointcloud->totpoint}); + + Map<AttributeIDRef, AttributeKind> attributes_to_propagate; + geometry_set.gather_attributes_for_propagation({GEO_COMPONENT_TYPE_INSTANCES}, + GEO_COMPONENT_TYPE_POINT_CLOUD, + false, + attributes_to_propagate); + /* These two attributes are added by the implicit inputs above. */ + attributes_to_propagate.remove("position"); + attributes_to_propagate.remove("radius"); + + for (const auto item : attributes_to_propagate.items()) { + const AttributeIDRef &attribute_id = item.key; + const AttributeKind attribute_kind = item.value; + + const GVArray src = instances.attribute_get_for_read( + attribute_id, ATTR_DOMAIN_INSTANCE, attribute_kind.data_type); + BLI_assert(src); + OutputAttribute dst = points.attribute_try_get_for_output_only( + attribute_id, ATTR_DOMAIN_POINT, attribute_kind.data_type); + BLI_assert(dst); + + attribute_math::convert_to_static_type(attribute_kind.data_type, [&](auto dummy) { + using T = decltype(dummy); + copy_attribute_to_points(src.typed<T>(), selection, dst.as_span().typed<T>()); + }); + dst.save(); + } +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Instances"); + + if (geometry_set.has_instances()) { + convert_instances_to_points(geometry_set, + params.extract_input<Field<float3>>("Position"), + params.extract_input<Field<float>>("Radius"), + params.extract_input<Field<bool>>("Selection")); + geometry_set.keep_only({GEO_COMPONENT_TYPE_POINT_CLOUD}); + params.set_output("Points", std::move(geometry_set)); + } + else { + params.set_default_remaining_outputs(); + } +} + +} // namespace blender::nodes::node_geo_instances_to_points_cc + +void register_node_type_geo_instances_to_points() +{ + namespace file_ns = blender::nodes::node_geo_instances_to_points_cc; + + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_INSTANCES_TO_POINTS, "Instances to Points", NODE_CLASS_GEOMETRY); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_is_viewport.cc b/source/blender/nodes/geometry/nodes/node_geo_is_viewport.cc index f8a1c764f61..c97bbad4665 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_is_viewport.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_is_viewport.cc @@ -18,14 +18,14 @@ #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_is_viewport_cc { -static void geo_node_is_viewport_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_output<decl::Bool>("Is Viewport"); + b.add_output<decl::Bool>(N_("Is Viewport")); } -static void geo_node_is_viewport_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { const Depsgraph *depsgraph = params.depsgraph(); const eEvaluationMode mode = DEG_get_mode(depsgraph); @@ -34,14 +34,16 @@ static void geo_node_is_viewport_exec(GeoNodeExecParams params) params.set_output("Is Viewport", is_viewport); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_is_viewport_cc void register_node_type_geo_is_viewport() { + namespace file_ns = blender::nodes::node_geo_is_viewport_cc; + static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_IS_VIEWPORT, "Is Viewport", NODE_CLASS_INPUT, 0); - ntype.geometry_node_execute = blender::nodes::geo_node_is_viewport_exec; - ntype.declare = blender::nodes::geo_node_is_viewport_declare; + geo_node_type_base(&ntype, GEO_NODE_IS_VIEWPORT, "Is Viewport", NODE_CLASS_INPUT); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.declare = file_ns::node_declare; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc index 93643298f92..1e521af6b13 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc @@ -19,127 +19,21 @@ #include "BKE_mesh_runtime.h" #include "BKE_pointcloud.h" #include "BKE_spline.hh" +#include "BKE_type_conversions.hh" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" -#include "NOD_type_conversions.hh" +#include "GEO_realize_instances.hh" #include "node_geometry_util.hh" -using blender::fn::GVArray_For_GSpan; +namespace blender::nodes::node_geo_join_geometry_cc { -namespace blender::nodes { - -static void geo_node_join_geometry_declare(NodeDeclarationBuilder &b) -{ - b.add_input<decl::Geometry>("Geometry").multi_input(); - b.add_output<decl::Geometry>("Geometry"); -} - -static Mesh *join_mesh_topology_and_builtin_attributes(Span<const MeshComponent *> src_components) +static void node_declare(NodeDeclarationBuilder &b) { - int totverts = 0; - int totloops = 0; - int totedges = 0; - int totpolys = 0; - - int64_t cd_dirty_vert = 0; - int64_t cd_dirty_poly = 0; - int64_t cd_dirty_edge = 0; - int64_t cd_dirty_loop = 0; - - VectorSet<Material *> materials; - - for (const MeshComponent *mesh_component : src_components) { - const Mesh *mesh = mesh_component->get_for_read(); - totverts += mesh->totvert; - totloops += mesh->totloop; - totedges += mesh->totedge; - totpolys += mesh->totpoly; - cd_dirty_vert |= mesh->runtime.cd_dirty_vert; - cd_dirty_poly |= mesh->runtime.cd_dirty_poly; - cd_dirty_edge |= mesh->runtime.cd_dirty_edge; - cd_dirty_loop |= mesh->runtime.cd_dirty_loop; - - for (const int slot_index : IndexRange(mesh->totcol)) { - Material *material = mesh->mat[slot_index]; - materials.add(material); - } - } - - const Mesh *first_input_mesh = src_components[0]->get_for_read(); - Mesh *new_mesh = BKE_mesh_new_nomain(totverts, totedges, 0, totloops, totpolys); - BKE_mesh_copy_parameters_for_eval(new_mesh, first_input_mesh); - - for (const int i : IndexRange(materials.size())) { - Material *material = materials[i]; - BKE_id_material_eval_assign(&new_mesh->id, i + 1, material); - } - - new_mesh->runtime.cd_dirty_vert = cd_dirty_vert; - new_mesh->runtime.cd_dirty_poly = cd_dirty_poly; - new_mesh->runtime.cd_dirty_edge = cd_dirty_edge; - new_mesh->runtime.cd_dirty_loop = cd_dirty_loop; - - int vert_offset = 0; - int loop_offset = 0; - int edge_offset = 0; - int poly_offset = 0; - for (const MeshComponent *mesh_component : src_components) { - const Mesh *mesh = mesh_component->get_for_read(); - if (mesh == nullptr) { - continue; - } - - Array<int> material_index_map(mesh->totcol); - for (const int i : IndexRange(mesh->totcol)) { - Material *material = mesh->mat[i]; - const int new_material_index = materials.index_of(material); - material_index_map[i] = new_material_index; - } - - for (const int i : IndexRange(mesh->totvert)) { - const MVert &old_vert = mesh->mvert[i]; - MVert &new_vert = new_mesh->mvert[vert_offset + i]; - new_vert = old_vert; - } - - for (const int i : IndexRange(mesh->totedge)) { - const MEdge &old_edge = mesh->medge[i]; - MEdge &new_edge = new_mesh->medge[edge_offset + i]; - new_edge = old_edge; - new_edge.v1 += vert_offset; - new_edge.v2 += vert_offset; - } - for (const int i : IndexRange(mesh->totloop)) { - const MLoop &old_loop = mesh->mloop[i]; - MLoop &new_loop = new_mesh->mloop[loop_offset + i]; - new_loop = old_loop; - new_loop.v += vert_offset; - new_loop.e += edge_offset; - } - for (const int i : IndexRange(mesh->totpoly)) { - const MPoly &old_poly = mesh->mpoly[i]; - MPoly &new_poly = new_mesh->mpoly[poly_offset + i]; - new_poly = old_poly; - new_poly.loopstart += loop_offset; - if (old_poly.mat_nr >= 0 && old_poly.mat_nr < mesh->totcol) { - new_poly.mat_nr = material_index_map[new_poly.mat_nr]; - } - else { - /* The material index was invalid before. */ - new_poly.mat_nr = 0; - } - } - - vert_offset += mesh->totvert; - loop_offset += mesh->totloop; - edge_offset += mesh->totedge; - poly_offset += mesh->totpoly; - } - - return new_mesh; + b.add_input<decl::Geometry>(N_("Geometry")).multi_input(); + b.add_output<decl::Geometry>(N_("Geometry")); } template<typename Component> @@ -190,10 +84,10 @@ static void fill_new_attribute(Span<const GeometryComponent *> src_components, if (domain_size == 0) { continue; } - GVArrayPtr read_attribute = component->attribute_get_for_read( + GVArray read_attribute = component->attribute_get_for_read( attribute_id, domain, data_type, nullptr); - GVArray_GSpan src_span{*read_attribute}; + GVArray_GSpan src_span{read_attribute}; const void *src_buffer = src_span.data(); void *dst_buffer = dst_span[offset]; cpp_type->copy_assign_n(src_buffer, dst_buffer, domain_size); @@ -209,7 +103,7 @@ static void join_attributes(Span<const GeometryComponent *> src_components, const Map<AttributeIDRef, AttributeMetaData> info = get_final_attribute_info(src_components, ignored_attributes); - for (const Map<AttributeIDRef, AttributeMetaData>::Item &item : info.items()) { + for (const Map<AttributeIDRef, AttributeMetaData>::Item item : info.items()) { const AttributeIDRef attribute_id = item.key; const AttributeMetaData &meta_data = item.value; @@ -225,33 +119,6 @@ static void join_attributes(Span<const GeometryComponent *> src_components, } } -static void join_components(Span<const MeshComponent *> src_components, GeometrySet &result) -{ - Mesh *new_mesh = join_mesh_topology_and_builtin_attributes(src_components); - - MeshComponent &dst_component = result.get_component_for_write<MeshComponent>(); - dst_component.replace(new_mesh); - - /* Don't copy attributes that are stored directly in the mesh data structs. */ - join_attributes(to_base_components(src_components), - dst_component, - {"position", "material_index", "normal", "shade_smooth", "crease"}); -} - -static void join_components(Span<const PointCloudComponent *> src_components, GeometrySet &result) -{ - int totpoints = 0; - for (const PointCloudComponent *pointcloud_component : src_components) { - totpoints += pointcloud_component->attribute_domain_size(ATTR_DOMAIN_POINT); - } - - PointCloudComponent &dst_component = result.get_component_for_write<PointCloudComponent>(); - PointCloud *pointcloud = BKE_pointcloud_new_nomain(totpoints); - dst_component.replace(pointcloud); - - join_attributes(to_base_components(src_components), dst_component); -} - static void join_components(Span<const InstancesComponent *> src_components, GeometrySet &result) { InstancesComponent &dst_component = result.get_component_for_write<InstancesComponent>(); @@ -270,17 +137,16 @@ static void join_components(Span<const InstancesComponent *> src_components, Geo } Span<float4x4> src_transforms = src_component->instance_transforms(); - Span<int> src_ids = src_component->instance_ids(); Span<int> src_reference_handles = src_component->instance_reference_handles(); for (const int i : src_transforms.index_range()) { const int src_handle = src_reference_handles[i]; const int dst_handle = handle_map[src_handle]; const float4x4 &transform = src_transforms[i]; - const int id = src_ids[i]; - dst_component.add_instance(dst_handle, transform, id); + dst_component.add_instance(dst_handle, transform); } } + join_attributes(to_base_components(src_components), dst_component, {"position"}); } static void join_components(Span<const VolumeComponent *> src_components, GeometrySet &result) @@ -291,169 +157,6 @@ static void join_components(Span<const VolumeComponent *> src_components, Geomet UNUSED_VARS(src_components, dst_component); } -/** - * \note This takes advantage of the fact that creating attributes on joined curves never - * changes a point attribute into a spline attribute; it is always the other way around. - */ -static void ensure_control_point_attribute(const AttributeIDRef &attribute_id, - const CustomDataType data_type, - Span<CurveComponent *> src_components, - CurveEval &result) -{ - MutableSpan<SplinePtr> splines = result.splines(); - const CPPType &type = *bke::custom_data_type_to_cpp_type(data_type); - - /* In order to fill point attributes with spline domain attribute values where necessary, keep - * track of the curve each spline came from while iterating over the splines in the result. */ - int src_component_index = 0; - int spline_index_in_component = 0; - const CurveEval *current_curve = src_components[src_component_index]->get_for_read(); - - for (SplinePtr &spline : splines) { - std::optional<GSpan> attribute = spline->attributes.get_for_read(attribute_id); - - if (attribute) { - if (attribute->type() != type) { - /* In this case, the attribute exists, but it has the wrong type. So create a buffer - * for the converted values, do the conversion, and then replace the attribute. */ - void *converted_buffer = MEM_mallocN_aligned( - spline->size() * type.size(), type.alignment(), __func__); - - const DataTypeConversions &conversions = blender::nodes::get_implicit_type_conversions(); - conversions.try_convert(std::make_unique<GVArray_For_GSpan>(*attribute), type) - ->materialize(converted_buffer); - - spline->attributes.remove(attribute_id); - spline->attributes.create_by_move(attribute_id, data_type, converted_buffer); - } - } - else { - spline->attributes.create(attribute_id, data_type); - - if (current_curve->attributes.get_for_read(attribute_id)) { - /* In this case the attribute did not exist, but there is a spline domain attribute - * we can retrieve a value from, as a spline to point domain conversion. So fill the - * new attribute with the value for this spline. */ - GVArrayPtr current_curve_attribute = current_curve->attributes.get_for_read( - attribute_id, data_type, nullptr); - - BLI_assert(spline->attributes.get_for_read(attribute_id)); - std::optional<GMutableSpan> new_attribute = spline->attributes.get_for_write(attribute_id); - - BUFFER_FOR_CPP_TYPE_VALUE(type, buffer); - current_curve_attribute->get(spline_index_in_component, buffer); - type.fill_assign_n(buffer, new_attribute->data(), new_attribute->size()); - } - } - - /* Move to the next spline and maybe the next input component. */ - spline_index_in_component++; - if (spline != splines.last() && spline_index_in_component >= current_curve->splines().size()) { - src_component_index++; - spline_index_in_component = 0; - - current_curve = src_components[src_component_index]->get_for_read(); - } - } -} - -/** - * Fill data for an attribute on the new curve based on all source curves. - */ -static void ensure_spline_attribute(const AttributeIDRef &attribute_id, - const CustomDataType data_type, - Span<CurveComponent *> src_components, - CurveEval &result) -{ - const CPPType &type = *bke::custom_data_type_to_cpp_type(data_type); - - result.attributes.create(attribute_id, data_type); - GMutableSpan result_attribute = *result.attributes.get_for_write(attribute_id); - - int offset = 0; - for (const CurveComponent *component : src_components) { - const CurveEval &curve = *component->get_for_read(); - const int size = curve.splines().size(); - if (size == 0) { - continue; - } - GVArrayPtr read_attribute = curve.attributes.get_for_read(attribute_id, data_type, nullptr); - GVArray_GSpan src_span{*read_attribute}; - - const void *src_buffer = src_span.data(); - type.copy_assign_n(src_buffer, result_attribute[offset], size); - - offset += size; - } -} - -/** - * Special handling for copying spline attributes. This is necessary because we move the splines - * out of the source components instead of copying them, meaning we can no longer access point - * domain attributes on the source components. - * - * \warning Splines have been moved out of the source components at this point, so it - * is important to only read curve-level data (spline domain attributes) from them. - */ -static void join_curve_attributes(const Map<AttributeIDRef, AttributeMetaData> &info, - Span<CurveComponent *> src_components, - CurveEval &result) -{ - for (const Map<AttributeIDRef, AttributeMetaData>::Item &item : info.items()) { - const AttributeIDRef attribute_id = item.key; - const AttributeMetaData meta_data = item.value; - - if (meta_data.domain == ATTR_DOMAIN_CURVE) { - ensure_spline_attribute(attribute_id, meta_data.data_type, src_components, result); - } - else { - ensure_control_point_attribute(attribute_id, meta_data.data_type, src_components, result); - } - } -} - -static void join_curve_components(MutableSpan<GeometrySet> src_geometry_sets, GeometrySet &result) -{ - Vector<CurveComponent *> src_components; - for (GeometrySet &geometry_set : src_geometry_sets) { - if (geometry_set.has_curve()) { - /* Retrieving with write access seems counterintuitive, but it can allow avoiding a copy - * in the case where the input spline has no other users, because the splines can be - * moved from the source curve rather than copied from a read-only source. Retrieving - * the curve for write will make a copy only when it has a user elsewhere. */ - CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>(); - src_components.append(&component); - } - } - - if (src_components.size() == 0) { - return; - } - if (src_components.size() == 1) { - result.add(*src_components[0]); - return; - } - - /* Retrieve attribute info before moving the splines out of the input components. */ - const Map<AttributeIDRef, AttributeMetaData> info = get_final_attribute_info( - {(const GeometryComponent **)src_components.data(), src_components.size()}, - {"position", "radius", "tilt", "cyclic", "resolution"}); - - CurveComponent &dst_component = result.get_component_for_write<CurveComponent>(); - CurveEval *dst_curve = new CurveEval(); - for (CurveComponent *component : src_components) { - CurveEval *src_curve = component->get_for_write(); - for (SplinePtr &spline : src_curve->splines()) { - dst_curve->add_spline(std::move(spline)); - } - } - dst_curve->attributes.reallocate(dst_curve->splines().size()); - - join_curve_attributes(info, src_components, *dst_curve); - - dst_component.replace(dst_curve); -} - template<typename Component> static void join_component_type(Span<GeometrySet> src_geometry_sets, GeometrySet &result) { @@ -472,10 +175,31 @@ static void join_component_type(Span<GeometrySet> src_geometry_sets, GeometrySet result.add(*components[0]); return; } - join_components(components, result); + + GeometrySet instances_geometry_set; + InstancesComponent &instances = + instances_geometry_set.get_component_for_write<InstancesComponent>(); + + if constexpr (is_same_any_v<Component, InstancesComponent, VolumeComponent>) { + join_components(components, result); + } + else { + for (const Component *component : components) { + GeometrySet tmp_geo; + tmp_geo.add(*component); + const int handle = instances.add_reference(InstanceReference{tmp_geo}); + instances.add_instance(handle, float4x4::identity()); + } + + geometry::RealizeInstancesOptions options; + options.keep_original_ids = true; + options.realize_instance_attributes = false; + GeometrySet joined_components = geometry::realize_instances(instances_geometry_set, options); + result.add(joined_components.get_component_for_write<Component>()); + } } -static void geo_node_join_geometry_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { Vector<GeometrySet> geometry_sets = params.extract_multi_input<GeometrySet>("Geometry"); @@ -484,18 +208,20 @@ static void geo_node_join_geometry_exec(GeoNodeExecParams params) join_component_type<PointCloudComponent>(geometry_sets, geometry_set_result); join_component_type<InstancesComponent>(geometry_sets, geometry_set_result); join_component_type<VolumeComponent>(geometry_sets, geometry_set_result); - join_curve_components(geometry_sets, geometry_set_result); + join_component_type<CurveComponent>(geometry_sets, geometry_set_result); params.set_output("Geometry", std::move(geometry_set_result)); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_join_geometry_cc void register_node_type_geo_join_geometry() { + namespace file_ns = blender::nodes::node_geo_join_geometry_cc; + static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_JOIN_GEOMETRY, "Join Geometry", NODE_CLASS_GEOMETRY, 0); - ntype.geometry_node_execute = blender::nodes::geo_node_join_geometry_exec; - ntype.declare = blender::nodes::geo_node_join_geometry_declare; + geo_node_type_base(&ntype, GEO_NODE_JOIN_GEOMETRY, "Join Geometry", NODE_CLASS_GEOMETRY); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.declare = file_ns::node_declare; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc b/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc deleted file mode 100644 index 780994996ae..00000000000 --- a/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc +++ /dev/null @@ -1,97 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include "node_geometry_util.hh" - -#include "UI_interface.h" -#include "UI_resources.h" - -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" - -#include "BKE_material.h" - -namespace blender::nodes { - -static void geo_node_material_assign_declare(NodeDeclarationBuilder &b) -{ - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Material>("Material").hide_label(); - b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field(); - b.add_output<decl::Geometry>("Geometry"); -} - -static void assign_material_to_faces(Mesh &mesh, const IndexMask selection, Material *material) -{ - int new_material_index = -1; - for (const int i : IndexRange(mesh.totcol)) { - Material *other_material = mesh.mat[i]; - if (other_material == material) { - new_material_index = i; - break; - } - } - if (new_material_index == -1) { - /* Append a new material index. */ - new_material_index = mesh.totcol; - BKE_id_material_eval_assign(&mesh.id, new_material_index + 1, material); - } - - mesh.mpoly = (MPoly *)CustomData_duplicate_referenced_layer(&mesh.pdata, CD_MPOLY, mesh.totpoly); - for (const int i : selection) { - MPoly &poly = mesh.mpoly[i]; - poly.mat_nr = new_material_index; - } -} - -static void geo_node_material_assign_exec(GeoNodeExecParams params) -{ - Material *material = params.extract_input<Material *>("Material"); - const Field<bool> selection_field = params.extract_input<Field<bool>>("Selection"); - - GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - - geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (geometry_set.has<MeshComponent>()) { - MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>(); - Mesh *mesh = mesh_component.get_for_write(); - if (mesh != nullptr) { - GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_FACE}; - - fn::FieldEvaluator selection_evaluator{field_context, mesh->totpoly}; - selection_evaluator.add(selection_field); - selection_evaluator.evaluate(); - const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); - - assign_material_to_faces(*mesh, selection, material); - } - } - }); - - params.set_output("Geometry", std::move(geometry_set)); -} - -} // namespace blender::nodes - -void register_node_type_geo_material_assign() -{ - static bNodeType ntype; - - geo_node_type_base(&ntype, GEO_NODE_MATERIAL_ASSIGN, "Material Assign", NODE_CLASS_GEOMETRY, 0); - ntype.declare = blender::nodes::geo_node_material_assign_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_material_assign_exec; - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc b/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc index a917434fa00..0309121db74 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc @@ -24,17 +24,17 @@ #include "BKE_material.h" -namespace blender::nodes { +namespace blender::nodes::node_geo_material_replace_cc { -static void geo_node_material_replace_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Material>("Old"); - b.add_input<decl::Material>("New"); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")).supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input<decl::Material>(N_("Old")); + b.add_input<decl::Material>(N_("New")); + b.add_output<decl::Geometry>(N_("Geometry")); } -static void geo_node_material_replace_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { Material *old_material = params.extract_input<Material *>("Old"); Material *new_material = params.extract_input<Material *>("New"); @@ -55,15 +55,16 @@ static void geo_node_material_replace_exec(GeoNodeExecParams params) params.set_output("Geometry", std::move(geometry_set)); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_material_replace_cc void register_node_type_geo_material_replace() { + namespace file_ns = blender::nodes::node_geo_material_replace_cc; + static bNodeType ntype; - geo_node_type_base( - &ntype, GEO_NODE_MATERIAL_REPLACE, "Material Replace", NODE_CLASS_GEOMETRY, 0); - ntype.declare = blender::nodes::geo_node_material_replace_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_material_replace_exec; + geo_node_type_base(&ntype, GEO_NODE_REPLACE_MATERIAL, "Replace Material", NODE_CLASS_GEOMETRY); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc index 337bd88c6e6..0b5f0bf34c5 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc @@ -26,12 +26,12 @@ #include "BKE_material.h" -namespace blender::nodes { +namespace blender::nodes::node_geo_material_selection_cc { -static void geo_node_material_selection_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Material>("Material").hide_label(true); - b.add_output<decl::Bool>("Selection").field_source(); + b.add_input<decl::Material>(N_("Material")).hide_label(true); + b.add_output<decl::Bool>(N_("Selection")).field_source(); } static void select_mesh_by_material(const Mesh &mesh, @@ -54,78 +54,76 @@ static void select_mesh_by_material(const Mesh &mesh, }); } -class MaterialSelectionFieldInput final : public fn::FieldInput { +class MaterialSelectionFieldInput final : public GeometryFieldInput { Material *material_; public: MaterialSelectionFieldInput(Material *material) - : fn::FieldInput(CPPType::get<bool>(), "Material Selection"), material_(material) + : GeometryFieldInput(CPPType::get<bool>(), "Material Selection node"), material_(material) { + category_ = Category::Generated; } - const GVArray *get_varray_for_context(const fn::FieldContext &context, - IndexMask mask, - ResourceScope &scope) const final + GVArray get_varray_for_context(const GeometryComponent &component, + const AttributeDomain domain, + IndexMask mask) const final { - if (const GeometryComponentFieldContext *geometry_context = - dynamic_cast<const GeometryComponentFieldContext *>(&context)) { - const GeometryComponent &component = geometry_context->geometry_component(); - const AttributeDomain domain = geometry_context->domain(); - if (component.type() != GEO_COMPONENT_TYPE_MESH) { - return nullptr; - } - const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); - const Mesh *mesh = mesh_component.get_for_read(); - if (mesh == nullptr) { - return nullptr; - } - - if (domain == ATTR_DOMAIN_FACE) { - Array<bool> selection(mask.min_array_size()); - select_mesh_by_material(*mesh, material_, mask, selection); - return &scope.construct<fn::GVArray_For_ArrayContainer<Array<bool>>>(std::move(selection)); - } - - Array<bool> selection(mesh->totpoly); - select_mesh_by_material(*mesh, material_, IndexMask(mesh->totpoly), selection); - GVArrayPtr face_selection = std::make_unique<fn::GVArray_For_ArrayContainer<Array<bool>>>( - std::move(selection)); - GVArrayPtr final_selection = mesh_component.attribute_try_adapt_domain( - std::move(face_selection), ATTR_DOMAIN_FACE, domain); - return scope.add_value(std::move(final_selection)).get(); + if (component.type() != GEO_COMPONENT_TYPE_MESH) { + return {}; + } + const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); + const Mesh *mesh = mesh_component.get_for_read(); + if (mesh == nullptr) { + return {}; + } + + if (domain == ATTR_DOMAIN_FACE) { + Array<bool> selection(mask.min_array_size()); + select_mesh_by_material(*mesh, material_, mask, selection); + return VArray<bool>::ForContainer(std::move(selection)); } + Array<bool> selection(mesh->totpoly); + select_mesh_by_material(*mesh, material_, IndexMask(mesh->totpoly), selection); + return mesh_component.attribute_try_adapt_domain<bool>( + VArray<bool>::ForContainer(std::move(selection)), ATTR_DOMAIN_FACE, domain); + return nullptr; } uint64_t hash() const override { - /* Some random constant hash. */ - return 91619626; + return get_default_hash(material_); } bool is_equal_to(const fn::FieldNode &other) const override { - return dynamic_cast<const MaterialSelectionFieldInput *>(&other) != nullptr; + if (const MaterialSelectionFieldInput *other_material_selection = + dynamic_cast<const MaterialSelectionFieldInput *>(&other)) { + return material_ == other_material_selection->material_; + } + return false; } }; -static void geo_node_material_selection_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { Material *material = params.extract_input<Material *>("Material"); Field<bool> material_field{std::make_shared<MaterialSelectionFieldInput>(material)}; params.set_output("Selection", std::move(material_field)); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_material_selection_cc void register_node_type_geo_material_selection() { + namespace file_ns = blender::nodes::node_geo_material_selection_cc; + static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_MATERIAL_SELECTION, "Material Selection", NODE_CLASS_GEOMETRY, 0); - ntype.declare = blender::nodes::geo_node_material_selection_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_material_selection_exec; + &ntype, GEO_NODE_MATERIAL_SELECTION, "Material Selection", NODE_CLASS_GEOMETRY); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc b/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc new file mode 100644 index 00000000000..deb149fd0f0 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc @@ -0,0 +1,108 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "GEO_mesh_merge_by_distance.hh" +#include "GEO_point_merge_by_distance.hh" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_merge_by_distance_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>(N_("Geometry")) + .supported_type({GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_MESH}); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input<decl::Float>(N_("Distance")).default_value(0.1f).min(0.0f).subtype(PROP_DISTANCE); + b.add_output<decl::Geometry>(N_("Geometry")); +} + +static PointCloud *pointcloud_merge_by_distance(const PointCloudComponent &src_points, + const float merge_distance, + const Field<bool> &selection_field) +{ + const int src_size = src_points.attribute_domain_size(ATTR_DOMAIN_POINT); + GeometryComponentFieldContext context{src_points, ATTR_DOMAIN_POINT}; + FieldEvaluator evaluator{context, src_size}; + evaluator.add(selection_field); + evaluator.evaluate(); + + const IndexMask selection = evaluator.get_evaluated_as_mask(0); + if (selection.is_empty()) { + return nullptr; + } + + return geometry::point_merge_by_distance(src_points, merge_distance, selection); +} + +static std::optional<Mesh *> mesh_merge_by_distance(const MeshComponent &mesh_component, + const float merge_distance, + const Field<bool> &selection_field) +{ + const int src_size = mesh_component.attribute_domain_size(ATTR_DOMAIN_POINT); + GeometryComponentFieldContext context{mesh_component, ATTR_DOMAIN_POINT}; + FieldEvaluator evaluator{context, src_size}; + evaluator.add(selection_field); + evaluator.evaluate(); + + const IndexMask selection = evaluator.get_evaluated_as_mask(0); + if (selection.is_empty()) { + return nullptr; + } + + const Mesh &mesh = *mesh_component.get_for_read(); + return geometry::mesh_merge_by_distance_all(mesh, selection, merge_distance); +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + + const Field<bool> selection = params.extract_input<Field<bool>>("Selection"); + const float merge_distance = params.extract_input<float>("Distance"); + + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (geometry_set.has_pointcloud()) { + PointCloud *result = pointcloud_merge_by_distance( + *geometry_set.get_component_for_read<PointCloudComponent>(), merge_distance, selection); + geometry_set.replace_pointcloud(result); + } + if (geometry_set.has_mesh()) { + std::optional<Mesh *> result = mesh_merge_by_distance( + *geometry_set.get_component_for_read<MeshComponent>(), merge_distance, selection); + if (result) { + geometry_set.replace_mesh(*result); + } + } + }); + + params.set_output("Geometry", std::move(geometry_set)); +} + +} // namespace blender::nodes::node_geo_merge_by_distance_cc + +void register_node_type_geo_merge_by_distance() +{ + namespace file_ns = blender::nodes::node_geo_merge_by_distance_cc; + + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_MERGE_BY_DISTANCE, "Merge by Distance", NODE_CLASS_GEOMETRY); + + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc index 9c477c639a2..b3b11b2e0e9 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc @@ -25,28 +25,34 @@ #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_mesh_primitive_circle_cc { -static void geo_node_mesh_primitive_circle_declare(NodeDeclarationBuilder &b) +NODE_STORAGE_FUNCS(NodeGeometryMeshCircle) + +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Int>("Vertices").default_value(32).min(3); - b.add_input<decl::Float>("Radius").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Int>(N_("Vertices")) + .default_value(32) + .min(3) + .description(N_("Number of vertices on the circle")); + b.add_input<decl::Float>(N_("Radius")) + .default_value(1.0f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description(N_("Distance of the vertices from the origin")); + b.add_output<decl::Geometry>(N_("Mesh")); } -static void geo_node_mesh_primitive_circle_layout(uiLayout *layout, - bContext *UNUSED(C), - PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); uiItemR(layout, ptr, "fill_type", 0, nullptr, ICON_NONE); } -static void geo_node_mesh_primitive_circle_init(bNodeTree *UNUSED(ntree), bNode *node) +static void node_init(bNodeTree *UNUSED(ntree), bNode *node) { - NodeGeometryMeshCircle *node_storage = (NodeGeometryMeshCircle *)MEM_callocN( - sizeof(NodeGeometryMeshCircle), __func__); + NodeGeometryMeshCircle *node_storage = MEM_cnew<NodeGeometryMeshCircle>(__func__); node_storage->fill_type = GEO_NODE_MESH_CIRCLE_FILL_NONE; @@ -132,12 +138,6 @@ static Mesh *create_circle_mesh(const float radius, copy_v3_v3(verts.last().co, float3(0)); } - /* Point all vertex normals in the up direction. */ - const short up_normal[3] = {0, 0, SHRT_MAX}; - for (MVert &vert : verts) { - copy_v3_v3_short(vert.no, up_normal); - } - /* Create outer edges. */ const short edge_flag = (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NONE) ? ME_LOOSEEDGE : @@ -192,42 +192,38 @@ static Mesh *create_circle_mesh(const float radius, return mesh; } -static void geo_node_mesh_primitive_circle_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { - const bNode &node = params.node(); - const NodeGeometryMeshCircle &storage = *(const NodeGeometryMeshCircle *)node.storage; - - const GeometryNodeMeshCircleFillType fill_type = (const GeometryNodeMeshCircleFillType) - storage.fill_type; + const NodeGeometryMeshCircle &storage = node_storage(params.node()); + const GeometryNodeMeshCircleFillType fill = (GeometryNodeMeshCircleFillType)storage.fill_type; const float radius = params.extract_input<float>("Radius"); const int verts_num = params.extract_input<int>("Vertices"); if (verts_num < 3) { params.error_message_add(NodeWarningType::Info, TIP_("Vertices must be at least 3")); - params.set_output("Geometry", GeometrySet()); + params.set_default_remaining_outputs(); return; } - Mesh *mesh = create_circle_mesh(radius, verts_num, fill_type); - - BLI_assert(BKE_mesh_is_valid(mesh)); + Mesh *mesh = create_circle_mesh(radius, verts_num, fill); - params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); + params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_mesh_primitive_circle_cc void register_node_type_geo_mesh_primitive_circle() { + namespace file_ns = blender::nodes::node_geo_mesh_primitive_circle_cc; + static bNodeType ntype; - geo_node_type_base( - &ntype, GEO_NODE_MESH_PRIMITIVE_CIRCLE, "Mesh Circle", NODE_CLASS_GEOMETRY, 0); - node_type_init(&ntype, blender::nodes::geo_node_mesh_primitive_circle_init); + geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_CIRCLE, "Mesh Circle", NODE_CLASS_GEOMETRY); + node_type_init(&ntype, file_ns::node_init); node_type_storage( &ntype, "NodeGeometryMeshCircle", node_free_standard_storage, node_copy_standard_storage); - ntype.geometry_node_execute = blender::nodes::geo_node_mesh_primitive_circle_exec; - ntype.draw_buttons = blender::nodes::geo_node_mesh_primitive_circle_layout; - ntype.declare = blender::nodes::geo_node_mesh_primitive_circle_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.draw_buttons = file_ns::node_layout; + ntype.declare = file_ns::node_declare; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc index 0d58476fc58..e0923344421 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc @@ -25,529 +25,797 @@ #include "node_geometry_util.hh" +#include <cmath> + namespace blender::nodes { -static void geo_node_mesh_primitive_cone_declare(NodeDeclarationBuilder &b) -{ - b.add_input<decl::Int>("Vertices").default_value(32).min(3); - b.add_input<decl::Float>("Radius Top").min(0.0f).subtype(PROP_DISTANCE); - b.add_input<decl::Float>("Radius Bottom").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input<decl::Float>("Depth").default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_output<decl::Geometry>("Geometry"); -} +struct ConeConfig { + float radius_top; + float radius_bottom; + float height; + int circle_segments; + int side_segments; + int fill_segments; + GeometryNodeMeshCircleFillType fill_type; + + bool top_is_point; + bool bottom_is_point; + /* The cone tip and a triangle fan filling are topologically identical. + * This simplifies the logic in some cases. */ + bool top_has_center_vert; + bool bottom_has_center_vert; + + /* Helpful quantities. */ + int tot_quad_rings; + int tot_edge_rings; + int tot_verts; + int tot_edges; + int tot_corners; + int tot_faces; + + /* Helpful vertex indices. */ + int first_vert; + int first_ring_verts_start; + int last_ring_verts_start; + int last_vert; + + /* Helpful edge indices. */ + int first_ring_edges_start; + int last_ring_edges_start; + int last_fan_edges_start; + int last_edge; + + /* Helpful face indices. */ + int top_faces_start; + int top_faces_len; + int side_faces_start; + int side_faces_len; + int bottom_faces_start; + int bottom_faces_len; + + ConeConfig(float radius_top, + float radius_bottom, + float depth, + int circle_segments, + int side_segments, + int fill_segments, + GeometryNodeMeshCircleFillType fill_type) + : radius_top(radius_top), + radius_bottom(radius_bottom), + height(0.5f * depth), + circle_segments(circle_segments), + side_segments(side_segments), + fill_segments(fill_segments), + fill_type(fill_type) + { + this->top_is_point = this->radius_top == 0.0f; + this->bottom_is_point = this->radius_bottom == 0.0f; + this->top_has_center_vert = this->top_is_point || + this->fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN; + this->bottom_has_center_vert = this->bottom_is_point || + this->fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN; + + this->tot_quad_rings = this->calculate_total_quad_rings(); + this->tot_edge_rings = this->calculate_total_edge_rings(); + this->tot_verts = this->calculate_total_verts(); + this->tot_edges = this->calculate_total_edges(); + this->tot_corners = this->calculate_total_corners(); + + this->first_vert = 0; + this->first_ring_verts_start = this->top_has_center_vert ? 1 : first_vert; + this->last_vert = this->tot_verts - 1; + this->last_ring_verts_start = this->last_vert - this->circle_segments; + + this->first_ring_edges_start = this->top_has_center_vert ? this->circle_segments : 0; + this->last_ring_edges_start = this->first_ring_edges_start + + this->tot_quad_rings * this->circle_segments * 2; + this->last_fan_edges_start = this->tot_edges - this->circle_segments; + this->last_edge = this->tot_edges - 1; + + this->top_faces_start = 0; + if (!this->top_is_point) { + this->top_faces_len = (fill_segments - 1) * circle_segments; + this->top_faces_len += this->top_has_center_vert ? circle_segments : 0; + this->top_faces_len += this->fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON ? 1 : 0; + } + else { + this->top_faces_len = 0; + } -static void geo_node_mesh_primitive_cone_layout(uiLayout *layout, - bContext *UNUSED(C), - PointerRNA *ptr) -{ - uiLayoutSetPropSep(layout, true); - uiLayoutSetPropDecorate(layout, false); - uiItemR(layout, ptr, "fill_type", 0, nullptr, ICON_NONE); -} + this->side_faces_start = this->top_faces_len; + if (this->top_is_point && this->bottom_is_point) { + this->side_faces_len = 0; + } + else { + this->side_faces_len = side_segments * circle_segments; + } -static void geo_node_mesh_primitive_cone_init(bNodeTree *UNUSED(ntree), bNode *node) -{ - NodeGeometryMeshCone *node_storage = (NodeGeometryMeshCone *)MEM_callocN( - sizeof(NodeGeometryMeshCone), __func__); + if (!this->bottom_is_point) { + this->bottom_faces_len = (fill_segments - 1) * circle_segments; + this->bottom_faces_len += this->bottom_has_center_vert ? circle_segments : 0; + this->bottom_faces_len += this->fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON ? 1 : 0; + } + else { + this->bottom_faces_len = 0; + } + this->bottom_faces_start = this->side_faces_start + this->side_faces_len; - node_storage->fill_type = GEO_NODE_MESH_CIRCLE_FILL_NGON; + this->tot_faces = this->top_faces_len + this->side_faces_len + this->bottom_faces_len; + } - node->storage = node_storage; -} + private: + int calculate_total_quad_rings(); + int calculate_total_edge_rings(); + int calculate_total_verts(); + int calculate_total_edges(); + int calculate_total_corners(); +}; -static int vert_total(const GeometryNodeMeshCircleFillType fill_type, - const int verts_num, - const bool top_is_point, - const bool bottom_is_point) +int ConeConfig::calculate_total_quad_rings() { - int vert_total = 0; - if (!top_is_point) { - vert_total += verts_num; - if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { - vert_total++; - } + if (top_is_point && bottom_is_point) { + return 0; } - else { - vert_total++; + + int quad_rings = 0; + + if (!top_is_point) { + quad_rings += fill_segments - 1; } + + quad_rings += (!top_is_point && !bottom_is_point) ? side_segments : (side_segments - 1); + if (!bottom_is_point) { - vert_total += verts_num; - if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { - vert_total++; - } - } - else { - vert_total++; + quad_rings += fill_segments - 1; } - return vert_total; + return quad_rings; } -static int edge_total(const GeometryNodeMeshCircleFillType fill_type, - const int verts_num, - const bool top_is_point, - const bool bottom_is_point) +int ConeConfig::calculate_total_edge_rings() { if (top_is_point && bottom_is_point) { - return 1; + return 0; } - int edge_total = 0; + int edge_rings = 0; + if (!top_is_point) { - edge_total += verts_num; - if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { - edge_total += verts_num; - } + edge_rings += fill_segments; } - edge_total += verts_num; + edge_rings += side_segments - 1; if (!bottom_is_point) { - edge_total += verts_num; - if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { - edge_total += verts_num; - } + edge_rings += fill_segments; } - return edge_total; + return edge_rings; } -static int corner_total(const GeometryNodeMeshCircleFillType fill_type, - const int verts_num, - const bool top_is_point, - const bool bottom_is_point) +int ConeConfig::calculate_total_verts() { if (top_is_point && bottom_is_point) { - return 0; + return side_segments + 1; + } + + int vert_total = 0; + + if (top_has_center_vert) { + vert_total++; } - int corner_total = 0; if (!top_is_point) { - if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { - corner_total += verts_num; - } - else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { - corner_total += verts_num * 3; - } + vert_total += circle_segments * fill_segments; } - if (!top_is_point && !bottom_is_point) { - corner_total += verts_num * 4; + vert_total += circle_segments * (side_segments - 1); + + if (!bottom_is_point) { + vert_total += circle_segments * fill_segments; } - else { - corner_total += verts_num * 3; + + if (bottom_has_center_vert) { + vert_total++; } - if (!bottom_is_point) { - if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { - corner_total += verts_num; - } - else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { - corner_total += verts_num * 3; - } + return vert_total; +} + +int ConeConfig::calculate_total_edges() +{ + if (top_is_point && bottom_is_point) { + return side_segments; } - return corner_total; + int edge_total = 0; + if (top_has_center_vert) { + edge_total += circle_segments; + } + + edge_total += circle_segments * (tot_quad_rings * 2 + 1); + + if (bottom_has_center_vert) { + edge_total += circle_segments; + } + + return edge_total; } -static int face_total(const GeometryNodeMeshCircleFillType fill_type, - const int verts_num, - const bool top_is_point, - const bool bottom_is_point) +int ConeConfig::calculate_total_corners() { if (top_is_point && bottom_is_point) { return 0; } - int face_total = 0; - if (!top_is_point) { - if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { - face_total++; - } - else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { - face_total += verts_num; - } + int corner_total = 0; + + if (top_has_center_vert) { + corner_total += (circle_segments * 3); + } + else if (!top_is_point && fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { + corner_total += circle_segments; } - face_total += verts_num; + corner_total += tot_quad_rings * (circle_segments * 4); - if (!bottom_is_point) { - if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { - face_total++; - } - else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { - face_total += verts_num; - } + if (bottom_has_center_vert) { + corner_total += (circle_segments * 3); + } + else if (!bottom_is_point && fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { + corner_total += circle_segments; } - return face_total; + return corner_total; } -static void calculate_uvs(Mesh *mesh, - const bool top_is_point, - const bool bottom_is_point, - const int verts_num, - const GeometryNodeMeshCircleFillType fill_type) +static void calculate_cone_vertices(const MutableSpan<MVert> &verts, const ConeConfig &config) { - MeshComponent mesh_component; - mesh_component.replace(mesh, GeometryOwnershipType::Editable); - OutputAttribute_Typed<float2> uv_attribute = - mesh_component.attribute_try_get_for_output_only<float2>("uv_map", ATTR_DOMAIN_CORNER); - MutableSpan<float2> uvs = uv_attribute.as_span(); - - Array<float2> circle(verts_num); + Array<float2> circle(config.circle_segments); + const float angle_delta = 2.0f * (M_PI / static_cast<float>(config.circle_segments)); float angle = 0.0f; - const float angle_delta = 2.0f * M_PI / static_cast<float>(verts_num); - for (const int i : IndexRange(verts_num)) { - circle[i].x = std::cos(angle) * 0.225f + 0.25f; - circle[i].y = std::sin(angle) * 0.225f + 0.25f; + for (const int i : IndexRange(config.circle_segments)) { + circle[i].x = std::cos(angle); + circle[i].y = std::sin(angle); angle += angle_delta; } - int loop_index = 0; - if (!top_is_point) { - if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { - for (const int i : IndexRange(verts_num)) { - uvs[loop_index++] = circle[i]; - } - } - else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { - for (const int i : IndexRange(verts_num)) { - uvs[loop_index++] = circle[i]; - uvs[loop_index++] = circle[(i + 1) % verts_num]; - uvs[loop_index++] = float2(0.25f, 0.25f); - } - } - } + int vert_index = 0; - /* Create side corners and faces. */ - if (!top_is_point && !bottom_is_point) { - const float bottom = (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NONE) ? 0.0f : 0.5f; - /* Quads connect the top and bottom. */ - for (const int i : IndexRange(verts_num)) { - const float vert = static_cast<float>(i); - uvs[loop_index++] = float2(vert / verts_num, bottom); - uvs[loop_index++] = float2(vert / verts_num, 1.0f); - uvs[loop_index++] = float2((vert + 1.0f) / verts_num, 1.0f); - uvs[loop_index++] = float2((vert + 1.0f) / verts_num, bottom); - } + /* Top cone tip or triangle fan center. */ + if (config.top_has_center_vert) { + copy_v3_fl3(verts[vert_index++].co, 0.0f, 0.0f, config.height); } - else { - /* Triangles connect the top and bottom section. */ - if (!top_is_point) { - for (const int i : IndexRange(verts_num)) { - uvs[loop_index++] = circle[i] + float2(0.5f, 0.0f); - uvs[loop_index++] = float2(0.75f, 0.25f); - uvs[loop_index++] = circle[(i + 1) % verts_num] + float2(0.5f, 0.0f); - } - } - else { - BLI_assert(!bottom_is_point); - for (const int i : IndexRange(verts_num)) { - uvs[loop_index++] = circle[i]; - uvs[loop_index++] = circle[(i + 1) % verts_num]; - uvs[loop_index++] = float2(0.25f, 0.25f); + + /* Top fill including the outer edge of the fill. */ + if (!config.top_is_point) { + const float top_fill_radius_delta = config.radius_top / + static_cast<float>(config.fill_segments); + for (const int i : IndexRange(config.fill_segments)) { + const float top_fill_radius = top_fill_radius_delta * (i + 1); + for (const int j : IndexRange(config.circle_segments)) { + const float x = circle[j].x * top_fill_radius; + const float y = circle[j].y * top_fill_radius; + copy_v3_fl3(verts[vert_index++].co, x, y, config.height); } } } - /* Create bottom corners and faces. */ - if (!bottom_is_point) { - if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { - for (const int i : IndexRange(verts_num)) { - /* Go backwards because of reversed face normal. */ - uvs[loop_index++] = circle[verts_num - 1 - i] + float2(0.5f, 0.0f); - } + /* Rings along the side. */ + const float side_radius_delta = (config.radius_bottom - config.radius_top) / + static_cast<float>(config.side_segments); + const float height_delta = 2.0f * config.height / static_cast<float>(config.side_segments); + for (const int i : IndexRange(config.side_segments - 1)) { + const float ring_radius = config.radius_top + (side_radius_delta * (i + 1)); + const float ring_height = config.height - (height_delta * (i + 1)); + for (const int j : IndexRange(config.circle_segments)) { + const float x = circle[j].x * ring_radius; + const float y = circle[j].y * ring_radius; + copy_v3_fl3(verts[vert_index++].co, x, y, ring_height); } - else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { - for (const int i : IndexRange(verts_num)) { - uvs[loop_index++] = circle[i] + float2(0.5f, 0.0f); - uvs[loop_index++] = float2(0.75f, 0.25f); - uvs[loop_index++] = circle[(i + 1) % verts_num] + float2(0.5f, 0.0f); + } + + /* Bottom fill including the outer edge of the fill. */ + if (!config.bottom_is_point) { + const float bottom_fill_radius_delta = config.radius_bottom / + static_cast<float>(config.fill_segments); + for (const int i : IndexRange(config.fill_segments)) { + const float bottom_fill_radius = config.radius_bottom - (i * bottom_fill_radius_delta); + for (const int j : IndexRange(config.circle_segments)) { + const float x = circle[j].x * bottom_fill_radius; + const float y = circle[j].y * bottom_fill_radius; + copy_v3_fl3(verts[vert_index++].co, x, y, -config.height); } } } - uv_attribute.save(); + /* Bottom cone tip or triangle fan center. */ + if (config.bottom_has_center_vert) { + copy_v3_fl3(verts[vert_index++].co, 0.0f, 0.0f, -config.height); + } } -Mesh *create_cylinder_or_cone_mesh(const float radius_top, - const float radius_bottom, - const float depth, - const int verts_num, - const GeometryNodeMeshCircleFillType fill_type) +static void calculate_cone_edges(const MutableSpan<MEdge> &edges, const ConeConfig &config) { - const bool top_is_point = radius_top == 0.0f; - const bool bottom_is_point = radius_bottom == 0.0f; - const float height = depth * 0.5f; - /* Handle the case of a line / single point before everything else to avoid - * the need to check for it later. */ - if (top_is_point && bottom_is_point) { - const bool single_vertex = height == 0.0f; - Mesh *mesh = BKE_mesh_new_nomain(single_vertex ? 1 : 2, single_vertex ? 0 : 1, 0, 0, 0); - copy_v3_v3(mesh->mvert[0].co, float3(0.0f, 0.0f, height)); - if (single_vertex) { - const short up[3] = {0, 0, SHRT_MAX}; - copy_v3_v3_short(mesh->mvert[0].no, up); - return mesh; - } - copy_v3_v3(mesh->mvert[1].co, float3(0.0f, 0.0f, -height)); - mesh->medge[0].v1 = 0; - mesh->medge[0].v2 = 1; - mesh->medge[0].flag |= ME_LOOSEEDGE; - BKE_mesh_normals_tag_dirty(mesh); - return mesh; - } - - Mesh *mesh = BKE_mesh_new_nomain( - vert_total(fill_type, verts_num, top_is_point, bottom_is_point), - edge_total(fill_type, verts_num, top_is_point, bottom_is_point), - 0, - corner_total(fill_type, verts_num, top_is_point, bottom_is_point), - face_total(fill_type, verts_num, top_is_point, bottom_is_point)); - BKE_id_material_eval_ensure_default_slot(&mesh->id); - MutableSpan<MVert> verts{mesh->mvert, mesh->totvert}; - MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop}; - MutableSpan<MEdge> edges{mesh->medge, mesh->totedge}; - MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly}; - - /* Calculate vertex positions. */ - const int top_verts_start = 0; - const int bottom_verts_start = top_verts_start + (!top_is_point ? verts_num : 1); - const float angle_delta = 2.0f * (M_PI / static_cast<float>(verts_num)); - for (const int i : IndexRange(verts_num)) { - const float angle = i * angle_delta; - const float x = std::cos(angle); - const float y = std::sin(angle); - if (!top_is_point) { - copy_v3_v3(verts[top_verts_start + i].co, float3(x * radius_top, y * radius_top, height)); - } - if (!bottom_is_point) { - copy_v3_v3(verts[bottom_verts_start + i].co, - float3(x * radius_bottom, y * radius_bottom, -height)); + int edge_index = 0; + + /* Edges for top cone tip or triangle fan */ + if (config.top_has_center_vert) { + for (const int i : IndexRange(config.circle_segments)) { + MEdge &edge = edges[edge_index++]; + edge.v1 = config.first_vert; + edge.v2 = config.first_ring_verts_start + i; + edge.flag = ME_EDGEDRAW | ME_EDGERENDER; } } - if (top_is_point) { - copy_v3_v3(verts[top_verts_start].co, float3(0.0f, 0.0f, height)); - } - if (bottom_is_point) { - copy_v3_v3(verts[bottom_verts_start].co, float3(0.0f, 0.0f, -height)); - } - /* Add center vertices for the triangle fans at the end. */ - const int top_center_vert_index = bottom_verts_start + (bottom_is_point ? 1 : verts_num); - const int bottom_center_vert_index = top_center_vert_index + (top_is_point ? 0 : 1); - if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { - if (!top_is_point) { - copy_v3_v3(verts[top_center_vert_index].co, float3(0.0f, 0.0f, height)); + /* Rings and connecting edges between the rings. */ + for (const int i : IndexRange(config.tot_edge_rings)) { + const int this_ring_vert_start = config.first_ring_verts_start + (i * config.circle_segments); + const int next_ring_vert_start = this_ring_vert_start + config.circle_segments; + /* Edge rings. */ + for (const int j : IndexRange(config.circle_segments)) { + MEdge &edge = edges[edge_index++]; + edge.v1 = this_ring_vert_start + j; + edge.v2 = this_ring_vert_start + ((j + 1) % config.circle_segments); + edge.flag = ME_EDGEDRAW | ME_EDGERENDER; } - if (!bottom_is_point) { - copy_v3_v3(verts[bottom_center_vert_index].co, float3(0.0f, 0.0f, -height)); + if (i == config.tot_edge_rings - 1) { + /* There is one fewer ring of connecting edges. */ + break; } - } - - /* Create top edges. */ - const int top_edges_start = 0; - const int top_fan_edges_start = (!top_is_point && - fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) ? - top_edges_start + verts_num : - top_edges_start; - if (!top_is_point) { - for (const int i : IndexRange(verts_num)) { - MEdge &edge = edges[top_edges_start + i]; - edge.v1 = top_verts_start + i; - edge.v2 = top_verts_start + (i + 1) % verts_num; + /* Connecting edges. */ + for (const int j : IndexRange(config.circle_segments)) { + MEdge &edge = edges[edge_index++]; + edge.v1 = this_ring_vert_start + j; + edge.v2 = next_ring_vert_start + j; edge.flag = ME_EDGEDRAW | ME_EDGERENDER; } - if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { - for (const int i : IndexRange(verts_num)) { - MEdge &edge = edges[top_fan_edges_start + i]; - edge.v1 = top_center_vert_index; - edge.v2 = top_verts_start + i; - edge.flag = ME_EDGEDRAW | ME_EDGERENDER; - } - } - } - - /* Create connecting edges. */ - const int connecting_edges_start = top_fan_edges_start + (!top_is_point ? verts_num : 0); - for (const int i : IndexRange(verts_num)) { - MEdge &edge = edges[connecting_edges_start + i]; - edge.v1 = top_verts_start + (!top_is_point ? i : 0); - edge.v2 = bottom_verts_start + (!bottom_is_point ? i : 0); - edge.flag = ME_EDGEDRAW | ME_EDGERENDER; } - /* Create bottom edges. */ - const int bottom_edges_start = connecting_edges_start + verts_num; - const int bottom_fan_edges_start = (!bottom_is_point && - fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) ? - bottom_edges_start + verts_num : - bottom_edges_start; - if (!bottom_is_point) { - for (const int i : IndexRange(verts_num)) { - MEdge &edge = edges[bottom_edges_start + i]; - edge.v1 = bottom_verts_start + i; - edge.v2 = bottom_verts_start + (i + 1) % verts_num; + /* Edges for bottom triangle fan or tip. */ + if (config.bottom_has_center_vert) { + for (const int i : IndexRange(config.circle_segments)) { + MEdge &edge = edges[edge_index++]; + edge.v1 = config.last_ring_verts_start + i; + edge.v2 = config.last_vert; edge.flag = ME_EDGEDRAW | ME_EDGERENDER; } - if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { - for (const int i : IndexRange(verts_num)) { - MEdge &edge = edges[bottom_fan_edges_start + i]; - edge.v1 = bottom_center_vert_index; - edge.v2 = bottom_verts_start + i; - edge.flag = ME_EDGEDRAW | ME_EDGERENDER; - } - } } +} - /* Create top corners and faces. */ +static void calculate_cone_faces(const MutableSpan<MLoop> &loops, + const MutableSpan<MPoly> &polys, + const ConeConfig &config) +{ int loop_index = 0; int poly_index = 0; - if (!top_is_point) { - if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { + + if (config.top_has_center_vert) { + /* Top cone tip or center triangle fan in the fill. */ + const int top_center_vert = 0; + const int top_fan_edges_start = 0; + + for (const int i : IndexRange(config.circle_segments)) { MPoly &poly = polys[poly_index++]; poly.loopstart = loop_index; - poly.totloop = verts_num; + poly.totloop = 3; - for (const int i : IndexRange(verts_num)) { - MLoop &loop = loops[loop_index++]; - loop.v = top_verts_start + i; - loop.e = top_edges_start + i; - } + MLoop &loop_a = loops[loop_index++]; + loop_a.v = config.first_ring_verts_start + i; + loop_a.e = config.first_ring_edges_start + i; + MLoop &loop_b = loops[loop_index++]; + loop_b.v = config.first_ring_verts_start + ((i + 1) % config.circle_segments); + loop_b.e = top_fan_edges_start + ((i + 1) % config.circle_segments); + MLoop &loop_c = loops[loop_index++]; + loop_c.v = top_center_vert; + loop_c.e = top_fan_edges_start + i; + } + } + else if (config.fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { + /* Center n-gon in the fill. */ + MPoly &poly = polys[poly_index++]; + poly.loopstart = loop_index; + poly.totloop = config.circle_segments; + for (const int i : IndexRange(config.circle_segments)) { + MLoop &loop = loops[loop_index++]; + loop.v = i; + loop.e = i; } - else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { - for (const int i : IndexRange(verts_num)) { + } + + /* Quads connect one edge ring to the next one. */ + if (config.tot_quad_rings > 0) { + for (const int i : IndexRange(config.tot_quad_rings)) { + const int this_ring_vert_start = config.first_ring_verts_start + + (i * config.circle_segments); + const int next_ring_vert_start = this_ring_vert_start + config.circle_segments; + + const int this_ring_edges_start = config.first_ring_edges_start + + (i * 2 * config.circle_segments); + const int next_ring_edges_start = this_ring_edges_start + (2 * config.circle_segments); + const int ring_connections_start = this_ring_edges_start + config.circle_segments; + + for (const int j : IndexRange(config.circle_segments)) { MPoly &poly = polys[poly_index++]; poly.loopstart = loop_index; - poly.totloop = 3; + poly.totloop = 4; MLoop &loop_a = loops[loop_index++]; - loop_a.v = top_verts_start + i; - loop_a.e = top_edges_start + i; + loop_a.v = this_ring_vert_start + j; + loop_a.e = ring_connections_start + j; MLoop &loop_b = loops[loop_index++]; - loop_b.v = top_verts_start + (i + 1) % verts_num; - loop_b.e = top_fan_edges_start + (i + 1) % verts_num; + loop_b.v = next_ring_vert_start + j; + loop_b.e = next_ring_edges_start + j; MLoop &loop_c = loops[loop_index++]; - loop_c.v = top_center_vert_index; - loop_c.e = top_fan_edges_start + i; + loop_c.v = next_ring_vert_start + ((j + 1) % config.circle_segments); + loop_c.e = ring_connections_start + ((j + 1) % config.circle_segments); + MLoop &loop_d = loops[loop_index++]; + loop_d.v = this_ring_vert_start + ((j + 1) % config.circle_segments); + loop_d.e = this_ring_edges_start + j; } } } - /* Create side corners and faces. */ - if (!top_is_point && !bottom_is_point) { - /* Quads connect the top and bottom. */ - for (const int i : IndexRange(verts_num)) { + if (config.bottom_has_center_vert) { + /* Bottom cone tip or center triangle fan in the fill. */ + for (const int i : IndexRange(config.circle_segments)) { MPoly &poly = polys[poly_index++]; poly.loopstart = loop_index; - poly.totloop = 4; + poly.totloop = 3; MLoop &loop_a = loops[loop_index++]; - loop_a.v = top_verts_start + i; - loop_a.e = connecting_edges_start + i; + loop_a.v = config.last_ring_verts_start + i; + loop_a.e = config.last_fan_edges_start + i; MLoop &loop_b = loops[loop_index++]; - loop_b.v = bottom_verts_start + i; - loop_b.e = bottom_edges_start + i; + loop_b.v = config.last_vert; + loop_b.e = config.last_fan_edges_start + (i + 1) % config.circle_segments; MLoop &loop_c = loops[loop_index++]; - loop_c.v = bottom_verts_start + (i + 1) % verts_num; - loop_c.e = connecting_edges_start + (i + 1) % verts_num; - MLoop &loop_d = loops[loop_index++]; - loop_d.v = top_verts_start + (i + 1) % verts_num; - loop_d.e = top_edges_start + i; + loop_c.v = config.last_ring_verts_start + (i + 1) % config.circle_segments; + loop_c.e = config.last_ring_edges_start + i; } } - else { - /* Triangles connect the top and bottom section. */ - if (!top_is_point) { - for (const int i : IndexRange(verts_num)) { - MPoly &poly = polys[poly_index++]; - poly.loopstart = loop_index; - poly.totloop = 3; + else if (config.fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { + /* Center n-gon in the fill. */ + MPoly &poly = polys[poly_index++]; + poly.loopstart = loop_index; + poly.totloop = config.circle_segments; - MLoop &loop_a = loops[loop_index++]; - loop_a.v = top_verts_start + i; - loop_a.e = connecting_edges_start + i; - MLoop &loop_b = loops[loop_index++]; - loop_b.v = bottom_verts_start; - loop_b.e = connecting_edges_start + (i + 1) % verts_num; - MLoop &loop_c = loops[loop_index++]; - loop_c.v = top_verts_start + (i + 1) % verts_num; - loop_c.e = top_edges_start + i; - } + for (const int i : IndexRange(config.circle_segments)) { + /* Go backwards to reverse surface normal. */ + MLoop &loop = loops[loop_index++]; + loop.v = config.last_vert - i; + loop.e = config.last_edge - ((i + 1) % config.circle_segments); + } + } +} + +static void calculate_selection_outputs(Mesh *mesh, + const ConeConfig &config, + ConeAttributeOutputs &attribute_outputs) +{ + MeshComponent mesh_component; + mesh_component.replace(mesh, GeometryOwnershipType::Editable); + + /* Populate "Top" selection output. */ + if (attribute_outputs.top_id) { + const bool face = !config.top_is_point && config.fill_type != GEO_NODE_MESH_CIRCLE_FILL_NONE; + OutputAttribute_Typed<bool> attribute = mesh_component.attribute_try_get_for_output_only<bool>( + attribute_outputs.top_id.get(), face ? ATTR_DOMAIN_FACE : ATTR_DOMAIN_POINT); + MutableSpan<bool> selection = attribute.as_span(); + + if (config.top_is_point) { + selection[config.first_vert] = true; } else { - BLI_assert(!bottom_is_point); - for (const int i : IndexRange(verts_num)) { - MPoly &poly = polys[poly_index++]; - poly.loopstart = loop_index; - poly.totloop = 3; + selection.slice(0, face ? config.top_faces_len : config.circle_segments).fill(true); + } + attribute.save(); + } - MLoop &loop_a = loops[loop_index++]; - loop_a.v = bottom_verts_start + i; - loop_a.e = bottom_edges_start + i; - MLoop &loop_b = loops[loop_index++]; - loop_b.v = bottom_verts_start + (i + 1) % verts_num; - loop_b.e = connecting_edges_start + (i + 1) % verts_num; - MLoop &loop_c = loops[loop_index++]; - loop_c.v = top_verts_start; - loop_c.e = connecting_edges_start + i; + /* Populate "Bottom" selection output. */ + if (attribute_outputs.bottom_id) { + const bool face = !config.bottom_is_point && + config.fill_type != GEO_NODE_MESH_CIRCLE_FILL_NONE; + OutputAttribute_Typed<bool> attribute = mesh_component.attribute_try_get_for_output_only<bool>( + attribute_outputs.bottom_id.get(), face ? ATTR_DOMAIN_FACE : ATTR_DOMAIN_POINT); + MutableSpan<bool> selection = attribute.as_span(); + + if (config.bottom_is_point) { + selection[config.last_vert] = true; + } + else { + selection + .slice(config.bottom_faces_start, + face ? config.bottom_faces_len : config.circle_segments) + .fill(true); + } + attribute.save(); + } + + /* Populate "Side" selection output. */ + if (attribute_outputs.side_id) { + OutputAttribute_Typed<bool> attribute = mesh_component.attribute_try_get_for_output_only<bool>( + attribute_outputs.side_id.get(), ATTR_DOMAIN_FACE); + MutableSpan<bool> selection = attribute.as_span(); + + selection.slice(config.side_faces_start, config.side_faces_len).fill(true); + attribute.save(); + } +} + +/** + * If the top is the cone tip or has a fill, it is unwrapped into a circle in the + * lower left quadrant of the UV. + * Likewise, if the bottom is the cone tip or has a fill, it is unwrapped into a circle + * in the lower right quadrant of the UV. + * If the mesh is a truncated cone or a cylinder, the side faces are unwrapped into + * a rectangle that fills the top half of the UV (or the entire UV, if there are no fills). + */ +static void calculate_cone_uvs(Mesh *mesh, const ConeConfig &config) +{ + MeshComponent mesh_component; + mesh_component.replace(mesh, GeometryOwnershipType::Editable); + OutputAttribute_Typed<float2> uv_attribute = + mesh_component.attribute_try_get_for_output_only<float2>("uv_map", ATTR_DOMAIN_CORNER); + MutableSpan<float2> uvs = uv_attribute.as_span(); + + Array<float2> circle(config.circle_segments); + float angle = 0.0f; + const float angle_delta = 2.0f * M_PI / static_cast<float>(config.circle_segments); + for (const int i : IndexRange(config.circle_segments)) { + circle[i].x = std::cos(angle) * 0.225f; + circle[i].y = std::sin(angle) * 0.225f; + angle += angle_delta; + } + + int loop_index = 0; + + /* Left circle of the UV representing the top fill or top cone tip. */ + if (config.top_is_point || config.fill_type != GEO_NODE_MESH_CIRCLE_FILL_NONE) { + const float2 center_left(0.25f, 0.25f); + const float radius_factor_delta = 1.0f / (config.top_is_point ? + static_cast<float>(config.side_segments) : + static_cast<float>(config.fill_segments)); + const int left_circle_segment_count = config.top_is_point ? config.side_segments : + config.fill_segments; + + if (config.top_has_center_vert) { + /* Cone tip itself or triangle fan center of the fill. */ + for (const int i : IndexRange(config.circle_segments)) { + uvs[loop_index++] = radius_factor_delta * circle[i] + center_left; + uvs[loop_index++] = radius_factor_delta * circle[(i + 1) % config.circle_segments] + + center_left; + uvs[loop_index++] = center_left; + } + } + else if (!config.top_is_point && config.fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { + /* N-gon at the center of the fill. */ + for (const int i : IndexRange(config.circle_segments)) { + uvs[loop_index++] = radius_factor_delta * circle[i] + center_left; + } + } + /* The rest of the top fill is made out of quad rings. */ + for (const int i : IndexRange(1, left_circle_segment_count - 1)) { + const float inner_radius_factor = i * radius_factor_delta; + const float outer_radius_factor = (i + 1) * radius_factor_delta; + for (const int j : IndexRange(config.circle_segments)) { + uvs[loop_index++] = inner_radius_factor * circle[j] + center_left; + uvs[loop_index++] = outer_radius_factor * circle[j] + center_left; + uvs[loop_index++] = outer_radius_factor * circle[(j + 1) % config.circle_segments] + + center_left; + uvs[loop_index++] = inner_radius_factor * circle[(j + 1) % config.circle_segments] + + center_left; } } } - /* Create bottom corners and faces. */ - if (!bottom_is_point) { - if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { - MPoly &poly = polys[poly_index++]; - poly.loopstart = loop_index; - poly.totloop = verts_num; + if (!config.top_is_point && !config.bottom_is_point) { + /* Mesh is a truncated cone or cylinder. The sides are unwrapped into a rectangle. */ + const float bottom = (config.fill_type == GEO_NODE_MESH_CIRCLE_FILL_NONE) ? 0.0f : 0.5f; + const float x_delta = 1.0f / static_cast<float>(config.circle_segments); + const float y_delta = (1.0f - bottom) / static_cast<float>(config.side_segments); - for (const int i : IndexRange(verts_num)) { - /* Go backwards to reverse surface normal. */ - MLoop &loop = loops[loop_index++]; - loop.v = bottom_verts_start + verts_num - 1 - i; - loop.e = bottom_edges_start + verts_num - 1 - (i + 1) % verts_num; + for (const int i : IndexRange(config.side_segments)) { + for (const int j : IndexRange(config.circle_segments)) { + uvs[loop_index++] = float2(j * x_delta, i * y_delta + bottom); + uvs[loop_index++] = float2(j * x_delta, (i + 1) * y_delta + bottom); + uvs[loop_index++] = float2((j + 1) * x_delta, (i + 1) * y_delta + bottom); + uvs[loop_index++] = float2((j + 1) * x_delta, i * y_delta + bottom); } } - else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { - for (const int i : IndexRange(verts_num)) { - MPoly &poly = polys[poly_index++]; - poly.loopstart = loop_index; - poly.totloop = 3; + } - MLoop &loop_a = loops[loop_index++]; - loop_a.v = bottom_verts_start + i; - loop_a.e = bottom_fan_edges_start + i; - MLoop &loop_b = loops[loop_index++]; - loop_b.v = bottom_center_vert_index; - loop_b.e = bottom_fan_edges_start + (i + 1) % verts_num; - MLoop &loop_c = loops[loop_index++]; - loop_c.v = bottom_verts_start + (i + 1) % verts_num; - loop_c.e = bottom_edges_start + i; + /* Right circle of the UV representing the bottom fill or bottom cone tip. */ + if (config.bottom_is_point || config.fill_type != GEO_NODE_MESH_CIRCLE_FILL_NONE) { + const float2 center_right(0.75f, 0.25f); + const float radius_factor_delta = 1.0f / (config.bottom_is_point ? + static_cast<float>(config.side_segments) : + static_cast<float>(config.fill_segments)); + const int right_circle_segment_count = config.bottom_is_point ? config.side_segments : + config.fill_segments; + + /* The bottom circle has to be created outside in to match the loop order. */ + for (const int i : IndexRange(right_circle_segment_count - 1)) { + const float outer_radius_factor = 1.0f - i * radius_factor_delta; + const float inner_radius_factor = 1.0f - (i + 1) * radius_factor_delta; + for (const int j : IndexRange(config.circle_segments)) { + uvs[loop_index++] = outer_radius_factor * circle[j] + center_right; + uvs[loop_index++] = inner_radius_factor * circle[j] + center_right; + uvs[loop_index++] = inner_radius_factor * circle[(j + 1) % config.circle_segments] + + center_right; + uvs[loop_index++] = outer_radius_factor * circle[(j + 1) % config.circle_segments] + + center_right; + } + } + + if (config.bottom_has_center_vert) { + /* Cone tip itself or triangle fan center of the fill. */ + for (const int i : IndexRange(config.circle_segments)) { + uvs[loop_index++] = radius_factor_delta * circle[i] + center_right; + uvs[loop_index++] = center_right; + uvs[loop_index++] = radius_factor_delta * circle[(i + 1) % config.circle_segments] + + center_right; + } + } + else if (!config.bottom_is_point && config.fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { + /* N-gon at the center of the fill. */ + for (const int i : IndexRange(config.circle_segments)) { + /* Go backwards because of reversed face normal. */ + uvs[loop_index++] = radius_factor_delta * circle[config.circle_segments - 1 - i] + + center_right; } } } - BKE_mesh_normals_tag_dirty(mesh); + uv_attribute.save(); +} + +static Mesh *create_vertex_mesh() +{ + /* Returns a mesh with a single vertex at the origin. */ + Mesh *mesh = BKE_mesh_new_nomain(1, 0, 0, 0, 0); + copy_v3_fl3(mesh->mvert[0].co, 0.0f, 0.0f, 0.0f); + return mesh; +} - calculate_uvs(mesh, top_is_point, bottom_is_point, verts_num, fill_type); +Mesh *create_cylinder_or_cone_mesh(const float radius_top, + const float radius_bottom, + const float depth, + const int circle_segments, + const int side_segments, + const int fill_segments, + const GeometryNodeMeshCircleFillType fill_type, + ConeAttributeOutputs &attribute_outputs) +{ + const ConeConfig config( + radius_top, radius_bottom, depth, circle_segments, side_segments, fill_segments, fill_type); + + /* Handle the case of a line / single point before everything else to avoid + * the need to check for it later. */ + if (config.top_is_point && config.bottom_is_point) { + if (config.height == 0.0f) { + return create_vertex_mesh(); + } + const float z_delta = -2.0f * config.height / static_cast<float>(config.side_segments); + const float3 start(0.0f, 0.0f, config.height); + const float3 delta(0.0f, 0.0f, z_delta); + return create_line_mesh(start, delta, config.tot_verts); + } + + Mesh *mesh = BKE_mesh_new_nomain( + config.tot_verts, config.tot_edges, 0, config.tot_corners, config.tot_faces); + BKE_id_material_eval_ensure_default_slot(&mesh->id); + + MutableSpan<MVert> verts{mesh->mvert, mesh->totvert}; + MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop}; + MutableSpan<MEdge> edges{mesh->medge, mesh->totedge}; + MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly}; + + calculate_cone_vertices(verts, config); + calculate_cone_edges(edges, config); + calculate_cone_faces(loops, polys, config); + calculate_cone_uvs(mesh, config); + calculate_selection_outputs(mesh, config, attribute_outputs); return mesh; } -static void geo_node_mesh_primitive_cone_exec(GeoNodeExecParams params) +} // namespace blender::nodes + +namespace blender::nodes::node_geo_mesh_primitive_cone_cc { + +NODE_STORAGE_FUNCS(NodeGeometryMeshCone) + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Int>(N_("Vertices")) + .default_value(32) + .min(3) + .max(512) + .description(N_("Number of points on the circle at the top and bottom")); + b.add_input<decl::Int>(N_("Side Segments")) + .default_value(1) + .min(1) + .max(512) + .description(N_("The number of edges running vertically along the side of the cone")); + b.add_input<decl::Int>(N_("Fill Segments")) + .default_value(1) + .min(1) + .max(512) + .description(N_("Number of concentric rings used to fill the round face")); + b.add_input<decl::Float>(N_("Radius Top")) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description(N_("Radius of the top circle of the cone")); + b.add_input<decl::Float>(N_("Radius Bottom")) + .default_value(1.0f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description(N_("Radius of the bottom circle of the cone")); + b.add_input<decl::Float>(N_("Depth")) + .default_value(2.0f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description(N_("Height of the generated cone")); + b.add_output<decl::Geometry>(N_("Mesh")); + b.add_output<decl::Bool>(N_("Top")).field_source(); + b.add_output<decl::Bool>(N_("Bottom")).field_source(); + b.add_output<decl::Bool>(N_("Side")).field_source(); +} + +static void node_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryMeshCone *node_storage = MEM_cnew<NodeGeometryMeshCone>(__func__); + + node_storage->fill_type = GEO_NODE_MESH_CIRCLE_FILL_NGON; + + node->storage = node_storage; +} + +static void node_update(bNodeTree *ntree, bNode *node) { - const bNode &node = params.node(); - const NodeGeometryMeshCone &storage = *(const NodeGeometryMeshCone *)node.storage; + bNodeSocket *vertices_socket = (bNodeSocket *)node->inputs.first; + bNodeSocket *rings_socket = vertices_socket->next; + bNodeSocket *fill_subdiv_socket = rings_socket->next; + + const NodeGeometryMeshCone &storage = node_storage(*node); + const GeometryNodeMeshCircleFillType fill = (GeometryNodeMeshCircleFillType)storage.fill_type; + const bool has_fill = fill != GEO_NODE_MESH_CIRCLE_FILL_NONE; + nodeSetSocketAvailability(ntree, fill_subdiv_socket, has_fill); +} - const GeometryNodeMeshCircleFillType fill_type = (const GeometryNodeMeshCircleFillType) - storage.fill_type; +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + uiItemR(layout, ptr, "fill_type", 0, nullptr, ICON_NONE); +} - const int verts_num = params.extract_input<int>("Vertices"); - if (verts_num < 3) { +static void node_geo_exec(GeoNodeExecParams params) +{ + const NodeGeometryMeshCone &storage = node_storage(params.node()); + const GeometryNodeMeshCircleFillType fill = (GeometryNodeMeshCircleFillType)storage.fill_type; + + const int circle_segments = params.extract_input<int>("Vertices"); + if (circle_segments < 3) { params.error_message_add(NodeWarningType::Info, TIP_("Vertices must be at least 3")); - params.set_output("Geometry", GeometrySet()); + params.set_default_remaining_outputs(); + return; + } + + const int side_segments = params.extract_input<int>("Side Segments"); + if (side_segments < 1) { + params.error_message_add(NodeWarningType::Info, TIP_("Side Segments must be at least 1")); + params.set_default_remaining_outputs(); + return; + } + + const bool no_fill = fill == GEO_NODE_MESH_CIRCLE_FILL_NONE; + const int fill_segments = no_fill ? 1 : params.extract_input<int>("Fill Segments"); + if (fill_segments < 1) { + params.error_message_add(NodeWarningType::Info, TIP_("Fill Segments must be at least 1")); + params.set_default_remaining_outputs(); return; } @@ -555,27 +823,64 @@ static void geo_node_mesh_primitive_cone_exec(GeoNodeExecParams params) const float radius_bottom = params.extract_input<float>("Radius Bottom"); const float depth = params.extract_input<float>("Depth"); - Mesh *mesh = create_cylinder_or_cone_mesh( - radius_top, radius_bottom, depth, verts_num, fill_type); + ConeAttributeOutputs attribute_outputs; + if (params.output_is_required("Top")) { + attribute_outputs.top_id = StrongAnonymousAttributeID("top_selection"); + } + if (params.output_is_required("Bottom")) { + attribute_outputs.bottom_id = StrongAnonymousAttributeID("bottom_selection"); + } + if (params.output_is_required("Side")) { + attribute_outputs.side_id = StrongAnonymousAttributeID("side_selection"); + } + + Mesh *mesh = create_cylinder_or_cone_mesh(radius_top, + radius_bottom, + depth, + circle_segments, + side_segments, + fill_segments, + fill, + attribute_outputs); /* Transform the mesh so that the base of the cone is at the origin. */ BKE_mesh_translate(mesh, float3(0.0f, 0.0f, depth * 0.5f), false); - params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); + if (attribute_outputs.top_id) { + params.set_output("Top", + AnonymousAttributeFieldInput::Create<bool>( + std::move(attribute_outputs.top_id), params.attribute_producer_name())); + } + if (attribute_outputs.bottom_id) { + params.set_output( + "Bottom", + AnonymousAttributeFieldInput::Create<bool>(std::move(attribute_outputs.bottom_id), + params.attribute_producer_name())); + } + if (attribute_outputs.side_id) { + params.set_output("Side", + AnonymousAttributeFieldInput::Create<bool>( + std::move(attribute_outputs.side_id), params.attribute_producer_name())); + } + + params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_mesh_primitive_cone_cc void register_node_type_geo_mesh_primitive_cone() { + namespace file_ns = blender::nodes::node_geo_mesh_primitive_cone_cc; + static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_CONE, "Cone", NODE_CLASS_GEOMETRY, 0); - node_type_init(&ntype, blender::nodes::geo_node_mesh_primitive_cone_init); + geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_CONE, "Cone", NODE_CLASS_GEOMETRY); + node_type_init(&ntype, file_ns::node_init); + node_type_update(&ntype, file_ns::node_update); node_type_storage( &ntype, "NodeGeometryMeshCone", node_free_standard_storage, node_copy_standard_storage); - ntype.geometry_node_execute = blender::nodes::geo_node_mesh_primitive_cone_exec; - ntype.draw_buttons = blender::nodes::geo_node_mesh_primitive_cone_layout; - ntype.declare = blender::nodes::geo_node_mesh_primitive_cone_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.draw_buttons = file_ns::node_layout; + ntype.declare = file_ns::node_declare; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc index af8ce02b3c1..5b67258a947 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc @@ -24,15 +24,6 @@ namespace blender::nodes { -static void geo_node_mesh_primitive_cube_declare(NodeDeclarationBuilder &b) -{ - b.add_input<decl::Vector>("Size").default_value(float3(1)).min(0.0f).subtype(PROP_TRANSLATION); - b.add_input<decl::Int>("Vertices X").default_value(2).min(2).max(1000); - b.add_input<decl::Int>("Vertices Y").default_value(2).min(2).max(1000); - b.add_input<decl::Int>("Vertices Z").default_value(2).min(2).max(1000); - b.add_output<decl::Geometry>("Geometry"); -} - struct CuboidConfig { float3 size; int verts_x; @@ -86,23 +77,37 @@ static void calculate_vertices(const CuboidConfig &config, MutableSpan<MVert> ve int vert_index = 0; - /* Though looping over all possible coordinates inside the cube only to skip them may be slow, - * the alternative is similar complexity to below in the poly index calculation. If this loop - * becomes a problem in the future it could be optimized, though only after proper performance - * testing. */ for (const int z : IndexRange(config.verts_z)) { - for (const int y : IndexRange(config.verts_y)) { - for (const int x : IndexRange(config.verts_x)) { - /* Only plot vertices on the surface of the cuboid. */ - if (ELEM(z, 0, config.edges_z) || ELEM(x, 0, config.edges_x) || - ELEM(y, 0, config.edges_y)) { - + if (ELEM(z, 0, config.edges_z)) { + /* Fill bottom and top. */ + const float z_pos = z_bottom + z_delta * z; + for (const int y : IndexRange(config.verts_y)) { + const float y_pos = y_front + y_delta * y; + for (const int x : IndexRange(config.verts_x)) { const float x_pos = x_left + x_delta * x; + copy_v3_v3(verts[vert_index++].co, float3(x_pos, y_pos, z_pos)); + } + } + } + else { + for (const int y : IndexRange(config.verts_y)) { + if (ELEM(y, 0, config.edges_y)) { + /* Fill y-sides. */ const float y_pos = y_front + y_delta * y; const float z_pos = z_bottom + z_delta * z; - copy_v3_v3(verts[vert_index].co, float3(x_pos, y_pos, z_pos)); - - vert_index++; + for (const int x : IndexRange(config.verts_x)) { + const float x_pos = x_left + x_delta * x; + copy_v3_v3(verts[vert_index++].co, float3(x_pos, y_pos, z_pos)); + } + } + else { + /* Fill x-sides. */ + const float x_pos = x_left; + const float y_pos = y_front + y_delta * y; + const float z_pos = z_bottom + z_delta * z; + copy_v3_v3(verts[vert_index++].co, float3(x_pos, y_pos, z_pos)); + const float x_pos2 = x_left + x_delta * config.edges_x; + copy_v3_v3(verts[vert_index++].co, float3(x_pos2, y_pos, z_pos)); } } } @@ -150,7 +155,7 @@ static void calculate_polys(const CuboidConfig &config, /* Calculate polys for Bottom faces. */ int vert_1_start = 0; - for (const int UNUSED(y) : IndexRange(config.edges_y)) { + for ([[maybe_unused]] const int y : IndexRange(config.edges_y)) { for (const int x : IndexRange(config.edges_x)) { const int vert_1 = vert_1_start + x; const int vert_2 = vert_1_start + config.verts_x + x; @@ -168,7 +173,7 @@ static void calculate_polys(const CuboidConfig &config, vert_1_start = 0; int vert_2_start = config.verts_x * config.verts_y; - for (const int UNUSED(z) : IndexRange(config.edges_z)) { + for ([[maybe_unused]] const int z : IndexRange(config.edges_z)) { for (const int x : IndexRange(config.edges_x)) { define_quad(polys, loops, @@ -191,7 +196,7 @@ static void calculate_polys(const CuboidConfig &config, (config.verts_x - 2) * (config.verts_y - 2)); vert_2_start = vert_1_start + config.verts_x; - for (const int UNUSED(y) : IndexRange(config.edges_y)) { + for ([[maybe_unused]] const int y : IndexRange(config.edges_y)) { for (const int x : IndexRange(config.edges_x)) { define_quad(polys, loops, @@ -423,6 +428,35 @@ Mesh *create_cuboid_mesh(const float3 size, return mesh; } +} // namespace blender::nodes + +namespace blender::nodes::node_geo_mesh_primitive_cube_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Vector>(N_("Size")) + .default_value(float3(1)) + .min(0.0f) + .subtype(PROP_TRANSLATION) + .description(N_("Side length along each axis")); + b.add_input<decl::Int>(N_("Vertices X")) + .default_value(2) + .min(2) + .max(1000) + .description(N_("Number of vertices for the X side of the shape")); + b.add_input<decl::Int>(N_("Vertices Y")) + .default_value(2) + .min(2) + .max(1000) + .description(N_("Number of vertices for the Y side of the shape")); + b.add_input<decl::Int>(N_("Vertices Z")) + .default_value(2) + .min(2) + .max(1000) + .description(N_("Number of vertices for the Z side of the shape")); + b.add_output<decl::Geometry>(N_("Mesh")); +} + static Mesh *create_cube_mesh(const float3 size, const int verts_x, const int verts_y, @@ -456,19 +490,19 @@ static Mesh *create_cube_mesh(const float3 size, } if (verts_y == 1) { /* XZ plane. */ Mesh *mesh = create_grid_mesh(verts_x, verts_z, size.x, size.z); - transform_mesh(mesh, float3(0), float3(M_PI_2, 0.0f, 0.0f), float3(1)); + transform_mesh(*mesh, float3(0), float3(M_PI_2, 0.0f, 0.0f), float3(1)); return mesh; } /* YZ plane. */ Mesh *mesh = create_grid_mesh(verts_z, verts_y, size.z, size.y); - transform_mesh(mesh, float3(0), float3(0.0f, M_PI_2, 0.0f), float3(1)); + transform_mesh(*mesh, float3(0), float3(0.0f, M_PI_2, 0.0f), float3(1)); return mesh; } return create_cuboid_mesh(size, verts_x, verts_y, verts_z); } -static void geo_node_mesh_primitive_cube_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { const float3 size = params.extract_input<float3>("Size"); const int verts_x = params.extract_input<int>("Vertices X"); @@ -476,23 +510,25 @@ static void geo_node_mesh_primitive_cube_exec(GeoNodeExecParams params) const int verts_z = params.extract_input<int>("Vertices Z"); if (verts_x < 1 || verts_y < 1 || verts_z < 1) { params.error_message_add(NodeWarningType::Info, TIP_("Vertices must be at least 1")); - params.set_output("Geometry", GeometrySet()); + params.set_default_remaining_outputs(); return; } Mesh *mesh = create_cube_mesh(size, verts_x, verts_y, verts_z); - params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); + params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_mesh_primitive_cube_cc void register_node_type_geo_mesh_primitive_cube() { + namespace file_ns = blender::nodes::node_geo_mesh_primitive_cube_cc; + static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_CUBE, "Cube", NODE_CLASS_GEOMETRY, 0); - ntype.declare = blender::nodes::geo_node_mesh_primitive_cube_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_mesh_primitive_cube_exec; + geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_CUBE, "Cube", NODE_CLASS_GEOMETRY); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc index 8c4defc3ca3..73f21cf31fa 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc @@ -25,81 +25,155 @@ #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_mesh_primitive_cylinder_cc { -static void geo_node_mesh_primitive_cylinder_declare(NodeDeclarationBuilder &b) +NODE_STORAGE_FUNCS(NodeGeometryMeshCylinder) + +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Int>("Vertices") + b.add_input<decl::Int>(N_("Vertices")) .default_value(32) .min(3) - .max(4096) - .description("The number of vertices around the circumference"); - b.add_input<decl::Float>("Radius") + .max(512) + .description(N_("The number of vertices on the top and bottom circles")); + b.add_input<decl::Int>(N_("Side Segments")) + .default_value(1) + .min(1) + .max(512) + .description(N_("The number of rectangular segments along each side")); + b.add_input<decl::Int>(N_("Fill Segments")) + .default_value(1) + .min(1) + .max(512) + .description(N_("The number of concentric rings used to fill the round faces")); + b.add_input<decl::Float>(N_("Radius")) .default_value(1.0f) .min(0.0f) .subtype(PROP_DISTANCE) - .description("The radius of the cylinder"); - b.add_input<decl::Float>("Depth") + .description(N_("The radius of the cylinder")); + b.add_input<decl::Float>(N_("Depth")) .default_value(2.0f) .min(0.0f) .subtype(PROP_DISTANCE) - .description("The height of the cylinder on the Z axis"); - b.add_output<decl::Geometry>("Geometry"); + .description(N_("The height of the cylinder")); + b.add_output<decl::Geometry>(N_("Mesh")); + b.add_output<decl::Bool>(N_("Top")).field_source(); + b.add_output<decl::Bool>(N_("Side")).field_source(); + b.add_output<decl::Bool>(N_("Bottom")).field_source(); } -static void geo_node_mesh_primitive_cylinder_layout(uiLayout *layout, - bContext *UNUSED(C), - PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); uiItemR(layout, ptr, "fill_type", 0, nullptr, ICON_NONE); } -static void geo_node_mesh_primitive_cylinder_init(bNodeTree *UNUSED(ntree), bNode *node) +static void node_init(bNodeTree *UNUSED(ntree), bNode *node) { - NodeGeometryMeshCylinder *node_storage = (NodeGeometryMeshCylinder *)MEM_callocN( - sizeof(NodeGeometryMeshCylinder), __func__); + NodeGeometryMeshCylinder *node_storage = MEM_cnew<NodeGeometryMeshCylinder>(__func__); node_storage->fill_type = GEO_NODE_MESH_CIRCLE_FILL_NGON; node->storage = node_storage; } -static void geo_node_mesh_primitive_cylinder_exec(GeoNodeExecParams params) +static void node_update(bNodeTree *ntree, bNode *node) { - const bNode &node = params.node(); - const NodeGeometryMeshCylinder &storage = *(const NodeGeometryMeshCylinder *)node.storage; + bNodeSocket *vertices_socket = (bNodeSocket *)node->inputs.first; + bNodeSocket *rings_socket = vertices_socket->next; + bNodeSocket *fill_subdiv_socket = rings_socket->next; + + const NodeGeometryMeshCylinder &storage = node_storage(*node); + const GeometryNodeMeshCircleFillType fill = (GeometryNodeMeshCircleFillType)storage.fill_type; + const bool has_fill = fill != GEO_NODE_MESH_CIRCLE_FILL_NONE; + nodeSetSocketAvailability(ntree, fill_subdiv_socket, has_fill); +} - const GeometryNodeMeshCircleFillType fill_type = (const GeometryNodeMeshCircleFillType) - storage.fill_type; +static void node_geo_exec(GeoNodeExecParams params) +{ + const NodeGeometryMeshCylinder &storage = node_storage(params.node()); + const GeometryNodeMeshCircleFillType fill = (GeometryNodeMeshCircleFillType)storage.fill_type; const float radius = params.extract_input<float>("Radius"); const float depth = params.extract_input<float>("Depth"); - const int verts_num = params.extract_input<int>("Vertices"); - if (verts_num < 3) { + const int circle_segments = params.extract_input<int>("Vertices"); + if (circle_segments < 3) { params.error_message_add(NodeWarningType::Info, TIP_("Vertices must be at least 3")); - params.set_output("Geometry", GeometrySet()); + params.set_default_remaining_outputs(); + return; + } + + const int side_segments = params.extract_input<int>("Side Segments"); + if (side_segments < 1) { + params.error_message_add(NodeWarningType::Info, TIP_("Side Segments must be at least 1")); + params.set_default_remaining_outputs(); + return; + } + + const bool no_fill = fill == GEO_NODE_MESH_CIRCLE_FILL_NONE; + const int fill_segments = no_fill ? 1 : params.extract_input<int>("Fill Segments"); + if (fill_segments < 1) { + params.error_message_add(NodeWarningType::Info, TIP_("Fill Segments must be at least 1")); + params.set_default_remaining_outputs(); return; } + ConeAttributeOutputs attribute_outputs; + if (params.output_is_required("Top")) { + attribute_outputs.top_id = StrongAnonymousAttributeID("top_selection"); + } + if (params.output_is_required("Bottom")) { + attribute_outputs.bottom_id = StrongAnonymousAttributeID("bottom_selection"); + } + if (params.output_is_required("Side")) { + attribute_outputs.side_id = StrongAnonymousAttributeID("side_selection"); + } + /* The cylinder is a special case of the cone mesh where the top and bottom radius are equal. */ - Mesh *mesh = create_cylinder_or_cone_mesh(radius, radius, depth, verts_num, fill_type); + Mesh *mesh = create_cylinder_or_cone_mesh(radius, + radius, + depth, + circle_segments, + side_segments, + fill_segments, + fill, + attribute_outputs); + + if (attribute_outputs.top_id) { + params.set_output("Top", + AnonymousAttributeFieldInput::Create<bool>( + std::move(attribute_outputs.top_id), params.attribute_producer_name())); + } + if (attribute_outputs.bottom_id) { + params.set_output( + "Bottom", + AnonymousAttributeFieldInput::Create<bool>(std::move(attribute_outputs.bottom_id), + params.attribute_producer_name())); + } + if (attribute_outputs.side_id) { + params.set_output("Side", + AnonymousAttributeFieldInput::Create<bool>( + std::move(attribute_outputs.side_id), params.attribute_producer_name())); + } - params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); + params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_mesh_primitive_cylinder_cc void register_node_type_geo_mesh_primitive_cylinder() { + namespace file_ns = blender::nodes::node_geo_mesh_primitive_cylinder_cc; + static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_CYLINDER, "Cylinder", NODE_CLASS_GEOMETRY, 0); - node_type_init(&ntype, blender::nodes::geo_node_mesh_primitive_cylinder_init); + geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_CYLINDER, "Cylinder", NODE_CLASS_GEOMETRY); + node_type_init(&ntype, file_ns::node_init); + node_type_update(&ntype, file_ns::node_update); node_type_storage( &ntype, "NodeGeometryMeshCylinder", node_free_standard_storage, node_copy_standard_storage); - ntype.declare = blender::nodes::geo_node_mesh_primitive_cylinder_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_mesh_primitive_cylinder_exec; - ntype.draw_buttons = blender::nodes::geo_node_mesh_primitive_cylinder_layout; + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.draw_buttons = file_ns::node_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc index 858ef8648f8..ecb3c785212 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc @@ -14,6 +14,8 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include "BLI_task.hh" + #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" @@ -27,15 +29,6 @@ namespace blender::nodes { -static void geo_node_mesh_primitive_grid_declare(NodeDeclarationBuilder &b) -{ - b.add_input<decl::Float>("Size X").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input<decl::Float>("Size Y").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input<decl::Int>("Vertices X").default_value(3).min(2).max(1000); - b.add_input<decl::Int>("Vertices Y").default_value(3).min(2).max(1000); - b.add_output<decl::Geometry>("Geometry"); -} - static void calculate_uvs( Mesh *mesh, Span<MVert> verts, Span<MLoop> loops, const float size_x, const float size_y) { @@ -47,11 +40,13 @@ static void calculate_uvs( const float dx = (size_x == 0.0f) ? 0.0f : 1.0f / size_x; const float dy = (size_y == 0.0f) ? 0.0f : 1.0f / size_y; - for (const int i : loops.index_range()) { - const float3 &co = verts[loops[i].v].co; - uvs[i].x = (co.x + size_x * 0.5f) * dx; - uvs[i].y = (co.y + size_y * 0.5f) * dy; - } + threading::parallel_for(loops.index_range(), 1024, [&](IndexRange range) { + for (const int i : range) { + const float3 &co = verts[loops[i].v].co; + uvs[i].x = (co.x + size_x * 0.5f) * dx; + uvs[i].y = (co.y + size_y * 0.5f) * dy; + } + }); uv_attribute.save(); } @@ -79,72 +74,87 @@ Mesh *create_grid_mesh(const int verts_x, const float dy = edges_y == 0 ? 0.0f : size_y / edges_y; const float x_shift = edges_x / 2.0f; const float y_shift = edges_y / 2.0f; - for (const int x_index : IndexRange(verts_x)) { - for (const int y_index : IndexRange(verts_y)) { - const int vert_index = x_index * verts_y + y_index; - verts[vert_index].co[0] = (x_index - x_shift) * dx; - verts[vert_index].co[1] = (y_index - y_shift) * dy; - verts[vert_index].co[2] = 0.0f; + threading::parallel_for(IndexRange(verts_x), 512, [&](IndexRange x_range) { + for (const int x : x_range) { + const int y_offset = x * verts_y; + threading::parallel_for(IndexRange(verts_y), 512, [&](IndexRange y_range) { + for (const int y : y_range) { + const int vert_index = y_offset + y; + verts[vert_index].co[0] = (x - x_shift) * dx; + verts[vert_index].co[1] = (y - y_shift) * dy; + verts[vert_index].co[2] = 0.0f; + } + }); } - } - } - - /* Point all vertex normals in the up direction. */ - const short up_normal[3] = {0, 0, SHRT_MAX}; - for (MVert &vert : verts) { - copy_v3_v3_short(vert.no, up_normal); + }); } - /* Build the horizontal edges in the X direction. */ const int y_edges_start = 0; + const int x_edges_start = verts_x * edges_y; const short edge_flag = (edges_x == 0 || edges_y == 0) ? ME_LOOSEEDGE : ME_EDGEDRAW | ME_EDGERENDER; - int edge_index = 0; - for (const int x : IndexRange(verts_x)) { - for (const int y : IndexRange(edges_y)) { - const int vert_index = x * verts_y + y; - MEdge &edge = edges[edge_index++]; - edge.v1 = vert_index; - edge.v2 = vert_index + 1; - edge.flag = edge_flag; + + /* Build the horizontal edges in the X direction. */ + threading::parallel_for(IndexRange(verts_x), 512, [&](IndexRange x_range) { + for (const int x : x_range) { + const int y_vert_offset = x * verts_y; + const int y_edge_offset = y_edges_start + x * edges_y; + threading::parallel_for(IndexRange(edges_y), 512, [&](IndexRange y_range) { + for (const int y : y_range) { + const int vert_index = y_vert_offset + y; + MEdge &edge = edges[y_edge_offset + y]; + edge.v1 = vert_index; + edge.v2 = vert_index + 1; + edge.flag = edge_flag; + } + }); } - } + }); /* Build the vertical edges in the Y direction. */ - const int x_edges_start = edge_index; - for (const int y : IndexRange(verts_y)) { - for (const int x : IndexRange(edges_x)) { - const int vert_index = x * verts_y + y; - MEdge &edge = edges[edge_index++]; - edge.v1 = vert_index; - edge.v2 = vert_index + verts_y; - edge.flag = edge_flag; + threading::parallel_for(IndexRange(verts_y), 512, [&](IndexRange y_range) { + for (const int y : y_range) { + const int x_edge_offset = x_edges_start + y * edges_x; + threading::parallel_for(IndexRange(edges_x), 512, [&](IndexRange x_range) { + for (const int x : x_range) { + const int vert_index = x * verts_y + y; + MEdge &edge = edges[x_edge_offset + x]; + edge.v1 = vert_index; + edge.v2 = vert_index + verts_y; + edge.flag = edge_flag; + } + }); } - } - - int loop_index = 0; - int poly_index = 0; - for (const int x : IndexRange(edges_x)) { - for (const int y : IndexRange(edges_y)) { - MPoly &poly = polys[poly_index++]; - poly.loopstart = loop_index; - poly.totloop = 4; - const int vert_index = x * verts_y + y; - - MLoop &loop_a = loops[loop_index++]; - loop_a.v = vert_index; - loop_a.e = x_edges_start + edges_x * y + x; - MLoop &loop_b = loops[loop_index++]; - loop_b.v = vert_index + verts_y; - loop_b.e = y_edges_start + edges_y * (x + 1) + y; - MLoop &loop_c = loops[loop_index++]; - loop_c.v = vert_index + verts_y + 1; - loop_c.e = x_edges_start + edges_x * (y + 1) + x; - MLoop &loop_d = loops[loop_index++]; - loop_d.v = vert_index + 1; - loop_d.e = y_edges_start + edges_y * x + y; + }); + + threading::parallel_for(IndexRange(edges_x), 512, [&](IndexRange x_range) { + for (const int x : x_range) { + const int y_offset = x * edges_y; + threading::parallel_for(IndexRange(edges_y), 512, [&](IndexRange y_range) { + for (const int y : y_range) { + const int poly_index = y_offset + y; + const int loop_index = poly_index * 4; + MPoly &poly = polys[poly_index]; + poly.loopstart = loop_index; + poly.totloop = 4; + const int vert_index = x * verts_y + y; + + MLoop &loop_a = loops[loop_index]; + loop_a.v = vert_index; + loop_a.e = x_edges_start + edges_x * y + x; + MLoop &loop_b = loops[loop_index + 1]; + loop_b.v = vert_index + verts_y; + loop_b.e = y_edges_start + edges_y * (x + 1) + y; + MLoop &loop_c = loops[loop_index + 2]; + loop_c.v = vert_index + verts_y + 1; + loop_c.e = x_edges_start + edges_x * (y + 1) + x; + MLoop &loop_d = loops[loop_index + 3]; + loop_d.v = vert_index + 1; + loop_d.e = y_edges_start + edges_y * x + y; + } + }); } - } + }); if (mesh->totpoly != 0) { calculate_uvs(mesh, verts, loops, size_x, size_y); @@ -153,32 +163,62 @@ Mesh *create_grid_mesh(const int verts_x, return mesh; } -static void geo_node_mesh_primitive_grid_exec(GeoNodeExecParams params) +} // namespace blender::nodes + +namespace blender::nodes::node_geo_mesh_primitive_grid_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Float>(N_("Size X")) + .default_value(1.0f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description(N_("Side length of the plane in the X direction")); + b.add_input<decl::Float>(N_("Size Y")) + .default_value(1.0f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description(N_("Side length of the plane in the Y direction")); + b.add_input<decl::Int>(N_("Vertices X")) + .default_value(3) + .min(2) + .max(1000) + .description(N_("Number of vertices in the X direction")); + b.add_input<decl::Int>(N_("Vertices Y")) + .default_value(3) + .min(2) + .max(1000) + .description(N_("Number of vertices in the Y direction")); + b.add_output<decl::Geometry>(N_("Mesh")); +} + +static void node_geo_exec(GeoNodeExecParams params) { const float size_x = params.extract_input<float>("Size X"); const float size_y = params.extract_input<float>("Size Y"); const int verts_x = params.extract_input<int>("Vertices X"); const int verts_y = params.extract_input<int>("Vertices Y"); if (verts_x < 1 || verts_y < 1) { - params.set_output("Geometry", GeometrySet()); + params.set_default_remaining_outputs(); return; } Mesh *mesh = create_grid_mesh(verts_x, verts_y, size_x, size_y); - BLI_assert(BKE_mesh_is_valid(mesh)); BKE_id_material_eval_ensure_default_slot(&mesh->id); - params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); + params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_mesh_primitive_grid_cc void register_node_type_geo_mesh_primitive_grid() { + namespace file_ns = blender::nodes::node_geo_mesh_primitive_grid_cc; + static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_GRID, "Grid", NODE_CLASS_GEOMETRY, 0); - ntype.declare = blender::nodes::geo_node_mesh_primitive_grid_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_mesh_primitive_grid_exec; + geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_GRID, "Grid", NODE_CLASS_GEOMETRY); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc index 5ea7165ac31..28a505c5bb8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc @@ -24,22 +24,31 @@ #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_mesh_primitive_ico_sphere_cc { -static void geo_node_mesh_primitive_ico_sphere_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Float>("Radius").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input<decl::Int>("Subdivisions").default_value(1).min(1).max(7); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Float>(N_("Radius")) + .default_value(1.0f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description(N_("Distance from the generated points to the origin")); + b.add_input<decl::Int>(N_("Subdivisions")) + .default_value(1) + .min(1) + .max(7) + .description(N_("Number of subdivisions on top of the basic icosahedron")); + b.add_output<decl::Geometry>(N_("Mesh")); } static Mesh *create_ico_sphere_mesh(const int subdivisions, const float radius) { const float4x4 transform = float4x4::identity(); - const BMeshCreateParams bmcp = {true}; + BMeshCreateParams bmesh_create_params{}; + bmesh_create_params.use_toolflags = true; const BMAllocTemplate allocsize = {0, 0, 0, 0}; - BMesh *bm = BM_mesh_create(&allocsize, &bmcp); + BMesh *bm = BM_mesh_create(&allocsize, &bmesh_create_params); BM_data_layer_add_named(bm, &bm->ldata, CD_MLOOPUV, nullptr); BMO_op_callf(bm, @@ -60,24 +69,26 @@ static Mesh *create_ico_sphere_mesh(const int subdivisions, const float radius) return mesh; } -static void geo_node_mesh_primitive_ico_sphere_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { const int subdivisions = std::min(params.extract_input<int>("Subdivisions"), 10); const float radius = params.extract_input<float>("Radius"); Mesh *mesh = create_ico_sphere_mesh(subdivisions, radius); - params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); + params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_mesh_primitive_ico_sphere_cc void register_node_type_geo_mesh_primitive_ico_sphere() { + namespace file_ns = blender::nodes::node_geo_mesh_primitive_ico_sphere_cc; + static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE, "Ico Sphere", NODE_CLASS_GEOMETRY, 0); - ntype.declare = blender::nodes::geo_node_mesh_primitive_ico_sphere_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_mesh_primitive_ico_sphere_exec; + &ntype, GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE, "Ico Sphere", NODE_CLASS_GEOMETRY); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc index 031223b5ca6..691267bccb8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc @@ -23,22 +23,39 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "NOD_socket_search_link.hh" + #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_mesh_primitive_line_cc { -static void geo_node_mesh_primitive_line_declare(NodeDeclarationBuilder &b) +NODE_STORAGE_FUNCS(NodeGeometryMeshLine) + +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Int>("Count").default_value(10).min(1).max(10000); - b.add_input<decl::Float>("Resolution").default_value(1.0f).min(0.1f).subtype(PROP_DISTANCE); - b.add_input<decl::Vector>("Start Location").subtype(PROP_TRANSLATION); - b.add_input<decl::Vector>("Offset").default_value({0.0f, 0.0f, 1.0f}).subtype(PROP_TRANSLATION); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Int>(N_("Count")) + .default_value(10) + .min(1) + .max(10000) + .description(N_("Number of vertices on the line")); + b.add_input<decl::Float>(N_("Resolution")) + .default_value(1.0f) + .min(0.1f) + .subtype(PROP_DISTANCE) + .description(N_("Length of each individual edge")); + b.add_input<decl::Vector>(N_("Start Location")) + .subtype(PROP_TRANSLATION) + .description(N_("Position of the first vertex")); + b.add_input<decl::Vector>(N_("Offset")) + .default_value({0.0f, 0.0f, 1.0f}) + .subtype(PROP_TRANSLATION) + .description(N_( + "In offset mode, the distance between each socket on each axis. In end points mode, the " + "position of the final vertex")); + b.add_output<decl::Geometry>(N_("Mesh")); } -static void geo_node_mesh_primitive_line_layout(uiLayout *layout, - bContext *UNUSED(C), - PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); @@ -48,10 +65,9 @@ static void geo_node_mesh_primitive_line_layout(uiLayout *layout, } } -static void geo_node_mesh_primitive_line_init(bNodeTree *UNUSED(ntree), bNode *node) +static void node_init(bNodeTree *UNUSED(ntree), bNode *node) { - NodeGeometryMeshLine *node_storage = (NodeGeometryMeshLine *)MEM_callocN( - sizeof(NodeGeometryMeshLine), __func__); + NodeGeometryMeshLine *node_storage = MEM_cnew<NodeGeometryMeshLine>(__func__); node_storage->mode = GEO_NODE_MESH_LINE_MODE_OFFSET; node_storage->count_mode = GEO_NODE_MESH_LINE_COUNT_TOTAL; @@ -59,68 +75,74 @@ static void geo_node_mesh_primitive_line_init(bNodeTree *UNUSED(ntree), bNode *n node->storage = node_storage; } -static void geo_node_mesh_primitive_line_update(bNodeTree *UNUSED(tree), bNode *node) +static void node_update(bNodeTree *ntree, bNode *node) { bNodeSocket *count_socket = (bNodeSocket *)node->inputs.first; bNodeSocket *resolution_socket = count_socket->next; bNodeSocket *start_socket = resolution_socket->next; bNodeSocket *end_and_offset_socket = start_socket->next; - const NodeGeometryMeshLine &storage = *(const NodeGeometryMeshLine *)node->storage; - const GeometryNodeMeshLineMode mode = (const GeometryNodeMeshLineMode)storage.mode; - const GeometryNodeMeshLineCountMode count_mode = (const GeometryNodeMeshLineCountMode) + const NodeGeometryMeshLine &storage = node_storage(*node); + const GeometryNodeMeshLineMode mode = (GeometryNodeMeshLineMode)storage.mode; + const GeometryNodeMeshLineCountMode count_mode = (GeometryNodeMeshLineCountMode) storage.count_mode; node_sock_label(end_and_offset_socket, (mode == GEO_NODE_MESH_LINE_MODE_END_POINTS) ? N_("End Location") : N_("Offset")); - nodeSetSocketAvailability(resolution_socket, + nodeSetSocketAvailability(ntree, + resolution_socket, mode == GEO_NODE_MESH_LINE_MODE_END_POINTS && count_mode == GEO_NODE_MESH_LINE_COUNT_RESOLUTION); - nodeSetSocketAvailability(count_socket, + nodeSetSocketAvailability(ntree, + count_socket, mode == GEO_NODE_MESH_LINE_MODE_OFFSET || count_mode == GEO_NODE_MESH_LINE_COUNT_TOTAL); } -static void fill_edge_data(MutableSpan<MEdge> edges) +static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) { - for (const int i : edges.index_range()) { - edges[i].v1 = i; - edges[i].v2 = i + 1; - edges[i].flag |= ME_LOOSEEDGE; + const NodeDeclaration &declaration = *params.node_type().fixed_declaration; + if (params.in_out() == SOCK_OUT) { + search_link_ops_for_declarations(params, declaration.outputs()); + return; } -} - -Mesh *create_line_mesh(const float3 start, const float3 delta, const int count) -{ - if (count < 1) { - return nullptr; + else if (params.node_tree().typeinfo->validate_link( + static_cast<eNodeSocketDatatype>(params.other_socket().type), SOCK_FLOAT)) { + params.add_item(IFACE_("Count"), [](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node("GeometryNodeMeshLine"); + node_storage(node).mode = GEO_NODE_MESH_LINE_MODE_OFFSET; + params.connect_available_socket(node, "Count"); + }); + params.add_item(IFACE_("Resolution"), [](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node("GeometryNodeMeshLine"); + node_storage(node).mode = GEO_NODE_MESH_LINE_MODE_OFFSET; + node_storage(node).count_mode = GEO_NODE_MESH_LINE_COUNT_RESOLUTION; + params.connect_available_socket(node, "Resolution"); + }); + params.add_item(IFACE_("Start Location"), [](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node("GeometryNodeMeshLine"); + params.connect_available_socket(node, "Start Location"); + }); + params.add_item(IFACE_("Offset"), [](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node("GeometryNodeMeshLine"); + params.connect_available_socket(node, "Offset"); + }); + /* The last socket is reused in end points mode. */ + params.add_item(IFACE_("End Location"), [](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node("GeometryNodeMeshLine"); + node_storage(node).mode = GEO_NODE_MESH_LINE_MODE_END_POINTS; + params.connect_available_socket(node, "Offset"); + }); } - - Mesh *mesh = BKE_mesh_new_nomain(count, count - 1, 0, 0, 0); - BKE_id_material_eval_ensure_default_slot(&mesh->id); - MutableSpan<MVert> verts{mesh->mvert, mesh->totvert}; - MutableSpan<MEdge> edges{mesh->medge, mesh->totedge}; - - short normal[3]; - normal_float_to_short_v3(normal, delta.normalized()); - - for (const int i : verts.index_range()) { - copy_v3_v3(verts[i].co, start + delta * i); - copy_v3_v3_short(verts[i].no, normal); - } - - fill_edge_data(edges); - - return mesh; } -static void geo_node_mesh_primitive_line_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { - const NodeGeometryMeshLine &storage = *(const NodeGeometryMeshLine *)params.node().storage; - const GeometryNodeMeshLineMode mode = (const GeometryNodeMeshLineMode)storage.mode; - const GeometryNodeMeshLineCountMode count_mode = (const GeometryNodeMeshLineCountMode) + const NodeGeometryMeshLine &storage = node_storage(params.node()); + const GeometryNodeMeshLineMode mode = (GeometryNodeMeshLineMode)storage.mode; + const GeometryNodeMeshLineCountMode count_mode = (GeometryNodeMeshLineCountMode) storage.count_mode; Mesh *mesh = nullptr; @@ -133,8 +155,8 @@ static void geo_node_mesh_primitive_line_exec(GeoNodeExecParams params) if (count_mode == GEO_NODE_MESH_LINE_COUNT_RESOLUTION) { /* Don't allow asymptotic count increase for low resolution values. */ const float resolution = std::max(params.extract_input<float>("Resolution"), 0.0001f); - const int count = total_delta.length() / resolution + 1; - const float3 delta = total_delta.normalized() * resolution; + const int count = math::length(total_delta) / resolution + 1; + const float3 delta = math::normalize(total_delta) * resolution; mesh = create_line_mesh(start, delta, count); } else if (count_mode == GEO_NODE_MESH_LINE_COUNT_TOTAL) { @@ -154,22 +176,58 @@ static void geo_node_mesh_primitive_line_exec(GeoNodeExecParams params) mesh = create_line_mesh(start, delta, count); } - params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); + params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); +} + +} // namespace blender::nodes::node_geo_mesh_primitive_line_cc + +namespace blender::nodes { + +static void fill_edge_data(MutableSpan<MEdge> edges) +{ + for (const int i : edges.index_range()) { + edges[i].v1 = i; + edges[i].v2 = i + 1; + edges[i].flag |= ME_LOOSEEDGE; + } +} + +Mesh *create_line_mesh(const float3 start, const float3 delta, const int count) +{ + if (count < 1) { + return nullptr; + } + + Mesh *mesh = BKE_mesh_new_nomain(count, count - 1, 0, 0, 0); + BKE_id_material_eval_ensure_default_slot(&mesh->id); + MutableSpan<MVert> verts{mesh->mvert, mesh->totvert}; + MutableSpan<MEdge> edges{mesh->medge, mesh->totedge}; + + for (const int i : verts.index_range()) { + copy_v3_v3(verts[i].co, start + delta * i); + } + + fill_edge_data(edges); + + return mesh; } } // namespace blender::nodes void register_node_type_geo_mesh_primitive_line() { + namespace file_ns = blender::nodes::node_geo_mesh_primitive_line_cc; + static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_LINE, "Mesh Line", NODE_CLASS_GEOMETRY, 0); - ntype.declare = blender::nodes::geo_node_mesh_primitive_line_declare; - node_type_init(&ntype, blender::nodes::geo_node_mesh_primitive_line_init); - node_type_update(&ntype, blender::nodes::geo_node_mesh_primitive_line_update); + geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_LINE, "Mesh Line", NODE_CLASS_GEOMETRY); + ntype.declare = file_ns::node_declare; + node_type_init(&ntype, file_ns::node_init); + node_type_update(&ntype, file_ns::node_update); node_type_storage( &ntype, "NodeGeometryMeshLine", node_free_standard_storage, node_copy_standard_storage); - ntype.geometry_node_execute = blender::nodes::geo_node_mesh_primitive_line_exec; - ntype.draw_buttons = blender::nodes::geo_node_mesh_primitive_line_layout; + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.draw_buttons = file_ns::node_layout; + ntype.gather_link_search_ops = file_ns::node_gather_link_searches; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc index 6fd6cdf5747..751cf917f6f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc @@ -25,14 +25,26 @@ #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_mesh_primitive_uv_sphere_cc { -static void geo_node_mesh_primitive_uv_shpere_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Int>("Segments").default_value(32).min(3).max(1024); - b.add_input<decl::Int>("Rings").default_value(16).min(2).max(1024); - b.add_input<decl::Float>("Radius").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Int>(N_("Segments")) + .default_value(32) + .min(3) + .max(1024) + .description(N_("Horizontal resolution of the sphere")); + b.add_input<decl::Int>(N_("Rings")) + .default_value(16) + .min(2) + .max(1024) + .description(N_("The number of horizontal rings")); + b.add_input<decl::Float>(N_("Radius")) + .default_value(1.0f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description(N_("Distance from the generated points to the origin")); + b.add_output<decl::Geometry>(N_("Mesh")); } static int sphere_vert_total(const int segments, const int rings) @@ -59,7 +71,12 @@ static int sphere_face_total(const int segments, const int rings) return quads + triangles; } +/** + * Also calculate vertex normals here, since the calculation is trivial, and it allows avoiding the + * calculation later, if it's necessary. The vertex normals are just the normalized positions. + */ static void calculate_sphere_vertex_data(MutableSpan<MVert> verts, + MutableSpan<float3> vert_normals, const float radius, const int segments, const int rings) @@ -68,7 +85,7 @@ static void calculate_sphere_vertex_data(MutableSpan<MVert> verts, const float delta_phi = (2.0f * M_PI) / segments; copy_v3_v3(verts[0].co, float3(0.0f, 0.0f, radius)); - normal_float_to_short_v3(verts[0].no, float3(0.0f, 0.0f, 1.0f)); + vert_normals.first() = float3(0.0f, 0.0f, 1.0f); int vert_index = 1; for (const int ring : IndexRange(1, rings - 1)) { @@ -80,13 +97,13 @@ static void calculate_sphere_vertex_data(MutableSpan<MVert> verts, const float x = sin_theta * std::cos(phi); const float y = sin_theta * std::sin(phi); copy_v3_v3(verts[vert_index].co, float3(x, y, z) * radius); - normal_float_to_short_v3(verts[vert_index].no, float3(x, y, z)); + vert_normals[vert_index] = float3(x, y, z); vert_index++; } } copy_v3_v3(verts.last().co, float3(0.0f, 0.0f, -radius)); - normal_float_to_short_v3(verts.last().no, float3(0.0f, 0.0f, -1.0f)); + vert_normals.last() = float3(0.0f, 0.0f, -1.0f); } static void calculate_sphere_edge_indices(MutableSpan<MEdge> edges, @@ -166,7 +183,7 @@ static void calculate_sphere_faces(MutableSpan<MLoop> loops, int ring_vert_index_start = 1; int ring_edge_index_start = segments; - for (const int UNUSED(ring) : IndexRange(1, rings - 2)) { + for ([[maybe_unused]] const int ring : IndexRange(1, rings - 2)) { const int next_ring_vert_index_start = ring_vert_index_start + segments; const int next_ring_edge_index_start = ring_edge_index_start + segments * 2; const int ring_vertical_edge_index_start = ring_edge_index_start + segments; @@ -267,7 +284,9 @@ static Mesh *create_uv_sphere_mesh(const float radius, const int segments, const MutableSpan<MEdge> edges{mesh->medge, mesh->totedge}; MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly}; - calculate_sphere_vertex_data(verts, radius, segments, rings); + MutableSpan vert_normals{(float3 *)BKE_mesh_vertex_normals_for_write(mesh), mesh->totvert}; + calculate_sphere_vertex_data(verts, vert_normals, radius, segments, rings); + BKE_mesh_vertex_normals_clear_dirty(mesh); calculate_sphere_edge_indices(edges, segments, rings); @@ -275,12 +294,10 @@ static Mesh *create_uv_sphere_mesh(const float radius, const int segments, const calculate_sphere_uvs(mesh, segments, rings); - BLI_assert(BKE_mesh_is_valid(mesh)); - return mesh; } -static void geo_node_mesh_primitive_uv_sphere_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { const int segments_num = params.extract_input<int>("Segments"); const int rings_num = params.extract_input<int>("Rings"); @@ -291,25 +308,26 @@ static void geo_node_mesh_primitive_uv_sphere_exec(GeoNodeExecParams params) if (rings_num < 3) { params.error_message_add(NodeWarningType::Info, TIP_("Rings must be at least 3")); } - params.set_output("Geometry", GeometrySet()); + params.set_default_remaining_outputs(); return; } const float radius = params.extract_input<float>("Radius"); Mesh *mesh = create_uv_sphere_mesh(radius, segments_num, rings_num); - params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); + params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_mesh_primitive_uv_sphere_cc void register_node_type_geo_mesh_primitive_uv_sphere() { + namespace file_ns = blender::nodes::node_geo_mesh_primitive_uv_sphere_cc; + static bNodeType ntype; - geo_node_type_base( - &ntype, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, "UV Sphere", NODE_CLASS_GEOMETRY, 0); - ntype.declare = blender::nodes::geo_node_mesh_primitive_uv_shpere_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_mesh_primitive_uv_sphere_exec; + geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, "UV Sphere", NODE_CLASS_GEOMETRY); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc index c436f5bd480..6d8a2fac8ad 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc @@ -23,13 +23,13 @@ #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_mesh_subdivide_cc { -static void geo_node_mesh_subdivide_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Int>("Level").default_value(1).min(0).max(6); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input<decl::Int>(N_("Level")).default_value(1).min(0).max(6); + b.add_output<decl::Geometry>(N_("Mesh")); } static void geometry_set_mesh_subdivide(GeometrySet &geometry_set, const int level) @@ -72,14 +72,14 @@ static void geometry_set_mesh_subdivide(GeometrySet &geometry_set, const int lev BKE_subdiv_free(subdiv); } -static void geo_node_mesh_subdivide_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { - GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh"); #ifndef WITH_OPENSUBDIV params.error_message_add(NodeWarningType::Error, TIP_("Disabled, Blender was compiled without OpenSubdiv")); - params.set_output("Geometry", std::move(geometry_set)); + params.set_default_remaining_outputs(); return; #endif @@ -87,24 +87,26 @@ static void geo_node_mesh_subdivide_exec(GeoNodeExecParams params) const int subdiv_level = clamp_i(params.extract_input<int>("Level"), 0, 11); if (subdiv_level == 0) { - params.set_output("Geometry", std::move(geometry_set)); + params.set_output("Mesh", std::move(geometry_set)); return; } geometry_set.modify_geometry_sets( [&](GeometrySet &geometry_set) { geometry_set_mesh_subdivide(geometry_set, subdiv_level); }); - params.set_output("Geometry", std::move(geometry_set)); + params.set_output("Mesh", std::move(geometry_set)); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_mesh_subdivide_cc void register_node_type_geo_mesh_subdivide() { + namespace file_ns = blender::nodes::node_geo_mesh_subdivide_cc; + static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_MESH_SUBDIVIDE, "Mesh Subdivide", NODE_CLASS_GEOMETRY, 0); - ntype.declare = blender::nodes::geo_node_mesh_subdivide_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_mesh_subdivide_exec; + geo_node_type_base(&ntype, GEO_NODE_SUBDIVIDE_MESH, "Subdivide Mesh", NODE_CLASS_GEOMETRY); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc new file mode 100644 index 00000000000..0f0fb3c230a --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc @@ -0,0 +1,71 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "GEO_mesh_to_curve.hh" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_mesh_to_curve_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_output<decl::Geometry>(N_("Curve")); +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh"); + + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (!geometry_set.has_mesh()) { + geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); + return; + } + + const MeshComponent &component = *geometry_set.get_component_for_read<MeshComponent>(); + GeometryComponentFieldContext context{component, ATTR_DOMAIN_EDGE}; + fn::FieldEvaluator evaluator{context, component.attribute_domain_size(ATTR_DOMAIN_EDGE)}; + evaluator.add(params.get_input<Field<bool>>("Selection")); + evaluator.evaluate(); + const IndexMask selection = evaluator.get_evaluated_as_mask(0); + if (selection.size() == 0) { + geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); + return; + } + + std::unique_ptr<CurveEval> curve = geometry::mesh_to_curve_convert(component, selection); + geometry_set.replace_curve(curve.release()); + geometry_set.keep_only({GEO_COMPONENT_TYPE_CURVE, GEO_COMPONENT_TYPE_INSTANCES}); + }); + + params.set_output("Curve", std::move(geometry_set)); +} + +} // namespace blender::nodes::node_geo_mesh_to_curve_cc + +void register_node_type_geo_mesh_to_curve() +{ + namespace file_ns = blender::nodes::node_geo_mesh_to_curve_cc; + + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_MESH_TO_CURVE, "Mesh to Curve", NODE_CLASS_GEOMETRY); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc index 2f59a3c968b..d0546cd2583 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc @@ -26,31 +26,32 @@ using blender::Array; -namespace blender::nodes { +namespace blender::nodes::node_geo_mesh_to_points_cc { -static void geo_node_mesh_to_points_declare(NodeDeclarationBuilder &b) +NODE_STORAGE_FUNCS(NodeGeometryMeshToPoints) + +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Mesh"); - b.add_input<decl::Vector>("Position").implicit_field(); - b.add_input<decl::Float>("Radius") + b.add_input<decl::Geometry>(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).supports_field().hide_value(); + b.add_input<decl::Vector>(N_("Position")).implicit_field(); + b.add_input<decl::Float>(N_("Radius")) .default_value(0.05f) .min(0.0f) .subtype(PROP_DISTANCE) .supports_field(); - b.add_input<decl::Bool>("Selection").default_value(true).supports_field().hide_value(); - b.add_output<decl::Geometry>("Points"); + b.add_output<decl::Geometry>(N_("Points")); } -static void geo_node_mesh_to_points_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "mode", 0, "", ICON_NONE); } -static void geo_node_mesh_to_points_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeGeometryMeshToPoints *data = (NodeGeometryMeshToPoints *)MEM_callocN( - sizeof(NodeGeometryMeshToPoints), __func__); - data->mode = GEO_NODE_MESH_TO_POINTS_FACES; + NodeGeometryMeshToPoints *data = MEM_cnew<NodeGeometryMeshToPoints>(__func__); + data->mode = GEO_NODE_MESH_TO_POINTS_VERTICES; node->storage = data; } @@ -81,10 +82,15 @@ static void geometry_set_mesh_to_points(GeometrySet &geometry_set, geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); return; } - fn::FieldEvaluator selection_evaluator{field_context, domain_size}; - selection_evaluator.add(selection_field); - selection_evaluator.evaluate(); - const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); + fn::FieldEvaluator evaluator{field_context, domain_size}; + evaluator.set_selection(selection_field); + /* Evaluating directly into the point cloud doesn't work because we are not using the full + * "min_array_size" array but compressing the selected elements into the final array with no + * gaps. */ + evaluator.add(position_field); + evaluator.add(radius_field); + evaluator.evaluate(); + const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); PointCloud *pointcloud = BKE_pointcloud_new_nomain(selection.size()); uninitialized_fill_n(pointcloud->radius, pointcloud->totpoint, 0.05f); @@ -92,13 +98,6 @@ static void geometry_set_mesh_to_points(GeometrySet &geometry_set, PointCloudComponent &point_component = geometry_set.get_component_for_write<PointCloudComponent>(); - /* Evaluating directly into the point cloud doesn't work because we are not using the full - * "min_array_size" array but compressing the selected elements into the final array with no - * gaps. */ - fn::FieldEvaluator evaluator{field_context, &selection}; - evaluator.add(position_field); - evaluator.add(radius_field); - evaluator.evaluate(); copy_attribute_to_points(evaluator.get_evaluated<float3>(0), selection, {(float3 *)pointcloud->co, pointcloud->totpoint}); @@ -113,14 +112,14 @@ static void geometry_set_mesh_to_points(GeometrySet &geometry_set, for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) { const AttributeIDRef attribute_id = entry.key; const CustomDataType data_type = entry.value.data_type; - GVArrayPtr src = mesh_component->attribute_get_for_read(attribute_id, domain, data_type); + GVArray src = mesh_component->attribute_get_for_read(attribute_id, domain, data_type); OutputAttribute dst = point_component.attribute_try_get_for_output_only( attribute_id, ATTR_DOMAIN_POINT, data_type); if (dst && src) { attribute_math::convert_to_static_type(data_type, [&](auto dummy) { using T = decltype(dummy); - GVArray_Typed<T> src_typed{*src}; - copy_attribute_to_points(*src_typed, selection, dst.as_span().typed<T>()); + VArray<T> src_typed = src.typed<T>(); + copy_attribute_to_points(src_typed, selection, dst.as_span().typed<T>()); }); dst.save(); } @@ -129,7 +128,7 @@ static void geometry_set_mesh_to_points(GeometrySet &geometry_set, geometry_set.keep_only({GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_INSTANCES}); } -static void geo_node_mesh_to_points_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh"); Field<float3> position = params.extract_input<Field<float3>>("Position"); @@ -144,8 +143,7 @@ static void geo_node_mesh_to_points_exec(GeoNodeExecParams params) FieldOperation(max_zero_fn, {std::move(radius)})); Field<float> positive_radius(std::move(max_zero_op), 0); - const NodeGeometryMeshToPoints &storage = - *(const NodeGeometryMeshToPoints *)params.node().storage; + const NodeGeometryMeshToPoints &storage = node_storage(params.node()); const GeometryNodeMeshToPointsMode mode = (GeometryNodeMeshToPointsMode)storage.mode; geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { @@ -172,17 +170,19 @@ static void geo_node_mesh_to_points_exec(GeoNodeExecParams params) params.set_output("Points", std::move(geometry_set)); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_mesh_to_points_cc void register_node_type_geo_mesh_to_points() { + namespace file_ns = blender::nodes::node_geo_mesh_to_points_cc; + static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_MESH_TO_POINTS, "Mesh to Points", NODE_CLASS_GEOMETRY, 0); - ntype.declare = blender::nodes::geo_node_mesh_to_points_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_mesh_to_points_exec; - node_type_init(&ntype, blender::nodes::geo_node_mesh_to_points_init); - ntype.draw_buttons = blender::nodes::geo_node_mesh_to_points_layout; + geo_node_type_base(&ntype, GEO_NODE_MESH_TO_POINTS, "Mesh to Points", NODE_CLASS_GEOMETRY); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + node_type_init(&ntype, file_ns::node_init); + ntype.draw_buttons = file_ns::node_layout; node_type_storage( &ntype, "NodeGeometryMeshToPoints", node_free_standard_storage, node_copy_standard_storage); nodeRegisterType(&ntype); diff --git a/source/blender/nodes/geometry/nodes/node_geo_object_info.cc b/source/blender/nodes/geometry/nodes/node_geo_object_info.cc index 389acc40f0f..d32875d2627 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_object_info.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_object_info.cc @@ -21,92 +21,106 @@ #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_object_info_cc { -static void geo_node_object_info_declare(NodeDeclarationBuilder &b) +NODE_STORAGE_FUNCS(NodeGeometryObjectInfo) + +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Object>("Object").hide_label(); - b.add_output<decl::Vector>("Location"); - b.add_output<decl::Vector>("Rotation"); - b.add_output<decl::Vector>("Scale"); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Object>(N_("Object")).hide_label(); + b.add_input<decl::Bool>(N_("As Instance")) + .description(N_("Output the entire object as single instance. " + "This allows instancing non-geometry object types")); + b.add_output<decl::Vector>(N_("Location")); + b.add_output<decl::Vector>(N_("Rotation")); + b.add_output<decl::Vector>(N_("Scale")); + b.add_output<decl::Geometry>(N_("Geometry")); } -static void geo_node_object_info_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "transform_space", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); } -static void geo_node_object_info_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { - const bNode &bnode = params.node(); - NodeGeometryObjectInfo *node_storage = (NodeGeometryObjectInfo *)bnode.storage; - const bool transform_space_relative = (node_storage->transform_space == + const NodeGeometryObjectInfo &storage = node_storage(params.node()); + const bool transform_space_relative = (storage.transform_space == GEO_NODE_TRANSFORM_SPACE_RELATIVE); Object *object = params.get_input<Object *>("Object"); - float3 location = {0, 0, 0}; - float3 rotation = {0, 0, 0}; - float3 scale = {0, 0, 0}; - GeometrySet geometry_set; - const Object *self_object = params.self_object(); + if (object == nullptr) { + params.set_default_remaining_outputs(); + return; + } - if (object != nullptr) { - float transform[4][4]; - mul_m4_m4m4(transform, self_object->imat, object->obmat); + const float4x4 &object_matrix = object->obmat; + const float4x4 transform = float4x4(self_object->imat) * object_matrix; - float quaternion[4]; - if (transform_space_relative) { - mat4_decompose(location, quaternion, scale, transform); - } - else { - mat4_decompose(location, quaternion, scale, object->obmat); + if (transform_space_relative) { + params.set_output("Location", transform.translation()); + params.set_output("Rotation", transform.to_euler()); + params.set_output("Scale", transform.scale()); + } + else { + params.set_output("Location", object_matrix.translation()); + params.set_output("Rotation", object_matrix.to_euler()); + params.set_output("Scale", object_matrix.scale()); + } + + if (params.output_is_required("Geometry")) { + if (object == self_object) { + params.error_message_add(NodeWarningType::Error, + TIP_("Geometry cannot be retrieved from the modifier object")); + params.set_default_remaining_outputs(); + return; } - quat_to_eul(rotation, quaternion); - if (object != self_object) { + GeometrySet geometry_set; + if (params.get_input<bool>("As Instance")) { InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>(); const int handle = instances.add_reference(*object); - if (transform_space_relative) { instances.add_instance(handle, transform); } else { - float unit_transform[4][4]; - unit_m4(unit_transform); - instances.add_instance(handle, unit_transform); + instances.add_instance(handle, float4x4::identity()); + } + } + else { + geometry_set = bke::object_get_evaluated_geometry_set(*object); + if (transform_space_relative) { + transform_geometry_set(geometry_set, transform, *params.depsgraph()); } } - } - params.set_output("Location", location); - params.set_output("Rotation", rotation); - params.set_output("Scale", scale); - params.set_output("Geometry", geometry_set); + params.set_output("Geometry", geometry_set); + } } -static void geo_node_object_info_node_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeGeometryObjectInfo *data = (NodeGeometryObjectInfo *)MEM_callocN( - sizeof(NodeGeometryObjectInfo), __func__); + NodeGeometryObjectInfo *data = MEM_cnew<NodeGeometryObjectInfo>(__func__); data->transform_space = GEO_NODE_TRANSFORM_SPACE_ORIGINAL; node->storage = data; } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_object_info_cc void register_node_type_geo_object_info() { + namespace file_ns = blender::nodes::node_geo_object_info_cc; + static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_OBJECT_INFO, "Object Info", NODE_CLASS_INPUT, 0); - node_type_init(&ntype, blender::nodes::geo_node_object_info_node_init); + geo_node_type_base(&ntype, GEO_NODE_OBJECT_INFO, "Object Info", NODE_CLASS_INPUT); + node_type_init(&ntype, file_ns::node_node_init); node_type_storage( &ntype, "NodeGeometryObjectInfo", node_free_standard_storage, node_copy_standard_storage); - ntype.geometry_node_execute = blender::nodes::geo_node_object_info_exec; - ntype.draw_buttons = blender::nodes::geo_node_object_info_layout; - ntype.declare = blender::nodes::geo_node_object_info_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.draw_buttons = file_ns::node_layout; + ntype.declare = file_ns::node_declare; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc index afd0ced6360..f3da591f684 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc @@ -21,15 +21,15 @@ #include "node_geometry_util.hh" -using blender::Array; +namespace blender::nodes::node_geo_points_to_vertices_cc { -namespace blender::nodes { +using blender::Array; -static void geo_node_points_to_vertices_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Points"); - b.add_input<decl::Bool>("Selection").default_value(true).supports_field().hide_value(); - b.add_output<decl::Geometry>("Mesh"); + b.add_input<decl::Geometry>(N_("Points")).supported_type(GEO_COMPONENT_TYPE_POINT_CLOUD); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).supports_field().hide_value(); + b.add_output<decl::Geometry>(N_("Mesh")); } template<typename T> @@ -74,15 +74,15 @@ static void geometry_set_points_to_vertices(GeometrySet &geometry_set, for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) { const AttributeIDRef attribute_id = entry.key; const CustomDataType data_type = entry.value.data_type; - GVArrayPtr src = point_component->attribute_get_for_read( + GVArray src = point_component->attribute_get_for_read( attribute_id, ATTR_DOMAIN_POINT, data_type); OutputAttribute dst = mesh_component.attribute_try_get_for_output_only( attribute_id, ATTR_DOMAIN_POINT, data_type); if (dst && src) { attribute_math::convert_to_static_type(data_type, [&](auto dummy) { using T = decltype(dummy); - GVArray_Typed<T> src_typed{*src}; - VArray_Span<T> src_typed_span{*src_typed}; + VArray<T> src_typed = src.typed<T>(); + VArray_Span<T> src_typed_span{src_typed}; copy_attribute_to_vertices(src_typed_span, selection, dst.as_span().typed<T>()); }); dst.save(); @@ -92,7 +92,7 @@ static void geometry_set_points_to_vertices(GeometrySet &geometry_set, geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES}); } -static void geo_node_points_to_vertices_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Points"); Field<bool> selection_field = params.extract_input<Field<bool>>("Selection"); @@ -104,15 +104,17 @@ static void geo_node_points_to_vertices_exec(GeoNodeExecParams params) params.set_output("Mesh", std::move(geometry_set)); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_points_to_vertices_cc void register_node_type_geo_points_to_vertices() { + namespace file_ns = blender::nodes::node_geo_points_to_vertices_cc; + static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_POINTS_TO_VERTICES, "Points to Vertices", NODE_CLASS_GEOMETRY, 0); - ntype.declare = blender::nodes::geo_node_points_to_vertices_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_points_to_vertices_exec; + &ntype, GEO_NODE_POINTS_TO_VERTICES, "Points to Vertices", NODE_CLASS_GEOMETRY); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc new file mode 100644 index 00000000000..c165bcf8e35 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc @@ -0,0 +1,284 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifdef WITH_OPENVDB +# include <openvdb/openvdb.h> +# include <openvdb/tools/LevelSetUtil.h> +# include <openvdb/tools/ParticlesToLevelSet.h> +#endif + +#include "node_geometry_util.hh" + +#include "BKE_lib_id.h" +#include "BKE_volume.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes::node_geo_points_to_volume_cc { + +NODE_STORAGE_FUNCS(NodeGeometryPointsToVolume) + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>(N_("Points")); + b.add_input<decl::Float>(N_("Density")).default_value(1.0f).min(0.0f); + b.add_input<decl::Float>(N_("Voxel Size")) + .default_value(0.3f) + .min(0.01f) + .subtype(PROP_DISTANCE) + .make_available([](bNode &node) { + node_storage(node).resolution_mode = GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_SIZE; + }); + b.add_input<decl::Float>(N_("Voxel Amount")) + .default_value(64.0f) + .min(0.0f) + .make_available([](bNode &node) { + node_storage(node).resolution_mode = GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_AMOUNT; + }); + b.add_input<decl::Float>(N_("Radius")) + .default_value(0.5f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .supports_field(); + b.add_output<decl::Geometry>(N_("Volume")); +} + +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + uiItemR(layout, ptr, "resolution_mode", 0, IFACE_("Resolution"), ICON_NONE); +} + +static void node_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryPointsToVolume *data = MEM_cnew<NodeGeometryPointsToVolume>(__func__); + data->resolution_mode = GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_AMOUNT; + node->storage = data; +} + +static void node_update(bNodeTree *ntree, bNode *node) +{ + const NodeGeometryPointsToVolume &storage = node_storage(*node); + bNodeSocket *voxel_size_socket = nodeFindSocket(node, SOCK_IN, "Voxel Size"); + bNodeSocket *voxel_amount_socket = nodeFindSocket(node, SOCK_IN, "Voxel Amount"); + nodeSetSocketAvailability(ntree, + voxel_amount_socket, + storage.resolution_mode == + GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_AMOUNT); + nodeSetSocketAvailability(ntree, + voxel_size_socket, + storage.resolution_mode == + GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_SIZE); +} + +#ifdef WITH_OPENVDB +namespace { +/* Implements the interface required by #openvdb::tools::ParticlesToLevelSet. */ +struct ParticleList { + using PosType = openvdb::Vec3R; + + Span<float3> positions; + Span<float> radii; + + size_t size() const + { + return (size_t)positions.size(); + } + + void getPos(size_t n, openvdb::Vec3R &xyz) const + { + xyz = &positions[n].x; + } + + void getPosRad(size_t n, openvdb::Vec3R &xyz, openvdb::Real &radius) const + { + xyz = &positions[n].x; + radius = radii[n]; + } +}; +} // namespace + +static openvdb::FloatGrid::Ptr generate_volume_from_points(const Span<float3> positions, + const Span<float> radii, + const float density) +{ + /* Create a new grid that will be filled. #ParticlesToLevelSet requires the background value to + * be positive. It will be set to zero later on. */ + openvdb::FloatGrid::Ptr new_grid = openvdb::FloatGrid::create(1.0f); + + /* Create a narrow-band level set grid based on the positions and radii. */ + openvdb::tools::ParticlesToLevelSet op{*new_grid}; + /* Don't ignore particles based on their radius. */ + op.setRmin(0.0f); + op.setRmax(FLT_MAX); + ParticleList particles{positions, radii}; + op.rasterizeSpheres(particles); + op.finalize(); + + /* Convert the level set to a fog volume. This also sets the background value to zero. Inside the + * fog there will be a density of 1. */ + openvdb::tools::sdfToFogVolume(*new_grid); + + /* Take the desired density into account. */ + openvdb::tools::foreach (new_grid->beginValueOn(), + [&](const openvdb::FloatGrid::ValueOnIter &iter) { + iter.modifyValue([&](float &value) { value *= density; }); + }); + return new_grid; +} + +static float compute_voxel_size(const GeoNodeExecParams ¶ms, + Span<float3> positions, + const float radius) +{ + const NodeGeometryPointsToVolume &storage = node_storage(params.node()); + + if (storage.resolution_mode == GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_SIZE) { + return params.get_input<float>("Voxel Size"); + } + + if (positions.is_empty()) { + return 0.0f; + } + + float3 min, max; + INIT_MINMAX(min, max); + minmax_v3v3_v3_array(min, max, (float(*)[3])positions.data(), positions.size()); + + const float voxel_amount = params.get_input<float>("Voxel Amount"); + if (voxel_amount <= 1) { + return 0.0f; + } + + /* The voxel size adapts to the final size of the volume. */ + const float diagonal = math::distance(min, max); + const float extended_diagonal = diagonal + 2.0f * radius; + const float voxel_size = extended_diagonal / voxel_amount; + return voxel_size; +} + +static void gather_point_data_from_component(GeoNodeExecParams ¶ms, + const GeometryComponent &component, + Vector<float3> &r_positions, + Vector<float> &r_radii) +{ + VArray<float3> positions = component.attribute_get_for_read<float3>( + "position", ATTR_DOMAIN_POINT, {0, 0, 0}); + + Field<float> radius_field = params.get_input<Field<float>>("Radius"); + GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; + const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); + + r_positions.resize(r_positions.size() + domain_size); + positions.materialize(r_positions.as_mutable_span().take_back(domain_size)); + + r_radii.resize(r_radii.size() + domain_size); + fn::FieldEvaluator evaluator{field_context, domain_size}; + evaluator.add_with_destination(radius_field, r_radii.as_mutable_span().take_back(domain_size)); + evaluator.evaluate(); +} + +static void convert_to_grid_index_space(const float voxel_size, + MutableSpan<float3> positions, + MutableSpan<float> radii) +{ + const float voxel_size_inv = 1.0f / voxel_size; + for (const int i : positions.index_range()) { + positions[i] *= voxel_size_inv; + /* Better align generated grid with source points. */ + positions[i] -= float3(0.5f); + radii[i] *= voxel_size_inv; + } +} + +static void initialize_volume_component_from_points(GeoNodeExecParams ¶ms, + GeometrySet &r_geometry_set) +{ + Vector<float3> positions; + Vector<float> radii; + + if (r_geometry_set.has<MeshComponent>()) { + gather_point_data_from_component( + params, *r_geometry_set.get_component_for_read<MeshComponent>(), positions, radii); + } + if (r_geometry_set.has<PointCloudComponent>()) { + gather_point_data_from_component( + params, *r_geometry_set.get_component_for_read<PointCloudComponent>(), positions, radii); + } + if (r_geometry_set.has<CurveComponent>()) { + gather_point_data_from_component( + params, *r_geometry_set.get_component_for_read<CurveComponent>(), positions, radii); + } + + const float max_radius = *std::max_element(radii.begin(), radii.end()); + const float voxel_size = compute_voxel_size(params, positions, max_radius); + if (voxel_size == 0.0f || positions.is_empty()) { + return; + } + + Volume *volume = (Volume *)BKE_id_new_nomain(ID_VO, nullptr); + BKE_volume_init_grids(volume); + + const float density = params.get_input<float>("Density"); + convert_to_grid_index_space(voxel_size, positions, radii); + openvdb::FloatGrid::Ptr new_grid = generate_volume_from_points(positions, radii, density); + new_grid->transform().postScale(voxel_size); + BKE_volume_grid_add_vdb(*volume, "density", std::move(new_grid)); + + r_geometry_set.keep_only({GEO_COMPONENT_TYPE_VOLUME, GEO_COMPONENT_TYPE_INSTANCES}); + r_geometry_set.replace_volume(volume); +} +#endif + +static void node_geo_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Points"); + +#ifdef WITH_OPENVDB + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + initialize_volume_component_from_points(params, geometry_set); + }); + params.set_output("Volume", std::move(geometry_set)); +#else + params.error_message_add(NodeWarningType::Error, + TIP_("Disabled, Blender was compiled without OpenVDB")); + params.set_default_remaining_outputs(); +#endif +} + +} // namespace blender::nodes::node_geo_points_to_volume_cc + +void register_node_type_geo_points_to_volume() +{ + namespace file_ns = blender::nodes::node_geo_points_to_volume_cc; + + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_POINTS_TO_VOLUME, "Points to Volume", NODE_CLASS_GEOMETRY); + node_type_storage(&ntype, + "NodeGeometryPointsToVolume", + node_free_standard_storage, + node_copy_standard_storage); + node_type_size(&ntype, 170, 120, 700); + node_type_init(&ntype, file_ns::node_init); + node_type_update(&ntype, file_ns::node_update); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.draw_buttons = file_ns::node_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_proximity.cc b/source/blender/nodes/geometry/nodes/node_geo_proximity.cc index 2b1de5fbf95..3f509942f7c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_proximity.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_proximity.cc @@ -27,30 +27,33 @@ #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_proximity_cc { -static void geo_node_proximity_declare(NodeDeclarationBuilder &b) +NODE_STORAGE_FUNCS(NodeGeometryProximity) + +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Vector>("Source Position").implicit_field(); - b.add_input<decl::Geometry>("Target"); - b.add_output<decl::Vector>("Position").dependent_field(); - b.add_output<decl::Float>("Distance").dependent_field(); + b.add_input<decl::Geometry>(N_("Target")) + .only_realized_data() + .supported_type({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD}); + b.add_input<decl::Vector>(N_("Source Position")).implicit_field(); + b.add_output<decl::Vector>(N_("Position")).dependent_field(); + b.add_output<decl::Float>(N_("Distance")).dependent_field(); } -static void geo_node_proximity_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "target_element", 0, "", ICON_NONE); } static void geo_proximity_init(bNodeTree *UNUSED(ntree), bNode *node) { - NodeGeometryProximity *node_storage = (NodeGeometryProximity *)MEM_callocN( - sizeof(NodeGeometryProximity), __func__); + NodeGeometryProximity *node_storage = MEM_cnew<NodeGeometryProximity>(__func__); node_storage->target_element = GEO_NODE_PROX_TARGET_FACES; node->storage = node_storage; } -static void calculate_mesh_proximity(const VArray<float3> &positions, +static bool calculate_mesh_proximity(const VArray<float3> &positions, const IndexMask mask, const Mesh &mesh, const GeometryNodeProximityTargetType type, @@ -71,7 +74,7 @@ static void calculate_mesh_proximity(const VArray<float3> &positions, } if (bvh_data.tree == nullptr) { - return; + return false; } threading::parallel_for(mask.index_range(), 512, [&](IndexRange range) { @@ -82,7 +85,7 @@ static void calculate_mesh_proximity(const VArray<float3> &positions, for (int i : range) { const int index = mask[i]; /* Use the distance to the last found point as upper bound to speedup the bvh lookup. */ - nearest.dist_sq = float3::distance_squared(nearest.co, positions[index]); + nearest.dist_sq = math::distance_squared(float3(nearest.co), positions[index]); BLI_bvhtree_find_nearest( bvh_data.tree, positions[index], &nearest, bvh_data.nearest_callback, &bvh_data); @@ -97,18 +100,19 @@ static void calculate_mesh_proximity(const VArray<float3> &positions, }); free_bvhtree_from_mesh(&bvh_data); + return true; } -static void calculate_pointcloud_proximity(const VArray<float3> &positions, +static bool calculate_pointcloud_proximity(const VArray<float3> &positions, const IndexMask mask, const PointCloud &pointcloud, - const MutableSpan<float> r_distances, - const MutableSpan<float3> r_locations) + MutableSpan<float> r_distances, + MutableSpan<float3> r_locations) { BVHTreeFromPointCloud bvh_data; BKE_bvhtree_from_pointcloud_get(&bvh_data, &pointcloud, 2); if (bvh_data.tree == nullptr) { - return; + return false; } threading::parallel_for(mask.index_range(), 512, [&](IndexRange range) { @@ -136,6 +140,7 @@ static void calculate_pointcloud_proximity(const VArray<float3> &positions, }); free_bvhtree_from_pointcloud(&bvh_data); + return true; } class ProximityFunction : public fn::MultiFunction { @@ -172,18 +177,29 @@ class ProximityFunction : public fn::MultiFunction { * comparison per vertex, so it's likely not worth it. */ MutableSpan<float> distances = params.uninitialized_single_output<float>(2, "Distance"); - distances.fill(FLT_MAX); + distances.fill_indices(mask, FLT_MAX); + bool success = false; if (target_.has_mesh()) { - calculate_mesh_proximity( + success |= calculate_mesh_proximity( src_positions, mask, *target_.get_mesh_for_read(), type_, distances, positions); } if (target_.has_pointcloud() && type_ == GEO_NODE_PROX_TARGET_POINTS) { - calculate_pointcloud_proximity( + success |= calculate_pointcloud_proximity( src_positions, mask, *target_.get_pointcloud_for_read(), distances, positions); } + if (!success) { + if (!positions.is_empty()) { + positions.fill_indices(mask, float3(0)); + } + if (!distances.is_empty()) { + distances.fill_indices(mask, 0.0f); + } + return; + } + if (params.single_output_is_required(2, "Distance")) { threading::parallel_for(mask.index_range(), 2048, [&](IndexRange range) { for (const int i : range) { @@ -195,17 +211,17 @@ class ProximityFunction : public fn::MultiFunction { } }; -static void geo_node_proximity_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set_target = params.extract_input<GeometrySet>("Target"); + geometry_set_target.ensure_owns_direct_data(); if (!geometry_set_target.has_mesh() && !geometry_set_target.has_pointcloud()) { - params.set_output("Position", fn::make_constant_field<float3>({0.0f, 0.0f, 0.0f})); - params.set_output("Distance", fn::make_constant_field<float>({0.0f})); + params.set_default_remaining_outputs(); return; } - const NodeGeometryProximity &storage = *(const NodeGeometryProximity *)params.node().storage; + const NodeGeometryProximity &storage = node_storage(params.node()); Field<float3> position_field = params.extract_input<Field<float3>>("Source Position"); auto proximity_fn = std::make_unique<ProximityFunction>( @@ -218,18 +234,20 @@ static void geo_node_proximity_exec(GeoNodeExecParams params) params.set_output("Distance", Field<float>(proximity_op, 1)); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_proximity_cc void register_node_type_geo_proximity() { + namespace file_ns = blender::nodes::node_geo_proximity_cc; + static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_PROXIMITY, "Geometry Proximity", NODE_CLASS_GEOMETRY, 0); - node_type_init(&ntype, blender::nodes::geo_proximity_init); + geo_node_type_base(&ntype, GEO_NODE_PROXIMITY, "Geometry Proximity", NODE_CLASS_GEOMETRY); + node_type_init(&ntype, file_ns::geo_proximity_init); node_type_storage( &ntype, "NodeGeometryProximity", node_free_standard_storage, node_copy_standard_storage); - ntype.declare = blender::nodes::geo_node_proximity_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_proximity_exec; - ntype.draw_buttons = blender::nodes::geo_node_proximity_layout; + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.draw_buttons = file_ns::node_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc new file mode 100644 index 00000000000..c38503f688c --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc @@ -0,0 +1,462 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "DNA_mesh_types.h" + +#include "BKE_attribute_math.hh" +#include "BKE_bvhutils.h" +#include "BKE_mesh_sample.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "NOD_socket_search_link.hh" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_raycast_cc { + +using namespace blender::bke::mesh_surface_sample; + +NODE_STORAGE_FUNCS(NodeGeometryRaycast) + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>(N_("Target Geometry")) + .only_realized_data() + .supported_type(GEO_COMPONENT_TYPE_MESH); + + b.add_input<decl::Vector>(N_("Attribute")).hide_value().supports_field(); + b.add_input<decl::Float>(N_("Attribute"), "Attribute_001").hide_value().supports_field(); + b.add_input<decl::Color>(N_("Attribute"), "Attribute_002").hide_value().supports_field(); + b.add_input<decl::Bool>(N_("Attribute"), "Attribute_003").hide_value().supports_field(); + b.add_input<decl::Int>(N_("Attribute"), "Attribute_004").hide_value().supports_field(); + + b.add_input<decl::Vector>(N_("Source Position")).implicit_field(); + b.add_input<decl::Vector>(N_("Ray Direction")) + .default_value({0.0f, 0.0f, -1.0f}) + .supports_field(); + b.add_input<decl::Float>(N_("Ray Length")) + .default_value(100.0f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .supports_field(); + + b.add_output<decl::Bool>(N_("Is Hit")).dependent_field(); + b.add_output<decl::Vector>(N_("Hit Position")).dependent_field(); + b.add_output<decl::Vector>(N_("Hit Normal")).dependent_field(); + b.add_output<decl::Float>(N_("Hit Distance")).dependent_field(); + + b.add_output<decl::Vector>(N_("Attribute")).dependent_field({1, 2, 3, 4, 5, 6}); + b.add_output<decl::Float>(N_("Attribute"), "Attribute_001").dependent_field({1, 2, 3, 4, 5, 6}); + b.add_output<decl::Color>(N_("Attribute"), "Attribute_002").dependent_field({1, 2, 3, 4, 5, 6}); + b.add_output<decl::Bool>(N_("Attribute"), "Attribute_003").dependent_field({1, 2, 3, 4, 5, 6}); + b.add_output<decl::Int>(N_("Attribute"), "Attribute_004").dependent_field({1, 2, 3, 4, 5, 6}); +} + +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); + uiItemR(layout, ptr, "mapping", 0, "", ICON_NONE); +} + +static void node_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryRaycast *data = MEM_cnew<NodeGeometryRaycast>(__func__); + data->mapping = GEO_NODE_RAYCAST_INTERPOLATED; + data->data_type = CD_PROP_FLOAT; + node->storage = data; +} + +static void node_update(bNodeTree *ntree, bNode *node) +{ + const NodeGeometryRaycast &storage = node_storage(*node); + const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type); + + bNodeSocket *socket_vector = (bNodeSocket *)BLI_findlink(&node->inputs, 1); + bNodeSocket *socket_float = socket_vector->next; + bNodeSocket *socket_color4f = socket_float->next; + bNodeSocket *socket_boolean = socket_color4f->next; + bNodeSocket *socket_int32 = socket_boolean->next; + + nodeSetSocketAvailability(ntree, socket_vector, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(ntree, socket_float, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(ntree, socket_color4f, data_type == CD_PROP_COLOR); + nodeSetSocketAvailability(ntree, socket_boolean, data_type == CD_PROP_BOOL); + nodeSetSocketAvailability(ntree, socket_int32, data_type == CD_PROP_INT32); + + bNodeSocket *out_socket_vector = (bNodeSocket *)BLI_findlink(&node->outputs, 4); + bNodeSocket *out_socket_float = out_socket_vector->next; + bNodeSocket *out_socket_color4f = out_socket_float->next; + bNodeSocket *out_socket_boolean = out_socket_color4f->next; + bNodeSocket *out_socket_int32 = out_socket_boolean->next; + + nodeSetSocketAvailability(ntree, out_socket_vector, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(ntree, out_socket_float, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(ntree, out_socket_color4f, data_type == CD_PROP_COLOR); + nodeSetSocketAvailability(ntree, out_socket_boolean, data_type == CD_PROP_BOOL); + nodeSetSocketAvailability(ntree, out_socket_int32, data_type == CD_PROP_INT32); +} + +static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) +{ + const NodeDeclaration &declaration = *params.node_type().fixed_declaration; + search_link_ops_for_declarations(params, declaration.inputs().take_front(1)); + search_link_ops_for_declarations(params, declaration.inputs().take_back(3)); + search_link_ops_for_declarations(params, declaration.outputs().take_front(4)); + + const std::optional<CustomDataType> type = node_data_type_to_custom_data_type( + (eNodeSocketDatatype)params.other_socket().type); + if (type && *type != CD_PROP_STRING) { + /* The input and output sockets have the same name. */ + params.add_item(IFACE_("Attribute"), [type](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node("GeometryNodeRaycast"); + node_storage(node).data_type = *type; + params.update_and_connect_available_socket(node, "Attribute"); + }); + } +} + +static eAttributeMapMode get_map_mode(GeometryNodeRaycastMapMode map_mode) +{ + switch (map_mode) { + case GEO_NODE_RAYCAST_INTERPOLATED: + return eAttributeMapMode::INTERPOLATED; + default: + case GEO_NODE_RAYCAST_NEAREST: + return eAttributeMapMode::NEAREST; + } +} + +static void raycast_to_mesh(IndexMask mask, + const Mesh &mesh, + const VArray<float3> &ray_origins, + const VArray<float3> &ray_directions, + const VArray<float> &ray_lengths, + const MutableSpan<bool> r_hit, + const MutableSpan<int> r_hit_indices, + const MutableSpan<float3> r_hit_positions, + const MutableSpan<float3> r_hit_normals, + const MutableSpan<float> r_hit_distances, + int &hit_count) +{ + BVHTreeFromMesh tree_data; + BKE_bvhtree_from_mesh_get(&tree_data, &mesh, BVHTREE_FROM_LOOPTRI, 4); + if (tree_data.tree == nullptr) { + free_bvhtree_from_mesh(&tree_data); + return; + } + + for (const int i : mask) { + const float ray_length = ray_lengths[i]; + const float3 ray_origin = ray_origins[i]; + const float3 ray_direction = math::normalize(ray_directions[i]); + + BVHTreeRayHit hit; + hit.index = -1; + hit.dist = ray_length; + if (BLI_bvhtree_ray_cast(tree_data.tree, + ray_origin, + ray_direction, + 0.0f, + &hit, + tree_data.raycast_callback, + &tree_data) != -1) { + hit_count++; + if (!r_hit.is_empty()) { + r_hit[i] = hit.index >= 0; + } + if (!r_hit_indices.is_empty()) { + /* The caller must be able to handle invalid indices anyway, so don't clamp this value. */ + r_hit_indices[i] = hit.index; + } + if (!r_hit_positions.is_empty()) { + r_hit_positions[i] = hit.co; + } + if (!r_hit_normals.is_empty()) { + r_hit_normals[i] = hit.no; + } + if (!r_hit_distances.is_empty()) { + r_hit_distances[i] = hit.dist; + } + } + else { + if (!r_hit.is_empty()) { + r_hit[i] = false; + } + if (!r_hit_indices.is_empty()) { + r_hit_indices[i] = -1; + } + if (!r_hit_positions.is_empty()) { + r_hit_positions[i] = float3(0.0f, 0.0f, 0.0f); + } + if (!r_hit_normals.is_empty()) { + r_hit_normals[i] = float3(0.0f, 0.0f, 0.0f); + } + if (!r_hit_distances.is_empty()) { + r_hit_distances[i] = ray_length; + } + } + } + + /* We shouldn't be rebuilding the BVH tree when calling this function in parallel. */ + BLI_assert(tree_data.cached); + free_bvhtree_from_mesh(&tree_data); +} + +class RaycastFunction : public fn::MultiFunction { + private: + GeometrySet target_; + GeometryNodeRaycastMapMode mapping_; + + /** The field for data evaluated on the target geometry. */ + std::optional<GeometryComponentFieldContext> target_context_; + std::unique_ptr<FieldEvaluator> target_evaluator_; + const GVArray *target_data_ = nullptr; + + /* Always evaluate the target domain data on the face corner domain because it contains the most + * information. Eventually this could be exposed as an option or determined automatically from + * the field inputs for better performance. */ + const AttributeDomain domain_ = ATTR_DOMAIN_CORNER; + + fn::MFSignature signature_; + + public: + RaycastFunction(GeometrySet target, GField src_field, GeometryNodeRaycastMapMode mapping) + : target_(std::move(target)), mapping_((GeometryNodeRaycastMapMode)mapping) + { + target_.ensure_owns_direct_data(); + this->evaluate_target_field(std::move(src_field)); + signature_ = create_signature(); + this->set_signature(&signature_); + } + + fn::MFSignature create_signature() + { + blender::fn::MFSignatureBuilder signature{"Geometry Proximity"}; + signature.single_input<float3>("Source Position"); + signature.single_input<float3>("Ray Direction"); + signature.single_input<float>("Ray Length"); + signature.single_output<bool>("Is Hit"); + signature.single_output<float3>("Hit Position"); + signature.single_output<float3>("Hit Normal"); + signature.single_output<float>("Distance"); + if (target_data_) { + signature.single_output("Attribute", target_data_->type()); + } + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + /* Hit positions are always necessary for retrieving the attribute from the target if that + * output is required, so always retrieve a span from the evaluator in that case (it's + * expected that the evaluator is more likely to have a spare buffer that could be used). */ + MutableSpan<float3> hit_positions = + (target_data_) ? params.uninitialized_single_output<float3>(4, "Hit Position") : + params.uninitialized_single_output_if_required<float3>(4, "Hit Position"); + + Array<int> hit_indices; + if (target_data_) { + hit_indices.reinitialize(mask.min_array_size()); + } + + BLI_assert(target_.has_mesh()); + const Mesh &mesh = *target_.get_mesh_for_read(); + + int hit_count = 0; + raycast_to_mesh(mask, + mesh, + params.readonly_single_input<float3>(0, "Source Position"), + params.readonly_single_input<float3>(1, "Ray Direction"), + params.readonly_single_input<float>(2, "Ray Length"), + params.uninitialized_single_output_if_required<bool>(3, "Is Hit"), + hit_indices, + hit_positions, + params.uninitialized_single_output_if_required<float3>(5, "Hit Normal"), + params.uninitialized_single_output_if_required<float>(6, "Distance"), + hit_count); + + if (target_data_) { + IndexMask hit_mask; + Vector<int64_t> hit_mask_indices; + if (hit_count < mask.size()) { + /* Not all rays hit the target. Create a corrected mask to avoid transferring attribute + * data to invalid indices. An alternative would be handling -1 indices in a separate case + * in #MeshAttributeInterpolator, but since it already has an IndexMask in its constructor, + * it's simpler to use that. */ + hit_mask_indices.reserve(hit_count); + for (const int64_t i : mask) { + if (hit_indices[i] != -1) { + hit_mask_indices.append(i); + } + hit_mask = IndexMask(hit_mask_indices); + } + } + else { + hit_mask = mask; + } + + GMutableSpan result = params.uninitialized_single_output_if_required(7, "Attribute"); + if (!result.is_empty()) { + MeshAttributeInterpolator interp(&mesh, hit_mask, hit_positions, hit_indices); + result.type().fill_assign_indices(result.type().default_value(), result.data(), mask); + interp.sample_data(*target_data_, domain_, get_map_mode(mapping_), result); + } + } + } + + private: + void evaluate_target_field(GField src_field) + { + if (!src_field) { + return; + } + const MeshComponent &mesh_component = *target_.get_component_for_read<MeshComponent>(); + target_context_.emplace(GeometryComponentFieldContext{mesh_component, domain_}); + const int domain_size = mesh_component.attribute_domain_size(domain_); + target_evaluator_ = std::make_unique<FieldEvaluator>(*target_context_, domain_size); + target_evaluator_->add(std::move(src_field)); + target_evaluator_->evaluate(); + target_data_ = &target_evaluator_->get_evaluated(0); + } +}; + +static GField get_input_attribute_field(GeoNodeExecParams ¶ms, const CustomDataType data_type) +{ + switch (data_type) { + case CD_PROP_FLOAT: + if (params.output_is_required("Attribute_001")) { + return params.extract_input<Field<float>>("Attribute_001"); + } + break; + case CD_PROP_FLOAT3: + if (params.output_is_required("Attribute")) { + return params.extract_input<Field<float3>>("Attribute"); + } + break; + case CD_PROP_COLOR: + if (params.output_is_required("Attribute_002")) { + return params.extract_input<Field<ColorGeometry4f>>("Attribute_002"); + } + break; + case CD_PROP_BOOL: + if (params.output_is_required("Attribute_003")) { + return params.extract_input<Field<bool>>("Attribute_003"); + } + break; + case CD_PROP_INT32: + if (params.output_is_required("Attribute_004")) { + return params.extract_input<Field<int>>("Attribute_004"); + } + break; + default: + BLI_assert_unreachable(); + } + return {}; +} + +static void output_attribute_field(GeoNodeExecParams ¶ms, GField field) +{ + switch (bke::cpp_type_to_custom_data_type(field.cpp_type())) { + case CD_PROP_FLOAT: { + params.set_output("Attribute_001", Field<float>(field)); + break; + } + case CD_PROP_FLOAT3: { + params.set_output("Attribute", Field<float3>(field)); + break; + } + case CD_PROP_COLOR: { + params.set_output("Attribute_002", Field<ColorGeometry4f>(field)); + break; + } + case CD_PROP_BOOL: { + params.set_output("Attribute_003", Field<bool>(field)); + break; + } + case CD_PROP_INT32: { + params.set_output("Attribute_004", Field<int>(field)); + break; + } + default: + break; + } +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + GeometrySet target = params.extract_input<GeometrySet>("Target Geometry"); + const NodeGeometryRaycast &storage = node_storage(params.node()); + const GeometryNodeRaycastMapMode mapping = (GeometryNodeRaycastMapMode)storage.mapping; + const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type); + + if (target.is_empty()) { + params.set_default_remaining_outputs(); + return; + } + + if (!target.has_mesh()) { + params.set_default_remaining_outputs(); + return; + } + + if (target.get_mesh_for_read()->totpoly == 0) { + params.error_message_add(NodeWarningType::Error, TIP_("The target mesh must have faces")); + params.set_default_remaining_outputs(); + return; + } + + GField field = get_input_attribute_field(params, data_type); + const bool do_attribute_transfer = bool(field); + Field<float3> position_field = params.extract_input<Field<float3>>("Source Position"); + Field<float3> direction_field = params.extract_input<Field<float3>>("Ray Direction"); + Field<float> length_field = params.extract_input<Field<float>>("Ray Length"); + + auto fn = std::make_unique<RaycastFunction>(std::move(target), std::move(field), mapping); + auto op = std::make_shared<FieldOperation>(FieldOperation( + std::move(fn), + {std::move(position_field), std::move(direction_field), std::move(length_field)})); + + params.set_output("Is Hit", Field<bool>(op, 0)); + params.set_output("Hit Position", Field<float3>(op, 1)); + params.set_output("Hit Normal", Field<float3>(op, 2)); + params.set_output("Hit Distance", Field<float>(op, 3)); + if (do_attribute_transfer) { + output_attribute_field(params, GField(op, 4)); + } +} + +} // namespace blender::nodes::node_geo_raycast_cc + +void register_node_type_geo_raycast() +{ + namespace file_ns = blender::nodes::node_geo_raycast_cc; + + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_RAYCAST, "Raycast", NODE_CLASS_GEOMETRY); + node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); + node_type_init(&ntype, file_ns::node_init); + node_type_update(&ntype, file_ns::node_update); + node_type_storage( + &ntype, "NodeGeometryRaycast", node_free_standard_storage, node_copy_standard_storage); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.draw_buttons = file_ns::node_layout; + ntype.gather_link_search_ops = file_ns::node_gather_link_searches; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_realize_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_realize_instances.cc index 3be79d5ba3b..48b88705ed2 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_realize_instances.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_realize_instances.cc @@ -16,33 +16,47 @@ #include "node_geometry_util.hh" +#include "GEO_realize_instances.hh" + #include "UI_interface.h" #include "UI_resources.h" -namespace blender::nodes { +namespace blender::nodes::node_geo_realize_instances_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_output<decl::Geometry>(N_("Geometry")); +} -static void geo_node_realize_instances_declare(NodeDeclarationBuilder &b) +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - b.add_input<decl::Geometry>("Geometry"); - b.add_output<decl::Geometry>("Geometry"); + uiItemR(layout, ptr, "legacy_behavior", 0, nullptr, ICON_NONE); } -static void geo_node_realize_instances_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { + const bool legacy_behavior = params.node().custom1 & GEO_NODE_REALIZE_INSTANCES_LEGACY_BEHAVIOR; + GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - geometry_set = bke::geometry_set_realize_instances(geometry_set); + geometry::RealizeInstancesOptions options; + options.keep_original_ids = legacy_behavior; + options.realize_instance_attributes = !legacy_behavior; + geometry_set = geometry::realize_instances(geometry_set, options); params.set_output("Geometry", std::move(geometry_set)); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_realize_instances_cc void register_node_type_geo_realize_instances() { + namespace file_ns = blender::nodes::node_geo_realize_instances_cc; + static bNodeType ntype; - geo_node_type_base( - &ntype, GEO_NODE_REALIZE_INSTANCES, "Realize Instances", NODE_CLASS_GEOMETRY, 0); - ntype.declare = blender::nodes::geo_node_realize_instances_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_realize_instances_exec; + geo_node_type_base(&ntype, GEO_NODE_REALIZE_INSTANCES, "Realize Instances", NODE_CLASS_GEOMETRY); + ntype.declare = file_ns::node_declare; + ntype.draw_buttons_ex = file_ns::node_layout; + ntype.geometry_node_execute = file_ns::node_geo_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc new file mode 100644 index 00000000000..7d5c5b77ffd --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc @@ -0,0 +1,119 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_task.hh" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_rotate_instances_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>(N_("Instances")).only_instances(); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input<decl::Vector>(N_("Rotation")).subtype(PROP_EULER).supports_field(); + b.add_input<decl::Vector>(N_("Pivot Point")).subtype(PROP_TRANSLATION).supports_field(); + b.add_input<decl::Bool>(N_("Local Space")).default_value(true).supports_field(); + b.add_output<decl::Geometry>(N_("Instances")); +} + +static void rotate_instances(GeoNodeExecParams ¶ms, InstancesComponent &instances_component) +{ + GeometryComponentFieldContext field_context{instances_component, ATTR_DOMAIN_INSTANCE}; + const int domain_size = instances_component.instances_amount(); + + fn::FieldEvaluator evaluator{field_context, domain_size}; + evaluator.set_selection(params.extract_input<Field<bool>>("Selection")); + evaluator.add(params.extract_input<Field<float3>>("Rotation")); + evaluator.add(params.extract_input<Field<float3>>("Pivot Point")); + evaluator.add(params.extract_input<Field<bool>>("Local Space")); + evaluator.evaluate(); + + const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); + const VArray<float3> &rotations = evaluator.get_evaluated<float3>(0); + const VArray<float3> &pivots = evaluator.get_evaluated<float3>(1); + const VArray<bool> &local_spaces = evaluator.get_evaluated<bool>(2); + + MutableSpan<float4x4> instance_transforms = instances_component.instance_transforms(); + + threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) { + for (const int i_selection : range) { + const int i = selection[i_selection]; + const float3 pivot = pivots[i]; + const float3 euler = rotations[i]; + float4x4 &instance_transform = instance_transforms[i]; + + float4x4 rotation_matrix; + float3 used_pivot; + + if (local_spaces[i]) { + /* Find rotation axis from the matrix. This should work even if the instance is skewed. */ + const float3 rotation_axis_x = instance_transform.values[0]; + const float3 rotation_axis_y = instance_transform.values[1]; + const float3 rotation_axis_z = instance_transform.values[2]; + + /* Create rotations around the individual axis. This could be optimized to skip some axis + * when the angle is zero. */ + float rotation_x[3][3], rotation_y[3][3], rotation_z[3][3]; + axis_angle_to_mat3(rotation_x, rotation_axis_x, euler.x); + axis_angle_to_mat3(rotation_y, rotation_axis_y, euler.y); + axis_angle_to_mat3(rotation_z, rotation_axis_z, euler.z); + + /* Combine the previously computed rotations into the final rotation matrix. */ + float rotation[3][3]; + mul_m3_series(rotation, rotation_z, rotation_y, rotation_x); + copy_m4_m3(rotation_matrix.values, rotation); + + /* Transform the passed in pivot into the local space of the instance. */ + used_pivot = instance_transform * pivot; + } + else { + used_pivot = pivot; + eul_to_mat4(rotation_matrix.values, euler); + } + /* Move the pivot to the origin so that we can rotate around it. */ + sub_v3_v3(instance_transform.values[3], used_pivot); + /* Perform the actual rotation. */ + mul_m4_m4_pre(instance_transform.values, rotation_matrix.values); + /* Undo the pivot shifting done before. */ + add_v3_v3(instance_transform.values[3], used_pivot); + } + }); +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Instances"); + if (geometry_set.has_instances()) { + InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>(); + rotate_instances(params, instances); + } + params.set_output("Instances", std::move(geometry_set)); +} + +} // namespace blender::nodes::node_geo_rotate_instances_cc + +void register_node_type_geo_rotate_instances() +{ + namespace file_ns = blender::nodes::node_geo_rotate_instances_cc; + + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_ROTATE_INSTANCES, "Rotate Instances", NODE_CLASS_GEOMETRY); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.declare = file_ns::node_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_scale_elements.cc b/source/blender/nodes/geometry/nodes/node_geo_scale_elements.cc new file mode 100644 index 00000000000..aaa2c156442 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_scale_elements.cc @@ -0,0 +1,485 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_array.hh" +#include "BLI_disjoint_set.hh" +#include "BLI_task.hh" +#include "BLI_vector.hh" +#include "BLI_vector_set.hh" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "BKE_mesh.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_scale_elements_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>(N_("Geometry")).supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input<decl::Float>(N_("Scale"), "Scale").default_value(1.0f).min(0.0f).supports_field(); + b.add_input<decl::Vector>(N_("Center")) + .subtype(PROP_TRANSLATION) + .implicit_field() + .description(N_("Origin of the scaling for each element. If multiple elements are " + "connected, their center is averaged")); + b.add_input<decl::Vector>(N_("Axis")) + .default_value({1.0f, 0.0f, 0.0f}) + .supports_field() + .description(N_("Direction in which to scale the element")); + b.add_output<decl::Geometry>(N_("Geometry")); +}; + +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "domain", 0, "", ICON_NONE); + uiItemR(layout, ptr, "scale_mode", 0, "", ICON_NONE); +} + +static void node_init(bNodeTree *UNUSED(tree), bNode *node) +{ + node->custom1 = ATTR_DOMAIN_FACE; + node->custom2 = GEO_NODE_SCALE_ELEMENTS_UNIFORM; +} + +static void node_update(bNodeTree *ntree, bNode *node) +{ + bNodeSocket *geometry_socket = static_cast<bNodeSocket *>(node->inputs.first); + bNodeSocket *selection_socket = geometry_socket->next; + bNodeSocket *scale_float_socket = selection_socket->next; + bNodeSocket *center_socket = scale_float_socket->next; + bNodeSocket *axis_socket = center_socket->next; + + const GeometryNodeScaleElementsMode mode = static_cast<GeometryNodeScaleElementsMode>( + node->custom2); + const bool use_single_axis = mode == GEO_NODE_SCALE_ELEMENTS_SINGLE_AXIS; + + nodeSetSocketAvailability(ntree, axis_socket, use_single_axis); +} + +struct UniformScaleFields { + Field<bool> selection; + Field<float> scale; + Field<float3> center; +}; + +struct UniformScaleParams { + IndexMask selection; + VArray<float> scales; + VArray<float3> centers; +}; + +struct AxisScaleFields { + Field<bool> selection; + Field<float> scale; + Field<float3> center; + Field<float3> axis; +}; + +struct AxisScaleParams { + IndexMask selection; + VArray<float> scales; + VArray<float3> centers; + VArray<float3> axis_vectors; +}; + +/** + * When multiple elements share the same vertices, they are scaled together. + */ +struct ElementIsland { + /* Either face or edge indices. */ + Vector<int> element_indices; +}; + +static float3 transform_with_uniform_scale(const float3 &position, + const float3 ¢er, + const float scale) +{ + const float3 diff = position - center; + const float3 scaled_diff = scale * diff; + const float3 new_position = center + scaled_diff; + return new_position; +} + +static float4x4 create_single_axis_transform(const float3 ¢er, + const float3 &axis, + const float scale) +{ + /* Scale along x axis. The other axis need to be orthogonal, but their specific value does not + * matter. */ + const float3 x_axis = math::normalize(axis); + float3 y_axis = math::cross(x_axis, float3(0.0f, 0.0f, 1.0f)); + if (math::is_zero(y_axis)) { + y_axis = math::cross(x_axis, float3(0.0f, 1.0f, 0.0f)); + } + y_axis = math::normalize(y_axis); + const float3 z_axis = math::cross(x_axis, y_axis); + + float4x4 transform = float4x4::identity(); + + /* Move scaling center to the origin. */ + sub_v3_v3(transform.values[3], center); + + /* `base_change` and `base_change_inv` are used to rotate space so that scaling along the + * provided axis is the same as scaling along the x axis. */ + float4x4 base_change = float4x4::identity(); + copy_v3_v3(base_change.values[0], x_axis); + copy_v3_v3(base_change.values[1], y_axis); + copy_v3_v3(base_change.values[2], z_axis); + + /* Can invert by transposing, because the matrix is orthonormal. */ + float4x4 base_change_inv = base_change.transposed(); + + float4x4 scale_transform = float4x4::identity(); + scale_transform.values[0][0] = scale; + + transform = base_change * scale_transform * base_change_inv * transform; + + /* Move scaling center back to where it was. */ + add_v3_v3(transform.values[3], center); + + return transform; +} + +using GetVertexIndicesFn = + FunctionRef<void(const Mesh &mesh, int element_index, VectorSet<int> &r_vertex_indices)>; + +static void scale_vertex_islands_uniformly(Mesh &mesh, + const Span<ElementIsland> islands, + const UniformScaleParams ¶ms, + const GetVertexIndicesFn get_vertex_indices) +{ + threading::parallel_for(islands.index_range(), 256, [&](const IndexRange range) { + for (const int island_index : range) { + const ElementIsland &island = islands[island_index]; + + float scale = 0.0f; + float3 center = {0.0f, 0.0f, 0.0f}; + + VectorSet<int> vertex_indices; + for (const int poly_index : island.element_indices) { + get_vertex_indices(mesh, poly_index, vertex_indices); + center += params.centers[poly_index]; + scale += params.scales[poly_index]; + } + + /* Divide by number of elements to get the average. */ + const float f = 1.0f / island.element_indices.size(); + scale *= f; + center *= f; + + for (const int vert_index : vertex_indices) { + MVert &vert = mesh.mvert[vert_index]; + const float3 old_position = vert.co; + const float3 new_position = transform_with_uniform_scale(old_position, center, scale); + copy_v3_v3(vert.co, new_position); + } + } + }); + + /* Positions have changed, so the normals will have to be recomputed. */ + BKE_mesh_normals_tag_dirty(&mesh); +} + +static void scale_vertex_islands_on_axis(Mesh &mesh, + const Span<ElementIsland> islands, + const AxisScaleParams ¶ms, + const GetVertexIndicesFn get_vertex_indices) +{ + threading::parallel_for(islands.index_range(), 256, [&](const IndexRange range) { + for (const int island_index : range) { + const ElementIsland &island = islands[island_index]; + + float scale = 0.0f; + float3 center = {0.0f, 0.0f, 0.0f}; + float3 axis = {0.0f, 0.0f, 0.0f}; + + VectorSet<int> vertex_indices; + for (const int poly_index : island.element_indices) { + get_vertex_indices(mesh, poly_index, vertex_indices); + center += params.centers[poly_index]; + scale += params.scales[poly_index]; + axis += params.axis_vectors[poly_index]; + } + + /* Divide by number of elements to get the average. */ + const float f = 1.0f / island.element_indices.size(); + scale *= f; + center *= f; + axis *= f; + + if (math::is_zero(axis)) { + axis = float3(1.0f, 0.0f, 0.0f); + } + + const float4x4 transform = create_single_axis_transform(center, axis, scale); + for (const int vert_index : vertex_indices) { + MVert &vert = mesh.mvert[vert_index]; + const float3 old_position = vert.co; + const float3 new_position = transform * old_position; + copy_v3_v3(vert.co, new_position); + } + } + }); + + /* Positions have changed, so the normals will have to be recomputed. */ + BKE_mesh_normals_tag_dirty(&mesh); +} + +static Vector<ElementIsland> prepare_face_islands(const Mesh &mesh, const IndexMask face_selection) +{ + /* Use the disjoint set data structure to determine which vertices have to be scaled together. */ + DisjointSet disjoint_set(mesh.totvert); + for (const int poly_index : face_selection) { + const MPoly &poly = mesh.mpoly[poly_index]; + const Span<MLoop> poly_loops{mesh.mloop + poly.loopstart, poly.totloop}; + for (const int loop_index : IndexRange(poly.totloop - 1)) { + const int v1 = poly_loops[loop_index].v; + const int v2 = poly_loops[loop_index + 1].v; + disjoint_set.join(v1, v2); + } + disjoint_set.join(poly_loops.first().v, poly_loops.last().v); + } + + VectorSet<int> island_ids; + Vector<ElementIsland> islands; + /* There are at most as many islands as there are selected faces. */ + islands.reserve(face_selection.size()); + + /* Gather all of the face indices in each island into separate vectors. */ + for (const int poly_index : face_selection) { + const MPoly &poly = mesh.mpoly[poly_index]; + const Span<MLoop> poly_loops{mesh.mloop + poly.loopstart, poly.totloop}; + const int island_id = disjoint_set.find_root(poly_loops[0].v); + const int island_index = island_ids.index_of_or_add(island_id); + if (island_index == islands.size()) { + islands.append_as(); + } + ElementIsland &island = islands[island_index]; + island.element_indices.append(poly_index); + } + + return islands; +} + +static void get_face_vertices(const Mesh &mesh, int face_index, VectorSet<int> &r_vertex_indices) +{ + const MPoly &poly = mesh.mpoly[face_index]; + const Span<MLoop> poly_loops{mesh.mloop + poly.loopstart, poly.totloop}; + for (const MLoop &loop : poly_loops) { + r_vertex_indices.add(loop.v); + } +} + +static AxisScaleParams evaluate_axis_scale_fields(FieldEvaluator &evaluator, + const AxisScaleFields &fields) +{ + AxisScaleParams out; + evaluator.set_selection(fields.selection); + evaluator.add(fields.scale, &out.scales); + evaluator.add(fields.center, &out.centers); + evaluator.add(fields.axis, &out.axis_vectors); + evaluator.evaluate(); + out.selection = evaluator.get_evaluated_selection_as_mask(); + return out; +} + +static void scale_faces_on_axis(MeshComponent &mesh_component, const AxisScaleFields &fields) +{ + Mesh &mesh = *mesh_component.get_for_write(); + mesh.mvert = static_cast<MVert *>( + CustomData_duplicate_referenced_layer(&mesh.vdata, CD_MVERT, mesh.totvert)); + + GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_FACE}; + FieldEvaluator evaluator{field_context, mesh.totpoly}; + AxisScaleParams params = evaluate_axis_scale_fields(evaluator, fields); + + Vector<ElementIsland> island = prepare_face_islands(mesh, params.selection); + scale_vertex_islands_on_axis(mesh, island, params, get_face_vertices); +} + +static UniformScaleParams evaluate_uniform_scale_fields(FieldEvaluator &evaluator, + const UniformScaleFields &fields) +{ + UniformScaleParams out; + evaluator.set_selection(fields.selection); + evaluator.add(fields.scale, &out.scales); + evaluator.add(fields.center, &out.centers); + evaluator.evaluate(); + out.selection = evaluator.get_evaluated_selection_as_mask(); + return out; +} + +static void scale_faces_uniformly(MeshComponent &mesh_component, const UniformScaleFields &fields) +{ + Mesh &mesh = *mesh_component.get_for_write(); + mesh.mvert = static_cast<MVert *>( + CustomData_duplicate_referenced_layer(&mesh.vdata, CD_MVERT, mesh.totvert)); + + GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_FACE}; + FieldEvaluator evaluator{field_context, mesh.totpoly}; + UniformScaleParams params = evaluate_uniform_scale_fields(evaluator, fields); + + Vector<ElementIsland> island = prepare_face_islands(mesh, params.selection); + scale_vertex_islands_uniformly(mesh, island, params, get_face_vertices); +} + +static Vector<ElementIsland> prepare_edge_islands(const Mesh &mesh, const IndexMask edge_selection) +{ + /* Use the disjoint set data structure to determine which vertices have to be scaled together. */ + DisjointSet disjoint_set(mesh.totvert); + for (const int edge_index : edge_selection) { + const MEdge &edge = mesh.medge[edge_index]; + disjoint_set.join(edge.v1, edge.v2); + } + + VectorSet<int> island_ids; + Vector<ElementIsland> islands; + /* There are at most as many islands as there are selected edges. */ + islands.reserve(edge_selection.size()); + + /* Gather all of the edge indices in each island into separate vectors. */ + for (const int edge_index : edge_selection) { + const MEdge &edge = mesh.medge[edge_index]; + const int island_id = disjoint_set.find_root(edge.v1); + const int island_index = island_ids.index_of_or_add(island_id); + if (island_index == islands.size()) { + islands.append_as(); + } + ElementIsland &island = islands[island_index]; + island.element_indices.append(edge_index); + } + + return islands; +} + +static void get_edge_vertices(const Mesh &mesh, int edge_index, VectorSet<int> &r_vertex_indices) +{ + const MEdge &edge = mesh.medge[edge_index]; + r_vertex_indices.add(edge.v1); + r_vertex_indices.add(edge.v2); +} + +static void scale_edges_uniformly(MeshComponent &mesh_component, const UniformScaleFields &fields) +{ + Mesh &mesh = *mesh_component.get_for_write(); + mesh.mvert = static_cast<MVert *>( + CustomData_duplicate_referenced_layer(&mesh.vdata, CD_MVERT, mesh.totvert)); + + GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_EDGE}; + FieldEvaluator evaluator{field_context, mesh.totedge}; + UniformScaleParams params = evaluate_uniform_scale_fields(evaluator, fields); + + Vector<ElementIsland> island = prepare_edge_islands(mesh, params.selection); + scale_vertex_islands_uniformly(mesh, island, params, get_edge_vertices); +} + +static void scale_edges_on_axis(MeshComponent &mesh_component, const AxisScaleFields &fields) +{ + Mesh &mesh = *mesh_component.get_for_write(); + mesh.mvert = static_cast<MVert *>( + CustomData_duplicate_referenced_layer(&mesh.vdata, CD_MVERT, mesh.totvert)); + + GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_EDGE}; + FieldEvaluator evaluator{field_context, mesh.totedge}; + AxisScaleParams params = evaluate_axis_scale_fields(evaluator, fields); + + Vector<ElementIsland> island = prepare_edge_islands(mesh, params.selection); + scale_vertex_islands_on_axis(mesh, island, params, get_edge_vertices); +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + const bNode &node = params.node(); + const AttributeDomain domain = static_cast<AttributeDomain>(node.custom1); + const GeometryNodeScaleElementsMode scale_mode = static_cast<GeometryNodeScaleElementsMode>( + node.custom2); + + GeometrySet geometry = params.extract_input<GeometrySet>("Geometry"); + + Field<bool> selection_field = params.get_input<Field<bool>>("Selection"); + Field<float> scale_field = params.get_input<Field<float>>("Scale"); + Field<float3> center_field = params.get_input<Field<float3>>("Center"); + Field<float3> axis_field; + if (scale_mode == GEO_NODE_SCALE_ELEMENTS_SINGLE_AXIS) { + axis_field = params.get_input<Field<float3>>("Axis"); + } + + geometry.modify_geometry_sets([&](GeometrySet &geometry) { + if (!geometry.has_mesh()) { + return; + } + MeshComponent &mesh_component = geometry.get_component_for_write<MeshComponent>(); + switch (domain) { + case ATTR_DOMAIN_FACE: { + switch (scale_mode) { + case GEO_NODE_SCALE_ELEMENTS_UNIFORM: { + scale_faces_uniformly(mesh_component, {selection_field, scale_field, center_field}); + break; + } + case GEO_NODE_SCALE_ELEMENTS_SINGLE_AXIS: { + scale_faces_on_axis(mesh_component, + {selection_field, scale_field, center_field, axis_field}); + break; + } + } + break; + } + case ATTR_DOMAIN_EDGE: { + switch (scale_mode) { + case GEO_NODE_SCALE_ELEMENTS_UNIFORM: { + scale_edges_uniformly(mesh_component, {selection_field, scale_field, center_field}); + break; + } + case GEO_NODE_SCALE_ELEMENTS_SINGLE_AXIS: { + scale_edges_on_axis(mesh_component, + {selection_field, scale_field, center_field, axis_field}); + break; + } + } + break; + } + default: + BLI_assert_unreachable(); + break; + } + }); + + params.set_output("Geometry", std::move(geometry)); +} + +} // namespace blender::nodes::node_geo_scale_elements_cc + +void register_node_type_geo_scale_elements() +{ + namespace file_ns = blender::nodes::node_geo_scale_elements_cc; + + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_SCALE_ELEMENTS, "Scale Elements", NODE_CLASS_GEOMETRY); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.declare = file_ns::node_declare; + ntype.draw_buttons = file_ns::node_layout; + ntype.initfunc = file_ns::node_init; + ntype.updatefunc = file_ns::node_update; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc new file mode 100644 index 00000000000..5bd2028ff41 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc @@ -0,0 +1,98 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_task.hh" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_scale_instances_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>(N_("Instances")).only_instances(); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input<decl::Vector>(N_("Scale")) + .subtype(PROP_XYZ) + .default_value({1, 1, 1}) + .supports_field(); + b.add_input<decl::Vector>(N_("Center")).subtype(PROP_TRANSLATION).supports_field(); + b.add_input<decl::Bool>(N_("Local Space")).default_value(true).supports_field(); + b.add_output<decl::Geometry>(N_("Instances")); +} + +static void scale_instances(GeoNodeExecParams ¶ms, InstancesComponent &instances_component) +{ + GeometryComponentFieldContext field_context{instances_component, ATTR_DOMAIN_INSTANCE}; + + fn::FieldEvaluator evaluator{field_context, instances_component.instances_amount()}; + evaluator.set_selection(params.extract_input<Field<bool>>("Selection")); + evaluator.add(params.extract_input<Field<float3>>("Scale")); + evaluator.add(params.extract_input<Field<float3>>("Center")); + evaluator.add(params.extract_input<Field<bool>>("Local Space")); + evaluator.evaluate(); + + const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); + const VArray<float3> &scales = evaluator.get_evaluated<float3>(0); + const VArray<float3> &pivots = evaluator.get_evaluated<float3>(1); + const VArray<bool> &local_spaces = evaluator.get_evaluated<bool>(2); + + MutableSpan<float4x4> instance_transforms = instances_component.instance_transforms(); + + threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) { + for (const int i_selection : range) { + const int i = selection[i_selection]; + const float3 pivot = pivots[i]; + float4x4 &instance_transform = instance_transforms[i]; + + if (local_spaces[i]) { + instance_transform *= float4x4::from_location(pivot); + rescale_m4(instance_transform.values, scales[i]); + instance_transform *= float4x4::from_location(-pivot); + } + else { + const float4x4 original_transform = instance_transform; + instance_transform = float4x4::from_location(pivot); + rescale_m4(instance_transform.values, scales[i]); + instance_transform *= float4x4::from_location(-pivot); + instance_transform *= original_transform; + } + } + }); +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Instances"); + if (geometry_set.has_instances()) { + InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>(); + scale_instances(params, instances); + } + params.set_output("Instances", std::move(geometry_set)); +} + +} // namespace blender::nodes::node_geo_scale_instances_cc + +void register_node_type_geo_scale_instances() +{ + namespace file_ns = blender::nodes::node_geo_scale_instances_cc; + + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_SCALE_INSTANCES, "Scale Instances", NODE_CLASS_GEOMETRY); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.declare = file_ns::node_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_separate_components.cc b/source/blender/nodes/geometry/nodes/node_geo_separate_components.cc index dafd10cee2d..3e34378d3e0 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_separate_components.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_separate_components.cc @@ -16,19 +16,19 @@ #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_separate_components_cc { -static void geo_node_join_geometry_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_output<decl::Geometry>("Mesh"); - b.add_output<decl::Geometry>("Point Cloud"); - b.add_output<decl::Geometry>("Curve"); - b.add_output<decl::Geometry>("Volume"); - b.add_output<decl::Geometry>("Instances"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_output<decl::Geometry>(N_("Mesh")); + b.add_output<decl::Geometry>(N_("Point Cloud")); + b.add_output<decl::Geometry>(N_("Curve")); + b.add_output<decl::Geometry>(N_("Volume")); + b.add_output<decl::Geometry>(N_("Instances")); } -static void geo_node_separate_components_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); @@ -61,15 +61,17 @@ static void geo_node_separate_components_exec(GeoNodeExecParams params) params.set_output("Instances", instances); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_separate_components_cc void register_node_type_geo_separate_components() { + namespace file_ns = blender::nodes::node_geo_separate_components_cc; + static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_SEPARATE_COMPONENTS, "Separate Components", NODE_CLASS_GEOMETRY, 0); - ntype.declare = blender::nodes::geo_node_join_geometry_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_separate_components_exec; + &ntype, GEO_NODE_SEPARATE_COMPONENTS, "Separate Components", NODE_CLASS_GEOMETRY); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_separate_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_separate_geometry.cc new file mode 100644 index 00000000000..fec1ac1363e --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_separate_geometry.cc @@ -0,0 +1,117 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_separate_geometry_cc { + +NODE_STORAGE_FUNCS(NodeGeometrySeparateGeometry) + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::Bool>(N_("Selection")) + .default_value(true) + .hide_value() + .supports_field() + .description(N_("The parts of the geometry that go into the first output")); + b.add_output<decl::Geometry>(N_("Selection")) + .description(N_("The parts of the geometry in the selection")); + b.add_output<decl::Geometry>(N_("Inverted")) + .description(N_("The parts of the geometry not in the selection")); +} + +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "domain", 0, "", ICON_NONE); +} + +static void node_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometrySeparateGeometry *data = MEM_cnew<NodeGeometrySeparateGeometry>(__func__); + data->domain = ATTR_DOMAIN_POINT; + + node->storage = data; +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + + const Field<bool> selection_field = params.extract_input<Field<bool>>("Selection"); + + const NodeGeometrySeparateGeometry &storage = node_storage(params.node()); + const AttributeDomain domain = static_cast<AttributeDomain>(storage.domain); + + auto separate_geometry_maybe_recursively = [&](GeometrySet &geometry_set, bool invert) { + bool is_error; + if (domain == ATTR_DOMAIN_INSTANCE) { + /* Only delete top level instances. */ + separate_geometry(geometry_set, + domain, + GEO_NODE_DELETE_GEOMETRY_MODE_ALL, + selection_field, + invert, + is_error); + } + else { + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + separate_geometry(geometry_set, + domain, + GEO_NODE_DELETE_GEOMETRY_MODE_ALL, + selection_field, + invert, + is_error); + }); + } + }; + + GeometrySet second_set(geometry_set); + if (params.output_is_required("Selection")) { + separate_geometry_maybe_recursively(geometry_set, false); + params.set_output("Selection", std::move(geometry_set)); + } + if (params.output_is_required("Inverted")) { + separate_geometry_maybe_recursively(second_set, true); + params.set_output("Inverted", std::move(second_set)); + } +} + +} // namespace blender::nodes::node_geo_separate_geometry_cc + +void register_node_type_geo_separate_geometry() +{ + namespace file_ns = blender::nodes::node_geo_separate_geometry_cc; + + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_SEPARATE_GEOMETRY, "Separate Geometry", NODE_CLASS_GEOMETRY); + + node_type_storage(&ntype, + "NodeGeometrySeparateGeometry", + node_free_standard_storage, + node_copy_standard_storage); + + node_type_init(&ntype, file_ns::node_init); + + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.draw_buttons = file_ns::node_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc new file mode 100644 index 00000000000..82d09bbc208 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc @@ -0,0 +1,181 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BKE_spline.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_set_curve_handles_cc { + +NODE_STORAGE_FUNCS(NodeGeometrySetCurveHandlePositions) + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input<decl::Vector>(N_("Position")).implicit_field(); + b.add_input<decl::Vector>(N_("Offset")).default_value(float3(0.0f, 0.0f, 0.0f)).supports_field(); + b.add_output<decl::Geometry>(N_("Curve")); +} + +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); +} + +static void node_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometrySetCurveHandlePositions *data = MEM_cnew<NodeGeometrySetCurveHandlePositions>( + __func__); + + data->mode = GEO_NODE_CURVE_HANDLE_LEFT; + node->storage = data; +} + +static void set_position_in_component(const GeometryNodeCurveHandleMode mode, + GeometryComponent &component, + const Field<bool> &selection_field, + const Field<float3> &position_field, + const Field<float3> &offset_field) +{ + GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; + const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); + if (domain_size == 0) { + return; + } + + fn::FieldEvaluator evaluator{field_context, domain_size}; + evaluator.set_selection(selection_field); + evaluator.add(position_field); + evaluator.add(offset_field); + evaluator.evaluate(); + const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); + + CurveComponent *curve_component = static_cast<CurveComponent *>(&component); + CurveEval *curve = curve_component->get_for_write(); + + StringRef side = mode & GEO_NODE_CURVE_HANDLE_LEFT ? "handle_left" : "handle_right"; + + int current_point = 0; + int current_mask = 0; + + for (const SplinePtr &spline : curve->splines()) { + if (spline->type() == Spline::Type::Bezier) { + BezierSpline &bezier = static_cast<BezierSpline &>(*spline); + for (int i : bezier.positions().index_range()) { + if (current_mask < selection.size() && selection[current_mask] == current_point) { + if (mode & GEO_NODE_CURVE_HANDLE_LEFT) { + if (bezier.handle_types_left()[i] == BezierSpline::HandleType::Vector) { + bezier.ensure_auto_handles(); + bezier.handle_types_left()[i] = BezierSpline::HandleType::Free; + } + else if (bezier.handle_types_left()[i] == BezierSpline::HandleType::Auto) { + bezier.ensure_auto_handles(); + bezier.handle_types_left()[i] = BezierSpline::HandleType::Align; + } + } + else { + if (bezier.handle_types_right()[i] == BezierSpline::HandleType::Vector) { + bezier.ensure_auto_handles(); + bezier.handle_types_right()[i] = BezierSpline::HandleType::Free; + } + else if (bezier.handle_types_right()[i] == BezierSpline::HandleType::Auto) { + bezier.ensure_auto_handles(); + bezier.handle_types_right()[i] = BezierSpline::HandleType::Align; + } + } + current_mask++; + } + current_point++; + } + } + else { + for ([[maybe_unused]] int i : spline->positions().index_range()) { + if (current_mask < selection.size() && selection[current_mask] == current_point) { + current_mask++; + } + current_point++; + } + } + } + + const VArray<float3> &positions_input = evaluator.get_evaluated<float3>(0); + const VArray<float3> &offsets_input = evaluator.get_evaluated<float3>(1); + + OutputAttribute_Typed<float3> positions = component.attribute_try_get_for_output<float3>( + side, ATTR_DOMAIN_POINT, {0, 0, 0}); + MutableSpan<float3> position_mutable = positions.as_span(); + + for (int i : selection) { + position_mutable[i] = positions_input[i] + offsets_input[i]; + } + + positions.save(); +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + const NodeGeometrySetCurveHandlePositions &storage = node_storage(params.node()); + const GeometryNodeCurveHandleMode mode = (GeometryNodeCurveHandleMode)storage.mode; + + GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); + Field<bool> selection_field = params.extract_input<Field<bool>>("Selection"); + Field<float3> position_field = params.extract_input<Field<float3>>("Position"); + Field<float3> offset_field = params.extract_input<Field<float3>>("Offset"); + + bool has_bezier = false; + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (geometry_set.has_curve() && + geometry_set.get_curve_for_read()->has_spline_with_type(Spline::Type::Bezier)) { + has_bezier = true; + set_position_in_component(mode, + geometry_set.get_component_for_write<CurveComponent>(), + selection_field, + position_field, + offset_field); + } + }); + if (!has_bezier) { + params.error_message_add(NodeWarningType::Info, + TIP_("The input geometry does not contain a Bezier spline")); + } + params.set_output("Curve", std::move(geometry_set)); +} + +} // namespace blender::nodes::node_geo_set_curve_handles_cc + +void register_node_type_geo_set_curve_handles() +{ + namespace file_ns = blender::nodes::node_geo_set_curve_handles_cc; + + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_SET_CURVE_HANDLES, "Set Handle Positions", NODE_CLASS_GEOMETRY); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.declare = file_ns::node_declare; + ntype.minwidth = 100.0f; + node_type_init(&ntype, file_ns::node_init); + node_type_storage(&ntype, + "NodeGeometrySetCurveHandlePositions", + node_free_standard_storage, + node_copy_standard_storage); + ntype.draw_buttons = file_ns::node_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc new file mode 100644 index 00000000000..06fe4427520 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc @@ -0,0 +1,82 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_set_curve_radius_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input<decl::Float>(N_("Radius")) + .min(0.0f) + .default_value(1.0f) + .supports_field() + .subtype(PROP_DISTANCE); + b.add_output<decl::Geometry>(N_("Curve")); +} + +static void set_radius_in_component(GeometryComponent &component, + const Field<bool> &selection_field, + const Field<float> &radius_field) +{ + GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; + const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); + if (domain_size == 0) { + return; + } + + OutputAttribute_Typed<float> radii = component.attribute_try_get_for_output_only<float>( + "radius", ATTR_DOMAIN_POINT); + + fn::FieldEvaluator evaluator{field_context, domain_size}; + evaluator.set_selection(selection_field); + evaluator.add_with_destination(radius_field, radii.varray()); + evaluator.evaluate(); + + radii.save(); +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); + Field<bool> selection_field = params.extract_input<Field<bool>>("Selection"); + Field<float> radii_field = params.extract_input<Field<float>>("Radius"); + + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (geometry_set.has_curve()) { + set_radius_in_component( + geometry_set.get_component_for_write<CurveComponent>(), selection_field, radii_field); + } + }); + + params.set_output("Curve", std::move(geometry_set)); +} + +} // namespace blender::nodes::node_geo_set_curve_radius_cc + +void register_node_type_geo_set_curve_radius() +{ + namespace file_ns = blender::nodes::node_geo_set_curve_radius_cc; + + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_SET_CURVE_RADIUS, "Set Curve Radius", NODE_CLASS_GEOMETRY); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.declare = file_ns::node_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc new file mode 100644 index 00000000000..0854d0a4549 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc @@ -0,0 +1,78 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_set_curve_tilt_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input<decl::Float>(N_("Tilt")).subtype(PROP_ANGLE).supports_field(); + b.add_output<decl::Geometry>(N_("Curve")); +} + +static void set_tilt_in_component(GeometryComponent &component, + const Field<bool> &selection_field, + const Field<float> &tilt_field) +{ + GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; + const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); + if (domain_size == 0) { + return; + } + + OutputAttribute_Typed<float> tilts = component.attribute_try_get_for_output_only<float>( + "tilt", ATTR_DOMAIN_POINT); + + fn::FieldEvaluator evaluator{field_context, domain_size}; + evaluator.set_selection(selection_field); + evaluator.add_with_destination(tilt_field, tilts.varray()); + evaluator.evaluate(); + + tilts.save(); +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); + Field<bool> selection_field = params.extract_input<Field<bool>>("Selection"); + Field<float> tilt_field = params.extract_input<Field<float>>("Tilt"); + + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (geometry_set.has_curve()) { + set_tilt_in_component( + geometry_set.get_component_for_write<CurveComponent>(), selection_field, tilt_field); + } + }); + + params.set_output("Curve", std::move(geometry_set)); +} + +} // namespace blender::nodes::node_geo_set_curve_tilt_cc + +void register_node_type_geo_set_curve_tilt() +{ + namespace file_ns = blender::nodes::node_geo_set_curve_tilt_cc; + + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_SET_CURVE_TILT, "Set Curve Tilt", NODE_CLASS_GEOMETRY); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.declare = file_ns::node_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_id.cc b/source/blender/nodes/geometry/nodes/node_geo_set_id.cc new file mode 100644 index 00000000000..110b8206944 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_set_id.cc @@ -0,0 +1,94 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_set_id_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input<decl::Int>(N_("ID")).implicit_field(); + b.add_output<decl::Geometry>(N_("Geometry")); +} + +static void set_id_in_component(GeometryComponent &component, + const Field<bool> &selection_field, + const Field<int> &id_field) +{ + GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; + const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); + if (domain_size == 0) { + return; + } + + fn::FieldEvaluator evaluator{field_context, domain_size}; + evaluator.set_selection(selection_field); + + /* Since adding the ID attribute can change the result of the field evaluation (the random value + * node uses the index if the ID is unavailable), make sure that it isn't added before evaluating + * the field. However, as an optimization, use a faster code path when it already exists. */ + if (component.attribute_exists("id")) { + OutputAttribute_Typed<int> id_attribute = component.attribute_try_get_for_output_only<int>( + "id", ATTR_DOMAIN_POINT); + evaluator.add_with_destination(id_field, id_attribute.varray()); + evaluator.evaluate(); + id_attribute.save(); + } + else { + evaluator.add(id_field); + evaluator.evaluate(); + const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); + const VArray<int> &result_ids = evaluator.get_evaluated<int>(0); + OutputAttribute_Typed<int> id_attribute = component.attribute_try_get_for_output_only<int>( + "id", ATTR_DOMAIN_POINT); + result_ids.materialize(selection, id_attribute.as_span()); + id_attribute.save(); + } +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + Field<bool> selection_field = params.extract_input<Field<bool>>("Selection"); + Field<int> id_field = params.extract_input<Field<int>>("ID"); + + for (const GeometryComponentType type : {GEO_COMPONENT_TYPE_INSTANCES, + GEO_COMPONENT_TYPE_MESH, + GEO_COMPONENT_TYPE_POINT_CLOUD, + GEO_COMPONENT_TYPE_CURVE}) { + if (geometry_set.has(type)) { + set_id_in_component(geometry_set.get_component_for_write(type), selection_field, id_field); + } + } + + params.set_output("Geometry", std::move(geometry_set)); +} + +} // namespace blender::nodes::node_geo_set_id_cc + +void register_node_type_geo_set_id() +{ + namespace file_ns = blender::nodes::node_geo_set_id_cc; + + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_SET_ID, "Set ID", NODE_CLASS_GEOMETRY); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.declare = file_ns::node_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_material.cc b/source/blender/nodes/geometry/nodes/node_geo_set_material.cc new file mode 100644 index 00000000000..ab2c778d6fc --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_set_material.cc @@ -0,0 +1,134 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_pointcloud_types.h" +#include "DNA_volume_types.h" + +#include "BKE_material.h" + +namespace blender::nodes::node_geo_set_material_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>(N_("Geometry")) + .supported_type( + {GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_VOLUME, GEO_COMPONENT_TYPE_POINT_CLOUD}); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input<decl::Material>(N_("Material")).hide_label(); + b.add_output<decl::Geometry>(N_("Geometry")); +} + +static void assign_material_to_faces(Mesh &mesh, const IndexMask selection, Material *material) +{ + if (selection.size() != mesh.totpoly) { + /* If the entire mesh isn't selected, and there is no material slot yet, add an empty + * slot so that the faces that aren't selected can still refer to the default material. */ + BKE_id_material_eval_ensure_default_slot(&mesh.id); + } + + int new_material_index = -1; + for (const int i : IndexRange(mesh.totcol)) { + Material *other_material = mesh.mat[i]; + if (other_material == material) { + new_material_index = i; + break; + } + } + if (new_material_index == -1) { + /* Append a new material index. */ + new_material_index = mesh.totcol; + BKE_id_material_eval_assign(&mesh.id, new_material_index + 1, material); + } + + mesh.mpoly = (MPoly *)CustomData_duplicate_referenced_layer(&mesh.pdata, CD_MPOLY, mesh.totpoly); + for (const int i : selection) { + MPoly &poly = mesh.mpoly[i]; + poly.mat_nr = new_material_index; + } +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + Material *material = params.extract_input<Material *>("Material"); + const Field<bool> selection_field = params.extract_input<Field<bool>>("Selection"); + + GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + + /* Only add the warnings once, even if there are many unique instances. */ + bool point_selection_warning = false; + bool volume_selection_warning = false; + + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (geometry_set.has_mesh()) { + MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>(); + Mesh &mesh = *mesh_component.get_for_write(); + GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_FACE}; + + fn::FieldEvaluator selection_evaluator{field_context, mesh.totpoly}; + selection_evaluator.add(selection_field); + selection_evaluator.evaluate(); + const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); + + assign_material_to_faces(mesh, selection, material); + } + if (Volume *volume = geometry_set.get_volume_for_write()) { + BKE_id_material_eval_assign(&volume->id, 1, material); + if (selection_field.node().depends_on_input()) { + volume_selection_warning = true; + } + } + if (PointCloud *pointcloud = geometry_set.get_pointcloud_for_write()) { + BKE_id_material_eval_assign(&pointcloud->id, 1, material); + if (selection_field.node().depends_on_input()) { + point_selection_warning = true; + } + } + }); + + if (volume_selection_warning) { + params.error_message_add( + NodeWarningType::Info, + TIP_("Volumes only support a single material; selection input can not be a field")); + } + if (point_selection_warning) { + params.error_message_add( + NodeWarningType::Info, + TIP_("Point clouds only support a single material; selection input can not be a field")); + } + + params.set_output("Geometry", std::move(geometry_set)); +} + +} // namespace blender::nodes::node_geo_set_material_cc + +void register_node_type_geo_set_material() +{ + namespace file_ns = blender::nodes::node_geo_set_material_cc; + + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_SET_MATERIAL, "Set Material", NODE_CLASS_GEOMETRY); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc b/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc new file mode 100644 index 00000000000..ca6d78adc80 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc @@ -0,0 +1,77 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_set_material_index_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>(N_("Geometry")).supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input<decl::Int>(N_("Material Index")).supports_field().min(0); + b.add_output<decl::Geometry>(N_("Geometry")); +} + +static void set_material_index_in_component(GeometryComponent &component, + const Field<bool> &selection_field, + const Field<int> &index_field) +{ + GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_FACE}; + const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE); + if (domain_size == 0) { + return; + } + + OutputAttribute_Typed<int> indices = component.attribute_try_get_for_output_only<int>( + "material_index", ATTR_DOMAIN_FACE); + + fn::FieldEvaluator evaluator{field_context, domain_size}; + evaluator.set_selection(selection_field); + evaluator.add_with_destination(index_field, indices.varray()); + evaluator.evaluate(); + indices.save(); +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + Field<bool> selection_field = params.extract_input<Field<bool>>("Selection"); + Field<int> index_field = params.extract_input<Field<int>>("Material Index"); + + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (geometry_set.has_mesh()) { + set_material_index_in_component( + geometry_set.get_component_for_write<MeshComponent>(), selection_field, index_field); + } + }); + params.set_output("Geometry", std::move(geometry_set)); +} + +} // namespace blender::nodes::node_geo_set_material_index_cc + +void register_node_type_geo_set_material_index() +{ + namespace file_ns = blender::nodes::node_geo_set_material_index_cc; + + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_SET_MATERIAL_INDEX, "Set Material Index", NODE_CLASS_GEOMETRY); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.declare = file_ns::node_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc b/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc new file mode 100644 index 00000000000..b7dd091da44 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc @@ -0,0 +1,83 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_set_point_radius_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>(N_("Points")).supported_type(GEO_COMPONENT_TYPE_POINT_CLOUD); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input<decl::Float>(N_("Radius")) + .default_value(0.05f) + .min(0.0f) + .supports_field() + .subtype(PROP_DISTANCE); + b.add_output<decl::Geometry>(N_("Points")); +} + +static void set_radius_in_component(GeometryComponent &component, + const Field<bool> &selection_field, + const Field<float> &radius_field) +{ + GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; + const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); + if (domain_size == 0) { + return; + } + + OutputAttribute_Typed<float> radii = component.attribute_try_get_for_output_only<float>( + "radius", ATTR_DOMAIN_POINT); + + fn::FieldEvaluator evaluator{field_context, domain_size}; + evaluator.set_selection(selection_field); + evaluator.add_with_destination(radius_field, radii.varray()); + evaluator.evaluate(); + + radii.save(); +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Points"); + Field<bool> selection_field = params.extract_input<Field<bool>>("Selection"); + Field<float> radii_field = params.extract_input<Field<float>>("Radius"); + + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (geometry_set.has_pointcloud()) { + set_radius_in_component(geometry_set.get_component_for_write<PointCloudComponent>(), + selection_field, + radii_field); + } + }); + + params.set_output("Points", std::move(geometry_set)); +} + +} // namespace blender::nodes::node_geo_set_point_radius_cc + +void register_node_type_geo_set_point_radius() +{ + namespace file_ns = blender::nodes::node_geo_set_point_radius_cc; + + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_SET_POINT_RADIUS, "Set Point Radius", NODE_CLASS_GEOMETRY); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.declare = file_ns::node_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc index 8caf961fc04..4a8e4e6eab8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc @@ -16,45 +16,126 @@ #include "DEG_depsgraph_query.h" +#include "BLI_task.hh" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_set_position_cc { -static void geo_node_set_position_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Vector>("Position").implicit_field(); - b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field(); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input<decl::Vector>(N_("Position")).implicit_field(); + b.add_input<decl::Vector>(N_("Offset")).supports_field().subtype(PROP_TRANSLATION); + b.add_output<decl::Geometry>(N_("Geometry")); +} + +static void set_computed_position_and_offset(GeometryComponent &component, + const VArray<float3> &in_positions, + const VArray<float3> &in_offsets, + const AttributeDomain domain, + const IndexMask selection) +{ + + OutputAttribute_Typed<float3> positions = component.attribute_try_get_for_output<float3>( + "position", domain, {0, 0, 0}); + + const int grain_size = 10000; + + switch (component.type()) { + case GEO_COMPONENT_TYPE_MESH: { + Mesh *mesh = static_cast<MeshComponent &>(component).get_for_write(); + MutableSpan<MVert> mverts{mesh->mvert, mesh->totvert}; + if (in_positions.is_same(positions.varray())) { + devirtualize_varray(in_offsets, [&](const auto in_offsets) { + threading::parallel_for( + selection.index_range(), grain_size, [&](const IndexRange range) { + for (const int i : selection.slice(range)) { + const float3 offset = in_offsets[i]; + add_v3_v3(mverts[i].co, offset); + } + }); + }); + } + else { + devirtualize_varray2( + in_positions, in_offsets, [&](const auto in_positions, const auto in_offsets) { + threading::parallel_for( + selection.index_range(), grain_size, [&](const IndexRange range) { + for (const int i : selection.slice(range)) { + const float3 new_position = in_positions[i] + in_offsets[i]; + copy_v3_v3(mverts[i].co, new_position); + } + }); + }); + } + break; + } + default: { + MutableSpan<float3> out_positions_span = positions.as_span(); + if (in_positions.is_same(positions.varray())) { + devirtualize_varray(in_offsets, [&](const auto in_offsets) { + threading::parallel_for( + selection.index_range(), grain_size, [&](const IndexRange range) { + for (const int i : selection.slice(range)) { + out_positions_span[i] += in_offsets[i]; + } + }); + }); + } + else { + devirtualize_varray2( + in_positions, in_offsets, [&](const auto in_positions, const auto in_offsets) { + threading::parallel_for( + selection.index_range(), grain_size, [&](const IndexRange range) { + for (const int i : selection.slice(range)) { + out_positions_span[i] = in_positions[i] + in_offsets[i]; + } + }); + }); + } + break; + } + } + + positions.save(); } static void set_position_in_component(GeometryComponent &component, const Field<bool> &selection_field, - const Field<float3> &position_field) + const Field<float3> &position_field, + const Field<float3> &offset_field) { - GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; - const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); + AttributeDomain domain = component.type() == GEO_COMPONENT_TYPE_INSTANCES ? + ATTR_DOMAIN_INSTANCE : + ATTR_DOMAIN_POINT; + GeometryComponentFieldContext field_context{component, domain}; + const int domain_size = component.attribute_domain_size(domain); if (domain_size == 0) { return; } - fn::FieldEvaluator selection_evaluator{field_context, domain_size}; - selection_evaluator.add(selection_field); - selection_evaluator.evaluate(); - const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); + fn::FieldEvaluator evaluator{field_context, domain_size}; + evaluator.set_selection(selection_field); + evaluator.add(position_field); + evaluator.add(offset_field); + evaluator.evaluate(); - OutputAttribute_Typed<float3> positions = component.attribute_try_get_for_output<float3>( - "position", ATTR_DOMAIN_POINT, {0, 0, 0}); - fn::FieldEvaluator position_evaluator{field_context, &selection}; - position_evaluator.add_with_destination(position_field, positions.varray()); - position_evaluator.evaluate(); - positions.save(); + const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); + const VArray<float3> &positions_input = evaluator.get_evaluated<float3>(0); + const VArray<float3> &offsets_input = evaluator.get_evaluated<float3>(1); + set_computed_position_and_offset(component, positions_input, offsets_input, domain, selection); } -static void geo_node_set_position_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry = params.extract_input<GeometrySet>("Geometry"); Field<bool> selection_field = params.extract_input<Field<bool>>("Selection"); + Field<float3> offset_field = params.extract_input<Field<float3>>("Offset"); Field<float3> position_field = params.extract_input<Field<float3>>("Position"); for (const GeometryComponentType type : {GEO_COMPONENT_TYPE_MESH, @@ -63,21 +144,23 @@ static void geo_node_set_position_exec(GeoNodeExecParams params) GEO_COMPONENT_TYPE_INSTANCES}) { if (geometry.has(type)) { set_position_in_component( - geometry.get_component_for_write(type), selection_field, position_field); + geometry.get_component_for_write(type), selection_field, position_field, offset_field); } } params.set_output("Geometry", std::move(geometry)); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_set_position_cc void register_node_type_geo_set_position() { + namespace file_ns = blender::nodes::node_geo_set_position_cc; + static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_SET_POSITION, "Set Position", NODE_CLASS_GEOMETRY, 0); - ntype.geometry_node_execute = blender::nodes::geo_node_set_position_exec; - ntype.declare = blender::nodes::geo_node_set_position_declare; + geo_node_type_base(&ntype, GEO_NODE_SET_POSITION, "Set Position", NODE_CLASS_GEOMETRY); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.declare = file_ns::node_declare; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc b/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc new file mode 100644 index 00000000000..d442cd37e81 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc @@ -0,0 +1,77 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_set_shade_smooth_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>(N_("Geometry")).supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input<decl::Bool>(N_("Shade Smooth")).supports_field().default_value(true); + b.add_output<decl::Geometry>(N_("Geometry")); +} + +static void set_smooth_in_component(GeometryComponent &component, + const Field<bool> &selection_field, + const Field<bool> &shade_field) +{ + GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_FACE}; + const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE); + if (domain_size == 0) { + return; + } + + OutputAttribute_Typed<bool> shades = component.attribute_try_get_for_output_only<bool>( + "shade_smooth", ATTR_DOMAIN_FACE); + + fn::FieldEvaluator evaluator{field_context, domain_size}; + evaluator.set_selection(selection_field); + evaluator.add_with_destination(shade_field, shades.varray()); + evaluator.evaluate(); + + shades.save(); +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + Field<bool> selection_field = params.extract_input<Field<bool>>("Selection"); + Field<bool> shade_field = params.extract_input<Field<bool>>("Shade Smooth"); + + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (geometry_set.has_mesh()) { + set_smooth_in_component( + geometry_set.get_component_for_write<MeshComponent>(), selection_field, shade_field); + } + }); + params.set_output("Geometry", std::move(geometry_set)); +} + +} // namespace blender::nodes::node_geo_set_shade_smooth_cc + +void register_node_type_geo_set_shade_smooth() +{ + namespace file_ns = blender::nodes::node_geo_set_shade_smooth_cc; + + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_SET_SHADE_SMOOTH, "Set Shade Smooth", NODE_CLASS_GEOMETRY); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.declare = file_ns::node_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc b/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc new file mode 100644 index 00000000000..13230e185a3 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc @@ -0,0 +1,78 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_set_spline_cyclic_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>(N_("Geometry")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input<decl::Bool>(N_("Cyclic")).supports_field(); + b.add_output<decl::Geometry>(N_("Geometry")); +} + +static void set_cyclic_in_component(GeometryComponent &component, + const Field<bool> &selection_field, + const Field<bool> &cyclic_field) +{ + GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE}; + const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE); + if (domain_size == 0) { + return; + } + + OutputAttribute_Typed<bool> cyclics = component.attribute_try_get_for_output_only<bool>( + "cyclic", ATTR_DOMAIN_CURVE); + + fn::FieldEvaluator evaluator{field_context, domain_size}; + evaluator.set_selection(selection_field); + evaluator.add_with_destination(cyclic_field, cyclics.varray()); + evaluator.evaluate(); + + cyclics.save(); +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + Field<bool> selection_field = params.extract_input<Field<bool>>("Selection"); + Field<bool> cyclic_field = params.extract_input<Field<bool>>("Cyclic"); + + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (geometry_set.has_curve()) { + set_cyclic_in_component( + geometry_set.get_component_for_write<CurveComponent>(), selection_field, cyclic_field); + } + }); + + params.set_output("Geometry", std::move(geometry_set)); +} + +} // namespace blender::nodes::node_geo_set_spline_cyclic_cc + +void register_node_type_geo_set_spline_cyclic() +{ + namespace file_ns = blender::nodes::node_geo_set_spline_cyclic_cc; + + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_SET_SPLINE_CYCLIC, "Set Spline Cyclic", NODE_CLASS_GEOMETRY); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.declare = file_ns::node_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc b/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc new file mode 100644 index 00000000000..e472e14671c --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc @@ -0,0 +1,95 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BKE_spline.hh" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_set_spline_resolution_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>(N_("Geometry")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input<decl::Int>(N_("Resolution")).min(1).default_value(12).supports_field(); + b.add_output<decl::Geometry>(N_("Geometry")); +} + +static void set_resolution_in_component(GeometryComponent &component, + const Field<bool> &selection_field, + const Field<int> &resolution_field) +{ + GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE}; + const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE); + if (domain_size == 0) { + return; + } + + OutputAttribute_Typed<int> resolutions = component.attribute_try_get_for_output_only<int>( + "resolution", ATTR_DOMAIN_CURVE); + + fn::FieldEvaluator evaluator{field_context, domain_size}; + evaluator.set_selection(selection_field); + evaluator.add_with_destination(resolution_field, resolutions.varray()); + evaluator.evaluate(); + + resolutions.save(); +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + Field<bool> selection_field = params.extract_input<Field<bool>>("Selection"); + Field<int> resolution_field = params.extract_input<Field<int>>("Resolution"); + + bool only_poly = true; + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (geometry_set.has_curve()) { + if (only_poly) { + for (const SplinePtr &spline : geometry_set.get_curve_for_read()->splines()) { + if (ELEM(spline->type(), Spline::Type::Bezier, Spline::Type::NURBS)) { + only_poly = false; + break; + } + } + } + set_resolution_in_component(geometry_set.get_component_for_write<CurveComponent>(), + selection_field, + resolution_field); + } + }); + + if (only_poly) { + params.error_message_add(NodeWarningType::Warning, + TIP_("Input geometry does not contain a Bezier or NURB spline")); + } + params.set_output("Geometry", std::move(geometry_set)); +} + +} // namespace blender::nodes::node_geo_set_spline_resolution_cc + +void register_node_type_geo_set_spline_resolution() +{ + namespace file_ns = blender::nodes::node_geo_set_spline_resolution_cc; + + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_SET_SPLINE_RESOLUTION, "Set Spline Resolution", NODE_CLASS_GEOMETRY); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.declare = file_ns::node_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_string_join.cc b/source/blender/nodes/geometry/nodes/node_geo_string_join.cc index 1e4a4d1f68b..176fcf3178a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_string_join.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_string_join.cc @@ -16,16 +16,16 @@ #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_string_join_cc { -static void geo_node_string_join_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::String>("Delimiter"); - b.add_input<decl::String>("Strings").multi_input().hide_value(); - b.add_output<decl::String>("String"); -}; + b.add_input<decl::String>(N_("Delimiter")); + b.add_input<decl::String>(N_("Strings")).multi_input().hide_value(); + b.add_output<decl::String>(N_("String")); +} -static void geo_node_string_join_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { Vector<std::string> strings = params.extract_multi_input<std::string>("Strings"); const std::string delim = params.extract_input<std::string>("Delimiter"); @@ -40,14 +40,16 @@ static void geo_node_string_join_exec(GeoNodeExecParams params) params.set_output("String", std::move(output)); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_string_join_cc void register_node_type_geo_string_join() { + namespace file_ns = blender::nodes::node_geo_string_join_cc; + static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_STRING_JOIN, "String Join", NODE_CLASS_CONVERTER, 0); - ntype.geometry_node_execute = blender::nodes::geo_node_string_join_exec; - ntype.declare = blender::nodes::geo_node_string_join_declare; + geo_node_type_base(&ntype, GEO_NODE_STRING_JOIN, "Join Strings", NODE_CLASS_CONVERTER); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.declare = file_ns::node_declare; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc index 5e2f03806c3..10c0d61ccb6 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc @@ -18,8 +18,8 @@ #include "DNA_vfont_types.h" #include "BKE_curve.h" -#include "BKE_font.h" #include "BKE_spline.hh" +#include "BKE_vfont.h" #include "BLI_hash.h" #include "BLI_string_utf8.h" @@ -30,25 +30,44 @@ #include "node_geometry_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_geo_string_to_curves_cc { -static void geo_node_string_to_curves_declare(NodeDeclarationBuilder &b) +NODE_STORAGE_FUNCS(NodeGeometryStringToCurves) + +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::String>("String"); - b.add_input<decl::Float>("Size").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input<decl::Float>("Character Spacing") + b.add_input<decl::String>(N_("String")); + b.add_input<decl::Float>(N_("Size")).default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); + b.add_input<decl::Float>(N_("Character Spacing")) + .default_value(1.0f) + .min(0.0f) + .subtype(PROP_DISTANCE); + b.add_input<decl::Float>(N_("Word Spacing")) .default_value(1.0f) .min(0.0f) .subtype(PROP_DISTANCE); - b.add_input<decl::Float>("Word Spacing").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input<decl::Float>("Line Spacing").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input<decl::Float>("Text Box Width").default_value(0.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input<decl::Float>("Text Box Height").default_value(0.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_output<decl::Geometry>("Curves"); - b.add_output<decl::String>("Remainder"); + b.add_input<decl::Float>(N_("Line Spacing")) + .default_value(1.0f) + .min(0.0f) + .subtype(PROP_DISTANCE); + b.add_input<decl::Float>(N_("Text Box Width")) + .default_value(0.0f) + .min(0.0f) + .subtype(PROP_DISTANCE); + b.add_input<decl::Float>(N_("Text Box Height")) + .default_value(0.0f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .make_available([](bNode &node) { + node_storage(node).overflow = GEO_NODE_STRING_TO_CURVES_MODE_SCALE_TO_FIT; + }); + b.add_output<decl::Geometry>(N_("Curve Instances")); + b.add_output<decl::String>(N_("Remainder")).make_available([](bNode &node) { + node_storage(node).overflow = GEO_NODE_STRING_TO_CURVES_MODE_TRUNCATE; + }); } -static void geo_node_string_to_curves_layout(uiLayout *layout, struct bContext *C, PointerRNA *ptr) +static void node_layout(uiLayout *layout, struct bContext *C, PointerRNA *ptr) { uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); @@ -67,10 +86,9 @@ static void geo_node_string_to_curves_layout(uiLayout *layout, struct bContext * uiItemR(layout, ptr, "align_y", 0, "", ICON_NONE); } -static void geo_node_string_to_curves_init(bNodeTree *UNUSED(ntree), bNode *node) +static void node_init(bNodeTree *UNUSED(ntree), bNode *node) { - NodeGeometryStringToCurves *data = (NodeGeometryStringToCurves *)MEM_callocN( - sizeof(NodeGeometryStringToCurves), __func__); + NodeGeometryStringToCurves *data = MEM_cnew<NodeGeometryStringToCurves>(__func__); data->overflow = GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW; data->align_x = GEO_NODE_STRING_TO_CURVES_ALIGN_X_LEFT; @@ -79,17 +97,19 @@ static void geo_node_string_to_curves_init(bNodeTree *UNUSED(ntree), bNode *node node->id = (ID *)BKE_vfont_builtin_get(); } -static void geo_node_string_to_curves_update(bNodeTree *UNUSED(ntree), bNode *node) +static void node_update(bNodeTree *ntree, bNode *node) { - const NodeGeometryStringToCurves *storage = (const NodeGeometryStringToCurves *)node->storage; + const NodeGeometryStringToCurves &storage = node_storage(*node); const GeometryNodeStringToCurvesOverflowMode overflow = (GeometryNodeStringToCurvesOverflowMode) - storage->overflow; + storage.overflow; bNodeSocket *socket_remainder = ((bNodeSocket *)node->outputs.first)->next; - nodeSetSocketAvailability(socket_remainder, overflow == GEO_NODE_STRING_TO_CURVES_MODE_TRUNCATE); + nodeSetSocketAvailability( + ntree, socket_remainder, overflow == GEO_NODE_STRING_TO_CURVES_MODE_TRUNCATE); bNodeSocket *height_socket = (bNodeSocket *)node->inputs.last; bNodeSocket *width_socket = height_socket->prev; - nodeSetSocketAvailability(height_socket, overflow != GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW); + nodeSetSocketAvailability( + ntree, height_socket, overflow != GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW); node_sock_label(width_socket, overflow == GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW ? N_("Max Width") : N_("Text Box Width")); @@ -117,8 +137,7 @@ static TextLayout get_text_layout(GeoNodeExecParams ¶ms) return {}; } - const NodeGeometryStringToCurves &storage = - *(const NodeGeometryStringToCurves *)params.node().storage; + const NodeGeometryStringToCurves &storage = node_storage(params.node()); const GeometryNodeStringToCurvesOverflowMode overflow = (GeometryNodeStringToCurvesOverflowMode) storage.overflow; const GeometryNodeStringToCurvesAlignXMode align_x = (GeometryNodeStringToCurvesAlignXMode) @@ -136,7 +155,7 @@ static TextLayout get_text_layout(GeoNodeExecParams ¶ms) params.extract_input<float>("Text Box Height"); VFont *vfont = (VFont *)params.node().id; - Curve cu = {nullptr}; + Curve cu = {{nullptr}}; cu.type = OB_FONT; /* Set defaults */ cu.resolu = 12; @@ -214,7 +233,7 @@ static Map<int, int> create_curve_instances(GeoNodeExecParams ¶ms, if (handles.contains(charcodes[i])) { continue; } - Curve cu = {nullptr}; + Curve cu = {{nullptr}}; cu.type = OB_FONT; cu.resolu = 12; cu.vfont = vfont; @@ -242,18 +261,16 @@ static void add_instances_from_handles(InstancesComponent &instances, instances.resize(positions.size()); MutableSpan<int> handles = instances.instance_reference_handles(); MutableSpan<float4x4> transforms = instances.instance_transforms(); - MutableSpan<int> instance_ids = instances.instance_ids(); threading::parallel_for(IndexRange(positions.size()), 256, [&](IndexRange range) { for (const int i : range) { handles[i] = char_handles.lookup(charcodes[i]); transforms[i] = float4x4::from_location({positions[i].x, positions[i].y, 0}); - instance_ids[i] = i; } }); } -static void geo_node_string_to_curves_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { TextLayout layout = get_text_layout(params); @@ -264,15 +281,16 @@ static void geo_node_string_to_curves_exec(GeoNodeExecParams params) } if (layout.positions.size() == 0) { - params.set_output("Curves", GeometrySet()); + params.set_output("Curve Instances", GeometrySet()); return; } /* Convert UTF-8 encoded string to UTF-32. */ size_t len_bytes; size_t len_chars = BLI_strlen_utf8_ex(layout.text.c_str(), &len_bytes); - Array<char32_t> char_codes(len_chars + 1); - BLI_str_utf8_as_utf32(char_codes.data(), layout.text.c_str(), len_chars + 1); + Array<char32_t> char_codes_with_null(len_chars + 1); + BLI_str_utf8_as_utf32(char_codes_with_null.data(), layout.text.c_str(), len_chars + 1); + const Span<char32_t> char_codes = char_codes_with_null.as_span().drop_back(1); /* Create and add instances. */ GeometrySet geometry_set_out; @@ -281,26 +299,27 @@ static void geo_node_string_to_curves_exec(GeoNodeExecParams params) params, layout.final_font_size, char_codes, instances); add_instances_from_handles(instances, char_handles, char_codes, layout.positions); - params.set_output("Curves", std::move(geometry_set_out)); + params.set_output("Curve Instances", std::move(geometry_set_out)); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_string_to_curves_cc void register_node_type_geo_string_to_curves() { + namespace file_ns = blender::nodes::node_geo_string_to_curves_cc; + static bNodeType ntype; - geo_node_type_base( - &ntype, GEO_NODE_STRING_TO_CURVES, "String to Curves", NODE_CLASS_GEOMETRY, 0); - ntype.declare = blender::nodes::geo_node_string_to_curves_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_string_to_curves_exec; - node_type_init(&ntype, blender::nodes::geo_node_string_to_curves_init); - node_type_update(&ntype, blender::nodes::geo_node_string_to_curves_update); + geo_node_type_base(&ntype, GEO_NODE_STRING_TO_CURVES, "String to Curves", NODE_CLASS_GEOMETRY); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + node_type_init(&ntype, file_ns::node_init); + node_type_update(&ntype, file_ns::node_update); node_type_size(&ntype, 190, 120, 700); node_type_storage(&ntype, "NodeGeometryStringToCurves", node_free_standard_storage, node_copy_standard_storage); - ntype.draw_buttons = blender::nodes::geo_node_string_to_curves_layout; + ntype.draw_buttons = file_ns::node_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc new file mode 100644 index 00000000000..eb1a5496845 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc @@ -0,0 +1,166 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" + +#include "BKE_mesh.h" +#include "BKE_subdiv.h" +#include "BKE_subdiv_mesh.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_subdivision_surface_cc { + +NODE_STORAGE_FUNCS(NodeGeometrySubdivisionSurface) + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input<decl::Int>(N_("Level")).default_value(1).min(0).max(6); + b.add_input<decl::Float>(N_("Crease")) + .default_value(0.0f) + .min(0.0f) + .max(1.0f) + .supports_field() + .subtype(PROP_FACTOR); + b.add_output<decl::Geometry>(N_("Mesh")); +} + +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "uv_smooth", 0, "", ICON_NONE); + uiItemR(layout, ptr, "boundary_smooth", 0, "", ICON_NONE); +} + +static void node_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometrySubdivisionSurface *data = MEM_cnew<NodeGeometrySubdivisionSurface>(__func__); + data->uv_smooth = SUBSURF_UV_SMOOTH_PRESERVE_BOUNDARIES; + data->boundary_smooth = SUBSURF_BOUNDARY_SMOOTH_ALL; + node->storage = data; +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh"); +#ifndef WITH_OPENSUBDIV + params.error_message_add(NodeWarningType::Error, + TIP_("Disabled, Blender was compiled without OpenSubdiv")); +#else + Field<float> crease_field = params.extract_input<Field<float>>("Crease"); + + const NodeGeometrySubdivisionSurface &storage = node_storage(params.node()); + const int uv_smooth = storage.uv_smooth; + const int boundary_smooth = storage.boundary_smooth; + const int subdiv_level = clamp_i(params.extract_input<int>("Level"), 0, 30); + + /* Only process subdivision if level is greater than 0. */ + if (subdiv_level == 0) { + params.set_output("Mesh", std::move(geometry_set)); + return; + } + + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (!geometry_set.has_mesh()) { + return; + } + + MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>(); + AttributeDomain domain = ATTR_DOMAIN_EDGE; + GeometryComponentFieldContext field_context{mesh_component, domain}; + const int domain_size = mesh_component.attribute_domain_size(domain); + + if (domain_size == 0) { + return; + } + + FieldEvaluator evaluator(field_context, domain_size); + evaluator.add(crease_field); + evaluator.evaluate(); + const VArray<float> &creases = evaluator.get_evaluated<float>(0); + + OutputAttribute_Typed<float> crease = mesh_component.attribute_try_get_for_output_only<float>( + "crease", domain); + MutableSpan<float> crease_span = crease.as_span(); + for (auto i : creases.index_range()) { + crease_span[i] = std::clamp(creases[i], 0.0f, 1.0f); + } + crease.save(); + + /* Initialize mesh settings. */ + SubdivToMeshSettings mesh_settings; + mesh_settings.resolution = (1 << subdiv_level) + 1; + mesh_settings.use_optimal_display = false; + + /* Initialize subdivision settings. */ + SubdivSettings subdiv_settings; + subdiv_settings.is_simple = false; + subdiv_settings.is_adaptive = false; + subdiv_settings.use_creases = !(creases.is_single() && creases.get_internal_single() == 0.0f); + subdiv_settings.level = subdiv_level; + + subdiv_settings.vtx_boundary_interpolation = + BKE_subdiv_vtx_boundary_interpolation_from_subsurf(boundary_smooth); + subdiv_settings.fvar_linear_interpolation = BKE_subdiv_fvar_interpolation_from_uv_smooth( + uv_smooth); + + Mesh *mesh_in = mesh_component.get_for_write(); + + /* Apply subdivision to mesh. */ + Subdiv *subdiv = BKE_subdiv_update_from_mesh(nullptr, &subdiv_settings, mesh_in); + + /* In case of bad topology, skip to input mesh. */ + if (subdiv == nullptr) { + return; + } + + Mesh *mesh_out = BKE_subdiv_to_mesh(subdiv, &mesh_settings, mesh_in); + BKE_mesh_normals_tag_dirty(mesh_out); + + mesh_component.replace(mesh_out); + + BKE_subdiv_free(subdiv); + }); +#endif + params.set_output("Mesh", std::move(geometry_set)); +} + +} // namespace blender::nodes::node_geo_subdivision_surface_cc + +void register_node_type_geo_subdivision_surface() +{ + namespace file_ns = blender::nodes::node_geo_subdivision_surface_cc; + + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_SUBDIVISION_SURFACE, "Subdivision Surface", NODE_CLASS_GEOMETRY); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.draw_buttons = file_ns::node_layout; + node_type_init(&ntype, file_ns::node_init); + node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); + node_type_storage(&ntype, + "NodeGeometrySubdivisionSurface", + node_free_standard_storage, + node_copy_standard_storage); + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_switch.cc b/source/blender/nodes/geometry/nodes/node_geo_switch.cc index ca857c4d2e3..a2f05677310 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_switch.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_switch.cc @@ -19,149 +19,298 @@ #include "UI_interface.h" #include "UI_resources.h" -namespace blender::nodes { +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" -static void geo_node_switch_declare(NodeDeclarationBuilder &b) +#include "BKE_material.h" + +#include "NOD_socket_search_link.hh" + +#include "FN_multi_function_signature.hh" + +namespace blender::nodes::node_geo_switch_cc { + +NODE_STORAGE_FUNCS(NodeSwitch) + +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Bool>("Switch"); - - b.add_input<decl::Float>("False"); - b.add_input<decl::Float>("True"); - b.add_input<decl::Int>("False", "False_001").min(-100000).max(100000); - b.add_input<decl::Int>("True", "True_001").min(-100000).max(100000); - b.add_input<decl::Bool>("False", "False_002"); - b.add_input<decl::Bool>("True", "True_002"); - b.add_input<decl::Vector>("False", "False_003"); - b.add_input<decl::Vector>("True", "True_003"); - b.add_input<decl::Color>("False", "False_004").default_value({0.8f, 0.8f, 0.8f, 1.0f}); - b.add_input<decl::Color>("True", "True_004").default_value({0.8f, 0.8f, 0.8f, 1.0f}); - b.add_input<decl::String>("False", "False_005"); - b.add_input<decl::String>("True", "True_005"); - b.add_input<decl::Geometry>("False", "False_006"); - b.add_input<decl::Geometry>("True", "True_006"); - b.add_input<decl::Object>("False", "False_007"); - b.add_input<decl::Object>("True", "True_007"); - b.add_input<decl::Collection>("False", "False_008"); - b.add_input<decl::Collection>("True", "True_008"); - b.add_input<decl::Texture>("False", "False_009"); - b.add_input<decl::Texture>("True", "True_009"); - b.add_input<decl::Material>("False", "False_010"); - b.add_input<decl::Material>("True", "True_010"); - - b.add_output<decl::Float>("Output"); - b.add_output<decl::Int>("Output", "Output_001"); - b.add_output<decl::Bool>("Output", "Output_002"); - b.add_output<decl::Vector>("Output", "Output_003"); - b.add_output<decl::Color>("Output", "Output_004"); - b.add_output<decl::String>("Output", "Output_005"); - b.add_output<decl::Geometry>("Output", "Output_006"); - b.add_output<decl::Object>("Output", "Output_007"); - b.add_output<decl::Collection>("Output", "Output_008"); - b.add_output<decl::Texture>("Output", "Output_009"); - b.add_output<decl::Material>("Output", "Output_010"); + b.add_input<decl::Bool>(N_("Switch")).default_value(false).supports_field(); + b.add_input<decl::Bool>(N_("Switch"), "Switch_001").default_value(false); + + b.add_input<decl::Float>(N_("False")).supports_field(); + b.add_input<decl::Float>(N_("True")).supports_field(); + b.add_input<decl::Int>(N_("False"), "False_001").min(-100000).max(100000).supports_field(); + b.add_input<decl::Int>(N_("True"), "True_001").min(-100000).max(100000).supports_field(); + b.add_input<decl::Bool>(N_("False"), "False_002") + .default_value(false) + .hide_value() + .supports_field(); + b.add_input<decl::Bool>(N_("True"), "True_002") + .default_value(true) + .hide_value() + .supports_field(); + b.add_input<decl::Vector>(N_("False"), "False_003").supports_field(); + b.add_input<decl::Vector>(N_("True"), "True_003").supports_field(); + b.add_input<decl::Color>(N_("False"), "False_004") + .default_value({0.8f, 0.8f, 0.8f, 1.0f}) + .supports_field(); + b.add_input<decl::Color>(N_("True"), "True_004") + .default_value({0.8f, 0.8f, 0.8f, 1.0f}) + .supports_field(); + b.add_input<decl::String>(N_("False"), "False_005").supports_field(); + b.add_input<decl::String>(N_("True"), "True_005").supports_field(); + + b.add_input<decl::Geometry>(N_("False"), "False_006"); + b.add_input<decl::Geometry>(N_("True"), "True_006"); + b.add_input<decl::Object>(N_("False"), "False_007"); + b.add_input<decl::Object>(N_("True"), "True_007"); + b.add_input<decl::Collection>(N_("False"), "False_008"); + b.add_input<decl::Collection>(N_("True"), "True_008"); + b.add_input<decl::Texture>(N_("False"), "False_009"); + b.add_input<decl::Texture>(N_("True"), "True_009"); + b.add_input<decl::Material>(N_("False"), "False_010"); + b.add_input<decl::Material>(N_("True"), "True_010"); + b.add_input<decl::Image>(N_("False"), "False_011"); + b.add_input<decl::Image>(N_("True"), "True_011"); + + b.add_output<decl::Float>(N_("Output")).dependent_field(); + b.add_output<decl::Int>(N_("Output"), "Output_001").dependent_field(); + b.add_output<decl::Bool>(N_("Output"), "Output_002").dependent_field(); + b.add_output<decl::Vector>(N_("Output"), "Output_003").dependent_field(); + b.add_output<decl::Color>(N_("Output"), "Output_004").dependent_field(); + b.add_output<decl::String>(N_("Output"), "Output_005").dependent_field(); + b.add_output<decl::Geometry>(N_("Output"), "Output_006"); + b.add_output<decl::Object>(N_("Output"), "Output_007"); + b.add_output<decl::Collection>(N_("Output"), "Output_008"); + b.add_output<decl::Texture>(N_("Output"), "Output_009"); + b.add_output<decl::Material>(N_("Output"), "Output_010"); + b.add_output<decl::Image>(N_("Output"), "Output_011"); } -static void geo_node_switch_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "input_type", 0, "", ICON_NONE); } -static void geo_node_switch_init(bNodeTree *UNUSED(tree), bNode *node) +static void node_init(bNodeTree *UNUSED(tree), bNode *node) { - NodeSwitch *data = (NodeSwitch *)MEM_callocN(sizeof(NodeSwitch), __func__); + NodeSwitch *data = MEM_cnew<NodeSwitch>(__func__); data->input_type = SOCK_GEOMETRY; node->storage = data; } -static void geo_node_switch_update(bNodeTree *UNUSED(ntree), bNode *node) +static void node_update(bNodeTree *ntree, bNode *node) { - NodeSwitch *node_storage = (NodeSwitch *)node->storage; + const NodeSwitch &storage = node_storage(*node); int index = 0; - LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) { - nodeSetSocketAvailability( - socket, index == 0 || socket->type == (eNodeSocketDatatype)node_storage->input_type); - index++; + bNodeSocket *field_switch = (bNodeSocket *)node->inputs.first; + bNodeSocket *non_field_switch = (bNodeSocket *)field_switch->next; + + const bool fields_type = ELEM( + storage.input_type, SOCK_FLOAT, SOCK_INT, SOCK_BOOLEAN, SOCK_VECTOR, SOCK_RGBA, SOCK_STRING); + + nodeSetSocketAvailability(ntree, field_switch, fields_type); + nodeSetSocketAvailability(ntree, non_field_switch, !fields_type); + + LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, &node->inputs, index) { + if (index <= 1) { + continue; + } + nodeSetSocketAvailability(ntree, socket, socket->type == storage.input_type); } + LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) { - nodeSetSocketAvailability(socket, - socket->type == (eNodeSocketDatatype)node_storage->input_type); + nodeSetSocketAvailability(ntree, socket, socket->type == storage.input_type); } } -template<typename T> -static void output_input(GeoNodeExecParams ¶ms, - const bool input, - const StringRef input_suffix, - const StringRef output_identifier) +static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) { - const std::string name_a = "False" + input_suffix; - const std::string name_b = "True" + input_suffix; - if (input) { - params.set_input_unused(name_a); - if (params.lazy_require_input(name_b)) { + if (params.in_out() == SOCK_OUT) { + params.add_item(IFACE_("Output"), [](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node("GeometryNodeSwitch"); + node_storage(node).input_type = params.socket.type; + params.update_and_connect_available_socket(node, "Output"); + }); + } + else { + if (params.other_socket().type == SOCK_BOOLEAN) { + params.add_item(IFACE_("Switch"), [](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node("GeometryNodeSwitch"); + params.connect_available_socket(node, "Start"); + }); + } + params.add_item(IFACE_("False"), [](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node("GeometryNodeSwitch"); + node_storage(node).input_type = params.socket.type; + params.update_and_connect_available_socket(node, "False"); + }); + params.add_item(IFACE_("True"), [](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node("GeometryNodeSwitch"); + node_storage(node).input_type = params.socket.type; + params.update_and_connect_available_socket(node, "True"); + }); + } +} + +template<typename T> class SwitchFieldsFunction : public fn::MultiFunction { + public: + SwitchFieldsFunction() + { + static fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + static fn::MFSignature create_signature() + { + fn::MFSignatureBuilder signature{"Switch"}; + signature.single_input<bool>("Switch"); + signature.single_input<T>("False"); + signature.single_input<T>("True"); + signature.single_output<T>("Output"); + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + const VArray<bool> &switches = params.readonly_single_input<bool>(0, "Switch"); + const VArray<T> &falses = params.readonly_single_input<T>(1, "False"); + const VArray<T> &trues = params.readonly_single_input<T>(2, "True"); + MutableSpan<T> values = params.uninitialized_single_output_if_required<T>(3, "Output"); + for (int64_t i : mask) { + new (&values[i]) T(switches[i] ? trues[i] : falses[i]); + } + } +}; + +template<typename T> void switch_fields(GeoNodeExecParams ¶ms, const StringRef suffix) +{ + if (params.lazy_require_input("Switch")) { + return; + } + + const std::string name_false = "False" + suffix; + const std::string name_true = "True" + suffix; + const std::string name_output = "Output" + suffix; + + Field<bool> switches_field = params.get_input<Field<bool>>("Switch"); + if (switches_field.node().depends_on_input()) { + /* The switch has to be incorporated into the field. Both inputs have to be evaluated. */ + const bool require_false = params.lazy_require_input(name_false); + const bool require_true = params.lazy_require_input(name_true); + if (require_false | require_true) { return; } - params.set_output(output_identifier, params.extract_input<T>(name_b)); + + Field<T> falses_field = params.extract_input<Field<T>>(name_false); + Field<T> trues_field = params.extract_input<Field<T>>(name_true); + + auto switch_fn = std::make_unique<SwitchFieldsFunction<T>>(); + auto switch_op = std::make_shared<FieldOperation>(FieldOperation( + std::move(switch_fn), + {std::move(switches_field), std::move(falses_field), std::move(trues_field)})); + + params.set_output(name_output, Field<T>(switch_op, 0)); } else { - params.set_input_unused(name_b); - if (params.lazy_require_input(name_a)) { - return; + /* The switch input is constant, so just evaluate and forward one of the inputs. */ + const bool switch_value = fn::evaluate_constant_field(switches_field); + if (switch_value) { + params.set_input_unused(name_false); + if (params.lazy_require_input(name_true)) { + return; + } + params.set_output(name_output, params.extract_input<Field<T>>(name_true)); + } + else { + params.set_input_unused(name_true); + if (params.lazy_require_input(name_false)) { + return; + } + params.set_output(name_output, params.extract_input<Field<T>>(name_false)); } - params.set_output(output_identifier, params.extract_input<T>(name_a)); } } -static void geo_node_switch_exec(GeoNodeExecParams params) +template<typename T> void switch_no_fields(GeoNodeExecParams ¶ms, const StringRef suffix) { - if (params.lazy_require_input("Switch")) { + if (params.lazy_require_input("Switch_001")) { return; } - const NodeSwitch &storage = *(const NodeSwitch *)params.node().storage; - const bool input = params.get_input<bool>("Switch"); - switch ((eNodeSocketDatatype)storage.input_type) { + bool switch_value = params.get_input<bool>("Switch_001"); + + const std::string name_false = "False" + suffix; + const std::string name_true = "True" + suffix; + const std::string name_output = "Output" + suffix; + + if (switch_value) { + params.set_input_unused(name_false); + if (params.lazy_require_input(name_true)) { + return; + } + params.set_output(name_output, params.extract_input<T>(name_true)); + } + else { + params.set_input_unused(name_true); + if (params.lazy_require_input(name_false)) { + return; + } + params.set_output(name_output, params.extract_input<T>(name_false)); + } +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + const NodeSwitch &storage = node_storage(params.node()); + const eNodeSocketDatatype data_type = static_cast<eNodeSocketDatatype>(storage.input_type); + + switch (data_type) { + case SOCK_FLOAT: { - output_input<float>(params, input, "", "Output"); + switch_fields<float>(params, ""); break; } case SOCK_INT: { - output_input<int>(params, input, "_001", "Output_001"); + switch_fields<int>(params, "_001"); break; } case SOCK_BOOLEAN: { - output_input<bool>(params, input, "_002", "Output_002"); + switch_fields<bool>(params, "_002"); break; } case SOCK_VECTOR: { - output_input<float3>(params, input, "_003", "Output_003"); + switch_fields<float3>(params, "_003"); break; } case SOCK_RGBA: { - output_input<ColorGeometry4f>(params, input, "_004", "Output_004"); + switch_fields<ColorGeometry4f>(params, "_004"); break; } case SOCK_STRING: { - output_input<std::string>(params, input, "_005", "Output_005"); + switch_fields<std::string>(params, "_005"); break; } case SOCK_GEOMETRY: { - output_input<GeometrySet>(params, input, "_006", "Output_006"); + switch_no_fields<GeometrySet>(params, "_006"); break; } case SOCK_OBJECT: { - output_input<Object *>(params, input, "_007", "Output_007"); + switch_no_fields<Object *>(params, "_007"); break; } case SOCK_COLLECTION: { - output_input<Collection *>(params, input, "_008", "Output_008"); + switch_no_fields<Collection *>(params, "_008"); break; } case SOCK_TEXTURE: { - output_input<Tex *>(params, input, "_009", "Output_009"); + switch_no_fields<Tex *>(params, "_009"); break; } case SOCK_MATERIAL: { - output_input<Material *>(params, input, "_010", "Output_010"); + switch_no_fields<Material *>(params, "_010"); + break; + } + case SOCK_IMAGE: { + switch_no_fields<Image *>(params, "_011"); break; } default: @@ -170,19 +319,22 @@ static void geo_node_switch_exec(GeoNodeExecParams params) } } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_switch_cc void register_node_type_geo_switch() { + namespace file_ns = blender::nodes::node_geo_switch_cc; + static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_SWITCH, "Switch", NODE_CLASS_CONVERTER, 0); - ntype.declare = blender::nodes::geo_node_switch_declare; - node_type_init(&ntype, blender::nodes::geo_node_switch_init); - node_type_update(&ntype, blender::nodes::geo_node_switch_update); + geo_node_type_base(&ntype, GEO_NODE_SWITCH, "Switch", NODE_CLASS_CONVERTER); + ntype.declare = file_ns::node_declare; + node_type_init(&ntype, file_ns::node_init); + node_type_update(&ntype, file_ns::node_update); node_type_storage(&ntype, "NodeSwitch", node_free_standard_storage, node_copy_standard_storage); - ntype.geometry_node_execute = blender::nodes::geo_node_switch_exec; + ntype.geometry_node_execute = file_ns::node_geo_exec; ntype.geometry_node_execute_supports_laziness = true; - ntype.draw_buttons = blender::nodes::geo_node_switch_layout; + ntype.gather_link_search_ops = file_ns::node_gather_link_searches; + ntype.draw_buttons = file_ns::node_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc new file mode 100644 index 00000000000..5a8d9ab470d --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc @@ -0,0 +1,842 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_kdopbvh.h" +#include "BLI_task.hh" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_pointcloud_types.h" + +#include "BKE_attribute_math.hh" +#include "BKE_bvhutils.h" +#include "BKE_mesh_runtime.h" +#include "BKE_mesh_sample.hh" + +#include "FN_generic_array.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "NOD_socket_search_link.hh" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_transfer_attribute_cc { + +using namespace blender::bke::mesh_surface_sample; +using blender::fn::GArray; + +NODE_STORAGE_FUNCS(NodeGeometryTransferAttribute) + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>(N_("Source")) + .supported_type({GEO_COMPONENT_TYPE_MESH, + GEO_COMPONENT_TYPE_POINT_CLOUD, + GEO_COMPONENT_TYPE_CURVE, + GEO_COMPONENT_TYPE_INSTANCES}); + + b.add_input<decl::Vector>(N_("Attribute")).hide_value().supports_field(); + b.add_input<decl::Float>(N_("Attribute"), "Attribute_001").hide_value().supports_field(); + b.add_input<decl::Color>(N_("Attribute"), "Attribute_002").hide_value().supports_field(); + b.add_input<decl::Bool>(N_("Attribute"), "Attribute_003").hide_value().supports_field(); + b.add_input<decl::Int>(N_("Attribute"), "Attribute_004").hide_value().supports_field(); + + b.add_input<decl::Vector>(N_("Source Position")) + .implicit_field() + .make_available([](bNode &node) { + node_storage(node).mode = GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED; + }); + b.add_input<decl::Int>(N_("Index")).implicit_field().make_available([](bNode &node) { + node_storage(node).mode = GEO_NODE_ATTRIBUTE_TRANSFER_INDEX; + }); + + b.add_output<decl::Vector>(N_("Attribute")).dependent_field({6, 7}); + b.add_output<decl::Float>(N_("Attribute"), "Attribute_001").dependent_field({6, 7}); + b.add_output<decl::Color>(N_("Attribute"), "Attribute_002").dependent_field({6, 7}); + b.add_output<decl::Bool>(N_("Attribute"), "Attribute_003").dependent_field({6, 7}); + b.add_output<decl::Int>(N_("Attribute"), "Attribute_004").dependent_field({6, 7}); +} + +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + const bNode &node = *static_cast<const bNode *>(ptr->data); + const NodeGeometryTransferAttribute &storage = node_storage(node); + const GeometryNodeAttributeTransferMode mapping = (GeometryNodeAttributeTransferMode) + storage.mode; + + uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); + uiItemR(layout, ptr, "mapping", 0, "", ICON_NONE); + if (mapping != GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED) { + uiItemR(layout, ptr, "domain", 0, "", ICON_NONE); + } +} + +static void node_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryTransferAttribute *data = MEM_cnew<NodeGeometryTransferAttribute>(__func__); + data->data_type = CD_PROP_FLOAT; + data->mode = GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED; + node->storage = data; +} + +static void node_update(bNodeTree *ntree, bNode *node) +{ + const NodeGeometryTransferAttribute &storage = node_storage(*node); + const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type); + const GeometryNodeAttributeTransferMode mapping = (GeometryNodeAttributeTransferMode) + storage.mode; + + bNodeSocket *socket_geometry = (bNodeSocket *)node->inputs.first; + bNodeSocket *socket_vector = socket_geometry->next; + bNodeSocket *socket_float = socket_vector->next; + bNodeSocket *socket_color4f = socket_float->next; + bNodeSocket *socket_boolean = socket_color4f->next; + bNodeSocket *socket_int32 = socket_boolean->next; + + bNodeSocket *socket_positions = socket_int32->next; + bNodeSocket *socket_indices = socket_positions->next; + + nodeSetSocketAvailability(ntree, socket_vector, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(ntree, socket_float, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(ntree, socket_color4f, data_type == CD_PROP_COLOR); + nodeSetSocketAvailability(ntree, socket_boolean, data_type == CD_PROP_BOOL); + nodeSetSocketAvailability(ntree, socket_int32, data_type == CD_PROP_INT32); + + nodeSetSocketAvailability(ntree, socket_positions, mapping != GEO_NODE_ATTRIBUTE_TRANSFER_INDEX); + nodeSetSocketAvailability(ntree, socket_indices, mapping == GEO_NODE_ATTRIBUTE_TRANSFER_INDEX); + + bNodeSocket *out_socket_vector = (bNodeSocket *)node->outputs.first; + bNodeSocket *out_socket_float = out_socket_vector->next; + bNodeSocket *out_socket_color4f = out_socket_float->next; + bNodeSocket *out_socket_boolean = out_socket_color4f->next; + bNodeSocket *out_socket_int32 = out_socket_boolean->next; + + nodeSetSocketAvailability(ntree, out_socket_vector, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(ntree, out_socket_float, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(ntree, out_socket_color4f, data_type == CD_PROP_COLOR); + nodeSetSocketAvailability(ntree, out_socket_boolean, data_type == CD_PROP_BOOL); + nodeSetSocketAvailability(ntree, out_socket_int32, data_type == CD_PROP_INT32); +} + +static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) +{ + const NodeDeclaration &declaration = *params.node_type().fixed_declaration; + search_link_ops_for_declarations(params, declaration.inputs().take_back(2)); + search_link_ops_for_declarations(params, declaration.inputs().take_front(1)); + + const std::optional<CustomDataType> type = node_data_type_to_custom_data_type( + (eNodeSocketDatatype)params.other_socket().type); + if (type && *type != CD_PROP_STRING) { + /* The input and output sockets have the same name. */ + params.add_item(IFACE_("Attribute"), [type](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node("GeometryNodeAttributeTransfer"); + node_storage(node).data_type = *type; + params.update_and_connect_available_socket(node, "Attribute"); + }); + } +} + +static void get_closest_in_bvhtree(BVHTreeFromMesh &tree_data, + const VArray<float3> &positions, + const IndexMask mask, + const MutableSpan<int> r_indices, + const MutableSpan<float> r_distances_sq, + const MutableSpan<float3> r_positions) +{ + BLI_assert(positions.size() >= r_indices.size()); + BLI_assert(positions.size() >= r_distances_sq.size()); + BLI_assert(positions.size() >= r_positions.size()); + + for (const int i : mask) { + BVHTreeNearest nearest; + nearest.dist_sq = FLT_MAX; + const float3 position = positions[i]; + BLI_bvhtree_find_nearest( + tree_data.tree, position, &nearest, tree_data.nearest_callback, &tree_data); + if (!r_indices.is_empty()) { + r_indices[i] = nearest.index; + } + if (!r_distances_sq.is_empty()) { + r_distances_sq[i] = nearest.dist_sq; + } + if (!r_positions.is_empty()) { + r_positions[i] = nearest.co; + } + } +} + +static void get_closest_pointcloud_points(const PointCloud &pointcloud, + const VArray<float3> &positions, + const IndexMask mask, + const MutableSpan<int> r_indices, + const MutableSpan<float> r_distances_sq) +{ + BLI_assert(positions.size() >= r_indices.size()); + BLI_assert(pointcloud.totpoint > 0); + + BVHTreeFromPointCloud tree_data; + BKE_bvhtree_from_pointcloud_get(&tree_data, &pointcloud, 2); + + for (const int i : mask) { + BVHTreeNearest nearest; + nearest.dist_sq = FLT_MAX; + const float3 position = positions[i]; + BLI_bvhtree_find_nearest( + tree_data.tree, position, &nearest, tree_data.nearest_callback, &tree_data); + r_indices[i] = nearest.index; + if (!r_distances_sq.is_empty()) { + r_distances_sq[i] = nearest.dist_sq; + } + } + + free_bvhtree_from_pointcloud(&tree_data); +} + +static void get_closest_mesh_points(const Mesh &mesh, + const VArray<float3> &positions, + const IndexMask mask, + const MutableSpan<int> r_point_indices, + const MutableSpan<float> r_distances_sq, + const MutableSpan<float3> r_positions) +{ + BLI_assert(mesh.totvert > 0); + BVHTreeFromMesh tree_data; + BKE_bvhtree_from_mesh_get(&tree_data, &mesh, BVHTREE_FROM_VERTS, 2); + get_closest_in_bvhtree(tree_data, positions, mask, r_point_indices, r_distances_sq, r_positions); + free_bvhtree_from_mesh(&tree_data); +} + +static void get_closest_mesh_edges(const Mesh &mesh, + const VArray<float3> &positions, + const IndexMask mask, + const MutableSpan<int> r_edge_indices, + const MutableSpan<float> r_distances_sq, + const MutableSpan<float3> r_positions) +{ + BLI_assert(mesh.totedge > 0); + BVHTreeFromMesh tree_data; + BKE_bvhtree_from_mesh_get(&tree_data, &mesh, BVHTREE_FROM_EDGES, 2); + get_closest_in_bvhtree(tree_data, positions, mask, r_edge_indices, r_distances_sq, r_positions); + free_bvhtree_from_mesh(&tree_data); +} + +static void get_closest_mesh_looptris(const Mesh &mesh, + const VArray<float3> &positions, + const IndexMask mask, + const MutableSpan<int> r_looptri_indices, + const MutableSpan<float> r_distances_sq, + const MutableSpan<float3> r_positions) +{ + BLI_assert(mesh.totpoly > 0); + BVHTreeFromMesh tree_data; + BKE_bvhtree_from_mesh_get(&tree_data, &mesh, BVHTREE_FROM_LOOPTRI, 2); + get_closest_in_bvhtree( + tree_data, positions, mask, r_looptri_indices, r_distances_sq, r_positions); + free_bvhtree_from_mesh(&tree_data); +} + +static void get_closest_mesh_polygons(const Mesh &mesh, + const VArray<float3> &positions, + const IndexMask mask, + const MutableSpan<int> r_poly_indices, + const MutableSpan<float> r_distances_sq, + const MutableSpan<float3> r_positions) +{ + BLI_assert(mesh.totpoly > 0); + + Array<int> looptri_indices(positions.size()); + get_closest_mesh_looptris(mesh, positions, mask, looptri_indices, r_distances_sq, r_positions); + + const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&mesh), + BKE_mesh_runtime_looptri_len(&mesh)}; + + for (const int i : mask) { + const MLoopTri &looptri = looptris[looptri_indices[i]]; + r_poly_indices[i] = looptri.poly; + } +} + +/* The closest corner is defined to be the closest corner on the closest face. */ +static void get_closest_mesh_corners(const Mesh &mesh, + const VArray<float3> &positions, + const IndexMask mask, + const MutableSpan<int> r_corner_indices, + const MutableSpan<float> r_distances_sq, + const MutableSpan<float3> r_positions) +{ + BLI_assert(mesh.totloop > 0); + Array<int> poly_indices(positions.size()); + get_closest_mesh_polygons(mesh, positions, mask, poly_indices, {}, {}); + + for (const int i : mask) { + const float3 position = positions[i]; + const int poly_index = poly_indices[i]; + const MPoly &poly = mesh.mpoly[poly_index]; + + /* Find the closest vertex in the polygon. */ + float min_distance_sq = FLT_MAX; + const MVert *closest_mvert; + int closest_loop_index = 0; + for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { + const MLoop &loop = mesh.mloop[loop_index]; + const int vertex_index = loop.v; + const MVert &mvert = mesh.mvert[vertex_index]; + const float distance_sq = math::distance_squared(position, float3(mvert.co)); + if (distance_sq < min_distance_sq) { + min_distance_sq = distance_sq; + closest_loop_index = loop_index; + closest_mvert = &mvert; + } + } + if (!r_corner_indices.is_empty()) { + r_corner_indices[i] = closest_loop_index; + } + if (!r_positions.is_empty()) { + r_positions[i] = closest_mvert->co; + } + if (!r_distances_sq.is_empty()) { + r_distances_sq[i] = min_distance_sq; + } + } +} + +template<typename T> +void copy_with_indices(const VArray<T> &src, + const IndexMask mask, + const Span<int> indices, + const MutableSpan<T> dst) +{ + if (src.is_empty()) { + return; + } + for (const int i : mask) { + dst[i] = src[indices[i]]; + } +} + +template<typename T> +void copy_with_indices_clamped(const VArray<T> &src, + const IndexMask mask, + const VArray<int> &indices, + const MutableSpan<T> dst) +{ + if (src.is_empty()) { + return; + } + const int max_index = src.size() - 1; + threading::parallel_for(mask.index_range(), 4096, [&](IndexRange range) { + for (const int i : range) { + const int index = mask[i]; + dst[index] = src[std::clamp(indices[index], 0, max_index)]; + } + }); +} + +template<typename T> +void copy_with_indices_and_comparison(const VArray<T> &src_1, + const VArray<T> &src_2, + const Span<float> distances_1, + const Span<float> distances_2, + const IndexMask mask, + const Span<int> indices_1, + const Span<int> indices_2, + const MutableSpan<T> dst) +{ + if (src_1.is_empty() || src_2.is_empty()) { + return; + } + for (const int i : mask) { + if (distances_1[i] < distances_2[i]) { + dst[i] = src_1[indices_1[i]]; + } + else { + dst[i] = src_2[indices_2[i]]; + } + } +} + +static bool component_is_available(const GeometrySet &geometry, + const GeometryComponentType type, + const AttributeDomain domain) +{ + if (!geometry.has(type)) { + return false; + } + const GeometryComponent &component = *geometry.get_component_for_read(type); + if (component.is_empty()) { + return false; + } + return component.attribute_domain_size(domain) != 0; +} + +/** + * \note Multi-threading for this function is provided by the field evaluator. Since the #call + * function could be called many times, calculate the data from the target geometry once and store + * it for later. + */ +class NearestInterpolatedTransferFunction : public fn::MultiFunction { + GeometrySet target_; + GField src_field_; + + /** + * This function is meant to sample the surface of a mesh rather than take the value from + * individual elements, so use the most complex domain, ensuring no information is lost. In the + * future, it should be possible to use the most complex domain required by the field inputs, to + * simplify sampling and avoid domain conversions. + */ + AttributeDomain domain_ = ATTR_DOMAIN_CORNER; + + fn::MFSignature signature_; + + std::optional<GeometryComponentFieldContext> target_context_; + std::unique_ptr<FieldEvaluator> target_evaluator_; + const GVArray *target_data_; + + public: + NearestInterpolatedTransferFunction(GeometrySet geometry, GField src_field) + : target_(std::move(geometry)), src_field_(std::move(src_field)) + { + target_.ensure_owns_direct_data(); + signature_ = this->create_signature(); + this->set_signature(&signature_); + this->evaluate_target_field(); + } + + fn::MFSignature create_signature() + { + blender::fn::MFSignatureBuilder signature{"Attribute Transfer Nearest Interpolated"}; + signature.single_input<float3>("Position"); + signature.single_output("Attribute", src_field_.cpp_type()); + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + const VArray<float3> &positions = params.readonly_single_input<float3>(0, "Position"); + GMutableSpan dst = params.uninitialized_single_output_if_required(1, "Attribute"); + + const MeshComponent &mesh_component = *target_.get_component_for_read<MeshComponent>(); + BLI_assert(mesh_component.has_mesh()); + const Mesh &mesh = *mesh_component.get_for_read(); + BLI_assert(mesh.totpoly > 0); + + /* Find closest points on the mesh surface. */ + Array<int> looptri_indices(mask.min_array_size()); + Array<float3> sampled_positions(mask.min_array_size()); + get_closest_mesh_looptris(mesh, positions, mask, looptri_indices, {}, sampled_positions); + + MeshAttributeInterpolator interp(&mesh, mask, sampled_positions, looptri_indices); + interp.sample_data(*target_data_, domain_, eAttributeMapMode::INTERPOLATED, dst); + } + + private: + void evaluate_target_field() + { + const MeshComponent &mesh_component = *target_.get_component_for_read<MeshComponent>(); + target_context_.emplace(GeometryComponentFieldContext{mesh_component, domain_}); + const int domain_size = mesh_component.attribute_domain_size(domain_); + target_evaluator_ = std::make_unique<FieldEvaluator>(*target_context_, domain_size); + target_evaluator_->add(src_field_); + target_evaluator_->evaluate(); + target_data_ = &target_evaluator_->get_evaluated(0); + } +}; + +/** + * \note Multi-threading for this function is provided by the field evaluator. Since the #call + * function could be called many times, calculate the data from the target geometry once and store + * it for later. + */ +class NearestTransferFunction : public fn::MultiFunction { + GeometrySet target_; + GField src_field_; + AttributeDomain domain_; + + fn::MFSignature signature_; + + bool use_mesh_; + bool use_points_; + + /* Store data from the target as a virtual array, since we may only access a few indices. */ + std::optional<GeometryComponentFieldContext> mesh_context_; + std::unique_ptr<FieldEvaluator> mesh_evaluator_; + const GVArray *mesh_data_; + + std::optional<GeometryComponentFieldContext> point_context_; + std::unique_ptr<FieldEvaluator> point_evaluator_; + const GVArray *point_data_; + + public: + NearestTransferFunction(GeometrySet geometry, GField src_field, AttributeDomain domain) + : target_(std::move(geometry)), src_field_(std::move(src_field)), domain_(domain) + { + target_.ensure_owns_direct_data(); + signature_ = this->create_signature(); + this->set_signature(&signature_); + + this->use_mesh_ = component_is_available(target_, GEO_COMPONENT_TYPE_MESH, domain_); + this->use_points_ = component_is_available(target_, GEO_COMPONENT_TYPE_POINT_CLOUD, domain_); + + this->evaluate_target_field(); + } + + fn::MFSignature create_signature() + { + blender::fn::MFSignatureBuilder signature{"Attribute Transfer Nearest"}; + signature.single_input<float3>("Position"); + signature.single_output("Attribute", src_field_.cpp_type()); + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + const VArray<float3> &positions = params.readonly_single_input<float3>(0, "Position"); + GMutableSpan dst = params.uninitialized_single_output_if_required(1, "Attribute"); + + if (!use_mesh_ && !use_points_) { + dst.type().fill_construct_indices(dst.type().default_value(), dst.data(), mask); + return; + } + + const Mesh *mesh = use_mesh_ ? target_.get_mesh_for_read() : nullptr; + const PointCloud *pointcloud = use_points_ ? target_.get_pointcloud_for_read() : nullptr; + + const int tot_samples = mask.min_array_size(); + + Array<int> point_indices; + Array<float> point_distances; + + /* Depending on where what domain the source attribute lives, these indices are either vertex, + * corner, edge or polygon indices. */ + Array<int> mesh_indices; + Array<float> mesh_distances; + + /* If there is a point cloud, find the closest points. */ + if (use_points_) { + point_indices.reinitialize(tot_samples); + if (use_mesh_) { + point_distances.reinitialize(tot_samples); + } + get_closest_pointcloud_points(*pointcloud, positions, mask, point_indices, point_distances); + } + + /* If there is a mesh, find the closest mesh elements. */ + if (use_mesh_) { + mesh_indices.reinitialize(tot_samples); + if (use_points_) { + mesh_distances.reinitialize(tot_samples); + } + switch (domain_) { + case ATTR_DOMAIN_POINT: { + get_closest_mesh_points(*mesh, positions, mask, mesh_indices, mesh_distances, {}); + break; + } + case ATTR_DOMAIN_EDGE: { + get_closest_mesh_edges(*mesh, positions, mask, mesh_indices, mesh_distances, {}); + break; + } + case ATTR_DOMAIN_FACE: { + get_closest_mesh_polygons(*mesh, positions, mask, mesh_indices, mesh_distances, {}); + break; + } + case ATTR_DOMAIN_CORNER: { + get_closest_mesh_corners(*mesh, positions, mask, mesh_indices, mesh_distances, {}); + break; + } + default: { + break; + } + } + } + + attribute_math::convert_to_static_type(dst.type(), [&](auto dummy) { + using T = decltype(dummy); + if (use_mesh_ && use_points_) { + VArray<T> src_mesh = mesh_data_->typed<T>(); + VArray<T> src_point = point_data_->typed<T>(); + copy_with_indices_and_comparison(src_mesh, + src_point, + mesh_distances, + point_distances, + mask, + mesh_indices, + point_indices, + dst.typed<T>()); + } + else if (use_points_) { + VArray<T> src_point = point_data_->typed<T>(); + copy_with_indices(src_point, mask, point_indices, dst.typed<T>()); + } + else if (use_mesh_) { + VArray<T> src_mesh = mesh_data_->typed<T>(); + copy_with_indices(src_mesh, mask, mesh_indices, dst.typed<T>()); + } + }); + } + + private: + void evaluate_target_field() + { + if (use_mesh_) { + const MeshComponent &mesh = *target_.get_component_for_read<MeshComponent>(); + const int domain_size = mesh.attribute_domain_size(domain_); + mesh_context_.emplace(GeometryComponentFieldContext(mesh, domain_)); + mesh_evaluator_ = std::make_unique<FieldEvaluator>(*mesh_context_, domain_size); + mesh_evaluator_->add(src_field_); + mesh_evaluator_->evaluate(); + mesh_data_ = &mesh_evaluator_->get_evaluated(0); + } + + if (use_points_) { + const PointCloudComponent &points = *target_.get_component_for_read<PointCloudComponent>(); + const int domain_size = points.attribute_domain_size(domain_); + point_context_.emplace(GeometryComponentFieldContext(points, domain_)); + point_evaluator_ = std::make_unique<FieldEvaluator>(*point_context_, domain_size); + point_evaluator_->add(src_field_); + point_evaluator_->evaluate(); + point_data_ = &point_evaluator_->get_evaluated(0); + } + } +}; + +static const GeometryComponent *find_target_component(const GeometrySet &geometry, + const AttributeDomain domain) +{ + /* Choose the other component based on a consistent order, rather than some more complicated + * heuristic. This is the same order visible in the spreadsheet and used in the ray-cast node. */ + static const Array<GeometryComponentType> supported_types = {GEO_COMPONENT_TYPE_MESH, + GEO_COMPONENT_TYPE_POINT_CLOUD, + GEO_COMPONENT_TYPE_CURVE, + GEO_COMPONENT_TYPE_INSTANCES}; + for (const GeometryComponentType src_type : supported_types) { + if (component_is_available(geometry, src_type, domain)) { + return geometry.get_component_for_read(src_type); + } + } + + return nullptr; +} + +/** + * The index-based transfer theoretically does not need realized data when there is only one + * instance geometry set in the target. A future optimization could be removing that limitation + * internally. + */ +class IndexTransferFunction : public fn::MultiFunction { + GeometrySet src_geometry_; + GField src_field_; + AttributeDomain domain_; + + fn::MFSignature signature_; + + std::optional<GeometryComponentFieldContext> geometry_context_; + std::unique_ptr<FieldEvaluator> evaluator_; + const GVArray *src_data_ = nullptr; + + public: + IndexTransferFunction(GeometrySet geometry, GField src_field, const AttributeDomain domain) + : src_geometry_(std::move(geometry)), src_field_(std::move(src_field)), domain_(domain) + { + src_geometry_.ensure_owns_direct_data(); + + signature_ = this->create_signature(); + this->set_signature(&signature_); + + this->evaluate_field(); + } + + fn::MFSignature create_signature() + { + fn::MFSignatureBuilder signature{"Attribute Transfer Index"}; + signature.single_input<int>("Index"); + signature.single_output("Attribute", src_field_.cpp_type()); + return signature.build(); + } + + void evaluate_field() + { + const GeometryComponent *component = find_target_component(src_geometry_, domain_); + if (component == nullptr) { + return; + } + const int domain_size = component->attribute_domain_size(domain_); + geometry_context_.emplace(GeometryComponentFieldContext(*component, domain_)); + evaluator_ = std::make_unique<FieldEvaluator>(*geometry_context_, domain_size); + evaluator_->add(src_field_); + evaluator_->evaluate(); + src_data_ = &evaluator_->get_evaluated(0); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + const VArray<int> &indices = params.readonly_single_input<int>(0, "Index"); + GMutableSpan dst = params.uninitialized_single_output(1, "Attribute"); + + const CPPType &type = dst.type(); + if (src_data_ == nullptr) { + type.fill_construct_indices(type.default_value(), dst.data(), mask); + return; + } + + attribute_math::convert_to_static_type(type, [&](auto dummy) { + using T = decltype(dummy); + copy_with_indices_clamped(src_data_->typed<T>(), mask, indices, dst.typed<T>()); + }); + } +}; + +static GField get_input_attribute_field(GeoNodeExecParams ¶ms, const CustomDataType data_type) +{ + switch (data_type) { + case CD_PROP_FLOAT: + return params.extract_input<Field<float>>("Attribute_001"); + case CD_PROP_FLOAT3: + return params.extract_input<Field<float3>>("Attribute"); + case CD_PROP_COLOR: + return params.extract_input<Field<ColorGeometry4f>>("Attribute_002"); + case CD_PROP_BOOL: + return params.extract_input<Field<bool>>("Attribute_003"); + case CD_PROP_INT32: + return params.extract_input<Field<int>>("Attribute_004"); + default: + BLI_assert_unreachable(); + } + return {}; +} + +static void output_attribute_field(GeoNodeExecParams ¶ms, GField field) +{ + switch (bke::cpp_type_to_custom_data_type(field.cpp_type())) { + case CD_PROP_FLOAT: { + params.set_output("Attribute_001", Field<float>(field)); + break; + } + case CD_PROP_FLOAT3: { + params.set_output("Attribute", Field<float3>(field)); + break; + } + case CD_PROP_COLOR: { + params.set_output("Attribute_002", Field<ColorGeometry4f>(field)); + break; + } + case CD_PROP_BOOL: { + params.set_output("Attribute_003", Field<bool>(field)); + break; + } + case CD_PROP_INT32: { + params.set_output("Attribute_004", Field<int>(field)); + break; + } + default: + break; + } +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + GeometrySet geometry = params.extract_input<GeometrySet>("Source"); + const NodeGeometryTransferAttribute &storage = node_storage(params.node()); + const GeometryNodeAttributeTransferMode mapping = (GeometryNodeAttributeTransferMode) + storage.mode; + const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type); + const AttributeDomain domain = static_cast<AttributeDomain>(storage.domain); + + GField field = get_input_attribute_field(params, data_type); + + auto return_default = [&]() { + attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + using T = decltype(dummy); + output_attribute_field(params, fn::make_constant_field<T>(T())); + }); + }; + + GField output_field; + switch (mapping) { + case GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED: { + const Mesh *mesh = geometry.get_mesh_for_read(); + if (mesh == nullptr) { + if (!geometry.is_empty()) { + params.error_message_add(NodeWarningType::Error, + TIP_("The target geometry must contain a mesh")); + } + return return_default(); + } + if (mesh->totpoly == 0) { + /* Don't add a warning for empty meshes. */ + if (mesh->totvert != 0) { + params.error_message_add(NodeWarningType::Error, + TIP_("The target mesh must have faces")); + } + return return_default(); + } + auto fn = std::make_unique<NearestInterpolatedTransferFunction>(std::move(geometry), + std::move(field)); + auto op = std::make_shared<FieldOperation>( + FieldOperation(std::move(fn), {params.extract_input<Field<float3>>("Source Position")})); + output_field = GField(std::move(op)); + break; + } + case GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST: { + if (geometry.has_curve() && !geometry.has_mesh() && !geometry.has_pointcloud()) { + params.error_message_add(NodeWarningType::Error, + TIP_("The target geometry must contain a mesh or a point cloud")); + return return_default(); + } + auto fn = std::make_unique<NearestTransferFunction>( + std::move(geometry), std::move(field), domain); + auto op = std::make_shared<FieldOperation>( + FieldOperation(std::move(fn), {params.extract_input<Field<float3>>("Source Position")})); + output_field = GField(std::move(op)); + break; + } + case GEO_NODE_ATTRIBUTE_TRANSFER_INDEX: { + Field<int> indices = params.extract_input<Field<int>>("Index"); + auto fn = std::make_unique<IndexTransferFunction>( + std::move(geometry), std::move(field), domain); + auto op = std::make_shared<FieldOperation>( + FieldOperation(std::move(fn), {std::move(indices)})); + output_field = GField(std::move(op)); + break; + } + } + + output_attribute_field(params, std::move(output_field)); +} + +} // namespace blender::nodes::node_geo_transfer_attribute_cc + +void register_node_type_geo_transfer_attribute() +{ + namespace file_ns = blender::nodes::node_geo_transfer_attribute_cc; + + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_TRANSFER_ATTRIBUTE, "Transfer Attribute", NODE_CLASS_ATTRIBUTE); + node_type_init(&ntype, file_ns::node_init); + node_type_update(&ntype, file_ns::node_update); + node_type_storage(&ntype, + "NodeGeometryTransferAttribute", + node_free_standard_storage, + node_copy_standard_storage); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.draw_buttons = file_ns::node_layout; + ntype.gather_link_search_ops = file_ns::node_gather_link_searches; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_transform.cc b/source/blender/nodes/geometry/nodes/node_geo_transform.cc index d5eb067cad0..6187a2eacf9 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_transform.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_transform.cc @@ -25,6 +25,7 @@ #include "DNA_volume_types.h" #include "BKE_mesh.h" +#include "BKE_pointcloud.h" #include "BKE_spline.hh" #include "BKE_volume.h" @@ -34,18 +35,9 @@ namespace blender::nodes { -static void geo_node_transform_declare(NodeDeclarationBuilder &b) -{ - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Vector>("Translation").subtype(PROP_TRANSLATION); - b.add_input<decl::Vector>("Rotation").subtype(PROP_EULER); - b.add_input<decl::Vector>("Scale").default_value({1, 1, 1}).subtype(PROP_XYZ); - b.add_output<decl::Geometry>("Geometry"); -} - static bool use_translate(const float3 rotation, const float3 scale) { - if (compare_ff(rotation.length_squared(), 0.0f, 1e-9f) != 1) { + if (compare_ff(math::length_squared(rotation), 0.0f, 1e-9f) != 1) { return false; } if (compare_ff(scale.x, 1.0f, 1e-9f) != 1 || compare_ff(scale.y, 1.0f, 1e-9f) != 1 || @@ -55,153 +47,187 @@ static bool use_translate(const float3 rotation, const float3 scale) return true; } -void transform_mesh(Mesh *mesh, - const float3 translation, - const float3 rotation, - const float3 scale) +static void translate_mesh(Mesh &mesh, const float3 translation) { - /* Use only translation if rotation and scale are zero. */ - if (use_translate(rotation, scale)) { - if (!translation.is_zero()) { - BKE_mesh_translate(mesh, translation, false); - } - } - else { - const float4x4 matrix = float4x4::from_loc_eul_scale(translation, rotation, scale); - BKE_mesh_transform(mesh, matrix.values, false); - BKE_mesh_normals_tag_dirty(mesh); + if (!math::is_zero(translation)) { + BKE_mesh_translate(&mesh, translation, false); } } -static void transform_pointcloud(PointCloud *pointcloud, - const float3 translation, - const float3 rotation, - const float3 scale) +static void transform_mesh(Mesh &mesh, const float4x4 &transform) { - /* Use only translation if rotation and scale don't apply. */ - if (use_translate(rotation, scale)) { - for (const int i : IndexRange(pointcloud->totpoint)) { - add_v3_v3(pointcloud->co[i], translation); - } + BKE_mesh_transform(&mesh, transform.values, false); + BKE_mesh_normals_tag_dirty(&mesh); +} + +static void translate_pointcloud(PointCloud &pointcloud, const float3 translation) +{ + CustomData_duplicate_referenced_layer(&pointcloud.pdata, CD_PROP_FLOAT3, pointcloud.totpoint); + BKE_pointcloud_update_customdata_pointers(&pointcloud); + for (const int i : IndexRange(pointcloud.totpoint)) { + add_v3_v3(pointcloud.co[i], translation); } - else { - const float4x4 matrix = float4x4::from_loc_eul_scale(translation, rotation, scale); - for (const int i : IndexRange(pointcloud->totpoint)) { - float3 &co = *(float3 *)pointcloud->co[i]; - co = matrix * co; - } +} + +static void transform_pointcloud(PointCloud &pointcloud, const float4x4 &transform) +{ + CustomData_duplicate_referenced_layer(&pointcloud.pdata, CD_PROP_FLOAT3, pointcloud.totpoint); + BKE_pointcloud_update_customdata_pointers(&pointcloud); + for (const int i : IndexRange(pointcloud.totpoint)) { + float3 &co = *(float3 *)pointcloud.co[i]; + co = transform * co; } } -static void transform_instances(InstancesComponent &instances, - const float3 translation, - const float3 rotation, - const float3 scale) +static void translate_instances(InstancesComponent &instances, const float3 translation) { MutableSpan<float4x4> transforms = instances.instance_transforms(); - - /* Use only translation if rotation and scale don't apply. */ - if (use_translate(rotation, scale)) { - for (float4x4 &transform : transforms) { - add_v3_v3(transform.ptr()[3], translation); - } + for (float4x4 &transform : transforms) { + add_v3_v3(transform.ptr()[3], translation); } - else { - const float4x4 matrix = float4x4::from_loc_eul_scale(translation, rotation, scale); - for (float4x4 &transform : transforms) { - transform = matrix * transform; - } +} + +static void transform_instances(InstancesComponent &instances, const float4x4 &transform) +{ + MutableSpan<float4x4> instance_transforms = instances.instance_transforms(); + for (float4x4 &instance_transform : instance_transforms) { + instance_transform = transform * instance_transform; } } -static void transform_volume(Volume *volume, - const float3 translation, - const float3 rotation, - const float3 scale, - GeoNodeExecParams ¶ms) +static void transform_volume(Volume &volume, const float4x4 &transform, const Depsgraph &depsgraph) { #ifdef WITH_OPENVDB /* Scaling an axis to zero is not supported for volumes. */ + const float3 translation = transform.translation(); + const float3 rotation = transform.to_euler(); + const float3 scale = transform.scale(); const float3 limited_scale = { (scale.x == 0.0f) ? FLT_EPSILON : scale.x, (scale.y == 0.0f) ? FLT_EPSILON : scale.y, (scale.z == 0.0f) ? FLT_EPSILON : scale.z, }; + const float4x4 scale_limited_transform = float4x4::from_loc_eul_scale( + translation, rotation, limited_scale); - const Main *bmain = DEG_get_bmain(params.depsgraph()); - BKE_volume_load(volume, bmain); - - const float4x4 matrix = float4x4::from_loc_eul_scale(translation, rotation, limited_scale); + const Main *bmain = DEG_get_bmain(&depsgraph); + BKE_volume_load(&volume, bmain); openvdb::Mat4s vdb_matrix; - memcpy(vdb_matrix.asPointer(), matrix, sizeof(float[4][4])); + memcpy(vdb_matrix.asPointer(), &scale_limited_transform, sizeof(float[4][4])); openvdb::Mat4d vdb_matrix_d{vdb_matrix}; - const int num_grids = BKE_volume_num_grids(volume); + const int num_grids = BKE_volume_num_grids(&volume); for (const int i : IndexRange(num_grids)) { - VolumeGrid *volume_grid = BKE_volume_grid_get_for_write(volume, i); + VolumeGrid *volume_grid = BKE_volume_grid_get_for_write(&volume, i); - openvdb::GridBase::Ptr grid = BKE_volume_grid_openvdb_for_write(volume, volume_grid, false); + openvdb::GridBase::Ptr grid = BKE_volume_grid_openvdb_for_write(&volume, volume_grid, false); openvdb::math::Transform &grid_transform = grid->transform(); grid_transform.postMult(vdb_matrix_d); } #else - UNUSED_VARS(volume, translation, rotation, scale, params); + UNUSED_VARS(volume, transform, depsgraph); #endif } -static void transform_curve(CurveEval &curve, - const float3 translation, - const float3 rotation, - const float3 scale) +static void translate_volume(Volume &volume, const float3 translation, const Depsgraph &depsgraph) { - if (use_translate(rotation, scale)) { - curve.translate(translation); + transform_volume(volume, float4x4::from_location(translation), depsgraph); +} + +static void translate_geometry_set(GeometrySet &geometry, + const float3 translation, + const Depsgraph &depsgraph) +{ + if (CurveEval *curve = geometry.get_curve_for_write()) { + curve->translate(translation); } - else { - const float4x4 matrix = float4x4::from_loc_eul_scale(translation, rotation, scale); - curve.transform(matrix); + if (Mesh *mesh = geometry.get_mesh_for_write()) { + translate_mesh(*mesh, translation); + } + if (PointCloud *pointcloud = geometry.get_pointcloud_for_write()) { + translate_pointcloud(*pointcloud, translation); + } + if (Volume *volume = geometry.get_volume_for_write()) { + translate_volume(*volume, translation, depsgraph); + } + if (geometry.has_instances()) { + translate_instances(geometry.get_component_for_write<InstancesComponent>(), translation); + } +} + +void transform_geometry_set(GeometrySet &geometry, + const float4x4 &transform, + const Depsgraph &depsgraph) +{ + if (CurveEval *curve = geometry.get_curve_for_write()) { + curve->transform(transform); + } + if (Mesh *mesh = geometry.get_mesh_for_write()) { + transform_mesh(*mesh, transform); + } + if (PointCloud *pointcloud = geometry.get_pointcloud_for_write()) { + transform_pointcloud(*pointcloud, transform); + } + if (Volume *volume = geometry.get_volume_for_write()) { + transform_volume(*volume, transform, depsgraph); + } + if (geometry.has_instances()) { + transform_instances(geometry.get_component_for_write<InstancesComponent>(), transform); } } -static void geo_node_transform_exec(GeoNodeExecParams params) +void transform_mesh(Mesh &mesh, + const float3 translation, + const float3 rotation, + const float3 scale) +{ + const float4x4 matrix = float4x4::from_loc_eul_scale(translation, rotation, scale); + transform_mesh(mesh, matrix); +} + +} // namespace blender::nodes + +namespace blender::nodes::node_geo_transform_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::Vector>(N_("Translation")).subtype(PROP_TRANSLATION); + b.add_input<decl::Vector>(N_("Rotation")).subtype(PROP_EULER); + b.add_input<decl::Vector>(N_("Scale")).default_value({1, 1, 1}).subtype(PROP_XYZ); + b.add_output<decl::Geometry>(N_("Geometry")); +} + +static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); const float3 translation = params.extract_input<float3>("Translation"); const float3 rotation = params.extract_input<float3>("Rotation"); const float3 scale = params.extract_input<float3>("Scale"); - if (geometry_set.has_mesh()) { - Mesh *mesh = geometry_set.get_mesh_for_write(); - transform_mesh(mesh, translation, rotation, scale); - } - if (geometry_set.has_pointcloud()) { - PointCloud *pointcloud = geometry_set.get_pointcloud_for_write(); - transform_pointcloud(pointcloud, translation, rotation, scale); - } - if (geometry_set.has_instances()) { - InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>(); - transform_instances(instances, translation, rotation, scale); - } - if (geometry_set.has_volume()) { - Volume *volume = geometry_set.get_volume_for_write(); - transform_volume(volume, translation, rotation, scale, params); + /* Use only translation if rotation and scale don't apply. */ + if (use_translate(rotation, scale)) { + translate_geometry_set(geometry_set, translation, *params.depsgraph()); } - if (geometry_set.has_curve()) { - CurveEval *curve = geometry_set.get_curve_for_write(); - transform_curve(*curve, translation, rotation, scale); + else { + transform_geometry_set(geometry_set, + float4x4::from_loc_eul_scale(translation, rotation, scale), + *params.depsgraph()); } params.set_output("Geometry", std::move(geometry_set)); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_transform_cc void register_node_type_geo_transform() { + namespace file_ns = blender::nodes::node_geo_transform_cc; + static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_TRANSFORM, "Transform", NODE_CLASS_GEOMETRY, 0); - ntype.declare = blender::nodes::geo_node_transform_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_transform_exec; + geo_node_type_base(&ntype, GEO_NODE_TRANSFORM, "Transform", NODE_CLASS_GEOMETRY); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_geo_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc new file mode 100644 index 00000000000..91c503ff047 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc @@ -0,0 +1,84 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_task.hh" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_translate_instances_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>(N_("Instances")).only_instances(); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input<decl::Vector>(N_("Translation")).subtype(PROP_TRANSLATION).supports_field(); + b.add_input<decl::Bool>(N_("Local Space")).default_value(true).supports_field(); + b.add_output<decl::Geometry>(N_("Instances")); +} + +static void translate_instances(GeoNodeExecParams ¶ms, InstancesComponent &instances_component) +{ + GeometryComponentFieldContext field_context{instances_component, ATTR_DOMAIN_INSTANCE}; + + fn::FieldEvaluator evaluator{field_context, instances_component.instances_amount()}; + evaluator.set_selection(params.extract_input<Field<bool>>("Selection")); + evaluator.add(params.extract_input<Field<float3>>("Translation")); + evaluator.add(params.extract_input<Field<bool>>("Local Space")); + evaluator.evaluate(); + + const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); + const VArray<float3> &translations = evaluator.get_evaluated<float3>(0); + const VArray<bool> &local_spaces = evaluator.get_evaluated<bool>(1); + + MutableSpan<float4x4> instance_transforms = instances_component.instance_transforms(); + + threading::parallel_for(selection.index_range(), 1024, [&](IndexRange range) { + for (const int i_selection : range) { + const int i = selection[i_selection]; + if (local_spaces[i]) { + instance_transforms[i] *= float4x4::from_location(translations[i]); + } + else { + add_v3_v3(instance_transforms[i].values[3], translations[i]); + } + } + }); +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Instances"); + if (geometry_set.has_instances()) { + InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>(); + translate_instances(params, instances); + } + params.set_output("Instances", std::move(geometry_set)); +} + +} // namespace blender::nodes::node_geo_translate_instances_cc + +void register_node_type_geo_translate_instances() +{ + namespace file_ns = blender::nodes::node_geo_translate_instances_cc; + + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_TRANSLATE_INSTANCES, "Translate Instances", NODE_CLASS_GEOMETRY); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.declare = file_ns::node_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc index 7ef0913622c..e78c4d7bc35 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc @@ -14,29 +14,30 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include "BKE_customdata.h" +#include "BKE_mesh.h" + +#include "bmesh.h" +#include "bmesh_tools.h" + +#include "DNA_mesh_types.h" + #include "UI_interface.h" #include "UI_resources.h" #include "node_geometry_util.hh" -extern "C" { -Mesh *triangulate_mesh(Mesh *mesh, - const int quad_method, - const int ngon_method, - const int min_vertices, - const int flag); -} +namespace blender::nodes::node_geo_triangulate_cc { -namespace blender::nodes { - -static void geo_node_triangulate_declare(NodeDeclarationBuilder &b) +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::Int>("Minimum Vertices").default_value(4).min(4).max(10000); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input<decl::Bool>(N_("Selection")).default_value(true).supports_field().hide_value(); + b.add_input<decl::Int>(N_("Minimum Vertices")).default_value(4).min(4).max(10000); + b.add_output<decl::Geometry>(N_("Mesh")); } -static void geo_node_triangulate_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "quad_method", 0, "", ICON_NONE); uiItemR(layout, ptr, "ngon_method", 0, "", ICON_NONE); @@ -48,9 +49,35 @@ static void geo_triangulate_init(bNodeTree *UNUSED(ntree), bNode *node) node->custom2 = GEO_NODE_TRIANGULATE_NGON_BEAUTY; } -static void geo_node_triangulate_exec(GeoNodeExecParams params) +static Mesh *triangulate_mesh_selection(const Mesh &mesh, + const int quad_method, + const int ngon_method, + const IndexMask selection, + const int min_vertices) { - GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + CustomData_MeshMasks cd_mask_extra = { + CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX, 0, CD_MASK_ORIGINDEX}; + BMeshCreateParams create_params{0}; + BMeshFromMeshParams from_mesh_params{true, 1, 1, 1, cd_mask_extra}; + BMesh *bm = BKE_mesh_to_bmesh_ex(&mesh, &create_params, &from_mesh_params); + + /* Tag faces to be triangulated from the selection mask. */ + BM_mesh_elem_table_ensure(bm, BM_FACE); + for (int i_face : selection) { + BM_elem_flag_set(BM_face_at_index(bm, i_face), BM_ELEM_TAG, true); + } + + BM_mesh_triangulate(bm, quad_method, ngon_method, min_vertices, true, NULL, NULL, NULL); + Mesh *result = BKE_mesh_from_bmesh_for_eval_nomain(bm, &cd_mask_extra, &mesh); + BM_mesh_free(bm); + BKE_mesh_normals_tag_dirty(result); + return result; +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh"); + Field<bool> selection_field = params.extract_input<Field<bool>>("Selection"); const int min_vertices = std::max(params.extract_input<int>("Minimum Vertices"), 4); GeometryNodeTriangulateQuads quad_method = static_cast<GeometryNodeTriangulateQuads>( @@ -59,26 +86,38 @@ static void geo_node_triangulate_exec(GeoNodeExecParams params) params.node().custom2); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - /* #triangulate_mesh might modify the input mesh currently. */ - Mesh *mesh_in = geometry_set.get_mesh_for_write(); - if (mesh_in != nullptr) { - Mesh *mesh_out = triangulate_mesh(mesh_in, quad_method, ngon_method, min_vertices, 0); - geometry_set.replace_mesh(mesh_out); + if (!geometry_set.has_mesh()) { + return; } + GeometryComponent &component = geometry_set.get_component_for_write<MeshComponent>(); + const Mesh &mesh_in = *geometry_set.get_mesh_for_read(); + + const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE); + GeometryComponentFieldContext context{component, ATTR_DOMAIN_FACE}; + FieldEvaluator evaluator{context, domain_size}; + evaluator.add(selection_field); + evaluator.evaluate(); + const IndexMask selection = evaluator.get_evaluated_as_mask(0); + + Mesh *mesh_out = triangulate_mesh_selection( + mesh_in, quad_method, ngon_method, selection, min_vertices); + geometry_set.replace_mesh(mesh_out); }); - params.set_output("Geometry", std::move(geometry_set)); + params.set_output("Mesh", std::move(geometry_set)); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_triangulate_cc void register_node_type_geo_triangulate() { + namespace file_ns = blender::nodes::node_geo_triangulate_cc; + static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_TRIANGULATE, "Triangulate", NODE_CLASS_GEOMETRY, 0); - ntype.declare = blender::nodes::geo_node_triangulate_declare; - node_type_init(&ntype, blender::nodes::geo_triangulate_init); - ntype.geometry_node_execute = blender::nodes::geo_node_triangulate_exec; - ntype.draw_buttons = blender::nodes::geo_node_triangulate_layout; + geo_node_type_base(&ntype, GEO_NODE_TRIANGULATE, "Triangulate", NODE_CLASS_GEOMETRY); + ntype.declare = file_ns::node_declare; + node_type_init(&ntype, file_ns::geo_triangulate_init); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.draw_buttons = file_ns::node_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_viewer.cc b/source/blender/nodes/geometry/nodes/node_geo_viewer.cc index 3331962341f..c717d90f7cc 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_viewer.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_viewer.cc @@ -14,20 +14,139 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include "BKE_context.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "ED_node.h" +#include "ED_spreadsheet.h" + +#include "NOD_socket_search_link.hh" + #include "node_geometry_util.hh" -namespace blender::nodes { -static void geo_node_viewer_declare(NodeDeclarationBuilder &b) +namespace blender::nodes::node_geo_viewer_cc { + +NODE_STORAGE_FUNCS(NodeGeometryViewer) + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::Float>(N_("Value")).supports_field().hide_value(); + b.add_input<decl::Vector>(N_("Value"), "Value_001").supports_field().hide_value(); + b.add_input<decl::Color>(N_("Value"), "Value_002").supports_field().hide_value(); + b.add_input<decl::Int>(N_("Value"), "Value_003").supports_field().hide_value(); + b.add_input<decl::Bool>(N_("Value"), "Value_004").supports_field().hide_value(); +} + +static void node_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryViewer *data = MEM_cnew<NodeGeometryViewer>(__func__); + data->data_type = CD_PROP_FLOAT; + + node->storage = data; +} + +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); +} + +static eNodeSocketDatatype custom_data_type_to_socket_type(const CustomDataType type) +{ + switch (type) { + case CD_PROP_FLOAT: + return SOCK_FLOAT; + case CD_PROP_INT32: + return SOCK_INT; + case CD_PROP_FLOAT3: + return SOCK_VECTOR; + case CD_PROP_BOOL: + return SOCK_BOOLEAN; + case CD_PROP_COLOR: + return SOCK_RGBA; + default: + BLI_assert_unreachable(); + return SOCK_FLOAT; + } +} + +static void node_update(bNodeTree *ntree, bNode *node) +{ + const NodeGeometryViewer &storage = node_storage(*node); + const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type); + const eNodeSocketDatatype socket_type = custom_data_type_to_socket_type(data_type); + + LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) { + if (socket->type == SOCK_GEOMETRY) { + continue; + } + nodeSetSocketAvailability(ntree, socket, socket->type == socket_type); + } +} + +static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) { - b.add_input<decl::Geometry>("Geometry"); + auto set_active_fn = [](LinkSearchOpParams ¶ms, bNode &viewer_node) { + /* Set this new viewer node active in spreadsheet editors. */ + SpaceNode *snode = CTX_wm_space_node(¶ms.C); + Main *bmain = CTX_data_main(¶ms.C); + ED_node_set_active(bmain, snode, ¶ms.node_tree, &viewer_node, nullptr); + ED_spreadsheet_context_paths_set_geometry_node(bmain, snode, &viewer_node); + }; + + const std::optional<CustomDataType> type = node_socket_to_custom_data_type( + params.other_socket()); + if (params.in_out() == SOCK_OUT) { + /* The viewer node only has inputs. */ + return; + } + if (params.other_socket().type == SOCK_GEOMETRY) { + params.add_item(IFACE_("Geometry"), [set_active_fn](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node("GeometryNodeViewer"); + params.connect_available_socket(node, "Geometry"); + set_active_fn(params, node); + }); + } + if (type && + ELEM(type, CD_PROP_FLOAT, CD_PROP_BOOL, CD_PROP_INT32, CD_PROP_FLOAT3, CD_PROP_COLOR)) { + params.add_item(IFACE_("Value"), [type, set_active_fn](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node("GeometryNodeViewer"); + node_storage(node).data_type = *type; + params.update_and_connect_available_socket(node, "Value"); + + /* If the source node has a geometry socket, connect it to the new viewer node as well. */ + LISTBASE_FOREACH (bNodeSocket *, socket, ¶ms.node.outputs) { + if (socket->type == SOCK_GEOMETRY && !(socket->flag & (SOCK_UNAVAIL | SOCK_HIDDEN))) { + nodeAddLink(¶ms.node_tree, + ¶ms.node, + socket, + &node, + static_cast<bNodeSocket *>(node.inputs.first)); + } + } + + set_active_fn(params, node); + }); + } } -} // namespace blender::nodes + +} // namespace blender::nodes::node_geo_viewer_cc void register_node_type_geo_viewer() { + namespace file_ns = blender::nodes::node_geo_viewer_cc; + static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_VIEWER, "Viewer", NODE_CLASS_OUTPUT, 0); - ntype.declare = blender::nodes::geo_node_viewer_declare; + geo_node_type_base(&ntype, GEO_NODE_VIEWER, "Viewer", NODE_CLASS_OUTPUT); + node_type_storage( + &ntype, "NodeGeometryViewer", node_free_standard_storage, node_copy_standard_storage); + node_type_update(&ntype, file_ns::node_update); + node_type_init(&ntype, file_ns::node_init); + ntype.declare = file_ns::node_declare; + ntype.draw_buttons_ex = file_ns::node_layout; + ntype.gather_link_search_ops = file_ns::node_gather_link_searches; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc index 229a35e0007..c7dc73f8a91 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc @@ -35,138 +35,193 @@ #include "UI_interface.h" #include "UI_resources.h" -namespace blender::nodes { +namespace blender::nodes::node_geo_volume_to_mesh_cc { -static void geo_node_volume_to_mesh_declare(NodeDeclarationBuilder &b) +NODE_STORAGE_FUNCS(NodeGeometryVolumeToMesh) + +static void node_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Geometry>("Geometry"); - b.add_input<decl::String>("Density"); - b.add_input<decl::Float>("Voxel Size").default_value(0.3f).min(0.01f).subtype(PROP_DISTANCE); - b.add_input<decl::Float>("Voxel Amount").default_value(64.0f).min(0.0f); - b.add_input<decl::Float>("Threshold").default_value(0.1f).min(0.0f); - b.add_input<decl::Float>("Adaptivity").min(0.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_output<decl::Geometry>("Geometry"); + b.add_input<decl::Geometry>(N_("Volume")).supported_type(GEO_COMPONENT_TYPE_VOLUME); + b.add_input<decl::Float>(N_("Voxel Size")) + .default_value(0.3f) + .min(0.01f) + .subtype(PROP_DISTANCE) + .make_available([](bNode &node) { + node_storage(node).resolution_mode = VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_SIZE; + }); + b.add_input<decl::Float>(N_("Voxel Amount")) + .default_value(64.0f) + .min(0.0f) + .make_available([](bNode &node) { + node_storage(node).resolution_mode = VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_AMOUNT; + }); + b.add_input<decl::Float>(N_("Threshold")).default_value(0.1f).min(0.0f); + b.add_input<decl::Float>(N_("Adaptivity")).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_output<decl::Geometry>(N_("Mesh")); } -static void geo_node_volume_to_mesh_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); uiItemR(layout, ptr, "resolution_mode", 0, IFACE_("Resolution"), ICON_NONE); } -static void geo_node_volume_to_mesh_init(bNodeTree *UNUSED(ntree), bNode *node) +static void node_init(bNodeTree *UNUSED(ntree), bNode *node) { - NodeGeometryVolumeToMesh *data = (NodeGeometryVolumeToMesh *)MEM_callocN( - sizeof(NodeGeometryVolumeToMesh), __func__); + NodeGeometryVolumeToMesh *data = MEM_cnew<NodeGeometryVolumeToMesh>(__func__); data->resolution_mode = VOLUME_TO_MESH_RESOLUTION_MODE_GRID; - - bNodeSocket *grid_socket = nodeFindSocket(node, SOCK_IN, "Density"); - bNodeSocketValueString *grid_socket_value = (bNodeSocketValueString *)grid_socket->default_value; - STRNCPY(grid_socket_value->value, "density"); - node->storage = data; } -static void geo_node_volume_to_mesh_update(bNodeTree *UNUSED(ntree), bNode *node) +static void node_update(bNodeTree *ntree, bNode *node) { - NodeGeometryVolumeToMesh *data = (NodeGeometryVolumeToMesh *)node->storage; + const NodeGeometryVolumeToMesh &storage = node_storage(*node); bNodeSocket *voxel_size_socket = nodeFindSocket(node, SOCK_IN, "Voxel Size"); bNodeSocket *voxel_amount_socket = nodeFindSocket(node, SOCK_IN, "Voxel Amount"); - nodeSetSocketAvailability(voxel_amount_socket, - data->resolution_mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_AMOUNT); - nodeSetSocketAvailability(voxel_size_socket, - data->resolution_mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_SIZE); + nodeSetSocketAvailability(ntree, + voxel_amount_socket, + storage.resolution_mode == + VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_AMOUNT); + nodeSetSocketAvailability(ntree, + voxel_size_socket, + storage.resolution_mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_SIZE); } #ifdef WITH_OPENVDB -static void create_mesh_from_volume(GeometrySet &geometry_set_in, - GeometrySet &geometry_set_out, - GeoNodeExecParams ¶ms) +static bke::VolumeToMeshResolution get_resolution_param(const GeoNodeExecParams ¶ms) { - if (!geometry_set_in.has<VolumeComponent>()) { - return; - } - - const NodeGeometryVolumeToMesh &storage = - *(const NodeGeometryVolumeToMesh *)params.node().storage; + const NodeGeometryVolumeToMesh &storage = node_storage(params.node()); bke::VolumeToMeshResolution resolution; resolution.mode = (VolumeToMeshResolutionMode)storage.resolution_mode; if (resolution.mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_AMOUNT) { - resolution.settings.voxel_amount = params.get_input<float>("Voxel Amount"); - if (resolution.settings.voxel_amount <= 0.0f) { - return; - } + resolution.settings.voxel_amount = std::max(params.get_input<float>("Voxel Amount"), 0.0f); } else if (resolution.mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_SIZE) { - resolution.settings.voxel_size = params.get_input<float>("Voxel Size"); - if (resolution.settings.voxel_size <= 0.0f) { - return; - } + resolution.settings.voxel_size = std::max(params.get_input<float>("Voxel Size"), 0.0f); + } + + return resolution; +} + +static Mesh *create_mesh_from_volume_grids(Span<openvdb::GridBase::ConstPtr> grids, + const float threshold, + const float adaptivity, + const bke::VolumeToMeshResolution &resolution) +{ + Array<bke::OpenVDBMeshData> mesh_data(grids.size()); + for (const int i : grids.index_range()) { + mesh_data[i] = bke::volume_to_mesh_data(*grids[i], resolution, threshold, adaptivity); + } + + int vert_offset = 0; + int poly_offset = 0; + int loop_offset = 0; + Array<int> vert_offsets(mesh_data.size()); + Array<int> poly_offsets(mesh_data.size()); + Array<int> loop_offsets(mesh_data.size()); + for (const int i : grids.index_range()) { + const bke::OpenVDBMeshData &data = mesh_data[i]; + vert_offsets[i] = vert_offset; + poly_offsets[i] = poly_offset; + loop_offsets[i] = loop_offset; + vert_offset += data.verts.size(); + poly_offset += (data.tris.size() + data.quads.size()); + loop_offset += (3 * data.tris.size() + 4 * data.quads.size()); } - const VolumeComponent *component = geometry_set_in.get_component_for_read<VolumeComponent>(); - const Volume *volume = component->get_for_read(); + Mesh *mesh = BKE_mesh_new_nomain(vert_offset, 0, 0, loop_offset, poly_offset); + BKE_id_material_eval_ensure_default_slot(&mesh->id); + MutableSpan<MVert> verts{mesh->mvert, mesh->totvert}; + MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop}; + MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly}; + + for (const int i : grids.index_range()) { + const bke::OpenVDBMeshData &data = mesh_data[i]; + bke::fill_mesh_from_openvdb_data(data.verts, + data.tris, + data.quads, + vert_offsets[i], + poly_offsets[i], + loop_offsets[i], + verts, + polys, + loops); + } + + BKE_mesh_calc_edges(mesh, false, false); + BKE_mesh_normals_tag_dirty(mesh); + + return mesh; +} + +static Mesh *create_mesh_from_volume(GeometrySet &geometry_set, GeoNodeExecParams ¶ms) +{ + const Volume *volume = geometry_set.get_volume_for_read(); if (volume == nullptr) { - return; + return nullptr; } + const bke::VolumeToMeshResolution resolution = get_resolution_param(params); const Main *bmain = DEG_get_bmain(params.depsgraph()); BKE_volume_load(volume, bmain); - const std::string grid_name = params.get_input<std::string>("Density"); - const VolumeGrid *volume_grid = BKE_volume_grid_find_for_read(volume, grid_name.c_str()); - if (volume_grid == nullptr) { - return; + Vector<openvdb::GridBase::ConstPtr> grids; + for (const int i : IndexRange(BKE_volume_num_grids(volume))) { + const VolumeGrid *volume_grid = BKE_volume_grid_get_for_read(volume, i); + openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid); + grids.append(std::move(grid)); } - float threshold = params.get_input<float>("Threshold"); - float adaptivity = params.get_input<float>("Adaptivity"); - - const openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid); - Mesh *mesh = bke::volume_to_mesh(*grid, resolution, threshold, adaptivity); - if (mesh == nullptr) { - return; + if (grids.is_empty()) { + return nullptr; } - BKE_id_material_eval_ensure_default_slot(&mesh->id); - MeshComponent &dst_component = geometry_set_out.get_component_for_write<MeshComponent>(); - dst_component.replace(mesh); + + return create_mesh_from_volume_grids(grids, + params.get_input<float>("Threshold"), + params.get_input<float>("Adaptivity"), + resolution); } #endif /* WITH_OPENVDB */ -static void geo_node_volume_to_mesh_exec(GeoNodeExecParams params) +static void node_geo_exec(GeoNodeExecParams params) { - GeometrySet geometry_set_in = params.extract_input<GeometrySet>("Geometry"); - GeometrySet geometry_set_out; + GeometrySet geometry_set = params.extract_input<GeometrySet>("Volume"); #ifdef WITH_OPENVDB - create_mesh_from_volume(geometry_set_in, geometry_set_out, params); + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + Mesh *mesh = create_mesh_from_volume(geometry_set, params); + geometry_set.replace_mesh(mesh); + geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES}); + }); #else params.error_message_add(NodeWarningType::Error, TIP_("Disabled, Blender was compiled without OpenVDB")); #endif - params.set_output("Geometry", geometry_set_out); + params.set_output("Mesh", std::move(geometry_set)); } -} // namespace blender::nodes +} // namespace blender::nodes::node_geo_volume_to_mesh_cc void register_node_type_geo_volume_to_mesh() { + namespace file_ns = blender::nodes::node_geo_volume_to_mesh_cc; + static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_VOLUME_TO_MESH, "Volume to Mesh", NODE_CLASS_GEOMETRY, 0); - ntype.declare = blender::nodes::geo_node_volume_to_mesh_declare; + geo_node_type_base(&ntype, GEO_NODE_VOLUME_TO_MESH, "Volume to Mesh", NODE_CLASS_GEOMETRY); + ntype.declare = file_ns::node_declare; node_type_storage( &ntype, "NodeGeometryVolumeToMesh", node_free_standard_storage, node_copy_standard_storage); - node_type_size(&ntype, 200, 120, 700); - node_type_init(&ntype, blender::nodes::geo_node_volume_to_mesh_init); - node_type_update(&ntype, blender::nodes::geo_node_volume_to_mesh_update); - ntype.geometry_node_execute = blender::nodes::geo_node_volume_to_mesh_exec; - ntype.draw_buttons = blender::nodes::geo_node_volume_to_mesh_layout; + node_type_size(&ntype, 170, 120, 700); + node_type_init(&ntype, file_ns::node_init); + node_type_update(&ntype, file_ns::node_update); + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.draw_buttons = file_ns::node_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/intern/derived_node_tree.cc b/source/blender/nodes/intern/derived_node_tree.cc index f7279cf7524..449c6598307 100644 --- a/source/blender/nodes/intern/derived_node_tree.cc +++ b/source/blender/nodes/intern/derived_node_tree.cc @@ -20,10 +20,6 @@ namespace blender::nodes { -/* Construct a new derived node tree for a given root node tree. The generated derived node tree - * does not own the used node tree refs (so that those can be used by others as well). The caller - * 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::DerivedNodeTree(bNodeTree &btree, NodeTreeRefMap &node_tree_refs) { /* Construct all possible contexts immediately. This is significantly cheaper than inlining all @@ -73,9 +69,6 @@ void DerivedNodeTree::destruct_context_recursively(DTreeContext *context) context->~DTreeContext(); } -/** - * \return True when there is a link cycle. Unavailable sockets are ignored. - */ bool DerivedNodeTree::has_link_cycles() const { for (const NodeTreeRef *tree_ref : used_node_tree_refs_) { @@ -96,7 +89,6 @@ bool DerivedNodeTree::has_undefined_nodes_or_sockets() const return false; } -/* Calls the given callback on all nodes in the (possibly nested) derived node tree. */ void DerivedNodeTree::foreach_node(FunctionRef<void(DNode)> callback) const { this->foreach_node_in_context_recursive(*root_context_, callback); @@ -167,7 +159,11 @@ DInputSocket DOutputSocket::get_active_corresponding_group_output_socket() const BLI_assert(socket_ref_->node().is_group_node()); const DTreeContext *child_context = context_->child_context(socket_ref_->node()); - BLI_assert(child_context != nullptr); + 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"); @@ -180,9 +176,6 @@ DInputSocket DOutputSocket::get_active_corresponding_group_output_socket() const return {}; } -/* Call `origin_fn` for every "real" origin socket. "Real" means that reroutes, muted nodes - * and node groups are handled by this function. Origin sockets are ones where a node gets its - * inputs from. */ void DInputSocket::foreach_origin_socket(FunctionRef<void(DSocket)> origin_fn) const { BLI_assert(*this); @@ -229,47 +222,91 @@ void DInputSocket::foreach_origin_socket(FunctionRef<void(DSocket)> origin_fn) c } } -/* Calls `target_fn` for every "real" target socket. "Real" means that reroutes, muted nodes - * and node groups are handled by this function. Target sockets are on the nodes that use the value - * from this socket. The `skipped_fn` function is called for sockets that have been skipped during - * the search for target sockets (e.g. reroutes). */ -void DOutputSocket::foreach_target_socket(FunctionRef<void(DInputSocket)> target_fn, - FunctionRef<void(DSocket)> skipped_fn) const +void DOutputSocket::foreach_target_socket(ForeachTargetSocketFn target_fn) const { - for (const SocketRef *skipped_socket : socket_ref_->logically_linked_skipped_sockets()) { - skipped_fn.call_safe({context_, skipped_socket}); - } - for (const InputSocketRef *linked_socket : socket_ref_->as_output().logically_linked_sockets()) { - const NodeRef &linked_node = linked_socket->node(); - DInputSocket linked_dsocket{context_, linked_socket}; + TargetSocketPathInfo path_info; + this->foreach_target_socket(target_fn, path_info); +} - if (linked_node.is_group_output_node()) { +void DOutputSocket::foreach_target_socket(ForeachTargetSocketFn target_fn, + TargetSocketPathInfo &path_info) const +{ + for (const LinkRef *link : socket_ref_->as_output().directly_linked_links()) { + if (link->is_muted()) { + continue; + } + const DInputSocket &linked_socket{context_, &link->to()}; + if (!linked_socket->is_available()) { + continue; + } + const DNode linked_node = linked_socket.node(); + if (linked_node->is_reroute_node()) { + const DInputSocket reroute_input = linked_socket; + const DOutputSocket reroute_output = linked_node.output(0); + path_info.sockets.append(reroute_input); + path_info.sockets.append(reroute_output); + reroute_output.foreach_target_socket(target_fn, path_info); + path_info.sockets.pop_last(); + 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()) { + continue; + } + /* The internal link only forwards the first incoming link. */ + if (linked_socket->is_multi_input_socket()) { + if (linked_socket->directly_linked_links()[0] != link) { + continue; + } + } + const DInputSocket mute_input = linked_socket; + const DOutputSocket mute_output{context_, &internal_link->to()}; + path_info.sockets.append(mute_input); + path_info.sockets.append(mute_output); + mute_output.foreach_target_socket(target_fn, path_info); + path_info.sockets.pop_last(); + path_info.sockets.pop_last(); + } + } + else if (linked_node->is_group_output_node()) { + if (linked_node.node_ref() != context_->tree().group_output_node()) { + continue; + } if (context_->is_root()) { /* This is a group output in the root node group. */ - target_fn(linked_dsocket); + path_info.sockets.append(linked_socket); + target_fn(linked_socket, path_info); + path_info.sockets.pop_last(); } else { /* Follow the links going out of the group node in the parent node group. */ - DOutputSocket socket_in_parent_group = - linked_dsocket.get_corresponding_group_node_output(); - skipped_fn.call_safe(linked_dsocket); - skipped_fn.call_safe(socket_in_parent_group); - socket_in_parent_group.foreach_target_socket(target_fn, skipped_fn); + const DOutputSocket socket_in_parent_group = + linked_socket.get_corresponding_group_node_output(); + path_info.sockets.append(linked_socket); + path_info.sockets.append(socket_in_parent_group); + socket_in_parent_group.foreach_target_socket(target_fn, path_info); + path_info.sockets.pop_last(); + path_info.sockets.pop_last(); } } - else if (linked_node.is_group_node()) { + else if (linked_node->is_group_node()) { /* Follow the links within the nested node group. */ - Vector<DOutputSocket> sockets_in_group = - linked_dsocket.get_corresponding_group_input_sockets(); - skipped_fn.call_safe(linked_dsocket); - for (DOutputSocket socket_in_group : sockets_in_group) { - skipped_fn.call_safe(socket_in_group); - socket_in_group.foreach_target_socket(target_fn, skipped_fn); + path_info.sockets.append(linked_socket); + const Vector<DOutputSocket> sockets_in_group = + linked_socket.get_corresponding_group_input_sockets(); + for (const DOutputSocket &socket_in_group : sockets_in_group) { + path_info.sockets.append(socket_in_group); + socket_in_group.foreach_target_socket(target_fn, path_info); + path_info.sockets.pop_last(); } + path_info.sockets.pop_last(); } else { /* The normal case: just use the linked input socket as target. */ - target_fn(linked_dsocket); + path_info.sockets.append(linked_socket); + target_fn(linked_socket, path_info); + path_info.sockets.pop_last(); } } } @@ -294,7 +331,6 @@ static dot::Cluster *get_dot_cluster_for_context( }); } -/* Generates a graph in dot format. The generated graph has all node groups inlined. */ std::string DerivedNodeTree::to_dot() const { dot::DirectedGraph digraph; diff --git a/source/blender/nodes/intern/geometry_nodes_eval_log.cc b/source/blender/nodes/intern/geometry_nodes_eval_log.cc index fa9bf09d8d9..e33f6cf345d 100644 --- a/source/blender/nodes/intern/geometry_nodes_eval_log.cc +++ b/source/blender/nodes/intern/geometry_nodes_eval_log.cc @@ -21,11 +21,23 @@ #include "DNA_modifier_types.h" #include "DNA_space_types.h" +#include "FN_field_cpp_type.hh" + +#include "BLT_translation.h" + +#include <chrono> + namespace blender::nodes::geometry_nodes_eval_log { using fn::CPPType; +using fn::FieldCPPType; +using fn::FieldInput; +using fn::GField; +using fn::ValueOrFieldCPPType; ModifierLog::ModifierLog(GeoLogger &logger) + : input_geometry_log_(std::move(logger.input_geometry_log_)), + output_geometry_log_(std::move(logger.output_geometry_log_)) { root_tree_logs_ = allocator_.construct<TreeLog>(); @@ -54,6 +66,17 @@ ModifierLog::ModifierLog(GeoLogger &logger) node_with_warning.node); node_log.warnings_.append(node_with_warning.warning); } + + for (NodeWithExecutionTime &node_with_exec_time : local_logger.node_exec_times_) { + NodeLog &node_log = this->lookup_or_add_node_log(log_by_tree_context, + node_with_exec_time.node); + node_log.exec_time_ = node_with_exec_time.exec_time; + } + + for (NodeWithDebugMessage &debug_message : local_logger.node_debug_messages_) { + NodeLog &node_log = this->lookup_or_add_node_log(log_by_tree_context, debug_message.node); + node_log.debug_messages_.append(debug_message.message); + } } } @@ -106,6 +129,15 @@ void ModifierLog::foreach_node_log(FunctionRef<void(const NodeLog &)> fn) const } } +const GeometryValueLog *ModifierLog::input_geometry_log() const +{ + return input_geometry_log_.get(); +} +const GeometryValueLog *ModifierLog::output_geometry_log() const +{ + return output_geometry_log_.get(); +} + const NodeLog *TreeLog::lookup_node_log(StringRef node_name) const { const destruct_ptr<NodeLog> *node_log = node_logs_.lookup_ptr_as(node_name); @@ -157,6 +189,36 @@ const SocketLog *NodeLog::lookup_socket_log(const bNode &node, const bNodeSocket return this->lookup_socket_log((eNodeSocketInOut)socket.in_out, index); } +GFieldValueLog::GFieldValueLog(fn::GField field, bool log_full_field) : type_(field.cpp_type()) +{ + const std::shared_ptr<const fn::FieldInputs> &field_input_nodes = field.node().field_inputs(); + + /* Put the deduplicated field inputs into a vector so that they can be sorted below. */ + Vector<std::reference_wrapper<const FieldInput>> field_inputs; + if (field_input_nodes) { + field_inputs.extend(field_input_nodes->deduplicated_nodes.begin(), + field_input_nodes->deduplicated_nodes.end()); + } + + std::sort( + field_inputs.begin(), field_inputs.end(), [](const FieldInput &a, const FieldInput &b) { + const int index_a = (int)a.category(); + const int index_b = (int)b.category(); + if (index_a == index_b) { + return a.socket_inspection_name().size() < b.socket_inspection_name().size(); + } + return index_a < index_b; + }); + + for (const FieldInput &field_input : field_inputs) { + input_tooltips_.append(field_input.socket_inspection_name()); + } + + if (log_full_field) { + field_ = std::move(field); + } +} + GeometryValueLog::GeometryValueLog(const GeometrySet &geometry_set, bool log_full_geometry) { static std::array all_component_types = {GEO_COMPONENT_TYPE_CURVE, @@ -164,13 +226,19 @@ GeometryValueLog::GeometryValueLog(const GeometrySet &geometry_set, bool log_ful GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_VOLUME}; + + /* Keep track handled attribute names to make sure that we do not return the same name twice. + * Currently #GeometrySet::attribute_foreach does not do that. Note that this will merge + * attributes with the same name but different domains or data types on separate components. */ + Set<StringRef> names; + geometry_set.attribute_foreach( all_component_types, true, [&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data, const GeometryComponent &UNUSED(component)) { - if (attribute_id.is_named()) { + if (attribute_id.is_named() && names.add(attribute_id.name())) { this->attributes_.append({attribute_id.name(), meta_data.domain, meta_data.data_type}); } }); @@ -354,7 +422,7 @@ void LocalGeoLogger::log_value_for_sockets(Span<DSocket> sockets, GPointer value if (type.is<GeometrySet>()) { bool log_full_geometry = false; for (const DSocket &socket : sockets) { - if (main_logger_->log_full_geometry_sockets_.contains(socket)) { + if (main_logger_->log_full_sockets_.contains(socket)) { log_full_geometry = true; break; } @@ -365,6 +433,39 @@ void LocalGeoLogger::log_value_for_sockets(Span<DSocket> sockets, GPointer value geometry_set, log_full_geometry); values_.append({copied_sockets, std::move(value_log)}); } + else if (const ValueOrFieldCPPType *value_or_field_type = + dynamic_cast<const ValueOrFieldCPPType *>(&type)) { + const void *value_or_field = value.get(); + if (value_or_field_type->is_field(value_or_field)) { + GField field = *value_or_field_type->get_field_ptr(value_or_field); + bool log_full_field = false; + if (!field.node().depends_on_input()) { + /* Always log constant fields so that their value can be shown in socket inspection. + * In the future we can also evaluate the field here and only store the value. */ + log_full_field = true; + } + if (!log_full_field) { + for (const DSocket &socket : sockets) { + if (main_logger_->log_full_sockets_.contains(socket)) { + log_full_field = true; + break; + } + } + } + destruct_ptr<GFieldValueLog> value_log = allocator_->construct<GFieldValueLog>( + std::move(field), log_full_field); + values_.append({copied_sockets, std::move(value_log)}); + } + else { + const CPPType &base_type = value_or_field_type->base_type(); + const void *value = value_or_field_type->get_value_ptr(value_or_field); + void *buffer = allocator_->allocate(base_type.size(), base_type.alignment()); + base_type.copy_construct(value, buffer); + destruct_ptr<GenericValueLog> value_log = allocator_->construct<GenericValueLog>( + GMutablePointer{base_type, buffer}); + values_.append({copied_sockets, std::move(value_log)}); + } + } else { void *buffer = allocator_->allocate(type.size(), type.alignment()); type.copy_construct(value.get(), buffer); @@ -385,4 +486,14 @@ void LocalGeoLogger::log_node_warning(DNode node, NodeWarningType type, std::str node_warnings_.append({node, {type, std::move(message)}}); } +void LocalGeoLogger::log_execution_time(DNode node, std::chrono::microseconds exec_time) +{ + node_exec_times_.append({node, exec_time}); +} + +void LocalGeoLogger::log_debug_message(DNode node, std::string message) +{ + node_debug_messages_.append({node, std::move(message)}); +} + } // namespace blender::nodes::geometry_nodes_eval_log diff --git a/source/blender/nodes/intern/math_functions.cc b/source/blender/nodes/intern/math_functions.cc index aa23777b664..00f4f2a3405 100644 --- a/source/blender/nodes/intern/math_functions.cc +++ b/source/blender/nodes/intern/math_functions.cc @@ -127,17 +127,17 @@ const FloatMathOperationInfo *get_float_compare_operation_info(const int operati ((void)0) switch (operation) { - case NODE_FLOAT_COMPARE_LESS_THAN: + case NODE_COMPARE_LESS_THAN: RETURN_OPERATION_INFO("Less Than", "math_less_than"); - case NODE_FLOAT_COMPARE_LESS_EQUAL: + case NODE_COMPARE_LESS_EQUAL: RETURN_OPERATION_INFO("Less Than or Equal", "math_less_equal"); - case NODE_FLOAT_COMPARE_GREATER_THAN: + case NODE_COMPARE_GREATER_THAN: RETURN_OPERATION_INFO("Greater Than", "math_greater_than"); - case NODE_FLOAT_COMPARE_GREATER_EQUAL: + case NODE_COMPARE_GREATER_EQUAL: RETURN_OPERATION_INFO("Greater Than or Equal", "math_greater_equal"); - case NODE_FLOAT_COMPARE_EQUAL: + case NODE_COMPARE_EQUAL: RETURN_OPERATION_INFO("Equal", "math_equal"); - case NODE_FLOAT_COMPARE_NOT_EQUAL: + case NODE_COMPARE_NOT_EQUAL: RETURN_OPERATION_INFO("Not Equal", "math_not_equal"); } diff --git a/source/blender/nodes/intern/node_common.c b/source/blender/nodes/intern/node_common.cc index 7625cb9e3f6..21dec8489b4 100644 --- a/source/blender/nodes/intern/node_common.c +++ b/source/blender/nodes/intern/node_common.cc @@ -21,18 +21,24 @@ * \ingroup nodes */ -#include <stddef.h> -#include <string.h> +#include <cstddef> +#include <cstring> #include "DNA_node_types.h" #include "BLI_listbase.h" +#include "BLI_map.hh" +#include "BLI_multi_value_map.hh" +#include "BLI_set.hh" +#include "BLI_stack.hh" #include "BLI_string.h" +#include "BLI_string_ref.hh" #include "BLI_utildefines.h" #include "BLT_translation.h" #include "BKE_node.h" +#include "BKE_node_tree_update.h" #include "RNA_types.h" @@ -42,39 +48,37 @@ #include "node_common.h" #include "node_util.h" -enum { - REFINE_FORWARD = 1 << 0, - REFINE_BACKWARD = 1 << 1, -}; +using blender::Map; +using blender::MultiValueMap; +using blender::Set; +using blender::Stack; +using blender::StringRef; /* -------------------------------------------------------------------- */ /** \name Node Group * \{ */ -bNodeSocket *node_group_find_input_socket(bNode *groupnode, const char *identifier) +static bNodeSocket *find_matching_socket(ListBase &sockets, StringRef identifier) { - bNodeSocket *sock; - for (sock = groupnode->inputs.first; sock; sock = sock->next) { - if (STREQ(sock->identifier, identifier)) { - return sock; + LISTBASE_FOREACH (bNodeSocket *, socket, &sockets) { + if (socket->identifier == identifier) { + return socket; } } - return NULL; + return nullptr; +} + +bNodeSocket *node_group_find_input_socket(bNode *groupnode, const char *identifier) +{ + return find_matching_socket(groupnode->inputs, identifier); } bNodeSocket *node_group_find_output_socket(bNode *groupnode, const char *identifier) { - bNodeSocket *sock; - for (sock = groupnode->outputs.first; sock; sock = sock->next) { - if (STREQ(sock->identifier, identifier)) { - return sock; - } - } - return NULL; + return find_matching_socket(groupnode->outputs, identifier); } -/* groups display their internal tree name as label */ -void node_group_label(bNodeTree *UNUSED(ntree), bNode *node, char *label, int maxlen) +void node_group_label(const bNodeTree *UNUSED(ntree), const bNode *node, char *label, int maxlen) { BLI_strncpy(label, (node->id) ? node->id->name + 2 : IFACE_("Missing Data-Block"), maxlen); } @@ -95,22 +99,21 @@ bool node_group_poll_instance(bNode *node, bNodeTree *nodetree, const char **dis bool nodeGroupPoll(bNodeTree *nodetree, bNodeTree *grouptree, const char **r_disabled_hint) { - bNode *node; bool valid = true; /* unspecified node group, generally allowed * (if anything, should be avoided on operator level) */ - if (grouptree == NULL) { + if (grouptree == nullptr) { return true; } if (nodetree == grouptree) { - *r_disabled_hint = "Nesting a node group inside of itself is not allowed"; + *r_disabled_hint = TIP_("Nesting a node group inside of itself is not allowed"); return false; } - for (node = grouptree->nodes.first; node; node = node->next) { + LISTBASE_FOREACH (bNode *, node, &grouptree->nodes) { if (node->typeinfo->poll_instance && !node->typeinfo->poll_instance(node, nodetree, r_disabled_hint)) { valid = false; @@ -120,81 +123,86 @@ bool nodeGroupPoll(bNodeTree *nodetree, bNodeTree *grouptree, const char **r_dis return valid; } -/* used for both group nodes and interface nodes */ -static bNodeSocket *group_verify_socket( - bNodeTree *ntree, bNode *gnode, bNodeSocket *iosock, ListBase *verify_lb, int in_out) +static void add_new_socket_from_interface(bNodeTree &node_tree, + bNode &node, + const bNodeSocket &interface_socket, + const eNodeSocketInOut in_out) { - bNodeSocket *sock; + bNodeSocket *socket = nodeAddSocket(&node_tree, + &node, + in_out, + interface_socket.idname, + interface_socket.identifier, + interface_socket.name); - for (sock = verify_lb->first; sock; sock = sock->next) { - if (STREQ(sock->identifier, iosock->identifier)) { - break; - } + if (interface_socket.typeinfo->interface_init_socket) { + interface_socket.typeinfo->interface_init_socket( + &node_tree, &interface_socket, &node, socket, "interface"); } - if (sock) { - strcpy(sock->name, iosock->name); +} - const int mask = SOCK_HIDE_VALUE; - sock->flag = (sock->flag & ~mask) | (iosock->flag & mask); +static void update_socket_to_match_interface(bNodeTree &node_tree, + bNode &node, + bNodeSocket &socket_to_update, + const bNodeSocket &interface_socket) +{ + strcpy(socket_to_update.name, interface_socket.name); - /* Update socket type if necessary */ - if (sock->typeinfo != iosock->typeinfo) { - nodeModifySocketType(ntree, gnode, sock, iosock->idname); - /* Flag the tree to make sure link validity is updated after type changes. */ - ntree->update |= NTREE_UPDATE_LINKS; - } + const int mask = SOCK_HIDE_VALUE; + socket_to_update.flag = (socket_to_update.flag & ~mask) | (interface_socket.flag & mask); - if (iosock->typeinfo->interface_verify_socket) { - iosock->typeinfo->interface_verify_socket(ntree, iosock, gnode, sock, "interface"); - } + /* Update socket type if necessary */ + if (socket_to_update.typeinfo != interface_socket.typeinfo) { + nodeModifySocketType(&node_tree, &node, &socket_to_update, interface_socket.idname); } - else { - sock = nodeAddSocket(ntree, gnode, in_out, iosock->idname, iosock->identifier, iosock->name); - if (iosock->typeinfo->interface_init_socket) { - iosock->typeinfo->interface_init_socket(ntree, iosock, gnode, sock, "interface"); - } + if (interface_socket.typeinfo->interface_verify_socket) { + interface_socket.typeinfo->interface_verify_socket( + &node_tree, &interface_socket, &node, &socket_to_update, "interface"); } - - /* remove from list temporarily, to distinguish from orphaned sockets */ - BLI_remlink(verify_lb, sock); - - return sock; } -/* used for both group nodes and interface nodes */ -static void group_verify_socket_list( - bNodeTree *ntree, bNode *gnode, ListBase *iosock_lb, ListBase *verify_lb, int in_out) +/** + * Used for group nodes and group input/output nodes to update the list of input or output sockets + * on a node to match the provided interface. Assumes that \a verify_lb is the node's matching + * input or output socket list, depending on whether the node is a group input/output or a group + * node. + */ +static void group_verify_socket_list(bNodeTree &node_tree, + bNode &node, + const ListBase &interface_sockets, + ListBase &verify_lb, + const eNodeSocketInOut in_out) { - bNodeSocket *iosock, *sock, *nextsock; - - /* step by step compare */ - - iosock = iosock_lb->first; - for (; iosock; iosock = iosock->next) { - /* abusing new_sock pointer for verification here! only used inside this function */ - iosock->new_sock = group_verify_socket(ntree, gnode, iosock, verify_lb, in_out); - } - /* leftovers are removed */ - for (sock = verify_lb->first; sock; sock = nextsock) { - nextsock = sock->next; - nodeRemoveSocket(ntree, gnode, sock); - } - /* and we put back the verified sockets */ - iosock = iosock_lb->first; - for (; iosock; iosock = iosock->next) { - if (iosock->new_sock) { - BLI_addtail(verify_lb, iosock->new_sock); - iosock->new_sock = NULL; + ListBase old_sockets = verify_lb; + BLI_listbase_clear(&verify_lb); + + LISTBASE_FOREACH (const bNodeSocket *, interface_socket, &interface_sockets) { + bNodeSocket *matching_socket = find_matching_socket(old_sockets, interface_socket->identifier); + if (matching_socket) { + /* If a socket with the same identifier exists in the previous socket list, update it + * with the correct name, type, etc. Then move it from the old list to the new one. */ + update_socket_to_match_interface(node_tree, node, *matching_socket, *interface_socket); + BLI_remlink(&old_sockets, matching_socket); + BLI_addtail(&verify_lb, matching_socket); + } + else { + /* If there was no socket withe the same identifier already, simply create a new socket + * based on the interface socket, which will already add it to the new list. */ + add_new_socket_from_interface(node_tree, node, *interface_socket, in_out); } } + + /* Remove leftover sockets that didn't match the node group's interface. */ + LISTBASE_FOREACH_MUTABLE (bNodeSocket *, unused_socket, &old_sockets) { + nodeRemoveSocket(&node_tree, &node, unused_socket); + } } -/* make sure all group node in ntree, which use ngroup, are sync'd */ void node_group_update(struct bNodeTree *ntree, struct bNode *node) { /* check inputs and outputs, and remove or insert them */ - if (node->id == NULL) { + if (node->id == nullptr) { nodeRemoveAllSockets(ntree, node); } else if ((ID_IS_LINKED(node->id) && (node->id->tag & LIB_TAG_MISSING))) { @@ -203,8 +211,8 @@ void node_group_update(struct bNodeTree *ntree, struct bNode *node) } else { bNodeTree *ngroup = (bNodeTree *)node->id; - group_verify_socket_list(ntree, node, &ngroup->inputs, &node->inputs, SOCK_IN); - group_verify_socket_list(ntree, node, &ngroup->outputs, &node->outputs, SOCK_OUT); + group_verify_socket_list(*ntree, *node, ngroup->inputs, node->inputs, SOCK_IN); + group_verify_socket_list(*ntree, *node, ngroup->outputs, node->outputs, SOCK_OUT); } } @@ -216,7 +224,7 @@ void node_group_update(struct bNodeTree *ntree, struct bNode *node) static void node_frame_init(bNodeTree *UNUSED(ntree), bNode *node) { - NodeFrame *data = (NodeFrame *)MEM_callocN(sizeof(NodeFrame), "frame node storage"); + NodeFrame *data = MEM_cnew<NodeFrame>("frame node storage"); node->storage = data; data->flag |= NODE_FRAME_SHRINK; @@ -224,16 +232,17 @@ static void node_frame_init(bNodeTree *UNUSED(ntree), bNode *node) data->label_size = 20; } -void register_node_type_frame(void) +void register_node_type_frame() { /* frame type is used for all tree types, needs dynamic allocation */ - bNodeType *ntype = MEM_callocN(sizeof(bNodeType), "frame node type"); + bNodeType *ntype = MEM_cnew<bNodeType>("frame node type"); ntype->free_self = (void (*)(bNodeType *))MEM_freeN; - node_type_base(ntype, NODE_FRAME, "Frame", NODE_CLASS_LAYOUT, NODE_BACKGROUND); + node_type_base(ntype, NODE_FRAME, "Frame", NODE_CLASS_LAYOUT); node_type_init(ntype, node_frame_init); node_type_storage(ntype, "NodeFrame", node_free_standard_storage, node_copy_standard_storage); node_type_size(ntype, 150, 100, 0); + ntype->flag |= NODE_BACKGROUND; nodeRegisterType(ntype); } @@ -244,26 +253,6 @@ void register_node_type_frame(void) /** \name Node Re-Route * \{ */ -/* simple, only a single input and output here */ -static void node_reroute_update_internal_links(bNodeTree *ntree, bNode *node) -{ - bNodeLink *link; - - /* Security check! */ - if (!ntree) { - return; - } - - link = MEM_callocN(sizeof(bNodeLink), "internal node link"); - link->fromnode = node; - link->fromsock = node->inputs.first; - link->tonode = node; - link->tosock = node->outputs.first; - /* internal link is always valid */ - link->flag |= NODE_LINK_VALID; - BLI_addtail(&node->internal_links, link); -} - static void node_reroute_init(bNodeTree *ntree, bNode *node) { /* NOTE: Cannot use socket templates for this, since it would reset the socket type @@ -273,106 +262,105 @@ static void node_reroute_init(bNodeTree *ntree, bNode *node) nodeAddStaticSocket(ntree, node, SOCK_OUT, SOCK_RGBA, PROP_NONE, "Output", "Output"); } -void register_node_type_reroute(void) +void register_node_type_reroute() { /* frame type is used for all tree types, needs dynamic allocation */ - bNodeType *ntype = MEM_callocN(sizeof(bNodeType), "frame node type"); + bNodeType *ntype = MEM_cnew<bNodeType>("frame node type"); ntype->free_self = (void (*)(bNodeType *))MEM_freeN; - node_type_base(ntype, NODE_REROUTE, "Reroute", NODE_CLASS_LAYOUT, 0); + node_type_base(ntype, NODE_REROUTE, "Reroute", NODE_CLASS_LAYOUT); node_type_init(ntype, node_reroute_init); - node_type_internal_links(ntype, node_reroute_update_internal_links); nodeRegisterType(ntype); } -static void node_reroute_inherit_type_recursive(bNodeTree *ntree, bNode *node, int flag) +static void propagate_reroute_type_from_start_socket( + bNodeSocket *start_socket, + const MultiValueMap<bNodeSocket *, bNodeLink *> &links_map, + Map<bNode *, const bNodeSocketType *> &r_reroute_types) { - bNodeSocket *input = node->inputs.first; - bNodeSocket *output = node->outputs.first; - bNodeLink *link; - int type = SOCK_FLOAT; - const char *type_idname = nodeStaticSocketType(type, PROP_NONE); - - /* XXX it would be a little bit more efficient to restrict actual updates - * to reroute nodes connected to an updated node, but there's no reliable flag - * to indicate updated nodes (node->update is not set on linking). - */ - - node->done = 1; + Stack<bNode *> nodes_to_check; + for (bNodeLink *link : links_map.lookup(start_socket)) { + if (link->tonode->type == NODE_REROUTE) { + nodes_to_check.push(link->tonode); + } + if (link->fromnode->type == NODE_REROUTE) { + nodes_to_check.push(link->fromnode); + } + } + const bNodeSocketType *current_type = start_socket->typeinfo; + while (!nodes_to_check.is_empty()) { + bNode *reroute_node = nodes_to_check.pop(); + BLI_assert(reroute_node->type == NODE_REROUTE); + if (r_reroute_types.add(reroute_node, current_type)) { + for (bNodeLink *link : links_map.lookup((bNodeSocket *)reroute_node->inputs.first)) { + if (link->fromnode->type == NODE_REROUTE) { + nodes_to_check.push(link->fromnode); + } + } + for (bNodeLink *link : links_map.lookup((bNodeSocket *)reroute_node->outputs.first)) { + if (link->tonode->type == NODE_REROUTE) { + nodes_to_check.push(link->tonode); + } + } + } + } +} - /* recursive update */ - for (link = ntree->links.first; link; link = link->next) { - bNode *fromnode = link->fromnode; - bNode *tonode = link->tonode; - if (!tonode || !fromnode) { +void ntree_update_reroute_nodes(bNodeTree *ntree) +{ + /* Contains nodes that are linked to at least one reroute node. */ + Set<bNode *> nodes_linked_with_reroutes; + /* Contains all links that are linked to at least one reroute node. */ + MultiValueMap<bNodeSocket *, bNodeLink *> links_map; + /* Build acceleration data structures for the algorithm below. */ + LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) { + if (link->fromsock == nullptr || link->tosock == nullptr) { continue; } - if (nodeLinkIsHidden(link)) { + if (link->fromnode->type != NODE_REROUTE && link->tonode->type != NODE_REROUTE) { continue; } - - if (flag & REFINE_FORWARD) { - if (tonode == node && fromnode->type == NODE_REROUTE && !fromnode->done) { - node_reroute_inherit_type_recursive(ntree, fromnode, REFINE_FORWARD); - } + if (link->fromnode->type != NODE_REROUTE) { + nodes_linked_with_reroutes.add(link->fromnode); } - if (flag & REFINE_BACKWARD) { - if (fromnode == node && tonode->type == NODE_REROUTE && !tonode->done) { - node_reroute_inherit_type_recursive(ntree, tonode, REFINE_BACKWARD); - } + if (link->tonode->type != NODE_REROUTE) { + nodes_linked_with_reroutes.add(link->tonode); } + links_map.add(link->fromsock, link); + links_map.add(link->tosock, link); } - /* determine socket type from unambiguous input/output connection if possible */ - if (nodeSocketLinkLimit(input) == 1 && input->link) { - type = input->link->fromsock->type; - type_idname = nodeStaticSocketType(type, PROP_NONE); - } - else if (nodeSocketLinkLimit(output) == 1 && output->link) { - type = output->link->tosock->type; - type_idname = nodeStaticSocketType(type, PROP_NONE); - } + /* Will contain the socket type for every linked reroute node. */ + Map<bNode *, const bNodeSocketType *> reroute_types; - if (input->type != type) { - bNodeSocket *ninput = nodeAddSocket(ntree, node, SOCK_IN, type_idname, "input", "Input"); - for (link = ntree->links.first; link; link = link->next) { - if (link->tosock == input) { - link->tosock = ninput; - ninput->link = link; - } + /* Propagate socket types from left to right. */ + for (bNode *start_node : nodes_linked_with_reroutes) { + LISTBASE_FOREACH (bNodeSocket *, output_socket, &start_node->outputs) { + propagate_reroute_type_from_start_socket(output_socket, links_map, reroute_types); } - nodeRemoveSocket(ntree, node, input); } - if (output->type != type) { - bNodeSocket *noutput = nodeAddSocket(ntree, node, SOCK_OUT, type_idname, "output", "Output"); - for (link = ntree->links.first; link; link = link->next) { - if (link->fromsock == output) { - link->fromsock = noutput; - } + /* Propagate socket types from right to left. This affects reroute nodes that haven't been + * changed in the the loop above. */ + for (bNode *start_node : nodes_linked_with_reroutes) { + LISTBASE_FOREACH (bNodeSocket *, input_socket, &start_node->inputs) { + propagate_reroute_type_from_start_socket(input_socket, links_map, reroute_types); } - nodeRemoveSocket(ntree, node, output); } - nodeUpdateInternalLinks(ntree, node); -} - -/* Global update function for Reroute node types. - * This depends on connected nodes, so must be done as a tree-wide update. - */ -void ntree_update_reroute_nodes(bNodeTree *ntree) -{ - bNode *node; - - /* clear tags */ - for (node = ntree->nodes.first; node; node = node->next) { - node->done = 0; - } + /* Actually update reroute nodes with changed types. */ + for (const auto item : reroute_types.items()) { + bNode *reroute_node = item.key; + const bNodeSocketType *socket_type = item.value; + bNodeSocket *input_socket = (bNodeSocket *)reroute_node->inputs.first; + bNodeSocket *output_socket = (bNodeSocket *)reroute_node->outputs.first; - for (node = ntree->nodes.first; node; node = node->next) { - if (node->type == NODE_REROUTE && !node->done) { - node_reroute_inherit_type_recursive(ntree, node, REFINE_FORWARD | REFINE_BACKWARD); + if (input_socket->typeinfo != socket_type) { + nodeModifySocketType(ntree, reroute_node, input_socket, socket_type->idname); + } + if (output_socket->typeinfo != socket_type) { + nodeModifySocketType(ntree, reroute_node, output_socket, socket_type->idname); } } } @@ -393,7 +381,7 @@ static bool node_is_connected_to_output_recursive(bNodeTree *ntree, bNode *node) } /* test all connected nodes, first positive find is sufficient to return true */ - for (link = ntree->links.first; link; link = link->next) { + for (link = (bNodeLink *)ntree->links.first; link; link = link->next) { if (link->fromnode == node) { if (node_is_connected_to_output_recursive(ntree, link->tonode)) { return true; @@ -408,7 +396,7 @@ bool BKE_node_is_connected_to_output(bNodeTree *ntree, bNode *node) bNode *tnode; /* clear flags */ - for (tnode = ntree->nodes.first; tnode; tnode = tnode->next) { + for (tnode = (bNode *)ntree->nodes.first; tnode; tnode = tnode->next) { tnode->done = 0; } @@ -419,9 +407,9 @@ void BKE_node_tree_unlink_id(ID *id, struct bNodeTree *ntree) { bNode *node; - for (node = ntree->nodes.first; node; node = node->next) { + for (node = (bNode *)ntree->nodes.first; node; node = node->next) { if (node->id == id) { - node->id = NULL; + node->id = nullptr; } } } @@ -432,6 +420,11 @@ void BKE_node_tree_unlink_id(ID *id, struct bNodeTree *ntree) /** \name Node #GROUP_INPUT / #GROUP_OUTPUT * \{ */ +static bool is_group_extension_socket(const bNode *node, const bNodeSocket *socket) +{ + return socket->type == SOCK_CUSTOM && ELEM(node->type, NODE_GROUP_OUTPUT, NODE_GROUP_INPUT); +} + static void node_group_input_init(bNodeTree *ntree, bNode *node) { node_group_input_update(ntree, node); @@ -440,17 +433,17 @@ static void node_group_input_init(bNodeTree *ntree, bNode *node) bNodeSocket *node_group_input_find_socket(bNode *node, const char *identifier) { bNodeSocket *sock; - for (sock = node->outputs.first; sock; sock = sock->next) { + for (sock = (bNodeSocket *)node->outputs.first; sock; sock = sock->next) { if (STREQ(sock->identifier, identifier)) { return sock; } } - return NULL; + return nullptr; } void node_group_input_update(bNodeTree *ntree, bNode *node) { - bNodeSocket *extsock = node->outputs.last; + bNodeSocket *extsock = (bNodeSocket *)node->outputs.last; bNodeLink *link, *linknext, *exposelink; /* Adding a tree socket and verifying will remove the extension socket! * This list caches the existing links from the extension socket @@ -460,14 +453,14 @@ void node_group_input_update(bNodeTree *ntree, bNode *node) /* find links from the extension socket and store them */ BLI_listbase_clear(&tmplinks); - for (link = ntree->links.first; link; link = linknext) { + for (link = (bNodeLink *)ntree->links.first; link; link = linknext) { linknext = link->next; if (nodeLinkIsHidden(link)) { continue; } if (link->fromsock == extsock) { - bNodeLink *tlink = MEM_callocN(sizeof(bNodeLink), "temporary link"); + bNodeLink *tlink = MEM_cnew<bNodeLink>("temporary link"); *tlink = *link; BLI_addtail(&tmplinks, tlink); @@ -476,14 +469,14 @@ void node_group_input_update(bNodeTree *ntree, bNode *node) } /* find valid link to expose */ - exposelink = NULL; - for (link = tmplinks.first; link; link = link->next) { + exposelink = nullptr; + for (link = (bNodeLink *)tmplinks.first; link; link = link->next) { /* XXX Multiple sockets can be connected to the extension socket at once, * in that case the arbitrary first link determines name and type. * This could be improved by choosing the "best" type among all links, * whatever that means. */ - if (link->tosock->type != SOCK_CUSTOM) { + if (!is_group_extension_socket(link->tonode, link->tosock)) { exposelink = link; break; } @@ -498,7 +491,7 @@ void node_group_input_update(bNodeTree *ntree, bNode *node) newsock = node_group_input_find_socket(node, gsock->identifier); /* redirect links from the extension socket */ - for (link = tmplinks.first; link; link = link->next) { + for (link = (bNodeLink *)tmplinks.first; link; link = link->next) { nodeAddLink(ntree, node, newsock, link->tonode, link->tosock); } } @@ -508,20 +501,20 @@ void node_group_input_update(bNodeTree *ntree, bNode *node) /* check inputs and outputs, and remove or insert them */ { /* value_in_out inverted for interface nodes to get correct socket value_property */ - group_verify_socket_list(ntree, node, &ntree->inputs, &node->outputs, SOCK_OUT); + group_verify_socket_list(*ntree, *node, ntree->inputs, node->outputs, SOCK_OUT); /* add virtual extension socket */ nodeAddSocket(ntree, node, SOCK_OUT, "NodeSocketVirtual", "__extend__", ""); } } -void register_node_type_group_input(void) +void register_node_type_group_input() { /* used for all tree types, needs dynamic allocation */ - bNodeType *ntype = MEM_callocN(sizeof(bNodeType), "node type"); + bNodeType *ntype = MEM_cnew<bNodeType>("node type"); ntype->free_self = (void (*)(bNodeType *))MEM_freeN; - node_type_base(ntype, NODE_GROUP_INPUT, "Group Input", NODE_CLASS_INTERFACE, 0); + node_type_base(ntype, NODE_GROUP_INPUT, "Group Input", NODE_CLASS_INTERFACE); node_type_size(ntype, 140, 80, 400); node_type_init(ntype, node_group_input_init); node_type_update(ntype, node_group_input_update); @@ -537,17 +530,17 @@ static void node_group_output_init(bNodeTree *ntree, bNode *node) bNodeSocket *node_group_output_find_socket(bNode *node, const char *identifier) { bNodeSocket *sock; - for (sock = node->inputs.first; sock; sock = sock->next) { + for (sock = (bNodeSocket *)node->inputs.first; sock; sock = sock->next) { if (STREQ(sock->identifier, identifier)) { return sock; } } - return NULL; + return nullptr; } void node_group_output_update(bNodeTree *ntree, bNode *node) { - bNodeSocket *extsock = node->inputs.last; + bNodeSocket *extsock = (bNodeSocket *)node->inputs.last; bNodeLink *link, *linknext, *exposelink; /* Adding a tree socket and verifying will remove the extension socket! * This list caches the existing links to the extension socket @@ -557,14 +550,14 @@ void node_group_output_update(bNodeTree *ntree, bNode *node) /* find links to the extension socket and store them */ BLI_listbase_clear(&tmplinks); - for (link = ntree->links.first; link; link = linknext) { + for (link = (bNodeLink *)ntree->links.first; link; link = linknext) { linknext = link->next; if (nodeLinkIsHidden(link)) { continue; } if (link->tosock == extsock) { - bNodeLink *tlink = MEM_callocN(sizeof(bNodeLink), "temporary link"); + bNodeLink *tlink = MEM_cnew<bNodeLink>("temporary link"); *tlink = *link; BLI_addtail(&tmplinks, tlink); @@ -573,14 +566,14 @@ void node_group_output_update(bNodeTree *ntree, bNode *node) } /* find valid link to expose */ - exposelink = NULL; - for (link = tmplinks.first; link; link = link->next) { + exposelink = nullptr; + for (link = (bNodeLink *)tmplinks.first; link; link = link->next) { /* XXX Multiple sockets can be connected to the extension socket at once, * in that case the arbitrary first link determines name and type. * This could be improved by choosing the "best" type among all links, * whatever that means. */ - if (link->fromsock->type != SOCK_CUSTOM) { + if (!is_group_extension_socket(link->fromnode, link->fromsock)) { exposelink = link; break; } @@ -596,7 +589,7 @@ void node_group_output_update(bNodeTree *ntree, bNode *node) newsock = node_group_output_find_socket(node, gsock->identifier); /* redirect links to the extension socket */ - for (link = tmplinks.first; link; link = link->next) { + for (link = (bNodeLink *)tmplinks.first; link; link = link->next) { nodeAddLink(ntree, link->fromnode, link->fromsock, node, newsock); } } @@ -606,24 +599,26 @@ void node_group_output_update(bNodeTree *ntree, bNode *node) /* check inputs and outputs, and remove or insert them */ { /* value_in_out inverted for interface nodes to get correct socket value_property */ - group_verify_socket_list(ntree, node, &ntree->outputs, &node->inputs, SOCK_IN); + group_verify_socket_list(*ntree, *node, ntree->outputs, node->inputs, SOCK_IN); /* add virtual extension socket */ nodeAddSocket(ntree, node, SOCK_IN, "NodeSocketVirtual", "__extend__", ""); } } -void register_node_type_group_output(void) +void register_node_type_group_output() { /* used for all tree types, needs dynamic allocation */ - bNodeType *ntype = MEM_callocN(sizeof(bNodeType), "node type"); + bNodeType *ntype = MEM_cnew<bNodeType>("node type"); ntype->free_self = (void (*)(bNodeType *))MEM_freeN; - node_type_base(ntype, NODE_GROUP_OUTPUT, "Group Output", NODE_CLASS_INTERFACE, 0); + node_type_base(ntype, NODE_GROUP_OUTPUT, "Group Output", NODE_CLASS_INTERFACE); node_type_size(ntype, 140, 80, 400); node_type_init(ntype, node_group_output_init); node_type_update(ntype, node_group_output_update); + ntype->no_muting = true; + nodeRegisterType(ntype); } diff --git a/source/blender/nodes/intern/node_common.h b/source/blender/nodes/intern/node_common.h index cdb7b6897b9..0d1b51224e6 100644 --- a/source/blender/nodes/intern/node_common.h +++ b/source/blender/nodes/intern/node_common.h @@ -31,11 +31,19 @@ extern "C" { struct bNodeTree; -void node_group_label(struct bNodeTree *ntree, struct bNode *node, char *label, int maxlen); +/** Groups display their internal tree name as label. */ +void node_group_label(const struct bNodeTree *ntree, + const struct bNode *node, + char *label, + int maxlen); bool node_group_poll_instance(struct bNode *node, struct bNodeTree *nodetree, const char **r_disabled_hint); +/** + * Global update function for Reroute node types. + * This depends on connected nodes, so must be done as a tree-wide update. + */ void ntree_update_reroute_nodes(struct bNodeTree *ntree); #ifdef __cplusplus diff --git a/source/blender/nodes/intern/node_declaration.cc b/source/blender/nodes/intern/node_declaration.cc index 8a38b68ec59..28e8bf5a4b7 100644 --- a/source/blender/nodes/intern/node_declaration.cc +++ b/source/blender/nodes/intern/node_declaration.cc @@ -20,16 +20,6 @@ namespace blender::nodes { -void NodeDeclaration::build(bNodeTree &ntree, bNode &node) const -{ - for (const SocketDeclarationPtr &decl : inputs_) { - decl->build(ntree, node, SOCK_IN); - } - for (const SocketDeclarationPtr &decl : outputs_) { - decl->build(ntree, node, SOCK_OUT); - } -} - bool NodeDeclaration::matches(const bNode &node) const { auto check_sockets = [&](ListBase sockets, Span<SocketDeclarationPtr> socket_decls) { @@ -61,15 +51,19 @@ bNodeSocket &SocketDeclaration::update_or_build(bNodeTree &ntree, bNodeSocket &socket) const { /* By default just rebuild. */ - return this->build(ntree, node, (eNodeSocketInOut)socket.in_out); + BLI_assert(socket.in_out == in_out_); + UNUSED_VARS_NDEBUG(socket); + return this->build(ntree, node); } void SocketDeclaration::set_common_flags(bNodeSocket &socket) const { + SET_FLAG_FROM_TEST(socket.flag, compact_, SOCK_COMPACT); SET_FLAG_FROM_TEST(socket.flag, hide_value_, SOCK_HIDE_VALUE); SET_FLAG_FROM_TEST(socket.flag, hide_label_, SOCK_HIDE_LABEL); SET_FLAG_FROM_TEST(socket.flag, is_multi_input_, SOCK_MULTI_INPUT); SET_FLAG_FROM_TEST(socket.flag, no_mute_links_, SOCK_NO_INTERNAL_LINK); + SET_FLAG_FROM_TEST(socket.flag, is_unavailable_, SOCK_UNAVAIL); } bool SocketDeclaration::matches_common_data(const bNodeSocket &socket) const @@ -80,6 +74,9 @@ bool SocketDeclaration::matches_common_data(const bNodeSocket &socket) const if (socket.identifier != identifier_) { return false; } + if (((socket.flag & SOCK_COMPACT) != 0) != compact_) { + return false; + } if (((socket.flag & SOCK_HIDE_VALUE) != 0) != hide_value_) { return false; } @@ -92,6 +89,9 @@ bool SocketDeclaration::matches_common_data(const bNodeSocket &socket) const if (((socket.flag & SOCK_NO_INTERNAL_LINK) != 0) != no_mute_links_) { return false; } + if (((socket.flag & SOCK_UNAVAIL) != 0) != is_unavailable_) { + return false; + } return true; } diff --git a/source/blender/nodes/intern/node_exec.cc b/source/blender/nodes/intern/node_exec.cc index 18403417af3..4ff662036c3 100644 --- a/source/blender/nodes/intern/node_exec.cc +++ b/source/blender/nodes/intern/node_exec.cc @@ -28,20 +28,19 @@ #include "BKE_global.h" #include "BKE_node.h" +#include "BKE_node_tree_update.h" #include "MEM_guardedalloc.h" #include "node_exec.h" #include "node_util.h" -/* supported socket types in old nodes */ int node_exec_socket_use_stack(bNodeSocket *sock) { /* NOTE: INT supported as FLOAT. Only for EEVEE. */ return ELEM(sock->type, SOCK_INT, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_SHADER); } -/* for a given socket, find the actual stack entry */ bNodeStack *node_get_socket_stack(bNodeStack *stack, bNodeSocket *sock) { if (stack && sock && sock->stack_index >= 0) { @@ -172,13 +171,13 @@ bNodeTreeExec *ntree_exec_begin(bNodeExecContext *context, /* Using global main here is likely totally wrong, not sure what to do about that one though... * We cannot even check ntree is in global main, * since most of the time it won't be (thanks to ntree design)!!! */ - ntreeUpdateTree(G.main, ntree); + BKE_ntree_update_main_tree(G.main, ntree, nullptr); /* get a dependency-sorted list of nodes */ ntreeGetDependencyList(ntree, &nodelist, &totnodes); /* XXX could let callbacks do this for specialized data */ - exec = (bNodeTreeExec *)MEM_callocN(sizeof(bNodeTreeExec), "node tree execution data"); + exec = MEM_cnew<bNodeTreeExec>("node tree execution data"); /* backpointer to node tree */ exec->nodetree = ntree; @@ -293,7 +292,7 @@ bNodeThreadStack *ntreeGetThreadStack(bNodeTreeExec *exec, int thread) } if (!nts) { - nts = (bNodeThreadStack *)MEM_callocN(sizeof(bNodeThreadStack), "bNodeThreadStack"); + nts = MEM_cnew<bNodeThreadStack>("bNodeThreadStack"); nts->stack = (bNodeStack *)MEM_dupallocN(exec->stack); nts->used = true; BLI_addtail(lb, nts); diff --git a/source/blender/nodes/intern/node_exec.h b/source/blender/nodes/intern/node_exec.h index de7cbb8cedb..b2e1c6564b6 100644 --- a/source/blender/nodes/intern/node_exec.h +++ b/source/blender/nodes/intern/node_exec.h @@ -71,8 +71,10 @@ typedef struct bNodeThreadStack { bool used; } bNodeThreadStack; +/** Supported socket types in old nodes. */ int node_exec_socket_use_stack(struct bNodeSocket *sock); +/** For a given socket, find the actual stack entry. */ struct bNodeStack *node_get_socket_stack(struct bNodeStack *stack, struct bNodeSocket *sock); void node_get_stack(struct bNode *node, struct bNodeStack *stack, diff --git a/source/blender/nodes/intern/node_geometry_exec.cc b/source/blender/nodes/intern/node_geometry_exec.cc index a3bbca90731..b5c4e71df74 100644 --- a/source/blender/nodes/intern/node_geometry_exec.cc +++ b/source/blender/nodes/intern/node_geometry_exec.cc @@ -18,8 +18,9 @@ #include "DEG_depsgraph_query.h" +#include "BKE_type_conversions.hh" + #include "NOD_geometry_exec.hh" -#include "NOD_type_conversions.hh" #include "node_geometry_util.hh" @@ -36,6 +37,72 @@ void GeoNodeExecParams::error_message_add(const NodeWarningType type, std::strin local_logger.log_node_warning(provider_->dnode, type, std::move(message)); } +void GeoNodeExecParams::check_input_geometry_set(StringRef identifier, + const GeometrySet &geometry_set) const +{ + const SocketDeclaration &decl = + *provider_->dnode->input_by_identifier(identifier).bsocket()->declaration; + const decl::Geometry *geo_decl = dynamic_cast<const decl::Geometry *>(&decl); + if (geo_decl == nullptr) { + return; + } + + const bool only_realized_data = geo_decl->only_realized_data(); + const bool only_instances = geo_decl->only_instances(); + const Span<GeometryComponentType> supported_types = geo_decl->supported_types(); + + if (only_realized_data) { + if (geometry_set.has_instances()) { + this->error_message_add(NodeWarningType::Info, + TIP_("Instances in input geometry are ignored")); + } + } + if (only_instances) { + if (geometry_set.has_realized_data()) { + this->error_message_add(NodeWarningType::Info, + TIP_("Realized data in input geometry is ignored")); + } + } + if (supported_types.is_empty()) { + /* Assume all types are supported. */ + return; + } + const Vector<GeometryComponentType> types_in_geometry = geometry_set.gather_component_types( + true, true); + for (const GeometryComponentType type : types_in_geometry) { + if (type == GEO_COMPONENT_TYPE_INSTANCES) { + continue; + } + if (supported_types.contains(type)) { + continue; + } + std::string message = TIP_("Input geometry has unsupported type: "); + switch (type) { + case GEO_COMPONENT_TYPE_MESH: { + message += TIP_("Mesh"); + break; + } + case GEO_COMPONENT_TYPE_POINT_CLOUD: { + message += TIP_("Point Cloud"); + break; + } + case GEO_COMPONENT_TYPE_INSTANCES: { + BLI_assert_unreachable(); + break; + } + case GEO_COMPONENT_TYPE_VOLUME: { + message += TIP_("Volume"); + break; + } + case GEO_COMPONENT_TYPE_CURVE: { + message += TIP_("Curve"); + break; + } + } + this->error_message_add(NodeWarningType::Info, std::move(message)); + } +} + const bNodeSocket *GeoNodeExecParams::find_available_socket(const StringRef name) const { for (const InputSocketRef *socket : provider_->dnode->inputs()) { @@ -47,11 +114,11 @@ const bNodeSocket *GeoNodeExecParams::find_available_socket(const StringRef name return nullptr; } -GVArrayPtr GeoNodeExecParams::get_input_attribute(const StringRef name, - const GeometryComponent &component, - const AttributeDomain domain, - const CustomDataType type, - const void *default_value) const +GVArray GeoNodeExecParams::get_input_attribute(const StringRef name, + const GeometryComponent &component, + const AttributeDomain domain, + const CustomDataType type, + const void *default_value) const { const bNodeSocket *found_socket = this->find_available_socket(name); BLI_assert(found_socket != nullptr); /* There should always be available socket for the name. */ @@ -63,13 +130,13 @@ GVArrayPtr GeoNodeExecParams::get_input_attribute(const StringRef name, } if (found_socket == nullptr) { - return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, default_value); + return GVArray::ForSingle(*cpp_type, domain_size, default_value); } if (found_socket->type == SOCK_STRING) { const std::string name = this->get_input<std::string>(found_socket->identifier); /* Try getting the attribute without the default value. */ - GVArrayPtr attribute = component.attribute_try_get_for_read(name, domain, type); + GVArray attribute = component.attribute_try_get_for_read(name, domain, type); if (attribute) { return attribute; } @@ -81,36 +148,36 @@ GVArrayPtr GeoNodeExecParams::get_input_attribute(const StringRef name, this->error_message_add(NodeWarningType::Error, TIP_("No attribute with name \"") + name + "\""); } - return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, default_value); + return GVArray::ForSingle(*cpp_type, domain_size, default_value); } - const DataTypeConversions &conversions = get_implicit_type_conversions(); + const bke::DataTypeConversions &conversions = bke::get_implicit_type_conversions(); if (found_socket->type == SOCK_FLOAT) { const float value = this->get_input<float>(found_socket->identifier); BUFFER_FOR_CPP_TYPE_VALUE(*cpp_type, buffer); conversions.convert_to_uninitialized(CPPType::get<float>(), *cpp_type, &value, buffer); - return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, buffer); + return GVArray::ForSingle(*cpp_type, domain_size, buffer); } if (found_socket->type == SOCK_INT) { const int value = this->get_input<int>(found_socket->identifier); BUFFER_FOR_CPP_TYPE_VALUE(*cpp_type, buffer); conversions.convert_to_uninitialized(CPPType::get<int>(), *cpp_type, &value, buffer); - return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, buffer); + return GVArray::ForSingle(*cpp_type, domain_size, buffer); } if (found_socket->type == SOCK_VECTOR) { const float3 value = this->get_input<float3>(found_socket->identifier); BUFFER_FOR_CPP_TYPE_VALUE(*cpp_type, buffer); conversions.convert_to_uninitialized(CPPType::get<float3>(), *cpp_type, &value, buffer); - return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, buffer); + return GVArray::ForSingle(*cpp_type, domain_size, buffer); } if (found_socket->type == SOCK_RGBA) { const ColorGeometry4f value = this->get_input<ColorGeometry4f>(found_socket->identifier); BUFFER_FOR_CPP_TYPE_VALUE(*cpp_type, buffer); conversions.convert_to_uninitialized( CPPType::get<ColorGeometry4f>(), *cpp_type, &value, buffer); - return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, buffer); + return GVArray::ForSingle(*cpp_type, domain_size, buffer); } BLI_assert(false); - return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, default_value); + return GVArray::ForSingle(*cpp_type, domain_size, default_value); } CustomDataType GeoNodeExecParams::get_input_attribute_data_type( @@ -149,11 +216,6 @@ CustomDataType GeoNodeExecParams::get_input_attribute_data_type( return default_type; } -/** - * If any of the corresponding input sockets are attributes instead of single values, - * use the highest priority attribute domain from among them. - * Otherwise return the default domain. - */ AttributeDomain GeoNodeExecParams::get_highest_priority_input_domain( Span<std::string> names, const GeometryComponent &component, @@ -183,6 +245,16 @@ AttributeDomain GeoNodeExecParams::get_highest_priority_input_domain( return default_domain; } +std::string GeoNodeExecParams::attribute_producer_name() const +{ + return provider_->dnode->label_or_name() + TIP_(" node"); +} + +void GeoNodeExecParams::set_default_remaining_outputs() +{ + provider_->set_default_remaining_outputs(); +} + void GeoNodeExecParams::check_input_access(StringRef identifier, const CPPType *requested_type) const { @@ -217,7 +289,7 @@ void GeoNodeExecParams::check_input_access(StringRef identifier, BLI_assert_unreachable(); } else if (requested_type != nullptr) { - const CPPType &expected_type = *found_socket->typeinfo->get_geometry_nodes_cpp_type(); + const CPPType &expected_type = *found_socket->typeinfo->geometry_nodes_cpp_type; if (*requested_type != expected_type) { std::cout << "The requested type '" << requested_type->name() << "' is incorrect. Expected '" << expected_type.name() << "'.\n"; @@ -257,7 +329,7 @@ void GeoNodeExecParams::check_output_access(StringRef identifier, const CPPType BLI_assert_unreachable(); } else { - const CPPType &expected_type = *found_socket->typeinfo->get_geometry_nodes_cpp_type(); + const CPPType &expected_type = *found_socket->typeinfo->geometry_nodes_cpp_type; if (value_type != expected_type) { std::cout << "The value type '" << value_type.name() << "' is incorrect. Expected '" << expected_type.name() << "'.\n"; diff --git a/source/blender/nodes/intern/node_multi_function.cc b/source/blender/nodes/intern/node_multi_function.cc index c91899ed8c2..6d79ed839b2 100644 --- a/source/blender/nodes/intern/node_multi_function.cc +++ b/source/blender/nodes/intern/node_multi_function.cc @@ -18,7 +18,7 @@ namespace blender::nodes { -NodeMultiFunctions::NodeMultiFunctions(const DerivedNodeTree &tree, ResourceScope &resource_scope) +NodeMultiFunctions::NodeMultiFunctions(const DerivedNodeTree &tree) { for (const NodeTreeRef *tree_ref : tree.used_node_tree_refs()) { bNodeTree *btree = tree_ref->btree(); @@ -27,11 +27,10 @@ NodeMultiFunctions::NodeMultiFunctions(const DerivedNodeTree &tree, ResourceScop if (bnode->typeinfo->build_multi_function == nullptr) { continue; } - NodeMultiFunctionBuilder builder{resource_scope, *bnode, *btree}; + NodeMultiFunctionBuilder builder{*bnode, *btree}; bnode->typeinfo->build_multi_function(builder); - const MultiFunction *fn = builder.built_fn_; - if (fn != nullptr) { - map_.add_new(bnode, fn); + if (builder.built_fn_ != nullptr) { + map_.add_new(bnode, {builder.built_fn_, std::move(builder.owned_built_fn_)}); } } } diff --git a/source/blender/nodes/intern/node_socket.cc b/source/blender/nodes/intern/node_socket.cc index 31260f95242..ed72580ccf1 100644 --- a/source/blender/nodes/intern/node_socket.cc +++ b/source/blender/nodes/intern/node_socket.cc @@ -26,9 +26,8 @@ #include "DNA_node_types.h" #include "BLI_color.hh" -#include "BLI_float3.hh" #include "BLI_listbase.h" -#include "BLI_math.h" +#include "BLI_math_vec_types.hh" #include "BLI_string.h" #include "BLI_utildefines.h" @@ -51,6 +50,7 @@ #include "FN_field.hh" using namespace blender; +using blender::fn::ValueOrField; using blender::nodes::SocketDeclarationPtr; struct bNodeSocket *node_add_socket_from_template(struct bNodeTree *ntree, @@ -191,7 +191,6 @@ static void refresh_socket_list(bNodeTree &ntree, bNode &node, ListBase &sockets, Span<SocketDeclarationPtr> socket_decls, - const eNodeSocketInOut in_out, const bool do_id_user) { Vector<bNodeSocket *> old_sockets = sockets; @@ -210,7 +209,7 @@ static void refresh_socket_list(bNodeTree &ntree, bNodeSocket *new_socket = nullptr; if (old_socket_with_same_identifier == nullptr) { /* Create a completely new socket. */ - new_socket = &socket_decl->build(ntree, node, in_out); + new_socket = &socket_decl->build(ntree, node); } else { STRNCPY(old_socket_with_same_identifier->name, socket_decl->name().c_str()); @@ -237,6 +236,14 @@ static void refresh_socket_list(bNodeTree &ntree, link->tosock = new_socket; } } + LISTBASE_FOREACH (bNodeLink *, internal_link, &node.internal_links) { + if (internal_link->fromsock == old_socket_with_same_identifier) { + internal_link->fromsock = new_socket; + } + else if (internal_link->tosock == old_socket_with_same_identifier) { + internal_link->tosock = new_socket; + } + } } } } @@ -258,8 +265,8 @@ static void refresh_node(bNodeTree &ntree, blender::nodes::NodeDeclaration &node_decl, bool do_id_user) { - refresh_socket_list(ntree, node, node.inputs, node_decl.inputs(), SOCK_IN, do_id_user); - refresh_socket_list(ntree, node, node.outputs, node_decl.outputs(), SOCK_OUT, do_id_user); + refresh_socket_list(ntree, node, node.inputs, node_decl.inputs(), do_id_user); + refresh_socket_list(ntree, node, node.outputs, node_decl.outputs(), do_id_user); } void node_verify_sockets(bNodeTree *ntree, bNode *node, bool do_id_user) @@ -269,10 +276,11 @@ void node_verify_sockets(bNodeTree *ntree, bNode *node, bool do_id_user) return; } if (ntype->declare != nullptr) { - nodeDeclarationEnsure(ntree, node); + nodeDeclarationEnsureOnOutdatedNode(ntree, node); if (!node->declaration->matches(*node)) { refresh_node(*ntree, *node, *node->declaration, do_id_user); } + nodeSocketDeclarationsUpdate(node); return; } /* Don't try to match socket lists when there are no templates. @@ -301,8 +309,7 @@ void node_socket_init_default_value(bNodeSocket *sock) switch (type) { case SOCK_FLOAT: { - bNodeSocketValueFloat *dval = (bNodeSocketValueFloat *)MEM_callocN( - sizeof(bNodeSocketValueFloat), "node socket value float"); + bNodeSocketValueFloat *dval = MEM_cnew<bNodeSocketValueFloat>("node socket value float"); dval->subtype = subtype; dval->value = 0.0f; dval->min = -FLT_MAX; @@ -312,8 +319,7 @@ void node_socket_init_default_value(bNodeSocket *sock) break; } case SOCK_INT: { - bNodeSocketValueInt *dval = (bNodeSocketValueInt *)MEM_callocN(sizeof(bNodeSocketValueInt), - "node socket value int"); + bNodeSocketValueInt *dval = MEM_cnew<bNodeSocketValueInt>("node socket value int"); dval->subtype = subtype; dval->value = 0; dval->min = INT_MIN; @@ -323,8 +329,7 @@ void node_socket_init_default_value(bNodeSocket *sock) break; } case SOCK_BOOLEAN: { - bNodeSocketValueBoolean *dval = (bNodeSocketValueBoolean *)MEM_callocN( - sizeof(bNodeSocketValueBoolean), "node socket value bool"); + bNodeSocketValueBoolean *dval = MEM_cnew<bNodeSocketValueBoolean>("node socket value bool"); dval->value = false; sock->default_value = dval; @@ -332,8 +337,7 @@ void node_socket_init_default_value(bNodeSocket *sock) } case SOCK_VECTOR: { static float default_value[] = {0.0f, 0.0f, 0.0f}; - bNodeSocketValueVector *dval = (bNodeSocketValueVector *)MEM_callocN( - sizeof(bNodeSocketValueVector), "node socket value vector"); + bNodeSocketValueVector *dval = MEM_cnew<bNodeSocketValueVector>("node socket value vector"); dval->subtype = subtype; copy_v3_v3(dval->value, default_value); dval->min = -FLT_MAX; @@ -344,16 +348,14 @@ void node_socket_init_default_value(bNodeSocket *sock) } case SOCK_RGBA: { static float default_value[] = {0.0f, 0.0f, 0.0f, 1.0f}; - bNodeSocketValueRGBA *dval = (bNodeSocketValueRGBA *)MEM_callocN( - sizeof(bNodeSocketValueRGBA), "node socket value color"); + bNodeSocketValueRGBA *dval = MEM_cnew<bNodeSocketValueRGBA>("node socket value color"); copy_v4_v4(dval->value, default_value); sock->default_value = dval; break; } case SOCK_STRING: { - bNodeSocketValueString *dval = (bNodeSocketValueString *)MEM_callocN( - sizeof(bNodeSocketValueString), "node socket value string"); + bNodeSocketValueString *dval = MEM_cnew<bNodeSocketValueString>("node socket value string"); dval->subtype = subtype; dval->value[0] = '\0'; @@ -361,40 +363,38 @@ void node_socket_init_default_value(bNodeSocket *sock) break; } case SOCK_OBJECT: { - bNodeSocketValueObject *dval = (bNodeSocketValueObject *)MEM_callocN( - sizeof(bNodeSocketValueObject), "node socket value object"); + bNodeSocketValueObject *dval = MEM_cnew<bNodeSocketValueObject>("node socket value object"); dval->value = nullptr; sock->default_value = dval; break; } case SOCK_IMAGE: { - bNodeSocketValueImage *dval = (bNodeSocketValueImage *)MEM_callocN( - sizeof(bNodeSocketValueImage), "node socket value image"); + bNodeSocketValueImage *dval = MEM_cnew<bNodeSocketValueImage>("node socket value image"); dval->value = nullptr; sock->default_value = dval; break; } case SOCK_COLLECTION: { - bNodeSocketValueCollection *dval = (bNodeSocketValueCollection *)MEM_callocN( - sizeof(bNodeSocketValueCollection), "node socket value object"); + bNodeSocketValueCollection *dval = MEM_cnew<bNodeSocketValueCollection>( + "node socket value object"); dval->value = nullptr; sock->default_value = dval; break; } case SOCK_TEXTURE: { - bNodeSocketValueTexture *dval = (bNodeSocketValueTexture *)MEM_callocN( - sizeof(bNodeSocketValueTexture), "node socket value texture"); + bNodeSocketValueTexture *dval = MEM_cnew<bNodeSocketValueTexture>( + "node socket value texture"); dval->value = nullptr; sock->default_value = dval; break; } case SOCK_MATERIAL: { - bNodeSocketValueMaterial *dval = (bNodeSocketValueMaterial *)MEM_callocN( - sizeof(bNodeSocketValueMaterial), "node socket value material"); + bNodeSocketValueMaterial *dval = MEM_cnew<bNodeSocketValueMaterial>( + "node socket value material"); dval->value = nullptr; sock->default_value = dval; @@ -547,7 +547,7 @@ void node_socket_skip_reroutes( } static void standard_node_socket_interface_init_socket(bNodeTree *UNUSED(ntree), - bNodeSocket *stemp, + const bNodeSocket *interface_socket, bNode *UNUSED(node), bNodeSocket *sock, const char *UNUSED(data_path)) @@ -558,47 +558,50 @@ static void standard_node_socket_interface_init_socket(bNodeTree *UNUSED(ntree), /* XXX socket interface 'type' value is not used really, * but has to match or the copy function will bail out */ - stemp->type = stemp->typeinfo->type; + const_cast<bNodeSocket *>(interface_socket)->type = interface_socket->typeinfo->type; /* copy default_value settings */ - node_socket_copy_default_value(sock, stemp); + node_socket_copy_default_value(sock, interface_socket); } /* copies settings that are not changed for each socket instance */ static void standard_node_socket_interface_verify_socket(bNodeTree *UNUSED(ntree), - bNodeSocket *stemp, + const bNodeSocket *interface_socket, bNode *UNUSED(node), bNodeSocket *sock, const char *UNUSED(data_path)) { /* sanity check */ - if (sock->type != stemp->typeinfo->type) { + if (sock->type != interface_socket->typeinfo->type) { return; } /* make sure both exist */ - if (!stemp->default_value) { + if (!interface_socket->default_value) { return; } node_socket_init_default_value(sock); - switch (stemp->typeinfo->type) { + switch (interface_socket->typeinfo->type) { case SOCK_FLOAT: { bNodeSocketValueFloat *toval = (bNodeSocketValueFloat *)sock->default_value; - bNodeSocketValueFloat *fromval = (bNodeSocketValueFloat *)stemp->default_value; + const bNodeSocketValueFloat *fromval = (const bNodeSocketValueFloat *) + interface_socket->default_value; toval->min = fromval->min; toval->max = fromval->max; break; } case SOCK_INT: { bNodeSocketValueInt *toval = (bNodeSocketValueInt *)sock->default_value; - bNodeSocketValueInt *fromval = (bNodeSocketValueInt *)stemp->default_value; + const bNodeSocketValueInt *fromval = (const bNodeSocketValueInt *) + interface_socket->default_value; toval->min = fromval->min; toval->max = fromval->max; break; } case SOCK_VECTOR: { bNodeSocketValueVector *toval = (bNodeSocketValueVector *)sock->default_value; - bNodeSocketValueVector *fromval = (bNodeSocketValueVector *)stemp->default_value; + const bNodeSocketValueVector *fromval = (const bNodeSocketValueVector *) + interface_socket->default_value; toval->min = fromval->min; toval->max = fromval->max; break; @@ -626,7 +629,7 @@ static bNodeSocketType *make_standard_socket_type(int type, int subtype) bNodeSocketType *stype; StructRNA *srna; - stype = (bNodeSocketType *)MEM_callocN(sizeof(bNodeSocketType), "node socket C type"); + stype = MEM_cnew<bNodeSocketType>("node socket C type"); stype->free_self = (void (*)(bNodeSocketType * stype)) MEM_freeN; BLI_strncpy(stype->idname, socket_idname, sizeof(stype->idname)); BLI_strncpy(stype->label, socket_label, sizeof(stype->label)); @@ -670,7 +673,7 @@ static bNodeSocketType *make_socket_type_virtual() bNodeSocketType *stype; StructRNA *srna; - stype = (bNodeSocketType *)MEM_callocN(sizeof(bNodeSocketType), "node socket C type"); + stype = MEM_cnew<bNodeSocketType>("node socket C type"); stype->free_self = (void (*)(bNodeSocketType * stype)) MEM_freeN; BLI_strncpy(stype->idname, socket_idname, sizeof(stype->idname)); @@ -696,17 +699,15 @@ static bNodeSocketType *make_socket_type_virtual() static bNodeSocketType *make_socket_type_bool() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_BOOLEAN, PROP_NONE); - socktype->get_base_cpp_type = []() { return &blender::fn::CPPType::get<bool>(); }; + socktype->base_cpp_type = &blender::fn::CPPType::get<bool>(); socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { *(bool *)r_value = ((bNodeSocketValueBoolean *)socket.default_value)->value; }; - socktype->get_geometry_nodes_cpp_type = []() { - return &blender::fn::CPPType::get<blender::fn::Field<bool>>(); - }; + socktype->geometry_nodes_cpp_type = &blender::fn::CPPType::get<ValueOrField<bool>>(); socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) { bool value; socket.typeinfo->get_base_cpp_value(socket, &value); - new (r_value) blender::fn::Field<bool>(blender::fn::make_constant_field(value)); + new (r_value) ValueOrField<bool>(value); }; return socktype; } @@ -714,17 +715,15 @@ static bNodeSocketType *make_socket_type_bool() static bNodeSocketType *make_socket_type_float(PropertySubType subtype) { bNodeSocketType *socktype = make_standard_socket_type(SOCK_FLOAT, subtype); - socktype->get_base_cpp_type = []() { return &blender::fn::CPPType::get<float>(); }; + socktype->base_cpp_type = &blender::fn::CPPType::get<float>(); socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { *(float *)r_value = ((bNodeSocketValueFloat *)socket.default_value)->value; }; - socktype->get_geometry_nodes_cpp_type = []() { - return &blender::fn::CPPType::get<blender::fn::Field<float>>(); - }; + socktype->geometry_nodes_cpp_type = &blender::fn::CPPType::get<ValueOrField<float>>(); socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) { float value; socket.typeinfo->get_base_cpp_value(socket, &value); - new (r_value) blender::fn::Field<float>(blender::fn::make_constant_field(value)); + new (r_value) ValueOrField<float>(value); }; return socktype; } @@ -732,17 +731,15 @@ static bNodeSocketType *make_socket_type_float(PropertySubType subtype) static bNodeSocketType *make_socket_type_int(PropertySubType subtype) { bNodeSocketType *socktype = make_standard_socket_type(SOCK_INT, subtype); - socktype->get_base_cpp_type = []() { return &blender::fn::CPPType::get<int>(); }; + socktype->base_cpp_type = &blender::fn::CPPType::get<int>(); socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { *(int *)r_value = ((bNodeSocketValueInt *)socket.default_value)->value; }; - socktype->get_geometry_nodes_cpp_type = []() { - return &blender::fn::CPPType::get<blender::fn::Field<int>>(); - }; + socktype->geometry_nodes_cpp_type = &blender::fn::CPPType::get<ValueOrField<int>>(); socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) { int value; socket.typeinfo->get_base_cpp_value(socket, &value); - new (r_value) blender::fn::Field<int>(blender::fn::make_constant_field(value)); + new (r_value) ValueOrField<int>(value); }; return socktype; } @@ -750,17 +747,15 @@ static bNodeSocketType *make_socket_type_int(PropertySubType subtype) static bNodeSocketType *make_socket_type_vector(PropertySubType subtype) { bNodeSocketType *socktype = make_standard_socket_type(SOCK_VECTOR, subtype); - socktype->get_base_cpp_type = []() { return &blender::fn::CPPType::get<blender::float3>(); }; + socktype->base_cpp_type = &blender::fn::CPPType::get<blender::float3>(); socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { *(blender::float3 *)r_value = ((bNodeSocketValueVector *)socket.default_value)->value; }; - socktype->get_geometry_nodes_cpp_type = []() { - return &blender::fn::CPPType::get<blender::fn::Field<blender::float3>>(); - }; + socktype->geometry_nodes_cpp_type = &blender::fn::CPPType::get<ValueOrField<blender::float3>>(); socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) { blender::float3 value; socket.typeinfo->get_base_cpp_value(socket, &value); - new (r_value) blender::fn::Field<blender::float3>(blender::fn::make_constant_field(value)); + new (r_value) ValueOrField<blender::float3>(value); }; return socktype; } @@ -768,20 +763,16 @@ static bNodeSocketType *make_socket_type_vector(PropertySubType subtype) static bNodeSocketType *make_socket_type_rgba() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_RGBA, PROP_NONE); - socktype->get_base_cpp_type = []() { - return &blender::fn::CPPType::get<blender::ColorGeometry4f>(); - }; + socktype->base_cpp_type = &blender::fn::CPPType::get<blender::ColorGeometry4f>(); socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { *(blender::ColorGeometry4f *)r_value = ((bNodeSocketValueRGBA *)socket.default_value)->value; }; - socktype->get_geometry_nodes_cpp_type = []() { - return &blender::fn::CPPType::get<blender::fn::Field<blender::ColorGeometry4f>>(); - }; + socktype->geometry_nodes_cpp_type = + &blender::fn::CPPType::get<ValueOrField<blender::ColorGeometry4f>>(); socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) { blender::ColorGeometry4f value; socket.typeinfo->get_base_cpp_value(socket, &value); - new (r_value) - blender::fn::Field<blender::ColorGeometry4f>(blender::fn::make_constant_field(value)); + new (r_value) ValueOrField<blender::ColorGeometry4f>(value); }; return socktype; } @@ -789,18 +780,16 @@ static bNodeSocketType *make_socket_type_rgba() static bNodeSocketType *make_socket_type_string() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_STRING, PROP_NONE); - socktype->get_base_cpp_type = []() { return &blender::fn::CPPType::get<std::string>(); }; + socktype->base_cpp_type = &blender::fn::CPPType::get<std::string>(); socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { new (r_value) std::string(((bNodeSocketValueString *)socket.default_value)->value); }; - socktype->get_geometry_nodes_cpp_type = []() { - return &blender::fn::CPPType::get<blender::fn::Field<std::string>>(); - }; + socktype->geometry_nodes_cpp_type = &blender::fn::CPPType::get<ValueOrField<std::string>>(); socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) { std::string value; value.~basic_string(); socket.typeinfo->get_base_cpp_value(socket, &value); - new (r_value) blender::fn::Field<std::string>(blender::fn::make_constant_field(value)); + new (r_value) ValueOrField<std::string>(value); }; return socktype; } @@ -808,16 +797,17 @@ static bNodeSocketType *make_socket_type_string() MAKE_CPP_TYPE(Object, Object *, CPPTypeFlags::BasicType) MAKE_CPP_TYPE(Collection, Collection *, CPPTypeFlags::BasicType) MAKE_CPP_TYPE(Texture, Tex *, CPPTypeFlags::BasicType) +MAKE_CPP_TYPE(Image, Image *, CPPTypeFlags::BasicType) MAKE_CPP_TYPE(Material, Material *, CPPTypeFlags::BasicType) static bNodeSocketType *make_socket_type_object() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_OBJECT, PROP_NONE); - socktype->get_base_cpp_type = []() { return &blender::fn::CPPType::get<Object *>(); }; + socktype->base_cpp_type = &blender::fn::CPPType::get<Object *>(); socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { *(Object **)r_value = ((bNodeSocketValueObject *)socket.default_value)->value; }; - socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type; + socktype->geometry_nodes_cpp_type = socktype->base_cpp_type; socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value; return socktype; } @@ -825,11 +815,11 @@ static bNodeSocketType *make_socket_type_object() static bNodeSocketType *make_socket_type_geometry() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_GEOMETRY, PROP_NONE); - socktype->get_base_cpp_type = []() { return &blender::fn::CPPType::get<GeometrySet>(); }; + socktype->base_cpp_type = &blender::fn::CPPType::get<GeometrySet>(); socktype->get_base_cpp_value = [](const bNodeSocket &UNUSED(socket), void *r_value) { new (r_value) GeometrySet(); }; - socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type; + socktype->geometry_nodes_cpp_type = socktype->base_cpp_type; socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value; return socktype; } @@ -837,11 +827,11 @@ static bNodeSocketType *make_socket_type_geometry() static bNodeSocketType *make_socket_type_collection() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_COLLECTION, PROP_NONE); - socktype->get_base_cpp_type = []() { return &blender::fn::CPPType::get<Collection *>(); }; + socktype->base_cpp_type = &blender::fn::CPPType::get<Collection *>(); socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { *(Collection **)r_value = ((bNodeSocketValueCollection *)socket.default_value)->value; }; - socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type; + socktype->geometry_nodes_cpp_type = socktype->base_cpp_type; socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value; return socktype; } @@ -849,11 +839,23 @@ static bNodeSocketType *make_socket_type_collection() static bNodeSocketType *make_socket_type_texture() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_TEXTURE, PROP_NONE); - socktype->get_base_cpp_type = []() { return &blender::fn::CPPType::get<Tex *>(); }; + socktype->base_cpp_type = &blender::fn::CPPType::get<Tex *>(); socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { *(Tex **)r_value = ((bNodeSocketValueTexture *)socket.default_value)->value; }; - socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type; + socktype->geometry_nodes_cpp_type = socktype->base_cpp_type; + socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value; + return socktype; +} + +static bNodeSocketType *make_socket_type_image() +{ + bNodeSocketType *socktype = make_standard_socket_type(SOCK_IMAGE, PROP_NONE); + socktype->base_cpp_type = &blender::fn::CPPType::get<Image *>(); + socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { + *(Image **)r_value = ((bNodeSocketValueImage *)socket.default_value)->value; + }; + socktype->geometry_nodes_cpp_type = socktype->base_cpp_type; socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value; return socktype; } @@ -861,18 +863,18 @@ static bNodeSocketType *make_socket_type_texture() static bNodeSocketType *make_socket_type_material() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_MATERIAL, PROP_NONE); - socktype->get_base_cpp_type = []() { return &blender::fn::CPPType::get<Material *>(); }; + socktype->base_cpp_type = &blender::fn::CPPType::get<Material *>(); socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { *(Material **)r_value = ((bNodeSocketValueMaterial *)socket.default_value)->value; }; - socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type; + socktype->geometry_nodes_cpp_type = socktype->base_cpp_type; socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value; return socktype; } -void register_standard_node_socket_types(void) +void register_standard_node_socket_types() { - /* draw callbacks are set in drawnode.c to avoid bad-level calls */ + /* Draw callbacks are set in `drawnode.c` to avoid bad-level calls. */ nodeRegisterSocketType(make_socket_type_float(PROP_NONE)); nodeRegisterSocketType(make_socket_type_float(PROP_UNSIGNED)); @@ -906,14 +908,14 @@ void register_standard_node_socket_types(void) nodeRegisterSocketType(make_socket_type_object()); - nodeRegisterSocketType(make_standard_socket_type(SOCK_IMAGE, PROP_NONE)); - nodeRegisterSocketType(make_socket_type_geometry()); nodeRegisterSocketType(make_socket_type_collection()); nodeRegisterSocketType(make_socket_type_texture()); + nodeRegisterSocketType(make_socket_type_image()); + nodeRegisterSocketType(make_socket_type_material()); nodeRegisterSocketType(make_socket_type_virtual()); diff --git a/source/blender/nodes/intern/node_socket_declarations.cc b/source/blender/nodes/intern/node_socket_declarations.cc index 4b0dbad3cff..1e6b77a9620 100644 --- a/source/blender/nodes/intern/node_socket_declarations.cc +++ b/source/blender/nodes/intern/node_socket_declarations.cc @@ -15,6 +15,7 @@ */ #include "NOD_socket_declarations.hh" +#include "NOD_socket_declarations_geometry.hh" #include "BKE_node.h" @@ -22,6 +23,52 @@ namespace blender::nodes::decl { +/** + * \note This function only deals with declarations, not the field status of existing nodes. If the + * field status of existing nodes was stored on the sockets, an improvement would be to check the + * existing socket's current status instead of the declaration. + */ +static bool field_types_are_compatible(const SocketDeclaration &input, + const SocketDeclaration &output) +{ + if (output.output_field_dependency().field_type() == OutputSocketFieldType::FieldSource) { + if (input.input_field_type() == InputSocketFieldType::None) { + return false; + } + } + return true; +} + +static bool sockets_can_connect(const SocketDeclaration &socket_decl, + const bNodeSocket &other_socket) +{ + /* Input sockets cannot connect to input sockets, outputs cannot connect to outputs. */ + if (socket_decl.in_out() == other_socket.in_out) { + return false; + } + + if (other_socket.declaration) { + if (socket_decl.in_out() == SOCK_IN) { + if (!field_types_are_compatible(socket_decl, *other_socket.declaration)) { + return false; + } + } + else { + if (!field_types_are_compatible(*other_socket.declaration, socket_decl)) { + return false; + } + } + } + + return true; +} + +static bool basic_types_can_connect(const SocketDeclaration &UNUSED(socket_decl), + const bNodeSocket &other_socket) +{ + return ELEM(other_socket.type, SOCK_FLOAT, SOCK_INT, SOCK_BOOLEAN, SOCK_VECTOR, SOCK_RGBA); +} + static void modify_subtype_except_for_storage(bNodeSocket &socket, int new_subtype) { const char *idname = nodeStaticSocketType(socket.type, new_subtype); @@ -30,14 +77,14 @@ static void modify_subtype_except_for_storage(bNodeSocket &socket, int new_subty socket.typeinfo = socktype; } -/* -------------------------------------------------------------------- - * Float. - */ +/* -------------------------------------------------------------------- */ +/** \name #Float + * \{ */ -bNodeSocket &Float::build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const +bNodeSocket &Float::build(bNodeTree &ntree, bNode &node) const { bNodeSocket &socket = *nodeAddStaticSocket( - &ntree, &node, in_out, SOCK_FLOAT, subtype_, identifier_.c_str(), name_.c_str()); + &ntree, &node, in_out_, SOCK_FLOAT, subtype_, identifier_.c_str(), name_.c_str()); this->set_common_flags(socket); bNodeSocketValueFloat &value = *(bNodeSocketValueFloat *)socket.default_value; value.min = soft_min_value_; @@ -67,10 +114,19 @@ bool Float::matches(const bNodeSocket &socket) const return true; } +bool Float::can_connect(const bNodeSocket &socket) const +{ + if (!sockets_can_connect(*this, socket)) { + return false; + } + return basic_types_can_connect(*this, socket); +} + bNodeSocket &Float::update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &socket) const { if (socket.type != SOCK_FLOAT) { - return this->build(ntree, node, (eNodeSocketInOut)socket.in_out); + BLI_assert(socket.in_out == in_out_); + return this->build(ntree, node); } if (socket.typeinfo->subtype != subtype_) { modify_subtype_except_for_storage(socket, subtype_); @@ -83,14 +139,16 @@ bNodeSocket &Float::update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket & return socket; } -/* -------------------------------------------------------------------- - * Int. - */ +/** \} */ -bNodeSocket &Int::build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const +/* -------------------------------------------------------------------- */ +/** \name #Int + * \{ */ + +bNodeSocket &Int::build(bNodeTree &ntree, bNode &node) const { bNodeSocket &socket = *nodeAddStaticSocket( - &ntree, &node, in_out, SOCK_INT, subtype_, identifier_.c_str(), name_.c_str()); + &ntree, &node, in_out_, SOCK_INT, subtype_, identifier_.c_str(), name_.c_str()); this->set_common_flags(socket); bNodeSocketValueInt &value = *(bNodeSocketValueInt *)socket.default_value; value.min = soft_min_value_; @@ -120,10 +178,19 @@ bool Int::matches(const bNodeSocket &socket) const return true; } +bool Int::can_connect(const bNodeSocket &socket) const +{ + if (!sockets_can_connect(*this, socket)) { + return false; + } + return basic_types_can_connect(*this, socket); +} + bNodeSocket &Int::update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &socket) const { if (socket.type != SOCK_INT) { - return this->build(ntree, node, (eNodeSocketInOut)socket.in_out); + BLI_assert(socket.in_out == in_out_); + return this->build(ntree, node); } if (socket.typeinfo->subtype != subtype_) { modify_subtype_except_for_storage(socket, subtype_); @@ -136,14 +203,16 @@ bNodeSocket &Int::update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &so return socket; } -/* -------------------------------------------------------------------- - * Vector. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #Vector + * \{ */ -bNodeSocket &Vector::build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const +bNodeSocket &Vector::build(bNodeTree &ntree, bNode &node) const { bNodeSocket &socket = *nodeAddStaticSocket( - &ntree, &node, in_out, SOCK_VECTOR, subtype_, identifier_.c_str(), name_.c_str()); + &ntree, &node, in_out_, SOCK_VECTOR, subtype_, identifier_.c_str(), name_.c_str()); this->set_common_flags(socket); bNodeSocketValueVector &value = *(bNodeSocketValueVector *)socket.default_value; copy_v3_v3(value.value, default_value_); @@ -166,10 +235,19 @@ bool Vector::matches(const bNodeSocket &socket) const return true; } +bool Vector::can_connect(const bNodeSocket &socket) const +{ + if (!sockets_can_connect(*this, socket)) { + return false; + } + return basic_types_can_connect(*this, socket); +} + bNodeSocket &Vector::update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &socket) const { if (socket.type != SOCK_VECTOR) { - return this->build(ntree, node, (eNodeSocketInOut)socket.in_out); + BLI_assert(socket.in_out == in_out_); + return this->build(ntree, node); } if (socket.typeinfo->subtype != subtype_) { modify_subtype_except_for_storage(socket, subtype_); @@ -181,14 +259,16 @@ bNodeSocket &Vector::update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket return socket; } -/* -------------------------------------------------------------------- - * Bool. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #Bool + * \{ */ -bNodeSocket &Bool::build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const +bNodeSocket &Bool::build(bNodeTree &ntree, bNode &node) const { bNodeSocket &socket = *nodeAddStaticSocket( - &ntree, &node, in_out, SOCK_BOOLEAN, PROP_NONE, identifier_.c_str(), name_.c_str()); + &ntree, &node, in_out_, SOCK_BOOLEAN, PROP_NONE, identifier_.c_str(), name_.c_str()); this->set_common_flags(socket); bNodeSocketValueBoolean &value = *(bNodeSocketValueBoolean *)socket.default_value; value.value = default_value_; @@ -206,14 +286,24 @@ bool Bool::matches(const bNodeSocket &socket) const return true; } -/* -------------------------------------------------------------------- - * Color. - */ +bool Bool::can_connect(const bNodeSocket &socket) const +{ + if (!sockets_can_connect(*this, socket)) { + return false; + } + return basic_types_can_connect(*this, socket); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #Color + * \{ */ -bNodeSocket &Color::build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const +bNodeSocket &Color::build(bNodeTree &ntree, bNode &node) const { bNodeSocket &socket = *nodeAddStaticSocket( - &ntree, &node, in_out, SOCK_RGBA, PROP_NONE, identifier_.c_str(), name_.c_str()); + &ntree, &node, in_out_, SOCK_RGBA, PROP_NONE, identifier_.c_str(), name_.c_str()); this->set_common_flags(socket); bNodeSocketValueRGBA &value = *(bNodeSocketValueRGBA *)socket.default_value; copy_v4_v4(value.value, default_value_); @@ -236,14 +326,25 @@ bool Color::matches(const bNodeSocket &socket) const return true; } -/* -------------------------------------------------------------------- - * String. - */ +bool Color::can_connect(const bNodeSocket &socket) const +{ + if (!sockets_can_connect(*this, socket)) { + return false; + } + return basic_types_can_connect(*this, socket); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #String + * \{ */ -bNodeSocket &String::build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const +bNodeSocket &String::build(bNodeTree &ntree, bNode &node) const { bNodeSocket &socket = *nodeAddStaticSocket( - &ntree, &node, in_out, SOCK_STRING, PROP_NONE, identifier_.c_str(), name_.c_str()); + &ntree, &node, in_out_, SOCK_STRING, PROP_NONE, identifier_.c_str(), name_.c_str()); + STRNCPY(((bNodeSocketValueString *)socket.default_value)->value, default_value_.c_str()); this->set_common_flags(socket); return socket; } @@ -259,16 +360,21 @@ bool String::matches(const bNodeSocket &socket) const return true; } -/* -------------------------------------------------------------------- - * IDSocketDeclaration. - */ +bool String::can_connect(const bNodeSocket &socket) const +{ + return sockets_can_connect(*this, socket) && socket.type == SOCK_STRING; +} + +/** \} */ -bNodeSocket &IDSocketDeclaration::build(bNodeTree &ntree, - bNode &node, - eNodeSocketInOut in_out) const +/* -------------------------------------------------------------------- */ +/** \name #IDSocketDeclaration + * \{ */ + +bNodeSocket &IDSocketDeclaration::build(bNodeTree &ntree, bNode &node) const { bNodeSocket &socket = *nodeAddSocket( - &ntree, &node, in_out, idname_, identifier_.c_str(), name_.c_str()); + &ntree, &node, in_out_, idname_, identifier_.c_str(), name_.c_str()); this->set_common_flags(socket); return socket; } @@ -284,25 +390,33 @@ bool IDSocketDeclaration::matches(const bNodeSocket &socket) const return true; } +bool IDSocketDeclaration::can_connect(const bNodeSocket &socket) const +{ + return sockets_can_connect(*this, socket) && STREQ(socket.idname, idname_); +} + bNodeSocket &IDSocketDeclaration::update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &socket) const { if (StringRef(socket.idname) != idname_) { - return this->build(ntree, node, (eNodeSocketInOut)socket.in_out); + BLI_assert(socket.in_out == in_out_); + return this->build(ntree, node); } this->set_common_flags(socket); return socket; } -/* -------------------------------------------------------------------- - * Geometry. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #Geometry + * \{ */ -bNodeSocket &Geometry::build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const +bNodeSocket &Geometry::build(bNodeTree &ntree, bNode &node) const { bNodeSocket &socket = *nodeAddSocket( - &ntree, &node, in_out, "NodeSocketGeometry", identifier_.c_str(), name_.c_str()); + &ntree, &node, in_out_, "NodeSocketGeometry", identifier_.c_str(), name_.c_str()); this->set_common_flags(socket); return socket; } @@ -318,4 +432,89 @@ bool Geometry::matches(const bNodeSocket &socket) const return true; } +bool Geometry::can_connect(const bNodeSocket &socket) const +{ + return sockets_can_connect(*this, socket) && socket.type == SOCK_GEOMETRY; +} + +Span<GeometryComponentType> Geometry::supported_types() const +{ + return supported_types_; +} + +bool Geometry::only_realized_data() const +{ + return only_realized_data_; +} + +bool Geometry::only_instances() const +{ + return only_instances_; +} + +GeometryBuilder &GeometryBuilder::supported_type(GeometryComponentType supported_type) +{ + decl_->supported_types_ = {supported_type}; + return *this; +} + +GeometryBuilder &GeometryBuilder::supported_type( + blender::Vector<GeometryComponentType> supported_types) +{ + decl_->supported_types_ = std::move(supported_types); + return *this; +} + +GeometryBuilder &GeometryBuilder::only_realized_data(bool value) +{ + decl_->only_realized_data_ = value; + return *this; +} + +GeometryBuilder &GeometryBuilder::only_instances(bool value) +{ + decl_->only_instances_ = value; + return *this; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #Shader + * \{ */ + +bNodeSocket &Shader::build(bNodeTree &ntree, bNode &node) const +{ + bNodeSocket &socket = *nodeAddSocket( + &ntree, &node, in_out_, "NodeSocketShader", identifier_.c_str(), name_.c_str()); + this->set_common_flags(socket); + return socket; +} + +bool Shader::matches(const bNodeSocket &socket) const +{ + if (!this->matches_common_data(socket)) { + return false; + } + if (socket.type != SOCK_SHADER) { + return false; + } + return true; +} + +bool Shader::can_connect(const bNodeSocket &socket) const +{ + if (!sockets_can_connect(*this, socket)) { + return false; + } + /* Basic types can convert to shaders, but not the other way around. */ + if (in_out_ == SOCK_IN) { + return ELEM( + socket.type, SOCK_VECTOR, SOCK_RGBA, SOCK_FLOAT, SOCK_INT, SOCK_BOOLEAN, SOCK_SHADER); + } + return socket.type == SOCK_SHADER; +} + +/** \} */ + } // namespace blender::nodes::decl diff --git a/source/blender/nodes/intern/node_tree_ref.cc b/source/blender/nodes/intern/node_tree_ref.cc index 43c7fbd2599..bc78533d45c 100644 --- a/source/blender/nodes/intern/node_tree_ref.cc +++ b/source/blender/nodes/intern/node_tree_ref.cc @@ -70,6 +70,8 @@ NodeTreeRef::NodeTreeRef(bNodeTree *btree) : btree_(btree) break; } } + BLI_assert(internal_link.from_ != nullptr); + BLI_assert(internal_link.to_ != nullptr); node.internal_links_.append(&internal_link); } @@ -115,6 +117,22 @@ NodeTreeRef::NodeTreeRef(bNodeTree *btree) : btree_(btree) 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() @@ -262,7 +280,6 @@ void InputSocketRef::foreach_logical_origin( skipped_fn.call_safe(origin); skipped_fn.call_safe(mute_input); mute_input.foreach_logical_origin(origin_fn, skipped_fn, true, seen_sockets_stack); - break; } } } @@ -442,9 +459,6 @@ static bool has_link_cycles_recursive(const NodeRef &node, return false; } -/** - * \return True when there is a link cycle. Unavailable sockets are ignored. - */ bool NodeTreeRef::has_link_cycles() const { const int node_amount = nodes_by_id_.size(); @@ -502,11 +516,15 @@ bool NodeRef::any_socket_is_directly_linked(eNodeSocketInOut in_out) const return this->any_output_is_directly_linked(); } -/** - * Sort nodes topologically from left to right or right to left. - * In the future the result if this could be cached on #NodeTreeRef. - */ -Vector<const NodeRef *> NodeTreeRef::toposort(const ToposortDirection direction) const +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; @@ -516,64 +534,103 @@ Vector<const NodeRef *> NodeTreeRef::toposort(const ToposortDirection direction) int link_index = 0; }; - Vector<const NodeRef *> toposort; - toposort.reserve(nodes_by_id_.size()); - Array<bool> node_is_done_by_id(nodes_by_id_.size(), false); - Stack<Item> nodes_to_check; + /* 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; + } - for (const NodeRef *start_node : nodes_by_id_) { - if (node_is_done_by_id[start_node->id()]) { + /* 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 (start_node->any_socket_is_directly_linked( + if (node->any_socket_is_directly_linked( direction == ToposortDirection::LeftToRight ? SOCK_OUT : SOCK_IN)) { /* Ignore non-start nodes. */ continue; } - /* Do a depth-first search to sort nodes topologically. */ - 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 == 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(); - if (node_is_done_by_id[linked_node.id()]) { - /* The linked node has already been visited. */ - item.link_index++; - continue; - } - nodes_to_check.push({&linked_node}); - break; - } + toposort_from_start_node(direction, *node, node_states, result); + } - /* If no other element has been pushed, the current node can be pushed to the sorted list. */ - if (&item == &nodes_to_check.peek()) { - node_is_done_by_id[node.id()] = true; - toposort.append(&node); - nodes_to_check.pop(); + /* 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); } } - return toposort; + 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 diff --git a/source/blender/nodes/intern/node_util.c b/source/blender/nodes/intern/node_util.c index 1aec280fd2b..5c2d84cf605 100644 --- a/source/blender/nodes/intern/node_util.c +++ b/source/blender/nodes/intern/node_util.c @@ -35,12 +35,15 @@ #include "BKE_colortools.h" #include "BKE_node.h" +#include "BKE_node_tree_update.h" #include "RNA_access.h" #include "RNA_enum_types.h" #include "MEM_guardedalloc.h" +#include "NOD_common.h" + #include "node_util.h" /* -------------------------------------------------------------------- */ @@ -97,12 +100,13 @@ void node_sock_label_clear(bNodeSocket *sock) } } -void node_math_update(bNodeTree *UNUSED(ntree), bNode *node) +void node_math_update(bNodeTree *ntree, bNode *node) { bNodeSocket *sock1 = BLI_findlink(&node->inputs, 0); bNodeSocket *sock2 = BLI_findlink(&node->inputs, 1); bNodeSocket *sock3 = BLI_findlink(&node->inputs, 2); - nodeSetSocketAvailability(sock2, + nodeSetSocketAvailability(ntree, + sock2, !ELEM(node->custom1, NODE_MATH_SQRT, NODE_MATH_SIGN, @@ -126,7 +130,8 @@ void node_math_update(bNodeTree *UNUSED(ntree), bNode *node) NODE_MATH_COSH, NODE_MATH_SINH, NODE_MATH_TANH)); - nodeSetSocketAvailability(sock3, + nodeSetSocketAvailability(ntree, + sock3, ELEM(node->custom1, NODE_MATH_COMPARE, NODE_MATH_MULTIPLY_ADD, @@ -186,7 +191,7 @@ void node_math_update(bNodeTree *UNUSED(ntree), bNode *node) /** \name Labels * \{ */ -void node_blend_label(bNodeTree *UNUSED(ntree), bNode *node, char *label, int maxlen) +void node_blend_label(const bNodeTree *UNUSED(ntree), const bNode *node, char *label, int maxlen) { const char *name; bool enum_label = RNA_enum_name(rna_enum_ramp_blend_items, node->custom1, &name); @@ -196,14 +201,14 @@ void node_blend_label(bNodeTree *UNUSED(ntree), bNode *node, char *label, int ma BLI_strncpy(label, IFACE_(name), maxlen); } -void node_image_label(bNodeTree *UNUSED(ntree), bNode *node, char *label, int maxlen) +void node_image_label(const bNodeTree *UNUSED(ntree), const bNode *node, char *label, int maxlen) { /* If there is no loaded image, return an empty string, * and let nodeLabel() fill in the proper type translation. */ BLI_strncpy(label, (node->id) ? node->id->name + 2 : "", maxlen); } -void node_math_label(bNodeTree *UNUSED(ntree), bNode *node, char *label, int maxlen) +void node_math_label(const bNodeTree *UNUSED(ntree), const bNode *node, char *label, int maxlen) { const char *name; bool enum_label = RNA_enum_name(rna_enum_node_math_items, node->custom1, &name); @@ -213,7 +218,10 @@ void node_math_label(bNodeTree *UNUSED(ntree), bNode *node, char *label, int max BLI_strncpy(label, IFACE_(name), maxlen); } -void node_vector_math_label(bNodeTree *UNUSED(ntree), bNode *node, char *label, int maxlen) +void node_vector_math_label(const bNodeTree *UNUSED(ntree), + const bNode *node, + char *label, + int maxlen) { const char *name; bool enum_label = RNA_enum_name(rna_enum_node_vec_math_items, node->custom1, &name); @@ -223,7 +231,7 @@ void node_vector_math_label(bNodeTree *UNUSED(ntree), bNode *node, char *label, BLI_strncpy(label, IFACE_(name), maxlen); } -void node_filter_label(bNodeTree *UNUSED(ntree), bNode *node, char *label, int maxlen) +void node_filter_label(const bNodeTree *UNUSED(ntree), const bNode *node, char *label, int maxlen) { const char *name; bool enum_label = RNA_enum_name(rna_enum_node_filter_items, node->custom1, &name); @@ -297,11 +305,6 @@ static bNodeSocket *node_find_linkable_socket(bNodeTree *ntree, return NULL; } -/** - * The idea behind this is: When a user connects an input to a socket that is - * already linked (and if its not an Multi Input Socket), we try to find a replacement socket for - * the link that we try to overwrite and connect that previous link to the new socket. - */ void node_insert_link_default(bNodeTree *ntree, bNode *node, bNodeLink *link) { bNodeSocket *socket = link->tosock; @@ -338,237 +341,6 @@ void node_insert_link_default(bNodeTree *ntree, bNode *node, bNodeLink *link) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Internal Links (mute and disconnect) - * \{ */ - -/** - * Common datatype priorities, works for compositor, shader and texture nodes alike - * defines priority of datatype connection based on output type (to): - * `< 0`: never connect these types. - * `>= 0`: priority of connection (higher values chosen first). - */ -static int node_datatype_priority(eNodeSocketDatatype from, eNodeSocketDatatype to) -{ - switch (to) { - case SOCK_RGBA: - switch (from) { - case SOCK_RGBA: - return 4; - case SOCK_FLOAT: - return 3; - case SOCK_INT: - return 2; - case SOCK_BOOLEAN: - return 1; - default: - return -1; - } - case SOCK_VECTOR: - switch (from) { - case SOCK_VECTOR: - return 4; - case SOCK_FLOAT: - return 3; - case SOCK_INT: - return 2; - case SOCK_BOOLEAN: - return 1; - default: - return -1; - } - case SOCK_FLOAT: - switch (from) { - case SOCK_FLOAT: - return 5; - case SOCK_INT: - return 4; - case SOCK_BOOLEAN: - return 3; - case SOCK_RGBA: - return 2; - case SOCK_VECTOR: - return 1; - default: - return -1; - } - case SOCK_INT: - switch (from) { - case SOCK_INT: - return 5; - case SOCK_FLOAT: - return 4; - case SOCK_BOOLEAN: - return 3; - case SOCK_RGBA: - return 2; - case SOCK_VECTOR: - return 1; - default: - return -1; - } - case SOCK_BOOLEAN: - switch (from) { - case SOCK_BOOLEAN: - return 5; - case SOCK_INT: - return 4; - case SOCK_FLOAT: - return 3; - case SOCK_RGBA: - return 2; - case SOCK_VECTOR: - return 1; - default: - return -1; - } - case SOCK_SHADER: - switch (from) { - case SOCK_SHADER: - return 1; - default: - return -1; - } - case SOCK_STRING: - switch (from) { - case SOCK_STRING: - return 1; - default: - return -1; - } - case SOCK_OBJECT: { - switch (from) { - case SOCK_OBJECT: - return 1; - default: - return -1; - } - } - case SOCK_GEOMETRY: { - switch (from) { - case SOCK_GEOMETRY: - return 1; - default: - return -1; - } - } - case SOCK_COLLECTION: { - switch (from) { - case SOCK_COLLECTION: - return 1; - default: - return -1; - } - } - case SOCK_TEXTURE: { - switch (from) { - case SOCK_TEXTURE: - return 1; - default: - return -1; - } - } - case SOCK_MATERIAL: { - switch (from) { - case SOCK_MATERIAL: - return 1; - default: - return -1; - } - } - default: - return -1; - } -} - -/* select a suitable input socket for an output */ -static bNodeSocket *select_internal_link_input(bNode *node, bNodeSocket *output) -{ - bNodeSocket *selected = NULL, *input; - int i; - int sel_priority = -1; - bool sel_is_linked = false; - - for (input = node->inputs.first, i = 0; input; input = input->next, i++) { - int priority = node_datatype_priority(input->type, output->type); - bool is_linked = (input->link != NULL); - bool preferred; - - if (nodeSocketIsHidden(input) || /* ignore hidden sockets */ - input->flag & - SOCK_NO_INTERNAL_LINK || /* ignore if input is not allowed for internal connections */ - priority < 0 || /* ignore incompatible types */ - priority < sel_priority) /* ignore if we already found a higher priority input */ - { - continue; - } - - /* determine if this input is preferred over the currently selected */ - preferred = (priority > sel_priority) || /* prefer higher datatype priority */ - (is_linked && !sel_is_linked); /* prefer linked over unlinked */ - - if (preferred) { - selected = input; - sel_is_linked = is_linked; - sel_priority = priority; - } - } - - return selected; -} - -void node_update_internal_links_default(bNodeTree *ntree, bNode *node) -{ - bNodeLink *link; - bNodeSocket *output, *input; - - /* sanity check */ - if (!ntree) { - return; - } - - /* use link pointer as a tag for handled sockets (for outputs is unused anyway) */ - for (output = node->outputs.first; output; output = output->next) { - output->link = NULL; - } - - for (link = ntree->links.first; link; link = link->next) { - if (nodeLinkIsHidden(link)) { - continue; - } - - output = link->fromsock; - if (link->fromnode != node || output->link) { - continue; - } - if (nodeSocketIsHidden(output) || output->flag & SOCK_NO_INTERNAL_LINK) { - continue; - } - output->link = link; /* not really used, just for tagging handled sockets */ - - /* look for suitable input */ - input = select_internal_link_input(node, output); - - if (input) { - bNodeLink *ilink = MEM_callocN(sizeof(bNodeLink), "internal node link"); - ilink->fromnode = node; - ilink->fromsock = input; - ilink->tonode = node; - ilink->tosock = output; - /* internal link is always valid */ - ilink->flag |= NODE_LINK_VALID; - BLI_addtail(&node->internal_links, ilink); - } - } - - /* clean up */ - for (output = node->outputs.first; output; output = output->next) { - output->link = NULL; - } -} - -/** \} */ - -/* -------------------------------------------------------------------- */ /** \name Default value RNA access * \{ */ diff --git a/source/blender/nodes/intern/node_util.h b/source/blender/nodes/intern/node_util.h index 9cbb21e02f7..d7da2303088 100644 --- a/source/blender/nodes/intern/node_util.h +++ b/source/blender/nodes/intern/node_util.h @@ -23,20 +23,6 @@ #pragma once -#include "DNA_listBase.h" - -#include "BLI_utildefines.h" - -#include "BKE_node.h" - -#include "MEM_guardedalloc.h" - -#include "NOD_socket.h" - -#include "GPU_material.h" /* For Shader muting GPU code... */ - -#include "RNA_access.h" - #ifdef __cplusplus extern "C" { #endif @@ -56,18 +42,18 @@ typedef struct bNodeExecData { /**** Storage Data ****/ -extern void node_free_curves(struct bNode *node); -extern void node_free_standard_storage(struct bNode *node); +void node_free_curves(struct bNode *node); +void node_free_standard_storage(struct bNode *node); -extern void node_copy_curves(struct bNodeTree *dest_ntree, - struct bNode *dest_node, - const struct bNode *src_node); -extern void node_copy_standard_storage(struct bNodeTree *dest_ntree, - struct bNode *dest_node, - const struct bNode *src_node); -extern void *node_initexec_curves(struct bNodeExecContext *context, - struct bNode *node, - bNodeInstanceKey key); +void node_copy_curves(struct bNodeTree *dest_ntree, + struct bNode *dest_node, + const struct bNode *src_node); +void node_copy_standard_storage(struct bNodeTree *dest_ntree, + struct bNode *dest_node, + const struct bNode *src_node); +void *node_initexec_curves(struct bNodeExecContext *context, + struct bNode *node, + bNodeInstanceKey key); /**** Updates ****/ void node_sock_label(struct bNodeSocket *sock, const char *name); @@ -75,15 +61,35 @@ void node_sock_label_clear(struct bNodeSocket *sock); void node_math_update(struct bNodeTree *ntree, struct bNode *node); /**** Labels ****/ -void node_blend_label(struct bNodeTree *ntree, struct bNode *node, char *label, int maxlen); -void node_image_label(struct bNodeTree *ntree, struct bNode *node, char *label, int maxlen); -void node_math_label(struct bNodeTree *ntree, struct bNode *node, char *label, int maxlen); -void node_vector_math_label(struct bNodeTree *ntree, struct bNode *node, char *label, int maxlen); -void node_filter_label(struct bNodeTree *ntree, struct bNode *node, char *label, int maxlen); +void node_blend_label(const struct bNodeTree *ntree, + const struct bNode *node, + char *label, + int maxlen); +void node_image_label(const struct bNodeTree *ntree, + const struct bNode *node, + char *label, + int maxlen); +void node_math_label(const struct bNodeTree *ntree, + const struct bNode *node, + char *label, + int maxlen); +void node_vector_math_label(const struct bNodeTree *ntree, + const struct bNode *node, + char *label, + int maxlen); +void node_filter_label(const struct bNodeTree *ntree, + const struct bNode *node, + char *label, + int maxlen); /*** Link Handling */ + +/** + * The idea behind this is: When a user connects an input to a socket that is + * already linked (and if its not an Multi Input Socket), we try to find a replacement socket for + * the link that we try to overwrite and connect that previous link to the new socket. + */ void node_insert_link_default(struct bNodeTree *ntree, struct bNode *node, struct bNodeLink *link); -void node_update_internal_links_default(struct bNodeTree *ntree, struct bNode *node); float node_socket_get_float(struct bNodeTree *ntree, struct bNode *node, struct bNodeSocket *sock); void node_socket_set_float(struct bNodeTree *ntree, diff --git a/source/blender/nodes/intern/socket_search_link.cc b/source/blender/nodes/intern/socket_search_link.cc new file mode 100644 index 00000000000..8543efe7f9b --- /dev/null +++ b/source/blender/nodes/intern/socket_search_link.cc @@ -0,0 +1,199 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_set.hh" + +#include "BKE_node.h" + +#include "UI_interface.h" + +#include "NOD_node_declaration.hh" +#include "NOD_socket_search_link.hh" + +namespace blender::nodes { + +void GatherLinkSearchOpParams::add_item(std::string socket_name, + SocketLinkOperation::LinkSocketFn fn, + const int weight) +{ + + std::string name = std::string(node_type_.ui_name) + " " + UI_MENU_ARROW_SEP + socket_name; + + items_.append({std::move(name), std::move(fn), weight}); +} + +const bNodeSocket &GatherLinkSearchOpParams::other_socket() const +{ + return other_socket_; +} + +const bNodeTree &GatherLinkSearchOpParams::node_tree() const +{ + return node_tree_; +} + +const bNodeType &GatherLinkSearchOpParams::node_type() const +{ + return node_type_; +} + +eNodeSocketInOut GatherLinkSearchOpParams::in_out() const +{ + return other_socket_.in_out == SOCK_IN ? SOCK_OUT : SOCK_IN; +} + +void LinkSearchOpParams::connect_available_socket(bNode &new_node, StringRef socket_name) +{ + const eNodeSocketInOut in_out = socket.in_out == SOCK_IN ? SOCK_OUT : SOCK_IN; + bNodeSocket *new_node_socket = bke::node_find_enabled_socket(new_node, in_out, socket_name); + if (new_node_socket == nullptr) { + /* If the socket isn't found, some node's search gather functions probably aren't configured + * properly. It's likely enough that it's worth avoiding a crash in a release build though. */ + BLI_assert_unreachable(); + return; + } + nodeAddLink(&node_tree, &new_node, new_node_socket, &node, &socket); +} + +bNode &LinkSearchOpParams::add_node(StringRef idname) +{ + std::string idname_str = idname; + bNode *node = nodeAddNode(&C, &node_tree, idname_str.c_str()); + BLI_assert(node != nullptr); + added_nodes_.append(node); + return *node; +} + +bNode &LinkSearchOpParams::add_node(const bNodeType &node_type) +{ + return this->add_node(node_type.idname); +} + +void LinkSearchOpParams::update_and_connect_available_socket(bNode &new_node, + StringRef socket_name) +{ + if (new_node.typeinfo->updatefunc) { + new_node.typeinfo->updatefunc(&node_tree, &new_node); + } + this->connect_available_socket(new_node, socket_name); +} + +void search_link_ops_for_declarations(GatherLinkSearchOpParams ¶ms, + Span<SocketDeclarationPtr> declarations) +{ + const bNodeType &node_type = params.node_type(); + + const SocketDeclaration *main_socket = nullptr; + Vector<const SocketDeclaration *> connectable_sockets; + + Set<StringRef> socket_names; + for (const int i : declarations.index_range()) { + const SocketDeclaration &socket = *declarations[i]; + if (!socket_names.add(socket.name())) { + /* Don't add sockets with the same name to the search. Needed to support being called from + * #search_link_ops_for_basic_node, which should have "okay" behavior for nodes with + * duplicate socket names. */ + continue; + } + if (!socket.can_connect(params.other_socket())) { + continue; + } + if (socket.is_default_link_socket() || main_socket == nullptr) { + /* Either the first connectable or explicitly tagged socket is the main socket. */ + main_socket = &socket; + } + connectable_sockets.append(&socket); + } + for (const int i : connectable_sockets.index_range()) { + const SocketDeclaration &socket = *connectable_sockets[i]; + /* Give non-main sockets a lower weight so that they don't show up at the top of the search + * when they are not explicitly searched for. The -1 is used to make sure that the first socket + * has a smaller weight than zero so that it does not have the same weight as the main socket. + * Negative weights are used to avoid making the highest weight dependent on the number of + * sockets. */ + const int weight = (&socket == main_socket) ? 0 : -1 - i; + params.add_item( + socket.name(), + [&node_type, &socket](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node(node_type); + socket.make_available(node); + params.update_and_connect_available_socket(node, socket.name()); + }, + weight); + } +} + +static void search_link_ops_for_socket_templates(GatherLinkSearchOpParams ¶ms, + const bNodeSocketTemplate *templates, + const eNodeSocketInOut in_out) +{ + const bNodeType &node_type = params.node_type(); + const bNodeTreeType &node_tree_type = *params.node_tree().typeinfo; + + Set<StringRef> socket_names; + for (const bNodeSocketTemplate *socket_template = templates; socket_template->type != -1; + socket_template++) { + eNodeSocketDatatype from = (eNodeSocketDatatype)socket_template->type; + eNodeSocketDatatype to = (eNodeSocketDatatype)params.other_socket().type; + if (in_out == SOCK_IN) { + std::swap(from, to); + } + if (node_tree_type.validate_link && !node_tree_type.validate_link(from, to)) { + continue; + } + if (!socket_names.add(socket_template->name)) { + /* See comment in #search_link_ops_for_declarations. */ + continue; + } + + params.add_item( + socket_template->name, [socket_template, node_type, in_out](LinkSearchOpParams ¶ms) { + bNode &node = params.add_node(node_type); + bNodeSocket *new_node_socket = bke::node_find_enabled_socket( + node, in_out, socket_template->name); + if (new_node_socket != nullptr) { + /* Rely on the way #nodeAddLink switches in/out if necessary. */ + nodeAddLink(¶ms.node_tree, ¶ms.node, ¶ms.socket, &node, new_node_socket); + } + }); + } +} + +void search_link_ops_for_basic_node(GatherLinkSearchOpParams ¶ms) +{ + const bNodeType &node_type = params.node_type(); + + if (node_type.declare) { + if (node_type.declaration_is_dynamic) { + /* Dynamic declarations (whatever they end up being) aren't supported + * by this function, but still avoid a crash in release builds. */ + BLI_assert_unreachable(); + return; + } + + const NodeDeclaration &declaration = *node_type.fixed_declaration; + + search_link_ops_for_declarations(params, declaration.sockets(params.in_out())); + } + else if (node_type.inputs && params.in_out() == SOCK_IN) { + search_link_ops_for_socket_templates(params, node_type.inputs, SOCK_IN); + } + else if (node_type.outputs && params.in_out() == SOCK_OUT) { + search_link_ops_for_socket_templates(params, node_type.outputs, SOCK_OUT); + } +} + +} // namespace blender::nodes diff --git a/source/blender/nodes/intern/type_conversions.cc b/source/blender/nodes/intern/type_conversions.cc deleted file mode 100644 index 1a71a3418a5..00000000000 --- a/source/blender/nodes/intern/type_conversions.cc +++ /dev/null @@ -1,349 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include "NOD_type_conversions.hh" - -#include "FN_multi_function_builder.hh" - -#include "BLI_color.hh" -#include "BLI_float2.hh" -#include "BLI_float3.hh" - -namespace blender::nodes { - -using fn::GVArrayPtr; -using fn::GVMutableArray; -using fn::GVMutableArrayPtr; -using fn::MFDataType; - -template<typename From, typename To, To (*ConversionF)(const From &)> -static void add_implicit_conversion(DataTypeConversions &conversions) -{ - const CPPType &from_type = CPPType::get<From>(); - const CPPType &to_type = CPPType::get<To>(); - const std::string conversion_name = from_type.name() + " to " + to_type.name(); - - static fn::CustomMF_SI_SO<From, To> multi_function{conversion_name, ConversionF}; - static auto convert_single_to_initialized = [](const void *src, void *dst) { - *(To *)dst = ConversionF(*(const From *)src); - }; - static auto convert_single_to_uninitialized = [](const void *src, void *dst) { - new (dst) To(ConversionF(*(const From *)src)); - }; - conversions.add(fn::MFDataType::ForSingle<From>(), - fn::MFDataType::ForSingle<To>(), - multi_function, - convert_single_to_initialized, - convert_single_to_uninitialized); -} - -static float2 float_to_float2(const float &a) -{ - return float2(a); -} -static float3 float_to_float3(const float &a) -{ - return float3(a); -} -static int32_t float_to_int(const float &a) -{ - return (int32_t)a; -} -static bool float_to_bool(const float &a) -{ - return a > 0.0f; -} -static ColorGeometry4f float_to_color(const float &a) -{ - return ColorGeometry4f(a, a, a, 1.0f); -} - -static float3 float2_to_float3(const float2 &a) -{ - return float3(a.x, a.y, 0.0f); -} -static float float2_to_float(const float2 &a) -{ - return (a.x + a.y) / 2.0f; -} -static int float2_to_int(const float2 &a) -{ - return (int32_t)((a.x + a.y) / 2.0f); -} -static bool float2_to_bool(const float2 &a) -{ - return !is_zero_v2(a); -} -static ColorGeometry4f float2_to_color(const float2 &a) -{ - return ColorGeometry4f(a.x, a.y, 0.0f, 1.0f); -} - -static bool float3_to_bool(const float3 &a) -{ - return !is_zero_v3(a); -} -static float float3_to_float(const float3 &a) -{ - return (a.x + a.y + a.z) / 3.0f; -} -static int float3_to_int(const float3 &a) -{ - return (int)((a.x + a.y + a.z) / 3.0f); -} -static float2 float3_to_float2(const float3 &a) -{ - return float2(a); -} -static ColorGeometry4f float3_to_color(const float3 &a) -{ - return ColorGeometry4f(a.x, a.y, a.z, 1.0f); -} - -static bool int_to_bool(const int32_t &a) -{ - return a > 0; -} -static float int_to_float(const int32_t &a) -{ - return (float)a; -} -static float2 int_to_float2(const int32_t &a) -{ - return float2((float)a); -} -static float3 int_to_float3(const int32_t &a) -{ - return float3((float)a); -} -static ColorGeometry4f int_to_color(const int32_t &a) -{ - return ColorGeometry4f((float)a, (float)a, (float)a, 1.0f); -} - -static float bool_to_float(const bool &a) -{ - return (bool)a; -} -static int32_t bool_to_int(const bool &a) -{ - return (int32_t)a; -} -static float2 bool_to_float2(const bool &a) -{ - return (a) ? float2(1.0f) : float2(0.0f); -} -static float3 bool_to_float3(const bool &a) -{ - return (a) ? float3(1.0f) : float3(0.0f); -} -static ColorGeometry4f bool_to_color(const bool &a) -{ - return (a) ? ColorGeometry4f(1.0f, 1.0f, 1.0f, 1.0f) : ColorGeometry4f(0.0f, 0.0f, 0.0f, 1.0f); -} - -static bool color_to_bool(const ColorGeometry4f &a) -{ - return rgb_to_grayscale(a) > 0.0f; -} -static float color_to_float(const ColorGeometry4f &a) -{ - return rgb_to_grayscale(a); -} -static int32_t color_to_int(const ColorGeometry4f &a) -{ - return (int)rgb_to_grayscale(a); -} -static float2 color_to_float2(const ColorGeometry4f &a) -{ - return float2(a.r, a.g); -} -static float3 color_to_float3(const ColorGeometry4f &a) -{ - return float3(a.r, a.g, a.b); -} - -static DataTypeConversions create_implicit_conversions() -{ - DataTypeConversions conversions; - - add_implicit_conversion<float, float2, float_to_float2>(conversions); - add_implicit_conversion<float, float3, float_to_float3>(conversions); - add_implicit_conversion<float, int32_t, float_to_int>(conversions); - add_implicit_conversion<float, bool, float_to_bool>(conversions); - add_implicit_conversion<float, ColorGeometry4f, float_to_color>(conversions); - - add_implicit_conversion<float2, float3, float2_to_float3>(conversions); - add_implicit_conversion<float2, float, float2_to_float>(conversions); - add_implicit_conversion<float2, int32_t, float2_to_int>(conversions); - add_implicit_conversion<float2, bool, float2_to_bool>(conversions); - add_implicit_conversion<float2, ColorGeometry4f, float2_to_color>(conversions); - - add_implicit_conversion<float3, bool, float3_to_bool>(conversions); - add_implicit_conversion<float3, float, float3_to_float>(conversions); - add_implicit_conversion<float3, int32_t, float3_to_int>(conversions); - add_implicit_conversion<float3, float2, float3_to_float2>(conversions); - add_implicit_conversion<float3, ColorGeometry4f, float3_to_color>(conversions); - - add_implicit_conversion<int32_t, bool, int_to_bool>(conversions); - add_implicit_conversion<int32_t, float, int_to_float>(conversions); - add_implicit_conversion<int32_t, float2, int_to_float2>(conversions); - add_implicit_conversion<int32_t, float3, int_to_float3>(conversions); - add_implicit_conversion<int32_t, ColorGeometry4f, int_to_color>(conversions); - - add_implicit_conversion<bool, float, bool_to_float>(conversions); - add_implicit_conversion<bool, int32_t, bool_to_int>(conversions); - add_implicit_conversion<bool, float2, bool_to_float2>(conversions); - add_implicit_conversion<bool, float3, bool_to_float3>(conversions); - add_implicit_conversion<bool, ColorGeometry4f, bool_to_color>(conversions); - - add_implicit_conversion<ColorGeometry4f, bool, color_to_bool>(conversions); - add_implicit_conversion<ColorGeometry4f, float, color_to_float>(conversions); - add_implicit_conversion<ColorGeometry4f, int32_t, color_to_int>(conversions); - add_implicit_conversion<ColorGeometry4f, float2, color_to_float2>(conversions); - add_implicit_conversion<ColorGeometry4f, float3, color_to_float3>(conversions); - - return conversions; -} - -const DataTypeConversions &get_implicit_type_conversions() -{ - static const DataTypeConversions conversions = create_implicit_conversions(); - return conversions; -} - -void DataTypeConversions::convert_to_uninitialized(const CPPType &from_type, - const CPPType &to_type, - const void *from_value, - void *to_value) const -{ - if (from_type == to_type) { - from_type.copy_construct(from_value, to_value); - return; - } - - const ConversionFunctions *functions = this->get_conversion_functions( - MFDataType::ForSingle(from_type), MFDataType::ForSingle(to_type)); - BLI_assert(functions != nullptr); - - functions->convert_single_to_uninitialized(from_value, to_value); -} - -class GVArray_For_ConvertedGVArray : public GVArray { - private: - GVArrayPtr varray_; - const CPPType &from_type_; - ConversionFunctions old_to_new_conversions_; - - public: - GVArray_For_ConvertedGVArray(GVArrayPtr varray, - const CPPType &to_type, - const DataTypeConversions &conversions) - : GVArray(to_type, varray->size()), varray_(std::move(varray)), from_type_(varray_->type()) - { - old_to_new_conversions_ = *conversions.get_conversion_functions(from_type_, to_type); - } - - private: - void get_impl(const int64_t index, void *r_value) const override - { - BUFFER_FOR_CPP_TYPE_VALUE(from_type_, buffer); - varray_->get(index, buffer); - old_to_new_conversions_.convert_single_to_initialized(buffer, r_value); - from_type_.destruct(buffer); - } - - void get_to_uninitialized_impl(const int64_t index, void *r_value) const override - { - BUFFER_FOR_CPP_TYPE_VALUE(from_type_, buffer); - varray_->get(index, buffer); - old_to_new_conversions_.convert_single_to_uninitialized(buffer, r_value); - from_type_.destruct(buffer); - } -}; - -class GVMutableArray_For_ConvertedGVMutableArray : public GVMutableArray { - private: - GVMutableArrayPtr varray_; - const CPPType &from_type_; - ConversionFunctions old_to_new_conversions_; - ConversionFunctions new_to_old_conversions_; - - public: - GVMutableArray_For_ConvertedGVMutableArray(GVMutableArrayPtr varray, - const CPPType &to_type, - const DataTypeConversions &conversions) - : GVMutableArray(to_type, varray->size()), - varray_(std::move(varray)), - from_type_(varray_->type()) - { - old_to_new_conversions_ = *conversions.get_conversion_functions(from_type_, to_type); - new_to_old_conversions_ = *conversions.get_conversion_functions(to_type, from_type_); - } - - private: - void get_impl(const int64_t index, void *r_value) const override - { - BUFFER_FOR_CPP_TYPE_VALUE(from_type_, buffer); - varray_->get(index, buffer); - old_to_new_conversions_.convert_single_to_initialized(buffer, r_value); - from_type_.destruct(buffer); - } - - void get_to_uninitialized_impl(const int64_t index, void *r_value) const override - { - BUFFER_FOR_CPP_TYPE_VALUE(from_type_, buffer); - varray_->get(index, buffer); - old_to_new_conversions_.convert_single_to_uninitialized(buffer, r_value); - from_type_.destruct(buffer); - } - - void set_by_move_impl(const int64_t index, void *value) override - { - BUFFER_FOR_CPP_TYPE_VALUE(from_type_, buffer); - new_to_old_conversions_.convert_single_to_uninitialized(value, buffer); - varray_->set_by_relocate(index, buffer); - } -}; - -fn::GVArrayPtr DataTypeConversions::try_convert(fn::GVArrayPtr varray, - const CPPType &to_type) const -{ - const CPPType &from_type = varray->type(); - if (from_type == to_type) { - return varray; - } - if (!this->is_convertible(from_type, to_type)) { - return {}; - } - return std::make_unique<GVArray_For_ConvertedGVArray>(std::move(varray), to_type, *this); -} - -fn::GVMutableArrayPtr DataTypeConversions::try_convert(fn::GVMutableArrayPtr varray, - const CPPType &to_type) const -{ - const CPPType &from_type = varray->type(); - if (from_type == to_type) { - return varray; - } - if (!this->is_convertible(from_type, to_type)) { - return {}; - } - return std::make_unique<GVMutableArray_For_ConvertedGVMutableArray>( - std::move(varray), to_type, *this); -} - -} // namespace blender::nodes diff --git a/source/blender/nodes/shader/CMakeLists.txt b/source/blender/nodes/shader/CMakeLists.txt new file mode 100644 index 00000000000..c8eb0b8d570 --- /dev/null +++ b/source/blender/nodes/shader/CMakeLists.txt @@ -0,0 +1,174 @@ +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# The Original Code is Copyright (C) 2021, Blender Foundation +# All rights reserved. +# ***** END GPL LICENSE BLOCK ***** + +set(INC + . + .. + ../intern + ../../blenkernel + ../../blenlib + ../../blentranslation + ../../depsgraph + ../../editors/include + ../../functions + ../../gpu + ../../imbuf + ../../makesdna + ../../makesrna + ../../render + ../../windowmanager + ../../../../intern/guardedalloc + ../../../../intern/sky/include +) + + +set(SRC + nodes/node_shader_add_shader.cc + nodes/node_shader_ambient_occlusion.cc + nodes/node_shader_attribute.cc + nodes/node_shader_background.cc + nodes/node_shader_bevel.cc + nodes/node_shader_blackbody.cc + nodes/node_shader_brightness.cc + nodes/node_shader_bsdf_anisotropic.cc + nodes/node_shader_bsdf_diffuse.cc + nodes/node_shader_bsdf_glass.cc + nodes/node_shader_bsdf_glossy.cc + nodes/node_shader_bsdf_hair.cc + nodes/node_shader_bsdf_hair_principled.cc + nodes/node_shader_bsdf_principled.cc + nodes/node_shader_bsdf_refraction.cc + nodes/node_shader_bsdf_toon.cc + nodes/node_shader_bsdf_translucent.cc + nodes/node_shader_bsdf_transparent.cc + nodes/node_shader_bsdf_velvet.cc + nodes/node_shader_bump.cc + nodes/node_shader_camera.cc + nodes/node_shader_clamp.cc + nodes/node_shader_color_ramp.cc + nodes/node_shader_common.cc + nodes/node_shader_curves.cc + nodes/node_shader_displacement.cc + nodes/node_shader_eevee_specular.cc + nodes/node_shader_emission.cc + nodes/node_shader_fresnel.cc + nodes/node_shader_gamma.cc + nodes/node_shader_geometry.cc + nodes/node_shader_hair_info.cc + nodes/node_shader_holdout.cc + nodes/node_shader_hueSatVal.cc + nodes/node_shader_ies_light.cc + nodes/node_shader_invert.cc + nodes/node_shader_layer_weight.cc + nodes/node_shader_light_falloff.cc + nodes/node_shader_light_path.cc + nodes/node_shader_map_range.cc + nodes/node_shader_mapping.cc + nodes/node_shader_math.cc + nodes/node_shader_mix_rgb.cc + nodes/node_shader_mix_shader.cc + nodes/node_shader_normal.cc + nodes/node_shader_normal_map.cc + nodes/node_shader_object_info.cc + nodes/node_shader_output_aov.cc + nodes/node_shader_output_light.cc + nodes/node_shader_output_linestyle.cc + nodes/node_shader_output_material.cc + nodes/node_shader_output_world.cc + nodes/node_shader_particle_info.cc + nodes/node_shader_point_info.cc + nodes/node_shader_rgb.cc + nodes/node_shader_rgb_to_bw.cc + nodes/node_shader_script.cc + nodes/node_shader_sepcomb_hsv.cc + nodes/node_shader_sepcomb_rgb.cc + nodes/node_shader_sepcomb_xyz.cc + nodes/node_shader_shader_to_rgb.cc + nodes/node_shader_squeeze.cc + nodes/node_shader_subsurface_scattering.cc + nodes/node_shader_tangent.cc + nodes/node_shader_tex_brick.cc + nodes/node_shader_tex_checker.cc + nodes/node_shader_tex_coord.cc + nodes/node_shader_tex_environment.cc + nodes/node_shader_tex_gradient.cc + nodes/node_shader_tex_image.cc + nodes/node_shader_tex_magic.cc + nodes/node_shader_tex_musgrave.cc + nodes/node_shader_tex_noise.cc + nodes/node_shader_tex_pointdensity.cc + nodes/node_shader_tex_sky.cc + nodes/node_shader_tex_voronoi.cc + nodes/node_shader_tex_wave.cc + nodes/node_shader_tex_white_noise.cc + nodes/node_shader_uv_along_stroke.cc + nodes/node_shader_uvmap.cc + nodes/node_shader_value.cc + nodes/node_shader_vector_displacement.cc + nodes/node_shader_vector_math.cc + nodes/node_shader_vector_rotate.cc + nodes/node_shader_vector_transform.cc + nodes/node_shader_vertex_color.cc + nodes/node_shader_volume_absorption.cc + nodes/node_shader_volume_info.cc + nodes/node_shader_volume_principled.cc + nodes/node_shader_volume_scatter.cc + nodes/node_shader_wavelength.cc + nodes/node_shader_wireframe.cc + + node_shader_tree.cc + node_shader_util.cc + + node_shader_util.hh +) + +set(LIB + bf_functions + bf_intern_sky +) + +if(WITH_PYTHON) + list(APPEND INC + ../../python + ) + list(APPEND INC_SYS + ${PYTHON_INCLUDE_DIRS} + ) + list(APPEND LIB + ${PYTHON_LINKFLAGS} + ${PYTHON_LIBRARIES} + ) + add_definitions(-DWITH_PYTHON) +endif() + +if(WITH_INTERNATIONAL) + add_definitions(-DWITH_INTERNATIONAL) +endif() + +if(WITH_FREESTYLE) + add_definitions(-DWITH_FREESTYLE) +endif() + +blender_add_lib(bf_nodes_shader "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") + +if(WITH_UNITY_BUILD) + set_target_properties(bf_nodes_shader PROPERTIES UNITY_BUILD ON) + set_target_properties(bf_nodes_shader PROPERTIES UNITY_BUILD_BATCH_SIZE 10) +endif() diff --git a/source/blender/nodes/shader/node_shader_tree.c b/source/blender/nodes/shader/node_shader_tree.cc index 46a2f7f1968..1cb1ee3163d 100644 --- a/source/blender/nodes/shader/node_shader_tree.c +++ b/source/blender/nodes/shader/node_shader_tree.cc @@ -21,7 +21,7 @@ * \ingroup nodes */ -#include <string.h> +#include <cstring> #include "DNA_light_types.h" #include "DNA_linestyle_types.h" @@ -44,6 +44,7 @@ #include "BKE_lib_id.h" #include "BKE_linestyle.h" #include "BKE_node.h" +#include "BKE_node_tree_update.h" #include "BKE_scene.h" #include "RNA_access.h" @@ -52,11 +53,13 @@ #include "RE_texture.h" +#include "UI_resources.h" + #include "NOD_common.h" #include "node_common.h" #include "node_exec.h" -#include "node_shader_util.h" +#include "node_shader_util.hh" #include "node_util.h" static bool shader_tree_poll(const bContext *C, bNodeTreeType *UNUSED(treetype)) @@ -85,7 +88,7 @@ static void shader_get_from_context(const bContext *C, if (ob) { *r_from = &ob->id; if (ob->type == OB_LAMP) { - *r_id = ob->data; + *r_id = static_cast<ID *>(ob->data); *r_ntree = ((Light *)ob->data)->nodetree; } else { @@ -101,7 +104,7 @@ static void shader_get_from_context(const bContext *C, else if (snode->shaderfrom == SNODE_SHADER_LINESTYLE) { FreestyleLineStyle *linestyle = BKE_linestyle_active_from_view_layer(view_layer); if (linestyle) { - *r_from = NULL; + *r_from = nullptr; *r_id = &linestyle->id; *r_ntree = linestyle->nodetree; } @@ -109,7 +112,7 @@ static void shader_get_from_context(const bContext *C, #endif else { /* SNODE_SHADER_WORLD */ if (scene->world) { - *r_from = NULL; + *r_from = nullptr; *r_id = &scene->world->id; *r_ntree = scene->world->nodetree; } @@ -133,12 +136,8 @@ static void foreach_nodeclass(Scene *UNUSED(scene), void *calldata, bNodeClassCa static void localize(bNodeTree *localtree, bNodeTree *UNUSED(ntree)) { - bNode *node, *node_next; - /* replace muted nodes and reroute nodes by internal links */ - for (node = localtree->nodes.first; node; node = node_next) { - node_next = node->next; - + LISTBASE_FOREACH_MUTABLE (bNode *, node, &localtree->nodes) { if (node->flag & NODE_MUTED || node->type == NODE_REROUTE) { nodeInternalRelink(localtree, node); ntreeFreeLocalNode(localtree, node); @@ -146,34 +145,19 @@ static void localize(bNodeTree *localtree, bNodeTree *UNUSED(ntree)) } } -static void local_sync(bNodeTree *localtree, bNodeTree *ntree) -{ - BKE_node_preview_sync_tree(ntree, localtree); -} - -static void local_merge(Main *UNUSED(bmain), bNodeTree *localtree, bNodeTree *ntree) -{ - BKE_node_preview_merge_tree(ntree, localtree, true); -} - static void update(bNodeTree *ntree) { ntreeSetOutput(ntree); ntree_update_reroute_nodes(ntree); - - if (ntree->update & NTREE_UPDATE_NODES) { - /* clean up preview cache, in case nodes have been removed */ - BKE_node_preview_remove_unused(ntree); - } } -static bool shader_validate_link(bNodeTree *UNUSED(ntree), bNodeLink *link) +static bool shader_validate_link(eNodeSocketDatatype from, eNodeSocketDatatype to) { /* Can't connect shader into other socket types, other way around is fine * since it will be interpreted as emission. */ - if (link->fromsock->type == SOCK_SHADER) { - return (link->tosock->type == SOCK_SHADER); + if (from == SOCK_SHADER) { + return to == SOCK_SHADER; } return true; } @@ -187,21 +171,18 @@ static bool shader_node_tree_socket_type_valid(bNodeTreeType *UNUSED(ntreetype), bNodeTreeType *ntreeType_Shader; -void register_node_tree_type_sh(void) +void register_node_tree_type_sh() { - bNodeTreeType *tt = ntreeType_Shader = MEM_callocN(sizeof(bNodeTreeType), - "shader node tree type"); + bNodeTreeType *tt = ntreeType_Shader = MEM_cnew<bNodeTreeType>("shader node tree type"); tt->type = NTREE_SHADER; strcpy(tt->idname, "ShaderNodeTree"); strcpy(tt->ui_name, N_("Shader Editor")); - tt->ui_icon = 0; /* defined in drawnode.c */ + tt->ui_icon = ICON_NODE_MATERIAL; strcpy(tt->ui_description, N_("Shader nodes")); tt->foreach_nodeclass = foreach_nodeclass; tt->localize = localize; - tt->local_sync = local_sync; - tt->local_merge = local_merge; tt->update = update; tt->poll = shader_tree_poll; tt->get_from_context = shader_get_from_context; @@ -215,13 +196,6 @@ void register_node_tree_type_sh(void) /* GPU material from shader nodes */ -/* Find an output node of the shader tree. - * - * NOTE: it will only return output which is NOT in the group, which isn't how - * render engines works but it's how the GPU shader compilation works. This we - * can change in the future and make it a generic function, but for now it stays - * private here. - */ bNode *ntreeShaderOutputNode(bNodeTree *ntree, int target) { /* Make sure we only have single node tagged as output. */ @@ -229,7 +203,7 @@ bNode *ntreeShaderOutputNode(bNodeTree *ntree, int target) /* Find output node that matches type and target. If there are * multiple, we prefer exact target match and active nodes. */ - bNode *output_node = NULL; + bNode *output_node = nullptr; LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { if (!ELEM(node->type, SH_NODE_OUTPUT_MATERIAL, SH_NODE_OUTPUT_WORLD, SH_NODE_OUTPUT_LIGHT)) { @@ -237,7 +211,7 @@ bNode *ntreeShaderOutputNode(bNodeTree *ntree, int target) } if (node->custom1 == SHD_OUTPUT_ALL) { - if (output_node == NULL) { + if (output_node == nullptr) { output_node = node; } else if (output_node->custom1 == SHD_OUTPUT_ALL) { @@ -247,7 +221,7 @@ bNode *ntreeShaderOutputNode(bNodeTree *ntree, int target) } } else if (node->custom1 == target) { - if (output_node == NULL) { + if (output_node == nullptr) { output_node = node; } else if (output_node->custom1 == SHD_OUTPUT_ALL) { @@ -265,12 +239,12 @@ bNode *ntreeShaderOutputNode(bNodeTree *ntree, int target) /* Find socket with a specified identifier. */ static bNodeSocket *ntree_shader_node_find_socket(ListBase *sockets, const char *identifier) { - for (bNodeSocket *sock = sockets->first; sock != NULL; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, sockets) { if (STREQ(sock->identifier, identifier)) { return sock; } } - return NULL; + return nullptr; } /* Find input socket with a specified identifier. */ @@ -311,37 +285,37 @@ static bool ntree_shader_expand_socket_default(bNodeTree *localtree, switch (socket->type) { case SOCK_VECTOR: - value_node = nodeAddStaticNode(NULL, localtree, SH_NODE_RGB); + value_node = nodeAddStaticNode(nullptr, localtree, SH_NODE_RGB); value_socket = ntree_shader_node_find_output(value_node, "Color"); - BLI_assert(value_socket != NULL); - src_vector = socket->default_value; - dst_rgba = value_socket->default_value; + BLI_assert(value_socket != nullptr); + src_vector = static_cast<bNodeSocketValueVector *>(socket->default_value); + dst_rgba = static_cast<bNodeSocketValueRGBA *>(value_socket->default_value); copy_v3_v3(dst_rgba->value, src_vector->value); dst_rgba->value[3] = 1.0f; /* should never be read */ break; case SOCK_RGBA: - value_node = nodeAddStaticNode(NULL, localtree, SH_NODE_RGB); + value_node = nodeAddStaticNode(nullptr, localtree, SH_NODE_RGB); value_socket = ntree_shader_node_find_output(value_node, "Color"); - BLI_assert(value_socket != NULL); - src_rgba = socket->default_value; - dst_rgba = value_socket->default_value; + BLI_assert(value_socket != nullptr); + src_rgba = static_cast<bNodeSocketValueRGBA *>(socket->default_value); + dst_rgba = static_cast<bNodeSocketValueRGBA *>(value_socket->default_value); copy_v4_v4(dst_rgba->value, src_rgba->value); break; case SOCK_INT: /* HACK: Support as float. */ - value_node = nodeAddStaticNode(NULL, localtree, SH_NODE_VALUE); + value_node = nodeAddStaticNode(nullptr, localtree, SH_NODE_VALUE); value_socket = ntree_shader_node_find_output(value_node, "Value"); - BLI_assert(value_socket != NULL); - src_int = socket->default_value; - dst_float = value_socket->default_value; + BLI_assert(value_socket != nullptr); + src_int = static_cast<bNodeSocketValueInt *>(socket->default_value); + dst_float = static_cast<bNodeSocketValueFloat *>(value_socket->default_value); dst_float->value = (float)(src_int->value); break; case SOCK_FLOAT: - value_node = nodeAddStaticNode(NULL, localtree, SH_NODE_VALUE); + value_node = nodeAddStaticNode(nullptr, localtree, SH_NODE_VALUE); value_socket = ntree_shader_node_find_output(value_node, "Value"); - BLI_assert(value_socket != NULL); - src_float = socket->default_value; - dst_float = value_socket->default_value; + BLI_assert(value_socket != nullptr); + src_float = static_cast<bNodeSocketValueFloat *>(socket->default_value); + dst_float = static_cast<bNodeSocketValueFloat *>(value_socket->default_value); dst_float->value = src_float->value; break; default: @@ -354,11 +328,10 @@ static bool ntree_shader_expand_socket_default(bNodeTree *localtree, static void ntree_shader_unlink_hidden_value_sockets(bNode *group_node, bNodeSocket *isock) { bNodeTree *group_ntree = (bNodeTree *)group_node->id; - bNode *node; bool removed_link = false; - for (node = group_ntree->nodes.first; node; node = node->next) { - const bool is_group = ELEM(node->type, NODE_GROUP, NODE_CUSTOM_GROUP) && (node->id != NULL); + LISTBASE_FOREACH (bNode *, node, &group_ntree->nodes) { + const bool is_group = ELEM(node->type, NODE_GROUP, NODE_CUSTOM_GROUP) && (node->id != nullptr); LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { if (!is_group && (sock->flag & SOCK_HIDE_VALUE) == 0) { @@ -381,7 +354,7 @@ static void ntree_shader_unlink_hidden_value_sockets(bNode *group_node, bNodeSoc } if (removed_link) { - ntreeUpdateTree(G.main, group_ntree); + BKE_ntree_update_main_tree(G.main, group_ntree, nullptr); } } @@ -392,7 +365,7 @@ static void ntree_shader_groups_expand_inputs(bNodeTree *localtree) bool link_added = false; LISTBASE_FOREACH (bNode *, node, &localtree->nodes) { - const bool is_group = ELEM(node->type, NODE_GROUP, NODE_CUSTOM_GROUP) && (node->id != NULL); + const bool is_group = ELEM(node->type, NODE_GROUP, NODE_CUSTOM_GROUP) && (node->id != nullptr); const bool is_group_output = node->type == NODE_GROUP_OUTPUT && (node->flag & NODE_DO_OUTPUT); if (is_group) { @@ -402,25 +375,32 @@ static void ntree_shader_groups_expand_inputs(bNodeTree *localtree) if (is_group || is_group_output) { LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) { - if (socket->link != NULL && !(socket->link->flag & NODE_LINK_MUTED)) { + if (socket->link != nullptr && !(socket->link->flag & NODE_LINK_MUTED)) { bNodeLink *link = socket->link; /* Fix the case where the socket is actually converting the data. (see T71374) * We only do the case of lossy conversion to float. */ if ((socket->type == SOCK_FLOAT) && (link->fromsock->type != link->tosock->type)) { if (link->fromsock->type == SOCK_RGBA) { - bNode *tmp = nodeAddStaticNode(NULL, localtree, SH_NODE_RGBTOBW); - nodeAddLink(localtree, link->fromnode, link->fromsock, tmp, tmp->inputs.first); - nodeAddLink(localtree, tmp, tmp->outputs.first, node, socket); + bNode *tmp = nodeAddStaticNode(nullptr, localtree, SH_NODE_RGBTOBW); + nodeAddLink(localtree, + link->fromnode, + link->fromsock, + tmp, + static_cast<bNodeSocket *>(tmp->inputs.first)); + nodeAddLink( + localtree, tmp, static_cast<bNodeSocket *>(tmp->outputs.first), node, socket); } else if (link->fromsock->type == SOCK_VECTOR) { - bNode *tmp = nodeAddStaticNode(NULL, localtree, SH_NODE_VECTOR_MATH); + bNode *tmp = nodeAddStaticNode(nullptr, localtree, SH_NODE_VECTOR_MATH); tmp->custom1 = NODE_VECTOR_MATH_DOT_PRODUCT; - bNodeSocket *dot_input1 = tmp->inputs.first; - bNodeSocket *dot_input2 = dot_input1->next; - bNodeSocketValueVector *input2_socket_value = dot_input2->default_value; + bNodeSocket *dot_input1 = static_cast<bNodeSocket *>(tmp->inputs.first); + bNodeSocket *dot_input2 = static_cast<bNodeSocket *>(dot_input1->next); + bNodeSocketValueVector *input2_socket_value = static_cast<bNodeSocketValueVector *>( + dot_input2->default_value); copy_v3_fl(input2_socket_value->value, 1.0f / 3.0f); nodeAddLink(localtree, link->fromnode, link->fromsock, tmp, dot_input1); - nodeAddLink(localtree, tmp, tmp->outputs.last, node, socket); + nodeAddLink( + localtree, tmp, static_cast<bNodeSocket *>(tmp->outputs.last), node, socket); } } continue; @@ -440,22 +420,33 @@ static void ntree_shader_groups_expand_inputs(bNodeTree *localtree) } if (link_added) { - ntreeUpdateTree(G.main, localtree); + BKE_ntree_update_main_tree(G.main, localtree, nullptr); } } -static void flatten_group_do(bNodeTree *ntree, bNode *gnode) +static void ntree_shader_groups_remove_muted_links(bNodeTree *ntree) { - bNodeLink *link, *linkn, *tlink; - bNode *node, *nextnode; - bNodeTree *ngroup; - LinkNode *group_interface_nodes = NULL; + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->type == NODE_GROUP) { + if (node->id != nullptr) { + ntree_shader_groups_remove_muted_links(reinterpret_cast<bNodeTree *>(node->id)); + } + } + } + LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree->links) { + if (link->flag & NODE_LINK_MUTED) { + nodeRemLink(ntree, link); + } + } +} - ngroup = (bNodeTree *)gnode->id; +static void flatten_group_do(bNodeTree *ntree, bNode *gnode) +{ + LinkNode *group_interface_nodes = nullptr; + bNodeTree *ngroup = (bNodeTree *)gnode->id; /* Add the nodes into the ntree */ - for (node = ngroup->nodes.first; node; node = nextnode) { - nextnode = node->next; + LISTBASE_FOREACH_MUTABLE (bNode *, node, &ngroup->nodes) { /* Remove interface nodes. * This also removes remaining links to and from interface nodes. * We must delay removal since sockets will reference this node. see: T52092 */ @@ -471,25 +462,26 @@ static void flatten_group_do(bNodeTree *ntree, bNode *gnode) } /* Save first and last link to iterate over flattened group links. */ - bNodeLink *glinks_first = ntree->links.last; + bNodeLink *glinks_first = static_cast<bNodeLink *>(ntree->links.last); /* Add internal links to the ntree */ - for (link = ngroup->links.first; link; link = linkn) { - linkn = link->next; + LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ngroup->links) { BLI_remlink(&ngroup->links, link); BLI_addtail(&ntree->links, link); } - bNodeLink *glinks_last = ntree->links.last; + bNodeLink *glinks_last = static_cast<bNodeLink *>(ntree->links.last); /* restore external links to and from the gnode */ - if (glinks_first != NULL) { + if (glinks_first != nullptr) { /* input links */ - for (link = glinks_first->next; link != glinks_last->next; link = link->next) { + for (bNodeLink *link = glinks_first->next; link != glinks_last->next; link = link->next) { if (link->fromnode->type == NODE_GROUP_INPUT) { const char *identifier = link->fromsock->identifier; /* find external links to this input */ - for (tlink = ntree->links.first; tlink != glinks_first->next; tlink = tlink->next) { + for (bNodeLink *tlink = static_cast<bNodeLink *>(ntree->links.first); + tlink != glinks_first->next; + tlink = tlink->next) { if (tlink->tonode == gnode && STREQ(tlink->tosock->identifier, identifier)) { nodeAddLink(ntree, tlink->fromnode, tlink->fromsock, link->tonode, link->tosock); } @@ -497,13 +489,15 @@ static void flatten_group_do(bNodeTree *ntree, bNode *gnode) } } /* Also iterate over the new links to cover passthrough links. */ - glinks_last = ntree->links.last; + glinks_last = static_cast<bNodeLink *>(ntree->links.last); /* output links */ - for (tlink = ntree->links.first; tlink != glinks_first->next; tlink = tlink->next) { + for (bNodeLink *tlink = static_cast<bNodeLink *>(ntree->links.first); + tlink != glinks_first->next; + tlink = tlink->next) { if (tlink->fromnode == gnode) { const char *identifier = tlink->fromsock->identifier; /* find internal links to this output */ - for (link = glinks_first->next; link != glinks_last->next; link = link->next) { + for (bNodeLink *link = glinks_first->next; link != glinks_last->next; link = link->next) { /* only use active output node */ if (link->tonode->type == NODE_GROUP_OUTPUT && (link->tonode->flag & NODE_DO_OUTPUT)) { if (STREQ(link->tosock->identifier, identifier)) { @@ -516,11 +510,11 @@ static void flatten_group_do(bNodeTree *ntree, bNode *gnode) } while (group_interface_nodes) { - node = BLI_linklist_pop(&group_interface_nodes); + bNode *node = static_cast<bNode *>(BLI_linklist_pop(&group_interface_nodes)); ntreeFreeLocalNode(ntree, node); } - ntree->update |= NTREE_UPDATE_NODES | NTREE_UPDATE_LINKS; + BKE_ntree_update_tag_all(ntree); } /* Flatten group to only have a simple single tree */ @@ -528,8 +522,9 @@ static void ntree_shader_groups_flatten(bNodeTree *localtree) { /* This is effectively recursive as the flattened groups will add * nodes at the end of the list, which will also get evaluated. */ - for (bNode *node = localtree->nodes.first, *node_next; node; node = node_next) { - if (ELEM(node->type, NODE_GROUP, NODE_CUSTOM_GROUP) && node->id != NULL) { + for (bNode *node = static_cast<bNode *>(localtree->nodes.first), *node_next; node; + node = node_next) { + if (ELEM(node->type, NODE_GROUP, NODE_CUSTOM_GROUP) && node->id != nullptr) { flatten_group_do(localtree, node); /* Continue even on new flattened nodes. */ node_next = node->next; @@ -545,7 +540,7 @@ static void ntree_shader_groups_flatten(bNodeTree *localtree) } } - ntreeUpdateTree(G.main, localtree); + BKE_ntree_update_main_tree(G.main, localtree, nullptr); } /* Check whether shader has a displacement. @@ -560,20 +555,20 @@ static bool ntree_shader_has_displacement(bNodeTree *ntree, bNodeSocket **r_socket, bNodeLink **r_link) { - if (output_node == NULL) { + if (output_node == nullptr) { /* We can't have displacement without output node, apparently. */ return false; } /* Make sure sockets links pointers are correct. */ - ntreeUpdateTree(G.main, ntree); + BKE_ntree_update_main_tree(G.main, ntree, nullptr); bNodeSocket *displacement = ntree_shader_node_find_input(output_node, "Displacement"); - if (displacement == NULL) { + if (displacement == nullptr) { /* Non-cycles node is used as an output. */ return false; } - if ((displacement->link != NULL) && !(displacement->link->flag & NODE_LINK_MUTED)) { + if ((displacement->link != nullptr) && !(displacement->link->flag & NODE_LINK_MUTED)) { *r_node = displacement->link->fromnode; *r_socket = displacement->link->fromsock; *r_link = displacement->link; @@ -591,7 +586,7 @@ static void ntree_shader_relink_node_normal(bNodeTree *ntree, * matching? */ LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { - if (STREQ(sock->identifier, "Normal") && sock->link == NULL) { + if (STREQ(sock->identifier, "Normal") && sock->link == nullptr) { /* It's a normal input and nothing is connected to it. */ nodeAddLink(ntree, node_from, socket_from, node, sock); } @@ -611,7 +606,7 @@ static void ntree_shader_link_builtin_normal(bNodeTree *ntree, bNode *node_from, bNodeSocket *socket_from) { - for (bNode *node = ntree->nodes.first; node != NULL; node = node->next) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { if (node == node_from) { /* Don't connect node itself! */ continue; @@ -636,7 +631,7 @@ static void ntree_shader_bypass_bump_link(bNodeTree *ntree, bNode *bump_node, bN fromnode = bump_normal_input->link->fromnode; } else { - fromnode = nodeAddStaticNode(NULL, ntree, SH_NODE_NEW_GEOMETRY); + fromnode = nodeAddStaticNode(nullptr, ntree, SH_NODE_NEW_GEOMETRY); fromsock = ntree_shader_node_find_output(fromnode, "Normal"); } /* Bypass the bump node by creating a link between the previous and next node. */ @@ -654,7 +649,7 @@ static void ntree_shader_bypass_tagged_bump_nodes(bNodeTree *ntree) ntree_shader_bypass_bump_link(ntree, node, link); } } - ntreeUpdateTree(G.main, ntree); + BKE_ntree_update_main_tree(G.main, ntree, nullptr); } static bool ntree_branch_count_and_tag_nodes(bNode *fromnode, bNode *tonode, void *userdata) @@ -688,19 +683,19 @@ static bNode *ntree_shader_copy_branch(bNodeTree *ntree, int node_count = 1; nodeChainIterBackwards(ntree, start_node, ntree_branch_count_and_tag_nodes, &node_count, 1); /* Make a full copy of the branch */ - bNode **nodes_copy = MEM_mallocN(sizeof(bNode *) * node_count, __func__); + bNode **nodes_copy = static_cast<bNode **>(MEM_mallocN(sizeof(bNode *) * node_count, __func__)); LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { if (node->tmp_flag >= 0) { int id = node->tmp_flag; - nodes_copy[id] = BKE_node_copy_ex( - ntree, node, LIB_ID_CREATE_NO_USER_REFCOUNT | LIB_ID_CREATE_NO_MAIN, false); + nodes_copy[id] = blender::bke::node_copy( + ntree, *node, LIB_ID_CREATE_NO_USER_REFCOUNT | LIB_ID_CREATE_NO_MAIN, false); nodes_copy[id]->tmp_flag = -2; /* Copy */ /* Make sure to clear all sockets links as they are invalid. */ LISTBASE_FOREACH (bNodeSocket *, sock, &nodes_copy[id]->inputs) { - sock->link = NULL; + sock->link = nullptr; } LISTBASE_FOREACH (bNodeSocket *, sock, &nodes_copy[id]->outputs) { - sock->link = NULL; + sock->link = nullptr; } } } @@ -733,13 +728,13 @@ static void ntree_shader_copy_branch_displacement(bNodeTree *ntree, /* Replace displacement socket/node/link. */ bNode *tonode = displacement_link->tonode; bNodeSocket *tosock = displacement_link->tosock; - displacement_node = ntree_shader_copy_branch(ntree, displacement_node, NULL, 0); + displacement_node = ntree_shader_copy_branch(ntree, displacement_node, nullptr, 0); displacement_socket = ntree_shader_node_find_output(displacement_node, displacement_socket->identifier); nodeRemLink(ntree, displacement_link); nodeAddLink(ntree, displacement_node, displacement_socket, tonode, tosock); - ntreeUpdateTree(G.main, ntree); + BKE_ntree_update_main_tree(G.main, ntree, nullptr); } /* Re-link displacement output to unconnected normal sockets via bump node. @@ -773,11 +768,11 @@ static void ntree_shader_relink_displacement(bNodeTree *ntree, bNode *output_nod nodeRemLink(ntree, displacement_link); /* Convert displacement vector to bump height. */ - bNode *dot_node = nodeAddStaticNode(NULL, ntree, SH_NODE_VECTOR_MATH); - bNode *geo_node = nodeAddStaticNode(NULL, ntree, SH_NODE_NEW_GEOMETRY); + bNode *dot_node = nodeAddStaticNode(nullptr, ntree, SH_NODE_VECTOR_MATH); + bNode *geo_node = nodeAddStaticNode(nullptr, ntree, SH_NODE_NEW_GEOMETRY); bNodeSocket *normal_socket = ntree_shader_node_find_output(geo_node, "Normal"); - bNodeSocket *dot_input1 = dot_node->inputs.first; - bNodeSocket *dot_input2 = dot_input1->next; + bNodeSocket *dot_input1 = static_cast<bNodeSocket *>(dot_node->inputs.first); + bNodeSocket *dot_input2 = static_cast<bNodeSocket *>(dot_input1->next); dot_node->custom1 = NODE_VECTOR_MATH_DOT_PRODUCT; nodeAddLink(ntree, displacement_node, displacement_socket, dot_node, dot_input1); @@ -788,11 +783,11 @@ static void ntree_shader_relink_displacement(bNodeTree *ntree, bNode *output_nod /* We can't connect displacement to normal directly, use bump node for that * and hope that it gives good enough approximation. */ - bNode *bump_node = nodeAddStaticNode(NULL, ntree, SH_NODE_BUMP); + bNode *bump_node = nodeAddStaticNode(nullptr, ntree, SH_NODE_BUMP); bNodeSocket *bump_input_socket = ntree_shader_node_find_input(bump_node, "Height"); bNodeSocket *bump_output_socket = ntree_shader_node_find_output(bump_node, "Normal"); - BLI_assert(bump_input_socket != NULL); - BLI_assert(bump_output_socket != NULL); + BLI_assert(bump_input_socket != nullptr); + BLI_assert(bump_output_socket != nullptr); /* Connect bump node to where displacement output was originally * connected to. */ @@ -803,12 +798,12 @@ static void ntree_shader_relink_displacement(bNodeTree *ntree, bNode *output_nod geo_node->tmp_flag = -2; bump_node->tmp_flag = -2; - ntreeUpdateTree(G.main, ntree); + BKE_ntree_update_main_tree(G.main, ntree, nullptr); /* Connect all free-standing Normal inputs and relink geometry/coordinate nodes. */ ntree_shader_link_builtin_normal(ntree, bump_node, bump_output_socket); /* We modified the tree, it needs to be updated now. */ - ntreeUpdateTree(G.main, ntree); + BKE_ntree_update_main_tree(G.main, ntree, nullptr); } static void node_tag_branch_as_derivative(bNode *node, int dx) @@ -868,12 +863,12 @@ static bool ntree_shader_implicit_closure_cast(bNodeTree *ntree) else if ((link->fromsock->type == SOCK_SHADER) && (link->tosock->type != SOCK_SHADER)) { /* Meh. Not directly visible to the user. But better than nothing. */ fprintf(stderr, "Shader Nodetree Error: Invalid implicit socket conversion\n"); - ntreeUpdateTree(G.main, ntree); + BKE_ntree_update_main_tree(G.main, ntree, nullptr); return false; } } if (modified) { - ntreeUpdateTree(G.main, ntree); + BKE_ntree_update_main_tree(G.main, ntree, nullptr); } return true; } @@ -1129,7 +1124,8 @@ static void ntree_shader_weight_tree_invert(bNodeTree *ntree, bNode *output_node break; } - /* Manually add the link to the socket to avoid calling ntreeUpdateTree in the loop. */ + /* Manually add the link to the socket to avoid calling + * BKE_ntree_update_main_tree(G.main, oop, nullptr. */ fromsock->link = nodeAddLink(ntree, fromnode, fromsock, tonode, tosock); BLI_assert(fromsock->link); } @@ -1145,23 +1141,23 @@ static void ntree_shader_weight_tree_invert(bNodeTree *ntree, bNode *output_node nodeAddLink( ntree, thickness_link->fromnode, thickness_link->fromsock, output_node, thickness_output); } - ntreeUpdateTree(G.main, ntree); + BKE_ntree_update_main_tree(G.main, ntree, nullptr); MEM_freeN(nodes_copy); } -/* This one needs to work on a local tree. */ void ntreeGPUMaterialNodes(bNodeTree *localtree, GPUMaterial *mat) { bNodeTreeExec *exec; bNode *output = ntreeShaderOutputNode(localtree, SHD_OUTPUT_EEVEE); + ntree_shader_groups_remove_muted_links(localtree); ntree_shader_groups_expand_inputs(localtree); ntree_shader_groups_flatten(localtree); - if (output == NULL) { + if (output == nullptr) { /* Search again, now including flattened nodes. */ output = ntreeShaderOutputNode(localtree, SHD_OUTPUT_EEVEE); } @@ -1186,7 +1182,6 @@ void ntreeGPUMaterialNodes(bNodeTree *localtree, GPUMaterial *mat) nodeChainIterBackwards(localtree, node, ntree_shader_bump_branches, localtree, 0); } } - exec = ntreeShaderBeginExecTree(localtree); ntreeExecGPUNodes(exec, mat, output); LISTBASE_FOREACH (bNode *, node, &localtree->nodes) { @@ -1201,19 +1196,17 @@ bNodeTreeExec *ntreeShaderBeginExecTree_internal(bNodeExecContext *context, bNodeTree *ntree, bNodeInstanceKey parent_key) { - bNodeTreeExec *exec; - bNode *node; - /* ensures only a single output node is enabled */ ntreeSetOutput(ntree); /* common base initialization */ - exec = ntree_exec_begin(context, ntree, parent_key); + bNodeTreeExec *exec = ntree_exec_begin(context, ntree, parent_key); /* allocate the thread stack listbase array */ - exec->threadstack = MEM_callocN(BLENDER_MAX_THREADS * sizeof(ListBase), "thread stack array"); + exec->threadstack = static_cast<ListBase *>( + MEM_callocN(BLENDER_MAX_THREADS * sizeof(ListBase), "thread stack array")); - for (node = exec->nodetree->nodes.first; node; node = node->next) { + LISTBASE_FOREACH (bNode *, node, &exec->nodetree->nodes) { node->need_exec = 1; } @@ -1236,8 +1229,8 @@ bNodeTreeExec *ntreeShaderBeginExecTree(bNodeTree *ntree) exec = ntreeShaderBeginExecTree_internal(&context, ntree, NODE_INSTANCE_KEY_BASE); - /* XXX this should not be necessary, but is still used for cmp/sha/tex nodes, - * which only store the ntree pointer. Should be fixed at some point! + /* XXX: this should not be necessary, but is still used for compositor/shader/texture nodes, + * which only store the `ntree` pointer. Should be fixed at some point! */ ntree->execdata = exec; @@ -1246,12 +1239,9 @@ bNodeTreeExec *ntreeShaderBeginExecTree(bNodeTree *ntree) void ntreeShaderEndExecTree_internal(bNodeTreeExec *exec) { - bNodeThreadStack *nts; - int a; - if (exec->threadstack) { - for (a = 0; a < BLENDER_MAX_THREADS; a++) { - for (nts = exec->threadstack[a].first; nts; nts = nts->next) { + for (int a = 0; a < BLENDER_MAX_THREADS; a++) { + LISTBASE_FOREACH (bNodeThreadStack *, nts, &exec->threadstack[a]) { if (nts->stack) { MEM_freeN(nts->stack); } @@ -1260,7 +1250,7 @@ void ntreeShaderEndExecTree_internal(bNodeTreeExec *exec) } MEM_freeN(exec->threadstack); - exec->threadstack = NULL; + exec->threadstack = nullptr; } ntree_exec_end(exec); @@ -1274,6 +1264,6 @@ void ntreeShaderEndExecTree(bNodeTreeExec *exec) ntreeShaderEndExecTree_internal(exec); /* XXX clear nodetree backpointer to exec data, same problem as noted in ntreeBeginExecTree */ - ntree->execdata = NULL; + ntree->execdata = nullptr; } } diff --git a/source/blender/nodes/shader/node_shader_util.c b/source/blender/nodes/shader/node_shader_util.cc index 1804bbcd22a..8ea690a426f 100644 --- a/source/blender/nodes/shader/node_shader_util.c +++ b/source/blender/nodes/shader/node_shader_util.cc @@ -23,14 +23,16 @@ #include "DNA_node_types.h" -#include "node_shader_util.h" +#include "node_shader_util.hh" + +#include "NOD_socket_search_link.hh" #include "node_exec.h" bool sh_node_poll_default(bNodeType *UNUSED(ntype), bNodeTree *ntree, const char **r_disabled_hint) { if (!STREQ(ntree->idname, "ShaderNodeTree")) { - *r_disabled_hint = "Not a shader node tree"; + *r_disabled_hint = TIP_("Not a shader node tree"); return false; } return true; @@ -40,32 +42,32 @@ static bool sh_fn_poll_default(bNodeType *UNUSED(ntype), bNodeTree *ntree, const char **r_disabled_hint) { - if (!STREQ(ntree->idname, "ShaderNodeTree") && !STREQ(ntree->idname, "GeometryNodeTree")) { - *r_disabled_hint = "Not a shader or geometry node tree"; + if (!STR_ELEM(ntree->idname, "ShaderNodeTree", "GeometryNodeTree")) { + *r_disabled_hint = TIP_("Not a shader or geometry node tree"); return false; } return true; } -void sh_node_type_base( - struct bNodeType *ntype, int type, const char *name, short nclass, short flag) +void sh_node_type_base(struct bNodeType *ntype, int type, const char *name, short nclass) { - node_type_base(ntype, type, name, nclass, flag); + node_type_base(ntype, type, name, nclass); ntype->poll = sh_node_poll_default; ntype->insert_link = node_insert_link_default; - ntype->update_internal_links = node_update_internal_links_default; + ntype->gather_link_search_ops = blender::nodes::search_link_ops_for_basic_node; } -void sh_fn_node_type_base(bNodeType *ntype, int type, const char *name, short nclass, short flag) +void sh_fn_node_type_base(bNodeType *ntype, int type, const char *name, short nclass) { - sh_node_type_base(ntype, type, name, nclass, flag); + sh_node_type_base(ntype, type, name, nclass); ntype->poll = sh_fn_poll_default; + ntype->gather_link_search_ops = blender::nodes::search_link_ops_for_basic_node; } /* ****** */ -void nodestack_get_vec(float *in, short type_in, bNodeStack *ns) +static void nodestack_get_vec(float *in, short type_in, bNodeStack *ns) { const float *from = ns->vec; @@ -108,11 +110,11 @@ void node_gpu_stack_from_data(struct GPUNodeStack *gs, int type, bNodeStack *ns) { memset(gs, 0, sizeof(*gs)); - if (ns == NULL) { - /* node_get_stack() will generate NULL bNodeStack pointers + if (ns == nullptr) { + /* node_get_stack() will generate nullptr bNodeStack pointers * for unknown/unsupported types of sockets. */ zero_v4(gs->vec); - gs->link = NULL; + gs->link = nullptr; gs->type = GPU_NONE; gs->hasinput = false; gs->hasoutput = false; @@ -120,7 +122,7 @@ void node_gpu_stack_from_data(struct GPUNodeStack *gs, int type, bNodeStack *ns) } else { nodestack_get_vec(gs->vec, type, ns); - gs->link = ns->data; + gs->link = (GPUNodeLink *)ns->data; if (type == SOCK_FLOAT) { gs->type = GPU_FLOAT; @@ -160,11 +162,9 @@ void node_data_from_gpu_stack(bNodeStack *ns, GPUNodeStack *gs) static void gpu_stack_from_data_list(GPUNodeStack *gs, ListBase *sockets, bNodeStack **ns) { - bNodeSocket *sock; int i; - - for (sock = sockets->first, i = 0; sock; sock = sock->next, i++) { - node_gpu_stack_from_data(&gs[i], sock->type, ns[i]); + LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, sockets, i) { + node_gpu_stack_from_data(&gs[i], socket->type, ns[i]); } gs[i].end = true; @@ -172,10 +172,8 @@ static void gpu_stack_from_data_list(GPUNodeStack *gs, ListBase *sockets, bNodeS static void data_from_gpu_stack_list(ListBase *sockets, bNodeStack **ns, GPUNodeStack *gs) { - bNodeSocket *sock; int i; - - for (sock = sockets->first, i = 0; sock; sock = sock->next, i++) { + LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, sockets, i) { node_data_from_gpu_stack(ns[i], &gs[i]); } } @@ -183,14 +181,14 @@ static void data_from_gpu_stack_list(ListBase *sockets, bNodeStack **ns, GPUNode bNode *nodeGetActiveTexture(bNodeTree *ntree) { /* this is the node we texture paint and draw in textured draw */ - bNode *node, *tnode, *inactivenode = NULL, *activetexnode = NULL, *activegroup = NULL; + bNode *inactivenode = nullptr, *activetexnode = nullptr, *activegroup = nullptr; bool hasgroup = false; if (!ntree) { - return NULL; + return nullptr; } - for (node = ntree->nodes.first; node; node = node->next) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { if (node->flag & NODE_ACTIVE_TEXTURE) { activetexnode = node; /* if active we can return immediately */ @@ -213,7 +211,7 @@ bNode *nodeGetActiveTexture(bNodeTree *ntree) /* first, check active group for textures */ if (activegroup) { - tnode = nodeGetActiveTexture((bNodeTree *)activegroup->id); + bNode *tnode = nodeGetActiveTexture((bNodeTree *)activegroup->id); /* active node takes priority, so ignore any other possible nodes here */ if (tnode) { return tnode; @@ -226,9 +224,9 @@ bNode *nodeGetActiveTexture(bNodeTree *ntree) if (hasgroup) { /* node active texture node in this tree, look inside groups */ - for (node = ntree->nodes.first; node; node = node->next) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { if (node->type == NODE_GROUP) { - tnode = nodeGetActiveTexture((bNodeTree *)node->id); + bNode *tnode = nodeGetActiveTexture((bNodeTree *)node->id); if (tnode && ((tnode->flag & NODE_ACTIVE_TEXTURE) || !inactivenode)) { return tnode; } @@ -258,7 +256,7 @@ void ntreeExecGPUNodes(bNodeTreeExec *exec, GPUMaterial *mat, bNode *output_node do_it = false; /* for groups, only execute outputs for edited group */ if (node->typeinfo->nclass == NODE_CLASS_OUTPUT) { - if ((output_node != NULL) && (node == output_node)) { + if ((output_node != nullptr) && (node == output_node)) { do_it = true; } } @@ -282,11 +280,11 @@ void ntreeExecGPUNodes(bNodeTreeExec *exec, GPUMaterial *mat, bNode *output_node void node_shader_gpu_bump_tex_coord(GPUMaterial *mat, bNode *node, GPUNodeLink **link) { if (node->branch_tag == 1) { - /* Add one time the value fo derivative to the input vector. */ + /* Add one time the value for derivative to the input vector. */ GPU_link(mat, "dfdx_v3", *link, link); } else if (node->branch_tag == 2) { - /* Add one time the value fo derivative to the input vector. */ + /* Add one time the value for derivative to the input vector. */ GPU_link(mat, "dfdy_v3", *link, link); } else { @@ -308,7 +306,7 @@ void node_shader_gpu_tex_mapping(GPUMaterial *mat, GPUNodeStack *in, GPUNodeStack *UNUSED(out)) { - NodeTexBase *base = node->storage; + NodeTexBase *base = (NodeTexBase *)node->storage; TexMapping *texmap = &base->tex_mapping; float domin = (texmap->flag & TEXMAP_CLIP_MIN) != 0; float domax = (texmap->flag & TEXMAP_CLIP_MAX) != 0; diff --git a/source/blender/nodes/shader/node_shader_util.h b/source/blender/nodes/shader/node_shader_util.hh index c647b86a19a..5a5b4f613f3 100644 --- a/source/blender/nodes/shader/node_shader_util.h +++ b/source/blender/nodes/shader/node_shader_util.hh @@ -23,29 +23,21 @@ #pragma once -#include <float.h> -#include <math.h> -#include <string.h> - -#include "MEM_guardedalloc.h" - -#include "DNA_ID.h" -#include "DNA_color_types.h" -#include "DNA_customdata_types.h" -#include "DNA_image_types.h" -#include "DNA_material_types.h" -#include "DNA_node_types.h" -#include "DNA_object_types.h" -#include "DNA_scene_types.h" -#include "DNA_texture_types.h" +#include <cfloat> +#include <cmath> +#include <cstring> #include "BLI_blenlib.h" +#include "BLI_color.hh" #include "BLI_math.h" #include "BLI_math_base_safe.h" +#include "BLI_math_vec_types.hh" #include "BLI_rand.h" #include "BLI_threads.h" #include "BLI_utildefines.h" +#include "BLT_translation.h" + #include "BKE_colorband.h" #include "BKE_colortools.h" #include "BKE_global.h" @@ -55,53 +47,46 @@ #include "BKE_node.h" #include "BKE_texture.h" -#include "NOD_shader.h" -#include "node_util.h" - -#include "BLT_translation.h" - -#include "IMB_colormanagement.h" +#include "DNA_ID.h" +#include "DNA_color_types.h" +#include "DNA_customdata_types.h" +#include "DNA_image_types.h" +#include "DNA_material_types.h" +#include "DNA_node_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_texture_types.h" -#include "RE_pipeline.h" -#include "RE_texture.h" +#include "FN_multi_function_builder.hh" #include "GPU_material.h" #include "GPU_texture.h" #include "GPU_uniform_buffer.h" -#ifdef __cplusplus -# include "FN_multi_function_builder.hh" +#include "IMB_colormanagement.h" -# include "NOD_multi_function.hh" -# include "NOD_socket_declarations.hh" +#include "MEM_guardedalloc.h" -# include "BLI_color.hh" -# include "BLI_float3.hh" +#include "NOD_multi_function.hh" +#include "NOD_shader.h" +#include "NOD_socket_declarations.hh" +#include "node_util.h" -extern "C" { -#endif +#include "RE_pipeline.h" +#include "RE_texture.h" bool sh_node_poll_default(struct bNodeType *ntype, struct bNodeTree *ntree, const char **r_disabled_hint); -void sh_node_type_base( - struct bNodeType *ntype, int type, const char *name, short nclass, short flag); -void sh_fn_node_type_base( - struct bNodeType *ntype, int type, const char *name, short nclass, short flag); +void sh_node_type_base(struct bNodeType *ntype, int type, const char *name, short nclass); +void sh_fn_node_type_base(struct bNodeType *ntype, int type, const char *name, short nclass); /* ********* exec data struct, remains internal *********** */ -typedef struct ShaderCallData { - /* Empty for now, may be reused if we convert shader to texture nodes. */ - int dummy; -} ShaderCallData; - -typedef struct XYZ_to_RGB /* Transposed #imbuf_xyz_to_rgb, passed as 3x vec3. */ +struct XYZ_to_RGB /* Transposed #imbuf_xyz_to_rgb, passed as 3x vec3. */ { float r[3], g[3], b[3]; -} XYZ_to_RGB; - -void nodestack_get_vec(float *in, short type_in, bNodeStack *ns); +}; void node_gpu_stack_from_data(struct GPUNodeStack *gs, int type, struct bNodeStack *ns); void node_data_from_gpu_stack(struct bNodeStack *ns, struct GPUNodeStack *gs); @@ -120,7 +105,3 @@ void ntreeExecGPUNodes(struct bNodeTreeExec *exec, struct GPUMaterial *mat, struct bNode *output_node); void get_XYZ_to_RGB_for_gpu(XYZ_to_RGB *data); - -#ifdef __cplusplus -} -#endif diff --git a/source/blender/nodes/shader/nodes/node_shader_add_shader.c b/source/blender/nodes/shader/nodes/node_shader_add_shader.cc index 12c138ac9d5..73d5c12ce96 100644 --- a/source/blender/nodes/shader/nodes/node_shader_add_shader.c +++ b/source/blender/nodes/shader/nodes/node_shader_add_shader.cc @@ -17,20 +17,16 @@ * All rights reserved. */ -#include "../node_shader_util.h" +#include "node_shader_util.hh" -/* **************** OUTPUT ******************** */ +namespace blender::nodes::node_shader_add_shader_cc { -static bNodeSocketTemplate sh_node_add_shader_in[] = { - {SOCK_SHADER, N_("Shader")}, - {SOCK_SHADER, N_("Shader")}, - {-1, ""}, -}; - -static bNodeSocketTemplate sh_node_add_shader_out[] = { - {SOCK_SHADER, N_("Shader")}, - {-1, ""}, -}; +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Shader>(N_("Shader")); + b.add_input<decl::Shader>(N_("Shader"), "Shader_001"); + b.add_output<decl::Shader>(N_("Shader")); +} static int node_shader_gpu_add_shader(GPUMaterial *mat, bNode *node, @@ -41,16 +37,18 @@ static int node_shader_gpu_add_shader(GPUMaterial *mat, return GPU_stack_link(mat, node, "node_add_shader", in, out); } +} // namespace blender::nodes::node_shader_add_shader_cc + /* node type definition */ -void register_node_type_sh_add_shader(void) +void register_node_type_sh_add_shader() { + namespace file_ns = blender::nodes::node_shader_add_shader_cc; + static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_ADD_SHADER, "Add Shader", NODE_CLASS_SHADER, 0); - node_type_socket_templates(&ntype, sh_node_add_shader_in, sh_node_add_shader_out); - node_type_init(&ntype, NULL); - node_type_storage(&ntype, "", NULL, NULL); - node_type_gpu(&ntype, node_shader_gpu_add_shader); + sh_node_type_base(&ntype, SH_NODE_ADD_SHADER, "Add Shader", NODE_CLASS_SHADER); + ntype.declare = file_ns::node_declare; + node_type_gpu(&ntype, file_ns::node_shader_gpu_add_shader); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_ambient_occlusion.c b/source/blender/nodes/shader/nodes/node_shader_ambient_occlusion.cc index abe80ebcefb..9c64594aab8 100644 --- a/source/blender/nodes/shader/nodes/node_shader_ambient_occlusion.c +++ b/source/blender/nodes/shader/nodes/node_shader_ambient_occlusion.cc @@ -17,22 +17,30 @@ * All rights reserved. */ -#include "../node_shader_util.h" +#include "node_shader_util.hh" -/* **************** OUTPUT ******************** */ +#include "UI_interface.h" +#include "UI_resources.h" -static bNodeSocketTemplate sh_node_ambient_occlusion_in[] = { - {SOCK_RGBA, N_("Color"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Distance"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1000.0f}, - {SOCK_VECTOR, N_("Normal"), 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, - {-1, ""}, -}; +namespace blender::nodes::node_shader_ambient_occlusion_cc { -static bNodeSocketTemplate sh_node_ambient_occlusion_out[] = { - {SOCK_RGBA, N_("Color"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("AO"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {-1, ""}, -}; +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Color>(N_("Color")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Float>(N_("Distance")).default_value(1.0f).min(0.0f).max(1000.0f); + b.add_input<decl::Vector>(N_("Normal")).min(-1.0f).max(1.0f).hide_value(); + b.add_output<decl::Color>(N_("Color")); + b.add_output<decl::Float>(N_("AO")); +} + +static void node_shader_buts_ambient_occlusion(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiItemR(layout, ptr, "samples", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(layout, ptr, "inside", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(layout, ptr, "only_local", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); +} static int node_shader_gpu_ambient_occlusion(GPUMaterial *mat, bNode *node, @@ -64,16 +72,20 @@ static void node_shader_init_ambient_occlusion(bNodeTree *UNUSED(ntree), bNode * node->custom2 = 0; } +} // namespace blender::nodes::node_shader_ambient_occlusion_cc + /* node type definition */ -void register_node_type_sh_ambient_occlusion(void) +void register_node_type_sh_ambient_occlusion() { + namespace file_ns = blender::nodes::node_shader_ambient_occlusion_cc; + static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_AMBIENT_OCCLUSION, "Ambient Occlusion", NODE_CLASS_INPUT, 0); - node_type_socket_templates(&ntype, sh_node_ambient_occlusion_in, sh_node_ambient_occlusion_out); - node_type_init(&ntype, node_shader_init_ambient_occlusion); - node_type_storage(&ntype, "", NULL, NULL); - node_type_gpu(&ntype, node_shader_gpu_ambient_occlusion); + sh_node_type_base(&ntype, SH_NODE_AMBIENT_OCCLUSION, "Ambient Occlusion", NODE_CLASS_INPUT); + ntype.declare = file_ns::node_declare; + ntype.draw_buttons = file_ns::node_shader_buts_ambient_occlusion; + node_type_init(&ntype, file_ns::node_shader_init_ambient_occlusion); + node_type_gpu(&ntype, file_ns::node_shader_gpu_ambient_occlusion); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_attribute.c b/source/blender/nodes/shader/nodes/node_shader_attribute.cc index 9b3122e38e0..cf1b49ba29c 100644 --- a/source/blender/nodes/shader/nodes/node_shader_attribute.c +++ b/source/blender/nodes/shader/nodes/node_shader_attribute.cc @@ -17,21 +17,30 @@ * All rights reserved. */ -#include "../node_shader_util.h" +#include "node_shader_util.hh" -/* **************** OUTPUT ******************** */ +#include "UI_interface.h" +#include "UI_resources.h" -static bNodeSocketTemplate sh_node_attribute_out[] = { - {SOCK_RGBA, N_("Color")}, - {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Fac"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_FACTOR}, - {SOCK_FLOAT, N_("Alpha"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_FACTOR}, - {-1, ""}, -}; +namespace blender::nodes::node_shader_attribute_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_output<decl::Color>(N_("Color")); + b.add_output<decl::Vector>(N_("Vector")); + b.add_output<decl::Float>(N_("Fac")); + b.add_output<decl::Float>(N_("Alpha")); +} + +static void node_shader_buts_attribute(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "attribute_type", UI_ITEM_R_SPLIT_EMPTY_NAME, IFACE_("Type"), ICON_NONE); + uiItemR(layout, ptr, "attribute_name", UI_ITEM_R_SPLIT_EMPTY_NAME, IFACE_("Name"), ICON_NONE); +} static void node_shader_init_attribute(bNodeTree *UNUSED(ntree), bNode *node) { - NodeShaderAttribute *attr = MEM_callocN(sizeof(NodeShaderAttribute), "NodeShaderAttribute"); + NodeShaderAttribute *attr = MEM_cnew<NodeShaderAttribute>("NodeShaderAttribute"); node->storage = attr; } @@ -41,7 +50,7 @@ static int node_shader_gpu_attribute(GPUMaterial *mat, GPUNodeStack *in, GPUNodeStack *out) { - NodeShaderAttribute *attr = node->storage; + NodeShaderAttribute *attr = static_cast<NodeShaderAttribute *>(node->storage); bool is_varying = attr->type == SHD_ATTRIBUTE_GEOMETRY; if (GPU_material_is_volume_shader(mat) && is_varying) { @@ -73,25 +82,30 @@ static int node_shader_gpu_attribute(GPUMaterial *mat, GPU_stack_link(mat, node, "node_attribute", in, out, cd_attr); - /* for each output. */ - for (int i = 0; sh_node_attribute_out[i].type != -1; i++) { + int i; + LISTBASE_FOREACH_INDEX (bNodeSocket *, sock, &node->outputs, i) { node_shader_gpu_bump_tex_coord(mat, node, &out[i].link); } return 1; } +} // namespace blender::nodes::node_shader_attribute_cc + /* node type definition */ -void register_node_type_sh_attribute(void) +void register_node_type_sh_attribute() { + namespace file_ns = blender::nodes::node_shader_attribute_cc; + static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_ATTRIBUTE, "Attribute", NODE_CLASS_INPUT, 0); - node_type_socket_templates(&ntype, NULL, sh_node_attribute_out); - node_type_init(&ntype, node_shader_init_attribute); + sh_node_type_base(&ntype, SH_NODE_ATTRIBUTE, "Attribute", NODE_CLASS_INPUT); + ntype.declare = file_ns::node_declare; + ntype.draw_buttons = file_ns::node_shader_buts_attribute; + node_type_init(&ntype, file_ns::node_shader_init_attribute); node_type_storage( &ntype, "NodeShaderAttribute", node_free_standard_storage, node_copy_standard_storage); - node_type_gpu(&ntype, node_shader_gpu_attribute); + node_type_gpu(&ntype, file_ns::node_shader_gpu_attribute); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_background.c b/source/blender/nodes/shader/nodes/node_shader_background.cc index 4301a9f716f..3b9e71cf842 100644 --- a/source/blender/nodes/shader/nodes/node_shader_background.c +++ b/source/blender/nodes/shader/nodes/node_shader_background.cc @@ -17,21 +17,16 @@ * All rights reserved. */ -#include "../node_shader_util.h" +#include "node_shader_util.hh" -/* **************** OUTPUT ******************** */ +namespace blender::nodes::node_shader_background_cc { -static bNodeSocketTemplate sh_node_background_in[] = { - {SOCK_RGBA, N_("Color"), 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Strength"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1000000.0f}, - {SOCK_FLOAT, N_("Weight"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, PROP_NONE, SOCK_UNAVAIL}, - {-1, ""}, -}; - -static bNodeSocketTemplate sh_node_background_out[] = { - {SOCK_SHADER, N_("Background")}, - {-1, ""}, -}; +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Color>(N_("Color")).default_value({0.8f, 0.8f, 0.8f, 1.0f}); + b.add_input<decl::Float>(N_("Strength")).default_value(1.0f).min(0.0f).max(1000000.0f); + b.add_output<decl::Shader>(N_("Background")); +} static int node_shader_gpu_background(GPUMaterial *mat, bNode *node, @@ -42,16 +37,18 @@ static int node_shader_gpu_background(GPUMaterial *mat, return GPU_stack_link(mat, node, "node_background", in, out); } +} // namespace blender::nodes::node_shader_background_cc + /* node type definition */ -void register_node_type_sh_background(void) +void register_node_type_sh_background() { + namespace file_ns = blender::nodes::node_shader_background_cc; + static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_BACKGROUND, "Background", NODE_CLASS_SHADER, 0); - node_type_socket_templates(&ntype, sh_node_background_in, sh_node_background_out); - node_type_init(&ntype, NULL); - node_type_storage(&ntype, "", NULL, NULL); - node_type_gpu(&ntype, node_shader_gpu_background); + sh_node_type_base(&ntype, SH_NODE_BACKGROUND, "Background", NODE_CLASS_SHADER); + ntype.declare = file_ns::node_declare; + node_type_gpu(&ntype, file_ns::node_shader_gpu_background); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_bevel.c b/source/blender/nodes/shader/nodes/node_shader_bevel.cc index d6bdda30e94..dfde18d8e2a 100644 --- a/source/blender/nodes/shader/nodes/node_shader_bevel.c +++ b/source/blender/nodes/shader/nodes/node_shader_bevel.cc @@ -17,20 +17,24 @@ * All rights reserved. */ -#include "../node_shader_util.h" +#include "node_shader_util.hh" -/* **************** OUTPUT ******************** */ +#include "UI_interface.h" +#include "UI_resources.h" -static bNodeSocketTemplate sh_node_bevel_in[] = { - {SOCK_FLOAT, N_("Radius"), 0.05f, 0.0f, 0.0f, 0.0f, 0.0f, 1000.0f}, - {SOCK_VECTOR, N_("Normal"), 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, - {-1, ""}, -}; +namespace blender::nodes::node_shader_bevel_cc { -static bNodeSocketTemplate sh_node_bevel_out[] = { - {SOCK_VECTOR, N_("Normal"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {-1, ""}, -}; +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Float>(N_("Radius")).default_value(0.05f).min(0.0f).max(1000.0f); + b.add_input<decl::Vector>(N_("Normal")).hide_value(); + b.add_output<decl::Vector>(N_("Normal")); +} + +static void node_shader_buts_bevel(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "samples", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); +} static void node_shader_init_bevel(bNodeTree *UNUSED(ntree), bNode *node) { @@ -50,16 +54,20 @@ static int gpu_shader_bevel(GPUMaterial *mat, return GPU_stack_link(mat, node, "node_bevel", in, out); } +} // namespace blender::nodes::node_shader_bevel_cc + /* node type definition */ -void register_node_type_sh_bevel(void) +void register_node_type_sh_bevel() { + namespace file_ns = blender::nodes::node_shader_bevel_cc; + static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_BEVEL, "Bevel", NODE_CLASS_INPUT, 0); - node_type_socket_templates(&ntype, sh_node_bevel_in, sh_node_bevel_out); - node_type_init(&ntype, node_shader_init_bevel); - node_type_storage(&ntype, "", NULL, NULL); - node_type_gpu(&ntype, gpu_shader_bevel); + sh_node_type_base(&ntype, SH_NODE_BEVEL, "Bevel", NODE_CLASS_INPUT); + ntype.declare = file_ns::node_declare; + ntype.draw_buttons = file_ns::node_shader_buts_bevel; + node_type_init(&ntype, file_ns::node_shader_init_bevel); + node_type_gpu(&ntype, file_ns::gpu_shader_bevel); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_blackbody.c b/source/blender/nodes/shader/nodes/node_shader_blackbody.cc index 95c35affc27..85e2ed08403 100644 --- a/source/blender/nodes/shader/nodes/node_shader_blackbody.c +++ b/source/blender/nodes/shader/nodes/node_shader_blackbody.cc @@ -17,18 +17,15 @@ * All rights reserved. */ -#include "../node_shader_util.h" +#include "node_shader_util.hh" -/* **************** Blackbody ******************** */ -static bNodeSocketTemplate sh_node_blackbody_in[] = { - {SOCK_FLOAT, N_("Temperature"), 1500.0f, 0.0f, 0.0f, 0.0f, 800.0f, 12000.0f}, - {-1, ""}, -}; +namespace blender::nodes::node_shader_blackbody_cc { -static bNodeSocketTemplate sh_node_blackbody_out[] = { - {SOCK_RGBA, N_("Color")}, - {-1, ""}, -}; +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Float>(N_("Temperature")).default_value(1500.0f).min(800.0f).max(12000.0f); + b.add_output<decl::Color>(N_("Color")); +} static int node_shader_gpu_blackbody(GPUMaterial *mat, bNode *node, @@ -37,7 +34,7 @@ static int node_shader_gpu_blackbody(GPUMaterial *mat, GPUNodeStack *out) { const int size = CM_TABLE + 1; - float *data = MEM_mallocN(sizeof(float) * size * 4, "blackbody texture"); + float *data = static_cast<float *>(MEM_mallocN(sizeof(float) * size * 4, "blackbody texture")); blackbody_temperature_to_rgb_table(data, size, 965.0f, 12000.0f); @@ -47,17 +44,19 @@ static int node_shader_gpu_blackbody(GPUMaterial *mat, return GPU_stack_link(mat, node, "node_blackbody", in, out, ramp_texture, GPU_constant(&layer)); } +} // namespace blender::nodes::node_shader_blackbody_cc + /* node type definition */ -void register_node_type_sh_blackbody(void) +void register_node_type_sh_blackbody() { + namespace file_ns = blender::nodes::node_shader_blackbody_cc; + static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_BLACKBODY, "Blackbody", NODE_CLASS_CONVERTER, 0); + sh_node_type_base(&ntype, SH_NODE_BLACKBODY, "Blackbody", NODE_CLASS_CONVERTER); + ntype.declare = file_ns::node_declare; node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); - node_type_socket_templates(&ntype, sh_node_blackbody_in, sh_node_blackbody_out); - node_type_init(&ntype, NULL); - node_type_storage(&ntype, "", NULL, NULL); - node_type_gpu(&ntype, node_shader_gpu_blackbody); + node_type_gpu(&ntype, file_ns::node_shader_gpu_blackbody); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_brightness.c b/source/blender/nodes/shader/nodes/node_shader_brightness.cc index 4f375c666de..66bfaba2785 100644 --- a/source/blender/nodes/shader/nodes/node_shader_brightness.c +++ b/source/blender/nodes/shader/nodes/node_shader_brightness.cc @@ -17,21 +17,17 @@ * All rights reserved. */ -#include "node_shader_util.h" +#include "node_shader_util.hh" -/* **************** Bright and contrast ******************** */ +namespace blender::nodes::node_shader_brightness_cc { -static bNodeSocketTemplate sh_node_brightcontrast_in[] = { - {SOCK_RGBA, N_("Color"), 1.0f, 1.0f, 1.0f, 1.0f}, - {SOCK_FLOAT, N_("Bright"), 0.0f, 0.0f, 0.0f, 0.0f, -100.0f, 100.0f, PROP_NONE}, - {SOCK_FLOAT, N_("Contrast"), 0.0f, 0.0f, 0.0f, 0.0f, -100.0f, 100.0f, PROP_NONE}, - {-1, ""}, -}; - -static bNodeSocketTemplate sh_node_brightcontrast_out[] = { - {SOCK_RGBA, N_("Color")}, - {-1, ""}, -}; +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Color>(N_("Color")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Float>(N_("Bright")).default_value(0.0f).min(-100.0f).max(100.0f); + b.add_input<decl::Float>(N_("Contrast")).default_value(0.0f).min(-100.0f).max(100.0f); + b.add_output<decl::Color>(N_("Color")); +} static int gpu_shader_brightcontrast(GPUMaterial *mat, bNode *node, @@ -42,15 +38,17 @@ static int gpu_shader_brightcontrast(GPUMaterial *mat, return GPU_stack_link(mat, node, "brightness_contrast", in, out); } -void register_node_type_sh_brightcontrast(void) +} // namespace blender::nodes::node_shader_brightness_cc + +void register_node_type_sh_brightcontrast() { + namespace file_ns = blender::nodes::node_shader_brightness_cc; + static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_BRIGHTCONTRAST, "Bright/Contrast", NODE_CLASS_OP_COLOR, 0); - node_type_socket_templates(&ntype, sh_node_brightcontrast_in, sh_node_brightcontrast_out); - node_type_init(&ntype, NULL); - node_type_storage(&ntype, "", NULL, NULL); - node_type_gpu(&ntype, gpu_shader_brightcontrast); + sh_node_type_base(&ntype, SH_NODE_BRIGHTCONTRAST, "Bright/Contrast", NODE_CLASS_OP_COLOR); + ntype.declare = file_ns::node_declare; + node_type_gpu(&ntype, file_ns::gpu_shader_brightcontrast); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_anisotropic.c b/source/blender/nodes/shader/nodes/node_shader_bsdf_anisotropic.c deleted file mode 100644 index 7c080337838..00000000000 --- a/source/blender/nodes/shader/nodes/node_shader_bsdf_anisotropic.c +++ /dev/null @@ -1,80 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2005 Blender Foundation. - * All rights reserved. - */ - -#include "../node_shader_util.h" - -/* **************** OUTPUT ******************** */ - -static bNodeSocketTemplate sh_node_bsdf_anisotropic_in[] = { - {SOCK_RGBA, N_("Color"), 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Roughness"), 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_FLOAT, N_("Anisotropy"), 0.5f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f}, - {SOCK_FLOAT, N_("Rotation"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_VECTOR, N_("Normal"), 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, - {SOCK_VECTOR, N_("Tangent"), 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, - {SOCK_FLOAT, N_("Weight"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, PROP_NONE, SOCK_UNAVAIL}, - {-1, ""}, -}; - -static bNodeSocketTemplate sh_node_bsdf_anisotropic_out[] = { - {SOCK_SHADER, N_("BSDF")}, - {-1, ""}, -}; - -static void node_shader_init_anisotropic(bNodeTree *UNUSED(ntree), bNode *node) -{ - node->custom1 = SHD_GLOSSY_GGX; -} - -static int node_shader_gpu_bsdf_anisotropic(GPUMaterial *mat, - bNode *node, - bNodeExecData *UNUSED(execdata), - GPUNodeStack *in, - GPUNodeStack *out) -{ - if (!in[4].link) { - GPU_link(mat, "world_normals_get", &in[4].link); - } - - GPU_material_flag_set(mat, GPU_MATFLAG_GLOSSY); - - float use_multi_scatter = (node->custom1 == SHD_GLOSSY_MULTI_GGX) ? 1.0f : 0.0f; - - GPU_stack_link(mat, node, "node_bsdf_anisotropic", in, out); - - GPU_stack_eval_link( - mat, node, "node_bsdf_anisotropic_eval", in, out, GPU_constant(&use_multi_scatter)); - - return true; -} - -/* node type definition */ -void register_node_type_sh_bsdf_anisotropic(void) -{ - static bNodeType ntype; - - sh_node_type_base(&ntype, SH_NODE_BSDF_ANISOTROPIC, "Anisotropic BSDF", NODE_CLASS_SHADER, 0); - node_type_socket_templates(&ntype, sh_node_bsdf_anisotropic_in, sh_node_bsdf_anisotropic_out); - node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); - node_type_init(&ntype, node_shader_init_anisotropic); - node_type_storage(&ntype, "", NULL, NULL); - node_type_gpu(&ntype, node_shader_gpu_bsdf_anisotropic); - - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_anisotropic.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_anisotropic.cc new file mode 100644 index 00000000000..3f0749ab2af --- /dev/null +++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_anisotropic.cc @@ -0,0 +1,96 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2005 Blender Foundation. + * All rights reserved. + */ + +#include "node_shader_util.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes::node_shader_bsdf_anisotropic_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Color>(N_("Color")).default_value({0.8f, 0.8f, 0.8f, 1.0f}); + b.add_input<decl::Float>(N_("Roughness")) + .default_value(0.5f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("Anisotropy")).default_value(0.5f).min(-1.0f).max(1.0f); + b.add_input<decl::Float>(N_("Rotation")) + .default_value(0.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Vector>(N_("Normal")).hide_value(); + b.add_input<decl::Vector>(N_("Tangent")).hide_value(); + b.add_output<decl::Shader>(N_("BSDF")); +} + +static void node_shader_buts_anisotropic(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "distribution", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); +} + +static void node_shader_init_anisotropic(bNodeTree *UNUSED(ntree), bNode *node) +{ + node->custom1 = SHD_GLOSSY_GGX; +} + +static int node_shader_gpu_bsdf_anisotropic(GPUMaterial *mat, + bNode *node, + bNodeExecData *UNUSED(execdata), + GPUNodeStack *in, + GPUNodeStack *out) +{ + if (!in[4].link) { + GPU_link(mat, "world_normals_get", &in[4].link); + } + + GPU_material_flag_set(mat, GPU_MATFLAG_GLOSSY); + + float use_multi_scatter = (node->custom1 == SHD_GLOSSY_MULTI_GGX) ? 1.0f : 0.0f; + + return GPU_stack_link(mat, + node, + "node_bsdf_anisotropic", + in, + out, + GPU_constant(&use_multi_scatter), + GPU_constant(&node->ssr_id)); +} + +} // namespace blender::nodes::node_shader_bsdf_anisotropic_cc + +/* node type definition */ +void register_node_type_sh_bsdf_anisotropic() +{ + namespace file_ns = blender::nodes::node_shader_bsdf_anisotropic_cc; + + static bNodeType ntype; + + sh_node_type_base(&ntype, SH_NODE_BSDF_ANISOTROPIC, "Anisotropic BSDF", NODE_CLASS_SHADER); + ntype.declare = file_ns::node_declare; + ntype.draw_buttons = file_ns::node_shader_buts_anisotropic; + node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); + node_type_init(&ntype, file_ns::node_shader_init_anisotropic); + node_type_gpu(&ntype, file_ns::node_shader_gpu_bsdf_anisotropic); + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_diffuse.c b/source/blender/nodes/shader/nodes/node_shader_bsdf_diffuse.cc index 3aa3b70025e..5848ca76cdd 100644 --- a/source/blender/nodes/shader/nodes/node_shader_bsdf_diffuse.c +++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_diffuse.cc @@ -17,22 +17,21 @@ * All rights reserved. */ -#include "../node_shader_util.h" +#include "node_shader_util.hh" -/* **************** OUTPUT ******************** */ +namespace blender::nodes::node_shader_bsdf_diffuse_cc { -static bNodeSocketTemplate sh_node_bsdf_diffuse_in[] = { - {SOCK_RGBA, N_("Color"), 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Roughness"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_VECTOR, N_("Normal"), 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, - {SOCK_FLOAT, N_("Weight"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, PROP_NONE, SOCK_UNAVAIL}, - {-1, ""}, -}; - -static bNodeSocketTemplate sh_node_bsdf_diffuse_out[] = { - {SOCK_SHADER, N_("BSDF")}, - {-1, ""}, -}; +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Color>(N_("Color")).default_value({0.8f, 0.8f, 0.8f, 1.0f}); + b.add_input<decl::Float>(N_("Roughness")) + .default_value(0.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Vector>(N_("Normal")).hide_value(); + b.add_output<decl::Shader>(N_("BSDF")); +} static int node_shader_gpu_bsdf_diffuse(GPUMaterial *mat, bNode *node, @@ -46,21 +45,22 @@ static int node_shader_gpu_bsdf_diffuse(GPUMaterial *mat, GPU_material_flag_set(mat, GPU_MATFLAG_DIFFUSE); - GPU_stack_link(mat, node, "node_bsdf_diffuse", in, out); - return GPU_stack_eval_link(mat, node, "node_bsdf_diffuse_eval", in, out); + return GPU_stack_link(mat, node, "node_bsdf_diffuse", in, out); } +} // namespace blender::nodes::node_shader_bsdf_diffuse_cc + /* node type definition */ -void register_node_type_sh_bsdf_diffuse(void) +void register_node_type_sh_bsdf_diffuse() { + namespace file_ns = blender::nodes::node_shader_bsdf_diffuse_cc; + static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_BSDF_DIFFUSE, "Diffuse BSDF", NODE_CLASS_SHADER, 0); - node_type_socket_templates(&ntype, sh_node_bsdf_diffuse_in, sh_node_bsdf_diffuse_out); + sh_node_type_base(&ntype, SH_NODE_BSDF_DIFFUSE, "Diffuse BSDF", NODE_CLASS_SHADER); + ntype.declare = file_ns::node_declare; node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); - node_type_init(&ntype, NULL); - node_type_storage(&ntype, "", NULL, NULL); - node_type_gpu(&ntype, node_shader_gpu_bsdf_diffuse); + node_type_gpu(&ntype, file_ns::node_shader_gpu_bsdf_diffuse); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_glass.c b/source/blender/nodes/shader/nodes/node_shader_bsdf_glass.c deleted file mode 100644 index e164e32723e..00000000000 --- a/source/blender/nodes/shader/nodes/node_shader_bsdf_glass.c +++ /dev/null @@ -1,96 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2005 Blender Foundation. - * All rights reserved. - */ - -#include "../node_shader_util.h" - -/* **************** OUTPUT ******************** */ - -static bNodeSocketTemplate sh_node_bsdf_glass_in[] = { - {SOCK_RGBA, N_("Color"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Roughness"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_FLOAT, N_("IOR"), 1.45f, 0.0f, 0.0f, 0.0f, 0.0f, 1000.0f}, - {SOCK_VECTOR, N_("Normal"), 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, - {SOCK_FLOAT, N_("Weight"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, PROP_NONE, SOCK_UNAVAIL}, - {-1, ""}, -}; - -static bNodeSocketTemplate sh_node_bsdf_glass_out[] = { - {SOCK_SHADER, N_("BSDF")}, - {-1, ""}, -}; - -static void node_shader_init_glass(bNodeTree *UNUSED(ntree), bNode *node) -{ - node->custom1 = SHD_GLOSSY_BECKMANN; -} - -static int node_shader_gpu_bsdf_glass(GPUMaterial *mat, - bNode *node, - bNodeExecData *UNUSED(execdata), - GPUNodeStack *in, - GPUNodeStack *out) -{ - if (!in[3].link) { - GPU_link(mat, "world_normals_get", &in[3].link); - } - - if (node->custom1 == SHD_GLOSSY_SHARP) { - GPU_link(mat, "set_value_zero", &in[1].link); - } - - GPU_material_flag_set(mat, GPU_MATFLAG_GLOSSY | GPU_MATFLAG_REFRACT); - - float use_multi_scatter = (node->custom1 == SHD_GLOSSY_MULTI_GGX) ? 1.0f : 0.0f; - - GPUNodeLink *reflection_weight; - GPUNodeLink *refraction_weight; - - GPU_stack_link(mat, - node, - "node_bsdf_glass", - in, - out, - GPU_constant(&use_multi_scatter), - &reflection_weight, - &refraction_weight); - - return GPU_stack_eval_link(mat, - node, - "node_bsdf_glass_eval", - in, - out, - GPU_constant(&use_multi_scatter), - reflection_weight, - refraction_weight); -} - -/* node type definition */ -void register_node_type_sh_bsdf_glass(void) -{ - static bNodeType ntype; - - sh_node_type_base(&ntype, SH_NODE_BSDF_GLASS, "Glass BSDF", NODE_CLASS_SHADER, 0); - node_type_socket_templates(&ntype, sh_node_bsdf_glass_in, sh_node_bsdf_glass_out); - node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); - node_type_init(&ntype, node_shader_init_glass); - node_type_storage(&ntype, "", NULL, NULL); - node_type_gpu(&ntype, node_shader_gpu_bsdf_glass); - - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_glass.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_glass.cc new file mode 100644 index 00000000000..47d4b87198b --- /dev/null +++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_glass.cc @@ -0,0 +1,85 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2005 Blender Foundation. + * All rights reserved. + */ + +#include "node_shader_util.hh" + +namespace blender::nodes::node_shader_bsdf_glass_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Color>(N_("Color")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Float>(N_("Roughness")) + .default_value(0.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("IOR")).default_value(1.45f).min(0.0f).max(1000.0f); + b.add_input<decl::Vector>(N_("Normal")).hide_value(); + b.add_output<decl::Shader>(N_("BSDF")); +} + +static void node_shader_init_glass(bNodeTree *UNUSED(ntree), bNode *node) +{ + node->custom1 = SHD_GLOSSY_BECKMANN; +} + +static int node_shader_gpu_bsdf_glass(GPUMaterial *mat, + bNode *node, + bNodeExecData *UNUSED(execdata), + GPUNodeStack *in, + GPUNodeStack *out) +{ + if (!in[3].link) { + GPU_link(mat, "world_normals_get", &in[3].link); + } + + if (node->custom1 == SHD_GLOSSY_SHARP) { + GPU_link(mat, "set_value_zero", &in[1].link); + } + + GPU_material_flag_set(mat, (eGPUMatFlag)(GPU_MATFLAG_GLOSSY | GPU_MATFLAG_REFRACT)); + + float use_multi_scatter = (node->custom1 == SHD_GLOSSY_MULTI_GGX) ? 1.0f : 0.0f; + + return GPU_stack_link(mat, + node, + "node_bsdf_glass", + in, + out, + GPU_constant(&use_multi_scatter), + GPU_constant(&node->ssr_id)); +} + +} // namespace blender::nodes::node_shader_bsdf_glass_cc + +/* node type definition */ +void register_node_type_sh_bsdf_glass() +{ + namespace file_ns = blender::nodes::node_shader_bsdf_glass_cc; + + static bNodeType ntype; + + sh_node_type_base(&ntype, SH_NODE_BSDF_GLASS, "Glass BSDF", NODE_CLASS_SHADER); + ntype.declare = file_ns::node_declare; + node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); + node_type_init(&ntype, file_ns::node_shader_init_glass); + node_type_gpu(&ntype, file_ns::node_shader_gpu_bsdf_glass); + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_glossy.c b/source/blender/nodes/shader/nodes/node_shader_bsdf_glossy.cc index e234dafb98e..03a3e634f56 100644 --- a/source/blender/nodes/shader/nodes/node_shader_bsdf_glossy.c +++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_glossy.cc @@ -17,22 +17,21 @@ * All rights reserved. */ -#include "../node_shader_util.h" +#include "node_shader_util.hh" -/* **************** OUTPUT ******************** */ +namespace blender::nodes::node_shader_bsdf_glossy_cc { -static bNodeSocketTemplate sh_node_bsdf_glossy_in[] = { - {SOCK_RGBA, N_("Color"), 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Roughness"), 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_VECTOR, N_("Normal"), 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, - {SOCK_FLOAT, N_("Weight"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, PROP_NONE, SOCK_UNAVAIL}, - {-1, ""}, -}; - -static bNodeSocketTemplate sh_node_bsdf_glossy_out[] = { - {SOCK_SHADER, N_("BSDF")}, - {-1, ""}, -}; +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Color>(N_("Color")).default_value({0.8f, 0.8f, 0.8f, 1.0f}); + b.add_input<decl::Float>(N_("Roughness")) + .default_value(0.5f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Vector>(N_("Normal")).hide_value(); + b.add_output<decl::Shader>(N_("BSDF")); +} static void node_shader_init_glossy(bNodeTree *UNUSED(ntree), bNode *node) { @@ -57,22 +56,29 @@ static int node_shader_gpu_bsdf_glossy(GPUMaterial *mat, float use_multi_scatter = (node->custom1 == SHD_GLOSSY_MULTI_GGX) ? 1.0f : 0.0f; - GPU_stack_link(mat, node, "node_bsdf_glossy", in, out); - return GPU_stack_eval_link( - mat, node, "node_bsdf_glossy_eval", in, out, GPU_constant(&use_multi_scatter)); + return GPU_stack_link(mat, + node, + "node_bsdf_glossy", + in, + out, + GPU_constant(&use_multi_scatter), + GPU_constant(&node->ssr_id)); } +} // namespace blender::nodes::node_shader_bsdf_glossy_cc + /* node type definition */ -void register_node_type_sh_bsdf_glossy(void) +void register_node_type_sh_bsdf_glossy() { + namespace file_ns = blender::nodes::node_shader_bsdf_glossy_cc; + static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_BSDF_GLOSSY, "Glossy BSDF", NODE_CLASS_SHADER, 0); - node_type_socket_templates(&ntype, sh_node_bsdf_glossy_in, sh_node_bsdf_glossy_out); + sh_node_type_base(&ntype, SH_NODE_BSDF_GLOSSY, "Glossy BSDF", NODE_CLASS_SHADER); + ntype.declare = file_ns::node_declare; node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); - node_type_init(&ntype, node_shader_init_glossy); - node_type_storage(&ntype, "", NULL, NULL); - node_type_gpu(&ntype, node_shader_gpu_bsdf_glossy); + node_type_init(&ntype, file_ns::node_shader_init_glossy); + node_type_gpu(&ntype, file_ns::node_shader_gpu_bsdf_glossy); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_hair.c b/source/blender/nodes/shader/nodes/node_shader_bsdf_hair.cc index 1f07cb1d202..3be2bd22f60 100644 --- a/source/blender/nodes/shader/nodes/node_shader_bsdf_hair.c +++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_hair.cc @@ -17,24 +17,39 @@ * All rights reserved. */ -#include "../node_shader_util.h" +#include "node_shader_util.hh" -/* **************** OUTPUT ******************** */ +#include "UI_interface.h" +#include "UI_resources.h" -static bNodeSocketTemplate sh_node_bsdf_hair_in[] = { - {SOCK_RGBA, N_("Color"), 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Offset"), 0.0f, 0.0f, 0.0f, 0.0f, -M_PI_2, M_PI_2, PROP_ANGLE}, - {SOCK_FLOAT, N_("RoughnessU"), 0.1f, 0.1f, 0.1f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_FLOAT, N_("RoughnessV"), 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_VECTOR, N_("Tangent"), 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, - {SOCK_FLOAT, N_("Weight"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, PROP_NONE, SOCK_UNAVAIL}, - {-1, ""}, -}; +namespace blender::nodes::node_shader_bsdf_hair_cc { -static bNodeSocketTemplate sh_node_bsdf_hair_out[] = { - {SOCK_SHADER, N_("BSDF")}, - {-1, ""}, -}; +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Color>(N_("Color")).default_value({0.8f, 0.8f, 0.8f, 1.0f}); + b.add_input<decl::Float>(N_("Offset")) + .default_value(0.0f) + .min(-M_PI_2) + .max(M_PI_2) + .subtype(PROP_ANGLE); + b.add_input<decl::Float>(N_("RoughnessU")) + .default_value(0.1f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("RoughnessV")) + .default_value(1.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Vector>(N_("Tangent")).hide_value(); + b.add_output<decl::Shader>(N_("BSDF")); +} + +static void node_shader_buts_hair(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "component", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); +} static int node_shader_gpu_bsdf_hair(GPUMaterial *mat, bNode *node, @@ -45,17 +60,20 @@ static int node_shader_gpu_bsdf_hair(GPUMaterial *mat, return GPU_stack_link(mat, node, "node_bsdf_hair", in, out); } +} // namespace blender::nodes::node_shader_bsdf_hair_cc + /* node type definition */ -void register_node_type_sh_bsdf_hair(void) +void register_node_type_sh_bsdf_hair() { + namespace file_ns = blender::nodes::node_shader_bsdf_hair_cc; + static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_BSDF_HAIR, "Hair BSDF", NODE_CLASS_SHADER, 0); - node_type_socket_templates(&ntype, sh_node_bsdf_hair_in, sh_node_bsdf_hair_out); + sh_node_type_base(&ntype, SH_NODE_BSDF_HAIR, "Hair BSDF", NODE_CLASS_SHADER); + ntype.declare = file_ns::node_declare; + ntype.draw_buttons = file_ns::node_shader_buts_hair; node_type_size(&ntype, 150, 60, 200); - node_type_init(&ntype, NULL); - node_type_storage(&ntype, "", NULL, NULL); - node_type_gpu(&ntype, node_shader_gpu_bsdf_hair); + node_type_gpu(&ntype, file_ns::node_shader_gpu_bsdf_hair); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_hair_principled.c b/source/blender/nodes/shader/nodes/node_shader_bsdf_hair_principled.c deleted file mode 100644 index d9bcbbcd7a4..00000000000 --- a/source/blender/nodes/shader/nodes/node_shader_bsdf_hair_principled.c +++ /dev/null @@ -1,135 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2018 Blender Foundation. - * All rights reserved. - */ - -#include "../node_shader_util.h" - -/* **************** OUTPUT ******************** */ - -/* Color, melanin and absorption coefficient default to approximately same brownish hair. */ -static bNodeSocketTemplate sh_node_bsdf_hair_principled_in[] = { - {SOCK_RGBA, N_("Color"), 0.017513f, 0.005763f, 0.002059f, 1.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Melanin"), 0.8f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_FLOAT, N_("Melanin Redness"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_RGBA, N_("Tint"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f}, - {SOCK_VECTOR, N_("Absorption Coefficient"), 0.245531f, 0.52f, 1.365f, 0.0f, 0.0f, 1000.0f}, - {SOCK_FLOAT, N_("Roughness"), 0.3f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_FLOAT, N_("Radial Roughness"), 0.3f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_FLOAT, N_("Coat"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_FLOAT, N_("IOR"), 1.55f, 0.0f, 0.0f, 0.0f, 0.0f, 1000.0f}, - {SOCK_FLOAT, - N_("Offset"), - 2.0f * ((float)M_PI) / 180.0f, - 0.0f, - 0.0f, - 0.0f, - -M_PI_2, - M_PI_2, - PROP_ANGLE}, - {SOCK_FLOAT, N_("Random Color"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_FLOAT, N_("Random Roughness"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_FLOAT, N_("Random"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, - {SOCK_FLOAT, N_("Weight"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, PROP_NONE, SOCK_UNAVAIL}, - {-1, ""}, -}; - -static bNodeSocketTemplate sh_node_bsdf_hair_principled_out[] = { - {SOCK_SHADER, N_("BSDF")}, - {-1, ""}, -}; - -/* Initialize the custom Parametrization property to Color. */ -static void node_shader_init_hair_principled(bNodeTree *UNUSED(ntree), bNode *node) -{ - node->custom1 = SHD_PRINCIPLED_HAIR_REFLECTANCE; -} - -/* Triggers (in)visibility of some sockets when changing Parametrization. */ -static void node_shader_update_hair_principled(bNodeTree *UNUSED(ntree), bNode *node) -{ - bNodeSocket *sock; - int parametrization = node->custom1; - - for (sock = node->inputs.first; sock; sock = sock->next) { - if (STREQ(sock->name, "Color")) { - if (parametrization == SHD_PRINCIPLED_HAIR_REFLECTANCE) { - sock->flag &= ~SOCK_UNAVAIL; - } - else { - sock->flag |= SOCK_UNAVAIL; - } - } - else if (STREQ(sock->name, "Melanin")) { - if (parametrization == SHD_PRINCIPLED_HAIR_PIGMENT_CONCENTRATION) { - sock->flag &= ~SOCK_UNAVAIL; - } - else { - sock->flag |= SOCK_UNAVAIL; - } - } - else if (STREQ(sock->name, "Melanin Redness")) { - if (parametrization == SHD_PRINCIPLED_HAIR_PIGMENT_CONCENTRATION) { - sock->flag &= ~SOCK_UNAVAIL; - } - else { - sock->flag |= SOCK_UNAVAIL; - } - } - else if (STREQ(sock->name, "Tint")) { - if (parametrization == SHD_PRINCIPLED_HAIR_PIGMENT_CONCENTRATION) { - sock->flag &= ~SOCK_UNAVAIL; - } - else { - sock->flag |= SOCK_UNAVAIL; - } - } - else if (STREQ(sock->name, "Absorption Coefficient")) { - if (parametrization == SHD_PRINCIPLED_HAIR_DIRECT_ABSORPTION) { - sock->flag &= ~SOCK_UNAVAIL; - } - else { - sock->flag |= SOCK_UNAVAIL; - } - } - else if (STREQ(sock->name, "Random Color")) { - if (parametrization == SHD_PRINCIPLED_HAIR_PIGMENT_CONCENTRATION) { - sock->flag &= ~SOCK_UNAVAIL; - } - else { - sock->flag |= SOCK_UNAVAIL; - } - } - } -} - -/* node type definition */ -void register_node_type_sh_bsdf_hair_principled(void) -{ - static bNodeType ntype; - - sh_node_type_base( - &ntype, SH_NODE_BSDF_HAIR_PRINCIPLED, "Principled Hair BSDF", NODE_CLASS_SHADER, 0); - node_type_socket_templates( - &ntype, sh_node_bsdf_hair_principled_in, sh_node_bsdf_hair_principled_out); - node_type_size_preset(&ntype, NODE_SIZE_LARGE); - node_type_init(&ntype, node_shader_init_hair_principled); - node_type_storage(&ntype, "", NULL, NULL); - node_type_update(&ntype, node_shader_update_hair_principled); - - nodeRegisterType(&ntype); -} 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 new file mode 100644 index 00000000000..7062888b5fb --- /dev/null +++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_hair_principled.cc @@ -0,0 +1,145 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2018 Blender Foundation. + * All rights reserved. + */ + +#include "node_shader_util.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes::node_shader_bsdf_hair_principled_cc { + +/* Color, melanin and absorption coefficient default to approximately same brownish hair. */ +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Color>(N_("Color")).default_value({0.017513f, 0.005763f, 0.002059f, 1.0f}); + b.add_input<decl::Float>(N_("Melanin")) + .default_value(0.8f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("Melanin Redness")) + .default_value(1.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Color>(N_("Tint")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Vector>(N_("Absorption Coefficient")) + .default_value({0.245531f, 0.52f, 1.365f}) + .min(0.0f) + .max(1000.0f); + b.add_input<decl::Float>(N_("Roughness")) + .default_value(0.3f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("Radial Roughness")) + .default_value(0.3f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("Coat")) + .default_value(0.0f) + .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_("Offset")) + .default_value(2.0f * ((float)M_PI) / 180.0f) + .min(-M_PI_2) + .max(M_PI_2) + .subtype(PROP_ANGLE); + b.add_input<decl::Float>(N_("Random Color")) + .default_value(0.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("Random Roughness")) + .default_value(0.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("Random")).hide_value(); + b.add_output<decl::Shader>(N_("BSDF")); +} + +static void node_shader_buts_principled_hair(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiItemR(layout, ptr, "parametrization", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); +} + +/* Initialize the custom Parametrization property to Color. */ +static void node_shader_init_hair_principled(bNodeTree *UNUSED(ntree), bNode *node) +{ + node->custom1 = SHD_PRINCIPLED_HAIR_REFLECTANCE; +} + +/* Triggers (in)visibility of some sockets when changing Parametrization. */ +static void node_shader_update_hair_principled(bNodeTree *ntree, bNode *node) +{ + int parametrization = node->custom1; + + LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { + if (STREQ(sock->name, "Color")) { + nodeSetSocketAvailability(ntree, sock, parametrization == SHD_PRINCIPLED_HAIR_REFLECTANCE); + } + else if (STREQ(sock->name, "Melanin")) { + nodeSetSocketAvailability( + ntree, sock, parametrization == SHD_PRINCIPLED_HAIR_PIGMENT_CONCENTRATION); + } + else if (STREQ(sock->name, "Melanin Redness")) { + nodeSetSocketAvailability( + ntree, sock, parametrization == SHD_PRINCIPLED_HAIR_PIGMENT_CONCENTRATION); + } + else if (STREQ(sock->name, "Tint")) { + nodeSetSocketAvailability( + ntree, sock, parametrization == SHD_PRINCIPLED_HAIR_PIGMENT_CONCENTRATION); + } + else if (STREQ(sock->name, "Absorption Coefficient")) { + nodeSetSocketAvailability( + ntree, sock, parametrization == SHD_PRINCIPLED_HAIR_DIRECT_ABSORPTION); + } + else if (STREQ(sock->name, "Random Color")) { + nodeSetSocketAvailability( + ntree, sock, parametrization == SHD_PRINCIPLED_HAIR_PIGMENT_CONCENTRATION); + } + } +} + +} // namespace blender::nodes::node_shader_bsdf_hair_principled_cc + +/* node type definition */ +void register_node_type_sh_bsdf_hair_principled() +{ + namespace file_ns = blender::nodes::node_shader_bsdf_hair_principled_cc; + + static bNodeType ntype; + + sh_node_type_base( + &ntype, SH_NODE_BSDF_HAIR_PRINCIPLED, "Principled Hair BSDF", NODE_CLASS_SHADER); + ntype.declare = file_ns::node_declare; + ntype.draw_buttons = file_ns::node_shader_buts_principled_hair; + node_type_size_preset(&ntype, NODE_SIZE_LARGE); + node_type_init(&ntype, file_ns::node_shader_init_hair_principled); + node_type_update(&ntype, file_ns::node_shader_update_hair_principled); + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.c b/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.c deleted file mode 100644 index 5fbf66d5a68..00000000000 --- a/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.c +++ /dev/null @@ -1,195 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2005 Blender Foundation. - * All rights reserved. - */ - -#include "../node_shader_util.h" - -/* **************** OUTPUT ******************** */ - -static bNodeSocketTemplate sh_node_bsdf_principled_in[] = { - {SOCK_RGBA, N_("Base Color"), 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Subsurface"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_VECTOR, - N_("Subsurface Radius"), - 1.0f, - 0.2f, - 0.1f, - 0.0f, - 0.0f, - 100.0f, - PROP_NONE, - SOCK_COMPACT}, - {SOCK_RGBA, N_("Subsurface Color"), 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Subsurface IOR"), 1.4f, 0.0f, 0.0f, 0.0f, 1.01f, 3.8f, PROP_FACTOR}, - {SOCK_FLOAT, N_("Subsurface Anisotropy"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_FLOAT, N_("Metallic"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_FLOAT, N_("Specular"), 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_FLOAT, N_("Specular Tint"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_FLOAT, N_("Roughness"), 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_FLOAT, N_("Anisotropic"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_FLOAT, N_("Anisotropic Rotation"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_FLOAT, N_("Sheen"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_FLOAT, N_("Sheen Tint"), 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_FLOAT, N_("Clearcoat"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_FLOAT, N_("Clearcoat Roughness"), 0.03f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_FLOAT, N_("IOR"), 1.45f, 0.0f, 0.0f, 0.0f, 0.0f, 1000.0f}, - {SOCK_FLOAT, N_("Transmission"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_FLOAT, N_("Transmission Roughness"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_RGBA, N_("Emission"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Emission Strength"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1000000.0f}, - {SOCK_FLOAT, N_("Alpha"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_VECTOR, N_("Normal"), 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, - {SOCK_VECTOR, - N_("Clearcoat Normal"), - 0.0f, - 0.0f, - 0.0f, - 1.0f, - -1.0f, - 1.0f, - PROP_NONE, - SOCK_HIDE_VALUE}, - {SOCK_VECTOR, N_("Tangent"), 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, - {SOCK_FLOAT, N_("Weight"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, PROP_NONE, SOCK_UNAVAIL}, - {-1, ""}, -}; - -static bNodeSocketTemplate sh_node_bsdf_principled_out[] = { - {SOCK_SHADER, N_("BSDF")}, - {-1, ""}, -}; - -static void node_shader_init_principled(bNodeTree *UNUSED(ntree), bNode *node) -{ - node->custom1 = SHD_GLOSSY_GGX; - node->custom2 = SHD_SUBSURFACE_RANDOM_WALK; -} - -#define socket_not_zero(sock) (in[sock].link || (clamp_f(in[sock].vec[0], 0.0f, 1.0f) > 1e-5f)) -#define socket_not_one(sock) \ - (in[sock].link || (clamp_f(in[sock].vec[0], 0.0f, 1.0f) < 1.0f - 1e-5f)) - -static int node_shader_gpu_bsdf_principled(GPUMaterial *mat, - bNode *node, - bNodeExecData *UNUSED(execdata), - GPUNodeStack *in, - GPUNodeStack *out) -{ - /* Normals */ - if (!in[22].link) { - GPU_link(mat, "world_normals_get", &in[22].link); - } - - /* Clearcoat Normals */ - if (!in[23].link) { - GPU_link(mat, "world_normals_get", &in[23].link); - } - -#if 0 /* Not used at the moment. */ - /* Tangents */ - if (!in[24].link) { - GPUNodeLink *orco = GPU_attribute(CD_ORCO, ""); - GPU_link(mat, "tangent_orco_z", orco, &in[24].link); - GPU_link(mat, "node_tangent", in[24].link, &in[24].link); - } -#endif - - bool use_diffuse = socket_not_one(6) && socket_not_one(17); - bool use_subsurf = socket_not_zero(1) && use_diffuse; - bool use_refract = socket_not_one(6) && socket_not_zero(17); - bool use_transparency = socket_not_one(21); - // bool use_clear = socket_not_zero(14); - - uint flag = GPU_MATFLAG_GLOSSY; - if (use_diffuse) { - flag |= GPU_MATFLAG_DIFFUSE; - } - if (use_refract) { - flag |= GPU_MATFLAG_REFRACT; - } - if (use_subsurf) { - flag |= GPU_MATFLAG_SUBSURFACE; - } - if (use_transparency) { - flag |= GPU_MATFLAG_TRANSPARENT; - } - - float use_multi_scatter = (node->custom1 == SHD_GLOSSY_MULTI_GGX) ? 1.0f : 0.0f; - - GPU_material_flag_set(mat, flag); - - GPUNodeLink *diffuse_weight, *specular_weight, *glass_reflection_weight, - *glass_transmission_weight, *clearcoat_weight; - - GPU_stack_link(mat, - node, - "node_bsdf_principled", - in, - out, - GPU_constant(&use_multi_scatter), - &diffuse_weight, - &specular_weight, - &glass_reflection_weight, - &glass_transmission_weight, - &clearcoat_weight); - - return GPU_stack_eval_link(mat, - node, - "node_bsdf_principled_eval", - in, - out, - GPU_constant(&use_multi_scatter), - diffuse_weight, - specular_weight, - glass_reflection_weight, - glass_transmission_weight, - clearcoat_weight); -} - -static void node_shader_update_principled(bNodeTree *UNUSED(ntree), bNode *node) -{ - bNodeSocket *sock; - int distribution = node->custom1; - - for (sock = node->inputs.first; sock; sock = sock->next) { - if (STREQ(sock->name, "Transmission Roughness")) { - if (distribution == SHD_GLOSSY_GGX) { - sock->flag &= ~SOCK_UNAVAIL; - } - else { - sock->flag |= SOCK_UNAVAIL; - } - } - } -} - -/* node type definition */ -void register_node_type_sh_bsdf_principled(void) -{ - static bNodeType ntype; - - sh_node_type_base(&ntype, SH_NODE_BSDF_PRINCIPLED, "Principled BSDF", NODE_CLASS_SHADER, 0); - node_type_socket_templates(&ntype, sh_node_bsdf_principled_in, sh_node_bsdf_principled_out); - node_type_size_preset(&ntype, NODE_SIZE_LARGE); - node_type_init(&ntype, node_shader_init_principled); - node_type_storage(&ntype, "", NULL, NULL); - node_type_gpu(&ntype, node_shader_gpu_bsdf_principled); - node_type_update(&ntype, node_shader_update_principled); - - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc new file mode 100644 index 00000000000..4c378d9bc09 --- /dev/null +++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc @@ -0,0 +1,259 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2005 Blender Foundation. + * All rights reserved. + */ + +#include "node_shader_util.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes::node_shader_bsdf_principled_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Color>(N_("Base Color")).default_value({0.8f, 0.8f, 0.8f, 1.0f}); + b.add_input<decl::Float>(N_("Subsurface")) + .default_value(0.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Vector>(N_("Subsurface Radius")) + .default_value({1.0f, 0.2f, 0.1f}) + .min(0.0f) + .max(100.0f) + .compact(); + b.add_input<decl::Color>(N_("Subsurface Color")).default_value({0.8f, 0.8f, 0.8f, 1.0f}); + b.add_input<decl::Float>(N_("Subsurface IOR")) + .default_value(1.4f) + .min(1.01f) + .max(3.8f) + .subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("Subsurface Anisotropy")) + .default_value(0.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("Metallic")) + .default_value(0.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("Specular")) + .default_value(0.5f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("Specular Tint")) + .default_value(0.5f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("Roughness")) + .default_value(0.5f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("Anisotropic")) + .default_value(0.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("Anisotropic Rotation")) + .default_value(0.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("Sheen")) + .default_value(0.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("Sheen Tint")) + .default_value(0.5f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("Clearcoat")) + .default_value(0.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("Clearcoat Roughness")) + .default_value(0.03f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("IOR")).default_value(1.45f).min(0.0f).max(1000.0f); + b.add_input<decl::Float>(N_("Transmission")) + .default_value(0.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("Transmission Roughness")) + .default_value(0.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Color>(N_("Emission")).default_value({0.0f, 0.0f, 0.0f, 1.0f}); + b.add_input<decl::Float>(N_("Emission Strength")).default_value(1.0).min(0.0f).max(1000000.0f); + b.add_input<decl::Float>(N_("Alpha")) + .default_value(1.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Vector>(N_("Normal")).hide_value(); + b.add_input<decl::Vector>(N_("Clearcoat Normal")).hide_value(); + b.add_input<decl::Vector>(N_("Tangent")).hide_value(); + b.add_output<decl::Shader>(N_("BSDF")); +} + +static void node_shader_buts_principled(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "distribution", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); + uiItemR(layout, ptr, "subsurface_method", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); +} + +static void node_shader_init_principled(bNodeTree *UNUSED(ntree), bNode *node) +{ + node->custom1 = SHD_GLOSSY_GGX; + node->custom2 = SHD_SUBSURFACE_RANDOM_WALK; +} + +#define socket_not_zero(sock) (in[sock].link || (clamp_f(in[sock].vec[0], 0.0f, 1.0f) > 1e-5f)) +#define socket_not_one(sock) \ + (in[sock].link || (clamp_f(in[sock].vec[0], 0.0f, 1.0f) < 1.0f - 1e-5f)) + +static int node_shader_gpu_bsdf_principled(GPUMaterial *mat, + bNode *node, + bNodeExecData *UNUSED(execdata), + GPUNodeStack *in, + GPUNodeStack *out) +{ + GPUNodeLink *sss_scale; + + /* Normals */ + if (!in[22].link) { + GPU_link(mat, "world_normals_get", &in[22].link); + } + + /* Clearcoat Normals */ + if (!in[23].link) { + GPU_link(mat, "world_normals_get", &in[23].link); + } + +#if 0 /* Not used at the moment. */ + /* Tangents */ + if (!in[24].link) { + GPUNodeLink *orco = GPU_attribute(CD_ORCO, ""); + GPU_link(mat, "tangent_orco_z", orco, &in[24].link); + GPU_link(mat, + "node_tangent", + GPU_builtin(GPU_WORLD_NORMAL), + in[24].link, + GPU_builtin(GPU_OBJECT_MATRIX), + &in[24].link); + } +#endif + + bool use_diffuse = socket_not_one(6) && socket_not_one(17); + bool use_subsurf = socket_not_zero(1) && use_diffuse && node->sss_id > 0; + bool use_refract = socket_not_one(6) && socket_not_zero(17); + bool use_clear = socket_not_zero(14); + + /* SSS Profile */ + if (use_subsurf) { + bNodeSocket *socket = (bNodeSocket *)BLI_findlink(&node->original->inputs, 2); + bNodeSocketValueRGBA *socket_data = (bNodeSocketValueRGBA *)socket->default_value; + /* For some reason it seems that the socket value is in ARGB format. */ + GPU_material_sss_profile_create(mat, &socket_data->value[1]); + } + + if (in[2].link) { + sss_scale = in[2].link; + } + else { + GPU_link(mat, "set_rgb_one", &sss_scale); + } + + uint flag = GPU_MATFLAG_GLOSSY; + if (use_diffuse) { + flag |= GPU_MATFLAG_DIFFUSE; + } + if (use_refract) { + flag |= GPU_MATFLAG_REFRACT; + } + if (use_subsurf) { + flag |= GPU_MATFLAG_SSS; + } + + float f_use_diffuse = use_diffuse ? 1.0f : 0.0f; + float f_use_clearcoat = use_clear ? 1.0f : 0.0f; + float f_use_refraction = use_refract ? 1.0f : 0.0f; + float use_multi_scatter = (node->custom1 == SHD_GLOSSY_MULTI_GGX) ? 1.0f : 0.0f; + + GPU_material_flag_set(mat, (eGPUMatFlag)flag); + + return GPU_stack_link(mat, + node, + "node_bsdf_principled", + in, + out, + GPU_constant(&f_use_diffuse), + GPU_constant(&f_use_clearcoat), + GPU_constant(&f_use_refraction), + GPU_constant(&use_multi_scatter), + GPU_constant(&node->ssr_id), + GPU_constant(&node->sss_id), + sss_scale); +} + +static void node_shader_update_principled(bNodeTree *ntree, bNode *node) +{ + const int distribution = node->custom1; + const int sss_method = node->custom2; + + LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { + if (STREQ(sock->name, "Transmission Roughness")) { + nodeSetSocketAvailability(ntree, sock, distribution == SHD_GLOSSY_GGX); + } + + if (STR_ELEM(sock->name, "Subsurface IOR", "Subsurface Anisotropy")) { + nodeSetSocketAvailability(ntree, sock, sss_method != SHD_SUBSURFACE_BURLEY); + } + } +} + +} // namespace blender::nodes::node_shader_bsdf_principled_cc + +/* node type definition */ +void register_node_type_sh_bsdf_principled() +{ + namespace file_ns = blender::nodes::node_shader_bsdf_principled_cc; + + static bNodeType ntype; + + sh_node_type_base(&ntype, SH_NODE_BSDF_PRINCIPLED, "Principled BSDF", NODE_CLASS_SHADER); + ntype.declare = file_ns::node_declare; + ntype.draw_buttons = file_ns::node_shader_buts_principled; + node_type_size_preset(&ntype, NODE_SIZE_LARGE); + node_type_init(&ntype, file_ns::node_shader_init_principled); + node_type_gpu(&ntype, file_ns::node_shader_gpu_bsdf_principled); + node_type_update(&ntype, file_ns::node_shader_update_principled); + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_refraction.c b/source/blender/nodes/shader/nodes/node_shader_bsdf_refraction.cc index f857708c9b6..0d588c82869 100644 --- a/source/blender/nodes/shader/nodes/node_shader_bsdf_refraction.c +++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_refraction.cc @@ -17,23 +17,22 @@ * All rights reserved. */ -#include "../node_shader_util.h" +#include "node_shader_util.hh" -/* **************** OUTPUT ******************** */ +namespace blender::nodes::node_shader_bsdf_refraction_cc { -static bNodeSocketTemplate sh_node_bsdf_refraction_in[] = { - {SOCK_RGBA, N_("Color"), 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Roughness"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_FLOAT, N_("IOR"), 1.45f, 0.0f, 0.0f, 0.0f, 0.0f, 1000.0f}, - {SOCK_VECTOR, N_("Normal"), 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, - {SOCK_FLOAT, N_("Weight"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, PROP_NONE, SOCK_UNAVAIL}, - {-1, ""}, -}; - -static bNodeSocketTemplate sh_node_bsdf_refraction_out[] = { - {SOCK_SHADER, N_("BSDF")}, - {-1, ""}, -}; +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Color>(N_("Color")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Float>(N_("Roughness")) + .default_value(0.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("IOR")).default_value(1.45f).min(0.0f).max(1000.0f); + b.add_input<decl::Vector>(N_("Normal")).hide_value(); + b.add_output<decl::Shader>(N_("BSDF")); +} static void node_shader_init_refraction(bNodeTree *UNUSED(ntree), bNode *node) { @@ -56,21 +55,23 @@ static int node_shader_gpu_bsdf_refraction(GPUMaterial *mat, GPU_material_flag_set(mat, GPU_MATFLAG_REFRACT); - GPU_stack_link(mat, node, "node_bsdf_refraction", in, out); - return GPU_stack_eval_link(mat, node, "node_bsdf_refraction_eval", in, out); + return GPU_stack_link(mat, node, "node_bsdf_refraction", in, out); } +} // namespace blender::nodes::node_shader_bsdf_refraction_cc + /* node type definition */ -void register_node_type_sh_bsdf_refraction(void) +void register_node_type_sh_bsdf_refraction() { + namespace file_ns = blender::nodes::node_shader_bsdf_refraction_cc; + static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_BSDF_REFRACTION, "Refraction BSDF", NODE_CLASS_SHADER, 0); - node_type_socket_templates(&ntype, sh_node_bsdf_refraction_in, sh_node_bsdf_refraction_out); + sh_node_type_base(&ntype, SH_NODE_BSDF_REFRACTION, "Refraction BSDF", NODE_CLASS_SHADER); + ntype.declare = file_ns::node_declare; node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); - node_type_init(&ntype, node_shader_init_refraction); - node_type_storage(&ntype, "", NULL, NULL); - node_type_gpu(&ntype, node_shader_gpu_bsdf_refraction); + node_type_init(&ntype, file_ns::node_shader_init_refraction); + node_type_gpu(&ntype, file_ns::node_shader_gpu_bsdf_refraction); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_toon.c b/source/blender/nodes/shader/nodes/node_shader_bsdf_toon.cc index e79c65d85d9..5093b896764 100644 --- a/source/blender/nodes/shader/nodes/node_shader_bsdf_toon.c +++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_toon.cc @@ -17,23 +17,34 @@ * All rights reserved. */ -#include "../node_shader_util.h" +#include "node_shader_util.hh" -/* **************** OUTPUT ******************** */ +#include "UI_interface.h" +#include "UI_resources.h" -static bNodeSocketTemplate sh_node_bsdf_toon_in[] = { - {SOCK_RGBA, N_("Color"), 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Size"), 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_FLOAT, N_("Smooth"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_VECTOR, N_("Normal"), 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, - {SOCK_FLOAT, N_("Weight"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, PROP_NONE, SOCK_UNAVAIL}, - {-1, ""}, -}; +namespace blender::nodes::node_shader_bsdf_toon_cc { -static bNodeSocketTemplate sh_node_bsdf_toon_out[] = { - {SOCK_SHADER, N_("BSDF")}, - {-1, ""}, -}; +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Color>(N_("Color")).default_value({0.8f, 0.8f, 0.8f, 1.0f}); + b.add_input<decl::Float>(N_("Size")) + .default_value(0.5f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("Smooth")) + .default_value(0.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Vector>(N_("Normal")).hide_value(); + b.add_output<decl::Shader>(N_("BSDF")); +} + +static void node_shader_buts_toon(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "component", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); +} static int node_shader_gpu_bsdf_toon(GPUMaterial *mat, bNode *node, @@ -47,21 +58,23 @@ static int node_shader_gpu_bsdf_toon(GPUMaterial *mat, GPU_material_flag_set(mat, GPU_MATFLAG_DIFFUSE); - GPU_stack_link(mat, node, "node_bsdf_toon", in, out); - return GPU_stack_eval_link(mat, node, "node_bsdf_toon_eval", in, out); + return GPU_stack_link(mat, node, "node_bsdf_toon", in, out); } +} // namespace blender::nodes::node_shader_bsdf_toon_cc + /* node type definition */ -void register_node_type_sh_bsdf_toon(void) +void register_node_type_sh_bsdf_toon() { + namespace file_ns = blender::nodes::node_shader_bsdf_toon_cc; + static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_BSDF_TOON, "Toon BSDF", NODE_CLASS_SHADER, 0); - node_type_socket_templates(&ntype, sh_node_bsdf_toon_in, sh_node_bsdf_toon_out); + sh_node_type_base(&ntype, SH_NODE_BSDF_TOON, "Toon BSDF", NODE_CLASS_SHADER); + ntype.declare = file_ns::node_declare; + ntype.draw_buttons = file_ns::node_shader_buts_toon; node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); - node_type_init(&ntype, NULL); - node_type_storage(&ntype, "", NULL, NULL); - node_type_gpu(&ntype, node_shader_gpu_bsdf_toon); + node_type_gpu(&ntype, file_ns::node_shader_gpu_bsdf_toon); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_translucent.c b/source/blender/nodes/shader/nodes/node_shader_bsdf_translucent.cc index d6c308459f5..22891738299 100644 --- a/source/blender/nodes/shader/nodes/node_shader_bsdf_translucent.c +++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_translucent.cc @@ -17,21 +17,16 @@ * All rights reserved. */ -#include "../node_shader_util.h" +#include "node_shader_util.hh" -/* **************** OUTPUT ******************** */ +namespace blender::nodes::node_shader_bsdf_translucent_cc { -static bNodeSocketTemplate sh_node_bsdf_translucent_in[] = { - {SOCK_RGBA, N_("Color"), 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f}, - {SOCK_VECTOR, N_("Normal"), 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, - {SOCK_FLOAT, N_("Weight"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, PROP_NONE, SOCK_UNAVAIL}, - {-1, ""}, -}; - -static bNodeSocketTemplate sh_node_bsdf_translucent_out[] = { - {SOCK_SHADER, N_("BSDF")}, - {-1, ""}, -}; +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Color>(N_("Color")).default_value({0.8f, 0.8f, 0.8f, 1.0f}); + b.add_input<decl::Vector>(N_("Normal")).hide_value(); + b.add_output<decl::Shader>(N_("BSDF")); +} static int node_shader_gpu_bsdf_translucent(GPUMaterial *mat, bNode *node, @@ -45,20 +40,21 @@ static int node_shader_gpu_bsdf_translucent(GPUMaterial *mat, GPU_material_flag_set(mat, GPU_MATFLAG_DIFFUSE); - GPU_stack_link(mat, node, "node_bsdf_translucent", in, out); - return GPU_stack_eval_link(mat, node, "node_bsdf_translucent_eval", in, out); + return GPU_stack_link(mat, node, "node_bsdf_translucent", in, out); } +} // namespace blender::nodes::node_shader_bsdf_translucent_cc + /* node type definition */ -void register_node_type_sh_bsdf_translucent(void) +void register_node_type_sh_bsdf_translucent() { + namespace file_ns = blender::nodes::node_shader_bsdf_translucent_cc; + static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_BSDF_TRANSLUCENT, "Translucent BSDF", NODE_CLASS_SHADER, 0); - node_type_socket_templates(&ntype, sh_node_bsdf_translucent_in, sh_node_bsdf_translucent_out); - node_type_init(&ntype, NULL); - node_type_storage(&ntype, "", NULL, NULL); - node_type_gpu(&ntype, node_shader_gpu_bsdf_translucent); + sh_node_type_base(&ntype, SH_NODE_BSDF_TRANSLUCENT, "Translucent BSDF", NODE_CLASS_SHADER); + ntype.declare = file_ns::node_declare; + node_type_gpu(&ntype, file_ns::node_shader_gpu_bsdf_translucent); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_transparent.c b/source/blender/nodes/shader/nodes/node_shader_bsdf_transparent.cc index a1c3b9066c8..d764f4dd76b 100644 --- a/source/blender/nodes/shader/nodes/node_shader_bsdf_transparent.c +++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_transparent.cc @@ -17,20 +17,15 @@ * All rights reserved. */ -#include "../node_shader_util.h" +#include "node_shader_util.hh" -/* **************** OUTPUT ******************** */ +namespace blender::nodes::node_shader_bsdf_transparent_cc { -static bNodeSocketTemplate sh_node_bsdf_transparent_in[] = { - {SOCK_RGBA, N_("Color"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Weight"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, PROP_NONE, SOCK_UNAVAIL}, - {-1, ""}, -}; - -static bNodeSocketTemplate sh_node_bsdf_transparent_out[] = { - {SOCK_SHADER, N_("BSDF")}, - {-1, ""}, -}; +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Color>(N_("Color")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output<decl::Shader>(N_("BSDF")); +} static int node_shader_gpu_bsdf_transparent(GPUMaterial *mat, bNode *node, @@ -38,22 +33,21 @@ static int node_shader_gpu_bsdf_transparent(GPUMaterial *mat, GPUNodeStack *in, GPUNodeStack *out) { - if (in[0].link || !equals_v3v3(in[0].vec, (float[3]){0.0f, 0.0f, 0.0f})) { - GPU_material_flag_set(mat, GPU_MATFLAG_TRANSPARENT); - } return GPU_stack_link(mat, node, "node_bsdf_transparent", in, out); } +} // namespace blender::nodes::node_shader_bsdf_transparent_cc + /* node type definition */ -void register_node_type_sh_bsdf_transparent(void) +void register_node_type_sh_bsdf_transparent() { + namespace file_ns = blender::nodes::node_shader_bsdf_transparent_cc; + static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_BSDF_TRANSPARENT, "Transparent BSDF", NODE_CLASS_SHADER, 0); - node_type_socket_templates(&ntype, sh_node_bsdf_transparent_in, sh_node_bsdf_transparent_out); - node_type_init(&ntype, NULL); - node_type_storage(&ntype, "", NULL, NULL); - node_type_gpu(&ntype, node_shader_gpu_bsdf_transparent); + sh_node_type_base(&ntype, SH_NODE_BSDF_TRANSPARENT, "Transparent BSDF", NODE_CLASS_SHADER); + ntype.declare = file_ns::node_declare; + node_type_gpu(&ntype, file_ns::node_shader_gpu_bsdf_transparent); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_velvet.c b/source/blender/nodes/shader/nodes/node_shader_bsdf_velvet.cc index 0a7828f5b9a..dd090236c08 100644 --- a/source/blender/nodes/shader/nodes/node_shader_bsdf_velvet.c +++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_velvet.cc @@ -17,22 +17,21 @@ * All rights reserved. */ -#include "../node_shader_util.h" +#include "node_shader_util.hh" -/* **************** OUTPUT ******************** */ +namespace blender::nodes::node_shader_bsdf_velvet_cc { -static bNodeSocketTemplate sh_node_bsdf_velvet_in[] = { - {SOCK_RGBA, N_("Color"), 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Sigma"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_VECTOR, N_("Normal"), 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, - {SOCK_FLOAT, N_("Weight"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, PROP_NONE, SOCK_UNAVAIL}, - {-1, ""}, -}; - -static bNodeSocketTemplate sh_node_bsdf_velvet_out[] = { - {SOCK_SHADER, N_("BSDF")}, - {-1, ""}, -}; +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Color>(N_("Color")).default_value({0.8f, 0.8f, 0.8f, 1.0f}); + b.add_input<decl::Float>(N_("Sigma")) + .default_value(1.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Vector>(N_("Normal")).hide_value(); + b.add_output<decl::Shader>(N_("BSDF")); +} static int node_shader_gpu_bsdf_velvet(GPUMaterial *mat, bNode *node, @@ -46,20 +45,21 @@ static int node_shader_gpu_bsdf_velvet(GPUMaterial *mat, GPU_material_flag_set(mat, GPU_MATFLAG_DIFFUSE); - GPU_stack_link(mat, node, "node_bsdf_velvet", in, out); - return GPU_stack_link(mat, node, "node_bsdf_velvet_eval", in, out); + return GPU_stack_link(mat, node, "node_bsdf_velvet", in, out); } +} // namespace blender::nodes::node_shader_bsdf_velvet_cc + /* node type definition */ -void register_node_type_sh_bsdf_velvet(void) +void register_node_type_sh_bsdf_velvet() { + namespace file_ns = blender::nodes::node_shader_bsdf_velvet_cc; + static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_BSDF_VELVET, "Velvet BSDF", NODE_CLASS_SHADER, 0); - node_type_socket_templates(&ntype, sh_node_bsdf_velvet_in, sh_node_bsdf_velvet_out); - node_type_init(&ntype, NULL); - node_type_storage(&ntype, "", NULL, NULL); - node_type_gpu(&ntype, node_shader_gpu_bsdf_velvet); + sh_node_type_base(&ntype, SH_NODE_BSDF_VELVET, "Velvet BSDF", NODE_CLASS_SHADER); + ntype.declare = file_ns::node_declare; + node_type_gpu(&ntype, file_ns::node_shader_gpu_bsdf_velvet); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_bump.c b/source/blender/nodes/shader/nodes/node_shader_bump.cc index 5c46e1d3775..f0788c543c2 100644 --- a/source/blender/nodes/shader/nodes/node_shader_bump.c +++ b/source/blender/nodes/shader/nodes/node_shader_bump.cc @@ -21,22 +21,38 @@ * \ingroup shdnodes */ -#include "node_shader_util.h" +#include "node_shader_util.hh" + +#include "UI_interface.h" +#include "UI_resources.h" /* **************** BUMP ******************** */ -/* clang-format off */ -static bNodeSocketTemplate sh_node_bump_in[] = { - {SOCK_FLOAT, N_("Strength"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_FLOAT, N_("Distance"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1000.0f}, - {SOCK_FLOAT, N_("Height"), 1.0f, 1.0f, 1.0f, 1.0f, -1000.0f, 1000.0f, PROP_NONE, SOCK_HIDE_VALUE}, - {SOCK_FLOAT, N_("Height_dx"), 1.0f, 1.0f, 1.0f, 1.0f, -1000.0f, 1000.0f, PROP_NONE, SOCK_UNAVAIL}, - {SOCK_FLOAT, N_("Height_dy"), 1.0f, 1.0f, 1.0f, 1.0f, -1000.0f, 1000.0f, PROP_NONE, SOCK_UNAVAIL}, - {SOCK_VECTOR, N_("Normal"), 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, - {-1, ""} -}; -/* clang-format on */ -static bNodeSocketTemplate sh_node_bump_out[] = {{SOCK_VECTOR, "Normal"}, {-1, ""}}; +namespace blender::nodes::node_shader_bump_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Float>(N_("Strength")) + .default_value(1.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("Distance")).default_value(1.0f).min(0.0f).max(1000.0f); + b.add_input<decl::Float>(N_("Height")) + .default_value(1.0f) + .min(-1000.0f) + .max(1000.0f) + .hide_value(); + b.add_input<decl::Float>(N_("Height_dx")).default_value(1.0f).unavailable(); + b.add_input<decl::Float>(N_("Height_dy")).default_value(1.0f).unavailable(); + b.add_input<decl::Vector>(N_("Normal")).min(-1.0f).max(1.0f).hide_value(); + b.add_output<decl::Vector>(N_("Normal")); +} + +static void node_shader_buts_bump(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "invert", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, 0); +} static int gpu_shader_bump(GPUMaterial *mat, bNode *node, @@ -53,15 +69,19 @@ static int gpu_shader_bump(GPUMaterial *mat, return GPU_stack_link(mat, node, "node_bump", in, out, GPU_constant(&invert)); } +} // namespace blender::nodes::node_shader_bump_cc + /* node type definition */ -void register_node_type_sh_bump(void) +void register_node_type_sh_bump() { + namespace file_ns = blender::nodes::node_shader_bump_cc; + static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_BUMP, "Bump", NODE_CLASS_OP_VECTOR, 0); - node_type_socket_templates(&ntype, sh_node_bump_in, sh_node_bump_out); - node_type_storage(&ntype, "", NULL, NULL); - node_type_gpu(&ntype, gpu_shader_bump); + sh_node_type_base(&ntype, SH_NODE_BUMP, "Bump", NODE_CLASS_OP_VECTOR); + ntype.declare = file_ns::node_declare; + ntype.draw_buttons = file_ns::node_shader_buts_bump; + node_type_gpu(&ntype, file_ns::gpu_shader_bump); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_camera.c b/source/blender/nodes/shader/nodes/node_shader_camera.cc index 9c47db4ea4e..90d2b55b0f7 100644 --- a/source/blender/nodes/shader/nodes/node_shader_camera.c +++ b/source/blender/nodes/shader/nodes/node_shader_camera.cc @@ -21,15 +21,16 @@ * \ingroup shdnodes */ -#include "node_shader_util.h" +#include "node_shader_util.hh" -/* **************** CAMERA INFO ******************** */ -static bNodeSocketTemplate sh_node_camera_out[] = { - {SOCK_VECTOR, N_("View Vector")}, - {SOCK_FLOAT, N_("View Z Depth")}, - {SOCK_FLOAT, N_("View Distance")}, - {-1, ""}, -}; +namespace blender::nodes::node_shader_camera_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_output<decl::Vector>(N_("View Vector")); + b.add_output<decl::Float>(N_("View Z Depth")); + b.add_output<decl::Float>(N_("View Distance")); +} static int gpu_shader_camera(GPUMaterial *mat, bNode *node, @@ -40,14 +41,17 @@ static int gpu_shader_camera(GPUMaterial *mat, return GPU_stack_link(mat, node, "camera", in, out); } -void register_node_type_sh_camera(void) +} // namespace blender::nodes::node_shader_camera_cc + +void register_node_type_sh_camera() { + namespace file_ns = blender::nodes::node_shader_camera_cc; + static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_CAMERA, "Camera Data", NODE_CLASS_INPUT, 0); - node_type_socket_templates(&ntype, NULL, sh_node_camera_out); - node_type_storage(&ntype, "", NULL, NULL); - node_type_gpu(&ntype, gpu_shader_camera); + sh_node_type_base(&ntype, SH_NODE_CAMERA, "Camera Data", NODE_CLASS_INPUT); + ntype.declare = file_ns::node_declare; + node_type_gpu(&ntype, file_ns::gpu_shader_camera); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_clamp.cc b/source/blender/nodes/shader/nodes/node_shader_clamp.cc index e8d4239937f..cd0f1b3c44d 100644 --- a/source/blender/nodes/shader/nodes/node_shader_clamp.cc +++ b/source/blender/nodes/shader/nodes/node_shader_clamp.cc @@ -21,20 +21,26 @@ * \ingroup shdnodes */ -#include "node_shader_util.h" +#include "node_shader_util.hh" -namespace blender::nodes { +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes::node_shader_clamp_cc { static void sh_node_clamp_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input<decl::Float>("Value").min(0.0f).max(1.0f).default_value(1.0f); - b.add_input<decl::Float>("Min").default_value(0.0f).min(-10000.0f).max(10000.0f); - b.add_input<decl::Float>("Max").default_value(1.0f).min(-10000.0f).max(10000.0f); - b.add_output<decl::Float>("Result"); -}; + b.add_input<decl::Float>(N_("Value")).default_value(1.0f); + b.add_input<decl::Float>(N_("Min")).default_value(0.0f).min(-10000.0f).max(10000.0f); + b.add_input<decl::Float>(N_("Max")).default_value(1.0f).min(-10000.0f).max(10000.0f); + b.add_output<decl::Float>(N_("Result")); +} -} // namespace blender::nodes +static void node_shader_buts_clamp(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "clamp_type", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); +} static void node_shader_init_clamp(bNodeTree *UNUSED(ntree), bNode *node) { @@ -75,15 +81,20 @@ static void sh_node_clamp_build_multi_function(blender::nodes::NodeMultiFunction } } -void register_node_type_sh_clamp(void) +} // namespace blender::nodes::node_shader_clamp_cc + +void register_node_type_sh_clamp() { + namespace file_ns = blender::nodes::node_shader_clamp_cc; + static bNodeType ntype; - sh_fn_node_type_base(&ntype, SH_NODE_CLAMP, "Clamp", NODE_CLASS_CONVERTER, 0); - ntype.declare = blender::nodes::sh_node_clamp_declare; - node_type_init(&ntype, node_shader_init_clamp); - node_type_gpu(&ntype, gpu_shader_clamp); - ntype.build_multi_function = sh_node_clamp_build_multi_function; + sh_fn_node_type_base(&ntype, SH_NODE_CLAMP, "Clamp", NODE_CLASS_CONVERTER); + ntype.declare = file_ns::sh_node_clamp_declare; + ntype.draw_buttons = file_ns::node_shader_buts_clamp; + node_type_init(&ntype, file_ns::node_shader_init_clamp); + node_type_gpu(&ntype, file_ns::gpu_shader_clamp); + ntype.build_multi_function = file_ns::sh_node_clamp_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc b/source/blender/nodes/shader/nodes/node_shader_color_ramp.cc index d4d08be5d49..d8c43e1d66b 100644 --- a/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc +++ b/source/blender/nodes/shader/nodes/node_shader_color_ramp.cc @@ -21,43 +21,20 @@ * \ingroup shdnodes */ -#include "IMB_colormanagement.h" - #include "DNA_texture_types.h" #include "BLI_color.hh" -#include "node_shader_util.h" +#include "node_shader_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_shader_color_ramp_cc { static void sh_node_valtorgb_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input<decl::Float>("Fac").default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_output<decl::Color>("Color"); - b.add_output<decl::Float>("Alpha"); -}; - -} // namespace blender::nodes - -static void node_shader_exec_valtorgb(void *UNUSED(data), - int UNUSED(thread), - bNode *node, - bNodeExecData *UNUSED(execdata), - bNodeStack **in, - bNodeStack **out) -{ - /* stack order in: fac */ - /* stack order out: col, alpha */ - - if (node->storage) { - float fac; - nodestack_get_vec(&fac, SOCK_FLOAT, in[0]); - - BKE_colorband_evaluate((ColorBand *)node->storage, fac, out[0]->vec); - out[1]->vec[0] = out[0]->vec[3]; - } + 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_("Color")); + b.add_output<decl::Float>(N_("Alpha")); } static void node_shader_init_valtorgb(bNodeTree *UNUSED(ntree), bNode *node) @@ -172,64 +149,21 @@ static void sh_node_valtorgb_build_multi_function( builder.construct_and_set_matching_fn<ColorBandFunction>(*color_band); } -void register_node_type_sh_valtorgb(void) -{ - static bNodeType ntype; +} // namespace blender::nodes::node_shader_color_ramp_cc - sh_fn_node_type_base(&ntype, SH_NODE_VALTORGB, "ColorRamp", NODE_CLASS_CONVERTER, 0); - ntype.declare = blender::nodes::sh_node_valtorgb_declare; - node_type_init(&ntype, node_shader_init_valtorgb); - node_type_size_preset(&ntype, NODE_SIZE_LARGE); - node_type_storage(&ntype, "ColorBand", node_free_standard_storage, node_copy_standard_storage); - node_type_exec(&ntype, nullptr, nullptr, node_shader_exec_valtorgb); - node_type_gpu(&ntype, gpu_shader_valtorgb); - ntype.build_multi_function = sh_node_valtorgb_build_multi_function; - - nodeRegisterType(&ntype); -} - -namespace blender::nodes { - -static void sh_node_rgbtobw_declare(NodeDeclarationBuilder &b) -{ - b.add_input<decl::Color>("Color").default_value({0.5f, 0.5f, 0.5f, 1.0f}); - b.add_output<decl::Float>("Val"); -}; - -} // namespace blender::nodes - -static void node_shader_exec_rgbtobw(void *UNUSED(data), - int UNUSED(thread), - bNode *UNUSED(node), - bNodeExecData *UNUSED(execdata), - bNodeStack **in, - bNodeStack **out) -{ - /* stack order out: bw */ - /* stack order in: col */ - float col[3]; - nodestack_get_vec(col, SOCK_VECTOR, in[0]); - - out[0]->vec[0] = IMB_colormanagement_get_luminance(col); -} - -static int gpu_shader_rgbtobw(GPUMaterial *mat, - bNode *node, - bNodeExecData *UNUSED(execdata), - GPUNodeStack *in, - GPUNodeStack *out) +void register_node_type_sh_valtorgb() { - return GPU_stack_link(mat, node, "rgbtobw", in, out); -} + namespace file_ns = blender::nodes::node_shader_color_ramp_cc; -void register_node_type_sh_rgbtobw(void) -{ static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_RGBTOBW, "RGB to BW", NODE_CLASS_CONVERTER, 0); - ntype.declare = blender::nodes::sh_node_rgbtobw_declare; - node_type_exec(&ntype, nullptr, nullptr, node_shader_exec_rgbtobw); - node_type_gpu(&ntype, gpu_shader_rgbtobw); + sh_fn_node_type_base(&ntype, SH_NODE_VALTORGB, "ColorRamp", NODE_CLASS_CONVERTER); + ntype.declare = file_ns::sh_node_valtorgb_declare; + node_type_init(&ntype, file_ns::node_shader_init_valtorgb); + node_type_size_preset(&ntype, NODE_SIZE_LARGE); + node_type_storage(&ntype, "ColorBand", node_free_standard_storage, node_copy_standard_storage); + node_type_gpu(&ntype, file_ns::gpu_shader_valtorgb); + ntype.build_multi_function = file_ns::sh_node_valtorgb_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_common.c b/source/blender/nodes/shader/nodes/node_shader_common.c deleted file mode 100644 index 4df5add7151..00000000000 --- a/source/blender/nodes/shader/nodes/node_shader_common.c +++ /dev/null @@ -1,270 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2006 Blender Foundation. - * All rights reserved. - * Juho Vepsäläinen - */ - -/** \file - * \ingroup shdnodes - */ - -#include "DNA_node_types.h" - -#include "BLI_utildefines.h" - -#include "BKE_node.h" - -#include "NOD_common.h" -#include "node_common.h" -#include "node_exec.h" -#include "node_shader_util.h" - -#include "RNA_access.h" - -static void copy_stack(bNodeStack *to, bNodeStack *from) -{ - if (to != from) { - copy_v4_v4(to->vec, from->vec); - to->data = from->data; - to->datatype = from->datatype; - - /* tag as copy to prevent freeing */ - to->is_copy = 1; - } -} - -static void move_stack(bNodeStack *to, bNodeStack *from) -{ - if (to != from) { - copy_v4_v4(to->vec, from->vec); - to->data = from->data; - to->datatype = from->datatype; - to->is_copy = from->is_copy; - - from->data = NULL; - from->is_copy = 0; - } -} - -/**** GROUP ****/ - -static void *group_initexec(bNodeExecContext *context, bNode *node, bNodeInstanceKey key) -{ - bNodeTree *ngroup = (bNodeTree *)node->id; - bNodeTreeExec *exec; - - if (!ngroup) { - return NULL; - } - - /* initialize the internal node tree execution */ - exec = ntreeShaderBeginExecTree_internal(context, ngroup, key); - - return exec; -} - -static void group_freeexec(void *nodedata) -{ - bNodeTreeExec *gexec = (bNodeTreeExec *)nodedata; - - if (gexec) { - ntreeShaderEndExecTree_internal(gexec); - } -} - -/* Copy inputs to the internal stack. - */ -static void group_copy_inputs(bNode *gnode, bNodeStack **in, bNodeStack *gstack) -{ - bNodeTree *ngroup = (bNodeTree *)gnode->id; - bNode *node; - bNodeSocket *sock; - bNodeStack *ns; - int a; - - for (node = ngroup->nodes.first; node; node = node->next) { - if (node->type == NODE_GROUP_INPUT) { - for (sock = node->outputs.first, a = 0; sock; sock = sock->next, a++) { - ns = node_get_socket_stack(gstack, sock); - if (ns) { - copy_stack(ns, in[a]); - } - } - } - } -} - -/* Copy internal results to the external outputs. - */ -static void group_move_outputs(bNode *gnode, bNodeStack **out, bNodeStack *gstack) -{ - bNodeTree *ngroup = (bNodeTree *)gnode->id; - bNode *node; - bNodeSocket *sock; - bNodeStack *ns; - int a; - - for (node = ngroup->nodes.first; node; node = node->next) { - if (node->type == NODE_GROUP_OUTPUT && (node->flag & NODE_DO_OUTPUT)) { - for (sock = node->inputs.first, a = 0; sock; sock = sock->next, a++) { - ns = node_get_socket_stack(gstack, sock); - if (ns) { - move_stack(out[a], ns); - } - } - break; /* only one active output node */ - } - } -} - -static void group_execute(void *data, - int thread, - struct bNode *node, - bNodeExecData *execdata, - struct bNodeStack **in, - struct bNodeStack **out) -{ - bNodeTreeExec *exec = execdata->data; - bNodeThreadStack *nts; - - if (!exec) { - return; - } - - /* XXX same behavior as trunk: all nodes inside group are executed. - * it's stupid, but just makes it work. compo redesign will do this better. - */ - { - bNode *inode; - for (inode = exec->nodetree->nodes.first; inode; inode = inode->next) { - inode->need_exec = 1; - } - } - - nts = ntreeGetThreadStack(exec, thread); - - group_copy_inputs(node, in, nts->stack); - ntreeExecThreadNodes(exec, nts, data, thread); - group_move_outputs(node, out, nts->stack); - - ntreeReleaseThreadStack(nts); -} - -static void group_gpu_copy_inputs(bNode *gnode, GPUNodeStack *in, bNodeStack *gstack) -{ - bNodeTree *ngroup = (bNodeTree *)gnode->id; - bNode *node; - bNodeSocket *sock; - bNodeStack *ns; - int a; - - for (node = ngroup->nodes.first; node; node = node->next) { - if (node->type == NODE_GROUP_INPUT) { - for (sock = node->outputs.first, a = 0; sock; sock = sock->next, a++) { - ns = node_get_socket_stack(gstack, sock); - if (ns) { - /* convert the external gpu stack back to internal node stack data */ - node_data_from_gpu_stack(ns, &in[a]); - } - } - } - } -} - -/* Copy internal results to the external outputs. - */ -static void group_gpu_move_outputs(bNode *gnode, GPUNodeStack *out, bNodeStack *gstack) -{ - bNodeTree *ngroup = (bNodeTree *)gnode->id; - bNode *node; - bNodeSocket *sock; - bNodeStack *ns; - int a; - - for (node = ngroup->nodes.first; node; node = node->next) { - if (node->type == NODE_GROUP_OUTPUT && (node->flag & NODE_DO_OUTPUT)) { - for (sock = node->inputs.first, a = 0; sock; sock = sock->next, a++) { - ns = node_get_socket_stack(gstack, sock); - if (ns) { - /* convert the node stack data result back to gpu stack */ - node_gpu_stack_from_data(&out[a], sock->type, ns); - } - } - break; /* only one active output node */ - } - } -} - -static int gpu_group_execute( - GPUMaterial *mat, bNode *node, bNodeExecData *execdata, GPUNodeStack *in, GPUNodeStack *out) -{ - bNodeTreeExec *exec = execdata->data; - - if (!node->id) { - return 0; - } - - group_gpu_copy_inputs(node, in, exec->stack); - ntreeExecGPUNodes(exec, mat, NULL); - group_gpu_move_outputs(node, out, exec->stack); - - return 1; -} - -void register_node_type_sh_group(void) -{ - static bNodeType ntype; - - /* NOTE: cannot use #sh_node_type_base for node group, because it would map the node type - * to the shared #NODE_GROUP integer type id. */ - - node_type_base_custom(&ntype, "ShaderNodeGroup", "Group", NODE_CLASS_GROUP, NODE_CONST_OUTPUT); - ntype.type = NODE_GROUP; - ntype.poll = sh_node_poll_default; - ntype.poll_instance = node_group_poll_instance; - ntype.insert_link = node_insert_link_default; - ntype.update_internal_links = node_update_internal_links_default; - ntype.rna_ext.srna = RNA_struct_find("ShaderNodeGroup"); - BLI_assert(ntype.rna_ext.srna != NULL); - RNA_struct_blender_type_set(ntype.rna_ext.srna, &ntype); - - node_type_socket_templates(&ntype, NULL, NULL); - node_type_size(&ntype, 140, 60, 400); - node_type_label(&ntype, node_group_label); - node_type_group_update(&ntype, node_group_update); - node_type_exec(&ntype, group_initexec, group_freeexec, group_execute); - node_type_gpu(&ntype, gpu_group_execute); - - nodeRegisterType(&ntype); -} - -void register_node_type_sh_custom_group(bNodeType *ntype) -{ - /* These methods can be overridden but need a default implementation otherwise. */ - if (ntype->poll == NULL) { - ntype->poll = sh_node_poll_default; - } - if (ntype->insert_link == NULL) { - ntype->insert_link = node_insert_link_default; - } - if (ntype->update_internal_links == NULL) { - ntype->update_internal_links = node_update_internal_links_default; - } - - node_type_exec(ntype, group_initexec, group_freeexec, group_execute); - node_type_gpu(ntype, gpu_group_execute); -} diff --git a/source/blender/nodes/shader/nodes/node_shader_common.cc b/source/blender/nodes/shader/nodes/node_shader_common.cc new file mode 100644 index 00000000000..3f0fff34533 --- /dev/null +++ b/source/blender/nodes/shader/nodes/node_shader_common.cc @@ -0,0 +1,130 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2006 Blender Foundation. + * All rights reserved. + * Juho Vepsäläinen + */ + +/** \file + * \ingroup shdnodes + */ + +#include "DNA_node_types.h" + +#include "BLI_utildefines.h" + +#include "BKE_node.h" + +#include "NOD_common.h" +#include "node_common.h" +#include "node_exec.h" +#include "node_shader_util.hh" + +#include "RNA_access.h" + +/**** GROUP ****/ + +static void group_gpu_copy_inputs(bNode *gnode, GPUNodeStack *in, bNodeStack *gstack) +{ + bNodeTree *ngroup = (bNodeTree *)gnode->id; + + LISTBASE_FOREACH (bNode *, node, &ngroup->nodes) { + if (node->type == NODE_GROUP_INPUT) { + int a; + LISTBASE_FOREACH_INDEX (bNodeSocket *, sock, &node->outputs, a) { + bNodeStack *ns = node_get_socket_stack(gstack, sock); + if (ns) { + /* convert the external gpu stack back to internal node stack data */ + node_data_from_gpu_stack(ns, &in[a]); + } + } + } + } +} + +/* Copy internal results to the external outputs. + */ +static void group_gpu_move_outputs(bNode *gnode, GPUNodeStack *out, bNodeStack *gstack) +{ + bNodeTree *ngroup = (bNodeTree *)gnode->id; + + LISTBASE_FOREACH (bNode *, node, &ngroup->nodes) { + if (node->type == NODE_GROUP_OUTPUT && (node->flag & NODE_DO_OUTPUT)) { + int a; + LISTBASE_FOREACH_INDEX (bNodeSocket *, sock, &node->inputs, a) { + bNodeStack *ns = node_get_socket_stack(gstack, sock); + if (ns) { + /* convert the node stack data result back to gpu stack */ + node_gpu_stack_from_data(&out[a], sock->type, ns); + } + } + break; /* only one active output node */ + } + } +} + +static int gpu_group_execute( + GPUMaterial *mat, bNode *node, bNodeExecData *execdata, GPUNodeStack *in, GPUNodeStack *out) +{ + bNodeTreeExec *exec = static_cast<bNodeTreeExec *>(execdata->data); + + if (!node->id) { + return 0; + } + + group_gpu_copy_inputs(node, in, exec->stack); + ntreeExecGPUNodes(exec, mat, nullptr); + group_gpu_move_outputs(node, out, exec->stack); + + return 1; +} + +void register_node_type_sh_group() +{ + static bNodeType ntype; + + /* NOTE: cannot use #sh_node_type_base for node group, because it would map the node type + * to the shared #NODE_GROUP integer type id. */ + + node_type_base_custom(&ntype, "ShaderNodeGroup", "Group", NODE_CLASS_GROUP); + ntype.type = NODE_GROUP; + ntype.poll = sh_node_poll_default; + ntype.poll_instance = node_group_poll_instance; + ntype.insert_link = node_insert_link_default; + ntype.rna_ext.srna = RNA_struct_find("ShaderNodeGroup"); + BLI_assert(ntype.rna_ext.srna != nullptr); + RNA_struct_blender_type_set(ntype.rna_ext.srna, &ntype); + + node_type_size(&ntype, 140, 60, 400); + ntype.labelfunc = node_group_label; + node_type_group_update(&ntype, node_group_update); + node_type_gpu(&ntype, gpu_group_execute); + + nodeRegisterType(&ntype); +} + +void register_node_type_sh_custom_group(bNodeType *ntype) +{ + /* These methods can be overridden but need a default implementation otherwise. */ + if (ntype->poll == nullptr) { + ntype->poll = sh_node_poll_default; + } + if (ntype->insert_link == nullptr) { + ntype->insert_link = node_insert_link_default; + } + + node_type_gpu(ntype, gpu_group_execute); +} diff --git a/source/blender/nodes/shader/nodes/node_shader_curves.cc b/source/blender/nodes/shader/nodes/node_shader_curves.cc index 887cc84bb76..bce59a60033 100644 --- a/source/blender/nodes/shader/nodes/node_shader_curves.cc +++ b/source/blender/nodes/shader/nodes/node_shader_curves.cc @@ -21,33 +21,16 @@ * \ingroup shdnodes */ -#include "node_shader_util.h" +#include "node_shader_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_shader_curves_cc { static void sh_node_curve_vec_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input<decl::Float>("Fac").min(0.0f).max(1.0f).default_value(1.0f).subtype(PROP_FACTOR); - b.add_input<decl::Vector>("Vector").min(-1.0f).max(1.0f); - b.add_output<decl::Vector>("Vector"); -}; - -} // namespace blender::nodes - -static void node_shader_exec_curve_vec(void *UNUSED(data), - int UNUSED(thread), - bNode *node, - bNodeExecData *UNUSED(execdata), - bNodeStack **in, - bNodeStack **out) -{ - float vec[3]; - - /* stack order input: vec */ - /* stack order output: vec */ - nodestack_get_vec(vec, SOCK_VECTOR, in[1]); - BKE_curvemapping_evaluate3F((CurveMapping *)node->storage, out[0]->vec, vec); + b.add_input<decl::Float>(N_("Fac")).min(0.0f).max(1.0f).default_value(1.0f).subtype(PROP_FACTOR); + b.add_input<decl::Vector>(N_("Vector")).min(-1.0f).max(1.0f); + b.add_output<decl::Vector>(N_("Vector")); } static void node_shader_init_curve_vec(bNodeTree *UNUSED(ntree), bNode *node) @@ -152,53 +135,35 @@ static void sh_node_curve_vec_build_multi_function( builder.construct_and_set_matching_fn<CurveVecFunction>(*cumap); } -void register_node_type_sh_curve_vec(void) +} // namespace blender::nodes::node_shader_curves_cc + +void register_node_type_sh_curve_vec() { + namespace file_ns = blender::nodes::node_shader_curves_cc; + static bNodeType ntype; - sh_fn_node_type_base(&ntype, SH_NODE_CURVE_VEC, "Vector Curves", NODE_CLASS_OP_VECTOR, 0); - ntype.declare = blender::nodes::sh_node_curve_vec_declare; - node_type_init(&ntype, node_shader_init_curve_vec); + sh_fn_node_type_base(&ntype, SH_NODE_CURVE_VEC, "Vector Curves", NODE_CLASS_OP_VECTOR); + ntype.declare = file_ns::sh_node_curve_vec_declare; + node_type_init(&ntype, file_ns::node_shader_init_curve_vec); node_type_size_preset(&ntype, NODE_SIZE_LARGE); node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves); - node_type_exec(&ntype, node_initexec_curves, nullptr, node_shader_exec_curve_vec); - node_type_gpu(&ntype, gpu_shader_curve_vec); - ntype.build_multi_function = sh_node_curve_vec_build_multi_function; + node_type_gpu(&ntype, file_ns::gpu_shader_curve_vec); + ntype.build_multi_function = file_ns::sh_node_curve_vec_build_multi_function; nodeRegisterType(&ntype); } /* **************** CURVE RGB ******************** */ -namespace blender::nodes { +namespace blender::nodes::node_shader_curves_cc { static void sh_node_curve_rgb_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Float>("Fac").min(0.0f).max(1.0f).default_value(1.0f).subtype(PROP_FACTOR); - b.add_input<decl::Color>("Color").default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_output<decl::Color>("Color"); -}; - -} // namespace blender::nodes - -static void node_shader_exec_curve_rgb(void *UNUSED(data), - int UNUSED(thread), - bNode *node, - bNodeExecData *UNUSED(execdata), - bNodeStack **in, - bNodeStack **out) -{ - float vec[3]; - float fac; - - /* stack order input: vec */ - /* stack order output: vec */ - nodestack_get_vec(&fac, SOCK_FLOAT, in[0]); - nodestack_get_vec(vec, SOCK_VECTOR, in[1]); - BKE_curvemapping_evaluateRGBF((CurveMapping *)node->storage, out[0]->vec, vec); - if (fac != 1.0f) { - interp_v3_v3v3(out[0]->vec, vec, out[0]->vec, fac); - } + b.is_function_node(); + b.add_input<decl::Float>(N_("Fac")).min(0.0f).max(1.0f).default_value(1.0f).subtype(PROP_FACTOR); + b.add_input<decl::Color>(N_("Color")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output<decl::Color>(N_("Color")); } static void node_shader_init_curve_rgb(bNodeTree *UNUSED(ntree), bNode *node) @@ -328,18 +293,148 @@ static void sh_node_curve_rgb_build_multi_function( builder.construct_and_set_matching_fn<CurveRGBFunction>(*cumap); } -void register_node_type_sh_curve_rgb(void) +} // namespace blender::nodes::node_shader_curves_cc + +void register_node_type_sh_curve_rgb() +{ + namespace file_ns = blender::nodes::node_shader_curves_cc; + + static bNodeType ntype; + + sh_fn_node_type_base(&ntype, SH_NODE_CURVE_RGB, "RGB Curves", NODE_CLASS_OP_COLOR); + ntype.declare = file_ns::sh_node_curve_rgb_declare; + node_type_init(&ntype, file_ns::node_shader_init_curve_rgb); + node_type_size_preset(&ntype, NODE_SIZE_LARGE); + node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves); + node_type_gpu(&ntype, file_ns::gpu_shader_curve_rgb); + ntype.build_multi_function = file_ns::sh_node_curve_rgb_build_multi_function; + + nodeRegisterType(&ntype); +} + +/* **************** CURVE FLOAT ******************** */ + +namespace blender::nodes::node_shader_curves_cc { + +static void sh_node_curve_float_declare(NodeDeclarationBuilder &b) +{ + b.is_function_node(); + b.add_input<decl::Float>(N_("Factor")) + .min(0.0f) + .max(1.0f) + .default_value(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("Value")).default_value(1.0f).is_default_link_socket(); + b.add_output<decl::Float>(N_("Value")); +} + +static void node_shader_init_curve_float(bNodeTree *UNUSED(ntree), bNode *node) +{ + node->storage = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); +} + +static int gpu_shader_curve_float(GPUMaterial *mat, + bNode *node, + bNodeExecData *UNUSED(execdata), + GPUNodeStack *in, + GPUNodeStack *out) +{ + float *array, layer; + int size; + + CurveMapping *cumap = (CurveMapping *)node->storage; + + BKE_curvemapping_table_F(cumap, &array, &size); + GPUNodeLink *tex = GPU_color_band(mat, size, array, &layer); + + float ext_xyz[4]; + float range_x; + + const CurveMap *cm = &cumap->cm[0]; + ext_xyz[0] = cm->mintable; + ext_xyz[2] = cm->maxtable; + range_x = 1.0f / max_ff(1e-8f, cm->maxtable - cm->mintable); + /* Compute extrapolation gradients. */ + if ((cumap->flag & CUMA_EXTEND_EXTRAPOLATE) != 0) { + ext_xyz[1] = (cm->ext_in[0] != 0.0f) ? (cm->ext_in[1] / (cm->ext_in[0] * range_x)) : 1e8f; + ext_xyz[3] = (cm->ext_out[0] != 0.0f) ? (cm->ext_out[1] / (cm->ext_out[0] * range_x)) : 1e8f; + } + else { + ext_xyz[1] = 0.0f; + ext_xyz[3] = 0.0f; + } + return GPU_stack_link(mat, + node, + "curve_float", + in, + out, + tex, + GPU_constant(&layer), + GPU_uniform(&range_x), + GPU_uniform(ext_xyz)); +} + +class CurveFloatFunction : public blender::fn::MultiFunction { + private: + const CurveMapping &cumap_; + + public: + CurveFloatFunction(const CurveMapping &cumap) : cumap_(cumap) + { + static blender::fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static blender::fn::MFSignature create_signature() + { + blender::fn::MFSignatureBuilder signature{"Curve Float"}; + signature.single_input<float>("Factor"); + signature.single_input<float>("Value"); + signature.single_output<float>("Value"); + return signature.build(); + } + + void call(blender::IndexMask mask, + blender::fn::MFParams params, + blender::fn::MFContext UNUSED(context)) const override + { + const blender::VArray<float> &fac = params.readonly_single_input<float>(0, "Factor"); + const blender::VArray<float> &val_in = params.readonly_single_input<float>(1, "Value"); + blender::MutableSpan<float> val_out = params.uninitialized_single_output<float>(2, "Value"); + + for (int64_t i : mask) { + val_out[i] = BKE_curvemapping_evaluateF(&cumap_, 0, val_in[i]); + if (fac[i] != 1.0f) { + val_out[i] = (1.0f - fac[i]) * val_in[i] + fac[i] * val_out[i]; + } + } + } +}; + +static void sh_node_curve_float_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) +{ + bNode &bnode = builder.node(); + CurveMapping *cumap = (CurveMapping *)bnode.storage; + BKE_curvemapping_init(cumap); + builder.construct_and_set_matching_fn<CurveFloatFunction>(*cumap); +} + +} // namespace blender::nodes::node_shader_curves_cc + +void register_node_type_sh_curve_float() { + namespace file_ns = blender::nodes::node_shader_curves_cc; + static bNodeType ntype; - sh_fn_node_type_base(&ntype, SH_NODE_CURVE_RGB, "RGB Curves", NODE_CLASS_OP_COLOR, 0); - ntype.declare = blender::nodes::sh_node_curve_rgb_declare; - node_type_init(&ntype, node_shader_init_curve_rgb); + sh_fn_node_type_base(&ntype, SH_NODE_CURVE_FLOAT, "Float Curve", NODE_CLASS_CONVERTER); + ntype.declare = file_ns::sh_node_curve_float_declare; + node_type_init(&ntype, file_ns::node_shader_init_curve_float); node_type_size_preset(&ntype, NODE_SIZE_LARGE); node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves); - node_type_exec(&ntype, node_initexec_curves, nullptr, node_shader_exec_curve_rgb); - node_type_gpu(&ntype, gpu_shader_curve_rgb); - ntype.build_multi_function = sh_node_curve_rgb_build_multi_function; + node_type_gpu(&ntype, file_ns::gpu_shader_curve_float); + ntype.build_multi_function = file_ns::sh_node_curve_float_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_displacement.c b/source/blender/nodes/shader/nodes/node_shader_displacement.cc index c88083d109f..47af2447440 100644 --- a/source/blender/nodes/shader/nodes/node_shader_displacement.c +++ b/source/blender/nodes/shader/nodes/node_shader_displacement.cc @@ -17,22 +17,18 @@ * All rights reserved. */ -#include "../node_shader_util.h" +#include "node_shader_util.hh" -/* **************** OUTPUT ******************** */ +namespace blender::nodes::node_shader_displacement_cc { -static bNodeSocketTemplate sh_node_displacement_in[] = { - {SOCK_FLOAT, N_("Height"), 0.00f, 0.0f, 0.0f, 0.0f, 0.0f, 1000.0f}, - {SOCK_FLOAT, N_("Midlevel"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1000.0f}, - {SOCK_FLOAT, N_("Scale"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1000.0f}, - {SOCK_VECTOR, N_("Normal"), 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, - {-1, ""}, -}; - -static bNodeSocketTemplate sh_node_displacement_out[] = { - {SOCK_VECTOR, N_("Displacement"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {-1, ""}, -}; +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Float>(N_("Height")).default_value(0.0f).min(0.0f).max(1000.0f); + b.add_input<decl::Float>(N_("Midlevel")).default_value(0.0f).min(0.0f).max(1000.0f); + b.add_input<decl::Float>(N_("Scale")).default_value(1.0f).min(0.0f).max(1000.0f); + b.add_input<decl::Vector>(N_("Normal")).hide_value(); + b.add_output<decl::Vector>(N_("Displacement")); +} static void node_shader_init_displacement(bNodeTree *UNUSED(ntree), bNode *node) { @@ -63,16 +59,19 @@ static int gpu_shader_displacement(GPUMaterial *mat, return GPU_stack_link(mat, node, "node_displacement_world", in, out); } +} // namespace blender::nodes::node_shader_displacement_cc + /* node type definition */ -void register_node_type_sh_displacement(void) +void register_node_type_sh_displacement() { + namespace file_ns = blender::nodes::node_shader_displacement_cc; + static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_DISPLACEMENT, "Displacement", NODE_CLASS_OP_VECTOR, 0); - node_type_socket_templates(&ntype, sh_node_displacement_in, sh_node_displacement_out); - node_type_storage(&ntype, "", NULL, NULL); - node_type_init(&ntype, node_shader_init_displacement); - node_type_gpu(&ntype, gpu_shader_displacement); + sh_node_type_base(&ntype, SH_NODE_DISPLACEMENT, "Displacement", NODE_CLASS_OP_VECTOR); + ntype.declare = file_ns::node_declare; + node_type_init(&ntype, file_ns::node_shader_init_displacement); + node_type_gpu(&ntype, file_ns::gpu_shader_displacement); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_eevee_specular.c b/source/blender/nodes/shader/nodes/node_shader_eevee_specular.c deleted file mode 100644 index 191a8b2a376..00000000000 --- a/source/blender/nodes/shader/nodes/node_shader_eevee_specular.c +++ /dev/null @@ -1,103 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2005 Blender Foundation. - * All rights reserved. - */ - -#include "../node_shader_util.h" - -/* **************** OUTPUT ******************** */ - -static bNodeSocketTemplate sh_node_eevee_specular_in[] = { - {SOCK_RGBA, N_("Base Color"), 0.8f, 0.8f, 0.8f, 1.0f}, - {SOCK_RGBA, N_("Specular"), 0.03f, 0.03f, 0.03f, 1.0f}, - {SOCK_FLOAT, N_("Roughness"), 0.2f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_RGBA, N_("Emissive Color"), 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Transparency"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_VECTOR, N_("Normal"), 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, - {SOCK_FLOAT, N_("Clear Coat"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_FLOAT, N_("Clear Coat Roughness"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_VECTOR, - N_("Clear Coat Normal"), - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 1.0f, - PROP_NONE, - SOCK_HIDE_VALUE}, - {SOCK_FLOAT, - N_("Ambient Occlusion"), - 1.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 1.0f, - PROP_NONE, - SOCK_HIDE_VALUE}, - {SOCK_FLOAT, N_("Weight"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, PROP_NONE, SOCK_UNAVAIL}, - {-1, ""}, -}; - -static bNodeSocketTemplate sh_node_eevee_specular_out[] = { - {SOCK_SHADER, N_("BSDF")}, - {-1, ""}, -}; - -static int node_shader_gpu_eevee_specular(GPUMaterial *mat, - bNode *node, - bNodeExecData *UNUSED(execdata), - GPUNodeStack *in, - GPUNodeStack *out) -{ - static float one = 1.0f; - - /* Normals */ - if (!in[5].link) { - GPU_link(mat, "world_normals_get", &in[5].link); - } - - /* Clearcoat Normals */ - if (!in[8].link) { - GPU_link(mat, "world_normals_get", &in[8].link); - } - - /* Occlusion */ - if (!in[9].link) { - GPU_link(mat, "set_value", GPU_constant(&one), &in[9].link); - } - - GPU_material_flag_set(mat, GPU_MATFLAG_DIFFUSE | GPU_MATFLAG_GLOSSY); - - GPU_stack_link(mat, node, "node_eevee_specular", in, out); - return GPU_stack_eval_link(mat, node, "node_eevee_specular_eval", in, out); -} - -/* node type definition */ -void register_node_type_sh_eevee_specular(void) -{ - static bNodeType ntype; - - sh_node_type_base(&ntype, SH_NODE_EEVEE_SPECULAR, "Specular BSDF", NODE_CLASS_SHADER, 0); - node_type_socket_templates(&ntype, sh_node_eevee_specular_in, sh_node_eevee_specular_out); - node_type_init(&ntype, NULL); - node_type_storage(&ntype, "", NULL, NULL); - node_type_gpu(&ntype, node_shader_gpu_eevee_specular); - - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/shader/nodes/node_shader_eevee_specular.cc b/source/blender/nodes/shader/nodes/node_shader_eevee_specular.cc new file mode 100644 index 00000000000..e4c80725c8e --- /dev/null +++ b/source/blender/nodes/shader/nodes/node_shader_eevee_specular.cc @@ -0,0 +1,97 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2005 Blender Foundation. + * All rights reserved. + */ + +#include "node_shader_util.hh" + +namespace blender::nodes::node_shader_eevee_specular_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Color>(N_("Base Color")).default_value({0.8f, 0.8f, 0.8f, 1.0f}); + b.add_input<decl::Color>(N_("Specular")).default_value({0.03f, 0.03f, 0.03f, 1.0f}); + b.add_input<decl::Float>(N_("Roughness")) + .default_value(0.2f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Color>(N_("Emissive Color")).default_value({0.0f, 0.0f, 0.0f, 1.0f}); + b.add_input<decl::Float>(N_("Transparency")) + .default_value(0.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Vector>(N_("Normal")).hide_value(); + b.add_input<decl::Float>(N_("Clear Coat")) + .default_value(0.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("Clear Coat Roughness")) + .default_value(0.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Vector>(N_("Clear Coat Normal")).hide_value(); + b.add_input<decl::Float>(N_("Ambient Occlusion")).hide_value(); + b.add_output<decl::Shader>(N_("BSDF")); +} + +static int node_shader_gpu_eevee_specular(GPUMaterial *mat, + bNode *node, + bNodeExecData *UNUSED(execdata), + GPUNodeStack *in, + GPUNodeStack *out) +{ + static float one = 1.0f; + + /* Normals */ + if (!in[5].link) { + GPU_link(mat, "world_normals_get", &in[5].link); + } + + /* Clearcoat Normals */ + if (!in[8].link) { + GPU_link(mat, "world_normals_get", &in[8].link); + } + + /* Occlusion */ + if (!in[9].link) { + GPU_link(mat, "set_value", GPU_constant(&one), &in[9].link); + } + + GPU_material_flag_set(mat, static_cast<eGPUMatFlag>(GPU_MATFLAG_DIFFUSE | GPU_MATFLAG_GLOSSY)); + + return GPU_stack_link(mat, node, "node_eevee_specular", in, out, GPU_constant(&node->ssr_id)); +} + +} // namespace blender::nodes::node_shader_eevee_specular_cc + +/* node type definition */ +void register_node_type_sh_eevee_specular() +{ + namespace file_ns = blender::nodes::node_shader_eevee_specular_cc; + + static bNodeType ntype; + + sh_node_type_base(&ntype, SH_NODE_EEVEE_SPECULAR, "Specular BSDF", NODE_CLASS_SHADER); + ntype.declare = file_ns::node_declare; + node_type_gpu(&ntype, file_ns::node_shader_gpu_eevee_specular); + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/shader/nodes/node_shader_emission.c b/source/blender/nodes/shader/nodes/node_shader_emission.cc index 64bef6adb46..ea36763578f 100644 --- a/source/blender/nodes/shader/nodes/node_shader_emission.c +++ b/source/blender/nodes/shader/nodes/node_shader_emission.cc @@ -17,21 +17,16 @@ * All rights reserved. */ -#include "../node_shader_util.h" +#include "node_shader_util.hh" -/* **************** OUTPUT ******************** */ +namespace blender::nodes::node_shader_emission_cc { -static bNodeSocketTemplate sh_node_emission_in[] = { - {SOCK_RGBA, N_("Color"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Strength"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1000000.0f}, - {SOCK_FLOAT, N_("Weight"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, PROP_NONE, SOCK_UNAVAIL}, - {-1, ""}, -}; - -static bNodeSocketTemplate sh_node_emission_out[] = { - {SOCK_SHADER, N_("Emission")}, - {-1, ""}, -}; +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Color>(N_("Color")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Float>(N_("Strength")).default_value(1.0f).min(0.0f).max(1000000.0f); + b.add_output<decl::Shader>(N_("Emission")); +} static int node_shader_gpu_emission(GPUMaterial *mat, bNode *node, @@ -39,20 +34,21 @@ static int node_shader_gpu_emission(GPUMaterial *mat, GPUNodeStack *in, GPUNodeStack *out) { - GPU_material_flag_set(mat, GPU_MATFLAG_EMISSION); - return GPU_stack_link(mat, node, "node_emission", in, out); + return GPU_stack_link(mat, node, "node_emission", in, out, GPU_builtin(GPU_VIEW_NORMAL)); } +} // namespace blender::nodes::node_shader_emission_cc + /* node type definition */ -void register_node_type_sh_emission(void) +void register_node_type_sh_emission() { + namespace file_ns = blender::nodes::node_shader_emission_cc; + static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_EMISSION, "Emission", NODE_CLASS_SHADER, 0); - node_type_socket_templates(&ntype, sh_node_emission_in, sh_node_emission_out); - node_type_init(&ntype, NULL); - node_type_storage(&ntype, "", NULL, NULL); - node_type_gpu(&ntype, node_shader_gpu_emission); + sh_node_type_base(&ntype, SH_NODE_EMISSION, "Emission", NODE_CLASS_SHADER); + ntype.declare = file_ns::node_declare; + node_type_gpu(&ntype, file_ns::node_shader_gpu_emission); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_fresnel.c b/source/blender/nodes/shader/nodes/node_shader_fresnel.cc index 9b25af912ef..19eb79e2138 100644 --- a/source/blender/nodes/shader/nodes/node_shader_fresnel.c +++ b/source/blender/nodes/shader/nodes/node_shader_fresnel.cc @@ -17,19 +17,16 @@ * All rights reserved. */ -#include "../node_shader_util.h" +#include "node_shader_util.hh" -/* **************** Fresnel ******************** */ -static bNodeSocketTemplate sh_node_fresnel_in[] = { - {SOCK_FLOAT, N_("IOR"), 1.45f, 0.0f, 0.0f, 0.0f, 0.0f, 1000.0f}, - {SOCK_VECTOR, N_("Normal"), 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, - {-1, ""}, -}; +namespace blender::nodes::node_shader_fresnel_cc { -static bNodeSocketTemplate sh_node_fresnel_out[] = { - {SOCK_FLOAT, N_("Fac"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {-1, ""}, -}; +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Float>(N_("IOR")).default_value(1.45f).min(0.0f).max(1000.0f); + b.add_input<decl::Vector>(N_("Normal")).hide_value(); + b.add_output<decl::Float>(N_("Fac")); +} static int node_shader_gpu_fresnel(GPUMaterial *mat, bNode *node, @@ -44,26 +41,18 @@ static int node_shader_gpu_fresnel(GPUMaterial *mat, return GPU_stack_link(mat, node, "node_fresnel", in, out); } -static void node_shader_exec_fresnel(void *UNUSED(data), - int UNUSED(thread), - bNode *UNUSED(node), - bNodeExecData *UNUSED(execdata), - bNodeStack **UNUSED(in), - bNodeStack **UNUSED(out)) -{ -} +} // namespace blender::nodes::node_shader_fresnel_cc /* node type definition */ -void register_node_type_sh_fresnel(void) +void register_node_type_sh_fresnel() { + namespace file_ns = blender::nodes::node_shader_fresnel_cc; + static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_FRESNEL, "Fresnel", NODE_CLASS_INPUT, 0); - node_type_socket_templates(&ntype, sh_node_fresnel_in, sh_node_fresnel_out); - node_type_init(&ntype, NULL); - node_type_storage(&ntype, "", NULL, NULL); - node_type_gpu(&ntype, node_shader_gpu_fresnel); - node_type_exec(&ntype, NULL, NULL, node_shader_exec_fresnel); + sh_node_type_base(&ntype, SH_NODE_FRESNEL, "Fresnel", NODE_CLASS_INPUT); + ntype.declare = file_ns::node_declare; + node_type_gpu(&ntype, file_ns::node_shader_gpu_fresnel); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_gamma.c b/source/blender/nodes/shader/nodes/node_shader_gamma.c deleted file mode 100644 index b48838e5f56..00000000000 --- a/source/blender/nodes/shader/nodes/node_shader_gamma.c +++ /dev/null @@ -1,73 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2006 Blender Foundation. - * All rights reserved. - */ - -#include "node_shader_util.h" - -/* **************** Gamma Tools ******************** */ - -static bNodeSocketTemplate sh_node_gamma_in[] = { - {SOCK_RGBA, N_("Color"), 1.0f, 1.0f, 1.0f, 1.0f}, - {SOCK_FLOAT, N_("Gamma"), 1.0f, 0.0f, 0.0f, 0.0f, 0.001f, 10.0f, PROP_UNSIGNED}, - {-1, ""}, -}; - -static bNodeSocketTemplate sh_node_gamma_out[] = { - {SOCK_RGBA, N_("Color")}, - {-1, ""}, -}; - -static void node_shader_exec_gamma(void *UNUSED(data), - int UNUSED(thread), - bNode *UNUSED(node), - bNodeExecData *UNUSED(execdata), - bNodeStack **in, - bNodeStack **out) -{ - float col[3]; - float gamma; - nodestack_get_vec(col, SOCK_VECTOR, in[0]); - nodestack_get_vec(&gamma, SOCK_FLOAT, in[1]); - - out[0]->vec[0] = col[0] > 0.0f ? powf(col[0], gamma) : col[0]; - out[0]->vec[1] = col[1] > 0.0f ? powf(col[1], gamma) : col[1]; - out[0]->vec[2] = col[2] > 0.0f ? powf(col[2], gamma) : col[2]; -} - -static int node_shader_gpu_gamma(GPUMaterial *mat, - bNode *node, - bNodeExecData *UNUSED(execdata), - GPUNodeStack *in, - GPUNodeStack *out) -{ - return GPU_stack_link(mat, node, "node_gamma", in, out); -} - -void register_node_type_sh_gamma(void) -{ - static bNodeType ntype; - - sh_node_type_base(&ntype, SH_NODE_GAMMA, "Gamma", NODE_CLASS_OP_COLOR, 0); - node_type_socket_templates(&ntype, sh_node_gamma_in, sh_node_gamma_out); - node_type_init(&ntype, NULL); - node_type_storage(&ntype, "", NULL, NULL); - node_type_exec(&ntype, NULL, NULL, node_shader_exec_gamma); - node_type_gpu(&ntype, node_shader_gpu_gamma); - - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/shader/nodes/node_shader_gamma.cc b/source/blender/nodes/shader/nodes/node_shader_gamma.cc new file mode 100644 index 00000000000..98a287b08da --- /dev/null +++ b/source/blender/nodes/shader/nodes/node_shader_gamma.cc @@ -0,0 +1,57 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2006 Blender Foundation. + * All rights reserved. + */ + +#include "node_shader_util.hh" + +namespace blender::nodes::node_shader_gamma_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Color>(N_("Color")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Float>(N_("Gamma")) + .default_value(1.0f) + .min(0.001f) + .max(10.0f) + .subtype(PROP_UNSIGNED); + b.add_output<decl::Color>(N_("Color")); +} + +static int node_shader_gpu_gamma(GPUMaterial *mat, + bNode *node, + bNodeExecData *UNUSED(execdata), + GPUNodeStack *in, + GPUNodeStack *out) +{ + return GPU_stack_link(mat, node, "node_gamma", in, out); +} + +} // namespace blender::nodes::node_shader_gamma_cc + +void register_node_type_sh_gamma() +{ + namespace file_ns = blender::nodes::node_shader_gamma_cc; + + static bNodeType ntype; + + sh_node_type_base(&ntype, SH_NODE_GAMMA, "Gamma", NODE_CLASS_OP_COLOR); + ntype.declare = file_ns::node_declare; + node_type_gpu(&ntype, file_ns::node_shader_gpu_gamma); + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/shader/nodes/node_shader_geometry.c b/source/blender/nodes/shader/nodes/node_shader_geometry.cc index 633f071ab86..35eef95adb5 100644 --- a/source/blender/nodes/shader/nodes/node_shader_geometry.c +++ b/source/blender/nodes/shader/nodes/node_shader_geometry.cc @@ -17,22 +17,22 @@ * All rights reserved. */ -#include "../node_shader_util.h" +#include "node_shader_util.hh" -/* **************** OUTPUT ******************** */ +namespace blender::nodes::node_shader_geometry_cc { -static bNodeSocketTemplate sh_node_geometry_out[] = { - {SOCK_VECTOR, N_("Position"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_VECTOR, N_("Normal"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_VECTOR, N_("Tangent"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_VECTOR, N_("True Normal"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_VECTOR, N_("Incoming"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_VECTOR, N_("Parametric"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Backfacing"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Pointiness"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Random Per Island"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {-1, ""}, -}; +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_output<decl::Vector>(N_("Position")); + b.add_output<decl::Vector>(N_("Normal")); + b.add_output<decl::Vector>(N_("Tangent")); + b.add_output<decl::Vector>(N_("True Normal")); + b.add_output<decl::Vector>(N_("Incoming")); + b.add_output<decl::Vector>(N_("Parametric")); + b.add_output<decl::Float>(N_("Backfacing")); + b.add_output<decl::Float>(N_("Pointiness")); + b.add_output<decl::Float>(N_("Random Per Island")); +} static int node_shader_gpu_geometry(GPUMaterial *mat, bNode *node, @@ -52,8 +52,8 @@ static int node_shader_gpu_geometry(GPUMaterial *mat, const bool success = GPU_stack_link(mat, node, "node_geometry", in, out, orco_link); - /* for each output */ - for (int i = 0; sh_node_geometry_out[i].type != -1; i++) { + int i; + LISTBASE_FOREACH_INDEX (bNodeSocket *, sock, &node->outputs, i) { node_shader_gpu_bump_tex_coord(mat, node, &out[i].link); /* Normalize some vectors after dFdx/dFdy offsets. * This is the case for interpolated, non linear functions. @@ -67,23 +67,25 @@ static int node_shader_gpu_geometry(GPUMaterial *mat, out[i].link, out[i].link, &out[i].link, - NULL); + nullptr); } } return success; } +} // namespace blender::nodes::node_shader_geometry_cc + /* node type definition */ -void register_node_type_sh_geometry(void) +void register_node_type_sh_geometry() { + namespace file_ns = blender::nodes::node_shader_geometry_cc; + static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_NEW_GEOMETRY, "Geometry", NODE_CLASS_INPUT, 0); - node_type_socket_templates(&ntype, NULL, sh_node_geometry_out); - node_type_init(&ntype, NULL); - node_type_storage(&ntype, "", NULL, NULL); - node_type_gpu(&ntype, node_shader_gpu_geometry); + sh_node_type_base(&ntype, SH_NODE_NEW_GEOMETRY, "Geometry", NODE_CLASS_INPUT); + ntype.declare = file_ns::node_declare; + node_type_gpu(&ntype, file_ns::node_shader_gpu_geometry); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_hair_info.c b/source/blender/nodes/shader/nodes/node_shader_hair_info.cc index c721fb9c77a..12c29c40b1d 100644 --- a/source/blender/nodes/shader/nodes/node_shader_hair_info.c +++ b/source/blender/nodes/shader/nodes/node_shader_hair_info.cc @@ -17,18 +17,19 @@ * All rights reserved. */ -#include "../node_shader_util.h" - -static bNodeSocketTemplate outputs[] = { - {SOCK_FLOAT, N_("Is Strand"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Intercept"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Length"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Thickness"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_VECTOR, N_("Tangent Normal"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - // { SOCK_FLOAT, 0, N_("Fade"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Random")}, - {-1, ""}, -}; +#include "node_shader_util.hh" + +namespace blender::nodes::node_shader_hair_info_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_output<decl::Float>(N_("Is Strand")); + b.add_output<decl::Float>(N_("Intercept")); + b.add_output<decl::Float>(N_("Length")); + b.add_output<decl::Float>(N_("Thickness")); + b.add_output<decl::Vector>(N_("Tangent Normal")); + b.add_output<decl::Float>(N_("Random")); +} static int node_shader_gpu_hair_info(GPUMaterial *mat, bNode *node, @@ -43,16 +44,18 @@ static int node_shader_gpu_hair_info(GPUMaterial *mat, return GPU_stack_link(mat, node, "node_hair_info", in, out, length_link); } +} // namespace blender::nodes::node_shader_hair_info_cc + /* node type definition */ -void register_node_type_sh_hair_info(void) +void register_node_type_sh_hair_info() { + namespace file_ns = blender::nodes::node_shader_hair_info_cc; + static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_HAIR_INFO, "Hair Info", NODE_CLASS_INPUT, 0); - node_type_socket_templates(&ntype, NULL, outputs); - node_type_init(&ntype, NULL); - node_type_storage(&ntype, "", NULL, NULL); - node_type_gpu(&ntype, node_shader_gpu_hair_info); + sh_node_type_base(&ntype, SH_NODE_HAIR_INFO, "Hair Info", NODE_CLASS_INPUT); + ntype.declare = file_ns::node_declare; + node_type_gpu(&ntype, file_ns::node_shader_gpu_hair_info); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_holdout.c b/source/blender/nodes/shader/nodes/node_shader_holdout.cc index eb087183a0b..4f6f8a639de 100644 --- a/source/blender/nodes/shader/nodes/node_shader_holdout.c +++ b/source/blender/nodes/shader/nodes/node_shader_holdout.cc @@ -17,19 +17,14 @@ * All rights reserved. */ -#include "../node_shader_util.h" +#include "node_shader_util.hh" -/* **************** OUTPUT ******************** */ +namespace blender::nodes::node_shader_holdout_cc { -static bNodeSocketTemplate sh_node_holdout_in[] = { - {SOCK_FLOAT, N_("Weight"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, PROP_NONE, SOCK_UNAVAIL}, - {-1, ""}, -}; - -static bNodeSocketTemplate sh_node_holdout_out[] = { - {SOCK_SHADER, N_("Holdout")}, - {-1, ""}, -}; +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_output<decl::Shader>(N_("Holdout")); +} static int gpu_shader_rgb(GPUMaterial *mat, bNode *node, @@ -37,20 +32,21 @@ static int gpu_shader_rgb(GPUMaterial *mat, GPUNodeStack *in, GPUNodeStack *out) { - GPU_material_flag_set(mat, GPU_MATFLAG_HOLDOUT); return GPU_stack_link(mat, node, "node_holdout", in, out); } +} // namespace blender::nodes::node_shader_holdout_cc + /* node type definition */ -void register_node_type_sh_holdout(void) +void register_node_type_sh_holdout() { + namespace file_ns = blender::nodes::node_shader_holdout_cc; + static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_HOLDOUT, "Holdout", NODE_CLASS_SHADER, 0); - node_type_socket_templates(&ntype, sh_node_holdout_in, sh_node_holdout_out); - node_type_init(&ntype, NULL); - node_type_storage(&ntype, "", NULL, NULL); - node_type_gpu(&ntype, gpu_shader_rgb); + sh_node_type_base(&ntype, SH_NODE_HOLDOUT, "Holdout", NODE_CLASS_SHADER); + ntype.declare = file_ns::node_declare; + node_type_gpu(&ntype, file_ns::gpu_shader_rgb); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_hueSatVal.c b/source/blender/nodes/shader/nodes/node_shader_hueSatVal.c deleted file mode 100644 index 50eb5bf32c9..00000000000 --- a/source/blender/nodes/shader/nodes/node_shader_hueSatVal.c +++ /dev/null @@ -1,99 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2006 Blender Foundation. - * All rights reserved. - */ - -/** \file - * \ingroup shdnodes - */ - -#include "node_shader_util.h" - -/* **************** Hue Saturation ******************** */ -static bNodeSocketTemplate sh_node_hue_sat_in[] = { - {SOCK_FLOAT, N_("Hue"), 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE}, - {SOCK_FLOAT, N_("Saturation"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 2.0f, PROP_NONE}, - {SOCK_FLOAT, N_("Value"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 2.0f, PROP_NONE}, - {SOCK_FLOAT, N_("Fac"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_RGBA, N_("Color"), 0.8f, 0.8f, 0.8f, 1.0f}, - {-1, ""}, -}; -static bNodeSocketTemplate sh_node_hue_sat_out[] = { - {SOCK_RGBA, N_("Color")}, - {-1, ""}, -}; - -/* NOTE: it would be possible to use CMP version for both nodes. */ -static void do_hue_sat_fac( - bNode *UNUSED(node), float *out, float hue, float sat, float val, const float in[4], float fac) -{ - if (fac != 0.0f && (hue != 0.5f || sat != 1.0f || val != 1.0f)) { - float col[3], hsv[3], mfac = 1.0f - fac; - - rgb_to_hsv(in[0], in[1], in[2], hsv, hsv + 1, hsv + 2); - hsv[0] = fmodf(hsv[0] + hue + 0.5f, 1.0f); - hsv[1] = clamp_f(hsv[1] * sat, 0.0f, 1.0f); - hsv[2] *= val; - hsv_to_rgb(hsv[0], hsv[1], hsv[2], col, col + 1, col + 2); - - out[0] = mfac * in[0] + fac * col[0]; - out[1] = mfac * in[1] + fac * col[1]; - out[2] = mfac * in[2] + fac * col[2]; - } - else { - copy_v4_v4(out, in); - } -} - -static void node_shader_exec_hue_sat(void *UNUSED(data), - int UNUSED(thread), - bNode *node, - bNodeExecData *UNUSED(execdata), - bNodeStack **in, - bNodeStack **out) -{ - float hue, sat, val, fac; - float col[4]; - nodestack_get_vec(&hue, SOCK_FLOAT, in[0]); - nodestack_get_vec(&sat, SOCK_FLOAT, in[1]); - nodestack_get_vec(&val, SOCK_FLOAT, in[2]); - nodestack_get_vec(&fac, SOCK_FLOAT, in[3]); - nodestack_get_vec(col, SOCK_RGBA, in[4]); - do_hue_sat_fac(node, out[0]->vec, hue, sat, val, col, fac); -} - -static int gpu_shader_hue_sat(GPUMaterial *mat, - bNode *node, - bNodeExecData *UNUSED(execdata), - GPUNodeStack *in, - GPUNodeStack *out) -{ - return GPU_stack_link(mat, node, "hue_sat", in, out); -} - -void register_node_type_sh_hue_sat(void) -{ - static bNodeType ntype; - - sh_node_type_base(&ntype, SH_NODE_HUE_SAT, "Hue Saturation Value", NODE_CLASS_OP_COLOR, 0); - node_type_socket_templates(&ntype, sh_node_hue_sat_in, sh_node_hue_sat_out); - node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); - node_type_exec(&ntype, NULL, NULL, node_shader_exec_hue_sat); - node_type_gpu(&ntype, gpu_shader_hue_sat); - - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/shader/nodes/node_shader_hueSatVal.cc b/source/blender/nodes/shader/nodes/node_shader_hueSatVal.cc new file mode 100644 index 00000000000..94b9a61c602 --- /dev/null +++ b/source/blender/nodes/shader/nodes/node_shader_hueSatVal.cc @@ -0,0 +1,61 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2006 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup shdnodes + */ + +#include "node_shader_util.hh" + +namespace blender::nodes::node_shader_hueSatVal_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Float>(N_("Hue")).default_value(0.5f).min(0.0f).max(1.0f); + b.add_input<decl::Float>(N_("Saturation")).default_value(1.0f).min(0.0f).max(2.0f); + b.add_input<decl::Float>(N_("Value")).default_value(1.0f).min(0.0f).max(2.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_("Color")).default_value({0.8f, 0.8f, 0.8f, 1.0f}); + b.add_output<decl::Color>(N_("Color")); +} + +static int gpu_shader_hue_sat(GPUMaterial *mat, + bNode *node, + bNodeExecData *UNUSED(execdata), + GPUNodeStack *in, + GPUNodeStack *out) +{ + return GPU_stack_link(mat, node, "hue_sat", in, out); +} + +} // namespace blender::nodes::node_shader_hueSatVal_cc + +void register_node_type_sh_hue_sat() +{ + namespace file_ns = blender::nodes::node_shader_hueSatVal_cc; + + static bNodeType ntype; + + sh_node_type_base(&ntype, SH_NODE_HUE_SAT, "Hue Saturation Value", NODE_CLASS_OP_COLOR); + ntype.declare = file_ns::node_declare; + node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); + node_type_gpu(&ntype, file_ns::gpu_shader_hue_sat); + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/shader/nodes/node_shader_ies_light.c b/source/blender/nodes/shader/nodes/node_shader_ies_light.c deleted file mode 100644 index 9cc5fd46181..00000000000 --- a/source/blender/nodes/shader/nodes/node_shader_ies_light.c +++ /dev/null @@ -1,53 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2018 Blender Foundation. - * All rights reserved. - */ - -#include "../node_shader_util.h" - -/* **************** IES Light ******************** */ - -static bNodeSocketTemplate sh_node_tex_ies_in[] = { - {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, - {SOCK_FLOAT, N_("Strength"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1000000.0f, PROP_NONE}, - {-1, ""}, -}; - -static bNodeSocketTemplate sh_node_tex_ies_out[] = { - {SOCK_FLOAT, N_("Fac")}, - {-1, ""}, -}; - -static void node_shader_init_tex_ies(bNodeTree *UNUSED(ntree), bNode *node) -{ - NodeShaderTexIES *tex = MEM_callocN(sizeof(NodeShaderTexIES), "NodeShaderIESLight"); - node->storage = tex; -} - -/* node type definition */ -void register_node_type_sh_tex_ies(void) -{ - static bNodeType ntype; - - sh_node_type_base(&ntype, SH_NODE_TEX_IES, "IES Texture", NODE_CLASS_TEXTURE, 0); - node_type_socket_templates(&ntype, sh_node_tex_ies_in, sh_node_tex_ies_out); - node_type_init(&ntype, node_shader_init_tex_ies); - node_type_storage( - &ntype, "NodeShaderTexIES", node_free_standard_storage, node_copy_standard_storage); - - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/shader/nodes/node_shader_ies_light.cc b/source/blender/nodes/shader/nodes/node_shader_ies_light.cc new file mode 100644 index 00000000000..82f0a3045c9 --- /dev/null +++ b/source/blender/nodes/shader/nodes/node_shader_ies_light.cc @@ -0,0 +1,74 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2018 Blender Foundation. + * All rights reserved. + */ + +#include "node_shader_util.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes::node_shader_ies_light_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Vector>(N_("Vector")).hide_value(); + b.add_input<decl::Float>(N_("Strength")).default_value(1.0f).min(0.0f).max(1000000.0f); + b.add_output<decl::Float>(N_("Fac")); +} + +static void node_shader_buts_ies(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayout *row; + + row = uiLayoutRow(layout, false); + uiItemR(row, ptr, "mode", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_EXPAND, nullptr, ICON_NONE); + + row = uiLayoutRow(layout, true); + + if (RNA_enum_get(ptr, "mode") == NODE_IES_INTERNAL) { + uiItemR(row, ptr, "ies", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); + } + else { + uiItemR(row, ptr, "filepath", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); + } +} + +static void node_shader_init_tex_ies(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeShaderTexIES *tex = MEM_cnew<NodeShaderTexIES>("NodeShaderIESLight"); + node->storage = tex; +} + +} // namespace blender::nodes::node_shader_ies_light_cc + +/* node type definition */ +void register_node_type_sh_tex_ies() +{ + namespace file_ns = blender::nodes::node_shader_ies_light_cc; + + static bNodeType ntype; + + sh_node_type_base(&ntype, SH_NODE_TEX_IES, "IES Texture", NODE_CLASS_TEXTURE); + ntype.declare = file_ns::node_declare; + ntype.draw_buttons = file_ns::node_shader_buts_ies; + node_type_init(&ntype, file_ns::node_shader_init_tex_ies); + node_type_storage( + &ntype, "NodeShaderTexIES", node_free_standard_storage, node_copy_standard_storage); + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/shader/nodes/node_shader_invert.c b/source/blender/nodes/shader/nodes/node_shader_invert.c deleted file mode 100644 index 0d6709a1968..00000000000 --- a/source/blender/nodes/shader/nodes/node_shader_invert.c +++ /dev/null @@ -1,78 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2005 Blender Foundation. - * All rights reserved. - */ - -/** \file - * \ingroup shdnodes - */ - -#include "node_shader_util.h" - -/* **************** INVERT ******************** */ -static bNodeSocketTemplate sh_node_invert_in[] = { - {SOCK_FLOAT, N_("Fac"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_RGBA, N_("Color"), 0.0f, 0.0f, 0.0f, 1.0f}, - {-1, ""}}; - -static bNodeSocketTemplate sh_node_invert_out[] = {{SOCK_RGBA, N_("Color")}, {-1, ""}}; - -static void node_shader_exec_invert(void *UNUSED(data), - int UNUSED(thread), - bNode *UNUSED(node), - bNodeExecData *UNUSED(execdata), - bNodeStack **in, - bNodeStack **out) -{ - float col[3], icol[3], fac; - - nodestack_get_vec(&fac, SOCK_FLOAT, in[0]); - nodestack_get_vec(col, SOCK_VECTOR, in[1]); - - icol[0] = 1.0f - col[0]; - icol[1] = 1.0f - col[1]; - icol[2] = 1.0f - col[2]; - - /* if fac, blend result against original input */ - if (fac < 1.0f) { - interp_v3_v3v3(out[0]->vec, col, icol, fac); - } - else { - copy_v3_v3(out[0]->vec, icol); - } -} - -static int gpu_shader_invert(GPUMaterial *mat, - bNode *node, - bNodeExecData *UNUSED(execdata), - GPUNodeStack *in, - GPUNodeStack *out) -{ - return GPU_stack_link(mat, node, "invert", in, out); -} - -void register_node_type_sh_invert(void) -{ - static bNodeType ntype; - - sh_node_type_base(&ntype, SH_NODE_INVERT, "Invert", NODE_CLASS_OP_COLOR, 0); - node_type_socket_templates(&ntype, sh_node_invert_in, sh_node_invert_out); - node_type_exec(&ntype, NULL, NULL, node_shader_exec_invert); - node_type_gpu(&ntype, gpu_shader_invert); - - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/shader/nodes/node_shader_invert.cc b/source/blender/nodes/shader/nodes/node_shader_invert.cc new file mode 100644 index 00000000000..3b5f4cb1d38 --- /dev/null +++ b/source/blender/nodes/shader/nodes/node_shader_invert.cc @@ -0,0 +1,57 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2005 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup shdnodes + */ + +#include "node_shader_util.hh" + +namespace blender::nodes::node_shader_invert_cc { + +static void node_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({0.0f, 0.0f, 0.0f, 1.0f}); + b.add_output<decl::Color>(N_("Color")); +} + +static int gpu_shader_invert(GPUMaterial *mat, + bNode *node, + bNodeExecData *UNUSED(execdata), + GPUNodeStack *in, + GPUNodeStack *out) +{ + return GPU_stack_link(mat, node, "invert", in, out); +} + +} // namespace blender::nodes::node_shader_invert_cc + +void register_node_type_sh_invert() +{ + namespace file_ns = blender::nodes::node_shader_invert_cc; + + static bNodeType ntype; + + sh_node_type_base(&ntype, SH_NODE_INVERT, "Invert", NODE_CLASS_OP_COLOR); + ntype.declare = file_ns::node_declare; + node_type_gpu(&ntype, file_ns::gpu_shader_invert); + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/shader/nodes/node_shader_layer_weight.c b/source/blender/nodes/shader/nodes/node_shader_layer_weight.cc index 599a44c2ec3..0294a0bde07 100644 --- a/source/blender/nodes/shader/nodes/node_shader_layer_weight.c +++ b/source/blender/nodes/shader/nodes/node_shader_layer_weight.cc @@ -17,21 +17,17 @@ * All rights reserved. */ -#include "../node_shader_util.h" +#include "node_shader_util.hh" -/* **************** Layer Weight ******************** */ +namespace blender::nodes::node_shader_layer_weight_cc { -static bNodeSocketTemplate sh_node_layer_weight_in[] = { - {SOCK_FLOAT, N_("Blend"), 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_VECTOR, N_("Normal"), 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, - {-1, ""}, -}; - -static bNodeSocketTemplate sh_node_layer_weight_out[] = { - {SOCK_FLOAT, N_("Fresnel"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Facing"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {-1, ""}, -}; +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Float>(N_("Blend")).default_value(0.5f).min(0.0f).max(1.0f); + b.add_input<decl::Vector>(N_("Normal")).hide_value(); + b.add_output<decl::Float>(N_("Fresnel")); + b.add_output<decl::Float>(N_("Facing")); +} static int node_shader_gpu_layer_weight(GPUMaterial *mat, bNode *node, @@ -46,26 +42,18 @@ static int node_shader_gpu_layer_weight(GPUMaterial *mat, return GPU_stack_link(mat, node, "node_layer_weight", in, out); } -static void node_shader_exec_layer_weight(void *UNUSED(data), - int UNUSED(thread), - bNode *UNUSED(node), - bNodeExecData *UNUSED(execdata), - bNodeStack **UNUSED(in), - bNodeStack **UNUSED(out)) -{ -} +} // namespace blender::nodes::node_shader_layer_weight_cc /* node type definition */ -void register_node_type_sh_layer_weight(void) +void register_node_type_sh_layer_weight() { + namespace file_ns = blender::nodes::node_shader_layer_weight_cc; + static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_LAYER_WEIGHT, "Layer Weight", NODE_CLASS_INPUT, 0); - node_type_socket_templates(&ntype, sh_node_layer_weight_in, sh_node_layer_weight_out); - node_type_init(&ntype, NULL); - node_type_storage(&ntype, "", NULL, NULL); - node_type_gpu(&ntype, node_shader_gpu_layer_weight); - node_type_exec(&ntype, NULL, NULL, node_shader_exec_layer_weight); + sh_node_type_base(&ntype, SH_NODE_LAYER_WEIGHT, "Layer Weight", NODE_CLASS_INPUT); + ntype.declare = file_ns::node_declare; + node_type_gpu(&ntype, file_ns::node_shader_gpu_layer_weight); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_light_falloff.c b/source/blender/nodes/shader/nodes/node_shader_light_falloff.cc index 22172221f77..50eea2b3643 100644 --- a/source/blender/nodes/shader/nodes/node_shader_light_falloff.c +++ b/source/blender/nodes/shader/nodes/node_shader_light_falloff.cc @@ -17,24 +17,18 @@ * All rights reserved. */ -#include "../node_shader_util.h" +#include "node_shader_util.hh" -/* **************** INPUT ********************* */ +namespace blender::nodes::node_shader_light_falloff_cc { -static bNodeSocketTemplate sh_node_light_falloff_in[] = { - {SOCK_FLOAT, N_("Strength"), 100.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1000000.0f}, - {SOCK_FLOAT, N_("Smooth"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1000.0f}, - {-1, ""}, -}; - -/* **************** OUTPUT ******************** */ - -static bNodeSocketTemplate sh_node_light_falloff_out[] = { - {SOCK_FLOAT, N_("Quadratic"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Linear"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Constant"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {-1, ""}, -}; +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Float>(N_("Strength")).default_value(100.0f).min(0.0f).max(1000000.0f); + b.add_input<decl::Float>(N_("Smooth")).default_value(0.0f).min(0.0f).max(1000.0f); + b.add_output<decl::Float>(N_("Quadratic")); + b.add_output<decl::Float>(N_("Linear")); + b.add_output<decl::Float>(N_("Constant")); +} static int node_shader_gpu_light_falloff(GPUMaterial *mat, bNode *node, @@ -45,17 +39,19 @@ static int node_shader_gpu_light_falloff(GPUMaterial *mat, return GPU_stack_link(mat, node, "node_light_falloff", in, out); } +} // namespace blender::nodes::node_shader_light_falloff_cc + /* node type definition */ -void register_node_type_sh_light_falloff(void) +void register_node_type_sh_light_falloff() { + namespace file_ns = blender::nodes::node_shader_light_falloff_cc; + static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_LIGHT_FALLOFF, "Light Falloff", NODE_CLASS_OP_COLOR, 0); - node_type_socket_templates(&ntype, sh_node_light_falloff_in, sh_node_light_falloff_out); + sh_node_type_base(&ntype, SH_NODE_LIGHT_FALLOFF, "Light Falloff", NODE_CLASS_OP_COLOR); + ntype.declare = file_ns::node_declare; node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); - node_type_init(&ntype, NULL); - node_type_storage(&ntype, "", NULL, NULL); - node_type_gpu(&ntype, node_shader_gpu_light_falloff); + node_type_gpu(&ntype, file_ns::node_shader_gpu_light_falloff); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_light_path.c b/source/blender/nodes/shader/nodes/node_shader_light_path.c deleted file mode 100644 index 45ad2133ee8..00000000000 --- a/source/blender/nodes/shader/nodes/node_shader_light_path.c +++ /dev/null @@ -1,62 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2005 Blender Foundation. - * All rights reserved. - */ - -#include "../node_shader_util.h" - -/* **************** OUTPUT ******************** */ - -static bNodeSocketTemplate sh_node_light_path_out[] = { - {SOCK_FLOAT, N_("Is Camera Ray"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Is Shadow Ray"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Is Diffuse Ray"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Is Glossy Ray"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Is Singular Ray"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Is Reflection Ray"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Is Transmission Ray"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Ray Length"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Ray Depth"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Diffuse Depth"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Glossy Depth"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Transparent Depth"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Transmission Depth"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {-1, ""}, -}; - -static int node_shader_gpu_light_path(GPUMaterial *mat, - bNode *node, - bNodeExecData *UNUSED(execdata), - GPUNodeStack *in, - GPUNodeStack *out) -{ - return GPU_stack_link(mat, node, "node_light_path", in, out); -} - -/* node type definition */ -void register_node_type_sh_light_path(void) -{ - static bNodeType ntype; - - sh_node_type_base(&ntype, SH_NODE_LIGHT_PATH, "Light Path", NODE_CLASS_INPUT, 0); - node_type_socket_templates(&ntype, NULL, sh_node_light_path_out); - node_type_init(&ntype, NULL); - node_type_storage(&ntype, "", NULL, NULL); - node_type_gpu(&ntype, node_shader_gpu_light_path); - - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/shader/nodes/node_shader_light_path.cc b/source/blender/nodes/shader/nodes/node_shader_light_path.cc new file mode 100644 index 00000000000..6fbd5751886 --- /dev/null +++ b/source/blender/nodes/shader/nodes/node_shader_light_path.cc @@ -0,0 +1,64 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2005 Blender Foundation. + * All rights reserved. + */ + +#include "node_shader_util.hh" + +namespace blender::nodes::node_shader_light_path_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_output<decl::Float>(N_("Is Camera Ray")); + b.add_output<decl::Float>(N_("Is Shadow Ray")); + b.add_output<decl::Float>(N_("Is Diffuse Ray")); + b.add_output<decl::Float>(N_("Is Glossy Ray")); + b.add_output<decl::Float>(N_("Is Singular Ray")); + b.add_output<decl::Float>(N_("Is Reflection Ray")); + b.add_output<decl::Float>(N_("Is Transmission Ray")); + b.add_output<decl::Float>(N_("Ray Length")); + b.add_output<decl::Float>(N_("Ray Depth")); + b.add_output<decl::Float>(N_("Diffuse Depth")); + b.add_output<decl::Float>(N_("Glossy Depth")); + b.add_output<decl::Float>(N_("Transparent Depth")); + b.add_output<decl::Float>(N_("Transmission Depth")); +} + +static int node_shader_gpu_light_path(GPUMaterial *mat, + bNode *node, + bNodeExecData *UNUSED(execdata), + GPUNodeStack *in, + GPUNodeStack *out) +{ + return GPU_stack_link(mat, node, "node_light_path", in, out); +} + +} // namespace blender::nodes::node_shader_light_path_cc + +/* node type definition */ +void register_node_type_sh_light_path() +{ + namespace file_ns = blender::nodes::node_shader_light_path_cc; + + static bNodeType ntype; + + sh_node_type_base(&ntype, SH_NODE_LIGHT_PATH, "Light Path", NODE_CLASS_INPUT); + ntype.declare = file_ns::node_declare; + node_type_gpu(&ntype, file_ns::node_shader_gpu_light_path); + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/shader/nodes/node_shader_map_range.cc b/source/blender/nodes/shader/nodes/node_shader_map_range.cc index 5ea194ddc83..bc7ca661a77 100644 --- a/source/blender/nodes/shader/nodes/node_shader_map_range.cc +++ b/source/blender/nodes/shader/nodes/node_shader_map_range.cc @@ -21,49 +21,173 @@ * \ingroup shdnodes */ -#include "node_shader_util.h" +#include <algorithm> + +#include "node_shader_util.hh" #include "BLI_math_base_safe.h" -namespace blender::nodes { +#include "NOD_socket_search_link.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes::node_shader_map_range_cc { + +NODE_STORAGE_FUNCS(NodeMapRange) static void sh_node_map_range_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input<decl::Float>("Value").min(-10000.0f).max(10000.0f).default_value(1.0f); - b.add_input<decl::Float>("From Min").min(-10000.0f).max(10000.0f); - b.add_input<decl::Float>("From Max").min(-10000.0f).max(10000.0f).default_value(1.0f); - b.add_input<decl::Float>("To Min").min(-10000.0f).max(10000.0f); - b.add_input<decl::Float>("To Max").min(-10000.0f).max(10000.0f).default_value(1.0f); - b.add_input<decl::Float>("Steps").min(-10000.0f).max(10000.0f).default_value(4.0f); - b.add_output<decl::Float>("Result"); -}; + b.add_input<decl::Float>(N_("Value")).min(-10000.0f).max(10000.0f).default_value(1.0f); + b.add_input<decl::Float>(N_("From Min")).min(-10000.0f).max(10000.0f); + b.add_input<decl::Float>(N_("From Max")).min(-10000.0f).max(10000.0f).default_value(1.0f); + b.add_input<decl::Float>(N_("To Min")).min(-10000.0f).max(10000.0f); + b.add_input<decl::Float>(N_("To Max")).min(-10000.0f).max(10000.0f).default_value(1.0f); + b.add_input<decl::Float>(N_("Steps")).min(-10000.0f).max(10000.0f).default_value(4.0f); + b.add_input<decl::Vector>(N_("Vector")).min(0.0f).max(1.0f).hide_value(); + b.add_input<decl::Vector>(N_("From Min"), "From_Min_FLOAT3"); + b.add_input<decl::Vector>(N_("From Max"), "From_Max_FLOAT3").default_value(float3(1.0f)); + b.add_input<decl::Vector>(N_("To Min"), "To_Min_FLOAT3"); + b.add_input<decl::Vector>(N_("To Max"), "To_Max_FLOAT3").default_value(float3(1.0f)); + b.add_input<decl::Vector>(N_("Steps"), "Steps_FLOAT3").default_value(float3(4.0f)); + b.add_output<decl::Float>(N_("Result")); + b.add_output<decl::Vector>(N_("Vector")); +} -} // namespace blender::nodes +static void node_shader_buts_map_range(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "data_type", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); + uiItemR(layout, ptr, "interpolation_type", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); + if (!ELEM(RNA_enum_get(ptr, "interpolation_type"), + NODE_MAP_RANGE_SMOOTHSTEP, + NODE_MAP_RANGE_SMOOTHERSTEP)) { + uiItemR(layout, ptr, "clamp", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + } +} -static void node_shader_update_map_range(bNodeTree *UNUSED(ntree), bNode *node) +static void node_shader_update_map_range(bNodeTree *ntree, bNode *node) { - bNodeSocket *sockSteps = nodeFindSocket(node, SOCK_IN, "Steps"); - nodeSetSocketAvailability(sockSteps, node->custom2 == NODE_MAP_RANGE_STEPPED); + const NodeMapRange &storage = node_storage(*node); + const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type); + const int type = (data_type == CD_PROP_FLOAT) ? SOCK_FLOAT : SOCK_VECTOR; + + LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) { + nodeSetSocketAvailability(ntree, socket, socket->type == type); + } + + LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) { + nodeSetSocketAvailability(ntree, socket, socket->type == type); + } + + if (storage.interpolation_type != NODE_MAP_RANGE_STEPPED) { + if (type == SOCK_FLOAT) { + bNodeSocket *sockSteps = (bNodeSocket *)BLI_findlink(&node->inputs, 5); + nodeSetSocketAvailability(ntree, sockSteps, false); + } + else { + bNodeSocket *sockSteps = (bNodeSocket *)BLI_findlink(&node->inputs, 11); + nodeSetSocketAvailability(ntree, sockSteps, false); + } + } } static void node_shader_init_map_range(bNodeTree *UNUSED(ntree), bNode *node) { + NodeMapRange *data = MEM_cnew<NodeMapRange>(__func__); + data->clamp = 1; + data->data_type = CD_PROP_FLOAT; + data->interpolation_type = NODE_MAP_RANGE_LINEAR; node->custom1 = true; /* use_clamp */ node->custom2 = NODE_MAP_RANGE_LINEAR; /* interpolation */ + node->storage = data; } -static const char *gpu_shader_get_name(int mode) +class SocketSearchOp { + public: + std::string socket_name; + CustomDataType data_type; + int interpolation_type = NODE_MAP_RANGE_LINEAR; + + void operator()(LinkSearchOpParams ¶ms) + { + bNode &node = params.add_node("ShaderNodeMapRange"); + node_storage(node).data_type = data_type; + node_storage(node).interpolation_type = interpolation_type; + params.update_and_connect_available_socket(node, socket_name); + } +}; + +static std::optional<CustomDataType> node_type_from_other_socket(const bNodeSocket &socket) +{ + switch (socket.type) { + case SOCK_FLOAT: + case SOCK_BOOLEAN: + case SOCK_INT: + return CD_PROP_FLOAT; + case SOCK_VECTOR: + case SOCK_RGBA: + return CD_PROP_FLOAT3; + default: + return {}; + } +} + +static void node_map_range_gather_link_searches(GatherLinkSearchOpParams ¶ms) { - switch (mode) { - case NODE_MAP_RANGE_LINEAR: - return "map_range_linear"; - case NODE_MAP_RANGE_STEPPED: - return "map_range_stepped"; - case NODE_MAP_RANGE_SMOOTHSTEP: - return "map_range_smoothstep"; - case NODE_MAP_RANGE_SMOOTHERSTEP: - return "map_range_smootherstep"; + const std::optional<CustomDataType> type = node_type_from_other_socket(params.other_socket()); + if (!type) { + return; + } + + if (params.in_out() == SOCK_IN) { + if (*type == CD_PROP_FLOAT3) { + params.add_item(IFACE_("Vector"), SocketSearchOp{"Vector", *type}, 0); + } + else { + params.add_item(IFACE_("Value"), SocketSearchOp{"Value", *type}, 0); + } + params.add_item(IFACE_("From Min"), SocketSearchOp{"From Min", *type}, -1); + params.add_item(IFACE_("From Max"), SocketSearchOp{"From Max", *type}, -1); + params.add_item(IFACE_("To Min"), SocketSearchOp{"To Min", *type}, -2); + params.add_item(IFACE_("To Max"), SocketSearchOp{"To Max", *type}, -2); + params.add_item(IFACE_("Steps"), SocketSearchOp{"Steps", *type, NODE_MAP_RANGE_STEPPED}, -3); + } + else { + if (*type == CD_PROP_FLOAT3) { + params.add_item(IFACE_("Vector"), SocketSearchOp{"Vector", *type}); + } + else { + params.add_item(IFACE_("Result"), SocketSearchOp{"Result", *type}); + } + } +} + +static const char *gpu_shader_get_name(int mode, bool use_vector) +{ + if (use_vector) { + switch (mode) { + case NODE_MAP_RANGE_LINEAR: + return "vector_map_range_linear"; + case NODE_MAP_RANGE_STEPPED: + return "vector_map_range_stepped"; + case NODE_MAP_RANGE_SMOOTHSTEP: + return "vector_map_range_smoothstep"; + case NODE_MAP_RANGE_SMOOTHERSTEP: + return "vector_map_range_smootherstep"; + } + } + else { + switch (mode) { + case NODE_MAP_RANGE_LINEAR: + return "map_range_linear"; + case NODE_MAP_RANGE_STEPPED: + return "map_range_stepped"; + case NODE_MAP_RANGE_SMOOTHSTEP: + return "map_range_smoothstep"; + case NODE_MAP_RANGE_SMOOTHERSTEP: + return "map_range_smootherstep"; + } } return nullptr; @@ -75,22 +199,205 @@ static int gpu_shader_map_range(GPUMaterial *mat, GPUNodeStack *in, GPUNodeStack *out) { - const char *name = gpu_shader_get_name(node->custom2); - + const NodeMapRange &storage = node_storage(*node); + bool use_vector = (storage.data_type == CD_PROP_FLOAT3); + const char *name = gpu_shader_get_name(storage.interpolation_type, use_vector); + float clamp = storage.clamp ? 1.0f : 0.0f; int ret = 0; if (name != nullptr) { - ret = GPU_stack_link(mat, node, name, in, out); + ret = GPU_stack_link(mat, node, name, in, out, GPU_constant(&clamp)); } else { - ret = GPU_stack_link(mat, node, "map_range_linear", in, out); + ret = GPU_stack_link(mat, node, "map_range_linear", in, out, GPU_constant(&clamp)); } - if (ret && node->custom1 && - !ELEM(node->custom2, NODE_MAP_RANGE_SMOOTHSTEP, NODE_MAP_RANGE_SMOOTHERSTEP)) { + if (ret && storage.clamp && !use_vector && + !ELEM(storage.interpolation_type, NODE_MAP_RANGE_SMOOTHSTEP, NODE_MAP_RANGE_SMOOTHERSTEP)) { GPU_link(mat, "clamp_range", out[0].link, in[3].link, in[4].link, &out[0].link); } return ret; } +static inline float clamp_range(const float value, const float min, const float max) +{ + return (min > max) ? std::clamp(value, max, min) : std::clamp(value, min, max); +} + +static float3 clamp_range(const float3 value, const float3 min, const float3 max) +{ + return float3(clamp_range(value.x, min.x, max.x), + clamp_range(value.y, min.y, max.y), + clamp_range(value.z, min.z, max.z)); +} + +static void map_range_vector_signature(blender::fn::MFSignatureBuilder *signature, bool use_steps) +{ + signature->single_input<float3>("Vector"); + signature->single_input<float3>("From Min"); + signature->single_input<float3>("From Max"); + signature->single_input<float3>("To Min"); + signature->single_input<float3>("To Max"); + if (use_steps) { + signature->single_input<float3>("Steps"); + } + signature->single_output<float3>("Vector"); +} + +class MapRangeVectorFunction : public blender::fn::MultiFunction { + private: + bool clamp_; + + public: + MapRangeVectorFunction(bool clamp) : clamp_(clamp) + { + static blender::fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static blender::fn::MFSignature create_signature() + { + blender::fn::MFSignatureBuilder signature{"Vector Map Range"}; + map_range_vector_signature(&signature, false); + return signature.build(); + } + + void call(blender::IndexMask mask, + blender::fn::MFParams params, + blender::fn::MFContext UNUSED(context)) const override + { + const blender::VArray<float3> &values = params.readonly_single_input<float3>(0, "Vector"); + const blender::VArray<float3> &from_min = params.readonly_single_input<float3>(1, "From Min"); + const blender::VArray<float3> &from_max = params.readonly_single_input<float3>(2, "From Max"); + const blender::VArray<float3> &to_min = params.readonly_single_input<float3>(3, "To Min"); + const blender::VArray<float3> &to_max = params.readonly_single_input<float3>(4, "To Max"); + blender::MutableSpan<float3> results = params.uninitialized_single_output<float3>(5, "Vector"); + + for (int64_t i : mask) { + float3 factor = math::safe_divide(values[i] - from_min[i], from_max[i] - from_min[i]); + results[i] = factor * (to_max[i] - to_min[i]) + to_min[i]; + } + + if (clamp_) { + for (int64_t i : mask) { + results[i] = clamp_range(results[i], to_min[i], to_max[i]); + } + } + } +}; + +class MapRangeSteppedVectorFunction : public blender::fn::MultiFunction { + private: + bool clamp_; + + public: + MapRangeSteppedVectorFunction(bool clamp) : clamp_(clamp) + { + static blender::fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static blender::fn::MFSignature create_signature() + { + blender::fn::MFSignatureBuilder signature{"Vector Map Range Stepped"}; + map_range_vector_signature(&signature, true); + return signature.build(); + } + + void call(blender::IndexMask mask, + blender::fn::MFParams params, + blender::fn::MFContext UNUSED(context)) const override + { + const blender::VArray<float3> &values = params.readonly_single_input<float3>(0, "Vector"); + const blender::VArray<float3> &from_min = params.readonly_single_input<float3>(1, "From Min"); + const blender::VArray<float3> &from_max = params.readonly_single_input<float3>(2, "From Max"); + const blender::VArray<float3> &to_min = params.readonly_single_input<float3>(3, "To Min"); + const blender::VArray<float3> &to_max = params.readonly_single_input<float3>(4, "To Max"); + const blender::VArray<float3> &steps = params.readonly_single_input<float3>(5, "Steps"); + blender::MutableSpan<float3> results = params.uninitialized_single_output<float3>(6, "Vector"); + + for (int64_t i : mask) { + float3 factor = math::safe_divide(values[i] - from_min[i], from_max[i] - from_min[i]); + factor = math::safe_divide(math::floor(factor * (steps[i] + 1.0f)), steps[i]); + results[i] = factor * (to_max[i] - to_min[i]) + to_min[i]; + } + + if (clamp_) { + for (int64_t i : mask) { + results[i] = clamp_range(results[i], to_min[i], to_max[i]); + } + } + } +}; + +class MapRangeSmoothstepVectorFunction : public blender::fn::MultiFunction { + public: + MapRangeSmoothstepVectorFunction() + { + static blender::fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static blender::fn::MFSignature create_signature() + { + blender::fn::MFSignatureBuilder signature{"Vector Map Range Smoothstep"}; + map_range_vector_signature(&signature, false); + return signature.build(); + } + + void call(blender::IndexMask mask, + blender::fn::MFParams params, + blender::fn::MFContext UNUSED(context)) const override + { + const blender::VArray<float3> &values = params.readonly_single_input<float3>(0, "Vector"); + const blender::VArray<float3> &from_min = params.readonly_single_input<float3>(1, "From Min"); + const blender::VArray<float3> &from_max = params.readonly_single_input<float3>(2, "From Max"); + const blender::VArray<float3> &to_min = params.readonly_single_input<float3>(3, "To Min"); + const blender::VArray<float3> &to_max = params.readonly_single_input<float3>(4, "To Max"); + blender::MutableSpan<float3> results = params.uninitialized_single_output<float3>(5, "Vector"); + + for (int64_t i : mask) { + float3 factor = math::safe_divide(values[i] - from_min[i], from_max[i] - from_min[i]); + clamp_v3(factor, 0.0f, 1.0f); + factor = (float3(3.0f) - 2.0f * factor) * (factor * factor); + results[i] = factor * (to_max[i] - to_min[i]) + to_min[i]; + } + } +}; + +class MapRangeSmootherstepVectorFunction : public blender::fn::MultiFunction { + public: + MapRangeSmootherstepVectorFunction() + { + static blender::fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static blender::fn::MFSignature create_signature() + { + blender::fn::MFSignatureBuilder signature{"Vector Map Range Smoothstep"}; + map_range_vector_signature(&signature, false); + return signature.build(); + } + + void call(blender::IndexMask mask, + blender::fn::MFParams params, + blender::fn::MFContext UNUSED(context)) const override + { + const blender::VArray<float3> &values = params.readonly_single_input<float3>(0, "Vector"); + const blender::VArray<float3> &from_min = params.readonly_single_input<float3>(1, "From Min"); + const blender::VArray<float3> &from_max = params.readonly_single_input<float3>(2, "From Max"); + const blender::VArray<float3> &to_min = params.readonly_single_input<float3>(3, "To Min"); + const blender::VArray<float3> &to_max = params.readonly_single_input<float3>(4, "To Max"); + blender::MutableSpan<float3> results = params.uninitialized_single_output<float3>(5, "Vector"); + + for (int64_t i : mask) { + float3 factor = math::safe_divide(values[i] - from_min[i], from_max[i] - from_min[i]); + clamp_v3(factor, 0.0f, 1.0f); + factor = factor * factor * factor * (factor * (factor * 6.0f - 15.0f) + 10.0f); + results[i] = factor * (to_max[i] - to_min[i]) + to_min[i]; + } + } +}; + static void map_range_signature(blender::fn::MFSignatureBuilder *signature, bool use_steps) { signature->single_input<float>("Value"); @@ -140,8 +447,7 @@ class MapRangeFunction : public blender::fn::MultiFunction { if (clamp_) { for (int64_t i : mask) { - results[i] = (to_min[i] > to_max[i]) ? clamp_f(results[i], to_max[i], to_min[i]) : - clamp_f(results[i], to_min[i], to_max[i]); + results[i] = clamp_range(results[i], to_min[i], to_max[i]); } } } @@ -185,8 +491,7 @@ class MapRangeSteppedFunction : public blender::fn::MultiFunction { if (clamp_) { for (int64_t i : mask) { - results[i] = (to_min[i] > to_max[i]) ? clamp_f(results[i], to_max[i], to_min[i]) : - clamp_f(results[i], to_min[i], to_max[i]); + results[i] = clamp_range(results[i], to_min[i], to_max[i]); } } } @@ -265,58 +570,107 @@ class MapRangeSmootherstepFunction : public blender::fn::MultiFunction { static void sh_node_map_range_build_multi_function( blender::nodes::NodeMultiFunctionBuilder &builder) { - bNode &bnode = builder.node(); - bool clamp = bnode.custom1 != 0; - int interpolation_type = bnode.custom2; - - switch (interpolation_type) { - case NODE_MAP_RANGE_LINEAR: { - if (clamp) { - static MapRangeFunction fn_with_clamp{true}; - builder.set_matching_fn(fn_with_clamp); - } - else { - static MapRangeFunction fn_without_clamp{false}; - builder.set_matching_fn(fn_without_clamp); + const NodeMapRange &storage = node_storage(builder.node()); + bool clamp = storage.clamp != 0; + int interpolation_type = storage.interpolation_type; + + switch (storage.data_type) { + case CD_PROP_FLOAT3: + switch (interpolation_type) { + case NODE_MAP_RANGE_LINEAR: { + if (clamp) { + static MapRangeVectorFunction fn_with_clamp{true}; + builder.set_matching_fn(fn_with_clamp); + } + else { + static MapRangeVectorFunction fn_without_clamp{false}; + builder.set_matching_fn(fn_without_clamp); + } + break; + } + case NODE_MAP_RANGE_STEPPED: { + if (clamp) { + static MapRangeSteppedVectorFunction fn_stepped_with_clamp{true}; + builder.set_matching_fn(fn_stepped_with_clamp); + } + else { + static MapRangeSteppedVectorFunction fn_stepped_without_clamp{false}; + builder.set_matching_fn(fn_stepped_without_clamp); + } + break; + } + case NODE_MAP_RANGE_SMOOTHSTEP: { + static MapRangeSmoothstepVectorFunction smoothstep; + builder.set_matching_fn(smoothstep); + break; + } + case NODE_MAP_RANGE_SMOOTHERSTEP: { + static MapRangeSmootherstepVectorFunction smootherstep; + builder.set_matching_fn(smootherstep); + break; + } + default: + break; } break; - } - case NODE_MAP_RANGE_STEPPED: { - if (clamp) { - static MapRangeSteppedFunction fn_stepped_with_clamp{true}; - builder.set_matching_fn(fn_stepped_with_clamp); - } - else { - static MapRangeSteppedFunction fn_stepped_without_clamp{false}; - builder.set_matching_fn(fn_stepped_without_clamp); + case CD_PROP_FLOAT: + switch (interpolation_type) { + case NODE_MAP_RANGE_LINEAR: { + if (clamp) { + static MapRangeFunction fn_with_clamp{true}; + builder.set_matching_fn(fn_with_clamp); + } + else { + static MapRangeFunction fn_without_clamp{false}; + builder.set_matching_fn(fn_without_clamp); + } + break; + } + case NODE_MAP_RANGE_STEPPED: { + if (clamp) { + static MapRangeSteppedFunction fn_stepped_with_clamp{true}; + builder.set_matching_fn(fn_stepped_with_clamp); + } + else { + static MapRangeSteppedFunction fn_stepped_without_clamp{false}; + builder.set_matching_fn(fn_stepped_without_clamp); + } + break; + } + case NODE_MAP_RANGE_SMOOTHSTEP: { + static MapRangeSmoothstepFunction smoothstep; + builder.set_matching_fn(smoothstep); + break; + } + case NODE_MAP_RANGE_SMOOTHERSTEP: { + static MapRangeSmootherstepFunction smootherstep; + builder.set_matching_fn(smootherstep); + break; + } + default: + break; } break; - } - case NODE_MAP_RANGE_SMOOTHSTEP: { - static MapRangeSmoothstepFunction smoothstep; - builder.set_matching_fn(smoothstep); - break; - } - case NODE_MAP_RANGE_SMOOTHERSTEP: { - static MapRangeSmootherstepFunction smootherstep; - builder.set_matching_fn(smootherstep); - break; - } - default: - break; } } -void register_node_type_sh_map_range(void) +} // namespace blender::nodes::node_shader_map_range_cc + +void register_node_type_sh_map_range() { - static bNodeType ntype; + namespace file_ns = blender::nodes::node_shader_map_range_cc; - sh_fn_node_type_base(&ntype, SH_NODE_MAP_RANGE, "Map Range", NODE_CLASS_CONVERTER, 0); - ntype.declare = blender::nodes::sh_node_map_range_declare; - node_type_init(&ntype, node_shader_init_map_range); - node_type_update(&ntype, node_shader_update_map_range); - node_type_gpu(&ntype, gpu_shader_map_range); - ntype.build_multi_function = sh_node_map_range_build_multi_function; + static bNodeType ntype; + sh_fn_node_type_base(&ntype, SH_NODE_MAP_RANGE, "Map Range", NODE_CLASS_CONVERTER); + ntype.declare = file_ns::sh_node_map_range_declare; + ntype.draw_buttons = file_ns::node_shader_buts_map_range; + node_type_init(&ntype, file_ns::node_shader_init_map_range); + node_type_storage( + &ntype, "NodeMapRange", node_free_standard_storage, node_copy_standard_storage); + node_type_update(&ntype, file_ns::node_shader_update_map_range); + node_type_gpu(&ntype, file_ns::gpu_shader_map_range); + ntype.build_multi_function = file_ns::sh_node_map_range_build_multi_function; + ntype.gather_link_search_ops = file_ns::node_map_range_gather_link_searches; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_mapping.c b/source/blender/nodes/shader/nodes/node_shader_mapping.c deleted file mode 100644 index 774e7fed029..00000000000 --- a/source/blender/nodes/shader/nodes/node_shader_mapping.c +++ /dev/null @@ -1,77 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2005 Blender Foundation. - * All rights reserved. - */ - -/** \file - * \ingroup shdnodes - */ - -#include "node_shader_util.h" - -/* **************** MAPPING ******************** */ -static bNodeSocketTemplate sh_node_mapping_in[] = { - {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_NONE}, - {SOCK_VECTOR, N_("Location"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION}, - {SOCK_VECTOR, N_("Rotation"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_EULER}, - {SOCK_VECTOR, N_("Scale"), 1.0f, 1.0f, 1.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_XYZ}, - {-1, ""}, -}; - -static bNodeSocketTemplate sh_node_mapping_out[] = { - {SOCK_VECTOR, N_("Vector")}, - {-1, ""}, -}; - -static int gpu_shader_mapping(GPUMaterial *mat, - bNode *node, - bNodeExecData *UNUSED(execdata), - GPUNodeStack *in, - GPUNodeStack *out) -{ - static const char *names[] = { - [NODE_MAPPING_TYPE_POINT] = "mapping_point", - [NODE_MAPPING_TYPE_TEXTURE] = "mapping_texture", - [NODE_MAPPING_TYPE_VECTOR] = "mapping_vector", - [NODE_MAPPING_TYPE_NORMAL] = "mapping_normal", - }; - - if (node->custom1 < ARRAY_SIZE(names) && names[node->custom1]) { - return GPU_stack_link(mat, node, names[node->custom1], in, out); - } - - return 0; -} - -static void node_shader_update_mapping(bNodeTree *UNUSED(ntree), bNode *node) -{ - bNodeSocket *sock = nodeFindSocket(node, SOCK_IN, "Location"); - nodeSetSocketAvailability( - sock, ELEM(node->custom1, NODE_MAPPING_TYPE_POINT, NODE_MAPPING_TYPE_TEXTURE)); -} - -void register_node_type_sh_mapping(void) -{ - static bNodeType ntype; - - sh_node_type_base(&ntype, SH_NODE_MAPPING, "Mapping", NODE_CLASS_OP_VECTOR, 0); - node_type_socket_templates(&ntype, sh_node_mapping_in, sh_node_mapping_out); - node_type_gpu(&ntype, gpu_shader_mapping); - node_type_update(&ntype, node_shader_update_mapping); - - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/shader/nodes/node_shader_mapping.cc b/source/blender/nodes/shader/nodes/node_shader_mapping.cc new file mode 100644 index 00000000000..19c3a26796e --- /dev/null +++ b/source/blender/nodes/shader/nodes/node_shader_mapping.cc @@ -0,0 +1,110 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2005 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup shdnodes + */ + +#include "node_shader_util.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes::node_shader_mapping_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Vector>(N_("Vector")) + .default_value({0.0f, 0.0f, 0.0f}) + .min(-FLT_MAX) + .max(FLT_MAX); + b.add_input<decl::Vector>(N_("Location")) + .default_value({0.0f, 0.0f, 0.0f}) + .min(-FLT_MAX) + .max(FLT_MAX) + .subtype(PROP_TRANSLATION); + b.add_input<decl::Vector>(N_("Rotation")) + .default_value({0.0f, 0.0f, 0.0f}) + .min(-FLT_MAX) + .max(FLT_MAX) + .subtype(PROP_EULER); + b.add_input<decl::Vector>(N_("Scale")) + .default_value({1.0f, 1.0f, 1.0f}) + .min(-FLT_MAX) + .max(FLT_MAX) + .subtype(PROP_XYZ); + b.add_output<decl::Vector>(N_("Vector")); +} + +static void node_shader_buts_mapping(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "vector_type", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); +} + +static const char *gpu_shader_get_name(int mode) +{ + switch (mode) { + case NODE_MAPPING_TYPE_POINT: + return "mapping_point"; + case NODE_MAPPING_TYPE_TEXTURE: + return "mapping_texture"; + case NODE_MAPPING_TYPE_VECTOR: + return "mapping_vector"; + case NODE_MAPPING_TYPE_NORMAL: + return "mapping_normal"; + } + return nullptr; +} + +static int gpu_shader_mapping(GPUMaterial *mat, + bNode *node, + bNodeExecData *UNUSED(execdata), + GPUNodeStack *in, + GPUNodeStack *out) +{ + if (gpu_shader_get_name(node->custom1)) { + return GPU_stack_link(mat, node, gpu_shader_get_name(node->custom1), in, out); + } + + return 0; +} + +static void node_shader_update_mapping(bNodeTree *ntree, bNode *node) +{ + bNodeSocket *sock = nodeFindSocket(node, SOCK_IN, "Location"); + nodeSetSocketAvailability( + ntree, sock, ELEM(node->custom1, NODE_MAPPING_TYPE_POINT, NODE_MAPPING_TYPE_TEXTURE)); +} + +} // namespace blender::nodes::node_shader_mapping_cc + +void register_node_type_sh_mapping() +{ + namespace file_ns = blender::nodes::node_shader_mapping_cc; + + static bNodeType ntype; + + sh_node_type_base(&ntype, SH_NODE_MAPPING, "Mapping", NODE_CLASS_OP_VECTOR); + ntype.declare = file_ns::node_declare; + ntype.draw_buttons = file_ns::node_shader_buts_mapping; + node_type_gpu(&ntype, file_ns::gpu_shader_mapping); + node_type_update(&ntype, file_ns::node_shader_update_mapping); + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/shader/nodes/node_shader_math.cc b/source/blender/nodes/shader/nodes/node_shader_math.cc index 96d1be49c04..50585405cbf 100644 --- a/source/blender/nodes/shader/nodes/node_shader_math.cc +++ b/source/blender/nodes/shader/nodes/node_shader_math.cc @@ -21,24 +21,67 @@ * \ingroup shdnodes */ -#include "node_shader_util.h" +#include "node_shader_util.hh" #include "NOD_math_functions.hh" +#include "NOD_socket_search_link.hh" + +#include "RNA_enum_types.h" /* **************** SCALAR MATH ******************** */ -namespace blender::nodes { +namespace blender::nodes::node_shader_math_cc { static void sh_node_math_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input<decl::Float>("Value").default_value(0.5f).min(-10000.0f).max(10000.0f); - b.add_input<decl::Float>("Value", "Value_001").default_value(0.5f).min(-10000.0f).max(10000.0f); - b.add_input<decl::Float>("Value", "Value_002").default_value(0.5f).min(-10000.0f).max(10000.0f); - b.add_output<decl::Float>("Value"); + b.add_input<decl::Float>(N_("Value")).default_value(0.5f).min(-10000.0f).max(10000.0f); + b.add_input<decl::Float>(N_("Value"), "Value_001") + .default_value(0.5f) + .min(-10000.0f) + .max(10000.0f); + b.add_input<decl::Float>(N_("Value"), "Value_002") + .default_value(0.5f) + .min(-10000.0f) + .max(10000.0f); + b.add_output<decl::Float>(N_("Value")); +} + +class SocketSearchOp { + public: + std::string socket_name; + NodeMathOperation mode = NODE_MATH_ADD; + void operator()(LinkSearchOpParams ¶ms) + { + bNode &node = params.add_node("ShaderNodeMath"); + node.custom1 = mode; + params.update_and_connect_available_socket(node, socket_name); + } }; -} // namespace blender::nodes +static void sh_node_math_gather_link_searches(GatherLinkSearchOpParams ¶ms) +{ + if (!params.node_tree().typeinfo->validate_link( + static_cast<eNodeSocketDatatype>(params.other_socket().type), SOCK_FLOAT)) { + return; + } + + const bool is_geometry_node_tree = params.node_tree().type == NTREE_GEOMETRY; + const int weight = ELEM(params.other_socket().type, SOCK_FLOAT, SOCK_BOOLEAN, SOCK_INT) ? 0 : -1; + + for (const EnumPropertyItem *item = rna_enum_node_math_items; item->identifier != nullptr; + item++) { + if (item->name != nullptr && item->identifier[0] != '\0') { + const int gn_weight = + (is_geometry_node_tree && + 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); + } + } +} static const char *gpu_shader_get_name(int mode) { @@ -82,7 +125,8 @@ static const blender::fn::MultiFunction *get_base_multi_function(bNode &node) blender::nodes::try_dispatch_float_math_fl_to_fl( mode, [&](auto function, const blender::nodes::FloatMathOperationInfo &info) { - static blender::fn::CustomMF_SI_SO<float, float> fn{info.title_case_name, function}; + static blender::fn::CustomMF_SI_SO<float, float> fn{info.title_case_name.c_str(), + function}; base_fn = &fn; }); if (base_fn != nullptr) { @@ -91,7 +135,7 @@ static const blender::fn::MultiFunction *get_base_multi_function(bNode &node) blender::nodes::try_dispatch_float_math_fl_fl_to_fl( mode, [&](auto function, const blender::nodes::FloatMathOperationInfo &info) { - static blender::fn::CustomMF_SI_SI_SO<float, float, float> fn{info.title_case_name, + static blender::fn::CustomMF_SI_SI_SO<float, float, float> fn{info.title_case_name.c_str(), function}; base_fn = &fn; }); @@ -102,7 +146,7 @@ static const blender::fn::MultiFunction *get_base_multi_function(bNode &node) blender::nodes::try_dispatch_float_math_fl_fl_fl_to_fl( mode, [&](auto function, const blender::nodes::FloatMathOperationInfo &info) { static blender::fn::CustomMF_SI_SI_SI_SO<float, float, float, float> fn{ - info.title_case_name, function}; + info.title_case_name.c_str(), function}; base_fn = &fn; }); if (base_fn != nullptr) { @@ -154,16 +198,21 @@ static void sh_node_math_build_multi_function(blender::nodes::NodeMultiFunctionB } } -void register_node_type_sh_math(void) +} // namespace blender::nodes::node_shader_math_cc + +void register_node_type_sh_math() { + namespace file_ns = blender::nodes::node_shader_math_cc; + static bNodeType ntype; - sh_fn_node_type_base(&ntype, SH_NODE_MATH, "Math", NODE_CLASS_CONVERTER, 0); - ntype.declare = blender::nodes::sh_node_math_declare; - node_type_label(&ntype, node_math_label); - node_type_gpu(&ntype, gpu_shader_math); + sh_fn_node_type_base(&ntype, SH_NODE_MATH, "Math", NODE_CLASS_CONVERTER); + ntype.declare = file_ns::sh_node_math_declare; + ntype.labelfunc = node_math_label; + node_type_gpu(&ntype, file_ns::gpu_shader_math); node_type_update(&ntype, node_math_update); - ntype.build_multi_function = sh_node_math_build_multi_function; + ntype.build_multi_function = file_ns::sh_node_math_build_multi_function; + ntype.gather_link_search_ops = file_ns::sh_node_math_gather_link_searches; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc b/source/blender/nodes/shader/nodes/node_shader_mix_rgb.cc index d4d02e80ada..9678e86d289 100644 --- a/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc +++ b/source/blender/nodes/shader/nodes/node_shader_mix_rgb.cc @@ -21,45 +21,17 @@ * \ingroup shdnodes */ -#include "node_shader_util.h" +#include "node_shader_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_shader_mix_rgb_cc { static void sh_node_mix_rgb_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input<decl::Float>("Fac").default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_input<decl::Color>("Color1").default_value({0.5f, 0.5f, 0.5f, 1.0f}); - b.add_input<decl::Color>("Color2").default_value({0.5f, 0.5f, 0.5f, 1.0f}); - b.add_output<decl::Color>("Color"); -}; - -} // namespace blender::nodes - -static void node_shader_exec_mix_rgb(void *UNUSED(data), - int UNUSED(thread), - bNode *node, - bNodeExecData *UNUSED(execdata), - bNodeStack **in, - bNodeStack **out) -{ - /* stack order in: fac, col1, col2 */ - /* stack order out: col */ - float col[3]; - float fac; - float vec[3]; - - nodestack_get_vec(&fac, SOCK_FLOAT, in[0]); - CLAMP(fac, 0.0f, 1.0f); - - nodestack_get_vec(col, SOCK_VECTOR, in[1]); - nodestack_get_vec(vec, SOCK_VECTOR, in[2]); - - ramp_blend(node->custom1, col, fac, vec); - if (node->custom2 & SHD_MIXRGB_CLAMP) { - CLAMP3(col, 0.0f, 1.0f); - } - copy_v3_v3(out[0]->vec, col); + b.add_input<decl::Float>(N_("Fac")).default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_input<decl::Color>(N_("Color1")).default_value({0.5f, 0.5f, 0.5f, 1.0f}); + b.add_input<decl::Color>(N_("Color2")).default_value({0.5f, 0.5f, 0.5f, 1.0f}); + b.add_output<decl::Color>(N_("Color")); } static const char *gpu_shader_get_name(int mode) @@ -183,16 +155,19 @@ static void sh_node_mix_rgb_build_multi_function(blender::nodes::NodeMultiFuncti builder.construct_and_set_matching_fn<MixRGBFunction>(clamp, mix_type); } -void register_node_type_sh_mix_rgb(void) +} // namespace blender::nodes::node_shader_mix_rgb_cc + +void register_node_type_sh_mix_rgb() { + namespace file_ns = blender::nodes::node_shader_mix_rgb_cc; + static bNodeType ntype; - sh_fn_node_type_base(&ntype, SH_NODE_MIX_RGB, "Mix", NODE_CLASS_OP_COLOR, 0); - ntype.declare = blender::nodes::sh_node_mix_rgb_declare; - node_type_label(&ntype, node_blend_label); - node_type_exec(&ntype, nullptr, nullptr, node_shader_exec_mix_rgb); - node_type_gpu(&ntype, gpu_shader_mix_rgb); - ntype.build_multi_function = sh_node_mix_rgb_build_multi_function; + sh_fn_node_type_base(&ntype, SH_NODE_MIX_RGB, "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; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_mix_shader.c b/source/blender/nodes/shader/nodes/node_shader_mix_shader.cc index 33cbf34543c..d9aa906e451 100644 --- a/source/blender/nodes/shader/nodes/node_shader_mix_shader.c +++ b/source/blender/nodes/shader/nodes/node_shader_mix_shader.cc @@ -17,21 +17,17 @@ * All rights reserved. */ -#include "../node_shader_util.h" +#include "node_shader_util.hh" -/* **************** OUTPUT ******************** */ +namespace blender::nodes::node_shader_mix_shader_cc { -static bNodeSocketTemplate sh_node_mix_shader_in[] = { - {SOCK_FLOAT, N_("Fac"), 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_SHADER, N_("Shader")}, - {SOCK_SHADER, N_("Shader")}, - {-1, ""}, -}; - -static bNodeSocketTemplate sh_node_mix_shader_out[] = { - {SOCK_SHADER, N_("Shader")}, - {-1, ""}, -}; +static void node_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_input<decl::Shader>(N_("Shader")); + b.add_input<decl::Shader>(N_("Shader"), "Shader_001"); + b.add_output<decl::Shader>(N_("Shader")); +} static int node_shader_gpu_mix_shader(GPUMaterial *mat, bNode *node, @@ -42,16 +38,18 @@ static int node_shader_gpu_mix_shader(GPUMaterial *mat, return GPU_stack_link(mat, node, "node_mix_shader", in, out); } +} // namespace blender::nodes::node_shader_mix_shader_cc + /* node type definition */ -void register_node_type_sh_mix_shader(void) +void register_node_type_sh_mix_shader() { + namespace file_ns = blender::nodes::node_shader_mix_shader_cc; + static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_MIX_SHADER, "Mix Shader", NODE_CLASS_SHADER, 0); - node_type_socket_templates(&ntype, sh_node_mix_shader_in, sh_node_mix_shader_out); - node_type_init(&ntype, NULL); - node_type_storage(&ntype, "", NULL, NULL); - node_type_gpu(&ntype, node_shader_gpu_mix_shader); + sh_node_type_base(&ntype, SH_NODE_MIX_SHADER, "Mix Shader", NODE_CLASS_SHADER); + ntype.declare = file_ns::node_declare; + node_type_gpu(&ntype, file_ns::node_shader_gpu_mix_shader); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_normal.c b/source/blender/nodes/shader/nodes/node_shader_normal.cc index 83d5abcba67..e677f36e425 100644 --- a/source/blender/nodes/shader/nodes/node_shader_normal.c +++ b/source/blender/nodes/shader/nodes/node_shader_normal.cc @@ -21,37 +21,23 @@ * \ingroup shdnodes */ -#include "node_shader_util.h" +#include "node_shader_util.hh" -/* **************** NORMAL ******************** */ -static bNodeSocketTemplate sh_node_normal_in[] = { - {SOCK_VECTOR, N_("Normal"), 0.0f, 0.0f, 1.0f, 0.0f, -1.0f, 1.0f, PROP_DIRECTION}, - {-1, ""}, -}; +namespace blender::nodes::node_shader_normal_cc { -static bNodeSocketTemplate sh_node_normal_out[] = { - {SOCK_VECTOR, N_("Normal"), 0.0f, 0.0f, 1.0f, 0.0f, -1.0f, 1.0f, PROP_DIRECTION}, - {SOCK_FLOAT, N_("Dot")}, - {-1, ""}, -}; - -/* generates normal, does dot product */ -static void node_shader_exec_normal(void *UNUSED(data), - int UNUSED(thread), - bNode *UNUSED(node), - bNodeExecData *UNUSED(execdata), - bNodeStack **in, - bNodeStack **out) +static void node_declare(NodeDeclarationBuilder &b) { - float vec[3]; - - /* stack order input: normal */ - /* stack order output: normal, value */ - - nodestack_get_vec(vec, SOCK_VECTOR, in[0]); - - /* render normals point inside... the widget points outside */ - out[1]->vec[0] = -dot_v3v3(vec, out[0]->vec); + b.add_input<decl::Vector>(N_("Normal")) + .default_value({0.0f, 0.0f, 1.0f}) + .min(-1.0f) + .max(1.0f) + .subtype(PROP_DIRECTION); + b.add_output<decl::Vector>(N_("Normal")) + .default_value({0.0f, 0.0f, 1.0f}) + .min(-1.0f) + .max(1.0f) + .subtype(PROP_DIRECTION); + b.add_output<decl::Float>(N_("Dot")); } static int gpu_shader_normal(GPUMaterial *mat, @@ -64,14 +50,17 @@ static int gpu_shader_normal(GPUMaterial *mat, return GPU_stack_link(mat, node, "normal_new_shading", in, out, vec); } -void register_node_type_sh_normal(void) +} // namespace blender::nodes::node_shader_normal_cc + +void register_node_type_sh_normal() { + namespace file_ns = blender::nodes::node_shader_normal_cc; + static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_NORMAL, "Normal", NODE_CLASS_OP_VECTOR, 0); - node_type_socket_templates(&ntype, sh_node_normal_in, sh_node_normal_out); - node_type_exec(&ntype, NULL, NULL, node_shader_exec_normal); - node_type_gpu(&ntype, gpu_shader_normal); + sh_node_type_base(&ntype, SH_NODE_NORMAL, "Normal", NODE_CLASS_OP_VECTOR); + ntype.declare = file_ns::node_declare; + node_type_gpu(&ntype, file_ns::gpu_shader_normal); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_normal_map.c b/source/blender/nodes/shader/nodes/node_shader_normal_map.cc index 6c4f2070035..9eadb6ce014 100644 --- a/source/blender/nodes/shader/nodes/node_shader_normal_map.c +++ b/source/blender/nodes/shader/nodes/node_shader_normal_map.cc @@ -17,34 +17,43 @@ * All rights reserved. */ -#include "../node_shader_util.h" +#include "node_shader_util.hh" -/* **************** OUTPUT ******************** */ +#include "BKE_context.h" -static bNodeSocketTemplate sh_node_normal_map_in[] = { - {SOCK_FLOAT, N_("Strength"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 10.0f}, - {SOCK_RGBA, N_("Color"), 0.5f, 0.5f, 1.0f, 1.0f, 0.0f, 1.0f}, - {-1, ""}, -}; +#include "UI_interface.h" +#include "UI_resources.h" -static bNodeSocketTemplate sh_node_normal_map_out[] = { - {SOCK_VECTOR, N_("Normal"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {-1, ""}, -}; +namespace blender::nodes::node_shader_normal_map_cc { -static void node_shader_init_normal_map(bNodeTree *UNUSED(ntree), bNode *node) +static void node_declare(NodeDeclarationBuilder &b) { - NodeShaderNormalMap *attr = MEM_callocN(sizeof(NodeShaderNormalMap), "NodeShaderNormalMap"); - node->storage = attr; + b.add_input<decl::Float>(N_("Strength")).default_value(1.0f).min(0.0f).max(10.0f); + b.add_input<decl::Color>(N_("Color")).default_value({0.5f, 0.5f, 1.0f, 1.0f}); + b.add_output<decl::Vector>(N_("Normal")); +} + +static void node_shader_buts_normal_map(uiLayout *layout, bContext *C, PointerRNA *ptr) +{ + uiItemR(layout, ptr, "space", UI_ITEM_R_SPLIT_EMPTY_NAME, "", 0); + + if (RNA_enum_get(ptr, "space") == SHD_SPACE_TANGENT) { + PointerRNA obptr = CTX_data_pointer_get(C, "active_object"); + + if (obptr.data && RNA_enum_get(&obptr, "type") == OB_MESH) { + PointerRNA dataptr = RNA_pointer_get(&obptr, "data"); + uiItemPointerR(layout, ptr, "uv_map", &dataptr, "uv_layers", "", ICON_NONE); + } + else { + uiItemR(layout, ptr, "uv_map", UI_ITEM_R_SPLIT_EMPTY_NAME, "", 0); + } + } } -static void node_shader_exec_normal_map(void *UNUSED(data), - int UNUSED(thread), - bNode *UNUSED(node), - bNodeExecData *UNUSED(execdata), - bNodeStack **UNUSED(in), - bNodeStack **UNUSED(out)) +static void node_shader_init_normal_map(bNodeTree *UNUSED(ntree), bNode *node) { + NodeShaderNormalMap *attr = MEM_cnew<NodeShaderNormalMap>("NodeShaderNormalMap"); + node->storage = attr; } static int gpu_shader_normal_map(GPUMaterial *mat, @@ -53,15 +62,16 @@ static int gpu_shader_normal_map(GPUMaterial *mat, GPUNodeStack *in, GPUNodeStack *out) { - NodeShaderNormalMap *nm = node->storage; + NodeShaderNormalMap *nm = static_cast<NodeShaderNormalMap *>(node->storage); GPUNodeLink *strength; if (in[0].link) { strength = in[0].link; } else if (node->original) { - bNodeSocket *socket = BLI_findlink(&node->original->inputs, 0); - bNodeSocketValueFloat *socket_data = socket->default_value; + bNodeSocket *socket = static_cast<bNodeSocket *>(BLI_findlink(&node->original->inputs, 0)); + bNodeSocketValueFloat *socket_data = static_cast<bNodeSocketValueFloat *>( + socket->default_value); strength = GPU_uniform(&socket_data->value); } else { @@ -73,8 +83,8 @@ static int gpu_shader_normal_map(GPUMaterial *mat, newnormal = in[1].link; } else if (node->original) { - bNodeSocket *socket = BLI_findlink(&node->original->inputs, 1); - bNodeSocketValueRGBA *socket_data = socket->default_value; + bNodeSocket *socket = static_cast<bNodeSocket *>(BLI_findlink(&node->original->inputs, 1)); + bNodeSocketValueRGBA *socket_data = static_cast<bNodeSocketValueRGBA *>(socket->default_value); newnormal = GPU_uniform(socket_data->value); } else { @@ -111,19 +121,23 @@ static int gpu_shader_normal_map(GPUMaterial *mat, return true; } +} // namespace blender::nodes::node_shader_normal_map_cc + /* node type definition */ -void register_node_type_sh_normal_map(void) +void register_node_type_sh_normal_map() { + namespace file_ns = blender::nodes::node_shader_normal_map_cc; + static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_NORMAL_MAP, "Normal Map", NODE_CLASS_OP_VECTOR, 0); - node_type_socket_templates(&ntype, sh_node_normal_map_in, sh_node_normal_map_out); + sh_node_type_base(&ntype, SH_NODE_NORMAL_MAP, "Normal Map", NODE_CLASS_OP_VECTOR); + ntype.declare = file_ns::node_declare; + ntype.draw_buttons = file_ns::node_shader_buts_normal_map; node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); - node_type_init(&ntype, node_shader_init_normal_map); + node_type_init(&ntype, file_ns::node_shader_init_normal_map); node_type_storage( &ntype, "NodeShaderNormalMap", node_free_standard_storage, node_copy_standard_storage); - node_type_gpu(&ntype, gpu_shader_normal_map); - node_type_exec(&ntype, NULL, NULL, node_shader_exec_normal_map); + node_type_gpu(&ntype, file_ns::gpu_shader_normal_map); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_object_info.c b/source/blender/nodes/shader/nodes/node_shader_object_info.cc index f3eb5dcc26d..75c9c8f9206 100644 --- a/source/blender/nodes/shader/nodes/node_shader_object_info.c +++ b/source/blender/nodes/shader/nodes/node_shader_object_info.cc @@ -17,18 +17,18 @@ * All rights reserved. */ -#include "../node_shader_util.h" +#include "node_shader_util.hh" -/* **************** OUTPUT ******************** */ +namespace blender::nodes::node_shader_object_info_cc { -static bNodeSocketTemplate sh_node_object_info_out[] = { - {SOCK_VECTOR, N_("Location"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_RGBA, N_("Color"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Object Index"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Material Index"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Random"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {-1, ""}, -}; +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_output<decl::Vector>(N_("Location")); + b.add_output<decl::Color>(N_("Color")); + b.add_output<decl::Float>(N_("Object Index")); + b.add_output<decl::Float>(N_("Material Index")); + b.add_output<decl::Float>(N_("Random")); +} static int node_shader_gpu_object_info(GPUMaterial *mat, bNode *node, @@ -42,13 +42,17 @@ static int node_shader_gpu_object_info(GPUMaterial *mat, return GPU_stack_link(mat, node, "node_object_info", in, out, GPU_constant(&index)); } -void register_node_type_sh_object_info(void) +} // namespace blender::nodes::node_shader_object_info_cc + +void register_node_type_sh_object_info() { + namespace file_ns = blender::nodes::node_shader_object_info_cc; + static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_OBJECT_INFO, "Object Info", NODE_CLASS_INPUT, 0); - node_type_socket_templates(&ntype, NULL, sh_node_object_info_out); - node_type_gpu(&ntype, node_shader_gpu_object_info); + sh_node_type_base(&ntype, SH_NODE_OBJECT_INFO, "Object Info", NODE_CLASS_INPUT); + ntype.declare = file_ns::node_declare; + node_type_gpu(&ntype, file_ns::node_shader_gpu_object_info); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_output_aov.c b/source/blender/nodes/shader/nodes/node_shader_output_aov.cc index 18c8edfe41c..b5177014f3a 100644 --- a/source/blender/nodes/shader/nodes/node_shader_output_aov.c +++ b/source/blender/nodes/shader/nodes/node_shader_output_aov.cc @@ -17,21 +17,29 @@ * All rights reserved. */ -#include "../node_shader_util.h" +#include "node_shader_util.hh" #include "BLI_hash.h" -/* **************** OUTPUT ******************** */ +#include "UI_interface.h" +#include "UI_resources.h" -static bNodeSocketTemplate sh_node_output_aov_in[] = { - {SOCK_RGBA, N_("Color"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Value"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {-1, ""}, -}; +namespace blender::nodes::node_shader_output_aov_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Color>(N_("Color")).default_value({0.0f, 0.0f, 0.0f, 1.0f}); + b.add_input<decl::Float>(N_("Value")).default_value(0.0f).min(0.0f).max(1.0f); +} + +static void node_shader_buts_output_aov(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "name", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); +} static void node_shader_init_output_aov(bNodeTree *UNUSED(ntree), bNode *node) { - NodeShaderOutputAOV *aov = MEM_callocN(sizeof(NodeShaderOutputAOV), "NodeShaderOutputAOV"); + NodeShaderOutputAOV *aov = MEM_cnew<NodeShaderOutputAOV>("NodeShaderOutputAOV"); node->storage = aov; } @@ -49,20 +57,24 @@ static int node_shader_gpu_output_aov(GPUMaterial *mat, return true; } +} // namespace blender::nodes::node_shader_output_aov_cc + /* node type definition */ -void register_node_type_sh_output_aov(void) +void register_node_type_sh_output_aov() { + namespace file_ns = blender::nodes::node_shader_output_aov_cc; + static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_OUTPUT_AOV, "AOV Output", NODE_CLASS_OUTPUT, 0); - node_type_socket_templates(&ntype, sh_node_output_aov_in, NULL); - node_type_init(&ntype, node_shader_init_output_aov); + sh_node_type_base(&ntype, SH_NODE_OUTPUT_AOV, "AOV Output", NODE_CLASS_OUTPUT); + ntype.declare = file_ns::node_declare; + ntype.draw_buttons = file_ns::node_shader_buts_output_aov; + node_type_init(&ntype, file_ns::node_shader_init_output_aov); node_type_storage( &ntype, "NodeShaderOutputAOV", node_free_standard_storage, node_copy_standard_storage); - node_type_gpu(&ntype, node_shader_gpu_output_aov); + node_type_gpu(&ntype, file_ns::node_shader_gpu_output_aov); - /* Do not allow muting output node. */ - node_type_internal_links(&ntype, NULL); + ntype.no_muting = true; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_output_light.c b/source/blender/nodes/shader/nodes/node_shader_output_light.cc index 722202bafdc..0c8288f801b 100644 --- a/source/blender/nodes/shader/nodes/node_shader_output_light.c +++ b/source/blender/nodes/shader/nodes/node_shader_output_light.cc @@ -17,27 +17,27 @@ * All rights reserved. */ -#include "../node_shader_util.h" +#include "node_shader_util.hh" -/* **************** OUTPUT ******************** */ +namespace blender::nodes::node_shader_output_light_cc { -static bNodeSocketTemplate sh_node_output_light_in[] = { - {SOCK_SHADER, N_("Surface"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {-1, ""}, -}; +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Shader>(N_("Surface")); +} + +} // namespace blender::nodes::node_shader_output_light_cc /* node type definition */ -void register_node_type_sh_output_light(void) +void register_node_type_sh_output_light() { - static bNodeType ntype; + namespace file_ns = blender::nodes::node_shader_output_light_cc; - sh_node_type_base(&ntype, SH_NODE_OUTPUT_LIGHT, "Light Output", NODE_CLASS_OUTPUT, 0); - node_type_socket_templates(&ntype, sh_node_output_light_in, NULL); - node_type_init(&ntype, NULL); - node_type_storage(&ntype, "", NULL, NULL); + static bNodeType ntype; - /* Do not allow muting output node. */ - node_type_internal_links(&ntype, NULL); + sh_node_type_base(&ntype, SH_NODE_OUTPUT_LIGHT, "Light Output", NODE_CLASS_OUTPUT); + ntype.declare = file_ns::node_declare; + ntype.no_muting = true; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_output_linestyle.c b/source/blender/nodes/shader/nodes/node_shader_output_linestyle.c deleted file mode 100644 index 5b4ebf21f5f..00000000000 --- a/source/blender/nodes/shader/nodes/node_shader_output_linestyle.c +++ /dev/null @@ -1,45 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2005 Blender Foundation. - * All rights reserved. - */ - -#include "../node_shader_util.h" - -/* **************** OUTPUT ******************** */ - -static bNodeSocketTemplate sh_node_output_linestyle_in[] = { - {SOCK_RGBA, N_("Color"), 1.0f, 0.0f, 1.0f, 1.0f}, - {SOCK_FLOAT, N_("Color Fac"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_FLOAT, N_("Alpha"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_FLOAT, N_("Alpha Fac"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {-1, ""}, -}; - -/* node type definition */ -void register_node_type_sh_output_linestyle(void) -{ - static bNodeType ntype; - - sh_node_type_base(&ntype, SH_NODE_OUTPUT_LINESTYLE, "Line Style Output", NODE_CLASS_OUTPUT, 0); - node_type_socket_templates(&ntype, sh_node_output_linestyle_in, NULL); - node_type_init(&ntype, NULL); - - /* Do not allow muting output node. */ - node_type_internal_links(&ntype, NULL); - - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/shader/nodes/node_shader_output_linestyle.cc b/source/blender/nodes/shader/nodes/node_shader_output_linestyle.cc new file mode 100644 index 00000000000..9b6c3292e75 --- /dev/null +++ b/source/blender/nodes/shader/nodes/node_shader_output_linestyle.cc @@ -0,0 +1,72 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2005 Blender Foundation. + * All rights reserved. + */ + +#include "node_shader_util.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes::node_shader_output_linestyle_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Color>(N_("Color")).default_value({1.0f, 0.0f, 1.0f, 1.0f}); + b.add_input<decl::Float>(N_("Color Fac")) + .default_value(1.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("Alpha")) + .default_value(1.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("Alpha Fac")) + .default_value(1.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); +} + +static void node_buts_output_linestyle(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayout *row, *col; + + col = uiLayoutColumn(layout, false); + row = uiLayoutRow(col, true); + uiItemR(row, ptr, "blend_type", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); + uiItemR(col, ptr, "use_clamp", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); +} + +} // namespace blender::nodes::node_shader_output_linestyle_cc + +/* node type definition */ +void register_node_type_sh_output_linestyle() +{ + namespace file_ns = blender::nodes::node_shader_output_linestyle_cc; + + static bNodeType ntype; + + sh_node_type_base(&ntype, SH_NODE_OUTPUT_LINESTYLE, "Line Style Output", NODE_CLASS_OUTPUT); + ntype.declare = file_ns::node_declare; + ntype.draw_buttons = file_ns::node_buts_output_linestyle; + ntype.no_muting = true; + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/shader/nodes/node_shader_output_material.c b/source/blender/nodes/shader/nodes/node_shader_output_material.c deleted file mode 100644 index 5b6d2914ccf..00000000000 --- a/source/blender/nodes/shader/nodes/node_shader_output_material.c +++ /dev/null @@ -1,85 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2005 Blender Foundation. - * All rights reserved. - */ - -#include "../node_shader_util.h" - -#include "BKE_scene.h" - -/* **************** OUTPUT ******************** */ - -static bNodeSocketTemplate sh_node_output_material_in[] = { - {SOCK_SHADER, N_("Surface")}, - {SOCK_SHADER, N_("Volume")}, - {SOCK_VECTOR, - N_("Displacement"), - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 1.0f, - PROP_NONE, - SOCK_HIDE_VALUE}, - {SOCK_FLOAT, N_("Thickness"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, - {-1, ""}, -}; - -static int node_shader_gpu_output_material(GPUMaterial *mat, - bNode *UNUSED(node), - bNodeExecData *UNUSED(execdata), - GPUNodeStack *in, - GPUNodeStack *UNUSED(out)) -{ - GPUNodeLink *outlink_surface, *outlink_volume, *outlink_displacement, *outlink_thickness; - /* Passthrough node in order to do the right socket conversions (important for displacement). */ - if (in[0].link) { - GPU_link(mat, "node_output_material_surface", in[0].link, &outlink_surface); - GPU_material_output_surface(mat, outlink_surface); - } - if (in[1].link) { - GPU_link(mat, "node_output_material_volume", in[1].link, &outlink_volume); - GPU_material_output_volume(mat, outlink_volume); - } - if (in[2].link) { - GPU_link(mat, "node_output_material_displacement", in[2].link, &outlink_displacement); - GPU_material_output_displacement(mat, outlink_displacement); - } - if (in[3].link) { - GPU_link(mat, "node_output_material_thickness", in[3].link, &outlink_thickness); - GPU_material_output_thickness(mat, outlink_thickness); - } - return true; -} - -/* node type definition */ -void register_node_type_sh_output_material(void) -{ - static bNodeType ntype; - - sh_node_type_base(&ntype, SH_NODE_OUTPUT_MATERIAL, "Material Output", NODE_CLASS_OUTPUT, 0); - node_type_socket_templates(&ntype, sh_node_output_material_in, NULL); - node_type_init(&ntype, NULL); - node_type_storage(&ntype, "", NULL, NULL); - node_type_gpu(&ntype, node_shader_gpu_output_material); - - /* Do not allow muting output node. */ - node_type_internal_links(&ntype, NULL); - - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/shader/nodes/node_shader_output_material.cc b/source/blender/nodes/shader/nodes/node_shader_output_material.cc new file mode 100644 index 00000000000..5fc95b92e3f --- /dev/null +++ b/source/blender/nodes/shader/nodes/node_shader_output_material.cc @@ -0,0 +1,83 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2005 Blender Foundation. + * All rights reserved. + */ + +#include "node_shader_util.hh" + +#include "BKE_scene.h" + +namespace blender::nodes::node_shader_output_material_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Shader>(N_("Surface")); + b.add_input<decl::Shader>(N_("Volume")); + b.add_input<decl::Vector>(N_("Displacement")).hide_value(); +} + +static int node_shader_gpu_output_material(GPUMaterial *mat, + bNode *node, + bNodeExecData *UNUSED(execdata), + GPUNodeStack *in, + GPUNodeStack *out) +{ + GPUNodeLink *outlink, *alpha_threshold_link, *shadow_threshold_link; + Material *ma = GPU_material_get_material(mat); + + static float no_alpha_threshold = -1.0f; + if (ma) { + alpha_threshold_link = GPU_uniform((ma->blend_method == MA_BM_CLIP) ? &ma->alpha_threshold : + &no_alpha_threshold); + shadow_threshold_link = GPU_uniform((ma->blend_shadow == MA_BS_CLIP) ? &ma->alpha_threshold : + &no_alpha_threshold); + } + else { + alpha_threshold_link = GPU_uniform(&no_alpha_threshold); + shadow_threshold_link = GPU_uniform(&no_alpha_threshold); + } + + GPU_stack_link(mat, + node, + "node_output_material", + in, + out, + alpha_threshold_link, + shadow_threshold_link, + &outlink); + GPU_material_output_link(mat, outlink); + + return true; +} + +} // namespace blender::nodes::node_shader_output_material_cc + +/* node type definition */ +void register_node_type_sh_output_material() +{ + namespace file_ns = blender::nodes::node_shader_output_material_cc; + + static bNodeType ntype; + + sh_node_type_base(&ntype, SH_NODE_OUTPUT_MATERIAL, "Material Output", NODE_CLASS_OUTPUT); + ntype.declare = file_ns::node_declare; + node_type_gpu(&ntype, file_ns::node_shader_gpu_output_material); + + ntype.no_muting = true; + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/shader/nodes/node_shader_output_world.c b/source/blender/nodes/shader/nodes/node_shader_output_world.cc index eaecfc266a3..a3509fd3ff4 100644 --- a/source/blender/nodes/shader/nodes/node_shader_output_world.c +++ b/source/blender/nodes/shader/nodes/node_shader_output_world.cc @@ -17,15 +17,15 @@ * All rights reserved. */ -#include "../node_shader_util.h" +#include "node_shader_util.hh" -/* **************** OUTPUT ******************** */ +namespace blender::nodes::node_shader_output_world_cc { -static bNodeSocketTemplate sh_node_output_world_in[] = { - {SOCK_SHADER, N_("Surface"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_SHADER, N_("Volume"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {-1, ""}, -}; +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Shader>(N_("Surface")); + b.add_input<decl::Shader>(N_("Volume")); +} static int node_shader_gpu_output_world(GPUMaterial *mat, bNode *UNUSED(node), @@ -45,19 +45,20 @@ static int node_shader_gpu_output_world(GPUMaterial *mat, return true; } +} // namespace blender::nodes::node_shader_output_world_cc + /* node type definition */ -void register_node_type_sh_output_world(void) +void register_node_type_sh_output_world() { + namespace file_ns = blender::nodes::node_shader_output_world_cc; + static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_OUTPUT_WORLD, "World Output", NODE_CLASS_OUTPUT, 0); - node_type_socket_templates(&ntype, sh_node_output_world_in, NULL); - node_type_init(&ntype, NULL); - node_type_storage(&ntype, "", NULL, NULL); - node_type_gpu(&ntype, node_shader_gpu_output_world); + sh_node_type_base(&ntype, SH_NODE_OUTPUT_WORLD, "World Output", NODE_CLASS_OUTPUT); + ntype.declare = file_ns::node_declare; + node_type_gpu(&ntype, file_ns::node_shader_gpu_output_world); - /* Do not allow muting output node. */ - node_type_internal_links(&ntype, NULL); + ntype.no_muting = true; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_particle_info.c b/source/blender/nodes/shader/nodes/node_shader_particle_info.cc index 75966843294..5792282fa0f 100644 --- a/source/blender/nodes/shader/nodes/node_shader_particle_info.c +++ b/source/blender/nodes/shader/nodes/node_shader_particle_info.cc @@ -17,30 +17,25 @@ * All rights reserved. */ -#include "../node_shader_util.h" +#include "node_shader_util.hh" + #include "RE_texture.h" -static bNodeSocketTemplate outputs[] = { - {SOCK_FLOAT, "Index"}, - {SOCK_FLOAT, "Random"}, - {SOCK_FLOAT, "Age"}, - {SOCK_FLOAT, "Lifetime"}, - {SOCK_VECTOR, "Location"}, +namespace blender::nodes::node_shader_particle_info_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_output<decl::Float>(N_("Index")); + b.add_output<decl::Float>(N_("Random")); + b.add_output<decl::Float>(N_("Age")); + b.add_output<decl::Float>(N_("Lifetime")); + b.add_output<decl::Vector>(N_("Location")); #if 0 /* quaternion sockets not yet supported */ - {SOCK_QUATERNION, "Rotation"}, + b.add_output<decl::Quaternion>(N_("Rotation")); #endif - {SOCK_FLOAT, "Size"}, - {SOCK_VECTOR, "Velocity"}, - {SOCK_VECTOR, "Angular Velocity"}, - {-1, ""}, -}; -static void node_shader_exec_particle_info(void *UNUSED(data), - int UNUSED(thread), - bNode *UNUSED(node), - bNodeExecData *UNUSED(execdata), - bNodeStack **UNUSED(in), - bNodeStack **UNUSED(out)) -{ + b.add_output<decl::Float>(N_("Size")); + b.add_output<decl::Vector>(N_("Velocity")); + b.add_output<decl::Vector>(N_("Angular Velocity")); } static int gpu_shader_particle_info(GPUMaterial *mat, @@ -54,15 +49,18 @@ static int gpu_shader_particle_info(GPUMaterial *mat, return GPU_stack_link(mat, node, "particle_info", in, out); } +} // namespace blender::nodes::node_shader_particle_info_cc + /* node type definition */ -void register_node_type_sh_particle_info(void) +void register_node_type_sh_particle_info() { + namespace file_ns = blender::nodes::node_shader_particle_info_cc; + static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_PARTICLE_INFO, "Particle Info", NODE_CLASS_INPUT, 0); - node_type_socket_templates(&ntype, NULL, outputs); - node_type_exec(&ntype, NULL, NULL, node_shader_exec_particle_info); - node_type_gpu(&ntype, gpu_shader_particle_info); + sh_node_type_base(&ntype, SH_NODE_PARTICLE_INFO, "Particle Info", NODE_CLASS_INPUT); + ntype.declare = file_ns::node_declare; + node_type_gpu(&ntype, file_ns::gpu_shader_particle_info); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_point_info.cc b/source/blender/nodes/shader/nodes/node_shader_point_info.cc new file mode 100644 index 00000000000..adc58ca065a --- /dev/null +++ b/source/blender/nodes/shader/nodes/node_shader_point_info.cc @@ -0,0 +1,54 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2005 Blender Foundation. + * All rights reserved. + */ + +#include "node_shader_util.hh" + +namespace blender::nodes::node_shader_point_info_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_output<decl::Vector>(N_("Position")); + b.add_output<decl::Float>(N_("Radius")); + b.add_output<decl::Float>(N_("Random")); +} + +static int node_shader_gpu_point_info(GPUMaterial *mat, + bNode *node, + bNodeExecData *UNUSED(execdata), + GPUNodeStack *in, + GPUNodeStack *out) +{ + return GPU_stack_link(mat, node, "node_point_info", in, out); +} + +} // namespace blender::nodes::node_shader_point_info_cc + +/* node type definition */ +void register_node_type_sh_point_info() +{ + namespace file_ns = blender::nodes::node_shader_point_info_cc; + + static bNodeType ntype; + + sh_node_type_base(&ntype, SH_NODE_POINT_INFO, "Point Info", NODE_CLASS_INPUT); + ntype.declare = file_ns::node_declare; + node_type_gpu(&ntype, file_ns::node_shader_gpu_point_info); + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/shader/nodes/node_shader_rgb.c b/source/blender/nodes/shader/nodes/node_shader_rgb.cc index 0bdef9a2a17..f3b83b72232 100644 --- a/source/blender/nodes/shader/nodes/node_shader_rgb.c +++ b/source/blender/nodes/shader/nodes/node_shader_rgb.cc @@ -21,13 +21,14 @@ * \ingroup shdnodes */ -#include "node_shader_util.h" +#include "node_shader_util.hh" -/* **************** RGB ******************** */ -static bNodeSocketTemplate sh_node_rgb_out[] = { - {SOCK_RGBA, N_("Color"), 0.5f, 0.5f, 0.5f, 1.0f}, - {-1, ""}, -}; +namespace blender::nodes::node_shader_rgb_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_output<decl::Color>(N_("Color")).default_value({0.5f, 0.5f, 0.5f, 1.0f}); +} static int gpu_shader_rgb(GPUMaterial *mat, bNode *node, @@ -39,13 +40,17 @@ static int gpu_shader_rgb(GPUMaterial *mat, return GPU_stack_link(mat, node, "set_rgba", in, out, link); } -void register_node_type_sh_rgb(void) +} // namespace blender::nodes::node_shader_rgb_cc + +void register_node_type_sh_rgb() { + namespace file_ns = blender::nodes::node_shader_rgb_cc; + static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_RGB, "RGB", NODE_CLASS_INPUT, 0); - node_type_socket_templates(&ntype, NULL, sh_node_rgb_out); - node_type_gpu(&ntype, gpu_shader_rgb); + sh_node_type_base(&ntype, SH_NODE_RGB, "RGB", NODE_CLASS_INPUT); + ntype.declare = file_ns::node_declare; + node_type_gpu(&ntype, file_ns::gpu_shader_rgb); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_rgb_to_bw.cc b/source/blender/nodes/shader/nodes/node_shader_rgb_to_bw.cc new file mode 100644 index 00000000000..13ba056d9ee --- /dev/null +++ b/source/blender/nodes/shader/nodes/node_shader_rgb_to_bw.cc @@ -0,0 +1,58 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2005 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup shdnodes + */ + +#include "IMB_colormanagement.h" + +#include "node_shader_util.hh" + +namespace blender::nodes::node_shader_rgb_to_bw_cc { + +static void sh_node_rgbtobw_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Color>(N_("Color")).default_value({0.5f, 0.5f, 0.5f, 1.0f}); + b.add_output<decl::Float>(N_("Val")); +} + +static int gpu_shader_rgbtobw(GPUMaterial *mat, + bNode *node, + bNodeExecData *UNUSED(execdata), + GPUNodeStack *in, + GPUNodeStack *out) +{ + return GPU_stack_link(mat, node, "rgbtobw", in, out); +} + +} // namespace blender::nodes::node_shader_rgb_to_bw_cc + +void register_node_type_sh_rgbtobw() +{ + namespace file_ns = blender::nodes::node_shader_rgb_to_bw_cc; + + static bNodeType ntype; + + sh_node_type_base(&ntype, SH_NODE_RGBTOBW, "RGB to BW", NODE_CLASS_CONVERTER); + ntype.declare = file_ns::sh_node_rgbtobw_declare; + node_type_gpu(&ntype, file_ns::gpu_shader_rgbtobw); + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/shader/nodes/node_shader_script.c b/source/blender/nodes/shader/nodes/node_shader_script.c deleted file mode 100644 index 42ab272de0e..00000000000 --- a/source/blender/nodes/shader/nodes/node_shader_script.c +++ /dev/null @@ -1,70 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2005 Blender Foundation. - * All rights reserved. - */ - -/** \file - * \ingroup shdnodes - */ - -#include "node_shader_util.h" - -/* **************** Script ******************** */ - -static void init(bNodeTree *UNUSED(ntree), bNode *node) -{ - NodeShaderScript *nss = MEM_callocN(sizeof(NodeShaderScript), "shader script node"); - node->storage = nss; -} - -static void node_free_script(bNode *node) -{ - NodeShaderScript *nss = node->storage; - - if (nss) { - if (nss->bytecode) { - MEM_freeN(nss->bytecode); - } - - MEM_freeN(nss); - } -} - -static void node_copy_script(bNodeTree *UNUSED(dest_ntree), - bNode *dest_node, - const bNode *src_node) -{ - NodeShaderScript *src_nss = src_node->storage; - NodeShaderScript *dest_nss = MEM_dupallocN(src_nss); - - if (src_nss->bytecode) { - dest_nss->bytecode = MEM_dupallocN(src_nss->bytecode); - } - - dest_node->storage = dest_nss; -} - -void register_node_type_sh_script(void) -{ - static bNodeType ntype; - - sh_node_type_base(&ntype, SH_NODE_SCRIPT, "Script", NODE_CLASS_SCRIPT, 0); - node_type_init(&ntype, init); - node_type_storage(&ntype, "NodeShaderScript", node_free_script, node_copy_script); - - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/shader/nodes/node_shader_script.cc b/source/blender/nodes/shader/nodes/node_shader_script.cc new file mode 100644 index 00000000000..e6af90fa588 --- /dev/null +++ b/source/blender/nodes/shader/nodes/node_shader_script.cc @@ -0,0 +1,112 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2005 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup shdnodes + */ + +#include "node_shader_util.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes::node_shader_script_cc { + +static void node_shader_buts_script(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayout *row; + + row = uiLayoutRow(layout, false); + uiItemR(row, ptr, "mode", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_EXPAND, nullptr, ICON_NONE); + + row = uiLayoutRow(layout, true); + + if (RNA_enum_get(ptr, "mode") == NODE_SCRIPT_INTERNAL) { + uiItemR(row, ptr, "script", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); + } + else { + uiItemR(row, ptr, "filepath", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); + } + + uiItemO(row, "", ICON_FILE_REFRESH, "node.shader_script_update"); +} + +static void node_shader_buts_script_ex(uiLayout *layout, bContext *C, PointerRNA *ptr) +{ + uiItemS(layout); + + node_shader_buts_script(layout, C, ptr); + +#if 0 /* not implemented yet */ + if (RNA_enum_get(ptr, "mode") == NODE_SCRIPT_EXTERNAL) { + uiItemR(layout, ptr, "use_auto_update", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + } +#endif +} + +static void init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeShaderScript *nss = MEM_cnew<NodeShaderScript>("shader script node"); + node->storage = nss; +} + +static void node_free_script(bNode *node) +{ + NodeShaderScript *nss = static_cast<NodeShaderScript *>(node->storage); + + if (nss) { + if (nss->bytecode) { + MEM_freeN(nss->bytecode); + } + + MEM_freeN(nss); + } +} + +static void node_copy_script(bNodeTree *UNUSED(dest_ntree), + bNode *dest_node, + const bNode *src_node) +{ + NodeShaderScript *src_nss = static_cast<NodeShaderScript *>(src_node->storage); + NodeShaderScript *dest_nss = static_cast<NodeShaderScript *>(MEM_dupallocN(src_nss)); + + if (src_nss->bytecode) { + dest_nss->bytecode = static_cast<char *>(MEM_dupallocN(src_nss->bytecode)); + } + + dest_node->storage = dest_nss; +} + +} // namespace blender::nodes::node_shader_script_cc + +void register_node_type_sh_script() +{ + namespace file_ns = blender::nodes::node_shader_script_cc; + + static bNodeType ntype; + + sh_node_type_base(&ntype, SH_NODE_SCRIPT, "Script", NODE_CLASS_SCRIPT); + ntype.draw_buttons = file_ns::node_shader_buts_script; + ntype.draw_buttons_ex = file_ns::node_shader_buts_script_ex; + node_type_init(&ntype, file_ns::init); + node_type_storage( + &ntype, "NodeShaderScript", file_ns::node_free_script, file_ns::node_copy_script); + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcombHSV.c b/source/blender/nodes/shader/nodes/node_shader_sepcombHSV.c deleted file mode 100644 index dfecb830b35..00000000000 --- a/source/blender/nodes/shader/nodes/node_shader_sepcombHSV.c +++ /dev/null @@ -1,118 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2013 Blender Foundation. - * All rights reserved. - */ - -/** \file - * \ingroup shdnodes - */ - -#include "node_shader_util.h" - -/* **************** SEPARATE HSV ******************** */ -static bNodeSocketTemplate sh_node_sephsv_in[] = { - {SOCK_RGBA, N_("Color"), 0.8f, 0.8f, 0.8f, 1.0f}, - {-1, ""}, -}; -static bNodeSocketTemplate sh_node_sephsv_out[] = { - {SOCK_FLOAT, N_("H")}, - {SOCK_FLOAT, N_("S")}, - {SOCK_FLOAT, N_("V")}, - {-1, ""}, -}; - -static void node_shader_exec_sephsv(void *UNUSED(data), - int UNUSED(thread), - bNode *UNUSED(node), - bNodeExecData *UNUSED(execdata), - bNodeStack **in, - bNodeStack **out) -{ - float col[3]; - nodestack_get_vec(col, SOCK_VECTOR, in[0]); - - rgb_to_hsv(col[0], col[1], col[2], &out[0]->vec[0], &out[1]->vec[0], &out[2]->vec[0]); -} - -static int gpu_shader_sephsv(GPUMaterial *mat, - bNode *node, - bNodeExecData *UNUSED(execdata), - GPUNodeStack *in, - GPUNodeStack *out) -{ - return GPU_stack_link(mat, node, "separate_hsv", in, out); -} - -void register_node_type_sh_sephsv(void) -{ - static bNodeType ntype; - - sh_node_type_base(&ntype, SH_NODE_SEPHSV, "Separate HSV", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, sh_node_sephsv_in, sh_node_sephsv_out); - node_type_exec(&ntype, NULL, NULL, node_shader_exec_sephsv); - node_type_gpu(&ntype, gpu_shader_sephsv); - - nodeRegisterType(&ntype); -} - -/* **************** COMBINE HSV ******************** */ -static bNodeSocketTemplate sh_node_combhsv_in[] = { - {SOCK_FLOAT, N_("H"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_UNSIGNED}, - {SOCK_FLOAT, N_("S"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_UNSIGNED}, - {SOCK_FLOAT, N_("V"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_UNSIGNED}, - {-1, ""}, -}; -static bNodeSocketTemplate sh_node_combhsv_out[] = { - {SOCK_RGBA, N_("Color")}, - {-1, ""}, -}; - -static void node_shader_exec_combhsv(void *UNUSED(data), - int UNUSED(thread), - bNode *UNUSED(node), - bNodeExecData *UNUSED(execdata), - bNodeStack **in, - bNodeStack **out) -{ - float h, s, v; - nodestack_get_vec(&h, SOCK_FLOAT, in[0]); - nodestack_get_vec(&s, SOCK_FLOAT, in[1]); - nodestack_get_vec(&v, SOCK_FLOAT, in[2]); - - hsv_to_rgb(h, s, v, &out[0]->vec[0], &out[0]->vec[1], &out[0]->vec[2]); -} - -static int gpu_shader_combhsv(GPUMaterial *mat, - bNode *node, - bNodeExecData *UNUSED(execdata), - GPUNodeStack *in, - GPUNodeStack *out) -{ - return GPU_stack_link(mat, node, "combine_hsv", in, out); -} - -void register_node_type_sh_combhsv(void) -{ - static bNodeType ntype; - - sh_node_type_base(&ntype, SH_NODE_COMBHSV, "Combine HSV", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, sh_node_combhsv_in, sh_node_combhsv_out); - node_type_exec(&ntype, NULL, NULL, node_shader_exec_combhsv); - node_type_gpu(&ntype, gpu_shader_combhsv); - - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcomb_hsv.cc b/source/blender/nodes/shader/nodes/node_shader_sepcomb_hsv.cc new file mode 100644 index 00000000000..700e4ce3667 --- /dev/null +++ b/source/blender/nodes/shader/nodes/node_shader_sepcomb_hsv.cc @@ -0,0 +1,96 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2013 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup shdnodes + */ + +#include "node_shader_util.hh" + +namespace blender::nodes::node_shader_sepcomb_hsv_cc { + +/* **************** SEPARATE HSV ******************** */ + +static void node_declare_sephsv(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Color>(N_("Color")).default_value({0.8f, 0.8f, 0.8f, 1.0}); + b.add_output<decl::Float>(N_("H")); + b.add_output<decl::Float>(N_("S")); + b.add_output<decl::Float>(N_("V")); +} + +static int gpu_shader_sephsv(GPUMaterial *mat, + bNode *node, + bNodeExecData *UNUSED(execdata), + GPUNodeStack *in, + GPUNodeStack *out) +{ + return GPU_stack_link(mat, node, "separate_hsv", in, out); +} + +} // namespace blender::nodes::node_shader_sepcomb_hsv_cc + +void register_node_type_sh_sephsv() +{ + namespace file_ns = blender::nodes::node_shader_sepcomb_hsv_cc; + + static bNodeType ntype; + + sh_node_type_base(&ntype, SH_NODE_SEPHSV, "Separate HSV", NODE_CLASS_CONVERTER); + ntype.declare = file_ns::node_declare_sephsv; + node_type_gpu(&ntype, file_ns::gpu_shader_sephsv); + + nodeRegisterType(&ntype); +} + +namespace blender::nodes::node_shader_sepcomb_hsv_cc { + +/* **************** COMBINE HSV ******************** */ + +static void node_declare_combhsv(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Float>(N_("H")).default_value(0.0f).min(0.0f).max(1.0f).subtype(PROP_UNSIGNED); + b.add_input<decl::Float>(N_("S")).default_value(0.0f).min(0.0f).max(1.0f).subtype(PROP_UNSIGNED); + b.add_input<decl::Float>(N_("V")).default_value(0.0f).min(0.0f).max(1.0f).subtype(PROP_UNSIGNED); + b.add_output<decl::Color>(N_("Color")); +} + +static int gpu_shader_combhsv(GPUMaterial *mat, + bNode *node, + bNodeExecData *UNUSED(execdata), + GPUNodeStack *in, + GPUNodeStack *out) +{ + return GPU_stack_link(mat, node, "combine_hsv", in, out); +} + +} // namespace blender::nodes::node_shader_sepcomb_hsv_cc + +void register_node_type_sh_combhsv() +{ + namespace file_ns = blender::nodes::node_shader_sepcomb_hsv_cc; + + static bNodeType ntype; + + sh_node_type_base(&ntype, SH_NODE_COMBHSV, "Combine HSV", NODE_CLASS_CONVERTER); + ntype.declare = file_ns::node_declare_combhsv; + node_type_gpu(&ntype, file_ns::gpu_shader_combhsv); + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc b/source/blender/nodes/shader/nodes/node_shader_sepcomb_rgb.cc index 24c5dcf7ba3..d4be0bd14dc 100644 --- a/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc +++ b/source/blender/nodes/shader/nodes/node_shader_sepcomb_rgb.cc @@ -21,34 +21,17 @@ * \ingroup shdnodes */ -#include "node_shader_util.h" +#include "node_shader_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_shader_sepcomb_rgb_cc { static void sh_node_seprgb_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input<decl::Color>("Image").default_value({0.8f, 0.8f, 0.8f, 1.0f}); - b.add_output<decl::Float>("R"); - b.add_output<decl::Float>("G"); - b.add_output<decl::Float>("B"); -}; - -} // namespace blender::nodes - -static void node_shader_exec_seprgb(void *UNUSED(data), - int UNUSED(thread), - bNode *UNUSED(node), - bNodeExecData *UNUSED(execdata), - bNodeStack **in, - bNodeStack **out) -{ - float col[3]; - nodestack_get_vec(col, SOCK_VECTOR, in[0]); - - out[0]->vec[0] = col[0]; - out[1]->vec[0] = col[1]; - out[2]->vec[0] = col[2]; + b.add_input<decl::Color>(N_("Image")).default_value({0.8f, 0.8f, 0.8f, 1.0f}); + b.add_output<decl::Float>(N_("R")); + b.add_output<decl::Float>(N_("G")); + b.add_output<decl::Float>(N_("B")); } static int gpu_shader_seprgb(GPUMaterial *mat, @@ -103,47 +86,31 @@ static void sh_node_seprgb_build_multi_function(blender::nodes::NodeMultiFunctio builder.set_matching_fn(fn); } -void register_node_type_sh_seprgb(void) +} // namespace blender::nodes::node_shader_sepcomb_rgb_cc + +void register_node_type_sh_seprgb() { + namespace file_ns = blender::nodes::node_shader_sepcomb_rgb_cc; + static bNodeType ntype; - sh_fn_node_type_base(&ntype, SH_NODE_SEPRGB, "Separate RGB", NODE_CLASS_CONVERTER, 0); - ntype.declare = blender::nodes::sh_node_seprgb_declare; - node_type_exec(&ntype, nullptr, nullptr, node_shader_exec_seprgb); - node_type_gpu(&ntype, gpu_shader_seprgb); - ntype.build_multi_function = sh_node_seprgb_build_multi_function; + sh_fn_node_type_base(&ntype, SH_NODE_SEPRGB, "Separate RGB", NODE_CLASS_CONVERTER); + 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; nodeRegisterType(&ntype); } -namespace blender::nodes { +namespace blender::nodes::node_shader_sepcomb_rgb_cc { static void sh_node_combrgb_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input<decl::Float>("R").min(0.0f).max(1.0f); - b.add_input<decl::Float>("G").min(0.0f).max(1.0f); - b.add_input<decl::Float>("B").min(0.0f).max(1.0f); - b.add_output<decl::Color>("Image"); -}; - -} // namespace blender::nodes - -static void node_shader_exec_combrgb(void *UNUSED(data), - int UNUSED(thread), - bNode *UNUSED(node), - bNodeExecData *UNUSED(execdata), - bNodeStack **in, - bNodeStack **out) -{ - float r, g, b; - nodestack_get_vec(&r, SOCK_FLOAT, in[0]); - nodestack_get_vec(&g, SOCK_FLOAT, in[1]); - nodestack_get_vec(&b, SOCK_FLOAT, in[2]); - - out[0]->vec[0] = r; - out[0]->vec[1] = g; - out[0]->vec[2] = 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_output<decl::Color>(N_("Image")); } static int gpu_shader_combrgb(GPUMaterial *mat, @@ -163,15 +130,18 @@ static void sh_node_combrgb_build_multi_function(blender::nodes::NodeMultiFuncti builder.set_matching_fn(fn); } -void register_node_type_sh_combrgb(void) +} // namespace blender::nodes::node_shader_sepcomb_rgb_cc + +void register_node_type_sh_combrgb() { + namespace file_ns = blender::nodes::node_shader_sepcomb_rgb_cc; + static bNodeType ntype; - sh_fn_node_type_base(&ntype, SH_NODE_COMBRGB, "Combine RGB", NODE_CLASS_CONVERTER, 0); - ntype.declare = blender::nodes::sh_node_combrgb_declare; - node_type_exec(&ntype, nullptr, nullptr, node_shader_exec_combrgb); - node_type_gpu(&ntype, gpu_shader_combrgb); - ntype.build_multi_function = sh_node_combrgb_build_multi_function; + sh_fn_node_type_base(&ntype, SH_NODE_COMBRGB, "Combine RGB", NODE_CLASS_CONVERTER); + 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; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc b/source/blender/nodes/shader/nodes/node_shader_sepcomb_xyz.cc index 8ca8fc19521..f8064eb192a 100644 --- a/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc +++ b/source/blender/nodes/shader/nodes/node_shader_sepcomb_xyz.cc @@ -21,20 +21,18 @@ * \ingroup shdnodes */ -#include "node_shader_util.h" +#include "node_shader_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_shader_sepcomb_xyz_cc { static void sh_node_sepxyz_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input<decl::Vector>("Vector").min(-10000.0f).max(10000.0f); - b.add_output<decl::Float>("X"); - b.add_output<decl::Float>("Y"); - b.add_output<decl::Float>("Z"); -}; - -} // namespace blender::nodes + b.add_input<decl::Vector>(N_("Vector")).min(-10000.0f).max(10000.0f); + b.add_output<decl::Float>(N_("X")); + b.add_output<decl::Float>(N_("Y")); + b.add_output<decl::Float>(N_("Z")); +} static int gpu_shader_sepxyz(GPUMaterial *mat, bNode *node, @@ -88,30 +86,32 @@ static void sh_node_sepxyz_build_multi_function(blender::nodes::NodeMultiFunctio builder.set_matching_fn(separate_fn); } -void register_node_type_sh_sepxyz(void) +} // namespace blender::nodes::node_shader_sepcomb_xyz_cc + +void register_node_type_sh_sepxyz() { + namespace file_ns = blender::nodes::node_shader_sepcomb_xyz_cc; + static bNodeType ntype; - sh_fn_node_type_base(&ntype, SH_NODE_SEPXYZ, "Separate XYZ", NODE_CLASS_CONVERTER, 0); - ntype.declare = blender::nodes::sh_node_sepxyz_declare; - node_type_gpu(&ntype, gpu_shader_sepxyz); - ntype.build_multi_function = sh_node_sepxyz_build_multi_function; + sh_fn_node_type_base(&ntype, SH_NODE_SEPXYZ, "Separate XYZ", NODE_CLASS_CONVERTER); + ntype.declare = file_ns::sh_node_sepxyz_declare; + node_type_gpu(&ntype, file_ns::gpu_shader_sepxyz); + ntype.build_multi_function = file_ns::sh_node_sepxyz_build_multi_function; nodeRegisterType(&ntype); } -namespace blender::nodes { +namespace blender::nodes::node_shader_sepcomb_xyz_cc { static void sh_node_combxyz_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input<decl::Float>("X").min(-10000.0f).max(10000.0f); - b.add_input<decl::Float>("Y").min(-10000.0f).max(10000.0f); - b.add_input<decl::Float>("Z").min(-10000.0f).max(10000.0f); - b.add_output<decl::Vector>("Vector"); -}; - -} // namespace blender::nodes + b.add_input<decl::Float>(N_("X")).min(-10000.0f).max(10000.0f); + b.add_input<decl::Float>(N_("Y")).min(-10000.0f).max(10000.0f); + b.add_input<decl::Float>(N_("Z")).min(-10000.0f).max(10000.0f); + b.add_output<decl::Vector>(N_("Vector")); +} static int gpu_shader_combxyz(GPUMaterial *mat, bNode *node, @@ -129,14 +129,18 @@ static void sh_node_combxyz_build_multi_function(blender::nodes::NodeMultiFuncti builder.set_matching_fn(fn); } -void register_node_type_sh_combxyz(void) +} // namespace blender::nodes::node_shader_sepcomb_xyz_cc + +void register_node_type_sh_combxyz() { + namespace file_ns = blender::nodes::node_shader_sepcomb_xyz_cc; + static bNodeType ntype; - sh_fn_node_type_base(&ntype, SH_NODE_COMBXYZ, "Combine XYZ", NODE_CLASS_CONVERTER, 0); - ntype.declare = blender::nodes::sh_node_combxyz_declare; - node_type_gpu(&ntype, gpu_shader_combxyz); - ntype.build_multi_function = sh_node_combxyz_build_multi_function; + sh_fn_node_type_base(&ntype, SH_NODE_COMBXYZ, "Combine XYZ", NODE_CLASS_CONVERTER); + ntype.declare = file_ns::sh_node_combxyz_declare; + node_type_gpu(&ntype, file_ns::gpu_shader_combxyz); + ntype.build_multi_function = file_ns::sh_node_combxyz_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_shaderToRgb.c b/source/blender/nodes/shader/nodes/node_shader_shader_to_rgb.cc index 4e8f47c087a..d601f45b49f 100644 --- a/source/blender/nodes/shader/nodes/node_shader_shaderToRgb.c +++ b/source/blender/nodes/shader/nodes/node_shader_shader_to_rgb.cc @@ -17,20 +17,16 @@ * All rights reserved. */ -#include "../node_shader_util.h" +#include "node_shader_util.hh" -/* **************** OUTPUT ******************** */ +namespace blender::nodes::node_shader_shader_to_rgb_cc { -static bNodeSocketTemplate sh_node_shadertorgb_in[] = { - {SOCK_SHADER, N_("Shader")}, - {-1, ""}, -}; - -static bNodeSocketTemplate sh_node_shadertorgb_out[] = { - {SOCK_RGBA, N_("Color")}, - {SOCK_FLOAT, N_("Alpha")}, - {-1, ""}, -}; +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Shader>(N_("Shader")); + b.add_output<decl::Color>(N_("Color")); + b.add_output<decl::Float>(N_("Alpha")); +} static int node_shader_gpu_shadertorgb(GPUMaterial *mat, bNode *node, @@ -43,16 +39,18 @@ static int node_shader_gpu_shadertorgb(GPUMaterial *mat, return GPU_stack_link(mat, node, "node_shader_to_rgba", in, out); } +} // namespace blender::nodes::node_shader_shader_to_rgb_cc + /* node type definition */ -void register_node_type_sh_shadertorgb(void) +void register_node_type_sh_shadertorgb() { + namespace file_ns = blender::nodes::node_shader_shader_to_rgb_cc; + static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_SHADERTORGB, "Shader to RGB", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, sh_node_shadertorgb_in, sh_node_shadertorgb_out); - node_type_init(&ntype, NULL); - node_type_storage(&ntype, "", NULL, NULL); - node_type_gpu(&ntype, node_shader_gpu_shadertorgb); + sh_node_type_base(&ntype, SH_NODE_SHADERTORGB, "Shader to RGB", NODE_CLASS_CONVERTER); + ntype.declare = file_ns::node_declare; + node_type_gpu(&ntype, file_ns::node_shader_gpu_shadertorgb); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_squeeze.c b/source/blender/nodes/shader/nodes/node_shader_squeeze.c deleted file mode 100644 index ca7bdf41df9..00000000000 --- a/source/blender/nodes/shader/nodes/node_shader_squeeze.c +++ /dev/null @@ -1,71 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2005 Blender Foundation. - * All rights reserved. - */ - -/** \file - * \ingroup shdnodes - */ - -#include "node_shader_util.h" - -/* **************** VALUE SQUEEZE ******************** */ -static bNodeSocketTemplate sh_node_squeeze_in[] = { - {SOCK_FLOAT, N_("Value"), 0.0f, 0.0f, 0.0f, 0.0f, -100.0f, 100.0f, PROP_NONE}, - {SOCK_FLOAT, N_("Width"), 1.0f, 0.0f, 0.0f, 0.0f, -100.0f, 100.0f, PROP_NONE}, - {SOCK_FLOAT, N_("Center"), 0.0f, 0.0f, 0.0f, 0.0f, -100.0f, 100.0f, PROP_NONE}, - {-1, ""}}; - -static bNodeSocketTemplate sh_node_squeeze_out[] = {{SOCK_FLOAT, N_("Value")}, {-1, ""}}; - -static void node_shader_exec_squeeze(void *UNUSED(data), - int UNUSED(thread), - bNode *UNUSED(node), - bNodeExecData *UNUSED(execdata), - bNodeStack **in, - bNodeStack **out) -{ - float vec[3]; - - nodestack_get_vec(vec, SOCK_FLOAT, in[0]); - nodestack_get_vec(vec + 1, SOCK_FLOAT, in[1]); - nodestack_get_vec(vec + 2, SOCK_FLOAT, in[2]); - - out[0]->vec[0] = 1.0f / (1.0f + powf(M_E, -((vec[0] - vec[2]) * vec[1]))); -} - -static int gpu_shader_squeeze(GPUMaterial *mat, - bNode *node, - bNodeExecData *UNUSED(execdata), - GPUNodeStack *in, - GPUNodeStack *out) -{ - return GPU_stack_link(mat, node, "squeeze", in, out); -} - -void register_node_type_sh_squeeze(void) -{ - static bNodeType ntype; - - sh_node_type_base(&ntype, SH_NODE_SQUEEZE, "Squeeze Value", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, sh_node_squeeze_in, sh_node_squeeze_out); - node_type_storage(&ntype, "", NULL, NULL); - node_type_exec(&ntype, NULL, NULL, node_shader_exec_squeeze); - node_type_gpu(&ntype, gpu_shader_squeeze); - - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/shader/nodes/node_shader_squeeze.cc b/source/blender/nodes/shader/nodes/node_shader_squeeze.cc new file mode 100644 index 00000000000..83965160019 --- /dev/null +++ b/source/blender/nodes/shader/nodes/node_shader_squeeze.cc @@ -0,0 +1,58 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2005 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup shdnodes + */ + +#include "node_shader_util.hh" + +namespace blender::nodes::node_shader_squeeze_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Float>(N_("Value")).default_value(0.0f).min(-100.0f).max(100.0f); + b.add_input<decl::Float>(N_("Width")).default_value(1.0f).min(-100.0f).max(100.0f); + b.add_input<decl::Float>(N_("Center")).default_value(0.0f).min(-100.0f).max(100.0f); + b.add_output<decl::Float>(N_("Value")); +} + +static int gpu_shader_squeeze(GPUMaterial *mat, + bNode *node, + bNodeExecData *UNUSED(execdata), + GPUNodeStack *in, + GPUNodeStack *out) +{ + return GPU_stack_link(mat, node, "squeeze", in, out); +} + +} // namespace blender::nodes::node_shader_squeeze_cc + +void register_node_type_sh_squeeze() +{ + namespace file_ns = blender::nodes::node_shader_squeeze_cc; + + static bNodeType ntype; + + sh_node_type_base(&ntype, SH_NODE_SQUEEZE, "Squeeze Value", NODE_CLASS_CONVERTER); + ntype.declare = file_ns::node_declare; + node_type_gpu(&ntype, file_ns::gpu_shader_squeeze); + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/shader/nodes/node_shader_subsurface_scattering.c b/source/blender/nodes/shader/nodes/node_shader_subsurface_scattering.c deleted file mode 100644 index 159e986e19e..00000000000 --- a/source/blender/nodes/shader/nodes/node_shader_subsurface_scattering.c +++ /dev/null @@ -1,76 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2005 Blender Foundation. - * All rights reserved. - */ - -#include "../node_shader_util.h" - -/* **************** OUTPUT ******************** */ - -static bNodeSocketTemplate sh_node_subsurface_scattering_in[] = { - {SOCK_RGBA, N_("Color"), 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Scale"), 1.0, 0.0f, 0.0f, 0.0f, 0.0f, 1000.0f}, - {SOCK_VECTOR, N_("Radius"), 1.0f, 0.2f, 0.1f, 0.0f, 0.0f, 100.0f, PROP_NONE, SOCK_COMPACT}, - {SOCK_FLOAT, N_("IOR"), 1.4f, 0.0f, 0.0f, 0.0f, 1.01f, 3.8f, PROP_FACTOR}, - {SOCK_FLOAT, N_("Anisotropy"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_VECTOR, N_("Normal"), 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, - {SOCK_FLOAT, N_("Weight"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, PROP_NONE, SOCK_UNAVAIL}, - {-1, ""}, -}; - -static bNodeSocketTemplate sh_node_subsurface_scattering_out[] = { - {SOCK_SHADER, N_("BSSRDF")}, - {-1, ""}, -}; - -static void node_shader_init_subsurface_scattering(bNodeTree *UNUSED(ntree), bNode *node) -{ - node->custom1 = SHD_SUBSURFACE_RANDOM_WALK; - node->custom2 = true; -} - -static int node_shader_gpu_subsurface_scattering(GPUMaterial *mat, - bNode *node, - bNodeExecData *UNUSED(execdata), - GPUNodeStack *in, - GPUNodeStack *out) -{ - if (!in[5].link) { - GPU_link(mat, "world_normals_get", &in[5].link); - } - - GPU_material_flag_set(mat, GPU_MATFLAG_DIFFUSE | GPU_MATFLAG_SUBSURFACE); - GPU_stack_link(mat, node, "node_subsurface_scattering", in, out); - return GPU_stack_eval_link(mat, node, "node_subsurface_scattering_eval", in, out); -} - -/* node type definition */ -void register_node_type_sh_subsurface_scattering(void) -{ - static bNodeType ntype; - - sh_node_type_base( - &ntype, SH_NODE_SUBSURFACE_SCATTERING, "Subsurface Scattering", NODE_CLASS_SHADER, 0); - node_type_socket_templates( - &ntype, sh_node_subsurface_scattering_in, sh_node_subsurface_scattering_out); - node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); - node_type_init(&ntype, node_shader_init_subsurface_scattering); - node_type_storage(&ntype, "", NULL, NULL); - node_type_gpu(&ntype, node_shader_gpu_subsurface_scattering); - - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/shader/nodes/node_shader_subsurface_scattering.cc b/source/blender/nodes/shader/nodes/node_shader_subsurface_scattering.cc new file mode 100644 index 00000000000..f60db81b4a9 --- /dev/null +++ b/source/blender/nodes/shader/nodes/node_shader_subsurface_scattering.cc @@ -0,0 +1,113 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2005 Blender Foundation. + * All rights reserved. + */ + +#include "node_shader_util.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes::node_shader_subsurface_scattering_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Color>(N_("Color")).default_value({0.8f, 0.8f, 0.8f, 1.0f}); + b.add_input<decl::Float>(N_("Scale")).default_value(1.0f).min(0.0f).max(1000.0f); + b.add_input<decl::Vector>(N_("Radius")) + .default_value({1.0f, 0.2f, 0.1f}) + .min(0.0f) + .max(100.0f) + .compact(); + b.add_input<decl::Float>(N_("IOR")).default_value(1.4f).min(1.01f).max(3.8f).subtype( + PROP_FACTOR); + b.add_input<decl::Float>(N_("Anisotropy")) + .default_value(0.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Vector>(N_("Normal")).hide_value(); + b.add_output<decl::Shader>(N_("BSSRDF")); +} + +static void node_shader_buts_subsurface(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "falloff", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); +} + +static void node_shader_init_subsurface_scattering(bNodeTree *UNUSED(ntree), bNode *node) +{ + node->custom1 = SHD_SUBSURFACE_RANDOM_WALK; + node->custom2 = true; +} + +static int node_shader_gpu_subsurface_scattering(GPUMaterial *mat, + bNode *node, + bNodeExecData *UNUSED(execdata), + GPUNodeStack *in, + GPUNodeStack *out) +{ + if (!in[5].link) { + GPU_link(mat, "world_normals_get", &in[5].link); + } + + if (node->sss_id > 0) { + bNodeSocket *socket = (bNodeSocket *)BLI_findlink(&node->original->inputs, 2); + bNodeSocketValueRGBA *socket_data = (bNodeSocketValueRGBA *)socket->default_value; + /* For some reason it seems that the socket value is in ARGB format. */ + GPU_material_sss_profile_create(mat, &socket_data->value[1]); + + /* sss_id is 0 only the node is not connected to any output. + * In this case flagging the material would trigger a bug (see T68736). */ + GPU_material_flag_set(mat, (eGPUMatFlag)(GPU_MATFLAG_DIFFUSE | GPU_MATFLAG_SSS)); + } + + return GPU_stack_link( + mat, node, "node_subsurface_scattering", in, out, GPU_constant(&node->sss_id)); +} + +static void node_shader_update_subsurface_scattering(bNodeTree *ntree, bNode *node) +{ + const int sss_method = node->custom1; + + LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { + if (STR_ELEM(sock->name, "IOR", "Anisotropy")) { + nodeSetSocketAvailability(ntree, sock, sss_method != SHD_SUBSURFACE_BURLEY); + } + } +} + +} // namespace blender::nodes::node_shader_subsurface_scattering_cc + +/* node type definition */ +void register_node_type_sh_subsurface_scattering() +{ + namespace file_ns = blender::nodes::node_shader_subsurface_scattering_cc; + + static bNodeType ntype; + + sh_node_type_base( + &ntype, SH_NODE_SUBSURFACE_SCATTERING, "Subsurface Scattering", NODE_CLASS_SHADER); + ntype.declare = file_ns::node_declare; + ntype.draw_buttons = file_ns::node_shader_buts_subsurface; + node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); + node_type_init(&ntype, file_ns::node_shader_init_subsurface_scattering); + node_type_gpu(&ntype, file_ns::node_shader_gpu_subsurface_scattering); + node_type_update(&ntype, file_ns::node_shader_update_subsurface_scattering); + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/shader/nodes/node_shader_tangent.c b/source/blender/nodes/shader/nodes/node_shader_tangent.cc index 7ec332bd34a..58625cacf9c 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tangent.c +++ b/source/blender/nodes/shader/nodes/node_shader_tangent.cc @@ -17,18 +17,49 @@ * All rights reserved. */ -#include "../node_shader_util.h" +#include "node_shader_util.hh" -/* **************** OUTPUT ******************** */ +#include "BKE_context.h" -static bNodeSocketTemplate sh_node_tangent_out[] = { - {SOCK_VECTOR, N_("Tangent"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {-1, ""}, -}; +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes::node_shader_tangent_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_output<decl::Vector>(N_("Tangent")); +} + +static void node_shader_buts_tangent(uiLayout *layout, bContext *C, PointerRNA *ptr) +{ + uiLayout *split, *row; + + split = uiLayoutSplit(layout, 0.0f, false); + + uiItemR(split, ptr, "direction_type", UI_ITEM_R_SPLIT_EMPTY_NAME, "", 0); + + row = uiLayoutRow(split, false); + + if (RNA_enum_get(ptr, "direction_type") == SHD_TANGENT_UVMAP) { + PointerRNA obptr = CTX_data_pointer_get(C, "active_object"); + + if (obptr.data && RNA_enum_get(&obptr, "type") == OB_MESH) { + PointerRNA dataptr = RNA_pointer_get(&obptr, "data"); + uiItemPointerR(row, ptr, "uv_map", &dataptr, "uv_layers", "", ICON_NONE); + } + else { + uiItemR(row, ptr, "uv_map", UI_ITEM_R_SPLIT_EMPTY_NAME, "", 0); + } + } + else { + uiItemR(row, ptr, "axis", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_EXPAND, nullptr, 0); + } +} static void node_shader_init_tangent(bNodeTree *UNUSED(ntree), bNode *node) { - NodeShaderTangent *attr = MEM_callocN(sizeof(NodeShaderTangent), "NodeShaderTangent"); + NodeShaderTangent *attr = MEM_cnew<NodeShaderTangent>("NodeShaderTangent"); attr->axis = SHD_TANGENT_AXIS_Z; node->storage = attr; } @@ -39,7 +70,7 @@ static int node_shader_gpu_tangent(GPUMaterial *mat, GPUNodeStack *in, GPUNodeStack *out) { - NodeShaderTangent *attr = node->storage; + NodeShaderTangent *attr = static_cast<NodeShaderTangent *>(node->storage); if (attr->direction_type == SHD_TANGENT_UVMAP) { return GPU_stack_link( @@ -61,16 +92,21 @@ static int node_shader_gpu_tangent(GPUMaterial *mat, return GPU_stack_link(mat, node, "node_tangent", in, out, orco); } +} // namespace blender::nodes::node_shader_tangent_cc + /* node type definition */ -void register_node_type_sh_tangent(void) +void register_node_type_sh_tangent() { + namespace file_ns = blender::nodes::node_shader_tangent_cc; + static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_TANGENT, "Tangent", NODE_CLASS_INPUT, 0); - node_type_socket_templates(&ntype, NULL, sh_node_tangent_out); + sh_node_type_base(&ntype, SH_NODE_TANGENT, "Tangent", NODE_CLASS_INPUT); + ntype.declare = file_ns::node_declare; + ntype.draw_buttons = file_ns::node_shader_buts_tangent; node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); - node_type_init(&ntype, node_shader_init_tangent); - node_type_gpu(&ntype, node_shader_gpu_tangent); + node_type_init(&ntype, file_ns::node_shader_init_tangent); + node_type_gpu(&ntype, file_ns::node_shader_gpu_tangent); node_type_storage( &ntype, "NodeShaderTangent", node_free_standard_storage, node_copy_standard_storage); diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_brick.c b/source/blender/nodes/shader/nodes/node_shader_tex_brick.c deleted file mode 100644 index 1b802f1dfd7..00000000000 --- a/source/blender/nodes/shader/nodes/node_shader_tex_brick.c +++ /dev/null @@ -1,181 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2005 Blender Foundation. - * All rights reserved. - */ - -#include "../node_shader_util.h" - -/* **************** OUTPUT ******************** */ - -static bNodeSocketTemplate sh_node_tex_brick_in[] = { - {SOCK_VECTOR, - N_("Vector"), - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 1.0f, - PROP_NONE, - SOCK_HIDE_VALUE | SOCK_NO_INTERNAL_LINK}, - {SOCK_RGBA, N_("Color1"), 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f}, - {SOCK_RGBA, N_("Color2"), 0.2f, 0.2f, 0.2f, 1.0f, 0.0f, 1.0f}, - {SOCK_RGBA, - N_("Mortar"), - 0.0f, - 0.0f, - 0.0f, - 1.0f, - 0.0f, - 1.0f, - PROP_NONE, - SOCK_NO_INTERNAL_LINK}, - {SOCK_FLOAT, - N_("Scale"), - 5.0f, - 0.0f, - 0.0f, - 0.0f, - -1000.0f, - 1000.0f, - PROP_NONE, - SOCK_NO_INTERNAL_LINK}, - {SOCK_FLOAT, - N_("Mortar Size"), - 0.02f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.125f, - PROP_NONE, - SOCK_NO_INTERNAL_LINK}, - {SOCK_FLOAT, - N_("Mortar Smooth"), - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 1.0f, - PROP_NONE, - SOCK_NO_INTERNAL_LINK}, - {SOCK_FLOAT, - N_("Bias"), - 0.0f, - 0.0f, - 0.0f, - 0.0f, - -1.0f, - 1.0f, - PROP_NONE, - SOCK_NO_INTERNAL_LINK}, - {SOCK_FLOAT, - N_("Brick Width"), - 0.5f, - 0.0f, - 0.0f, - 0.0f, - 0.01f, - 100.0f, - PROP_NONE, - SOCK_NO_INTERNAL_LINK}, - {SOCK_FLOAT, - N_("Row Height"), - 0.25f, - 0.0f, - 0.0f, - 0.0f, - 0.01f, - 100.0f, - PROP_NONE, - SOCK_NO_INTERNAL_LINK}, - {-1, ""}, -}; - -static bNodeSocketTemplate sh_node_tex_brick_out[] = { - {SOCK_RGBA, N_("Color"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, - N_("Fac"), - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 1.0f, - PROP_FACTOR, - SOCK_NO_INTERNAL_LINK}, - {-1, ""}, -}; - -static void node_shader_init_tex_brick(bNodeTree *UNUSED(ntree), bNode *node) -{ - NodeTexBrick *tex = MEM_callocN(sizeof(NodeTexBrick), "NodeTexBrick"); - BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT); - BKE_texture_colormapping_default(&tex->base.color_mapping); - - tex->offset = 0.5f; - tex->squash = 1.0f; - tex->offset_freq = 2; - tex->squash_freq = 2; - - node->storage = tex; - - LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { - if (STREQ(sock->name, "Mortar Smooth")) { - ((bNodeSocketValueFloat *)sock->default_value)->value = 0.1f; - } - } -} - -static int node_shader_gpu_tex_brick(GPUMaterial *mat, - bNode *node, - bNodeExecData *UNUSED(execdata), - GPUNodeStack *in, - GPUNodeStack *out) -{ - node_shader_gpu_default_tex_coord(mat, node, &in[0].link); - node_shader_gpu_tex_mapping(mat, node, in, out); - NodeTexBrick *tex = (NodeTexBrick *)node->storage; - float offset_freq = tex->offset_freq; - float squash_freq = tex->squash_freq; - return GPU_stack_link(mat, - node, - "node_tex_brick", - in, - out, - GPU_uniform(&tex->offset), - GPU_constant(&offset_freq), - GPU_uniform(&tex->squash), - GPU_constant(&squash_freq)); -} - -/* node type definition */ -void register_node_type_sh_tex_brick(void) -{ - static bNodeType ntype; - - sh_node_type_base(&ntype, SH_NODE_TEX_BRICK, "Brick Texture", NODE_CLASS_TEXTURE, 0); - node_type_socket_templates(&ntype, sh_node_tex_brick_in, sh_node_tex_brick_out); - node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); - node_type_init(&ntype, node_shader_init_tex_brick); - node_type_storage( - &ntype, "NodeTexBrick", node_free_standard_storage, node_copy_standard_storage); - node_type_gpu(&ntype, node_shader_gpu_tex_brick); - - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_brick.cc b/source/blender/nodes/shader/nodes/node_shader_tex_brick.cc new file mode 100644 index 00000000000..81a69ef18da --- /dev/null +++ b/source/blender/nodes/shader/nodes/node_shader_tex_brick.cc @@ -0,0 +1,306 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2005 Blender Foundation. + * All rights reserved. + */ + +#include "node_shader_util.hh" + +#include "BLI_math_vec_types.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes::node_shader_tex_brick_cc { + +static void sh_node_tex_brick_declare(NodeDeclarationBuilder &b) +{ + b.is_function_node(); + b.add_input<decl::Vector>(N_("Vector")).min(-10000.0f).max(10000.0f).implicit_field(); + b.add_input<decl::Color>(N_("Color1")).default_value({0.8f, 0.8f, 0.8f, 1.0f}); + b.add_input<decl::Color>(N_("Color2")).default_value({0.2f, 0.2f, 0.2f, 1.0f}); + b.add_input<decl::Color>(N_("Mortar")).default_value({0.0f, 0.0f, 0.0f, 1.0f}).no_muted_links(); + b.add_input<decl::Float>(N_("Scale")) + .min(-1000.0f) + .max(1000.0f) + .default_value(5.0f) + .no_muted_links(); + b.add_input<decl::Float>(N_("Mortar Size")) + .min(0.0f) + .max(0.125f) + .default_value(0.02f) + .no_muted_links(); + b.add_input<decl::Float>(N_("Mortar Smooth")).min(0.0f).max(1.0f).no_muted_links(); + b.add_input<decl::Float>(N_("Bias")).min(-1.0f).max(1.0f).no_muted_links(); + b.add_input<decl::Float>(N_("Brick Width")) + .min(0.01f) + .max(100.0f) + .default_value(0.5f) + .no_muted_links(); + b.add_input<decl::Float>(N_("Row Height")) + .min(0.01f) + .max(100.0f) + .default_value(0.25f) + .no_muted_links(); + b.add_output<decl::Color>(N_("Color")); + b.add_output<decl::Float>(N_("Fac")); +} + +static void node_shader_buts_tex_brick(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayout *col; + + col = uiLayoutColumn(layout, true); + uiItemR(col, + ptr, + "offset", + UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, + IFACE_("Offset"), + ICON_NONE); + uiItemR( + col, ptr, "offset_frequency", UI_ITEM_R_SPLIT_EMPTY_NAME, IFACE_("Frequency"), ICON_NONE); + + col = uiLayoutColumn(layout, true); + uiItemR(col, ptr, "squash", UI_ITEM_R_SPLIT_EMPTY_NAME, IFACE_("Squash"), ICON_NONE); + uiItemR( + col, ptr, "squash_frequency", UI_ITEM_R_SPLIT_EMPTY_NAME, IFACE_("Frequency"), ICON_NONE); +} + +static void node_shader_init_tex_brick(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeTexBrick *tex = MEM_cnew<NodeTexBrick>(__func__); + BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT); + BKE_texture_colormapping_default(&tex->base.color_mapping); + + tex->offset = 0.5f; + tex->squash = 1.0f; + tex->offset_freq = 2; + tex->squash_freq = 2; + + node->storage = tex; + + LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { + if (STREQ(sock->name, "Mortar Smooth")) { + ((bNodeSocketValueFloat *)sock->default_value)->value = 0.1f; + } + } +} + +static int node_shader_gpu_tex_brick(GPUMaterial *mat, + bNode *node, + bNodeExecData *UNUSED(execdata), + GPUNodeStack *in, + GPUNodeStack *out) +{ + node_shader_gpu_default_tex_coord(mat, node, &in[0].link); + node_shader_gpu_tex_mapping(mat, node, in, out); + NodeTexBrick *tex = (NodeTexBrick *)node->storage; + float offset_freq = tex->offset_freq; + float squash_freq = tex->squash_freq; + return GPU_stack_link(mat, + node, + "node_tex_brick", + in, + out, + GPU_uniform(&tex->offset), + GPU_constant(&offset_freq), + GPU_uniform(&tex->squash), + GPU_constant(&squash_freq)); +} + +class BrickFunction : public fn::MultiFunction { + private: + const float offset_; + const int offset_freq_; + const float squash_; + const int squash_freq_; + + public: + BrickFunction(const float offset, + const int offset_freq, + const float squash, + const int squash_freq) + : offset_(offset), offset_freq_(offset_freq), squash_(squash), squash_freq_(squash_freq) + { + static fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static fn::MFSignature create_signature() + { + fn::MFSignatureBuilder signature{"BrickTexture"}; + signature.single_input<float3>("Vector"); + signature.single_input<ColorGeometry4f>("Color1"); + signature.single_input<ColorGeometry4f>("Color2"); + signature.single_input<ColorGeometry4f>("Mortar"); + signature.single_input<float>("Scale"); + signature.single_input<float>("Mortar Size"); + signature.single_input<float>("Mortar Smooth"); + signature.single_input<float>("Bias"); + signature.single_input<float>("Brick Width"); + signature.single_input<float>("Row Height"); + signature.single_output<ColorGeometry4f>("Color"); + signature.single_output<float>("Fac"); + return signature.build(); + } + + /* Fast integer noise. */ + static float brick_noise(uint n) + { + n = (n + 1013) & 0x7fffffff; + n = (n >> 13) ^ n; + const uint nn = (n * (n * n * 60493 + 19990303) + 1376312589) & 0x7fffffff; + return 0.5f * ((float)nn / 1073741824.0f); + } + + static float smoothstepf(const float f) + { + const float ff = f * f; + return (3.0f * ff - 2.0f * ff * f); + } + + static float2 brick(float3 p, + float mortar_size, + float mortar_smooth, + float bias, + float brick_width, + float row_height, + float offset_amount, + int offset_frequency, + float squash_amount, + int squash_frequency) + { + float offset = 0.0f; + + const int rownum = (int)floorf(p.y / row_height); + + if (offset_frequency && squash_frequency) { + brick_width *= (rownum % squash_frequency) ? 1.0f : squash_amount; + offset = (rownum % offset_frequency) ? 0.0f : (brick_width * offset_amount); + } + + const int bricknum = (int)floorf((p.x + offset) / brick_width); + + const float x = (p.x + offset) - brick_width * bricknum; + const float y = p.y - row_height * rownum; + + const float tint = clamp_f( + brick_noise((rownum << 16) + (bricknum & 0xFFFF)) + bias, 0.0f, 1.0f); + float min_dist = std::min(std::min(x, y), std::min(brick_width - x, row_height - y)); + + float mortar; + if (min_dist >= mortar_size) { + mortar = 0.0f; + } + else if (mortar_smooth == 0.0f) { + mortar = 1.0f; + } + else { + min_dist = 1.0f - min_dist / mortar_size; + mortar = (min_dist < mortar_smooth) ? smoothstepf(min_dist / mortar_smooth) : 1.0f; + } + + return float2(tint, mortar); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + const VArray<float3> &vector = params.readonly_single_input<float3>(0, "Vector"); + const VArray<ColorGeometry4f> &color1_values = params.readonly_single_input<ColorGeometry4f>( + 1, "Color1"); + const VArray<ColorGeometry4f> &color2_values = params.readonly_single_input<ColorGeometry4f>( + 2, "Color2"); + const VArray<ColorGeometry4f> &mortar_values = params.readonly_single_input<ColorGeometry4f>( + 3, "Mortar"); + const VArray<float> &scale = params.readonly_single_input<float>(4, "Scale"); + const VArray<float> &mortar_size = params.readonly_single_input<float>(5, "Mortar Size"); + const VArray<float> &mortar_smooth = params.readonly_single_input<float>(6, "Mortar Smooth"); + const VArray<float> &bias = params.readonly_single_input<float>(7, "Bias"); + const VArray<float> &brick_width = params.readonly_single_input<float>(8, "Brick Width"); + const VArray<float> &row_height = params.readonly_single_input<float>(9, "Row Height"); + + MutableSpan<ColorGeometry4f> r_color = + params.uninitialized_single_output_if_required<ColorGeometry4f>(10, "Color"); + MutableSpan<float> r_fac = params.uninitialized_single_output_if_required<float>(11, "Fac"); + + const bool store_fac = !r_fac.is_empty(); + const bool store_color = !r_color.is_empty(); + + for (int64_t i : mask) { + const float2 f2 = brick(vector[i] * scale[i], + mortar_size[i], + mortar_smooth[i], + bias[i], + brick_width[i], + row_height[i], + offset_, + offset_freq_, + squash_, + squash_freq_); + + float4 color_data, color1, color2, mortar; + copy_v4_v4(color_data, color1_values[i]); + copy_v4_v4(color1, color1_values[i]); + copy_v4_v4(color2, color2_values[i]); + copy_v4_v4(mortar, mortar_values[i]); + const float tint = f2.x; + const float f = f2.y; + + if (f != 1.0f) { + const float facm = 1.0f - tint; + color_data = color1 * facm + color2 * tint; + } + + if (store_color) { + color_data = color_data * (1.0f - f) + mortar * f; + copy_v4_v4(r_color[i], color_data); + } + if (store_fac) { + r_fac[i] = f; + } + } + } +}; + +static void sh_node_brick_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder) +{ + bNode &node = builder.node(); + NodeTexBrick *tex = (NodeTexBrick *)node.storage; + + builder.construct_and_set_matching_fn<BrickFunction>( + tex->offset, tex->offset_freq, tex->squash, tex->squash_freq); +} + +} // namespace blender::nodes::node_shader_tex_brick_cc + +void register_node_type_sh_tex_brick() +{ + namespace file_ns = blender::nodes::node_shader_tex_brick_cc; + + static bNodeType ntype; + + sh_fn_node_type_base(&ntype, SH_NODE_TEX_BRICK, "Brick Texture", NODE_CLASS_TEXTURE); + ntype.declare = file_ns::sh_node_tex_brick_declare; + ntype.draw_buttons = file_ns::node_shader_buts_tex_brick; + node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); + node_type_init(&ntype, file_ns::node_shader_init_tex_brick); + node_type_storage( + &ntype, "NodeTexBrick", node_free_standard_storage, node_copy_standard_storage); + node_type_gpu(&ntype, file_ns::node_shader_gpu_tex_brick); + ntype.build_multi_function = file_ns::sh_node_brick_build_multi_function; + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_checker.c b/source/blender/nodes/shader/nodes/node_shader_tex_checker.c deleted file mode 100644 index 75219f4c3f9..00000000000 --- a/source/blender/nodes/shader/nodes/node_shader_tex_checker.c +++ /dev/null @@ -1,90 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2005 Blender Foundation. - * All rights reserved. - */ - -#include "../node_shader_util.h" - -/* **************** OUTPUT ******************** */ - -static bNodeSocketTemplate sh_node_tex_checker_in[] = { - {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, - {SOCK_RGBA, N_("Color1"), 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f}, - {SOCK_RGBA, N_("Color2"), 0.2f, 0.2f, 0.2f, 1.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, - N_("Scale"), - 5.0f, - 0.0f, - 0.0f, - 0.0f, - -1000.0f, - 1000.0f, - PROP_NONE, - SOCK_NO_INTERNAL_LINK}, - {-1, ""}, -}; - -static bNodeSocketTemplate sh_node_tex_checker_out[] = { - {SOCK_RGBA, N_("Color"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, - N_("Fac"), - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 1.0f, - PROP_FACTOR, - SOCK_NO_INTERNAL_LINK}, - {-1, ""}, -}; - -static void node_shader_init_tex_checker(bNodeTree *UNUSED(ntree), bNode *node) -{ - NodeTexChecker *tex = MEM_callocN(sizeof(NodeTexChecker), "NodeTexChecker"); - BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT); - BKE_texture_colormapping_default(&tex->base.color_mapping); - - node->storage = tex; -} - -static int node_shader_gpu_tex_checker(GPUMaterial *mat, - bNode *node, - bNodeExecData *UNUSED(execdata), - GPUNodeStack *in, - GPUNodeStack *out) -{ - node_shader_gpu_default_tex_coord(mat, node, &in[0].link); - node_shader_gpu_tex_mapping(mat, node, in, out); - - return GPU_stack_link(mat, node, "node_tex_checker", in, out); -} - -/* node type definition */ -void register_node_type_sh_tex_checker(void) -{ - static bNodeType ntype; - - sh_node_type_base(&ntype, SH_NODE_TEX_CHECKER, "Checker Texture", NODE_CLASS_TEXTURE, 0); - node_type_socket_templates(&ntype, sh_node_tex_checker_in, sh_node_tex_checker_out); - node_type_init(&ntype, node_shader_init_tex_checker); - node_type_storage( - &ntype, "NodeTexChecker", node_free_standard_storage, node_copy_standard_storage); - node_type_gpu(&ntype, node_shader_gpu_tex_checker); - - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_checker.cc b/source/blender/nodes/shader/nodes/node_shader_tex_checker.cc new file mode 100644 index 00000000000..6022f13821a --- /dev/null +++ b/source/blender/nodes/shader/nodes/node_shader_tex_checker.cc @@ -0,0 +1,137 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2005 Blender Foundation. + * All rights reserved. + */ + +#include "node_shader_util.hh" + +namespace blender::nodes::node_shader_tex_checker_cc { + +static void sh_node_tex_checker_declare(NodeDeclarationBuilder &b) +{ + b.is_function_node(); + b.add_input<decl::Vector>(N_("Vector")).min(-10000.0f).max(10000.0f).implicit_field(); + b.add_input<decl::Color>(N_("Color1")).default_value({0.8f, 0.8f, 0.8f, 1.0f}); + b.add_input<decl::Color>(N_("Color2")).default_value({0.2f, 0.2f, 0.2f, 1.0f}); + b.add_input<decl::Float>(N_("Scale")) + .min(-10000.0f) + .max(10000.0f) + .default_value(5.0f) + .no_muted_links(); + b.add_output<decl::Color>(N_("Color")); + b.add_output<decl::Float>(N_("Fac")); +} + +static void node_shader_init_tex_checker(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeTexChecker *tex = MEM_cnew<NodeTexChecker>(__func__); + BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT); + BKE_texture_colormapping_default(&tex->base.color_mapping); + + node->storage = tex; +} + +static int node_shader_gpu_tex_checker(GPUMaterial *mat, + bNode *node, + bNodeExecData *UNUSED(execdata), + GPUNodeStack *in, + GPUNodeStack *out) +{ + node_shader_gpu_default_tex_coord(mat, node, &in[0].link); + node_shader_gpu_tex_mapping(mat, node, in, out); + + return GPU_stack_link(mat, node, "node_tex_checker", in, out); +} + +class NodeTexChecker : public fn::MultiFunction { + public: + NodeTexChecker() + { + static fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static fn::MFSignature create_signature() + { + fn::MFSignatureBuilder signature{"Checker"}; + signature.single_input<float3>("Vector"); + signature.single_input<ColorGeometry4f>("Color1"); + signature.single_input<ColorGeometry4f>("Color2"); + signature.single_input<float>("Scale"); + signature.single_output<ColorGeometry4f>("Color"); + signature.single_output<float>("Fac"); + return signature.build(); + } + + void call(blender::IndexMask mask, + fn::MFParams params, + fn::MFContext UNUSED(context)) const override + { + const VArray<float3> &vector = params.readonly_single_input<float3>(0, "Vector"); + const VArray<ColorGeometry4f> &color1 = params.readonly_single_input<ColorGeometry4f>( + 1, "Color1"); + const VArray<ColorGeometry4f> &color2 = params.readonly_single_input<ColorGeometry4f>( + 2, "Color2"); + const VArray<float> &scale = params.readonly_single_input<float>(3, "Scale"); + MutableSpan<ColorGeometry4f> r_color = + params.uninitialized_single_output_if_required<ColorGeometry4f>(4, "Color"); + MutableSpan<float> r_fac = params.uninitialized_single_output<float>(5, "Fac"); + + for (int64_t i : mask) { + /* Avoid precision issues on unit coordinates. */ + const float3 p = (vector[i] * scale[i] + 0.000001f) * 0.999999f; + + const int xi = abs((int)(floorf(p.x))); + const int yi = abs((int)(floorf(p.y))); + const int zi = abs((int)(floorf(p.z))); + + r_fac[i] = ((xi % 2 == yi % 2) == (zi % 2)) ? 1.0f : 0.0f; + } + + if (!r_color.is_empty()) { + for (int64_t i : mask) { + r_color[i] = (r_fac[i] == 1.0f) ? color1[i] : color2[i]; + } + } + } +}; + +static void sh_node_tex_checker_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) +{ + static NodeTexChecker fn; + builder.set_matching_fn(fn); +} + +} // namespace blender::nodes::node_shader_tex_checker_cc + +void register_node_type_sh_tex_checker() +{ + namespace file_ns = blender::nodes::node_shader_tex_checker_cc; + + static bNodeType ntype; + + sh_fn_node_type_base(&ntype, SH_NODE_TEX_CHECKER, "Checker Texture", NODE_CLASS_TEXTURE); + ntype.declare = file_ns::sh_node_tex_checker_declare; + node_type_init(&ntype, file_ns::node_shader_init_tex_checker); + node_type_storage( + &ntype, "NodeTexChecker", node_free_standard_storage, node_copy_standard_storage); + node_type_gpu(&ntype, file_ns::node_shader_gpu_tex_checker); + ntype.build_multi_function = file_ns::sh_node_tex_checker_build_multi_function; + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_coord.c b/source/blender/nodes/shader/nodes/node_shader_tex_coord.cc index 76966e0bbee..1bbaed88ea5 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_coord.c +++ b/source/blender/nodes/shader/nodes/node_shader_tex_coord.cc @@ -17,22 +17,31 @@ * All rights reserved. */ -#include "../node_shader_util.h" +#include "node_shader_util.hh" #include "DNA_customdata_types.h" -/* **************** OUTPUT ******************** */ +#include "UI_interface.h" +#include "UI_resources.h" -static bNodeSocketTemplate sh_node_tex_coord_out[] = { - {SOCK_VECTOR, N_("Generated"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_VECTOR, N_("Normal"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_VECTOR, N_("UV"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_VECTOR, N_("Object"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_VECTOR, N_("Camera"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_VECTOR, N_("Window"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_VECTOR, N_("Reflection"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {-1, ""}, -}; +namespace blender::nodes::node_shader_tex_coord_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_output<decl::Vector>(N_("Generated")); + b.add_output<decl::Vector>(N_("Normal")); + b.add_output<decl::Vector>(N_("UV")); + b.add_output<decl::Vector>(N_("Object")); + b.add_output<decl::Vector>(N_("Camera")); + b.add_output<decl::Vector>(N_("Window")); + b.add_output<decl::Vector>(N_("Reflection")); +} + +static void node_shader_buts_tex_coord(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "object", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, 0); + uiItemR(layout, ptr, "from_instancer", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, 0); +} static int node_shader_gpu_tex_coord(GPUMaterial *mat, bNode *node, @@ -42,25 +51,27 @@ static int node_shader_gpu_tex_coord(GPUMaterial *mat, { Object *ob = (Object *)node->id; - /* 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_builtin(GPU_INVERSE_OBJECT_MATRIX); /* Opti: don't request orco if not needed. */ - GPUNodeLink *orco = (!out[0].hasoutput) ? GPU_constant((float[4]){0.0f, 0.0f, 0.0f, 0.0f}) : + const float default_coords[4] = {0.0f, 0.0f, 0.0f, 0.0f}; + GPUNodeLink *orco = (!out[0].hasoutput) ? GPU_constant(default_coords) : GPU_attribute(mat, CD_ORCO, ""); GPUNodeLink *mtface = GPU_attribute(mat, CD_MTFACE, ""); + GPUNodeLink *viewpos = GPU_builtin(GPU_VIEW_POSITION); + GPUNodeLink *worldnor = GPU_builtin(GPU_WORLD_NORMAL); + GPUNodeLink *texcofacs = GPU_builtin(GPU_CAMERA_TEXCO_FACTORS); if (out[0].hasoutput) { GPU_link(mat, "generated_from_orco", orco, &orco); } - GPU_stack_link(mat, node, "node_tex_coord", in, out, inv_obmat, orco, mtface); + GPU_stack_link( + mat, node, "node_tex_coord", in, out, viewpos, worldnor, inv_obmat, texcofacs, orco, mtface); - /* for each output. */ - for (int i = 0; sh_node_tex_coord_out[i].type != -1; i++) { + int i; + LISTBASE_FOREACH_INDEX (bNodeSocket *, sock, &node->outputs, i) { node_shader_gpu_bump_tex_coord(mat, node, &out[i].link); /* Normalize some vectors after dFdx/dFdy offsets. * This is the case for interpolated, non linear functions. @@ -74,23 +85,26 @@ static int node_shader_gpu_tex_coord(GPUMaterial *mat, out[i].link, out[i].link, &out[i].link, - NULL); + nullptr); } } return 1; } +} // namespace blender::nodes::node_shader_tex_coord_cc + /* node type definition */ -void register_node_type_sh_tex_coord(void) +void register_node_type_sh_tex_coord() { + namespace file_ns = blender::nodes::node_shader_tex_coord_cc; + static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_TEX_COORD, "Texture Coordinate", NODE_CLASS_INPUT, 0); - node_type_socket_templates(&ntype, NULL, sh_node_tex_coord_out); - node_type_init(&ntype, NULL); - node_type_storage(&ntype, "", NULL, NULL); - node_type_gpu(&ntype, node_shader_gpu_tex_coord); + sh_node_type_base(&ntype, SH_NODE_TEX_COORD, "Texture Coordinate", NODE_CLASS_INPUT); + ntype.declare = file_ns::node_declare; + ntype.draw_buttons = file_ns::node_shader_buts_tex_coord; + node_type_gpu(&ntype, file_ns::node_shader_gpu_tex_coord); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_environment.c b/source/blender/nodes/shader/nodes/node_shader_tex_environment.cc index 51ced8c2842..24ecdee12d8 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_environment.c +++ b/source/blender/nodes/shader/nodes/node_shader_tex_environment.cc @@ -17,23 +17,19 @@ * All rights reserved. */ -#include "../node_shader_util.h" +#include "node_shader_util.hh" -/* **************** OUTPUT ******************** */ +namespace blender::nodes::node_shader_tex_environment_cc { -static bNodeSocketTemplate sh_node_tex_environment_in[] = { - {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, - {-1, ""}, -}; - -static bNodeSocketTemplate sh_node_tex_environment_out[] = { - {SOCK_RGBA, N_("Color"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_NO_INTERNAL_LINK}, - {-1, ""}, -}; +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Vector>(N_("Vector")).hide_value(); + b.add_output<decl::Color>(N_("Color")).no_muted_links(); +} static void node_shader_init_tex_environment(bNodeTree *UNUSED(ntree), bNode *node) { - NodeTexEnvironment *tex = MEM_callocN(sizeof(NodeTexEnvironment), "NodeTexEnvironment"); + NodeTexEnvironment *tex = MEM_cnew<NodeTexEnvironment>("NodeTexEnvironment"); BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT); BKE_texture_colormapping_default(&tex->base.color_mapping); tex->projection = SHD_PROJ_EQUIRECTANGULAR; @@ -49,12 +45,12 @@ static int node_shader_gpu_tex_environment(GPUMaterial *mat, GPUNodeStack *out) { Image *ima = (Image *)node->id; - NodeTexEnvironment *tex = node->storage; + NodeTexEnvironment *tex = (NodeTexEnvironment *)node->storage; /* We get the image user from the original node, since GPU image keeps * a pointer to it and the dependency refreshes the original. */ bNode *node_original = node->original ? node->original : node; - NodeTexImage *tex_original = node_original->storage; + NodeTexImage *tex_original = (NodeTexImage *)node_original->storage; ImageUser *iuser = &tex_original->iuser; eGPUSamplerState sampler = GPU_SAMPLER_REPEAT | GPU_SAMPLER_ANISO | GPU_SAMPLER_FILTER; /* TODO(fclem): For now assume mipmap is always enabled. */ @@ -130,18 +126,22 @@ static int node_shader_gpu_tex_environment(GPUMaterial *mat, return true; } +} // namespace blender::nodes::node_shader_tex_environment_cc + /* node type definition */ -void register_node_type_sh_tex_environment(void) +void register_node_type_sh_tex_environment() { + namespace file_ns = blender::nodes::node_shader_tex_environment_cc; + static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_TEX_ENVIRONMENT, "Environment Texture", NODE_CLASS_TEXTURE, 0); - node_type_socket_templates(&ntype, sh_node_tex_environment_in, sh_node_tex_environment_out); - node_type_init(&ntype, node_shader_init_tex_environment); + sh_node_type_base(&ntype, SH_NODE_TEX_ENVIRONMENT, "Environment Texture", NODE_CLASS_TEXTURE); + ntype.declare = file_ns::node_declare; + node_type_init(&ntype, file_ns::node_shader_init_tex_environment); node_type_storage( &ntype, "NodeTexEnvironment", node_free_standard_storage, node_copy_standard_storage); - node_type_gpu(&ntype, node_shader_gpu_tex_environment); - node_type_label(&ntype, node_image_label); + node_type_gpu(&ntype, file_ns::node_shader_gpu_tex_environment); + ntype.labelfunc = node_image_label; node_type_size_preset(&ntype, NODE_SIZE_LARGE); nodeRegisterType(&ntype); 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 e0520ee49d3..53be5bc09d9 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_gradient.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_gradient.cc @@ -17,23 +17,29 @@ * All rights reserved. */ -#include "../node_shader_util.h" +#include "node_shader_util.hh" -namespace blender::nodes { +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes::node_shader_tex_gradient_cc { static void sh_node_tex_gradient_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Vector>("Vector").hide_value(); - b.add_output<decl::Color>("Color").no_muted_links(); - b.add_output<decl::Float>("Fac").no_muted_links(); -}; + b.is_function_node(); + b.add_input<decl::Vector>(N_("Vector")).hide_value().implicit_field(); + b.add_output<decl::Color>(N_("Color")).no_muted_links(); + b.add_output<decl::Float>(N_("Fac")).no_muted_links(); +} -} // namespace blender::nodes +static void node_shader_buts_tex_gradient(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "gradient_type", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); +} static void node_shader_init_tex_gradient(bNodeTree *UNUSED(ntree), bNode *node) { - NodeTexGradient *tex = (NodeTexGradient *)MEM_callocN(sizeof(NodeTexGradient), - "NodeTexGradient"); + NodeTexGradient *tex = MEM_cnew<NodeTexGradient>(__func__); BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT); BKE_texture_colormapping_default(&tex->base.color_mapping); tex->gradient_type = SHD_BLEND_LINEAR; @@ -55,17 +61,122 @@ static int node_shader_gpu_tex_gradient(GPUMaterial *mat, return GPU_stack_link(mat, node, "node_tex_gradient", in, out, GPU_constant(&gradient_type)); } -/* node type definition */ -void register_node_type_sh_tex_gradient(void) +class GradientFunction : public fn::MultiFunction { + private: + int gradient_type_; + + public: + GradientFunction(int gradient_type) : gradient_type_(gradient_type) + { + static fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static fn::MFSignature create_signature() + { + fn::MFSignatureBuilder signature{"GradientFunction"}; + signature.single_input<float3>("Vector"); + signature.single_output<ColorGeometry4f>("Color"); + signature.single_output<float>("Fac"); + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + const VArray<float3> &vector = params.readonly_single_input<float3>(0, "Vector"); + + MutableSpan<ColorGeometry4f> r_color = + params.uninitialized_single_output_if_required<ColorGeometry4f>(1, "Color"); + MutableSpan<float> fac = params.uninitialized_single_output<float>(2, "Fac"); + + const bool compute_color = !r_color.is_empty(); + + switch (gradient_type_) { + case SHD_BLEND_LINEAR: { + for (int64_t i : mask) { + fac[i] = vector[i].x; + } + break; + } + case SHD_BLEND_QUADRATIC: { + for (int64_t i : mask) { + const float r = std::max(vector[i].x, 0.0f); + fac[i] = r * r; + } + break; + } + case SHD_BLEND_EASING: { + for (int64_t i : mask) { + const float r = std::min(std::max(vector[i].x, 0.0f), 1.0f); + const float t = r * r; + fac[i] = (3.0f * t - 2.0f * t * r); + } + break; + } + case SHD_BLEND_DIAGONAL: { + for (int64_t i : mask) { + fac[i] = (vector[i].x + vector[i].y) * 0.5f; + } + break; + } + case SHD_BLEND_RADIAL: { + for (int64_t i : mask) { + fac[i] = atan2f(vector[i].y, vector[i].x) / (M_PI * 2.0f) + 0.5f; + } + break; + } + case SHD_BLEND_QUADRATIC_SPHERE: { + for (int64_t i : mask) { + /* Bias a little bit for the case where input is a unit length vector, + * to get exactly zero instead of a small random value depending + * on float precision. */ + const float r = std::max(0.999999f - math::length(vector[i]), 0.0f); + fac[i] = r * r; + } + break; + } + case SHD_BLEND_SPHERICAL: { + for (int64_t i : mask) { + /* Bias a little bit for the case where input is a unit length vector, + * to get exactly zero instead of a small random value depending + * on float precision. */ + fac[i] = std::max(0.999999f - math::length(vector[i]), 0.0f); + } + break; + } + } + if (compute_color) { + for (int64_t i : mask) { + r_color[i] = ColorGeometry4f(fac[i], fac[i], fac[i], 1.0f); + } + } + } +}; + +static void sh_node_gradient_tex_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) +{ + bNode &node = builder.node(); + NodeTexGradient *tex = (NodeTexGradient *)node.storage; + builder.construct_and_set_matching_fn<GradientFunction>(tex->gradient_type); +} + +} // namespace blender::nodes::node_shader_tex_gradient_cc + +void register_node_type_sh_tex_gradient() { + namespace file_ns = blender::nodes::node_shader_tex_gradient_cc; + static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_TEX_GRADIENT, "Gradient Texture", NODE_CLASS_TEXTURE, 0); - ntype.declare = blender::nodes::sh_node_tex_gradient_declare; - node_type_init(&ntype, node_shader_init_tex_gradient); + sh_fn_node_type_base(&ntype, SH_NODE_TEX_GRADIENT, "Gradient Texture", NODE_CLASS_TEXTURE); + ntype.declare = file_ns::sh_node_tex_gradient_declare; + ntype.draw_buttons = file_ns::node_shader_buts_tex_gradient; + node_type_init(&ntype, file_ns::node_shader_init_tex_gradient); node_type_storage( &ntype, "NodeTexGradient", node_free_standard_storage, node_copy_standard_storage); - node_type_gpu(&ntype, node_shader_gpu_tex_gradient); + node_type_gpu(&ntype, file_ns::node_shader_gpu_tex_gradient); + ntype.build_multi_function = file_ns::sh_node_gradient_tex_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_image.c b/source/blender/nodes/shader/nodes/node_shader_tex_image.cc index bf7c4edc1c9..7c7154b8f7a 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_image.c +++ b/source/blender/nodes/shader/nodes/node_shader_tex_image.cc @@ -17,33 +17,21 @@ * All rights reserved. */ -#include "../node_shader_util.h" - -/* **************** OUTPUT ******************** */ - -static bNodeSocketTemplate sh_node_tex_image_in[] = { - {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, - {-1, ""}, -}; - -static bNodeSocketTemplate sh_node_tex_image_out[] = { - {SOCK_RGBA, N_("Color"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_NO_INTERNAL_LINK}, - {SOCK_FLOAT, - N_("Alpha"), - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 1.0f, - PROP_NONE, - SOCK_NO_INTERNAL_LINK}, - {-1, ""}, -}; +#include "node_shader_util.hh" + +namespace blender::nodes::node_shader_tex_image_cc { + +static void sh_node_tex_image_declare(NodeDeclarationBuilder &b) +{ + b.is_function_node(); + b.add_input<decl::Vector>(N_("Vector")).implicit_field(); + b.add_output<decl::Color>(N_("Color")).no_muted_links(); + b.add_output<decl::Float>(N_("Alpha")).no_muted_links(); +} static void node_shader_init_tex_image(bNodeTree *UNUSED(ntree), bNode *node) { - NodeTexImage *tex = MEM_callocN(sizeof(NodeTexImage), "NodeTexImage"); + NodeTexImage *tex = MEM_cnew<NodeTexImage>(__func__); BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT); BKE_texture_colormapping_default(&tex->base.color_mapping); BKE_imageuser_default(&tex->iuser); @@ -58,12 +46,12 @@ static int node_shader_gpu_tex_image(GPUMaterial *mat, GPUNodeStack *out) { Image *ima = (Image *)node->id; - NodeTexImage *tex = node->storage; + NodeTexImage *tex = (NodeTexImage *)node->storage; /* We get the image user from the original node, since GPU image keeps * a pointer to it and the dependency refreshes the original. */ bNode *node_original = node->original ? node->original : node; - NodeTexImage *tex_original = node_original->storage; + NodeTexImage *tex_original = (NodeTexImage *)node_original->storage; ImageUser *iuser = &tex_original->iuser; if (!ima) { @@ -78,7 +66,7 @@ static int node_shader_gpu_tex_image(GPUMaterial *mat, node_shader_gpu_tex_mapping(mat, node, in, out); - eGPUSamplerState sampler_state = 0; + eGPUSamplerState sampler_state = GPU_SAMPLER_DEFAULT; switch (tex->extension) { case SHD_IMAGE_EXTENSION_REPEAT: @@ -94,7 +82,7 @@ static int node_shader_gpu_tex_image(GPUMaterial *mat, if (tex->interpolation != SHD_INTERP_CLOSEST) { sampler_state |= GPU_SAMPLER_ANISO | GPU_SAMPLER_FILTER; /* TODO(fclem): For now assume mipmap is always enabled. */ - sampler_state |= true ? GPU_SAMPLER_MIPMAP : 0; + sampler_state |= GPU_SAMPLER_MIPMAP; } const bool use_cubic = ELEM(tex->interpolation, SHD_INTERP_CUBIC, SHD_INTERP_SMART); @@ -181,18 +169,21 @@ static int node_shader_gpu_tex_image(GPUMaterial *mat, return true; } -/* node type definition */ -void register_node_type_sh_tex_image(void) +} // namespace blender::nodes::node_shader_tex_image_cc + +void register_node_type_sh_tex_image() { + namespace file_ns = blender::nodes::node_shader_tex_image_cc; + static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_TEX_IMAGE, "Image Texture", NODE_CLASS_TEXTURE, 0); - node_type_socket_templates(&ntype, sh_node_tex_image_in, sh_node_tex_image_out); - node_type_init(&ntype, node_shader_init_tex_image); + sh_node_type_base(&ntype, SH_NODE_TEX_IMAGE, "Image Texture", NODE_CLASS_TEXTURE); + ntype.declare = file_ns::sh_node_tex_image_declare; + node_type_init(&ntype, file_ns::node_shader_init_tex_image); node_type_storage( &ntype, "NodeTexImage", node_free_standard_storage, node_copy_standard_storage); - node_type_gpu(&ntype, node_shader_gpu_tex_image); - node_type_label(&ntype, node_image_label); + node_type_gpu(&ntype, file_ns::node_shader_gpu_tex_image); + ntype.labelfunc = node_image_label; node_type_size_preset(&ntype, NODE_SIZE_LARGE); nodeRegisterType(&ntype); diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_magic.c b/source/blender/nodes/shader/nodes/node_shader_tex_magic.c deleted file mode 100644 index 51721f8bb09..00000000000 --- a/source/blender/nodes/shader/nodes/node_shader_tex_magic.c +++ /dev/null @@ -1,84 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2005 Blender Foundation. - * All rights reserved. - */ - -#include "../node_shader_util.h" - -/* **************** OUTPUT ******************** */ - -static bNodeSocketTemplate sh_node_tex_magic_in[] = { - {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, - {SOCK_FLOAT, N_("Scale"), 5.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f}, - {SOCK_FLOAT, N_("Distortion"), 1.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f}, - {-1, ""}, -}; - -static bNodeSocketTemplate sh_node_tex_magic_out[] = { - {SOCK_RGBA, N_("Color"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_NO_INTERNAL_LINK}, - {SOCK_FLOAT, - N_("Fac"), - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 1.0f, - PROP_FACTOR, - SOCK_NO_INTERNAL_LINK}, - {-1, ""}, -}; - -static void node_shader_init_tex_magic(bNodeTree *UNUSED(ntree), bNode *node) -{ - NodeTexMagic *tex = MEM_callocN(sizeof(NodeTexMagic), "NodeTexMagic"); - BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT); - BKE_texture_colormapping_default(&tex->base.color_mapping); - tex->depth = 2; - - node->storage = tex; -} - -static int node_shader_gpu_tex_magic(GPUMaterial *mat, - bNode *node, - bNodeExecData *UNUSED(execdata), - GPUNodeStack *in, - GPUNodeStack *out) -{ - NodeTexMagic *tex = (NodeTexMagic *)node->storage; - float depth = tex->depth; - - node_shader_gpu_default_tex_coord(mat, node, &in[0].link); - node_shader_gpu_tex_mapping(mat, node, in, out); - - return GPU_stack_link(mat, node, "node_tex_magic", in, out, GPU_constant(&depth)); -} - -/* node type definition */ -void register_node_type_sh_tex_magic(void) -{ - static bNodeType ntype; - - sh_node_type_base(&ntype, SH_NODE_TEX_MAGIC, "Magic Texture", NODE_CLASS_TEXTURE, 0); - node_type_socket_templates(&ntype, sh_node_tex_magic_in, sh_node_tex_magic_out); - node_type_init(&ntype, node_shader_init_tex_magic); - node_type_storage( - &ntype, "NodeTexMagic", node_free_standard_storage, node_copy_standard_storage); - node_type_gpu(&ntype, node_shader_gpu_tex_magic); - - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_magic.cc b/source/blender/nodes/shader/nodes/node_shader_tex_magic.cc new file mode 100644 index 00000000000..e40914783b6 --- /dev/null +++ b/source/blender/nodes/shader/nodes/node_shader_tex_magic.cc @@ -0,0 +1,204 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2005 Blender Foundation. + * All rights reserved. + */ + +#include "node_shader_util.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes::node_shader_tex_magic_cc { + +static void sh_node_tex_magic_declare(NodeDeclarationBuilder &b) +{ + b.is_function_node(); + b.add_input<decl::Vector>(N_("Vector")).implicit_field(); + b.add_input<decl::Float>(N_("Scale")).min(-1000.0f).max(1000.0f).default_value(5.0f); + b.add_input<decl::Float>(N_("Distortion")).min(-1000.0f).max(1000.0f).default_value(1.0f); + b.add_output<decl::Color>(N_("Color")).no_muted_links(); + b.add_output<decl::Float>(N_("Fac")).no_muted_links(); +} + +static void node_shader_buts_tex_magic(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "turbulence_depth", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); +} + +static void node_shader_init_tex_magic(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeTexMagic *tex = MEM_cnew<NodeTexMagic>(__func__); + BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT); + BKE_texture_colormapping_default(&tex->base.color_mapping); + tex->depth = 2; + + node->storage = tex; +} + +static int node_shader_gpu_tex_magic(GPUMaterial *mat, + bNode *node, + bNodeExecData *UNUSED(execdata), + GPUNodeStack *in, + GPUNodeStack *out) +{ + NodeTexMagic *tex = (NodeTexMagic *)node->storage; + float depth = tex->depth; + + node_shader_gpu_default_tex_coord(mat, node, &in[0].link); + node_shader_gpu_tex_mapping(mat, node, in, out); + + return GPU_stack_link(mat, node, "node_tex_magic", in, out, GPU_constant(&depth)); +} + +class MagicFunction : public fn::MultiFunction { + private: + int depth_; + + public: + MagicFunction(int depth) : depth_(depth) + { + static fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static fn::MFSignature create_signature() + { + fn::MFSignatureBuilder signature{"MagicFunction"}; + signature.single_input<float3>("Vector"); + signature.single_input<float>("Scale"); + signature.single_input<float>("Distortion"); + signature.single_output<ColorGeometry4f>("Color"); + signature.single_output<float>("Fac"); + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + const VArray<float3> &vector = params.readonly_single_input<float3>(0, "Vector"); + const VArray<float> &scale = params.readonly_single_input<float>(1, "Scale"); + const VArray<float> &distortion = params.readonly_single_input<float>(2, "Distortion"); + + MutableSpan<ColorGeometry4f> r_color = params.uninitialized_single_output<ColorGeometry4f>( + 3, "Color"); + MutableSpan<float> r_fac = params.uninitialized_single_output_if_required<float>(4, "Fac"); + + const bool compute_factor = !r_fac.is_empty(); + + for (int64_t i : mask) { + const float3 co = vector[i] * scale[i]; + const float distort = distortion[i]; + float x = sinf((co[0] + co[1] + co[2]) * 5.0f); + float y = cosf((-co[0] + co[1] - co[2]) * 5.0f); + float z = -cosf((-co[0] - co[1] + co[2]) * 5.0f); + + if (depth_ > 0) { + x *= distort; + y *= distort; + z *= distort; + y = -cosf(x - y + z); + y *= distort; + + if (depth_ > 1) { + x = cosf(x - y - z); + x *= distort; + + if (depth_ > 2) { + z = sinf(-x - y - z); + z *= distort; + + if (depth_ > 3) { + x = -cosf(-x + y - z); + x *= distort; + + if (depth_ > 4) { + y = -sinf(-x + y + z); + y *= distort; + + if (depth_ > 5) { + y = -cosf(-x + y + z); + y *= distort; + + if (depth_ > 6) { + x = cosf(x + y + z); + x *= distort; + + if (depth_ > 7) { + z = sinf(x + y - z); + z *= distort; + + if (depth_ > 8) { + x = -cosf(-x - y + z); + x *= distort; + + if (depth_ > 9) { + y = -sinf(x - y + z); + y *= distort; + } + } + } + } + } + } + } + } + } + } + + if (distort != 0.0f) { + const float d = distort * 2.0f; + x /= d; + y /= d; + z /= d; + } + + r_color[i] = ColorGeometry4f(0.5f - x, 0.5f - y, 0.5f - z, 1.0f); + } + if (compute_factor) { + for (int64_t i : mask) { + r_fac[i] = (r_color[i].r + r_color[i].g + r_color[i].b) * (1.0f / 3.0f); + } + } + } +}; + +static void sh_node_magic_tex_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) +{ + bNode &node = builder.node(); + NodeTexMagic *tex = (NodeTexMagic *)node.storage; + builder.construct_and_set_matching_fn<MagicFunction>(tex->depth); +} + +} // namespace blender::nodes::node_shader_tex_magic_cc + +void register_node_type_sh_tex_magic() +{ + namespace file_ns = blender::nodes::node_shader_tex_magic_cc; + + static bNodeType ntype; + + sh_fn_node_type_base(&ntype, SH_NODE_TEX_MAGIC, "Magic Texture", NODE_CLASS_TEXTURE); + ntype.declare = file_ns::sh_node_tex_magic_declare; + ntype.draw_buttons = file_ns::node_shader_buts_tex_magic; + node_type_init(&ntype, file_ns::node_shader_init_tex_magic); + node_type_storage( + &ntype, "NodeTexMagic", node_free_standard_storage, node_copy_standard_storage); + node_type_gpu(&ntype, file_ns::node_shader_gpu_tex_magic); + ntype.build_multi_function = file_ns::sh_node_magic_tex_build_multi_function; + + nodeRegisterType(&ntype); +} 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 23f150d8135..45c2a83c178 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc @@ -17,30 +17,43 @@ * All rights reserved. */ -#include "../node_shader_util.h" +#include "node_shader_util.hh" -namespace blender::nodes { +#include "BLI_noise.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes::node_shader_tex_musgrave_cc { + +NODE_STORAGE_FUNCS(NodeTexMusgrave) static void sh_node_tex_musgrave_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input<decl::Vector>("Vector").hide_value(); - b.add_input<decl::Float>("W").min(-1000.0f).max(1000.0f); - b.add_input<decl::Float>("Scale").min(-1000.0f).max(1000.0f).default_value(5.0f); - b.add_input<decl::Float>("Detail").min(0.0f).max(16.0f).default_value(2.0f); - b.add_input<decl::Float>("Dimension").min(0.0f).max(1000.0f).default_value(2.0f); - b.add_input<decl::Float>("Lacunarity").min(0.0f).max(1000.0f).default_value(2.0f); - b.add_input<decl::Float>("Offset").min(-1000.0f).max(1000.0f); - b.add_input<decl::Float>("Gain").min(0.0f).max(1000.0f).default_value(1.0f); - b.add_output<decl::Float>("Fac").no_muted_links(); -}; + b.add_input<decl::Vector>(N_("Vector")).hide_value().implicit_field(); + b.add_input<decl::Float>(N_("W")).min(-1000.0f).max(1000.0f).make_available([](bNode &node) { + /* Default to 1 instead of 4, because it is much faster. */ + node_storage(node).dimensions = 1; + }); + b.add_input<decl::Float>(N_("Scale")).min(-1000.0f).max(1000.0f).default_value(5.0f); + b.add_input<decl::Float>(N_("Detail")).min(0.0f).max(15.0f).default_value(2.0f); + b.add_input<decl::Float>(N_("Dimension")).min(0.0f).max(1000.0f).default_value(2.0f); + b.add_input<decl::Float>(N_("Lacunarity")).min(0.0f).max(1000.0f).default_value(2.0f); + b.add_input<decl::Float>(N_("Offset")).min(-1000.0f).max(1000.0f); + b.add_input<decl::Float>(N_("Gain")).min(0.0f).max(1000.0f).default_value(1.0f); + b.add_output<decl::Float>(N_("Fac")).no_muted_links(); +} -} // namespace blender::nodes +static void node_shader_buts_tex_musgrave(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "musgrave_dimensions", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); + uiItemR(layout, ptr, "musgrave_type", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); +} static void node_shader_init_tex_musgrave(bNodeTree *UNUSED(ntree), bNode *node) { - NodeTexMusgrave *tex = (NodeTexMusgrave *)MEM_callocN(sizeof(NodeTexMusgrave), - "NodeTexMusgrave"); + NodeTexMusgrave *tex = MEM_cnew<NodeTexMusgrave>(__func__); BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT); BKE_texture_colormapping_default(&tex->base.color_mapping); tex->musgrave_type = SHD_MUSGRAVE_FBM; @@ -102,40 +115,447 @@ static int node_shader_gpu_tex_musgrave(GPUMaterial *mat, return GPU_stack_link(mat, node, name, in, out); } -static void node_shader_update_tex_musgrave(bNodeTree *UNUSED(ntree), bNode *node) +static void node_shader_update_tex_musgrave(bNodeTree *ntree, bNode *node) { - NodeTexMusgrave *tex = (NodeTexMusgrave *)node->storage; + const NodeTexMusgrave &storage = node_storage(*node); bNodeSocket *inVectorSock = nodeFindSocket(node, SOCK_IN, "Vector"); bNodeSocket *inWSock = nodeFindSocket(node, SOCK_IN, "W"); bNodeSocket *inOffsetSock = nodeFindSocket(node, SOCK_IN, "Offset"); bNodeSocket *inGainSock = nodeFindSocket(node, SOCK_IN, "Gain"); - nodeSetSocketAvailability(inVectorSock, tex->dimensions != 1); - nodeSetSocketAvailability(inWSock, tex->dimensions == 1 || tex->dimensions == 4); - nodeSetSocketAvailability(inOffsetSock, - tex->musgrave_type != SHD_MUSGRAVE_MULTIFRACTAL && - tex->musgrave_type != SHD_MUSGRAVE_FBM); - nodeSetSocketAvailability(inGainSock, - tex->musgrave_type == SHD_MUSGRAVE_HYBRID_MULTIFRACTAL || - tex->musgrave_type == SHD_MUSGRAVE_RIDGED_MULTIFRACTAL); + nodeSetSocketAvailability(ntree, inVectorSock, storage.dimensions != 1); + nodeSetSocketAvailability(ntree, inWSock, storage.dimensions == 1 || storage.dimensions == 4); + nodeSetSocketAvailability(ntree, + inOffsetSock, + storage.musgrave_type != SHD_MUSGRAVE_MULTIFRACTAL && + storage.musgrave_type != SHD_MUSGRAVE_FBM); + nodeSetSocketAvailability(ntree, + inGainSock, + storage.musgrave_type == SHD_MUSGRAVE_HYBRID_MULTIFRACTAL || + storage.musgrave_type == SHD_MUSGRAVE_RIDGED_MULTIFRACTAL); bNodeSocket *outFacSock = nodeFindSocket(node, SOCK_OUT, "Fac"); node_sock_label(outFacSock, "Height"); } -void register_node_type_sh_tex_musgrave(void) +class MusgraveFunction : public fn::MultiFunction { + private: + const int dimensions_; + const int musgrave_type_; + + public: + MusgraveFunction(const int dimensions, const int musgrave_type) + : dimensions_(dimensions), musgrave_type_(musgrave_type) + { + BLI_assert(dimensions >= 1 && dimensions <= 4); + BLI_assert(musgrave_type >= 0 && musgrave_type <= 4); + static std::array<fn::MFSignature, 20> signatures{ + create_signature(1, SHD_MUSGRAVE_MULTIFRACTAL), + create_signature(2, SHD_MUSGRAVE_MULTIFRACTAL), + create_signature(3, SHD_MUSGRAVE_MULTIFRACTAL), + create_signature(4, SHD_MUSGRAVE_MULTIFRACTAL), + + create_signature(1, SHD_MUSGRAVE_FBM), + create_signature(2, SHD_MUSGRAVE_FBM), + create_signature(3, SHD_MUSGRAVE_FBM), + create_signature(4, SHD_MUSGRAVE_FBM), + + create_signature(1, SHD_MUSGRAVE_HYBRID_MULTIFRACTAL), + create_signature(2, SHD_MUSGRAVE_HYBRID_MULTIFRACTAL), + create_signature(3, SHD_MUSGRAVE_HYBRID_MULTIFRACTAL), + create_signature(4, SHD_MUSGRAVE_HYBRID_MULTIFRACTAL), + + create_signature(1, SHD_MUSGRAVE_RIDGED_MULTIFRACTAL), + create_signature(2, SHD_MUSGRAVE_RIDGED_MULTIFRACTAL), + create_signature(3, SHD_MUSGRAVE_RIDGED_MULTIFRACTAL), + create_signature(4, SHD_MUSGRAVE_RIDGED_MULTIFRACTAL), + + create_signature(1, SHD_MUSGRAVE_HETERO_TERRAIN), + create_signature(2, SHD_MUSGRAVE_HETERO_TERRAIN), + create_signature(3, SHD_MUSGRAVE_HETERO_TERRAIN), + create_signature(4, SHD_MUSGRAVE_HETERO_TERRAIN), + }; + this->set_signature(&signatures[dimensions + musgrave_type * 4 - 1]); + } + + static fn::MFSignature create_signature(const int dimensions, const int musgrave_type) + { + fn::MFSignatureBuilder signature{"Musgrave"}; + + if (ELEM(dimensions, 2, 3, 4)) { + signature.single_input<float3>("Vector"); + } + if (ELEM(dimensions, 1, 4)) { + signature.single_input<float>("W"); + } + signature.single_input<float>("Scale"); + signature.single_input<float>("Detail"); + signature.single_input<float>("Dimension"); + signature.single_input<float>("Lacunarity"); + if (ELEM(musgrave_type, + SHD_MUSGRAVE_RIDGED_MULTIFRACTAL, + SHD_MUSGRAVE_HYBRID_MULTIFRACTAL, + SHD_MUSGRAVE_HETERO_TERRAIN)) { + signature.single_input<float>("Offset"); + } + if (ELEM(musgrave_type, SHD_MUSGRAVE_RIDGED_MULTIFRACTAL, SHD_MUSGRAVE_HYBRID_MULTIFRACTAL)) { + signature.single_input<float>("Gain"); + } + + signature.single_output<float>("Fac"); + + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + auto get_vector = [&](int param_index) -> VArray<float3> { + return params.readonly_single_input<float3>(param_index, "Vector"); + }; + auto get_w = [&](int param_index) -> VArray<float> { + return params.readonly_single_input<float>(param_index, "W"); + }; + auto get_scale = [&](int param_index) -> VArray<float> { + return params.readonly_single_input<float>(param_index, "Scale"); + }; + auto get_detail = [&](int param_index) -> VArray<float> { + return params.readonly_single_input<float>(param_index, "Detail"); + }; + auto get_dimension = [&](int param_index) -> VArray<float> { + return params.readonly_single_input<float>(param_index, "Dimension"); + }; + auto get_lacunarity = [&](int param_index) -> VArray<float> { + return params.readonly_single_input<float>(param_index, "Lacunarity"); + }; + auto get_offset = [&](int param_index) -> VArray<float> { + return params.readonly_single_input<float>(param_index, "Offset"); + }; + auto get_gain = [&](int param_index) -> VArray<float> { + return params.readonly_single_input<float>(param_index, "Gain"); + }; + + auto get_r_factor = [&](int param_index) -> MutableSpan<float> { + return params.uninitialized_single_output_if_required<float>(param_index, "Fac"); + }; + + int param = ELEM(dimensions_, 2, 3, 4) + ELEM(dimensions_, 1, 4); + const VArray<float> &scale = get_scale(param++); + const VArray<float> &detail = get_detail(param++); + const VArray<float> &dimension = get_dimension(param++); + const VArray<float> &lacunarity = get_lacunarity(param++); + + switch (musgrave_type_) { + case SHD_MUSGRAVE_MULTIFRACTAL: { + MutableSpan<float> r_factor = get_r_factor(param++); + const bool compute_factor = !r_factor.is_empty(); + switch (dimensions_) { + case 1: { + const VArray<float> &w = get_w(0); + if (compute_factor) { + for (int64_t i : mask) { + const float position = w[i] * scale[i]; + r_factor[i] = noise::musgrave_multi_fractal( + position, dimension[i], lacunarity[i], detail[i]); + } + } + break; + } + case 2: { + const VArray<float3> &vector = get_vector(0); + if (compute_factor) { + for (int64_t i : mask) { + const float3 pxyz = vector[i] * scale[i]; + const float2 position = float2(pxyz[0], pxyz[1]); + r_factor[i] = noise::musgrave_multi_fractal( + position, dimension[i], lacunarity[i], detail[i]); + } + } + break; + } + case 3: { + const VArray<float3> &vector = get_vector(0); + if (compute_factor) { + for (int64_t i : mask) { + const float3 position = vector[i] * scale[i]; + r_factor[i] = noise::musgrave_multi_fractal( + position, dimension[i], lacunarity[i], detail[i]); + } + } + break; + } + case 4: { + const VArray<float3> &vector = get_vector(0); + const VArray<float> &w = get_w(1); + if (compute_factor) { + for (int64_t i : mask) { + const float3 pxyz = vector[i] * scale[i]; + const float pw = w[i] * scale[i]; + const float4 position{pxyz[0], pxyz[1], pxyz[2], pw}; + r_factor[i] = noise::musgrave_multi_fractal( + position, dimension[i], lacunarity[i], detail[i]); + } + } + break; + } + } + break; + } + case SHD_MUSGRAVE_RIDGED_MULTIFRACTAL: { + const VArray<float> &offset = get_offset(param++); + const VArray<float> &gain = get_gain(param++); + MutableSpan<float> r_factor = get_r_factor(param++); + const bool compute_factor = !r_factor.is_empty(); + switch (dimensions_) { + case 1: { + const VArray<float> &w = get_w(0); + if (compute_factor) { + for (int64_t i : mask) { + const float position = w[i] * scale[i]; + r_factor[i] = noise::musgrave_ridged_multi_fractal( + position, dimension[i], lacunarity[i], detail[i], offset[i], gain[i]); + } + } + break; + } + case 2: { + const VArray<float3> &vector = get_vector(0); + if (compute_factor) { + for (int64_t i : mask) { + const float3 pxyz = vector[i] * scale[i]; + const float2 position = float2(pxyz[0], pxyz[1]); + r_factor[i] = noise::musgrave_ridged_multi_fractal( + position, dimension[i], lacunarity[i], detail[i], offset[i], gain[i]); + } + } + break; + } + case 3: { + const VArray<float3> &vector = get_vector(0); + if (compute_factor) { + for (int64_t i : mask) { + const float3 position = vector[i] * scale[i]; + r_factor[i] = noise::musgrave_ridged_multi_fractal( + position, dimension[i], lacunarity[i], detail[i], offset[i], gain[i]); + } + } + break; + } + case 4: { + const VArray<float3> &vector = get_vector(0); + const VArray<float> &w = get_w(1); + if (compute_factor) { + for (int64_t i : mask) { + const float3 pxyz = vector[i] * scale[i]; + const float pw = w[i] * scale[i]; + const float4 position{pxyz[0], pxyz[1], pxyz[2], pw}; + r_factor[i] = noise::musgrave_ridged_multi_fractal( + position, dimension[i], lacunarity[i], detail[i], offset[i], gain[i]); + } + } + break; + } + } + break; + } + case SHD_MUSGRAVE_HYBRID_MULTIFRACTAL: { + const VArray<float> &offset = get_offset(param++); + const VArray<float> &gain = get_gain(param++); + MutableSpan<float> r_factor = get_r_factor(param++); + const bool compute_factor = !r_factor.is_empty(); + switch (dimensions_) { + case 1: { + const VArray<float> &w = get_w(0); + if (compute_factor) { + for (int64_t i : mask) { + const float position = w[i] * scale[i]; + r_factor[i] = noise::musgrave_hybrid_multi_fractal( + position, dimension[i], lacunarity[i], detail[i], offset[i], gain[i]); + } + } + break; + } + case 2: { + const VArray<float3> &vector = get_vector(0); + if (compute_factor) { + for (int64_t i : mask) { + const float3 pxyz = vector[i] * scale[i]; + const float2 position = float2(pxyz[0], pxyz[1]); + r_factor[i] = noise::musgrave_hybrid_multi_fractal( + position, dimension[i], lacunarity[i], detail[i], offset[i], gain[i]); + } + } + break; + } + case 3: { + const VArray<float3> &vector = get_vector(0); + if (compute_factor) { + for (int64_t i : mask) { + const float3 position = vector[i] * scale[i]; + r_factor[i] = noise::musgrave_hybrid_multi_fractal( + position, dimension[i], lacunarity[i], detail[i], offset[i], gain[i]); + } + } + break; + } + case 4: { + const VArray<float3> &vector = get_vector(0); + const VArray<float> &w = get_w(1); + if (compute_factor) { + for (int64_t i : mask) { + const float3 pxyz = vector[i] * scale[i]; + const float pw = w[i] * scale[i]; + const float4 position{pxyz[0], pxyz[1], pxyz[2], pw}; + r_factor[i] = noise::musgrave_hybrid_multi_fractal( + position, dimension[i], lacunarity[i], detail[i], offset[i], gain[i]); + } + } + break; + } + } + break; + } + case SHD_MUSGRAVE_FBM: { + MutableSpan<float> r_factor = get_r_factor(param++); + const bool compute_factor = !r_factor.is_empty(); + switch (dimensions_) { + case 1: { + const VArray<float> &w = get_w(0); + if (compute_factor) { + for (int64_t i : mask) { + const float position = w[i] * scale[i]; + r_factor[i] = noise::musgrave_fBm( + position, dimension[i], lacunarity[i], detail[i]); + } + } + break; + } + case 2: { + const VArray<float3> &vector = get_vector(0); + if (compute_factor) { + for (int64_t i : mask) { + const float3 pxyz = vector[i] * scale[i]; + const float2 position = float2(pxyz[0], pxyz[1]); + r_factor[i] = noise::musgrave_fBm( + position, dimension[i], lacunarity[i], detail[i]); + } + } + break; + } + case 3: { + const VArray<float3> &vector = get_vector(0); + if (compute_factor) { + for (int64_t i : mask) { + const float3 position = vector[i] * scale[i]; + r_factor[i] = noise::musgrave_fBm( + position, dimension[i], lacunarity[i], detail[i]); + } + } + break; + } + case 4: { + const VArray<float3> &vector = get_vector(0); + const VArray<float> &w = get_w(1); + if (compute_factor) { + for (int64_t i : mask) { + const float3 pxyz = vector[i] * scale[i]; + const float pw = w[i] * scale[i]; + const float4 position{pxyz[0], pxyz[1], pxyz[2], pw}; + r_factor[i] = noise::musgrave_fBm( + position, dimension[i], lacunarity[i], detail[i]); + } + } + break; + } + } + break; + } + case SHD_MUSGRAVE_HETERO_TERRAIN: { + const VArray<float> &offset = get_offset(param++); + MutableSpan<float> r_factor = get_r_factor(param++); + const bool compute_factor = !r_factor.is_empty(); + switch (dimensions_) { + case 1: { + const VArray<float> &w = get_w(0); + if (compute_factor) { + for (int64_t i : mask) { + const float position = w[i] * scale[i]; + r_factor[i] = noise::musgrave_hetero_terrain( + position, dimension[i], lacunarity[i], detail[i], offset[i]); + } + } + break; + } + case 2: { + const VArray<float3> &vector = get_vector(0); + if (compute_factor) { + for (int64_t i : mask) { + const float3 pxyz = vector[i] * scale[i]; + const float2 position = float2(pxyz[0], pxyz[1]); + r_factor[i] = noise::musgrave_hetero_terrain( + position, dimension[i], lacunarity[i], detail[i], offset[i]); + } + } + break; + } + case 3: { + const VArray<float3> &vector = get_vector(0); + if (compute_factor) { + for (int64_t i : mask) { + const float3 position = vector[i] * scale[i]; + r_factor[i] = noise::musgrave_hetero_terrain( + position, dimension[i], lacunarity[i], detail[i], offset[i]); + } + } + break; + } + case 4: { + const VArray<float3> &vector = get_vector(0); + const VArray<float> &w = get_w(1); + if (compute_factor) { + for (int64_t i : mask) { + const float3 pxyz = vector[i] * scale[i]; + const float pw = w[i] * scale[i]; + const float4 position{pxyz[0], pxyz[1], pxyz[2], pw}; + r_factor[i] = noise::musgrave_hetero_terrain( + position, dimension[i], lacunarity[i], detail[i], offset[i]); + } + } + break; + } + } + break; + } + } + } +}; + +static void sh_node_musgrave_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) { + bNode &node = builder.node(); + NodeTexMusgrave *tex = (NodeTexMusgrave *)node.storage; + builder.construct_and_set_matching_fn<MusgraveFunction>(tex->dimensions, tex->musgrave_type); +} + +} // namespace blender::nodes::node_shader_tex_musgrave_cc + +void register_node_type_sh_tex_musgrave() +{ + namespace file_ns = blender::nodes::node_shader_tex_musgrave_cc; + static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_TEX_MUSGRAVE, "Musgrave Texture", NODE_CLASS_TEXTURE, 0); - ntype.declare = blender::nodes::sh_node_tex_musgrave_declare; + sh_fn_node_type_base(&ntype, SH_NODE_TEX_MUSGRAVE, "Musgrave Texture", NODE_CLASS_TEXTURE); + ntype.declare = file_ns::sh_node_tex_musgrave_declare; + ntype.draw_buttons = file_ns::node_shader_buts_tex_musgrave; node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); - node_type_init(&ntype, node_shader_init_tex_musgrave); + node_type_init(&ntype, file_ns::node_shader_init_tex_musgrave); node_type_storage( &ntype, "NodeTexMusgrave", node_free_standard_storage, node_copy_standard_storage); - node_type_gpu(&ntype, node_shader_gpu_tex_musgrave); - node_type_update(&ntype, node_shader_update_tex_musgrave); + node_type_gpu(&ntype, file_ns::node_shader_gpu_tex_musgrave); + node_type_update(&ntype, file_ns::node_shader_update_tex_musgrave); + ntype.build_multi_function = file_ns::sh_node_musgrave_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc b/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc index 6ffc8979815..1c703313edf 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc @@ -17,34 +17,45 @@ * All rights reserved. */ -#include "../node_shader_util.h" +#include "node_shader_util.hh" #include "BLI_noise.hh" -namespace blender::nodes { +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes::node_shader_tex_noise_cc { + +NODE_STORAGE_FUNCS(NodeTexNoise) static void sh_node_tex_noise_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input<decl::Vector>("Vector").implicit_field(); - b.add_input<decl::Float>("W").min(-1000.0f).max(1000.0f); - b.add_input<decl::Float>("Scale").min(-1000.0f).max(1000.0f).default_value(5.0f); - b.add_input<decl::Float>("Detail").min(0.0f).max(16.0f).default_value(2.0f); - b.add_input<decl::Float>("Roughness") + b.add_input<decl::Vector>(N_("Vector")).implicit_field(); + b.add_input<decl::Float>(N_("W")).min(-1000.0f).max(1000.0f).make_available([](bNode &node) { + /* Default to 1 instead of 4, because it is much faster. */ + node_storage(node).dimensions = 1; + }); + b.add_input<decl::Float>(N_("Scale")).min(-1000.0f).max(1000.0f).default_value(5.0f); + b.add_input<decl::Float>(N_("Detail")).min(0.0f).max(15.0f).default_value(2.0f); + b.add_input<decl::Float>(N_("Roughness")) .min(0.0f) .max(1.0f) .default_value(0.5f) .subtype(PROP_FACTOR); - b.add_input<decl::Float>("Distortion").min(-1000.0f).max(1000.0f).default_value(0.0f); - b.add_output<decl::Float>("Fac").no_muted_links(); - b.add_output<decl::Color>("Color").no_muted_links(); -}; + b.add_input<decl::Float>(N_("Distortion")).min(-1000.0f).max(1000.0f).default_value(0.0f); + b.add_output<decl::Float>(N_("Fac")).no_muted_links(); + b.add_output<decl::Color>(N_("Color")).no_muted_links(); +} -} // namespace blender::nodes +static void node_shader_buts_tex_noise(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "noise_dimensions", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); +} static void node_shader_init_tex_noise(bNodeTree *UNUSED(ntree), bNode *node) { - NodeTexNoise *tex = (NodeTexNoise *)MEM_callocN(sizeof(NodeTexNoise), "NodeTexNoise"); + NodeTexNoise *tex = MEM_cnew<NodeTexNoise>(__func__); BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT); BKE_texture_colormapping_default(&tex->base.color_mapping); tex->dimensions = 3; @@ -71,23 +82,21 @@ static int node_shader_gpu_tex_noise(GPUMaterial *mat, node_shader_gpu_default_tex_coord(mat, node, &in[0].link); node_shader_gpu_tex_mapping(mat, node, in, out); - NodeTexNoise *tex = (NodeTexNoise *)node->storage; - const char *name = gpu_shader_get_name(tex->dimensions); + const NodeTexNoise &storage = node_storage(*node); + const char *name = gpu_shader_get_name(storage.dimensions); return GPU_stack_link(mat, node, name, in, out); } -static void node_shader_update_tex_noise(bNodeTree *UNUSED(ntree), bNode *node) +static void node_shader_update_tex_noise(bNodeTree *ntree, bNode *node) { bNodeSocket *sockVector = nodeFindSocket(node, SOCK_IN, "Vector"); bNodeSocket *sockW = nodeFindSocket(node, SOCK_IN, "W"); - NodeTexNoise *tex = (NodeTexNoise *)node->storage; - nodeSetSocketAvailability(sockVector, tex->dimensions != 1); - nodeSetSocketAvailability(sockW, tex->dimensions == 1 || tex->dimensions == 4); + const NodeTexNoise &storage = node_storage(*node); + nodeSetSocketAvailability(ntree, sockVector, storage.dimensions != 1); + nodeSetSocketAvailability(ntree, sockW, storage.dimensions == 1 || storage.dimensions == 4); } -namespace blender::nodes { - class NoiseFunction : public fn::MultiFunction { private: int dimensions_; @@ -167,14 +176,14 @@ class NoiseFunction : public fn::MultiFunction { const VArray<float3> &vector = params.readonly_single_input<float3>(0, "Vector"); if (compute_factor) { for (int64_t i : mask) { - const float2 position = vector[i] * scale[i]; + const float2 position = float2(vector[i] * scale[i]); r_factor[i] = noise::perlin_fractal_distorted( position, detail[i], roughness[i], distortion[i]); } } if (compute_color) { for (int64_t i : mask) { - const float2 position = vector[i] * scale[i]; + const float2 position = float2(vector[i] * scale[i]); const float3 c = noise::perlin_float3_fractal_distorted( position, detail[i], roughness[i], distortion[i]); r_color[i] = ColorGeometry4f(c[0], c[1], c[2], 1.0f); @@ -229,30 +238,39 @@ class NoiseFunction : public fn::MultiFunction { } } } + + ExecutionHints get_execution_hints() const override + { + ExecutionHints hints; + hints.allocates_array = false; + hints.min_grain_size = 100; + return hints; + } }; static void sh_node_noise_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder) { - bNode &node = builder.node(); - NodeTexNoise *tex = (NodeTexNoise *)node.storage; - builder.construct_and_set_matching_fn<NoiseFunction>(tex->dimensions); + const NodeTexNoise &storage = node_storage(builder.node()); + builder.construct_and_set_matching_fn<NoiseFunction>(storage.dimensions); } -} // namespace blender::nodes +} // namespace blender::nodes::node_shader_tex_noise_cc -/* node type definition */ -void register_node_type_sh_tex_noise(void) +void register_node_type_sh_tex_noise() { + namespace file_ns = blender::nodes::node_shader_tex_noise_cc; + static bNodeType ntype; - sh_fn_node_type_base(&ntype, SH_NODE_TEX_NOISE, "Noise Texture", NODE_CLASS_TEXTURE, 0); - ntype.declare = blender::nodes::sh_node_tex_noise_declare; - node_type_init(&ntype, node_shader_init_tex_noise); + sh_fn_node_type_base(&ntype, SH_NODE_TEX_NOISE, "Noise Texture", NODE_CLASS_TEXTURE); + ntype.declare = file_ns::sh_node_tex_noise_declare; + ntype.draw_buttons = file_ns::node_shader_buts_tex_noise; + node_type_init(&ntype, file_ns::node_shader_init_tex_noise); node_type_storage( &ntype, "NodeTexNoise", node_free_standard_storage, node_copy_standard_storage); - node_type_gpu(&ntype, node_shader_gpu_tex_noise); - node_type_update(&ntype, node_shader_update_tex_noise); - ntype.build_multi_function = blender::nodes::sh_node_noise_build_multi_function; + node_type_gpu(&ntype, file_ns::node_shader_gpu_tex_noise); + node_type_update(&ntype, file_ns::node_shader_update_tex_noise); + ntype.build_multi_function = file_ns::sh_node_noise_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_pointdensity.c b/source/blender/nodes/shader/nodes/node_shader_tex_pointdensity.c deleted file mode 100644 index 14cd1fd4c0c..00000000000 --- a/source/blender/nodes/shader/nodes/node_shader_tex_pointdensity.c +++ /dev/null @@ -1,82 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2015 Blender Foundation. - * All rights reserved. - */ - -#include "../node_shader_util.h" - -#include "RE_texture.h" - -/* **************** OUTPUT ******************** */ - -static bNodeSocketTemplate sh_node_tex_pointdensity_in[] = { - {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, - {-1, ""}, -}; - -static bNodeSocketTemplate sh_node_tex_pointdensity_out[] = { - {SOCK_RGBA, N_("Color"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Density"), 0.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f}, - {-1, ""}, -}; - -static void node_shader_init_tex_pointdensity(bNodeTree *UNUSED(ntree), bNode *node) -{ - NodeShaderTexPointDensity *point_density = MEM_callocN(sizeof(NodeShaderTexPointDensity), - "new pd node"); - point_density->resolution = 100; - point_density->radius = 0.3f; - point_density->space = SHD_POINTDENSITY_SPACE_OBJECT; - point_density->color_source = SHD_POINTDENSITY_COLOR_PARTAGE; - node->storage = point_density; -} - -static void node_shader_free_tex_pointdensity(bNode *node) -{ - NodeShaderTexPointDensity *point_density = node->storage; - PointDensity *pd = &point_density->pd; - RE_point_density_free(pd); - BKE_texture_pointdensity_free_data(pd); - memset(pd, 0, sizeof(*pd)); - MEM_freeN(point_density); -} - -static void node_shader_copy_tex_pointdensity(bNodeTree *UNUSED(dest_ntree), - bNode *dest_node, - const bNode *src_node) -{ - dest_node->storage = MEM_dupallocN(src_node->storage); - NodeShaderTexPointDensity *point_density = dest_node->storage; - PointDensity *pd = &point_density->pd; - memset(pd, 0, sizeof(*pd)); -} - -/* node type definition */ -void register_node_type_sh_tex_pointdensity(void) -{ - static bNodeType ntype; - - sh_node_type_base(&ntype, SH_NODE_TEX_POINTDENSITY, "Point Density", NODE_CLASS_TEXTURE, 0); - node_type_socket_templates(&ntype, sh_node_tex_pointdensity_in, sh_node_tex_pointdensity_out); - node_type_init(&ntype, node_shader_init_tex_pointdensity); - node_type_storage(&ntype, - "NodeShaderTexPointDensity", - node_shader_free_tex_pointdensity, - node_shader_copy_tex_pointdensity); - - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_pointdensity.cc b/source/blender/nodes/shader/nodes/node_shader_tex_pointdensity.cc new file mode 100644 index 00000000000..1a4cf70565f --- /dev/null +++ b/source/blender/nodes/shader/nodes/node_shader_tex_pointdensity.cc @@ -0,0 +1,131 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2015 Blender Foundation. + * All rights reserved. + */ + +#include "node_shader_util.hh" + +#include "RE_texture.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes::node_shader_tex_pointdensity_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Vector>(N_("Vector")).hide_value(); + b.add_output<decl::Color>(N_("Color")); + b.add_output<decl::Float>(N_("Density")); +} + +static void node_shader_buts_tex_pointdensity(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + bNode *node = (bNode *)ptr->data; + NodeShaderTexPointDensity *shader_point_density = (NodeShaderTexPointDensity *)node->storage; + Object *ob = (Object *)node->id; + + PointerRNA ob_ptr, obdata_ptr; + RNA_id_pointer_create((ID *)ob, &ob_ptr); + RNA_id_pointer_create(ob ? (ID *)ob->data : nullptr, &obdata_ptr); + + uiItemR(layout, ptr, "point_source", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); + uiItemR(layout, ptr, "object", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + + if (node->id && shader_point_density->point_source == SHD_POINTDENSITY_SOURCE_PSYS) { + PointerRNA dataptr; + RNA_id_pointer_create((ID *)node->id, &dataptr); + uiItemPointerR( + layout, ptr, "particle_system", &dataptr, "particle_systems", nullptr, ICON_NONE); + } + + uiItemR(layout, ptr, "space", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(layout, ptr, "radius", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(layout, ptr, "interpolation", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(layout, ptr, "resolution", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + if (shader_point_density->point_source == SHD_POINTDENSITY_SOURCE_PSYS) { + uiItemR(layout, ptr, "particle_color_source", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + } + else { + uiItemR(layout, ptr, "vertex_color_source", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + if (shader_point_density->ob_color_source == SHD_POINTDENSITY_COLOR_VERTWEIGHT) { + if (ob_ptr.data) { + uiItemPointerR( + layout, ptr, "vertex_attribute_name", &ob_ptr, "vertex_groups", "", ICON_NONE); + } + } + if (shader_point_density->ob_color_source == SHD_POINTDENSITY_COLOR_VERTCOL) { + if (obdata_ptr.data) { + uiItemPointerR( + layout, ptr, "vertex_attribute_name", &obdata_ptr, "vertex_colors", "", ICON_NONE); + } + } + } +} + +static void node_shader_init_tex_pointdensity(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeShaderTexPointDensity *point_density = MEM_cnew<NodeShaderTexPointDensity>("new pd node"); + point_density->resolution = 100; + point_density->radius = 0.3f; + point_density->space = SHD_POINTDENSITY_SPACE_OBJECT; + point_density->color_source = SHD_POINTDENSITY_COLOR_PARTAGE; + node->storage = point_density; +} + +static void node_shader_free_tex_pointdensity(bNode *node) +{ + NodeShaderTexPointDensity *point_density = (NodeShaderTexPointDensity *)node->storage; + PointDensity *pd = &point_density->pd; + RE_point_density_free(pd); + BKE_texture_pointdensity_free_data(pd); + memset(pd, 0, sizeof(*pd)); + MEM_freeN(point_density); +} + +static void node_shader_copy_tex_pointdensity(bNodeTree *UNUSED(dest_ntree), + bNode *dest_node, + const bNode *src_node) +{ + dest_node->storage = MEM_dupallocN(src_node->storage); + NodeShaderTexPointDensity *point_density = (NodeShaderTexPointDensity *)dest_node->storage; + PointDensity *pd = &point_density->pd; + memset(pd, 0, sizeof(*pd)); +} + +} // namespace blender::nodes::node_shader_tex_pointdensity_cc + +/* node type definition */ +void register_node_type_sh_tex_pointdensity() +{ + namespace file_ns = blender::nodes::node_shader_tex_pointdensity_cc; + + static bNodeType ntype; + + sh_node_type_base(&ntype, SH_NODE_TEX_POINTDENSITY, "Point Density", NODE_CLASS_TEXTURE); + ntype.declare = file_ns::node_declare; + ntype.draw_buttons = file_ns::node_shader_buts_tex_pointdensity; + node_type_init(&ntype, file_ns::node_shader_init_tex_pointdensity); + node_type_storage(&ntype, + "NodeShaderTexPointDensity", + file_ns::node_shader_free_tex_pointdensity, + file_ns::node_shader_copy_tex_pointdensity); + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_sky.c b/source/blender/nodes/shader/nodes/node_shader_tex_sky.cc index 5dc11c4df00..b8728d69bba 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_sky.c +++ b/source/blender/nodes/shader/nodes/node_shader_tex_sky.cc @@ -17,24 +17,66 @@ * All rights reserved. */ -#include "../node_shader_util.h" +#include "node_shader_util.hh" #include "sky_model.h" -/* **************** OUTPUT ******************** */ +#include "BKE_context.h" +#include "BKE_scene.h" -static bNodeSocketTemplate sh_node_tex_sky_in[] = { - {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, - {-1, ""}, -}; +#include "UI_interface.h" +#include "UI_resources.h" -static bNodeSocketTemplate sh_node_tex_sky_out[] = { - {SOCK_RGBA, N_("Color"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_NO_INTERNAL_LINK}, - {-1, ""}, -}; +namespace blender::nodes::node_shader_tex_sky_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Vector>(N_("Vector")).hide_value(); + b.add_output<decl::Color>(N_("Color")).no_muted_links(); +} + +static void node_shader_buts_tex_sky(uiLayout *layout, bContext *C, PointerRNA *ptr) +{ + uiItemR(layout, ptr, "sky_type", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); + + if (RNA_enum_get(ptr, "sky_type") == SHD_SKY_PREETHAM) { + uiItemR(layout, ptr, "sun_direction", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); + uiItemR(layout, ptr, "turbidity", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + } + if (RNA_enum_get(ptr, "sky_type") == SHD_SKY_HOSEK) { + uiItemR(layout, ptr, "sun_direction", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); + uiItemR(layout, ptr, "turbidity", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(layout, ptr, "ground_albedo", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + } + if (RNA_enum_get(ptr, "sky_type") == SHD_SKY_NISHITA) { + Scene *scene = CTX_data_scene(C); + if (BKE_scene_uses_blender_eevee(scene)) { + uiItemL(layout, TIP_("Nishita not available in Eevee"), ICON_ERROR); + } + uiItemR(layout, ptr, "sun_disc", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, 0); + + uiLayout *col; + if (RNA_boolean_get(ptr, "sun_disc")) { + col = uiLayoutColumn(layout, true); + uiItemR(col, ptr, "sun_size", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(col, ptr, "sun_intensity", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + } + + col = uiLayoutColumn(layout, true); + uiItemR(col, ptr, "sun_elevation", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(col, ptr, "sun_rotation", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + + uiItemR(layout, ptr, "altitude", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + + col = uiLayoutColumn(layout, true); + uiItemR(col, ptr, "air_density", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(col, ptr, "dust_density", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(col, ptr, "ozone_density", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + } +} static void node_shader_init_tex_sky(bNodeTree *UNUSED(ntree), bNode *node) { - NodeTexSky *tex = MEM_callocN(sizeof(NodeTexSky), "NodeTexSky"); + NodeTexSky *tex = MEM_cnew<NodeTexSky>("NodeTexSky"); BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT); BKE_texture_colormapping_default(&tex->base.color_mapping); tex->sun_direction[0] = 0.0f; @@ -55,10 +97,10 @@ static void node_shader_init_tex_sky(bNodeTree *UNUSED(ntree), bNode *node) node->storage = tex; } -typedef struct SkyModelPreetham { +struct SkyModelPreetham { float config_Y[5], config_x[5], config_y[5]; /* named after xyY color space */ float radiance[3]; -} SkyModelPreetham; +}; static float sky_perez_function(const float *lam, float theta, float gamma) { @@ -195,27 +237,32 @@ static int node_shader_gpu_tex_sky(GPUMaterial *mat, return GPU_stack_link(mat, node, "node_tex_sky_nishita", in, out); } -static void node_shader_update_sky(bNodeTree *UNUSED(ntree), bNode *node) +static void node_shader_update_sky(bNodeTree *ntree, bNode *node) { bNodeSocket *sockVector = nodeFindSocket(node, SOCK_IN, "Vector"); NodeTexSky *tex = (NodeTexSky *)node->storage; - nodeSetSocketAvailability(sockVector, !(tex->sky_model == 2 && tex->sun_disc == 1)); + nodeSetSocketAvailability(ntree, sockVector, !(tex->sky_model == 2 && tex->sun_disc == 1)); } +} // namespace blender::nodes::node_shader_tex_sky_cc + /* node type definition */ -void register_node_type_sh_tex_sky(void) +void register_node_type_sh_tex_sky() { + namespace file_ns = blender::nodes::node_shader_tex_sky_cc; + static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_TEX_SKY, "Sky Texture", NODE_CLASS_TEXTURE, 0); - node_type_socket_templates(&ntype, sh_node_tex_sky_in, sh_node_tex_sky_out); + sh_node_type_base(&ntype, SH_NODE_TEX_SKY, "Sky Texture", NODE_CLASS_TEXTURE); + ntype.declare = file_ns::node_declare; + ntype.draw_buttons = file_ns::node_shader_buts_tex_sky; node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); - node_type_init(&ntype, node_shader_init_tex_sky); + node_type_init(&ntype, file_ns::node_shader_init_tex_sky); node_type_storage(&ntype, "NodeTexSky", node_free_standard_storage, node_copy_standard_storage); - node_type_gpu(&ntype, node_shader_gpu_tex_sky); - /* remove Vector input for Nishita */ - node_type_update(&ntype, node_shader_update_sky); + node_type_gpu(&ntype, file_ns::node_shader_gpu_tex_sky); + /* Remove vector input for Nishita sky model. */ + node_type_update(&ntype, file_ns::node_shader_update_sky); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc b/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc index e12e5724e8e..209f96449cd 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc @@ -17,39 +17,68 @@ * All rights reserved. */ -#include "../node_shader_util.h" +#include "node_shader_util.hh" -namespace blender::nodes { +#include "BLI_noise.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes::node_shader_tex_voronoi_cc { + +NODE_STORAGE_FUNCS(NodeTexVoronoi) static void sh_node_tex_voronoi_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input<decl::Vector>("Vector").hide_value(); - b.add_input<decl::Float>("W").min(-1000.0f).max(1000.0f); - b.add_input<decl::Float>("Scale").min(-1000.0f).max(1000.0f).default_value(5.0f); - b.add_input<decl::Float>("Smoothness") + b.add_input<decl::Vector>(N_("Vector")).hide_value().implicit_field(); + b.add_input<decl::Float>(N_("W")).min(-1000.0f).max(1000.0f).make_available([](bNode &node) { + /* Default to 1 instead of 4, because it is much faster. */ + node_storage(node).dimensions = 1; + }); + b.add_input<decl::Float>(N_("Scale")).min(-1000.0f).max(1000.0f).default_value(5.0f); + b.add_input<decl::Float>(N_("Smoothness")) .min(0.0f) .max(1.0f) .default_value(1.0f) - .subtype(PROP_FACTOR); - b.add_input<decl::Float>("Exponent").min(0.0f).max(32.0f).default_value(0.5f); - b.add_input<decl::Float>("Randomness") + .subtype(PROP_FACTOR) + .make_available([](bNode &node) { node_storage(node).feature = SHD_VORONOI_SMOOTH_F1; }); + b.add_input<decl::Float>(N_("Exponent")) + .min(0.0f) + .max(32.0f) + .default_value(0.5f) + .make_available([](bNode &node) { node_storage(node).distance = SHD_VORONOI_MINKOWSKI; }); + b.add_input<decl::Float>(N_("Randomness")) .min(0.0f) .max(1.0f) .default_value(1.0f) .subtype(PROP_FACTOR); - b.add_output<decl::Float>("Distance").no_muted_links(); - b.add_output<decl::Color>("Color").no_muted_links(); - b.add_output<decl::Vector>("Position").no_muted_links(); - b.add_output<decl::Float>("W").no_muted_links(); - b.add_output<decl::Float>("Radius").no_muted_links(); -}; + b.add_output<decl::Float>(N_("Distance")).no_muted_links(); + b.add_output<decl::Color>(N_("Color")).no_muted_links(); + b.add_output<decl::Vector>(N_("Position")).no_muted_links(); + b.add_output<decl::Float>(N_("W")).no_muted_links().make_available([](bNode &node) { + /* Default to 1 instead of 4, because it is much faster. */ + node_storage(node).dimensions = 1; + }); + b.add_output<decl::Float>(N_("Radius")).no_muted_links().make_available([](bNode &node) { + node_storage(node).feature = SHD_VORONOI_N_SPHERE_RADIUS; + }); +} -} // namespace blender::nodes +static void node_shader_buts_tex_voronoi(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "voronoi_dimensions", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); + uiItemR(layout, ptr, "feature", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); + int feature = RNA_enum_get(ptr, "feature"); + if (!ELEM(feature, SHD_VORONOI_DISTANCE_TO_EDGE, SHD_VORONOI_N_SPHERE_RADIUS) && + RNA_enum_get(ptr, "voronoi_dimensions") != 1) { + uiItemR(layout, ptr, "distance", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); + } +} static void node_shader_init_tex_voronoi(bNodeTree *UNUSED(ntree), bNode *node) { - NodeTexVoronoi *tex = (NodeTexVoronoi *)MEM_callocN(sizeof(NodeTexVoronoi), "NodeTexVoronoi"); + NodeTexVoronoi *tex = MEM_cnew<NodeTexVoronoi>(__func__); BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT); BKE_texture_colormapping_default(&tex->base.color_mapping); tex->dimensions = 3; @@ -121,7 +150,7 @@ static int node_shader_gpu_tex_voronoi(GPUMaterial *mat, return GPU_stack_link(mat, node, name, in, out, GPU_constant(&metric)); } -static void node_shader_update_tex_voronoi(bNodeTree *UNUSED(ntree), bNode *node) +static void node_shader_update_tex_voronoi(bNodeTree *ntree, bNode *node) { bNodeSocket *inVectorSock = nodeFindSocket(node, SOCK_IN, "Vector"); bNodeSocket *inWSock = nodeFindSocket(node, SOCK_IN, "W"); @@ -134,41 +163,1210 @@ static void node_shader_update_tex_voronoi(bNodeTree *UNUSED(ntree), bNode *node bNodeSocket *outWSock = nodeFindSocket(node, SOCK_OUT, "W"); bNodeSocket *outRadiusSock = nodeFindSocket(node, SOCK_OUT, "Radius"); - NodeTexVoronoi *tex = (NodeTexVoronoi *)node->storage; + const NodeTexVoronoi &storage = node_storage(*node); - nodeSetSocketAvailability(inWSock, tex->dimensions == 1 || tex->dimensions == 4); - nodeSetSocketAvailability(inVectorSock, tex->dimensions != 1); + nodeSetSocketAvailability(ntree, inWSock, storage.dimensions == 1 || storage.dimensions == 4); + nodeSetSocketAvailability(ntree, inVectorSock, storage.dimensions != 1); nodeSetSocketAvailability( + ntree, inExponentSock, - tex->distance == SHD_VORONOI_MINKOWSKI && tex->dimensions != 1 && - !ELEM(tex->feature, SHD_VORONOI_DISTANCE_TO_EDGE, SHD_VORONOI_N_SPHERE_RADIUS)); - nodeSetSocketAvailability(inSmoothnessSock, tex->feature == SHD_VORONOI_SMOOTH_F1); - nodeSetSocketAvailability(outDistanceSock, tex->feature != SHD_VORONOI_N_SPHERE_RADIUS); - nodeSetSocketAvailability(outColorSock, - tex->feature != SHD_VORONOI_DISTANCE_TO_EDGE && - tex->feature != SHD_VORONOI_N_SPHERE_RADIUS); - nodeSetSocketAvailability(outPositionSock, - tex->feature != SHD_VORONOI_DISTANCE_TO_EDGE && - tex->feature != SHD_VORONOI_N_SPHERE_RADIUS && - tex->dimensions != 1); - nodeSetSocketAvailability(outWSock, - tex->feature != SHD_VORONOI_DISTANCE_TO_EDGE && - tex->feature != SHD_VORONOI_N_SPHERE_RADIUS && - (tex->dimensions == 1 || tex->dimensions == 4)); - nodeSetSocketAvailability(outRadiusSock, tex->feature == SHD_VORONOI_N_SPHERE_RADIUS); + storage.distance == SHD_VORONOI_MINKOWSKI && storage.dimensions != 1 && + !ELEM(storage.feature, SHD_VORONOI_DISTANCE_TO_EDGE, SHD_VORONOI_N_SPHERE_RADIUS)); + nodeSetSocketAvailability(ntree, inSmoothnessSock, storage.feature == SHD_VORONOI_SMOOTH_F1); + + nodeSetSocketAvailability( + ntree, outDistanceSock, storage.feature != SHD_VORONOI_N_SPHERE_RADIUS); + nodeSetSocketAvailability(ntree, + outColorSock, + storage.feature != SHD_VORONOI_DISTANCE_TO_EDGE && + storage.feature != SHD_VORONOI_N_SPHERE_RADIUS); + nodeSetSocketAvailability(ntree, + outPositionSock, + storage.feature != SHD_VORONOI_DISTANCE_TO_EDGE && + storage.feature != SHD_VORONOI_N_SPHERE_RADIUS && + storage.dimensions != 1); + nodeSetSocketAvailability(ntree, + outWSock, + storage.feature != SHD_VORONOI_DISTANCE_TO_EDGE && + storage.feature != SHD_VORONOI_N_SPHERE_RADIUS && + (ELEM(storage.dimensions, 1, 4))); + nodeSetSocketAvailability(ntree, outRadiusSock, storage.feature == SHD_VORONOI_N_SPHERE_RADIUS); +} + +static MultiFunction::ExecutionHints voronoi_execution_hints{50, false}; + +class VoronoiMinowskiFunction : public fn::MultiFunction { + private: + int dimensions_; + int feature_; + + public: + VoronoiMinowskiFunction(int dimensions, int feature) : dimensions_(dimensions), feature_(feature) + { + BLI_assert(dimensions >= 2 && dimensions <= 4); + BLI_assert(feature >= 0 && feature <= 2); + static std::array<fn::MFSignature, 9> signatures{ + create_signature(2, SHD_VORONOI_F1), + create_signature(3, SHD_VORONOI_F1), + create_signature(4, SHD_VORONOI_F1), + + create_signature(2, SHD_VORONOI_F2), + create_signature(3, SHD_VORONOI_F2), + create_signature(4, SHD_VORONOI_F2), + + create_signature(2, SHD_VORONOI_SMOOTH_F1), + create_signature(3, SHD_VORONOI_SMOOTH_F1), + create_signature(4, SHD_VORONOI_SMOOTH_F1), + }; + this->set_signature(&signatures[(dimensions - 1) + feature * 3 - 1]); + } + + static fn::MFSignature create_signature(int dimensions, int feature) + { + fn::MFSignatureBuilder signature{"voronoi_minowski"}; + + if (ELEM(dimensions, 2, 3, 4)) { + signature.single_input<float3>("Vector"); + } + if (ELEM(dimensions, 1, 4)) { + signature.single_input<float>("W"); + } + signature.single_input<float>("Scale"); + if (feature == SHD_VORONOI_SMOOTH_F1) { + signature.single_input<float>("Smoothness"); + } + signature.single_input<float>("Exponent"); + signature.single_input<float>("Randomness"); + signature.single_output<float>("Distance"); + signature.single_output<ColorGeometry4f>("Color"); + + if (dimensions != 1) { + signature.single_output<float3>("Position"); + } + if (ELEM(dimensions, 1, 4)) { + signature.single_output<float>("W"); + } + + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + auto get_vector = [&](int param_index) -> VArray<float3> { + return params.readonly_single_input<float3>(param_index, "Vector"); + }; + auto get_w = [&](int param_index) -> VArray<float> { + return params.readonly_single_input<float>(param_index, "W"); + }; + auto get_scale = [&](int param_index) -> VArray<float> { + return params.readonly_single_input<float>(param_index, "Scale"); + }; + auto get_smoothness = [&](int param_index) -> VArray<float> { + return params.readonly_single_input<float>(param_index, "Smoothness"); + }; + auto get_exponent = [&](int param_index) -> VArray<float> { + return params.readonly_single_input<float>(param_index, "Exponent"); + }; + auto get_randomness = [&](int param_index) -> VArray<float> { + return params.readonly_single_input<float>(param_index, "Randomness"); + }; + auto get_r_distance = [&](int param_index) -> MutableSpan<float> { + return params.uninitialized_single_output_if_required<float>(param_index, "Distance"); + }; + auto get_r_color = [&](int param_index) -> MutableSpan<ColorGeometry4f> { + return params.uninitialized_single_output_if_required<ColorGeometry4f>(param_index, "Color"); + }; + auto get_r_position = [&](int param_index) -> MutableSpan<float3> { + return params.uninitialized_single_output_if_required<float3>(param_index, "Position"); + }; + auto get_r_w = [&](int param_index) -> MutableSpan<float> { + return params.uninitialized_single_output_if_required<float>(param_index, "W"); + }; + + int param = 0; + switch (dimensions_) { + case 2: { + switch (feature_) { + case SHD_VORONOI_F1: { + const VArray<float3> &vector = get_vector(param++); + const VArray<float> &scale = get_scale(param++); + const VArray<float> &exponent = get_exponent(param++); + const VArray<float> &randomness = get_randomness(param++); + MutableSpan<float> r_distance = get_r_distance(param++); + MutableSpan<ColorGeometry4f> r_color = get_r_color(param++); + MutableSpan<float3> r_position = get_r_position(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + float2 pos; + noise::voronoi_f1(float2(vector[i].x, vector[i].y) * scale[i], + exponent[i], + rand, + SHD_VORONOI_MINKOWSKI, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position ? &pos : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position) { + pos = math::safe_divide(pos, scale[i]); + r_position[i] = float3(pos.x, pos.y, 0.0f); + } + } + break; + } + case SHD_VORONOI_F2: { + const VArray<float3> &vector = get_vector(param++); + const VArray<float> &scale = get_scale(param++); + const VArray<float> &exponent = get_exponent(param++); + const VArray<float> &randomness = get_randomness(param++); + MutableSpan<float> r_distance = get_r_distance(param++); + MutableSpan<ColorGeometry4f> r_color = get_r_color(param++); + MutableSpan<float3> r_position = get_r_position(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + float2 pos; + noise::voronoi_f2(float2(vector[i].x, vector[i].y) * scale[i], + exponent[i], + rand, + SHD_VORONOI_MINKOWSKI, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position ? &pos : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position) { + pos = math::safe_divide(pos, scale[i]); + r_position[i] = float3(pos.x, pos.y, 0.0f); + } + } + break; + } + case SHD_VORONOI_SMOOTH_F1: { + const VArray<float3> &vector = get_vector(param++); + const VArray<float> &scale = get_scale(param++); + const VArray<float> &smoothness = get_smoothness(param++); + const VArray<float> &exponent = get_exponent(param++); + const VArray<float> &randomness = get_randomness(param++); + MutableSpan<float> r_distance = get_r_distance(param++); + MutableSpan<ColorGeometry4f> r_color = get_r_color(param++); + MutableSpan<float3> r_position = get_r_position(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); + for (int64_t i : mask) { + const float smth = std::min(std::max(smoothness[i] / 2.0f, 0.0f), 0.5f); + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + float2 pos; + noise::voronoi_smooth_f1(float2(vector[i].x, vector[i].y) * scale[i], + smth, + exponent[i], + rand, + SHD_VORONOI_MINKOWSKI, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position ? &pos : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position) { + pos = math::safe_divide(pos, scale[i]); + r_position[i] = float3(pos.x, pos.y, 0.0f); + } + } + break; + } + } + break; + } + case 3: { + switch (feature_) { + case SHD_VORONOI_F1: { + const VArray<float3> &vector = get_vector(param++); + const VArray<float> &scale = get_scale(param++); + const VArray<float> &exponent = get_exponent(param++); + const VArray<float> &randomness = get_randomness(param++); + MutableSpan<float> r_distance = get_r_distance(param++); + MutableSpan<ColorGeometry4f> r_color = get_r_color(param++); + MutableSpan<float3> r_position = get_r_position(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + noise::voronoi_f1(vector[i] * scale[i], + exponent[i], + rand, + SHD_VORONOI_MINKOWSKI, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position ? &r_position[i] : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position) { + r_position[i] = math::safe_divide(r_position[i], scale[i]); + } + } + break; + } + case SHD_VORONOI_F2: { + const VArray<float3> &vector = get_vector(param++); + const VArray<float> &scale = get_scale(param++); + const VArray<float> &exponent = get_exponent(param++); + const VArray<float> &randomness = get_randomness(param++); + MutableSpan<float> r_distance = get_r_distance(param++); + MutableSpan<ColorGeometry4f> r_color = get_r_color(param++); + MutableSpan<float3> r_position = get_r_position(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + noise::voronoi_f2(vector[i] * scale[i], + exponent[i], + rand, + SHD_VORONOI_MINKOWSKI, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position ? &r_position[i] : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position) { + r_position[i] = math::safe_divide(r_position[i], scale[i]); + } + } + break; + } + case SHD_VORONOI_SMOOTH_F1: { + const VArray<float3> &vector = get_vector(param++); + const VArray<float> &scale = get_scale(param++); + const VArray<float> &smoothness = get_smoothness(param++); + const VArray<float> &exponent = get_exponent(param++); + const VArray<float> &randomness = get_randomness(param++); + MutableSpan<float> r_distance = get_r_distance(param++); + MutableSpan<ColorGeometry4f> r_color = get_r_color(param++); + MutableSpan<float3> r_position = get_r_position(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); + for (int64_t i : mask) { + const float smth = std::min(std::max(smoothness[i] / 2.0f, 0.0f), 0.5f); + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + noise::voronoi_smooth_f1(vector[i] * scale[i], + smth, + exponent[i], + rand, + SHD_VORONOI_MINKOWSKI, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position ? &r_position[i] : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position) { + r_position[i] = math::safe_divide(r_position[i], scale[i]); + } + } + break; + } + } + break; + } + case 4: { + switch (feature_) { + case SHD_VORONOI_F1: { + const VArray<float3> &vector = get_vector(param++); + const VArray<float> &w = get_w(param++); + const VArray<float> &scale = get_scale(param++); + const VArray<float> &exponent = get_exponent(param++); + const VArray<float> &randomness = get_randomness(param++); + MutableSpan<float> r_distance = get_r_distance(param++); + MutableSpan<ColorGeometry4f> r_color = get_r_color(param++); + MutableSpan<float3> r_position = get_r_position(param++); + MutableSpan<float> r_w = get_r_w(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); + const bool calc_w = !r_w.is_empty(); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + const float4 p = float4(vector[i].x, vector[i].y, vector[i].z, w[i]) * scale[i]; + float3 col; + float4 pos; + noise::voronoi_f1(p, + exponent[i], + rand, + SHD_VORONOI_F1, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position || calc_w ? &pos : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position || calc_w) { + pos = math::safe_divide(pos, scale[i]); + if (calc_position) { + r_position[i] = float3(pos.x, pos.y, pos.z); + } + if (calc_w) { + r_w[i] = pos.w; + } + } + } + break; + } + case SHD_VORONOI_F2: { + const VArray<float3> &vector = get_vector(param++); + const VArray<float> &w = get_w(param++); + const VArray<float> &scale = get_scale(param++); + const VArray<float> &exponent = get_exponent(param++); + const VArray<float> &randomness = get_randomness(param++); + MutableSpan<float> r_distance = get_r_distance(param++); + MutableSpan<ColorGeometry4f> r_color = get_r_color(param++); + MutableSpan<float3> r_position = get_r_position(param++); + MutableSpan<float> r_w = get_r_w(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); + const bool calc_w = !r_w.is_empty(); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + const float4 p = float4(vector[i].x, vector[i].y, vector[i].z, w[i]) * scale[i]; + float3 col; + float4 pos; + noise::voronoi_f2(p, + exponent[i], + rand, + SHD_VORONOI_MINKOWSKI, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position || calc_w ? &pos : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position || calc_w) { + pos = math::safe_divide(pos, scale[i]); + if (calc_position) { + r_position[i] = float3(pos.x, pos.y, pos.z); + } + if (calc_w) { + r_w[i] = pos.w; + } + } + } + break; + } + case SHD_VORONOI_SMOOTH_F1: { + const VArray<float3> &vector = get_vector(param++); + const VArray<float> &w = get_w(param++); + const VArray<float> &scale = get_scale(param++); + const VArray<float> &smoothness = get_smoothness(param++); + const VArray<float> &exponent = get_exponent(param++); + const VArray<float> &randomness = get_randomness(param++); + MutableSpan<float> r_distance = get_r_distance(param++); + MutableSpan<ColorGeometry4f> r_color = get_r_color(param++); + MutableSpan<float3> r_position = get_r_position(param++); + MutableSpan<float> r_w = get_r_w(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); + const bool calc_w = !r_w.is_empty(); + for (int64_t i : mask) { + const float smth = std::min(std::max(smoothness[i] / 2.0f, 0.0f), 0.5f); + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + const float4 p = float4(vector[i].x, vector[i].y, vector[i].z, w[i]) * scale[i]; + float3 col; + float4 pos; + noise::voronoi_smooth_f1(p, + smth, + exponent[i], + rand, + SHD_VORONOI_MINKOWSKI, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position || calc_w ? &pos : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position || calc_w) { + pos = math::safe_divide(pos, scale[i]); + if (calc_position) { + r_position[i] = float3(pos.x, pos.y, pos.z); + } + if (calc_w) { + r_w[i] = pos.w; + } + } + } + break; + } + } + break; + } + } + } + + ExecutionHints get_execution_hints() const override + { + return voronoi_execution_hints; + } +}; + +class VoronoiMetricFunction : public fn::MultiFunction { + private: + int dimensions_; + int feature_; + int metric_; + + public: + VoronoiMetricFunction(int dimensions, int feature, int metric) + : dimensions_(dimensions), feature_(feature), metric_(metric) + { + BLI_assert(dimensions >= 1 && dimensions <= 4); + BLI_assert(feature >= 0 && feature <= 4); + static std::array<fn::MFSignature, 12> signatures{ + create_signature(1, SHD_VORONOI_F1), + create_signature(2, SHD_VORONOI_F1), + create_signature(3, SHD_VORONOI_F1), + create_signature(4, SHD_VORONOI_F1), + + create_signature(1, SHD_VORONOI_F2), + create_signature(2, SHD_VORONOI_F2), + create_signature(3, SHD_VORONOI_F2), + create_signature(4, SHD_VORONOI_F2), + + create_signature(1, SHD_VORONOI_SMOOTH_F1), + create_signature(2, SHD_VORONOI_SMOOTH_F1), + create_signature(3, SHD_VORONOI_SMOOTH_F1), + create_signature(4, SHD_VORONOI_SMOOTH_F1), + }; + this->set_signature(&signatures[dimensions + feature * 4 - 1]); + } + + static fn::MFSignature create_signature(int dimensions, int feature) + { + fn::MFSignatureBuilder signature{"voronoi_metric"}; + + if (ELEM(dimensions, 2, 3, 4)) { + signature.single_input<float3>("Vector"); + } + if (ELEM(dimensions, 1, 4)) { + signature.single_input<float>("W"); + } + signature.single_input<float>("Scale"); + if (feature == SHD_VORONOI_SMOOTH_F1) { + signature.single_input<float>("Smoothness"); + } + signature.single_input<float>("Randomness"); + signature.single_output<float>("Distance"); + signature.single_output<ColorGeometry4f>("Color"); + + if (dimensions != 1) { + signature.single_output<float3>("Position"); + } + if (ELEM(dimensions, 1, 4)) { + signature.single_output<float>("W"); + } + + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + auto get_vector = [&](int param_index) -> VArray<float3> { + return params.readonly_single_input<float3>(param_index, "Vector"); + }; + auto get_w = [&](int param_index) -> VArray<float> { + return params.readonly_single_input<float>(param_index, "W"); + }; + auto get_scale = [&](int param_index) -> VArray<float> { + return params.readonly_single_input<float>(param_index, "Scale"); + }; + auto get_smoothness = [&](int param_index) -> VArray<float> { + return params.readonly_single_input<float>(param_index, "Smoothness"); + }; + auto get_randomness = [&](int param_index) -> VArray<float> { + return params.readonly_single_input<float>(param_index, "Randomness"); + }; + auto get_r_distance = [&](int param_index) -> MutableSpan<float> { + return params.uninitialized_single_output_if_required<float>(param_index, "Distance"); + }; + auto get_r_color = [&](int param_index) -> MutableSpan<ColorGeometry4f> { + return params.uninitialized_single_output_if_required<ColorGeometry4f>(param_index, "Color"); + }; + auto get_r_position = [&](int param_index) -> MutableSpan<float3> { + return params.uninitialized_single_output_if_required<float3>(param_index, "Position"); + }; + auto get_r_w = [&](int param_index) -> MutableSpan<float> { + return params.uninitialized_single_output_if_required<float>(param_index, "W"); + }; + + int param = 0; + switch (dimensions_) { + case 1: { + switch (feature_) { + case SHD_VORONOI_F1: { + const VArray<float> &w = get_w(param++); + const VArray<float> &scale = get_scale(param++); + const VArray<float> &randomness = get_randomness(param++); + MutableSpan<float> r_distance = get_r_distance(param++); + MutableSpan<ColorGeometry4f> r_color = get_r_color(param++); + MutableSpan<float> r_w = get_r_w(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_w = !r_w.is_empty(); + for (int64_t i : mask) { + const float p = w[i] * scale[i]; + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + noise::voronoi_f1(p, + rand, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_w ? &r_w[i] : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_w) { + r_w[i] = safe_divide(r_w[i], scale[i]); + } + } + break; + } + case SHD_VORONOI_F2: { + const VArray<float> &w = get_w(param++); + const VArray<float> &scale = get_scale(param++); + const VArray<float> &randomness = get_randomness(param++); + MutableSpan<float> r_distance = get_r_distance(param++); + MutableSpan<ColorGeometry4f> r_color = get_r_color(param++); + MutableSpan<float> r_w = get_r_w(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_w = !r_w.is_empty(); + for (int64_t i : mask) { + const float p = w[i] * scale[i]; + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + noise::voronoi_f2(p, + rand, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_w ? &r_w[i] : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_w) { + r_w[i] = safe_divide(r_w[i], scale[i]); + } + } + break; + } + case SHD_VORONOI_SMOOTH_F1: { + const VArray<float> &w = get_w(param++); + const VArray<float> &scale = get_scale(param++); + const VArray<float> &smoothness = get_smoothness(param++); + const VArray<float> &randomness = get_randomness(param++); + MutableSpan<float> r_distance = get_r_distance(param++); + MutableSpan<ColorGeometry4f> r_color = get_r_color(param++); + MutableSpan<float> r_w = get_r_w(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_w = !r_w.is_empty(); + for (int64_t i : mask) { + const float p = w[i] * scale[i]; + const float smth = std::min(std::max(smoothness[i] / 2.0f, 0.0f), 0.5f); + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + noise::voronoi_smooth_f1(p, + smth, + rand, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_w ? &r_w[i] : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_w) { + r_w[i] = safe_divide(r_w[i], scale[i]); + } + } + break; + } + } + break; + } + case 2: { + switch (feature_) { + case SHD_VORONOI_F1: { + const VArray<float3> &vector = get_vector(param++); + const VArray<float> &scale = get_scale(param++); + const VArray<float> &randomness = get_randomness(param++); + MutableSpan<float> r_distance = get_r_distance(param++); + MutableSpan<ColorGeometry4f> r_color = get_r_color(param++); + MutableSpan<float3> r_position = get_r_position(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + float2 pos; + noise::voronoi_f1(float2(vector[i].x, vector[i].y) * scale[i], + 0.0f, + rand, + metric_, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position ? &pos : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position) { + pos = math::safe_divide(pos, scale[i]); + r_position[i] = float3(pos.x, pos.y, 0.0f); + } + } + break; + } + case SHD_VORONOI_F2: { + const VArray<float3> &vector = get_vector(param++); + const VArray<float> &scale = get_scale(param++); + const VArray<float> &randomness = get_randomness(param++); + MutableSpan<float> r_distance = get_r_distance(param++); + MutableSpan<ColorGeometry4f> r_color = get_r_color(param++); + MutableSpan<float3> r_position = get_r_position(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + float2 pos; + noise::voronoi_f2(float2(vector[i].x, vector[i].y) * scale[i], + 0.0f, + rand, + metric_, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position ? &pos : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position) { + pos = math::safe_divide(pos, scale[i]); + r_position[i] = float3(pos.x, pos.y, 0.0f); + } + } + break; + } + case SHD_VORONOI_SMOOTH_F1: { + const VArray<float3> &vector = get_vector(param++); + const VArray<float> &scale = get_scale(param++); + const VArray<float> &smoothness = get_smoothness(param++); + const VArray<float> &randomness = get_randomness(param++); + MutableSpan<float> r_distance = get_r_distance(param++); + MutableSpan<ColorGeometry4f> r_color = get_r_color(param++); + MutableSpan<float3> r_position = get_r_position(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); + for (int64_t i : mask) { + const float smth = std::min(std::max(smoothness[i] / 2.0f, 0.0f), 0.5f); + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + float2 pos; + noise::voronoi_smooth_f1(float2(vector[i].x, vector[i].y) * scale[i], + smth, + 0.0f, + rand, + metric_, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position ? &pos : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position) { + pos = math::safe_divide(pos, scale[i]); + r_position[i] = float3(pos.x, pos.y, 0.0f); + } + } + break; + } + } + break; + } + case 3: { + switch (feature_) { + case SHD_VORONOI_F1: { + const VArray<float3> &vector = get_vector(param++); + const VArray<float> &scale = get_scale(param++); + const VArray<float> &randomness = get_randomness(param++); + MutableSpan<float> r_distance = get_r_distance(param++); + MutableSpan<ColorGeometry4f> r_color = get_r_color(param++); + MutableSpan<float3> r_position = get_r_position(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + noise::voronoi_f1(vector[i] * scale[i], + 0.0f, + rand, + metric_, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position ? &r_position[i] : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position) { + r_position[i] = math::safe_divide(r_position[i], scale[i]); + } + } + break; + } + case SHD_VORONOI_F2: { + const VArray<float3> &vector = get_vector(param++); + const VArray<float> &scale = get_scale(param++); + const VArray<float> &randomness = get_randomness(param++); + MutableSpan<float> r_distance = get_r_distance(param++); + MutableSpan<ColorGeometry4f> r_color = get_r_color(param++); + MutableSpan<float3> r_position = get_r_position(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + noise::voronoi_f2(vector[i] * scale[i], + 0.0f, + rand, + metric_, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position ? &r_position[i] : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position) { + r_position[i] = math::safe_divide(r_position[i], scale[i]); + } + } + break; + } + case SHD_VORONOI_SMOOTH_F1: { + const VArray<float3> &vector = get_vector(param++); + const VArray<float> &scale = get_scale(param++); + const VArray<float> &smoothness = get_smoothness(param++); + const VArray<float> &randomness = get_randomness(param++); + MutableSpan<float> r_distance = get_r_distance(param++); + MutableSpan<ColorGeometry4f> r_color = get_r_color(param++); + MutableSpan<float3> r_position = get_r_position(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); + { + for (int64_t i : mask) { + const float smth = std::min(std::max(smoothness[i] / 2.0f, 0.0f), 0.5f); + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + noise::voronoi_smooth_f1(vector[i] * scale[i], + smth, + 0.0f, + rand, + metric_, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position ? &r_position[i] : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position) { + r_position[i] = math::safe_divide(r_position[i], scale[i]); + } + } + } + + break; + } + } + break; + } + case 4: { + switch (feature_) { + case SHD_VORONOI_F1: { + const VArray<float3> &vector = get_vector(param++); + const VArray<float> &w = get_w(param++); + const VArray<float> &scale = get_scale(param++); + const VArray<float> &randomness = get_randomness(param++); + MutableSpan<float> r_distance = get_r_distance(param++); + MutableSpan<ColorGeometry4f> r_color = get_r_color(param++); + MutableSpan<float3> r_position = get_r_position(param++); + MutableSpan<float> r_w = get_r_w(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); + const bool calc_w = !r_w.is_empty(); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + const float4 p = float4(vector[i].x, vector[i].y, vector[i].z, w[i]) * scale[i]; + float3 col; + float4 pos; + noise::voronoi_f1(p, + 0.0f, + rand, + metric_, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position || calc_w ? &pos : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position || calc_w) { + pos = math::safe_divide(pos, scale[i]); + if (calc_position) { + r_position[i] = float3(pos.x, pos.y, pos.z); + } + if (calc_w) { + r_w[i] = pos.w; + } + } + } + break; + } + case SHD_VORONOI_F2: { + const VArray<float3> &vector = get_vector(param++); + const VArray<float> &w = get_w(param++); + const VArray<float> &scale = get_scale(param++); + const VArray<float> &randomness = get_randomness(param++); + MutableSpan<float> r_distance = get_r_distance(param++); + MutableSpan<ColorGeometry4f> r_color = get_r_color(param++); + MutableSpan<float3> r_position = get_r_position(param++); + MutableSpan<float> r_w = get_r_w(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); + const bool calc_w = !r_w.is_empty(); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + const float4 p = float4(vector[i].x, vector[i].y, vector[i].z, w[i]) * scale[i]; + float3 col; + float4 pos; + noise::voronoi_f2(p, + 0.0f, + rand, + metric_, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position || calc_w ? &pos : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position || calc_w) { + pos = math::safe_divide(pos, scale[i]); + if (calc_position) { + r_position[i] = float3(pos.x, pos.y, pos.z); + } + if (calc_w) { + r_w[i] = pos.w; + } + } + } + break; + } + case SHD_VORONOI_SMOOTH_F1: { + const VArray<float3> &vector = get_vector(param++); + const VArray<float> &w = get_w(param++); + const VArray<float> &scale = get_scale(param++); + const VArray<float> &smoothness = get_smoothness(param++); + const VArray<float> &randomness = get_randomness(param++); + MutableSpan<float> r_distance = get_r_distance(param++); + MutableSpan<ColorGeometry4f> r_color = get_r_color(param++); + MutableSpan<float3> r_position = get_r_position(param++); + MutableSpan<float> r_w = get_r_w(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); + const bool calc_w = !r_w.is_empty(); + for (int64_t i : mask) { + const float smth = std::min(std::max(smoothness[i] / 2.0f, 0.0f), 0.5f); + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + const float4 p = float4(vector[i].x, vector[i].y, vector[i].z, w[i]) * scale[i]; + float3 col; + float4 pos; + noise::voronoi_smooth_f1(p, + smth, + 0.0f, + rand, + metric_, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position || calc_w ? &pos : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position || calc_w) { + pos = math::safe_divide(pos, scale[i]); + if (calc_position) { + r_position[i] = float3(pos.x, pos.y, pos.z); + } + if (calc_w) { + r_w[i] = pos.w; + } + } + } + break; + } + } + break; + } + } + } + + ExecutionHints get_execution_hints() const override + { + return voronoi_execution_hints; + } +}; + +class VoronoiEdgeFunction : public fn::MultiFunction { + private: + int dimensions_; + int feature_; + + public: + VoronoiEdgeFunction(int dimensions, int feature) : dimensions_(dimensions), feature_(feature) + { + BLI_assert(dimensions >= 1 && dimensions <= 4); + BLI_assert(feature >= 3 && feature <= 4); + static std::array<fn::MFSignature, 8> signatures{ + create_signature(1, SHD_VORONOI_DISTANCE_TO_EDGE), + create_signature(2, SHD_VORONOI_DISTANCE_TO_EDGE), + create_signature(3, SHD_VORONOI_DISTANCE_TO_EDGE), + create_signature(4, SHD_VORONOI_DISTANCE_TO_EDGE), + + create_signature(1, SHD_VORONOI_N_SPHERE_RADIUS), + create_signature(2, SHD_VORONOI_N_SPHERE_RADIUS), + create_signature(3, SHD_VORONOI_N_SPHERE_RADIUS), + create_signature(4, SHD_VORONOI_N_SPHERE_RADIUS), + }; + this->set_signature(&signatures[dimensions + (feature - 3) * 4 - 1]); + } + + static fn::MFSignature create_signature(int dimensions, int feature) + { + fn::MFSignatureBuilder signature{"voronoi_edge"}; + + if (ELEM(dimensions, 2, 3, 4)) { + signature.single_input<float3>("Vector"); + } + if (ELEM(dimensions, 1, 4)) { + signature.single_input<float>("W"); + } + signature.single_input<float>("Scale"); + signature.single_input<float>("Randomness"); + + if (feature == SHD_VORONOI_DISTANCE_TO_EDGE) { + signature.single_output<float>("Distance"); + } + if (feature == SHD_VORONOI_N_SPHERE_RADIUS) { + signature.single_output<float>("Radius"); + } + + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + auto get_vector = [&](int param_index) -> VArray<float3> { + return params.readonly_single_input<float3>(param_index, "Vector"); + }; + auto get_w = [&](int param_index) -> VArray<float> { + return params.readonly_single_input<float>(param_index, "W"); + }; + auto get_scale = [&](int param_index) -> VArray<float> { + return params.readonly_single_input<float>(param_index, "Scale"); + }; + auto get_randomness = [&](int param_index) -> VArray<float> { + return params.readonly_single_input<float>(param_index, "Randomness"); + }; + auto get_r_distance = [&](int param_index) -> MutableSpan<float> { + return params.uninitialized_single_output<float>(param_index, "Distance"); + }; + auto get_r_radius = [&](int param_index) -> MutableSpan<float> { + return params.uninitialized_single_output<float>(param_index, "Radius"); + }; + + int param = 0; + switch (dimensions_) { + case 1: { + const VArray<float> &w = get_w(param++); + const VArray<float> &scale = get_scale(param++); + const VArray<float> &randomness = get_randomness(param++); + switch (feature_) { + case SHD_VORONOI_DISTANCE_TO_EDGE: { + MutableSpan<float> r_distance = get_r_distance(param++); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + const float p = w[i] * scale[i]; + noise::voronoi_distance_to_edge(p, rand, &r_distance[i]); + } + break; + } + case SHD_VORONOI_N_SPHERE_RADIUS: { + MutableSpan<float> r_radius = get_r_radius(param++); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + const float p = w[i] * scale[i]; + noise::voronoi_n_sphere_radius(p, rand, &r_radius[i]); + } + break; + } + } + break; + } + case 2: { + const VArray<float3> &vector = get_vector(param++); + const VArray<float> &scale = get_scale(param++); + const VArray<float> &randomness = get_randomness(param++); + switch (feature_) { + case SHD_VORONOI_DISTANCE_TO_EDGE: { + MutableSpan<float> r_distance = get_r_distance(param++); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + const float2 p = float2(vector[i].x, vector[i].y) * scale[i]; + noise::voronoi_distance_to_edge(p, rand, &r_distance[i]); + } + break; + } + case SHD_VORONOI_N_SPHERE_RADIUS: { + MutableSpan<float> r_radius = get_r_radius(param++); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + const float2 p = float2(vector[i].x, vector[i].y) * scale[i]; + noise::voronoi_n_sphere_radius(p, rand, &r_radius[i]); + } + break; + } + } + break; + } + case 3: { + const VArray<float3> &vector = get_vector(param++); + const VArray<float> &scale = get_scale(param++); + const VArray<float> &randomness = get_randomness(param++); + switch (feature_) { + case SHD_VORONOI_DISTANCE_TO_EDGE: { + MutableSpan<float> r_distance = get_r_distance(param++); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + noise::voronoi_distance_to_edge(vector[i] * scale[i], rand, &r_distance[i]); + } + break; + } + case SHD_VORONOI_N_SPHERE_RADIUS: { + MutableSpan<float> r_radius = get_r_radius(param++); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + noise::voronoi_n_sphere_radius(vector[i] * scale[i], rand, &r_radius[i]); + } + break; + } + } + break; + } + case 4: { + const VArray<float3> &vector = get_vector(param++); + const VArray<float> &w = get_w(param++); + const VArray<float> &scale = get_scale(param++); + const VArray<float> &randomness = get_randomness(param++); + switch (feature_) { + case SHD_VORONOI_DISTANCE_TO_EDGE: { + MutableSpan<float> r_distance = get_r_distance(param++); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + const float4 p = float4(vector[i].x, vector[i].y, vector[i].z, w[i]) * scale[i]; + noise::voronoi_distance_to_edge(p, rand, &r_distance[i]); + } + break; + } + case SHD_VORONOI_N_SPHERE_RADIUS: { + MutableSpan<float> r_radius = get_r_radius(param++); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + const float4 p = float4(vector[i].x, vector[i].y, vector[i].z, w[i]) * scale[i]; + noise::voronoi_n_sphere_radius(p, rand, &r_radius[i]); + } + break; + } + } + break; + } + } + } + + ExecutionHints get_execution_hints() const override + { + return voronoi_execution_hints; + } +}; + +static void sh_node_voronoi_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder) +{ + const NodeTexVoronoi &storage = node_storage(builder.node()); + bool minowski = + (storage.distance == SHD_VORONOI_MINKOWSKI && storage.dimensions != 1 && + !ELEM(storage.feature, SHD_VORONOI_DISTANCE_TO_EDGE, SHD_VORONOI_N_SPHERE_RADIUS)); + bool dist_radius = ELEM( + storage.feature, SHD_VORONOI_DISTANCE_TO_EDGE, SHD_VORONOI_N_SPHERE_RADIUS); + if (dist_radius) { + builder.construct_and_set_matching_fn<VoronoiEdgeFunction>(storage.dimensions, + storage.feature); + } + else if (minowski) { + builder.construct_and_set_matching_fn<VoronoiMinowskiFunction>(storage.dimensions, + storage.feature); + } + else { + builder.construct_and_set_matching_fn<VoronoiMetricFunction>( + storage.dimensions, storage.feature, storage.distance); + } } -void register_node_type_sh_tex_voronoi(void) +} // namespace blender::nodes::node_shader_tex_voronoi_cc + +void register_node_type_sh_tex_voronoi() { + namespace file_ns = blender::nodes::node_shader_tex_voronoi_cc; + static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_TEX_VORONOI, "Voronoi Texture", NODE_CLASS_TEXTURE, 0); - ntype.declare = blender::nodes::sh_node_tex_voronoi_declare; - node_type_init(&ntype, node_shader_init_tex_voronoi); + sh_fn_node_type_base(&ntype, SH_NODE_TEX_VORONOI, "Voronoi Texture", NODE_CLASS_TEXTURE); + ntype.declare = file_ns::sh_node_tex_voronoi_declare; + ntype.draw_buttons = file_ns::node_shader_buts_tex_voronoi; + node_type_init(&ntype, file_ns::node_shader_init_tex_voronoi); node_type_storage( &ntype, "NodeTexVoronoi", node_free_standard_storage, node_copy_standard_storage); - node_type_gpu(&ntype, node_shader_gpu_tex_voronoi); - node_type_update(&ntype, node_shader_update_tex_voronoi); + node_type_gpu(&ntype, file_ns::node_shader_gpu_tex_voronoi); + node_type_update(&ntype, file_ns::node_shader_update_tex_voronoi); + ntype.build_multi_function = file_ns::sh_node_voronoi_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_wave.c b/source/blender/nodes/shader/nodes/node_shader_tex_wave.c deleted file mode 100644 index bba568ed5b7..00000000000 --- a/source/blender/nodes/shader/nodes/node_shader_tex_wave.c +++ /dev/null @@ -1,101 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2005 Blender Foundation. - * All rights reserved. - */ - -#include "../node_shader_util.h" - -/* **************** WAVE ******************** */ - -static bNodeSocketTemplate sh_node_tex_wave_in[] = { - {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, - {SOCK_FLOAT, N_("Scale"), 5.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f}, - {SOCK_FLOAT, N_("Distortion"), 0.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f}, - {SOCK_FLOAT, N_("Detail"), 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, 16.0f}, - {SOCK_FLOAT, N_("Detail Scale"), 1.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f}, - {SOCK_FLOAT, N_("Detail Roughness"), 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_FLOAT, N_("Phase Offset"), 0.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f}, - {-1, ""}, -}; - -static bNodeSocketTemplate sh_node_tex_wave_out[] = { - {SOCK_RGBA, N_("Color"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_NO_INTERNAL_LINK}, - {SOCK_FLOAT, - N_("Fac"), - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 1.0f, - PROP_FACTOR, - SOCK_NO_INTERNAL_LINK}, - {-1, ""}, -}; - -static void node_shader_init_tex_wave(bNodeTree *UNUSED(ntree), bNode *node) -{ - NodeTexWave *tex = MEM_callocN(sizeof(NodeTexWave), "NodeTexWave"); - BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT); - BKE_texture_colormapping_default(&tex->base.color_mapping); - tex->wave_type = SHD_WAVE_BANDS; - tex->bands_direction = SHD_WAVE_BANDS_DIRECTION_X; - tex->rings_direction = SHD_WAVE_RINGS_DIRECTION_X; - tex->wave_profile = SHD_WAVE_PROFILE_SIN; - node->storage = tex; -} - -static int node_shader_gpu_tex_wave(GPUMaterial *mat, - bNode *node, - bNodeExecData *UNUSED(execdata), - GPUNodeStack *in, - GPUNodeStack *out) -{ - node_shader_gpu_default_tex_coord(mat, node, &in[0].link); - node_shader_gpu_tex_mapping(mat, node, in, out); - - NodeTexWave *tex = (NodeTexWave *)node->storage; - float wave_type = tex->wave_type; - float bands_direction = tex->bands_direction; - float rings_direction = tex->rings_direction; - float wave_profile = tex->wave_profile; - - return GPU_stack_link(mat, - node, - "node_tex_wave", - in, - out, - GPU_constant(&wave_type), - GPU_constant(&bands_direction), - GPU_constant(&rings_direction), - GPU_constant(&wave_profile)); -} - -/* node type definition */ -void register_node_type_sh_tex_wave(void) -{ - static bNodeType ntype; - - sh_node_type_base(&ntype, SH_NODE_TEX_WAVE, "Wave Texture", NODE_CLASS_TEXTURE, 0); - node_type_socket_templates(&ntype, sh_node_tex_wave_in, sh_node_tex_wave_out); - node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); - node_type_init(&ntype, node_shader_init_tex_wave); - node_type_storage(&ntype, "NodeTexWave", node_free_standard_storage, node_copy_standard_storage); - node_type_gpu(&ntype, node_shader_gpu_tex_wave); - - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_wave.cc b/source/blender/nodes/shader/nodes/node_shader_tex_wave.cc new file mode 100644 index 00000000000..fc6c66061ff --- /dev/null +++ b/source/blender/nodes/shader/nodes/node_shader_tex_wave.cc @@ -0,0 +1,250 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2005 Blender Foundation. + * All rights reserved. + */ + +#include "node_shader_util.hh" + +#include "BLI_noise.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes::node_shader_tex_wave_cc { + +static void sh_node_tex_wave_declare(NodeDeclarationBuilder &b) +{ + b.is_function_node(); + b.add_input<decl::Vector>(N_("Vector")).implicit_field(); + b.add_input<decl::Float>(N_("Scale")).min(-1000.0f).max(1000.0f).default_value(5.0f); + b.add_input<decl::Float>(N_("Distortion")).min(-1000.0f).max(1000.0f).default_value(0.0f); + b.add_input<decl::Float>(N_("Detail")).min(0.0f).max(15.0f).default_value(2.0f); + b.add_input<decl::Float>(N_("Detail Scale")).min(-1000.0f).max(1000.0f).default_value(1.0f); + b.add_input<decl::Float>(N_("Detail Roughness")) + .min(0.0f) + .max(1.0f) + .default_value(0.5f) + .subtype(PROP_FACTOR); + b.add_input<decl::Float>(N_("Phase Offset")).min(-1000.0f).max(1000.0f).default_value(0.0f); + b.add_output<decl::Color>(N_("Color")).no_muted_links(); + b.add_output<decl::Float>(N_("Fac")).no_muted_links(); +} + +static void node_shader_buts_tex_wave(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "wave_type", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); + int type = RNA_enum_get(ptr, "wave_type"); + if (type == SHD_WAVE_BANDS) { + uiItemR(layout, ptr, "bands_direction", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); + } + else { /* SHD_WAVE_RINGS */ + uiItemR(layout, ptr, "rings_direction", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); + } + + uiItemR(layout, ptr, "wave_profile", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); +} + +static void node_shader_init_tex_wave(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeTexWave *tex = MEM_cnew<NodeTexWave>(__func__); + BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT); + BKE_texture_colormapping_default(&tex->base.color_mapping); + tex->wave_type = SHD_WAVE_BANDS; + tex->bands_direction = SHD_WAVE_BANDS_DIRECTION_X; + tex->rings_direction = SHD_WAVE_RINGS_DIRECTION_X; + tex->wave_profile = SHD_WAVE_PROFILE_SIN; + node->storage = tex; +} + +static int node_shader_gpu_tex_wave(GPUMaterial *mat, + bNode *node, + bNodeExecData *UNUSED(execdata), + GPUNodeStack *in, + GPUNodeStack *out) +{ + node_shader_gpu_default_tex_coord(mat, node, &in[0].link); + node_shader_gpu_tex_mapping(mat, node, in, out); + + NodeTexWave *tex = (NodeTexWave *)node->storage; + float wave_type = tex->wave_type; + float bands_direction = tex->bands_direction; + float rings_direction = tex->rings_direction; + float wave_profile = tex->wave_profile; + + return GPU_stack_link(mat, + node, + "node_tex_wave", + in, + out, + GPU_constant(&wave_type), + GPU_constant(&bands_direction), + GPU_constant(&rings_direction), + GPU_constant(&wave_profile)); +} + +class WaveFunction : public fn::MultiFunction { + private: + int wave_type_; + int bands_direction_; + int rings_direction_; + int wave_profile_; + + public: + WaveFunction(int wave_type, int bands_direction, int rings_direction, int wave_profile) + : wave_type_(wave_type), + bands_direction_(bands_direction), + rings_direction_(rings_direction), + wave_profile_(wave_profile) + { + static fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static fn::MFSignature create_signature() + { + fn::MFSignatureBuilder signature{"MagicFunction"}; + signature.single_input<float3>("Vector"); + signature.single_input<float>("Scale"); + signature.single_input<float>("Distortion"); + signature.single_input<float>("Detail"); + signature.single_input<float>("Detail Scale"); + signature.single_input<float>("Detail Roughness"); + signature.single_input<float>("Phase Offset"); + signature.single_output<ColorGeometry4f>("Color"); + signature.single_output<float>("Fac"); + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + const VArray<float3> &vector = params.readonly_single_input<float3>(0, "Vector"); + const VArray<float> &scale = params.readonly_single_input<float>(1, "Scale"); + const VArray<float> &distortion = params.readonly_single_input<float>(2, "Distortion"); + const VArray<float> &detail = params.readonly_single_input<float>(3, "Detail"); + const VArray<float> &dscale = params.readonly_single_input<float>(4, "Detail Scale"); + const VArray<float> &droughness = params.readonly_single_input<float>(5, "Detail Roughness"); + const VArray<float> &phase = params.readonly_single_input<float>(6, "Phase Offset"); + + MutableSpan<ColorGeometry4f> r_color = + params.uninitialized_single_output_if_required<ColorGeometry4f>(7, "Color"); + MutableSpan<float> r_fac = params.uninitialized_single_output<float>(8, "Fac"); + + for (int64_t i : mask) { + + float3 p = vector[i] * scale[i]; + /* Prevent precision issues on unit coordinates. */ + p = (p + 0.000001f) * 0.999999f; + + float n = 0.0f; + float val = 0.0f; + + switch (wave_type_) { + case SHD_WAVE_BANDS: + switch (bands_direction_) { + case SHD_WAVE_BANDS_DIRECTION_X: + n = p.x * 20.0f; + break; + case SHD_WAVE_BANDS_DIRECTION_Y: + n = p.y * 20.0f; + break; + case SHD_WAVE_BANDS_DIRECTION_Z: + n = p.z * 20.0f; + break; + case SHD_WAVE_BANDS_DIRECTION_DIAGONAL: + n = (p.x + p.y + p.z) * 10.0f; + break; + } + break; + case SHD_WAVE_RINGS: + float3 rp = p; + switch (rings_direction_) { + case SHD_WAVE_RINGS_DIRECTION_X: + rp *= float3(0.0f, 1.0f, 1.0f); + break; + case SHD_WAVE_RINGS_DIRECTION_Y: + rp *= float3(1.0f, 0.0f, 1.0f); + break; + case SHD_WAVE_RINGS_DIRECTION_Z: + rp *= float3(1.0f, 1.0f, 0.0f); + break; + case SHD_WAVE_RINGS_DIRECTION_SPHERICAL: + /* Ignore. */ + break; + } + n = len_v3(rp) * 20.0f; + break; + } + + n += phase[i]; + + if (distortion[i] != 0.0f) { + n += distortion[i] * + (noise::perlin_fractal(p * dscale[i], detail[i], droughness[i]) * 2.0f - 1.0f); + } + + switch (wave_profile_) { + case SHD_WAVE_PROFILE_SIN: + val = 0.5f + 0.5f * sinf(n - M_PI_2); + break; + case SHD_WAVE_PROFILE_SAW: + n /= M_PI * 2.0f; + val = n - floorf(n); + break; + case SHD_WAVE_PROFILE_TRI: + n /= M_PI * 2.0f; + val = fabsf(n - floorf(n + 0.5f)) * 2.0f; + break; + } + + r_fac[i] = val; + } + if (!r_color.is_empty()) { + for (int64_t i : mask) { + r_color[i] = ColorGeometry4f(r_fac[i], r_fac[i], r_fac[i], 1.0f); + } + } + } +}; + +static void sh_node_wave_tex_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) +{ + 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); +} + +} // namespace blender::nodes::node_shader_tex_wave_cc + +void register_node_type_sh_tex_wave() +{ + namespace file_ns = blender::nodes::node_shader_tex_wave_cc; + + static bNodeType ntype; + + sh_fn_node_type_base(&ntype, SH_NODE_TEX_WAVE, "Wave Texture", NODE_CLASS_TEXTURE); + ntype.declare = file_ns::sh_node_tex_wave_declare; + ntype.draw_buttons = file_ns::node_shader_buts_tex_wave; + node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); + node_type_init(&ntype, file_ns::node_shader_init_tex_wave); + node_type_storage(&ntype, "NodeTexWave", node_free_standard_storage, node_copy_standard_storage); + node_type_gpu(&ntype, file_ns::node_shader_gpu_tex_wave); + ntype.build_multi_function = file_ns::sh_node_wave_tex_build_multi_function; + + nodeRegisterType(&ntype); +} 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 03543e5f7fe..3a5bc98896c 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 @@ -17,20 +17,31 @@ * All rights reserved. */ -#include "../node_shader_util.h" +#include "node_shader_util.hh" -namespace blender::nodes { +#include "BLI_noise.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes::node_shader_tex_white_noise_cc { static void sh_node_tex_white_noise_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input<decl::Vector>("Vector").min(-10000.0f).max(10000.0f); - b.add_input<decl::Float>("W").min(-10000.0f).max(10000.0f); - b.add_output<decl::Float>("Value"); - b.add_output<decl::Color>("Color"); -}; + b.add_input<decl::Vector>(N_("Vector")).min(-10000.0f).max(10000.0f).implicit_field(); + b.add_input<decl::Float>(N_("W")).min(-10000.0f).max(10000.0f).make_available([](bNode &node) { + /* Default to 1 instead of 4, because it is faster. */ + node.custom1 = 1; + }); + b.add_output<decl::Float>(N_("Value")); + b.add_output<decl::Color>(N_("Color")); +} -} // namespace blender::nodes +static void node_shader_buts_white_noise(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "noise_dimensions", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); +} static void node_shader_init_tex_white_noise(bNodeTree *UNUSED(ntree), bNode *node) { @@ -56,24 +67,150 @@ static int gpu_shader_tex_white_noise(GPUMaterial *mat, return GPU_stack_link(mat, node, name, in, out); } -static void node_shader_update_tex_white_noise(bNodeTree *UNUSED(ntree), bNode *node) +static void node_shader_update_tex_white_noise(bNodeTree *ntree, bNode *node) { bNodeSocket *sockVector = nodeFindSocket(node, SOCK_IN, "Vector"); bNodeSocket *sockW = nodeFindSocket(node, SOCK_IN, "W"); - nodeSetSocketAvailability(sockVector, node->custom1 != 1); - nodeSetSocketAvailability(sockW, node->custom1 == 1 || node->custom1 == 4); + nodeSetSocketAvailability(ntree, sockVector, node->custom1 != 1); + nodeSetSocketAvailability(ntree, sockW, node->custom1 == 1 || node->custom1 == 4); +} + +class WhiteNoiseFunction : public fn::MultiFunction { + private: + int dimensions_; + + public: + WhiteNoiseFunction(int dimensions) : dimensions_(dimensions) + { + BLI_assert(dimensions >= 1 && dimensions <= 4); + static std::array<fn::MFSignature, 4> signatures{ + create_signature(1), + create_signature(2), + create_signature(3), + create_signature(4), + }; + this->set_signature(&signatures[dimensions - 1]); + } + + static fn::MFSignature create_signature(int dimensions) + { + fn::MFSignatureBuilder signature{"WhiteNoise"}; + + if (ELEM(dimensions, 2, 3, 4)) { + signature.single_input<float3>("Vector"); + } + if (ELEM(dimensions, 1, 4)) { + signature.single_input<float>("W"); + } + + signature.single_output<float>("Value"); + signature.single_output<ColorGeometry4f>("Color"); + + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + int param = ELEM(dimensions_, 2, 3, 4) + ELEM(dimensions_, 1, 4); + + MutableSpan<float> r_value = params.uninitialized_single_output_if_required<float>(param++, + "Value"); + MutableSpan<ColorGeometry4f> r_color = + params.uninitialized_single_output_if_required<ColorGeometry4f>(param++, "Color"); + + const bool compute_value = !r_value.is_empty(); + const bool compute_color = !r_color.is_empty(); + + switch (dimensions_) { + case 1: { + const VArray<float> &w = params.readonly_single_input<float>(0, "W"); + if (compute_color) { + for (int64_t i : mask) { + const float3 c = noise::hash_float_to_float3(w[i]); + r_color[i] = ColorGeometry4f(c[0], c[1], c[2], 1.0f); + } + } + if (compute_value) { + for (int64_t i : mask) { + r_value[i] = noise::hash_float_to_float(w[i]); + } + } + break; + } + case 2: { + const VArray<float3> &vector = params.readonly_single_input<float3>(0, "Vector"); + if (compute_color) { + for (int64_t i : mask) { + const float3 c = noise::hash_float_to_float3(float2(vector[i].x, vector[i].y)); + r_color[i] = ColorGeometry4f(c[0], c[1], c[2], 1.0f); + } + } + if (compute_value) { + for (int64_t i : mask) { + r_value[i] = noise::hash_float_to_float(float2(vector[i].x, vector[i].y)); + } + } + break; + } + case 3: { + const VArray<float3> &vector = params.readonly_single_input<float3>(0, "Vector"); + if (compute_color) { + for (int64_t i : mask) { + const float3 c = noise::hash_float_to_float3(vector[i]); + r_color[i] = ColorGeometry4f(c[0], c[1], c[2], 1.0f); + } + } + if (compute_value) { + for (int64_t i : mask) { + r_value[i] = noise::hash_float_to_float(vector[i]); + } + } + break; + } + case 4: { + const VArray<float3> &vector = params.readonly_single_input<float3>(0, "Vector"); + const VArray<float> &w = params.readonly_single_input<float>(1, "W"); + if (compute_color) { + for (int64_t i : mask) { + const float3 c = noise::hash_float_to_float3( + float4(vector[i].x, vector[i].y, vector[i].z, w[i])); + r_color[i] = ColorGeometry4f(c[0], c[1], c[2], 1.0f); + } + } + if (compute_value) { + for (int64_t i : mask) { + r_value[i] = noise::hash_float_to_float( + float4(vector[i].x, vector[i].y, vector[i].z, w[i])); + } + } + break; + } + } + } +}; + +static void sh_node_noise_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder) +{ + bNode &node = builder.node(); + builder.construct_and_set_matching_fn<WhiteNoiseFunction>((int)node.custom1); } -void register_node_type_sh_tex_white_noise(void) +} // namespace blender::nodes::node_shader_tex_white_noise_cc + +void register_node_type_sh_tex_white_noise() { + namespace file_ns = blender::nodes::node_shader_tex_white_noise_cc; + static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_TEX_WHITE_NOISE, "White Noise Texture", NODE_CLASS_TEXTURE, 0); - ntype.declare = blender::nodes::sh_node_tex_white_noise_declare; - node_type_init(&ntype, node_shader_init_tex_white_noise); - node_type_gpu(&ntype, gpu_shader_tex_white_noise); - node_type_update(&ntype, node_shader_update_tex_white_noise); + sh_fn_node_type_base(&ntype, SH_NODE_TEX_WHITE_NOISE, "White Noise Texture", NODE_CLASS_TEXTURE); + ntype.declare = file_ns::sh_node_tex_white_noise_declare; + ntype.draw_buttons = file_ns::node_shader_buts_white_noise; + node_type_init(&ntype, file_ns::node_shader_init_tex_white_noise); + node_type_gpu(&ntype, file_ns::gpu_shader_tex_white_noise); + node_type_update(&ntype, file_ns::node_shader_update_tex_white_noise); + ntype.build_multi_function = file_ns::sh_node_noise_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_uvAlongStroke.c b/source/blender/nodes/shader/nodes/node_shader_uv_along_stroke.cc index 05c3248af65..382a0f16ecd 100644 --- a/source/blender/nodes/shader/nodes/node_shader_uvAlongStroke.c +++ b/source/blender/nodes/shader/nodes/node_shader_uv_along_stroke.cc @@ -17,23 +17,35 @@ * All rights reserved. */ -#include "../node_shader_util.h" +#include "node_shader_util.hh" -/* **************** OUTPUT ******************** */ +#include "UI_interface.h" +#include "UI_resources.h" -static bNodeSocketTemplate sh_node_uvalongstroke_out[] = { - {SOCK_VECTOR, N_("UV"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {-1, ""}, -}; +namespace blender::nodes::node_shader_uv_along_stroke_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_output<decl::Vector>(N_("UV")); +} + +static void node_shader_buts_uvalongstroke(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "use_tips", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, 0); +} + +} // namespace blender::nodes::node_shader_uv_along_stroke_cc /* node type definition */ -void register_node_type_sh_uvalongstroke(void) +void register_node_type_sh_uvalongstroke() { + namespace file_ns = blender::nodes::node_shader_uv_along_stroke_cc; + static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_UVALONGSTROKE, "UV Along Stroke", NODE_CLASS_INPUT, 0); - node_type_socket_templates(&ntype, NULL, sh_node_uvalongstroke_out); - node_type_init(&ntype, NULL); + sh_node_type_base(&ntype, SH_NODE_UVALONGSTROKE, "UV Along Stroke", NODE_CLASS_INPUT); + ntype.declare = file_ns::node_declare; + ntype.draw_buttons = file_ns::node_shader_buts_uvalongstroke; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_uvmap.c b/source/blender/nodes/shader/nodes/node_shader_uvmap.cc index 775b8ffbc06..b004be75188 100644 --- a/source/blender/nodes/shader/nodes/node_shader_uvmap.c +++ b/source/blender/nodes/shader/nodes/node_shader_uvmap.cc @@ -17,20 +17,39 @@ * All rights reserved. */ -#include "../node_shader_util.h" +#include "node_shader_util.hh" + +#include "BKE_context.h" #include "DNA_customdata_types.h" -/* **************** OUTPUT ******************** */ +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes::node_shader_uvmap_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_output<decl::Vector>(N_("UV")); +} -static bNodeSocketTemplate sh_node_uvmap_out[] = { - {SOCK_VECTOR, N_("UV"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {-1, ""}, -}; +static void node_shader_buts_uvmap(uiLayout *layout, bContext *C, PointerRNA *ptr) +{ + uiItemR(layout, ptr, "from_instancer", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, 0); + + if (!RNA_boolean_get(ptr, "from_instancer")) { + PointerRNA obptr = CTX_data_pointer_get(C, "active_object"); + + if (obptr.data && RNA_enum_get(&obptr, "type") == OB_MESH) { + PointerRNA dataptr = RNA_pointer_get(&obptr, "data"); + uiItemPointerR(layout, ptr, "uv_map", &dataptr, "uv_layers", "", ICON_NONE); + } + } +} static void node_shader_init_uvmap(bNodeTree *UNUSED(ntree), bNode *node) { - NodeShaderUVMap *attr = MEM_callocN(sizeof(NodeShaderUVMap), "NodeShaderUVMap"); + NodeShaderUVMap *attr = MEM_cnew<NodeShaderUVMap>("NodeShaderUVMap"); node->storage = attr; } @@ -40,7 +59,7 @@ static int node_shader_gpu_uvmap(GPUMaterial *mat, GPUNodeStack *in, GPUNodeStack *out) { - NodeShaderUVMap *attr = node->storage; + NodeShaderUVMap *attr = static_cast<NodeShaderUVMap *>(node->storage); GPUNodeLink *mtface = GPU_attribute(mat, CD_MTFACE, attr->uv_map); GPU_stack_link(mat, node, "node_uvmap", in, out, mtface); @@ -50,18 +69,23 @@ static int node_shader_gpu_uvmap(GPUMaterial *mat, return 1; } +} // namespace blender::nodes::node_shader_uvmap_cc + /* node type definition */ -void register_node_type_sh_uvmap(void) +void register_node_type_sh_uvmap() { + namespace file_ns = blender::nodes::node_shader_uvmap_cc; + static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_UVMAP, "UV Map", NODE_CLASS_INPUT, 0); - node_type_socket_templates(&ntype, NULL, sh_node_uvmap_out); + sh_node_type_base(&ntype, SH_NODE_UVMAP, "UV Map", NODE_CLASS_INPUT); + ntype.declare = file_ns::node_declare; + ntype.draw_buttons = file_ns::node_shader_buts_uvmap; node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); - node_type_init(&ntype, node_shader_init_uvmap); + node_type_init(&ntype, file_ns::node_shader_init_uvmap); node_type_storage( &ntype, "NodeShaderUVMap", node_free_standard_storage, node_copy_standard_storage); - node_type_gpu(&ntype, node_shader_gpu_uvmap); + node_type_gpu(&ntype, file_ns::node_shader_gpu_uvmap); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_value.cc b/source/blender/nodes/shader/nodes/node_shader_value.cc index 1344ce5c5d9..265f03e6e88 100644 --- a/source/blender/nodes/shader/nodes/node_shader_value.cc +++ b/source/blender/nodes/shader/nodes/node_shader_value.cc @@ -21,16 +21,14 @@ * \ingroup shdnodes */ -#include "node_shader_util.h" +#include "node_shader_util.hh" -namespace blender::nodes { +namespace blender::nodes::node_shader_value_cc { static void sh_node_value_declare(NodeDeclarationBuilder &b) { - b.add_output<decl::Float>("Value"); -}; - -} // namespace blender::nodes + b.add_output<decl::Float>(N_("Value")); +} static int gpu_shader_value(GPUMaterial *mat, bNode *node, @@ -49,14 +47,18 @@ static void sh_node_value_build_multi_function(blender::nodes::NodeMultiFunction builder.construct_and_set_matching_fn<blender::fn::CustomMF_Constant<float>>(value->value); } -void register_node_type_sh_value(void) +} // namespace blender::nodes::node_shader_value_cc + +void register_node_type_sh_value() { + namespace file_ns = blender::nodes::node_shader_value_cc; + static bNodeType ntype; - sh_fn_node_type_base(&ntype, SH_NODE_VALUE, "Value", NODE_CLASS_INPUT, 0); - ntype.declare = blender::nodes::sh_node_value_declare; - node_type_gpu(&ntype, gpu_shader_value); - ntype.build_multi_function = sh_node_value_build_multi_function; + sh_fn_node_type_base(&ntype, SH_NODE_VALUE, "Value", NODE_CLASS_INPUT); + ntype.declare = file_ns::sh_node_value_declare; + node_type_gpu(&ntype, file_ns::gpu_shader_value); + ntype.build_multi_function = file_ns::sh_node_value_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_vector_displacement.c b/source/blender/nodes/shader/nodes/node_shader_vector_displacement.cc index 2b69e781e30..d34ef29c30a 100644 --- a/source/blender/nodes/shader/nodes/node_shader_vector_displacement.c +++ b/source/blender/nodes/shader/nodes/node_shader_vector_displacement.cc @@ -17,21 +17,17 @@ * All rights reserved. */ -#include "../node_shader_util.h" +#include "node_shader_util.hh" -/* **************** OUTPUT ******************** */ +namespace blender::nodes::node_shader_vector_displacement_cc { -static bNodeSocketTemplate sh_node_vector_displacement_in[] = { - {SOCK_RGBA, N_("Vector"), 0.00f, 0.0f, 0.0f, 0.0f, 0.0f, 1000.0f, PROP_NONE, SOCK_HIDE_VALUE}, - {SOCK_FLOAT, N_("Midlevel"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1000.0f}, - {SOCK_FLOAT, N_("Scale"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1000.0f}, - {-1, ""}, -}; - -static bNodeSocketTemplate sh_node_vector_displacement_out[] = { - {SOCK_VECTOR, N_("Displacement"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {-1, ""}, -}; +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Color>(N_("Vector")).hide_value(); + b.add_input<decl::Float>(N_("Midlevel")).default_value(0.0f).min(0.0f).max(1000.0f); + b.add_input<decl::Float>(N_("Scale")).default_value(1.0f).min(0.0f).max(1000.0f); + b.add_output<decl::Vector>(N_("Displacement")); +} static void node_shader_init_vector_displacement(bNodeTree *UNUSED(ntree), bNode *node) { @@ -60,18 +56,20 @@ static int gpu_shader_vector_displacement(GPUMaterial *mat, } } +} // namespace blender::nodes::node_shader_vector_displacement_cc + /* node type definition */ -void register_node_type_sh_vector_displacement(void) +void register_node_type_sh_vector_displacement() { + namespace file_ns = blender::nodes::node_shader_vector_displacement_cc; + static bNodeType ntype; sh_node_type_base( - &ntype, SH_NODE_VECTOR_DISPLACEMENT, "Vector Displacement", NODE_CLASS_OP_VECTOR, 0); - node_type_socket_templates( - &ntype, sh_node_vector_displacement_in, sh_node_vector_displacement_out); - node_type_storage(&ntype, "", NULL, NULL); - node_type_init(&ntype, node_shader_init_vector_displacement); - node_type_gpu(&ntype, gpu_shader_vector_displacement); + &ntype, SH_NODE_VECTOR_DISPLACEMENT, "Vector Displacement", NODE_CLASS_OP_VECTOR); + ntype.declare = file_ns::node_declare; + node_type_init(&ntype, file_ns::node_shader_init_vector_displacement); + node_type_gpu(&ntype, file_ns::gpu_shader_vector_displacement); nodeRegisterType(&ntype); } 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 f49ff06cef1..591734c7dd6 100644 --- a/source/blender/nodes/shader/nodes/node_shader_vector_math.cc +++ b/source/blender/nodes/shader/nodes/node_shader_vector_math.cc @@ -21,24 +21,74 @@ * \ingroup shdnodes */ -#include "node_shader_util.h" +#include "node_shader_util.hh" #include "NOD_math_functions.hh" +#include "NOD_socket_search_link.hh" -namespace blender::nodes { +#include "RNA_enum_types.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes::node_shader_vector_math_cc { static void sh_node_vector_math_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input<decl::Vector>("Vector").min(-10000.0f).max(10000.0f); - b.add_input<decl::Vector>("Vector", "Vector_001").min(-10000.0f).max(10000.0f); - b.add_input<decl::Vector>("Vector", "Vector_002").min(-10000.0f).max(10000.0f); - b.add_input<decl::Float>("Scale").default_value(1.0f).min(-10000.0f).max(10000.0f); - b.add_output<decl::Vector>("Vector"); - b.add_output<decl::Float>("Value"); + b.add_input<decl::Vector>(N_("Vector")).min(-10000.0f).max(10000.0f); + b.add_input<decl::Vector>(N_("Vector"), "Vector_001").min(-10000.0f).max(10000.0f); + b.add_input<decl::Vector>(N_("Vector"), "Vector_002").min(-10000.0f).max(10000.0f); + b.add_input<decl::Float>(N_("Scale")).default_value(1.0f).min(-10000.0f).max(10000.0f); + b.add_output<decl::Vector>(N_("Vector")); + b.add_output<decl::Float>(N_("Value")); +} + +static void node_shader_buts_vect_math(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "operation", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); +} + +class SocketSearchOp { + public: + std::string socket_name; + NodeVectorMathOperation mode = NODE_VECTOR_MATH_ADD; + void operator()(LinkSearchOpParams ¶ms) + { + bNode &node = params.add_node("ShaderNodeVectorMath"); + node.custom1 = mode; + params.update_and_connect_available_socket(node, socket_name); + } }; -} // namespace blender::nodes +static void sh_node_vector_math_gather_link_searches(GatherLinkSearchOpParams ¶ms) +{ + if (!params.node_tree().typeinfo->validate_link( + static_cast<eNodeSocketDatatype>(params.other_socket().type), SOCK_VECTOR)) { + return; + } + + const int weight = ELEM(params.other_socket().type, SOCK_VECTOR, SOCK_RGBA) ? 0 : -1; + + for (const EnumPropertyItem *item = rna_enum_node_vec_math_items; item->identifier != nullptr; + item++) { + if (item->name != nullptr && item->identifier[0] != '\0') { + if ((params.in_out() == SOCK_OUT) && ELEM(item->value, + NODE_VECTOR_MATH_LENGTH, + NODE_VECTOR_MATH_DISTANCE, + NODE_VECTOR_MATH_DOT_PRODUCT)) { + params.add_item(IFACE_(item->name), + SocketSearchOp{"Value", (NodeVectorMathOperation)item->value}, + weight); + } + else { + params.add_item(IFACE_(item->name), + SocketSearchOp{"Vector", (NodeVectorMathOperation)item->value}, + weight); + } + } + } +} static const char *gpu_shader_get_name(int mode) { @@ -119,7 +169,7 @@ static int gpu_shader_vector_math(GPUMaterial *mat, return 0; } -static void node_shader_update_vector_math(bNodeTree *UNUSED(ntree), bNode *node) +static void node_shader_update_vector_math(bNodeTree *ntree, bNode *node) { bNodeSocket *sockB = (bNodeSocket *)BLI_findlink(&node->inputs, 1); bNodeSocket *sockC = (bNodeSocket *)BLI_findlink(&node->inputs, 2); @@ -128,7 +178,8 @@ static void node_shader_update_vector_math(bNodeTree *UNUSED(ntree), bNode *node bNodeSocket *sockVector = nodeFindSocket(node, SOCK_OUT, "Vector"); bNodeSocket *sockValue = nodeFindSocket(node, SOCK_OUT, "Value"); - nodeSetSocketAvailability(sockB, + nodeSetSocketAvailability(ntree, + sockB, !ELEM(node->custom1, NODE_VECTOR_MATH_SINE, NODE_VECTOR_MATH_COSINE, @@ -140,19 +191,22 @@ static void node_shader_update_vector_math(bNodeTree *UNUSED(ntree), bNode *node NODE_VECTOR_MATH_ABSOLUTE, NODE_VECTOR_MATH_FRACTION, NODE_VECTOR_MATH_NORMALIZE)); - nodeSetSocketAvailability(sockC, + nodeSetSocketAvailability(ntree, + sockC, ELEM(node->custom1, NODE_VECTOR_MATH_WRAP, NODE_VECTOR_MATH_FACEFORWARD, NODE_VECTOR_MATH_MULTIPLY_ADD)); - nodeSetSocketAvailability(sockScale, - ELEM(node->custom1, NODE_VECTOR_MATH_SCALE, NODE_VECTOR_MATH_REFRACT)); - nodeSetSocketAvailability(sockVector, + nodeSetSocketAvailability( + ntree, sockScale, ELEM(node->custom1, NODE_VECTOR_MATH_SCALE, NODE_VECTOR_MATH_REFRACT)); + nodeSetSocketAvailability(ntree, + sockVector, !ELEM(node->custom1, NODE_VECTOR_MATH_LENGTH, NODE_VECTOR_MATH_DISTANCE, NODE_VECTOR_MATH_DOT_PRODUCT)); - nodeSetSocketAvailability(sockValue, + nodeSetSocketAvailability(ntree, + sockValue, ELEM(node->custom1, NODE_VECTOR_MATH_LENGTH, NODE_VECTOR_MATH_DISTANCE, @@ -197,8 +251,8 @@ static const blender::fn::MultiFunction *get_multi_function(bNode &node) blender::nodes::try_dispatch_float_math_fl3_fl3_to_fl3( operation, [&](auto function, const blender::nodes::FloatMathOperationInfo &info) { - static blender::fn::CustomMF_SI_SI_SO<float3, float3, float3> fn{info.title_case_name, - function}; + static blender::fn::CustomMF_SI_SI_SO<float3, float3, float3> fn{ + info.title_case_name.c_str(), function}; multi_fn = &fn; }); if (multi_fn != nullptr) { @@ -208,7 +262,7 @@ static const blender::fn::MultiFunction *get_multi_function(bNode &node) blender::nodes::try_dispatch_float_math_fl3_fl3_fl3_to_fl3( operation, [&](auto function, const blender::nodes::FloatMathOperationInfo &info) { static blender::fn::CustomMF_SI_SI_SI_SO<float3, float3, float3, float3> fn{ - info.title_case_name, function}; + info.title_case_name.c_str(), function}; multi_fn = &fn; }); if (multi_fn != nullptr) { @@ -218,7 +272,7 @@ static const blender::fn::MultiFunction *get_multi_function(bNode &node) blender::nodes::try_dispatch_float_math_fl3_fl3_fl_to_fl3( operation, [&](auto function, const blender::nodes::FloatMathOperationInfo &info) { static blender::fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> fn{ - info.title_case_name, function}; + info.title_case_name.c_str(), function}; multi_fn = &fn; }); if (multi_fn != nullptr) { @@ -227,8 +281,8 @@ static const blender::fn::MultiFunction *get_multi_function(bNode &node) blender::nodes::try_dispatch_float_math_fl3_fl3_to_fl( operation, [&](auto function, const blender::nodes::FloatMathOperationInfo &info) { - static blender::fn::CustomMF_SI_SI_SO<float3, float3, float> fn{info.title_case_name, - function}; + static blender::fn::CustomMF_SI_SI_SO<float3, float3, float> fn{ + info.title_case_name.c_str(), function}; multi_fn = &fn; }); if (multi_fn != nullptr) { @@ -237,8 +291,8 @@ static const blender::fn::MultiFunction *get_multi_function(bNode &node) blender::nodes::try_dispatch_float_math_fl3_fl_to_fl3( operation, [&](auto function, const blender::nodes::FloatMathOperationInfo &info) { - static blender::fn::CustomMF_SI_SI_SO<float3, float, float3> fn{info.title_case_name, - function}; + static blender::fn::CustomMF_SI_SI_SO<float3, float, float3> fn{ + info.title_case_name.c_str(), function}; multi_fn = &fn; }); if (multi_fn != nullptr) { @@ -247,7 +301,8 @@ static const blender::fn::MultiFunction *get_multi_function(bNode &node) blender::nodes::try_dispatch_float_math_fl3_to_fl3( operation, [&](auto function, const blender::nodes::FloatMathOperationInfo &info) { - static blender::fn::CustomMF_SI_SO<float3, float3> fn{info.title_case_name, function}; + static blender::fn::CustomMF_SI_SO<float3, float3> fn{info.title_case_name.c_str(), + function}; multi_fn = &fn; }); if (multi_fn != nullptr) { @@ -256,7 +311,8 @@ static const blender::fn::MultiFunction *get_multi_function(bNode &node) blender::nodes::try_dispatch_float_math_fl3_to_fl( operation, [&](auto function, const blender::nodes::FloatMathOperationInfo &info) { - static blender::fn::CustomMF_SI_SO<float3, float> fn{info.title_case_name, function}; + static blender::fn::CustomMF_SI_SO<float3, float> fn{info.title_case_name.c_str(), + function}; multi_fn = &fn; }); if (multi_fn != nullptr) { @@ -273,16 +329,22 @@ static void sh_node_vector_math_build_multi_function( builder.set_matching_fn(fn); } -void register_node_type_sh_vect_math(void) +} // namespace blender::nodes::node_shader_vector_math_cc + +void register_node_type_sh_vect_math() { + namespace file_ns = blender::nodes::node_shader_vector_math_cc; + static bNodeType ntype; - sh_fn_node_type_base(&ntype, SH_NODE_VECTOR_MATH, "Vector Math", NODE_CLASS_OP_VECTOR, 0); - ntype.declare = blender::nodes::sh_node_vector_math_declare; - node_type_label(&ntype, node_vector_math_label); - node_type_gpu(&ntype, gpu_shader_vector_math); - node_type_update(&ntype, node_shader_update_vector_math); - ntype.build_multi_function = sh_node_vector_math_build_multi_function; + sh_fn_node_type_base(&ntype, SH_NODE_VECTOR_MATH, "Vector Math", NODE_CLASS_OP_VECTOR); + ntype.declare = file_ns::sh_node_vector_math_declare; + ntype.draw_buttons = file_ns::node_shader_buts_vect_math; + ntype.labelfunc = node_vector_math_label; + node_type_gpu(&ntype, file_ns::gpu_shader_vector_math); + node_type_update(&ntype, file_ns::node_shader_update_vector_math); + ntype.build_multi_function = file_ns::sh_node_vector_math_build_multi_function; + ntype.gather_link_search_ops = file_ns::sh_node_vector_math_gather_link_searches; nodeRegisterType(&ntype); } 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 c9b26fa5199..d8dcd028c56 100644 --- a/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc +++ b/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc @@ -21,22 +21,29 @@ * \ingroup shdnodes */ -#include "../node_shader_util.h" +#include "node_shader_util.hh" -namespace blender::nodes { +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes::node_shader_vector_rotate_cc { static void sh_node_vector_rotate_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input<decl::Vector>("Vector").min(0.0f).max(1.0f).hide_value(); - b.add_input<decl::Vector>("Center"); - b.add_input<decl::Vector>("Axis").min(-1.0f).max(1.0f).default_value({0.0f, 0.0f, 1.0f}); - b.add_input<decl::Float>("Angle").subtype(PROP_ANGLE); - b.add_input<decl::Vector>("Rotation").subtype(PROP_EULER); - b.add_output<decl::Vector>("Vector"); -}; + b.add_input<decl::Vector>(N_("Vector")).min(0.0f).max(1.0f).hide_value(); + b.add_input<decl::Vector>(N_("Center")); + b.add_input<decl::Vector>(N_("Axis")).min(-1.0f).max(1.0f).default_value({0.0f, 0.0f, 1.0f}); + b.add_input<decl::Float>(N_("Angle")).subtype(PROP_ANGLE); + b.add_input<decl::Vector>(N_("Rotation")).subtype(PROP_EULER); + b.add_output<decl::Vector>(N_("Vector")); +} -} // namespace blender::nodes +static void node_shader_buts_vector_rotate(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "rotation_type", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); + uiItemR(layout, ptr, "invert", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, 0); +} static const char *gpu_shader_get_name(int mode) { @@ -193,25 +200,32 @@ static void sh_node_vector_rotate_build_multi_function( builder.set_matching_fn(fn); } -static void node_shader_update_vector_rotate(bNodeTree *UNUSED(ntree), bNode *node) +static void node_shader_update_vector_rotate(bNodeTree *ntree, bNode *node) { bNodeSocket *sock_rotation = nodeFindSocket(node, SOCK_IN, "Rotation"); - nodeSetSocketAvailability(sock_rotation, ELEM(node->custom1, NODE_VECTOR_ROTATE_TYPE_EULER_XYZ)); + nodeSetSocketAvailability( + ntree, sock_rotation, ELEM(node->custom1, NODE_VECTOR_ROTATE_TYPE_EULER_XYZ)); bNodeSocket *sock_axis = nodeFindSocket(node, SOCK_IN, "Axis"); - nodeSetSocketAvailability(sock_axis, ELEM(node->custom1, NODE_VECTOR_ROTATE_TYPE_AXIS)); + nodeSetSocketAvailability(ntree, sock_axis, ELEM(node->custom1, NODE_VECTOR_ROTATE_TYPE_AXIS)); bNodeSocket *sock_angle = nodeFindSocket(node, SOCK_IN, "Angle"); - nodeSetSocketAvailability(sock_angle, !ELEM(node->custom1, NODE_VECTOR_ROTATE_TYPE_EULER_XYZ)); + nodeSetSocketAvailability( + ntree, sock_angle, !ELEM(node->custom1, NODE_VECTOR_ROTATE_TYPE_EULER_XYZ)); } -void register_node_type_sh_vector_rotate(void) +} // namespace blender::nodes::node_shader_vector_rotate_cc + +void register_node_type_sh_vector_rotate() { + namespace file_ns = blender::nodes::node_shader_vector_rotate_cc; + static bNodeType ntype; - sh_fn_node_type_base(&ntype, SH_NODE_VECTOR_ROTATE, "Vector Rotate", NODE_CLASS_OP_VECTOR, 0); - ntype.declare = blender::nodes::sh_node_vector_rotate_declare; - node_type_gpu(&ntype, gpu_shader_vector_rotate); - node_type_update(&ntype, node_shader_update_vector_rotate); - ntype.build_multi_function = sh_node_vector_rotate_build_multi_function; + sh_fn_node_type_base(&ntype, SH_NODE_VECTOR_ROTATE, "Vector Rotate", NODE_CLASS_OP_VECTOR); + ntype.declare = file_ns::sh_node_vector_rotate_declare; + ntype.draw_buttons = file_ns::node_shader_buts_vector_rotate; + node_type_gpu(&ntype, file_ns::gpu_shader_vector_rotate); + node_type_update(&ntype, file_ns::node_shader_update_vector_rotate); + ntype.build_multi_function = file_ns::sh_node_vector_rotate_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_vectTransform.c b/source/blender/nodes/shader/nodes/node_shader_vector_transform.cc index 41e61da60d7..a8a6902e30c 100644 --- a/source/blender/nodes/shader/nodes/node_shader_vectTransform.c +++ b/source/blender/nodes/shader/nodes/node_shader_vector_transform.cc @@ -21,23 +21,37 @@ * \ingroup shdnodes */ -#include "BLI_string.h" +#include "node_shader_util.hh" -#include "../node_shader_util.h" +#include "UI_interface.h" +#include "UI_resources.h" -/* **************** Vector Transform ******************** */ -static bNodeSocketTemplate sh_node_vect_transform_in[] = { - {SOCK_VECTOR, N_("Vector"), 0.5f, 0.5f, 0.5f, 1.0f, -10000.0f, 10000.0f, PROP_NONE}, {-1, ""}}; +namespace blender::nodes::node_shader_vector_transform_cc { -static bNodeSocketTemplate sh_node_vect_transform_out[] = { - {SOCK_VECTOR, N_("Vector")}, - {-1, ""}, -}; +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Vector>(N_("Vector")) + .default_value({0.5f, 0.5f, 0.5f}) + .min(-10000.0f) + .max(10000.0f); + b.add_output<decl::Vector>(N_("Vector")); +} + +static void node_shader_buts_vect_transform(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, + ptr, + "vector_type", + UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_EXPAND, + nullptr, + ICON_NONE); + uiItemR(layout, ptr, "convert_from", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); + uiItemR(layout, ptr, "convert_to", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); +} static void node_shader_init_vect_transform(bNodeTree *UNUSED(ntree), bNode *node) { - NodeShaderVectTransform *vect = MEM_callocN(sizeof(NodeShaderVectTransform), - "NodeShaderVectTransform"); + NodeShaderVectTransform *vect = MEM_cnew<NodeShaderVectTransform>("NodeShaderVectTransform"); /* Convert World into Object Space per default */ vect->convert_to = 1; @@ -45,52 +59,42 @@ static void node_shader_init_vect_transform(bNodeTree *UNUSED(ntree), bNode *nod node->storage = vect; } -static void node_shader_exec_vect_transform(void *UNUSED(data), - int UNUSED(thread), - bNode *UNUSED(node), - bNodeExecData *UNUSED(execdata), - bNodeStack **UNUSED(in), - bNodeStack **UNUSED(out)) -{ -} - -static const char *get_gpufn_name_from_to(short from, short to) +static GPUNodeLink *get_gpulink_matrix_from_to(short from, short to) { switch (from) { case SHD_VECT_TRANSFORM_SPACE_OBJECT: switch (to) { case SHD_VECT_TRANSFORM_SPACE_OBJECT: - return NULL; + return nullptr; case SHD_VECT_TRANSFORM_SPACE_WORLD: - return "object_to_world"; + return GPU_builtin(GPU_OBJECT_MATRIX); case SHD_VECT_TRANSFORM_SPACE_CAMERA: - return "object_to_view"; + return GPU_builtin(GPU_LOC_TO_VIEW_MATRIX); } break; case SHD_VECT_TRANSFORM_SPACE_WORLD: switch (to) { case SHD_VECT_TRANSFORM_SPACE_WORLD: - return NULL; + return nullptr; case SHD_VECT_TRANSFORM_SPACE_CAMERA: - return "world_to_view"; + return GPU_builtin(GPU_VIEW_MATRIX); case SHD_VECT_TRANSFORM_SPACE_OBJECT: - return "world_to_object"; + return GPU_builtin(GPU_INVERSE_OBJECT_MATRIX); } break; case SHD_VECT_TRANSFORM_SPACE_CAMERA: switch (to) { case SHD_VECT_TRANSFORM_SPACE_CAMERA: - return NULL; + return nullptr; case SHD_VECT_TRANSFORM_SPACE_WORLD: - return "view_to_world"; + return GPU_builtin(GPU_INVERSE_VIEW_MATRIX); case SHD_VECT_TRANSFORM_SPACE_OBJECT: - return "view_to_object"; + return GPU_builtin(GPU_INVERSE_LOC_TO_VIEW_MATRIX); } break; } - return NULL; + return nullptr; } - static int gpu_shader_vect_transform(GPUMaterial *mat, bNode *node, bNodeExecData *UNUSED(execdata), @@ -98,6 +102,11 @@ static int gpu_shader_vect_transform(GPUMaterial *mat, GPUNodeStack *out) { struct GPUNodeLink *inputlink; + struct GPUNodeLink *fromto; + + const char *vtransform = "direction_transform_m4v3"; + const char *ptransform = "point_transform_m4v3"; + const char *func_name = nullptr; NodeShaderVectTransform *nodeprop = (NodeShaderVectTransform *)node->storage; @@ -108,10 +117,9 @@ static int gpu_shader_vect_transform(GPUMaterial *mat, inputlink = GPU_constant(in[0].vec); } - const char *xform = (nodeprop->type == SHD_VECT_TRANSFORM_TYPE_POINT) ? "point_transform_" : - "direction_transform_"; - const char *fromto = get_gpufn_name_from_to(nodeprop->convert_from, nodeprop->convert_to); + fromto = get_gpulink_matrix_from_to(nodeprop->convert_from, nodeprop->convert_to); + func_name = (nodeprop->type == SHD_VECT_TRANSFORM_TYPE_POINT) ? ptransform : vtransform; if (fromto) { /* For cycles we have inverted Z */ /* TODO: pass here the correct matrices */ @@ -119,11 +127,7 @@ static int gpu_shader_vect_transform(GPUMaterial *mat, nodeprop->convert_to != SHD_VECT_TRANSFORM_SPACE_CAMERA) { GPU_link(mat, "invert_z", inputlink, &inputlink); } - - char func_name[48]; - SNPRINTF(func_name, "%s%s", xform, fromto); - GPU_link(mat, func_name, inputlink, &out[0].link); - + GPU_link(mat, func_name, inputlink, fromto, &out[0].link); if (nodeprop->convert_to == SHD_VECT_TRANSFORM_SPACE_CAMERA && nodeprop->convert_from != SHD_VECT_TRANSFORM_SPACE_CAMERA) { GPU_link(mat, "invert_z", out[0].link, &out[0].link); @@ -140,17 +144,21 @@ static int gpu_shader_vect_transform(GPUMaterial *mat, return true; } -void register_node_type_sh_vect_transform(void) +} // namespace blender::nodes::node_shader_vector_transform_cc + +void register_node_type_sh_vect_transform() { + namespace file_ns = blender::nodes::node_shader_vector_transform_cc; + static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_VECT_TRANSFORM, "Vector Transform", NODE_CLASS_OP_VECTOR, 0); - node_type_init(&ntype, node_shader_init_vect_transform); - node_type_socket_templates(&ntype, sh_node_vect_transform_in, sh_node_vect_transform_out); + sh_node_type_base(&ntype, SH_NODE_VECT_TRANSFORM, "Vector Transform", NODE_CLASS_OP_VECTOR); + ntype.declare = file_ns::node_declare; + ntype.draw_buttons = file_ns::node_shader_buts_vect_transform; + node_type_init(&ntype, file_ns::node_shader_init_vect_transform); node_type_storage( &ntype, "NodeShaderVectTransform", node_free_standard_storage, node_copy_standard_storage); - node_type_exec(&ntype, NULL, NULL, node_shader_exec_vect_transform); - node_type_gpu(&ntype, gpu_shader_vect_transform); + node_type_gpu(&ntype, file_ns::gpu_shader_vect_transform); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_vertex_color.c b/source/blender/nodes/shader/nodes/node_shader_vertex_color.cc index 40576b68dd5..6501527ef5d 100644 --- a/source/blender/nodes/shader/nodes/node_shader_vertex_color.c +++ b/source/blender/nodes/shader/nodes/node_shader_vertex_color.cc @@ -17,18 +17,44 @@ * All rights reserved. */ -#include "../node_shader_util.h" +#include "node_shader_util.hh" -static bNodeSocketTemplate sh_node_vertex_color_out[] = { - {SOCK_RGBA, N_("Color")}, - {SOCK_FLOAT, N_("Alpha")}, - {-1, ""}, -}; +#include "BKE_context.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes::node_shader_vertex_color_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_output<decl::Color>(N_("Color")); + b.add_output<decl::Float>(N_("Alpha")); +} + +static void node_shader_buts_vertex_color(uiLayout *layout, bContext *C, PointerRNA *ptr) +{ + PointerRNA obptr = CTX_data_pointer_get(C, "active_object"); + if (obptr.data && RNA_enum_get(&obptr, "type") == OB_MESH) { + PointerRNA dataptr = RNA_pointer_get(&obptr, "data"); + + if (U.experimental.use_sculpt_vertex_colors && + RNA_collection_length(&dataptr, "sculpt_vertex_colors")) { + uiItemPointerR( + layout, ptr, "layer_name", &dataptr, "sculpt_vertex_colors", "", ICON_GROUP_VCOL); + } + else { + uiItemPointerR(layout, ptr, "layer_name", &dataptr, "vertex_colors", "", ICON_GROUP_VCOL); + } + } + else { + uiItemL(layout, TIP_("No mesh in active object"), ICON_ERROR); + } +} static void node_shader_init_vertex_color(bNodeTree *UNUSED(ntree), bNode *node) { - NodeShaderVertexColor *vertexColor = MEM_callocN(sizeof(NodeShaderVertexColor), - "NodeShaderVertexColor"); + NodeShaderVertexColor *vertexColor = MEM_cnew<NodeShaderVertexColor>("NodeShaderVertexColor"); node->storage = vertexColor; } @@ -47,16 +73,21 @@ static int node_shader_gpu_vertex_color(GPUMaterial *mat, return GPU_stack_link(mat, node, "node_vertex_color", in, out, vertexColorLink); } -void register_node_type_sh_vertex_color(void) +} // namespace blender::nodes::node_shader_vertex_color_cc + +void register_node_type_sh_vertex_color() { + namespace file_ns = blender::nodes::node_shader_vertex_color_cc; + static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_VERTEX_COLOR, "Vertex Color", NODE_CLASS_INPUT, 0); - node_type_socket_templates(&ntype, NULL, sh_node_vertex_color_out); - node_type_init(&ntype, node_shader_init_vertex_color); + sh_node_type_base(&ntype, SH_NODE_VERTEX_COLOR, "Vertex Color", NODE_CLASS_INPUT); + ntype.declare = file_ns::node_declare; + ntype.draw_buttons = file_ns::node_shader_buts_vertex_color; + node_type_init(&ntype, file_ns::node_shader_init_vertex_color); node_type_storage( &ntype, "NodeShaderVertexColor", node_free_standard_storage, node_copy_standard_storage); - node_type_gpu(&ntype, node_shader_gpu_vertex_color); + node_type_gpu(&ntype, file_ns::node_shader_gpu_vertex_color); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_volume_absorption.c b/source/blender/nodes/shader/nodes/node_shader_volume_absorption.cc index 3c9fab2401b..a31bfdb6543 100644 --- a/source/blender/nodes/shader/nodes/node_shader_volume_absorption.c +++ b/source/blender/nodes/shader/nodes/node_shader_volume_absorption.cc @@ -17,21 +17,16 @@ * All rights reserved. */ -#include "../node_shader_util.h" +#include "node_shader_util.hh" -/* **************** OUTPUT ******************** */ +namespace blender::nodes::node_shader_volume_absorption_cc { -static bNodeSocketTemplate sh_node_volume_absorption_in[] = { - {SOCK_RGBA, N_("Color"), 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Density"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1000.0f}, - {SOCK_FLOAT, N_("Weight"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, PROP_NONE, SOCK_UNAVAIL}, - {-1, ""}, -}; - -static bNodeSocketTemplate sh_node_volume_absorption_out[] = { - {SOCK_SHADER, N_("Volume")}, - {-1, ""}, -}; +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Color>(N_("Color")).default_value({0.8f, 0.8f, 0.8f, 1.0f}); + b.add_input<decl::Float>(N_("Density")).default_value(1.0f).min(0.0f).max(1000.0f); + b.add_output<decl::Shader>(N_("Volume")); +} static int node_shader_gpu_volume_absorption(GPUMaterial *mat, bNode *node, @@ -42,16 +37,18 @@ static int node_shader_gpu_volume_absorption(GPUMaterial *mat, return GPU_stack_link(mat, node, "node_volume_absorption", in, out); } +} // namespace blender::nodes::node_shader_volume_absorption_cc + /* node type definition */ -void register_node_type_sh_volume_absorption(void) +void register_node_type_sh_volume_absorption() { + namespace file_ns = blender::nodes::node_shader_volume_absorption_cc; + static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_VOLUME_ABSORPTION, "Volume Absorption", NODE_CLASS_SHADER, 0); - node_type_socket_templates(&ntype, sh_node_volume_absorption_in, sh_node_volume_absorption_out); - node_type_init(&ntype, NULL); - node_type_storage(&ntype, "", NULL, NULL); - node_type_gpu(&ntype, node_shader_gpu_volume_absorption); + sh_node_type_base(&ntype, SH_NODE_VOLUME_ABSORPTION, "Volume Absorption", NODE_CLASS_SHADER); + ntype.declare = file_ns::node_declare; + node_type_gpu(&ntype, file_ns::node_shader_gpu_volume_absorption); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_volume_info.c b/source/blender/nodes/shader/nodes/node_shader_volume_info.cc index 6cafc991e13..6a973cdbad7 100644 --- a/source/blender/nodes/shader/nodes/node_shader_volume_info.c +++ b/source/blender/nodes/shader/nodes/node_shader_volume_info.cc @@ -17,15 +17,17 @@ * All rights reserved. */ -#include "../node_shader_util.h" +#include "node_shader_util.hh" -static bNodeSocketTemplate sh_node_volume_info_out[] = { - {SOCK_RGBA, N_("Color")}, - {SOCK_FLOAT, N_("Density")}, - {SOCK_FLOAT, N_("Flame")}, - {SOCK_FLOAT, N_("Temperature")}, - {-1, ""}, -}; +namespace blender::nodes::node_shader_volume_info_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_output<decl::Color>(N_("Color")); + b.add_output<decl::Float>(N_("Density")); + b.add_output<decl::Float>(N_("Flame")); + b.add_output<decl::Float>(N_("Temperature")); +} static int node_shader_gpu_volume_info(GPUMaterial *mat, bNode *UNUSED(node), @@ -49,13 +51,17 @@ static int node_shader_gpu_volume_info(GPUMaterial *mat, return true; } -void register_node_type_sh_volume_info(void) +} // namespace blender::nodes::node_shader_volume_info_cc + +void register_node_type_sh_volume_info() { + namespace file_ns = blender::nodes::node_shader_volume_info_cc; + static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_VOLUME_INFO, "Volume Info", NODE_CLASS_INPUT, 0); - node_type_socket_templates(&ntype, NULL, sh_node_volume_info_out); - node_type_gpu(&ntype, node_shader_gpu_volume_info); + sh_node_type_base(&ntype, SH_NODE_VOLUME_INFO, "Volume Info", NODE_CLASS_INPUT); + ntype.declare = file_ns::node_declare; + node_type_gpu(&ntype, file_ns::node_shader_gpu_volume_info); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_volume_principled.c b/source/blender/nodes/shader/nodes/node_shader_volume_principled.cc index 41c3abd7ca4..54e92eafcf6 100644 --- a/source/blender/nodes/shader/nodes/node_shader_volume_principled.c +++ b/source/blender/nodes/shader/nodes/node_shader_volume_principled.cc @@ -17,31 +17,34 @@ * All rights reserved. */ -#include "../node_shader_util.h" - -/* **************** OUTPUT ******************** */ - -static bNodeSocketTemplate sh_node_volume_principled_in[] = { - {SOCK_RGBA, N_("Color"), 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 1.0f}, - {SOCK_STRING, N_("Color Attribute"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Density"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1000.0f}, - {SOCK_STRING, N_("Density Attribute"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Anisotropy"), 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, PROP_FACTOR}, - {SOCK_RGBA, N_("Absorption Color"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Emission Strength"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1000.0f}, - {SOCK_RGBA, N_("Emission Color"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Blackbody Intensity"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_RGBA, N_("Blackbody Tint"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Temperature"), 1000.0f, 0.0f, 0.0f, 0.0f, 0.0f, 6500.0f}, - {SOCK_STRING, N_("Temperature Attribute"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Weight"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, PROP_NONE, SOCK_UNAVAIL}, - {-1, ""}, -}; - -static bNodeSocketTemplate sh_node_volume_principled_out[] = { - {SOCK_SHADER, N_("Volume")}, - {-1, ""}, -}; +#include "node_shader_util.hh" + +namespace blender::nodes::node_shader_volume_principled_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Color>(N_("Color")).default_value({0.5f, 0.5f, 0.5f, 1.0f}); + b.add_input<decl::String>(N_("Color Attribute")); + b.add_input<decl::Float>(N_("Density")).default_value(1.0f).min(0.0f).max(1000.0f); + b.add_input<decl::String>(N_("Density Attribute")); + b.add_input<decl::Float>(N_("Anisotropy")) + .default_value(0.0f) + .min(-1.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Color>(N_("Absorption Color")).default_value({0.0f, 0.0f, 0.0f, 1.0f}); + b.add_input<decl::Float>(N_("Emission Strength")).default_value(0.0f).min(0.0f).max(1000.0f); + b.add_input<decl::Color>(N_("Emission Color")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Float>(N_("Blackbody Intensity")) + .default_value(0.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input<decl::Color>(N_("Blackbody Tint")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Float>(N_("Temperature")).default_value(1000.0f).min(0.0f).max(6500.0f); + b.add_input<decl::String>(N_("Temperature Attribute")); + b.add_output<decl::Shader>(N_("Volume")); +} static void node_shader_init_volume_principled(bNodeTree *UNUSED(ntree), bNode *node) { @@ -65,14 +68,14 @@ static int node_shader_gpu_volume_principled(GPUMaterial *mat, bool use_blackbody = (in[8].link || in[8].vec[0] != 0.0f); /* Get volume attributes. */ - GPUNodeLink *density = NULL, *color = NULL, *temperature = NULL; + GPUNodeLink *density = nullptr, *color = nullptr, *temperature = nullptr; LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { if (sock->typeinfo->type != SOCK_STRING) { continue; } - bNodeSocketValueString *value = sock->default_value; + bNodeSocketValueString *value = (bNodeSocketValueString *)sock->default_value; const char *attribute_name = value->value; if (attribute_name[0] == '\0') { continue; @@ -107,11 +110,11 @@ static int node_shader_gpu_volume_principled(GPUMaterial *mat, const int size = CM_TABLE + 1; float *data, layer; if (use_blackbody) { - data = MEM_mallocN(sizeof(float) * size * 4, "blackbody texture"); + data = (float *)MEM_mallocN(sizeof(float) * size * 4, "blackbody texture"); blackbody_temperature_to_rgb_table(data, size, 965.0f, 12000.0f); } else { - data = MEM_callocN(sizeof(float) * size * 4, "blackbody black"); + data = (float *)MEM_callocN(sizeof(float) * size * 4, "blackbody black"); } GPUNodeLink *spectrummap = GPU_color_band(mat, size, data, &layer); @@ -127,17 +130,20 @@ static int node_shader_gpu_volume_principled(GPUMaterial *mat, GPU_constant(&layer)); } +} // namespace blender::nodes::node_shader_volume_principled_cc + /* node type definition */ -void register_node_type_sh_volume_principled(void) +void register_node_type_sh_volume_principled() { + namespace file_ns = blender::nodes::node_shader_volume_principled_cc; + static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_VOLUME_PRINCIPLED, "Principled Volume", NODE_CLASS_SHADER, 0); - node_type_socket_templates(&ntype, sh_node_volume_principled_in, sh_node_volume_principled_out); + sh_node_type_base(&ntype, SH_NODE_VOLUME_PRINCIPLED, "Principled Volume", NODE_CLASS_SHADER); + ntype.declare = file_ns::node_declare; node_type_size_preset(&ntype, NODE_SIZE_LARGE); - node_type_init(&ntype, node_shader_init_volume_principled); - node_type_storage(&ntype, "", NULL, NULL); - node_type_gpu(&ntype, node_shader_gpu_volume_principled); + node_type_init(&ntype, file_ns::node_shader_init_volume_principled); + node_type_gpu(&ntype, file_ns::node_shader_gpu_volume_principled); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_volume_scatter.c b/source/blender/nodes/shader/nodes/node_shader_volume_scatter.cc index 282a49dc944..abd10cfcbcf 100644 --- a/source/blender/nodes/shader/nodes/node_shader_volume_scatter.c +++ b/source/blender/nodes/shader/nodes/node_shader_volume_scatter.cc @@ -17,22 +17,21 @@ * All rights reserved. */ -#include "../node_shader_util.h" +#include "node_shader_util.hh" -/* **************** OUTPUT ******************** */ +namespace blender::nodes::node_shader_volume_scatter_cc { -static bNodeSocketTemplate sh_node_volume_scatter_in[] = { - {SOCK_RGBA, N_("Color"), 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Density"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1000.0f}, - {SOCK_FLOAT, N_("Anisotropy"), 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, PROP_FACTOR}, - {SOCK_FLOAT, N_("Weight"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, PROP_NONE, SOCK_UNAVAIL}, - {-1, ""}, -}; - -static bNodeSocketTemplate sh_node_volume_scatter_out[] = { - {SOCK_SHADER, N_("Volume")}, - {-1, ""}, -}; +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Color>(N_("Color")).default_value({0.8f, 0.8f, 0.8f, 1.0f}); + b.add_input<decl::Float>(N_("Density")).default_value(1.0f).min(0.0f).max(1000.0f); + b.add_input<decl::Float>(N_("Anisotropy")) + .default_value(0.0f) + .min(-1.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_output<decl::Shader>(N_("Volume")); +} static int node_shader_gpu_volume_scatter(GPUMaterial *mat, bNode *node, @@ -43,16 +42,18 @@ static int node_shader_gpu_volume_scatter(GPUMaterial *mat, return GPU_stack_link(mat, node, "node_volume_scatter", in, out); } +} // namespace blender::nodes::node_shader_volume_scatter_cc + /* node type definition */ -void register_node_type_sh_volume_scatter(void) +void register_node_type_sh_volume_scatter() { + namespace file_ns = blender::nodes::node_shader_volume_scatter_cc; + static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_VOLUME_SCATTER, "Volume Scatter", NODE_CLASS_SHADER, 0); - node_type_socket_templates(&ntype, sh_node_volume_scatter_in, sh_node_volume_scatter_out); - node_type_init(&ntype, NULL); - node_type_storage(&ntype, "", NULL, NULL); - node_type_gpu(&ntype, node_shader_gpu_volume_scatter); + sh_node_type_base(&ntype, SH_NODE_VOLUME_SCATTER, "Volume Scatter", NODE_CLASS_SHADER); + ntype.declare = file_ns::node_declare; + node_type_gpu(&ntype, file_ns::node_shader_gpu_volume_scatter); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_wavelength.c b/source/blender/nodes/shader/nodes/node_shader_wavelength.cc index f978537ee85..a67c7830edd 100644 --- a/source/blender/nodes/shader/nodes/node_shader_wavelength.c +++ b/source/blender/nodes/shader/nodes/node_shader_wavelength.cc @@ -17,18 +17,15 @@ * All rights reserved. */ -#include "../node_shader_util.h" +#include "node_shader_util.hh" -/* **************** Wavelength ******************** */ -static bNodeSocketTemplate sh_node_wavelength_in[] = { - {SOCK_FLOAT, N_("Wavelength"), 500.0f, 0.0f, 0.0f, 0.0f, 380.0f, 780.0f}, - {-1, ""}, -}; +namespace blender::nodes::node_shader_wavelength_cc { -static bNodeSocketTemplate sh_node_wavelength_out[] = { - {SOCK_RGBA, N_("Color")}, - {-1, ""}, -}; +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Float>(N_("Wavelength")).default_value(500.0f).min(380.0f).max(780.0f); + b.add_output<decl::Color>(N_("Color")); +} static int node_shader_gpu_wavelength(GPUMaterial *mat, bNode *node, @@ -37,7 +34,7 @@ static int node_shader_gpu_wavelength(GPUMaterial *mat, GPUNodeStack *out) { const int size = CM_TABLE + 1; - float *data = MEM_mallocN(sizeof(float) * size * 4, "cie_xyz texture"); + float *data = static_cast<float *>(MEM_mallocN(sizeof(float) * size * 4, "cie_xyz texture")); wavelength_to_xyz_table(data, size); @@ -57,17 +54,19 @@ static int node_shader_gpu_wavelength(GPUMaterial *mat, GPU_uniform(xyz_to_rgb.b)); } +} // namespace blender::nodes::node_shader_wavelength_cc + /* node type definition */ -void register_node_type_sh_wavelength(void) +void register_node_type_sh_wavelength() { + namespace file_ns = blender::nodes::node_shader_wavelength_cc; + static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_WAVELENGTH, "Wavelength", NODE_CLASS_CONVERTER, 0); + sh_node_type_base(&ntype, SH_NODE_WAVELENGTH, "Wavelength", NODE_CLASS_CONVERTER); + ntype.declare = file_ns::node_declare; node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); - node_type_socket_templates(&ntype, sh_node_wavelength_in, sh_node_wavelength_out); - node_type_init(&ntype, NULL); - node_type_storage(&ntype, "", NULL, NULL); - node_type_gpu(&ntype, node_shader_gpu_wavelength); + node_type_gpu(&ntype, file_ns::node_shader_gpu_wavelength); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_wireframe.c b/source/blender/nodes/shader/nodes/node_shader_wireframe.cc index e8c2b6971bc..c4771cc13e9 100644 --- a/source/blender/nodes/shader/nodes/node_shader_wireframe.c +++ b/source/blender/nodes/shader/nodes/node_shader_wireframe.cc @@ -17,18 +17,23 @@ * All rights reserved. */ -#include "../node_shader_util.h" +#include "node_shader_util.hh" -/* **************** Wireframe ******************** */ -static bNodeSocketTemplate sh_node_wireframe_in[] = { - {SOCK_FLOAT, N_("Size"), 0.01f, 0.0f, 0.0f, 0.0f, 0.0f, 100.0f}, - {-1, ""}, -}; +#include "UI_interface.h" +#include "UI_resources.h" -static bNodeSocketTemplate sh_node_wireframe_out[] = { - {SOCK_FLOAT, N_("Fac"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {-1, ""}, -}; +namespace blender::nodes::node_shader_wireframe_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Float>(N_("Size")).default_value(0.01f).min(0.0f).max(100.0f); + b.add_output<decl::Float>(N_("Fac")); +} + +static void node_shader_buts_wireframe(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "use_pixel_size", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, 0); +} static int node_shader_gpu_wireframe(GPUMaterial *mat, bNode *node, @@ -46,16 +51,19 @@ static int node_shader_gpu_wireframe(GPUMaterial *mat, } } +} // namespace blender::nodes::node_shader_wireframe_cc + /* node type definition */ -void register_node_type_sh_wireframe(void) +void register_node_type_sh_wireframe() { + namespace file_ns = blender::nodes::node_shader_wireframe_cc; + static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_WIREFRAME, "Wireframe", NODE_CLASS_INPUT, 0); - node_type_socket_templates(&ntype, sh_node_wireframe_in, sh_node_wireframe_out); - node_type_init(&ntype, NULL); - node_type_storage(&ntype, "", NULL, NULL); - node_type_gpu(&ntype, node_shader_gpu_wireframe); + sh_node_type_base(&ntype, SH_NODE_WIREFRAME, "Wireframe", NODE_CLASS_INPUT); + ntype.declare = file_ns::node_declare; + ntype.draw_buttons = file_ns::node_shader_buts_wireframe; + node_type_gpu(&ntype, file_ns::node_shader_gpu_wireframe); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/texture/CMakeLists.txt b/source/blender/nodes/texture/CMakeLists.txt new file mode 100644 index 00000000000..053b17e4e57 --- /dev/null +++ b/source/blender/nodes/texture/CMakeLists.txt @@ -0,0 +1,89 @@ +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ***** END GPL LICENSE BLOCK ***** + +set(INC + . + ../ + ../intern + ../../editors/include + ../../blenkernel + ../../blenlib + ../../blentranslation + ../../depsgraph + ../../imbuf + ../../makesdna + ../../makesrna + ../../render + ../../windowmanager + ../../../../intern/guardedalloc +) + + +set(SRC + nodes/node_texture_at.c + nodes/node_texture_bricks.c + nodes/node_texture_checker.c + nodes/node_texture_common.c + nodes/node_texture_compose.c + nodes/node_texture_coord.c + nodes/node_texture_curves.c + nodes/node_texture_decompose.c + nodes/node_texture_distance.c + nodes/node_texture_hueSatVal.c + nodes/node_texture_image.c + nodes/node_texture_invert.c + nodes/node_texture_math.c + nodes/node_texture_mixRgb.c + nodes/node_texture_output.c + nodes/node_texture_proc.c + nodes/node_texture_rotate.c + nodes/node_texture_scale.c + nodes/node_texture_texture.c + nodes/node_texture_translate.c + nodes/node_texture_valToNor.c + nodes/node_texture_valToRgb.c + nodes/node_texture_viewer.c + node_texture_tree.c + node_texture_util.c + + node_texture_util.h +) + +set(LIB + bf_nodes +) + +if(WITH_PYTHON) + list(APPEND INC + ../../python + ) + list(APPEND INC_SYS + ${PYTHON_INCLUDE_DIRS} + ) + list(APPEND LIB + ${PYTHON_LINKFLAGS} + ${PYTHON_LIBRARIES} + ) + add_definitions(-DWITH_PYTHON) +endif() + +if(WITH_INTERNATIONAL) + add_definitions(-DWITH_INTERNATIONAL) +endif() + +blender_add_lib(bf_nodes_texture "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/nodes/texture/node_texture_tree.c b/source/blender/nodes/texture/node_texture_tree.c index 7452007639c..3d914d486c3 100644 --- a/source/blender/nodes/texture/node_texture_tree.c +++ b/source/blender/nodes/texture/node_texture_tree.c @@ -50,6 +50,8 @@ #include "RE_texture.h" +#include "UI_resources.h" + static void texture_get_from_context(const bContext *C, bNodeTreeType *UNUSED(treetype), bNodeTree **r_ntree, @@ -132,24 +134,9 @@ static void localize(bNodeTree *UNUSED(localtree), bNodeTree *UNUSED(ntree)) } #endif -static void local_sync(bNodeTree *localtree, bNodeTree *ntree) -{ - BKE_node_preview_sync_tree(ntree, localtree); -} - -static void local_merge(Main *UNUSED(bmain), bNodeTree *localtree, bNodeTree *ntree) -{ - BKE_node_preview_merge_tree(ntree, localtree, true); -} - static void update(bNodeTree *ntree) { ntree_update_reroute_nodes(ntree); - - if (ntree->update & NTREE_UPDATE_NODES) { - /* clean up preview cache, in case nodes have been removed */ - BKE_node_preview_remove_unused(ntree); - } } static bool texture_node_tree_socket_type_valid(bNodeTreeType *UNUSED(ntreetype), @@ -169,14 +156,12 @@ void register_node_tree_type_tex(void) tt->type = NTREE_TEXTURE; strcpy(tt->idname, "TextureNodeTree"); strcpy(tt->ui_name, N_("Texture Node Editor")); - tt->ui_icon = 0; /* defined in drawnode.c */ + tt->ui_icon = ICON_NODE_TEXTURE; /* Defined in `drawnode.c`. */ strcpy(tt->ui_description, N_("Texture nodes")); tt->foreach_nodeclass = foreach_nodeclass; tt->update = update; tt->localize = localize; - tt->local_sync = local_sync; - tt->local_merge = local_merge; tt->get_from_context = texture_get_from_context; tt->valid_socket_type = texture_node_tree_socket_type_valid; diff --git a/source/blender/nodes/texture/node_texture_util.c b/source/blender/nodes/texture/node_texture_util.c index 570b10d6e89..dc5e2bfcd6b 100644 --- a/source/blender/nodes/texture/node_texture_util.c +++ b/source/blender/nodes/texture/node_texture_util.c @@ -44,30 +44,24 @@ bool tex_node_poll_default(bNodeType *UNUSED(ntype), const char **r_disabled_hint) { if (!STREQ(ntree->idname, "TextureNodeTree")) { - *r_disabled_hint = "Not a texture node tree"; + *r_disabled_hint = TIP_("Not a texture node tree"); return false; } return true; } -void tex_node_type_base( - struct bNodeType *ntype, int type, const char *name, short nclass, short flag) +void tex_node_type_base(struct bNodeType *ntype, int type, const char *name, short nclass) { - node_type_base(ntype, type, name, nclass, flag); + node_type_base(ntype, type, name, nclass); ntype->poll = tex_node_poll_default; ntype->insert_link = node_insert_link_default; - ntype->update_internal_links = node_update_internal_links_default; } static void tex_call_delegate(TexDelegate *dg, float *out, TexParams *params, short thread) { if (dg->node->need_exec) { dg->fn(out, params, dg->node, dg->in, thread); - - if (dg->cdata->do_preview) { - tex_do_preview(dg->preview, params->previewco, out, dg->cdata->do_manage); - } } } @@ -124,19 +118,6 @@ void params_from_cdata(TexParams *out, TexCallData *in) out->mtex = in->mtex; } -void tex_do_preview(bNodePreview *preview, - const float coord[2], - const float col[4], - bool do_manage) -{ - if (preview) { - int xs = ((coord[0] + 1.0f) * 0.5f) * preview->xsize; - int ys = ((coord[1] + 1.0f) * 0.5f) * preview->ysize; - - BKE_node_preview_set_pixel(preview, col, xs, ys, do_manage); - } -} - void tex_output(bNode *node, bNodeExecData *execdata, bNodeStack **in, diff --git a/source/blender/nodes/texture/node_texture_util.h b/source/blender/nodes/texture/node_texture_util.h index 8f63a1ad07d..d53000058a3 100644 --- a/source/blender/nodes/texture/node_texture_util.h +++ b/source/blender/nodes/texture/node_texture_util.h @@ -37,8 +37,7 @@ #include "DNA_scene_types.h" #include "DNA_texture_types.h" -#include "BLI_blenlib.h" -#include "BLI_math.h" +#include "BLI_math_vector.h" #include "BLI_rand.h" #include "BLI_threads.h" #include "BLI_utildefines.h" @@ -109,8 +108,7 @@ typedef struct TexDelegate { bool tex_node_poll_default(struct bNodeType *ntype, struct bNodeTree *ntree, const char **r_disabled_hint); -void tex_node_type_base( - struct bNodeType *ntype, int type, const char *name, short nclass, short flag); +void tex_node_type_base(struct bNodeType *ntype, int type, const char *name, short nclass); void tex_input_rgba(float *out, bNodeStack *in, TexParams *params, short thread); void tex_input_vec(float *out, bNodeStack *in, TexParams *params, short thread); @@ -122,10 +120,6 @@ void tex_output(bNode *node, bNodeStack *out, TexFn texfn, TexCallData *data); -void tex_do_preview(bNodePreview *preview, - const float coord[2], - const float col[4], - bool do_manage); void params_from_cdata(TexParams *out, TexCallData *in); diff --git a/source/blender/nodes/texture/nodes/node_texture_at.c b/source/blender/nodes/texture/nodes/node_texture_at.c index a6f8d28db75..41dea303ea2 100644 --- a/source/blender/nodes/texture/nodes/node_texture_at.c +++ b/source/blender/nodes/texture/nodes/node_texture_at.c @@ -58,7 +58,7 @@ void register_node_type_tex_at(void) { static bNodeType ntype; - tex_node_type_base(&ntype, TEX_NODE_AT, "At", NODE_CLASS_DISTORT, 0); + tex_node_type_base(&ntype, TEX_NODE_AT, "At", NODE_CLASS_DISTORT); node_type_socket_templates(&ntype, inputs, outputs); node_type_size(&ntype, 140, 100, 320); node_type_exec(&ntype, NULL, NULL, exec); diff --git a/source/blender/nodes/texture/nodes/node_texture_bricks.c b/source/blender/nodes/texture/nodes/node_texture_bricks.c index 72690d6ccfe..0dc92dc33d0 100644 --- a/source/blender/nodes/texture/nodes/node_texture_bricks.c +++ b/source/blender/nodes/texture/nodes/node_texture_bricks.c @@ -119,11 +119,12 @@ void register_node_type_tex_bricks(void) { static bNodeType ntype; - tex_node_type_base(&ntype, TEX_NODE_BRICKS, "Bricks", NODE_CLASS_PATTERN, NODE_PREVIEW); + tex_node_type_base(&ntype, TEX_NODE_BRICKS, "Bricks", NODE_CLASS_PATTERN); node_type_socket_templates(&ntype, inputs, outputs); node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); node_type_init(&ntype, init); node_type_exec(&ntype, NULL, NULL, exec); + ntype.flag |= NODE_PREVIEW; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/texture/nodes/node_texture_checker.c b/source/blender/nodes/texture/nodes/node_texture_checker.c index e3c4d44e7f5..62657cd7def 100644 --- a/source/blender/nodes/texture/nodes/node_texture_checker.c +++ b/source/blender/nodes/texture/nodes/node_texture_checker.c @@ -70,9 +70,10 @@ void register_node_type_tex_checker(void) { static bNodeType ntype; - tex_node_type_base(&ntype, TEX_NODE_CHECKER, "Checker", NODE_CLASS_PATTERN, NODE_PREVIEW); + tex_node_type_base(&ntype, TEX_NODE_CHECKER, "Checker", NODE_CLASS_PATTERN); node_type_socket_templates(&ntype, inputs, outputs); node_type_exec(&ntype, NULL, NULL, exec); + ntype.flag |= NODE_PREVIEW; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/texture/nodes/node_texture_common.c b/source/blender/nodes/texture/nodes/node_texture_common.c index 2de64779ea6..d68cfe78b44 100644 --- a/source/blender/nodes/texture/nodes/node_texture_common.c +++ b/source/blender/nodes/texture/nodes/node_texture_common.c @@ -161,19 +161,17 @@ void register_node_type_tex_group(void) /* NOTE: Cannot use #sh_node_type_base for node group, because it would map the node type * to the shared #NODE_GROUP integer type id. */ - node_type_base_custom(&ntype, "TextureNodeGroup", "Group", NODE_CLASS_GROUP, NODE_CONST_OUTPUT); + node_type_base_custom(&ntype, "TextureNodeGroup", "Group", NODE_CLASS_GROUP); ntype.type = NODE_GROUP; ntype.poll = tex_node_poll_default; ntype.poll_instance = node_group_poll_instance; ntype.insert_link = node_insert_link_default; - ntype.update_internal_links = node_update_internal_links_default; ntype.rna_ext.srna = RNA_struct_find("TextureNodeGroup"); BLI_assert(ntype.rna_ext.srna != NULL); RNA_struct_blender_type_set(ntype.rna_ext.srna, &ntype); - node_type_socket_templates(&ntype, NULL, NULL); node_type_size(&ntype, 140, 60, 400); - node_type_label(&ntype, node_group_label); + ntype.labelfunc = node_group_label; node_type_group_update(&ntype, node_group_update); node_type_exec(&ntype, group_initexec, group_freeexec, group_execute); diff --git a/source/blender/nodes/texture/nodes/node_texture_compose.c b/source/blender/nodes/texture/nodes/node_texture_compose.c index ffa0e9ae43e..cd918ca8314 100644 --- a/source/blender/nodes/texture/nodes/node_texture_compose.c +++ b/source/blender/nodes/texture/nodes/node_texture_compose.c @@ -58,7 +58,7 @@ void register_node_type_tex_compose(void) { static bNodeType ntype; - tex_node_type_base(&ntype, TEX_NODE_COMPOSE, "Combine RGBA", NODE_CLASS_OP_COLOR, 0); + tex_node_type_base(&ntype, TEX_NODE_COMPOSE, "Combine RGBA", NODE_CLASS_OP_COLOR); node_type_socket_templates(&ntype, inputs, outputs); node_type_exec(&ntype, NULL, NULL, exec); diff --git a/source/blender/nodes/texture/nodes/node_texture_coord.c b/source/blender/nodes/texture/nodes/node_texture_coord.c index 5a0cf5eb497..e31fdcafbf2 100644 --- a/source/blender/nodes/texture/nodes/node_texture_coord.c +++ b/source/blender/nodes/texture/nodes/node_texture_coord.c @@ -49,9 +49,8 @@ void register_node_type_tex_coord(void) { static bNodeType ntype; - tex_node_type_base(&ntype, TEX_NODE_COORD, "Coordinates", NODE_CLASS_INPUT, 0); + tex_node_type_base(&ntype, TEX_NODE_COORD, "Coordinates", NODE_CLASS_INPUT); node_type_socket_templates(&ntype, NULL, outputs); - node_type_storage(&ntype, "", NULL, NULL); node_type_exec(&ntype, NULL, NULL, exec); nodeRegisterType(&ntype); diff --git a/source/blender/nodes/texture/nodes/node_texture_curves.c b/source/blender/nodes/texture/nodes/node_texture_curves.c index 70f7e731720..7fed45c5558 100644 --- a/source/blender/nodes/texture/nodes/node_texture_curves.c +++ b/source/blender/nodes/texture/nodes/node_texture_curves.c @@ -26,7 +26,7 @@ /* **************** CURVE Time ******************** */ -/* custom1 = sfra, custom2 = efra */ +/* custom1 = start-frame, custom2 = end-frame. */ static bNodeSocketTemplate time_outputs[] = {{SOCK_FLOAT, N_("Value")}, {-1, ""}}; static void time_colorfn( @@ -65,7 +65,7 @@ void register_node_type_tex_curve_time(void) { static bNodeType ntype; - tex_node_type_base(&ntype, TEX_NODE_CURVE_TIME, "Time", NODE_CLASS_INPUT, 0); + tex_node_type_base(&ntype, TEX_NODE_CURVE_TIME, "Time", NODE_CLASS_INPUT); node_type_socket_templates(&ntype, NULL, time_outputs); node_type_size_preset(&ntype, NODE_SIZE_LARGE); node_type_init(&ntype, time_init); @@ -114,7 +114,7 @@ void register_node_type_tex_curve_rgb(void) { static bNodeType ntype; - tex_node_type_base(&ntype, TEX_NODE_CURVE_RGB, "RGB Curves", NODE_CLASS_OP_COLOR, 0); + tex_node_type_base(&ntype, TEX_NODE_CURVE_RGB, "RGB Curves", NODE_CLASS_OP_COLOR); node_type_socket_templates(&ntype, rgb_inputs, rgb_outputs); node_type_size_preset(&ntype, NODE_SIZE_LARGE); node_type_init(&ntype, rgb_init); diff --git a/source/blender/nodes/texture/nodes/node_texture_decompose.c b/source/blender/nodes/texture/nodes/node_texture_decompose.c index 83922ea03ab..9c3cb6911e1 100644 --- a/source/blender/nodes/texture/nodes/node_texture_decompose.c +++ b/source/blender/nodes/texture/nodes/node_texture_decompose.c @@ -78,7 +78,7 @@ void register_node_type_tex_decompose(void) { static bNodeType ntype; - tex_node_type_base(&ntype, TEX_NODE_DECOMPOSE, "Separate RGBA", NODE_CLASS_OP_COLOR, 0); + tex_node_type_base(&ntype, TEX_NODE_DECOMPOSE, "Separate RGBA", NODE_CLASS_OP_COLOR); node_type_socket_templates(&ntype, inputs, outputs); node_type_exec(&ntype, NULL, NULL, exec); diff --git a/source/blender/nodes/texture/nodes/node_texture_distance.c b/source/blender/nodes/texture/nodes/node_texture_distance.c index f7deac9ff4a..2f8b28722bd 100644 --- a/source/blender/nodes/texture/nodes/node_texture_distance.c +++ b/source/blender/nodes/texture/nodes/node_texture_distance.c @@ -21,7 +21,6 @@ * \ingroup texnodes */ -#include "BLI_math.h" #include "NOD_texture.h" #include "node_texture_util.h" #include <math.h> @@ -61,9 +60,8 @@ void register_node_type_tex_distance(void) { static bNodeType ntype; - tex_node_type_base(&ntype, TEX_NODE_DISTANCE, "Distance", NODE_CLASS_CONVERTER, 0); + tex_node_type_base(&ntype, TEX_NODE_DISTANCE, "Distance", NODE_CLASS_CONVERTER); node_type_socket_templates(&ntype, inputs, outputs); - node_type_storage(&ntype, "", NULL, NULL); node_type_exec(&ntype, NULL, NULL, exec); nodeRegisterType(&ntype); diff --git a/source/blender/nodes/texture/nodes/node_texture_hueSatVal.c b/source/blender/nodes/texture/nodes/node_texture_hueSatVal.c index 759fb3d43d5..f405c3b0bec 100644 --- a/source/blender/nodes/texture/nodes/node_texture_hueSatVal.c +++ b/source/blender/nodes/texture/nodes/node_texture_hueSatVal.c @@ -107,7 +107,7 @@ void register_node_type_tex_hue_sat(void) { static bNodeType ntype; - tex_node_type_base(&ntype, TEX_NODE_HUE_SAT, "Hue Saturation Value", NODE_CLASS_OP_COLOR, 0); + tex_node_type_base(&ntype, TEX_NODE_HUE_SAT, "Hue Saturation Value", NODE_CLASS_OP_COLOR); node_type_socket_templates(&ntype, inputs, outputs); node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); node_type_exec(&ntype, NULL, NULL, exec); diff --git a/source/blender/nodes/texture/nodes/node_texture_image.c b/source/blender/nodes/texture/nodes/node_texture_image.c index 0d10b3270d7..18ae3609407 100644 --- a/source/blender/nodes/texture/nodes/node_texture_image.c +++ b/source/blender/nodes/texture/nodes/node_texture_image.c @@ -101,7 +101,6 @@ static void init(bNodeTree *UNUSED(ntree), bNode *node) ImageUser *iuser = MEM_callocN(sizeof(ImageUser), "node image user"); node->storage = iuser; iuser->sfra = 1; - iuser->ok = 1; iuser->flag |= IMA_ANIM_ALWAYS; } @@ -109,12 +108,13 @@ void register_node_type_tex_image(void) { static bNodeType ntype; - tex_node_type_base(&ntype, TEX_NODE_IMAGE, "Image", NODE_CLASS_INPUT, NODE_PREVIEW); + tex_node_type_base(&ntype, TEX_NODE_IMAGE, "Image", NODE_CLASS_INPUT); node_type_socket_templates(&ntype, NULL, outputs); node_type_init(&ntype, init); node_type_storage(&ntype, "ImageUser", node_free_standard_storage, node_copy_standard_storage); node_type_exec(&ntype, NULL, NULL, exec); - node_type_label(&ntype, node_image_label); + ntype.labelfunc = node_image_label; + ntype.flag |= NODE_PREVIEW; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/texture/nodes/node_texture_invert.c b/source/blender/nodes/texture/nodes/node_texture_invert.c index 5d3f86c5c9c..7854ac4b5b8 100644 --- a/source/blender/nodes/texture/nodes/node_texture_invert.c +++ b/source/blender/nodes/texture/nodes/node_texture_invert.c @@ -63,7 +63,7 @@ void register_node_type_tex_invert(void) { static bNodeType ntype; - tex_node_type_base(&ntype, TEX_NODE_INVERT, "Invert", NODE_CLASS_OP_COLOR, 0); + tex_node_type_base(&ntype, TEX_NODE_INVERT, "Invert", NODE_CLASS_OP_COLOR); node_type_socket_templates(&ntype, inputs, outputs); node_type_exec(&ntype, NULL, NULL, exec); diff --git a/source/blender/nodes/texture/nodes/node_texture_math.c b/source/blender/nodes/texture/nodes/node_texture_math.c index ab226a4dd38..2f50f6aaae0 100644 --- a/source/blender/nodes/texture/nodes/node_texture_math.c +++ b/source/blender/nodes/texture/nodes/node_texture_math.c @@ -335,10 +335,9 @@ void register_node_type_tex_math(void) { static bNodeType ntype; - tex_node_type_base(&ntype, TEX_NODE_MATH, "Math", NODE_CLASS_CONVERTER, 0); + tex_node_type_base(&ntype, TEX_NODE_MATH, "Math", NODE_CLASS_CONVERTER); node_type_socket_templates(&ntype, inputs, outputs); - node_type_label(&ntype, node_math_label); - node_type_storage(&ntype, "", NULL, NULL); + ntype.labelfunc = node_math_label; node_type_exec(&ntype, NULL, NULL, exec); node_type_update(&ntype, node_math_update); diff --git a/source/blender/nodes/texture/nodes/node_texture_mixRgb.c b/source/blender/nodes/texture/nodes/node_texture_mixRgb.c index b1aeb269018..5599807d8b1 100644 --- a/source/blender/nodes/texture/nodes/node_texture_mixRgb.c +++ b/source/blender/nodes/texture/nodes/node_texture_mixRgb.c @@ -69,9 +69,9 @@ void register_node_type_tex_mix_rgb(void) { static bNodeType ntype; - tex_node_type_base(&ntype, TEX_NODE_MIX_RGB, "Mix", NODE_CLASS_OP_COLOR, 0); + tex_node_type_base(&ntype, TEX_NODE_MIX_RGB, "Mix", NODE_CLASS_OP_COLOR); node_type_socket_templates(&ntype, inputs, outputs); - node_type_label(&ntype, node_blend_label); + ntype.labelfunc = node_blend_label; node_type_exec(&ntype, NULL, NULL, exec); nodeRegisterType(&ntype); diff --git a/source/blender/nodes/texture/nodes/node_texture_output.c b/source/blender/nodes/texture/nodes/node_texture_output.c index b24781e032b..4911ab7ba9e 100644 --- a/source/blender/nodes/texture/nodes/node_texture_output.c +++ b/source/blender/nodes/texture/nodes/node_texture_output.c @@ -21,6 +21,8 @@ * \ingroup texnodes */ +#include "BLI_string.h" + #include "NOD_texture.h" #include "node_texture_util.h" @@ -35,7 +37,7 @@ static bNodeSocketTemplate inputs[] = { static void exec(void *data, int UNUSED(thread), bNode *node, - bNodeExecData *execdata, + bNodeExecData *UNUSED(execdata), bNodeStack **in, bNodeStack **UNUSED(out)) { @@ -52,7 +54,6 @@ static void exec(void *data, else { tex_input_rgba(&target->tr, in[0], ¶ms, cdata->thread); } - tex_do_preview(execdata->preview, params.co, &target->tr, cdata->do_manage); } else { /* 0 means don't care, so just use first */ @@ -165,15 +166,15 @@ void register_node_type_tex_output(void) { static bNodeType ntype; - tex_node_type_base(&ntype, TEX_NODE_OUTPUT, "Output", NODE_CLASS_OUTPUT, NODE_PREVIEW); + tex_node_type_base(&ntype, TEX_NODE_OUTPUT, "Output", NODE_CLASS_OUTPUT); node_type_socket_templates(&ntype, inputs, NULL); node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); node_type_init(&ntype, init); node_type_storage(&ntype, "TexNodeOutput", node_free_standard_storage, copy); node_type_exec(&ntype, NULL, NULL, exec); - /* Do not allow muting output. */ - node_type_internal_links(&ntype, NULL); + ntype.flag |= NODE_PREVIEW; + ntype.no_muting = true; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/texture/nodes/node_texture_proc.c b/source/blender/nodes/texture/nodes/node_texture_proc.c index a8a82153e58..8c294b5954d 100644 --- a/source/blender/nodes/texture/nodes/node_texture_proc.c +++ b/source/blender/nodes/texture/nodes/node_texture_proc.c @@ -294,12 +294,13 @@ static void init(bNodeTree *UNUSED(ntree), bNode *node) { \ static bNodeType ntype; \ \ - tex_node_type_base(&ntype, TEX_NODE_PROC + TEXTYPE, Name, NODE_CLASS_TEXTURE, NODE_PREVIEW); \ + tex_node_type_base(&ntype, TEX_NODE_PROC + TEXTYPE, Name, NODE_CLASS_TEXTURE); \ node_type_socket_templates(&ntype, name##_inputs, outputs); \ node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); \ node_type_init(&ntype, init); \ node_type_storage(&ntype, "Tex", node_free_standard_storage, node_copy_standard_storage); \ node_type_exec(&ntype, NULL, NULL, name##_exec); \ + ntype.flag |= NODE_PREVIEW; \ \ nodeRegisterType(&ntype); \ } diff --git a/source/blender/nodes/texture/nodes/node_texture_rotate.c b/source/blender/nodes/texture/nodes/node_texture_rotate.c index 9985499772e..18024a4d41d 100644 --- a/source/blender/nodes/texture/nodes/node_texture_rotate.c +++ b/source/blender/nodes/texture/nodes/node_texture_rotate.c @@ -95,7 +95,7 @@ void register_node_type_tex_rotate(void) { static bNodeType ntype; - tex_node_type_base(&ntype, TEX_NODE_ROTATE, "Rotate", NODE_CLASS_DISTORT, 0); + tex_node_type_base(&ntype, TEX_NODE_ROTATE, "Rotate", NODE_CLASS_DISTORT); node_type_socket_templates(&ntype, inputs, outputs); node_type_exec(&ntype, NULL, NULL, exec); diff --git a/source/blender/nodes/texture/nodes/node_texture_scale.c b/source/blender/nodes/texture/nodes/node_texture_scale.c index d23b1b4d037..d570c73a67b 100644 --- a/source/blender/nodes/texture/nodes/node_texture_scale.c +++ b/source/blender/nodes/texture/nodes/node_texture_scale.c @@ -68,7 +68,7 @@ void register_node_type_tex_scale(void) { static bNodeType ntype; - tex_node_type_base(&ntype, TEX_NODE_SCALE, "Scale", NODE_CLASS_DISTORT, 0); + tex_node_type_base(&ntype, TEX_NODE_SCALE, "Scale", NODE_CLASS_DISTORT); node_type_socket_templates(&ntype, inputs, outputs); node_type_exec(&ntype, NULL, NULL, exec); diff --git a/source/blender/nodes/texture/nodes/node_texture_texture.c b/source/blender/nodes/texture/nodes/node_texture_texture.c index 59e2e9be581..083ae67ccb6 100644 --- a/source/blender/nodes/texture/nodes/node_texture_texture.c +++ b/source/blender/nodes/texture/nodes/node_texture_texture.c @@ -94,9 +94,10 @@ void register_node_type_tex_texture(void) { static bNodeType ntype; - tex_node_type_base(&ntype, TEX_NODE_TEXTURE, "Texture", NODE_CLASS_INPUT, NODE_PREVIEW); + tex_node_type_base(&ntype, TEX_NODE_TEXTURE, "Texture", NODE_CLASS_INPUT); node_type_socket_templates(&ntype, inputs, outputs); node_type_exec(&ntype, NULL, NULL, exec); + ntype.flag |= NODE_PREVIEW; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/texture/nodes/node_texture_translate.c b/source/blender/nodes/texture/nodes/node_texture_translate.c index 2eef3132a18..732cd0a89a1 100644 --- a/source/blender/nodes/texture/nodes/node_texture_translate.c +++ b/source/blender/nodes/texture/nodes/node_texture_translate.c @@ -64,7 +64,7 @@ void register_node_type_tex_translate(void) { static bNodeType ntype; - tex_node_type_base(&ntype, TEX_NODE_TRANSLATE, "Translate", NODE_CLASS_DISTORT, 0); + tex_node_type_base(&ntype, TEX_NODE_TRANSLATE, "Translate", NODE_CLASS_DISTORT); node_type_socket_templates(&ntype, inputs, outputs); node_type_exec(&ntype, NULL, NULL, exec); diff --git a/source/blender/nodes/texture/nodes/node_texture_valToNor.c b/source/blender/nodes/texture/nodes/node_texture_valToNor.c index 5ccd44b8bf0..3f0d4fb668e 100644 --- a/source/blender/nodes/texture/nodes/node_texture_valToNor.c +++ b/source/blender/nodes/texture/nodes/node_texture_valToNor.c @@ -80,7 +80,7 @@ void register_node_type_tex_valtonor(void) { static bNodeType ntype; - tex_node_type_base(&ntype, TEX_NODE_VALTONOR, "Value to Normal", NODE_CLASS_CONVERTER, 0); + tex_node_type_base(&ntype, TEX_NODE_VALTONOR, "Value to Normal", NODE_CLASS_CONVERTER); node_type_socket_templates(&ntype, inputs, outputs); node_type_exec(&ntype, NULL, NULL, exec); diff --git a/source/blender/nodes/texture/nodes/node_texture_valToRgb.c b/source/blender/nodes/texture/nodes/node_texture_valToRgb.c index 2446ef05e0c..844a4188663 100644 --- a/source/blender/nodes/texture/nodes/node_texture_valToRgb.c +++ b/source/blender/nodes/texture/nodes/node_texture_valToRgb.c @@ -63,7 +63,7 @@ void register_node_type_tex_valtorgb(void) { static bNodeType ntype; - tex_node_type_base(&ntype, TEX_NODE_VALTORGB, "ColorRamp", NODE_CLASS_CONVERTER, 0); + tex_node_type_base(&ntype, TEX_NODE_VALTORGB, "ColorRamp", NODE_CLASS_CONVERTER); node_type_socket_templates(&ntype, valtorgb_in, valtorgb_out); node_type_size_preset(&ntype, NODE_SIZE_LARGE); node_type_init(&ntype, valtorgb_init); @@ -105,7 +105,7 @@ void register_node_type_tex_rgbtobw(void) { static bNodeType ntype; - tex_node_type_base(&ntype, TEX_NODE_RGBTOBW, "RGB to BW", NODE_CLASS_CONVERTER, 0); + tex_node_type_base(&ntype, TEX_NODE_RGBTOBW, "RGB to BW", NODE_CLASS_CONVERTER); node_type_socket_templates(&ntype, rgbtobw_in, rgbtobw_out); node_type_exec(&ntype, NULL, NULL, rgbtobw_exec); diff --git a/source/blender/nodes/texture/nodes/node_texture_viewer.c b/source/blender/nodes/texture/nodes/node_texture_viewer.c index c96ff2062cb..ea69de9a1d6 100644 --- a/source/blender/nodes/texture/nodes/node_texture_viewer.c +++ b/source/blender/nodes/texture/nodes/node_texture_viewer.c @@ -29,14 +29,11 @@ static bNodeSocketTemplate inputs[] = { {SOCK_RGBA, N_("Color"), 1.0f, 0.0f, 0.0f, 1.0f}, {-1, ""}, }; -static bNodeSocketTemplate outputs[] = { - {-1, ""}, -}; static void exec(void *data, int UNUSED(thread), bNode *UNUSED(node), - bNodeExecData *execdata, + bNodeExecData *UNUSED(execdata), bNodeStack **in, bNodeStack **UNUSED(out)) { @@ -48,7 +45,6 @@ static void exec(void *data, params_from_cdata(¶ms, cdata); tex_input_rgba(col, in[0], ¶ms, cdata->thread); - tex_do_preview(execdata->preview, params.previewco, col, cdata->do_manage); } } @@ -56,12 +52,12 @@ void register_node_type_tex_viewer(void) { static bNodeType ntype; - tex_node_type_base(&ntype, TEX_NODE_VIEWER, "Viewer", NODE_CLASS_OUTPUT, NODE_PREVIEW); - node_type_socket_templates(&ntype, inputs, outputs); + tex_node_type_base(&ntype, TEX_NODE_VIEWER, "Viewer", NODE_CLASS_OUTPUT); + node_type_socket_templates(&ntype, inputs, NULL); node_type_exec(&ntype, NULL, NULL, exec); - /* Do not allow muting viewer node. */ - node_type_internal_links(&ntype, NULL); + ntype.no_muting = true; + ntype.flag |= NODE_PREVIEW; nodeRegisterType(&ntype); } |