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.txt59
-rw-r--r--source/blender/nodes/NOD_composite.h1
-rw-r--r--source/blender/nodes/NOD_derived_node_tree.hh47
-rw-r--r--source/blender/nodes/NOD_geometry.h22
-rw-r--r--source/blender/nodes/NOD_geometry_exec.hh236
-rw-r--r--source/blender/nodes/NOD_math_functions.hh5
-rw-r--r--source/blender/nodes/NOD_node_tree_multi_function.hh46
-rw-r--r--source/blender/nodes/NOD_node_tree_ref.hh70
-rw-r--r--source/blender/nodes/NOD_socket.h2
-rw-r--r--source/blender/nodes/NOD_static_types.h84
-rw-r--r--source/blender/nodes/NOD_type_conversions.hh83
-rw-r--r--source/blender/nodes/composite/node_composite_tree.c7
-rw-r--r--source/blender/nodes/composite/node_composite_util.c10
-rw-r--r--source/blender/nodes/composite/node_composite_util.h4
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_antialiasing.c65
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc133
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_image.c34
-rw-r--r--source/blender/nodes/function/node_function_util.cc10
-rw-r--r--source/blender/nodes/function/node_function_util.hh1
-rw-r--r--source/blender/nodes/geometry/node_geometry_tree.cc32
-rw-r--r--source/blender/nodes/geometry/node_geometry_util.cc10
-rw-r--r--source/blender/nodes/geometry/node_geometry_util.hh14
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc192
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc284
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc55
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_combine_xyz.cc31
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc118
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc93
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc232
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc39
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_map_range.cc437
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc106
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc166
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc38
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc96
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc62
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_separate_xyz.cc68
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc597
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc268
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc352
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_boolean.cc170
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc177
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_collection_info.cc43
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_common.cc14
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc321
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_length.cc58
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc211
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc313
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc678
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_edge_split.cc5
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_material.cc51
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_is_viewport.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc123
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_material_assign.cc107
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_material_replace.cc75
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc3
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc372
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc1
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_plane.cc)66
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc10
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc252
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc316
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_object_info.cc20
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc190
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_point_instance.cc222
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_point_rotate.cc101
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_point_scale.cc78
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_point_separate.cc53
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_point_translate.cc30
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc78
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_select_by_material.cc106
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_subdivide.cc6
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc23
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_switch.cc191
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_transform.cc60
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_triangulate.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc16
-rw-r--r--source/blender/nodes/intern/derived_node_tree.cc50
-rw-r--r--source/blender/nodes/intern/math_functions.cc2
-rw-r--r--source/blender/nodes/intern/node_common.c20
-rw-r--r--source/blender/nodes/intern/node_common.h4
-rw-r--r--source/blender/nodes/intern/node_exec.cc (renamed from source/blender/nodes/intern/node_exec.c)49
-rw-r--r--source/blender/nodes/intern/node_geometry_exec.cc101
-rw-r--r--source/blender/nodes/intern/node_socket.cc137
-rw-r--r--source/blender/nodes/intern/node_tree_multi_function.cc128
-rw-r--r--source/blender/nodes/intern/node_tree_ref.cc223
-rw-r--r--source/blender/nodes/intern/node_util.c125
-rw-r--r--source/blender/nodes/intern/type_callbacks.cc3
-rw-r--r--source/blender/nodes/intern/type_conversions.cc349
-rw-r--r--source/blender/nodes/shader/node_shader_tree.c38
-rw-r--r--source/blender/nodes/shader/node_shader_util.c32
-rw-r--r--source/blender/nodes/shader/node_shader_util.h10
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_curves.cc (renamed from source/blender/nodes/shader/nodes/node_shader_curves.c)112
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_map_range.cc182
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_mixRgb.cc (renamed from source/blender/nodes/shader/nodes/node_shader_mixRgb.c)69
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_output_aov.c5
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc13
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_sky.c19
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_valToRgb.cc8
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_vector_math.cc199
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_wavelength.c28
-rw-r--r--source/blender/nodes/texture/node_texture_tree.c7
-rw-r--r--source/blender/nodes/texture/node_texture_util.c10
-rw-r--r--source/blender/nodes/texture/node_texture_util.h4
107 files changed, 8550 insertions, 2144 deletions
diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt
index 42679f39f18..54925ea3317 100644
--- a/source/blender/nodes/CMakeLists.txt
+++ b/source/blender/nodes/CMakeLists.txt
@@ -47,6 +47,7 @@ set(INC
set(SRC
composite/nodes/node_composite_alphaOver.c
+ composite/nodes/node_composite_antialiasing.c
composite/nodes/node_composite_bilateralblur.c
composite/nodes/node_composite_blur.c
composite/nodes/node_composite_bokehblur.c
@@ -140,33 +141,48 @@ set(SRC
function/node_function_util.cc
geometry/nodes/node_geo_align_rotation_to_vector.cc
+ geometry/nodes/node_geo_attribute_clamp.cc
geometry/nodes/node_geo_attribute_color_ramp.cc
geometry/nodes/node_geo_attribute_combine_xyz.cc
geometry/nodes/node_geo_attribute_compare.cc
geometry/nodes/node_geo_attribute_convert.cc
+ geometry/nodes/node_geo_attribute_curve_map.cc
geometry/nodes/node_geo_attribute_fill.cc
+ geometry/nodes/node_geo_attribute_map_range.cc
geometry/nodes/node_geo_attribute_math.cc
geometry/nodes/node_geo_attribute_mix.cc
geometry/nodes/node_geo_attribute_proximity.cc
geometry/nodes/node_geo_attribute_randomize.cc
+ geometry/nodes/node_geo_attribute_remove.cc
geometry/nodes/node_geo_attribute_sample_texture.cc
geometry/nodes/node_geo_attribute_separate_xyz.cc
+ geometry/nodes/node_geo_attribute_transfer.cc
geometry/nodes/node_geo_attribute_vector_math.cc
- geometry/nodes/node_geo_attribute_remove.cc
+ geometry/nodes/node_geo_attribute_vector_rotate.cc
geometry/nodes/node_geo_boolean.cc
+ geometry/nodes/node_geo_bounding_box.cc
geometry/nodes/node_geo_collection_info.cc
geometry/nodes/node_geo_common.cc
+ geometry/nodes/node_geo_convex_hull.cc
+ geometry/nodes/node_geo_curve_length.cc
+ geometry/nodes/node_geo_curve_to_mesh.cc
+ geometry/nodes/node_geo_curve_resample.cc
+ geometry/nodes/node_geo_delete_geometry.cc
geometry/nodes/node_geo_edge_split.cc
+ geometry/nodes/node_geo_input_material.cc
geometry/nodes/node_geo_is_viewport.cc
geometry/nodes/node_geo_join_geometry.cc
+ geometry/nodes/node_geo_material_assign.cc
+ geometry/nodes/node_geo_material_replace.cc
geometry/nodes/node_geo_mesh_primitive_circle.cc
geometry/nodes/node_geo_mesh_primitive_cone.cc
geometry/nodes/node_geo_mesh_primitive_cube.cc
geometry/nodes/node_geo_mesh_primitive_cylinder.cc
+ geometry/nodes/node_geo_mesh_primitive_grid.cc
geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc
geometry/nodes/node_geo_mesh_primitive_line.cc
- geometry/nodes/node_geo_mesh_primitive_plane.cc
geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc
+ geometry/nodes/node_geo_mesh_to_curve.cc
geometry/nodes/node_geo_object_info.cc
geometry/nodes/node_geo_point_distribute.cc
geometry/nodes/node_geo_point_instance.cc
@@ -175,8 +191,10 @@ set(SRC
geometry/nodes/node_geo_point_separate.cc
geometry/nodes/node_geo_point_translate.cc
geometry/nodes/node_geo_points_to_volume.cc
+ geometry/nodes/node_geo_select_by_material.cc
geometry/nodes/node_geo_subdivide.cc
geometry/nodes/node_geo_subdivision_surface.cc
+ geometry/nodes/node_geo_switch.cc
geometry/nodes/node_geo_transform.cc
geometry/nodes/node_geo_triangulate.cc
geometry/nodes/node_geo_volume_to_mesh.cc
@@ -207,7 +225,7 @@ set(SRC
shader/nodes/node_shader_camera.c
shader/nodes/node_shader_clamp.cc
shader/nodes/node_shader_common.c
- shader/nodes/node_shader_curves.c
+ shader/nodes/node_shader_curves.cc
shader/nodes/node_shader_displacement.c
shader/nodes/node_shader_eevee_specular.c
shader/nodes/node_shader_emission.c
@@ -225,7 +243,7 @@ set(SRC
shader/nodes/node_shader_map_range.cc
shader/nodes/node_shader_mapping.c
shader/nodes/node_shader_math.cc
- shader/nodes/node_shader_mixRgb.c
+ shader/nodes/node_shader_mixRgb.cc
shader/nodes/node_shader_mix_shader.c
shader/nodes/node_shader_normal.c
shader/nodes/node_shader_normal_map.c
@@ -306,13 +324,14 @@ set(SRC
intern/derived_node_tree.cc
intern/math_functions.cc
intern/node_common.c
- intern/node_exec.c
+ intern/node_exec.cc
intern/node_geometry_exec.cc
intern/node_socket.cc
intern/node_tree_multi_function.cc
intern/node_tree_ref.cc
intern/node_util.c
intern/type_callbacks.cc
+ intern/type_conversions.cc
composite/node_composite_util.h
function/node_function_util.hh
@@ -334,6 +353,7 @@ set(SRC
NOD_static_types.h
NOD_texture.h
NOD_type_callbacks.hh
+ NOD_type_conversions.hh
intern/node_common.h
intern/node_exec.h
intern/node_util.h
@@ -345,6 +365,23 @@ set(LIB
bf_intern_sky
)
+if(WITH_BULLET)
+ list(APPEND INC_SYS
+ ${BULLET_INCLUDE_DIRS}
+ "../../../intern/rigidbody/"
+ )
+ if(NOT WITH_SYSTEM_BULLET)
+ list(APPEND LIB
+ extern_bullet
+ )
+ endif()
+
+ list(APPEND LIB
+ ${BULLET_LIBRARIES}
+ )
+ add_definitions(-DWITH_BULLET)
+endif()
+
if(WITH_PYTHON)
list(APPEND INC
../python
@@ -389,6 +426,18 @@ if(WITH_OPENSUBDIV)
add_definitions(-DWITH_OPENSUBDIV)
endif()
+if(WITH_GMP)
+ add_definitions(-DWITH_GMP)
+
+ list(APPEND INC_SYS
+ ${GMP_INCLUDE_DIRS}
+ )
+
+ list(APPEND LIB
+ ${GMP_LIBRARIES}
+ )
+endif()
+
if(WITH_OPENVDB)
list(APPEND INC_SYS
${OPENVDB_INCLUDE_DIRS}
diff --git a/source/blender/nodes/NOD_composite.h b/source/blender/nodes/NOD_composite.h
index b0dd0edeec5..258e4c961c9 100644
--- a/source/blender/nodes/NOD_composite.h
+++ b/source/blender/nodes/NOD_composite.h
@@ -79,6 +79,7 @@ void register_node_type_cmp_inpaint(void);
void register_node_type_cmp_despeckle(void);
void register_node_type_cmp_defocus(void);
void register_node_type_cmp_denoise(void);
+void register_node_type_cmp_antialiasing(void);
void register_node_type_cmp_valtorgb(void);
void register_node_type_cmp_rgbtobw(void);
diff --git a/source/blender/nodes/NOD_derived_node_tree.hh b/source/blender/nodes/NOD_derived_node_tree.hh
index c29de611e18..de9e4c8c812 100644
--- a/source/blender/nodes/NOD_derived_node_tree.hh
+++ b/source/blender/nodes/NOD_derived_node_tree.hh
@@ -59,6 +59,7 @@ class DTreeContext {
const NodeTreeRef *tree_;
/* All the children contexts of this context. */
Map<const NodeRef *, DTreeContext *> children_;
+ DerivedNodeTree *derived_tree_;
friend DerivedNodeTree;
@@ -67,6 +68,7 @@ class DTreeContext {
const DTreeContext *parent_context() const;
const NodeRef *parent_node() const;
const DTreeContext *child_context(const NodeRef &node) const;
+ const DerivedNodeTree &derived_tree() const;
bool is_root() const;
};
@@ -90,6 +92,12 @@ class DNode {
operator bool() const;
uint64_t hash() const;
+
+ DInputSocket input(int index) const;
+ DOutputSocket output(int index) const;
+
+ DInputSocket input_by_identifier(StringRef identifier) const;
+ DOutputSocket output_by_identifier(StringRef identifier) const;
};
/* A (nullable) reference to a socket and the context it is in. It is unique within an entire
@@ -117,6 +125,8 @@ class DSocket {
operator bool() const;
uint64_t hash() const;
+
+ DNode node() const;
};
/* A (nullable) reference to an input socket and the context it is in. */
@@ -132,7 +142,7 @@ class DInputSocket : public DSocket {
DOutputSocket get_corresponding_group_node_output() const;
Vector<DOutputSocket, 4> get_corresponding_group_input_sockets() const;
- void foreach_origin_socket(FunctionRef<void(DSocket)> callback) const;
+ void foreach_origin_socket(FunctionRef<void(DSocket)> origin_fn) const;
};
/* A (nullable) reference to an output socket and the context it is in. */
@@ -148,7 +158,8 @@ class DOutputSocket : public DSocket {
DInputSocket get_corresponding_group_node_input() const;
DInputSocket get_active_corresponding_group_output_socket() const;
- void foreach_target_socket(FunctionRef<void(DInputSocket)> callback) const;
+ void foreach_target_socket(FunctionRef<void(DInputSocket)> target_fn,
+ FunctionRef<void(DSocket)> skipped_fn) const;
};
class DerivedNodeTree {
@@ -165,6 +176,7 @@ class DerivedNodeTree {
Span<const NodeTreeRef *> used_node_tree_refs() const;
bool has_link_cycles() const;
+ bool has_undefined_nodes_or_sockets() const;
void foreach_node(FunctionRef<void(DNode)> callback) const;
std::string to_dot() const;
@@ -214,6 +226,11 @@ inline const DTreeContext *DTreeContext::child_context(const NodeRef &node) cons
return children_.lookup_default(&node, nullptr);
}
+inline const DerivedNodeTree &DTreeContext::derived_tree() const
+{
+ return *derived_tree_;
+}
+
inline bool DTreeContext::is_root() const
{
return parent_context_ == nullptr;
@@ -264,6 +281,26 @@ inline uint64_t DNode::hash() const
return get_default_hash_2(context_, node_ref_);
}
+inline DInputSocket DNode::input(int index) const
+{
+ return {context_, &node_ref_->input(index)};
+}
+
+inline DOutputSocket DNode::output(int index) const
+{
+ return {context_, &node_ref_->output(index)};
+}
+
+inline DInputSocket DNode::input_by_identifier(StringRef identifier) const
+{
+ return {context_, &node_ref_->input_by_identifier(identifier)};
+}
+
+inline DOutputSocket DNode::output_by_identifier(StringRef identifier) const
+{
+ return {context_, &node_ref_->output_by_identifier(identifier)};
+}
+
/* --------------------------------------------------------------------
* DSocket inline methods.
*/
@@ -319,6 +356,12 @@ inline uint64_t DSocket::hash() const
return get_default_hash_2(context_, socket_ref_);
}
+inline DNode DSocket::node() const
+{
+ BLI_assert(socket_ref_ != nullptr);
+ return {context_, &socket_ref_->node()};
+}
+
/* --------------------------------------------------------------------
* DInputSocket inline methods.
*/
diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h
index 03115852d80..b7e1b0b657c 100644
--- a/source/blender/nodes/NOD_geometry.h
+++ b/source/blender/nodes/NOD_geometry.h
@@ -20,38 +20,56 @@
extern "C" {
#endif
+#include "BKE_node.h"
+
extern struct bNodeTreeType *ntreeType_Geometry;
void register_node_tree_type_geo(void);
void register_node_type_geo_group(void);
+void register_node_type_geo_custom_group(bNodeType *ntype);
void register_node_type_geo_align_rotation_to_vector(void);
+void register_node_type_geo_attribute_clamp(void);
void register_node_type_geo_attribute_color_ramp(void);
void register_node_type_geo_attribute_combine_xyz(void);
void register_node_type_geo_attribute_compare(void);
void register_node_type_geo_attribute_convert(void);
+void register_node_type_geo_attribute_curve_map(void);
void register_node_type_geo_attribute_fill(void);
+void register_node_type_geo_attribute_map_range(void);
void register_node_type_geo_attribute_math(void);
void register_node_type_geo_attribute_mix(void);
void register_node_type_geo_attribute_proximity(void);
void register_node_type_geo_attribute_randomize(void);
void register_node_type_geo_attribute_separate_xyz(void);
+void register_node_type_geo_attribute_transfer(void);
void register_node_type_geo_attribute_vector_math(void);
+void register_node_type_geo_attribute_vector_rotate(void);
void register_node_type_geo_attribute_remove(void);
void register_node_type_geo_boolean(void);
+void register_node_type_geo_bounding_box(void);
void register_node_type_geo_collection_info(void);
+void register_node_type_geo_convex_hull(void);
+void register_node_type_geo_curve_length(void);
+void register_node_type_geo_curve_to_mesh(void);
+void register_node_type_geo_curve_resample(void);
+void register_node_type_geo_delete_geometry(void);
void register_node_type_geo_edge_split(void);
+void register_node_type_geo_input_material(void);
void register_node_type_geo_is_viewport(void);
void register_node_type_geo_join_geometry(void);
+void register_node_type_geo_material_assign(void);
+void register_node_type_geo_material_replace(void);
void register_node_type_geo_mesh_primitive_circle(void);
void register_node_type_geo_mesh_primitive_cone(void);
void register_node_type_geo_mesh_primitive_cube(void);
-void register_node_type_geo_mesh_primitive_plane(void);
void register_node_type_geo_mesh_primitive_cylinder(void);
+void register_node_type_geo_mesh_primitive_grid(void);
void register_node_type_geo_mesh_primitive_ico_sphere(void);
void register_node_type_geo_mesh_primitive_line(void);
void register_node_type_geo_mesh_primitive_uv_sphere(void);
+void register_node_type_geo_mesh_to_curve(void);
void register_node_type_geo_object_info(void);
void register_node_type_geo_point_distribute(void);
void register_node_type_geo_point_instance(void);
@@ -61,8 +79,10 @@ void register_node_type_geo_point_separate(void);
void register_node_type_geo_point_translate(void);
void register_node_type_geo_points_to_volume(void);
void register_node_type_geo_sample_texture(void);
+void register_node_type_geo_select_by_material(void);
void register_node_type_geo_subdivide(void);
void register_node_type_geo_subdivision_surface(void);
+void register_node_type_geo_switch(void);
void register_node_type_geo_transform(void);
void register_node_type_geo_triangulate(void);
void register_node_type_geo_volume_to_mesh(void);
diff --git a/source/blender/nodes/NOD_geometry_exec.hh b/source/blender/nodes/NOD_geometry_exec.hh
index ba606dacdb0..86c98525814 100644
--- a/source/blender/nodes/NOD_geometry_exec.hh
+++ b/source/blender/nodes/NOD_geometry_exec.hh
@@ -22,7 +22,6 @@
#include "BKE_geometry_set.hh"
#include "BKE_geometry_set_instances.hh"
#include "BKE_node_ui_storage.hh"
-#include "BKE_persistent_data_handle.hh"
#include "DNA_node_types.h"
@@ -33,55 +32,90 @@ struct ModifierData;
namespace blender::nodes {
-using bke::BooleanReadAttribute;
-using bke::BooleanWriteAttribute;
-using bke::Color4fReadAttribute;
-using bke::Color4fWriteAttribute;
-using bke::Float2ReadAttribute;
-using bke::Float2WriteAttribute;
-using bke::Float3ReadAttribute;
-using bke::Float3WriteAttribute;
-using bke::FloatReadAttribute;
-using bke::FloatWriteAttribute;
using bke::geometry_set_realize_instances;
-using bke::Int32ReadAttribute;
-using bke::Int32WriteAttribute;
-using bke::PersistentDataHandleMap;
-using bke::PersistentObjectHandle;
-using bke::ReadAttribute;
-using bke::ReadAttributePtr;
-using bke::WriteAttribute;
-using bke::WriteAttributePtr;
+using bke::OutputAttribute;
+using bke::OutputAttribute_Typed;
+using bke::ReadAttributeLookup;
+using bke::WriteAttributeLookup;
using fn::CPPType;
using fn::GMutablePointer;
+using fn::GMutableSpan;
using fn::GPointer;
+using fn::GSpan;
using fn::GValueMap;
+using fn::GVArray;
+using fn::GVArray_GSpan;
+using fn::GVArray_Span;
+using fn::GVArray_Typed;
+using fn::GVArrayPtr;
+using fn::GVMutableArray;
+using fn::GVMutableArray_GSpan;
+using fn::GVMutableArray_Typed;
+using fn::GVMutableArrayPtr;
+
+/**
+ * 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;
+
+ /**
+ * 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;
+};
class GeoNodeExecParams {
private:
- const DNode node_;
- GValueMap<StringRef> &input_values_;
- GValueMap<StringRef> &output_values_;
- const PersistentDataHandleMap &handle_map_;
- const Object *self_object_;
- const ModifierData *modifier_;
- Depsgraph *depsgraph_;
+ GeoNodeExecParamsProvider *provider_;
public:
- GeoNodeExecParams(const DNode node,
- GValueMap<StringRef> &input_values,
- GValueMap<StringRef> &output_values,
- const PersistentDataHandleMap &handle_map,
- const Object *self_object,
- const ModifierData *modifier,
- Depsgraph *depsgraph)
- : node_(node),
- input_values_(input_values),
- output_values_(output_values),
- handle_map_(handle_map),
- self_object_(self_object),
- modifier_(modifier),
- depsgraph_(depsgraph)
+ GeoNodeExecParams(GeoNodeExecParamsProvider &provider) : provider_(&provider)
{
}
@@ -94,9 +128,9 @@ class GeoNodeExecParams {
GMutablePointer extract_input(StringRef identifier)
{
#ifdef DEBUG
- this->check_extract_input(identifier);
+ this->check_input_access(identifier);
#endif
- return input_values_.extract(identifier);
+ return provider_->extract_input(identifier);
}
/**
@@ -107,9 +141,10 @@ class GeoNodeExecParams {
template<typename T> T extract_input(StringRef identifier)
{
#ifdef DEBUG
- this->check_extract_input(identifier, &CPPType::get<T>());
+ this->check_input_access(identifier, &CPPType::get<T>());
#endif
- return input_values_.extract<T>(identifier);
+ GMutablePointer gvalue = this->extract_input(identifier);
+ return gvalue.relocate_out<T>();
}
/**
@@ -119,18 +154,10 @@ class GeoNodeExecParams {
*/
template<typename T> Vector<T> extract_multi_input(StringRef identifier)
{
+ Vector<GMutablePointer> gvalues = provider_->extract_multi_input(identifier);
Vector<T> values;
- int index = 0;
- while (true) {
- std::string sub_identifier = identifier;
- if (index > 0) {
- sub_identifier += "[" + std::to_string(index) + "]";
- }
- if (!input_values_.contains(sub_identifier)) {
- break;
- }
- values.append(input_values_.extract<T, StringRef>(sub_identifier));
- index++;
+ for (GMutablePointer gvalue : gvalues) {
+ values.append(gvalue.relocate_out<T>());
}
return values;
}
@@ -141,67 +168,83 @@ class GeoNodeExecParams {
template<typename T> const T &get_input(StringRef identifier) const
{
#ifdef DEBUG
- this->check_extract_input(identifier, &CPPType::get<T>());
+ this->check_input_access(identifier, &CPPType::get<T>());
#endif
- return input_values_.lookup<T>(identifier);
+ GPointer gvalue = provider_->get_input(identifier);
+ BLI_assert(gvalue.is_type<T>());
+ return *(const T *)gvalue.get();
}
/**
- * Move-construct a new value based on the given value and store it for the given socket
- * identifier.
+ * Store the output value for the given socket identifier.
*/
- void set_output_by_move(StringRef identifier, GMutablePointer value)
+ template<typename T> void set_output(StringRef identifier, T &&value)
{
+ using StoredT = std::decay_t<T>;
+ const CPPType &type = CPPType::get<std::decay_t<T>>();
#ifdef DEBUG
- BLI_assert(value.type() != nullptr);
- BLI_assert(value.get() != nullptr);
- this->check_set_output(identifier, *value.type());
+ this->check_output_access(identifier, type);
#endif
- output_values_.add_new_by_move(identifier, value);
+ GMutablePointer gvalue = provider_->alloc_output_value(type);
+ new (gvalue.get()) StoredT(std::forward<T>(value));
+ provider_->set_output(identifier, gvalue);
}
- void set_output_by_copy(StringRef identifier, GPointer value)
+ /**
+ * Tell the evaluator that a specific input won't be used anymore.
+ */
+ void set_input_unused(StringRef identifier)
{
-#ifdef DEBUG
- BLI_assert(value.type() != nullptr);
- BLI_assert(value.get() != nullptr);
- this->check_set_output(identifier, *value.type());
-#endif
- output_values_.add_new_by_copy(identifier, value);
+ provider_->set_input_unused(identifier);
}
/**
- * Store the output value for the given socket identifier.
+ * Returns true when the output has to be computed.
+ * Nodes that support laziness could use the #lazy_output_is_required variant to possibly avoid
+ * some computations.
*/
- template<typename T> void set_output(StringRef identifier, T &&value)
+ bool output_is_required(StringRef identifier) const
{
-#ifdef DEBUG
- this->check_set_output(identifier, CPPType::get<std::decay_t<T>>());
-#endif
- output_values_.add_new(identifier, std::forward<T>(value));
+ return provider_->output_is_required(identifier);
}
/**
- * Get the node that is currently being executed.
+ * Tell the evaluator that a specific input is required.
+ * This returns true when the input will only be available in the next execution.
+ * False is returned if the input is available already.
+ * This can only be used when the node supports laziness.
*/
- const bNode &node() const
+ bool lazy_require_input(StringRef identifier)
{
- return *node_->bnode();
+ return provider_->lazy_require_input(identifier);
}
- const PersistentDataHandleMap &handle_map() const
+ /**
+ * Asks the evaluator if a specific output is required right now. If this returns false, the
+ * value might still need to be computed later.
+ * This can only be used when the node supports laziness.
+ */
+ bool lazy_output_is_required(StringRef identifier)
+ {
+ return provider_->lazy_output_is_required(identifier);
+ }
+
+ /**
+ * Get the node that is currently being executed.
+ */
+ const bNode &node() const
{
- return handle_map_;
+ return *provider_->dnode->bnode();
}
const Object *self_object() const
{
- return self_object_;
+ return provider_->self_object;
}
Depsgraph *depsgraph() const
{
- return depsgraph_;
+ return provider_->depsgraph;
}
/**
@@ -217,20 +260,21 @@ class GeoNodeExecParams {
* \note This will add an error message if the string socket is active and
* the input attribute does not exist.
*/
- ReadAttributePtr get_input_attribute(const StringRef name,
- const GeometryComponent &component,
- const AttributeDomain domain,
- const CustomDataType type,
- const void *default_value) const;
+ GVArrayPtr get_input_attribute(const StringRef name,
+ const GeometryComponent &component,
+ const AttributeDomain domain,
+ const CustomDataType type,
+ const void *default_value) const;
template<typename T>
- bke::TypedReadAttribute<T> get_input_attribute(const StringRef name,
- const GeometryComponent &component,
- const AttributeDomain domain,
- const T &default_value) const
+ GVArray_Typed<T> get_input_attribute(const StringRef name,
+ const GeometryComponent &component,
+ const AttributeDomain domain,
+ const T &default_value) const
{
const CustomDataType type = bke::cpp_type_to_custom_data_type(CPPType::get<T>());
- return this->get_input_attribute(name, component, domain, type, &default_value);
+ GVArrayPtr varray = this->get_input_attribute(name, component, domain, type, &default_value);
+ return GVArray_Typed<T>(std::move(varray));
}
/**
@@ -247,8 +291,8 @@ class GeoNodeExecParams {
private:
/* Utilities for detecting common errors at when using this class. */
- void check_extract_input(StringRef identifier, const CPPType *requested_type = nullptr) const;
- void check_set_output(StringRef identifier, const CPPType &value_type) const;
+ void check_input_access(StringRef identifier, const CPPType *requested_type = nullptr) const;
+ void check_output_access(StringRef identifier, const CPPType &value_type) const;
/* Find the active socket socket with the input name (not the identifier). */
const bNodeSocket *find_available_socket(const StringRef name) const;
diff --git a/source/blender/nodes/NOD_math_functions.hh b/source/blender/nodes/NOD_math_functions.hh
index 08496e044c5..f3eb1c24087 100644
--- a/source/blender/nodes/NOD_math_functions.hh
+++ b/source/blender/nodes/NOD_math_functions.hh
@@ -347,6 +347,8 @@ inline bool try_dispatch_float_math_fl3_fl3_fl3_to_fl3(const NodeVectorMathOpera
};
switch (operation) {
+ case NODE_VECTOR_MATH_MULTIPLY_ADD:
+ return dispatch([](float3 a, float3 b, float3 c) { return a * b + c; });
case NODE_VECTOR_MATH_WRAP:
return dispatch([](float3 a, float3 b, float3 c) {
return float3(wrapf(a.x, b.x, c.x), wrapf(a.y, b.y, c.y), wrapf(a.z, b.z, c.z));
@@ -379,7 +381,8 @@ inline bool try_dispatch_float_math_fl3_fl3_fl_to_fl3(const NodeVectorMathOperat
switch (operation) {
case NODE_VECTOR_MATH_REFRACT:
- return dispatch([](float3 a, float3 b, float c) { return float3::refract(a, b.normalized(), c); });
+ return dispatch(
+ [](float3 a, float3 b, float c) { return float3::refract(a, b.normalized(), c); });
default:
return false;
}
diff --git a/source/blender/nodes/NOD_node_tree_multi_function.hh b/source/blender/nodes/NOD_node_tree_multi_function.hh
index df31ee18369..e3f31e011e4 100644
--- a/source/blender/nodes/NOD_node_tree_multi_function.hh
+++ b/source/blender/nodes/NOD_node_tree_multi_function.hh
@@ -29,7 +29,7 @@
#include "NOD_type_callbacks.hh"
#include "BLI_multi_value_map.hh"
-#include "BLI_resource_collector.hh"
+#include "BLI_resource_scope.hh"
namespace blender::nodes {
@@ -190,7 +190,7 @@ class MFNetworkTreeMap {
* This data is necessary throughout the generation of a MFNetwork from a node tree.
*/
struct CommonMFNetworkBuilderData {
- ResourceCollector &resources;
+ ResourceScope &scope;
fn::MFNetwork &network;
MFNetworkTreeMap &network_map;
const DerivedNodeTree &tree;
@@ -225,9 +225,9 @@ class MFNetworkBuilderBase {
* Returns a resource collector that will only be destructed after the multi-function network is
* destructed.
*/
- ResourceCollector &resources()
+ ResourceScope &resource_scope()
{
- return common_.resources;
+ return common_.scope;
}
/**
@@ -236,9 +236,9 @@ class MFNetworkBuilderBase {
template<typename T, typename... Args> T &construct_fn(Args &&... args)
{
BLI_STATIC_ASSERT((std::is_base_of_v<fn::MultiFunction, T>), "");
- void *buffer = common_.resources.linear_allocator().allocate(sizeof(T), alignof(T));
+ void *buffer = common_.scope.linear_allocator().allocate(sizeof(T), alignof(T));
T *fn = new (buffer) T(std::forward<Args>(args)...);
- common_.resources.add(destruct_ptr<T>(fn), fn->name().c_str());
+ common_.scope.add(destruct_ptr<T>(fn), fn->name().c_str());
return *fn;
}
};
@@ -382,39 +382,9 @@ class NodeMFNetworkBuilder : public MFNetworkBuilderBase {
MFNetworkTreeMap insert_node_tree_into_mf_network(fn::MFNetwork &network,
const DerivedNodeTree &tree,
- ResourceCollector &resources);
+ ResourceScope &scope);
using MultiFunctionByNode = Map<DNode, const fn::MultiFunction *>;
-MultiFunctionByNode get_multi_function_per_node(const DerivedNodeTree &tree,
- ResourceCollector &resources);
-
-class DataTypeConversions {
- private:
- Map<std::pair<fn::MFDataType, fn::MFDataType>, const fn::MultiFunction *> conversions_;
-
- public:
- void add(fn::MFDataType from_type, fn::MFDataType to_type, const fn::MultiFunction &fn)
- {
- conversions_.add_new({from_type, to_type}, &fn);
- }
-
- const fn::MultiFunction *get_conversion(fn::MFDataType from, fn::MFDataType to) const
- {
- return conversions_.lookup_default({from, to}, nullptr);
- }
-
- bool is_convertible(const CPPType &from_type, const CPPType &to_type) const
- {
- return conversions_.contains(
- {fn::MFDataType::ForSingle(from_type), fn::MFDataType::ForSingle(to_type)});
- }
-
- void convert(const CPPType &from_type,
- const CPPType &to_type,
- const void *from_value,
- void *to_value) const;
-};
-
-const DataTypeConversions &get_implicit_type_conversions();
+MultiFunctionByNode get_multi_function_per_node(const DerivedNodeTree &tree, ResourceScope &scope);
} // namespace blender::nodes
diff --git a/source/blender/nodes/NOD_node_tree_ref.hh b/source/blender/nodes/NOD_node_tree_ref.hh
index 3710bd2fe00..b028fc28bbc 100644
--- a/source/blender/nodes/NOD_node_tree_ref.hh
+++ b/source/blender/nodes/NOD_node_tree_ref.hh
@@ -70,6 +70,8 @@ class NodeTreeRef;
class LinkRef;
class InternalLinkRef;
+using SocketIndexByIdentifierMap = Map<std::string, int>;
+
class SocketRef : NonCopyable, NonMovable {
protected:
NodeRef *node_;
@@ -81,15 +83,19 @@ class SocketRef : NonCopyable, NonMovable {
Vector<LinkRef *> directly_linked_links_;
/* These sockets are linked directly, i.e. with a single link in between. */
- MutableSpan<SocketRef *> directly_linked_sockets_;
+ MutableSpan<const SocketRef *> directly_linked_sockets_;
/* These sockets are linked when reroutes, muted links and muted nodes have been taken into
* account. */
- MutableSpan<SocketRef *> logically_linked_sockets_;
+ 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;
@@ -121,6 +127,7 @@ class SocketRef : NonCopyable, NonMovable {
bNodeTree *btree() const;
bool is_available() const;
+ bool is_undefined() const;
void *default_value() const;
template<typename T> T *default_value() const;
@@ -128,16 +135,31 @@ class SocketRef : NonCopyable, NonMovable {
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 *> &handled_sockets) 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 *> &handled_sockets) const;
};
class NodeRef : NonCopyable, NonMovable {
@@ -149,6 +171,8 @@ class NodeRef : NonCopyable, NonMovable {
Vector<InputSocketRef *> inputs_;
Vector<OutputSocketRef *> outputs_;
Vector<InternalLinkRef *> internal_links_;
+ SocketIndexByIdentifierMap *input_index_by_identifier_;
+ SocketIndexByIdentifierMap *output_index_by_identifier_;
friend NodeTreeRef;
@@ -162,6 +186,9 @@ class NodeRef : NonCopyable, NonMovable {
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;
+
bNode *bnode() const;
bNodeTree *btree() const;
@@ -178,6 +205,7 @@ class NodeRef : NonCopyable, NonMovable {
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;
@@ -225,6 +253,7 @@ class NodeTreeRef : NonCopyable, NonMovable {
Vector<OutputSocketRef *> output_sockets_;
Vector<LinkRef *> links_;
MultiValueMap<const bNodeType *, NodeRef *> nodes_by_type_;
+ Vector<std::unique_ptr<SocketIndexByIdentifierMap>> owned_identifier_maps_;
public:
NodeTreeRef(bNodeTree *btree);
@@ -241,6 +270,7 @@ class NodeTreeRef : NonCopyable, NonMovable {
Span<const LinkRef *> links() const;
bool has_link_cycles() const;
+ bool has_undefined_nodes_or_sockets() const;
bNodeTree *btree() const;
StringRefNull name() const;
@@ -257,12 +287,7 @@ class NodeTreeRef : NonCopyable, NonMovable {
bNodeSocket *bsocket);
void create_linked_socket_caches();
-
- void foreach_logical_origin(InputSocketRef &socket,
- FunctionRef<void(OutputSocketRef &)> callback,
- bool only_follow_first_input_link = false);
- void foreach_logical_target(OutputSocketRef &socket,
- FunctionRef<void(InputSocketRef &)> callback);
+ void create_socket_identifier_maps();
};
using NodeTreeRefMap = Map<bNodeTree *, std::unique_ptr<const NodeTreeRef>>;
@@ -287,6 +312,11 @@ 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_;
@@ -399,6 +429,11 @@ 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;
@@ -476,6 +511,18 @@ 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_;
@@ -518,7 +565,7 @@ inline bool NodeRef::is_reroute_node() const
inline bool NodeRef::is_group_node() const
{
- return bnode_->type == NODE_GROUP;
+ return bnode_->type == NODE_GROUP || bnode_->type == NODE_CUSTOM_GROUP;
}
inline bool NodeRef::is_group_input_node() const
@@ -536,6 +583,11 @@ 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;
diff --git a/source/blender/nodes/NOD_socket.h b/source/blender/nodes/NOD_socket.h
index 3344a25bdea..6bcfda70d04 100644
--- a/source/blender/nodes/NOD_socket.h
+++ b/source/blender/nodes/NOD_socket.h
@@ -41,7 +41,7 @@ extern "C" {
struct bNodeSocket *node_add_socket_from_template(struct bNodeTree *ntree,
struct bNode *node,
struct bNodeSocketTemplate *stemp,
- int in_out);
+ eNodeSocketInOut in_out);
void node_verify_socket_templates(struct bNodeTree *ntree, struct bNode *node);
diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h
index ec687ae6b70..b255c6e5f23 100644
--- a/source/blender/nodes/NOD_static_types.h
+++ b/source/blender/nodes/NOD_static_types.h
@@ -224,6 +224,7 @@ DefNode(CompositorNode, CMP_NODE_CRYPTOMATTE, def_cmp_cryptomatte, "CRYPTO
DefNode(CompositorNode, CMP_NODE_CRYPTOMATTE_LEGACY, def_cmp_cryptomatte_legacy, "CRYPTOMATTE", Cryptomatte, "Cryptomatte (Legacy)", "" )
DefNode(CompositorNode, CMP_NODE_DENOISE, def_cmp_denoise, "DENOISE", Denoise, "Denoise", "" )
DefNode(CompositorNode, CMP_NODE_EXPOSURE, 0, "EXPOSURE", Exposure, "Exposure", "" )
+DefNode(CompositorNode, CMP_NODE_ANTIALIASING, def_cmp_antialiasing, "ANTIALIASING", AntiAliasing, "Anti-Aliasing", "" )
DefNode(TextureNode, TEX_NODE_OUTPUT, def_tex_output, "OUTPUT", Output, "Output", "" )
DefNode(TextureNode, TEX_NODE_CHECKER, 0, "CHECKER", Checker, "Checker", "" )
@@ -262,50 +263,67 @@ DefNode(TextureNode, TEX_NODE_PROC+TEX_DISTNOISE, 0, "TEX_DI
DefNode(FunctionNode, FN_NODE_BOOLEAN_MATH, def_boolean_math, "BOOLEAN_MATH", BooleanMath, "Boolean Math", "")
DefNode(FunctionNode, FN_NODE_FLOAT_COMPARE, def_float_compare, "FLOAT_COMPARE", FloatCompare, "Float Compare", "")
-DefNode(FunctionNode, FN_NODE_RANDOM_FLOAT, 0, "RANDOM_FLOAT", RandomFloat, "Random Float", "")
-DefNode(FunctionNode, FN_NODE_INPUT_VECTOR, def_fn_input_vector, "INPUT_VECTOR", InputVector, "Vector", "")
DefNode(FunctionNode, FN_NODE_INPUT_STRING, def_fn_input_string, "INPUT_STRING", InputString, "String", "")
+DefNode(FunctionNode, FN_NODE_INPUT_VECTOR, def_fn_input_vector, "INPUT_VECTOR", InputVector, "Vector", "")
+DefNode(FunctionNode, FN_NODE_RANDOM_FLOAT, 0, "RANDOM_FLOAT", RandomFloat, "Random Float", "")
-DefNode(GeometryNode, GEO_NODE_TRIANGULATE, def_geo_triangulate, "TRIANGULATE", Triangulate, "Triangulate", "")
-DefNode(GeometryNode, GEO_NODE_EDGE_SPLIT, 0, "EDGE_SPLIT", EdgeSplit, "Edge Split", "")
-DefNode(GeometryNode, GEO_NODE_TRANSFORM, 0, "TRANSFORM", Transform, "Transform", "")
-DefNode(GeometryNode, GEO_NODE_SUBDIVISION_SURFACE, 0, "SUBDIVISION_SURFACE", SubdivisionSurface, "Subdivision Surface", "")
-DefNode(GeometryNode, GEO_NODE_BOOLEAN, def_geo_boolean, "BOOLEAN", Boolean, "Boolean", "")
-DefNode(GeometryNode, GEO_NODE_POINT_DISTRIBUTE, def_geo_point_distribute, "POINT_DISTRIBUTE", PointDistribute, "Point Distribute", "")
-DefNode(GeometryNode, GEO_NODE_POINT_INSTANCE, def_geo_point_instance, "POINT_INSTANCE", PointInstance, "Point Instance", "")
-DefNode(GeometryNode, GEO_NODE_OBJECT_INFO, def_geo_object_info, "OBJECT_INFO", ObjectInfo, "Object Info", "")
-DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_RANDOMIZE, def_geo_attribute_randomize, "ATTRIBUTE_RANDOMIZE", AttributeRandomize, "Attribute Randomize", "")
-DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MATH, def_geo_attribute_math, "ATTRIBUTE_MATH", AttributeMath, "Attribute Math", "")
-DefNode(GeometryNode, GEO_NODE_JOIN_GEOMETRY, 0, "JOIN_GEOMETRY", JoinGeometry, "Join Geometry", "")
-DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_FILL, def_geo_attribute_fill, "ATTRIBUTE_FILL", AttributeFill, "Attribute Fill", "")
-DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MIX, def_geo_attribute_mix, "ATTRIBUTE_MIX", AttributeMix, "Attribute Mix", "")
+DefNode(GeometryNode, GEO_NODE_ALIGN_ROTATION_TO_VECTOR, def_geo_align_rotation_to_vector, "ALIGN_ROTATION_TO_VECTOR", AlignRotationToVector, "Align Rotation to Vector", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_CLAMP, def_geo_attribute_clamp, "ATTRIBUTE_CLAMP", AttributeClamp, "Attribute Clamp", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_COLOR_RAMP, def_geo_attribute_color_ramp, "ATTRIBUTE_COLOR_RAMP", AttributeColorRamp, "Attribute Color Ramp", "")
-DefNode(GeometryNode, GEO_NODE_POINT_SEPARATE, 0, "POINT_SEPARATE", PointSeparate, "Point Separate", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_COMBINE_XYZ, def_geo_attribute_combine_xyz, "ATTRIBUTE_COMBINE_XYZ", AttributeCombineXYZ, "Attribute Combine XYZ", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_COMPARE, def_geo_attribute_attribute_compare, "ATTRIBUTE_COMPARE", AttributeCompare, "Attribute Compare", "")
-DefNode(GeometryNode, GEO_NODE_POINT_ROTATE, def_geo_point_rotate, "POINT_ROTATE", RotatePoints, "Point Rotate", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_CONVERT, def_geo_attribute_convert, "ATTRIBUTE_CONVERT", AttributeConvert, "Attribute Convert", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_CURVE_MAP, def_geo_attribute_curve_map, "ATTRIBUTE_CURVE_MAP", AttributeCurveMap, "Attribute Curve Map", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_FILL, def_geo_attribute_fill, "ATTRIBUTE_FILL", AttributeFill, "Attribute Fill", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MAP_RANGE, def_geo_attribute_map_range, "ATTRIBUTE_MAP_RANGE", AttributeMapRange, "Attribute Map Range", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MATH, def_geo_attribute_math, "ATTRIBUTE_MATH", AttributeMath, "Attribute Math", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MIX, def_geo_attribute_mix, "ATTRIBUTE_MIX", AttributeMix, "Attribute Mix", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_PROXIMITY, def_geo_attribute_proximity, "ATTRIBUTE_PROXIMITY", AttributeProximity, "Attribute Proximity", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_RANDOMIZE, def_geo_attribute_randomize, "ATTRIBUTE_RANDOMIZE", AttributeRandomize, "Attribute Randomize", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_REMOVE, 0, "ATTRIBUTE_REMOVE", AttributeRemove, "Attribute Remove", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE, 0, "ATTRIBUTE_SAMPLE_TEXTURE", AttributeSampleTexture, "Attribute Sample Texture", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_SEPARATE_XYZ, def_geo_attribute_separate_xyz, "ATTRIBUTE_SEPARATE_XYZ", AttributeSeparateXYZ, "Attribute Separate XYZ", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_TRANSFER, def_geo_attribute_transfer, "ATTRIBUTE_TRANSFER", AttributeTransfer, "Attribute Transfer", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_VECTOR_MATH, def_geo_attribute_vector_math, "ATTRIBUTE_VECTOR_MATH", AttributeVectorMath, "Attribute Vector Math", "")
-DefNode(GeometryNode, GEO_NODE_ALIGN_ROTATION_TO_VECTOR, def_geo_align_rotation_to_vector, "ALIGN_ROTATION_TO_VECTOR", AlignRotationToVector, "Align Rotation to Vector", "")
-DefNode(GeometryNode, GEO_NODE_POINT_SCALE, def_geo_point_scale, "POINT_SCALE", PointScale, "Point Scale", "")
-DefNode(GeometryNode, GEO_NODE_POINT_TRANSLATE, def_geo_point_translate, "POINT_TRANSLATE", PointTranslate, "Point Translate", "")
-DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE, def_geo_attribute_sample_texture, "ATTRIBUTE_SAMPLE_TEXTURE", AttributeSampleTexture, "Attribute Sample Texture", "")
-DefNode(GeometryNode, GEO_NODE_POINTS_TO_VOLUME, def_geo_points_to_volume, "POINTS_TO_VOLUME", PointsToVolume, "Points to Volume", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_VECTOR_ROTATE, def_geo_attribute_vector_rotate, "ATTRIBUTE_VECTOR_ROTATE", AttributeVectorRotate, "Attribute Vector Rotate", "")
+DefNode(GeometryNode, GEO_NODE_BOOLEAN, def_geo_boolean, "BOOLEAN", Boolean, "Boolean", "")
+DefNode(GeometryNode, GEO_NODE_BOUNDING_BOX, 0, "BOUNDING_BOX", BoundBox, "Bounding Box", "")
DefNode(GeometryNode, GEO_NODE_COLLECTION_INFO, def_geo_collection_info, "COLLECTION_INFO", CollectionInfo, "Collection Info", "")
+DefNode(GeometryNode, GEO_NODE_CONVEX_HULL, 0, "CONVEX_HULL", ConvexHull, "Convex Hull", "")
+DefNode(GeometryNode, GEO_NODE_CURVE_LENGTH, 0, "CURVE_LENGTH", CurveLength, "Curve Length", "")
+DefNode(GeometryNode, GEO_NODE_CURVE_RESAMPLE, def_geo_curve_resample, "CURVE_RESAMPLE", CurveResample, "Resample Curve", "")
+DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, "Curve to Mesh", "")
+DefNode(GeometryNode, GEO_NODE_DELETE_GEOMETRY, 0, "DELETE_GEOMETRY", DeleteGeometry, "Delete Geometry", "")
+DefNode(GeometryNode, GEO_NODE_EDGE_SPLIT, 0, "EDGE_SPLIT", EdgeSplit, "Edge Split", "")
+DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL, def_geo_input_material, "INPUT_MATERIAL", InputMaterial, "Material", "")
DefNode(GeometryNode, GEO_NODE_IS_VIEWPORT, 0, "IS_VIEWPORT", IsViewport, "Is Viewport", "")
-DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_PROXIMITY, def_geo_attribute_proximity, "ATTRIBUTE_PROXIMITY", AttributeProximity, "Attribute Proximity", "")
-DefNode(GeometryNode, GEO_NODE_VOLUME_TO_MESH, def_geo_volume_to_mesh, "VOLUME_TO_MESH", VolumeToMesh, "Volume to Mesh", "")
-DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_COMBINE_XYZ, def_geo_attribute_combine_xyz, "ATTRIBUTE_COMBINE_XYZ", AttributeCombineXYZ, "Attribute Combine XYZ", "")
-DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_SEPARATE_XYZ, def_geo_attribute_separate_xyz, "ATTRIBUTE_SEPARATE_XYZ", AttributeSeparateXYZ, "Attribute Separate XYZ", "")
-DefNode(GeometryNode, GEO_NODE_SUBDIVIDE, 0, "SUBDIVIDE", Subdivide, "Subdivide", "")
-DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_REMOVE, 0, "ATTRIBUTE_REMOVE", AttributeRemove, "Attribute Remove", "")
-DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_CONVERT, def_geo_attribute_convert, "ATTRIBUTE_CONVERT", AttributeConvert, "Attribute Convert", "")
-DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CUBE, 0, "MESH_PRIMITIVE_CUBE", MeshCube, "Cube", "")
+DefNode(GeometryNode, GEO_NODE_JOIN_GEOMETRY, 0, "JOIN_GEOMETRY", JoinGeometry, "Join Geometry", "")
+DefNode(GeometryNode, GEO_NODE_MATERIAL_ASSIGN, 0, "MATERIAL_ASSIGN", MaterialAssign, "Material Assign", "")
+DefNode(GeometryNode, GEO_NODE_MATERIAL_REPLACE, 0, "MATERIAL_REPLACE", MaterialReplace, "Material Replace", "")
DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CIRCLE, def_geo_mesh_circle, "MESH_PRIMITIVE_CIRCLE", MeshCircle, "Circle", "")
-DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, 0, "MESH_PRIMITIVE_UV_SPHERE", MeshUVSphere, "UV Sphere", "")
+DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CONE, def_geo_mesh_cone, "MESH_PRIMITIVE_CONE", MeshCone, "Cone", "")
+DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CUBE, 0, "MESH_PRIMITIVE_CUBE", MeshCube, "Cube", "")
DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CYLINDER, def_geo_mesh_cylinder, "MESH_PRIMITIVE_CYLINDER", MeshCylinder, "Cylinder", "")
+DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_GRID, 0, "MESH_PRIMITIVE_GRID", MeshGrid, "Grid", "")
DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE, 0, "MESH_PRIMITIVE_ICO_SPHERE", MeshIcoSphere, "Ico Sphere", "")
-DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CONE, def_geo_mesh_cone, "MESH_PRIMITIVE_CONE", MeshCone, "Cone", "")
DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_LINE, def_geo_mesh_line, "MESH_PRIMITIVE_LINE", MeshLine, "Line", "")
-DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_PLANE, 0, "MESH_PRIMITIVE_PLANE", MeshPlane, "Plane", "")
+DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, 0, "MESH_PRIMITIVE_UV_SPHERE", MeshUVSphere, "UV Sphere", "")
+DefNode(GeometryNode, GEO_NODE_MESH_TO_CURVE, 0, "MESH_TO_CURVE", MeshToCurve, "Mesh to Curve", "")
+DefNode(GeometryNode, GEO_NODE_OBJECT_INFO, def_geo_object_info, "OBJECT_INFO", ObjectInfo, "Object Info", "")
+DefNode(GeometryNode, GEO_NODE_POINT_DISTRIBUTE, def_geo_point_distribute, "POINT_DISTRIBUTE", PointDistribute, "Point Distribute", "")
+DefNode(GeometryNode, GEO_NODE_POINT_INSTANCE, def_geo_point_instance, "POINT_INSTANCE", PointInstance, "Point Instance", "")
+DefNode(GeometryNode, GEO_NODE_POINT_ROTATE, def_geo_point_rotate, "POINT_ROTATE", RotatePoints, "Point Rotate", "")
+DefNode(GeometryNode, GEO_NODE_POINT_SCALE, def_geo_point_scale, "POINT_SCALE", PointScale, "Point Scale", "")
+DefNode(GeometryNode, GEO_NODE_POINT_SEPARATE, 0, "POINT_SEPARATE", PointSeparate, "Point Separate", "")
+DefNode(GeometryNode, GEO_NODE_POINT_TRANSLATE, def_geo_point_translate, "POINT_TRANSLATE", PointTranslate, "Point Translate", "")
+DefNode(GeometryNode, GEO_NODE_POINTS_TO_VOLUME, def_geo_points_to_volume, "POINTS_TO_VOLUME", PointsToVolume, "Points to Volume", "")
+DefNode(GeometryNode, GEO_NODE_SELECT_BY_MATERIAL, 0, "SELECT_BY_MATERIAL", SelectByMaterial, "Select by Material", "")
+DefNode(GeometryNode, GEO_NODE_SUBDIVIDE, 0, "SUBDIVIDE", Subdivide, "Subdivide", "")
+DefNode(GeometryNode, GEO_NODE_SUBDIVISION_SURFACE, 0, "SUBDIVISION_SURFACE", SubdivisionSurface, "Subdivision Surface", "")
+DefNode(GeometryNode, GEO_NODE_SWITCH, def_geo_switch, "SWITCH", Switch, "Switch", "")
+DefNode(GeometryNode, GEO_NODE_TRANSFORM, 0, "TRANSFORM", Transform, "Transform", "")
+DefNode(GeometryNode, GEO_NODE_TRIANGULATE, def_geo_triangulate, "TRIANGULATE", Triangulate, "Triangulate", "")
+DefNode(GeometryNode, GEO_NODE_VOLUME_TO_MESH, def_geo_volume_to_mesh, "VOLUME_TO_MESH", VolumeToMesh, "Volume to Mesh", "")
/* undefine macros */
#undef DefNode
diff --git a/source/blender/nodes/NOD_type_conversions.hh b/source/blender/nodes/NOD_type_conversions.hh
new file mode 100644
index 00000000000..ec4859f0657
--- /dev/null
+++ b/source/blender/nodes/NOD_type_conversions.hh
@@ -0,0 +1,83 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#include "FN_multi_function.hh"
+
+namespace blender::nodes {
+
+using fn::CPPType;
+using fn::GVArray;
+
+struct ConversionFunctions {
+ const fn::MultiFunction *multi_function;
+ void (*convert_single_to_initialized)(const void *src, void *dst);
+ void (*convert_single_to_uninitialized)(const void *src, void *dst);
+};
+
+class DataTypeConversions {
+ private:
+ Map<std::pair<fn::MFDataType, fn::MFDataType>, ConversionFunctions> conversions_;
+
+ public:
+ void add(fn::MFDataType from_type,
+ fn::MFDataType to_type,
+ const fn::MultiFunction &fn,
+ void (*convert_single_to_initialized)(const void *src, void *dst),
+ void (*convert_single_to_uninitialized)(const void *src, void *dst))
+ {
+ conversions_.add_new({from_type, to_type},
+ {&fn, convert_single_to_initialized, convert_single_to_uninitialized});
+ }
+
+ const ConversionFunctions *get_conversion_functions(fn::MFDataType from, fn::MFDataType to) const
+ {
+ return conversions_.lookup_ptr({from, to});
+ }
+
+ const ConversionFunctions *get_conversion_functions(const CPPType &from, const CPPType &to) const
+ {
+ return this->get_conversion_functions(fn::MFDataType::ForSingle(from),
+ fn::MFDataType::ForSingle(to));
+ }
+
+ const fn::MultiFunction *get_conversion_multi_function(fn::MFDataType from,
+ fn::MFDataType to) const
+ {
+ const ConversionFunctions *functions = this->get_conversion_functions(from, to);
+ return functions ? functions->multi_function : nullptr;
+ }
+
+ bool is_convertible(const CPPType &from_type, const CPPType &to_type) const
+ {
+ return conversions_.contains(
+ {fn::MFDataType::ForSingle(from_type), fn::MFDataType::ForSingle(to_type)});
+ }
+
+ void convert_to_uninitialized(const CPPType &from_type,
+ const CPPType &to_type,
+ const void *from_value,
+ void *to_value) const;
+
+ fn::GVArrayPtr try_convert(fn::GVArrayPtr varray, const CPPType &to_type) const;
+
+ fn::GVMutableArrayPtr try_convert(fn::GVMutableArrayPtr varray, const CPPType &to_type) const;
+};
+
+const DataTypeConversions &get_implicit_type_conversions();
+
+} // namespace blender::nodes
diff --git a/source/blender/nodes/composite/node_composite_tree.c b/source/blender/nodes/composite/node_composite_tree.c
index 085b5c463b9..19815d01278 100644
--- a/source/blender/nodes/composite/node_composite_tree.c
+++ b/source/blender/nodes/composite/node_composite_tree.c
@@ -205,6 +205,12 @@ static void composite_node_add_init(bNodeTree *UNUSED(bnodetree), bNode *bnode)
}
}
+static bool composite_node_tree_socket_type_valid(eNodeSocketDatatype socket_type,
+ bNodeTreeType *UNUSED(ntreetype))
+{
+ return ELEM(socket_type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA);
+}
+
bNodeTreeType *ntreeType_Composite;
void register_node_tree_type_cmp(void)
@@ -227,6 +233,7 @@ void register_node_tree_type_cmp(void)
tt->update = update;
tt->get_from_context = composite_get_from_context;
tt->node_add_init = composite_node_add_init;
+ tt->valid_socket_type = composite_node_tree_socket_type_valid;
tt->rna_ext.srna = &RNA_CompositorNodeTree;
diff --git a/source/blender/nodes/composite/node_composite_util.c b/source/blender/nodes/composite/node_composite_util.c
index b6cbffea413..6cc17d8c272 100644
--- a/source/blender/nodes/composite/node_composite_util.c
+++ b/source/blender/nodes/composite/node_composite_util.c
@@ -23,9 +23,15 @@
#include "node_composite_util.h"
-bool cmp_node_poll_default(bNodeType *UNUSED(ntype), bNodeTree *ntree)
+bool cmp_node_poll_default(bNodeType *UNUSED(ntype),
+ bNodeTree *ntree,
+ const char **r_disabled_hint)
{
- return STREQ(ntree->idname, "CompositorNodeTree");
+ if (!STREQ(ntree->idname, "CompositorNodeTree")) {
+ *r_disabled_hint = "Not a compositor node tree";
+ return false;
+ }
+ return true;
}
void cmp_node_update_default(bNodeTree *UNUSED(ntree), bNode *node)
diff --git a/source/blender/nodes/composite/node_composite_util.h b/source/blender/nodes/composite/node_composite_util.h
index 800c55df4d6..4fcccbb79f0 100644
--- a/source/blender/nodes/composite/node_composite_util.h
+++ b/source/blender/nodes/composite/node_composite_util.h
@@ -54,7 +54,9 @@ extern "C" {
#define CMP_SCALE_MAX 12000
-bool cmp_node_poll_default(struct bNodeType *ntype, struct bNodeTree *ntree);
+bool cmp_node_poll_default(struct bNodeType *ntype,
+ struct bNodeTree *ntree,
+ const char **r_disabled_info);
void cmp_node_update_default(struct bNodeTree *ntree, struct bNode *node);
void cmp_node_type_base(
struct bNodeType *ntype, int type, const char *name, short nclass, short flag);
diff --git a/source/blender/nodes/composite/nodes/node_composite_antialiasing.c b/source/blender/nodes/composite/nodes/node_composite_antialiasing.c
new file mode 100644
index 00000000000..7437496d878
--- /dev/null
+++ b/source/blender/nodes/composite/nodes/node_composite_antialiasing.c
@@ -0,0 +1,65 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2017 Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): IRIE Shinsuke
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/nodes/composite/nodes/node_composite_antialiasing.c
+ * \ingroup cmpnodes
+ */
+
+#include "node_composite_util.h"
+
+/* **************** Anti-Aliasing (SMAA 1x) ******************** */
+
+static bNodeSocketTemplate cmp_node_antialiasing_in[] = {
+ {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, {-1, ""}};
+
+static bNodeSocketTemplate cmp_node_antialiasing_out[] = {{SOCK_RGBA, N_("Image")}, {-1, ""}};
+
+static void node_composit_init_antialiasing(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeAntiAliasingData *data = MEM_callocN(sizeof(NodeAntiAliasingData), "node antialiasing data");
+
+ data->threshold = 1.0f;
+ data->contrast_limit = 0.2f;
+ data->corner_rounding = 0.25f;
+
+ node->storage = data;
+}
+
+void register_node_type_cmp_antialiasing(void)
+{
+ static bNodeType ntype;
+
+ cmp_node_type_base(
+ &ntype, CMP_NODE_ANTIALIASING, "Anti-Aliasing", NODE_CLASS_OP_FILTER, NODE_PREVIEW);
+ node_type_socket_templates(&ntype, cmp_node_antialiasing_in, cmp_node_antialiasing_out);
+ node_type_size(&ntype, 170, 140, 200);
+ node_type_init(&ntype, node_composit_init_antialiasing);
+ node_type_storage(
+ &ntype, "NodeAntiAliasingData", node_free_standard_storage, node_copy_standard_storage);
+
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc
index c5135482ec2..51dd73a86af 100644
--- a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc
@@ -40,61 +40,74 @@
/** \name Cryptomatte
* \{ */
+static blender::bke::cryptomatte::CryptomatteSessionPtr cryptomatte_init_from_node_render(
+ const bNode &node, const bool use_meta_data)
+{
+ blender::bke::cryptomatte::CryptomatteSessionPtr session;
+
+ Scene *scene = (Scene *)node.id;
+ if (!scene) {
+ return session;
+ }
+ BLI_assert(GS(scene->id.name) == ID_SCE);
+
+ if (use_meta_data) {
+ Render *render = RE_GetSceneRender(scene);
+ RenderResult *render_result = render ? RE_AcquireResultRead(render) : nullptr;
+ if (render_result) {
+ session = blender::bke::cryptomatte::CryptomatteSessionPtr(
+ BKE_cryptomatte_init_from_render_result(render_result));
+ }
+ if (render) {
+ RE_ReleaseResult(render);
+ }
+ }
+
+ if (session == nullptr) {
+ session = blender::bke::cryptomatte::CryptomatteSessionPtr(
+ BKE_cryptomatte_init_from_scene(scene));
+ }
+ return session;
+}
+
+static blender::bke::cryptomatte::CryptomatteSessionPtr cryptomatte_init_from_node_image(
+ const Scene &scene, const bNode &node)
+{
+ blender::bke::cryptomatte::CryptomatteSessionPtr session;
+ Image *image = (Image *)node.id;
+ if (!image) {
+ return session;
+ }
+ BLI_assert(GS(image->id.name) == ID_IM);
+
+ NodeCryptomatte *node_cryptomatte = static_cast<NodeCryptomatte *>(node.storage);
+ ImageUser *iuser = &node_cryptomatte->iuser;
+ BKE_image_user_frame_calc(image, iuser, scene.r.cfra);
+ ImBuf *ibuf = BKE_image_acquire_ibuf(image, iuser, nullptr);
+ RenderResult *render_result = image->rr;
+ if (render_result) {
+ session = blender::bke::cryptomatte::CryptomatteSessionPtr(
+ BKE_cryptomatte_init_from_render_result(render_result));
+ }
+ BKE_image_release_ibuf(image, ibuf, nullptr);
+ return session;
+}
static blender::bke::cryptomatte::CryptomatteSessionPtr cryptomatte_init_from_node(
- const bNode &node, const int frame_number, const bool use_meta_data)
+ const Scene &scene, const bNode &node, const bool use_meta_data)
{
blender::bke::cryptomatte::CryptomatteSessionPtr session;
if (node.type != CMP_NODE_CRYPTOMATTE) {
return session;
}
- NodeCryptomatte *node_cryptomatte = static_cast<NodeCryptomatte *>(node.storage);
switch (node.custom1) {
case CMP_CRYPTOMATTE_SRC_RENDER: {
- Scene *scene = (Scene *)node.id;
- if (!scene) {
- return session;
- }
- BLI_assert(GS(scene->id.name) == ID_SCE);
-
- if (use_meta_data) {
- Render *render = (scene) ? RE_GetSceneRender(scene) : nullptr;
- RenderResult *render_result = render ? RE_AcquireResultRead(render) : nullptr;
- if (render_result) {
- session = blender::bke::cryptomatte::CryptomatteSessionPtr(
- BKE_cryptomatte_init_from_render_result(render_result));
- }
- if (render) {
- RE_ReleaseResult(render);
- }
- }
-
- if (session == nullptr) {
- session = blender::bke::cryptomatte::CryptomatteSessionPtr(
- BKE_cryptomatte_init_from_scene(scene));
- }
-
- break;
+ return cryptomatte_init_from_node_render(node, use_meta_data);
}
case CMP_CRYPTOMATTE_SRC_IMAGE: {
- Image *image = (Image *)node.id;
- if (!image) {
- break;
- }
- BLI_assert(GS(image->id.name) == ID_IM);
-
- ImageUser *iuser = &node_cryptomatte->iuser;
- BKE_image_user_frame_calc(image, iuser, frame_number);
- ImBuf *ibuf = BKE_image_acquire_ibuf(image, iuser, nullptr);
- RenderResult *render_result = image->rr;
- if (render_result) {
- session = blender::bke::cryptomatte::CryptomatteSessionPtr(
- BKE_cryptomatte_init_from_render_result(render_result));
- }
- BKE_image_release_ibuf(image, ibuf, nullptr);
- break;
+ return cryptomatte_init_from_node_image(scene, node);
}
}
return session;
@@ -111,7 +124,10 @@ static CryptomatteEntry *cryptomatte_find(const NodeCryptomatte &n, float encode
return nullptr;
}
-static void cryptomatte_add(bNode &node, NodeCryptomatte &node_cryptomatte, float encoded_hash)
+static void cryptomatte_add(const Scene &scene,
+ bNode &node,
+ NodeCryptomatte &node_cryptomatte,
+ float encoded_hash)
{
/* Check if entry already exist. */
if (cryptomatte_find(node_cryptomatte, encoded_hash)) {
@@ -121,9 +137,8 @@ static void cryptomatte_add(bNode &node, NodeCryptomatte &node_cryptomatte, floa
CryptomatteEntry *entry = static_cast<CryptomatteEntry *>(
MEM_callocN(sizeof(CryptomatteEntry), __func__));
entry->encoded_hash = encoded_hash;
- /* TODO(jbakker): Get current frame from scene. */
blender::bke::cryptomatte::CryptomatteSessionPtr session = cryptomatte_init_from_node(
- node, 0, true);
+ scene, node, true);
if (session) {
BKE_cryptomatte_find_name(session.get(), encoded_hash, entry->name, sizeof(entry->name));
}
@@ -151,12 +166,12 @@ static bNodeSocketTemplate cmp_node_cryptomatte_out[] = {
{-1, ""},
};
-void ntreeCompositCryptomatteSyncFromAdd(bNode *node)
+void ntreeCompositCryptomatteSyncFromAdd(const Scene *scene, bNode *node)
{
BLI_assert(ELEM(node->type, CMP_NODE_CRYPTOMATTE, CMP_NODE_CRYPTOMATTE_LEGACY));
NodeCryptomatte *n = static_cast<NodeCryptomatte *>(node->storage);
if (n->runtime.add[0] != 0.0f) {
- cryptomatte_add(*node, *n, n->runtime.add[0]);
+ cryptomatte_add(*scene, *node, *n, n->runtime.add[0]);
zero_v3(n->runtime.add);
}
}
@@ -170,14 +185,14 @@ void ntreeCompositCryptomatteSyncFromRemove(bNode *node)
zero_v3(n->runtime.remove);
}
}
-void ntreeCompositCryptomatteUpdateLayerNames(bNode *node)
+void ntreeCompositCryptomatteUpdateLayerNames(const Scene *scene, bNode *node)
{
BLI_assert(node->type == CMP_NODE_CRYPTOMATTE);
NodeCryptomatte *n = static_cast<NodeCryptomatte *>(node->storage);
BLI_freelistN(&n->runtime.layers);
blender::bke::cryptomatte::CryptomatteSessionPtr session = cryptomatte_init_from_node(
- *node, 0, false);
+ *scene, *node, false);
if (session) {
for (blender::StringRef layer_name :
@@ -190,12 +205,15 @@ void ntreeCompositCryptomatteUpdateLayerNames(bNode *node)
}
}
-void ntreeCompositCryptomatteLayerPrefix(const bNode *node, char *r_prefix, size_t prefix_len)
+void ntreeCompositCryptomatteLayerPrefix(const Scene *scene,
+ const bNode *node,
+ char *r_prefix,
+ size_t prefix_len)
{
BLI_assert(node->type == CMP_NODE_CRYPTOMATTE);
NodeCryptomatte *node_cryptomatte = (NodeCryptomatte *)node->storage;
blender::bke::cryptomatte::CryptomatteSessionPtr session = cryptomatte_init_from_node(
- *node, 0, false);
+ *scene, *node, false);
std::string first_layer_name;
if (session) {
@@ -216,10 +234,10 @@ void ntreeCompositCryptomatteLayerPrefix(const bNode *node, char *r_prefix, size
BLI_strncpy(r_prefix, cstr, prefix_len);
}
-CryptomatteSession *ntreeCompositCryptomatteSession(bNode *node)
+CryptomatteSession *ntreeCompositCryptomatteSession(const Scene *scene, bNode *node)
{
blender::bke::cryptomatte::CryptomatteSessionPtr session_ptr = cryptomatte_init_from_node(
- *node, 0, true);
+ *scene, *node, true);
return session_ptr.release();
}
@@ -263,7 +281,9 @@ static void node_copy_cryptomatte(bNodeTree *UNUSED(dest_ntree),
dest_node->storage = dest_nc;
}
-static bool node_poll_cryptomatte(bNodeType *UNUSED(ntype), bNodeTree *ntree)
+static bool node_poll_cryptomatte(bNodeType *UNUSED(ntype),
+ bNodeTree *ntree,
+ const char **r_disabled_hint)
{
if (STREQ(ntree->idname, "CompositorNodeTree")) {
Scene *scene;
@@ -276,8 +296,13 @@ static bool node_poll_cryptomatte(bNodeType *UNUSED(ntype), bNodeTree *ntree)
}
}
+ if (scene == nullptr) {
+ *r_disabled_hint =
+ "The node tree must be the compositing node tree of any scene in the file";
+ }
return scene != nullptr;
}
+ *r_disabled_hint = "Not a compositor node tree";
return false;
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_image.c b/source/blender/nodes/composite/nodes/node_composite_image.c
index 53ea02ff8a7..243300b0a44 100644
--- a/source/blender/nodes/composite/nodes/node_composite_image.c
+++ b/source/blender/nodes/composite/nodes/node_composite_image.c
@@ -526,24 +526,32 @@ static void node_composit_init_rlayers(const bContext *C, PointerRNA *ptr)
}
}
-static bool node_composit_poll_rlayers(bNodeType *UNUSED(ntype), bNodeTree *ntree)
+static bool node_composit_poll_rlayers(bNodeType *UNUSED(ntype),
+ bNodeTree *ntree,
+ const char **r_disabled_hint)
{
- if (STREQ(ntree->idname, "CompositorNodeTree")) {
- Scene *scene;
+ if (!STREQ(ntree->idname, "CompositorNodeTree")) {
+ *r_disabled_hint = "Not a compositor node tree";
+ return false;
+ }
- /* XXX ugly: check if ntree is a local scene node tree.
- * Render layers node can only be used in local scene->nodetree,
- * since it directly links to the scene.
- */
- for (scene = G.main->scenes.first; scene; scene = scene->id.next) {
- if (scene->nodetree == ntree) {
- break;
- }
+ Scene *scene;
+
+ /* XXX ugly: check if ntree is a local scene node tree.
+ * Render layers node can only be used in local scene->nodetree,
+ * since it directly links to the scene.
+ */
+ for (scene = G.main->scenes.first; scene; scene = scene->id.next) {
+ if (scene->nodetree == ntree) {
+ break;
}
+ }
- return (scene != NULL);
+ if (scene == NULL) {
+ *r_disabled_hint = "The node tree must be the compositing node tree of any scene in the file";
+ return false;
}
- return false;
+ return true;
}
static void node_composit_free_rlayers(bNode *node)
diff --git a/source/blender/nodes/function/node_function_util.cc b/source/blender/nodes/function/node_function_util.cc
index 827572d1069..8ff8b416310 100644
--- a/source/blender/nodes/function/node_function_util.cc
+++ b/source/blender/nodes/function/node_function_util.cc
@@ -17,10 +17,16 @@
#include "node_function_util.hh"
#include "node_util.h"
-bool fn_node_poll_default(bNodeType *UNUSED(ntype), bNodeTree *ntree)
+static bool fn_node_poll_default(bNodeType *UNUSED(ntype),
+ bNodeTree *ntree,
+ const char **r_disabled_hint)
{
/* Function nodes are only supported in simulation node trees so far. */
- return STREQ(ntree->idname, "GeometryNodeTree");
+ if (!STREQ(ntree->idname, "GeometryNodeTree")) {
+ *r_disabled_hint = "Not a geometry node tree";
+ return false;
+ }
+ return true;
}
void fn_node_type_base(bNodeType *ntype, int type, const char *name, short nclass, short flag)
diff --git a/source/blender/nodes/function/node_function_util.hh b/source/blender/nodes/function/node_function_util.hh
index d57d1383019..9fbd6712827 100644
--- a/source/blender/nodes/function/node_function_util.hh
+++ b/source/blender/nodes/function/node_function_util.hh
@@ -38,4 +38,3 @@
void fn_node_type_base(
struct bNodeType *ntype, int type, const char *name, short nclass, short flag);
-bool fn_node_poll_default(struct bNodeType *ntype, struct bNodeTree *ntree);
diff --git a/source/blender/nodes/geometry/node_geometry_tree.cc b/source/blender/nodes/geometry/node_geometry_tree.cc
index 2c4c7da64cc..f4cd00b88ed 100644
--- a/source/blender/nodes/geometry/node_geometry_tree.cc
+++ b/source/blender/nodes/geometry/node_geometry_tree.cc
@@ -67,6 +67,8 @@ static void geometry_node_tree_get_from_context(const bContext *C,
static void geometry_node_tree_update(bNodeTree *ntree)
{
+ ntreeSetOutput(ntree);
+
/* Needed to give correct types to reroutes. */
ntree_update_reroute_nodes(ntree);
}
@@ -82,6 +84,34 @@ static void foreach_nodeclass(Scene *UNUSED(scene), void *calldata, bNodeClassCa
func(calldata, NODE_CLASS_LAYOUT, N_("Layout"));
}
+static bool geometry_node_tree_validate_link(bNodeTree *UNUSED(ntree), bNodeLink *link)
+{
+ /* Geometry, string, object, material, texture and collection sockets can only be connected to
+ * themselves. The other types can be converted between each other. */
+ if (ELEM(link->fromsock->type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_BOOLEAN, SOCK_INT) &&
+ ELEM(link->tosock->type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_BOOLEAN, SOCK_INT)) {
+ return true;
+ }
+ return (link->tosock->type == link->fromsock->type);
+}
+
+static bool geometry_node_tree_socket_type_valid(eNodeSocketDatatype socket_type,
+ bNodeTreeType *UNUSED(ntreetype))
+{
+ return ELEM(socket_type,
+ SOCK_FLOAT,
+ SOCK_VECTOR,
+ SOCK_RGBA,
+ SOCK_BOOLEAN,
+ SOCK_INT,
+ SOCK_STRING,
+ SOCK_OBJECT,
+ SOCK_GEOMETRY,
+ SOCK_COLLECTION,
+ SOCK_TEXTURE,
+ SOCK_MATERIAL);
+}
+
void register_node_tree_type_geo(void)
{
bNodeTreeType *tt = ntreeType_Geometry = static_cast<bNodeTreeType *>(
@@ -95,6 +125,8 @@ void register_node_tree_type_geo(void)
tt->update = geometry_node_tree_update;
tt->get_from_context = geometry_node_tree_get_from_context;
tt->foreach_nodeclass = foreach_nodeclass;
+ tt->valid_socket_type = geometry_node_tree_socket_type_valid;
+ tt->validate_link = geometry_node_tree_validate_link;
ntreeTypeAdd(tt);
}
diff --git a/source/blender/nodes/geometry/node_geometry_util.cc b/source/blender/nodes/geometry/node_geometry_util.cc
index 0f725ecf211..93cada2982b 100644
--- a/source/blender/nodes/geometry/node_geometry_util.cc
+++ b/source/blender/nodes/geometry/node_geometry_util.cc
@@ -56,9 +56,15 @@ void update_attribute_input_socket_availabilities(bNode &node,
} // namespace blender::nodes
-bool geo_node_poll_default(bNodeType *UNUSED(ntype), bNodeTree *ntree)
+bool geo_node_poll_default(bNodeType *UNUSED(ntype),
+ bNodeTree *ntree,
+ const char **r_disabled_hint)
{
- return STREQ(ntree->idname, "GeometryNodeTree");
+ if (!STREQ(ntree->idname, "GeometryNodeTree")) {
+ *r_disabled_hint = "Not a geometry node tree";
+ return false;
+ }
+ return true;
}
void geo_node_type_base(bNodeType *ntype, int type, const char *name, short nclass, short flag)
diff --git a/source/blender/nodes/geometry/node_geometry_util.hh b/source/blender/nodes/geometry/node_geometry_util.hh
index fb80bd08797..79a98c5ebf0 100644
--- a/source/blender/nodes/geometry/node_geometry_util.hh
+++ b/source/blender/nodes/geometry/node_geometry_util.hh
@@ -36,7 +36,9 @@
void geo_node_type_base(
struct bNodeType *ntype, int type, const char *name, short nclass, short flag);
-bool geo_node_poll_default(struct bNodeType *ntype, struct bNodeTree *ntree);
+bool geo_node_poll_default(struct bNodeType *ntype,
+ struct bNodeTree *ntree,
+ const char **r_disabled_hint);
namespace blender::nodes {
void update_attribute_input_socket_availabilities(bNode &node,
@@ -58,4 +60,14 @@ Mesh *create_cylinder_or_cone_mesh(const float radius_top,
const int verts_num,
const GeometryNodeMeshCircleFillType fill_type);
+Mesh *create_cube_mesh(const float size);
+
+/**
+ * Copies the point domain attributes from `in_component` that are in the mask to `out_component`.
+ */
+void copy_point_attributes_based_on_mask(const GeometryComponent &in_component,
+ GeometryComponent &result_component,
+ Span<bool> masks,
+ const bool invert);
+
} // namespace blender::nodes
diff --git a/source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc b/source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc
index fca460e3566..d1b71d6f2ba 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc
@@ -14,13 +14,14 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "node_geometry_util.hh"
-
#include "BLI_math_rotation.h"
+#include "BLI_task.hh"
#include "UI_interface.h"
#include "UI_resources.h"
+#include "node_geometry_util.hh"
+
static bNodeSocketTemplate geo_node_align_rotation_to_vector_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_STRING, N_("Factor")},
@@ -50,91 +51,117 @@ static void geo_node_align_rotation_to_vector_layout(uiLayout *layout,
namespace blender::nodes {
-static void align_rotations_auto_pivot(const Float3ReadAttribute &vectors,
- const FloatReadAttribute &factors,
+static void geo_node_align_rotation_to_vector_init(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeGeometryAlignRotationToVector *node_storage = (NodeGeometryAlignRotationToVector *)
+ MEM_callocN(sizeof(NodeGeometryAlignRotationToVector), __func__);
+
+ node_storage->axis = GEO_NODE_ALIGN_ROTATION_TO_VECTOR_AXIS_X;
+ node_storage->input_type_factor = GEO_NODE_ATTRIBUTE_INPUT_FLOAT;
+ node_storage->input_type_vector = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
+
+ node->storage = node_storage;
+}
+
+static void geo_node_align_rotation_to_vector_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeGeometryAlignRotationToVector *node_storage = (NodeGeometryAlignRotationToVector *)
+ node->storage;
+ update_attribute_input_socket_availabilities(
+ *node, "Factor", (GeometryNodeAttributeInputMode)node_storage->input_type_factor);
+ update_attribute_input_socket_availabilities(
+ *node, "Vector", (GeometryNodeAttributeInputMode)node_storage->input_type_vector);
+}
+
+static void align_rotations_auto_pivot(const VArray<float3> &vectors,
+ const VArray<float> &factors,
const float3 local_main_axis,
- MutableSpan<float3> rotations)
+ const MutableSpan<float3> rotations)
{
- for (const int i : IndexRange(vectors.size())) {
- const float3 vector = vectors[i];
- if (is_zero_v3(vector)) {
- continue;
- }
+ parallel_for(IndexRange(vectors.size()), 128, [&](IndexRange range) {
+ for (const int i : range) {
+ const float3 vector = vectors[i];
+ if (is_zero_v3(vector)) {
+ continue;
+ }
- float old_rotation[3][3];
- eul_to_mat3(old_rotation, rotations[i]);
- float3 old_axis;
- mul_v3_m3v3(old_axis, old_rotation, local_main_axis);
+ float old_rotation[3][3];
+ eul_to_mat3(old_rotation, rotations[i]);
+ float3 old_axis;
+ mul_v3_m3v3(old_axis, old_rotation, local_main_axis);
- const float3 new_axis = vector.normalized();
- float3 rotation_axis = float3::cross_high_precision(old_axis, new_axis);
- if (is_zero_v3(rotation_axis)) {
- /* The vectors are linearly dependent, so we fall back to another axis. */
- rotation_axis = float3::cross_high_precision(old_axis, float3(1, 0, 0));
+ const float3 new_axis = vector.normalized();
+ float3 rotation_axis = float3::cross_high_precision(old_axis, new_axis);
if (is_zero_v3(rotation_axis)) {
- /* This is now guaranteed to not be zero. */
- rotation_axis = float3::cross_high_precision(old_axis, float3(0, 1, 0));
+ /* The vectors are linearly dependent, so we fall back to another axis. */
+ rotation_axis = float3::cross_high_precision(old_axis, float3(1, 0, 0));
+ if (is_zero_v3(rotation_axis)) {
+ /* This is now guaranteed to not be zero. */
+ rotation_axis = float3::cross_high_precision(old_axis, float3(0, 1, 0));
+ }
}
- }
- const float full_angle = angle_normalized_v3v3(old_axis, new_axis);
- const float angle = factors[i] * full_angle;
+ const float full_angle = angle_normalized_v3v3(old_axis, new_axis);
+ const float angle = factors[i] * full_angle;
- float rotation[3][3];
- axis_angle_to_mat3(rotation, rotation_axis, angle);
+ float rotation[3][3];
+ axis_angle_to_mat3(rotation, rotation_axis, angle);
- float new_rotation_matrix[3][3];
- mul_m3_m3m3(new_rotation_matrix, rotation, old_rotation);
+ float new_rotation_matrix[3][3];
+ mul_m3_m3m3(new_rotation_matrix, rotation, old_rotation);
- float3 new_rotation;
- mat3_to_eul(new_rotation, new_rotation_matrix);
+ float3 new_rotation;
+ mat3_to_eul(new_rotation, new_rotation_matrix);
- rotations[i] = new_rotation;
- }
+ rotations[i] = new_rotation;
+ }
+ });
}
-static void align_rotations_fixed_pivot(const Float3ReadAttribute &vectors,
- const FloatReadAttribute &factors,
+static void align_rotations_fixed_pivot(const VArray<float3> &vectors,
+ const VArray<float> &factors,
const float3 local_main_axis,
const float3 local_pivot_axis,
- MutableSpan<float3> rotations)
+ const MutableSpan<float3> rotations)
{
if (local_main_axis == local_pivot_axis) {
/* Can't compute any meaningful rotation angle in this case. */
return;
}
- for (const int i : IndexRange(vectors.size())) {
- const float3 vector = vectors[i];
- if (is_zero_v3(vector)) {
- continue;
- }
+ parallel_for(IndexRange(vectors.size()), 128, [&](IndexRange range) {
+ for (const int i : range) {
+ const float3 vector = vectors[i];
+ if (is_zero_v3(vector)) {
+ continue;
+ }
- float old_rotation[3][3];
- eul_to_mat3(old_rotation, rotations[i]);
- float3 old_axis;
- mul_v3_m3v3(old_axis, old_rotation, local_main_axis);
- float3 pivot_axis;
- mul_v3_m3v3(pivot_axis, old_rotation, local_pivot_axis);
-
- float full_angle = angle_signed_on_axis_v3v3_v3(vector, old_axis, pivot_axis);
- if (full_angle > M_PI) {
- /* Make sure the point is rotated as little as possible. */
- full_angle -= 2.0f * M_PI;
- }
- const float angle = factors[i] * full_angle;
+ float old_rotation[3][3];
+ eul_to_mat3(old_rotation, rotations[i]);
+ float3 old_axis;
+ mul_v3_m3v3(old_axis, old_rotation, local_main_axis);
+ float3 pivot_axis;
+ mul_v3_m3v3(pivot_axis, old_rotation, local_pivot_axis);
+
+ float full_angle = angle_signed_on_axis_v3v3_v3(vector, old_axis, pivot_axis);
+ if (full_angle > M_PI) {
+ /* Make sure the point is rotated as little as possible. */
+ full_angle -= 2.0f * M_PI;
+ }
+ const float angle = factors[i] * full_angle;
- float rotation[3][3];
- axis_angle_to_mat3(rotation, pivot_axis, angle);
+ float rotation[3][3];
+ axis_angle_to_mat3(rotation, pivot_axis, angle);
- float new_rotation_matrix[3][3];
- mul_m3_m3m3(new_rotation_matrix, rotation, old_rotation);
+ float new_rotation_matrix[3][3];
+ mul_m3_m3m3(new_rotation_matrix, rotation, old_rotation);
- float3 new_rotation;
- mat3_to_eul(new_rotation, new_rotation_matrix);
+ float3 new_rotation;
+ mat3_to_eul(new_rotation, new_rotation_matrix);
- rotations[i] = new_rotation;
- }
+ rotations[i] = new_rotation;
+ }
+ });
}
static void align_rotations_on_component(GeometryComponent &component,
@@ -144,30 +171,30 @@ static void align_rotations_on_component(GeometryComponent &component,
const NodeGeometryAlignRotationToVector &storage = *(const NodeGeometryAlignRotationToVector *)
node.storage;
- OutputAttributePtr rotation_attribute = component.attribute_try_get_for_output(
- "rotation", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3);
- if (!rotation_attribute) {
+ OutputAttribute_Typed<float3> rotations = component.attribute_try_get_for_output<float3>(
+ "rotation", ATTR_DOMAIN_POINT, {0, 0, 0});
+ if (!rotations) {
return;
}
- MutableSpan<float3> rotations = rotation_attribute->get_span<float3>();
- FloatReadAttribute factors = params.get_input_attribute<float>(
+ GVArray_Typed<float> factors = params.get_input_attribute<float>(
"Factor", component, ATTR_DOMAIN_POINT, 1.0f);
- Float3ReadAttribute vectors = params.get_input_attribute<float3>(
+ GVArray_Typed<float3> vectors = params.get_input_attribute<float3>(
"Vector", component, ATTR_DOMAIN_POINT, {0, 0, 1});
float3 local_main_axis{0, 0, 0};
local_main_axis[storage.axis] = 1;
if (storage.pivot_axis == GEO_NODE_ALIGN_ROTATION_TO_VECTOR_PIVOT_AXIS_AUTO) {
- align_rotations_auto_pivot(vectors, factors, local_main_axis, rotations);
+ align_rotations_auto_pivot(vectors, factors, local_main_axis, rotations.as_span());
}
else {
float3 local_pivot_axis{0, 0, 0};
local_pivot_axis[storage.pivot_axis - 1] = 1;
- align_rotations_fixed_pivot(vectors, factors, local_main_axis, local_pivot_axis, rotations);
+ align_rotations_fixed_pivot(
+ vectors, factors, local_main_axis, local_pivot_axis, rotations.as_span());
}
- rotation_attribute.apply_span_and_save();
+ rotations.save();
}
static void geo_node_align_rotation_to_vector_exec(GeoNodeExecParams params)
@@ -183,32 +210,13 @@ static void geo_node_align_rotation_to_vector_exec(GeoNodeExecParams params)
align_rotations_on_component(geometry_set.get_component_for_write<PointCloudComponent>(),
params);
}
+ if (geometry_set.has<CurveComponent>()) {
+ align_rotations_on_component(geometry_set.get_component_for_write<CurveComponent>(), params);
+ }
params.set_output("Geometry", geometry_set);
}
-static void geo_node_align_rotation_to_vector_init(bNodeTree *UNUSED(ntree), bNode *node)
-{
- NodeGeometryAlignRotationToVector *node_storage = (NodeGeometryAlignRotationToVector *)
- MEM_callocN(sizeof(NodeGeometryAlignRotationToVector), __func__);
-
- node_storage->axis = GEO_NODE_ALIGN_ROTATION_TO_VECTOR_AXIS_X;
- node_storage->input_type_factor = GEO_NODE_ATTRIBUTE_INPUT_FLOAT;
- node_storage->input_type_vector = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
-
- node->storage = node_storage;
-}
-
-static void geo_node_align_rotation_to_vector_update(bNodeTree *UNUSED(ntree), bNode *node)
-{
- NodeGeometryAlignRotationToVector *node_storage = (NodeGeometryAlignRotationToVector *)
- node->storage;
- update_attribute_input_socket_availabilities(
- *node, "Factor", (GeometryNodeAttributeInputMode)node_storage->input_type_factor);
- update_attribute_input_socket_availabilities(
- *node, "Vector", (GeometryNodeAttributeInputMode)node_storage->input_type_vector);
-}
-
} // namespace blender::nodes
void register_node_type_geo_align_rotation_to_vector()
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc
new file mode 100644
index 00000000000..71643df1cb6
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc
@@ -0,0 +1,284 @@
+
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_attribute_clamp_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_STRING, N_("Attribute")},
+ {SOCK_STRING, N_("Result")},
+ {SOCK_VECTOR, N_("Min"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_VECTOR, N_("Max"), 1.0f, 1.0f, 1.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_FLOAT, N_("Min"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_FLOAT, N_("Max"), 1.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_INT, N_("Min"), 0.0f, 0.0f, 0.0f, 0.0f, -100000, 100000},
+ {SOCK_INT, N_("Max"), 100.0f, 0.0f, 0.0f, 0.0f, -100000, 100000},
+ {SOCK_RGBA, N_("Min"), 0.5, 0.5, 0.5, 1.0},
+ {SOCK_RGBA, N_("Max"), 0.5, 0.5, 0.5, 1.0},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_attribute_clamp_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+static void geo_node_attribute_clamp_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE);
+ uiItemR(layout, ptr, "operation", 0, "", ICON_NONE);
+}
+
+static void geo_node_attribute_clamp_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeAttributeClamp *data = (NodeAttributeClamp *)MEM_callocN(sizeof(NodeAttributeClamp),
+ __func__);
+ data->data_type = CD_PROP_FLOAT;
+ data->operation = NODE_CLAMP_MINMAX;
+ node->storage = data;
+}
+
+static void geo_node_attribute_clamp_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ bNodeSocket *sock_min_vector = (bNodeSocket *)BLI_findlink(&node->inputs, 3);
+ bNodeSocket *sock_max_vector = sock_min_vector->next;
+ bNodeSocket *sock_min_float = sock_max_vector->next;
+ bNodeSocket *sock_max_float = sock_min_float->next;
+ bNodeSocket *sock_min_int = sock_max_float->next;
+ bNodeSocket *sock_max_int = sock_min_int->next;
+ bNodeSocket *sock_min_color = sock_max_int->next;
+ bNodeSocket *sock_max_color = sock_min_color->next;
+
+ const NodeAttributeClamp &storage = *(const NodeAttributeClamp *)node->storage;
+ const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type);
+ nodeSetSocketAvailability(sock_min_vector, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(sock_max_vector, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(sock_min_float, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(sock_max_float, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(sock_min_int, data_type == CD_PROP_INT32);
+ nodeSetSocketAvailability(sock_max_int, data_type == CD_PROP_INT32);
+ nodeSetSocketAvailability(sock_min_color, data_type == CD_PROP_COLOR);
+ nodeSetSocketAvailability(sock_max_color, data_type == CD_PROP_COLOR);
+}
+
+namespace blender::nodes {
+
+template<typename T> T clamp_value(const T val, const T min, const T max);
+
+template<> inline float clamp_value(const float val, const float min, const float max)
+{
+ return std::min(std::max(val, min), max);
+}
+
+template<> inline int clamp_value(const int val, const int min, const int max)
+{
+ return std::min(std::max(val, min), max);
+}
+
+template<> inline float3 clamp_value(const float3 val, const float3 min, const float3 max)
+{
+ float3 tmp;
+ tmp.x = std::min(std::max(val.x, min.x), max.x);
+ tmp.y = std::min(std::max(val.y, min.y), max.y);
+ tmp.z = std::min(std::max(val.z, min.z), max.z);
+ return tmp;
+}
+
+template<>
+inline ColorGeometry4f clamp_value(const ColorGeometry4f val,
+ const ColorGeometry4f min,
+ const ColorGeometry4f max)
+{
+ ColorGeometry4f tmp;
+ tmp.r = std::min(std::max(val.r, min.r), max.r);
+ tmp.g = std::min(std::max(val.g, min.g), max.g);
+ tmp.b = std::min(std::max(val.b, min.b), max.b);
+ tmp.a = std::min(std::max(val.a, min.a), max.a);
+ return tmp;
+}
+
+template<typename T>
+static void clamp_attribute(const VArray<T> &inputs,
+ const MutableSpan<T> outputs,
+ const T min,
+ const T max)
+{
+ for (const int i : IndexRange(outputs.size())) {
+ outputs[i] = clamp_value<T>(inputs[i], min, max);
+ }
+}
+
+static AttributeDomain get_result_domain(const GeometryComponent &component,
+ StringRef source_name,
+ StringRef result_name)
+{
+ std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name);
+ if (result_info) {
+ return result_info->domain;
+ }
+ std::optional<AttributeMetaData> source_info = component.attribute_get_meta_data(source_name);
+ if (source_info) {
+ return source_info->domain;
+ }
+ return ATTR_DOMAIN_POINT;
+}
+
+static void clamp_attribute(GeometryComponent &component, const GeoNodeExecParams &params)
+{
+ const std::string attribute_name = params.get_input<std::string>("Attribute");
+ const std::string result_name = params.get_input<std::string>("Result");
+
+ if (attribute_name.empty() || result_name.empty()) {
+ return;
+ }
+
+ if (!component.attribute_exists(attribute_name)) {
+ params.error_message_add(NodeWarningType::Error,
+ TIP_("No attribute with name \"") + attribute_name + "\"");
+ return;
+ }
+
+ const NodeAttributeClamp &storage = *(const NodeAttributeClamp *)params.node().storage;
+ const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type);
+ const AttributeDomain domain = get_result_domain(component, attribute_name, result_name);
+ const int operation = static_cast<int>(storage.operation);
+
+ GVArrayPtr attribute_input = component.attribute_try_get_for_read(
+ attribute_name, domain, data_type);
+
+ OutputAttribute attribute_result = component.attribute_try_get_for_output_only(
+ result_name, domain, data_type);
+
+ if (!attribute_result) {
+ params.error_message_add(NodeWarningType::Error,
+ TIP_("Could not find or create attribute with name \"") +
+ result_name + "\"");
+ return;
+ }
+
+ switch (data_type) {
+ case CD_PROP_FLOAT3: {
+ float3 min = params.get_input<float3>("Min");
+ float3 max = params.get_input<float3>("Max");
+ if (operation == NODE_CLAMP_RANGE) {
+ if (min.x > max.x) {
+ std::swap(min.x, max.x);
+ }
+ if (min.y > max.y) {
+ std::swap(min.y, max.y);
+ }
+ if (min.z > max.z) {
+ std::swap(min.z, max.z);
+ }
+ }
+ MutableSpan<float3> results = attribute_result.as_span<float3>();
+ clamp_attribute<float3>(attribute_input->typed<float3>(), results, min, max);
+ break;
+ }
+ case CD_PROP_FLOAT: {
+ const float min = params.get_input<float>("Min_001");
+ const float max = params.get_input<float>("Max_001");
+ MutableSpan<float> results = attribute_result.as_span<float>();
+ if (operation == NODE_CLAMP_RANGE && min > max) {
+ clamp_attribute<float>(attribute_input->typed<float>(), results, max, min);
+ }
+ else {
+ clamp_attribute<float>(attribute_input->typed<float>(), results, min, max);
+ }
+ break;
+ }
+ case CD_PROP_INT32: {
+ const int min = params.get_input<int>("Min_002");
+ const int max = params.get_input<int>("Max_002");
+ MutableSpan<int> results = attribute_result.as_span<int>();
+ if (operation == NODE_CLAMP_RANGE && min > max) {
+ clamp_attribute<int>(attribute_input->typed<int>(), results, max, min);
+ }
+ else {
+ clamp_attribute<int>(attribute_input->typed<int>(), results, min, max);
+ }
+ break;
+ }
+ case CD_PROP_COLOR: {
+ ColorGeometry4f min = params.get_input<ColorGeometry4f>("Min_003");
+ ColorGeometry4f max = params.get_input<ColorGeometry4f>("Max_003");
+ if (operation == NODE_CLAMP_RANGE) {
+ if (min.r > max.r) {
+ std::swap(min.r, max.r);
+ }
+ if (min.g > max.g) {
+ std::swap(min.g, max.g);
+ }
+ if (min.b > max.b) {
+ std::swap(min.b, max.b);
+ }
+ if (min.a > max.a) {
+ std::swap(min.a, max.a);
+ }
+ }
+ MutableSpan<ColorGeometry4f> results = attribute_result.as_span<ColorGeometry4f>();
+ clamp_attribute<ColorGeometry4f>(
+ attribute_input->typed<ColorGeometry4f>(), results, min, max);
+ break;
+ }
+ default: {
+ BLI_assert(false);
+ break;
+ }
+ }
+
+ attribute_result.save();
+}
+
+static void geo_node_attribute_clamp_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+ geometry_set = geometry_set_realize_instances(geometry_set);
+
+ if (geometry_set.has<MeshComponent>()) {
+ clamp_attribute(geometry_set.get_component_for_write<MeshComponent>(), params);
+ }
+ if (geometry_set.has<PointCloudComponent>()) {
+ clamp_attribute(geometry_set.get_component_for_write<PointCloudComponent>(), params);
+ }
+ if (geometry_set.has<CurveComponent>()) {
+ clamp_attribute(geometry_set.get_component_for_write<CurveComponent>(), params);
+ }
+
+ params.set_output("Geometry", geometry_set);
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_attribute_clamp()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_ATTRIBUTE_CLAMP, "Attribute Clamp", NODE_CLASS_ATTRIBUTE, 0);
+ node_type_socket_templates(&ntype, geo_node_attribute_clamp_in, geo_node_attribute_clamp_out);
+ node_type_init(&ntype, geo_node_attribute_clamp_init);
+ node_type_update(&ntype, geo_node_attribute_clamp_update);
+ ntype.geometry_node_execute = blender::nodes::geo_node_attribute_clamp_exec;
+ ntype.draw_buttons = geo_node_attribute_clamp_layout;
+ node_type_storage(
+ &ntype, "NodeAttributeClamp", node_free_standard_storage, node_copy_standard_storage);
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc
index 98bf612f589..5293dd8c876 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc
@@ -14,13 +14,15 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "node_geometry_util.hh"
+#include "BLI_task.hh"
#include "BKE_colorband.h"
#include "UI_interface.h"
#include "UI_resources.h"
+#include "node_geometry_util.hh"
+
static bNodeSocketTemplate geo_node_attribute_color_ramp_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_STRING, N_("Attribute")},
@@ -42,20 +44,28 @@ static void geo_node_attribute_color_ramp_layout(uiLayout *layout,
namespace blender::nodes {
+static void geo_node_attribute_color_ramp_init(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeAttributeColorRamp *node_storage = (NodeAttributeColorRamp *)MEM_callocN(
+ sizeof(NodeAttributeColorRamp), __func__);
+ BKE_colorband_init(&node_storage->color_ramp, true);
+ node->storage = node_storage;
+}
+
static AttributeDomain get_result_domain(const GeometryComponent &component,
StringRef input_name,
StringRef result_name)
{
/* Use the domain of the result attribute if it already exists. */
- ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name);
- if (result_attribute) {
- return result_attribute->domain();
+ std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name);
+ if (result_info) {
+ return result_info->domain;
}
/* Otherwise use the input attribute's domain if it exists. */
- ReadAttributePtr input_attribute = component.attribute_try_get_for_read(input_name);
- if (input_attribute) {
- return input_attribute->domain();
+ std::optional<AttributeMetaData> source_info = component.attribute_get_meta_data(input_name);
+ if (source_info) {
+ return source_info->domain;
}
return ATTR_DOMAIN_POINT;
@@ -71,27 +81,27 @@ static void execute_on_component(const GeoNodeExecParams &params, GeometryCompon
/* Always output a color attribute for now. We might want to allow users to customize.
* Using the type of an existing attribute could work, but does not have a real benefit
* currently. */
- const CustomDataType result_type = CD_PROP_COLOR;
const AttributeDomain result_domain = get_result_domain(component, input_name, result_name);
- OutputAttributePtr attribute_result = component.attribute_try_get_for_output(
- result_name, result_domain, result_type);
+ OutputAttribute_Typed<ColorGeometry4f> attribute_result =
+ component.attribute_try_get_for_output_only<ColorGeometry4f>(result_name, result_domain);
if (!attribute_result) {
return;
}
- FloatReadAttribute attribute_in = component.attribute_get_for_read<float>(
+ GVArray_Typed<float> attribute_in = component.attribute_get_for_read<float>(
input_name, result_domain, 0.0f);
- Span<float> data_in = attribute_in.get_span();
- MutableSpan<Color4f> data_out = attribute_result->get_span_for_write_only<Color4f>();
+ MutableSpan<ColorGeometry4f> results = attribute_result.as_span();
ColorBand *color_ramp = &node_storage->color_ramp;
- for (const int i : data_in.index_range()) {
- BKE_colorband_evaluate(color_ramp, data_in[i], data_out[i]);
- }
+ parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ BKE_colorband_evaluate(color_ramp, attribute_in[i], results[i]);
+ }
+ });
- attribute_result.apply_span_and_save();
+ attribute_result.save();
}
static void geo_node_attribute_color_ramp_exec(GeoNodeExecParams params)
@@ -106,18 +116,13 @@ static void geo_node_attribute_color_ramp_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
execute_on_component(params, geometry_set.get_component_for_write<PointCloudComponent>());
}
+ if (geometry_set.has<CurveComponent>()) {
+ execute_on_component(params, geometry_set.get_component_for_write<CurveComponent>());
+ }
params.set_output("Geometry", std::move(geometry_set));
}
-static void geo_node_attribute_color_ramp_init(bNodeTree *UNUSED(ntree), bNode *node)
-{
- NodeAttributeColorRamp *node_storage = (NodeAttributeColorRamp *)MEM_callocN(
- sizeof(NodeAttributeColorRamp), __func__);
- BKE_colorband_init(&node_storage->color_ramp, true);
- node->storage = node_storage;
-}
-
} // namespace blender::nodes
void register_node_type_geo_attribute_color_ramp()
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_combine_xyz.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_combine_xyz.cc
index 9c5c7e270b1..d8c52d16f41 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_combine_xyz.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_combine_xyz.cc
@@ -14,11 +14,11 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "node_geometry_util.hh"
-
#include "UI_interface.h"
#include "UI_resources.h"
+#include "node_geometry_util.hh"
+
static bNodeSocketTemplate geo_node_attribute_combine_xyz_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_STRING, N_("X")},
@@ -77,9 +77,9 @@ static AttributeDomain get_result_domain(const GeometryComponent &component,
StringRef result_name)
{
/* Use the domain of the result attribute if it already exists. */
- ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name);
- if (result_attribute) {
- return result_attribute->domain();
+ std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name);
+ if (result_info) {
+ return result_info->domain;
}
/* Otherwise use the highest priority domain from existing input attributes, or the default. */
@@ -94,27 +94,25 @@ static void combine_attributes(GeometryComponent &component, const GeoNodeExecPa
}
const AttributeDomain result_domain = get_result_domain(component, params, result_name);
- OutputAttributePtr attribute_result = component.attribute_try_get_for_output(
- result_name, result_domain, CD_PROP_FLOAT3);
+ OutputAttribute_Typed<float3> attribute_result =
+ component.attribute_try_get_for_output_only<float3>(result_name, result_domain);
if (!attribute_result) {
return;
}
- FloatReadAttribute attribute_x = params.get_input_attribute<float>(
+ GVArray_Typed<float> attribute_x = params.get_input_attribute<float>(
"X", component, result_domain, 0.0f);
- FloatReadAttribute attribute_y = params.get_input_attribute<float>(
+ GVArray_Typed<float> attribute_y = params.get_input_attribute<float>(
"Y", component, result_domain, 0.0f);
- FloatReadAttribute attribute_z = params.get_input_attribute<float>(
+ GVArray_Typed<float> attribute_z = params.get_input_attribute<float>(
"Z", component, result_domain, 0.0f);
- MutableSpan<float3> results = attribute_result->get_span_for_write_only<float3>();
- for (const int i : results.index_range()) {
+ for (const int i : IndexRange(attribute_result->size())) {
const float x = attribute_x[i];
const float y = attribute_y[i];
const float z = attribute_z[i];
- const float3 result = float3(x, y, z);
- results[i] = result;
+ attribute_result->set(i, {x, y, z});
}
- attribute_result.apply_span_and_save();
+ attribute_result.save();
}
static void geo_node_attribute_combine_xyz_exec(GeoNodeExecParams params)
@@ -129,6 +127,9 @@ static void geo_node_attribute_combine_xyz_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
combine_attributes(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
+ if (geometry_set.has<CurveComponent>()) {
+ combine_attributes(geometry_set.get_component_for_write<CurveComponent>(), params);
+ }
params.set_output("Geometry", geometry_set);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc
index db1a5d18744..57ac68b4cd4 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc
@@ -14,23 +14,13 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "node_geometry_util.hh"
-
-#include "BKE_attribute.h"
-#include "BKE_attribute_access.hh"
-
-#include "BLI_array.hh"
-#include "BLI_math_base_safe.h"
-#include "BLI_rand.hh"
-
-#include "DNA_mesh_types.h"
-#include "DNA_pointcloud_types.h"
-
#include "UI_interface.h"
#include "UI_resources.h"
#include "NOD_math_functions.hh"
+#include "node_geometry_util.hh"
+
static bNodeSocketTemplate geo_node_attribute_compare_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_STRING, N_("A")},
@@ -91,21 +81,18 @@ static void geo_node_attribute_compare_update(bNodeTree *UNUSED(ntree), bNode *n
nodeSetSocketAvailability(socket_threshold, operation_tests_equality(*node_storage));
}
-static void do_math_operation(const FloatReadAttribute &input_a,
- const FloatReadAttribute &input_b,
+static void do_math_operation(const VArray<float> &input_a,
+ const VArray<float> &input_b,
const FloatCompareOperation operation,
MutableSpan<bool> span_result)
{
const int size = input_a.size();
- Span<float> span_a = input_a.get_span();
- Span<float> span_b = input_b.get_span();
-
if (try_dispatch_float_math_fl_fl_to_bool(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
for (const int i : IndexRange(size)) {
- const float a = span_a[i];
- const float b = span_b[i];
+ const float a = input_a[i];
+ const float b = input_b[i];
const bool out = math_function(a, b);
span_result[i] = out;
}
@@ -117,8 +104,8 @@ static void do_math_operation(const FloatReadAttribute &input_a,
BLI_assert(false);
}
-static void do_equal_operation_float(const FloatReadAttribute &input_a,
- const FloatReadAttribute &input_b,
+static void do_equal_operation_float(const VArray<float> &input_a,
+ const VArray<float> &input_b,
const float threshold,
MutableSpan<bool> span_result)
{
@@ -130,8 +117,8 @@ static void do_equal_operation_float(const FloatReadAttribute &input_a,
}
}
-static void do_equal_operation_float3(const Float3ReadAttribute &input_a,
- const Float3ReadAttribute &input_b,
+static void do_equal_operation_float3(const VArray<float3> &input_a,
+ const VArray<float3> &input_b,
const float threshold,
MutableSpan<bool> span_result)
{
@@ -144,22 +131,22 @@ static void do_equal_operation_float3(const Float3ReadAttribute &input_a,
}
}
-static void do_equal_operation_color4f(const Color4fReadAttribute &input_a,
- const Color4fReadAttribute &input_b,
+static void do_equal_operation_color4f(const VArray<ColorGeometry4f> &input_a,
+ const VArray<ColorGeometry4f> &input_b,
const float threshold,
MutableSpan<bool> span_result)
{
const float threshold_squared = pow2f(threshold);
const int size = input_a.size();
for (const int i : IndexRange(size)) {
- const Color4f a = input_a[i];
- const Color4f b = input_b[i];
+ const ColorGeometry4f a = input_a[i];
+ const ColorGeometry4f b = input_b[i];
span_result[i] = len_squared_v4v4(a, b) < threshold_squared;
}
}
-static void do_equal_operation_bool(const BooleanReadAttribute &input_a,
- const BooleanReadAttribute &input_b,
+static void do_equal_operation_bool(const VArray<bool> &input_a,
+ const VArray<bool> &input_b,
const float UNUSED(threshold),
MutableSpan<bool> span_result)
{
@@ -171,8 +158,8 @@ static void do_equal_operation_bool(const BooleanReadAttribute &input_a,
}
}
-static void do_not_equal_operation_float(const FloatReadAttribute &input_a,
- const FloatReadAttribute &input_b,
+static void do_not_equal_operation_float(const VArray<float> &input_a,
+ const VArray<float> &input_b,
const float threshold,
MutableSpan<bool> span_result)
{
@@ -184,8 +171,8 @@ static void do_not_equal_operation_float(const FloatReadAttribute &input_a,
}
}
-static void do_not_equal_operation_float3(const Float3ReadAttribute &input_a,
- const Float3ReadAttribute &input_b,
+static void do_not_equal_operation_float3(const VArray<float3> &input_a,
+ const VArray<float3> &input_b,
const float threshold,
MutableSpan<bool> span_result)
{
@@ -198,22 +185,22 @@ static void do_not_equal_operation_float3(const Float3ReadAttribute &input_a,
}
}
-static void do_not_equal_operation_color4f(const Color4fReadAttribute &input_a,
- const Color4fReadAttribute &input_b,
+static void do_not_equal_operation_color4f(const VArray<ColorGeometry4f> &input_a,
+ const VArray<ColorGeometry4f> &input_b,
const float threshold,
MutableSpan<bool> span_result)
{
const float threshold_squared = pow2f(threshold);
const int size = input_a.size();
for (const int i : IndexRange(size)) {
- const Color4f a = input_a[i];
- const Color4f b = input_b[i];
+ const ColorGeometry4f a = input_a[i];
+ const ColorGeometry4f b = input_b[i];
span_result[i] = len_squared_v4v4(a, b) >= threshold_squared;
}
}
-static void do_not_equal_operation_bool(const BooleanReadAttribute &input_a,
- const BooleanReadAttribute &input_b,
+static void do_not_equal_operation_bool(const VArray<bool> &input_a,
+ const VArray<bool> &input_b,
const float UNUSED(threshold),
MutableSpan<bool> span_result)
{
@@ -247,9 +234,9 @@ static AttributeDomain get_result_domain(const GeometryComponent &component,
StringRef result_name)
{
/* Use the domain of the result attribute if it already exists. */
- ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name);
- if (result_attribute) {
- return result_attribute->domain();
+ std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name);
+ if (result_info) {
+ return result_info->domain;
}
/* Otherwise use the highest priority domain from existing input attributes, or the default. */
@@ -264,20 +251,19 @@ static void attribute_compare_calc(GeometryComponent &component, const GeoNodeEx
node_storage->operation);
const std::string result_name = params.get_input<std::string>("Result");
- const CustomDataType result_type = CD_PROP_BOOL;
const AttributeDomain result_domain = get_result_domain(component, params, result_name);
- OutputAttributePtr attribute_result = component.attribute_try_get_for_output(
- result_name, result_domain, result_type);
+ OutputAttribute_Typed<bool> attribute_result = component.attribute_try_get_for_output_only<bool>(
+ result_name, result_domain);
if (!attribute_result) {
return;
}
const CustomDataType input_data_type = get_data_type(component, params, *node_storage);
- ReadAttributePtr attribute_a = params.get_input_attribute(
+ GVArrayPtr attribute_a = params.get_input_attribute(
"A", component, result_domain, input_data_type, nullptr);
- ReadAttributePtr attribute_b = params.get_input_attribute(
+ GVArrayPtr attribute_b = params.get_input_attribute(
"B", component, result_domain, input_data_type, nullptr);
if (!attribute_a || !attribute_b) {
@@ -285,7 +271,7 @@ static void attribute_compare_calc(GeometryComponent &component, const GeoNodeEx
return;
}
- MutableSpan<bool> result_span = attribute_result->get_span_for_write_only<bool>();
+ MutableSpan<bool> result_span = attribute_result.as_span();
/* Use specific types for correct equality operations, but for other operations we use implicit
* conversions and float comparison. In other words, the comparison is not element-wise. */
@@ -293,38 +279,51 @@ static void attribute_compare_calc(GeometryComponent &component, const GeoNodeEx
const float threshold = params.get_input<float>("Threshold");
if (operation == NODE_FLOAT_COMPARE_EQUAL) {
if (input_data_type == CD_PROP_FLOAT) {
- do_equal_operation_float(*attribute_a, *attribute_b, threshold, result_span);
+ do_equal_operation_float(
+ attribute_a->typed<float>(), attribute_b->typed<float>(), threshold, result_span);
}
else if (input_data_type == CD_PROP_FLOAT3) {
- do_equal_operation_float3(*attribute_a, *attribute_b, threshold, result_span);
+ do_equal_operation_float3(
+ attribute_a->typed<float3>(), attribute_b->typed<float3>(), threshold, result_span);
}
else if (input_data_type == CD_PROP_COLOR) {
- do_equal_operation_color4f(*attribute_a, *attribute_b, threshold, result_span);
+ do_equal_operation_color4f(attribute_a->typed<ColorGeometry4f>(),
+ attribute_b->typed<ColorGeometry4f>(),
+ threshold,
+ result_span);
}
else if (input_data_type == CD_PROP_BOOL) {
- do_equal_operation_bool(*attribute_a, *attribute_b, threshold, result_span);
+ do_equal_operation_bool(
+ attribute_a->typed<bool>(), attribute_b->typed<bool>(), threshold, result_span);
}
}
else if (operation == NODE_FLOAT_COMPARE_NOT_EQUAL) {
if (input_data_type == CD_PROP_FLOAT) {
- do_not_equal_operation_float(*attribute_a, *attribute_b, threshold, result_span);
+ do_not_equal_operation_float(
+ attribute_a->typed<float>(), attribute_b->typed<float>(), threshold, result_span);
}
else if (input_data_type == CD_PROP_FLOAT3) {
- do_not_equal_operation_float3(*attribute_a, *attribute_b, threshold, result_span);
+ do_not_equal_operation_float3(
+ attribute_a->typed<float3>(), attribute_b->typed<float3>(), threshold, result_span);
}
else if (input_data_type == CD_PROP_COLOR) {
- do_not_equal_operation_color4f(*attribute_a, *attribute_b, threshold, result_span);
+ do_not_equal_operation_color4f(attribute_a->typed<ColorGeometry4f>(),
+ attribute_b->typed<ColorGeometry4f>(),
+ threshold,
+ result_span);
}
else if (input_data_type == CD_PROP_BOOL) {
- do_not_equal_operation_bool(*attribute_a, *attribute_b, threshold, result_span);
+ do_not_equal_operation_bool(
+ attribute_a->typed<bool>(), attribute_b->typed<bool>(), threshold, result_span);
}
}
}
else {
- do_math_operation(*attribute_a, *attribute_b, operation, result_span);
+ do_math_operation(
+ attribute_a->typed<float>(), attribute_b->typed<float>(), operation, result_span);
}
- attribute_result.apply_span_and_save();
+ attribute_result.save();
}
static void geo_node_attribute_compare_exec(GeoNodeExecParams params)
@@ -339,6 +338,9 @@ static void geo_node_attribute_compare_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
attribute_compare_calc(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
+ if (geometry_set.has<CurveComponent>()) {
+ attribute_compare_calc(geometry_set.get_component_for_write<CurveComponent>(), params);
+ }
params.set_output("Geometry", geometry_set);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc
index 11d220dd903..7b40456b180 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc
@@ -14,11 +14,11 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "node_geometry_util.hh"
-
#include "UI_interface.h"
#include "UI_resources.h"
+#include "node_geometry_util.hh"
+
static bNodeSocketTemplate geo_node_attribute_convert_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_STRING, N_("Attribute")},
@@ -35,8 +35,10 @@ static void geo_node_attribute_convert_layout(uiLayout *layout,
bContext *UNUSED(C),
PointerRNA *ptr)
{
- uiItemR(layout, ptr, "domain", 0, "", ICON_NONE);
- uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE);
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
+ uiItemR(layout, ptr, "domain", 0, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "data_type", 0, IFACE_("Type"), ICON_NONE);
}
static void geo_node_attribute_convert_init(bNodeTree *UNUSED(tree), bNode *node)
@@ -44,41 +46,69 @@ static void geo_node_attribute_convert_init(bNodeTree *UNUSED(tree), bNode *node
NodeAttributeConvert *data = (NodeAttributeConvert *)MEM_callocN(sizeof(NodeAttributeConvert),
__func__);
- data->data_type = CD_PROP_FLOAT;
+ data->data_type = CD_AUTO_FROM_NAME;
data->domain = ATTR_DOMAIN_AUTO;
node->storage = data;
}
namespace blender::nodes {
-static AttributeDomain get_result_domain(const GeometryComponent &component,
- StringRef source_name,
- StringRef result_name)
+static AttributeMetaData get_result_domain_and_type(const GeometryComponent &component,
+ const StringRef source_name,
+ const StringRef result_name)
+{
+ std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name);
+ if (result_info) {
+ return *result_info;
+ }
+ std::optional<AttributeMetaData> source_info = component.attribute_get_meta_data(source_name);
+ if (source_info) {
+ return *source_info;
+ }
+ /* The node won't do anything in this case, but we still have to return a value. */
+ return AttributeMetaData{ATTR_DOMAIN_POINT, CD_PROP_BOOL};
+}
+
+static bool conversion_can_be_skipped(const GeometryComponent &component,
+ const StringRef source_name,
+ const StringRef result_name,
+ const AttributeDomain result_domain,
+ const CustomDataType result_type)
{
- ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name);
- if (result_attribute) {
- return result_attribute->domain();
+ if (source_name != result_name) {
+ return false;
}
- ReadAttributePtr source_attribute = component.attribute_try_get_for_read(source_name);
- if (source_attribute) {
- return source_attribute->domain();
+ std::optional<AttributeMetaData> info = component.attribute_get_meta_data(result_name);
+ if (!info) {
+ return false;
}
- return ATTR_DOMAIN_POINT;
+ if (info->domain != result_domain) {
+ return false;
+ }
+ if (info->data_type != result_type) {
+ return false;
+ }
+ return true;
}
static void attribute_convert_calc(GeometryComponent &component,
const GeoNodeExecParams &params,
const StringRef source_name,
const StringRef result_name,
- const CustomDataType result_type,
+ const CustomDataType data_type,
const AttributeDomain domain)
{
- const AttributeDomain result_domain = (domain == ATTR_DOMAIN_AUTO) ?
- get_result_domain(
- component, source_name, result_name) :
- domain;
+ const AttributeMetaData auto_info = get_result_domain_and_type(
+ component, source_name, result_name);
+ const AttributeDomain result_domain = (domain == ATTR_DOMAIN_AUTO) ? auto_info.domain : domain;
+ const CustomDataType result_type = (data_type == CD_AUTO_FROM_NAME) ? auto_info.data_type :
+ data_type;
+
+ if (conversion_can_be_skipped(component, source_name, result_name, result_domain, result_type)) {
+ return;
+ }
- ReadAttributePtr source_attribute = component.attribute_try_get_for_read(
+ GVArrayPtr source_attribute = component.attribute_try_get_for_read(
source_name, result_domain, result_type);
if (!source_attribute) {
params.error_message_add(NodeWarningType::Error,
@@ -86,25 +116,22 @@ static void attribute_convert_calc(GeometryComponent &component,
return;
}
- OutputAttributePtr result_attribute = component.attribute_try_get_for_output(
+ OutputAttribute result_attribute = component.attribute_try_get_for_output_only(
result_name, result_domain, result_type);
if (!result_attribute) {
return;
}
- fn::GSpan source_span = source_attribute->get_span();
- fn::GMutableSpan result_span = result_attribute->get_span_for_write_only();
- if (source_span.is_empty() || result_span.is_empty()) {
- return;
- }
+ GVArray_GSpan source_span{*source_attribute};
+ GMutableSpan result_span = result_attribute.as_span();
+
BLI_assert(source_span.size() == result_span.size());
const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(result_type);
BLI_assert(cpp_type != nullptr);
cpp_type->copy_to_initialized_n(source_span.data(), result_span.data(), result_span.size());
-
- result_attribute.apply_span_and_save();
+ result_attribute.save();
}
static void geo_node_attribute_convert_exec(GeoNodeExecParams params)
@@ -140,6 +167,14 @@ static void geo_node_attribute_convert_exec(GeoNodeExecParams params)
data_type,
domain);
}
+ if (geometry_set.has<CurveComponent>()) {
+ attribute_convert_calc(geometry_set.get_component_for_write<CurveComponent>(),
+ params,
+ source_name,
+ result_name,
+ data_type,
+ domain);
+ }
params.set_output("Geometry", geometry_set);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc
new file mode 100644
index 00000000000..599c9e58e52
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc
@@ -0,0 +1,232 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BLI_blenlib.h"
+#include "BLI_task.hh"
+
+#include "BKE_colortools.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_attribute_curve_map_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_STRING, N_("Attribute")},
+ {SOCK_STRING, N_("Result")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_attribute_curve_map_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+static void geo_node_attribute_curve_map_layout(uiLayout *layout,
+ bContext *UNUSED(C),
+ PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE);
+ bNode *node = (bNode *)ptr->data;
+ NodeAttributeCurveMap *data = (NodeAttributeCurveMap *)node->storage;
+ switch (data->data_type) {
+ case CD_PROP_FLOAT:
+ uiTemplateCurveMapping(layout, ptr, "curve_vec", 0, false, false, false, false);
+ break;
+ case CD_PROP_FLOAT3:
+ uiTemplateCurveMapping(layout, ptr, "curve_vec", 'v', false, false, false, false);
+ break;
+ case CD_PROP_COLOR:
+ uiTemplateCurveMapping(layout, ptr, "curve_rgb", 'c', false, false, false, false);
+ break;
+ }
+}
+
+static void geo_node_attribute_curve_map_free_storage(bNode *node)
+{
+ if (node->storage) {
+ NodeAttributeCurveMap *data = (NodeAttributeCurveMap *)node->storage;
+ BKE_curvemapping_free(data->curve_vec);
+ BKE_curvemapping_free(data->curve_rgb);
+ MEM_freeN(node->storage);
+ }
+}
+
+static void geo_node_attribute_curve_map_copy_storage(bNodeTree *UNUSED(dest_ntree),
+ bNode *dest_node,
+ const bNode *src_node)
+{
+ dest_node->storage = MEM_dupallocN(src_node->storage);
+ NodeAttributeCurveMap *src_data = (NodeAttributeCurveMap *)src_node->storage;
+ NodeAttributeCurveMap *dest_data = (NodeAttributeCurveMap *)dest_node->storage;
+ dest_data->curve_vec = BKE_curvemapping_copy(src_data->curve_vec);
+ dest_data->curve_rgb = BKE_curvemapping_copy(src_data->curve_rgb);
+}
+
+static void geo_node_attribute_curve_map_init(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeAttributeCurveMap *data = (NodeAttributeCurveMap *)MEM_callocN(sizeof(NodeAttributeCurveMap),
+ __func__);
+
+ data->data_type = CD_PROP_FLOAT;
+ data->curve_vec = BKE_curvemapping_add(4, -1.0f, -1.0f, 1.0f, 1.0f);
+ data->curve_vec->cur = 3;
+ data->curve_rgb = BKE_curvemapping_add(4, 0.0f, 0.0f, 1.0f, 1.0f);
+ node->storage = data;
+}
+
+static void geo_node_attribute_curve_map_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ /* Set the active curve when data type is changed. */
+ NodeAttributeCurveMap *data = (NodeAttributeCurveMap *)node->storage;
+ if (data->data_type == CD_PROP_FLOAT) {
+ data->curve_vec->cur = 3;
+ }
+ else if (data->data_type == CD_PROP_FLOAT3) {
+ data->curve_vec->cur = 0;
+ }
+}
+
+namespace blender::nodes {
+
+static AttributeDomain get_result_domain(const GeometryComponent &component,
+ StringRef input_name,
+ StringRef result_name)
+{
+ /* Use the domain of the result attribute if it already exists. */
+ ReadAttributeLookup result_attribute = component.attribute_try_get_for_read(result_name);
+ if (result_attribute) {
+ return result_attribute.domain;
+ }
+
+ /* Otherwise use the input attribute's domain if it exists. */
+ ReadAttributeLookup input_attribute = component.attribute_try_get_for_read(input_name);
+ if (input_attribute) {
+ return input_attribute.domain;
+ }
+
+ return ATTR_DOMAIN_POINT;
+}
+
+static void execute_on_component(const GeoNodeExecParams &params, GeometryComponent &component)
+{
+ const bNode &bnode = params.node();
+ NodeAttributeCurveMap &node_storage = *(NodeAttributeCurveMap *)bnode.storage;
+ const std::string result_name = params.get_input<std::string>("Result");
+ const std::string input_name = params.get_input<std::string>("Attribute");
+
+ const CustomDataType result_type = (CustomDataType)node_storage.data_type;
+ const AttributeDomain result_domain = get_result_domain(component, input_name, result_name);
+
+ OutputAttribute attribute_result = component.attribute_try_get_for_output_only(
+ result_name, result_domain, result_type);
+ if (!attribute_result) {
+ return;
+ }
+
+ switch (result_type) {
+ case CD_PROP_FLOAT: {
+ const CurveMapping *cumap = (CurveMapping *)node_storage.curve_vec;
+ GVArray_Typed<float> attribute_in = component.attribute_get_for_read<float>(
+ input_name, result_domain, float(0.0f));
+ MutableSpan<float> results = attribute_result.as_span<float>();
+ parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ results[i] = BKE_curvemapping_evaluateF(cumap, 3, attribute_in[i]);
+ }
+ });
+ break;
+ }
+ case CD_PROP_FLOAT3: {
+ const CurveMapping *cumap = (CurveMapping *)node_storage.curve_vec;
+ GVArray_Typed<float3> attribute_in = component.attribute_get_for_read<float3>(
+ input_name, result_domain, float3(0.0f));
+ MutableSpan<float3> results = attribute_result.as_span<float3>();
+ parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ BKE_curvemapping_evaluate3F(cumap, results[i], attribute_in[i]);
+ }
+ });
+ break;
+ }
+ case CD_PROP_COLOR: {
+ const CurveMapping *cumap = (CurveMapping *)node_storage.curve_rgb;
+ GVArray_Typed<ColorGeometry4f> attribute_in =
+ component.attribute_get_for_read<ColorGeometry4f>(
+ input_name, result_domain, ColorGeometry4f(0.0f, 0.0f, 0.0f, 1.0f));
+ MutableSpan<ColorGeometry4f> results = attribute_result.as_span<ColorGeometry4f>();
+ parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ BKE_curvemapping_evaluateRGBF(cumap, results[i], attribute_in[i]);
+ }
+ });
+ break;
+ }
+ default: {
+ BLI_assert_unreachable();
+ break;
+ }
+ }
+
+ attribute_result.save();
+}
+
+static void geo_node_attribute_curve_map_exec(GeoNodeExecParams params)
+{
+ const bNode &bnode = params.node();
+ NodeAttributeCurveMap *data = (NodeAttributeCurveMap *)bnode.storage;
+ BKE_curvemapping_init(data->curve_vec);
+ BKE_curvemapping_init(data->curve_rgb);
+
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+ geometry_set = geometry_set_realize_instances(geometry_set);
+
+ if (geometry_set.has<MeshComponent>()) {
+ execute_on_component(params, geometry_set.get_component_for_write<MeshComponent>());
+ }
+ if (geometry_set.has<PointCloudComponent>()) {
+ execute_on_component(params, geometry_set.get_component_for_write<PointCloudComponent>());
+ }
+ if (geometry_set.has<CurveComponent>()) {
+ execute_on_component(params, geometry_set.get_component_for_write<CurveComponent>());
+ }
+
+ params.set_output("Geometry", std::move(geometry_set));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_attribute_curve_map()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_ATTRIBUTE_CURVE_MAP, "Attribute Curve Map", NODE_CLASS_ATTRIBUTE, 0);
+ node_type_socket_templates(
+ &ntype, geo_node_attribute_curve_map_in, geo_node_attribute_curve_map_out);
+ node_type_update(&ntype, geo_node_attribute_curve_map_update);
+ node_type_init(&ntype, geo_node_attribute_curve_map_init);
+ node_type_size_preset(&ntype, NODE_SIZE_LARGE);
+ node_type_storage(&ntype,
+ "NodeAttributeCurveMap",
+ geo_node_attribute_curve_map_free_storage,
+ geo_node_attribute_curve_map_copy_storage);
+ ntype.geometry_node_execute = blender::nodes::geo_node_attribute_curve_map_exec;
+ ntype.draw_buttons = geo_node_attribute_curve_map_layout;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc
index cbe44c54fb4..51fd65f65fd 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc
@@ -27,6 +27,11 @@
#include "WM_types.h"
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
static bNodeSocketTemplate geo_node_attribute_fill_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_STRING, N_("Attribute")},
@@ -188,13 +193,12 @@ static void geo_node_attribute_fill_layout(uiLayout *layout, bContext *C, Pointe
draw_input_socket(C, layout, node_ptr, "Value", "data_type");
}
-static AttributeDomain get_result_domain(const GeometryComponent &component,
- StringRef attribute_name)
+static AttributeDomain get_result_domain(const GeometryComponent &component, const StringRef name)
{
/* Use the domain of the result attribute if it already exists. */
- ReadAttributePtr result_attribute = component.attribute_try_get_for_read(attribute_name);
- if (result_attribute) {
- return result_attribute->domain();
+ std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(name);
+ if (result_info) {
+ return result_info->domain;
}
return ATTR_DOMAIN_POINT;
}
@@ -213,7 +217,7 @@ static void fill_attribute(GeometryComponent &component, const GeoNodeExecParams
get_result_domain(component, attribute_name) :
domain;
- OutputAttributePtr attribute = component.attribute_try_get_for_output(
+ OutputAttribute attribute = component.attribute_try_get_for_output_only(
attribute_name, result_domain, data_type);
if (!attribute) {
return;
@@ -222,38 +226,34 @@ static void fill_attribute(GeometryComponent &component, const GeoNodeExecParams
switch (data_type) {
case CD_PROP_FLOAT: {
const float value = params.get_input<float>("Value_001");
- MutableSpan<float> attribute_span = attribute->get_span_for_write_only<float>();
- attribute_span.fill(value);
+ attribute->fill(&value);
break;
}
case CD_PROP_FLOAT3: {
const float3 value = params.get_input<float3>("Value");
- MutableSpan<float3> attribute_span = attribute->get_span_for_write_only<float3>();
- attribute_span.fill(value);
+ attribute->fill(&value);
break;
}
case CD_PROP_COLOR: {
- const Color4f value = params.get_input<Color4f>("Value_002");
- MutableSpan<Color4f> attribute_span = attribute->get_span_for_write_only<Color4f>();
- attribute_span.fill(value);
+ const ColorGeometry4f value = params.get_input<ColorGeometry4f>("Value_002");
+ attribute->fill(&value);
break;
}
case CD_PROP_BOOL: {
const bool value = params.get_input<bool>("Value_003");
- MutableSpan<bool> attribute_span = attribute->get_span_for_write_only<bool>();
- attribute_span.fill(value);
+ attribute->fill(&value);
break;
}
case CD_PROP_INT32: {
const int value = params.get_input<int>("Value_004");
- MutableSpan<int> attribute_span = attribute->get_span_for_write_only<int>();
- attribute_span.fill(value);
+ attribute->fill(&value);
+ break;
}
default:
break;
}
- attribute.apply_span_and_save();
+ attribute.save();
}
static void geo_node_attribute_fill_exec(GeoNodeExecParams params)
@@ -268,6 +268,9 @@ static void geo_node_attribute_fill_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
fill_attribute(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
+ if (geometry_set.has<CurveComponent>()) {
+ fill_attribute(geometry_set.get_component_for_write<CurveComponent>(), params);
+ }
params.set_output("Geometry", geometry_set);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_map_range.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_map_range.cc
new file mode 100644
index 00000000000..40fe675bd6c
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_map_range.cc
@@ -0,0 +1,437 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BLI_math_base_safe.h"
+#include "BLI_task.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_attribute_map_range_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_STRING, N_("Attribute")},
+ {SOCK_STRING, N_("Result")},
+ {SOCK_FLOAT, N_("From Min"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_FLOAT, N_("From Max"), 1.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_FLOAT, N_("To Min"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_FLOAT, N_("To Max"), 1.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_FLOAT, N_("Steps"), 4.0f, 4.0f, 4.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_VECTOR, N_("From Min"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_VECTOR, N_("From Max"), 1.0f, 1.0f, 1.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_VECTOR, N_("To Min"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_VECTOR, N_("To Max"), 1.0f, 1.0f, 1.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_VECTOR, N_("Steps"), 4.0f, 4.0f, 4.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_BOOLEAN, N_("Clamp")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_attribute_map_range_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+static void fn_attribute_map_range_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE);
+ uiItemR(layout, ptr, "interpolation_type", 0, "", ICON_NONE);
+}
+
+static void geo_node_attribute_map_range_init(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeAttributeMapRange *data = (NodeAttributeMapRange *)MEM_callocN(sizeof(NodeAttributeMapRange),
+ __func__);
+ data->data_type = CD_PROP_FLOAT;
+ data->interpolation_type = NODE_MAP_RANGE_LINEAR;
+
+ node->storage = data;
+}
+static void geo_node_attribute_map_range_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeAttributeMapRange &node_storage = *(NodeAttributeMapRange *)node->storage;
+
+ bNodeSocket *sock_from_min_float = (bNodeSocket *)BLI_findlink(&node->inputs, 3);
+ bNodeSocket *sock_from_max_float = sock_from_min_float->next;
+ bNodeSocket *sock_to_min_float = sock_from_max_float->next;
+ bNodeSocket *sock_to_max_float = sock_to_min_float->next;
+ bNodeSocket *sock_steps_float = sock_to_max_float->next;
+
+ bNodeSocket *sock_from_min_vector = sock_steps_float->next;
+ bNodeSocket *sock_from_max_vector = sock_from_min_vector->next;
+ bNodeSocket *sock_to_min_vector = sock_from_max_vector->next;
+ bNodeSocket *sock_to_max_vector = sock_to_min_vector->next;
+ bNodeSocket *sock_steps_vector = sock_to_max_vector->next;
+
+ bNodeSocket *sock_clamp = sock_steps_vector->next;
+
+ const CustomDataType data_type = static_cast<CustomDataType>(node_storage.data_type);
+
+ nodeSetSocketAvailability(sock_clamp,
+ node_storage.interpolation_type == NODE_MAP_RANGE_LINEAR ||
+ node_storage.interpolation_type == NODE_MAP_RANGE_STEPPED);
+
+ nodeSetSocketAvailability(sock_from_min_float, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(sock_from_max_float, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(sock_to_min_float, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(sock_to_max_float, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(sock_steps_float,
+ data_type == CD_PROP_FLOAT &&
+ node_storage.interpolation_type == NODE_MAP_RANGE_STEPPED);
+
+ nodeSetSocketAvailability(sock_from_min_vector, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(sock_from_max_vector, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(sock_to_min_vector, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(sock_to_max_vector, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(sock_steps_vector,
+ data_type == CD_PROP_FLOAT3 &&
+ node_storage.interpolation_type == NODE_MAP_RANGE_STEPPED);
+}
+
+namespace blender::nodes {
+
+static float map_linear(const float value,
+ const float min_from,
+ const float max_from,
+ const float min_to,
+ const float max_to)
+{
+ /* First we calculate a fraction that measures how far along
+ * the [min_from, max_from] interval the value lies.
+ *
+ * value
+ * min_from [------>|------------------------] max_from
+ * factor (e.g. 0.25)
+ *
+ * Then to find where the value is mapped, we add the same fraction
+ * of the [min_to, max_to] interval to min_to.
+ *
+ * min_to [--->|-----------] max_to
+ * v
+ * min_to + (max_to - min_to) * factor
+ */
+ const float factor = safe_divide(value - min_from, max_from - min_from);
+ return min_to + factor * (max_to - min_to);
+}
+
+static float map_stepped(const float value,
+ const float min_from,
+ const float max_from,
+ const float min_to,
+ const float max_to,
+ const float steps)
+{
+ /* First the factor is calculated here in the same way as for the linear mapping.
+ *
+ * Then the factor is mapped to multiples of 1.0 / steps.
+ * This is best understood with a few examples. Assume steps == 3.
+ * ____________________________________
+ * | factor | * 4.0 | floor() | / 3.0 |
+ * |--------|-------|---------|-------|
+ * | 0.0 | 0.0 | 0.0 | 0.0 |
+ * | 0.1 | 0.4 | 0.0 | 0.0 |
+ * | 0.25 | 1.0 | 1.0 | 0.333 |
+ * | 0.45 | 1.8 | 1.0 | 0.333 |
+ * | 0.5 | 2.0 | 2.0 | 0.666 |
+ * | 0.55 | 2.2 | 2.0 | 0.666 |
+ * | 0.999 | 3.999 | 3.0 | 1.0 |
+ * | 1.0 | 4.0 | 4.0 | 1.333 |
+ * ------------------------------------
+ * Note that the factor is not always mapped the closest multiple of 1.0 /steps.
+ */
+ const float factor = safe_divide(value - min_from, max_from - min_from);
+ const float factor_mapped = safe_divide(floorf(factor * (steps + 1.0f)), steps);
+ return min_to + factor_mapped * (max_to - min_to);
+}
+
+static float smoothstep_polynomial(float x)
+{
+ /* This polynomial is only meant to be used for the [0, 1] range. */
+ return (3.0f - 2.0f * x) * (x * x);
+}
+
+static float map_smoothstep(const float value,
+ const float min_from,
+ const float max_from,
+ const float min_to,
+ const float max_to)
+{
+ const float factor = safe_divide(value - min_from, max_from - min_from);
+ const float factor_clamped = std::clamp(factor, 0.0f, 1.0f);
+ const float factor_mapped = smoothstep_polynomial(factor_clamped);
+ return min_to + factor_mapped * (max_to - min_to);
+}
+
+static float smootherstep_polynomial(float x)
+{
+ /* This polynomial is only meant to be used for the [0, 1] range. */
+ return x * x * x * (x * (x * 6.0f - 15.0f) + 10.0f);
+}
+
+static float map_smootherstep(const float value,
+ const float min_from,
+ const float max_from,
+ const float min_to,
+ const float max_to)
+{
+ const float factor = safe_divide(value - min_from, max_from - min_from);
+ const float factor_clamped = std::clamp(factor, 0.0f, 1.0f);
+ const float factor_mapped = smootherstep_polynomial(factor_clamped);
+ return min_to + factor_mapped * (max_to - min_to);
+}
+
+static void map_range_float(const VArray<float> &attribute_input,
+ MutableSpan<float> results,
+ const GeoNodeExecParams &params)
+{
+ const bNode &node = params.node();
+ NodeAttributeMapRange &node_storage = *(NodeAttributeMapRange *)node.storage;
+ const int interpolation_type = node_storage.interpolation_type;
+ const float min_from = params.get_input<float>("From Min");
+ const float max_from = params.get_input<float>("From Max");
+ const float min_to = params.get_input<float>("To Min");
+ const float max_to = params.get_input<float>("To Max");
+
+ VArray_Span<float> span{attribute_input};
+
+ switch (interpolation_type) {
+ case NODE_MAP_RANGE_LINEAR: {
+ parallel_for(span.index_range(), 2048, [&](IndexRange range) {
+ for (const int i : range) {
+ results[i] = map_linear(span[i], min_from, max_from, min_to, max_to);
+ }
+ });
+ break;
+ }
+ case NODE_MAP_RANGE_STEPPED: {
+ const float steps = params.get_input<float>("Steps");
+ parallel_for(span.index_range(), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ results[i] = map_stepped(span[i], min_from, max_from, min_to, max_to, steps);
+ }
+ });
+ break;
+ }
+ case NODE_MAP_RANGE_SMOOTHSTEP: {
+ parallel_for(span.index_range(), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ results[i] = map_smoothstep(span[i], min_from, max_from, min_to, max_to);
+ }
+ });
+ break;
+ }
+ case NODE_MAP_RANGE_SMOOTHERSTEP: {
+ parallel_for(span.index_range(), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ results[i] = map_smootherstep(span[i], min_from, max_from, min_to, max_to);
+ }
+ });
+ break;
+ }
+ }
+
+ if (ELEM(interpolation_type, NODE_MAP_RANGE_LINEAR, NODE_MAP_RANGE_STEPPED) &&
+ params.get_input<bool>("Clamp")) {
+ /* Users can specify min_to > max_to, but clamping expects min < max. */
+ const float clamp_min = min_to < max_to ? min_to : max_to;
+ const float clamp_max = min_to < max_to ? max_to : min_to;
+
+ parallel_for(results.index_range(), 2048, [&](IndexRange range) {
+ for (const int i : range) {
+ results[i] = std::clamp(results[i], clamp_min, clamp_max);
+ }
+ });
+ }
+}
+
+static void map_range_float3(const VArray<float3> &attribute_input,
+ const MutableSpan<float3> results,
+ const GeoNodeExecParams &params)
+{
+ const bNode &node = params.node();
+ NodeAttributeMapRange &node_storage = *(NodeAttributeMapRange *)node.storage;
+ const int interpolation_type = node_storage.interpolation_type;
+ const float3 min_from = params.get_input<float3>("From Min_001");
+ const float3 max_from = params.get_input<float3>("From Max_001");
+ const float3 min_to = params.get_input<float3>("To Min_001");
+ const float3 max_to = params.get_input<float3>("To Max_001");
+
+ VArray_Span<float3> span{attribute_input};
+
+ switch (interpolation_type) {
+ case NODE_MAP_RANGE_LINEAR: {
+ parallel_for(span.index_range(), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ results[i].x = map_linear(span[i].x, min_from.x, max_from.x, min_to.x, max_to.x);
+ results[i].y = map_linear(span[i].y, min_from.y, max_from.y, min_to.y, max_to.y);
+ results[i].z = map_linear(span[i].z, min_from.z, max_from.z, min_to.z, max_to.z);
+ }
+ });
+ break;
+ }
+ case NODE_MAP_RANGE_STEPPED: {
+ const float3 steps = params.get_input<float3>("Steps_001");
+ parallel_for(span.index_range(), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ results[i].x = map_stepped(
+ span[i].x, min_from.x, max_from.x, min_to.x, max_to.x, steps.x);
+ results[i].y = map_stepped(
+ span[i].y, min_from.y, max_from.y, min_to.y, max_to.y, steps.y);
+ results[i].z = map_stepped(
+ span[i].z, min_from.z, max_from.z, min_to.z, max_to.z, steps.z);
+ }
+ });
+ break;
+ }
+ case NODE_MAP_RANGE_SMOOTHSTEP: {
+ parallel_for(span.index_range(), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ results[i].x = map_smoothstep(span[i].x, min_from.x, max_from.x, min_to.x, max_to.x);
+ results[i].y = map_smoothstep(span[i].y, min_from.y, max_from.y, min_to.y, max_to.y);
+ results[i].z = map_smoothstep(span[i].z, min_from.z, max_from.z, min_to.z, max_to.z);
+ }
+ });
+ break;
+ }
+ case NODE_MAP_RANGE_SMOOTHERSTEP: {
+ parallel_for(span.index_range(), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ results[i].x = map_smootherstep(span[i].x, min_from.x, max_from.x, min_to.x, max_to.x);
+ results[i].y = map_smootherstep(span[i].y, min_from.y, max_from.y, min_to.y, max_to.y);
+ results[i].z = map_smootherstep(span[i].z, min_from.z, max_from.z, min_to.z, max_to.z);
+ }
+ });
+ break;
+ }
+ }
+
+ if (ELEM(interpolation_type, NODE_MAP_RANGE_LINEAR, NODE_MAP_RANGE_STEPPED) &&
+ params.get_input<bool>("Clamp")) {
+ /* Users can specify min_to > max_to, but clamping expects min < max. */
+ float3 clamp_min;
+ float3 clamp_max;
+ clamp_min.x = min_to.x < max_to.x ? min_to.x : max_to.x;
+ clamp_max.x = min_to.x < max_to.x ? max_to.x : min_to.x;
+ clamp_min.y = min_to.y < max_to.y ? min_to.y : max_to.y;
+ clamp_max.y = min_to.y < max_to.y ? max_to.y : min_to.y;
+ clamp_min.z = min_to.z < max_to.z ? min_to.z : max_to.z;
+ clamp_max.z = min_to.z < max_to.z ? max_to.z : min_to.z;
+
+ for (int i : results.index_range()) {
+ clamp_v3_v3v3(results[i], clamp_min, clamp_max);
+ }
+ }
+}
+
+static AttributeDomain get_result_domain(const GeometryComponent &component,
+ StringRef source_name,
+ StringRef result_name)
+{
+ std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name);
+ if (result_info) {
+ return result_info->domain;
+ }
+ std::optional<AttributeMetaData> source_info = component.attribute_get_meta_data(source_name);
+ if (source_info) {
+ return source_info->domain;
+ }
+ return ATTR_DOMAIN_POINT;
+}
+
+static void map_range_attribute(GeometryComponent &component, const GeoNodeExecParams &params)
+{
+ const std::string input_name = params.get_input<std::string>("Attribute");
+ const std::string result_name = params.get_input<std::string>("Result");
+
+ if (input_name.empty() || result_name.empty()) {
+ return;
+ }
+
+ const bNode &node = params.node();
+ NodeAttributeMapRange &node_storage = *(NodeAttributeMapRange *)node.storage;
+ const CustomDataType data_type = static_cast<CustomDataType>(node_storage.data_type);
+
+ const AttributeDomain domain = get_result_domain(component, input_name, result_name);
+
+ GVArrayPtr attribute_input = component.attribute_try_get_for_read(input_name, domain, data_type);
+
+ if (!attribute_input) {
+ params.error_message_add(NodeWarningType::Error,
+ TIP_("No attribute with name \"") + input_name + "\"");
+ return;
+ }
+
+ OutputAttribute attribute_result = component.attribute_try_get_for_output_only(
+ result_name, domain, data_type);
+ if (!attribute_result) {
+ params.error_message_add(NodeWarningType::Error,
+ TIP_("Could not find or create attribute with name \"") +
+ result_name + "\"");
+ return;
+ }
+
+ switch (data_type) {
+ case CD_PROP_FLOAT: {
+ map_range_float(attribute_input->typed<float>(), attribute_result.as_span<float>(), params);
+ break;
+ }
+ case CD_PROP_FLOAT3: {
+ map_range_float3(
+ attribute_input->typed<float3>(), attribute_result.as_span<float3>(), params);
+ break;
+ }
+ default:
+ BLI_assert_unreachable();
+ }
+
+ attribute_result.save();
+}
+
+static void geo_node_attribute_map_range_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+ if (geometry_set.has<MeshComponent>()) {
+ map_range_attribute(geometry_set.get_component_for_write<MeshComponent>(), params);
+ }
+ if (geometry_set.has<PointCloudComponent>()) {
+ map_range_attribute(geometry_set.get_component_for_write<PointCloudComponent>(), params);
+ }
+ if (geometry_set.has<CurveComponent>()) {
+ map_range_attribute(geometry_set.get_component_for_write<CurveComponent>(), params);
+ }
+
+ params.set_output("Geometry", geometry_set);
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_attribute_map_range()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_ATTRIBUTE_MAP_RANGE, "Attribute Map Range", NODE_CLASS_ATTRIBUTE, 0);
+ node_type_socket_templates(
+ &ntype, geo_node_attribute_map_range_in, geo_node_attribute_map_range_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_attribute_map_range_exec;
+ node_type_init(&ntype, geo_node_attribute_map_range_init);
+ node_type_update(&ntype, geo_node_attribute_map_range_update);
+ node_type_storage(
+ &ntype, "NodeAttributeMapRange", node_free_standard_storage, node_copy_standard_storage);
+ ntype.draw_buttons = fn_attribute_map_range_layout;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc
index 8c6d49e322c..5b78b4cd39e 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc
@@ -14,23 +14,15 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "node_geometry_util.hh"
-
-#include "BKE_attribute.h"
-#include "BKE_attribute_access.hh"
-
-#include "BLI_array.hh"
-#include "BLI_math_base_safe.h"
-#include "BLI_rand.hh"
-
-#include "DNA_mesh_types.h"
-#include "DNA_pointcloud_types.h"
+#include "BLI_task.hh"
#include "UI_interface.h"
#include "UI_resources.h"
#include "NOD_math_functions.hh"
+#include "node_geometry_util.hh"
+
static bNodeSocketTemplate geo_node_attribute_math_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_STRING, N_("A")},
@@ -163,46 +155,52 @@ static void geo_node_attribute_math_update(bNodeTree *UNUSED(ntree), bNode *node
operation_use_input_c(operation));
}
-static void do_math_operation(Span<float> span_a,
- Span<float> span_b,
- Span<float> span_c,
+static void do_math_operation(const VArray<float> &span_a,
+ const VArray<float> &span_b,
+ const VArray<float> &span_c,
MutableSpan<float> span_result,
const NodeMathOperation operation)
{
bool success = try_dispatch_float_math_fl_fl_fl_to_fl(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- for (const int i : IndexRange(span_result.size())) {
- span_result[i] = math_function(span_a[i], span_b[i], span_c[i]);
- }
+ parallel_for(IndexRange(span_result.size()), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ span_result[i] = math_function(span_a[i], span_b[i], span_c[i]);
+ }
+ });
});
BLI_assert(success);
UNUSED_VARS_NDEBUG(success);
}
-static void do_math_operation(Span<float> span_a,
- Span<float> span_b,
+static void do_math_operation(const VArray<float> &span_a,
+ const VArray<float> &span_b,
MutableSpan<float> span_result,
const NodeMathOperation operation)
{
bool success = try_dispatch_float_math_fl_fl_to_fl(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- for (const int i : IndexRange(span_result.size())) {
- span_result[i] = math_function(span_a[i], span_b[i]);
- }
+ parallel_for(IndexRange(span_result.size()), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ span_result[i] = math_function(span_a[i], span_b[i]);
+ }
+ });
});
BLI_assert(success);
UNUSED_VARS_NDEBUG(success);
}
-static void do_math_operation(Span<float> span_input,
+static void do_math_operation(const VArray<float> &span_input,
MutableSpan<float> span_result,
const NodeMathOperation operation)
{
bool success = try_dispatch_float_math_fl_to_fl(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- for (const int i : IndexRange(span_result.size())) {
- span_result[i] = math_function(span_input[i]);
- }
+ parallel_for(IndexRange(span_result.size()), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ span_result[i] = math_function(span_input[i]);
+ }
+ });
});
BLI_assert(success);
UNUSED_VARS_NDEBUG(success);
@@ -214,9 +212,9 @@ static AttributeDomain get_result_domain(const GeometryComponent &component,
StringRef result_name)
{
/* Use the domain of the result attribute if it already exists. */
- ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name);
- if (result_attribute) {
- return result_attribute->domain();
+ std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name);
+ if (result_info) {
+ return result_info->domain;
}
/* Otherwise use the highest priority domain from existing input attributes, or the default. */
@@ -238,56 +236,39 @@ static void attribute_math_calc(GeometryComponent &component, const GeoNodeExecP
const std::string result_name = params.get_input<std::string>("Result");
/* The result type of this node is always float. */
- const CustomDataType result_type = CD_PROP_FLOAT;
const AttributeDomain result_domain = get_result_domain(
component, params, operation, result_name);
- OutputAttributePtr attribute_result = component.attribute_try_get_for_output(
- result_name, result_domain, result_type);
+ OutputAttribute_Typed<float> attribute_result =
+ component.attribute_try_get_for_output_only<float>(result_name, result_domain);
if (!attribute_result) {
return;
}
- ReadAttributePtr attribute_a = params.get_input_attribute(
- "A", component, result_domain, result_type, nullptr);
- if (!attribute_a) {
- return;
- }
+ GVArray_Typed<float> attribute_a = params.get_input_attribute<float>(
+ "A", component, result_domain, 0.0f);
+
+ MutableSpan<float> result_span = attribute_result.as_span();
- /* Note that passing the data with `get_span<float>()` works
+ /* Note that passing the data with `get_internal_span<float>()` works
* because the attributes were accessed with #CD_PROP_FLOAT. */
if (operation_use_input_b(operation)) {
- ReadAttributePtr attribute_b = params.get_input_attribute(
- "B", component, result_domain, result_type, nullptr);
- if (!attribute_b) {
- return;
- }
+ GVArray_Typed<float> attribute_b = params.get_input_attribute<float>(
+ "B", component, result_domain, 0.0f);
if (operation_use_input_c(operation)) {
- ReadAttributePtr attribute_c = params.get_input_attribute(
- "C", component, result_domain, result_type, nullptr);
- if (!attribute_c) {
- return;
- }
- do_math_operation(attribute_a->get_span<float>(),
- attribute_b->get_span<float>(),
- attribute_c->get_span<float>(),
- attribute_result->get_span_for_write_only<float>(),
- operation);
+ GVArray_Typed<float> attribute_c = params.get_input_attribute<float>(
+ "C", component, result_domain, 0.0f);
+ do_math_operation(attribute_a, attribute_b, attribute_c, result_span, operation);
}
else {
- do_math_operation(attribute_a->get_span<float>(),
- attribute_b->get_span<float>(),
- attribute_result->get_span_for_write_only<float>(),
- operation);
+ do_math_operation(attribute_a, attribute_b, result_span, operation);
}
}
else {
- do_math_operation(attribute_a->get_span<float>(),
- attribute_result->get_span_for_write_only<float>(),
- operation);
+ do_math_operation(attribute_a, result_span, operation);
}
- attribute_result.apply_span_and_save();
+ attribute_result.save();
}
static void geo_node_attribute_math_exec(GeoNodeExecParams params)
@@ -302,6 +283,9 @@ static void geo_node_attribute_math_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
attribute_math_calc(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
+ if (geometry_set.has<CurveComponent>()) {
+ attribute_math_calc(geometry_set.get_component_for_write<CurveComponent>(), params);
+ }
params.set_output("Geometry", geometry_set);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc
index 9d8cd3dfa82..a6bd6c0ee32 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc
@@ -14,6 +14,8 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#include "BLI_task.hh"
+
#include "BKE_material.h"
#include "DNA_material_types.h"
@@ -57,73 +59,110 @@ static void geo_node_attribute_mix_layout(uiLayout *layout, bContext *UNUSED(C),
namespace blender::nodes {
+static void geo_node_attribute_mix_init(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeAttributeMix *data = (NodeAttributeMix *)MEM_callocN(sizeof(NodeAttributeMix),
+ "attribute mix node");
+ data->blend_type = MA_RAMP_BLEND;
+ data->input_type_factor = GEO_NODE_ATTRIBUTE_INPUT_FLOAT;
+ data->input_type_a = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE;
+ data->input_type_b = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE;
+ node->storage = data;
+}
+
+static void geo_node_attribute_mix_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeAttributeMix *node_storage = (NodeAttributeMix *)node->storage;
+ update_attribute_input_socket_availabilities(
+ *node, "Factor", (GeometryNodeAttributeInputMode)node_storage->input_type_factor);
+ update_attribute_input_socket_availabilities(
+ *node, "A", (GeometryNodeAttributeInputMode)node_storage->input_type_a);
+ update_attribute_input_socket_availabilities(
+ *node, "B", (GeometryNodeAttributeInputMode)node_storage->input_type_b);
+}
+
static void do_mix_operation_float(const int blend_mode,
- const FloatReadAttribute &factors,
- const FloatReadAttribute &inputs_a,
- const FloatReadAttribute &inputs_b,
- FloatWriteAttribute results)
+ const VArray<float> &factors,
+ const VArray<float> &inputs_a,
+ const VArray<float> &inputs_b,
+ VMutableArray<float> &results)
{
const int size = results.size();
- for (const int i : IndexRange(size)) {
- const float factor = factors[i];
- float3 a{inputs_a[i]};
- const float3 b{inputs_b[i]};
- ramp_blend(blend_mode, a, factor, b);
- const float result = a.x;
- results.set(i, result);
- }
+ parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const float factor = factors[i];
+ float3 a{inputs_a[i]};
+ const float3 b{inputs_b[i]};
+ ramp_blend(blend_mode, a, factor, b);
+ const float result = a.x;
+ results.set(i, result);
+ }
+ });
}
static void do_mix_operation_float3(const int blend_mode,
- const FloatReadAttribute &factors,
- const Float3ReadAttribute &inputs_a,
- const Float3ReadAttribute &inputs_b,
- Float3WriteAttribute results)
+ const VArray<float> &factors,
+ const VArray<float3> &inputs_a,
+ const VArray<float3> &inputs_b,
+ VMutableArray<float3> &results)
{
const int size = results.size();
- for (const int i : IndexRange(size)) {
- const float factor = factors[i];
- float3 a = inputs_a[i];
- const float3 b = inputs_b[i];
- ramp_blend(blend_mode, a, factor, b);
- results.set(i, a);
- }
+ parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const float factor = factors[i];
+ float3 a = inputs_a[i];
+ const float3 b = inputs_b[i];
+ ramp_blend(blend_mode, a, factor, b);
+ results.set(i, a);
+ }
+ });
}
static void do_mix_operation_color4f(const int blend_mode,
- const FloatReadAttribute &factors,
- const Color4fReadAttribute &inputs_a,
- const Color4fReadAttribute &inputs_b,
- Color4fWriteAttribute results)
+ const VArray<float> &factors,
+ const VArray<ColorGeometry4f> &inputs_a,
+ const VArray<ColorGeometry4f> &inputs_b,
+ VMutableArray<ColorGeometry4f> &results)
{
const int size = results.size();
- for (const int i : IndexRange(size)) {
- const float factor = factors[i];
- Color4f a = inputs_a[i];
- const Color4f b = inputs_b[i];
- ramp_blend(blend_mode, a, factor, b);
- results.set(i, a);
- }
+ parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const float factor = factors[i];
+ ColorGeometry4f a = inputs_a[i];
+ const ColorGeometry4f b = inputs_b[i];
+ ramp_blend(blend_mode, a, factor, b);
+ results.set(i, a);
+ }
+ });
}
static void do_mix_operation(const CustomDataType result_type,
int blend_mode,
- const FloatReadAttribute &attribute_factor,
- const ReadAttribute &attribute_a,
- const ReadAttribute &attribute_b,
- WriteAttribute &attribute_result)
+ const VArray<float> &attribute_factor,
+ const GVArray &attribute_a,
+ const GVArray &attribute_b,
+ GVMutableArray &attribute_result)
{
if (result_type == CD_PROP_FLOAT) {
- do_mix_operation_float(
- blend_mode, attribute_factor, attribute_a, attribute_b, attribute_result);
+ do_mix_operation_float(blend_mode,
+ attribute_factor,
+ attribute_a.typed<float>(),
+ attribute_b.typed<float>(),
+ attribute_result.typed<float>());
}
else if (result_type == CD_PROP_FLOAT3) {
- do_mix_operation_float3(
- blend_mode, attribute_factor, attribute_a, attribute_b, attribute_result);
+ do_mix_operation_float3(blend_mode,
+ attribute_factor,
+ attribute_a.typed<float3>(),
+ attribute_b.typed<float3>(),
+ attribute_result.typed<float3>());
}
else if (result_type == CD_PROP_COLOR) {
- do_mix_operation_color4f(
- blend_mode, attribute_factor, attribute_a, attribute_b, attribute_result);
+ do_mix_operation_color4f(blend_mode,
+ attribute_factor,
+ attribute_a.typed<ColorGeometry4f>(),
+ attribute_b.typed<ColorGeometry4f>(),
+ attribute_result.typed<ColorGeometry4f>());
}
}
@@ -132,9 +171,9 @@ static AttributeDomain get_result_domain(const GeometryComponent &component,
StringRef result_name)
{
/* Use the domain of the result attribute if it already exists. */
- ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name);
- if (result_attribute) {
- return result_attribute->domain();
+ std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name);
+ if (result_info) {
+ return result_info->domain;
}
/* Otherwise use the highest priority domain from existing input attributes, or the default. */
@@ -158,17 +197,17 @@ static void attribute_mix_calc(GeometryComponent &component, const GeoNodeExecPa
const AttributeDomain result_domain = get_result_domain(component, params, result_name);
- OutputAttributePtr attribute_result = component.attribute_try_get_for_output(
+ OutputAttribute attribute_result = component.attribute_try_get_for_output_only(
result_name, result_domain, result_type);
if (!attribute_result) {
return;
}
- FloatReadAttribute attribute_factor = params.get_input_attribute<float>(
+ GVArray_Typed<float> attribute_factor = params.get_input_attribute<float>(
"Factor", component, result_domain, 0.5f);
- ReadAttributePtr attribute_a = params.get_input_attribute(
+ GVArrayPtr attribute_a = params.get_input_attribute(
"A", component, result_domain, result_type, nullptr);
- ReadAttributePtr attribute_b = params.get_input_attribute(
+ GVArrayPtr attribute_b = params.get_input_attribute(
"B", component, result_domain, result_type, nullptr);
do_mix_operation(result_type,
@@ -192,32 +231,13 @@ static void geo_node_attribute_mix_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
attribute_mix_calc(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
+ if (geometry_set.has<CurveComponent>()) {
+ attribute_mix_calc(geometry_set.get_component_for_write<CurveComponent>(), params);
+ }
params.set_output("Geometry", geometry_set);
}
-static void geo_node_attribute_mix_init(bNodeTree *UNUSED(ntree), bNode *node)
-{
- NodeAttributeMix *data = (NodeAttributeMix *)MEM_callocN(sizeof(NodeAttributeMix),
- "attribute mix node");
- data->blend_type = MA_RAMP_BLEND;
- data->input_type_factor = GEO_NODE_ATTRIBUTE_INPUT_FLOAT;
- data->input_type_a = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE;
- data->input_type_b = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE;
- node->storage = data;
-}
-
-static void geo_node_attribute_mix_update(bNodeTree *UNUSED(ntree), bNode *node)
-{
- NodeAttributeMix *node_storage = (NodeAttributeMix *)node->storage;
- update_attribute_input_socket_availabilities(
- *node, "Factor", (GeometryNodeAttributeInputMode)node_storage->input_type_factor);
- update_attribute_input_socket_availabilities(
- *node, "A", (GeometryNodeAttributeInputMode)node_storage->input_type_a);
- update_attribute_input_socket_availabilities(
- *node, "B", (GeometryNodeAttributeInputMode)node_storage->input_type_b);
-}
-
} // namespace blender::nodes
void register_node_type_geo_attribute_mix()
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc
index f09a9bf056e..9c22b7fa87f 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc
@@ -62,7 +62,7 @@ namespace blender::nodes {
static void proximity_calc(MutableSpan<float> distance_span,
MutableSpan<float3> location_span,
- Span<float3> positions,
+ const VArray<float3> &positions,
BVHTreeFromMesh &tree_data_mesh,
BVHTreeFromPointCloud &tree_data_pointcloud,
const bool bvh_mesh_success,
@@ -169,19 +169,18 @@ static void attribute_calc_proximity(GeometryComponent &component,
const AttributeDomain result_domain = ATTR_DOMAIN_POINT;
const std::string distance_attribute_name = params.get_input<std::string>("Distance");
- OutputAttributePtr distance_attribute = component.attribute_try_get_for_output(
- distance_attribute_name, result_domain, CD_PROP_FLOAT);
+ OutputAttribute_Typed<float> distance_attribute =
+ component.attribute_try_get_for_output_only<float>(distance_attribute_name, result_domain);
const std::string location_attribute_name = params.get_input<std::string>("Position");
- OutputAttributePtr location_attribute = component.attribute_try_get_for_output(
- location_attribute_name, result_domain, CD_PROP_FLOAT3);
-
- ReadAttributePtr position_attribute = component.attribute_try_get_for_read("position");
- BLI_assert(position_attribute->custom_data_type() == CD_PROP_FLOAT3);
+ OutputAttribute_Typed<float3> location_attribute =
+ component.attribute_try_get_for_output_only<float3>(location_attribute_name, result_domain);
+ ReadAttributeLookup position_attribute = component.attribute_try_get_for_read("position");
if (!position_attribute || (!distance_attribute && !location_attribute)) {
return;
}
+ BLI_assert(position_attribute.varray->type().is<float3>());
const bNode &node = params.node();
const NodeGeometryAttributeProximity &storage = *(const NodeGeometryAttributeProximity *)
@@ -204,18 +203,15 @@ static void attribute_calc_proximity(GeometryComponent &component,
tree_data_pointcloud);
}
- Span<float3> position_span = position_attribute->get_span<float3>();
-
- MutableSpan<float> distance_span = distance_attribute ?
- distance_attribute->get_span_for_write_only<float>() :
- MutableSpan<float>();
- MutableSpan<float3> location_span = location_attribute ?
- location_attribute->get_span_for_write_only<float3>() :
- MutableSpan<float3>();
+ GVArray_Typed<float3> positions{*position_attribute.varray};
+ MutableSpan<float> distance_span = distance_attribute ? distance_attribute.as_span() :
+ MutableSpan<float>();
+ MutableSpan<float3> location_span = location_attribute ? location_attribute.as_span() :
+ MutableSpan<float3>();
proximity_calc(distance_span,
location_span,
- position_span,
+ positions,
tree_data_mesh,
tree_data_pointcloud,
bvh_mesh_success,
@@ -231,10 +227,10 @@ static void attribute_calc_proximity(GeometryComponent &component,
}
if (distance_attribute) {
- distance_attribute.apply_span_and_save();
+ distance_attribute.save();
}
if (location_attribute) {
- location_attribute.apply_span_and_save();
+ location_attribute.save();
}
}
@@ -257,6 +253,10 @@ static void geo_node_attribute_proximity_exec(GeoNodeExecParams params)
attribute_calc_proximity(
geometry_set.get_component_for_write<PointCloudComponent>(), geometry_set_target, params);
}
+ if (geometry_set.has<CurveComponent>()) {
+ attribute_calc_proximity(
+ geometry_set.get_component_for_write<CurveComponent>(), geometry_set_target, params);
+ }
params.set_output("Geometry", geometry_set);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc
index 27c35da7d37..286411b7d28 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc
@@ -14,16 +14,14 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "node_geometry_util.hh"
-
#include "BLI_hash.h"
#include "BLI_rand.hh"
+#include "BLI_task.hh"
#include "UI_interface.h"
#include "UI_resources.h"
-#include "DNA_mesh_types.h"
-#include "DNA_pointcloud_types.h"
+#include "node_geometry_util.hh"
static bNodeSocketTemplate geo_node_attribute_randomize_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
@@ -128,28 +126,36 @@ static void randomize_attribute(MutableSpan<T> span,
/* The operations could be templated too, but it doesn't make the code much shorter. */
switch (operation) {
case GEO_NODE_ATTRIBUTE_RANDOMIZE_REPLACE_CREATE:
- for (const int i : span.index_range()) {
- const T random_value = random_value_in_range<T>(ids[i], seed, min, max);
- span[i] = random_value;
- }
+ parallel_for(span.index_range(), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const T random_value = random_value_in_range<T>(ids[i], seed, min, max);
+ span[i] = random_value;
+ }
+ });
break;
case GEO_NODE_ATTRIBUTE_RANDOMIZE_ADD:
- for (const int i : span.index_range()) {
- const T random_value = random_value_in_range<T>(ids[i], seed, min, max);
- span[i] = span[i] + random_value;
- }
+ parallel_for(span.index_range(), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const T random_value = random_value_in_range<T>(ids[i], seed, min, max);
+ span[i] = span[i] + random_value;
+ }
+ });
break;
case GEO_NODE_ATTRIBUTE_RANDOMIZE_SUBTRACT:
- for (const int i : span.index_range()) {
- const T random_value = random_value_in_range<T>(ids[i], seed, min, max);
- span[i] = span[i] - random_value;
- }
+ parallel_for(span.index_range(), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const T random_value = random_value_in_range<T>(ids[i], seed, min, max);
+ span[i] = span[i] - random_value;
+ }
+ });
break;
case GEO_NODE_ATTRIBUTE_RANDOMIZE_MULTIPLY:
- for (const int i : span.index_range()) {
- const T random_value = random_value_in_range<T>(ids[i], seed, min, max);
- span[i] = span[i] * random_value;
- }
+ parallel_for(span.index_range(), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const T random_value = random_value_in_range<T>(ids[i], seed, min, max);
+ span[i] = span[i] * random_value;
+ }
+ });
break;
default:
BLI_assert(false);
@@ -164,10 +170,12 @@ static void randomize_attribute_bool(MutableSpan<bool> span,
{
BLI_assert(operation == GEO_NODE_ATTRIBUTE_RANDOMIZE_REPLACE_CREATE);
UNUSED_VARS_NDEBUG(operation);
- for (const int i : span.index_range()) {
- const bool random_value = BLI_hash_int_2d_to_float(ids[i], seed) > 0.5f;
- span[i] = random_value;
- }
+ parallel_for(span.index_range(), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const bool random_value = BLI_hash_int_2d_to_float(ids[i], seed) > 0.5f;
+ span[i] = random_value;
+ }
+ });
}
Array<uint32_t> get_geometry_element_ids_as_uints(const GeometryComponent &component,
@@ -176,15 +184,17 @@ Array<uint32_t> get_geometry_element_ids_as_uints(const GeometryComponent &compo
const int domain_size = component.attribute_domain_size(domain);
/* Hash the reserved name attribute "id" as a (hopefully) stable seed for each point. */
- ReadAttributePtr hash_attribute = component.attribute_try_get_for_read("id", domain);
+ GVArrayPtr hash_attribute = component.attribute_try_get_for_read("id", domain);
Array<uint32_t> hashes(domain_size);
if (hash_attribute) {
BLI_assert(hashes.size() == hash_attribute->size());
- const CPPType &cpp_type = hash_attribute->cpp_type();
- fn::GSpan items = hash_attribute->get_span();
- for (const int i : hashes.index_range()) {
- hashes[i] = cpp_type.hash(items[i]);
- }
+ const CPPType &cpp_type = hash_attribute->type();
+ GVArray_GSpan items{*hash_attribute};
+ parallel_for(hashes.index_range(), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ hashes[i] = cpp_type.hash(items[i]);
+ }
+ });
}
else {
/* If there is no "id" attribute for per-point variation, just create it here. */
@@ -199,12 +209,12 @@ Array<uint32_t> get_geometry_element_ids_as_uints(const GeometryComponent &compo
static AttributeDomain get_result_domain(const GeometryComponent &component,
const GeoNodeExecParams &params,
- StringRef attribute_name)
+ const StringRef name)
{
/* Use the domain of the result attribute if it already exists. */
- ReadAttributePtr result_attribute = component.attribute_try_get_for_read(attribute_name);
- if (result_attribute) {
- return result_attribute->domain();
+ std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(name);
+ if (result_info) {
+ return result_info->domain;
}
/* Otherwise use the input domain chosen in the interface. */
@@ -231,15 +241,13 @@ static void randomize_attribute_on_component(GeometryComponent &component,
const AttributeDomain domain = get_result_domain(component, params, attribute_name);
- OutputAttributePtr attribute = component.attribute_try_get_for_output(
+ OutputAttribute attribute = component.attribute_try_get_for_output(
attribute_name, domain, data_type);
if (!attribute) {
return;
}
- fn::GMutableSpan span = (operation == GEO_NODE_ATTRIBUTE_RANDOMIZE_REPLACE_CREATE) ?
- attribute->get_span_for_write_only() :
- attribute->get_span();
+ GMutableSpan span = attribute.as_span();
Array<uint32_t> hashes = get_geometry_element_ids_as_uints(component, domain);
@@ -272,8 +280,8 @@ static void randomize_attribute_on_component(GeometryComponent &component,
}
}
- attribute.apply_span_and_save();
-} // namespace blender::nodes
+ attribute.save();
+}
static void geo_node_random_attribute_exec(GeoNodeExecParams params)
{
@@ -307,6 +315,14 @@ static void geo_node_random_attribute_exec(GeoNodeExecParams params)
operation,
seed);
}
+ if (geometry_set.has<CurveComponent>()) {
+ randomize_attribute_on_component(geometry_set.get_component_for_write<CurveComponent>(),
+ params,
+ attribute_name,
+ data_type,
+ operation,
+ seed);
+ }
params.set_output("Geometry", geometry_set);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc
index 837f0c3629a..e4f3230ebb9 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc
@@ -70,6 +70,10 @@ static void geo_node_attribute_remove_exec(GeoNodeExecParams params)
remove_attribute(
geometry_set.get_component_for_write<PointCloudComponent>(), params, attribute_names);
}
+ if (geometry_set.has<CurveComponent>()) {
+ remove_attribute(
+ geometry_set.get_component_for_write<CurveComponent>(), params, attribute_names);
+ }
params.set_output("Geometry", geometry_set);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc
index d0b2595b5b9..d6b1ad3e9e0 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc
@@ -15,6 +15,7 @@
*/
#include "BLI_compiler_attrs.h"
+#include "BLI_task.hh"
#include "DNA_texture_types.h"
@@ -29,6 +30,7 @@
static bNodeSocketTemplate geo_node_attribute_sample_texture_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_TEXTURE, N_("Texture")},
{SOCK_STRING, N_("Mapping")},
{SOCK_STRING, N_("Result")},
{-1, ""},
@@ -39,29 +41,22 @@ static bNodeSocketTemplate geo_node_attribute_sample_texture_out[] = {
{-1, ""},
};
-static void geo_node_attribute_sample_texture_layout(uiLayout *layout,
- bContext *C,
- PointerRNA *ptr)
-{
- uiTemplateID(layout, C, ptr, "texture", "texture.new", nullptr, nullptr, 0, ICON_NONE, nullptr);
-}
-
namespace blender::nodes {
static AttributeDomain get_result_domain(const GeometryComponent &component,
- StringRef result_attribute_name,
- StringRef map_attribute_name)
+ const StringRef result_name,
+ const StringRef map_name)
{
/* Use the domain of the result attribute if it already exists. */
- ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_attribute_name);
- if (result_attribute) {
- return result_attribute->domain();
+ std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name);
+ if (result_info) {
+ return result_info->domain;
}
/* Otherwise use the name of the map attribute. */
- ReadAttributePtr map_attribute = component.attribute_try_get_for_read(map_attribute_name);
- if (map_attribute) {
- return map_attribute->domain();
+ std::optional<AttributeMetaData> map_info = component.attribute_get_meta_data(map_name);
+ if (map_info) {
+ return map_info->domain;
}
/* The node won't execute in this case, but we still have to return a value. */
@@ -70,8 +65,7 @@ static AttributeDomain get_result_domain(const GeometryComponent &component,
static void execute_on_component(GeometryComponent &component, const GeoNodeExecParams &params)
{
- const bNode &node = params.node();
- Tex *texture = reinterpret_cast<Tex *>(node.id);
+ Tex *texture = params.get_input<Tex *>("Texture");
if (texture == nullptr) {
return;
}
@@ -85,25 +79,29 @@ static void execute_on_component(GeometryComponent &component, const GeoNodeExec
const AttributeDomain result_domain = get_result_domain(
component, result_attribute_name, mapping_name);
- OutputAttributePtr attribute_out = component.attribute_try_get_for_output(
- result_attribute_name, result_domain, CD_PROP_COLOR);
+ OutputAttribute_Typed<ColorGeometry4f> attribute_out =
+ component.attribute_try_get_for_output_only<ColorGeometry4f>(result_attribute_name,
+ result_domain);
if (!attribute_out) {
return;
}
- Float3ReadAttribute mapping_attribute = component.attribute_get_for_read<float3>(
+ GVArray_Typed<float3> mapping_attribute = component.attribute_get_for_read<float3>(
mapping_name, result_domain, {0, 0, 0});
- MutableSpan<Color4f> colors = attribute_out->get_span<Color4f>();
- for (const int i : IndexRange(mapping_attribute.size())) {
- TexResult texture_result = {0};
- const float3 position = mapping_attribute[i];
- /* For legacy reasons we have to map [0, 1] to [-1, 1] to support uv mappings. */
- const float3 remapped_position = position * 2.0f - float3(1.0f);
- BKE_texture_get_value(nullptr, texture, remapped_position, &texture_result, false);
- colors[i] = {texture_result.tr, texture_result.tg, texture_result.tb, texture_result.ta};
- }
- attribute_out.apply_span_and_save();
+ MutableSpan<ColorGeometry4f> colors = attribute_out.as_span();
+ parallel_for(IndexRange(mapping_attribute.size()), 128, [&](IndexRange range) {
+ for (const int i : range) {
+ TexResult texture_result = {0};
+ const float3 position = mapping_attribute[i];
+ /* For legacy reasons we have to map [0, 1] to [-1, 1] to support uv mappings. */
+ const float3 remapped_position = position * 2.0f - float3(1.0f);
+ BKE_texture_get_value(nullptr, texture, remapped_position, &texture_result, false);
+ colors[i] = {texture_result.tr, texture_result.tg, texture_result.tb, texture_result.ta};
+ }
+ });
+
+ attribute_out.save();
}
static void geo_node_attribute_sample_texture_exec(GeoNodeExecParams params)
@@ -118,6 +116,9 @@ static void geo_node_attribute_sample_texture_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
execute_on_component(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
+ if (geometry_set.has<CurveComponent>()) {
+ execute_on_component(geometry_set.get_component_for_write<CurveComponent>(), params);
+ }
params.set_output("Geometry", geometry_set);
}
@@ -137,6 +138,5 @@ void register_node_type_geo_sample_texture()
node_type_socket_templates(
&ntype, geo_node_attribute_sample_texture_in, geo_node_attribute_sample_texture_out);
ntype.geometry_node_execute = blender::nodes::geo_node_attribute_sample_texture_exec;
- ntype.draw_buttons = geo_node_attribute_sample_texture_layout;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_separate_xyz.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_separate_xyz.cc
index 55b933e8582..137a72bb707 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_separate_xyz.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_separate_xyz.cc
@@ -14,13 +14,11 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "DNA_material_types.h"
-
-#include "node_geometry_util.hh"
-
#include "UI_interface.h"
#include "UI_resources.h"
+#include "node_geometry_util.hh"
+
static bNodeSocketTemplate geo_node_attribute_separate_xyz_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_STRING, N_("Vector")},
@@ -73,23 +71,23 @@ static void extract_input(const int index, const Span<float3> &input, MutableSpa
static AttributeDomain get_result_domain(const GeometryComponent &component,
const GeoNodeExecParams &params,
- StringRef result_name_x,
- StringRef result_name_y,
- StringRef result_name_z)
+ const StringRef name_x,
+ const StringRef name_y,
+ const StringRef name_z)
{
/* Use the highest priority domain from any existing attribute outputs. */
Vector<AttributeDomain, 3> output_domains;
- ReadAttributePtr attribute_x = component.attribute_try_get_for_read(result_name_x);
- ReadAttributePtr attribute_y = component.attribute_try_get_for_read(result_name_y);
- ReadAttributePtr attribute_z = component.attribute_try_get_for_read(result_name_z);
- if (attribute_x) {
- output_domains.append(attribute_x->domain());
+ std::optional<AttributeMetaData> info_x = component.attribute_get_meta_data(name_x);
+ std::optional<AttributeMetaData> info_y = component.attribute_get_meta_data(name_y);
+ std::optional<AttributeMetaData> info_z = component.attribute_get_meta_data(name_z);
+ if (info_x) {
+ output_domains.append(info_x->domain);
}
- if (attribute_y) {
- output_domains.append(attribute_y->domain());
+ if (info_y) {
+ output_domains.append(info_y->domain);
}
- if (attribute_z) {
- output_domains.append(attribute_z->domain());
+ if (info_z) {
+ output_domains.append(info_z->domain);
}
if (output_domains.size() > 0) {
return bke::attribute_domain_highest_priority(output_domains);
@@ -109,37 +107,32 @@ static void separate_attribute(GeometryComponent &component, const GeoNodeExecPa
}
/* The node is only for float3 to float conversions. */
- const CustomDataType input_type = CD_PROP_FLOAT3;
- const CustomDataType result_type = CD_PROP_FLOAT;
const AttributeDomain result_domain = get_result_domain(
component, params, result_name_x, result_name_y, result_name_z);
- ReadAttributePtr attribute_input = params.get_input_attribute(
- "Vector", component, result_domain, input_type, nullptr);
- if (!attribute_input) {
- return;
- }
- const Span<float3> input_span = attribute_input->get_span<float3>();
+ GVArray_Typed<float3> attribute_input = params.get_input_attribute<float3>(
+ "Vector", component, result_domain, {0, 0, 0});
+ VArray_Span<float3> input_span{*attribute_input};
- OutputAttributePtr attribute_result_x = component.attribute_try_get_for_output(
- result_name_x, result_domain, result_type);
- OutputAttributePtr attribute_result_y = component.attribute_try_get_for_output(
- result_name_y, result_domain, result_type);
- OutputAttributePtr attribute_result_z = component.attribute_try_get_for_output(
- result_name_z, result_domain, result_type);
+ OutputAttribute_Typed<float> attribute_result_x =
+ component.attribute_try_get_for_output_only<float>(result_name_x, result_domain);
+ OutputAttribute_Typed<float> attribute_result_y =
+ component.attribute_try_get_for_output_only<float>(result_name_y, result_domain);
+ OutputAttribute_Typed<float> attribute_result_z =
+ component.attribute_try_get_for_output_only<float>(result_name_z, result_domain);
/* Only extract the components for the outputs with a given attribute. */
if (attribute_result_x) {
- extract_input(0, input_span, attribute_result_x->get_span_for_write_only<float>());
- attribute_result_x.apply_span_and_save();
+ extract_input(0, input_span, attribute_result_x.as_span());
+ attribute_result_x.save();
}
if (attribute_result_y) {
- extract_input(1, input_span, attribute_result_y->get_span_for_write_only<float>());
- attribute_result_y.apply_span_and_save();
+ extract_input(1, input_span, attribute_result_y.as_span());
+ attribute_result_y.save();
}
if (attribute_result_z) {
- extract_input(2, input_span, attribute_result_z->get_span_for_write_only<float>());
- attribute_result_z.apply_span_and_save();
+ extract_input(2, input_span, attribute_result_z.as_span());
+ attribute_result_z.save();
}
}
@@ -155,6 +148,9 @@ static void geo_node_attribute_separate_xyz_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
separate_attribute(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
+ if (geometry_set.has<CurveComponent>()) {
+ separate_attribute(geometry_set.get_component_for_write<CurveComponent>(), params);
+ }
params.set_output("Geometry", geometry_set);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc
new file mode 100644
index 00000000000..4b677dc5c82
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc
@@ -0,0 +1,597 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BLI_kdopbvh.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_pointcloud_types.h"
+
+#include "BKE_bvhutils.h"
+#include "BKE_mesh_runtime.h"
+#include "BKE_mesh_sample.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_attribute_transfer_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_GEOMETRY, N_("Source Geometry")},
+ {SOCK_STRING, N_("Source")},
+ {SOCK_STRING, N_("Destination")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_attribute_transfer_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+static void geo_node_attribute_transfer_layout(uiLayout *layout,
+ bContext *UNUSED(C),
+ PointerRNA *ptr)
+{
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
+ uiItemR(layout, ptr, "domain", 0, IFACE_("Domain"), ICON_NONE);
+ uiItemR(layout, ptr, "mapping", 0, IFACE_("Mapping"), ICON_NONE);
+}
+
+namespace blender::nodes {
+
+static void geo_node_attribute_transfer_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometryAttributeTransfer *data = (NodeGeometryAttributeTransfer *)MEM_callocN(
+ sizeof(NodeGeometryAttributeTransfer), __func__);
+ data->domain = ATTR_DOMAIN_AUTO;
+ node->storage = data;
+}
+
+static void get_result_domain_and_data_type(const GeometrySet &src_geometry,
+ const GeometryComponent &dst_component,
+ const StringRef attribute_name,
+ CustomDataType *r_data_type,
+ AttributeDomain *r_domain)
+{
+ Vector<CustomDataType> data_types;
+ Vector<AttributeDomain> domains;
+
+ const PointCloudComponent *pointcloud_component =
+ src_geometry.get_component_for_read<PointCloudComponent>();
+ if (pointcloud_component != nullptr) {
+ std::optional<AttributeMetaData> meta_data = pointcloud_component->attribute_get_meta_data(
+ attribute_name);
+ if (meta_data.has_value()) {
+ data_types.append(meta_data->data_type);
+ domains.append(meta_data->domain);
+ }
+ }
+
+ const MeshComponent *mesh_component = src_geometry.get_component_for_read<MeshComponent>();
+ if (mesh_component != nullptr) {
+ std::optional<AttributeMetaData> meta_data = mesh_component->attribute_get_meta_data(
+ attribute_name);
+ if (meta_data.has_value()) {
+ data_types.append(meta_data->data_type);
+ domains.append(meta_data->domain);
+ }
+ }
+
+ *r_data_type = bke::attribute_data_type_highest_complexity(data_types);
+
+ if (dst_component.type() == GEO_COMPONENT_TYPE_POINT_CLOUD) {
+ *r_domain = ATTR_DOMAIN_POINT;
+ }
+ else {
+ *r_domain = bke::attribute_domain_highest_priority(domains);
+ }
+}
+
+static Span<MLoopTri> get_mesh_looptris(const Mesh &mesh)
+{
+ /* This only updates a cache and can be considered to be logically const. */
+ const MLoopTri *looptris = BKE_mesh_runtime_looptri_ensure(const_cast<Mesh *>(&mesh));
+ const int looptris_len = BKE_mesh_runtime_looptri_len(&mesh);
+ return {looptris, looptris_len};
+}
+
+static void get_closest_in_bvhtree(BVHTreeFromMesh &tree_data,
+ const VArray<float3> &positions,
+ const MutableSpan<int> r_indices,
+ const MutableSpan<float> r_distances_sq,
+ const MutableSpan<float3> r_positions)
+{
+ BLI_assert(positions.size() == r_indices.size() || r_indices.is_empty());
+ BLI_assert(positions.size() == r_distances_sq.size() || r_distances_sq.is_empty());
+ BLI_assert(positions.size() == r_positions.size() || r_positions.is_empty());
+
+ for (const int i : positions.index_range()) {
+ 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 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 : positions.index_range()) {
+ 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;
+ 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 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, const_cast<Mesh *>(&mesh), BVHTREE_FROM_VERTS, 2);
+ get_closest_in_bvhtree(tree_data, positions, 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 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, const_cast<Mesh *>(&mesh), BVHTREE_FROM_EDGES, 2);
+ get_closest_in_bvhtree(tree_data, positions, 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 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, const_cast<Mesh *>(&mesh), BVHTREE_FROM_LOOPTRI, 2);
+ get_closest_in_bvhtree(tree_data, positions, 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 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, looptri_indices, r_distances_sq, r_positions);
+
+ Span<MLoopTri> looptris = get_mesh_looptris(mesh);
+ for (const int i : positions.index_range()) {
+ 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 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, poly_indices, {}, {});
+
+ for (const int i : positions.index_range()) {
+ 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 = float3::distance_squared(position, 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 void get_barycentric_coords(const Mesh &mesh,
+ const Span<int> looptri_indices,
+ const Span<float3> positions,
+ const MutableSpan<float3> r_bary_coords)
+{
+ BLI_assert(r_bary_coords.size() == positions.size());
+ BLI_assert(r_bary_coords.size() == looptri_indices.size());
+
+ Span<MLoopTri> looptris = get_mesh_looptris(mesh);
+
+ for (const int i : r_bary_coords.index_range()) {
+ const int looptri_index = looptri_indices[i];
+ const MLoopTri &looptri = looptris[looptri_index];
+
+ 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;
+
+ interp_weights_tri_v3(r_bary_coords[i],
+ mesh.mvert[v0_index].co,
+ mesh.mvert[v1_index].co,
+ mesh.mvert[v2_index].co,
+ positions[i]);
+ }
+}
+
+static void transfer_attribute_nearest_face_interpolated(const GeometrySet &src_geometry,
+ GeometryComponent &dst_component,
+ const VArray<float3> &dst_positions,
+ const AttributeDomain dst_domain,
+ const CustomDataType data_type,
+ const StringRef src_name,
+ const StringRef dst_name)
+{
+ const int tot_samples = dst_positions.size();
+ const MeshComponent *component = src_geometry.get_component_for_read<MeshComponent>();
+ if (component == nullptr) {
+ return;
+ }
+ const Mesh *mesh = component->get_for_read();
+ if (mesh == nullptr) {
+ return;
+ }
+ if (mesh->totpoly == 0) {
+ return;
+ }
+ ReadAttributeLookup src_attribute = component->attribute_try_get_for_read(src_name, data_type);
+ if (!src_attribute) {
+ return;
+ }
+
+ /* Find closest points on the mesh surface. */
+ Array<int> looptri_indices(tot_samples);
+ Array<float3> positions(tot_samples);
+ get_closest_mesh_looptris(*mesh, dst_positions, looptri_indices, {}, positions);
+
+ OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
+ dst_name, dst_domain, data_type);
+ if (!dst_attribute) {
+ return;
+ }
+ GMutableSpan dst_span = dst_attribute.as_span();
+ Array<float3> bary_coords;
+
+ /* Compute barycentric coordinates only when they are needed. */
+ if (src_attribute.domain != ATTR_DOMAIN_FACE) {
+ bary_coords.reinitialize(tot_samples);
+ get_barycentric_coords(*mesh, looptri_indices, positions, bary_coords);
+ }
+ /* Interpolate the source attribute on the surface. */
+ switch (src_attribute.domain) {
+ case ATTR_DOMAIN_POINT: {
+ bke::mesh_surface_sample::sample_point_attribute(
+ *mesh, looptri_indices, bary_coords, *src_attribute.varray, dst_span);
+ break;
+ }
+ case ATTR_DOMAIN_FACE: {
+ bke::mesh_surface_sample::sample_face_attribute(
+ *mesh, looptri_indices, *src_attribute.varray, dst_span);
+ break;
+ }
+ case ATTR_DOMAIN_CORNER: {
+ bke::mesh_surface_sample::sample_corner_attribute(
+ *mesh, looptri_indices, bary_coords, *src_attribute.varray, dst_span);
+ break;
+ }
+ case ATTR_DOMAIN_EDGE: {
+ /* Not yet supported. */
+ break;
+ }
+ default: {
+ BLI_assert_unreachable();
+ break;
+ }
+ }
+ dst_attribute.save();
+}
+
+static void transfer_attribute_nearest(const GeometrySet &src_geometry,
+ GeometryComponent &dst_component,
+ const VArray<float3> &dst_positions,
+ const AttributeDomain dst_domain,
+ const CustomDataType data_type,
+ const StringRef src_name,
+ const StringRef dst_name)
+{
+ const CPPType &type = *bke::custom_data_type_to_cpp_type(data_type);
+
+ /* Get pointcloud data from geometry. */
+ const PointCloudComponent *pointcloud_component =
+ src_geometry.get_component_for_read<PointCloudComponent>();
+ const PointCloud *pointcloud = pointcloud_component ? pointcloud_component->get_for_read() :
+ nullptr;
+
+ /* Get mesh data from geometry. */
+ const MeshComponent *mesh_component = src_geometry.get_component_for_read<MeshComponent>();
+ const Mesh *mesh = mesh_component ? mesh_component->get_for_read() : nullptr;
+
+ const int tot_samples = dst_positions.size();
+
+ Array<int> pointcloud_indices;
+ Array<float> pointcloud_distances_sq;
+ bool use_pointcloud = false;
+
+ /* 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_sq;
+ bool use_mesh = false;
+
+ /* If there is a pointcloud, find the closest points. */
+ if (pointcloud != nullptr && pointcloud->totpoint > 0) {
+ if (pointcloud_component->attribute_exists(src_name)) {
+ use_pointcloud = true;
+ pointcloud_indices.reinitialize(tot_samples);
+ pointcloud_distances_sq.reinitialize(tot_samples);
+ get_closest_pointcloud_points(
+ *pointcloud, dst_positions, pointcloud_indices, pointcloud_distances_sq);
+ }
+ }
+
+ /* If there is a mesh, find the closest mesh elements. */
+ if (mesh != nullptr) {
+ ReadAttributeLookup src_attribute = mesh_component->attribute_try_get_for_read(src_name);
+ if (src_attribute) {
+ switch (src_attribute.domain) {
+ case ATTR_DOMAIN_POINT: {
+ if (mesh->totvert > 0) {
+ use_mesh = true;
+ mesh_indices.reinitialize(tot_samples);
+ mesh_distances_sq.reinitialize(tot_samples);
+ get_closest_mesh_points(*mesh, dst_positions, mesh_indices, mesh_distances_sq, {});
+ }
+ break;
+ }
+ case ATTR_DOMAIN_EDGE: {
+ if (mesh->totedge > 0) {
+ use_mesh = true;
+ mesh_indices.reinitialize(tot_samples);
+ mesh_distances_sq.reinitialize(tot_samples);
+ get_closest_mesh_edges(*mesh, dst_positions, mesh_indices, mesh_distances_sq, {});
+ }
+ break;
+ }
+ case ATTR_DOMAIN_FACE: {
+ if (mesh->totpoly > 0) {
+ use_mesh = true;
+ mesh_indices.reinitialize(tot_samples);
+ mesh_distances_sq.reinitialize(tot_samples);
+ get_closest_mesh_polygons(*mesh, dst_positions, mesh_indices, mesh_distances_sq, {});
+ }
+ break;
+ }
+ case ATTR_DOMAIN_CORNER: {
+ use_mesh = true;
+ mesh_indices.reinitialize(tot_samples);
+ mesh_distances_sq.reinitialize(tot_samples);
+ get_closest_mesh_corners(*mesh, dst_positions, mesh_indices, mesh_distances_sq, {});
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+ }
+ }
+
+ if (!use_pointcloud && !use_mesh) {
+ return;
+ }
+
+ OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
+ dst_name, dst_domain, data_type);
+ if (!dst_attribute) {
+ return;
+ }
+
+ /* Create a buffer for intermediate values. */
+ BUFFER_FOR_CPP_TYPE_VALUE(type, buffer);
+
+ if (use_mesh && use_pointcloud) {
+ /* When there is a mesh and a pointcloud, we still have to check whether a pointcloud point or
+ * a mesh element is closer to every point. */
+ ReadAttributeLookup pointcloud_src_attribute =
+ pointcloud_component->attribute_try_get_for_read(src_name, data_type);
+ ReadAttributeLookup mesh_src_attribute = mesh_component->attribute_try_get_for_read(src_name,
+ data_type);
+ for (const int i : IndexRange(tot_samples)) {
+ if (pointcloud_distances_sq[i] < mesh_distances_sq[i]) {
+ /* Pointcloud point is closer. */
+ const int index = pointcloud_indices[i];
+ pointcloud_src_attribute.varray->get(index, buffer);
+ dst_attribute->set_by_relocate(i, buffer);
+ }
+ else {
+ /* Mesh element is closer. */
+ const int index = mesh_indices[i];
+ mesh_src_attribute.varray->get(index, buffer);
+ dst_attribute->set_by_relocate(i, buffer);
+ }
+ }
+ }
+ else if (use_pointcloud) {
+ /* The source geometry only has a pointcloud. */
+ ReadAttributeLookup src_attribute = pointcloud_component->attribute_try_get_for_read(
+ src_name, data_type);
+ for (const int i : IndexRange(tot_samples)) {
+ const int index = pointcloud_indices[i];
+ src_attribute.varray->get(index, buffer);
+ dst_attribute->set_by_relocate(i, buffer);
+ }
+ }
+ else if (use_mesh) {
+ /* The source geometry only has a mesh. */
+ ReadAttributeLookup src_attribute = mesh_component->attribute_try_get_for_read(src_name,
+ data_type);
+ for (const int i : IndexRange(tot_samples)) {
+ const int index = mesh_indices[i];
+ src_attribute.varray->get(index, buffer);
+ dst_attribute->set_by_relocate(i, buffer);
+ }
+ }
+
+ dst_attribute.save();
+}
+
+static void transfer_attribute(const GeoNodeExecParams &params,
+ const GeometrySet &src_geometry,
+ GeometryComponent &dst_component,
+ const StringRef src_name,
+ const StringRef dst_name)
+{
+ const NodeGeometryAttributeTransfer &storage =
+ *(const NodeGeometryAttributeTransfer *)params.node().storage;
+ const GeometryNodeAttributeTransferMapMode mapping = (GeometryNodeAttributeTransferMapMode)
+ storage.mapping;
+ const AttributeDomain input_domain = (AttributeDomain)storage.domain;
+
+ CustomDataType data_type;
+ AttributeDomain auto_domain;
+ get_result_domain_and_data_type(src_geometry, dst_component, src_name, &data_type, &auto_domain);
+ const AttributeDomain dst_domain = (input_domain == ATTR_DOMAIN_AUTO) ? auto_domain :
+ input_domain;
+
+ GVArray_Typed<float3> dst_positions = dst_component.attribute_get_for_read<float3>(
+ "position", dst_domain, {0, 0, 0});
+
+ switch (mapping) {
+ case GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED: {
+ transfer_attribute_nearest_face_interpolated(
+ src_geometry, dst_component, dst_positions, dst_domain, data_type, src_name, dst_name);
+ break;
+ }
+ case GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST: {
+ transfer_attribute_nearest(
+ src_geometry, dst_component, dst_positions, dst_domain, data_type, src_name, dst_name);
+ break;
+ }
+ }
+}
+
+static void geo_node_attribute_transfer_exec(GeoNodeExecParams params)
+{
+ GeometrySet dst_geometry_set = params.extract_input<GeometrySet>("Geometry");
+ GeometrySet src_geometry_set = params.extract_input<GeometrySet>("Source Geometry");
+ const std::string src_attribute_name = params.extract_input<std::string>("Source");
+ const std::string dst_attribute_name = params.extract_input<std::string>("Destination");
+
+ if (src_attribute_name.empty() || dst_attribute_name.empty()) {
+ params.set_output("Geometry", dst_geometry_set);
+ return;
+ }
+
+ dst_geometry_set = bke::geometry_set_realize_instances(dst_geometry_set);
+ src_geometry_set = bke::geometry_set_realize_instances(src_geometry_set);
+
+ if (dst_geometry_set.has<MeshComponent>()) {
+ transfer_attribute(params,
+ src_geometry_set,
+ dst_geometry_set.get_component_for_write<MeshComponent>(),
+ src_attribute_name,
+ dst_attribute_name);
+ }
+ if (dst_geometry_set.has<PointCloudComponent>()) {
+ transfer_attribute(params,
+ src_geometry_set,
+ dst_geometry_set.get_component_for_write<PointCloudComponent>(),
+ src_attribute_name,
+ dst_attribute_name);
+ }
+
+ params.set_output("Geometry", dst_geometry_set);
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_attribute_transfer()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_ATTRIBUTE_TRANSFER, "Attribute Transfer", NODE_CLASS_ATTRIBUTE, 0);
+ node_type_socket_templates(
+ &ntype, geo_node_attribute_transfer_in, geo_node_attribute_transfer_out);
+ node_type_init(&ntype, blender::nodes::geo_node_attribute_transfer_init);
+ node_type_storage(&ntype,
+ "NodeGeometryAttributeTransfer",
+ node_free_standard_storage,
+ node_copy_standard_storage);
+ ntype.geometry_node_execute = blender::nodes::geo_node_attribute_transfer_exec;
+ ntype.draw_buttons = geo_node_attribute_transfer_layout;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc
index ee2fd763ead..5a5fc099779 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc
@@ -14,23 +14,16 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "node_geometry_util.hh"
-
-#include "BKE_attribute.h"
-#include "BKE_attribute_access.hh"
-
-#include "BLI_array.hh"
#include "BLI_math_base_safe.h"
-#include "BLI_rand.hh"
-
-#include "DNA_mesh_types.h"
-#include "DNA_pointcloud_types.h"
+#include "BLI_task.hh"
#include "UI_interface.h"
#include "UI_resources.h"
#include "NOD_math_functions.hh"
+#include "node_geometry_util.hh"
+
static bNodeSocketTemplate geo_node_attribute_vector_math_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_STRING, N_("A")},
@@ -66,8 +59,11 @@ static bool operation_use_input_b(const NodeVectorMathOperation operation)
static bool operation_use_input_c(const NodeVectorMathOperation operation)
{
- return ELEM(
- operation, NODE_VECTOR_MATH_WRAP, NODE_VECTOR_MATH_REFRACT, NODE_VECTOR_MATH_FACEFORWARD);
+ return ELEM(operation,
+ NODE_VECTOR_MATH_WRAP,
+ NODE_VECTOR_MATH_REFRACT,
+ NODE_VECTOR_MATH_FACEFORWARD,
+ NODE_VECTOR_MATH_MULTIPLY_ADD);
}
static CustomDataType operation_get_read_type_b(const NodeVectorMathOperation operation)
@@ -129,6 +125,7 @@ static CustomDataType operation_get_result_type(const NodeVectorMathOperation op
case NODE_VECTOR_MATH_TANGENT:
case NODE_VECTOR_MATH_REFRACT:
case NODE_VECTOR_MATH_FACEFORWARD:
+ case NODE_VECTOR_MATH_MULTIPLY_ADD:
return CD_PROP_FLOAT3;
case NODE_VECTOR_MATH_DOT_PRODUCT:
case NODE_VECTOR_MATH_DISTANCE:
@@ -182,196 +179,210 @@ static void geo_node_attribute_vector_math_update(bNodeTree *UNUSED(ntree), bNod
operation_use_input_c(operation));
}
-static void do_math_operation_fl3_fl3_to_fl3(const Float3ReadAttribute &input_a,
- const Float3ReadAttribute &input_b,
- Float3WriteAttribute result,
+static void do_math_operation_fl3_fl3_to_fl3(const VArray<float3> &input_a,
+ const VArray<float3> &input_b,
+ VMutableArray<float3> &result,
const NodeVectorMathOperation operation)
{
const int size = input_a.size();
- Span<float3> span_a = input_a.get_span();
- Span<float3> span_b = input_b.get_span();
- MutableSpan<float3> span_result = result.get_span_for_write_only();
+ VArray_Span<float3> span_a{input_a};
+ VArray_Span<float3> span_b{input_b};
+ VMutableArray_Span<float3> span_result{result, false};
bool success = try_dispatch_float_math_fl3_fl3_to_fl3(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- for (const int i : IndexRange(size)) {
- const float3 a = span_a[i];
- const float3 b = span_b[i];
- const float3 out = math_function(a, b);
- span_result[i] = out;
- }
+ parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const float3 a = span_a[i];
+ const float3 b = span_b[i];
+ const float3 out = math_function(a, b);
+ span_result[i] = out;
+ }
+ });
});
- result.apply_span();
+ span_result.save();
/* The operation is not supported by this node currently. */
BLI_assert(success);
UNUSED_VARS_NDEBUG(success);
}
-static void do_math_operation_fl3_fl3_fl3_to_fl3(const Float3ReadAttribute &input_a,
- const Float3ReadAttribute &input_b,
- const Float3ReadAttribute &input_c,
- Float3WriteAttribute result,
+static void do_math_operation_fl3_fl3_fl3_to_fl3(const VArray<float3> &input_a,
+ const VArray<float3> &input_b,
+ const VArray<float3> &input_c,
+ VMutableArray<float3> &result,
const NodeVectorMathOperation operation)
{
const int size = input_a.size();
- Span<float3> span_a = input_a.get_span();
- Span<float3> span_b = input_b.get_span();
- Span<float3> span_c = input_c.get_span();
- MutableSpan<float3> span_result = result.get_span_for_write_only();
+ VArray_Span<float3> span_a{input_a};
+ VArray_Span<float3> span_b{input_b};
+ VArray_Span<float3> span_c{input_c};
+ VMutableArray_Span<float3> span_result{result};
bool success = try_dispatch_float_math_fl3_fl3_fl3_to_fl3(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- for (const int i : IndexRange(size)) {
- const float3 a = span_a[i];
- const float3 b = span_b[i];
- const float3 c = span_c[i];
- const float3 out = math_function(a, b, c);
- span_result[i] = out;
- }
+ parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const float3 a = span_a[i];
+ const float3 b = span_b[i];
+ const float3 c = span_c[i];
+ const float3 out = math_function(a, b, c);
+ span_result[i] = out;
+ }
+ });
});
- result.apply_span();
+ span_result.save();
/* The operation is not supported by this node currently. */
BLI_assert(success);
UNUSED_VARS_NDEBUG(success);
}
-static void do_math_operation_fl3_fl3_fl_to_fl3(const Float3ReadAttribute &input_a,
- const Float3ReadAttribute &input_b,
- const FloatReadAttribute &input_c,
- Float3WriteAttribute result,
+static void do_math_operation_fl3_fl3_fl_to_fl3(const VArray<float3> &input_a,
+ const VArray<float3> &input_b,
+ const VArray<float> &input_c,
+ VMutableArray<float3> &result,
const NodeVectorMathOperation operation)
{
const int size = input_a.size();
- Span<float3> span_a = input_a.get_span();
- Span<float3> span_b = input_b.get_span();
- Span<float> span_c = input_c.get_span();
- MutableSpan<float3> span_result = result.get_span_for_write_only();
+ VArray_Span<float3> span_a{input_a};
+ VArray_Span<float3> span_b{input_b};
+ VArray_Span<float> span_c{input_c};
+ VMutableArray_Span<float3> span_result{result, false};
bool success = try_dispatch_float_math_fl3_fl3_fl_to_fl3(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- for (const int i : IndexRange(size)) {
- const float3 a = span_a[i];
- const float3 b = span_b[i];
- const float c = span_c[i];
- const float3 out = math_function(a, b, c);
- span_result[i] = out;
- }
+ parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const float3 a = span_a[i];
+ const float3 b = span_b[i];
+ const float c = span_c[i];
+ const float3 out = math_function(a, b, c);
+ span_result[i] = out;
+ }
+ });
});
- result.apply_span();
+ span_result.save();
/* The operation is not supported by this node currently. */
BLI_assert(success);
UNUSED_VARS_NDEBUG(success);
}
-static void do_math_operation_fl3_fl3_to_fl(const Float3ReadAttribute &input_a,
- const Float3ReadAttribute &input_b,
- FloatWriteAttribute result,
+static void do_math_operation_fl3_fl3_to_fl(const VArray<float3> &input_a,
+ const VArray<float3> &input_b,
+ VMutableArray<float> &result,
const NodeVectorMathOperation operation)
{
const int size = input_a.size();
- Span<float3> span_a = input_a.get_span();
- Span<float3> span_b = input_b.get_span();
- MutableSpan<float> span_result = result.get_span_for_write_only();
+ VArray_Span<float3> span_a{input_a};
+ VArray_Span<float3> span_b{input_b};
+ VMutableArray_Span<float> span_result{result, false};
bool success = try_dispatch_float_math_fl3_fl3_to_fl(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- for (const int i : IndexRange(size)) {
- const float3 a = span_a[i];
- const float3 b = span_b[i];
- const float out = math_function(a, b);
- span_result[i] = out;
- }
+ parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const float3 a = span_a[i];
+ const float3 b = span_b[i];
+ const float out = math_function(a, b);
+ span_result[i] = out;
+ }
+ });
});
- result.apply_span();
+ span_result.save();
/* The operation is not supported by this node currently. */
BLI_assert(success);
UNUSED_VARS_NDEBUG(success);
}
-static void do_math_operation_fl3_fl_to_fl3(const Float3ReadAttribute &input_a,
- const FloatReadAttribute &input_b,
- Float3WriteAttribute result,
+static void do_math_operation_fl3_fl_to_fl3(const VArray<float3> &input_a,
+ const VArray<float> &input_b,
+ VMutableArray<float3> &result,
const NodeVectorMathOperation operation)
{
const int size = input_a.size();
- Span<float3> span_a = input_a.get_span();
- Span<float> span_b = input_b.get_span();
- MutableSpan<float3> span_result = result.get_span_for_write_only();
+ VArray_Span<float3> span_a{input_a};
+ VArray_Span<float> span_b{input_b};
+ VMutableArray_Span<float3> span_result{result, false};
bool success = try_dispatch_float_math_fl3_fl_to_fl3(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- for (const int i : IndexRange(size)) {
- const float3 a = span_a[i];
- const float b = span_b[i];
- const float3 out = math_function(a, b);
- span_result[i] = out;
- }
+ parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const float3 a = span_a[i];
+ const float b = span_b[i];
+ const float3 out = math_function(a, b);
+ span_result[i] = out;
+ }
+ });
});
- result.apply_span();
+ span_result.save();
/* The operation is not supported by this node currently. */
BLI_assert(success);
UNUSED_VARS_NDEBUG(success);
}
-static void do_math_operation_fl3_to_fl3(const Float3ReadAttribute &input_a,
- Float3WriteAttribute result,
+static void do_math_operation_fl3_to_fl3(const VArray<float3> &input_a,
+ VMutableArray<float3> &result,
const NodeVectorMathOperation operation)
{
const int size = input_a.size();
- Span<float3> span_a = input_a.get_span();
- MutableSpan<float3> span_result = result.get_span_for_write_only();
+ VArray_Span<float3> span_a{input_a};
+ VMutableArray_Span<float3> span_result{result, false};
bool success = try_dispatch_float_math_fl3_to_fl3(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- for (const int i : IndexRange(size)) {
- const float3 in = span_a[i];
- const float3 out = math_function(in);
- span_result[i] = out;
- }
+ parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const float3 in = span_a[i];
+ const float3 out = math_function(in);
+ span_result[i] = out;
+ }
+ });
});
- result.apply_span();
+ span_result.save();
/* The operation is not supported by this node currently. */
BLI_assert(success);
UNUSED_VARS_NDEBUG(success);
}
-static void do_math_operation_fl3_to_fl(const Float3ReadAttribute &input_a,
- FloatWriteAttribute result,
+static void do_math_operation_fl3_to_fl(const VArray<float3> &input_a,
+ VMutableArray<float> &result,
const NodeVectorMathOperation operation)
{
const int size = input_a.size();
- Span<float3> span_a = input_a.get_span();
- MutableSpan<float> span_result = result.get_span_for_write_only();
+ VArray_Span<float3> span_a{input_a};
+ VMutableArray_Span<float> span_result{result, false};
bool success = try_dispatch_float_math_fl3_to_fl(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- for (const int i : IndexRange(size)) {
- const float3 in = span_a[i];
- const float out = math_function(in);
- span_result[i] = out;
- }
+ parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const float3 in = span_a[i];
+ const float out = math_function(in);
+ span_result[i] = out;
+ }
+ });
});
- result.apply_span();
+ span_result.save();
/* The operation is not supported by this node currently. */
BLI_assert(success);
@@ -384,9 +395,9 @@ static AttributeDomain get_result_domain(const GeometryComponent &component,
StringRef result_name)
{
/* Use the domain of the result attribute if it already exists. */
- ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name);
+ ReadAttributeLookup result_attribute = component.attribute_try_get_for_read(result_name);
if (result_attribute) {
- return result_attribute->domain();
+ return result_attribute.domain;
}
/* Otherwise use the highest priority domain from existing input attributes, or the default. */
@@ -420,13 +431,13 @@ static void attribute_vector_math_calc(GeometryComponent &component,
const AttributeDomain result_domain = get_result_domain(
component, params, operation, result_name);
- ReadAttributePtr attribute_a = params.get_input_attribute(
+ GVArrayPtr attribute_a = params.get_input_attribute(
"A", component, result_domain, read_type_a, nullptr);
if (!attribute_a) {
return;
}
- ReadAttributePtr attribute_b;
- ReadAttributePtr attribute_c;
+ GVArrayPtr attribute_b;
+ GVArrayPtr attribute_c;
if (use_input_b) {
attribute_b = params.get_input_attribute("B", component, result_domain, read_type_b, nullptr);
if (!attribute_b) {
@@ -441,7 +452,7 @@ static void attribute_vector_math_calc(GeometryComponent &component,
}
/* Get result attribute first, in case it has to overwrite one of the existing attributes. */
- OutputAttributePtr attribute_result = component.attribute_try_get_for_output(
+ OutputAttribute attribute_result = component.attribute_try_get_for_output_only(
result_name, result_domain, result_type);
if (!attribute_result) {
return;
@@ -459,17 +470,27 @@ static void attribute_vector_math_calc(GeometryComponent &component,
case NODE_VECTOR_MATH_MODULO:
case NODE_VECTOR_MATH_MINIMUM:
case NODE_VECTOR_MATH_MAXIMUM:
- do_math_operation_fl3_fl3_to_fl3(*attribute_a, *attribute_b, *attribute_result, operation);
+ do_math_operation_fl3_fl3_to_fl3(attribute_a->typed<float3>(),
+ attribute_b->typed<float3>(),
+ attribute_result->typed<float3>(),
+ operation);
break;
case NODE_VECTOR_MATH_DOT_PRODUCT:
case NODE_VECTOR_MATH_DISTANCE:
- do_math_operation_fl3_fl3_to_fl(*attribute_a, *attribute_b, *attribute_result, operation);
+ do_math_operation_fl3_fl3_to_fl(attribute_a->typed<float3>(),
+ attribute_b->typed<float3>(),
+ attribute_result->typed<float>(),
+ operation);
break;
case NODE_VECTOR_MATH_LENGTH:
- do_math_operation_fl3_to_fl(*attribute_a, *attribute_result, operation);
+ do_math_operation_fl3_to_fl(
+ attribute_a->typed<float3>(), attribute_result->typed<float>(), operation);
break;
case NODE_VECTOR_MATH_SCALE:
- do_math_operation_fl3_fl_to_fl3(*attribute_a, *attribute_b, *attribute_result, operation);
+ do_math_operation_fl3_fl_to_fl3(attribute_a->typed<float3>(),
+ attribute_b->typed<float>(),
+ attribute_result->typed<float3>(),
+ operation);
break;
case NODE_VECTOR_MATH_NORMALIZE:
case NODE_VECTOR_MATH_FLOOR:
@@ -479,16 +500,24 @@ static void attribute_vector_math_calc(GeometryComponent &component,
case NODE_VECTOR_MATH_SINE:
case NODE_VECTOR_MATH_COSINE:
case NODE_VECTOR_MATH_TANGENT:
- do_math_operation_fl3_to_fl3(*attribute_a, *attribute_result, operation);
+ do_math_operation_fl3_to_fl3(
+ attribute_a->typed<float3>(), attribute_result->typed<float3>(), operation);
break;
case NODE_VECTOR_MATH_WRAP:
case NODE_VECTOR_MATH_FACEFORWARD:
- do_math_operation_fl3_fl3_fl3_to_fl3(
- *attribute_a, *attribute_b, *attribute_c, *attribute_result, operation);
+ case NODE_VECTOR_MATH_MULTIPLY_ADD:
+ do_math_operation_fl3_fl3_fl3_to_fl3(attribute_a->typed<float3>(),
+ attribute_b->typed<float3>(),
+ attribute_c->typed<float3>(),
+ attribute_result->typed<float3>(),
+ operation);
break;
case NODE_VECTOR_MATH_REFRACT:
- do_math_operation_fl3_fl3_fl_to_fl3(
- *attribute_a, *attribute_b, *attribute_c, *attribute_result, operation);
+ do_math_operation_fl3_fl3_fl_to_fl3(attribute_a->typed<float3>(),
+ attribute_b->typed<float3>(),
+ attribute_c->typed<float>(),
+ attribute_result->typed<float3>(),
+ operation);
break;
}
attribute_result.save();
@@ -507,6 +536,9 @@ static void geo_node_attribute_vector_math_exec(GeoNodeExecParams params)
attribute_vector_math_calc(geometry_set.get_component_for_write<PointCloudComponent>(),
params);
}
+ if (geometry_set.has<CurveComponent>()) {
+ attribute_vector_math_calc(geometry_set.get_component_for_write<CurveComponent>(), params);
+ }
params.set_output("Geometry", geometry_set);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc
new file mode 100644
index 00000000000..4d568ab5c3a
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc
@@ -0,0 +1,352 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "node_geometry_util.hh"
+
+#include "BLI_task.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+static bNodeSocketTemplate geo_node_attribute_vector_rotate_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_STRING, N_("Vector")},
+ {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE},
+ {SOCK_STRING, N_("Center")},
+ {SOCK_VECTOR, N_("Center"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_XYZ},
+ {SOCK_STRING, N_("Axis")},
+ {SOCK_VECTOR, N_("Axis"), 0.0f, 0.0f, 1.0f, 0.0f, -1.0f, 1.0f, PROP_XYZ, PROP_NONE},
+ {SOCK_STRING, N_("Angle")},
+ {SOCK_FLOAT, N_("Angle"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_ANGLE, PROP_NONE},
+ {SOCK_STRING, N_("Rotation")},
+ {SOCK_VECTOR, N_("Rotation"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_EULER},
+ {SOCK_BOOLEAN, N_("Invert")},
+ {SOCK_STRING, N_("Result")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_attribute_vector_rotate_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+static void geo_node_attribute_vector_rotate_layout(uiLayout *layout,
+ bContext *UNUSED(C),
+ PointerRNA *ptr)
+{
+ bNode *node = (bNode *)ptr->data;
+ const NodeAttributeVectorRotate &node_storage = *(NodeAttributeVectorRotate *)node->storage;
+ const GeometryNodeAttributeVectorRotateMode mode = (const GeometryNodeAttributeVectorRotateMode)
+ node_storage.mode;
+
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
+ uiLayout *column = uiLayoutColumn(layout, false);
+
+ uiItemR(column, ptr, "rotation_mode", 0, "", ICON_NONE);
+
+ uiItemR(column, ptr, "input_type_vector", 0, IFACE_("Vector"), ICON_NONE);
+ uiItemR(column, ptr, "input_type_center", 0, IFACE_("Center"), ICON_NONE);
+ if (mode == GEO_NODE_VECTOR_ROTATE_TYPE_AXIS) {
+ uiItemR(column, ptr, "input_type_axis", 0, IFACE_("Axis"), ICON_NONE);
+ }
+ if (mode != GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ) {
+ uiItemR(column, ptr, "input_type_angle", 0, IFACE_("Angle"), ICON_NONE);
+ }
+ if (mode == GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ) {
+ uiItemR(column, ptr, "input_type_rotation", 0, IFACE_("Rotation"), ICON_NONE);
+ }
+}
+
+namespace blender::nodes {
+
+static void geo_node_attribute_vector_rotate_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ const NodeAttributeVectorRotate *node_storage = (NodeAttributeVectorRotate *)node->storage;
+ const GeometryNodeAttributeVectorRotateMode mode = (const GeometryNodeAttributeVectorRotateMode)
+ node_storage->mode;
+
+ update_attribute_input_socket_availabilities(
+ *node, "Vector", (GeometryNodeAttributeInputMode)node_storage->input_type_vector);
+ update_attribute_input_socket_availabilities(
+ *node, "Center", (GeometryNodeAttributeInputMode)node_storage->input_type_center);
+ update_attribute_input_socket_availabilities(
+ *node,
+ "Axis",
+ (GeometryNodeAttributeInputMode)node_storage->input_type_axis,
+ (mode == GEO_NODE_VECTOR_ROTATE_TYPE_AXIS));
+ update_attribute_input_socket_availabilities(
+ *node,
+ "Angle",
+ (GeometryNodeAttributeInputMode)node_storage->input_type_angle,
+ (mode != GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ));
+ update_attribute_input_socket_availabilities(
+ *node,
+ "Rotation",
+ (GeometryNodeAttributeInputMode)node_storage->input_type_rotation,
+ (mode == GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ));
+}
+
+static float3 vector_rotate_around_axis(const float3 vector,
+ const float3 center,
+ const float3 axis,
+ const float angle)
+{
+ float3 result = vector - center;
+ float mat[3][3];
+ axis_angle_to_mat3(mat, axis, angle);
+ mul_m3_v3(mat, result);
+ return result + center;
+}
+
+static void geo_node_attribute_vector_rotate_init(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeAttributeVectorRotate *node_storage = (NodeAttributeVectorRotate *)MEM_callocN(
+ sizeof(NodeAttributeVectorRotate), __func__);
+
+ node_storage->mode = GEO_NODE_VECTOR_ROTATE_TYPE_AXIS;
+ node_storage->input_type_vector = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE;
+ node_storage->input_type_center = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
+ node_storage->input_type_axis = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
+ node_storage->input_type_angle = GEO_NODE_ATTRIBUTE_INPUT_FLOAT;
+ node_storage->input_type_rotation = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
+
+ node->storage = node_storage;
+}
+
+static float3 vector_rotate_euler(const float3 vector,
+ const float3 center,
+ const float3 rotation,
+ const bool invert)
+{
+ float mat[3][3];
+ float3 result = vector - center;
+ eul_to_mat3(mat, rotation);
+ if (invert) {
+ invert_m3(mat);
+ }
+ mul_m3_v3(mat, result);
+ return result + center;
+}
+
+static void do_vector_rotate_around_axis(const VArray<float3> &vector,
+ const VArray<float3> &center,
+ const VArray<float3> &axis,
+ const VArray<float> &angle,
+ MutableSpan<float3> results,
+ const bool invert)
+{
+ VArray_Span<float3> span_vector{vector};
+ VArray_Span<float3> span_center{center};
+ VArray_Span<float3> span_axis{axis};
+ VArray_Span<float> span_angle{angle};
+
+ parallel_for(IndexRange(results.size()), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ float angle = (invert) ? -span_angle[i] : span_angle[i];
+ results[i] = vector_rotate_around_axis(span_vector[i], span_center[i], span_axis[i], angle);
+ }
+ });
+}
+
+static void do_vector_rotate_around_fixed_axis(const VArray<float3> &vector,
+ const VArray<float3> &center,
+ const float3 axis,
+ const VArray<float> &angle,
+ MutableSpan<float3> results,
+ const bool invert)
+{
+ VArray_Span<float3> span_vector{vector};
+ VArray_Span<float3> span_center{center};
+ VArray_Span<float> span_angle{angle};
+
+ parallel_for(IndexRange(results.size()), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ float angle = (invert) ? -span_angle[i] : span_angle[i];
+ results[i] = vector_rotate_around_axis(span_vector[i], span_center[i], axis, angle);
+ }
+ });
+}
+
+static void do_vector_rotate_euler(const VArray<float3> &vector,
+ const VArray<float3> &center,
+ const VArray<float3> &rotation,
+ MutableSpan<float3> results,
+ const bool invert)
+{
+ VArray_Span<float3> span_vector{vector};
+ VArray_Span<float3> span_center{center};
+ VArray_Span<float3> span_rotation{rotation};
+
+ parallel_for(IndexRange(results.size()), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ results[i] = vector_rotate_euler(span_vector[i], span_center[i], span_rotation[i], invert);
+ }
+ });
+}
+
+static AttributeDomain get_result_domain(const GeometryComponent &component,
+ const GeoNodeExecParams &params,
+ StringRef result_name)
+{
+ /* Use the domain of the result attribute if it already exists. */
+ std::optional<AttributeMetaData> meta_data = component.attribute_get_meta_data(result_name);
+ if (meta_data) {
+ return meta_data->domain;
+ }
+
+ /* Otherwise use the highest priority domain from existing input attributes, or the default. */
+ const AttributeDomain default_domain = ATTR_DOMAIN_POINT;
+ return params.get_highest_priority_input_domain({"Vector", "Center"}, component, default_domain);
+}
+
+static void execute_on_component(const GeoNodeExecParams &params, GeometryComponent &component)
+{
+ const bNode &node = params.node();
+ const NodeAttributeVectorRotate *node_storage = (const NodeAttributeVectorRotate *)node.storage;
+ const GeometryNodeAttributeVectorRotateMode mode = (GeometryNodeAttributeVectorRotateMode)
+ node_storage->mode;
+ const std::string result_name = params.get_input<std::string>("Result");
+ const AttributeDomain result_domain = get_result_domain(component, params, result_name);
+ const bool invert = params.get_input<bool>("Invert");
+
+ GVArrayPtr attribute_vector = params.get_input_attribute(
+ "Vector", component, result_domain, CD_PROP_FLOAT3, nullptr);
+ if (!attribute_vector) {
+ return;
+ }
+ GVArrayPtr attribute_center = params.get_input_attribute(
+ "Center", component, result_domain, CD_PROP_FLOAT3, nullptr);
+ if (!attribute_center) {
+ return;
+ }
+
+ OutputAttribute attribute_result = component.attribute_try_get_for_output_only(
+ result_name, result_domain, CD_PROP_FLOAT3);
+ if (!attribute_result) {
+ return;
+ }
+
+ if (mode == GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ) {
+ GVArrayPtr attribute_rotation = params.get_input_attribute(
+ "Rotation", component, result_domain, CD_PROP_FLOAT3, nullptr);
+ if (!attribute_rotation) {
+ return;
+ }
+ do_vector_rotate_euler(attribute_vector->typed<float3>(),
+ attribute_center->typed<float3>(),
+ attribute_rotation->typed<float3>(),
+ attribute_result.as_span<float3>(),
+ invert);
+ attribute_result.save();
+ return;
+ }
+
+ GVArrayPtr attribute_angle = params.get_input_attribute(
+ "Angle", component, result_domain, CD_PROP_FLOAT, nullptr);
+ if (!attribute_angle) {
+ return;
+ }
+
+ switch (mode) {
+ case GEO_NODE_VECTOR_ROTATE_TYPE_AXIS: {
+ GVArrayPtr attribute_axis = params.get_input_attribute(
+ "Axis", component, result_domain, CD_PROP_FLOAT3, nullptr);
+ if (!attribute_axis) {
+ return;
+ }
+ do_vector_rotate_around_axis(attribute_vector->typed<float3>(),
+ attribute_center->typed<float3>(),
+ attribute_axis->typed<float3>(),
+ attribute_angle->typed<float>(),
+ attribute_result.as_span<float3>(),
+ invert);
+ } break;
+ case GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_X:
+ do_vector_rotate_around_fixed_axis(attribute_vector->typed<float3>(),
+ attribute_center->typed<float3>(),
+ float3(1.0f, 0.0f, 0.0f),
+ attribute_angle->typed<float>(),
+ attribute_result.as_span<float3>(),
+ invert);
+ break;
+ case GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_Y:
+ do_vector_rotate_around_fixed_axis(attribute_vector->typed<float3>(),
+ attribute_center->typed<float3>(),
+ float3(0.0f, 1.0f, 0.0f),
+ attribute_angle->typed<float>(),
+ attribute_result.as_span<float3>(),
+ invert);
+
+ break;
+ case GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_Z:
+ do_vector_rotate_around_fixed_axis(attribute_vector->typed<float3>(),
+ attribute_center->typed<float3>(),
+ float3(0.0f, 0.0f, 1.0f),
+ attribute_angle->typed<float>(),
+ attribute_result.as_span<float3>(),
+ invert);
+
+ break;
+ case GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ:
+ /* Euler is handled before other modes to avoid processing the unavailable angle socket. */
+ BLI_assert_unreachable();
+ break;
+ }
+ attribute_result.save();
+}
+
+static void geo_node_attribute_vector_rotate_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+ geometry_set = geometry_set_realize_instances(geometry_set);
+
+ if (geometry_set.has<MeshComponent>()) {
+ execute_on_component(params, geometry_set.get_component_for_write<MeshComponent>());
+ }
+ if (geometry_set.has<PointCloudComponent>()) {
+ execute_on_component(params, geometry_set.get_component_for_write<PointCloudComponent>());
+ }
+ if (geometry_set.has<CurveComponent>()) {
+ execute_on_component(params, geometry_set.get_component_for_write<CurveComponent>());
+ }
+
+ params.set_output("Geometry", std::move(geometry_set));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_attribute_vector_rotate()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype,
+ GEO_NODE_ATTRIBUTE_VECTOR_ROTATE,
+ "Attribute Vector Rotate",
+ NODE_CLASS_ATTRIBUTE,
+ 0);
+ node_type_socket_templates(
+ &ntype, geo_node_attribute_vector_rotate_in, geo_node_attribute_vector_rotate_out);
+ node_type_update(&ntype, blender::nodes::geo_node_attribute_vector_rotate_update);
+ node_type_init(&ntype, blender::nodes::geo_node_attribute_vector_rotate_init);
+ node_type_size(&ntype, 165, 100, 600);
+ node_type_storage(
+ &ntype, "NodeAttributeVectorRotate", node_free_standard_storage, node_copy_standard_storage);
+ ntype.geometry_node_execute = blender::nodes::geo_node_attribute_vector_rotate_exec;
+ ntype.draw_buttons = geo_node_attribute_vector_rotate_layout;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc
index 9f331190420..d8029ea1eeb 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc
@@ -14,29 +14,29 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "MEM_guardedalloc.h"
-
-#include "BLI_alloca.h"
-#include "BLI_math_matrix.h"
-
#include "DNA_mesh_types.h"
-#include "DNA_modifier_types.h"
-#include "RNA_enum_types.h"
+#include "BKE_mesh_boolean_convert.hh"
#include "UI_interface.h"
#include "UI_resources.h"
-#include "BKE_mesh.h"
-
-#include "bmesh.h"
-#include "tools/bmesh_boolean.h"
-
#include "node_geometry_util.hh"
static bNodeSocketTemplate geo_node_boolean_in[] = {
{SOCK_GEOMETRY, N_("Geometry 1")},
- {SOCK_GEOMETRY, N_("Geometry 2")},
+ {SOCK_GEOMETRY,
+ N_("Geometry 2"),
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ PROP_NONE,
+ SOCK_MULTI_INPUT},
+ {SOCK_BOOLEAN, N_("Self Intersection")},
+ {SOCK_BOOLEAN, N_("Hole Tolerant")},
{-1, ""},
};
@@ -50,109 +50,87 @@ static void geo_node_boolean_layout(uiLayout *layout, bContext *UNUSED(C), Point
uiItemR(layout, ptr, "operation", 0, "", ICON_NONE);
}
-static int bm_face_isect_pair(BMFace *f, void *UNUSED(user_data))
+static void geo_node_boolean_update(bNodeTree *UNUSED(ntree), bNode *node)
{
- return BM_elem_flag_test(f, BM_ELEM_DRAW) ? 1 : 0;
-}
-
-static Mesh *mesh_boolean_calc(const Mesh *mesh_a, const Mesh *mesh_b, int boolean_mode)
-{
- const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh_a, mesh_b);
-
- BMesh *bm;
- {
- struct BMeshCreateParams bmesh_create_params = {0};
- bmesh_create_params.use_toolflags = false;
- bm = BM_mesh_create(&allocsize, &bmesh_create_params);
- }
-
- {
- struct BMeshFromMeshParams bmesh_from_mesh_params = {0};
- bmesh_from_mesh_params.calc_face_normal = true;
- BM_mesh_bm_from_me(bm, mesh_b, &bmesh_from_mesh_params);
- BM_mesh_bm_from_me(bm, mesh_a, &bmesh_from_mesh_params);
- }
-
- const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop);
- int tottri;
- BMLoop *(*looptris)[3] = (BMLoop *
- (*)[3])(MEM_malloc_arrayN(looptris_tot, sizeof(*looptris), __func__));
- BM_mesh_calc_tessellation_beauty(bm, looptris, &tottri);
-
- const int i_faces_end = mesh_b->totpoly;
-
- /* We need face normals because of 'BM_face_split_edgenet'
- * we could calculate on the fly too (before calling split). */
-
- int i = 0;
- BMIter iter;
- BMFace *bm_face;
- BM_ITER_MESH (bm_face, &iter, bm, BM_FACES_OF_MESH) {
- normalize_v3(bm_face->no);
+ GeometryNodeBooleanOperation operation = (GeometryNodeBooleanOperation)node->custom1;
- /* Temp tag to test which side split faces are from. */
- BM_elem_flag_enable(bm_face, BM_ELEM_DRAW);
+ bNodeSocket *geometry_1_socket = (bNodeSocket *)node->inputs.first;
+ bNodeSocket *geometry_2_socket = geometry_1_socket->next;
- i++;
- if (i == i_faces_end) {
+ switch (operation) {
+ case GEO_NODE_BOOLEAN_INTERSECT:
+ case GEO_NODE_BOOLEAN_UNION:
+ nodeSetSocketAvailability(geometry_1_socket, false);
+ nodeSetSocketAvailability(geometry_2_socket, true);
+ node_sock_label(geometry_2_socket, N_("Geometry"));
+ break;
+ case GEO_NODE_BOOLEAN_DIFFERENCE:
+ nodeSetSocketAvailability(geometry_1_socket, true);
+ nodeSetSocketAvailability(geometry_2_socket, true);
+ node_sock_label(geometry_2_socket, N_("Geometry 2"));
break;
- }
}
+}
- BM_mesh_boolean(
- bm, looptris, tottri, bm_face_isect_pair, nullptr, 2, false, false, false, boolean_mode);
-
- Mesh *result = BKE_mesh_from_bmesh_for_eval_nomain(bm, nullptr, mesh_a);
- BM_mesh_free(bm);
- result->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
- MEM_freeN(looptris);
-
- return result;
+static void geo_node_boolean_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ node->custom1 = GEO_NODE_BOOLEAN_DIFFERENCE;
}
namespace blender::nodes {
+
static void geo_node_boolean_exec(GeoNodeExecParams params)
{
- GeometrySet geometry_set_in_a = params.extract_input<GeometrySet>("Geometry 1");
- GeometrySet geometry_set_in_b = params.extract_input<GeometrySet>("Geometry 2");
- GeometrySet geometry_set_out;
-
GeometryNodeBooleanOperation operation = (GeometryNodeBooleanOperation)params.node().custom1;
- if (operation < 0 || operation > 2) {
- BLI_assert(false);
- params.set_output("Geometry", std::move(geometry_set_out));
- return;
+ const bool use_self = params.get_input<bool>("Self Intersection");
+ const bool hole_tolerant = params.get_input<bool>("Hole Tolerant");
+
+#ifndef WITH_GMP
+ params.error_message_add(NodeWarningType::Error,
+ TIP_("Disabled, Blender was compiled without GMP"));
+#endif
+
+ Vector<const Mesh *> meshes;
+ Vector<const float4x4 *> transforms;
+
+ GeometrySet set_a;
+ if (operation == GEO_NODE_BOOLEAN_DIFFERENCE) {
+ set_a = params.extract_input<GeometrySet>("Geometry 1");
+ /* Note that it technically wouldn't be necessary to realize the instances for the first
+ * geometry input, but the boolean code expects the first shape for the difference operation
+ * to be a single mesh. */
+ set_a = geometry_set_realize_instances(set_a);
+ const Mesh *mesh_in_a = set_a.get_mesh_for_read();
+ if (mesh_in_a != nullptr) {
+ meshes.append(mesh_in_a);
+ transforms.append(nullptr);
+ }
}
- /* TODO: Boolean does support an input of multiple meshes. Currently they must all be
- * converted to BMesh before running the operation though. D9957 will make it possible
- * to use the mesh structure directly. */
- geometry_set_in_a = geometry_set_realize_instances(geometry_set_in_a);
- geometry_set_in_b = geometry_set_realize_instances(geometry_set_in_b);
-
- const Mesh *mesh_in_a = geometry_set_in_a.get_mesh_for_read();
- const Mesh *mesh_in_b = geometry_set_in_b.get_mesh_for_read();
+ /* The instance transform matrices are owned by the instance group, so we have to
+ * keep all of them around for use during the boolean operation. */
+ Vector<bke::GeometryInstanceGroup> set_groups;
+ Vector<GeometrySet> geometry_sets = params.extract_multi_input<GeometrySet>("Geometry 2");
+ for (const GeometrySet &geometry_set : geometry_sets) {
+ bke::geometry_set_gather_instances(geometry_set, set_groups);
+ }
- if (mesh_in_a == nullptr || mesh_in_b == nullptr) {
- if (operation == GEO_NODE_BOOLEAN_UNION) {
- if (mesh_in_a != nullptr) {
- params.set_output("Geometry", geometry_set_in_a);
+ for (const bke::GeometryInstanceGroup &set_group : set_groups) {
+ const Mesh *mesh_in = set_group.geometry_set.get_mesh_for_read();
+ if (mesh_in != nullptr) {
+ meshes.append_n_times(mesh_in, set_group.transforms.size());
+ for (const int i : set_group.transforms.index_range()) {
+ transforms.append(set_group.transforms.begin() + i);
}
- else {
- params.set_output("Geometry", geometry_set_in_b);
- }
- }
- else {
- params.set_output("Geometry", geometry_set_in_a);
}
- return;
}
- Mesh *mesh_out = mesh_boolean_calc(mesh_in_a, mesh_in_b, operation);
- geometry_set_out = GeometrySet::create_with_mesh(mesh_out);
+ Mesh *result = blender::meshintersect::direct_mesh_boolean(
+ meshes, transforms, float4x4::identity(), {}, use_self, hole_tolerant, operation);
- params.set_output("Geometry", std::move(geometry_set_out));
+ params.set_output("Geometry", GeometrySet::create_with_mesh(result));
}
+
} // namespace blender::nodes
void register_node_type_geo_boolean()
@@ -162,6 +140,8 @@ void register_node_type_geo_boolean()
geo_node_type_base(&ntype, GEO_NODE_BOOLEAN, "Boolean", NODE_CLASS_GEOMETRY, 0);
node_type_socket_templates(&ntype, geo_node_boolean_in, geo_node_boolean_out);
ntype.draw_buttons = geo_node_boolean_layout;
+ ntype.updatefunc = geo_node_boolean_update;
+ node_type_init(&ntype, geo_node_boolean_init);
ntype.geometry_node_execute = blender::nodes::geo_node_boolean_exec;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc b/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc
new file mode 100644
index 00000000000..83d3558a7cd
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc
@@ -0,0 +1,177 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BKE_spline.hh"
+#include "BKE_volume.h"
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_bounding_box_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_bounding_box_out[] = {
+ {SOCK_GEOMETRY, N_("Bounding Box")},
+ {SOCK_VECTOR, N_("Min")},
+ {SOCK_VECTOR, N_("Max")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+
+using bke::GeometryInstanceGroup;
+
+static void compute_min_max_from_position_and_transform(const GeometryComponent &component,
+ Span<float4x4> transforms,
+ float3 &r_min,
+ float3 &r_max)
+{
+ GVArray_Typed<float3> positions = component.attribute_get_for_read<float3>(
+ "position", ATTR_DOMAIN_POINT, {0, 0, 0});
+
+ for (const float4x4 &transform : transforms) {
+ for (const int i : positions.index_range()) {
+ const float3 position = positions[i];
+ const float3 transformed_position = transform * position;
+ minmax_v3v3_v3(r_min, r_max, transformed_position);
+ }
+ }
+}
+
+static void compute_min_max_from_volume_and_transforms(const VolumeComponent &volume_component,
+ Span<float4x4> transforms,
+ float3 &r_min,
+ float3 &r_max)
+{
+#ifdef WITH_OPENVDB
+ const Volume *volume = volume_component.get_for_read();
+ if (volume == nullptr) {
+ return;
+ }
+ for (const int i : IndexRange(BKE_volume_num_grids(volume))) {
+ const VolumeGrid *volume_grid = BKE_volume_grid_get_for_read(volume, i);
+ openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid);
+
+ for (const float4x4 &transform : transforms) {
+ openvdb::GridBase::ConstPtr instance_grid = BKE_volume_grid_shallow_transform(grid,
+ transform);
+ float3 grid_min = float3(FLT_MAX);
+ float3 grid_max = float3(-FLT_MAX);
+ if (BKE_volume_grid_bounds(instance_grid, grid_min, grid_max)) {
+ DO_MIN(grid_min, r_min);
+ DO_MAX(grid_max, r_max);
+ }
+ }
+ }
+#else
+ UNUSED_VARS(volume_component, transforms, r_min, r_max);
+#endif
+}
+
+static void compute_min_max_from_curve_and_transforms(const CurveComponent &curve_component,
+ Span<float4x4> transforms,
+ float3 &r_min,
+ float3 &r_max)
+{
+ const CurveEval *curve = curve_component.get_for_read();
+ if (curve == nullptr) {
+ return;
+ }
+ for (const SplinePtr &spline : curve->splines()) {
+ Span<float3> positions = spline->evaluated_positions();
+
+ for (const float4x4 &transform : transforms) {
+ for (const int i : positions.index_range()) {
+ const float3 position = positions[i];
+ const float3 transformed_position = transform * position;
+ minmax_v3v3_v3(r_min, r_max, transformed_position);
+ }
+ }
+ }
+}
+
+static void compute_geometry_set_instances_boundbox(const GeometrySet &geometry_set,
+ float3 &r_min,
+ float3 &r_max)
+{
+ Vector<GeometryInstanceGroup> set_groups;
+ bke::geometry_set_gather_instances(geometry_set, set_groups);
+
+ for (const GeometryInstanceGroup &set_group : set_groups) {
+ const GeometrySet &set = set_group.geometry_set;
+ Span<float4x4> transforms = set_group.transforms;
+
+ if (set.has<PointCloudComponent>()) {
+ compute_min_max_from_position_and_transform(
+ *set.get_component_for_read<PointCloudComponent>(), transforms, r_min, r_max);
+ }
+ if (set.has<MeshComponent>()) {
+ compute_min_max_from_position_and_transform(
+ *set.get_component_for_read<MeshComponent>(), transforms, r_min, r_max);
+ }
+ if (set.has<VolumeComponent>()) {
+ compute_min_max_from_volume_and_transforms(
+ *set.get_component_for_read<VolumeComponent>(), transforms, r_min, r_max);
+ }
+ if (set.has<CurveComponent>()) {
+ compute_min_max_from_curve_and_transforms(
+ *set.get_component_for_read<CurveComponent>(), transforms, r_min, r_max);
+ }
+ }
+}
+
+static void geo_node_bounding_box_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+ float3 min = float3(FLT_MAX);
+ float3 max = float3(-FLT_MAX);
+
+ if (geometry_set.has_instances()) {
+ compute_geometry_set_instances_boundbox(geometry_set, min, max);
+ }
+ else {
+ geometry_set.compute_boundbox_without_instances(&min, &max);
+ }
+
+ if (min == float3(FLT_MAX)) {
+ params.set_output("Bounding Box", GeometrySet());
+ params.set_output("Min", float3(0));
+ params.set_output("Max", float3(0));
+ }
+ else {
+ const float3 scale = max - min;
+ const float3 center = min + scale / 2.0f;
+ Mesh *mesh = create_cube_mesh(1.0f);
+ transform_mesh(mesh, center, float3(0), scale);
+ params.set_output("Bounding Box", GeometrySet::create_with_mesh(mesh));
+ params.set_output("Min", min);
+ params.set_output("Max", max);
+ }
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_bounding_box()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_BOUNDING_BOX, "Bounding Box", NODE_CLASS_GEOMETRY, 0);
+ node_type_socket_templates(&ntype, geo_node_bounding_box_in, geo_node_bounding_box_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_bounding_box_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc b/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc
index 8991a26ba4b..b2dc4661e9c 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc
@@ -14,8 +14,6 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "node_geometry_util.hh"
-
#include "BLI_math_matrix.h"
#include "DNA_collection_types.h"
@@ -23,8 +21,19 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "node_geometry_util.hh"
+
static bNodeSocketTemplate geo_node_collection_info_in[] = {
- {SOCK_COLLECTION, N_("Collection")},
+ {SOCK_COLLECTION,
+ N_("Collection"),
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ PROP_NONE,
+ SOCK_HIDE_LABEL},
{-1, ""},
};
@@ -40,11 +49,17 @@ static void geo_node_collection_info_layout(uiLayout *layout, bContext *UNUSED(C
namespace blender::nodes {
+static void geo_node_collection_info_node_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometryCollectionInfo *data = (NodeGeometryCollectionInfo *)MEM_callocN(
+ sizeof(NodeGeometryCollectionInfo), __func__);
+ data->transform_space = GEO_NODE_TRANSFORM_SPACE_ORIGINAL;
+ node->storage = data;
+}
+
static void geo_node_collection_info_exec(GeoNodeExecParams params)
{
- bke::PersistentCollectionHandle collection_handle =
- params.extract_input<bke::PersistentCollectionHandle>("Collection");
- Collection *collection = params.handle_map().lookup(collection_handle);
+ Collection *collection = params.get_input<Collection *>("Collection");
GeometrySet geometry_set_out;
@@ -58,10 +73,6 @@ static void geo_node_collection_info_exec(GeoNodeExecParams params)
const bool transform_space_relative = (node_storage->transform_space ==
GEO_NODE_TRANSFORM_SPACE_RELATIVE);
- InstancedData instance;
- instance.type = INSTANCE_DATA_TYPE_COLLECTION;
- instance.data.collection = collection;
-
InstancesComponent &instances = geometry_set_out.get_component_for_write<InstancesComponent>();
float transform_mat[4][4];
@@ -73,17 +84,11 @@ static void geo_node_collection_info_exec(GeoNodeExecParams params)
mul_m4_m4_pre(transform_mat, self_object->imat);
}
- instances.add_instance(instance, transform_mat, -1);
- params.set_output("Geometry", geometry_set_out);
-}
+ const int handle = instances.add_reference(*collection);
+ instances.add_instance(handle, transform_mat, -1);
-static void geo_node_collection_info_node_init(bNodeTree *UNUSED(tree), bNode *node)
-{
- NodeGeometryCollectionInfo *data = (NodeGeometryCollectionInfo *)MEM_callocN(
- sizeof(NodeGeometryCollectionInfo), __func__);
- data->transform_space = GEO_NODE_TRANSFORM_SPACE_ORIGINAL;
- node->storage = data;
+ params.set_output("Geometry", geometry_set_out);
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/geometry/nodes/node_geo_common.cc b/source/blender/nodes/geometry/nodes/node_geo_common.cc
index 441ad6bdc13..e2bb7e9f939 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_common.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_common.cc
@@ -43,3 +43,17 @@ void register_node_type_geo_group(void)
nodeRegisterType(&ntype);
}
+
+void register_node_type_geo_custom_group(bNodeType *ntype)
+{
+ /* These methods can be overridden but need a default implementation otherwise. */
+ if (ntype->poll == nullptr) {
+ ntype->poll = geo_node_poll_default;
+ }
+ if (ntype->insert_link == nullptr) {
+ ntype->insert_link = node_insert_link_default;
+ }
+ if (ntype->update_internal_links == nullptr) {
+ ntype->update_internal_links = node_update_internal_links_default;
+ }
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc
new file mode 100644
index 00000000000..b1b17a321b8
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc
@@ -0,0 +1,321 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_pointcloud_types.h"
+
+#include "BKE_material.h"
+#include "BKE_mesh.h"
+#include "BKE_spline.hh"
+
+#include "node_geometry_util.hh"
+
+#ifdef WITH_BULLET
+# include "RBI_hull_api.h"
+#endif
+
+static bNodeSocketTemplate geo_node_convex_hull_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_convex_hull_out[] = {
+ {SOCK_GEOMETRY, N_("Convex Hull")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+
+using bke::GeometryInstanceGroup;
+
+#ifdef WITH_BULLET
+
+static Mesh *hull_from_bullet(const Mesh *mesh, Span<float3> coords)
+{
+ plConvexHull hull = plConvexHullCompute((float(*)[3])coords.data(), coords.size());
+
+ const int num_verts = plConvexHullNumVertices(hull);
+ const int num_faces = num_verts <= 2 ? 0 : plConvexHullNumFaces(hull);
+ const int num_loops = num_verts <= 2 ? 0 : plConvexHullNumLoops(hull);
+ /* Half as many edges as loops, because the mesh is manifold. */
+ const int num_edges = num_verts == 2 ? 1 : num_verts < 2 ? 0 : num_loops / 2;
+
+ /* Create Mesh *result with proper capacity. */
+ Mesh *result;
+ if (mesh) {
+ result = BKE_mesh_new_nomain_from_template(
+ mesh, num_verts, num_edges, 0, num_loops, num_faces);
+ }
+ else {
+ result = BKE_mesh_new_nomain(num_verts, num_edges, 0, num_loops, num_faces);
+ BKE_id_material_eval_ensure_default_slot(&result->id);
+ }
+
+ /* Copy vertices. */
+ for (const int i : IndexRange(num_verts)) {
+ float co[3];
+ int original_index;
+ plConvexHullGetVertex(hull, i, co, &original_index);
+
+ if (original_index >= 0 && original_index < coords.size()) {
+# if 0 /* Disabled because it only works for meshes, not predictable enough. */
+ /* Copy custom data on vertices, like vertex groups etc. */
+ if (mesh && original_index < mesh->totvert) {
+ CustomData_copy_data(&mesh->vdata, &result->vdata, (int)original_index, (int)i, 1);
+ }
+# endif
+ /* Copy the position of the original point. */
+ copy_v3_v3(result->mvert[i].co, co);
+ }
+ else {
+ BLI_assert(!"Unexpected new vertex in hull output");
+ }
+ }
+
+ /* Copy edges and loops. */
+
+ /* NOTE: ConvexHull from Bullet uses a half-edge data structure
+ * for its mesh. To convert that, each half-edge needs to be converted
+ * to a loop and edges need to be created from that. */
+ Array<MLoop> mloop_src(num_loops);
+ uint edge_index = 0;
+ for (const int i : IndexRange(num_loops)) {
+ int v_from;
+ int v_to;
+ plConvexHullGetLoop(hull, i, &v_from, &v_to);
+
+ 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];
+ edge.v1 = v_from;
+ edge.v2 = v_to;
+ edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
+
+ /* Write edge index into both loops that have it. */
+ int reverse_index = plConvexHullGetReversedLoopIndex(hull, i);
+ mloop_src[i].e = edge_index;
+ mloop_src[reverse_index].e = edge_index;
+ edge_index++;
+ }
+ }
+ if (num_edges == 1) {
+ /* In this case there are no loops. */
+ MEdge &edge = result->medge[0];
+ edge.v1 = 0;
+ edge.v2 = 1;
+ edge.flag |= ME_EDGEDRAW | ME_EDGERENDER | ME_LOOSEEDGE;
+ edge_index++;
+ }
+ BLI_assert(edge_index == num_edges);
+
+ /* Copy faces. */
+ Array<int> loops;
+ int j = 0;
+ MLoop *loop = result->mloop;
+ for (const int i : IndexRange(num_faces)) {
+ const int len = plConvexHullGetFaceSize(hull, i);
+
+ BLI_assert(len > 2);
+
+ /* Get face loop indices. */
+ loops.reinitialize(len);
+ plConvexHullGetFaceLoops(hull, i, loops.data());
+
+ MPoly &face = result->mpoly[i];
+ face.loopstart = j;
+ face.totloop = len;
+ for (const int k : IndexRange(len)) {
+ MLoop &src_loop = mloop_src[loops[k]];
+ loop->v = src_loop.v;
+ loop->e = src_loop.e;
+ loop++;
+ }
+ j += len;
+ }
+
+ plConvexHullDelete(hull);
+
+ BKE_mesh_calc_normals(result);
+ return result;
+}
+
+static Mesh *compute_hull(const GeometrySet &geometry_set)
+{
+ int span_count = 0;
+ int count = 0;
+ int total_size = 0;
+
+ Span<float3> positions_span;
+
+ if (geometry_set.has_mesh()) {
+ count++;
+ const MeshComponent *component = geometry_set.get_component_for_read<MeshComponent>();
+ total_size += component->attribute_domain_size(ATTR_DOMAIN_POINT);
+ }
+
+ if (geometry_set.has_pointcloud()) {
+ count++;
+ span_count++;
+ const PointCloudComponent *component =
+ geometry_set.get_component_for_read<PointCloudComponent>();
+ GVArray_Typed<float3> varray = component->attribute_get_for_read<float3>(
+ "position", ATTR_DOMAIN_POINT, {0, 0, 0});
+ total_size += varray->size();
+ positions_span = varray->get_internal_span();
+ }
+
+ if (geometry_set.has_curve()) {
+ const CurveEval &curve = *geometry_set.get_curve_for_read();
+ for (const SplinePtr &spline : curve.splines()) {
+ positions_span = spline->evaluated_positions();
+ total_size += positions_span.size();
+ count++;
+ span_count++;
+ }
+ }
+
+ if (count == 0) {
+ return nullptr;
+ }
+
+ /* If there is only one positions virtual array and it is already contiguous, avoid copying
+ * all of the positions and instead pass the span directly to the convex hull function. */
+ if (span_count == 1 && count == 1) {
+ return hull_from_bullet(nullptr, positions_span);
+ }
+
+ Array<float3> positions(total_size);
+ int offset = 0;
+
+ if (geometry_set.has_mesh()) {
+ const MeshComponent *component = geometry_set.get_component_for_read<MeshComponent>();
+ GVArray_Typed<float3> varray = component->attribute_get_for_read<float3>(
+ "position", ATTR_DOMAIN_POINT, {0, 0, 0});
+ varray->materialize(positions.as_mutable_span().slice(offset, varray.size()));
+ offset += varray.size();
+ }
+
+ if (geometry_set.has_pointcloud()) {
+ const PointCloudComponent *component =
+ geometry_set.get_component_for_read<PointCloudComponent>();
+ GVArray_Typed<float3> varray = component->attribute_get_for_read<float3>(
+ "position", ATTR_DOMAIN_POINT, {0, 0, 0});
+ varray->materialize(positions.as_mutable_span().slice(offset, varray.size()));
+ offset += varray.size();
+ }
+
+ if (geometry_set.has_curve()) {
+ const CurveEval &curve = *geometry_set.get_curve_for_read();
+ for (const SplinePtr &spline : curve.splines()) {
+ Span<float3> array = spline->evaluated_positions();
+ positions.as_mutable_span().slice(offset, array.size()).copy_from(array);
+ offset += array.size();
+ }
+ }
+
+ return hull_from_bullet(geometry_set.get_mesh_for_read(), positions);
+}
+
+static void read_positions(const GeometryComponent &component,
+ Span<float4x4> transforms,
+ Vector<float3> *r_coords)
+{
+ GVArray_Typed<float3> positions = component.attribute_get_for_read<float3>(
+ "position", ATTR_DOMAIN_POINT, {0, 0, 0});
+
+ /* NOTE: could use convex hull operation here to
+ * cut out some vertices, before accumulating,
+ * but can also be done by the user beforehand. */
+
+ r_coords->reserve(r_coords->size() + positions.size() * transforms.size());
+ for (const float4x4 &transform : transforms) {
+ for (const int i : positions.index_range()) {
+ const float3 position = positions[i];
+ const float3 transformed_position = transform * position;
+ r_coords->append(transformed_position);
+ }
+ }
+}
+
+static void read_curve_positions(const CurveEval &curve,
+ Span<float4x4> transforms,
+ Vector<float3> *r_coords)
+{
+ const Array<int> offsets = curve.evaluated_point_offsets();
+ const int total_size = offsets.last();
+ r_coords->reserve(r_coords->size() + total_size * transforms.size());
+ for (const SplinePtr &spline : curve.splines()) {
+ Span<float3> positions = spline->evaluated_positions();
+ for (const float4x4 &transform : transforms) {
+ for (const float3 &position : positions) {
+ r_coords->append(transform * position);
+ }
+ }
+ }
+}
+
+#endif /* WITH_BULLET */
+
+static void geo_node_convex_hull_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+#ifdef WITH_BULLET
+ Mesh *mesh = nullptr;
+ if (geometry_set.has_instances()) {
+ Vector<GeometryInstanceGroup> set_groups;
+ bke::geometry_set_gather_instances(geometry_set, set_groups);
+
+ Vector<float3> coords;
+
+ for (const GeometryInstanceGroup &set_group : set_groups) {
+ const GeometrySet &set = set_group.geometry_set;
+ Span<float4x4> transforms = set_group.transforms;
+
+ if (set.has_pointcloud()) {
+ read_positions(*set.get_component_for_read<PointCloudComponent>(), transforms, &coords);
+ }
+ if (set.has_mesh()) {
+ read_positions(*set.get_component_for_read<MeshComponent>(), transforms, &coords);
+ }
+ if (set.has_curve()) {
+ read_curve_positions(*set.get_curve_for_read(), transforms, &coords);
+ }
+ }
+ mesh = hull_from_bullet(nullptr, coords);
+ }
+ else {
+ mesh = compute_hull(geometry_set);
+ }
+ params.set_output("Convex Hull", GeometrySet::create_with_mesh(mesh));
+#else
+ params.set_output("Convex Hull", geometry_set);
+#endif /* WITH_BULLET */
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_convex_hull()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_CONVEX_HULL, "Convex Hull", NODE_CLASS_GEOMETRY, 0);
+ node_type_socket_templates(&ntype, geo_node_convex_hull_in, geo_node_convex_hull_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_convex_hull_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc
new file mode 100644
index 00000000000..306085e3b75
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc
@@ -0,0 +1,58 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BKE_spline.hh"
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_curve_length_in[] = {
+ {SOCK_GEOMETRY, N_("Curve")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_curve_length_out[] = {
+ {SOCK_FLOAT, N_("Length")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+
+static void geo_node_curve_length_exec(GeoNodeExecParams params)
+{
+ GeometrySet curve_set = params.extract_input<GeometrySet>("Curve");
+ curve_set = bke::geometry_set_realize_instances(curve_set);
+ if (!curve_set.has_curve()) {
+ params.set_output("Length", 0.0f);
+ return;
+ }
+ const CurveEval &curve = *curve_set.get_curve_for_read();
+ float length = 0.0f;
+ for (const SplinePtr &spline : curve.splines()) {
+ length += spline->length();
+ }
+ params.set_output("Length", length);
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_curve_length()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_CURVE_LENGTH, "Curve Length", NODE_CLASS_GEOMETRY, 0);
+ node_type_socket_templates(&ntype, geo_node_curve_length_in, geo_node_curve_length_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_curve_length_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc
new file mode 100644
index 00000000000..e879ec624c0
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc
@@ -0,0 +1,211 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BLI_array.hh"
+#include "BLI_task.hh"
+#include "BLI_timeit.hh"
+
+#include "BKE_attribute_math.hh"
+#include "BKE_spline.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+using blender::fn::GVArray_For_GSpan;
+using blender::fn::GVArray_For_Span;
+using blender::fn::GVArray_Typed;
+
+static bNodeSocketTemplate geo_node_curve_resample_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_INT, N_("Count"), 10, 0, 0, 0, 1, 100000},
+ {SOCK_FLOAT, N_("Length"), 0.1f, 0.0f, 0.0f, 0.0f, 0.001f, FLT_MAX, PROP_DISTANCE},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_curve_resample_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+static void geo_node_curve_resample_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
+}
+
+static void geo_node_curve_resample_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometryCurveResample *data = (NodeGeometryCurveResample *)MEM_callocN(
+ sizeof(NodeGeometryCurveResample), __func__);
+
+ data->mode = GEO_NODE_CURVE_SAMPLE_COUNT;
+ node->storage = data;
+}
+
+static void geo_node_curve_resample_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeGeometryCurveResample &node_storage = *(NodeGeometryCurveResample *)node->storage;
+ const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode;
+
+ bNodeSocket *count_socket = ((bNodeSocket *)node->inputs.first)->next;
+ bNodeSocket *length_socket = count_socket->next;
+
+ nodeSetSocketAvailability(count_socket, mode == GEO_NODE_CURVE_SAMPLE_COUNT);
+ nodeSetSocketAvailability(length_socket, mode == GEO_NODE_CURVE_SAMPLE_LENGTH);
+}
+
+namespace blender::nodes {
+
+struct SampleModeParam {
+ GeometryNodeCurveSampleMode mode;
+ std::optional<float> length;
+ std::optional<int> count;
+};
+
+static SplinePtr resample_spline(const Spline &input_spline, const int count)
+{
+ std::unique_ptr<PolySpline> output_spline = std::make_unique<PolySpline>();
+ output_spline->set_cyclic(input_spline.is_cyclic());
+ output_spline->normal_mode = input_spline.normal_mode;
+
+ if (input_spline.evaluated_edges_size() < 1 || count == 1) {
+ output_spline->add_point(input_spline.positions().first(),
+ input_spline.tilts().first(),
+ input_spline.radii().first());
+ output_spline->attributes.reallocate(1);
+ return output_spline;
+ }
+
+ output_spline->resize(count);
+
+ Array<float> uniform_samples = input_spline.sample_uniform_index_factors(count);
+
+ input_spline.sample_based_on_index_factors<float3>(
+ input_spline.evaluated_positions(), uniform_samples, output_spline->positions());
+
+ input_spline.sample_based_on_index_factors<float>(
+ input_spline.interpolate_to_evaluated_points(input_spline.radii()),
+ uniform_samples,
+ output_spline->radii());
+
+ input_spline.sample_based_on_index_factors<float>(
+ input_spline.interpolate_to_evaluated_points(input_spline.tilts()),
+ uniform_samples,
+ output_spline->tilts());
+
+ output_spline->attributes.reallocate(count);
+ input_spline.attributes.foreach_attribute(
+ [&](StringRefNull name, const AttributeMetaData &meta_data) {
+ std::optional<GSpan> input_attribute = input_spline.attributes.get_for_read(name);
+ BLI_assert(input_attribute);
+ if (!output_spline->attributes.create(name, meta_data.data_type)) {
+ BLI_assert_unreachable();
+ return false;
+ }
+ std::optional<GMutableSpan> output_attribute = output_spline->attributes.get_for_write(
+ name);
+ if (!output_attribute) {
+ BLI_assert_unreachable();
+ return false;
+ }
+
+ input_spline.sample_based_on_index_factors(
+ *input_spline.interpolate_to_evaluated_points(*input_attribute),
+ uniform_samples,
+ *output_attribute);
+
+ return true;
+ },
+ ATTR_DOMAIN_POINT);
+
+ return output_spline;
+}
+
+static std::unique_ptr<CurveEval> resample_curve(const CurveEval &input_curve,
+ const SampleModeParam &mode_param)
+{
+ std::unique_ptr<CurveEval> output_curve = std::make_unique<CurveEval>();
+
+ for (const SplinePtr &spline : input_curve.splines()) {
+ if (mode_param.mode == GEO_NODE_CURVE_SAMPLE_COUNT) {
+ BLI_assert(mode_param.count);
+ output_curve->add_spline(resample_spline(*spline, *mode_param.count));
+ }
+ else if (mode_param.mode == GEO_NODE_CURVE_SAMPLE_LENGTH) {
+ BLI_assert(mode_param.length);
+ const float length = spline->length();
+ const int count = std::max(int(length / *mode_param.length), 1);
+ output_curve->add_spline(resample_spline(*spline, count));
+ }
+ }
+
+ output_curve->attributes = input_curve.attributes;
+
+ return output_curve;
+}
+
+static void geo_node_resample_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+ geometry_set = bke::geometry_set_realize_instances(geometry_set);
+
+ if (!geometry_set.has_curve()) {
+ params.set_output("Geometry", GeometrySet());
+ return;
+ }
+
+ const CurveEval &input_curve = *geometry_set.get_curve_for_read();
+ NodeGeometryCurveResample &node_storage = *(NodeGeometryCurveResample *)params.node().storage;
+ const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode;
+ SampleModeParam mode_param;
+ mode_param.mode = mode;
+ if (mode == GEO_NODE_CURVE_SAMPLE_COUNT) {
+ const int count = params.extract_input<int>("Count");
+ if (count < 1) {
+ params.set_output("Geometry", GeometrySet());
+ return;
+ }
+ mode_param.count.emplace(count);
+ }
+ else if (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) {
+ /* Don't allow asymptotic count increase for low resolution values. */
+ const float resolution = std::max(params.extract_input<float>("Length"), 0.0001f);
+ mode_param.length.emplace(resolution);
+ }
+
+ std::unique_ptr<CurveEval> output_curve = resample_curve(input_curve, mode_param);
+
+ params.set_output("Geometry", GeometrySet::create_with_curve(output_curve.release()));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_curve_resample()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_CURVE_RESAMPLE, "Resample Curve", NODE_CLASS_GEOMETRY, 0);
+ node_type_socket_templates(&ntype, geo_node_curve_resample_in, geo_node_curve_resample_out);
+ ntype.draw_buttons = geo_node_curve_resample_layout;
+ node_type_storage(
+ &ntype, "NodeGeometryCurveResample", node_free_standard_storage, node_copy_standard_storage);
+ node_type_init(&ntype, geo_node_curve_resample_init);
+ node_type_update(&ntype, geo_node_curve_resample_update);
+ ntype.geometry_node_execute = blender::nodes::geo_node_resample_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc
new file mode 100644
index 00000000000..b6f04352929
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc
@@ -0,0 +1,313 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BLI_array.hh"
+#include "BLI_float4x4.hh"
+#include "BLI_timeit.hh"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_material.h"
+#include "BKE_mesh.h"
+#include "BKE_spline.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_curve_to_mesh_in[] = {
+ {SOCK_GEOMETRY, N_("Curve")},
+ {SOCK_GEOMETRY, N_("Profile Curve")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_curve_to_mesh_out[] = {
+ {SOCK_GEOMETRY, N_("Mesh")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+
+static void vert_extrude_to_mesh_data(const Spline &spline,
+ const float3 profile_vert,
+ MutableSpan<MVert> r_verts,
+ MutableSpan<MEdge> r_edges,
+ int &vert_offset,
+ int &edge_offset)
+{
+ Span<float3> positions = spline.evaluated_positions();
+
+ for (const int i : IndexRange(positions.size() - 1)) {
+ MEdge &edge = r_edges[edge_offset++];
+ edge.v1 = vert_offset + i;
+ edge.v2 = vert_offset + i + 1;
+ edge.flag = ME_LOOSEEDGE;
+ }
+
+ if (spline.is_cyclic() && spline.evaluated_edges_size() > 1) {
+ MEdge &edge = r_edges[edge_offset++];
+ edge.v1 = vert_offset;
+ edge.v2 = vert_offset + positions.size() - 1;
+ edge.flag = ME_LOOSEEDGE;
+ }
+
+ for (const int i : positions.index_range()) {
+ MVert &vert = r_verts[vert_offset++];
+ copy_v3_v3(vert.co, positions[i] + profile_vert);
+ }
+}
+
+static void mark_edges_sharp(MutableSpan<MEdge> edges)
+{
+ for (MEdge &edge : edges) {
+ edge.flag |= ME_SHARP;
+ }
+}
+
+static void spline_extrude_to_mesh_data(const Spline &spline,
+ const Spline &profile_spline,
+ MutableSpan<MVert> r_verts,
+ MutableSpan<MEdge> r_edges,
+ MutableSpan<MLoop> r_loops,
+ MutableSpan<MPoly> r_polys,
+ int &vert_offset,
+ int &edge_offset,
+ int &loop_offset,
+ int &poly_offset)
+{
+ const int spline_vert_len = spline.evaluated_points_size();
+ const int spline_edge_len = spline.evaluated_edges_size();
+ const int profile_vert_len = profile_spline.evaluated_points_size();
+ const int profile_edge_len = profile_spline.evaluated_edges_size();
+ if (spline_vert_len == 0) {
+ return;
+ }
+
+ if (profile_vert_len == 1) {
+ vert_extrude_to_mesh_data(spline,
+ profile_spline.evaluated_positions()[0],
+ r_verts,
+ r_edges,
+ vert_offset,
+ edge_offset);
+ return;
+ }
+
+ /* Add the edges running along the length of the curve, starting at each profile vertex. */
+ const int spline_edges_start = edge_offset;
+ for (const int i_profile : IndexRange(profile_vert_len)) {
+ for (const int i_ring : IndexRange(spline_edge_len)) {
+ const int i_next_ring = (i_ring == spline_vert_len - 1) ? 0 : i_ring + 1;
+
+ const int ring_vert_offset = vert_offset + profile_vert_len * i_ring;
+ const int next_ring_vert_offset = vert_offset + profile_vert_len * i_next_ring;
+
+ MEdge &edge = r_edges[edge_offset++];
+ edge.v1 = ring_vert_offset + i_profile;
+ edge.v2 = next_ring_vert_offset + i_profile;
+ edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
+ }
+ }
+
+ /* Add the edges running along each profile ring. */
+ const int profile_edges_start = edge_offset;
+ for (const int i_ring : IndexRange(spline_vert_len)) {
+ const int ring_vert_offset = vert_offset + profile_vert_len * i_ring;
+
+ for (const int i_profile : IndexRange(profile_edge_len)) {
+ const int i_next_profile = (i_profile == profile_vert_len - 1) ? 0 : i_profile + 1;
+
+ MEdge &edge = r_edges[edge_offset++];
+ edge.v1 = ring_vert_offset + i_profile;
+ edge.v2 = ring_vert_offset + i_next_profile;
+ edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
+ }
+ }
+
+ /* Calculate poly and corner indices. */
+ for (const int i_ring : IndexRange(spline_edge_len)) {
+ const int i_next_ring = (i_ring == spline_vert_len - 1) ? 0 : i_ring + 1;
+
+ const int ring_vert_offset = vert_offset + profile_vert_len * i_ring;
+ const int next_ring_vert_offset = vert_offset + profile_vert_len * i_next_ring;
+
+ const int ring_edge_start = profile_edges_start + profile_edge_len * i_ring;
+ const int next_ring_edge_offset = profile_edges_start + profile_edge_len * i_next_ring;
+
+ for (const int i_profile : IndexRange(profile_edge_len)) {
+ const int i_next_profile = (i_profile == profile_vert_len - 1) ? 0 : i_profile + 1;
+
+ const int spline_edge_start = spline_edges_start + spline_edge_len * i_profile;
+ const int next_spline_edge_start = spline_edges_start + spline_edge_len * i_next_profile;
+
+ MPoly &poly = r_polys[poly_offset++];
+ poly.loopstart = loop_offset;
+ poly.totloop = 4;
+ poly.flag = ME_SMOOTH;
+
+ MLoop &loop_a = r_loops[loop_offset++];
+ loop_a.v = ring_vert_offset + i_profile;
+ loop_a.e = ring_edge_start + i_profile;
+ MLoop &loop_b = r_loops[loop_offset++];
+ loop_b.v = ring_vert_offset + i_next_profile;
+ loop_b.e = next_spline_edge_start + i_ring;
+ MLoop &loop_c = r_loops[loop_offset++];
+ loop_c.v = next_ring_vert_offset + i_next_profile;
+ loop_c.e = next_ring_edge_offset + i_profile;
+ MLoop &loop_d = r_loops[loop_offset++];
+ loop_d.v = next_ring_vert_offset + i_profile;
+ loop_d.e = spline_edge_start + i_ring;
+ }
+ }
+
+ /* Calculate the positions of each profile ring profile along the spline. */
+ Span<float3> positions = spline.evaluated_positions();
+ Span<float3> tangents = spline.evaluated_tangents();
+ Span<float3> normals = spline.evaluated_normals();
+ Span<float3> profile_positions = profile_spline.evaluated_positions();
+
+ GVArray_Typed<float> radii = spline.interpolate_to_evaluated_points(spline.radii());
+ for (const int i_ring : IndexRange(spline_vert_len)) {
+ float4x4 point_matrix = float4x4::from_normalized_axis_data(
+ positions[i_ring], normals[i_ring], tangents[i_ring]);
+
+ point_matrix.apply_scale(radii[i_ring]);
+
+ for (const int i_profile : IndexRange(profile_vert_len)) {
+ MVert &vert = r_verts[vert_offset++];
+ copy_v3_v3(vert.co, point_matrix * profile_positions[i_profile]);
+ }
+ }
+
+ /* Mark edge loops from sharp vector control points sharp. */
+ if (profile_spline.type() == Spline::Type::Bezier) {
+ const BezierSpline &bezier_spline = static_cast<const BezierSpline &>(profile_spline);
+ Span<int> control_point_offsets = bezier_spline.control_point_offsets();
+ for (const int i : IndexRange(bezier_spline.size())) {
+ if (bezier_spline.point_is_sharp(i)) {
+ mark_edges_sharp(r_edges.slice(
+ spline_edges_start + spline_edge_len * control_point_offsets[i], spline_edge_len));
+ }
+ }
+ }
+}
+
+static Mesh *curve_to_mesh_calculate(const CurveEval &curve, const CurveEval &profile_curve)
+{
+ int profile_vert_total = 0;
+ int profile_edge_total = 0;
+ for (const SplinePtr &profile_spline : profile_curve.splines()) {
+ profile_vert_total += profile_spline->evaluated_points_size();
+ profile_edge_total += profile_spline->evaluated_edges_size();
+ }
+
+ int vert_total = 0;
+ int edge_total = 0;
+ int poly_total = 0;
+ for (const SplinePtr &spline : curve.splines()) {
+ const int spline_vert_len = spline->evaluated_points_size();
+ const int spline_edge_len = spline->evaluated_edges_size();
+ vert_total += spline_vert_len * profile_vert_total;
+ poly_total += spline_edge_len * profile_edge_total;
+
+ /* Add the ring edges, with one ring for every curve vertex, and the edge loops
+ * that run along the length of the curve, starting on the first profile. */
+ edge_total += profile_edge_total * spline_vert_len + profile_vert_total * spline_edge_len;
+ }
+ const int corner_total = poly_total * 4;
+
+ if (vert_total == 0) {
+ return nullptr;
+ }
+
+ Mesh *mesh = BKE_mesh_new_nomain(vert_total, edge_total, 0, corner_total, poly_total);
+ BKE_id_material_eval_ensure_default_slot(&mesh->id);
+ 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};
+ mesh->flag |= ME_AUTOSMOOTH;
+ mesh->smoothresh = DEG2RADF(180.0f);
+
+ int vert_offset = 0;
+ int edge_offset = 0;
+ int loop_offset = 0;
+ int poly_offset = 0;
+ for (const SplinePtr &spline : curve.splines()) {
+ for (const SplinePtr &profile_spline : profile_curve.splines()) {
+ spline_extrude_to_mesh_data(*spline,
+ *profile_spline,
+ verts,
+ edges,
+ loops,
+ polys,
+ vert_offset,
+ edge_offset,
+ loop_offset,
+ poly_offset);
+ }
+ }
+
+ BKE_mesh_calc_normals(mesh);
+
+ return mesh;
+}
+
+static CurveEval get_curve_single_vert()
+{
+ CurveEval curve;
+ std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>();
+ spline->add_point(float3(0), 0, 0.0f);
+ curve.add_spline(std::move(spline));
+
+ return curve;
+}
+
+static void geo_node_curve_to_mesh_exec(GeoNodeExecParams params)
+{
+ GeometrySet curve_set = params.extract_input<GeometrySet>("Curve");
+ GeometrySet profile_set = params.extract_input<GeometrySet>("Profile Curve");
+
+ curve_set = bke::geometry_set_realize_instances(curve_set);
+ profile_set = bke::geometry_set_realize_instances(profile_set);
+
+ if (!curve_set.has_curve()) {
+ params.set_output("Mesh", GeometrySet());
+ return;
+ }
+
+ const CurveEval *profile_curve = profile_set.get_curve_for_read();
+
+ static const CurveEval vert_curve = get_curve_single_vert();
+
+ Mesh *mesh = curve_to_mesh_calculate(*curve_set.get_curve_for_read(),
+ (profile_curve == nullptr) ? vert_curve : *profile_curve);
+ params.set_output("Mesh", GeometrySet::create_with_mesh(mesh));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_curve_to_mesh()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_CURVE_TO_MESH, "Curve to Mesh", NODE_CLASS_GEOMETRY, 0);
+ node_type_socket_templates(&ntype, geo_node_curve_to_mesh_in, geo_node_curve_to_mesh_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_curve_to_mesh_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc
new file mode 100644
index 00000000000..910adc467d6
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc
@@ -0,0 +1,678 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BLI_array.hh"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_customdata.h"
+#include "BKE_mesh.h"
+#include "BKE_pointcloud.h"
+#include "BKE_spline.hh"
+
+#include "node_geometry_util.hh"
+
+using blender::bke::CustomDataAttributes;
+
+/* Code from the mask modifier in MOD_mask.cc. */
+extern void copy_masked_vertices_to_new_mesh(const Mesh &src_mesh,
+ Mesh &dst_mesh,
+ blender::Span<int> vertex_map);
+extern void copy_masked_edges_to_new_mesh(const Mesh &src_mesh,
+ Mesh &dst_mesh,
+ blender::Span<int> vertex_map,
+ blender::Span<int> edge_map);
+extern void copy_masked_polys_to_new_mesh(const Mesh &src_mesh,
+ Mesh &dst_mesh,
+ blender::Span<int> vertex_map,
+ blender::Span<int> edge_map,
+ blender::Span<int> masked_poly_indices,
+ blender::Span<int> new_loop_starts);
+
+static bNodeSocketTemplate geo_node_delete_geometry_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_STRING, N_("Selection")},
+ {SOCK_BOOLEAN, N_("Invert")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_delete_geometry_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+
+template<typename T> static void copy_data(Span<T> data, MutableSpan<T> r_data, IndexMask mask)
+{
+ for (const int i_out : mask.index_range()) {
+ r_data[i_out] = data[mask[i_out]];
+ }
+}
+
+static void spline_copy_builtin_attributes(const Spline &spline,
+ Spline &r_spline,
+ const IndexMask mask)
+{
+ copy_data(spline.positions(), r_spline.positions(), mask);
+ copy_data(spline.radii(), r_spline.radii(), mask);
+ copy_data(spline.tilts(), r_spline.tilts(), mask);
+ switch (spline.type()) {
+ case Spline::Type::Poly:
+ break;
+ case Spline::Type::Bezier: {
+ const BezierSpline &src = static_cast<const BezierSpline &>(spline);
+ BezierSpline &dst = static_cast<BezierSpline &>(r_spline);
+ copy_data(src.handle_positions_left(), dst.handle_positions_left(), mask);
+ copy_data(src.handle_positions_right(), dst.handle_positions_right(), mask);
+ copy_data(src.handle_types_left(), dst.handle_types_left(), mask);
+ copy_data(src.handle_types_right(), dst.handle_types_right(), mask);
+ break;
+ }
+ case Spline::Type::NURBS: {
+ const NURBSpline &src = static_cast<const NURBSpline &>(spline);
+ NURBSpline &dst = static_cast<NURBSpline &>(r_spline);
+ copy_data(src.weights(), dst.weights(), mask);
+ break;
+ }
+ }
+}
+
+static void copy_dynamic_attributes(const CustomDataAttributes &src,
+ CustomDataAttributes &dst,
+ const IndexMask mask)
+{
+ src.foreach_attribute(
+ [&](StringRefNull name, const AttributeMetaData &meta_data) {
+ std::optional<GSpan> src_attribute = src.get_for_read(name);
+ BLI_assert(src_attribute);
+
+ if (!dst.create(name, meta_data.data_type)) {
+ /* Since the source spline of the same type had the attribute, adding it should work. */
+ BLI_assert_unreachable();
+ }
+
+ std::optional<GMutableSpan> new_attribute = dst.get_for_write(name);
+ BLI_assert(new_attribute);
+
+ attribute_math::convert_to_static_type(new_attribute->type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ copy_data(src_attribute->typed<T>(), new_attribute->typed<T>(), mask);
+ });
+ return true;
+ },
+ ATTR_DOMAIN_POINT);
+}
+
+static SplinePtr spline_delete(const Spline &spline, const IndexMask mask)
+{
+ SplinePtr new_spline = spline.copy_settings();
+ new_spline->resize(mask.size());
+
+ spline_copy_builtin_attributes(spline, *new_spline, mask);
+ copy_dynamic_attributes(spline.attributes, new_spline->attributes, mask);
+
+ return new_spline;
+}
+
+static std::unique_ptr<CurveEval> curve_delete(const CurveEval &input_curve,
+ const StringRef name,
+ const bool invert)
+{
+ Span<SplinePtr> input_splines = input_curve.splines();
+ std::unique_ptr<CurveEval> output_curve = std::make_unique<CurveEval>();
+
+ /* Keep track of which splines were copied to the result to copy spline domain attributes. */
+ Vector<int64_t> copied_splines;
+
+ if (input_curve.attributes.get_for_read(name)) {
+ GVArray_Typed<bool> selection = input_curve.attributes.get_for_read<bool>(name, false);
+ for (const int i : input_splines.index_range()) {
+ if (selection[i] == invert) {
+ output_curve->add_spline(input_splines[i]->copy());
+ copied_splines.append(i);
+ }
+ }
+ }
+ else {
+ /* Reuse index vector for each spline. */
+ Vector<int64_t> indices_to_copy;
+
+ for (const int i : input_splines.index_range()) {
+ const Spline &spline = *input_splines[i];
+ GVArray_Typed<bool> selection = spline.attributes.get_for_read<bool>(name, false);
+
+ indices_to_copy.clear();
+ for (const int i_point : IndexRange(spline.size())) {
+ if (selection[i_point] == invert) {
+ indices_to_copy.append(i_point);
+ }
+ }
+
+ /* Avoid creating an empty spline. */
+ if (indices_to_copy.is_empty()) {
+ continue;
+ }
+
+ SplinePtr new_spline = spline_delete(spline, IndexMask(indices_to_copy));
+ output_curve->add_spline(std::move(new_spline));
+ copied_splines.append(i);
+ }
+ }
+
+ if (copied_splines.is_empty()) {
+ return {};
+ }
+
+ output_curve->attributes.reallocate(output_curve->splines().size());
+ copy_dynamic_attributes(
+ input_curve.attributes, output_curve->attributes, IndexMask(copied_splines));
+
+ return output_curve;
+}
+
+static void delete_curve_selection(const CurveComponent &in_component,
+ CurveComponent &r_component,
+ const StringRef selection_name,
+ const bool invert)
+{
+ std::unique_ptr<CurveEval> r_curve = curve_delete(
+ *in_component.get_for_read(), selection_name, invert);
+ if (r_curve) {
+ r_component.replace(r_curve.release());
+ }
+ else {
+ r_component.clear();
+ }
+}
+
+static void delete_point_cloud_selection(const PointCloudComponent &in_component,
+ PointCloudComponent &out_component,
+ const StringRef selection_name,
+ const bool invert)
+{
+ const GVArray_Typed<bool> selection_attribute = in_component.attribute_get_for_read<bool>(
+ selection_name, ATTR_DOMAIN_POINT, false);
+ VArray_Span<bool> selection{selection_attribute};
+
+ const int total = selection.count(invert);
+ if (total == 0) {
+ out_component.clear();
+ return;
+ }
+ out_component.replace(BKE_pointcloud_new_nomain(total));
+
+ /* Invert the inversion, because this deletes the selected points instead of keeping them. */
+ copy_point_attributes_based_on_mask(in_component, out_component, selection, !invert);
+}
+
+static void compute_selected_vertices_from_vertex_selection(const VArray<bool> &vertex_selection,
+ const bool invert,
+ MutableSpan<int> r_vertex_map,
+ uint *r_num_selected_vertices)
+{
+ BLI_assert(vertex_selection.size() == r_vertex_map.size());
+
+ uint num_selected_vertices = 0;
+ for (const int i : r_vertex_map.index_range()) {
+ if (vertex_selection[i] != invert) {
+ r_vertex_map[i] = num_selected_vertices;
+ num_selected_vertices++;
+ }
+ else {
+ r_vertex_map[i] = -1;
+ }
+ }
+
+ *r_num_selected_vertices = num_selected_vertices;
+}
+
+static void compute_selected_edges_from_vertex_selection(const Mesh &mesh,
+ const VArray<bool> &vertex_selection,
+ const bool invert,
+ MutableSpan<int> r_edge_map,
+ uint *r_num_selected_edges)
+{
+ BLI_assert(mesh.totedge == r_edge_map.size());
+
+ uint num_selected_edges = 0;
+ for (const int i : IndexRange(mesh.totedge)) {
+ const MEdge &edge = mesh.medge[i];
+
+ /* Only add the edge if both vertices will be in the new mesh. */
+ if (vertex_selection[edge.v1] != invert && vertex_selection[edge.v2] != invert) {
+ r_edge_map[i] = num_selected_edges;
+ num_selected_edges++;
+ }
+ else {
+ r_edge_map[i] = -1;
+ }
+ }
+
+ *r_num_selected_edges = num_selected_edges;
+}
+
+static void compute_selected_polygons_from_vertex_selection(const Mesh &mesh,
+ const VArray<bool> &vertex_selection,
+ const bool invert,
+ Vector<int> &r_selected_poly_indices,
+ Vector<int> &r_loop_starts,
+ uint *r_num_selected_polys,
+ uint *r_num_selected_loops)
+{
+ BLI_assert(mesh.totvert == vertex_selection.size());
+
+ r_selected_poly_indices.reserve(mesh.totpoly);
+ r_loop_starts.reserve(mesh.totloop);
+
+ uint num_selected_loops = 0;
+ for (const int i : IndexRange(mesh.totpoly)) {
+ const MPoly &poly_src = mesh.mpoly[i];
+
+ bool all_verts_in_selection = true;
+ Span<MLoop> loops_src(&mesh.mloop[poly_src.loopstart], poly_src.totloop);
+ for (const MLoop &loop : loops_src) {
+ if (vertex_selection[loop.v] == invert) {
+ all_verts_in_selection = false;
+ break;
+ }
+ }
+
+ if (all_verts_in_selection) {
+ r_selected_poly_indices.append_unchecked(i);
+ r_loop_starts.append_unchecked(num_selected_loops);
+ num_selected_loops += poly_src.totloop;
+ }
+ }
+
+ *r_num_selected_polys = r_selected_poly_indices.size();
+ *r_num_selected_loops = num_selected_loops;
+}
+
+/**
+ * Checks for every edge if it is in `edge_selection`. If it is, then the two vertices of the edge
+ * are kept along with the edge.
+ */
+static void compute_selected_vertices_and_edges_from_edge_selection(
+ const Mesh &mesh,
+ const VArray<bool> &edge_selection,
+ const bool invert,
+ MutableSpan<int> r_vertex_map,
+ MutableSpan<int> r_edge_map,
+ uint *r_num_selected_vertices,
+ uint *r_num_selected_edges)
+{
+ BLI_assert(mesh.totedge == edge_selection.size());
+
+ uint num_selected_edges = 0;
+ uint num_selected_vertices = 0;
+ for (const int i : IndexRange(mesh.totedge)) {
+ const MEdge &edge = mesh.medge[i];
+ if (edge_selection[i] != invert) {
+ r_edge_map[i] = num_selected_edges;
+ num_selected_edges++;
+ if (r_vertex_map[edge.v1] == -1) {
+ r_vertex_map[edge.v1] = num_selected_vertices;
+ num_selected_vertices++;
+ }
+ if (r_vertex_map[edge.v2] == -1) {
+ r_vertex_map[edge.v2] = num_selected_vertices;
+ num_selected_vertices++;
+ }
+ }
+ else {
+ r_edge_map[i] = -1;
+ }
+ }
+
+ *r_num_selected_vertices = num_selected_vertices;
+ *r_num_selected_edges = num_selected_edges;
+}
+
+/**
+ * Checks for every 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 VArray<bool> &edge_selection,
+ const bool invert,
+ Vector<int> &r_selected_poly_indices,
+ Vector<int> &r_loop_starts,
+ uint *r_num_selected_polys,
+ uint *r_num_selected_loops)
+{
+ r_selected_poly_indices.reserve(mesh.totpoly);
+ r_loop_starts.reserve(mesh.totloop);
+
+ uint num_selected_loops = 0;
+ for (const int i : IndexRange(mesh.totpoly)) {
+ const MPoly &poly_src = mesh.mpoly[i];
+
+ bool all_edges_in_selection = true;
+ Span<MLoop> loops_src(&mesh.mloop[poly_src.loopstart], poly_src.totloop);
+ for (const MLoop &loop : loops_src) {
+ if (edge_selection[loop.e] == invert) {
+ all_edges_in_selection = false;
+ break;
+ }
+ }
+
+ if (all_edges_in_selection) {
+ r_selected_poly_indices.append_unchecked(i);
+ r_loop_starts.append_unchecked(num_selected_loops);
+ num_selected_loops += poly_src.totloop;
+ }
+ }
+
+ *r_num_selected_polys = r_selected_poly_indices.size();
+ *r_num_selected_loops = num_selected_loops;
+}
+
+/**
+ * Checks for every vertex if it is in `vertex_selection`. The polygons and edges are kept if all
+ * vertices of that polygon or edge are in the selection.
+ */
+static void compute_selected_mesh_data_from_vertex_selection(const Mesh &mesh,
+ const VArray<bool> &vertex_selection,
+ const bool invert,
+ MutableSpan<int> r_vertex_map,
+ MutableSpan<int> r_edge_map,
+ Vector<int> &r_selected_poly_indices,
+ Vector<int> &r_loop_starts,
+ uint *r_num_selected_vertices,
+ uint *r_num_selected_edges,
+ uint *r_num_selected_polys,
+ uint *r_num_selected_loops)
+{
+ compute_selected_vertices_from_vertex_selection(
+ vertex_selection, invert, r_vertex_map, r_num_selected_vertices);
+
+ compute_selected_edges_from_vertex_selection(
+ mesh, vertex_selection, invert, r_edge_map, r_num_selected_edges);
+
+ compute_selected_polygons_from_vertex_selection(mesh,
+ vertex_selection,
+ invert,
+ r_selected_poly_indices,
+ r_loop_starts,
+ r_num_selected_polys,
+ r_num_selected_loops);
+}
+
+/**
+ * Checks for every edge if it is in `edge_selection`. If it is, the vertices belonging to
+ * that edge are kept as well. The polygons are kept if all edges are in the selection.
+ */
+static void compute_selected_mesh_data_from_edge_selection(const Mesh &mesh,
+ const VArray<bool> &edge_selection,
+ const bool invert,
+ MutableSpan<int> r_vertex_map,
+ MutableSpan<int> r_edge_map,
+ Vector<int> &r_selected_poly_indices,
+ Vector<int> &r_loop_starts,
+ uint *r_num_selected_vertices,
+ uint *r_num_selected_edges,
+ uint *r_num_selected_polys,
+ uint *r_num_selected_loops)
+{
+ r_vertex_map.fill(-1);
+ compute_selected_vertices_and_edges_from_edge_selection(mesh,
+ edge_selection,
+ invert,
+ r_vertex_map,
+ r_edge_map,
+ r_num_selected_vertices,
+ r_num_selected_edges);
+ compute_selected_polygons_from_edge_selection(mesh,
+ edge_selection,
+ invert,
+ r_selected_poly_indices,
+ r_loop_starts,
+ r_num_selected_polys,
+ r_num_selected_loops);
+}
+
+/**
+ * Checks for every polygon if it is in `poly_selection`. If it is, the edges and vertices
+ * belonging to that polygon are kept as well.
+ */
+static void compute_selected_mesh_data_from_poly_selection(const Mesh &mesh,
+ const VArray<bool> &poly_selection,
+ const bool invert,
+ MutableSpan<int> r_vertex_map,
+ MutableSpan<int> r_edge_map,
+ Vector<int> &r_selected_poly_indices,
+ Vector<int> &r_loop_starts,
+ uint *r_num_selected_vertices,
+ uint *r_num_selected_edges,
+ uint *r_num_selected_polys,
+ uint *r_num_selected_loops)
+{
+ BLI_assert(mesh.totpoly == poly_selection.size());
+ BLI_assert(mesh.totedge == r_edge_map.size());
+ r_vertex_map.fill(-1);
+ r_edge_map.fill(-1);
+
+ r_selected_poly_indices.reserve(mesh.totpoly);
+ r_loop_starts.reserve(mesh.totloop);
+
+ uint num_selected_loops = 0;
+ uint num_selected_vertices = 0;
+ uint num_selected_edges = 0;
+ for (const int i : IndexRange(mesh.totpoly)) {
+ const MPoly &poly_src = mesh.mpoly[i];
+ /* We keep this one. */
+ if (poly_selection[i] != invert) {
+ r_selected_poly_indices.append_unchecked(i);
+ r_loop_starts.append_unchecked(num_selected_loops);
+ num_selected_loops += poly_src.totloop;
+
+ /* Add the vertices and the edges. */
+ Span<MLoop> loops_src(&mesh.mloop[poly_src.loopstart], poly_src.totloop);
+ for (const MLoop &loop : loops_src) {
+ /* Check first if it has not yet been added. */
+ if (r_vertex_map[loop.v] == -1) {
+ r_vertex_map[loop.v] = num_selected_vertices;
+ num_selected_vertices++;
+ }
+ if (r_edge_map[loop.e] == -1) {
+ r_edge_map[loop.e] = num_selected_edges;
+ num_selected_edges++;
+ }
+ }
+ }
+ }
+ *r_num_selected_vertices = num_selected_vertices;
+ *r_num_selected_edges = num_selected_edges;
+ *r_num_selected_polys = r_selected_poly_indices.size();
+ *r_num_selected_loops = num_selected_loops;
+}
+
+using FillMapsFunction = void (*)(const Mesh &mesh,
+ const VArray<bool> &selection,
+ const bool invert,
+ MutableSpan<int> r_vertex_map,
+ MutableSpan<int> r_edge_map,
+ Vector<int> &r_selected_poly_indices,
+ Vector<int> &r_loop_starts,
+ uint *r_num_selected_vertices,
+ uint *r_num_selected_edges,
+ uint *r_num_selected_polys,
+ uint *r_num_selected_loops);
+
+/**
+ * Delete the parts of the mesh that are in the selection. The `fill_maps_function`
+ * depends on the selection type: vertices, edges or faces.
+ */
+static Mesh *delete_mesh_selection(const Mesh &mesh_in,
+ const VArray<bool> &selection,
+ const bool invert,
+ FillMapsFunction fill_maps_function)
+{
+ Array<int> vertex_map(mesh_in.totvert);
+ uint num_selected_vertices;
+
+ Array<int> edge_map(mesh_in.totedge);
+ uint num_selected_edges;
+
+ Vector<int> selected_poly_indices;
+ Vector<int> new_loop_starts;
+ uint num_selected_polys;
+ uint num_selected_loops;
+
+ /* Fill all the maps based on the selection. We delete everything
+ * in the selection instead of keeping it, so we need to invert it. */
+ fill_maps_function(mesh_in,
+ selection,
+ !invert,
+ vertex_map,
+ edge_map,
+ selected_poly_indices,
+ new_loop_starts,
+ &num_selected_vertices,
+ &num_selected_edges,
+ &num_selected_polys,
+ &num_selected_loops);
+
+ Mesh *result = BKE_mesh_new_nomain_from_template(&mesh_in,
+ num_selected_vertices,
+ num_selected_edges,
+ 0,
+ num_selected_loops,
+ num_selected_polys);
+
+ /* Copy the selected parts of the mesh over to the new mesh. */
+ copy_masked_vertices_to_new_mesh(mesh_in, *result, vertex_map);
+ copy_masked_edges_to_new_mesh(mesh_in, *result, vertex_map, edge_map);
+ copy_masked_polys_to_new_mesh(
+ mesh_in, *result, vertex_map, edge_map, selected_poly_indices, new_loop_starts);
+ BKE_mesh_calc_edges_loose(result);
+ /* Tag to recalculate normals later. */
+ result->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
+
+ return result;
+}
+
+static AttributeDomain get_mesh_selection_domain(MeshComponent &component, const StringRef name)
+{
+ std::optional<AttributeMetaData> selection_attribute = component.attribute_get_meta_data(name);
+ if (!selection_attribute) {
+ /* The node will not do anything in this case, but this function must return something. */
+ return ATTR_DOMAIN_POINT;
+ }
+
+ /* Corners can't be deleted separately, so interpolate corner attributes
+ * to the face domain. Note that this choice is somewhat arbitrary. */
+ if (selection_attribute->domain == ATTR_DOMAIN_CORNER) {
+ return ATTR_DOMAIN_FACE;
+ }
+
+ return selection_attribute->domain;
+}
+
+static void delete_mesh_selection(MeshComponent &component,
+ const Mesh &mesh_in,
+ const StringRef selection_name,
+ const bool invert)
+{
+ /* Figure out the best domain to use. */
+ const AttributeDomain selection_domain = get_mesh_selection_domain(component, selection_name);
+
+ /* This already checks if the attribute exists, and displays a warning in that case. */
+ GVArray_Typed<bool> selection = component.attribute_get_for_read<bool>(
+ selection_name, selection_domain, false);
+
+ /* Check if there is anything to delete. */
+ bool delete_nothing = true;
+ for (const int i : selection.index_range()) {
+ if (selection[i] != invert) {
+ delete_nothing = false;
+ break;
+ }
+ }
+ if (delete_nothing) {
+ return;
+ }
+
+ Mesh *mesh_out;
+ switch (selection_domain) {
+ case ATTR_DOMAIN_POINT:
+ mesh_out = delete_mesh_selection(
+ mesh_in, selection, invert, compute_selected_mesh_data_from_vertex_selection);
+ break;
+ case ATTR_DOMAIN_EDGE:
+ mesh_out = delete_mesh_selection(
+ mesh_in, selection, invert, compute_selected_mesh_data_from_edge_selection);
+ break;
+ case ATTR_DOMAIN_FACE:
+ mesh_out = delete_mesh_selection(
+ mesh_in, selection, invert, compute_selected_mesh_data_from_poly_selection);
+ break;
+ default:
+ BLI_assert_unreachable();
+ mesh_out = nullptr;
+ break;
+ }
+ component.replace_mesh_but_keep_vertex_group_names(mesh_out);
+}
+
+static void geo_node_delete_geometry_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ geometry_set = bke::geometry_set_realize_instances(geometry_set);
+
+ const bool invert = params.extract_input<bool>("Invert");
+ const std::string selection_name = params.extract_input<std::string>("Selection");
+ if (selection_name.empty()) {
+ params.set_output("Geometry", std::move(geometry_set));
+ return;
+ }
+
+ GeometrySet out_set(geometry_set);
+ if (geometry_set.has<PointCloudComponent>()) {
+ delete_point_cloud_selection(*geometry_set.get_component_for_read<PointCloudComponent>(),
+ out_set.get_component_for_write<PointCloudComponent>(),
+ selection_name,
+ invert);
+ }
+ if (geometry_set.has<MeshComponent>()) {
+ delete_mesh_selection(out_set.get_component_for_write<MeshComponent>(),
+ *geometry_set.get_mesh_for_read(),
+ selection_name,
+ invert);
+ }
+ if (geometry_set.has<CurveComponent>()) {
+ delete_curve_selection(*geometry_set.get_component_for_read<CurveComponent>(),
+ out_set.get_component_for_write<CurveComponent>(),
+ selection_name,
+ invert);
+ }
+
+ params.set_output("Geometry", std::move(out_set));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_delete_geometry()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_DELETE_GEOMETRY, "Delete Geometry", NODE_CLASS_GEOMETRY, 0);
+ node_type_socket_templates(&ntype, geo_node_delete_geometry_in, geo_node_delete_geometry_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_delete_geometry_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc
index 1c794d1f43e..740b828d503 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc
@@ -14,9 +14,6 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "BLI_math_base.h"
-#include "BLI_math_rotation.h"
-
#include "DNA_modifier_types.h"
#include "node_geometry_util.hh"
@@ -47,6 +44,7 @@ static bNodeSocketTemplate geo_node_edge_split_out[] = {
};
namespace blender::nodes {
+
static void geo_node_edge_split_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
@@ -85,6 +83,7 @@ static void geo_node_edge_split_exec(GeoNodeExecParams params)
params.set_output("Geometry", std::move(geometry_set));
}
+
} // namespace blender::nodes
void register_node_type_geo_edge_split()
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_material.cc b/source/blender/nodes/geometry/nodes/node_geo_input_material.cc
new file mode 100644
index 00000000000..6bad71a62a2
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_material.cc
@@ -0,0 +1,51 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_input_material_out[] = {
+ {SOCK_MATERIAL, N_("Material")},
+ {-1, ""},
+};
+
+static void geo_node_input_material_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "material", 0, "", ICON_NONE);
+}
+
+namespace blender::nodes {
+
+static void geo_node_input_material_exec(GeoNodeExecParams params)
+{
+ Material *material = (Material *)params.node().id;
+ params.set_output("Material", material);
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_input_material()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_INPUT_MATERIAL, "Material", NODE_CLASS_INPUT, 0);
+ node_type_socket_templates(&ntype, nullptr, geo_node_input_material_out);
+ ntype.draw_buttons = geo_node_input_material_layout;
+ ntype.geometry_node_execute = blender::nodes::geo_node_input_material_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_is_viewport.cc b/source/blender/nodes/geometry/nodes/node_geo_is_viewport.cc
index 667beaa1722..ec875b9f983 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_is_viewport.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_is_viewport.cc
@@ -14,10 +14,10 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "node_geometry_util.hh"
-
#include "DEG_depsgraph_query.h"
+#include "node_geometry_util.hh"
+
static bNodeSocketTemplate geo_node_is_viewport_out[] = {
{SOCK_BOOLEAN, N_("Is Viewport")},
{-1, ""},
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 52512769a47..adfd924f185 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
@@ -14,9 +14,11 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#include "BKE_material.h"
#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
#include "BKE_pointcloud.h"
+#include "BKE_spline.hh"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
@@ -56,6 +58,8 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<const MeshComponent
int64_t cd_dirty_edge = 0;
int64_t cd_dirty_loop = 0;
+ VectorSet<Material *> materials;
+
for (const MeshComponent *mesh_component : src_components) {
const Mesh *mesh = mesh_component->get_for_read();
totverts += mesh->totvert;
@@ -66,12 +70,22 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<const MeshComponent
cd_dirty_poly |= mesh->runtime.cd_dirty_poly;
cd_dirty_edge |= mesh->runtime.cd_dirty_edge;
cd_dirty_loop |= mesh->runtime.cd_dirty_loop;
+
+ for (const int slot_index : IndexRange(mesh->totcol)) {
+ Material *material = mesh->mat[slot_index];
+ materials.add(material);
+ }
}
const Mesh *first_input_mesh = src_components[0]->get_for_read();
Mesh *new_mesh = BKE_mesh_new_nomain(totverts, totedges, 0, totloops, totpolys);
BKE_mesh_copy_settings(new_mesh, first_input_mesh);
+ for (const int i : IndexRange(materials.size())) {
+ Material *material = materials[i];
+ BKE_id_material_eval_assign(&new_mesh->id, i + 1, material);
+ }
+
new_mesh->runtime.cd_dirty_vert = cd_dirty_vert;
new_mesh->runtime.cd_dirty_poly = cd_dirty_poly;
new_mesh->runtime.cd_dirty_edge = cd_dirty_edge;
@@ -87,6 +101,13 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<const MeshComponent
continue;
}
+ Array<int> material_index_map(mesh->totcol);
+ for (const int i : IndexRange(mesh->totcol)) {
+ Material *material = mesh->mat[i];
+ const int new_material_index = materials.index_of(material);
+ material_index_map[i] = new_material_index;
+ }
+
for (const int i : IndexRange(mesh->totvert)) {
const MVert &old_vert = mesh->mvert[i];
MVert &new_vert = new_mesh->mvert[vert_offset + i];
@@ -112,6 +133,13 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<const MeshComponent
MPoly &new_poly = new_mesh->mpoly[poly_offset + i];
new_poly = old_poly;
new_poly.loopstart += loop_offset;
+ if (old_poly.mat_nr >= 0 && old_poly.mat_nr < mesh->totcol) {
+ new_poly.mat_nr = material_index_map[new_poly.mat_nr];
+ }
+ else {
+ /* The material index was invalid before. */
+ new_poly.mat_nr = 0;
+ }
}
vert_offset += mesh->totvert;
@@ -149,10 +177,10 @@ static void determine_final_data_type_and_domain(Span<const GeometryComponent *>
Vector<CustomDataType> data_types;
Vector<AttributeDomain> domains;
for (const GeometryComponent *component : components) {
- ReadAttributePtr attribute = component->attribute_try_get_for_read(attribute_name);
+ ReadAttributeLookup attribute = component->attribute_try_get_for_read(attribute_name);
if (attribute) {
- data_types.append(attribute->custom_data_type());
- domains.append(attribute->domain());
+ data_types.append(bke::cpp_type_to_custom_data_type(attribute.varray->type()));
+ domains.append(attribute.domain);
}
}
@@ -164,7 +192,7 @@ static void fill_new_attribute(Span<const GeometryComponent *> src_components,
StringRef attribute_name,
const CustomDataType data_type,
const AttributeDomain domain,
- fn::GMutableSpan dst_span)
+ GMutableSpan dst_span)
{
const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(data_type);
BLI_assert(cpp_type != nullptr);
@@ -175,10 +203,10 @@ static void fill_new_attribute(Span<const GeometryComponent *> src_components,
if (domain_size == 0) {
continue;
}
- ReadAttributePtr read_attribute = component->attribute_get_for_read(
+ GVArrayPtr read_attribute = component->attribute_get_for_read(
attribute_name, domain, data_type, nullptr);
- fn::GSpan src_span = read_attribute->get_span();
+ GVArray_GSpan src_span{*read_attribute};
const void *src_buffer = src_span.data();
void *dst_buffer = dst_span[offset];
cpp_type->copy_to_initialized_n(src_buffer, dst_buffer, domain_size);
@@ -201,16 +229,14 @@ static void join_attributes(Span<const GeometryComponent *> src_components,
AttributeDomain domain;
determine_final_data_type_and_domain(src_components, attribute_name, &data_type, &domain);
- OutputAttributePtr write_attribute = result.attribute_try_get_for_output(
+ OutputAttribute write_attribute = result.attribute_try_get_for_output_only(
attribute_name, domain, data_type);
- if (!write_attribute ||
- &write_attribute->cpp_type() != bke::custom_data_type_to_cpp_type(data_type) ||
- write_attribute->domain() != domain) {
+ if (!write_attribute) {
continue;
}
- fn::GMutableSpan dst_span = write_attribute->get_span_for_write_only();
+ GMutableSpan dst_span = write_attribute.as_span();
fill_new_attribute(src_components, attribute_name, data_type, domain, dst_span);
- write_attribute.apply_span_and_save();
+ write_attribute.save();
}
}
@@ -244,12 +270,30 @@ static void join_components(Span<const PointCloudComponent *> src_components, Ge
static void join_components(Span<const InstancesComponent *> src_components, GeometrySet &result)
{
InstancesComponent &dst_component = result.get_component_for_write<InstancesComponent>();
- for (const InstancesComponent *component : src_components) {
- const int size = component->instances_amount();
- Span<InstancedData> instanced_data = component->instanced_data();
- Span<float4x4> transforms = component->transforms();
- for (const int i : IndexRange(size)) {
- dst_component.add_instance(instanced_data[i], transforms[i]);
+
+ int tot_instances = 0;
+ for (const InstancesComponent *src_component : src_components) {
+ tot_instances += src_component->instances_amount();
+ }
+ dst_component.reserve(tot_instances);
+
+ for (const InstancesComponent *src_component : src_components) {
+ Span<InstanceReference> src_references = src_component->references();
+ Array<int> handle_map(src_references.size());
+ for (const int src_handle : src_references.index_range()) {
+ handle_map[src_handle] = dst_component.add_reference(src_references[src_handle]);
+ }
+
+ Span<float4x4> src_transforms = src_component->instance_transforms();
+ Span<int> src_ids = src_component->instance_ids();
+ Span<int> src_reference_handles = src_component->instance_reference_handles();
+
+ for (const int i : src_transforms.index_range()) {
+ const int src_handle = src_reference_handles[i];
+ const int dst_handle = handle_map[src_handle];
+ const float4x4 &transform = src_transforms[i];
+ const int id = src_ids[i];
+ dst_component.add_instance(dst_handle, transform, id);
}
}
}
@@ -262,6 +306,48 @@ static void join_components(Span<const VolumeComponent *> src_components, Geomet
UNUSED_VARS(src_components, dst_component);
}
+static void join_curve_components(MutableSpan<GeometrySet> src_geometry_sets, GeometrySet &result)
+{
+ Vector<CurveComponent *> src_components;
+ for (GeometrySet &geometry_set : src_geometry_sets) {
+ if (geometry_set.has_curve()) {
+ /* Retrieving with write access seems counterintuitive, but it can allow avoiding a copy
+ * in the case where the input spline has no other users, because the splines can be
+ * moved from the source curve rather than copied from a read-only source. Retrieving
+ * the curve for write will make a copy only when it has a user elsewhere. */
+ CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>();
+ src_components.append(&component);
+ }
+ }
+
+ if (src_components.size() == 0) {
+ return;
+ }
+ if (src_components.size() == 1) {
+ result.add(*src_components[0]);
+ return;
+ }
+
+ CurveComponent &dst_component = result.get_component_for_write<CurveComponent>();
+ CurveEval *dst_curve = new CurveEval();
+ for (CurveComponent *component : src_components) {
+ CurveEval *src_curve = component->get_for_write();
+ for (SplinePtr &spline : src_curve->splines()) {
+ dst_curve->add_spline(std::move(spline));
+ }
+ }
+
+ /* For now, remove all custom attributes, since they might have different types,
+ * or an attribute might not exist on all splines. */
+ dst_curve->attributes.reallocate(dst_curve->splines().size());
+ CustomData_reset(&dst_curve->attributes.data);
+ for (SplinePtr &spline : dst_curve->splines()) {
+ CustomData_reset(&spline->attributes.data);
+ }
+
+ dst_component.replace(dst_curve);
+}
+
template<typename Component>
static void join_component_type(Span<GeometrySet> src_geometry_sets, GeometrySet &result)
{
@@ -292,6 +378,7 @@ static void geo_node_join_geometry_exec(GeoNodeExecParams params)
join_component_type<PointCloudComponent>(geometry_sets, geometry_set_result);
join_component_type<InstancesComponent>(geometry_sets, geometry_set_result);
join_component_type<VolumeComponent>(geometry_sets, geometry_set_result);
+ join_curve_components(geometry_sets, geometry_set_result);
params.set_output("Geometry", std::move(geometry_set_result));
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc b/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc
new file mode 100644
index 00000000000..02b2d685bdd
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc
@@ -0,0 +1,107 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "node_geometry_util.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_material.h"
+
+static bNodeSocketTemplate geo_node_material_assign_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_MATERIAL,
+ N_("Material"),
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ PROP_NONE,
+ SOCK_HIDE_LABEL},
+ {SOCK_STRING, N_("Selection")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_material_assign_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+
+static void assign_material_to_faces(Mesh &mesh, const VArray<bool> &face_mask, Material *material)
+{
+ int new_material_index = -1;
+ for (const int i : IndexRange(mesh.totcol)) {
+ Material *other_material = mesh.mat[i];
+ if (other_material == material) {
+ new_material_index = i;
+ break;
+ }
+ }
+ if (new_material_index == -1) {
+ /* Append a new material index. */
+ new_material_index = mesh.totcol;
+ BKE_id_material_eval_assign(&mesh.id, new_material_index + 1, material);
+ }
+
+ mesh.mpoly = (MPoly *)CustomData_duplicate_referenced_layer(&mesh.pdata, CD_MPOLY, mesh.totpoly);
+ for (const int i : IndexRange(mesh.totpoly)) {
+ if (face_mask[i]) {
+ MPoly &poly = mesh.mpoly[i];
+ poly.mat_nr = new_material_index;
+ }
+ }
+}
+
+static void geo_node_material_assign_exec(GeoNodeExecParams params)
+{
+ Material *material = params.extract_input<Material *>("Material");
+ const std::string mask_name = params.extract_input<std::string>("Selection");
+
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+ geometry_set = geometry_set_realize_instances(geometry_set);
+
+ if (geometry_set.has<MeshComponent>()) {
+ MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
+ Mesh *mesh = mesh_component.get_for_write();
+ if (mesh != nullptr) {
+ GVArray_Typed<bool> face_mask = mesh_component.attribute_get_for_read<bool>(
+ mask_name, ATTR_DOMAIN_FACE, true);
+ assign_material_to_faces(*mesh, face_mask, material);
+ }
+ }
+
+ params.set_output("Geometry", std::move(geometry_set));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_material_assign()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_MATERIAL_ASSIGN, "Material Assign", NODE_CLASS_GEOMETRY, 0);
+ node_type_socket_templates(&ntype, geo_node_material_assign_in, geo_node_material_assign_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_material_assign_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc b/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc
new file mode 100644
index 00000000000..40ecab98dea
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc
@@ -0,0 +1,75 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "node_geometry_util.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_material.h"
+
+static bNodeSocketTemplate geo_node_material_replace_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_MATERIAL, N_("Old")},
+ {SOCK_MATERIAL, N_("New")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_material_replace_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+
+static void geo_node_material_replace_exec(GeoNodeExecParams params)
+{
+ Material *old_material = params.extract_input<Material *>("Old");
+ Material *new_material = params.extract_input<Material *>("New");
+
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ geometry_set = geometry_set_realize_instances(geometry_set);
+
+ if (geometry_set.has<MeshComponent>()) {
+ MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
+ Mesh *mesh = mesh_component.get_for_write();
+ if (mesh != nullptr) {
+ for (const int i : IndexRange(mesh->totcol)) {
+ if (mesh->mat[i] == old_material) {
+ mesh->mat[i] = new_material;
+ }
+ }
+ }
+ }
+
+ params.set_output("Geometry", std::move(geometry_set));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_material_replace()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_MATERIAL_REPLACE, "Material Replace", NODE_CLASS_GEOMETRY, 0);
+ node_type_socket_templates(&ntype, geo_node_material_replace_in, geo_node_material_replace_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_material_replace_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 01016ec9b44..2915a17d2c8 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
@@ -17,6 +17,7 @@
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
+#include "BKE_material.h"
#include "BKE_mesh.h"
#include "UI_interface.h"
@@ -119,6 +120,7 @@ static Mesh *create_circle_mesh(const float radius,
0,
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};
@@ -161,6 +163,7 @@ static Mesh *create_circle_mesh(const float radius,
MEdge &edge = edges[verts_num + i];
edge.v1 = verts_num;
edge.v2 = i;
+ edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
}
}
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 e9228a2942b..925ed0f8da8 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
@@ -17,14 +17,12 @@
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
-#include "BKE_lib_id.h"
+#include "BKE_material.h"
#include "BKE_mesh.h"
#include "UI_interface.h"
#include "UI_resources.h"
-#include "bmesh.h"
-
#include "node_geometry_util.hh"
static bNodeSocketTemplate geo_node_mesh_primitive_cone_in[] = {
@@ -189,45 +187,358 @@ static int face_total(const GeometryNodeMeshCircleFillType fill_type,
return face_total;
}
+static void calculate_uvs(Mesh *mesh,
+ const bool top_is_point,
+ const bool bottom_is_point,
+ const int verts_num,
+ const GeometryNodeMeshCircleFillType fill_type)
+{
+ MeshComponent mesh_component;
+ mesh_component.replace(mesh, GeometryOwnershipType::Editable);
+ OutputAttribute_Typed<float2> uv_attribute =
+ mesh_component.attribute_try_get_for_output_only<float2>("uv_map", ATTR_DOMAIN_CORNER);
+ MutableSpan<float2> uvs = uv_attribute.as_span();
+
+ Array<float2> circle(verts_num);
+ float angle = 0.0f;
+ const float angle_delta = 2.0f * M_PI / static_cast<float>(verts_num);
+ for (const int i : IndexRange(verts_num)) {
+ circle[i].x = std::cos(angle) * 0.225f + 0.25f;
+ circle[i].y = std::sin(angle) * 0.225f + 0.25f;
+ angle += angle_delta;
+ }
+
+ int loop_index = 0;
+ if (!top_is_point) {
+ if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
+ for (const int i : IndexRange(verts_num)) {
+ uvs[loop_index++] = circle[i];
+ }
+ }
+ else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
+ for (const int i : IndexRange(verts_num)) {
+ uvs[loop_index++] = circle[i];
+ uvs[loop_index++] = circle[(i + 1) % verts_num];
+ uvs[loop_index++] = float2(0.25f, 0.25f);
+ }
+ }
+ }
+
+ /* Create side corners and faces. */
+ if (!top_is_point && !bottom_is_point) {
+ const float bottom = (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NONE) ? 0.0f : 0.5f;
+ /* Quads connect the top and bottom. */
+ for (const int i : IndexRange(verts_num)) {
+ const float vert = static_cast<float>(i);
+ uvs[loop_index++] = float2(vert / verts_num, bottom);
+ uvs[loop_index++] = float2(vert / verts_num, 1.0f);
+ uvs[loop_index++] = float2((vert + 1.0f) / verts_num, 1.0f);
+ uvs[loop_index++] = float2((vert + 1.0f) / verts_num, bottom);
+ }
+ }
+ else {
+ /* Triangles connect the top and bottom section. */
+ if (!top_is_point) {
+ for (const int i : IndexRange(verts_num)) {
+ uvs[loop_index++] = circle[i] + float2(0.5f, 0.0f);
+ uvs[loop_index++] = float2(0.75f, 0.25f);
+ uvs[loop_index++] = circle[(i + 1) % verts_num] + float2(0.5f, 0.0f);
+ }
+ }
+ else {
+ BLI_assert(!bottom_is_point);
+ for (const int i : IndexRange(verts_num)) {
+ uvs[loop_index++] = circle[i];
+ uvs[loop_index++] = circle[(i + 1) % verts_num];
+ uvs[loop_index++] = float2(0.25f, 0.25f);
+ }
+ }
+ }
+
+ /* Create bottom corners and faces. */
+ if (!bottom_is_point) {
+ if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
+ for (const int i : IndexRange(verts_num)) {
+ /* Go backwards because of reversed face normal. */
+ uvs[loop_index++] = circle[verts_num - 1 - i] + float2(0.5f, 0.0f);
+ }
+ }
+ else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
+ for (const int i : IndexRange(verts_num)) {
+ uvs[loop_index++] = circle[i] + float2(0.5f, 0.0f);
+ uvs[loop_index++] = float2(0.75f, 0.25f);
+ uvs[loop_index++] = circle[(i + 1) % verts_num] + float2(0.5f, 0.0f);
+ }
+ }
+ }
+
+ uv_attribute.save();
+}
+
Mesh *create_cylinder_or_cone_mesh(const float radius_top,
const float radius_bottom,
const float depth,
const int verts_num,
const GeometryNodeMeshCircleFillType fill_type)
{
- const float4x4 transform = float4x4::identity();
-
const bool top_is_point = radius_top == 0.0f;
const bool bottom_is_point = radius_bottom == 0.0f;
+ const float height = depth * 0.5f;
+ /* Handle the case of a line / single point before everything else to avoid
+ * the need to check for it later. */
+ if (top_is_point && bottom_is_point) {
+ const bool single_vertex = height == 0.0f;
+ Mesh *mesh = BKE_mesh_new_nomain(single_vertex ? 1 : 2, single_vertex ? 0 : 1, 0, 0, 0);
+ copy_v3_v3(mesh->mvert[0].co, float3(0.0f, 0.0f, height));
+ if (single_vertex) {
+ const short up[3] = {0, 0, SHRT_MAX};
+ copy_v3_v3_short(mesh->mvert[0].no, up);
+ return mesh;
+ }
+ copy_v3_v3(mesh->mvert[1].co, float3(0.0f, 0.0f, -height));
+ mesh->medge[0].v1 = 0;
+ mesh->medge[0].v2 = 1;
+ mesh->medge[0].flag |= ME_LOOSEEDGE;
+ BKE_mesh_calc_normals(mesh);
+ return mesh;
+ }
- const BMeshCreateParams bmcp = {true};
- const BMAllocTemplate allocsize = {
+ Mesh *mesh = BKE_mesh_new_nomain(
vert_total(fill_type, verts_num, top_is_point, bottom_is_point),
edge_total(fill_type, verts_num, top_is_point, bottom_is_point),
+ 0,
corner_total(fill_type, verts_num, top_is_point, bottom_is_point),
- face_total(fill_type, verts_num, top_is_point, bottom_is_point)};
- BMesh *bm = BM_mesh_create(&allocsize, &bmcp);
-
- const bool cap_end = (fill_type != GEO_NODE_MESH_CIRCLE_FILL_NONE);
- const bool cap_tri = (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN);
- BMO_op_callf(bm,
- BMO_FLAG_DEFAULTS,
- "create_cone segments=%i diameter1=%f diameter2=%f cap_ends=%b "
- "cap_tris=%b depth=%f matrix=%m4 calc_uvs=%b",
- verts_num,
- radius_bottom,
- radius_top,
- cap_end,
- cap_tri,
- depth,
- transform.values,
- true);
-
- BMeshToMeshParams params{};
- params.calc_object_remap = false;
- Mesh *mesh = (Mesh *)BKE_id_new_nomain(ID_ME, nullptr);
- BM_mesh_bm_to_me(nullptr, bm, mesh, &params);
- BM_mesh_free(bm);
+ face_total(fill_type, verts_num, top_is_point, bottom_is_point));
+ BKE_id_material_eval_ensure_default_slot(&mesh->id);
+ MutableSpan<MVert> verts{mesh->mvert, mesh->totvert};
+ MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop};
+ MutableSpan<MEdge> edges{mesh->medge, mesh->totedge};
+ MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly};
+
+ /* Calculate vertex positions. */
+ const int top_verts_start = 0;
+ const int bottom_verts_start = top_verts_start + (!top_is_point ? verts_num : 1);
+ float angle = 0.0f;
+ const float angle_delta = 2.0f * M_PI / static_cast<float>(verts_num);
+ for (const int i : IndexRange(verts_num)) {
+ const float x = std::cos(angle);
+ const float y = std::sin(angle);
+ if (!top_is_point) {
+ copy_v3_v3(verts[top_verts_start + i].co, float3(x * radius_top, y * radius_top, height));
+ }
+ if (!bottom_is_point) {
+ copy_v3_v3(verts[bottom_verts_start + i].co,
+ float3(x * radius_bottom, y * radius_bottom, -height));
+ }
+ angle += angle_delta;
+ }
+ if (top_is_point) {
+ copy_v3_v3(verts[top_verts_start].co, float3(0.0f, 0.0f, height));
+ }
+ if (bottom_is_point) {
+ copy_v3_v3(verts[bottom_verts_start].co, float3(0.0f, 0.0f, -height));
+ }
+
+ /* Add center vertices for the triangle fans at the end. */
+ const int top_center_vert_index = bottom_verts_start + (bottom_is_point ? 1 : verts_num);
+ const int bottom_center_vert_index = top_center_vert_index + (top_is_point ? 0 : 1);
+ if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
+ if (!top_is_point) {
+ copy_v3_v3(verts[top_center_vert_index].co, float3(0.0f, 0.0f, height));
+ }
+ if (!bottom_is_point) {
+ copy_v3_v3(verts[bottom_center_vert_index].co, float3(0.0f, 0.0f, -height));
+ }
+ }
+
+ /* Create top edges. */
+ const int top_edges_start = 0;
+ const int top_fan_edges_start = (!top_is_point &&
+ fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) ?
+ top_edges_start + verts_num :
+ top_edges_start;
+ if (!top_is_point) {
+ for (const int i : IndexRange(verts_num)) {
+ MEdge &edge = edges[top_edges_start + i];
+ edge.v1 = top_verts_start + i;
+ edge.v2 = top_verts_start + (i + 1) % verts_num;
+ edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
+ }
+ if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
+ for (const int i : IndexRange(verts_num)) {
+ MEdge &edge = edges[top_fan_edges_start + i];
+ edge.v1 = top_center_vert_index;
+ edge.v2 = top_verts_start + i;
+ edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
+ }
+ }
+ }
+
+ /* Create connecting edges. */
+ const int connecting_edges_start = top_fan_edges_start + (!top_is_point ? verts_num : 0);
+ for (const int i : IndexRange(verts_num)) {
+ MEdge &edge = edges[connecting_edges_start + i];
+ edge.v1 = top_verts_start + (!top_is_point ? i : 0);
+ edge.v2 = bottom_verts_start + (!bottom_is_point ? i : 0);
+ edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
+ }
+
+ /* Create bottom edges. */
+ const int bottom_edges_start = connecting_edges_start + verts_num;
+ const int bottom_fan_edges_start = (!bottom_is_point &&
+ fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) ?
+ bottom_edges_start + verts_num :
+ bottom_edges_start;
+ if (!bottom_is_point) {
+ for (const int i : IndexRange(verts_num)) {
+ MEdge &edge = edges[bottom_edges_start + i];
+ edge.v1 = bottom_verts_start + i;
+ edge.v2 = bottom_verts_start + (i + 1) % verts_num;
+ edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
+ }
+ if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
+ for (const int i : IndexRange(verts_num)) {
+ MEdge &edge = edges[bottom_fan_edges_start + i];
+ edge.v1 = bottom_center_vert_index;
+ edge.v2 = bottom_verts_start + i;
+ edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
+ }
+ }
+ }
+
+ /* Create top corners and faces. */
+ int loop_index = 0;
+ int poly_index = 0;
+ if (!top_is_point) {
+ if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
+ MPoly &poly = polys[poly_index++];
+ poly.loopstart = loop_index;
+ poly.totloop = verts_num;
+
+ for (const int i : IndexRange(verts_num)) {
+ MLoop &loop = loops[loop_index++];
+ loop.v = top_verts_start + i;
+ loop.e = top_edges_start + i;
+ }
+ }
+ else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
+ for (const int i : IndexRange(verts_num)) {
+ MPoly &poly = polys[poly_index++];
+ poly.loopstart = loop_index;
+ poly.totloop = 3;
+
+ MLoop &loop_a = loops[loop_index++];
+ loop_a.v = top_verts_start + i;
+ loop_a.e = top_edges_start + i;
+ MLoop &loop_b = loops[loop_index++];
+ loop_b.v = top_verts_start + (i + 1) % verts_num;
+ loop_b.e = top_fan_edges_start + (i + 1) % verts_num;
+ MLoop &loop_c = loops[loop_index++];
+ loop_c.v = top_center_vert_index;
+ loop_c.e = top_fan_edges_start + i;
+ }
+ }
+ }
+
+ /* Create side corners and faces. */
+ if (!top_is_point && !bottom_is_point) {
+ /* Quads connect the top and bottom. */
+ for (const int i : IndexRange(verts_num)) {
+ MPoly &poly = polys[poly_index++];
+ poly.loopstart = loop_index;
+ poly.totloop = 4;
+
+ MLoop &loop_a = loops[loop_index++];
+ loop_a.v = top_verts_start + i;
+ loop_a.e = connecting_edges_start + i;
+ MLoop &loop_b = loops[loop_index++];
+ loop_b.v = bottom_verts_start + i;
+ loop_b.e = bottom_edges_start + i;
+ MLoop &loop_c = loops[loop_index++];
+ loop_c.v = bottom_verts_start + (i + 1) % verts_num;
+ loop_c.e = connecting_edges_start + (i + 1) % verts_num;
+ MLoop &loop_d = loops[loop_index++];
+ loop_d.v = top_verts_start + (i + 1) % verts_num;
+ loop_d.e = top_edges_start + i;
+ }
+ }
+ else {
+ /* Triangles connect the top and bottom section. */
+ if (!top_is_point) {
+ for (const int i : IndexRange(verts_num)) {
+ MPoly &poly = polys[poly_index++];
+ poly.loopstart = loop_index;
+ poly.totloop = 3;
+
+ MLoop &loop_a = loops[loop_index++];
+ loop_a.v = top_verts_start + i;
+ loop_a.e = connecting_edges_start + i;
+ MLoop &loop_b = loops[loop_index++];
+ loop_b.v = bottom_verts_start;
+ loop_b.e = connecting_edges_start + (i + 1) % verts_num;
+ MLoop &loop_c = loops[loop_index++];
+ loop_c.v = top_verts_start + (i + 1) % verts_num;
+ loop_c.e = top_edges_start + i;
+ }
+ }
+ else {
+ BLI_assert(!bottom_is_point);
+ for (const int i : IndexRange(verts_num)) {
+ MPoly &poly = polys[poly_index++];
+ poly.loopstart = loop_index;
+ poly.totloop = 3;
+
+ MLoop &loop_a = loops[loop_index++];
+ loop_a.v = bottom_verts_start + i;
+ loop_a.e = bottom_edges_start + i;
+ MLoop &loop_b = loops[loop_index++];
+ loop_b.v = bottom_verts_start + (i + 1) % verts_num;
+ loop_b.e = connecting_edges_start + (i + 1) % verts_num;
+ MLoop &loop_c = loops[loop_index++];
+ loop_c.v = top_verts_start;
+ loop_c.e = connecting_edges_start + i;
+ }
+ }
+ }
+
+ /* Create bottom corners and faces. */
+ if (!bottom_is_point) {
+ if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
+ MPoly &poly = polys[poly_index++];
+ poly.loopstart = loop_index;
+ poly.totloop = verts_num;
+
+ for (const int i : IndexRange(verts_num)) {
+ /* Go backwards to reverse surface normal. */
+ MLoop &loop = loops[loop_index++];
+ loop.v = bottom_verts_start + verts_num - 1 - i;
+ loop.e = bottom_edges_start + verts_num - 1 - (i + 1) % verts_num;
+ }
+ }
+ else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
+ for (const int i : IndexRange(verts_num)) {
+ MPoly &poly = polys[poly_index++];
+ poly.loopstart = loop_index;
+ poly.totloop = 3;
+
+ MLoop &loop_a = loops[loop_index++];
+ loop_a.v = bottom_verts_start + i;
+ loop_a.e = bottom_fan_edges_start + i;
+ MLoop &loop_b = loops[loop_index++];
+ loop_b.v = bottom_center_vert_index;
+ loop_b.e = bottom_fan_edges_start + (i + 1) % verts_num;
+ MLoop &loop_c = loops[loop_index++];
+ loop_c.v = bottom_verts_start + (i + 1) % verts_num;
+ loop_c.e = bottom_edges_start + i;
+ }
+ }
+ }
+
+ BKE_mesh_calc_normals(mesh);
+
+ calculate_uvs(mesh, top_is_point, bottom_is_point, verts_num, fill_type);
+
+ BLI_assert(BKE_mesh_is_valid(mesh));
return mesh;
}
@@ -253,6 +564,7 @@ static void geo_node_mesh_primitive_cone_exec(GeoNodeExecParams params)
Mesh *mesh = create_cylinder_or_cone_mesh(
radius_top, radius_bottom, depth, verts_num, fill_type);
+ /* Transform the mesh so that the base of the cone is at the origin. */
BKE_mesh_translate(mesh, float3(0.0f, 0.0f, depth * 0.5f), false);
params.set_output("Geometry", GeometrySet::create_with_mesh(mesh));
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc
index f8a9bfd2ed1..9651301cb34 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc
@@ -17,6 +17,7 @@
#include "DNA_mesh_types.h"
#include "BKE_lib_id.h"
+#include "BKE_material.h"
#include "BKE_mesh.h"
#include "bmesh.h"
@@ -35,7 +36,7 @@ static bNodeSocketTemplate geo_node_mesh_primitive_cube_out[] = {
namespace blender::nodes {
-static Mesh *create_cube_mesh(const float size)
+Mesh *create_cube_mesh(const float size)
{
const float4x4 transform = float4x4::identity();
@@ -53,6 +54,7 @@ static Mesh *create_cube_mesh(const float size)
BMeshToMeshParams params{};
params.calc_object_remap = false;
Mesh *mesh = (Mesh *)BKE_id_new_nomain(ID_ME, nullptr);
+ BKE_id_material_eval_ensure_default_slot(&mesh->id);
BM_mesh_bm_to_me(nullptr, bm, mesh, &params);
BM_mesh_free(bm);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc
index f443b4387d3..1767f765da4 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc
@@ -17,6 +17,7 @@
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
+#include "BKE_material.h"
#include "BKE_mesh.h"
#include "UI_interface.h"
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_plane.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc
index eff84d7d1ad..ac2f5a23a4d 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_plane.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc
@@ -14,12 +14,10 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "BLI_map.hh"
-#include "BLI_math_matrix.h"
-
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
+#include "BKE_material.h"
#include "BKE_mesh.h"
#include "UI_interface.h"
@@ -27,39 +25,47 @@
#include "node_geometry_util.hh"
-static bNodeSocketTemplate geo_node_mesh_primitive_plane_in[] = {
- {SOCK_FLOAT, N_("Size"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE},
- {SOCK_INT, N_("Vertices X"), 10, 0.0f, 0.0f, 0.0f, 2, 1000},
- {SOCK_INT, N_("Vertices Y"), 10, 0.0f, 0.0f, 0.0f, 2, 1000},
+static bNodeSocketTemplate geo_node_mesh_primitive_grid_in[] = {
+ {SOCK_FLOAT, N_("Size X"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE},
+ {SOCK_FLOAT, N_("Size Y"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE},
+ {SOCK_INT, N_("Vertices X"), 3, 0.0f, 0.0f, 0.0f, 2, 1000},
+ {SOCK_INT, N_("Vertices Y"), 3, 0.0f, 0.0f, 0.0f, 2, 1000},
{-1, ""},
};
-static bNodeSocketTemplate geo_node_mesh_primitive_plane_out[] = {
+static bNodeSocketTemplate geo_node_mesh_primitive_grid_out[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{-1, ""},
};
namespace blender::nodes {
-static void calculate_uvs(Mesh *mesh, Span<MVert> verts, Span<MLoop> loops, const float size)
+static void calculate_uvs(
+ Mesh *mesh, Span<MVert> verts, Span<MLoop> loops, const float size_x, const float size_y)
{
MeshComponent mesh_component;
mesh_component.replace(mesh, GeometryOwnershipType::Editable);
- OutputAttributePtr uv_attribute = mesh_component.attribute_try_get_for_output(
- "uv", ATTR_DOMAIN_CORNER, CD_PROP_FLOAT2, nullptr);
- MutableSpan<float2> uvs = uv_attribute->get_span_for_write_only<float2>();
+ OutputAttribute_Typed<float2> uv_attribute =
+ mesh_component.attribute_try_get_for_output_only<float2>("uv_map", ATTR_DOMAIN_CORNER);
+ MutableSpan<float2> uvs = uv_attribute.as_span();
+ const float dx = (size_x == 0.0f) ? 0.0f : 1.0f / size_x;
+ const float dy = (size_y == 0.0f) ? 0.0f : 1.0f / size_y;
for (const int i : loops.index_range()) {
const float3 &co = verts[loops[i].v].co;
- uvs[i].x = (co.x + size) / (size * 2.0f);
- uvs[i].y = (co.y + size) / (size * 2.0f);
+ uvs[i].x = (co.x + size_x * 0.5f) * dx;
+ uvs[i].y = (co.y + size_y * 0.5f) * dy;
}
- uv_attribute.apply_span_and_save();
+ uv_attribute.save();
}
-static Mesh *create_plane_mesh(const int verts_x, const int verts_y, const float size)
+static Mesh *create_grid_mesh(const int verts_x,
+ const int verts_y,
+ const float size_x,
+ const float size_y)
{
+ BLI_assert(verts_x > 1 && verts_y > 1);
const int edges_x = verts_x - 1;
const int edges_y = verts_y - 1;
Mesh *mesh = BKE_mesh_new_nomain(verts_x * verts_y,
@@ -73,11 +79,11 @@ static Mesh *create_plane_mesh(const int verts_x, const int verts_y, const float
MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly};
{
- const float dx = size / edges_x;
- const float dy = size / edges_y;
- float x = -size;
+ const float dx = size_x / edges_x;
+ const float dy = size_y / edges_y;
+ float x = -size_x * 0.5;
for (const int x_index : IndexRange(verts_x)) {
- float y = -size;
+ float y = -size_y * 0.5;
for (const int y_index : IndexRange(verts_y)) {
const int vert_index = x_index * verts_y + y_index;
verts[vert_index].co[0] = x;
@@ -104,6 +110,7 @@ static Mesh *create_plane_mesh(const int verts_x, const int verts_y, const float
MEdge &edge = edges[edge_index++];
edge.v1 = vert_index;
edge.v2 = vert_index + 1;
+ edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
}
}
@@ -115,6 +122,7 @@ static Mesh *create_plane_mesh(const int verts_x, const int verts_y, const float
MEdge &edge = edges[edge_index++];
edge.v1 = vert_index;
edge.v2 = vert_index + verts_y;
+ edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
}
}
@@ -142,14 +150,15 @@ static Mesh *create_plane_mesh(const int verts_x, const int verts_y, const float
}
}
- calculate_uvs(mesh, verts, loops, size);
+ calculate_uvs(mesh, verts, loops, size_x, size_y);
return mesh;
}
-static void geo_node_mesh_primitive_plane_exec(GeoNodeExecParams params)
+static void geo_node_mesh_primitive_grid_exec(GeoNodeExecParams params)
{
- const float size = params.extract_input<float>("Size");
+ const float size_x = params.extract_input<float>("Size X");
+ const float size_y = params.extract_input<float>("Size Y");
const int verts_x = params.extract_input<int>("Vertices X");
const int verts_y = params.extract_input<int>("Vertices Y");
if (verts_x < 2 || verts_y < 2) {
@@ -157,21 +166,22 @@ static void geo_node_mesh_primitive_plane_exec(GeoNodeExecParams params)
return;
}
- Mesh *mesh = create_plane_mesh(verts_x, verts_y, size);
+ Mesh *mesh = create_grid_mesh(verts_x, verts_y, size_x, size_y);
BLI_assert(BKE_mesh_is_valid(mesh));
+ BKE_id_material_eval_ensure_default_slot(&mesh->id);
params.set_output("Geometry", GeometrySet::create_with_mesh(mesh));
}
} // namespace blender::nodes
-void register_node_type_geo_mesh_primitive_plane()
+void register_node_type_geo_mesh_primitive_grid()
{
static bNodeType ntype;
- geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_PLANE, "Plane", NODE_CLASS_GEOMETRY, 0);
+ geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_GRID, "Grid", NODE_CLASS_GEOMETRY, 0);
node_type_socket_templates(
- &ntype, geo_node_mesh_primitive_plane_in, geo_node_mesh_primitive_plane_out);
- ntype.geometry_node_execute = blender::nodes::geo_node_mesh_primitive_plane_exec;
+ &ntype, geo_node_mesh_primitive_grid_in, geo_node_mesh_primitive_grid_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_mesh_primitive_grid_exec;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc
index 242cc6ed7df..a3a1b72006c 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc
@@ -17,6 +17,7 @@
#include "DNA_mesh_types.h"
#include "BKE_lib_id.h"
+#include "BKE_material.h"
#include "BKE_mesh.h"
#include "bmesh.h"
@@ -55,6 +56,7 @@ static Mesh *create_ico_sphere_mesh(const int subdivisions, const float radius)
BMeshToMeshParams params{};
params.calc_object_remap = false;
Mesh *mesh = (Mesh *)BKE_id_new_nomain(ID_ME, nullptr);
+ BKE_id_material_eval_ensure_default_slot(&mesh->id);
BM_mesh_bm_to_me(nullptr, bm, mesh, &params);
BM_mesh_free(bm);
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 a7d40571c39..e841455e58c 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
@@ -14,12 +14,10 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "BLI_map.hh"
-#include "BLI_math_matrix.h"
-
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
+#include "BKE_material.h"
#include "BKE_mesh.h"
#include "UI_interface.h"
@@ -113,6 +111,7 @@ static Mesh *create_line_mesh(const float3 start, const float3 delta, const int
}
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};
@@ -154,7 +153,10 @@ static void geo_node_mesh_primitive_line_exec(GeoNodeExecParams params)
}
else if (count_mode == GEO_NODE_MESH_LINE_COUNT_TOTAL) {
const int count = params.extract_input<int>("Count");
- if (count > 1) {
+ if (count == 1) {
+ mesh = create_line_mesh(start, float3(0), count);
+ }
+ else {
const float3 delta = total_delta / (float)(count - 1);
mesh = create_line_mesh(start, delta, 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 8efba91da1a..599c59e4a2e 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
@@ -17,19 +17,17 @@
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
-#include "BKE_lib_id.h"
+#include "BKE_material.h"
#include "BKE_mesh.h"
#include "UI_interface.h"
#include "UI_resources.h"
-#include "bmesh.h"
-
#include "node_geometry_util.hh"
static bNodeSocketTemplate geo_node_mesh_primitive_uv_sphere_in[] = {
{SOCK_INT, N_("Segments"), 32, 0.0f, 0.0f, 0.0f, 3, 1024},
- {SOCK_INT, N_("Rings"), 16, 0.0f, 0.0f, 0.0f, 3, 1024},
+ {SOCK_INT, N_("Rings"), 16, 0.0f, 0.0f, 0.0f, 2, 1024},
{SOCK_FLOAT, N_("Radius"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE},
{-1, ""},
};
@@ -65,31 +63,225 @@ static int sphere_face_total(const int segments, const int rings)
return quads + triangles;
}
-static Mesh *create_uv_sphere_mesh_bmesh(const float radius, const int segments, const int rings)
+static void calculate_sphere_vertex_data(MutableSpan<MVert> verts,
+ const float radius,
+ const int segments,
+ const int rings)
+{
+ const float delta_theta = M_PI / rings;
+ const float delta_phi = (2 * M_PI) / segments;
+
+ copy_v3_v3(verts[0].co, float3(0.0f, 0.0f, radius));
+ normal_float_to_short_v3(verts[0].no, float3(0.0f, 0.0f, 1.0f));
+
+ int vert_index = 1;
+ float theta = delta_theta;
+ for (const int UNUSED(ring) : IndexRange(rings - 1)) {
+ float phi = 0.0f;
+ const float z = cosf(theta);
+ for (const int UNUSED(segment) : IndexRange(segments)) {
+ const float sin_theta = std::sin(theta);
+ const float x = sin_theta * std::cos(phi);
+ const float y = sin_theta * std::sin(phi);
+ copy_v3_v3(verts[vert_index].co, float3(x, y, z) * radius);
+ normal_float_to_short_v3(verts[vert_index].no, float3(x, y, z));
+ phi += delta_phi;
+ vert_index++;
+ }
+ theta += delta_theta;
+ }
+
+ copy_v3_v3(verts.last().co, float3(0.0f, 0.0f, -radius));
+ normal_float_to_short_v3(verts.last().no, float3(0.0f, 0.0f, -1.0f));
+}
+
+static void calculate_sphere_edge_indices(MutableSpan<MEdge> edges,
+ const int segments,
+ const int rings)
+{
+ int edge_index = 0;
+
+ /* Add the edges connecting the top vertex to the first ring. */
+ const int first_vert_ring_index_start = 1;
+ for (const int segment : IndexRange(segments)) {
+ MEdge &edge = edges[edge_index++];
+ edge.v1 = 0;
+ edge.v2 = first_vert_ring_index_start + segment;
+ edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
+ }
+
+ int ring_vert_index_start = 1;
+ for (const int ring : IndexRange(rings - 1)) {
+ const int next_ring_vert_index_start = ring_vert_index_start + segments;
+
+ /* Add the edges running along each ring. */
+ for (const int segment : IndexRange(segments)) {
+ MEdge &edge = edges[edge_index++];
+ edge.v1 = ring_vert_index_start + segment;
+ edge.v2 = ring_vert_index_start + ((segment + 1) % segments);
+ edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
+ }
+
+ /* Add the edges connecting to the next ring. */
+ if (ring < rings - 2) {
+ for (const int segment : IndexRange(segments)) {
+ MEdge &edge = edges[edge_index++];
+ edge.v1 = ring_vert_index_start + segment;
+ edge.v2 = next_ring_vert_index_start + segment;
+ edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
+ }
+ }
+ ring_vert_index_start += segments;
+ }
+
+ /* Add the edges connecting the last ring to the bottom vertex. */
+ const int last_vert_index = sphere_vert_total(segments, rings) - 1;
+ const int last_vert_ring_start = last_vert_index - segments;
+ for (const int segment : IndexRange(segments)) {
+ MEdge &edge = edges[edge_index++];
+ edge.v1 = last_vert_index;
+ edge.v2 = last_vert_ring_start + segment;
+ edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
+ }
+}
+
+static void calculate_sphere_faces(MutableSpan<MLoop> loops,
+ MutableSpan<MPoly> polys,
+ const int segments,
+ const int rings)
{
- const float4x4 transform = float4x4::identity();
-
- const BMeshCreateParams bmcp = {true};
- const BMAllocTemplate allocsize = {sphere_vert_total(segments, rings),
- sphere_edge_total(segments, rings),
- sphere_corner_total(segments, rings),
- sphere_face_total(segments, rings)};
- BMesh *bm = BM_mesh_create(&allocsize, &bmcp);
-
- BMO_op_callf(bm,
- BMO_FLAG_DEFAULTS,
- "create_uvsphere u_segments=%i v_segments=%i diameter=%f matrix=%m4 calc_uvs=%b",
- segments,
- rings,
- radius,
- transform.values,
- true);
-
- BMeshToMeshParams params{};
- params.calc_object_remap = false;
- Mesh *mesh = (Mesh *)BKE_id_new_nomain(ID_ME, nullptr);
- BM_mesh_bm_to_me(nullptr, bm, mesh, &params);
- BM_mesh_free(bm);
+ int loop_index = 0;
+ int poly_index = 0;
+
+ /* Add the triangles connected to the top vertex. */
+ const int first_vert_ring_index_start = 1;
+ for (const int segment : IndexRange(segments)) {
+ MPoly &poly = polys[poly_index++];
+ poly.loopstart = loop_index;
+ poly.totloop = 3;
+ MLoop &loop_a = loops[loop_index++];
+ loop_a.v = 0;
+ loop_a.e = segment;
+ MLoop &loop_b = loops[loop_index++];
+ loop_b.v = first_vert_ring_index_start + segment;
+ loop_b.e = segments + segment;
+ MLoop &loop_c = loops[loop_index++];
+ loop_c.v = first_vert_ring_index_start + (segment + 1) % segments;
+ loop_c.e = (segment + 1) % segments;
+ }
+
+ int ring_vert_index_start = 1;
+ int ring_edge_index_start = segments;
+ for (const int UNUSED(ring) : IndexRange(1, rings - 2)) {
+ const int next_ring_vert_index_start = ring_vert_index_start + segments;
+ const int next_ring_edge_index_start = ring_edge_index_start + segments * 2;
+ const int ring_vertical_edge_index_start = ring_edge_index_start + segments;
+
+ for (const int segment : IndexRange(segments)) {
+ MPoly &poly = polys[poly_index++];
+ poly.loopstart = loop_index;
+ poly.totloop = 4;
+
+ MLoop &loop_a = loops[loop_index++];
+ loop_a.v = ring_vert_index_start + segment;
+ loop_a.e = ring_vertical_edge_index_start + segment;
+ MLoop &loop_b = loops[loop_index++];
+ loop_b.v = next_ring_vert_index_start + segment;
+ loop_b.e = next_ring_edge_index_start + segment;
+ MLoop &loop_c = loops[loop_index++];
+ loop_c.v = next_ring_vert_index_start + (segment + 1) % segments;
+ loop_c.e = ring_vertical_edge_index_start + (segment + 1) % segments;
+ MLoop &loop_d = loops[loop_index++];
+ loop_d.v = ring_vert_index_start + (segment + 1) % segments;
+ loop_d.e = ring_edge_index_start + segment;
+ }
+ ring_vert_index_start += segments;
+ ring_edge_index_start += segments * 2;
+ }
+
+ /* Add the triangles connected to the bottom vertex. */
+ const int last_edge_ring_start = segments * (rings - 2) * 2 + segments;
+ const int bottom_edge_fan_start = last_edge_ring_start + segments;
+ const int last_vert_index = sphere_vert_total(segments, rings) - 1;
+ const int last_vert_ring_start = last_vert_index - segments;
+ for (const int segment : IndexRange(segments)) {
+ MPoly &poly = polys[poly_index++];
+ poly.loopstart = loop_index;
+ poly.totloop = 3;
+
+ MLoop &loop_a = loops[loop_index++];
+ loop_a.v = last_vert_index;
+ loop_a.e = bottom_edge_fan_start + (segment + 1) % segments;
+ MLoop &loop_b = loops[loop_index++];
+ loop_b.v = last_vert_ring_start + (segment + 1) % segments;
+ loop_b.e = last_edge_ring_start + segment;
+ MLoop &loop_c = loops[loop_index++];
+ loop_c.v = last_vert_ring_start + segment;
+ loop_c.e = bottom_edge_fan_start + segment;
+ }
+}
+
+static void calculate_sphere_uvs(Mesh *mesh, const float segments, const float rings)
+{
+ MeshComponent mesh_component;
+ mesh_component.replace(mesh, GeometryOwnershipType::Editable);
+ OutputAttribute_Typed<float2> uv_attribute =
+ mesh_component.attribute_try_get_for_output_only<float2>("uv_map", ATTR_DOMAIN_CORNER);
+ MutableSpan<float2> uvs = uv_attribute.as_span();
+
+ int loop_index = 0;
+ const float dy = 1.0f / rings;
+
+ for (const int i_segment : IndexRange(segments)) {
+ const float segment = static_cast<float>(i_segment);
+ uvs[loop_index++] = float2((segment + 0.5f) / segments, 0.0f);
+ uvs[loop_index++] = float2(segment / segments, dy);
+ uvs[loop_index++] = float2((segment + 1.0f) / segments, dy);
+ }
+
+ for (const int i_ring : IndexRange(1, rings - 2)) {
+ const float ring = static_cast<float>(i_ring);
+ for (const int i_segment : IndexRange(segments)) {
+ const float segment = static_cast<float>(i_segment);
+ uvs[loop_index++] = float2(segment / segments, ring / rings);
+ uvs[loop_index++] = float2(segment / segments, (ring + 1.0f) / rings);
+ uvs[loop_index++] = float2((segment + 1.0f) / segments, (ring + 1.0f) / rings);
+ uvs[loop_index++] = float2((segment + 1.0f) / segments, ring / rings);
+ }
+ }
+
+ for (const int i_segment : IndexRange(segments)) {
+ const float segment = static_cast<float>(i_segment);
+ uvs[loop_index++] = float2((segment + 0.5f) / segments, 1.0f);
+ uvs[loop_index++] = float2((segment + 1.0f) / segments, 1.0f - dy);
+ uvs[loop_index++] = float2(segment / segments, 1.0f - dy);
+ }
+
+ uv_attribute.save();
+}
+
+static Mesh *create_uv_sphere_mesh(const float radius, const int segments, const int rings)
+{
+ Mesh *mesh = BKE_mesh_new_nomain(sphere_vert_total(segments, rings),
+ sphere_edge_total(segments, rings),
+ 0,
+ 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};
+
+ calculate_sphere_vertex_data(verts, radius, segments, rings);
+
+ calculate_sphere_edge_indices(edges, segments, rings);
+
+ calculate_sphere_faces(loops, polys, segments, rings);
+
+ calculate_sphere_uvs(mesh, segments, rings);
+
+ BLI_assert(BKE_mesh_is_valid(mesh));
return mesh;
}
@@ -98,14 +290,14 @@ static void geo_node_mesh_primitive_uv_sphere_exec(GeoNodeExecParams params)
{
const int segments_num = params.extract_input<int>("Segments");
const int rings_num = params.extract_input<int>("Rings");
- if (segments_num < 3 || rings_num < 3) {
+ if (segments_num < 3 || rings_num < 2) {
params.set_output("Geometry", GeometrySet());
return;
}
const float radius = params.extract_input<float>("Radius");
- Mesh *mesh = create_uv_sphere_mesh_bmesh(radius, segments_num, rings_num);
+ Mesh *mesh = create_uv_sphere_mesh(radius, segments_num, rings_num);
params.set_output("Geometry", GeometrySet::create_with_mesh(mesh));
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc
new file mode 100644
index 00000000000..0fb7910c904
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc
@@ -0,0 +1,316 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BLI_array.hh"
+#include "BLI_task.hh"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_attribute_math.hh"
+#include "BKE_spline.hh"
+
+#include "node_geometry_util.hh"
+
+using blender::Array;
+
+static bNodeSocketTemplate geo_node_mesh_to_curve_in[] = {
+ {SOCK_GEOMETRY, N_("Mesh")},
+ {SOCK_STRING, N_("Selection")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_mesh_to_curve_out[] = {
+ {SOCK_GEOMETRY, N_("Curve")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+
+template<typename T>
+static void copy_attribute_to_points(const VArray<T> &source_data,
+ Span<int> map,
+ MutableSpan<T> dest_data)
+{
+ for (const int point_index : map.index_range()) {
+ const int vert_index = map[point_index];
+ dest_data[point_index] = source_data[vert_index];
+ }
+}
+
+static void copy_attributes_to_points(CurveEval &curve,
+ const MeshComponent &mesh_component,
+ Span<Vector<int>> point_to_vert_maps)
+{
+ MutableSpan<SplinePtr> splines = curve.splines();
+ Set<std::string> source_attribute_names = mesh_component.attribute_names();
+
+ /* Copy builtin control point attributes. */
+ if (source_attribute_names.contains_as("tilt")) {
+ const GVArray_Typed<float> tilt_attribute = mesh_component.attribute_get_for_read<float>(
+ "tilt", ATTR_DOMAIN_POINT, 0.0f);
+ parallel_for(splines.index_range(), 256, [&](IndexRange range) {
+ for (const int i : range) {
+ copy_attribute_to_points<float>(
+ *tilt_attribute, point_to_vert_maps[i], splines[i]->tilts());
+ }
+ });
+ source_attribute_names.remove_contained_as("tilt");
+ }
+ if (source_attribute_names.contains_as("radius")) {
+ const GVArray_Typed<float> radius_attribute = mesh_component.attribute_get_for_read<float>(
+ "radius", ATTR_DOMAIN_POINT, 1.0f);
+ parallel_for(splines.index_range(), 256, [&](IndexRange range) {
+ for (const int i : range) {
+ copy_attribute_to_points<float>(
+ *radius_attribute, point_to_vert_maps[i], splines[i]->radii());
+ }
+ });
+ source_attribute_names.remove_contained_as("radius");
+ }
+
+ /* Don't copy other builtin control point attributes. */
+ source_attribute_names.remove_as("position");
+
+ /* Copy dynamic control point attributes. */
+ for (const StringRef name : source_attribute_names) {
+ const GVArrayPtr mesh_attribute = mesh_component.attribute_try_get_for_read(name,
+ ATTR_DOMAIN_POINT);
+ /* Some attributes might not exist if they were builtin attribute on domains that don't
+ * have any elements, i.e. a face attribute on the output of the line primitive node. */
+ if (!mesh_attribute) {
+ continue;
+ }
+
+ const CustomDataType data_type = bke::cpp_type_to_custom_data_type(mesh_attribute->type());
+
+ parallel_for(splines.index_range(), 128, [&](IndexRange range) {
+ for (const int i : range) {
+ /* Create attribute on the spline points. */
+ splines[i]->attributes.create(name, data_type);
+ std::optional<GMutableSpan> spline_attribute = splines[i]->attributes.get_for_write(name);
+ BLI_assert(spline_attribute);
+
+ /* Copy attribute based on the map for this spline. */
+ attribute_math::convert_to_static_type(mesh_attribute->type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ copy_attribute_to_points<T>(
+ mesh_attribute->typed<T>(), point_to_vert_maps[i], spline_attribute->typed<T>());
+ });
+ }
+ });
+ }
+
+ curve.assert_valid_point_attributes();
+}
+
+struct CurveFromEdgesOutput {
+ std::unique_ptr<CurveEval> curve;
+ Vector<Vector<int>> point_to_vert_maps;
+};
+
+static CurveFromEdgesOutput mesh_to_curve(Span<MVert> verts, Span<std::pair<int, int>> edges)
+{
+ std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>();
+ Vector<Vector<int>> point_to_vert_maps;
+
+ /* Compute the number of edges connecting to each vertex. */
+ Array<int> neighbor_count(verts.size(), 0);
+ for (const std::pair<int, int> &edge : edges) {
+ neighbor_count[edge.first]++;
+ neighbor_count[edge.second]++;
+ }
+
+ /* Compute an offset into the array of neighbor edges based on the counts. */
+ Array<int> neighbor_offsets(verts.size());
+ int start = 0;
+ for (const int i : verts.index_range()) {
+ neighbor_offsets[i] = start;
+ start += neighbor_count[i];
+ }
+
+ /* Use as an index into the "neighbor group" for each vertex. */
+ Array<int> used_slots(verts.size(), 0);
+ /* Calculate the indices of each vertex's neighboring edges. */
+ Array<int> neighbors(edges.size() * 2);
+ for (const int i : edges.index_range()) {
+ const int v1 = edges[i].first;
+ const int v2 = edges[i].second;
+ neighbors[neighbor_offsets[v1] + used_slots[v1]] = v2;
+ neighbors[neighbor_offsets[v2] + used_slots[v2]] = v1;
+ used_slots[v1]++;
+ used_slots[v2]++;
+ }
+
+ /* Now use the neighbor group offsets calculated above as a count used edges at each vertex. */
+ Array<int> unused_edges = std::move(used_slots);
+
+ for (const int start_vert : verts.index_range()) {
+ /* The vertex will be part of a cyclic spline. */
+ if (neighbor_count[start_vert] == 2) {
+ continue;
+ }
+
+ /* The vertex has no connected edges, or they were already used. */
+ if (unused_edges[start_vert] == 0) {
+ continue;
+ }
+
+ for (const int i : IndexRange(neighbor_count[start_vert])) {
+ int current_vert = start_vert;
+ int next_vert = neighbors[neighbor_offsets[current_vert] + i];
+
+ if (unused_edges[next_vert] == 0) {
+ continue;
+ }
+
+ std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>();
+ Vector<int> point_to_vert_map;
+
+ spline->add_point(verts[current_vert].co, 1.0f, 0.0f);
+ point_to_vert_map.append(current_vert);
+
+ /* Follow connected edges until we read a vertex with more than two connected edges. */
+ while (true) {
+ int last_vert = current_vert;
+ current_vert = next_vert;
+
+ spline->add_point(verts[current_vert].co, 1.0f, 0.0f);
+ point_to_vert_map.append(current_vert);
+ unused_edges[current_vert]--;
+ unused_edges[last_vert]--;
+
+ if (neighbor_count[current_vert] != 2) {
+ break;
+ }
+
+ const int offset = neighbor_offsets[current_vert];
+ const int next_a = neighbors[offset];
+ const int next_b = neighbors[offset + 1];
+ next_vert = (last_vert == next_a) ? next_b : next_a;
+ }
+
+ spline->attributes.reallocate(spline->size());
+ curve->add_spline(std::move(spline));
+ point_to_vert_maps.append(std::move(point_to_vert_map));
+ }
+ }
+
+ /* All remaining edges are part of cyclic splines (we skipped vertices with two edges before). */
+ for (const int start_vert : verts.index_range()) {
+ if (unused_edges[start_vert] != 2) {
+ continue;
+ }
+
+ int current_vert = start_vert;
+ int next_vert = neighbors[neighbor_offsets[current_vert]];
+
+ std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>();
+ Vector<int> point_to_vert_map;
+ spline->set_cyclic(true);
+
+ spline->add_point(verts[current_vert].co, 1.0f, 0.0f);
+ point_to_vert_map.append(current_vert);
+
+ /* Follow connected edges until we loop back to the start vertex. */
+ while (next_vert != start_vert) {
+ const int last_vert = current_vert;
+ current_vert = next_vert;
+
+ spline->add_point(verts[current_vert].co, 1.0f, 0.0f);
+ point_to_vert_map.append(current_vert);
+ unused_edges[current_vert]--;
+ unused_edges[last_vert]--;
+
+ const int offset = neighbor_offsets[current_vert];
+ const int next_a = neighbors[offset];
+ const int next_b = neighbors[offset + 1];
+ next_vert = (last_vert == next_a) ? next_b : next_a;
+ }
+
+ spline->attributes.reallocate(spline->size());
+ curve->add_spline(std::move(spline));
+ point_to_vert_maps.append(std::move(point_to_vert_map));
+ }
+
+ curve->attributes.reallocate(curve->splines().size());
+ return {std::move(curve), std::move(point_to_vert_maps)};
+}
+
+/**
+ * Get a separate array of the indices for edges in a selection (a boolean attribute).
+ * This helps to make the above algorithm simpler by removing the need to check for selection
+ * in many places.
+ */
+static Vector<std::pair<int, int>> get_selected_edges(GeoNodeExecParams params,
+ const MeshComponent &component)
+{
+ const Mesh &mesh = *component.get_for_read();
+ const std::string selection_name = params.extract_input<std::string>("Selection");
+ if (!selection_name.empty() && !component.attribute_exists(selection_name)) {
+ params.error_message_add(NodeWarningType::Error,
+ TIP_("No attribute with name \"") + selection_name + "\"");
+ }
+ GVArray_Typed<bool> selection = component.attribute_get_for_read<bool>(
+ selection_name, ATTR_DOMAIN_EDGE, true);
+
+ Vector<std::pair<int, int>> selected_edges;
+ for (const int i : IndexRange(mesh.totedge)) {
+ if (selection[i]) {
+ selected_edges.append({mesh.medge[i].v1, mesh.medge[i].v2});
+ }
+ }
+
+ return selected_edges;
+}
+
+static void geo_node_mesh_to_curve_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh");
+
+ geometry_set = bke::geometry_set_realize_instances(geometry_set);
+
+ if (!geometry_set.has_mesh()) {
+ params.set_output("Curve", GeometrySet());
+ return;
+ }
+
+ const MeshComponent &component = *geometry_set.get_component_for_read<MeshComponent>();
+ const Mesh &mesh = *component.get_for_read();
+ Span<MVert> verts = Span{mesh.mvert, mesh.totvert};
+ Vector<std::pair<int, int>> selected_edges = get_selected_edges(params, component);
+ if (selected_edges.size() == 0) {
+ params.set_output("Curve", GeometrySet());
+ return;
+ }
+
+ CurveFromEdgesOutput output = mesh_to_curve(verts, selected_edges);
+ copy_attributes_to_points(*output.curve, component, output.point_to_vert_maps);
+
+ params.set_output("Curve", GeometrySet::create_with_curve(output.curve.release()));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_mesh_to_curve()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_MESH_TO_CURVE, "Mesh to Curve", NODE_CLASS_GEOMETRY, 0);
+ node_type_socket_templates(&ntype, geo_node_mesh_to_curve_in, geo_node_mesh_to_curve_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_mesh_to_curve_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_object_info.cc b/source/blender/nodes/geometry/nodes/node_geo_object_info.cc
index 54ecb20dae7..167812d5656 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_object_info.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_object_info.cc
@@ -14,20 +14,15 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "node_geometry_util.hh"
-
-#include "BKE_mesh.h"
-#include "BKE_mesh_wrapper.h"
-#include "BKE_modifier.h"
-#include "BKE_volume.h"
-
#include "BLI_math_matrix.h"
#include "UI_interface.h"
#include "UI_resources.h"
+#include "node_geometry_util.hh"
+
static bNodeSocketTemplate geo_node_object_info_in[] = {
- {SOCK_OBJECT, N_("Object")},
+ {SOCK_OBJECT, N_("Object"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, PROP_NONE, SOCK_HIDE_LABEL},
{-1, ""},
};
@@ -52,9 +47,7 @@ static void geo_node_object_info_exec(GeoNodeExecParams params)
const bool transform_space_relative = (node_storage->transform_space ==
GEO_NODE_TRANSFORM_SPACE_RELATIVE);
- bke::PersistentObjectHandle object_handle = params.extract_input<bke::PersistentObjectHandle>(
- "Object");
- Object *object = params.handle_map().lookup(object_handle);
+ Object *object = params.get_input<Object *>("Object");
float3 location = {0, 0, 0};
float3 rotation = {0, 0, 0};
@@ -78,14 +71,15 @@ static void geo_node_object_info_exec(GeoNodeExecParams params)
if (object != self_object) {
InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>();
+ const int handle = instances.add_reference(*object);
if (transform_space_relative) {
- instances.add_instance(object, transform);
+ instances.add_instance(handle, transform);
}
else {
float unit_transform[4][4];
unit_m4(unit_transform);
- instances.add_instance(object, unit_transform);
+ instances.add_instance(handle, unit_transform);
}
}
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc b/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc
index 2e3460ee5fe..772bd8a1080 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc
@@ -14,12 +14,9 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "BLI_float3.hh"
#include "BLI_hash.h"
#include "BLI_kdtree.h"
-#include "BLI_math_vector.h"
#include "BLI_rand.hh"
-#include "BLI_span.hh"
#include "BLI_timeit.hh"
#include "DNA_mesh_types.h"
@@ -28,10 +25,10 @@
#include "BKE_attribute_math.hh"
#include "BKE_bvhutils.h"
-#include "BKE_deform.h"
#include "BKE_geometry_set_instances.hh"
#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
+#include "BKE_mesh_sample.hh"
#include "BKE_pointcloud.h"
#include "UI_interface.h"
@@ -44,7 +41,7 @@ using blender::bke::GeometryInstanceGroup;
static bNodeSocketTemplate geo_node_point_distribute_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
- {SOCK_FLOAT, N_("Distance Min"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 100000.0f, PROP_NONE},
+ {SOCK_FLOAT, N_("Distance Min"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 100000.0f, PROP_DISTANCE},
{SOCK_FLOAT, N_("Density Max"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 100000.0f, PROP_NONE},
{SOCK_STRING, N_("Density Attribute")},
{SOCK_INT, N_("Seed"), 0, 0, 0, 0, -10000, 10000},
@@ -95,7 +92,7 @@ static Span<MLoopTri> get_mesh_looptris(const Mesh &mesh)
static void sample_mesh_surface(const Mesh &mesh,
const float4x4 &transform,
const float base_density,
- const FloatReadAttribute *density_factors,
+ const VArray<float> *density_factors,
const int seed,
Vector<float3> &r_positions,
Vector<float3> &r_bary_coords,
@@ -117,9 +114,9 @@ static void sample_mesh_surface(const Mesh &mesh,
float looptri_density_factor = 1.0f;
if (density_factors != nullptr) {
- const float v0_density_factor = std::max(0.0f, (*density_factors)[v0_loop]);
- const float v1_density_factor = std::max(0.0f, (*density_factors)[v1_loop]);
- const float v2_density_factor = std::max(0.0f, (*density_factors)[v2_loop]);
+ const float v0_density_factor = std::max(0.0f, density_factors->get(v0_loop));
+ const float v1_density_factor = std::max(0.0f, density_factors->get(v1_loop));
+ const float v2_density_factor = std::max(0.0f, density_factors->get(v2_loop));
looptri_density_factor = (v0_density_factor + v1_density_factor + v2_density_factor) / 3.0f;
}
const float area = area_tri_v3(v0_pos, v1_pos, v2_pos);
@@ -207,7 +204,7 @@ BLI_NOINLINE static void update_elimination_mask_for_close_points(
BLI_NOINLINE static void update_elimination_mask_based_on_density_factors(
const Mesh &mesh,
- const FloatReadAttribute &density_factors,
+ const VArray<float> &density_factors,
Span<float3> bary_coords,
Span<int> looptri_indices,
MutableSpan<bool> elimination_mask)
@@ -253,99 +250,27 @@ BLI_NOINLINE static void eliminate_points_based_on_mask(Span<bool> elimination_m
}
}
-template<typename T>
-BLI_NOINLINE static void interpolate_attribute_point(const Mesh &mesh,
- const Span<float3> bary_coords,
- const Span<int> looptri_indices,
- const Span<T> data_in,
- MutableSpan<T> data_out)
-{
- BLI_assert(data_in.size() == mesh.totvert);
- Span<MLoopTri> looptris = get_mesh_looptris(mesh);
-
- for (const int i : bary_coords.index_range()) {
- const int looptri_index = looptri_indices[i];
- 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 T &v0 = data_in[v0_index];
- const T &v1 = data_in[v1_index];
- const T &v2 = data_in[v2_index];
-
- const T interpolated_value = attribute_math::mix3(bary_coord, v0, v1, v2);
- data_out[i] = interpolated_value;
- }
-}
-
-template<typename T>
-BLI_NOINLINE static void interpolate_attribute_corner(const Mesh &mesh,
- const Span<float3> bary_coords,
- const Span<int> looptri_indices,
- const Span<T> data_in,
- MutableSpan<T> data_out)
-{
- BLI_assert(data_in.size() == mesh.totloop);
- Span<MLoopTri> looptris = get_mesh_looptris(mesh);
-
- for (const int i : bary_coords.index_range()) {
- const int looptri_index = looptri_indices[i];
- const MLoopTri &looptri = looptris[looptri_index];
- const float3 &bary_coord = bary_coords[i];
-
- const int loop_index_0 = looptri.tri[0];
- const int loop_index_1 = looptri.tri[1];
- const int loop_index_2 = looptri.tri[2];
-
- const T &v0 = data_in[loop_index_0];
- const T &v1 = data_in[loop_index_1];
- const T &v2 = data_in[loop_index_2];
-
- const T interpolated_value = attribute_math::mix3(bary_coord, v0, v1, v2);
- data_out[i] = interpolated_value;
- }
-}
-
-template<typename T>
-BLI_NOINLINE static void interpolate_attribute_face(const Mesh &mesh,
- const Span<int> looptri_indices,
- const Span<T> data_in,
- MutableSpan<T> data_out)
-{
- BLI_assert(data_in.size() == mesh.totpoly);
- Span<MLoopTri> looptris = get_mesh_looptris(mesh);
-
- for (const int i : data_out.index_range()) {
- const int looptri_index = looptri_indices[i];
- const MLoopTri &looptri = looptris[looptri_index];
- const int poly_index = looptri.poly;
- data_out[i] = data_in[poly_index];
- }
-}
-
-template<typename T>
BLI_NOINLINE static void interpolate_attribute(const Mesh &mesh,
Span<float3> bary_coords,
Span<int> looptri_indices,
const AttributeDomain source_domain,
- Span<T> source_span,
- MutableSpan<T> output_span)
+ const GVArray &source_data,
+ GMutableSpan output_data)
{
switch (source_domain) {
case ATTR_DOMAIN_POINT: {
- interpolate_attribute_point<T>(mesh, bary_coords, looptri_indices, source_span, output_span);
+ bke::mesh_surface_sample::sample_point_attribute(
+ mesh, looptri_indices, bary_coords, source_data, output_data);
break;
}
case ATTR_DOMAIN_CORNER: {
- interpolate_attribute_corner<T>(
- mesh, bary_coords, looptri_indices, source_span, output_span);
+ bke::mesh_surface_sample::sample_corner_attribute(
+ mesh, looptri_indices, bary_coords, source_data, output_data);
break;
}
case ATTR_DOMAIN_FACE: {
- interpolate_attribute_face<T>(mesh, looptri_indices, source_span, output_span);
+ bke::mesh_surface_sample::sample_face_attribute(
+ mesh, looptri_indices, source_data, output_data);
break;
}
default: {
@@ -367,13 +292,13 @@ BLI_NOINLINE static void interpolate_existing_attributes(
StringRef attribute_name = entry.key;
const CustomDataType output_data_type = entry.value.data_type;
/* The output domain is always #ATTR_DOMAIN_POINT, since we are creating a point cloud. */
- OutputAttributePtr attribute_out = component.attribute_try_get_for_output(
+ OutputAttribute attribute_out = component.attribute_try_get_for_output_only(
attribute_name, ATTR_DOMAIN_POINT, output_data_type);
if (!attribute_out) {
continue;
}
- fn::GMutableSpan out_span = attribute_out->get_span_for_write_only();
+ GMutableSpan out_span = attribute_out.as_span();
int i_instance = 0;
for (const GeometryInstanceGroup &set_group : set_groups) {
@@ -381,47 +306,41 @@ BLI_NOINLINE static void interpolate_existing_attributes(
const MeshComponent &source_component = *set.get_component_for_read<MeshComponent>();
const Mesh &mesh = *source_component.get_for_read();
- /* Use a dummy read without specifying a domain or data type in order to
- * get the existing attribute's domain. Interpolation is done manually based
- * on the bary coords in #interpolate_attribute. */
- ReadAttributePtr dummy_attribute = source_component.attribute_try_get_for_read(
+ std::optional<AttributeMetaData> attribute_info = component.attribute_get_meta_data(
attribute_name);
- if (!dummy_attribute) {
+ if (!attribute_info) {
i_instance += set_group.transforms.size();
continue;
}
- const AttributeDomain source_domain = dummy_attribute->domain();
- ReadAttributePtr source_attribute = source_component.attribute_get_for_read(
+ const AttributeDomain source_domain = attribute_info->domain;
+ GVArrayPtr source_attribute = source_component.attribute_get_for_read(
attribute_name, source_domain, output_data_type, nullptr);
if (!source_attribute) {
i_instance += set_group.transforms.size();
continue;
}
- fn::GSpan source_span = source_attribute->get_span();
+
+ for (const int UNUSED(i_set_instance) : set_group.transforms.index_range()) {
+ const int offset = instance_start_offsets[i_instance];
+ Span<float3> bary_coords = bary_coords_array[i_instance];
+ Span<int> looptri_indices = looptri_indices_array[i_instance];
+
+ GMutableSpan instance_span = out_span.slice(offset, bary_coords.size());
+ interpolate_attribute(
+ mesh, bary_coords, looptri_indices, source_domain, *source_attribute, instance_span);
+
+ i_instance++;
+ }
attribute_math::convert_to_static_type(output_data_type, [&](auto dummy) {
using T = decltype(dummy);
- for (const int UNUSED(i_set_instance) : set_group.transforms.index_range()) {
- const int offset = instance_start_offsets[i_instance];
- Span<float3> bary_coords = bary_coords_array[i_instance];
- Span<int> looptri_indices = looptri_indices_array[i_instance];
-
- MutableSpan<T> instance_span = out_span.typed<T>().slice(offset, bary_coords.size());
- interpolate_attribute<T>(mesh,
- bary_coords,
- looptri_indices,
- source_domain,
- source_span.typed<T>(),
- instance_span);
-
- i_instance++;
- }
+ GVArray_Span<T> source_span{*source_attribute};
});
}
- attribute_out.apply_span_and_save();
+ attribute_out.save();
}
}
@@ -431,16 +350,16 @@ BLI_NOINLINE static void compute_special_attributes(Span<GeometryInstanceGroup>
Span<Vector<float3>> bary_coords_array,
Span<Vector<int>> looptri_indices_array)
{
- OutputAttributePtr id_attribute = component.attribute_try_get_for_output(
- "id", ATTR_DOMAIN_POINT, CD_PROP_INT32);
- OutputAttributePtr normal_attribute = component.attribute_try_get_for_output(
- "normal", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3);
- OutputAttributePtr rotation_attribute = component.attribute_try_get_for_output(
- "rotation", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3);
+ OutputAttribute_Typed<int> id_attribute = component.attribute_try_get_for_output_only<int>(
+ "id", ATTR_DOMAIN_POINT);
+ OutputAttribute_Typed<float3> normal_attribute =
+ component.attribute_try_get_for_output_only<float3>("normal", ATTR_DOMAIN_POINT);
+ OutputAttribute_Typed<float3> rotation_attribute =
+ component.attribute_try_get_for_output_only<float3>("rotation", ATTR_DOMAIN_POINT);
- MutableSpan<int> result_ids = id_attribute->get_span_for_write_only<int>();
- MutableSpan<float3> result_normals = normal_attribute->get_span_for_write_only<float3>();
- MutableSpan<float3> result_rotations = rotation_attribute->get_span_for_write_only<float3>();
+ MutableSpan<int> result_ids = id_attribute.as_span();
+ MutableSpan<float3> result_normals = normal_attribute.as_span();
+ MutableSpan<float3> result_rotations = rotation_attribute.as_span();
int i_instance = 0;
for (const GeometryInstanceGroup &set_group : sets) {
@@ -484,9 +403,9 @@ BLI_NOINLINE static void compute_special_attributes(Span<GeometryInstanceGroup>
}
}
- id_attribute.apply_span_and_save();
- normal_attribute.apply_span_and_save();
- rotation_attribute.apply_span_and_save();
+ id_attribute.save();
+ normal_attribute.save();
+ rotation_attribute.save();
}
BLI_NOINLINE static void add_remaining_point_attributes(
@@ -524,7 +443,7 @@ static void distribute_points_random(Span<GeometryInstanceGroup> set_groups,
for (const GeometryInstanceGroup &set_group : set_groups) {
const GeometrySet &set = set_group.geometry_set;
const MeshComponent &component = *set.get_component_for_read<MeshComponent>();
- const FloatReadAttribute density_factors = component.attribute_get_for_read<float>(
+ GVArray_Typed<float> density_factors = component.attribute_get_for_read<float>(
density_attribute_name, ATTR_DOMAIN_CORNER, use_one_default ? 1.0f : 0.0f);
const Mesh &mesh = *component.get_for_read();
for (const float4x4 &transform : set_group.transforms) {
@@ -534,7 +453,7 @@ static void distribute_points_random(Span<GeometryInstanceGroup> set_groups,
sample_mesh_surface(mesh,
transform,
density,
- &density_factors,
+ &*density_factors,
seed,
positions,
bary_coords,
@@ -593,7 +512,7 @@ static void distribute_points_poisson_disk(Span<GeometryInstanceGroup> set_group
const GeometrySet &set = set_group.geometry_set;
const MeshComponent &component = *set.get_component_for_read<MeshComponent>();
const Mesh &mesh = *component.get_for_read();
- const FloatReadAttribute density_factors = component.attribute_get_for_read<float>(
+ const GVArray_Typed<float> density_factors = component.attribute_get_for_read<float>(
density_attribute_name, ATTR_DOMAIN_CORNER, use_one_default ? 1.0f : 0.0f);
for (const int UNUSED(i_set_instance) : set_group.transforms.index_range()) {
@@ -626,7 +545,7 @@ static void geo_node_point_distribute_exec(GeoNodeExecParams params)
const GeometryNodePointDistributeMode distribute_method =
static_cast<GeometryNodePointDistributeMode>(params.node().custom1);
- const int seed = params.get_input<int>("Seed");
+ const int seed = params.get_input<int>("Seed") * 5383843;
const float density = params.extract_input<float>("Density Max");
const std::string density_attribute_name = params.extract_input<std::string>(
"Density Attribute");
@@ -636,7 +555,8 @@ static void geo_node_point_distribute_exec(GeoNodeExecParams params)
return;
}
- Vector<GeometryInstanceGroup> set_groups = bke::geometry_set_gather_instances(geometry_set);
+ Vector<GeometryInstanceGroup> set_groups;
+ geometry_set_gather_instances(geometry_set, set_groups);
if (set_groups.is_empty()) {
params.set_output("Geometry", GeometrySet());
return;
@@ -715,8 +635,8 @@ static void geo_node_point_distribute_exec(GeoNodeExecParams params)
geometry_set_out.get_component_for_write<PointCloudComponent>();
Map<std::string, AttributeKind> attributes;
- bke::gather_attribute_info(
- attributes, {GEO_COMPONENT_TYPE_MESH}, set_groups, {"position", "normal", "id"});
+ bke::geometry_set_gather_instances_attribute_info(
+ set_groups, {GEO_COMPONENT_TYPE_MESH}, {"position", "normal", "id"}, attributes);
add_remaining_point_attributes(set_groups,
instance_start_offsets,
attributes,
diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc b/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc
index dbbb73bd36d..e52ab1b2127 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc
@@ -14,15 +14,10 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "BKE_mesh.h"
-#include "BKE_persistent_data_handle.hh"
-
#include "DNA_collection_types.h"
-#include "DNA_mesh_types.h"
-#include "DNA_meshdata_types.h"
-#include "DNA_pointcloud_types.h"
#include "BLI_hash.h"
+#include "BLI_task.hh"
#include "UI_interface.h"
#include "UI_resources.h"
@@ -31,8 +26,17 @@
static bNodeSocketTemplate geo_node_point_instance_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
- {SOCK_OBJECT, N_("Object")},
- {SOCK_COLLECTION, N_("Collection")},
+ {SOCK_OBJECT, N_("Object"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, PROP_NONE, SOCK_HIDE_LABEL},
+ {SOCK_COLLECTION,
+ N_("Collection"),
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ PROP_NONE,
+ SOCK_HIDE_LABEL},
{SOCK_INT, N_("Seed"), 0, 0, 0, 0, -10000, 10000},
{-1, ""},
};
@@ -52,6 +56,15 @@ static void geo_node_point_instance_layout(uiLayout *layout, bContext *UNUSED(C)
namespace blender::nodes {
+static void geo_node_point_instance_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometryPointInstance *data = (NodeGeometryPointInstance *)MEM_callocN(
+ sizeof(NodeGeometryPointInstance), __func__);
+ data->instance_type = GEO_NODE_POINT_INSTANCE_TYPE_OBJECT;
+ data->flag |= GEO_NODE_POINT_INSTANCE_WHOLE_COLLECTION;
+ node->storage = data;
+}
+
static void geo_node_point_instance_update(bNodeTree *UNUSED(tree), bNode *node)
{
bNodeSocket *object_socket = (bNodeSocket *)BLI_findlink(&node->inputs, 1);
@@ -69,129 +82,133 @@ static void geo_node_point_instance_update(bNodeTree *UNUSED(tree), bNode *node)
seed_socket, type == GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION && !use_whole_collection);
}
-static void get_instanced_data__object(const GeoNodeExecParams &params,
- MutableSpan<std::optional<InstancedData>> r_instances_data)
+static Vector<InstanceReference> get_instance_references__object(GeoNodeExecParams &params)
{
- bke::PersistentObjectHandle object_handle = params.get_input<bke::PersistentObjectHandle>(
- "Object");
- Object *object = params.handle_map().lookup(object_handle);
+ Object *object = params.extract_input<Object *>("Object");
if (object == params.self_object()) {
- object = nullptr;
+ return {};
}
if (object != nullptr) {
- InstancedData instance;
- instance.type = INSTANCE_DATA_TYPE_OBJECT;
- instance.data.object = object;
- r_instances_data.fill(instance);
+ return {*object};
}
+ return {};
}
-static void get_instanced_data__collection(
- const GeoNodeExecParams &params,
- const GeometryComponent &component,
- MutableSpan<std::optional<InstancedData>> r_instances_data)
+static Vector<InstanceReference> get_instance_references__collection(GeoNodeExecParams &params)
{
const bNode &node = params.node();
NodeGeometryPointInstance *node_storage = (NodeGeometryPointInstance *)node.storage;
- bke::PersistentCollectionHandle collection_handle =
- params.get_input<bke::PersistentCollectionHandle>("Collection");
- Collection *collection = params.handle_map().lookup(collection_handle);
+ Collection *collection = params.get_input<Collection *>("Collection");
if (collection == nullptr) {
- return;
+ return {};
}
if (BLI_listbase_is_empty(&collection->children) &&
BLI_listbase_is_empty(&collection->gobject)) {
params.error_message_add(NodeWarningType::Info, TIP_("Collection is empty"));
- return;
+ return {};
}
- const bool use_whole_collection = (node_storage->flag &
- GEO_NODE_POINT_INSTANCE_WHOLE_COLLECTION) != 0;
- if (use_whole_collection) {
- InstancedData instance;
- instance.type = INSTANCE_DATA_TYPE_COLLECTION;
- instance.data.collection = collection;
- r_instances_data.fill(instance);
+ if (node_storage->flag & GEO_NODE_POINT_INSTANCE_WHOLE_COLLECTION) {
+ return {*collection};
}
- else {
- Vector<InstancedData> possible_instances;
- /* Direct child objects are instanced as objects. */
- LISTBASE_FOREACH (CollectionObject *, cob, &collection->gobject) {
- Object *object = cob->ob;
- InstancedData instance;
- instance.type = INSTANCE_DATA_TYPE_OBJECT;
- instance.data.object = object;
- possible_instances.append(instance);
- }
- /* Direct child collections are instanced as collections. */
- LISTBASE_FOREACH (CollectionChild *, child, &collection->children) {
- Collection *child_collection = child->collection;
- InstancedData instance;
- instance.type = INSTANCE_DATA_TYPE_COLLECTION;
- instance.data.collection = child_collection;
- possible_instances.append(instance);
- }
- if (!possible_instances.is_empty()) {
- const int seed = params.get_input<int>("Seed");
- Array<uint32_t> ids = get_geometry_element_ids_as_uints(component, ATTR_DOMAIN_POINT);
- for (const int i : r_instances_data.index_range()) {
- const int index = BLI_hash_int_2d(ids[i], seed) % possible_instances.size();
- r_instances_data[i] = possible_instances[index];
- }
- }
+ Vector<InstanceReference> references;
+ /* Direct child objects are instanced as objects. */
+ LISTBASE_FOREACH (CollectionObject *, cob, &collection->gobject) {
+ references.append(*cob->ob);
+ }
+ /* Direct child collections are instanced as collections. */
+ LISTBASE_FOREACH (CollectionChild *, child, &collection->children) {
+ references.append(*child->collection);
}
+
+ return references;
}
-static Array<std::optional<InstancedData>> get_instanced_data(const GeoNodeExecParams &params,
- const GeometryComponent &component,
- const int amount)
+static Vector<InstanceReference> get_instance_references(GeoNodeExecParams &params)
{
const bNode &node = params.node();
NodeGeometryPointInstance *node_storage = (NodeGeometryPointInstance *)node.storage;
const GeometryNodePointInstanceType type = (GeometryNodePointInstanceType)
node_storage->instance_type;
- Array<std::optional<InstancedData>> instances_data(amount);
switch (type) {
case GEO_NODE_POINT_INSTANCE_TYPE_OBJECT: {
- get_instanced_data__object(params, instances_data);
- break;
+ return get_instance_references__object(params);
}
case GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION: {
- get_instanced_data__collection(params, component, instances_data);
- break;
+ return get_instance_references__collection(params);
}
}
- return instances_data;
+ return {};
+}
+
+/**
+ * Add the instance references to the component as a separate step from actually creating the
+ * instances in order to avoid a map lookup for every transform. While this might add some
+ * unnecessary references if they are not chosen while adding transforms, in the common cases
+ * there are many more transforms than there are references, so that isn't likely.
+ */
+static Array<int> add_instance_references(InstancesComponent &instance_component,
+ Span<InstanceReference> possible_references)
+{
+ Array<int> possible_handles(possible_references.size());
+ for (const int i : possible_references.index_range()) {
+ possible_handles[i] = instance_component.add_reference(possible_references[i]);
+ }
+ return possible_handles;
}
-static void add_instances_from_geometry_component(InstancesComponent &instances,
- const GeometryComponent &src_geometry,
- const GeoNodeExecParams &params)
+static void add_instances_from_component(InstancesComponent &instances,
+ const GeometryComponent &src_geometry,
+ Span<int> possible_handles,
+ const GeoNodeExecParams &params)
{
const AttributeDomain domain = ATTR_DOMAIN_POINT;
const int domain_size = src_geometry.attribute_domain_size(domain);
- Array<std::optional<InstancedData>> instances_data = get_instanced_data(
- params, src_geometry, domain_size);
- Float3ReadAttribute positions = src_geometry.attribute_get_for_read<float3>(
+ GVArray_Typed<float3> positions = src_geometry.attribute_get_for_read<float3>(
"position", domain, {0, 0, 0});
- Float3ReadAttribute rotations = src_geometry.attribute_get_for_read<float3>(
+ GVArray_Typed<float3> rotations = src_geometry.attribute_get_for_read<float3>(
"rotation", domain, {0, 0, 0});
- Float3ReadAttribute scales = src_geometry.attribute_get_for_read<float3>(
+ GVArray_Typed<float3> scales = src_geometry.attribute_get_for_read<float3>(
"scale", domain, {1, 1, 1});
- Int32ReadAttribute ids = src_geometry.attribute_get_for_read<int>("id", domain, -1);
-
- for (const int i : IndexRange(domain_size)) {
- if (instances_data[i].has_value()) {
- float transform[4][4];
- loc_eul_size_to_mat4(transform, positions[i], rotations[i], scales[i]);
- instances.add_instance(*instances_data[i], transform, ids[i]);
- }
+ GVArray_Typed<int> id_attribute = src_geometry.attribute_get_for_read<int>("id", domain, -1);
+
+ /* The initial size of the component might be non-zero if there are two component types. */
+ const int start_len = instances.instances_amount();
+ instances.resize(start_len + domain_size);
+ MutableSpan<int> handles = instances.instance_reference_handles().slice(start_len, domain_size);
+ MutableSpan<float4x4> transforms = instances.instance_transforms().slice(start_len, domain_size);
+ MutableSpan<int> instance_ids = instances.instance_ids().slice(start_len, domain_size);
+
+ /* Skip all of the randomness handling if there is only a single possible instance
+ * (anything except for collection mode with "Whole Collection" turned off). */
+ if (possible_handles.size() == 1) {
+ const int handle = possible_handles.first();
+ parallel_for(IndexRange(domain_size), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ handles[i] = handle;
+ transforms[i] = float4x4::from_loc_eul_scale(positions[i], rotations[i], scales[i]);
+ instance_ids[i] = id_attribute[i];
+ }
+ });
+ }
+ else {
+ const int seed = params.get_input<int>("Seed");
+ Array<uint32_t> ids = get_geometry_element_ids_as_uints(src_geometry, ATTR_DOMAIN_POINT);
+ parallel_for(IndexRange(domain_size), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ const int index = BLI_hash_int_2d(ids[i], seed) % possible_handles.size();
+ const int handle = possible_handles[index];
+ handles[i] = handle;
+ transforms[i] = float4x4::from_loc_eul_scale(positions[i], rotations[i], scales[i]);
+ instance_ids[i] = id_attribute[i];
+ }
+ });
}
}
@@ -204,28 +221,37 @@ static void geo_node_point_instance_exec(GeoNodeExecParams params)
* rather than making the entire input geometry set real. */
geometry_set = geometry_set_realize_instances(geometry_set);
+ const Vector<InstanceReference> possible_references = get_instance_references(params);
+ if (possible_references.is_empty()) {
+ params.set_output("Geometry", std::move(geometry_set_out));
+ return;
+ }
+
InstancesComponent &instances = geometry_set_out.get_component_for_write<InstancesComponent>();
+ Array<int> possible_handles = add_instance_references(instances, possible_references);
+
if (geometry_set.has<MeshComponent>()) {
- add_instances_from_geometry_component(
- instances, *geometry_set.get_component_for_read<MeshComponent>(), params);
+ add_instances_from_component(instances,
+ *geometry_set.get_component_for_read<MeshComponent>(),
+ possible_handles,
+ params);
}
if (geometry_set.has<PointCloudComponent>()) {
- add_instances_from_geometry_component(
- instances, *geometry_set.get_component_for_read<PointCloudComponent>(), params);
+ add_instances_from_component(instances,
+ *geometry_set.get_component_for_read<PointCloudComponent>(),
+ possible_handles,
+ params);
+ }
+ if (geometry_set.has<CurveComponent>()) {
+ add_instances_from_component(instances,
+ *geometry_set.get_component_for_read<CurveComponent>(),
+ possible_handles,
+ params);
}
params.set_output("Geometry", std::move(geometry_set_out));
}
-static void geo_node_point_instance_init(bNodeTree *UNUSED(tree), bNode *node)
-{
- NodeGeometryPointInstance *data = (NodeGeometryPointInstance *)MEM_callocN(
- sizeof(NodeGeometryPointInstance), __func__);
- data->instance_type = GEO_NODE_POINT_INSTANCE_TYPE_OBJECT;
- data->flag |= GEO_NODE_POINT_INSTANCE_WHOLE_COLLECTION;
- node->storage = data;
-}
-
} // namespace blender::nodes
void register_node_type_geo_point_instance()
diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_rotate.cc b/source/blender/nodes/geometry/nodes/node_geo_point_rotate.cc
index 5eef2fabce0..828d3f50551 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_point_rotate.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_point_rotate.cc
@@ -14,13 +14,13 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "node_geometry_util.hh"
-
#include "BLI_math_rotation.h"
#include "UI_interface.h"
#include "UI_resources.h"
+#include "node_geometry_util.hh"
+
static bNodeSocketTemplate geo_node_point_rotate_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_STRING, N_("Axis")},
@@ -59,9 +59,43 @@ static void geo_node_point_rotate_layout(uiLayout *layout, bContext *UNUSED(C),
namespace blender::nodes {
+static void geo_node_point_rotate_init(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeGeometryRotatePoints *node_storage = (NodeGeometryRotatePoints *)MEM_callocN(
+ sizeof(NodeGeometryRotatePoints), __func__);
+
+ node_storage->type = GEO_NODE_POINT_ROTATE_TYPE_EULER;
+ node_storage->space = GEO_NODE_POINT_ROTATE_SPACE_OBJECT;
+ node_storage->input_type_axis = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
+ node_storage->input_type_angle = GEO_NODE_ATTRIBUTE_INPUT_FLOAT;
+ node_storage->input_type_rotation = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
+
+ node->storage = node_storage;
+}
+
+static void geo_node_point_rotate_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeGeometryRotatePoints *node_storage = (NodeGeometryRotatePoints *)node->storage;
+ update_attribute_input_socket_availabilities(
+ *node,
+ "Axis",
+ (GeometryNodeAttributeInputMode)node_storage->input_type_axis,
+ node_storage->type == GEO_NODE_POINT_ROTATE_TYPE_AXIS_ANGLE);
+ update_attribute_input_socket_availabilities(
+ *node,
+ "Angle",
+ (GeometryNodeAttributeInputMode)node_storage->input_type_angle,
+ node_storage->type == GEO_NODE_POINT_ROTATE_TYPE_AXIS_ANGLE);
+ update_attribute_input_socket_availabilities(
+ *node,
+ "Rotation",
+ (GeometryNodeAttributeInputMode)node_storage->input_type_rotation,
+ node_storage->type == GEO_NODE_POINT_ROTATE_TYPE_EULER);
+}
+
static void point_rotate__axis_angle__object_space(const int domain_size,
- const Float3ReadAttribute &axis,
- const FloatReadAttribute &angles,
+ const VArray<float3> &axis,
+ const VArray<float> &angles,
MutableSpan<float3> rotations)
{
for (const int i : IndexRange(domain_size)) {
@@ -76,8 +110,8 @@ static void point_rotate__axis_angle__object_space(const int domain_size,
}
static void point_rotate__axis_angle__point_space(const int domain_size,
- const Float3ReadAttribute &axis,
- const FloatReadAttribute &angles,
+ const VArray<float3> &axis,
+ const VArray<float> &angles,
MutableSpan<float3> rotations)
{
for (const int i : IndexRange(domain_size)) {
@@ -92,7 +126,7 @@ static void point_rotate__axis_angle__point_space(const int domain_size,
}
static void point_rotate__euler__object_space(const int domain_size,
- const Float3ReadAttribute &eulers,
+ const VArray<float3> &eulers,
MutableSpan<float3> rotations)
{
for (const int i : IndexRange(domain_size)) {
@@ -107,7 +141,7 @@ static void point_rotate__euler__object_space(const int domain_size,
}
static void point_rotate__euler__point_space(const int domain_size,
- const Float3ReadAttribute &eulers,
+ const VArray<float3> &eulers,
MutableSpan<float3> rotations)
{
for (const int i : IndexRange(domain_size)) {
@@ -127,19 +161,19 @@ static void point_rotate_on_component(GeometryComponent &component,
const bNode &node = params.node();
const NodeGeometryRotatePoints &storage = *(const NodeGeometryRotatePoints *)node.storage;
- OutputAttributePtr rotation_attribute = component.attribute_try_get_for_output(
- "rotation", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3);
+ OutputAttribute_Typed<float3> rotation_attribute =
+ component.attribute_try_get_for_output<float3>("rotation", ATTR_DOMAIN_POINT, {0, 0, 0});
if (!rotation_attribute) {
return;
}
- MutableSpan<float3> rotations = rotation_attribute->get_span<float3>();
+ MutableSpan<float3> rotations = rotation_attribute.as_span();
const int domain_size = rotations.size();
if (storage.type == GEO_NODE_POINT_ROTATE_TYPE_AXIS_ANGLE) {
- Float3ReadAttribute axis = params.get_input_attribute<float3>(
+ GVArray_Typed<float3> axis = params.get_input_attribute<float3>(
"Axis", component, ATTR_DOMAIN_POINT, {0, 0, 1});
- FloatReadAttribute angles = params.get_input_attribute<float>(
+ GVArray_Typed<float> angles = params.get_input_attribute<float>(
"Angle", component, ATTR_DOMAIN_POINT, 0);
if (storage.space == GEO_NODE_POINT_ROTATE_SPACE_OBJECT) {
@@ -150,7 +184,7 @@ static void point_rotate_on_component(GeometryComponent &component,
}
}
else {
- Float3ReadAttribute eulers = params.get_input_attribute<float3>(
+ GVArray_Typed<float3> eulers = params.get_input_attribute<float3>(
"Rotation", component, ATTR_DOMAIN_POINT, {0, 0, 0});
if (storage.space == GEO_NODE_POINT_ROTATE_SPACE_OBJECT) {
@@ -161,7 +195,7 @@ static void point_rotate_on_component(GeometryComponent &component,
}
}
- rotation_attribute.apply_span_and_save();
+ rotation_attribute.save();
}
static void geo_node_point_rotate_exec(GeoNodeExecParams params)
@@ -176,44 +210,13 @@ static void geo_node_point_rotate_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
point_rotate_on_component(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
+ if (geometry_set.has<CurveComponent>()) {
+ point_rotate_on_component(geometry_set.get_component_for_write<CurveComponent>(), params);
+ }
params.set_output("Geometry", geometry_set);
}
-static void geo_node_point_rotate_init(bNodeTree *UNUSED(ntree), bNode *node)
-{
- NodeGeometryRotatePoints *node_storage = (NodeGeometryRotatePoints *)MEM_callocN(
- sizeof(NodeGeometryRotatePoints), __func__);
-
- node_storage->type = GEO_NODE_POINT_ROTATE_TYPE_EULER;
- node_storage->space = GEO_NODE_POINT_ROTATE_SPACE_OBJECT;
- node_storage->input_type_axis = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
- node_storage->input_type_angle = GEO_NODE_ATTRIBUTE_INPUT_FLOAT;
- node_storage->input_type_rotation = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
-
- node->storage = node_storage;
-}
-
-static void geo_node_point_rotate_update(bNodeTree *UNUSED(ntree), bNode *node)
-{
- NodeGeometryRotatePoints *node_storage = (NodeGeometryRotatePoints *)node->storage;
- update_attribute_input_socket_availabilities(
- *node,
- "Axis",
- (GeometryNodeAttributeInputMode)node_storage->input_type_axis,
- node_storage->type == GEO_NODE_POINT_ROTATE_TYPE_AXIS_ANGLE);
- update_attribute_input_socket_availabilities(
- *node,
- "Angle",
- (GeometryNodeAttributeInputMode)node_storage->input_type_angle,
- node_storage->type == GEO_NODE_POINT_ROTATE_TYPE_AXIS_ANGLE);
- update_attribute_input_socket_availabilities(
- *node,
- "Rotation",
- (GeometryNodeAttributeInputMode)node_storage->input_type_rotation,
- node_storage->type == GEO_NODE_POINT_ROTATE_TYPE_EULER);
-}
-
} // namespace blender::nodes
void register_node_type_geo_point_rotate()
diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_scale.cc b/source/blender/nodes/geometry/nodes/node_geo_point_scale.cc
index 9df103ff057..265ec9fb776 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_point_scale.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_point_scale.cc
@@ -14,17 +14,18 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "node_geometry_util.hh"
-
#include "BKE_colorband.h"
#include "UI_interface.h"
#include "UI_resources.h"
+#include "node_geometry_util.hh"
+
static bNodeSocketTemplate geo_node_point_scale_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_STRING, N_("Factor")},
{SOCK_VECTOR, N_("Factor"), 1.0f, 1.0f, 1.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_XYZ},
+ {SOCK_FLOAT, N_("Factor"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX},
{-1, ""},
};
@@ -42,27 +43,64 @@ static void geo_node_point_scale_layout(uiLayout *layout, bContext *UNUSED(C), P
namespace blender::nodes {
+static void geo_node_point_scale_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometryPointScale *data = (NodeGeometryPointScale *)MEM_callocN(
+ sizeof(NodeGeometryPointScale), __func__);
+
+ data->input_type = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
+ node->storage = data;
+}
+
+static void geo_node_point_scale_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeGeometryPointScale &node_storage = *(NodeGeometryPointScale *)node->storage;
+
+ update_attribute_input_socket_availabilities(
+ *node, "Factor", (GeometryNodeAttributeInputMode)node_storage.input_type);
+}
+
static void execute_on_component(GeoNodeExecParams params, GeometryComponent &component)
{
+ /* Note that scale doesn't necessarily need to be created with a vector type-- it could also use
+ * the highest complexity of the existing attribute's type (if it exists) and the data type used
+ * for the factor. But for it's simpler to simply always use float3, since that is usually
+ * expected anyway. */
static const float3 scale_default = float3(1.0f);
- OutputAttributePtr scale_attribute = component.attribute_try_get_for_output(
+ OutputAttribute_Typed<float3> scale_attribute = component.attribute_try_get_for_output(
"scale", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, &scale_default);
if (!scale_attribute) {
return;
}
- ReadAttributePtr attribute = params.get_input_attribute(
- "Factor", component, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, nullptr);
+
+ const bNode &node = params.node();
+ const NodeGeometryPointScale &node_storage = *(const NodeGeometryPointScale *)node.storage;
+ const GeometryNodeAttributeInputMode input_type = (GeometryNodeAttributeInputMode)
+ node_storage.input_type;
+ const CustomDataType data_type = (input_type == GEO_NODE_ATTRIBUTE_INPUT_FLOAT) ? CD_PROP_FLOAT :
+ CD_PROP_FLOAT3;
+
+ GVArrayPtr attribute = params.get_input_attribute(
+ "Factor", component, ATTR_DOMAIN_POINT, data_type, nullptr);
if (!attribute) {
return;
}
- Span<float3> data = attribute->get_span<float3>();
- MutableSpan<float3> scale_span = scale_attribute->get_span<float3>();
- for (const int i : scale_span.index_range()) {
- scale_span[i] = scale_span[i] * data[i];
+ MutableSpan<float3> scale_span = scale_attribute.as_span();
+ if (data_type == CD_PROP_FLOAT) {
+ GVArray_Typed<float> factors{*attribute};
+ for (const int i : scale_span.index_range()) {
+ scale_span[i] = scale_span[i] * factors[i];
+ }
+ }
+ else if (data_type == CD_PROP_FLOAT3) {
+ GVArray_Typed<float3> factors{*attribute};
+ for (const int i : scale_span.index_range()) {
+ scale_span[i] = scale_span[i] * factors[i];
+ }
}
- scale_attribute.apply_span_and_save();
+ scale_attribute.save();
}
static void geo_node_point_scale_exec(GeoNodeExecParams params)
@@ -77,27 +115,13 @@ static void geo_node_point_scale_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
execute_on_component(params, geometry_set.get_component_for_write<PointCloudComponent>());
}
+ if (geometry_set.has<CurveComponent>()) {
+ execute_on_component(params, geometry_set.get_component_for_write<CurveComponent>());
+ }
params.set_output("Geometry", std::move(geometry_set));
}
-static void geo_node_point_scale_init(bNodeTree *UNUSED(tree), bNode *node)
-{
- NodeGeometryPointScale *data = (NodeGeometryPointScale *)MEM_callocN(
- sizeof(NodeGeometryPointScale), __func__);
-
- data->input_type = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE;
- node->storage = data;
-}
-
-static void geo_node_point_scale_update(bNodeTree *UNUSED(ntree), bNode *node)
-{
- NodeGeometryPointScale &node_storage = *(NodeGeometryPointScale *)node->storage;
-
- update_attribute_input_socket_availabilities(
- *node, "Factor", (GeometryNodeAttributeInputMode)node_storage.input_type);
-}
-
} // namespace blender::nodes
void register_node_type_geo_point_scale()
diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc b/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc
index 522dea4aa0e..fc04d1e275f 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc
@@ -52,33 +52,33 @@ static void copy_data_based_on_mask(Span<T> data,
}
}
-static void copy_attributes_based_on_mask(const GeometryComponent &in_component,
- GeometryComponent &result_component,
- Span<bool> masks,
- const bool invert)
+void copy_point_attributes_based_on_mask(const GeometryComponent &in_component,
+ GeometryComponent &result_component,
+ Span<bool> masks,
+ const bool invert)
{
for (const std::string &name : in_component.attribute_names()) {
- ReadAttributePtr attribute = in_component.attribute_try_get_for_read(name);
- const CustomDataType data_type = attribute->custom_data_type();
+ ReadAttributeLookup attribute = in_component.attribute_try_get_for_read(name);
+ const CustomDataType data_type = bke::cpp_type_to_custom_data_type(attribute.varray->type());
/* Only copy point attributes. Theoretically this could interpolate attributes on other
* domains to the point domain, but that would conflict with attributes that are built-in
* on other domains, which causes creating the attributes to fail. */
- if (attribute->domain() != ATTR_DOMAIN_POINT) {
+ if (attribute.domain != ATTR_DOMAIN_POINT) {
continue;
}
- OutputAttributePtr result_attribute = result_component.attribute_try_get_for_output(
+ OutputAttribute result_attribute = result_component.attribute_try_get_for_output_only(
name, ATTR_DOMAIN_POINT, data_type);
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
using T = decltype(dummy);
- Span<T> span = attribute->get_span<T>();
- MutableSpan<T> out_span = result_attribute->get_span_for_write_only<T>();
+ GVArray_Span<T> span{*attribute.varray};
+ MutableSpan<T> out_span = result_attribute.as_span<T>();
copy_data_based_on_mask(span, masks, invert, out_span);
});
- result_attribute.apply_span_and_save();
+ result_attribute.save();
}
}
@@ -107,9 +107,9 @@ static void separate_points_from_component(const GeometryComponent &in_component
return;
}
- const BooleanReadAttribute mask_attribute = in_component.attribute_get_for_read<bool>(
+ const GVArray_Typed<bool> mask_attribute = in_component.attribute_get_for_read<bool>(
mask_name, ATTR_DOMAIN_POINT, false);
- Span<bool> masks = mask_attribute.get_span();
+ VArray_Span<bool> masks{mask_attribute};
const int total = masks.count(!invert);
if (total == 0) {
@@ -118,7 +118,7 @@ static void separate_points_from_component(const GeometryComponent &in_component
create_component_points(out_component, total);
- copy_attributes_based_on_mask(in_component, out_component, masks, invert);
+ copy_point_attributes_based_on_mask(in_component, out_component, masks, invert);
}
static GeometrySet separate_geometry_set(const GeometrySet &set_in,
@@ -127,6 +127,10 @@ static GeometrySet separate_geometry_set(const GeometrySet &set_in,
{
GeometrySet set_out;
for (const GeometryComponent *component : set_in.get_components_for_read()) {
+ if (component->type() == GEO_COMPONENT_TYPE_CURVE) {
+ /* Don't support the curve component for now, even though it has a point domain. */
+ continue;
+ }
GeometryComponent &out_component = set_out.get_component_for_write(component->type());
separate_points_from_component(*component, out_component, mask_name, invert);
}
@@ -135,15 +139,27 @@ static GeometrySet separate_geometry_set(const GeometrySet &set_in,
static void geo_node_point_separate_exec(GeoNodeExecParams params)
{
- const std::string mask_attribute_name = params.extract_input<std::string>("Mask");
- GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ bool wait_for_inputs = false;
+ wait_for_inputs |= params.lazy_require_input("Geometry");
+ wait_for_inputs |= params.lazy_require_input("Mask");
+ if (wait_for_inputs) {
+ return;
+ }
+ const std::string mask_attribute_name = params.get_input<std::string>("Mask");
+ GeometrySet geometry_set = params.get_input<GeometrySet>("Geometry");
/* TODO: This is not necessary-- the input geometry set can be read only,
* but it must be rewritten to handle instance groups. */
geometry_set = geometry_set_realize_instances(geometry_set);
- params.set_output("Geometry 1", separate_geometry_set(geometry_set, mask_attribute_name, true));
- params.set_output("Geometry 2", separate_geometry_set(geometry_set, mask_attribute_name, false));
+ if (params.lazy_output_is_required("Geometry 1")) {
+ params.set_output("Geometry 1",
+ separate_geometry_set(geometry_set, mask_attribute_name, true));
+ }
+ if (params.lazy_output_is_required("Geometry 2")) {
+ params.set_output("Geometry 2",
+ separate_geometry_set(geometry_set, mask_attribute_name, false));
+ }
}
} // namespace blender::nodes
@@ -155,5 +171,6 @@ void register_node_type_geo_point_separate()
geo_node_type_base(&ntype, GEO_NODE_POINT_SEPARATE, "Point Separate", NODE_CLASS_GEOMETRY, 0);
node_type_socket_templates(&ntype, geo_node_point_instance_in, geo_node_point_instance_out);
ntype.geometry_node_execute = blender::nodes::geo_node_point_separate_exec;
+ ntype.geometry_node_execute_supports_laziness = true;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc b/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc
index 015f4cd38e7..293f151fe19 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc
@@ -14,13 +14,11 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "node_geometry_util.hh"
-
-#include "BKE_colorband.h"
-
#include "UI_interface.h"
#include "UI_resources.h"
+#include "node_geometry_util.hh"
+
static bNodeSocketTemplate geo_node_point_translate_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_STRING, N_("Translation")},
@@ -44,24 +42,19 @@ namespace blender::nodes {
static void execute_on_component(GeoNodeExecParams params, GeometryComponent &component)
{
- OutputAttributePtr position_attribute = component.attribute_try_get_for_output(
- "position", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3);
+ OutputAttribute_Typed<float3> position_attribute =
+ component.attribute_try_get_for_output<float3>("position", ATTR_DOMAIN_POINT, {0, 0, 0});
if (!position_attribute) {
return;
}
- ReadAttributePtr attribute = params.get_input_attribute(
- "Translation", component, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, nullptr);
- if (!attribute) {
- return;
- }
+ GVArray_Typed<float3> attribute = params.get_input_attribute<float3>(
+ "Translation", component, ATTR_DOMAIN_POINT, {0, 0, 0});
- Span<float3> data = attribute->get_span<float3>();
- MutableSpan<float3> scale_span = position_attribute->get_span<float3>();
- for (const int i : scale_span.index_range()) {
- scale_span[i] = scale_span[i] + data[i];
+ for (const int i : IndexRange(attribute.size())) {
+ position_attribute->set(i, position_attribute->get(i) + attribute[i]);
}
- position_attribute.apply_span_and_save();
+ position_attribute.save();
}
static void geo_node_point_translate_exec(GeoNodeExecParams params)
@@ -76,6 +69,9 @@ static void geo_node_point_translate_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
execute_on_component(params, geometry_set.get_component_for_write<PointCloudComponent>());
}
+ if (geometry_set.has<CurveComponent>()) {
+ execute_on_component(params, geometry_set.get_component_for_write<CurveComponent>());
+ }
params.set_output("Geometry", std::move(geometry_set));
}
@@ -85,7 +81,7 @@ static void geo_node_point_translate_init(bNodeTree *UNUSED(tree), bNode *node)
NodeGeometryPointTranslate *data = (NodeGeometryPointTranslate *)MEM_callocN(
sizeof(NodeGeometryPointTranslate), __func__);
- data->input_type = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE;
+ data->input_type = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
node->storage = data;
}
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 b79ca0a6197..65306b1c452 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
@@ -31,14 +31,14 @@
static bNodeSocketTemplate geo_node_points_to_volume_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_FLOAT, N_("Density"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX},
- {SOCK_FLOAT, N_("Voxel Size"), 0.3f, 0.0f, 0.0f, 0.0f, 0.01f, FLT_MAX},
+ {SOCK_FLOAT, N_("Voxel Size"), 0.3f, 0.0f, 0.0f, 0.0f, 0.01f, FLT_MAX, PROP_DISTANCE},
{SOCK_FLOAT, N_("Voxel Amount"), 64.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX},
{SOCK_STRING, N_("Radius")},
{SOCK_FLOAT, N_("Radius"), 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX},
{-1, ""},
};
-static bNodeSocketTemplate geo_node_point_translate_out[] = {
+static bNodeSocketTemplate geo_node_points_to_volume_out[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{-1, ""},
};
@@ -55,6 +55,35 @@ static void geo_node_points_to_volume_layout(uiLayout *layout,
namespace blender::nodes {
+static void geo_node_points_to_volume_init(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeGeometryPointsToVolume *data = (NodeGeometryPointsToVolume *)MEM_callocN(
+ sizeof(NodeGeometryPointsToVolume), __func__);
+ data->resolution_mode = GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_AMOUNT;
+ data->input_type_radius = GEO_NODE_ATTRIBUTE_INPUT_FLOAT;
+ node->storage = data;
+
+ bNodeSocket *radius_attribute_socket = nodeFindSocket(node, SOCK_IN, "Radius");
+ bNodeSocketValueString *radius_attribute_socket_value =
+ (bNodeSocketValueString *)radius_attribute_socket->default_value;
+ STRNCPY(radius_attribute_socket_value->value, "radius");
+}
+
+static void geo_node_points_to_volume_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeGeometryPointsToVolume *data = (NodeGeometryPointsToVolume *)node->storage;
+ bNodeSocket *voxel_size_socket = nodeFindSocket(node, SOCK_IN, "Voxel Size");
+ bNodeSocket *voxel_amount_socket = nodeFindSocket(node, SOCK_IN, "Voxel Amount");
+ nodeSetSocketAvailability(voxel_amount_socket,
+ data->resolution_mode ==
+ GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_AMOUNT);
+ nodeSetSocketAvailability(
+ voxel_size_socket, data->resolution_mode == GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_SIZE);
+
+ update_attribute_input_socket_availabilities(
+ *node, "Radius", (GeometryNodeAttributeInputMode)data->input_type_radius);
+}
+
#ifdef WITH_OPENVDB
namespace {
/* Implements the interface required by #openvdb::tools::ParticlesToLevelSet. */
@@ -147,13 +176,15 @@ static void gather_point_data_from_component(const GeoNodeExecParams &params,
Vector<float3> &r_positions,
Vector<float> &r_radii)
{
- Float3ReadAttribute positions = component.attribute_get_for_read<float3>(
+ GVArray_Typed<float3> positions = component.attribute_get_for_read<float3>(
"position", ATTR_DOMAIN_POINT, {0, 0, 0});
- FloatReadAttribute radii = params.get_input_attribute<float>(
+ GVArray_Typed<float> radii = params.get_input_attribute<float>(
"Radius", component, ATTR_DOMAIN_POINT, 0.0f);
- r_positions.extend(positions.get_span());
- r_radii.extend(radii.get_span());
+ for (const int i : IndexRange(positions.size())) {
+ r_positions.append(positions[i]);
+ r_radii.append(radii[i]);
+ }
}
static void convert_to_grid_index_space(const float voxel_size,
@@ -184,6 +215,10 @@ static void initialize_volume_component_from_points(const GeometrySet &geometry_
gather_point_data_from_component(
params, *geometry_set_in.get_component_for_read<PointCloudComponent>(), positions, radii);
}
+ if (geometry_set_in.has<CurveComponent>()) {
+ gather_point_data_from_component(
+ params, *geometry_set_in.get_component_for_read<CurveComponent>(), positions, radii);
+ }
const float max_radius = *std::max_element(radii.begin(), radii.end());
const float voxel_size = compute_voxel_size(params, positions, max_radius);
@@ -225,35 +260,6 @@ static void geo_node_points_to_volume_exec(GeoNodeExecParams params)
params.set_output("Geometry", std::move(geometry_set_out));
}
-static void geo_node_points_to_volume_init(bNodeTree *UNUSED(ntree), bNode *node)
-{
- NodeGeometryPointsToVolume *data = (NodeGeometryPointsToVolume *)MEM_callocN(
- sizeof(NodeGeometryPointsToVolume), __func__);
- data->resolution_mode = GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_AMOUNT;
- data->input_type_radius = GEO_NODE_ATTRIBUTE_INPUT_FLOAT;
- node->storage = data;
-
- bNodeSocket *radius_attribute_socket = nodeFindSocket(node, SOCK_IN, "Radius");
- bNodeSocketValueString *radius_attribute_socket_value =
- (bNodeSocketValueString *)radius_attribute_socket->default_value;
- STRNCPY(radius_attribute_socket_value->value, "radius");
-}
-
-static void geo_node_points_to_volume_update(bNodeTree *UNUSED(ntree), bNode *node)
-{
- NodeGeometryPointsToVolume *data = (NodeGeometryPointsToVolume *)node->storage;
- bNodeSocket *voxel_size_socket = nodeFindSocket(node, SOCK_IN, "Voxel Size");
- bNodeSocket *voxel_amount_socket = nodeFindSocket(node, SOCK_IN, "Voxel Amount");
- nodeSetSocketAvailability(voxel_amount_socket,
- data->resolution_mode ==
- GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_AMOUNT);
- nodeSetSocketAvailability(
- voxel_size_socket, data->resolution_mode == GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_SIZE);
-
- update_attribute_input_socket_availabilities(
- *node, "Radius", (GeometryNodeAttributeInputMode)data->input_type_radius);
-}
-
} // namespace blender::nodes
void register_node_type_geo_points_to_volume()
@@ -262,7 +268,7 @@ void register_node_type_geo_points_to_volume()
geo_node_type_base(
&ntype, GEO_NODE_POINTS_TO_VOLUME, "Points to Volume", NODE_CLASS_GEOMETRY, 0);
- node_type_socket_templates(&ntype, geo_node_points_to_volume_in, geo_node_point_translate_out);
+ node_type_socket_templates(&ntype, geo_node_points_to_volume_in, geo_node_points_to_volume_out);
node_type_storage(&ntype,
"NodeGeometryPointsToVolume",
node_free_standard_storage,
diff --git a/source/blender/nodes/geometry/nodes/node_geo_select_by_material.cc b/source/blender/nodes/geometry/nodes/node_geo_select_by_material.cc
new file mode 100644
index 00000000000..9bc963eec43
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_select_by_material.cc
@@ -0,0 +1,106 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "node_geometry_util.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BLI_task.hh"
+
+#include "BKE_material.h"
+
+static bNodeSocketTemplate geo_node_select_by_material_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_MATERIAL,
+ N_("Material"),
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ PROP_NONE,
+ SOCK_HIDE_LABEL},
+ {SOCK_STRING, N_("Selection")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_select_by_material_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+
+static void select_mesh_by_material(const Mesh &mesh,
+ const Material *material,
+ const MutableSpan<bool> r_selection)
+{
+ BLI_assert(mesh.totpoly == r_selection.size());
+ Vector<int> material_indices;
+ for (const int i : IndexRange(mesh.totcol)) {
+ if (mesh.mat[i] == material) {
+ material_indices.append(i);
+ }
+ }
+ parallel_for(r_selection.index_range(), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ r_selection[i] = material_indices.contains(mesh.mpoly[i].mat_nr);
+ }
+ });
+}
+
+static void geo_node_select_by_material_exec(GeoNodeExecParams params)
+{
+ Material *material = params.extract_input<Material *>("Material");
+ const std::string selection_name = params.extract_input<std::string>("Selection");
+
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ geometry_set = geometry_set_realize_instances(geometry_set);
+
+ if (geometry_set.has<MeshComponent>()) {
+ MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
+ const Mesh *mesh = mesh_component.get_for_read();
+ if (mesh != nullptr) {
+ OutputAttribute_Typed<bool> selection =
+ mesh_component.attribute_try_get_for_output_only<bool>(selection_name, ATTR_DOMAIN_FACE);
+ if (selection) {
+ select_mesh_by_material(*mesh, material, selection.as_span());
+ selection.save();
+ }
+ }
+ }
+
+ params.set_output("Geometry", std::move(geometry_set));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_select_by_material()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_SELECT_BY_MATERIAL, "Select by Material", NODE_CLASS_GEOMETRY, 0);
+ node_type_socket_templates(
+ &ntype, geo_node_select_by_material_in, geo_node_select_by_material_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_select_by_material_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_subdivide.cc
index 06c5586a3ff..a4c6522c57a 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_subdivide.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_subdivide.cc
@@ -14,7 +14,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "MEM_guardedalloc.h"
+// #include "MEM_guardedalloc.h"
#include "BKE_mesh.h"
#include "BKE_subdiv.h"
@@ -41,6 +41,7 @@ namespace blender::nodes {
static void geo_node_subdivide_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ geometry_set = geometry_set_realize_instances(geometry_set);
if (!geometry_set.has_mesh()) {
params.set_output("Geometry", geometry_set);
@@ -49,7 +50,7 @@ static void geo_node_subdivide_exec(GeoNodeExecParams params)
#ifndef WITH_OPENSUBDIV
params.error_message_add(NodeWarningType::Error,
- TIP_("Disabled, Blender was built without OpenSubdiv"));
+ TIP_("Disabled, Blender was compiled without OpenSubdiv"));
params.set_output("Geometry", std::move(geometry_set));
return;
#endif
@@ -98,6 +99,7 @@ static void geo_node_subdivide_exec(GeoNodeExecParams params)
params.set_output("Geometry", std::move(geometry_set));
}
+
} // namespace blender::nodes
void register_node_type_geo_subdivide()
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 b14a512ce28..20ffcdb516d 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc
@@ -14,8 +14,6 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "MEM_guardedalloc.h"
-
#include "BKE_mesh.h"
#include "BKE_subdiv.h"
#include "BKE_subdiv_mesh.h"
@@ -39,17 +37,6 @@ static bNodeSocketTemplate geo_node_subdivision_surface_out[] = {
{-1, ""},
};
-static void geo_node_subdivision_surface_layout(uiLayout *layout,
- bContext *UNUSED(C),
- PointerRNA *UNUSED(ptr))
-{
-#ifndef WITH_OPENSUBDIV
- uiItemL(layout, IFACE_("Disabled, built without OpenSubdiv"), ICON_ERROR);
-#else
- UNUSED_VARS(layout);
-#endif
-}
-
namespace blender::nodes {
static void geo_node_subdivision_surface_exec(GeoNodeExecParams params)
{
@@ -63,9 +50,8 @@ static void geo_node_subdivision_surface_exec(GeoNodeExecParams params)
}
#ifndef WITH_OPENSUBDIV
- /* Return input geometry if Blender is built without OpenSubdiv. */
- params.set_output("Geometry", std::move(geometry_set));
- return;
+ params.error_message_add(NodeWarningType::Error,
+ TIP_("Disabled, Blender was compiled without OpenSubdiv"));
#else
const int subdiv_level = clamp_i(params.extract_input<int>("Level"), 0, 30);
@@ -115,9 +101,11 @@ static void geo_node_subdivision_surface_exec(GeoNodeExecParams params)
// BKE_subdiv_stats_print(&subdiv->stats);
BKE_subdiv_free(subdiv);
- params.set_output("Geometry", std::move(geometry_set));
#endif
+
+ params.set_output("Geometry", std::move(geometry_set));
}
+
} // namespace blender::nodes
void register_node_type_geo_subdivision_surface()
@@ -129,6 +117,5 @@ void register_node_type_geo_subdivision_surface()
node_type_socket_templates(
&ntype, geo_node_subdivision_surface_in, geo_node_subdivision_surface_out);
ntype.geometry_node_execute = blender::nodes::geo_node_subdivision_surface_exec;
- ntype.draw_buttons = geo_node_subdivision_surface_layout;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_switch.cc b/source/blender/nodes/geometry/nodes/node_geo_switch.cc
new file mode 100644
index 00000000000..0aa5c68aaf5
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_switch.cc
@@ -0,0 +1,191 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "node_geometry_util.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+static bNodeSocketTemplate geo_node_switch_in[] = {
+ {SOCK_BOOLEAN, N_("Switch")},
+
+ {SOCK_FLOAT, N_("False"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX},
+ {SOCK_FLOAT, N_("True"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX},
+ {SOCK_INT, N_("False"), 0, 0, 0, 0, -100000, 100000},
+ {SOCK_INT, N_("True"), 0, 0, 0, 0, -100000, 100000},
+ {SOCK_BOOLEAN, N_("False")},
+ {SOCK_BOOLEAN, N_("True")},
+ {SOCK_VECTOR, N_("False"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX},
+ {SOCK_VECTOR, N_("True"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX},
+ {SOCK_RGBA, N_("False"), 0.8, 0.8, 0.8, 1.0},
+ {SOCK_RGBA, N_("True"), 0.8, 0.8, 0.8, 1.0},
+ {SOCK_STRING, N_("False")},
+ {SOCK_STRING, N_("True")},
+ {SOCK_GEOMETRY, N_("False")},
+ {SOCK_GEOMETRY, N_("True")},
+ {SOCK_OBJECT, N_("False")},
+ {SOCK_OBJECT, N_("True")},
+ {SOCK_COLLECTION, N_("False")},
+ {SOCK_COLLECTION, N_("True")},
+ {SOCK_TEXTURE, N_("False")},
+ {SOCK_TEXTURE, N_("True")},
+ {SOCK_MATERIAL, N_("False")},
+ {SOCK_MATERIAL, N_("True")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_switch_out[] = {
+ {SOCK_FLOAT, N_("Output")},
+ {SOCK_INT, N_("Output")},
+ {SOCK_BOOLEAN, N_("Output")},
+ {SOCK_VECTOR, N_("Output")},
+ {SOCK_RGBA, N_("Output")},
+ {SOCK_STRING, N_("Output")},
+ {SOCK_GEOMETRY, N_("Output")},
+ {SOCK_OBJECT, N_("Output")},
+ {SOCK_COLLECTION, N_("Output")},
+ {SOCK_TEXTURE, N_("Output")},
+ {SOCK_MATERIAL, N_("Output")},
+ {-1, ""},
+};
+
+static void geo_node_switch_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "input_type", 0, "", ICON_NONE);
+}
+
+static void geo_node_switch_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeSwitch *data = (NodeSwitch *)MEM_callocN(sizeof(NodeSwitch), __func__);
+ data->input_type = SOCK_GEOMETRY;
+ node->storage = data;
+}
+
+namespace blender::nodes {
+
+static void geo_node_switch_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeSwitch *node_storage = (NodeSwitch *)node->storage;
+ int index = 0;
+ LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) {
+ nodeSetSocketAvailability(
+ socket, index == 0 || socket->type == (eNodeSocketDatatype)node_storage->input_type);
+ index++;
+ }
+ LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) {
+ nodeSetSocketAvailability(socket,
+ socket->type == (eNodeSocketDatatype)node_storage->input_type);
+ }
+}
+
+template<typename T>
+static void output_input(GeoNodeExecParams &params,
+ const bool input,
+ const StringRef input_suffix,
+ const StringRef output_identifier)
+{
+ const std::string name_a = "False" + input_suffix;
+ const std::string name_b = "True" + input_suffix;
+ if (input) {
+ params.set_input_unused(name_a);
+ if (params.lazy_require_input(name_b)) {
+ return;
+ }
+ params.set_output(output_identifier, params.extract_input<T>(name_b));
+ }
+ else {
+ params.set_input_unused(name_b);
+ if (params.lazy_require_input(name_a)) {
+ return;
+ }
+ params.set_output(output_identifier, params.extract_input<T>(name_a));
+ }
+}
+
+static void geo_node_switch_exec(GeoNodeExecParams params)
+{
+ if (params.lazy_require_input("Switch")) {
+ return;
+ }
+ const NodeSwitch &storage = *(const NodeSwitch *)params.node().storage;
+ const bool input = params.get_input<bool>("Switch");
+ switch ((eNodeSocketDatatype)storage.input_type) {
+ case SOCK_FLOAT: {
+ output_input<float>(params, input, "", "Output");
+ break;
+ }
+ case SOCK_INT: {
+ output_input<int>(params, input, "_001", "Output_001");
+ break;
+ }
+ case SOCK_BOOLEAN: {
+ output_input<bool>(params, input, "_002", "Output_002");
+ break;
+ }
+ case SOCK_VECTOR: {
+ output_input<float3>(params, input, "_003", "Output_003");
+ break;
+ }
+ case SOCK_RGBA: {
+ output_input<ColorGeometry4f>(params, input, "_004", "Output_004");
+ break;
+ }
+ case SOCK_STRING: {
+ output_input<std::string>(params, input, "_005", "Output_005");
+ break;
+ }
+ case SOCK_GEOMETRY: {
+ output_input<GeometrySet>(params, input, "_006", "Output_006");
+ break;
+ }
+ case SOCK_OBJECT: {
+ output_input<Object *>(params, input, "_007", "Output_007");
+ break;
+ }
+ case SOCK_COLLECTION: {
+ output_input<Collection *>(params, input, "_008", "Output_008");
+ break;
+ }
+ case SOCK_TEXTURE: {
+ output_input<Tex *>(params, input, "_009", "Output_009");
+ break;
+ }
+ case SOCK_MATERIAL: {
+ output_input<Material *>(params, input, "_010", "Output_010");
+ break;
+ }
+ default:
+ BLI_assert_unreachable();
+ break;
+ }
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_switch()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_SWITCH, "Switch", NODE_CLASS_CONVERTOR, 0);
+ node_type_socket_templates(&ntype, geo_node_switch_in, geo_node_switch_out);
+ node_type_init(&ntype, geo_node_switch_init);
+ node_type_update(&ntype, blender::nodes::geo_node_switch_update);
+ node_type_storage(&ntype, "NodeSwitch", node_free_standard_storage, node_copy_standard_storage);
+ ntype.geometry_node_execute = blender::nodes::geo_node_switch_exec;
+ ntype.geometry_node_execute_supports_laziness = true;
+ ntype.draw_buttons = geo_node_switch_layout;
+ 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 842a78b839a..cdd2df56b77 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_transform.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_transform.cc
@@ -18,12 +18,14 @@
# include <openvdb/openvdb.h>
#endif
-#include "BLI_math_matrix.h"
+#include "BLI_float4x4.hh"
+#include "DNA_mesh_types.h"
#include "DNA_pointcloud_types.h"
#include "DNA_volume_types.h"
#include "BKE_mesh.h"
+#include "BKE_spline.hh"
#include "BKE_volume.h"
#include "DEG_depsgraph_query.h"
@@ -81,14 +83,14 @@ void transform_mesh(Mesh *mesh,
/* Use only translation if rotation and scale are zero. */
if (use_translate(rotation, scale)) {
if (!translation.is_zero()) {
- BKE_mesh_translate(mesh, translation, true);
+ BKE_mesh_translate(mesh, translation, false);
}
}
else {
- float mat[4][4];
- loc_eul_size_to_mat4(mat, translation, rotation, scale);
- BKE_mesh_transform(mesh, mat, true);
- BKE_mesh_calc_normals(mesh);
+ const float4x4 matrix = float4x4::from_loc_eul_scale(translation, rotation, scale);
+ BKE_mesh_transform(mesh, matrix.values, false);
+ mesh->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
+ mesh->runtime.cd_dirty_poly |= CD_MASK_NORMAL;
}
}
@@ -99,15 +101,15 @@ static void transform_pointcloud(PointCloud *pointcloud,
{
/* Use only translation if rotation and scale don't apply. */
if (use_translate(rotation, scale)) {
- for (int i = 0; i < pointcloud->totpoint; i++) {
+ for (const int i : IndexRange(pointcloud->totpoint)) {
add_v3_v3(pointcloud->co[i], translation);
}
}
else {
- float mat[4][4];
- loc_eul_size_to_mat4(mat, translation, rotation, scale);
- for (int i = 0; i < pointcloud->totpoint; i++) {
- mul_m4_v3(mat, pointcloud->co[i]);
+ const float4x4 matrix = float4x4::from_loc_eul_scale(translation, rotation, scale);
+ for (const int i : IndexRange(pointcloud->totpoint)) {
+ float3 &co = *(float3 *)pointcloud->co[i];
+ co = matrix * co;
}
}
}
@@ -117,7 +119,7 @@ static void transform_instances(InstancesComponent &instances,
const float3 rotation,
const float3 scale)
{
- MutableSpan<float4x4> transforms = instances.transforms();
+ MutableSpan<float4x4> transforms = instances.instance_transforms();
/* Use only translation if rotation and scale don't apply. */
if (use_translate(rotation, scale)) {
@@ -126,11 +128,9 @@ static void transform_instances(InstancesComponent &instances,
}
}
else {
- float mat[4][4];
-
- loc_eul_size_to_mat4(mat, translation, rotation, scale);
+ const float4x4 matrix = float4x4::from_loc_eul_scale(translation, rotation, scale);
for (float4x4 &transform : transforms) {
- mul_m4_m4_pre(transform.ptr(), mat);
+ transform = matrix * transform;
}
}
}
@@ -149,11 +149,10 @@ static void transform_volume(Volume *volume,
(scale.z == 0.0f) ? FLT_EPSILON : scale.z,
};
- Main *bmain = DEG_get_bmain(params.depsgraph());
+ const Main *bmain = DEG_get_bmain(params.depsgraph());
BKE_volume_load(volume, bmain);
- float matrix[4][4];
- loc_eul_size_to_mat4(matrix, translation, rotation, limited_scale);
+ const float4x4 matrix = float4x4::from_loc_eul_scale(translation, rotation, limited_scale);
openvdb::Mat4s vdb_matrix;
memcpy(vdb_matrix.asPointer(), matrix, sizeof(float[4][4]));
@@ -161,7 +160,7 @@ static void transform_volume(Volume *volume,
const int num_grids = BKE_volume_num_grids(volume);
for (const int i : IndexRange(num_grids)) {
- VolumeGrid *volume_grid = BKE_volume_grid_get(volume, i);
+ VolumeGrid *volume_grid = BKE_volume_grid_get_for_write(volume, i);
openvdb::GridBase::Ptr grid = BKE_volume_grid_openvdb_for_write(volume, volume_grid, false);
openvdb::math::Transform &grid_transform = grid->transform();
@@ -172,6 +171,20 @@ static void transform_volume(Volume *volume,
#endif
}
+static void transform_curve(CurveEval &curve,
+ const float3 translation,
+ const float3 rotation,
+ const float3 scale)
+{
+ if (use_translate(rotation, scale)) {
+ curve.translate(translation);
+ }
+ else {
+ const float4x4 matrix = float4x4::from_loc_eul_scale(translation, rotation, scale);
+ curve.transform(matrix);
+ }
+}
+
static void geo_node_transform_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
@@ -183,21 +196,22 @@ static void geo_node_transform_exec(GeoNodeExecParams params)
Mesh *mesh = geometry_set.get_mesh_for_write();
transform_mesh(mesh, translation, rotation, scale);
}
-
if (geometry_set.has_pointcloud()) {
PointCloud *pointcloud = geometry_set.get_pointcloud_for_write();
transform_pointcloud(pointcloud, translation, rotation, scale);
}
-
if (geometry_set.has_instances()) {
InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>();
transform_instances(instances, translation, rotation, scale);
}
-
if (geometry_set.has_volume()) {
Volume *volume = geometry_set.get_volume_for_write();
transform_volume(volume, translation, rotation, scale, params);
}
+ if (geometry_set.has_curve()) {
+ CurveEval *curve = geometry_set.get_curve_for_write();
+ transform_curve(*curve, translation, rotation, scale);
+ }
params.set_output("Geometry", std::move(geometry_set));
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc
index 0c754ad3b37..03581b28f18 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc
@@ -14,10 +14,6 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "DNA_node_types.h"
-
-#include "RNA_enum_types.h"
-
#include "UI_interface.h"
#include "UI_resources.h"
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 54dccb613a1..403f4906d07 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
@@ -23,6 +23,7 @@
#include "node_geometry_util.hh"
#include "BKE_lib_id.h"
+#include "BKE_material.h"
#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
#include "BKE_volume.h"
@@ -36,8 +37,8 @@
static bNodeSocketTemplate geo_node_volume_to_mesh_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
- {SOCK_STRING, N_("Grid")},
- {SOCK_FLOAT, N_("Voxel Size"), 0.3f, 0.0f, 0.0f, 0.0f, 0.01f, FLT_MAX},
+ {SOCK_STRING, N_("Density")},
+ {SOCK_FLOAT, N_("Voxel Size"), 0.3f, 0.0f, 0.0f, 0.0f, 0.01f, FLT_MAX, PROP_DISTANCE},
{SOCK_FLOAT, N_("Voxel Amount"), 64.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX},
{SOCK_FLOAT, N_("Threshold"), 0.1f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX},
{SOCK_FLOAT, N_("Adaptivity"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR},
@@ -64,7 +65,7 @@ static void geo_node_volume_to_mesh_init(bNodeTree *UNUSED(ntree), bNode *node)
sizeof(NodeGeometryVolumeToMesh), __func__);
data->resolution_mode = VOLUME_TO_MESH_RESOLUTION_MODE_GRID;
- bNodeSocket *grid_socket = nodeFindSocket(node, SOCK_IN, "Grid");
+ bNodeSocket *grid_socket = nodeFindSocket(node, SOCK_IN, "Density");
bNodeSocketValueString *grid_socket_value = (bNodeSocketValueString *)grid_socket->default_value;
STRNCPY(grid_socket_value->value, "density");
@@ -117,11 +118,11 @@ static void create_mesh_from_volume(GeometrySet &geometry_set_in,
return;
}
- Main *bmain = DEG_get_bmain(params.depsgraph());
- BKE_volume_load(const_cast<Volume *>(volume), bmain);
+ const Main *bmain = DEG_get_bmain(params.depsgraph());
+ BKE_volume_load(volume, bmain);
- const std::string grid_name = params.get_input<std::string>("Grid");
- VolumeGrid *volume_grid = BKE_volume_grid_find(volume, grid_name.c_str());
+ const std::string grid_name = params.get_input<std::string>("Density");
+ const VolumeGrid *volume_grid = BKE_volume_grid_find_for_read(volume, grid_name.c_str());
if (volume_grid == nullptr) {
return;
}
@@ -134,6 +135,7 @@ static void create_mesh_from_volume(GeometrySet &geometry_set_in,
if (mesh == nullptr) {
return;
}
+ BKE_id_material_eval_ensure_default_slot(&mesh->id);
MeshComponent &dst_component = geometry_set_out.get_component_for_write<MeshComponent>();
dst_component.replace(mesh);
}
diff --git a/source/blender/nodes/intern/derived_node_tree.cc b/source/blender/nodes/intern/derived_node_tree.cc
index e4f8a0c5fda..9a3eb574bcd 100644
--- a/source/blender/nodes/intern/derived_node_tree.cc
+++ b/source/blender/nodes/intern/derived_node_tree.cc
@@ -40,6 +40,7 @@ DTreeContext &DerivedNodeTree::construct_context_recursively(DTreeContext *paren
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_);
@@ -83,6 +84,16 @@ bool DerivedNodeTree::has_link_cycles() const
return false;
}
+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()) {
+ return true;
+ }
+ }
+ return false;
+}
+
/* Calls the given callback on all nodes in the (possibly nested) derived node tree. */
void DerivedNodeTree::foreach_node(FunctionRef<void(DNode)> callback) const
{
@@ -167,10 +178,10 @@ DInputSocket DOutputSocket::get_active_corresponding_group_output_socket() const
return {};
}
-/* Call the given callback for every "real" origin socket. "Real" means that reroutes, muted nodes
+/* Call `origin_fn` for every "real" origin socket. "Real" means that reroutes, muted nodes
* and node groups are handled by this function. Origin sockets are ones where a node gets its
* inputs from. */
-void DInputSocket::foreach_origin_socket(FunctionRef<void(DSocket)> callback) 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()) {
@@ -180,18 +191,18 @@ void DInputSocket::foreach_origin_socket(FunctionRef<void(DSocket)> callback) co
if (linked_node.is_group_input_node()) {
if (context_->is_root()) {
/* This is a group input in the root node group. */
- callback(linked_dsocket);
+ origin_fn(linked_dsocket);
}
else {
DInputSocket socket_in_parent_group = linked_dsocket.get_corresponding_group_node_input();
if (socket_in_parent_group->is_logically_linked()) {
/* Follow the links coming into the corresponding socket on the parent group node. */
- socket_in_parent_group.foreach_origin_socket(callback);
+ socket_in_parent_group.foreach_origin_socket(origin_fn);
}
else {
/* The corresponding input on the parent group node is not connected. Therefore, we use
* the value of that input socket directly. */
- callback(socket_in_parent_group);
+ origin_fn(socket_in_parent_group);
}
}
}
@@ -200,27 +211,32 @@ void DInputSocket::foreach_origin_socket(FunctionRef<void(DSocket)> callback) co
if (socket_in_group) {
if (socket_in_group->is_logically_linked()) {
/* Follow the links coming into the group output node of the child node group. */
- socket_in_group.foreach_origin_socket(callback);
+ socket_in_group.foreach_origin_socket(origin_fn);
}
else {
/* The output of the child node group is not connected, so we have to get the value from
* that socket. */
- callback(socket_in_group);
+ origin_fn(socket_in_group);
}
}
}
else {
/* The normal case: just use the value of a linked output socket. */
- callback(linked_dsocket);
+ origin_fn(linked_dsocket);
}
}
}
-/* Calls the given callback for every "real" target socket. "Real" means that reroutes, muted nodes
+/* Calls `target_fn` for every "real" target socket. "Real" means that reroutes, muted nodes
* and node groups are handled by this function. Target sockets are on the nodes that use the value
- * from this socket. */
-void DOutputSocket::foreach_target_socket(FunctionRef<void(DInputSocket)> callback) const
+ * from this socket. The `skipped_fn` function is called for sockets that have been skipped during
+ * the search for target sockets (e.g. reroutes). */
+void DOutputSocket::foreach_target_socket(FunctionRef<void(DInputSocket)> target_fn,
+ FunctionRef<void(DSocket)> skipped_fn) const
{
+ for (const SocketRef *skipped_socket : socket_ref_->logically_linked_skipped_sockets()) {
+ skipped_fn.call_safe({context_, skipped_socket});
+ }
for (const InputSocketRef *linked_socket : socket_ref_->as_output().logically_linked_sockets()) {
const NodeRef &linked_node = linked_socket->node();
DInputSocket linked_dsocket{context_, linked_socket};
@@ -228,26 +244,30 @@ void DOutputSocket::foreach_target_socket(FunctionRef<void(DInputSocket)> callba
if (linked_node.is_group_output_node()) {
if (context_->is_root()) {
/* This is a group output in the root node group. */
- callback(linked_dsocket);
+ target_fn(linked_dsocket);
}
else {
/* Follow the links going out of the group node in the parent node group. */
DOutputSocket socket_in_parent_group =
linked_dsocket.get_corresponding_group_node_output();
- socket_in_parent_group.foreach_target_socket(callback);
+ skipped_fn.call_safe(linked_dsocket);
+ skipped_fn.call_safe(socket_in_parent_group);
+ socket_in_parent_group.foreach_target_socket(target_fn, skipped_fn);
}
}
else if (linked_node.is_group_node()) {
/* Follow the links within the nested node group. */
Vector<DOutputSocket> sockets_in_group =
linked_dsocket.get_corresponding_group_input_sockets();
+ skipped_fn.call_safe(linked_dsocket);
for (DOutputSocket socket_in_group : sockets_in_group) {
- socket_in_group.foreach_target_socket(callback);
+ skipped_fn.call_safe(socket_in_group);
+ socket_in_group.foreach_target_socket(target_fn, skipped_fn);
}
}
else {
/* The normal case: just use the linked input socket as target. */
- callback(linked_dsocket);
+ target_fn(linked_dsocket);
}
}
}
diff --git a/source/blender/nodes/intern/math_functions.cc b/source/blender/nodes/intern/math_functions.cc
index fb34144abf6..aa23777b664 100644
--- a/source/blender/nodes/intern/math_functions.cc
+++ b/source/blender/nodes/intern/math_functions.cc
@@ -209,6 +209,8 @@ const FloatMathOperationInfo *get_float3_math_operation_info(const int operation
RETURN_OPERATION_INFO("Refract", "vector_math_refract");
case NODE_VECTOR_MATH_FACEFORWARD:
RETURN_OPERATION_INFO("Faceforward", "vector_math_faceforward");
+ case NODE_VECTOR_MATH_MULTIPLY_ADD:
+ RETURN_OPERATION_INFO("Multiply Add", "vector_math_multiply_add");
}
#undef RETURN_OPERATION_INFO
diff --git a/source/blender/nodes/intern/node_common.c b/source/blender/nodes/intern/node_common.c
index 0b1ab85c059..7fc9f664df0 100644
--- a/source/blender/nodes/intern/node_common.c
+++ b/source/blender/nodes/intern/node_common.c
@@ -79,12 +79,12 @@ void node_group_label(bNodeTree *UNUSED(ntree), bNode *node, char *label, int ma
BLI_strncpy(label, (node->id) ? node->id->name + 2 : IFACE_("Missing Data-Block"), maxlen);
}
-bool node_group_poll_instance(bNode *node, bNodeTree *nodetree)
+bool node_group_poll_instance(bNode *node, bNodeTree *nodetree, const char **disabled_hint)
{
- if (node->typeinfo->poll(node->typeinfo, nodetree)) {
+ if (node->typeinfo->poll(node->typeinfo, nodetree, disabled_hint)) {
bNodeTree *grouptree = (bNodeTree *)node->id;
if (grouptree) {
- return nodeGroupPoll(nodetree, grouptree);
+ return nodeGroupPoll(nodetree, grouptree, disabled_hint);
}
return true; /* without a linked node tree, group node is always ok */
@@ -93,25 +93,27 @@ bool node_group_poll_instance(bNode *node, bNodeTree *nodetree)
return false;
}
-int nodeGroupPoll(bNodeTree *nodetree, bNodeTree *grouptree)
+bool nodeGroupPoll(bNodeTree *nodetree, bNodeTree *grouptree, const char **r_disabled_hint)
{
bNode *node;
- int valid = 1;
+ bool valid = true;
/* unspecified node group, generally allowed
* (if anything, should be avoided on operator level)
*/
if (grouptree == NULL) {
- return 1;
+ return true;
}
if (nodetree == grouptree) {
- return 0;
+ *r_disabled_hint = "Nesting a node group inside of itself is not allowed";
+ return false;
}
for (node = grouptree->nodes.first; node; node = node->next) {
- if (node->typeinfo->poll_instance && !node->typeinfo->poll_instance(node, nodetree)) {
- valid = 0;
+ if (node->typeinfo->poll_instance &&
+ !node->typeinfo->poll_instance(node, nodetree, r_disabled_hint)) {
+ valid = false;
break;
}
}
diff --git a/source/blender/nodes/intern/node_common.h b/source/blender/nodes/intern/node_common.h
index 7aad6782640..cdb7b6897b9 100644
--- a/source/blender/nodes/intern/node_common.h
+++ b/source/blender/nodes/intern/node_common.h
@@ -32,7 +32,9 @@ extern "C" {
struct bNodeTree;
void node_group_label(struct bNodeTree *ntree, struct bNode *node, char *label, int maxlen);
-bool node_group_poll_instance(struct bNode *node, struct bNodeTree *nodetree);
+bool node_group_poll_instance(struct bNode *node,
+ struct bNodeTree *nodetree,
+ const char **r_disabled_hint);
void ntree_update_reroute_nodes(struct bNodeTree *ntree);
diff --git a/source/blender/nodes/intern/node_exec.c b/source/blender/nodes/intern/node_exec.cc
index dd9d0b6796a..18403417af3 100644
--- a/source/blender/nodes/intern/node_exec.c
+++ b/source/blender/nodes/intern/node_exec.cc
@@ -47,7 +47,7 @@ bNodeStack *node_get_socket_stack(bNodeStack *stack, bNodeSocket *sock)
if (stack && sock && sock->stack_index >= 0) {
return stack + sock->stack_index;
}
- return NULL;
+ return nullptr;
}
void node_get_stack(bNode *node, bNodeStack *stack, bNodeStack **in, bNodeStack **out)
@@ -56,13 +56,13 @@ void node_get_stack(bNode *node, bNodeStack *stack, bNodeStack **in, bNodeStack
/* build pointer stack */
if (in) {
- for (sock = node->inputs.first; sock; sock = sock->next) {
+ for (sock = (bNodeSocket *)node->inputs.first; sock; sock = sock->next) {
*(in++) = node_get_socket_stack(stack, sock);
}
}
if (out) {
- for (sock = node->outputs.first; sock; sock = sock->next) {
+ for (sock = (bNodeSocket *)node->outputs.first; sock; sock = sock->next) {
*(out++) = node_get_socket_stack(stack, sock);
}
}
@@ -90,7 +90,7 @@ static void node_init_output_index(bNodeSocket *sock, int *index, ListBase *inte
if (internal_links) {
bNodeLink *link;
/* copy the stack index from internally connected input to skip the node */
- for (link = internal_links->first; link; link = link->next) {
+ for (link = (bNodeLink *)internal_links->first; link; link = link->next) {
if (link->tosock == sock) {
sock->stack_index = link->fromsock->stack_index;
/* set the link pointer to indicate that this socket
@@ -128,7 +128,7 @@ static struct bNodeStack *setup_stack(bNodeStack *stack,
{
bNodeStack *ns = node_get_socket_stack(stack, sock);
if (!ns) {
- return NULL;
+ return nullptr;
}
/* don't mess with remote socket stacks, these are initialized by other nodes! */
@@ -166,7 +166,7 @@ bNodeTreeExec *ntree_exec_begin(bNodeExecContext *context,
int index;
bNode **nodelist;
int totnodes, n;
- /* XXX texnodes have threading issues with muting, have to disable it there ... */
+ /* XXX: texture-nodes have threading issues with muting, have to disable it there. */
/* ensure all sock->link pointers and node levels are correct */
/* Using global main here is likely totally wrong, not sure what to do about that one though...
@@ -178,7 +178,7 @@ bNodeTreeExec *ntree_exec_begin(bNodeExecContext *context,
ntreeGetDependencyList(ntree, &nodelist, &totnodes);
/* XXX could let callbacks do this for specialized data */
- exec = MEM_callocN(sizeof(bNodeTreeExec), "node tree execution data");
+ exec = (bNodeTreeExec *)MEM_callocN(sizeof(bNodeTreeExec), "node tree execution data");
/* backpointer to node tree */
exec->nodetree = ntree;
@@ -188,28 +188,29 @@ bNodeTreeExec *ntree_exec_begin(bNodeExecContext *context,
node = nodelist[n];
/* init node socket stack indexes */
- for (sock = node->inputs.first; sock; sock = sock->next) {
+ for (sock = (bNodeSocket *)node->inputs.first; sock; sock = sock->next) {
node_init_input_index(sock, &index);
}
if (node->flag & NODE_MUTED || node->type == NODE_REROUTE) {
- for (sock = node->outputs.first; sock; sock = sock->next) {
+ for (sock = (bNodeSocket *)node->outputs.first; sock; sock = sock->next) {
node_init_output_index(sock, &index, &node->internal_links);
}
}
else {
- for (sock = node->outputs.first; sock; sock = sock->next) {
- node_init_output_index(sock, &index, NULL);
+ for (sock = (bNodeSocket *)node->outputs.first; sock; sock = sock->next) {
+ node_init_output_index(sock, &index, nullptr);
}
}
}
/* allocated exec data pointers for nodes */
exec->totnodes = totnodes;
- exec->nodeexec = MEM_callocN(exec->totnodes * sizeof(bNodeExec), "node execution data");
+ exec->nodeexec = (bNodeExec *)MEM_callocN(exec->totnodes * sizeof(bNodeExec),
+ "node execution data");
/* allocate data pointer for node stack */
exec->stacksize = index;
- exec->stack = MEM_callocN(exec->stacksize * sizeof(bNodeStack), "bNodeStack");
+ exec->stack = (bNodeStack *)MEM_callocN(exec->stacksize * sizeof(bNodeStack), "bNodeStack");
/* all non-const results are considered inputs */
for (n = 0; n < exec->stacksize; n++) {
@@ -222,7 +223,7 @@ bNodeTreeExec *ntree_exec_begin(bNodeExecContext *context,
nodeexec->free_exec_fn = node->typeinfo->free_exec_fn;
/* tag inputs */
- for (sock = node->inputs.first; sock; sock = sock->next) {
+ for (sock = (bNodeSocket *)node->inputs.first; sock; sock = sock->next) {
/* disable the node if an input link is invalid */
if (sock->link && !(sock->link->flag & NODE_LINK_VALID)) {
node->need_exec = 0;
@@ -235,14 +236,14 @@ bNodeTreeExec *ntree_exec_begin(bNodeExecContext *context,
}
/* tag all outputs */
- for (sock = node->outputs.first; sock; sock = sock->next) {
+ for (sock = (bNodeSocket *)node->outputs.first; sock; sock = sock->next) {
/* ns = */ setup_stack(exec->stack, ntree, node, sock);
}
nodekey = BKE_node_instance_key(parent_key, ntree, node);
- nodeexec->data.preview = context->previews ?
- BKE_node_instance_hash_lookup(context->previews, nodekey) :
- NULL;
+ nodeexec->data.preview = context->previews ? (bNodePreview *)BKE_node_instance_hash_lookup(
+ context->previews, nodekey) :
+ nullptr;
if (node->typeinfo->init_exec_fn) {
nodeexec->data.data = node->typeinfo->init_exec_fn(context, node, nodekey);
}
@@ -284,7 +285,7 @@ bNodeThreadStack *ntreeGetThreadStack(bNodeTreeExec *exec, int thread)
ListBase *lb = &exec->threadstack[thread];
bNodeThreadStack *nts;
- for (nts = lb->first; nts; nts = nts->next) {
+ for (nts = (bNodeThreadStack *)lb->first; nts; nts = nts->next) {
if (!nts->used) {
nts->used = true;
break;
@@ -292,8 +293,8 @@ bNodeThreadStack *ntreeGetThreadStack(bNodeTreeExec *exec, int thread)
}
if (!nts) {
- nts = MEM_callocN(sizeof(bNodeThreadStack), "bNodeThreadStack");
- nts->stack = MEM_dupallocN(exec->stack);
+ nts = (bNodeThreadStack *)MEM_callocN(sizeof(bNodeThreadStack), "bNodeThreadStack");
+ nts->stack = (bNodeStack *)MEM_dupallocN(exec->stack);
nts->used = true;
BLI_addtail(lb, nts);
}
@@ -303,13 +304,13 @@ bNodeThreadStack *ntreeGetThreadStack(bNodeTreeExec *exec, int thread)
void ntreeReleaseThreadStack(bNodeThreadStack *nts)
{
- nts->used = 0;
+ nts->used = false;
}
bool ntreeExecThreadNodes(bNodeTreeExec *exec, bNodeThreadStack *nts, void *callerdata, int thread)
{
- bNodeStack *nsin[MAX_SOCKET] = {NULL}; /* arbitrary... watch this */
- bNodeStack *nsout[MAX_SOCKET] = {NULL}; /* arbitrary... watch this */
+ bNodeStack *nsin[MAX_SOCKET] = {nullptr}; /* arbitrary... watch this */
+ bNodeStack *nsout[MAX_SOCKET] = {nullptr}; /* arbitrary... watch this */
bNodeExec *nodeexec;
bNode *node;
int n;
diff --git a/source/blender/nodes/intern/node_geometry_exec.cc b/source/blender/nodes/intern/node_geometry_exec.cc
index a4fb99a988e..188d198e159 100644
--- a/source/blender/nodes/intern/node_geometry_exec.cc
+++ b/source/blender/nodes/intern/node_geometry_exec.cc
@@ -22,6 +22,7 @@
#include "NOD_geometry_exec.hh"
#include "NOD_type_callbacks.hh"
+#include "NOD_type_conversions.hh"
#include "node_geometry_util.hh"
@@ -29,22 +30,22 @@ namespace blender::nodes {
void GeoNodeExecParams::error_message_add(const NodeWarningType type, std::string message) const
{
- bNodeTree *btree_cow = node_->btree();
+ bNodeTree *btree_cow = provider_->dnode->btree();
BLI_assert(btree_cow != nullptr);
if (btree_cow == nullptr) {
return;
}
bNodeTree *btree_original = (bNodeTree *)DEG_get_original_id((ID *)btree_cow);
- const NodeTreeEvaluationContext context(*self_object_, *modifier_);
+ const NodeTreeEvaluationContext context(*provider_->self_object, *provider_->modifier);
BKE_nodetree_error_message_add(
- *btree_original, context, *node_->bnode(), type, std::move(message));
+ *btree_original, context, *provider_->dnode->bnode(), type, std::move(message));
}
const bNodeSocket *GeoNodeExecParams::find_available_socket(const StringRef name) const
{
- for (const InputSocketRef *socket : node_->inputs()) {
+ for (const InputSocketRef *socket : provider_->dnode->inputs()) {
if (socket->is_available() && socket->name() == name) {
return socket->bsocket();
}
@@ -53,22 +54,29 @@ const bNodeSocket *GeoNodeExecParams::find_available_socket(const StringRef name
return nullptr;
}
-ReadAttributePtr GeoNodeExecParams::get_input_attribute(const StringRef name,
- const GeometryComponent &component,
- const AttributeDomain domain,
- const CustomDataType type,
- const void *default_value) const
+GVArrayPtr GeoNodeExecParams::get_input_attribute(const StringRef name,
+ const GeometryComponent &component,
+ const AttributeDomain domain,
+ const CustomDataType type,
+ const void *default_value) const
{
const bNodeSocket *found_socket = this->find_available_socket(name);
BLI_assert(found_socket != nullptr); /* There should always be available socket for the name. */
+ const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(type);
+ const int64_t domain_size = component.attribute_domain_size(domain);
+
+ if (default_value == nullptr) {
+ default_value = cpp_type->default_value();
+ }
+
if (found_socket == nullptr) {
- return component.attribute_get_constant_for_read(domain, type, default_value);
+ return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, default_value);
}
if (found_socket->type == SOCK_STRING) {
const std::string name = this->get_input<std::string>(found_socket->identifier);
/* Try getting the attribute without the default value. */
- ReadAttributePtr attribute = component.attribute_try_get_for_read(name, domain, type);
+ GVArrayPtr attribute = component.attribute_try_get_for_read(name, domain, type);
if (attribute) {
return attribute;
}
@@ -80,25 +88,30 @@ ReadAttributePtr GeoNodeExecParams::get_input_attribute(const StringRef name,
this->error_message_add(NodeWarningType::Error,
TIP_("No attribute with name \"") + name + "\"");
}
- return component.attribute_get_constant_for_read(domain, type, default_value);
+ return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, default_value);
}
+ const DataTypeConversions &conversions = get_implicit_type_conversions();
if (found_socket->type == SOCK_FLOAT) {
const float value = this->get_input<float>(found_socket->identifier);
- return component.attribute_get_constant_for_read_converted(
- domain, CD_PROP_FLOAT, type, &value);
+ BUFFER_FOR_CPP_TYPE_VALUE(*cpp_type, buffer);
+ conversions.convert_to_uninitialized(CPPType::get<float>(), *cpp_type, &value, buffer);
+ return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, buffer);
}
if (found_socket->type == SOCK_VECTOR) {
const float3 value = this->get_input<float3>(found_socket->identifier);
- return component.attribute_get_constant_for_read_converted(
- domain, CD_PROP_FLOAT3, type, &value);
+ BUFFER_FOR_CPP_TYPE_VALUE(*cpp_type, buffer);
+ conversions.convert_to_uninitialized(CPPType::get<float3>(), *cpp_type, &value, buffer);
+ return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, buffer);
}
if (found_socket->type == SOCK_RGBA) {
- const Color4f value = this->get_input<Color4f>(found_socket->identifier);
- return component.attribute_get_constant_for_read_converted(
- domain, CD_PROP_COLOR, type, &value);
+ const ColorGeometry4f value = this->get_input<ColorGeometry4f>(found_socket->identifier);
+ BUFFER_FOR_CPP_TYPE_VALUE(*cpp_type, buffer);
+ conversions.convert_to_uninitialized(
+ CPPType::get<ColorGeometry4f>(), *cpp_type, &value, buffer);
+ return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, buffer);
}
BLI_assert(false);
- return component.attribute_get_constant_for_read(domain, type, default_value);
+ return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, default_value);
}
CustomDataType GeoNodeExecParams::get_input_attribute_data_type(
@@ -114,11 +127,11 @@ CustomDataType GeoNodeExecParams::get_input_attribute_data_type(
if (found_socket->type == SOCK_STRING) {
const std::string name = this->get_input<std::string>(found_socket->identifier);
- ReadAttributePtr attribute = component.attribute_try_get_for_read(name);
- if (!attribute) {
- return default_type;
+ std::optional<AttributeMetaData> info = component.attribute_get_meta_data(name);
+ if (info) {
+ return info->data_type;
}
- return attribute->custom_data_type();
+ return default_type;
}
if (found_socket->type == SOCK_FLOAT) {
return CD_PROP_FLOAT;
@@ -157,9 +170,9 @@ AttributeDomain GeoNodeExecParams::get_highest_priority_input_domain(
if (found_socket->type == SOCK_STRING) {
const std::string name = this->get_input<std::string>(found_socket->identifier);
- ReadAttributePtr attribute = component.attribute_try_get_for_read(name);
- if (attribute) {
- input_domains.append(attribute->domain());
+ std::optional<AttributeMetaData> info = component.attribute_get_meta_data(name);
+ if (info) {
+ input_domains.append(info->domain);
}
}
}
@@ -171,11 +184,11 @@ AttributeDomain GeoNodeExecParams::get_highest_priority_input_domain(
return default_domain;
}
-void GeoNodeExecParams::check_extract_input(StringRef identifier,
- const CPPType *requested_type) const
+void GeoNodeExecParams::check_input_access(StringRef identifier,
+ const CPPType *requested_type) const
{
bNodeSocket *found_socket = nullptr;
- for (const InputSocketRef *socket : node_->inputs()) {
+ for (const InputSocketRef *socket : provider_->dnode->inputs()) {
if (socket->identifier() == identifier) {
found_socket = socket->bsocket();
break;
@@ -185,39 +198,39 @@ void GeoNodeExecParams::check_extract_input(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 : node_->inputs()) {
+ for (const InputSocketRef *socket : provider_->dnode->inputs()) {
if (socket->is_available()) {
std::cout << "'" << socket->identifier() << "', ";
}
}
std::cout << "\n";
- BLI_assert(false);
+ BLI_assert_unreachable();
}
else if (found_socket->flag & SOCK_UNAVAIL) {
std::cout << "The socket corresponding to the identifier '" << identifier
<< "' is disabled.\n";
- BLI_assert(false);
+ BLI_assert_unreachable();
}
- else if (!input_values_.contains(identifier)) {
+ 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(false);
+ BLI_assert_unreachable();
}
else if (requested_type != nullptr) {
const CPPType &expected_type = *socket_cpp_type_get(*found_socket->typeinfo);
if (*requested_type != expected_type) {
std::cout << "The requested type '" << requested_type->name() << "' is incorrect. Expected '"
<< expected_type.name() << "'.\n";
- BLI_assert(false);
+ BLI_assert_unreachable();
}
}
}
-void GeoNodeExecParams::check_set_output(StringRef identifier, const CPPType &value_type) const
+void GeoNodeExecParams::check_output_access(StringRef identifier, const CPPType &value_type) const
{
bNodeSocket *found_socket = nullptr;
- for (const OutputSocketRef *socket : node_->outputs()) {
+ for (const OutputSocketRef *socket : provider_->dnode->outputs()) {
if (socket->identifier() == identifier) {
found_socket = socket->bsocket();
break;
@@ -227,29 +240,29 @@ void GeoNodeExecParams::check_set_output(StringRef identifier, const CPPType &va
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 : node_->outputs()) {
+ for (const OutputSocketRef *socket : provider_->dnode->outputs()) {
if (socket->is_available()) {
std::cout << "'" << socket->identifier() << "', ";
}
}
std::cout << "\n";
- BLI_assert(false);
+ BLI_assert_unreachable();
}
else if (found_socket->flag & SOCK_UNAVAIL) {
std::cout << "The socket corresponding to the identifier '" << identifier
<< "' is disabled.\n";
- BLI_assert(false);
+ BLI_assert_unreachable();
}
- else if (output_values_.contains(identifier)) {
+ else if (!provider_->can_set_output(identifier)) {
std::cout << "The identifier '" << identifier << "' has been set already.\n";
- BLI_assert(false);
+ BLI_assert_unreachable();
}
else {
const CPPType &expected_type = *socket_cpp_type_get(*found_socket->typeinfo);
if (value_type != expected_type) {
std::cout << "The value type '" << value_type.name() << "' is incorrect. Expected '"
<< expected_type.name() << "'.\n";
- BLI_assert(false);
+ BLI_assert_unreachable();
}
}
}
diff --git a/source/blender/nodes/intern/node_socket.cc b/source/blender/nodes/intern/node_socket.cc
index 26df3f77738..d00bf636e15 100644
--- a/source/blender/nodes/intern/node_socket.cc
+++ b/source/blender/nodes/intern/node_socket.cc
@@ -35,9 +35,9 @@
#include "BKE_geometry_set.hh"
#include "BKE_lib_id.h"
#include "BKE_node.h"
-#include "BKE_persistent_data_handle.hh"
#include "DNA_collection_types.h"
+#include "DNA_material_types.h"
#include "RNA_access.h"
#include "RNA_types.h"
@@ -52,7 +52,7 @@
struct bNodeSocket *node_add_socket_from_template(struct bNodeTree *ntree,
struct bNode *node,
struct bNodeSocketTemplate *stemp,
- int in_out)
+ eNodeSocketInOut in_out)
{
bNodeSocket *sock = nodeAddStaticSocket(
ntree, node, in_out, stemp->type, stemp->subtype, stemp->identifier, stemp->name);
@@ -102,8 +102,11 @@ struct bNodeSocket *node_add_socket_from_template(struct bNodeTree *ntree,
return sock;
}
-static bNodeSocket *verify_socket_template(
- bNodeTree *ntree, bNode *node, int in_out, ListBase *socklist, bNodeSocketTemplate *stemp)
+static bNodeSocket *verify_socket_template(bNodeTree *ntree,
+ bNode *node,
+ eNodeSocketInOut in_out,
+ ListBase *socklist,
+ bNodeSocketTemplate *stemp)
{
bNodeSocket *sock;
@@ -132,7 +135,7 @@ static bNodeSocket *verify_socket_template(
static void verify_socket_template_list(bNodeTree *ntree,
bNode *node,
- int in_out,
+ eNodeSocketInOut in_out,
ListBase *socklist,
bNodeSocketTemplate *stemp_first)
{
@@ -291,6 +294,21 @@ void node_socket_init_default_value(bNodeSocket *sock)
sock->default_value = dval;
break;
+ }
+ case SOCK_TEXTURE: {
+ bNodeSocketValueTexture *dval = (bNodeSocketValueTexture *)MEM_callocN(
+ sizeof(bNodeSocketValueTexture), "node socket value texture");
+ dval->value = nullptr;
+
+ sock->default_value = dval;
+ break;
+ }
+ case SOCK_MATERIAL: {
+ bNodeSocketValueMaterial *dval = (bNodeSocketValueMaterial *)MEM_callocN(
+ sizeof(bNodeSocketValueMaterial), "node socket value material");
+ dval->value = nullptr;
+
+ sock->default_value = dval;
break;
}
}
@@ -372,6 +390,20 @@ void node_socket_copy_default_value(bNodeSocket *to, const bNodeSocket *from)
id_us_plus(&toval->value->id);
break;
}
+ case SOCK_TEXTURE: {
+ bNodeSocketValueTexture *toval = (bNodeSocketValueTexture *)to->default_value;
+ bNodeSocketValueTexture *fromval = (bNodeSocketValueTexture *)from->default_value;
+ *toval = *fromval;
+ id_us_plus(&toval->value->id);
+ break;
+ }
+ case SOCK_MATERIAL: {
+ bNodeSocketValueMaterial *toval = (bNodeSocketValueMaterial *)to->default_value;
+ bNodeSocketValueMaterial *fromval = (bNodeSocketValueMaterial *)from->default_value;
+ *toval = *fromval;
+ id_us_plus(&toval->value->id);
+ break;
+ }
}
to->flag |= (from->flag & SOCK_HIDE_VALUE);
@@ -613,9 +645,9 @@ static bNodeSocketType *make_socket_type_vector(PropertySubType subtype)
static bNodeSocketType *make_socket_type_rgba()
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_RGBA, PROP_NONE);
- socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<blender::Color4f>(); };
+ socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<blender::ColorGeometry4f>(); };
socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) {
- *(blender::Color4f *)r_value = ((bNodeSocketValueRGBA *)socket.default_value)->value;
+ *(blender::ColorGeometry4f *)r_value = ((bNodeSocketValueRGBA *)socket.default_value)->value;
};
return socktype;
}
@@ -630,63 +662,17 @@ static bNodeSocketType *make_socket_type_string()
return socktype;
}
-class ObjectSocketMultiFunction : public blender::fn::MultiFunction {
- private:
- Object *object_;
-
- public:
- ObjectSocketMultiFunction(Object *object) : object_(object)
- {
- static blender::fn::MFSignature signature = create_signature();
- this->set_signature(&signature);
- }
-
- static blender::fn::MFSignature create_signature()
- {
- blender::fn::MFSignatureBuilder signature{"Object Socket"};
- signature.depends_on_context();
- signature.single_output<blender::bke::PersistentObjectHandle>("Object");
- return signature.build();
- }
-
- void call(blender::IndexMask mask,
- blender::fn::MFParams params,
- blender::fn::MFContext context) const override
- {
- blender::MutableSpan output =
- params.uninitialized_single_output<blender::bke::PersistentObjectHandle>(0, "Object");
-
- /* Try to get a handle map, so that the object can be converted to a handle. */
- const blender::bke::PersistentDataHandleMap *handle_map =
- context.get_global_context<blender::bke::PersistentDataHandleMap>(
- "PersistentDataHandleMap");
-
- if (handle_map == nullptr) {
- /* Return empty handles when there is no handle map. */
- output.fill_indices(mask, blender::bke::PersistentObjectHandle());
- return;
- }
-
- blender::bke::PersistentObjectHandle handle = handle_map->lookup(object_);
- for (int64_t i : mask) {
- output[i] = handle;
- }
- }
-};
-
-MAKE_CPP_TYPE(PersistentObjectHandle, blender::bke::PersistentObjectHandle);
-MAKE_CPP_TYPE(PersistentCollectionHandle, blender::bke::PersistentCollectionHandle);
+MAKE_CPP_TYPE(Object, Object *)
+MAKE_CPP_TYPE(Collection, Collection *)
+MAKE_CPP_TYPE(Texture, Tex *)
+MAKE_CPP_TYPE(Material, Material *)
static bNodeSocketType *make_socket_type_object()
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_OBJECT, PROP_NONE);
- socktype->get_cpp_type = []() {
- /* Objects are not passed along as raw pointers, but as handles. */
- return &blender::fn::CPPType::get<blender::bke::PersistentObjectHandle>();
- };
- socktype->expand_in_mf_network = [](blender::nodes::SocketMFNetworkBuilder &builder) {
- Object *object = builder.socket_default_value<bNodeSocketValueObject>()->value;
- builder.construct_generator_fn<ObjectSocketMultiFunction>(object);
+ socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<Object *>(); };
+ socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) {
+ *(Object **)r_value = ((bNodeSocketValueObject *)socket.default_value)->value;
};
return socktype;
}
@@ -704,9 +690,29 @@ static bNodeSocketType *make_socket_type_geometry()
static bNodeSocketType *make_socket_type_collection()
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_COLLECTION, PROP_NONE);
- socktype->get_cpp_type = []() {
- /* Objects are not passed along as raw pointers, but as handles. */
- return &blender::fn::CPPType::get<blender::bke::PersistentCollectionHandle>();
+ socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<Collection *>(); };
+ socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) {
+ *(Collection **)r_value = ((bNodeSocketValueCollection *)socket.default_value)->value;
+ };
+ return socktype;
+}
+
+static bNodeSocketType *make_socket_type_texture()
+{
+ bNodeSocketType *socktype = make_standard_socket_type(SOCK_TEXTURE, PROP_NONE);
+ socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<Tex *>(); };
+ socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) {
+ *(Tex **)r_value = ((bNodeSocketValueTexture *)socket.default_value)->value;
+ };
+ return socktype;
+}
+
+static bNodeSocketType *make_socket_type_material()
+{
+ bNodeSocketType *socktype = make_standard_socket_type(SOCK_MATERIAL, PROP_NONE);
+ socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<Material *>(); };
+ socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) {
+ *(Material **)r_value = ((bNodeSocketValueMaterial *)socket.default_value)->value;
};
return socktype;
}
@@ -721,6 +727,7 @@ void register_standard_node_socket_types(void)
nodeRegisterSocketType(make_socket_type_float(PROP_FACTOR));
nodeRegisterSocketType(make_socket_type_float(PROP_ANGLE));
nodeRegisterSocketType(make_socket_type_float(PROP_TIME));
+ nodeRegisterSocketType(make_socket_type_float(PROP_TIME_ABSOLUTE));
nodeRegisterSocketType(make_socket_type_float(PROP_DISTANCE));
nodeRegisterSocketType(make_socket_type_int(PROP_NONE));
@@ -752,5 +759,9 @@ void register_standard_node_socket_types(void)
nodeRegisterSocketType(make_socket_type_collection());
+ nodeRegisterSocketType(make_socket_type_texture());
+
+ nodeRegisterSocketType(make_socket_type_material());
+
nodeRegisterSocketType(make_socket_type_virtual());
}
diff --git a/source/blender/nodes/intern/node_tree_multi_function.cc b/source/blender/nodes/intern/node_tree_multi_function.cc
index b973350becd..7ab6495f733 100644
--- a/source/blender/nodes/intern/node_tree_multi_function.cc
+++ b/source/blender/nodes/intern/node_tree_multi_function.cc
@@ -15,6 +15,7 @@
*/
#include "NOD_node_tree_multi_function.hh"
+#include "NOD_type_conversions.hh"
#include "FN_multi_function_network_evaluation.hh"
@@ -142,118 +143,16 @@ static void insert_nodes(CommonMFNetworkBuilderData &common)
});
}
-template<typename From, typename To>
-static void add_implicit_conversion(DataTypeConversions &conversions)
-{
- static fn::CustomMF_Convert<From, To> function;
- conversions.add(fn::MFDataType::ForSingle<From>(), fn::MFDataType::ForSingle<To>(), function);
-}
-
-template<typename From, typename To, typename ConversionF>
-static void add_implicit_conversion(DataTypeConversions &conversions,
- StringRef name,
- ConversionF conversion)
-{
- static fn::CustomMF_SI_SO<From, To> function{name, conversion};
- conversions.add(fn::MFDataType::ForSingle<From>(), fn::MFDataType::ForSingle<To>(), function);
-}
-
-static DataTypeConversions create_implicit_conversions()
-{
- DataTypeConversions conversions;
- add_implicit_conversion<float, float2>(conversions);
- add_implicit_conversion<float, float3>(conversions);
- add_implicit_conversion<float, int32_t>(conversions);
- add_implicit_conversion<float, bool>(conversions);
- add_implicit_conversion<float, Color4f>(
- conversions, "float to Color4f", [](float a) { return Color4f(a, a, a, 1.0f); });
-
- add_implicit_conversion<float2, float3>(
- conversions, "float2 to float3", [](float2 a) { return float3(a.x, a.y, 0.0f); });
- add_implicit_conversion<float2, float>(
- conversions, "float2 to float", [](float2 a) { return (a.x + a.y) / 2.0f; });
- add_implicit_conversion<float2, int32_t>(
- conversions, "float2 to int32_t", [](float2 a) { return (int32_t)((a.x + a.y) / 2.0f); });
- add_implicit_conversion<float2, bool>(
- conversions, "float2 to bool", [](float2 a) { return !is_zero_v2(a); });
- add_implicit_conversion<float2, Color4f>(
- conversions, "float2 to Color4f", [](float2 a) { return Color4f(a.x, a.y, 0.0f, 1.0f); });
-
- add_implicit_conversion<float3, bool>(
- conversions, "float3 to boolean", [](float3 a) { return !is_zero_v3(a); });
- add_implicit_conversion<float3, float>(
- conversions, "float3 to float", [](float3 a) { return (a.x + a.y + a.z) / 3.0f; });
- add_implicit_conversion<float3, int32_t>(
- conversions, "float3 to int32_t", [](float3 a) { return (int)((a.x + a.y + a.z) / 3.0f); });
- add_implicit_conversion<float3, float2>(conversions);
- add_implicit_conversion<float3, Color4f>(
- conversions, "float3 to Color4f", [](float3 a) { return Color4f(a.x, a.y, a.z, 1.0f); });
-
- add_implicit_conversion<int32_t, bool>(conversions);
- add_implicit_conversion<int32_t, float>(conversions);
- add_implicit_conversion<int32_t, float2>(
- conversions, "int32 to float2", [](int32_t a) { return float2((float)a); });
- add_implicit_conversion<int32_t, float3>(
- conversions, "int32 to float3", [](int32_t a) { return float3((float)a); });
- add_implicit_conversion<int32_t, Color4f>(conversions, "int32 to Color4f", [](int32_t a) {
- return Color4f((float)a, (float)a, (float)a, 1.0f);
- });
-
- add_implicit_conversion<bool, float>(conversions);
- add_implicit_conversion<bool, int32_t>(conversions);
- add_implicit_conversion<bool, float2>(
- conversions, "boolean to float2", [](bool a) { return (a) ? float2(1.0f) : float2(0.0f); });
- add_implicit_conversion<bool, float3>(
- conversions, "boolean to float3", [](bool a) { return (a) ? float3(1.0f) : float3(0.0f); });
- add_implicit_conversion<bool, Color4f>(conversions, "boolean to Color4f", [](bool a) {
- return (a) ? Color4f(1.0f, 1.0f, 1.0f, 1.0f) : Color4f(0.0f, 0.0f, 0.0f, 1.0f);
- });
-
- add_implicit_conversion<Color4f, bool>(conversions, "Color4f to boolean", [](Color4f a) {
- return a.r != 0.0f && a.g != 0.0f && a.b != 0.0f;
- });
- add_implicit_conversion<Color4f, float>(
- conversions, "Color4f to float", [](Color4f a) { return rgb_to_grayscale(a); });
- add_implicit_conversion<Color4f, float2>(
- conversions, "Color4f to float2", [](Color4f a) { return float2(a.r, a.g); });
- add_implicit_conversion<Color4f, float3>(
- conversions, "Color4f to float3", [](Color4f a) { return float3(a.r, a.g, a.b); });
-
- return conversions;
-}
-
-const DataTypeConversions &get_implicit_type_conversions()
-{
- static const DataTypeConversions conversions = create_implicit_conversions();
- return conversions;
-}
-
-void DataTypeConversions::convert(const CPPType &from_type,
- const CPPType &to_type,
- const void *from_value,
- void *to_value) const
-{
- const fn::MultiFunction *fn = this->get_conversion(MFDataType::ForSingle(from_type),
- MFDataType::ForSingle(to_type));
- BLI_assert(fn != nullptr);
-
- fn::MFContextBuilder context;
- fn::MFParamsBuilder params{*fn, 1};
- params.add_readonly_single_input(fn::GSpan(from_type, from_value, 1));
- params.add_uninitialized_single_output(fn::GMutableSpan(to_type, to_value, 1));
- fn->call({0}, params, context);
-}
-
static fn::MFOutputSocket &insert_default_value_for_type(CommonMFNetworkBuilderData &common,
fn::MFDataType type)
{
const fn::MultiFunction *default_fn;
if (type.is_single()) {
- default_fn = &common.resources.construct<fn::CustomMF_GenericConstant>(
+ default_fn = &common.scope.construct<fn::CustomMF_GenericConstant>(
AT, type.single_type(), type.single_type().default_value());
}
else {
- default_fn = &common.resources.construct<fn::CustomMF_GenericConstantArray>(
+ default_fn = &common.scope.construct<fn::CustomMF_GenericConstantArray>(
AT, fn::GSpan(type.vector_base_type()));
}
@@ -320,8 +219,8 @@ static void insert_links_and_unlinked_inputs(CommonMFNetworkBuilderData &common)
const fn::MFDataType from_type = from_socket->data_type();
if (from_type != to_type) {
- const fn::MultiFunction *conversion_fn = get_implicit_type_conversions().get_conversion(
- from_type, to_type);
+ const fn::MultiFunction *conversion_fn =
+ get_implicit_type_conversions().get_conversion_multi_function(from_type, to_type);
if (conversion_fn != nullptr) {
fn::MFNode &node = common.network.add_function(*conversion_fn);
common.network.add_link(*from_socket, node.input(0));
@@ -348,11 +247,11 @@ static void insert_links_and_unlinked_inputs(CommonMFNetworkBuilderData &common)
*/
MFNetworkTreeMap insert_node_tree_into_mf_network(fn::MFNetwork &network,
const DerivedNodeTree &tree,
- ResourceCollector &resources)
+ ResourceScope &scope)
{
MFNetworkTreeMap network_map{tree, network};
- CommonMFNetworkBuilderData common{resources, network, network_map, tree};
+ CommonMFNetworkBuilderData common{scope, network, network_map, tree};
insert_nodes(common);
insert_links_and_unlinked_inputs(common);
@@ -426,7 +325,7 @@ static const fn::MultiFunction &create_function_for_node_that_expands_into_multi
const DNode &dnode,
fn::MFNetwork &network,
MFNetworkTreeMap &network_map,
- ResourceCollector &resources)
+ ResourceScope &scope)
{
Vector<const fn::MFOutputSocket *> dummy_fn_inputs;
for (const InputSocketRef *dsocket : dnode->inputs()) {
@@ -451,7 +350,7 @@ static const fn::MultiFunction &create_function_for_node_that_expands_into_multi
}
}
- fn::MFNetworkEvaluator &fn_evaluator = resources.construct<fn::MFNetworkEvaluator>(
+ fn::MFNetworkEvaluator &fn_evaluator = scope.construct<fn::MFNetworkEvaluator>(
__func__, std::move(dummy_fn_inputs), std::move(dummy_fn_outputs));
return fn_evaluator;
}
@@ -460,16 +359,15 @@ static const fn::MultiFunction &create_function_for_node_that_expands_into_multi
* Returns a single multi-function for every node that supports it. This makes it easier to reuse
* the multi-function implementation of nodes in different contexts.
*/
-MultiFunctionByNode get_multi_function_per_node(const DerivedNodeTree &tree,
- ResourceCollector &resources)
+MultiFunctionByNode get_multi_function_per_node(const DerivedNodeTree &tree, ResourceScope &scope)
{
/* Build a network that nodes can insert themselves into. However, the individual nodes are not
* connected. */
- fn::MFNetwork &network = resources.construct<fn::MFNetwork>(__func__);
+ fn::MFNetwork &network = scope.construct<fn::MFNetwork>(__func__);
MFNetworkTreeMap network_map{tree, network};
MultiFunctionByNode functions_by_node;
- CommonMFNetworkBuilderData common{resources, network, network_map, tree};
+ CommonMFNetworkBuilderData common{scope, network, network_map, tree};
tree.foreach_node([&](DNode dnode) {
const bNodeType *node_type = dnode->typeinfo();
@@ -498,7 +396,7 @@ MultiFunctionByNode get_multi_function_per_node(const DerivedNodeTree &tree,
/* If a node expanded into multiple functions, a new function has to be created that
* combines those. */
const fn::MultiFunction &fn = create_function_for_node_that_expands_into_multiple(
- dnode, network, network_map, resources);
+ dnode, network, network_map, scope);
functions_by_node.add_new(dnode, &fn);
break;
}
diff --git a/source/blender/nodes/intern/node_tree_ref.cc b/source/blender/nodes/intern/node_tree_ref.cc
index e627a7f4497..154ee716153 100644
--- a/source/blender/nodes/intern/node_tree_ref.cc
+++ b/source/blender/nodes/intern/node_tree_ref.cc
@@ -14,6 +14,8 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#include <mutex>
+
#include "NOD_node_tree_ref.hh"
#include "BLI_dot_export.hh"
@@ -105,6 +107,7 @@ NodeTreeRef::NodeTreeRef(bNodeTree *btree) : btree_(btree)
}
}
+ this->create_socket_identifier_maps();
this->create_linked_socket_caches();
for (NodeRef *node : nodes_by_id_) {
@@ -163,7 +166,7 @@ void NodeTreeRef::create_linked_socket_caches()
{
for (InputSocketRef *socket : input_sockets_) {
/* Find directly linked socket based on incident links. */
- Vector<SocketRef *> directly_linked_sockets;
+ Vector<const SocketRef *> directly_linked_sockets;
for (LinkRef *link : socket->directly_linked_links_) {
directly_linked_sockets.append(link->from_);
}
@@ -171,9 +174,14 @@ void NodeTreeRef::create_linked_socket_caches()
directly_linked_sockets.as_span());
/* Find logically linked sockets. */
- Vector<SocketRef *> logically_linked_sockets;
- this->foreach_logical_origin(
- *socket, [&](OutputSocketRef &origin) { logically_linked_sockets.append(&origin); });
+ Vector<const SocketRef *> logically_linked_sockets;
+ Vector<const SocketRef *> logically_linked_skipped_sockets;
+ Vector<const InputSocketRef *> handled_sockets;
+ socket->foreach_logical_origin(
+ [&](const OutputSocketRef &origin) { logically_linked_sockets.append(&origin); },
+ [&](const SocketRef &socket) { logically_linked_skipped_sockets.append(&socket); },
+ false,
+ handled_sockets);
if (logically_linked_sockets == directly_linked_sockets) {
socket->logically_linked_sockets_ = socket->directly_linked_sockets_;
}
@@ -181,11 +189,13 @@ void NodeTreeRef::create_linked_socket_caches()
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<SocketRef *> directly_linked_sockets;
+ Vector<const SocketRef *> directly_linked_sockets;
for (LinkRef *link : socket->directly_linked_links_) {
directly_linked_sockets.append(link->to_);
}
@@ -193,9 +203,13 @@ void NodeTreeRef::create_linked_socket_caches()
directly_linked_sockets.as_span());
/* Find logically linked sockets. */
- Vector<SocketRef *> logically_linked_sockets;
- this->foreach_logical_target(
- *socket, [&](InputSocketRef &target) { logically_linked_sockets.append(&target); });
+ 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_;
}
@@ -203,61 +217,187 @@ void NodeTreeRef::create_linked_socket_caches()
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 NodeTreeRef::foreach_logical_origin(InputSocketRef &socket,
- FunctionRef<void(OutputSocketRef &)> callback,
- bool only_follow_first_input_link)
+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 *> &handled_sockets) const
{
- Span<LinkRef *> links_to_check = socket.directly_linked_links_;
+ /* Protect against loops. */
+ if (handled_sockets.contains(this)) {
+ return;
+ }
+ handled_sockets.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 (LinkRef *link : links_to_check) {
+ for (const LinkRef *link : links_to_check) {
if (link->is_muted()) {
continue;
}
- OutputSocketRef *origin = link->from_;
- NodeRef *origin_node = origin->node_;
- if (origin_node->is_reroute_node()) {
- this->foreach_logical_origin(*origin_node->inputs_[0], callback, false);
+ 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_muted()) {
- for (InternalLinkRef *internal_link : origin_node->internal_links_) {
- if (internal_link->to_ == origin) {
- this->foreach_logical_origin(*internal_link->from_, callback, true);
+ 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, handled_sockets);
+ }
+ 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, handled_sockets);
break;
}
}
}
else {
- callback(*origin);
+ origin_fn(origin);
}
}
}
-void NodeTreeRef::foreach_logical_target(OutputSocketRef &socket,
- FunctionRef<void(InputSocketRef &)> callback)
+void OutputSocketRef::foreach_logical_target(
+ FunctionRef<void(const InputSocketRef &)> target_fn,
+ FunctionRef<void(const SocketRef &)> skipped_fn,
+ Vector<const OutputSocketRef *> &handled_sockets) const
{
- for (LinkRef *link : socket.directly_linked_links_) {
+ /* Protect against loops. */
+ if (handled_sockets.contains(this)) {
+ return;
+ }
+ handled_sockets.append(this);
+
+ for (const LinkRef *link : this->directly_linked_links()) {
if (link->is_muted()) {
continue;
}
- InputSocketRef *target = link->to_;
- NodeRef *target_node = target->node_;
- if (target_node->is_reroute_node()) {
- this->foreach_logical_target(*target_node->outputs_[0], callback);
+ 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_muted()) {
- for (InternalLinkRef *internal_link : target_node->internal_links_) {
- if (internal_link->from_ == target) {
- this->foreach_logical_target(*internal_link->to_, callback);
+ 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, handled_sockets);
+ }
+ 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, handled_sockets);
}
}
}
else {
- callback(*target);
+ target_fn(target);
+ }
+ }
+}
+
+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));
}
}
}
@@ -304,6 +444,21 @@ bool NodeTreeRef::has_link_cycles() const
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 true;
+}
+
std::string NodeTreeRef::to_dot() const
{
dot::DirectedGraph digraph;
diff --git a/source/blender/nodes/intern/node_util.c b/source/blender/nodes/intern/node_util.c
index 00db819721c..1aec280fd2b 100644
--- a/source/blender/nodes/intern/node_util.c
+++ b/source/blender/nodes/intern/node_util.c
@@ -140,8 +140,8 @@ void node_math_update(bNodeTree *UNUSED(ntree), bNode *node)
switch (node->custom1) {
case NODE_MATH_WRAP:
- node_sock_label(sock2, "Min");
- node_sock_label(sock3, "Max");
+ node_sock_label(sock2, "Max");
+ node_sock_label(sock3, "Min");
break;
case NODE_MATH_MULTIPLY_ADD:
node_sock_label(sock2, "Multiplier");
@@ -239,24 +239,21 @@ void node_filter_label(bNodeTree *UNUSED(ntree), bNode *node, char *label, int m
/** \name Link Insertion
* \{ */
-/* test if two sockets are interchangeable */
-static bool node_link_socket_match(bNodeSocket *a, bNodeSocket *b)
+static bool node_link_socket_match(const bNodeSocket *a, const bNodeSocket *b)
{
- /* check if sockets are of the same type */
+ /* Check if sockets are of the same type. */
if (a->typeinfo != b->typeinfo) {
return false;
}
- /* tests if alphabetic prefix matches
- * this allows for imperfect matches, such as numeric suffixes,
- * like Color1/Color2
- */
+ /* Test if alphabetic prefix matches, allowing for imperfect matches, such as numeric suffixes
+ * like Color1/Color2. */
int prefix_len = 0;
- char *ca = a->name, *cb = b->name;
+ const char *ca = a->name, *cb = b->name;
for (; *ca != '\0' && *cb != '\0'; ca++, cb++) {
- /* end of common prefix? */
+ /* End of common prefix? */
if (*ca != *cb) {
- /* prefix delimited by non-alphabetic char */
+ /* Prefix delimited by non-alphabetic char. */
if (isalpha(*ca) || isalpha(*cb)) {
return false;
}
@@ -267,75 +264,73 @@ static bool node_link_socket_match(bNodeSocket *a, bNodeSocket *b)
return prefix_len > 0;
}
-static int node_count_links(bNodeTree *ntree, bNodeSocket *sock)
+static int node_count_links(const bNodeTree *ntree, const bNodeSocket *socket)
{
- bNodeLink *link;
int count = 0;
- for (link = ntree->links.first; link; link = link->next) {
- if (link->fromsock == sock) {
- count++;
- }
- if (link->tosock == sock) {
+ LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
+ if (ELEM(socket, link->fromsock, link->tosock)) {
count++;
}
}
return count;
}
-/* Find an eligible socket for linking. */
-static bNodeSocket *node_find_linkable_socket(bNodeTree *ntree, bNode *node, bNodeSocket *cur)
+static bNodeSocket *node_find_linkable_socket(bNodeTree *ntree,
+ bNode *node,
+ bNodeSocket *to_socket)
{
- bNodeSocket *first = cur->in_out == SOCK_IN ? node->inputs.first : node->outputs.first;
- bNodeSocket *sock;
-
- /* Iterate over all sockets of the target node, to find one that matches the same socket type.
- * The idea behind this is: When a user connects an input to a socket that is
- * already linked (and if its not an Multi Input Socket), we try to find a replacement socket for
- * the link that we try to overwrite and connect that previous link to the new socket. */
- sock = cur->next ? cur->next : first; /* Wrap around the list end. */
- while (sock != cur) {
- if (!nodeSocketIsHidden(sock) && node_link_socket_match(sock, cur)) {
- break;
+ bNodeSocket *first = to_socket->in_out == SOCK_IN ? node->inputs.first : node->outputs.first;
+
+ /* Wrap around the list end. */
+ bNodeSocket *socket_iter = to_socket->next ? to_socket->next : first;
+ while (socket_iter != to_socket) {
+ if (!nodeSocketIsHidden(socket_iter) && node_link_socket_match(socket_iter, to_socket)) {
+ const int link_count = node_count_links(ntree, socket_iter);
+ /* Add one to account for the new link being added. */
+ if (link_count + 1 <= nodeSocketLinkLimit(socket_iter)) {
+ return socket_iter; /* Found a valid free socket we can swap to. */
+ }
}
- sock = sock->next ? sock->next : first; /* Wrap around the list end. */
+ socket_iter = socket_iter->next ? socket_iter->next : first; /* Wrap around the list end. */
}
- if (!nodeSocketIsHidden(sock) && node_link_socket_match(sock, cur)) {
- int link_count = node_count_links(ntree, sock);
- /* Take +1 into account since we would add a new link. */
- if (link_count + 1 <= nodeSocketLinkLimit(sock)) {
- return sock; /* Found a valid free socket we can swap to. */
- }
- }
return NULL;
}
+/**
+ * The idea behind this is: When a user connects an input to a socket that is
+ * already linked (and if its not an Multi Input Socket), we try to find a replacement socket for
+ * the link that we try to overwrite and connect that previous link to the new socket.
+ */
void node_insert_link_default(bNodeTree *ntree, bNode *node, bNodeLink *link)
{
- bNodeSocket *sock = link->tosock;
- bNodeLink *tlink, *tlink_next;
+ bNodeSocket *socket = link->tosock;
if (node != link->tonode) {
return;
}
- for (tlink = ntree->links.first; tlink; tlink = tlink_next) {
- bNodeSocket *new_sock;
- tlink_next = tlink->next;
+ /* If we're not at the link limit of the target socket, we can skip
+ * trying to move existing links to another socket. */
+ const int to_link_limit = nodeSocketLinkLimit(socket);
+ if (socket->total_inputs + 1 < to_link_limit) {
+ return;
+ }
- if (sock != tlink->tosock) {
- continue;
- }
+ LISTBASE_FOREACH_MUTABLE (bNodeLink *, to_link, &ntree->links) {
+ if (socket == to_link->tosock) {
+ bNodeSocket *new_socket = node_find_linkable_socket(ntree, node, socket);
+ if (new_socket && new_socket != socket) {
+ /* Attempt to redirect the existing link to the new socket. */
+ to_link->tosock = new_socket;
+ return;
+ }
- new_sock = node_find_linkable_socket(ntree, node, sock);
- if (new_sock && new_sock != sock) {
- /* redirect existing link */
- tlink->tosock = new_sock;
- }
- else if (!new_sock) {
- /* no possible replacement, remove tlink */
- nodeRemLink(ntree, tlink);
- tlink = NULL;
+ if (new_socket == NULL) {
+ /* No possible replacement, remove the existing link. */
+ nodeRemLink(ntree, to_link);
+ return;
+ }
}
}
}
@@ -464,6 +459,22 @@ static int node_datatype_priority(eNodeSocketDatatype from, eNodeSocketDatatype
return -1;
}
}
+ case SOCK_TEXTURE: {
+ switch (from) {
+ case SOCK_TEXTURE:
+ return 1;
+ default:
+ return -1;
+ }
+ }
+ case SOCK_MATERIAL: {
+ switch (from) {
+ case SOCK_MATERIAL:
+ return 1;
+ default:
+ return -1;
+ }
+ }
default:
return -1;
}
diff --git a/source/blender/nodes/intern/type_callbacks.cc b/source/blender/nodes/intern/type_callbacks.cc
index 26eeaebc6d4..5160432aea5 100644
--- a/source/blender/nodes/intern/type_callbacks.cc
+++ b/source/blender/nodes/intern/type_callbacks.cc
@@ -64,7 +64,8 @@ void socket_expand_in_mf_network(SocketMFNetworkBuilder &builder)
}
else if (socket.typeinfo->get_cpp_value != nullptr) {
const CPPType &type = *socket_cpp_type_get(*socket.typeinfo);
- void *buffer = builder.resources().linear_allocator().allocate(type.size(), type.alignment());
+ void *buffer = builder.resource_scope().linear_allocator().allocate(type.size(),
+ type.alignment());
socket.typeinfo->get_cpp_value(socket, buffer);
builder.set_constant_value(type, buffer);
}
diff --git a/source/blender/nodes/intern/type_conversions.cc b/source/blender/nodes/intern/type_conversions.cc
new file mode 100644
index 00000000000..220e5ea9046
--- /dev/null
+++ b/source/blender/nodes/intern/type_conversions.cc
@@ -0,0 +1,349 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "NOD_type_conversions.hh"
+
+#include "FN_multi_function_builder.hh"
+
+#include "BLI_color.hh"
+#include "BLI_float2.hh"
+#include "BLI_float3.hh"
+
+namespace blender::nodes {
+
+using fn::GVArrayPtr;
+using fn::GVMutableArray;
+using fn::GVMutableArrayPtr;
+using fn::MFDataType;
+
+template<typename From, typename To, To (*ConversionF)(const From &)>
+static void add_implicit_conversion(DataTypeConversions &conversions)
+{
+ const CPPType &from_type = CPPType::get<From>();
+ const CPPType &to_type = CPPType::get<To>();
+ const std::string conversion_name = from_type.name() + " to " + to_type.name();
+
+ static fn::CustomMF_SI_SO<From, To> multi_function{conversion_name, ConversionF};
+ static auto convert_single_to_initialized = [](const void *src, void *dst) {
+ *(To *)dst = ConversionF(*(const From *)src);
+ };
+ static auto convert_single_to_uninitialized = [](const void *src, void *dst) {
+ new (dst) To(ConversionF(*(const From *)src));
+ };
+ conversions.add(fn::MFDataType::ForSingle<From>(),
+ fn::MFDataType::ForSingle<To>(),
+ multi_function,
+ convert_single_to_initialized,
+ convert_single_to_uninitialized);
+}
+
+static float2 float_to_float2(const float &a)
+{
+ return float2(a);
+}
+static float3 float_to_float3(const float &a)
+{
+ return float3(a);
+}
+static int32_t float_to_int(const float &a)
+{
+ return (int32_t)a;
+}
+static bool float_to_bool(const float &a)
+{
+ return a > 0.0f;
+}
+static ColorGeometry4f float_to_color(const float &a)
+{
+ return ColorGeometry4f(a, a, a, 1.0f);
+}
+
+static float3 float2_to_float3(const float2 &a)
+{
+ return float3(a.x, a.y, 0.0f);
+}
+static float float2_to_float(const float2 &a)
+{
+ return (a.x + a.y) / 2.0f;
+}
+static int float2_to_int(const float2 &a)
+{
+ return (int32_t)((a.x + a.y) / 2.0f);
+}
+static bool float2_to_bool(const float2 &a)
+{
+ return !is_zero_v2(a);
+}
+static ColorGeometry4f float2_to_color(const float2 &a)
+{
+ return ColorGeometry4f(a.x, a.y, 0.0f, 1.0f);
+}
+
+static bool float3_to_bool(const float3 &a)
+{
+ return !is_zero_v3(a);
+}
+static float float3_to_float(const float3 &a)
+{
+ return (a.x + a.y + a.z) / 3.0f;
+}
+static int float3_to_int(const float3 &a)
+{
+ return (int)((a.x + a.y + a.z) / 3.0f);
+}
+static float2 float3_to_float2(const float3 &a)
+{
+ return float2(a);
+}
+static ColorGeometry4f float3_to_color(const float3 &a)
+{
+ return ColorGeometry4f(a.x, a.y, a.z, 1.0f);
+}
+
+static bool int_to_bool(const int32_t &a)
+{
+ return a > 0;
+}
+static float int_to_float(const int32_t &a)
+{
+ return (float)a;
+}
+static float2 int_to_float2(const int32_t &a)
+{
+ return float2((float)a);
+}
+static float3 int_to_float3(const int32_t &a)
+{
+ return float3((float)a);
+}
+static ColorGeometry4f int_to_color(const int32_t &a)
+{
+ return ColorGeometry4f((float)a, (float)a, (float)a, 1.0f);
+}
+
+static float bool_to_float(const bool &a)
+{
+ return (bool)a;
+}
+static int32_t bool_to_int(const bool &a)
+{
+ return (int32_t)a;
+}
+static float2 bool_to_float2(const bool &a)
+{
+ return (a) ? float2(1.0f) : float2(0.0f);
+}
+static float3 bool_to_float3(const bool &a)
+{
+ return (a) ? float3(1.0f) : float3(0.0f);
+}
+static ColorGeometry4f bool_to_color(const bool &a)
+{
+ return (a) ? ColorGeometry4f(1.0f, 1.0f, 1.0f, 1.0f) : ColorGeometry4f(0.0f, 0.0f, 0.0f, 1.0f);
+}
+
+static bool color_to_bool(const ColorGeometry4f &a)
+{
+ return rgb_to_grayscale(a) > 0.0f;
+}
+static float color_to_float(const ColorGeometry4f &a)
+{
+ return rgb_to_grayscale(a);
+}
+static int32_t color_to_int(const ColorGeometry4f &a)
+{
+ return (int)rgb_to_grayscale(a);
+}
+static float2 color_to_float2(const ColorGeometry4f &a)
+{
+ return float2(a.r, a.g);
+}
+static float3 color_to_float3(const ColorGeometry4f &a)
+{
+ return float3(a.r, a.g, a.b);
+}
+
+static DataTypeConversions create_implicit_conversions()
+{
+ DataTypeConversions conversions;
+
+ add_implicit_conversion<float, float2, float_to_float2>(conversions);
+ add_implicit_conversion<float, float3, float_to_float3>(conversions);
+ add_implicit_conversion<float, int32_t, float_to_int>(conversions);
+ add_implicit_conversion<float, bool, float_to_bool>(conversions);
+ add_implicit_conversion<float, ColorGeometry4f, float_to_color>(conversions);
+
+ add_implicit_conversion<float2, float3, float2_to_float3>(conversions);
+ add_implicit_conversion<float2, float, float2_to_float>(conversions);
+ add_implicit_conversion<float2, int32_t, float2_to_int>(conversions);
+ add_implicit_conversion<float2, bool, float2_to_bool>(conversions);
+ add_implicit_conversion<float2, ColorGeometry4f, float2_to_color>(conversions);
+
+ add_implicit_conversion<float3, bool, float3_to_bool>(conversions);
+ add_implicit_conversion<float3, float, float3_to_float>(conversions);
+ add_implicit_conversion<float3, int32_t, float3_to_int>(conversions);
+ add_implicit_conversion<float3, float2, float3_to_float2>(conversions);
+ add_implicit_conversion<float3, ColorGeometry4f, float3_to_color>(conversions);
+
+ add_implicit_conversion<int32_t, bool, int_to_bool>(conversions);
+ add_implicit_conversion<int32_t, float, int_to_float>(conversions);
+ add_implicit_conversion<int32_t, float2, int_to_float2>(conversions);
+ add_implicit_conversion<int32_t, float3, int_to_float3>(conversions);
+ add_implicit_conversion<int32_t, ColorGeometry4f, int_to_color>(conversions);
+
+ add_implicit_conversion<bool, float, bool_to_float>(conversions);
+ add_implicit_conversion<bool, int32_t, bool_to_int>(conversions);
+ add_implicit_conversion<bool, float2, bool_to_float2>(conversions);
+ add_implicit_conversion<bool, float3, bool_to_float3>(conversions);
+ add_implicit_conversion<bool, ColorGeometry4f, bool_to_color>(conversions);
+
+ add_implicit_conversion<ColorGeometry4f, bool, color_to_bool>(conversions);
+ add_implicit_conversion<ColorGeometry4f, float, color_to_float>(conversions);
+ add_implicit_conversion<ColorGeometry4f, int32_t, color_to_int>(conversions);
+ add_implicit_conversion<ColorGeometry4f, float2, color_to_float2>(conversions);
+ add_implicit_conversion<ColorGeometry4f, float3, color_to_float3>(conversions);
+
+ return conversions;
+}
+
+const DataTypeConversions &get_implicit_type_conversions()
+{
+ static const DataTypeConversions conversions = create_implicit_conversions();
+ return conversions;
+}
+
+void DataTypeConversions::convert_to_uninitialized(const CPPType &from_type,
+ const CPPType &to_type,
+ const void *from_value,
+ void *to_value) const
+{
+ if (from_type == to_type) {
+ from_type.copy_to_uninitialized(from_value, to_value);
+ return;
+ }
+
+ const ConversionFunctions *functions = this->get_conversion_functions(
+ MFDataType::ForSingle(from_type), MFDataType::ForSingle(to_type));
+ BLI_assert(functions != nullptr);
+
+ functions->convert_single_to_uninitialized(from_value, to_value);
+}
+
+class GVArray_For_ConvertedGVArray : public GVArray {
+ private:
+ GVArrayPtr varray_;
+ const CPPType &from_type_;
+ ConversionFunctions old_to_new_conversions_;
+
+ public:
+ GVArray_For_ConvertedGVArray(GVArrayPtr varray,
+ const CPPType &to_type,
+ const DataTypeConversions &conversions)
+ : GVArray(to_type, varray->size()), varray_(std::move(varray)), from_type_(varray_->type())
+ {
+ old_to_new_conversions_ = *conversions.get_conversion_functions(from_type_, to_type);
+ }
+
+ private:
+ void get_impl(const int64_t index, void *r_value) const override
+ {
+ BUFFER_FOR_CPP_TYPE_VALUE(from_type_, buffer);
+ varray_->get(index, buffer);
+ old_to_new_conversions_.convert_single_to_initialized(buffer, r_value);
+ from_type_.destruct(buffer);
+ }
+
+ void get_to_uninitialized_impl(const int64_t index, void *r_value) const override
+ {
+ BUFFER_FOR_CPP_TYPE_VALUE(from_type_, buffer);
+ varray_->get(index, buffer);
+ old_to_new_conversions_.convert_single_to_uninitialized(buffer, r_value);
+ from_type_.destruct(buffer);
+ }
+};
+
+class GVMutableArray_For_ConvertedGVMutableArray : public GVMutableArray {
+ private:
+ GVMutableArrayPtr varray_;
+ const CPPType &from_type_;
+ ConversionFunctions old_to_new_conversions_;
+ ConversionFunctions new_to_old_conversions_;
+
+ public:
+ GVMutableArray_For_ConvertedGVMutableArray(GVMutableArrayPtr varray,
+ const CPPType &to_type,
+ const DataTypeConversions &conversions)
+ : GVMutableArray(to_type, varray->size()),
+ varray_(std::move(varray)),
+ from_type_(varray_->type())
+ {
+ old_to_new_conversions_ = *conversions.get_conversion_functions(from_type_, to_type);
+ new_to_old_conversions_ = *conversions.get_conversion_functions(to_type, from_type_);
+ }
+
+ private:
+ void get_impl(const int64_t index, void *r_value) const override
+ {
+ BUFFER_FOR_CPP_TYPE_VALUE(from_type_, buffer);
+ varray_->get(index, buffer);
+ old_to_new_conversions_.convert_single_to_initialized(buffer, r_value);
+ from_type_.destruct(buffer);
+ }
+
+ void get_to_uninitialized_impl(const int64_t index, void *r_value) const override
+ {
+ BUFFER_FOR_CPP_TYPE_VALUE(from_type_, buffer);
+ varray_->get(index, buffer);
+ old_to_new_conversions_.convert_single_to_uninitialized(buffer, r_value);
+ from_type_.destruct(buffer);
+ }
+
+ void set_by_move_impl(const int64_t index, void *value) override
+ {
+ BUFFER_FOR_CPP_TYPE_VALUE(from_type_, buffer);
+ new_to_old_conversions_.convert_single_to_uninitialized(value, buffer);
+ varray_->set_by_relocate(index, buffer);
+ }
+};
+
+fn::GVArrayPtr DataTypeConversions::try_convert(fn::GVArrayPtr varray,
+ const CPPType &to_type) const
+{
+ const CPPType &from_type = varray->type();
+ if (from_type == to_type) {
+ return varray;
+ }
+ if (!this->is_convertible(from_type, to_type)) {
+ return {};
+ }
+ return std::make_unique<GVArray_For_ConvertedGVArray>(std::move(varray), to_type, *this);
+}
+
+fn::GVMutableArrayPtr DataTypeConversions::try_convert(fn::GVMutableArrayPtr varray,
+ const CPPType &to_type) const
+{
+ const CPPType &from_type = varray->type();
+ if (from_type == to_type) {
+ return varray;
+ }
+ if (!this->is_convertible(from_type, to_type)) {
+ return {};
+ }
+ return std::make_unique<GVMutableArray_For_ConvertedGVMutableArray>(
+ std::move(varray), to_type, *this);
+}
+
+} // namespace blender::nodes
diff --git a/source/blender/nodes/shader/node_shader_tree.c b/source/blender/nodes/shader/node_shader_tree.c
index 3fb4d10979d..5ec982c4e7f 100644
--- a/source/blender/nodes/shader/node_shader_tree.c
+++ b/source/blender/nodes/shader/node_shader_tree.c
@@ -184,6 +184,12 @@ static bool shader_validate_link(bNodeTree *UNUSED(ntree), bNodeLink *link)
return true;
}
+static bool shader_node_tree_socket_type_valid(eNodeSocketDatatype socket_type,
+ bNodeTreeType *UNUSED(ntreetype))
+{
+ return ELEM(socket_type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_SHADER);
+}
+
bNodeTreeType *ntreeType_Shader;
void register_node_tree_type_sh(void)
@@ -205,6 +211,7 @@ void register_node_tree_type_sh(void)
tt->poll = shader_tree_poll;
tt->get_from_context = shader_get_from_context;
tt->validate_link = shader_validate_link;
+ tt->valid_socket_type = shader_node_tree_socket_type_valid;
tt->rna_ext.srna = &RNA_ShaderNodeTree;
@@ -557,12 +564,14 @@ static bool ntree_shader_has_displacement(bNodeTree *ntree,
/* Non-cycles node is used as an output. */
return false;
}
+
if ((displacement->link != NULL) && !(displacement->link->flag & NODE_LINK_MUTED)) {
*r_node = displacement->link->fromnode;
*r_socket = displacement->link->fromsock;
*r_link = displacement->link;
+ return true;
}
- return displacement->link != NULL;
+ return false;
}
static void ntree_shader_relink_node_normal(bNodeTree *ntree,
@@ -1028,30 +1037,3 @@ void ntreeShaderEndExecTree(bNodeTreeExec *exec)
ntree->execdata = NULL;
}
}
-
-/* TODO: left over from Blender Internal, could reuse for new texture nodes. */
-bool ntreeShaderExecTree(bNodeTree *ntree, int thread)
-{
- ShaderCallData scd;
- bNodeThreadStack *nts = NULL;
- bNodeTreeExec *exec = ntree->execdata;
- int compat;
-
- /* ensure execdata is only initialized once */
- if (!exec) {
- BLI_thread_lock(LOCK_NODES);
- if (!ntree->execdata) {
- ntree->execdata = ntreeShaderBeginExecTree(ntree);
- }
- BLI_thread_unlock(LOCK_NODES);
-
- exec = ntree->execdata;
- }
-
- nts = ntreeGetThreadStack(exec, thread);
- compat = ntreeExecThreadNodes(exec, nts, &scd, thread);
- ntreeReleaseThreadStack(nts);
-
- /* if compat is zero, it has been using non-compatible nodes */
- return compat;
-}
diff --git a/source/blender/nodes/shader/node_shader_util.c b/source/blender/nodes/shader/node_shader_util.c
index 1a2405e021f..abc2c7008c7 100644
--- a/source/blender/nodes/shader/node_shader_util.c
+++ b/source/blender/nodes/shader/node_shader_util.c
@@ -27,14 +27,24 @@
#include "node_exec.h"
-bool sh_node_poll_default(bNodeType *UNUSED(ntype), bNodeTree *ntree)
+bool sh_node_poll_default(bNodeType *UNUSED(ntype), bNodeTree *ntree, const char **r_disabled_hint)
{
- return STREQ(ntree->idname, "ShaderNodeTree");
+ if (!STREQ(ntree->idname, "ShaderNodeTree")) {
+ *r_disabled_hint = "Not a shader node tree";
+ return false;
+ }
+ return true;
}
-static bool sh_fn_poll_default(bNodeType *UNUSED(ntype), bNodeTree *ntree)
+static bool sh_fn_poll_default(bNodeType *UNUSED(ntype),
+ bNodeTree *ntree,
+ const char **r_disabled_hint)
{
- return STREQ(ntree->idname, "ShaderNodeTree") || STREQ(ntree->idname, "GeometryNodeTree");
+ if (!STREQ(ntree->idname, "ShaderNodeTree") && !STREQ(ntree->idname, "GeometryNodeTree")) {
+ *r_disabled_hint = "Not a shader or geometry node tree";
+ return false;
+ }
+ return true;
}
void sh_node_type_base(
@@ -322,3 +332,17 @@ void node_shader_gpu_tex_mapping(GPUMaterial *mat,
}
}
}
+
+void get_XYZ_to_RGB_for_gpu(XYZ_to_RGB *data)
+{
+ const float *xyz_to_rgb = IMB_colormanagement_get_xyz_to_rgb();
+ data->r[0] = xyz_to_rgb[0];
+ data->r[1] = xyz_to_rgb[3];
+ data->r[2] = xyz_to_rgb[6];
+ data->g[0] = xyz_to_rgb[1];
+ data->g[1] = xyz_to_rgb[4];
+ data->g[2] = xyz_to_rgb[7];
+ data->b[0] = xyz_to_rgb[2];
+ data->b[1] = xyz_to_rgb[5];
+ data->b[2] = xyz_to_rgb[8];
+}
diff --git a/source/blender/nodes/shader/node_shader_util.h b/source/blender/nodes/shader/node_shader_util.h
index 91454c3c982..dc44f0fa98f 100644
--- a/source/blender/nodes/shader/node_shader_util.h
+++ b/source/blender/nodes/shader/node_shader_util.h
@@ -80,7 +80,9 @@
extern "C" {
#endif
-bool sh_node_poll_default(struct bNodeType *ntype, struct bNodeTree *ntree);
+bool sh_node_poll_default(struct bNodeType *ntype,
+ struct bNodeTree *ntree,
+ const char **r_disabled_hint);
void sh_node_type_base(
struct bNodeType *ntype, int type, const char *name, short nclass, short flag);
void sh_fn_node_type_base(
@@ -93,6 +95,11 @@ typedef struct ShaderCallData {
int dummy;
} ShaderCallData;
+typedef struct XYZ_to_RGB /* Transposed #imbuf_xyz_to_rgb, passed as 3x vec3. */
+{
+ float r[3], g[3], b[3];
+} XYZ_to_RGB;
+
void nodestack_get_vec(float *in, short type_in, bNodeStack *ns);
void node_gpu_stack_from_data(struct GPUNodeStack *gs, int type, struct bNodeStack *ns);
@@ -111,6 +118,7 @@ void node_shader_gpu_tex_mapping(struct GPUMaterial *mat,
void ntreeExecGPUNodes(struct bNodeTreeExec *exec,
struct GPUMaterial *mat,
struct bNode *output_node);
+void get_XYZ_to_RGB_for_gpu(XYZ_to_RGB *data);
#ifdef __cplusplus
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_curves.c b/source/blender/nodes/shader/nodes/node_shader_curves.cc
index 42299a193e2..f1d5040a292 100644
--- a/source/blender/nodes/shader/nodes/node_shader_curves.c
+++ b/source/blender/nodes/shader/nodes/node_shader_curves.cc
@@ -47,7 +47,7 @@ static void node_shader_exec_curve_vec(void *UNUSED(data),
/* stack order input: vec */
/* stack order output: vec */
nodestack_get_vec(vec, SOCK_VECTOR, in[1]);
- BKE_curvemapping_evaluate3F(node->storage, out[0]->vec, vec);
+ BKE_curvemapping_evaluate3F((CurveMapping *)node->storage, out[0]->vec, vec);
}
static void node_shader_init_curve_vec(bNodeTree *UNUSED(ntree), bNode *node)
@@ -64,7 +64,7 @@ static int gpu_shader_curve_vec(GPUMaterial *mat,
float *array, layer;
int size;
- CurveMapping *cumap = node->storage;
+ CurveMapping *cumap = (CurveMapping *)node->storage;
BKE_curvemapping_table_RGBA(cumap, &array, &size);
GPUNodeLink *tex = GPU_color_band(mat, size, array, &layer);
@@ -104,17 +104,65 @@ static int gpu_shader_curve_vec(GPUMaterial *mat,
GPU_uniform(ext_xyz[2]));
}
+class CurveVecFunction : public blender::fn::MultiFunction {
+ private:
+ const CurveMapping &cumap_;
+
+ public:
+ CurveVecFunction(const CurveMapping &cumap) : cumap_(cumap)
+ {
+ static blender::fn::MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ }
+
+ static blender::fn::MFSignature create_signature()
+ {
+ blender::fn::MFSignatureBuilder signature{"Curve Vec"};
+ signature.single_input<float>("Fac");
+ signature.single_input<blender::float3>("Vector");
+ signature.single_output<blender::float3>("Vector");
+ return signature.build();
+ }
+
+ void call(blender::IndexMask mask,
+ blender::fn::MFParams params,
+ blender::fn::MFContext UNUSED(context)) const override
+ {
+ const blender::VArray<float> &fac = params.readonly_single_input<float>(0, "Fac");
+ const blender::VArray<blender::float3> &vec_in = params.readonly_single_input<blender::float3>(
+ 1, "Vector");
+ blender::MutableSpan<blender::float3> vec_out =
+ params.uninitialized_single_output<blender::float3>(2, "Vector");
+
+ for (int64_t i : mask) {
+ BKE_curvemapping_evaluate3F(&cumap_, vec_out[i], vec_in[i]);
+ if (fac[i] != 1.0f) {
+ interp_v3_v3v3(vec_out[i], vec_in[i], vec_out[i], fac[i]);
+ }
+ }
+ }
+};
+
+static void sh_node_curve_vec_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
+{
+ bNode &bnode = builder.bnode();
+ CurveMapping *cumap = (CurveMapping *)bnode.storage;
+ BKE_curvemapping_init(cumap);
+ builder.construct_and_set_matching_fn<CurveVecFunction>(*cumap);
+}
+
void register_node_type_sh_curve_vec(void)
{
static bNodeType ntype;
- sh_node_type_base(&ntype, SH_NODE_CURVE_VEC, "Vector Curves", NODE_CLASS_OP_VECTOR, 0);
+ sh_fn_node_type_base(&ntype, SH_NODE_CURVE_VEC, "Vector Curves", NODE_CLASS_OP_VECTOR, 0);
node_type_socket_templates(&ntype, sh_node_curve_vec_in, sh_node_curve_vec_out);
node_type_init(&ntype, node_shader_init_curve_vec);
node_type_size_preset(&ntype, NODE_SIZE_LARGE);
node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves);
- node_type_exec(&ntype, node_initexec_curves, NULL, node_shader_exec_curve_vec);
+ node_type_exec(&ntype, node_initexec_curves, nullptr, node_shader_exec_curve_vec);
node_type_gpu(&ntype, gpu_shader_curve_vec);
+ ntype.expand_in_mf_network = sh_node_curve_vec_expand_in_mf_network;
nodeRegisterType(&ntype);
}
@@ -145,7 +193,7 @@ static void node_shader_exec_curve_rgb(void *UNUSED(data),
/* stack order output: vec */
nodestack_get_vec(&fac, SOCK_FLOAT, in[0]);
nodestack_get_vec(vec, SOCK_VECTOR, in[1]);
- BKE_curvemapping_evaluateRGBF(node->storage, out[0]->vec, vec);
+ BKE_curvemapping_evaluateRGBF((CurveMapping *)node->storage, out[0]->vec, vec);
if (fac != 1.0f) {
interp_v3_v3v3(out[0]->vec, vec, out[0]->vec, fac);
}
@@ -166,7 +214,7 @@ static int gpu_shader_curve_rgb(GPUMaterial *mat,
int size;
bool use_opti = true;
- CurveMapping *cumap = node->storage;
+ CurveMapping *cumap = (CurveMapping *)node->storage;
BKE_curvemapping_init(cumap);
BKE_curvemapping_table_RGBA(cumap, &array, &size);
@@ -230,17 +278,65 @@ static int gpu_shader_curve_rgb(GPUMaterial *mat,
GPU_uniform(ext_rgba[3]));
}
+class CurveRGBFunction : public blender::fn::MultiFunction {
+ private:
+ const CurveMapping &cumap_;
+
+ public:
+ CurveRGBFunction(const CurveMapping &cumap) : cumap_(cumap)
+ {
+ static blender::fn::MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ }
+
+ static blender::fn::MFSignature create_signature()
+ {
+ blender::fn::MFSignatureBuilder signature{"Curve RGB"};
+ signature.single_input<float>("Fac");
+ signature.single_input<blender::ColorGeometry4f>("Color");
+ signature.single_output<blender::ColorGeometry4f>("Color");
+ return signature.build();
+ }
+
+ void call(blender::IndexMask mask,
+ blender::fn::MFParams params,
+ blender::fn::MFContext UNUSED(context)) const override
+ {
+ const blender::VArray<float> &fac = params.readonly_single_input<float>(0, "Fac");
+ const blender::VArray<blender::ColorGeometry4f> &col_in =
+ params.readonly_single_input<blender::ColorGeometry4f>(1, "Color");
+ blender::MutableSpan<blender::ColorGeometry4f> col_out =
+ params.uninitialized_single_output<blender::ColorGeometry4f>(2, "Color");
+
+ for (int64_t i : mask) {
+ BKE_curvemapping_evaluateRGBF(&cumap_, col_out[i], col_in[i]);
+ if (fac[i] != 1.0f) {
+ interp_v3_v3v3(col_out[i], col_in[i], col_out[i], fac[i]);
+ }
+ }
+ }
+};
+
+static void sh_node_curve_rgb_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
+{
+ bNode &bnode = builder.bnode();
+ CurveMapping *cumap = (CurveMapping *)bnode.storage;
+ BKE_curvemapping_init(cumap);
+ builder.construct_and_set_matching_fn<CurveRGBFunction>(*cumap);
+}
+
void register_node_type_sh_curve_rgb(void)
{
static bNodeType ntype;
- sh_node_type_base(&ntype, SH_NODE_CURVE_RGB, "RGB Curves", NODE_CLASS_OP_COLOR, 0);
+ sh_fn_node_type_base(&ntype, SH_NODE_CURVE_RGB, "RGB Curves", NODE_CLASS_OP_COLOR, 0);
node_type_socket_templates(&ntype, sh_node_curve_rgb_in, sh_node_curve_rgb_out);
node_type_init(&ntype, node_shader_init_curve_rgb);
node_type_size_preset(&ntype, NODE_SIZE_LARGE);
node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves);
- node_type_exec(&ntype, node_initexec_curves, NULL, node_shader_exec_curve_rgb);
+ node_type_exec(&ntype, node_initexec_curves, nullptr, node_shader_exec_curve_rgb);
node_type_gpu(&ntype, gpu_shader_curve_rgb);
+ ntype.expand_in_mf_network = sh_node_curve_rgb_expand_in_mf_network;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_map_range.cc b/source/blender/nodes/shader/nodes/node_shader_map_range.cc
index 3b4ea3d1bdf..ad7abd9d491 100644
--- a/source/blender/nodes/shader/nodes/node_shader_map_range.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_map_range.cc
@@ -23,6 +23,8 @@
#include "node_shader_util.h"
+#include "BLI_math_base_safe.h"
+
/* **************** Map Range ******************** */
static bNodeSocketTemplate sh_node_map_range_in[] = {
{SOCK_FLOAT, N_("Value"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, PROP_NONE},
@@ -88,6 +90,19 @@ static int gpu_shader_map_range(GPUMaterial *mat,
return ret;
}
+static void map_range_signature(blender::fn::MFSignatureBuilder *signature, bool use_steps)
+{
+ signature->single_input<float>("Value");
+ signature->single_input<float>("From Min");
+ signature->single_input<float>("From Max");
+ signature->single_input<float>("To Min");
+ signature->single_input<float>("To Max");
+ if (use_steps) {
+ signature->single_input<float>("Steps");
+ }
+ signature->single_output<float>("Result");
+}
+
class MapRangeFunction : public blender::fn::MultiFunction {
private:
bool clamp_;
@@ -102,12 +117,7 @@ class MapRangeFunction : public blender::fn::MultiFunction {
static blender::fn::MFSignature create_signature()
{
blender::fn::MFSignatureBuilder signature{"Map Range"};
- signature.single_input<float>("Value");
- signature.single_input<float>("From Min");
- signature.single_input<float>("From Max");
- signature.single_input<float>("To Min");
- signature.single_input<float>("To Max");
- signature.single_output<float>("Result");
+ map_range_signature(&signature, false);
return signature.build();
}
@@ -136,25 +146,163 @@ class MapRangeFunction : public blender::fn::MultiFunction {
}
};
+class MapRangeSteppedFunction : public blender::fn::MultiFunction {
+ private:
+ bool clamp_;
+
+ public:
+ MapRangeSteppedFunction(bool clamp) : clamp_(clamp)
+ {
+ static blender::fn::MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ }
+
+ static blender::fn::MFSignature create_signature()
+ {
+ blender::fn::MFSignatureBuilder signature{"Map Range Stepped"};
+ map_range_signature(&signature, true);
+ return signature.build();
+ }
+
+ void call(blender::IndexMask mask,
+ blender::fn::MFParams params,
+ blender::fn::MFContext UNUSED(context)) const override
+ {
+ const blender::VArray<float> &values = params.readonly_single_input<float>(0, "Value");
+ const blender::VArray<float> &from_min = params.readonly_single_input<float>(1, "From Min");
+ const blender::VArray<float> &from_max = params.readonly_single_input<float>(2, "From Max");
+ const blender::VArray<float> &to_min = params.readonly_single_input<float>(3, "To Min");
+ const blender::VArray<float> &to_max = params.readonly_single_input<float>(4, "To Max");
+ const blender::VArray<float> &steps = params.readonly_single_input<float>(5, "Steps");
+ blender::MutableSpan<float> results = params.uninitialized_single_output<float>(6, "Result");
+
+ for (int64_t i : mask) {
+ float factor = safe_divide(values[i] - from_min[i], from_max[i] - from_min[i]);
+ factor = safe_divide(floorf(factor * (steps[i] + 1.0f)), steps[i]);
+ results[i] = to_min[i] + factor * (to_max[i] - to_min[i]);
+ }
+
+ if (clamp_) {
+ for (int64_t i : mask) {
+ results[i] = (to_min[i] > to_max[i]) ? clamp_f(results[i], to_max[i], to_min[i]) :
+ clamp_f(results[i], to_min[i], to_max[i]);
+ }
+ }
+ }
+};
+
+class MapRangeSmoothstepFunction : public blender::fn::MultiFunction {
+ public:
+ MapRangeSmoothstepFunction()
+ {
+ static blender::fn::MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ }
+
+ static blender::fn::MFSignature create_signature()
+ {
+ blender::fn::MFSignatureBuilder signature{"Map Range Smoothstep"};
+ map_range_signature(&signature, false);
+ return signature.build();
+ }
+
+ void call(blender::IndexMask mask,
+ blender::fn::MFParams params,
+ blender::fn::MFContext UNUSED(context)) const override
+ {
+ const blender::VArray<float> &values = params.readonly_single_input<float>(0, "Value");
+ const blender::VArray<float> &from_min = params.readonly_single_input<float>(1, "From Min");
+ const blender::VArray<float> &from_max = params.readonly_single_input<float>(2, "From Max");
+ const blender::VArray<float> &to_min = params.readonly_single_input<float>(3, "To Min");
+ const blender::VArray<float> &to_max = params.readonly_single_input<float>(4, "To Max");
+ blender::MutableSpan<float> results = params.uninitialized_single_output<float>(5, "Result");
+
+ for (int64_t i : mask) {
+ float factor = safe_divide(values[i] - from_min[i], from_max[i] - from_min[i]);
+ factor = std::clamp(factor, 0.0f, 1.0f);
+ factor = (3.0f - 2.0f * factor) * (factor * factor);
+ results[i] = to_min[i] + factor * (to_max[i] - to_min[i]);
+ }
+ }
+};
+
+class MapRangeSmootherstepFunction : public blender::fn::MultiFunction {
+ public:
+ MapRangeSmootherstepFunction()
+ {
+ static blender::fn::MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ }
+
+ static blender::fn::MFSignature create_signature()
+ {
+ blender::fn::MFSignatureBuilder signature{"Map Range Smoothstep"};
+ map_range_signature(&signature, false);
+ return signature.build();
+ }
+
+ void call(blender::IndexMask mask,
+ blender::fn::MFParams params,
+ blender::fn::MFContext UNUSED(context)) const override
+ {
+ const blender::VArray<float> &values = params.readonly_single_input<float>(0, "Value");
+ const blender::VArray<float> &from_min = params.readonly_single_input<float>(1, "From Min");
+ const blender::VArray<float> &from_max = params.readonly_single_input<float>(2, "From Max");
+ const blender::VArray<float> &to_min = params.readonly_single_input<float>(3, "To Min");
+ const blender::VArray<float> &to_max = params.readonly_single_input<float>(4, "To Max");
+ blender::MutableSpan<float> results = params.uninitialized_single_output<float>(5, "Result");
+
+ for (int64_t i : mask) {
+ float factor = safe_divide(values[i] - from_min[i], from_max[i] - from_min[i]);
+ factor = std::clamp(factor, 0.0f, 1.0f);
+ factor = factor * factor * factor * (factor * (factor * 6.0f - 15.0f) + 10.0f);
+ results[i] = to_min[i] + factor * (to_max[i] - to_min[i]);
+ }
+ }
+};
+
static void sh_node_map_range_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
{
bNode &bnode = builder.bnode();
bool clamp = bnode.custom1 != 0;
int interpolation_type = bnode.custom2;
- if (interpolation_type == NODE_MAP_RANGE_LINEAR) {
- static MapRangeFunction fn_with_clamp{true};
- static MapRangeFunction fn_without_clamp{false};
-
- if (clamp) {
- builder.set_matching_fn(fn_with_clamp);
+ switch (interpolation_type) {
+ case NODE_MAP_RANGE_LINEAR: {
+ if (clamp) {
+ static MapRangeFunction fn_with_clamp{true};
+ builder.set_matching_fn(fn_with_clamp);
+ }
+ else {
+ static MapRangeFunction fn_without_clamp{false};
+ builder.set_matching_fn(fn_without_clamp);
+ }
+ break;
}
- else {
- builder.set_matching_fn(fn_without_clamp);
+ case NODE_MAP_RANGE_STEPPED: {
+ if (clamp) {
+ static MapRangeSteppedFunction fn_stepped_with_clamp{true};
+ builder.set_matching_fn(fn_stepped_with_clamp);
+ }
+ else {
+ static MapRangeSteppedFunction fn_stepped_without_clamp{false};
+ builder.set_matching_fn(fn_stepped_without_clamp);
+ }
+ break;
}
- }
- else {
- builder.set_not_implemented();
+ case NODE_MAP_RANGE_SMOOTHSTEP: {
+ static MapRangeSmoothstepFunction smoothstep;
+ builder.set_matching_fn(smoothstep);
+ break;
+ }
+ case NODE_MAP_RANGE_SMOOTHERSTEP: {
+ static MapRangeSmootherstepFunction smootherstep;
+ builder.set_matching_fn(smootherstep);
+ break;
+ }
+ default:
+ builder.set_not_implemented();
+ break;
}
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_mixRgb.c b/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc
index 090c6216224..52f2adb10dd 100644
--- a/source/blender/nodes/shader/nodes/node_shader_mixRgb.c
+++ b/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc
@@ -61,35 +61,60 @@ static void node_shader_exec_mix_rgb(void *UNUSED(data),
copy_v3_v3(out[0]->vec, col);
}
+static const char *gpu_shader_get_name(int mode)
+{
+ switch (mode) {
+ case MA_RAMP_BLEND:
+ return "mix_blend";
+ case MA_RAMP_ADD:
+ return "mix_add";
+ case MA_RAMP_MULT:
+ return "mix_mult";
+ case MA_RAMP_SUB:
+ return "mix_sub";
+ case MA_RAMP_SCREEN:
+ return "mix_screen";
+ case MA_RAMP_DIV:
+ return "mix_div";
+ case MA_RAMP_DIFF:
+ return "mix_diff";
+ case MA_RAMP_DARK:
+ return "mix_dark";
+ case MA_RAMP_LIGHT:
+ return "mix_light";
+ case MA_RAMP_OVERLAY:
+ return "mix_overlay";
+ case MA_RAMP_DODGE:
+ return "mix_dodge";
+ case MA_RAMP_BURN:
+ return "mix_burn";
+ case MA_RAMP_HUE:
+ return "mix_hue";
+ case MA_RAMP_SAT:
+ return "mix_sat";
+ case MA_RAMP_VAL:
+ return "mix_val";
+ case MA_RAMP_COLOR:
+ return "mix_color";
+ case MA_RAMP_SOFT:
+ return "mix_soft";
+ case MA_RAMP_LINEAR:
+ return "mix_linear";
+ }
+
+ return nullptr;
+}
+
static int gpu_shader_mix_rgb(GPUMaterial *mat,
bNode *node,
bNodeExecData *UNUSED(execdata),
GPUNodeStack *in,
GPUNodeStack *out)
{
- static const char *names[] = {
- "mix_blend",
- "mix_add",
- "mix_mult",
- "mix_sub",
- "mix_screen",
- "mix_div",
- "mix_diff",
- "mix_dark",
- "mix_light",
- "mix_overlay",
- "mix_dodge",
- "mix_burn",
- "mix_hue",
- "mix_sat",
- "mix_val",
- "mix_color",
- "mix_soft",
- "mix_linear",
- };
+ const char *name = gpu_shader_get_name(node->custom1);
- if (node->custom1 < ARRAY_SIZE(names) && names[node->custom1]) {
- int ret = GPU_stack_link(mat, node, names[node->custom1], in, out);
+ if (name != nullptr) {
+ int ret = GPU_stack_link(mat, node, name, in, out);
if (ret && node->custom2 & SHD_MIXRGB_CLAMP) {
const float min[3] = {0.0f, 0.0f, 0.0f};
const float max[3] = {1.0f, 1.0f, 1.0f};
diff --git a/source/blender/nodes/shader/nodes/node_shader_output_aov.c b/source/blender/nodes/shader/nodes/node_shader_output_aov.c
index 403b3e6d9d6..7e7e1b703f1 100644
--- a/source/blender/nodes/shader/nodes/node_shader_output_aov.c
+++ b/source/blender/nodes/shader/nodes/node_shader_output_aov.c
@@ -43,8 +43,9 @@ static int node_shader_gpu_output_aov(GPUMaterial *mat,
{
GPUNodeLink *outlink;
NodeShaderOutputAOV *aov = (NodeShaderOutputAOV *)node->storage;
- /* Keep in sync with `renderpass_lib.glsl#render_pass_aov_hash`. */
- unsigned int hash = BLI_hash_string(aov->name) & ~1;
+ /* Keep in sync with `renderpass_lib.glsl#render_pass_aov_hash` and
+ * `EEVEE_renderpasses_aov_hash`. */
+ unsigned int hash = BLI_hash_string(aov->name) << 1;
GPU_stack_link(mat, node, "node_output_aov", in, out, &outlink);
GPU_material_add_output_link_aov(mat, outlink, hash);
diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc b/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc
index 8ca4a6bab5f..a7239154633 100644
--- a/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc
@@ -70,7 +70,7 @@ class SeparateRGBFunction : public blender::fn::MultiFunction {
static blender::fn::MFSignature create_signature()
{
blender::fn::MFSignatureBuilder signature{"Separate RGB"};
- signature.single_input<blender::Color4f>("Color");
+ signature.single_input<blender::ColorGeometry4f>("Color");
signature.single_output<float>("R");
signature.single_output<float>("G");
signature.single_output<float>("B");
@@ -81,14 +81,14 @@ class SeparateRGBFunction : public blender::fn::MultiFunction {
blender::fn::MFParams params,
blender::fn::MFContext UNUSED(context)) const override
{
- const blender::VArray<blender::Color4f> &colors =
- params.readonly_single_input<blender::Color4f>(0, "Color");
+ const blender::VArray<blender::ColorGeometry4f> &colors =
+ params.readonly_single_input<blender::ColorGeometry4f>(0, "Color");
blender::MutableSpan<float> rs = params.uninitialized_single_output<float>(1, "R");
blender::MutableSpan<float> gs = params.uninitialized_single_output<float>(2, "G");
blender::MutableSpan<float> bs = params.uninitialized_single_output<float>(3, "B");
for (int64_t i : mask) {
- blender::Color4f color = colors[i];
+ blender::ColorGeometry4f color = colors[i];
rs[i] = color.r;
gs[i] = color.g;
bs[i] = color.b;
@@ -155,8 +155,9 @@ static int gpu_shader_combrgb(GPUMaterial *mat,
static void sh_node_combrgb_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
{
- static blender::fn::CustomMF_SI_SI_SI_SO<float, float, float, blender::Color4f> fn{
- "Combine RGB", [](float r, float g, float b) { return blender::Color4f(r, g, b, 1.0f); }};
+ static blender::fn::CustomMF_SI_SI_SI_SO<float, float, float, blender::ColorGeometry4f> fn{
+ "Combine RGB",
+ [](float r, float g, float b) { return blender::ColorGeometry4f(r, g, b, 1.0f); }};
builder.set_matching_fn(fn);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_sky.c b/source/blender/nodes/shader/nodes/node_shader_tex_sky.c
index 9ef05d781bd..5dc11c4df00 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_sky.c
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_sky.c
@@ -60,11 +60,6 @@ typedef struct SkyModelPreetham {
float radiance[3];
} SkyModelPreetham;
-typedef struct XYZ_to_RGB /* transposed imbuf_xyz_to_rgb, passed as 3x vec3 */
-{
- float r[3], g[3], b[3];
-} XYZ_to_RGB;
-
static float sky_perez_function(const float *lam, float theta, float gamma)
{
float ctheta = cosf(theta);
@@ -119,20 +114,6 @@ static void sky_precompute_old(SkyModelPreetham *sunsky, const float sun_angles[
sunsky->radiance[2] /= sky_perez_function(sunsky->config_y, 0, theta);
}
-static void get_XYZ_to_RGB_for_gpu(XYZ_to_RGB *data)
-{
- const float *xyz_to_rgb = IMB_colormangement_get_xyz_to_rgb();
- data->r[0] = xyz_to_rgb[0];
- data->r[1] = xyz_to_rgb[3];
- data->r[2] = xyz_to_rgb[6];
- data->g[0] = xyz_to_rgb[1];
- data->g[1] = xyz_to_rgb[4];
- data->g[2] = xyz_to_rgb[7];
- data->b[0] = xyz_to_rgb[2];
- data->b[1] = xyz_to_rgb[5];
- data->b[2] = xyz_to_rgb[8];
-}
-
static int node_shader_gpu_tex_sky(GPUMaterial *mat,
bNode *node,
bNodeExecData *UNUSED(execdata),
diff --git a/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc b/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc
index 90e8161c09f..5b2eb300aac 100644
--- a/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc
@@ -140,7 +140,7 @@ class ColorBandFunction : public blender::fn::MultiFunction {
{
blender::fn::MFSignatureBuilder signature{"Color Band"};
signature.single_input<float>("Value");
- signature.single_output<blender::Color4f>("Color");
+ signature.single_output<blender::ColorGeometry4f>("Color");
signature.single_output<float>("Alpha");
return signature.build();
}
@@ -150,12 +150,12 @@ class ColorBandFunction : public blender::fn::MultiFunction {
blender::fn::MFContext UNUSED(context)) const override
{
const blender::VArray<float> &values = params.readonly_single_input<float>(0, "Value");
- blender::MutableSpan<blender::Color4f> colors =
- params.uninitialized_single_output<blender::Color4f>(1, "Color");
+ blender::MutableSpan<blender::ColorGeometry4f> colors =
+ params.uninitialized_single_output<blender::ColorGeometry4f>(1, "Color");
blender::MutableSpan<float> alphas = params.uninitialized_single_output<float>(2, "Alpha");
for (int64_t i : mask) {
- blender::Color4f color;
+ blender::ColorGeometry4f color;
BKE_colorband_evaluate(&color_band_, values[i], color);
colors[i] = color;
alphas[i] = color.a;
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 3a9822fbc8e..419a11201aa 100644
--- a/source/blender/nodes/shader/nodes/node_shader_vector_math.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_vector_math.cc
@@ -23,6 +23,8 @@
#include "node_shader_util.h"
+#include "NOD_math_functions.hh"
+
/* **************** VECTOR MATH ******************** */
static bNodeSocketTemplate sh_node_vector_math_in[] = {
{SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 1.0f, -10000.0f, 10000.0f, PROP_NONE},
@@ -92,6 +94,8 @@ static const char *gpu_shader_get_name(int mode)
return "vector_math_refract";
case NODE_VECTOR_MATH_FACEFORWARD:
return "vector_math_faceforward";
+ case NODE_VECTOR_MATH_MULTIPLY_ADD:
+ return "vector_math_multiply_add";
}
return nullptr;
@@ -132,8 +136,11 @@ static void node_shader_update_vector_math(bNodeTree *UNUSED(ntree), bNode *node
NODE_VECTOR_MATH_ABSOLUTE,
NODE_VECTOR_MATH_FRACTION,
NODE_VECTOR_MATH_NORMALIZE));
- nodeSetSocketAvailability(
- sockC, ELEM(node->custom1, NODE_VECTOR_MATH_WRAP, NODE_VECTOR_MATH_FACEFORWARD));
+ nodeSetSocketAvailability(sockC,
+ ELEM(node->custom1,
+ NODE_VECTOR_MATH_WRAP,
+ NODE_VECTOR_MATH_FACEFORWARD,
+ NODE_VECTOR_MATH_MULTIPLY_ADD));
nodeSetSocketAvailability(sockScale,
ELEM(node->custom1, NODE_VECTOR_MATH_SCALE, NODE_VECTOR_MATH_REFRACT));
nodeSetSocketAvailability(sockVector,
@@ -152,6 +159,10 @@ static void node_shader_update_vector_math(bNodeTree *UNUSED(ntree), bNode *node
node_sock_label_clear(sockC);
node_sock_label_clear(sockScale);
switch (node->custom1) {
+ case NODE_VECTOR_MATH_MULTIPLY_ADD:
+ node_sock_label(sockB, "Multiplier");
+ node_sock_label(sockC, "Addend");
+ break;
case NODE_VECTOR_MATH_FACEFORWARD:
node_sock_label(sockB, "Incident");
node_sock_label(sockC, "Reference");
@@ -177,117 +188,79 @@ static const blender::fn::MultiFunction &get_multi_function(
{
using blender::float3;
- const int mode = builder.bnode().custom1;
- switch (mode) {
- case NODE_VECTOR_MATH_ADD: {
- static blender::fn::CustomMF_SI_SI_SO<float3, float3, float3> fn{
- "Add", [](float3 a, float3 b) { return a + b; }};
- return fn;
- }
- case NODE_VECTOR_MATH_SUBTRACT: {
- static blender::fn::CustomMF_SI_SI_SO<float3, float3, float3> fn{
- "Subtract", [](float3 a, float3 b) { return a - b; }};
- return fn;
- }
- case NODE_VECTOR_MATH_MULTIPLY: {
- static blender::fn::CustomMF_SI_SI_SO<float3, float3, float3> fn{
- "Multiply", [](float3 a, float3 b) { return a * b; }};
- return fn;
- }
- case NODE_VECTOR_MATH_DIVIDE: {
- static blender::fn::CustomMF_SI_SI_SO<float3, float3, float3> fn{
- "Divide", [](float3 a, float3 b) { return float3::safe_divide(a, b); }};
- return fn;
- }
- case NODE_VECTOR_MATH_CROSS_PRODUCT: {
- static blender::fn::CustomMF_SI_SI_SO<float3, float3, float3> fn{
- "Cross Product", float3::cross_high_precision};
- return fn;
- }
- case NODE_VECTOR_MATH_PROJECT: {
- static blender::fn::CustomMF_SI_SI_SO<float3, float3, float3> fn{"Project", float3::project};
- return fn;
- }
- case NODE_VECTOR_MATH_REFLECT: {
- static blender::fn::CustomMF_SI_SI_SO<float3, float3, float3> fn{
- "Reflect", [](float3 a, float3 b) { return a.reflected(b); }};
- return fn;
- }
- case NODE_VECTOR_MATH_DOT_PRODUCT: {
- static blender::fn::CustomMF_SI_SI_SO<float3, float3, float> fn{"Dot Product", float3::dot};
- return fn;
- }
- case NODE_VECTOR_MATH_DISTANCE: {
- static blender::fn::CustomMF_SI_SI_SO<float3, float3, float> fn{"Distance",
- float3::distance};
- return fn;
- }
- case NODE_VECTOR_MATH_LENGTH: {
- static blender::fn::CustomMF_SI_SO<float3, float> fn{"Length",
- [](float3 a) { return a.length(); }};
- return fn;
- }
- case NODE_VECTOR_MATH_SCALE: {
- static blender::fn::CustomMF_SI_SI_SO<float3, float, float3> fn{
- "Scale", [](float3 a, float factor) { return a * factor; }};
- return fn;
- }
- case NODE_VECTOR_MATH_NORMALIZE: {
- static blender::fn::CustomMF_SI_SO<float3, float3> fn{
- "Normalize", [](float3 a) { return a.normalized(); }};
- return fn;
- }
- case NODE_VECTOR_MATH_REFRACT: {
- static blender::fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> fn{
- "Refract",
- [](float3 a, float3 b, float c) { return float3::refract(a, b.normalized(), c); }};
- return fn;
- }
- case NODE_VECTOR_MATH_FACEFORWARD: {
- static blender::fn::CustomMF_SI_SI_SI_SO<float3, float3, float3, float3> fn{
- "Faceforward", float3::faceforward};
- return fn;
- }
- case NODE_VECTOR_MATH_SNAP: {
- return builder.get_not_implemented_fn();
- }
- case NODE_VECTOR_MATH_FLOOR: {
- return builder.get_not_implemented_fn();
- }
- case NODE_VECTOR_MATH_CEIL: {
- return builder.get_not_implemented_fn();
- }
- case NODE_VECTOR_MATH_MODULO: {
- return builder.get_not_implemented_fn();
- }
- case NODE_VECTOR_MATH_FRACTION: {
- return builder.get_not_implemented_fn();
- }
- case NODE_VECTOR_MATH_ABSOLUTE: {
- return builder.get_not_implemented_fn();
- }
- case NODE_VECTOR_MATH_MINIMUM: {
- return builder.get_not_implemented_fn();
- }
- case NODE_VECTOR_MATH_MAXIMUM: {
- return builder.get_not_implemented_fn();
- }
- case NODE_VECTOR_MATH_WRAP: {
- return builder.get_not_implemented_fn();
- }
- case NODE_VECTOR_MATH_SINE: {
- return builder.get_not_implemented_fn();
- }
- case NODE_VECTOR_MATH_COSINE: {
- return builder.get_not_implemented_fn();
- }
- case NODE_VECTOR_MATH_TANGENT: {
- return builder.get_not_implemented_fn();
- }
- default:
- BLI_assert_unreachable();
- return builder.get_not_implemented_fn();
- };
+ NodeVectorMathOperation operation = NodeVectorMathOperation(builder.bnode().custom1);
+
+ const blender::fn::MultiFunction *multi_fn = nullptr;
+
+ blender::nodes::try_dispatch_float_math_fl3_fl3_to_fl3(
+ operation, [&](auto function, const blender::nodes::FloatMathOperationInfo &info) {
+ static blender::fn::CustomMF_SI_SI_SO<float3, float3, float3> fn{info.title_case_name,
+ function};
+ multi_fn = &fn;
+ });
+ if (multi_fn != nullptr) {
+ return *multi_fn;
+ }
+
+ blender::nodes::try_dispatch_float_math_fl3_fl3_fl3_to_fl3(
+ operation, [&](auto function, const blender::nodes::FloatMathOperationInfo &info) {
+ static blender::fn::CustomMF_SI_SI_SI_SO<float3, float3, float3, float3> fn{
+ info.title_case_name, function};
+ multi_fn = &fn;
+ });
+ if (multi_fn != nullptr) {
+ return *multi_fn;
+ }
+
+ blender::nodes::try_dispatch_float_math_fl3_fl3_fl_to_fl3(
+ operation, [&](auto function, const blender::nodes::FloatMathOperationInfo &info) {
+ static blender::fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> fn{
+ info.title_case_name, function};
+ multi_fn = &fn;
+ });
+ if (multi_fn != nullptr) {
+ return *multi_fn;
+ }
+
+ blender::nodes::try_dispatch_float_math_fl3_fl3_to_fl(
+ operation, [&](auto function, const blender::nodes::FloatMathOperationInfo &info) {
+ static blender::fn::CustomMF_SI_SI_SO<float3, float3, float> fn{info.title_case_name,
+ function};
+ multi_fn = &fn;
+ });
+ if (multi_fn != nullptr) {
+ return *multi_fn;
+ }
+
+ blender::nodes::try_dispatch_float_math_fl3_fl_to_fl3(
+ operation, [&](auto function, const blender::nodes::FloatMathOperationInfo &info) {
+ static blender::fn::CustomMF_SI_SI_SO<float3, float, float3> fn{info.title_case_name,
+ function};
+ multi_fn = &fn;
+ });
+ if (multi_fn != nullptr) {
+ return *multi_fn;
+ }
+
+ blender::nodes::try_dispatch_float_math_fl3_to_fl3(
+ operation, [&](auto function, const blender::nodes::FloatMathOperationInfo &info) {
+ static blender::fn::CustomMF_SI_SO<float3, float3> fn{info.title_case_name, function};
+ multi_fn = &fn;
+ });
+ if (multi_fn != nullptr) {
+ return *multi_fn;
+ }
+
+ blender::nodes::try_dispatch_float_math_fl3_to_fl(
+ operation, [&](auto function, const blender::nodes::FloatMathOperationInfo &info) {
+ static blender::fn::CustomMF_SI_SO<float3, float> fn{info.title_case_name, function};
+ multi_fn = &fn;
+ });
+ if (multi_fn != nullptr) {
+ return *multi_fn;
+ }
+
+ return builder.get_not_implemented_fn();
}
static void sh_node_vector_math_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
diff --git a/source/blender/nodes/shader/nodes/node_shader_wavelength.c b/source/blender/nodes/shader/nodes/node_shader_wavelength.c
index 6b7e1399328..30f69557020 100644
--- a/source/blender/nodes/shader/nodes/node_shader_wavelength.c
+++ b/source/blender/nodes/shader/nodes/node_shader_wavelength.c
@@ -30,6 +30,33 @@ static bNodeSocketTemplate sh_node_wavelength_out[] = {
{-1, ""},
};
+static int node_shader_gpu_wavelength(GPUMaterial *mat,
+ bNode *node,
+ bNodeExecData *UNUSED(execdata),
+ GPUNodeStack *in,
+ GPUNodeStack *out)
+{
+ const int size = CM_TABLE + 1;
+ float *data = MEM_mallocN(sizeof(float) * size * 4, "cie_xyz texture");
+
+ wavelength_to_xyz_table(data, size);
+
+ float layer;
+ GPUNodeLink *ramp_texture = GPU_color_band(mat, size, data, &layer);
+ XYZ_to_RGB xyz_to_rgb;
+ get_XYZ_to_RGB_for_gpu(&xyz_to_rgb);
+ return GPU_stack_link(mat,
+ node,
+ "node_wavelength",
+ in,
+ out,
+ ramp_texture,
+ GPU_constant(&layer),
+ GPU_uniform(xyz_to_rgb.r),
+ GPU_uniform(xyz_to_rgb.g),
+ GPU_uniform(xyz_to_rgb.b));
+}
+
/* node type definition */
void register_node_type_sh_wavelength(void)
{
@@ -40,6 +67,7 @@ void register_node_type_sh_wavelength(void)
node_type_socket_templates(&ntype, sh_node_wavelength_in, sh_node_wavelength_out);
node_type_init(&ntype, NULL);
node_type_storage(&ntype, "", NULL, NULL);
+ node_type_gpu(&ntype, node_shader_gpu_wavelength);
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/texture/node_texture_tree.c b/source/blender/nodes/texture/node_texture_tree.c
index 48027dc847b..2ae722e3cd8 100644
--- a/source/blender/nodes/texture/node_texture_tree.c
+++ b/source/blender/nodes/texture/node_texture_tree.c
@@ -152,6 +152,12 @@ static void update(bNodeTree *ntree)
}
}
+static bool texture_node_tree_socket_type_valid(eNodeSocketDatatype socket_type,
+ bNodeTreeType *UNUSED(ntreetype))
+{
+ return ELEM(socket_type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA);
+}
+
bNodeTreeType *ntreeType_Texture;
void register_node_tree_type_tex(void)
@@ -171,6 +177,7 @@ void register_node_tree_type_tex(void)
tt->local_sync = local_sync;
tt->local_merge = local_merge;
tt->get_from_context = texture_get_from_context;
+ tt->valid_socket_type = texture_node_tree_socket_type_valid;
tt->rna_ext.srna = &RNA_TextureNodeTree;
diff --git a/source/blender/nodes/texture/node_texture_util.c b/source/blender/nodes/texture/node_texture_util.c
index 2091a8bf10e..570b10d6e89 100644
--- a/source/blender/nodes/texture/node_texture_util.c
+++ b/source/blender/nodes/texture/node_texture_util.c
@@ -39,9 +39,15 @@
#include "node_texture_util.h"
-bool tex_node_poll_default(bNodeType *UNUSED(ntype), bNodeTree *ntree)
+bool tex_node_poll_default(bNodeType *UNUSED(ntype),
+ bNodeTree *ntree,
+ const char **r_disabled_hint)
{
- return STREQ(ntree->idname, "TextureNodeTree");
+ if (!STREQ(ntree->idname, "TextureNodeTree")) {
+ *r_disabled_hint = "Not a texture node tree";
+ return false;
+ }
+ return true;
}
void tex_node_type_base(
diff --git a/source/blender/nodes/texture/node_texture_util.h b/source/blender/nodes/texture/node_texture_util.h
index 74f27ef3974..8f63a1ad07d 100644
--- a/source/blender/nodes/texture/node_texture_util.h
+++ b/source/blender/nodes/texture/node_texture_util.h
@@ -106,7 +106,9 @@ typedef struct TexDelegate {
int type;
} TexDelegate;
-bool tex_node_poll_default(struct bNodeType *ntype, struct bNodeTree *ntree);
+bool tex_node_poll_default(struct bNodeType *ntype,
+ struct bNodeTree *ntree,
+ const char **r_disabled_hint);
void tex_node_type_base(
struct bNodeType *ntype, int type, const char *name, short nclass, short flag);