Blender V5.0
mesh_attributes.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2024 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
10#include "BKE_deform.hh"
11#include "BKE_mesh.hh"
12
13#include "DNA_meshdata_types.h"
14#include "DNA_object_types.h"
15
16#include "BLI_listbase.h"
17
19
21
22namespace blender::bke {
23
24template<typename T>
26 const VArray<T> &src,
27 MutableSpan<T> r_dst)
28{
29 BLI_assert(r_dst.size() == mesh.verts_num);
30 const GroupedSpan<int> vert_to_face_map = mesh.vert_to_face_map();
31 const Span<int> corner_verts = mesh.corner_verts();
32 const OffsetIndices<int> faces = mesh.faces();
33
34 threading::parallel_for(vert_to_face_map.index_range(), 2048, [&](const IndexRange range) {
35 for (const int64_t vert : range) {
36 const Span<int> vert_faces = vert_to_face_map[vert];
37
38 attribute_math::DefaultMixer<T> mixer({&r_dst[vert], 1});
39 for (const int face : vert_faces) {
40 const int corner = mesh::face_find_corner_from_vert(faces[face], corner_verts, int(vert));
41 mixer.mix_in(0, src[corner]);
42 }
43 mixer.finalize();
44 }
45 });
46}
47
48/* A vertex is selected if all connected face corners were selected and it is not loose. */
49template<>
51 const VArray<bool> &src,
53{
54 BLI_assert(r_dst.size() == mesh.verts_num);
55 const Span<int> corner_verts = mesh.corner_verts();
56
57 r_dst.fill(true);
58 threading::parallel_for(IndexRange(mesh.corners_num), 4096, [&](const IndexRange range) {
59 for (const int corner : range) {
60 const int vert = corner_verts[corner];
61 if (!src[corner]) {
62 r_dst[vert] = false;
63 }
64 }
65 });
66
67 /* Deselect loose vertices without corners that are still selected from the 'true' default. */
68 const LooseVertCache &loose_verts = mesh.verts_no_face();
69 if (loose_verts.count > 0) {
70 const BitSpan bits = loose_verts.is_loose_bits;
71 threading::parallel_for(bits.index_range(), 2048, [&](const IndexRange range) {
72 for (const int vert_index : range) {
73 if (bits[vert_index]) {
74 r_dst[vert_index] = false;
75 }
76 }
77 });
78 }
79}
80
82{
83 GArray<> values(varray.type(), mesh.verts_num);
84 attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
85 using T = decltype(dummy);
86 if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
87 /* We compute all interpolated values at once, because for this interpolation, one has to
88 * iterate over all loops anyway. */
89 adapt_mesh_domain_corner_to_point_impl<T>(
90 mesh, varray.typed<T>(), values.as_mutable_span().typed<T>());
91 }
92 });
93 return GVArray::from_garray(std::move(values));
94}
95
100{
101 const Span<int> corner_verts = mesh.corner_verts();
102
103 GVArray new_varray;
104 attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
105 using T = decltype(dummy);
106 new_varray = VArray<T>::from_func(
107 mesh.corners_num, [corner_verts, varray = varray.typed<T>()](const int64_t corner) {
108 return varray[corner_verts[corner]];
109 });
110 });
111 return new_varray;
112}
113
115{
116 const OffsetIndices faces = mesh.faces();
117
118 GVArray new_varray;
119 attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
120 using T = decltype(dummy);
121 if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
122 if constexpr (std::is_same_v<T, bool>) {
123 new_varray = VArray<T>::from_func(
124 faces.size(), [faces, varray = varray.typed<bool>()](const int face_index) {
125 /* A face is selected if all of its corners were selected. */
126 for (const int corner : faces[face_index]) {
127 if (!varray[corner]) {
128 return false;
129 }
130 }
131 return true;
132 });
133 }
134 else {
135 new_varray = VArray<T>::from_func(
136 faces.size(), [faces, varray = varray.typed<T>()](const int face_index) {
137 T return_value;
138 attribute_math::DefaultMixer<T> mixer({&return_value, 1});
139 for (const int corner : faces[face_index]) {
140 const T value = varray[corner];
141 mixer.mix_in(0, value);
142 }
143 mixer.finalize();
144 return return_value;
145 });
146 }
147 }
148 });
149 return new_varray;
150}
151
152template<typename T>
154 const VArray<T> &old_values,
155 MutableSpan<T> r_values)
156{
157 BLI_assert(r_values.size() == mesh.edges_num);
158 const OffsetIndices faces = mesh.faces();
159 const Span<int> corner_edges = mesh.corner_edges();
160
161 attribute_math::DefaultMixer<T> mixer(r_values);
162
163 for (const int face_index : faces.index_range()) {
164 const IndexRange face = faces[face_index];
165
166 /* For every edge, mix values from the two adjacent corners (the current and next corner). */
167 for (const int corner : face) {
168 const int next_corner = mesh::face_corner_next(face, corner);
169 const int edge_index = corner_edges[corner];
170 mixer.mix_in(edge_index, old_values[corner]);
171 mixer.mix_in(edge_index, old_values[next_corner]);
172 }
173 }
174
175 mixer.finalize();
176}
177
178/* An edge is selected if all corners on adjacent faces were selected. */
179template<>
181 const VArray<bool> &old_values,
182 MutableSpan<bool> r_values)
183{
184 BLI_assert(r_values.size() == mesh.edges_num);
185 const OffsetIndices faces = mesh.faces();
186 const Span<int> corner_edges = mesh.corner_edges();
187
188 r_values.fill(true);
189 for (const int face_index : faces.index_range()) {
190 const IndexRange face = faces[face_index];
191
192 for (const int corner : face) {
193 const int next_corner = mesh::face_corner_next(face, corner);
194 const int edge_index = corner_edges[corner];
195 if (!old_values[corner] || !old_values[next_corner]) {
196 r_values[edge_index] = false;
197 }
198 }
199 }
200
201 const LooseEdgeCache &loose_edges = mesh.loose_edges();
202 if (loose_edges.count > 0) {
203 /* Deselect loose edges without corners that are still selected from the 'true' default. */
204 threading::parallel_for(IndexRange(mesh.edges_num), 2048, [&](const IndexRange range) {
205 for (const int edge_index : range) {
206 if (loose_edges.is_loose_bits[edge_index]) {
207 r_values[edge_index] = false;
208 }
209 }
210 });
211 }
212}
213
215{
216 GArray<> values(varray.type(), mesh.edges_num);
217 attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
218 using T = decltype(dummy);
219 if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
220 adapt_mesh_domain_corner_to_edge_impl<T>(
221 mesh, varray.typed<T>(), values.as_mutable_span().typed<T>());
222 }
223 });
224 return GVArray::from_garray(std::move(values));
225}
226
228{
229 GVArray new_varray;
230 attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
231 using T = decltype(dummy);
232 if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
233 VArray<T> src = varray.typed<T>();
234 const GroupedSpan<int> vert_to_face_map = mesh.vert_to_face_map();
235 if constexpr (std::is_same_v<T, bool>) {
236 new_varray = VArray<T>::from_func(
237 mesh.verts_num, [vert_to_face_map, src](const int point_i) {
238 const Span<int> vert_faces = vert_to_face_map[point_i];
239 /* A vertex is selected if any of the connected faces were selected. */
240 return std::any_of(
241 vert_faces.begin(), vert_faces.end(), [&](const int face) { return src[face]; });
242 });
243 }
244 else {
245 new_varray = VArray<T>::from_func(
246 mesh.verts_num, [vert_to_face_map, src](const int point_i) {
247 const Span<int> vert_faces = vert_to_face_map[point_i];
248 T return_value;
249 attribute_math::DefaultMixer<T> mixer({&return_value, 1});
250 for (const int face : vert_faces) {
251 mixer.mix_in(0, src[face]);
252 }
253 mixer.finalize();
254 return return_value;
255 });
256 }
257 }
258 });
259 return new_varray;
260}
261
262/* Each corner's value is simply a copy of the value at its face. */
263template<typename T>
265 const VArray<T> &old_values,
266 MutableSpan<T> r_values)
267{
268 BLI_assert(r_values.size() == mesh.corners_num);
269 const OffsetIndices faces = mesh.faces();
270
271 threading::parallel_for(faces.index_range(), 1024, [&](const IndexRange range) {
272 for (const int face_index : range) {
273 MutableSpan<T> face_corner_values = r_values.slice(faces[face_index]);
274 face_corner_values.fill(old_values[face_index]);
275 }
276 });
277}
278
280{
281 GArray<> values(varray.type(), mesh.corners_num);
282 attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
283 using T = decltype(dummy);
284 if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
285 adapt_mesh_domain_face_to_corner_impl<T>(
286 mesh, varray.typed<T>(), values.as_mutable_span().typed<T>());
287 }
288 });
289 return GVArray::from_garray(std::move(values));
290}
291
292template<typename T>
294 const VArray<T> &old_values,
295 MutableSpan<T> r_values)
296{
297 BLI_assert(r_values.size() == mesh.edges_num);
298 const OffsetIndices faces = mesh.faces();
299 const Span<int> corner_edges = mesh.corner_edges();
300
301 attribute_math::DefaultMixer<T> mixer(r_values);
302
303 for (const int face_index : faces.index_range()) {
304 const T value = old_values[face_index];
305 for (const int edge : corner_edges.slice(faces[face_index])) {
306 mixer.mix_in(edge, value);
307 }
308 }
309 mixer.finalize();
310}
311
312/* An edge is selected if any connected face was selected. */
313template<>
315 const VArray<bool> &old_values,
316 MutableSpan<bool> r_values)
317{
318 BLI_assert(r_values.size() == mesh.edges_num);
319 const OffsetIndices faces = mesh.faces();
320 const Span<int> corner_edges = mesh.corner_edges();
321
322 r_values.fill(false);
323 threading::parallel_for(faces.index_range(), 2048, [&](const IndexRange range) {
324 for (const int face_index : range) {
325 if (old_values[face_index]) {
326 for (const int edge : corner_edges.slice(faces[face_index])) {
327 r_values[edge] = true;
328 }
329 }
330 }
331 });
332}
333
335{
336 GArray<> values(varray.type(), mesh.edges_num);
337 attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
338 using T = decltype(dummy);
339 if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
340 adapt_mesh_domain_face_to_edge_impl<T>(
341 mesh, varray.typed<T>(), values.as_mutable_span().typed<T>());
342 }
343 });
344 return GVArray::from_garray(std::move(values));
345}
346
348{
349 const OffsetIndices faces = mesh.faces();
350 const Span<int> corner_verts = mesh.corner_verts();
351
352 GVArray new_varray;
353 attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
354 using T = decltype(dummy);
355 if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
356 if constexpr (std::is_same_v<T, bool>) {
357 new_varray = VArray<T>::from_func(
358 mesh.faces_num,
359 [corner_verts, faces, varray = varray.typed<bool>()](const int face_index) {
360 /* A face is selected if all of its vertices were selected. */
361 for (const int vert : corner_verts.slice(faces[face_index])) {
362 if (!varray[vert]) {
363 return false;
364 }
365 }
366 return true;
367 });
368 }
369 else {
370 new_varray = VArray<T>::from_func(
371 mesh.faces_num,
372 [corner_verts, faces, varray = varray.typed<T>()](const int face_index) {
373 T return_value;
374 attribute_math::DefaultMixer<T> mixer({&return_value, 1});
375 for (const int vert : corner_verts.slice(faces[face_index])) {
376 mixer.mix_in(0, varray[vert]);
377 }
378 mixer.finalize();
379 return return_value;
380 });
381 }
382 }
383 });
384 return new_varray;
385}
386
388{
389 const Span<int2> edges = mesh.edges();
390
391 GVArray new_varray;
392 attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
393 using T = decltype(dummy);
394 if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
395 if constexpr (std::is_same_v<T, bool>) {
396 /* An edge is selected if both of its vertices were selected. */
397 new_varray = VArray<bool>::from_func(
398 edges.size(), [edges, varray = varray.typed<bool>()](const int edge_index) {
399 const int2 &edge = edges[edge_index];
400 return varray[edge[0]] && varray[edge[1]];
401 });
402 }
403 else {
404 new_varray = VArray<T>::from_func(
405 edges.size(), [edges, varray = varray.typed<T>()](const int edge_index) {
406 const int2 &edge = edges[edge_index];
407 return attribute_math::mix2(0.5f, varray[edge[0]], varray[edge[1]]);
408 });
409 }
410 }
411 });
412 return new_varray;
413}
414
415template<typename T>
417 const VArray<T> &old_values,
418 MutableSpan<T> r_values)
419{
420 BLI_assert(r_values.size() == mesh.corners_num);
421 const OffsetIndices faces = mesh.faces();
422 const Span<int> corner_edges = mesh.corner_edges();
423
424 attribute_math::DefaultMixer<T> mixer(r_values);
425
426 for (const int face_index : faces.index_range()) {
427 const IndexRange face = faces[face_index];
428
429 /* For every corner, mix the values from the adjacent edges on the face. */
430 for (const int corner : face) {
431 const int corner_prev = mesh::face_corner_prev(face, corner);
432 const int edge = corner_edges[corner];
433 const int edge_prev = corner_edges[corner_prev];
434 mixer.mix_in(corner, old_values[edge]);
435 mixer.mix_in(corner, old_values[edge_prev]);
436 }
437 }
438
439 mixer.finalize();
440}
441
442/* A corner is selected if its two adjacent edges were selected. */
443template<>
445 const VArray<bool> &old_values,
446 MutableSpan<bool> r_values)
447{
448 BLI_assert(r_values.size() == mesh.corners_num);
449 const OffsetIndices faces = mesh.faces();
450 const Span<int> corner_edges = mesh.corner_edges();
451
452 r_values.fill(false);
453
454 threading::parallel_for(faces.index_range(), 2048, [&](const IndexRange range) {
455 for (const int face_index : range) {
456 const IndexRange face = faces[face_index];
457 for (const int corner : face) {
458 const int corner_prev = mesh::face_corner_prev(face, corner);
459 const int edge = corner_edges[corner];
460 const int edge_prev = corner_edges[corner_prev];
461 if (old_values[edge] && old_values[edge_prev]) {
462 r_values[corner] = true;
463 }
464 }
465 }
466 });
467}
468
470{
471 GArray<> values(varray.type(), mesh.corners_num);
472 attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
473 using T = decltype(dummy);
474 if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
475 adapt_mesh_domain_edge_to_corner_impl<T>(
476 mesh, varray.typed<T>(), values.as_mutable_span().typed<T>());
477 }
478 });
479 return GVArray::from_garray(std::move(values));
480}
481
482template<typename T>
484 const VArray<T> &old_values,
485 MutableSpan<T> r_values)
486{
487 BLI_assert(r_values.size() == mesh.verts_num);
488 const Span<int2> edges = mesh.edges();
489
490 attribute_math::DefaultMixer<T> mixer(r_values);
491
492 for (const int edge_index : IndexRange(mesh.edges_num)) {
493 const int2 &edge = edges[edge_index];
494 const T value = old_values[edge_index];
495 mixer.mix_in(edge[0], value);
496 mixer.mix_in(edge[1], value);
497 }
498
499 mixer.finalize();
500}
501
502/* A vertex is selected if any connected edge was selected. */
503template<>
505 const VArray<bool> &old_values,
506 MutableSpan<bool> r_values)
507{
508 BLI_assert(r_values.size() == mesh.verts_num);
509 const Span<int2> edges = mesh.edges();
510
511 /* Multiple threads can write to the same index here, but they are only
512 * writing true, and writing to single bytes is expected to be threadsafe. */
513 r_values.fill(false);
514 threading::parallel_for(edges.index_range(), 4096, [&](const IndexRange range) {
515 for (const int edge_index : range) {
516 if (old_values[edge_index]) {
517 const int2 &edge = edges[edge_index];
518 r_values[edge[0]] = true;
519 r_values[edge[1]] = true;
520 }
521 }
522 });
523}
524
526{
527 GArray<> values(varray.type(), mesh.verts_num);
528 attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
529 using T = decltype(dummy);
530 if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
531 adapt_mesh_domain_edge_to_point_impl<T>(
532 mesh, varray.typed<T>(), values.as_mutable_span().typed<T>());
533 }
534 });
535 return GVArray::from_garray(std::move(values));
536}
537
539{
540 const OffsetIndices faces = mesh.faces();
541 const Span<int> corner_edges = mesh.corner_edges();
542
543 GVArray new_varray;
544 attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
545 using T = decltype(dummy);
546 if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
547 if constexpr (std::is_same_v<T, bool>) {
548 /* A face is selected if all of its edges are selected. */
549 new_varray = VArray<bool>::from_func(
550 faces.size(), [corner_edges, faces, varray = varray.typed<T>()](const int face_index) {
551 for (const int edge : corner_edges.slice(faces[face_index])) {
552 if (!varray[edge]) {
553 return false;
554 }
555 }
556 return true;
557 });
558 }
559 else {
560 new_varray = VArray<T>::from_func(
561 faces.size(), [corner_edges, faces, varray = varray.typed<T>()](const int face_index) {
562 T return_value;
563 attribute_math::DefaultMixer<T> mixer({&return_value, 1});
564 for (const int edge : corner_edges.slice(faces[face_index])) {
565 mixer.mix_in(0, varray[edge]);
566 }
567 mixer.finalize();
568 return return_value;
569 });
570 }
571 }
572 });
573 return new_varray;
574}
575
577 const AttrDomain from_domain,
578 const AttrDomain to_domain)
579{
580 /* For some domain combinations, a single value will always map directly. For others, there may
581 * be loose elements on the result domain that should have the default value rather than the
582 * single value from the source. */
583 switch (from_domain) {
585 /* All other domains are always connected to points. */
586 return true;
587 case AttrDomain::Edge:
588 if (to_domain == AttrDomain::Point) {
589 return mesh.loose_verts().count == 0;
590 }
591 return true;
592 case AttrDomain::Face:
593 if (to_domain == AttrDomain::Point) {
594 return mesh.verts_no_face().count == 0;
595 }
596 if (to_domain == AttrDomain::Edge) {
597 return mesh.loose_edges().count == 0;
598 }
599 return true;
601 if (to_domain == AttrDomain::Point) {
602 return mesh.verts_no_face().count == 0;
603 }
604 if (to_domain == AttrDomain::Edge) {
605 return mesh.loose_edges().count == 0;
606 }
607 return true;
608 default:
610 return false;
611 }
612}
613
615 const GVArray &varray,
616 const AttrDomain from_domain,
617 const AttrDomain to_domain)
618{
619 if (!varray) {
620 return {};
621 }
622 if (varray.is_empty()) {
623 return {};
624 }
625 if (from_domain == to_domain) {
626 return varray;
627 }
628 if (varray.is_single()) {
629 if (can_simple_adapt_for_single(mesh, from_domain, to_domain)) {
630 BUFFER_FOR_CPP_TYPE_VALUE(varray.type(), value);
631 varray.get_internal_single(value);
632 return GVArray::from_single(varray.type(), mesh.attributes().domain_size(to_domain), value);
633 }
634 }
635
636 switch (from_domain) {
637 case AttrDomain::Corner: {
638 switch (to_domain) {
641 case AttrDomain::Face:
643 case AttrDomain::Edge:
645 default:
646 break;
647 }
648 break;
649 }
650 case AttrDomain::Point: {
651 switch (to_domain) {
654 case AttrDomain::Face:
656 case AttrDomain::Edge:
658 default:
659 break;
660 }
661 break;
662 }
663 case AttrDomain::Face: {
664 switch (to_domain) {
669 case AttrDomain::Edge:
670 return adapt_mesh_domain_face_to_edge(mesh, varray);
671 default:
672 break;
673 }
674 break;
675 }
676 case AttrDomain::Edge: {
677 switch (to_domain) {
682 case AttrDomain::Face:
683 return adapt_mesh_domain_edge_to_face(mesh, varray);
684 default:
685 break;
686 }
687 break;
688 }
689 default:
690 break;
691 }
692
693 return {};
694}
695
696static void tag_component_positions_changed(void *owner)
697{
698 Mesh *mesh = static_cast<Mesh *>(owner);
699 if (mesh != nullptr) {
700 mesh->tag_positions_changed();
701 }
702}
703
704static void tag_component_sharpness_changed(void *owner)
705{
706 if (Mesh *mesh = static_cast<Mesh *>(owner)) {
707 mesh->tag_sharpness_changed();
708 }
709}
710
711static void tag_material_index_changed(void *owner)
712{
713 if (Mesh *mesh = static_cast<Mesh *>(owner)) {
714 mesh->tag_material_index_changed();
715 }
716}
717
722 public:
723 GAttributeReader try_get_for_read(const void *owner, const StringRef attribute_id) const final
724 {
725 const Mesh *mesh = static_cast<const Mesh *>(owner);
726 if (mesh == nullptr) {
727 return {};
728 }
729 const int vertex_group_index = BKE_defgroup_name_index(&mesh->vertex_group_names,
730 attribute_id);
731 if (vertex_group_index < 0) {
732 return {};
733 }
734 const Span<MDeformVert> dverts = mesh->deform_verts();
735 return this->get_for_vertex_group_index(*mesh, dverts, vertex_group_index);
736 }
737
739 const Span<MDeformVert> dverts,
740 const int vertex_group_index) const
741 {
742 BLI_assert(vertex_group_index >= 0);
743 if (dverts.is_empty()) {
744 return {VArray<float>::from_single(0.0f, mesh.verts_num), AttrDomain::Point};
745 }
746 return {varray_for_deform_verts(dverts, vertex_group_index), AttrDomain::Point};
747 }
748
749 GAttributeWriter try_get_for_write(void *owner, const StringRef attribute_id) const final
750 {
751 Mesh *mesh = static_cast<Mesh *>(owner);
752 if (mesh == nullptr) {
753 return {};
754 }
755
756 const int vertex_group_index = BKE_defgroup_name_index(&mesh->vertex_group_names,
757 attribute_id);
758 if (vertex_group_index < 0) {
759 return {};
760 }
761 MutableSpan<MDeformVert> dverts = mesh->deform_verts_for_write();
762 return {varray_for_mutable_deform_verts(dverts, vertex_group_index), AttrDomain::Point};
763 }
764
765 bool try_delete(void *owner, const StringRef name) const final
766 {
767 Mesh *mesh = static_cast<Mesh *>(owner);
768 if (mesh == nullptr) {
769 return true;
770 }
771
772 int index;
773 bDeformGroup *group;
774 if (!BKE_id_defgroup_name_find(&mesh->id, name, &index, &group)) {
775 return false;
776 }
777 BLI_remlink(&mesh->vertex_group_names, group);
778 MEM_freeN(group);
779 if (mesh->deform_verts().is_empty()) {
780 return true;
781 }
782
783 MutableSpan<MDeformVert> dverts = mesh->deform_verts_for_write();
784 remove_defgroup_index(dverts, index);
785 return true;
786 }
787
788 bool foreach_attribute(const void *owner,
789 const FunctionRef<void(const AttributeIter &)> fn) const final
790 {
791 const Mesh *mesh = static_cast<const Mesh *>(owner);
792 if (mesh == nullptr) {
793 return true;
794 }
795 const AttributeAccessor accessor = mesh->attributes();
796 const Span<MDeformVert> dverts = mesh->deform_verts();
797
798 int group_index = 0;
799 LISTBASE_FOREACH_INDEX (const bDeformGroup *, group, &mesh->vertex_group_names, group_index) {
800 const auto get_fn = [&]() {
801 return this->get_for_vertex_group_index(*mesh, dverts, group_index);
802 };
803
804 AttributeIter iter{group->name, AttrDomain::Point, bke::AttrType::Float, get_fn};
805 iter.is_builtin = false;
806 iter.accessor = &accessor;
807 fn(iter);
808 if (iter.is_stopped()) {
809 return false;
810 }
811 }
812 return true;
813 }
814
815 void foreach_domain(const FunctionRef<void(AttrDomain)> callback) const final
816 {
817 callback(AttrDomain::Point);
818 }
819};
820
821static std::function<void()> get_tag_modified_function(void *owner, const StringRef name)
822{
823 if (name.startswith(".hide")) {
824 return [owner]() { (static_cast<Mesh *>(owner))->tag_visibility_changed(); };
825 }
826 if (name == "custom_normal") {
827 return [owner]() { (static_cast<Mesh *>(owner))->tag_custom_normals_changed(); };
828 }
829 return {};
830}
831
837{
838#define MAKE_MUTABLE_CUSTOM_DATA_GETTER(NAME) \
839 [](void *owner) -> CustomData * { \
840 Mesh *mesh = static_cast<Mesh *>(owner); \
841 return &mesh->NAME; \
842 }
843#define MAKE_CONST_CUSTOM_DATA_GETTER(NAME) \
844 [](const void *owner) -> const CustomData * { \
845 const Mesh *mesh = static_cast<const Mesh *>(owner); \
846 return &mesh->NAME; \
847 }
848#define MAKE_GET_ELEMENT_NUM_GETTER(NAME) \
849 [](const void *owner) -> int { \
850 const Mesh *mesh = static_cast<const Mesh *>(owner); \
851 return mesh->NAME; \
852 }
853
854 static CustomDataAccessInfo corner_access = {MAKE_MUTABLE_CUSTOM_DATA_GETTER(corner_data),
856 MAKE_GET_ELEMENT_NUM_GETTER(corners_num),
858 static CustomDataAccessInfo point_access = {MAKE_MUTABLE_CUSTOM_DATA_GETTER(vert_data),
862 static CustomDataAccessInfo edge_access = {MAKE_MUTABLE_CUSTOM_DATA_GETTER(edge_data),
866 static CustomDataAccessInfo face_access = {MAKE_MUTABLE_CUSTOM_DATA_GETTER(face_data),
870
871#undef MAKE_CONST_CUSTOM_DATA_GETTER
872#undef MAKE_MUTABLE_CUSTOM_DATA_GETTER
873
874 static BuiltinCustomDataLayerProvider position("position",
878 point_access,
880
881 static const auto material_index_clamp = mf::build::SI1_SO<int, int>(
882 "Material Index Validate",
883 [](int value) {
884 /* Use #short for the maximum since many areas still use that type for indices. */
885 return std::clamp<int>(value, 0, std::numeric_limits<short>::max());
886 },
887 mf::build::exec_presets::AllSpanOrSingle());
888 static BuiltinCustomDataLayerProvider material_index("material_index",
892 face_access,
894 AttributeValidator{&material_index_clamp});
895
896 static const auto int2_index_clamp = mf::build::SI1_SO<int2, int2>(
897 "Index Validate",
898 [](int2 value) { return math::max(value, int2(0)); },
899 mf::build::exec_presets::AllSpanOrSingle());
900 static BuiltinCustomDataLayerProvider edge_verts(".edge_verts",
904 edge_access,
905 nullptr,
906 AttributeValidator{&int2_index_clamp});
907
908 /* NOTE: This clamping is more of a last resort, since it's quite easy to make an
909 * invalid mesh that will crash Blender by arbitrarily editing this attribute. */
910 static const auto int_index_clamp = mf::build::SI1_SO<int, int>(
911 "Index Validate",
912 [](int value) { return std::max(value, 0); },
913 mf::build::exec_presets::AllSpanOrSingle());
914 static BuiltinCustomDataLayerProvider corner_vert(".corner_vert",
918 corner_access,
919 nullptr,
920 AttributeValidator{&int_index_clamp});
921 static BuiltinCustomDataLayerProvider corner_edge(".corner_edge",
925 corner_access,
926 nullptr,
927 AttributeValidator{&int_index_clamp});
928
929 static BuiltinCustomDataLayerProvider sharp_face("sharp_face",
933 face_access,
935
936 static BuiltinCustomDataLayerProvider sharp_edge("sharp_edge",
940 edge_access,
942
943 static MeshVertexGroupsAttributeProvider vertex_groups;
944 static CustomDataAttributeProvider corner_custom_data(AttrDomain::Corner, corner_access);
945 static CustomDataAttributeProvider point_custom_data(AttrDomain::Point, point_access);
946 static CustomDataAttributeProvider edge_custom_data(AttrDomain::Edge, edge_access);
947 static CustomDataAttributeProvider face_custom_data(AttrDomain::Face, face_access);
948
949 return GeometryAttributeProviders({&position,
950 &edge_verts,
951 &corner_vert,
952 &corner_edge,
953 &material_index,
954 &sharp_face,
955 &sharp_edge},
956 {&corner_custom_data,
957 &vertex_groups,
958 &point_custom_data,
959 &edge_custom_data,
960 &face_custom_data});
961}
962
964{
968 fn.domain_size = [](const void *owner, const AttrDomain domain) {
969 if (owner == nullptr) {
970 return 0;
971 }
972 const Mesh &mesh = *static_cast<const Mesh *>(owner);
973 switch (domain) {
975 return mesh.verts_num;
976 case AttrDomain::Edge:
977 return mesh.edges_num;
978 case AttrDomain::Face:
979 return mesh.faces_num;
981 return mesh.corners_num;
982 default:
983 return 0;
984 }
985 };
986 fn.domain_supported = [](const void * /*owner*/, const AttrDomain domain) {
988 };
989 fn.adapt_domain = [](const void *owner,
990 const GVArray &varray,
991 const AttrDomain from_domain,
992 const AttrDomain to_domain) -> GVArray {
993 if (owner == nullptr) {
994 return {};
995 }
996 const Mesh &mesh = *static_cast<const Mesh *>(owner);
997 return adapt_mesh_attribute_domain(mesh, varray, from_domain, to_domain);
998 };
999 return fn;
1000}
1001
1007
1008} // namespace blender::bke
support for deformation groups and hooks.
bool BKE_id_defgroup_name_find(const ID *id, blender::StringRef name, int *r_index, bDeformGroup **r_group)
Definition deform.cc:577
int BKE_defgroup_name_index(const ListBase *defbase, blender::StringRef name)
Definition deform.cc:540
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BUFFER_FOR_CPP_TYPE_VALUE(type, variable_name)
#define final(a, b, c)
Definition BLI_hash.h:19
#define LISTBASE_FOREACH_INDEX(type, var, list, index_var)
void BLI_remlink(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:131
#define ELEM(...)
@ CD_PROP_FLOAT3
@ CD_PROP_INT32_2D
@ CD_PROP_INT32
Object is a sort of wrapper for general info.
static VArray from_single(T value, const int64_t size)
void get_internal_single(void *r_value) const
static GVArray from_garray(GArray<> array)
static GVArray from_single(const CPPType &type, int64_t size, const void *value)
constexpr int64_t size() const
Definition BLI_span.hh:493
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 IndexRange index_range() const
Definition BLI_span.hh:401
constexpr bool is_empty() const
Definition BLI_span.hh:260
const AttributeAccessor * accessor
GAttributeReader get_for_vertex_group_index(const Mesh &mesh, const Span< MDeformVert > dverts, const int vertex_group_index) const
GAttributeReader try_get_for_read(const void *owner, const StringRef attribute_id) const final
bool foreach_attribute(const void *owner, const FunctionRef< void(const AttributeIter &)> fn) const final
void foreach_domain(const FunctionRef< void(AttrDomain)> callback) const final
GAttributeWriter try_get_for_write(void *owner, const StringRef attribute_id) const final
bool try_delete(void *owner, const StringRef name) const final
VecBase< int, 2 > int2
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
#define T
static char faces[256]
#define MAKE_CONST_CUSTOM_DATA_GETTER(NAME)
#define MAKE_GET_ELEMENT_NUM_GETTER(NAME)
#define MAKE_MUTABLE_CUSTOM_DATA_GETTER(NAME)
AttributeAccessorFunctions accessor_functions_for_providers()
void convert_to_static_type(const CPPType &cpp_type, const Func &func)
typename DefaultMixerStruct< T >::type DefaultMixer
int face_corner_prev(const IndexRange face, const int corner)
Definition BKE_mesh.hh:306
int face_corner_next(const IndexRange face, const int corner)
Definition BKE_mesh.hh:315
static std::function< void()> get_tag_modified_function(void *owner, const StringRef name)
static AttributeAccessorFunctions get_mesh_accessor_functions()
static void adapt_mesh_domain_edge_to_point_impl(const Mesh &mesh, const VArray< T > &old_values, MutableSpan< T > r_values)
static GVArray adapt_mesh_domain_face_to_edge(const Mesh &mesh, const GVArray &varray)
static bool can_simple_adapt_for_single(const Mesh &mesh, const AttrDomain from_domain, const AttrDomain to_domain)
static GVArray adapt_mesh_domain_corner_to_point(const Mesh &mesh, const GVArray &varray)
const AttributeAccessorFunctions & mesh_attribute_accessor_functions()
static GVArray adapt_mesh_domain_face_to_corner(const Mesh &mesh, const GVArray &varray)
static void tag_component_sharpness_changed(void *owner)
static GVArray adapt_mesh_domain_edge_to_point(const Mesh &mesh, const GVArray &varray)
static void tag_component_positions_changed(void *owner)
static void adapt_mesh_domain_corner_to_point_impl(const Mesh &mesh, const VArray< T > &src, MutableSpan< T > r_dst)
void remove_defgroup_index(MutableSpan< MDeformVert > dverts, int defgroup_index)
Definition deform.cc:1753
static GVArray adapt_mesh_domain_point_to_corner(const Mesh &mesh, const GVArray &varray)
void adapt_mesh_domain_face_to_edge_impl(const Mesh &mesh, const VArray< T > &old_values, MutableSpan< T > r_values)
static GeometryAttributeProviders create_attribute_providers_for_mesh()
static GVArray adapt_mesh_domain_corner_to_edge(const Mesh &mesh, const GVArray &varray)
static void tag_material_index_changed(void *owner)
static void adapt_mesh_domain_corner_to_edge_impl(const Mesh &mesh, const VArray< T > &old_values, MutableSpan< T > r_values)
static GVArray adapt_mesh_domain_point_to_edge(const Mesh &mesh, const GVArray &varray)
static GVArray adapt_mesh_domain_face_to_point(const Mesh &mesh, const GVArray &varray)
void adapt_mesh_domain_edge_to_corner_impl(const Mesh &mesh, const VArray< T > &old_values, MutableSpan< T > r_values)
static GVArray adapt_mesh_attribute_domain(const Mesh &mesh, const GVArray &varray, const AttrDomain from_domain, const AttrDomain to_domain)
static GVArray adapt_mesh_domain_corner_to_face(const Mesh &mesh, const GVArray &varray)
static GVArray adapt_mesh_domain_edge_to_face(const Mesh &mesh, const GVArray &varray)
static GVArray adapt_mesh_domain_edge_to_corner(const Mesh &mesh, const GVArray &varray)
void adapt_mesh_domain_face_to_corner_impl(const Mesh &mesh, const VArray< T > &old_values, MutableSpan< T > r_values)
static GVArray adapt_mesh_domain_point_to_face(const Mesh &mesh, const GVArray &varray)
VMutableArray< float > varray_for_mutable_deform_verts(MutableSpan< MDeformVert > dverts, int defgroup_index)
Definition deform.cc:1747
VArray< float > varray_for_deform_verts(Span< MDeformVert > dverts, int defgroup_index)
Definition deform.cc:1743
T max(const T &a, const T &b)
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
VecBase< int32_t, 2 > int2
const char * name