Blender V4.3
geometry_component_mesh.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "BLI_listbase.h"
6#include "BLI_task.hh"
7
9#include "DNA_object_types.h"
10
11#include "BKE_attribute_math.hh"
12#include "BKE_deform.hh"
14#include "BKE_geometry_set.hh"
15#include "BKE_lib_id.hh"
16#include "BKE_mesh.hh"
17#include "BKE_mesh_mapping.hh"
18
20
22
23namespace blender::bke {
24
25/* -------------------------------------------------------------------- */
30
32 : GeometryComponent(Type::Mesh), mesh_(mesh), ownership_(ownership)
33{
34}
35
40
42{
43 MeshComponent *new_component = new MeshComponent();
44 if (mesh_ != nullptr) {
45 new_component->mesh_ = BKE_mesh_copy_for_eval(*mesh_);
46 new_component->ownership_ = GeometryOwnershipType::Owned;
47 }
48 return GeometryComponentPtr(new_component);
49}
50
52{
53 BLI_assert(this->is_mutable() || this->is_expired());
54 if (mesh_ != nullptr) {
55 if (ownership_ == GeometryOwnershipType::Owned) {
56 BKE_id_free(nullptr, mesh_);
57 }
58 mesh_ = nullptr;
59 }
60}
61
63{
64 return mesh_ != nullptr;
65}
66
68{
69 BLI_assert(this->is_mutable());
70 this->clear();
71 mesh_ = mesh;
72 ownership_ = ownership;
73}
74
76{
77 BLI_assert(this->is_mutable());
78 Mesh *mesh = mesh_;
79 mesh_ = nullptr;
80 return mesh;
81}
82
84{
85 return mesh_;
86}
87
89{
90 BLI_assert(this->is_mutable());
91 if (ownership_ == GeometryOwnershipType::ReadOnly) {
92 mesh_ = BKE_mesh_copy_for_eval(*mesh_);
94 }
95 return mesh_;
96}
97
99{
100 return mesh_ == nullptr;
101}
102
104{
105 return ownership_ == GeometryOwnershipType::Owned;
106}
107
109{
110 BLI_assert(this->is_mutable());
111 if (ownership_ != GeometryOwnershipType::Owned) {
112 if (mesh_) {
113 mesh_ = BKE_mesh_copy_for_eval(*mesh_);
114 }
115 ownership_ = GeometryOwnershipType::Owned;
116 }
117}
118
120{
121 if (mesh_) {
122 mesh_->count_memory(memory);
123 }
124}
125
128/* -------------------------------------------------------------------- */
133 const IndexMask &mask,
134 const AttrDomain domain)
135{
136 switch (domain) {
137 case AttrDomain::Face: {
138 return VArray<float3>::ForSpan(mesh.face_normals());
139 }
140 case AttrDomain::Point: {
141 return VArray<float3>::ForSpan(mesh.vert_normals());
142 }
143 case AttrDomain::Edge: {
144 /* In this case, start with vertex normals and convert to the edge domain, since the
145 * conversion from edges to vertices is very simple. Use "manual" domain interpolation
146 * instead of the GeometryComponent API to avoid calculating unnecessary values and to
147 * allow normalizing the result more simply. */
148 Span<float3> vert_normals = mesh.vert_normals();
149 const Span<int2> edges = mesh.edges();
150 Array<float3> edge_normals(mask.min_array_size());
151 mask.foreach_index([&](const int i) {
152 const int2 &edge = edges[i];
153 edge_normals[i] = math::normalize(
154 math::interpolate(vert_normals[edge[0]], vert_normals[edge[1]], 0.5f));
155 });
156
157 return VArray<float3>::ForContainer(std::move(edge_normals));
158 }
159 case AttrDomain::Corner: {
160 /* The normals on corners are just the mesh's face normals, so start with the face normal
161 * array and copy the face normal for each of its corners. In this case using the mesh
162 * component's generic domain interpolation is fine, the data will still be normalized,
163 * since the face normal is just copied to every corner. */
164 return mesh.attributes().adapt_domain(
166 }
167 default:
168 return {};
169 }
170}
171
174/* -------------------------------------------------------------------- */
178template<typename T>
180 const VArray<T> &old_values,
181 MutableSpan<T> r_values)
182{
183 BLI_assert(r_values.size() == mesh.verts_num);
184 const Span<int> corner_verts = mesh.corner_verts();
185
186 attribute_math::DefaultMixer<T> mixer(r_values);
187 for (const int corner : IndexRange(mesh.corners_num)) {
188 mixer.mix_in(corner_verts[corner], old_values[corner]);
189 }
190 mixer.finalize();
191}
192
193/* A vertex is selected if all connected face corners were selected and it is not loose. */
194template<>
196 const VArray<bool> &old_values,
197 MutableSpan<bool> r_values)
198{
199 BLI_assert(r_values.size() == mesh.verts_num);
200 const Span<int> corner_verts = mesh.corner_verts();
201
202 r_values.fill(true);
203 for (const int corner : IndexRange(mesh.corners_num)) {
204 const int point_index = corner_verts[corner];
205
206 if (!old_values[corner]) {
207 r_values[point_index] = false;
208 }
209 }
210
211 /* Deselect loose vertices without corners that are still selected from the 'true' default. */
212 const LooseVertCache &loose_verts = mesh.verts_no_face();
213 if (loose_verts.count > 0) {
214 const BitSpan bits = loose_verts.is_loose_bits;
215 threading::parallel_for(bits.index_range(), 2048, [&](const IndexRange range) {
216 for (const int vert_index : range) {
217 if (bits[vert_index]) {
218 r_values[vert_index] = false;
219 }
220 }
221 });
222 }
223}
224
225static GVArray adapt_mesh_domain_corner_to_point(const Mesh &mesh, const GVArray &varray)
226{
227 GArray<> values(varray.type(), mesh.verts_num);
228 attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
229 using T = decltype(dummy);
230 if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
231 /* We compute all interpolated values at once, because for this interpolation, one has to
232 * iterate over all loops anyway. */
233 adapt_mesh_domain_corner_to_point_impl<T>(
234 mesh, varray.typed<T>(), values.as_mutable_span().typed<T>());
235 }
236 });
237 return GVArray::ForGArray(std::move(values));
238}
239
243static GVArray adapt_mesh_domain_point_to_corner(const Mesh &mesh, const GVArray &varray)
244{
245 const Span<int> corner_verts = mesh.corner_verts();
246
247 GVArray new_varray;
248 attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
249 using T = decltype(dummy);
250 new_varray = VArray<T>::ForFunc(
251 mesh.corners_num, [corner_verts, varray = varray.typed<T>()](const int64_t corner) {
252 return varray[corner_verts[corner]];
253 });
254 });
255 return new_varray;
256}
257
258static GVArray adapt_mesh_domain_corner_to_face(const Mesh &mesh, const GVArray &varray)
259{
260 const OffsetIndices faces = mesh.faces();
261
262 GVArray new_varray;
263 attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
264 using T = decltype(dummy);
265 if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
266 if constexpr (std::is_same_v<T, bool>) {
267 new_varray = VArray<T>::ForFunc(
268 faces.size(), [faces, varray = varray.typed<bool>()](const int face_index) {
269 /* A face is selected if all of its corners were selected. */
270 for (const int loop_index : faces[face_index]) {
271 if (!varray[loop_index]) {
272 return false;
273 }
274 }
275 return true;
276 });
277 }
278 else {
279 new_varray = VArray<T>::ForFunc(
280 faces.size(), [faces, varray = varray.typed<T>()](const int face_index) {
281 T return_value;
282 attribute_math::DefaultMixer<T> mixer({&return_value, 1});
283 for (const int loop_index : faces[face_index]) {
284 const T value = varray[loop_index];
285 mixer.mix_in(0, value);
286 }
287 mixer.finalize();
288 return return_value;
289 });
290 }
291 }
292 });
293 return new_varray;
294}
295
296template<typename T>
298 const VArray<T> &old_values,
299 MutableSpan<T> r_values)
300{
301 BLI_assert(r_values.size() == mesh.edges_num);
302 const OffsetIndices faces = mesh.faces();
303 const Span<int> corner_edges = mesh.corner_edges();
304
305 attribute_math::DefaultMixer<T> mixer(r_values);
306
307 for (const int face_index : faces.index_range()) {
308 const IndexRange face = faces[face_index];
309
310 /* For every edge, mix values from the two adjacent corners (the current and next corner). */
311 for (const int corner : face) {
312 const int next_corner = mesh::face_corner_next(face, corner);
313 const int edge_index = corner_edges[corner];
314 mixer.mix_in(edge_index, old_values[corner]);
315 mixer.mix_in(edge_index, old_values[next_corner]);
316 }
317 }
318
319 mixer.finalize();
320}
321
322/* An edge is selected if all corners on adjacent faces were selected. */
323template<>
325 const VArray<bool> &old_values,
326 MutableSpan<bool> r_values)
327{
328 BLI_assert(r_values.size() == mesh.edges_num);
329 const OffsetIndices faces = mesh.faces();
330 const Span<int> corner_edges = mesh.corner_edges();
331
332 r_values.fill(true);
333 for (const int face_index : faces.index_range()) {
334 const IndexRange face = faces[face_index];
335
336 for (const int corner : face) {
337 const int next_corner = mesh::face_corner_next(face, corner);
338 const int edge_index = corner_edges[corner];
339 if (!old_values[corner] || !old_values[next_corner]) {
340 r_values[edge_index] = false;
341 }
342 }
343 }
344
345 const LooseEdgeCache &loose_edges = mesh.loose_edges();
346 if (loose_edges.count > 0) {
347 /* Deselect loose edges without corners that are still selected from the 'true' default. */
348 threading::parallel_for(IndexRange(mesh.edges_num), 2048, [&](const IndexRange range) {
349 for (const int edge_index : range) {
350 if (loose_edges.is_loose_bits[edge_index]) {
351 r_values[edge_index] = false;
352 }
353 }
354 });
355 }
356}
357
358static GVArray adapt_mesh_domain_corner_to_edge(const Mesh &mesh, const GVArray &varray)
359{
360 GArray<> values(varray.type(), mesh.edges_num);
361 attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
362 using T = decltype(dummy);
363 if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
364 adapt_mesh_domain_corner_to_edge_impl<T>(
365 mesh, varray.typed<T>(), values.as_mutable_span().typed<T>());
366 }
367 });
368 return GVArray::ForGArray(std::move(values));
369}
370
371template<typename T>
373 const VArray<T> &old_values,
374 MutableSpan<T> r_values)
375{
376 BLI_assert(r_values.size() == mesh.verts_num);
377 const OffsetIndices faces = mesh.faces();
378 const Span<int> corner_verts = mesh.corner_verts();
379
380 attribute_math::DefaultMixer<T> mixer(r_values);
381
382 for (const int face_index : faces.index_range()) {
383 const T value = old_values[face_index];
384 for (const int vert : corner_verts.slice(faces[face_index])) {
385 mixer.mix_in(vert, value);
386 }
387 }
388
389 mixer.finalize();
390}
391
392/* A vertex is selected if any of the connected faces were selected. */
393template<>
395 const VArray<bool> &old_values,
396 MutableSpan<bool> r_values)
397{
398 BLI_assert(r_values.size() == mesh.verts_num);
399 const OffsetIndices faces = mesh.faces();
400 const Span<int> corner_verts = mesh.corner_verts();
401
402 r_values.fill(false);
403 threading::parallel_for(faces.index_range(), 2048, [&](const IndexRange range) {
404 for (const int face_index : range) {
405 if (old_values[face_index]) {
406 for (const int vert : corner_verts.slice(faces[face_index])) {
407 r_values[vert] = true;
408 }
409 }
410 }
411 });
412}
413
414static GVArray adapt_mesh_domain_face_to_point(const Mesh &mesh, const GVArray &varray)
415{
416 GArray<> values(varray.type(), mesh.verts_num);
417 attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
418 using T = decltype(dummy);
419 if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
420 adapt_mesh_domain_face_to_point_impl<T>(
421 mesh, varray.typed<T>(), values.as_mutable_span().typed<T>());
422 }
423 });
424 return GVArray::ForGArray(std::move(values));
425}
426
427/* Each corner's value is simply a copy of the value at its face. */
428template<typename T>
430 const VArray<T> &old_values,
431 MutableSpan<T> r_values)
432{
433 BLI_assert(r_values.size() == mesh.corners_num);
434 const OffsetIndices faces = mesh.faces();
435
436 threading::parallel_for(faces.index_range(), 1024, [&](const IndexRange range) {
437 for (const int face_index : range) {
438 MutableSpan<T> face_corner_values = r_values.slice(faces[face_index]);
439 face_corner_values.fill(old_values[face_index]);
440 }
441 });
442}
443
444static GVArray adapt_mesh_domain_face_to_corner(const Mesh &mesh, const GVArray &varray)
445{
446 GArray<> values(varray.type(), mesh.corners_num);
447 attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
448 using T = decltype(dummy);
449 if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
450 adapt_mesh_domain_face_to_corner_impl<T>(
451 mesh, varray.typed<T>(), values.as_mutable_span().typed<T>());
452 }
453 });
454 return GVArray::ForGArray(std::move(values));
455}
456
457template<typename T>
459 const VArray<T> &old_values,
460 MutableSpan<T> r_values)
461{
462 BLI_assert(r_values.size() == mesh.edges_num);
463 const OffsetIndices faces = mesh.faces();
464 const Span<int> corner_edges = mesh.corner_edges();
465
466 attribute_math::DefaultMixer<T> mixer(r_values);
467
468 for (const int face_index : faces.index_range()) {
469 const T value = old_values[face_index];
470 for (const int edge : corner_edges.slice(faces[face_index])) {
471 mixer.mix_in(edge, value);
472 }
473 }
474 mixer.finalize();
475}
476
477/* An edge is selected if any connected face was selected. */
478template<>
480 const VArray<bool> &old_values,
481 MutableSpan<bool> r_values)
482{
483 BLI_assert(r_values.size() == mesh.edges_num);
484 const OffsetIndices faces = mesh.faces();
485 const Span<int> corner_edges = mesh.corner_edges();
486
487 r_values.fill(false);
488 threading::parallel_for(faces.index_range(), 2048, [&](const IndexRange range) {
489 for (const int face_index : range) {
490 if (old_values[face_index]) {
491 for (const int edge : corner_edges.slice(faces[face_index])) {
492 r_values[edge] = true;
493 }
494 }
495 }
496 });
497}
498
499static GVArray adapt_mesh_domain_face_to_edge(const Mesh &mesh, const GVArray &varray)
500{
501 GArray<> values(varray.type(), mesh.edges_num);
502 attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
503 using T = decltype(dummy);
504 if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
505 adapt_mesh_domain_face_to_edge_impl<T>(
506 mesh, varray.typed<T>(), values.as_mutable_span().typed<T>());
507 }
508 });
509 return GVArray::ForGArray(std::move(values));
510}
511
512static GVArray adapt_mesh_domain_point_to_face(const Mesh &mesh, const GVArray &varray)
513{
514 const OffsetIndices faces = mesh.faces();
515 const Span<int> corner_verts = mesh.corner_verts();
516
517 GVArray new_varray;
518 attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
519 using T = decltype(dummy);
520 if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
521 if constexpr (std::is_same_v<T, bool>) {
522 new_varray = VArray<T>::ForFunc(
523 mesh.faces_num,
524 [corner_verts, faces, varray = varray.typed<bool>()](const int face_index) {
525 /* A face is selected if all of its vertices were selected. */
526 for (const int vert : corner_verts.slice(faces[face_index])) {
527 if (!varray[vert]) {
528 return false;
529 }
530 }
531 return true;
532 });
533 }
534 else {
535 new_varray = VArray<T>::ForFunc(
536 mesh.faces_num,
537 [corner_verts, faces, varray = varray.typed<T>()](const int face_index) {
538 T return_value;
539 attribute_math::DefaultMixer<T> mixer({&return_value, 1});
540 for (const int vert : corner_verts.slice(faces[face_index])) {
541 mixer.mix_in(0, varray[vert]);
542 }
543 mixer.finalize();
544 return return_value;
545 });
546 }
547 }
548 });
549 return new_varray;
550}
551
552static GVArray adapt_mesh_domain_point_to_edge(const Mesh &mesh, const GVArray &varray)
553{
554 const Span<int2> edges = mesh.edges();
555
556 GVArray new_varray;
557 attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
558 using T = decltype(dummy);
559 if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
560 if constexpr (std::is_same_v<T, bool>) {
561 /* An edge is selected if both of its vertices were selected. */
562 new_varray = VArray<bool>::ForFunc(
563 edges.size(), [edges, varray = varray.typed<bool>()](const int edge_index) {
564 const int2 &edge = edges[edge_index];
565 return varray[edge[0]] && varray[edge[1]];
566 });
567 }
568 else {
569 new_varray = VArray<T>::ForFunc(
570 edges.size(), [edges, varray = varray.typed<T>()](const int edge_index) {
571 T return_value;
572 attribute_math::DefaultMixer<T> mixer({&return_value, 1});
573 const int2 &edge = edges[edge_index];
574 mixer.mix_in(0, varray[edge[0]]);
575 mixer.mix_in(0, varray[edge[1]]);
576 mixer.finalize();
577 return return_value;
578 });
579 }
580 }
581 });
582 return new_varray;
583}
584
585template<typename T>
587 const VArray<T> &old_values,
588 MutableSpan<T> r_values)
589{
590 BLI_assert(r_values.size() == mesh.corners_num);
591 const OffsetIndices faces = mesh.faces();
592 const Span<int> corner_edges = mesh.corner_edges();
593
594 attribute_math::DefaultMixer<T> mixer(r_values);
595
596 for (const int face_index : faces.index_range()) {
597 const IndexRange face = faces[face_index];
598
599 /* For every corner, mix the values from the adjacent edges on the face. */
600 for (const int loop_index : face) {
601 const int loop_index_prev = mesh::face_corner_prev(face, loop_index);
602 const int edge = corner_edges[loop_index];
603 const int edge_prev = corner_edges[loop_index_prev];
604 mixer.mix_in(loop_index, old_values[edge]);
605 mixer.mix_in(loop_index, old_values[edge_prev]);
606 }
607 }
608
609 mixer.finalize();
610}
611
612/* A corner is selected if its two adjacent edges were selected. */
613template<>
615 const VArray<bool> &old_values,
616 MutableSpan<bool> r_values)
617{
618 BLI_assert(r_values.size() == mesh.corners_num);
619 const OffsetIndices faces = mesh.faces();
620 const Span<int> corner_edges = mesh.corner_edges();
621
622 r_values.fill(false);
623
624 threading::parallel_for(faces.index_range(), 2048, [&](const IndexRange range) {
625 for (const int face_index : range) {
626 const IndexRange face = faces[face_index];
627 for (const int loop_index : face) {
628 const int loop_index_prev = mesh::face_corner_prev(face, loop_index);
629 const int edge = corner_edges[loop_index];
630 const int edge_prev = corner_edges[loop_index_prev];
631 if (old_values[edge] && old_values[edge_prev]) {
632 r_values[loop_index] = true;
633 }
634 }
635 }
636 });
637}
638
639static GVArray adapt_mesh_domain_edge_to_corner(const Mesh &mesh, const GVArray &varray)
640{
641 GArray<> values(varray.type(), mesh.corners_num);
642 attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
643 using T = decltype(dummy);
644 if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
645 adapt_mesh_domain_edge_to_corner_impl<T>(
646 mesh, varray.typed<T>(), values.as_mutable_span().typed<T>());
647 }
648 });
649 return GVArray::ForGArray(std::move(values));
650}
651
652template<typename T>
654 const VArray<T> &old_values,
655 MutableSpan<T> r_values)
656{
657 BLI_assert(r_values.size() == mesh.verts_num);
658 const Span<int2> edges = mesh.edges();
659
660 attribute_math::DefaultMixer<T> mixer(r_values);
661
662 for (const int edge_index : IndexRange(mesh.edges_num)) {
663 const int2 &edge = edges[edge_index];
664 const T value = old_values[edge_index];
665 mixer.mix_in(edge[0], value);
666 mixer.mix_in(edge[1], value);
667 }
668
669 mixer.finalize();
670}
671
672/* A vertex is selected if any connected edge was selected. */
673template<>
675 const VArray<bool> &old_values,
676 MutableSpan<bool> r_values)
677{
678 BLI_assert(r_values.size() == mesh.verts_num);
679 const Span<int2> edges = mesh.edges();
680
681 /* Multiple threads can write to the same index here, but they are only
682 * writing true, and writing to single bytes is expected to be threadsafe. */
683 r_values.fill(false);
684 threading::parallel_for(edges.index_range(), 4096, [&](const IndexRange range) {
685 for (const int edge_index : range) {
686 if (old_values[edge_index]) {
687 const int2 &edge = edges[edge_index];
688 r_values[edge[0]] = true;
689 r_values[edge[1]] = true;
690 }
691 }
692 });
693}
694
695static GVArray adapt_mesh_domain_edge_to_point(const Mesh &mesh, const GVArray &varray)
696{
697 GArray<> values(varray.type(), mesh.verts_num);
698 attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
699 using T = decltype(dummy);
700 if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
701 adapt_mesh_domain_edge_to_point_impl<T>(
702 mesh, varray.typed<T>(), values.as_mutable_span().typed<T>());
703 }
704 });
705 return GVArray::ForGArray(std::move(values));
706}
707
708static GVArray adapt_mesh_domain_edge_to_face(const Mesh &mesh, const GVArray &varray)
709{
710 const OffsetIndices faces = mesh.faces();
711 const Span<int> corner_edges = mesh.corner_edges();
712
713 GVArray new_varray;
714 attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
715 using T = decltype(dummy);
716 if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
717 if constexpr (std::is_same_v<T, bool>) {
718 /* A face is selected if all of its edges are selected. */
719 new_varray = VArray<bool>::ForFunc(
720 faces.size(), [corner_edges, faces, varray = varray.typed<T>()](const int face_index) {
721 for (const int edge : corner_edges.slice(faces[face_index])) {
722 if (!varray[edge]) {
723 return false;
724 }
725 }
726 return true;
727 });
728 }
729 else {
730 new_varray = VArray<T>::ForFunc(
731 faces.size(), [corner_edges, faces, varray = varray.typed<T>()](const int face_index) {
732 T return_value;
733 attribute_math::DefaultMixer<T> mixer({&return_value, 1});
734 for (const int edge : corner_edges.slice(faces[face_index])) {
735 mixer.mix_in(0, varray[edge]);
736 }
737 mixer.finalize();
738 return return_value;
739 });
740 }
741 }
742 });
743 return new_varray;
744}
745
746static bool can_simple_adapt_for_single(const Mesh &mesh,
747 const AttrDomain from_domain,
748 const AttrDomain to_domain)
749{
750 /* For some domain combinations, a single value will always map directly. For others, there may
751 * be loose elements on the result domain that should have the default value rather than the
752 * single value from the source. */
753 switch (from_domain) {
754 case AttrDomain::Point:
755 /* All other domains are always connected to points. */
756 return true;
757 case AttrDomain::Edge:
758 if (to_domain == AttrDomain::Point) {
759 return mesh.loose_verts().count == 0;
760 }
761 return true;
762 case AttrDomain::Face:
763 if (to_domain == AttrDomain::Point) {
764 return mesh.verts_no_face().count == 0;
765 }
766 if (to_domain == AttrDomain::Edge) {
767 return mesh.loose_edges().count == 0;
768 }
769 return true;
770 case AttrDomain::Corner:
771 if (to_domain == AttrDomain::Point) {
772 return mesh.verts_no_face().count == 0;
773 }
774 if (to_domain == AttrDomain::Edge) {
775 return mesh.loose_edges().count == 0;
776 }
777 return true;
778 default:
780 return false;
781 }
782}
783
785 const GVArray &varray,
786 const AttrDomain from_domain,
787 const AttrDomain to_domain)
788{
789 if (!varray) {
790 return {};
791 }
792 if (varray.is_empty()) {
793 return {};
794 }
795 if (from_domain == to_domain) {
796 return varray;
797 }
798 if (varray.is_single()) {
799 if (can_simple_adapt_for_single(mesh, from_domain, to_domain)) {
800 BUFFER_FOR_CPP_TYPE_VALUE(varray.type(), value);
801 varray.get_internal_single(value);
802 return GVArray::ForSingle(varray.type(), mesh.attributes().domain_size(to_domain), value);
803 }
804 }
805
806 switch (from_domain) {
807 case AttrDomain::Corner: {
808 switch (to_domain) {
809 case AttrDomain::Point:
810 return adapt_mesh_domain_corner_to_point(mesh, varray);
811 case AttrDomain::Face:
812 return adapt_mesh_domain_corner_to_face(mesh, varray);
813 case AttrDomain::Edge:
814 return adapt_mesh_domain_corner_to_edge(mesh, varray);
815 default:
816 break;
817 }
818 break;
819 }
820 case AttrDomain::Point: {
821 switch (to_domain) {
822 case AttrDomain::Corner:
823 return adapt_mesh_domain_point_to_corner(mesh, varray);
824 case AttrDomain::Face:
825 return adapt_mesh_domain_point_to_face(mesh, varray);
826 case AttrDomain::Edge:
827 return adapt_mesh_domain_point_to_edge(mesh, varray);
828 default:
829 break;
830 }
831 break;
832 }
833 case AttrDomain::Face: {
834 switch (to_domain) {
835 case AttrDomain::Point:
836 return adapt_mesh_domain_face_to_point(mesh, varray);
837 case AttrDomain::Corner:
838 return adapt_mesh_domain_face_to_corner(mesh, varray);
839 case AttrDomain::Edge:
840 return adapt_mesh_domain_face_to_edge(mesh, varray);
841 default:
842 break;
843 }
844 break;
845 }
846 case AttrDomain::Edge: {
847 switch (to_domain) {
848 case AttrDomain::Corner:
849 return adapt_mesh_domain_edge_to_corner(mesh, varray);
850 case AttrDomain::Point:
851 return adapt_mesh_domain_edge_to_point(mesh, varray);
852 case AttrDomain::Face:
853 return adapt_mesh_domain_edge_to_face(mesh, varray);
854 default:
855 break;
856 }
857 break;
858 }
859 default:
860 break;
861 }
862
863 return {};
864}
865
866static void tag_component_positions_changed(void *owner)
867{
868 Mesh *mesh = static_cast<Mesh *>(owner);
869 if (mesh != nullptr) {
870 mesh->tag_positions_changed();
871 }
872}
873
874static void tag_component_sharpness_changed(void *owner)
875{
876 if (Mesh *mesh = static_cast<Mesh *>(owner)) {
877 mesh->tag_sharpness_changed();
878 }
879}
880
885 public:
886 GAttributeReader try_get_for_read(const void *owner, const StringRef attribute_id) const final
887 {
888 if (bke::attribute_name_is_anonymous(attribute_id)) {
889 return {};
890 }
891 const Mesh *mesh = static_cast<const Mesh *>(owner);
892 if (mesh == nullptr) {
893 return {};
894 }
895 const int vertex_group_index = BKE_defgroup_name_index(&mesh->vertex_group_names,
896 attribute_id);
897 if (vertex_group_index < 0) {
898 return {};
899 }
900 const Span<MDeformVert> dverts = mesh->deform_verts();
901 return this->get_for_vertex_group_index(*mesh, dverts, vertex_group_index);
902 }
903
905 const Span<MDeformVert> dverts,
906 const int vertex_group_index) const
907 {
908 BLI_assert(vertex_group_index >= 0);
909 if (dverts.is_empty()) {
910 return {VArray<float>::ForSingle(0.0f, mesh.verts_num), AttrDomain::Point};
911 }
912 return {varray_for_deform_verts(dverts, vertex_group_index), AttrDomain::Point};
913 }
914
915 GAttributeWriter try_get_for_write(void *owner, const StringRef attribute_id) const final
916 {
917 if (bke::attribute_name_is_anonymous(attribute_id)) {
918 return {};
919 }
920 Mesh *mesh = static_cast<Mesh *>(owner);
921 if (mesh == nullptr) {
922 return {};
923 }
924
925 const int vertex_group_index = BKE_defgroup_name_index(&mesh->vertex_group_names,
926 attribute_id);
927 if (vertex_group_index < 0) {
928 return {};
929 }
930 MutableSpan<MDeformVert> dverts = mesh->deform_verts_for_write();
931 return {varray_for_mutable_deform_verts(dverts, vertex_group_index), AttrDomain::Point};
932 }
933
934 bool try_delete(void *owner, const StringRef attribute_id) const final
935 {
936 if (bke::attribute_name_is_anonymous(attribute_id)) {
937 return false;
938 }
939 Mesh *mesh = static_cast<Mesh *>(owner);
940 if (mesh == nullptr) {
941 return true;
942 }
943
944 const std::string name = attribute_id;
945
946 int index;
947 bDeformGroup *group;
948 if (!BKE_id_defgroup_name_find(&mesh->id, name.c_str(), &index, &group)) {
949 return false;
950 }
951 BLI_remlink(&mesh->vertex_group_names, group);
952 MEM_freeN(group);
953 if (mesh->deform_verts().is_empty()) {
954 return true;
955 }
956
957 MutableSpan<MDeformVert> dverts = mesh->deform_verts_for_write();
958 remove_defgroup_index(dverts, index);
959 return true;
960 }
961
962 bool foreach_attribute(const void *owner,
963 const FunctionRef<void(const AttributeIter &)> fn) const final
964 {
965 const Mesh *mesh = static_cast<const Mesh *>(owner);
966 if (mesh == nullptr) {
967 return true;
968 }
969
970 const Span<MDeformVert> dverts = mesh->deform_verts();
971
972 int group_index = 0;
973 LISTBASE_FOREACH_INDEX (const bDeformGroup *, group, &mesh->vertex_group_names, group_index) {
974 const auto get_fn = [&]() {
975 return this->get_for_vertex_group_index(*mesh, dverts, group_index);
976 };
977
978 AttributeIter iter{group->name, AttrDomain::Point, CD_PROP_FLOAT, get_fn};
979 fn(iter);
980 if (iter.is_stopped()) {
981 return false;
982 }
983 }
984 return true;
985 }
986
987 void foreach_domain(const FunctionRef<void(AttrDomain)> callback) const final
988 {
989 callback(AttrDomain::Point);
990 }
991};
992
998{
999#define MAKE_MUTABLE_CUSTOM_DATA_GETTER(NAME) \
1000 [](void *owner) -> CustomData * { \
1001 Mesh *mesh = static_cast<Mesh *>(owner); \
1002 return &mesh->NAME; \
1003 }
1004#define MAKE_CONST_CUSTOM_DATA_GETTER(NAME) \
1005 [](const void *owner) -> const CustomData * { \
1006 const Mesh *mesh = static_cast<const Mesh *>(owner); \
1007 return &mesh->NAME; \
1008 }
1009#define MAKE_GET_ELEMENT_NUM_GETTER(NAME) \
1010 [](const void *owner) -> int { \
1011 const Mesh *mesh = static_cast<const Mesh *>(owner); \
1012 return mesh->NAME; \
1013 }
1014
1015 static CustomDataAccessInfo corner_access = {MAKE_MUTABLE_CUSTOM_DATA_GETTER(corner_data),
1016 MAKE_CONST_CUSTOM_DATA_GETTER(corner_data),
1017 MAKE_GET_ELEMENT_NUM_GETTER(corners_num)};
1018 static CustomDataAccessInfo point_access = {MAKE_MUTABLE_CUSTOM_DATA_GETTER(vert_data),
1020 MAKE_GET_ELEMENT_NUM_GETTER(verts_num)};
1021 static CustomDataAccessInfo edge_access = {MAKE_MUTABLE_CUSTOM_DATA_GETTER(edge_data),
1023 MAKE_GET_ELEMENT_NUM_GETTER(edges_num)};
1024 static CustomDataAccessInfo face_access = {MAKE_MUTABLE_CUSTOM_DATA_GETTER(face_data),
1026 MAKE_GET_ELEMENT_NUM_GETTER(faces_num)};
1027
1028#undef MAKE_CONST_CUSTOM_DATA_GETTER
1029#undef MAKE_MUTABLE_CUSTOM_DATA_GETTER
1030
1031 static BuiltinCustomDataLayerProvider position("position",
1032 AttrDomain::Point,
1034 BuiltinAttributeProvider::NonDeletable,
1035 point_access,
1037
1038 static BuiltinCustomDataLayerProvider id("id",
1039 AttrDomain::Point,
1041 BuiltinAttributeProvider::Deletable,
1042 point_access,
1043 nullptr);
1044
1045 static const auto material_index_clamp = mf::build::SI1_SO<int, int>(
1046 "Material Index Validate",
1047 [](int value) {
1048 /* Use #short for the maximum since many areas still use that type for indices. */
1049 return std::clamp<int>(value, 0, std::numeric_limits<short>::max());
1050 },
1051 mf::build::exec_presets::AllSpanOrSingle());
1052 static BuiltinCustomDataLayerProvider material_index("material_index",
1053 AttrDomain::Face,
1055 BuiltinAttributeProvider::Deletable,
1056 face_access,
1057 nullptr,
1058 AttributeValidator{&material_index_clamp});
1059
1060 static const auto int2_index_clamp = mf::build::SI1_SO<int2, int2>(
1061 "Index Validate",
1062 [](int2 value) { return math::max(value, int2(0)); },
1063 mf::build::exec_presets::AllSpanOrSingle());
1064 static BuiltinCustomDataLayerProvider edge_verts(".edge_verts",
1065 AttrDomain::Edge,
1067 BuiltinAttributeProvider::NonDeletable,
1068 edge_access,
1069 nullptr,
1070 AttributeValidator{&int2_index_clamp});
1071
1072 /* NOTE: This clamping is more of a last resort, since it's quite easy to make an
1073 * invalid mesh that will crash Blender by arbitrarily editing this attribute. */
1074 static const auto int_index_clamp = mf::build::SI1_SO<int, int>(
1075 "Index Validate",
1076 [](int value) { return std::max(value, 0); },
1077 mf::build::exec_presets::AllSpanOrSingle());
1078 static BuiltinCustomDataLayerProvider corner_vert(".corner_vert",
1079 AttrDomain::Corner,
1081 BuiltinAttributeProvider::NonDeletable,
1082 corner_access,
1083 nullptr,
1084 AttributeValidator{&int_index_clamp});
1085 static BuiltinCustomDataLayerProvider corner_edge(".corner_edge",
1086 AttrDomain::Corner,
1088 BuiltinAttributeProvider::NonDeletable,
1089 corner_access,
1090 nullptr,
1091 AttributeValidator{&int_index_clamp});
1092
1093 static BuiltinCustomDataLayerProvider sharp_face("sharp_face",
1094 AttrDomain::Face,
1096 BuiltinAttributeProvider::Deletable,
1097 face_access,
1099
1100 static BuiltinCustomDataLayerProvider sharp_edge("sharp_edge",
1101 AttrDomain::Edge,
1103 BuiltinAttributeProvider::Deletable,
1104 edge_access,
1106
1107 static MeshVertexGroupsAttributeProvider vertex_groups;
1108 static CustomDataAttributeProvider corner_custom_data(AttrDomain::Corner, corner_access);
1109 static CustomDataAttributeProvider point_custom_data(AttrDomain::Point, point_access);
1110 static CustomDataAttributeProvider edge_custom_data(AttrDomain::Edge, edge_access);
1111 static CustomDataAttributeProvider face_custom_data(AttrDomain::Face, face_access);
1112
1113 return ComponentAttributeProviders({&position,
1114 &edge_verts,
1115 &corner_vert,
1116 &corner_edge,
1117 &id,
1118 &material_index,
1119 &sharp_face,
1120 &sharp_edge},
1121 {&corner_custom_data,
1122 &vertex_groups,
1123 &point_custom_data,
1124 &edge_custom_data,
1125 &face_custom_data});
1126}
1127
1129{
1132 attribute_accessor_functions::accessor_functions_for_providers<providers>();
1133 fn.domain_size = [](const void *owner, const AttrDomain domain) {
1134 if (owner == nullptr) {
1135 return 0;
1136 }
1137 const Mesh &mesh = *static_cast<const Mesh *>(owner);
1138 switch (domain) {
1139 case AttrDomain::Point:
1140 return mesh.verts_num;
1141 case AttrDomain::Edge:
1142 return mesh.edges_num;
1143 case AttrDomain::Face:
1144 return mesh.faces_num;
1145 case AttrDomain::Corner:
1146 return mesh.corners_num;
1147 default:
1148 return 0;
1149 }
1150 };
1151 fn.domain_supported = [](const void * /*owner*/, const AttrDomain domain) {
1152 return ELEM(domain, AttrDomain::Point, AttrDomain::Edge, AttrDomain::Face, AttrDomain::Corner);
1153 };
1154 fn.adapt_domain = [](const void *owner,
1155 const GVArray &varray,
1156 const AttrDomain from_domain,
1157 const AttrDomain to_domain) -> GVArray {
1158 if (owner == nullptr) {
1159 return {};
1160 }
1161 const Mesh &mesh = *static_cast<const Mesh *>(owner);
1162 return adapt_mesh_attribute_domain(mesh, varray, from_domain, to_domain);
1163 };
1164 return fn;
1165}
1166
1172
1173} // namespace blender::bke
1174
1176{
1178}
1179
1180blender::bke::MutableAttributeAccessor Mesh::attributes_for_write()
1181{
1184}
1185
1186namespace blender::bke {
1187
1188std::optional<AttributeAccessor> MeshComponent::attributes() const
1189{
1191}
1192
1193std::optional<MutableAttributeAccessor> MeshComponent::attributes_for_write()
1194{
1195 Mesh *mesh = this->get_for_write();
1197}
1198
1201} // 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:571
int BKE_defgroup_name_index(const ListBase *defbase, blender::StringRef name)
Definition deform.cc:534
void BKE_id_free(Main *bmain, void *idv)
Mesh * BKE_mesh_copy_for_eval(const Mesh &source)
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BUFFER_FOR_CPP_TYPE_VALUE(type, variable_name)
#define LISTBASE_FOREACH_INDEX(type, var, list, index_var)
void BLI_remlink(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:130
#define ELEM(...)
@ CD_PROP_FLOAT
@ CD_PROP_FLOAT3
@ CD_PROP_INT32_2D
@ CD_PROP_INT32
Object is a sort of wrapper for general info.
AttributeSet attributes
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:494
constexpr void fill(const T &value) const
Definition BLI_span.hh:518
constexpr Span slice(int64_t start, int64_t size) const
Definition BLI_span.hh:138
constexpr bool is_empty() const
Definition BLI_span.hh:261
static VArray ForContainer(ContainerT container)
static VArray ForSpan(Span< T > values)
IndexRange index_range() const
void replace(Mesh *mesh, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
GeometryComponentPtr copy() const override
void count_memory(MemoryCounter &memory) const override
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 attribute_id) const final
DEGForeachIDComponentCallback callback
#define MAKE_CONST_CUSTOM_DATA_GETTER(NAME)
#define MAKE_GET_ELEMENT_NUM_GETTER(NAME)
#define MAKE_MUTABLE_CUSTOM_DATA_GETTER(NAME)
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
typename DefaultMixerStruct< T >::type DefaultMixer
void adapt_mesh_domain_face_to_point_impl(const Mesh &mesh, const VArray< T > &old_values, MutableSpan< T > r_values)
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)
ImplicitSharingPtr< GeometryComponent > GeometryComponentPtr
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)
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)
void remove_defgroup_index(MutableSpan< MDeformVert > dverts, int defgroup_index)
Definition deform.cc:1796
static const AttributeAccessorFunctions & get_mesh_accessor_functions_ref()
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 GVArray adapt_mesh_domain_corner_to_edge(const Mesh &mesh, const GVArray &varray)
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 void adapt_mesh_domain_corner_to_point_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)
VArray< float3 > mesh_normals_varray(const Mesh &mesh, const IndexMask &mask, AttrDomain domain)
static ComponentAttributeProviders create_attribute_providers_for_mesh()
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:1790
VArray< float > varray_for_deform_verts(Span< MDeformVert > dverts, int defgroup_index)
Definition deform.cc:1786
T interpolate(const T &a, const T &b, const FactorT &t)
MatBase< T, NumCol, NumRow > normalize(const MatBase< T, NumCol, NumRow > &a)
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:95
VecBase< int32_t, 2 > int2
int verts_num
GVArray(* adapt_domain)(const void *owner, const GVArray &varray, AttrDomain from_domain, AttrDomain to_domain)
bool(* domain_supported)(const void *owner, AttrDomain domain)
int(* domain_size)(const void *owner, AttrDomain domain)
blender::BitVector is_loose_bits