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_util.cc1
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc6
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_map_range.cc18
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc6
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc6
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc12
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc82
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc14
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc6
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_deform.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc47
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc132
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc407
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc157
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc390
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc201
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc1
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc3
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc6
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_point_instance.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_raycast.cc321
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_select_by_material.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_separate_components.cc77
28 files changed, 1678 insertions, 237 deletions
diff --git a/source/blender/nodes/geometry/node_geometry_util.cc b/source/blender/nodes/geometry/node_geometry_util.cc
index 93cada2982b..46e9d36c09c 100644
--- a/source/blender/nodes/geometry/node_geometry_util.cc
+++ b/source/blender/nodes/geometry/node_geometry_util.cc
@@ -47,6 +47,7 @@ void update_attribute_input_socket_availabilities(bNode &node,
name_is_available &&
((socket->type == SOCK_STRING && mode_ == GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE) ||
(socket->type == SOCK_FLOAT && mode_ == GEO_NODE_ATTRIBUTE_INPUT_FLOAT) ||
+ (socket->type == SOCK_INT && mode_ == GEO_NODE_ATTRIBUTE_INPUT_INTEGER) ||
(socket->type == SOCK_VECTOR && mode_ == GEO_NODE_ATTRIBUTE_INPUT_VECTOR) ||
(socket->type == SOCK_RGBA && mode_ == GEO_NODE_ATTRIBUTE_INPUT_COLOR));
nodeSetSocketAvailability(socket, socket_is_available);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc b/source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc
index d1b71d6f2ba..9b6824fdb5c 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc
@@ -78,7 +78,7 @@ static void align_rotations_auto_pivot(const VArray<float3> &vectors,
const float3 local_main_axis,
const MutableSpan<float3> rotations)
{
- parallel_for(IndexRange(vectors.size()), 128, [&](IndexRange range) {
+ threading::parallel_for(IndexRange(vectors.size()), 128, [&](IndexRange range) {
for (const int i : range) {
const float3 vector = vectors[i];
if (is_zero_v3(vector)) {
@@ -129,7 +129,7 @@ static void align_rotations_fixed_pivot(const VArray<float3> &vectors,
return;
}
- parallel_for(IndexRange(vectors.size()), 128, [&](IndexRange range) {
+ threading::parallel_for(IndexRange(vectors.size()), 128, [&](IndexRange range) {
for (const int i : range) {
const float3 vector = vectors[i];
if (is_zero_v3(vector)) {
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc
index 5293dd8c876..c5740395dfb 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc
@@ -95,7 +95,7 @@ static void execute_on_component(const GeoNodeExecParams &params, GeometryCompon
MutableSpan<ColorGeometry4f> results = attribute_result.as_span();
ColorBand *color_ramp = &node_storage->color_ramp;
- parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) {
+ threading::parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) {
for (const int i : range) {
BKE_colorband_evaluate(color_ramp, attribute_in[i], results[i]);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc
index 599c9e58e52..06a4327a6c5 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc
@@ -144,7 +144,7 @@ static void execute_on_component(const GeoNodeExecParams &params, GeometryCompon
GVArray_Typed<float> attribute_in = component.attribute_get_for_read<float>(
input_name, result_domain, float(0.0f));
MutableSpan<float> results = attribute_result.as_span<float>();
- parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) {
+ threading::parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) {
for (const int i : range) {
results[i] = BKE_curvemapping_evaluateF(cumap, 3, attribute_in[i]);
}
@@ -156,7 +156,7 @@ static void execute_on_component(const GeoNodeExecParams &params, GeometryCompon
GVArray_Typed<float3> attribute_in = component.attribute_get_for_read<float3>(
input_name, result_domain, float3(0.0f));
MutableSpan<float3> results = attribute_result.as_span<float3>();
- parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) {
+ threading::parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) {
for (const int i : range) {
BKE_curvemapping_evaluate3F(cumap, results[i], attribute_in[i]);
}
@@ -169,7 +169,7 @@ static void execute_on_component(const GeoNodeExecParams &params, GeometryCompon
component.attribute_get_for_read<ColorGeometry4f>(
input_name, result_domain, ColorGeometry4f(0.0f, 0.0f, 0.0f, 1.0f));
MutableSpan<ColorGeometry4f> results = attribute_result.as_span<ColorGeometry4f>();
- parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) {
+ threading::parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) {
for (const int i : range) {
BKE_curvemapping_evaluateRGBF(cumap, results[i], attribute_in[i]);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_map_range.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_map_range.cc
index 40fe675bd6c..00f38fb0c6b 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_map_range.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_map_range.cc
@@ -209,7 +209,7 @@ static void map_range_float(const VArray<float> &attribute_input,
switch (interpolation_type) {
case NODE_MAP_RANGE_LINEAR: {
- parallel_for(span.index_range(), 2048, [&](IndexRange range) {
+ threading::parallel_for(span.index_range(), 2048, [&](IndexRange range) {
for (const int i : range) {
results[i] = map_linear(span[i], min_from, max_from, min_to, max_to);
}
@@ -218,7 +218,7 @@ static void map_range_float(const VArray<float> &attribute_input,
}
case NODE_MAP_RANGE_STEPPED: {
const float steps = params.get_input<float>("Steps");
- parallel_for(span.index_range(), 1024, [&](IndexRange range) {
+ threading::parallel_for(span.index_range(), 1024, [&](IndexRange range) {
for (const int i : range) {
results[i] = map_stepped(span[i], min_from, max_from, min_to, max_to, steps);
}
@@ -226,7 +226,7 @@ static void map_range_float(const VArray<float> &attribute_input,
break;
}
case NODE_MAP_RANGE_SMOOTHSTEP: {
- parallel_for(span.index_range(), 1024, [&](IndexRange range) {
+ threading::parallel_for(span.index_range(), 1024, [&](IndexRange range) {
for (const int i : range) {
results[i] = map_smoothstep(span[i], min_from, max_from, min_to, max_to);
}
@@ -234,7 +234,7 @@ static void map_range_float(const VArray<float> &attribute_input,
break;
}
case NODE_MAP_RANGE_SMOOTHERSTEP: {
- parallel_for(span.index_range(), 1024, [&](IndexRange range) {
+ threading::parallel_for(span.index_range(), 1024, [&](IndexRange range) {
for (const int i : range) {
results[i] = map_smootherstep(span[i], min_from, max_from, min_to, max_to);
}
@@ -249,7 +249,7 @@ static void map_range_float(const VArray<float> &attribute_input,
const float clamp_min = min_to < max_to ? min_to : max_to;
const float clamp_max = min_to < max_to ? max_to : min_to;
- parallel_for(results.index_range(), 2048, [&](IndexRange range) {
+ threading::parallel_for(results.index_range(), 2048, [&](IndexRange range) {
for (const int i : range) {
results[i] = std::clamp(results[i], clamp_min, clamp_max);
}
@@ -273,7 +273,7 @@ static void map_range_float3(const VArray<float3> &attribute_input,
switch (interpolation_type) {
case NODE_MAP_RANGE_LINEAR: {
- parallel_for(span.index_range(), 1024, [&](IndexRange range) {
+ threading::parallel_for(span.index_range(), 1024, [&](IndexRange range) {
for (const int i : range) {
results[i].x = map_linear(span[i].x, min_from.x, max_from.x, min_to.x, max_to.x);
results[i].y = map_linear(span[i].y, min_from.y, max_from.y, min_to.y, max_to.y);
@@ -284,7 +284,7 @@ static void map_range_float3(const VArray<float3> &attribute_input,
}
case NODE_MAP_RANGE_STEPPED: {
const float3 steps = params.get_input<float3>("Steps_001");
- parallel_for(span.index_range(), 1024, [&](IndexRange range) {
+ threading::parallel_for(span.index_range(), 1024, [&](IndexRange range) {
for (const int i : range) {
results[i].x = map_stepped(
span[i].x, min_from.x, max_from.x, min_to.x, max_to.x, steps.x);
@@ -297,7 +297,7 @@ static void map_range_float3(const VArray<float3> &attribute_input,
break;
}
case NODE_MAP_RANGE_SMOOTHSTEP: {
- parallel_for(span.index_range(), 1024, [&](IndexRange range) {
+ threading::parallel_for(span.index_range(), 1024, [&](IndexRange range) {
for (const int i : range) {
results[i].x = map_smoothstep(span[i].x, min_from.x, max_from.x, min_to.x, max_to.x);
results[i].y = map_smoothstep(span[i].y, min_from.y, max_from.y, min_to.y, max_to.y);
@@ -307,7 +307,7 @@ static void map_range_float3(const VArray<float3> &attribute_input,
break;
}
case NODE_MAP_RANGE_SMOOTHERSTEP: {
- parallel_for(span.index_range(), 1024, [&](IndexRange range) {
+ threading::parallel_for(span.index_range(), 1024, [&](IndexRange range) {
for (const int i : range) {
results[i].x = map_smootherstep(span[i].x, min_from.x, max_from.x, min_to.x, max_to.x);
results[i].y = map_smootherstep(span[i].y, min_from.y, max_from.y, min_to.y, max_to.y);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc
index ce0ca31cc2b..9309863f4e9 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc
@@ -159,7 +159,7 @@ static void do_math_operation(const VArray<float> &span_a,
{
bool success = try_dispatch_float_math_fl_fl_fl_to_fl(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- parallel_for(IndexRange(span_result.size()), 512, [&](IndexRange range) {
+ threading::parallel_for(IndexRange(span_result.size()), 512, [&](IndexRange range) {
for (const int i : range) {
span_result[i] = math_function(span_a[i], span_b[i], span_c[i]);
}
@@ -176,7 +176,7 @@ static void do_math_operation(const VArray<float> &span_a,
{
bool success = try_dispatch_float_math_fl_fl_to_fl(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- parallel_for(IndexRange(span_result.size()), 1024, [&](IndexRange range) {
+ threading::parallel_for(IndexRange(span_result.size()), 1024, [&](IndexRange range) {
for (const int i : range) {
span_result[i] = math_function(span_a[i], span_b[i]);
}
@@ -192,7 +192,7 @@ static void do_math_operation(const VArray<float> &span_input,
{
bool success = try_dispatch_float_math_fl_to_fl(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- parallel_for(IndexRange(span_result.size()), 1024, [&](IndexRange range) {
+ threading::parallel_for(IndexRange(span_result.size()), 1024, [&](IndexRange range) {
for (const int i : range) {
span_result[i] = math_function(span_input[i]);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc
index a6bd6c0ee32..931b7758a57 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc
@@ -88,7 +88,7 @@ static void do_mix_operation_float(const int blend_mode,
VMutableArray<float> &results)
{
const int size = results.size();
- parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ threading::parallel_for(IndexRange(size), 512, [&](IndexRange range) {
for (const int i : range) {
const float factor = factors[i];
float3 a{inputs_a[i]};
@@ -107,7 +107,7 @@ static void do_mix_operation_float3(const int blend_mode,
VMutableArray<float3> &results)
{
const int size = results.size();
- parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ threading::parallel_for(IndexRange(size), 512, [&](IndexRange range) {
for (const int i : range) {
const float factor = factors[i];
float3 a = inputs_a[i];
@@ -125,7 +125,7 @@ static void do_mix_operation_color4f(const int blend_mode,
VMutableArray<ColorGeometry4f> &results)
{
const int size = results.size();
- parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ threading::parallel_for(IndexRange(size), 512, [&](IndexRange range) {
for (const int i : range) {
const float factor = factors[i];
ColorGeometry4f a = inputs_a[i];
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc
index 9c22b7fa87f..b7863d38fc2 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc
@@ -71,7 +71,7 @@ static void proximity_calc(MutableSpan<float> distance_span,
const bool store_locations)
{
IndexRange range = positions.index_range();
- parallel_for(range, 512, [&](IndexRange range) {
+ threading::parallel_for(range, 512, [&](IndexRange range) {
BVHTreeNearest nearest_from_mesh;
BVHTreeNearest nearest_from_pointcloud;
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc
index 286411b7d28..eeb77abd624 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc
@@ -126,7 +126,7 @@ static void randomize_attribute(MutableSpan<T> span,
/* The operations could be templated too, but it doesn't make the code much shorter. */
switch (operation) {
case GEO_NODE_ATTRIBUTE_RANDOMIZE_REPLACE_CREATE:
- parallel_for(span.index_range(), 512, [&](IndexRange range) {
+ threading::parallel_for(span.index_range(), 512, [&](IndexRange range) {
for (const int i : range) {
const T random_value = random_value_in_range<T>(ids[i], seed, min, max);
span[i] = random_value;
@@ -134,7 +134,7 @@ static void randomize_attribute(MutableSpan<T> span,
});
break;
case GEO_NODE_ATTRIBUTE_RANDOMIZE_ADD:
- parallel_for(span.index_range(), 512, [&](IndexRange range) {
+ threading::parallel_for(span.index_range(), 512, [&](IndexRange range) {
for (const int i : range) {
const T random_value = random_value_in_range<T>(ids[i], seed, min, max);
span[i] = span[i] + random_value;
@@ -142,7 +142,7 @@ static void randomize_attribute(MutableSpan<T> span,
});
break;
case GEO_NODE_ATTRIBUTE_RANDOMIZE_SUBTRACT:
- parallel_for(span.index_range(), 512, [&](IndexRange range) {
+ threading::parallel_for(span.index_range(), 512, [&](IndexRange range) {
for (const int i : range) {
const T random_value = random_value_in_range<T>(ids[i], seed, min, max);
span[i] = span[i] - random_value;
@@ -150,7 +150,7 @@ static void randomize_attribute(MutableSpan<T> span,
});
break;
case GEO_NODE_ATTRIBUTE_RANDOMIZE_MULTIPLY:
- parallel_for(span.index_range(), 512, [&](IndexRange range) {
+ threading::parallel_for(span.index_range(), 512, [&](IndexRange range) {
for (const int i : range) {
const T random_value = random_value_in_range<T>(ids[i], seed, min, max);
span[i] = span[i] * random_value;
@@ -170,7 +170,7 @@ static void randomize_attribute_bool(MutableSpan<bool> span,
{
BLI_assert(operation == GEO_NODE_ATTRIBUTE_RANDOMIZE_REPLACE_CREATE);
UNUSED_VARS_NDEBUG(operation);
- parallel_for(span.index_range(), 512, [&](IndexRange range) {
+ threading::parallel_for(span.index_range(), 512, [&](IndexRange range) {
for (const int i : range) {
const bool random_value = BLI_hash_int_2d_to_float(ids[i], seed) > 0.5f;
span[i] = random_value;
@@ -190,7 +190,7 @@ Array<uint32_t> get_geometry_element_ids_as_uints(const GeometryComponent &compo
BLI_assert(hashes.size() == hash_attribute->size());
const CPPType &cpp_type = hash_attribute->type();
GVArray_GSpan items{*hash_attribute};
- parallel_for(hashes.index_range(), 512, [&](IndexRange range) {
+ threading::parallel_for(hashes.index_range(), 512, [&](IndexRange range) {
for (const int i : range) {
hashes[i] = cpp_type.hash(items[i]);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc
index d6b1ad3e9e0..e0a3f5ad334 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc
@@ -90,7 +90,7 @@ static void execute_on_component(GeometryComponent &component, const GeoNodeExec
mapping_name, result_domain, {0, 0, 0});
MutableSpan<ColorGeometry4f> colors = attribute_out.as_span();
- parallel_for(IndexRange(mapping_attribute.size()), 128, [&](IndexRange range) {
+ threading::parallel_for(IndexRange(mapping_attribute.size()), 128, [&](IndexRange range) {
for (const int i : range) {
TexResult texture_result = {0};
const float3 position = mapping_attribute[i];
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc
index 4b677dc5c82..d1114713672 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc
@@ -102,14 +102,6 @@ static void get_result_domain_and_data_type(const GeometrySet &src_geometry,
}
}
-static Span<MLoopTri> get_mesh_looptris(const Mesh &mesh)
-{
- /* This only updates a cache and can be considered to be logically const. */
- const MLoopTri *looptris = BKE_mesh_runtime_looptri_ensure(const_cast<Mesh *>(&mesh));
- const int looptris_len = BKE_mesh_runtime_looptri_len(&mesh);
- return {looptris, looptris_len};
-}
-
static void get_closest_in_bvhtree(BVHTreeFromMesh &tree_data,
const VArray<float3> &positions,
const MutableSpan<int> r_indices,
@@ -212,7 +204,7 @@ static void get_closest_mesh_polygons(const Mesh &mesh,
Array<int> looptri_indices(positions.size());
get_closest_mesh_looptris(mesh, positions, looptri_indices, r_distances_sq, r_positions);
- Span<MLoopTri> looptris = get_mesh_looptris(mesh);
+ Span<MLoopTri> looptris = bke::mesh_surface_sample::get_mesh_looptris(mesh);
for (const int i : positions.index_range()) {
const MLoopTri &looptri = looptris[looptri_indices[i]];
r_poly_indices[i] = looptri.poly;
@@ -262,32 +254,6 @@ static void get_closest_mesh_corners(const Mesh &mesh,
}
}
-static void get_barycentric_coords(const Mesh &mesh,
- const Span<int> looptri_indices,
- const Span<float3> positions,
- const MutableSpan<float3> r_bary_coords)
-{
- BLI_assert(r_bary_coords.size() == positions.size());
- BLI_assert(r_bary_coords.size() == looptri_indices.size());
-
- Span<MLoopTri> looptris = get_mesh_looptris(mesh);
-
- for (const int i : r_bary_coords.index_range()) {
- const int looptri_index = looptri_indices[i];
- const MLoopTri &looptri = looptris[looptri_index];
-
- const int v0_index = mesh.mloop[looptri.tri[0]].v;
- const int v1_index = mesh.mloop[looptri.tri[1]].v;
- const int v2_index = mesh.mloop[looptri.tri[2]].v;
-
- interp_weights_tri_v3(r_bary_coords[i],
- mesh.mvert[v0_index].co,
- mesh.mvert[v1_index].co,
- mesh.mvert[v2_index].co,
- positions[i]);
- }
-}
-
static void transfer_attribute_nearest_face_interpolated(const GeometrySet &src_geometry,
GeometryComponent &dst_component,
const VArray<float3> &dst_positions,
@@ -308,8 +274,11 @@ static void transfer_attribute_nearest_face_interpolated(const GeometrySet &src_
if (mesh->totpoly == 0) {
return;
}
+
ReadAttributeLookup src_attribute = component->attribute_try_get_for_read(src_name, data_type);
- if (!src_attribute) {
+ OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
+ dst_name, dst_domain, data_type);
+ if (!src_attribute || !dst_attribute) {
return;
}
@@ -318,45 +287,10 @@ static void transfer_attribute_nearest_face_interpolated(const GeometrySet &src_
Array<float3> positions(tot_samples);
get_closest_mesh_looptris(*mesh, dst_positions, looptri_indices, {}, positions);
- OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
- dst_name, dst_domain, data_type);
- if (!dst_attribute) {
- return;
- }
- GMutableSpan dst_span = dst_attribute.as_span();
- Array<float3> bary_coords;
+ bke::mesh_surface_sample::MeshAttributeInterpolator interp(mesh, positions, looptri_indices);
+ interp.sample_attribute(
+ src_attribute, dst_attribute, bke::mesh_surface_sample::eAttributeMapMode::INTERPOLATED);
- /* Compute barycentric coordinates only when they are needed. */
- if (src_attribute.domain != ATTR_DOMAIN_FACE) {
- bary_coords.reinitialize(tot_samples);
- get_barycentric_coords(*mesh, looptri_indices, positions, bary_coords);
- }
- /* Interpolate the source attribute on the surface. */
- switch (src_attribute.domain) {
- case ATTR_DOMAIN_POINT: {
- bke::mesh_surface_sample::sample_point_attribute(
- *mesh, looptri_indices, bary_coords, *src_attribute.varray, dst_span);
- break;
- }
- case ATTR_DOMAIN_FACE: {
- bke::mesh_surface_sample::sample_face_attribute(
- *mesh, looptri_indices, *src_attribute.varray, dst_span);
- break;
- }
- case ATTR_DOMAIN_CORNER: {
- bke::mesh_surface_sample::sample_corner_attribute(
- *mesh, looptri_indices, bary_coords, *src_attribute.varray, dst_span);
- break;
- }
- case ATTR_DOMAIN_EDGE: {
- /* Not yet supported. */
- break;
- }
- default: {
- BLI_assert_unreachable();
- break;
- }
- }
dst_attribute.save();
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc
index b04e04d1cb7..e2cf6e8b480 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc
@@ -186,7 +186,7 @@ static void do_math_operation_fl3_fl3_to_fl3(const VArray<float3> &input_a,
bool success = try_dispatch_float_math_fl3_fl3_to_fl3(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ threading::parallel_for(IndexRange(size), 512, [&](IndexRange range) {
for (const int i : range) {
const float3 a = span_a[i];
const float3 b = span_b[i];
@@ -218,7 +218,7 @@ static void do_math_operation_fl3_fl3_fl3_to_fl3(const VArray<float3> &input_a,
bool success = try_dispatch_float_math_fl3_fl3_fl3_to_fl3(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ threading::parallel_for(IndexRange(size), 512, [&](IndexRange range) {
for (const int i : range) {
const float3 a = span_a[i];
const float3 b = span_b[i];
@@ -251,7 +251,7 @@ static void do_math_operation_fl3_fl3_fl_to_fl3(const VArray<float3> &input_a,
bool success = try_dispatch_float_math_fl3_fl3_fl_to_fl3(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ threading::parallel_for(IndexRange(size), 512, [&](IndexRange range) {
for (const int i : range) {
const float3 a = span_a[i];
const float3 b = span_b[i];
@@ -282,7 +282,7 @@ static void do_math_operation_fl3_fl3_to_fl(const VArray<float3> &input_a,
bool success = try_dispatch_float_math_fl3_fl3_to_fl(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ threading::parallel_for(IndexRange(size), 512, [&](IndexRange range) {
for (const int i : range) {
const float3 a = span_a[i];
const float3 b = span_b[i];
@@ -312,7 +312,7 @@ static void do_math_operation_fl3_fl_to_fl3(const VArray<float3> &input_a,
bool success = try_dispatch_float_math_fl3_fl_to_fl3(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ threading::parallel_for(IndexRange(size), 512, [&](IndexRange range) {
for (const int i : range) {
const float3 a = span_a[i];
const float b = span_b[i];
@@ -340,7 +340,7 @@ static void do_math_operation_fl3_to_fl3(const VArray<float3> &input_a,
bool success = try_dispatch_float_math_fl3_to_fl3(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ threading::parallel_for(IndexRange(size), 512, [&](IndexRange range) {
for (const int i : range) {
const float3 in = span_a[i];
const float3 out = math_function(in);
@@ -367,7 +367,7 @@ static void do_math_operation_fl3_to_fl(const VArray<float3> &input_a,
bool success = try_dispatch_float_math_fl3_to_fl(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ threading::parallel_for(IndexRange(size), 512, [&](IndexRange range) {
for (const int i : range) {
const float3 in = span_a[i];
const float out = math_function(in);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc
index 4d568ab5c3a..da753dfc11b 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc
@@ -154,7 +154,7 @@ static void do_vector_rotate_around_axis(const VArray<float3> &vector,
VArray_Span<float3> span_axis{axis};
VArray_Span<float> span_angle{angle};
- parallel_for(IndexRange(results.size()), 1024, [&](IndexRange range) {
+ threading::parallel_for(IndexRange(results.size()), 1024, [&](IndexRange range) {
for (const int i : range) {
float angle = (invert) ? -span_angle[i] : span_angle[i];
results[i] = vector_rotate_around_axis(span_vector[i], span_center[i], span_axis[i], angle);
@@ -173,7 +173,7 @@ static void do_vector_rotate_around_fixed_axis(const VArray<float3> &vector,
VArray_Span<float3> span_center{center};
VArray_Span<float> span_angle{angle};
- parallel_for(IndexRange(results.size()), 1024, [&](IndexRange range) {
+ threading::parallel_for(IndexRange(results.size()), 1024, [&](IndexRange range) {
for (const int i : range) {
float angle = (invert) ? -span_angle[i] : span_angle[i];
results[i] = vector_rotate_around_axis(span_vector[i], span_center[i], axis, angle);
@@ -191,7 +191,7 @@ static void do_vector_rotate_euler(const VArray<float3> &vector,
VArray_Span<float3> span_center{center};
VArray_Span<float3> span_rotation{rotation};
- parallel_for(IndexRange(results.size()), 1024, [&](IndexRange range) {
+ threading::parallel_for(IndexRange(results.size()), 1024, [&](IndexRange range) {
for (const int i : range) {
results[i] = vector_rotate_euler(span_vector[i], span_center[i], span_rotation[i], invert);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_deform.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_deform.cc
index 9b485284b35..dde7191fb69 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_deform.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_deform.cc
@@ -220,7 +220,7 @@ static void execute_on_component(const GeoNodeExecParams &params,
const Bounds bounds = position_bounds(positions);
const Bounds parameter_bounds = input.use_bounds ? bounds : dummy_parameter_bounds(deform_axis);
- parallel_for(positions.index_range(), 1024, [&](IndexRange range) {
+ threading::parallel_for(positions.index_range(), 1024, [&](IndexRange range) {
for (const int i : range) {
const float parameter = process_parameter(
positions[i], axis_index, is_negative, input, parameter_bounds);
@@ -267,7 +267,7 @@ static void geo_node_curve_deform_exec(GeoNodeExecParams params)
spline.evaluated_positions(),
spline.evaluated_tangents(),
spline.evaluated_normals(),
- spline.interpolate_to_evaluated_points(spline.radii()),
+ spline.interpolate_to_evaluated(spline.radii()),
total_length,
params.extract_input<bool>("Stretch"),
params.extract_input<bool>("Use Bounds")};
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 e879ec624c0..fc65d1754e9 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc
@@ -94,16 +94,16 @@ static SplinePtr resample_spline(const Spline &input_spline, const int count)
Array<float> uniform_samples = input_spline.sample_uniform_index_factors(count);
- input_spline.sample_based_on_index_factors<float3>(
+ input_spline.sample_with_index_factors<float3>(
input_spline.evaluated_positions(), uniform_samples, output_spline->positions());
- input_spline.sample_based_on_index_factors<float>(
- input_spline.interpolate_to_evaluated_points(input_spline.radii()),
+ input_spline.sample_with_index_factors<float>(
+ input_spline.interpolate_to_evaluated(input_spline.radii()),
uniform_samples,
output_spline->radii());
- input_spline.sample_based_on_index_factors<float>(
- input_spline.interpolate_to_evaluated_points(input_spline.tilts()),
+ input_spline.sample_with_index_factors<float>(
+ input_spline.interpolate_to_evaluated(input_spline.tilts()),
uniform_samples,
output_spline->tilts());
@@ -123,8 +123,8 @@ static SplinePtr resample_spline(const Spline &input_spline, const int count)
return false;
}
- input_spline.sample_based_on_index_factors(
- *input_spline.interpolate_to_evaluated_points(*input_attribute),
+ input_spline.sample_with_index_factors(
+ *input_spline.interpolate_to_evaluated(*input_attribute),
uniform_samples,
*output_attribute);
@@ -138,19 +138,28 @@ static SplinePtr resample_spline(const Spline &input_spline, const int count)
static std::unique_ptr<CurveEval> resample_curve(const CurveEval &input_curve,
const SampleModeParam &mode_param)
{
- std::unique_ptr<CurveEval> output_curve = std::make_unique<CurveEval>();
+ Span<SplinePtr> input_splines = input_curve.splines();
- for (const SplinePtr &spline : input_curve.splines()) {
- if (mode_param.mode == GEO_NODE_CURVE_SAMPLE_COUNT) {
- BLI_assert(mode_param.count);
- output_curve->add_spline(resample_spline(*spline, *mode_param.count));
- }
- else if (mode_param.mode == GEO_NODE_CURVE_SAMPLE_LENGTH) {
- BLI_assert(mode_param.length);
- const float length = spline->length();
- const int count = std::max(int(length / *mode_param.length), 1);
- output_curve->add_spline(resample_spline(*spline, count));
- }
+ 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_SAMPLE_COUNT) {
+ 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);
+ }
+ });
+ }
+ else if (mode_param.mode == GEO_NODE_CURVE_SAMPLE_LENGTH) {
+ 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);
+ output_splines[i] = resample_spline(*input_splines[i], count);
+ }
+ });
}
output_curve->attributes = input_curve.attributes;
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc
new file mode 100644
index 00000000000..e92d22a6064
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc
@@ -0,0 +1,132 @@
+/*
+ * 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"
+
+static bNodeSocketTemplate geo_node_curve_reverse_in[] = {
+ {SOCK_GEOMETRY, N_("Curve")},
+ {SOCK_STRING, N_("Selection")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_curve_reverse_out[] = {
+ {SOCK_GEOMETRY, N_("Curve")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+
+/**
+ * Reverse the data in a MutableSpan object.
+ */
+template<typename T> static void reverse_data(MutableSpan<T> r_data)
+{
+ const int size = r_data.size();
+ for (const int i : IndexRange(size / 2)) {
+ std::swap(r_data[size - 1 - i], r_data[i]);
+ }
+}
+
+/**
+ * Reverse and Swap the data between 2 MutableSpans.
+ */
+template<typename T> static void reverse_data(MutableSpan<T> left, MutableSpan<T> right)
+{
+ BLI_assert(left.size() == right.size());
+ const int size = left.size();
+
+ for (const int i : IndexRange(size / 2 + size % 2)) {
+ std::swap(left[i], right[size - 1 - i]);
+ std::swap(right[i], left[size - 1 - i]);
+ }
+}
+
+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]) {
+ continue;
+ }
+
+ reverse_data<float3>(splines[i]->positions());
+ reverse_data<float>(splines[i]->radii());
+ reverse_data<float>(splines[i]->tilts());
+
+ splines[i]->attributes.foreach_attribute(
+ [&](StringRefNull name, const AttributeMetaData &meta_data) {
+ std::optional<blender::fn::GMutableSpan> output_attribute =
+ splines[i]->attributes.get_for_write(name);
+ if (!output_attribute) {
+ BLI_assert_unreachable();
+ return false;
+ }
+ attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) {
+ using T = decltype(dummy);
+ reverse_data(output_attribute->typed<T>());
+ });
+ return true;
+ },
+ ATTR_DOMAIN_POINT);
+
+ /* Deal with extra info on derived types. */
+ if (BezierSpline *spline = dynamic_cast<BezierSpline *>(splines[i].get())) {
+ reverse_data<BezierSpline::HandleType>(spline->handle_types_left());
+ reverse_data<BezierSpline::HandleType>(spline->handle_types_right());
+ reverse_data<float3>(spline->handle_positions_left(), spline->handle_positions_right());
+ }
+ else if (NURBSpline *spline = dynamic_cast<NURBSpline *>(splines[i].get())) {
+ reverse_data<float>(spline->weights());
+ }
+ /* Nothing to do for poly splines. */
+
+ splines[i]->mark_cache_invalid();
+ }
+ });
+
+ params.set_output("Curve", geometry_set);
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_curve_reverse()
+{
+ static bNodeType ntype;
+ geo_node_type_base(&ntype, GEO_NODE_CURVE_REVERSE, "Curve Reverse", NODE_CLASS_GEOMETRY, 0);
+ node_type_socket_templates(&ntype, geo_node_curve_reverse_in, geo_node_curve_reverse_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_curve_reverse_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc
new file mode 100644
index 00000000000..3de2604cd0a
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc
@@ -0,0 +1,407 @@
+/*
+ * 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;
+
+static bNodeSocketTemplate geo_node_curve_subdivide_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_STRING, N_("Cuts")},
+ {SOCK_INT, N_("Cuts"), 1, 0, 0, 0, 0, 1000},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_curve_subdivide_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+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;
+}
+
+namespace blender::nodes {
+
+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();
+ }
+}
+
+/**
+ * De Casteljau Bezier subdivision.
+ *
+ * <pre>
+ * handle_prev handle_next
+ * O----------------O
+ * / \
+ * / x---O---x \
+ * / new_* \
+ * / \
+ * O O
+ * point_prev point_next
+ * </pre>
+ */
+static void calculate_new_bezier_point(const float3 &point_prev,
+ float3 &handle_prev,
+ float3 &new_left_handle,
+ float3 &new_position,
+ float3 &new_right_handle,
+ float3 &handle_next,
+ const float3 &point_next,
+ const float parameter)
+{
+ const float3 center_point = float3::interpolate(handle_prev, handle_next, parameter);
+
+ handle_prev = float3::interpolate(point_prev, handle_prev, parameter);
+ handle_next = float3::interpolate(handle_next, point_next, parameter);
+ new_left_handle = float3::interpolate(handle_prev, center_point, parameter);
+ new_right_handle = float3::interpolate(center_point, handle_next, parameter);
+ new_position = float3::interpolate(new_left_handle, new_right_handle, parameter);
+}
+
+/**
+ * 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;
+ 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);
+
+ dst_positions[offset] = src_positions[index];
+ 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;
+ dst_positions[offset] = src_positions[index];
+ dst_handles_right[offset] = src_handles_right[index];
+ dst_handles_left[i_segment_last] = src_handles_left[next_index];
+
+ for (const int cut : IndexRange(result_size - 1)) {
+ const float parameter = 1.0f / (result_size - cut);
+ calculate_new_bezier_point(dst_positions[offset + cut],
+ dst_handles_right[offset + cut],
+ dst_handles_left[offset + cut + 1],
+ dst_positions[offset + cut + 1],
+ dst_handles_right[offset + cut + 1],
+ dst_handles_left[i_segment_last],
+ src_positions[next_index],
+ parameter);
+ }
+ }
+}
+
+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(
+ [&](StringRefNull name, const AttributeMetaData &meta_data) {
+ std::optional<GSpan> src = src_spline.attributes.get_for_read(name);
+ BLI_assert(src);
+
+ if (!dst_spline.attributes.create(name, meta_data.data_type)) {
+ /* Since the source spline of the same type had the attribute, adding it should work. */
+ BLI_assert_unreachable();
+ }
+
+ std::optional<GMutableSpan> dst = dst_spline.attributes.get_for_write(name);
+ 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_curve_subdivide()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_CURVE_SUBDIVIDE, "Curve Subdivide", NODE_CLASS_GEOMETRY, 0);
+ node_type_socket_templates(&ntype, geo_node_curve_subdivide_in, geo_node_curve_subdivide_out);
+ ntype.draw_buttons = geo_node_curve_subdivide_layout;
+ node_type_storage(&ntype,
+ "NodeGeometryCurveSubdivide",
+ node_free_standard_storage,
+ node_copy_standard_storage);
+ node_type_init(&ntype, 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 b6f04352929..c0d817385e2 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
@@ -16,7 +16,7 @@
#include "BLI_array.hh"
#include "BLI_float4x4.hh"
-#include "BLI_timeit.hh"
+#include "BLI_task.hh"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
@@ -47,8 +47,8 @@ static void vert_extrude_to_mesh_data(const Spline &spline,
const float3 profile_vert,
MutableSpan<MVert> r_verts,
MutableSpan<MEdge> r_edges,
- int &vert_offset,
- int &edge_offset)
+ int vert_offset,
+ int edge_offset)
{
Span<float3> positions = spline.evaluated_positions();
@@ -85,10 +85,10 @@ static void spline_extrude_to_mesh_data(const Spline &spline,
MutableSpan<MEdge> r_edges,
MutableSpan<MLoop> r_loops,
MutableSpan<MPoly> r_polys,
- int &vert_offset,
- int &edge_offset,
- int &loop_offset,
- int &poly_offset)
+ int vert_offset,
+ int edge_offset,
+ int loop_offset,
+ int poly_offset)
{
const int spline_vert_len = spline.evaluated_points_size();
const int spline_edge_len = spline.evaluated_edges_size();
@@ -181,7 +181,7 @@ static void spline_extrude_to_mesh_data(const Spline &spline,
Span<float3> normals = spline.evaluated_normals();
Span<float3> profile_positions = profile_spline.evaluated_positions();
- GVArray_Typed<float> radii = spline.interpolate_to_evaluated_points(spline.radii());
+ GVArray_Typed<float> radii = spline.interpolate_to_evaluated(spline.radii());
for (const int i_ring : IndexRange(spline_vert_len)) {
float4x4 point_matrix = float4x4::from_normalized_axis_data(
positions[i_ring], normals[i_ring], tangents[i_ring]);
@@ -207,63 +207,114 @@ static void spline_extrude_to_mesh_data(const Spline &spline,
}
}
-static Mesh *curve_to_mesh_calculate(const CurveEval &curve, const CurveEval &profile_curve)
+static inline int spline_extrude_vert_size(const Spline &curve, const Spline &profile)
{
- int profile_vert_total = 0;
- int profile_edge_total = 0;
- for (const SplinePtr &profile_spline : profile_curve.splines()) {
- profile_vert_total += profile_spline->evaluated_points_size();
- profile_edge_total += profile_spline->evaluated_edges_size();
- }
+ return curve.evaluated_points_size() * profile.evaluated_points_size();
+}
- int vert_total = 0;
- int edge_total = 0;
- int poly_total = 0;
- for (const SplinePtr &spline : curve.splines()) {
- const int spline_vert_len = spline->evaluated_points_size();
- const int spline_edge_len = spline->evaluated_edges_size();
- vert_total += spline_vert_len * profile_vert_total;
- poly_total += spline_edge_len * profile_edge_total;
-
- /* Add the ring edges, with one ring for every curve vertex, and the edge loops
- * that run along the length of the curve, starting on the first profile. */
- edge_total += profile_edge_total * spline_vert_len + profile_vert_total * spline_edge_len;
- }
- const int corner_total = poly_total * 4;
+static inline int spline_extrude_edge_size(const Spline &curve, const Spline &profile)
+{
+ /* Add the ring edges, with one ring for every curve vertex, and the edge loops
+ * that run along the length of the curve, starting on the first profile. */
+ return curve.evaluated_points_size() * profile.evaluated_edges_size() +
+ curve.evaluated_edges_size() * profile.evaluated_points_size();
+}
- if (vert_total == 0) {
- return nullptr;
- }
+static inline int spline_extrude_loop_size(const Spline &curve, const Spline &profile)
+{
+ return curve.evaluated_edges_size() * profile.evaluated_edges_size() * 4;
+}
- Mesh *mesh = BKE_mesh_new_nomain(vert_total, edge_total, 0, corner_total, poly_total);
- BKE_id_material_eval_ensure_default_slot(&mesh->id);
- MutableSpan<MVert> verts{mesh->mvert, mesh->totvert};
- MutableSpan<MEdge> edges{mesh->medge, mesh->totedge};
- MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop};
- MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly};
- mesh->flag |= ME_AUTOSMOOTH;
- mesh->smoothresh = DEG2RADF(180.0f);
+static inline int spline_extrude_poly_size(const Spline &curve, const Spline &profile)
+{
+ return curve.evaluated_edges_size() * profile.evaluated_edges_size();
+}
+struct ResultOffsets {
+ Array<int> vert;
+ Array<int> edge;
+ Array<int> loop;
+ Array<int> poly;
+};
+static ResultOffsets calculate_result_offsets(Span<SplinePtr> profiles, Span<SplinePtr> curves)
+{
+ const int total = profiles.size() * curves.size();
+ Array<int> vert(total + 1);
+ Array<int> edge(total + 1);
+ Array<int> loop(total + 1);
+ Array<int> poly(total + 1);
+
+ int mesh_index = 0;
int vert_offset = 0;
int edge_offset = 0;
int loop_offset = 0;
int poly_offset = 0;
- for (const SplinePtr &spline : curve.splines()) {
- for (const SplinePtr &profile_spline : profile_curve.splines()) {
- spline_extrude_to_mesh_data(*spline,
- *profile_spline,
- verts,
- edges,
- loops,
- polys,
- vert_offset,
- edge_offset,
- loop_offset,
- poly_offset);
+ for (const int i_spline : curves.index_range()) {
+ for (const int i_profile : profiles.index_range()) {
+ vert[mesh_index] = vert_offset;
+ edge[mesh_index] = edge_offset;
+ loop[mesh_index] = loop_offset;
+ poly[mesh_index] = poly_offset;
+ vert_offset += spline_extrude_vert_size(*curves[i_spline], *profiles[i_profile]);
+ edge_offset += spline_extrude_edge_size(*curves[i_spline], *profiles[i_profile]);
+ loop_offset += spline_extrude_loop_size(*curves[i_spline], *profiles[i_profile]);
+ poly_offset += spline_extrude_poly_size(*curves[i_spline], *profiles[i_profile]);
+ mesh_index++;
}
}
+ vert.last() = vert_offset;
+ edge.last() = edge_offset;
+ loop.last() = loop_offset;
+ poly.last() = poly_offset;
+
+ return {std::move(vert), std::move(edge), std::move(loop), std::move(poly)};
+}
+
+/**
+ * \note Normal calculation is by far the slowest part of calculations relating to the result mesh.
+ * Although it would be a sensible decision to use the better topology information available while
+ * generating the mesh to also generate the normals, that work may wasted if the output mesh is
+ * changed anyway in a way that affects the normals. So currently this code uses the safer /
+ * simpler solution of not calculating normals.
+ */
+static Mesh *curve_to_mesh_calculate(const CurveEval &curve, const CurveEval &profile)
+{
+ Span<SplinePtr> profiles = profile.splines();
+ Span<SplinePtr> curves = curve.splines();
+
+ const ResultOffsets offsets = calculate_result_offsets(profiles, curves);
+ if (offsets.vert.last() == 0) {
+ return nullptr;
+ }
- BKE_mesh_calc_normals(mesh);
+ Mesh *mesh = BKE_mesh_new_nomain(
+ offsets.vert.last(), offsets.edge.last(), 0, offsets.loop.last(), offsets.poly.last());
+ BKE_id_material_eval_ensure_default_slot(&mesh->id);
+ mesh->flag |= ME_AUTOSMOOTH;
+ mesh->smoothresh = DEG2RADF(180.0f);
+ mesh->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
+ mesh->runtime.cd_dirty_poly |= CD_MASK_NORMAL;
+
+ threading::parallel_for(curves.index_range(), 128, [&](IndexRange curves_range) {
+ for (const int i_spline : curves_range) {
+ const int spline_start_index = i_spline * profiles.size();
+ threading::parallel_for(profiles.index_range(), 128, [&](IndexRange profiles_range) {
+ for (const int i_profile : profiles_range) {
+ const int i_mesh = spline_start_index + i_profile;
+ spline_extrude_to_mesh_data(*curves[i_spline],
+ *profiles[i_profile],
+ {mesh->mvert, mesh->totvert},
+ {mesh->medge, mesh->totedge},
+ {mesh->mloop, mesh->totloop},
+ {mesh->mpoly, mesh->totpoly},
+ offsets.vert[i_mesh],
+ offsets.edge[i_mesh],
+ offsets.loop[i_mesh],
+ offsets.poly[i_mesh]);
+ }
+ });
+ }
+ });
return mesh;
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc
new file mode 100644
index 00000000000..2725c625913
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc
@@ -0,0 +1,390 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BLI_array.hh"
+#include "BLI_task.hh"
+#include "BLI_timeit.hh"
+
+#include "BKE_pointcloud.h"
+#include "BKE_spline.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_curve_to_points_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_INT, N_("Count"), 10, 0, 0, 0, 2, 100000},
+ {SOCK_FLOAT, N_("Length"), 0.1f, 0.0f, 0.0f, 0.0f, 0.001f, FLT_MAX, PROP_DISTANCE},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_curve_to_points_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+static void geo_node_curve_to_points_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "mode", 0, "", ICON_NONE);
+}
+
+static void geo_node_curve_to_points_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometryCurveToPoints *data = (NodeGeometryCurveToPoints *)MEM_callocN(
+ sizeof(NodeGeometryCurveToPoints), __func__);
+
+ data->mode = GEO_NODE_CURVE_SAMPLE_COUNT;
+ node->storage = data;
+}
+
+static void geo_node_curve_to_points_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeGeometryCurveToPoints &node_storage = *(NodeGeometryCurveToPoints *)node->storage;
+ const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode;
+
+ bNodeSocket *count_socket = ((bNodeSocket *)node->inputs.first)->next;
+ bNodeSocket *length_socket = count_socket->next;
+
+ nodeSetSocketAvailability(count_socket, mode == GEO_NODE_CURVE_SAMPLE_COUNT);
+ nodeSetSocketAvailability(length_socket, mode == GEO_NODE_CURVE_SAMPLE_LENGTH);
+}
+
+namespace blender::nodes {
+
+/**
+ * Evaluate splines in parallel to speed up the rest of the node's execution.
+ */
+static void evaluate_splines(Span<SplinePtr> splines)
+{
+ threading::parallel_for_each(splines, [](const SplinePtr &spline) {
+ /* These functions fill the corresponding caches on each spline. */
+ spline->evaluated_positions();
+ spline->evaluated_tangents();
+ spline->evaluated_normals();
+ spline->evaluated_lengths();
+ });
+}
+
+static Array<int> calculate_spline_point_offsets(GeoNodeExecParams &params,
+ const GeometryNodeCurveSampleMode mode,
+ const CurveEval &curve,
+ const Span<SplinePtr> splines)
+{
+ const int size = curve.splines().size();
+ switch (mode) {
+ case GEO_NODE_CURVE_SAMPLE_COUNT: {
+ const int count = params.extract_input<int>("Count");
+ if (count < 1) {
+ return {0};
+ }
+ Array<int> offsets(size + 1);
+ for (const int i : offsets.index_range()) {
+ offsets[i] = count * i;
+ }
+ return offsets;
+ }
+ case GEO_NODE_CURVE_SAMPLE_LENGTH: {
+ /* Don't allow asymptotic count increase for low resolution values. */
+ const float resolution = std::max(params.extract_input<float>("Length"), 0.0001f);
+ Array<int> offsets(size + 1);
+ int offset = 0;
+ for (const int i : IndexRange(size)) {
+ offsets[i] = offset;
+ offset += splines[i]->length() / resolution;
+ }
+ offsets.last() = offset;
+ return offsets;
+ }
+ case GEO_NODE_CURVE_SAMPLE_EVALUATED: {
+ return curve.evaluated_point_offsets();
+ }
+ }
+ BLI_assert_unreachable();
+ return {0};
+}
+
+/**
+ * \note This doesn't store a map for spline domain attributes.
+ */
+struct ResultAttributes {
+ int result_size;
+ MutableSpan<float3> positions;
+ MutableSpan<float> radii;
+ MutableSpan<float> tilts;
+
+ Map<std::string, GMutableSpan> point_attributes;
+
+ MutableSpan<float3> tangents;
+ MutableSpan<float3> normals;
+ MutableSpan<float3> rotations;
+};
+
+static GMutableSpan create_attribute_and_retrieve_span(PointCloudComponent &points,
+ const StringRef name,
+ const CustomDataType data_type)
+{
+ points.attribute_try_create(name, ATTR_DOMAIN_POINT, data_type, AttributeInitDefault());
+ WriteAttributeLookup attribute = points.attribute_try_get_for_write(name);
+ BLI_assert(attribute);
+ return attribute.varray->get_internal_span();
+}
+
+template<typename T>
+static MutableSpan<T> create_attribute_and_retrieve_span(PointCloudComponent &points,
+ const StringRef name)
+{
+ GMutableSpan attribute = create_attribute_and_retrieve_span(
+ points, name, bke::cpp_type_to_custom_data_type(CPPType::get<T>()));
+ return attribute.typed<T>();
+}
+
+/**
+ * Create references for all result point cloud attributes to simplify accessing them later on.
+ */
+static ResultAttributes create_point_attributes(PointCloudComponent &points,
+ const CurveEval &curve)
+{
+ ResultAttributes attributes;
+
+ attributes.result_size = points.attribute_domain_size(ATTR_DOMAIN_POINT);
+
+ attributes.positions = create_attribute_and_retrieve_span<float3>(points, "position");
+ attributes.radii = create_attribute_and_retrieve_span<float>(points, "radius");
+ attributes.tilts = create_attribute_and_retrieve_span<float>(points, "tilt");
+
+ /* Because of the invariants of the curve component, we use the attributes of the
+ * first spline as a representative for the attribute meta data all splines. */
+ curve.splines().first()->attributes.foreach_attribute(
+ [&](StringRefNull name, const AttributeMetaData &meta_data) {
+ attributes.point_attributes.add_new(
+ name, create_attribute_and_retrieve_span(points, name, meta_data.data_type));
+ return true;
+ },
+ ATTR_DOMAIN_POINT);
+
+ attributes.tangents = create_attribute_and_retrieve_span<float3>(points, "tangent");
+ attributes.normals = create_attribute_and_retrieve_span<float3>(points, "normal");
+ attributes.rotations = create_attribute_and_retrieve_span<float3>(points, "rotation");
+
+ return attributes;
+}
+
+/**
+ * TODO: For non-poly splines, this has double copies that could be avoided as part
+ * of a general look at optimizing uses of #Spline::interpolate_to_evaluated.
+ */
+static void copy_evaluated_point_attributes(Span<SplinePtr> splines,
+ Span<int> offsets,
+ ResultAttributes &data)
+{
+ threading::parallel_for(splines.index_range(), 64, [&](IndexRange range) {
+ for (const int i : range) {
+ const Spline &spline = *splines[i];
+ const int offset = offsets[i];
+ const int size = offsets[i + 1] - offsets[i];
+
+ data.positions.slice(offset, size).copy_from(spline.evaluated_positions());
+ spline.interpolate_to_evaluated(spline.radii())->materialize(data.radii.slice(offset, size));
+ spline.interpolate_to_evaluated(spline.tilts())->materialize(data.tilts.slice(offset, size));
+
+ for (const Map<std::string, GMutableSpan>::Item &item : data.point_attributes.items()) {
+ const StringRef name = item.key;
+ GMutableSpan point_span = item.value;
+
+ BLI_assert(spline.attributes.get_for_read(name));
+ GSpan spline_span = *spline.attributes.get_for_read(name);
+
+ spline.interpolate_to_evaluated(spline_span)
+ ->materialize(point_span.slice(offset, size).data());
+ }
+
+ data.tangents.slice(offset, size).copy_from(spline.evaluated_tangents());
+ data.normals.slice(offset, size).copy_from(spline.evaluated_normals());
+ }
+ });
+}
+
+static void copy_uniform_sample_point_attributes(Span<SplinePtr> splines,
+ Span<int> offsets,
+ ResultAttributes &data)
+{
+ threading::parallel_for(splines.index_range(), 64, [&](IndexRange range) {
+ for (const int i : range) {
+ const Spline &spline = *splines[i];
+ const int offset = offsets[i];
+ const int size = offsets[i + 1] - offsets[i];
+ if (size == 0) {
+ continue;
+ }
+
+ const Array<float> uniform_samples = spline.sample_uniform_index_factors(size);
+
+ spline.sample_with_index_factors<float3>(
+ spline.evaluated_positions(), uniform_samples, data.positions.slice(offset, size));
+
+ spline.sample_with_index_factors<float>(spline.interpolate_to_evaluated(spline.radii()),
+ uniform_samples,
+ data.radii.slice(offset, size));
+
+ spline.sample_with_index_factors<float>(spline.interpolate_to_evaluated(spline.tilts()),
+ uniform_samples,
+ data.tilts.slice(offset, size));
+
+ for (const Map<std::string, GMutableSpan>::Item &item : data.point_attributes.items()) {
+ const StringRef name = item.key;
+ GMutableSpan point_span = item.value;
+
+ BLI_assert(spline.attributes.get_for_read(name));
+ GSpan spline_span = *spline.attributes.get_for_read(name);
+
+ spline.sample_with_index_factors(*spline.interpolate_to_evaluated(spline_span),
+ uniform_samples,
+ point_span.slice(offset, size));
+ }
+
+ spline.sample_with_index_factors<float3>(
+ spline.evaluated_tangents(), uniform_samples, data.tangents.slice(offset, size));
+ for (float3 &tangent : data.tangents) {
+ tangent.normalize();
+ }
+
+ spline.sample_with_index_factors<float3>(
+ spline.evaluated_normals(), uniform_samples, data.normals.slice(offset, size));
+ for (float3 &normals : data.normals) {
+ normals.normalize();
+ }
+ }
+ });
+}
+
+/**
+ * \note Use attributes from the curve component rather than the attribute data directly on the
+ * attribute storage to allow reading the virtual spline attributes like "cyclic" and "resolution".
+ */
+static void copy_spline_domain_attributes(const CurveComponent &curve_component,
+ Span<int> offsets,
+ PointCloudComponent &points)
+{
+ curve_component.attribute_foreach([&](StringRefNull name, const AttributeMetaData &meta_data) {
+ if (meta_data.domain != ATTR_DOMAIN_CURVE) {
+ return true;
+ }
+ GVArrayPtr spline_attribute = curve_component.attribute_get_for_read(
+ name, ATTR_DOMAIN_CURVE, meta_data.data_type);
+ const CPPType &type = spline_attribute->type();
+
+ OutputAttribute result_attribute = points.attribute_try_get_for_output_only(
+ name, ATTR_DOMAIN_POINT, meta_data.data_type);
+ GMutableSpan result = result_attribute.as_span();
+
+ for (const int i : IndexRange(spline_attribute->size())) {
+ const int offset = offsets[i];
+ const int size = offsets[i + 1] - offsets[i];
+ if (size != 0) {
+ BUFFER_FOR_CPP_TYPE_VALUE(type, buffer);
+ spline_attribute->get(i, buffer);
+ type.fill_initialized(buffer, result[offset], size);
+ }
+ }
+
+ result_attribute.save();
+ return true;
+ });
+}
+
+static void create_default_rotation_attribute(ResultAttributes &data)
+{
+ threading::parallel_for(IndexRange(data.result_size), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ data.rotations[i] = float4x4::from_normalized_axis_data(
+ {0, 0, 0}, data.normals[i], data.tangents[i])
+ .to_euler();
+ }
+ });
+}
+
+static void geo_node_curve_to_points_exec(GeoNodeExecParams params)
+{
+ NodeGeometryCurveToPoints &node_storage = *(NodeGeometryCurveToPoints *)params.node().storage;
+ const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode;
+ 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 CurveComponent &curve_component = *geometry_set.get_component_for_read<CurveComponent>();
+ const CurveEval &curve = *curve_component.get_for_read();
+ const Span<SplinePtr> splines = curve.splines();
+ curve.assert_valid_point_attributes();
+
+ evaluate_splines(splines);
+
+ const Array<int> offsets = calculate_spline_point_offsets(params, mode, curve, splines);
+ const int total_size = offsets.last();
+ if (total_size == 0) {
+ params.set_output("Geometry", GeometrySet());
+ return;
+ }
+
+ GeometrySet result = GeometrySet::create_with_pointcloud(BKE_pointcloud_new_nomain(total_size));
+ PointCloudComponent &point_component = result.get_component_for_write<PointCloudComponent>();
+
+ ResultAttributes new_attributes = create_point_attributes(point_component, curve);
+
+ switch (mode) {
+ case GEO_NODE_CURVE_SAMPLE_COUNT:
+ case GEO_NODE_CURVE_SAMPLE_LENGTH:
+ copy_uniform_sample_point_attributes(splines, offsets, new_attributes);
+ break;
+ case GEO_NODE_CURVE_SAMPLE_EVALUATED:
+ copy_evaluated_point_attributes(splines, offsets, new_attributes);
+ break;
+ }
+
+ copy_spline_domain_attributes(curve_component, offsets, point_component);
+ create_default_rotation_attribute(new_attributes);
+
+ /* The default radius is way too large for points, divide by 10. */
+ for (float &radius : new_attributes.radii) {
+ radius *= 0.1f;
+ }
+
+ params.set_output("Geometry", std::move(result));
+}
+
+} // namespace blender::nodes
+
+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);
+ node_type_socket_templates(&ntype, geo_node_curve_to_points_in, geo_node_curve_to_points_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_curve_to_points_exec;
+ ntype.draw_buttons = geo_node_curve_to_points_layout;
+ node_type_storage(
+ &ntype, "NodeGeometryCurveToPoints", node_free_standard_storage, node_copy_standard_storage);
+ node_type_init(&ntype, geo_node_curve_to_points_init);
+ node_type_update(&ntype, geo_node_curve_to_points_update);
+
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc
index 910adc467d6..b1da2dcd3c4 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc
@@ -120,7 +120,7 @@ static void copy_dynamic_attributes(const CustomDataAttributes &src,
static SplinePtr spline_delete(const Spline &spline, const IndexMask mask)
{
- SplinePtr new_spline = spline.copy_settings();
+ SplinePtr new_spline = spline.copy_only_settings();
new_spline->resize(mask.size());
spline_copy_builtin_attributes(spline, *new_spline, mask);
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 adfd924f185..bc758b59987 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
@@ -23,8 +23,12 @@
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
+#include "NOD_type_conversions.hh"
+
#include "node_geometry_util.hh"
+using blender::fn::GVArray_For_GSpan;
+
static bNodeSocketTemplate geo_node_join_geometry_in[] = {
{SOCK_GEOMETRY,
N_("Geometry"),
@@ -79,7 +83,7 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<const MeshComponent
const Mesh *first_input_mesh = src_components[0]->get_for_read();
Mesh *new_mesh = BKE_mesh_new_nomain(totverts, totedges, 0, totloops, totpolys);
- BKE_mesh_copy_settings(new_mesh, first_input_mesh);
+ BKE_mesh_copy_parameters_for_eval(new_mesh, first_input_mesh);
for (const int i : IndexRange(materials.size())) {
Material *material = materials[i];
@@ -157,35 +161,30 @@ static Array<const GeometryComponent *> to_base_components(Span<const Component
return components;
}
-static Set<std::string> find_all_attribute_names(Span<const GeometryComponent *> components)
+static Map<std::string, AttributeMetaData> get_final_attribute_info(
+ Span<const GeometryComponent *> components, Span<StringRef> ignored_attributes)
{
- Set<std::string> attribute_names;
- for (const GeometryComponent *component : components) {
- Set<std::string> names = component->attribute_names();
- for (const std::string &name : names) {
- attribute_names.add(name);
- }
- }
- return attribute_names;
-}
+ Map<std::string, AttributeMetaData> info;
-static void determine_final_data_type_and_domain(Span<const GeometryComponent *> components,
- StringRef attribute_name,
- CustomDataType *r_type,
- AttributeDomain *r_domain)
-{
- Vector<CustomDataType> data_types;
- Vector<AttributeDomain> domains;
for (const GeometryComponent *component : components) {
- ReadAttributeLookup attribute = component->attribute_try_get_for_read(attribute_name);
- if (attribute) {
- data_types.append(bke::cpp_type_to_custom_data_type(attribute.varray->type()));
- domains.append(attribute.domain);
- }
+ component->attribute_foreach([&](StringRefNull name, const AttributeMetaData &meta_data) {
+ if (ignored_attributes.contains(name)) {
+ return true;
+ }
+ info.add_or_modify(
+ name,
+ [&](AttributeMetaData *meta_data_final) { *meta_data_final = meta_data; },
+ [&](AttributeMetaData *meta_data_final) {
+ meta_data_final->data_type = blender::bke::attribute_data_type_highest_complexity(
+ {meta_data_final->data_type, meta_data.data_type});
+ meta_data_final->domain = blender::bke::attribute_domain_highest_priority(
+ {meta_data_final->domain, meta_data.domain});
+ });
+ return true;
+ });
}
- *r_type = bke::attribute_data_type_highest_complexity(data_types);
- *r_domain = bke::attribute_domain_highest_priority(domains);
+ return info;
}
static void fill_new_attribute(Span<const GeometryComponent *> src_components,
@@ -219,23 +218,20 @@ static void join_attributes(Span<const GeometryComponent *> src_components,
GeometryComponent &result,
Span<StringRef> ignored_attributes = {})
{
- Set<std::string> attribute_names = find_all_attribute_names(src_components);
- for (StringRef name : ignored_attributes) {
- attribute_names.remove(name);
- }
+ const Map<std::string, AttributeMetaData> info = get_final_attribute_info(src_components,
+ ignored_attributes);
- for (const std::string &attribute_name : attribute_names) {
- CustomDataType data_type;
- AttributeDomain domain;
- determine_final_data_type_and_domain(src_components, attribute_name, &data_type, &domain);
+ for (const Map<std::string, AttributeMetaData>::Item &item : info.items()) {
+ const StringRef name = item.key;
+ const AttributeMetaData &meta_data = item.value;
OutputAttribute write_attribute = result.attribute_try_get_for_output_only(
- attribute_name, domain, data_type);
+ name, meta_data.domain, meta_data.data_type);
if (!write_attribute) {
continue;
}
GMutableSpan dst_span = write_attribute.as_span();
- fill_new_attribute(src_components, attribute_name, data_type, domain, dst_span);
+ fill_new_attribute(src_components, name, meta_data.data_type, meta_data.domain, dst_span);
write_attribute.save();
}
}
@@ -306,6 +302,127 @@ static void join_components(Span<const VolumeComponent *> src_components, Geomet
UNUSED_VARS(src_components, dst_component);
}
+/**
+ * \note This takes advantage of the fact that creating attributes on joined curves never
+ * changes a point attribute into a spline attribute; it is always the other way around.
+ */
+static void ensure_control_point_attribute(const StringRef name,
+ const CustomDataType data_type,
+ Span<CurveComponent *> src_components,
+ CurveEval &result)
+{
+ MutableSpan<SplinePtr> splines = result.splines();
+ const CPPType &type = *bke::custom_data_type_to_cpp_type(data_type);
+
+ /* In order to fill point attributes with spline domain attribute values where necessary, keep
+ * track of the curve each spline came from while iterating over the splines in the result. */
+ int src_component_index = 0;
+ int spline_index_in_component = 0;
+ const CurveEval *current_curve = src_components[src_component_index]->get_for_read();
+
+ for (SplinePtr &spline : splines) {
+ std::optional<GSpan> attribute = spline->attributes.get_for_read(name);
+
+ if (attribute) {
+ if (attribute->type() != type) {
+ /* In this case, the attribute exists, but it has the wrong type. So create a buffer
+ * for the converted values, do the conversion, and then replace the attribute. */
+ void *converted_buffer = MEM_mallocN_aligned(
+ spline->size() * type.size(), type.alignment(), __func__);
+
+ const DataTypeConversions &conversions = blender::nodes::get_implicit_type_conversions();
+ conversions.try_convert(std::make_unique<GVArray_For_GSpan>(*attribute), type)
+ ->materialize(converted_buffer);
+
+ spline->attributes.remove(name);
+ spline->attributes.create_by_move(name, data_type, converted_buffer);
+ }
+ }
+ else {
+ spline->attributes.create(name, data_type);
+
+ if (current_curve->attributes.get_for_read(name)) {
+ /* In this case the attribute did not exist, but there is a spline domain attribute
+ * we can retrieve a value from, as a spline to point domain conversion. So fill the
+ * new attribute with the value for this spline. */
+ GVArrayPtr current_curve_attribute = current_curve->attributes.get_for_read(
+ name, data_type, nullptr);
+
+ BLI_assert(spline->attributes.get_for_read(name));
+ std::optional<GMutableSpan> new_attribute = spline->attributes.get_for_write(name);
+
+ BUFFER_FOR_CPP_TYPE_VALUE(type, buffer);
+ current_curve_attribute->get(spline_index_in_component, buffer);
+ type.fill_initialized(buffer, new_attribute->data(), new_attribute->size());
+ }
+ }
+
+ /* Move to the next spline and maybe the next input component. */
+ spline_index_in_component++;
+ if (spline != splines.last() && spline_index_in_component >= current_curve->splines().size()) {
+ src_component_index++;
+ spline_index_in_component = 0;
+
+ current_curve = src_components[src_component_index]->get_for_read();
+ }
+ }
+}
+
+/**
+ * Fill data for an attribute on the new curve based on all source curves.
+ */
+static void ensure_spline_attribute(const StringRef name,
+ const CustomDataType data_type,
+ Span<CurveComponent *> src_components,
+ CurveEval &result)
+{
+ const CPPType &type = *bke::custom_data_type_to_cpp_type(data_type);
+
+ result.attributes.create(name, data_type);
+ GMutableSpan result_attribute = *result.attributes.get_for_write(name);
+
+ int offset = 0;
+ for (const CurveComponent *component : src_components) {
+ const CurveEval &curve = *component->get_for_read();
+ const int size = curve.splines().size();
+ if (size == 0) {
+ continue;
+ }
+ GVArrayPtr read_attribute = curve.attributes.get_for_read(name, data_type, nullptr);
+ GVArray_GSpan src_span{*read_attribute};
+
+ const void *src_buffer = src_span.data();
+ type.copy_to_initialized_n(src_buffer, result_attribute[offset], size);
+
+ offset += size;
+ }
+}
+
+/**
+ * Special handling for copying spline attributes. This is necessary because we move the splines
+ * out of the source components instead of copying them, meaning we can no longer access point
+ * domain attributes on the source components.
+ *
+ * \warning Splines have been moved out of the source components at this point, so it
+ * is important to only read curve-level data (spline domain attributes) from them.
+ */
+static void join_curve_attributes(const Map<std::string, AttributeMetaData> &info,
+ Span<CurveComponent *> src_components,
+ CurveEval &result)
+{
+ for (const Map<std::string, AttributeMetaData>::Item &item : info.items()) {
+ const StringRef name = item.key;
+ const AttributeMetaData meta_data = item.value;
+
+ if (meta_data.domain == ATTR_DOMAIN_CURVE) {
+ ensure_spline_attribute(name, meta_data.data_type, src_components, result);
+ }
+ else {
+ ensure_control_point_attribute(name, meta_data.data_type, src_components, result);
+ }
+ }
+}
+
static void join_curve_components(MutableSpan<GeometrySet> src_geometry_sets, GeometrySet &result)
{
Vector<CurveComponent *> src_components;
@@ -328,6 +445,11 @@ static void join_curve_components(MutableSpan<GeometrySet> src_geometry_sets, Ge
return;
}
+ /* Retrieve attribute info before moving the splines out of the input components. */
+ const Map<std::string, AttributeMetaData> info = get_final_attribute_info(
+ {(const GeometryComponent **)src_components.data(), src_components.size()},
+ {"position", "radius", "tilt", "cyclic", "resolution"});
+
CurveComponent &dst_component = result.get_component_for_write<CurveComponent>();
CurveEval *dst_curve = new CurveEval();
for (CurveComponent *component : src_components) {
@@ -336,14 +458,9 @@ static void join_curve_components(MutableSpan<GeometrySet> src_geometry_sets, Ge
dst_curve->add_spline(std::move(spline));
}
}
-
- /* For now, remove all custom attributes, since they might have different types,
- * or an attribute might not exist on all splines. */
dst_curve->attributes.reallocate(dst_curve->splines().size());
- CustomData_reset(&dst_curve->attributes.data);
- for (SplinePtr &spline : dst_curve->splines()) {
- CustomData_reset(&spline->attributes.data);
- }
+
+ join_curve_attributes(info, src_components, *dst_curve);
dst_component.replace(dst_curve);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc
index 9651301cb34..3a93bc22b7e 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc
@@ -43,6 +43,7 @@ Mesh *create_cube_mesh(const float size)
const BMeshCreateParams bmcp = {true};
const BMAllocTemplate allocsize = {8, 12, 24, 6};
BMesh *bm = BM_mesh_create(&allocsize, &bmcp);
+ BM_data_layer_add_named(bm, &bm->ldata, CD_MLOOPUV, nullptr);
BMO_op_callf(bm,
BMO_FLAG_DEFAULTS,
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc
index a3a1b72006c..22195b9e8db 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc
@@ -26,7 +26,7 @@
static bNodeSocketTemplate geo_node_mesh_primitive_ico_sphere_in[] = {
{SOCK_FLOAT, N_("Radius"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE},
- {SOCK_INT, N_("Subdivisions"), 1, 0, 0, 0, 0, 7},
+ {SOCK_INT, N_("Subdivisions"), 1, 0, 0, 0, 1, 7},
{-1, ""},
};
@@ -44,6 +44,7 @@ static Mesh *create_ico_sphere_mesh(const int subdivisions, const float radius)
const BMeshCreateParams bmcp = {true};
const BMAllocTemplate allocsize = {0, 0, 0, 0};
BMesh *bm = BM_mesh_create(&allocsize, &bmcp);
+ BM_data_layer_add_named(bm, &bm->ldata, CD_MLOOPUV, nullptr);
BMO_op_callf(bm,
BMO_FLAG_DEFAULTS,
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc
index 0fb7910c904..637003a46c7 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc
@@ -62,7 +62,7 @@ static void copy_attributes_to_points(CurveEval &curve,
if (source_attribute_names.contains_as("tilt")) {
const GVArray_Typed<float> tilt_attribute = mesh_component.attribute_get_for_read<float>(
"tilt", ATTR_DOMAIN_POINT, 0.0f);
- parallel_for(splines.index_range(), 256, [&](IndexRange range) {
+ threading::parallel_for(splines.index_range(), 256, [&](IndexRange range) {
for (const int i : range) {
copy_attribute_to_points<float>(
*tilt_attribute, point_to_vert_maps[i], splines[i]->tilts());
@@ -73,7 +73,7 @@ static void copy_attributes_to_points(CurveEval &curve,
if (source_attribute_names.contains_as("radius")) {
const GVArray_Typed<float> radius_attribute = mesh_component.attribute_get_for_read<float>(
"radius", ATTR_DOMAIN_POINT, 1.0f);
- parallel_for(splines.index_range(), 256, [&](IndexRange range) {
+ threading::parallel_for(splines.index_range(), 256, [&](IndexRange range) {
for (const int i : range) {
copy_attribute_to_points<float>(
*radius_attribute, point_to_vert_maps[i], splines[i]->radii());
@@ -97,7 +97,7 @@ static void copy_attributes_to_points(CurveEval &curve,
const CustomDataType data_type = bke::cpp_type_to_custom_data_type(mesh_attribute->type());
- parallel_for(splines.index_range(), 128, [&](IndexRange range) {
+ threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) {
for (const int i : range) {
/* Create attribute on the spline points. */
splines[i]->attributes.create(name, data_type);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc b/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc
index e52ab1b2127..b119b7b31e9 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc
@@ -189,7 +189,7 @@ static void add_instances_from_component(InstancesComponent &instances,
* (anything except for collection mode with "Whole Collection" turned off). */
if (possible_handles.size() == 1) {
const int handle = possible_handles.first();
- parallel_for(IndexRange(domain_size), 1024, [&](IndexRange range) {
+ threading::parallel_for(IndexRange(domain_size), 1024, [&](IndexRange range) {
for (const int i : range) {
handles[i] = handle;
transforms[i] = float4x4::from_loc_eul_scale(positions[i], rotations[i], scales[i]);
@@ -200,7 +200,7 @@ static void add_instances_from_component(InstancesComponent &instances,
else {
const int seed = params.get_input<int>("Seed");
Array<uint32_t> ids = get_geometry_element_ids_as_uints(src_geometry, ATTR_DOMAIN_POINT);
- parallel_for(IndexRange(domain_size), 1024, [&](IndexRange range) {
+ threading::parallel_for(IndexRange(domain_size), 1024, [&](IndexRange range) {
for (const int i : range) {
const int index = BLI_hash_int_2d(ids[i], seed) % possible_handles.size();
const int handle = possible_handles[index];
diff --git a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc
new file mode 100644
index 00000000000..bfd6027e0fc
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc
@@ -0,0 +1,321 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "DNA_mesh_types.h"
+
+#include "BKE_bvhutils.h"
+#include "BKE_mesh_sample.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_raycast_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_GEOMETRY, N_("Target Geometry")},
+ {SOCK_STRING, N_("Ray Direction")},
+ {SOCK_VECTOR, N_("Ray Direction"), 0.0, 0.0, 1.0, 0.0, -FLT_MAX, FLT_MAX},
+ {SOCK_STRING, N_("Ray Length")},
+ {SOCK_FLOAT, N_("Ray Length"), 100.0, 0.0, 0.0, 0.0, 0.0f, FLT_MAX, PROP_DISTANCE},
+ {SOCK_STRING, N_("Target Attribute")},
+ {SOCK_STRING, N_("Is Hit")},
+ {SOCK_STRING, N_("Hit Position")},
+ {SOCK_STRING, N_("Hit Normal")},
+ {SOCK_STRING, N_("Hit Distance")},
+ {SOCK_STRING, N_("Hit Attribute")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_raycast_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+static void geo_node_raycast_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
+ uiItemR(layout, ptr, "mapping", 0, IFACE_("Mapping"), ICON_NONE);
+ uiItemR(layout, ptr, "input_type_ray_direction", 0, IFACE_("Ray Direction"), ICON_NONE);
+ uiItemR(layout, ptr, "input_type_ray_length", 0, IFACE_("Ray Length"), ICON_NONE);
+}
+
+static void geo_node_raycast_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometryRaycast *data = (NodeGeometryRaycast *)MEM_callocN(sizeof(NodeGeometryRaycast),
+ __func__);
+ data->input_type_ray_direction = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
+ data->input_type_ray_length = GEO_NODE_ATTRIBUTE_INPUT_FLOAT;
+ node->storage = data;
+}
+
+static void geo_node_raycast_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeGeometryRaycast *node_storage = (NodeGeometryRaycast *)node->storage;
+ blender::nodes::update_attribute_input_socket_availabilities(
+ *node,
+ "Ray Direction",
+ (GeometryNodeAttributeInputMode)node_storage->input_type_ray_direction);
+ blender::nodes::update_attribute_input_socket_availabilities(
+ *node, "Ray Length", (GeometryNodeAttributeInputMode)node_storage->input_type_ray_length);
+}
+
+namespace blender::nodes {
+
+static void raycast_to_mesh(const Mesh *mesh,
+ const VArray<float3> &ray_origins,
+ const VArray<float3> &ray_directions,
+ const VArray<float> &ray_lengths,
+ const MutableSpan<bool> r_hit,
+ const MutableSpan<int> r_hit_indices,
+ const MutableSpan<float3> r_hit_positions,
+ const MutableSpan<float3> r_hit_normals,
+ const MutableSpan<float> r_hit_distances)
+{
+ BLI_assert(ray_origins.size() == ray_directions.size());
+ BLI_assert(ray_origins.size() == ray_lengths.size());
+ BLI_assert(ray_origins.size() == r_hit.size() || r_hit.is_empty());
+ BLI_assert(ray_origins.size() == r_hit_indices.size() || r_hit_indices.is_empty());
+ BLI_assert(ray_origins.size() == r_hit_positions.size() || r_hit_positions.is_empty());
+ BLI_assert(ray_origins.size() == r_hit_normals.size() || r_hit_normals.is_empty());
+ BLI_assert(ray_origins.size() == r_hit_distances.size() || r_hit_distances.is_empty());
+
+ BVHTreeFromMesh tree_data;
+ BKE_bvhtree_from_mesh_get(&tree_data, const_cast<Mesh *>(mesh), BVHTREE_FROM_LOOPTRI, 4);
+
+ if (tree_data.tree != nullptr) {
+ for (const int i : ray_origins.index_range()) {
+ const float ray_length = ray_lengths[i];
+ const float3 ray_origin = ray_origins[i];
+ const float3 ray_direction = ray_directions[i].normalized();
+
+ BVHTreeRayHit hit;
+ hit.index = -1;
+ hit.dist = ray_length;
+ if (BLI_bvhtree_ray_cast(tree_data.tree,
+ ray_origin,
+ ray_direction,
+ 0.0f,
+ &hit,
+ tree_data.raycast_callback,
+ &tree_data) != -1) {
+ if (!r_hit.is_empty()) {
+ r_hit[i] = hit.index >= 0;
+ }
+ if (!r_hit_indices.is_empty()) {
+ /* Index should always be a valid looptri index, use 0 when hit failed. */
+ r_hit_indices[i] = max_ii(hit.index, 0);
+ }
+ if (!r_hit_positions.is_empty()) {
+ r_hit_positions[i] = hit.co;
+ }
+ if (!r_hit_normals.is_empty()) {
+ r_hit_normals[i] = hit.no;
+ }
+ if (!r_hit_distances.is_empty()) {
+ r_hit_distances[i] = hit.dist;
+ }
+ }
+ else {
+ if (!r_hit.is_empty()) {
+ r_hit[i] = false;
+ }
+ if (!r_hit_indices.is_empty()) {
+ r_hit_indices[i] = 0;
+ }
+ if (!r_hit_positions.is_empty()) {
+ r_hit_positions[i] = float3(0.0f, 0.0f, 0.0f);
+ }
+ if (!r_hit_normals.is_empty()) {
+ r_hit_normals[i] = float3(0.0f, 0.0f, 0.0f);
+ }
+ if (!r_hit_distances.is_empty()) {
+ r_hit_distances[i] = ray_length;
+ }
+ }
+ }
+
+ free_bvhtree_from_mesh(&tree_data);
+ }
+}
+
+static bke::mesh_surface_sample::eAttributeMapMode get_map_mode(
+ GeometryNodeRaycastMapMode map_mode)
+{
+ switch (map_mode) {
+ case GEO_NODE_RAYCAST_INTERPOLATED:
+ return bke::mesh_surface_sample::eAttributeMapMode::INTERPOLATED;
+ default:
+ case GEO_NODE_RAYCAST_NEAREST:
+ return bke::mesh_surface_sample::eAttributeMapMode::NEAREST;
+ }
+}
+
+static void raycast_from_points(const GeoNodeExecParams &params,
+ const GeometrySet &src_geometry,
+ GeometryComponent &dst_component,
+ const StringRef hit_name,
+ const StringRef hit_position_name,
+ const StringRef hit_normal_name,
+ const StringRef hit_distance_name,
+ const Span<std::string> hit_attribute_names,
+ const Span<std::string> hit_attribute_output_names)
+{
+ BLI_assert(hit_attribute_names.size() == hit_attribute_output_names.size());
+
+ const MeshComponent *src_mesh_component = src_geometry.get_component_for_read<MeshComponent>();
+ if (src_mesh_component == nullptr) {
+ return;
+ }
+ const Mesh *src_mesh = src_mesh_component->get_for_read();
+ if (src_mesh == nullptr) {
+ return;
+ }
+ if (src_mesh->totpoly == 0) {
+ return;
+ }
+
+ const NodeGeometryRaycast &storage = *(const NodeGeometryRaycast *)params.node().storage;
+ bke::mesh_surface_sample::eAttributeMapMode map_mode = get_map_mode(
+ (GeometryNodeRaycastMapMode)storage.mapping);
+ const AttributeDomain result_domain = ATTR_DOMAIN_POINT;
+
+ GVArray_Typed<float3> ray_origins = dst_component.attribute_get_for_read<float3>(
+ "position", result_domain, {0, 0, 0});
+ GVArray_Typed<float3> ray_directions = params.get_input_attribute<float3>(
+ "Ray Direction", dst_component, result_domain, {0, 0, 0});
+ GVArray_Typed<float> ray_lengths = params.get_input_attribute<float>(
+ "Ray Length", dst_component, result_domain, 0);
+
+ OutputAttribute_Typed<bool> hit_attribute =
+ dst_component.attribute_try_get_for_output_only<bool>(hit_name, result_domain);
+ OutputAttribute_Typed<float3> hit_position_attribute =
+ dst_component.attribute_try_get_for_output_only<float3>(hit_position_name, result_domain);
+ OutputAttribute_Typed<float3> hit_normal_attribute =
+ dst_component.attribute_try_get_for_output_only<float3>(hit_normal_name, result_domain);
+ OutputAttribute_Typed<float> hit_distance_attribute =
+ dst_component.attribute_try_get_for_output_only<float>(hit_distance_name, result_domain);
+
+ /* Positions and looptri indices are always needed for interpolation,
+ * so create temporary arrays if no output attribute is given.
+ */
+ Array<int> hit_indices;
+ Array<float3> hit_positions_internal;
+ if (!hit_attribute_names.is_empty()) {
+ hit_indices.reinitialize(ray_origins->size());
+
+ if (!hit_position_attribute) {
+ hit_positions_internal.reinitialize(ray_origins->size());
+ }
+ }
+ const MutableSpan<bool> is_hit = hit_attribute ? hit_attribute.as_span() : MutableSpan<bool>();
+ const MutableSpan<float3> hit_positions = hit_position_attribute ?
+ hit_position_attribute.as_span() :
+ hit_positions_internal;
+ const MutableSpan<float3> hit_normals = hit_normal_attribute ? hit_normal_attribute.as_span() :
+ MutableSpan<float3>();
+ const MutableSpan<float> hit_distances = hit_distance_attribute ?
+ hit_distance_attribute.as_span() :
+ MutableSpan<float>();
+
+ raycast_to_mesh(src_mesh,
+ ray_origins,
+ ray_directions,
+ ray_lengths,
+ is_hit,
+ hit_indices,
+ hit_positions,
+ hit_normals,
+ hit_distances);
+
+ hit_attribute.save();
+ hit_position_attribute.save();
+ hit_normal_attribute.save();
+ hit_distance_attribute.save();
+
+ /* Custom interpolated attributes */
+ bke::mesh_surface_sample::MeshAttributeInterpolator interp(src_mesh, hit_positions, hit_indices);
+ for (const int i : hit_attribute_names.index_range()) {
+ const std::optional<AttributeMetaData> meta_data = src_mesh_component->attribute_get_meta_data(
+ hit_attribute_names[i]);
+ if (meta_data) {
+ ReadAttributeLookup hit_attribute = src_mesh_component->attribute_try_get_for_read(
+ hit_attribute_names[i]);
+ OutputAttribute hit_attribute_output = dst_component.attribute_try_get_for_output_only(
+ hit_attribute_output_names[i], result_domain, meta_data->data_type);
+
+ interp.sample_attribute(hit_attribute, hit_attribute_output, map_mode);
+
+ hit_attribute_output.save();
+ }
+ }
+}
+
+static void geo_node_raycast_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ GeometrySet cast_geometry_set = params.extract_input<GeometrySet>("Target Geometry");
+
+ const std::string hit_name = params.extract_input<std::string>("Is Hit");
+ const std::string hit_position_name = params.extract_input<std::string>("Hit Position");
+ const std::string hit_normal_name = params.extract_input<std::string>("Hit Normal");
+ const std::string hit_distance_name = params.extract_input<std::string>("Hit Distance");
+
+ const Array<std::string> hit_attribute_names = {
+ params.extract_input<std::string>("Target Attribute")};
+ const Array<std::string> hit_attribute_output_names = {
+ params.extract_input<std::string>("Hit Attribute")};
+
+ geometry_set = bke::geometry_set_realize_instances(geometry_set);
+ cast_geometry_set = bke::geometry_set_realize_instances(cast_geometry_set);
+
+ static const Array<GeometryComponentType> SupportedTypes = {
+ GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE};
+ for (GeometryComponentType geo_type : SupportedTypes) {
+ if (geometry_set.has(geo_type)) {
+ raycast_from_points(params,
+ cast_geometry_set,
+ geometry_set.get_component_for_write(geo_type),
+ hit_name,
+ hit_position_name,
+ hit_normal_name,
+ hit_distance_name,
+ hit_attribute_names,
+ hit_attribute_output_names);
+ }
+ }
+
+ params.set_output("Geometry", geometry_set);
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_raycast()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_RAYCAST, "Raycast", NODE_CLASS_GEOMETRY, 0);
+ node_type_socket_templates(&ntype, geo_node_raycast_in, geo_node_raycast_out);
+ node_type_size_preset(&ntype, NODE_SIZE_LARGE);
+ node_type_init(&ntype, geo_node_raycast_init);
+ node_type_update(&ntype, geo_node_raycast_update);
+ node_type_storage(
+ &ntype, "NodeGeometryRaycast", node_free_standard_storage, node_copy_standard_storage);
+ ntype.geometry_node_execute = blender::nodes::geo_node_raycast_exec;
+ ntype.draw_buttons = geo_node_raycast_layout;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_select_by_material.cc b/source/blender/nodes/geometry/nodes/node_geo_select_by_material.cc
index 9bc963eec43..ee114741a77 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_select_by_material.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_select_by_material.cc
@@ -60,7 +60,7 @@ static void select_mesh_by_material(const Mesh &mesh,
material_indices.append(i);
}
}
- parallel_for(r_selection.index_range(), 1024, [&](IndexRange range) {
+ threading::parallel_for(r_selection.index_range(), 1024, [&](IndexRange range) {
for (const int i : range) {
r_selection[i] = material_indices.contains(mesh.mpoly[i].mat_nr);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_separate_components.cc b/source/blender/nodes/geometry/nodes/node_geo_separate_components.cc
new file mode 100644
index 00000000000..bdc3e56783c
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_separate_components.cc
@@ -0,0 +1,77 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_join_geometry_in[]{
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_join_geometry_out[]{
+ {SOCK_GEOMETRY, N_("Mesh")},
+ {SOCK_GEOMETRY, N_("Point Cloud")},
+ {SOCK_GEOMETRY, N_("Curve")},
+ {SOCK_GEOMETRY, N_("Volume")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+
+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;
+
+ if (geometry_set.has<MeshComponent>()) {
+ meshes.add(*geometry_set.get_component_for_read<MeshComponent>());
+ }
+ if (geometry_set.has<PointCloudComponent>()) {
+ point_clouds.add(*geometry_set.get_component_for_read<PointCloudComponent>());
+ }
+ if (geometry_set.has<CurveComponent>()) {
+ curves.add(*geometry_set.get_component_for_read<CurveComponent>());
+ }
+ if (geometry_set.has<VolumeComponent>()) {
+ volumes.add(*geometry_set.get_component_for_read<VolumeComponent>());
+ }
+
+ params.set_output("Mesh", meshes);
+ params.set_output("Point Cloud", point_clouds);
+ params.set_output("Curve", curves);
+ params.set_output("Volume", volumes);
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_separate_components()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_SEPARATE_COMPONENTS, "Separate Components", NODE_CLASS_GEOMETRY, 0);
+ node_type_socket_templates(&ntype, geo_node_join_geometry_in, geo_node_join_geometry_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_separate_components_exec;
+ nodeRegisterType(&ntype);
+}