Blender V4.5
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::ForGArray(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>::ForFunc(
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>::ForFunc(
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>::ForFunc(
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::ForGArray(std::move(values));
225}
226
227template<typename T>
229 const VArray<T> &src,
230 MutableSpan<T> r_dst)
231{
232 BLI_assert(r_dst.size() == mesh.verts_num);
233 const GroupedSpan<int> vert_to_face_map = mesh.vert_to_face_map();
234
235 threading::parallel_for(vert_to_face_map.index_range(), 2048, [&](const IndexRange range) {
236 for (const int vert : range) {
237 attribute_math::DefaultMixer<T> mixer({&r_dst[vert], 1});
238 for (const int face : vert_to_face_map[vert]) {
239 mixer.mix_in(0, src[face]);
240 }
241 mixer.finalize();
242 }
243 });
244}
245
246/* A vertex is selected if any of the connected faces were selected. */
247template<>
249 const VArray<bool> &src,
250 MutableSpan<bool> r_dst)
251{
252 BLI_assert(r_dst.size() == mesh.verts_num);
253 const GroupedSpan<int> vert_to_face_map = mesh.vert_to_face_map();
254
255 threading::parallel_for(vert_to_face_map.index_range(), 2048, [&](const IndexRange range) {
256 for (const int vert : range) {
257 const Span<int> vert_faces = vert_to_face_map[vert];
258 r_dst[vert] = std::any_of(
259 vert_faces.begin(), vert_faces.end(), [&](const int face) { return src[face]; });
260 }
261 });
262}
263
265{
266 GArray<> values(varray.type(), mesh.verts_num);
267 attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
268 using T = decltype(dummy);
269 if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
270 adapt_mesh_domain_face_to_point_impl<T>(
271 mesh, varray.typed<T>(), values.as_mutable_span().typed<T>());
272 }
273 });
274 return GVArray::ForGArray(std::move(values));
275}
276
277/* Each corner's value is simply a copy of the value at its face. */
278template<typename T>
280 const VArray<T> &old_values,
281 MutableSpan<T> r_values)
282{
283 BLI_assert(r_values.size() == mesh.corners_num);
284 const OffsetIndices faces = mesh.faces();
285
286 threading::parallel_for(faces.index_range(), 1024, [&](const IndexRange range) {
287 for (const int face_index : range) {
288 MutableSpan<T> face_corner_values = r_values.slice(faces[face_index]);
289 face_corner_values.fill(old_values[face_index]);
290 }
291 });
292}
293
295{
296 GArray<> values(varray.type(), mesh.corners_num);
297 attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
298 using T = decltype(dummy);
299 if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
300 adapt_mesh_domain_face_to_corner_impl<T>(
301 mesh, varray.typed<T>(), values.as_mutable_span().typed<T>());
302 }
303 });
304 return GVArray::ForGArray(std::move(values));
305}
306
307template<typename T>
309 const VArray<T> &old_values,
310 MutableSpan<T> r_values)
311{
312 BLI_assert(r_values.size() == mesh.edges_num);
313 const OffsetIndices faces = mesh.faces();
314 const Span<int> corner_edges = mesh.corner_edges();
315
316 attribute_math::DefaultMixer<T> mixer(r_values);
317
318 for (const int face_index : faces.index_range()) {
319 const T value = old_values[face_index];
320 for (const int edge : corner_edges.slice(faces[face_index])) {
321 mixer.mix_in(edge, value);
322 }
323 }
324 mixer.finalize();
325}
326
327/* An edge is selected if any connected face was selected. */
328template<>
330 const VArray<bool> &old_values,
331 MutableSpan<bool> r_values)
332{
333 BLI_assert(r_values.size() == mesh.edges_num);
334 const OffsetIndices faces = mesh.faces();
335 const Span<int> corner_edges = mesh.corner_edges();
336
337 r_values.fill(false);
338 threading::parallel_for(faces.index_range(), 2048, [&](const IndexRange range) {
339 for (const int face_index : range) {
340 if (old_values[face_index]) {
341 for (const int edge : corner_edges.slice(faces[face_index])) {
342 r_values[edge] = true;
343 }
344 }
345 }
346 });
347}
348
350{
351 GArray<> values(varray.type(), mesh.edges_num);
352 attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
353 using T = decltype(dummy);
354 if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
355 adapt_mesh_domain_face_to_edge_impl<T>(
356 mesh, varray.typed<T>(), values.as_mutable_span().typed<T>());
357 }
358 });
359 return GVArray::ForGArray(std::move(values));
360}
361
363{
364 const OffsetIndices faces = mesh.faces();
365 const Span<int> corner_verts = mesh.corner_verts();
366
367 GVArray new_varray;
368 attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
369 using T = decltype(dummy);
370 if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
371 if constexpr (std::is_same_v<T, bool>) {
372 new_varray = VArray<T>::ForFunc(
373 mesh.faces_num,
374 [corner_verts, faces, varray = varray.typed<bool>()](const int face_index) {
375 /* A face is selected if all of its vertices were selected. */
376 for (const int vert : corner_verts.slice(faces[face_index])) {
377 if (!varray[vert]) {
378 return false;
379 }
380 }
381 return true;
382 });
383 }
384 else {
385 new_varray = VArray<T>::ForFunc(
386 mesh.faces_num,
387 [corner_verts, faces, varray = varray.typed<T>()](const int face_index) {
388 T return_value;
389 attribute_math::DefaultMixer<T> mixer({&return_value, 1});
390 for (const int vert : corner_verts.slice(faces[face_index])) {
391 mixer.mix_in(0, varray[vert]);
392 }
393 mixer.finalize();
394 return return_value;
395 });
396 }
397 }
398 });
399 return new_varray;
400}
401
403{
404 const Span<int2> edges = mesh.edges();
405
406 GVArray new_varray;
407 attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
408 using T = decltype(dummy);
409 if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
410 if constexpr (std::is_same_v<T, bool>) {
411 /* An edge is selected if both of its vertices were selected. */
412 new_varray = VArray<bool>::ForFunc(
413 edges.size(), [edges, varray = varray.typed<bool>()](const int edge_index) {
414 const int2 &edge = edges[edge_index];
415 return varray[edge[0]] && varray[edge[1]];
416 });
417 }
418 else {
419 new_varray = VArray<T>::ForFunc(
420 edges.size(), [edges, varray = varray.typed<T>()](const int edge_index) {
421 const int2 &edge = edges[edge_index];
422 return attribute_math::mix2(0.5f, varray[edge[0]], varray[edge[1]]);
423 });
424 }
425 }
426 });
427 return new_varray;
428}
429
430template<typename T>
432 const VArray<T> &old_values,
433 MutableSpan<T> r_values)
434{
435 BLI_assert(r_values.size() == mesh.corners_num);
436 const OffsetIndices faces = mesh.faces();
437 const Span<int> corner_edges = mesh.corner_edges();
438
439 attribute_math::DefaultMixer<T> mixer(r_values);
440
441 for (const int face_index : faces.index_range()) {
442 const IndexRange face = faces[face_index];
443
444 /* For every corner, mix the values from the adjacent edges on the face. */
445 for (const int corner : face) {
446 const int corner_prev = mesh::face_corner_prev(face, corner);
447 const int edge = corner_edges[corner];
448 const int edge_prev = corner_edges[corner_prev];
449 mixer.mix_in(corner, old_values[edge]);
450 mixer.mix_in(corner, old_values[edge_prev]);
451 }
452 }
453
454 mixer.finalize();
455}
456
457/* A corner is selected if its two adjacent edges were selected. */
458template<>
460 const VArray<bool> &old_values,
461 MutableSpan<bool> r_values)
462{
463 BLI_assert(r_values.size() == mesh.corners_num);
464 const OffsetIndices faces = mesh.faces();
465 const Span<int> corner_edges = mesh.corner_edges();
466
467 r_values.fill(false);
468
469 threading::parallel_for(faces.index_range(), 2048, [&](const IndexRange range) {
470 for (const int face_index : range) {
471 const IndexRange face = faces[face_index];
472 for (const int corner : face) {
473 const int corner_prev = mesh::face_corner_prev(face, corner);
474 const int edge = corner_edges[corner];
475 const int edge_prev = corner_edges[corner_prev];
476 if (old_values[edge] && old_values[edge_prev]) {
477 r_values[corner] = true;
478 }
479 }
480 }
481 });
482}
483
485{
486 GArray<> values(varray.type(), mesh.corners_num);
487 attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
488 using T = decltype(dummy);
489 if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
490 adapt_mesh_domain_edge_to_corner_impl<T>(
491 mesh, varray.typed<T>(), values.as_mutable_span().typed<T>());
492 }
493 });
494 return GVArray::ForGArray(std::move(values));
495}
496
497template<typename T>
499 const VArray<T> &old_values,
500 MutableSpan<T> r_values)
501{
502 BLI_assert(r_values.size() == mesh.verts_num);
503 const Span<int2> edges = mesh.edges();
504
505 attribute_math::DefaultMixer<T> mixer(r_values);
506
507 for (const int edge_index : IndexRange(mesh.edges_num)) {
508 const int2 &edge = edges[edge_index];
509 const T value = old_values[edge_index];
510 mixer.mix_in(edge[0], value);
511 mixer.mix_in(edge[1], value);
512 }
513
514 mixer.finalize();
515}
516
517/* A vertex is selected if any connected edge was selected. */
518template<>
520 const VArray<bool> &old_values,
521 MutableSpan<bool> r_values)
522{
523 BLI_assert(r_values.size() == mesh.verts_num);
524 const Span<int2> edges = mesh.edges();
525
526 /* Multiple threads can write to the same index here, but they are only
527 * writing true, and writing to single bytes is expected to be threadsafe. */
528 r_values.fill(false);
529 threading::parallel_for(edges.index_range(), 4096, [&](const IndexRange range) {
530 for (const int edge_index : range) {
531 if (old_values[edge_index]) {
532 const int2 &edge = edges[edge_index];
533 r_values[edge[0]] = true;
534 r_values[edge[1]] = true;
535 }
536 }
537 });
538}
539
541{
542 GArray<> values(varray.type(), mesh.verts_num);
543 attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
544 using T = decltype(dummy);
545 if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
546 adapt_mesh_domain_edge_to_point_impl<T>(
547 mesh, varray.typed<T>(), values.as_mutable_span().typed<T>());
548 }
549 });
550 return GVArray::ForGArray(std::move(values));
551}
552
554{
555 const OffsetIndices faces = mesh.faces();
556 const Span<int> corner_edges = mesh.corner_edges();
557
558 GVArray new_varray;
559 attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
560 using T = decltype(dummy);
561 if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
562 if constexpr (std::is_same_v<T, bool>) {
563 /* A face is selected if all of its edges are selected. */
564 new_varray = VArray<bool>::ForFunc(
565 faces.size(), [corner_edges, faces, varray = varray.typed<T>()](const int face_index) {
566 for (const int edge : corner_edges.slice(faces[face_index])) {
567 if (!varray[edge]) {
568 return false;
569 }
570 }
571 return true;
572 });
573 }
574 else {
575 new_varray = VArray<T>::ForFunc(
576 faces.size(), [corner_edges, faces, varray = varray.typed<T>()](const int face_index) {
577 T return_value;
578 attribute_math::DefaultMixer<T> mixer({&return_value, 1});
579 for (const int edge : corner_edges.slice(faces[face_index])) {
580 mixer.mix_in(0, varray[edge]);
581 }
582 mixer.finalize();
583 return return_value;
584 });
585 }
586 }
587 });
588 return new_varray;
589}
590
592 const AttrDomain from_domain,
593 const AttrDomain to_domain)
594{
595 /* For some domain combinations, a single value will always map directly. For others, there may
596 * be loose elements on the result domain that should have the default value rather than the
597 * single value from the source. */
598 switch (from_domain) {
600 /* All other domains are always connected to points. */
601 return true;
602 case AttrDomain::Edge:
603 if (to_domain == AttrDomain::Point) {
604 return mesh.loose_verts().count == 0;
605 }
606 return true;
607 case AttrDomain::Face:
608 if (to_domain == AttrDomain::Point) {
609 return mesh.verts_no_face().count == 0;
610 }
611 if (to_domain == AttrDomain::Edge) {
612 return mesh.loose_edges().count == 0;
613 }
614 return true;
616 if (to_domain == AttrDomain::Point) {
617 return mesh.verts_no_face().count == 0;
618 }
619 if (to_domain == AttrDomain::Edge) {
620 return mesh.loose_edges().count == 0;
621 }
622 return true;
623 default:
625 return false;
626 }
627}
628
630 const GVArray &varray,
631 const AttrDomain from_domain,
632 const AttrDomain to_domain)
633{
634 if (!varray) {
635 return {};
636 }
637 if (varray.is_empty()) {
638 return {};
639 }
640 if (from_domain == to_domain) {
641 return varray;
642 }
643 if (varray.is_single()) {
644 if (can_simple_adapt_for_single(mesh, from_domain, to_domain)) {
645 BUFFER_FOR_CPP_TYPE_VALUE(varray.type(), value);
646 varray.get_internal_single(value);
647 return GVArray::ForSingle(varray.type(), mesh.attributes().domain_size(to_domain), value);
648 }
649 }
650
651 switch (from_domain) {
652 case AttrDomain::Corner: {
653 switch (to_domain) {
656 case AttrDomain::Face:
658 case AttrDomain::Edge:
660 default:
661 break;
662 }
663 break;
664 }
665 case AttrDomain::Point: {
666 switch (to_domain) {
669 case AttrDomain::Face:
671 case AttrDomain::Edge:
673 default:
674 break;
675 }
676 break;
677 }
678 case AttrDomain::Face: {
679 switch (to_domain) {
684 case AttrDomain::Edge:
685 return adapt_mesh_domain_face_to_edge(mesh, varray);
686 default:
687 break;
688 }
689 break;
690 }
691 case AttrDomain::Edge: {
692 switch (to_domain) {
697 case AttrDomain::Face:
698 return adapt_mesh_domain_edge_to_face(mesh, varray);
699 default:
700 break;
701 }
702 break;
703 }
704 default:
705 break;
706 }
707
708 return {};
709}
710
711static void tag_component_positions_changed(void *owner)
712{
713 Mesh *mesh = static_cast<Mesh *>(owner);
714 if (mesh != nullptr) {
715 mesh->tag_positions_changed();
716 }
717}
718
719static void tag_component_sharpness_changed(void *owner)
720{
721 if (Mesh *mesh = static_cast<Mesh *>(owner)) {
722 mesh->tag_sharpness_changed();
723 }
724}
725
726static void tag_material_index_changed(void *owner)
727{
728 if (Mesh *mesh = static_cast<Mesh *>(owner)) {
729 mesh->tag_material_index_changed();
730 }
731}
732
737 public:
738 GAttributeReader try_get_for_read(const void *owner, const StringRef attribute_id) const final
739 {
740 const Mesh *mesh = static_cast<const Mesh *>(owner);
741 if (mesh == nullptr) {
742 return {};
743 }
744 const int vertex_group_index = BKE_defgroup_name_index(&mesh->vertex_group_names,
745 attribute_id);
746 if (vertex_group_index < 0) {
747 return {};
748 }
749 const Span<MDeformVert> dverts = mesh->deform_verts();
750 return this->get_for_vertex_group_index(*mesh, dverts, vertex_group_index);
751 }
752
754 const Span<MDeformVert> dverts,
755 const int vertex_group_index) const
756 {
757 BLI_assert(vertex_group_index >= 0);
758 if (dverts.is_empty()) {
759 return {VArray<float>::ForSingle(0.0f, mesh.verts_num), AttrDomain::Point};
760 }
761 return {varray_for_deform_verts(dverts, vertex_group_index), AttrDomain::Point};
762 }
763
764 GAttributeWriter try_get_for_write(void *owner, const StringRef attribute_id) const final
765 {
766 Mesh *mesh = static_cast<Mesh *>(owner);
767 if (mesh == nullptr) {
768 return {};
769 }
770
771 const int vertex_group_index = BKE_defgroup_name_index(&mesh->vertex_group_names,
772 attribute_id);
773 if (vertex_group_index < 0) {
774 return {};
775 }
776 MutableSpan<MDeformVert> dverts = mesh->deform_verts_for_write();
777 return {varray_for_mutable_deform_verts(dverts, vertex_group_index), AttrDomain::Point};
778 }
779
780 bool try_delete(void *owner, const StringRef name) const final
781 {
782 Mesh *mesh = static_cast<Mesh *>(owner);
783 if (mesh == nullptr) {
784 return true;
785 }
786
787 int index;
788 bDeformGroup *group;
789 if (!BKE_id_defgroup_name_find(&mesh->id, name, &index, &group)) {
790 return false;
791 }
792 BLI_remlink(&mesh->vertex_group_names, group);
793 MEM_freeN(group);
794 if (mesh->deform_verts().is_empty()) {
795 return true;
796 }
797
798 MutableSpan<MDeformVert> dverts = mesh->deform_verts_for_write();
799 remove_defgroup_index(dverts, index);
800 return true;
801 }
802
803 bool foreach_attribute(const void *owner,
804 const FunctionRef<void(const AttributeIter &)> fn) const final
805 {
806 const Mesh *mesh = static_cast<const Mesh *>(owner);
807 if (mesh == nullptr) {
808 return true;
809 }
810
811 const Span<MDeformVert> dverts = mesh->deform_verts();
812
813 int group_index = 0;
814 LISTBASE_FOREACH_INDEX (const bDeformGroup *, group, &mesh->vertex_group_names, group_index) {
815 const auto get_fn = [&]() {
816 return this->get_for_vertex_group_index(*mesh, dverts, group_index);
817 };
818
819 AttributeIter iter{group->name, AttrDomain::Point, CD_PROP_FLOAT, get_fn};
820 fn(iter);
821 if (iter.is_stopped()) {
822 return false;
823 }
824 }
825 return true;
826 }
827
828 void foreach_domain(const FunctionRef<void(AttrDomain)> callback) const final
829 {
830 callback(AttrDomain::Point);
831 }
832};
833
834static std::function<void()> get_tag_modified_function(void *owner, const StringRef name)
835{
836 if (name.startswith(".hide")) {
837 return [owner]() { (static_cast<Mesh *>(owner))->tag_visibility_changed(); };
838 }
839 if (name == "custom_normal") {
840 return [owner]() { (static_cast<Mesh *>(owner))->tag_custom_normals_changed(); };
841 }
842 return {};
843}
844
850{
851#define MAKE_MUTABLE_CUSTOM_DATA_GETTER(NAME) \
852 [](void *owner) -> CustomData * { \
853 Mesh *mesh = static_cast<Mesh *>(owner); \
854 return &mesh->NAME; \
855 }
856#define MAKE_CONST_CUSTOM_DATA_GETTER(NAME) \
857 [](const void *owner) -> const CustomData * { \
858 const Mesh *mesh = static_cast<const Mesh *>(owner); \
859 return &mesh->NAME; \
860 }
861#define MAKE_GET_ELEMENT_NUM_GETTER(NAME) \
862 [](const void *owner) -> int { \
863 const Mesh *mesh = static_cast<const Mesh *>(owner); \
864 return mesh->NAME; \
865 }
866
867 static CustomDataAccessInfo corner_access = {MAKE_MUTABLE_CUSTOM_DATA_GETTER(corner_data),
869 MAKE_GET_ELEMENT_NUM_GETTER(corners_num),
871 static CustomDataAccessInfo point_access = {MAKE_MUTABLE_CUSTOM_DATA_GETTER(vert_data),
875 static CustomDataAccessInfo edge_access = {MAKE_MUTABLE_CUSTOM_DATA_GETTER(edge_data),
879 static CustomDataAccessInfo face_access = {MAKE_MUTABLE_CUSTOM_DATA_GETTER(face_data),
883
884#undef MAKE_CONST_CUSTOM_DATA_GETTER
885#undef MAKE_MUTABLE_CUSTOM_DATA_GETTER
886
887 static BuiltinCustomDataLayerProvider position("position",
891 point_access,
893
898 point_access,
899 nullptr);
900
901 static const auto material_index_clamp = mf::build::SI1_SO<int, int>(
902 "Material Index Validate",
903 [](int value) {
904 /* Use #short for the maximum since many areas still use that type for indices. */
905 return std::clamp<int>(value, 0, std::numeric_limits<short>::max());
906 },
907 mf::build::exec_presets::AllSpanOrSingle());
908 static BuiltinCustomDataLayerProvider material_index("material_index",
912 face_access,
914 AttributeValidator{&material_index_clamp});
915
916 static const auto int2_index_clamp = mf::build::SI1_SO<int2, int2>(
917 "Index Validate",
918 [](int2 value) { return math::max(value, int2(0)); },
919 mf::build::exec_presets::AllSpanOrSingle());
920 static BuiltinCustomDataLayerProvider edge_verts(".edge_verts",
924 edge_access,
925 nullptr,
926 AttributeValidator{&int2_index_clamp});
927
928 /* NOTE: This clamping is more of a last resort, since it's quite easy to make an
929 * invalid mesh that will crash Blender by arbitrarily editing this attribute. */
930 static const auto int_index_clamp = mf::build::SI1_SO<int, int>(
931 "Index Validate",
932 [](int value) { return std::max(value, 0); },
933 mf::build::exec_presets::AllSpanOrSingle());
934 static BuiltinCustomDataLayerProvider corner_vert(".corner_vert",
938 corner_access,
939 nullptr,
940 AttributeValidator{&int_index_clamp});
941 static BuiltinCustomDataLayerProvider corner_edge(".corner_edge",
945 corner_access,
946 nullptr,
947 AttributeValidator{&int_index_clamp});
948
949 static BuiltinCustomDataLayerProvider sharp_face("sharp_face",
953 face_access,
955
956 static BuiltinCustomDataLayerProvider sharp_edge("sharp_edge",
960 edge_access,
962
963 static MeshVertexGroupsAttributeProvider vertex_groups;
964 static CustomDataAttributeProvider corner_custom_data(AttrDomain::Corner, corner_access);
965 static CustomDataAttributeProvider point_custom_data(AttrDomain::Point, point_access);
966 static CustomDataAttributeProvider edge_custom_data(AttrDomain::Edge, edge_access);
967 static CustomDataAttributeProvider face_custom_data(AttrDomain::Face, face_access);
968
969 return GeometryAttributeProviders({&position,
970 &edge_verts,
971 &corner_vert,
972 &corner_edge,
973 &id,
974 &material_index,
975 &sharp_face,
976 &sharp_edge},
977 {&corner_custom_data,
978 &vertex_groups,
979 &point_custom_data,
980 &edge_custom_data,
981 &face_custom_data});
982}
983
985{
989 fn.domain_size = [](const void *owner, const AttrDomain domain) {
990 if (owner == nullptr) {
991 return 0;
992 }
993 const Mesh &mesh = *static_cast<const Mesh *>(owner);
994 switch (domain) {
996 return mesh.verts_num;
997 case AttrDomain::Edge:
998 return mesh.edges_num;
999 case AttrDomain::Face:
1000 return mesh.faces_num;
1001 case AttrDomain::Corner:
1002 return mesh.corners_num;
1003 default:
1004 return 0;
1005 }
1006 };
1007 fn.domain_supported = [](const void * /*owner*/, const AttrDomain domain) {
1009 };
1010 fn.adapt_domain = [](const void *owner,
1011 const GVArray &varray,
1012 const AttrDomain from_domain,
1013 const AttrDomain to_domain) -> GVArray {
1014 if (owner == nullptr) {
1015 return {};
1016 }
1017 const Mesh &mesh = *static_cast<const Mesh *>(owner);
1018 return adapt_mesh_attribute_domain(mesh, varray, from_domain, to_domain);
1019 };
1020 return fn;
1021}
1022
1028
1029} // 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:566
int BKE_defgroup_name_index(const ListBase *defbase, blender::StringRef name)
Definition deform.cc:529
#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_FLOAT
@ CD_PROP_FLOAT3
@ CD_PROP_INT32_2D
@ CD_PROP_INT32
Object is a sort of wrapper for general info.
static VArray ForSingle(T value, const int64_t size)
void get_internal_single(void *r_value) const
static GVArray ForGArray(GArray<> array)
static GVArray ForSingle(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
constexpr bool startswith(StringRef prefix) const
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:290
int face_corner_next(const IndexRange face, const int corner)
Definition BKE_mesh.hh:299
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)
void adapt_mesh_domain_face_to_point_impl(const Mesh &mesh, const VArray< T > &src, MutableSpan< T > r_dst)
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:1793
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:1787
VArray< float > varray_for_deform_verts(Span< MDeformVert > dverts, int defgroup_index)
Definition deform.cc:1783
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