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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/nodes/geometry')
-rw-r--r--source/blender/nodes/geometry/node_geometry_tree.cc2
-rw-r--r--source/blender/nodes/geometry/node_geometry_util.hh4
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_align_rotation_to_vector.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc)0
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_clamp.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc)2
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_color_ramp.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc)0
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_combine_xyz.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_attribute_combine_xyz.cc)0
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_compare.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc)0
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_convert.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc)0
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_curve_map.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc)0
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_fill.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc)0
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_map_range.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_attribute_map_range.cc)0
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_math.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc)0
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_mix.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc)0
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_proximity.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc)2
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_randomize.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc)26
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_sample_texture.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc)0
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_separate_xyz.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_attribute_separate_xyz.cc)0
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_transfer.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc)2
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_vector_math.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc)0
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_vector_rotate.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc)2
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_curve_endpoints.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_curve_endpoints.cc)3
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_curve_reverse.cc71
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_curve_select_by_handle_type.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_curve_select_by_handle_type.cc)0
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_curve_set_handles.cc144
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_curve_spline_type.cc302
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_curve_subdivide.cc392
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_curve_to_points.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc)3
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_delete_geometry.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc)0
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_edge_split.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_edge_split.cc)2
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_mesh_to_curve.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc)0
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_point_distribute.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc)1
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_point_instance.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_point_instance.cc)0
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_point_rotate.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_point_rotate.cc)0
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_point_scale.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_point_scale.cc)0
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_point_separate.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_point_separate.cc)0
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_point_translate.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_point_translate.cc)0
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_points_to_volume.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc)0
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_raycast.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_raycast.cc)0
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_subdivision_surface.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc)2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc22
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_boolean.cc6
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_collection_info.cc73
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc64
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc19
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc47
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_length.cc1
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_parameter.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc16
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc67
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc43
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc10
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc98
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc72
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc70
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc55
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc43
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc594
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_index.cc22
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_normal.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_position.cc5
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc109
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc207
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_material_assign.cc29
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_material_replace.cc8
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_material_selection.cc11
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc897
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc64
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc47
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc189
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc118
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_proximity.cc235
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_separate_components.cc10
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_position.cc7
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc306
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_switch.cc198
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_triangulate.cc16
80 files changed, 3910 insertions, 842 deletions
diff --git a/source/blender/nodes/geometry/node_geometry_tree.cc b/source/blender/nodes/geometry/node_geometry_tree.cc
index d6b23c38ee4..20b610a4db9 100644
--- a/source/blender/nodes/geometry/node_geometry_tree.cc
+++ b/source/blender/nodes/geometry/node_geometry_tree.cc
@@ -119,7 +119,7 @@ void register_node_tree_type_geo(void)
tt->type = NTREE_GEOMETRY;
strcpy(tt->idname, "GeometryNodeTree");
strcpy(tt->ui_name, N_("Geometry Node Editor"));
- tt->ui_icon = 0; /* defined in drawnode.c */
+ tt->ui_icon = 0; /* Defined in `drawnode.c`. */
strcpy(tt->ui_description, N_("Geometry nodes"));
tt->rna_ext.srna = &RNA_GeometryNodeTree;
tt->update = geometry_node_tree_update;
diff --git a/source/blender/nodes/geometry/node_geometry_util.hh b/source/blender/nodes/geometry/node_geometry_util.hh
index 015ac0de002..5896b5bd6cc 100644
--- a/source/blender/nodes/geometry/node_geometry_util.hh
+++ b/source/blender/nodes/geometry/node_geometry_util.hh
@@ -65,7 +65,9 @@ Mesh *create_grid_mesh(const int verts_x,
Mesh *create_cylinder_or_cone_mesh(const float radius_top,
const float radius_bottom,
const float depth,
- const int verts_num,
+ const int circle_segments,
+ const int side_segments,
+ const int fill_segments,
const GeometryNodeMeshCircleFillType fill_type);
Mesh *create_cuboid_mesh(float3 size, int verts_x, int verts_y, int verts_z);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_align_rotation_to_vector.cc
index d0bb906e8af..d0bb906e8af 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_align_rotation_to_vector.cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_clamp.cc
index 97070184609..2e931a2da98 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_clamp.cc
@@ -269,7 +269,7 @@ void register_node_type_geo_attribute_clamp()
static bNodeType ntype;
geo_node_type_base(
- &ntype, GEO_NODE_LECAGY_ATTRIBUTE_CLAMP, "Attribute Clamp", NODE_CLASS_ATTRIBUTE, 0);
+ &ntype, GEO_NODE_LEGACY_ATTRIBUTE_CLAMP, "Attribute Clamp", NODE_CLASS_ATTRIBUTE, 0);
node_type_init(&ntype, blender::nodes::geo_node_attribute_clamp_init);
node_type_update(&ntype, blender::nodes::geo_node_attribute_clamp_update);
ntype.declare = blender::nodes::geo_node_attribute_clamp_declare;
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_color_ramp.cc
index aa054af3acd..aa054af3acd 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_color_ramp.cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_combine_xyz.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_combine_xyz.cc
index 569d5a824ca..569d5a824ca 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_combine_xyz.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_combine_xyz.cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_compare.cc
index 0b9708dae14..0b9708dae14 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_compare.cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_convert.cc
index a2382aa9d25..a2382aa9d25 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_convert.cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_curve_map.cc
index b9621b4ae92..b9621b4ae92 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_curve_map.cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_fill.cc
index 3c50ae5c837..3c50ae5c837 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_fill.cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_map_range.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_map_range.cc
index 0ea3bbe1e45..0ea3bbe1e45 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_map_range.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_map_range.cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_math.cc
index efa09215b45..efa09215b45 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_math.cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_mix.cc
index 74e05cb997d..74e05cb997d 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_mix.cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_proximity.cc
index 0cf411343cf..6120118f611 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_proximity.cc
@@ -232,7 +232,7 @@ static void geo_node_attribute_proximity_exec(GeoNodeExecParams params)
} // namespace blender::nodes
-void register_node_type_geo_attribute_proximity()
+void register_node_type_geo_legacy_attribute_proximity()
{
static bNodeType ntype;
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_randomize.cc
index 60b9910399c..2e6ba456725 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_randomize.cc
@@ -25,7 +25,7 @@
namespace blender::nodes {
-static void geo_node_attribute_randomize_declare(NodeDeclarationBuilder &b)
+static void geo_node_legacy_attribute_randomize_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Geometry>("Geometry");
b.add_input<decl::String>("Attribute");
@@ -39,15 +39,15 @@ static void geo_node_attribute_randomize_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Geometry>("Geometry");
}
-static void geo_node_attribute_random_layout(uiLayout *layout,
- bContext *UNUSED(C),
- PointerRNA *ptr)
+static void geo_node_legacy_attribute_random_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_randomize_init(bNodeTree *UNUSED(tree), bNode *node)
+static void geo_node_legacy_attribute_randomize_init(bNodeTree *UNUSED(tree), bNode *node)
{
NodeAttributeRandomize *data = (NodeAttributeRandomize *)MEM_callocN(
sizeof(NodeAttributeRandomize), __func__);
@@ -57,7 +57,7 @@ static void geo_node_attribute_randomize_init(bNodeTree *UNUSED(tree), bNode *no
node->storage = data;
}
-static void geo_node_attribute_randomize_update(bNodeTree *UNUSED(ntree), bNode *node)
+static void geo_node_legacy_attribute_randomize_update(bNodeTree *UNUSED(ntree), bNode *node)
{
bNodeSocket *sock_min_vector = (bNodeSocket *)BLI_findlink(&node->inputs, 2);
bNodeSocket *sock_max_vector = sock_min_vector->next;
@@ -280,7 +280,7 @@ static void randomize_attribute_on_component(GeometryComponent &component,
attribute.save();
}
-static void geo_node_random_attribute_exec(GeoNodeExecParams params)
+static void geo_node_legacy_random_attribute_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
const std::string attribute_name = params.get_input<std::string>("Attribute");
@@ -326,18 +326,18 @@ static void geo_node_random_attribute_exec(GeoNodeExecParams params)
} // namespace blender::nodes
-void register_node_type_geo_attribute_randomize()
+void register_node_type_geo_legacy_attribute_randomize()
{
static bNodeType ntype;
geo_node_type_base(
&ntype, GEO_NODE_LEGACY_ATTRIBUTE_RANDOMIZE, "Attribute Randomize", NODE_CLASS_ATTRIBUTE, 0);
- node_type_init(&ntype, blender::nodes::geo_node_attribute_randomize_init);
- node_type_update(&ntype, blender::nodes::geo_node_attribute_randomize_update);
+ node_type_init(&ntype, blender::nodes::geo_node_legacy_attribute_randomize_init);
+ node_type_update(&ntype, blender::nodes::geo_node_legacy_attribute_randomize_update);
- ntype.declare = blender::nodes::geo_node_attribute_randomize_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_random_attribute_exec;
- ntype.draw_buttons = blender::nodes::geo_node_attribute_random_layout;
+ ntype.declare = blender::nodes::geo_node_legacy_attribute_randomize_declare;
+ ntype.geometry_node_execute = blender::nodes::geo_node_legacy_random_attribute_exec;
+ ntype.draw_buttons = blender::nodes::geo_node_legacy_attribute_random_layout;
node_type_storage(
&ntype, "NodeAttributeRandomize", node_free_standard_storage, node_copy_standard_storage);
nodeRegisterType(&ntype);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_sample_texture.cc
index 52f97475941..52f97475941 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_sample_texture.cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_separate_xyz.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_separate_xyz.cc
index de0090406c6..de0090406c6 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_separate_xyz.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_separate_xyz.cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_transfer.cc
index 874350cd714..f187ee39b94 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_transfer.cc
@@ -404,7 +404,7 @@ static void transfer_attribute_nearest(const GeometrySet &src_geometry,
data_type);
for (const int i : IndexRange(tot_samples)) {
if (pointcloud_distances_sq[i] < mesh_distances_sq[i]) {
- /* Pointcloud point is closer. */
+ /* Point-cloud point is closer. */
const int index = pointcloud_indices[i];
pointcloud_src_attribute.varray->get(index, buffer);
dst_attribute->set_by_relocate(i, buffer);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_vector_math.cc
index 59903050f88..59903050f88 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_vector_math.cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_vector_rotate.cc
index adaa4de3029..0c515fa63fb 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_vector_rotate.cc
@@ -332,7 +332,7 @@ void register_node_type_geo_attribute_vector_rotate()
static bNodeType ntype;
geo_node_type_base(&ntype,
- GEO_NODE_ATTRIBUTE_VECTOR_ROTATE,
+ GEO_NODE_LEGACY_ATTRIBUTE_VECTOR_ROTATE,
"Attribute Vector Rotate",
NODE_CLASS_ATTRIBUTE,
0);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_endpoints.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_endpoints.cc
index 7853c5aa04a..65d22eca39c 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_endpoints.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_endpoints.cc
@@ -212,7 +212,8 @@ void register_node_type_geo_curve_endpoints()
{
static bNodeType ntype;
- geo_node_type_base(&ntype, GEO_NODE_CURVE_ENDPOINTS, "Curve Endpoints", NODE_CLASS_GEOMETRY, 0);
+ geo_node_type_base(
+ &ntype, GEO_NODE_LEGACY_CURVE_ENDPOINTS, "Curve Endpoints", NODE_CLASS_GEOMETRY, 0);
ntype.declare = blender::nodes::geo_node_curve_endpoints_declare;
ntype.geometry_node_execute = blender::nodes::geo_node_curve_endpoints_exec;
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_reverse.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_reverse.cc
new file mode 100644
index 00000000000..d1c81333c30
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_reverse.cc
@@ -0,0 +1,71 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BLI_task.hh"
+
+#include "BKE_spline.hh"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes {
+
+static void geo_node_curve_reverse_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>("Curve");
+ b.add_input<decl::String>("Selection");
+ b.add_output<decl::Geometry>("Curve");
+}
+
+static void geo_node_curve_reverse_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
+ geometry_set = bke::geometry_set_realize_instances(geometry_set);
+ if (!geometry_set.has_curve()) {
+ params.set_output("Curve", geometry_set);
+ return;
+ }
+
+ /* Retrieve data for write access so we can avoid new allocations for the reversed data. */
+ CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>();
+ CurveEval &curve = *curve_component.get_for_write();
+ MutableSpan<SplinePtr> splines = curve.splines();
+
+ const std::string selection_name = params.extract_input<std::string>("Selection");
+ GVArray_Typed<bool> selection = curve_component.attribute_get_for_read(
+ selection_name, ATTR_DOMAIN_CURVE, true);
+
+ threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) {
+ for (const int i : range) {
+ if (selection[i]) {
+ splines[i]->reverse();
+ }
+ }
+ });
+
+ params.set_output("Curve", geometry_set);
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_legacy_curve_reverse()
+{
+ static bNodeType ntype;
+ geo_node_type_base(
+ &ntype, GEO_NODE_LEGACY_CURVE_REVERSE, "Curve Reverse", NODE_CLASS_GEOMETRY, 0);
+ ntype.declare = blender::nodes::geo_node_curve_reverse_declare;
+ ntype.geometry_node_execute = blender::nodes::geo_node_curve_reverse_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_select_by_handle_type.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_select_by_handle_type.cc
index dfcae2e65b0..dfcae2e65b0 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_select_by_handle_type.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_select_by_handle_type.cc
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_set_handles.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_set_handles.cc
new file mode 100644
index 00000000000..339029336d9
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_set_handles.cc
@@ -0,0 +1,144 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BKE_spline.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes {
+
+static void geo_node_curve_set_handles_decalre(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>("Curve");
+ b.add_input<decl::String>("Selection");
+ b.add_output<decl::Geometry>("Curve");
+}
+
+static void geo_node_curve_set_handles_layout(uiLayout *layout,
+ bContext *UNUSED(C),
+ PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "handle_type", 0, "", ICON_NONE);
+}
+
+static void geo_node_curve_set_handles_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometryCurveSetHandles *data = (NodeGeometryCurveSetHandles *)MEM_callocN(
+ sizeof(NodeGeometryCurveSetHandles), __func__);
+
+ data->handle_type = GEO_NODE_CURVE_HANDLE_AUTO;
+ data->mode = GEO_NODE_CURVE_HANDLE_LEFT | GEO_NODE_CURVE_HANDLE_RIGHT;
+ node->storage = data;
+}
+
+static BezierSpline::HandleType handle_type_from_input_type(GeometryNodeCurveHandleType type)
+{
+ switch (type) {
+ case GEO_NODE_CURVE_HANDLE_AUTO:
+ return BezierSpline::HandleType::Auto;
+ case GEO_NODE_CURVE_HANDLE_ALIGN:
+ return BezierSpline::HandleType::Align;
+ case GEO_NODE_CURVE_HANDLE_FREE:
+ return BezierSpline::HandleType::Free;
+ case GEO_NODE_CURVE_HANDLE_VECTOR:
+ return BezierSpline::HandleType::Vector;
+ }
+ BLI_assert_unreachable();
+ return BezierSpline::HandleType::Auto;
+}
+
+static void geo_node_curve_set_handles_exec(GeoNodeExecParams params)
+{
+ const NodeGeometryCurveSetHandles *node_storage =
+ (NodeGeometryCurveSetHandles *)params.node().storage;
+ const GeometryNodeCurveHandleType type = (GeometryNodeCurveHandleType)node_storage->handle_type;
+ const GeometryNodeCurveHandleMode mode = (GeometryNodeCurveHandleMode)node_storage->mode;
+
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
+ geometry_set = bke::geometry_set_realize_instances(geometry_set);
+ if (!geometry_set.has_curve()) {
+ params.set_output("Curve", geometry_set);
+ return;
+ }
+
+ /* Retrieve data for write access so we can avoid new allocations for the handles data. */
+ CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>();
+ CurveEval &curve = *curve_component.get_for_write();
+ MutableSpan<SplinePtr> splines = curve.splines();
+
+ const std::string selection_name = params.extract_input<std::string>("Selection");
+ GVArray_Typed<bool> selection = curve_component.attribute_get_for_read(
+ selection_name, ATTR_DOMAIN_POINT, true);
+
+ const BezierSpline::HandleType new_handle_type = handle_type_from_input_type(type);
+ int point_index = 0;
+ bool has_bezier_spline = false;
+ for (SplinePtr &spline : splines) {
+ if (spline->type() != Spline::Type::Bezier) {
+ point_index += spline->positions().size();
+ continue;
+ }
+
+ BezierSpline &bezier_spline = static_cast<BezierSpline &>(*spline);
+ if (ELEM(new_handle_type, BezierSpline::HandleType::Free, BezierSpline::HandleType::Align)) {
+ /* In this case the automatically calculated handle types need to be "baked", because
+ * they're possibly changing from a type that is calculated automatically to a type that
+ * is positioned manually. */
+ bezier_spline.ensure_auto_handles();
+ }
+ has_bezier_spline = true;
+ for (int i_point : IndexRange(bezier_spline.size())) {
+ if (selection[point_index]) {
+ if (mode & GEO_NODE_CURVE_HANDLE_LEFT) {
+ bezier_spline.handle_types_left()[i_point] = new_handle_type;
+ }
+ if (mode & GEO_NODE_CURVE_HANDLE_RIGHT) {
+ bezier_spline.handle_types_right()[i_point] = new_handle_type;
+ }
+ }
+ point_index++;
+ }
+ bezier_spline.mark_cache_invalid();
+ }
+
+ if (!has_bezier_spline) {
+ params.error_message_add(NodeWarningType::Info, TIP_("No Bezier splines in input curve"));
+ }
+
+ params.set_output("Curve", geometry_set);
+}
+} // namespace blender::nodes
+
+void register_node_type_geo_legacy_curve_set_handles()
+{
+ static bNodeType ntype;
+ geo_node_type_base(
+ &ntype, GEO_NODE_LEGACY_CURVE_SET_HANDLES, "Set Handle Type", NODE_CLASS_GEOMETRY, 0);
+ ntype.declare = blender::nodes::geo_node_curve_set_handles_decalre;
+ ntype.geometry_node_execute = blender::nodes::geo_node_curve_set_handles_exec;
+ node_type_init(&ntype, blender::nodes::geo_node_curve_set_handles_init);
+ node_type_storage(&ntype,
+ "NodeGeometryCurveSetHandles",
+ node_free_standard_storage,
+ node_copy_standard_storage);
+ ntype.draw_buttons = blender::nodes::geo_node_curve_set_handles_layout;
+
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_spline_type.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_spline_type.cc
new file mode 100644
index 00000000000..44522e990d9
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_spline_type.cc
@@ -0,0 +1,302 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BKE_spline.hh"
+
+#include "BLI_task.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes {
+
+static void geo_node_legacy_curve_spline_type_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>("Curve");
+ b.add_input<decl::String>("Selection");
+ b.add_output<decl::Geometry>("Curve");
+}
+
+static void geo_node_legacy_curve_spline_type_layout(uiLayout *layout,
+ bContext *UNUSED(C),
+ PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "spline_type", 0, "", ICON_NONE);
+}
+
+static void geo_node_legacy_curve_spline_type_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometryCurveSplineType *data = (NodeGeometryCurveSplineType *)MEM_callocN(
+ sizeof(NodeGeometryCurveSplineType), __func__);
+
+ data->spline_type = GEO_NODE_SPLINE_TYPE_POLY;
+ node->storage = data;
+}
+
+template<class T>
+static void scale_input_assign(const Span<T> input,
+ const int scale,
+ const int offset,
+ const MutableSpan<T> r_output)
+{
+ for (const int i : IndexRange(r_output.size())) {
+ r_output[i] = input[i * scale + offset];
+ }
+}
+
+template<class T>
+static void scale_output_assign(const Span<T> input,
+ const int scale,
+ const int offset,
+ const MutableSpan<T> &r_output)
+{
+ for (const int i : IndexRange(input.size())) {
+ r_output[i * scale + offset] = input[i];
+ }
+}
+
+template<typename CopyFn>
+static void copy_attributes(const Spline &input_spline, Spline &output_spline, CopyFn copy_fn)
+{
+ input_spline.attributes.foreach_attribute(
+ [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
+ std::optional<GSpan> src = input_spline.attributes.get_for_read(attribute_id);
+ BLI_assert(src);
+ if (!output_spline.attributes.create(attribute_id, meta_data.data_type)) {
+ BLI_assert_unreachable();
+ return false;
+ }
+ std::optional<GMutableSpan> dst = output_spline.attributes.get_for_write(attribute_id);
+ if (!dst) {
+ BLI_assert_unreachable();
+ return false;
+ }
+
+ copy_fn(*src, *dst);
+
+ return true;
+ },
+ ATTR_DOMAIN_POINT);
+}
+
+static SplinePtr convert_to_poly_spline(const Spline &input)
+{
+ std::unique_ptr<PolySpline> output = std::make_unique<PolySpline>();
+ output->resize(input.positions().size());
+ output->positions().copy_from(input.positions());
+ output->radii().copy_from(input.radii());
+ output->tilts().copy_from(input.tilts());
+ Spline::copy_base_settings(input, *output);
+ output->attributes = input.attributes;
+ return output;
+}
+
+static SplinePtr poly_to_nurbs(const Spline &input)
+{
+ std::unique_ptr<NURBSpline> output = std::make_unique<NURBSpline>();
+ output->resize(input.positions().size());
+ output->positions().copy_from(input.positions());
+ output->radii().copy_from(input.radii());
+ output->tilts().copy_from(input.tilts());
+ output->weights().fill(1.0f);
+ output->set_resolution(12);
+ output->set_order(4);
+ Spline::copy_base_settings(input, *output);
+ output->knots_mode = NURBSpline::KnotsMode::Bezier;
+ output->attributes = input.attributes;
+ return output;
+}
+
+static SplinePtr bezier_to_nurbs(const Spline &input)
+{
+ const BezierSpline &bezier_spline = static_cast<const BezierSpline &>(input);
+ std::unique_ptr<NURBSpline> output = std::make_unique<NURBSpline>();
+ output->resize(input.size() * 3);
+
+ scale_output_assign(bezier_spline.handle_positions_left(), 3, 0, output->positions());
+ scale_output_assign(input.radii(), 3, 0, output->radii());
+ scale_output_assign(input.tilts(), 3, 0, output->tilts());
+
+ scale_output_assign(bezier_spline.positions(), 3, 1, output->positions());
+ scale_output_assign(input.radii(), 3, 1, output->radii());
+ scale_output_assign(input.tilts(), 3, 1, output->tilts());
+
+ scale_output_assign(bezier_spline.handle_positions_right(), 3, 2, output->positions());
+ scale_output_assign(input.radii(), 3, 2, output->radii());
+ scale_output_assign(input.tilts(), 3, 2, output->tilts());
+
+ Spline::copy_base_settings(input, *output);
+ output->weights().fill(1.0f);
+ output->set_resolution(12);
+ output->set_order(4);
+ output->set_cyclic(input.is_cyclic());
+ output->knots_mode = NURBSpline::KnotsMode::Bezier;
+ output->attributes.reallocate(output->size());
+ copy_attributes(input, *output, [](GSpan src, GMutableSpan dst) {
+ attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ scale_output_assign<T>(src.typed<T>(), 3, 0, dst.typed<T>());
+ scale_output_assign<T>(src.typed<T>(), 3, 1, dst.typed<T>());
+ scale_output_assign<T>(src.typed<T>(), 3, 2, dst.typed<T>());
+ });
+ });
+ return output;
+}
+
+static SplinePtr poly_to_bezier(const Spline &input)
+{
+ std::unique_ptr<BezierSpline> output = std::make_unique<BezierSpline>();
+ output->resize(input.size());
+ output->positions().copy_from(input.positions());
+ output->radii().copy_from(input.radii());
+ output->tilts().copy_from(input.tilts());
+ output->handle_types_left().fill(BezierSpline::HandleType::Vector);
+ output->handle_types_right().fill(BezierSpline::HandleType::Vector);
+ output->set_resolution(12);
+ Spline::copy_base_settings(input, *output);
+ output->attributes = input.attributes;
+ return output;
+}
+
+static SplinePtr nurbs_to_bezier(const Spline &input)
+{
+ const NURBSpline &nurbs_spline = static_cast<const NURBSpline &>(input);
+ std::unique_ptr<BezierSpline> output = std::make_unique<BezierSpline>();
+ output->resize(input.size() / 3);
+ scale_input_assign<float3>(input.positions(), 3, 1, output->positions());
+ scale_input_assign<float3>(input.positions(), 3, 0, output->handle_positions_left());
+ scale_input_assign<float3>(input.positions(), 3, 2, output->handle_positions_right());
+ scale_input_assign<float>(input.radii(), 3, 2, output->radii());
+ scale_input_assign<float>(input.tilts(), 3, 2, output->tilts());
+ output->handle_types_left().fill(BezierSpline::HandleType::Align);
+ output->handle_types_right().fill(BezierSpline::HandleType::Align);
+ output->set_resolution(nurbs_spline.resolution());
+ Spline::copy_base_settings(input, *output);
+ output->attributes.reallocate(output->size());
+ copy_attributes(input, *output, [](GSpan src, GMutableSpan dst) {
+ attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ scale_input_assign<T>(src.typed<T>(), 3, 1, dst.typed<T>());
+ });
+ });
+ return output;
+}
+
+static SplinePtr convert_to_bezier(const Spline &input, GeoNodeExecParams params)
+{
+ switch (input.type()) {
+ case Spline::Type::Bezier:
+ return input.copy();
+ case Spline::Type::Poly:
+ return poly_to_bezier(input);
+ case Spline::Type::NURBS:
+ if (input.size() < 6) {
+ params.error_message_add(
+ NodeWarningType::Info,
+ TIP_("NURBS must have minimum of 6 points for Bezier Conversion"));
+ return input.copy();
+ }
+ else {
+ if (input.size() % 3 != 0) {
+ params.error_message_add(NodeWarningType::Info,
+ TIP_("NURBS must have multiples of 3 points for full Bezier "
+ "conversion, curve truncated"));
+ }
+ return nurbs_to_bezier(input);
+ }
+ }
+ BLI_assert_unreachable();
+ return {};
+}
+
+static SplinePtr convert_to_nurbs(const Spline &input)
+{
+ switch (input.type()) {
+ case Spline::Type::NURBS:
+ return input.copy();
+ case Spline::Type::Bezier:
+ return bezier_to_nurbs(input);
+ case Spline::Type::Poly:
+ return poly_to_nurbs(input);
+ }
+ BLI_assert_unreachable();
+ return {};
+}
+
+static void geo_node_legacy_curve_spline_type_exec(GeoNodeExecParams params)
+{
+ const NodeGeometryCurveSplineType *storage =
+ (const NodeGeometryCurveSplineType *)params.node().storage;
+ const GeometryNodeSplineType output_type = (const GeometryNodeSplineType)storage->spline_type;
+
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
+ geometry_set = bke::geometry_set_realize_instances(geometry_set);
+ if (!geometry_set.has_curve()) {
+ params.set_output("Curve", geometry_set);
+ return;
+ }
+
+ const CurveComponent *curve_component = geometry_set.get_component_for_read<CurveComponent>();
+ const CurveEval &curve = *curve_component->get_for_read();
+
+ const std::string selection_name = params.extract_input<std::string>("Selection");
+ GVArray_Typed<bool> selection = curve_component->attribute_get_for_read(
+ selection_name, ATTR_DOMAIN_CURVE, true);
+
+ std::unique_ptr<CurveEval> new_curve = std::make_unique<CurveEval>();
+ for (const int i : curve.splines().index_range()) {
+ if (selection[i]) {
+ switch (output_type) {
+ case GEO_NODE_SPLINE_TYPE_POLY:
+ new_curve->add_spline(convert_to_poly_spline(*curve.splines()[i]));
+ break;
+ case GEO_NODE_SPLINE_TYPE_BEZIER:
+ new_curve->add_spline(convert_to_bezier(*curve.splines()[i], params));
+ break;
+ case GEO_NODE_SPLINE_TYPE_NURBS:
+ new_curve->add_spline(convert_to_nurbs(*curve.splines()[i]));
+ break;
+ }
+ }
+ else {
+ new_curve->add_spline(curve.splines()[i]->copy());
+ }
+ }
+
+ new_curve->attributes = curve.attributes;
+ params.set_output("Curve", GeometrySet::create_with_curve(new_curve.release()));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_legacy_curve_spline_type()
+{
+ static bNodeType ntype;
+ geo_node_type_base(
+ &ntype, GEO_NODE_LEGACY_CURVE_SPLINE_TYPE, "Set Spline Type", NODE_CLASS_GEOMETRY, 0);
+ ntype.declare = blender::nodes::geo_node_legacy_curve_spline_type_declare;
+ ntype.geometry_node_execute = blender::nodes::geo_node_legacy_curve_spline_type_exec;
+ node_type_init(&ntype, blender::nodes::geo_node_legacy_curve_spline_type_init);
+ node_type_storage(&ntype,
+ "NodeGeometryCurveSplineType",
+ node_free_standard_storage,
+ node_copy_standard_storage);
+ ntype.draw_buttons = blender::nodes::geo_node_legacy_curve_spline_type_layout;
+
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_subdivide.cc
new file mode 100644
index 00000000000..f32a68bc042
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_subdivide.cc
@@ -0,0 +1,392 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BLI_task.hh"
+#include "BLI_timeit.hh"
+
+#include "BKE_attribute_math.hh"
+#include "BKE_spline.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+using blender::fn::GVArray_For_GSpan;
+using blender::fn::GVArray_For_Span;
+using blender::fn::GVArray_Typed;
+
+namespace blender::nodes {
+
+static void geo_node_curve_subdivide_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>("Geometry");
+ b.add_input<decl::String>("Cuts");
+ b.add_input<decl::Int>("Cuts", "Cuts_001").default_value(1).min(0).max(1000);
+ b.add_output<decl::Geometry>("Geometry");
+}
+
+static void geo_node_curve_subdivide_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
+ uiItemR(layout, ptr, "cuts_type", 0, IFACE_("Cuts"), ICON_NONE);
+}
+
+static void geo_node_curve_subdivide_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometryCurveSubdivide *data = (NodeGeometryCurveSubdivide *)MEM_callocN(
+ sizeof(NodeGeometryCurveSubdivide), __func__);
+
+ data->cuts_type = GEO_NODE_ATTRIBUTE_INPUT_INTEGER;
+ node->storage = data;
+}
+
+static void geo_node_curve_subdivide_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeGeometryPointTranslate &node_storage = *(NodeGeometryPointTranslate *)node->storage;
+
+ update_attribute_input_socket_availabilities(
+ *node, "Cuts", (GeometryNodeAttributeInputMode)node_storage.input_type);
+}
+
+static Array<int> get_subdivided_offsets(const Spline &spline,
+ const VArray<int> &cuts,
+ const int spline_offset)
+{
+ Array<int> offsets(spline.segments_size() + 1);
+ int offset = 0;
+ for (const int i : IndexRange(spline.segments_size())) {
+ offsets[i] = offset;
+ offset = offset + std::max(cuts[spline_offset + i], 0) + 1;
+ }
+ offsets.last() = offset;
+ return offsets;
+}
+
+template<typename T>
+static void subdivide_attribute(Span<T> src,
+ const Span<int> offsets,
+ const bool is_cyclic,
+ MutableSpan<T> dst)
+{
+ const int src_size = src.size();
+ threading::parallel_for(IndexRange(src_size - 1), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ const int cuts = offsets[i + 1] - offsets[i];
+ dst[offsets[i]] = src[i];
+ const float factor_delta = 1.0f / (cuts + 1.0f);
+ for (const int cut : IndexRange(cuts)) {
+ const float factor = (cut + 1) * factor_delta;
+ dst[offsets[i] + cut] = attribute_math::mix2(factor, src[i], src[i + 1]);
+ }
+ }
+ });
+
+ if (is_cyclic) {
+ const int i = src_size - 1;
+ const int cuts = offsets[i + 1] - offsets[i];
+ dst[offsets[i]] = src.last();
+ const float factor_delta = 1.0f / (cuts + 1.0f);
+ for (const int cut : IndexRange(cuts)) {
+ const float factor = (cut + 1) * factor_delta;
+ dst[offsets[i] + cut] = attribute_math::mix2(factor, src.last(), src.first());
+ }
+ }
+ else {
+ dst.last() = src.last();
+ }
+}
+
+/**
+ * In order to generate a Bezier spline with the same shape as the input spline, apply the
+ * De Casteljau algorithm iteratively for the provided number of cuts, constantly updating the
+ * previous result point's right handle and the left handle at the end of the segment.
+ *
+ * \note Non-vector segments in the result spline are given free handles. This could possibly be
+ * improved with another pass that sets handles to aligned where possible, but currently that does
+ * not provide much benefit for the increased complexity.
+ */
+static void subdivide_bezier_segment(const BezierSpline &src,
+ const int index,
+ const int offset,
+ const int result_size,
+ Span<float3> src_positions,
+ Span<float3> src_handles_left,
+ Span<float3> src_handles_right,
+ MutableSpan<float3> dst_positions,
+ MutableSpan<float3> dst_handles_left,
+ MutableSpan<float3> dst_handles_right,
+ MutableSpan<BezierSpline::HandleType> dst_type_left,
+ MutableSpan<BezierSpline::HandleType> dst_type_right)
+{
+ const bool is_last_cyclic_segment = index == (src.size() - 1);
+ const int next_index = is_last_cyclic_segment ? 0 : index + 1;
+
+ /* The first point in the segment is always copied. */
+ dst_positions[offset] = src_positions[index];
+
+ if (src.segment_is_vector(index)) {
+ if (is_last_cyclic_segment) {
+ dst_type_left.first() = BezierSpline::HandleType::Vector;
+ }
+ dst_type_left.slice(offset + 1, result_size).fill(BezierSpline::HandleType::Vector);
+ dst_type_right.slice(offset, result_size).fill(BezierSpline::HandleType::Vector);
+
+ const float factor_delta = 1.0f / result_size;
+ for (const int cut : IndexRange(result_size)) {
+ const float factor = cut * factor_delta;
+ dst_positions[offset + cut] = attribute_math::mix2(
+ factor, src_positions[index], src_positions[next_index]);
+ }
+ }
+ else {
+ if (is_last_cyclic_segment) {
+ dst_type_left.first() = BezierSpline::HandleType::Free;
+ }
+ dst_type_left.slice(offset + 1, result_size).fill(BezierSpline::HandleType::Free);
+ dst_type_right.slice(offset, result_size).fill(BezierSpline::HandleType::Free);
+
+ const int i_segment_last = is_last_cyclic_segment ? 0 : offset + result_size;
+
+ /* Create a Bezier segment to update iteratively for every subdivision
+ * and references to the meaningful values for ease of use. */
+ BezierSpline temp;
+ temp.resize(2);
+ float3 &segment_start = temp.positions().first();
+ float3 &segment_end = temp.positions().last();
+ float3 &handle_prev = temp.handle_positions_right().first();
+ float3 &handle_next = temp.handle_positions_left().last();
+ segment_start = src_positions[index];
+ segment_end = src_positions[next_index];
+ handle_prev = src_handles_right[index];
+ handle_next = src_handles_left[next_index];
+
+ for (const int cut : IndexRange(result_size - 1)) {
+ const float parameter = 1.0f / (result_size - cut);
+ const BezierSpline::InsertResult insert = temp.calculate_segment_insertion(0, 1, parameter);
+
+ /* Copy relevant temporary data to the result. */
+ dst_handles_right[offset + cut] = insert.handle_prev;
+ dst_handles_left[offset + cut + 1] = insert.left_handle;
+ dst_positions[offset + cut + 1] = insert.position;
+
+ /* Update the segment to prepare it for the next subdivision. */
+ segment_start = insert.position;
+ handle_prev = insert.right_handle;
+ handle_next = insert.handle_next;
+ }
+
+ /* Copy the handles for the last segment from the temporary spline. */
+ dst_handles_right[offset + result_size - 1] = handle_prev;
+ dst_handles_left[i_segment_last] = handle_next;
+ }
+}
+
+static void subdivide_bezier_spline(const BezierSpline &src,
+ const Span<int> offsets,
+ BezierSpline &dst)
+{
+ Span<float3> src_positions = src.positions();
+ Span<float3> src_handles_left = src.handle_positions_left();
+ Span<float3> src_handles_right = src.handle_positions_right();
+ MutableSpan<float3> dst_positions = dst.positions();
+ MutableSpan<float3> dst_handles_left = dst.handle_positions_left();
+ MutableSpan<float3> dst_handles_right = dst.handle_positions_right();
+ MutableSpan<BezierSpline::HandleType> dst_type_left = dst.handle_types_left();
+ MutableSpan<BezierSpline::HandleType> dst_type_right = dst.handle_types_right();
+
+ threading::parallel_for(IndexRange(src.size() - 1), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ subdivide_bezier_segment(src,
+ i,
+ offsets[i],
+ offsets[i + 1] - offsets[i],
+ src_positions,
+ src_handles_left,
+ src_handles_right,
+ dst_positions,
+ dst_handles_left,
+ dst_handles_right,
+ dst_type_left,
+ dst_type_right);
+ }
+ });
+
+ if (src.is_cyclic()) {
+ const int i_last = src.size() - 1;
+ subdivide_bezier_segment(src,
+ i_last,
+ offsets[i_last],
+ offsets.last() - offsets[i_last],
+ src_positions,
+ src_handles_left,
+ src_handles_right,
+ dst_positions,
+ dst_handles_left,
+ dst_handles_right,
+ dst_type_left,
+ dst_type_right);
+ }
+ else {
+ dst_positions.last() = src_positions.last();
+ }
+}
+
+static void subdivide_builtin_attributes(const Spline &src_spline,
+ const Span<int> offsets,
+ Spline &dst_spline)
+{
+ const bool is_cyclic = src_spline.is_cyclic();
+ subdivide_attribute<float>(src_spline.radii(), offsets, is_cyclic, dst_spline.radii());
+ subdivide_attribute<float>(src_spline.tilts(), offsets, is_cyclic, dst_spline.tilts());
+ switch (src_spline.type()) {
+ case Spline::Type::Poly: {
+ const PolySpline &src = static_cast<const PolySpline &>(src_spline);
+ PolySpline &dst = static_cast<PolySpline &>(dst_spline);
+ subdivide_attribute<float3>(src.positions(), offsets, is_cyclic, dst.positions());
+ break;
+ }
+ case Spline::Type::Bezier: {
+ const BezierSpline &src = static_cast<const BezierSpline &>(src_spline);
+ BezierSpline &dst = static_cast<BezierSpline &>(dst_spline);
+ subdivide_bezier_spline(src, offsets, dst);
+ dst.mark_cache_invalid();
+ break;
+ }
+ case Spline::Type::NURBS: {
+ const NURBSpline &src = static_cast<const NURBSpline &>(src_spline);
+ NURBSpline &dst = static_cast<NURBSpline &>(dst_spline);
+ subdivide_attribute<float3>(src.positions(), offsets, is_cyclic, dst.positions());
+ subdivide_attribute<float>(src.weights(), offsets, is_cyclic, dst.weights());
+ break;
+ }
+ }
+}
+
+static void subdivide_dynamic_attributes(const Spline &src_spline,
+ const Span<int> offsets,
+ Spline &dst_spline)
+{
+ const bool is_cyclic = src_spline.is_cyclic();
+ src_spline.attributes.foreach_attribute(
+ [&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
+ std::optional<GSpan> src = src_spline.attributes.get_for_read(attribute_id);
+ BLI_assert(src);
+
+ if (!dst_spline.attributes.create(attribute_id, meta_data.data_type)) {
+ /* Since the source spline of the same type had the attribute, adding it should work. */
+ BLI_assert_unreachable();
+ }
+
+ std::optional<GMutableSpan> dst = dst_spline.attributes.get_for_write(attribute_id);
+ BLI_assert(dst);
+
+ attribute_math::convert_to_static_type(dst->type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ subdivide_attribute<T>(src->typed<T>(), offsets, is_cyclic, dst->typed<T>());
+ });
+ return true;
+ },
+ ATTR_DOMAIN_POINT);
+}
+
+static SplinePtr subdivide_spline(const Spline &spline,
+ const VArray<int> &cuts,
+ const int spline_offset)
+{
+ /* Since we expect to access each value many times, it should be worth it to make sure the
+ * attribute is a real span (especially considering the note below). Using the offset at each
+ * point facilitates subdividing in parallel later. */
+ Array<int> offsets = get_subdivided_offsets(spline, cuts, spline_offset);
+ const int result_size = offsets.last() + int(!spline.is_cyclic());
+ SplinePtr new_spline = spline.copy_only_settings();
+ new_spline->resize(result_size);
+ subdivide_builtin_attributes(spline, offsets, *new_spline);
+ subdivide_dynamic_attributes(spline, offsets, *new_spline);
+ return new_spline;
+}
+
+/**
+ * \note Passing the virtual array for the entire spline is possibly quite inefficient here when
+ * the attribute was on the point domain and stored separately for each spline already, and it
+ * prevents some other optimizations like skipping splines with a single attribute value of < 1.
+ * However, it allows the node to access builtin attribute easily, so it the makes most sense this
+ * way until the attribute API is refactored.
+ */
+static std::unique_ptr<CurveEval> subdivide_curve(const CurveEval &input_curve,
+ const VArray<int> &cuts)
+{
+ const Array<int> control_point_offsets = input_curve.control_point_offsets();
+ const Span<SplinePtr> input_splines = input_curve.splines();
+
+ std::unique_ptr<CurveEval> output_curve = std::make_unique<CurveEval>();
+ output_curve->resize(input_splines.size());
+ output_curve->attributes = input_curve.attributes;
+ MutableSpan<SplinePtr> output_splines = output_curve->splines();
+
+ threading::parallel_for(input_splines.index_range(), 128, [&](IndexRange range) {
+ for (const int i : range) {
+ output_splines[i] = subdivide_spline(*input_splines[i], cuts, control_point_offsets[i]);
+ }
+ });
+
+ return output_curve;
+}
+
+static void geo_node_subdivide_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", geometry_set);
+ return;
+ }
+
+ const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>();
+ GVArray_Typed<int> cuts = params.get_input_attribute<int>(
+ "Cuts", component, ATTR_DOMAIN_POINT, 0);
+ if (cuts->is_single() && cuts->get_internal_single() < 1) {
+ params.set_output("Geometry", geometry_set);
+ return;
+ }
+
+ std::unique_ptr<CurveEval> output_curve = subdivide_curve(*component.get_for_read(), *cuts);
+
+ params.set_output("Geometry", GeometrySet::create_with_curve(output_curve.release()));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_legacy_curve_subdivide()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_LEGACY_CURVE_SUBDIVIDE, "Curve Subdivide", NODE_CLASS_GEOMETRY, 0);
+ ntype.declare = blender::nodes::geo_node_curve_subdivide_declare;
+ ntype.draw_buttons = blender::nodes::geo_node_curve_subdivide_layout;
+ node_type_storage(&ntype,
+ "NodeGeometryCurveSubdivide",
+ node_free_standard_storage,
+ node_copy_standard_storage);
+ node_type_init(&ntype, blender::nodes::geo_node_curve_subdivide_init);
+ node_type_update(&ntype, blender::nodes::geo_node_curve_subdivide_update);
+ ntype.geometry_node_execute = blender::nodes::geo_node_subdivide_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_to_points.cc
index 1e66b340f5c..0c435d69991 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_to_points.cc
@@ -358,7 +358,8 @@ void register_node_type_geo_curve_to_points()
{
static bNodeType ntype;
- geo_node_type_base(&ntype, GEO_NODE_CURVE_TO_POINTS, "Curve to Points", NODE_CLASS_GEOMETRY, 0);
+ geo_node_type_base(
+ &ntype, GEO_NODE_LEGACY_CURVE_TO_POINTS, "Curve to Points", NODE_CLASS_GEOMETRY, 0);
ntype.declare = blender::nodes::geo_node_curve_to_points_declare;
ntype.geometry_node_execute = blender::nodes::geo_node_curve_to_points_exec;
ntype.draw_buttons = blender::nodes::geo_node_curve_to_points_layout;
diff --git a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_delete_geometry.cc
index 1e2f652cd78..1e2f652cd78 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_delete_geometry.cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_edge_split.cc
index 867fecea251..2ea6516996d 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_edge_split.cc
@@ -82,7 +82,7 @@ void register_node_type_geo_edge_split()
{
static bNodeType ntype;
- geo_node_type_base(&ntype, GEO_NODE_EDGE_SPLIT, "Edge Split", NODE_CLASS_GEOMETRY, 0);
+ geo_node_type_base(&ntype, GEO_NODE_LEGACY_EDGE_SPLIT, "Edge Split", NODE_CLASS_GEOMETRY, 0);
ntype.geometry_node_execute = blender::nodes::geo_node_edge_split_exec;
ntype.declare = blender::nodes::geo_node_edge_split_declare;
nodeRegisterType(&ntype);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_mesh_to_curve.cc
index 11349dc7d42..11349dc7d42 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_mesh_to_curve.cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_distribute.cc
index 04b4003daed..f95b0da86ed 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_distribute.cc
@@ -36,7 +36,6 @@
#include "node_geometry_util.hh"
-using blender::bke::AttributeKind;
using blender::bke::GeometryInstanceGroup;
namespace blender::nodes {
diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_instance.cc
index fb45c22ced4..fb45c22ced4 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_instance.cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_rotate.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_rotate.cc
index 60c82360007..60c82360007 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_point_rotate.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_rotate.cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_scale.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_scale.cc
index 99adce149e9..99adce149e9 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_point_scale.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_scale.cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_separate.cc
index 48b6676c1dd..48b6676c1dd 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_separate.cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_translate.cc
index f2fce45c57b..f2fce45c57b 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_translate.cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_points_to_volume.cc
index d920c8de9f0..d920c8de9f0 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_points_to_volume.cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_raycast.cc
index 401a478f04c..401a478f04c 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_raycast.cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_subdivision_surface.cc
index 4541bf3569f..07d3f89bdb7 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_subdivision_surface.cc
@@ -133,7 +133,7 @@ void register_node_type_geo_subdivision_surface()
static bNodeType ntype;
geo_node_type_base(
- &ntype, GEO_NODE_SUBDIVISION_SURFACE, "Subdivision Surface", NODE_CLASS_GEOMETRY, 0);
+ &ntype, GEO_NODE_LEGACY_SUBDIVISION_SURFACE, "Subdivision Surface", NODE_CLASS_GEOMETRY, 0);
ntype.declare = blender::nodes::geo_node_subdivision_surface_declare;
ntype.geometry_node_execute = blender::nodes::geo_node_subdivision_surface_exec;
ntype.draw_buttons = blender::nodes::geo_node_subdivision_surface_layout;
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc
index c8a33205de4..43fb00a482c 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc
@@ -26,18 +26,18 @@ namespace blender::nodes {
static void geo_node_attribute_capture_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Vector>("Value");
- b.add_input<decl::Float>("Value", "Value_001");
- b.add_input<decl::Color>("Value", "Value_002");
- b.add_input<decl::Bool>("Value", "Value_003");
- b.add_input<decl::Int>("Value", "Value_004");
+ b.add_input<decl::Vector>("Value").supports_field();
+ b.add_input<decl::Float>("Value", "Value_001").supports_field();
+ b.add_input<decl::Color>("Value", "Value_002").supports_field();
+ b.add_input<decl::Bool>("Value", "Value_003").supports_field();
+ b.add_input<decl::Int>("Value", "Value_004").supports_field();
b.add_output<decl::Geometry>("Geometry");
- b.add_output<decl::Vector>("Attribute");
- b.add_output<decl::Float>("Attribute", "Attribute_001");
- b.add_output<decl::Color>("Attribute", "Attribute_002");
- b.add_output<decl::Bool>("Attribute", "Attribute_003");
- b.add_output<decl::Int>("Attribute", "Attribute_004");
+ b.add_output<decl::Vector>("Attribute").field_source();
+ b.add_output<decl::Float>("Attribute", "Attribute_001").field_source();
+ b.add_output<decl::Color>("Attribute", "Attribute_002").field_source();
+ b.add_output<decl::Bool>("Attribute", "Attribute_003").field_source();
+ b.add_output<decl::Int>("Attribute", "Attribute_004").field_source();
}
static void geo_node_attribute_capture_layout(uiLayout *layout,
@@ -117,8 +117,6 @@ static void geo_node_attribute_capture_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = bke::geometry_set_realize_instances(geometry_set);
-
const bNode &node = params.node();
const NodeGeometryAttributeCapture &storage = *(const NodeGeometryAttributeCapture *)
node.storage;
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 21a9a338857..f93ef6f1db3 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc
@@ -47,8 +47,6 @@ static void geo_node_attribute_remove_exec(GeoNodeExecParams params)
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
Vector<std::string> attribute_names = params.extract_multi_input<std::string>("Attribute");
- geometry_set = geometry_set_realize_instances(geometry_set);
-
if (geometry_set.has<MeshComponent>()) {
remove_attribute(
geometry_set.get_component_for_write<MeshComponent>(), params, attribute_names);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc
index 5001034518c..1b7d2fe28a1 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc
@@ -29,8 +29,8 @@ namespace blender::nodes {
static void geo_node_attribute_statistic_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Float>("Attribute").hide_value();
- b.add_input<decl::Vector>("Attribute", "Attribute_001").hide_value();
+ b.add_input<decl::Float>("Attribute").hide_value().supports_field();
+ b.add_input<decl::Vector>("Attribute", "Attribute_001").hide_value().supports_field();
b.add_output<decl::Float>("Mean");
b.add_output<decl::Float>("Median");
diff --git a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc
index 2a1c43a89fe..21b425c0ed4 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc
@@ -83,10 +83,14 @@ static void geo_node_boolean_exec(GeoNodeExecParams params)
GeometrySet set_a;
if (operation == GEO_NODE_BOOLEAN_DIFFERENCE) {
set_a = params.extract_input<GeometrySet>("Geometry 1");
+ if (set_a.has_instances()) {
+ params.error_message_add(
+ NodeWarningType::Info,
+ TIP_("Instances are not supported for the first geometry input, and will not be used"));
+ }
/* 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);
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 f4c295b06fb..d03221703f0 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc
@@ -21,6 +21,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "BKE_collection.h"
+
#include "node_geometry_util.hh"
namespace blender::nodes {
@@ -28,6 +30,12 @@ namespace blender::nodes {
static void geo_node_collection_info_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Collection>("Collection").hide_label();
+ b.add_input<decl::Bool>("Separate Children")
+ .description("Output each child of the collection as a separate instance");
+ b.add_input<decl::Bool>("Reset Children")
+ .description(
+ "Reset the transforms of every child instance in the output. Only used when Separate "
+ "Children is enabled");
b.add_output<decl::Geometry>("Geometry");
}
@@ -57,23 +65,66 @@ static void geo_node_collection_info_exec(GeoNodeExecParams params)
const bNode &bnode = params.node();
NodeGeometryCollectionInfo *node_storage = (NodeGeometryCollectionInfo *)bnode.storage;
- const bool transform_space_relative = (node_storage->transform_space ==
- GEO_NODE_TRANSFORM_SPACE_RELATIVE);
+ const bool use_relative_transform = (node_storage->transform_space ==
+ GEO_NODE_TRANSFORM_SPACE_RELATIVE);
InstancesComponent &instances = geometry_set_out.get_component_for_write<InstancesComponent>();
- float transform_mat[4][4];
- unit_m4(transform_mat);
const Object *self_object = params.self_object();
- if (transform_space_relative) {
- copy_v3_v3(transform_mat[3], collection->instance_offset);
-
- mul_m4_m4_pre(transform_mat, self_object->imat);
+ const bool separate_children = params.get_input<bool>("Separate Children");
+ if (separate_children) {
+ const bool reset_children = params.get_input<bool>("Reset Children");
+ Vector<Collection *> children_collections;
+ LISTBASE_FOREACH (CollectionChild *, collection_child, &collection->children) {
+ children_collections.append(collection_child->collection);
+ }
+ Vector<Object *> children_objects;
+ LISTBASE_FOREACH (CollectionObject *, collection_object, &collection->gobject) {
+ children_objects.append(collection_object->ob);
+ }
+
+ instances.reserve(children_collections.size() + children_objects.size());
+
+ for (Collection *child_collection : children_collections) {
+ float4x4 transform = float4x4::identity();
+ if (!reset_children) {
+ add_v3_v3(transform.values[3], child_collection->instance_offset);
+ if (use_relative_transform) {
+ mul_m4_m4_pre(transform.values, self_object->imat);
+ }
+ else {
+ sub_v3_v3(transform.values[3], collection->instance_offset);
+ }
+ }
+ const int handle = instances.add_reference(*child_collection);
+ instances.add_instance(handle, transform);
+ }
+ for (Object *child_object : children_objects) {
+ const int handle = instances.add_reference(*child_object);
+ float4x4 transform = float4x4::identity();
+ if (!reset_children) {
+ if (use_relative_transform) {
+ transform = self_object->imat;
+ }
+ else {
+ sub_v3_v3(transform.values[3], collection->instance_offset);
+ }
+ mul_m4_m4_post(transform.values, child_object->obmat);
+ }
+ instances.add_instance(handle, transform);
+ }
+ }
+ else {
+ float4x4 transform = float4x4::identity();
+ if (use_relative_transform) {
+ copy_v3_v3(transform.values[3], collection->instance_offset);
+ mul_m4_m4_pre(transform.values, self_object->imat);
+ }
+
+ const int handle = instances.add_reference(*collection);
+ instances.add_instance(handle, transform);
}
-
- const int handle = instances.add_reference(*collection);
- instances.add_instance(handle, transform_mat, -1);
params.set_output("Geometry", geometry_set_out);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc
index f1e10e3d276..4377d32210d 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc
@@ -227,9 +227,13 @@ static Mesh *compute_hull(const GeometrySet &geometry_set)
return hull_from_bullet(geometry_set.get_mesh_for_read(), positions);
}
+/* Since only positions are read from the instances, this can be used as an internal optimization
+ * to avoid the cost of realizing instances before the node. But disable this for now, since
+ * re-enabling that optimization will be a separate step. */
+# if 0
static void read_positions(const GeometryComponent &component,
- Span<float4x4> transforms,
- Vector<float3> *r_coords)
+ Span<float4x4> transforms,
+ Vector<float3> *r_coords)
{
GVArray_Typed<float3> positions = component.attribute_get_for_read<float3>(
"position", ATTR_DOMAIN_POINT, {0, 0, 0});
@@ -265,6 +269,31 @@ static void read_curve_positions(const CurveEval &curve,
}
}
+static Mesh *convex_hull_from_instances(const GeometrySet &geometry_set)
+{
+ 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);
+ }
+ }
+ return hull_from_bullet(nullptr, coords);
+}
+# endif
+
#endif /* WITH_BULLET */
static void geo_node_convex_hull_exec(GeoNodeExecParams params)
@@ -272,33 +301,14 @@ 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;
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ Mesh *mesh = compute_hull(geometry_set);
+ geometry_set.replace_mesh(mesh);
+ geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES});
+ });
- 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));
+ params.set_output("Convex Hull", std::move(geometry_set));
#else
params.error_message_add(NodeWarningType::Error,
TIP_("Disabled, Blender was compiled without Bullet"));
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc
index 8de2975f9b0..c30741cf786 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc
@@ -154,23 +154,8 @@ static void geo_node_curve_fill_exec(GeoNodeExecParams params)
const NodeGeometryCurveFill &storage = *(const NodeGeometryCurveFill *)params.node().storage;
const GeometryNodeCurveFillMode mode = (GeometryNodeCurveFillMode)storage.mode;
- if (geometry_set.has_instances()) {
- InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>();
- instances.ensure_geometry_instances();
-
- threading::parallel_for(IndexRange(instances.references_amount()), 16, [&](IndexRange range) {
- for (int i : range) {
- GeometrySet &geometry_set = instances.geometry_set_from_reference(i);
- geometry_set = bke::geometry_set_realize_instances(geometry_set);
- curve_fill_calculate(geometry_set, mode);
- }
- });
-
- params.set_output("Mesh", std::move(geometry_set));
- return;
- }
-
- curve_fill_calculate(geometry_set, mode);
+ geometry_set.modify_geometry_sets(
+ [&](GeometrySet &geometry_set) { curve_fill_calculate(geometry_set, mode); });
params.set_output("Mesh", std::move(geometry_set));
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc
index 830cfcc8331..67ce20efd9d 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc
@@ -563,19 +563,16 @@ static std::unique_ptr<CurveEval> fillet_curve(const CurveEval &input_curve,
return output_curve;
}
-static void geo_node_fillet_exec(GeoNodeExecParams params)
+static void calculate_curve_fillet(GeometrySet &geometry_set,
+ const GeometryNodeCurveFilletMode mode,
+ const Field<float> &radius_field,
+ const std::optional<Field<int>> &count_field,
+ const bool limit_radius)
{
- GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
-
- geometry_set = bke::geometry_set_realize_instances(geometry_set);
-
if (!geometry_set.has_curve()) {
- params.set_output("Curve", geometry_set);
return;
}
- NodeGeometryCurveFillet &node_storage = *(NodeGeometryCurveFillet *)params.node().storage;
- const GeometryNodeCurveFilletMode mode = (GeometryNodeCurveFilletMode)node_storage.mode;
FilletParam fillet_param;
fillet_param.mode = mode;
@@ -584,19 +581,16 @@ static void geo_node_fillet_exec(GeoNodeExecParams params)
const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT);
fn::FieldEvaluator field_evaluator{field_context, domain_size};
- Field<float> radius_field = params.extract_input<Field<float>>("Radius");
- field_evaluator.add(std::move(radius_field));
+ field_evaluator.add(radius_field);
if (mode == GEO_NODE_CURVE_FILLET_POLY) {
- Field<int> count_field = params.extract_input<Field<int>>("Count");
- field_evaluator.add(std::move(count_field));
+ field_evaluator.add(*count_field);
}
field_evaluator.evaluate();
fillet_param.radii = &field_evaluator.get_evaluated<float>(0);
if (fillet_param.radii->is_single() && fillet_param.radii->get_internal_single() < 0.0f) {
- params.set_output("Geometry", geometry_set);
return;
}
@@ -604,13 +598,36 @@ static void geo_node_fillet_exec(GeoNodeExecParams params)
fillet_param.counts = &field_evaluator.get_evaluated<int>(1);
}
- fillet_param.limit_radius = params.extract_input<bool>("Limit Radius");
+ fillet_param.limit_radius = limit_radius;
const CurveEval &input_curve = *geometry_set.get_curve_for_read();
std::unique_ptr<CurveEval> output_curve = fillet_curve(input_curve, fillet_param);
- params.set_output("Curve", GeometrySet::create_with_curve(output_curve.release()));
+ geometry_set.replace_curve(output_curve.release());
+}
+
+static void geo_node_fillet_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
+
+ NodeGeometryCurveFillet &node_storage = *(NodeGeometryCurveFillet *)params.node().storage;
+ const GeometryNodeCurveFilletMode mode = (GeometryNodeCurveFilletMode)node_storage.mode;
+
+ Field<float> radius_field = params.extract_input<Field<float>>("Radius");
+ const bool limit_radius = params.extract_input<bool>("Limit Radius");
+
+ std::optional<Field<int>> count_field;
+ if (mode == GEO_NODE_CURVE_FILLET_POLY) {
+ count_field.emplace(params.extract_input<Field<int>>("Count"));
+ }
+
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ calculate_curve_fillet(geometry_set, mode, radius_field, count_field, limit_radius);
+ });
+
+ params.set_output("Curve", std::move(geometry_set));
}
+
} // namespace blender::nodes
void register_node_type_geo_curve_fillet()
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc
index 8fe054633a1..ac7df35bb72 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc
@@ -28,7 +28,6 @@ static void geo_node_curve_length_declare(NodeDeclarationBuilder &b)
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;
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_parameter.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_parameter.cc
index 2cde198e679..90853387ec7 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_parameter.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_parameter.cc
@@ -24,7 +24,7 @@ namespace blender::nodes {
static void geo_node_curve_parameter_declare(NodeDeclarationBuilder &b)
{
- b.add_output<decl::Float>("Factor");
+ b.add_output<decl::Float>("Factor").field_source();
}
/**
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc
index 0803d43e5c3..7292fafc8b0 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc
@@ -43,26 +43,18 @@ static std::unique_ptr<CurveEval> create_spiral_curve(const float rotations,
const int totalpoints = std::max(int(resolution * rotations), 1);
const float delta_radius = (end_radius - start_radius) / (float)totalpoints;
- float radius = start_radius;
const float delta_height = height / (float)totalpoints;
- const float delta_theta = (M_PI * 2 * rotations) / (float)totalpoints;
- float theta = 0.0f;
+ const float delta_theta = (M_PI * 2 * rotations) / (float)totalpoints *
+ (direction ? 1.0f : -1.0f);
for (const int i : IndexRange(totalpoints + 1)) {
+ const float theta = i * delta_theta;
+ const float radius = start_radius + i * delta_radius;
const float x = radius * cos(theta);
const float y = radius * sin(theta);
const float z = delta_height * i;
spline->add_point(float3(x, y, z), 1.0f, 0.0f);
-
- radius += delta_radius;
-
- if (direction) {
- theta += delta_theta;
- }
- else {
- theta -= delta_theta;
- }
}
spline->attributes.reallocate(spline->size());
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc
index 208525f17f6..e5be9b7a6f4 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc
@@ -35,8 +35,9 @@ namespace blender::nodes {
static void geo_node_curve_resample_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Int>("Count").default_value(10).min(1).max(100000);
- b.add_input<decl::Float>("Length").default_value(0.1f).min(0.001f).subtype(PROP_DISTANCE);
+ b.add_input<decl::Int>("Count").default_value(10).min(1).max(100000).supports_field();
+ b.add_input<decl::Float>("Length").default_value(0.1f).min(0.001f).supports_field().subtype(
+ PROP_DISTANCE);
b.add_output<decl::Geometry>("Geometry");
}
@@ -68,8 +69,8 @@ static void geo_node_curve_resample_update(bNodeTree *UNUSED(ntree), bNode *node
struct SampleModeParam {
GeometryNodeCurveResampleMode mode;
- std::optional<float> length;
- std::optional<int> count;
+ std::optional<Field<float>> length;
+ std::optional<Field<int>> count;
};
static SplinePtr resample_spline(const Spline &src, const int count)
@@ -163,28 +164,44 @@ static SplinePtr resample_spline_evaluated(const Spline &src)
return dst;
}
-static std::unique_ptr<CurveEval> resample_curve(const CurveEval &input_curve,
+static std::unique_ptr<CurveEval> resample_curve(const CurveComponent *component,
const SampleModeParam &mode_param)
{
- Span<SplinePtr> input_splines = input_curve.splines();
+ const CurveEval *input_curve = component->get_for_read();
+ GeometryComponentFieldContext field_context{*component, ATTR_DOMAIN_CURVE};
+ const int domain_size = component->attribute_domain_size(ATTR_DOMAIN_CURVE);
+
+ fn::FieldEvaluator evaluator{field_context, domain_size};
+
+ Span<SplinePtr> input_splines = input_curve->splines();
std::unique_ptr<CurveEval> output_curve = std::make_unique<CurveEval>();
output_curve->resize(input_splines.size());
MutableSpan<SplinePtr> output_splines = output_curve->splines();
if (mode_param.mode == GEO_NODE_CURVE_RESAMPLE_COUNT) {
+ evaluator.add(*mode_param.count);
+ evaluator.evaluate();
+ const VArray<int> &cuts = evaluator.get_evaluated<int>(0);
+
threading::parallel_for(input_splines.index_range(), 128, [&](IndexRange range) {
for (const int i : range) {
BLI_assert(mode_param.count);
- output_splines[i] = resample_spline(*input_splines[i], *mode_param.count);
+ output_splines[i] = resample_spline(*input_splines[i], std::max(cuts[i], 1));
}
});
}
else if (mode_param.mode == GEO_NODE_CURVE_RESAMPLE_LENGTH) {
+ evaluator.add(*mode_param.length);
+ evaluator.evaluate();
+ const VArray<float> &lengths = evaluator.get_evaluated<float>(0);
+
threading::parallel_for(input_splines.index_range(), 128, [&](IndexRange range) {
for (const int i : range) {
- const float length = input_splines[i]->length();
- const int count = std::max(int(length / *mode_param.length) + 1, 1);
+ /* Don't allow asymptotic count increase for low resolution values. */
+ const float divide_length = std::max(lengths[i], 0.0001f);
+ const float spline_length = input_splines[i]->length();
+ const int count = std::max(int(spline_length / divide_length) + 1, 1);
output_splines[i] = resample_spline(*input_splines[i], count);
}
});
@@ -197,29 +214,35 @@ static std::unique_ptr<CurveEval> resample_curve(const CurveEval &input_curve,
});
}
- output_curve->attributes = input_curve.attributes;
+ output_curve->attributes = input_curve->attributes;
return output_curve;
}
-static void geo_node_resample_exec(GeoNodeExecParams params)
+static void geometry_set_curve_resample(GeometrySet &geometry_set,
+ const SampleModeParam &mode_param)
{
- 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();
+ std::unique_ptr<CurveEval> output_curve = resample_curve(
+ geometry_set.get_component_for_read<CurveComponent>(), mode_param);
+
+ geometry_set.replace_curve(output_curve.release());
+}
+
+static void geo_node_resample_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
NodeGeometryCurveResample &node_storage = *(NodeGeometryCurveResample *)params.node().storage;
const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)node_storage.mode;
+
SampleModeParam mode_param;
mode_param.mode = mode;
if (mode == GEO_NODE_CURVE_RESAMPLE_COUNT) {
- const int count = params.extract_input<int>("Count");
+ Field<int> count = params.extract_input<Field<int>>("Count");
if (count < 1) {
params.set_output("Geometry", GeometrySet());
return;
@@ -227,14 +250,14 @@ static void geo_node_resample_exec(GeoNodeExecParams params)
mode_param.count.emplace(count);
}
else if (mode == GEO_NODE_CURVE_RESAMPLE_LENGTH) {
- /* Don't allow asymptotic count increase for low resolution values. */
- const float resolution = std::max(params.extract_input<float>("Length"), 0.0001f);
+ Field<int> resolution = params.extract_input<Field<int>>("Length");
mode_param.length.emplace(resolution);
}
- std::unique_ptr<CurveEval> output_curve = resample_curve(input_curve, mode_param);
+ geometry_set.modify_geometry_sets(
+ [&](GeometrySet &geometry_set) { geometry_set_curve_resample(geometry_set, mode_param); });
- params.set_output("Geometry", GeometrySet::create_with_curve(output_curve.release()));
+ params.set_output("Geometry", std::move(geometry_set));
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc
index 32bcbe2c608..b644faabedb 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc
@@ -25,37 +25,39 @@ namespace blender::nodes {
static void geo_node_curve_reverse_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Geometry>("Curve");
- b.add_input<decl::String>("Selection");
+ b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field();
b.add_output<decl::Geometry>("Curve");
}
static void geo_node_curve_reverse_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
- geometry_set = bke::geometry_set_realize_instances(geometry_set);
- if (!geometry_set.has_curve()) {
- params.set_output("Curve", geometry_set);
- return;
- }
- /* Retrieve data for write access so we can avoid new allocations for the reversed data. */
- CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>();
- CurveEval &curve = *curve_component.get_for_write();
- MutableSpan<SplinePtr> splines = curve.splines();
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ if (!geometry_set.has_curve()) {
+ return;
+ }
+
+ Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
+ CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>();
+ GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE};
+ const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE);
- const std::string selection_name = params.extract_input<std::string>("Selection");
- GVArray_Typed<bool> selection = curve_component.attribute_get_for_read(
- selection_name, ATTR_DOMAIN_CURVE, true);
+ fn::FieldEvaluator selection_evaluator{field_context, domain_size};
+ selection_evaluator.add(selection_field);
+ selection_evaluator.evaluate();
+ const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0);
- threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) {
- for (const int i : range) {
- if (selection[i]) {
- splines[i]->reverse();
+ CurveEval &curve = *component.get_for_write();
+ MutableSpan<SplinePtr> splines = curve.splines();
+ threading::parallel_for(selection.index_range(), 128, [&](IndexRange range) {
+ for (const int i : range) {
+ splines[selection[i]]->reverse();
}
- }
+ });
});
- params.set_output("Curve", geometry_set);
+ params.set_output("Curve", std::move(geometry_set));
}
} // namespace blender::nodes
@@ -63,8 +65,7 @@ static void geo_node_curve_reverse_exec(GeoNodeExecParams params)
void register_node_type_geo_curve_reverse()
{
static bNodeType ntype;
- geo_node_type_base(
- &ntype, GEO_NODE_LEGACY_CURVE_REVERSE, "Curve Reverse", NODE_CLASS_GEOMETRY, 0);
+ geo_node_type_base(&ntype, GEO_NODE_CURVE_REVERSE, "Curve Reverse", NODE_CLASS_GEOMETRY, 0);
ntype.declare = blender::nodes::geo_node_curve_reverse_declare;
ntype.geometry_node_execute = blender::nodes::geo_node_curve_reverse_exec;
nodeRegisterType(&ntype);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc
index ac0cd510ffa..1266f525861 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc
@@ -28,12 +28,12 @@ namespace blender::nodes {
static void geo_node_curve_sample_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Geometry>("Curve");
- b.add_input<decl::Float>("Factor").min(0.0f).max(1.0f).subtype(PROP_FACTOR);
- b.add_input<decl::Float>("Length").min(0.0f).subtype(PROP_DISTANCE);
+ b.add_input<decl::Float>("Factor").min(0.0f).max(1.0f).subtype(PROP_FACTOR).supports_field();
+ b.add_input<decl::Float>("Length").min(0.0f).subtype(PROP_DISTANCE).supports_field();
- b.add_output<decl::Vector>("Position");
- b.add_output<decl::Vector>("Tangent");
- b.add_output<decl::Vector>("Normal");
+ b.add_output<decl::Vector>("Position").dependent_field();
+ b.add_output<decl::Vector>("Tangent").dependent_field();
+ b.add_output<decl::Vector>("Normal").dependent_field();
}
static void geo_node_curve_sample_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc
index 31c13134f79..9e7ac60c29d 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc
@@ -23,10 +23,10 @@
namespace blender::nodes {
-static void geo_node_curve_set_handles_decalre(NodeDeclarationBuilder &b)
+static void geo_node_curve_set_handles_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Geometry>("Curve");
- b.add_input<decl::String>("Selection");
+ b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field();
b.add_output<decl::Geometry>("Curve");
}
@@ -44,7 +44,7 @@ static void geo_node_curve_set_handles_init(bNodeTree *UNUSED(tree), bNode *node
sizeof(NodeGeometryCurveSetHandles), __func__);
data->handle_type = GEO_NODE_CURVE_HANDLE_AUTO;
- data->mode = GEO_NODE_CURVE_HANDLE_LEFT | GEO_NODE_CURVE_HANDLE_RIGHT;
+ data->mode = GEO_NODE_CURVE_HANDLE_LEFT;
node->storage = data;
}
@@ -72,57 +72,63 @@ static void geo_node_curve_set_handles_exec(GeoNodeExecParams params)
const GeometryNodeCurveHandleMode mode = (GeometryNodeCurveHandleMode)node_storage->mode;
GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
- geometry_set = bke::geometry_set_realize_instances(geometry_set);
- if (!geometry_set.has_curve()) {
- params.set_output("Curve", geometry_set);
- return;
- }
-
- /* Retrieve data for write access so we can avoid new allocations for the handles data. */
- CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>();
- CurveEval &curve = *curve_component.get_for_write();
- MutableSpan<SplinePtr> splines = curve.splines();
+ Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
- const std::string selection_name = params.extract_input<std::string>("Selection");
- GVArray_Typed<bool> selection = curve_component.attribute_get_for_read(
- selection_name, ATTR_DOMAIN_POINT, true);
-
- const BezierSpline::HandleType new_handle_type = handle_type_from_input_type(type);
- int point_index = 0;
bool has_bezier_spline = false;
- for (SplinePtr &spline : splines) {
- if (spline->type() != Spline::Type::Bezier) {
- point_index += spline->positions().size();
- continue;
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ if (!geometry_set.has_curve()) {
+ return;
}
- BezierSpline &bezier_spline = static_cast<BezierSpline &>(*spline);
- if (ELEM(new_handle_type, BezierSpline::HandleType::Free, BezierSpline::HandleType::Align)) {
- /* In this case the automatically calculated handle types need to be "baked", because
- * they're possibly changing from a type that is calculated automatically to a type that
- * is positioned manually. */
- bezier_spline.ensure_auto_handles();
- }
- has_bezier_spline = true;
- for (int i_point : IndexRange(bezier_spline.size())) {
- if (selection[point_index]) {
- if (mode & GEO_NODE_CURVE_HANDLE_LEFT) {
- bezier_spline.handle_types_left()[i_point] = new_handle_type;
- }
- if (mode & GEO_NODE_CURVE_HANDLE_RIGHT) {
- bezier_spline.handle_types_right()[i_point] = new_handle_type;
+ /* Retrieve data for write access so we can avoid new allocations for the handles data. */
+ CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>();
+ CurveEval &curve = *curve_component.get_for_write();
+ MutableSpan<SplinePtr> splines = curve.splines();
+
+ GeometryComponentFieldContext field_context{curve_component, ATTR_DOMAIN_POINT};
+ const int domain_size = curve_component.attribute_domain_size(ATTR_DOMAIN_POINT);
+
+ fn::FieldEvaluator selection_evaluator{field_context, domain_size};
+ selection_evaluator.add(selection_field);
+ selection_evaluator.evaluate();
+ const VArray<bool> &selection = selection_evaluator.get_evaluated<bool>(0);
+
+ const BezierSpline::HandleType new_handle_type = handle_type_from_input_type(type);
+ int point_index = 0;
+
+ for (SplinePtr &spline : splines) {
+ if (spline->type() != Spline::Type::Bezier) {
+ point_index += spline->positions().size();
+ continue;
+ }
+
+ has_bezier_spline = true;
+ BezierSpline &bezier_spline = static_cast<BezierSpline &>(*spline);
+ if (ELEM(new_handle_type, BezierSpline::HandleType::Free, BezierSpline::HandleType::Align)) {
+ /* In this case the automatically calculated handle types need to be "baked", because
+ * they're possibly changing from a type that is calculated automatically to a type that
+ * is positioned manually. */
+ bezier_spline.ensure_auto_handles();
+ }
+
+ for (int i_point : IndexRange(bezier_spline.size())) {
+ if (selection[point_index]) {
+ if (mode & GEO_NODE_CURVE_HANDLE_LEFT) {
+ bezier_spline.handle_types_left()[i_point] = new_handle_type;
+ }
+ if (mode & GEO_NODE_CURVE_HANDLE_RIGHT) {
+ bezier_spline.handle_types_right()[i_point] = new_handle_type;
+ }
}
+ point_index++;
}
- point_index++;
+ bezier_spline.mark_cache_invalid();
}
- bezier_spline.mark_cache_invalid();
- }
-
+ });
if (!has_bezier_spline) {
params.error_message_add(NodeWarningType::Info, TIP_("No Bezier splines in input curve"));
}
-
- params.set_output("Curve", geometry_set);
+ params.set_output("Curve", std::move(geometry_set));
}
} // namespace blender::nodes
@@ -130,8 +136,8 @@ void register_node_type_geo_curve_set_handles()
{
static bNodeType ntype;
geo_node_type_base(
- &ntype, GEO_NODE_LEGACY_CURVE_SET_HANDLES, "Set Handle Type", NODE_CLASS_GEOMETRY, 0);
- ntype.declare = blender::nodes::geo_node_curve_set_handles_decalre;
+ &ntype, GEO_NODE_CURVE_SET_HANDLES, "Set Handle Type", NODE_CLASS_GEOMETRY, 0);
+ ntype.declare = blender::nodes::geo_node_curve_set_handles_declare;
ntype.geometry_node_execute = blender::nodes::geo_node_curve_set_handles_exec;
node_type_init(&ntype, blender::nodes::geo_node_curve_set_handles_init);
node_type_storage(&ntype,
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc
index 0ef107fd8a4..ec72154db13 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc
@@ -28,7 +28,7 @@ namespace blender::nodes {
static void geo_node_curve_spline_type_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Geometry>("Curve");
- b.add_input<decl::String>("Selection");
+ b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field();
b.add_output<decl::Geometry>("Curve");
}
@@ -245,41 +245,47 @@ static void geo_node_curve_spline_type_exec(GeoNodeExecParams params)
const GeometryNodeSplineType output_type = (const GeometryNodeSplineType)storage->spline_type;
GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
- geometry_set = bke::geometry_set_realize_instances(geometry_set);
- if (!geometry_set.has_curve()) {
- params.set_output("Curve", geometry_set);
- return;
- }
+ Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
- const CurveComponent *curve_component = geometry_set.get_component_for_read<CurveComponent>();
- const CurveEval &curve = *curve_component->get_for_read();
-
- const std::string selection_name = params.extract_input<std::string>("Selection");
- GVArray_Typed<bool> selection = curve_component->attribute_get_for_read(
- selection_name, ATTR_DOMAIN_CURVE, true);
-
- std::unique_ptr<CurveEval> new_curve = std::make_unique<CurveEval>();
- for (const int i : curve.splines().index_range()) {
- if (selection[i]) {
- switch (output_type) {
- case GEO_NODE_SPLINE_TYPE_POLY:
- new_curve->add_spline(convert_to_poly_spline(*curve.splines()[i]));
- break;
- case GEO_NODE_SPLINE_TYPE_BEZIER:
- new_curve->add_spline(convert_to_bezier(*curve.splines()[i], params));
- break;
- case GEO_NODE_SPLINE_TYPE_NURBS:
- new_curve->add_spline(convert_to_nurbs(*curve.splines()[i]));
- break;
- }
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ if (!geometry_set.has_curve()) {
+ return;
}
- else {
- new_curve->add_spline(curve.splines()[i]->copy());
+
+ const CurveComponent *curve_component = geometry_set.get_component_for_read<CurveComponent>();
+ const CurveEval &curve = *curve_component->get_for_read();
+ GeometryComponentFieldContext field_context{*curve_component, ATTR_DOMAIN_CURVE};
+ const int domain_size = curve_component->attribute_domain_size(ATTR_DOMAIN_CURVE);
+
+ fn::FieldEvaluator selection_evaluator{field_context, domain_size};
+ selection_evaluator.add(selection_field);
+ selection_evaluator.evaluate();
+ const VArray<bool> &selection = selection_evaluator.get_evaluated<bool>(0);
+
+ std::unique_ptr<CurveEval> new_curve = std::make_unique<CurveEval>();
+ for (const int i : curve.splines().index_range()) {
+ if (selection[i]) {
+ switch (output_type) {
+ case GEO_NODE_SPLINE_TYPE_POLY:
+ new_curve->add_spline(convert_to_poly_spline(*curve.splines()[i]));
+ break;
+ case GEO_NODE_SPLINE_TYPE_BEZIER:
+ new_curve->add_spline(convert_to_bezier(*curve.splines()[i], params));
+ break;
+ case GEO_NODE_SPLINE_TYPE_NURBS:
+ new_curve->add_spline(convert_to_nurbs(*curve.splines()[i]));
+ break;
+ }
+ }
+ else {
+ new_curve->add_spline(curve.splines()[i]->copy());
+ }
}
- }
+ new_curve->attributes = curve.attributes;
+ geometry_set.replace_curve(new_curve.release());
+ });
- new_curve->attributes = curve.attributes;
- params.set_output("Curve", GeometrySet::create_with_curve(new_curve.release()));
+ params.set_output("Curve", std::move(geometry_set));
}
} // namespace blender::nodes
@@ -288,7 +294,7 @@ void register_node_type_geo_curve_spline_type()
{
static bNodeType ntype;
geo_node_type_base(
- &ntype, GEO_NODE_LEGACY_CURVE_SPLINE_TYPE, "Set Spline Type", NODE_CLASS_GEOMETRY, 0);
+ &ntype, GEO_NODE_CURVE_SPLINE_TYPE, "Set Spline Type", NODE_CLASS_GEOMETRY, 0);
ntype.declare = blender::nodes::geo_node_curve_spline_type_declare;
ntype.geometry_node_execute = blender::nodes::geo_node_curve_spline_type_exec;
node_type_init(&ntype, blender::nodes::geo_node_curve_spline_type_init);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc
index 0522f2b8981..34997c66cbb 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc
@@ -34,35 +34,10 @@ namespace blender::nodes {
static void geo_node_curve_subdivide_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::String>("Cuts");
- b.add_input<decl::Int>("Cuts", "Cuts_001").default_value(1).min(0).max(1000);
+ b.add_input<decl::Int>("Cuts").default_value(1).min(0).max(1000).supports_field();
b.add_output<decl::Geometry>("Geometry");
}
-static void geo_node_curve_subdivide_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
-{
- uiLayoutSetPropSep(layout, true);
- uiLayoutSetPropDecorate(layout, false);
- uiItemR(layout, ptr, "cuts_type", 0, IFACE_("Cuts"), ICON_NONE);
-}
-
-static void geo_node_curve_subdivide_init(bNodeTree *UNUSED(tree), bNode *node)
-{
- NodeGeometryCurveSubdivide *data = (NodeGeometryCurveSubdivide *)MEM_callocN(
- sizeof(NodeGeometryCurveSubdivide), __func__);
-
- data->cuts_type = GEO_NODE_ATTRIBUTE_INPUT_INTEGER;
- node->storage = data;
-}
-
-static void geo_node_curve_subdivide_update(bNodeTree *UNUSED(ntree), bNode *node)
-{
- NodeGeometryPointTranslate &node_storage = *(NodeGeometryPointTranslate *)node->storage;
-
- update_attribute_input_socket_availabilities(
- *node, "Cuts", (GeometryNodeAttributeInputMode)node_storage.input_type);
-}
-
static Array<int> get_subdivided_offsets(const Spline &spline,
const VArray<int> &cuts,
const int spline_offset)
@@ -350,25 +325,30 @@ static std::unique_ptr<CurveEval> subdivide_curve(const CurveEval &input_curve,
static void geo_node_subdivide_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ Field<int> cuts_field = params.extract_input<Field<int>>("Cuts");
- geometry_set = bke::geometry_set_realize_instances(geometry_set);
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ if (!geometry_set.has_curve()) {
+ return;
+ }
- if (!geometry_set.has_curve()) {
- params.set_output("Geometry", geometry_set);
- return;
- }
+ const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>();
+ GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
+ const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT);
- const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>();
- GVArray_Typed<int> cuts = params.get_input_attribute<int>(
- "Cuts", component, ATTR_DOMAIN_POINT, 0);
- if (cuts->is_single() && cuts->get_internal_single() < 1) {
- params.set_output("Geometry", geometry_set);
- return;
- }
+ fn::FieldEvaluator evaluator{field_context, domain_size};
+ evaluator.add(cuts_field);
+ evaluator.evaluate();
+ const VArray<int> &cuts = evaluator.get_evaluated<int>(0);
- std::unique_ptr<CurveEval> output_curve = subdivide_curve(*component.get_for_read(), *cuts);
+ if (cuts.is_single() && cuts.get_internal_single() < 1) {
+ return;
+ }
- params.set_output("Geometry", GeometrySet::create_with_curve(output_curve.release()));
+ std::unique_ptr<CurveEval> output_curve = subdivide_curve(*component.get_for_read(), cuts);
+ geometry_set.replace_curve(output_curve.release());
+ });
+ params.set_output("Geometry", geometry_set);
}
} // namespace blender::nodes
@@ -377,16 +357,8 @@ void register_node_type_geo_curve_subdivide()
{
static bNodeType ntype;
- geo_node_type_base(
- &ntype, GEO_NODE_LEGACY_CURVE_SUBDIVIDE, "Curve Subdivide", NODE_CLASS_GEOMETRY, 0);
+ geo_node_type_base(&ntype, GEO_NODE_CURVE_SUBDIVIDE, "Curve Subdivide", NODE_CLASS_GEOMETRY, 0);
ntype.declare = blender::nodes::geo_node_curve_subdivide_declare;
- ntype.draw_buttons = blender::nodes::geo_node_curve_subdivide_layout;
- node_type_storage(&ntype,
- "NodeGeometryCurveSubdivide",
- node_free_standard_storage,
- node_copy_standard_storage);
- node_type_init(&ntype, blender::nodes::geo_node_curve_subdivide_init);
- node_type_update(&ntype, blender::nodes::geo_node_curve_subdivide_update);
ntype.geometry_node_execute = blender::nodes::geo_node_subdivide_exec;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc
index 89ba635ff4b..00451946af9 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc
@@ -32,39 +32,52 @@ static void geo_node_curve_to_mesh_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Geometry>("Mesh");
}
+static void geometry_set_curve_to_mesh(GeometrySet &geometry_set, const GeometrySet &profile_set)
+{
+ const CurveEval *profile_curve = profile_set.get_curve_for_read();
+
+ if (profile_curve == nullptr) {
+ Mesh *mesh = bke::curve_to_wire_mesh(*geometry_set.get_curve_for_read());
+ geometry_set.replace_mesh(mesh);
+ }
+ else {
+ Mesh *mesh = bke::curve_to_mesh_sweep(*geometry_set.get_curve_for_read(), *profile_curve);
+ geometry_set.replace_mesh(mesh);
+ }
+}
+
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 (profile_set.has_instances()) {
+ params.error_message_add(NodeWarningType::Error,
+ TIP_("Instances are not supported in the profile input"));
+ params.set_output("Mesh", GeometrySet());
+ return;
+ }
- /* NOTE: Theoretically an "is empty" check would be more correct for errors. */
- if (profile_set.has_mesh() && !profile_set.has_curve()) {
+ if (!profile_set.has_curve() && !profile_set.is_empty()) {
params.error_message_add(NodeWarningType::Warning,
- TIP_("No curve data available in profile input"));
+ TIP_("No curve data available in the profile input"));
}
- if (!curve_set.has_curve()) {
- if (curve_set.has_mesh()) {
- params.error_message_add(NodeWarningType::Warning,
- TIP_("No curve data available in curve input"));
+ bool has_curve = false;
+ curve_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ if (geometry_set.has_curve()) {
+ has_curve = true;
+ geometry_set_curve_to_mesh(geometry_set, profile_set);
}
- params.set_output("Mesh", GeometrySet());
- return;
- }
-
- const CurveEval *profile_curve = profile_set.get_curve_for_read();
+ geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES});
+ });
- if (profile_curve == nullptr) {
- Mesh *mesh = bke::curve_to_wire_mesh(*curve_set.get_curve_for_read());
- params.set_output("Mesh", GeometrySet::create_with_mesh(mesh));
- }
- else {
- Mesh *mesh = bke::curve_to_mesh_sweep(*curve_set.get_curve_for_read(), *profile_curve);
- params.set_output("Mesh", GeometrySet::create_with_mesh(mesh));
+ if (!has_curve && !curve_set.is_empty()) {
+ params.error_message_add(NodeWarningType::Warning,
+ TIP_("No curve data available in curve input"));
}
+
+ params.set_output("Mesh", std::move(curve_set));
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc
index 2b6d25b6bf3..97043980899 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc
@@ -320,28 +320,18 @@ static void trim_bezier_spline(Spline &spline,
bezier_spline.resize(size);
}
-static void geo_node_curve_trim_exec(GeoNodeExecParams params)
+static void geometry_set_curve_trim(GeometrySet &geometry_set,
+ const GeometryNodeCurveSampleMode mode,
+ const float start,
+ const float end)
{
- const NodeGeometryCurveTrim &node_storage = *(NodeGeometryCurveTrim *)params.node().storage;
- const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode;
-
- GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
- geometry_set = bke::geometry_set_realize_instances(geometry_set);
if (!geometry_set.has_curve()) {
- params.set_output("Curve", std::move(geometry_set));
return;
}
- CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>();
- CurveEval &curve = *curve_component.get_for_write();
+ CurveEval &curve = *geometry_set.get_curve_for_write();
MutableSpan<SplinePtr> splines = curve.splines();
- const float start = mode == GEO_NODE_CURVE_SAMPLE_FACTOR ?
- params.extract_input<float>("Start") :
- params.extract_input<float>("Start_001");
- const float end = mode == GEO_NODE_CURVE_SAMPLE_FACTOR ? params.extract_input<float>("End") :
- params.extract_input<float>("End_001");
-
threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) {
for (const int i : range) {
Spline &spline = *splines[i];
@@ -382,6 +372,29 @@ static void geo_node_curve_trim_exec(GeoNodeExecParams params)
splines[i]->mark_cache_invalid();
}
});
+}
+
+static void geo_node_curve_trim_exec(GeoNodeExecParams params)
+{
+ const NodeGeometryCurveTrim &node_storage = *(NodeGeometryCurveTrim *)params.node().storage;
+ const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode;
+
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
+
+ if (mode == GEO_NODE_CURVE_SAMPLE_FACTOR) {
+ const float start = params.extract_input<float>("Start");
+ const float end = params.extract_input<float>("End");
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ geometry_set_curve_trim(geometry_set, mode, start, end);
+ });
+ }
+ else if (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) {
+ const float start = params.extract_input<float>("Start_001");
+ const float end = params.extract_input<float>("End_001");
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ geometry_set_curve_trim(geometry_set, mode, start, end);
+ });
+ }
params.set_output("Curve", std::move(geometry_set));
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc
new file mode 100644
index 00000000000..1a4c5d84dbf
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc
@@ -0,0 +1,594 @@
+/*
+ * 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_kdtree.h"
+#include "BLI_noise.hh"
+#include "BLI_rand.hh"
+#include "BLI_task.hh"
+#include "BLI_timeit.hh"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_pointcloud_types.h"
+
+#include "BKE_attribute_math.hh"
+#include "BKE_bvhutils.h"
+#include "BKE_mesh.h"
+#include "BKE_mesh_runtime.h"
+#include "BKE_mesh_sample.hh"
+#include "BKE_pointcloud.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+using blender::bke::GeometryInstanceGroup;
+
+namespace blender::nodes {
+
+static void geo_node_point_distribute_points_on_faces_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>("Geometry");
+ b.add_input<decl::Float>("Distance Min").min(0.0f).subtype(PROP_DISTANCE);
+ b.add_input<decl::Float>("Density Max").default_value(10.0f).min(0.0f);
+ b.add_input<decl::Float>("Density").default_value(10.0f).supports_field();
+ b.add_input<decl::Float>("Density Factor")
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR)
+ .supports_field();
+ b.add_input<decl::Int>("Seed");
+ b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field();
+
+ b.add_output<decl::Geometry>("Points");
+ b.add_output<decl::Vector>("Normal").field_source();
+ b.add_output<decl::Vector>("Rotation").subtype(PROP_EULER).field_source();
+ b.add_output<decl::Int>("Stable ID").field_source();
+}
+
+static void geo_node_point_distribute_points_on_faces_layout(uiLayout *layout,
+ bContext *UNUSED(C),
+ PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "distribute_method", 0, "", ICON_NONE);
+}
+
+static void node_point_distribute_points_on_faces_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ bNodeSocket *sock_distance_min = (bNodeSocket *)BLI_findlink(&node->inputs, 1);
+ bNodeSocket *sock_density_max = (bNodeSocket *)sock_distance_min->next;
+ bNodeSocket *sock_density = sock_density_max->next;
+ bNodeSocket *sock_density_factor = sock_density->next;
+ nodeSetSocketAvailability(sock_distance_min,
+ node->custom1 == GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_POISSON);
+ nodeSetSocketAvailability(sock_density_max,
+ node->custom1 == GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_POISSON);
+ nodeSetSocketAvailability(sock_density,
+ node->custom1 == GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_RANDOM);
+ nodeSetSocketAvailability(sock_density_factor,
+ node->custom1 == GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_POISSON);
+}
+
+/**
+ * Use an arbitrary choice of axes for a usable rotation attribute directly out of this node.
+ */
+static float3 normal_to_euler_rotation(const float3 normal)
+{
+ float quat[4];
+ vec_to_quat(quat, normal, OB_NEGZ, OB_POSY);
+ float3 rotation;
+ quat_to_eul(rotation, quat);
+ return rotation;
+}
+
+static void sample_mesh_surface(const Mesh &mesh,
+ const float base_density,
+ const Span<float> density_factors,
+ const int seed,
+ Vector<float3> &r_positions,
+ Vector<float3> &r_bary_coords,
+ Vector<int> &r_looptri_indices)
+{
+ const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&mesh),
+ BKE_mesh_runtime_looptri_len(&mesh)};
+
+ for (const int looptri_index : looptris.index_range()) {
+ const MLoopTri &looptri = looptris[looptri_index];
+ const int v0_loop = looptri.tri[0];
+ const int v1_loop = looptri.tri[1];
+ const int v2_loop = looptri.tri[2];
+ const int v0_index = mesh.mloop[v0_loop].v;
+ const int v1_index = mesh.mloop[v1_loop].v;
+ const int v2_index = mesh.mloop[v2_loop].v;
+ const float3 v0_pos = float3(mesh.mvert[v0_index].co);
+ const float3 v1_pos = float3(mesh.mvert[v1_index].co);
+ const float3 v2_pos = float3(mesh.mvert[v2_index].co);
+
+ float looptri_density_factor = 1.0f;
+ if (!density_factors.is_empty()) {
+ const float v0_density_factor = std::max(0.0f, density_factors[v0_loop]);
+ const float v1_density_factor = std::max(0.0f, density_factors[v1_loop]);
+ const float v2_density_factor = std::max(0.0f, density_factors[v2_loop]);
+ looptri_density_factor = (v0_density_factor + v1_density_factor + v2_density_factor) / 3.0f;
+ }
+ const float area = area_tri_v3(v0_pos, v1_pos, v2_pos);
+
+ const int looptri_seed = noise::hash(looptri_index, seed);
+ RandomNumberGenerator looptri_rng(looptri_seed);
+
+ const float points_amount_fl = area * base_density * looptri_density_factor;
+ const float add_point_probability = fractf(points_amount_fl);
+ const bool add_point = add_point_probability > looptri_rng.get_float();
+ const int point_amount = (int)points_amount_fl + (int)add_point;
+
+ for (int i = 0; i < point_amount; i++) {
+ const float3 bary_coord = looptri_rng.get_barycentric_coordinates();
+ float3 point_pos;
+ interp_v3_v3v3v3(point_pos, v0_pos, v1_pos, v2_pos, bary_coord);
+ r_positions.append(point_pos);
+ r_bary_coords.append(bary_coord);
+ r_looptri_indices.append(looptri_index);
+ }
+ }
+}
+
+BLI_NOINLINE static KDTree_3d *build_kdtree(Span<float3> positions)
+{
+ KDTree_3d *kdtree = BLI_kdtree_3d_new(positions.size());
+
+ int i_point = 0;
+ for (const float3 position : positions) {
+ BLI_kdtree_3d_insert(kdtree, i_point, position);
+ i_point++;
+ }
+
+ BLI_kdtree_3d_balance(kdtree);
+ return kdtree;
+}
+
+BLI_NOINLINE static void update_elimination_mask_for_close_points(
+ Span<float3> positions, const float minimum_distance, MutableSpan<bool> elimination_mask)
+{
+ if (minimum_distance <= 0.0f) {
+ return;
+ }
+
+ KDTree_3d *kdtree = build_kdtree(positions);
+
+ for (const int i : positions.index_range()) {
+ if (elimination_mask[i]) {
+ continue;
+ }
+
+ struct CallbackData {
+ int index;
+ MutableSpan<bool> elimination_mask;
+ } callback_data = {i, elimination_mask};
+
+ BLI_kdtree_3d_range_search_cb(
+ kdtree,
+ positions[i],
+ minimum_distance,
+ [](void *user_data, int index, const float *UNUSED(co), float UNUSED(dist_sq)) {
+ CallbackData &callback_data = *static_cast<CallbackData *>(user_data);
+ if (index != callback_data.index) {
+ callback_data.elimination_mask[index] = true;
+ }
+ return true;
+ },
+ &callback_data);
+ }
+
+ BLI_kdtree_3d_free(kdtree);
+}
+
+BLI_NOINLINE static void update_elimination_mask_based_on_density_factors(
+ const Mesh &mesh,
+ const Span<float> density_factors,
+ const Span<float3> bary_coords,
+ const Span<int> looptri_indices,
+ const MutableSpan<bool> elimination_mask)
+{
+ const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&mesh),
+ BKE_mesh_runtime_looptri_len(&mesh)};
+ for (const int i : bary_coords.index_range()) {
+ if (elimination_mask[i]) {
+ continue;
+ }
+
+ const MLoopTri &looptri = looptris[looptri_indices[i]];
+ const float3 bary_coord = bary_coords[i];
+
+ const int v0_loop = looptri.tri[0];
+ const int v1_loop = looptri.tri[1];
+ const int v2_loop = looptri.tri[2];
+
+ 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 probablity = v0_density_factor * bary_coord.x + v1_density_factor * bary_coord.y +
+ v2_density_factor * bary_coord.z;
+
+ const float hash = noise::hash_float_to_float(bary_coord);
+ if (hash > probablity) {
+ elimination_mask[i] = true;
+ }
+ }
+}
+
+BLI_NOINLINE static void eliminate_points_based_on_mask(const Span<bool> elimination_mask,
+ Vector<float3> &positions,
+ Vector<float3> &bary_coords,
+ Vector<int> &looptri_indices)
+{
+ for (int i = positions.size() - 1; i >= 0; i--) {
+ if (elimination_mask[i]) {
+ positions.remove_and_reorder(i);
+ bary_coords.remove_and_reorder(i);
+ looptri_indices.remove_and_reorder(i);
+ }
+ }
+}
+
+BLI_NOINLINE static void interpolate_attribute(const Mesh &mesh,
+ const Span<float3> bary_coords,
+ const Span<int> looptri_indices,
+ const AttributeDomain source_domain,
+ const GVArray &source_data,
+ GMutableSpan output_data)
+{
+ switch (source_domain) {
+ case ATTR_DOMAIN_POINT: {
+ bke::mesh_surface_sample::sample_point_attribute(
+ mesh, looptri_indices, bary_coords, source_data, output_data);
+ break;
+ }
+ case ATTR_DOMAIN_CORNER: {
+ bke::mesh_surface_sample::sample_corner_attribute(
+ mesh, looptri_indices, bary_coords, source_data, output_data);
+ break;
+ }
+ case ATTR_DOMAIN_FACE: {
+ bke::mesh_surface_sample::sample_face_attribute(
+ mesh, looptri_indices, source_data, output_data);
+ break;
+ }
+ default: {
+ /* Not supported currently. */
+ return;
+ }
+ }
+}
+
+BLI_NOINLINE static void propagate_existing_attributes(
+ const MeshComponent &mesh_component,
+ const Map<AttributeIDRef, AttributeKind> &attributes,
+ GeometryComponent &point_component,
+ const Span<float3> bary_coords,
+ const Span<int> looptri_indices)
+{
+ const Mesh &mesh = *mesh_component.get_for_read();
+
+ for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
+ const AttributeIDRef attribute_id = 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. */
+ OutputAttribute attribute_out = point_component.attribute_try_get_for_output_only(
+ attribute_id, ATTR_DOMAIN_POINT, output_data_type);
+ if (!attribute_out) {
+ continue;
+ }
+
+ GMutableSpan out_span = attribute_out.as_span();
+
+ std::optional<AttributeMetaData> attribute_info = point_component.attribute_get_meta_data(
+ attribute_id);
+ if (!attribute_info) {
+ continue;
+ }
+
+ const AttributeDomain source_domain = attribute_info->domain;
+ GVArrayPtr source_attribute = mesh_component.attribute_get_for_read(
+ attribute_id, source_domain, output_data_type, nullptr);
+ if (!source_attribute) {
+ continue;
+ }
+
+ interpolate_attribute(
+ mesh, bary_coords, looptri_indices, source_domain, *source_attribute, out_span);
+
+ attribute_out.save();
+ }
+}
+
+namespace {
+struct AttributeOutputs {
+ StrongAnonymousAttributeID normal_id;
+ StrongAnonymousAttributeID rotation_id;
+ StrongAnonymousAttributeID stable_id_id;
+};
+} // namespace
+
+BLI_NOINLINE static void compute_attribute_outputs(const MeshComponent &mesh_component,
+ PointCloudComponent &point_component,
+ const Span<float3> bary_coords,
+ const Span<int> looptri_indices,
+ const AttributeOutputs &attribute_outputs)
+{
+ std::optional<OutputAttribute_Typed<int>> id_attribute;
+ std::optional<OutputAttribute_Typed<float3>> normal_attribute;
+ std::optional<OutputAttribute_Typed<float3>> rotation_attribute;
+
+ MutableSpan<int> ids;
+ MutableSpan<float3> normals;
+ MutableSpan<float3> rotations;
+
+ if (attribute_outputs.stable_id_id) {
+ id_attribute.emplace(point_component.attribute_try_get_for_output_only<int>(
+ attribute_outputs.stable_id_id.get(), ATTR_DOMAIN_POINT));
+ ids = id_attribute->as_span();
+ }
+ if (attribute_outputs.normal_id) {
+ normal_attribute.emplace(point_component.attribute_try_get_for_output_only<float3>(
+ attribute_outputs.normal_id.get(), ATTR_DOMAIN_POINT));
+ normals = normal_attribute->as_span();
+ }
+ if (attribute_outputs.rotation_id) {
+ rotation_attribute.emplace(point_component.attribute_try_get_for_output_only<float3>(
+ attribute_outputs.rotation_id.get(), ATTR_DOMAIN_POINT));
+ rotations = rotation_attribute->as_span();
+ }
+
+ const Mesh &mesh = *mesh_component.get_for_read();
+ const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&mesh),
+ BKE_mesh_runtime_looptri_len(&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 float3 v0_pos = float3(mesh.mvert[v0_index].co);
+ const float3 v1_pos = float3(mesh.mvert[v1_index].co);
+ const float3 v2_pos = float3(mesh.mvert[v2_index].co);
+
+ if (!ids.is_empty()) {
+ ids[i] = noise::hash(noise::hash_float(bary_coord), looptri_index);
+ }
+ float3 normal;
+ if (!normals.is_empty() || !rotations.is_empty()) {
+ normal_tri_v3(normal, v0_pos, v1_pos, v2_pos);
+ }
+ if (!normals.is_empty()) {
+ normals[i] = normal;
+ }
+ if (!rotations.is_empty()) {
+ rotations[i] = normal_to_euler_rotation(normal);
+ }
+ }
+
+ if (id_attribute) {
+ id_attribute->save();
+ }
+ if (normal_attribute) {
+ normal_attribute->save();
+ }
+ if (rotation_attribute) {
+ rotation_attribute->save();
+ }
+}
+
+static Array<float> calc_full_density_factors_with_selection(const MeshComponent &component,
+ const Field<float> &density_field,
+ const Field<bool> &selection_field)
+{
+ const AttributeDomain attribute_domain = ATTR_DOMAIN_CORNER;
+ GeometryComponentFieldContext field_context{component, attribute_domain};
+ const int domain_size = component.attribute_domain_size(attribute_domain);
+
+ fn::FieldEvaluator selection_evaluator{field_context, domain_size};
+ selection_evaluator.add(selection_field);
+ selection_evaluator.evaluate();
+ const IndexMask selection_mask = selection_evaluator.get_evaluated_as_mask(0);
+
+ Array<float> densities(domain_size, 0.0f);
+
+ fn::FieldEvaluator density_evaluator{field_context, &selection_mask};
+ density_evaluator.add_with_destination(density_field, densities.as_mutable_span());
+ density_evaluator.evaluate();
+ return densities;
+}
+
+static void distribute_points_random(const MeshComponent &component,
+ const Field<float> &density_field,
+ const Field<bool> &selection_field,
+ const int seed,
+ Vector<float3> &positions,
+ Vector<float3> &bary_coords,
+ Vector<int> &looptri_indices)
+{
+ const Array<float> densities = calc_full_density_factors_with_selection(
+ component, density_field, selection_field);
+ const Mesh &mesh = *component.get_for_read();
+ sample_mesh_surface(mesh, 1.0f, densities, seed, positions, bary_coords, looptri_indices);
+}
+
+static void distribute_points_poisson_disk(const MeshComponent &mesh_component,
+ const float minimum_distance,
+ const float max_density,
+ const Field<float> &density_factor_field,
+ const Field<bool> &selection_field,
+ const int seed,
+ Vector<float3> &positions,
+ Vector<float3> &bary_coords,
+ Vector<int> &looptri_indices)
+{
+ const Mesh &mesh = *mesh_component.get_for_read();
+ sample_mesh_surface(mesh, max_density, {}, seed, positions, bary_coords, looptri_indices);
+
+ Array<bool> elimination_mask(positions.size(), false);
+ update_elimination_mask_for_close_points(positions, minimum_distance, elimination_mask);
+
+ const Array<float> density_factors = calc_full_density_factors_with_selection(
+ mesh_component, density_factor_field, selection_field);
+
+ update_elimination_mask_based_on_density_factors(
+ mesh, density_factors, bary_coords, looptri_indices, elimination_mask.as_mutable_span());
+
+ eliminate_points_based_on_mask(
+ elimination_mask.as_span(), positions, bary_coords, looptri_indices);
+}
+
+static void point_distribution_calculate(GeometrySet &geometry_set,
+ const Field<bool> selection_field,
+ const GeometryNodeDistributePointsOnFacesMode method,
+ const int seed,
+ const AttributeOutputs &attribute_outputs,
+ const GeoNodeExecParams &params)
+{
+ if (!geometry_set.has_mesh()) {
+ return;
+ }
+
+ const MeshComponent &mesh_component = *geometry_set.get_component_for_read<MeshComponent>();
+
+ Vector<float3> positions;
+ Vector<float3> bary_coords;
+ Vector<int> looptri_indices;
+
+ switch (method) {
+ case GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_RANDOM: {
+ const Field<float> density_field = params.get_input<Field<float>>("Density");
+ distribute_points_random(mesh_component,
+ density_field,
+ selection_field,
+ seed,
+ positions,
+ bary_coords,
+ looptri_indices);
+ break;
+ }
+ case GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_POISSON: {
+ const float minimum_distance = params.get_input<float>("Distance Min");
+ const float density_max = params.get_input<float>("Density Max");
+ const Field<float> density_factors_field = params.get_input<Field<float>>("Density Factor");
+ distribute_points_poisson_disk(mesh_component,
+ minimum_distance,
+ density_max,
+ density_factors_field,
+ selection_field,
+ seed,
+ positions,
+ bary_coords,
+ looptri_indices);
+ break;
+ }
+ }
+
+ PointCloud *pointcloud = BKE_pointcloud_new_nomain(positions.size());
+ memcpy(pointcloud->co, positions.data(), sizeof(float3) * positions.size());
+ uninitialized_fill_n(pointcloud->radius, pointcloud->totpoint, 0.05f);
+ geometry_set.replace_pointcloud(pointcloud);
+
+ PointCloudComponent &point_component =
+ geometry_set.get_component_for_write<PointCloudComponent>();
+
+ Map<AttributeIDRef, AttributeKind> attributes;
+ geometry_set.gather_attributes_for_propagation(
+ {GEO_COMPONENT_TYPE_MESH}, GEO_COMPONENT_TYPE_POINT_CLOUD, false, attributes);
+
+ /* Position is set separately. */
+ attributes.remove("position");
+
+ propagate_existing_attributes(
+ mesh_component, attributes, point_component, bary_coords, looptri_indices);
+
+ compute_attribute_outputs(
+ mesh_component, point_component, bary_coords, looptri_indices, attribute_outputs);
+}
+
+static void geo_node_point_distribute_points_on_faces_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+ const GeometryNodeDistributePointsOnFacesMode method =
+ static_cast<GeometryNodeDistributePointsOnFacesMode>(params.node().custom1);
+
+ const int seed = params.get_input<int>("Seed") * 5383843;
+ const Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
+
+ AttributeOutputs attribute_outputs;
+ if (params.output_is_required("Normal")) {
+ attribute_outputs.normal_id = StrongAnonymousAttributeID("normal");
+ }
+ if (params.output_is_required("Rotation")) {
+ attribute_outputs.rotation_id = StrongAnonymousAttributeID("rotation");
+ }
+ if (params.output_is_required("Stable ID")) {
+ attribute_outputs.stable_id_id = StrongAnonymousAttributeID("stable id");
+ }
+
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ point_distribution_calculate(
+ geometry_set, selection_field, method, seed, attribute_outputs, params);
+ /* Keep instances because the original geometry set may contain instances that are processed as
+ * well. */
+ geometry_set.keep_only({GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_INSTANCES});
+ });
+
+ params.set_output("Points", std::move(geometry_set));
+
+ if (attribute_outputs.normal_id) {
+ params.set_output(
+ "Normal",
+ AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.normal_id)));
+ }
+ if (attribute_outputs.rotation_id) {
+ params.set_output(
+ "Rotation",
+ AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.rotation_id)));
+ }
+ if (attribute_outputs.stable_id_id) {
+ params.set_output(
+ "Stable ID",
+ AnonymousAttributeFieldInput::Create<int>(std::move(attribute_outputs.stable_id_id)));
+ }
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_distribute_points_on_faces()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype,
+ GEO_NODE_DISTRIBUTE_POINTS_ON_FACES,
+ "Distribute Points on Faces",
+ NODE_CLASS_GEOMETRY,
+ 0);
+ node_type_update(&ntype, blender::nodes::node_point_distribute_points_on_faces_update);
+ node_type_size(&ntype, 170, 100, 320);
+ ntype.declare = blender::nodes::geo_node_point_distribute_points_on_faces_declare;
+ ntype.geometry_node_execute = blender::nodes::geo_node_point_distribute_points_on_faces_exec;
+ ntype.draw_buttons = blender::nodes::geo_node_point_distribute_points_on_faces_layout;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_index.cc b/source/blender/nodes/geometry/nodes/node_geo_input_index.cc
index c52ff3d448e..7fcbaf429dd 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_index.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_index.cc
@@ -20,30 +20,12 @@ namespace blender::nodes {
static void geo_node_input_index_declare(NodeDeclarationBuilder &b)
{
- b.add_output<decl::Int>("Index");
+ b.add_output<decl::Int>("Index").field_source();
}
-class IndexFieldInput final : public fn::FieldInput {
- public:
- IndexFieldInput() : FieldInput(CPPType::get<int>(), "Index")
- {
- }
-
- const GVArray *get_varray_for_context(const fn::FieldContext &UNUSED(context),
- IndexMask mask,
- ResourceScope &scope) const final
- {
- /* TODO: Investigate a similar method to IndexRange::as_span() */
- auto index_func = [](int i) { return i; };
- return &scope.construct<
- fn::GVArray_For_EmbeddedVArray<int, VArray_For_Func<int, decltype(index_func)>>>(
- mask.min_array_size(), mask.min_array_size(), index_func);
- }
-};
-
static void geo_node_input_index_exec(GeoNodeExecParams params)
{
- Field<int> index_field{std::make_shared<IndexFieldInput>()};
+ Field<int> index_field{std::make_shared<fn::IndexFieldInput>()};
params.set_output("Index", std::move(index_field));
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc b/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc
index f92086acdf0..5a2495afb9e 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc
@@ -28,7 +28,7 @@ namespace blender::nodes {
static void geo_node_input_normal_declare(NodeDeclarationBuilder &b)
{
- b.add_output<decl::Vector>("Normal");
+ b.add_output<decl::Vector>("Normal").field_source();
}
static GVArrayPtr mesh_face_normals(const Mesh &mesh,
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_position.cc b/source/blender/nodes/geometry/nodes/node_geo_input_position.cc
index 3f3457a3acb..44874259e20 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_position.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_position.cc
@@ -20,13 +20,12 @@ namespace blender::nodes {
static void geo_node_input_position_declare(NodeDeclarationBuilder &b)
{
- b.add_output<decl::Vector>("Position");
+ b.add_output<decl::Vector>("Position").field_source();
}
static void geo_node_input_position_exec(GeoNodeExecParams params)
{
- Field<float3> position_field{
- std::make_shared<AttributeFieldInput>("position", CPPType::get<float3>())};
+ Field<float3> position_field{AttributeFieldInput::Create<float3>("position")};
params.set_output("Position", std::move(position_field));
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc b/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc
new file mode 100644
index 00000000000..b5f3e1b0c28
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc
@@ -0,0 +1,109 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "node_geometry_util.hh"
+
+#include "BKE_spline.hh"
+
+namespace blender::nodes {
+
+static void geo_node_input_spline_length_declare(NodeDeclarationBuilder &b)
+{
+ b.add_output<decl::Float>("Length").field_source();
+}
+
+static const GVArray *construct_spline_length_gvarray(const CurveComponent &component,
+ const AttributeDomain domain,
+ ResourceScope &scope)
+{
+ const CurveEval *curve = component.get_for_read();
+ if (curve == nullptr) {
+ return nullptr;
+ }
+
+ Span<SplinePtr> splines = curve->splines();
+ auto length_fn = [splines](int i) { return splines[i]->length(); };
+
+ if (domain == ATTR_DOMAIN_CURVE) {
+ return &scope.construct<
+ fn::GVArray_For_EmbeddedVArray<float, VArray_For_Func<float, decltype(length_fn)>>>(
+ splines.size(), splines.size(), length_fn);
+ }
+ if (domain == ATTR_DOMAIN_POINT) {
+ GVArrayPtr length = std::make_unique<
+ fn::GVArray_For_EmbeddedVArray<float, VArray_For_Func<float, decltype(length_fn)>>>(
+ splines.size(), splines.size(), length_fn);
+ return scope
+ .add_value(component.attribute_try_adapt_domain(
+ std::move(length), ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT))
+ .get();
+ }
+
+ return nullptr;
+}
+
+class SplineLengthFieldInput final : public fn::FieldInput {
+ public:
+ SplineLengthFieldInput() : fn::FieldInput(CPPType::get<float>(), "Spline Length")
+ {
+ }
+
+ const GVArray *get_varray_for_context(const fn::FieldContext &context,
+ IndexMask UNUSED(mask),
+ ResourceScope &scope) const final
+ {
+ if (const GeometryComponentFieldContext *geometry_context =
+ dynamic_cast<const GeometryComponentFieldContext *>(&context)) {
+
+ const GeometryComponent &component = geometry_context->geometry_component();
+ const AttributeDomain domain = geometry_context->domain();
+ if (component.type() == GEO_COMPONENT_TYPE_CURVE) {
+ const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
+ return construct_spline_length_gvarray(curve_component, domain, scope);
+ }
+ }
+ return nullptr;
+ }
+
+ uint64_t hash() const override
+ {
+ /* Some random constant hash. */
+ return 3549623580;
+ }
+
+ bool is_equal_to(const fn::FieldNode &other) const override
+ {
+ return dynamic_cast<const SplineLengthFieldInput *>(&other) != nullptr;
+ }
+};
+
+static void geo_node_input_spline_length_exec(GeoNodeExecParams params)
+{
+ Field<float> length_field{std::make_shared<SplineLengthFieldInput>()};
+ params.set_output("Length", std::move(length_field));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_input_spline_length()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_INPUT_SPLINE_LENGTH, "Spline Length", NODE_CLASS_INPUT, 0);
+ ntype.geometry_node_execute = blender::nodes::geo_node_input_spline_length_exec;
+ ntype.declare = blender::nodes::geo_node_input_spline_length_declare;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc b/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc
index 68788709f1e..d690642373a 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc
@@ -24,7 +24,7 @@ namespace blender::nodes {
static void geo_node_input_tangent_declare(NodeDeclarationBuilder &b)
{
- b.add_output<decl::Vector>("Tangent");
+ b.add_output<decl::Vector>("Tangent").field_source();
}
static void calculate_bezier_tangents(const BezierSpline &spline, MutableSpan<float3> tangents)
diff --git a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc
new file mode 100644
index 00000000000..8c0c0763be8
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc
@@ -0,0 +1,207 @@
+/*
+ * 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_collection_types.h"
+
+#include "BLI_hash.h"
+#include "BLI_task.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes {
+
+static void geo_node_instance_on_points_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>("Points").description("Points to instance on");
+ b.add_input<decl::Geometry>("Instance").description("Geometry that is instanced on the points");
+ b.add_input<decl::Bool>("Pick Instance")
+ .supports_field()
+ .description("Place different instances on different points");
+ b.add_input<decl::Int>("Instance Index")
+ .implicit_field()
+ .description(
+ "Index of the instance that used for each point. This is only used when Pick Instances "
+ "is on. By default the point index is used");
+ b.add_input<decl::Vector>("Rotation")
+ .subtype(PROP_EULER)
+ .supports_field()
+ .description("Rotation of the instances");
+ b.add_input<decl::Vector>("Scale")
+ .default_value({1.0f, 1.0f, 1.0f})
+ .supports_field()
+ .description("Scale of the instances");
+ b.add_input<decl::Int>("Stable ID")
+ .supports_field()
+ .description(
+ "ID for every instance that is used to identify it over time even when the number of "
+ "instances changes. Used for example for motion blur");
+
+ b.add_output<decl::Geometry>("Instances");
+}
+
+static void add_instances_from_component(InstancesComponent &dst_component,
+ const GeometryComponent &src_component,
+ const GeometrySet &instance,
+ const GeoNodeExecParams &params)
+{
+ const AttributeDomain domain = ATTR_DOMAIN_POINT;
+ const int domain_size = src_component.attribute_domain_size(domain);
+
+ /* The initial size of the component might be non-zero when this function is called for multiple
+ * component types. */
+ const int start_len = dst_component.instances_amount();
+ dst_component.resize(start_len + domain_size);
+ MutableSpan<int> dst_handles = dst_component.instance_reference_handles().slice(start_len,
+ domain_size);
+ MutableSpan<float4x4> dst_transforms = dst_component.instance_transforms().slice(start_len,
+ domain_size);
+ MutableSpan<int> dst_stable_ids = dst_component.instance_ids().slice(start_len, domain_size);
+
+ GeometryComponentFieldContext field_context{src_component, domain};
+ FieldEvaluator field_evaluator{field_context, domain_size};
+
+ const VArray<bool> *pick_instance = nullptr;
+ const VArray<int> *indices = nullptr;
+ const VArray<float3> *rotations = nullptr;
+ const VArray<float3> *scales = nullptr;
+ field_evaluator.add(params.get_input<Field<bool>>("Pick Instance"), &pick_instance);
+ field_evaluator.add(params.get_input<Field<int>>("Instance Index"), &indices);
+ field_evaluator.add(params.get_input<Field<float3>>("Rotation"), &rotations);
+ field_evaluator.add(params.get_input<Field<float3>>("Scale"), &scales);
+ field_evaluator.add_with_destination(params.get_input<Field<int>>("Stable ID"), dst_stable_ids);
+ field_evaluator.evaluate();
+
+ GVArray_Typed<float3> positions = src_component.attribute_get_for_read<float3>(
+ "position", domain, {0, 0, 0});
+
+ const InstancesComponent *src_instances = instance.get_component_for_read<InstancesComponent>();
+
+ /* Maps handles from the source instances to handles on the new instance. */
+ Array<int> handle_mapping;
+ /* Only fill #handle_mapping when it may be used below. */
+ if (src_instances != nullptr &&
+ (!pick_instance->is_single() || pick_instance->get_internal_single())) {
+ Span<InstanceReference> src_references = src_instances->references();
+ handle_mapping.reinitialize(src_references.size());
+ for (const int src_instance_handle : src_references.index_range()) {
+ const InstanceReference &reference = src_references[src_instance_handle];
+ const int dst_instance_handle = dst_component.add_reference(reference);
+ handle_mapping[src_instance_handle] = dst_instance_handle;
+ }
+ }
+
+ const int full_instance_handle = dst_component.add_reference(instance);
+ /* Add this reference last, because it is the most likely one to be removed later on. */
+ const int empty_reference_handle = dst_component.add_reference(InstanceReference());
+
+ threading::parallel_for(IndexRange(domain_size), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ /* Compute base transform for every instances. */
+ float4x4 &dst_transform = dst_transforms[i];
+ dst_transform = float4x4::from_loc_eul_scale(
+ positions[i], rotations->get(i), scales->get(i));
+
+ /* Reference that will be used by this new instance. */
+ int dst_handle = empty_reference_handle;
+
+ const bool use_individual_instance = pick_instance->get(i);
+ if (use_individual_instance) {
+ if (src_instances != nullptr) {
+ const int src_instances_amount = src_instances->instances_amount();
+ const int original_index = indices->get(i);
+ /* Use #mod_i instead of `%` to get the desirable wrap around behavior where -1 refers to
+ * the last element. */
+ const int index = mod_i(original_index, std::max(src_instances_amount, 1));
+ if (index < src_instances_amount) {
+ /* Get the reference to the source instance. */
+ const int src_handle = src_instances->instance_reference_handles()[index];
+ dst_handle = handle_mapping[src_handle];
+
+ /* Take transforms of the source instance into account. */
+ mul_m4_m4_post(dst_transform.values,
+ src_instances->instance_transforms()[index].values);
+ }
+ }
+ }
+ else {
+ /* Use entire source geometry as instance. */
+ dst_handle = full_instance_handle;
+ }
+ /* Set properties of new instance. */
+ dst_handles[i] = dst_handle;
+ }
+ });
+
+ if (pick_instance->is_single()) {
+ if (pick_instance->get_internal_single()) {
+ if (instance.has_realized_data()) {
+ params.error_message_add(
+ NodeWarningType::Info,
+ TIP_("Realized geometry is not used when pick instances is true"));
+ }
+ }
+ }
+}
+
+static void geo_node_instance_on_points_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Points");
+ GeometrySet instance = params.get_input<GeometrySet>("Instance");
+ instance.ensure_owns_direct_data();
+
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>();
+
+ if (geometry_set.has<MeshComponent>()) {
+ add_instances_from_component(
+ instances, *geometry_set.get_component_for_read<MeshComponent>(), instance, params);
+ geometry_set.remove(GEO_COMPONENT_TYPE_MESH);
+ }
+ if (geometry_set.has<PointCloudComponent>()) {
+ add_instances_from_component(instances,
+ *geometry_set.get_component_for_read<PointCloudComponent>(),
+ instance,
+ params);
+ geometry_set.remove(GEO_COMPONENT_TYPE_POINT_CLOUD);
+ }
+ if (geometry_set.has<CurveComponent>()) {
+ add_instances_from_component(
+ instances, *geometry_set.get_component_for_read<CurveComponent>(), instance, params);
+ geometry_set.remove(GEO_COMPONENT_TYPE_CURVE);
+ }
+ /* Unused references may have been added above. Remove those now so that other nodes don't
+ * process them needlessly. */
+ instances.remove_unused_references();
+ });
+
+ params.set_output("Instances", std::move(geometry_set));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_instance_on_points()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_INSTANCE_ON_POINTS, "Instance on Points", NODE_CLASS_GEOMETRY, 0);
+ ntype.declare = blender::nodes::geo_node_instance_on_points_declare;
+ ntype.geometry_node_execute = blender::nodes::geo_node_instance_on_points_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
index 93643298f92..3e9b615f478 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
@@ -437,7 +437,7 @@ static void join_curve_components(MutableSpan<GeometrySet> src_geometry_sets, Ge
/* Retrieve attribute info before moving the splines out of the input components. */
const Map<AttributeIDRef, AttributeMetaData> info = get_final_attribute_info(
{(const GeometryComponent **)src_components.data(), src_components.size()},
- {"position", "radius", "tilt", "cyclic", "resolution"});
+ {"position", "radius", "tilt", "handle_left", "handle_right", "cyclic", "resolution"});
CurveComponent &dst_component = result.get_component_for_write<CurveComponent>();
CurveEval *dst_curve = new CurveEval();
diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc b/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc
index 43818947272..780994996ae 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc
@@ -30,7 +30,7 @@ static void geo_node_material_assign_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Geometry>("Geometry");
b.add_input<decl::Material>("Material").hide_label();
- b.add_input<decl::Bool>("Selection").default_value(true).hide_value();
+ b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field();
b.add_output<decl::Geometry>("Geometry");
}
@@ -64,23 +64,22 @@ static void geo_node_material_assign_exec(GeoNodeExecParams params)
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- geometry_set = geometry_set_realize_instances(geometry_set);
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ if (geometry_set.has<MeshComponent>()) {
+ MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
+ Mesh *mesh = mesh_component.get_for_write();
+ if (mesh != nullptr) {
+ GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_FACE};
- 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) {
+ fn::FieldEvaluator selection_evaluator{field_context, mesh->totpoly};
+ selection_evaluator.add(selection_field);
+ selection_evaluator.evaluate();
+ const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0);
- GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_FACE};
-
- fn::FieldEvaluator selection_evaluator{field_context, mesh->totpoly};
- selection_evaluator.add(selection_field);
- selection_evaluator.evaluate();
- const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0);
-
- assign_material_to_faces(*mesh, selection, material);
+ assign_material_to_faces(*mesh, selection, material);
+ }
}
- }
+ });
params.set_output("Geometry", std::move(geometry_set));
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc b/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc
index a9c3bfc6ce0..a917434fa00 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc
@@ -40,11 +40,9 @@ static void geo_node_material_replace_exec(GeoNodeExecParams params)
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();
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ Mesh *mesh = geometry_set.get_mesh_for_write();
if (mesh != nullptr) {
for (const int i : IndexRange(mesh->totcol)) {
if (mesh->mat[i] == old_material) {
@@ -52,7 +50,7 @@ static void geo_node_material_replace_exec(GeoNodeExecParams params)
}
}
}
- }
+ });
params.set_output("Geometry", std::move(geometry_set));
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc
index 22c24e34314..9d4533b9bda 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc
@@ -31,7 +31,7 @@ namespace blender::nodes {
static void geo_node_material_selection_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Material>("Material").hide_label(true);
- b.add_output<decl::Bool>("Selection");
+ b.add_output<decl::Bool>("Selection").field_source();
}
static void select_mesh_by_material(const Mesh &mesh,
@@ -100,13 +100,16 @@ class MaterialSelectionFieldInput final : public fn::FieldInput {
uint64_t hash() const override
{
- /* Some random constant hash. */
- return 91619626;
+ return get_default_hash(material_);
}
bool is_equal_to(const fn::FieldNode &other) const override
{
- return dynamic_cast<const MaterialSelectionFieldInput *>(&other) != nullptr;
+ if (const MaterialSelectionFieldInput *other_material_selection =
+ dynamic_cast<const MaterialSelectionFieldInput *>(&other)) {
+ return material_ == other_material_selection->material_;
+ }
+ return false;
}
};
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc
index 0d58476fc58..059e6e8680c 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
@@ -29,13 +29,38 @@ namespace blender::nodes {
static void geo_node_mesh_primitive_cone_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Int>("Vertices").default_value(32).min(3);
+ b.add_input<decl::Int>("Vertices").default_value(32).min(3).max(512);
+ b.add_input<decl::Int>("Side Segments").default_value(1).min(1).max(512);
+ b.add_input<decl::Int>("Fill Segments").default_value(1).min(1).max(512);
b.add_input<decl::Float>("Radius Top").min(0.0f).subtype(PROP_DISTANCE);
b.add_input<decl::Float>("Radius Bottom").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE);
b.add_input<decl::Float>("Depth").default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE);
b.add_output<decl::Geometry>("Geometry");
}
+static void geo_node_mesh_primitive_cone_init(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeGeometryMeshCone *node_storage = (NodeGeometryMeshCone *)MEM_callocN(
+ sizeof(NodeGeometryMeshCone), __func__);
+
+ node_storage->fill_type = GEO_NODE_MESH_CIRCLE_FILL_NGON;
+
+ node->storage = node_storage;
+}
+
+static void geo_node_mesh_primitive_cone_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ bNodeSocket *vertices_socket = (bNodeSocket *)node->inputs.first;
+ bNodeSocket *rings_socket = vertices_socket->next;
+ bNodeSocket *fill_subdiv_socket = rings_socket->next;
+
+ const NodeGeometryMeshCone &storage = *(const NodeGeometryMeshCone *)node->storage;
+ const GeometryNodeMeshCircleFillType fill_type =
+ static_cast<const GeometryNodeMeshCircleFillType>(storage.fill_type);
+ const bool has_fill = fill_type != GEO_NODE_MESH_CIRCLE_FILL_NONE;
+ nodeSetSocketAvailability(fill_subdiv_socket, has_fill);
+}
+
static void geo_node_mesh_primitive_cone_layout(uiLayout *layout,
bContext *UNUSED(C),
PointerRNA *ptr)
@@ -45,493 +70,632 @@ static void geo_node_mesh_primitive_cone_layout(uiLayout *layout,
uiItemR(layout, ptr, "fill_type", 0, nullptr, ICON_NONE);
}
-static void geo_node_mesh_primitive_cone_init(bNodeTree *UNUSED(ntree), bNode *node)
+struct ConeConfig {
+ float radius_top;
+ float radius_bottom;
+ float height;
+ int circle_segments;
+ int side_segments;
+ int fill_segments;
+ GeometryNodeMeshCircleFillType fill_type;
+
+ bool top_is_point;
+ bool bottom_is_point;
+ /* The cone tip and a triangle fan filling are topologically identical.
+ * This simplifies the logic in some cases. */
+ bool top_has_center_vert;
+ bool bottom_has_center_vert;
+
+ /* Helpful quantities. */
+ int tot_quad_rings;
+ int tot_edge_rings;
+ int tot_verts;
+ int tot_edges;
+
+ /* Helpful vertex indices. */
+ int first_vert;
+ int first_ring_verts_start;
+ int last_ring_verts_start;
+ int last_vert;
+
+ /* Helpful edge indices. */
+ int first_ring_edges_start;
+ int last_ring_edges_start;
+ int last_fan_edges_start;
+ int last_edge;
+
+ ConeConfig(float radius_top,
+ float radius_bottom,
+ float depth,
+ int circle_segments,
+ int side_segments,
+ int fill_segments,
+ GeometryNodeMeshCircleFillType fill_type)
+ : radius_top(radius_top),
+ radius_bottom(radius_bottom),
+ height(0.5f * depth),
+ circle_segments(circle_segments),
+ side_segments(side_segments),
+ fill_segments(fill_segments),
+ fill_type(fill_type)
+ {
+ this->top_is_point = this->radius_top == 0.0f;
+ this->bottom_is_point = this->radius_bottom == 0.0f;
+ this->top_has_center_vert = this->top_is_point ||
+ this->fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN;
+ this->bottom_has_center_vert = this->bottom_is_point ||
+ this->fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN;
+
+ this->tot_quad_rings = this->calculate_total_quad_rings();
+ this->tot_edge_rings = this->calculate_total_edge_rings();
+ this->tot_verts = this->calculate_total_verts();
+ this->tot_edges = this->calculate_total_edges();
+
+ this->first_vert = 0;
+ this->first_ring_verts_start = this->top_has_center_vert ? 1 : first_vert;
+ this->last_vert = this->tot_verts - 1;
+ this->last_ring_verts_start = this->last_vert - this->circle_segments;
+
+ this->first_ring_edges_start = this->top_has_center_vert ? this->circle_segments : 0;
+ this->last_ring_edges_start = this->first_ring_edges_start +
+ this->tot_quad_rings * this->circle_segments * 2;
+ this->last_fan_edges_start = this->tot_edges - this->circle_segments;
+ this->last_edge = this->tot_edges - 1;
+ }
+
+ private:
+ int calculate_total_quad_rings();
+ int calculate_total_edge_rings();
+ int calculate_total_verts();
+ int calculate_total_edges();
+
+ public:
+ int get_tot_corners() const;
+ int get_tot_faces() const;
+};
+
+int ConeConfig::calculate_total_quad_rings()
{
- NodeGeometryMeshCone *node_storage = (NodeGeometryMeshCone *)MEM_callocN(
- sizeof(NodeGeometryMeshCone), __func__);
+ if (top_is_point && bottom_is_point) {
+ return 0;
+ }
- node_storage->fill_type = GEO_NODE_MESH_CIRCLE_FILL_NGON;
+ int quad_rings = 0;
- node->storage = node_storage;
+ if (!top_is_point) {
+ quad_rings += fill_segments - 1;
+ }
+
+ quad_rings += (!top_is_point && !bottom_is_point) ? side_segments : (side_segments - 1);
+
+ if (!bottom_is_point) {
+ quad_rings += fill_segments - 1;
+ }
+
+ return quad_rings;
}
-static int vert_total(const GeometryNodeMeshCircleFillType fill_type,
- const int verts_num,
- const bool top_is_point,
- const bool bottom_is_point)
+int ConeConfig::calculate_total_edge_rings()
{
- int vert_total = 0;
+ if (top_is_point && bottom_is_point) {
+ return 0;
+ }
+
+ int edge_rings = 0;
+
if (!top_is_point) {
- vert_total += verts_num;
- if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
- vert_total++;
- }
+ edge_rings += fill_segments;
}
- else {
+
+ edge_rings += side_segments - 1;
+
+ if (!bottom_is_point) {
+ edge_rings += fill_segments;
+ }
+
+ return edge_rings;
+}
+
+int ConeConfig::calculate_total_verts()
+{
+ if (top_is_point && bottom_is_point) {
+ return side_segments + 1;
+ }
+
+ int vert_total = 0;
+
+ if (top_has_center_vert) {
vert_total++;
}
+
+ if (!top_is_point) {
+ vert_total += circle_segments * fill_segments;
+ }
+
+ vert_total += circle_segments * (side_segments - 1);
+
if (!bottom_is_point) {
- vert_total += verts_num;
- if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
- vert_total++;
- }
+ vert_total += circle_segments * fill_segments;
}
- else {
+
+ if (bottom_has_center_vert) {
vert_total++;
}
return vert_total;
}
-static int edge_total(const GeometryNodeMeshCircleFillType fill_type,
- const int verts_num,
- const bool top_is_point,
- const bool bottom_is_point)
+int ConeConfig::calculate_total_edges()
{
if (top_is_point && bottom_is_point) {
- return 1;
+ return side_segments;
}
int edge_total = 0;
- if (!top_is_point) {
- edge_total += verts_num;
- if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
- edge_total += verts_num;
- }
+ if (top_has_center_vert) {
+ edge_total += circle_segments;
}
- edge_total += verts_num;
+ edge_total += circle_segments * (tot_quad_rings * 2 + 1);
- if (!bottom_is_point) {
- edge_total += verts_num;
- if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
- edge_total += verts_num;
- }
+ if (bottom_has_center_vert) {
+ edge_total += circle_segments;
}
return edge_total;
}
-static int corner_total(const GeometryNodeMeshCircleFillType fill_type,
- const int verts_num,
- const bool top_is_point,
- const bool bottom_is_point)
+int ConeConfig::get_tot_corners() const
{
if (top_is_point && bottom_is_point) {
return 0;
}
int corner_total = 0;
- if (!top_is_point) {
- if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
- corner_total += verts_num;
- }
- else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
- corner_total += verts_num * 3;
- }
- }
- if (!top_is_point && !bottom_is_point) {
- corner_total += verts_num * 4;
+ if (top_has_center_vert) {
+ corner_total += (circle_segments * 3);
}
- else {
- corner_total += verts_num * 3;
+ else if (!top_is_point && fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
+ corner_total += circle_segments;
}
- if (!bottom_is_point) {
- if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
- corner_total += verts_num;
- }
- else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
- corner_total += verts_num * 3;
- }
+ corner_total += tot_quad_rings * (circle_segments * 4);
+
+ if (bottom_has_center_vert) {
+ corner_total += (circle_segments * 3);
+ }
+ else if (!bottom_is_point && fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
+ corner_total += circle_segments;
}
return corner_total;
}
-static int face_total(const GeometryNodeMeshCircleFillType fill_type,
- const int verts_num,
- const bool top_is_point,
- const bool bottom_is_point)
+int ConeConfig::get_tot_faces() const
{
if (top_is_point && bottom_is_point) {
return 0;
}
int face_total = 0;
- if (!top_is_point) {
- if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
- face_total++;
- }
- else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
- face_total += verts_num;
- }
+ if (top_has_center_vert) {
+ face_total += circle_segments;
+ }
+ else if (!top_is_point && fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
+ face_total++;
}
- face_total += verts_num;
+ face_total += tot_quad_rings * circle_segments;
- if (!bottom_is_point) {
- if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
- face_total++;
- }
- else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
- face_total += verts_num;
- }
+ if (bottom_has_center_vert) {
+ face_total += circle_segments;
+ }
+ else if (!bottom_is_point && fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
+ face_total++;
}
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)
+static void calculate_cone_vertices(const MutableSpan<MVert> &verts, const ConeConfig &config)
{
- MeshComponent mesh_component;
- mesh_component.replace(mesh, GeometryOwnershipType::Editable);
- OutputAttribute_Typed<float2> uv_attribute =
- mesh_component.attribute_try_get_for_output_only<float2>("uv_map", ATTR_DOMAIN_CORNER);
- MutableSpan<float2> uvs = uv_attribute.as_span();
-
- Array<float2> circle(verts_num);
+ Array<float2> circle(config.circle_segments);
+ const float angle_delta = 2.0f * (M_PI / static_cast<float>(config.circle_segments));
float angle = 0.0f;
- const float angle_delta = 2.0f * M_PI / static_cast<float>(verts_num);
- for (const int i : IndexRange(verts_num)) {
- circle[i].x = std::cos(angle) * 0.225f + 0.25f;
- circle[i].y = std::sin(angle) * 0.225f + 0.25f;
+ for (const int i : IndexRange(config.circle_segments)) {
+ circle[i].x = std::cos(angle);
+ circle[i].y = std::sin(angle);
angle += angle_delta;
}
- int loop_index = 0;
- if (!top_is_point) {
- if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
- for (const int i : IndexRange(verts_num)) {
- uvs[loop_index++] = circle[i];
- }
- }
- else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
- for (const int i : IndexRange(verts_num)) {
- uvs[loop_index++] = circle[i];
- uvs[loop_index++] = circle[(i + 1) % verts_num];
- uvs[loop_index++] = float2(0.25f, 0.25f);
- }
- }
- }
+ int vert_index = 0;
- /* Create side corners and faces. */
- if (!top_is_point && !bottom_is_point) {
- const float bottom = (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NONE) ? 0.0f : 0.5f;
- /* Quads connect the top and bottom. */
- for (const int i : IndexRange(verts_num)) {
- const float vert = static_cast<float>(i);
- uvs[loop_index++] = float2(vert / verts_num, bottom);
- uvs[loop_index++] = float2(vert / verts_num, 1.0f);
- uvs[loop_index++] = float2((vert + 1.0f) / verts_num, 1.0f);
- uvs[loop_index++] = float2((vert + 1.0f) / verts_num, bottom);
- }
+ /* Top cone tip or triangle fan center. */
+ if (config.top_has_center_vert) {
+ copy_v3_fl3(verts[vert_index++].co, 0.0f, 0.0f, config.height);
}
- else {
- /* Triangles connect the top and bottom section. */
- if (!top_is_point) {
- for (const int i : IndexRange(verts_num)) {
- uvs[loop_index++] = circle[i] + float2(0.5f, 0.0f);
- uvs[loop_index++] = float2(0.75f, 0.25f);
- uvs[loop_index++] = circle[(i + 1) % verts_num] + float2(0.5f, 0.0f);
- }
- }
- else {
- BLI_assert(!bottom_is_point);
- for (const int i : IndexRange(verts_num)) {
- uvs[loop_index++] = circle[i];
- uvs[loop_index++] = circle[(i + 1) % verts_num];
- uvs[loop_index++] = float2(0.25f, 0.25f);
+
+ /* Top fill including the outer edge of the fill. */
+ if (!config.top_is_point) {
+ const float top_fill_radius_delta = config.radius_top /
+ static_cast<float>(config.fill_segments);
+ for (const int i : IndexRange(config.fill_segments)) {
+ const float top_fill_radius = top_fill_radius_delta * (i + 1);
+ for (const int j : IndexRange(config.circle_segments)) {
+ const float x = circle[j].x * top_fill_radius;
+ const float y = circle[j].y * top_fill_radius;
+ copy_v3_fl3(verts[vert_index++].co, x, y, config.height);
}
}
}
- /* Create bottom corners and faces. */
- if (!bottom_is_point) {
- if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
- for (const int i : IndexRange(verts_num)) {
- /* Go backwards because of reversed face normal. */
- uvs[loop_index++] = circle[verts_num - 1 - i] + float2(0.5f, 0.0f);
- }
+ /* Rings along the side. */
+ const float side_radius_delta = (config.radius_bottom - config.radius_top) /
+ static_cast<float>(config.side_segments);
+ const float height_delta = 2.0f * config.height / static_cast<float>(config.side_segments);
+ for (const int i : IndexRange(config.side_segments - 1)) {
+ const float ring_radius = config.radius_top + (side_radius_delta * (i + 1));
+ const float ring_height = config.height - (height_delta * (i + 1));
+ for (const int j : IndexRange(config.circle_segments)) {
+ const float x = circle[j].x * ring_radius;
+ const float y = circle[j].y * ring_radius;
+ copy_v3_fl3(verts[vert_index++].co, x, y, ring_height);
}
- else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
- for (const int i : IndexRange(verts_num)) {
- uvs[loop_index++] = circle[i] + float2(0.5f, 0.0f);
- uvs[loop_index++] = float2(0.75f, 0.25f);
- uvs[loop_index++] = circle[(i + 1) % verts_num] + float2(0.5f, 0.0f);
+ }
+
+ /* Bottom fill including the outer edge of the fill. */
+ if (!config.bottom_is_point) {
+ const float bottom_fill_radius_delta = config.radius_bottom /
+ static_cast<float>(config.fill_segments);
+ for (const int i : IndexRange(config.fill_segments)) {
+ const float bottom_fill_radius = config.radius_bottom - (i * bottom_fill_radius_delta);
+ for (const int j : IndexRange(config.circle_segments)) {
+ const float x = circle[j].x * bottom_fill_radius;
+ const float y = circle[j].y * bottom_fill_radius;
+ copy_v3_fl3(verts[vert_index++].co, x, y, -config.height);
}
}
}
- uv_attribute.save();
+ /* Bottom cone tip or triangle fan center. */
+ if (config.bottom_has_center_vert) {
+ copy_v3_fl3(verts[vert_index++].co, 0.0f, 0.0f, -config.height);
+ }
}
-Mesh *create_cylinder_or_cone_mesh(const float radius_top,
- const float radius_bottom,
- const float depth,
- const int verts_num,
- const GeometryNodeMeshCircleFillType fill_type)
+static void calculate_cone_edges(const MutableSpan<MEdge> &edges, const ConeConfig &config)
{
- const bool top_is_point = radius_top == 0.0f;
- const bool bottom_is_point = radius_bottom == 0.0f;
- const float height = depth * 0.5f;
- /* Handle the case of a line / single point before everything else to avoid
- * the need to check for it later. */
- if (top_is_point && bottom_is_point) {
- const bool single_vertex = height == 0.0f;
- Mesh *mesh = BKE_mesh_new_nomain(single_vertex ? 1 : 2, single_vertex ? 0 : 1, 0, 0, 0);
- copy_v3_v3(mesh->mvert[0].co, float3(0.0f, 0.0f, height));
- if (single_vertex) {
- const short up[3] = {0, 0, SHRT_MAX};
- copy_v3_v3_short(mesh->mvert[0].no, up);
- return mesh;
- }
- copy_v3_v3(mesh->mvert[1].co, float3(0.0f, 0.0f, -height));
- mesh->medge[0].v1 = 0;
- mesh->medge[0].v2 = 1;
- mesh->medge[0].flag |= ME_LOOSEEDGE;
- BKE_mesh_normals_tag_dirty(mesh);
- return mesh;
- }
-
- Mesh *mesh = BKE_mesh_new_nomain(
- vert_total(fill_type, verts_num, top_is_point, bottom_is_point),
- edge_total(fill_type, verts_num, top_is_point, bottom_is_point),
- 0,
- corner_total(fill_type, verts_num, top_is_point, bottom_is_point),
- face_total(fill_type, verts_num, top_is_point, bottom_is_point));
- BKE_id_material_eval_ensure_default_slot(&mesh->id);
- MutableSpan<MVert> verts{mesh->mvert, mesh->totvert};
- MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop};
- MutableSpan<MEdge> edges{mesh->medge, mesh->totedge};
- MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly};
-
- /* Calculate vertex positions. */
- const int top_verts_start = 0;
- const int bottom_verts_start = top_verts_start + (!top_is_point ? verts_num : 1);
- const float angle_delta = 2.0f * (M_PI / static_cast<float>(verts_num));
- for (const int i : IndexRange(verts_num)) {
- const float angle = i * angle_delta;
- const float x = std::cos(angle);
- const float y = std::sin(angle);
- if (!top_is_point) {
- copy_v3_v3(verts[top_verts_start + i].co, float3(x * radius_top, y * radius_top, height));
- }
- if (!bottom_is_point) {
- copy_v3_v3(verts[bottom_verts_start + i].co,
- float3(x * radius_bottom, y * radius_bottom, -height));
+ int edge_index = 0;
+
+ /* Edges for top cone tip or triangle fan */
+ if (config.top_has_center_vert) {
+ for (const int i : IndexRange(config.circle_segments)) {
+ MEdge &edge = edges[edge_index++];
+ edge.v1 = config.first_vert;
+ edge.v2 = config.first_ring_verts_start + i;
+ edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
}
}
- if (top_is_point) {
- copy_v3_v3(verts[top_verts_start].co, float3(0.0f, 0.0f, height));
- }
- if (bottom_is_point) {
- copy_v3_v3(verts[bottom_verts_start].co, float3(0.0f, 0.0f, -height));
- }
- /* Add center vertices for the triangle fans at the end. */
- const int top_center_vert_index = bottom_verts_start + (bottom_is_point ? 1 : verts_num);
- const int bottom_center_vert_index = top_center_vert_index + (top_is_point ? 0 : 1);
- if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
- if (!top_is_point) {
- copy_v3_v3(verts[top_center_vert_index].co, float3(0.0f, 0.0f, height));
+ /* Rings and connecting edges between the rings. */
+ for (const int i : IndexRange(config.tot_edge_rings)) {
+ const int this_ring_vert_start = config.first_ring_verts_start + (i * config.circle_segments);
+ const int next_ring_vert_start = this_ring_vert_start + config.circle_segments;
+ /* Edge rings. */
+ for (const int j : IndexRange(config.circle_segments)) {
+ MEdge &edge = edges[edge_index++];
+ edge.v1 = this_ring_vert_start + j;
+ edge.v2 = this_ring_vert_start + ((j + 1) % config.circle_segments);
+ edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
}
- if (!bottom_is_point) {
- copy_v3_v3(verts[bottom_center_vert_index].co, float3(0.0f, 0.0f, -height));
+ if (i == config.tot_edge_rings - 1) {
+ /* There is one fewer ring of connecting edges. */
+ break;
}
- }
-
- /* Create top edges. */
- const int top_edges_start = 0;
- const int top_fan_edges_start = (!top_is_point &&
- fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) ?
- top_edges_start + verts_num :
- top_edges_start;
- if (!top_is_point) {
- for (const int i : IndexRange(verts_num)) {
- MEdge &edge = edges[top_edges_start + i];
- edge.v1 = top_verts_start + i;
- edge.v2 = top_verts_start + (i + 1) % verts_num;
+ /* Connecting edges. */
+ for (const int j : IndexRange(config.circle_segments)) {
+ MEdge &edge = edges[edge_index++];
+ edge.v1 = this_ring_vert_start + j;
+ edge.v2 = next_ring_vert_start + j;
edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
}
- if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
- for (const int i : IndexRange(verts_num)) {
- MEdge &edge = edges[top_fan_edges_start + i];
- edge.v1 = top_center_vert_index;
- edge.v2 = top_verts_start + i;
- edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
- }
- }
}
- /* Create connecting edges. */
- const int connecting_edges_start = top_fan_edges_start + (!top_is_point ? verts_num : 0);
- for (const int i : IndexRange(verts_num)) {
- MEdge &edge = edges[connecting_edges_start + i];
- edge.v1 = top_verts_start + (!top_is_point ? i : 0);
- edge.v2 = bottom_verts_start + (!bottom_is_point ? i : 0);
- edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
- }
-
- /* Create bottom edges. */
- const int bottom_edges_start = connecting_edges_start + verts_num;
- const int bottom_fan_edges_start = (!bottom_is_point &&
- fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) ?
- bottom_edges_start + verts_num :
- bottom_edges_start;
- if (!bottom_is_point) {
- for (const int i : IndexRange(verts_num)) {
- MEdge &edge = edges[bottom_edges_start + i];
- edge.v1 = bottom_verts_start + i;
- edge.v2 = bottom_verts_start + (i + 1) % verts_num;
+ /* Edges for bottom triangle fan or tip. */
+ if (config.bottom_has_center_vert) {
+ for (const int i : IndexRange(config.circle_segments)) {
+ MEdge &edge = edges[edge_index++];
+ edge.v1 = config.last_ring_verts_start + i;
+ edge.v2 = config.last_vert;
edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
}
- if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
- for (const int i : IndexRange(verts_num)) {
- MEdge &edge = edges[bottom_fan_edges_start + i];
- edge.v1 = bottom_center_vert_index;
- edge.v2 = bottom_verts_start + i;
- edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
- }
- }
}
+}
- /* Create top corners and faces. */
+static void calculate_cone_faces(const MutableSpan<MLoop> &loops,
+ const MutableSpan<MPoly> &polys,
+ const ConeConfig &config)
+{
int loop_index = 0;
int poly_index = 0;
- if (!top_is_point) {
- if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
+
+ if (config.top_has_center_vert) {
+ /* Top cone tip or center triangle fan in the fill. */
+ const int top_center_vert = 0;
+ const int top_fan_edges_start = 0;
+
+ for (const int i : IndexRange(config.circle_segments)) {
MPoly &poly = polys[poly_index++];
poly.loopstart = loop_index;
- poly.totloop = verts_num;
+ poly.totloop = 3;
- for (const int i : IndexRange(verts_num)) {
- MLoop &loop = loops[loop_index++];
- loop.v = top_verts_start + i;
- loop.e = top_edges_start + i;
- }
+ MLoop &loop_a = loops[loop_index++];
+ loop_a.v = config.first_ring_verts_start + i;
+ loop_a.e = config.first_ring_edges_start + i;
+ MLoop &loop_b = loops[loop_index++];
+ loop_b.v = config.first_ring_verts_start + ((i + 1) % config.circle_segments);
+ loop_b.e = top_fan_edges_start + ((i + 1) % config.circle_segments);
+ MLoop &loop_c = loops[loop_index++];
+ loop_c.v = top_center_vert;
+ loop_c.e = top_fan_edges_start + i;
}
- else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
- for (const int i : IndexRange(verts_num)) {
+ }
+ else if (config.fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
+ /* Center n-gon in the fill. */
+ MPoly &poly = polys[poly_index++];
+ poly.loopstart = loop_index;
+ poly.totloop = config.circle_segments;
+ for (const int i : IndexRange(config.circle_segments)) {
+ MLoop &loop = loops[loop_index++];
+ loop.v = i;
+ loop.e = i;
+ }
+ }
+
+ /* Quads connect one edge ring to the next one. */
+ if (config.tot_quad_rings > 0) {
+ for (const int i : IndexRange(config.tot_quad_rings)) {
+ const int this_ring_vert_start = config.first_ring_verts_start +
+ (i * config.circle_segments);
+ const int next_ring_vert_start = this_ring_vert_start + config.circle_segments;
+
+ const int this_ring_edges_start = config.first_ring_edges_start +
+ (i * 2 * config.circle_segments);
+ const int next_ring_edges_start = this_ring_edges_start + (2 * config.circle_segments);
+ const int ring_connections_start = this_ring_edges_start + config.circle_segments;
+
+ for (const int j : IndexRange(config.circle_segments)) {
MPoly &poly = polys[poly_index++];
poly.loopstart = loop_index;
- poly.totloop = 3;
+ poly.totloop = 4;
MLoop &loop_a = loops[loop_index++];
- loop_a.v = top_verts_start + i;
- loop_a.e = top_edges_start + i;
+ loop_a.v = this_ring_vert_start + j;
+ loop_a.e = ring_connections_start + j;
MLoop &loop_b = loops[loop_index++];
- loop_b.v = top_verts_start + (i + 1) % verts_num;
- loop_b.e = top_fan_edges_start + (i + 1) % verts_num;
+ loop_b.v = next_ring_vert_start + j;
+ loop_b.e = next_ring_edges_start + j;
MLoop &loop_c = loops[loop_index++];
- loop_c.v = top_center_vert_index;
- loop_c.e = top_fan_edges_start + i;
+ loop_c.v = next_ring_vert_start + ((j + 1) % config.circle_segments);
+ loop_c.e = ring_connections_start + ((j + 1) % config.circle_segments);
+ MLoop &loop_d = loops[loop_index++];
+ loop_d.v = this_ring_vert_start + ((j + 1) % config.circle_segments);
+ loop_d.e = this_ring_edges_start + j;
}
}
}
- /* Create side corners and faces. */
- if (!top_is_point && !bottom_is_point) {
- /* Quads connect the top and bottom. */
- for (const int i : IndexRange(verts_num)) {
+ if (config.bottom_has_center_vert) {
+ /* Bottom cone tip or center triangle fan in the fill. */
+ for (const int i : IndexRange(config.circle_segments)) {
MPoly &poly = polys[poly_index++];
poly.loopstart = loop_index;
- poly.totloop = 4;
+ poly.totloop = 3;
MLoop &loop_a = loops[loop_index++];
- loop_a.v = top_verts_start + i;
- loop_a.e = connecting_edges_start + i;
+ loop_a.v = config.last_ring_verts_start + i;
+ loop_a.e = config.last_fan_edges_start + i;
MLoop &loop_b = loops[loop_index++];
- loop_b.v = bottom_verts_start + i;
- loop_b.e = bottom_edges_start + i;
+ loop_b.v = config.last_vert;
+ loop_b.e = config.last_fan_edges_start + (i + 1) % config.circle_segments;
MLoop &loop_c = loops[loop_index++];
- loop_c.v = bottom_verts_start + (i + 1) % verts_num;
- loop_c.e = connecting_edges_start + (i + 1) % verts_num;
- MLoop &loop_d = loops[loop_index++];
- loop_d.v = top_verts_start + (i + 1) % verts_num;
- loop_d.e = top_edges_start + i;
+ loop_c.v = config.last_ring_verts_start + (i + 1) % config.circle_segments;
+ loop_c.e = config.last_ring_edges_start + i;
}
}
- else {
- /* Triangles connect the top and bottom section. */
- if (!top_is_point) {
- for (const int i : IndexRange(verts_num)) {
- MPoly &poly = polys[poly_index++];
- poly.loopstart = loop_index;
- poly.totloop = 3;
+ else if (config.fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
+ /* Center n-gon in the fill. */
+ MPoly &poly = polys[poly_index++];
+ poly.loopstart = loop_index;
+ poly.totloop = config.circle_segments;
- MLoop &loop_a = loops[loop_index++];
- loop_a.v = top_verts_start + i;
- loop_a.e = connecting_edges_start + i;
- MLoop &loop_b = loops[loop_index++];
- loop_b.v = bottom_verts_start;
- loop_b.e = connecting_edges_start + (i + 1) % verts_num;
- MLoop &loop_c = loops[loop_index++];
- loop_c.v = top_verts_start + (i + 1) % verts_num;
- loop_c.e = top_edges_start + i;
- }
+ for (const int i : IndexRange(config.circle_segments)) {
+ /* Go backwards to reverse surface normal. */
+ MLoop &loop = loops[loop_index++];
+ loop.v = config.last_vert - i;
+ loop.e = config.last_edge - ((i + 1) % config.circle_segments);
}
- 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;
+/**
+ * If the top is the cone tip or has a fill, it is unwrapped into a circle in the
+ * lower left quadrant of the UV.
+ * Likewise, if the bottom is the cone tip or has a fill, it is unwrapped into a circle
+ * in the lower right quadrant of the UV.
+ * If the mesh is a truncated cone or a cylinder, the side faces are unwrapped into
+ * a rectangle that fills the top half of the UV (or the entire UV, if there are no fills).
+ */
+static void calculate_cone_uvs(Mesh *mesh, const ConeConfig &config)
+{
+ MeshComponent mesh_component;
+ mesh_component.replace(mesh, GeometryOwnershipType::Editable);
+ OutputAttribute_Typed<float2> uv_attribute =
+ mesh_component.attribute_try_get_for_output_only<float2>("uv_map", ATTR_DOMAIN_CORNER);
+ MutableSpan<float2> uvs = uv_attribute.as_span();
+
+ Array<float2> circle(config.circle_segments);
+ float angle = 0.0f;
+ const float angle_delta = 2.0f * M_PI / static_cast<float>(config.circle_segments);
+ for (const int i : IndexRange(config.circle_segments)) {
+ circle[i].x = std::cos(angle) * 0.225f;
+ circle[i].y = std::sin(angle) * 0.225f;
+ angle += angle_delta;
+ }
+
+ int loop_index = 0;
+
+ /* Left circle of the UV representing the top fill or top cone tip. */
+ if (config.top_is_point || config.fill_type != GEO_NODE_MESH_CIRCLE_FILL_NONE) {
+ const float2 center_left(0.25f, 0.25f);
+ const float radius_factor_delta = 1.0f / (config.top_is_point ?
+ static_cast<float>(config.side_segments) :
+ static_cast<float>(config.fill_segments));
+ const int left_circle_segment_count = config.top_is_point ? config.side_segments :
+ config.fill_segments;
+
+ if (config.top_has_center_vert) {
+ /* Cone tip itself or triangle fan center of the fill. */
+ for (const int i : IndexRange(config.circle_segments)) {
+ uvs[loop_index++] = radius_factor_delta * circle[i] + center_left;
+ uvs[loop_index++] = radius_factor_delta * circle[(i + 1) % config.circle_segments] +
+ center_left;
+ uvs[loop_index++] = center_left;
+ }
+ }
+ else if (!config.top_is_point && config.fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
+ /* N-gon at the center of the fill. */
+ for (const int i : IndexRange(config.circle_segments)) {
+ uvs[loop_index++] = radius_factor_delta * circle[i] + center_left;
+ }
+ }
+ /* The rest of the top fill is made out of quad rings. */
+ for (const int i : IndexRange(1, left_circle_segment_count - 1)) {
+ const float inner_radius_factor = i * radius_factor_delta;
+ const float outer_radius_factor = (i + 1) * radius_factor_delta;
+ for (const int j : IndexRange(config.circle_segments)) {
+ uvs[loop_index++] = inner_radius_factor * circle[j] + center_left;
+ uvs[loop_index++] = outer_radius_factor * circle[j] + center_left;
+ uvs[loop_index++] = outer_radius_factor * circle[(j + 1) % config.circle_segments] +
+ center_left;
+ uvs[loop_index++] = inner_radius_factor * circle[(j + 1) % config.circle_segments] +
+ center_left;
}
}
}
- /* Create bottom corners and faces. */
- if (!bottom_is_point) {
- if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
- MPoly &poly = polys[poly_index++];
- poly.loopstart = loop_index;
- poly.totloop = verts_num;
+ if (!config.top_is_point && !config.bottom_is_point) {
+ /* Mesh is a truncated cone or cylinder. The sides are unwrapped into a rectangle. */
+ const float bottom = (config.fill_type == GEO_NODE_MESH_CIRCLE_FILL_NONE) ? 0.0f : 0.5f;
+ const float x_delta = 1.0f / static_cast<float>(config.circle_segments);
+ const float y_delta = (1.0f - bottom) / static_cast<float>(config.side_segments);
- for (const int i : IndexRange(verts_num)) {
- /* Go backwards to reverse surface normal. */
- MLoop &loop = loops[loop_index++];
- loop.v = bottom_verts_start + verts_num - 1 - i;
- loop.e = bottom_edges_start + verts_num - 1 - (i + 1) % verts_num;
+ for (const int i : IndexRange(config.side_segments)) {
+ for (const int j : IndexRange(config.circle_segments)) {
+ uvs[loop_index++] = float2(j * x_delta, i * y_delta + bottom);
+ uvs[loop_index++] = float2(j * x_delta, (i + 1) * y_delta + bottom);
+ uvs[loop_index++] = float2((j + 1) * x_delta, (i + 1) * y_delta + bottom);
+ uvs[loop_index++] = float2((j + 1) * x_delta, i * y_delta + bottom);
}
}
- else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
- for (const int i : IndexRange(verts_num)) {
- MPoly &poly = polys[poly_index++];
- poly.loopstart = loop_index;
- poly.totloop = 3;
+ }
- MLoop &loop_a = loops[loop_index++];
- loop_a.v = bottom_verts_start + i;
- loop_a.e = bottom_fan_edges_start + i;
- MLoop &loop_b = loops[loop_index++];
- loop_b.v = bottom_center_vert_index;
- loop_b.e = bottom_fan_edges_start + (i + 1) % verts_num;
- MLoop &loop_c = loops[loop_index++];
- loop_c.v = bottom_verts_start + (i + 1) % verts_num;
- loop_c.e = bottom_edges_start + i;
+ /* Right circle of the UV representing the bottom fill or bottom cone tip. */
+ if (config.bottom_is_point || config.fill_type != GEO_NODE_MESH_CIRCLE_FILL_NONE) {
+ const float2 center_right(0.75f, 0.25f);
+ const float radius_factor_delta = 1.0f / (config.bottom_is_point ?
+ static_cast<float>(config.side_segments) :
+ static_cast<float>(config.fill_segments));
+ const int right_circle_segment_count = config.bottom_is_point ? config.side_segments :
+ config.fill_segments;
+
+ /* The bottom circle has to be created outside in to match the loop order. */
+ for (const int i : IndexRange(right_circle_segment_count - 1)) {
+ const float outer_radius_factor = 1.0f - i * radius_factor_delta;
+ const float inner_radius_factor = 1.0f - (i + 1) * radius_factor_delta;
+ for (const int j : IndexRange(config.circle_segments)) {
+ uvs[loop_index++] = outer_radius_factor * circle[j] + center_right;
+ uvs[loop_index++] = inner_radius_factor * circle[j] + center_right;
+ uvs[loop_index++] = inner_radius_factor * circle[(j + 1) % config.circle_segments] +
+ center_right;
+ uvs[loop_index++] = outer_radius_factor * circle[(j + 1) % config.circle_segments] +
+ center_right;
+ }
+ }
+
+ if (config.bottom_has_center_vert) {
+ /* Cone tip itself or triangle fan center of the fill. */
+ for (const int i : IndexRange(config.circle_segments)) {
+ uvs[loop_index++] = radius_factor_delta * circle[i] + center_right;
+ uvs[loop_index++] = center_right;
+ uvs[loop_index++] = radius_factor_delta * circle[(i + 1) % config.circle_segments] +
+ center_right;
+ }
+ }
+ else if (!config.bottom_is_point && config.fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
+ /* N-gon at the center of the fill. */
+ for (const int i : IndexRange(config.circle_segments)) {
+ /* Go backwards because of reversed face normal. */
+ uvs[loop_index++] = radius_factor_delta * circle[config.circle_segments - 1 - i] +
+ center_right;
}
}
}
+ uv_attribute.save();
+}
+
+static Mesh *create_vertex_mesh()
+{
+ /* Returns a mesh with a single vertex at the origin. */
+ Mesh *mesh = BKE_mesh_new_nomain(1, 0, 0, 0, 0);
+ copy_v3_fl3(mesh->mvert[0].co, 0.0f, 0.0f, 0.0f);
+ const short up[3] = {0, 0, SHRT_MAX};
+ copy_v3_v3_short(mesh->mvert[0].no, up);
+ return mesh;
+}
+
+Mesh *create_cylinder_or_cone_mesh(const float radius_top,
+ const float radius_bottom,
+ const float depth,
+ const int circle_segments,
+ const int side_segments,
+ const int fill_segments,
+ const GeometryNodeMeshCircleFillType fill_type)
+{
+ const ConeConfig config(
+ radius_top, radius_bottom, depth, circle_segments, side_segments, fill_segments, fill_type);
+
+ /* Handle the case of a line / single point before everything else to avoid
+ * the need to check for it later. */
+ if (config.top_is_point && config.bottom_is_point) {
+ if (config.height == 0.0f) {
+ return create_vertex_mesh();
+ }
+ const float z_delta = -2.0f * config.height / static_cast<float>(config.side_segments);
+ const float3 start(0.0f, 0.0f, config.height);
+ const float3 delta(0.0f, 0.0f, z_delta);
+ return create_line_mesh(start, delta, config.tot_verts);
+ }
+
+ Mesh *mesh = BKE_mesh_new_nomain(
+ config.tot_verts, config.tot_edges, 0, config.get_tot_corners(), config.get_tot_faces());
+ BKE_id_material_eval_ensure_default_slot(&mesh->id);
+
+ MutableSpan<MVert> verts{mesh->mvert, mesh->totvert};
+ MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop};
+ MutableSpan<MEdge> edges{mesh->medge, mesh->totedge};
+ MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly};
+
+ calculate_cone_vertices(verts, config);
+ calculate_cone_edges(edges, config);
+ calculate_cone_faces(loops, polys, config);
+ calculate_cone_uvs(mesh, config);
+
BKE_mesh_normals_tag_dirty(mesh);
- calculate_uvs(mesh, top_is_point, bottom_is_point, verts_num, fill_type);
+ calculate_cone_uvs(mesh, config);
return mesh;
}
@@ -540,23 +704,37 @@ static void geo_node_mesh_primitive_cone_exec(GeoNodeExecParams params)
{
const bNode &node = params.node();
const NodeGeometryMeshCone &storage = *(const NodeGeometryMeshCone *)node.storage;
-
const GeometryNodeMeshCircleFillType fill_type = (const GeometryNodeMeshCircleFillType)
storage.fill_type;
- const int verts_num = params.extract_input<int>("Vertices");
- if (verts_num < 3) {
+ const int circle_segments = params.extract_input<int>("Vertices");
+ if (circle_segments < 3) {
params.error_message_add(NodeWarningType::Info, TIP_("Vertices must be at least 3"));
params.set_output("Geometry", GeometrySet());
return;
}
+ const int side_segments = params.extract_input<int>("Side Segments");
+ if (side_segments < 1) {
+ params.error_message_add(NodeWarningType::Info, TIP_("Side Segments must be at least 1"));
+ params.set_output("Geometry", GeometrySet());
+ return;
+ }
+
+ const bool no_fill = fill_type == GEO_NODE_MESH_CIRCLE_FILL_NONE;
+ const int fill_segments = no_fill ? 1 : params.extract_input<int>("Fill Segments");
+ if (fill_segments < 1) {
+ params.error_message_add(NodeWarningType::Info, TIP_("Fill Segments must be at least 1"));
+ params.set_output("Geometry", GeometrySet());
+ return;
+ }
+
const float radius_top = params.extract_input<float>("Radius Top");
const float radius_bottom = params.extract_input<float>("Radius Bottom");
const float depth = params.extract_input<float>("Depth");
Mesh *mesh = create_cylinder_or_cone_mesh(
- radius_top, radius_bottom, depth, verts_num, fill_type);
+ radius_top, radius_bottom, depth, circle_segments, side_segments, fill_segments, 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);
@@ -572,6 +750,7 @@ void register_node_type_geo_mesh_primitive_cone()
geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_CONE, "Cone", NODE_CLASS_GEOMETRY, 0);
node_type_init(&ntype, blender::nodes::geo_node_mesh_primitive_cone_init);
+ node_type_update(&ntype, blender::nodes::geo_node_mesh_primitive_cone_update);
node_type_storage(
&ntype, "NodeGeometryMeshCone", node_free_standard_storage, node_copy_standard_storage);
ntype.geometry_node_execute = blender::nodes::geo_node_mesh_primitive_cone_exec;
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 ed701c921ca..287ea896ade 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
@@ -29,9 +29,31 @@ namespace blender::nodes {
static void geo_node_mesh_primitive_cylinder_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Int>("Vertices").default_value(32).min(3).max(4096);
- b.add_input<decl::Float>("Radius").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE);
- b.add_input<decl::Float>("Depth").default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE);
+ b.add_input<decl::Int>("Vertices")
+ .default_value(32)
+ .min(3)
+ .max(512)
+ .description("The number of vertices around the circumference");
+ b.add_input<decl::Int>("Side Segments")
+ .default_value(1)
+ .min(1)
+ .max(512)
+ .description("The number of segments along the side");
+ b.add_input<decl::Int>("Fill Segments")
+ .default_value(1)
+ .min(1)
+ .max(512)
+ .description("The number of concentric segments of the fill");
+ b.add_input<decl::Float>("Radius")
+ .default_value(1.0f)
+ .min(0.0f)
+ .subtype(PROP_DISTANCE)
+ .description("The radius of the cylinder");
+ b.add_input<decl::Float>("Depth")
+ .default_value(2.0f)
+ .min(0.0f)
+ .subtype(PROP_DISTANCE)
+ .description("The height of the cylinder on the Z axis");
b.add_output<decl::Geometry>("Geometry");
}
@@ -54,6 +76,19 @@ static void geo_node_mesh_primitive_cylinder_init(bNodeTree *UNUSED(ntree), bNod
node->storage = node_storage;
}
+static void geo_node_mesh_primitive_cylinder_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ bNodeSocket *vertices_socket = (bNodeSocket *)node->inputs.first;
+ bNodeSocket *rings_socket = vertices_socket->next;
+ bNodeSocket *fill_subdiv_socket = rings_socket->next;
+
+ const NodeGeometryMeshCone &storage = *(const NodeGeometryMeshCone *)node->storage;
+ const GeometryNodeMeshCircleFillType fill_type =
+ static_cast<const GeometryNodeMeshCircleFillType>(storage.fill_type);
+ const bool has_fill = fill_type != GEO_NODE_MESH_CIRCLE_FILL_NONE;
+ nodeSetSocketAvailability(fill_subdiv_socket, has_fill);
+}
+
static void geo_node_mesh_primitive_cylinder_exec(GeoNodeExecParams params)
{
const bNode &node = params.node();
@@ -64,15 +99,31 @@ static void geo_node_mesh_primitive_cylinder_exec(GeoNodeExecParams params)
const float radius = params.extract_input<float>("Radius");
const float depth = params.extract_input<float>("Depth");
- const int verts_num = params.extract_input<int>("Vertices");
- if (verts_num < 3) {
+ const int circle_segments = params.extract_input<int>("Vertices");
+ if (circle_segments < 3) {
params.error_message_add(NodeWarningType::Info, TIP_("Vertices must be at least 3"));
params.set_output("Geometry", GeometrySet());
return;
}
+ const int side_segments = params.extract_input<int>("Side Segments");
+ if (side_segments < 1) {
+ params.error_message_add(NodeWarningType::Info, TIP_("Side Segments must be at least 1"));
+ params.set_output("Geometry", GeometrySet());
+ return;
+ }
+
+ const bool no_fill = fill_type == GEO_NODE_MESH_CIRCLE_FILL_NONE;
+ const int fill_segments = no_fill ? 1 : params.extract_input<int>("Fill Segments");
+ if (fill_segments < 1) {
+ params.error_message_add(NodeWarningType::Info, TIP_("Fill Segments must be at least 1"));
+ params.set_output("Geometry", GeometrySet());
+ return;
+ }
+
/* The cylinder is a special case of the cone mesh where the top and bottom radius are equal. */
- Mesh *mesh = create_cylinder_or_cone_mesh(radius, radius, depth, verts_num, fill_type);
+ Mesh *mesh = create_cylinder_or_cone_mesh(
+ radius, radius, depth, circle_segments, side_segments, fill_segments, fill_type);
params.set_output("Geometry", GeometrySet::create_with_mesh(mesh));
}
@@ -84,6 +135,7 @@ void register_node_type_geo_mesh_primitive_cylinder()
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_CYLINDER, "Cylinder", NODE_CLASS_GEOMETRY, 0);
node_type_init(&ntype, blender::nodes::geo_node_mesh_primitive_cylinder_init);
+ node_type_update(&ntype, blender::nodes::geo_node_mesh_primitive_cylinder_update);
node_type_storage(
&ntype, "NodeGeometryMeshCylinder", node_free_standard_storage, node_copy_standard_storage);
ntype.declare = blender::nodes::geo_node_mesh_primitive_cylinder_declare;
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc
index 9ca74fed9a7..c436f5bd480 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc
@@ -32,28 +32,9 @@ static void geo_node_mesh_subdivide_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Geometry>("Geometry");
}
-static void geo_node_mesh_subdivide_exec(GeoNodeExecParams params)
+static void geometry_set_mesh_subdivide(GeometrySet &geometry_set, const int level)
{
- 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);
- return;
- }
-
-#ifndef WITH_OPENSUBDIV
- params.error_message_add(NodeWarningType::Error,
- TIP_("Disabled, Blender was compiled without OpenSubdiv"));
- params.set_output("Geometry", std::move(geometry_set));
- return;
-#endif
-
- /* See CCGSUBSURF_LEVEL_MAX for max limit. */
- const int subdiv_level = clamp_i(params.extract_input<int>("Level"), 0, 11);
-
- if (subdiv_level == 0) {
- params.set_output("Geometry", std::move(geometry_set));
return;
}
@@ -61,7 +42,7 @@ static void geo_node_mesh_subdivide_exec(GeoNodeExecParams params)
/* Initialize mesh settings. */
SubdivToMeshSettings mesh_settings;
- mesh_settings.resolution = (1 << subdiv_level) + 1;
+ mesh_settings.resolution = (1 << level) + 1;
mesh_settings.use_optimal_display = false;
/* Initialize subdivision settings. */
@@ -79,7 +60,6 @@ static void geo_node_mesh_subdivide_exec(GeoNodeExecParams params)
/* In case of bad topology, skip to input mesh. */
if (subdiv == nullptr) {
- params.set_output("Geometry", std::move(geometry_set));
return;
}
@@ -90,6 +70,29 @@ static void geo_node_mesh_subdivide_exec(GeoNodeExecParams params)
mesh_component.replace(mesh_out);
BKE_subdiv_free(subdiv);
+}
+
+static void geo_node_mesh_subdivide_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+#ifndef WITH_OPENSUBDIV
+ params.error_message_add(NodeWarningType::Error,
+ TIP_("Disabled, Blender was compiled without OpenSubdiv"));
+ params.set_output("Geometry", std::move(geometry_set));
+ return;
+#endif
+
+ /* See CCGSUBSURF_LEVEL_MAX for max limit. */
+ const int subdiv_level = clamp_i(params.extract_input<int>("Level"), 0, 11);
+
+ if (subdiv_level == 0) {
+ params.set_output("Geometry", std::move(geometry_set));
+ return;
+ }
+
+ geometry_set.modify_geometry_sets(
+ [&](GeometrySet &geometry_set) { geometry_set_mesh_subdivide(geometry_set, subdiv_level); });
params.set_output("Geometry", std::move(geometry_set));
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc
new file mode 100644
index 00000000000..6863f685eae
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc
@@ -0,0 +1,189 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "DNA_pointcloud_types.h"
+
+#include "BKE_attribute_math.hh"
+#include "BKE_pointcloud.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+using blender::Array;
+
+namespace blender::nodes {
+
+static void geo_node_mesh_to_points_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>("Mesh");
+ b.add_input<decl::Vector>("Position").implicit_field();
+ b.add_input<decl::Float>("Radius")
+ .default_value(0.05f)
+ .min(0.0f)
+ .subtype(PROP_DISTANCE)
+ .supports_field();
+ b.add_input<decl::Bool>("Selection").default_value(true).supports_field().hide_value();
+ b.add_output<decl::Geometry>("Points");
+}
+
+static void geo_node_mesh_to_points_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "mode", 0, "", ICON_NONE);
+}
+
+static void geo_node_mesh_to_points_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometryMeshToPoints *data = (NodeGeometryMeshToPoints *)MEM_callocN(
+ sizeof(NodeGeometryMeshToPoints), __func__);
+ data->mode = GEO_NODE_MESH_TO_POINTS_VERTICES;
+ node->storage = data;
+}
+
+template<typename T>
+static void copy_attribute_to_points(const VArray<T> &src,
+ const IndexMask mask,
+ MutableSpan<T> dst)
+{
+ for (const int i : mask.index_range()) {
+ dst[i] = src[mask[i]];
+ }
+}
+
+static void geometry_set_mesh_to_points(GeometrySet &geometry_set,
+ Field<float3> &position_field,
+ Field<float> &radius_field,
+ Field<bool> &selection_field,
+ const AttributeDomain domain)
+{
+ const MeshComponent *mesh_component = geometry_set.get_component_for_read<MeshComponent>();
+ if (mesh_component == nullptr) {
+ geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ return;
+ }
+ GeometryComponentFieldContext field_context{*mesh_component, domain};
+ const int domain_size = mesh_component->attribute_domain_size(domain);
+ if (domain_size == 0) {
+ geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ return;
+ }
+ fn::FieldEvaluator selection_evaluator{field_context, domain_size};
+ selection_evaluator.add(selection_field);
+ selection_evaluator.evaluate();
+ const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0);
+
+ PointCloud *pointcloud = BKE_pointcloud_new_nomain(selection.size());
+ uninitialized_fill_n(pointcloud->radius, pointcloud->totpoint, 0.05f);
+ geometry_set.replace_pointcloud(pointcloud);
+ PointCloudComponent &point_component =
+ geometry_set.get_component_for_write<PointCloudComponent>();
+
+ /* Evaluating directly into the point cloud doesn't work because we are not using the full
+ * "min_array_size" array but compressing the selected elements into the final array with no
+ * gaps. */
+ fn::FieldEvaluator evaluator{field_context, &selection};
+ evaluator.add(position_field);
+ evaluator.add(radius_field);
+ evaluator.evaluate();
+ copy_attribute_to_points(evaluator.get_evaluated<float3>(0),
+ selection,
+ {(float3 *)pointcloud->co, pointcloud->totpoint});
+ copy_attribute_to_points(
+ evaluator.get_evaluated<float>(1), selection, {pointcloud->radius, pointcloud->totpoint});
+
+ Map<AttributeIDRef, AttributeKind> attributes;
+ geometry_set.gather_attributes_for_propagation(
+ {GEO_COMPONENT_TYPE_MESH}, GEO_COMPONENT_TYPE_POINT_CLOUD, false, attributes);
+ attributes.remove("position");
+
+ for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
+ const AttributeIDRef attribute_id = entry.key;
+ const CustomDataType data_type = entry.value.data_type;
+ GVArrayPtr src = mesh_component->attribute_get_for_read(attribute_id, domain, data_type);
+ OutputAttribute dst = point_component.attribute_try_get_for_output_only(
+ attribute_id, ATTR_DOMAIN_POINT, data_type);
+ if (dst && src) {
+ attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ using T = decltype(dummy);
+ GVArray_Typed<T> src_typed{*src};
+ copy_attribute_to_points(*src_typed, selection, dst.as_span().typed<T>());
+ });
+ dst.save();
+ }
+ }
+
+ geometry_set.keep_only({GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_INSTANCES});
+}
+
+static void geo_node_mesh_to_points_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh");
+ Field<float3> position = params.extract_input<Field<float3>>("Position");
+ Field<float> radius = params.extract_input<Field<float>>("Radius");
+ Field<bool> selection = params.extract_input<Field<bool>>("Selection");
+
+ /* Use another multi-function operation to make sure the input radius is greater than zero.
+ * TODO: Use mutable multi-function once that is supported. */
+ static fn::CustomMF_SI_SO<float, float> max_zero_fn(
+ __func__, [](float value) { return std::max(0.0f, value); });
+ auto max_zero_op = std::make_shared<FieldOperation>(
+ FieldOperation(max_zero_fn, {std::move(radius)}));
+ Field<float> positive_radius(std::move(max_zero_op), 0);
+
+ const NodeGeometryMeshToPoints &storage =
+ *(const NodeGeometryMeshToPoints *)params.node().storage;
+ const GeometryNodeMeshToPointsMode mode = (GeometryNodeMeshToPointsMode)storage.mode;
+
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ switch (mode) {
+ case GEO_NODE_MESH_TO_POINTS_VERTICES:
+ geometry_set_mesh_to_points(
+ geometry_set, position, positive_radius, selection, ATTR_DOMAIN_POINT);
+ break;
+ case GEO_NODE_MESH_TO_POINTS_EDGES:
+ geometry_set_mesh_to_points(
+ geometry_set, position, positive_radius, selection, ATTR_DOMAIN_EDGE);
+ break;
+ case GEO_NODE_MESH_TO_POINTS_FACES:
+ geometry_set_mesh_to_points(
+ geometry_set, position, positive_radius, selection, ATTR_DOMAIN_FACE);
+ break;
+ case GEO_NODE_MESH_TO_POINTS_CORNERS:
+ geometry_set_mesh_to_points(
+ geometry_set, position, positive_radius, selection, ATTR_DOMAIN_CORNER);
+ break;
+ }
+ });
+
+ params.set_output("Points", std::move(geometry_set));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_mesh_to_points()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_MESH_TO_POINTS, "Mesh to Points", NODE_CLASS_GEOMETRY, 0);
+ ntype.declare = blender::nodes::geo_node_mesh_to_points_declare;
+ ntype.geometry_node_execute = blender::nodes::geo_node_mesh_to_points_exec;
+ node_type_init(&ntype, blender::nodes::geo_node_mesh_to_points_init);
+ ntype.draw_buttons = blender::nodes::geo_node_mesh_to_points_layout;
+ node_type_storage(
+ &ntype, "NodeGeometryMeshToPoints", node_free_standard_storage, node_copy_standard_storage);
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc
new file mode 100644
index 00000000000..afd0ced6360
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc
@@ -0,0 +1,118 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BLI_task.hh"
+
+#include "BKE_attribute_math.hh"
+#include "BKE_mesh.h"
+
+#include "node_geometry_util.hh"
+
+using blender::Array;
+
+namespace blender::nodes {
+
+static void geo_node_points_to_vertices_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>("Points");
+ b.add_input<decl::Bool>("Selection").default_value(true).supports_field().hide_value();
+ b.add_output<decl::Geometry>("Mesh");
+}
+
+template<typename T>
+static void copy_attribute_to_vertices(const Span<T> src, const IndexMask mask, MutableSpan<T> dst)
+{
+ for (const int i : mask.index_range()) {
+ dst[i] = src[mask[i]];
+ }
+}
+
+/* One improvement would be to move the attribute arrays directly to the mesh when possible. */
+static void geometry_set_points_to_vertices(GeometrySet &geometry_set,
+ Field<bool> &selection_field)
+{
+ const PointCloudComponent *point_component =
+ geometry_set.get_component_for_read<PointCloudComponent>();
+ if (point_component == nullptr) {
+ geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ return;
+ }
+
+ GeometryComponentFieldContext field_context{*point_component, ATTR_DOMAIN_POINT};
+ const int domain_size = point_component->attribute_domain_size(ATTR_DOMAIN_POINT);
+ if (domain_size == 0) {
+ geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ return;
+ }
+
+ fn::FieldEvaluator selection_evaluator{field_context, domain_size};
+ selection_evaluator.add(selection_field);
+ selection_evaluator.evaluate();
+ const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0);
+
+ Map<AttributeIDRef, AttributeKind> attributes;
+ geometry_set.gather_attributes_for_propagation(
+ {GEO_COMPONENT_TYPE_POINT_CLOUD}, GEO_COMPONENT_TYPE_MESH, false, attributes);
+
+ Mesh *mesh = BKE_mesh_new_nomain(selection.size(), 0, 0, 0, 0);
+ geometry_set.replace_mesh(mesh);
+ MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
+
+ for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
+ const AttributeIDRef attribute_id = entry.key;
+ const CustomDataType data_type = entry.value.data_type;
+ GVArrayPtr src = point_component->attribute_get_for_read(
+ attribute_id, ATTR_DOMAIN_POINT, data_type);
+ OutputAttribute dst = mesh_component.attribute_try_get_for_output_only(
+ attribute_id, ATTR_DOMAIN_POINT, data_type);
+ if (dst && src) {
+ attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ using T = decltype(dummy);
+ GVArray_Typed<T> src_typed{*src};
+ VArray_Span<T> src_typed_span{*src_typed};
+ copy_attribute_to_vertices(src_typed_span, selection, dst.as_span().typed<T>());
+ });
+ dst.save();
+ }
+ }
+
+ geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES});
+}
+
+static void geo_node_points_to_vertices_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Points");
+ Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
+
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ geometry_set_points_to_vertices(geometry_set, selection_field);
+ });
+
+ params.set_output("Mesh", std::move(geometry_set));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_points_to_vertices()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_POINTS_TO_VERTICES, "Points to Vertices", NODE_CLASS_GEOMETRY, 0);
+ ntype.declare = blender::nodes::geo_node_points_to_vertices_declare;
+ ntype.geometry_node_execute = blender::nodes::geo_node_points_to_vertices_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_proximity.cc b/source/blender/nodes/geometry/nodes/node_geo_proximity.cc
new file mode 100644
index 00000000000..7062deff2f1
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_proximity.cc
@@ -0,0 +1,235 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BLI_task.hh"
+#include "BLI_timeit.hh"
+
+#include "DNA_mesh_types.h"
+
+#include "BKE_bvhutils.h"
+#include "BKE_geometry_set.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes {
+
+static void geo_node_proximity_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>("Target");
+ b.add_input<decl::Vector>("Source Position").implicit_field();
+ b.add_output<decl::Vector>("Position").dependent_field();
+ b.add_output<decl::Float>("Distance").dependent_field();
+}
+
+static void geo_node_proximity_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "target_element", 0, "", ICON_NONE);
+}
+
+static void geo_proximity_init(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeGeometryProximity *node_storage = (NodeGeometryProximity *)MEM_callocN(
+ sizeof(NodeGeometryProximity), __func__);
+ node_storage->target_element = GEO_NODE_PROX_TARGET_FACES;
+ node->storage = node_storage;
+}
+
+static void calculate_mesh_proximity(const VArray<float3> &positions,
+ const IndexMask mask,
+ const Mesh &mesh,
+ const GeometryNodeProximityTargetType type,
+ const MutableSpan<float> r_distances,
+ const MutableSpan<float3> r_locations)
+{
+ BVHTreeFromMesh bvh_data;
+ switch (type) {
+ case GEO_NODE_PROX_TARGET_POINTS:
+ BKE_bvhtree_from_mesh_get(&bvh_data, &mesh, BVHTREE_FROM_VERTS, 2);
+ break;
+ case GEO_NODE_PROX_TARGET_EDGES:
+ BKE_bvhtree_from_mesh_get(&bvh_data, &mesh, BVHTREE_FROM_EDGES, 2);
+ break;
+ case GEO_NODE_PROX_TARGET_FACES:
+ BKE_bvhtree_from_mesh_get(&bvh_data, &mesh, BVHTREE_FROM_LOOPTRI, 2);
+ break;
+ }
+
+ if (bvh_data.tree == nullptr) {
+ return;
+ }
+
+ threading::parallel_for(mask.index_range(), 512, [&](IndexRange range) {
+ BVHTreeNearest nearest;
+ copy_v3_fl(nearest.co, FLT_MAX);
+ nearest.index = -1;
+
+ for (int i : range) {
+ const int index = mask[i];
+ /* Use the distance to the last found point as upper bound to speedup the bvh lookup. */
+ nearest.dist_sq = float3::distance_squared(nearest.co, positions[index]);
+
+ BLI_bvhtree_find_nearest(
+ bvh_data.tree, positions[index], &nearest, bvh_data.nearest_callback, &bvh_data);
+
+ if (nearest.dist_sq < r_distances[index]) {
+ r_distances[index] = nearest.dist_sq;
+ if (!r_locations.is_empty()) {
+ r_locations[index] = nearest.co;
+ }
+ }
+ }
+ });
+
+ free_bvhtree_from_mesh(&bvh_data);
+}
+
+static void calculate_pointcloud_proximity(const VArray<float3> &positions,
+ const IndexMask mask,
+ const PointCloud &pointcloud,
+ const MutableSpan<float> r_distances,
+ const MutableSpan<float3> r_locations)
+{
+ BVHTreeFromPointCloud bvh_data;
+ BKE_bvhtree_from_pointcloud_get(&bvh_data, &pointcloud, 2);
+ if (bvh_data.tree == nullptr) {
+ return;
+ }
+
+ threading::parallel_for(mask.index_range(), 512, [&](IndexRange range) {
+ BVHTreeNearest nearest;
+ copy_v3_fl(nearest.co, FLT_MAX);
+ nearest.index = -1;
+
+ for (int i : range) {
+ const int index = mask[i];
+ /* Use the distance to the closest point in the mesh to speedup the pointcloud bvh lookup.
+ * This is ok because we only need to find the closest point in the pointcloud if it's
+ * closer than the mesh. */
+ nearest.dist_sq = r_distances[index];
+
+ BLI_bvhtree_find_nearest(
+ bvh_data.tree, positions[index], &nearest, bvh_data.nearest_callback, &bvh_data);
+
+ if (nearest.dist_sq < r_distances[index]) {
+ r_distances[index] = nearest.dist_sq;
+ if (!r_locations.is_empty()) {
+ r_locations[index] = nearest.co;
+ }
+ }
+ }
+ });
+
+ free_bvhtree_from_pointcloud(&bvh_data);
+}
+
+class ProximityFunction : public fn::MultiFunction {
+ private:
+ GeometrySet target_;
+ GeometryNodeProximityTargetType type_;
+
+ public:
+ ProximityFunction(GeometrySet target, GeometryNodeProximityTargetType type)
+ : target_(std::move(target)), type_(type)
+ {
+ static fn::MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ }
+
+ static fn::MFSignature create_signature()
+ {
+ blender::fn::MFSignatureBuilder signature{"Geometry Proximity"};
+ signature.single_input<float3>("Source Position");
+ signature.single_output<float3>("Position");
+ signature.single_output<float>("Distance");
+ return signature.build();
+ }
+
+ void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override
+ {
+ const VArray<float3> &src_positions = params.readonly_single_input<float3>(0,
+ "Source Position");
+ MutableSpan<float3> positions = params.uninitialized_single_output_if_required<float3>(
+ 1, "Position");
+ /* Make sure there is a distance array, used for finding the smaller distance when there are
+ * multiple components. Theoretically it would be possible to avoid using the distance array
+ * when there is only one component. However, this only adds an allocation and a single float
+ * comparison per vertex, so it's likely not worth it. */
+ MutableSpan<float> distances = params.uninitialized_single_output<float>(2, "Distance");
+
+ distances.fill(FLT_MAX);
+
+ if (target_.has_mesh()) {
+ calculate_mesh_proximity(
+ src_positions, mask, *target_.get_mesh_for_read(), type_, distances, positions);
+ }
+
+ if (target_.has_pointcloud() && type_ == GEO_NODE_PROX_TARGET_POINTS) {
+ calculate_pointcloud_proximity(
+ src_positions, mask, *target_.get_pointcloud_for_read(), distances, positions);
+ }
+
+ if (params.single_output_is_required(2, "Distance")) {
+ threading::parallel_for(mask.index_range(), 2048, [&](IndexRange range) {
+ for (const int i : range) {
+ const int j = mask[i];
+ distances[j] = std::sqrt(distances[j]);
+ }
+ });
+ }
+ }
+};
+
+static void geo_node_proximity_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set_target = params.extract_input<GeometrySet>("Target");
+
+ if (!geometry_set_target.has_mesh() && !geometry_set_target.has_pointcloud()) {
+ params.set_output("Position", fn::make_constant_field<float3>({0.0f, 0.0f, 0.0f}));
+ params.set_output("Distance", fn::make_constant_field<float>({0.0f}));
+ return;
+ }
+
+ const NodeGeometryProximity &storage = *(const NodeGeometryProximity *)params.node().storage;
+ Field<float3> position_field = params.extract_input<Field<float3>>("Source Position");
+
+ auto proximity_fn = std::make_unique<ProximityFunction>(
+ std::move(geometry_set_target),
+ static_cast<GeometryNodeProximityTargetType>(storage.target_element));
+ auto proximity_op = std::make_shared<FieldOperation>(
+ FieldOperation(std::move(proximity_fn), {std::move(position_field)}));
+
+ params.set_output("Position", Field<float3>(proximity_op, 0));
+ params.set_output("Distance", Field<float>(proximity_op, 1));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_proximity()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_PROXIMITY, "Geometry Proximity", NODE_CLASS_GEOMETRY, 0);
+ node_type_init(&ntype, blender::nodes::geo_proximity_init);
+ node_type_storage(
+ &ntype, "NodeGeometryProximity", node_free_standard_storage, node_copy_standard_storage);
+ ntype.declare = blender::nodes::geo_node_proximity_declare;
+ ntype.geometry_node_execute = blender::nodes::geo_node_proximity_exec;
+ ntype.draw_buttons = blender::nodes::geo_node_proximity_layout;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_separate_components.cc b/source/blender/nodes/geometry/nodes/node_geo_separate_components.cc
index c63e4ec49d9..dafd10cee2d 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_separate_components.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_separate_components.cc
@@ -25,20 +25,18 @@ static void geo_node_join_geometry_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Geometry>("Point Cloud");
b.add_output<decl::Geometry>("Curve");
b.add_output<decl::Geometry>("Volume");
+ b.add_output<decl::Geometry>("Instances");
}
static void geo_node_separate_components_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- /* Note that it will be possible to skip realizing instances here when instancing
- * geometry directly is supported by creating corresponding geometry instances. */
- geometry_set = bke::geometry_set_realize_instances(geometry_set);
-
GeometrySet meshes;
GeometrySet point_clouds;
GeometrySet volumes;
GeometrySet curves;
+ GeometrySet instances;
if (geometry_set.has<MeshComponent>()) {
meshes.add(*geometry_set.get_component_for_read<MeshComponent>());
@@ -52,11 +50,15 @@ static void geo_node_separate_components_exec(GeoNodeExecParams params)
if (geometry_set.has<VolumeComponent>()) {
volumes.add(*geometry_set.get_component_for_read<VolumeComponent>());
}
+ if (geometry_set.has<InstancesComponent>()) {
+ instances.add(*geometry_set.get_component_for_read<InstancesComponent>());
+ }
params.set_output("Mesh", meshes);
params.set_output("Point Cloud", point_clouds);
params.set_output("Curve", curves);
params.set_output("Volume", volumes);
+ params.set_output("Instances", instances);
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc
index c5e10b788ac..8caf961fc04 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc
@@ -23,8 +23,8 @@ namespace blender::nodes {
static void geo_node_set_position_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Vector>("Position").hide_value();
- b.add_input<decl::Bool>("Selection").default_value(true).hide_value();
+ b.add_input<decl::Vector>("Position").implicit_field();
+ b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field();
b.add_output<decl::Geometry>("Geometry");
}
@@ -34,6 +34,9 @@ static void set_position_in_component(GeometryComponent &component,
{
GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT);
+ if (domain_size == 0) {
+ return;
+ }
fn::FieldEvaluator selection_evaluator{field_context, domain_size};
selection_evaluator.add(selection_field);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc
new file mode 100644
index 00000000000..5e2f03806c3
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc
@@ -0,0 +1,306 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "DNA_curve_types.h"
+#include "DNA_vfont_types.h"
+
+#include "BKE_curve.h"
+#include "BKE_font.h"
+#include "BKE_spline.hh"
+
+#include "BLI_hash.h"
+#include "BLI_string_utf8.h"
+#include "BLI_task.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes {
+
+static void geo_node_string_to_curves_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::String>("String");
+ b.add_input<decl::Float>("Size").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE);
+ b.add_input<decl::Float>("Character Spacing")
+ .default_value(1.0f)
+ .min(0.0f)
+ .subtype(PROP_DISTANCE);
+ b.add_input<decl::Float>("Word Spacing").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE);
+ b.add_input<decl::Float>("Line Spacing").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE);
+ b.add_input<decl::Float>("Text Box Width").default_value(0.0f).min(0.0f).subtype(PROP_DISTANCE);
+ b.add_input<decl::Float>("Text Box Height").default_value(0.0f).min(0.0f).subtype(PROP_DISTANCE);
+ b.add_output<decl::Geometry>("Curves");
+ b.add_output<decl::String>("Remainder");
+}
+
+static void geo_node_string_to_curves_layout(uiLayout *layout, struct bContext *C, PointerRNA *ptr)
+{
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
+ uiTemplateID(layout,
+ C,
+ ptr,
+ "font",
+ nullptr,
+ "FONT_OT_open",
+ "FONT_OT_unlink",
+ UI_TEMPLATE_ID_FILTER_ALL,
+ false,
+ nullptr);
+ uiItemR(layout, ptr, "overflow", 0, "", ICON_NONE);
+ uiItemR(layout, ptr, "align_x", 0, "", ICON_NONE);
+ uiItemR(layout, ptr, "align_y", 0, "", ICON_NONE);
+}
+
+static void geo_node_string_to_curves_init(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeGeometryStringToCurves *data = (NodeGeometryStringToCurves *)MEM_callocN(
+ sizeof(NodeGeometryStringToCurves), __func__);
+
+ data->overflow = GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW;
+ data->align_x = GEO_NODE_STRING_TO_CURVES_ALIGN_X_LEFT;
+ data->align_y = GEO_NODE_STRING_TO_CURVES_ALIGN_Y_TOP_BASELINE;
+ node->storage = data;
+ node->id = (ID *)BKE_vfont_builtin_get();
+}
+
+static void geo_node_string_to_curves_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ const NodeGeometryStringToCurves *storage = (const NodeGeometryStringToCurves *)node->storage;
+ const GeometryNodeStringToCurvesOverflowMode overflow = (GeometryNodeStringToCurvesOverflowMode)
+ storage->overflow;
+ bNodeSocket *socket_remainder = ((bNodeSocket *)node->outputs.first)->next;
+ nodeSetSocketAvailability(socket_remainder, overflow == GEO_NODE_STRING_TO_CURVES_MODE_TRUNCATE);
+
+ bNodeSocket *height_socket = (bNodeSocket *)node->inputs.last;
+ bNodeSocket *width_socket = height_socket->prev;
+ nodeSetSocketAvailability(height_socket, overflow != GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW);
+ node_sock_label(width_socket,
+ overflow == GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW ? N_("Max Width") :
+ N_("Text Box Width"));
+}
+
+struct TextLayout {
+ /* Position of each character. */
+ Vector<float2> positions;
+
+ /* The text that fit into the text box, with newline character sequences replaced. */
+ std::string text;
+
+ /* The text that didn't fit into the text box in 'Truncate' mode. May be empty. */
+ std::string truncated_text;
+
+ /* Font size could be modified if in 'Scale to fit'-mode. */
+ float final_font_size;
+};
+
+static TextLayout get_text_layout(GeoNodeExecParams &params)
+{
+ TextLayout layout;
+ layout.text = params.extract_input<std::string>("String");
+ if (layout.text.empty()) {
+ return {};
+ }
+
+ const NodeGeometryStringToCurves &storage =
+ *(const NodeGeometryStringToCurves *)params.node().storage;
+ const GeometryNodeStringToCurvesOverflowMode overflow = (GeometryNodeStringToCurvesOverflowMode)
+ storage.overflow;
+ const GeometryNodeStringToCurvesAlignXMode align_x = (GeometryNodeStringToCurvesAlignXMode)
+ storage.align_x;
+ const GeometryNodeStringToCurvesAlignYMode align_y = (GeometryNodeStringToCurvesAlignYMode)
+ storage.align_y;
+
+ const float font_size = std::max(params.extract_input<float>("Size"), 0.0f);
+ const float char_spacing = params.extract_input<float>("Character Spacing");
+ const float word_spacing = params.extract_input<float>("Word Spacing");
+ const float line_spacing = params.extract_input<float>("Line Spacing");
+ const float textbox_w = params.extract_input<float>("Text Box Width");
+ const float textbox_h = overflow == GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW ?
+ 0.0f :
+ params.extract_input<float>("Text Box Height");
+ VFont *vfont = (VFont *)params.node().id;
+
+ Curve cu = {nullptr};
+ cu.type = OB_FONT;
+ /* Set defaults */
+ cu.resolu = 12;
+ cu.smallcaps_scale = 0.75f;
+ cu.wordspace = 1.0f;
+ /* Set values from inputs */
+ cu.spacemode = align_x;
+ cu.align_y = align_y;
+ cu.fsize = font_size;
+ cu.spacing = char_spacing;
+ cu.wordspace = word_spacing;
+ cu.linedist = line_spacing;
+ cu.vfont = vfont;
+ cu.overflow = overflow;
+ cu.tb = (TextBox *)MEM_calloc_arrayN(MAXTEXTBOX, sizeof(TextBox), __func__);
+ cu.tb->w = textbox_w;
+ cu.tb->h = textbox_h;
+ cu.totbox = 1;
+ size_t len_bytes;
+ size_t len_chars = BLI_strlen_utf8_ex(layout.text.c_str(), &len_bytes);
+ cu.len_char32 = len_chars;
+ cu.len = len_bytes;
+ cu.pos = len_chars;
+ /* The reason for the additional character here is unknown, but reflects other code elsewhere. */
+ cu.str = (char *)MEM_mallocN(len_bytes + sizeof(char32_t), __func__);
+ cu.strinfo = (CharInfo *)MEM_callocN((len_chars + 1) * sizeof(CharInfo), __func__);
+ BLI_strncpy(cu.str, layout.text.c_str(), len_bytes + 1);
+
+ struct CharTrans *chartransdata = nullptr;
+ int text_len;
+ bool text_free;
+ const char32_t *r_text = nullptr;
+ /* Mode FO_DUPLI used because it doesn't create curve splines. */
+ BKE_vfont_to_curve_ex(
+ nullptr, &cu, FO_DUPLI, nullptr, &r_text, &text_len, &text_free, &chartransdata);
+
+ if (text_free) {
+ MEM_freeN((void *)r_text);
+ }
+
+ Span<CharInfo> info{cu.strinfo, text_len};
+ layout.final_font_size = cu.fsize_realtime;
+ layout.positions.reserve(text_len);
+
+ for (const int i : IndexRange(text_len)) {
+ CharTrans &ct = chartransdata[i];
+ layout.positions.append(float2(ct.xof, ct.yof) * layout.final_font_size);
+
+ if ((info[i].flag & CU_CHINFO_OVERFLOW) && (cu.overflow == CU_OVERFLOW_TRUNCATE)) {
+ const int offset = BLI_str_utf8_offset_from_index(layout.text.c_str(), i + 1);
+ layout.truncated_text = layout.text.substr(offset);
+ layout.text = layout.text.substr(0, offset);
+ break;
+ }
+ }
+
+ MEM_SAFE_FREE(chartransdata);
+ MEM_SAFE_FREE(cu.str);
+ MEM_SAFE_FREE(cu.strinfo);
+ MEM_SAFE_FREE(cu.tb);
+
+ return layout;
+}
+
+/* Returns a mapping of UTF-32 character code to instance handle. */
+static Map<int, int> create_curve_instances(GeoNodeExecParams &params,
+ const float fontsize,
+ const Span<char32_t> charcodes,
+ InstancesComponent &instance_component)
+{
+ VFont *vfont = (VFont *)params.node().id;
+ Map<int, int> handles;
+
+ for (int i : charcodes.index_range()) {
+ if (handles.contains(charcodes[i])) {
+ continue;
+ }
+ Curve cu = {nullptr};
+ cu.type = OB_FONT;
+ cu.resolu = 12;
+ cu.vfont = vfont;
+ CharInfo charinfo = {0};
+ charinfo.mat_nr = 1;
+
+ BKE_vfont_build_char(&cu, &cu.nurb, charcodes[i], &charinfo, 0, 0, 0, i, 1);
+ std::unique_ptr<CurveEval> curve_eval = curve_eval_from_dna_curve(cu);
+ BKE_nurbList_free(&cu.nurb);
+ float4x4 size_matrix = float4x4::identity();
+ size_matrix.apply_scale(fontsize);
+ curve_eval->transform(size_matrix);
+
+ GeometrySet geometry_set_curve = GeometrySet::create_with_curve(curve_eval.release());
+ handles.add_new(charcodes[i], instance_component.add_reference(std::move(geometry_set_curve)));
+ }
+ return handles;
+}
+
+static void add_instances_from_handles(InstancesComponent &instances,
+ const Map<int, int> &char_handles,
+ const Span<char32_t> charcodes,
+ const Span<float2> positions)
+{
+ instances.resize(positions.size());
+ MutableSpan<int> handles = instances.instance_reference_handles();
+ MutableSpan<float4x4> transforms = instances.instance_transforms();
+ MutableSpan<int> instance_ids = instances.instance_ids();
+
+ threading::parallel_for(IndexRange(positions.size()), 256, [&](IndexRange range) {
+ for (const int i : range) {
+ handles[i] = char_handles.lookup(charcodes[i]);
+ transforms[i] = float4x4::from_location({positions[i].x, positions[i].y, 0});
+ instance_ids[i] = i;
+ }
+ });
+}
+
+static void geo_node_string_to_curves_exec(GeoNodeExecParams params)
+{
+ TextLayout layout = get_text_layout(params);
+
+ const NodeGeometryStringToCurves &storage =
+ *(const NodeGeometryStringToCurves *)params.node().storage;
+ if (storage.overflow == GEO_NODE_STRING_TO_CURVES_MODE_TRUNCATE) {
+ params.set_output("Remainder", std::move(layout.truncated_text));
+ }
+
+ if (layout.positions.size() == 0) {
+ params.set_output("Curves", GeometrySet());
+ return;
+ }
+
+ /* Convert UTF-8 encoded string to UTF-32. */
+ size_t len_bytes;
+ size_t len_chars = BLI_strlen_utf8_ex(layout.text.c_str(), &len_bytes);
+ Array<char32_t> char_codes(len_chars + 1);
+ BLI_str_utf8_as_utf32(char_codes.data(), layout.text.c_str(), len_chars + 1);
+
+ /* Create and add instances. */
+ GeometrySet geometry_set_out;
+ InstancesComponent &instances = geometry_set_out.get_component_for_write<InstancesComponent>();
+ Map<int, int> char_handles = create_curve_instances(
+ params, layout.final_font_size, char_codes, instances);
+ add_instances_from_handles(instances, char_handles, char_codes, layout.positions);
+
+ params.set_output("Curves", std::move(geometry_set_out));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_string_to_curves()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_STRING_TO_CURVES, "String to Curves", NODE_CLASS_GEOMETRY, 0);
+ ntype.declare = blender::nodes::geo_node_string_to_curves_declare;
+ ntype.geometry_node_execute = blender::nodes::geo_node_string_to_curves_exec;
+ node_type_init(&ntype, blender::nodes::geo_node_string_to_curves_init);
+ node_type_update(&ntype, blender::nodes::geo_node_string_to_curves_update);
+ node_type_size(&ntype, 190, 120, 700);
+ node_type_storage(&ntype,
+ "NodeGeometryStringToCurves",
+ node_free_standard_storage,
+ node_copy_standard_storage);
+ ntype.draw_buttons = blender::nodes::geo_node_string_to_curves_layout;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_switch.cc b/source/blender/nodes/geometry/nodes/node_geo_switch.cc
index ca857c4d2e3..05df927fb39 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_switch.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_switch.cc
@@ -19,24 +19,37 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_material.h"
+
+#include "FN_multi_function_signature.hh"
+
namespace blender::nodes {
static void geo_node_switch_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Bool>("Switch");
-
- b.add_input<decl::Float>("False");
- b.add_input<decl::Float>("True");
- b.add_input<decl::Int>("False", "False_001").min(-100000).max(100000);
- b.add_input<decl::Int>("True", "True_001").min(-100000).max(100000);
- b.add_input<decl::Bool>("False", "False_002");
- b.add_input<decl::Bool>("True", "True_002");
- b.add_input<decl::Vector>("False", "False_003");
- b.add_input<decl::Vector>("True", "True_003");
- b.add_input<decl::Color>("False", "False_004").default_value({0.8f, 0.8f, 0.8f, 1.0f});
- b.add_input<decl::Color>("True", "True_004").default_value({0.8f, 0.8f, 0.8f, 1.0f});
- b.add_input<decl::String>("False", "False_005");
- b.add_input<decl::String>("True", "True_005");
+ b.add_input<decl::Bool>("Switch").default_value(false).supports_field();
+ b.add_input<decl::Bool>("Switch", "Switch_001").default_value(false);
+
+ b.add_input<decl::Float>("False").supports_field();
+ b.add_input<decl::Float>("True").supports_field();
+ b.add_input<decl::Int>("False", "False_001").min(-100000).max(100000).supports_field();
+ b.add_input<decl::Int>("True", "True_001").min(-100000).max(100000).supports_field();
+ b.add_input<decl::Bool>("False", "False_002").default_value(false).hide_value().supports_field();
+ b.add_input<decl::Bool>("True", "True_002").default_value(true).hide_value().supports_field();
+ b.add_input<decl::Vector>("False", "False_003").supports_field();
+ b.add_input<decl::Vector>("True", "True_003").supports_field();
+ b.add_input<decl::Color>("False", "False_004")
+ .default_value({0.8f, 0.8f, 0.8f, 1.0f})
+ .supports_field();
+ b.add_input<decl::Color>("True", "True_004")
+ .default_value({0.8f, 0.8f, 0.8f, 1.0f})
+ .supports_field();
+ b.add_input<decl::String>("False", "False_005").supports_field();
+ b.add_input<decl::String>("True", "True_005").supports_field();
+
b.add_input<decl::Geometry>("False", "False_006");
b.add_input<decl::Geometry>("True", "True_006");
b.add_input<decl::Object>("False", "False_007");
@@ -48,12 +61,12 @@ static void geo_node_switch_declare(NodeDeclarationBuilder &b)
b.add_input<decl::Material>("False", "False_010");
b.add_input<decl::Material>("True", "True_010");
- b.add_output<decl::Float>("Output");
- b.add_output<decl::Int>("Output", "Output_001");
- b.add_output<decl::Bool>("Output", "Output_002");
- b.add_output<decl::Vector>("Output", "Output_003");
- b.add_output<decl::Color>("Output", "Output_004");
- b.add_output<decl::String>("Output", "Output_005");
+ b.add_output<decl::Float>("Output").dependent_field();
+ b.add_output<decl::Int>("Output", "Output_001").dependent_field();
+ b.add_output<decl::Bool>("Output", "Output_002").dependent_field();
+ b.add_output<decl::Vector>("Output", "Output_003").dependent_field();
+ b.add_output<decl::Color>("Output", "Output_004").dependent_field();
+ b.add_output<decl::String>("Output", "Output_005").dependent_field();
b.add_output<decl::Geometry>("Output", "Output_006");
b.add_output<decl::Object>("Output", "Output_007");
b.add_output<decl::Collection>("Output", "Output_008");
@@ -77,91 +90,168 @@ 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++;
+ bNodeSocket *field_switch = (bNodeSocket *)node->inputs.first;
+ bNodeSocket *non_field_switch = (bNodeSocket *)field_switch->next;
+
+ const bool fields_type = ELEM((eNodeSocketDatatype)node_storage->input_type,
+ SOCK_FLOAT,
+ SOCK_INT,
+ SOCK_BOOLEAN,
+ SOCK_VECTOR,
+ SOCK_RGBA,
+ SOCK_STRING);
+
+ nodeSetSocketAvailability(field_switch, fields_type);
+ nodeSetSocketAvailability(non_field_switch, !fields_type);
+
+ LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, &node->inputs, index) {
+ if (index <= 1) {
+ continue;
+ }
+ nodeSetSocketAvailability(socket,
+ socket->type == (eNodeSocketDatatype)node_storage->input_type);
}
+
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)
+template<typename T> class SwitchFieldsFunction : public fn::MultiFunction {
+ public:
+ SwitchFieldsFunction()
+ {
+ static fn::MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ }
+ static fn::MFSignature create_signature()
+ {
+ fn::MFSignatureBuilder signature{"Switch"};
+ signature.single_input<bool>("Switch");
+ signature.single_input<T>("False");
+ signature.single_input<T>("True");
+ signature.single_output<T>("Output");
+ return signature.build();
+ }
+
+ void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override
+ {
+ const VArray<bool> &switches = params.readonly_single_input<bool>(0, "Switch");
+ const VArray<T> &falses = params.readonly_single_input<T>(1, "False");
+ const VArray<T> &trues = params.readonly_single_input<T>(2, "True");
+ MutableSpan<T> values = params.uninitialized_single_output_if_required<T>(3, "Output");
+ for (int64_t i : mask) {
+ new (&values[i]) T(switches[i] ? trues[i] : falses[i]);
+ }
+ }
+};
+
+template<typename T> void switch_fields(GeoNodeExecParams &params, const StringRef suffix)
+{
+ if (params.lazy_require_input("Switch")) {
+ return;
+ }
+
+ const std::string name_false = "False" + suffix;
+ const std::string name_true = "True" + suffix;
+ const std::string name_output = "Output" + suffix;
+
+ /* TODO: Allow for Laziness when the switch field is constant. */
+ const bool require_false = params.lazy_require_input(name_false);
+ const bool require_true = params.lazy_require_input(name_true);
+ if (require_false | require_true) {
+ return;
+ }
+
+ Field<bool> switches_field = params.extract_input<Field<bool>>("Switch");
+ Field<T> falses_field = params.extract_input<Field<T>>(name_false);
+ Field<T> trues_field = params.extract_input<Field<T>>(name_true);
+
+ auto switch_fn = std::make_unique<SwitchFieldsFunction<T>>();
+ auto switch_op = std::make_shared<FieldOperation>(FieldOperation(
+ std::move(switch_fn),
+ {std::move(switches_field), std::move(falses_field), std::move(trues_field)}));
+
+ params.set_output(name_output, Field<T>(switch_op, 0));
+}
+
+template<typename T> void switch_no_fields(GeoNodeExecParams &params, const StringRef suffix)
{
- const std::string name_a = "False" + input_suffix;
- const std::string name_b = "True" + input_suffix;
- if (input) {
- params.set_input_unused(name_a);
- if (params.lazy_require_input(name_b)) {
+ if (params.lazy_require_input("Switch_001")) {
+ return;
+ }
+ bool switch_value = params.get_input<bool>("Switch_001");
+
+ const std::string name_false = "False" + suffix;
+ const std::string name_true = "True" + suffix;
+ const std::string name_output = "Output" + suffix;
+
+ if (switch_value) {
+ params.set_input_unused(name_false);
+ if (params.lazy_require_input(name_true)) {
return;
}
- params.set_output(output_identifier, params.extract_input<T>(name_b));
+ params.set_output(name_output, params.extract_input<T>(name_true));
}
else {
- params.set_input_unused(name_b);
- if (params.lazy_require_input(name_a)) {
+ params.set_input_unused(name_true);
+ if (params.lazy_require_input(name_false)) {
return;
}
- params.set_output(output_identifier, params.extract_input<T>(name_a));
+ params.set_output(name_output, params.extract_input<T>(name_false));
}
}
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) {
+ const eNodeSocketDatatype data_type = static_cast<eNodeSocketDatatype>(storage.input_type);
+
+ switch (data_type) {
+
case SOCK_FLOAT: {
- output_input<float>(params, input, "", "Output");
+ switch_fields<float>(params, "");
break;
}
case SOCK_INT: {
- output_input<int>(params, input, "_001", "Output_001");
+ switch_fields<int>(params, "_001");
break;
}
case SOCK_BOOLEAN: {
- output_input<bool>(params, input, "_002", "Output_002");
+ switch_fields<bool>(params, "_002");
break;
}
case SOCK_VECTOR: {
- output_input<float3>(params, input, "_003", "Output_003");
+ switch_fields<float3>(params, "_003");
break;
}
case SOCK_RGBA: {
- output_input<ColorGeometry4f>(params, input, "_004", "Output_004");
+ switch_fields<ColorGeometry4f>(params, "_004");
break;
}
case SOCK_STRING: {
- output_input<std::string>(params, input, "_005", "Output_005");
+ switch_fields<std::string>(params, "_005");
break;
}
case SOCK_GEOMETRY: {
- output_input<GeometrySet>(params, input, "_006", "Output_006");
+ switch_no_fields<GeometrySet>(params, "_006");
break;
}
case SOCK_OBJECT: {
- output_input<Object *>(params, input, "_007", "Output_007");
+ switch_no_fields<Object *>(params, "_007");
break;
}
case SOCK_COLLECTION: {
- output_input<Collection *>(params, input, "_008", "Output_008");
+ switch_no_fields<Collection *>(params, "_008");
break;
}
case SOCK_TEXTURE: {
- output_input<Tex *>(params, input, "_009", "Output_009");
+ switch_no_fields<Tex *>(params, "_009");
break;
}
case SOCK_MATERIAL: {
- output_input<Material *>(params, input, "_010", "Output_010");
+ switch_no_fields<Material *>(params, "_010");
break;
}
default:
diff --git a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc
index 8886c7194db..7ef0913622c 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc
@@ -58,14 +58,14 @@ static void geo_node_triangulate_exec(GeoNodeExecParams params)
GeometryNodeTriangulateNGons ngon_method = static_cast<GeometryNodeTriangulateNGons>(
params.node().custom2);
- geometry_set = geometry_set_realize_instances(geometry_set);
-
- /* #triangulate_mesh might modify the input mesh currently. */
- Mesh *mesh_in = geometry_set.get_mesh_for_write();
- if (mesh_in != nullptr) {
- Mesh *mesh_out = triangulate_mesh(mesh_in, quad_method, ngon_method, min_vertices, 0);
- geometry_set.replace_mesh(mesh_out);
- }
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ /* #triangulate_mesh might modify the input mesh currently. */
+ Mesh *mesh_in = geometry_set.get_mesh_for_write();
+ if (mesh_in != nullptr) {
+ Mesh *mesh_out = triangulate_mesh(mesh_in, quad_method, ngon_method, min_vertices, 0);
+ geometry_set.replace_mesh(mesh_out);
+ }
+ });
params.set_output("Geometry", std::move(geometry_set));
}