Blender V4.5
curve_to_mesh_convert.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_array.hh"
6#include "BLI_array_utils.hh"
7#include "BLI_math_matrix.hh"
8#include "BLI_task.hh"
9
10#include "BKE_attribute_math.hh"
11#include "BKE_curves.hh"
12#include "BKE_customdata.hh"
13#include "BKE_geometry_set.hh"
14#include "BKE_mesh.hh"
15
16#include "BKE_curve_to_mesh.hh"
17
18namespace blender::bke {
19
20static int segments_num_no_duplicate_edge(const int points_num, const bool cyclic)
21{
22 if (points_num == 0) {
23 return 0;
24 }
25 if (points_num <= 2) {
26 return curves::segments_num(points_num, false);
27 }
28 return curves::segments_num(points_num, cyclic);
29}
30
31static inline bool has_caps(const bool main_cyclic,
32 const bool profile_cyclic,
33 const int profile_segment_num)
34{
35 return !main_cyclic && profile_cyclic && profile_segment_num > 2;
36}
37
38static void fill_mesh_topology(const int vert_offset,
39 const int edge_offset,
40 const int face_offset,
41 const int loop_offset,
42 const int main_point_num,
43 const int profile_point_num,
44 const bool main_cyclic,
45 const bool profile_cyclic,
46 const bool fill_caps,
48 MutableSpan<int> corner_verts,
49 MutableSpan<int> corner_edges,
50 MutableSpan<int> face_offsets)
51{
52 const int main_segment_num = segments_num_no_duplicate_edge(main_point_num, main_cyclic);
53 const int profile_segment_num = curves::segments_num(profile_point_num, profile_cyclic);
54
55 if (profile_point_num == 1) {
56 for (const int i : IndexRange(main_point_num - 1)) {
57 int2 &edge = edges[edge_offset + i];
58 edge[0] = vert_offset + i;
59 edge[1] = vert_offset + i + 1;
60 }
61
62 if (main_cyclic && main_segment_num > 2) {
63 int2 &edge = edges[edge_offset + main_segment_num - 1];
64 edge[0] = vert_offset + main_point_num - 1;
65 edge[1] = vert_offset;
66 }
67 return;
68 }
69
70 /* Add the edges running along the length of the curve, starting at each profile vertex. */
71 const int main_edges_start = edge_offset;
72 for (const int i_profile : IndexRange(profile_point_num)) {
73 const int profile_edge_offset = main_edges_start + i_profile * main_segment_num;
74 for (const int i_ring : IndexRange(main_segment_num)) {
75 const int i_next_ring = (i_ring == main_point_num - 1) ? 0 : i_ring + 1;
76
77 const int ring_vert_offset = vert_offset + profile_point_num * i_ring;
78 const int next_ring_vert_offset = vert_offset + profile_point_num * i_next_ring;
79
80 int2 &edge = edges[profile_edge_offset + i_ring];
81 edge[0] = ring_vert_offset + i_profile;
82 edge[1] = next_ring_vert_offset + i_profile;
83 }
84 }
85
86 /* Add the edges running along each profile ring. */
87 const int profile_edges_start = main_edges_start + profile_point_num * main_segment_num;
88 for (const int i_ring : IndexRange(main_point_num)) {
89 const int ring_vert_offset = vert_offset + profile_point_num * i_ring;
90
91 const int ring_edge_offset = profile_edges_start + i_ring * profile_segment_num;
92 for (const int i_profile : IndexRange(profile_segment_num)) {
93 const int i_next_profile = (i_profile == profile_point_num - 1) ? 0 : i_profile + 1;
94
95 int2 &edge = edges[ring_edge_offset + i_profile];
96 edge[0] = ring_vert_offset + i_profile;
97 edge[1] = ring_vert_offset + i_next_profile;
98 }
99 }
100
101 /* Calculate face and corner indices. */
102 for (const int i_ring : IndexRange(main_segment_num)) {
103 const int i_next_ring = (i_ring == main_point_num - 1) ? 0 : i_ring + 1;
104
105 const int ring_vert_offset = vert_offset + profile_point_num * i_ring;
106 const int next_ring_vert_offset = vert_offset + profile_point_num * i_next_ring;
107
108 const int ring_edge_start = profile_edges_start + profile_segment_num * i_ring;
109 const int next_ring_edge_offset = profile_edges_start + profile_segment_num * i_next_ring;
110
111 const int ring_face_offset = face_offset + i_ring * profile_segment_num;
112 const int ring_loop_offset = loop_offset + i_ring * profile_segment_num * 4;
113
114 for (const int i_profile : IndexRange(profile_segment_num)) {
115 const int ring_segment_loop_offset = ring_loop_offset + i_profile * 4;
116 const int i_next_profile = (i_profile == profile_point_num - 1) ? 0 : i_profile + 1;
117
118 const int main_edge_start = main_edges_start + main_segment_num * i_profile;
119 const int next_main_edge_start = main_edges_start + main_segment_num * i_next_profile;
120
121 face_offsets[ring_face_offset + i_profile] = ring_segment_loop_offset;
122
123 corner_verts[ring_segment_loop_offset] = ring_vert_offset + i_profile;
124 corner_edges[ring_segment_loop_offset] = ring_edge_start + i_profile;
125
126 corner_verts[ring_segment_loop_offset + 1] = ring_vert_offset + i_next_profile;
127 corner_edges[ring_segment_loop_offset + 1] = next_main_edge_start + i_ring;
128
129 corner_verts[ring_segment_loop_offset + 2] = next_ring_vert_offset + i_next_profile;
130 corner_edges[ring_segment_loop_offset + 2] = next_ring_edge_offset + i_profile;
131
132 corner_verts[ring_segment_loop_offset + 3] = next_ring_vert_offset + i_profile;
133 corner_edges[ring_segment_loop_offset + 3] = main_edge_start + i_ring;
134 }
135 }
136
137 if (fill_caps & has_caps(main_cyclic, profile_cyclic, profile_segment_num)) {
138 const int face_num = main_segment_num * profile_segment_num;
139 const int cap_loop_offset = loop_offset + face_num * 4;
140 const int cap_face_offset = face_offset + face_num;
141
142 face_offsets[cap_face_offset] = cap_loop_offset;
143 face_offsets[cap_face_offset + 1] = cap_loop_offset + profile_segment_num;
144
145 const int last_ring_index = main_point_num - 1;
146 const int last_ring_vert_offset = vert_offset + profile_point_num * last_ring_index;
147 const int last_ring_edge_offset = profile_edges_start + profile_segment_num * last_ring_index;
148
149 for (const int i : IndexRange(profile_segment_num)) {
150 const int i_inv = profile_segment_num - i - 1;
151 corner_verts[cap_loop_offset + i] = vert_offset + i_inv;
152 corner_edges[cap_loop_offset + i] = profile_edges_start + ((i == (profile_segment_num - 1)) ?
153 (profile_segment_num - 1) :
154 (i_inv - 1));
155 corner_verts[cap_loop_offset + profile_segment_num + i] = last_ring_vert_offset + i;
156 corner_edges[cap_loop_offset + profile_segment_num + i] = last_ring_edge_offset + i;
157 }
158 }
159}
160
162static void mark_bezier_vector_edges_sharp(const int profile_point_num,
163 const int main_segment_num,
164 const Span<int> control_point_offsets,
165 const Span<int8_t> handle_types_left,
166 const Span<int8_t> handle_types_right,
167 MutableSpan<bool> sharp_edges)
168{
169 const int main_edges_start = 0;
170 if (curves::bezier::point_is_sharp(handle_types_left, handle_types_right, 0)) {
171 sharp_edges.slice(main_edges_start, main_segment_num).fill(true);
172 }
173
174 for (const int i : IndexRange(profile_point_num).drop_front(1)) {
175 if (curves::bezier::point_is_sharp(handle_types_left, handle_types_right, i)) {
176 const int offset = main_edges_start + main_segment_num * control_point_offsets[i];
177 sharp_edges.slice(offset, main_segment_num).fill(true);
178 }
179 }
180}
181
182static float4x4 build_point_matrix(const float3 &location,
183 const float3 &tangent,
184 const float3 &normal)
185{
186 float4x4 matrix = float4x4::identity();
187 matrix.x_axis() = tangent;
188 /* Normal and tangent may not be orthogonal in case of custom normals. */
189 matrix.y_axis() = math::normalize(math::cross(normal, tangent));
190 matrix.z_axis() = normal;
191 matrix.location() = location;
192 return matrix;
193}
194
195static void fill_mesh_positions(const int main_point_num,
196 const int profile_point_num,
197 const Span<float3> main_positions,
198 const Span<float3> profile_positions,
199 const Span<float3> tangents,
200 const Span<float3> normals,
201 const Span<float> scales,
202 MutableSpan<float3> mesh_positions)
203{
204 if (profile_point_num == 1) {
205 for (const int i_ring : IndexRange(main_point_num)) {
206 float4x4 point_matrix = build_point_matrix(
207 main_positions[i_ring], normals[i_ring], tangents[i_ring]);
208 if (!scales.is_empty()) {
209 point_matrix = math::scale(point_matrix, float3(scales[i_ring]));
210 }
211 mesh_positions[i_ring] = math::transform_point(point_matrix, profile_positions.first());
212 }
213 }
214 else {
215 for (const int i_ring : IndexRange(main_point_num)) {
216 float4x4 point_matrix = build_point_matrix(
217 main_positions[i_ring], normals[i_ring], tangents[i_ring]);
218 if (!scales.is_empty()) {
219 point_matrix = math::scale(point_matrix, float3(scales[i_ring]));
220 }
221
222 const int ring_vert_start = i_ring * profile_point_num;
223 for (const int i_profile : IndexRange(profile_point_num)) {
224 mesh_positions[ring_vert_start + i_profile] = math::transform_point(
225 point_matrix, profile_positions[i_profile]);
226 }
227 }
228 }
229}
230
234
235 /* Make sure these are spans because they are potentially accessed many times. */
238};
240{
241 return {main, profile, main.cyclic(), profile.cyclic()};
242}
243
245{
246 for (const int64_t i : offsets.index_range()) {
247 if (offsets[i].size() == 1) {
248 return true;
249 }
250 }
251 return false;
252}
253
256 int total;
257
263
264 /* The indices of the main and profile curves that form each combination. */
267
271};
272static ResultOffsets calculate_result_offsets(const CurvesInfo &info, const bool fill_caps)
273{
275 result.total = info.main.curves_num() * info.profile.curves_num();
276
277 const OffsetIndices<int> main_offsets = info.main.evaluated_points_by_curve();
278 const OffsetIndices<int> profile_offsets = info.profile.evaluated_points_by_curve();
279
281 result.total > 1024,
282 [&]() {
283 result.vert.reinitialize(result.total + 1);
284 result.edge.reinitialize(result.total + 1);
285 result.loop.reinitialize(result.total + 1);
286 result.face.reinitialize(result.total + 1);
287
288 int mesh_index = 0;
289 int vert_offset = 0;
290 int edge_offset = 0;
291 int loop_offset = 0;
292 int face_offset = 0;
293 for (const int i_main : main_offsets.index_range()) {
294 const bool main_cyclic = info.main_cyclic[i_main];
295 const int main_point_num = main_offsets[i_main].size();
296 const int main_segment_num = segments_num_no_duplicate_edge(main_point_num, main_cyclic);
297 for (const int i_profile : profile_offsets.index_range()) {
298 result.vert[mesh_index] = vert_offset;
299 result.edge[mesh_index] = edge_offset;
300 result.loop[mesh_index] = loop_offset;
301 result.face[mesh_index] = face_offset;
302
303 const bool profile_cyclic = info.profile_cyclic[i_profile];
304 const int profile_point_num = profile_offsets[i_profile].size();
305 if (profile_point_num == 0) {
306 continue;
307 }
308 const int profile_segment_num = curves::segments_num(profile_point_num,
309 profile_cyclic);
310
311 const bool caps = fill_caps &
312 has_caps(main_cyclic, profile_cyclic, profile_segment_num);
313 const int tube_face_num = main_segment_num * profile_segment_num;
314
315 vert_offset += main_point_num * profile_point_num;
316
317 /* Add the ring edges, with one ring for every curve vertex, and the edge loops
318 * that run along the length of the curve, starting on the first profile. */
319 edge_offset += main_point_num * profile_segment_num +
320 main_segment_num * profile_point_num;
321
322 /* Add two cap N-gons for every ending. */
323 face_offset += tube_face_num + (caps ? 2 : 0);
324
325 /* All faces on the tube are quads, and all cap faces are N-gons with an edge for each
326 * profile edge. */
327 loop_offset += tube_face_num * 4 + (caps ? profile_segment_num * 2 : 0);
328
329 mesh_index++;
330 }
331 }
332
333 result.vert.last() = vert_offset;
334 result.edge.last() = edge_offset;
335 result.loop.last() = loop_offset;
336 result.face.last() = face_offset;
337 },
338 [&]() {
339 result.main_indices.reinitialize(result.total);
340 result.profile_indices.reinitialize(result.total);
341
342 int mesh_index = 0;
343 for (const int i_main : main_offsets.index_range()) {
344 for (const int i_profile : profile_offsets.index_range()) {
345 result.main_indices[mesh_index] = i_main;
346 result.profile_indices[mesh_index] = i_profile;
347 mesh_index++;
348 }
349 }
350 },
351 [&]() { result.any_single_point_main = offsets_contain_single_point(main_offsets); },
352 [&]() { result.any_single_point_profile = offsets_contain_single_point(profile_offsets); });
353
354 return result;
355}
356
358 const StringRef attribute_id)
359{
360 /* Only use a different domain if it is builtin and must only exist on one domain. */
361 if (!mesh_attributes.is_builtin(attribute_id)) {
362 return AttrDomain::Point;
363 }
364
365 std::optional<AttributeMetaData> meta_data = mesh_attributes.lookup_meta_data(attribute_id);
366 if (!meta_data) {
367 return AttrDomain::Point;
368 }
369
370 return meta_data->domain;
371}
372
373static bool should_add_attribute_to_mesh(const AttributeAccessor &curve_attributes,
374 const AttributeAccessor &mesh_attributes,
375 const StringRef id,
376 const AttributeMetaData &meta_data,
377 const AttributeFilter &attribute_filter)
378{
379
380 if (id == "position") {
381 /* The position attribute has special non-generic evaluation. */
382 return false;
383 }
384 if (id == "custom_normal") {
385 /* The custom normal attribute is builtin on both meshes and curves, but has a different
386 * meaning and shouldn't be directly propagated. */
387 return false;
388 }
389 /* Don't propagate built-in curves attributes that are not built-in on meshes. */
390 if (curve_attributes.is_builtin(id) && !mesh_attributes.is_builtin(id)) {
391 return false;
392 }
393 if (attribute_filter.allow_skip(id)) {
394 return false;
395 }
396 if (meta_data.data_type == CD_PROP_STRING) {
397 return false;
398 }
399 return true;
400}
401
403 const CurvesGeometry &curves,
404 Vector<std::byte> &buffer)
405{
406 /* Poly curves evaluated points match the curve points, no need to interpolate. */
407 if (curves.is_single_type(CURVE_TYPE_POLY)) {
408 if (src.is_span()) {
409 return src.get_internal_span();
410 }
411 buffer.reinitialize(curves.points_num() * src.type().size);
412 src.materialize(buffer.data());
413 GMutableSpan eval{src.type(), buffer.data(), curves.points_num()};
414 return eval;
415 }
416
417 if (src.is_span()) {
418 buffer.reinitialize(curves.evaluated_points_num() * src.type().size);
419 GMutableSpan eval{src.type(), buffer.data(), curves.evaluated_points_num()};
420 curves.interpolate_to_evaluated(src.get_internal_span(), eval);
421 return eval;
422 }
423 GVArraySpan src_buffer(src);
424 buffer.reinitialize(curves.evaluated_points_num() * src.type().size);
425 GMutableSpan eval{src.type(), buffer.data(), curves.evaluated_points_num()};
426 curves.interpolate_to_evaluated(src_buffer, eval);
427 return eval;
428}
429
449template<typename Fn>
450static void foreach_curve_combination(const CurvesInfo &info,
451 const ResultOffsets &offsets,
452 const Fn &fn)
453{
454 const OffsetIndices<int> main_offsets = info.main.evaluated_points_by_curve();
455 const OffsetIndices<int> profile_offsets = info.profile.evaluated_points_by_curve();
456 const OffsetIndices<int> vert_offsets(offsets.vert);
457 const OffsetIndices<int> edge_offsets(offsets.edge);
458 const OffsetIndices<int> face_offsets(offsets.face);
459 const OffsetIndices<int> loop_offsets(offsets.loop);
460 threading::parallel_for(IndexRange(offsets.total), 512, [&](IndexRange range) {
461 for (const int i : range) {
462 const int i_main = offsets.main_indices[i];
463 const int i_profile = offsets.profile_indices[i];
464
465 const IndexRange main_points = main_offsets[i_main];
466 const IndexRange profile_points = profile_offsets[i_profile];
467 if (main_points.is_empty() || profile_points.is_empty()) {
468 continue;
469 }
470
471 const bool main_cyclic = info.main_cyclic[i_main];
472 const bool profile_cyclic = info.profile_cyclic[i_profile];
473
474 /* Pass all information in a struct to avoid repeating arguments in many lambdas.
475 * The idea is that inlining `fn` will help avoid accessing unnecessary information,
476 * though that may or may not happen in practice. */
477 fn(CombinationInfo{i_main,
478 i_profile,
479 main_points,
480 profile_points,
481 main_cyclic,
482 profile_cyclic,
483 curves::segments_num(main_points.size(), main_cyclic),
484 curves::segments_num(profile_points.size(), profile_cyclic),
485 vert_offsets[i],
486 edge_offsets[i],
487 face_offsets[i],
488 loop_offsets[i]});
489 }
490 });
491}
492
493static void build_mesh_positions(const CurvesInfo &curves_info,
494 const ResultOffsets &offsets,
495 const VArray<float> &scales,
496 Vector<std::byte> &eval_buffer,
497 Mesh &mesh)
498{
499 BLI_assert(!mesh.attributes().contains("position"));
500 const Span<float3> profile_positions = curves_info.profile.evaluated_positions();
501 const bool ignore_profile_position = profile_positions.size() == 1 &&
502 math::is_equal(profile_positions.first(), float3(0.0f));
503 if (ignore_profile_position) {
504 if (mesh.verts_num == curves_info.main.points_num()) {
505 const GAttributeReader src = curves_info.main.attributes().lookup("position");
506 if (src.sharing_info && src.varray.is_span()) {
508 if (mesh.attributes_for_write().add<float3>("position", AttrDomain::Point, init)) {
509 return;
510 }
511 }
512 }
513 }
514 const Span<float3> main_positions = curves_info.main.evaluated_positions();
515 mesh.attributes_for_write().add<float3>("position", AttrDomain::Point, AttributeInitConstruct());
516 MutableSpan<float3> positions = mesh.vert_positions_for_write();
517 if (ignore_profile_position) {
518 array_utils::copy(main_positions, positions);
519 return;
520 }
521 const Span<float3> tangents = curves_info.main.evaluated_tangents();
522 const Span<float3> normals = curves_info.main.evaluated_normals();
523 Span<float> eval_scales;
524 if (!scales.is_empty() && scales.get_if_single() != 1.0f) {
525 eval_scales = evaluate_attribute(scales, curves_info.main, eval_buffer).typed<float>();
526 }
527 foreach_curve_combination(curves_info, offsets, [&](const CombinationInfo &info) {
529 info.profile_points.size(),
530 main_positions.slice(info.main_points),
531 profile_positions.slice(info.profile_points),
532 tangents.slice(info.main_points),
533 normals.slice(info.main_points),
534 eval_scales.is_empty() ? eval_scales : eval_scales.slice(info.main_points),
535 positions.slice(info.vert_range));
536 });
537}
538
539template<typename T>
541 const int profile_point_num,
542 MutableSpan<T> dst)
543{
544 for (const int i_ring : src.index_range()) {
545 const int ring_vert_start = i_ring * profile_point_num;
546 dst.slice(ring_vert_start, profile_point_num).fill(src[i_ring]);
547 }
548}
549
550template<typename T>
552 const int profile_point_num,
553 const int main_segment_num,
554 const int profile_segment_num,
555 MutableSpan<T> dst)
556{
557 const int edges_start = profile_point_num * main_segment_num;
558 for (const int i_ring : src.index_range()) {
559 const int ring_edge_start = edges_start + profile_segment_num * i_ring;
560 dst.slice(ring_edge_start, profile_segment_num).fill(src[i_ring]);
561 }
562}
563
564template<typename T>
566 const int main_segment_num,
567 const int profile_segment_num,
568 MutableSpan<T> dst)
569{
570 for (const int i_ring : IndexRange(main_segment_num)) {
571 const int ring_face_start = profile_segment_num * i_ring;
572 dst.slice(ring_face_start, profile_segment_num).fill(src[i_ring]);
573 }
574}
575
577 const StringRef id,
578 const GAttributeReader &src,
579 MutableAttributeAccessor mesh_attributes)
580{
581 if (mesh_attributes.domain_size(AttrDomain::Point) != main.points_num()) {
582 return false;
583 }
584 if (!src.sharing_info || !src.varray.is_span()) {
585 return false;
586 }
587 return mesh_attributes.add(
588 id,
592}
593
595 const GAttributeReader &src,
596 GMutableSpan dst)
597{
598 if (dst.size() != main.evaluated_points_num()) {
599 return false;
600 }
601 if (!src.varray.is_span()) {
602 return false;
603 }
604 main.interpolate_to_evaluated(src.varray.get_internal_span(), dst);
605 return true;
606}
607
609 const StringRef id,
610 const ResultOffsets &offsets,
611 const AttrDomain dst_domain,
612 const GAttributeReader &src_attribute,
613 Vector<std::byte> &eval_buffer,
614 MutableAttributeAccessor mesh_attributes)
615{
616 if (dst_domain == AttrDomain::Point) {
617 if (try_sharing_point_data(curves_info.main, id, src_attribute, mesh_attributes)) {
618 return;
619 }
620 }
621 GSpanAttributeWriter dst_attribute = mesh_attributes.lookup_or_add_for_write_only_span(
622 id, dst_domain, bke::cpp_type_to_custom_data_type(src_attribute.varray.type()));
623 if (!dst_attribute) {
624 return;
625 }
626 if (dst_domain == AttrDomain::Point) {
627 if (try_direct_evaluate_point_data(curves_info.main, src_attribute, dst_attribute.span)) {
628 dst_attribute.finish();
629 return;
630 }
631 }
632 const GSpan src_all = evaluate_attribute(*src_attribute, curves_info.main, eval_buffer);
633 attribute_math::convert_to_static_type(src_attribute.varray.type(), [&](auto dummy) {
634 using T = decltype(dummy);
635 const Span<T> src = src_all.typed<T>();
636 MutableSpan<T> dst = dst_attribute.span.typed<T>();
637 switch (dst_domain) {
638 case AttrDomain::Point:
639 foreach_curve_combination(curves_info, offsets, [&](const CombinationInfo &info) {
640 copy_main_point_data_to_mesh_verts(
641 src.slice(info.main_points), info.profile_points.size(), dst.slice(info.vert_range));
642 });
643 break;
644 case AttrDomain::Edge:
645 foreach_curve_combination(curves_info, offsets, [&](const CombinationInfo &info) {
646 copy_main_point_data_to_mesh_edges(src.slice(info.main_points),
647 info.profile_points.size(),
648 info.main_segment_num,
649 info.profile_segment_num,
650 dst.slice(info.edge_range));
651 });
652 break;
653 case AttrDomain::Face:
654 foreach_curve_combination(curves_info, offsets, [&](const CombinationInfo &info) {
655 copy_main_point_data_to_mesh_faces(src.slice(info.main_points),
656 info.main_segment_num,
657 info.profile_segment_num,
658 dst.slice(info.face_range));
659 });
660 break;
661 case AttrDomain::Corner:
662 /* Unsupported for now, since there are no builtin attributes to convert into. */
663 break;
664 default:
665 BLI_assert_unreachable();
666 break;
667 }
668 });
669 dst_attribute.finish();
670}
671
672template<typename T>
674 const int main_point_num,
675 MutableSpan<T> dst)
676{
677 for (const int i_ring : IndexRange(main_point_num)) {
678 const int profile_vert_start = i_ring * src.size();
679 for (const int i_profile : src.index_range()) {
680 dst[profile_vert_start + i_profile] = src[i_profile];
681 }
682 }
683}
684
685template<typename T>
687 const int main_segment_num,
688 MutableSpan<T> dst)
689{
690 for (const int i_profile : src.index_range()) {
691 const int profile_edge_offset = i_profile * main_segment_num;
692 dst.slice(profile_edge_offset, main_segment_num).fill(src[i_profile]);
693 }
694}
695
696template<typename T>
698 const int main_segment_num,
699 const int profile_segment_num,
700 MutableSpan<T> dst)
701{
702 for (const int i_ring : IndexRange(main_segment_num)) {
703 const int profile_face_start = i_ring * profile_segment_num;
704 for (const int i_profile : IndexRange(profile_segment_num)) {
705 dst[profile_face_start + i_profile] = src[i_profile];
706 }
707 }
708}
709
711 const ResultOffsets &offsets,
712 const AttrDomain dst_domain,
713 const GSpan src_all,
714 GMutableSpan dst_all)
715{
716 attribute_math::convert_to_static_type(src_all.type(), [&](auto dummy) {
717 using T = decltype(dummy);
718 const Span<T> src = src_all.typed<T>();
719 MutableSpan<T> dst = dst_all.typed<T>();
720 switch (dst_domain) {
721 case AttrDomain::Point:
722 foreach_curve_combination(curves_info, offsets, [&](const CombinationInfo &info) {
723 copy_profile_point_data_to_mesh_verts(
724 src.slice(info.profile_points), info.main_points.size(), dst.slice(info.vert_range));
725 });
726 break;
727 case AttrDomain::Edge:
728 foreach_curve_combination(curves_info, offsets, [&](const CombinationInfo &info) {
729 copy_profile_point_data_to_mesh_edges(
730 src.slice(info.profile_points), info.main_segment_num, dst.slice(info.edge_range));
731 });
732 break;
733 case AttrDomain::Face:
734 foreach_curve_combination(curves_info, offsets, [&](const CombinationInfo &info) {
735 copy_profile_point_data_to_mesh_faces(src.slice(info.profile_points),
736 info.main_segment_num,
737 info.profile_segment_num,
738 dst.slice(info.face_range));
739 });
740 break;
741 case AttrDomain::Corner:
742 /* Unsupported for now, since there are no builtin attributes to convert into. */
743 break;
744 default:
745 BLI_assert_unreachable();
746 break;
747 }
748 });
749}
750
751template<typename T>
753 const Span<int> curve_indices,
754 const OffsetIndices<int> mesh_offsets,
755 MutableSpan<T> dst)
756{
757 /* This unnecessarily instantiates the "is single" case (which should be handled elsewhere if
758 * it's ever used for attributes), but the alternative is duplicating the function for spans and
759 * other virtual arrays. */
760 devirtualize_varray(src, [&](const auto src) {
761 threading::parallel_for(curve_indices.index_range(), 512, [&](IndexRange range) {
762 for (const int i : range) {
763 dst.slice(mesh_offsets[i]).fill(src[curve_indices[i]]);
764 }
765 });
766 });
767}
768
770 const Span<int> curve_indices,
771 const AttrDomain dst_domain,
772 const GVArray &src,
773 GMutableSpan dst)
774{
775 Span<int> offsets;
776 switch (dst_domain) {
778 offsets = mesh_offsets.vert;
779 break;
780 case AttrDomain::Edge:
781 offsets = mesh_offsets.edge;
782 break;
783 case AttrDomain::Face:
784 offsets = mesh_offsets.face;
785 break;
787 offsets = mesh_offsets.loop;
788 break;
789 default:
791 return;
792 }
793 attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
794 using T = decltype(dummy);
795 copy_indices_to_offset_ranges(src.typed<T>(), curve_indices, offsets, dst.typed<T>());
796 });
797}
798
799static void write_sharp_bezier_edges(const CurvesInfo &curves_info,
800 const ResultOffsets &offsets,
801 MutableAttributeAccessor mesh_attributes,
802 SpanAttributeWriter<bool> &sharp_edges)
803{
804 const CurvesGeometry &profile = curves_info.profile;
806 return;
807 }
808 const VArraySpan<int8_t> handle_types_left{profile.handle_types_left()};
809 const VArraySpan<int8_t> handle_types_right{profile.handle_types_right()};
810 if (!handle_types_left.contains(BEZIER_HANDLE_VECTOR) &&
811 !handle_types_right.contains(BEZIER_HANDLE_VECTOR))
812 {
813 return;
814 }
815
816 sharp_edges = mesh_attributes.lookup_or_add_for_write_span<bool>("sharp_edge", AttrDomain::Edge);
817
818 const OffsetIndices profile_points_by_curve = profile.points_by_curve();
819 const VArray<int8_t> types = profile.curve_types();
820 foreach_curve_combination(curves_info, offsets, [&](const CombinationInfo &info) {
821 if (types[info.i_profile] == CURVE_TYPE_BEZIER) {
822 const IndexRange points = profile_points_by_curve[info.i_profile];
824 info.main_segment_num,
826 handle_types_left.slice(points),
827 handle_types_right.slice(points),
828 sharp_edges.span.slice(info.edge_range));
829 }
830 });
831}
832
834 const CurvesGeometry &profile,
835 const VArray<float> &scales,
836 const bool fill_caps,
837 const AttributeFilter &attribute_filter)
838{
839 const CurvesInfo curves_info = get_curves_info(main, profile);
840
841 const ResultOffsets offsets = calculate_result_offsets(curves_info, fill_caps);
842 if (offsets.vert.last() == 0) {
843 return nullptr;
844 }
845
846 /* Add the position attribute later so it can be shared in some cases. */
848 0, offsets.edge.last(), offsets.face.last(), offsets.loop.last());
849 CustomData_free_layer_named(&mesh->vert_data, "position");
850 mesh->verts_num = offsets.vert.last();
851
852 MutableSpan<int2> edges = mesh->edges_for_write();
853 MutableSpan<int> face_offsets = mesh->face_offsets_for_write();
854 MutableSpan<int> corner_verts = mesh->corner_verts_for_write();
855 MutableSpan<int> corner_edges = mesh->corner_edges_for_write();
856 MutableAttributeAccessor mesh_attributes = mesh->attributes_for_write();
857
858 foreach_curve_combination(curves_info, offsets, [&](const CombinationInfo &info) {
860 info.edge_range.start(),
861 info.face_range.start(),
862 info.loop_range.start(),
863 info.main_points.size(),
864 info.profile_points.size(),
865 info.main_cyclic,
866 info.profile_cyclic,
867 fill_caps,
868 edges,
869 corner_verts,
870 corner_edges,
871 face_offsets);
872 });
873
874 if (fill_caps) {
875 /* TODO: This is used to keep the tests passing after refactoring mesh shade smooth flags. It
876 * can be removed if the tests are updated and the final shading results will be the same. */
877 SpanAttributeWriter<bool> sharp_faces = mesh_attributes.lookup_or_add_for_write_span<bool>(
878 "sharp_face", AttrDomain::Face);
879 foreach_curve_combination(curves_info, offsets, [&](const CombinationInfo &info) {
881 const int face_num = info.main_segment_num * info.profile_segment_num;
882 const int cap_face_offset = info.face_range.start() + face_num;
883 sharp_faces.span[cap_face_offset] = true;
884 sharp_faces.span[cap_face_offset + 1] = true;
885 }
886 });
887 sharp_faces.finish();
888 }
889
890 Vector<std::byte> eval_buffer;
891
892 /* Make sure curve attributes can be interpolated. */
893 main.ensure_can_interpolate_to_evaluated();
894
895 build_mesh_positions(curves_info, offsets, scales, eval_buffer, *mesh);
896
897 mesh->tag_overlapping_none();
898 if (!offsets.any_single_point_main) {
899 /* If there are no single point curves, every combination will have at least loose edges. */
900 mesh->tag_loose_verts_none();
901 if (!offsets.any_single_point_profile) {
902 /* If there are no single point profiles, every combination will have faces. */
903 mesh->tag_loose_edges_none();
904 }
905 }
906
907 SpanAttributeWriter<bool> sharp_edges;
908 write_sharp_bezier_edges(curves_info, offsets, mesh_attributes, sharp_edges);
909 if (fill_caps) {
910 if (!sharp_edges) {
911 sharp_edges = mesh_attributes.lookup_or_add_for_write_span<bool>("sharp_edge",
913 }
914 foreach_curve_combination(curves_info, offsets, [&](const CombinationInfo &info) {
915 if (info.main_cyclic || !info.profile_cyclic) {
916 return;
917 }
918 const int main_edges_start = info.edge_range.start();
919 const int last_ring_index = info.main_points.size() - 1;
920 const int profile_edges_start = main_edges_start +
922 const int last_ring_edge_offset = profile_edges_start +
923 info.profile_segment_num * last_ring_index;
924
925 sharp_edges.span.slice(profile_edges_start, info.profile_segment_num).fill(true);
926 sharp_edges.span.slice(last_ring_edge_offset, info.profile_segment_num).fill(true);
927 });
928 }
929 sharp_edges.finish();
930
931 const AttributeAccessor main_attributes = main.attributes();
932 main_attributes.foreach_attribute([&](const AttributeIter &iter) {
933 if (!should_add_attribute_to_mesh(main_attributes,
934 mesh_attributes,
935 iter.name,
936 {iter.domain, iter.data_type},
937 attribute_filter))
938 {
939 return;
940 }
941
942 const AttrDomain src_domain = iter.domain;
943 const eCustomDataType type = iter.data_type;
944 const GAttributeReader src = iter.get();
945 const AttrDomain dst_domain = get_attribute_domain_for_mesh(mesh_attributes, iter.name);
946
947 if (src_domain == AttrDomain::Point) {
949 curves_info, iter.name, offsets, dst_domain, src, eval_buffer, mesh_attributes);
950 }
951 else if (src_domain == AttrDomain::Curve) {
953 iter.name, dst_domain, type);
954 if (dst) {
956 offsets, offsets.main_indices, dst_domain, *src, dst.span);
957 }
958 dst.finish();
959 }
960 });
961
962 /* Make sure profile attributes can be interpolated. */
964
965 const AttributeAccessor profile_attributes = profile.attributes();
966 profile_attributes.foreach_attribute([&](const AttributeIter &iter) {
967 if (main_attributes.contains(iter.name)) {
968 return;
969 }
970 if (!should_add_attribute_to_mesh(profile_attributes,
971 mesh_attributes,
972 iter.name,
973 {iter.domain, iter.data_type},
974 attribute_filter))
975 {
976 return;
977 }
978 const AttrDomain src_domain = iter.domain;
979 const eCustomDataType type = iter.data_type;
980 const GVArray src = *iter.get();
981
982 const AttrDomain dst_domain = get_attribute_domain_for_mesh(mesh_attributes, iter.name);
984 iter.name, dst_domain, type);
985 if (!dst) {
986 return;
987 }
988
989 if (src_domain == AttrDomain::Point) {
991 offsets,
992 dst_domain,
993 evaluate_attribute(src, profile, eval_buffer),
994 dst.span);
995 }
996 else if (src_domain == AttrDomain::Curve) {
998 offsets, offsets.profile_indices, dst_domain, src, dst.span);
999 }
1000
1001 dst.finish();
1002 });
1003
1004 return mesh;
1005}
1006
1008{
1009 CurvesGeometry curves(1, 1);
1010 curves.offsets_for_write().last() = 1;
1011 curves.positions_for_write().fill(float3(0.0f));
1012 curves.fill_curve_types(CURVE_TYPE_POLY);
1013
1014 return curves;
1015}
1016
1017Mesh *curve_to_wire_mesh(const CurvesGeometry &curve, const AttributeFilter &attribute_filter)
1018{
1019 static const CurvesGeometry vert_curve = get_curve_single_vert();
1020 return curve_to_mesh_sweep(curve, vert_curve, {}, false, attribute_filter);
1021}
1022
1023} // namespace blender::bke
Low-level operations for curves.
CustomData interface, see also DNA_customdata_types.h.
bool CustomData_free_layer_named(CustomData *data, blender::StringRef name)
Mesh * BKE_mesh_new_nomain(int verts_num, int edges_num, int faces_num, int corners_num)
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
@ CURVE_TYPE_BEZIER
@ CURVE_TYPE_POLY
@ BEZIER_HANDLE_VECTOR
@ CD_PROP_STRING
void init()
long long int int64_t
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
const T & last(const int64_t n=0) const
Definition BLI_array.hh:285
const CPPType & type() const
const void * data() const
void materialize(void *dst) const
constexpr int64_t size() const
constexpr int64_t start() const
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
Definition BLI_span.hh:573
constexpr void fill(const T &value) const
Definition BLI_span.hh:517
constexpr Span slice(int64_t start, int64_t size) const
Definition BLI_span.hh:137
constexpr const T & first() const
Definition BLI_span.hh:315
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
constexpr bool is_empty() const
Definition BLI_span.hh:260
constexpr bool contains(const T &value) const
Definition BLI_span.hh:277
std::optional< T > get_if_single() const
void reinitialize(const int64_t new_size)
bool is_builtin(const StringRef attribute_id) const
void foreach_attribute(const FunctionRef< void(const AttributeIter &)> fn) const
bool contains(StringRef attribute_id) const
GAttributeReader lookup(const StringRef attribute_id) const
int domain_size(const AttrDomain domain) const
std::optional< AttributeMetaData > lookup_meta_data(StringRef attribute_id) const
GAttributeReader get() const
VArray< int8_t > handle_types_left() const
OffsetIndices< int > points_by_curve() const
VArray< int8_t > handle_types_right() const
void ensure_can_interpolate_to_evaluated() const
Span< float3 > evaluated_tangents() const
Span< int > bezier_evaluated_offsets_for_curve(int curve_index) const
Span< float3 > evaluated_normals() const
OffsetIndices< int > evaluated_points_by_curve() const
bool has_curve_with_type(CurveType type) const
AttributeAccessor attributes() const
Span< float3 > evaluated_positions() const
VArray< int8_t > curve_types() const
GSpanAttributeWriter lookup_or_add_for_write_span(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type, const AttributeInit &initializer=AttributeInitDefaultValue())
bool add(const StringRef attribute_id, const AttrDomain domain, const eCustomDataType data_type, const AttributeInit &initializer)
GSpanAttributeWriter lookup_or_add_for_write_only_span(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type)
static float normals[][3]
VecBase< float, 3 > float3
#define main()
static char ** types
Definition makesdna.cc:71
void copy(const GVArray &src, GMutableSpan dst, int64_t grain_size=4096)
void convert_to_static_type(const CPPType &cpp_type, const Func &func)
bool point_is_sharp(Span< int8_t > handle_types_left, Span< int8_t > handle_types_right, int index)
int segments_num(const int points_num, const bool cyclic)
static bool has_caps(const bool main_cyclic, const bool profile_cyclic, const int profile_segment_num)
static void copy_main_point_data_to_mesh_verts(const Span< T > src, const int profile_point_num, MutableSpan< T > dst)
static void copy_indices_to_offset_ranges(const VArray< T > &src, const Span< int > curve_indices, const OffsetIndices< int > mesh_offsets, MutableSpan< T > dst)
static GSpan evaluate_attribute(const GVArray &src, const CurvesGeometry &curves, Vector< std::byte > &buffer)
static void copy_curve_domain_attribute_to_mesh(const ResultOffsets &mesh_offsets, const Span< int > curve_indices, const AttrDomain dst_domain, const GVArray &src, GMutableSpan dst)
static CurvesInfo get_curves_info(const CurvesGeometry &main, const CurvesGeometry &profile)
static void copy_profile_point_data_to_mesh_edges(const Span< T > src, const int main_segment_num, MutableSpan< T > dst)
static CurvesGeometry get_curve_single_vert()
static void fill_mesh_positions(const int main_point_num, const int profile_point_num, const Span< float3 > main_positions, const Span< float3 > profile_positions, const Span< float3 > tangents, const Span< float3 > normals, const Span< float > scales, MutableSpan< float3 > mesh_positions)
static bool try_sharing_point_data(const CurvesGeometry &main, const StringRef id, const GAttributeReader &src, MutableAttributeAccessor mesh_attributes)
eCustomDataType cpp_type_to_custom_data_type(const CPPType &type)
static void copy_main_point_data_to_mesh_faces(const Span< T > src, const int main_segment_num, const int profile_segment_num, MutableSpan< T > dst)
static void foreach_curve_combination(const CurvesInfo &info, const ResultOffsets &offsets, const Fn &fn)
static void copy_profile_point_domain_attribute_to_mesh(const CurvesInfo &curves_info, const ResultOffsets &offsets, const AttrDomain dst_domain, const GSpan src_all, GMutableSpan dst_all)
static void mark_bezier_vector_edges_sharp(const int profile_point_num, const int main_segment_num, const Span< int > control_point_offsets, const Span< int8_t > handle_types_left, const Span< int8_t > handle_types_right, MutableSpan< bool > sharp_edges)
static AttrDomain get_attribute_domain_for_mesh(const AttributeAccessor &mesh_attributes, const StringRef attribute_id)
static void copy_main_point_domain_attribute_to_mesh(const CurvesInfo &curves_info, const StringRef id, const ResultOffsets &offsets, const AttrDomain dst_domain, const GAttributeReader &src_attribute, Vector< std::byte > &eval_buffer, MutableAttributeAccessor mesh_attributes)
static void fill_mesh_topology(const int vert_offset, const int edge_offset, const int face_offset, const int loop_offset, const int main_point_num, const int profile_point_num, const bool main_cyclic, const bool profile_cyclic, const bool fill_caps, MutableSpan< int2 > edges, MutableSpan< int > corner_verts, MutableSpan< int > corner_edges, MutableSpan< int > face_offsets)
static void write_sharp_bezier_edges(const CurvesInfo &curves_info, const ResultOffsets &offsets, MutableAttributeAccessor mesh_attributes, SpanAttributeWriter< bool > &sharp_edges)
static ResultOffsets calculate_result_offsets(const CurvesInfo &info, const bool fill_caps)
static bool should_add_attribute_to_mesh(const AttributeAccessor &curve_attributes, const AttributeAccessor &mesh_attributes, const StringRef id, const AttributeMetaData &meta_data, const AttributeFilter &attribute_filter)
static void copy_profile_point_data_to_mesh_verts(const Span< T > src, const int main_point_num, MutableSpan< T > dst)
static void copy_main_point_data_to_mesh_edges(const Span< T > src, const int profile_point_num, const int main_segment_num, const int profile_segment_num, MutableSpan< T > dst)
static bool try_direct_evaluate_point_data(const CurvesGeometry &main, const GAttributeReader &src, GMutableSpan dst)
Mesh * curve_to_mesh_sweep(const CurvesGeometry &main, const CurvesGeometry &profile, const VArray< float > &scales, bool fill_caps, const bke::AttributeFilter &attribute_filter={})
Mesh * curve_to_wire_mesh(const CurvesGeometry &curve, const bke::AttributeFilter &attribute_filter={})
static float4x4 build_point_matrix(const float3 &location, const float3 &tangent, const float3 &normal)
static int segments_num_no_duplicate_edge(const int points_num, const bool cyclic)
static void build_mesh_positions(const CurvesInfo &curves_info, const ResultOffsets &offsets, const VArray< float > &scales, Vector< std::byte > &eval_buffer, Mesh &mesh)
static bool offsets_contain_single_point(const OffsetIndices< int > offsets)
static void copy_profile_point_data_to_mesh_faces(const Span< T > src, const int main_segment_num, const int profile_segment_num, MutableSpan< T > dst)
MatBase< T, NumCol, NumRow > scale(const MatBase< T, NumCol, NumRow > &mat, const VectorT &scale)
AxisSigned cross(const AxisSigned a, const AxisSigned b)
MatBase< T, NumCol, NumRow > normalize(const MatBase< T, NumCol, NumRow > &a)
bool is_equal(const MatBase< T, NumCol, NumRow > &a, const MatBase< T, NumCol, NumRow > &b, const T epsilon=T(0))
VecBase< T, 3 > transform_point(const CartesianBasis &basis, const VecBase< T, 3 > &v)
void parallel_invoke(Functions &&...functions)
Definition BLI_task.hh:221
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:93
void devirtualize_varray(const VArray< T > &varray, const Func &func, bool enable=true)
MatBase< float, 4, 4 > float4x4
VecBase< int32_t, 2 > int2
VecBase< float, 3 > float3
static void init(bNodeTree *, bNode *node)
bool allow_skip(const StringRef name) const
const CurvesGeometry & profile
const ImplicitSharingInfo * sharing_info
i
Definition text_draw.cc:230