Blender V4.3
add_curves_on_mesh.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "BLI_math_vector.hh"
6
7#include "BLI_kdtree.h"
9#include "BLI_math_matrix.h"
10#include "BLI_math_matrix.hh"
11#include "BLI_math_rotation.h"
12#include "BLI_task.hh"
13
14#include "BKE_attribute_math.hh"
15#include "BKE_mesh.hh"
16#include "BKE_mesh_sample.hh"
17
20
27namespace blender::geometry {
28
29using bke::CurvesGeometry;
30
32 /* Curve index of the neighbor. */
33 int index;
34 /* The weights of all neighbors of a new curve add up to 1. */
35 float weight;
36};
37
38static constexpr int max_neighbors = 5;
40
42 const float3 &bary_coord,
43 const Span<float3> corner_normals)
44{
46 bary_coord, corner_tri, corner_normals);
47 return math::normalize(value);
48}
49
51 const float3 &b,
53{
54 const float step = math::rcp(float(dst.size() - 1));
55 for (const int i : dst.index_range()) {
56 dst[i] = bke::attribute_math::mix2(i * step, a, b);
57 }
58}
59
61 const KDTree_3d &old_roots_kdtree)
62{
63 const int tot_added_curves = root_positions.size();
64 Array<NeighborCurves> neighbors_per_curve(tot_added_curves);
65 threading::parallel_for(IndexRange(tot_added_curves), 128, [&](const IndexRange range) {
66 for (const int i : range) {
67 const float3 root = root_positions[i];
68 std::array<KDTreeNearest_3d, max_neighbors> nearest_n;
69 const int found_neighbors = BLI_kdtree_3d_find_nearest_n(
70 &old_roots_kdtree, root, nearest_n.data(), max_neighbors);
71 float tot_weight = 0.0f;
72 for (const int neighbor_i : IndexRange(found_neighbors)) {
73 KDTreeNearest_3d &nearest = nearest_n[neighbor_i];
74 const float weight = 1.0f / std::max(nearest.dist, 0.00001f);
75 tot_weight += weight;
76 neighbors_per_curve[i].append({nearest.index, weight});
77 }
78 /* Normalize weights. */
79 for (NeighborCurve &neighbor : neighbors_per_curve[i]) {
80 neighbor.weight /= tot_weight;
81 }
82 }
83 });
84 return neighbors_per_curve;
85}
86
87template<typename T, typename GetValueF>
89 const T &fallback,
90 const GetValueF &get_value_from_neighbor,
91 MutableSpan<T> r_interpolated_values)
92{
93 bke::attribute_math::DefaultMixer<T> mixer{r_interpolated_values};
94 threading::parallel_for(r_interpolated_values.index_range(), 512, [&](const IndexRange range) {
95 for (const int i : range) {
96 const NeighborCurves &neighbors = neighbors_per_curve[i];
97 if (neighbors.is_empty()) {
98 mixer.mix_in(i, fallback, 1.0f);
99 }
100 else {
101 for (const NeighborCurve &neighbor : neighbors) {
102 const T neighbor_value = get_value_from_neighbor(neighbor.index);
103 mixer.mix_in(i, neighbor_value, neighbor.weight);
104 }
105 }
106 }
107 mixer.finalize(range);
108 });
109}
110
112 const int old_curves_num,
113 const Span<float3> root_positions_cu,
114 const Span<float> new_lengths_cu,
115 const Span<float3> new_normals_su,
116 const float4x4 &surface_to_curves_normal_mat)
117{
118 const int added_curves_num = root_positions_cu.size();
119 const OffsetIndices points_by_curve = curves.points_by_curve();
120 MutableSpan<float3> positions_cu = curves.positions_for_write();
121 threading::parallel_for(IndexRange(added_curves_num), 256, [&](const IndexRange range) {
122 for (const int i : range) {
123 const int curve_i = old_curves_num + i;
124 const IndexRange points = points_by_curve[curve_i];
125 const float3 &root_cu = root_positions_cu[i];
126 const float length = new_lengths_cu[i];
127 const float3 &normal_su = new_normals_su[i];
128 const float3 normal_cu = math::normalize(
129 math::transform_direction(surface_to_curves_normal_mat, normal_su));
130 const float3 tip_cu = root_cu + length * normal_cu;
131
132 calc_straight_curve_positions(root_cu, tip_cu, positions_cu.slice(points));
133 }
134 });
135}
136
138 const Span<float3> root_positions_cu,
139 const Span<NeighborCurves> neighbors_per_curve,
140 const int old_curves_num,
141 const Span<float> new_lengths_cu,
142 const Span<float3> new_normals_su,
143 const bke::CurvesSurfaceTransforms &transforms,
144 const Span<int3> corner_tris,
145 const ReverseUVSampler &reverse_uv_sampler,
146 const Span<float3> corner_normals_su)
147{
148 MutableSpan<float3> positions_cu = curves.positions_for_write();
149 const int added_curves_num = root_positions_cu.size();
150
151 const OffsetIndices points_by_curve = curves.points_by_curve();
152 const Span<float2> uv_coords = curves.surface_uv_coords();
153
154 threading::parallel_for(IndexRange(added_curves_num), 256, [&](const IndexRange range) {
155 for (const int added_curve_i : range) {
156 const NeighborCurves &neighbors = neighbors_per_curve[added_curve_i];
157 const int curve_i = old_curves_num + added_curve_i;
158 const IndexRange points = points_by_curve[curve_i];
159
160 const float length_cu = new_lengths_cu[added_curve_i];
161 const float3 &normal_su = new_normals_su[added_curve_i];
162 const float3 normal_cu = math::normalize(
164
165 const float3 &root_cu = root_positions_cu[added_curve_i];
166
167 if (neighbors.is_empty()) {
168 /* If there are no neighbors, just make a straight line. */
169 const float3 tip_cu = root_cu + length_cu * normal_cu;
170 calc_straight_curve_positions(root_cu, tip_cu, positions_cu.slice(points));
171 continue;
172 }
173
174 positions_cu.slice(points).fill(root_cu);
175
176 for (const NeighborCurve &neighbor : neighbors) {
177 const int neighbor_curve_i = neighbor.index;
178 const float2 neighbor_uv = uv_coords[neighbor_curve_i];
179 const ReverseUVSampler::Result result = reverse_uv_sampler.sample(neighbor_uv);
180 if (result.type != ReverseUVSampler::ResultType::Ok) {
181 continue;
182 }
183
184 const float3 neighbor_normal_su = compute_surface_point_normal(
185 corner_tris[result.tri_index], result.bary_weights, corner_normals_su);
186 const float3 neighbor_normal_cu = math::normalize(
187 math::transform_direction(transforms.surface_to_curves_normal, neighbor_normal_su));
188
189 /* The rotation matrix used to transform relative coordinates of the neighbor curve
190 * to the new curve. */
191 float normal_rotation_cu[3][3];
192 rotation_between_vecs_to_mat3(normal_rotation_cu, neighbor_normal_cu, normal_cu);
193
194 const IndexRange neighbor_points = points_by_curve[neighbor_curve_i];
195 const float3 &neighbor_root_cu = positions_cu[neighbor_points[0]];
196
197 /* Sample the positions on neighbors and mix them into the final positions of the curve.
198 * Resampling is necessary if the length of the new curve does not match the length of the
199 * neighbors or the number of handle points is different.
200 *
201 * TODO: The lengths can be cached so they aren't recomputed if a curve is a neighbor for
202 * multiple new curves. Also, allocations could be avoided by reusing some arrays. */
203
204 const Span<float3> neighbor_positions_cu = positions_cu.slice(neighbor_points);
205 if (neighbor_positions_cu.size() == 1) {
206 /* Skip interpolating positions from neighbors with only one point. */
207 continue;
208 }
209 Array<float, 32> lengths(length_parameterize::segments_num(neighbor_points.size(), false));
210 length_parameterize::accumulate_lengths<float3>(neighbor_positions_cu, false, lengths);
211 const float neighbor_length_cu = lengths.last();
212
213 Array<float, 32> sample_lengths(points.size());
214 const float length_factor = std::min(1.0f, length_cu / neighbor_length_cu);
215 const float resample_factor = (1.0f / (points.size() - 1.0f)) * length_factor;
216 for (const int i : sample_lengths.index_range()) {
217 sample_lengths[i] = i * resample_factor * neighbor_length_cu;
218 }
219
220 Array<int, 32> indices(points.size());
221 Array<float, 32> factors(points.size());
222 length_parameterize::sample_at_lengths(lengths, sample_lengths, indices, factors);
223
224 for (const int i : IndexRange(points.size())) {
225 const float3 sample_cu = math::interpolate(neighbor_positions_cu[indices[i]],
226 neighbor_positions_cu[indices[i] + 1],
227 factors[i]);
228 const float3 relative_to_root_cu = sample_cu - neighbor_root_cu;
229 float3 rotated_relative_coord = relative_to_root_cu;
230 mul_m3_v3(normal_rotation_cu, rotated_relative_coord);
231 positions_cu[points[i]] += neighbor.weight * rotated_relative_coord;
232 }
233 }
234 }
235 });
236}
237
239 const IndexRange new_points_range,
240 const float radius)
241{
242 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
243 bke::SpanAttributeWriter radius_attr = attributes.lookup_or_add_for_write_span<float>(
244 "radius", bke::AttrDomain::Point);
245 radius_attr.span.slice(new_points_range).fill(radius);
246 radius_attr.finish();
247}
248
250 const int old_curves_num,
251 const float radius,
252 const Span<float> new_lengths_cu,
253 const Span<NeighborCurves> neighbors_per_curve)
254{
255 const int added_curves_num = new_lengths_cu.size();
256 const OffsetIndices points_by_curve = curves.points_by_curve();
257 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
258 bke::SpanAttributeWriter radius_attr = attributes.lookup_for_write_span<float>("radius");
259 if (!radius_attr) {
260 return;
261 }
262
263 MutableSpan<float3> positions_cu = curves.positions_for_write();
264 MutableSpan<float> radii_cu = radius_attr.span;
265
266 threading::parallel_for(IndexRange(added_curves_num), 256, [&](const IndexRange range) {
267 for (const int i : range) {
268 const NeighborCurves &neighbors = neighbors_per_curve[i];
269 const float length_cu = new_lengths_cu[i];
270 const int curve_i = old_curves_num + i;
271 const IndexRange points = points_by_curve[curve_i];
272
273 if (neighbors.is_empty()) {
274 /* If there are no neighbors, just using uniform radius. */
275 radii_cu.slice(points).fill(radius);
276 continue;
277 }
278
279 radii_cu.slice(points).fill(0.0f);
280
281 for (const NeighborCurve &neighbor : neighbors) {
282 const int neighbor_curve_i = neighbor.index;
283 const IndexRange neighbor_points = points_by_curve[neighbor_curve_i];
284 const Span<float3> neighbor_positions_cu = positions_cu.slice(neighbor_points);
285 const Span<float> neighbor_radii_cu = radius_attr.span.slice(neighbor_points);
286
287 Array<float, 32> lengths(length_parameterize::segments_num(neighbor_points.size(), false));
288 length_parameterize::accumulate_lengths<float3>(neighbor_positions_cu, false, lengths);
289
290 const float neighbor_length_cu = lengths.last();
291
292 Array<float, 32> sample_lengths(points.size());
293 const float length_factor = std::min(1.0f, length_cu / neighbor_length_cu);
294 const float resample_factor = (1.0f / (points.size() - 1.0f)) * length_factor;
295 for (const int i : sample_lengths.index_range()) {
296 sample_lengths[i] = i * resample_factor * neighbor_length_cu;
297 }
298
299 Array<int, 32> indices(points.size());
300 Array<float, 32> factors(points.size());
301 length_parameterize::sample_at_lengths(lengths, sample_lengths, indices, factors);
302
303 for (const int i : IndexRange(points.size())) {
304 const float sample_cu = math::interpolate(
305 neighbor_radii_cu[indices[i]], neighbor_radii_cu[indices[i] + 1], factors[i]);
306
307 radii_cu[points[i]] += neighbor.weight * sample_cu;
308 }
309 }
310 }
311 });
312 radius_attr.finish();
313}
314
316 const AddCurvesOnMeshInputs &inputs)
317{
319
320 const bool use_interpolation = inputs.interpolate_length || inputs.interpolate_point_count ||
321 inputs.interpolate_radius || inputs.interpolate_shape ||
322 inputs.interpolate_resolution;
323
324 Vector<float3> root_positions_cu;
325 Vector<float3> bary_coords;
326 Vector<int> tri_indices;
327 Vector<float2> used_uvs;
328
329 /* Find faces that the passed in uvs belong to. */
330 const Span<float3> surface_positions = inputs.surface->vert_positions();
331 const Span<int> surface_corner_verts = inputs.surface->corner_verts();
332 for (const int i : inputs.uvs.index_range()) {
333 const float2 &uv = inputs.uvs[i];
334 const ReverseUVSampler::Result result = inputs.reverse_uv_sampler->sample(uv);
335 if (result.type != ReverseUVSampler::ResultType::Ok) {
336 outputs.uv_error = true;
337 continue;
338 }
339 const int3 &tri = inputs.surface_corner_tris[result.tri_index];
340 bary_coords.append(result.bary_weights);
341 tri_indices.append(result.tri_index);
342 const float3 root_position_su = bke::attribute_math::mix3<float3>(
343 result.bary_weights,
344 surface_positions[surface_corner_verts[tri[0]]],
345 surface_positions[surface_corner_verts[tri[1]]],
346 surface_positions[surface_corner_verts[tri[2]]]);
347 root_positions_cu.append(
348 math::transform_point(inputs.transforms->surface_to_curves, root_position_su));
349 used_uvs.append(uv);
350 }
351
352 Array<NeighborCurves> neighbors_per_curve;
353 if (use_interpolation) {
354 BLI_assert(inputs.old_roots_kdtree != nullptr);
355 neighbors_per_curve = find_curve_neighbors(root_positions_cu, *inputs.old_roots_kdtree);
356 }
357
358 const int added_curves_num = root_positions_cu.size();
359 const int old_points_num = curves.points_num();
360 const int old_curves_num = curves.curves_num();
361 const int new_curves_num = old_curves_num + added_curves_num;
362
363 /* Grow number of curves first, so that the offsets array can be filled. */
364 curves.resize(old_points_num, new_curves_num);
365 if (new_curves_num == 0) {
366 return outputs;
367 }
368
369 /* Compute new curve offsets. */
370 MutableSpan<int> curve_offsets = curves.offsets_for_write();
371 Array<int> new_point_counts_per_curve(added_curves_num);
372 if (inputs.interpolate_point_count && old_curves_num > 0) {
373 const OffsetIndices<int> old_points_by_curve{curve_offsets.take_front(old_curves_num + 1)};
374 interpolate_from_neighbor_curves<int>(
375 neighbors_per_curve,
376 inputs.fallback_point_count,
377 [&](const int curve_i) { return old_points_by_curve[curve_i].size(); },
378 new_point_counts_per_curve);
379 }
380 else {
381 new_point_counts_per_curve.fill(inputs.fallback_point_count);
382 }
383 curve_offsets[old_curves_num] = old_points_num;
384 int offset = old_points_num;
385 for (const int i : new_point_counts_per_curve.index_range()) {
386 const int point_count_in_curve = new_point_counts_per_curve[i];
387 curve_offsets[old_curves_num + i + 1] = offset + point_count_in_curve;
388 offset += point_count_in_curve;
389 }
390
391 const int new_points_num = curves.offsets().last();
392 curves.resize(new_points_num, new_curves_num);
393 const OffsetIndices points_by_curve = curves.points_by_curve();
394
395 /* The new elements are added at the end of the arrays. */
396 outputs.new_points_range = curves.points_range().drop_front(old_points_num);
397 outputs.new_curves_range = curves.curves_range().drop_front(old_curves_num);
398
399 /* Initialize attachment information. */
400 MutableSpan<float2> surface_uv_coords = curves.surface_uv_coords_for_write();
401 surface_uv_coords.take_back(added_curves_num).copy_from(used_uvs);
402
403 /* Determine length of new curves. */
404 Span<float3> positions_cu = curves.positions();
405 Array<float> new_lengths_cu(added_curves_num);
406 if (inputs.interpolate_length) {
407 interpolate_from_neighbor_curves<float>(
408 neighbors_per_curve,
409 inputs.fallback_curve_length,
410 [&](const int curve_i) {
411 const IndexRange points = points_by_curve[curve_i];
412 float length = 0.0f;
413 for (const int segment_i : points.drop_back(1)) {
414 const float3 &p1 = positions_cu[segment_i];
415 const float3 &p2 = positions_cu[segment_i + 1];
416 length += math::distance(p1, p2);
417 }
418 return length;
419 },
420 new_lengths_cu);
421 }
422 else {
423 new_lengths_cu.fill(inputs.fallback_curve_length);
424 }
425
426 /* Find surface normal at root points. */
427 Array<float3> new_normals_su(added_curves_num);
428 bke::mesh_surface_sample::sample_corner_normals(inputs.surface_corner_tris,
429 tri_indices,
430 bary_coords,
431 inputs.corner_normals_su,
432 IndexMask(added_curves_num),
433 new_normals_su);
434
435 /* Initialize position attribute. */
436 if (inputs.interpolate_shape) {
437 calc_position_with_interpolation(curves,
438 root_positions_cu,
439 neighbors_per_curve,
440 old_curves_num,
441 new_lengths_cu,
442 new_normals_su,
443 *inputs.transforms,
444 inputs.surface_corner_tris,
445 *inputs.reverse_uv_sampler,
446 inputs.corner_normals_su);
447 }
448 else {
450 old_curves_num,
451 root_positions_cu,
452 new_lengths_cu,
453 new_normals_su,
454 inputs.transforms->surface_to_curves_normal);
455 }
456
457 /* Initialize radius attribute */
458 if (inputs.interpolate_radius) {
460 curves, old_curves_num, inputs.fallback_curve_radius, new_lengths_cu, neighbors_per_curve);
461 }
462 else {
464 curves, outputs.new_points_range, inputs.fallback_curve_radius);
465 }
466
467 curves.fill_curve_types(outputs.new_curves_range, CURVE_TYPE_CATMULL_ROM);
468
469 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
470
471 if (bke::SpanAttributeWriter<int> resolution = attributes.lookup_for_write_span<int>(
472 "resolution"))
473 {
474 if (inputs.interpolate_resolution) {
476 neighbors_per_curve,
477 12,
478 [&](const int curve_i) { return resolution.span[curve_i]; },
479 resolution.span.take_back(added_curves_num));
480 resolution.finish();
481 }
482 else {
483 resolution.span.take_back(added_curves_num).fill(12);
484 }
485 }
486
487 /* Explicitly set all other attributes besides those processed above to default values. */
488 bke::fill_attribute_range_default(attributes,
489 bke::AttrDomain::Point,
490 bke::attribute_filter_from_skip_ref({"position", "radius"}),
491 outputs.new_points_range);
492 bke::fill_attribute_range_default(
493 attributes,
494 bke::AttrDomain::Curve,
495 bke::attribute_filter_from_skip_ref({"curve_type", "surface_uv_coordinate", "resolution"}),
496 outputs.new_curves_range);
497
498 return outputs;
499}
500
501} // namespace blender::geometry
#define BLI_assert(a)
Definition BLI_assert.h:50
A KD-tree for nearest neighbor search.
void mul_m3_v3(const float M[3][3], float r[3])
void rotation_between_vecs_to_mat3(float m[3][3], const float v1[3], const float v2[3])
@ CURVE_TYPE_CATMULL_ROM
IndexRange index_range() const
Definition BLI_array.hh:349
void fill(const T &value) const
Definition BLI_array.hh:261
constexpr int64_t size() const
constexpr int64_t size() const
Definition BLI_span.hh:494
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
Definition BLI_span.hh:574
constexpr MutableSpan take_back(const int64_t n) const
Definition BLI_span.hh:641
constexpr void fill(const T &value) const
Definition BLI_span.hh:518
constexpr IndexRange index_range() const
Definition BLI_span.hh:671
constexpr void copy_from(Span< T > values) const
Definition BLI_span.hh:726
constexpr MutableSpan take_front(const int64_t n) const
Definition BLI_span.hh:630
constexpr T & last(const int64_t n=0) const
Definition BLI_span.hh:690
constexpr int64_t size() const
Definition BLI_span.hh:253
int64_t size() const
void append(const T &value)
bool is_empty() const
Result sample(const float2 &query_uv) const
local_group_size(16, 16) .push_constant(Type b
static ushort indices[]
IndexRange range
typename DefaultMixerStruct< T >::type DefaultMixer
T mix2(float factor, const T &a, const T &b)
T sample_corner_attribute_with_bary_coords(const float3 &bary_weights, const int3 &corner_tri, const Span< T > corner_attribute)
static void calc_radius_without_interpolation(CurvesGeometry &curves, const IndexRange new_points_range, const float radius)
static Array< NeighborCurves > find_curve_neighbors(const Span< float3 > root_positions, const KDTree_3d &old_roots_kdtree)
void interpolate_from_neighbor_curves(const Span< NeighborCurves > neighbors_per_curve, const T &fallback, const GetValueF &get_value_from_neighbor, MutableSpan< T > r_interpolated_values)
AddCurvesOnMeshOutputs add_curves_on_mesh(bke::CurvesGeometry &curves, const AddCurvesOnMeshInputs &inputs)
static constexpr int max_neighbors
float3 compute_surface_point_normal(const int3 &tri, const float3 &bary_coord, Span< float3 > corner_normals)
static void calc_position_with_interpolation(CurvesGeometry &curves, const Span< float3 > root_positions_cu, const Span< NeighborCurves > neighbors_per_curve, const int old_curves_num, const Span< float > new_lengths_cu, const Span< float3 > new_normals_su, const bke::CurvesSurfaceTransforms &transforms, const Span< int3 > corner_tris, const ReverseUVSampler &reverse_uv_sampler, const Span< float3 > corner_normals_su)
static void calc_radius_with_interpolation(CurvesGeometry &curves, const int old_curves_num, const float radius, const Span< float > new_lengths_cu, const Span< NeighborCurves > neighbors_per_curve)
static void calc_position_without_interpolation(CurvesGeometry &curves, const int old_curves_num, const Span< float3 > root_positions_cu, const Span< float > new_lengths_cu, const Span< float3 > new_normals_su, const float4x4 &surface_to_curves_normal_mat)
static void calc_straight_curve_positions(const float3 &a, const float3 &b, MutableSpan< float3 > dst)
T interpolate(const T &a, const T &b, const FactorT &t)
T rcp(const T &a)
MatBase< T, NumCol, NumRow > normalize(const MatBase< T, NumCol, NumRow > &a)
VecBase< T, 3 > transform_direction(const MatBase< T, 3, 3 > &mat, const VecBase< T, 3 > &direction)
VecBase< T, 3 > transform_point(const CartesianBasis &basis, const VecBase< T, 3 > &v)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:95
static blender::bke::bNodeSocketTemplate outputs[]