Blender  V2.93
geometry_component_mesh.cc
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  */
16 
17 #include "BLI_listbase.h"
18 
19 #include "DNA_mesh_types.h"
20 #include "DNA_meshdata_types.h"
21 #include "DNA_object_types.h"
22 
23 #include "BKE_attribute_access.hh"
24 #include "BKE_attribute_math.hh"
25 #include "BKE_deform.h"
26 #include "BKE_geometry_set.hh"
27 #include "BKE_lib_id.h"
28 #include "BKE_mesh.h"
29 
31 
32 /* Can't include BKE_object_deform.h right now, due to an enum forward declaration. */
34 
36 
37 /* -------------------------------------------------------------------- */
42 {
43 }
44 
46 {
47  this->clear();
48 }
49 
51 {
52  MeshComponent *new_component = new MeshComponent();
53  if (mesh_ != nullptr) {
54  new_component->mesh_ = BKE_mesh_copy_for_eval(mesh_, false);
55  new_component->ownership_ = GeometryOwnershipType::Owned;
56  new_component->vertex_group_names_ = blender::Map(vertex_group_names_);
57  }
58  return new_component;
59 }
60 
62 {
63  BLI_assert(this->is_mutable());
64  if (mesh_ != nullptr) {
65  if (ownership_ == GeometryOwnershipType::Owned) {
66  BKE_id_free(nullptr, mesh_);
67  }
68  mesh_ = nullptr;
69  }
70  vertex_group_names_.clear();
71 }
72 
74 {
75  return mesh_ != nullptr;
76 }
77 
78 /* Clear the component and replace it with the new mesh. */
80 {
81  BLI_assert(this->is_mutable());
82  this->clear();
83  mesh_ = mesh;
84  ownership_ = ownership;
85 }
86 
87 /* This function exists for the same reason as #vertex_group_names_. Non-nodes modifiers need to
88  * be able to replace the mesh data without losing the vertex group names, which may have come
89  * from another object. */
91  GeometryOwnershipType ownership)
92 {
93  BLI_assert(this->is_mutable());
94  if (mesh_ != nullptr) {
95  if (ownership_ == GeometryOwnershipType::Owned) {
96  BKE_id_free(nullptr, mesh_);
97  }
98  mesh_ = nullptr;
99  }
100  mesh_ = mesh;
101  ownership_ = ownership;
102 }
103 
104 /* Return the mesh and clear the component. The caller takes over responsibility for freeing the
105  * mesh (if the component was responsible before). */
107 {
108  BLI_assert(this->is_mutable());
109  Mesh *mesh = mesh_;
110  mesh_ = nullptr;
111  return mesh;
112 }
113 
115 {
116  BLI_assert(this->is_mutable());
117  vertex_group_names_.clear();
118  int index = 0;
119  LISTBASE_FOREACH (const bDeformGroup *, group, &object.defbase) {
120  vertex_group_names_.add(group->name, index);
121  index++;
122  }
123 }
124 
126 {
127  return vertex_group_names_;
128 }
129 
130 /* This is only exposed for the internal attribute API. */
132 {
133  return vertex_group_names_;
134 }
135 
136 /* Get the mesh from this component. This method can be used by multiple threads at the same
137  * time. Therefore, the returned mesh should not be modified. No ownership is transferred. */
139 {
140  return mesh_;
141 }
142 
143 /* Get the mesh from this component. This method can only be used when the component is mutable,
144  * i.e. it is not shared. The returned mesh can be modified. No ownership is transferred. */
146 {
147  BLI_assert(this->is_mutable());
148  if (ownership_ == GeometryOwnershipType::ReadOnly) {
149  mesh_ = BKE_mesh_copy_for_eval(mesh_, false);
150  ownership_ = GeometryOwnershipType::Owned;
151  }
152  return mesh_;
153 }
154 
156 {
157  return mesh_ == nullptr;
158 }
159 
161 {
162  return ownership_ == GeometryOwnershipType::Owned;
163 }
164 
166 {
167  BLI_assert(this->is_mutable());
168  if (ownership_ != GeometryOwnershipType::Owned) {
169  mesh_ = BKE_mesh_copy_for_eval(mesh_, false);
170  ownership_ = GeometryOwnershipType::Owned;
171  }
172 }
173 
176 /* -------------------------------------------------------------------- */
181 {
182  if (mesh_ == nullptr) {
183  return 0;
184  }
185  switch (domain) {
186  case ATTR_DOMAIN_CORNER:
187  return mesh_->totloop;
188  case ATTR_DOMAIN_POINT:
189  return mesh_->totvert;
190  case ATTR_DOMAIN_EDGE:
191  return mesh_->totedge;
192  case ATTR_DOMAIN_FACE:
193  return mesh_->totpoly;
194  default:
195  break;
196  }
197  return 0;
198 }
199 
200 namespace blender::bke {
201 
202 template<typename T>
204  const TypedReadAttribute<T> &attribute,
205  MutableSpan<T> r_values)
206 {
207  BLI_assert(r_values.size() == mesh.totvert);
208  attribute_math::DefaultMixer<T> mixer(r_values);
209 
210  for (const int loop_index : IndexRange(mesh.totloop)) {
211  const T value = attribute[loop_index];
212  const MLoop &loop = mesh.mloop[loop_index];
213  const int point_index = loop.v;
214  mixer.mix_in(point_index, value);
215  }
216  mixer.finalize();
217 }
218 
220  ReadAttributePtr attribute)
221 {
222  ReadAttributePtr new_attribute;
223  const CustomDataType data_type = attribute->custom_data_type();
224  attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
225  using T = decltype(dummy);
226  if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
227  /* We compute all interpolated values at once, because for this interpolation, one has to
228  * iterate over all loops anyway. */
229  Array<T> values(mesh.totvert);
230  adapt_mesh_domain_corner_to_point_impl<T>(mesh, *attribute, values);
231  new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
232  std::move(values));
233  }
234  });
235  return new_attribute;
236 }
237 
238 template<typename T>
240  const TypedReadAttribute<T> &attribute,
241  MutableSpan<T> r_values)
242 {
243  BLI_assert(r_values.size() == mesh.totloop);
244 
245  for (const int loop_index : IndexRange(mesh.totloop)) {
246  const int vertex_index = mesh.mloop[loop_index].v;
247  r_values[loop_index] = attribute[vertex_index];
248  }
249 }
250 
252  ReadAttributePtr attribute)
253 {
254  ReadAttributePtr new_attribute;
255  const CustomDataType data_type = attribute->custom_data_type();
256  attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
257  using T = decltype(dummy);
258  /* It is not strictly necessary to compute the value for all corners here. Instead one could
259  * lazily lookup the mesh topology when a specific index accessed. This can be more efficient
260  * when an algorithm only accesses very few of the corner values. However, for the algorithms
261  * we currently have, precomputing the array is fine. Also, it is easier to implement. */
262  Array<T> values(mesh.totloop);
263  adapt_mesh_domain_point_to_corner_impl<T>(mesh, *attribute, values);
264  new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_CORNER,
265  std::move(values));
266  });
267  return new_attribute;
268 }
269 
275 template<typename T>
277  Span<T> old_values,
278  MutableSpan<T> r_values)
279 {
280  BLI_assert(r_values.size() == mesh.totpoly);
281  attribute_math::DefaultMixer<T> mixer(r_values);
282 
283  for (const int poly_index : IndexRange(mesh.totpoly)) {
284  const MPoly &poly = mesh.mpoly[poly_index];
285  for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
286  const T value = old_values[loop_index];
287  mixer.mix_in(poly_index, value);
288  }
289  }
290 
291  mixer.finalize();
292 }
293 
295  ReadAttributePtr attribute)
296 {
297  ReadAttributePtr new_attribute;
298  const CustomDataType data_type = attribute->custom_data_type();
299  attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
300  using T = decltype(dummy);
301  if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
302  Array<T> values(mesh.totpoly);
303  adapt_mesh_domain_corner_to_face_impl<T>(mesh, attribute->get_span<T>(), values);
304  new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
305  std::move(values));
306  }
307  });
308  return new_attribute;
309 }
310 
311 template<typename T>
313  Span<T> old_values,
314  MutableSpan<T> r_values)
315 {
316  BLI_assert(r_values.size() == mesh.totedge);
317  attribute_math::DefaultMixer<T> mixer(r_values);
318 
319  for (const int poly_index : IndexRange(mesh.totpoly)) {
320  const MPoly &poly = mesh.mpoly[poly_index];
321 
322  /* For every edge, mix values from the two adjacent corners (the current and next corner). */
323  for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
324  const int loop_index_next = (loop_index + 1) % poly.totloop;
325  const MLoop &loop = mesh.mloop[loop_index];
326  const int edge_index = loop.e;
327  mixer.mix_in(edge_index, old_values[loop_index]);
328  mixer.mix_in(edge_index, old_values[loop_index_next]);
329  }
330  }
331 
332  mixer.finalize();
333 }
334 
336  ReadAttributePtr attribute)
337 {
338  ReadAttributePtr new_attribute;
339  const CustomDataType data_type = attribute->custom_data_type();
340  attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
341  using T = decltype(dummy);
342  if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
343  Array<T> values(mesh.totedge);
344  adapt_mesh_domain_corner_to_edge_impl<T>(mesh, attribute->get_span<T>(), values);
345  new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
346  std::move(values));
347  }
348  });
349  return new_attribute;
350 }
351 
352 template<typename T>
354  Span<T> old_values,
355  MutableSpan<T> r_values)
356 {
357  BLI_assert(r_values.size() == mesh.totvert);
358  attribute_math::DefaultMixer<T> mixer(r_values);
359 
360  for (const int poly_index : IndexRange(mesh.totpoly)) {
361  const MPoly &poly = mesh.mpoly[poly_index];
362  const T value = old_values[poly_index];
363  for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
364  const MLoop &loop = mesh.mloop[loop_index];
365  const int point_index = loop.v;
366  mixer.mix_in(point_index, value);
367  }
368  }
369 
370  mixer.finalize();
371 }
372 
374  ReadAttributePtr attribute)
375 {
376  ReadAttributePtr new_attribute;
377  const CustomDataType data_type = attribute->custom_data_type();
378  attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
379  using T = decltype(dummy);
380  if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
381  Array<T> values(mesh.totvert);
382  adapt_mesh_domain_face_to_point_impl<T>(mesh, attribute->get_span<T>(), values);
383  new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
384  std::move(values));
385  }
386  });
387  return new_attribute;
388 }
389 
390 template<typename T>
392  const Span<T> old_values,
393  MutableSpan<T> r_values)
394 {
395  BLI_assert(r_values.size() == mesh.totloop);
396 
397  for (const int poly_index : IndexRange(mesh.totpoly)) {
398  const MPoly &poly = mesh.mpoly[poly_index];
399  MutableSpan<T> poly_corner_values = r_values.slice(poly.loopstart, poly.totloop);
400  poly_corner_values.fill(old_values[poly_index]);
401  }
402 }
403 
405  ReadAttributePtr attribute)
406 {
407  ReadAttributePtr new_attribute;
408  const CustomDataType data_type = attribute->custom_data_type();
409  attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
410  using T = decltype(dummy);
411  if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
412  Array<T> values(mesh.totloop);
413  adapt_mesh_domain_face_to_corner_impl<T>(mesh, attribute->get_span<T>(), values);
414  new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
415  std::move(values));
416  }
417  });
418  return new_attribute;
419 }
420 
421 template<typename T>
423  const Span<T> old_values,
424  MutableSpan<T> r_values)
425 {
426  BLI_assert(r_values.size() == mesh.totedge);
427  attribute_math::DefaultMixer<T> mixer(r_values);
428 
429  for (const int poly_index : IndexRange(mesh.totpoly)) {
430  const MPoly &poly = mesh.mpoly[poly_index];
431  const T value = old_values[poly_index];
432  for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
433  const MLoop &loop = mesh.mloop[loop_index];
434  mixer.mix_in(loop.e, value);
435  }
436  }
437  mixer.finalize();
438 }
439 
441  ReadAttributePtr attribute)
442 {
443  ReadAttributePtr new_attribute;
444  const CustomDataType data_type = attribute->custom_data_type();
445  attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
446  using T = decltype(dummy);
447  if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
448  Array<T> values(mesh.totedge);
449  adapt_mesh_domain_face_to_edge_impl<T>(mesh, attribute->get_span<T>(), values);
450  new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
451  std::move(values));
452  }
453  });
454  return new_attribute;
455 }
456 
462 template<typename T>
464  const Span<T> old_values,
465  MutableSpan<T> r_values)
466 {
467  BLI_assert(r_values.size() == mesh.totpoly);
468  attribute_math::DefaultMixer<T> mixer(r_values);
469 
470  for (const int poly_index : IndexRange(mesh.totpoly)) {
471  const MPoly &poly = mesh.mpoly[poly_index];
472  for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
473  MLoop &loop = mesh.mloop[loop_index];
474  const int point_index = loop.v;
475  mixer.mix_in(poly_index, old_values[point_index]);
476  }
477  }
478  mixer.finalize();
479 }
480 
482  ReadAttributePtr attribute)
483 {
484  ReadAttributePtr new_attribute;
485  const CustomDataType data_type = attribute->custom_data_type();
486  attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
487  using T = decltype(dummy);
488  if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
489  Array<T> values(mesh.totpoly);
490  adapt_mesh_domain_point_to_face_impl<T>(mesh, attribute->get_span<T>(), values);
491  new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
492  std::move(values));
493  }
494  });
495  return new_attribute;
496 }
497 
503 template<typename T>
505  const Span<T> old_values,
506  MutableSpan<T> r_values)
507 {
508  BLI_assert(r_values.size() == mesh.totedge);
509  attribute_math::DefaultMixer<T> mixer(r_values);
510 
511  for (const int edge_index : IndexRange(mesh.totedge)) {
512  const MEdge &edge = mesh.medge[edge_index];
513  mixer.mix_in(edge_index, old_values[edge.v1]);
514  mixer.mix_in(edge_index, old_values[edge.v2]);
515  }
516 
517  mixer.finalize();
518 }
519 
521  ReadAttributePtr attribute)
522 {
523  ReadAttributePtr new_attribute;
524  const CustomDataType data_type = attribute->custom_data_type();
525  attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
526  using T = decltype(dummy);
527  if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
528  Array<T> values(mesh.totedge);
529  adapt_mesh_domain_point_to_edge_impl<T>(mesh, attribute->get_span<T>(), values);
530  new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
531  std::move(values));
532  }
533  });
534  return new_attribute;
535 }
536 
537 template<typename T>
539  const Span<T> old_values,
540  MutableSpan<T> r_values)
541 {
542  BLI_assert(r_values.size() == mesh.totloop);
543  attribute_math::DefaultMixer<T> mixer(r_values);
544 
545  for (const int poly_index : IndexRange(mesh.totpoly)) {
546  const MPoly &poly = mesh.mpoly[poly_index];
547 
548  /* For every corner, mix the values from the adjacent edges on the face. */
549  for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
550  const int loop_index_prev = loop_index - 1 + (loop_index == poly.loopstart) * poly.totloop;
551  const MLoop &loop = mesh.mloop[loop_index];
552  const MLoop &loop_prev = mesh.mloop[loop_index_prev];
553  mixer.mix_in(loop_index, old_values[loop.e]);
554  mixer.mix_in(loop_index, old_values[loop_prev.e]);
555  }
556  }
557 
558  mixer.finalize();
559 }
560 
562  ReadAttributePtr attribute)
563 {
564  ReadAttributePtr new_attribute;
565  const CustomDataType data_type = attribute->custom_data_type();
566  attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
567  using T = decltype(dummy);
568  if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
569  Array<T> values(mesh.totloop);
570  adapt_mesh_domain_edge_to_corner_impl<T>(mesh, attribute->get_span<T>(), values);
571  new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
572  std::move(values));
573  }
574  });
575  return new_attribute;
576 }
577 
578 template<typename T>
580  const Span<T> old_values,
581  MutableSpan<T> r_values)
582 {
583  BLI_assert(r_values.size() == mesh.totvert);
584  attribute_math::DefaultMixer<T> mixer(r_values);
585 
586  for (const int edge_index : IndexRange(mesh.totedge)) {
587  const MEdge &edge = mesh.medge[edge_index];
588  const T value = old_values[edge_index];
589  mixer.mix_in(edge.v1, value);
590  mixer.mix_in(edge.v2, value);
591  }
592 
593  mixer.finalize();
594 }
595 
597  ReadAttributePtr attribute)
598 {
599  ReadAttributePtr new_attribute;
600  const CustomDataType data_type = attribute->custom_data_type();
601  attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
602  using T = decltype(dummy);
603  if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
604  Array<T> values(mesh.totvert);
605  adapt_mesh_domain_edge_to_point_impl<T>(mesh, attribute->get_span<T>(), values);
606  new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
607  std::move(values));
608  }
609  });
610  return new_attribute;
611 }
612 
618 template<typename T>
620  const Span<T> old_values,
621  MutableSpan<T> r_values)
622 {
623  BLI_assert(r_values.size() == mesh.totpoly);
624  attribute_math::DefaultMixer<T> mixer(r_values);
625 
626  for (const int poly_index : IndexRange(mesh.totpoly)) {
627  const MPoly &poly = mesh.mpoly[poly_index];
628  for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
629  const MLoop &loop = mesh.mloop[loop_index];
630  mixer.mix_in(poly_index, old_values[loop.e]);
631  }
632  }
633 
634  mixer.finalize();
635 }
636 
638  ReadAttributePtr attribute)
639 {
640  ReadAttributePtr new_attribute;
641  const CustomDataType data_type = attribute->custom_data_type();
642  attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
643  using T = decltype(dummy);
644  if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
645  Array<T> values(mesh.totpoly);
646  adapt_mesh_domain_edge_to_face_impl<T>(mesh, attribute->get_span<T>(), values);
647  new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
648  std::move(values));
649  }
650  });
651  return new_attribute;
652 }
653 
654 } // namespace blender::bke
655 
657  const AttributeDomain new_domain) const
658 {
659  if (!attribute) {
660  return {};
661  }
662  if (attribute->size() == 0) {
663  return {};
664  }
665  const AttributeDomain old_domain = attribute->domain();
666  if (old_domain == new_domain) {
667  return attribute;
668  }
669 
670  switch (old_domain) {
671  case ATTR_DOMAIN_CORNER: {
672  switch (new_domain) {
673  case ATTR_DOMAIN_POINT:
674  return blender::bke::adapt_mesh_domain_corner_to_point(*mesh_, std::move(attribute));
675  case ATTR_DOMAIN_FACE:
676  return blender::bke::adapt_mesh_domain_corner_to_face(*mesh_, std::move(attribute));
677  case ATTR_DOMAIN_EDGE:
678  return blender::bke::adapt_mesh_domain_corner_to_edge(*mesh_, std::move(attribute));
679  default:
680  break;
681  }
682  break;
683  }
684  case ATTR_DOMAIN_POINT: {
685  switch (new_domain) {
686  case ATTR_DOMAIN_CORNER:
687  return blender::bke::adapt_mesh_domain_point_to_corner(*mesh_, std::move(attribute));
688  case ATTR_DOMAIN_FACE:
689  return blender::bke::adapt_mesh_domain_point_to_face(*mesh_, std::move(attribute));
690  case ATTR_DOMAIN_EDGE:
691  return blender::bke::adapt_mesh_domain_point_to_edge(*mesh_, std::move(attribute));
692  default:
693  break;
694  }
695  break;
696  }
697  case ATTR_DOMAIN_FACE: {
698  switch (new_domain) {
699  case ATTR_DOMAIN_POINT:
700  return blender::bke::adapt_mesh_domain_face_to_point(*mesh_, std::move(attribute));
701  case ATTR_DOMAIN_CORNER:
702  return blender::bke::adapt_mesh_domain_face_to_corner(*mesh_, std::move(attribute));
703  case ATTR_DOMAIN_EDGE:
704  return blender::bke::adapt_mesh_domain_face_to_edge(*mesh_, std::move(attribute));
705  default:
706  break;
707  }
708  break;
709  }
710  case ATTR_DOMAIN_EDGE: {
711  switch (new_domain) {
712  case ATTR_DOMAIN_CORNER:
713  return blender::bke::adapt_mesh_domain_edge_to_corner(*mesh_, std::move(attribute));
714  case ATTR_DOMAIN_POINT:
715  return blender::bke::adapt_mesh_domain_edge_to_point(*mesh_, std::move(attribute));
716  case ATTR_DOMAIN_FACE:
717  return blender::bke::adapt_mesh_domain_edge_to_face(*mesh_, std::move(attribute));
718  default:
719  break;
720  }
721  break;
722  }
723  default:
724  break;
725  }
726 
727  return {};
728 }
729 
731 {
733  MeshComponent &mesh_component = static_cast<MeshComponent &>(component);
734  return mesh_component.get_for_write();
735 }
736 
738 {
740  const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
741  return mesh_component.get_for_read();
742 }
743 
744 namespace blender::bke {
745 
746 template<typename StructT,
747  typename ElemT,
748  ElemT (*GetFunc)(const StructT &),
749  AttributeDomain Domain>
750 static ReadAttributePtr make_derived_read_attribute(const void *data, const int domain_size)
751 {
752  return std::make_unique<DerivedArrayReadAttribute<StructT, ElemT, GetFunc>>(
753  Domain, Span<StructT>((const StructT *)data, domain_size));
754 }
755 
756 template<typename StructT,
757  typename ElemT,
758  ElemT (*GetFunc)(const StructT &),
759  void (*SetFunc)(StructT &, const ElemT &),
760  AttributeDomain Domain>
761 static WriteAttributePtr make_derived_write_attribute(void *data, const int domain_size)
762 {
763  return std::make_unique<DerivedArrayWriteAttribute<StructT, ElemT, GetFunc, SetFunc>>(
764  Domain, MutableSpan<StructT>((StructT *)data, domain_size));
765 }
766 
767 static float3 get_vertex_position(const MVert &vert)
768 {
769  return float3(vert.co);
770 }
771 
772 static void set_vertex_position(MVert &vert, const float3 &position)
773 {
774  copy_v3_v3(vert.co, position);
775 }
776 
778 {
780  if (mesh != nullptr) {
782  }
783 }
784 
785 static int get_material_index(const MPoly &mpoly)
786 {
787  return static_cast<int>(mpoly.mat_nr);
788 }
789 
790 static void set_material_index(MPoly &mpoly, const int &index)
791 {
792  mpoly.mat_nr = static_cast<short>(std::clamp(index, 0, SHRT_MAX));
793 }
794 
795 static bool get_shade_smooth(const MPoly &mpoly)
796 {
797  return mpoly.flag & ME_SMOOTH;
798 }
799 
800 static void set_shade_smooth(MPoly &mpoly, const bool &value)
801 {
802  SET_FLAG_FROM_TEST(mpoly.flag, value, ME_SMOOTH);
803 }
804 
805 static float2 get_loop_uv(const MLoopUV &uv)
806 {
807  return float2(uv.uv);
808 }
809 
810 static void set_loop_uv(MLoopUV &uv, const float2 &co)
811 {
812  copy_v2_v2(uv.uv, co);
813 }
814 
816 {
817  Color4f srgb_color;
818  rgba_uchar_to_float(srgb_color, &col.r);
819  Color4f linear_color;
820  srgb_to_linearrgb_v4(linear_color, srgb_color);
821  return linear_color;
822 }
823 
824 static void set_loop_color(MLoopCol &col, const Color4f &linear_color)
825 {
826  linearrgb_to_srgb_uchar4(&col.r, linear_color);
827 }
828 
829 static float get_crease(const MEdge &edge)
830 {
831  return edge.crease / 255.0f;
832 }
833 
834 static void set_crease(MEdge &edge, const float &value)
835 {
836  edge.crease = round_fl_to_uchar_clamp(value * 255.0f);
837 }
838 
840  private:
841  MDeformVert *dverts_;
842  const int dvert_index_;
843 
844  public:
845  VertexWeightWriteAttribute(MDeformVert *dverts, const int totvert, const int dvert_index)
847  dverts_(dverts),
848  dvert_index_(dvert_index)
849  {
850  }
851 
852  void get_internal(const int64_t index, void *r_value) const override
853  {
854  get_internal(dverts_, dvert_index_, index, r_value);
855  }
856 
857  void set_internal(const int64_t index, const void *value) override
858  {
859  MDeformWeight *weight = BKE_defvert_ensure_index(&dverts_[index], dvert_index_);
860  weight->weight = *reinterpret_cast<const float *>(value);
861  }
862 
863  static void get_internal(const MDeformVert *dverts,
864  const int dvert_index,
865  const int64_t index,
866  void *r_value)
867  {
868  if (dverts == nullptr) {
869  *(float *)r_value = 0.0f;
870  return;
871  }
872  const MDeformVert &dvert = dverts[index];
873  for (const MDeformWeight &weight : Span(dvert.dw, dvert.totweight)) {
874  if (weight.def_nr == dvert_index) {
875  *(float *)r_value = weight.weight;
876  return;
877  }
878  }
879  *(float *)r_value = 0.0f;
880  }
881 };
882 
884  private:
885  const MDeformVert *dverts_;
886  const int dvert_index_;
887 
888  public:
889  VertexWeightReadAttribute(const MDeformVert *dverts, const int totvert, const int dvert_index)
891  dverts_(dverts),
892  dvert_index_(dvert_index)
893  {
894  }
895 
896  void get_internal(const int64_t index, void *r_value) const override
897  {
898  VertexWeightWriteAttribute::get_internal(dverts_, dvert_index_, index, r_value);
899  }
900 };
901 
906  public:
908  const StringRef attribute_name) const final
909  {
911  const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
912  const Mesh *mesh = mesh_component.get_for_read();
913  const int vertex_group_index = mesh_component.vertex_group_names().lookup_default_as(
914  attribute_name, -1);
915  if (vertex_group_index < 0) {
916  return {};
917  }
918  if (mesh == nullptr || mesh->dvert == nullptr) {
919  static const float default_value = 0.0f;
920  return std::make_unique<ConstantReadAttribute>(
921  ATTR_DOMAIN_POINT, mesh->totvert, CPPType::get<float>(), &default_value);
922  }
923  return std::make_unique<VertexWeightReadAttribute>(
924  mesh->dvert, mesh->totvert, vertex_group_index);
925  }
926 
928  const StringRef attribute_name) const final
929  {
931  MeshComponent &mesh_component = static_cast<MeshComponent &>(component);
932  Mesh *mesh = mesh_component.get_for_write();
933  if (mesh == nullptr) {
934  return {};
935  }
936  const int vertex_group_index = mesh_component.vertex_group_names().lookup_default_as(
937  attribute_name, -1);
938  if (vertex_group_index < 0) {
939  return {};
940  }
941  if (mesh->dvert == nullptr) {
943  }
944  else {
945  /* Copy the data layer if it is shared with some other mesh. */
947  &mesh->vdata, CD_MDEFORMVERT, mesh->totvert);
948  }
949  return std::make_unique<blender::bke::VertexWeightWriteAttribute>(
950  mesh->dvert, mesh->totvert, vertex_group_index);
951  }
952 
953  bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final
954  {
956  MeshComponent &mesh_component = static_cast<MeshComponent &>(component);
957 
958  const int vertex_group_index = mesh_component.vertex_group_names().pop_default_as(
959  attribute_name, -1);
960  if (vertex_group_index < 0) {
961  return false;
962  }
963  Mesh *mesh = mesh_component.get_for_write();
964  if (mesh == nullptr) {
965  return true;
966  }
967  if (mesh->dvert == nullptr) {
968  return true;
969  }
970  for (MDeformVert &dvert : MutableSpan(mesh->dvert, mesh->totvert)) {
971  MDeformWeight *weight = BKE_defvert_find_index(&dvert, vertex_group_index);
972  BKE_defvert_remove_group(&dvert, weight);
973  }
974  return true;
975  }
976 
978  const AttributeForeachCallback callback) const final
979  {
981  const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
982  for (const auto item : mesh_component.vertex_group_names().items()) {
983  const StringRefNull name = item.key;
984  const int vertex_group_index = item.value;
985  if (vertex_group_index >= 0) {
987  if (!callback(name, meta_data)) {
988  return false;
989  }
990  }
991  }
992  return true;
993  }
994 
995  void foreach_domain(const FunctionRef<void(AttributeDomain)> callback) const final
996  {
998  }
999 };
1000 
1005  public:
1009  {
1010  }
1011 
1013  {
1014  const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
1015  const Mesh *mesh = mesh_component.get_for_read();
1016  if (mesh == nullptr) {
1017  return {};
1018  }
1019 
1020  /* Use existing normals if possible. */
1022  CustomData_has_layer(&mesh->pdata, CD_NORMAL)) {
1023  const void *data = CustomData_get_layer(&mesh->pdata, CD_NORMAL);
1024 
1025  return std::make_unique<ArrayReadAttribute<float3>>(
1027  }
1028 
1030  for (const int i : IndexRange(mesh->totpoly)) {
1031  const MPoly *poly = &mesh->mpoly[i];
1033  }
1034 
1035  return std::make_unique<OwnedArrayReadAttribute<float3>>(ATTR_DOMAIN_FACE, std::move(normals));
1036  }
1037 
1039  {
1040  return {};
1041  }
1042 
1044  {
1045  return false;
1046  }
1047 
1049  {
1050  return false;
1051  }
1052 
1053  bool exists(const GeometryComponent &component) const final
1054  {
1055  return component.attribute_domain_size(ATTR_DOMAIN_FACE) != 0;
1056  }
1057 };
1058 
1064 {
1065  static auto update_custom_data_pointers = [](GeometryComponent &component) {
1067  if (mesh != nullptr) {
1069  }
1070  };
1071 
1072 #define MAKE_MUTABLE_CUSTOM_DATA_GETTER(NAME) \
1073  [](GeometryComponent &component) -> CustomData * { \
1074  Mesh *mesh = get_mesh_from_component_for_write(component); \
1075  return mesh ? &mesh->NAME : nullptr; \
1076  }
1077 #define MAKE_CONST_CUSTOM_DATA_GETTER(NAME) \
1078  [](const GeometryComponent &component) -> const CustomData * { \
1079  const Mesh *mesh = get_mesh_from_component_for_read(component); \
1080  return mesh ? &mesh->NAME : nullptr; \
1081  }
1082 
1083  static CustomDataAccessInfo corner_access = {MAKE_MUTABLE_CUSTOM_DATA_GETTER(ldata),
1085  update_custom_data_pointers};
1086  static CustomDataAccessInfo point_access = {MAKE_MUTABLE_CUSTOM_DATA_GETTER(vdata),
1088  update_custom_data_pointers};
1089  static CustomDataAccessInfo edge_access = {MAKE_MUTABLE_CUSTOM_DATA_GETTER(edata),
1091  update_custom_data_pointers};
1092  static CustomDataAccessInfo face_access = {MAKE_MUTABLE_CUSTOM_DATA_GETTER(pdata),
1094  update_custom_data_pointers};
1095 
1096 #undef MAKE_CONST_CUSTOM_DATA_GETTER
1097 #undef MAKE_MUTABLE_CUSTOM_DATA_GETTER
1098 
1099  static BuiltinCustomDataLayerProvider position(
1100  "position",
1103  CD_MVERT,
1107  point_access,
1108  make_derived_read_attribute<MVert, float3, get_vertex_position, ATTR_DOMAIN_POINT>,
1110  float3,
1115 
1117 
1118  static BuiltinCustomDataLayerProvider material_index(
1119  "material_index",
1121  CD_PROP_INT32,
1122  CD_MPOLY,
1126  face_access,
1127  make_derived_read_attribute<MPoly, int, get_material_index, ATTR_DOMAIN_FACE>,
1129  int,
1133  nullptr);
1134 
1135  static BuiltinCustomDataLayerProvider shade_smooth(
1136  "shade_smooth",
1138  CD_PROP_BOOL,
1139  CD_MPOLY,
1143  face_access,
1144  make_derived_read_attribute<MPoly, bool, get_shade_smooth, ATTR_DOMAIN_FACE>,
1146  bool,
1150  nullptr);
1151 
1152  static BuiltinCustomDataLayerProvider crease(
1153  "crease",
1155  CD_PROP_FLOAT,
1156  CD_MEDGE,
1160  edge_access,
1161  make_derived_read_attribute<MEdge, float, get_crease, ATTR_DOMAIN_EDGE>,
1162  make_derived_write_attribute<MEdge, float, get_crease, set_crease, ATTR_DOMAIN_EDGE>,
1163  nullptr);
1164 
1165  static NamedLegacyCustomDataProvider uvs(
1168  CD_MLOOPUV,
1169  corner_access,
1170  make_derived_read_attribute<MLoopUV, float2, get_loop_uv, ATTR_DOMAIN_CORNER>,
1171  make_derived_write_attribute<MLoopUV, float2, get_loop_uv, set_loop_uv, ATTR_DOMAIN_CORNER>);
1172 
1173  static NamedLegacyCustomDataProvider vertex_colors(
1175  CD_PROP_COLOR,
1176  CD_MLOOPCOL,
1177  corner_access,
1178  make_derived_read_attribute<MLoopCol, Color4f, get_loop_color, ATTR_DOMAIN_CORNER>,
1180  Color4f,
1184 
1185  static VertexGroupsAttributeProvider vertex_groups;
1186  static CustomDataAttributeProvider corner_custom_data(ATTR_DOMAIN_CORNER, corner_access);
1187  static CustomDataAttributeProvider point_custom_data(ATTR_DOMAIN_POINT, point_access);
1188  static CustomDataAttributeProvider edge_custom_data(ATTR_DOMAIN_EDGE, edge_access);
1189  static CustomDataAttributeProvider face_custom_data(ATTR_DOMAIN_FACE, face_access);
1190 
1191  return ComponentAttributeProviders({&position, &material_index, &shade_smooth, &normal, &crease},
1192  {&uvs,
1193  &vertex_colors,
1194  &corner_custom_data,
1195  &vertex_groups,
1196  &point_custom_data,
1197  &edge_custom_data,
1198  &face_custom_data});
1199 }
1200 
1201 } // namespace blender::bke
1202 
1203 const blender::bke::ComponentAttributeProviders *MeshComponent::get_attribute_providers() const
1204 {
1207  return &providers;
1208 }
1209 
typedef float(TangentPoint)[2]
AttributeDomain
Definition: BKE_attribute.h:41
@ ATTR_DOMAIN_POINT
Definition: BKE_attribute.h:43
@ ATTR_DOMAIN_FACE
Definition: BKE_attribute.h:46
@ ATTR_DOMAIN_CORNER
Definition: BKE_attribute.h:45
@ ATTR_DOMAIN_EDGE
Definition: BKE_attribute.h:44
bool CustomData_has_layer(const struct CustomData *data, int type)
void * CustomData_duplicate_referenced_layer(struct CustomData *data, const int type, const int totelem)
Definition: customdata.c:2788
void * CustomData_get_layer(const struct CustomData *data, int type)
support for deformation groups and hooks.
struct MDeformWeight * BKE_defvert_ensure_index(struct MDeformVert *dv, const int defgroup)
Definition: deform.c:688
struct MDeformWeight * BKE_defvert_find_index(const struct MDeformVert *dv, const int defgroup)
void BKE_defvert_remove_group(struct MDeformVert *dvert, struct MDeformWeight *dw)
Definition: deform.c:754
@ GEO_COMPONENT_TYPE_MESH
GeometryOwnershipType
void BKE_id_free(struct Main *bmain, void *idv)
struct Mesh * BKE_mesh_copy_for_eval(struct Mesh *source, bool reference)
Definition: mesh.c:995
void BKE_mesh_calc_poly_normal(const struct MPoly *mpoly, const struct MLoop *loopstart, const struct MVert *mvarray, float r_no[3])
void BKE_mesh_update_customdata_pointers(struct Mesh *me, const bool do_ensure_tess_cd)
Definition: mesh.c:766
#define BLI_assert(a)
Definition: BLI_assert.h:58
#define final(a, b, c)
Definition: BLI_hash.h:35
#define LISTBASE_FOREACH(type, var, list)
Definition: BLI_listbase.h:172
MINLINE unsigned char round_fl_to_uchar_clamp(float a)
MINLINE void srgb_to_linearrgb_v4(float linear[4], const float srgb[4])
void rgba_uchar_to_float(float r_col[4], const unsigned char col_ub[4])
Definition: math_color.c:414
MINLINE void linearrgb_to_srgb_uchar4(unsigned char srgb[4], const float linear[4])
MINLINE void copy_v2_v2(float r[2], const float a[2])
MINLINE void copy_v3_v3(float r[3], const float a[3])
#define UNUSED(x)
#define SET_FLAG_FROM_TEST(value, test, flag)
static uint8 component(Color32 c, uint i)
Definition: ColorBlock.cpp:126
#define CD_MASK_NORMAL
CustomDataType
@ CD_PROP_FLOAT
@ CD_PROP_FLOAT3
@ CD_MDEFORMVERT
@ CD_PROP_COLOR
@ CD_PROP_INT32
@ CD_PROP_FLOAT2
@ CD_PROP_BOOL
@ CD_MLOOPCOL
@ CD_MVERT
@ CD_MLOOPUV
@ ME_SMOOTH
Object is a sort of wrapper for general info.
bool is_mutable() const
Definition: geometry_set.cc:81
const Mesh * get_for_read() const
void replace_mesh_but_keep_vertex_group_names(Mesh *mesh, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
bool is_empty() const final
void replace(Mesh *mesh, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
void copy_vertex_group_names_from_object(const struct Object &object)
GeometryComponent * copy() const override
bool owns_direct_data() const override
void ensure_owns_direct_data() override
blender::bke::ReadAttributePtr attribute_try_adapt_domain(blender::bke::ReadAttributePtr attribute, const AttributeDomain new_domain) const final
const blender::Map< std::string, int > & vertex_group_names() const
int attribute_domain_size(const AttributeDomain domain) const final
Value pop_default_as(const ForwardKey &key, ForwardValue &&default_value)
Definition: BLI_map.hh:417
void clear()
Definition: BLI_map.hh:916
bool add(const Key &key, const Value &value)
Definition: BLI_map.hh:264
ItemIterator items() const
Definition: BLI_map.hh:825
Value lookup_default_as(const ForwardKey &key, ForwardValue &&default_value) const
Definition: BLI_map.hh:529
constexpr int64_t size() const
Definition: BLI_span.hh:524
constexpr void fill(const T &value)
Definition: BLI_span.hh:540
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
Definition: BLI_span.hh:594
bool exists(const GeometryComponent &component) const final
ReadAttributePtr try_get_for_read(const GeometryComponent &component) const final
WriteAttributePtr try_get_for_write(GeometryComponent &UNUSED(component)) const final
bool try_create(GeometryComponent &UNUSED(component)) const final
bool try_delete(GeometryComponent &UNUSED(component)) const final
void get(const int64_t index, void *r_value) const
void foreach_domain(const FunctionRef< void(AttributeDomain)> callback) const final
bool foreach_attribute(const GeometryComponent &component, const AttributeForeachCallback callback) const final
WriteAttributePtr try_get_for_write(GeometryComponent &component, const StringRef attribute_name) const final
ReadAttributePtr try_get_for_read(const GeometryComponent &component, const StringRef attribute_name) const final
bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final
void get_internal(const int64_t index, void *r_value) const override
VertexWeightReadAttribute(const MDeformVert *dverts, const int totvert, const int dvert_index)
static void get_internal(const MDeformVert *dverts, const int dvert_index, const int64_t index, void *r_value)
VertexWeightWriteAttribute(MDeformVert *dverts, const int totvert, const int dvert_index)
void get_internal(const int64_t index, void *r_value) const override
void set_internal(const int64_t index, const void *value) override
void get(const int64_t index, void *r_value) const
DEGForeachIDComponentCallback callback
static float normals[][3]
#define MAKE_CONST_CUSTOM_DATA_GETTER(NAME)
MDeformVert * BKE_object_defgroup_data_create(ID *id)
#define MAKE_MUTABLE_CUSTOM_DATA_GETTER(NAME)
static Mesh * get_mesh_from_component_for_write(GeometryComponent &component)
static const Mesh * get_mesh_from_component_for_read(const GeometryComponent &component)
uint col
IconTextureDrawCall normal
#define T
typename DefaultMixerStruct< T >::type DefaultMixer
void convert_to_static_type(const CustomDataType data_type, const Func &func)
static ReadAttributePtr adapt_mesh_domain_point_to_face(const Mesh &mesh, ReadAttributePtr attribute)
static float3 get_vertex_position(const MVert &vert)
static ReadAttributePtr adapt_mesh_domain_edge_to_corner(const Mesh &mesh, ReadAttributePtr attribute)
static ReadAttributePtr adapt_mesh_domain_edge_to_point(const Mesh &mesh, ReadAttributePtr attribute)
static void adapt_mesh_domain_edge_to_point_impl(const Mesh &mesh, const Span< T > old_values, MutableSpan< T > r_values)
static void adapt_mesh_domain_point_to_face_impl(const Mesh &mesh, const Span< T > old_values, MutableSpan< T > r_values)
void adapt_mesh_domain_edge_to_corner_impl(const Mesh &mesh, const Span< T > old_values, MutableSpan< T > r_values)
static ReadAttributePtr adapt_mesh_domain_corner_to_point(const Mesh &mesh, ReadAttributePtr attribute)
static float get_crease(const MEdge &edge)
void adapt_mesh_domain_face_to_point_impl(const Mesh &mesh, Span< T > old_values, MutableSpan< T > r_values)
static void set_crease(MEdge &edge, const float &value)
static ReadAttributePtr adapt_mesh_domain_face_to_point(const Mesh &mesh, ReadAttributePtr attribute)
static void adapt_mesh_domain_point_to_corner_impl(const Mesh &mesh, const TypedReadAttribute< T > &attribute, MutableSpan< T > r_values)
static void adapt_mesh_domain_corner_to_point_impl(const Mesh &mesh, const TypedReadAttribute< T > &attribute, MutableSpan< T > r_values)
static WriteAttributePtr make_derived_write_attribute(void *data, const int domain_size)
static ReadAttributePtr adapt_mesh_domain_face_to_edge(const Mesh &mesh, ReadAttributePtr attribute)
static bool get_shade_smooth(const MPoly &mpoly)
static Color4f get_loop_color(const MLoopCol &col)
static ReadAttributePtr adapt_mesh_domain_face_to_corner(const Mesh &mesh, ReadAttributePtr attribute)
static ReadAttributePtr adapt_mesh_domain_corner_to_face(const Mesh &mesh, ReadAttributePtr attribute)
std::unique_ptr< WriteAttribute > WriteAttributePtr
static ReadAttributePtr adapt_mesh_domain_point_to_edge(const Mesh &mesh, ReadAttributePtr attribute)
static ReadAttributePtr make_derived_read_attribute(const void *data, const int domain_size)
static void set_loop_uv(MLoopUV &uv, const float2 &co)
static ReadAttributePtr adapt_mesh_domain_point_to_corner(const Mesh &mesh, ReadAttributePtr attribute)
static ReadAttributePtr adapt_mesh_domain_corner_to_edge(const Mesh &mesh, ReadAttributePtr attribute)
static void adapt_mesh_domain_edge_to_face_impl(const Mesh &mesh, const Span< T > old_values, MutableSpan< T > r_values)
static void tag_normals_dirty_when_writing_position(GeometryComponent &component)
static void set_loop_color(MLoopCol &col, const Color4f &linear_color)
void adapt_mesh_domain_face_to_corner_impl(const Mesh &mesh, const Span< T > old_values, MutableSpan< T > r_values)
static ComponentAttributeProviders create_attribute_providers_for_mesh()
static void set_vertex_position(MVert &vert, const float3 &position)
std::unique_ptr< ReadAttribute > ReadAttributePtr
static void adapt_mesh_domain_corner_to_edge_impl(const Mesh &mesh, Span< T > old_values, MutableSpan< T > r_values)
void adapt_mesh_domain_face_to_edge_impl(const Mesh &mesh, const Span< T > old_values, MutableSpan< T > r_values)
static void set_shade_smooth(MPoly &mpoly, const bool &value)
static ReadAttributePtr adapt_mesh_domain_edge_to_face(const Mesh &mesh, ReadAttributePtr attribute)
static float2 get_loop_uv(const MLoopUV &uv)
static void adapt_mesh_domain_corner_to_face_impl(const Mesh &mesh, Span< T > old_values, MutableSpan< T > r_values)
static void adapt_mesh_domain_point_to_edge_impl(const Mesh &mesh, const Span< T > old_values, MutableSpan< T > r_values)
static int get_material_index(const MPoly &mpoly)
static void set_material_index(MPoly &mpoly, const int &index)
__int64 int64_t
Definition: stdint.h:92
Definition: DNA_ID.h:273
struct MDeformWeight * dw
unsigned int v1
unsigned int v2
unsigned int e
unsigned int v
short mat_nr
float co[3]
int64_t cd_dirty_poly
int64_t cd_dirty_vert
struct MEdge * medge
struct MVert * mvert
struct MDeformVert * dvert
int totedge
int totvert
struct MLoop * mloop
Mesh_Runtime runtime
int totpoly
int totloop
struct MPoly * mpoly
ccl_device_inline int clamp(int a, int mn, int mx)
Definition: util_math.h:283