Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/nodes')
-rw-r--r--source/blender/nodes/CMakeLists.txt22
-rw-r--r--source/blender/nodes/NOD_derived_node_tree.hh172
-rw-r--r--source/blender/nodes/NOD_geometry.h7
-rw-r--r--source/blender/nodes/NOD_geometry_exec.hh209
-rw-r--r--source/blender/nodes/NOD_geometry_nodes_eval_log.hh411
-rw-r--r--source/blender/nodes/NOD_geometry_nodes_lazy_function.hh181
-rw-r--r--source/blender/nodes/NOD_geometry_nodes_log.hh340
-rw-r--r--source/blender/nodes/NOD_multi_function.hh26
-rw-r--r--source/blender/nodes/NOD_node_declaration.hh18
-rw-r--r--source/blender/nodes/NOD_node_tree_ref.hh760
-rw-r--r--source/blender/nodes/NOD_shader.h1
-rw-r--r--source/blender/nodes/NOD_static_types.h14
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_alpha_over.cc4
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_bilateralblur.cc55
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_blur.cc401
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_bokehblur.cc103
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_bokehimage.cc48
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_boxmask.cc13
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_channel_matte.cc15
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_chroma_matte.cc13
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_color_matte.cc13
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_color_spill.cc21
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_colorbalance.cc23
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc53
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_crop.cc9
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_curves.cc18
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_despeckle.cc54
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_diff_matte.cc11
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_dilate.cc468
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_directionalblur.cc122
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_distance_matte.cc13
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_ellipsemask.cc13
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_filter.cc112
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_huecorrect.cc6
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_image.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_lensdist.cc13
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_luma_matte.cc11
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_map_value.cc21
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_movieclip.cc11
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_normal.cc7
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_pixelate.cc29
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_rgb.cc4
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_scale.cc149
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_sepcomb_color.cc22
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_setalpha.cc9
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_translate.cc13
-rw-r--r--source/blender/nodes/function/node_function_util.hh2
-rw-r--r--source/blender/nodes/function/nodes/node_fn_align_euler_to_vector.cc2
-rw-r--r--source/blender/nodes/function/nodes/node_fn_boolean_math.cc2
-rw-r--r--source/blender/nodes/function/nodes/node_fn_combine_color.cc2
-rw-r--r--source/blender/nodes/function/nodes/node_fn_compare.cc2
-rw-r--r--source/blender/nodes/function/nodes/node_fn_float_to_int.cc2
-rw-r--r--source/blender/nodes/function/nodes/node_fn_input_bool.cc2
-rw-r--r--source/blender/nodes/function/nodes/node_fn_input_color.cc2
-rw-r--r--source/blender/nodes/function/nodes/node_fn_input_int.cc2
-rw-r--r--source/blender/nodes/function/nodes/node_fn_input_string.cc2
-rw-r--r--source/blender/nodes/function/nodes/node_fn_input_vector.cc2
-rw-r--r--source/blender/nodes/function/nodes/node_fn_rotate_euler.cc2
-rw-r--r--source/blender/nodes/geometry/CMakeLists.txt21
-rw-r--r--source/blender/nodes/geometry/node_geometry_exec.cc1
-rw-r--r--source/blender/nodes/geometry/node_geometry_tree.cc5
-rw-r--r--source/blender/nodes/geometry/node_geometry_util.hh14
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc40
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_boolean.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc16
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc28
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc8
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc5
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc17
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc41
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc12
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_set_handle_type.cc37
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc53
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc5
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc5
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc397
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc482
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc36
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc385
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_distribute_points_in_volume.cc285
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc105
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc191
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc121
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_curves.cc13
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_selection.cc45
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_edge_split.cc23
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc183
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc19
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc25
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_geometry_to_instance.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc20
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_instance_rotation.cc22
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_instance_scale.cc22
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_angle.cc78
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_neighbors.cc28
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_vertices.cc64
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_area.cc32
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_is_planar.cc48
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_neighbors.cc66
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc53
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_mesh_vertex_neighbors.cc58
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_named_attribute.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_shortest_edge_paths.cc79
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc23
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc25
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc9
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_interpolate_domain.cc22
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_material_selection.cc38
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc42
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_face_set_boundaries.cc89
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc8
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc24
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc10
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc10
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc7
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc46
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_to_volume.cc3
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_object_info.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_points.cc9
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc25
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc7
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_raycast.cc8
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_remove_attribute.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc6
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_sample_index.cc337
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_sample_nearest.cc345
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_sample_nearest_surface.cc280
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_scale_elements.cc158
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc5
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_self_object.cc29
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc37
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc23
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc24
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_id.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_material.cc13
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc20
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc20
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_position.cc9
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc29
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc24
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc28
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc11
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_string_join.cc5
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc22
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc826
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_transform.cc78
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc5
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_triangulate.cc9
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_uv_pack_islands.cc44
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_uv_unwrap.cc47
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_volume_cube.cc21
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc13
-rw-r--r--source/blender/nodes/intern/derived_node_tree.cc139
-rw-r--r--source/blender/nodes/intern/geometry_nodes_eval_log.cc520
-rw-r--r--source/blender/nodes/intern/geometry_nodes_lazy_function.cc1423
-rw-r--r--source/blender/nodes/intern/geometry_nodes_log.cc610
-rw-r--r--source/blender/nodes/intern/node_common.cc13
-rw-r--r--source/blender/nodes/intern/node_geometry_exec.cc71
-rw-r--r--source/blender/nodes/intern/node_multi_function.cc26
-rw-r--r--source/blender/nodes/intern/node_tree_ref.cc679
-rw-r--r--source/blender/nodes/intern/node_util.c2
-rw-r--r--source/blender/nodes/shader/CMakeLists.txt27
-rw-r--r--source/blender/nodes/shader/node_shader_tree.cc30
-rw-r--r--source/blender/nodes/shader/node_shader_util.hh2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_attribute.cc16
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc21
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_color_ramp.cc2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_curves.cc6
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_math.cc7
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_mix.cc443
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_mix_rgb.cc6
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_brick.cc2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_coord.cc4
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_environment.cc6
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_gradient.cc2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_magic.cc2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_sky.cc52
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_wave.cc2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_vector_math.cc2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_vector_transform.cc2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_volume_info.cc2
-rw-r--r--source/blender/nodes/texture/CMakeLists.txt15
-rw-r--r--source/blender/nodes/texture/node_texture_tree.c6
191 files changed, 8080 insertions, 6237 deletions
diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt
index 386e5fe14c9..e9c0f2be368 100644
--- a/source/blender/nodes/CMakeLists.txt
+++ b/source/blender/nodes/CMakeLists.txt
@@ -29,7 +29,6 @@ set(INC
../makesrna
../render
../windowmanager
- ../../../intern/glew-mx
../../../intern/guardedalloc
# dna_type_offsets.h
@@ -41,7 +40,8 @@ set(INC
set(SRC
intern/derived_node_tree.cc
- intern/geometry_nodes_eval_log.cc
+ intern/geometry_nodes_lazy_function.cc
+ intern/geometry_nodes_log.cc
intern/math_functions.cc
intern/node_common.cc
intern/node_declaration.cc
@@ -50,7 +50,6 @@ set(SRC
intern/node_multi_function.cc
intern/node_socket.cc
intern/node_socket_declarations.cc
- intern/node_tree_ref.cc
intern/node_util.c
intern/socket_search_link.cc
@@ -60,11 +59,10 @@ set(SRC
NOD_function.h
NOD_geometry.h
NOD_geometry_exec.hh
- NOD_geometry_nodes_eval_log.hh
+ NOD_geometry_nodes_lazy_function.hh
NOD_math_functions.hh
NOD_multi_function.hh
NOD_node_declaration.hh
- NOD_node_tree_ref.hh
NOD_shader.h
NOD_socket.h
NOD_socket_declarations.hh
@@ -104,20 +102,6 @@ if(WITH_BULLET)
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_TBB)
list(APPEND INC_SYS
${TBB_INCLUDE_DIRS}
diff --git a/source/blender/nodes/NOD_derived_node_tree.hh b/source/blender/nodes/NOD_derived_node_tree.hh
index b0799d90dcd..b3775e729da 100644
--- a/source/blender/nodes/NOD_derived_node_tree.hh
+++ b/source/blender/nodes/NOD_derived_node_tree.hh
@@ -5,16 +5,17 @@
/** \file
* \ingroup nodes
*
- * DerivedNodeTree builds on top of NodeTreeRef and makes working with (nested) node groups more
- * convenient and safe. It does so by pairing nodes and sockets with a context. The context
- * contains information about the current "instance" of the node or socket. A node might be
- * "instanced" multiple times when it is in a node group that is used multiple times.
+ * DerivedNodeTree makes working with (nested) node groups more convenient and safe. It does so by
+ * pairing nodes and sockets with a context. The context contains information about the current
+ * "instance" of the node or socket. A node might be "instanced" multiple times when it is in a
+ * node group that is used multiple times.
*/
#include "BLI_function_ref.hh"
+#include "BLI_linear_allocator.hh"
#include "BLI_vector_set.hh"
-#include "NOD_node_tree_ref.hh"
+#include "BKE_node_runtime.hh"
namespace blender::nodes {
@@ -40,20 +41,20 @@ class DTreeContext {
DTreeContext *parent_context_;
/* Null when this context is for the root node group. Otherwise it points to the group node in
* the parent node group that contains this context. */
- const NodeRef *parent_node_;
+ const bNode *parent_node_;
/* The current node tree. */
- const NodeTreeRef *tree_;
+ const bNodeTree *btree_;
/* All the children contexts of this context. */
- Map<const NodeRef *, DTreeContext *> children_;
+ Map<const bNode *, DTreeContext *> children_;
DerivedNodeTree *derived_tree_;
friend DerivedNodeTree;
public:
- const NodeTreeRef &tree() const;
+ const bNodeTree &btree() const;
const DTreeContext *parent_context() const;
- const NodeRef *parent_node() const;
- const DTreeContext *child_context(const NodeRef &node) const;
+ const bNode *parent_node() const;
+ const DTreeContext *child_context(const bNode &node) const;
const DerivedNodeTree &derived_tree() const;
bool is_root() const;
};
@@ -65,15 +66,16 @@ class DTreeContext {
class DNode {
private:
const DTreeContext *context_ = nullptr;
- const NodeRef *node_ref_ = nullptr;
+ const bNode *bnode_ = nullptr;
public:
DNode() = default;
- DNode(const DTreeContext *context, const NodeRef *node);
+ DNode(const DTreeContext *context, const bNode *node);
const DTreeContext *context() const;
- const NodeRef *node_ref() const;
- const NodeRef *operator->() const;
+ const bNode *bnode() const;
+ const bNode *operator->() const;
+ const bNode &operator*() const;
friend bool operator==(const DNode &a, const DNode &b);
friend bool operator!=(const DNode &a, const DNode &b);
@@ -98,17 +100,18 @@ class DNode {
class DSocket {
protected:
const DTreeContext *context_ = nullptr;
- const SocketRef *socket_ref_ = nullptr;
+ const bNodeSocket *bsocket_ = nullptr;
public:
DSocket() = default;
- DSocket(const DTreeContext *context, const SocketRef *socket);
+ DSocket(const DTreeContext *context, const bNodeSocket *socket);
DSocket(const DInputSocket &input_socket);
DSocket(const DOutputSocket &output_socket);
const DTreeContext *context() const;
- const SocketRef *socket_ref() const;
- const SocketRef *operator->() const;
+ const bNodeSocket *bsocket() const;
+ const bNodeSocket *operator->() const;
+ const bNodeSocket &operator*() const;
friend bool operator==(const DSocket &a, const DSocket &b);
friend bool operator!=(const DSocket &a, const DSocket &b);
@@ -123,12 +126,9 @@ class DSocket {
class DInputSocket : public DSocket {
public:
DInputSocket() = default;
- DInputSocket(const DTreeContext *context, const InputSocketRef *socket);
+ DInputSocket(const DTreeContext *context, const bNodeSocket *socket);
explicit DInputSocket(const DSocket &base_socket);
- const InputSocketRef *socket_ref() const;
- const InputSocketRef *operator->() const;
-
DOutputSocket get_corresponding_group_node_output() const;
Vector<DOutputSocket, 4> get_corresponding_group_input_sockets() const;
@@ -144,12 +144,9 @@ class DInputSocket : public DSocket {
class DOutputSocket : public DSocket {
public:
DOutputSocket() = default;
- DOutputSocket(const DTreeContext *context, const OutputSocketRef *socket);
+ DOutputSocket(const DTreeContext *context, const bNodeSocket *socket);
explicit DOutputSocket(const DSocket &base_socket);
- const OutputSocketRef *socket_ref() const;
- const OutputSocketRef *operator->() const;
-
DInputSocket get_corresponding_group_node_input() const;
DInputSocket get_active_corresponding_group_output_socket() const;
@@ -177,7 +174,7 @@ class DerivedNodeTree {
private:
LinearAllocator<> allocator_;
DTreeContext *root_context_;
- VectorSet<const NodeTreeRef *> used_node_tree_refs_;
+ VectorSet<const bNodeTree *> used_btrees_;
public:
/**
@@ -186,11 +183,11 @@ class DerivedNodeTree {
* has to make sure that the node tree refs added to #node_tree_refs live at least as long as the
* derived node tree.
*/
- DerivedNodeTree(bNodeTree &btree, NodeTreeRefMap &node_tree_refs);
+ DerivedNodeTree(const bNodeTree &btree);
~DerivedNodeTree();
const DTreeContext &root_context() const;
- Span<const NodeTreeRef *> used_node_tree_refs() const;
+ Span<const bNodeTree *> used_btrees() const;
/**
* \return True when there is a link cycle. Unavailable sockets are ignored.
@@ -205,9 +202,8 @@ class DerivedNodeTree {
private:
DTreeContext &construct_context_recursively(DTreeContext *parent_context,
- const NodeRef *parent_node,
- bNodeTree &btree,
- NodeTreeRefMap &node_tree_refs);
+ const bNode *parent_node,
+ const bNodeTree &btree);
void destruct_context_recursively(DTreeContext *context);
void foreach_node_in_context_recursive(const DTreeContext &context,
@@ -215,7 +211,6 @@ class DerivedNodeTree {
};
namespace derived_node_tree_types {
-using namespace node_tree_ref_types;
using nodes::DerivedNodeTree;
using nodes::DInputSocket;
using nodes::DNode;
@@ -228,9 +223,9 @@ using nodes::DTreeContext;
/** \name #DTreeContext Inline Methods
* \{ */
-inline const NodeTreeRef &DTreeContext::tree() const
+inline const bNodeTree &DTreeContext::btree() const
{
- return *tree_;
+ return *btree_;
}
inline const DTreeContext *DTreeContext::parent_context() const
@@ -238,12 +233,12 @@ inline const DTreeContext *DTreeContext::parent_context() const
return parent_context_;
}
-inline const NodeRef *DTreeContext::parent_node() const
+inline const bNode *DTreeContext::parent_node() const
{
return parent_node_;
}
-inline const DTreeContext *DTreeContext::child_context(const NodeRef &node) const
+inline const DTreeContext *DTreeContext::child_context(const bNode &node) const
{
return children_.lookup_default(&node, nullptr);
}
@@ -264,10 +259,10 @@ inline bool DTreeContext::is_root() const
/** \name #DNode Inline Methods
* \{ */
-inline DNode::DNode(const DTreeContext *context, const NodeRef *node_ref)
- : context_(context), node_ref_(node_ref)
+inline DNode::DNode(const DTreeContext *context, const bNode *bnode)
+ : context_(context), bnode_(bnode)
{
- BLI_assert(node_ref == nullptr || &node_ref->tree() == &context->tree());
+ BLI_assert(bnode == nullptr || bnode->runtime->owner_tree == &context->btree());
}
inline const DTreeContext *DNode::context() const
@@ -275,14 +270,14 @@ inline const DTreeContext *DNode::context() const
return context_;
}
-inline const NodeRef *DNode::node_ref() const
+inline const bNode *DNode::bnode() const
{
- return node_ref_;
+ return bnode_;
}
inline bool operator==(const DNode &a, const DNode &b)
{
- return a.context_ == b.context_ && a.node_ref_ == b.node_ref_;
+ return a.context_ == b.context_ && a.bnode_ == b.bnode_;
}
inline bool operator!=(const DNode &a, const DNode &b)
@@ -292,37 +287,43 @@ inline bool operator!=(const DNode &a, const DNode &b)
inline DNode::operator bool() const
{
- return node_ref_ != nullptr;
+ return bnode_ != nullptr;
+}
+
+inline const bNode *DNode::operator->() const
+{
+ return bnode_;
}
-inline const NodeRef *DNode::operator->() const
+inline const bNode &DNode::operator*() const
{
- return node_ref_;
+ BLI_assert(bnode_ != nullptr);
+ return *bnode_;
}
inline uint64_t DNode::hash() const
{
- return get_default_hash_2(context_, node_ref_);
+ return get_default_hash_2(context_, bnode_);
}
inline DInputSocket DNode::input(int index) const
{
- return {context_, &node_ref_->input(index)};
+ return {context_, &bnode_->input_socket(index)};
}
inline DOutputSocket DNode::output(int index) const
{
- return {context_, &node_ref_->output(index)};
+ return {context_, &bnode_->output_socket(index)};
}
inline DInputSocket DNode::input_by_identifier(StringRef identifier) const
{
- return {context_, &node_ref_->input_by_identifier(identifier)};
+ return {context_, &bnode_->input_by_identifier(identifier)};
}
inline DOutputSocket DNode::output_by_identifier(StringRef identifier) const
{
- return {context_, &node_ref_->output_by_identifier(identifier)};
+ return {context_, &bnode_->output_by_identifier(identifier)};
}
/** \} */
@@ -331,19 +332,20 @@ inline DOutputSocket DNode::output_by_identifier(StringRef identifier) const
/** \name #DSocket Inline Methods
* \{ */
-inline DSocket::DSocket(const DTreeContext *context, const SocketRef *socket_ref)
- : context_(context), socket_ref_(socket_ref)
+inline DSocket::DSocket(const DTreeContext *context, const bNodeSocket *bsocket)
+ : context_(context), bsocket_(bsocket)
{
- BLI_assert(socket_ref == nullptr || &socket_ref->tree() == &context->tree());
+ BLI_assert(bsocket == nullptr ||
+ bsocket->runtime->owner_node->runtime->owner_tree == &context->btree());
}
inline DSocket::DSocket(const DInputSocket &input_socket)
- : DSocket(input_socket.context_, input_socket.socket_ref_)
+ : DSocket(input_socket.context_, input_socket.bsocket_)
{
}
inline DSocket::DSocket(const DOutputSocket &output_socket)
- : DSocket(output_socket.context_, output_socket.socket_ref_)
+ : DSocket(output_socket.context_, output_socket.bsocket_)
{
}
@@ -352,14 +354,14 @@ inline const DTreeContext *DSocket::context() const
return context_;
}
-inline const SocketRef *DSocket::socket_ref() const
+inline const bNodeSocket *DSocket::bsocket() const
{
- return socket_ref_;
+ return bsocket_;
}
inline bool operator==(const DSocket &a, const DSocket &b)
{
- return a.context_ == b.context_ && a.socket_ref_ == b.socket_ref_;
+ return a.context_ == b.context_ && a.bsocket_ == b.bsocket_;
}
inline bool operator!=(const DSocket &a, const DSocket &b)
@@ -369,23 +371,29 @@ inline bool operator!=(const DSocket &a, const DSocket &b)
inline DSocket::operator bool() const
{
- return socket_ref_ != nullptr;
+ return bsocket_ != nullptr;
}
-inline const SocketRef *DSocket::operator->() const
+inline const bNodeSocket *DSocket::operator->() const
{
- return socket_ref_;
+ return bsocket_;
+}
+
+inline const bNodeSocket &DSocket::operator*() const
+{
+ BLI_assert(bsocket_ != nullptr);
+ return *bsocket_;
}
inline uint64_t DSocket::hash() const
{
- return get_default_hash_2(context_, socket_ref_);
+ return get_default_hash_2(context_, bsocket_);
}
inline DNode DSocket::node() const
{
- BLI_assert(socket_ref_ != nullptr);
- return {context_, &socket_ref_->node()};
+ BLI_assert(bsocket_ != nullptr);
+ return {context_, bsocket_->runtime->owner_node};
}
/** \} */
@@ -394,8 +402,8 @@ inline DNode DSocket::node() const
/** \name #DInputSocket Inline Methods
* \{ */
-inline DInputSocket::DInputSocket(const DTreeContext *context, const InputSocketRef *socket_ref)
- : DSocket(context, socket_ref)
+inline DInputSocket::DInputSocket(const DTreeContext *context, const bNodeSocket *bsocket)
+ : DSocket(context, bsocket)
{
}
@@ -404,24 +412,14 @@ inline DInputSocket::DInputSocket(const DSocket &base_socket) : DSocket(base_soc
BLI_assert(base_socket->is_input());
}
-inline const InputSocketRef *DInputSocket::socket_ref() const
-{
- return (const InputSocketRef *)socket_ref_;
-}
-
-inline const InputSocketRef *DInputSocket::operator->() const
-{
- return (const InputSocketRef *)socket_ref_;
-}
-
/** \} */
/* -------------------------------------------------------------------- */
/** \name #DOutputSocket Inline Methods
* \{ */
-inline DOutputSocket::DOutputSocket(const DTreeContext *context, const OutputSocketRef *socket_ref)
- : DSocket(context, socket_ref)
+inline DOutputSocket::DOutputSocket(const DTreeContext *context, const bNodeSocket *bsocket)
+ : DSocket(context, bsocket)
{
}
@@ -430,16 +428,6 @@ inline DOutputSocket::DOutputSocket(const DSocket &base_socket) : DSocket(base_s
BLI_assert(base_socket->is_output());
}
-inline const OutputSocketRef *DOutputSocket::socket_ref() const
-{
- return (const OutputSocketRef *)socket_ref_;
-}
-
-inline const OutputSocketRef *DOutputSocket::operator->() const
-{
- return (const OutputSocketRef *)socket_ref_;
-}
-
/** \} */
/* -------------------------------------------------------------------- */
@@ -451,9 +439,9 @@ inline const DTreeContext &DerivedNodeTree::root_context() const
return *root_context_;
}
-inline Span<const NodeTreeRef *> DerivedNodeTree::used_node_tree_refs() const
+inline Span<const bNodeTree *> DerivedNodeTree::used_btrees() const
{
- return used_node_tree_refs_;
+ return used_btrees_;
}
/** \} */
diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h
index e16cd7a253f..d02bbeb67f7 100644
--- a/source/blender/nodes/NOD_geometry.h
+++ b/source/blender/nodes/NOD_geometry.h
@@ -49,6 +49,7 @@ void register_node_type_geo_curve_to_points(void);
void register_node_type_geo_curve_trim(void);
void register_node_type_geo_deform_curves_on_surface(void);
void register_node_type_geo_delete_geometry(void);
+void register_node_type_geo_distribute_points_in_volume(void);
void register_node_type_geo_distribute_points_on_faces(void);
void register_node_type_geo_dual_mesh(void);
void register_node_type_geo_duplicate_elements(void);
@@ -95,6 +96,7 @@ void register_node_type_geo_join_geometry(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_face_set_boundaries(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);
@@ -116,11 +118,15 @@ void register_node_type_geo_raycast(void);
void register_node_type_geo_realize_instances(void);
void register_node_type_geo_remove_attribute(void);
void register_node_type_geo_rotate_instances(void);
+void register_node_type_geo_sample_index(void);
+void register_node_type_geo_sample_nearest_surface(void);
+void register_node_type_geo_sample_nearest(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_self_object(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);
@@ -137,7 +143,6 @@ 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);
diff --git a/source/blender/nodes/NOD_geometry_exec.hh b/source/blender/nodes/NOD_geometry_exec.hh
index c5bc42b059d..73e82f741ab 100644
--- a/source/blender/nodes/NOD_geometry_exec.hh
+++ b/source/blender/nodes/NOD_geometry_exec.hh
@@ -3,6 +3,7 @@
#pragma once
#include "FN_field.hh"
+#include "FN_lazy_function.hh"
#include "FN_multi_function_builder.hh"
#include "BKE_geometry_fields.hh"
@@ -11,9 +12,8 @@
#include "DNA_node_types.h"
#include "NOD_derived_node_tree.hh"
-#include "NOD_geometry_nodes_eval_log.hh"
+#include "NOD_geometry_nodes_lazy_function.hh"
-struct Depsgraph;
struct ModifierData;
namespace blender::nodes {
@@ -28,8 +28,6 @@ using bke::AttributeReader;
using bke::AttributeWriter;
using bke::GAttributeReader;
using bke::GAttributeWriter;
-using bke::GeometryComponentFieldContext;
-using bke::GeometryFieldInput;
using bke::GSpanAttributeWriter;
using bke::MutableAttributeAccessor;
using bke::SpanAttributeWriter;
@@ -42,75 +40,18 @@ using fn::FieldInput;
using fn::FieldOperation;
using fn::GField;
using fn::ValueOrField;
-using geometry_nodes_eval_log::eNamedAttrUsage;
-using geometry_nodes_eval_log::NodeWarningType;
-
-/**
- * This class exists to separate the memory management details of the geometry nodes evaluator
- * from the node execution functions and related utilities.
- */
-class GeoNodeExecParamsProvider {
- public:
- DNode dnode;
- const Object *self_object = nullptr;
- const ModifierData *modifier = nullptr;
- Depsgraph *depsgraph = nullptr;
- geometry_nodes_eval_log::GeoLogger *logger = nullptr;
-
- /**
- * Returns true when the node is allowed to get/extract the input value. The identifier is
- * expected to be valid. This may return false if the input value has been consumed already.
- */
- virtual bool can_get_input(StringRef identifier) const = 0;
-
- /**
- * Returns true when the node is allowed to set the output value. The identifier is expected to
- * be valid. This may return false if the output value has been set already.
- */
- virtual bool can_set_output(StringRef identifier) const = 0;
-
- /**
- * Take ownership of an input value. The caller is responsible for destructing the value. It does
- * not have to be freed, because the memory is managed by the geometry nodes evaluator.
- */
- virtual GMutablePointer extract_input(StringRef identifier) = 0;
-
- /**
- * Similar to #extract_input, but has to be used for multi-input sockets.
- */
- virtual Vector<GMutablePointer> extract_multi_input(StringRef identifier) = 0;
-
- /**
- * Get the input value for the identifier without taking ownership of it.
- */
- virtual GPointer get_input(StringRef identifier) const = 0;
-
- /**
- * Prepare a memory buffer for an output value of the node. The returned memory has to be
- * initialized by the caller. The identifier and type are expected to be correct.
- */
- virtual GMutablePointer alloc_output_value(const CPPType &type) = 0;
-
- /**
- * The value has been allocated with #alloc_output_value.
- */
- virtual void set_output(StringRef identifier, GMutablePointer value) = 0;
-
- /* A description for these methods is provided in GeoNodeExecParams. */
- virtual void set_input_unused(StringRef identifier) = 0;
- 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;
-};
+using geo_eval_log::NamedAttributeUsage;
+using geo_eval_log::NodeWarningType;
class GeoNodeExecParams {
private:
- GeoNodeExecParamsProvider *provider_;
+ const bNode &node_;
+ lf::Params &params_;
+ const lf::Context &lf_context_;
public:
- GeoNodeExecParams(GeoNodeExecParamsProvider &provider) : provider_(&provider)
+ GeoNodeExecParams(const bNode &node, lf::Params &params, const lf::Context &lf_context)
+ : node_(node), params_(params), lf_context_(lf_context)
{
}
@@ -121,20 +62,6 @@ class GeoNodeExecParams {
/**
* Get the input value for the input socket with the given identifier.
*
- * The node calling becomes responsible for destructing the value before it is done
- * executing. This method can only be called once for each identifier.
- */
- GMutablePointer extract_input(StringRef identifier)
- {
-#ifdef DEBUG
- this->check_input_access(identifier);
-#endif
- return provider_->extract_input(identifier);
- }
-
- /**
- * Get the input value for the input socket with the given identifier.
- *
* This method can only be called once for each identifier.
*/
template<typename T> T extract_input(StringRef identifier)
@@ -153,8 +80,8 @@ class GeoNodeExecParams {
#ifdef DEBUG
this->check_input_access(identifier, &CPPType::get<T>());
#endif
- GMutablePointer gvalue = this->extract_input(identifier);
- T value = gvalue.relocate_out<T>();
+ const int index = this->get_input_index(identifier);
+ T value = params_.extract_input<T>(index);
if constexpr (std::is_same_v<T, GeometrySet>) {
this->check_input_geometry_set(identifier, value);
}
@@ -166,27 +93,6 @@ class GeoNodeExecParams {
void check_output_geometry_set(const GeometrySet &geometry_set) const;
/**
- * Get input as vector for multi input socket with the given identifier.
- *
- * This method can only be called once for each identifier.
- */
- template<typename T> Vector<T> extract_multi_input(StringRef identifier)
- {
- Vector<GMutablePointer> gvalues = provider_->extract_multi_input(identifier);
- Vector<T> values;
- for (GMutablePointer gvalue : gvalues) {
- 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>());
- }
- }
- return values;
- }
-
- /**
* Get the input value for the input socket with the given identifier.
*/
template<typename T> T get_input(StringRef identifier) const
@@ -204,9 +110,8 @@ class GeoNodeExecParams {
#ifdef DEBUG
this->check_input_access(identifier, &CPPType::get<T>());
#endif
- GPointer gvalue = provider_->get_input(identifier);
- BLI_assert(gvalue.is_type<T>());
- const T &value = *(const T *)gvalue.get();
+ const int index = this->get_input_index(identifier);
+ const T &value = params_.get_input<T>(index);
if constexpr (std::is_same_v<T, GeometrySet>) {
this->check_input_geometry_set(identifier, value);
}
@@ -228,17 +133,28 @@ class GeoNodeExecParams {
this->set_output(identifier, ValueOrField<BaseType>(std::forward<T>(value)));
}
else {
- const CPPType &type = CPPType::get<StoredT>();
#ifdef DEBUG
+ const CPPType &type = CPPType::get<StoredT>();
this->check_output_access(identifier, type);
#endif
if constexpr (std::is_same_v<StoredT, GeometrySet>) {
this->check_output_geometry_set(value);
}
- GMutablePointer gvalue = provider_->alloc_output_value(type);
- new (gvalue.get()) StoredT(std::forward<T>(value));
- provider_->set_output(identifier, gvalue);
+ const int index = this->get_output_index(identifier);
+ params_.set_output(index, std::forward<T>(value));
+ }
+ }
+
+ geo_eval_log::GeoTreeLogger *get_local_tree_logger() const
+ {
+ GeoNodesLFUserData *user_data = this->user_data();
+ BLI_assert(user_data != nullptr);
+ const ComputeContext *compute_context = user_data->compute_context;
+ BLI_assert(compute_context != nullptr);
+ if (user_data->modifier_data->eval_log == nullptr) {
+ return nullptr;
}
+ return &user_data->modifier_data->eval_log->get_local_tree_logger(*compute_context);
}
/**
@@ -246,7 +162,8 @@ class GeoNodeExecParams {
*/
void set_input_unused(StringRef identifier)
{
- provider_->set_input_unused(identifier);
+ const int index = this->get_input_index(identifier);
+ params_.set_input_unused(index);
}
/**
@@ -256,7 +173,8 @@ class GeoNodeExecParams {
*/
bool output_is_required(StringRef identifier) const
{
- return provider_->output_is_required(identifier);
+ const int index = this->get_output_index(identifier);
+ return params_.get_output_usage(index) != lf::ValueUsage::Unused;
}
/**
@@ -267,7 +185,8 @@ class GeoNodeExecParams {
*/
bool lazy_require_input(StringRef identifier)
{
- return provider_->lazy_require_input(identifier);
+ const int index = this->get_input_index(identifier);
+ return params_.try_get_input_data_ptr_or_request(index) == nullptr;
}
/**
@@ -277,7 +196,8 @@ class GeoNodeExecParams {
*/
bool lazy_output_is_required(StringRef identifier)
{
- return provider_->lazy_output_is_required(identifier);
+ const int index = this->get_output_index(identifier);
+ return params_.get_output_usage(index) == lf::ValueUsage::Used;
}
/**
@@ -285,30 +205,45 @@ class GeoNodeExecParams {
*/
const bNode &node() const
{
- return *provider_->dnode->bnode();
+ return node_;
}
const Object *self_object() const
{
- return provider_->self_object;
+ if (const auto *data = this->user_data()) {
+ if (data->modifier_data) {
+ return data->modifier_data->self_object;
+ }
+ }
+ return nullptr;
}
Depsgraph *depsgraph() const
{
- return provider_->depsgraph;
+ if (const auto *data = this->user_data()) {
+ if (data->modifier_data) {
+ return data->modifier_data->depsgraph;
+ }
+ }
+ return nullptr;
+ }
+
+ GeoNodesLFUserData *user_data() const
+ {
+ return dynamic_cast<GeoNodesLFUserData *>(lf_context_.user_data);
}
/**
* Add an error message displayed at the top of the node when displaying the node tree,
* and potentially elsewhere in Blender.
*/
- void error_message_add(const NodeWarningType type, std::string message) const;
+ void error_message_add(const NodeWarningType type, StringRef message) const;
std::string attribute_producer_name() const;
void set_default_remaining_outputs();
- void used_named_attribute(std::string attribute_name, eNamedAttrUsage usage);
+ void used_named_attribute(StringRef attribute_name, NamedAttributeUsage usage);
private:
/* Utilities for detecting common errors at when using this class. */
@@ -317,6 +252,38 @@ class GeoNodeExecParams {
/* Find the active socket with the input name (not the identifier). */
const bNodeSocket *find_available_socket(const StringRef name) const;
+
+ int get_input_index(const StringRef identifier) const
+ {
+ int counter = 0;
+ for (const bNodeSocket *socket : node_.input_sockets()) {
+ if (!socket->is_available()) {
+ continue;
+ }
+ if (socket->identifier == identifier) {
+ return counter;
+ }
+ counter++;
+ }
+ BLI_assert_unreachable();
+ return -1;
+ }
+
+ int get_output_index(const StringRef identifier) const
+ {
+ int counter = 0;
+ for (const bNodeSocket *socket : node_.output_sockets()) {
+ if (!socket->is_available()) {
+ continue;
+ }
+ if (socket->identifier == identifier) {
+ return counter;
+ }
+ counter++;
+ }
+ BLI_assert_unreachable();
+ return -1;
+ }
};
} // namespace blender::nodes
diff --git a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh b/source/blender/nodes/NOD_geometry_nodes_eval_log.hh
deleted file mode 100644
index 46ba72d14d8..00000000000
--- a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh
+++ /dev/null
@@ -1,411 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-
-#pragma once
-
-/**
- * Many geometry nodes related UI features need access to data produced during evaluation. Not only
- * is the final output required but also the intermediate results. Those features include
- * attribute search, node warnings, socket inspection and the viewer node.
- *
- * This file provides the framework for logging data during evaluation and accessing the data after
- * evaluation.
- *
- * During logging every thread gets its own local logger to avoid too much locking (logging
- * generally happens for every socket). After geometry nodes evaluation is done, the thread-local
- * logging information is combined and post-processed to make it easier for the UI to lookup.
- * necessary information.
- */
-
-#include "BLI_enumerable_thread_specific.hh"
-#include "BLI_function_ref.hh"
-#include "BLI_generic_pointer.hh"
-#include "BLI_linear_allocator.hh"
-#include "BLI_map.hh"
-
-#include "BKE_geometry_set.hh"
-
-#include "NOD_derived_node_tree.hh"
-
-#include "FN_field.hh"
-
-#include <chrono>
-
-struct SpaceNode;
-struct SpaceSpreadsheet;
-
-namespace blender::nodes::geometry_nodes_eval_log {
-
-/** Contains information about a value that has been computed during geometry nodes evaluation. */
-class ValueLog {
- public:
- virtual ~ValueLog() = default;
-};
-
-/** Contains an owned copy of a value of a generic type. */
-class GenericValueLog : public ValueLog {
- private:
- GMutablePointer data_;
-
- public:
- GenericValueLog(GMutablePointer data) : data_(data)
- {
- }
-
- ~GenericValueLog()
- {
- data_.destruct();
- }
-
- GPointer value() const
- {
- return data_;
- }
-};
-
-class GFieldValueLog : public ValueLog {
- private:
- fn::GField field_;
- const 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 CPPType &type() const
- {
- return type_;
- }
-};
-
-struct GeometryAttributeInfo {
- std::string name;
- /** Can be empty when #name does not actually exist on a geometry yet. */
- std::optional<eAttrDomain> domain;
- std::optional<eCustomDataType> data_type;
-};
-
-/** Contains information about a geometry set. In most cases this does not store the entire
- * geometry set as this would require too much memory. */
-class GeometryValueLog : public ValueLog {
- private:
- Vector<GeometryAttributeInfo> attributes_;
- Vector<GeometryComponentType> component_types_;
- std::unique_ptr<GeometrySet> full_geometry_;
-
- public:
- struct MeshInfo {
- int verts_num, edges_num, faces_num;
- };
- struct CurveInfo {
- int splines_num;
- };
- struct PointCloudInfo {
- int points_num;
- };
- struct InstancesInfo {
- int instances_num;
- };
- struct EditDataInfo {
- bool has_deformed_positions;
- bool has_deform_matrices;
- };
-
- std::optional<MeshInfo> mesh_info;
- std::optional<CurveInfo> curve_info;
- std::optional<PointCloudInfo> pointcloud_info;
- std::optional<InstancesInfo> instances_info;
- std::optional<EditDataInfo> edit_data_info;
-
- GeometryValueLog(const GeometrySet &geometry_set, bool log_full_geometry = false);
-
- Span<GeometryAttributeInfo> attributes() const
- {
- return attributes_;
- }
-
- Span<GeometryComponentType> component_types() const
- {
- return component_types_;
- }
-
- const GeometrySet *full_geometry() const
- {
- return full_geometry_.get();
- }
-};
-
-enum class NodeWarningType {
- Error,
- Warning,
- Info,
-};
-
-struct NodeWarning {
- NodeWarningType type;
- std::string message;
-};
-
-struct NodeWithWarning {
- DNode node;
- 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;
- destruct_ptr<ValueLog> value;
-};
-
-enum class eNamedAttrUsage {
- None = 0,
- Read = 1 << 0,
- Write = 1 << 1,
- Remove = 1 << 2,
-};
-ENUM_OPERATORS(eNamedAttrUsage, eNamedAttrUsage::Remove);
-
-struct UsedNamedAttribute {
- std::string name;
- eNamedAttrUsage usage;
-};
-
-struct NodeWithUsedNamedAttribute {
- DNode node;
- UsedNamedAttribute attribute;
-};
-
-class GeoLogger;
-class ModifierLog;
-
-/** Every thread has its own local logger to avoid having to communicate between threads during
- * evaluation. After evaluation the individual logs are combined. */
-class LocalGeoLogger {
- private:
- /* Back pointer to the owner of this local logger. */
- GeoLogger *main_logger_;
- /* Allocator for the many small allocations during logging. This is in a `unique_ptr` so that
- * ownership can be transferred later on. */
- std::unique_ptr<LinearAllocator<>> allocator_;
- Vector<ValueOfSockets> values_;
- Vector<NodeWithWarning> node_warnings_;
- Vector<NodeWithExecutionTime> node_exec_times_;
- Vector<NodeWithDebugMessage> node_debug_messages_;
- Vector<NodeWithUsedNamedAttribute> used_named_attributes_;
-
- friend ModifierLog;
-
- public:
- LocalGeoLogger(GeoLogger &main_logger) : main_logger_(&main_logger)
- {
- this->allocator_ = std::make_unique<LinearAllocator<>>();
- }
-
- 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);
- void log_used_named_attribute(DNode node, std::string attribute_name, eNamedAttrUsage usage);
- /**
- * 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:
- /**
- * 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_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();
- }
-
- auto begin()
- {
- return threadlocals_.begin();
- }
-
- auto end()
- {
- return threadlocals_.end();
- }
-};
-
-/** Contains information that has been logged for one specific socket. */
-class SocketLog {
- private:
- ValueLog *value_ = nullptr;
-
- friend ModifierLog;
-
- public:
- const ValueLog *value() const
- {
- return value_;
- }
-};
-
-/** Contains information that has been logged for one specific node. */
-class NodeLog {
- private:
- Vector<SocketLog> input_logs_;
- Vector<SocketLog> output_logs_;
- Vector<NodeWarning, 0> warnings_;
- Vector<std::string, 0> debug_messages_;
- Vector<UsedNamedAttribute, 0> used_named_attributes_;
- 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
- {
- return input_logs_;
- }
-
- Span<SocketLog> output_logs() const
- {
- return output_logs_;
- }
-
- Span<NodeWarning> warnings() const
- {
- return warnings_;
- }
-
- Span<std::string> debug_messages() const
- {
- return debug_messages_;
- }
-
- Span<UsedNamedAttribute> used_named_attributes() const
- {
- return used_named_attributes_;
- }
-
- std::chrono::microseconds execution_time() const
- {
- return exec_time_;
- }
-
- Vector<const GeometryAttributeInfo *> lookup_available_attributes() const;
-};
-
-/** Contains information that has been logged for one specific tree. */
-class TreeLog {
- private:
- Map<std::string, destruct_ptr<NodeLog>> node_logs_;
- Map<std::string, destruct_ptr<TreeLog>> child_logs_;
-
- friend ModifierLog;
-
- public:
- const NodeLog *lookup_node_log(StringRef node_name) const;
- const NodeLog *lookup_node_log(const bNode &node) const;
- const TreeLog *lookup_child_log(StringRef node_name) const;
- void foreach_node_log(FunctionRef<void(const NodeLog &)> fn) const;
-};
-
-/** Contains information about an entire geometry nodes evaluation. */
-class ModifierLog {
- private:
- LinearAllocator<> allocator_;
- /* Allocators of the individual loggers. */
- Vector<std::unique_ptr<LinearAllocator<>>> logger_allocators_;
- 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);
-
- const TreeLog &root_tree() const
- {
- return *root_tree_logs_;
- }
-
- /* Utilities to find logged information for a specific context. */
- static const ModifierLog *find_root_by_node_editor_context(const SpaceNode &snode);
- static const TreeLog *find_tree_by_node_editor_context(const SpaceNode &snode);
- static const NodeLog *find_node_by_node_editor_context(const SpaceNode &snode,
- const bNode &node);
- static const NodeLog *find_node_by_node_editor_context(const SpaceNode &snode,
- const StringRef node_name);
- static const SocketLog *find_socket_by_node_editor_context(const SpaceNode &snode,
- const bNode &node,
- const bNodeSocket &socket);
- static const NodeLog *find_node_by_spreadsheet_editor_context(
- 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 *>;
-
- TreeLog &lookup_or_add_tree_log(LogByTreeContext &log_by_tree_context,
- const DTreeContext &tree_context);
- NodeLog &lookup_or_add_node_log(LogByTreeContext &log_by_tree_context, DNode node);
- SocketLog &lookup_or_add_socket_log(LogByTreeContext &log_by_tree_context, DSocket socket);
-};
-
-} // namespace blender::nodes::geometry_nodes_eval_log
diff --git a/source/blender/nodes/NOD_geometry_nodes_lazy_function.hh b/source/blender/nodes/NOD_geometry_nodes_lazy_function.hh
new file mode 100644
index 00000000000..240a0115f68
--- /dev/null
+++ b/source/blender/nodes/NOD_geometry_nodes_lazy_function.hh
@@ -0,0 +1,181 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+/**
+ * For evaluation, geometry node groups are converted to a lazy-function graph. The generated graph
+ * is cached per node group, so it only has to be generated once after a change.
+ *
+ * Node groups are *not* inlined into the lazy-function graph. This could be added in the future as
+ * it might improve performance in some cases, but generally does not seem necessary. Inlining node
+ * groups also has disadvantages like making per-node-group caches less useful, resulting in more
+ * overhead.
+ *
+ * Instead, group nodes are just like all other nodes in the lazy-function graph. What makes them
+ * special is that they reference the lazy-function graph of the group they reference.
+ *
+ * During lazy-function graph generation, a mapping between the #bNodeTree and
+ * #lazy_function::Graph is build that can be used when evaluating the graph (e.g. for logging).
+ */
+
+#include "FN_lazy_function_graph.hh"
+#include "FN_lazy_function_graph_executor.hh"
+
+#include "NOD_geometry_nodes_log.hh"
+#include "NOD_multi_function.hh"
+
+#include "BLI_compute_context.hh"
+
+struct Object;
+struct Depsgraph;
+
+namespace blender::nodes {
+
+namespace lf = fn::lazy_function;
+using lf::LazyFunction;
+
+/**
+ * Data that is passed into geometry nodes evaluation from the modifier.
+ */
+struct GeoNodesModifierData {
+ /** Object that is currently evaluated. */
+ const Object *self_object = nullptr;
+ /** Depsgraph that is evaluating the modifier. */
+ Depsgraph *depsgraph = nullptr;
+ /** Optional logger. */
+ geo_eval_log::GeoModifierLog *eval_log = nullptr;
+ /**
+ * Some nodes should be executed even when their output is not used (e.g. active viewer nodes and
+ * the node groups they are contained in).
+ */
+ const MultiValueMap<ComputeContextHash, const lf::FunctionNode *> *side_effect_nodes;
+};
+
+/**
+ * Custom user data that is passed to every geometry nodes related lazy-function evaluation.
+ */
+struct GeoNodesLFUserData : public lf::UserData {
+ /**
+ * Data from the modifier that is being evaluated.
+ */
+ GeoNodesModifierData *modifier_data = nullptr;
+ /**
+ * Current compute context. This is different depending in the (nested) node group that is being
+ * evaluated.
+ */
+ const ComputeContext *compute_context = nullptr;
+};
+
+/**
+ * Contains the mapping between the #bNodeTree and the corresponding lazy-function graph.
+ * This is *not* a one-to-one mapping.
+ */
+struct GeometryNodeLazyFunctionGraphMapping {
+ /**
+ * Contains mapping of sockets for special nodes like group input and group output.
+ */
+ Map<const bNodeSocket *, lf::Socket *> dummy_socket_map;
+ /**
+ * The inputs sockets in the graph. Multiple group input nodes are combined into one in the
+ * lazy-function graph.
+ */
+ Vector<lf::OutputSocket *> group_input_sockets;
+ /**
+ * A mapping used for logging intermediate values.
+ */
+ MultiValueMap<const lf::Socket *, const bNodeSocket *> bsockets_by_lf_socket_map;
+ /**
+ * Mappings for some special node types. Generally, this mapping does not exist for all node
+ * types, so better have more specialized mappings for now.
+ */
+ Map<const bNode *, const lf::FunctionNode *> group_node_map;
+ Map<const bNode *, const lf::FunctionNode *> viewer_node_map;
+};
+
+/**
+ * Data that is cached for every #bNodeTree.
+ */
+struct GeometryNodesLazyFunctionGraphInfo {
+ /**
+ * Allocator used for many things contained in this struct.
+ */
+ LinearAllocator<> allocator;
+ /**
+ * Many nodes are implemented as multi-functions. So this contains a mapping from nodes to their
+ * corresponding multi-functions.
+ */
+ std::unique_ptr<NodeMultiFunctions> node_multi_functions;
+ /**
+ * Many lazy-functions are build for the lazy-function graph. Since the graph does not own them,
+ * we have to keep track of them separately.
+ */
+ Vector<std::unique_ptr<LazyFunction>> functions;
+ /**
+ * Many sockets have default values. Since those are not owned by the lazy-function graph, we
+ * have to keep track of them separately. This only owns the values, the memory is owned by the
+ * allocator above.
+ */
+ Vector<GMutablePointer> values_to_destruct;
+ /**
+ * The actual lazy-function graph.
+ */
+ lf::Graph graph;
+ /**
+ * Mappings between the lazy-function graph and the #bNodeTree.
+ */
+ GeometryNodeLazyFunctionGraphMapping mapping;
+ /**
+ * Approximate number of nodes in the graph if all sub-graphs were inlined.
+ * This can be used as a simple heuristic for the complexity of the node group.
+ */
+ int num_inline_nodes_approximate = 0;
+
+ GeometryNodesLazyFunctionGraphInfo();
+ ~GeometryNodesLazyFunctionGraphInfo();
+};
+
+/**
+ * Logs intermediate values from the lazy-function graph evaluation into #GeoModifierLog based on
+ * the mapping between the lazy-function graph and the corresponding #bNodeTree.
+ */
+class GeometryNodesLazyFunctionLogger : public fn::lazy_function::GraphExecutor::Logger {
+ private:
+ const GeometryNodesLazyFunctionGraphInfo &lf_graph_info_;
+
+ public:
+ GeometryNodesLazyFunctionLogger(const GeometryNodesLazyFunctionGraphInfo &lf_graph_info);
+ void log_socket_value(const fn::lazy_function::Socket &lf_socket,
+ GPointer value,
+ const fn::lazy_function::Context &context) const override;
+ void dump_when_outputs_are_missing(const lf::FunctionNode &node,
+ Span<const lf::OutputSocket *> missing_sockets,
+ const lf::Context &context) const override;
+ void dump_when_input_is_set_twice(const lf::InputSocket &target_socket,
+ const lf::OutputSocket &from_socket,
+ const lf::Context &context) const override;
+ void log_before_node_execute(const lf::FunctionNode &node,
+ const lf::Params &params,
+ const lf::Context &context) const override;
+};
+
+/**
+ * Tells the lazy-function graph evaluator which nodes have side effects based on the current
+ * context. For example, the same viewer node can have side effects in one context, but not in
+ * another (depending on e.g. which tree path is currently viewed in the node editor).
+ */
+class GeometryNodesLazyFunctionSideEffectProvider
+ : public fn::lazy_function::GraphExecutor::SideEffectProvider {
+ public:
+ Vector<const lf::FunctionNode *> get_nodes_with_side_effects(
+ const lf::Context &context) const override;
+};
+
+/**
+ * Main function that converts a #bNodeTree into a lazy-function graph. If the graph has been
+ * generated already, nothing is done. Under some circumstances a valid graph cannot be created. In
+ * those cases null is returned.
+ */
+const GeometryNodesLazyFunctionGraphInfo *ensure_geometry_nodes_lazy_function_graph(
+ const bNodeTree &btree);
+
+} // namespace blender::nodes
diff --git a/source/blender/nodes/NOD_geometry_nodes_log.hh b/source/blender/nodes/NOD_geometry_nodes_log.hh
new file mode 100644
index 00000000000..cf59c99bc79
--- /dev/null
+++ b/source/blender/nodes/NOD_geometry_nodes_log.hh
@@ -0,0 +1,340 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+/**
+ * Many geometry nodes related UI features need access to data produced during evaluation. Not only
+ * is the final output required but also the intermediate results. Those features include attribute
+ * search, node warnings, socket inspection and the viewer node.
+ *
+ * This file provides the system for logging data during evaluation and accessing the data after
+ * evaluation. Geometry nodes is executed by a modifier, therefore the "root" of logging is
+ * #GeoModifierLog which will contain all data generated in a modifier.
+ *
+ * The system makes a distinction between "loggers" and the "log":
+ * - Logger (#GeoTreeLogger): Is used during geometry nodes evaluation. Each thread logs data
+ * independently to avoid communication between threads. Logging should generally be fast.
+ * Generally, the logged data is just dumped into simple containers. Any processing of the data
+ * happens later if necessary. This is important for performance, because in practice, most of
+ * the logged data is never used again. So any processing of the data is likely to be a waste of
+ * resources.
+ * - Log (#GeoTreeLog, #GeoNodeLog): Those are used when accessing logged data in UI code. They
+ * contain and cache preprocessed data produced during logging. The log combines data from all
+ * thread-local loggers to provide simple access. Importantly, the (preprocessed) log is only
+ * created when it is actually used by UI code.
+ */
+
+#include <chrono>
+
+#include "BLI_compute_context.hh"
+#include "BLI_enumerable_thread_specific.hh"
+#include "BLI_generic_pointer.hh"
+#include "BLI_multi_value_map.hh"
+
+#include "BKE_attribute.h"
+#include "BKE_geometry_set.hh"
+
+#include "FN_field.hh"
+
+#include "DNA_node_types.h"
+
+struct SpaceNode;
+struct SpaceSpreadsheet;
+struct NodesModifierData;
+
+namespace blender::nodes::geo_eval_log {
+
+using fn::GField;
+
+enum class NodeWarningType {
+ Error,
+ Warning,
+ Info,
+};
+
+struct NodeWarning {
+ NodeWarningType type;
+ std::string message;
+};
+
+enum class NamedAttributeUsage {
+ None = 0,
+ Read = 1 << 0,
+ Write = 1 << 1,
+ Remove = 1 << 2,
+};
+ENUM_OPERATORS(NamedAttributeUsage, NamedAttributeUsage::Remove);
+
+/**
+ * Values of different types are logged differently. This is necessary because some types are so
+ * simple that we can log them entirely (e.g. `int`), while we don't want to log all intermediate
+ * geometries in their entirety.
+ *
+ * #ValueLog is a base class for the different ways we log values.
+ */
+class ValueLog {
+ public:
+ virtual ~ValueLog() = default;
+};
+
+/**
+ * Simplest logger. It just stores a copy of the entire value. This is used for most simple types
+ * like `int`.
+ */
+class GenericValueLog : public ValueLog {
+ public:
+ /**
+ * This is owning the value, but not the memory.
+ */
+ GMutablePointer value;
+
+ GenericValueLog(const GMutablePointer value) : value(value)
+ {
+ }
+
+ ~GenericValueLog();
+};
+
+/**
+ * Fields are not logged entirely, because they might contain arbitrarily large data (e.g.
+ * geometries that are sampled). Instead, only the data needed for UI features is logged.
+ */
+class FieldInfoLog : public ValueLog {
+ public:
+ const CPPType &type;
+ Vector<std::string> input_tooltips;
+
+ FieldInfoLog(const GField &field);
+};
+
+struct GeometryAttributeInfo {
+ std::string name;
+ /** Can be empty when #name does not actually exist on a geometry yet. */
+ std::optional<eAttrDomain> domain;
+ std::optional<eCustomDataType> data_type;
+};
+
+/**
+ * Geometries are not logged entirely, because that would result in a lot of time and memory
+ * overhead. Instead, only the data needed for UI features is logged.
+ */
+class GeometryInfoLog : public ValueLog {
+ public:
+ Vector<GeometryAttributeInfo> attributes;
+ Vector<GeometryComponentType> component_types;
+
+ struct MeshInfo {
+ int verts_num, edges_num, faces_num;
+ };
+ struct CurveInfo {
+ int splines_num;
+ };
+ struct PointCloudInfo {
+ int points_num;
+ };
+ struct InstancesInfo {
+ int instances_num;
+ };
+ struct EditDataInfo {
+ bool has_deformed_positions;
+ bool has_deform_matrices;
+ };
+
+ std::optional<MeshInfo> mesh_info;
+ std::optional<CurveInfo> curve_info;
+ std::optional<PointCloudInfo> pointcloud_info;
+ std::optional<InstancesInfo> instances_info;
+ std::optional<EditDataInfo> edit_data_info;
+
+ GeometryInfoLog(const GeometrySet &geometry_set);
+};
+
+/**
+ * Data logged by a viewer node when it is executed. In this case, we do want to log the entire
+ * geometry.
+ */
+class ViewerNodeLog {
+ public:
+ GeometrySet geometry;
+ GField field;
+};
+
+using Clock = std::chrono::steady_clock;
+using TimePoint = Clock::time_point;
+
+/**
+ * Logs all data for a specific geometry node tree in a specific context. When the same node group
+ * is used in multiple times each instantiation will have a separate logger.
+ */
+class GeoTreeLogger {
+ public:
+ std::optional<ComputeContextHash> parent_hash;
+ std::optional<StringRefNull> group_node_name;
+ Vector<ComputeContextHash> children_hashes;
+
+ LinearAllocator<> *allocator = nullptr;
+
+ struct WarningWithNode {
+ StringRefNull node_name;
+ NodeWarning warning;
+ };
+ struct SocketValueLog {
+ StringRefNull node_name;
+ StringRefNull socket_identifier;
+ destruct_ptr<ValueLog> value;
+ };
+ struct NodeExecutionTime {
+ StringRefNull node_name;
+ TimePoint start;
+ TimePoint end;
+ };
+ struct ViewerNodeLogWithNode {
+ StringRefNull node_name;
+ destruct_ptr<ViewerNodeLog> viewer_log;
+ };
+ struct AttributeUsageWithNode {
+ StringRefNull node_name;
+ StringRefNull attribute_name;
+ NamedAttributeUsage usage;
+ };
+ struct DebugMessage {
+ StringRefNull node_name;
+ StringRefNull message;
+ };
+
+ Vector<WarningWithNode> node_warnings;
+ Vector<SocketValueLog> input_socket_values;
+ Vector<SocketValueLog> output_socket_values;
+ Vector<NodeExecutionTime> node_execution_times;
+ Vector<ViewerNodeLogWithNode, 0> viewer_node_logs;
+ Vector<AttributeUsageWithNode, 0> used_named_attributes;
+ Vector<DebugMessage, 0> debug_messages;
+
+ GeoTreeLogger();
+ ~GeoTreeLogger();
+
+ void log_value(const bNode &node, const bNodeSocket &socket, GPointer value);
+ void log_viewer_node(const bNode &viewer_node, const GeometrySet &geometry, const GField &field);
+};
+
+/**
+ * Contains data that has been logged for a specific node in a context. So when the node is in a
+ * node group that is used multiple times, there will be a different #GeoNodeLog for every
+ * instance.
+ *
+ * By default, not all of the info below is valid. A #GeoTreeLog::ensure_* method has to be called
+ * first.
+ */
+class GeoNodeLog {
+ public:
+ /** Warnings generated for that node. */
+ Vector<NodeWarning> warnings;
+ /**
+ * Time spend in that node. For node groups this is the sum of the run times of the nodes
+ * inside.
+ */
+ std::chrono::nanoseconds run_time{0};
+ /** Maps from socket identifiers to their values. */
+ Map<StringRefNull, ValueLog *> input_values_;
+ Map<StringRefNull, ValueLog *> output_values_;
+ /** Maps from attribute name to their usage flags. */
+ Map<StringRefNull, NamedAttributeUsage> used_named_attributes;
+ /** Messages that are used for debugging purposes during development. */
+ Vector<StringRefNull> debug_messages;
+
+ GeoNodeLog();
+ ~GeoNodeLog();
+};
+
+class GeoModifierLog;
+
+/**
+ * Contains data that has been logged for a specific node group in a context. If the same node
+ * group is used multiple times, there will be a different #GeoTreeLog for every instance.
+ *
+ * This contains lazily evaluated data. Call the corresponding `ensure_*` methods before accessing
+ * data.
+ */
+class GeoTreeLog {
+ private:
+ GeoModifierLog *modifier_log_;
+ Vector<GeoTreeLogger *> tree_loggers_;
+ VectorSet<ComputeContextHash> children_hashes_;
+ bool reduced_node_warnings_ = false;
+ bool reduced_node_run_times_ = false;
+ bool reduced_socket_values_ = false;
+ bool reduced_viewer_node_logs_ = false;
+ bool reduced_existing_attributes_ = false;
+ bool reduced_used_named_attributes_ = false;
+ bool reduced_debug_messages_ = false;
+
+ public:
+ Map<StringRefNull, GeoNodeLog> nodes;
+ Map<StringRefNull, ViewerNodeLog *, 0> viewer_node_logs;
+ Vector<NodeWarning> all_warnings;
+ std::chrono::nanoseconds run_time_sum{0};
+ Vector<const GeometryAttributeInfo *> existing_attributes;
+ Map<StringRefNull, NamedAttributeUsage> used_named_attributes;
+
+ GeoTreeLog(GeoModifierLog *modifier_log, Vector<GeoTreeLogger *> tree_loggers);
+ ~GeoTreeLog();
+
+ void ensure_node_warnings();
+ void ensure_node_run_time();
+ void ensure_socket_values();
+ void ensure_viewer_node_logs();
+ void ensure_existing_attributes();
+ void ensure_used_named_attributes();
+ void ensure_debug_messages();
+
+ ValueLog *find_socket_value_log(const bNodeSocket &query_socket);
+};
+
+/**
+ * There is one #GeoModifierLog for every modifier that evaluates geometry nodes. It contains all
+ * the loggers that are used during evaluation as well as the preprocessed logs that are used by UI
+ * code.
+ */
+class GeoModifierLog {
+ private:
+ /** Data that is stored for each thread. */
+ struct LocalData {
+ /** Each thread has its own allocator. */
+ LinearAllocator<> allocator;
+ /**
+ * Store a separate #GeoTreeLogger for each instance of the corresponding node group (e.g.
+ * when the same node group is used multiple times).
+ */
+ Map<ComputeContextHash, destruct_ptr<GeoTreeLogger>> tree_logger_by_context;
+ };
+
+ /** Container for all thread-local data. */
+ threading::EnumerableThreadSpecific<LocalData> data_per_thread_;
+ /**
+ * A #GeoTreeLog for every compute context. Those are created lazily when requested by UI code.
+ */
+ Map<ComputeContextHash, std::unique_ptr<GeoTreeLog>> tree_logs_;
+
+ public:
+ GeoModifierLog();
+ ~GeoModifierLog();
+
+ /**
+ * Get a thread-local logger for the current node tree.
+ */
+ GeoTreeLogger &get_local_tree_logger(const ComputeContext &compute_context);
+
+ /**
+ * Get a log a specific node tree instance.
+ */
+ GeoTreeLog &get_tree_log(const ComputeContextHash &compute_context_hash);
+
+ /**
+ * Utility accessor to logged data.
+ */
+ static GeoTreeLog *get_tree_log_for_node_editor(const SpaceNode &snode);
+ static const ViewerNodeLog *find_viewer_node_log_for_spreadsheet(
+ const SpaceSpreadsheet &sspreadsheet);
+};
+
+} // namespace blender::nodes::geo_eval_log
diff --git a/source/blender/nodes/NOD_multi_function.hh b/source/blender/nodes/NOD_multi_function.hh
index b6d51578b1c..676bf03927e 100644
--- a/source/blender/nodes/NOD_multi_function.hh
+++ b/source/blender/nodes/NOD_multi_function.hh
@@ -6,8 +6,6 @@
#include "DNA_node_types.h"
-#include "NOD_derived_node_tree.hh"
-
namespace blender::nodes {
using namespace fn::multi_function_types;
@@ -19,15 +17,15 @@ class NodeMultiFunctions;
*/
class NodeMultiFunctionBuilder : NonCopyable, NonMovable {
private:
- bNode &node_;
- bNodeTree &tree_;
+ const bNode &node_;
+ const bNodeTree &tree_;
std::shared_ptr<MultiFunction> owned_built_fn_;
const MultiFunction *built_fn_ = nullptr;
friend NodeMultiFunctions;
public:
- NodeMultiFunctionBuilder(bNode &node, bNodeTree &tree);
+ NodeMultiFunctionBuilder(const bNode &node, const bNodeTree &tree);
/**
* Assign a multi-function for the current node. The input and output parameters of the function
@@ -42,8 +40,8 @@ class NodeMultiFunctionBuilder : NonCopyable, NonMovable {
*/
template<typename T, typename... Args> void construct_and_set_matching_fn(Args &&...args);
- bNode &node();
- bNodeTree &tree();
+ const bNode &node();
+ const bNodeTree &tree();
};
/**
@@ -60,26 +58,26 @@ class NodeMultiFunctions {
Map<const bNode *, Item> map_;
public:
- NodeMultiFunctions(const DerivedNodeTree &tree);
+ NodeMultiFunctions(const bNodeTree &tree);
- const Item &try_get(const DNode &node) const;
+ const Item &try_get(const bNode &node) const;
};
/* -------------------------------------------------------------------- */
/** \name #NodeMultiFunctionBuilder Inline Methods
* \{ */
-inline NodeMultiFunctionBuilder::NodeMultiFunctionBuilder(bNode &node, bNodeTree &tree)
+inline NodeMultiFunctionBuilder::NodeMultiFunctionBuilder(const bNode &node, const bNodeTree &tree)
: node_(node), tree_(tree)
{
}
-inline bNode &NodeMultiFunctionBuilder::node()
+inline const bNode &NodeMultiFunctionBuilder::node()
{
return node_;
}
-inline bNodeTree &NodeMultiFunctionBuilder::tree()
+inline const bNodeTree &NodeMultiFunctionBuilder::tree()
{
return tree_;
}
@@ -107,10 +105,10 @@ inline void NodeMultiFunctionBuilder::construct_and_set_matching_fn(Args &&...ar
/** \name #NodeMultiFunctions Inline Methods
* \{ */
-inline const NodeMultiFunctions::Item &NodeMultiFunctions::try_get(const DNode &node) const
+inline const NodeMultiFunctions::Item &NodeMultiFunctions::try_get(const bNode &node) const
{
static Item empty_item;
- const Item *item = map_.lookup_ptr(node->bnode());
+ const Item *item = map_.lookup_ptr(&node);
if (item == nullptr) {
return empty_item;
}
diff --git a/source/blender/nodes/NOD_node_declaration.hh b/source/blender/nodes/NOD_node_declaration.hh
index d8b8c354230..42755b2e8dd 100644
--- a/source/blender/nodes/NOD_node_declaration.hh
+++ b/source/blender/nodes/NOD_node_declaration.hh
@@ -92,6 +92,10 @@ class SocketDeclaration {
* realtime_compositor::InputDescriptor for more information. */
int compositor_domain_priority_ = 0;
+ /** This input shouldn't be realized on the operation domain of the node. See
+ * realtime_compositor::InputDescriptor for more information. */
+ bool compositor_skip_realization_ = false;
+
/** This input expects a single value and can't operate on non-single values. See
* realtime_compositor::InputDescriptor for more information. */
bool compositor_expects_single_value_ = false;
@@ -133,6 +137,7 @@ class SocketDeclaration {
const OutputFieldDependency &output_field_dependency() const;
int compositor_domain_priority() const;
+ bool compositor_skip_realization() const;
bool compositor_expects_single_value() const;
protected:
@@ -257,6 +262,14 @@ class SocketDeclarationBuilder : public BaseSocketDeclarationBuilder {
return *(Self *)this;
}
+ /** This input shouldn't be realized on the operation domain of the node. See
+ * realtime_compositor::InputDescriptor for more information. */
+ Self &compositor_skip_realization(bool value = true)
+ {
+ decl_->compositor_skip_realization_ = value;
+ return *(Self *)this;
+ }
+
/** This input expects a single value and can't operate on non-single values. See
* realtime_compositor::InputDescriptor for more information. */
Self &compositor_expects_single_value(bool value = true)
@@ -460,6 +473,11 @@ inline int SocketDeclaration::compositor_domain_priority() const
return compositor_domain_priority_;
}
+inline bool SocketDeclaration::compositor_skip_realization() const
+{
+ return compositor_skip_realization_;
+}
+
inline bool SocketDeclaration::compositor_expects_single_value() const
{
return compositor_expects_single_value_;
diff --git a/source/blender/nodes/NOD_node_tree_ref.hh b/source/blender/nodes/NOD_node_tree_ref.hh
deleted file mode 100644
index 257aa5f4110..00000000000
--- a/source/blender/nodes/NOD_node_tree_ref.hh
+++ /dev/null
@@ -1,760 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-
-#pragma once
-
-/** \file
- * \ingroup nodes
- *
- * NodeTreeRef makes querying information about a bNodeTree more efficient. It is an immutable data
- * structure. It should not be used after anymore, after the underlying node tree changed.
- *
- * The following queries are supported efficiently:
- * - socket -> index of socket
- * - socket -> directly linked sockets
- * - socket -> directly linked links
- * - socket -> linked sockets when skipping reroutes
- * - socket -> node
- * - socket/node -> rna pointer
- * - node -> inputs/outputs
- * - node -> tree
- * - tree -> all nodes
- * - tree -> all (input/output) sockets
- * - idname -> nodes
- *
- * Every socket has an id. The id-space is shared between input and output sockets.
- * When storing data per socket, it is often better to use the id as index into an array, instead
- * of a hash table.
- *
- * Every node has an id as well. The same rule regarding hash tables applies.
- *
- * There is an utility to export this data structure as graph in dot format.
- */
-
-#include "BLI_array.hh"
-#include "BLI_function_ref.hh"
-#include "BLI_linear_allocator.hh"
-#include "BLI_map.hh"
-#include "BLI_multi_value_map.hh"
-#include "BLI_string_ref.hh"
-#include "BLI_timeit.hh"
-#include "BLI_utility_mixins.hh"
-#include "BLI_vector.hh"
-
-#include "BKE_node.h"
-#include "BKE_node_runtime.hh"
-
-#include "DNA_node_types.h"
-
-#include "RNA_access.h"
-
-namespace blender::nodes {
-
-class SocketRef;
-class InputSocketRef;
-class OutputSocketRef;
-class NodeRef;
-class NodeTreeRef;
-class LinkRef;
-class InternalLinkRef;
-
-using SocketIndexByIdentifierMap = Map<std::string, int>;
-
-class SocketRef : NonCopyable, NonMovable {
- protected:
- NodeRef *node_;
- bNodeSocket *bsocket_;
- bool is_input_;
- int id_;
- int index_;
- Vector<LinkRef *> directly_linked_links_;
-
- /* These sockets are linked directly, i.e. with a single link in between. */
- MutableSpan<const SocketRef *> directly_linked_sockets_;
- /* These sockets are linked when reroutes, muted links and muted nodes have been taken into
- * account. */
- MutableSpan<const SocketRef *> logically_linked_sockets_;
- /* These are the sockets that have been skipped when searching for logically linked sockets.
- * That includes for example the input and output socket of an intermediate reroute node. */
- MutableSpan<const SocketRef *> logically_linked_skipped_sockets_;
-
- friend NodeTreeRef;
-
- public:
- Span<const SocketRef *> logically_linked_sockets() const;
- Span<const SocketRef *> logically_linked_skipped_sockets() const;
- Span<const SocketRef *> directly_linked_sockets() const;
- Span<const LinkRef *> directly_linked_links() const;
-
- bool is_directly_linked() const;
- bool is_logically_linked() const;
-
- const NodeRef &node() const;
- const NodeTreeRef &tree() const;
-
- int id() const;
- int index() const;
-
- bool is_input() const;
- bool is_output() const;
-
- const SocketRef &as_base() const;
- const InputSocketRef &as_input() const;
- const OutputSocketRef &as_output() const;
-
- PointerRNA rna() const;
-
- StringRefNull idname() const;
- StringRefNull name() const;
- StringRefNull identifier() const;
- bNodeSocketType *typeinfo() const;
-
- bNodeSocket *bsocket() const;
- bNode *bnode() const;
- bNodeTree *btree() const;
-
- bool is_available() const;
- bool is_undefined() const;
-
- void *default_value() const;
- template<typename T> T *default_value() const;
-};
-
-class InputSocketRef final : public SocketRef {
- public:
- friend NodeTreeRef;
-
- Span<const OutputSocketRef *> logically_linked_sockets() const;
- Span<const OutputSocketRef *> directly_linked_sockets() const;
-
- bool is_multi_input_socket() const;
-
- private:
- void foreach_logical_origin(FunctionRef<void(const OutputSocketRef &)> origin_fn,
- FunctionRef<void(const SocketRef &)> skipped_fn,
- bool only_follow_first_input_link,
- Vector<const InputSocketRef *> &seen_sockets_stack) const;
-};
-
-class OutputSocketRef final : public SocketRef {
- public:
- friend NodeTreeRef;
-
- Span<const InputSocketRef *> logically_linked_sockets() const;
- Span<const InputSocketRef *> directly_linked_sockets() const;
-
- private:
- void foreach_logical_target(FunctionRef<void(const InputSocketRef &)> target_fn,
- FunctionRef<void(const SocketRef &)> skipped_fn,
- Vector<const OutputSocketRef *> &seen_sockets_stack) const;
-};
-
-class NodeRef : NonCopyable, NonMovable {
- private:
- NodeTreeRef *tree_;
- bNode *bnode_;
- int id_;
- Vector<InputSocketRef *> inputs_;
- Vector<OutputSocketRef *> outputs_;
- Vector<InternalLinkRef *> internal_links_;
- SocketIndexByIdentifierMap *input_index_by_identifier_;
- SocketIndexByIdentifierMap *output_index_by_identifier_;
-
- friend NodeTreeRef;
-
- public:
- const NodeTreeRef &tree() const;
-
- Span<const InputSocketRef *> inputs() const;
- Span<const OutputSocketRef *> outputs() const;
- Span<const InternalLinkRef *> internal_links() const;
- Span<const SocketRef *> sockets(eNodeSocketInOut in_out) const;
-
- const InputSocketRef &input(int index) const;
- const OutputSocketRef &output(int index) const;
-
- const InputSocketRef &input_by_identifier(StringRef identifier) const;
- const OutputSocketRef &output_by_identifier(StringRef identifier) const;
-
- bool any_input_is_directly_linked() const;
- bool any_output_is_directly_linked() const;
- bool any_socket_is_directly_linked(eNodeSocketInOut in_out) const;
-
- bNode *bnode() const;
- bNodeTree *btree() const;
-
- PointerRNA rna() const;
- StringRefNull idname() const;
- StringRefNull name() const;
- StringRefNull label() const;
- StringRefNull label_or_name() const;
- bNodeType *typeinfo() const;
- const NodeDeclaration *declaration() const;
-
- int id() const;
-
- bool is_reroute_node() const;
- bool is_group_node() const;
- bool is_group_input_node() const;
- bool is_group_output_node() const;
- bool is_muted() const;
- bool is_frame() const;
- bool is_undefined() const;
-
- void *storage() const;
- template<typename T> T *storage() const;
-};
-
-class LinkRef : NonCopyable, NonMovable {
- private:
- OutputSocketRef *from_;
- InputSocketRef *to_;
- bNodeLink *blink_;
-
- friend NodeTreeRef;
-
- public:
- const OutputSocketRef &from() const;
- const InputSocketRef &to() const;
-
- bNodeLink *blink() const;
-
- bool is_muted() const;
-};
-
-class InternalLinkRef : NonCopyable, NonMovable {
- private:
- InputSocketRef *from_;
- OutputSocketRef *to_;
- bNodeLink *blink_;
-
- friend NodeTreeRef;
-
- public:
- const InputSocketRef &from() const;
- const OutputSocketRef &to() const;
-
- bNodeLink *blink() const;
-};
-
-class NodeTreeRef : NonCopyable, NonMovable {
- private:
- LinearAllocator<> allocator_;
- bNodeTree *btree_;
- Vector<NodeRef *> nodes_by_id_;
- Vector<SocketRef *> sockets_by_id_;
- Vector<InputSocketRef *> input_sockets_;
- Vector<OutputSocketRef *> output_sockets_;
- Vector<LinkRef *> links_;
- MultiValueMap<const bNodeType *, NodeRef *> nodes_by_type_;
- Vector<std::unique_ptr<SocketIndexByIdentifierMap>> owned_identifier_maps_;
- const NodeRef *group_output_node_ = nullptr;
-
- public:
- NodeTreeRef(bNodeTree *btree);
- ~NodeTreeRef();
-
- Span<const NodeRef *> nodes() const;
- Span<const NodeRef *> nodes_by_type(StringRefNull idname) const;
- Span<const NodeRef *> nodes_by_type(const bNodeType *nodetype) const;
-
- Span<const SocketRef *> sockets() const;
- Span<const InputSocketRef *> input_sockets() const;
- Span<const OutputSocketRef *> output_sockets() const;
-
- Span<const LinkRef *> links() const;
-
- const NodeRef *find_node(const bNode &bnode) const;
-
- /**
- * This is the active group output node if there are multiple.
- */
- const NodeRef *group_output_node() const;
-
- /**
- * \return True when there is a link cycle. Unavailable sockets are ignored.
- */
- bool has_link_cycles() const;
- bool has_undefined_nodes_or_sockets() const;
-
- enum class ToposortDirection {
- LeftToRight,
- RightToLeft,
- };
-
- struct ToposortResult {
- Vector<const NodeRef *> sorted_nodes;
- /**
- * There can't be a correct topological sort of the nodes when there is a cycle. The nodes will
- * still be sorted to some degree. The caller has to decide whether it can handle non-perfect
- * sorts or not.
- */
- bool has_cycle = false;
- };
-
- /**
- * Sort nodes topologically from left to right or right to left.
- * In the future the result if this could be cached on #NodeTreeRef.
- */
- ToposortResult toposort(ToposortDirection direction) const;
-
- bNodeTree *btree() const;
- StringRefNull name() const;
-
- std::string to_dot() const;
-
- private:
- /* Utility functions used during construction. */
- InputSocketRef &find_input_socket(Map<bNode *, NodeRef *> &node_mapping,
- bNode *bnode,
- bNodeSocket *bsocket);
- OutputSocketRef &find_output_socket(Map<bNode *, NodeRef *> &node_mapping,
- bNode *bnode,
- bNodeSocket *bsocket);
-
- void create_linked_socket_caches();
- void create_socket_identifier_maps();
-};
-
-using NodeTreeRefMap = Map<bNodeTree *, std::unique_ptr<const NodeTreeRef>>;
-
-const NodeTreeRef &get_tree_ref_from_map(NodeTreeRefMap &node_tree_refs, bNodeTree &btree);
-
-namespace node_tree_ref_types {
-using nodes::InputSocketRef;
-using nodes::NodeRef;
-using nodes::NodeTreeRef;
-using nodes::NodeTreeRefMap;
-using nodes::OutputSocketRef;
-using nodes::SocketRef;
-} // namespace node_tree_ref_types
-
-/* -------------------------------------------------------------------- */
-/** \name #SocketRef Inline Methods
- * \{ */
-
-inline Span<const SocketRef *> SocketRef::logically_linked_sockets() const
-{
- return logically_linked_sockets_;
-}
-
-inline Span<const SocketRef *> SocketRef::logically_linked_skipped_sockets() const
-{
- return logically_linked_skipped_sockets_;
-}
-
-inline Span<const SocketRef *> SocketRef::directly_linked_sockets() const
-{
- return directly_linked_sockets_;
-}
-
-inline Span<const LinkRef *> SocketRef::directly_linked_links() const
-{
- return directly_linked_links_;
-}
-
-inline bool SocketRef::is_directly_linked() const
-{
- return directly_linked_sockets_.size() > 0;
-}
-
-inline bool SocketRef::is_logically_linked() const
-{
- return logically_linked_sockets_.size() > 0;
-}
-
-inline const NodeRef &SocketRef::node() const
-{
- return *node_;
-}
-
-inline const NodeTreeRef &SocketRef::tree() const
-{
- return node_->tree();
-}
-
-inline int SocketRef::id() const
-{
- return id_;
-}
-
-inline int SocketRef::index() const
-{
- return index_;
-}
-
-inline bool SocketRef::is_input() const
-{
- return is_input_;
-}
-
-inline bool SocketRef::is_output() const
-{
- return !is_input_;
-}
-
-inline const SocketRef &SocketRef::as_base() const
-{
- return *this;
-}
-
-inline const InputSocketRef &SocketRef::as_input() const
-{
- BLI_assert(this->is_input());
- return static_cast<const InputSocketRef &>(*this);
-}
-
-inline const OutputSocketRef &SocketRef::as_output() const
-{
- BLI_assert(this->is_output());
- return static_cast<const OutputSocketRef &>(*this);
-}
-
-inline StringRefNull SocketRef::idname() const
-{
- return bsocket_->idname;
-}
-
-inline StringRefNull SocketRef::name() const
-{
- return bsocket_->name;
-}
-
-inline StringRefNull SocketRef::identifier() const
-{
- return bsocket_->identifier;
-}
-
-inline bNodeSocketType *SocketRef::typeinfo() const
-{
- return bsocket_->typeinfo;
-}
-
-inline bNodeSocket *SocketRef::bsocket() const
-{
- return bsocket_;
-}
-
-inline bNode *SocketRef::bnode() const
-{
- return node_->bnode();
-}
-
-inline bNodeTree *SocketRef::btree() const
-{
- return node_->btree();
-}
-
-inline bool SocketRef::is_available() const
-{
- return (bsocket_->flag & SOCK_UNAVAIL) == 0;
-}
-
-inline bool SocketRef::is_undefined() const
-{
- return bsocket_->typeinfo == &NodeSocketTypeUndefined;
-}
-
-inline void *SocketRef::default_value() const
-{
- return bsocket_->default_value;
-}
-
-template<typename T> inline T *SocketRef::default_value() const
-{
- return (T *)bsocket_->default_value;
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name #InputSocketRef Inline Methods
- * \{ */
-
-inline Span<const OutputSocketRef *> InputSocketRef::logically_linked_sockets() const
-{
- return logically_linked_sockets_.as_span().cast<const OutputSocketRef *>();
-}
-
-inline Span<const OutputSocketRef *> InputSocketRef::directly_linked_sockets() const
-{
- return directly_linked_sockets_.cast<const OutputSocketRef *>();
-}
-
-inline bool InputSocketRef::is_multi_input_socket() const
-{
- return bsocket_->flag & SOCK_MULTI_INPUT;
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name #OutputSocketRef Inline Methods
- * \{ */
-
-inline Span<const InputSocketRef *> OutputSocketRef::logically_linked_sockets() const
-{
- return logically_linked_sockets_.as_span().cast<const InputSocketRef *>();
-}
-
-inline Span<const InputSocketRef *> OutputSocketRef::directly_linked_sockets() const
-{
- return directly_linked_sockets_.cast<const InputSocketRef *>();
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name #NodeRef Inline Methods
- * \{ */
-
-inline const NodeTreeRef &NodeRef::tree() const
-{
- return *tree_;
-}
-
-inline Span<const InputSocketRef *> NodeRef::inputs() const
-{
- return inputs_;
-}
-
-inline Span<const OutputSocketRef *> NodeRef::outputs() const
-{
- return outputs_;
-}
-
-inline Span<const SocketRef *> NodeRef::sockets(const eNodeSocketInOut in_out) const
-{
- return in_out == SOCK_IN ? inputs_.as_span().cast<const SocketRef *>() :
- outputs_.as_span().cast<const SocketRef *>();
-}
-
-inline Span<const InternalLinkRef *> NodeRef::internal_links() const
-{
- return internal_links_;
-}
-
-inline const InputSocketRef &NodeRef::input(int index) const
-{
- return *inputs_[index];
-}
-
-inline const OutputSocketRef &NodeRef::output(int index) const
-{
- return *outputs_[index];
-}
-
-inline const InputSocketRef &NodeRef::input_by_identifier(StringRef identifier) const
-{
- const int index = input_index_by_identifier_->lookup_as(identifier);
- return this->input(index);
-}
-
-inline const OutputSocketRef &NodeRef::output_by_identifier(StringRef identifier) const
-{
- const int index = output_index_by_identifier_->lookup_as(identifier);
- return this->output(index);
-}
-
-inline bNode *NodeRef::bnode() const
-{
- return bnode_;
-}
-
-inline bNodeTree *NodeRef::btree() const
-{
- return tree_->btree();
-}
-
-inline StringRefNull NodeRef::idname() const
-{
- return bnode_->idname;
-}
-
-inline StringRefNull NodeRef::name() const
-{
- return bnode_->name;
-}
-
-inline StringRefNull NodeRef::label() const
-{
- return bnode_->label;
-}
-
-inline StringRefNull NodeRef::label_or_name() const
-{
- const StringRefNull label = this->label();
- if (!label.is_empty()) {
- return label;
- }
- return this->name();
-}
-
-inline bNodeType *NodeRef::typeinfo() const
-{
- return bnode_->typeinfo;
-}
-
-/* Returns a pointer because not all nodes have declarations currently. */
-inline const NodeDeclaration *NodeRef::declaration() const
-{
- nodeDeclarationEnsure(this->tree().btree(), bnode_);
- return bnode_->runtime->declaration;
-}
-
-inline int NodeRef::id() const
-{
- return id_;
-}
-
-inline bool NodeRef::is_reroute_node() const
-{
- return bnode_->type == NODE_REROUTE;
-}
-
-inline bool NodeRef::is_group_node() const
-{
- return bnode_->type == NODE_GROUP || bnode_->type == NODE_CUSTOM_GROUP;
-}
-
-inline bool NodeRef::is_group_input_node() const
-{
- return bnode_->type == NODE_GROUP_INPUT;
-}
-
-inline bool NodeRef::is_group_output_node() const
-{
- return bnode_->type == NODE_GROUP_OUTPUT;
-}
-
-inline bool NodeRef::is_frame() const
-{
- return bnode_->type == NODE_FRAME;
-}
-
-inline bool NodeRef::is_undefined() const
-{
- return bnode_->typeinfo == &NodeTypeUndefined;
-}
-
-inline bool NodeRef::is_muted() const
-{
- return (bnode_->flag & NODE_MUTED) != 0;
-}
-
-inline void *NodeRef::storage() const
-{
- return bnode_->storage;
-}
-
-template<typename T> inline T *NodeRef::storage() const
-{
- return (T *)bnode_->storage;
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name #LinkRef Inline Methods
- * \{ */
-
-inline const OutputSocketRef &LinkRef::from() const
-{
- return *from_;
-}
-
-inline const InputSocketRef &LinkRef::to() const
-{
- return *to_;
-}
-
-inline bNodeLink *LinkRef::blink() const
-{
- return blink_;
-}
-
-inline bool LinkRef::is_muted() const
-{
- return blink_->flag & NODE_LINK_MUTED;
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name #InternalLinkRef Inline Methods
- * \{ */
-
-inline const InputSocketRef &InternalLinkRef::from() const
-{
- return *from_;
-}
-
-inline const OutputSocketRef &InternalLinkRef::to() const
-{
- return *to_;
-}
-
-inline bNodeLink *InternalLinkRef::blink() const
-{
- return blink_;
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name #NodeTreeRef Inline Methods
- * \{ */
-
-inline Span<const NodeRef *> NodeTreeRef::nodes() const
-{
- return nodes_by_id_;
-}
-
-inline Span<const NodeRef *> NodeTreeRef::nodes_by_type(StringRefNull idname) const
-{
- const bNodeType *nodetype = nodeTypeFind(idname.c_str());
- return this->nodes_by_type(nodetype);
-}
-
-inline Span<const NodeRef *> NodeTreeRef::nodes_by_type(const bNodeType *nodetype) const
-{
- return nodes_by_type_.lookup(nodetype);
-}
-
-inline Span<const SocketRef *> NodeTreeRef::sockets() const
-{
- return sockets_by_id_;
-}
-
-inline Span<const InputSocketRef *> NodeTreeRef::input_sockets() const
-{
- return input_sockets_;
-}
-
-inline Span<const OutputSocketRef *> NodeTreeRef::output_sockets() const
-{
- return output_sockets_;
-}
-
-inline Span<const LinkRef *> NodeTreeRef::links() const
-{
- return links_;
-}
-
-inline const NodeRef *NodeTreeRef::group_output_node() const
-{
- return group_output_node_;
-}
-
-inline bNodeTree *NodeTreeRef::btree() const
-{
- return btree_;
-}
-
-inline StringRefNull NodeTreeRef::name() const
-{
- return btree_->id.name + 2;
-}
-
-/** \} */
-
-} // namespace blender::nodes
diff --git a/source/blender/nodes/NOD_shader.h b/source/blender/nodes/NOD_shader.h
index 1d1310360b8..8fe77bffaad 100644
--- a/source/blender/nodes/NOD_shader.h
+++ b/source/blender/nodes/NOD_shader.h
@@ -26,6 +26,7 @@ void register_node_type_sh_camera(void);
void register_node_type_sh_value(void);
void register_node_type_sh_rgb(void);
void register_node_type_sh_mix_rgb(void);
+void register_node_type_sh_mix(void);
void register_node_type_sh_valtorgb(void);
void register_node_type_sh_rgbtobw(void);
void register_node_type_sh_shadertorgb(void);
diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h
index 786ce88152e..8f2a4adcf9a 100644
--- a/source/blender/nodes/NOD_static_types.h
+++ b/source/blender/nodes/NOD_static_types.h
@@ -25,7 +25,7 @@ DefNode(Node, NODE_REROUTE, 0, "REROUT
DefNode(ShaderNode, SH_NODE_RGB, 0, "RGB", RGB, "RGB", "A color picker")
DefNode(ShaderNode, SH_NODE_VALUE, 0, "VALUE", Value, "Value", "Used to Input numerical values to other nodes in the tree")
-DefNode(ShaderNode, SH_NODE_MIX_RGB, def_mix_rgb, "MIX_RGB", MixRGB, "MixRGB", "Mix two input colors")
+DefNode(ShaderNode, SH_NODE_MIX_RGB_LEGACY, def_mix_rgb, "MIX_RGB", MixRGB, "MixRGB", "Mix two input colors")
DefNode(ShaderNode, SH_NODE_VALTORGB, def_colorramp, "VALTORGB", ValToRGB, "ColorRamp", "Map values to colors with the use of a gradient")
DefNode(ShaderNode, SH_NODE_RGBTOBW, 0, "RGBTOBW", RGBToBW, "RGB to BW", "Convert a color's luminance to a grayscale value")
DefNode(ShaderNode, SH_NODE_SHADERTORGB, 0, "SHADERTORGB", ShaderToRGB, "Shader to RGB", "Convert rendering effect (such as light and shadow) to color. Typically used for non-photorealistic rendering, to apply additional effects on the output of BSDFs.\nNote: only supported for Eevee")
@@ -122,6 +122,7 @@ DefNode(ShaderNode, SH_NODE_OUTPUT_AOV, def_sh_output_aov, "OUT
DefNode(ShaderNode, SH_NODE_CURVE_FLOAT, def_float_curve, "CURVE_FLOAT", FloatCurve, "Float Curve", "Map an input float to a curve and outputs a float value")
DefNode(ShaderNode, SH_NODE_COMBINE_COLOR, def_sh_combsep_color, "COMBINE_COLOR", CombineColor, "Combine Color", "Create a color from individual components using multiple models")
DefNode(ShaderNode, SH_NODE_SEPARATE_COLOR, def_sh_combsep_color, "SEPARATE_COLOR", SeparateColor, "Separate Color", "Split a color into its individual components using multiple models")
+DefNode(ShaderNode, SH_NODE_MIX, def_sh_mix, "MIX", Mix, "Mix", "Mix values by a factor")
DefNode(CompositorNode, CMP_NODE_VIEWER, def_cmp_viewer, "VIEWER", Viewer, "Viewer", "" )
DefNode(CompositorNode, CMP_NODE_RGB, 0, "RGB", RGB, "RGB", "" )
@@ -304,6 +305,7 @@ DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, "
DefNode(GeometryNode, GEO_NODE_CURVE_TO_POINTS, def_geo_curve_to_points, "CURVE_TO_POINTS", CurveToPoints, "Curve to Points", "Generate a point cloud by sampling positions along curves")
DefNode(GeometryNode, GEO_NODE_DEFORM_CURVES_ON_SURFACE, 0, "DEFORM_CURVES_ON_SURFACE", DeformCurvesOnSurface, "Deform Curves on Surface", "Translate and rotate curves based on changes between the object's original and evaluated surface mesh")
DefNode(GeometryNode, GEO_NODE_DELETE_GEOMETRY, def_geo_delete_geometry, "DELETE_GEOMETRY", DeleteGeometry, "Delete Geometry", "Remove selected elements of a geometry")
+DefNode(GeometryNode, GEO_NODE_DISTRIBUTE_POINTS_IN_VOLUME, def_geo_distribute_points_in_volume, "DISTRIBUTE_POINTS_IN_VOLUME", DistributePointsInVolume, "Distribute Points In Volume", "Generate points inside a volume")
DefNode(GeometryNode, GEO_NODE_DISTRIBUTE_POINTS_ON_FACES, def_geo_distribute_points_on_faces, "DISTRIBUTE_POINTS_ON_FACES", DistributePointsOnFaces, "Distribute Points on Faces", "Generate points spread out on the surface of a mesh")
DefNode(GeometryNode, GEO_NODE_DUAL_MESH, 0, "DUAL_MESH", DualMesh, "Dual Mesh", "Convert Faces into vertices and vertices into faces")
DefNode(GeometryNode, GEO_NODE_DUPLICATE_ELEMENTS, def_geo_duplicate_elements, "DUPLICATE_ELEMENTS", DuplicateElements, "Duplicate Elements", "Generate an arbitrary number copies of each selected input element")
@@ -328,7 +330,7 @@ DefNode(GeometryNode, GEO_NODE_INPUT_MESH_EDGE_ANGLE, 0, "MESH_EDGE_ANGLE", Inpu
DefNode(GeometryNode, GEO_NODE_INPUT_MESH_EDGE_NEIGHBORS, 0, "MESH_EDGE_NEIGHBORS",InputMeshEdgeNeighbors, "Edge Neighbors", "Retrieve the number of faces that use each edge as one of their sides")
DefNode(GeometryNode, GEO_NODE_INPUT_MESH_EDGE_VERTICES, 0, "MESH_EDGE_VERTICES", InputMeshEdgeVertices, "Edge Vertices", "Retrieve topology information relating to each edge of a mesh")
DefNode(GeometryNode, GEO_NODE_INPUT_MESH_FACE_AREA, 0, "MESH_FACE_AREA", InputMeshFaceArea, "Face Area", "Calculate the surface area of a mesh's faces")
-DefNode(GeometryNode, GEO_NODE_INPUT_MESH_FACE_IS_PLANAR, 0, "MESH_FACE_IS_PLANAR",InputMeshFaceIsPlanar, "Face is Planar", "Retrieve whether all triangles in a face are on the same plane, i.e. whether have the same normal")
+DefNode(GeometryNode, GEO_NODE_INPUT_MESH_FACE_IS_PLANAR, 0, "MESH_FACE_IS_PLANAR",InputMeshFaceIsPlanar, "Is Face Planar", "Retrieve whether all triangles in a face are on the same plane, i.e. whether have the same normal")
DefNode(GeometryNode, GEO_NODE_INPUT_MESH_FACE_NEIGHBORS, 0, "MESH_FACE_NEIGHBORS",InputMeshFaceNeighbors, "Face Neighbors", "Retrieve topology information relating to each face of a mesh")
DefNode(GeometryNode, GEO_NODE_INPUT_MESH_ISLAND, 0, "MESH_ISLAND", InputMeshIsland, "Mesh Island", "Retrieve information about separate connected regions in a mesh")
DefNode(GeometryNode, GEO_NODE_INPUT_MESH_VERTEX_NEIGHBORS, 0, "MESH_VERTEX_NEIGHBORS", InputMeshVertexNeighbors, "Vertex Neighbors", "Retrieve topology information relating to each vertex of a mesh")
@@ -351,6 +353,7 @@ DefNode(GeometryNode, GEO_NODE_JOIN_GEOMETRY, 0, "JOIN_GEOMETRY", JoinGeometry,
DefNode(GeometryNode, GEO_NODE_MATERIAL_SELECTION, 0, "MATERIAL_SELECTION", MaterialSelection, "Material Selection", "Provide a selection of faces that use the specified material")
DefNode(GeometryNode, GEO_NODE_MERGE_BY_DISTANCE, def_geo_merge_by_distance,"MERGE_BY_DISTANCE", MergeByDistance, "Merge by Distance", "Merge vertices or points within a given distance")
DefNode(GeometryNode, GEO_NODE_MESH_BOOLEAN, def_geo_boolean, "MESH_BOOLEAN", MeshBoolean, "Mesh Boolean", "Cut, subtract, or join multiple mesh inputs")
+DefNode(GeometryNode, GEO_NODE_MESH_FACE_SET_BOUNDARIES, 0, "MESH_FACE_SET_BOUNDARIES", MeshFaceSetBoundaries, "Face Set Boundaries", "Find edges on the boundaries between face sets")
DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CIRCLE, def_geo_mesh_circle, "MESH_PRIMITIVE_CIRCLE", MeshCircle, "Mesh Circle", "Generate a circular ring of edges")
DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CONE, def_geo_mesh_cone, "MESH_PRIMITIVE_CONE",MeshCone, "Cone", "Generate a cone mesh")
DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CUBE, 0, "MESH_PRIMITIVE_CUBE",MeshCube, "Cube", "Generate a cuboid mesh with variable side lengths and subdivisions")
@@ -361,7 +364,7 @@ DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_LINE, def_geo_mesh_line, "MESH_PRI
DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, 0, "MESH_PRIMITIVE_UV_SPHERE", MeshUVSphere, "UV Sphere", "Generate a spherical mesh with quads, except for triangles at the top and bottom")
DefNode(GeometryNode, GEO_NODE_MESH_TO_CURVE, 0, "MESH_TO_CURVE", MeshToCurve, "Mesh to Curve", "Generate a curve from a mesh")
DefNode(GeometryNode, GEO_NODE_MESH_TO_POINTS, def_geo_mesh_to_points, "MESH_TO_POINTS", MeshToPoints, "Mesh to Points", "Generate a point cloud from a mesh's vertices")
-DefNode(GeometryNode, GEO_NODE_MESH_TO_VOLUME, def_geo_mesh_to_volume, "MESH_TO_VOLUME", MeshToVolume, "Mesh To Volume", "Create a fog volume with the shape of the input mesh's surface")
+DefNode(GeometryNode, GEO_NODE_MESH_TO_VOLUME, def_geo_mesh_to_volume, "MESH_TO_VOLUME", MeshToVolume, "Mesh to Volume", "Create a fog volume with the shape of the input mesh's surface")
DefNode(GeometryNode, GEO_NODE_OBJECT_INFO, def_geo_object_info, "OBJECT_INFO", ObjectInfo, "Object Info", "Retrieve information from an object")
DefNode(GeometryNode, GEO_NODE_POINTS_TO_VERTICES, 0, "POINTS_TO_VERTICES", PointsToVertices, "Points to Vertices", "Generate a mesh vertex for each point cloud point")
DefNode(GeometryNode, GEO_NODE_POINTS_TO_VOLUME, def_geo_points_to_volume, "POINTS_TO_VOLUME", PointsToVolume, "Points to Volume", "Generate a fog volume sphere around every point")
@@ -375,10 +378,14 @@ DefNode(GeometryNode, GEO_NODE_RESAMPLE_CURVE, def_geo_curve_resample, "RESAMPLE
DefNode(GeometryNode, GEO_NODE_REVERSE_CURVE, 0, "REVERSE_CURVE", ReverseCurve, "Reverse Curve", "Swap the start and end of splines")
DefNode(GeometryNode, GEO_NODE_ROTATE_INSTANCES, 0, "ROTATE_INSTANCES", RotateInstances, "Rotate Instances", "Rotate geometry instances in local or global space")
DefNode(GeometryNode, GEO_NODE_SAMPLE_CURVE, def_geo_curve_sample, "SAMPLE_CURVE", SampleCurve, "Sample Curve", "Retrieve data from a point on a curve at a certain distance from its start")
+DefNode(GeometryNode, GEO_NODE_SAMPLE_INDEX, def_geo_sample_index, "SAMPLE_INDEX", SampleIndex, "Sample Index", "Retrieve values from specific geometry elements")
+DefNode(GeometryNode, GEO_NODE_SAMPLE_NEAREST_SURFACE, def_geo_sample_nearest_surface, "sample_nearest_surface", SampleNearestSurface, "Sample Nearest Surface", "Calculate the interpolated value of a mesh attribute on the closest point of its surface")
+DefNode(GeometryNode, GEO_NODE_SAMPLE_NEAREST, def_geo_sample_nearest, "SAMPLE_NEAREST", SampleNearest, "Sample Nearest", "Find the element of a geometry closest to a position")
DefNode(GeometryNode, GEO_NODE_SCALE_ELEMENTS, def_geo_scale_elements, "SCALE_ELEMENTS", ScaleElements, "Scale Elements", "Scale groups of connected edges and faces")
DefNode(GeometryNode, GEO_NODE_SCALE_INSTANCES, 0, "SCALE_INSTANCES", ScaleInstances, "Scale Instances", "Scale geometry instances in local or global space")
DefNode(GeometryNode, GEO_NODE_SEPARATE_COMPONENTS, 0, "SEPARATE_COMPONENTS",SeparateComponents, "Separate Components","Split a geometry into a separate output for each type of data in the geometry")
DefNode(GeometryNode, GEO_NODE_SEPARATE_GEOMETRY, def_geo_separate_geometry,"SEPARATE_GEOMETRY", SeparateGeometry, "Separate Geometry", "Split a geometry into two geometry outputs based on a selection")
+DefNode(GeometryNode, GEO_NODE_SELF_OBJECT, 0, "SELF_OBJECT", SelfObject, "Self Object", "Retrieve the object that contains the geometry nodes modifier currently being executed")
DefNode(GeometryNode, GEO_NODE_SET_CURVE_HANDLES, def_geo_curve_set_handle_positions, "SET_CURVE_HANDLES", SetCurveHandlePositions, "Set Handle Positions", "Set the positions for the handles of Bézier curves")
DefNode(GeometryNode, GEO_NODE_SET_CURVE_RADIUS, 0, "SET_CURVE_RADIUS", SetCurveRadius, "Set Curve Radius", "Set the radius of the curve at each control point")
DefNode(GeometryNode, GEO_NODE_SET_CURVE_TILT, 0, "SET_CURVE_TILT", SetCurveTilt, "Set Curve Tilt", "Set the tilt angle at each curve control point")
@@ -398,7 +405,6 @@ DefNode(GeometryNode, GEO_NODE_SUBDIVIDE_CURVE, 0, "SUBDIVIDE_CURVE", SubdivideC
DefNode(GeometryNode, GEO_NODE_SUBDIVIDE_MESH, 0, "SUBDIVIDE_MESH", SubdivideMesh, "Subdivide Mesh", "Divide mesh faces into smaller ones without changing the shape or volume, using linear interpolation to place the new vertices")
DefNode(GeometryNode, GEO_NODE_SUBDIVISION_SURFACE, def_geo_subdivision_surface, "SUBDIVISION_SURFACE",SubdivisionSurface, "Subdivision Surface","Divide mesh faces to form a smooth surface, using the Catmull-Clark subdivision method")
DefNode(GeometryNode, GEO_NODE_SWITCH, def_geo_switch, "SWITCH", Switch, "Switch", "Switch between two inputs")
-DefNode(GeometryNode, GEO_NODE_TRANSFER_ATTRIBUTE, def_geo_transfer_attribute, "ATTRIBUTE_TRANSFER", AttributeTransfer, "Transfer Attribute", "Retrieve values from a source geometry and provides them as a field by interpolating them with the context geometry")
DefNode(GeometryNode, GEO_NODE_TRANSFORM, 0, "TRANSFORM", Transform, "Transform", "Translate, rotate or scale the geometry")
DefNode(GeometryNode, GEO_NODE_TRANSLATE_INSTANCES, 0, "TRANSLATE_INSTANCES",TranslateInstances, "Translate Instances","Move top-level geometry instances in local or global space")
DefNode(GeometryNode, GEO_NODE_TRIANGULATE, def_geo_triangulate, "TRIANGULATE", Triangulate, "Triangulate", "Convert all faces in a mesh to triangular faces")
diff --git a/source/blender/nodes/composite/nodes/node_composite_alpha_over.cc b/source/blender/nodes/composite/nodes/node_composite_alpha_over.cc
index 64c59eb24e3..12f81da3d1c 100644
--- a/source/blender/nodes/composite/nodes/node_composite_alpha_over.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_alpha_over.cc
@@ -18,6 +18,8 @@
namespace blender::nodes::node_composite_alpha_over_cc {
+NODE_STORAGE_FUNCS(NodeTwoFloats)
+
static void cmp_node_alphaover_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Float>(N_("Fac"))
@@ -86,7 +88,7 @@ class AlphaOverShaderNode : public ShaderNode {
float get_premultiply_factor()
{
- return ((NodeTwoFloats *)bnode().storage)->x;
+ return node_storage(bnode()).x;
}
};
diff --git a/source/blender/nodes/composite/nodes/node_composite_bilateralblur.cc b/source/blender/nodes/composite/nodes/node_composite_bilateralblur.cc
index 66a321eb088..ac9a6c89aa4 100644
--- a/source/blender/nodes/composite/nodes/node_composite_bilateralblur.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_bilateralblur.cc
@@ -5,10 +5,15 @@
* \ingroup cmpnodes
*/
+#include "BLI_math_base.hh"
+
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_shader.h"
+
#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
#include "node_composite_util.hh"
@@ -16,10 +21,16 @@
namespace blender::nodes::node_composite_bilateralblur_cc {
+NODE_STORAGE_FUNCS(NodeBilateralBlurData)
+
static void cmp_node_bilateralblur_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Color>(N_("Determinator")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Color>(N_("Determinator"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(1);
b.add_output<decl::Color>(N_("Image"));
}
@@ -52,7 +63,45 @@ class BilateralBlurOperation : public NodeOperation {
void execute() override
{
- get_input("Image").pass_through(get_result("Image"));
+ const Result &input_image = get_input("Image");
+ /* Single value inputs can't be blurred and are returned as is. */
+ if (input_image.is_single_value()) {
+ get_input("Image").pass_through(get_result("Image"));
+ return;
+ }
+
+ GPUShader *shader = shader_manager().get("compositor_bilateral_blur");
+ GPU_shader_bind(shader);
+
+ GPU_shader_uniform_1i(shader, "radius", get_blur_radius());
+ GPU_shader_uniform_1f(shader, "threshold", get_threshold());
+
+ input_image.bind_as_texture(shader, "input_tx");
+
+ const Result &determinator_image = get_input("Determinator");
+ determinator_image.bind_as_texture(shader, "determinator_tx");
+
+ const Domain domain = compute_domain();
+ Result &output_image = get_result("Image");
+ output_image.allocate_texture(domain);
+ output_image.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ GPU_shader_unbind();
+ output_image.unbind_as_image();
+ input_image.unbind_as_texture();
+ determinator_image.unbind_as_texture();
+ }
+
+ int get_blur_radius()
+ {
+ return math::ceil(node_storage(bnode()).iter + node_storage(bnode()).sigma_space);
+ }
+
+ float get_threshold()
+ {
+ return node_storage(bnode()).sigma_color;
}
};
diff --git a/source/blender/nodes/composite/nodes/node_composite_blur.cc b/source/blender/nodes/composite/nodes/node_composite_blur.cc
index cb1d93fe10b..630f18361e3 100644
--- a/source/blender/nodes/composite/nodes/node_composite_blur.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_blur.cc
@@ -5,12 +5,27 @@
* \ingroup cmpnodes
*/
+#include <cstdint>
+
+#include "BLI_array.hh"
+#include "BLI_assert.h"
+#include "BLI_index_range.hh"
+#include "BLI_math_base.hh"
+#include "BLI_math_vec_types.hh"
+#include "BLI_math_vector.hh"
+
#include "RNA_access.h"
#include "UI_interface.h"
#include "UI_resources.h"
+#include "RE_pipeline.h"
+
+#include "GPU_state.h"
+#include "GPU_texture.h"
+
#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
#include "node_composite_util.hh"
@@ -18,6 +33,8 @@
namespace blender::nodes::node_composite_blur_cc {
+NODE_STORAGE_FUNCS(NodeBlurData)
+
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});
@@ -75,13 +92,395 @@ static void node_composit_buts_blur(uiLayout *layout, bContext *UNUSED(C), Point
using namespace blender::realtime_compositor;
+/* A helper class that computes and caches a 1D GPU texture containing the weights of the separable
+ * filter of the given type and radius. The filter is assumed to be symmetric, because the filter
+ * functions are all even functions. Consequently, only the positive half of the filter is computed
+ * and the shader takes that into consideration. */
+class SymmetricSeparableBlurWeights {
+ private:
+ float radius_ = 1.0f;
+ int type_ = R_FILTER_GAUSS;
+ GPUTexture *texture_ = nullptr;
+
+ public:
+ ~SymmetricSeparableBlurWeights()
+ {
+ if (texture_) {
+ GPU_texture_free(texture_);
+ }
+ }
+
+ /* Check if a texture containing the weights was already computed for the given filter type and
+ * radius. If such texture exists, do nothing, otherwise, free the already computed texture and
+ * recompute it with the given filter type and radius. */
+ void update(float radius, int type)
+ {
+ if (texture_ && type == type_ && radius == radius_) {
+ return;
+ }
+
+ if (texture_) {
+ GPU_texture_free(texture_);
+ }
+
+ /* The size of filter is double the radius plus 1, but since the filter is symmetric, we only
+ * compute half of it and no doubling happens. We add 1 to make sure the filter size is always
+ * odd and there is a center weight. */
+ const int size = math::ceil(radius) + 1;
+ Array<float> weights(size);
+
+ float sum = 0.0f;
+
+ /* First, compute the center weight. */
+ const float center_weight = RE_filter_value(type, 0.0f);
+ weights[0] = center_weight;
+ sum += center_weight;
+
+ /* Second, compute the other weights in the positive direction, making sure to add double the
+ * weight to the sum of weights because the filter is symmetric and we only loop over half of
+ * it. Skip the center weight already computed by dropping the front index. */
+ const float scale = radius > 0.0f ? 1.0f / radius : 0.0f;
+ for (const int i : weights.index_range().drop_front(1)) {
+ const float weight = RE_filter_value(type, i * scale);
+ weights[i] = weight;
+ sum += weight * 2.0f;
+ }
+
+ /* Finally, normalize the weights. */
+ for (const int i : weights.index_range()) {
+ weights[i] /= sum;
+ }
+
+ texture_ = GPU_texture_create_1d("Weights", size, 1, GPU_R16F, weights.data());
+
+ type_ = type;
+ radius_ = radius;
+ }
+
+ void bind_as_texture(GPUShader *shader, const char *texture_name)
+ {
+ const int texture_image_unit = GPU_shader_get_texture_binding(shader, texture_name);
+ GPU_texture_bind(texture_, texture_image_unit);
+ }
+
+ void unbind_as_texture()
+ {
+ GPU_texture_unbind(texture_);
+ }
+};
+
+/* A helper class that computes and caches a 2D GPU texture containing the weights of the filter of
+ * the given type and radius. The filter is assumed to be symmetric, because the filter functions
+ * are evaluated on the normalized distance to the center. Consequently, only the upper right
+ * quadrant are computed and the shader takes that into consideration. */
+class SymmetricBlurWeights {
+ private:
+ int type_ = R_FILTER_GAUSS;
+ float2 radius_ = float2(1.0f);
+ GPUTexture *texture_ = nullptr;
+
+ public:
+ ~SymmetricBlurWeights()
+ {
+ if (texture_) {
+ GPU_texture_free(texture_);
+ }
+ }
+
+ /* Check if a texture containing the weights was already computed for the given filter type and
+ * radius. If such texture exists, do nothing, otherwise, free the already computed texture and
+ * recompute it with the given filter type and radius. */
+ void update(float2 radius, int type)
+ {
+ if (texture_ && type == type_ && radius == radius_) {
+ return;
+ }
+
+ if (texture_) {
+ GPU_texture_free(texture_);
+ }
+
+ /* The full size of filter is double the radius plus 1, but since the filter is symmetric, we
+ * only compute a single quadrant of it and so no doubling happens. We add 1 to make sure the
+ * filter size is always odd and there is a center weight. */
+ const float2 scale = math::safe_divide(float2(1.0f), radius);
+ const int2 size = int2(math::ceil(radius)) + int2(1);
+ Array<float> weights(size.x * size.y);
+
+ float sum = 0.0f;
+
+ /* First, compute the center weight. */
+ const float center_weight = RE_filter_value(type, 0.0f);
+ weights[0] = center_weight;
+ sum += center_weight;
+
+ /* Then, compute the weights along the positive x axis, making sure to add double the weight to
+ * the sum of weights because the filter is symmetric and we only loop over the positive half
+ * of the x axis. Skip the center weight already computed by dropping the front index. */
+ for (const int x : IndexRange(size.x).drop_front(1)) {
+ const float weight = RE_filter_value(type, x * scale.x);
+ weights[x] = weight;
+ sum += weight * 2.0f;
+ }
+
+ /* Then, compute the weights along the positive y axis, making sure to add double the weight to
+ * the sum of weights because the filter is symmetric and we only loop over the positive half
+ * of the y axis. Skip the center weight already computed by dropping the front index. */
+ for (const int y : IndexRange(size.y).drop_front(1)) {
+ const float weight = RE_filter_value(type, y * scale.y);
+ weights[size.x * y] = weight;
+ sum += weight * 2.0f;
+ }
+
+ /* Then, compute the other weights in the upper right quadrant, making sure to add quadruple
+ * the weight to the sum of weights because the filter is symmetric and we only loop over one
+ * quadrant of it. Skip the weights along the y and x axis already computed by dropping the
+ * front index. */
+ for (const int y : IndexRange(size.y).drop_front(1)) {
+ for (const int x : IndexRange(size.x).drop_front(1)) {
+ const float weight = RE_filter_value(type, math::length(float2(x, y) * scale));
+ weights[size.x * y + x] = weight;
+ sum += weight * 4.0f;
+ }
+ }
+
+ /* Finally, normalize the weights. */
+ for (const int y : IndexRange(size.y)) {
+ for (const int x : IndexRange(size.x)) {
+ weights[size.x * y + x] /= sum;
+ }
+ }
+
+ texture_ = GPU_texture_create_2d("Weights", size.x, size.y, 1, GPU_R16F, weights.data());
+
+ type_ = type;
+ radius_ = radius;
+ }
+
+ void bind_as_texture(GPUShader *shader, const char *texture_name)
+ {
+ const int texture_image_unit = GPU_shader_get_texture_binding(shader, texture_name);
+ GPU_texture_bind(texture_, texture_image_unit);
+ }
+
+ void unbind_as_texture()
+ {
+ GPU_texture_unbind(texture_);
+ }
+};
+
class BlurOperation : public NodeOperation {
+ private:
+ /* Cached symmetric blur weights. */
+ SymmetricBlurWeights blur_weights_;
+ /* Cached symmetric blur weights for the separable horizontal pass. */
+ SymmetricSeparableBlurWeights blur_horizontal_weights_;
+ /* Cached symmetric blur weights for the separable vertical pass. */
+ SymmetricSeparableBlurWeights blur_vertical_weights_;
+
public:
using NodeOperation::NodeOperation;
void execute() override
{
- get_input("Image").pass_through(get_result("Image"));
+ if (is_identity()) {
+ get_input("Image").pass_through(get_result("Image"));
+ return;
+ }
+
+ if (use_separable_filter()) {
+ GPUTexture *horizontal_pass_result = execute_separable_blur_horizontal_pass();
+ execute_separable_blur_vertical_pass(horizontal_pass_result);
+ }
+ else {
+ execute_blur();
+ }
+ }
+
+ void execute_blur()
+ {
+ GPUShader *shader = shader_manager().get("compositor_symmetric_blur");
+ GPU_shader_bind(shader);
+
+ GPU_shader_uniform_1b(shader, "extend_bounds", get_extend_bounds());
+ GPU_shader_uniform_1b(shader, "gamma_correct", node_storage(bnode()).gamma);
+
+ const Result &input_image = get_input("Image");
+ input_image.bind_as_texture(shader, "input_tx");
+
+ blur_weights_.update(compute_blur_radius(), node_storage(bnode()).filtertype);
+ blur_weights_.bind_as_texture(shader, "weights_tx");
+
+ Domain domain = compute_domain();
+ if (get_extend_bounds()) {
+ /* Add a radius amount of pixels in both sides of the image, hence the multiply by 2. */
+ domain.size += int2(math::ceil(compute_blur_radius())) * 2;
+ }
+
+ Result &output_image = get_result("Image");
+ output_image.allocate_texture(domain);
+ output_image.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ GPU_shader_unbind();
+ output_image.unbind_as_image();
+ input_image.unbind_as_texture();
+ blur_weights_.unbind_as_texture();
+ }
+
+ GPUTexture *execute_separable_blur_horizontal_pass()
+ {
+ GPUShader *shader = shader_manager().get("compositor_symmetric_separable_blur");
+ GPU_shader_bind(shader);
+
+ GPU_shader_uniform_1b(shader, "extend_bounds", get_extend_bounds());
+ GPU_shader_uniform_1b(shader, "gamma_correct_input", node_storage(bnode()).gamma);
+ GPU_shader_uniform_1b(shader, "gamma_uncorrect_output", false);
+
+ const Result &input_image = get_input("Image");
+ input_image.bind_as_texture(shader, "input_tx");
+
+ blur_horizontal_weights_.update(compute_blur_radius().x, node_storage(bnode()).filtertype);
+ blur_horizontal_weights_.bind_as_texture(shader, "weights_tx");
+
+ Domain domain = compute_domain();
+ if (get_extend_bounds()) {
+ domain.size.x += static_cast<int>(math::ceil(compute_blur_radius().x)) * 2;
+ }
+
+ /* We allocate an output image of a transposed size, that is, with a height equivalent to the
+ * width of the input and vice versa. This is done as a performance optimization. The shader
+ * will blur the image horizontally and write it to the intermediate output transposed. Then
+ * the vertical pass will execute the same horizontal blur shader, but since its input is
+ * transposed, it will effectively do a vertical blur and write to the output transposed,
+ * effectively undoing the transposition in the horizontal pass. This is done to improve
+ * spatial cache locality in the shader and to avoid having two separate shaders for each blur
+ * pass. */
+ const int2 transposed_domain = int2(domain.size.y, domain.size.x);
+
+ GPUTexture *horizontal_pass_result = texture_pool().acquire_color(transposed_domain);
+ const int image_unit = GPU_shader_get_texture_binding(shader, "output_img");
+ GPU_texture_image_bind(horizontal_pass_result, image_unit);
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ GPU_shader_unbind();
+ input_image.unbind_as_texture();
+ blur_horizontal_weights_.unbind_as_texture();
+ GPU_texture_image_unbind(horizontal_pass_result);
+
+ return horizontal_pass_result;
+ }
+
+ void execute_separable_blur_vertical_pass(GPUTexture *horizontal_pass_result)
+ {
+ GPUShader *shader = shader_manager().get("compositor_symmetric_separable_blur");
+ GPU_shader_bind(shader);
+
+ GPU_shader_uniform_1b(shader, "extend_bounds", get_extend_bounds());
+ GPU_shader_uniform_1b(shader, "gamma_correct_input", false);
+ GPU_shader_uniform_1b(shader, "gamma_uncorrect_output", node_storage(bnode()).gamma);
+
+ GPU_memory_barrier(GPU_BARRIER_TEXTURE_FETCH);
+ const int texture_image_unit = GPU_shader_get_texture_binding(shader, "input_tx");
+ GPU_texture_bind(horizontal_pass_result, texture_image_unit);
+
+ blur_vertical_weights_.update(compute_blur_radius().y, node_storage(bnode()).filtertype);
+ blur_vertical_weights_.bind_as_texture(shader, "weights_tx");
+
+ Domain domain = compute_domain();
+ if (get_extend_bounds()) {
+ /* Add a radius amount of pixels in both sides of the image, hence the multiply by 2. */
+ domain.size += int2(math::ceil(compute_blur_radius())) * 2;
+ }
+
+ Result &output_image = get_result("Image");
+ output_image.allocate_texture(domain);
+ output_image.bind_as_image(shader, "output_img");
+
+ /* Notice that the domain is transposed, see the note on the horizontal pass method for more
+ * information on the reasoning behind this. */
+ compute_dispatch_threads_at_least(shader, int2(domain.size.y, domain.size.x));
+
+ GPU_shader_unbind();
+ output_image.unbind_as_image();
+ blur_vertical_weights_.unbind_as_texture();
+ GPU_texture_unbind(horizontal_pass_result);
+ }
+
+ float2 compute_blur_radius()
+ {
+ const float size = math::clamp(get_input("Size").get_float_value_default(1.0f), 0.0f, 1.0f);
+
+ if (!node_storage(bnode()).relative) {
+ return float2(node_storage(bnode()).sizex, node_storage(bnode()).sizey) * size;
+ }
+
+ int2 image_size = get_input("Image").domain().size;
+ switch (node_storage(bnode()).aspect) {
+ case CMP_NODE_BLUR_ASPECT_Y:
+ image_size.y = image_size.x;
+ break;
+ case CMP_NODE_BLUR_ASPECT_X:
+ image_size.x = image_size.y;
+ break;
+ default:
+ BLI_assert(node_storage(bnode()).aspect == CMP_NODE_BLUR_ASPECT_NONE);
+ break;
+ }
+
+ return float2(image_size) * get_size_factor() * size;
+ }
+
+ /* Returns true if the operation does nothing and the input can be passed through. */
+ bool is_identity()
+ {
+ const Result &input = get_input("Image");
+ /* Single value inputs can't be blurred and are returned as is. */
+ if (input.is_single_value()) {
+ return true;
+ }
+
+ /* Zero blur radius. The operation does nothing and the input can be passed through. */
+ if (compute_blur_radius() == float2(0.0)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /* The blur node can operate with different filter types, evaluated on the normalized distance to
+ * the center of the filter. Some of those filters are separable and can be computed as such. If
+ * the bokeh member is disabled in the node, then the filter is always computed as separable even
+ * if it is not in fact separable, in which case, the used filter is a cheaper approximation to
+ * the actual filter. If the bokeh member is enabled, then the filter is computed as separable if
+ * it is in fact separable and as a normal 2D filter otherwise. */
+ bool use_separable_filter()
+ {
+ if (!node_storage(bnode()).bokeh) {
+ return true;
+ }
+
+ /* Both Box and Gaussian filters are separable. The rest is not. */
+ switch (node_storage(bnode()).filtertype) {
+ case R_FILTER_BOX:
+ case R_FILTER_GAUSS:
+ case R_FILTER_FAST_GAUSS:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ float2 get_size_factor()
+ {
+ return float2(node_storage(bnode()).percentx, node_storage(bnode()).percenty) / 100.0f;
+ }
+
+ bool get_extend_bounds()
+ {
+ return bnode().custom1 & CMP_NODEFLAG_BLUR_EXTEND_BOUNDS;
}
};
diff --git a/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc b/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc
index 538f00af12d..9c0617ee8c3 100644
--- a/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc
@@ -5,10 +5,16 @@
* \ingroup cmpnodes
*/
+#include "BLI_math_base.hh"
+#include "BLI_math_vec_types.hh"
+
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_texture.h"
+
#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
#include "node_composite_util.hh"
@@ -18,10 +24,22 @@ 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_input<decl::Color>(N_("Image"))
+ .default_value({0.8f, 0.8f, 0.8f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Color>(N_("Bokeh"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_skip_realization();
+ b.add_input<decl::Float>(N_("Size"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(10.0f)
+ .compositor_domain_priority(1);
+ b.add_input<decl::Float>(N_("Bounding box"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .compositor_domain_priority(2);
b.add_output<decl::Color>(N_("Image"));
}
@@ -47,7 +65,82 @@ class BokehBlurOperation : public NodeOperation {
void execute() override
{
- get_input("Image").pass_through(get_result("Image"));
+ if (is_identity()) {
+ get_input("Image").pass_through(get_result("Image"));
+ return;
+ }
+
+ GPUShader *shader = shader_manager().get("compositor_blur");
+ GPU_shader_bind(shader);
+
+ GPU_shader_uniform_1i(shader, "radius", compute_blur_radius());
+ GPU_shader_uniform_1b(shader, "extend_bounds", get_extend_bounds());
+
+ const Result &input_image = get_input("Image");
+ input_image.bind_as_texture(shader, "input_tx");
+
+ const Result &input_weights = get_input("Bokeh");
+ input_weights.bind_as_texture(shader, "weights_tx");
+
+ const Result &input_mask = get_input("Bounding box");
+ input_mask.bind_as_texture(shader, "mask_tx");
+
+ Domain domain = compute_domain();
+ if (get_extend_bounds()) {
+ /* Add a radius amount of pixels in both sides of the image, hence the multiply by 2. */
+ domain.size += int2(compute_blur_radius() * 2);
+ }
+
+ Result &output_image = get_result("Image");
+ output_image.allocate_texture(domain);
+ output_image.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ GPU_shader_unbind();
+ output_image.unbind_as_image();
+ input_image.unbind_as_texture();
+ input_weights.unbind_as_texture();
+ input_mask.unbind_as_texture();
+ }
+
+ int compute_blur_radius()
+ {
+ const int2 image_size = get_input("Image").domain().size;
+ const int max_size = math::max(image_size.x, image_size.y);
+
+ /* The [0, 10] range of the size is arbitrary and is merely in place to avoid very long
+ * computations of the bokeh blur. */
+ const float size = math::clamp(get_input("Size").get_float_value_default(1.0f), 0.0f, 10.0f);
+
+ /* The 100 divisor is arbitrary and was chosen using visual judgment. */
+ return size * (max_size / 100.0f);
+ }
+
+ bool is_identity()
+ {
+ const Result &input = get_input("Image");
+ if (input.is_single_value()) {
+ return true;
+ }
+
+ if (compute_blur_radius() == 0) {
+ return true;
+ }
+
+ /* This input is, in fact, a boolean mask. If it is zero, no blurring will take place.
+ * Otherwise, the blurring will take place ignoring the value of the input entirely. */
+ const Result &bounding_box = get_input("Bounding box");
+ if (bounding_box.is_single_value() && bounding_box.get_float_value() == 0.0) {
+ return true;
+ }
+
+ return false;
+ }
+
+ bool get_extend_bounds()
+ {
+ return bnode().custom1 & CMP_NODEFLAG_BLUR_EXTEND_BOUNDS;
}
};
diff --git a/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc b/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc
index a11cba37191..81cc8990d35 100644
--- a/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc
@@ -5,10 +5,16 @@
* \ingroup cmpnodes
*/
+#include "BLI_math_base.h"
+#include "BLI_math_vec_types.hh"
+
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_shader.h"
+
#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
#include "node_composite_util.hh"
@@ -16,6 +22,8 @@
namespace blender::nodes::node_composite_bokehimage_cc {
+NODE_STORAGE_FUNCS(NodeBokehImage)
+
static void cmp_node_bokehimage_declare(NodeDeclarationBuilder &b)
{
b.add_output<decl::Color>(N_("Image"));
@@ -55,7 +63,45 @@ class BokehImageOperation : public NodeOperation {
void execute() override
{
- get_result("Image").allocate_invalid();
+ GPUShader *shader = shader_manager().get("compositor_bokeh_image");
+ GPU_shader_bind(shader);
+
+ GPU_shader_uniform_1f(shader, "exterior_angle", get_exterior_angle());
+ GPU_shader_uniform_1f(shader, "rotation", get_rotation());
+ GPU_shader_uniform_1f(shader, "roundness", node_storage(bnode()).rounding);
+ GPU_shader_uniform_1f(shader, "catadioptric", node_storage(bnode()).catadioptric);
+ GPU_shader_uniform_1f(shader, "lens_shift", node_storage(bnode()).lensshift);
+
+ Result &output = get_result("Image");
+ const Domain domain = compute_domain();
+ output.allocate_texture(domain);
+ output.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ output.unbind_as_image();
+ GPU_shader_unbind();
+ }
+
+ Domain compute_domain() override
+ {
+ return Domain(int2(512));
+ }
+
+ /* The exterior angle is the angle between each two consecutive vertices of the regular polygon
+ * from its center. */
+ float get_exterior_angle()
+ {
+ return (M_PI * 2.0f) / node_storage(bnode()).flaps;
+ }
+
+ float get_rotation()
+ {
+ /* Offset the rotation such that the second vertex of the regular polygon lies on the positive
+ * y axis, which is 90 degrees minus the angle that it makes with the positive x axis assuming
+ * the first vertex lies on the positive x axis. */
+ const float offset = M_PI_2 - get_exterior_angle();
+ return node_storage(bnode()).angle - offset;
}
};
diff --git a/source/blender/nodes/composite/nodes/node_composite_boxmask.cc b/source/blender/nodes/composite/nodes/node_composite_boxmask.cc
index 9c7bb6432cb..3cf0932e1b3 100644
--- a/source/blender/nodes/composite/nodes/node_composite_boxmask.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_boxmask.cc
@@ -23,6 +23,8 @@
namespace blender::nodes::node_composite_boxmask_cc {
+NODE_STORAGE_FUNCS(NodeBoxMask)
+
static void cmp_node_boxmask_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Float>(N_("Mask")).default_value(0.0f).min(0.0f).max(1.0f);
@@ -123,24 +125,19 @@ class BoxMaskOperation : public NodeOperation {
}
}
- NodeBoxMask &get_node_box_mask()
- {
- return *static_cast<NodeBoxMask *>(bnode().storage);
- }
-
float2 get_location()
{
- return float2(get_node_box_mask().x, get_node_box_mask().y);
+ return float2(node_storage(bnode()).x, node_storage(bnode()).y);
}
float2 get_size()
{
- return float2(get_node_box_mask().width, get_node_box_mask().height);
+ return float2(node_storage(bnode()).width, node_storage(bnode()).height);
}
float get_angle()
{
- return get_node_box_mask().rotation;
+ return node_storage(bnode()).rotation;
}
};
diff --git a/source/blender/nodes/composite/nodes/node_composite_channel_matte.cc b/source/blender/nodes/composite/nodes/node_composite_channel_matte.cc
index 018632f776c..3b825017da8 100644
--- a/source/blender/nodes/composite/nodes/node_composite_channel_matte.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_channel_matte.cc
@@ -20,6 +20,8 @@
namespace blender::nodes::node_composite_channel_matte_cc {
+NODE_STORAGE_FUNCS(NodeChroma)
+
static void cmp_node_channel_matte_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Color>(N_("Image"))
@@ -130,15 +132,10 @@ class ChannelMatteShaderNode : public ShaderNode {
return bnode().custom2 - 1;
}
- NodeChroma *get_node_chroma()
- {
- return static_cast<NodeChroma *>(bnode().storage);
- }
-
/* Get the index of the channel used to compute the limit value. */
int get_limit_channel()
{
- return get_node_chroma()->channel - 1;
+ return node_storage(bnode()).channel - 1;
}
/* Get the indices of the channels used to compute the limit value. We always assume the limit
@@ -146,7 +143,7 @@ class ChannelMatteShaderNode : public ShaderNode {
* the maximum of two identical values is the same value. */
void get_limit_channels(float limit_channels[2])
{
- if (get_node_chroma()->algorithm == CMP_NODE_CHANNEL_MATTE_LIMIT_ALGORITHM_MAX) {
+ if (node_storage(bnode()).algorithm == CMP_NODE_CHANNEL_MATTE_LIMIT_ALGORITHM_MAX) {
/* If the algorithm is Max, store the indices of the other two channels other than the matte
* channel. */
limit_channels[0] = (get_matte_channel() + 1) % 3;
@@ -161,12 +158,12 @@ class ChannelMatteShaderNode : public ShaderNode {
float get_max_limit()
{
- return get_node_chroma()->t1;
+ return node_storage(bnode()).t1;
}
float get_min_limit()
{
- return get_node_chroma()->t2;
+ return node_storage(bnode()).t2;
}
};
diff --git a/source/blender/nodes/composite/nodes/node_composite_chroma_matte.cc b/source/blender/nodes/composite/nodes/node_composite_chroma_matte.cc
index cb3648c5680..e5ce87169d4 100644
--- a/source/blender/nodes/composite/nodes/node_composite_chroma_matte.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_chroma_matte.cc
@@ -22,6 +22,8 @@
namespace blender::nodes::node_composite_chroma_matte_cc {
+NODE_STORAGE_FUNCS(NodeChroma)
+
static void cmp_node_chroma_matte_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Color>(N_("Image"))
@@ -86,24 +88,19 @@ class ChromaMatteShaderNode : public ShaderNode {
GPU_uniform(&falloff));
}
- NodeChroma *get_node_chroma()
- {
- return static_cast<NodeChroma *>(bnode().storage);
- }
-
float get_acceptance()
{
- return std::tan(get_node_chroma()->t1) / 2.0f;
+ return std::tan(node_storage(bnode()).t1) / 2.0f;
}
float get_cutoff()
{
- return get_node_chroma()->t2;
+ return node_storage(bnode()).t2;
}
float get_falloff()
{
- return get_node_chroma()->fstrength;
+ return node_storage(bnode()).fstrength;
}
};
diff --git a/source/blender/nodes/composite/nodes/node_composite_color_matte.cc b/source/blender/nodes/composite/nodes/node_composite_color_matte.cc
index 5e3aaf512e6..08329601f14 100644
--- a/source/blender/nodes/composite/nodes/node_composite_color_matte.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_color_matte.cc
@@ -18,6 +18,8 @@
namespace blender::nodes::node_composite_color_matte_cc {
+NODE_STORAGE_FUNCS(NodeChroma)
+
static void cmp_node_color_matte_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Color>(N_("Image"))
@@ -83,25 +85,20 @@ class ColorMatteShaderNode : public ShaderNode {
GPU_uniform(&value_epsilon));
}
- NodeChroma *get_node_chroma()
- {
- return static_cast<NodeChroma *>(bnode().storage);
- }
-
float get_hue_epsilon()
{
/* Divide by 2 because the hue wraps around. */
- return get_node_chroma()->t1 / 2.0f;
+ return node_storage(bnode()).t1 / 2.0f;
}
float get_saturation_epsilon()
{
- return get_node_chroma()->t2;
+ return node_storage(bnode()).t2;
}
float get_value_epsilon()
{
- return get_node_chroma()->t3;
+ return node_storage(bnode()).t3;
}
};
diff --git a/source/blender/nodes/composite/nodes/node_composite_color_spill.cc b/source/blender/nodes/composite/nodes/node_composite_color_spill.cc
index 9744c01a256..29401d7b20f 100644
--- a/source/blender/nodes/composite/nodes/node_composite_color_spill.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_color_spill.cc
@@ -20,6 +20,8 @@
namespace blender::nodes::node_composite_color_spill_cc {
+NODE_STORAGE_FUNCS(NodeColorspill)
+
static void cmp_node_color_spill_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Color>(N_("Image"))
@@ -131,18 +133,13 @@ class ColorSpillShaderNode : public ShaderNode {
return (CMPNodeColorSpillLimitAlgorithm)bnode().custom2;
}
- NodeColorspill *get_node_color_spill()
- {
- return static_cast<NodeColorspill *>(bnode().storage);
- }
-
void get_spill_scale(float spill_scale[3])
{
- const NodeColorspill *node_color_spill = get_node_color_spill();
- if (node_color_spill->unspill) {
- spill_scale[0] = node_color_spill->uspillr;
- spill_scale[1] = node_color_spill->uspillg;
- spill_scale[2] = node_color_spill->uspillb;
+ const NodeColorspill &node_color_spill = node_storage(bnode());
+ if (node_color_spill.unspill) {
+ spill_scale[0] = node_color_spill.uspillr;
+ spill_scale[1] = node_color_spill.uspillg;
+ spill_scale[2] = node_color_spill.uspillb;
spill_scale[get_spill_channel()] *= -1.0f;
}
else {
@@ -156,7 +153,7 @@ class ColorSpillShaderNode : public ShaderNode {
/* Get the index of the channel used for limiting. */
int get_limit_channel()
{
- return get_node_color_spill()->limchan;
+ return node_storage(bnode()).limchan;
}
/* Get the indices of the channels used to compute the limit value. We always assume the limit
@@ -179,7 +176,7 @@ class ColorSpillShaderNode : public ShaderNode {
float get_limit_scale()
{
- return get_node_color_spill()->limscale;
+ return node_storage(bnode()).limscale;
}
};
diff --git a/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc b/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc
index 95675169c76..e05fbf00a25 100644
--- a/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc
@@ -48,6 +48,8 @@ void ntreeCompositColorBalanceSyncFromCDL(bNodeTree *UNUSED(ntree), bNode *node)
namespace blender::nodes::node_composite_colorbalance_cc {
+NODE_STORAGE_FUNCS(NodeColorBalance)
+
static void cmp_node_colorbalance_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Float>(N_("Fac"))
@@ -161,7 +163,7 @@ class ColorBalanceShaderNode : public ShaderNode {
GPUNodeStack *inputs = get_inputs_array();
GPUNodeStack *outputs = get_outputs_array();
- const NodeColorBalance *node_color_balance = get_node_color_balance();
+ const NodeColorBalance &node_color_balance = node_storage(bnode());
if (get_color_balance_method() == CMP_NODE_COLOR_BALANCE_LGG) {
GPU_stack_link(material,
@@ -169,9 +171,9 @@ class ColorBalanceShaderNode : public ShaderNode {
"node_composite_color_balance_lgg",
inputs,
outputs,
- GPU_uniform(node_color_balance->lift),
- GPU_uniform(node_color_balance->gamma),
- GPU_uniform(node_color_balance->gain));
+ GPU_uniform(node_color_balance.lift),
+ GPU_uniform(node_color_balance.gamma),
+ GPU_uniform(node_color_balance.gain));
return;
}
@@ -180,21 +182,16 @@ class ColorBalanceShaderNode : public ShaderNode {
"node_composite_color_balance_asc_cdl",
inputs,
outputs,
- GPU_uniform(node_color_balance->offset),
- GPU_uniform(node_color_balance->power),
- GPU_uniform(node_color_balance->slope),
- GPU_uniform(&node_color_balance->offset_basis));
+ GPU_uniform(node_color_balance.offset),
+ GPU_uniform(node_color_balance.power),
+ GPU_uniform(node_color_balance.slope),
+ GPU_uniform(&node_color_balance.offset_basis));
}
CMPNodeColorBalanceMethod get_color_balance_method()
{
return (CMPNodeColorBalanceMethod)bnode().custom1;
}
-
- NodeColorBalance *get_node_color_balance()
- {
- return static_cast<NodeColorBalance *>(bnode().storage);
- }
};
static ShaderNode *get_compositor_shader_node(DNode node)
diff --git a/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc b/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc
index 36e6672ce1c..92b10fc1877 100644
--- a/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc
@@ -20,6 +20,8 @@
namespace blender::nodes::node_composite_colorcorrection_cc {
+NODE_STORAGE_FUNCS(NodeColorCorrection)
+
static void cmp_node_colorcorrection_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Color>(N_("Image"))
@@ -294,7 +296,7 @@ class ColorCorrectionShaderNode : public ShaderNode {
float luminance_coefficients[3];
IMB_colormanagement_get_luminance_coefficients(luminance_coefficients);
- const NodeColorCorrection *node_color_correction = get_node_color_correction();
+ const NodeColorCorrection &node_color_correction = node_storage(bnode());
GPU_stack_link(material,
&bnode(),
@@ -302,28 +304,28 @@ class ColorCorrectionShaderNode : public ShaderNode {
inputs,
outputs,
GPU_constant(enabled_channels),
- GPU_uniform(&node_color_correction->startmidtones),
- GPU_uniform(&node_color_correction->endmidtones),
- GPU_uniform(&node_color_correction->master.saturation),
- GPU_uniform(&node_color_correction->master.contrast),
- GPU_uniform(&node_color_correction->master.gamma),
- GPU_uniform(&node_color_correction->master.gain),
- GPU_uniform(&node_color_correction->master.lift),
- GPU_uniform(&node_color_correction->shadows.saturation),
- GPU_uniform(&node_color_correction->shadows.contrast),
- GPU_uniform(&node_color_correction->shadows.gamma),
- GPU_uniform(&node_color_correction->shadows.gain),
- GPU_uniform(&node_color_correction->shadows.lift),
- GPU_uniform(&node_color_correction->midtones.saturation),
- GPU_uniform(&node_color_correction->midtones.contrast),
- GPU_uniform(&node_color_correction->midtones.gamma),
- GPU_uniform(&node_color_correction->midtones.gain),
- GPU_uniform(&node_color_correction->midtones.lift),
- GPU_uniform(&node_color_correction->highlights.saturation),
- GPU_uniform(&node_color_correction->highlights.contrast),
- GPU_uniform(&node_color_correction->highlights.gamma),
- GPU_uniform(&node_color_correction->highlights.gain),
- GPU_uniform(&node_color_correction->highlights.lift),
+ GPU_uniform(&node_color_correction.startmidtones),
+ GPU_uniform(&node_color_correction.endmidtones),
+ GPU_uniform(&node_color_correction.master.saturation),
+ GPU_uniform(&node_color_correction.master.contrast),
+ GPU_uniform(&node_color_correction.master.gamma),
+ GPU_uniform(&node_color_correction.master.gain),
+ GPU_uniform(&node_color_correction.master.lift),
+ GPU_uniform(&node_color_correction.shadows.saturation),
+ GPU_uniform(&node_color_correction.shadows.contrast),
+ GPU_uniform(&node_color_correction.shadows.gamma),
+ GPU_uniform(&node_color_correction.shadows.gain),
+ GPU_uniform(&node_color_correction.shadows.lift),
+ GPU_uniform(&node_color_correction.midtones.saturation),
+ GPU_uniform(&node_color_correction.midtones.contrast),
+ GPU_uniform(&node_color_correction.midtones.gamma),
+ GPU_uniform(&node_color_correction.midtones.gain),
+ GPU_uniform(&node_color_correction.midtones.lift),
+ GPU_uniform(&node_color_correction.highlights.saturation),
+ GPU_uniform(&node_color_correction.highlights.contrast),
+ GPU_uniform(&node_color_correction.highlights.gamma),
+ GPU_uniform(&node_color_correction.highlights.gain),
+ GPU_uniform(&node_color_correction.highlights.lift),
GPU_constant(luminance_coefficients));
}
@@ -333,11 +335,6 @@ class ColorCorrectionShaderNode : public ShaderNode {
enabled_channels[i] = (bnode().custom1 & (1 << i)) ? 1.0f : 0.0f;
}
}
-
- NodeColorCorrection *get_node_color_correction()
- {
- return static_cast<NodeColorCorrection *>(bnode().storage);
- }
};
static ShaderNode *get_compositor_shader_node(DNode node)
diff --git a/source/blender/nodes/composite/nodes/node_composite_crop.cc b/source/blender/nodes/composite/nodes/node_composite_crop.cc
index d7331732fc7..13d02a707be 100644
--- a/source/blender/nodes/composite/nodes/node_composite_crop.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_crop.cc
@@ -27,6 +27,8 @@
namespace blender::nodes::node_composite_crop_cc {
+NODE_STORAGE_FUNCS(NodeTwoXYs)
+
static void cmp_node_crop_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Color>(N_("Image"))
@@ -163,11 +165,6 @@ class CropOperation : public NodeOperation {
return bnode().custom2;
}
- NodeTwoXYs &get_node_two_xys()
- {
- return *static_cast<NodeTwoXYs *>(bnode().storage);
- }
-
/* Returns true if the operation does nothing and the input can be passed through. */
bool is_identity()
{
@@ -190,7 +187,7 @@ class CropOperation : public NodeOperation {
void compute_cropping_bounds(int2 &lower_bound, int2 &upper_bound)
{
- const NodeTwoXYs &node_two_xys = get_node_two_xys();
+ const NodeTwoXYs &node_two_xys = node_storage(bnode());
const int2 input_size = get_input("Image").domain().size;
if (get_is_relative()) {
diff --git a/source/blender/nodes/composite/nodes/node_composite_curves.cc b/source/blender/nodes/composite/nodes/node_composite_curves.cc
index c5d303c576a..bf45e219730 100644
--- a/source/blender/nodes/composite/nodes/node_composite_curves.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_curves.cc
@@ -47,15 +47,15 @@ class TimeCurveOperation : public NodeOperation {
Result &result = get_result("Fac");
result.allocate_single_value();
- CurveMapping *curve_mapping = get_curve_mapping();
+ CurveMapping *curve_mapping = const_cast<CurveMapping *>(get_curve_mapping());
BKE_curvemapping_init(curve_mapping);
const float time = BKE_curvemapping_evaluateF(curve_mapping, 0, compute_normalized_time());
result.set_float_value(clamp_f(time, 0.0f, 1.0f));
}
- CurveMapping *get_curve_mapping()
+ const CurveMapping *get_curve_mapping()
{
- return static_cast<CurveMapping *>(bnode().storage);
+ return static_cast<const CurveMapping *>(bnode().storage);
}
int get_start_time()
@@ -143,7 +143,7 @@ class VectorCurvesShaderNode : public ShaderNode {
GPUNodeStack *inputs = get_inputs_array();
GPUNodeStack *outputs = get_outputs_array();
- CurveMapping *curve_mapping = get_curve_mapping();
+ CurveMapping *curve_mapping = const_cast<CurveMapping *>(get_curve_mapping());
BKE_curvemapping_init(curve_mapping);
float *band_values;
@@ -173,9 +173,9 @@ class VectorCurvesShaderNode : public ShaderNode {
GPU_uniform(end_slopes));
}
- CurveMapping *get_curve_mapping()
+ const CurveMapping *get_curve_mapping()
{
- return static_cast<CurveMapping *>(bnode().storage);
+ return static_cast<const CurveMapping *>(bnode().storage);
}
};
@@ -239,7 +239,7 @@ class RGBCurvesShaderNode : public ShaderNode {
GPUNodeStack *inputs = get_inputs_array();
GPUNodeStack *outputs = get_outputs_array();
- CurveMapping *curve_mapping = get_curve_mapping();
+ CurveMapping *curve_mapping = const_cast<CurveMapping *>(get_curve_mapping());
BKE_curvemapping_init(curve_mapping);
float *band_values;
@@ -311,9 +311,9 @@ class RGBCurvesShaderNode : public ShaderNode {
GPU_uniform(end_slopes));
}
- CurveMapping *get_curve_mapping()
+ const CurveMapping *get_curve_mapping()
{
- return static_cast<CurveMapping *>(bnode().storage);
+ return static_cast<const CurveMapping *>(bnode().storage);
}
};
diff --git a/source/blender/nodes/composite/nodes/node_composite_despeckle.cc b/source/blender/nodes/composite/nodes/node_composite_despeckle.cc
index 0b9f9c8f76d..aa6725b8750 100644
--- a/source/blender/nodes/composite/nodes/node_composite_despeckle.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_despeckle.cc
@@ -8,7 +8,10 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_shader.h"
+
#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
#include "node_composite_util.hh"
@@ -18,8 +21,15 @@ namespace blender::nodes::node_composite_despeckle_cc {
static void cmp_node_despeckle_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Float>(N_("Fac"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(1);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Color>(N_("Image"));
}
@@ -46,7 +56,45 @@ class DespeckleOperation : public NodeOperation {
void execute() override
{
- get_input("Image").pass_through(get_result("Image"));
+ const Result &input_image = get_input("Image");
+ /* Single value inputs can't be despeckled and are returned as is. */
+ if (input_image.is_single_value()) {
+ get_input("Image").pass_through(get_result("Image"));
+ return;
+ }
+
+ GPUShader *shader = shader_manager().get("compositor_despeckle");
+ GPU_shader_bind(shader);
+
+ GPU_shader_uniform_1f(shader, "threshold", get_threshold());
+ GPU_shader_uniform_1f(shader, "neighbor_threshold", get_neighbor_threshold());
+
+ input_image.bind_as_texture(shader, "input_tx");
+
+ const Result &factor_image = get_input("Fac");
+ factor_image.bind_as_texture(shader, "factor_tx");
+
+ const Domain domain = compute_domain();
+ Result &output_image = get_result("Image");
+ output_image.allocate_texture(domain);
+ output_image.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ GPU_shader_unbind();
+ output_image.unbind_as_image();
+ input_image.unbind_as_texture();
+ factor_image.unbind_as_texture();
+ }
+
+ float get_threshold()
+ {
+ return bnode().custom3;
+ }
+
+ float get_neighbor_threshold()
+ {
+ return bnode().custom4;
}
};
diff --git a/source/blender/nodes/composite/nodes/node_composite_diff_matte.cc b/source/blender/nodes/composite/nodes/node_composite_diff_matte.cc
index e129dcaa6ef..8912d00a9be 100644
--- a/source/blender/nodes/composite/nodes/node_composite_diff_matte.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_diff_matte.cc
@@ -18,6 +18,8 @@
namespace blender::nodes::node_composite_diff_matte_cc {
+NODE_STORAGE_FUNCS(NodeChroma)
+
static void cmp_node_diff_matte_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Color>(N_("Image 1"))
@@ -71,19 +73,14 @@ class DifferenceMatteShaderNode : public ShaderNode {
GPU_uniform(&falloff));
}
- NodeChroma *get_node_chroma()
- {
- return static_cast<NodeChroma *>(bnode().storage);
- }
-
float get_tolerance()
{
- return get_node_chroma()->t1;
+ return node_storage(bnode()).t1;
}
float get_falloff()
{
- return get_node_chroma()->t2;
+ return node_storage(bnode()).t2;
}
};
diff --git a/source/blender/nodes/composite/nodes/node_composite_dilate.cc b/source/blender/nodes/composite/nodes/node_composite_dilate.cc
index 46199d3ff04..551dfacb276 100644
--- a/source/blender/nodes/composite/nodes/node_composite_dilate.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_dilate.cc
@@ -5,12 +5,27 @@
* \ingroup cmpnodes
*/
+#include <cmath>
+
+#include "BLI_array.hh"
+#include "BLI_assert.h"
+#include "BLI_math_base.hh"
+
+#include "DNA_scene_types.h"
+
#include "RNA_access.h"
#include "UI_interface.h"
#include "UI_resources.h"
+#include "RE_pipeline.h"
+
+#include "GPU_shader.h"
+#include "GPU_state.h"
+#include "GPU_texture.h"
+
#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
#include "node_composite_util.hh"
@@ -18,6 +33,8 @@
namespace blender::nodes::node_composite_dilate_cc {
+NODE_STORAGE_FUNCS(NodeDilateErode)
+
static void cmp_node_dilate_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Float>(N_("Mask")).default_value(0.0f).min(0.0f).max(1.0f);
@@ -36,10 +53,10 @@ static void node_composit_buts_dilateerode(uiLayout *layout, bContext *UNUSED(C)
uiItemR(layout, ptr, "mode", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
uiItemR(layout, ptr, "distance", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
switch (RNA_enum_get(ptr, "mode")) {
- case CMP_NODE_DILATEERODE_DISTANCE_THRESH:
+ case CMP_NODE_DILATE_ERODE_DISTANCE_THRESHOLD:
uiItemR(layout, ptr, "edge", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
break;
- case CMP_NODE_DILATEERODE_DISTANCE_FEATHER:
+ case CMP_NODE_DILATE_ERODE_DISTANCE_FEATHER:
uiItemR(layout, ptr, "falloff", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
break;
}
@@ -47,13 +64,458 @@ static void node_composit_buts_dilateerode(uiLayout *layout, bContext *UNUSED(C)
using namespace blender::realtime_compositor;
+/* Computes a falloff that is equal to 1 at an input of zero and decrease to zero at an input of 1,
+ * with the rate of decrease depending on the falloff type. */
+static float compute_distance_falloff(float x, int falloff_type)
+{
+ x = 1.0f - x;
+
+ switch (falloff_type) {
+ case PROP_SMOOTH:
+ return 3.0f * x * x - 2.0f * x * x * x;
+ case PROP_SPHERE:
+ return std::sqrt(2.0f * x - x * x);
+ case PROP_ROOT:
+ return std::sqrt(x);
+ case PROP_SHARP:
+ return x * x;
+ case PROP_INVSQUARE:
+ return x * (2.0f - x);
+ case PROP_LIN:
+ return x;
+ default:
+ BLI_assert_unreachable();
+ return x;
+ }
+}
+
+/* A helper class that computes and caches 1D GPU textures containing the weights of the separable
+ * Gaussian filter of the given radius as well as an inverse distance falloff of the given type and
+ * radius. The weights and falloffs are symmetric, because the Gaussian and falloff functions are
+ * all even functions. Consequently, only the positive half of the filter is computed and the
+ * shader takes that into consideration. */
+class SymmetricSeparableMorphologicalDistanceFeatherWeights {
+ private:
+ int radius_ = 1;
+ int falloff_type_ = PROP_SMOOTH;
+ GPUTexture *weights_texture_ = nullptr;
+ GPUTexture *distance_falloffs_texture_ = nullptr;
+
+ public:
+ ~SymmetricSeparableMorphologicalDistanceFeatherWeights()
+ {
+ if (weights_texture_) {
+ GPU_texture_free(weights_texture_);
+ }
+
+ if (distance_falloffs_texture_) {
+ GPU_texture_free(distance_falloffs_texture_);
+ }
+ }
+
+ /* Check if textures containing the weights and distance falloffs were already computed for the
+ * given distance falloff type and radius. If such textures exists, do nothing, otherwise, free
+ * the already computed textures and recompute it with the given distance falloff type and
+ * radius. */
+ void update(int radius, int falloff_type)
+ {
+ if (weights_texture_ && distance_falloffs_texture_ && falloff_type == falloff_type_ &&
+ radius == radius_) {
+ return;
+ }
+
+ radius_ = radius;
+ falloff_type_ = falloff_type;
+
+ compute_weights();
+ compute_distance_falloffs();
+ }
+
+ void compute_weights()
+ {
+ if (weights_texture_) {
+ GPU_texture_free(weights_texture_);
+ }
+
+ /* The size of filter is double the radius plus 1, but since the filter is symmetric, we only
+ * compute half of it and no doubling happens. We add 1 to make sure the filter size is always
+ * odd and there is a center weight. */
+ const int size = radius_ + 1;
+ Array<float> weights(size);
+
+ float sum = 0.0f;
+
+ /* First, compute the center weight. */
+ const float center_weight = RE_filter_value(R_FILTER_GAUSS, 0.0f);
+ weights[0] = center_weight;
+ sum += center_weight;
+
+ /* Second, compute the other weights in the positive direction, making sure to add double the
+ * weight to the sum of weights because the filter is symmetric and we only loop over half of
+ * it. Skip the center weight already computed by dropping the front index. */
+ const float scale = radius_ > 0.0f ? 1.0f / radius_ : 0.0f;
+ for (const int i : weights.index_range().drop_front(1)) {
+ const float weight = RE_filter_value(R_FILTER_GAUSS, i * scale);
+ weights[i] = weight;
+ sum += weight * 2.0f;
+ }
+
+ /* Finally, normalize the weights. */
+ for (const int i : weights.index_range()) {
+ weights[i] /= sum;
+ }
+
+ weights_texture_ = GPU_texture_create_1d("Weights", size, 1, GPU_R16F, weights.data());
+ }
+
+ void compute_distance_falloffs()
+ {
+ if (distance_falloffs_texture_) {
+ GPU_texture_free(distance_falloffs_texture_);
+ }
+
+ /* The size of the distance falloffs is double the radius plus 1, but since the falloffs are
+ * symmetric, we only compute half of them and no doubling happens. We add 1 to make sure the
+ * falloffs size is always odd and there is a center falloff. */
+ const int size = radius_ + 1;
+ Array<float> falloffs(size);
+
+ /* Compute the distance falloffs in the positive direction only, because the falloffs are
+ * symmetric. */
+ const float scale = radius_ > 0.0f ? 1.0f / radius_ : 0.0f;
+ for (const int i : falloffs.index_range()) {
+ falloffs[i] = compute_distance_falloff(i * scale, falloff_type_);
+ }
+
+ distance_falloffs_texture_ = GPU_texture_create_1d(
+ "Distance Factors", size, 1, GPU_R16F, falloffs.data());
+ }
+
+ void bind_weights_as_texture(GPUShader *shader, const char *texture_name)
+ {
+ const int texture_image_unit = GPU_shader_get_texture_binding(shader, texture_name);
+ GPU_texture_bind(weights_texture_, texture_image_unit);
+ }
+
+ void unbind_weights_as_texture()
+ {
+ GPU_texture_unbind(weights_texture_);
+ }
+
+ void bind_distance_falloffs_as_texture(GPUShader *shader, const char *texture_name)
+ {
+ const int texture_image_unit = GPU_shader_get_texture_binding(shader, texture_name);
+ GPU_texture_bind(distance_falloffs_texture_, texture_image_unit);
+ }
+
+ void unbind_distance_falloffs_as_texture()
+ {
+ GPU_texture_unbind(distance_falloffs_texture_);
+ }
+};
+
class DilateErodeOperation : public NodeOperation {
+ private:
+ /* Cached symmetric blur weights and distance falloffs for the distance feature method. */
+ SymmetricSeparableMorphologicalDistanceFeatherWeights distance_feather_weights_;
+
public:
using NodeOperation::NodeOperation;
void execute() override
{
- get_input("Mask").pass_through(get_result("Mask"));
+ if (is_identity()) {
+ get_input("Mask").pass_through(get_result("Mask"));
+ return;
+ }
+
+ switch (get_method()) {
+ case CMP_NODE_DILATE_ERODE_STEP:
+ execute_step();
+ return;
+ case CMP_NODE_DILATE_ERODE_DISTANCE:
+ execute_distance();
+ return;
+ case CMP_NODE_DILATE_ERODE_DISTANCE_THRESHOLD:
+ execute_distance_threshold();
+ return;
+ case CMP_NODE_DILATE_ERODE_DISTANCE_FEATHER:
+ execute_distance_feather();
+ return;
+ default:
+ BLI_assert_unreachable();
+ return;
+ }
+ }
+
+ /* ----------------------------
+ * Step Morphological Operator.
+ * ---------------------------- */
+
+ void execute_step()
+ {
+ GPUTexture *horizontal_pass_result = execute_step_horizontal_pass();
+ execute_step_vertical_pass(horizontal_pass_result);
+ }
+
+ GPUTexture *execute_step_horizontal_pass()
+ {
+ GPUShader *shader = shader_manager().get(get_morphological_step_shader_name());
+ GPU_shader_bind(shader);
+
+ /* Pass the absolute value of the distance. We have specialized shaders for each sign. */
+ GPU_shader_uniform_1i(shader, "radius", math::abs(get_distance()));
+
+ const Result &input_mask = get_input("Mask");
+ input_mask.bind_as_texture(shader, "input_tx");
+
+ /* We allocate an output image of a transposed size, that is, with a height equivalent to the
+ * width of the input and vice versa. This is done as a performance optimization. The shader
+ * will process the image horizontally and write it to the intermediate output transposed. Then
+ * the vertical pass will execute the same horizontal pass shader, but since its input is
+ * transposed, it will effectively do a vertical pass and write to the output transposed,
+ * effectively undoing the transposition in the horizontal pass. This is done to improve
+ * spatial cache locality in the shader and to avoid having two separate shaders for each of
+ * the passes. */
+ const Domain domain = compute_domain();
+ const int2 transposed_domain = int2(domain.size.y, domain.size.x);
+
+ GPUTexture *horizontal_pass_result = texture_pool().acquire_color(transposed_domain);
+ const int image_unit = GPU_shader_get_texture_binding(shader, "output_img");
+ GPU_texture_image_bind(horizontal_pass_result, image_unit);
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ GPU_shader_unbind();
+ input_mask.unbind_as_texture();
+ GPU_texture_image_unbind(horizontal_pass_result);
+
+ return horizontal_pass_result;
+ }
+
+ void execute_step_vertical_pass(GPUTexture *horizontal_pass_result)
+ {
+ GPUShader *shader = shader_manager().get(get_morphological_step_shader_name());
+ GPU_shader_bind(shader);
+
+ /* Pass the absolute value of the distance. We have specialized shaders for each sign. */
+ GPU_shader_uniform_1i(shader, "radius", math::abs(get_distance()));
+
+ GPU_memory_barrier(GPU_BARRIER_TEXTURE_FETCH);
+ const int texture_image_unit = GPU_shader_get_texture_binding(shader, "input_tx");
+ GPU_texture_bind(horizontal_pass_result, texture_image_unit);
+
+ const Domain domain = compute_domain();
+ Result &output_mask = get_result("Mask");
+ output_mask.allocate_texture(domain);
+ output_mask.bind_as_image(shader, "output_img");
+
+ /* Notice that the domain is transposed, see the note on the horizontal pass method for more
+ * information on the reasoning behind this. */
+ compute_dispatch_threads_at_least(shader, int2(domain.size.y, domain.size.x));
+
+ GPU_shader_unbind();
+ output_mask.unbind_as_image();
+ GPU_texture_unbind(horizontal_pass_result);
+ }
+
+ const char *get_morphological_step_shader_name()
+ {
+ if (get_distance() > 0) {
+ return "compositor_morphological_step_dilate";
+ }
+ return "compositor_morphological_step_erode";
+ }
+
+ /* --------------------------------
+ * Distance Morphological Operator.
+ * -------------------------------- */
+
+ void execute_distance()
+ {
+ GPUShader *shader = shader_manager().get(get_morphological_distance_shader_name());
+ GPU_shader_bind(shader);
+
+ /* Pass the absolute value of the distance. We have specialized shaders for each sign. */
+ GPU_shader_uniform_1i(shader, "radius", math::abs(get_distance()));
+
+ const Result &input_mask = get_input("Mask");
+ input_mask.bind_as_texture(shader, "input_tx");
+
+ const Domain domain = compute_domain();
+ Result &output_mask = get_result("Mask");
+ output_mask.allocate_texture(domain);
+ output_mask.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ GPU_shader_unbind();
+ output_mask.unbind_as_image();
+ input_mask.unbind_as_texture();
+ }
+
+ const char *get_morphological_distance_shader_name()
+ {
+ if (get_distance() > 0) {
+ return "compositor_morphological_distance_dilate";
+ }
+ return "compositor_morphological_distance_erode";
+ }
+
+ /* ------------------------------------------
+ * Distance Threshold Morphological Operator.
+ * ------------------------------------------ */
+
+ void execute_distance_threshold()
+ {
+ GPUShader *shader = shader_manager().get("compositor_morphological_distance_threshold");
+ GPU_shader_bind(shader);
+
+ GPU_shader_uniform_1f(shader, "inset", get_inset());
+ GPU_shader_uniform_1i(shader, "radius", get_morphological_distance_threshold_radius());
+ GPU_shader_uniform_1i(shader, "distance", get_distance());
+
+ const Result &input_mask = get_input("Mask");
+ input_mask.bind_as_texture(shader, "input_tx");
+
+ const Domain domain = compute_domain();
+ Result &output_mask = get_result("Mask");
+ output_mask.allocate_texture(domain);
+ output_mask.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ GPU_shader_unbind();
+ output_mask.unbind_as_image();
+ input_mask.unbind_as_texture();
+ }
+
+ /* See the discussion in the implementation for more information. */
+ int get_morphological_distance_threshold_radius()
+ {
+ return static_cast<int>(math::ceil(get_inset())) + math::abs(get_distance());
+ }
+
+ /* ----------------------------------------
+ * Distance Feather Morphological Operator.
+ * ---------------------------------------- */
+
+ void execute_distance_feather()
+ {
+ GPUTexture *horizontal_pass_result = execute_distance_feather_horizontal_pass();
+ execute_distance_feather_vertical_pass(horizontal_pass_result);
+ }
+
+ GPUTexture *execute_distance_feather_horizontal_pass()
+ {
+ GPUShader *shader = shader_manager().get(get_morphological_distance_feather_shader_name());
+ GPU_shader_bind(shader);
+
+ const Result &input_image = get_input("Mask");
+ input_image.bind_as_texture(shader, "input_tx");
+
+ distance_feather_weights_.update(math::abs(get_distance()), node_storage(bnode()).falloff);
+ distance_feather_weights_.bind_weights_as_texture(shader, "weights_tx");
+ distance_feather_weights_.bind_distance_falloffs_as_texture(shader, "falloffs_tx");
+
+ /* We allocate an output image of a transposed size, that is, with a height equivalent to the
+ * width of the input and vice versa. This is done as a performance optimization. The shader
+ * will process the image horizontally and write it to the intermediate output transposed. Then
+ * the vertical pass will execute the same horizontal pass shader, but since its input is
+ * transposed, it will effectively do a vertical pass and write to the output transposed,
+ * effectively undoing the transposition in the horizontal pass. This is done to improve
+ * spatial cache locality in the shader and to avoid having two separate shaders for each of
+ * the passes. */
+ const Domain domain = compute_domain();
+ const int2 transposed_domain = int2(domain.size.y, domain.size.x);
+
+ GPUTexture *horizontal_pass_result = texture_pool().acquire_color(transposed_domain);
+ const int image_unit = GPU_shader_get_texture_binding(shader, "output_img");
+ GPU_texture_image_bind(horizontal_pass_result, image_unit);
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ GPU_shader_unbind();
+ input_image.unbind_as_texture();
+ distance_feather_weights_.unbind_weights_as_texture();
+ distance_feather_weights_.unbind_distance_falloffs_as_texture();
+ GPU_texture_image_unbind(horizontal_pass_result);
+
+ return horizontal_pass_result;
+ }
+
+ void execute_distance_feather_vertical_pass(GPUTexture *horizontal_pass_result)
+ {
+ GPUShader *shader = shader_manager().get(get_morphological_distance_feather_shader_name());
+ GPU_shader_bind(shader);
+
+ GPU_memory_barrier(GPU_BARRIER_TEXTURE_FETCH);
+ const int texture_image_unit = GPU_shader_get_texture_binding(shader, "input_tx");
+ GPU_texture_bind(horizontal_pass_result, texture_image_unit);
+
+ distance_feather_weights_.update(math::abs(get_distance()), node_storage(bnode()).falloff);
+ distance_feather_weights_.bind_weights_as_texture(shader, "weights_tx");
+ distance_feather_weights_.bind_distance_falloffs_as_texture(shader, "falloffs_tx");
+
+ const Domain domain = compute_domain();
+ Result &output_image = get_result("Mask");
+ output_image.allocate_texture(domain);
+ output_image.bind_as_image(shader, "output_img");
+
+ /* Notice that the domain is transposed, see the note on the horizontal pass method for more
+ * information on the reasoning behind this. */
+ compute_dispatch_threads_at_least(shader, int2(domain.size.y, domain.size.x));
+
+ GPU_shader_unbind();
+ output_image.unbind_as_image();
+ distance_feather_weights_.unbind_weights_as_texture();
+ distance_feather_weights_.unbind_distance_falloffs_as_texture();
+ GPU_texture_unbind(horizontal_pass_result);
+ }
+
+ const char *get_morphological_distance_feather_shader_name()
+ {
+ if (get_distance() > 0) {
+ return "compositor_morphological_distance_feather_dilate";
+ }
+ return "compositor_morphological_distance_feather_erode";
+ }
+
+ /* ---------------
+ * Common Methods.
+ * --------------- */
+
+ bool is_identity()
+ {
+ const Result &input = get_input("Mask");
+ if (input.is_single_value()) {
+ return true;
+ }
+
+ if (get_method() == CMP_NODE_DILATE_ERODE_DISTANCE_THRESHOLD && get_inset() != 0.0f) {
+ return false;
+ }
+
+ if (get_distance() == 0) {
+ return true;
+ }
+
+ return false;
+ }
+
+ int get_distance()
+ {
+ return bnode().custom2;
+ }
+
+ float get_inset()
+ {
+ return bnode().custom3;
+ }
+
+ CMPNodeDilateErodeMethod get_method()
+ {
+ return (CMPNodeDilateErodeMethod)bnode().custom1;
}
};
diff --git a/source/blender/nodes/composite/nodes/node_composite_directionalblur.cc b/source/blender/nodes/composite/nodes/node_composite_directionalblur.cc
index eacba5ad12d..6e6bec70283 100644
--- a/source/blender/nodes/composite/nodes/node_composite_directionalblur.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_directionalblur.cc
@@ -5,18 +5,30 @@
* \ingroup cmpnodes
*/
+#include "BLI_float3x3.hh"
+#include "BLI_math_base.hh"
+#include "BLI_math_vec_types.hh"
+#include "BLI_math_vector.hh"
+
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_shader.h"
+
#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
#include "node_composite_util.hh"
namespace blender::nodes::node_composite_directionalblur_cc {
+NODE_STORAGE_FUNCS(NodeDBlurData)
+
static void cmp_node_directional_blur_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Color>(N_("Image"));
}
@@ -61,7 +73,113 @@ class DirectionalBlurOperation : public NodeOperation {
void execute() override
{
- get_input("Image").pass_through(get_result("Image"));
+ if (is_identity()) {
+ get_input("Image").pass_through(get_result("Image"));
+ return;
+ }
+
+ GPUShader *shader = shader_manager().get("compositor_directional_blur");
+ GPU_shader_bind(shader);
+
+ /* The number of iterations does not cover the original image, that is, the image with no
+ * transformation. So add an extra iteration for the original image and put that into
+ * consideration in the shader. */
+ GPU_shader_uniform_1i(shader, "iterations", get_iterations() + 1);
+ GPU_shader_uniform_mat3_as_mat4(shader, "inverse_transformation", get_transformation().ptr());
+
+ const Result &input_image = get_input("Image");
+ input_image.bind_as_texture(shader, "input_tx");
+
+ GPU_texture_filter_mode(input_image.texture(), true);
+ GPU_texture_wrap_mode(input_image.texture(), false, false);
+
+ const Domain domain = compute_domain();
+ Result &output_image = get_result("Image");
+ output_image.allocate_texture(domain);
+ output_image.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ GPU_shader_unbind();
+ output_image.unbind_as_image();
+ input_image.unbind_as_texture();
+ }
+
+ /* Get the amount of translation that will be applied on each iteration. The translation is in
+ * the negative x direction rotated in the clock-wise direction, hence the negative sign for the
+ * rotation and translation vector. */
+ float2 get_translation()
+ {
+ const float diagonal_length = math::length(float2(get_input("Image").domain().size));
+ const float translation_amount = diagonal_length * node_storage(bnode()).distance;
+ const float3x3 rotation = float3x3::from_rotation(-node_storage(bnode()).angle);
+ return rotation * float2(-translation_amount / get_iterations(), 0.0f);
+ }
+
+ /* Get the amount of rotation that will be applied on each iteration. */
+ float get_rotation()
+ {
+ return node_storage(bnode()).spin / get_iterations();
+ }
+
+ /* Get the amount of scale that will be applied on each iteration. The scale is identity when the
+ * user supplies 0, so we add 1. */
+ float2 get_scale()
+ {
+ return float2(1.0f + node_storage(bnode()).zoom / get_iterations());
+ }
+
+ float2 get_origin()
+ {
+ const float2 center = float2(node_storage(bnode()).center_x, node_storage(bnode()).center_y);
+ return float2(get_input("Image").domain().size) * center;
+ }
+
+ float3x3 get_transformation()
+ {
+ /* Construct the transformation that will be applied on each iteration. */
+ const float3x3 transformation = float3x3::from_translation_rotation_scale(
+ get_translation(), get_rotation(), get_scale());
+ /* Change the origin of the transformation to the user-specified origin. */
+ const float3x3 origin_transformation = float3x3::from_origin_transformation(transformation,
+ get_origin());
+ /* The shader will transform the coordinates, not the image itself, so take the inverse. */
+ return origin_transformation.inverted();
+ }
+
+ /* The actual number of iterations is 2 to the power of the user supplied iterations. The power
+ * is implemented using a bit shift. But also make sure it doesn't exceed the upper limit which
+ * is the number of diagonal pixels. */
+ int get_iterations()
+ {
+ const int iterations = 2 << (node_storage(bnode()).iter - 1);
+ const int upper_limit = math::ceil(math::length(float2(get_input("Image").domain().size)));
+ return math::min(iterations, upper_limit);
+ }
+
+ /* Returns true if the operation does nothing and the input can be passed through. */
+ bool is_identity()
+ {
+ const Result &input = get_input("Image");
+ /* Single value inputs can't be blurred and are returned as is. */
+ if (input.is_single_value()) {
+ return true;
+ }
+
+ /* If any of the following options are non-zero, then the operation is not an identity. */
+ if (node_storage(bnode()).distance != 0.0f) {
+ return false;
+ }
+
+ if (node_storage(bnode()).spin != 0.0f) {
+ return false;
+ }
+
+ if (node_storage(bnode()).zoom != 0.0f) {
+ return false;
+ }
+
+ return true;
}
};
diff --git a/source/blender/nodes/composite/nodes/node_composite_distance_matte.cc b/source/blender/nodes/composite/nodes/node_composite_distance_matte.cc
index 9d910b3f409..6a786571f43 100644
--- a/source/blender/nodes/composite/nodes/node_composite_distance_matte.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_distance_matte.cc
@@ -18,6 +18,8 @@
namespace blender::nodes::node_composite_distance_matte_cc {
+NODE_STORAGE_FUNCS(NodeChroma)
+
static void cmp_node_distance_matte_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Color>(N_("Image"))
@@ -90,24 +92,19 @@ class DistanceMatteShaderNode : public ShaderNode {
GPU_uniform(&falloff));
}
- NodeChroma *get_node_chroma()
- {
- return static_cast<NodeChroma *>(bnode().storage);
- }
-
CMPNodeDistanceMatteColorSpace get_color_space()
{
- return (CMPNodeDistanceMatteColorSpace)get_node_chroma()->channel;
+ return (CMPNodeDistanceMatteColorSpace)node_storage(bnode()).channel;
}
float get_tolerance()
{
- return get_node_chroma()->t1;
+ return node_storage(bnode()).t1;
}
float get_falloff()
{
- return get_node_chroma()->t2;
+ return node_storage(bnode()).t2;
}
};
diff --git a/source/blender/nodes/composite/nodes/node_composite_ellipsemask.cc b/source/blender/nodes/composite/nodes/node_composite_ellipsemask.cc
index 54dfa00eadd..7c031b354e5 100644
--- a/source/blender/nodes/composite/nodes/node_composite_ellipsemask.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_ellipsemask.cc
@@ -23,6 +23,8 @@
namespace blender::nodes::node_composite_ellipsemask_cc {
+NODE_STORAGE_FUNCS(NodeEllipseMask)
+
static void cmp_node_ellipsemask_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Float>(N_("Mask")).default_value(0.0f).min(0.0f).max(1.0f);
@@ -121,24 +123,19 @@ class EllipseMaskOperation : public NodeOperation {
}
}
- NodeEllipseMask &get_node_ellipse_mask()
- {
- return *static_cast<NodeEllipseMask *>(bnode().storage);
- }
-
float2 get_location()
{
- return float2(get_node_ellipse_mask().x, get_node_ellipse_mask().y);
+ return float2(node_storage(bnode()).x, node_storage(bnode()).y);
}
float2 get_size()
{
- return float2(get_node_ellipse_mask().width, get_node_ellipse_mask().height);
+ return float2(node_storage(bnode()).width, node_storage(bnode()).height);
}
float get_angle()
{
- return get_node_ellipse_mask().rotation;
+ return node_storage(bnode()).rotation;
}
};
diff --git a/source/blender/nodes/composite/nodes/node_composite_filter.cc b/source/blender/nodes/composite/nodes/node_composite_filter.cc
index 854cf684806..bd7b443e17e 100644
--- a/source/blender/nodes/composite/nodes/node_composite_filter.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_filter.cc
@@ -5,10 +5,13 @@
* \ingroup cmpnodes
*/
+#include "BLI_float3x3.hh"
+
#include "UI_interface.h"
#include "UI_resources.h"
#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
#include "node_composite_util.hh"
@@ -18,8 +21,15 @@ namespace blender::nodes::node_composite_filter_cc {
static void cmp_node_filter_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Float>(N_("Fac"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(1);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Color>(N_("Image"));
}
@@ -36,7 +46,103 @@ class FilterOperation : public NodeOperation {
void execute() override
{
- get_input("Image").pass_through(get_result("Image"));
+ GPUShader *shader = shader_manager().get(get_shader_name());
+ GPU_shader_bind(shader);
+
+ GPU_shader_uniform_mat3_as_mat4(shader, "kernel", get_filter_kernel().ptr());
+
+ const Result &input_image = get_input("Image");
+ input_image.bind_as_texture(shader, "input_tx");
+
+ const Result &factor = get_input("Fac");
+ factor.bind_as_texture(shader, "factor_tx");
+
+ const Domain domain = compute_domain();
+
+ Result &output_image = get_result("Image");
+ output_image.allocate_texture(domain);
+ output_image.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ input_image.unbind_as_texture();
+ factor.unbind_as_texture();
+ output_image.unbind_as_image();
+ GPU_shader_unbind();
+ }
+
+ CMPNodeFilterMethod get_filter_method()
+ {
+ return (CMPNodeFilterMethod)bnode().custom1;
+ }
+
+ float3x3 get_filter_kernel()
+ {
+ /* Initialize the kernels as arrays of rows with the top row first. Edge detection kernels
+ * return the kernel in the X direction, while the kernel in the Y direction will be computed
+ * inside the shader by transposing the kernel in the X direction. */
+ switch (get_filter_method()) {
+ case CMP_NODE_FILTER_SOFT: {
+ const float kernel[3][3] = {{1.0f / 16.0f, 2.0f / 16.0f, 1.0f / 16.0f},
+ {2.0f / 16.0f, 4.0f / 16.0f, 2.0f / 16.0f},
+ {1.0f / 16.0f, 2.0f / 16.0f, 1.0f / 16.0f}};
+ return float3x3(kernel);
+ }
+ case CMP_NODE_FILTER_SHARP_BOX: {
+ const float kernel[3][3] = {
+ {-1.0f, -1.0f, -1.0f}, {-1.0f, 9.0f, -1.0f}, {-1.0f, -1.0f, -1.0f}};
+ return float3x3(kernel);
+ }
+ case CMP_NODE_FILTER_LAPLACE: {
+ const float kernel[3][3] = {{-1.0f / 8.0f, -1.0f / 8.0f, -1.0f / 8.0f},
+ {-1.0f / 8.0f, 1.0f, -1.0f / 8.0f},
+ {-1.0f / 8.0f, -1.0f / 8.0f, -1.0f / 8.0f}};
+ return float3x3(kernel);
+ }
+ case CMP_NODE_FILTER_SOBEL: {
+ const float kernel[3][3] = {{1.0f, 0.0f, -1.0f}, {2.0f, 0.0f, -2.0f}, {1.0f, 0.0f, -1.0f}};
+ return float3x3(kernel);
+ }
+ case CMP_NODE_FILTER_PREWITT: {
+ const float kernel[3][3] = {{1.0f, 0.0f, -1.0f}, {1.0f, 0.0f, -1.0f}, {1.0f, 0.0f, -1.0f}};
+ return float3x3(kernel);
+ }
+ case CMP_NODE_FILTER_KIRSCH: {
+ const float kernel[3][3] = {
+ {5.0f, -3.0f, -2.0f}, {5.0f, -3.0f, -2.0f}, {5.0f, -3.0f, -2.0f}};
+ return float3x3(kernel);
+ }
+ case CMP_NODE_FILTER_SHADOW: {
+ const float kernel[3][3] = {{1.0f, 2.0f, 1.0f}, {0.0f, 1.0f, 0.0f}, {-1.0f, -2.0f, -1.0f}};
+ return float3x3(kernel);
+ }
+ case CMP_NODE_FILTER_SHARP_DIAMOND: {
+ const float kernel[3][3] = {
+ {0.0f, -1.0f, 0.0f}, {-1.0f, 5.0f, -1.0f}, {0.0f, -1.0f, 0.0f}};
+ return float3x3(kernel);
+ }
+ default: {
+ const float kernel[3][3] = {{0.0f, 0.0f, 0.0f}, {0.0f, 1.0f, 0.0f}, {0.0f, 0.0f, 0.0f}};
+ return float3x3(kernel);
+ }
+ }
+ }
+
+ const char *get_shader_name()
+ {
+ switch (get_filter_method()) {
+ case CMP_NODE_FILTER_LAPLACE:
+ case CMP_NODE_FILTER_SOBEL:
+ case CMP_NODE_FILTER_PREWITT:
+ case CMP_NODE_FILTER_KIRSCH:
+ return "compositor_edge_filter";
+ case CMP_NODE_FILTER_SOFT:
+ case CMP_NODE_FILTER_SHARP_BOX:
+ case CMP_NODE_FILTER_SHADOW:
+ case CMP_NODE_FILTER_SHARP_DIAMOND:
+ default:
+ return "compositor_filter";
+ }
}
};
diff --git a/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc b/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc
index a84420231aa..6333860a19b 100644
--- a/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc
@@ -59,7 +59,7 @@ class HueCorrectShaderNode : public ShaderNode {
GPUNodeStack *inputs = get_inputs_array();
GPUNodeStack *outputs = get_outputs_array();
- CurveMapping *curve_mapping = get_curve_mapping();
+ CurveMapping *curve_mapping = const_cast<CurveMapping *>(get_curve_mapping());
BKE_curvemapping_init(curve_mapping);
float *band_values;
@@ -84,9 +84,9 @@ class HueCorrectShaderNode : public ShaderNode {
GPU_uniform(range_dividers));
}
- CurveMapping *get_curve_mapping()
+ const CurveMapping *get_curve_mapping()
{
- return static_cast<CurveMapping *>(bnode().storage);
+ return static_cast<const CurveMapping *>(bnode().storage);
}
};
diff --git a/source/blender/nodes/composite/nodes/node_composite_image.cc b/source/blender/nodes/composite/nodes/node_composite_image.cc
index d8852e9333f..4d1eff0b940 100644
--- a/source/blender/nodes/composite/nodes/node_composite_image.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_image.cc
@@ -457,8 +457,8 @@ class ImageOperation : public NodeOperation {
update_image_frame_number();
- for (const OutputSocketRef *output : node()->outputs()) {
- compute_output(output->identifier());
+ for (const bNodeSocket *output : this->node()->output_sockets()) {
+ compute_output(output->identifier);
}
}
@@ -488,12 +488,12 @@ class ImageOperation : public NodeOperation {
/* Allocate all needed outputs as invalid. This should be called when is_valid returns false. */
void allocate_invalid()
{
- for (const OutputSocketRef *output : node()->outputs()) {
- if (!should_compute_output(output->identifier())) {
+ for (const bNodeSocket *output : this->node()->output_sockets()) {
+ if (!should_compute_output(output->identifier)) {
continue;
}
- Result &result = get_result(output->identifier());
+ Result &result = get_result(output->identifier);
result.allocate_invalid();
}
}
@@ -535,7 +535,7 @@ class ImageOperation : public NodeOperation {
/* Get a copy of the image user that is appropriate to retrieve the image buffer for the output
* with the given identifier. This essentially sets the appropriate pass and view indices that
- * corresponds to the output. */
+ * corresponds to the output. */
ImageUser compute_image_user_for_output(StringRef identifier)
{
ImageUser image_user = *get_image_user();
@@ -594,7 +594,7 @@ class ImageOperation : public NodeOperation {
const char *get_pass_name(StringRef identifier)
{
DOutputSocket output = node().output_by_identifier(identifier);
- return static_cast<NodeImageLayer *>(output->bsocket()->storage)->pass_name;
+ return static_cast<NodeImageLayer *>(output->storage)->pass_name;
}
/* Get the index of the pass with the given name in the selected render layer's passes list
@@ -850,9 +850,9 @@ class RenderLayerOperation : public NodeOperation {
alpha_result.unbind_as_image();
/* Other output passes are not supported for now, so allocate them as invalid. */
- for (const OutputSocketRef *output : node()->outputs()) {
- if (output->identifier() != "Image" && output->identifier() != "Alpha") {
- get_result(output->identifier()).allocate_invalid();
+ for (const bNodeSocket *output : this->node()->output_sockets()) {
+ if (!STREQ(output->identifier, "Image") && !STREQ(output->identifier, "Alpha")) {
+ get_result(output->identifier).allocate_invalid();
}
}
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_lensdist.cc b/source/blender/nodes/composite/nodes/node_composite_lensdist.cc
index 2d4c0afcda7..260fccf66d0 100644
--- a/source/blender/nodes/composite/nodes/node_composite_lensdist.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_lensdist.cc
@@ -32,6 +32,8 @@
namespace blender::nodes::node_composite_lensdist_cc {
+NODE_STORAGE_FUNCS(NodeLensDist)
+
static void cmp_node_lensdist_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Color>(N_("Image"))
@@ -197,22 +199,17 @@ class LensDistortionOperation : public NodeOperation {
bool get_is_projector()
{
- return get_node_lens_distortion().proj;
+ return node_storage(bnode()).proj;
}
bool get_is_jitter()
{
- return get_node_lens_distortion().jit;
+ return node_storage(bnode()).jit;
}
bool get_is_fit()
{
- return get_node_lens_distortion().fit;
- }
-
- NodeLensDist &get_node_lens_distortion()
- {
- return *static_cast<NodeLensDist *>(bnode().storage);
+ return node_storage(bnode()).fit;
}
/* Returns true if the operation does nothing and the input can be passed through. */
diff --git a/source/blender/nodes/composite/nodes/node_composite_luma_matte.cc b/source/blender/nodes/composite/nodes/node_composite_luma_matte.cc
index 092a12a7ea4..59ae62ec411 100644
--- a/source/blender/nodes/composite/nodes/node_composite_luma_matte.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_luma_matte.cc
@@ -20,6 +20,8 @@
namespace blender::nodes::node_composite_luma_matte_cc {
+NODE_STORAGE_FUNCS(NodeChroma)
+
static void cmp_node_luma_matte_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Color>(N_("Image"))
@@ -74,19 +76,14 @@ class LuminanceMatteShaderNode : public ShaderNode {
GPU_constant(luminance_coefficients));
}
- NodeChroma *get_node_chroma()
- {
- return static_cast<NodeChroma *>(bnode().storage);
- }
-
float get_high()
{
- return get_node_chroma()->t1;
+ return node_storage(bnode()).t1;
}
float get_low()
{
- return get_node_chroma()->t2;
+ return node_storage(bnode()).t2;
}
};
diff --git a/source/blender/nodes/composite/nodes/node_composite_map_value.cc b/source/blender/nodes/composite/nodes/node_composite_map_value.cc
index ec9b2d56636..e30de39605d 100644
--- a/source/blender/nodes/composite/nodes/node_composite_map_value.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_map_value.cc
@@ -22,6 +22,8 @@
namespace blender::nodes::node_composite_map_value_cc {
+NODE_STORAGE_FUNCS(TexMapping)
+
static void cmp_node_map_value_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Float>(N_("Value"))
@@ -69,7 +71,7 @@ class MapValueShaderNode : public ShaderNode {
GPUNodeStack *inputs = get_inputs_array();
GPUNodeStack *outputs = get_outputs_array();
- const TexMapping *texture_mapping = get_texture_mapping();
+ const TexMapping &texture_mapping = node_storage(bnode());
const float use_min = get_use_min();
const float use_max = get_use_max();
@@ -79,27 +81,22 @@ class MapValueShaderNode : public ShaderNode {
"node_composite_map_value",
inputs,
outputs,
- GPU_uniform(texture_mapping->loc),
- GPU_uniform(texture_mapping->size),
+ GPU_uniform(texture_mapping.loc),
+ GPU_uniform(texture_mapping.size),
GPU_constant(&use_min),
- GPU_uniform(texture_mapping->min),
+ GPU_uniform(texture_mapping.min),
GPU_constant(&use_max),
- GPU_uniform(texture_mapping->max));
- }
-
- TexMapping *get_texture_mapping()
- {
- return static_cast<TexMapping *>(bnode().storage);
+ GPU_uniform(texture_mapping.max));
}
bool get_use_min()
{
- return get_texture_mapping()->flag & TEXMAP_CLIP_MIN;
+ return node_storage(bnode()).flag & TEXMAP_CLIP_MIN;
}
bool get_use_max()
{
- return get_texture_mapping()->flag & TEXMAP_CLIP_MAX;
+ return node_storage(bnode()).flag & TEXMAP_CLIP_MAX;
}
};
diff --git a/source/blender/nodes/composite/nodes/node_composite_movieclip.cc b/source/blender/nodes/composite/nodes/node_composite_movieclip.cc
index ec95de3da18..b9d9620a214 100644
--- a/source/blender/nodes/composite/nodes/node_composite_movieclip.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_movieclip.cc
@@ -239,7 +239,7 @@ class MovieClipOperation : public NodeOperation {
GPUTexture *get_movie_clip_texture()
{
MovieClip *movie_clip = get_movie_clip();
- MovieClipUser *movie_clip_user = static_cast<MovieClipUser *>(bnode().storage);
+ MovieClipUser *movie_clip_user = get_movie_clip_user();
BKE_movieclip_user_set_frame(movie_clip_user, context().get_frame_number());
return BKE_movieclip_get_gpu_texture(movie_clip, movie_clip_user);
}
@@ -247,13 +247,20 @@ class MovieClipOperation : public NodeOperation {
void free_movie_clip_texture()
{
MovieClip *movie_clip = get_movie_clip();
- return BKE_movieclip_free_gputexture(movie_clip);
+ if (movie_clip) {
+ BKE_movieclip_free_gputexture(movie_clip);
+ }
}
MovieClip *get_movie_clip()
{
return (MovieClip *)bnode().id;
}
+
+ MovieClipUser *get_movie_clip_user()
+ {
+ return static_cast<MovieClipUser *>(bnode().storage);
+ }
};
static NodeOperation *get_compositor_operation(Context &context, DNode node)
diff --git a/source/blender/nodes/composite/nodes/node_composite_normal.cc b/source/blender/nodes/composite/nodes/node_composite_normal.cc
index f61ace01cfd..a1a6303e21b 100644
--- a/source/blender/nodes/composite/nodes/node_composite_normal.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_normal.cc
@@ -51,9 +51,12 @@ class NormalShaderNode : public ShaderNode {
}
/* The vector value is stored in the default value of the output socket. */
- float *get_vector_value()
+ const float *get_vector_value()
{
- return node().output_by_identifier("Normal")->default_value<bNodeSocketValueVector>()->value;
+ return node()
+ .output_by_identifier("Normal")
+ ->default_value_typed<bNodeSocketValueVector>()
+ ->value;
}
};
diff --git a/source/blender/nodes/composite/nodes/node_composite_pixelate.cc b/source/blender/nodes/composite/nodes/node_composite_pixelate.cc
index 4567464a547..c4e42f8247d 100644
--- a/source/blender/nodes/composite/nodes/node_composite_pixelate.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_pixelate.cc
@@ -5,6 +5,9 @@
* \ingroup cmpnodes
*/
+#include "BLI_math_vec_types.hh"
+#include "BLI_math_vector.hh"
+
#include "COM_node_operation.hh"
#include "node_composite_util.hh"
@@ -27,8 +30,34 @@ class PixelateOperation : public NodeOperation {
void execute() override
{
+ /* It might seems strange that the input is passed through without any processing, but note
+ * that the actual processing happens inside the domain realization input processor of the
+ * input. Indeed, the pixelate node merely realizes its input on a smaller-sized domain that
+ * matches its apparent size, that is, its size after the domain transformation. The pixelate
+ * node has no effect if the input is scaled-up. See the compute_domain method for more
+ * information. */
get_input("Color").pass_through(get_result("Color"));
}
+
+ /* Compute a smaller-sized domain that matches the apparent size of the input while having a unit
+ * scale transformation, see the execute method for more information. */
+ Domain compute_domain() override
+ {
+ Domain domain = get_input("Color").domain();
+
+ /* Get the scaling component of the domain transformation, but make sure it doesn't exceed 1,
+ * because pixelation should only happen if the input is scaled down. */
+ const float2 scale = math::min(float2(1.0f), domain.transformation.scale_2d());
+
+ /* Multiply the size of the domain by its scale to match its apparent size, but make sure it is
+ * at least 1 pixel in both axis. */
+ domain.size = math::max(int2(float2(domain.size) * scale), int2(1));
+
+ /* Reset the scale of the transformation by transforming it with the inverse of the scale. */
+ domain.transformation *= float3x3::from_scale(math::safe_divide(float2(1.0f), scale));
+
+ return domain;
+ }
};
static NodeOperation *get_compositor_operation(Context &context, DNode node)
diff --git a/source/blender/nodes/composite/nodes/node_composite_rgb.cc b/source/blender/nodes/composite/nodes/node_composite_rgb.cc
index 6f3a00af7e3..f107961f301 100644
--- a/source/blender/nodes/composite/nodes/node_composite_rgb.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_rgb.cc
@@ -33,8 +33,8 @@ class RGBOperation : public NodeOperation {
Result &result = get_result("RGBA");
result.allocate_single_value();
- const bNodeSocket *socket = static_cast<bNodeSocket *>(bnode().outputs.first);
- float4 color = float4(static_cast<bNodeSocketValueRGBA *>(socket->default_value)->value);
+ const bNodeSocket *socket = static_cast<const bNodeSocket *>(bnode().outputs.first);
+ float4 color = float4(static_cast<const bNodeSocketValueRGBA *>(socket->default_value)->value);
result.set_color_value(color);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_scale.cc b/source/blender/nodes/composite/nodes/node_composite_scale.cc
index 8b43ae8c9ca..eb2d7162c69 100644
--- a/source/blender/nodes/composite/nodes/node_composite_scale.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_scale.cc
@@ -5,6 +5,11 @@
* \ingroup cmpnodes
*/
+#include "BLI_assert.h"
+#include "BLI_float3x3.hh"
+#include "BLI_math_base.hh"
+#include "BLI_math_vec_types.hh"
+
#include "RNA_access.h"
#include "UI_interface.h"
@@ -20,16 +25,26 @@ 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_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("X"))
+ .default_value(1.0f)
+ .min(0.0001f)
+ .max(CMP_SCALE_MAX)
+ .compositor_expects_single_value();
+ b.add_input<decl::Float>(N_("Y"))
+ .default_value(1.0f)
+ .min(0.0001f)
+ .max(CMP_SCALE_MAX)
+ .compositor_expects_single_value();
b.add_output<decl::Color>(N_("Image"));
}
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);
+ bool use_xy_scale = ELEM(node->custom1, CMP_NODE_SCALE_RELATIVE, CMP_NODE_SCALE_ABSOLUTE);
/* Only show X/Y scale factor inputs for modes using them! */
for (sock = (bNodeSocket *)node->inputs.first; sock; sock = sock->next) {
@@ -43,7 +58,7 @@ static void node_composit_buts_scale(uiLayout *layout, bContext *UNUSED(C), Poin
{
uiItemR(layout, ptr, "space", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
- if (RNA_enum_get(ptr, "space") == CMP_SCALE_RENDERPERCENT) {
+ if (RNA_enum_get(ptr, "space") == CMP_NODE_SCALE_RENDER_SIZE) {
uiLayout *row;
uiItemR(layout,
ptr,
@@ -65,7 +80,129 @@ class ScaleOperation : public NodeOperation {
void execute() override
{
- get_input("Image").pass_through(get_result("Image"));
+ Result &input = get_input("Image");
+ Result &result = get_result("Image");
+ input.pass_through(result);
+
+ const float3x3 transformation = float3x3::from_translation_rotation_scale(
+ get_translation(), 0.0f, get_scale());
+
+ result.transform(transformation);
+ result.get_realization_options().interpolation = Interpolation::Bilinear;
+ }
+
+ float2 get_scale()
+ {
+ switch (get_scale_method()) {
+ case CMP_NODE_SCALE_RELATIVE:
+ return get_scale_relative();
+ case CMP_NODE_SCALE_ABSOLUTE:
+ return get_scale_absolute();
+ case CMP_NODE_SCALE_RENDER_PERCENT:
+ return get_scale_render_percent();
+ case CMP_NODE_SCALE_RENDER_SIZE:
+ return get_scale_render_size();
+ default:
+ BLI_assert_unreachable();
+ return float2(1.0f);
+ }
+ }
+
+ /* Scale by the input factors. */
+ float2 get_scale_relative()
+ {
+ return float2(get_input("X").get_float_value_default(1.0f),
+ get_input("Y").get_float_value_default(1.0f));
+ }
+
+ /* Scale such that the new size matches the input absolute size. */
+ float2 get_scale_absolute()
+ {
+ const float2 input_size = float2(get_input("Image").domain().size);
+ const float2 absolute_size = float2(get_input("X").get_float_value_default(1.0f),
+ get_input("Y").get_float_value_default(1.0f));
+ return absolute_size / input_size;
+ }
+
+ /* Scale by the render resolution percentage. */
+ float2 get_scale_render_percent()
+ {
+ return float2(context().get_scene()->r.size / 100.0f);
+ }
+
+ float2 get_scale_render_size()
+ {
+ switch (get_scale_render_size_method()) {
+ case CMP_NODE_SCALE_RENDER_SIZE_STRETCH:
+ return get_scale_render_size_stretch();
+ case CMP_NODE_SCALE_RENDER_SIZE_FIT:
+ return get_scale_render_size_fit();
+ case CMP_NODE_SCALE_RENDER_SIZE_CROP:
+ return get_scale_render_size_crop();
+ default:
+ BLI_assert_unreachable();
+ return float2(1.0f);
+ }
+ }
+
+ /* Scale such that the new size matches the render size. Since the input is freely scaled, it is
+ * potentially stretched, hence the name. */
+ float2 get_scale_render_size_stretch()
+ {
+ const float2 input_size = float2(get_input("Image").domain().size);
+ const float2 render_size = float2(context().get_output_size());
+ return render_size / input_size;
+ }
+
+ /* Scale such that the dimension with the smaller scaling factor matches that of the render size
+ * while maintaining the input's aspect ratio. Since the other dimension is guaranteed not to
+ * exceed the render size region due to its larger scaling factor, the image is said to be fit
+ * inside that region, hence the name. */
+ float2 get_scale_render_size_fit()
+ {
+ const float2 input_size = float2(get_input("Image").domain().size);
+ const float2 render_size = float2(context().get_output_size());
+ const float2 scale = render_size / input_size;
+ return float2(math::min(scale.x, scale.y));
+ }
+
+ /* Scale such that the dimension with the larger scaling factor matches that of the render size
+ * while maintaining the input's aspect ratio. Since the other dimension is guaranteed to exceed
+ * the render size region due to its lower scaling factor, the image will be cropped inside that
+ * region, hence the name. */
+ float2 get_scale_render_size_crop()
+ {
+ const float2 input_size = float2(get_input("Image").domain().size);
+ const float2 render_size = float2(context().get_output_size());
+ const float2 scale = render_size / input_size;
+ return float2(math::max(scale.x, scale.y));
+ }
+
+ float2 get_translation()
+ {
+ /* Only the render size option supports offset translation. */
+ if (get_scale_method() != CMP_NODE_SCALE_RENDER_SIZE) {
+ return float2(0.0f);
+ }
+
+ /* Translate by the offset factor relative to the new size. */
+ const float2 input_size = float2(get_input("Image").domain().size);
+ return get_offset() * input_size * get_scale();
+ }
+
+ CMPNodeScaleMethod get_scale_method()
+ {
+ return (CMPNodeScaleMethod)bnode().custom1;
+ }
+
+ CMPNodeScaleRenderSizeMethod get_scale_render_size_method()
+ {
+ return (CMPNodeScaleRenderSizeMethod)bnode().custom2;
+ }
+
+ float2 get_offset()
+ {
+ return float2(bnode().custom3, bnode().custom4);
}
};
diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_color.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_color.cc
index d1f0b7977f8..f6792d7ce61 100644
--- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_color.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_color.cc
@@ -62,6 +62,8 @@ static void node_cmp_combsep_color_label(const ListBase *sockets, CMPNodeCombSep
namespace blender::nodes::node_composite_separate_color_cc {
+NODE_STORAGE_FUNCS(NodeCMPCombSepColor)
+
static void cmp_node_separate_color_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Color>(N_("Image"))
@@ -93,14 +95,9 @@ class SeparateColorShaderNode : public ShaderNode {
GPU_stack_link(material, &bnode(), get_shader_function_name(), inputs, outputs);
}
- NodeCMPCombSepColor *get_node_combine_separate_color()
- {
- return static_cast<NodeCMPCombSepColor *>(bnode().storage);
- }
-
const char *get_shader_function_name()
{
- switch (get_node_combine_separate_color()->mode) {
+ switch (node_storage(bnode()).mode) {
case CMP_NODE_COMBSEP_COLOR_RGB:
return "node_composite_separate_rgba";
case CMP_NODE_COMBSEP_COLOR_HSV:
@@ -110,7 +107,7 @@ class SeparateColorShaderNode : public ShaderNode {
case CMP_NODE_COMBSEP_COLOR_YUV:
return "node_composite_separate_yuva_itu_709";
case CMP_NODE_COMBSEP_COLOR_YCC:
- switch (get_node_combine_separate_color()->ycc_mode) {
+ switch (node_storage(bnode()).ycc_mode) {
case BLI_YCC_ITU_BT601:
return "node_composite_separate_ycca_itu_601";
case BLI_YCC_ITU_BT709:
@@ -153,6 +150,8 @@ void register_node_type_cmp_separate_color()
namespace blender::nodes::node_composite_combine_color_cc {
+NODE_STORAGE_FUNCS(NodeCMPCombSepColor)
+
static void cmp_node_combine_color_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Float>(N_("Red"))
@@ -202,14 +201,9 @@ class CombineColorShaderNode : public ShaderNode {
GPU_stack_link(material, &bnode(), get_shader_function_name(), inputs, outputs);
}
- NodeCMPCombSepColor *get_node_combine_separate_color()
- {
- return static_cast<NodeCMPCombSepColor *>(bnode().storage);
- }
-
const char *get_shader_function_name()
{
- switch (get_node_combine_separate_color()->mode) {
+ switch (node_storage(bnode()).mode) {
case CMP_NODE_COMBSEP_COLOR_RGB:
return "node_composite_combine_rgba";
case CMP_NODE_COMBSEP_COLOR_HSV:
@@ -219,7 +213,7 @@ class CombineColorShaderNode : public ShaderNode {
case CMP_NODE_COMBSEP_COLOR_YUV:
return "node_composite_combine_yuva_itu_709";
case CMP_NODE_COMBSEP_COLOR_YCC:
- switch (get_node_combine_separate_color()->ycc_mode) {
+ switch (node_storage(bnode()).ycc_mode) {
case BLI_YCC_ITU_BT601:
return "node_composite_combine_ycca_itu_601";
case BLI_YCC_ITU_BT709:
diff --git a/source/blender/nodes/composite/nodes/node_composite_setalpha.cc b/source/blender/nodes/composite/nodes/node_composite_setalpha.cc
index 9930125aa70..df3aca2c665 100644
--- a/source/blender/nodes/composite/nodes/node_composite_setalpha.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_setalpha.cc
@@ -18,6 +18,8 @@
namespace blender::nodes::node_composite_setalpha_cc {
+NODE_STORAGE_FUNCS(NodeSetAlpha)
+
static void cmp_node_setalpha_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Color>(N_("Image"))
@@ -54,18 +56,13 @@ class SetAlphaShaderNode : public ShaderNode {
GPUNodeStack *inputs = get_inputs_array();
GPUNodeStack *outputs = get_outputs_array();
- if (get_node_set_alpha()->mode == CMP_NODE_SETALPHA_MODE_APPLY) {
+ if (node_storage(bnode()).mode == CMP_NODE_SETALPHA_MODE_APPLY) {
GPU_stack_link(material, &bnode(), "node_composite_set_alpha_apply", inputs, outputs);
return;
}
GPU_stack_link(material, &bnode(), "node_composite_set_alpha_replace", inputs, outputs);
}
-
- NodeSetAlpha *get_node_set_alpha()
- {
- return static_cast<NodeSetAlpha *>(bnode().storage);
- }
};
static ShaderNode *get_compositor_shader_node(DNode node)
diff --git a/source/blender/nodes/composite/nodes/node_composite_translate.cc b/source/blender/nodes/composite/nodes/node_composite_translate.cc
index fbd53b8310f..e0f87ff604a 100644
--- a/source/blender/nodes/composite/nodes/node_composite_translate.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_translate.cc
@@ -19,6 +19,8 @@
namespace blender::nodes::node_composite_translate_cc {
+NODE_STORAGE_FUNCS(NodeTranslateData)
+
static void cmp_node_translate_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Color>(N_("Image"))
@@ -76,24 +78,19 @@ class TranslateOperation : public NodeOperation {
result.get_realization_options().repeat_y = get_repeat_y();
}
- NodeTranslateData &get_node_translate()
- {
- return *static_cast<NodeTranslateData *>(bnode().storage);
- }
-
bool get_use_relative()
{
- return get_node_translate().relative;
+ return node_storage(bnode()).relative;
}
bool get_repeat_x()
{
- return ELEM(get_node_translate().wrap_axis, CMP_NODE_WRAP_X, CMP_NODE_WRAP_XY);
+ return ELEM(node_storage(bnode()).wrap_axis, CMP_NODE_WRAP_X, CMP_NODE_WRAP_XY);
}
bool get_repeat_y()
{
- return ELEM(get_node_translate().wrap_axis, CMP_NODE_WRAP_Y, CMP_NODE_WRAP_XY);
+ return ELEM(node_storage(bnode()).wrap_axis, CMP_NODE_WRAP_Y, CMP_NODE_WRAP_XY);
}
};
diff --git a/source/blender/nodes/function/node_function_util.hh b/source/blender/nodes/function/node_function_util.hh
index fd0b6c31b1d..059b2f9bc17 100644
--- a/source/blender/nodes/function/node_function_util.hh
+++ b/source/blender/nodes/function/node_function_util.hh
@@ -23,4 +23,6 @@
#include "FN_multi_function_builder.hh"
+#include "RNA_access.h"
+
void fn_node_type_base(struct bNodeType *ntype, int type, const char *name, short nclass);
diff --git a/source/blender/nodes/function/nodes/node_fn_align_euler_to_vector.cc b/source/blender/nodes/function/nodes/node_fn_align_euler_to_vector.cc
index 7d08d57c503..e5c89567d44 100644
--- a/source/blender/nodes/function/nodes/node_fn_align_euler_to_vector.cc
+++ b/source/blender/nodes/function/nodes/node_fn_align_euler_to_vector.cc
@@ -190,7 +190,7 @@ class MF_AlignEulerToVector : public fn::MultiFunction {
static void fn_node_align_euler_to_vector_build_multi_function(NodeMultiFunctionBuilder &builder)
{
- bNode &node = builder.node();
+ const bNode &node = builder.node();
builder.construct_and_set_matching_fn<MF_AlignEulerToVector>(node.custom1, node.custom2);
}
diff --git a/source/blender/nodes/function/nodes/node_fn_boolean_math.cc b/source/blender/nodes/function/nodes/node_fn_boolean_math.cc
index b6d7e6c9a5f..5fc28509a49 100644
--- a/source/blender/nodes/function/nodes/node_fn_boolean_math.cc
+++ b/source/blender/nodes/function/nodes/node_fn_boolean_math.cc
@@ -68,7 +68,7 @@ static void node_gather_link_searches(GatherLinkSearchOpParams &params)
}
}
-static const fn::MultiFunction *get_multi_function(bNode &bnode)
+static const fn::MultiFunction *get_multi_function(const bNode &bnode)
{
static auto exec_preset = fn::CustomMF_presets::AllSpanOrSingle();
static fn::CustomMF_SI_SI_SO<bool, bool, bool> and_fn{
diff --git a/source/blender/nodes/function/nodes/node_fn_combine_color.cc b/source/blender/nodes/function/nodes/node_fn_combine_color.cc
index c5fd3ce38a1..450cd166e78 100644
--- a/source/blender/nodes/function/nodes/node_fn_combine_color.cc
+++ b/source/blender/nodes/function/nodes/node_fn_combine_color.cc
@@ -49,7 +49,7 @@ static void fn_node_combine_color_init(bNodeTree *UNUSED(tree), bNode *node)
node->storage = data;
}
-static const fn::MultiFunction *get_multi_function(bNode &bnode)
+static const fn::MultiFunction *get_multi_function(const bNode &bnode)
{
const NodeCombSepColor &storage = node_storage(bnode);
diff --git a/source/blender/nodes/function/nodes/node_fn_compare.cc b/source/blender/nodes/function/nodes/node_fn_compare.cc
index e3f13dc7d6b..122d1a3c93e 100644
--- a/source/blender/nodes/function/nodes/node_fn_compare.cc
+++ b/source/blender/nodes/function/nodes/node_fn_compare.cc
@@ -167,7 +167,7 @@ static float component_average(float3 a)
return (a.x + a.y + a.z) / 3.0f;
}
-static const fn::MultiFunction *get_multi_function(bNode &node)
+static const fn::MultiFunction *get_multi_function(const bNode &node)
{
const NodeFunctionCompare *data = (NodeFunctionCompare *)node.storage;
diff --git a/source/blender/nodes/function/nodes/node_fn_float_to_int.cc b/source/blender/nodes/function/nodes/node_fn_float_to_int.cc
index 9c9d8620a7e..aad2f532d20 100644
--- a/source/blender/nodes/function/nodes/node_fn_float_to_int.cc
+++ b/source/blender/nodes/function/nodes/node_fn_float_to_int.cc
@@ -39,7 +39,7 @@ static void node_float_to_int_label(const bNodeTree *UNUSED(ntree),
BLI_strncpy(label, IFACE_(name), maxlen);
}
-static const fn::MultiFunction *get_multi_function(bNode &bnode)
+static const fn::MultiFunction *get_multi_function(const bNode &bnode)
{
static auto exec_preset = fn::CustomMF_presets::AllSpanOrSingle();
static fn::CustomMF_SI_SO<float, int> round_fn{
diff --git a/source/blender/nodes/function/nodes/node_fn_input_bool.cc b/source/blender/nodes/function/nodes/node_fn_input_bool.cc
index 5ced719627f..717f4d1ac6b 100644
--- a/source/blender/nodes/function/nodes/node_fn_input_bool.cc
+++ b/source/blender/nodes/function/nodes/node_fn_input_bool.cc
@@ -22,7 +22,7 @@ static void fn_node_input_bool_layout(uiLayout *layout, bContext *UNUSED(C), Poi
static void fn_node_input_bool_build_multi_function(NodeMultiFunctionBuilder &builder)
{
- bNode &bnode = builder.node();
+ const bNode &bnode = builder.node();
NodeInputBool *node_storage = static_cast<NodeInputBool *>(bnode.storage);
builder.construct_and_set_matching_fn<fn::CustomMF_Constant<bool>>(node_storage->boolean);
}
diff --git a/source/blender/nodes/function/nodes/node_fn_input_color.cc b/source/blender/nodes/function/nodes/node_fn_input_color.cc
index 46787f7575d..cdad1542c66 100644
--- a/source/blender/nodes/function/nodes/node_fn_input_color.cc
+++ b/source/blender/nodes/function/nodes/node_fn_input_color.cc
@@ -23,7 +23,7 @@ static void fn_node_input_color_layout(uiLayout *layout, bContext *UNUSED(C), Po
static void fn_node_input_color_build_multi_function(
blender::nodes::NodeMultiFunctionBuilder &builder)
{
- bNode &bnode = builder.node();
+ const bNode &bnode = builder.node();
NodeInputColor *node_storage = static_cast<NodeInputColor *>(bnode.storage);
blender::ColorGeometry4f color = (ColorGeometry4f)node_storage->color;
builder.construct_and_set_matching_fn<blender::fn::CustomMF_Constant<ColorGeometry4f>>(color);
diff --git a/source/blender/nodes/function/nodes/node_fn_input_int.cc b/source/blender/nodes/function/nodes/node_fn_input_int.cc
index 1e5dcd5ae7a..16506b5f9b8 100644
--- a/source/blender/nodes/function/nodes/node_fn_input_int.cc
+++ b/source/blender/nodes/function/nodes/node_fn_input_int.cc
@@ -22,7 +22,7 @@ static void fn_node_input_int_layout(uiLayout *layout, bContext *UNUSED(C), Poin
static void fn_node_input_int_build_multi_function(NodeMultiFunctionBuilder &builder)
{
- bNode &bnode = builder.node();
+ const bNode &bnode = builder.node();
NodeInputInt *node_storage = static_cast<NodeInputInt *>(bnode.storage);
builder.construct_and_set_matching_fn<fn::CustomMF_Constant<int>>(node_storage->integer);
}
diff --git a/source/blender/nodes/function/nodes/node_fn_input_string.cc b/source/blender/nodes/function/nodes/node_fn_input_string.cc
index 124a8572f78..129d19f4f04 100644
--- a/source/blender/nodes/function/nodes/node_fn_input_string.cc
+++ b/source/blender/nodes/function/nodes/node_fn_input_string.cc
@@ -20,7 +20,7 @@ static void fn_node_input_string_layout(uiLayout *layout, bContext *UNUSED(C), P
static void fn_node_input_string_build_multi_function(NodeMultiFunctionBuilder &builder)
{
- bNode &bnode = builder.node();
+ const bNode &bnode = builder.node();
NodeInputString *node_storage = static_cast<NodeInputString *>(bnode.storage);
std::string string = std::string((node_storage->string) ? node_storage->string : "");
builder.construct_and_set_matching_fn<fn::CustomMF_Constant<std::string>>(std::move(string));
diff --git a/source/blender/nodes/function/nodes/node_fn_input_vector.cc b/source/blender/nodes/function/nodes/node_fn_input_vector.cc
index 898c19e92f0..de894a4038d 100644
--- a/source/blender/nodes/function/nodes/node_fn_input_vector.cc
+++ b/source/blender/nodes/function/nodes/node_fn_input_vector.cc
@@ -22,7 +22,7 @@ static void fn_node_input_vector_layout(uiLayout *layout, bContext *UNUSED(C), P
static void fn_node_input_vector_build_multi_function(NodeMultiFunctionBuilder &builder)
{
- bNode &bnode = builder.node();
+ const bNode &bnode = builder.node();
NodeInputVector *node_storage = static_cast<NodeInputVector *>(bnode.storage);
float3 vector(node_storage->vector);
builder.construct_and_set_matching_fn<fn::CustomMF_Constant<float3>>(vector);
diff --git a/source/blender/nodes/function/nodes/node_fn_rotate_euler.cc b/source/blender/nodes/function/nodes/node_fn_rotate_euler.cc
index a4fc1a6bfd1..299c0f7a932 100644
--- a/source/blender/nodes/function/nodes/node_fn_rotate_euler.cc
+++ b/source/blender/nodes/function/nodes/node_fn_rotate_euler.cc
@@ -52,7 +52,7 @@ static void fn_node_rotate_euler_layout(uiLayout *layout, bContext *UNUSED(C), P
uiItemR(layout, ptr, "space", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
}
-static const fn::MultiFunction *get_multi_function(bNode &bnode)
+static const fn::MultiFunction *get_multi_function(const bNode &bnode)
{
static fn::CustomMF_SI_SI_SO<float3, float3, float3> obj_euler_rot{
"Rotate Euler by Euler/Object", [](const float3 &input, const float3 &rotation) {
diff --git a/source/blender/nodes/geometry/CMakeLists.txt b/source/blender/nodes/geometry/CMakeLists.txt
index 31c00cc6b82..19a325eca89 100644
--- a/source/blender/nodes/geometry/CMakeLists.txt
+++ b/source/blender/nodes/geometry/CMakeLists.txt
@@ -59,6 +59,7 @@ set(SRC
nodes/node_geo_curve_trim.cc
nodes/node_geo_deform_curves_on_surface.cc
nodes/node_geo_delete_geometry.cc
+ nodes/node_geo_distribute_points_in_volume.cc
nodes/node_geo_distribute_points_on_faces.cc
nodes/node_geo_dual_mesh.cc
nodes/node_geo_duplicate_elements.cc
@@ -105,6 +106,7 @@ set(SRC
nodes/node_geo_material_replace.cc
nodes/node_geo_material_selection.cc
nodes/node_geo_merge_by_distance.cc
+ nodes/node_geo_mesh_face_set_boundaries.cc
nodes/node_geo_mesh_primitive_circle.cc
nodes/node_geo_mesh_primitive_cone.cc
nodes/node_geo_mesh_primitive_cube.cc
@@ -126,10 +128,14 @@ set(SRC
nodes/node_geo_realize_instances.cc
nodes/node_geo_remove_attribute.cc
nodes/node_geo_rotate_instances.cc
+ nodes/node_geo_sample_index.cc
+ nodes/node_geo_sample_nearest_surface.cc
+ nodes/node_geo_sample_nearest.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_self_object.cc
nodes/node_geo_set_curve_handles.cc
nodes/node_geo_set_curve_radius.cc
nodes/node_geo_set_curve_tilt.cc
@@ -146,7 +152,6 @@ set(SRC
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
@@ -187,20 +192,6 @@ if(WITH_BULLET)
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_TBB)
list(APPEND INC_SYS
${TBB_INCLUDE_DIRS}
diff --git a/source/blender/nodes/geometry/node_geometry_exec.cc b/source/blender/nodes/geometry/node_geometry_exec.cc
index 58ded7aadd2..ef4daf94bbe 100644
--- a/source/blender/nodes/geometry/node_geometry_exec.cc
+++ b/source/blender/nodes/geometry/node_geometry_exec.cc
@@ -4,3 +4,4 @@
#include "NOD_geometry_exec.hh"
BLI_CPP_TYPE_MAKE(GeometrySet, GeometrySet, CPPTypeFlags::Printable);
+BLI_CPP_TYPE_MAKE(GeometrySetVector, blender::Vector<GeometrySet>, CPPTypeFlags::None);
diff --git a/source/blender/nodes/geometry/node_geometry_tree.cc b/source/blender/nodes/geometry/node_geometry_tree.cc
index e3998322741..1a12d2c49e6 100644
--- a/source/blender/nodes/geometry/node_geometry_tree.cc
+++ b/source/blender/nodes/geometry/node_geometry_tree.cc
@@ -7,6 +7,7 @@
#include "NOD_geometry.h"
#include "BKE_context.h"
+#include "BKE_layer.h"
#include "BKE_node.h"
#include "BKE_object.h"
@@ -31,8 +32,10 @@ static void geometry_node_tree_get_from_context(const bContext *C,
ID **r_id,
ID **r_from)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
if (ob == nullptr) {
return;
diff --git a/source/blender/nodes/geometry/node_geometry_util.hh b/source/blender/nodes/geometry/node_geometry_util.hh
index efb7efaf1cc..5aeb68b3fdc 100644
--- a/source/blender/nodes/geometry/node_geometry_util.hh
+++ b/source/blender/nodes/geometry/node_geometry_util.hh
@@ -20,8 +20,12 @@
#include "NOD_socket_declarations.hh"
#include "NOD_socket_declarations_geometry.hh"
+#include "RNA_access.h"
+
#include "node_util.h"
+struct BVHTreeFromMesh;
+
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,
@@ -34,7 +38,8 @@ void transform_mesh(Mesh &mesh,
const float3 rotation,
const float3 scale);
-void transform_geometry_set(GeometrySet &geometry,
+void transform_geometry_set(GeoNodeExecParams &params,
+ GeometrySet &geometry,
const float4x4 &transform,
const Depsgraph &depsgraph);
@@ -75,6 +80,13 @@ void separate_geometry(GeometrySet &geometry_set,
const Field<bool> &selection_field,
bool &r_is_error);
+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);
+
std::optional<eCustomDataType> node_data_type_to_custom_data_type(eNodeSocketDatatype type);
std::optional<eCustomDataType> node_socket_to_custom_data_type(const bNodeSocket &socket);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc b/source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc
index 58fbfb5a000..13a9cdc8600 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc
@@ -192,7 +192,7 @@ static void node_gather_link_searches(GatherLinkSearchOpParams &params)
}
}
-template<typename T> class AccumulateFieldInput final : public GeometryFieldInput {
+template<typename T> class AccumulateFieldInput final : public bke::GeometryFieldInput {
private:
Field<T> input_;
Field<int> group_index_;
@@ -204,7 +204,7 @@ template<typename T> class AccumulateFieldInput final : public GeometryFieldInpu
Field<T> input,
Field<int> group_index,
AccumulationMode accumulation_mode)
- : GeometryFieldInput(CPPType::get<T>(), "Accumulation"),
+ : bke::GeometryFieldInput(CPPType::get<T>(), "Accumulation"),
input_(input),
group_index_(group_index),
source_domain_(source_domain),
@@ -212,18 +212,18 @@ template<typename T> class AccumulateFieldInput final : public GeometryFieldInpu
{
}
- GVArray get_varray_for_context(const GeometryComponent &component,
- const eAttrDomain domain,
- IndexMask UNUSED(mask)) const final
+ GVArray get_varray_for_context(const bke::GeometryFieldContext &context,
+ const IndexMask /*mask*/) const final
{
- const GeometryComponentFieldContext field_context{component, source_domain_};
- const int domain_size = component.attribute_domain_size(field_context.domain());
+ const AttributeAccessor attributes = *context.attributes();
+ const int domain_size = attributes.domain_size(source_domain_);
if (domain_size == 0) {
return {};
}
- const AttributeAccessor attributes = *component.attributes();
- fn::FieldEvaluator evaluator{field_context, domain_size};
+ const bke::GeometryFieldContext source_context{
+ context.geometry(), context.type(), source_domain_};
+ fn::FieldEvaluator evaluator{source_context, domain_size};
evaluator.add(input_);
evaluator.add(group_index_);
evaluator.evaluate();
@@ -266,7 +266,7 @@ template<typename T> class AccumulateFieldInput final : public GeometryFieldInpu
}
return attributes.adapt_domain<T>(
- VArray<T>::ForContainer(std::move(accumulations_out)), source_domain_, domain);
+ VArray<T>::ForContainer(std::move(accumulations_out)), source_domain_, context.domain());
}
uint64_t hash() const override
@@ -287,7 +287,7 @@ template<typename T> class AccumulateFieldInput final : public GeometryFieldInpu
}
};
-template<typename T> class TotalFieldInput final : public GeometryFieldInput {
+template<typename T> class TotalFieldInput final : public bke::GeometryFieldInput {
private:
Field<T> input_;
Field<int> group_index_;
@@ -295,25 +295,25 @@ template<typename T> class TotalFieldInput final : public GeometryFieldInput {
public:
TotalFieldInput(const eAttrDomain source_domain, Field<T> input, Field<int> group_index)
- : GeometryFieldInput(CPPType::get<T>(), "Total Value"),
+ : bke::GeometryFieldInput(CPPType::get<T>(), "Total Value"),
input_(input),
group_index_(group_index),
source_domain_(source_domain)
{
}
- GVArray get_varray_for_context(const GeometryComponent &component,
- const eAttrDomain domain,
- IndexMask UNUSED(mask)) const final
+ GVArray get_varray_for_context(const bke::GeometryFieldContext &context,
+ IndexMask /*mask*/) const final
{
- const GeometryComponentFieldContext field_context{component, source_domain_};
- const int domain_size = component.attribute_domain_size(field_context.domain());
+ const AttributeAccessor attributes = *context.attributes();
+ const int domain_size = attributes.domain_size(source_domain_);
if (domain_size == 0) {
return {};
}
- const AttributeAccessor attributes = *component.attributes();
- fn::FieldEvaluator evaluator{field_context, domain_size};
+ const bke::GeometryFieldContext source_context{
+ context.geometry(), context.type(), source_domain_};
+ fn::FieldEvaluator evaluator{source_context, domain_size};
evaluator.add(input_);
evaluator.add(group_index_);
evaluator.evaluate();
@@ -339,7 +339,7 @@ template<typename T> class TotalFieldInput final : public GeometryFieldInput {
}
return attributes.adapt_domain<T>(
- VArray<T>::ForContainer(std::move(accumulations_out)), source_domain_, domain);
+ VArray<T>::ForContainer(std::move(accumulations_out)), source_domain_, context.domain());
}
uint64_t hash() const override
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc
index 9f317075bb5..8c11288efdd 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc
@@ -115,7 +115,7 @@ static void try_capture_field_on_geometry(GeometryComponent &component,
if (domain_size == 0) {
return;
}
- GeometryComponentFieldContext field_context{component, domain};
+ bke::GeometryFieldContext field_context{component, domain};
MutableAttributeAccessor attributes = *component.attributes_for_write();
const IndexMask mask{IndexMask(domain_size)};
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 34e28e50c81..af0007c2fa4 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc
@@ -200,7 +200,7 @@ static void node_geo_exec(GeoNodeExecParams params)
continue;
}
if (attributes->domain_supported(domain)) {
- GeometryComponentFieldContext field_context{*component, domain};
+ bke::GeometryFieldContext field_context{*component, domain};
const int domain_num = attributes->domain_size(domain);
fn::FieldEvaluator data_evaluator{field_context, domain_num};
@@ -282,7 +282,7 @@ static void node_geo_exec(GeoNodeExecParams params)
continue;
}
if (attributes->domain_supported(domain)) {
- GeometryComponentFieldContext field_context{*component, domain};
+ bke::GeometryFieldContext field_context{*component, domain};
const int domain_num = attributes->domain_size(domain);
fn::FieldEvaluator data_evaluator{field_context, domain_num};
diff --git a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc
index 69938f3e447..c8c58945bce 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc
@@ -93,7 +93,7 @@ static void node_geo_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>("Mesh 2");
+ Vector<GeometrySet> geometry_sets = params.extract_input<Vector<GeometrySet>>("Mesh 2");
for (const GeometrySet &geometry_set : geometry_sets) {
bke::geometry_set_gather_instances(geometry_set, set_groups);
}
@@ -154,7 +154,7 @@ static void node_geo_exec(GeoNodeExecParams params)
/* Store intersecting edges in attribute. */
if (attribute_outputs.intersecting_edges_id) {
- MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*result);
+ MutableAttributeAccessor attributes = result->attributes_for_write();
SpanAttributeWriter<bool> selection = attributes.lookup_or_add_for_write_only_span<bool>(
attribute_outputs.intersecting_edges_id.get(), ATTR_DOMAIN_EDGE);
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 03892501c89..c8b692651e9 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc
@@ -46,6 +46,7 @@ static Mesh *hull_from_bullet(const Mesh *mesh, Span<float3> coords)
}
/* Copy vertices. */
+ MutableSpan<MVert> dst_verts = result->verts_for_write();
for (const int i : IndexRange(verts_num)) {
float co[3];
int original_index;
@@ -59,7 +60,7 @@ static Mesh *hull_from_bullet(const Mesh *mesh, Span<float3> coords)
}
# endif
/* Copy the position of the original point. */
- copy_v3_v3(result->mvert[i].co, co);
+ copy_v3_v3(dst_verts[i].co, co);
}
else {
BLI_assert_msg(0, "Unexpected new vertex in hull output");
@@ -73,6 +74,8 @@ static Mesh *hull_from_bullet(const Mesh *mesh, Span<float3> coords)
* to a loop and edges need to be created from that. */
Array<MLoop> mloop_src(loops_num);
uint edge_index = 0;
+ MutableSpan<MEdge> edges = result->edges_for_write();
+
for (const int i : IndexRange(loops_num)) {
int v_from;
int v_to;
@@ -81,7 +84,7 @@ static Mesh *hull_from_bullet(const Mesh *mesh, Span<float3> coords)
mloop_src[i].v = (uint)v_from;
/* Add edges for ascending order loops only. */
if (v_from < v_to) {
- MEdge &edge = result->medge[edge_index];
+ MEdge &edge = edges[edge_index];
edge.v1 = v_from;
edge.v2 = v_to;
edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
@@ -95,7 +98,7 @@ static Mesh *hull_from_bullet(const Mesh *mesh, Span<float3> coords)
}
if (edges_num == 1) {
/* In this case there are no loops. */
- MEdge &edge = result->medge[0];
+ MEdge &edge = edges[0];
edge.v1 = 0;
edge.v2 = 1;
edge.flag |= ME_EDGEDRAW | ME_EDGERENDER | ME_LOOSEEDGE;
@@ -106,7 +109,10 @@ static Mesh *hull_from_bullet(const Mesh *mesh, Span<float3> coords)
/* Copy faces. */
Array<int> loops;
int j = 0;
- MLoop *loop = result->mloop;
+ MutableSpan<MPoly> polys = result->polys_for_write();
+ MutableSpan<MLoop> mesh_loops = result->loops_for_write();
+ MLoop *loop = mesh_loops.data();
+
for (const int i : IndexRange(faces_num)) {
const int len = plConvexHullGetFaceSize(hull, i);
@@ -116,7 +122,7 @@ static Mesh *hull_from_bullet(const Mesh *mesh, Span<float3> coords)
loops.reinitialize(len);
plConvexHullGetFaceLoops(hull, i, loops.data());
- MPoly &face = result->mpoly[i];
+ MPoly &face = polys[i];
face.loopstart = j;
face.totloop = len;
for (const int k : IndexRange(len)) {
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc
index db3f108aad5..28d979facac 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc
@@ -27,39 +27,31 @@ static void node_declare(NodeDeclarationBuilder &b)
N_("The selection from the start and end of the splines based on the input sizes"));
}
-class EndpointFieldInput final : public GeometryFieldInput {
+class EndpointFieldInput final : public bke::CurvesFieldInput {
Field<int> start_size_;
Field<int> end_size_;
public:
EndpointFieldInput(Field<int> start_size, Field<int> end_size)
- : GeometryFieldInput(CPPType::get<bool>(), "Endpoint Selection node"),
+ : bke::CurvesFieldInput(CPPType::get<bool>(), "Endpoint Selection node"),
start_size_(start_size),
end_size_(end_size)
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const bke::CurvesGeometry &curves,
const eAttrDomain domain,
IndexMask UNUSED(mask)) const final
{
- if (component.type() != GEO_COMPONENT_TYPE_CURVE || domain != ATTR_DOMAIN_POINT) {
- return nullptr;
+ if (domain != ATTR_DOMAIN_POINT) {
+ return {};
}
-
- const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
- if (!curve_component.has_curves()) {
- return nullptr;
- }
-
- const Curves &curves_id = *curve_component.get_for_read();
- const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
if (curves.points_num() == 0) {
- return nullptr;
+ return {};
}
- GeometryComponentFieldContext size_context{curve_component, ATTR_DOMAIN_CURVE};
+ bke::CurvesFieldContext size_context{curves, ATTR_DOMAIN_CURVE};
fn::FieldEvaluator evaluator{size_context, curves.curves_num()};
evaluator.add(start_size_);
evaluator.add(end_size_);
@@ -72,12 +64,12 @@ class EndpointFieldInput final : public GeometryFieldInput {
devirtualize_varray2(start_size, end_size, [&](const auto &start_size, const auto &end_size) {
threading::parallel_for(curves.curves_range(), 1024, [&](IndexRange curves_range) {
for (const int i : curves_range) {
- const IndexRange range = curves.points_for_curve(i);
+ const IndexRange points = curves.points_for_curve(i);
const int start = std::max(start_size[i], 0);
const int end = std::max(end_size[i], 0);
- selection_span.slice(range.take_front(start)).fill(true);
- selection_span.slice(range.take_back(end)).fill(true);
+ selection_span.slice(points.take_front(start)).fill(true);
+ selection_span.slice(points.take_back(end)).fill(true);
}
});
});
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc
index c9a8dba55b2..c675ee472cd 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc
@@ -79,10 +79,10 @@ static Mesh *cdt_to_mesh(const meshintersect::CDT_result<double> &result)
}
Mesh *mesh = BKE_mesh_new_nomain(vert_len, edge_len, 0, loop_len, poly_len);
- MutableSpan<MVert> verts{mesh->mvert, mesh->totvert};
- MutableSpan<MEdge> edges{mesh->medge, mesh->totedge};
- MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop};
- MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly};
+ MutableSpan<MVert> verts = mesh->verts_for_write();
+ MutableSpan<MEdge> edges = mesh->edges_for_write();
+ MutableSpan<MPoly> polys = mesh->polys_for_write();
+ MutableSpan<MLoop> loops = mesh->loops_for_write();
for (const int i : IndexRange(result.vert.size())) {
copy_v3_v3(verts[i].co, float3((float)result.vert[i].x, (float)result.vert[i].y, 0.0f));
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc
index ab1f8269c39..4586bb24464 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc
@@ -72,10 +72,9 @@ static void node_geo_exec(GeoNodeExecParams params)
return;
}
- const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>();
- const Curves &curves_id = *component.get_for_read();
+ const Curves &curves_id = *geometry_set.get_curves_for_read();
const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
- GeometryComponentFieldContext context{component, ATTR_DOMAIN_POINT};
+ bke::CurvesFieldContext context{curves, ATTR_DOMAIN_POINT};
fn::FieldEvaluator evaluator{context, curves.points_num()};
evaluator.add(radius_field);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc
index 5ef20f03f28..b34b22e995d 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc
@@ -70,35 +70,28 @@ static void select_by_handle_type(const bke::CurvesGeometry &curves,
}
}
-class HandleTypeFieldInput final : public GeometryFieldInput {
+class HandleTypeFieldInput final : public bke::CurvesFieldInput {
HandleType type_;
GeometryNodeCurveHandleMode mode_;
public:
HandleTypeFieldInput(HandleType type, GeometryNodeCurveHandleMode mode)
- : GeometryFieldInput(CPPType::get<bool>(), "Handle Type Selection node"),
+ : bke::CurvesFieldInput(CPPType::get<bool>(), "Handle Type Selection node"),
type_(type),
mode_(mode)
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const bke::CurvesGeometry &curves,
const eAttrDomain domain,
IndexMask mask) const final
{
- if (component.type() != GEO_COMPONENT_TYPE_CURVE || domain != ATTR_DOMAIN_POINT) {
+ if (domain != ATTR_DOMAIN_POINT) {
return {};
}
-
- const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
- const Curves *curves_id = curve_component.get_for_read();
- if (curves_id == nullptr) {
- return {};
- }
-
Array<bool> selection(mask.min_array_size());
- select_by_handle_type(bke::CurvesGeometry::wrap(curves_id->geometry), type_, mode_, selection);
+ select_by_handle_type(curves, type_, mode_, selection);
return VArray<bool>::ForContainer(std::move(selection));
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc
index 37fc6823b9a..41eafe2a741 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc
@@ -66,12 +66,14 @@ static void node_geo_exec(GeoNodeExecParams params)
case GEO_NODE_CURVE_RESAMPLE_COUNT: {
Field<int> count = params.extract_input<Field<int>>("Count");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry) {
- if (const CurveComponent *component = geometry.get_component_for_read<CurveComponent>()) {
- if (const Curves *src_curves = component->get_for_read()) {
- Curves *dst_curves = geometry::resample_to_count(*component, selection, count);
- bke::curves_copy_parameters(*src_curves, *dst_curves);
- geometry.replace_curves(dst_curves);
- }
+ if (const Curves *src_curves_id = geometry.get_curves_for_read()) {
+ const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(
+ src_curves_id->geometry);
+ bke::CurvesGeometry dst_curves = geometry::resample_to_count(
+ src_curves, selection, count);
+ Curves *dst_curves_id = bke::curves_new_nomain(std::move(dst_curves));
+ bke::curves_copy_parameters(*src_curves_id, *dst_curves_id);
+ geometry.replace_curves(dst_curves_id);
}
});
break;
@@ -79,24 +81,27 @@ static void node_geo_exec(GeoNodeExecParams params)
case GEO_NODE_CURVE_RESAMPLE_LENGTH: {
Field<float> length = params.extract_input<Field<float>>("Length");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry) {
- if (const CurveComponent *component = geometry.get_component_for_read<CurveComponent>()) {
- if (const Curves *src_curves = component->get_for_read()) {
- Curves *dst_curves = geometry::resample_to_length(*component, selection, length);
- bke::curves_copy_parameters(*src_curves, *dst_curves);
- geometry.replace_curves(dst_curves);
- }
+ if (const Curves *src_curves_id = geometry.get_curves_for_read()) {
+ const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(
+ src_curves_id->geometry);
+ bke::CurvesGeometry dst_curves = geometry::resample_to_length(
+ src_curves, selection, length);
+ Curves *dst_curves_id = bke::curves_new_nomain(std::move(dst_curves));
+ bke::curves_copy_parameters(*src_curves_id, *dst_curves_id);
+ geometry.replace_curves(dst_curves_id);
}
});
break;
}
case GEO_NODE_CURVE_RESAMPLE_EVALUATED:
geometry_set.modify_geometry_sets([&](GeometrySet &geometry) {
- if (const CurveComponent *component = geometry.get_component_for_read<CurveComponent>()) {
- if (const Curves *src_curves = component->get_for_read()) {
- Curves *dst_curves = geometry::resample_to_evaluated(*component, selection);
- bke::curves_copy_parameters(*src_curves, *dst_curves);
- geometry.replace_curves(dst_curves);
- }
+ if (const Curves *src_curves_id = geometry.get_curves_for_read()) {
+ const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(
+ src_curves_id->geometry);
+ bke::CurvesGeometry dst_curves = geometry::resample_to_evaluated(src_curves, selection);
+ Curves *dst_curves_id = bke::curves_new_nomain(std::move(dst_curves));
+ bke::curves_copy_parameters(*src_curves_id, *dst_curves_id);
+ geometry.replace_curves(dst_curves_id);
}
});
break;
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc
index de29735bd2d..0169ead5bd2 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc
@@ -23,14 +23,12 @@ static void node_geo_exec(GeoNodeExecParams params)
if (!geometry_set.has_curves()) {
return;
}
+ const Curves &src_curves_id = *geometry_set.get_curves_for_read();
+ const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(src_curves_id.geometry);
- Field<bool> selection_field = params.get_input<Field<bool>>("Selection");
- const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>();
- GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE};
- const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE);
-
- fn::FieldEvaluator selection_evaluator{field_context, domain_size};
- selection_evaluator.add(selection_field);
+ bke::CurvesFieldContext field_context{src_curves, ATTR_DOMAIN_CURVE};
+ fn::FieldEvaluator selection_evaluator{field_context, src_curves.curves_num()};
+ selection_evaluator.add(params.get_input<Field<bool>>("Selection"));
selection_evaluator.evaluate();
const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0);
if (selection.is_empty()) {
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_set_handle_type.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_set_handle_type.cc
index f7ba724c377..8bb24821064 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_set_handle_type.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_set_handle_type.cc
@@ -51,15 +51,12 @@ static HandleType handle_type_from_input_type(GeometryNodeCurveHandleType type)
return BEZIER_HANDLE_AUTO;
}
-static void set_type_in_component(CurveComponent &component,
- const GeometryNodeCurveHandleMode mode,
- const HandleType new_handle_type,
- const Field<bool> &selection_field)
+static void set_handle_type(bke::CurvesGeometry &curves,
+ const GeometryNodeCurveHandleMode mode,
+ const HandleType new_handle_type,
+ const Field<bool> &selection_field)
{
- Curves &curves_id = *component.get_for_write();
- bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
-
- GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
+ bke::CurvesFieldContext field_context{curves, ATTR_DOMAIN_POINT};
fn::FieldEvaluator evaluator{field_context, curves.points_num()};
evaluator.set_selection(selection_field);
evaluator.evaluate();
@@ -93,21 +90,17 @@ static void node_geo_exec(GeoNodeExecParams params)
std::atomic<bool> has_bezier = false;
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
- if (!geometry_set.has_curves()) {
- return;
- }
- has_curves = true;
- const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>();
- const AttributeAccessor attributes = *component.attributes();
- if (!attributes.contains("handle_type_left") || !attributes.contains("handle_type_right")) {
- return;
+ if (Curves *curves_id = geometry_set.get_curves_for_write()) {
+ bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry);
+ has_curves = true;
+ const AttributeAccessor attributes = curves.attributes();
+ if (!attributes.contains("handle_type_left") || !attributes.contains("handle_type_right")) {
+ return;
+ }
+ has_bezier = true;
+
+ set_handle_type(curves, mode, new_handle_type, selection_field);
}
- has_bezier = true;
-
- set_type_in_component(geometry_set.get_component_for_write<CurveComponent>(),
- mode,
- new_handle_type,
- selection_field);
});
if (has_curves && !has_bezier) {
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc
index b98541e3446..b5d8d1f020a 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc
@@ -143,8 +143,8 @@ static VArray<float> construct_curve_parameter_varray(const bke::CurvesGeometry
Array<float> lengths = accumulated_lengths_curve_domain(curves);
const int last_index = curves.curves_num() - 1;
- const int total_length = lengths.last() + curves.evaluated_length_total_for_curve(
- last_index, cyclic[last_index]);
+ const float total_length = lengths.last() + curves.evaluated_length_total_for_curve(
+ last_index, cyclic[last_index]);
if (total_length > 0.0f) {
const float factor = 1.0f / total_length;
for (float &value : lengths) {
@@ -203,26 +203,18 @@ static VArray<int> construct_index_on_spline_varray(const bke::CurvesGeometry &c
return {};
}
-class CurveParameterFieldInput final : public GeometryFieldInput {
+class CurveParameterFieldInput final : public bke::CurvesFieldInput {
public:
- CurveParameterFieldInput() : GeometryFieldInput(CPPType::get<float>(), "Curve Parameter node")
+ CurveParameterFieldInput() : bke::CurvesFieldInput(CPPType::get<float>(), "Curve Parameter node")
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const bke::CurvesGeometry &curves,
const eAttrDomain domain,
IndexMask mask) const final
{
- if (component.type() == GEO_COMPONENT_TYPE_CURVE) {
- const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
- if (curve_component.has_curves()) {
- const Curves &curves_id = *curve_component.get_for_read();
- const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
- return construct_curve_parameter_varray(curves, mask, domain);
- }
- }
- return {};
+ return construct_curve_parameter_varray(curves, mask, domain);
}
uint64_t hash() const override
@@ -237,26 +229,19 @@ class CurveParameterFieldInput final : public GeometryFieldInput {
}
};
-class CurveLengthParameterFieldInput final : public GeometryFieldInput {
+class CurveLengthParameterFieldInput final : public bke::CurvesFieldInput {
public:
- CurveLengthParameterFieldInput() : GeometryFieldInput(CPPType::get<float>(), "Curve Length node")
+ CurveLengthParameterFieldInput()
+ : bke::CurvesFieldInput(CPPType::get<float>(), "Curve Length node")
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const bke::CurvesGeometry &curves,
const eAttrDomain domain,
IndexMask mask) const final
{
- if (component.type() == GEO_COMPONENT_TYPE_CURVE) {
- const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
- if (curve_component.has_curves()) {
- const Curves &curves_id = *curve_component.get_for_read();
- const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
- return construct_curve_length_parameter_varray(curves, mask, domain);
- }
- }
- return {};
+ return construct_curve_length_parameter_varray(curves, mask, domain);
}
uint64_t hash() const override
@@ -271,26 +256,18 @@ class CurveLengthParameterFieldInput final : public GeometryFieldInput {
}
};
-class IndexOnSplineFieldInput final : public GeometryFieldInput {
+class IndexOnSplineFieldInput final : public bke::CurvesFieldInput {
public:
- IndexOnSplineFieldInput() : GeometryFieldInput(CPPType::get<int>(), "Spline Index")
+ IndexOnSplineFieldInput() : bke::CurvesFieldInput(CPPType::get<int>(), "Spline Index")
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const bke::CurvesGeometry &curves,
const eAttrDomain domain,
IndexMask mask) const final
{
- if (component.type() == GEO_COMPONENT_TYPE_CURVE) {
- const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
- if (curve_component.has_curves()) {
- const Curves &curves_id = *curve_component.get_for_read();
- const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
- return construct_index_on_spline_varray(curves, mask, domain);
- }
- }
- return {};
+ return construct_index_on_spline_varray(curves, mask, domain);
}
uint64_t hash() const override
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc
index a92479bc5f1..4d7e5f13969 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc
@@ -45,14 +45,13 @@ static void node_geo_exec(GeoNodeExecParams params)
if (!geometry_set.has_curves()) {
return;
}
- const CurveComponent &src_component = *geometry_set.get_component_for_read<CurveComponent>();
- const Curves &src_curves_id = *src_component.get_for_read();
+ const Curves &src_curves_id = *geometry_set.get_curves_for_read();
const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(src_curves_id.geometry);
if (src_curves.is_single_type(dst_type)) {
return;
}
- GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_CURVE};
+ bke::CurvesFieldContext field_context{src_curves, ATTR_DOMAIN_CURVE};
fn::FieldEvaluator evaluator{field_context, src_curves.curves_num()};
evaluator.set_selection(selection_field);
evaluator.evaluate();
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc
index bd44adb35a2..919d0056bca 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc
@@ -34,11 +34,10 @@ static void node_geo_exec(GeoNodeExecParams params)
return;
}
- const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>();
- const Curves &src_curves_id = *component.get_for_read();
+ const Curves &src_curves_id = *geometry_set.get_curves_for_read();
const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(src_curves_id.geometry);
- GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
+ bke::CurvesFieldContext field_context{src_curves, ATTR_DOMAIN_POINT};
fn::FieldEvaluator evaluator{field_context, src_curves.points_num()};
evaluator.add(cuts_field);
evaluator.evaluate();
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc
index 0ddf06dc8c7..799a65ec3d1 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc
@@ -4,8 +4,11 @@
#include "BLI_task.hh"
#include "BLI_timeit.hh"
+#include "DNA_pointcloud_types.h"
+
#include "BKE_pointcloud.h"
-#include "BKE_spline.hh"
+
+#include "GEO_resample_curves.hh"
#include "UI_interface.h"
#include "UI_resources.h"
@@ -62,9 +65,9 @@ static void node_update(bNodeTree *ntree, bNode *node)
nodeSetSocketAvailability(ntree, length_socket, mode == GEO_NODE_CURVE_RESAMPLE_LENGTH);
}
-static void curve_create_default_rotation_attribute(Span<float3> tangents,
- Span<float3> normals,
- MutableSpan<float3> rotations)
+static void fill_rotation_attribute(const Span<float3> tangents,
+ const Span<float3> normals,
+ MutableSpan<float3> rotations)
{
threading::parallel_for(IndexRange(rotations.size()), 512, [&](IndexRange range) {
for (const int i : range) {
@@ -74,239 +77,30 @@ static void curve_create_default_rotation_attribute(Span<float3> tangents,
});
}
-static Array<int> calculate_spline_point_offsets(GeoNodeExecParams &params,
- const GeometryNodeCurveResampleMode mode,
- const CurveEval &curve,
- const Span<SplinePtr> splines)
-{
- const int size = curve.splines().size();
- switch (mode) {
- case GEO_NODE_CURVE_RESAMPLE_COUNT: {
- const int count = params.get_input<int>("Count");
- if (count < 1) {
- return {0};
- }
- Array<int> offsets(size + 1);
- int offset = 0;
- for (const int i : IndexRange(size)) {
- offsets[i] = offset;
- if (splines[i]->evaluated_points_num() > 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_num() > 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 eCustomDataType data_type)
-{
- GAttributeWriter attribute = points.attributes_for_write()->lookup_or_add_for_write(
- attribute_id, ATTR_DOMAIN_POINT, data_type);
- GMutableSpan span = attribute.varray.get_internal_span();
- attribute.finish();
- return span;
-}
-
-template<typename T>
-static MutableSpan<T> ensure_point_attribute(PointCloudComponent &points,
- const AttributeIDRef &attribute_id)
-{
- AttributeWriter<T> attribute = points.attributes_for_write()->lookup_or_add_for_write<T>(
- attribute_id, ATTR_DOMAIN_POINT);
- MutableSpan<T> span = attribute.varray.get_internal_span();
- attribute.finish();
- return span;
-}
-
-namespace {
-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)
+static PointCloud *pointcloud_from_curves(bke::CurvesGeometry curves,
+ const AttributeIDRef &tangent_id,
+ const AttributeIDRef &normal_id,
+ const AttributeIDRef &rotation_id)
{
- 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());
+ PointCloud *pointcloud = BKE_pointcloud_new_nomain(0);
+ pointcloud->totpoint = curves.points_num();
+
+ if (rotation_id) {
+ MutableAttributeAccessor attributes = curves.attributes_for_write();
+ const VArraySpan<float3> tangents = attributes.lookup<float3>(tangent_id, ATTR_DOMAIN_POINT);
+ const VArraySpan<float3> normals = attributes.lookup<float3>(normal_id, ATTR_DOMAIN_POINT);
+ SpanAttributeWriter<float3> rotations = attributes.lookup_or_add_for_write_only_span<float3>(
+ rotation_id, ATTR_DOMAIN_POINT);
+ fill_rotation_attribute(tangents, normals, rotations.span);
+ rotations.finish();
}
- /* 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));
+ /* Move the curve point custom data to the pointcloud, to avoid any copying. */
+ CustomData_free(&pointcloud->pdata, pointcloud->totpoint);
+ pointcloud->pdata = curves.point_data;
+ CustomData_reset(&curves.point_data);
- 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 num = offsets[i + 1] - offsets[i];
- if (num == 0) {
- continue;
- }
-
- const Array<float> uniform_samples = spline.sample_uniform_index_factors(num);
-
- spline.sample_with_index_factors<float3>(
- spline.evaluated_positions(), uniform_samples, data.positions.slice(offset, num));
- spline.sample_with_index_factors<float>(spline.interpolate_to_evaluated(spline.radii()),
- uniform_samples,
- data.radii.slice(offset, num));
-
- 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, num));
- }
-
- if (!data.tangents.is_empty()) {
- Span<float3> src_tangents = spline.evaluated_tangents();
- MutableSpan<float3> sampled_tangents = data.tangents.slice(offset, num);
- spline.sample_with_index_factors<float3>(src_tangents, uniform_samples, sampled_tangents);
- for (float3 &vector : sampled_tangents) {
- vector = math::normalize(vector);
- }
- }
-
- if (!data.normals.is_empty()) {
- Span<float3> src_normals = spline.evaluated_normals();
- MutableSpan<float3> sampled_normals = data.normals.slice(offset, num);
- spline.sample_with_index_factors<float3>(src_normals, uniform_samples, sampled_normals);
- for (float3 &vector : sampled_normals) {
- vector = math::normalize(vector);
- }
- }
- }
- });
-}
-
-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 num = offsets[i + 1] - offsets[i];
- type.fill_assign_n(curve_attribute[i], dst[offset], num);
- }
-
- return true;
- },
- ATTR_DOMAIN_CURVE);
+ return pointcloud;
}
static void node_geo_exec(GeoNodeExecParams params)
@@ -315,73 +109,96 @@ static void node_geo_exec(GeoNodeExecParams params)
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");
-
GeometryComponentEditData::remember_deformed_curve_positions_if_necessary(geometry_set);
- geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
- if (!geometry_set.has_curves()) {
- geometry_set.remove_geometry_during_modify();
- return;
- }
- const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(
- *geometry_set.get_curves_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_num = offsets.last();
- if (total_num == 0) {
- geometry_set.remove_geometry_during_modify();
- return;
- }
+ StrongAnonymousAttributeID tangent_anonymous_id;
+ StrongAnonymousAttributeID normal_anonymous_id;
+ StrongAnonymousAttributeID rotation_anonymous_id;
+ const bool rotation_required = params.output_is_required("Rotation");
+ if (params.output_is_required("Tangent") || rotation_required) {
+ tangent_anonymous_id = StrongAnonymousAttributeID("Tangent");
+ }
+ if (params.output_is_required("Normal") || rotation_required) {
+ normal_anonymous_id = StrongAnonymousAttributeID("Normal");
+ }
+ if (rotation_required) {
+ rotation_anonymous_id = StrongAnonymousAttributeID("Rotation");
+ }
- geometry_set.replace_pointcloud(BKE_pointcloud_new_nomain(total_num));
- PointCloudComponent &points = geometry_set.get_component_for_write<PointCloudComponent>();
- ResultAttributes point_attributes = create_attributes_for_transfer(
- points, *curve, attribute_outputs);
+ geometry::ResampleCurvesOutputAttributeIDs resample_attributes;
+ resample_attributes.tangent_id = tangent_anonymous_id.get();
+ resample_attributes.normal_id = normal_anonymous_id.get();
- 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;
+ switch (mode) {
+ case GEO_NODE_CURVE_RESAMPLE_COUNT: {
+ Field<int> count = params.extract_input<Field<int>>("Count");
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry) {
+ if (const Curves *src_curves_id = geometry.get_curves_for_read()) {
+ const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(
+ src_curves_id->geometry);
+ bke::CurvesGeometry dst_curves = geometry::resample_to_count(
+ src_curves, fn::make_constant_field<bool>(true), count, resample_attributes);
+ PointCloud *pointcloud = pointcloud_from_curves(std::move(dst_curves),
+ resample_attributes.tangent_id,
+ resample_attributes.normal_id,
+ rotation_anonymous_id.get());
+ geometry.remove_geometry_during_modify();
+ geometry.replace_pointcloud(pointcloud);
+ }
+ });
+ 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);
+ case GEO_NODE_CURVE_RESAMPLE_LENGTH: {
+ Field<float> length = params.extract_input<Field<float>>("Length");
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry) {
+ if (const Curves *src_curves_id = geometry.get_curves_for_read()) {
+ const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(
+ src_curves_id->geometry);
+ bke::CurvesGeometry dst_curves = geometry::resample_to_length(
+ src_curves, fn::make_constant_field<bool>(true), length, resample_attributes);
+ PointCloud *pointcloud = pointcloud_from_curves(std::move(dst_curves),
+ resample_attributes.tangent_id,
+ resample_attributes.normal_id,
+ rotation_anonymous_id.get());
+ geometry.remove_geometry_during_modify();
+ geometry.replace_pointcloud(pointcloud);
+ }
+ });
+ break;
}
-
- geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_POINT_CLOUD});
- });
+ case GEO_NODE_CURVE_RESAMPLE_EVALUATED:
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry) {
+ if (const Curves *src_curves_id = geometry.get_curves_for_read()) {
+ const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(
+ src_curves_id->geometry);
+ bke::CurvesGeometry dst_curves = geometry::resample_to_evaluated(
+ src_curves, fn::make_constant_field<bool>(true), resample_attributes);
+ PointCloud *pointcloud = pointcloud_from_curves(std::move(dst_curves),
+ resample_attributes.tangent_id,
+ resample_attributes.normal_id,
+ rotation_anonymous_id.get());
+ geometry.remove_geometry_during_modify();
+ geometry.replace_pointcloud(pointcloud);
+ }
+ });
+ break;
+ }
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 (tangent_anonymous_id) {
+ params.set_output("Tangent",
+ AnonymousAttributeFieldInput::Create<float3>(
+ std::move(tangent_anonymous_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 (normal_anonymous_id) {
+ params.set_output("Normal",
+ AnonymousAttributeFieldInput::Create<float3>(
+ std::move(normal_anonymous_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()));
+ if (rotation_anonymous_id) {
+ params.set_output("Rotation",
+ AnonymousAttributeFieldInput::Create<float3>(
+ std::move(rotation_anonymous_id), params.attribute_producer_name()));
}
}
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 0932de237a9..1576b573058 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc
@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BKE_curves.hh"
-#include "BKE_spline.hh"
#include "BLI_task.hh"
#include "UI_interface.h"
@@ -9,12 +8,12 @@
#include "NOD_socket_search_link.hh"
+#include "GEO_trim_curves.hh"
+
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_curve_trim_cc {
-using blender::attribute_math::mix2;
-
NODE_STORAGE_FUNCS(NodeGeometryCurveTrim)
static void node_declare(NodeDeclarationBuilder &b)
@@ -108,394 +107,6 @@ static void node_gather_link_searches(GatherLinkSearchOpParams &params)
}
}
-struct TrimLocation {
- /* Control point index at the start side of the trim location. */
- int left_index;
- /* Control point index at the end of the trim location's segment. */
- int right_index;
- /* The factor between the left and right indices. */
- float factor;
-};
-
-template<typename T>
-static void shift_slice_to_start(MutableSpan<T> data, const int start_index, const int num)
-{
- BLI_assert(start_index + num - 1 <= data.size());
- memmove(data.data(), &data[start_index], sizeof(T) * num);
-}
-
-/* Shift slice to start of span and modifies start and end data. */
-template<typename T>
-static void linear_trim_data(const TrimLocation &start,
- const TrimLocation &end,
- MutableSpan<T> data)
-{
- const int num = end.right_index - start.left_index + 1;
-
- if (start.left_index > 0) {
- shift_slice_to_start<T>(data, start.left_index, num);
- }
-
- const T start_data = mix2<T>(start.factor, data.first(), data[1]);
- const T end_data = mix2<T>(end.factor, data[num - 2], data[num - 1]);
-
- data.first() = start_data;
- data[num - 1] = end_data;
-}
-
-/**
- * Identical operation as #linear_trim_data, but copy data to a new #MutableSpan rather than
- * modifying the original data.
- */
-template<typename T>
-static void linear_trim_to_output_data(const TrimLocation &start,
- const TrimLocation &end,
- Span<T> src,
- MutableSpan<T> dst)
-{
- const int num = end.right_index - start.left_index + 1;
-
- const T start_data = mix2<T>(start.factor, src[start.left_index], src[start.right_index]);
- const T end_data = mix2<T>(end.factor, src[end.left_index], src[end.right_index]);
-
- dst.copy_from(src.slice(start.left_index, num));
- dst.first() = start_data;
- dst.last() = end_data;
-}
-
-/* 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,
- const BezierSpline &spline)
-{
- 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_num = offsets[left + 1] - offsets[left];
- const float factor = std::clamp(offset_in_segment / segment_eval_num, 0.0f, 1.0f);
-
- return {left, right, factor};
-}
-
-static void trim_poly_spline(Spline &spline,
- const Spline::LookupResult &start_lookup,
- const Spline::LookupResult &end_lookup)
-{
- /* Poly splines have a 1 to 1 mapping between control points and evaluated points. */
- const TrimLocation start = {
- start_lookup.evaluated_index, start_lookup.next_evaluated_index, start_lookup.factor};
- const TrimLocation end = {
- end_lookup.evaluated_index, end_lookup.next_evaluated_index, end_lookup.factor};
-
- const int num = end.right_index - start.left_index + 1;
-
- linear_trim_data<float3>(start, end, spline.positions());
- linear_trim_data<float>(start, end, spline.radii());
- linear_trim_data<float>(start, end, spline.tilts());
-
- spline.attributes.foreach_attribute(
- [&](const AttributeIDRef &attribute_id, const AttributeMetaData &UNUSED(meta_data)) {
- std::optional<GMutableSpan> src = spline.attributes.get_for_write(attribute_id);
- BLI_assert(src);
- attribute_math::convert_to_static_type(src->type(), [&](auto dummy) {
- using T = decltype(dummy);
- linear_trim_data<T>(start, end, src->typed<T>());
- });
- return true;
- },
- ATTR_DOMAIN_POINT);
-
- spline.resize(num);
-}
-
-/**
- * Trim NURB splines by converting to a poly spline.
- */
-static PolySpline trim_nurbs_spline(const Spline &spline,
- const Spline::LookupResult &start_lookup,
- const Spline::LookupResult &end_lookup)
-{
- /* Since this outputs a poly spline, the evaluated indices are the control point indices. */
- const TrimLocation start = {
- start_lookup.evaluated_index, start_lookup.next_evaluated_index, start_lookup.factor};
- const TrimLocation end = {
- end_lookup.evaluated_index, end_lookup.next_evaluated_index, end_lookup.factor};
-
- const int num = end.right_index - start.left_index + 1;
-
- /* Create poly spline and copy trimmed data to it. */
- PolySpline new_spline;
- new_spline.resize(num);
-
- /* Copy generic attribute data. */
- spline.attributes.foreach_attribute(
- [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
- std::optional<GSpan> src = spline.attributes.get_for_read(attribute_id);
- BLI_assert(src);
- if (!new_spline.attributes.create(attribute_id, meta_data.data_type)) {
- BLI_assert_unreachable();
- return false;
- }
- std::optional<GMutableSpan> dst = new_spline.attributes.get_for_write(attribute_id);
- BLI_assert(dst);
-
- 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>());
- linear_trim_to_output_data<T>(
- start, end, eval_data.get_internal_span(), dst->typed<T>());
- });
- return true;
- },
- ATTR_DOMAIN_POINT);
-
- linear_trim_to_output_data<float3>(
- start, end, spline.evaluated_positions(), new_spline.positions());
-
- 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());
-
- 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());
-
- return new_spline;
-}
-
-/**
- * Trim Bezier splines by adjusting the first and last handles
- * and control points to maintain the original shape.
- */
-static void trim_bezier_spline(Spline &spline,
- const Spline::LookupResult &start_lookup,
- const Spline::LookupResult &end_lookup)
-{
- BezierSpline &bezier_spline = static_cast<BezierSpline &>(spline);
-
- 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 num = end.right_index - start.left_index + 1;
-
- /* Trim the spline attributes. Done before end.factor recalculation as it needs
- * the original end.factor value. */
- linear_trim_data<float>(start, end, bezier_spline.radii());
- linear_trim_data<float>(start, end, bezier_spline.tilts());
- spline.attributes.foreach_attribute(
- [&](const AttributeIDRef &attribute_id, const AttributeMetaData &UNUSED(meta_data)) {
- std::optional<GMutableSpan> src = spline.attributes.get_for_write(attribute_id);
- BLI_assert(src);
- attribute_math::convert_to_static_type(src->type(), [&](auto dummy) {
- using T = decltype(dummy);
- linear_trim_data<T>(start, end, src->typed<T>());
- });
- return true;
- },
- ATTR_DOMAIN_POINT);
-
- /* Recalculate end.factor if the `num` is two, because the adjustment in the
- * position of the control point of the spline to the left of the new end point will change the
- * factor between them. */
- if (num == 2) {
- if (start_lookup.factor == 1.0f) {
- end.factor = 0.0f;
- }
- else {
- end.factor = (end_lookup.evaluated_index + end_lookup.factor -
- (start_lookup.evaluated_index + start_lookup.factor)) /
- (control_offsets[end.right_index] -
- (start_lookup.evaluated_index + start_lookup.factor));
- end.factor = std::clamp(end.factor, 0.0f, 1.0f);
- }
- }
-
- BezierSpline::InsertResult start_point = bezier_spline.calculate_segment_insertion(
- start.left_index, start.right_index, start.factor);
-
- /* Update the start control point parameters so they are used calculating the new end point. */
- bezier_spline.positions()[start.left_index] = start_point.position;
- bezier_spline.handle_positions_right()[start.left_index] = start_point.right_handle;
- bezier_spline.handle_positions_left()[start.right_index] = start_point.handle_next;
-
- const BezierSpline::InsertResult end_point = bezier_spline.calculate_segment_insertion(
- end.left_index, end.right_index, end.factor);
-
- /* If `num` is two, then the start point right handle needs to change to reflect the end point
- * previous handle update. */
- if (num == 2) {
- start_point.right_handle = end_point.handle_prev;
- }
-
- /* Shift control point position data to start at beginning of array. */
- if (start.left_index > 0) {
- shift_slice_to_start(bezier_spline.positions(), start.left_index, num);
- shift_slice_to_start(bezier_spline.handle_positions_left(), start.left_index, num);
- shift_slice_to_start(bezier_spline.handle_positions_right(), start.left_index, num);
- }
-
- bezier_spline.positions().first() = start_point.position;
- bezier_spline.positions()[num - 1] = end_point.position;
-
- bezier_spline.handle_positions_left().first() = start_point.left_handle;
- bezier_spline.handle_positions_left()[num - 1] = end_point.left_handle;
-
- bezier_spline.handle_positions_right().first() = start_point.right_handle;
- bezier_spline.handle_positions_right()[num - 1] = end_point.right_handle;
-
- /* If there is at least one control point between the endpoints, update the control
- * point handle to the right of the start point and to the left of the end point. */
- if (num > 2) {
- bezier_spline.handle_positions_left()[start.right_index - start.left_index] =
- start_point.handle_next;
- bezier_spline.handle_positions_right()[end.left_index - start.left_index] =
- end_point.handle_prev;
- }
-
- bezier_spline.resize(num);
-}
-
-static void trim_spline(SplinePtr &spline,
- const Spline::LookupResult start,
- const Spline::LookupResult end)
-{
- switch (spline->type()) {
- case CURVE_TYPE_BEZIER:
- trim_bezier_spline(*spline, start, end);
- break;
- case CURVE_TYPE_POLY:
- trim_poly_spline(*spline, start, end);
- break;
- case CURVE_TYPE_NURBS:
- spline = std::make_unique<PolySpline>(trim_nurbs_spline(*spline, start, end));
- break;
- case CURVE_TYPE_CATMULL_ROM:
- BLI_assert_unreachable();
- spline = {};
- }
- 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() = BEZIER_HANDLE_FREE;
- bezier.handle_types_right().first() = BEZIER_HANDLE_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 CURVE_TYPE_BEZIER:
- to_single_point_bezier(*spline, lookup);
- break;
- case CURVE_TYPE_POLY:
- to_single_point_poly(*spline, lookup);
- break;
- case CURVE_TYPE_NURBS:
- spline = std::make_unique<PolySpline>(to_single_point_nurbs(*spline, lookup));
- break;
- case CURVE_TYPE_CATMULL_ROM:
- BLI_assert_unreachable();
- spline = {};
- }
-}
-
static void geometry_set_curve_trim(GeometrySet &geometry_set,
const GeometryNodeCurveSampleMode mode,
Field<float> &start_field,
@@ -504,71 +115,50 @@ static void geometry_set_curve_trim(GeometrySet &geometry_set,
if (!geometry_set.has_curves()) {
return;
}
+ const Curves &src_curves_id = *geometry_set.get_curves_for_read();
+ const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(src_curves_id.geometry);
+ if (src_curves.curves_num() == 0) {
+ 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};
+ bke::CurvesFieldContext field_context{src_curves, ATTR_DOMAIN_CURVE};
+ fn::FieldEvaluator evaluator{field_context, src_curves.curves_num()};
evaluator.add(start_field);
evaluator.add(end_field);
evaluator.evaluate();
const VArray<float> starts = evaluator.get_evaluated<float>(0);
const VArray<float> ends = evaluator.get_evaluated<float>(1);
- const Curves &src_curves_id = *geometry_set.get_curves_for_read();
- std::unique_ptr<CurveEval> curve = curves_to_curve_eval(src_curves_id);
- MutableSpan<SplinePtr> splines = curve->splines();
-
- threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) {
- for (const int i : range) {
- SplinePtr &spline = splines[i];
-
- /* Currently trimming cyclic splines is not supported. It could be in the future though. */
- if (spline->is_cyclic()) {
- continue;
- }
-
- if (spline->evaluated_edges_num() == 0) {
- continue;
- }
-
- 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)));
- }
+ const VArray<bool> cyclic = src_curves.cyclic();
+
+ /* If node length input is on form [0, 1] instead of [0, length]*/
+ const bool normalized_length_lookup = mode == GEO_NODE_CURVE_SAMPLE_FACTOR;
+
+ /* Stack start + end field. */
+ Vector<float> length_factors(src_curves.curves_num() * 2);
+ Vector<int64_t> lookup_indices(src_curves.curves_num() * 2);
+ threading::parallel_for(src_curves.curves_range(), 512, [&](IndexRange curve_range) {
+ for (const int64_t curve_i : curve_range) {
+ const bool negative_trim = !cyclic[curve_i] && starts[curve_i] > ends[curve_i];
+ length_factors[curve_i] = starts[curve_i];
+ length_factors[curve_i + src_curves.curves_num()] = negative_trim ? starts[curve_i] :
+ ends[curve_i];
+ lookup_indices[curve_i] = curve_i;
+ lookup_indices[curve_i + src_curves.curves_num()] = curve_i;
}
});
- Curves *dst_curves_id = curve_eval_to_curves(*curve);
+ /* Create curve trim lookup table. */
+ Array<bke::curves::CurvePoint, 12> point_lookups = geometry::lookup_curve_points(
+ src_curves, length_factors, lookup_indices, normalized_length_lookup);
+
+ bke::CurvesGeometry dst_curves = geometry::trim_curves(
+ src_curves,
+ src_curves.curves_range().as_span(),
+ point_lookups.as_span().slice(0, src_curves.curves_num()),
+ point_lookups.as_span().slice(src_curves.curves_num(), src_curves.curves_num()));
+
+ Curves *dst_curves_id = bke::curves_new_nomain(std::move(dst_curves));
bke::curves_copy_parameters(src_curves_id, *dst_curves_id);
geometry_set.replace_curves(dst_curves_id);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc
index 5a40ededa96..3f6155460ed 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc
@@ -66,6 +66,12 @@ static void deform_curves(const CurvesGeometry &curves,
const float4x4 curves_to_surface = surface_to_curves.inverted();
+ const Span<MVert> surface_verts_old = surface_mesh_old.verts();
+ const Span<MLoop> surface_loops_old = surface_mesh_old.loops();
+
+ const Span<MVert> surface_verts_new = surface_mesh_new.verts();
+ const Span<MLoop> surface_loops_new = surface_mesh_new.loops();
+
threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange range) {
for (const int curve_i : range) {
const ReverseUVSampler::Result &surface_sample_old = surface_samples_old[curve_i];
@@ -92,13 +98,13 @@ static void deform_curves(const CurvesGeometry &curves,
const int corner_1_new = looptri_new.tri[1];
const int corner_2_new = looptri_new.tri[2];
- const int vert_0_old = surface_mesh_old.mloop[corner_0_old].v;
- const int vert_1_old = surface_mesh_old.mloop[corner_1_old].v;
- const int vert_2_old = surface_mesh_old.mloop[corner_2_old].v;
+ const int vert_0_old = surface_loops_old[corner_0_old].v;
+ const int vert_1_old = surface_loops_old[corner_1_old].v;
+ const int vert_2_old = surface_loops_old[corner_2_old].v;
- const int vert_0_new = surface_mesh_new.mloop[corner_0_new].v;
- const int vert_1_new = surface_mesh_new.mloop[corner_1_new].v;
- const int vert_2_new = surface_mesh_new.mloop[corner_2_new].v;
+ const int vert_0_new = surface_loops_new[corner_0_new].v;
+ const int vert_1_new = surface_loops_new[corner_1_new].v;
+ const int vert_2_new = surface_loops_new[corner_2_new].v;
const float3 &normal_0_old = corner_normals_old[corner_0_old];
const float3 &normal_1_old = corner_normals_old[corner_1_old];
@@ -112,14 +118,14 @@ static void deform_curves(const CurvesGeometry &curves,
const float3 normal_new = math::normalize(
mix3(bary_weights_new, normal_0_new, normal_1_new, normal_2_new));
- const float3 &pos_0_old = surface_mesh_old.mvert[vert_0_old].co;
- const float3 &pos_1_old = surface_mesh_old.mvert[vert_1_old].co;
- const float3 &pos_2_old = surface_mesh_old.mvert[vert_2_old].co;
+ const float3 &pos_0_old = surface_verts_old[vert_0_old].co;
+ const float3 &pos_1_old = surface_verts_old[vert_1_old].co;
+ const float3 &pos_2_old = surface_verts_old[vert_2_old].co;
const float3 pos_old = mix3(bary_weights_old, pos_0_old, pos_1_old, pos_2_old);
- const float3 &pos_0_new = surface_mesh_new.mvert[vert_0_new].co;
- const float3 &pos_1_new = surface_mesh_new.mvert[vert_1_new].co;
- const float3 &pos_2_new = surface_mesh_new.mvert[vert_2_new].co;
+ const float3 &pos_0_new = surface_verts_new[vert_0_new].co;
+ const float3 &pos_1_new = surface_verts_new[vert_1_new].co;
+ const float3 &pos_2_new = surface_verts_new[vert_2_new].co;
const float3 pos_new = mix3(bary_weights_new, pos_0_new, pos_1_new, pos_2_new);
/* The translation is just the difference between the old and new position on the surface. */
@@ -249,7 +255,7 @@ static void node_geo_exec(GeoNodeExecParams params)
Mesh &surface_object_data = *static_cast<Mesh *>(surface_ob_orig->data);
if (BMEditMesh *em = surface_object_data.edit_mesh) {
- surface_mesh_orig = BKE_mesh_from_bmesh_for_eval_nomain(em->bm, NULL, &surface_object_data);
+ surface_mesh_orig = BKE_mesh_from_bmesh_for_eval_nomain(em->bm, nullptr, &surface_object_data);
free_suface_mesh_orig = true;
}
else {
@@ -264,8 +270,8 @@ static void node_geo_exec(GeoNodeExecParams params)
BKE_mesh_wrapper_ensure_mdata(surface_mesh_eval);
- const AttributeAccessor mesh_attributes_eval = bke::mesh_attributes(*surface_mesh_eval);
- const AttributeAccessor mesh_attributes_orig = bke::mesh_attributes(*surface_mesh_orig);
+ const AttributeAccessor mesh_attributes_eval = surface_mesh_eval->attributes();
+ const AttributeAccessor mesh_attributes_orig = surface_mesh_orig->attributes();
Curves &curves_id = *curves_geometry.get_curves_for_write();
CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc
index b74b4e45199..851ca622d6b 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc
@@ -7,6 +7,7 @@
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
+#include "DNA_pointcloud_types.h"
#include "BKE_attribute_math.hh"
#include "BKE_curves.hh"
@@ -160,10 +161,11 @@ static void copy_face_corner_attributes(const Map<AttributeIDRef, AttributeKind>
const Span<int> selected_poly_indices,
const Mesh &mesh_in)
{
+ const Span<MPoly> polys = mesh_in.polys();
Vector<int64_t> indices;
indices.reserve(selected_loops_num);
for (const int src_poly_index : selected_poly_indices) {
- const MPoly &src_poly = mesh_in.mpoly[src_poly_index];
+ const MPoly &src_poly = polys[src_poly_index];
const int src_loop_start = src_poly.loopstart;
const int tot_loop = src_poly.totloop;
for (const int i : IndexRange(tot_loop)) {
@@ -174,39 +176,35 @@ static void copy_face_corner_attributes(const Map<AttributeIDRef, AttributeKind>
attributes, src_attributes, dst_attributes, ATTR_DOMAIN_CORNER, IndexMask(indices));
}
-static void copy_masked_vertices_to_new_mesh(const Mesh &src_mesh,
- Mesh &dst_mesh,
- Span<int> vertex_map)
+static void copy_masked_verts_to_new_mesh(const Mesh &src_mesh,
+ Mesh &dst_mesh,
+ Span<int> vertex_map)
{
BLI_assert(src_mesh.totvert == vertex_map.size());
+ const Span<MVert> src_verts = src_mesh.verts();
+ MutableSpan<MVert> dst_verts = dst_mesh.verts_for_write();
+
for (const int i_src : vertex_map.index_range()) {
const int i_dst = vertex_map[i_src];
if (i_dst == -1) {
continue;
}
-
- const MVert &v_src = src_mesh.mvert[i_src];
- MVert &v_dst = dst_mesh.mvert[i_dst];
-
- v_dst = v_src;
+ dst_verts[i_dst] = src_verts[i_src];
}
}
static void copy_masked_edges_to_new_mesh(const Mesh &src_mesh, Mesh &dst_mesh, Span<int> edge_map)
{
BLI_assert(src_mesh.totedge == edge_map.size());
+ const Span<MEdge> src_edges = src_mesh.edges();
+ MutableSpan<MEdge> dst_edges = dst_mesh.edges_for_write();
+
for (const int i_src : IndexRange(src_mesh.totedge)) {
const int i_dst = edge_map[i_src];
if (ELEM(i_dst, -1, -2)) {
continue;
}
-
- const MEdge &e_src = src_mesh.medge[i_src];
- MEdge &e_dst = dst_mesh.medge[i_dst];
-
- e_dst = e_src;
- e_dst.v1 = e_src.v1;
- e_dst.v2 = e_src.v2;
+ dst_edges[i_dst] = src_edges[i_src];
}
}
@@ -217,14 +215,16 @@ static void copy_masked_edges_to_new_mesh(const Mesh &src_mesh,
{
BLI_assert(src_mesh.totvert == vertex_map.size());
BLI_assert(src_mesh.totedge == edge_map.size());
+ const Span<MEdge> src_edges = src_mesh.edges();
+ MutableSpan<MEdge> dst_edges = dst_mesh.edges_for_write();
+
for (const int i_src : IndexRange(src_mesh.totedge)) {
const int i_dst = edge_map[i_src];
if (i_dst == -1) {
continue;
}
-
- const MEdge &e_src = src_mesh.medge[i_src];
- MEdge &e_dst = dst_mesh.medge[i_dst];
+ const MEdge &e_src = src_edges[i_src];
+ MEdge &e_dst = dst_edges[i_dst];
e_dst = e_src;
e_dst.v1 = vertex_map[e_src.v1];
@@ -239,16 +239,21 @@ static void copy_masked_polys_to_new_mesh(const Mesh &src_mesh,
Span<int> masked_poly_indices,
Span<int> new_loop_starts)
{
+ const Span<MPoly> src_polys = src_mesh.polys();
+ const Span<MLoop> src_loops = src_mesh.loops();
+ MutableSpan<MPoly> dst_polys = dst_mesh.polys_for_write();
+ MutableSpan<MLoop> dst_loops = dst_mesh.loops_for_write();
+
for (const int i_dst : masked_poly_indices.index_range()) {
const int i_src = masked_poly_indices[i_dst];
- const MPoly &mp_src = src_mesh.mpoly[i_src];
- MPoly &mp_dst = dst_mesh.mpoly[i_dst];
+ const MPoly &mp_src = src_polys[i_src];
+ MPoly &mp_dst = dst_polys[i_dst];
const int i_ml_src = mp_src.loopstart;
const int i_ml_dst = new_loop_starts[i_dst];
- const MLoop *ml_src = src_mesh.mloop + i_ml_src;
- MLoop *ml_dst = dst_mesh.mloop + i_ml_dst;
+ const MLoop *ml_src = &src_loops[i_ml_src];
+ MLoop *ml_dst = &dst_loops[i_ml_dst];
mp_dst = mp_src;
mp_dst.loopstart = i_ml_dst;
@@ -265,16 +270,21 @@ static void copy_masked_polys_to_new_mesh(const Mesh &src_mesh,
Span<int> masked_poly_indices,
Span<int> new_loop_starts)
{
+ const Span<MPoly> src_polys = src_mesh.polys();
+ const Span<MLoop> src_loops = src_mesh.loops();
+ MutableSpan<MPoly> dst_polys = dst_mesh.polys_for_write();
+ MutableSpan<MLoop> dst_loops = dst_mesh.loops_for_write();
+
for (const int i_dst : masked_poly_indices.index_range()) {
const int i_src = masked_poly_indices[i_dst];
- const MPoly &mp_src = src_mesh.mpoly[i_src];
- MPoly &mp_dst = dst_mesh.mpoly[i_dst];
+ const MPoly &mp_src = src_polys[i_src];
+ MPoly &mp_dst = dst_polys[i_dst];
const int i_ml_src = mp_src.loopstart;
const int i_ml_dst = new_loop_starts[i_dst];
- const MLoop *ml_src = src_mesh.mloop + i_ml_src;
- MLoop *ml_dst = dst_mesh.mloop + i_ml_dst;
+ const MLoop *ml_src = &src_loops[i_ml_src];
+ MLoop *ml_dst = &dst_loops[i_ml_dst];
mp_dst = mp_src;
mp_dst.loopstart = i_ml_dst;
@@ -292,16 +302,21 @@ static void copy_masked_polys_to_new_mesh(const Mesh &src_mesh,
Span<int> masked_poly_indices,
Span<int> new_loop_starts)
{
+ const Span<MPoly> src_polys = src_mesh.polys();
+ const Span<MLoop> src_loops = src_mesh.loops();
+ MutableSpan<MPoly> dst_polys = dst_mesh.polys_for_write();
+ MutableSpan<MLoop> dst_loops = dst_mesh.loops_for_write();
+
for (const int i_dst : masked_poly_indices.index_range()) {
const int i_src = masked_poly_indices[i_dst];
- const MPoly &mp_src = src_mesh.mpoly[i_src];
- MPoly &mp_dst = dst_mesh.mpoly[i_dst];
+ const MPoly &mp_src = src_polys[i_src];
+ MPoly &mp_dst = dst_polys[i_dst];
const int i_ml_src = mp_src.loopstart;
const int i_ml_dst = new_loop_starts[i_dst];
- const MLoop *ml_src = src_mesh.mloop + i_ml_src;
- MLoop *ml_dst = dst_mesh.mloop + i_ml_dst;
+ const MLoop *ml_src = &src_loops[i_ml_src];
+ MLoop *ml_dst = &dst_loops[i_ml_dst];
mp_dst = mp_src;
mp_dst.loopstart = i_ml_dst;
@@ -316,18 +331,19 @@ static void delete_curves_selection(GeometrySet &geometry_set,
const Field<bool> &selection_field,
const eAttrDomain selection_domain)
{
- const CurveComponent &src_component = *geometry_set.get_component_for_read<CurveComponent>();
- GeometryComponentFieldContext field_context{src_component, selection_domain};
+ const Curves &src_curves_id = *geometry_set.get_curves_for_read();
+ const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(src_curves_id.geometry);
- const int domain_num = src_component.attribute_domain_size(selection_domain);
- fn::FieldEvaluator evaluator{field_context, domain_num};
+ const int domain_size = src_curves.attributes().domain_size(selection_domain);
+ bke::CurvesFieldContext field_context{src_curves, selection_domain};
+ fn::FieldEvaluator evaluator{field_context, domain_size};
evaluator.set_selection(selection_field);
evaluator.evaluate();
const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
if (selection.is_empty()) {
return;
}
- if (selection.size() == domain_num) {
+ if (selection.size() == domain_size) {
geometry_set.remove<CurveComponent>();
return;
}
@@ -347,11 +363,10 @@ static void delete_curves_selection(GeometrySet &geometry_set,
static void separate_point_cloud_selection(GeometrySet &geometry_set,
const Field<bool> &selection_field)
{
- const PointCloudComponent &src_points =
- *geometry_set.get_component_for_read<PointCloudComponent>();
- GeometryComponentFieldContext field_context{src_points, ATTR_DOMAIN_POINT};
+ const PointCloud &src_pointcloud = *geometry_set.get_pointcloud_for_read();
- fn::FieldEvaluator evaluator{field_context, src_points.attribute_domain_size(ATTR_DOMAIN_POINT)};
+ bke::PointCloudFieldContext field_context{src_pointcloud};
+ fn::FieldEvaluator evaluator{field_context, src_pointcloud.totpoint};
evaluator.set_selection(selection_field);
evaluator.evaluate();
const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
@@ -367,8 +382,8 @@ static void separate_point_cloud_selection(GeometrySet &geometry_set,
{GEO_COMPONENT_TYPE_POINT_CLOUD}, GEO_COMPONENT_TYPE_POINT_CLOUD, false, attributes);
copy_attributes_based_on_mask(attributes,
- bke::pointcloud_attributes(*src_points.get_for_read()),
- bke::pointcloud_attributes_for_write(*pointcloud),
+ src_pointcloud.attributes(),
+ pointcloud->attributes_for_write(),
ATTR_DOMAIN_POINT,
selection);
geometry_set.replace_pointcloud(pointcloud);
@@ -378,7 +393,7 @@ static void delete_selected_instances(GeometrySet &geometry_set,
const Field<bool> &selection_field)
{
InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>();
- GeometryComponentFieldContext field_context{instances, ATTR_DOMAIN_INSTANCE};
+ bke::GeometryFieldContext field_context{instances, ATTR_DOMAIN_INSTANCE};
fn::FieldEvaluator evaluator{field_context, instances.instances_num()};
evaluator.set_selection(selection_field);
@@ -392,9 +407,9 @@ static void delete_selected_instances(GeometrySet &geometry_set,
instances.remove_instances(selection);
}
-static void compute_selected_vertices_from_vertex_selection(const Span<bool> vertex_selection,
- MutableSpan<int> r_vertex_map,
- int *r_selected_vertices_num)
+static void compute_selected_verts_from_vertex_selection(const Span<bool> vertex_selection,
+ MutableSpan<int> r_vertex_map,
+ int *r_selected_verts_num)
{
BLI_assert(vertex_selection.size() == r_vertex_map.size());
@@ -409,7 +424,7 @@ static void compute_selected_vertices_from_vertex_selection(const Span<bool> ver
}
}
- *r_selected_vertices_num = selected_verts_num;
+ *r_selected_verts_num = selected_verts_num;
}
static void compute_selected_edges_from_vertex_selection(const Mesh &mesh,
@@ -418,10 +433,11 @@ static void compute_selected_edges_from_vertex_selection(const Mesh &mesh,
int *r_selected_edges_num)
{
BLI_assert(mesh.totedge == r_edge_map.size());
+ const Span<MEdge> edges = mesh.edges();
int selected_edges_num = 0;
for (const int i : IndexRange(mesh.totedge)) {
- const MEdge &edge = mesh.medge[i];
+ const MEdge &edge = edges[i];
/* Only add the edge if both vertices will be in the new mesh. */
if (vertex_selection[edge.v1] && vertex_selection[edge.v2]) {
@@ -436,25 +452,27 @@ static void compute_selected_edges_from_vertex_selection(const Mesh &mesh,
*r_selected_edges_num = selected_edges_num;
}
-static void compute_selected_polygons_from_vertex_selection(const Mesh &mesh,
- const Span<bool> vertex_selection,
- Vector<int> &r_selected_poly_indices,
- Vector<int> &r_loop_starts,
- int *r_selected_polys_num,
- int *r_selected_loops_num)
+static void compute_selected_polys_from_vertex_selection(const Mesh &mesh,
+ const Span<bool> vertex_selection,
+ Vector<int> &r_selected_poly_indices,
+ Vector<int> &r_loop_starts,
+ int *r_selected_polys_num,
+ int *r_selected_loops_num)
{
BLI_assert(mesh.totvert == vertex_selection.size());
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
r_selected_poly_indices.reserve(mesh.totpoly);
r_loop_starts.reserve(mesh.totloop);
int selected_loops_num = 0;
- for (const int i : IndexRange(mesh.totpoly)) {
- const MPoly &poly_src = mesh.mpoly[i];
+ for (const int i : polys.index_range()) {
+ const MPoly &poly_src = polys[i];
bool all_verts_in_selection = true;
- Span<MLoop> loops_src(&mesh.mloop[poly_src.loopstart], poly_src.totloop);
- for (const MLoop &loop : loops_src) {
+ const Span<MLoop> poly_loops = loops.slice(poly_src.loopstart, poly_src.totloop);
+ for (const MLoop &loop : poly_loops) {
if (!vertex_selection[loop.v]) {
all_verts_in_selection = false;
break;
@@ -476,20 +494,20 @@ static void compute_selected_polygons_from_vertex_selection(const Mesh &mesh,
* Checks for every edge if it is in `edge_selection`. If it is, then the two vertices of the
* edge are kept along with the edge.
*/
-static void compute_selected_vertices_and_edges_from_edge_selection(
- const Mesh &mesh,
- const Span<bool> edge_selection,
- MutableSpan<int> r_vertex_map,
- MutableSpan<int> r_edge_map,
- int *r_selected_vertices_num,
- int *r_selected_edges_num)
+static void compute_selected_verts_and_edges_from_edge_selection(const Mesh &mesh,
+ const Span<bool> edge_selection,
+ MutableSpan<int> r_vertex_map,
+ MutableSpan<int> r_edge_map,
+ int *r_selected_verts_num,
+ int *r_selected_edges_num)
{
BLI_assert(mesh.totedge == edge_selection.size());
+ const Span<MEdge> edges = mesh.edges();
int selected_edges_num = 0;
int selected_verts_num = 0;
for (const int i : IndexRange(mesh.totedge)) {
- const MEdge &edge = mesh.medge[i];
+ const MEdge &edge = edges[i];
if (edge_selection[i]) {
r_edge_map[i] = selected_edges_num;
selected_edges_num++;
@@ -507,7 +525,7 @@ static void compute_selected_vertices_and_edges_from_edge_selection(
}
}
- *r_selected_vertices_num = selected_verts_num;
+ *r_selected_verts_num = selected_verts_num;
*r_selected_edges_num = selected_edges_num;
}
@@ -539,23 +557,26 @@ static void compute_selected_edges_from_edge_selection(const Mesh &mesh,
* Checks for every polygon if all the edges are in `edge_selection`. If they are, then that
* polygon is kept.
*/
-static void compute_selected_polygons_from_edge_selection(const Mesh &mesh,
- const Span<bool> edge_selection,
- Vector<int> &r_selected_poly_indices,
- Vector<int> &r_loop_starts,
- int *r_selected_polys_num,
- int *r_selected_loops_num)
+static void compute_selected_polys_from_edge_selection(const Mesh &mesh,
+ const Span<bool> edge_selection,
+ Vector<int> &r_selected_poly_indices,
+ Vector<int> &r_loop_starts,
+ int *r_selected_polys_num,
+ int *r_selected_loops_num)
{
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
+
r_selected_poly_indices.reserve(mesh.totpoly);
r_loop_starts.reserve(mesh.totloop);
int selected_loops_num = 0;
- for (const int i : IndexRange(mesh.totpoly)) {
- const MPoly &poly_src = mesh.mpoly[i];
+ for (const int i : polys.index_range()) {
+ const MPoly &poly_src = polys[i];
bool all_edges_in_selection = true;
- Span<MLoop> loops_src(&mesh.mloop[poly_src.loopstart], poly_src.totloop);
- for (const MLoop &loop : loops_src) {
+ const Span<MLoop> poly_loops = loops.slice(poly_src.loopstart, poly_src.totloop);
+ for (const MLoop &loop : poly_loops) {
if (!edge_selection[loop.e]) {
all_edges_in_selection = false;
break;
@@ -590,12 +611,12 @@ static void compute_selected_mesh_data_from_vertex_selection_edge_face(
compute_selected_edges_from_vertex_selection(
mesh, vertex_selection, r_edge_map, r_selected_edges_num);
- compute_selected_polygons_from_vertex_selection(mesh,
- vertex_selection,
- r_selected_poly_indices,
- r_loop_starts,
- r_selected_polys_num,
- r_selected_loops_num);
+ compute_selected_polys_from_vertex_selection(mesh,
+ vertex_selection,
+ r_selected_poly_indices,
+ r_loop_starts,
+ r_selected_polys_num,
+ r_selected_loops_num);
}
/**
@@ -608,23 +629,23 @@ static void compute_selected_mesh_data_from_vertex_selection(const Mesh &mesh,
MutableSpan<int> r_edge_map,
Vector<int> &r_selected_poly_indices,
Vector<int> &r_loop_starts,
- int *r_selected_vertices_num,
+ int *r_selected_verts_num,
int *r_selected_edges_num,
int *r_selected_polys_num,
int *r_selected_loops_num)
{
- compute_selected_vertices_from_vertex_selection(
- vertex_selection, r_vertex_map, r_selected_vertices_num);
+ compute_selected_verts_from_vertex_selection(
+ vertex_selection, r_vertex_map, r_selected_verts_num);
compute_selected_edges_from_vertex_selection(
mesh, vertex_selection, r_edge_map, r_selected_edges_num);
- compute_selected_polygons_from_vertex_selection(mesh,
- vertex_selection,
- r_selected_poly_indices,
- r_loop_starts,
- r_selected_polys_num,
- r_selected_loops_num);
+ compute_selected_polys_from_vertex_selection(mesh,
+ vertex_selection,
+ r_selected_poly_indices,
+ r_loop_starts,
+ r_selected_polys_num,
+ r_selected_loops_num);
}
/**
@@ -643,17 +664,17 @@ static void compute_selected_mesh_data_from_edge_selection_edge_face(
{
compute_selected_edges_from_edge_selection(
mesh, edge_selection, r_edge_map, r_selected_edges_num);
- compute_selected_polygons_from_edge_selection(mesh,
- edge_selection,
- r_selected_poly_indices,
- r_loop_starts,
- r_selected_polys_num,
- r_selected_loops_num);
+ compute_selected_polys_from_edge_selection(mesh,
+ edge_selection,
+ r_selected_poly_indices,
+ r_loop_starts,
+ r_selected_polys_num,
+ r_selected_loops_num);
}
/**
* Checks for every edge if it is in `edge_selection`. If it is, the vertices belonging to
- * that edge are kept as well. The polygons are kept if all edges are in the selection.
+ * that edge are kept as well. The polys are kept if all edges are in the selection.
*/
static void compute_selected_mesh_data_from_edge_selection(const Mesh &mesh,
const Span<bool> edge_selection,
@@ -661,44 +682,41 @@ static void compute_selected_mesh_data_from_edge_selection(const Mesh &mesh,
MutableSpan<int> r_edge_map,
Vector<int> &r_selected_poly_indices,
Vector<int> &r_loop_starts,
- int *r_selected_vertices_num,
+ int *r_selected_verts_num,
int *r_selected_edges_num,
int *r_selected_polys_num,
int *r_selected_loops_num)
{
r_vertex_map.fill(-1);
- compute_selected_vertices_and_edges_from_edge_selection(mesh,
- edge_selection,
- r_vertex_map,
- r_edge_map,
- r_selected_vertices_num,
- r_selected_edges_num);
- compute_selected_polygons_from_edge_selection(mesh,
- edge_selection,
- r_selected_poly_indices,
- r_loop_starts,
- r_selected_polys_num,
- r_selected_loops_num);
+ compute_selected_verts_and_edges_from_edge_selection(
+ mesh, edge_selection, r_vertex_map, r_edge_map, r_selected_verts_num, r_selected_edges_num);
+ compute_selected_polys_from_edge_selection(mesh,
+ edge_selection,
+ r_selected_poly_indices,
+ r_loop_starts,
+ r_selected_polys_num,
+ r_selected_loops_num);
}
/**
* Checks for every polygon if it is in `poly_selection`.
*/
-static void compute_selected_polygons_from_poly_selection(const Mesh &mesh,
- const Span<bool> poly_selection,
- Vector<int> &r_selected_poly_indices,
- Vector<int> &r_loop_starts,
- int *r_selected_polys_num,
- int *r_selected_loops_num)
+static void compute_selected_polys_from_poly_selection(const Mesh &mesh,
+ const Span<bool> poly_selection,
+ Vector<int> &r_selected_poly_indices,
+ Vector<int> &r_loop_starts,
+ int *r_selected_polys_num,
+ int *r_selected_loops_num)
{
BLI_assert(mesh.totpoly == poly_selection.size());
+ const Span<MPoly> polys = mesh.polys();
r_selected_poly_indices.reserve(mesh.totpoly);
r_loop_starts.reserve(mesh.totloop);
int selected_loops_num = 0;
- for (const int i : IndexRange(mesh.totpoly)) {
- const MPoly &poly_src = mesh.mpoly[i];
+ for (const int i : polys.index_range()) {
+ const MPoly &poly_src = polys[i];
/* We keep this one. */
if (poly_selection[i]) {
r_selected_poly_indices.append_unchecked(i);
@@ -725,6 +743,9 @@ static void compute_selected_mesh_data_from_poly_selection_edge_face(
{
BLI_assert(mesh.totpoly == poly_selection.size());
BLI_assert(mesh.totedge == r_edge_map.size());
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
+
r_edge_map.fill(-1);
r_selected_poly_indices.reserve(mesh.totpoly);
@@ -732,8 +753,8 @@ static void compute_selected_mesh_data_from_poly_selection_edge_face(
int selected_loops_num = 0;
int selected_edges_num = 0;
- for (const int i : IndexRange(mesh.totpoly)) {
- const MPoly &poly_src = mesh.mpoly[i];
+ for (const int i : polys.index_range()) {
+ const MPoly &poly_src = polys[i];
/* We keep this one. */
if (poly_selection[i]) {
r_selected_poly_indices.append_unchecked(i);
@@ -741,8 +762,8 @@ static void compute_selected_mesh_data_from_poly_selection_edge_face(
selected_loops_num += poly_src.totloop;
/* Add the vertices and the edges. */
- Span<MLoop> loops_src(&mesh.mloop[poly_src.loopstart], poly_src.totloop);
- for (const MLoop &loop : loops_src) {
+ const Span<MLoop> poly_loops = loops.slice(poly_src.loopstart, poly_src.totloop);
+ for (const MLoop &loop : poly_loops) {
/* Check first if it has not yet been added. */
if (r_edge_map[loop.e] == -1) {
r_edge_map[loop.e] = selected_edges_num;
@@ -766,13 +787,16 @@ static void compute_selected_mesh_data_from_poly_selection(const Mesh &mesh,
MutableSpan<int> r_edge_map,
Vector<int> &r_selected_poly_indices,
Vector<int> &r_loop_starts,
- int *r_selected_vertices_num,
+ int *r_selected_verts_num,
int *r_selected_edges_num,
int *r_selected_polys_num,
int *r_selected_loops_num)
{
BLI_assert(mesh.totpoly == poly_selection.size());
BLI_assert(mesh.totedge == r_edge_map.size());
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
+
r_vertex_map.fill(-1);
r_edge_map.fill(-1);
@@ -782,8 +806,8 @@ static void compute_selected_mesh_data_from_poly_selection(const Mesh &mesh,
int selected_loops_num = 0;
int selected_verts_num = 0;
int selected_edges_num = 0;
- for (const int i : IndexRange(mesh.totpoly)) {
- const MPoly &poly_src = mesh.mpoly[i];
+ for (const int i : polys.index_range()) {
+ const MPoly &poly_src = polys[i];
/* We keep this one. */
if (poly_selection[i]) {
r_selected_poly_indices.append_unchecked(i);
@@ -791,8 +815,8 @@ static void compute_selected_mesh_data_from_poly_selection(const Mesh &mesh,
selected_loops_num += poly_src.totloop;
/* Add the vertices and the edges. */
- Span<MLoop> loops_src(&mesh.mloop[poly_src.loopstart], poly_src.totloop);
- for (const MLoop &loop : loops_src) {
+ const Span<MLoop> poly_loops = loops.slice(poly_src.loopstart, poly_src.totloop);
+ for (const MLoop &loop : poly_loops) {
/* Check first if it has not yet been added. */
if (r_vertex_map[loop.v] == -1) {
r_vertex_map[loop.v] = selected_verts_num;
@@ -805,7 +829,7 @@ static void compute_selected_mesh_data_from_poly_selection(const Mesh &mesh,
}
}
}
- *r_selected_vertices_num = selected_verts_num;
+ *r_selected_verts_num = selected_verts_num;
*r_selected_edges_num = selected_edges_num;
*r_selected_polys_num = r_selected_poly_indices.size();
*r_selected_loops_num = selected_loops_num;
@@ -890,30 +914,30 @@ static void do_mesh_separation(GeometrySet &geometry_set,
selected_polys_num);
/* Copy the selected parts of the mesh over to the new mesh. */
- copy_masked_vertices_to_new_mesh(mesh_in, *mesh_out, vertex_map);
+ copy_masked_verts_to_new_mesh(mesh_in, *mesh_out, vertex_map);
copy_masked_edges_to_new_mesh(mesh_in, *mesh_out, vertex_map, edge_map);
copy_masked_polys_to_new_mesh(
mesh_in, *mesh_out, vertex_map, edge_map, selected_poly_indices, new_loop_starts);
/* Copy attributes. */
copy_attributes_based_on_map(attributes,
- bke::mesh_attributes(mesh_in),
- bke::mesh_attributes_for_write(*mesh_out),
+ mesh_in.attributes(),
+ mesh_out->attributes_for_write(),
ATTR_DOMAIN_POINT,
vertex_map);
copy_attributes_based_on_map(attributes,
- bke::mesh_attributes(mesh_in),
- bke::mesh_attributes_for_write(*mesh_out),
+ mesh_in.attributes(),
+ mesh_out->attributes_for_write(),
ATTR_DOMAIN_EDGE,
edge_map);
copy_attributes_based_on_mask(attributes,
- bke::mesh_attributes(mesh_in),
- bke::mesh_attributes_for_write(*mesh_out),
+ mesh_in.attributes(),
+ mesh_out->attributes_for_write(),
ATTR_DOMAIN_FACE,
IndexMask(Vector<int64_t>(selected_poly_indices.as_span())));
copy_face_corner_attributes(attributes,
- bke::mesh_attributes(mesh_in),
- bke::mesh_attributes_for_write(*mesh_out),
+ mesh_in.attributes(),
+ mesh_out->attributes_for_write(),
selected_loops_num,
selected_poly_indices,
mesh_in);
@@ -967,29 +991,27 @@ static void do_mesh_separation(GeometrySet &geometry_set,
selected_polys_num);
/* Copy the selected parts of the mesh over to the new mesh. */
- memcpy(mesh_out->mvert, mesh_in.mvert, mesh_in.totvert * sizeof(MVert));
+ mesh_out->verts_for_write().copy_from(mesh_in.verts());
copy_masked_edges_to_new_mesh(mesh_in, *mesh_out, edge_map);
copy_masked_polys_to_new_mesh(
mesh_in, *mesh_out, edge_map, selected_poly_indices, new_loop_starts);
/* Copy attributes. */
- copy_attributes(attributes,
- bke::mesh_attributes(mesh_in),
- bke::mesh_attributes_for_write(*mesh_out),
- {ATTR_DOMAIN_POINT});
+ copy_attributes(
+ attributes, mesh_in.attributes(), mesh_out->attributes_for_write(), {ATTR_DOMAIN_POINT});
copy_attributes_based_on_map(attributes,
- bke::mesh_attributes(mesh_in),
- bke::mesh_attributes_for_write(*mesh_out),
+ mesh_in.attributes(),
+ mesh_out->attributes_for_write(),
ATTR_DOMAIN_EDGE,
edge_map);
copy_attributes_based_on_mask(attributes,
- bke::mesh_attributes(mesh_in),
- bke::mesh_attributes_for_write(*mesh_out),
+ mesh_in.attributes(),
+ mesh_out->attributes_for_write(),
ATTR_DOMAIN_FACE,
IndexMask(Vector<int64_t>(selected_poly_indices.as_span())));
copy_face_corner_attributes(attributes,
- bke::mesh_attributes(mesh_in),
- bke::mesh_attributes_for_write(*mesh_out),
+ mesh_in.attributes(),
+ mesh_out->attributes_for_write(),
selected_loops_num,
selected_poly_indices,
mesh_in);
@@ -999,28 +1021,28 @@ static void do_mesh_separation(GeometrySet &geometry_set,
/* Fill all the maps based on the selection. */
switch (domain) {
case ATTR_DOMAIN_POINT:
- compute_selected_polygons_from_vertex_selection(mesh_in,
- selection,
- selected_poly_indices,
- new_loop_starts,
- &selected_polys_num,
- &selected_loops_num);
+ compute_selected_polys_from_vertex_selection(mesh_in,
+ selection,
+ selected_poly_indices,
+ new_loop_starts,
+ &selected_polys_num,
+ &selected_loops_num);
break;
case ATTR_DOMAIN_EDGE:
- compute_selected_polygons_from_edge_selection(mesh_in,
- selection,
- selected_poly_indices,
- new_loop_starts,
- &selected_polys_num,
- &selected_loops_num);
+ compute_selected_polys_from_edge_selection(mesh_in,
+ selection,
+ selected_poly_indices,
+ new_loop_starts,
+ &selected_polys_num,
+ &selected_loops_num);
break;
case ATTR_DOMAIN_FACE:
- compute_selected_polygons_from_poly_selection(mesh_in,
- selection,
- selected_poly_indices,
- new_loop_starts,
- &selected_polys_num,
- &selected_loops_num);
+ compute_selected_polys_from_poly_selection(mesh_in,
+ selection,
+ selected_poly_indices,
+ new_loop_starts,
+ &selected_polys_num,
+ &selected_loops_num);
break;
default:
BLI_assert_unreachable();
@@ -1030,23 +1052,23 @@ static void do_mesh_separation(GeometrySet &geometry_set,
&mesh_in, mesh_in.totvert, mesh_in.totedge, 0, selected_loops_num, selected_polys_num);
/* Copy the selected parts of the mesh over to the new mesh. */
- memcpy(mesh_out->mvert, mesh_in.mvert, mesh_in.totvert * sizeof(MVert));
- memcpy(mesh_out->medge, mesh_in.medge, mesh_in.totedge * sizeof(MEdge));
+ mesh_out->verts_for_write().copy_from(mesh_in.verts());
+ mesh_out->edges_for_write().copy_from(mesh_in.edges());
copy_masked_polys_to_new_mesh(mesh_in, *mesh_out, selected_poly_indices, new_loop_starts);
/* Copy attributes. */
copy_attributes(attributes,
- bke::mesh_attributes(mesh_in),
- bke::mesh_attributes_for_write(*mesh_out),
+ mesh_in.attributes(),
+ mesh_out->attributes_for_write(),
{ATTR_DOMAIN_POINT, ATTR_DOMAIN_EDGE});
copy_attributes_based_on_mask(attributes,
- bke::mesh_attributes(mesh_in),
- bke::mesh_attributes_for_write(*mesh_out),
+ mesh_in.attributes(),
+ mesh_out->attributes_for_write(),
ATTR_DOMAIN_FACE,
IndexMask(Vector<int64_t>(selected_poly_indices.as_span())));
copy_face_corner_attributes(attributes,
- bke::mesh_attributes(mesh_in),
- bke::mesh_attributes_for_write(*mesh_out),
+ mesh_in.attributes(),
+ mesh_out->attributes_for_write(),
selected_loops_num,
selected_poly_indices,
mesh_in);
@@ -1063,11 +1085,9 @@ static void separate_mesh_selection(GeometrySet &geometry_set,
const eAttrDomain selection_domain,
const GeometryNodeDeleteGeometryMode mode)
{
- const MeshComponent &src_component = *geometry_set.get_component_for_read<MeshComponent>();
- GeometryComponentFieldContext field_context{src_component, selection_domain};
-
- fn::FieldEvaluator evaluator{field_context,
- src_component.attribute_domain_size(selection_domain)};
+ const Mesh &src_mesh = *geometry_set.get_mesh_for_read();
+ bke::MeshFieldContext field_context{src_mesh, selection_domain};
+ fn::FieldEvaluator evaluator{field_context, src_mesh.attributes().domain_size(selection_domain)};
evaluator.add(selection_field);
evaluator.evaluate();
const VArray<bool> selection = evaluator.get_evaluated<bool>(0);
@@ -1078,8 +1098,7 @@ static void separate_mesh_selection(GeometrySet &geometry_set,
const VArraySpan<bool> selection_span{selection};
- do_mesh_separation(
- geometry_set, *src_component.get_for_read(), selection_span, selection_domain, mode);
+ do_mesh_separation(geometry_set, src_mesh, selection_span, selection_domain, mode);
}
} // namespace blender::nodes::node_geo_delete_geometry_cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_in_volume.cc b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_in_volume.cc
new file mode 100644
index 00000000000..a0bd28218cc
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_in_volume.cc
@@ -0,0 +1,285 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#ifdef WITH_OPENVDB
+# include <openvdb/openvdb.h>
+# include <openvdb/tools/Interpolation.h>
+# include <openvdb/tools/PointScatter.h>
+#endif
+
+#include "DNA_node_types.h"
+#include "DNA_pointcloud_types.h"
+
+#include "BKE_pointcloud.h"
+#include "BKE_volume.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes {
+
+NODE_STORAGE_FUNCS(NodeGeometryDistributePointsInVolume)
+
+static void geo_node_distribute_points_in_volume_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>(N_("Volume")).supported_type(GEO_COMPONENT_TYPE_VOLUME);
+ b.add_input<decl::Float>(N_("Density"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(100000.0f)
+ .subtype(PROP_NONE)
+ .description(N_("Number of points to sample per unit volume"));
+ b.add_input<decl::Int>(N_("Seed"))
+ .min(-10000)
+ .max(10000)
+ .description(N_("Seed used by the random number generator to generate random points"));
+ b.add_input<decl::Vector>(N_("Spacing"))
+ .default_value({0.3, 0.3, 0.3})
+ .min(0.0001f)
+ .subtype(PROP_XYZ)
+ .description(N_("Spacing between grid points"));
+ b.add_input<decl::Float>(N_("Threshold"))
+ .default_value(0.1f)
+ .min(0.0f)
+ .max(FLT_MAX)
+ .description(N_("Minimum density of a volume cell to contain a grid point"));
+ b.add_output<decl::Geometry>(N_("Points"));
+}
+
+static void geo_node_distribute_points_in_volume_layout(uiLayout *layout,
+ bContext *UNUSED(C),
+ PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "mode", 0, "", ICON_NONE);
+}
+
+static void node_distribute_points_in_volume_init(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeGeometryDistributePointsInVolume *data = MEM_cnew<NodeGeometryDistributePointsInVolume>(
+ __func__);
+ data->mode = GEO_NODE_DISTRIBUTE_POINTS_IN_VOLUME_DENSITY_RANDOM;
+ node->storage = data;
+}
+
+static void node_distribute_points_in_volume_update(bNodeTree *ntree, bNode *node)
+{
+ const NodeGeometryDistributePointsInVolume &storage = node_storage(*node);
+ GeometryNodeDistributePointsInVolumeMode mode =
+ static_cast<GeometryNodeDistributePointsInVolumeMode>(storage.mode);
+
+ bNodeSocket *sock_density = ((bNodeSocket *)(node->inputs.first))->next;
+ bNodeSocket *sock_seed = sock_density->next;
+ bNodeSocket *sock_spacing = sock_seed->next;
+ bNodeSocket *sock_threshold = sock_spacing->next;
+
+ nodeSetSocketAvailability(
+ ntree, sock_density, mode == GEO_NODE_DISTRIBUTE_POINTS_IN_VOLUME_DENSITY_RANDOM);
+ nodeSetSocketAvailability(
+ ntree, sock_seed, mode == GEO_NODE_DISTRIBUTE_POINTS_IN_VOLUME_DENSITY_RANDOM);
+ nodeSetSocketAvailability(
+ ntree, sock_spacing, mode == GEO_NODE_DISTRIBUTE_POINTS_IN_VOLUME_DENSITY_GRID);
+ nodeSetSocketAvailability(
+ ntree, sock_threshold, mode == GEO_NODE_DISTRIBUTE_POINTS_IN_VOLUME_DENSITY_GRID);
+}
+
+#ifdef WITH_OPENVDB
+/* Implements the interface required by #openvdb::tools::NonUniformPointScatter. */
+class PositionsVDBWrapper {
+ private:
+ float3 offset_fix_;
+ Vector<float3> &vector_;
+
+ public:
+ PositionsVDBWrapper(Vector<float3> &vector, const float3 offset_fix)
+ : offset_fix_(offset_fix), vector_(vector)
+ {
+ }
+ PositionsVDBWrapper(const PositionsVDBWrapper &wrapper) = default;
+
+ void add(const openvdb::Vec3R &pos)
+ {
+ vector_.append((float3((float)pos[0], (float)pos[1], (float)pos[2]) + offset_fix_));
+ }
+};
+
+/* Use #std::mt19937 as a random number generator,
+ * it has a very long period and thus there should be no visible patterns in the generated points.
+ */
+using RNGType = std::mt19937;
+/* Non-uniform scatter allows the amount of points to be scaled with the volume's density. */
+using NonUniformPointScatterVDB =
+ openvdb::tools::NonUniformPointScatter<PositionsVDBWrapper, RNGType>;
+
+static void point_scatter_density_random(const openvdb::FloatGrid &grid,
+ const float density,
+ const int seed,
+ Vector<float3> &r_positions)
+{
+ /* Offset points by half a voxel so that grid points are aligned with world grid points. */
+ const float3 offset_fix = {0.5f * (float)grid.voxelSize().x(),
+ 0.5f * (float)grid.voxelSize().y(),
+ 0.5f * (float)grid.voxelSize().z()};
+ /* Setup and call into OpenVDB's point scatter API. */
+ PositionsVDBWrapper vdb_position_wrapper = PositionsVDBWrapper(r_positions, offset_fix);
+ RNGType random_generator(seed);
+ NonUniformPointScatterVDB point_scatter(vdb_position_wrapper, density, random_generator);
+ point_scatter(grid);
+}
+
+static void point_scatter_density_grid(const openvdb::FloatGrid &grid,
+ const float3 spacing,
+ const float threshold,
+ Vector<float3> &r_positions)
+{
+ const openvdb::Vec3d half_voxel(0.5, 0.5, 0.5);
+ const openvdb::Vec3d voxel_spacing((double)spacing.x / grid.voxelSize().x(),
+ (double)spacing.y / grid.voxelSize().y(),
+ (double)spacing.z / grid.voxelSize().z());
+
+ /* Abort if spacing is zero. */
+ const double min_spacing = std::min(voxel_spacing.x(),
+ std::min(voxel_spacing.y(), voxel_spacing.z()));
+ if (std::abs(min_spacing) < 0.0001) {
+ return;
+ }
+
+ /* Iterate through tiles and voxels on the grid. */
+ for (openvdb::FloatGrid::ValueOnCIter cell = grid.cbeginValueOn(); cell; ++cell) {
+ /* Check if the cell's value meets the minimum threshold. */
+ if (cell.getValue() < threshold) {
+ continue;
+ }
+ /* Compute the bounding box of each tile/voxel. */
+ const openvdb::CoordBBox bbox = cell.getBoundingBox();
+ const openvdb::Vec3d box_min = bbox.min().asVec3d() - half_voxel;
+ const openvdb::Vec3d box_max = bbox.max().asVec3d() + half_voxel;
+
+ /* Pick a starting point rounded up to the nearest possible point. */
+ double abs_spacing_x = std::abs(voxel_spacing.x());
+ double abs_spacing_y = std::abs(voxel_spacing.y());
+ double abs_spacing_z = std::abs(voxel_spacing.z());
+ const openvdb::Vec3d start(ceil(box_min.x() / abs_spacing_x) * abs_spacing_x,
+ ceil(box_min.y() / abs_spacing_y) * abs_spacing_y,
+ ceil(box_min.z() / abs_spacing_z) * abs_spacing_z);
+
+ /* Iterate through all possible points in box. */
+ for (double x = start.x(); x < box_max.x(); x += abs_spacing_x) {
+ for (double y = start.y(); y < box_max.y(); y += abs_spacing_y) {
+ for (double z = start.z(); z < box_max.z(); z += abs_spacing_z) {
+ /* Transform with grid matrix and add point. */
+ const openvdb::Vec3d idx_pos(x, y, z);
+ const openvdb::Vec3d local_pos = grid.indexToWorld(idx_pos + half_voxel);
+ r_positions.append({(float)local_pos.x(), (float)local_pos.y(), (float)local_pos.z()});
+ }
+ }
+ }
+ }
+}
+
+#endif /* WITH_OPENVDB */
+
+static void geo_node_distribute_points_in_volume_exec(GeoNodeExecParams params)
+{
+#ifdef WITH_OPENVDB
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Volume");
+
+ const NodeGeometryDistributePointsInVolume &storage = node_storage(params.node());
+ const GeometryNodeDistributePointsInVolumeMode mode =
+ static_cast<GeometryNodeDistributePointsInVolumeMode>(storage.mode);
+
+ float density;
+ int seed;
+ float3 spacing{0, 0, 0};
+ float threshold;
+ if (mode == GEO_NODE_DISTRIBUTE_POINTS_IN_VOLUME_DENSITY_RANDOM) {
+ density = params.extract_input<float>("Density");
+ seed = params.extract_input<int>("Seed");
+ }
+ else if (mode == GEO_NODE_DISTRIBUTE_POINTS_IN_VOLUME_DENSITY_GRID) {
+ spacing = params.extract_input<float3>("Spacing");
+ threshold = params.extract_input<float>("Threshold");
+ }
+
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ if (!geometry_set.has_volume()) {
+ geometry_set.keep_only({GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_INSTANCES});
+ return;
+ }
+ const VolumeComponent *component = geometry_set.get_component_for_read<VolumeComponent>();
+ const Volume *volume = component->get_for_read();
+
+ Vector<float3> positions;
+
+ for (const int i : IndexRange(BKE_volume_num_grids(volume))) {
+ const VolumeGrid *volume_grid = BKE_volume_grid_get_for_read(volume, i);
+ if (volume_grid == nullptr) {
+ continue;
+ }
+
+ openvdb::GridBase::ConstPtr base_grid = BKE_volume_grid_openvdb_for_read(volume,
+ volume_grid);
+ if (!base_grid) {
+ continue;
+ }
+
+ if (!base_grid->isType<openvdb::FloatGrid>()) {
+ continue;
+ }
+
+ const openvdb::FloatGrid::ConstPtr grid = openvdb::gridConstPtrCast<openvdb::FloatGrid>(
+ base_grid);
+
+ if (mode == GEO_NODE_DISTRIBUTE_POINTS_IN_VOLUME_DENSITY_RANDOM) {
+ point_scatter_density_random(*grid, density, seed, positions);
+ }
+ else if (mode == GEO_NODE_DISTRIBUTE_POINTS_IN_VOLUME_DENSITY_GRID) {
+ point_scatter_density_grid(*grid, spacing, threshold, positions);
+ }
+ }
+
+ PointCloud *pointcloud = BKE_pointcloud_new_nomain(positions.size());
+ bke::MutableAttributeAccessor point_attributes = pointcloud->attributes_for_write();
+ bke::SpanAttributeWriter<float3> point_positions =
+ point_attributes.lookup_or_add_for_write_only_span<float3>("position", ATTR_DOMAIN_POINT);
+ bke::SpanAttributeWriter<float> point_radii =
+ point_attributes.lookup_or_add_for_write_only_span<float>("radius", ATTR_DOMAIN_POINT);
+
+ point_positions.span.copy_from(positions);
+ point_radii.span.fill(0.05f);
+ point_positions.finish();
+ point_radii.finish();
+
+ geometry_set.replace_pointcloud(pointcloud);
+ geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_POINT_CLOUD});
+ });
+
+ params.set_output("Points", std::move(geometry_set));
+
+#else
+ params.set_default_remaining_outputs();
+ params.error_message_add(NodeWarningType::Error,
+ TIP_("Disabled, Blender was compiled without OpenVDB"));
+#endif
+}
+} // namespace blender::nodes
+
+void register_node_type_geo_distribute_points_in_volume()
+{
+ static bNodeType ntype;
+ geo_node_type_base(&ntype,
+ GEO_NODE_DISTRIBUTE_POINTS_IN_VOLUME,
+ "Distribute Points in Volume",
+ NODE_CLASS_GEOMETRY);
+ node_type_storage(&ntype,
+ "NodeGeometryDistributePointsInVolume",
+ node_free_standard_storage,
+ node_copy_standard_storage);
+ node_type_init(&ntype, blender::nodes::node_distribute_points_in_volume_init);
+ node_type_update(&ntype, blender::nodes::node_distribute_points_in_volume_update);
+ node_type_size(&ntype, 170, 100, 320);
+ ntype.declare = blender::nodes::geo_node_distribute_points_in_volume_declare;
+ ntype.geometry_node_execute = blender::nodes::geo_node_distribute_points_in_volume_exec;
+ ntype.draw_buttons = blender::nodes::geo_node_distribute_points_in_volume_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 a6c67cac916..cdcb16985ac 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc
@@ -105,6 +105,8 @@ static void sample_mesh_surface(const Mesh &mesh,
Vector<float3> &r_bary_coords,
Vector<int> &r_looptri_indices)
{
+ const Span<MVert> verts = mesh.verts();
+ const Span<MLoop> loops = mesh.loops();
const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&mesh),
BKE_mesh_runtime_looptri_len(&mesh)};
@@ -113,12 +115,12 @@ static void sample_mesh_surface(const Mesh &mesh,
const int v0_loop = looptri.tri[0];
const int v1_loop = looptri.tri[1];
const int v2_loop = looptri.tri[2];
- const int v0_index = mesh.mloop[v0_loop].v;
- const int v1_index = mesh.mloop[v1_loop].v;
- const int v2_index = mesh.mloop[v2_loop].v;
- const float3 v0_pos = float3(mesh.mvert[v0_index].co);
- const float3 v1_pos = float3(mesh.mvert[v1_index].co);
- const float3 v2_pos = float3(mesh.mvert[v2_index].co);
+ const int v0_index = loops[v0_loop].v;
+ const int v1_index = loops[v1_loop].v;
+ const int v2_index = loops[v2_loop].v;
+ const float3 v0_pos = verts[v0_index].co;
+ const float3 v1_pos = verts[v1_index].co;
+ const float3 v2_pos = verts[v2_index].co;
float looptri_density_factor = 1.0f;
if (!density_factors.is_empty()) {
@@ -283,15 +285,14 @@ BLI_NOINLINE static void interpolate_attribute(const Mesh &mesh,
}
BLI_NOINLINE static void propagate_existing_attributes(
- const MeshComponent &mesh_component,
+ const Mesh &mesh,
const Map<AttributeIDRef, AttributeKind> &attributes,
- GeometryComponent &point_component,
+ PointCloud &points,
const Span<float3> bary_coords,
const Span<int> looptri_indices)
{
- const Mesh &mesh = *mesh_component.get_for_read();
- const AttributeAccessor mesh_attributes = *mesh_component.attributes();
- MutableAttributeAccessor point_attributes = *point_component.attributes_for_write();
+ const AttributeAccessor mesh_attributes = mesh.attributes();
+ MutableAttributeAccessor point_attributes = points.attributes_for_write();
for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
const AttributeIDRef attribute_id = entry.key;
@@ -326,30 +327,31 @@ struct AttributeOutputs {
};
} // namespace
-BLI_NOINLINE static void compute_attribute_outputs(const MeshComponent &mesh_component,
- PointCloudComponent &point_component,
+BLI_NOINLINE static void compute_attribute_outputs(const Mesh &mesh,
+ PointCloud &points,
const Span<float3> bary_coords,
const Span<int> looptri_indices,
const AttributeOutputs &attribute_outputs)
{
- MutableAttributeAccessor pointcloud_attributes = *point_component.attributes_for_write();
+ MutableAttributeAccessor point_attributes = points.attributes_for_write();
- SpanAttributeWriter<int> ids = pointcloud_attributes.lookup_or_add_for_write_only_span<int>(
+ SpanAttributeWriter<int> ids = point_attributes.lookup_or_add_for_write_only_span<int>(
"id", ATTR_DOMAIN_POINT);
SpanAttributeWriter<float3> normals;
SpanAttributeWriter<float3> rotations;
if (attribute_outputs.normal_id) {
- normals = pointcloud_attributes.lookup_or_add_for_write_only_span<float3>(
+ normals = point_attributes.lookup_or_add_for_write_only_span<float3>(
attribute_outputs.normal_id.get(), ATTR_DOMAIN_POINT);
}
if (attribute_outputs.rotation_id) {
- rotations = pointcloud_attributes.lookup_or_add_for_write_only_span<float3>(
+ rotations = point_attributes.lookup_or_add_for_write_only_span<float3>(
attribute_outputs.rotation_id.get(), ATTR_DOMAIN_POINT);
}
- const Mesh &mesh = *mesh_component.get_for_read();
+ const Span<MVert> verts = mesh.verts();
+ const Span<MLoop> loops = mesh.loops();
const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&mesh),
BKE_mesh_runtime_looptri_len(&mesh)};
@@ -358,12 +360,12 @@ BLI_NOINLINE static void compute_attribute_outputs(const MeshComponent &mesh_com
const MLoopTri &looptri = looptris[looptri_index];
const float3 &bary_coord = bary_coords[i];
- const int v0_index = mesh.mloop[looptri.tri[0]].v;
- const int v1_index = mesh.mloop[looptri.tri[1]].v;
- const int v2_index = mesh.mloop[looptri.tri[2]].v;
- const float3 v0_pos = float3(mesh.mvert[v0_index].co);
- const float3 v1_pos = float3(mesh.mvert[v1_index].co);
- const float3 v2_pos = float3(mesh.mvert[v2_index].co);
+ const int v0_index = loops[looptri.tri[0]].v;
+ const int v1_index = loops[looptri.tri[1]].v;
+ const int v2_index = loops[looptri.tri[2]].v;
+ const float3 v0_pos = verts[v0_index].co;
+ const float3 v1_pos = verts[v1_index].co;
+ const float3 v2_pos = verts[v2_index].co;
ids.span[i] = noise::hash(noise::hash_float(bary_coord), looptri_index);
@@ -380,25 +382,19 @@ BLI_NOINLINE static void compute_attribute_outputs(const MeshComponent &mesh_com
}
ids.finish();
-
- if (normals) {
- normals.finish();
- }
- if (rotations) {
- rotations.finish();
- }
+ normals.finish();
+ rotations.finish();
}
-static Array<float> calc_full_density_factors_with_selection(const MeshComponent &component,
+static Array<float> calc_full_density_factors_with_selection(const Mesh &mesh,
const Field<float> &density_field,
const Field<bool> &selection_field)
{
- const eAttrDomain attribute_domain = ATTR_DOMAIN_CORNER;
- GeometryComponentFieldContext field_context{component, attribute_domain};
- const int domain_size = component.attribute_domain_size(attribute_domain);
-
+ const eAttrDomain domain = ATTR_DOMAIN_CORNER;
+ const int domain_size = mesh.attributes().domain_size(domain);
Array<float> densities(domain_size, 0.0f);
+ bke::MeshFieldContext field_context{mesh, domain};
fn::FieldEvaluator evaluator{field_context, domain_size};
evaluator.set_selection(selection_field);
evaluator.add_with_destination(density_field, densities.as_mutable_span());
@@ -406,7 +402,7 @@ static Array<float> calc_full_density_factors_with_selection(const MeshComponent
return densities;
}
-static void distribute_points_random(const MeshComponent &component,
+static void distribute_points_random(const Mesh &mesh,
const Field<float> &density_field,
const Field<bool> &selection_field,
const int seed,
@@ -415,12 +411,11 @@ static void distribute_points_random(const MeshComponent &component,
Vector<int> &looptri_indices)
{
const Array<float> densities = calc_full_density_factors_with_selection(
- component, density_field, selection_field);
- const Mesh &mesh = *component.get_for_read();
+ mesh, density_field, selection_field);
sample_mesh_surface(mesh, 1.0f, densities, seed, positions, bary_coords, looptri_indices);
}
-static void distribute_points_poisson_disk(const MeshComponent &mesh_component,
+static void distribute_points_poisson_disk(const Mesh &mesh,
const float minimum_distance,
const float max_density,
const Field<float> &density_factor_field,
@@ -430,14 +425,13 @@ static void distribute_points_poisson_disk(const MeshComponent &mesh_component,
Vector<float3> &bary_coords,
Vector<int> &looptri_indices)
{
- const Mesh &mesh = *mesh_component.get_for_read();
sample_mesh_surface(mesh, max_density, {}, seed, positions, bary_coords, looptri_indices);
Array<bool> elimination_mask(positions.size(), false);
update_elimination_mask_for_close_points(positions, minimum_distance, elimination_mask);
const Array<float> density_factors = calc_full_density_factors_with_selection(
- mesh_component, density_factor_field, selection_field);
+ mesh, density_factor_field, selection_field);
update_elimination_mask_based_on_density_factors(
mesh, density_factors, bary_coords, looptri_indices, elimination_mask.as_mutable_span());
@@ -457,7 +451,7 @@ static void point_distribution_calculate(GeometrySet &geometry_set,
return;
}
- const MeshComponent &mesh_component = *geometry_set.get_component_for_read<MeshComponent>();
+ const Mesh &mesh = *geometry_set.get_mesh_for_read();
Vector<float3> positions;
Vector<float3> bary_coords;
@@ -466,20 +460,15 @@ static void point_distribution_calculate(GeometrySet &geometry_set,
switch (method) {
case GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_RANDOM: {
const Field<float> density_field = params.get_input<Field<float>>("Density");
- distribute_points_random(mesh_component,
- density_field,
- selection_field,
- seed,
- positions,
- bary_coords,
- looptri_indices);
+ distribute_points_random(
+ mesh, density_field, selection_field, seed, positions, bary_coords, looptri_indices);
break;
}
case GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_POISSON: {
const float minimum_distance = params.get_input<float>("Distance Min");
const float density_max = params.get_input<float>("Density Max");
const Field<float> density_factors_field = params.get_input<Field<float>>("Density Factor");
- distribute_points_poisson_disk(mesh_component,
+ distribute_points_poisson_disk(mesh,
minimum_distance,
density_max,
density_factors_field,
@@ -497,8 +486,7 @@ static void point_distribution_calculate(GeometrySet &geometry_set,
}
PointCloud *pointcloud = BKE_pointcloud_new_nomain(positions.size());
- bke::MutableAttributeAccessor point_attributes = bke::pointcloud_attributes_for_write(
- *pointcloud);
+ bke::MutableAttributeAccessor point_attributes = pointcloud->attributes_for_write();
bke::SpanAttributeWriter<float3> point_positions =
point_attributes.lookup_or_add_for_write_only_span<float3>("position", ATTR_DOMAIN_POINT);
bke::SpanAttributeWriter<float> point_radii =
@@ -510,9 +498,6 @@ static void point_distribution_calculate(GeometrySet &geometry_set,
geometry_set.replace_pointcloud(pointcloud);
- PointCloudComponent &point_component =
- geometry_set.get_component_for_write<PointCloudComponent>();
-
Map<AttributeIDRef, AttributeKind> attributes;
geometry_set.gather_attributes_for_propagation(
{GEO_COMPONENT_TYPE_MESH}, GEO_COMPONENT_TYPE_POINT_CLOUD, false, attributes);
@@ -520,11 +505,9 @@ static void point_distribution_calculate(GeometrySet &geometry_set,
/* Position is set separately. */
attributes.remove("position");
- propagate_existing_attributes(
- mesh_component, attributes, point_component, bary_coords, looptri_indices);
+ propagate_existing_attributes(mesh, attributes, *pointcloud, bary_coords, looptri_indices);
- compute_attribute_outputs(
- mesh_component, point_component, bary_coords, looptri_indices, attribute_outputs);
+ compute_attribute_outputs(mesh, *pointcloud, bary_coords, looptri_indices, attribute_outputs);
}
static void node_geo_exec(GeoNodeExecParams params)
@@ -545,6 +528,8 @@ static void node_geo_exec(GeoNodeExecParams params)
attribute_outputs.rotation_id = StrongAnonymousAttributeID("Rotation");
}
+ lazy_threading::send_hint();
+
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
point_distribution_calculate(
geometry_set, selection_field, method, seed, attribute_outputs, params);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc
index 76eeee95239..84e63845b84 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc
@@ -209,13 +209,18 @@ static void calc_boundaries(const Mesh &mesh,
{
BLI_assert(r_vertex_types.size() == mesh.totvert);
BLI_assert(r_edge_types.size() == mesh.totedge);
+ const Span<MEdge> edges = mesh.edges();
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
+
r_vertex_types.fill(VertexType::Loose);
r_edge_types.fill(EdgeType::Loose);
/* Add up the number of polys connected to each edge. */
for (const int i : IndexRange(mesh.totpoly)) {
- const MPoly &poly = mesh.mpoly[i];
- for (const MLoop &loop : Span<MLoop>(&mesh.mloop[poly.loopstart], poly.totloop)) {
+ const MPoly &poly = polys[i];
+ const Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop);
+ for (const MLoop &loop : poly_loops) {
r_edge_types[loop.e] = get_edge_type_with_added_neighbor(r_edge_types[loop.e]);
}
}
@@ -226,7 +231,7 @@ static void calc_boundaries(const Mesh &mesh,
if (edge_type == EdgeType::Loose) {
continue;
}
- const MEdge &edge = mesh.medge[i];
+ const MEdge &edge = edges[i];
if (edge_type == EdgeType::Boundary) {
r_vertex_types[edge.v1] = get_vertex_type_with_added_neighbor(r_vertex_types[edge.v1]);
r_vertex_types[edge.v2] = get_vertex_type_with_added_neighbor(r_vertex_types[edge.v2]);
@@ -241,7 +246,7 @@ static void calc_boundaries(const Mesh &mesh,
for (const int i : IndexRange(mesh.totedge)) {
const EdgeType edge_type = r_edge_types[i];
if (edge_type == EdgeType::Normal) {
- const MEdge &edge = mesh.medge[i];
+ const MEdge &edge = edges[i];
if (r_vertex_types[edge.v1] == VertexType::Loose) {
r_vertex_types[edge.v1] = VertexType::Normal;
}
@@ -258,9 +263,12 @@ static void calc_boundaries(const Mesh &mesh,
static void create_vertex_poly_map(const Mesh &mesh,
MutableSpan<Vector<int>> r_vertex_poly_indices)
{
- for (const int i : IndexRange(mesh.totpoly)) {
- const MPoly &poly = mesh.mpoly[i];
- for (const MLoop &loop : Span<MLoop>(&mesh.mloop[poly.loopstart], poly.totloop)) {
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
+ for (const int i : polys.index_range()) {
+ const MPoly &poly = polys[i];
+ const Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop);
+ for (const MLoop &loop : poly_loops) {
r_vertex_poly_indices[loop.v].append(i);
}
}
@@ -321,26 +329,28 @@ static void create_vertex_poly_map(const Mesh &mesh,
* - Finally if we are in the normal case we also need to add the last "shared edge" to close the
* loop.
*/
-static bool sort_vertex_polys(const Mesh &mesh,
+static bool sort_vertex_polys(const Span<MEdge> edges,
+ const Span<MPoly> polys,
+ const Span<MLoop> loops,
const int vertex_index,
const bool boundary_vertex,
const Span<EdgeType> edge_types,
- MutableSpan<int> connected_polygons,
+ MutableSpan<int> connected_polys,
MutableSpan<int> r_shared_edges,
MutableSpan<int> r_sorted_corners)
{
- if (connected_polygons.size() <= 2 && (!boundary_vertex || connected_polygons.size() == 0)) {
+ if (connected_polys.size() <= 2 && (!boundary_vertex || connected_polys.size() == 0)) {
return true;
}
/* For each polygon store the two corners whose edge contains the vertex. */
- Array<std::pair<int, int>> poly_vertex_corners(connected_polygons.size());
- for (const int i : connected_polygons.index_range()) {
- const MPoly &poly = mesh.mpoly[connected_polygons[i]];
+ Array<std::pair<int, int>> poly_vertex_corners(connected_polys.size());
+ for (const int i : connected_polys.index_range()) {
+ const MPoly &poly = polys[connected_polys[i]];
bool first_edge_done = false;
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
- const MLoop &loop = mesh.mloop[loop_index];
- if (mesh.medge[loop.e].v1 == vertex_index || mesh.medge[loop.e].v2 == vertex_index) {
+ const MLoop &loop = loops[loop_index];
+ if (edges[loop.e].v1 == vertex_index || edges[loop.e].v2 == vertex_index) {
if (!first_edge_done) {
poly_vertex_corners[i].first = loop_index;
first_edge_done = true;
@@ -359,20 +369,20 @@ static bool sort_vertex_polys(const Mesh &mesh,
* the loop to determine the 'average' orientation. */
if (boundary_vertex) {
/* Our first polygon needs to be one which has a boundary edge. */
- for (const int i : connected_polygons.index_range()) {
- const MLoop &first_loop = mesh.mloop[poly_vertex_corners[i].first];
- const MLoop &second_loop = mesh.mloop[poly_vertex_corners[i].second];
+ for (const int i : connected_polys.index_range()) {
+ const MLoop &first_loop = loops[poly_vertex_corners[i].first];
+ const MLoop &second_loop = loops[poly_vertex_corners[i].second];
if (edge_types[first_loop.e] == EdgeType::Boundary && first_loop.v == vertex_index) {
shared_edge_i = second_loop.e;
r_sorted_corners[0] = poly_vertex_corners[i].first;
- std::swap(connected_polygons[i], connected_polygons[0]);
+ std::swap(connected_polys[i], connected_polys[0]);
std::swap(poly_vertex_corners[i], poly_vertex_corners[0]);
break;
}
if (edge_types[second_loop.e] == EdgeType::Boundary && second_loop.v == vertex_index) {
shared_edge_i = first_loop.e;
r_sorted_corners[0] = poly_vertex_corners[i].second;
- std::swap(connected_polygons[i], connected_polygons[0]);
+ std::swap(connected_polys[i], connected_polys[0]);
std::swap(poly_vertex_corners[i], poly_vertex_corners[0]);
break;
}
@@ -380,20 +390,20 @@ static bool sort_vertex_polys(const Mesh &mesh,
if (shared_edge_i == -1) {
/* The rotation is inconsistent between the two polygons on the boundary. Just choose one
* of the polygon's orientation. */
- for (const int i : connected_polygons.index_range()) {
- const MLoop &first_loop = mesh.mloop[poly_vertex_corners[i].first];
- const MLoop &second_loop = mesh.mloop[poly_vertex_corners[i].second];
+ for (const int i : connected_polys.index_range()) {
+ const MLoop &first_loop = loops[poly_vertex_corners[i].first];
+ const MLoop &second_loop = loops[poly_vertex_corners[i].second];
if (edge_types[first_loop.e] == EdgeType::Boundary) {
shared_edge_i = second_loop.e;
r_sorted_corners[0] = poly_vertex_corners[i].first;
- std::swap(connected_polygons[i], connected_polygons[0]);
+ std::swap(connected_polys[i], connected_polys[0]);
std::swap(poly_vertex_corners[i], poly_vertex_corners[0]);
break;
}
if (edge_types[second_loop.e] == EdgeType::Boundary) {
shared_edge_i = first_loop.e;
r_sorted_corners[0] = poly_vertex_corners[i].second;
- std::swap(connected_polygons[i], connected_polygons[0]);
+ std::swap(connected_polys[i], connected_polys[0]);
std::swap(poly_vertex_corners[i], poly_vertex_corners[0]);
break;
}
@@ -402,8 +412,8 @@ static bool sort_vertex_polys(const Mesh &mesh,
}
else {
/* Any polygon can be the first. Just need to check the orientation. */
- const MLoop &first_loop = mesh.mloop[poly_vertex_corners[0].first];
- const MLoop &second_loop = mesh.mloop[poly_vertex_corners[0].second];
+ const MLoop &first_loop = loops[poly_vertex_corners[0].first];
+ const MLoop &second_loop = loops[poly_vertex_corners[0].second];
if (first_loop.v == vertex_index) {
shared_edge_i = second_loop.e;
r_sorted_corners[0] = poly_vertex_corners[0].first;
@@ -415,14 +425,14 @@ static bool sort_vertex_polys(const Mesh &mesh,
}
BLI_assert(shared_edge_i != -1);
- for (const int i : IndexRange(connected_polygons.size() - 1)) {
+ for (const int i : IndexRange(connected_polys.size() - 1)) {
r_shared_edges[i] = shared_edge_i;
/* Look at the other polys to see if it has this shared edge. */
int j = i + 1;
- for (; j < connected_polygons.size(); ++j) {
- const MLoop &first_loop = mesh.mloop[poly_vertex_corners[j].first];
- const MLoop &second_loop = mesh.mloop[poly_vertex_corners[j].second];
+ for (; j < connected_polys.size(); ++j) {
+ const MLoop &first_loop = loops[poly_vertex_corners[j].first];
+ const MLoop &second_loop = loops[poly_vertex_corners[j].second];
if (first_loop.e == shared_edge_i) {
r_sorted_corners[i + 1] = poly_vertex_corners[j].first;
shared_edge_i = second_loop.e;
@@ -434,13 +444,13 @@ static bool sort_vertex_polys(const Mesh &mesh,
break;
}
}
- if (j == connected_polygons.size()) {
+ if (j == connected_polys.size()) {
/* The vertex is not manifold because the polygons around the vertex don't form a loop, and
* hence can't be sorted. */
return false;
}
- std::swap(connected_polygons[i + 1], connected_polygons[j]);
+ std::swap(connected_polys[i + 1], connected_polys[j]);
std::swap(poly_vertex_corners[i + 1], poly_vertex_corners[j]);
}
@@ -455,14 +465,16 @@ static bool sort_vertex_polys(const Mesh &mesh,
* Get the edge on the poly that contains the given vertex and is a boundary edge.
*/
static void boundary_edge_on_poly(const MPoly &poly,
- const Mesh &mesh,
+ const Span<MEdge> edges,
+ const Span<MLoop> loops,
const int vertex_index,
const Span<EdgeType> edge_types,
int &r_edge)
{
- for (const MLoop &loop : Span<MLoop>(&mesh.mloop[poly.loopstart], poly.totloop)) {
+ const Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop);
+ for (const MLoop &loop : poly_loops) {
if (edge_types[loop.e] == EdgeType::Boundary) {
- const MEdge &edge = mesh.medge[loop.e];
+ const MEdge &edge = edges[loop.e];
if (edge.v1 == vertex_index || edge.v2 == vertex_index) {
r_edge = loop.e;
return;
@@ -476,7 +488,8 @@ static void boundary_edge_on_poly(const MPoly &poly,
* orientation of the poly is taken into account.
*/
static void boundary_edges_on_poly(const MPoly &poly,
- const Mesh &mesh,
+ const Span<MEdge> edges,
+ const Span<MLoop> loops,
const int vertex_index,
const Span<EdgeType> edge_types,
int &r_edge1,
@@ -486,9 +499,10 @@ static void boundary_edges_on_poly(const MPoly &poly,
/* This is set to true if the order in which we encounter the two edges is inconsistent with the
* orientation of the polygon. */
bool needs_swap = false;
- for (const MLoop &loop : Span<MLoop>(&mesh.mloop[poly.loopstart], poly.totloop)) {
+ const Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop);
+ for (const MLoop &loop : poly_loops) {
if (edge_types[loop.e] == EdgeType::Boundary) {
- const MEdge &edge = mesh.medge[loop.e];
+ const MEdge &edge = edges[loop.e];
if (edge.v1 == vertex_index || edge.v2 == vertex_index) {
if (edge1_done) {
if (needs_swap) {
@@ -510,7 +524,7 @@ static void boundary_edges_on_poly(const MPoly &poly,
}
}
-static void add_edge(const Mesh &mesh,
+static void add_edge(const Span<MEdge> src_edges,
const int old_edge_i,
const int v1,
const int v2,
@@ -518,7 +532,7 @@ static void add_edge(const Mesh &mesh,
Vector<MEdge> &new_edges,
Vector<int> &loop_edges)
{
- MEdge new_edge = MEdge(mesh.medge[old_edge_i]);
+ MEdge new_edge = src_edges[old_edge_i];
new_edge.v1 = v1;
new_edge.v2 = v2;
const int new_edge_i = new_edges.size();
@@ -549,14 +563,17 @@ static bool vertex_needs_dissolving(const int vertex,
* edges in the input mesh which contain such a vertex are marked as 'done' to prevent duplicate
* edges being created. (See T94144)
*/
-static void dissolve_redundant_verts(const Mesh &mesh,
+static void dissolve_redundant_verts(const Span<MEdge> edges,
+ const Span<MPoly> polys,
+ const Span<MLoop> loops,
const Span<Vector<int>> vertex_poly_indices,
MutableSpan<VertexType> vertex_types,
MutableSpan<int> old_to_new_edges_map,
Vector<MEdge> &new_edges,
Vector<int> &new_to_old_edges_map)
{
- for (const int vert_i : IndexRange(mesh.totvert)) {
+ const int vertex_num = vertex_types.size();
+ for (const int vert_i : IndexRange(vertex_num)) {
if (vertex_poly_indices[vert_i].size() != 2 || vertex_types[vert_i] != VertexType::Normal) {
continue;
}
@@ -564,9 +581,10 @@ static void dissolve_redundant_verts(const Mesh &mesh,
const int second_poly_index = vertex_poly_indices[vert_i][1];
const int new_edge_index = new_edges.size();
bool edge_created = false;
- const MPoly &poly = mesh.mpoly[first_poly_index];
- for (const MLoop &loop : Span<MLoop>(&mesh.mloop[poly.loopstart], poly.totloop)) {
- const MEdge &edge = mesh.medge[loop.e];
+ const MPoly &poly = polys[first_poly_index];
+ const Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop);
+ for (const MLoop &loop : poly_loops) {
+ const MEdge &edge = edges[loop.e];
const int v1 = edge.v1;
const int v2 = edge.v2;
bool mark_edge = false;
@@ -617,6 +635,10 @@ static void calc_dual_mesh(GeometrySet &geometry_set,
const bool keep_boundaries)
{
const Mesh &mesh_in = *in_component.get_for_read();
+ const Span<MVert> src_verts = mesh_in.verts();
+ const Span<MEdge> src_edges = mesh_in.edges();
+ const Span<MPoly> src_polys = mesh_in.polys();
+ const Span<MLoop> src_loops = mesh_in.loops();
Map<AttributeIDRef, AttributeKind> attributes;
geometry_set.gather_attributes_for_propagation(
@@ -644,14 +666,28 @@ static void calc_dual_mesh(GeometrySet &geometry_set,
bool vertex_ok = true;
if (vertex_types[i] == VertexType::Normal) {
Array<int> shared_edges(loop_indices.size());
- vertex_ok = sort_vertex_polys(
- mesh_in, i, false, edge_types, loop_indices, shared_edges, sorted_corners);
+ vertex_ok = sort_vertex_polys(src_edges,
+ src_polys,
+ src_loops,
+ i,
+ false,
+ edge_types,
+ loop_indices,
+ shared_edges,
+ sorted_corners);
vertex_shared_edges[i] = std::move(shared_edges);
}
else {
Array<int> shared_edges(loop_indices.size() - 1);
- vertex_ok = sort_vertex_polys(
- mesh_in, i, true, edge_types, loop_indices, shared_edges, sorted_corners);
+ vertex_ok = sort_vertex_polys(src_edges,
+ src_polys,
+ src_loops,
+ i,
+ true,
+ edge_types,
+ loop_indices,
+ shared_edges,
+ sorted_corners);
vertex_shared_edges[i] = std::move(shared_edges);
}
if (!vertex_ok) {
@@ -666,9 +702,9 @@ static void calc_dual_mesh(GeometrySet &geometry_set,
Vector<float3> vertex_positions(mesh_in.totpoly);
for (const int i : IndexRange(mesh_in.totpoly)) {
- const MPoly poly = mesh_in.mpoly[i];
+ const MPoly &poly = src_polys[i];
BKE_mesh_calc_poly_center(
- &poly, &mesh_in.mloop[poly.loopstart], mesh_in.mvert, vertex_positions[i]);
+ &poly, &src_loops[poly.loopstart], src_verts.data(), vertex_positions[i]);
}
Array<int> boundary_edge_midpoint_index;
@@ -679,8 +715,8 @@ static void calc_dual_mesh(GeometrySet &geometry_set,
for (const int i : IndexRange(mesh_in.totedge)) {
if (edge_types[i] == EdgeType::Boundary) {
float3 mid;
- const MEdge &edge = mesh_in.medge[i];
- mid_v3_v3v3(mid, mesh_in.mvert[edge.v1].co, mesh_in.mvert[edge.v2].co);
+ const MEdge &edge = src_edges[i];
+ mid_v3_v3v3(mid, src_verts[edge.v1].co, src_verts[edge.v2].co);
boundary_edge_midpoint_index[i] = vertex_positions.size();
vertex_positions.append(mid);
}
@@ -706,7 +742,9 @@ static void calc_dual_mesh(GeometrySet &geometry_set,
/* This is necessary to prevent duplicate edges from being created, but will likely not do
* anything for most meshes. */
- dissolve_redundant_verts(mesh_in,
+ dissolve_redundant_verts(src_edges,
+ src_polys,
+ src_loops,
vertex_poly_indices,
vertex_types,
old_to_new_edges_map,
@@ -734,7 +772,7 @@ static void calc_dual_mesh(GeometrySet &geometry_set,
const int old_edge_i = shared_edges[i];
if (old_to_new_edges_map[old_edge_i] == -1) {
/* This edge has not been created yet. */
- MEdge new_edge = MEdge(mesh_in.medge[old_edge_i]);
+ MEdge new_edge = src_edges[old_edge_i];
new_edge.v1 = loop_indices[i];
new_edge.v2 = loop_indices[(i + 1) % loop_indices.size()];
new_to_old_edges_map.append(old_edge_i);
@@ -776,7 +814,7 @@ static void calc_dual_mesh(GeometrySet &geometry_set,
const int old_edge_i = shared_edges[i];
if (old_to_new_edges_map[old_edge_i] == -1) {
/* This edge has not been created yet. */
- MEdge new_edge = MEdge(mesh_in.medge[old_edge_i]);
+ MEdge new_edge = src_edges[old_edge_i];
new_edge.v1 = loop_indices[i];
new_edge.v2 = loop_indices[i + 1];
new_to_old_edges_map.append(old_edge_i);
@@ -795,13 +833,15 @@ static void calc_dual_mesh(GeometrySet &geometry_set,
int edge2;
if (loop_indices.size() >= 2) {
/* The first boundary edge is at the end of the chain of polygons. */
- boundary_edge_on_poly(mesh_in.mpoly[loop_indices.last()], mesh_in, i, edge_types, edge1);
- boundary_edge_on_poly(mesh_in.mpoly[loop_indices.first()], mesh_in, i, edge_types, edge2);
+ boundary_edge_on_poly(
+ src_polys[loop_indices.last()], src_edges, src_loops, i, edge_types, edge1);
+ boundary_edge_on_poly(
+ src_polys[loop_indices.first()], src_edges, src_loops, i, edge_types, edge2);
}
else {
/* If there is only one polygon both edges are in that polygon. */
boundary_edges_on_poly(
- mesh_in.mpoly[loop_indices[0]], mesh_in, i, edge_types, edge1, edge2);
+ src_polys[loop_indices[0]], src_edges, src_loops, i, edge_types, edge1, edge2);
}
const int last_face_center = loop_indices.last();
@@ -809,7 +849,7 @@ static void calc_dual_mesh(GeometrySet &geometry_set,
new_to_old_face_corners_map.append(sorted_corners.last());
const int first_midpoint = loop_indices.last();
if (old_to_new_edges_map[edge1] == -1) {
- add_edge(mesh_in,
+ add_edge(src_edges,
edge1,
last_face_center,
first_midpoint,
@@ -827,9 +867,9 @@ static void calc_dual_mesh(GeometrySet &geometry_set,
new_to_old_face_corners_map.append(sorted_corners.first());
boundary_vertex_to_relevant_face_map.append(
std::pair(loop_indices.last(), last_face_center));
- vertex_positions.append(mesh_in.mvert[i].co);
+ vertex_positions.append(src_verts[i].co);
const int boundary_vertex = loop_indices.last();
- add_edge(mesh_in,
+ add_edge(src_edges,
edge1,
first_midpoint,
boundary_vertex,
@@ -840,7 +880,7 @@ static void calc_dual_mesh(GeometrySet &geometry_set,
loop_indices.append(boundary_edge_midpoint_index[edge2]);
new_to_old_face_corners_map.append(sorted_corners.first());
const int second_midpoint = loop_indices.last();
- add_edge(mesh_in,
+ add_edge(src_edges,
edge2,
boundary_vertex,
second_midpoint,
@@ -850,7 +890,7 @@ static void calc_dual_mesh(GeometrySet &geometry_set,
if (old_to_new_edges_map[edge2] == -1) {
const int first_face_center = loop_indices.first();
- add_edge(mesh_in,
+ add_edge(src_edges,
edge2,
second_midpoint,
first_face_center,
@@ -878,23 +918,28 @@ static void calc_dual_mesh(GeometrySet &geometry_set,
new_to_old_edges_map,
new_to_old_face_corners_map,
boundary_vertex_to_relevant_face_map,
- bke::mesh_attributes(mesh_in),
- bke::mesh_attributes_for_write(*mesh_out));
+ mesh_in.attributes(),
+ mesh_out->attributes_for_write());
+
+ MutableSpan<MVert> dst_verts = mesh_out->verts_for_write();
+ MutableSpan<MEdge> dst_edges = mesh_out->edges_for_write();
+ MutableSpan<MPoly> dst_polys = mesh_out->polys_for_write();
+ MutableSpan<MLoop> dst_loops = mesh_out->loops_for_write();
int loop_start = 0;
for (const int i : IndexRange(mesh_out->totpoly)) {
- mesh_out->mpoly[i].loopstart = loop_start;
- mesh_out->mpoly[i].totloop = loop_lengths[i];
+ dst_polys[i].loopstart = loop_start;
+ dst_polys[i].totloop = loop_lengths[i];
loop_start += loop_lengths[i];
}
for (const int i : IndexRange(mesh_out->totloop)) {
- mesh_out->mloop[i].v = loops[i];
- mesh_out->mloop[i].e = loop_edges[i];
+ dst_loops[i].v = loops[i];
+ dst_loops[i].e = loop_edges[i];
}
for (const int i : IndexRange(mesh_out->totvert)) {
- copy_v3_v3(mesh_out->mvert[i].co, vertex_positions[i]);
+ copy_v3_v3(dst_verts[i].co, vertex_positions[i]);
}
- memcpy(mesh_out->medge, new_edges.data(), sizeof(MEdge) * new_edges.size());
+ dst_edges.copy_from(new_edges);
geometry_set.replace_mesh(mesh_out);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc b/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc
index c6b0fb4c068..d2a3c339301 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc
@@ -334,11 +334,10 @@ static void duplicate_curves(GeometrySet &geometry_set,
geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_CURVE});
GeometryComponentEditData::remember_deformed_curve_positions_if_necessary(geometry_set);
- const CurveComponent &src_component = *geometry_set.get_component_for_read<CurveComponent>();
- const Curves &curves_id = *src_component.get_for_read();
+ const Curves &curves_id = *geometry_set.get_curves_for_read();
const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
- GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_CURVE};
+ bke::CurvesFieldContext field_context{curves, ATTR_DOMAIN_CURVE};
FieldEvaluator evaluator{field_context, curves.curves_num()};
evaluator.add(count_field);
evaluator.set_selection(selection_field);
@@ -487,7 +486,7 @@ static void copy_stable_id_faces(const Mesh &mesh,
VArraySpan<int> src{src_attribute.varray.typed<int>()};
MutableSpan<int> dst = dst_attribute.span.typed<int>();
- Span<MPoly> polys(mesh.mpoly, mesh.totpoly);
+ const Span<MPoly> polys = mesh.polys();
int loop_index = 0;
for (const int i_poly : selection.index_range()) {
const IndexRange range = range_for_offsets_index(poly_offsets, i_poly);
@@ -522,14 +521,13 @@ static void duplicate_faces(GeometrySet &geometry_set,
}
geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_MESH});
- const MeshComponent &src_component = *geometry_set.get_component_for_read<MeshComponent>();
- const Mesh &mesh = *src_component.get_for_read();
- Span<MVert> verts(mesh.mvert, mesh.totvert);
- Span<MEdge> edges(mesh.medge, mesh.totedge);
- Span<MPoly> polys(mesh.mpoly, mesh.totpoly);
- Span<MLoop> loops(mesh.mloop, mesh.totloop);
+ const Mesh &mesh = *geometry_set.get_mesh_for_read();
+ const Span<MVert> verts = mesh.verts();
+ const Span<MEdge> edges = mesh.edges();
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
- GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_FACE};
+ bke::MeshFieldContext field_context{mesh, ATTR_DOMAIN_FACE};
FieldEvaluator evaluator(field_context, polys.size());
evaluator.add(count_field);
evaluator.set_selection(selection_field);
@@ -549,10 +547,10 @@ static void duplicate_faces(GeometrySet &geometry_set,
offsets[selection.size()] = total_polys;
Mesh *new_mesh = BKE_mesh_new_nomain(total_loops, total_loops, 0, total_loops, total_polys);
- MutableSpan<MVert> new_verts(new_mesh->mvert, new_mesh->totvert);
- MutableSpan<MEdge> new_edges(new_mesh->medge, new_mesh->totedge);
- MutableSpan<MLoop> new_loops(new_mesh->mloop, new_mesh->totloop);
- MutableSpan<MPoly> new_poly(new_mesh->mpoly, new_mesh->totpoly);
+ MutableSpan<MVert> new_verts = new_mesh->verts_for_write();
+ MutableSpan<MEdge> new_edges = new_mesh->edges_for_write();
+ MutableSpan<MPoly> new_polys = new_mesh->polys_for_write();
+ MutableSpan<MLoop> new_loops = new_mesh->loops_for_write();
Array<int> vert_mapping(new_verts.size());
Array<int> edge_mapping(new_edges.size());
@@ -565,8 +563,8 @@ static void duplicate_faces(GeometrySet &geometry_set,
const MPoly &source = polys[selection[i_selection]];
for ([[maybe_unused]] const int i_duplicate : IndexRange(poly_range.size())) {
- new_poly[poly_index] = source;
- new_poly[poly_index].loopstart = loop_index;
+ new_polys[poly_index] = source;
+ new_polys[poly_index].loopstart = loop_index;
for (const int i_loops : IndexRange(source.totloop)) {
const MLoop &current_loop = loops[source.loopstart + i_loops];
loop_mapping[loop_index] = source.loopstart + i_loops;
@@ -579,7 +577,7 @@ static void duplicate_faces(GeometrySet &geometry_set,
new_edges[loop_index].v2 = loop_index + 1;
}
else {
- new_edges[loop_index].v2 = new_poly[poly_index].loopstart;
+ new_edges[loop_index].v2 = new_polys[poly_index].loopstart;
}
new_loops[loop_index].v = loop_index;
new_loops[loop_index].e = loop_index;
@@ -595,22 +593,15 @@ static void duplicate_faces(GeometrySet &geometry_set,
loop_mapping,
offsets,
selection,
- bke::mesh_attributes(mesh),
- bke::mesh_attributes_for_write(*new_mesh));
+ mesh.attributes(),
+ new_mesh->attributes_for_write());
- copy_stable_id_faces(mesh,
- selection,
- offsets,
- vert_mapping,
- bke::mesh_attributes(mesh),
- bke::mesh_attributes_for_write(*new_mesh));
+ copy_stable_id_faces(
+ mesh, selection, offsets, vert_mapping, mesh.attributes(), new_mesh->attributes_for_write());
if (attribute_outputs.duplicate_index) {
- create_duplicate_index_attribute(bke::mesh_attributes_for_write(*new_mesh),
- ATTR_DOMAIN_FACE,
- selection,
- attribute_outputs,
- offsets);
+ create_duplicate_index_attribute(
+ new_mesh->attributes_for_write(), ATTR_DOMAIN_FACE, selection, attribute_outputs, offsets);
}
geometry_set.replace_mesh(new_mesh);
@@ -691,7 +682,7 @@ static void copy_stable_id_edges(const Mesh &mesh,
return;
}
- Span<MEdge> edges(mesh.medge, mesh.totedge);
+ const Span<MEdge> edges = mesh.edges();
VArraySpan<int> src{src_attribute.varray.typed<int>()};
MutableSpan<int> dst = dst_attribute.span.typed<int>();
@@ -724,12 +715,10 @@ static void duplicate_edges(GeometrySet &geometry_set,
geometry_set.remove_geometry_during_modify();
return;
};
- const MeshComponent &src_component = *geometry_set.get_component_for_read<MeshComponent>();
- const Mesh &mesh = *src_component.get_for_read();
- Span<MVert> verts(mesh.mvert, mesh.totvert);
- Span<MEdge> edges(mesh.medge, mesh.totedge);
+ const Mesh &mesh = *geometry_set.get_mesh_for_read();
+ const Span<MEdge> edges = mesh.edges();
- GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_EDGE};
+ bke::MeshFieldContext field_context{mesh, ATTR_DOMAIN_EDGE};
FieldEvaluator evaluator{field_context, edges.size()};
evaluator.add(count_field);
evaluator.set_selection(selection_field);
@@ -740,8 +729,7 @@ static void duplicate_edges(GeometrySet &geometry_set,
Array<int> edge_offsets = accumulate_counts_to_offsets(selection, counts);
Mesh *new_mesh = BKE_mesh_new_nomain(edge_offsets.last() * 2, edge_offsets.last(), 0, 0, 0);
- MutableSpan<MVert> new_verts(new_mesh->mvert, new_mesh->totvert);
- MutableSpan<MEdge> new_edges(new_mesh->medge, new_mesh->totedge);
+ MutableSpan<MEdge> new_edges = new_mesh->edges_for_write();
Array<int> vert_orig_indices(edge_offsets.last() * 2);
threading::parallel_for(selection.index_range(), 1024, [&](IndexRange range) {
@@ -774,17 +762,14 @@ static void duplicate_edges(GeometrySet &geometry_set,
vert_orig_indices,
edge_offsets,
selection,
- bke::mesh_attributes(mesh),
- bke::mesh_attributes_for_write(*new_mesh));
+ mesh.attributes(),
+ new_mesh->attributes_for_write());
- copy_stable_id_edges(mesh,
- selection,
- edge_offsets,
- bke::mesh_attributes(mesh),
- bke::mesh_attributes_for_write(*new_mesh));
+ copy_stable_id_edges(
+ mesh, selection, edge_offsets, mesh.attributes(), new_mesh->attributes_for_write());
if (attribute_outputs.duplicate_index) {
- create_duplicate_index_attribute(bke::mesh_attributes_for_write(*new_mesh),
+ create_duplicate_index_attribute(new_mesh->attributes_for_write(),
ATTR_DOMAIN_EDGE,
selection,
attribute_outputs,
@@ -805,14 +790,13 @@ static void duplicate_points_curve(GeometrySet &geometry_set,
const Field<bool> &selection_field,
const IndexAttributes &attribute_outputs)
{
- const CurveComponent &src_component = *geometry_set.get_component_for_read<CurveComponent>();
- const Curves &src_curves_id = *src_component.get_for_read();
+ const Curves &src_curves_id = *geometry_set.get_curves_for_read();
const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(src_curves_id.geometry);
if (src_curves.points_num() == 0) {
return;
}
- GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_POINT};
+ bke::CurvesFieldContext field_context{src_curves, ATTR_DOMAIN_POINT};
FieldEvaluator evaluator{field_context, src_curves.points_num()};
evaluator.add(count_field);
evaluator.set_selection(selection_field);
@@ -845,7 +829,7 @@ static void duplicate_points_curve(GeometrySet &geometry_set,
for (const Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
const AttributeIDRef attribute_id = entry.key;
- GAttributeReader src_attribute = src_component.attributes()->lookup(attribute_id);
+ GAttributeReader src_attribute = src_curves.attributes().lookup(attribute_id);
if (!src_attribute) {
continue;
}
@@ -909,11 +893,10 @@ static void duplicate_points_mesh(GeometrySet &geometry_set,
const Field<bool> &selection_field,
const IndexAttributes &attribute_outputs)
{
- const MeshComponent &src_component = *geometry_set.get_component_for_read<MeshComponent>();
const Mesh &mesh = *geometry_set.get_mesh_for_read();
- Span<MVert> src_verts(mesh.mvert, mesh.totvert);
+ const Span<MVert> src_verts = mesh.verts();
- GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_POINT};
+ bke::MeshFieldContext field_context{mesh, ATTR_DOMAIN_POINT};
FieldEvaluator evaluator{field_context, src_verts.size()};
evaluator.add(count_field);
evaluator.set_selection(selection_field);
@@ -924,7 +907,7 @@ static void duplicate_points_mesh(GeometrySet &geometry_set,
Array<int> offsets = accumulate_counts_to_offsets(selection, counts);
Mesh *new_mesh = BKE_mesh_new_nomain(offsets.last(), 0, 0, 0, 0);
- MutableSpan<MVert> dst_verts(new_mesh->mvert, new_mesh->totvert);
+ MutableSpan<MVert> dst_verts = new_mesh->verts_for_write();
threaded_slice_fill(offsets.as_span(), selection, src_verts, dst_verts);
@@ -933,14 +916,13 @@ static void duplicate_points_mesh(GeometrySet &geometry_set,
ATTR_DOMAIN_POINT,
offsets,
selection,
- bke::mesh_attributes(mesh),
- bke::mesh_attributes_for_write(*new_mesh));
+ mesh.attributes(),
+ new_mesh->attributes_for_write());
- copy_stable_id_point(
- offsets, bke::mesh_attributes(mesh), bke::mesh_attributes_for_write(*new_mesh));
+ copy_stable_id_point(offsets, mesh.attributes(), new_mesh->attributes_for_write());
if (attribute_outputs.duplicate_index) {
- create_duplicate_index_attribute(bke::mesh_attributes_for_write(*new_mesh),
+ create_duplicate_index_attribute(new_mesh->attributes_for_write(),
ATTR_DOMAIN_POINT,
selection,
attribute_outputs,
@@ -961,12 +943,10 @@ static void duplicate_points_pointcloud(GeometrySet &geometry_set,
const Field<bool> &selection_field,
const IndexAttributes &attribute_outputs)
{
- const PointCloudComponent &src_points =
- *geometry_set.get_component_for_read<PointCloudComponent>();
- const int point_num = src_points.attribute_domain_size(ATTR_DOMAIN_POINT);
+ const PointCloud &src_points = *geometry_set.get_pointcloud_for_read();
- GeometryComponentFieldContext field_context{src_points, ATTR_DOMAIN_POINT};
- FieldEvaluator evaluator{field_context, point_num};
+ bke::PointCloudFieldContext field_context{src_points};
+ FieldEvaluator evaluator{field_context, src_points.totpoint};
evaluator.add(count_field);
evaluator.set_selection(selection_field);
evaluator.evaluate();
@@ -982,14 +962,13 @@ static void duplicate_points_pointcloud(GeometrySet &geometry_set,
ATTR_DOMAIN_POINT,
offsets,
selection,
- *src_points.attributes(),
- bke::pointcloud_attributes_for_write(*pointcloud));
+ src_points.attributes(),
+ pointcloud->attributes_for_write());
- copy_stable_id_point(
- offsets, *src_points.attributes(), bke::pointcloud_attributes_for_write(*pointcloud));
+ copy_stable_id_point(offsets, src_points.attributes(), pointcloud->attributes_for_write());
if (attribute_outputs.duplicate_index) {
- create_duplicate_index_attribute(bke::pointcloud_attributes_for_write(*pointcloud),
+ create_duplicate_index_attribute(pointcloud->attributes_for_write(),
ATTR_DOMAIN_POINT,
selection,
attribute_outputs,
@@ -1055,7 +1034,7 @@ static void duplicate_instances(GeometrySet &geometry_set,
const InstancesComponent &src_instances =
*geometry_set.get_component_for_read<InstancesComponent>();
- GeometryComponentFieldContext field_context{src_instances, ATTR_DOMAIN_INSTANCE};
+ bke::GeometryFieldContext field_context{src_instances, ATTR_DOMAIN_INSTANCE};
FieldEvaluator evaluator{field_context, src_instances.instances_num()};
evaluator.add(count_field);
evaluator.set_selection(selection_field);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_curves.cc b/source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_curves.cc
index 89abfa0aa88..ba09acf0bf0 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_curves.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_curves.cc
@@ -3,7 +3,6 @@
#include "BKE_curves.hh"
#include "DNA_mesh_types.h"
-#include "DNA_meshdata_types.h"
#include "GEO_mesh_to_curve.hh"
@@ -23,7 +22,6 @@ static Curves *edge_paths_to_curves_convert(const Mesh &mesh,
const IndexMask start_verts_mask,
const Span<int> next_indices)
{
- const Span<MVert> mvert{mesh.mvert, mesh.totvert};
Vector<int> vert_indices;
Vector<int> curve_offsets;
Array<bool> visited(mesh.totvert, false);
@@ -70,14 +68,14 @@ 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()) {
+ const Mesh *mesh = geometry_set.get_mesh_for_read();
+ if (mesh == nullptr) {
geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
return;
}
- const MeshComponent &component = *geometry_set.get_component_for_read<MeshComponent>();
- GeometryComponentFieldContext context{component, ATTR_DOMAIN_POINT};
- fn::FieldEvaluator evaluator{context, component.attribute_domain_size(ATTR_DOMAIN_POINT)};
+ bke::MeshFieldContext context{*mesh, ATTR_DOMAIN_POINT};
+ fn::FieldEvaluator evaluator{context, mesh->totvert};
evaluator.add(params.get_input<Field<int>>("Next Vertex Index"));
evaluator.add(params.get_input<Field<bool>>("Start Vertices"));
evaluator.evaluate();
@@ -89,8 +87,7 @@ static void node_geo_exec(GeoNodeExecParams params)
return;
}
- const Mesh &mesh = *component.get_for_read();
- geometry_set.replace_curves(edge_paths_to_curves_convert(mesh, start_verts, next_vert));
+ geometry_set.replace_curves(edge_paths_to_curves_convert(*mesh, start_verts, next_vert));
geometry_set.keep_only({GEO_COMPONENT_TYPE_CURVE, GEO_COMPONENT_TYPE_INSTANCES});
});
diff --git a/source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_selection.cc
index 53cbd691fdb..9ef9ee8ad6e 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_selection.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_selection.cc
@@ -7,9 +7,6 @@
#include "BLI_set.hh"
#include "BLI_task.hh"
-#include "DNA_mesh_types.h"
-#include "DNA_meshdata_types.h"
-
#include "node_geometry_util.hh"
#include <set>
@@ -28,6 +25,8 @@ static void edge_paths_to_selection(const Mesh &src_mesh,
const Span<int> next_indices,
MutableSpan<bool> r_selection)
{
+ const Span<MEdge> edges = src_mesh.edges();
+
Array<bool> selection(src_mesh.totvert, false);
for (const int start_vert : start_selection) {
@@ -45,8 +44,8 @@ static void edge_paths_to_selection(const Mesh &src_mesh,
}
}
- for (const int i : IndexRange(src_mesh.totedge)) {
- const MEdge &edge = src_mesh.medge[i];
+ for (const int i : edges.index_range()) {
+ const MEdge &edge = edges[i];
if ((selection[edge.v1] && selection[edge.v2]) &&
(edge.v1 == next_indices[edge.v2] || edge.v2 == next_indices[edge.v1])) {
r_selection[i] = true;
@@ -54,36 +53,26 @@ static void edge_paths_to_selection(const Mesh &src_mesh,
}
}
-class PathToEdgeSelectionFieldInput final : public GeometryFieldInput {
+class PathToEdgeSelectionFieldInput final : public bke::MeshFieldInput {
private:
Field<bool> start_vertices_;
Field<int> next_vertex_;
public:
- PathToEdgeSelectionFieldInput(Field<bool> start_vertices, Field<int> next_vertex)
- : GeometryFieldInput(CPPType::get<bool>(), "Edge Selection"),
- start_vertices_(start_vertices),
+ PathToEdgeSelectionFieldInput(Field<bool> start_verts, Field<int> next_vertex)
+ : bke::MeshFieldInput(CPPType::get<bool>(), "Edge Selection"),
+ start_vertices_(start_verts),
next_vertex_(next_vertex)
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const Mesh &mesh,
const eAttrDomain domain,
- [[maybe_unused]] IndexMask mask) const final
+ const IndexMask /*mask*/) const final
{
- if (component.type() != GEO_COMPONENT_TYPE_MESH) {
- return {};
- }
-
- const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
- const Mesh *mesh = mesh_component.get_for_read();
- if (mesh == nullptr) {
- return {};
- }
-
- GeometryComponentFieldContext context{mesh_component, ATTR_DOMAIN_POINT};
- fn::FieldEvaluator evaluator{context, mesh_component.attribute_domain_size(ATTR_DOMAIN_POINT)};
+ bke::MeshFieldContext context{mesh, ATTR_DOMAIN_POINT};
+ fn::FieldEvaluator evaluator{context, mesh.totvert};
evaluator.add(next_vertex_);
evaluator.add(start_vertices_);
evaluator.evaluate();
@@ -94,12 +83,12 @@ class PathToEdgeSelectionFieldInput final : public GeometryFieldInput {
return {};
}
- Array<bool> selection(mesh->totedge, false);
+ Array<bool> selection(mesh.totedge, false);
MutableSpan<bool> selection_span = selection.as_mutable_span();
- edge_paths_to_selection(*mesh, start_verts, next_vert, selection_span);
+ edge_paths_to_selection(mesh, start_verts, next_vert, selection_span);
- return mesh_component.attributes()->adapt_domain<bool>(
+ return mesh.attributes().adapt_domain<bool>(
VArray<bool>::ForContainer(std::move(selection)), ATTR_DOMAIN_EDGE, domain);
}
@@ -121,10 +110,10 @@ class PathToEdgeSelectionFieldInput final : public GeometryFieldInput {
static void node_geo_exec(GeoNodeExecParams params)
{
- Field<bool> start_vertices = params.extract_input<Field<bool>>("Start Vertices");
+ Field<bool> start_verts = params.extract_input<Field<bool>>("Start Vertices");
Field<int> next_vertex = params.extract_input<Field<int>>("Next Vertex Index");
Field<bool> selection_field{
- std::make_shared<PathToEdgeSelectionFieldInput>(start_vertices, next_vertex)};
+ std::make_shared<PathToEdgeSelectionFieldInput>(start_verts, next_vertex)};
params.set_output("Selection", std::move(selection_field));
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc
index 84acab47661..0b4d5bd53f3 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc
@@ -1,5 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include "DNA_mesh_types.h"
+
#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
@@ -51,19 +53,18 @@ static void node_geo_exec(GeoNodeExecParams params)
const Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
- if (!geometry_set.has_mesh()) {
- return;
- }
+ if (const Mesh *mesh = geometry_set.get_mesh_for_write()) {
- const MeshComponent &mesh_component = *geometry_set.get_component_for_read<MeshComponent>();
- GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_EDGE};
- const int domain_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);
+ bke::MeshFieldContext field_context{*mesh, ATTR_DOMAIN_EDGE};
+ fn::FieldEvaluator selection_evaluator{field_context, mesh->totedge};
+ selection_evaluator.add(selection_field);
+ selection_evaluator.evaluate();
+ const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0);
- geometry_set.replace_mesh(mesh_edge_split(*mesh_component.get_for_read(), selection));
+ Mesh *result = mesh_edge_split(*mesh, selection);
+
+ geometry_set.replace_mesh(result);
+ }
});
params.set_output("Mesh", std::move(geometry_set));
diff --git a/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc
index 024dbd1c852..5614d5b66dc 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc
@@ -61,15 +61,15 @@ struct AttributeOutputs {
StrongAnonymousAttributeID side_id;
};
-static void save_selection_as_attribute(MeshComponent &component,
+static void save_selection_as_attribute(Mesh &mesh,
const AnonymousAttributeID *id,
const eAttrDomain domain,
const IndexMask selection)
{
- BLI_assert(!component.attributes()->contains(id));
+ MutableAttributeAccessor attributes = mesh.attributes_for_write();
+ BLI_assert(!attributes.contains(id));
- SpanAttributeWriter<bool> attribute =
- component.attributes_for_write()->lookup_or_add_for_write_span<bool>(id, domain);
+ SpanAttributeWriter<bool> attribute = attributes.lookup_or_add_for_write_span<bool>(id, domain);
/* Rely on the new attribute being zeroed by default. */
BLI_assert(!attribute.span.as_span().contains(true));
@@ -83,31 +83,6 @@ static void save_selection_as_attribute(MeshComponent &component,
attribute.finish();
}
-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.
@@ -119,30 +94,25 @@ static void expand_mesh(Mesh &mesh,
const int loop_expand)
{
if (vert_expand != 0) {
- CustomData_duplicate_referenced_layers(&mesh.vdata, mesh.totvert);
+ const int old_verts_num = 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);
+ CustomData_realloc(&mesh.vdata, old_verts_num, mesh.totvert);
}
if (edge_expand != 0) {
- CustomData_duplicate_referenced_layers(&mesh.edata, mesh.totedge);
+ const int old_edges_num = mesh.totedge;
mesh.totedge += edge_expand;
- CustomData_realloc(&mesh.edata, mesh.totedge);
+ CustomData_realloc(&mesh.edata, old_edges_num, mesh.totedge);
}
if (poly_expand != 0) {
- CustomData_duplicate_referenced_layers(&mesh.pdata, mesh.totpoly);
+ const int old_polys_num = mesh.totpoly;
mesh.totpoly += poly_expand;
- CustomData_realloc(&mesh.pdata, mesh.totpoly);
+ CustomData_realloc(&mesh.pdata, old_polys_num, mesh.totpoly);
}
if (loop_expand != 0) {
- CustomData_duplicate_referenced_layers(&mesh.ldata, mesh.totloop);
+ const int old_loops_num = mesh.totloop;
mesh.totloop += loop_expand;
- CustomData_realloc(&mesh.ldata, mesh.totloop);
+ CustomData_realloc(&mesh.ldata, old_loops_num, mesh.totloop);
}
- BKE_mesh_update_customdata_pointers(&mesh, false);
}
static CustomData &get_customdata(Mesh &mesh, const eAttrDomain domain)
@@ -162,9 +132,12 @@ static CustomData &get_customdata(Mesh &mesh, const eAttrDomain domain)
}
}
+/**
+ * \note The result may be an empty span.
+ */
static MutableSpan<int> get_orig_index_layer(Mesh &mesh, const eAttrDomain domain)
{
- const bke::AttributeAccessor attributes = bke::mesh_attributes(mesh);
+ const bke::AttributeAccessor attributes = mesh.attributes();
CustomData &custom_data = get_customdata(mesh, domain);
if (int *orig_indices = static_cast<int *>(CustomData_get_layer(&custom_data, CD_ORIGINDEX))) {
return {orig_indices, attributes.domain_size(domain)};
@@ -247,16 +220,15 @@ static Array<Vector<int>> create_vert_to_edge_map(const int vert_size,
return vert_to_edge_map;
}
-static void extrude_mesh_vertices(MeshComponent &component,
+static void extrude_mesh_vertices(Mesh &mesh,
const Field<bool> &selection_field,
const Field<float3> &offset_field,
const AttributeOutputs &attribute_outputs)
{
- Mesh &mesh = *component.get_for_write();
const int orig_vert_size = mesh.totvert;
const int orig_edge_size = mesh.totedge;
- GeometryComponentFieldContext context{component, ATTR_DOMAIN_POINT};
+ bke::MeshFieldContext context{mesh, ATTR_DOMAIN_POINT};
FieldEvaluator evaluator{context, mesh.totvert};
evaluator.add(offset_field);
evaluator.set_selection(selection_field);
@@ -265,21 +237,21 @@ static void extrude_mesh_vertices(MeshComponent &component,
const VArray<float3> offsets = evaluator.get_evaluated<float3>(0);
/* This allows parallelizing attribute mixing for new edges. */
- Array<Vector<int>> vert_to_edge_map = create_vert_to_edge_map(orig_vert_size, mesh_edges(mesh));
+ Array<Vector<int>> vert_to_edge_map = create_vert_to_edge_map(orig_vert_size, mesh.edges());
expand_mesh(mesh, selection.size(), selection.size(), 0, 0);
const IndexRange new_vert_range{orig_vert_size, selection.size()};
const IndexRange new_edge_range{orig_edge_size, selection.size()};
- MutableSpan<MVert> new_verts = mesh_verts(mesh).slice(new_vert_range);
- MutableSpan<MEdge> new_edges = mesh_edges(mesh).slice(new_edge_range);
+ MutableSpan<MVert> new_verts = mesh.verts_for_write().slice(new_vert_range);
+ MutableSpan<MEdge> new_edges = mesh.edges_for_write().slice(new_edge_range);
for (const int i_selection : selection.index_range()) {
new_edges[i_selection] = new_loose_edge(selection[i_selection], new_vert_range[i_selection]);
}
- MutableAttributeAccessor attributes = *component.attributes_for_write();
+ MutableAttributeAccessor attributes = mesh.attributes_for_write();
attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
if (!ELEM(meta_data.domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_EDGE)) {
@@ -324,13 +296,16 @@ static void extrude_mesh_vertices(MeshComponent &component,
MutableSpan<int> vert_orig_indices = get_orig_index_layer(mesh, ATTR_DOMAIN_POINT);
vert_orig_indices.slice(new_vert_range).fill(ORIGINDEX_NONE);
+ MutableSpan<int> new_edge_orig_indices = get_orig_index_layer(mesh, ATTR_DOMAIN_EDGE);
+ new_edge_orig_indices.slice(new_edge_range).fill(ORIGINDEX_NONE);
+
if (attribute_outputs.top_id) {
save_selection_as_attribute(
- component, attribute_outputs.top_id.get(), ATTR_DOMAIN_POINT, new_vert_range);
+ mesh, attribute_outputs.top_id.get(), ATTR_DOMAIN_POINT, new_vert_range);
}
if (attribute_outputs.side_id) {
save_selection_as_attribute(
- component, attribute_outputs.side_id.get(), ATTR_DOMAIN_EDGE, new_edge_range);
+ mesh, attribute_outputs.side_id.get(), ATTR_DOMAIN_EDGE, new_edge_range);
}
BKE_mesh_runtime_clear_cache(&mesh);
@@ -338,8 +313,8 @@ static void extrude_mesh_vertices(MeshComponent &component,
static Array<Vector<int, 2>> mesh_calculate_polys_of_edge(const Mesh &mesh)
{
- Span<MPoly> polys = mesh_polys(mesh);
- Span<MLoop> loops = mesh_loops(mesh);
+ Span<MPoly> polys = mesh.polys();
+ Span<MLoop> loops = mesh.loops();
Array<Vector<int, 2>> polys_of_edge(mesh.totedge);
for (const int i_poly : polys.index_range()) {
@@ -397,29 +372,29 @@ template<typename T>
static VectorSet<int> vert_indices_from_edges(const Mesh &mesh, const Span<T> edge_indices)
{
static_assert(is_same_any_v<T, int, int64_t>);
+ const Span<MEdge> edges = mesh.edges();
VectorSet<int> vert_indices;
vert_indices.reserve(edge_indices.size());
for (const T i_edge : edge_indices) {
- const MEdge &edge = mesh.medge[i_edge];
+ const MEdge &edge = edges[i_edge];
vert_indices.add(edge.v1);
vert_indices.add(edge.v2);
}
return vert_indices;
}
-static void extrude_mesh_edges(MeshComponent &component,
+static void extrude_mesh_edges(Mesh &mesh,
const Field<bool> &selection_field,
const Field<float3> &offset_field,
const AttributeOutputs &attribute_outputs)
{
- Mesh &mesh = *component.get_for_write();
const int orig_vert_size = mesh.totvert;
- Span<MEdge> orig_edges = mesh_edges(mesh);
- Span<MPoly> orig_polys = mesh_polys(mesh);
+ Span<MEdge> orig_edges = mesh.edges();
+ Span<MPoly> orig_polys = mesh.polys();
const int orig_loop_size = mesh.totloop;
- GeometryComponentFieldContext edge_context{component, ATTR_DOMAIN_EDGE};
+ bke::MeshFieldContext edge_context{mesh, ATTR_DOMAIN_EDGE};
FieldEvaluator edge_evaluator{edge_context, mesh.totedge};
edge_evaluator.set_selection(selection_field);
edge_evaluator.add(offset_field);
@@ -465,12 +440,12 @@ static void extrude_mesh_edges(MeshComponent &component,
new_poly_range.size(),
new_loop_range.size());
- MutableSpan<MVert> new_verts = mesh_verts(mesh).slice(new_vert_range);
- MutableSpan<MEdge> connect_edges = mesh_edges(mesh).slice(connect_edge_range);
- MutableSpan<MEdge> duplicate_edges = mesh_edges(mesh).slice(duplicate_edge_range);
- MutableSpan<MPoly> polys = mesh_polys(mesh);
+ MutableSpan<MEdge> edges = mesh.edges_for_write();
+ MutableSpan<MEdge> connect_edges = edges.slice(connect_edge_range);
+ MutableSpan<MEdge> duplicate_edges = edges.slice(duplicate_edge_range);
+ MutableSpan<MPoly> polys = mesh.polys_for_write();
MutableSpan<MPoly> new_polys = polys.slice(new_poly_range);
- MutableSpan<MLoop> loops = mesh_loops(mesh);
+ MutableSpan<MLoop> loops = mesh.loops_for_write();
MutableSpan<MLoop> new_loops = loops.slice(new_loop_range);
for (const int i : connect_edges.index_range()) {
@@ -478,7 +453,7 @@ static void extrude_mesh_edges(MeshComponent &component,
}
for (const int i : duplicate_edges.index_range()) {
- const MEdge &orig_edge = mesh.medge[edge_selection[i]];
+ const MEdge &orig_edge = edges[edge_selection[i]];
const int i_new_vert_1 = new_vert_indices.index_of(orig_edge.v1);
const int i_new_vert_2 = new_vert_indices.index_of(orig_edge.v2);
duplicate_edges[i] = new_edge(new_vert_range[i_new_vert_1], new_vert_range[i_new_vert_2]);
@@ -525,7 +500,7 @@ static void extrude_mesh_edges(MeshComponent &component,
const Array<Vector<int>> new_vert_to_duplicate_edge_map = create_vert_to_edge_map(
new_vert_range.size(), duplicate_edges, orig_vert_size);
- MutableAttributeAccessor attributes = *component.attributes_for_write();
+ MutableAttributeAccessor attributes = mesh.attributes_for_write();
attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
GSpanAttributeWriter attribute = attributes.lookup_or_add_for_write_span(
@@ -633,6 +608,7 @@ static void extrude_mesh_edges(MeshComponent &component,
return true;
});
+ MutableSpan<MVert> new_verts = mesh.verts_for_write().slice(new_vert_range);
if (edge_offsets.is_single()) {
const float3 offset = edge_offsets.get_internal_single();
threading::parallel_for(new_verts.index_range(), 1024, [&](const IndexRange range) {
@@ -656,13 +632,16 @@ static void extrude_mesh_edges(MeshComponent &component,
edge_orig_indices.slice(connect_edge_range).fill(ORIGINDEX_NONE);
edge_orig_indices.slice(duplicate_edge_range).fill(ORIGINDEX_NONE);
+ MutableSpan<int> poly_orig_indices = get_orig_index_layer(mesh, ATTR_DOMAIN_FACE);
+ poly_orig_indices.slice(new_poly_range).fill(ORIGINDEX_NONE);
+
if (attribute_outputs.top_id) {
save_selection_as_attribute(
- component, attribute_outputs.top_id.get(), ATTR_DOMAIN_EDGE, duplicate_edge_range);
+ mesh, attribute_outputs.top_id.get(), ATTR_DOMAIN_EDGE, duplicate_edge_range);
}
if (attribute_outputs.side_id) {
save_selection_as_attribute(
- component, attribute_outputs.side_id.get(), ATTR_DOMAIN_FACE, new_poly_range);
+ mesh, attribute_outputs.side_id.get(), ATTR_DOMAIN_FACE, new_poly_range);
}
BKE_mesh_runtime_clear_cache(&mesh);
@@ -672,18 +651,17 @@ static void extrude_mesh_edges(MeshComponent &component,
* Edges connected to one selected face are on the boundary of a region and will be duplicated into
* a "side face". Edges inside a region will be duplicated to leave any original faces unchanged.
*/
-static void extrude_mesh_face_regions(MeshComponent &component,
+static void extrude_mesh_face_regions(Mesh &mesh,
const Field<bool> &selection_field,
const Field<float3> &offset_field,
const AttributeOutputs &attribute_outputs)
{
- Mesh &mesh = *component.get_for_write();
const int orig_vert_size = mesh.totvert;
- Span<MEdge> orig_edges = mesh_edges(mesh);
- Span<MPoly> orig_polys = mesh_polys(mesh);
- Span<MLoop> orig_loops = mesh_loops(mesh);
+ Span<MEdge> orig_edges = mesh.edges();
+ Span<MPoly> orig_polys = mesh.polys();
+ Span<MLoop> orig_loops = mesh.loops();
- GeometryComponentFieldContext poly_context{component, ATTR_DOMAIN_FACE};
+ bke::MeshFieldContext poly_context{mesh, ATTR_DOMAIN_FACE};
FieldEvaluator poly_evaluator{poly_context, mesh.totpoly};
poly_evaluator.set_selection(selection_field);
poly_evaluator.add(offset_field);
@@ -784,7 +762,7 @@ static void extrude_mesh_face_regions(MeshComponent &component,
/* The vertices attached to duplicate inner edges also have to be duplicated. */
for (const int i_edge : new_inner_edge_indices) {
- const MEdge &edge = mesh.medge[i_edge];
+ const MEdge &edge = orig_edges[i_edge];
new_vert_indices.add(edge.v1);
new_vert_indices.add(edge.v2);
}
@@ -808,13 +786,13 @@ static void extrude_mesh_face_regions(MeshComponent &component,
side_poly_range.size(),
side_loop_range.size());
- MutableSpan<MEdge> edges = mesh_edges(mesh);
+ MutableSpan<MEdge> edges = mesh.edges_for_write();
MutableSpan<MEdge> connect_edges = edges.slice(connect_edge_range);
MutableSpan<MEdge> boundary_edges = edges.slice(boundary_edge_range);
MutableSpan<MEdge> new_inner_edges = edges.slice(new_inner_edge_range);
- MutableSpan<MPoly> polys = mesh_polys(mesh);
+ MutableSpan<MPoly> polys = mesh.polys_for_write();
MutableSpan<MPoly> new_polys = polys.slice(side_poly_range);
- MutableSpan<MLoop> loops = mesh_loops(mesh);
+ MutableSpan<MLoop> loops = mesh.loops_for_write();
MutableSpan<MLoop> new_loops = loops.slice(side_loop_range);
/* Initialize the edges that form the sides of the extrusion. */
@@ -905,7 +883,7 @@ static void extrude_mesh_face_regions(MeshComponent &component,
const Array<Vector<int>> new_vert_to_duplicate_edge_map = create_vert_to_edge_map(
new_vert_range.size(), boundary_edges, orig_vert_size);
- MutableAttributeAccessor attributes = *component.attributes_for_write();
+ MutableAttributeAccessor attributes = mesh.attributes_for_write();
attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
GSpanAttributeWriter attribute = attributes.lookup_or_add_for_write_span(
@@ -1003,13 +981,14 @@ static void extrude_mesh_face_regions(MeshComponent &component,
/* Translate vertices based on the offset. If the vertex is used by a selected edge, it will
* have been duplicated and only the new vertex should use the offset. Otherwise the vertex might
* still need an offset, but it was reused on the inside of a region of extruded faces. */
+ MutableSpan<MVert> verts = mesh.verts_for_write();
if (poly_offsets.is_single()) {
const float3 offset = poly_offsets.get_internal_single();
threading::parallel_for(
IndexRange(all_selected_verts.size()), 1024, [&](const IndexRange range) {
for (const int i_orig : all_selected_verts.as_span().slice(range)) {
const int i_new = new_vert_indices.index_of_try(i_orig);
- MVert &vert = mesh_verts(mesh)[(i_new == -1) ? i_orig : new_vert_range[i_new]];
+ MVert &vert = verts[(i_new == -1) ? i_orig : new_vert_range[i_new]];
add_v3_v3(vert.co, offset);
}
});
@@ -1020,7 +999,7 @@ static void extrude_mesh_face_regions(MeshComponent &component,
for (const int i_orig : all_selected_verts.as_span().slice(range)) {
const int i_new = new_vert_indices.index_of_try(i_orig);
const float3 offset = vert_offsets[i_orig];
- MVert &vert = mesh_verts(mesh)[(i_new == -1) ? i_orig : new_vert_range[i_new]];
+ MVert &vert = verts[(i_new == -1) ? i_orig : new_vert_range[i_new]];
add_v3_v3(vert.co, offset);
}
});
@@ -1039,11 +1018,11 @@ static void extrude_mesh_face_regions(MeshComponent &component,
if (attribute_outputs.top_id) {
save_selection_as_attribute(
- component, attribute_outputs.top_id.get(), ATTR_DOMAIN_FACE, poly_selection);
+ mesh, attribute_outputs.top_id.get(), ATTR_DOMAIN_FACE, poly_selection);
}
if (attribute_outputs.side_id) {
save_selection_as_attribute(
- component, attribute_outputs.side_id.get(), ATTR_DOMAIN_FACE, side_poly_range);
+ mesh, attribute_outputs.side_id.get(), ATTR_DOMAIN_FACE, side_poly_range);
}
BKE_mesh_runtime_clear_cache(&mesh);
@@ -1057,21 +1036,20 @@ static IndexRange selected_corner_range(Span<int> offsets, const int index)
return IndexRange(offset, next_offset - offset);
}
-static void extrude_individual_mesh_faces(MeshComponent &component,
+static void extrude_individual_mesh_faces(Mesh &mesh,
const Field<bool> &selection_field,
const Field<float3> &offset_field,
const AttributeOutputs &attribute_outputs)
{
- Mesh &mesh = *component.get_for_write();
const int orig_vert_size = mesh.totvert;
const int orig_edge_size = mesh.totedge;
- Span<MPoly> orig_polys = mesh_polys(mesh);
- Span<MLoop> orig_loops = mesh_loops(mesh);
+ Span<MPoly> orig_polys = mesh.polys();
+ Span<MLoop> orig_loops = mesh.loops();
/* Use a mesh for the result of the evaluation because the mesh is reallocated before
* the vertices are moved, and the evaluated result might reference an attribute. */
Array<float3> poly_offset(orig_polys.size());
- GeometryComponentFieldContext poly_context{component, ATTR_DOMAIN_FACE};
+ bke::MeshFieldContext poly_context{mesh, ATTR_DOMAIN_FACE};
FieldEvaluator poly_evaluator{poly_context, mesh.totpoly};
poly_evaluator.set_selection(selection_field);
poly_evaluator.add_with_destination(offset_field, poly_offset.as_mutable_span());
@@ -1105,13 +1083,13 @@ static void extrude_individual_mesh_faces(MeshComponent &component,
side_poly_range.size(),
side_loop_range.size());
- MutableSpan<MVert> new_verts = mesh_verts(mesh).slice(new_vert_range);
- MutableSpan<MEdge> edges{mesh.medge, mesh.totedge};
+ MutableSpan<MVert> new_verts = mesh.verts_for_write().slice(new_vert_range);
+ MutableSpan<MEdge> edges = mesh.edges_for_write();
MutableSpan<MEdge> connect_edges = edges.slice(connect_edge_range);
MutableSpan<MEdge> duplicate_edges = edges.slice(duplicate_edge_range);
- MutableSpan<MPoly> polys{mesh.mpoly, mesh.totpoly};
+ MutableSpan<MPoly> polys = mesh.polys_for_write();
MutableSpan<MPoly> new_polys = polys.slice(side_poly_range);
- MutableSpan<MLoop> loops{mesh.mloop, mesh.totloop};
+ MutableSpan<MLoop> loops = mesh.loops_for_write();
/* For every selected polygon, build the faces that form the sides of the extrusion. Filling some
* of this data like the new edges or polygons could be easily split into separate loops, which
@@ -1159,7 +1137,7 @@ static void extrude_individual_mesh_faces(MeshComponent &component,
}
});
- MutableAttributeAccessor attributes = *component.attributes_for_write();
+ MutableAttributeAccessor attributes = mesh.attributes_for_write();
attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
GSpanAttributeWriter attribute = attributes.lookup_or_add_for_write_span(
@@ -1318,11 +1296,11 @@ static void extrude_individual_mesh_faces(MeshComponent &component,
if (attribute_outputs.top_id) {
save_selection_as_attribute(
- component, attribute_outputs.top_id.get(), ATTR_DOMAIN_FACE, poly_selection);
+ mesh, attribute_outputs.top_id.get(), ATTR_DOMAIN_FACE, poly_selection);
}
if (attribute_outputs.side_id) {
save_selection_as_attribute(
- component, attribute_outputs.side_id.get(), ATTR_DOMAIN_FACE, side_poly_range);
+ mesh, attribute_outputs.side_id.get(), ATTR_DOMAIN_FACE, side_poly_range);
}
BKE_mesh_runtime_clear_cache(&mesh);
@@ -1359,27 +1337,26 @@ static void node_geo_exec(GeoNodeExecParams params)
params.extract_input<bool>("Individual");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
- if (geometry_set.has_mesh()) {
- MeshComponent &component = geometry_set.get_component_for_write<MeshComponent>();
+ if (Mesh *mesh = geometry_set.get_mesh_for_write()) {
switch (mode) {
case GEO_NODE_EXTRUDE_MESH_VERTICES:
- extrude_mesh_vertices(component, selection, final_offset, attribute_outputs);
+ extrude_mesh_vertices(*mesh, selection, final_offset, attribute_outputs);
break;
case GEO_NODE_EXTRUDE_MESH_EDGES:
- extrude_mesh_edges(component, selection, final_offset, attribute_outputs);
+ extrude_mesh_edges(*mesh, selection, final_offset, attribute_outputs);
break;
case GEO_NODE_EXTRUDE_MESH_FACES: {
if (extrude_individual) {
- extrude_individual_mesh_faces(component, selection, final_offset, attribute_outputs);
+ extrude_individual_mesh_faces(*mesh, selection, final_offset, attribute_outputs);
}
else {
- extrude_mesh_face_regions(component, selection, final_offset, attribute_outputs);
+ extrude_mesh_face_regions(*mesh, selection, final_offset, attribute_outputs);
}
break;
}
}
- BLI_assert(BKE_mesh_is_valid(component.get_for_write()));
+ BLI_assert(BKE_mesh_is_valid(mesh));
}
});
diff --git a/source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc b/source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc
index bde4af12d84..3c9889b664b 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc
@@ -89,7 +89,7 @@ static void node_gather_link_searches(GatherLinkSearchOpParams &params)
}
}
-class FieldAtIndex final : public GeometryFieldInput {
+class FieldAtIndex final : public bke::GeometryFieldInput {
private:
Field<int> index_field_;
GField value_field_;
@@ -97,26 +97,25 @@ class FieldAtIndex final : public GeometryFieldInput {
public:
FieldAtIndex(Field<int> index_field, GField value_field, eAttrDomain value_field_domain)
- : GeometryFieldInput(value_field.cpp_type(), "Field at Index"),
+ : bke::GeometryFieldInput(value_field.cpp_type(), "Field at Index"),
index_field_(std::move(index_field)),
value_field_(std::move(value_field)),
value_field_domain_(value_field_domain)
{
}
- GVArray get_varray_for_context(const GeometryComponent &component,
- const eAttrDomain domain,
- IndexMask mask) const final
+ GVArray get_varray_for_context(const bke::GeometryFieldContext &context,
+ const IndexMask mask) const final
{
- const GeometryComponentFieldContext value_field_context{component, value_field_domain_};
+ const bke::GeometryFieldContext value_field_context{
+ context.geometry(), context.type(), value_field_domain_};
FieldEvaluator value_evaluator{value_field_context,
- component.attribute_domain_size(value_field_domain_)};
+ context.attributes()->domain_size(value_field_domain_)};
value_evaluator.add(value_field_);
value_evaluator.evaluate();
const GVArray &values = value_evaluator.get_evaluated(0);
- const GeometryComponentFieldContext index_field_context{component, domain};
- FieldEvaluator index_evaluator{index_field_context, &mask};
+ FieldEvaluator index_evaluator{context, &mask};
index_evaluator.add(index_field_);
index_evaluator.evaluate();
const VArray<int> indices = index_evaluator.get_evaluated<int>(0);
@@ -129,7 +128,7 @@ class FieldAtIndex final : public GeometryFieldInput {
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()) {
+ if (src_values.index_range().contains(index)) {
dst_array[i] = src_values[index];
}
else {
diff --git a/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc
index 15b2822805a..613425716d4 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc
@@ -19,24 +19,19 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Geometry>(N_("Mesh"));
}
-static void mesh_flip_faces(MeshComponent &component, const Field<bool> &selection_field)
+static void mesh_flip_faces(Mesh &mesh, const Field<bool> &selection_field)
{
- GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_FACE};
- const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE);
- if (domain_size == 0) {
+ if (mesh.totpoly == 0) {
return;
}
- fn::FieldEvaluator evaluator{field_context, domain_size};
+ bke::MeshFieldContext field_context{mesh, ATTR_DOMAIN_FACE};
+ fn::FieldEvaluator evaluator{field_context, mesh.totpoly};
evaluator.add(selection_field);
evaluator.evaluate();
const IndexMask selection = evaluator.get_evaluated_as_mask(0);
- Mesh *mesh = component.get_for_write();
-
- mesh->mloop = (MLoop *)CustomData_duplicate_referenced_layer(
- &mesh->ldata, CD_MLOOP, mesh->totloop);
- Span<MPoly> polys{mesh->mpoly, mesh->totpoly};
- MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop};
+ const Span<MPoly> polys = mesh.polys();
+ MutableSpan<MLoop> loops = mesh.loops_for_write();
for (const int i : selection.index_range()) {
const MPoly &poly = polys[selection[i]];
@@ -49,7 +44,7 @@ static void mesh_flip_faces(MeshComponent &component, const Field<bool> &selecti
}
}
- MutableAttributeAccessor attributes = *component.attributes_for_write();
+ MutableAttributeAccessor attributes = mesh.attributes_for_write();
attributes.for_all(
[&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
if (meta_data.domain == ATTR_DOMAIN_CORNER) {
@@ -76,11 +71,9 @@ static void node_geo_exec(GeoNodeExecParams params)
const Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
- if (!geometry_set.has_mesh()) {
- return;
+ if (Mesh *mesh = geometry_set.get_mesh_for_write()) {
+ mesh_flip_faces(*mesh, selection_field);
}
- MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
- mesh_flip_faces(mesh_component, selection_field);
});
params.set_output("Mesh", std::move(geometry_set));
diff --git a/source/blender/nodes/geometry/nodes/node_geo_geometry_to_instance.cc b/source/blender/nodes/geometry/nodes/node_geo_geometry_to_instance.cc
index 1f84f8f288d..8e64209a418 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_geometry_to_instance.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_geometry_to_instance.cc
@@ -12,7 +12,7 @@ static void node_declare(NodeDeclarationBuilder &b)
static void node_geo_exec(GeoNodeExecParams params)
{
- Vector<GeometrySet> geometries = params.extract_multi_input<GeometrySet>("Geometry");
+ Vector<GeometrySet> geometries = params.extract_input<Vector<GeometrySet>>("Geometry");
GeometrySet instances_geometry;
InstancesComponent &instances_component =
instances_geometry.get_component_for_write<InstancesComponent>();
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc
index bc1b9e940a1..bff2e7831c6 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc
@@ -1,5 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include "BKE_curves.hh"
+
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_input_curve_handles_cc {
@@ -15,31 +17,27 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Vector>(N_("Right")).field_source();
}
-class HandlePositionFieldInput final : public GeometryFieldInput {
+class HandlePositionFieldInput final : public bke::CurvesFieldInput {
Field<bool> relative_;
bool left_;
public:
HandlePositionFieldInput(Field<bool> relative, bool left)
- : GeometryFieldInput(CPPType::get<float3>(), "Handle"), relative_(relative), left_(left)
+ : bke::CurvesFieldInput(CPPType::get<float3>(), "Handle"), relative_(relative), left_(left)
{
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const bke::CurvesGeometry &curves,
const eAttrDomain domain,
- IndexMask mask) const final
+ const IndexMask mask) const final
{
- if (component.type() != GEO_COMPONENT_TYPE_CURVE) {
- return {};
- }
-
- GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
+ bke::CurvesFieldContext field_context{curves, ATTR_DOMAIN_POINT};
fn::FieldEvaluator evaluator(field_context, &mask);
evaluator.add(relative_);
evaluator.evaluate();
const VArray<bool> relative = evaluator.get_evaluated<bool>(0);
- const AttributeAccessor attributes = *component.attributes();
+ const AttributeAccessor attributes = curves.attributes();
VArray<float3> positions = attributes.lookup_or_default<float3>(
"position", ATTR_DOMAIN_POINT, {0, 0, 0});
@@ -69,7 +67,7 @@ class HandlePositionFieldInput final : public GeometryFieldInput {
output[i] = handles[i];
}
}
- return component.attributes()->adapt_domain<float3>(
+ return attributes.adapt_domain<float3>(
VArray<float3>::ForContainer(std::move(output)), ATTR_DOMAIN_POINT, domain);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_instance_rotation.cc b/source/blender/nodes/geometry/nodes/node_geo_input_instance_rotation.cc
index 4c7a148a797..8c5a92904ab 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_instance_rotation.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_instance_rotation.cc
@@ -9,28 +9,20 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Vector>(N_("Rotation")).field_source();
}
-class VectorFieldInput final : public GeometryFieldInput {
+class InstanceRotationFieldInput final : public bke::InstancesFieldInput {
public:
- VectorFieldInput() : GeometryFieldInput(CPPType::get<float3>(), "Rotation")
+ InstanceRotationFieldInput() : bke::InstancesFieldInput(CPPType::get<float3>(), "Rotation")
{
}
- GVArray get_varray_for_context(const GeometryComponent &component,
- const eAttrDomain UNUSED(domain),
+ GVArray get_varray_for_context(const InstancesComponent &instances,
IndexMask UNUSED(mask)) const final
{
- if (component.type() != GEO_COMPONENT_TYPE_INSTANCES) {
- return {};
- }
-
- const InstancesComponent &instance_component = static_cast<const InstancesComponent &>(
- component);
-
auto rotation_fn = [&](const int i) -> float3 {
- return instance_component.instance_transforms()[i].to_euler();
+ return instances.instance_transforms()[i].to_euler();
};
- return VArray<float3>::ForFunc(instance_component.instances_num(), rotation_fn);
+ return VArray<float3>::ForFunc(instances.instances_num(), rotation_fn);
}
uint64_t hash() const override
@@ -40,13 +32,13 @@ class VectorFieldInput final : public GeometryFieldInput {
bool is_equal_to(const fn::FieldNode &other) const override
{
- return dynamic_cast<const VectorFieldInput *>(&other) != nullptr;
+ return dynamic_cast<const InstanceRotationFieldInput *>(&other) != nullptr;
}
};
static void node_geo_exec(GeoNodeExecParams params)
{
- Field<float3> rotation{std::make_shared<VectorFieldInput>()};
+ Field<float3> rotation{std::make_shared<InstanceRotationFieldInput>()};
params.set_output("Rotation", std::move(rotation));
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_instance_scale.cc b/source/blender/nodes/geometry/nodes/node_geo_input_instance_scale.cc
index b3a362fbf3e..b79e73915b7 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_instance_scale.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_instance_scale.cc
@@ -9,28 +9,20 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Vector>(N_("Scale")).field_source();
}
-class VectorFieldInput final : public GeometryFieldInput {
+class InstanceScaleFieldInput final : public bke::InstancesFieldInput {
public:
- VectorFieldInput() : GeometryFieldInput(CPPType::get<float3>(), "Scale")
+ InstanceScaleFieldInput() : bke::InstancesFieldInput(CPPType::get<float3>(), "Scale")
{
}
- GVArray get_varray_for_context(const GeometryComponent &component,
- const eAttrDomain UNUSED(domain),
+ GVArray get_varray_for_context(const InstancesComponent &instances,
IndexMask UNUSED(mask)) const final
{
- if (component.type() != GEO_COMPONENT_TYPE_INSTANCES) {
- return {};
- }
-
- const InstancesComponent &instance_component = static_cast<const InstancesComponent &>(
- component);
-
auto scale_fn = [&](const int i) -> float3 {
- return instance_component.instance_transforms()[i].scale();
+ return instances.instance_transforms()[i].scale();
};
- return VArray<float3>::ForFunc(instance_component.instances_num(), scale_fn);
+ return VArray<float3>::ForFunc(instances.instances_num(), scale_fn);
}
uint64_t hash() const override
@@ -40,13 +32,13 @@ class VectorFieldInput final : public GeometryFieldInput {
bool is_equal_to(const fn::FieldNode &other) const override
{
- return dynamic_cast<const VectorFieldInput *>(&other) != nullptr;
+ return dynamic_cast<const InstanceScaleFieldInput *>(&other) != nullptr;
}
};
static void node_geo_exec(GeoNodeExecParams params)
{
- Field<float3> scale{std::make_shared<VectorFieldInput>()};
+ Field<float3> scale{std::make_shared<InstanceScaleFieldInput>()};
params.set_output("Scale", std::move(scale));
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_angle.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_angle.cc
index b009aaa5291..f2e7379b3a2 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_angle.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_angle.cc
@@ -53,46 +53,36 @@ static Array<EdgeMapEntry> create_edge_map(const Span<MPoly> polys,
return edge_map;
}
-class AngleFieldInput final : public GeometryFieldInput {
+class AngleFieldInput final : public bke::MeshFieldInput {
public:
- AngleFieldInput() : GeometryFieldInput(CPPType::get<float>(), "Unsigned Angle Field")
+ AngleFieldInput() : bke::MeshFieldInput(CPPType::get<float>(), "Unsigned Angle Field")
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const Mesh &mesh,
const eAttrDomain domain,
IndexMask UNUSED(mask)) const final
{
- if (component.type() != GEO_COMPONENT_TYPE_MESH) {
- return {};
- }
-
- const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
- const Mesh *mesh = mesh_component.get_for_read();
- if (mesh == nullptr) {
- return {};
- }
+ const Span<MVert> verts = mesh.verts();
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
+ Array<EdgeMapEntry> edge_map = create_edge_map(polys, loops, mesh.totedge);
- Span<MPoly> polys{mesh->mpoly, mesh->totpoly};
- Span<MLoop> loops{mesh->mloop, mesh->totloop};
- Array<EdgeMapEntry> edge_map = create_edge_map(polys, loops, mesh->totedge);
-
- auto angle_fn = [edge_map, polys, loops, mesh](const int i) -> float {
+ auto angle_fn = [edge_map = std::move(edge_map), verts, polys, loops](const int i) -> float {
if (edge_map[i].face_count != 2) {
return 0.0f;
}
const MPoly &mpoly_1 = polys[edge_map[i].face_index_1];
const MPoly &mpoly_2 = polys[edge_map[i].face_index_2];
float3 normal_1, normal_2;
- BKE_mesh_calc_poly_normal(&mpoly_1, &loops[mpoly_1.loopstart], mesh->mvert, normal_1);
- BKE_mesh_calc_poly_normal(&mpoly_2, &loops[mpoly_2.loopstart], mesh->mvert, normal_2);
+ BKE_mesh_calc_poly_normal(&mpoly_1, &loops[mpoly_1.loopstart], verts.data(), normal_1);
+ BKE_mesh_calc_poly_normal(&mpoly_2, &loops[mpoly_2.loopstart], verts.data(), normal_2);
return angle_normalized_v3v3(normal_1, normal_2);
};
- VArray<float> angles = VArray<float>::ForFunc(mesh->totedge, angle_fn);
- return component.attributes()->adapt_domain<float>(
- std::move(angles), ATTR_DOMAIN_EDGE, domain);
+ VArray<float> angles = VArray<float>::ForFunc(mesh.totedge, angle_fn);
+ return mesh.attributes().adapt_domain<float>(std::move(angles), ATTR_DOMAIN_EDGE, domain);
}
uint64_t hash() const override
@@ -107,32 +97,25 @@ class AngleFieldInput final : public GeometryFieldInput {
}
};
-class SignedAngleFieldInput final : public GeometryFieldInput {
+class SignedAngleFieldInput final : public bke::MeshFieldInput {
public:
- SignedAngleFieldInput() : GeometryFieldInput(CPPType::get<float>(), "Signed Angle Field")
+ SignedAngleFieldInput() : bke::MeshFieldInput(CPPType::get<float>(), "Signed Angle Field")
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const Mesh &mesh,
const eAttrDomain domain,
IndexMask UNUSED(mask)) const final
{
- if (component.type() != GEO_COMPONENT_TYPE_MESH) {
- return {};
- }
-
- const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
- const Mesh *mesh = mesh_component.get_for_read();
- if (mesh == nullptr) {
- return {};
- }
-
- Span<MPoly> polys{mesh->mpoly, mesh->totpoly};
- Span<MLoop> loops{mesh->mloop, mesh->totloop};
- Array<EdgeMapEntry> edge_map = create_edge_map(polys, loops, mesh->totedge);
-
- auto angle_fn = [edge_map, polys, loops, mesh](const int i) -> float {
+ const Span<MVert> verts = mesh.verts();
+ const Span<MEdge> edges = mesh.edges();
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
+ Array<EdgeMapEntry> edge_map = create_edge_map(polys, loops, mesh.totedge);
+
+ auto angle_fn =
+ [edge_map = std::move(edge_map), verts, edges, polys, loops](const int i) -> float {
if (edge_map[i].face_count != 2) {
return 0.0f;
}
@@ -141,18 +124,18 @@ class SignedAngleFieldInput final : public GeometryFieldInput {
/* Find the normals of the 2 polys. */
float3 poly_1_normal, poly_2_normal;
- BKE_mesh_calc_poly_normal(&mpoly_1, &loops[mpoly_1.loopstart], mesh->mvert, poly_1_normal);
- BKE_mesh_calc_poly_normal(&mpoly_2, &loops[mpoly_2.loopstart], mesh->mvert, poly_2_normal);
+ BKE_mesh_calc_poly_normal(&mpoly_1, &loops[mpoly_1.loopstart], verts.data(), poly_1_normal);
+ BKE_mesh_calc_poly_normal(&mpoly_2, &loops[mpoly_2.loopstart], verts.data(), poly_2_normal);
/* Find the centerpoint of the axis edge */
- const float3 edge_centerpoint = (float3(mesh->mvert[mesh->medge[i].v1].co) +
- float3(mesh->mvert[mesh->medge[i].v2].co)) *
+ const float3 edge_centerpoint = (float3(verts[edges[i].v1].co) +
+ float3(verts[edges[i].v2].co)) *
0.5f;
/* Get the centerpoint of poly 2 and subtract the edge centerpoint to get a tangent
* normal for poly 2. */
float3 poly_center_2;
- BKE_mesh_calc_poly_center(&mpoly_2, &loops[mpoly_2.loopstart], mesh->mvert, poly_center_2);
+ BKE_mesh_calc_poly_center(&mpoly_2, &loops[mpoly_2.loopstart], verts.data(), poly_center_2);
const float3 poly_2_tangent = math::normalize(poly_center_2 - edge_centerpoint);
const float concavity = math::dot(poly_1_normal, poly_2_tangent);
@@ -165,9 +148,8 @@ class SignedAngleFieldInput final : public GeometryFieldInput {
return -angle;
};
- VArray<float> angles = VArray<float>::ForFunc(mesh->totedge, angle_fn);
- return component.attributes()->adapt_domain<float>(
- std::move(angles), ATTR_DOMAIN_EDGE, domain);
+ VArray<float> angles = VArray<float>::ForFunc(mesh.totedge, angle_fn);
+ return mesh.attributes().adapt_domain<float>(std::move(angles), ATTR_DOMAIN_EDGE, domain);
}
uint64_t hash() const override
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_neighbors.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_neighbors.cc
index 50d6998bb27..bfe8753c039 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_neighbors.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_neighbors.cc
@@ -16,34 +16,26 @@ static void node_declare(NodeDeclarationBuilder &b)
.description(N_("The number of faces that use each edge as one of their sides"));
}
-class EdgeNeighborCountFieldInput final : public GeometryFieldInput {
+class EdgeNeighborCountFieldInput final : public bke::MeshFieldInput {
public:
EdgeNeighborCountFieldInput()
- : GeometryFieldInput(CPPType::get<int>(), "Edge Neighbor Count Field")
+ : bke::MeshFieldInput(CPPType::get<int>(), "Edge Neighbor Count Field")
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const Mesh &mesh,
const eAttrDomain domain,
IndexMask UNUSED(mask)) const final
{
- if (component.type() == GEO_COMPONENT_TYPE_MESH) {
- const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
- const Mesh *mesh = mesh_component.get_for_read();
- if (mesh == nullptr) {
- return {};
- }
-
- Array<int> face_count(mesh->totedge, 0);
- for (const int i : IndexRange(mesh->totloop)) {
- face_count[mesh->mloop[i].e]++;
- }
-
- return mesh_component.attributes()->adapt_domain<int>(
- VArray<int>::ForContainer(std::move(face_count)), ATTR_DOMAIN_EDGE, domain);
+ const Span<MLoop> loops = mesh.loops();
+ Array<int> face_count(mesh.totedge, 0);
+ for (const MLoop &loop : loops) {
+ face_count[loop.e]++;
}
- return {};
+
+ return mesh.attributes().adapt_domain<int>(
+ VArray<int>::ForContainer(std::move(face_count)), ATTR_DOMAIN_EDGE, domain);
}
uint64_t hash() const override
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_vertices.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_vertices.cc
index 83e511f45c2..c8ceae239a4 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_vertices.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_vertices.cc
@@ -27,45 +27,37 @@ static void node_declare(NodeDeclarationBuilder &b)
enum VertexNumber { VERTEX_ONE, VERTEX_TWO };
-static VArray<int> construct_edge_vertices_gvarray(const MeshComponent &component,
- const VertexNumber vertex,
- const eAttrDomain domain)
+static VArray<int> construct_edge_verts_gvarray(const Mesh &mesh,
+ const VertexNumber vertex,
+ const eAttrDomain domain)
{
- const Mesh *mesh = component.get_for_read();
- if (mesh == nullptr) {
- return {};
- }
+ const Span<MEdge> edges = mesh.edges();
if (domain == ATTR_DOMAIN_EDGE) {
if (vertex == VERTEX_ONE) {
- return VArray<int>::ForFunc(mesh->totedge,
- [mesh](const int i) -> int { return mesh->medge[i].v1; });
+ return VArray<int>::ForFunc(edges.size(),
+ [edges](const int i) -> int { return edges[i].v1; });
}
- return VArray<int>::ForFunc(mesh->totedge,
- [mesh](const int i) -> int { return mesh->medge[i].v2; });
+ return VArray<int>::ForFunc(edges.size(), [edges](const int i) -> int { return edges[i].v2; });
}
return {};
}
-class EdgeVerticesFieldInput final : public GeometryFieldInput {
+class EdgeVerticesFieldInput final : public bke::MeshFieldInput {
private:
VertexNumber vertex_;
public:
EdgeVerticesFieldInput(VertexNumber vertex)
- : GeometryFieldInput(CPPType::get<int>(), "Edge Vertices Field"), vertex_(vertex)
+ : bke::MeshFieldInput(CPPType::get<int>(), "Edge Vertices Field"), vertex_(vertex)
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const Mesh &mesh,
const eAttrDomain domain,
IndexMask UNUSED(mask)) const final
{
- if (component.type() == GEO_COMPONENT_TYPE_MESH) {
- const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
- return construct_edge_vertices_gvarray(mesh_component, vertex_, domain);
- }
- return {};
+ return construct_edge_verts_gvarray(mesh, vertex_, domain);
}
uint64_t hash() const override
@@ -83,51 +75,43 @@ class EdgeVerticesFieldInput final : public GeometryFieldInput {
}
};
-static VArray<float3> construct_edge_positions_gvarray(const MeshComponent &component,
+static VArray<float3> construct_edge_positions_gvarray(const Mesh &mesh,
const VertexNumber vertex,
const eAttrDomain domain)
{
- const Mesh *mesh = component.get_for_read();
- if (mesh == nullptr) {
- return {};
- }
+ const Span<MVert> verts = mesh.verts();
+ const Span<MEdge> edges = mesh.edges();
if (vertex == VERTEX_ONE) {
- return component.attributes()->adapt_domain<float3>(
- VArray<float3>::ForFunc(
- mesh->totedge,
- [mesh](const int i) { return float3(mesh->mvert[mesh->medge[i].v1].co); }),
+ return mesh.attributes().adapt_domain<float3>(
+ VArray<float3>::ForFunc(edges.size(),
+ [verts, edges](const int i) { return verts[edges[i].v1].co; }),
ATTR_DOMAIN_EDGE,
domain);
}
- return component.attributes()->adapt_domain<float3>(
- VArray<float3>::ForFunc(
- mesh->totedge,
- [mesh](const int i) { return float3(mesh->mvert[mesh->medge[i].v2].co); }),
+ return mesh.attributes().adapt_domain<float3>(
+ VArray<float3>::ForFunc(edges.size(),
+ [verts, edges](const int i) { return verts[edges[i].v2].co; }),
ATTR_DOMAIN_EDGE,
domain);
}
-class EdgePositionFieldInput final : public GeometryFieldInput {
+class EdgePositionFieldInput final : public bke::MeshFieldInput {
private:
VertexNumber vertex_;
public:
EdgePositionFieldInput(VertexNumber vertex)
- : GeometryFieldInput(CPPType::get<float3>(), "Edge Position Field"), vertex_(vertex)
+ : bke::MeshFieldInput(CPPType::get<float3>(), "Edge Position Field"), vertex_(vertex)
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const Mesh &mesh,
const eAttrDomain domain,
IndexMask UNUSED(mask)) const final
{
- if (component.type() == GEO_COMPONENT_TYPE_MESH) {
- const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
- return construct_edge_positions_gvarray(mesh_component, vertex_, domain);
- }
- return {};
+ return construct_edge_positions_gvarray(mesh, vertex_, domain);
}
uint64_t hash() const override
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_area.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_area.cc
index 4d21bf9443a..be921c1f1c5 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_area.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_area.cc
@@ -16,39 +16,33 @@ static void node_declare(NodeDeclarationBuilder &b)
.description(N_("The surface area of each of the mesh's faces"));
}
-static VArray<float> construct_face_area_gvarray(const MeshComponent &component,
- const eAttrDomain domain)
+static VArray<float> construct_face_area_varray(const Mesh &mesh, const eAttrDomain domain)
{
- const Mesh *mesh = component.get_for_read();
- if (mesh == nullptr) {
- return {};
- }
+ const Span<MVert> verts = mesh.verts();
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
- auto area_fn = [mesh](const int i) -> float {
- const MPoly *mp = &mesh->mpoly[i];
- return BKE_mesh_calc_poly_area(mp, &mesh->mloop[mp->loopstart], mesh->mvert);
+ auto area_fn = [verts, polys, loops](const int i) -> float {
+ const MPoly &poly = polys[i];
+ return BKE_mesh_calc_poly_area(&poly, &loops[poly.loopstart], verts.data());
};
- return component.attributes()->adapt_domain<float>(
- VArray<float>::ForFunc(mesh->totpoly, area_fn), ATTR_DOMAIN_FACE, domain);
+ return mesh.attributes().adapt_domain<float>(
+ VArray<float>::ForFunc(polys.size(), area_fn), ATTR_DOMAIN_FACE, domain);
}
-class FaceAreaFieldInput final : public GeometryFieldInput {
+class FaceAreaFieldInput final : public bke::MeshFieldInput {
public:
- FaceAreaFieldInput() : GeometryFieldInput(CPPType::get<float>(), "Face Area Field")
+ FaceAreaFieldInput() : bke::MeshFieldInput(CPPType::get<float>(), "Face Area Field")
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const Mesh &mesh,
const eAttrDomain domain,
IndexMask UNUSED(mask)) const final
{
- if (component.type() == GEO_COMPONENT_TYPE_MESH) {
- const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
- return construct_face_area_gvarray(mesh_component, domain);
- }
- return {};
+ return construct_face_area_varray(mesh, domain);
}
uint64_t hash() const override
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_is_planar.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_is_planar.cc
index 6b04ff08d9e..72c45de7b0f 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_is_planar.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_is_planar.cc
@@ -22,53 +22,45 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Bool>("Planar").field_source();
}
-class PlanarFieldInput final : public GeometryFieldInput {
+class PlanarFieldInput final : public bke::MeshFieldInput {
private:
Field<float> threshold_;
public:
PlanarFieldInput(Field<float> threshold)
- : GeometryFieldInput(CPPType::get<bool>(), "Planar"), threshold_(threshold)
+ : bke::MeshFieldInput(CPPType::get<bool>(), "Planar"), threshold_(threshold)
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const Mesh &mesh,
const eAttrDomain domain,
- [[maybe_unused]] IndexMask mask) const final
+ IndexMask /*mask*/) const final
{
- if (component.type() != GEO_COMPONENT_TYPE_MESH) {
- return {};
- }
-
- const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
- const Mesh *mesh = mesh_component.get_for_read();
- if (mesh == nullptr) {
- return {};
- }
-
- GeometryComponentFieldContext context{mesh_component, ATTR_DOMAIN_FACE};
- fn::FieldEvaluator evaluator{context, mesh->totpoly};
+ const Span<MVert> verts = mesh.verts();
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
+ const Span<float3> poly_normals{(float3 *)BKE_mesh_poly_normals_ensure(&mesh), mesh.totpoly};
+
+ bke::MeshFieldContext context{mesh, ATTR_DOMAIN_FACE};
+ fn::FieldEvaluator evaluator{context, polys.size()};
evaluator.add(threshold_);
evaluator.evaluate();
const VArray<float> thresholds = evaluator.get_evaluated<float>(0);
- Span<float3> poly_normals{(float3 *)BKE_mesh_poly_normals_ensure(mesh), mesh->totpoly};
-
- auto planar_fn = [mesh, thresholds, poly_normals](const int i_poly) -> bool {
- if (mesh->mpoly[i_poly].totloop <= 3) {
+ auto planar_fn = [verts, polys, loops, thresholds, poly_normals](const int i) -> bool {
+ const MPoly &poly = polys[i];
+ if (poly.totloop <= 3) {
return true;
}
- const int loopstart = mesh->mpoly[i_poly].loopstart;
- const int loops = mesh->mpoly[i_poly].totloop;
- Span<MLoop> poly_loops(&mesh->mloop[loopstart], loops);
- float3 reference_normal = poly_normals[i_poly];
+ const Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop);
+ const float3 &reference_normal = poly_normals[i];
float min = FLT_MAX;
float max = -FLT_MAX;
for (const int i_loop : poly_loops.index_range()) {
- const float3 vert = mesh->mvert[poly_loops[i_loop].v].co;
+ const float3 vert = verts[poly_loops[i_loop].v].co;
float dot = math::dot(reference_normal, vert);
if (dot > max) {
max = dot;
@@ -77,11 +69,11 @@ class PlanarFieldInput final : public GeometryFieldInput {
min = dot;
}
}
- return max - min < thresholds[i_poly] / 2.0f;
+ return max - min < thresholds[i] / 2.0f;
};
- return component.attributes()->adapt_domain<bool>(
- VArray<bool>::ForFunc(mesh->totpoly, planar_fn), ATTR_DOMAIN_FACE, domain);
+ return mesh.attributes().adapt_domain<bool>(
+ VArray<bool>::ForFunc(polys.size(), planar_fn), ATTR_DOMAIN_FACE, domain);
}
uint64_t hash() const override
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_neighbors.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_neighbors.cc
index a225ce61b14..9e85eae3a31 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_neighbors.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_neighbors.cc
@@ -19,48 +19,41 @@ static void node_declare(NodeDeclarationBuilder &b)
.description(N_("Number of faces which share an edge with the face"));
}
-static VArray<int> construct_neighbor_count_gvarray(const MeshComponent &component,
- const eAttrDomain domain)
+static VArray<int> construct_neighbor_count_varray(const Mesh &mesh, const eAttrDomain domain)
{
- const Mesh *mesh = component.get_for_read();
- if (mesh == nullptr) {
- return {};
- }
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
- Array<int> edge_count(mesh->totedge, 0);
- for (const int i : IndexRange(mesh->totloop)) {
- edge_count[mesh->mloop[i].e]++;
+ Array<int> edge_count(mesh.totedge, 0);
+ for (const MLoop &loop : loops) {
+ edge_count[loop.e]++;
}
- Array<int> poly_count(mesh->totpoly, 0);
- for (const int poly_num : IndexRange(mesh->totpoly)) {
- MPoly &poly = mesh->mpoly[poly_num];
- for (const int loop_num : IndexRange(poly.loopstart, poly.totloop)) {
- poly_count[poly_num] += edge_count[mesh->mloop[loop_num].e] - 1;
+ Array<int> poly_count(polys.size(), 0);
+ for (const int poly_index : polys.index_range()) {
+ const MPoly &poly = polys[poly_index];
+ for (const MLoop &loop : loops.slice(poly.loopstart, poly.totloop)) {
+ poly_count[poly_index] += edge_count[loop.e] - 1;
}
}
- return component.attributes()->adapt_domain<int>(
+ return mesh.attributes().adapt_domain<int>(
VArray<int>::ForContainer(std::move(poly_count)), ATTR_DOMAIN_FACE, domain);
}
-class FaceNeighborCountFieldInput final : public GeometryFieldInput {
+class FaceNeighborCountFieldInput final : public bke::MeshFieldInput {
public:
FaceNeighborCountFieldInput()
- : GeometryFieldInput(CPPType::get<int>(), "Face Neighbor Count Field")
+ : bke::MeshFieldInput(CPPType::get<int>(), "Face Neighbor Count Field")
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const Mesh &mesh,
const eAttrDomain domain,
IndexMask UNUSED(mask)) const final
{
- if (component.type() == GEO_COMPONENT_TYPE_MESH) {
- const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
- return construct_neighbor_count_gvarray(mesh_component, domain);
- }
- return {};
+ return construct_neighbor_count_varray(mesh, domain);
}
uint64_t hash() const override
@@ -75,37 +68,28 @@ class FaceNeighborCountFieldInput final : public GeometryFieldInput {
}
};
-static VArray<int> construct_vertex_count_gvarray(const MeshComponent &component,
- const eAttrDomain domain)
+static VArray<int> construct_vertex_count_varray(const Mesh &mesh, const eAttrDomain domain)
{
- const Mesh *mesh = component.get_for_read();
- if (mesh == nullptr) {
- return {};
- }
-
- return component.attributes()->adapt_domain<int>(
- VArray<int>::ForFunc(mesh->totpoly,
- [mesh](const int i) -> float { return mesh->mpoly[i].totloop; }),
+ const Span<MPoly> polys = mesh.polys();
+ return mesh.attributes().adapt_domain<int>(
+ VArray<int>::ForFunc(polys.size(),
+ [polys](const int i) -> float { return polys[i].totloop; }),
ATTR_DOMAIN_FACE,
domain);
}
-class FaceVertexCountFieldInput final : public GeometryFieldInput {
+class FaceVertexCountFieldInput final : public bke::MeshFieldInput {
public:
- FaceVertexCountFieldInput() : GeometryFieldInput(CPPType::get<int>(), "Vertex Count Field")
+ FaceVertexCountFieldInput() : bke::MeshFieldInput(CPPType::get<int>(), "Vertex Count Field")
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const Mesh &mesh,
const eAttrDomain domain,
IndexMask UNUSED(mask)) const final
{
- if (component.type() == GEO_COMPONENT_TYPE_MESH) {
- const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
- return construct_vertex_count_gvarray(mesh_component, domain);
- }
- return {};
+ return construct_vertex_count_varray(mesh, domain);
}
uint64_t hash() const override
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc
index 2c7eef5665f..9d7735e707d 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc
@@ -22,39 +22,32 @@ static void node_declare(NodeDeclarationBuilder &b)
.description(N_("The total number of mesh islands"));
}
-class IslandFieldInput final : public GeometryFieldInput {
+class IslandFieldInput final : public bke::MeshFieldInput {
public:
- IslandFieldInput() : GeometryFieldInput(CPPType::get<int>(), "Island Index")
+ IslandFieldInput() : bke::MeshFieldInput(CPPType::get<int>(), "Island Index")
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const Mesh &mesh,
const eAttrDomain domain,
IndexMask UNUSED(mask)) const final
{
- if (component.type() != GEO_COMPONENT_TYPE_MESH) {
- return {};
- }
- const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
- const Mesh *mesh = mesh_component.get_for_read();
- if (mesh == nullptr) {
- return {};
- }
+ const Span<MEdge> edges = mesh.edges();
- DisjointSet islands(mesh->totvert);
- for (const int i : IndexRange(mesh->totedge)) {
- islands.join(mesh->medge[i].v1, mesh->medge[i].v2);
+ DisjointSet islands(mesh.totvert);
+ for (const int i : edges.index_range()) {
+ islands.join(edges[i].v1, edges[i].v2);
}
- Array<int> output(mesh->totvert);
+ Array<int> output(mesh.totvert);
VectorSet<int> ordered_roots;
- for (const int i : IndexRange(mesh->totvert)) {
+ for (const int i : IndexRange(mesh.totvert)) {
const int64_t root = islands.find_root(i);
output[i] = ordered_roots.index_of_or_add(root);
}
- return mesh_component.attributes()->adapt_domain<int>(
+ return mesh.attributes().adapt_domain<int>(
VArray<int>::ForContainer(std::move(output)), ATTR_DOMAIN_POINT, domain);
}
@@ -70,39 +63,31 @@ class IslandFieldInput final : public GeometryFieldInput {
}
};
-class IslandCountFieldInput final : public GeometryFieldInput {
+class IslandCountFieldInput final : public bke::MeshFieldInput {
public:
- IslandCountFieldInput() : GeometryFieldInput(CPPType::get<int>(), "Island Count")
+ IslandCountFieldInput() : bke::MeshFieldInput(CPPType::get<int>(), "Island Count")
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const Mesh &mesh,
const eAttrDomain domain,
IndexMask UNUSED(mask)) const final
{
- if (component.type() != GEO_COMPONENT_TYPE_MESH) {
- return {};
- }
- const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
- const Mesh *mesh = mesh_component.get_for_read();
- if (mesh == nullptr) {
- return {};
- }
+ const Span<MEdge> edges = mesh.edges();
- DisjointSet islands(mesh->totvert);
- for (const int i : IndexRange(mesh->totedge)) {
- islands.join(mesh->medge[i].v1, mesh->medge[i].v2);
+ DisjointSet islands(mesh.totvert);
+ for (const int i : edges.index_range()) {
+ islands.join(edges[i].v1, edges[i].v2);
}
Set<int> island_list;
- for (const int i_vert : IndexRange(mesh->totvert)) {
+ for (const int i_vert : IndexRange(mesh.totvert)) {
const int64_t root = islands.find_root(i_vert);
island_list.add(root);
}
- return VArray<int>::ForSingle(island_list.size(),
- mesh_component.attribute_domain_size(domain));
+ return VArray<int>::ForSingle(island_list.size(), mesh.attributes().domain_size(domain));
}
uint64_t hash() const override
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_vertex_neighbors.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_vertex_neighbors.cc
index 62b3f9d0e92..ab44a6c8515 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_vertex_neighbors.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_vertex_neighbors.cc
@@ -20,41 +20,32 @@ static void node_declare(NodeDeclarationBuilder &b)
.description(N_("Number of faces that contain the vertex"));
}
-static VArray<int> construct_vertex_count_gvarray(const MeshComponent &component,
- const eAttrDomain domain)
+static VArray<int> construct_vertex_count_gvarray(const Mesh &mesh, const eAttrDomain domain)
{
- const Mesh *mesh = component.get_for_read();
- if (mesh == nullptr) {
- return {};
- }
-
+ const Span<MEdge> edges = mesh.edges();
if (domain == ATTR_DOMAIN_POINT) {
- Array<int> vertices(mesh->totvert, 0);
- for (const int i : IndexRange(mesh->totedge)) {
- vertices[mesh->medge[i].v1]++;
- vertices[mesh->medge[i].v2]++;
+ Array<int> counts(mesh.totvert, 0);
+ for (const int i : edges.index_range()) {
+ counts[edges[i].v1]++;
+ counts[edges[i].v2]++;
}
- return VArray<int>::ForContainer(std::move(vertices));
+ return VArray<int>::ForContainer(std::move(counts));
}
return {};
}
-class VertexCountFieldInput final : public GeometryFieldInput {
+class VertexCountFieldInput final : public bke::MeshFieldInput {
public:
- VertexCountFieldInput() : GeometryFieldInput(CPPType::get<int>(), "Vertex Count Field")
+ VertexCountFieldInput() : bke::MeshFieldInput(CPPType::get<int>(), "Vertex Count Field")
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const Mesh &mesh,
const eAttrDomain domain,
IndexMask UNUSED(mask)) const final
{
- if (component.type() == GEO_COMPONENT_TYPE_MESH) {
- const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
- return construct_vertex_count_gvarray(mesh_component, domain);
- }
- return {};
+ return construct_vertex_count_gvarray(mesh, domain);
}
uint64_t hash() const override
@@ -69,18 +60,13 @@ class VertexCountFieldInput final : public GeometryFieldInput {
}
};
-static VArray<int> construct_face_count_gvarray(const MeshComponent &component,
- const eAttrDomain domain)
+static VArray<int> construct_face_count_gvarray(const Mesh &mesh, const eAttrDomain domain)
{
- const Mesh *mesh = component.get_for_read();
- if (mesh == nullptr) {
- return {};
- }
-
+ const Span<MLoop> loops = mesh.loops();
if (domain == ATTR_DOMAIN_POINT) {
- Array<int> vertices(mesh->totvert, 0);
- for (const int i : IndexRange(mesh->totloop)) {
- int vertex = mesh->mloop[i].v;
+ Array<int> vertices(mesh.totvert, 0);
+ for (const int i : loops.index_range()) {
+ int vertex = loops[i].v;
vertices[vertex]++;
}
return VArray<int>::ForContainer(std::move(vertices));
@@ -88,22 +74,18 @@ static VArray<int> construct_face_count_gvarray(const MeshComponent &component,
return {};
}
-class VertexFaceCountFieldInput final : public GeometryFieldInput {
+class VertexFaceCountFieldInput final : public bke::MeshFieldInput {
public:
- VertexFaceCountFieldInput() : GeometryFieldInput(CPPType::get<int>(), "Vertex Face Count Field")
+ VertexFaceCountFieldInput() : bke::MeshFieldInput(CPPType::get<int>(), "Vertex Face Count Field")
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const Mesh &mesh,
const eAttrDomain domain,
IndexMask UNUSED(mask)) const final
{
- if (component.type() == GEO_COMPONENT_TYPE_MESH) {
- const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
- return construct_face_count_gvarray(mesh_component, domain);
- }
- return {};
+ return construct_face_count_gvarray(mesh, domain);
}
uint64_t hash() const override
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_named_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_input_named_attribute.cc
index 122c7b352c7..da09d3650e3 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_named_attribute.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_named_attribute.cc
@@ -88,7 +88,7 @@ static void node_geo_exec(GeoNodeExecParams params)
return;
}
- params.used_named_attribute(name, eNamedAttrUsage::Read);
+ params.used_named_attribute(name, NamedAttributeUsage::Read);
switch (data_type) {
case CD_PROP_FLOAT:
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_shortest_edge_paths.cc b/source/blender/nodes/geometry/nodes/node_geo_input_shortest_edge_paths.cc
index ca6406d2810..a54daabde3b 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_shortest_edge_paths.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_shortest_edge_paths.cc
@@ -2,14 +2,12 @@
#include <queue>
-#include "BKE_curves.hh"
-
#include "BLI_map.hh"
#include "BLI_math_vec_types.hh"
#include "BLI_set.hh"
+#include "BLI_task.hh"
-#include "DNA_mesh_types.h"
-#include "DNA_meshdata_types.h"
+#include "BKE_mesh.h"
#include "node_geometry_util.hh"
@@ -28,10 +26,10 @@ typedef std::pair<float, int> VertPriority;
struct EdgeVertMap {
Array<Vector<int>> edges_by_vertex_map;
- EdgeVertMap(const Mesh *mesh)
+ EdgeVertMap(const Mesh &mesh)
{
- const Span<MEdge> edges{mesh->medge, mesh->totedge};
- edges_by_vertex_map.reinitialize(mesh->totvert);
+ const Span<MEdge> edges = mesh.edges();
+ edges_by_vertex_map.reinitialize(mesh.totvert);
for (const int edge_i : edges.index_range()) {
const MEdge &edge = edges[edge_i];
edges_by_vertex_map[edge.v1].append(edge_i);
@@ -40,16 +38,15 @@ struct EdgeVertMap {
}
};
-static void shortest_paths(const Mesh *mesh,
+static void shortest_paths(const Mesh &mesh,
EdgeVertMap &maps,
const IndexMask end_selection,
const VArray<float> &input_cost,
MutableSpan<int> r_next_index,
MutableSpan<float> r_cost)
{
- const Span<MVert> verts{mesh->mvert, mesh->totvert};
- const Span<MEdge> edges{mesh->medge, mesh->totedge};
- Array<bool> visited(mesh->totvert, false);
+ const Span<MEdge> edges = mesh.edges();
+ Array<bool> visited(mesh.totvert, false);
std::priority_queue<VertPriority, std::vector<VertPriority>, std::greater<VertPriority>> queue;
@@ -84,46 +81,38 @@ static void shortest_paths(const Mesh *mesh,
}
}
-class ShortestEdgePathsNextVertFieldInput final : public GeometryFieldInput {
+class ShortestEdgePathsNextVertFieldInput final : public bke::MeshFieldInput {
private:
Field<bool> end_selection_;
Field<float> cost_;
public:
ShortestEdgePathsNextVertFieldInput(Field<bool> end_selection, Field<float> cost)
- : GeometryFieldInput(CPPType::get<int>(), "Shortest Edge Paths Next Vertex Field"),
+ : bke::MeshFieldInput(CPPType::get<int>(), "Shortest Edge Paths Next Vertex Field"),
end_selection_(end_selection),
cost_(cost)
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const Mesh &mesh,
const eAttrDomain domain,
- [[maybe_unused]] IndexMask mask) const final
+ const IndexMask /*mask*/) const final
{
- if (component.type() != GEO_COMPONENT_TYPE_MESH) {
- return {};
- }
- const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
- const Mesh *mesh = mesh_component.get_for_read();
- if (mesh == nullptr) {
- return {};
- }
- GeometryComponentFieldContext edge_context{component, ATTR_DOMAIN_EDGE};
- fn::FieldEvaluator edge_evaluator{edge_context, mesh->totedge};
+ bke::MeshFieldContext edge_context{mesh, ATTR_DOMAIN_EDGE};
+ fn::FieldEvaluator edge_evaluator{edge_context, mesh.totedge};
edge_evaluator.add(cost_);
edge_evaluator.evaluate();
const VArray<float> input_cost = edge_evaluator.get_evaluated<float>(0);
- GeometryComponentFieldContext point_context{component, ATTR_DOMAIN_POINT};
- fn::FieldEvaluator point_evaluator{point_context, mesh->totvert};
+ bke::MeshFieldContext point_context{mesh, ATTR_DOMAIN_POINT};
+ fn::FieldEvaluator point_evaluator{point_context, mesh.totvert};
point_evaluator.add(end_selection_);
point_evaluator.evaluate();
const IndexMask end_selection = point_evaluator.get_evaluated_as_mask(0);
- Array<int> next_index(mesh->totvert, -1);
- Array<float> cost(mesh->totvert, FLT_MAX);
+ Array<int> next_index(mesh.totvert, -1);
+ Array<float> cost(mesh.totvert, FLT_MAX);
if (!end_selection.is_empty()) {
EdgeVertMap maps(mesh);
@@ -136,7 +125,7 @@ class ShortestEdgePathsNextVertFieldInput final : public GeometryFieldInput {
}
}
});
- return component.attributes()->adapt_domain<int>(
+ return mesh.attributes().adapt_domain<int>(
VArray<int>::ForContainer(std::move(next_index)), ATTR_DOMAIN_POINT, domain);
}
@@ -156,46 +145,38 @@ class ShortestEdgePathsNextVertFieldInput final : public GeometryFieldInput {
}
};
-class ShortestEdgePathsCostFieldInput final : public GeometryFieldInput {
+class ShortestEdgePathsCostFieldInput final : public bke::MeshFieldInput {
private:
Field<bool> end_selection_;
Field<float> cost_;
public:
ShortestEdgePathsCostFieldInput(Field<bool> end_selection, Field<float> cost)
- : GeometryFieldInput(CPPType::get<float>(), "Shortest Edge Paths Cost Field"),
+ : bke::MeshFieldInput(CPPType::get<float>(), "Shortest Edge Paths Cost Field"),
end_selection_(end_selection),
cost_(cost)
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const Mesh &mesh,
const eAttrDomain domain,
- [[maybe_unused]] IndexMask mask) const final
+ const IndexMask /*mask*/) const final
{
- if (component.type() != GEO_COMPONENT_TYPE_MESH) {
- return {};
- }
- const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
- const Mesh *mesh = mesh_component.get_for_read();
- if (mesh == nullptr) {
- return {};
- }
- GeometryComponentFieldContext edge_context{component, ATTR_DOMAIN_EDGE};
- fn::FieldEvaluator edge_evaluator{edge_context, mesh->totedge};
+ bke::MeshFieldContext edge_context{mesh, ATTR_DOMAIN_EDGE};
+ fn::FieldEvaluator edge_evaluator{edge_context, mesh.totedge};
edge_evaluator.add(cost_);
edge_evaluator.evaluate();
const VArray<float> input_cost = edge_evaluator.get_evaluated<float>(0);
- GeometryComponentFieldContext point_context{component, ATTR_DOMAIN_POINT};
- fn::FieldEvaluator point_evaluator{point_context, mesh->totvert};
+ bke::MeshFieldContext point_context{mesh, ATTR_DOMAIN_POINT};
+ fn::FieldEvaluator point_evaluator{point_context, mesh.totvert};
point_evaluator.add(end_selection_);
point_evaluator.evaluate();
const IndexMask end_selection = point_evaluator.get_evaluated_as_mask(0);
- Array<int> next_index(mesh->totvert, -1);
- Array<float> cost(mesh->totvert, FLT_MAX);
+ Array<int> next_index(mesh.totvert, -1);
+ Array<float> cost(mesh.totvert, FLT_MAX);
if (!end_selection.is_empty()) {
EdgeVertMap maps(mesh);
@@ -208,7 +189,7 @@ class ShortestEdgePathsCostFieldInput final : public GeometryFieldInput {
}
}
});
- return component.attributes()->adapt_domain<float>(
+ return mesh.attributes().adapt_domain<float>(
VArray<float>::ForContainer(std::move(cost)), ATTR_DOMAIN_POINT, domain);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc b/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc
index 267ba44cc00..07dc158ff48 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc
@@ -16,15 +16,9 @@ static void node_declare(NodeDeclarationBuilder &b)
* Spline Count
*/
-static VArray<int> construct_curve_point_count_gvarray(const CurveComponent &component,
+static VArray<int> construct_curve_point_count_gvarray(const bke::CurvesGeometry &curves,
const eAttrDomain domain)
{
- if (!component.has_curves()) {
- return {};
- }
- const Curves &curves_id = *component.get_for_read();
- const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
-
auto count_fn = [curves](int64_t i) { return curves.points_for_curve(i).size(); };
if (domain == ATTR_DOMAIN_CURVE) {
@@ -32,29 +26,24 @@ static VArray<int> construct_curve_point_count_gvarray(const CurveComponent &com
}
if (domain == ATTR_DOMAIN_POINT) {
VArray<int> count = VArray<int>::ForFunc(curves.curves_num(), count_fn);
- return component.attributes()->adapt_domain<int>(
- std::move(count), ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT);
+ return curves.adapt_domain<int>(std::move(count), ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT);
}
return {};
}
-class SplineCountFieldInput final : public GeometryFieldInput {
+class SplineCountFieldInput final : public bke::CurvesFieldInput {
public:
- SplineCountFieldInput() : GeometryFieldInput(CPPType::get<int>(), "Spline Point Count")
+ SplineCountFieldInput() : bke::CurvesFieldInput(CPPType::get<int>(), "Spline Point Count")
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const bke::CurvesGeometry &curves,
const eAttrDomain domain,
IndexMask UNUSED(mask)) const final
{
- if (component.type() == GEO_COMPONENT_TYPE_CURVE) {
- const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
- return construct_curve_point_count_gvarray(curve_component, domain);
- }
- return {};
+ return construct_curve_point_count_gvarray(curves, domain);
}
uint64_t hash() const override
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc b/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc
index a2aab5464aa..ea3d060f03c 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc
@@ -63,19 +63,12 @@ static Array<float3> curve_tangent_point_domain(const bke::CurvesGeometry &curve
return results;
}
-static VArray<float3> construct_curve_tangent_gvarray(const CurveComponent &component,
+static VArray<float3> construct_curve_tangent_gvarray(const bke::CurvesGeometry &curves,
const eAttrDomain domain)
{
- if (!component.has_curves()) {
- return {};
- }
-
- const Curves &curves_id = *component.get_for_read();
- const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
-
const VArray<int8_t> types = curves.curve_types();
if (curves.is_single_type(CURVE_TYPE_POLY)) {
- return component.attributes()->adapt_domain<float3>(
+ return curves.adapt_domain<float3>(
VArray<float3>::ForSpan(curves.evaluated_tangents()), ATTR_DOMAIN_POINT, domain);
}
@@ -86,29 +79,25 @@ static VArray<float3> construct_curve_tangent_gvarray(const CurveComponent &comp
}
if (domain == ATTR_DOMAIN_CURVE) {
- return component.attributes()->adapt_domain<float3>(
+ return curves.adapt_domain<float3>(
VArray<float3>::ForContainer(std::move(tangents)), ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE);
}
return nullptr;
}
-class TangentFieldInput final : public GeometryFieldInput {
+class TangentFieldInput final : public bke::CurvesFieldInput {
public:
- TangentFieldInput() : GeometryFieldInput(CPPType::get<float3>(), "Tangent node")
+ TangentFieldInput() : bke::CurvesFieldInput(CPPType::get<float3>(), "Tangent node")
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const bke::CurvesGeometry &curves,
const eAttrDomain domain,
IndexMask UNUSED(mask)) const final
{
- if (component.type() == GEO_COMPONENT_TYPE_CURVE) {
- const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
- return construct_curve_tangent_gvarray(curve_component, domain);
- }
- return {};
+ return construct_curve_tangent_gvarray(curves, domain);
}
uint64_t hash() const override
diff --git a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc
index 37f9917f39d..d54d082311f 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc
@@ -57,7 +57,7 @@ static void add_instances_from_component(
VArray<float3> rotations;
VArray<float3> scales;
- GeometryComponentFieldContext field_context{src_component, domain};
+ bke::GeometryFieldContext field_context{src_component, domain};
const Field<bool> selection_field = params.get_input<Field<bool>>("Selection");
fn::FieldEvaluator evaluator{field_context, domain_num};
evaluator.set_selection(selection_field);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc
index 5e0789e557b..ec2f1b00e6c 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc
@@ -29,10 +29,8 @@ static void convert_instances_to_points(GeometrySet &geometry_set,
{
const InstancesComponent &instances = *geometry_set.get_component_for_read<InstancesComponent>();
- GeometryComponentFieldContext field_context{instances, ATTR_DOMAIN_INSTANCE};
- const int domain_size = instances.instances_num();
-
- fn::FieldEvaluator evaluator{field_context, domain_size};
+ const bke::InstancesFieldContext context{instances};
+ fn::FieldEvaluator evaluator{context, instances.instances_num()};
evaluator.set_selection(std::move(selection_field));
evaluator.add(std::move(position_field));
evaluator.add(std::move(radius_field));
@@ -47,8 +45,7 @@ static void convert_instances_to_points(GeometrySet &geometry_set,
PointCloud *pointcloud = BKE_pointcloud_new_nomain(selection.size());
geometry_set.replace_pointcloud(pointcloud);
- bke::MutableAttributeAccessor point_attributes = bke::pointcloud_attributes_for_write(
- *pointcloud);
+ bke::MutableAttributeAccessor point_attributes = pointcloud->attributes_for_write();
bke::SpanAttributeWriter<float3> point_positions =
point_attributes.lookup_or_add_for_write_only_span<float3>("position", ATTR_DOMAIN_POINT);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_interpolate_domain.cc b/source/blender/nodes/geometry/nodes/node_geo_interpolate_domain.cc
index 93203988552..8e38ef14aba 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_interpolate_domain.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_interpolate_domain.cc
@@ -83,31 +83,33 @@ static void node_gather_link_searches(GatherLinkSearchOpParams &params)
}
}
-class InterpolateDomain final : public GeometryFieldInput {
+class InterpolateDomain final : public bke::GeometryFieldInput {
private:
GField src_field_;
eAttrDomain src_domain_;
public:
InterpolateDomain(GField field, eAttrDomain domain)
- : GeometryFieldInput(field.cpp_type(), "Interpolate Domain"),
+ : bke::GeometryFieldInput(field.cpp_type(), "Interpolate Domain"),
src_field_(std::move(field)),
src_domain_(domain)
{
}
- GVArray get_varray_for_context(const GeometryComponent &component,
- const eAttrDomain domain,
- IndexMask /* mask */) const final
+ GVArray get_varray_for_context(const bke::GeometryFieldContext &context,
+ IndexMask /*mask*/) const final
{
- const GeometryComponentFieldContext context{component, src_domain_};
- const int64_t src_domain_size = component.attribute_domain_size(src_domain_);
+ const bke::AttributeAccessor attributes = *context.attributes();
+
+ const bke::GeometryFieldContext other_domain_context{
+ context.geometry(), context.type(), src_domain_};
+ const int64_t src_domain_size = attributes.domain_size(src_domain_);
GArray values(src_field_.cpp_type(), src_domain_size);
- FieldEvaluator value_evaluator{context, src_domain_size};
+ FieldEvaluator value_evaluator{other_domain_context, src_domain_size};
value_evaluator.add_with_destination(src_field_, values.as_mutable_span());
value_evaluator.evaluate();
- return component.attributes()->adapt_domain(
- GVArray::ForGArray(std::move(values)), src_domain_, domain);
+ return attributes.adapt_domain(
+ GVArray::ForGArray(std::move(values)), src_domain_, context.domain());
}
};
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 023d7a32a61..9fdf7fe7d31 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
@@ -177,7 +177,7 @@ static void join_component_type(Span<GeometrySet> src_geometry_sets, GeometrySet
static void node_geo_exec(GeoNodeExecParams params)
{
- Vector<GeometrySet> geometry_sets = params.extract_multi_input<GeometrySet>("Geometry");
+ Vector<GeometrySet> geometry_sets = params.extract_input<Vector<GeometrySet>>("Geometry");
GeometrySet geometry_set_result;
join_component_type<MeshComponent>(geometry_sets, geometry_set_result);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc
index ca613ae009b..628688f3b47 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc
@@ -23,46 +23,56 @@ static void node_declare(NodeDeclarationBuilder &b)
static void select_mesh_by_material(const Mesh &mesh,
const Material *material,
const IndexMask mask,
- const MutableSpan<bool> r_selection)
+ MutableSpan<bool> r_selection)
{
BLI_assert(mesh.totpoly >= r_selection.size());
- Vector<int> material_indices;
+ Vector<int> slots;
for (const int i : IndexRange(mesh.totcol)) {
if (mesh.mat[i] == material) {
- material_indices.append(i);
+ slots.append(i);
}
}
+ const AttributeAccessor attributes = mesh.attributes();
+ const VArray<int> material_indices = attributes.lookup_or_default<int>(
+ "material_index", ATTR_DOMAIN_FACE, 0);
+ if (material != nullptr && material_indices.is_single() &&
+ material_indices.get_internal_single() == 0) {
+ r_selection.fill_indices(mask, false);
+ return;
+ }
+
+ const VArraySpan<int> material_indices_span(material_indices);
+
threading::parallel_for(mask.index_range(), 1024, [&](IndexRange range) {
for (const int i : range) {
const int face_index = mask[i];
- r_selection[i] = material_indices.contains(mesh.mpoly[face_index].mat_nr);
+ r_selection[i] = slots.contains(material_indices_span[face_index]);
}
});
}
-class MaterialSelectionFieldInput final : public GeometryFieldInput {
+class MaterialSelectionFieldInput final : public bke::GeometryFieldInput {
Material *material_;
public:
MaterialSelectionFieldInput(Material *material)
- : GeometryFieldInput(CPPType::get<bool>(), "Material Selection node"), material_(material)
+ : bke::GeometryFieldInput(CPPType::get<bool>(), "Material Selection node"),
+ material_(material)
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
- const eAttrDomain domain,
- IndexMask mask) const final
+ GVArray get_varray_for_context(const bke::GeometryFieldContext &context,
+ const IndexMask mask) const final
{
- if (component.type() != GEO_COMPONENT_TYPE_MESH) {
+ if (context.type() != GEO_COMPONENT_TYPE_MESH) {
return {};
}
- const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
- const Mesh *mesh = mesh_component.get_for_read();
+ const Mesh *mesh = context.mesh();
if (mesh == nullptr) {
return {};
}
-
+ const eAttrDomain domain = context.domain();
if (domain == ATTR_DOMAIN_FACE) {
Array<bool> selection(mask.min_array_size());
select_mesh_by_material(*mesh, material_, mask, selection);
@@ -71,7 +81,7 @@ class MaterialSelectionFieldInput final : public GeometryFieldInput {
Array<bool> selection(mesh->totpoly);
select_mesh_by_material(*mesh, material_, IndexMask(mesh->totpoly), selection);
- return mesh_component.attributes()->adapt_domain<bool>(
+ return mesh->attributes().adapt_domain<bool>(
VArray<bool>::ForContainer(std::move(selection)), ATTR_DOMAIN_FACE, domain);
return nullptr;
diff --git a/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc b/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc
index a4fb79bef7a..f64f997810e 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc
@@ -1,5 +1,8 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include "DNA_mesh_types.h"
+#include "DNA_pointcloud_types.h"
+
#include "GEO_mesh_merge_by_distance.hh"
#include "GEO_point_merge_by_distance.hh"
@@ -35,13 +38,12 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node)
node->storage = data;
}
-static PointCloud *pointcloud_merge_by_distance(const PointCloudComponent &src_points,
+static PointCloud *pointcloud_merge_by_distance(const PointCloud &src_points,
const float merge_distance,
const Field<bool> &selection_field)
{
- const int src_num = src_points.attribute_domain_size(ATTR_DOMAIN_POINT);
- GeometryComponentFieldContext context{src_points, ATTR_DOMAIN_POINT};
- FieldEvaluator evaluator{context, src_num};
+ bke::PointCloudFieldContext context{src_points};
+ FieldEvaluator evaluator{context, src_points.totpoint};
evaluator.add(selection_field);
evaluator.evaluate();
@@ -50,31 +52,28 @@ static PointCloud *pointcloud_merge_by_distance(const PointCloudComponent &src_p
return nullptr;
}
- return geometry::point_merge_by_distance(*src_points.get_for_read(), merge_distance, selection);
+ return geometry::point_merge_by_distance(src_points, merge_distance, selection);
}
-static std::optional<Mesh *> mesh_merge_by_distance_connected(const MeshComponent &mesh_component,
+static std::optional<Mesh *> mesh_merge_by_distance_connected(const Mesh &mesh,
const float merge_distance,
const Field<bool> &selection_field)
{
- const int src_num = mesh_component.attribute_domain_size(ATTR_DOMAIN_POINT);
- Array<bool> selection(src_num);
- GeometryComponentFieldContext context{mesh_component, ATTR_DOMAIN_POINT};
- FieldEvaluator evaluator{context, src_num};
+ Array<bool> selection(mesh.totvert);
+ bke::MeshFieldContext context{mesh, ATTR_DOMAIN_POINT};
+ FieldEvaluator evaluator{context, mesh.totvert};
evaluator.add_with_destination(selection_field, selection.as_mutable_span());
evaluator.evaluate();
- const Mesh &mesh = *mesh_component.get_for_read();
return geometry::mesh_merge_by_distance_connected(mesh, selection, merge_distance, false);
}
-static std::optional<Mesh *> mesh_merge_by_distance_all(const MeshComponent &mesh_component,
+static std::optional<Mesh *> mesh_merge_by_distance_all(const Mesh &mesh,
const float merge_distance,
const Field<bool> &selection_field)
{
- const int src_num = mesh_component.attribute_domain_size(ATTR_DOMAIN_POINT);
- GeometryComponentFieldContext context{mesh_component, ATTR_DOMAIN_POINT};
- FieldEvaluator evaluator{context, src_num};
+ bke::MeshFieldContext context{mesh, ATTR_DOMAIN_POINT};
+ FieldEvaluator evaluator{context, mesh.totvert};
evaluator.add(selection_field);
evaluator.evaluate();
@@ -83,7 +82,6 @@ static std::optional<Mesh *> mesh_merge_by_distance_all(const MeshComponent &mes
return std::nullopt;
}
- const Mesh &mesh = *mesh_component.get_for_read();
return geometry::mesh_merge_by_distance_all(mesh, selection, merge_distance);
}
@@ -98,22 +96,20 @@ static void node_geo_exec(GeoNodeExecParams params)
const float merge_distance = params.extract_input<float>("Distance");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
- if (geometry_set.has_pointcloud()) {
- PointCloud *result = pointcloud_merge_by_distance(
- *geometry_set.get_component_for_read<PointCloudComponent>(), merge_distance, selection);
+ if (const PointCloud *pointcloud = geometry_set.get_pointcloud_for_read()) {
+ PointCloud *result = pointcloud_merge_by_distance(*pointcloud, merge_distance, selection);
if (result) {
geometry_set.replace_pointcloud(result);
}
}
- if (geometry_set.has_mesh()) {
- const MeshComponent &component = *geometry_set.get_component_for_read<MeshComponent>();
+ if (const Mesh *mesh = geometry_set.get_mesh_for_read()) {
std::optional<Mesh *> result;
switch (mode) {
case GEO_NODE_MERGE_BY_DISTANCE_MODE_ALL:
- result = mesh_merge_by_distance_all(component, merge_distance, selection);
+ result = mesh_merge_by_distance_all(*mesh, merge_distance, selection);
break;
case GEO_NODE_MERGE_BY_DISTANCE_MODE_CONNECTED:
- result = mesh_merge_by_distance_connected(component, merge_distance, selection);
+ result = mesh_merge_by_distance_connected(*mesh, merge_distance, selection);
break;
default:
BLI_assert_unreachable();
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_face_set_boundaries.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_face_set_boundaries.cc
new file mode 100644
index 00000000000..88cccfb2f94
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_face_set_boundaries.cc
@@ -0,0 +1,89 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_mesh.h"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_mesh_face_set_boundaries_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Int>(N_("Face Set"))
+ .default_value(0)
+ .hide_value()
+ .supports_field()
+ .description(N_("An identifier for the group of each face. All contiguous faces with the "
+ "same value are in the same region"));
+ b.add_output<decl::Bool>(N_("Boundary Edges"))
+ .field_source()
+ .description(N_("The edges that lie on the boundaries between the different face sets"));
+}
+
+class BoundaryFieldInput final : public bke::MeshFieldInput {
+ private:
+ const Field<int> face_set;
+
+ public:
+ BoundaryFieldInput(const Field<int> face_set)
+ : bke::MeshFieldInput(CPPType::get<bool>(), "Boundary Field"), face_set(face_set)
+ {
+ category_ = Category::Generated;
+ }
+
+ GVArray get_varray_for_context(const Mesh &mesh,
+ const eAttrDomain domain,
+ const IndexMask /*mask*/) const final
+ {
+ const bke::MeshFieldContext face_context{mesh, ATTR_DOMAIN_FACE};
+ FieldEvaluator face_evaluator{face_context, mesh.totpoly};
+ face_evaluator.add(face_set);
+ face_evaluator.evaluate();
+ const VArray<int> face_set = face_evaluator.get_evaluated<int>(0);
+
+ Array<bool> boundary(mesh.totedge, false);
+ Array<bool> edge_visited(mesh.totedge, false);
+ Array<int> edge_face_set(mesh.totedge, 0);
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
+ for (const int i : polys.index_range()) {
+ const MPoly &poly = polys[i];
+ for (const MLoop &loop : loops.slice(poly.loopstart, poly.totloop)) {
+ const int edge = loop.e;
+ if (edge_visited[edge]) {
+ if (edge_face_set[edge] != face_set[i]) {
+ /* This edge is connected to two faces on different face sets. */
+ boundary[edge] = true;
+ }
+ }
+ edge_visited[edge] = true;
+ edge_face_set[edge] = face_set[i];
+ }
+ }
+ return mesh.attributes().adapt_domain<bool>(
+ VArray<bool>::ForContainer(std::move(boundary)), ATTR_DOMAIN_EDGE, domain);
+ }
+};
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ const Field<int> face_set_field = params.extract_input<Field<int>>("Face Set");
+ Field<bool> face_set_boundaries{std::make_shared<BoundaryFieldInput>(face_set_field)};
+ params.set_output("Boundary Edges", std::move(face_set_boundaries));
+}
+
+} // namespace blender::nodes::node_geo_mesh_face_set_boundaries_cc
+
+void register_node_type_geo_mesh_face_set_boundaries()
+{
+ namespace file_ns = blender::nodes::node_geo_mesh_face_set_boundaries_cc;
+
+ static bNodeType ntype;
+ geo_node_type_base(
+ &ntype, GEO_NODE_MESH_FACE_SET_BOUNDARIES, "Face Set Boundaries", 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_mesh_primitive_circle.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc
index 9e85547315c..801b3c78060 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc
@@ -109,10 +109,10 @@ static Mesh *create_circle_mesh(const float radius,
circle_corner_total(fill_type, verts_num),
circle_face_total(fill_type, verts_num));
BKE_id_material_eval_ensure_default_slot(&mesh->id);
- MutableSpan<MVert> verts{mesh->mvert, mesh->totvert};
- MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop};
- MutableSpan<MEdge> edges{mesh->medge, mesh->totedge};
- MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly};
+ MutableSpan<MVert> verts = mesh->verts_for_write();
+ MutableSpan<MEdge> edges = mesh->edges_for_write();
+ MutableSpan<MPoly> polys = mesh->polys_for_write();
+ MutableSpan<MLoop> loops = mesh->loops_for_write();
/* Assign vertex coordinates. */
const float angle_delta = 2.0f * (M_PI / static_cast<float>(verts_num));
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc
index cb79ef93de9..1f9ad9f6ea2 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc
@@ -255,7 +255,7 @@ int ConeConfig::calculate_total_corners()
return corner_total;
}
-static void calculate_cone_vertices(const MutableSpan<MVert> &verts, const ConeConfig &config)
+static void calculate_cone_verts(const MutableSpan<MVert> &verts, const ConeConfig &config)
{
Array<float2> circle(config.circle_segments);
const float angle_delta = 2.0f * (M_PI / static_cast<float>(config.circle_segments));
@@ -480,12 +480,12 @@ static void calculate_selection_outputs(Mesh *mesh,
const ConeConfig &config,
ConeAttributeOutputs &attribute_outputs)
{
- MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*mesh);
+ MutableAttributeAccessor attributes = mesh->attributes_for_write();
/* Populate "Top" selection output. */
if (attribute_outputs.top_id) {
const bool face = !config.top_is_point && config.fill_type != GEO_NODE_MESH_CIRCLE_FILL_NONE;
- SpanAttributeWriter<bool> selection = attributes.lookup_or_add_for_write_only_span<bool>(
+ SpanAttributeWriter<bool> selection = attributes.lookup_or_add_for_write_span<bool>(
attribute_outputs.top_id.get(), face ? ATTR_DOMAIN_FACE : ATTR_DOMAIN_POINT);
if (config.top_is_point) {
@@ -501,7 +501,7 @@ static void calculate_selection_outputs(Mesh *mesh,
if (attribute_outputs.bottom_id) {
const bool face = !config.bottom_is_point &&
config.fill_type != GEO_NODE_MESH_CIRCLE_FILL_NONE;
- SpanAttributeWriter<bool> selection = attributes.lookup_or_add_for_write_only_span<bool>(
+ SpanAttributeWriter<bool> selection = attributes.lookup_or_add_for_write_span<bool>(
attribute_outputs.bottom_id.get(), face ? ATTR_DOMAIN_FACE : ATTR_DOMAIN_POINT);
if (config.bottom_is_point) {
@@ -518,7 +518,7 @@ static void calculate_selection_outputs(Mesh *mesh,
/* Populate "Side" selection output. */
if (attribute_outputs.side_id) {
- SpanAttributeWriter<bool> selection = attributes.lookup_or_add_for_write_only_span<bool>(
+ SpanAttributeWriter<bool> selection = attributes.lookup_or_add_for_write_span<bool>(
attribute_outputs.side_id.get(), ATTR_DOMAIN_FACE);
selection.span.slice(config.side_faces_start, config.side_faces_len).fill(true);
@@ -536,7 +536,7 @@ static void calculate_selection_outputs(Mesh *mesh,
*/
static void calculate_cone_uvs(Mesh *mesh, const ConeConfig &config)
{
- MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*mesh);
+ MutableAttributeAccessor attributes = mesh->attributes_for_write();
SpanAttributeWriter<float2> uv_attribute = attributes.lookup_or_add_for_write_only_span<float2>(
"uv_map", ATTR_DOMAIN_CORNER);
@@ -657,7 +657,7 @@ static Mesh *create_vertex_mesh()
{
/* Returns a mesh with a single vertex at the origin. */
Mesh *mesh = BKE_mesh_new_nomain(1, 0, 0, 0, 0);
- copy_v3_fl3(mesh->mvert[0].co, 0.0f, 0.0f, 0.0f);
+ copy_v3_fl3(mesh->verts_for_write().first().co, 0.0f, 0.0f, 0.0f);
return mesh;
}
@@ -689,12 +689,12 @@ Mesh *create_cylinder_or_cone_mesh(const float radius_top,
config.tot_verts, config.tot_edges, 0, config.tot_corners, config.tot_faces);
BKE_id_material_eval_ensure_default_slot(&mesh->id);
- MutableSpan<MVert> verts{mesh->mvert, mesh->totvert};
- MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop};
- MutableSpan<MEdge> edges{mesh->medge, mesh->totedge};
- MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly};
+ MutableSpan<MVert> verts = mesh->verts_for_write();
+ MutableSpan<MEdge> edges = mesh->edges_for_write();
+ MutableSpan<MPoly> polys = mesh->polys_for_write();
+ MutableSpan<MLoop> loops = mesh->loops_for_write();
- calculate_cone_vertices(verts, config);
+ calculate_cone_verts(verts, config);
calculate_cone_edges(edges, config);
calculate_cone_faces(loops, polys, config);
calculate_cone_uvs(mesh, config);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc
index 9baf0b3171e..6f0b8283b72 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc
@@ -18,7 +18,7 @@ namespace blender::nodes {
static void calculate_uvs(
Mesh *mesh, Span<MVert> verts, Span<MLoop> loops, const float size_x, const float size_y)
{
- MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*mesh);
+ MutableAttributeAccessor attributes = mesh->attributes_for_write();
SpanAttributeWriter<float2> uv_attribute = attributes.lookup_or_add_for_write_only_span<float2>(
"uv_map", ATTR_DOMAIN_CORNER);
@@ -49,10 +49,10 @@ Mesh *create_grid_mesh(const int verts_x,
0,
edges_x * edges_y * 4,
edges_x * edges_y);
- MutableSpan<MVert> verts{mesh->mvert, mesh->totvert};
- MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop};
- MutableSpan<MEdge> edges{mesh->medge, mesh->totedge};
- MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly};
+ MutableSpan<MVert> verts = mesh->verts_for_write();
+ MutableSpan<MEdge> edges = mesh->edges_for_write();
+ MutableSpan<MPoly> polys = mesh->polys_for_write();
+ MutableSpan<MLoop> loops = mesh->loops_for_write();
{
const float dx = edges_x == 0 ? 0.0f : size_x / edges_x;
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc
index a5edc6c4b3f..4fd6399f4eb 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc
@@ -179,8 +179,8 @@ Mesh *create_line_mesh(const float3 start, const float3 delta, const int count)
Mesh *mesh = BKE_mesh_new_nomain(count, count - 1, 0, 0, 0);
BKE_id_material_eval_ensure_default_slot(&mesh->id);
- MutableSpan<MVert> verts{mesh->mvert, mesh->totvert};
- MutableSpan<MEdge> edges{mesh->medge, mesh->totedge};
+ MutableSpan<MVert> verts = mesh->verts_for_write();
+ MutableSpan<MEdge> edges = mesh->edges_for_write();
threading::parallel_invoke(
1024 < count,
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 85facf1e758..d39e72b7f0a 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc
@@ -254,7 +254,7 @@ BLI_NOINLINE static void calculate_sphere_corners(MutableSpan<MLoop> loops,
BLI_NOINLINE static void calculate_sphere_uvs(Mesh *mesh, const float segments, const float rings)
{
- MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*mesh);
+ MutableAttributeAccessor attributes = mesh->attributes_for_write();
SpanAttributeWriter<float2> uv_attribute = attributes.lookup_or_add_for_write_only_span<float2>(
"uv_map", ATTR_DOMAIN_CORNER);
@@ -301,10 +301,10 @@ static Mesh *create_uv_sphere_mesh(const float radius, const int segments, const
sphere_corner_total(segments, rings),
sphere_face_total(segments, rings));
BKE_id_material_eval_ensure_default_slot(&mesh->id);
- MutableSpan<MVert> verts{mesh->mvert, mesh->totvert};
- MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop};
- MutableSpan<MEdge> edges{mesh->medge, mesh->totedge};
- MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly};
+ MutableSpan<MVert> verts = mesh->verts_for_write();
+ MutableSpan<MEdge> edges = mesh->edges_for_write();
+ MutableSpan<MPoly> polys = mesh->polys_for_write();
+ MutableSpan<MLoop> loops = mesh->loops_for_write();
threading::parallel_invoke(
1024 < segments * rings,
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc
index 40169def51e..4d08fa40a29 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc
@@ -1,5 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include "DNA_mesh_types.h"
+
#include "GEO_mesh_to_curve.hh"
#include "node_geometry_util.hh"
@@ -24,9 +26,8 @@ static void node_geo_exec(GeoNodeExecParams params)
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)};
+ bke::MeshFieldContext context{*mesh, ATTR_DOMAIN_EDGE};
+ fn::FieldEvaluator evaluator{context, mesh->totedge};
evaluator.add(params.get_input<Field<bool>>("Selection"));
evaluator.evaluate();
const IndexMask selection = evaluator.get_evaluated_as_mask(0);
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 d3d1312be6d..ce06ccbda75 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
@@ -1,7 +1,9 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include "BLI_array_utils.hh"
#include "BLI_task.hh"
+#include "DNA_mesh_types.h"
#include "DNA_pointcloud_types.h"
#include "BKE_attribute_math.hh"
@@ -43,35 +45,24 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node)
node->storage = data;
}
-static void materialize_compressed_to_uninitialized_threaded(const GVArray &src,
- const IndexMask mask,
- GMutableSpan dst)
-{
- BLI_assert(src.type() == dst.type());
- BLI_assert(mask.size() == dst.size());
- threading::parallel_for(mask.index_range(), 4096, [&](IndexRange range) {
- src.materialize_compressed_to_uninitialized(mask.slice(range), dst.slice(range).data());
- });
-}
-
static void geometry_set_mesh_to_points(GeometrySet &geometry_set,
Field<float3> &position_field,
Field<float> &radius_field,
Field<bool> &selection_field,
const eAttrDomain domain)
{
- const MeshComponent *mesh_component = geometry_set.get_component_for_read<MeshComponent>();
- if (mesh_component == nullptr) {
+ const Mesh *mesh = geometry_set.get_mesh_for_read();
+ if (mesh == nullptr) {
geometry_set.remove_geometry_during_modify();
return;
}
- GeometryComponentFieldContext field_context{*mesh_component, domain};
- const int domain_num = mesh_component->attribute_domain_size(domain);
- if (domain_num == 0) {
+ const int domain_size = mesh->attributes().domain_size(domain);
+ if (domain_size == 0) {
geometry_set.remove_geometry_during_modify();
return;
}
- fn::FieldEvaluator evaluator{field_context, domain_num};
+ bke::MeshFieldContext field_context{*mesh, domain};
+ fn::FieldEvaluator evaluator{field_context, domain_size};
evaluator.set_selection(selection_field);
/* Evaluating directly into the point cloud doesn't work because we are not using the full
* "min_array_size" array but compressing the selected elements into the final array with no
@@ -83,19 +74,16 @@ static void geometry_set_mesh_to_points(GeometrySet &geometry_set,
PointCloud *pointcloud = BKE_pointcloud_new_nomain(selection.size());
geometry_set.replace_pointcloud(pointcloud);
- MutableAttributeAccessor pointcloud_attributes = bke::pointcloud_attributes_for_write(
- *pointcloud);
+ MutableAttributeAccessor dst_attributes = pointcloud->attributes_for_write();
- GSpanAttributeWriter position = pointcloud_attributes.lookup_or_add_for_write_only_span(
+ GSpanAttributeWriter position = dst_attributes.lookup_or_add_for_write_only_span(
"position", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3);
- materialize_compressed_to_uninitialized_threaded(
- evaluator.get_evaluated(0), selection, position.span);
+ array_utils::gather(evaluator.get_evaluated(0), selection, position.span);
position.finish();
- GSpanAttributeWriter radius = pointcloud_attributes.lookup_or_add_for_write_only_span(
+ GSpanAttributeWriter radius = dst_attributes.lookup_or_add_for_write_only_span(
"radius", ATTR_DOMAIN_POINT, CD_PROP_FLOAT);
- materialize_compressed_to_uninitialized_threaded(
- evaluator.get_evaluated(1), selection, radius.span);
+ array_utils::gather(evaluator.get_evaluated(1), selection, radius.span);
radius.finish();
Map<AttributeIDRef, AttributeKind> attributes;
@@ -103,14 +91,16 @@ static void geometry_set_mesh_to_points(GeometrySet &geometry_set,
{GEO_COMPONENT_TYPE_MESH}, GEO_COMPONENT_TYPE_POINT_CLOUD, false, attributes);
attributes.remove("position");
+ const AttributeAccessor src_attributes = mesh->attributes();
+
for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
const AttributeIDRef attribute_id = entry.key;
const eCustomDataType data_type = entry.value.data_type;
- GVArray src = mesh_component->attributes()->lookup_or_default(attribute_id, domain, data_type);
- GSpanAttributeWriter dst = pointcloud_attributes.lookup_or_add_for_write_only_span(
+ GVArray src = src_attributes.lookup_or_default(attribute_id, domain, data_type);
+ GSpanAttributeWriter dst = dst_attributes.lookup_or_add_for_write_only_span(
attribute_id, ATTR_DOMAIN_POINT, data_type);
if (dst && src) {
- materialize_compressed_to_uninitialized_threaded(src, selection, dst.span);
+ array_utils::gather(src, selection, dst.span);
dst.finish();
}
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_volume.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_volume.cc
index 92814a8bc5e..f6fa5c99013 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_volume.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_volume.cc
@@ -149,7 +149,6 @@ static void node_geo_exec(GeoNodeExecParams params)
{
#ifdef WITH_OPENVDB
GeometrySet geometry_set(params.extract_input<GeometrySet>("Mesh"));
-
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
if (geometry_set.has_mesh()) {
Volume *volume = create_volume_from_mesh(*geometry_set.get_mesh_for_read(), params);
@@ -159,9 +158,9 @@ static void node_geo_exec(GeoNodeExecParams params)
});
params.set_output("Volume", std::move(geometry_set));
#else
+ params.set_default_remaining_outputs();
params.error_message_add(NodeWarningType::Error,
TIP_("Disabled, Blender was compiled without OpenVDB"));
- params.set_default_remaining_outputs();
return;
#endif
}
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 0b2159364f1..691988249df 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_object_info.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_object_info.cc
@@ -80,7 +80,7 @@ static void node_geo_exec(GeoNodeExecParams params)
else {
geometry_set = bke::object_get_evaluated_geometry_set(*object);
if (transform_space_relative) {
- transform_geometry_set(geometry_set, transform, *params.depsgraph());
+ transform_geometry_set(params, geometry_set, transform, *params.depsgraph());
}
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_points.cc b/source/blender/nodes/geometry/nodes/node_geo_points.cc
index dd32e6714f4..4a294076834 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_points.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_points.cc
@@ -1,6 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BKE_pointcloud.h"
+#include "DNA_pointcloud_types.h"
#include "BLI_task.hh"
@@ -69,10 +70,8 @@ static void node_geo_exec(GeoNodeExecParams params)
Field<float3> position_field = params.extract_input<Field<float3>>("Position");
Field<float> radius_field = params.extract_input<Field<float>>("Radius");
- PointCloud *new_point_cloud = BKE_pointcloud_new_nomain(count);
- GeometrySet geometry_set = GeometrySet::create_with_pointcloud(new_point_cloud);
- PointCloudComponent &points = geometry_set.get_component_for_write<PointCloudComponent>();
- MutableAttributeAccessor attributes = *points.attributes_for_write();
+ PointCloud *points = BKE_pointcloud_new_nomain(count);
+ MutableAttributeAccessor attributes = points->attributes_for_write();
AttributeWriter<float3> output_position = attributes.lookup_or_add_for_write<float3>(
"position", ATTR_DOMAIN_POINT);
AttributeWriter<float> output_radii = attributes.lookup_or_add_for_write<float>(
@@ -86,7 +85,7 @@ static void node_geo_exec(GeoNodeExecParams params)
output_position.finish();
output_radii.finish();
- params.set_output("Geometry", std::move(geometry_set));
+ params.set_output("Geometry", GeometrySet::create_with_pointcloud(points));
}
} // namespace blender::nodes::node_geo_points_cc
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 ed7ef9b7c71..4ac3bf712f7 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc
@@ -2,6 +2,8 @@
#include "BLI_task.hh"
+#include "DNA_pointcloud_types.h"
+
#include "BKE_attribute_math.hh"
#include "BKE_mesh.h"
@@ -22,21 +24,18 @@ static void node_declare(NodeDeclarationBuilder &b)
static void geometry_set_points_to_vertices(GeometrySet &geometry_set,
Field<bool> &selection_field)
{
- const PointCloudComponent *point_component =
- geometry_set.get_component_for_read<PointCloudComponent>();
- if (point_component == nullptr) {
+ const PointCloud *points = geometry_set.get_pointcloud_for_read();
+ if (points == nullptr) {
geometry_set.remove_geometry_during_modify();
return;
}
-
- GeometryComponentFieldContext field_context{*point_component, ATTR_DOMAIN_POINT};
- const int domain_num = point_component->attribute_domain_size(ATTR_DOMAIN_POINT);
- if (domain_num == 0) {
+ if (points->totpoint == 0) {
geometry_set.remove_geometry_during_modify();
return;
}
- fn::FieldEvaluator selection_evaluator{field_context, domain_num};
+ bke::PointCloudFieldContext field_context{*points};
+ fn::FieldEvaluator selection_evaluator{field_context, points->totpoint};
selection_evaluator.add(selection_field);
selection_evaluator.evaluate();
const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0);
@@ -47,16 +46,16 @@ static void geometry_set_points_to_vertices(GeometrySet &geometry_set,
Mesh *mesh = BKE_mesh_new_nomain(selection.size(), 0, 0, 0, 0);
geometry_set.replace_mesh(mesh);
- MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
+
+ const AttributeAccessor src_attributes = points->attributes();
+ MutableAttributeAccessor dst_attributes = mesh->attributes_for_write();
for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
const AttributeIDRef attribute_id = entry.key;
const eCustomDataType data_type = entry.value.data_type;
- GVArray src = point_component->attributes()->lookup_or_default(
+ GVArray src = src_attributes.lookup_or_default(attribute_id, ATTR_DOMAIN_POINT, data_type);
+ GSpanAttributeWriter dst = dst_attributes.lookup_or_add_for_write_only_span(
attribute_id, ATTR_DOMAIN_POINT, data_type);
- GSpanAttributeWriter dst =
- mesh_component.attributes_for_write()->lookup_or_add_for_write_only_span(
- attribute_id, ATTR_DOMAIN_POINT, data_type);
if (dst && src) {
src.materialize_compressed_to_uninitialized(selection, dst.span.data());
dst.finish();
diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc
index 4a3048e5f4a..0990eebb903 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc
@@ -170,7 +170,7 @@ static void gather_point_data_from_component(GeoNodeExecParams &params,
"position", ATTR_DOMAIN_POINT, {0, 0, 0});
Field<float> radius_field = params.get_input<Field<float>>("Radius");
- GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
+ bke::GeometryFieldContext field_context{component, ATTR_DOMAIN_POINT};
const int domain_num = component.attribute_domain_size(ATTR_DOMAIN_POINT);
r_positions.resize(r_positions.size() + domain_num);
@@ -231,17 +231,16 @@ static void initialize_volume_component_from_points(GeoNodeExecParams &params,
static void node_geo_exec(GeoNodeExecParams params)
{
- GeometrySet geometry_set = params.extract_input<GeometrySet>("Points");
-
#ifdef WITH_OPENVDB
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Points");
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.set_default_remaining_outputs();
params.error_message_add(NodeWarningType::Error,
TIP_("Disabled, Blender was compiled without OpenVDB"));
- params.set_default_remaining_outputs();
#endif
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc
index f81748da587..f657b128c51 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc
@@ -208,7 +208,7 @@ class RaycastFunction : public fn::MultiFunction {
GeometryNodeRaycastMapMode mapping_;
/** The field for data evaluated on the target geometry. */
- std::optional<GeometryComponentFieldContext> target_context_;
+ std::optional<bke::MeshFieldContext> target_context_;
std::unique_ptr<FieldEvaluator> target_evaluator_;
const GVArray *target_data_ = nullptr;
@@ -310,9 +310,9 @@ class RaycastFunction : public fn::MultiFunction {
if (!src_field) {
return;
}
- const MeshComponent &mesh_component = *target_.get_component_for_read<MeshComponent>();
- target_context_.emplace(GeometryComponentFieldContext{mesh_component, domain_});
- const int domain_size = mesh_component.attribute_domain_size(domain_);
+ const Mesh &mesh = *target_.get_mesh_for_read();
+ target_context_.emplace(bke::MeshFieldContext{mesh, domain_});
+ const int domain_size = mesh.attributes().domain_size(domain_);
target_evaluator_ = std::make_unique<FieldEvaluator>(*target_context_, domain_size);
target_evaluator_->add(std::move(src_field));
target_evaluator_->evaluate();
diff --git a/source/blender/nodes/geometry/nodes/node_geo_remove_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_remove_attribute.cc
index ee279ba58f9..1b398f63691 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_remove_attribute.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_remove_attribute.cc
@@ -55,7 +55,7 @@ static void node_geo_exec(GeoNodeExecParams params)
});
if (attribute_exists && !cannot_delete) {
- params.used_named_attribute(name, eNamedAttrUsage::Remove);
+ params.used_named_attribute(name, NamedAttributeUsage::Remove);
}
if (!attribute_exists) {
diff --git a/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc
index d414bb1fa1d..4ed94e67e74 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc
@@ -18,10 +18,8 @@ static void node_declare(NodeDeclarationBuilder &b)
static void rotate_instances(GeoNodeExecParams &params, InstancesComponent &instances_component)
{
- GeometryComponentFieldContext field_context{instances_component, ATTR_DOMAIN_INSTANCE};
- const int domain_num = instances_component.instances_num();
-
- fn::FieldEvaluator evaluator{field_context, domain_num};
+ const bke::InstancesFieldContext context{instances_component};
+ fn::FieldEvaluator evaluator{context, instances_component.instances_num()};
evaluator.set_selection(params.extract_input<Field<bool>>("Selection"));
evaluator.add(params.extract_input<Field<float3>>("Rotation"));
evaluator.add(params.extract_input<Field<float3>>("Pivot Point"));
diff --git a/source/blender/nodes/geometry/nodes/node_geo_sample_index.cc b/source/blender/nodes/geometry/nodes/node_geo_sample_index.cc
new file mode 100644
index 00000000000..bfaf9b70f13
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_sample_index.cc
@@ -0,0 +1,337 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "BLI_task.hh"
+
+#include "BKE_attribute_math.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_sample_index_cc {
+
+NODE_STORAGE_FUNCS(NodeGeometrySampleIndex);
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>(N_("Geometry"))
+ .supported_type({GEO_COMPONENT_TYPE_MESH,
+ GEO_COMPONENT_TYPE_POINT_CLOUD,
+ GEO_COMPONENT_TYPE_CURVE,
+ GEO_COMPONENT_TYPE_INSTANCES});
+
+ b.add_input<decl::Float>(N_("Value"), "Value_Float").hide_value().supports_field();
+ b.add_input<decl::Int>(N_("Value"), "Value_Int").hide_value().supports_field();
+ b.add_input<decl::Vector>(N_("Value"), "Value_Vector").hide_value().supports_field();
+ b.add_input<decl::Color>(N_("Value"), "Value_Color").hide_value().supports_field();
+ b.add_input<decl::Bool>(N_("Value"), "Value_Bool").hide_value().supports_field();
+ b.add_input<decl::Int>(N_("Index"))
+ .supports_field()
+ .description(N_("Which element to retrieve a value from on the geometry"));
+
+ b.add_output<decl::Float>(N_("Value"), "Value_Float").dependent_field({6});
+ b.add_output<decl::Int>(N_("Value"), "Value_Int").dependent_field({6});
+ b.add_output<decl::Vector>(N_("Value"), "Value_Vector").dependent_field({6});
+ b.add_output<decl::Color>(N_("Value"), "Value_Color").dependent_field({6});
+ b.add_output<decl::Bool>(N_("Value"), "Value_Bool").dependent_field({6});
+}
+
+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);
+ uiItemR(layout, ptr, "clamp", 0, nullptr, ICON_NONE);
+}
+
+static void node_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometrySampleIndex *data = MEM_cnew<NodeGeometrySampleIndex>(__func__);
+ data->data_type = CD_PROP_FLOAT;
+ data->domain = ATTR_DOMAIN_POINT;
+ data->clamp = 0;
+ node->storage = data;
+}
+
+static void node_update(bNodeTree *ntree, bNode *node)
+{
+ const eCustomDataType data_type = eCustomDataType(node_storage(*node).data_type);
+
+ bNodeSocket *in_socket_geometry = static_cast<bNodeSocket *>(node->inputs.first);
+ bNodeSocket *in_socket_float = in_socket_geometry->next;
+ bNodeSocket *in_socket_int32 = in_socket_float->next;
+ bNodeSocket *in_socket_vector = in_socket_int32->next;
+ bNodeSocket *in_socket_color4f = in_socket_vector->next;
+ bNodeSocket *in_socket_bool = in_socket_color4f->next;
+
+ nodeSetSocketAvailability(ntree, in_socket_vector, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(ntree, in_socket_float, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(ntree, in_socket_color4f, data_type == CD_PROP_COLOR);
+ nodeSetSocketAvailability(ntree, in_socket_bool, data_type == CD_PROP_BOOL);
+ nodeSetSocketAvailability(ntree, in_socket_int32, data_type == CD_PROP_INT32);
+
+ bNodeSocket *out_socket_float = static_cast<bNodeSocket *>(node->outputs.first);
+ bNodeSocket *out_socket_int32 = out_socket_float->next;
+ bNodeSocket *out_socket_vector = out_socket_int32->next;
+ bNodeSocket *out_socket_color4f = out_socket_vector->next;
+ bNodeSocket *out_socket_bool = out_socket_color4f->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_bool, data_type == CD_PROP_BOOL);
+ nodeSetSocketAvailability(ntree, out_socket_int32, data_type == CD_PROP_INT32);
+}
+
+static void node_gather_link_searches(GatherLinkSearchOpParams &params)
+{
+ const NodeDeclaration &declaration = *params.node_type().fixed_declaration;
+ search_link_ops_for_declarations(params, declaration.inputs().take_back(1));
+ search_link_ops_for_declarations(params, declaration.inputs().take_front(1));
+
+ const std::optional<eCustomDataType> 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_("Value"), [type](LinkSearchOpParams &params) {
+ bNode &node = params.add_node("GeometryNodeSampleIndex");
+ node_storage(node).data_type = *type;
+ params.update_and_connect_available_socket(node, "Value");
+ });
+ }
+}
+
+static bool component_is_available(const GeometrySet &geometry,
+ const GeometryComponentType type,
+ const eAttrDomain 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;
+}
+
+static const GeometryComponent *find_source_component(const GeometrySet &geometry,
+ const eAttrDomain 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;
+}
+
+template<typename T>
+void copy_with_indices(const VArray<T> &src,
+ const VArray<int> &indices,
+ const IndexMask mask,
+ MutableSpan<T> dst)
+{
+ const IndexRange src_range = src.index_range();
+ devirtualize_varray2(src, indices, [&](const auto src, const auto indices) {
+ threading::parallel_for(mask.index_range(), 4096, [&](IndexRange range) {
+ for (const int i : mask.slice(range)) {
+ const int index = indices[i];
+ if (src_range.contains(index)) {
+ dst[i] = src[index];
+ }
+ else {
+ dst[i] = {};
+ }
+ }
+ });
+ });
+}
+
+template<typename T>
+void copy_with_clamped_indices(const VArray<T> &src,
+ const VArray<int> &indices,
+ const IndexMask mask,
+ MutableSpan<T> dst)
+{
+ const int last_index = src.index_range().last();
+ devirtualize_varray2(src, indices, [&](const auto src, const auto indices) {
+ threading::parallel_for(mask.index_range(), 4096, [&](IndexRange range) {
+ for (const int i : mask.slice(range)) {
+ const int index = indices[i];
+ dst[i] = src[std::clamp(index, 0, last_index)];
+ }
+ });
+ });
+}
+
+/**
+ * The index-based transfer theoretically does not need realized data when there is only one
+ * instance geometry set in the source. A future optimization could be removing that limitation
+ * internally.
+ */
+class SampleIndexFunction : public fn::MultiFunction {
+ GeometrySet src_geometry_;
+ GField src_field_;
+ eAttrDomain domain_;
+ bool clamp_;
+
+ fn::MFSignature signature_;
+
+ std::optional<bke::GeometryFieldContext> geometry_context_;
+ std::unique_ptr<FieldEvaluator> evaluator_;
+ const GVArray *src_data_ = nullptr;
+
+ public:
+ SampleIndexFunction(GeometrySet geometry,
+ GField src_field,
+ const eAttrDomain domain,
+ const bool clamp)
+ : src_geometry_(std::move(geometry)),
+ src_field_(std::move(src_field)),
+ domain_(domain),
+ clamp_(clamp)
+ {
+ src_geometry_.ensure_owns_direct_data();
+
+ signature_ = this->create_signature();
+ this->set_signature(&signature_);
+
+ this->evaluate_field();
+ }
+
+ fn::MFSignature create_signature()
+ {
+ fn::MFSignatureBuilder signature{"Sample Index"};
+ signature.single_input<int>("Index");
+ signature.single_output("Value", src_field_.cpp_type());
+ return signature.build();
+ }
+
+ void evaluate_field()
+ {
+ const GeometryComponent *component = find_source_component(src_geometry_, domain_);
+ if (component == nullptr) {
+ return;
+ }
+ const int domain_num = component->attribute_domain_size(domain_);
+ geometry_context_.emplace(bke::GeometryFieldContext(*component, domain_));
+ evaluator_ = std::make_unique<FieldEvaluator>(*geometry_context_, domain_num);
+ evaluator_->add(src_field_);
+ evaluator_->evaluate();
+ 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, "Value");
+
+ const CPPType &type = dst.type();
+ if (src_data_ == nullptr) {
+ type.value_initialize_indices(dst.data(), mask);
+ return;
+ }
+
+ attribute_math::convert_to_static_type(type, [&](auto dummy) {
+ using T = decltype(dummy);
+ if (clamp_) {
+ copy_with_clamped_indices(src_data_->typed<T>(), indices, mask, dst.typed<T>());
+ }
+ else {
+ copy_with_indices(src_data_->typed<T>(), indices, mask, dst.typed<T>());
+ }
+ });
+ }
+};
+
+static GField get_input_attribute_field(GeoNodeExecParams &params, const eCustomDataType data_type)
+{
+ switch (data_type) {
+ case CD_PROP_FLOAT:
+ return params.extract_input<Field<float>>("Value_Float");
+ case CD_PROP_FLOAT3:
+ return params.extract_input<Field<float3>>("Value_Vector");
+ case CD_PROP_COLOR:
+ return params.extract_input<Field<ColorGeometry4f>>("Value_Color");
+ case CD_PROP_BOOL:
+ return params.extract_input<Field<bool>>("Value_Bool");
+ case CD_PROP_INT32:
+ return params.extract_input<Field<int>>("Value_Int");
+ default:
+ BLI_assert_unreachable();
+ }
+ return {};
+}
+
+static void output_attribute_field(GeoNodeExecParams &params, GField field)
+{
+ switch (bke::cpp_type_to_custom_data_type(field.cpp_type())) {
+ case CD_PROP_FLOAT: {
+ params.set_output("Value_Float", Field<float>(field));
+ break;
+ }
+ case CD_PROP_FLOAT3: {
+ params.set_output("Value_Vector", Field<float3>(field));
+ break;
+ }
+ case CD_PROP_COLOR: {
+ params.set_output("Value_Color", Field<ColorGeometry4f>(field));
+ break;
+ }
+ case CD_PROP_BOOL: {
+ params.set_output("Value_Bool", Field<bool>(field));
+ break;
+ }
+ case CD_PROP_INT32: {
+ params.set_output("Value_Int", Field<int>(field));
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry = params.extract_input<GeometrySet>("Geometry");
+ const NodeGeometrySampleIndex &storage = node_storage(params.node());
+ const eCustomDataType data_type = eCustomDataType(storage.data_type);
+ const eAttrDomain domain = eAttrDomain(storage.domain);
+
+ auto fn = std::make_shared<SampleIndexFunction>(std::move(geometry),
+ get_input_attribute_field(params, data_type),
+ domain,
+ bool(storage.clamp));
+ auto op = FieldOperation::Create(std::move(fn), {params.extract_input<Field<int>>("Index")});
+ output_attribute_field(params, GField(std::move(op)));
+}
+
+} // namespace blender::nodes::node_geo_sample_index_cc
+
+void register_node_type_geo_sample_index()
+{
+ namespace file_ns = blender::nodes::node_geo_sample_index_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_SAMPLE_INDEX, "Sample Index", NODE_CLASS_GEOMETRY);
+ node_type_init(&ntype, file_ns::node_init);
+ node_type_update(&ntype, file_ns::node_update);
+ ntype.declare = file_ns::node_declare;
+ node_type_storage(
+ &ntype, "NodeGeometrySampleIndex", node_free_standard_storage, node_copy_standard_storage);
+ 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_sample_nearest.cc b/source/blender/nodes/geometry/nodes/node_geo_sample_nearest.cc
new file mode 100644
index 00000000000..691e3101d3d
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_sample_nearest.cc
@@ -0,0 +1,345 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "BLI_task.hh"
+
+#include "DNA_pointcloud_types.h"
+
+#include "BKE_bvhutils.h"
+#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 {
+
+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;
+ }
+ }
+}
+
+} // namespace blender::nodes
+
+namespace blender::nodes::node_geo_sample_nearest_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>(N_("Geometry"))
+ .supported_type({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD});
+ b.add_input<decl::Vector>(N_("Sample Position")).implicit_field();
+ b.add_output<decl::Int>(N_("Index")).dependent_field({1});
+}
+
+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)
+{
+ node->custom1 = CD_PROP_FLOAT;
+ node->custom2 = ATTR_DOMAIN_POINT;
+}
+
+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_polys(const Mesh &mesh,
+ const VArray<float3> &positions,
+ const IndexMask mask,
+ const MutableSpan<int> r_poly_indices,
+ const MutableSpan<float> r_distances_sq,
+ const MutableSpan<float3> r_positions)
+{
+ BLI_assert(mesh.totpoly > 0);
+
+ 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)
+{
+ const Span<MVert> verts = mesh.verts();
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
+
+ BLI_assert(mesh.totloop > 0);
+ Array<int> poly_indices(positions.size());
+ get_closest_mesh_polys(mesh, positions, mask, poly_indices, {}, {});
+
+ for (const int i : mask) {
+ const float3 position = positions[i];
+ const int poly_index = poly_indices[i];
+ const MPoly &poly = polys[poly_index];
+
+ /* Find the closest vertex in the polygon. */
+ float min_distance_sq = FLT_MAX;
+ const MVert *closest_mvert;
+ int closest_loop_index = 0;
+ for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
+ const MLoop &loop = loops[loop_index];
+ const int vertex_index = loop.v;
+ const MVert &mvert = verts[vertex_index];
+ const float distance_sq = math::distance_squared(position, float3(mvert.co));
+ if (distance_sq < min_distance_sq) {
+ min_distance_sq = distance_sq;
+ 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;
+ }
+ }
+}
+
+static bool component_is_available(const GeometrySet &geometry,
+ const GeometryComponentType type,
+ const eAttrDomain 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;
+}
+
+static const GeometryComponent *find_source_component(const GeometrySet &geometry,
+ const eAttrDomain 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;
+}
+
+class SampleNearestFunction : public fn::MultiFunction {
+ GeometrySet source_;
+ eAttrDomain domain_;
+
+ const GeometryComponent *src_component_;
+
+ fn::MFSignature signature_;
+
+ public:
+ SampleNearestFunction(GeometrySet geometry, eAttrDomain domain)
+ : source_(std::move(geometry)), domain_(domain)
+ {
+ source_.ensure_owns_direct_data();
+ signature_ = this->create_signature();
+ this->set_signature(&signature_);
+
+ this->src_component_ = find_source_component(source_, domain_);
+ }
+
+ fn::MFSignature create_signature()
+ {
+ blender::fn::MFSignatureBuilder signature{"Sample Nearest"};
+ signature.single_input<float3>("Position");
+ signature.single_output<int>("Index");
+ 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");
+ MutableSpan<int> indices = params.uninitialized_single_output<int>(1, "Index");
+ if (!src_component_) {
+ indices.fill_indices(mask, 0);
+ return;
+ }
+
+ switch (src_component_->type()) {
+ case GEO_COMPONENT_TYPE_MESH: {
+ const MeshComponent &component = *static_cast<const MeshComponent *>(src_component_);
+ const Mesh &mesh = *component.get_for_read();
+ Array<float> distances(mask.min_array_size());
+ switch (domain_) {
+ case ATTR_DOMAIN_POINT:
+ get_closest_mesh_points(mesh, positions, mask, indices, distances, {});
+ break;
+ case ATTR_DOMAIN_EDGE:
+ get_closest_mesh_edges(mesh, positions, mask, indices, distances, {});
+ break;
+ case ATTR_DOMAIN_FACE:
+ get_closest_mesh_polys(mesh, positions, mask, indices, distances, {});
+ break;
+ case ATTR_DOMAIN_CORNER:
+ get_closest_mesh_corners(mesh, positions, mask, indices, distances, {});
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ case GEO_COMPONENT_TYPE_POINT_CLOUD: {
+ const PointCloudComponent &component = *static_cast<const PointCloudComponent *>(
+ src_component_);
+ const PointCloud &points = *component.get_for_read();
+ Array<float> distances(mask.min_array_size());
+ get_closest_pointcloud_points(points, positions, mask, indices, distances);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+};
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry = params.extract_input<GeometrySet>("Geometry");
+ const eAttrDomain domain = eAttrDomain(params.node().custom2);
+ if (geometry.has_curves() && !geometry.has_mesh() && !geometry.has_pointcloud()) {
+ params.error_message_add(NodeWarningType::Error,
+ TIP_("The source geometry must contain a mesh or a point cloud"));
+ params.set_default_remaining_outputs();
+ return;
+ }
+
+ Field<float3> positions = params.extract_input<Field<float3>>("Sample Position");
+ auto fn = std::make_shared<SampleNearestFunction>(std::move(geometry), domain);
+ auto op = FieldOperation::Create(std::move(fn), {std::move(positions)});
+ params.set_output<Field<int>>("Index", Field<int>(std::move(op)));
+}
+
+} // namespace blender::nodes::node_geo_sample_nearest_cc
+
+void register_node_type_geo_sample_nearest()
+{
+ namespace file_ns = blender::nodes::node_geo_sample_nearest_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_SAMPLE_NEAREST, "Sample Nearest", NODE_CLASS_GEOMETRY);
+ 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_sample_nearest_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_sample_nearest_surface.cc
new file mode 100644
index 00000000000..9290185aef0
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_sample_nearest_surface.cc
@@ -0,0 +1,280 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "BLI_generic_array.hh"
+#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.h"
+#include "BKE_mesh_runtime.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_sample_nearest_surface_cc {
+
+using namespace blender::bke::mesh_surface_sample;
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>(N_("Mesh")).supported_type({GEO_COMPONENT_TYPE_MESH});
+
+ b.add_input<decl::Float>(N_("Value"), "Value_Float").hide_value().supports_field();
+ b.add_input<decl::Int>(N_("Value"), "Value_Int").hide_value().supports_field();
+ b.add_input<decl::Vector>(N_("Value"), "Value_Vector").hide_value().supports_field();
+ b.add_input<decl::Color>(N_("Value"), "Value_Color").hide_value().supports_field();
+ b.add_input<decl::Bool>(N_("Value"), "Value_Bool").hide_value().supports_field();
+
+ b.add_input<decl::Vector>(N_("Sample Position")).implicit_field();
+
+ b.add_output<decl::Float>(N_("Value"), "Value_Float").dependent_field({6});
+ b.add_output<decl::Int>(N_("Value"), "Value_Int").dependent_field({6});
+ b.add_output<decl::Vector>(N_("Value"), "Value_Vector").dependent_field({6});
+ b.add_output<decl::Color>(N_("Value"), "Value_Color").dependent_field({6});
+ b.add_output<decl::Bool>(N_("Value"), "Value_Bool").dependent_field({6});
+}
+
+static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE);
+}
+
+static void node_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ node->custom1 = CD_PROP_FLOAT;
+}
+
+static void node_update(bNodeTree *ntree, bNode *node)
+{
+ const eCustomDataType data_type = eCustomDataType(node->custom1);
+
+ bNodeSocket *in_socket_mesh = static_cast<bNodeSocket *>(node->inputs.first);
+ bNodeSocket *in_socket_float = in_socket_mesh->next;
+ bNodeSocket *in_socket_int32 = in_socket_float->next;
+ bNodeSocket *in_socket_vector = in_socket_int32->next;
+ bNodeSocket *in_socket_color4f = in_socket_vector->next;
+ bNodeSocket *in_socket_bool = in_socket_color4f->next;
+
+ nodeSetSocketAvailability(ntree, in_socket_vector, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(ntree, in_socket_float, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(ntree, in_socket_color4f, data_type == CD_PROP_COLOR);
+ nodeSetSocketAvailability(ntree, in_socket_bool, data_type == CD_PROP_BOOL);
+ nodeSetSocketAvailability(ntree, in_socket_int32, data_type == CD_PROP_INT32);
+
+ bNodeSocket *out_socket_float = static_cast<bNodeSocket *>(node->outputs.first);
+ bNodeSocket *out_socket_int32 = out_socket_float->next;
+ bNodeSocket *out_socket_vector = out_socket_int32->next;
+ bNodeSocket *out_socket_color4f = out_socket_vector->next;
+ bNodeSocket *out_socket_bool = out_socket_color4f->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_bool, data_type == CD_PROP_BOOL);
+ nodeSetSocketAvailability(ntree, out_socket_int32, data_type == CD_PROP_INT32);
+}
+
+static void node_gather_link_searches(GatherLinkSearchOpParams &params)
+{
+ const NodeDeclaration &declaration = *params.node_type().fixed_declaration;
+ search_link_ops_for_declarations(params, declaration.inputs().take_back(2));
+ search_link_ops_for_declarations(params, declaration.inputs().take_front(1));
+
+ const std::optional<eCustomDataType> 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_("Value"), [type](LinkSearchOpParams &params) {
+ bNode &node = params.add_node("GeometryNodeSampleNearestSurface");
+ node.custom1 = *type;
+ params.update_and_connect_available_socket(node, "Value");
+ });
+ }
+}
+
+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);
+}
+
+/**
+ * \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 source geometry once and store
+ * it for later.
+ */
+class SampleNearestSurfaceFunction : public fn::MultiFunction {
+ GeometrySet source_;
+ 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.
+ */
+ eAttrDomain domain_ = ATTR_DOMAIN_CORNER;
+
+ fn::MFSignature signature_;
+
+ std::optional<bke::MeshFieldContext> source_context_;
+ std::unique_ptr<FieldEvaluator> source_evaluator_;
+ const GVArray *source_data_;
+
+ public:
+ SampleNearestSurfaceFunction(GeometrySet geometry, GField src_field)
+ : source_(std::move(geometry)), src_field_(std::move(src_field))
+ {
+ source_.ensure_owns_direct_data();
+ signature_ = this->create_signature();
+ this->set_signature(&signature_);
+ this->evaluate_source_field();
+ }
+
+ fn::MFSignature create_signature()
+ {
+ blender::fn::MFSignatureBuilder signature{"Sample Nearest Surface"};
+ signature.single_input<float3>("Position");
+ signature.single_output("Value", 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, "Value");
+
+ const MeshComponent &mesh_component = *source_.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(*source_data_, domain_, eAttributeMapMode::INTERPOLATED, dst);
+ }
+
+ private:
+ void evaluate_source_field()
+ {
+ const Mesh &mesh = *source_.get_mesh_for_read();
+ source_context_.emplace(bke::MeshFieldContext{mesh, domain_});
+ const int domain_size = mesh.attributes().domain_size(domain_);
+ source_evaluator_ = std::make_unique<FieldEvaluator>(*source_context_, domain_size);
+ source_evaluator_->add(src_field_);
+ source_evaluator_->evaluate();
+ source_data_ = &source_evaluator_->get_evaluated(0);
+ }
+};
+
+static GField get_input_attribute_field(GeoNodeExecParams &params, const eCustomDataType data_type)
+{
+ switch (data_type) {
+ case CD_PROP_FLOAT:
+ return params.extract_input<Field<float>>("Value_Float");
+ case CD_PROP_FLOAT3:
+ return params.extract_input<Field<float3>>("Value_Vector");
+ case CD_PROP_COLOR:
+ return params.extract_input<Field<ColorGeometry4f>>("Value_Color");
+ case CD_PROP_BOOL:
+ return params.extract_input<Field<bool>>("Value_Bool");
+ case CD_PROP_INT32:
+ return params.extract_input<Field<int>>("Value_Int");
+ default:
+ BLI_assert_unreachable();
+ }
+ return {};
+}
+
+static void output_attribute_field(GeoNodeExecParams &params, GField field)
+{
+ switch (bke::cpp_type_to_custom_data_type(field.cpp_type())) {
+ case CD_PROP_FLOAT: {
+ params.set_output("Value_Float", Field<float>(field));
+ break;
+ }
+ case CD_PROP_FLOAT3: {
+ params.set_output("Value_Vector", Field<float3>(field));
+ break;
+ }
+ case CD_PROP_COLOR: {
+ params.set_output("Value_Color", Field<ColorGeometry4f>(field));
+ break;
+ }
+ case CD_PROP_BOOL: {
+ params.set_output("Value_Bool", Field<bool>(field));
+ break;
+ }
+ case CD_PROP_INT32: {
+ params.set_output("Value_Int", Field<int>(field));
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry = params.extract_input<GeometrySet>("Mesh");
+ const eCustomDataType data_type = eCustomDataType(params.node().custom1);
+ const Mesh *mesh = geometry.get_mesh_for_read();
+ if (mesh == nullptr) {
+ params.set_default_remaining_outputs();
+ return;
+ }
+ if (mesh->totpoly == 0 && mesh->totvert != 0) {
+ params.error_message_add(NodeWarningType::Error, TIP_("The source mesh must have faces"));
+ params.set_default_remaining_outputs();
+ return;
+ }
+
+ Field<float3> positions = params.extract_input<Field<float3>>("Sample Position");
+ GField field = get_input_attribute_field(params, data_type);
+ auto fn = std::make_shared<SampleNearestSurfaceFunction>(std::move(geometry), std::move(field));
+ auto op = FieldOperation::Create(std::move(fn), {std::move(positions)});
+ output_attribute_field(params, GField(std::move(op)));
+}
+
+} // namespace blender::nodes::node_geo_sample_nearest_surface_cc
+
+void register_node_type_geo_sample_nearest_surface()
+{
+ namespace file_ns = blender::nodes::node_geo_sample_nearest_surface_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_SAMPLE_NEAREST_SURFACE, "Sample Nearest Surface", NODE_CLASS_GEOMETRY);
+ node_type_init(&ntype, file_ns::node_init);
+ node_type_update(&ntype, file_ns::node_update);
+ ntype.declare = file_ns::node_declare;
+ node_type_size_preset(&ntype, NODE_SIZE_MIDDLE);
+ 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_scale_elements.cc b/source/blender/nodes/geometry/nodes/node_geo_scale_elements.cc
index d674f611c9f..2ebbf88b8ad 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_scale_elements.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_scale_elements.cc
@@ -147,14 +147,22 @@ static float4x4 create_single_axis_transform(const float3 &center,
return transform;
}
-using GetVertexIndicesFn =
- FunctionRef<void(const Mesh &mesh, int element_index, VectorSet<int> &r_vertex_indices)>;
+using GetVertexIndicesFn = FunctionRef<void(Span<MEdge> edges,
+ Span<MPoly> polys,
+ Span<MLoop> loops,
+ int element_index,
+ VectorSet<int> &r_vertex_indices)>;
static void scale_vertex_islands_uniformly(Mesh &mesh,
const Span<ElementIsland> islands,
const UniformScaleParams &params,
const GetVertexIndicesFn get_vertex_indices)
{
+ MutableSpan<MVert> verts = mesh.verts_for_write();
+ const Span<MEdge> edges = mesh.edges();
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
+
threading::parallel_for(islands.index_range(), 256, [&](const IndexRange range) {
for (const int island_index : range) {
const ElementIsland &island = islands[island_index];
@@ -164,7 +172,7 @@ static void scale_vertex_islands_uniformly(Mesh &mesh,
VectorSet<int> vertex_indices;
for (const int poly_index : island.element_indices) {
- get_vertex_indices(mesh, poly_index, vertex_indices);
+ get_vertex_indices(edges, polys, loops, poly_index, vertex_indices);
center += params.centers[poly_index];
scale += params.scales[poly_index];
}
@@ -175,7 +183,7 @@ static void scale_vertex_islands_uniformly(Mesh &mesh,
center *= f;
for (const int vert_index : vertex_indices) {
- MVert &vert = mesh.mvert[vert_index];
+ MVert &vert = verts[vert_index];
const float3 old_position = vert.co;
const float3 new_position = transform_with_uniform_scale(old_position, center, scale);
copy_v3_v3(vert.co, new_position);
@@ -191,6 +199,11 @@ static void scale_vertex_islands_on_axis(Mesh &mesh,
const AxisScaleParams &params,
const GetVertexIndicesFn get_vertex_indices)
{
+ MutableSpan<MVert> verts = mesh.verts_for_write();
+ const Span<MEdge> edges = mesh.edges();
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
+
threading::parallel_for(islands.index_range(), 256, [&](const IndexRange range) {
for (const int island_index : range) {
const ElementIsland &island = islands[island_index];
@@ -201,7 +214,7 @@ static void scale_vertex_islands_on_axis(Mesh &mesh,
VectorSet<int> vertex_indices;
for (const int poly_index : island.element_indices) {
- get_vertex_indices(mesh, poly_index, vertex_indices);
+ get_vertex_indices(edges, polys, loops, poly_index, vertex_indices);
center += params.centers[poly_index];
scale += params.scales[poly_index];
axis += params.axis_vectors[poly_index];
@@ -219,7 +232,7 @@ static void scale_vertex_islands_on_axis(Mesh &mesh,
const float4x4 transform = create_single_axis_transform(center, axis, scale);
for (const int vert_index : vertex_indices) {
- MVert &vert = mesh.mvert[vert_index];
+ MVert &vert = verts[vert_index];
const float3 old_position = vert.co;
const float3 new_position = transform * old_position;
copy_v3_v3(vert.co, new_position);
@@ -232,11 +245,14 @@ static void scale_vertex_islands_on_axis(Mesh &mesh,
static Vector<ElementIsland> prepare_face_islands(const Mesh &mesh, const IndexMask face_selection)
{
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
+
/* Use the disjoint set data structure to determine which vertices have to be scaled together. */
DisjointSet disjoint_set(mesh.totvert);
for (const int poly_index : face_selection) {
- const MPoly &poly = mesh.mpoly[poly_index];
- const Span<MLoop> poly_loops{mesh.mloop + poly.loopstart, poly.totloop};
+ const MPoly &poly = polys[poly_index];
+ const Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop);
for (const int loop_index : IndexRange(poly.totloop - 1)) {
const int v1 = poly_loops[loop_index].v;
const int v2 = poly_loops[loop_index + 1].v;
@@ -252,8 +268,8 @@ static Vector<ElementIsland> prepare_face_islands(const Mesh &mesh, const IndexM
/* Gather all of the face indices in each island into separate vectors. */
for (const int poly_index : face_selection) {
- const MPoly &poly = mesh.mpoly[poly_index];
- const Span<MLoop> poly_loops{mesh.mloop + poly.loopstart, poly.totloop};
+ const MPoly &poly = polys[poly_index];
+ const Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop);
const int island_id = disjoint_set.find_root(poly_loops[0].v);
const int island_index = island_ids.index_of_or_add(island_id);
if (island_index == islands.size()) {
@@ -266,10 +282,14 @@ static Vector<ElementIsland> prepare_face_islands(const Mesh &mesh, const IndexM
return islands;
}
-static void get_face_vertices(const Mesh &mesh, int face_index, VectorSet<int> &r_vertex_indices)
+static void get_face_verts(const Span<MEdge> /*edges*/,
+ const Span<MPoly> polys,
+ const Span<MLoop> loops,
+ int face_index,
+ VectorSet<int> &r_vertex_indices)
{
- const MPoly &poly = mesh.mpoly[face_index];
- const Span<MLoop> poly_loops{mesh.mloop + poly.loopstart, poly.totloop};
+ const MPoly &poly = polys[face_index];
+ const Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop);
for (const MLoop &loop : poly_loops) {
r_vertex_indices.add(loop.v);
}
@@ -288,18 +308,14 @@ static AxisScaleParams evaluate_axis_scale_fields(FieldEvaluator &evaluator,
return out;
}
-static void scale_faces_on_axis(MeshComponent &mesh_component, const AxisScaleFields &fields)
+static void scale_faces_on_axis(Mesh &mesh, const AxisScaleFields &fields)
{
- Mesh &mesh = *mesh_component.get_for_write();
- mesh.mvert = static_cast<MVert *>(
- CustomData_duplicate_referenced_layer(&mesh.vdata, CD_MVERT, mesh.totvert));
-
- GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_FACE};
+ bke::MeshFieldContext field_context{mesh, ATTR_DOMAIN_FACE};
FieldEvaluator evaluator{field_context, mesh.totpoly};
AxisScaleParams params = evaluate_axis_scale_fields(evaluator, fields);
Vector<ElementIsland> island = prepare_face_islands(mesh, params.selection);
- scale_vertex_islands_on_axis(mesh, island, params, get_face_vertices);
+ scale_vertex_islands_on_axis(mesh, island, params, get_face_verts);
}
static UniformScaleParams evaluate_uniform_scale_fields(FieldEvaluator &evaluator,
@@ -314,26 +330,24 @@ static UniformScaleParams evaluate_uniform_scale_fields(FieldEvaluator &evaluato
return out;
}
-static void scale_faces_uniformly(MeshComponent &mesh_component, const UniformScaleFields &fields)
+static void scale_faces_uniformly(Mesh &mesh, const UniformScaleFields &fields)
{
- Mesh &mesh = *mesh_component.get_for_write();
- mesh.mvert = static_cast<MVert *>(
- CustomData_duplicate_referenced_layer(&mesh.vdata, CD_MVERT, mesh.totvert));
-
- GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_FACE};
+ bke::MeshFieldContext field_context{mesh, ATTR_DOMAIN_FACE};
FieldEvaluator evaluator{field_context, mesh.totpoly};
UniformScaleParams params = evaluate_uniform_scale_fields(evaluator, fields);
Vector<ElementIsland> island = prepare_face_islands(mesh, params.selection);
- scale_vertex_islands_uniformly(mesh, island, params, get_face_vertices);
+ scale_vertex_islands_uniformly(mesh, island, params, get_face_verts);
}
static Vector<ElementIsland> prepare_edge_islands(const Mesh &mesh, const IndexMask edge_selection)
{
+ const Span<MEdge> edges = mesh.edges();
+
/* Use the disjoint set data structure to determine which vertices have to be scaled together. */
DisjointSet disjoint_set(mesh.totvert);
for (const int edge_index : edge_selection) {
- const MEdge &edge = mesh.medge[edge_index];
+ const MEdge &edge = edges[edge_index];
disjoint_set.join(edge.v1, edge.v2);
}
@@ -344,7 +358,7 @@ static Vector<ElementIsland> prepare_edge_islands(const Mesh &mesh, const IndexM
/* Gather all of the edge indices in each island into separate vectors. */
for (const int edge_index : edge_selection) {
- const MEdge &edge = mesh.medge[edge_index];
+ const MEdge &edge = edges[edge_index];
const int island_id = disjoint_set.find_root(edge.v1);
const int island_index = island_ids.index_of_or_add(island_id);
if (island_index == islands.size()) {
@@ -357,39 +371,35 @@ static Vector<ElementIsland> prepare_edge_islands(const Mesh &mesh, const IndexM
return islands;
}
-static void get_edge_vertices(const Mesh &mesh, int edge_index, VectorSet<int> &r_vertex_indices)
+static void get_edge_verts(const Span<MEdge> edges,
+ const Span<MPoly> /*polys*/,
+ const Span<MLoop> /*loops*/,
+ int edge_index,
+ VectorSet<int> &r_vertex_indices)
{
- const MEdge &edge = mesh.medge[edge_index];
+ const MEdge &edge = edges[edge_index];
r_vertex_indices.add(edge.v1);
r_vertex_indices.add(edge.v2);
}
-static void scale_edges_uniformly(MeshComponent &mesh_component, const UniformScaleFields &fields)
+static void scale_edges_uniformly(Mesh &mesh, const UniformScaleFields &fields)
{
- Mesh &mesh = *mesh_component.get_for_write();
- mesh.mvert = static_cast<MVert *>(
- CustomData_duplicate_referenced_layer(&mesh.vdata, CD_MVERT, mesh.totvert));
-
- GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_EDGE};
+ bke::MeshFieldContext field_context{mesh, ATTR_DOMAIN_EDGE};
FieldEvaluator evaluator{field_context, mesh.totedge};
UniformScaleParams params = evaluate_uniform_scale_fields(evaluator, fields);
Vector<ElementIsland> island = prepare_edge_islands(mesh, params.selection);
- scale_vertex_islands_uniformly(mesh, island, params, get_edge_vertices);
+ scale_vertex_islands_uniformly(mesh, island, params, get_edge_verts);
}
-static void scale_edges_on_axis(MeshComponent &mesh_component, const AxisScaleFields &fields)
+static void scale_edges_on_axis(Mesh &mesh, const AxisScaleFields &fields)
{
- Mesh &mesh = *mesh_component.get_for_write();
- mesh.mvert = static_cast<MVert *>(
- CustomData_duplicate_referenced_layer(&mesh.vdata, CD_MVERT, mesh.totvert));
-
- GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_EDGE};
+ bke::MeshFieldContext field_context{mesh, ATTR_DOMAIN_EDGE};
FieldEvaluator evaluator{field_context, mesh.totedge};
AxisScaleParams params = evaluate_axis_scale_fields(evaluator, fields);
Vector<ElementIsland> island = prepare_edge_islands(mesh, params.selection);
- scale_vertex_islands_on_axis(mesh, island, params, get_edge_vertices);
+ scale_vertex_islands_on_axis(mesh, island, params, get_edge_verts);
}
static void node_geo_exec(GeoNodeExecParams params)
@@ -410,42 +420,38 @@ static void node_geo_exec(GeoNodeExecParams params)
}
geometry.modify_geometry_sets([&](GeometrySet &geometry) {
- if (!geometry.has_mesh()) {
- return;
- }
- MeshComponent &mesh_component = geometry.get_component_for_write<MeshComponent>();
- switch (domain) {
- case ATTR_DOMAIN_FACE: {
- switch (scale_mode) {
- case GEO_NODE_SCALE_ELEMENTS_UNIFORM: {
- scale_faces_uniformly(mesh_component, {selection_field, scale_field, center_field});
- break;
- }
- case GEO_NODE_SCALE_ELEMENTS_SINGLE_AXIS: {
- scale_faces_on_axis(mesh_component,
- {selection_field, scale_field, center_field, axis_field});
- break;
+ if (Mesh *mesh = geometry.get_mesh_for_write()) {
+ switch (domain) {
+ case ATTR_DOMAIN_FACE: {
+ switch (scale_mode) {
+ case GEO_NODE_SCALE_ELEMENTS_UNIFORM: {
+ scale_faces_uniformly(*mesh, {selection_field, scale_field, center_field});
+ break;
+ }
+ case GEO_NODE_SCALE_ELEMENTS_SINGLE_AXIS: {
+ scale_faces_on_axis(*mesh, {selection_field, scale_field, center_field, axis_field});
+ break;
+ }
}
+ break;
}
- break;
- }
- case ATTR_DOMAIN_EDGE: {
- switch (scale_mode) {
- case GEO_NODE_SCALE_ELEMENTS_UNIFORM: {
- scale_edges_uniformly(mesh_component, {selection_field, scale_field, center_field});
- break;
- }
- case GEO_NODE_SCALE_ELEMENTS_SINGLE_AXIS: {
- scale_edges_on_axis(mesh_component,
- {selection_field, scale_field, center_field, axis_field});
- break;
+ case ATTR_DOMAIN_EDGE: {
+ switch (scale_mode) {
+ case GEO_NODE_SCALE_ELEMENTS_UNIFORM: {
+ scale_edges_uniformly(*mesh, {selection_field, scale_field, center_field});
+ break;
+ }
+ case GEO_NODE_SCALE_ELEMENTS_SINGLE_AXIS: {
+ scale_edges_on_axis(*mesh, {selection_field, scale_field, center_field, axis_field});
+ break;
+ }
}
+ break;
}
- break;
+ default:
+ BLI_assert_unreachable();
+ break;
}
- default:
- BLI_assert_unreachable();
- break;
}
});
diff --git a/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc
index 7156feb37d7..21fe724e194 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc
@@ -21,9 +21,8 @@ static void node_declare(NodeDeclarationBuilder &b)
static void scale_instances(GeoNodeExecParams &params, InstancesComponent &instances_component)
{
- GeometryComponentFieldContext field_context{instances_component, ATTR_DOMAIN_INSTANCE};
-
- fn::FieldEvaluator evaluator{field_context, instances_component.instances_num()};
+ const bke::InstancesFieldContext context{instances_component};
+ fn::FieldEvaluator evaluator{context, instances_component.instances_num()};
evaluator.set_selection(params.extract_input<Field<bool>>("Selection"));
evaluator.add(params.extract_input<Field<float3>>("Scale"));
evaluator.add(params.extract_input<Field<float3>>("Center"));
diff --git a/source/blender/nodes/geometry/nodes/node_geo_self_object.cc b/source/blender/nodes/geometry/nodes/node_geo_self_object.cc
new file mode 100644
index 00000000000..7b33afdb4a0
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_self_object.cc
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_self_object_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_output<decl::Object>(N_("Self Object"));
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ params.set_output("Self Object", const_cast<Object *>(params.self_object()));
+}
+
+} // namespace blender::nodes::node_geo_self_object_cc
+
+void register_node_type_geo_self_object()
+{
+ namespace file_ns = blender::nodes::node_geo_self_object_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_SELF_OBJECT, "Self Object", 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_set_curve_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc
index fc3cb7006bb..e529ddddabe 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc
@@ -68,19 +68,18 @@ static void update_handle_types_for_movement(int8_t &type, int8_t &other)
}
}
-static void set_position_in_component(CurveComponent &component,
+static void set_position_in_component(bke::CurvesGeometry &curves,
const GeometryNodeCurveHandleMode mode,
const Field<bool> &selection_field,
const Field<float3> &position_field,
const Field<float3> &offset_field)
{
- GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
- const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT);
- if (domain_size == 0) {
+ if (curves.points_num() == 0) {
return;
}
- fn::FieldEvaluator evaluator{field_context, domain_size};
+ bke::CurvesFieldContext field_context{curves, ATTR_DOMAIN_POINT};
+ fn::FieldEvaluator evaluator{field_context, curves.points_num()};
evaluator.set_selection(selection_field);
evaluator.add(position_field);
evaluator.add(offset_field);
@@ -89,9 +88,6 @@ static void set_position_in_component(CurveComponent &component,
const VArray<float3> new_positions = evaluator.get_evaluated<float3>(0);
const VArray<float3> new_offsets = evaluator.get_evaluated<float3>(1);
- Curves &curves_id = *component.get_for_write();
- bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
-
Span<float3> positions = curves.positions();
const bool use_left = mode == GEO_NODE_CURVE_HANDLE_LEFT;
@@ -141,22 +137,17 @@ static void node_geo_exec(GeoNodeExecParams params)
std::atomic<bool> has_bezier = false;
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
- if (!geometry_set.has_curves()) {
- return;
- }
- has_curves = true;
- const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>();
- const AttributeAccessor attributes = *component.attributes();
- if (!attributes.contains("handle_left") || !attributes.contains("handle_right")) {
- return;
- }
- has_bezier = true;
+ if (Curves *curves_id = geometry_set.get_curves_for_write()) {
+ bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry);
+ has_curves = true;
+ const AttributeAccessor attributes = curves.attributes();
+ if (!attributes.contains("handle_left") || !attributes.contains("handle_right")) {
+ return;
+ }
+ has_bezier = true;
- set_position_in_component(geometry_set.get_component_for_write<CurveComponent>(),
- mode,
- selection_field,
- position_field,
- offset_field);
+ set_position_in_component(curves, mode, selection_field, position_field, offset_field);
+ }
});
if (has_curves && !has_bezier) {
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc
index e4fae95b5a5..0d361090068 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc
@@ -1,5 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include "BKE_curves.hh"
+
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_set_curve_radius_cc {
@@ -16,21 +18,19 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Geometry>(N_("Curve"));
}
-static void set_radius_in_component(GeometryComponent &component,
- const Field<bool> &selection_field,
- const Field<float> &radius_field)
+static void set_radius(bke::CurvesGeometry &curves,
+ const Field<bool> &selection_field,
+ const Field<float> &radius_field)
{
- const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT);
- if (domain_size == 0) {
+ if (curves.points_num() == 0) {
return;
}
- MutableAttributeAccessor attributes = *component.attributes_for_write();
- GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
-
+ MutableAttributeAccessor attributes = curves.attributes_for_write();
AttributeWriter<float> radii = attributes.lookup_or_add_for_write<float>("radius",
ATTR_DOMAIN_POINT);
- fn::FieldEvaluator evaluator{field_context, domain_size};
+ bke::CurvesFieldContext field_context{curves, ATTR_DOMAIN_POINT};
+ fn::FieldEvaluator evaluator{field_context, curves.points_num()};
evaluator.set_selection(selection_field);
evaluator.add_with_destination(radius_field, radii.varray);
evaluator.evaluate();
@@ -45,9 +45,8 @@ static void node_geo_exec(GeoNodeExecParams params)
Field<float> radii_field = params.extract_input<Field<float>>("Radius");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
- if (geometry_set.has_curves()) {
- set_radius_in_component(
- geometry_set.get_component_for_write<CurveComponent>(), selection_field, radii_field);
+ if (Curves *curves_id = geometry_set.get_curves_for_write()) {
+ set_radius(bke::CurvesGeometry::wrap(curves_id->geometry), selection_field, radii_field);
}
});
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc
index 2211ac62727..8c1fb883f46 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc
@@ -1,5 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include "BKE_curves.hh"
+
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_set_curve_tilt_cc {
@@ -12,22 +14,19 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Geometry>(N_("Curve"));
}
-static void set_tilt_in_component(GeometryComponent &component,
- const Field<bool> &selection_field,
- const Field<float> &tilt_field)
+static void set_tilt(bke::CurvesGeometry &curves,
+ const Field<bool> &selection_field,
+ const Field<float> &tilt_field)
{
- const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT);
- if (domain_size == 0) {
+ if (curves.points_num() == 0) {
return;
}
- MutableAttributeAccessor attributes = *component.attributes_for_write();
-
- GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
-
+ MutableAttributeAccessor attributes = curves.attributes_for_write();
AttributeWriter<float> tilts = attributes.lookup_or_add_for_write<float>("tilt",
ATTR_DOMAIN_POINT);
- fn::FieldEvaluator evaluator{field_context, domain_size};
+ bke::CurvesFieldContext field_context{curves, ATTR_DOMAIN_POINT};
+ fn::FieldEvaluator evaluator{field_context, curves.points_num()};
evaluator.set_selection(selection_field);
evaluator.add_with_destination(tilt_field, tilts.varray);
evaluator.evaluate();
@@ -42,9 +41,8 @@ static void node_geo_exec(GeoNodeExecParams params)
Field<float> tilt_field = params.extract_input<Field<float>>("Tilt");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
- if (geometry_set.has_curves()) {
- set_tilt_in_component(
- geometry_set.get_component_for_write<CurveComponent>(), selection_field, tilt_field);
+ if (Curves *curves_id = geometry_set.get_curves_for_write()) {
+ set_tilt(bke::CurvesGeometry::wrap(curves_id->geometry), selection_field, tilt_field);
}
});
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_id.cc b/source/blender/nodes/geometry/nodes/node_geo_set_id.cc
index fbb2ecbb799..5864401223b 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_id.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_id.cc
@@ -24,7 +24,7 @@ static void set_id_in_component(GeometryComponent &component,
return;
}
MutableAttributeAccessor attributes = *component.attributes_for_write();
- GeometryComponentFieldContext field_context{component, domain};
+ bke::GeometryFieldContext field_context{component, domain};
fn::FieldEvaluator evaluator{field_context, domain_size};
evaluator.set_selection(selection_field);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_material.cc b/source/blender/nodes/geometry/nodes/node_geo_set_material.cc
index 507c6e81b1f..8d00d82664b 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_material.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_material.cc
@@ -12,6 +12,7 @@
#include "DNA_volume_types.h"
#include "BKE_material.h"
+#include "BKE_mesh.h"
namespace blender::nodes::node_geo_set_material_cc {
@@ -49,11 +50,11 @@ static void assign_material_to_faces(Mesh &mesh, const IndexMask selection, Mate
BKE_id_material_eval_assign(&mesh.id, new_material_index + 1, material);
}
- mesh.mpoly = (MPoly *)CustomData_duplicate_referenced_layer(&mesh.pdata, CD_MPOLY, mesh.totpoly);
- for (const int i : selection) {
- MPoly &poly = mesh.mpoly[i];
- poly.mat_nr = new_material_index;
- }
+ MutableAttributeAccessor attributes = mesh.attributes_for_write();
+ SpanAttributeWriter<int> material_indices = attributes.lookup_or_add_for_write_span<int>(
+ "material_index", ATTR_DOMAIN_FACE);
+ material_indices.span.fill_indices(selection, new_material_index);
+ material_indices.finish();
}
static void node_geo_exec(GeoNodeExecParams params)
@@ -72,8 +73,8 @@ static void node_geo_exec(GeoNodeExecParams params)
if (geometry_set.has_mesh()) {
MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
Mesh &mesh = *mesh_component.get_for_write();
- GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_FACE};
+ bke::MeshFieldContext field_context{mesh, ATTR_DOMAIN_FACE};
fn::FieldEvaluator selection_evaluator{field_context, mesh.totpoly};
selection_evaluator.add(selection_field);
selection_evaluator.evaluate();
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc b/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc
index 0dc89bb7ef4..bb9ac9b5d4c 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc
@@ -14,21 +14,23 @@ static void node_declare(NodeDeclarationBuilder &b)
static void set_material_index_in_component(GeometryComponent &component,
const Field<bool> &selection_field,
- const Field<int> &index_field)
+ const Field<int> &index_field,
+ const eAttrDomain domain)
{
- const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE);
+ const int domain_size = component.attribute_domain_size(domain);
if (domain_size == 0) {
return;
}
MutableAttributeAccessor attributes = *component.attributes_for_write();
- GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_FACE};
+ bke::GeometryFieldContext field_context{component, domain};
- AttributeWriter<int> indices = attributes.lookup_or_add_for_write<int>("material_index",
- ATTR_DOMAIN_FACE);
+ const bke::AttributeValidator validator = attributes.lookup_validator("material_index");
+ AttributeWriter<int> indices = attributes.lookup_or_add_for_write<int>("material_index", domain);
fn::FieldEvaluator evaluator{field_context, domain_size};
evaluator.set_selection(selection_field);
- evaluator.add_with_destination(index_field, indices.varray);
+ evaluator.add_with_destination(validator.validate_field_if_necessary(index_field),
+ indices.varray);
evaluator.evaluate();
indices.finish();
}
@@ -41,8 +43,10 @@ static void node_geo_exec(GeoNodeExecParams params)
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
if (geometry_set.has_mesh()) {
- set_material_index_in_component(
- geometry_set.get_component_for_write<MeshComponent>(), selection_field, index_field);
+ set_material_index_in_component(geometry_set.get_component_for_write<MeshComponent>(),
+ selection_field,
+ index_field,
+ ATTR_DOMAIN_FACE);
}
});
params.set_output("Geometry", std::move(geometry_set));
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc b/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc
index da7977a4fb4..28d07b31218 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc
@@ -1,5 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include "DNA_pointcloud_types.h"
+
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_set_point_radius_cc {
@@ -16,21 +18,19 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Geometry>(N_("Points"));
}
-static void set_radius_in_component(GeometryComponent &component,
+static void set_radius_in_component(PointCloud &pointcloud,
const Field<bool> &selection_field,
const Field<float> &radius_field)
{
- const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT);
- if (domain_size == 0) {
+ if (pointcloud.totpoint == 0) {
return;
}
- MutableAttributeAccessor attributes = *component.attributes_for_write();
- GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
-
+ MutableAttributeAccessor attributes = pointcloud.attributes_for_write();
AttributeWriter<float> radii = attributes.lookup_or_add_for_write<float>("radius",
ATTR_DOMAIN_POINT);
- fn::FieldEvaluator evaluator{field_context, domain_size};
+ bke::PointCloudFieldContext field_context{pointcloud};
+ fn::FieldEvaluator evaluator{field_context, pointcloud.totpoint};
evaluator.set_selection(selection_field);
evaluator.add_with_destination(radius_field, radii.varray);
evaluator.evaluate();
@@ -45,10 +45,8 @@ static void node_geo_exec(GeoNodeExecParams params)
Field<float> radii_field = params.extract_input<Field<float>>("Radius");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
- if (geometry_set.has_pointcloud()) {
- set_radius_in_component(geometry_set.get_component_for_write<PointCloudComponent>(),
- selection_field,
- radii_field);
+ if (PointCloud *pointcloud = geometry_set.get_pointcloud_for_write()) {
+ set_radius_in_component(*pointcloud, selection_field, radii_field);
}
});
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc
index 880252de4fa..613f140ff0a 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc
@@ -8,6 +8,7 @@
#include "DNA_meshdata_types.h"
#include "BKE_curves.hh"
+#include "BKE_mesh.h"
#include "node_geometry_util.hh"
@@ -35,14 +36,14 @@ static void set_computed_position_and_offset(GeometryComponent &component,
switch (component.type()) {
case GEO_COMPONENT_TYPE_MESH: {
Mesh *mesh = static_cast<MeshComponent &>(component).get_for_write();
- MutableSpan<MVert> mverts{mesh->mvert, mesh->totvert};
+ MutableSpan<MVert> verts = mesh->verts_for_write();
if (in_positions.is_same(positions.varray)) {
devirtualize_varray(in_offsets, [&](const auto in_offsets) {
threading::parallel_for(
selection.index_range(), grain_size, [&](const IndexRange range) {
for (const int i : selection.slice(range)) {
const float3 offset = in_offsets[i];
- add_v3_v3(mverts[i].co, offset);
+ add_v3_v3(verts[i].co, offset);
}
});
});
@@ -54,7 +55,7 @@ static void set_computed_position_and_offset(GeometryComponent &component,
selection.index_range(), grain_size, [&](const IndexRange range) {
for (const int i : selection.slice(range)) {
const float3 new_position = in_positions[i] + in_offsets[i];
- copy_v3_v3(mverts[i].co, new_position);
+ copy_v3_v3(verts[i].co, new_position);
}
});
});
@@ -136,7 +137,7 @@ static void set_position_in_component(GeometryComponent &component,
{
eAttrDomain domain = component.type() == GEO_COMPONENT_TYPE_INSTANCES ? ATTR_DOMAIN_INSTANCE :
ATTR_DOMAIN_POINT;
- GeometryComponentFieldContext field_context{component, domain};
+ bke::GeometryFieldContext field_context{component, domain};
const int domain_size = component.attribute_domain_size(domain);
if (domain_size == 0) {
return;
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc b/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc
index e0cf0f98d58..0df51e49827 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc
@@ -1,5 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include "DNA_mesh_types.h"
+
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_set_shade_smooth_cc {
@@ -12,27 +14,25 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Geometry>(N_("Geometry"));
}
-static void set_smooth_in_component(GeometryComponent &component,
- const Field<bool> &selection_field,
- const Field<bool> &shade_field)
+static void set_smooth(Mesh &mesh,
+ const Field<bool> &selection_field,
+ const Field<bool> &shade_field)
{
- const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE);
- if (domain_size == 0) {
+ if (mesh.totpoly == 0) {
return;
}
- MutableAttributeAccessor attributes = *component.attributes_for_write();
-
- GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_FACE};
- AttributeWriter<bool> shades = attributes.lookup_or_add_for_write<bool>("shade_smooth",
+ MutableAttributeAccessor attributes = mesh.attributes_for_write();
+ AttributeWriter<bool> smooth = attributes.lookup_or_add_for_write<bool>("shade_smooth",
ATTR_DOMAIN_FACE);
- fn::FieldEvaluator evaluator{field_context, domain_size};
+ bke::MeshFieldContext field_context{mesh, ATTR_DOMAIN_FACE};
+ fn::FieldEvaluator evaluator{field_context, mesh.totpoly};
evaluator.set_selection(selection_field);
- evaluator.add_with_destination(shade_field, shades.varray);
+ evaluator.add_with_destination(shade_field, smooth.varray);
evaluator.evaluate();
- shades.finish();
+ smooth.finish();
}
static void node_geo_exec(GeoNodeExecParams params)
@@ -42,9 +42,8 @@ static void node_geo_exec(GeoNodeExecParams params)
Field<bool> shade_field = params.extract_input<Field<bool>>("Shade Smooth");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
- if (geometry_set.has_mesh()) {
- set_smooth_in_component(
- geometry_set.get_component_for_write<MeshComponent>(), selection_field, shade_field);
+ if (Mesh *mesh = geometry_set.get_mesh_for_write()) {
+ set_smooth(*mesh, selection_field, shade_field);
}
});
params.set_output("Geometry", std::move(geometry_set));
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc b/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc
index a35d8d66558..d8faa154477 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc
@@ -1,5 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include "BKE_curves.hh"
+
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_set_spline_cyclic_cc {
@@ -12,22 +14,19 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Geometry>(N_("Geometry"));
}
-static void set_cyclic_in_component(GeometryComponent &component,
- const Field<bool> &selection_field,
- const Field<bool> &cyclic_field)
+static void set_cyclic(bke::CurvesGeometry &curves,
+ const Field<bool> &selection_field,
+ const Field<bool> &cyclic_field)
{
- const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE);
- if (domain_size == 0) {
+ if (curves.curves_num() == 0) {
return;
}
- MutableAttributeAccessor attributes = *component.attributes_for_write();
-
- GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE};
-
+ MutableAttributeAccessor attributes = curves.attributes_for_write();
AttributeWriter<bool> cyclics = attributes.lookup_or_add_for_write<bool>("cyclic",
ATTR_DOMAIN_CURVE);
- fn::FieldEvaluator evaluator{field_context, domain_size};
+ bke::CurvesFieldContext field_context{curves, ATTR_DOMAIN_CURVE};
+ fn::FieldEvaluator evaluator{field_context, curves.curves_num()};
evaluator.set_selection(selection_field);
evaluator.add_with_destination(cyclic_field, cyclics.varray);
evaluator.evaluate();
@@ -42,9 +41,8 @@ static void node_geo_exec(GeoNodeExecParams params)
Field<bool> cyclic_field = params.extract_input<Field<bool>>("Cyclic");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
- if (geometry_set.has_curves()) {
- set_cyclic_in_component(
- geometry_set.get_component_for_write<CurveComponent>(), selection_field, cyclic_field);
+ if (Curves *curves_id = geometry_set.get_curves_for_write()) {
+ set_cyclic(bke::CurvesGeometry::wrap(curves_id->geometry), selection_field, cyclic_field);
}
});
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc b/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc
index fcebc1116d7..d46ceac92ba 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc
@@ -1,5 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include "BKE_curves.hh"
+
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_set_spline_resolution_cc {
@@ -12,22 +14,19 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Geometry>(N_("Geometry"));
}
-static void set_resolution_in_component(GeometryComponent &component,
- const Field<bool> &selection_field,
- const Field<int> &resolution_field)
+static void set_resolution(bke::CurvesGeometry &curves,
+ const Field<bool> &selection_field,
+ const Field<int> &resolution_field)
{
- const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE);
- if (domain_size == 0) {
+ if (curves.curves_num() == 0) {
return;
}
- MutableAttributeAccessor attributes = *component.attributes_for_write();
-
- GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE};
-
+ MutableAttributeAccessor attributes = curves.attributes_for_write();
AttributeWriter<int> resolutions = attributes.lookup_or_add_for_write<int>("resolution",
ATTR_DOMAIN_CURVE);
- fn::FieldEvaluator evaluator{field_context, domain_size};
+ bke::CurvesFieldContext field_context{curves, ATTR_DOMAIN_CURVE};
+ fn::FieldEvaluator evaluator{field_context, curves.curves_num()};
evaluator.set_selection(selection_field);
evaluator.add_with_destination(resolution_field, resolutions.varray);
evaluator.evaluate();
@@ -38,12 +37,13 @@ static void set_resolution_in_component(GeometryComponent &component,
static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
- Field<int> resolution_field = params.extract_input<Field<int>>("Resolution");
+ Field<bool> selection = params.extract_input<Field<bool>>("Selection");
+ Field<int> resolution = params.extract_input<Field<int>>("Resolution");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
- set_resolution_in_component(
- geometry_set.get_component_for_write<CurveComponent>(), selection_field, resolution_field);
+ if (Curves *curves_id = geometry_set.get_curves_for_write()) {
+ set_resolution(bke::CurvesGeometry::wrap(curves_id->geometry), selection, resolution);
+ }
});
params.set_output("Geometry", std::move(geometry_set));
diff --git a/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc
index 70c33ad6a96..37e55602fb0 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc
@@ -71,6 +71,7 @@ static void node_gather_link_searches(GatherLinkSearchOpParams &params)
{
const NodeDeclaration &declaration = *params.node_type().fixed_declaration;
search_link_ops_for_declarations(params, declaration.inputs().take_front(2));
+ search_link_ops_for_declarations(params, declaration.outputs().take_front(1));
if (params.in_out() == SOCK_IN) {
const std::optional<eCustomDataType> type = node_data_type_to_custom_data_type(
@@ -98,11 +99,12 @@ static void try_capture_field_on_geometry(GeometryComponent &component,
return;
}
- GeometryComponentFieldContext field_context{component, domain};
+ bke::GeometryFieldContext field_context{component, domain};
const IndexMask mask{IndexMask(domain_size)};
const CPPType &type = field.cpp_type();
const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(type);
+ const bke::AttributeValidator validator = attributes.lookup_validator(name);
/* Could avoid allocating a new buffer if:
* - We are writing to an attribute that exists already with the correct domain and type.
@@ -110,7 +112,8 @@ static void try_capture_field_on_geometry(GeometryComponent &component,
void *buffer = MEM_mallocN(type.size() * domain_size, __func__);
fn::FieldEvaluator evaluator{field_context, &mask};
- evaluator.add_with_destination(field, GMutableSpan{type, buffer, domain_size});
+ evaluator.add_with_destination(validator.validate_field_if_necessary(field),
+ GMutableSpan{type, buffer, domain_size});
evaluator.evaluate();
if (GAttributeWriter attribute = attributes.lookup_for_write(name)) {
@@ -123,7 +126,7 @@ static void try_capture_field_on_geometry(GeometryComponent &component,
}
}
attributes.remove(name);
- if (attributes.add(name, domain, data_type, bke::AttributeInitMove{buffer})) {
+ if (attributes.add(name, domain, data_type, bke::AttributeInitMoveArray{buffer})) {
return;
}
@@ -149,7 +152,7 @@ static void node_geo_exec(GeoNodeExecParams params)
return;
}
- params.used_named_attribute(name, eNamedAttrUsage::Write);
+ params.used_named_attribute(name, NamedAttributeUsage::Write);
const NodeGeometryStoreNamedAttribute &storage = node_storage(params.node());
const eCustomDataType data_type = static_cast<eCustomDataType>(storage.data_type);
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 bb33430a02f..09c01b8c627 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_string_join.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_string_join.cc
@@ -13,12 +13,13 @@ static void node_declare(NodeDeclarationBuilder &b)
static void node_geo_exec(GeoNodeExecParams params)
{
- Vector<std::string> strings = params.extract_multi_input<std::string>("Strings");
+ Vector<fn::ValueOrField<std::string>> strings =
+ params.extract_input<Vector<fn::ValueOrField<std::string>>>("Strings");
const std::string delim = params.extract_input<std::string>("Delimiter");
std::string output;
for (const int i : strings.index_range()) {
- output += strings[i];
+ output += strings[i].as_value();
if (i < (strings.size() - 1)) {
output += delim;
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc
index eda6a51d412..60c8a89a6bf 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc
@@ -73,7 +73,7 @@ static void write_vertex_creases(Mesh &mesh, const VArray<float> &crease_varray)
}
else {
crease = static_cast<float *>(
- CustomData_add_layer(&mesh.vdata, CD_CREASE, CD_DEFAULT, nullptr, mesh.totvert));
+ CustomData_add_layer(&mesh.vdata, CD_CREASE, CD_CONSTRUCT, nullptr, mesh.totvert));
}
materialize_and_clamp_creases(crease_varray, {crease, mesh.totvert});
}
@@ -119,21 +119,19 @@ static void node_geo_exec(GeoNodeExecParams params)
return;
}
- const MeshComponent &mesh_component = *geometry_set.get_component_for_read<MeshComponent>();
- const int verts_num = mesh_component.attribute_domain_size(ATTR_DOMAIN_POINT);
- const int edges_num = mesh_component.attribute_domain_size(ATTR_DOMAIN_EDGE);
- if (verts_num == 0 || edges_num == 0) {
+ const Mesh &mesh = *geometry_set.get_mesh_for_read();
+ if (mesh.totvert == 0 || mesh.totedge == 0) {
return;
}
- GeometryComponentFieldContext point_context{mesh_component, ATTR_DOMAIN_POINT};
- FieldEvaluator point_evaluator(point_context, verts_num);
+ bke::MeshFieldContext point_context{mesh, ATTR_DOMAIN_POINT};
+ FieldEvaluator point_evaluator(point_context, mesh.totvert);
point_evaluator.add(vertex_crease_field);
point_evaluator.evaluate();
const VArray<float> vertex_creases = point_evaluator.get_evaluated<float>(0);
- GeometryComponentFieldContext edge_context{mesh_component, ATTR_DOMAIN_EDGE};
- FieldEvaluator edge_evaluator(edge_context, edges_num);
+ bke::MeshFieldContext edge_context{mesh, ATTR_DOMAIN_EDGE};
+ FieldEvaluator edge_evaluator(edge_context, mesh.totedge);
edge_evaluator.add(edge_crease_field);
edge_evaluator.evaluate();
const VArray<float> edge_creases = edge_evaluator.get_evaluated<float>(0);
@@ -162,17 +160,15 @@ static void node_geo_exec(GeoNodeExecParams params)
subdiv_settings.fvar_linear_interpolation = BKE_subdiv_fvar_interpolation_from_uv_smooth(
uv_smooth);
- const Mesh &mesh_in = *geometry_set.get_mesh_for_read();
-
/* Apply subdivision to mesh. */
- Subdiv *subdiv = BKE_subdiv_update_from_mesh(nullptr, &subdiv_settings, &mesh_in);
+ Subdiv *subdiv = BKE_subdiv_update_from_mesh(nullptr, &subdiv_settings, &mesh);
/* In case of bad topology, skip to input mesh. */
if (subdiv == nullptr) {
return;
}
- Mesh *mesh_out = BKE_subdiv_to_mesh(subdiv, &mesh_settings, &mesh_in);
+ Mesh *mesh_out = BKE_subdiv_to_mesh(subdiv, &mesh_settings, &mesh);
geometry_set.replace_mesh(mesh_out);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc
deleted file mode 100644
index cd75822f665..00000000000
--- a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc
+++ /dev/null
@@ -1,826 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-
-#include "BLI_generic_array.hh"
-#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 "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;
-
-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 eCustomDataType data_type = static_cast<eCustomDataType>(storage.data_type);
- const GeometryNodeAttributeTransferMode mapping = (GeometryNodeAttributeTransferMode)
- storage.mode;
-
- bNodeSocket *socket_geometry = (bNodeSocket *)node->inputs.first;
- bNodeSocket *socket_vector = socket_geometry->next;
- bNodeSocket *socket_float = socket_vector->next;
- bNodeSocket *socket_color4f = socket_float->next;
- bNodeSocket *socket_boolean = socket_color4f->next;
- bNodeSocket *socket_int32 = socket_boolean->next;
-
- bNodeSocket *socket_positions = socket_int32->next;
- bNodeSocket *socket_indices = socket_positions->next;
-
- nodeSetSocketAvailability(ntree, socket_vector, data_type == CD_PROP_FLOAT3);
- nodeSetSocketAvailability(ntree, socket_float, data_type == CD_PROP_FLOAT);
- nodeSetSocketAvailability(ntree, socket_color4f, data_type == CD_PROP_COLOR);
- nodeSetSocketAvailability(ntree, socket_boolean, data_type == CD_PROP_BOOL);
- nodeSetSocketAvailability(ntree, socket_int32, data_type == CD_PROP_INT32);
-
- nodeSetSocketAvailability(ntree, socket_positions, mapping != GEO_NODE_ATTRIBUTE_TRANSFER_INDEX);
- nodeSetSocketAvailability(ntree, socket_indices, mapping == GEO_NODE_ATTRIBUTE_TRANSFER_INDEX);
-
- bNodeSocket *out_socket_vector = (bNodeSocket *)node->outputs.first;
- bNodeSocket *out_socket_float = out_socket_vector->next;
- bNodeSocket *out_socket_color4f = out_socket_float->next;
- bNodeSocket *out_socket_boolean = out_socket_color4f->next;
- bNodeSocket *out_socket_int32 = out_socket_boolean->next;
-
- nodeSetSocketAvailability(ntree, out_socket_vector, data_type == CD_PROP_FLOAT3);
- nodeSetSocketAvailability(ntree, out_socket_float, data_type == CD_PROP_FLOAT);
- nodeSetSocketAvailability(ntree, out_socket_color4f, data_type == CD_PROP_COLOR);
- nodeSetSocketAvailability(ntree, out_socket_boolean, data_type == CD_PROP_BOOL);
- nodeSetSocketAvailability(ntree, out_socket_int32, data_type == CD_PROP_INT32);
-}
-
-static void node_gather_link_searches(GatherLinkSearchOpParams &params)
-{
- const NodeDeclaration &declaration = *params.node_type().fixed_declaration;
- search_link_ops_for_declarations(params, declaration.inputs().take_back(2));
- search_link_ops_for_declarations(params, declaration.inputs().take_front(1));
-
- const std::optional<eCustomDataType> type = node_data_type_to_custom_data_type(
- (eNodeSocketDatatype)params.other_socket().type);
- if (type && *type != CD_PROP_STRING) {
- /* The input and output sockets have the same name. */
- params.add_item(IFACE_("Attribute"), [type](LinkSearchOpParams &params) {
- bNode &node = params.add_node("GeometryNodeAttributeTransfer");
- node_storage(node).data_type = *type;
- params.update_and_connect_available_socket(node, "Attribute");
- });
- }
-}
-
-static void get_closest_in_bvhtree(BVHTreeFromMesh &tree_data,
- const VArray<float3> &positions,
- const IndexMask mask,
- const MutableSpan<int> r_indices,
- const MutableSpan<float> r_distances_sq,
- const MutableSpan<float3> r_positions)
-{
- BLI_assert(positions.size() >= r_indices.size());
- BLI_assert(positions.size() >= r_distances_sq.size());
- BLI_assert(positions.size() >= r_positions.size());
-
- for (const int i : mask) {
- BVHTreeNearest nearest;
- nearest.dist_sq = FLT_MAX;
- const float3 position = positions[i];
- BLI_bvhtree_find_nearest(
- tree_data.tree, position, &nearest, tree_data.nearest_callback, &tree_data);
- if (!r_indices.is_empty()) {
- r_indices[i] = nearest.index;
- }
- if (!r_distances_sq.is_empty()) {
- r_distances_sq[i] = nearest.dist_sq;
- }
- if (!r_positions.is_empty()) {
- r_positions[i] = nearest.co;
- }
- }
-}
-
-static void get_closest_pointcloud_points(const PointCloud &pointcloud,
- const VArray<float3> &positions,
- const IndexMask mask,
- const MutableSpan<int> r_indices,
- const MutableSpan<float> r_distances_sq)
-{
- BLI_assert(positions.size() >= r_indices.size());
- BLI_assert(pointcloud.totpoint > 0);
-
- BVHTreeFromPointCloud tree_data;
- BKE_bvhtree_from_pointcloud_get(&tree_data, &pointcloud, 2);
-
- for (const int i : mask) {
- BVHTreeNearest nearest;
- nearest.dist_sq = FLT_MAX;
- const float3 position = positions[i];
- BLI_bvhtree_find_nearest(
- tree_data.tree, position, &nearest, tree_data.nearest_callback, &tree_data);
- r_indices[i] = nearest.index;
- if (!r_distances_sq.is_empty()) {
- r_distances_sq[i] = nearest.dist_sq;
- }
- }
-
- free_bvhtree_from_pointcloud(&tree_data);
-}
-
-static void get_closest_mesh_points(const Mesh &mesh,
- const VArray<float3> &positions,
- const IndexMask mask,
- const MutableSpan<int> r_point_indices,
- const MutableSpan<float> r_distances_sq,
- const MutableSpan<float3> r_positions)
-{
- BLI_assert(mesh.totvert > 0);
- BVHTreeFromMesh tree_data;
- BKE_bvhtree_from_mesh_get(&tree_data, &mesh, BVHTREE_FROM_VERTS, 2);
- get_closest_in_bvhtree(tree_data, positions, mask, r_point_indices, r_distances_sq, r_positions);
- free_bvhtree_from_mesh(&tree_data);
-}
-
-static void get_closest_mesh_edges(const Mesh &mesh,
- const VArray<float3> &positions,
- const IndexMask mask,
- const MutableSpan<int> r_edge_indices,
- const MutableSpan<float> r_distances_sq,
- const MutableSpan<float3> r_positions)
-{
- BLI_assert(mesh.totedge > 0);
- BVHTreeFromMesh tree_data;
- BKE_bvhtree_from_mesh_get(&tree_data, &mesh, BVHTREE_FROM_EDGES, 2);
- get_closest_in_bvhtree(tree_data, positions, mask, r_edge_indices, r_distances_sq, r_positions);
- free_bvhtree_from_mesh(&tree_data);
-}
-
-static void get_closest_mesh_looptris(const Mesh &mesh,
- const VArray<float3> &positions,
- const IndexMask mask,
- const MutableSpan<int> r_looptri_indices,
- const MutableSpan<float> r_distances_sq,
- const MutableSpan<float3> r_positions)
-{
- BLI_assert(mesh.totpoly > 0);
- BVHTreeFromMesh tree_data;
- BKE_bvhtree_from_mesh_get(&tree_data, &mesh, BVHTREE_FROM_LOOPTRI, 2);
- get_closest_in_bvhtree(
- tree_data, positions, mask, r_looptri_indices, r_distances_sq, r_positions);
- free_bvhtree_from_mesh(&tree_data);
-}
-
-static void get_closest_mesh_polygons(const Mesh &mesh,
- const VArray<float3> &positions,
- const IndexMask mask,
- const MutableSpan<int> r_poly_indices,
- const MutableSpan<float> r_distances_sq,
- const MutableSpan<float3> r_positions)
-{
- BLI_assert(mesh.totpoly > 0);
-
- Array<int> looptri_indices(positions.size());
- get_closest_mesh_looptris(mesh, positions, mask, looptri_indices, r_distances_sq, r_positions);
-
- const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&mesh),
- BKE_mesh_runtime_looptri_len(&mesh)};
-
- for (const int i : mask) {
- const MLoopTri &looptri = looptris[looptri_indices[i]];
- r_poly_indices[i] = looptri.poly;
- }
-}
-
-/* The closest corner is defined to be the closest corner on the closest face. */
-static void get_closest_mesh_corners(const Mesh &mesh,
- const VArray<float3> &positions,
- const IndexMask mask,
- const MutableSpan<int> r_corner_indices,
- const MutableSpan<float> r_distances_sq,
- const MutableSpan<float3> r_positions)
-{
- BLI_assert(mesh.totloop > 0);
- Array<int> poly_indices(positions.size());
- get_closest_mesh_polygons(mesh, positions, mask, poly_indices, {}, {});
-
- for (const int i : mask) {
- const float3 position = positions[i];
- const int poly_index = poly_indices[i];
- const MPoly &poly = mesh.mpoly[poly_index];
-
- /* Find the closest vertex in the polygon. */
- float min_distance_sq = FLT_MAX;
- const MVert *closest_mvert;
- int closest_loop_index = 0;
- for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
- const MLoop &loop = mesh.mloop[loop_index];
- const int vertex_index = loop.v;
- const MVert &mvert = mesh.mvert[vertex_index];
- const float distance_sq = math::distance_squared(position, float3(mvert.co));
- if (distance_sq < min_distance_sq) {
- min_distance_sq = distance_sq;
- closest_loop_index = loop_index;
- closest_mvert = &mvert;
- }
- }
- if (!r_corner_indices.is_empty()) {
- r_corner_indices[i] = closest_loop_index;
- }
- if (!r_positions.is_empty()) {
- r_positions[i] = closest_mvert->co;
- }
- if (!r_distances_sq.is_empty()) {
- r_distances_sq[i] = min_distance_sq;
- }
- }
-}
-
-template<typename T>
-void copy_with_indices(const VArray<T> &src,
- const IndexMask mask,
- const Span<int> indices,
- const MutableSpan<T> dst)
-{
- if (src.is_empty()) {
- return;
- }
- for (const int i : mask) {
- dst[i] = src[indices[i]];
- }
-}
-
-template<typename T>
-void copy_with_indices_clamped(const VArray<T> &src,
- const IndexMask mask,
- const VArray<int> &indices,
- const MutableSpan<T> dst)
-{
- if (src.is_empty()) {
- return;
- }
- const int max_index = src.size() - 1;
- threading::parallel_for(mask.index_range(), 4096, [&](IndexRange range) {
- for (const int i : range) {
- const int index = mask[i];
- dst[index] = src[std::clamp(indices[index], 0, max_index)];
- }
- });
-}
-
-template<typename T>
-void copy_with_indices_and_comparison(const VArray<T> &src_1,
- const VArray<T> &src_2,
- const Span<float> distances_1,
- const Span<float> distances_2,
- const IndexMask mask,
- const Span<int> indices_1,
- const Span<int> indices_2,
- const MutableSpan<T> dst)
-{
- if (src_1.is_empty() || src_2.is_empty()) {
- return;
- }
- for (const int i : mask) {
- if (distances_1[i] < distances_2[i]) {
- dst[i] = src_1[indices_1[i]];
- }
- else {
- dst[i] = src_2[indices_2[i]];
- }
- }
-}
-
-static bool component_is_available(const GeometrySet &geometry,
- const GeometryComponentType type,
- const eAttrDomain 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 source geometry once and store
- * it for later.
- */
-class NearestInterpolatedTransferFunction : public fn::MultiFunction {
- GeometrySet source_;
- 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.
- */
- eAttrDomain domain_ = ATTR_DOMAIN_CORNER;
-
- fn::MFSignature signature_;
-
- std::optional<GeometryComponentFieldContext> source_context_;
- std::unique_ptr<FieldEvaluator> source_evaluator_;
- const GVArray *source_data_;
-
- public:
- NearestInterpolatedTransferFunction(GeometrySet geometry, GField src_field)
- : source_(std::move(geometry)), src_field_(std::move(src_field))
- {
- source_.ensure_owns_direct_data();
- signature_ = this->create_signature();
- this->set_signature(&signature_);
- this->evaluate_source_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 = *source_.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(*source_data_, domain_, eAttributeMapMode::INTERPOLATED, dst);
- }
-
- private:
- void evaluate_source_field()
- {
- const MeshComponent &mesh_component = *source_.get_component_for_read<MeshComponent>();
- source_context_.emplace(GeometryComponentFieldContext{mesh_component, domain_});
- const int domain_num = mesh_component.attribute_domain_size(domain_);
- source_evaluator_ = std::make_unique<FieldEvaluator>(*source_context_, domain_num);
- source_evaluator_->add(src_field_);
- source_evaluator_->evaluate();
- source_data_ = &source_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 source geometry once and store
- * it for later.
- */
-class NearestTransferFunction : public fn::MultiFunction {
- GeometrySet source_;
- GField src_field_;
- eAttrDomain domain_;
-
- fn::MFSignature signature_;
-
- bool use_mesh_;
- bool use_points_;
-
- /* Store data from the source as a virtual array, since we may only access a few indices. */
- std::optional<GeometryComponentFieldContext> mesh_context_;
- std::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, eAttrDomain domain)
- : source_(std::move(geometry)), src_field_(std::move(src_field)), domain_(domain)
- {
- source_.ensure_owns_direct_data();
- signature_ = this->create_signature();
- this->set_signature(&signature_);
-
- this->use_mesh_ = component_is_available(source_, GEO_COMPONENT_TYPE_MESH, domain_);
- this->use_points_ = component_is_available(source_, GEO_COMPONENT_TYPE_POINT_CLOUD, domain_);
-
- this->evaluate_source_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().value_initialize_indices(dst.data(), mask);
- return;
- }
-
- const Mesh *mesh = use_mesh_ ? source_.get_mesh_for_read() : nullptr;
- const PointCloud *pointcloud = use_points_ ? source_.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_source_field()
- {
- if (use_mesh_) {
- const MeshComponent &mesh = *source_.get_component_for_read<MeshComponent>();
- const int domain_num = mesh.attribute_domain_size(domain_);
- mesh_context_.emplace(GeometryComponentFieldContext(mesh, domain_));
- mesh_evaluator_ = std::make_unique<FieldEvaluator>(*mesh_context_, domain_num);
- mesh_evaluator_->add(src_field_);
- mesh_evaluator_->evaluate();
- mesh_data_ = &mesh_evaluator_->get_evaluated(0);
- }
-
- if (use_points_) {
- const PointCloudComponent &points = *source_.get_component_for_read<PointCloudComponent>();
- const int domain_num = points.attribute_domain_size(domain_);
- point_context_.emplace(GeometryComponentFieldContext(points, domain_));
- point_evaluator_ = std::make_unique<FieldEvaluator>(*point_context_, domain_num);
- point_evaluator_->add(src_field_);
- point_evaluator_->evaluate();
- point_data_ = &point_evaluator_->get_evaluated(0);
- }
- }
-};
-
-static const GeometryComponent *find_source_component(const GeometrySet &geometry,
- const eAttrDomain 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 source. A future optimization could be removing that limitation
- * internally.
- */
-class IndexTransferFunction : public fn::MultiFunction {
- GeometrySet src_geometry_;
- GField src_field_;
- eAttrDomain 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 eAttrDomain 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_source_component(src_geometry_, domain_);
- if (component == nullptr) {
- return;
- }
- const int domain_num = component->attribute_domain_size(domain_);
- geometry_context_.emplace(GeometryComponentFieldContext(*component, domain_));
- evaluator_ = std::make_unique<FieldEvaluator>(*geometry_context_, domain_num);
- 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.value_initialize_indices(dst.data(), mask);
- return;
- }
-
- attribute_math::convert_to_static_type(type, [&](auto dummy) {
- using T = decltype(dummy);
- copy_with_indices_clamped(src_data_->typed<T>(), mask, indices, dst.typed<T>());
- });
- }
-};
-
-static GField get_input_attribute_field(GeoNodeExecParams &params, const eCustomDataType data_type)
-{
- switch (data_type) {
- case CD_PROP_FLOAT:
- return params.extract_input<Field<float>>("Attribute_001");
- case CD_PROP_FLOAT3:
- return params.extract_input<Field<float3>>("Attribute");
- case CD_PROP_COLOR:
- return params.extract_input<Field<ColorGeometry4f>>("Attribute_002");
- case CD_PROP_BOOL:
- return params.extract_input<Field<bool>>("Attribute_003");
- case CD_PROP_INT32:
- return params.extract_input<Field<int>>("Attribute_004");
- default:
- BLI_assert_unreachable();
- }
- return {};
-}
-
-static void output_attribute_field(GeoNodeExecParams &params, GField field)
-{
- switch (bke::cpp_type_to_custom_data_type(field.cpp_type())) {
- case CD_PROP_FLOAT: {
- params.set_output("Attribute_001", Field<float>(field));
- break;
- }
- case CD_PROP_FLOAT3: {
- params.set_output("Attribute", Field<float3>(field));
- break;
- }
- case CD_PROP_COLOR: {
- params.set_output("Attribute_002", Field<ColorGeometry4f>(field));
- break;
- }
- case CD_PROP_BOOL: {
- params.set_output("Attribute_003", Field<bool>(field));
- break;
- }
- case CD_PROP_INT32: {
- params.set_output("Attribute_004", Field<int>(field));
- break;
- }
- default:
- break;
- }
-}
-
-static void node_geo_exec(GeoNodeExecParams params)
-{
- GeometrySet geometry = params.extract_input<GeometrySet>("Source");
- const NodeGeometryTransferAttribute &storage = node_storage(params.node());
- const GeometryNodeAttributeTransferMode mapping = (GeometryNodeAttributeTransferMode)
- storage.mode;
- const eCustomDataType data_type = static_cast<eCustomDataType>(storage.data_type);
- const eAttrDomain domain = static_cast<eAttrDomain>(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 source 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 source 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_curves() && !geometry.has_mesh() && !geometry.has_pointcloud()) {
- params.error_message_add(NodeWarningType::Error,
- TIP_("The source 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 945d5fbdcac..4130cad3bda 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_transform.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_transform.cc
@@ -47,7 +47,7 @@ static void transform_mesh(Mesh &mesh, const float4x4 &transform)
static void translate_pointcloud(PointCloud &pointcloud, const float3 translation)
{
- MutableAttributeAccessor attributes = bke::pointcloud_attributes_for_write(pointcloud);
+ MutableAttributeAccessor attributes = pointcloud.attributes_for_write();
SpanAttributeWriter position = attributes.lookup_or_add_for_write_span<float3>(
"position", ATTR_DOMAIN_POINT);
for (const int i : position.span.index_range()) {
@@ -58,7 +58,7 @@ static void translate_pointcloud(PointCloud &pointcloud, const float3 translatio
static void transform_pointcloud(PointCloud &pointcloud, const float4x4 &transform)
{
- MutableAttributeAccessor attributes = bke::pointcloud_attributes_for_write(pointcloud);
+ MutableAttributeAccessor attributes = pointcloud.attributes_for_write();
SpanAttributeWriter position = attributes.lookup_or_add_for_write_span<float3>(
"position", ATTR_DOMAIN_POINT);
for (const int i : position.span.index_range()) {
@@ -83,44 +83,61 @@ static void transform_instances(InstancesComponent &instances, const float4x4 &t
}
}
-static void transform_volume(Volume &volume, const float4x4 &transform, const Depsgraph &depsgraph)
+static void transform_volume(GeoNodeExecParams &params,
+ Volume &volume,
+ const float4x4 &transform,
+ const Depsgraph &depsgraph)
{
#ifdef WITH_OPENVDB
- /* Scaling an axis to zero is not supported for volumes. */
- const float3 translation = transform.translation();
- const float3 rotation = transform.to_euler();
- const float3 scale = transform.scale();
- const float3 limited_scale = {
- (scale.x == 0.0f) ? FLT_EPSILON : scale.x,
- (scale.y == 0.0f) ? FLT_EPSILON : scale.y,
- (scale.z == 0.0f) ? FLT_EPSILON : scale.z,
- };
- const float4x4 scale_limited_transform = float4x4::from_loc_eul_scale(
- translation, rotation, limited_scale);
-
const Main *bmain = DEG_get_bmain(&depsgraph);
BKE_volume_load(&volume, bmain);
openvdb::Mat4s vdb_matrix;
- memcpy(vdb_matrix.asPointer(), &scale_limited_transform, sizeof(float[4][4]));
+ memcpy(vdb_matrix.asPointer(), &transform, sizeof(float[4][4]));
openvdb::Mat4d vdb_matrix_d{vdb_matrix};
+ bool found_too_small_scale = false;
const int grids_num = BKE_volume_num_grids(&volume);
for (const int i : IndexRange(grids_num)) {
VolumeGrid *volume_grid = BKE_volume_grid_get_for_write(&volume, i);
-
- openvdb::GridBase::Ptr grid = BKE_volume_grid_openvdb_for_write(&volume, volume_grid, false);
- openvdb::math::Transform &grid_transform = grid->transform();
- grid_transform.postMult(vdb_matrix_d);
+ float4x4 grid_matrix;
+ BKE_volume_grid_transform_matrix(volume_grid, grid_matrix.values);
+ mul_m4_m4_pre(grid_matrix.values, transform.values);
+ const float determinant = determinant_m4(grid_matrix.values);
+ if (!BKE_volume_grid_determinant_valid(determinant)) {
+ found_too_small_scale = true;
+ /* Clear the tree because it is too small. */
+ BKE_volume_grid_clear_tree(volume, *volume_grid);
+ if (determinant == 0) {
+ /* Reset rotation and scale. */
+ copy_v3_fl3(grid_matrix.values[0], 1, 0, 0);
+ copy_v3_fl3(grid_matrix.values[1], 0, 1, 0);
+ copy_v3_fl3(grid_matrix.values[2], 0, 0, 1);
+ }
+ else {
+ /* Keep rotation but reset scale. */
+ normalize_v3(grid_matrix.values[0]);
+ normalize_v3(grid_matrix.values[1]);
+ normalize_v3(grid_matrix.values[2]);
+ }
+ }
+ BKE_volume_grid_transform_matrix_set(volume_grid, grid_matrix.values);
+ }
+ if (found_too_small_scale) {
+ params.error_message_add(NodeWarningType::Warning,
+ TIP_("Volume scale is lower than permitted by OpenVDB"));
}
#else
- UNUSED_VARS(volume, transform, depsgraph);
+ UNUSED_VARS(params, volume, transform, depsgraph);
#endif
}
-static void translate_volume(Volume &volume, const float3 translation, const Depsgraph &depsgraph)
+static void translate_volume(GeoNodeExecParams &params,
+ Volume &volume,
+ const float3 translation,
+ const Depsgraph &depsgraph)
{
- transform_volume(volume, float4x4::from_location(translation), depsgraph);
+ transform_volume(params, volume, float4x4::from_location(translation), depsgraph);
}
static void transform_curve_edit_hints(bke::CurvesEditHints &edit_hints, const float4x4 &transform)
@@ -151,7 +168,8 @@ static void translate_curve_edit_hints(bke::CurvesEditHints &edit_hints, const f
}
}
-static void translate_geometry_set(GeometrySet &geometry,
+static void translate_geometry_set(GeoNodeExecParams &params,
+ GeometrySet &geometry,
const float3 translation,
const Depsgraph &depsgraph)
{
@@ -165,7 +183,7 @@ static void translate_geometry_set(GeometrySet &geometry,
translate_pointcloud(*pointcloud, translation);
}
if (Volume *volume = geometry.get_volume_for_write()) {
- translate_volume(*volume, translation, depsgraph);
+ translate_volume(params, *volume, translation, depsgraph);
}
if (geometry.has_instances()) {
translate_instances(geometry.get_component_for_write<InstancesComponent>(), translation);
@@ -175,7 +193,8 @@ static void translate_geometry_set(GeometrySet &geometry,
}
}
-void transform_geometry_set(GeometrySet &geometry,
+void transform_geometry_set(GeoNodeExecParams &params,
+ GeometrySet &geometry,
const float4x4 &transform,
const Depsgraph &depsgraph)
{
@@ -189,7 +208,7 @@ void transform_geometry_set(GeometrySet &geometry,
transform_pointcloud(*pointcloud, transform);
}
if (Volume *volume = geometry.get_volume_for_write()) {
- transform_volume(*volume, transform, depsgraph);
+ transform_volume(params, *volume, transform, depsgraph);
}
if (geometry.has_instances()) {
transform_instances(geometry.get_component_for_write<InstancesComponent>(), transform);
@@ -230,10 +249,11 @@ static void node_geo_exec(GeoNodeExecParams params)
/* Use only translation if rotation and scale don't apply. */
if (use_translate(rotation, scale)) {
- translate_geometry_set(geometry_set, translation, *params.depsgraph());
+ translate_geometry_set(params, geometry_set, translation, *params.depsgraph());
}
else {
- transform_geometry_set(geometry_set,
+ transform_geometry_set(params,
+ geometry_set,
float4x4::from_loc_eul_scale(translation, rotation, scale),
*params.depsgraph());
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc
index ae538072e65..3e9fe99adb0 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc
@@ -17,9 +17,8 @@ static void node_declare(NodeDeclarationBuilder &b)
static void translate_instances(GeoNodeExecParams &params, InstancesComponent &instances_component)
{
- GeometryComponentFieldContext field_context{instances_component, ATTR_DOMAIN_INSTANCE};
-
- fn::FieldEvaluator evaluator{field_context, instances_component.instances_num()};
+ const bke::InstancesFieldContext context{instances_component};
+ fn::FieldEvaluator evaluator{context, instances_component.instances_num()};
evaluator.set_selection(params.extract_input<Field<bool>>("Selection"));
evaluator.add(params.extract_input<Field<float3>>("Translation"));
evaluator.add(params.extract_input<Field<bool>>("Local Space"));
diff --git a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc
index 992470e8279..57487059437 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc
@@ -47,9 +47,6 @@ static Mesh *triangulate_mesh_selection(const Mesh &mesh,
BMeshFromMeshParams from_mesh_params{};
from_mesh_params.calc_face_normal = true;
from_mesh_params.calc_vert_normal = true;
- from_mesh_params.add_key_index = true;
- from_mesh_params.use_shapekey = true;
- from_mesh_params.active_shapekey = 1;
from_mesh_params.cd_mask_extra = cd_mask_extra;
BMesh *bm = BKE_mesh_to_bmesh_ex(&mesh, &create_params, &from_mesh_params);
@@ -80,12 +77,10 @@ static void node_geo_exec(GeoNodeExecParams params)
if (!geometry_set.has_mesh()) {
return;
}
- GeometryComponent &component = geometry_set.get_component_for_write<MeshComponent>();
const Mesh &mesh_in = *geometry_set.get_mesh_for_read();
- const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE);
- GeometryComponentFieldContext context{component, ATTR_DOMAIN_FACE};
- FieldEvaluator evaluator{context, domain_size};
+ bke::MeshFieldContext context{mesh_in, ATTR_DOMAIN_FACE};
+ FieldEvaluator evaluator{context, mesh_in.totpoly};
evaluator.add(selection_field);
evaluator.evaluate();
const IndexMask selection = evaluator.get_evaluated_as_mask(0);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_uv_pack_islands.cc b/source/blender/nodes/geometry/nodes/node_geo_uv_pack_islands.cc
index 17413e64f7d..ccb489f6e29 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_uv_pack_islands.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_uv_pack_islands.cc
@@ -5,6 +5,8 @@
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
+#include "BKE_mesh.h"
+
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_uv_pack_islands_cc {
@@ -28,21 +30,19 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Vector>(N_("UV")).field_source();
}
-static VArray<float3> construct_uv_gvarray(const MeshComponent &component,
+static VArray<float3> construct_uv_gvarray(const Mesh &mesh,
const Field<bool> selection_field,
const Field<float3> uv_field,
const bool rotate,
const float margin,
const eAttrDomain domain)
{
- const Mesh *mesh = component.get_for_read();
- if (mesh == nullptr) {
- return {};
- }
+ const Span<MVert> verts = mesh.verts();
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
- const int face_num = component.attribute_domain_size(ATTR_DOMAIN_FACE);
- GeometryComponentFieldContext face_context{component, ATTR_DOMAIN_FACE};
- FieldEvaluator face_evaluator{face_context, face_num};
+ bke::MeshFieldContext face_context{mesh, ATTR_DOMAIN_FACE};
+ FieldEvaluator face_evaluator{face_context, polys.size()};
face_evaluator.add(selection_field);
face_evaluator.evaluate();
const IndexMask selection = face_evaluator.get_evaluated_as_mask(0);
@@ -50,25 +50,24 @@ static VArray<float3> construct_uv_gvarray(const MeshComponent &component,
return {};
}
- const int corner_num = component.attribute_domain_size(ATTR_DOMAIN_CORNER);
- GeometryComponentFieldContext corner_context{component, ATTR_DOMAIN_CORNER};
- FieldEvaluator evaluator{corner_context, corner_num};
- Array<float3> uv(corner_num);
+ bke::MeshFieldContext corner_context{mesh, ATTR_DOMAIN_CORNER};
+ FieldEvaluator evaluator{corner_context, mesh.totloop};
+ Array<float3> uv(mesh.totloop);
evaluator.add_with_destination(uv_field, uv.as_mutable_span());
evaluator.evaluate();
ParamHandle *handle = GEO_uv_parametrizer_construct_begin();
for (const int mp_index : selection) {
- const MPoly &mp = mesh->mpoly[mp_index];
+ const MPoly &mp = polys[mp_index];
Array<ParamKey, 16> mp_vkeys(mp.totloop);
Array<bool, 16> mp_pin(mp.totloop);
Array<bool, 16> mp_select(mp.totloop);
Array<const float *, 16> mp_co(mp.totloop);
Array<float *, 16> mp_uv(mp.totloop);
for (const int i : IndexRange(mp.totloop)) {
- const MLoop &ml = mesh->mloop[mp.loopstart + i];
+ const MLoop &ml = loops[mp.loopstart + i];
mp_vkeys[i] = ml.v;
- mp_co[i] = mesh->mvert[ml.v].co;
+ mp_co[i] = verts[ml.v].co;
mp_uv[i] = uv[mp.loopstart + i];
mp_pin[i] = false;
mp_select[i] = false;
@@ -88,11 +87,11 @@ static VArray<float3> construct_uv_gvarray(const MeshComponent &component,
GEO_uv_parametrizer_flush(handle);
GEO_uv_parametrizer_delete(handle);
- return component.attributes()->adapt_domain<float3>(
+ return mesh.attributes().adapt_domain<float3>(
VArray<float3>::ForContainer(std::move(uv)), ATTR_DOMAIN_CORNER, domain);
}
-class PackIslandsFieldInput final : public GeometryFieldInput {
+class PackIslandsFieldInput final : public bke::MeshFieldInput {
private:
const Field<bool> selection_field;
const Field<float3> uv_field;
@@ -104,7 +103,7 @@ class PackIslandsFieldInput final : public GeometryFieldInput {
const Field<float3> uv_field,
const bool rotate,
const float margin)
- : GeometryFieldInput(CPPType::get<float3>(), "Pack UV Islands Field"),
+ : bke::MeshFieldInput(CPPType::get<float3>(), "Pack UV Islands Field"),
selection_field(selection_field),
uv_field(uv_field),
rotate(rotate),
@@ -113,16 +112,11 @@ class PackIslandsFieldInput final : public GeometryFieldInput {
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const Mesh &mesh,
const eAttrDomain domain,
IndexMask UNUSED(mask)) const final
{
- if (component.type() == GEO_COMPONENT_TYPE_MESH) {
- const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
- return construct_uv_gvarray(
- mesh_component, selection_field, uv_field, rotate, margin, domain);
- }
- return {};
+ return construct_uv_gvarray(mesh, selection_field, uv_field, rotate, margin, domain);
}
};
diff --git a/source/blender/nodes/geometry/nodes/node_geo_uv_unwrap.cc b/source/blender/nodes/geometry/nodes/node_geo_uv_unwrap.cc
index 03657f3e016..801bc3f4642 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_uv_unwrap.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_uv_unwrap.cc
@@ -5,6 +5,8 @@
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
+#include "BKE_mesh.h"
+
#include "UI_interface.h"
#include "UI_resources.h"
@@ -52,7 +54,7 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node)
node->storage = data;
}
-static VArray<float3> construct_uv_gvarray(const MeshComponent &component,
+static VArray<float3> construct_uv_gvarray(const Mesh &mesh,
const Field<bool> selection_field,
const Field<bool> seam_field,
const bool fill_holes,
@@ -60,14 +62,13 @@ static VArray<float3> construct_uv_gvarray(const MeshComponent &component,
const GeometryNodeUVUnwrapMethod method,
const eAttrDomain domain)
{
- const Mesh *mesh = component.get_for_read();
- if (mesh == nullptr) {
- return {};
- }
+ const Span<MVert> verts = mesh.verts();
+ const Span<MEdge> edges = mesh.edges();
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
- const int face_num = component.attribute_domain_size(ATTR_DOMAIN_FACE);
- GeometryComponentFieldContext face_context{component, ATTR_DOMAIN_FACE};
- FieldEvaluator face_evaluator{face_context, face_num};
+ bke::MeshFieldContext face_context{mesh, ATTR_DOMAIN_FACE};
+ FieldEvaluator face_evaluator{face_context, polys.size()};
face_evaluator.add(selection_field);
face_evaluator.evaluate();
const IndexMask selection = face_evaluator.get_evaluated_as_mask(0);
@@ -75,27 +76,26 @@ static VArray<float3> construct_uv_gvarray(const MeshComponent &component,
return {};
}
- const int edge_num = component.attribute_domain_size(ATTR_DOMAIN_EDGE);
- GeometryComponentFieldContext edge_context{component, ATTR_DOMAIN_EDGE};
- FieldEvaluator edge_evaluator{edge_context, edge_num};
+ bke::MeshFieldContext edge_context{mesh, ATTR_DOMAIN_EDGE};
+ FieldEvaluator edge_evaluator{edge_context, edges.size()};
edge_evaluator.add(seam_field);
edge_evaluator.evaluate();
const IndexMask seam = edge_evaluator.get_evaluated_as_mask(0);
- Array<float3> uv(mesh->totloop, float3(0));
+ Array<float3> uv(loops.size(), float3(0));
ParamHandle *handle = GEO_uv_parametrizer_construct_begin();
for (const int mp_index : selection) {
- const MPoly &mp = mesh->mpoly[mp_index];
+ const MPoly &mp = polys[mp_index];
Array<ParamKey, 16> mp_vkeys(mp.totloop);
Array<bool, 16> mp_pin(mp.totloop);
Array<bool, 16> mp_select(mp.totloop);
Array<const float *, 16> mp_co(mp.totloop);
Array<float *, 16> mp_uv(mp.totloop);
for (const int i : IndexRange(mp.totloop)) {
- const MLoop &ml = mesh->mloop[mp.loopstart + i];
+ const MLoop &ml = loops[mp.loopstart + i];
mp_vkeys[i] = ml.v;
- mp_co[i] = mesh->mvert[ml.v].co;
+ mp_co[i] = verts[ml.v].co;
mp_uv[i] = uv[mp.loopstart + i];
mp_pin[i] = false;
mp_select[i] = false;
@@ -110,7 +110,7 @@ static VArray<float3> construct_uv_gvarray(const MeshComponent &component,
mp_select.data());
}
for (const int i : seam) {
- const MEdge &edge = mesh->medge[i];
+ const MEdge &edge = edges[i];
ParamKey vkeys[2]{edge.v1, edge.v2};
GEO_uv_parametrizer_edge_set_seam(handle, vkeys);
}
@@ -126,11 +126,11 @@ static VArray<float3> construct_uv_gvarray(const MeshComponent &component,
GEO_uv_parametrizer_flush(handle);
GEO_uv_parametrizer_delete(handle);
- return component.attributes()->adapt_domain<float3>(
+ return mesh.attributes().adapt_domain<float3>(
VArray<float3>::ForContainer(std::move(uv)), ATTR_DOMAIN_CORNER, domain);
}
-class UnwrapFieldInput final : public GeometryFieldInput {
+class UnwrapFieldInput final : public bke::MeshFieldInput {
private:
const Field<bool> selection;
const Field<bool> seam;
@@ -144,7 +144,7 @@ class UnwrapFieldInput final : public GeometryFieldInput {
const bool fill_holes,
const float margin,
const GeometryNodeUVUnwrapMethod method)
- : GeometryFieldInput(CPPType::get<float3>(), "UV Unwrap Field"),
+ : bke::MeshFieldInput(CPPType::get<float3>(), "UV Unwrap Field"),
selection(selection),
seam(seam),
fill_holes(fill_holes),
@@ -154,16 +154,11 @@ class UnwrapFieldInput final : public GeometryFieldInput {
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const Mesh &mesh,
const eAttrDomain domain,
IndexMask UNUSED(mask)) const final
{
- if (component.type() == GEO_COMPONENT_TYPE_MESH) {
- const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
- return construct_uv_gvarray(
- mesh_component, selection, seam, fill_holes, margin, method, domain);
- }
- return {};
+ return construct_uv_gvarray(mesh, selection, seam, fill_holes, margin, method, domain);
}
};
diff --git a/source/blender/nodes/geometry/nodes/node_geo_volume_cube.cc b/source/blender/nodes/geometry/nodes/node_geo_volume_cube.cc
index d7e9e38ea0d..c102b91acb1 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_volume_cube.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_volume_cube.cc
@@ -113,9 +113,9 @@ class Grid3DFieldContext : public FieldContext {
}
};
-#ifdef WITH_OPENVDB
static void node_geo_exec(GeoNodeExecParams params)
{
+#ifdef WITH_OPENVDB
const float3 bounds_min = params.extract_input<float3>("Min");
const float3 bounds_max = params.extract_input<float3>("Max");
@@ -137,6 +137,14 @@ static void node_geo_exec(GeoNodeExecParams params)
return;
}
+ const double3 scale_fac = double3(bounds_max - bounds_min) / double3(resolution - 1);
+ if (!BKE_volume_grid_determinant_valid(scale_fac.x * scale_fac.y * scale_fac.z)) {
+ params.error_message_add(NodeWarningType::Warning,
+ TIP_("Volume scale is lower than permitted by OpenVDB"));
+ params.set_default_remaining_outputs();
+ return;
+ }
+
Field<float> input_field = params.extract_input<Field<float>>("Density");
/* Evaluate input field on a 3D grid. */
@@ -157,8 +165,7 @@ static void node_geo_exec(GeoNodeExecParams params)
openvdb::tools::copyFromDense(dense_grid, *grid, 0.0f);
grid->transform().preTranslate(openvdb::math::Vec3<float>(-0.5f));
- const float3 scale_fac = (bounds_max - bounds_min) / float3(resolution - 1);
- grid->transform().postScale(openvdb::math::Vec3<float>(scale_fac.x, scale_fac.y, scale_fac.z));
+ grid->transform().postScale(openvdb::math::Vec3<double>(scale_fac.x, scale_fac.y, scale_fac.z));
grid->transform().postTranslate(
openvdb::math::Vec3<float>(bounds_min.x, bounds_min.y, bounds_min.z));
@@ -170,16 +177,12 @@ static void node_geo_exec(GeoNodeExecParams params)
GeometrySet r_geometry_set;
r_geometry_set.replace_volume(volume);
params.set_output("Volume", r_geometry_set);
-}
-
#else
-static void node_geo_exec(GeoNodeExecParams params)
-{
+ params.set_default_remaining_outputs();
params.error_message_add(NodeWarningType::Error,
TIP_("Disabled, Blender was compiled without OpenVDB"));
- params.set_default_remaining_outputs();
+#endif
}
-#endif /* WITH_OPENVDB */
} // namespace blender::nodes::node_geo_volume_cube_cc
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 91429560ac8..763e207b388 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
@@ -123,9 +123,9 @@ static Mesh *create_mesh_from_volume_grids(Span<openvdb::GridBase::ConstPtr> gri
Mesh *mesh = BKE_mesh_new_nomain(vert_offset, 0, 0, loop_offset, poly_offset);
BKE_id_material_eval_ensure_default_slot(&mesh->id);
- MutableSpan<MVert> verts{mesh->mvert, mesh->totvert};
- MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop};
- MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly};
+ MutableSpan<MVert> verts = mesh->verts_for_write();
+ MutableSpan<MPoly> polys = mesh->polys_for_write();
+ MutableSpan<MLoop> loops = mesh->loops_for_write();
for (const int i : grids.index_range()) {
const bke::OpenVDBMeshData &data = mesh_data[i];
@@ -187,20 +187,19 @@ static Mesh *create_mesh_from_volume(GeometrySet &geometry_set, GeoNodeExecParam
static void node_geo_exec(GeoNodeExecParams params)
{
- GeometrySet geometry_set = params.extract_input<GeometrySet>("Volume");
-
#ifdef WITH_OPENVDB
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Volume");
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_during_modify({GEO_COMPONENT_TYPE_MESH});
});
+ params.set_output("Mesh", std::move(geometry_set));
#else
+ params.set_default_remaining_outputs();
params.error_message_add(NodeWarningType::Error,
TIP_("Disabled, Blender was compiled without OpenVDB"));
#endif
-
- params.set_output("Mesh", std::move(geometry_set));
}
} // namespace blender::nodes::node_geo_volume_to_mesh_cc
diff --git a/source/blender/nodes/intern/derived_node_tree.cc b/source/blender/nodes/intern/derived_node_tree.cc
index e589da09b16..2ea80008af8 100644
--- a/source/blender/nodes/intern/derived_node_tree.cc
+++ b/source/blender/nodes/intern/derived_node_tree.cc
@@ -2,38 +2,38 @@
#include "NOD_derived_node_tree.hh"
+#include "BKE_node.h"
+
#include "BLI_dot_export.hh"
namespace blender::nodes {
-DerivedNodeTree::DerivedNodeTree(bNodeTree &btree, NodeTreeRefMap &node_tree_refs)
+DerivedNodeTree::DerivedNodeTree(const bNodeTree &btree)
{
/* Construct all possible contexts immediately. This is significantly cheaper than inlining all
* node groups. If it still becomes a performance issue in the future, contexts could be
* constructed lazily when they are needed. */
- root_context_ = &this->construct_context_recursively(nullptr, nullptr, btree, node_tree_refs);
+ root_context_ = &this->construct_context_recursively(nullptr, nullptr, btree);
}
DTreeContext &DerivedNodeTree::construct_context_recursively(DTreeContext *parent_context,
- const NodeRef *parent_node,
- bNodeTree &btree,
- NodeTreeRefMap &node_tree_refs)
+ const bNode *parent_node,
+ const bNodeTree &btree)
{
+ btree.ensure_topology_cache();
DTreeContext &context = *allocator_.construct<DTreeContext>().release();
context.parent_context_ = parent_context;
context.parent_node_ = parent_node;
context.derived_tree_ = this;
- context.tree_ = &get_tree_ref_from_map(node_tree_refs, btree);
- used_node_tree_refs_.add(context.tree_);
+ context.btree_ = &btree;
+ used_btrees_.add(context.btree_);
- for (const NodeRef *node : context.tree_->nodes()) {
- if (node->is_group_node()) {
- bNode *bnode = node->bnode();
+ for (const bNode *bnode : context.btree_->all_nodes()) {
+ if (bnode->is_group()) {
bNodeTree *child_btree = reinterpret_cast<bNodeTree *>(bnode->id);
if (child_btree != nullptr) {
- DTreeContext &child = this->construct_context_recursively(
- &context, node, *child_btree, node_tree_refs);
- context.children_.add_new(node, &child);
+ DTreeContext &child = this->construct_context_recursively(&context, bnode, *child_btree);
+ context.children_.add_new(bnode, &child);
}
}
}
@@ -57,8 +57,8 @@ void DerivedNodeTree::destruct_context_recursively(DTreeContext *context)
bool DerivedNodeTree::has_link_cycles() const
{
- for (const NodeTreeRef *tree_ref : used_node_tree_refs_) {
- if (tree_ref->has_link_cycles()) {
+ for (const bNodeTree *btree : used_btrees_) {
+ if (btree->has_available_link_cycle()) {
return true;
}
}
@@ -67,8 +67,8 @@ bool DerivedNodeTree::has_link_cycles() const
bool DerivedNodeTree::has_undefined_nodes_or_sockets() const
{
- for (const NodeTreeRef *tree_ref : used_node_tree_refs_) {
- if (tree_ref->has_undefined_nodes_or_sockets()) {
+ for (const bNodeTree *btree : used_btrees_) {
+ if (btree->has_undefined_nodes_or_sockets()) {
return true;
}
}
@@ -83,8 +83,8 @@ void DerivedNodeTree::foreach_node(FunctionRef<void(DNode)> callback) const
void DerivedNodeTree::foreach_node_in_context_recursive(const DTreeContext &context,
FunctionRef<void(DNode)> callback) const
{
- for (const NodeRef *node_ref : context.tree_->nodes()) {
- callback(DNode(&context, node_ref));
+ for (const bNode *bnode : context.btree_->all_nodes()) {
+ callback(DNode(&context, bnode));
}
for (const DTreeContext *child_context : context.children_.values()) {
this->foreach_node_in_context_recursive(*child_context, callback);
@@ -94,32 +94,32 @@ void DerivedNodeTree::foreach_node_in_context_recursive(const DTreeContext &cont
DOutputSocket DInputSocket::get_corresponding_group_node_output() const
{
BLI_assert(*this);
- BLI_assert(socket_ref_->node().is_group_output_node());
- BLI_assert(socket_ref_->index() < socket_ref_->node().inputs().size() - 1);
+ BLI_assert(bsocket_->owner_node().is_group_output());
+ BLI_assert(bsocket_->index() < bsocket_->owner_node().input_sockets().size() - 1);
const DTreeContext *parent_context = context_->parent_context();
- const NodeRef *parent_node = context_->parent_node();
+ const bNode *parent_node = context_->parent_node();
BLI_assert(parent_context != nullptr);
BLI_assert(parent_node != nullptr);
- const int socket_index = socket_ref_->index();
- return {parent_context, &parent_node->output(socket_index)};
+ const int socket_index = bsocket_->index();
+ return {parent_context, &parent_node->output_socket(socket_index)};
}
Vector<DOutputSocket> DInputSocket::get_corresponding_group_input_sockets() const
{
BLI_assert(*this);
- BLI_assert(socket_ref_->node().is_group_node());
+ BLI_assert(bsocket_->owner_node().is_group());
- const DTreeContext *child_context = context_->child_context(socket_ref_->node());
+ const DTreeContext *child_context = context_->child_context(bsocket_->owner_node());
BLI_assert(child_context != nullptr);
- const NodeTreeRef &child_tree = child_context->tree();
- Span<const NodeRef *> group_input_nodes = child_tree.nodes_by_type("NodeGroupInput");
- const int socket_index = socket_ref_->index();
+ const bNodeTree &child_tree = child_context->btree();
+ Span<const bNode *> group_input_nodes = child_tree.nodes_by_type("NodeGroupInput");
+ const int socket_index = bsocket_->index();
Vector<DOutputSocket> sockets;
- for (const NodeRef *group_input_node : group_input_nodes) {
- sockets.append(DOutputSocket(child_context, &group_input_node->output(socket_index)));
+ for (const bNode *group_input_node : group_input_nodes) {
+ sockets.append(DOutputSocket(child_context, &group_input_node->output_socket(socket_index)));
}
return sockets;
}
@@ -127,36 +127,36 @@ Vector<DOutputSocket> DInputSocket::get_corresponding_group_input_sockets() cons
DInputSocket DOutputSocket::get_corresponding_group_node_input() const
{
BLI_assert(*this);
- BLI_assert(socket_ref_->node().is_group_input_node());
- BLI_assert(socket_ref_->index() < socket_ref_->node().outputs().size() - 1);
+ BLI_assert(bsocket_->owner_node().is_group_input());
+ BLI_assert(bsocket_->index() < bsocket_->owner_node().output_sockets().size() - 1);
const DTreeContext *parent_context = context_->parent_context();
- const NodeRef *parent_node = context_->parent_node();
+ const bNode *parent_node = context_->parent_node();
BLI_assert(parent_context != nullptr);
BLI_assert(parent_node != nullptr);
- const int socket_index = socket_ref_->index();
- return {parent_context, &parent_node->input(socket_index)};
+ const int socket_index = bsocket_->index();
+ return {parent_context, &parent_node->input_socket(socket_index)};
}
DInputSocket DOutputSocket::get_active_corresponding_group_output_socket() const
{
BLI_assert(*this);
- BLI_assert(socket_ref_->node().is_group_node());
+ BLI_assert(bsocket_->owner_node().is_group());
- const DTreeContext *child_context = context_->child_context(socket_ref_->node());
+ const DTreeContext *child_context = context_->child_context(bsocket_->owner_node());
if (child_context == nullptr) {
/* Can happen when the group node references a non-existent group (e.g. when the group is
* linked but the original file is not found). */
return {};
}
- const NodeTreeRef &child_tree = child_context->tree();
- Span<const NodeRef *> group_output_nodes = child_tree.nodes_by_type("NodeGroupOutput");
- const int socket_index = socket_ref_->index();
- for (const NodeRef *group_output_node : group_output_nodes) {
- if (group_output_node->bnode()->flag & NODE_DO_OUTPUT || group_output_nodes.size() == 1) {
- return {child_context, &group_output_node->input(socket_index)};
+ const bNodeTree &child_tree = child_context->btree();
+ Span<const bNode *> group_output_nodes = child_tree.nodes_by_type("NodeGroupOutput");
+ const int socket_index = bsocket_->index();
+ for (const bNode *group_output_node : group_output_nodes) {
+ if (group_output_node->flag & NODE_DO_OUTPUT || group_output_nodes.size() == 1) {
+ return {child_context, &group_output_node->input_socket(socket_index)};
}
}
return {};
@@ -165,11 +165,11 @@ DInputSocket DOutputSocket::get_active_corresponding_group_output_socket() const
void DInputSocket::foreach_origin_socket(FunctionRef<void(DSocket)> origin_fn) const
{
BLI_assert(*this);
- for (const OutputSocketRef *linked_socket : socket_ref_->as_input().logically_linked_sockets()) {
- const NodeRef &linked_node = linked_socket->node();
+ for (const bNodeSocket *linked_socket : bsocket_->logically_linked_sockets()) {
+ const bNode &linked_node = linked_socket->owner_node();
DOutputSocket linked_dsocket{context_, linked_socket};
- if (linked_node.is_group_input_node()) {
+ if (linked_node.is_group_input()) {
if (context_->is_root()) {
/* This is a group input in the root node group. */
origin_fn(linked_dsocket);
@@ -187,7 +187,7 @@ void DInputSocket::foreach_origin_socket(FunctionRef<void(DSocket)> origin_fn) c
}
}
}
- else if (linked_node.is_group_node()) {
+ else if (linked_node.is_group()) {
DInputSocket socket_in_group = linked_dsocket.get_active_corresponding_group_output_socket();
if (socket_in_group) {
if (socket_in_group->is_logically_linked()) {
@@ -217,16 +217,16 @@ void DOutputSocket::foreach_target_socket(ForeachTargetSocketFn target_fn) const
void DOutputSocket::foreach_target_socket(ForeachTargetSocketFn target_fn,
TargetSocketPathInfo &path_info) const
{
- for (const LinkRef *link : socket_ref_->as_output().directly_linked_links()) {
+ for (const bNodeLink *link : bsocket_->directly_linked_links()) {
if (link->is_muted()) {
continue;
}
- const DInputSocket &linked_socket{context_, &link->to()};
+ const DInputSocket &linked_socket{context_, link->tosock};
if (!linked_socket->is_available()) {
continue;
}
const DNode linked_node = linked_socket.node();
- if (linked_node->is_reroute_node()) {
+ if (linked_node->is_reroute()) {
const DInputSocket reroute_input = linked_socket;
const DOutputSocket reroute_output = linked_node.output(0);
path_info.sockets.append(reroute_input);
@@ -236,18 +236,18 @@ void DOutputSocket::foreach_target_socket(ForeachTargetSocketFn target_fn,
path_info.sockets.pop_last();
}
else if (linked_node->is_muted()) {
- for (const InternalLinkRef *internal_link : linked_node->internal_links()) {
- if (&internal_link->from() != linked_socket.socket_ref()) {
+ for (const bNodeLink *internal_link : linked_node->internal_links_span()) {
+ if (internal_link->fromsock != linked_socket.bsocket()) {
continue;
}
/* The internal link only forwards the first incoming link. */
- if (linked_socket->is_multi_input_socket()) {
+ if (linked_socket->is_multi_input()) {
if (linked_socket->directly_linked_links()[0] != link) {
continue;
}
}
const DInputSocket mute_input = linked_socket;
- const DOutputSocket mute_output{context_, &internal_link->to()};
+ const DOutputSocket mute_output{context_, internal_link->tosock};
path_info.sockets.append(mute_input);
path_info.sockets.append(mute_output);
mute_output.foreach_target_socket(target_fn, path_info);
@@ -255,8 +255,8 @@ void DOutputSocket::foreach_target_socket(ForeachTargetSocketFn target_fn,
path_info.sockets.pop_last();
}
}
- else if (linked_node->is_group_output_node()) {
- if (linked_node.node_ref() != context_->tree().group_output_node()) {
+ else if (linked_node->is_group_output()) {
+ if (linked_node.bnode() != context_->btree().group_output_node()) {
continue;
}
if (context_->is_root()) {
@@ -276,7 +276,7 @@ void DOutputSocket::foreach_target_socket(ForeachTargetSocketFn target_fn,
path_info.sockets.pop_last();
}
}
- else if (linked_node->is_group_node()) {
+ else if (linked_node->is_group()) {
/* Follow the links within the nested node group. */
path_info.sockets.append(linked_socket);
const Vector<DOutputSocket> sockets_in_group =
@@ -310,7 +310,8 @@ static dot::Cluster *get_dot_cluster_for_context(
}
dot::Cluster *parent_cluster = get_dot_cluster_for_context(
digraph, parent_context, dot_clusters);
- std::string cluster_name = context->tree().name() + " / " + context->parent_node()->name();
+ std::string cluster_name = StringRef(context->btree().id.name + 2) + " / " +
+ context->parent_node()->name;
dot::Cluster &cluster = digraph.new_cluster(cluster_name);
cluster.set_parent_cluster(parent_cluster);
return &cluster;
@@ -328,11 +329,11 @@ std::string DerivedNodeTree::to_dot() const
this->foreach_node([&](DNode node) {
/* Ignore nodes that should not show up in the final output. */
- if (node->is_muted() || node->is_group_node() || node->is_reroute_node() || node->is_frame()) {
+ if (node->is_muted() || node->is_group() || node->is_reroute() || node->is_frame()) {
return;
}
if (!node.context()->is_root()) {
- if (node->is_group_input_node() || node->is_group_output_node()) {
+ if (node->is_group_input() || node->is_group_output()) {
return;
}
}
@@ -345,22 +346,22 @@ std::string DerivedNodeTree::to_dot() const
Vector<std::string> input_names;
Vector<std::string> output_names;
- for (const InputSocketRef *socket : node->inputs()) {
+ for (const bNodeSocket *socket : node->input_sockets()) {
if (socket->is_available()) {
- input_names.append(socket->name());
+ input_names.append(socket->name);
}
}
- for (const OutputSocketRef *socket : node->outputs()) {
+ for (const bNodeSocket *socket : node->output_sockets()) {
if (socket->is_available()) {
- output_names.append(socket->name());
+ output_names.append(socket->name);
}
}
dot::NodeWithSocketsRef dot_node_with_sockets = dot::NodeWithSocketsRef(
- dot_node, node->name(), input_names, output_names);
+ dot_node, node->name, input_names, output_names);
int input_index = 0;
- for (const InputSocketRef *socket : node->inputs()) {
+ for (const bNodeSocket *socket : node->input_sockets()) {
if (socket->is_available()) {
dot_input_sockets.add_new(DInputSocket{node.context(), socket},
dot_node_with_sockets.input(input_index));
@@ -368,7 +369,7 @@ std::string DerivedNodeTree::to_dot() const
}
}
int output_index = 0;
- for (const OutputSocketRef *socket : node->outputs()) {
+ for (const bNodeSocket *socket : node->output_sockets()) {
if (socket->is_available()) {
dot_output_sockets.add_new(DOutputSocket{node.context(), socket},
dot_node_with_sockets.output(output_index));
@@ -392,7 +393,7 @@ std::string DerivedNodeTree::to_dot() const
}
}
dot::Node &dot_node = *dot_floating_inputs.lookup_or_add_cb(from_socket, [&]() {
- dot::Node &dot_node = digraph.new_node(from_socket->name());
+ dot::Node &dot_node = digraph.new_node(from_socket->name);
dot_node.set_background_color("white");
dot_node.set_shape(dot::Attr_shape::Ellipse);
dot_node.set_parent_cluster(
diff --git a/source/blender/nodes/intern/geometry_nodes_eval_log.cc b/source/blender/nodes/intern/geometry_nodes_eval_log.cc
deleted file mode 100644
index 55930dcb1ee..00000000000
--- a/source/blender/nodes/intern/geometry_nodes_eval_log.cc
+++ /dev/null
@@ -1,520 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-
-#include "NOD_geometry_nodes_eval_log.hh"
-
-#include "BKE_curves.hh"
-#include "BKE_geometry_set_instances.hh"
-
-#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::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>();
-
- LogByTreeContext log_by_tree_context;
-
- /* Combine all the local loggers that have been used by separate threads. */
- for (LocalGeoLogger &local_logger : logger) {
- /* Take ownership of the allocator. */
- logger_allocators_.append(std::move(local_logger.allocator_));
-
- for (ValueOfSockets &value_of_sockets : local_logger.values_) {
- ValueLog *value_log = value_of_sockets.value.get();
-
- /* Take centralized ownership of the logged value. It might be referenced by multiple
- * sockets. */
- logged_values_.append(std::move(value_of_sockets.value));
-
- for (const DSocket &socket : value_of_sockets.sockets) {
- SocketLog &socket_log = this->lookup_or_add_socket_log(log_by_tree_context, socket);
- socket_log.value_ = value_log;
- }
- }
-
- for (NodeWithWarning &node_with_warning : local_logger.node_warnings_) {
- NodeLog &node_log = this->lookup_or_add_node_log(log_by_tree_context,
- 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);
- }
-
- for (NodeWithUsedNamedAttribute &node_with_attribute_name :
- local_logger.used_named_attributes_) {
- NodeLog &node_log = this->lookup_or_add_node_log(log_by_tree_context,
- node_with_attribute_name.node);
- node_log.used_named_attributes_.append(std::move(node_with_attribute_name.attribute));
- }
- }
-}
-
-TreeLog &ModifierLog::lookup_or_add_tree_log(LogByTreeContext &log_by_tree_context,
- const DTreeContext &tree_context)
-{
- TreeLog *tree_log = log_by_tree_context.lookup_default(&tree_context, nullptr);
- if (tree_log != nullptr) {
- return *tree_log;
- }
-
- const DTreeContext *parent_context = tree_context.parent_context();
- if (parent_context == nullptr) {
- return *root_tree_logs_.get();
- }
- TreeLog &parent_log = this->lookup_or_add_tree_log(log_by_tree_context, *parent_context);
- destruct_ptr<TreeLog> owned_tree_log = allocator_.construct<TreeLog>();
- tree_log = owned_tree_log.get();
- log_by_tree_context.add_new(&tree_context, tree_log);
- parent_log.child_logs_.add_new(tree_context.parent_node()->name(), std::move(owned_tree_log));
- return *tree_log;
-}
-
-NodeLog &ModifierLog::lookup_or_add_node_log(LogByTreeContext &log_by_tree_context, DNode node)
-{
- TreeLog &tree_log = this->lookup_or_add_tree_log(log_by_tree_context, *node.context());
- NodeLog &node_log = *tree_log.node_logs_.lookup_or_add_cb(node->name(), [&]() {
- destruct_ptr<NodeLog> node_log = allocator_.construct<NodeLog>();
- node_log->input_logs_.resize(node->inputs().size());
- node_log->output_logs_.resize(node->outputs().size());
- return node_log;
- });
- return node_log;
-}
-
-SocketLog &ModifierLog::lookup_or_add_socket_log(LogByTreeContext &log_by_tree_context,
- DSocket socket)
-{
- NodeLog &node_log = this->lookup_or_add_node_log(log_by_tree_context, socket.node());
- MutableSpan<SocketLog> socket_logs = socket->is_input() ? node_log.input_logs_ :
- node_log.output_logs_;
- SocketLog &socket_log = socket_logs[socket->index()];
- return socket_log;
-}
-
-void ModifierLog::foreach_node_log(FunctionRef<void(const NodeLog &)> fn) const
-{
- if (root_tree_logs_) {
- root_tree_logs_->foreach_node_log(fn);
- }
-}
-
-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);
- if (node_log == nullptr) {
- return nullptr;
- }
- return node_log->get();
-}
-
-const NodeLog *TreeLog::lookup_node_log(const bNode &node) const
-{
- return this->lookup_node_log(node.name);
-}
-
-const TreeLog *TreeLog::lookup_child_log(StringRef node_name) const
-{
- const destruct_ptr<TreeLog> *tree_log = child_logs_.lookup_ptr_as(node_name);
- if (tree_log == nullptr) {
- return nullptr;
- }
- return tree_log->get();
-}
-
-void TreeLog::foreach_node_log(FunctionRef<void(const NodeLog &)> fn) const
-{
- for (auto node_log : node_logs_.items()) {
- fn(*node_log.value);
- }
-
- for (auto child : child_logs_.items()) {
- child.value->foreach_node_log(fn);
- }
-}
-
-const SocketLog *NodeLog::lookup_socket_log(eNodeSocketInOut in_out, int index) const
-{
- BLI_assert(index >= 0);
- Span<SocketLog> socket_logs = (in_out == SOCK_IN) ? input_logs_ : output_logs_;
- if (index >= socket_logs.size()) {
- return nullptr;
- }
- return &socket_logs[index];
-}
-
-const SocketLog *NodeLog::lookup_socket_log(const bNode &node, const bNodeSocket &socket) const
-{
- ListBase sockets = socket.in_out == SOCK_IN ? node.inputs : node.outputs;
- int index = BLI_findindex(&sockets, &socket);
- 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,
- GEO_COMPONENT_TYPE_INSTANCES,
- 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 bke::AttributeMetaData &meta_data,
- const GeometryComponent &UNUSED(component)) {
- if (attribute_id.is_named() && names.add(attribute_id.name())) {
- this->attributes_.append({attribute_id.name(), meta_data.domain, meta_data.data_type});
- }
- });
-
- for (const GeometryComponent *component : geometry_set.get_components_for_read()) {
- component_types_.append(component->type());
- switch (component->type()) {
- case GEO_COMPONENT_TYPE_MESH: {
- const MeshComponent &mesh_component = *(const MeshComponent *)component;
- MeshInfo &info = this->mesh_info.emplace();
- info.verts_num = mesh_component.attribute_domain_size(ATTR_DOMAIN_POINT);
- info.edges_num = mesh_component.attribute_domain_size(ATTR_DOMAIN_EDGE);
- info.faces_num = mesh_component.attribute_domain_size(ATTR_DOMAIN_FACE);
- break;
- }
- case GEO_COMPONENT_TYPE_CURVE: {
- const CurveComponent &curve_component = *(const CurveComponent *)component;
- CurveInfo &info = this->curve_info.emplace();
- info.splines_num = curve_component.attribute_domain_size(ATTR_DOMAIN_CURVE);
- break;
- }
- case GEO_COMPONENT_TYPE_POINT_CLOUD: {
- const PointCloudComponent &pointcloud_component = *(const PointCloudComponent *)component;
- PointCloudInfo &info = this->pointcloud_info.emplace();
- info.points_num = pointcloud_component.attribute_domain_size(ATTR_DOMAIN_POINT);
- break;
- }
- case GEO_COMPONENT_TYPE_INSTANCES: {
- const InstancesComponent &instances_component = *(const InstancesComponent *)component;
- InstancesInfo &info = this->instances_info.emplace();
- info.instances_num = instances_component.instances_num();
- break;
- }
- case GEO_COMPONENT_TYPE_EDIT: {
- const GeometryComponentEditData &edit_component = *(
- const GeometryComponentEditData *)component;
- if (const bke::CurvesEditHints *curve_edit_hints =
- edit_component.curves_edit_hints_.get()) {
- EditDataInfo &info = this->edit_data_info.emplace();
- info.has_deform_matrices = curve_edit_hints->deform_mats.has_value();
- info.has_deformed_positions = curve_edit_hints->positions.has_value();
- }
- break;
- }
- case GEO_COMPONENT_TYPE_VOLUME: {
- break;
- }
- }
- }
- if (log_full_geometry) {
- full_geometry_ = std::make_unique<GeometrySet>(geometry_set);
- full_geometry_->ensure_owns_direct_data();
- }
-}
-
-Vector<const GeometryAttributeInfo *> NodeLog::lookup_available_attributes() const
-{
- Vector<const GeometryAttributeInfo *> attributes;
- Set<StringRef> names;
- for (const SocketLog &socket_log : input_logs_) {
- const ValueLog *value_log = socket_log.value();
- if (const GeometryValueLog *geo_value_log = dynamic_cast<const GeometryValueLog *>(
- value_log)) {
- for (const GeometryAttributeInfo &attribute : geo_value_log->attributes()) {
- if (names.add(attribute.name)) {
- attributes.append(&attribute);
- }
- }
- }
- }
- return attributes;
-}
-
-const ModifierLog *ModifierLog::find_root_by_node_editor_context(const SpaceNode &snode)
-{
- if (snode.id == nullptr) {
- return nullptr;
- }
- if (GS(snode.id->name) != ID_OB) {
- return nullptr;
- }
- Object *object = (Object *)snode.id;
- LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
- if (md->type == eModifierType_Nodes) {
- NodesModifierData *nmd = (NodesModifierData *)md;
- if (nmd->node_group == snode.nodetree) {
- return (ModifierLog *)nmd->runtime_eval_log;
- }
- }
- }
- return nullptr;
-}
-
-const TreeLog *ModifierLog::find_tree_by_node_editor_context(const SpaceNode &snode)
-{
- const ModifierLog *eval_log = ModifierLog::find_root_by_node_editor_context(snode);
- if (eval_log == nullptr) {
- return nullptr;
- }
- Vector<bNodeTreePath *> tree_path_vec = snode.treepath;
- if (tree_path_vec.is_empty()) {
- return nullptr;
- }
- TreeLog *current = eval_log->root_tree_logs_.get();
- for (bNodeTreePath *path : tree_path_vec.as_span().drop_front(1)) {
- destruct_ptr<TreeLog> *tree_log = current->child_logs_.lookup_ptr_as(path->node_name);
- if (tree_log == nullptr) {
- return nullptr;
- }
- current = tree_log->get();
- }
- return current;
-}
-
-const NodeLog *ModifierLog::find_node_by_node_editor_context(const SpaceNode &snode,
- const bNode &node)
-{
- const TreeLog *tree_log = ModifierLog::find_tree_by_node_editor_context(snode);
- if (tree_log == nullptr) {
- return nullptr;
- }
- return tree_log->lookup_node_log(node);
-}
-
-const NodeLog *ModifierLog::find_node_by_node_editor_context(const SpaceNode &snode,
- const StringRef node_name)
-{
- const TreeLog *tree_log = ModifierLog::find_tree_by_node_editor_context(snode);
- if (tree_log == nullptr) {
- return nullptr;
- }
- return tree_log->lookup_node_log(node_name);
-}
-
-const SocketLog *ModifierLog::find_socket_by_node_editor_context(const SpaceNode &snode,
- const bNode &node,
- const bNodeSocket &socket)
-{
- const NodeLog *node_log = ModifierLog::find_node_by_node_editor_context(snode, node);
- if (node_log == nullptr) {
- return nullptr;
- }
- return node_log->lookup_socket_log(node, socket);
-}
-
-const NodeLog *ModifierLog::find_node_by_spreadsheet_editor_context(
- const SpaceSpreadsheet &sspreadsheet)
-{
- Vector<SpreadsheetContext *> context_path = sspreadsheet.context_path;
- if (context_path.size() <= 2) {
- return nullptr;
- }
- if (context_path[0]->type != SPREADSHEET_CONTEXT_OBJECT) {
- return nullptr;
- }
- if (context_path[1]->type != SPREADSHEET_CONTEXT_MODIFIER) {
- return nullptr;
- }
- for (SpreadsheetContext *context : context_path.as_span().drop_front(2)) {
- if (context->type != SPREADSHEET_CONTEXT_NODE) {
- return nullptr;
- }
- }
- Span<SpreadsheetContextNode *> node_contexts =
- context_path.as_span().drop_front(2).cast<SpreadsheetContextNode *>();
-
- Object *object = ((SpreadsheetContextObject *)context_path[0])->object;
- StringRefNull modifier_name = ((SpreadsheetContextModifier *)context_path[1])->modifier_name;
- if (object == nullptr) {
- return nullptr;
- }
-
- const ModifierLog *eval_log = nullptr;
- LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
- if (md->type == eModifierType_Nodes) {
- if (md->name == modifier_name) {
- NodesModifierData *nmd = (NodesModifierData *)md;
- eval_log = (const ModifierLog *)nmd->runtime_eval_log;
- break;
- }
- }
- }
- if (eval_log == nullptr) {
- return nullptr;
- }
-
- const TreeLog *tree_log = &eval_log->root_tree();
- for (SpreadsheetContextNode *context : node_contexts.drop_back(1)) {
- tree_log = tree_log->lookup_child_log(context->node_name);
- if (tree_log == nullptr) {
- return nullptr;
- }
- }
- const NodeLog *node_log = tree_log->lookup_node_log(node_contexts.last()->node_name);
- return node_log;
-}
-
-void LocalGeoLogger::log_value_for_sockets(Span<DSocket> sockets, GPointer value)
-{
- const CPPType &type = *value.type();
- Span<DSocket> copied_sockets = allocator_->construct_array_copy(sockets);
- if (type.is<GeometrySet>()) {
- bool log_full_geometry = false;
- for (const DSocket &socket : sockets) {
- if (main_logger_->log_full_sockets_.contains(socket)) {
- log_full_geometry = true;
- break;
- }
- }
-
- const GeometrySet &geometry_set = *value.get<GeometrySet>();
- destruct_ptr<GeometryValueLog> value_log = allocator_->construct<GeometryValueLog>(
- 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);
- destruct_ptr<GenericValueLog> value_log = allocator_->construct<GenericValueLog>(
- GMutablePointer{type, buffer});
- values_.append({copied_sockets, std::move(value_log)});
- }
-}
-
-void LocalGeoLogger::log_multi_value_socket(DSocket socket, Span<GPointer> values)
-{
- /* Doesn't have to be logged currently. */
- UNUSED_VARS(socket, values);
-}
-
-void LocalGeoLogger::log_node_warning(DNode node, NodeWarningType type, std::string message)
-{
- 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_used_named_attribute(DNode node,
- std::string attribute_name,
- eNamedAttrUsage usage)
-{
- used_named_attributes_.append({node, {std::move(attribute_name), usage}});
-}
-
-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/geometry_nodes_lazy_function.cc b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc
new file mode 100644
index 00000000000..cb296cdd93f
--- /dev/null
+++ b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc
@@ -0,0 +1,1423 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/**
+ * This file mainly converts a #bNodeTree into a lazy-function graph. This generally works by
+ * creating a lazy-function for every node, which is then put into the lazy-function graph. Then
+ * the nodes in the new graph are linked based on links in the original #bNodeTree. Some additional
+ * nodes are inserted for things like type conversions and multi-input sockets.
+ *
+ * Currently, lazy-functions are even created for nodes that don't strictly require it, like
+ * reroutes or muted nodes. In the future we could avoid that at the cost of additional code
+ * complexity. So far, this does not seem to be a performance issue.
+ */
+
+#include "NOD_geometry_exec.hh"
+#include "NOD_geometry_nodes_lazy_function.hh"
+#include "NOD_multi_function.hh"
+#include "NOD_node_declaration.hh"
+
+#include "BLI_lazy_threading.hh"
+#include "BLI_map.hh"
+
+#include "DNA_ID.h"
+
+#include "BKE_compute_contexts.hh"
+#include "BKE_geometry_set.hh"
+#include "BKE_type_conversions.hh"
+
+#include "FN_field_cpp_type.hh"
+#include "FN_lazy_function_graph_executor.hh"
+
+namespace blender::nodes {
+
+using fn::ValueOrField;
+using fn::ValueOrFieldCPPType;
+using namespace fn::multi_function_types;
+
+static const CPPType *get_socket_cpp_type(const bNodeSocketType &typeinfo)
+{
+ const CPPType *type = typeinfo.geometry_nodes_cpp_type;
+ if (type == nullptr) {
+ return nullptr;
+ }
+ BLI_assert(type->has_special_member_functions());
+ return type;
+}
+
+static const CPPType *get_socket_cpp_type(const bNodeSocket &socket)
+{
+ return get_socket_cpp_type(*socket.typeinfo);
+}
+
+static const CPPType *get_vector_type(const CPPType &type)
+{
+ /* This could be generalized in the future. For now we only support a small set of vectors. */
+ if (type.is<GeometrySet>()) {
+ return &CPPType::get<Vector<GeometrySet>>();
+ }
+ if (type.is<ValueOrField<std::string>>()) {
+ return &CPPType::get<Vector<ValueOrField<std::string>>>();
+ }
+ return nullptr;
+}
+
+/**
+ * Checks which sockets of the node are available and creates corresponding inputs/outputs on the
+ * lazy-function.
+ */
+static void lazy_function_interface_from_node(const bNode &node,
+ Vector<const bNodeSocket *> &r_used_inputs,
+ Vector<const bNodeSocket *> &r_used_outputs,
+ Vector<lf::Input> &r_inputs,
+ Vector<lf::Output> &r_outputs)
+{
+ const bool is_muted = node.is_muted();
+ const bool supports_laziness = node.typeinfo->geometry_node_execute_supports_laziness ||
+ node.is_group();
+ const lf::ValueUsage input_usage = supports_laziness ? lf::ValueUsage::Maybe :
+ lf::ValueUsage::Used;
+ for (const bNodeSocket *socket : node.input_sockets()) {
+ if (!socket->is_available()) {
+ continue;
+ }
+ const CPPType *type = get_socket_cpp_type(*socket);
+ if (type == nullptr) {
+ continue;
+ }
+ if (socket->is_multi_input() && !is_muted) {
+ type = get_vector_type(*type);
+ }
+ r_inputs.append({socket->identifier, *type, input_usage});
+ r_used_inputs.append(socket);
+ }
+ for (const bNodeSocket *socket : node.output_sockets()) {
+ if (!socket->is_available()) {
+ continue;
+ }
+ const CPPType *type = get_socket_cpp_type(*socket);
+ if (type == nullptr) {
+ continue;
+ }
+ r_outputs.append({socket->identifier, *type});
+ r_used_outputs.append(socket);
+ }
+}
+
+/**
+ * Used for most normal geometry nodes like Subdivision Surface and Set Position.
+ */
+class LazyFunctionForGeometryNode : public LazyFunction {
+ private:
+ const bNode &node_;
+
+ public:
+ LazyFunctionForGeometryNode(const bNode &node,
+ Vector<const bNodeSocket *> &r_used_inputs,
+ Vector<const bNodeSocket *> &r_used_outputs)
+ : node_(node)
+ {
+ BLI_assert(node.typeinfo->geometry_node_execute != nullptr);
+ debug_name_ = node.name;
+ lazy_function_interface_from_node(node, r_used_inputs, r_used_outputs, inputs_, outputs_);
+ }
+
+ void execute_impl(lf::Params &params, const lf::Context &context) const override
+ {
+ GeoNodesLFUserData *user_data = dynamic_cast<GeoNodesLFUserData *>(context.user_data);
+ BLI_assert(user_data != nullptr);
+
+ GeoNodeExecParams geo_params{node_, params, context};
+
+ geo_eval_log::TimePoint start_time = geo_eval_log::Clock::now();
+ node_.typeinfo->geometry_node_execute(geo_params);
+ geo_eval_log::TimePoint end_time = geo_eval_log::Clock::now();
+
+ if (geo_eval_log::GeoModifierLog *modifier_log = user_data->modifier_data->eval_log) {
+ geo_eval_log::GeoTreeLogger &tree_logger = modifier_log->get_local_tree_logger(
+ *user_data->compute_context);
+ tree_logger.node_execution_times.append(
+ {tree_logger.allocator->copy_string(node_.name), start_time, end_time});
+ }
+ }
+};
+
+/**
+ * Used to gather all inputs of a multi-input socket. A separate node is necessary because
+ * multi-inputs are not supported in lazy-function graphs.
+ */
+class LazyFunctionForMultiInput : public LazyFunction {
+ private:
+ const CPPType *base_type_;
+
+ public:
+ LazyFunctionForMultiInput(const bNodeSocket &socket)
+ {
+ debug_name_ = "Multi Input";
+ base_type_ = get_socket_cpp_type(socket);
+ BLI_assert(base_type_ != nullptr);
+ BLI_assert(socket.is_multi_input());
+ for (const bNodeLink *link : socket.directly_linked_links()) {
+ if (!link->is_muted()) {
+ inputs_.append({"Input", *base_type_});
+ }
+ }
+ const CPPType *vector_type = get_vector_type(*base_type_);
+ BLI_assert(vector_type != nullptr);
+ outputs_.append({"Output", *vector_type});
+ }
+
+ void execute_impl(lf::Params &params, const lf::Context &UNUSED(context)) const override
+ {
+ /* Currently we only have multi-inputs for geometry and string sockets. This could be
+ * generalized in the future. */
+ base_type_->to_static_type_tag<GeometrySet, ValueOrField<std::string>>([&](auto type_tag) {
+ using T = typename decltype(type_tag)::type;
+ if constexpr (std::is_void_v<T>) {
+ /* This type is not supported in this node for now. */
+ BLI_assert_unreachable();
+ }
+ else {
+ void *output_ptr = params.get_output_data_ptr(0);
+ Vector<T> &values = *new (output_ptr) Vector<T>();
+ for (const int i : inputs_.index_range()) {
+ values.append(params.extract_input<T>(i));
+ }
+ params.output_set(0);
+ }
+ });
+ }
+};
+
+/**
+ * Simple lazy-function that just forwards the input.
+ */
+class LazyFunctionForRerouteNode : public LazyFunction {
+ public:
+ LazyFunctionForRerouteNode(const CPPType &type)
+ {
+ debug_name_ = "Reroute";
+ inputs_.append({"Input", type});
+ outputs_.append({"Output", type});
+ }
+
+ void execute_impl(lf::Params &params, const lf::Context &UNUSED(context)) const override
+ {
+ void *input_value = params.try_get_input_data_ptr(0);
+ void *output_value = params.get_output_data_ptr(0);
+ BLI_assert(input_value != nullptr);
+ BLI_assert(output_value != nullptr);
+ const CPPType &type = *inputs_[0].type;
+ type.move_construct(input_value, output_value);
+ params.output_set(0);
+ }
+};
+
+/**
+ * Lazy functions for nodes whose type cannot be found. An undefined function just outputs default
+ * values. It's useful to have so other parts of the conversion don't have to care about undefined
+ * nodes.
+ */
+class LazyFunctionForUndefinedNode : public LazyFunction {
+ public:
+ LazyFunctionForUndefinedNode(const bNode &node, Vector<const bNodeSocket *> &r_used_outputs)
+ {
+ debug_name_ = "Undefined";
+ Vector<const bNodeSocket *> dummy_used_inputs;
+ Vector<lf::Input> dummy_inputs;
+ lazy_function_interface_from_node(
+ node, dummy_used_inputs, r_used_outputs, dummy_inputs, outputs_);
+ }
+
+ void execute_impl(lf::Params &params, const lf::Context &UNUSED(context)) const override
+ {
+ params.set_default_remaining_outputs();
+ }
+};
+
+/**
+ * Executes a multi-function. If all inputs are single values, the results will also be single
+ * values. If any input is a field, the outputs will also be fields.
+ */
+static void execute_multi_function_on_value_or_field(
+ const MultiFunction &fn,
+ const std::shared_ptr<MultiFunction> &owned_fn,
+ const Span<const ValueOrFieldCPPType *> input_types,
+ const Span<const ValueOrFieldCPPType *> output_types,
+ const Span<const void *> input_values,
+ const Span<void *> output_values)
+{
+ BLI_assert(fn.param_amount() == input_types.size() + output_types.size());
+ BLI_assert(input_types.size() == input_values.size());
+ BLI_assert(output_types.size() == output_values.size());
+
+ /* Check if any input is a field. */
+ bool any_input_is_field = false;
+ for (const int i : input_types.index_range()) {
+ const ValueOrFieldCPPType &type = *input_types[i];
+ const void *value_or_field = input_values[i];
+ if (type.is_field(value_or_field)) {
+ any_input_is_field = true;
+ break;
+ }
+ }
+
+ if (any_input_is_field) {
+ /* Convert all inputs into fields, so that they can be used as input in the new field. */
+ Vector<GField> input_fields;
+ for (const int i : input_types.index_range()) {
+ const ValueOrFieldCPPType &type = *input_types[i];
+ const void *value_or_field = input_values[i];
+ input_fields.append(type.as_field(value_or_field));
+ }
+
+ /* Construct the new field node. */
+ std::shared_ptr<fn::FieldOperation> operation;
+ if (owned_fn) {
+ operation = std::make_shared<fn::FieldOperation>(owned_fn, std::move(input_fields));
+ }
+ else {
+ operation = std::make_shared<fn::FieldOperation>(fn, std::move(input_fields));
+ }
+
+ /* Store the new fields in the output. */
+ for (const int i : output_types.index_range()) {
+ const ValueOrFieldCPPType &type = *output_types[i];
+ void *value_or_field = output_values[i];
+ type.construct_from_field(value_or_field, GField{operation, i});
+ }
+ }
+ else {
+ /* In this case, the multi-function is evaluated directly. */
+ MFParamsBuilder params{fn, 1};
+ MFContextBuilder context;
+
+ for (const int i : input_types.index_range()) {
+ const ValueOrFieldCPPType &type = *input_types[i];
+ const CPPType &base_type = type.base_type();
+ const void *value_or_field = input_values[i];
+ const void *value = type.get_value_ptr(value_or_field);
+ params.add_readonly_single_input(GVArray::ForSingleRef(base_type, 1, value));
+ }
+ for (const int i : output_types.index_range()) {
+ const ValueOrFieldCPPType &type = *output_types[i];
+ const CPPType &base_type = type.base_type();
+ void *value_or_field = output_values[i];
+ type.default_construct(value_or_field);
+ void *value = type.get_value_ptr(value_or_field);
+ base_type.destruct(value);
+ params.add_uninitialized_single_output(GMutableSpan{base_type, value, 1});
+ }
+ fn.call(IndexRange(1), params, context);
+ }
+}
+
+/**
+ * Behavior of muted nodes:
+ * - Some inputs are forwarded to outputs without changes.
+ * - Some inputs are converted to a different type which becomes the output.
+ * - Some outputs are value initialized because they don't have a corresponding input.
+ */
+class LazyFunctionForMutedNode : public LazyFunction {
+ private:
+ Array<int> input_by_output_index_;
+
+ public:
+ LazyFunctionForMutedNode(const bNode &node,
+ Vector<const bNodeSocket *> &r_used_inputs,
+ Vector<const bNodeSocket *> &r_used_outputs)
+ {
+ debug_name_ = "Muted";
+ lazy_function_interface_from_node(node, r_used_inputs, r_used_outputs, inputs_, outputs_);
+ for (lf::Input &fn_input : inputs_) {
+ fn_input.usage = lf::ValueUsage::Maybe;
+ }
+
+ for (lf::Input &fn_input : inputs_) {
+ fn_input.usage = lf::ValueUsage::Unused;
+ }
+
+ input_by_output_index_.reinitialize(outputs_.size());
+ input_by_output_index_.fill(-1);
+ for (const bNodeLink *internal_link : node.internal_links_span()) {
+ const int input_i = r_used_inputs.first_index_of_try(internal_link->fromsock);
+ const int output_i = r_used_outputs.first_index_of_try(internal_link->tosock);
+ if (ELEM(-1, input_i, output_i)) {
+ continue;
+ }
+ input_by_output_index_[output_i] = input_i;
+ inputs_[input_i].usage = lf::ValueUsage::Maybe;
+ }
+ }
+
+ void execute_impl(lf::Params &params, const lf::Context &UNUSED(context)) const override
+ {
+ for (const int output_i : outputs_.index_range()) {
+ if (params.output_was_set(output_i)) {
+ continue;
+ }
+ const CPPType &output_type = *outputs_[output_i].type;
+ void *output_value = params.get_output_data_ptr(output_i);
+ const int input_i = input_by_output_index_[output_i];
+ if (input_i == -1) {
+ /* The output does not have a corresponding input. */
+ output_type.value_initialize(output_value);
+ params.output_set(output_i);
+ continue;
+ }
+ const void *input_value = params.try_get_input_data_ptr_or_request(input_i);
+ if (input_value == nullptr) {
+ continue;
+ }
+ const CPPType &input_type = *inputs_[input_i].type;
+ if (input_type == output_type) {
+ /* Forward the value as is. */
+ input_type.copy_construct(input_value, output_value);
+ params.output_set(output_i);
+ continue;
+ }
+ /* Perform a type conversion and then format the value. */
+ const bke::DataTypeConversions &conversions = bke::get_implicit_type_conversions();
+ const auto *from_field_type = dynamic_cast<const ValueOrFieldCPPType *>(&input_type);
+ const auto *to_field_type = dynamic_cast<const ValueOrFieldCPPType *>(&output_type);
+ if (from_field_type != nullptr && to_field_type != nullptr) {
+ const CPPType &from_base_type = from_field_type->base_type();
+ const CPPType &to_base_type = to_field_type->base_type();
+ if (conversions.is_convertible(from_base_type, to_base_type)) {
+ const MultiFunction &multi_fn = *conversions.get_conversion_multi_function(
+ MFDataType::ForSingle(from_base_type), MFDataType::ForSingle(to_base_type));
+ execute_multi_function_on_value_or_field(
+ multi_fn, {}, {from_field_type}, {to_field_type}, {input_value}, {output_value});
+ }
+ params.output_set(output_i);
+ continue;
+ }
+ /* Use a value initialization if the conversion does not work. */
+ output_type.value_initialize(output_value);
+ params.output_set(output_i);
+ }
+ }
+};
+
+/**
+ * Type conversions are generally implemented as multi-functions. This node checks if the input is
+ * a field or single value and outputs a field or single value respectively.
+ */
+class LazyFunctionForMultiFunctionConversion : public LazyFunction {
+ private:
+ const MultiFunction &fn_;
+ const ValueOrFieldCPPType &from_type_;
+ const ValueOrFieldCPPType &to_type_;
+ const Vector<const bNodeSocket *> target_sockets_;
+
+ public:
+ LazyFunctionForMultiFunctionConversion(const MultiFunction &fn,
+ const ValueOrFieldCPPType &from,
+ const ValueOrFieldCPPType &to,
+ Vector<const bNodeSocket *> &&target_sockets)
+ : fn_(fn), from_type_(from), to_type_(to), target_sockets_(std::move(target_sockets))
+ {
+ debug_name_ = "Convert";
+ inputs_.append({"From", from});
+ outputs_.append({"To", to});
+ }
+
+ void execute_impl(lf::Params &params, const lf::Context &UNUSED(context)) const override
+ {
+ const void *from_value = params.try_get_input_data_ptr(0);
+ void *to_value = params.get_output_data_ptr(0);
+ BLI_assert(from_value != nullptr);
+ BLI_assert(to_value != nullptr);
+
+ execute_multi_function_on_value_or_field(
+ fn_, {}, {&from_type_}, {&to_type_}, {from_value}, {to_value});
+
+ params.output_set(0);
+ }
+};
+
+/**
+ * This lazy-function wraps nodes that are implemented as multi-function (mostly math nodes).
+ */
+class LazyFunctionForMultiFunctionNode : public LazyFunction {
+ private:
+ const NodeMultiFunctions::Item fn_item_;
+ Vector<const ValueOrFieldCPPType *> input_types_;
+ Vector<const ValueOrFieldCPPType *> output_types_;
+
+ public:
+ LazyFunctionForMultiFunctionNode(const bNode &node,
+ NodeMultiFunctions::Item fn_item,
+ Vector<const bNodeSocket *> &r_used_inputs,
+ Vector<const bNodeSocket *> &r_used_outputs)
+ : fn_item_(std::move(fn_item))
+ {
+ BLI_assert(fn_item_.fn != nullptr);
+ debug_name_ = node.name;
+ lazy_function_interface_from_node(node, r_used_inputs, r_used_outputs, inputs_, outputs_);
+ for (const lf::Input &fn_input : inputs_) {
+ input_types_.append(dynamic_cast<const ValueOrFieldCPPType *>(fn_input.type));
+ }
+ for (const lf::Output &fn_output : outputs_) {
+ output_types_.append(dynamic_cast<const ValueOrFieldCPPType *>(fn_output.type));
+ }
+ }
+
+ void execute_impl(lf::Params &params, const lf::Context &UNUSED(context)) const override
+ {
+ Vector<const void *> input_values(inputs_.size());
+ Vector<void *> output_values(outputs_.size());
+ for (const int i : inputs_.index_range()) {
+ input_values[i] = params.try_get_input_data_ptr(i);
+ }
+ for (const int i : outputs_.index_range()) {
+ output_values[i] = params.get_output_data_ptr(i);
+ }
+ execute_multi_function_on_value_or_field(
+ *fn_item_.fn, fn_item_.owned_fn, input_types_, output_types_, input_values, output_values);
+ for (const int i : outputs_.index_range()) {
+ params.output_set(i);
+ }
+ }
+};
+
+/**
+ * Some sockets have non-trivial implicit inputs (e.g. the Position input of the Set Position
+ * node). Those are implemented as a separate node that outputs the value.
+ */
+class LazyFunctionForImplicitInput : public LazyFunction {
+ private:
+ /**
+ * The function that generates the implicit input. The passed in memory is uninitialized.
+ */
+ std::function<void(void *)> init_fn_;
+
+ public:
+ LazyFunctionForImplicitInput(const CPPType &type, std::function<void(void *)> init_fn)
+ : init_fn_(std::move(init_fn))
+ {
+ debug_name_ = "Input";
+ outputs_.append({"Output", type});
+ }
+
+ void execute_impl(lf::Params &params, const lf::Context &UNUSED(context)) const override
+ {
+ void *value = params.get_output_data_ptr(0);
+ init_fn_(value);
+ params.output_set(0);
+ }
+};
+
+/**
+ * The viewer node does not have outputs. Instead it is executed because the executor knows that it
+ * has side effects. The side effect is that the inputs to the viewer are logged.
+ */
+class LazyFunctionForViewerNode : public LazyFunction {
+ private:
+ const bNode &bnode_;
+ /** The field is only logged when it is linked. */
+ bool use_field_input_ = true;
+
+ public:
+ LazyFunctionForViewerNode(const bNode &bnode, Vector<const bNodeSocket *> &r_used_inputs)
+ : bnode_(bnode)
+ {
+ debug_name_ = "Viewer";
+ Vector<const bNodeSocket *> dummy_used_outputs;
+ lazy_function_interface_from_node(bnode, r_used_inputs, dummy_used_outputs, inputs_, outputs_);
+ if (!r_used_inputs[1]->is_directly_linked()) {
+ use_field_input_ = false;
+ r_used_inputs.pop_last();
+ inputs_.pop_last();
+ }
+ }
+
+ void execute_impl(lf::Params &params, const lf::Context &context) const override
+ {
+ GeoNodesLFUserData *user_data = dynamic_cast<GeoNodesLFUserData *>(context.user_data);
+ BLI_assert(user_data != nullptr);
+
+ GeometrySet geometry = params.extract_input<GeometrySet>(0);
+
+ GField field;
+ if (use_field_input_) {
+ const void *value_or_field = params.try_get_input_data_ptr(1);
+ BLI_assert(value_or_field != nullptr);
+ const ValueOrFieldCPPType &value_or_field_type = static_cast<const ValueOrFieldCPPType &>(
+ *inputs_[1].type);
+ field = value_or_field_type.as_field(value_or_field);
+ }
+
+ geo_eval_log::GeoTreeLogger &tree_logger =
+ user_data->modifier_data->eval_log->get_local_tree_logger(*user_data->compute_context);
+ tree_logger.log_viewer_node(bnode_, geometry, field);
+ }
+};
+
+/**
+ * This lazy-function wraps a group node. Internally it just executes the lazy-function graph of
+ * the referenced group.
+ */
+class LazyFunctionForGroupNode : public LazyFunction {
+ private:
+ const bNode &group_node_;
+ bool has_many_nodes_ = false;
+ std::optional<GeometryNodesLazyFunctionLogger> lf_logger_;
+ std::optional<GeometryNodesLazyFunctionSideEffectProvider> lf_side_effect_provider_;
+ std::optional<lf::GraphExecutor> graph_executor_;
+
+ public:
+ LazyFunctionForGroupNode(const bNode &group_node,
+ const GeometryNodesLazyFunctionGraphInfo &lf_graph_info,
+ Vector<const bNodeSocket *> &r_used_inputs,
+ Vector<const bNodeSocket *> &r_used_outputs)
+ : group_node_(group_node)
+ {
+ debug_name_ = group_node.name;
+ lazy_function_interface_from_node(
+ group_node, r_used_inputs, r_used_outputs, inputs_, outputs_);
+
+ bNodeTree *group_btree = reinterpret_cast<bNodeTree *>(group_node_.id);
+ BLI_assert(group_btree != nullptr);
+
+ has_many_nodes_ = lf_graph_info.num_inline_nodes_approximate > 1000;
+
+ Vector<const lf::OutputSocket *> graph_inputs;
+ for (const lf::OutputSocket *socket : lf_graph_info.mapping.group_input_sockets) {
+ if (socket != nullptr) {
+ graph_inputs.append(socket);
+ }
+ }
+ Vector<const lf::InputSocket *> graph_outputs;
+ if (const bNode *group_output_bnode = group_btree->group_output_node()) {
+ for (const bNodeSocket *bsocket : group_output_bnode->input_sockets().drop_back(1)) {
+ const lf::Socket *socket = lf_graph_info.mapping.dummy_socket_map.lookup_default(bsocket,
+ nullptr);
+ if (socket != nullptr) {
+ graph_outputs.append(&socket->as_input());
+ }
+ }
+ }
+
+ lf_logger_.emplace(lf_graph_info);
+ lf_side_effect_provider_.emplace();
+ graph_executor_.emplace(lf_graph_info.graph,
+ std::move(graph_inputs),
+ std::move(graph_outputs),
+ &*lf_logger_,
+ &*lf_side_effect_provider_);
+ }
+
+ void execute_impl(lf::Params &params, const lf::Context &context) const override
+ {
+ GeoNodesLFUserData *user_data = dynamic_cast<GeoNodesLFUserData *>(context.user_data);
+ BLI_assert(user_data != nullptr);
+
+ if (has_many_nodes_) {
+ /* If the called node group has many nodes, it's likely that executing it takes a while even
+ * if every individual node is very small. */
+ lazy_threading::send_hint();
+ }
+
+ /* The compute context changes when entering a node group. */
+ bke::NodeGroupComputeContext compute_context{user_data->compute_context, group_node_.name};
+ GeoNodesLFUserData group_user_data = *user_data;
+ group_user_data.compute_context = &compute_context;
+
+ lf::Context group_context = context;
+ group_context.user_data = &group_user_data;
+
+ graph_executor_->execute(params, group_context);
+ }
+
+ void *init_storage(LinearAllocator<> &allocator) const override
+ {
+ return graph_executor_->init_storage(allocator);
+ }
+
+ void destruct_storage(void *storage) const override
+ {
+ graph_executor_->destruct_storage(storage);
+ }
+};
+
+static GMutablePointer get_socket_default_value(LinearAllocator<> &allocator,
+ const bNodeSocket &bsocket)
+{
+ const bNodeSocketType &typeinfo = *bsocket.typeinfo;
+ const CPPType *type = get_socket_cpp_type(typeinfo);
+ if (type == nullptr) {
+ return {};
+ }
+ void *buffer = allocator.allocate(type->size(), type->alignment());
+ typeinfo.get_geometry_nodes_cpp_value(bsocket, buffer);
+ return {type, buffer};
+}
+
+/**
+ * Utility class to build a lazy-function graph based on a geometry nodes tree.
+ * This is mainly a separate class because it makes it easier to have variables that can be
+ * accessed by many functions.
+ */
+struct GeometryNodesLazyFunctionGraphBuilder {
+ private:
+ const bNodeTree &btree_;
+ GeometryNodesLazyFunctionGraphInfo *lf_graph_info_;
+ lf::Graph *lf_graph_;
+ GeometryNodeLazyFunctionGraphMapping *mapping_;
+ MultiValueMap<const bNodeSocket *, lf::InputSocket *> input_socket_map_;
+ Map<const bNodeSocket *, lf::OutputSocket *> output_socket_map_;
+ Map<const bNodeSocket *, lf::Node *> multi_input_socket_nodes_;
+ const bke::DataTypeConversions *conversions_;
+
+ /**
+ * All group input nodes are combined into one dummy node in the lazy-function graph.
+ * If some input has an invalid type, it is ignored in the new graph. In this case null and -1 is
+ * used in the vectors below.
+ */
+ Vector<const CPPType *> group_input_types_;
+ Vector<int> group_input_indices_;
+ lf::DummyNode *group_input_lf_node_;
+
+ /**
+ * The output types or null if an output is invalid. Each group output node gets a separate
+ * corresponding dummy node in the new graph.
+ */
+ Vector<const CPPType *> group_output_types_;
+ Vector<int> group_output_indices_;
+
+ public:
+ GeometryNodesLazyFunctionGraphBuilder(const bNodeTree &btree,
+ GeometryNodesLazyFunctionGraphInfo &lf_graph_info)
+ : btree_(btree), lf_graph_info_(&lf_graph_info)
+ {
+ }
+
+ void build()
+ {
+ btree_.ensure_topology_cache();
+
+ lf_graph_ = &lf_graph_info_->graph;
+ mapping_ = &lf_graph_info_->mapping;
+ conversions_ = &bke::get_implicit_type_conversions();
+
+ this->prepare_node_multi_functions();
+ this->prepare_group_inputs();
+ this->prepare_group_outputs();
+ this->build_group_input_node();
+ this->handle_nodes();
+ this->handle_links();
+ this->add_default_inputs();
+
+ lf_graph_->update_node_indices();
+ lf_graph_info_->num_inline_nodes_approximate += lf_graph_->nodes().size();
+ }
+
+ private:
+ void prepare_node_multi_functions()
+ {
+ lf_graph_info_->node_multi_functions = std::make_unique<NodeMultiFunctions>(btree_);
+ }
+
+ void prepare_group_inputs()
+ {
+ LISTBASE_FOREACH (const bNodeSocket *, interface_bsocket, &btree_.inputs) {
+ const CPPType *type = get_socket_cpp_type(*interface_bsocket->typeinfo);
+ if (type != nullptr) {
+ const int index = group_input_types_.append_and_get_index(type);
+ group_input_indices_.append(index);
+ }
+ else {
+ group_input_indices_.append(-1);
+ }
+ }
+ }
+
+ void prepare_group_outputs()
+ {
+ LISTBASE_FOREACH (const bNodeSocket *, interface_bsocket, &btree_.outputs) {
+ const CPPType *type = get_socket_cpp_type(*interface_bsocket->typeinfo);
+ if (type != nullptr) {
+ const int index = group_output_types_.append_and_get_index(type);
+ group_output_indices_.append(index);
+ }
+ else {
+ group_output_indices_.append(-1);
+ }
+ }
+ }
+
+ void build_group_input_node()
+ {
+ /* Create a dummy node for the group inputs. */
+ group_input_lf_node_ = &lf_graph_->add_dummy({}, group_input_types_);
+ for (const int group_input_index : group_input_indices_) {
+ if (group_input_index == -1) {
+ mapping_->group_input_sockets.append(nullptr);
+ }
+ else {
+ mapping_->group_input_sockets.append(&group_input_lf_node_->output(group_input_index));
+ }
+ }
+ }
+
+ void handle_nodes()
+ {
+ /* Insert all nodes into the lazy function graph. */
+ for (const bNode *bnode : btree_.all_nodes()) {
+ const bNodeType *node_type = bnode->typeinfo;
+ if (node_type == nullptr) {
+ continue;
+ }
+ if (bnode->is_muted()) {
+ this->handle_muted_node(*bnode);
+ continue;
+ }
+ switch (node_type->type) {
+ case NODE_FRAME: {
+ /* Ignored. */
+ break;
+ }
+ case NODE_REROUTE: {
+ this->handle_reroute_node(*bnode);
+ break;
+ }
+ case NODE_GROUP_INPUT: {
+ this->handle_group_input_node(*bnode);
+ break;
+ }
+ case NODE_GROUP_OUTPUT: {
+ this->handle_group_output_node(*bnode);
+ break;
+ }
+ case NODE_CUSTOM_GROUP:
+ case NODE_GROUP: {
+ this->handle_group_node(*bnode);
+ break;
+ }
+ case GEO_NODE_VIEWER: {
+ this->handle_viewer_node(*bnode);
+ break;
+ }
+ default: {
+ if (node_type->geometry_node_execute) {
+ this->handle_geometry_node(*bnode);
+ break;
+ }
+ const NodeMultiFunctions::Item &fn_item = lf_graph_info_->node_multi_functions->try_get(
+ *bnode);
+ if (fn_item.fn != nullptr) {
+ this->handle_multi_function_node(*bnode, fn_item);
+ break;
+ }
+ if (node_type == &NodeTypeUndefined) {
+ this->handle_undefined_node(*bnode);
+ break;
+ }
+ /* Nodes that don't match any of the criteria above are just ignored. */
+ break;
+ }
+ }
+ }
+ }
+
+ void handle_muted_node(const bNode &bnode)
+ {
+ Vector<const bNodeSocket *> used_inputs;
+ Vector<const bNodeSocket *> used_outputs;
+ auto lazy_function = std::make_unique<LazyFunctionForMutedNode>(
+ bnode, used_inputs, used_outputs);
+ lf::Node &lf_node = lf_graph_->add_function(*lazy_function);
+ lf_graph_info_->functions.append(std::move(lazy_function));
+ for (const int i : used_inputs.index_range()) {
+ const bNodeSocket &bsocket = *used_inputs[i];
+ lf::InputSocket &lf_socket = lf_node.input(i);
+ input_socket_map_.add(&bsocket, &lf_socket);
+ mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket);
+ }
+ for (const int i : used_outputs.index_range()) {
+ const bNodeSocket &bsocket = *used_outputs[i];
+ lf::OutputSocket &lf_socket = lf_node.output(i);
+ output_socket_map_.add_new(&bsocket, &lf_socket);
+ mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket);
+ }
+ }
+
+ void handle_reroute_node(const bNode &bnode)
+ {
+ const bNodeSocket &input_bsocket = bnode.input_socket(0);
+ const bNodeSocket &output_bsocket = bnode.output_socket(0);
+ const CPPType *type = get_socket_cpp_type(input_bsocket);
+ if (type == nullptr) {
+ return;
+ }
+
+ auto lazy_function = std::make_unique<LazyFunctionForRerouteNode>(*type);
+ lf::Node &lf_node = lf_graph_->add_function(*lazy_function);
+ lf_graph_info_->functions.append(std::move(lazy_function));
+
+ lf::InputSocket &lf_input = lf_node.input(0);
+ lf::OutputSocket &lf_output = lf_node.output(0);
+ input_socket_map_.add(&input_bsocket, &lf_input);
+ output_socket_map_.add_new(&output_bsocket, &lf_output);
+ mapping_->bsockets_by_lf_socket_map.add(&lf_input, &input_bsocket);
+ mapping_->bsockets_by_lf_socket_map.add(&lf_output, &output_bsocket);
+ }
+
+ void handle_group_input_node(const bNode &bnode)
+ {
+ for (const int btree_index : group_input_indices_.index_range()) {
+ const int lf_index = group_input_indices_[btree_index];
+ if (lf_index == -1) {
+ continue;
+ }
+ const bNodeSocket &bsocket = bnode.output_socket(btree_index);
+ lf::OutputSocket &lf_socket = group_input_lf_node_->output(lf_index);
+ output_socket_map_.add_new(&bsocket, &lf_socket);
+ mapping_->dummy_socket_map.add_new(&bsocket, &lf_socket);
+ mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket);
+ }
+ }
+
+ void handle_group_output_node(const bNode &bnode)
+ {
+ lf::DummyNode &group_output_lf_node = lf_graph_->add_dummy(group_output_types_, {});
+ for (const int btree_index : group_output_indices_.index_range()) {
+ const int lf_index = group_output_indices_[btree_index];
+ if (lf_index == -1) {
+ continue;
+ }
+ const bNodeSocket &bsocket = bnode.input_socket(btree_index);
+ lf::InputSocket &lf_socket = group_output_lf_node.input(lf_index);
+ input_socket_map_.add(&bsocket, &lf_socket);
+ mapping_->dummy_socket_map.add(&bsocket, &lf_socket);
+ mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket);
+ }
+ }
+
+ void handle_group_node(const bNode &bnode)
+ {
+ const bNodeTree *group_btree = reinterpret_cast<bNodeTree *>(bnode.id);
+ if (group_btree == nullptr) {
+ return;
+ }
+ const GeometryNodesLazyFunctionGraphInfo *group_lf_graph_info =
+ ensure_geometry_nodes_lazy_function_graph(*group_btree);
+ if (group_lf_graph_info == nullptr) {
+ return;
+ }
+
+ Vector<const bNodeSocket *> used_inputs;
+ Vector<const bNodeSocket *> used_outputs;
+ auto lazy_function = std::make_unique<LazyFunctionForGroupNode>(
+ bnode, *group_lf_graph_info, used_inputs, used_outputs);
+ lf::FunctionNode &lf_node = lf_graph_->add_function(*lazy_function);
+ lf_graph_info_->functions.append(std::move(lazy_function));
+ for (const int i : used_inputs.index_range()) {
+ const bNodeSocket &bsocket = *used_inputs[i];
+ BLI_assert(!bsocket.is_multi_input());
+ lf::InputSocket &lf_socket = lf_node.input(i);
+ input_socket_map_.add(&bsocket, &lf_socket);
+ mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket);
+ }
+ for (const int i : used_outputs.index_range()) {
+ const bNodeSocket &bsocket = *used_outputs[i];
+ lf::OutputSocket &lf_socket = lf_node.output(i);
+ output_socket_map_.add_new(&bsocket, &lf_socket);
+ mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket);
+ }
+ mapping_->group_node_map.add(&bnode, &lf_node);
+ lf_graph_info_->num_inline_nodes_approximate +=
+ group_lf_graph_info->num_inline_nodes_approximate;
+ }
+
+ void handle_geometry_node(const bNode &bnode)
+ {
+ Vector<const bNodeSocket *> used_inputs;
+ Vector<const bNodeSocket *> used_outputs;
+ auto lazy_function = std::make_unique<LazyFunctionForGeometryNode>(
+ bnode, used_inputs, used_outputs);
+ lf::Node &lf_node = lf_graph_->add_function(*lazy_function);
+ lf_graph_info_->functions.append(std::move(lazy_function));
+
+ for (const int i : used_inputs.index_range()) {
+ const bNodeSocket &bsocket = *used_inputs[i];
+ lf::InputSocket &lf_socket = lf_node.input(i);
+
+ if (bsocket.is_multi_input()) {
+ auto multi_input_lazy_function = std::make_unique<LazyFunctionForMultiInput>(bsocket);
+ lf::Node &lf_multi_input_node = lf_graph_->add_function(*multi_input_lazy_function);
+ lf_graph_info_->functions.append(std::move(multi_input_lazy_function));
+ lf_graph_->add_link(lf_multi_input_node.output(0), lf_socket);
+ multi_input_socket_nodes_.add_new(&bsocket, &lf_multi_input_node);
+ for (lf::InputSocket *lf_multi_input_socket : lf_multi_input_node.inputs()) {
+ mapping_->bsockets_by_lf_socket_map.add(lf_multi_input_socket, &bsocket);
+ }
+ }
+ else {
+ input_socket_map_.add(&bsocket, &lf_socket);
+ mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket);
+ }
+ }
+ for (const int i : used_outputs.index_range()) {
+ const bNodeSocket &bsocket = *used_outputs[i];
+ lf::OutputSocket &lf_socket = lf_node.output(i);
+ output_socket_map_.add_new(&bsocket, &lf_socket);
+ mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket);
+ }
+ }
+
+ void handle_multi_function_node(const bNode &bnode, const NodeMultiFunctions::Item &fn_item)
+ {
+ Vector<const bNodeSocket *> used_inputs;
+ Vector<const bNodeSocket *> used_outputs;
+ auto lazy_function = std::make_unique<LazyFunctionForMultiFunctionNode>(
+ bnode, fn_item, used_inputs, used_outputs);
+ lf::Node &lf_node = lf_graph_->add_function(*lazy_function);
+ lf_graph_info_->functions.append(std::move(lazy_function));
+
+ for (const int i : used_inputs.index_range()) {
+ const bNodeSocket &bsocket = *used_inputs[i];
+ BLI_assert(!bsocket.is_multi_input());
+ lf::InputSocket &lf_socket = lf_node.input(i);
+ input_socket_map_.add(&bsocket, &lf_socket);
+ mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket);
+ }
+ for (const int i : used_outputs.index_range()) {
+ const bNodeSocket &bsocket = *used_outputs[i];
+ lf::OutputSocket &lf_socket = lf_node.output(i);
+ output_socket_map_.add(&bsocket, &lf_socket);
+ mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket);
+ }
+ }
+
+ void handle_viewer_node(const bNode &bnode)
+ {
+ Vector<const bNodeSocket *> used_inputs;
+ auto lazy_function = std::make_unique<LazyFunctionForViewerNode>(bnode, used_inputs);
+ lf::FunctionNode &lf_node = lf_graph_->add_function(*lazy_function);
+ lf_graph_info_->functions.append(std::move(lazy_function));
+
+ for (const int i : used_inputs.index_range()) {
+ const bNodeSocket &bsocket = *used_inputs[i];
+ lf::InputSocket &lf_socket = lf_node.input(i);
+ input_socket_map_.add(&bsocket, &lf_socket);
+ mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket);
+ }
+
+ mapping_->viewer_node_map.add(&bnode, &lf_node);
+ }
+
+ void handle_undefined_node(const bNode &bnode)
+ {
+ Vector<const bNodeSocket *> used_outputs;
+ auto lazy_function = std::make_unique<LazyFunctionForUndefinedNode>(bnode, used_outputs);
+ lf::FunctionNode &lf_node = lf_graph_->add_function(*lazy_function);
+ lf_graph_info_->functions.append(std::move(lazy_function));
+
+ for (const int i : used_outputs.index_range()) {
+ const bNodeSocket &bsocket = *used_outputs[i];
+ lf::OutputSocket &lf_socket = lf_node.output(i);
+ output_socket_map_.add(&bsocket, &lf_socket);
+ mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket);
+ }
+ }
+
+ void handle_links()
+ {
+ for (const auto item : output_socket_map_.items()) {
+ this->insert_links_from_socket(*item.key, *item.value);
+ }
+ }
+
+ void insert_links_from_socket(const bNodeSocket &from_bsocket, lf::OutputSocket &from_lf_socket)
+ {
+ const Span<const bNodeLink *> links_from_bsocket = from_bsocket.directly_linked_links();
+
+ struct TypeWithLinks {
+ const CPPType *type;
+ Vector<const bNodeLink *> links;
+ };
+
+ /* Group available target sockets by type so that they can be handled together. */
+ Vector<TypeWithLinks> types_with_links;
+ for (const bNodeLink *link : links_from_bsocket) {
+ if (link->is_muted()) {
+ continue;
+ }
+ if (!link->is_available()) {
+ continue;
+ }
+ const bNodeSocket &to_bsocket = *link->tosock;
+ const CPPType *to_type = get_socket_cpp_type(to_bsocket);
+ if (to_type == nullptr) {
+ continue;
+ }
+ bool inserted = false;
+ for (TypeWithLinks &types_with_links : types_with_links) {
+ if (types_with_links.type == to_type) {
+ types_with_links.links.append(link);
+ inserted = true;
+ break;
+ }
+ }
+ if (inserted) {
+ continue;
+ }
+ types_with_links.append({to_type, {link}});
+ }
+
+ for (const TypeWithLinks &type_with_links : types_with_links) {
+ const CPPType &to_type = *type_with_links.type;
+ const Span<const bNodeLink *> links = type_with_links.links;
+
+ Vector<const bNodeSocket *> target_bsockets;
+ for (const bNodeLink *link : links) {
+ target_bsockets.append(link->tosock);
+ }
+
+ lf::OutputSocket *converted_from_lf_socket = this->insert_type_conversion_if_necessary(
+ from_lf_socket, to_type, std::move(target_bsockets));
+
+ auto make_input_link_or_set_default = [&](lf::InputSocket &to_lf_socket) {
+ if (converted_from_lf_socket == nullptr) {
+ const void *default_value = to_type.default_value();
+ to_lf_socket.set_default_value(default_value);
+ }
+ else {
+ lf_graph_->add_link(*converted_from_lf_socket, to_lf_socket);
+ }
+ };
+
+ for (const bNodeLink *link : links) {
+ const bNodeSocket &to_bsocket = *link->tosock;
+ if (to_bsocket.is_multi_input()) {
+ /* TODO: Cache this index on the link. */
+ int link_index = 0;
+ for (const bNodeLink *multi_input_link : to_bsocket.directly_linked_links()) {
+ if (multi_input_link == link) {
+ break;
+ }
+ if (!multi_input_link->is_muted()) {
+ link_index++;
+ }
+ }
+ if (to_bsocket.owner_node().is_muted()) {
+ if (link_index == 0) {
+ for (lf::InputSocket *to_lf_socket : input_socket_map_.lookup(&to_bsocket)) {
+ make_input_link_or_set_default(*to_lf_socket);
+ }
+ }
+ }
+ else {
+ lf::Node *multi_input_lf_node = multi_input_socket_nodes_.lookup_default(&to_bsocket,
+ nullptr);
+ if (multi_input_lf_node == nullptr) {
+ continue;
+ }
+ make_input_link_or_set_default(multi_input_lf_node->input(link_index));
+ }
+ }
+ else {
+ for (lf::InputSocket *to_lf_socket : input_socket_map_.lookup(&to_bsocket)) {
+ make_input_link_or_set_default(*to_lf_socket);
+ }
+ }
+ }
+ }
+ }
+
+ lf::OutputSocket *insert_type_conversion_if_necessary(
+ lf::OutputSocket &from_socket,
+ const CPPType &to_type,
+ Vector<const bNodeSocket *> &&target_sockets)
+ {
+ const CPPType &from_type = from_socket.type();
+ if (from_type == to_type) {
+ return &from_socket;
+ }
+ const auto *from_field_type = dynamic_cast<const ValueOrFieldCPPType *>(&from_type);
+ const auto *to_field_type = dynamic_cast<const ValueOrFieldCPPType *>(&to_type);
+ if (from_field_type != nullptr && to_field_type != nullptr) {
+ const CPPType &from_base_type = from_field_type->base_type();
+ const CPPType &to_base_type = to_field_type->base_type();
+ if (conversions_->is_convertible(from_base_type, to_base_type)) {
+ const MultiFunction &multi_fn = *conversions_->get_conversion_multi_function(
+ MFDataType::ForSingle(from_base_type), MFDataType::ForSingle(to_base_type));
+ auto fn = std::make_unique<LazyFunctionForMultiFunctionConversion>(
+ multi_fn, *from_field_type, *to_field_type, std::move(target_sockets));
+ lf::Node &conversion_node = lf_graph_->add_function(*fn);
+ lf_graph_info_->functions.append(std::move(fn));
+ lf_graph_->add_link(from_socket, conversion_node.input(0));
+ return &conversion_node.output(0);
+ }
+ }
+ return nullptr;
+ }
+
+ void add_default_inputs()
+ {
+ for (auto item : input_socket_map_.items()) {
+ const bNodeSocket &bsocket = *item.key;
+ const Span<lf::InputSocket *> lf_sockets = item.value;
+ for (lf::InputSocket *lf_socket : lf_sockets) {
+ if (lf_socket->origin() != nullptr) {
+ /* Is linked already. */
+ continue;
+ }
+ this->add_default_input(bsocket, *lf_socket);
+ }
+ }
+ }
+
+ void add_default_input(const bNodeSocket &input_bsocket, lf::InputSocket &input_lf_socket)
+ {
+ if (this->try_add_implicit_input(input_bsocket, input_lf_socket)) {
+ return;
+ }
+ GMutablePointer value = get_socket_default_value(lf_graph_info_->allocator, input_bsocket);
+ if (value.get() == nullptr) {
+ /* Not possible to add a default value. */
+ return;
+ }
+ input_lf_socket.set_default_value(value.get());
+ if (!value.type()->is_trivially_destructible()) {
+ lf_graph_info_->values_to_destruct.append(value);
+ }
+ }
+
+ bool try_add_implicit_input(const bNodeSocket &input_bsocket, lf::InputSocket &input_lf_socket)
+ {
+ const bNode &bnode = input_bsocket.owner_node();
+ const NodeDeclaration *node_declaration = bnode.declaration();
+ if (node_declaration == nullptr) {
+ return false;
+ }
+ const SocketDeclaration &socket_declaration =
+ *node_declaration->inputs()[input_bsocket.index()];
+ if (socket_declaration.input_field_type() != InputSocketFieldType::Implicit) {
+ return false;
+ }
+ const CPPType &type = input_lf_socket.type();
+ std::function<void(void *)> init_fn = this->get_implicit_input_init_function(bnode,
+ input_bsocket);
+ if (!init_fn) {
+ return false;
+ }
+
+ auto lazy_function = std::make_unique<LazyFunctionForImplicitInput>(type, std::move(init_fn));
+ lf::Node &lf_node = lf_graph_->add_function(*lazy_function);
+ lf_graph_info_->functions.append(std::move(lazy_function));
+ lf_graph_->add_link(lf_node.output(0), input_lf_socket);
+ return true;
+ }
+
+ std::function<void(void *)> get_implicit_input_init_function(const bNode &bnode,
+ const bNodeSocket &bsocket)
+ {
+ const bNodeSocketType &socket_type = *bsocket.typeinfo;
+ if (socket_type.type == SOCK_VECTOR) {
+ if (bnode.type == GEO_NODE_SET_CURVE_HANDLES) {
+ StringRef side = ((NodeGeometrySetCurveHandlePositions *)bnode.storage)->mode ==
+ GEO_NODE_CURVE_HANDLE_LEFT ?
+ "handle_left" :
+ "handle_right";
+ return [side](void *r_value) {
+ new (r_value) ValueOrField<float3>(bke::AttributeFieldInput::Create<float3>(side));
+ };
+ }
+ else if (bnode.type == GEO_NODE_EXTRUDE_MESH) {
+ return [](void *r_value) {
+ new (r_value)
+ ValueOrField<float3>(Field<float3>(std::make_shared<bke::NormalFieldInput>()));
+ };
+ }
+ else {
+ return [](void *r_value) {
+ new (r_value) ValueOrField<float3>(bke::AttributeFieldInput::Create<float3>("position"));
+ };
+ }
+ }
+ else if (socket_type.type == SOCK_INT) {
+ if (ELEM(bnode.type, FN_NODE_RANDOM_VALUE, GEO_NODE_INSTANCE_ON_POINTS)) {
+ return [](void *r_value) {
+ new (r_value)
+ ValueOrField<int>(Field<int>(std::make_shared<bke::IDAttributeFieldInput>()));
+ };
+ }
+ else {
+ return [](void *r_value) {
+ new (r_value) ValueOrField<int>(Field<int>(std::make_shared<fn::IndexFieldInput>()));
+ };
+ }
+ }
+ return {};
+ }
+};
+
+const GeometryNodesLazyFunctionGraphInfo *ensure_geometry_nodes_lazy_function_graph(
+ const bNodeTree &btree)
+{
+ btree.ensure_topology_cache();
+ if (btree.has_available_link_cycle()) {
+ return nullptr;
+ }
+
+ std::unique_ptr<GeometryNodesLazyFunctionGraphInfo> &lf_graph_info_ptr =
+ btree.runtime->geometry_nodes_lazy_function_graph_info;
+
+ if (lf_graph_info_ptr) {
+ return lf_graph_info_ptr.get();
+ }
+ std::lock_guard lock{btree.runtime->geometry_nodes_lazy_function_graph_info_mutex};
+ if (lf_graph_info_ptr) {
+ return lf_graph_info_ptr.get();
+ }
+
+ auto lf_graph_info = std::make_unique<GeometryNodesLazyFunctionGraphInfo>();
+ GeometryNodesLazyFunctionGraphBuilder builder{btree, *lf_graph_info};
+ builder.build();
+
+ lf_graph_info_ptr = std::move(lf_graph_info);
+ return lf_graph_info_ptr.get();
+}
+
+GeometryNodesLazyFunctionLogger::GeometryNodesLazyFunctionLogger(
+ const GeometryNodesLazyFunctionGraphInfo &lf_graph_info)
+ : lf_graph_info_(lf_graph_info)
+{
+}
+
+void GeometryNodesLazyFunctionLogger::log_socket_value(
+ const fn::lazy_function::Socket &lf_socket,
+ const GPointer value,
+ const fn::lazy_function::Context &context) const
+{
+ const Span<const bNodeSocket *> bsockets =
+ lf_graph_info_.mapping.bsockets_by_lf_socket_map.lookup(&lf_socket);
+ if (bsockets.is_empty()) {
+ return;
+ }
+
+ GeoNodesLFUserData *user_data = dynamic_cast<GeoNodesLFUserData *>(context.user_data);
+ BLI_assert(user_data != nullptr);
+ if (user_data->modifier_data->eval_log == nullptr) {
+ return;
+ }
+ geo_eval_log::GeoTreeLogger &tree_logger =
+ user_data->modifier_data->eval_log->get_local_tree_logger(*user_data->compute_context);
+ for (const bNodeSocket *bsocket : bsockets) {
+ /* Avoid logging to some sockets when the same value will also be logged to a linked socket.
+ * This reduces the number of logged values without losing information. */
+ if (bsocket->is_input() && bsocket->is_directly_linked()) {
+ continue;
+ }
+ const bNode &bnode = bsocket->owner_node();
+ if (bnode.is_reroute()) {
+ continue;
+ }
+ tree_logger.log_value(bsocket->owner_node(), *bsocket, value);
+ }
+}
+
+static std::mutex dump_error_context_mutex;
+
+void GeometryNodesLazyFunctionLogger::dump_when_outputs_are_missing(
+ const lf::FunctionNode &node,
+ Span<const lf::OutputSocket *> missing_sockets,
+ const lf::Context &context) const
+{
+ std::lock_guard lock{dump_error_context_mutex};
+
+ GeoNodesLFUserData *user_data = dynamic_cast<GeoNodesLFUserData *>(context.user_data);
+ BLI_assert(user_data != nullptr);
+ user_data->compute_context->print_stack(std::cout, node.name());
+ std::cout << "Missing outputs:\n";
+ for (const lf::OutputSocket *socket : missing_sockets) {
+ std::cout << " " << socket->name() << "\n";
+ }
+}
+
+void GeometryNodesLazyFunctionLogger::dump_when_input_is_set_twice(
+ const lf::InputSocket &target_socket,
+ const lf::OutputSocket &from_socket,
+ const lf::Context &context) const
+{
+ std::lock_guard lock{dump_error_context_mutex};
+
+ std::stringstream ss;
+ ss << from_socket.node().name() << ":" << from_socket.name() << " -> "
+ << target_socket.node().name() << ":" << target_socket.name();
+
+ GeoNodesLFUserData *user_data = dynamic_cast<GeoNodesLFUserData *>(context.user_data);
+ BLI_assert(user_data != nullptr);
+ user_data->compute_context->print_stack(std::cout, ss.str());
+}
+
+Vector<const lf::FunctionNode *> GeometryNodesLazyFunctionSideEffectProvider::
+ get_nodes_with_side_effects(const lf::Context &context) const
+{
+ GeoNodesLFUserData *user_data = dynamic_cast<GeoNodesLFUserData *>(context.user_data);
+ BLI_assert(user_data != nullptr);
+ const ComputeContextHash &context_hash = user_data->compute_context->hash();
+ const GeoNodesModifierData &modifier_data = *user_data->modifier_data;
+ return modifier_data.side_effect_nodes->lookup(context_hash);
+}
+
+GeometryNodesLazyFunctionGraphInfo::GeometryNodesLazyFunctionGraphInfo() = default;
+GeometryNodesLazyFunctionGraphInfo::~GeometryNodesLazyFunctionGraphInfo()
+{
+ for (GMutablePointer &p : this->values_to_destruct) {
+ p.destruct();
+ }
+}
+
+[[maybe_unused]] static void add_thread_id_debug_message(
+ const GeometryNodesLazyFunctionGraphInfo &lf_graph_info,
+ const lf::FunctionNode &node,
+ const lf::Context &context)
+{
+ static std::atomic<int> thread_id_source = 0;
+ static thread_local const int thread_id = thread_id_source.fetch_add(1);
+ static thread_local const std::string thread_id_str = "Thread: " + std::to_string(thread_id);
+
+ GeoNodesLFUserData *user_data = dynamic_cast<GeoNodesLFUserData *>(context.user_data);
+ BLI_assert(user_data != nullptr);
+ if (user_data->modifier_data->eval_log == nullptr) {
+ return;
+ }
+ geo_eval_log::GeoTreeLogger &tree_logger =
+ user_data->modifier_data->eval_log->get_local_tree_logger(*user_data->compute_context);
+
+ /* Find corresponding node based on the socket mapping. */
+ auto check_sockets = [&](const Span<const lf::Socket *> lf_sockets) {
+ for (const lf::Socket *lf_socket : lf_sockets) {
+ const Span<const bNodeSocket *> bsockets =
+ lf_graph_info.mapping.bsockets_by_lf_socket_map.lookup(lf_socket);
+ if (!bsockets.is_empty()) {
+ const bNodeSocket &bsocket = *bsockets[0];
+ const bNode &bnode = bsocket.owner_node();
+ tree_logger.debug_messages.append(
+ {tree_logger.allocator->copy_string(bnode.name), thread_id_str});
+ return true;
+ }
+ }
+ return false;
+ };
+
+ if (check_sockets(node.inputs().cast<const lf::Socket *>())) {
+ return;
+ }
+ check_sockets(node.outputs().cast<const lf::Socket *>());
+}
+
+void GeometryNodesLazyFunctionLogger::log_before_node_execute(const lf::FunctionNode &node,
+ const lf::Params &UNUSED(params),
+ const lf::Context &context) const
+{
+ /* Enable this to see the threads that invoked a node. */
+ if constexpr (false) {
+ add_thread_id_debug_message(lf_graph_info_, node, context);
+ }
+}
+
+} // namespace blender::nodes
diff --git a/source/blender/nodes/intern/geometry_nodes_log.cc b/source/blender/nodes/intern/geometry_nodes_log.cc
new file mode 100644
index 00000000000..110573c9119
--- /dev/null
+++ b/source/blender/nodes/intern/geometry_nodes_log.cc
@@ -0,0 +1,610 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "NOD_geometry_nodes_lazy_function.hh"
+#include "NOD_geometry_nodes_log.hh"
+
+#include "BKE_compute_contexts.hh"
+#include "BKE_curves.hh"
+#include "BKE_node_runtime.hh"
+
+#include "FN_field_cpp_type.hh"
+
+#include "DNA_modifier_types.h"
+#include "DNA_space_types.h"
+
+namespace blender::nodes::geo_eval_log {
+
+using fn::FieldInput;
+using fn::FieldInputs;
+
+GenericValueLog::~GenericValueLog()
+{
+ this->value.destruct();
+}
+
+FieldInfoLog::FieldInfoLog(const GField &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) {
+ this->input_tooltips.append(field_input.socket_inspection_name());
+ }
+}
+
+GeometryInfoLog::GeometryInfoLog(const GeometrySet &geometry_set)
+{
+ static std::array all_component_types = {GEO_COMPONENT_TYPE_CURVE,
+ GEO_COMPONENT_TYPE_INSTANCES,
+ 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 bke::AttributeMetaData &meta_data,
+ const GeometryComponent &UNUSED(component)) {
+ if (attribute_id.is_named() && names.add(attribute_id.name())) {
+ this->attributes.append({attribute_id.name(), meta_data.domain, meta_data.data_type});
+ }
+ });
+
+ for (const GeometryComponent *component : geometry_set.get_components_for_read()) {
+ this->component_types.append(component->type());
+ switch (component->type()) {
+ case GEO_COMPONENT_TYPE_MESH: {
+ const MeshComponent &mesh_component = *(const MeshComponent *)component;
+ MeshInfo &info = this->mesh_info.emplace();
+ info.verts_num = mesh_component.attribute_domain_size(ATTR_DOMAIN_POINT);
+ info.edges_num = mesh_component.attribute_domain_size(ATTR_DOMAIN_EDGE);
+ info.faces_num = mesh_component.attribute_domain_size(ATTR_DOMAIN_FACE);
+ break;
+ }
+ case GEO_COMPONENT_TYPE_CURVE: {
+ const CurveComponent &curve_component = *(const CurveComponent *)component;
+ CurveInfo &info = this->curve_info.emplace();
+ info.splines_num = curve_component.attribute_domain_size(ATTR_DOMAIN_CURVE);
+ break;
+ }
+ case GEO_COMPONENT_TYPE_POINT_CLOUD: {
+ const PointCloudComponent &pointcloud_component = *(const PointCloudComponent *)component;
+ PointCloudInfo &info = this->pointcloud_info.emplace();
+ info.points_num = pointcloud_component.attribute_domain_size(ATTR_DOMAIN_POINT);
+ break;
+ }
+ case GEO_COMPONENT_TYPE_INSTANCES: {
+ const InstancesComponent &instances_component = *(const InstancesComponent *)component;
+ InstancesInfo &info = this->instances_info.emplace();
+ info.instances_num = instances_component.instances_num();
+ break;
+ }
+ case GEO_COMPONENT_TYPE_EDIT: {
+ const GeometryComponentEditData &edit_component = *(
+ const GeometryComponentEditData *)component;
+ if (const bke::CurvesEditHints *curve_edit_hints =
+ edit_component.curves_edit_hints_.get()) {
+ EditDataInfo &info = this->edit_data_info.emplace();
+ info.has_deform_matrices = curve_edit_hints->deform_mats.has_value();
+ info.has_deformed_positions = curve_edit_hints->positions.has_value();
+ }
+ break;
+ }
+ case GEO_COMPONENT_TYPE_VOLUME: {
+ break;
+ }
+ }
+ }
+}
+
+/* Avoid generating these in every translation unit. */
+GeoModifierLog::GeoModifierLog() = default;
+GeoModifierLog::~GeoModifierLog() = default;
+
+GeoTreeLogger::GeoTreeLogger() = default;
+GeoTreeLogger::~GeoTreeLogger() = default;
+
+GeoNodeLog::GeoNodeLog() = default;
+GeoNodeLog::~GeoNodeLog() = default;
+
+GeoTreeLog::GeoTreeLog(GeoModifierLog *modifier_log, Vector<GeoTreeLogger *> tree_loggers)
+ : modifier_log_(modifier_log), tree_loggers_(std::move(tree_loggers))
+{
+ for (GeoTreeLogger *tree_logger : tree_loggers_) {
+ for (const ComputeContextHash &hash : tree_logger->children_hashes) {
+ children_hashes_.add(hash);
+ }
+ }
+}
+
+GeoTreeLog::~GeoTreeLog() = default;
+
+void GeoTreeLogger::log_value(const bNode &node, const bNodeSocket &socket, const GPointer value)
+{
+ const CPPType &type = *value.type();
+
+ auto store_logged_value = [&](destruct_ptr<ValueLog> value_log) {
+ auto &socket_values = socket.in_out == SOCK_IN ? this->input_socket_values :
+ this->output_socket_values;
+ socket_values.append({this->allocator->copy_string(node.name),
+ this->allocator->copy_string(socket.identifier),
+ std::move(value_log)});
+ };
+
+ auto log_generic_value = [&](const CPPType &type, const void *value) {
+ void *buffer = this->allocator->allocate(type.size(), type.alignment());
+ type.copy_construct(value, buffer);
+ store_logged_value(this->allocator->construct<GenericValueLog>(GMutablePointer{type, buffer}));
+ };
+
+ if (type.is<GeometrySet>()) {
+ const GeometrySet &geometry = *value.get<GeometrySet>();
+ store_logged_value(this->allocator->construct<GeometryInfoLog>(geometry));
+ }
+ else if (const auto *value_or_field_type = dynamic_cast<const fn::ValueOrFieldCPPType *>(
+ &type)) {
+ const void *value_or_field = value.get();
+ const CPPType &base_type = value_or_field_type->base_type();
+ if (value_or_field_type->is_field(value_or_field)) {
+ const GField *field = value_or_field_type->get_field_ptr(value_or_field);
+ if (field->node().depends_on_input()) {
+ store_logged_value(this->allocator->construct<FieldInfoLog>(*field));
+ }
+ else {
+ BUFFER_FOR_CPP_TYPE_VALUE(base_type, value);
+ fn::evaluate_constant_field(*field, value);
+ log_generic_value(base_type, value);
+ }
+ }
+ else {
+ const void *value = value_or_field_type->get_value_ptr(value_or_field);
+ log_generic_value(base_type, value);
+ }
+ }
+ else {
+ log_generic_value(type, value.get());
+ }
+}
+
+void GeoTreeLogger::log_viewer_node(const bNode &viewer_node,
+ const GeometrySet &geometry,
+ const GField &field)
+{
+ destruct_ptr<ViewerNodeLog> log = this->allocator->construct<ViewerNodeLog>();
+ log->geometry = geometry;
+ log->field = field;
+ log->geometry.ensure_owns_direct_data();
+ this->viewer_node_logs.append({this->allocator->copy_string(viewer_node.name), std::move(log)});
+}
+
+void GeoTreeLog::ensure_node_warnings()
+{
+ if (reduced_node_warnings_) {
+ return;
+ }
+ for (GeoTreeLogger *tree_logger : tree_loggers_) {
+ for (const GeoTreeLogger::WarningWithNode &warnings : tree_logger->node_warnings) {
+ this->nodes.lookup_or_add_default(warnings.node_name).warnings.append(warnings.warning);
+ this->all_warnings.append(warnings.warning);
+ }
+ }
+ for (const ComputeContextHash &child_hash : children_hashes_) {
+ GeoTreeLog &child_log = modifier_log_->get_tree_log(child_hash);
+ child_log.ensure_node_warnings();
+ const std::optional<std::string> &group_node_name =
+ child_log.tree_loggers_[0]->group_node_name;
+ if (group_node_name.has_value()) {
+ this->nodes.lookup_or_add_default(*group_node_name).warnings.extend(child_log.all_warnings);
+ }
+ this->all_warnings.extend(child_log.all_warnings);
+ }
+ reduced_node_warnings_ = true;
+}
+
+void GeoTreeLog::ensure_node_run_time()
+{
+ if (reduced_node_run_times_) {
+ return;
+ }
+ for (GeoTreeLogger *tree_logger : tree_loggers_) {
+ for (const GeoTreeLogger::NodeExecutionTime &timings : tree_logger->node_execution_times) {
+ const std::chrono::nanoseconds duration = timings.end - timings.start;
+ this->nodes.lookup_or_add_default_as(timings.node_name).run_time += duration;
+ this->run_time_sum += duration;
+ }
+ }
+ for (const ComputeContextHash &child_hash : children_hashes_) {
+ GeoTreeLog &child_log = modifier_log_->get_tree_log(child_hash);
+ child_log.ensure_node_run_time();
+ const std::optional<std::string> &group_node_name =
+ child_log.tree_loggers_[0]->group_node_name;
+ if (group_node_name.has_value()) {
+ this->nodes.lookup_or_add_default(*group_node_name).run_time += child_log.run_time_sum;
+ }
+ this->run_time_sum += child_log.run_time_sum;
+ }
+ reduced_node_run_times_ = true;
+}
+
+void GeoTreeLog::ensure_socket_values()
+{
+ if (reduced_socket_values_) {
+ return;
+ }
+ for (GeoTreeLogger *tree_logger : tree_loggers_) {
+ for (const GeoTreeLogger::SocketValueLog &value_log_data : tree_logger->input_socket_values) {
+ this->nodes.lookup_or_add_as(value_log_data.node_name)
+ .input_values_.add(value_log_data.socket_identifier, value_log_data.value.get());
+ }
+ for (const GeoTreeLogger::SocketValueLog &value_log_data : tree_logger->output_socket_values) {
+ this->nodes.lookup_or_add_as(value_log_data.node_name)
+ .output_values_.add(value_log_data.socket_identifier, value_log_data.value.get());
+ }
+ }
+ reduced_socket_values_ = true;
+}
+
+void GeoTreeLog::ensure_viewer_node_logs()
+{
+ if (reduced_viewer_node_logs_) {
+ return;
+ }
+ for (GeoTreeLogger *tree_logger : tree_loggers_) {
+ for (const GeoTreeLogger::ViewerNodeLogWithNode &viewer_log : tree_logger->viewer_node_logs) {
+ this->viewer_node_logs.add(viewer_log.node_name, viewer_log.viewer_log.get());
+ }
+ }
+ reduced_viewer_node_logs_ = true;
+}
+
+void GeoTreeLog::ensure_existing_attributes()
+{
+ if (reduced_existing_attributes_) {
+ return;
+ }
+ this->ensure_socket_values();
+
+ Set<StringRef> names;
+
+ auto handle_value_log = [&](const ValueLog &value_log) {
+ const GeometryInfoLog *geo_log = dynamic_cast<const GeometryInfoLog *>(&value_log);
+ if (geo_log == nullptr) {
+ return;
+ }
+ for (const GeometryAttributeInfo &attribute : geo_log->attributes) {
+ if (names.add(attribute.name)) {
+ this->existing_attributes.append(&attribute);
+ }
+ }
+ };
+
+ for (const GeoNodeLog &node_log : this->nodes.values()) {
+ for (const ValueLog *value_log : node_log.input_values_.values()) {
+ handle_value_log(*value_log);
+ }
+ for (const ValueLog *value_log : node_log.output_values_.values()) {
+ handle_value_log(*value_log);
+ }
+ }
+ reduced_existing_attributes_ = true;
+}
+
+void GeoTreeLog::ensure_used_named_attributes()
+{
+ if (reduced_used_named_attributes_) {
+ return;
+ }
+
+ auto add_attribute = [&](const StringRefNull node_name,
+ const StringRefNull attribute_name,
+ const NamedAttributeUsage &usage) {
+ this->nodes.lookup_or_add_default(node_name).used_named_attributes.lookup_or_add(
+ attribute_name, usage) |= usage;
+ this->used_named_attributes.lookup_or_add_as(attribute_name, usage) |= usage;
+ };
+
+ for (GeoTreeLogger *tree_logger : tree_loggers_) {
+ for (const GeoTreeLogger::AttributeUsageWithNode &item : tree_logger->used_named_attributes) {
+ add_attribute(item.node_name, item.attribute_name, item.usage);
+ }
+ }
+ for (const ComputeContextHash &child_hash : children_hashes_) {
+ GeoTreeLog &child_log = modifier_log_->get_tree_log(child_hash);
+ child_log.ensure_used_named_attributes();
+ if (const std::optional<std::string> &group_node_name =
+ child_log.tree_loggers_[0]->group_node_name) {
+ for (const auto &item : child_log.used_named_attributes.items()) {
+ add_attribute(*group_node_name, item.key, item.value);
+ }
+ }
+ }
+ reduced_used_named_attributes_ = true;
+}
+
+void GeoTreeLog::ensure_debug_messages()
+{
+ if (reduced_debug_messages_) {
+ return;
+ }
+ for (GeoTreeLogger *tree_logger : tree_loggers_) {
+ for (const GeoTreeLogger::DebugMessage &debug_message : tree_logger->debug_messages) {
+ this->nodes.lookup_or_add_as(debug_message.node_name)
+ .debug_messages.append(debug_message.message);
+ }
+ }
+ reduced_debug_messages_ = true;
+}
+
+ValueLog *GeoTreeLog::find_socket_value_log(const bNodeSocket &query_socket)
+{
+ /**
+ * Geometry nodes does not log values for every socket. That would produce a lot of redundant
+ * data,because often many linked sockets have the same value. To find the logged value for a
+ * socket one might have to look at linked sockets as well.
+ */
+
+ BLI_assert(reduced_socket_values_);
+ if (query_socket.is_multi_input()) {
+ /* Not supported currently. */
+ return nullptr;
+ }
+
+ Set<const bNodeSocket *> added_sockets;
+ Stack<const bNodeSocket *> sockets_to_check;
+ sockets_to_check.push(&query_socket);
+ added_sockets.add(&query_socket);
+
+ while (!sockets_to_check.is_empty()) {
+ const bNodeSocket &socket = *sockets_to_check.pop();
+ const bNode &node = socket.owner_node();
+ if (GeoNodeLog *node_log = this->nodes.lookup_ptr(node.name)) {
+ ValueLog *value_log = socket.is_input() ?
+ node_log->input_values_.lookup_default(socket.identifier,
+ nullptr) :
+ node_log->output_values_.lookup_default(socket.identifier,
+ nullptr);
+ if (value_log != nullptr) {
+ return value_log;
+ }
+ }
+
+ if (socket.is_input()) {
+ const Span<const bNodeLink *> links = socket.directly_linked_links();
+ for (const bNodeLink *link : links) {
+ const bNodeSocket &from_socket = *link->fromsock;
+ if (added_sockets.add(&from_socket)) {
+ sockets_to_check.push(&from_socket);
+ }
+ }
+ }
+ else {
+ if (node.is_reroute()) {
+ const bNodeSocket &input_socket = node.input_socket(0);
+ if (added_sockets.add(&input_socket)) {
+ sockets_to_check.push(&input_socket);
+ }
+ const Span<const bNodeLink *> links = input_socket.directly_linked_links();
+ for (const bNodeLink *link : links) {
+ const bNodeSocket &from_socket = *link->fromsock;
+ if (added_sockets.add(&from_socket)) {
+ sockets_to_check.push(&from_socket);
+ }
+ }
+ }
+ else if (node.is_muted()) {
+ if (const bNodeSocket *input_socket = socket.internal_link_input()) {
+ if (added_sockets.add(input_socket)) {
+ sockets_to_check.push(input_socket);
+ }
+ const Span<const bNodeLink *> links = input_socket->directly_linked_links();
+ for (const bNodeLink *link : links) {
+ const bNodeSocket &from_socket = *link->fromsock;
+ if (added_sockets.add(&from_socket)) {
+ sockets_to_check.push(&from_socket);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return nullptr;
+}
+
+GeoTreeLogger &GeoModifierLog::get_local_tree_logger(const ComputeContext &compute_context)
+{
+ LocalData &local_data = data_per_thread_.local();
+ Map<ComputeContextHash, destruct_ptr<GeoTreeLogger>> &local_tree_loggers =
+ local_data.tree_logger_by_context;
+ destruct_ptr<GeoTreeLogger> &tree_logger_ptr = local_tree_loggers.lookup_or_add_default(
+ compute_context.hash());
+ if (tree_logger_ptr) {
+ return *tree_logger_ptr;
+ }
+ tree_logger_ptr = local_data.allocator.construct<GeoTreeLogger>();
+ GeoTreeLogger &tree_logger = *tree_logger_ptr;
+ tree_logger.allocator = &local_data.allocator;
+ const ComputeContext *parent_compute_context = compute_context.parent();
+ if (parent_compute_context != nullptr) {
+ tree_logger.parent_hash = parent_compute_context->hash();
+ GeoTreeLogger &parent_logger = this->get_local_tree_logger(*parent_compute_context);
+ parent_logger.children_hashes.append(compute_context.hash());
+ }
+ if (const bke::NodeGroupComputeContext *node_group_compute_context =
+ dynamic_cast<const bke::NodeGroupComputeContext *>(&compute_context)) {
+ tree_logger.group_node_name.emplace(node_group_compute_context->node_name());
+ }
+ return tree_logger;
+}
+
+GeoTreeLog &GeoModifierLog::get_tree_log(const ComputeContextHash &compute_context_hash)
+{
+ GeoTreeLog &reduced_tree_log = *tree_logs_.lookup_or_add_cb(compute_context_hash, [&]() {
+ Vector<GeoTreeLogger *> tree_logs;
+ for (LocalData &local_data : data_per_thread_) {
+ destruct_ptr<GeoTreeLogger> *tree_log = local_data.tree_logger_by_context.lookup_ptr(
+ compute_context_hash);
+ if (tree_log != nullptr) {
+ tree_logs.append(tree_log->get());
+ }
+ }
+ return std::make_unique<GeoTreeLog>(this, std::move(tree_logs));
+ });
+ return reduced_tree_log;
+}
+
+struct ObjectAndModifier {
+ const Object *object;
+ const NodesModifierData *nmd;
+};
+
+static std::optional<ObjectAndModifier> get_modifier_for_node_editor(const SpaceNode &snode)
+{
+ if (snode.id == nullptr) {
+ return std::nullopt;
+ }
+ if (GS(snode.id->name) != ID_OB) {
+ return std::nullopt;
+ }
+ const Object *object = reinterpret_cast<Object *>(snode.id);
+ const NodesModifierData *used_modifier = nullptr;
+ if (snode.flag & SNODE_PIN) {
+ LISTBASE_FOREACH (const ModifierData *, md, &object->modifiers) {
+ if (md->type == eModifierType_Nodes) {
+ const NodesModifierData *nmd = reinterpret_cast<const NodesModifierData *>(md);
+ /* Would be good to store the name of the pinned modifier in the node editor. */
+ if (nmd->node_group == snode.nodetree) {
+ used_modifier = nmd;
+ break;
+ }
+ }
+ }
+ }
+ else {
+ LISTBASE_FOREACH (const ModifierData *, md, &object->modifiers) {
+ if (md->type == eModifierType_Nodes) {
+ const NodesModifierData *nmd = reinterpret_cast<const NodesModifierData *>(md);
+ if (nmd->node_group == snode.nodetree) {
+ if (md->flag & eModifierFlag_Active) {
+ used_modifier = nmd;
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (used_modifier == nullptr) {
+ return std::nullopt;
+ }
+ return ObjectAndModifier{object, used_modifier};
+}
+
+GeoTreeLog *GeoModifierLog::get_tree_log_for_node_editor(const SpaceNode &snode)
+{
+ std::optional<ObjectAndModifier> object_and_modifier = get_modifier_for_node_editor(snode);
+ if (!object_and_modifier) {
+ return nullptr;
+ }
+ GeoModifierLog *modifier_log = static_cast<GeoModifierLog *>(
+ object_and_modifier->nmd->runtime_eval_log);
+ if (modifier_log == nullptr) {
+ return nullptr;
+ }
+ Vector<const bNodeTreePath *> tree_path = snode.treepath;
+ if (tree_path.is_empty()) {
+ return nullptr;
+ }
+ ComputeContextBuilder compute_context_builder;
+ compute_context_builder.push<bke::ModifierComputeContext>(
+ object_and_modifier->nmd->modifier.name);
+ for (const bNodeTreePath *path_item : tree_path.as_span().drop_front(1)) {
+ compute_context_builder.push<bke::NodeGroupComputeContext>(path_item->node_name);
+ }
+ return &modifier_log->get_tree_log(compute_context_builder.hash());
+}
+
+const ViewerNodeLog *GeoModifierLog::find_viewer_node_log_for_spreadsheet(
+ const SpaceSpreadsheet &sspreadsheet)
+{
+ Vector<const SpreadsheetContext *> context_path = sspreadsheet.context_path;
+ if (context_path.size() < 3) {
+ return nullptr;
+ }
+ if (context_path[0]->type != SPREADSHEET_CONTEXT_OBJECT) {
+ return nullptr;
+ }
+ if (context_path[1]->type != SPREADSHEET_CONTEXT_MODIFIER) {
+ return nullptr;
+ }
+ const SpreadsheetContextObject *object_context =
+ reinterpret_cast<const SpreadsheetContextObject *>(context_path[0]);
+ const SpreadsheetContextModifier *modifier_context =
+ reinterpret_cast<const SpreadsheetContextModifier *>(context_path[1]);
+ if (object_context->object == nullptr) {
+ return nullptr;
+ }
+ NodesModifierData *nmd = nullptr;
+ LISTBASE_FOREACH (ModifierData *, md, &object_context->object->modifiers) {
+ if (STREQ(md->name, modifier_context->modifier_name)) {
+ if (md->type == eModifierType_Nodes) {
+ nmd = reinterpret_cast<NodesModifierData *>(md);
+ }
+ }
+ }
+ if (nmd == nullptr) {
+ return nullptr;
+ }
+ if (nmd->runtime_eval_log == nullptr) {
+ return nullptr;
+ }
+ nodes::geo_eval_log::GeoModifierLog *modifier_log =
+ static_cast<nodes::geo_eval_log::GeoModifierLog *>(nmd->runtime_eval_log);
+
+ ComputeContextBuilder compute_context_builder;
+ compute_context_builder.push<bke::ModifierComputeContext>(modifier_context->modifier_name);
+ for (const SpreadsheetContext *context : context_path.as_span().drop_front(2).drop_back(1)) {
+ if (context->type != SPREADSHEET_CONTEXT_NODE) {
+ return nullptr;
+ }
+ const SpreadsheetContextNode &node_context = *reinterpret_cast<const SpreadsheetContextNode *>(
+ context);
+ compute_context_builder.push<bke::NodeGroupComputeContext>(node_context.node_name);
+ }
+ const ComputeContextHash context_hash = compute_context_builder.hash();
+ nodes::geo_eval_log::GeoTreeLog &tree_log = modifier_log->get_tree_log(context_hash);
+ tree_log.ensure_viewer_node_logs();
+
+ const SpreadsheetContext *last_context = context_path.last();
+ if (last_context->type != SPREADSHEET_CONTEXT_NODE) {
+ return nullptr;
+ }
+ const SpreadsheetContextNode &last_node_context =
+ *reinterpret_cast<const SpreadsheetContextNode *>(last_context);
+ const ViewerNodeLog *viewer_log = tree_log.viewer_node_logs.lookup_default(
+ last_node_context.node_name, nullptr);
+ return viewer_log;
+}
+
+} // namespace blender::nodes::geo_eval_log
diff --git a/source/blender/nodes/intern/node_common.cc b/source/blender/nodes/intern/node_common.cc
index 6402ec3f3d6..1f085375329 100644
--- a/source/blender/nodes/intern/node_common.cc
+++ b/source/blender/nodes/intern/node_common.cc
@@ -82,7 +82,9 @@ bool node_group_poll_instance(bNode *node, bNodeTree *nodetree, const char **dis
return false;
}
-bool nodeGroupPoll(bNodeTree *nodetree, bNodeTree *grouptree, const char **r_disabled_hint)
+bool nodeGroupPoll(const bNodeTree *nodetree,
+ const bNodeTree *grouptree,
+ const char **r_disabled_hint)
{
bool valid = true;
@@ -94,13 +96,16 @@ bool nodeGroupPoll(bNodeTree *nodetree, bNodeTree *grouptree, const char **r_dis
}
if (nodetree == grouptree) {
- *r_disabled_hint = TIP_("Nesting a node group inside of itself is not allowed");
+ if (r_disabled_hint) {
+ *r_disabled_hint = TIP_("Nesting a node group inside of itself is not allowed");
+ }
return false;
}
- LISTBASE_FOREACH (bNode *, node, &grouptree->nodes) {
+ LISTBASE_FOREACH (const bNode *, node, &grouptree->nodes) {
if (node->typeinfo->poll_instance &&
- !node->typeinfo->poll_instance(node, nodetree, r_disabled_hint)) {
+ !node->typeinfo->poll_instance(
+ const_cast<bNode *>(node), const_cast<bNodeTree *>(nodetree), r_disabled_hint)) {
valid = false;
break;
}
diff --git a/source/blender/nodes/intern/node_geometry_exec.cc b/source/blender/nodes/intern/node_geometry_exec.cc
index c6ebc22c43c..1de92fa8409 100644
--- a/source/blender/nodes/intern/node_geometry_exec.cc
+++ b/source/blender/nodes/intern/node_geometry_exec.cc
@@ -11,34 +11,31 @@
#include "node_geometry_util.hh"
-using blender::nodes::geometry_nodes_eval_log::LocalGeoLogger;
-
namespace blender::nodes {
-void GeoNodeExecParams::error_message_add(const NodeWarningType type, std::string message) const
+void GeoNodeExecParams::error_message_add(const NodeWarningType type,
+ const StringRef message) const
{
- if (provider_->logger == nullptr) {
- return;
+ if (geo_eval_log::GeoTreeLogger *tree_logger = this->get_local_tree_logger()) {
+ tree_logger->node_warnings.append({tree_logger->allocator->copy_string(node_.name),
+ {type, tree_logger->allocator->copy_string(message)}});
}
- LocalGeoLogger &local_logger = provider_->logger->local();
- local_logger.log_node_warning(provider_->dnode, type, std::move(message));
}
-void GeoNodeExecParams::used_named_attribute(std::string attribute_name,
- const eNamedAttrUsage usage)
+void GeoNodeExecParams::used_named_attribute(const StringRef attribute_name,
+ const NamedAttributeUsage usage)
{
- if (provider_->logger == nullptr) {
- return;
+ if (geo_eval_log::GeoTreeLogger *tree_logger = this->get_local_tree_logger()) {
+ tree_logger->used_named_attributes.append({tree_logger->allocator->copy_string(node_.name),
+ tree_logger->allocator->copy_string(attribute_name),
+ usage});
}
- LocalGeoLogger &local_logger = provider_->logger->local();
- local_logger.log_used_named_attribute(provider_->dnode, std::move(attribute_name), usage);
}
void GeoNodeExecParams::check_input_geometry_set(StringRef identifier,
const GeometrySet &geometry_set) const
{
- const SocketDeclaration &decl =
- *provider_->dnode->input_by_identifier(identifier).bsocket()->runtime->declaration;
+ const SocketDeclaration &decl = *node_.input_by_identifier(identifier).runtime->declaration;
const decl::Geometry *geo_decl = dynamic_cast<const decl::Geometry *>(&decl);
if (geo_decl == nullptr) {
return;
@@ -118,9 +115,9 @@ void GeoNodeExecParams::check_output_geometry_set(const GeometrySet &geometry_se
const bNodeSocket *GeoNodeExecParams::find_available_socket(const StringRef name) const
{
- for (const InputSocketRef *socket : provider_->dnode->inputs()) {
- if (socket->is_available() && socket->name() == name) {
- return socket->bsocket();
+ for (const bNodeSocket *socket : node_.input_sockets()) {
+ if (socket->is_available() && socket->name == name) {
+ return socket;
}
}
@@ -129,21 +126,21 @@ const bNodeSocket *GeoNodeExecParams::find_available_socket(const StringRef name
std::string GeoNodeExecParams::attribute_producer_name() const
{
- return provider_->dnode->label_or_name() + TIP_(" node");
+ return node_.label_or_name() + TIP_(" node");
}
void GeoNodeExecParams::set_default_remaining_outputs()
{
- provider_->set_default_remaining_outputs();
+ params_.set_default_remaining_outputs();
}
void GeoNodeExecParams::check_input_access(StringRef identifier,
const CPPType *requested_type) const
{
- bNodeSocket *found_socket = nullptr;
- for (const InputSocketRef *socket : provider_->dnode->inputs()) {
- if (socket->identifier() == identifier) {
- found_socket = socket->bsocket();
+ const bNodeSocket *found_socket = nullptr;
+ for (const bNodeSocket *socket : node_.input_sockets()) {
+ if (socket->identifier == identifier) {
+ found_socket = socket;
break;
}
}
@@ -151,9 +148,9 @@ void GeoNodeExecParams::check_input_access(StringRef identifier,
if (found_socket == nullptr) {
std::cout << "Did not find an input socket with the identifier '" << identifier << "'.\n";
std::cout << "Possible identifiers are: ";
- for (const InputSocketRef *socket : provider_->dnode->inputs()) {
+ for (const bNodeSocket *socket : node_.input_sockets()) {
if (socket->is_available()) {
- std::cout << "'" << socket->identifier() << "', ";
+ std::cout << "'" << socket->identifier << "', ";
}
}
std::cout << "\n";
@@ -164,13 +161,7 @@ void GeoNodeExecParams::check_input_access(StringRef identifier,
<< "' is disabled.\n";
BLI_assert_unreachable();
}
- else if (!provider_->can_get_input(identifier)) {
- std::cout << "The identifier '" << identifier
- << "' is valid, but there is no value for it anymore.\n";
- std::cout << "Most likely it has been extracted before.\n";
- BLI_assert_unreachable();
- }
- else if (requested_type != nullptr) {
+ else if (requested_type != nullptr && (found_socket->flag & SOCK_MULTI_INPUT) == 0) {
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 '"
@@ -182,10 +173,10 @@ void GeoNodeExecParams::check_input_access(StringRef identifier,
void GeoNodeExecParams::check_output_access(StringRef identifier, const CPPType &value_type) const
{
- bNodeSocket *found_socket = nullptr;
- for (const OutputSocketRef *socket : provider_->dnode->outputs()) {
- if (socket->identifier() == identifier) {
- found_socket = socket->bsocket();
+ const bNodeSocket *found_socket = nullptr;
+ for (const bNodeSocket *socket : node_.output_sockets()) {
+ if (socket->identifier == identifier) {
+ found_socket = socket;
break;
}
}
@@ -193,9 +184,9 @@ void GeoNodeExecParams::check_output_access(StringRef identifier, const CPPType
if (found_socket == nullptr) {
std::cout << "Did not find an output socket with the identifier '" << identifier << "'.\n";
std::cout << "Possible identifiers are: ";
- for (const OutputSocketRef *socket : provider_->dnode->outputs()) {
+ for (const bNodeSocket *socket : node_.output_sockets()) {
if (socket->is_available()) {
- std::cout << "'" << socket->identifier() << "', ";
+ std::cout << "'" << socket->identifier << "', ";
}
}
std::cout << "\n";
@@ -206,7 +197,7 @@ void GeoNodeExecParams::check_output_access(StringRef identifier, const CPPType
<< "' is disabled.\n";
BLI_assert_unreachable();
}
- else if (!provider_->can_set_output(identifier)) {
+ else if (params_.output_was_set(this->get_output_index(identifier))) {
std::cout << "The identifier '" << identifier << "' has been set already.\n";
BLI_assert_unreachable();
}
diff --git a/source/blender/nodes/intern/node_multi_function.cc b/source/blender/nodes/intern/node_multi_function.cc
index 13bfdfbfac1..d731fe8f877 100644
--- a/source/blender/nodes/intern/node_multi_function.cc
+++ b/source/blender/nodes/intern/node_multi_function.cc
@@ -2,22 +2,22 @@
#include "NOD_multi_function.hh"
+#include "BKE_node.h"
+#include "BKE_node_runtime.hh"
+
namespace blender::nodes {
-NodeMultiFunctions::NodeMultiFunctions(const DerivedNodeTree &tree)
+NodeMultiFunctions::NodeMultiFunctions(const bNodeTree &tree)
{
- for (const NodeTreeRef *tree_ref : tree.used_node_tree_refs()) {
- bNodeTree *btree = tree_ref->btree();
- for (const NodeRef *node : tree_ref->nodes()) {
- bNode *bnode = node->bnode();
- if (bnode->typeinfo->build_multi_function == nullptr) {
- continue;
- }
- NodeMultiFunctionBuilder builder{*bnode, *btree};
- bnode->typeinfo->build_multi_function(builder);
- if (builder.built_fn_ != nullptr) {
- map_.add_new(bnode, {builder.built_fn_, std::move(builder.owned_built_fn_)});
- }
+ tree.ensure_topology_cache();
+ for (const bNode *bnode : tree.all_nodes()) {
+ if (bnode->typeinfo->build_multi_function == nullptr) {
+ continue;
+ }
+ NodeMultiFunctionBuilder builder{*bnode, tree};
+ bnode->typeinfo->build_multi_function(builder);
+ 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_tree_ref.cc b/source/blender/nodes/intern/node_tree_ref.cc
deleted file mode 100644
index 05e7fe33776..00000000000
--- a/source/blender/nodes/intern/node_tree_ref.cc
+++ /dev/null
@@ -1,679 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-
-#include <mutex>
-
-#include "NOD_node_tree_ref.hh"
-
-#include "BLI_dot_export.hh"
-#include "BLI_stack.hh"
-
-#include "RNA_prototypes.h"
-
-namespace blender::nodes {
-
-NodeTreeRef::NodeTreeRef(bNodeTree *btree) : btree_(btree)
-{
- Map<bNode *, NodeRef *> node_mapping;
-
- LISTBASE_FOREACH (bNode *, bnode, &btree->nodes) {
- NodeRef &node = *allocator_.construct<NodeRef>().release();
-
- node.tree_ = this;
- node.bnode_ = bnode;
- node.id_ = nodes_by_id_.append_and_get_index(&node);
-
- LISTBASE_FOREACH (bNodeSocket *, bsocket, &bnode->inputs) {
- InputSocketRef &socket = *allocator_.construct<InputSocketRef>().release();
- socket.node_ = &node;
- socket.index_ = node.inputs_.append_and_get_index(&socket);
- socket.is_input_ = true;
- socket.bsocket_ = bsocket;
- socket.id_ = sockets_by_id_.append_and_get_index(&socket);
- }
-
- LISTBASE_FOREACH (bNodeSocket *, bsocket, &bnode->outputs) {
- OutputSocketRef &socket = *allocator_.construct<OutputSocketRef>().release();
- socket.node_ = &node;
- socket.index_ = node.outputs_.append_and_get_index(&socket);
- socket.is_input_ = false;
- socket.bsocket_ = bsocket;
- socket.id_ = sockets_by_id_.append_and_get_index(&socket);
- }
-
- LISTBASE_FOREACH (bNodeLink *, blink, &bnode->internal_links) {
- InternalLinkRef &internal_link = *allocator_.construct<InternalLinkRef>().release();
- internal_link.blink_ = blink;
- for (InputSocketRef *socket_ref : node.inputs_) {
- if (socket_ref->bsocket_ == blink->fromsock) {
- internal_link.from_ = socket_ref;
- break;
- }
- }
- for (OutputSocketRef *socket_ref : node.outputs_) {
- if (socket_ref->bsocket_ == blink->tosock) {
- internal_link.to_ = socket_ref;
- break;
- }
- }
- BLI_assert(internal_link.from_ != nullptr);
- BLI_assert(internal_link.to_ != nullptr);
- node.internal_links_.append(&internal_link);
- }
-
- input_sockets_.extend(node.inputs_.as_span());
- output_sockets_.extend(node.outputs_.as_span());
-
- node_mapping.add_new(bnode, &node);
- }
-
- LISTBASE_FOREACH (bNodeLink *, blink, &btree->links) {
- OutputSocketRef &from_socket = this->find_output_socket(
- node_mapping, blink->fromnode, blink->fromsock);
- InputSocketRef &to_socket = this->find_input_socket(
- node_mapping, blink->tonode, blink->tosock);
-
- LinkRef &link = *allocator_.construct<LinkRef>().release();
- link.from_ = &from_socket;
- link.to_ = &to_socket;
- link.blink_ = blink;
-
- links_.append(&link);
-
- from_socket.directly_linked_links_.append(&link);
- to_socket.directly_linked_links_.append(&link);
- }
-
- for (InputSocketRef *input_socket : input_sockets_) {
- if (input_socket->is_multi_input_socket()) {
- std::sort(input_socket->directly_linked_links_.begin(),
- input_socket->directly_linked_links_.end(),
- [&](const LinkRef *a, const LinkRef *b) -> bool {
- int index_a = a->blink()->multi_input_socket_index;
- int index_b = b->blink()->multi_input_socket_index;
- return index_a > index_b;
- });
- }
- }
-
- this->create_socket_identifier_maps();
- this->create_linked_socket_caches();
-
- for (NodeRef *node : nodes_by_id_) {
- const bNodeType *nodetype = node->bnode_->typeinfo;
- nodes_by_type_.add(nodetype, node);
- }
-
- const Span<const NodeRef *> group_output_nodes = this->nodes_by_type("NodeGroupOutput");
- if (group_output_nodes.is_empty()) {
- group_output_node_ = nullptr;
- }
- else if (group_output_nodes.size() == 1) {
- group_output_node_ = group_output_nodes.first();
- }
- else {
- for (const NodeRef *group_output : group_output_nodes) {
- if (group_output->bnode_->flag & NODE_DO_OUTPUT) {
- group_output_node_ = group_output;
- break;
- }
- }
- }
-}
-
-NodeTreeRef::~NodeTreeRef()
-{
- /* The destructor has to be called manually, because these types are allocated in a linear
- * allocator. */
- for (NodeRef *node : nodes_by_id_) {
- node->~NodeRef();
- }
- for (InputSocketRef *socket : input_sockets_) {
- socket->~InputSocketRef();
- }
- for (OutputSocketRef *socket : output_sockets_) {
- socket->~OutputSocketRef();
- }
- for (LinkRef *link : links_) {
- link->~LinkRef();
- }
-}
-
-InputSocketRef &NodeTreeRef::find_input_socket(Map<bNode *, NodeRef *> &node_mapping,
- bNode *bnode,
- bNodeSocket *bsocket)
-{
- NodeRef *node = node_mapping.lookup(bnode);
- for (InputSocketRef *socket : node->inputs_) {
- if (socket->bsocket_ == bsocket) {
- return *socket;
- }
- }
- BLI_assert_unreachable();
- return *node->inputs_[0];
-}
-
-OutputSocketRef &NodeTreeRef::find_output_socket(Map<bNode *, NodeRef *> &node_mapping,
- bNode *bnode,
- bNodeSocket *bsocket)
-{
- NodeRef *node = node_mapping.lookup(bnode);
- for (OutputSocketRef *socket : node->outputs_) {
- if (socket->bsocket_ == bsocket) {
- return *socket;
- }
- }
- BLI_assert_unreachable();
- return *node->outputs_[0];
-}
-
-void NodeTreeRef::create_linked_socket_caches()
-{
- for (InputSocketRef *socket : input_sockets_) {
- /* Find directly linked socket based on incident links. */
- Vector<const SocketRef *> directly_linked_sockets;
- for (LinkRef *link : socket->directly_linked_links_) {
- directly_linked_sockets.append(link->from_);
- }
- socket->directly_linked_sockets_ = allocator_.construct_array_copy(
- directly_linked_sockets.as_span());
-
- /* Find logically linked sockets. */
- Vector<const SocketRef *> logically_linked_sockets;
- Vector<const SocketRef *> logically_linked_skipped_sockets;
- Vector<const InputSocketRef *> seen_sockets_stack;
- socket->foreach_logical_origin(
- [&](const OutputSocketRef &origin) { logically_linked_sockets.append(&origin); },
- [&](const SocketRef &socket) { logically_linked_skipped_sockets.append(&socket); },
- false,
- seen_sockets_stack);
- if (logically_linked_sockets == directly_linked_sockets) {
- socket->logically_linked_sockets_ = socket->directly_linked_sockets_;
- }
- else {
- socket->logically_linked_sockets_ = allocator_.construct_array_copy(
- logically_linked_sockets.as_span());
- }
- socket->logically_linked_skipped_sockets_ = allocator_.construct_array_copy(
- logically_linked_skipped_sockets.as_span());
- }
-
- for (OutputSocketRef *socket : output_sockets_) {
- /* Find directly linked socket based on incident links. */
- Vector<const SocketRef *> directly_linked_sockets;
- for (LinkRef *link : socket->directly_linked_links_) {
- directly_linked_sockets.append(link->to_);
- }
- socket->directly_linked_sockets_ = allocator_.construct_array_copy(
- directly_linked_sockets.as_span());
-
- /* Find logically linked sockets. */
- Vector<const SocketRef *> logically_linked_sockets;
- Vector<const SocketRef *> logically_linked_skipped_sockets;
- Vector<const OutputSocketRef *> handled_sockets;
- socket->foreach_logical_target(
- [&](const InputSocketRef &target) { logically_linked_sockets.append(&target); },
- [&](const SocketRef &socket) { logically_linked_skipped_sockets.append(&socket); },
- handled_sockets);
- if (logically_linked_sockets == directly_linked_sockets) {
- socket->logically_linked_sockets_ = socket->directly_linked_sockets_;
- }
- else {
- socket->logically_linked_sockets_ = allocator_.construct_array_copy(
- logically_linked_sockets.as_span());
- }
- socket->logically_linked_skipped_sockets_ = allocator_.construct_array_copy(
- logically_linked_skipped_sockets.as_span());
- }
-}
-
-void InputSocketRef::foreach_logical_origin(
- FunctionRef<void(const OutputSocketRef &)> origin_fn,
- FunctionRef<void(const SocketRef &)> skipped_fn,
- bool only_follow_first_input_link,
- Vector<const InputSocketRef *> &seen_sockets_stack) const
-{
- /* Protect against loops. */
- if (seen_sockets_stack.contains(this)) {
- return;
- }
- seen_sockets_stack.append(this);
-
- Span<const LinkRef *> links_to_check = this->directly_linked_links();
- if (only_follow_first_input_link) {
- links_to_check = links_to_check.take_front(1);
- }
- for (const LinkRef *link : links_to_check) {
- if (link->is_muted()) {
- continue;
- }
- const OutputSocketRef &origin = link->from();
- const NodeRef &origin_node = origin.node();
- if (!origin.is_available()) {
- /* Non available sockets are ignored. */
- }
- else if (origin_node.is_reroute_node()) {
- const InputSocketRef &reroute_input = origin_node.input(0);
- const OutputSocketRef &reroute_output = origin_node.output(0);
- skipped_fn.call_safe(reroute_input);
- skipped_fn.call_safe(reroute_output);
- reroute_input.foreach_logical_origin(origin_fn, skipped_fn, false, seen_sockets_stack);
- }
- else if (origin_node.is_muted()) {
- for (const InternalLinkRef *internal_link : origin_node.internal_links()) {
- if (&internal_link->to() == &origin) {
- const InputSocketRef &mute_input = internal_link->from();
- skipped_fn.call_safe(origin);
- skipped_fn.call_safe(mute_input);
- mute_input.foreach_logical_origin(origin_fn, skipped_fn, true, seen_sockets_stack);
- }
- }
- }
- else {
- origin_fn(origin);
- }
- }
-
- seen_sockets_stack.pop_last();
-}
-
-void OutputSocketRef::foreach_logical_target(
- FunctionRef<void(const InputSocketRef &)> target_fn,
- FunctionRef<void(const SocketRef &)> skipped_fn,
- Vector<const OutputSocketRef *> &seen_sockets_stack) const
-{
- /* Protect against loops. */
- if (seen_sockets_stack.contains(this)) {
- return;
- }
- seen_sockets_stack.append(this);
-
- for (const LinkRef *link : this->directly_linked_links()) {
- if (link->is_muted()) {
- continue;
- }
- const InputSocketRef &target = link->to();
- const NodeRef &target_node = target.node();
- if (!target.is_available()) {
- /* Non available sockets are ignored. */
- }
- else if (target_node.is_reroute_node()) {
- const OutputSocketRef &reroute_output = target_node.output(0);
- skipped_fn.call_safe(target);
- skipped_fn.call_safe(reroute_output);
- reroute_output.foreach_logical_target(target_fn, skipped_fn, seen_sockets_stack);
- }
- else if (target_node.is_muted()) {
- skipped_fn.call_safe(target);
- for (const InternalLinkRef *internal_link : target_node.internal_links()) {
- if (&internal_link->from() == &target) {
- /* The internal link only forwards the first incoming link. */
- if (target.is_multi_input_socket()) {
- if (target.directly_linked_links()[0] != link) {
- continue;
- }
- }
- const OutputSocketRef &mute_output = internal_link->to();
- skipped_fn.call_safe(target);
- skipped_fn.call_safe(mute_output);
- mute_output.foreach_logical_target(target_fn, skipped_fn, seen_sockets_stack);
- }
- }
- }
- else {
- target_fn(target);
- }
- }
-
- seen_sockets_stack.pop_last();
-}
-
-namespace {
-struct SocketByIdentifierMap {
- SocketIndexByIdentifierMap *map = nullptr;
- std::unique_ptr<SocketIndexByIdentifierMap> owned_map;
-};
-} // namespace
-
-static std::unique_ptr<SocketIndexByIdentifierMap> create_identifier_map(const ListBase &sockets)
-{
- std::unique_ptr<SocketIndexByIdentifierMap> map = std::make_unique<SocketIndexByIdentifierMap>();
- int index;
- LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, &sockets, index) {
- map->add_new(socket->identifier, index);
- }
- return map;
-}
-
-/* This function is not threadsafe. */
-static SocketByIdentifierMap get_or_create_identifier_map(
- const bNode &node, const ListBase &sockets, const bNodeSocketTemplate *sockets_template)
-{
- SocketByIdentifierMap map;
- if (sockets_template == nullptr) {
- if (BLI_listbase_is_empty(&sockets)) {
- static SocketIndexByIdentifierMap empty_map;
- map.map = &empty_map;
- }
- else if (node.type == NODE_REROUTE) {
- if (&node.inputs == &sockets) {
- static SocketIndexByIdentifierMap reroute_input_map = [] {
- SocketIndexByIdentifierMap map;
- map.add_new("Input", 0);
- return map;
- }();
- map.map = &reroute_input_map;
- }
- else {
- static SocketIndexByIdentifierMap reroute_output_map = [] {
- SocketIndexByIdentifierMap map;
- map.add_new("Output", 0);
- return map;
- }();
- map.map = &reroute_output_map;
- }
- }
- else {
- /* The node has a dynamic amount of sockets. Therefore we need to create a new map. */
- map.owned_map = create_identifier_map(sockets);
- map.map = &*map.owned_map;
- }
- }
- else {
- /* Cache only one map for nodes that have the same sockets. */
- static Map<const bNodeSocketTemplate *, std::unique_ptr<SocketIndexByIdentifierMap>> maps;
- map.map = &*maps.lookup_or_add_cb(sockets_template,
- [&]() { return create_identifier_map(sockets); });
- }
- return map;
-}
-
-void NodeTreeRef::create_socket_identifier_maps()
-{
- /* `get_or_create_identifier_map` is not threadsafe, therefore we have to hold a lock here. */
- static std::mutex mutex;
- std::lock_guard lock{mutex};
-
- for (NodeRef *node : nodes_by_id_) {
- bNode &bnode = *node->bnode_;
- SocketByIdentifierMap inputs_map = get_or_create_identifier_map(
- bnode, bnode.inputs, bnode.typeinfo->inputs);
- SocketByIdentifierMap outputs_map = get_or_create_identifier_map(
- bnode, bnode.outputs, bnode.typeinfo->outputs);
- node->input_index_by_identifier_ = inputs_map.map;
- node->output_index_by_identifier_ = outputs_map.map;
- if (inputs_map.owned_map) {
- owned_identifier_maps_.append(std::move(inputs_map.owned_map));
- }
- if (outputs_map.owned_map) {
- owned_identifier_maps_.append(std::move(outputs_map.owned_map));
- }
- }
-}
-
-static bool has_link_cycles_recursive(const NodeRef &node,
- MutableSpan<bool> visited,
- MutableSpan<bool> is_in_stack)
-{
- const int node_id = node.id();
- if (is_in_stack[node_id]) {
- return true;
- }
- if (visited[node_id]) {
- return false;
- }
-
- visited[node_id] = true;
- is_in_stack[node_id] = true;
-
- for (const OutputSocketRef *from_socket : node.outputs()) {
- if (!from_socket->is_available()) {
- continue;
- }
- for (const InputSocketRef *to_socket : from_socket->directly_linked_sockets()) {
- if (!to_socket->is_available()) {
- continue;
- }
- const NodeRef &to_node = to_socket->node();
- if (has_link_cycles_recursive(to_node, visited, is_in_stack)) {
- return true;
- }
- }
- }
-
- is_in_stack[node_id] = false;
- return false;
-}
-
-bool NodeTreeRef::has_link_cycles() const
-{
- const int node_amount = nodes_by_id_.size();
- Array<bool> visited(node_amount, false);
- Array<bool> is_in_stack(node_amount, false);
-
- for (const NodeRef *node : nodes_by_id_) {
- if (has_link_cycles_recursive(*node, visited, is_in_stack)) {
- return true;
- }
- }
- return false;
-}
-
-bool NodeTreeRef::has_undefined_nodes_or_sockets() const
-{
- for (const NodeRef *node : nodes_by_id_) {
- if (node->is_undefined()) {
- return true;
- }
- }
- for (const SocketRef *socket : sockets_by_id_) {
- if (socket->is_undefined()) {
- return true;
- }
- }
- return false;
-}
-
-bool NodeRef::any_input_is_directly_linked() const
-{
- for (const SocketRef *socket : inputs_) {
- if (!socket->directly_linked_sockets().is_empty()) {
- return true;
- }
- }
- return false;
-}
-
-bool NodeRef::any_output_is_directly_linked() const
-{
- for (const SocketRef *socket : outputs_) {
- if (!socket->directly_linked_sockets().is_empty()) {
- return true;
- }
- }
- return false;
-}
-
-bool NodeRef::any_socket_is_directly_linked(eNodeSocketInOut in_out) const
-{
- if (in_out == SOCK_IN) {
- return this->any_input_is_directly_linked();
- }
- return this->any_output_is_directly_linked();
-}
-
-struct ToposortNodeState {
- bool is_done = false;
- bool is_in_stack = false;
-};
-
-static void toposort_from_start_node(const NodeTreeRef::ToposortDirection direction,
- const NodeRef &start_node,
- MutableSpan<ToposortNodeState> node_states,
- NodeTreeRef::ToposortResult &result)
-{
- struct Item {
- const NodeRef *node;
- /* Index of the next socket that is checked in the depth-first search. */
- int socket_index = 0;
- /* Link index in the next socket that is checked in the depth-first search. */
- int link_index = 0;
- };
-
- /* Do a depth-first search to sort nodes topologically. */
- Stack<Item, 64> nodes_to_check;
- nodes_to_check.push({&start_node});
- node_states[start_node.id()].is_in_stack = true;
- while (!nodes_to_check.is_empty()) {
- Item &item = nodes_to_check.peek();
- const NodeRef &node = *item.node;
- const Span<const SocketRef *> sockets = node.sockets(
- direction == NodeTreeRef::ToposortDirection::LeftToRight ? SOCK_IN : SOCK_OUT);
-
- while (true) {
- if (item.socket_index == sockets.size()) {
- /* All sockets have already been visited. */
- break;
- }
- const SocketRef &socket = *sockets[item.socket_index];
- const Span<const SocketRef *> linked_sockets = socket.directly_linked_sockets();
- if (item.link_index == linked_sockets.size()) {
- /* All links connected to this socket have already been visited. */
- item.socket_index++;
- item.link_index = 0;
- continue;
- }
- const SocketRef &linked_socket = *linked_sockets[item.link_index];
- const NodeRef &linked_node = linked_socket.node();
- ToposortNodeState &linked_node_state = node_states[linked_node.id()];
- if (linked_node_state.is_done) {
- /* The linked node has already been visited. */
- item.link_index++;
- continue;
- }
- if (linked_node_state.is_in_stack) {
- result.has_cycle = true;
- }
- else {
- nodes_to_check.push({&linked_node});
- linked_node_state.is_in_stack = true;
- }
- break;
- }
-
- /* If no other element has been pushed, the current node can be pushed to the sorted list. */
- if (&item == &nodes_to_check.peek()) {
- ToposortNodeState &node_state = node_states[node.id()];
- node_state.is_done = true;
- node_state.is_in_stack = false;
- result.sorted_nodes.append(&node);
- nodes_to_check.pop();
- }
- }
-}
-
-NodeTreeRef::ToposortResult NodeTreeRef::toposort(const ToposortDirection direction) const
-{
- ToposortResult result;
- result.sorted_nodes.reserve(nodes_by_id_.size());
-
- Array<ToposortNodeState> node_states(nodes_by_id_.size());
-
- for (const NodeRef *node : nodes_by_id_) {
- if (node_states[node->id()].is_done) {
- /* Ignore nodes that are done already. */
- continue;
- }
- if (node->any_socket_is_directly_linked(
- direction == ToposortDirection::LeftToRight ? SOCK_OUT : SOCK_IN)) {
- /* Ignore non-start nodes. */
- continue;
- }
-
- toposort_from_start_node(direction, *node, node_states, result);
- }
-
- /* Check if the loop above forgot some nodes because there is a cycle. */
- if (result.sorted_nodes.size() < nodes_by_id_.size()) {
- result.has_cycle = true;
- for (const NodeRef *node : nodes_by_id_) {
- if (node_states[node->id()].is_done) {
- /* Ignore nodes that are done already. */
- continue;
- }
- /* Start toposort at this node which is somewhere in the middle of a loop. */
- toposort_from_start_node(direction, *node, node_states, result);
- }
- }
-
- BLI_assert(result.sorted_nodes.size() == nodes_by_id_.size());
- return result;
-}
-
-const NodeRef *NodeTreeRef::find_node(const bNode &bnode) const
-{
- for (const NodeRef *node : this->nodes_by_type(bnode.typeinfo)) {
- if (node->bnode_ == &bnode) {
- return node;
- }
- }
- return nullptr;
-}
-
-std::string NodeTreeRef::to_dot() const
-{
- dot::DirectedGraph digraph;
- digraph.set_rankdir(dot::Attr_rankdir::LeftToRight);
-
- Map<const NodeRef *, dot::NodeWithSocketsRef> dot_nodes;
-
- for (const NodeRef *node : nodes_by_id_) {
- dot::Node &dot_node = digraph.new_node("");
- dot_node.set_background_color("white");
-
- Vector<std::string> input_names;
- Vector<std::string> output_names;
- for (const InputSocketRef *socket : node->inputs()) {
- input_names.append(socket->name());
- }
- for (const OutputSocketRef *socket : node->outputs()) {
- output_names.append(socket->name());
- }
-
- dot_nodes.add_new(node,
- dot::NodeWithSocketsRef(dot_node, node->name(), input_names, output_names));
- }
-
- for (const OutputSocketRef *from_socket : output_sockets_) {
- for (const InputSocketRef *to_socket : from_socket->directly_linked_sockets()) {
- dot::NodeWithSocketsRef &from_dot_node = dot_nodes.lookup(&from_socket->node());
- dot::NodeWithSocketsRef &to_dot_node = dot_nodes.lookup(&to_socket->node());
-
- digraph.new_edge(from_dot_node.output(from_socket->index()),
- to_dot_node.input(to_socket->index()));
- }
- }
-
- return digraph.to_dot_string();
-}
-
-const NodeTreeRef &get_tree_ref_from_map(NodeTreeRefMap &node_tree_refs, bNodeTree &btree)
-{
- return *node_tree_refs.lookup_or_add_cb(&btree,
- [&]() { return std::make_unique<NodeTreeRef>(&btree); });
-}
-
-PointerRNA NodeRef::rna() const
-{
- PointerRNA rna;
- RNA_pointer_create(&tree_->btree()->id, &RNA_Node, bnode_, &rna);
- return rna;
-}
-
-PointerRNA SocketRef::rna() const
-{
- PointerRNA rna;
- RNA_pointer_create(&this->tree().btree()->id, &RNA_NodeSocket, bsocket_, &rna);
- return rna;
-}
-
-} // namespace blender::nodes
diff --git a/source/blender/nodes/intern/node_util.c b/source/blender/nodes/intern/node_util.c
index e8be093c606..ddab455509d 100644
--- a/source/blender/nodes/intern/node_util.c
+++ b/source/blender/nodes/intern/node_util.c
@@ -200,7 +200,7 @@ void node_math_label(const bNodeTree *UNUSED(ntree), const bNode *node, char *la
if (!enum_label) {
name = "Unknown";
}
- BLI_strncpy(label, IFACE_(name), maxlen);
+ BLI_strncpy(label, CTX_IFACE_(BLT_I18NCONTEXT_ID_NODETREE, name), maxlen);
}
void node_vector_math_label(const bNodeTree *UNUSED(ntree),
diff --git a/source/blender/nodes/shader/CMakeLists.txt b/source/blender/nodes/shader/CMakeLists.txt
index 3e90f185168..e6f90449843 100644
--- a/source/blender/nodes/shader/CMakeLists.txt
+++ b/source/blender/nodes/shader/CMakeLists.txt
@@ -67,6 +67,7 @@ set(SRC
nodes/node_shader_map_range.cc
nodes/node_shader_mapping.cc
nodes/node_shader_math.cc
+ nodes/node_shader_mix.cc
nodes/node_shader_mix_rgb.cc
nodes/node_shader_mix_shader.cc
nodes/node_shader_normal.cc
@@ -131,22 +132,24 @@ set(LIB
bf_nodes
)
-if(WITH_PYTHON)
- list(APPEND INC
- ../../python
- )
+if(WITH_FREESTYLE)
+ add_definitions(-DWITH_FREESTYLE)
+endif()
+
+if(WITH_TBB)
+ 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()
list(APPEND INC_SYS
- ${PYTHON_INCLUDE_DIRS}
+ ${TBB_INCLUDE_DIRS}
)
+
list(APPEND LIB
- ${PYTHON_LINKFLAGS}
- ${PYTHON_LIBRARIES}
+ ${TBB_LIBRARIES}
)
- add_definitions(-DWITH_PYTHON)
-endif()
-
-if(WITH_FREESTYLE)
- add_definitions(-DWITH_FREESTYLE)
endif()
blender_add_lib(bf_nodes_shader "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/nodes/shader/node_shader_tree.cc b/source/blender/nodes/shader/node_shader_tree.cc
index 02ac54f4b2b..a6d2e954a0c 100644
--- a/source/blender/nodes/shader/node_shader_tree.cc
+++ b/source/blender/nodes/shader/node_shader_tree.cc
@@ -26,6 +26,7 @@
#include "BLT_translation.h"
#include "BKE_context.h"
+#include "BKE_layer.h"
#include "BKE_lib_id.h"
#include "BKE_linestyle.h"
#include "BKE_node.h"
@@ -71,7 +72,8 @@ static void shader_get_from_context(const bContext *C,
SpaceNode *snode = CTX_wm_space_node(C);
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
if (snode->shaderfrom == SNODE_SHADER_OBJECT) {
if (ob) {
@@ -617,7 +619,7 @@ static bool ntree_shader_implicit_closure_cast(bNodeTree *ntree)
bool modified = false;
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree->links) {
if ((link->fromsock->type != SOCK_SHADER) && (link->tosock->type == SOCK_SHADER)) {
- bNode *emission_node = nodeAddStaticNode(NULL, ntree, SH_NODE_EMISSION);
+ bNode *emission_node = nodeAddStaticNode(nullptr, ntree, SH_NODE_EMISSION);
bNodeSocket *in_sock = ntree_shader_node_find_input(emission_node, "Color");
bNodeSocket *out_sock = ntree_shader_node_find_output(emission_node, "Emission");
nodeAddLink(ntree, link->fromnode, link->fromsock, emission_node, in_sock);
@@ -645,7 +647,7 @@ static void ntree_weight_tree_merge_weight(bNodeTree *ntree,
bNode **tonode,
bNodeSocket **tosock)
{
- bNode *addnode = nodeAddStaticNode(NULL, ntree, SH_NODE_MATH);
+ bNode *addnode = nodeAddStaticNode(nullptr, ntree, SH_NODE_MATH);
addnode->custom1 = NODE_MATH_ADD;
addnode->tmp_flag = -2; /* Copy */
bNodeSocket *addsock_out = ntree_shader_node_output_get(addnode, 0);
@@ -683,19 +685,19 @@ static bool ntree_weight_tree_tag_nodes(bNode *fromnode, bNode *tonode, void *us
* with their respective weights. */
static void ntree_shader_weight_tree_invert(bNodeTree *ntree, bNode *output_node)
{
- bNodeLink *displace_link = NULL;
+ bNodeLink *displace_link = nullptr;
bNodeSocket *displace_output = ntree_shader_node_find_input(output_node, "Displacement");
if (displace_output && displace_output->link) {
/* Remove any displacement link to avoid tagging it later on. */
displace_link = displace_output->link;
- displace_output->link = NULL;
+ displace_output->link = nullptr;
}
- bNodeLink *thickness_link = NULL;
+ bNodeLink *thickness_link = nullptr;
bNodeSocket *thickness_output = ntree_shader_node_find_input(output_node, "Thickness");
if (thickness_output && thickness_output->link) {
/* Remove any thickness link to avoid tagging it later on. */
thickness_link = thickness_output->link;
- thickness_output->link = NULL;
+ thickness_output->link = nullptr;
}
/* Init tmp flag. */
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
@@ -717,7 +719,7 @@ static void ntree_shader_weight_tree_invert(bNodeTree *ntree, bNode *output_node
case SH_NODE_OUTPUT_WORLD:
case SH_NODE_OUTPUT_MATERIAL: {
/* Start the tree with full weight. */
- nodes_copy[id] = nodeAddStaticNode(NULL, ntree, SH_NODE_VALUE);
+ nodes_copy[id] = nodeAddStaticNode(nullptr, ntree, SH_NODE_VALUE);
nodes_copy[id]->tmp_flag = -2; /* Copy */
((bNodeSocketValueFloat *)ntree_shader_node_output_get(nodes_copy[id], 0)->default_value)
->value = 1.0f;
@@ -726,7 +728,7 @@ static void ntree_shader_weight_tree_invert(bNodeTree *ntree, bNode *output_node
case SH_NODE_ADD_SHADER: {
/* Simple passthrough node. Each original inputs will get the same weight. */
/* TODO(fclem): Better use some kind of reroute node? */
- nodes_copy[id] = nodeAddStaticNode(NULL, ntree, SH_NODE_MATH);
+ nodes_copy[id] = nodeAddStaticNode(nullptr, ntree, SH_NODE_MATH);
nodes_copy[id]->custom1 = NODE_MATH_ADD;
nodes_copy[id]->tmp_flag = -2; /* Copy */
((bNodeSocketValueFloat *)ntree_shader_node_input_get(nodes_copy[id], 0)->default_value)
@@ -739,17 +741,17 @@ static void ntree_shader_weight_tree_invert(bNodeTree *ntree, bNode *output_node
bNodeSocket *fromsock, *tosock;
int id_start = id;
/* output = (factor * input_weight) */
- nodes_copy[id] = nodeAddStaticNode(NULL, ntree, SH_NODE_MATH);
+ nodes_copy[id] = nodeAddStaticNode(nullptr, ntree, SH_NODE_MATH);
nodes_copy[id]->custom1 = NODE_MATH_MULTIPLY;
nodes_copy[id]->tmp_flag = -2; /* Copy */
id++;
/* output = ((1.0 - factor) * input_weight) <=> (input_weight - factor * input_weight) */
- nodes_copy[id] = nodeAddStaticNode(NULL, ntree, SH_NODE_MATH);
+ nodes_copy[id] = nodeAddStaticNode(nullptr, ntree, SH_NODE_MATH);
nodes_copy[id]->custom1 = NODE_MATH_SUBTRACT;
nodes_copy[id]->tmp_flag = -2; /* Copy */
id++;
/* Node sanitizes the input mix factor by clamping it. */
- nodes_copy[id] = nodeAddStaticNode(NULL, ntree, SH_NODE_MATH);
+ nodes_copy[id] = nodeAddStaticNode(nullptr, ntree, SH_NODE_MATH);
nodes_copy[id]->custom1 = NODE_MATH_ADD;
nodes_copy[id]->custom2 = SHD_MATH_CLAMP;
nodes_copy[id]->tmp_flag = -2; /* Copy */
@@ -765,7 +767,7 @@ static void ntree_shader_weight_tree_invert(bNodeTree *ntree, bNode *output_node
id++;
/* Reroute the weight input to the 3 processing nodes. Simplify linking later-on. */
/* TODO(fclem): Better use some kind of reroute node? */
- nodes_copy[id] = nodeAddStaticNode(NULL, ntree, SH_NODE_MATH);
+ nodes_copy[id] = nodeAddStaticNode(nullptr, ntree, SH_NODE_MATH);
nodes_copy[id]->custom1 = NODE_MATH_ADD;
nodes_copy[id]->tmp_flag = -2; /* Copy */
((bNodeSocketValueFloat *)ntree_shader_node_input_get(nodes_copy[id], 0)->default_value)
@@ -1040,7 +1042,7 @@ void ntreeGPUMaterialNodes(bNodeTree *localtree, GPUMaterial *mat)
/* Tree is valid if it contains no undefined implicit socket type cast. */
bool valid_tree = ntree_shader_implicit_closure_cast(localtree);
- if (valid_tree && output != NULL) {
+ if (valid_tree && output != nullptr) {
ntree_shader_pruned_unused(localtree, output);
ntree_shader_shader_to_rgba_branch(localtree, output);
ntree_shader_weight_tree_invert(localtree, output);
diff --git a/source/blender/nodes/shader/node_shader_util.hh b/source/blender/nodes/shader/node_shader_util.hh
index d5f54d9cac9..38220634695 100644
--- a/source/blender/nodes/shader/node_shader_util.hh
+++ b/source/blender/nodes/shader/node_shader_util.hh
@@ -59,6 +59,8 @@
#include "RE_pipeline.h"
#include "RE_texture.h"
+#include "RNA_access.h"
+
bool sh_node_poll_default(struct bNodeType *ntype,
struct bNodeTree *ntree,
const char **r_disabled_hint);
diff --git a/source/blender/nodes/shader/nodes/node_shader_attribute.cc b/source/blender/nodes/shader/nodes/node_shader_attribute.cc
index d01271c15d3..65d053e6379 100644
--- a/source/blender/nodes/shader/nodes/node_shader_attribute.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_attribute.cc
@@ -36,6 +36,7 @@ static int node_shader_gpu_attribute(GPUMaterial *mat,
{
NodeShaderAttribute *attr = static_cast<NodeShaderAttribute *>(node->storage);
bool is_varying = attr->type == SHD_ATTRIBUTE_GEOMETRY;
+ float attr_hash = 0.0f;
GPUNodeLink *cd_attr;
@@ -43,7 +44,12 @@ static int node_shader_gpu_attribute(GPUMaterial *mat,
cd_attr = GPU_attribute(mat, CD_AUTO_FROM_NAME, attr->name);
}
else {
- cd_attr = GPU_uniform_attribute(mat, attr->name, attr->type == SHD_ATTRIBUTE_INSTANCER);
+ cd_attr = GPU_uniform_attribute(mat,
+ attr->name,
+ attr->type == SHD_ATTRIBUTE_INSTANCER,
+ reinterpret_cast<uint32_t *>(&attr_hash));
+
+ GPU_link(mat, "node_attribute_uniform", cd_attr, GPU_constant(&attr_hash), &cd_attr);
}
if (STREQ(attr->name, "color")) {
@@ -55,9 +61,11 @@ static int node_shader_gpu_attribute(GPUMaterial *mat,
GPU_stack_link(mat, node, "node_attribute", in, out, cd_attr);
- int i;
- LISTBASE_FOREACH_INDEX (bNodeSocket *, sock, &node->outputs, i) {
- node_shader_gpu_bump_tex_coord(mat, node, &out[i].link);
+ if (is_varying) {
+ int i;
+ LISTBASE_FOREACH_INDEX (bNodeSocket *, sock, &node->outputs, i) {
+ node_shader_gpu_bump_tex_coord(mat, node, &out[i].link);
+ }
}
return 1;
diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc
index a63c7aede04..7b72d4b9be4 100644
--- a/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc
@@ -167,6 +167,27 @@ static int node_shader_gpu_bsdf_principled(GPUMaterial *mat,
if (use_transparency) {
flag |= GPU_MATFLAG_TRANSPARENT;
}
+ if (use_clear) {
+ flag |= GPU_MATFLAG_CLEARCOAT;
+ }
+
+ /* Ref. T98190: Defines are optimizations for old compilers.
+ * Might become unnecessary with EEVEE-Next. */
+ if (use_diffuse == false && use_refract == false && use_clear == true) {
+ flag |= GPU_MATFLAG_PRINCIPLED_CLEARCOAT;
+ }
+ else if (use_diffuse == false && use_refract == false && use_clear == false) {
+ flag |= GPU_MATFLAG_PRINCIPLED_METALLIC;
+ }
+ else if (use_diffuse == true && use_refract == false && use_clear == false) {
+ flag |= GPU_MATFLAG_PRINCIPLED_DIELECTRIC;
+ }
+ else if (use_diffuse == false && use_refract == true && use_clear == false) {
+ flag |= GPU_MATFLAG_PRINCIPLED_GLASS;
+ }
+ else {
+ flag |= GPU_MATFLAG_PRINCIPLED_ANY;
+ }
if (use_subsurf) {
bNodeSocket *socket = (bNodeSocket *)BLI_findlink(&node->original->inputs, 2);
diff --git a/source/blender/nodes/shader/nodes/node_shader_color_ramp.cc b/source/blender/nodes/shader/nodes/node_shader_color_ramp.cc
index 3723480ffa3..d73ffd89288 100644
--- a/source/blender/nodes/shader/nodes/node_shader_color_ramp.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_color_ramp.cc
@@ -125,7 +125,7 @@ class ColorBandFunction : public fn::MultiFunction {
static void sh_node_valtorgb_build_multi_function(nodes::NodeMultiFunctionBuilder &builder)
{
- bNode &bnode = builder.node();
+ const bNode &bnode = builder.node();
const ColorBand *color_band = (const ColorBand *)bnode.storage;
builder.construct_and_set_matching_fn<ColorBandFunction>(*color_band);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_curves.cc b/source/blender/nodes/shader/nodes/node_shader_curves.cc
index eb47059063d..4725aef5991 100644
--- a/source/blender/nodes/shader/nodes/node_shader_curves.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_curves.cc
@@ -95,7 +95,7 @@ class CurveVecFunction : public fn::MultiFunction {
static void sh_node_curve_vec_build_multi_function(NodeMultiFunctionBuilder &builder)
{
- bNode &bnode = builder.node();
+ const bNode &bnode = builder.node();
CurveMapping *cumap = (CurveMapping *)bnode.storage;
BKE_curvemapping_init(cumap);
builder.construct_and_set_matching_fn<CurveVecFunction>(*cumap);
@@ -237,7 +237,7 @@ class CurveRGBFunction : public fn::MultiFunction {
static void sh_node_curve_rgb_build_multi_function(NodeMultiFunctionBuilder &builder)
{
- bNode &bnode = builder.node();
+ const bNode &bnode = builder.node();
CurveMapping *cumap = (CurveMapping *)bnode.storage;
BKE_curvemapping_init(cumap);
builder.construct_and_set_matching_fn<CurveRGBFunction>(*cumap);
@@ -356,7 +356,7 @@ class CurveFloatFunction : public fn::MultiFunction {
static void sh_node_curve_float_build_multi_function(NodeMultiFunctionBuilder &builder)
{
- bNode &bnode = builder.node();
+ const bNode &bnode = builder.node();
CurveMapping *cumap = (CurveMapping *)bnode.storage;
BKE_curvemapping_init(cumap);
builder.construct_and_set_matching_fn<CurveFloatFunction>(*cumap);
diff --git a/source/blender/nodes/shader/nodes/node_shader_math.cc b/source/blender/nodes/shader/nodes/node_shader_math.cc
index 8a2b18d7d76..bd83f8dac37 100644
--- a/source/blender/nodes/shader/nodes/node_shader_math.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_math.cc
@@ -61,8 +61,9 @@ static void sh_node_math_gather_link_searches(GatherLinkSearchOpParams &params)
ELEM(item->value, NODE_MATH_COMPARE, NODE_MATH_GREATER_THAN, NODE_MATH_LESS_THAN)) ?
-1 :
weight;
- params.add_item(
- IFACE_(item->name), SocketSearchOp{"Value", (NodeMathOperation)item->value}, gn_weight);
+ params.add_item(CTX_IFACE_(BLT_I18NCONTEXT_ID_NODETREE, item->name),
+ SocketSearchOp{"Value", (NodeMathOperation)item->value},
+ gn_weight);
}
}
}
@@ -101,7 +102,7 @@ static int gpu_shader_math(GPUMaterial *mat,
return 0;
}
-static const fn::MultiFunction *get_base_multi_function(bNode &node)
+static const fn::MultiFunction *get_base_multi_function(const bNode &node)
{
const int mode = node.custom1;
const fn::MultiFunction *base_fn = nullptr;
diff --git a/source/blender/nodes/shader/nodes/node_shader_mix.cc b/source/blender/nodes/shader/nodes/node_shader_mix.cc
new file mode 100644
index 00000000000..f785e32832e
--- /dev/null
+++ b/source/blender/nodes/shader/nodes/node_shader_mix.cc
@@ -0,0 +1,443 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2005 Blender Foundation. All rights reserved. */
+
+/** \file
+ * \ingroup shdnodes
+ */
+
+#include <algorithm>
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_shader_util.hh"
+
+#include "NOD_socket_search_link.hh"
+#include "RNA_enum_types.h"
+
+namespace blender::nodes::node_sh_mix_cc {
+
+NODE_STORAGE_FUNCS(NodeShaderMix)
+
+static void sh_node_mix_declare(NodeDeclarationBuilder &b)
+{
+ b.is_function_node();
+ b.add_input<decl::Float>(N_("Factor"), "Factor_Float")
+ .default_value(0.5f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR);
+ b.add_input<decl::Vector>(N_("Factor"), "Factor_Vector")
+ .default_value(float3(0.5f))
+ .subtype(PROP_FACTOR);
+
+ b.add_input<decl::Float>(N_("A"), "A_Float")
+ .min(-10000.0f)
+ .max(10000.0f)
+ .is_default_link_socket();
+ b.add_input<decl::Float>(N_("B"), "B_Float").min(-10000.0f).max(10000.0f);
+
+ b.add_input<decl::Vector>(N_("A"), "A_Vector").is_default_link_socket();
+ b.add_input<decl::Vector>(N_("B"), "B_Vector");
+
+ b.add_input<decl::Color>(N_("A"), "A_Color")
+ .default_value({0.5f, 0.5f, 0.5f, 1.0f})
+ .is_default_link_socket();
+ b.add_input<decl::Color>(N_("B"), "B_Color").default_value({0.5f, 0.5f, 0.5f, 1.0f});
+
+ b.add_output<decl::Float>(N_("Result"), "Result_Float");
+ b.add_output<decl::Vector>(N_("Result"), "Result_Vector");
+ b.add_output<decl::Color>(N_("Result"), "Result_Color");
+};
+
+static void sh_node_mix_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ const NodeShaderMix &data = node_storage(*static_cast<const bNode *>(ptr->data));
+ uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE);
+ if (data.data_type == SOCK_VECTOR) {
+ uiItemR(layout, ptr, "factor_mode", 0, "", ICON_NONE);
+ }
+ if (data.data_type == SOCK_RGBA) {
+ uiItemR(layout, ptr, "blend_type", 0, "", ICON_NONE);
+ uiItemR(layout, ptr, "clamp_result", 0, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "clamp_factor", 0, nullptr, ICON_NONE);
+ }
+ else {
+ uiItemR(layout, ptr, "clamp_factor", 0, nullptr, ICON_NONE);
+ }
+}
+
+static void sh_node_mix_label(const bNodeTree *UNUSED(ntree),
+ const bNode *node,
+ char *label,
+ int maxlen)
+{
+ const NodeShaderMix &storage = node_storage(*node);
+ if (storage.data_type == SOCK_RGBA) {
+ const char *name;
+ bool enum_label = RNA_enum_name(rna_enum_ramp_blend_items, storage.blend_type, &name);
+ if (!enum_label) {
+ name = "Unknown";
+ }
+ BLI_strncpy(label, IFACE_(name), maxlen);
+ }
+}
+
+static int sh_node_mix_ui_class(const bNode *node)
+{
+ const NodeShaderMix &storage = node_storage(*node);
+ const eNodeSocketDatatype data_type = static_cast<eNodeSocketDatatype>(storage.data_type);
+
+ switch (data_type) {
+ case SOCK_VECTOR:
+ return NODE_CLASS_OP_VECTOR;
+ case SOCK_RGBA:
+ return NODE_CLASS_OP_COLOR;
+ default:
+ return NODE_CLASS_CONVERTER;
+ }
+}
+
+static void sh_node_mix_update(bNodeTree *ntree, bNode *node)
+{
+ const NodeShaderMix &storage = node_storage(*node);
+ const eNodeSocketDatatype data_type = static_cast<eNodeSocketDatatype>(storage.data_type);
+
+ LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) {
+ nodeSetSocketAvailability(ntree, socket, socket->type == data_type);
+ }
+
+ LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) {
+ nodeSetSocketAvailability(ntree, socket, socket->type == data_type);
+ }
+
+ bool use_vector_factor = data_type == SOCK_VECTOR &&
+ storage.factor_mode != NODE_MIX_MODE_UNIFORM;
+
+ bNodeSocket *sock_factor = (bNodeSocket *)BLI_findlink(&node->inputs, 0);
+ nodeSetSocketAvailability(ntree, sock_factor, !use_vector_factor);
+
+ bNodeSocket *sock_factor_vec = (bNodeSocket *)BLI_findlink(&node->inputs, 1);
+ nodeSetSocketAvailability(ntree, sock_factor_vec, use_vector_factor);
+}
+
+static void node_mix_gather_link_searches(GatherLinkSearchOpParams &params)
+{
+ const eNodeSocketDatatype sock_type = static_cast<eNodeSocketDatatype>(
+ params.other_socket().type);
+
+ if (ELEM(sock_type, SOCK_BOOLEAN, SOCK_FLOAT, SOCK_RGBA, SOCK_VECTOR, SOCK_INT)) {
+ const eNodeSocketDatatype type = ELEM(sock_type, SOCK_BOOLEAN, SOCK_INT) ? SOCK_FLOAT :
+ sock_type;
+
+ if (params.in_out() == SOCK_OUT) {
+ params.add_item(IFACE_("Result"), [type](LinkSearchOpParams &params) {
+ bNode &node = params.add_node("ShaderNodeMix");
+ node_storage(node).data_type = type;
+ params.update_and_connect_available_socket(node, "Result");
+ });
+ }
+ else {
+ if (ELEM(sock_type, SOCK_VECTOR, SOCK_RGBA)) {
+ params.add_item(IFACE_("Factor (Non-Uniform)"), [](LinkSearchOpParams &params) {
+ bNode &node = params.add_node("ShaderNodeMix");
+ node_storage(node).data_type = SOCK_VECTOR;
+ node_storage(node).factor_mode = NODE_MIX_MODE_NON_UNIFORM;
+ params.update_and_connect_available_socket(node, "Factor");
+ });
+ }
+ params.add_item(IFACE_("Factor"), [type](LinkSearchOpParams &params) {
+ bNode &node = params.add_node("ShaderNodeMix");
+ node_storage(node).data_type = type;
+ params.update_and_connect_available_socket(node, "Factor");
+ });
+ params.add_item(IFACE_("A"), [type](LinkSearchOpParams &params) {
+ bNode &node = params.add_node("ShaderNodeMix");
+ node_storage(node).data_type = type;
+ params.update_and_connect_available_socket(node, "A");
+ });
+ params.add_item(IFACE_("B"), [type](LinkSearchOpParams &params) {
+ bNode &node = params.add_node("ShaderNodeMix");
+ node_storage(node).data_type = type;
+ params.update_and_connect_available_socket(node, "B");
+ });
+ }
+ }
+}
+
+static void node_mix_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeShaderMix *data = MEM_cnew<NodeShaderMix>(__func__);
+ data->data_type = SOCK_FLOAT;
+ data->factor_mode = NODE_MIX_MODE_UNIFORM;
+ data->clamp_factor = 1;
+ data->clamp_result = 0;
+ data->blend_type = MA_RAMP_BLEND;
+ node->storage = data;
+}
+
+static const char *gpu_shader_get_name(eNodeSocketDatatype data_type,
+ const bool non_uniform,
+ const int blend_type)
+{
+ switch (data_type) {
+ case SOCK_FLOAT:
+ return "node_mix_float";
+ case SOCK_VECTOR:
+ return (non_uniform) ? "node_mix_vector_non_uniform" : "node_mix_vector";
+ case SOCK_RGBA:
+ switch (blend_type) {
+ case MA_RAMP_BLEND:
+ return "node_mix_blend";
+ case MA_RAMP_ADD:
+ return "node_mix_add";
+ case MA_RAMP_MULT:
+ return "node_mix_mult";
+ case MA_RAMP_SUB:
+ return "node_mix_sub";
+ case MA_RAMP_SCREEN:
+ return "node_mix_screen";
+ case MA_RAMP_DIV:
+ return "node_mix_div_fallback";
+ case MA_RAMP_DIFF:
+ return "node_mix_diff";
+ case MA_RAMP_DARK:
+ return "node_mix_dark";
+ case MA_RAMP_LIGHT:
+ return "node_mix_light";
+ case MA_RAMP_OVERLAY:
+ return "node_mix_overlay";
+ case MA_RAMP_DODGE:
+ return "node_mix_dodge";
+ case MA_RAMP_BURN:
+ return "node_mix_burn";
+ case MA_RAMP_HUE:
+ return "node_mix_hue";
+ case MA_RAMP_SAT:
+ return "node_mix_sat";
+ case MA_RAMP_VAL:
+ return "node_mix_val";
+ case MA_RAMP_COLOR:
+ return "node_mix_color";
+ case MA_RAMP_SOFT:
+ return "node_mix_soft";
+ case MA_RAMP_LINEAR:
+ return "node_mix_linear";
+ default:
+ BLI_assert_unreachable();
+ return nullptr;
+ }
+ default:
+ BLI_assert_unreachable();
+ return nullptr;
+ }
+}
+
+static int gpu_shader_mix(GPUMaterial *mat,
+ bNode *node,
+ bNodeExecData *UNUSED(execdata),
+ GPUNodeStack *in,
+ GPUNodeStack *out)
+{
+ const NodeShaderMix &storage = node_storage(*node);
+ const bool is_non_uniform = storage.factor_mode == NODE_MIX_MODE_NON_UNIFORM;
+ const bool is_color_mode = storage.data_type == SOCK_RGBA;
+ const bool is_vector_mode = storage.data_type == SOCK_VECTOR;
+ const int blend_type = storage.blend_type;
+ const char *name = gpu_shader_get_name(
+ (eNodeSocketDatatype)storage.data_type, is_non_uniform, blend_type);
+
+ if (name == nullptr) {
+ return 0;
+ }
+
+ if (storage.clamp_factor) {
+ if (is_non_uniform && is_vector_mode) {
+ const float min[3] = {0.0f, 0.0f, 0.0f};
+ const float max[3] = {1.0f, 1.0f, 1.0f};
+ const GPUNodeLink *factor_link = in[1].link ? in[1].link : GPU_uniform(in[1].vec);
+ GPU_link(mat,
+ "node_mix_clamp_vector",
+ factor_link,
+ GPU_constant(min),
+ GPU_constant(max),
+ &in[1].link);
+ }
+ else {
+ const float min = 0.0f;
+ const float max = 1.0f;
+ const GPUNodeLink *factor_link = in[0].link ? in[0].link : GPU_uniform(in[0].vec);
+ GPU_link(mat,
+ "node_mix_clamp_value",
+ factor_link,
+ GPU_constant(&min),
+ GPU_constant(&max),
+ &in[0].link);
+ }
+ }
+
+ int ret = GPU_stack_link(mat, node, name, in, out);
+
+ if (ret && is_color_mode && storage.clamp_result) {
+ const float min[3] = {0.0f, 0.0f, 0.0f};
+ const float max[3] = {1.0f, 1.0f, 1.0f};
+ GPU_link(mat,
+ "node_mix_clamp_vector",
+ out[2].link,
+ GPU_constant(min),
+ GPU_constant(max),
+ &out[2].link);
+ }
+ return ret;
+}
+
+class MixColorFunction : public fn::MultiFunction {
+ private:
+ const bool clamp_factor_;
+ const bool clamp_result_;
+ const int blend_type_;
+
+ public:
+ MixColorFunction(const bool clamp_factor, const bool clamp_result, const int blend_type)
+ : clamp_factor_(clamp_factor), clamp_result_(clamp_result), blend_type_(blend_type)
+ {
+ static fn::MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ }
+
+ static fn::MFSignature create_signature()
+ {
+ fn::MFSignatureBuilder signature{"MixColor"};
+ signature.single_input<float>("Factor");
+ signature.single_input<ColorGeometry4f>("A");
+ signature.single_input<ColorGeometry4f>("B");
+ signature.single_output<ColorGeometry4f>("Result");
+ return signature.build();
+ }
+
+ void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override
+ {
+ const VArray<float> &fac = params.readonly_single_input<float>(0, "Factor");
+ const VArray<ColorGeometry4f> &col1 = params.readonly_single_input<ColorGeometry4f>(1, "A");
+ const VArray<ColorGeometry4f> &col2 = params.readonly_single_input<ColorGeometry4f>(2, "B");
+ MutableSpan<ColorGeometry4f> results = params.uninitialized_single_output<ColorGeometry4f>(
+ 3, "Result");
+
+ if (clamp_factor_) {
+ for (int64_t i : mask) {
+ results[i] = col1[i];
+ ramp_blend(blend_type_, results[i], std::clamp(fac[i], 0.0f, 1.0f), col2[i]);
+ }
+ }
+ else {
+ for (int64_t i : mask) {
+ results[i] = col1[i];
+ ramp_blend(blend_type_, results[i], fac[i], col2[i]);
+ }
+ }
+
+ if (clamp_result_) {
+ for (int64_t i : mask) {
+ clamp_v3(results[i], 0.0f, 1.0f);
+ }
+ }
+ }
+};
+
+static const fn::MultiFunction *get_multi_function(const bNode &node)
+{
+ const NodeShaderMix *data = (NodeShaderMix *)node.storage;
+ bool uniform_factor = data->factor_mode == NODE_MIX_MODE_UNIFORM;
+ const bool clamp_factor = data->clamp_factor;
+ switch (data->data_type) {
+ case SOCK_FLOAT: {
+ if (clamp_factor) {
+ static fn::CustomMF_SI_SI_SI_SO<float, float, float, float> fn{
+ "Clamp Mix Float", [](float t, const float a, const float b) {
+ return math::interpolate(a, b, std::clamp(t, 0.0f, 1.0f));
+ }};
+ return &fn;
+ }
+ else {
+ static fn::CustomMF_SI_SI_SI_SO<float, float, float, float> fn{
+ "Mix Float", [](const float t, const float a, const float b) {
+ return math::interpolate(a, b, t);
+ }};
+ return &fn;
+ }
+ }
+ case SOCK_VECTOR: {
+ if (clamp_factor) {
+ if (uniform_factor) {
+ static fn::CustomMF_SI_SI_SI_SO<float, float3, float3, float3> fn{
+ "Clamp Mix Vector", [](const float t, const float3 a, const float3 b) {
+ return math::interpolate(a, b, std::clamp(t, 0.0f, 1.0f));
+ }};
+ return &fn;
+ }
+ else {
+ static fn::CustomMF_SI_SI_SI_SO<float3, float3, float3, float3> fn{
+ "Clamp Mix Vector Non Uniform", [](float3 t, const float3 a, const float3 b) {
+ t = math::clamp(t, 0.0f, 1.0f);
+ return a * (float3(1.0f) - t) + b * t;
+ }};
+ return &fn;
+ }
+ }
+ else {
+ if (uniform_factor) {
+ static fn::CustomMF_SI_SI_SI_SO<float, float3, float3, float3> fn{
+ "Mix Vector", [](const float t, const float3 a, const float3 b) {
+ return math::interpolate(a, b, t);
+ }};
+ return &fn;
+ }
+ else {
+ static fn::CustomMF_SI_SI_SI_SO<float3, float3, float3, float3> fn{
+ "Mix Vector Non Uniform", [](const float3 t, const float3 a, const float3 b) {
+ return a * (float3(1.0f) - t) + b * t;
+ }};
+ return &fn;
+ }
+ }
+ }
+ }
+ BLI_assert_unreachable();
+ return nullptr;
+}
+
+static void sh_node_mix_build_multi_function(NodeMultiFunctionBuilder &builder)
+{
+ const NodeShaderMix &storage = node_storage(builder.node());
+
+ if (storage.data_type == SOCK_RGBA) {
+ builder.construct_and_set_matching_fn<MixColorFunction>(
+ storage.clamp_factor, storage.clamp_result, storage.blend_type);
+ }
+ else {
+ const fn::MultiFunction *fn = get_multi_function(builder.node());
+ builder.set_matching_fn(fn);
+ }
+}
+
+} // namespace blender::nodes::node_sh_mix_cc
+
+void register_node_type_sh_mix()
+{
+ namespace file_ns = blender::nodes::node_sh_mix_cc;
+
+ static bNodeType ntype;
+ sh_fn_node_type_base(&ntype, SH_NODE_MIX, "Mix", NODE_CLASS_CONVERTER);
+ ntype.declare = file_ns::sh_node_mix_declare;
+ ntype.ui_class = file_ns::sh_node_mix_ui_class;
+ node_type_gpu(&ntype, file_ns::gpu_shader_mix);
+ node_type_update(&ntype, file_ns::sh_node_mix_update);
+ node_type_init(&ntype, file_ns::node_mix_init);
+ node_type_storage(
+ &ntype, "NodeShaderMix", node_free_standard_storage, node_copy_standard_storage);
+ ntype.build_multi_function = file_ns::sh_node_mix_build_multi_function;
+ ntype.draw_buttons = file_ns::sh_node_mix_layout;
+ ntype.labelfunc = file_ns::sh_node_mix_label;
+ ntype.gather_link_search_ops = file_ns::node_mix_gather_link_searches;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/shader/nodes/node_shader_mix_rgb.cc b/source/blender/nodes/shader/nodes/node_shader_mix_rgb.cc
index 478a6812c36..46ac8f05803 100644
--- a/source/blender/nodes/shader/nodes/node_shader_mix_rgb.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_mix_rgb.cc
@@ -136,7 +136,7 @@ class MixRGBFunction : public fn::MultiFunction {
static void sh_node_mix_rgb_build_multi_function(NodeMultiFunctionBuilder &builder)
{
- bNode &node = builder.node();
+ const bNode &node = builder.node();
bool clamp = node.custom2 & SHD_MIXRGB_CLAMP;
int mix_type = node.custom1;
builder.construct_and_set_matching_fn<MixRGBFunction>(clamp, mix_type);
@@ -150,11 +150,11 @@ void register_node_type_sh_mix_rgb()
static bNodeType ntype;
- sh_fn_node_type_base(&ntype, SH_NODE_MIX_RGB, "Mix", NODE_CLASS_OP_COLOR);
+ sh_fn_node_type_base(&ntype, SH_NODE_MIX_RGB_LEGACY, "Mix", NODE_CLASS_OP_COLOR);
ntype.declare = file_ns::sh_node_mix_rgb_declare;
ntype.labelfunc = node_blend_label;
node_type_gpu(&ntype, file_ns::gpu_shader_mix_rgb);
ntype.build_multi_function = file_ns::sh_node_mix_rgb_build_multi_function;
-
+ ntype.gather_link_search_ops = nullptr;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_brick.cc b/source/blender/nodes/shader/nodes/node_shader_tex_brick.cc
index cad9e1b33f2..a1c51a440d0 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_brick.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_brick.cc
@@ -261,7 +261,7 @@ class BrickFunction : public fn::MultiFunction {
static void sh_node_brick_build_multi_function(NodeMultiFunctionBuilder &builder)
{
- bNode &node = builder.node();
+ const bNode &node = builder.node();
NodeTexBrick *tex = (NodeTexBrick *)node.storage;
builder.construct_and_set_matching_fn<BrickFunction>(
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_coord.cc b/source/blender/nodes/shader/nodes/node_shader_tex_coord.cc
index 0a28b34902e..9727a6089a6 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_coord.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_coord.cc
@@ -38,8 +38,8 @@ static int node_shader_gpu_tex_coord(GPUMaterial *mat,
/* Use special matrix to let the shader branch to using the render object's matrix. */
float dummy_matrix[4][4];
dummy_matrix[3][3] = 0.0f;
- GPUNodeLink *inv_obmat = (ob != NULL) ? GPU_uniform(&ob->imat[0][0]) :
- GPU_uniform(&dummy_matrix[0][0]);
+ GPUNodeLink *inv_obmat = (ob != nullptr) ? GPU_uniform(&ob->imat[0][0]) :
+ GPU_uniform(&dummy_matrix[0][0]);
/* Optimization: don't request orco if not needed. */
float4 zero(0.0f);
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_environment.cc b/source/blender/nodes/shader/nodes/node_shader_tex_environment.cc
index df5fbac3ab5..2739a75ed21 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_environment.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_environment.cc
@@ -50,7 +50,11 @@ static int node_shader_gpu_tex_environment(GPUMaterial *mat,
return GPU_stack_link(mat, node, "node_tex_environment_empty", in, out);
}
- node_shader_gpu_default_tex_coord(mat, node, &in[0].link);
+ if (!in[0].link) {
+ GPU_link(mat, "node_tex_coord_position", &in[0].link);
+ node_shader_gpu_bump_tex_coord(mat, node, &in[0].link);
+ }
+
node_shader_gpu_tex_mapping(mat, node, in, out);
/* Compute texture coordinate. */
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_gradient.cc b/source/blender/nodes/shader/nodes/node_shader_tex_gradient.cc
index 8478cbd406b..37c72ec1f17 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_gradient.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_gradient.cc
@@ -139,7 +139,7 @@ class GradientFunction : public fn::MultiFunction {
static void sh_node_gradient_tex_build_multi_function(NodeMultiFunctionBuilder &builder)
{
- bNode &node = builder.node();
+ const bNode &node = builder.node();
NodeTexGradient *tex = (NodeTexGradient *)node.storage;
builder.construct_and_set_matching_fn<GradientFunction>(tex->gradient_type);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_magic.cc b/source/blender/nodes/shader/nodes/node_shader_tex_magic.cc
index 95c4a8b8e46..205d3b89016 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_magic.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_magic.cc
@@ -161,7 +161,7 @@ class MagicFunction : public fn::MultiFunction {
static void sh_node_magic_tex_build_multi_function(NodeMultiFunctionBuilder &builder)
{
- bNode &node = builder.node();
+ const bNode &node = builder.node();
NodeTexMagic *tex = (NodeTexMagic *)node.storage;
builder.construct_and_set_matching_fn<MagicFunction>(tex->depth);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc b/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc
index c13ce3c3df3..a2241c2327f 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc
@@ -516,7 +516,7 @@ class MusgraveFunction : public fn::MultiFunction {
static void sh_node_musgrave_build_multi_function(NodeMultiFunctionBuilder &builder)
{
- bNode &node = builder.node();
+ const bNode &node = builder.node();
NodeTexMusgrave *tex = (NodeTexMusgrave *)node.storage;
builder.construct_and_set_matching_fn<MusgraveFunction>(tex->dimensions, tex->musgrave_type);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_sky.cc b/source/blender/nodes/shader/nodes/node_shader_tex_sky.cc
index f5a4d087dbd..97b7f2616ae 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_sky.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_sky.cc
@@ -4,6 +4,8 @@
#include "node_shader_util.hh"
#include "sky_model.h"
+#include "BLI_task.hh"
+
#include "BKE_context.h"
#include "BKE_scene.h"
@@ -36,7 +38,7 @@ static void node_shader_buts_tex_sky(uiLayout *layout, bContext *C, PointerRNA *
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);
+ uiItemL(layout, TIP_("Sun disc not available in Eevee"), ICON_ERROR);
}
uiItemR(layout, ptr, "sun_disc", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, 0);
@@ -179,7 +181,7 @@ static int node_shader_gpu_tex_sky(GPUMaterial *mat,
GPU_uniform(xyz_to_rgb.g),
GPU_uniform(xyz_to_rgb.b));
}
- if (tex->sky_model == 1) {
+ else if (tex->sky_model == 1) {
/* Hosek / Wilkie */
sun_angles[0] = fmin(M_PI_2, sun_angles[0]); /* clamp to horizon */
SKY_ArHosekSkyModelState *sky_state = SKY_arhosek_xyz_skymodelstate_alloc_init(
@@ -219,8 +221,52 @@ static int node_shader_gpu_tex_sky(GPUMaterial *mat,
GPU_uniform(xyz_to_rgb.g),
GPU_uniform(xyz_to_rgb.b));
}
+ else {
+ /* Nishita */
+
+ Array<float> pixels(4 * GPU_SKY_WIDTH * GPU_SKY_HEIGHT);
+
+ threading::parallel_for(IndexRange(GPU_SKY_HEIGHT), 2, [&](IndexRange range) {
+ SKY_nishita_skymodel_precompute_texture(pixels.data(),
+ 4,
+ range.first(),
+ range.one_after_last(),
+ GPU_SKY_WIDTH,
+ GPU_SKY_HEIGHT,
+ tex->sun_elevation,
+ tex->altitude,
+ tex->air_density,
+ tex->dust_density,
+ tex->ozone_density);
+ });
+
+ float sun_rotation = fmodf(tex->sun_rotation, 2.0f * M_PI);
+ if (sun_rotation < 0.0f) {
+ sun_rotation += 2.0f * M_PI;
+ }
+ sun_rotation = 2.0f * M_PI - sun_rotation;
+
+ XYZ_to_RGB xyz_to_rgb;
+ get_XYZ_to_RGB_for_gpu(&xyz_to_rgb);
- return GPU_stack_link(mat, node, "node_tex_sky_nishita", in, out);
+ eGPUSamplerState sampler = GPU_SAMPLER_REPEAT | GPU_SAMPLER_FILTER;
+ /* To fix pole issue we clamp the v coordinate. */
+ sampler &= ~GPU_SAMPLER_REPEAT_T;
+ float layer;
+ GPUNodeLink *sky_texture = GPU_image_sky(
+ mat, GPU_SKY_WIDTH, GPU_SKY_HEIGHT, pixels.data(), &layer, sampler);
+ return GPU_stack_link(mat,
+ node,
+ "node_tex_sky_nishita",
+ in,
+ out,
+ GPU_constant(&sun_rotation),
+ GPU_uniform(xyz_to_rgb.r),
+ GPU_uniform(xyz_to_rgb.g),
+ GPU_uniform(xyz_to_rgb.b),
+ sky_texture,
+ GPU_constant(&layer));
+ }
}
static void node_shader_update_sky(bNodeTree *ntree, bNode *node)
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_wave.cc b/source/blender/nodes/shader/nodes/node_shader_tex_wave.cc
index ad24224dc7f..8475101dbaf 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_wave.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_wave.cc
@@ -206,7 +206,7 @@ class WaveFunction : public fn::MultiFunction {
static void sh_node_wave_tex_build_multi_function(NodeMultiFunctionBuilder &builder)
{
- bNode &node = builder.node();
+ const bNode &node = builder.node();
NodeTexWave *tex = (NodeTexWave *)node.storage;
builder.construct_and_set_matching_fn<WaveFunction>(
tex->wave_type, tex->bands_direction, tex->rings_direction, tex->wave_profile);
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc b/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc
index 6d4c491046b..64075a903ab 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc
@@ -176,7 +176,7 @@ class WhiteNoiseFunction : public fn::MultiFunction {
static void sh_node_noise_build_multi_function(NodeMultiFunctionBuilder &builder)
{
- bNode &node = builder.node();
+ const bNode &node = builder.node();
builder.construct_and_set_matching_fn<WhiteNoiseFunction>((int)node.custom1);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_vector_math.cc b/source/blender/nodes/shader/nodes/node_shader_vector_math.cc
index 21f5c44c640..d01e03f9d71 100644
--- a/source/blender/nodes/shader/nodes/node_shader_vector_math.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_vector_math.cc
@@ -225,7 +225,7 @@ static void node_shader_update_vector_math(bNodeTree *ntree, bNode *node)
}
}
-static const fn::MultiFunction *get_multi_function(bNode &node)
+static const fn::MultiFunction *get_multi_function(const bNode &node)
{
NodeVectorMathOperation operation = NodeVectorMathOperation(node.custom1);
diff --git a/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc b/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc
index b35f686e331..a036fc52d84 100644
--- a/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc
@@ -96,7 +96,7 @@ static float3 sh_node_vector_rotate_euler(const float3 &vector,
return result + center;
}
-static const fn::MultiFunction *get_multi_function(bNode &node)
+static const fn::MultiFunction *get_multi_function(const bNode &node)
{
bool invert = node.custom2;
const int mode = node.custom1;
diff --git a/source/blender/nodes/shader/nodes/node_shader_vector_transform.cc b/source/blender/nodes/shader/nodes/node_shader_vector_transform.cc
index de588f9005f..159bd238212 100644
--- a/source/blender/nodes/shader/nodes/node_shader_vector_transform.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_vector_transform.cc
@@ -83,7 +83,7 @@ static const char *get_gpufn_name_from_to(short from, short to, bool is_directio
}
break;
}
- return NULL;
+ return nullptr;
}
static int gpu_shader_vect_transform(GPUMaterial *mat,
diff --git a/source/blender/nodes/shader/nodes/node_shader_volume_info.cc b/source/blender/nodes/shader/nodes/node_shader_volume_info.cc
index a202312f8d8..1f31e9c605f 100644
--- a/source/blender/nodes/shader/nodes/node_shader_volume_info.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_volume_info.cc
@@ -25,9 +25,11 @@ static int node_shader_gpu_volume_info(GPUMaterial *mat,
}
if (out[1].hasoutput) {
out[1].link = GPU_attribute(mat, CD_AUTO_FROM_NAME, "density");
+ GPU_link(mat, "node_attribute_density", out[1].link, &out[1].link);
}
if (out[2].hasoutput) {
out[2].link = GPU_attribute(mat, CD_AUTO_FROM_NAME, "flame");
+ GPU_link(mat, "node_attribute_flame", out[2].link, &out[2].link);
}
if (out[3].hasoutput) {
out[3].link = GPU_attribute(mat, CD_AUTO_FROM_NAME, "temperature");
diff --git a/source/blender/nodes/texture/CMakeLists.txt b/source/blender/nodes/texture/CMakeLists.txt
index 2ccdf4c0bc9..77db71d4b1a 100644
--- a/source/blender/nodes/texture/CMakeLists.txt
+++ b/source/blender/nodes/texture/CMakeLists.txt
@@ -57,21 +57,6 @@ 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()
-
-
blender_add_lib(bf_nodes_texture "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
# RNA_prototypes.h
diff --git a/source/blender/nodes/texture/node_texture_tree.c b/source/blender/nodes/texture/node_texture_tree.c
index ac105b5bcb9..81d0b0fbc84 100644
--- a/source/blender/nodes/texture/node_texture_tree.c
+++ b/source/blender/nodes/texture/node_texture_tree.c
@@ -18,6 +18,7 @@
#include "BLT_translation.h"
#include "BKE_context.h"
+#include "BKE_layer.h"
#include "BKE_linestyle.h"
#include "BKE_node.h"
#include "BKE_paint.h"
@@ -46,7 +47,8 @@ static void texture_get_from_context(const bContext *C,
SpaceNode *snode = CTX_wm_space_node(C);
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
Tex *tx = NULL;
if (snode->texfrom == SNODE_TEX_BRUSH) {
@@ -249,7 +251,7 @@ bNodeTreeExec *ntreeTexBeginExecTree(bNodeTree *ntree)
exec = ntreeTexBeginExecTree_internal(&context, ntree, NODE_INSTANCE_KEY_BASE);
- /* XXX this should not be necessary, but is still used for cmp/sha/tex nodes,
+ /* XXX this should not be necessary, but is still used for compositor/shading/texture nodes,
* which only store the ntree pointer. Should be fixed at some point!
*/
ntree->execdata = exec;