Blender V4.3
mesh_legacy_convert.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
11#define DNA_DEPRECATED_ALLOW
12
13#include "MEM_guardedalloc.h"
14
15#include "DNA_mesh_types.h"
16#include "DNA_meshdata_types.h"
17#include "DNA_object_types.h"
18
19#include "BLI_array_utils.hh"
20#include "BLI_listbase.h"
21#include "BLI_map.hh"
22#include "BLI_math_geom.h"
23#include "BLI_math_matrix.h"
24#include "BLI_math_rotation.h"
26#include "BLI_memarena.h"
28#include "BLI_polyfill_2d.h"
29#include "BLI_string.h"
30#include "BLI_task.hh"
31#include "BLI_utildefines.h"
32
33#include "BKE_attribute.hh"
34#include "BKE_customdata.hh"
35#include "BKE_global.hh"
36#include "BKE_idprop.hh"
37#include "BKE_lib_id.hh"
38#include "BKE_main.hh"
39#include "BKE_main_namemap.hh"
40#include "BKE_mesh.hh"
42#include "BKE_modifier.hh"
43#include "BKE_multires.hh"
44#include "BKE_node.hh"
45#include "BKE_node_runtime.hh"
47
48#include "BLT_translation.hh"
49
51using blender::Span;
52
53/* -------------------------------------------------------------------- */
57struct EdgeSort {
60};
61
62/* edges have to be added with lowest index first for sorting */
63static void to_edgesort(EdgeSort *ed, uint v1, uint v2, char is_loose, short is_draw)
64{
65 if (v1 < v2) {
66 ed->v1 = v1;
67 ed->v2 = v2;
68 }
69 else {
70 ed->v1 = v2;
71 ed->v2 = v1;
72 }
73 ed->is_loose = is_loose;
74 ed->is_draw = is_draw;
75}
76
77static int vergedgesort(const void *v1, const void *v2)
78{
79 const EdgeSort *x1 = static_cast<const EdgeSort *>(v1);
80 const EdgeSort *x2 = static_cast<const EdgeSort *>(v2);
81
82 if (x1->v1 > x2->v1) {
83 return 1;
84 }
85 if (x1->v1 < x2->v1) {
86 return -1;
87 }
88 if (x1->v2 > x2->v2) {
89 return 1;
90 }
91 if (x1->v2 < x2->v2) {
92 return -1;
93 }
94
95 return 0;
96}
97
98/* Create edges based on known verts and faces,
99 * this function is only used when loading very old blend files */
100static void mesh_calc_edges_mdata(const MVert * /*allvert*/,
101 const MFace *allface,
102 MLoop *allloop,
103 const MPoly *allpoly,
104 int /*totvert*/,
105 int totface,
106 int /*totloop*/,
107 int faces_num,
108 MEdge **r_medge,
109 int *r_totedge)
110{
111 const MPoly *mpoly;
112 const MFace *mface;
113 MEdge *edges, *edge;
114 EdgeSort *edsort, *ed;
115 int a, totedge = 0;
116 uint totedge_final = 0;
117 uint edge_index;
118
119 /* we put all edges in array, sort them, and detect doubles that way */
120
121 for (a = totface, mface = allface; a > 0; a--, mface++) {
122 if (mface->v4) {
123 totedge += 4;
124 }
125 else if (mface->v3) {
126 totedge += 3;
127 }
128 else {
129 totedge += 1;
130 }
131 }
132
133 if (totedge == 0) {
134 *r_medge = nullptr;
135 *r_totedge = 0;
136 return;
137 }
138
139 ed = edsort = (EdgeSort *)MEM_mallocN(totedge * sizeof(EdgeSort), "EdgeSort");
140
141 for (a = totface, mface = allface; a > 0; a--, mface++) {
142 to_edgesort(ed++, mface->v1, mface->v2, !mface->v3, mface->edcode & ME_V1V2);
143 if (mface->v4) {
144 to_edgesort(ed++, mface->v2, mface->v3, 0, mface->edcode & ME_V2V3);
145 to_edgesort(ed++, mface->v3, mface->v4, 0, mface->edcode & ME_V3V4);
146 to_edgesort(ed++, mface->v4, mface->v1, 0, mface->edcode & ME_V4V1);
147 }
148 else if (mface->v3) {
149 to_edgesort(ed++, mface->v2, mface->v3, 0, mface->edcode & ME_V2V3);
150 to_edgesort(ed++, mface->v3, mface->v1, 0, mface->edcode & ME_V3V1);
151 }
152 }
153
154 qsort(edsort, totedge, sizeof(EdgeSort), vergedgesort);
155
156 /* count final amount */
157 for (a = totedge, ed = edsort; a > 1; a--, ed++) {
158 /* edge is unique when it differs from next edge, or is last */
159 if (ed->v1 != (ed + 1)->v1 || ed->v2 != (ed + 1)->v2) {
160 totedge_final++;
161 }
162 }
163 totedge_final++;
164
165 edges = (MEdge *)MEM_callocN(sizeof(MEdge) * totedge_final, __func__);
166
167 for (a = totedge, edge = edges, ed = edsort; a > 1; a--, ed++) {
168 /* edge is unique when it differs from next edge, or is last */
169 if (ed->v1 != (ed + 1)->v1 || ed->v2 != (ed + 1)->v2) {
170 edge->v1 = ed->v1;
171 edge->v2 = ed->v2;
172
173 /* order is swapped so extruding this edge as a surface won't flip face normals
174 * with cyclic curves */
175 if (ed->v1 + 1 != ed->v2) {
176 std::swap(edge->v1, edge->v2);
177 }
178 edge++;
179 }
180 else {
181 /* Equal edge, merge the draw-flag. */
182 (ed + 1)->is_draw |= ed->is_draw;
183 }
184 }
185 /* last edge */
186 edge->v1 = ed->v1;
187 edge->v2 = ed->v2;
188
189 MEM_freeN(edsort);
190
191 /* set edge members of mloops */
193 hash.reserve(totedge_final);
194 for (edge_index = 0, edge = edges; edge_index < totedge_final; edge_index++, edge++) {
195 hash.add({edge->v1, edge->v2}, edge_index);
196 }
197
198 mpoly = allpoly;
199 for (a = 0; a < faces_num; a++, mpoly++) {
200 MLoop *ml, *ml_next;
201 int i = mpoly->totloop;
202
203 ml_next = allloop + mpoly->loopstart; /* first loop */
204 ml = &ml_next[i - 1]; /* last loop */
205
206 while (i-- != 0) {
207 ml->e = hash.lookup({ml->v, ml_next->v});
208 ml = ml_next;
209 ml_next++;
210 }
211 }
212
213 BLI_assert(totedge_final > 0);
214 *r_medge = edges;
215 *r_totedge = totedge_final;
216}
217
219{
220 using namespace blender;
221 MEdge *edges;
222 int totedge = 0;
223 const Span<MVert> verts(
224 static_cast<const MVert *>(CustomData_get_layer(&mesh->vert_data, CD_MVERT)),
225 mesh->verts_num);
226
228 verts.data(),
229 mesh->mface,
230 static_cast<MLoop *>(
231 CustomData_get_layer_for_write(&mesh->corner_data, CD_MLOOP, mesh->corners_num)),
232 static_cast<const MPoly *>(CustomData_get_layer(&mesh->face_data, CD_MPOLY)),
233 verts.size(),
234 mesh->totface_legacy,
235 mesh->corners_num,
236 mesh->faces_num,
237 &edges,
238 &totedge);
239
240 if (totedge == 0) {
241 BLI_assert(edges == nullptr);
242 mesh->edges_num = 0;
243 return;
244 }
245
246 edges = (MEdge *)CustomData_add_layer_with_data(
247 &mesh->edge_data, CD_MEDGE, edges, totedge, nullptr);
248 mesh->edges_num = totedge;
249
250 mesh->tag_topology_changed();
252}
253
255{
256 /* NOTE: We need to keep this for edge creation (for now?), and some old `readfile.cc` code. */
257 MFace *f;
258 int a, b;
259 MFace *mfaces = mesh->mface;
260
261 for (a = b = 0, f = mfaces; a < mesh->totface_legacy; a++, f++) {
262 if (f->v3) {
263 if (a != b) {
264 memcpy(&mfaces[b], f, sizeof(mfaces[b]));
265 CustomData_copy_data(&mesh->fdata_legacy, &mesh->fdata_legacy, a, b, 1);
266 }
267 b++;
268 }
269 }
270 if (a != b) {
271 CustomData_free_elem(&mesh->fdata_legacy, b, a - b);
272 mesh->totface_legacy = b;
273 }
274}
275
278/* -------------------------------------------------------------------- */
283{
284 using namespace blender;
285 if (UNLIKELY(mesh->cd_flag)) {
286 return;
287 }
288
289 const Span<MVert> verts(
290 static_cast<const MVert *>(CustomData_get_layer(&mesh->vert_data, CD_MVERT)),
291 mesh->verts_num);
292 const Span<MEdge> edges(
293 static_cast<const MEdge *>(CustomData_get_layer(&mesh->edge_data, CD_MEDGE)),
294 mesh->edges_num);
295
296 for (const MVert &vert : verts) {
297 if (vert.bweight_legacy != 0) {
298 mesh->cd_flag |= ME_CDFLAG_VERT_BWEIGHT;
299 break;
300 }
301 }
302
303 for (const MEdge &edge : edges) {
304 if (edge.bweight_legacy != 0) {
305 mesh->cd_flag |= ME_CDFLAG_EDGE_BWEIGHT;
306 if (mesh->cd_flag & ME_CDFLAG_EDGE_CREASE) {
307 break;
308 }
309 }
310 if (edge.crease_legacy != 0) {
311 mesh->cd_flag |= ME_CDFLAG_EDGE_CREASE;
312 if (mesh->cd_flag & ME_CDFLAG_EDGE_BWEIGHT) {
313 break;
314 }
315 }
316 }
317}
318
321/* -------------------------------------------------------------------- */
325#define MESH_MLOOPCOL_FROM_MCOL(_mloopcol, _mcol) \
326 { \
327 MLoopCol *mloopcol__tmp = _mloopcol; \
328 const MCol *mcol__tmp = _mcol; \
329 mloopcol__tmp->r = mcol__tmp->b; \
330 mloopcol__tmp->g = mcol__tmp->g; \
331 mloopcol__tmp->b = mcol__tmp->r; \
332 mloopcol__tmp->a = mcol__tmp->a; \
333 } \
334 (void)0
335
337 CustomData *fdata_legacy,
338 const int totface,
339 CustomData *ldata,
340 MFace *mface,
341 int totloop,
342 int findex,
343 int loopstart,
344 int numTex,
345 int numCol)
346{
347 MFace *mf = mface + findex;
348
349 for (int i = 0; i < numTex; i++) {
350 const MTFace *texface = (const MTFace *)CustomData_get_n_for_write(
351 fdata_legacy, CD_MTFACE, findex, i, totface);
352
353 blender::float2 *uv = static_cast<blender::float2 *>(
354 CustomData_get_n_for_write(ldata, CD_PROP_FLOAT2, loopstart, i, totloop));
355 copy_v2_v2(*uv, texface->uv[0]);
356 uv++;
357 copy_v2_v2(*uv, texface->uv[1]);
358 uv++;
359 copy_v2_v2(*uv, texface->uv[2]);
360 uv++;
361
362 if (mf->v4) {
363 copy_v2_v2(*uv, texface->uv[3]);
364 uv++;
365 }
366 }
367
368 for (int i = 0; i < numCol; i++) {
370 ldata, CD_PROP_BYTE_COLOR, loopstart, i, totloop);
371 const MCol *mcol = (const MCol *)CustomData_get_n_for_write(
372 fdata_legacy, CD_MCOL, findex, i, totface);
373
374 MESH_MLOOPCOL_FROM_MCOL(mloopcol, &mcol[0]);
375 mloopcol++;
376 MESH_MLOOPCOL_FROM_MCOL(mloopcol, &mcol[1]);
377 mloopcol++;
378 MESH_MLOOPCOL_FROM_MCOL(mloopcol, &mcol[2]);
379 mloopcol++;
380 if (mf->v4) {
381 MESH_MLOOPCOL_FROM_MCOL(mloopcol, &mcol[3]);
382 mloopcol++;
383 }
384 }
385
386 if (CustomData_has_layer(fdata_legacy, CD_TESSLOOPNORMAL)) {
387 float(*loop_normals)[3] = (float(*)[3])CustomData_get_for_write(
388 ldata, loopstart, CD_NORMAL, totloop);
389 const short(*tessloop_normals)[3] = (short(*)[3])CustomData_get_for_write(
390 fdata_legacy, findex, CD_TESSLOOPNORMAL, totface);
391 const int max = mf->v4 ? 4 : 3;
392
393 for (int i = 0; i < max; i++, loop_normals++, tessloop_normals++) {
394 normal_short_to_float_v3(*loop_normals, *tessloop_normals);
395 }
396 }
397
398 if (CustomData_has_layer(fdata_legacy, CD_MDISPS)) {
399 MDisps *ld = (MDisps *)CustomData_get_for_write(ldata, loopstart, CD_MDISPS, totloop);
400 const MDisps *fd = (const MDisps *)CustomData_get_for_write(
401 fdata_legacy, findex, CD_MDISPS, totface);
402 const float(*disps)[3] = fd->disps;
403 int tot = mf->v4 ? 4 : 3;
404 int corners;
405
406 if (CustomData_external_test(fdata_legacy, CD_MDISPS)) {
407 if (id && fdata_legacy->external) {
408 CustomData_external_add(ldata, id, CD_MDISPS, totloop, fdata_legacy->external->filepath);
409 }
410 }
411
412 corners = multires_mdisp_corners(fd);
413
414 if (corners == 0) {
415 /* Empty #MDisp layers appear in at least one of the `sintel.blend` files.
416 * Not sure why this happens, but it seems fine to just ignore them here.
417 * If `corners == 0` for a non-empty layer though, something went wrong. */
418 BLI_assert(fd->totdisp == 0);
419 }
420 else {
421 const int side = int(sqrtf(float(fd->totdisp / corners)));
422 const int side_sq = side * side;
423
424 for (int i = 0; i < tot; i++, disps += side_sq, ld++) {
425 ld->totdisp = side_sq;
426 ld->level = int(logf(float(side) - 1.0f) / float(M_LN2)) + 1;
427
428 if (ld->disps) {
429 MEM_freeN(ld->disps);
430 }
431
432 ld->disps = (float(*)[3])MEM_malloc_arrayN(
433 size_t(side_sq), sizeof(float[3]), "converted loop mdisps");
434 if (fd->disps) {
435 memcpy(ld->disps, disps, size_t(side_sq) * sizeof(float[3]));
436 }
437 else {
438 memset(ld->disps, 0, size_t(side_sq) * sizeof(float[3]));
439 }
440 }
441 }
442 }
443}
444
445static void CustomData_to_bmeshpoly(CustomData *fdata_legacy, CustomData *ldata, int totloop)
446{
447 for (int i = 0; i < fdata_legacy->totlayer; i++) {
448 if (fdata_legacy->layers[i].type == CD_MTFACE) {
450 ldata, CD_PROP_FLOAT2, CD_SET_DEFAULT, totloop, fdata_legacy->layers[i].name);
451 }
452 else if (fdata_legacy->layers[i].type == CD_MCOL) {
454 ldata, CD_PROP_BYTE_COLOR, CD_SET_DEFAULT, totloop, fdata_legacy->layers[i].name);
455 }
456 else if (fdata_legacy->layers[i].type == CD_MDISPS) {
458 ldata, CD_MDISPS, CD_SET_DEFAULT, totloop, fdata_legacy->layers[i].name);
459 }
460 else if (fdata_legacy->layers[i].type == CD_TESSLOOPNORMAL) {
462 ldata, CD_NORMAL, CD_SET_DEFAULT, totloop, fdata_legacy->layers[i].name);
463 }
464 }
465}
466
468 CustomData *fdata_legacy,
469 CustomData *ldata,
470 CustomData *pdata,
471 int totedge_i,
472 int totface_i,
473 int totloop_i,
474 int faces_num_i,
475 blender::int2 *edges,
476 MFace *mface,
477 int *r_totloop,
478 int *r_faces_num)
479{
480 MFace *mf;
481 MLoop *ml, *mloop;
482 MPoly *poly, *mpoly;
483 int numTex, numCol;
484 int i, j, totloop, faces_num, *polyindex;
485
486 /* just in case some of these layers are filled in (can happen with python created meshes) */
487 CustomData_free(ldata, totloop_i);
488 CustomData_free(pdata, faces_num_i);
489
490 faces_num = totface_i;
491 mpoly = (MPoly *)CustomData_add_layer(pdata, CD_MPOLY, CD_SET_DEFAULT, faces_num);
492 int *material_indices = static_cast<int *>(
493 CustomData_get_layer_named_for_write(pdata, CD_PROP_INT32, "material_index", faces_num));
494 if (material_indices == nullptr) {
495 material_indices = static_cast<int *>(CustomData_add_layer_named(
496 pdata, CD_PROP_INT32, CD_SET_DEFAULT, faces_num, "material_index"));
497 }
498 bool *sharp_faces = static_cast<bool *>(
499 CustomData_get_layer_named_for_write(pdata, CD_PROP_BOOL, "sharp_face", faces_num));
500 if (!sharp_faces) {
501 sharp_faces = static_cast<bool *>(
502 CustomData_add_layer_named(pdata, CD_PROP_BOOL, CD_SET_DEFAULT, faces_num, "sharp_face"));
503 }
504
505 numTex = CustomData_number_of_layers(fdata_legacy, CD_MTFACE);
506 numCol = CustomData_number_of_layers(fdata_legacy, CD_MCOL);
507
508 totloop = 0;
509 mf = mface;
510 for (i = 0; i < totface_i; i++, mf++) {
511 totloop += mf->v4 ? 4 : 3;
512 }
513
514 mloop = (MLoop *)CustomData_add_layer(ldata, CD_MLOOP, CD_SET_DEFAULT, totloop);
515
516 CustomData_to_bmeshpoly(fdata_legacy, ldata, totloop);
517
518 if (id) {
519 /* ensure external data is transferred */
520 /* TODO(sergey): Use multiresModifier_ensure_external_read(). */
521 CustomData_external_read(fdata_legacy, id, CD_MASK_MDISPS, totface_i);
522 }
523
525 eh.reserve(totedge_i);
526
527 /* build edge hash */
528 for (i = 0; i < totedge_i; i++) {
529 eh.add(edges[i], i);
530 }
531
532 polyindex = (int *)CustomData_get_layer(fdata_legacy, CD_ORIGINDEX);
533
534 j = 0; /* current loop index */
535 ml = mloop;
536 mf = mface;
537 poly = mpoly;
538 for (i = 0; i < totface_i; i++, mf++, poly++) {
539 poly->loopstart = j;
540
541 poly->totloop = mf->v4 ? 4 : 3;
542
543 material_indices[i] = mf->mat_nr;
544 sharp_faces[i] = (mf->flag & ME_SMOOTH) == 0;
545
546#define ML(v1, v2) \
547 { \
548 ml->v = mf->v1; \
549 ml->e = eh.lookup({mf->v1, mf->v2}); \
550 ml++; \
551 j++; \
552 } \
553 (void)0
554
555 ML(v1, v2);
556 ML(v2, v3);
557 if (mf->v4) {
558 ML(v3, v4);
559 ML(v4, v1);
560 }
561 else {
562 ML(v3, v1);
563 }
564
565#undef ML
566
568 id, fdata_legacy, totface_i, ldata, mface, totloop, i, poly->loopstart, numTex, numCol);
569
570 if (polyindex) {
571 *polyindex = i;
572 polyindex++;
573 }
574 }
575
576 /* NOTE: we don't convert NGons at all, these are not even real ngons,
577 * they have their own UVs, colors etc - it's more an editing feature. */
578
579 *r_faces_num = faces_num;
580 *r_totloop = totloop;
581}
582
583static void update_active_fdata_layers(Mesh &mesh, CustomData *fdata_legacy, CustomData *ldata)
584{
585 int act;
586
589 CustomData_set_layer_active(fdata_legacy, CD_MTFACE, act);
590
592 CustomData_set_layer_render(fdata_legacy, CD_MTFACE, act);
593
595 CustomData_set_layer_clone(fdata_legacy, CD_MTFACE, act);
596
598 CustomData_set_layer_stencil(fdata_legacy, CD_MTFACE, act);
599 }
600
602 if (mesh.active_color_attribute != nullptr) {
603 act = CustomData_get_named_layer(ldata, CD_PROP_BYTE_COLOR, mesh.active_color_attribute);
604 CustomData_set_layer_active(fdata_legacy, CD_MCOL, act);
605 }
606
607 if (mesh.default_color_attribute != nullptr) {
608 act = CustomData_get_named_layer(ldata, CD_PROP_BYTE_COLOR, mesh.default_color_attribute);
609 CustomData_set_layer_render(fdata_legacy, CD_MCOL, act);
610 }
611
613 CustomData_set_layer_clone(fdata_legacy, CD_MCOL, act);
614
616 CustomData_set_layer_stencil(fdata_legacy, CD_MCOL, act);
617 }
618}
619
620#ifndef NDEBUG
628 CustomData *ldata,
629 bool fallback)
630{
631 int a_num = 0, b_num = 0;
632# define LAYER_CMP(l_a, t_a, l_b, t_b) \
633 ((a_num += CustomData_number_of_layers(l_a, t_a)) == \
634 (b_num += CustomData_number_of_layers(l_b, t_b)))
635
636 if (!LAYER_CMP(ldata, CD_PROP_FLOAT2, fdata_legacy, CD_MTFACE)) {
637 return false;
638 }
639 if (!LAYER_CMP(ldata, CD_PROP_BYTE_COLOR, fdata_legacy, CD_MCOL)) {
640 return false;
641 }
642 if (!LAYER_CMP(ldata, CD_ORIGSPACE_MLOOP, fdata_legacy, CD_ORIGSPACE)) {
643 return false;
644 }
645 if (!LAYER_CMP(ldata, CD_NORMAL, fdata_legacy, CD_TESSLOOPNORMAL)) {
646 return false;
647 }
648 if (!LAYER_CMP(ldata, CD_TANGENT, fdata_legacy, CD_TANGENT)) {
649 return false;
650 }
651
652# undef LAYER_CMP
653
654 /* if no layers are on either CustomData's,
655 * then there was nothing to do... */
656 return a_num ? true : fallback;
657}
658#endif /* !NDEBUG */
659
660static void add_mface_layers(Mesh &mesh, CustomData *fdata_legacy, CustomData *ldata, int total)
661{
662 /* avoid accumulating extra layers */
663 BLI_assert(!check_matching_legacy_layer_counts(fdata_legacy, ldata, false));
664
665 for (int i = 0; i < ldata->totlayer; i++) {
666 if (ldata->layers[i].type == CD_PROP_FLOAT2) {
668 fdata_legacy, CD_MTFACE, CD_SET_DEFAULT, total, ldata->layers[i].name);
669 }
670 if (ldata->layers[i].type == CD_PROP_BYTE_COLOR) {
672 fdata_legacy, CD_MCOL, CD_SET_DEFAULT, total, ldata->layers[i].name);
673 }
674 else if (ldata->layers[i].type == CD_ORIGSPACE_MLOOP) {
676 fdata_legacy, CD_ORIGSPACE, CD_SET_DEFAULT, total, ldata->layers[i].name);
677 }
678 else if (ldata->layers[i].type == CD_NORMAL) {
680 fdata_legacy, CD_TESSLOOPNORMAL, CD_SET_DEFAULT, total, ldata->layers[i].name);
681 }
682 else if (ldata->layers[i].type == CD_TANGENT) {
684 fdata_legacy, CD_TANGENT, CD_SET_DEFAULT, total, ldata->layers[i].name);
685 }
686 }
687
688 update_active_fdata_layers(mesh, fdata_legacy, ldata);
689}
690
692{
693 if (UNLIKELY((mesh->totface_legacy != 0) && (mesh->faces_num == 0))) {
694 /* Pass, otherwise this function clears 'mface' before
695 * versioning 'mface -> mpoly' code kicks in #30583.
696 *
697 * Callers could also check but safer to do here - campbell */
698 }
699 else {
700 const int tottex_original = CustomData_number_of_layers(&mesh->corner_data, CD_PROP_FLOAT2);
701 const int totcol_original = CustomData_number_of_layers(&mesh->corner_data,
703
704 const int tottex_tessface = CustomData_number_of_layers(&mesh->fdata_legacy, CD_MTFACE);
705 const int totcol_tessface = CustomData_number_of_layers(&mesh->fdata_legacy, CD_MCOL);
706
707 if (tottex_tessface != tottex_original || totcol_tessface != totcol_original) {
709
710 add_mface_layers(*mesh, &mesh->fdata_legacy, &mesh->corner_data, mesh->totface_legacy);
711
712 /* TODO: add some `--debug-mesh` option. */
713 if (G.debug & G_DEBUG) {
714 /* NOTE(campbell): this warning may be un-called for if we are initializing the mesh for
715 * the first time from #BMesh, rather than giving a warning about this we could be smarter
716 * and check if there was any data to begin with, for now just print the warning with
717 * some info to help troubleshoot what's going on. */
718 printf(
719 "%s: warning! Tessellation uvs or vcol data got out of sync, "
720 "had to reset!\n CD_MTFACE: %d != CD_PROP_FLOAT2: %d || CD_MCOL: %d != "
721 "CD_PROP_BYTE_COLOR: "
722 "%d\n",
723 __func__,
724 tottex_tessface,
725 tottex_original,
726 totcol_tessface,
727 totcol_original);
728 }
729 }
730 }
731}
732
734{
735 convert_mfaces_to_mpolys(&mesh->id,
736 &mesh->fdata_legacy,
737 &mesh->corner_data,
738 &mesh->face_data,
739 mesh->edges_num,
740 mesh->totface_legacy,
741 mesh->corners_num,
742 mesh->faces_num,
743 mesh->edges_for_write().data(),
744 (MFace *)CustomData_get_layer(&mesh->fdata_legacy, CD_MFACE),
745 &mesh->corners_num,
746 &mesh->faces_num);
749
751}
752
760 CustomData *corner_data)
761{
762 int act;
763
764 if (CustomData_has_layer(fdata_legacy, CD_MTFACE)) {
765 act = CustomData_get_active_layer(fdata_legacy, CD_MTFACE);
767
768 act = CustomData_get_render_layer(fdata_legacy, CD_MTFACE);
770
771 act = CustomData_get_clone_layer(fdata_legacy, CD_MTFACE);
773
774 act = CustomData_get_stencil_layer(fdata_legacy, CD_MTFACE);
776 }
777
778 if (CustomData_has_layer(fdata_legacy, CD_MCOL)) {
779 act = CustomData_get_active_layer(fdata_legacy, CD_MCOL);
781
782 act = CustomData_get_render_layer(fdata_legacy, CD_MCOL);
784
785 act = CustomData_get_clone_layer(fdata_legacy, CD_MCOL);
787
788 act = CustomData_get_stencil_layer(fdata_legacy, CD_MCOL);
790 }
791}
792
794{
795 convert_mfaces_to_mpolys(&mesh->id,
796 &mesh->fdata_legacy,
797 &mesh->corner_data,
798 &mesh->face_data,
799 mesh->edges_num,
800 mesh->totface_legacy,
801 mesh->corners_num,
802 mesh->faces_num,
803 mesh->edges_for_write().data(),
804 (MFace *)CustomData_get_layer(&mesh->fdata_legacy, CD_MFACE),
805 &mesh->corners_num,
806 &mesh->faces_num);
809
810 CustomData_bmesh_do_versions_update_active_layers(&mesh->fdata_legacy, &mesh->corner_data);
811
813}
814
817/* -------------------------------------------------------------------- */
823#define MESH_MLOOPCOL_TO_MCOL(_mloopcol, _mcol) \
824 { \
825 const MLoopCol *mloopcol__tmp = _mloopcol; \
826 MCol *mcol__tmp = _mcol; \
827 mcol__tmp->b = mloopcol__tmp->r; \
828 mcol__tmp->g = mloopcol__tmp->g; \
829 mcol__tmp->r = mloopcol__tmp->b; \
830 mcol__tmp->a = mloopcol__tmp->a; \
831 } \
832 (void)0
833
843static void mesh_loops_to_tessdata(CustomData *fdata_legacy,
844 CustomData *corner_data,
845 MFace *mface,
846 const int *polyindices,
847 uint (*loopindices)[4],
848 const int num_faces)
849{
850 /* NOTE(mont29): performances are sub-optimal when we get a null #MFace,
851 * we could be ~25% quicker with dedicated code.
852 * The issue is, unless having two different functions with nearly the same code,
853 * there's not much ways to solve this. Better IMHO to live with it for now (sigh). */
854 const int numUV = CustomData_number_of_layers(corner_data, CD_PROP_FLOAT2);
855 const int numCol = CustomData_number_of_layers(corner_data, CD_PROP_BYTE_COLOR);
856 const bool hasOrigSpace = CustomData_has_layer(corner_data, CD_ORIGSPACE_MLOOP);
857 const bool hasLoopNormal = CustomData_has_layer(corner_data, CD_NORMAL);
858 const bool hasLoopTangent = CustomData_has_layer(corner_data, CD_TANGENT);
859 int findex, i, j;
860 const int *pidx;
861 uint(*lidx)[4];
862
863 for (i = 0; i < numUV; i++) {
865 fdata_legacy, CD_MTFACE, i, num_faces);
866 const blender::float2 *uv = static_cast<const blender::float2 *>(
867 CustomData_get_layer_n(corner_data, CD_PROP_FLOAT2, i));
868
869 for (findex = 0, pidx = polyindices, lidx = loopindices; findex < num_faces;
870 pidx++, lidx++, findex++, texface++)
871 {
872 for (j = (mface ? mface[findex].v4 : (*lidx)[3]) ? 4 : 3; j--;) {
873 copy_v2_v2(texface->uv[j], uv[(*lidx)[j]]);
874 }
875 }
876 }
877
878 for (i = 0; i < numCol; i++) {
879 MCol(*mcol)[4] = (MCol(*)[4])CustomData_get_layer_n_for_write(
880 fdata_legacy, CD_MCOL, i, num_faces);
881 const MLoopCol *mloopcol = (const MLoopCol *)CustomData_get_layer_n(
882 corner_data, CD_PROP_BYTE_COLOR, i);
883
884 for (findex = 0, lidx = loopindices; findex < num_faces; lidx++, findex++, mcol++) {
885 for (j = (mface ? mface[findex].v4 : (*lidx)[3]) ? 4 : 3; j--;) {
886 MESH_MLOOPCOL_TO_MCOL(&mloopcol[(*lidx)[j]], &(*mcol)[j]);
887 }
888 }
889 }
890
891 if (hasOrigSpace) {
893 const OrigSpaceLoop *lof = (const OrigSpaceLoop *)CustomData_get_layer(corner_data,
895
896 for (findex = 0, lidx = loopindices; findex < num_faces; lidx++, findex++, of++) {
897 for (j = (mface ? mface[findex].v4 : (*lidx)[3]) ? 4 : 3; j--;) {
898 copy_v2_v2(of->uv[j], lof[(*lidx)[j]].uv);
899 }
900 }
901 }
902
903 if (hasLoopNormal) {
904 short(*face_normals)[4][3] = (short(*)[4][3])CustomData_get_layer(fdata_legacy,
906 const float(*loop_normals)[3] = (const float(*)[3])CustomData_get_layer(corner_data,
907 CD_NORMAL);
908
909 for (findex = 0, lidx = loopindices; findex < num_faces; lidx++, findex++, face_normals++) {
910 for (j = (mface ? mface[findex].v4 : (*lidx)[3]) ? 4 : 3; j--;) {
911 normal_float_to_short_v3((*face_normals)[j], loop_normals[(*lidx)[j]]);
912 }
913 }
914 }
915
916 if (hasLoopTangent) {
917 /* Need to do for all UV maps at some point. */
918 float(*ftangents)[4] = (float(*)[4])CustomData_get_layer(fdata_legacy, CD_TANGENT);
919 const float(*ltangents)[4] = (const float(*)[4])CustomData_get_layer(corner_data, CD_TANGENT);
920
921 for (findex = 0, pidx = polyindices, lidx = loopindices; findex < num_faces;
922 pidx++, lidx++, findex++)
923 {
924 int nverts = (mface ? mface[findex].v4 : (*lidx)[3]) ? 4 : 3;
925 for (j = nverts; j--;) {
926 copy_v4_v4(ftangents[findex * 4 + j], ltangents[(*lidx)[j]]);
927 }
928 }
929 }
930}
931
932int BKE_mesh_mface_index_validate(MFace *mface, CustomData *fdata_legacy, int mfindex, int nr)
933{
934 /* first test if the face is legal */
935 if ((mface->v3 || nr == 4) && mface->v3 == mface->v4) {
936 mface->v4 = 0;
937 nr--;
938 }
939 if ((mface->v2 || mface->v4) && mface->v2 == mface->v3) {
940 mface->v3 = mface->v4;
941 mface->v4 = 0;
942 nr--;
943 }
944 if (mface->v1 == mface->v2) {
945 mface->v2 = mface->v3;
946 mface->v3 = mface->v4;
947 mface->v4 = 0;
948 nr--;
949 }
950
951 /* Check corrupt cases, bow-tie geometry,
952 * can't handle these because edge data won't exist so just return 0. */
953 if (nr == 3) {
954 if (
955 /* real edges */
956 mface->v1 == mface->v2 || mface->v2 == mface->v3 || mface->v3 == mface->v1)
957 {
958 return 0;
959 }
960 }
961 else if (nr == 4) {
962 if (
963 /* real edges */
964 mface->v1 == mface->v2 || mface->v2 == mface->v3 || mface->v3 == mface->v4 ||
965 mface->v4 == mface->v1 ||
966 /* across the face */
967 mface->v1 == mface->v3 || mface->v2 == mface->v4)
968 {
969 return 0;
970 }
971 }
972
973 /* prevent a zero at wrong index location */
974 if (nr == 3) {
975 if (mface->v3 == 0) {
976 static int corner_indices[4] = {1, 2, 0, 3};
977
978 std::swap(mface->v1, mface->v2);
979 std::swap(mface->v2, mface->v3);
980
981 if (fdata_legacy) {
982 CustomData_swap_corners(fdata_legacy, mfindex, corner_indices);
983 }
984 }
985 }
986 else if (nr == 4) {
987 if (mface->v3 == 0 || mface->v4 == 0) {
988 static int corner_indices[4] = {2, 3, 0, 1};
989
990 std::swap(mface->v1, mface->v3);
991 std::swap(mface->v2, mface->v4);
992
993 if (fdata_legacy) {
994 CustomData_swap_corners(fdata_legacy, mfindex, corner_indices);
995 }
996 }
997 }
998
999 return nr;
1000}
1001
1002static int mesh_tessface_calc(Mesh &mesh,
1003 CustomData *fdata_legacy,
1004 CustomData *ldata,
1005 CustomData *pdata,
1006 float (*positions)[3],
1007 int totface,
1008 int totloop,
1009 int faces_num)
1010{
1011#define USE_TESSFACE_SPEEDUP
1012#define USE_TESSFACE_QUADS
1013
1014/* We abuse #MFace.edcode to tag quad faces. See below for details. */
1015#define TESSFACE_IS_QUAD 1
1016
1017 const int corner_tris_num = poly_to_tri_count(faces_num, totloop);
1018
1019 MFace *mface, *mf;
1020 MemArena *arena = nullptr;
1021 int *mface_to_poly_map;
1022 uint(*lindices)[4];
1023 int poly_index, mface_index;
1024 uint j;
1025
1026 const blender::OffsetIndices faces = mesh.faces();
1027 const Span<int> corner_verts = mesh.corner_verts();
1028 const int *material_indices = static_cast<const int *>(
1029 CustomData_get_layer_named(pdata, CD_PROP_INT32, "material_index"));
1030 const bool *sharp_faces = static_cast<const bool *>(
1031 CustomData_get_layer_named(pdata, CD_PROP_BOOL, "sharp_face"));
1032
1033 /* Allocate the length of `totfaces`, avoid many small reallocation's,
1034 * if all faces are triangles it will be correct, `quads == 2x` allocations. */
1035 /* Take care since memory is _not_ zeroed so be sure to initialize each field. */
1036 mface_to_poly_map = (int *)MEM_malloc_arrayN(
1037 size_t(corner_tris_num), sizeof(*mface_to_poly_map), __func__);
1038 mface = (MFace *)MEM_malloc_arrayN(size_t(corner_tris_num), sizeof(*mface), __func__);
1039 lindices = (uint(*)[4])MEM_malloc_arrayN(size_t(corner_tris_num), sizeof(*lindices), __func__);
1040
1041 mface_index = 0;
1042 for (poly_index = 0; poly_index < faces_num; poly_index++) {
1043 const uint mp_loopstart = uint(faces[poly_index].start());
1044 const uint mp_totloop = uint(faces[poly_index].size());
1045 uint l1, l2, l3, l4;
1046 uint *lidx;
1047 if (mp_totloop < 3) {
1048 /* Do nothing. */
1049 }
1050
1051#ifdef USE_TESSFACE_SPEEDUP
1052
1053# define ML_TO_MF(i1, i2, i3) \
1054 mface_to_poly_map[mface_index] = poly_index; \
1055 mf = &mface[mface_index]; \
1056 lidx = lindices[mface_index]; \
1057 /* Set loop indices, transformed to vert indices later. */ \
1058 l1 = mp_loopstart + i1; \
1059 l2 = mp_loopstart + i2; \
1060 l3 = mp_loopstart + i3; \
1061 mf->v1 = corner_verts[l1]; \
1062 mf->v2 = corner_verts[l2]; \
1063 mf->v3 = corner_verts[l3]; \
1064 mf->v4 = 0; \
1065 lidx[0] = l1; \
1066 lidx[1] = l2; \
1067 lidx[2] = l3; \
1068 lidx[3] = 0; \
1069 mf->mat_nr = material_indices ? material_indices[poly_index] : 0; \
1070 mf->flag = (sharp_faces && sharp_faces[poly_index]) ? 0 : ME_SMOOTH; \
1071 mf->edcode = 0; \
1072 (void)0
1073
1074/* ALMOST IDENTICAL TO DEFINE ABOVE (see EXCEPTION) */
1075# define ML_TO_MF_QUAD() \
1076 mface_to_poly_map[mface_index] = poly_index; \
1077 mf = &mface[mface_index]; \
1078 lidx = lindices[mface_index]; \
1079 /* Set loop indices, transformed to vert indices later. */ \
1080 l1 = mp_loopstart + 0; /* EXCEPTION */ \
1081 l2 = mp_loopstart + 1; /* EXCEPTION */ \
1082 l3 = mp_loopstart + 2; /* EXCEPTION */ \
1083 l4 = mp_loopstart + 3; /* EXCEPTION */ \
1084 mf->v1 = corner_verts[l1]; \
1085 mf->v2 = corner_verts[l2]; \
1086 mf->v3 = corner_verts[l3]; \
1087 mf->v4 = corner_verts[l4]; \
1088 lidx[0] = l1; \
1089 lidx[1] = l2; \
1090 lidx[2] = l3; \
1091 lidx[3] = l4; \
1092 mf->mat_nr = material_indices ? material_indices[poly_index] : 0; \
1093 mf->flag = (sharp_faces && sharp_faces[poly_index]) ? 0 : ME_SMOOTH; \
1094 mf->edcode = TESSFACE_IS_QUAD; \
1095 (void)0
1096
1097 else if (mp_totloop == 3) {
1098 ML_TO_MF(0, 1, 2);
1099 mface_index++;
1100 }
1101 else if (mp_totloop == 4) {
1102# ifdef USE_TESSFACE_QUADS
1103 ML_TO_MF_QUAD();
1104 mface_index++;
1105# else
1106 ML_TO_MF(0, 1, 2);
1107 mface_index++;
1108 ML_TO_MF(0, 2, 3);
1109 mface_index++;
1110# endif
1111 }
1112#endif /* USE_TESSFACE_SPEEDUP */
1113 else {
1114 const float *co_curr, *co_prev;
1115
1116 float normal[3];
1117
1118 float axis_mat[3][3];
1119 float(*projverts)[2];
1120 uint(*tris)[3];
1121
1122 const uint totfilltri = mp_totloop - 2;
1123
1124 if (UNLIKELY(arena == nullptr)) {
1125 arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
1126 }
1127
1128 tris = (uint(*)[3])BLI_memarena_alloc(arena, sizeof(*tris) * size_t(totfilltri));
1129 projverts = (float(*)[2])BLI_memarena_alloc(arena, sizeof(*projverts) * size_t(mp_totloop));
1130
1131 zero_v3(normal);
1132
1133 /* Calculate the normal, flipped: to get a positive 2D cross product. */
1134 co_prev = positions[corner_verts[mp_loopstart + mp_totloop - 1]];
1135 for (j = 0; j < mp_totloop; j++) {
1136 const int vert = corner_verts[mp_loopstart + j];
1137 co_curr = positions[vert];
1138 add_newell_cross_v3_v3v3(normal, co_prev, co_curr);
1139 co_prev = co_curr;
1140 }
1141 if (UNLIKELY(normalize_v3(normal) == 0.0f)) {
1142 normal[2] = 1.0f;
1143 }
1144
1145 /* Project verts to 2D. */
1146 axis_dominant_v3_to_m3_negate(axis_mat, normal);
1147
1148 for (j = 0; j < mp_totloop; j++) {
1149 const int vert = corner_verts[mp_loopstart + j];
1150 mul_v2_m3v3(projverts[j], axis_mat, positions[vert]);
1151 }
1152
1153 BLI_polyfill_calc_arena(projverts, mp_totloop, 1, tris, arena);
1154
1155 /* Apply fill. */
1156 for (j = 0; j < totfilltri; j++) {
1157 uint *tri = tris[j];
1158 lidx = lindices[mface_index];
1159
1160 mface_to_poly_map[mface_index] = poly_index;
1161 mf = &mface[mface_index];
1162
1163 /* Set loop indices, transformed to vert indices later. */
1164 l1 = mp_loopstart + tri[0];
1165 l2 = mp_loopstart + tri[1];
1166 l3 = mp_loopstart + tri[2];
1167
1168 mf->v1 = corner_verts[l1];
1169 mf->v2 = corner_verts[l2];
1170 mf->v3 = corner_verts[l3];
1171 mf->v4 = 0;
1172
1173 lidx[0] = l1;
1174 lidx[1] = l2;
1175 lidx[2] = l3;
1176 lidx[3] = 0;
1177
1178 mf->mat_nr = material_indices ? material_indices[poly_index] : 0;
1179 mf->edcode = 0;
1180
1181 mface_index++;
1182 }
1183
1184 BLI_memarena_clear(arena);
1185 }
1186 }
1187
1188 if (arena) {
1189 BLI_memarena_free(arena);
1190 arena = nullptr;
1191 }
1192
1193 CustomData_free(fdata_legacy, totface);
1194 totface = mface_index;
1195
1196 BLI_assert(totface <= corner_tris_num);
1197
1198 /* Not essential but without this we store over-allocated memory in the #CustomData layers. */
1199 if (LIKELY(corner_tris_num != totface)) {
1200 mface = (MFace *)MEM_reallocN(mface, sizeof(*mface) * size_t(totface));
1201 mface_to_poly_map = (int *)MEM_reallocN(mface_to_poly_map,
1202 sizeof(*mface_to_poly_map) * size_t(totface));
1203 }
1204
1205 CustomData_add_layer_with_data(fdata_legacy, CD_MFACE, mface, totface, nullptr);
1206
1207 /* #CD_ORIGINDEX will contain an array of indices from tessellation-faces to the polygons
1208 * they are directly tessellated from. */
1209 CustomData_add_layer_with_data(fdata_legacy, CD_ORIGINDEX, mface_to_poly_map, totface, nullptr);
1210 add_mface_layers(mesh, fdata_legacy, ldata, totface);
1211
1212 /* NOTE: quad detection issue - fourth vertex-index vs fourth loop-index:
1213 * Polygons take care of their loops ordering, hence not of their vertices ordering.
1214 * Currently, the #TFace fourth vertex index might be 0 even for a quad.
1215 * However, we know our fourth loop index is never 0 for quads
1216 * (because they are sorted for polygons, and our quads are still mere copies of their polygons).
1217 * So we pass nullptr as #MFace pointer, and #mesh_loops_to_tessdata
1218 * will use the fourth loop index as quad test. */
1219 mesh_loops_to_tessdata(fdata_legacy, ldata, nullptr, mface_to_poly_map, lindices, totface);
1220
1221 /* NOTE: quad detection issue - fourth vert-index vs fourth loop-index:
1222 * ...However, most #TFace code uses `MFace->v4 == 0` test to check whether it is a tri or quad.
1223 * BKE_mesh_mface_index_validate() will check this and rotate the tessellated face if needed.
1224 */
1225#ifdef USE_TESSFACE_QUADS
1226 mf = mface;
1227 for (mface_index = 0; mface_index < totface; mface_index++, mf++) {
1228 if (mf->edcode == TESSFACE_IS_QUAD) {
1229 BKE_mesh_mface_index_validate(mf, fdata_legacy, mface_index, 4);
1230 mf->edcode = 0;
1231 }
1232 }
1233#endif
1234
1235 MEM_freeN(lindices);
1236
1237 return totface;
1238
1239#undef USE_TESSFACE_SPEEDUP
1240#undef USE_TESSFACE_QUADS
1241
1242#undef ML_TO_MF
1243#undef ML_TO_MF_QUAD
1244}
1245
1247{
1248 mesh->totface_legacy = mesh_tessface_calc(
1249 *mesh,
1250 &mesh->fdata_legacy,
1251 &mesh->corner_data,
1252 &mesh->face_data,
1253 reinterpret_cast<float(*)[3]>(mesh->vert_positions_for_write().data()),
1254 mesh->totface_legacy,
1255 mesh->corners_num,
1256 mesh->faces_num);
1257
1259}
1260
1262{
1263 if (mesh->faces_num && mesh->totface_legacy == 0) {
1265 }
1266}
1267
1270/* -------------------------------------------------------------------- */
1275{
1276 using namespace blender;
1277 using namespace blender::bke;
1278 MutableAttributeAccessor attributes = mesh->attributes_for_write();
1279 if (attributes.contains("sharp_face") || !CustomData_get_layer(&mesh->face_data, CD_MPOLY)) {
1280 return;
1281 }
1282 const Span<MPoly> polys(
1283 static_cast<const MPoly *>(CustomData_get_layer(&mesh->face_data, CD_MPOLY)),
1284 mesh->faces_num);
1285 if (std::any_of(polys.begin(), polys.end(), [](const MPoly &poly) {
1286 return !(poly.flag_legacy & ME_SMOOTH);
1287 }))
1288 {
1289 SpanAttributeWriter<bool> sharp_faces = attributes.lookup_or_add_for_write_only_span<bool>(
1290 "sharp_face", AttrDomain::Face);
1291 threading::parallel_for(polys.index_range(), 4096, [&](const IndexRange range) {
1292 for (const int i : range) {
1293 sharp_faces.span[i] = !(polys[i].flag_legacy & ME_SMOOTH);
1294 }
1295 });
1296 sharp_faces.finish();
1297 }
1298 else {
1299 attributes.remove("sharp_face");
1300 }
1301}
1302
1305/* -------------------------------------------------------------------- */
1310{
1311 using namespace blender;
1312 if (mesh->attributes().contains(".sculpt_face_set")) {
1313 return;
1314 }
1315 void *faceset_data = nullptr;
1316 const ImplicitSharingInfo *faceset_sharing_info = nullptr;
1317 for (const int i : IndexRange(mesh->face_data.totlayer)) {
1318 CustomDataLayer &layer = mesh->face_data.layers[i];
1319 if (layer.type == CD_SCULPT_FACE_SETS) {
1320 faceset_data = layer.data;
1321 faceset_sharing_info = layer.sharing_info;
1322 layer.data = nullptr;
1323 layer.sharing_info = nullptr;
1324 CustomData_free_layer(&mesh->face_data, CD_SCULPT_FACE_SETS, mesh->faces_num, i);
1325 break;
1326 }
1327 }
1328 if (faceset_data != nullptr) {
1329 CustomData_add_layer_named_with_data(&mesh->face_data,
1331 faceset_data,
1332 mesh->faces_num,
1333 ".sculpt_face_set",
1334 faceset_sharing_info);
1335 }
1336 if (faceset_sharing_info != nullptr) {
1337 faceset_sharing_info->remove_user_and_delete_if_last();
1338 }
1339}
1340
1343/* -------------------------------------------------------------------- */
1348{
1349 using namespace blender;
1350 if (mesh->attributes().contains("face_maps")) {
1351 return;
1352 }
1353 int *data = nullptr;
1354 const ImplicitSharingInfo *sharing_info = nullptr;
1355 for (const int i : IndexRange(mesh->face_data.totlayer)) {
1356 CustomDataLayer &layer = mesh->face_data.layers[i];
1357 if (layer.type == CD_FACEMAP) {
1358 data = static_cast<int *>(layer.data);
1359 sharing_info = layer.sharing_info;
1360 layer.data = nullptr;
1361 layer.sharing_info = nullptr;
1362 CustomData_free_layer(&mesh->face_data, CD_FACEMAP, mesh->faces_num, i);
1363 break;
1364 }
1365 }
1366 if (!data) {
1367 return;
1368 }
1369
1371 &mesh->face_data, CD_PROP_INT32, data, mesh->faces_num, "face_maps", sharing_info);
1372 if (sharing_info != nullptr) {
1373 sharing_info->remove_user_and_delete_if_last();
1374 }
1375
1377 for (const int i : IndexRange(mesh->faces_num)) {
1378 if (data[i] == -1) {
1379 /* -1 values "didn't have" a face map. */
1380 continue;
1381 }
1382 groups.add(data[i], i);
1383 }
1384
1385 bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
1386 for (const auto item : groups.items()) {
1387 bke::SpanAttributeWriter<bool> attribute = attributes.lookup_or_add_for_write_span<bool>(
1388 ".temp_face_map_" + std::to_string(item.key), bke::AttrDomain::Face);
1389 if (attribute) {
1390 attribute.span.fill_indices(item.value.as_span(), true);
1391 attribute.finish();
1392 }
1393 }
1394}
1395
1397{
1398 LISTBASE_FOREACH (Mesh *, mesh, &bmain->meshes) {
1400 }
1401
1402 LISTBASE_FOREACH (Object *, object, &bmain->objects) {
1403 if (object->type != OB_MESH) {
1404 continue;
1405 }
1406 Mesh *mesh = static_cast<Mesh *>(object->data);
1407 int i;
1408 LISTBASE_FOREACH_INDEX (bFaceMap *, face_map, &object->fmaps, i) {
1409 mesh->attributes_for_write().rename(".temp_face_map_" + std::to_string(i), face_map->name);
1410 }
1411 BLI_freelistN(&object->fmaps);
1412 }
1413}
1414
1417/* -------------------------------------------------------------------- */
1422{
1423 using namespace blender;
1424 if (mesh->mvert && !CustomData_has_layer(&mesh->vert_data, CD_BWEIGHT)) {
1425 const Span<MVert> verts(mesh->mvert, mesh->verts_num);
1426 if (mesh->cd_flag & ME_CDFLAG_VERT_BWEIGHT) {
1427 float *weights = static_cast<float *>(
1428 CustomData_add_layer(&mesh->vert_data, CD_BWEIGHT, CD_CONSTRUCT, verts.size()));
1429 for (const int i : verts.index_range()) {
1430 weights[i] = verts[i].bweight_legacy / 255.0f;
1431 }
1432 }
1433 }
1434
1435 if (mesh->medge && !CustomData_has_layer(&mesh->edge_data, CD_BWEIGHT)) {
1436 const Span<MEdge> edges(mesh->medge, mesh->edges_num);
1437 if (mesh->cd_flag & ME_CDFLAG_EDGE_BWEIGHT) {
1438 float *weights = static_cast<float *>(
1439 CustomData_add_layer(&mesh->edge_data, CD_BWEIGHT, CD_CONSTRUCT, edges.size()));
1440 for (const int i : edges.index_range()) {
1441 weights[i] = edges[i].bweight_legacy / 255.0f;
1442 }
1443 }
1444 }
1445}
1446
1448 const eCustomDataType old_type,
1449 const eCustomDataType new_type,
1450 const int elems_num,
1451 const char *new_name)
1452{
1453 using namespace blender;
1454 void *data = nullptr;
1455 const ImplicitSharingInfo *sharing_info = nullptr;
1456 for (const int i : IndexRange(custom_data.totlayer)) {
1457 CustomDataLayer &layer = custom_data.layers[i];
1458 if (layer.type == old_type) {
1459 data = layer.data;
1460 sharing_info = layer.sharing_info;
1461 layer.data = nullptr;
1462 layer.sharing_info = nullptr;
1463 CustomData_free_layer(&custom_data, old_type, elems_num, i);
1464 break;
1465 }
1466 }
1467 if (data != nullptr) {
1469 &custom_data, new_type, data, elems_num, new_name, sharing_info);
1470 }
1471 if (sharing_info != nullptr) {
1472 sharing_info->remove_user_and_delete_if_last();
1473 }
1474}
1475
1477{
1478 if (!mesh->attributes().contains("bevel_weight_vert")) {
1480 mesh->vert_data, CD_BWEIGHT, CD_PROP_FLOAT, mesh->verts_num, "bevel_weight_vert");
1481 }
1482 if (!mesh->attributes().contains("bevel_weight_edge")) {
1484 mesh->edge_data, CD_BWEIGHT, CD_PROP_FLOAT, mesh->edges_num, "bevel_weight_edge");
1485 }
1486}
1487
1490/* -------------------------------------------------------------------- */
1495{
1496 using namespace blender;
1497 if (!mesh->medge) {
1498 return;
1499 }
1500 if (CustomData_has_layer(&mesh->edge_data, CD_CREASE)) {
1501 return;
1502 }
1503 const Span<MEdge> edges(mesh->medge, mesh->edges_num);
1504 if (mesh->cd_flag & ME_CDFLAG_EDGE_CREASE) {
1505 float *creases = static_cast<float *>(
1506 CustomData_add_layer(&mesh->edge_data, CD_CREASE, CD_CONSTRUCT, edges.size()));
1507 for (const int i : edges.index_range()) {
1508 creases[i] = edges[i].crease_legacy / 255.0f;
1509 }
1510 }
1511}
1512
1514{
1515 if (!mesh->attributes().contains("crease_vert")) {
1517 mesh->vert_data, CD_CREASE, CD_PROP_FLOAT, mesh->verts_num, "crease_vert");
1518 }
1519 if (!mesh->attributes().contains("crease_edge")) {
1521 mesh->edge_data, CD_CREASE, CD_PROP_FLOAT, mesh->edges_num, "crease_edge");
1522 }
1523}
1524
1527/* -------------------------------------------------------------------- */
1532{
1533 using namespace blender;
1534 using namespace blender::bke;
1535 if (!mesh->medge) {
1536 return;
1537 }
1538 const Span<MEdge> edges(mesh->medge, mesh->edges_num);
1539 MutableAttributeAccessor attributes = mesh->attributes_for_write();
1540 if (attributes.contains("sharp_edge")) {
1541 return;
1542 }
1543 if (std::any_of(edges.begin(), edges.end(), [](const MEdge &edge) {
1544 return edge.flag_legacy & ME_SHARP;
1545 }))
1546 {
1547 SpanAttributeWriter<bool> sharp_edges = attributes.lookup_or_add_for_write_only_span<bool>(
1548 "sharp_edge", AttrDomain::Edge);
1549 threading::parallel_for(edges.index_range(), 4096, [&](const IndexRange range) {
1550 for (const int i : range) {
1551 sharp_edges.span[i] = edges[i].flag_legacy & ME_SHARP;
1552 }
1553 });
1554 sharp_edges.finish();
1555 }
1556}
1557
1560/* -------------------------------------------------------------------- */
1565{
1566 using namespace blender;
1567 using namespace blender::bke;
1568 if (!mesh->medge) {
1569 return;
1570 }
1571 MutableSpan<MEdge> edges(mesh->medge, mesh->edges_num);
1572 MutableAttributeAccessor attributes = mesh->attributes_for_write();
1573 if (attributes.contains(".uv_seam")) {
1574 return;
1575 }
1576 if (std::any_of(edges.begin(), edges.end(), [](const MEdge &edge) {
1577 return edge.flag_legacy & ME_SEAM;
1578 }))
1579 {
1580 SpanAttributeWriter<bool> uv_seams = attributes.lookup_or_add_for_write_only_span<bool>(
1581 ".uv_seam", AttrDomain::Edge);
1582 threading::parallel_for(edges.index_range(), 4096, [&](const IndexRange range) {
1583 for (const int i : range) {
1584 uv_seams.span[i] = edges[i].flag_legacy & ME_SEAM;
1585 }
1586 });
1587 uv_seams.finish();
1588 }
1589}
1590
1593/* -------------------------------------------------------------------- */
1598{
1599 using namespace blender;
1600 using namespace blender::bke;
1601 MutableAttributeAccessor attributes = mesh->attributes_for_write();
1602 if (!mesh->mvert || attributes.contains(".hide_vert") || attributes.contains(".hide_edge") ||
1603 attributes.contains(".hide_poly"))
1604 {
1605 return;
1606 }
1607 const Span<MVert> verts(mesh->mvert, mesh->verts_num);
1608 if (std::any_of(verts.begin(), verts.end(), [](const MVert &vert) {
1609 return vert.flag_legacy & ME_HIDE;
1610 }))
1611 {
1612 SpanAttributeWriter<bool> hide_vert = attributes.lookup_or_add_for_write_only_span<bool>(
1613 ".hide_vert", AttrDomain::Point);
1614 threading::parallel_for(verts.index_range(), 4096, [&](IndexRange range) {
1615 for (const int i : range) {
1616 hide_vert.span[i] = verts[i].flag_legacy & ME_HIDE;
1617 }
1618 });
1619 hide_vert.finish();
1620 }
1621
1622 if (mesh->medge) {
1623 const Span<MEdge> edges(mesh->medge, mesh->edges_num);
1624 if (std::any_of(edges.begin(), edges.end(), [](const MEdge &edge) {
1625 return edge.flag_legacy & ME_HIDE;
1626 }))
1627 {
1628 SpanAttributeWriter<bool> hide_edge = attributes.lookup_or_add_for_write_only_span<bool>(
1629 ".hide_edge", AttrDomain::Edge);
1630 threading::parallel_for(edges.index_range(), 4096, [&](IndexRange range) {
1631 for (const int i : range) {
1632 hide_edge.span[i] = edges[i].flag_legacy & ME_HIDE;
1633 }
1634 });
1635 hide_edge.finish();
1636 }
1637 }
1638
1639 const Span<MPoly> polys(
1640 static_cast<const MPoly *>(CustomData_get_layer(&mesh->face_data, CD_MPOLY)),
1641 mesh->faces_num);
1642 if (std::any_of(polys.begin(), polys.end(), [](const MPoly &poly) {
1643 return poly.flag_legacy & ME_HIDE;
1644 }))
1645 {
1646 SpanAttributeWriter<bool> hide_poly = attributes.lookup_or_add_for_write_only_span<bool>(
1647 ".hide_poly", AttrDomain::Face);
1648 threading::parallel_for(polys.index_range(), 4096, [&](IndexRange range) {
1649 for (const int i : range) {
1650 hide_poly.span[i] = polys[i].flag_legacy & ME_HIDE;
1651 }
1652 });
1653 hide_poly.finish();
1654 }
1655}
1656
1659/* -------------------------------------------------------------------- */
1664{
1665 using namespace blender;
1666 using namespace blender::bke;
1667 MutableAttributeAccessor attributes = mesh->attributes_for_write();
1668 if (!CustomData_has_layer(&mesh->face_data, CD_MPOLY) || attributes.contains("material_index")) {
1669 return;
1670 }
1671 const Span<MPoly> polys(
1672 static_cast<const MPoly *>(CustomData_get_layer(&mesh->face_data, CD_MPOLY)),
1673 mesh->faces_num);
1674 if (std::any_of(
1675 polys.begin(), polys.end(), [](const MPoly &poly) { return poly.mat_nr_legacy != 0; }))
1676 {
1677 SpanAttributeWriter<int> material_indices = attributes.lookup_or_add_for_write_only_span<int>(
1678 "material_index", AttrDomain::Face);
1679 threading::parallel_for(polys.index_range(), 4096, [&](IndexRange range) {
1680 for (const int i : range) {
1681 material_indices.span[i] = polys[i].mat_nr_legacy;
1682 }
1683 });
1684 material_indices.finish();
1685 }
1686}
1687
1690/* -------------------------------------------------------------------- */
1695{
1696 using namespace blender;
1697 using namespace blender::bke;
1698 if (!CustomData_has_layer(&mesh->corner_data, CD_MLOOPUV)) {
1699 return;
1700 }
1701
1702 /* Store layer names since they will be removed, used to set the active status of new layers.
1703 * Use intermediate #StringRef because the names can be null. */
1704
1705 Array<std::string> uv_names(CustomData_number_of_layers(&mesh->corner_data, CD_MLOOPUV));
1706 for (const int i : uv_names.index_range()) {
1707 uv_names[i] = CustomData_get_layer_name(&mesh->corner_data, CD_MLOOPUV, i);
1708 }
1709 const int active_name_i = uv_names.as_span().first_index_try(
1710 StringRef(CustomData_get_active_layer_name(&mesh->corner_data, CD_MLOOPUV)));
1711 const int default_name_i = uv_names.as_span().first_index_try(
1712 StringRef(CustomData_get_render_layer_name(&mesh->corner_data, CD_MLOOPUV)));
1713
1714 for (const int i : uv_names.index_range()) {
1715 const MLoopUV *mloopuv = static_cast<const MLoopUV *>(
1716 CustomData_get_layer_named(&mesh->corner_data, CD_MLOOPUV, uv_names[i]));
1717 const uint32_t needed_boolean_attributes = threading::parallel_reduce(
1718 IndexRange(mesh->corners_num),
1719 4096,
1720 0,
1721 [&](const IndexRange range, uint32_t init) {
1722 for (const int i : range) {
1723 init |= mloopuv[i].flag;
1724 }
1725 return init;
1726 },
1727 [](const uint32_t a, const uint32_t b) { return a | b; });
1728
1729 float2 *coords = static_cast<float2 *>(
1730 MEM_malloc_arrayN(mesh->corners_num, sizeof(float2), __func__));
1731 bool *vert_selection = nullptr;
1732 bool *edge_selection = nullptr;
1733 bool *pin = nullptr;
1734 if (needed_boolean_attributes & MLOOPUV_VERTSEL) {
1735 vert_selection = static_cast<bool *>(
1736 MEM_malloc_arrayN(mesh->corners_num, sizeof(bool), __func__));
1737 }
1738 if (needed_boolean_attributes & MLOOPUV_EDGESEL) {
1739 edge_selection = static_cast<bool *>(
1740 MEM_malloc_arrayN(mesh->corners_num, sizeof(bool), __func__));
1741 }
1742 if (needed_boolean_attributes & MLOOPUV_PINNED) {
1743 pin = static_cast<bool *>(MEM_malloc_arrayN(mesh->corners_num, sizeof(bool), __func__));
1744 }
1745
1746 threading::parallel_for(IndexRange(mesh->corners_num), 4096, [&](IndexRange range) {
1747 for (const int i : range) {
1748 coords[i] = mloopuv[i].uv;
1749 }
1750 if (vert_selection) {
1751 for (const int i : range) {
1752 vert_selection[i] = mloopuv[i].flag & MLOOPUV_VERTSEL;
1753 }
1754 }
1755 if (edge_selection) {
1756 for (const int i : range) {
1757 edge_selection[i] = mloopuv[i].flag & MLOOPUV_EDGESEL;
1758 }
1759 }
1760 if (pin) {
1761 for (const int i : range) {
1762 pin[i] = mloopuv[i].flag & MLOOPUV_PINNED;
1763 }
1764 }
1765 });
1766
1767 CustomData_free_layer_named(&mesh->corner_data, uv_names[i], mesh->corners_num);
1768
1769 AttributeOwner owner = AttributeOwner::from_id(&mesh->id);
1770 const std::string new_name = BKE_attribute_calc_unique_name(owner, uv_names[i].c_str());
1771 uv_names[i] = new_name;
1772
1774 &mesh->corner_data, CD_PROP_FLOAT2, coords, mesh->corners_num, new_name, nullptr);
1775 char buffer[MAX_CUSTOMDATA_LAYER_NAME];
1776 if (vert_selection) {
1778 &mesh->corner_data,
1780 vert_selection,
1781 mesh->corners_num,
1782 BKE_uv_map_vert_select_name_get(new_name.c_str(), buffer),
1783 nullptr);
1784 }
1785 if (edge_selection) {
1787 &mesh->corner_data,
1789 edge_selection,
1790 mesh->corners_num,
1791 BKE_uv_map_edge_select_name_get(new_name.c_str(), buffer),
1792 nullptr);
1793 }
1794 if (pin) {
1795 CustomData_add_layer_named_with_data(&mesh->corner_data,
1797 pin,
1798 mesh->corners_num,
1799 BKE_uv_map_pin_name_get(new_name.c_str(), buffer),
1800 nullptr);
1801 }
1802 }
1803
1804 if (active_name_i != -1) {
1805 CustomData_set_layer_active_index(&mesh->corner_data,
1807 CustomData_get_named_layer_index(&mesh->corner_data,
1809 uv_names[active_name_i]));
1810 }
1811 if (default_name_i != -1) {
1812 CustomData_set_layer_render_index(&mesh->corner_data,
1814 CustomData_get_named_layer_index(&mesh->corner_data,
1816 uv_names[default_name_i]));
1817 }
1818}
1819
1826{
1827 using namespace blender;
1828 using namespace blender::bke;
1829 MutableAttributeAccessor attributes = mesh->attributes_for_write();
1830 if (!mesh->mvert || attributes.contains(".select_vert") || attributes.contains(".select_edge") ||
1831 attributes.contains(".select_poly"))
1832 {
1833 return;
1834 }
1835
1836 const Span<MVert> verts(mesh->mvert, mesh->verts_num);
1837 if (std::any_of(
1838 verts.begin(), verts.end(), [](const MVert &vert) { return vert.flag_legacy & SELECT; }))
1839 {
1840 SpanAttributeWriter<bool> select_vert = attributes.lookup_or_add_for_write_only_span<bool>(
1841 ".select_vert", AttrDomain::Point);
1842 threading::parallel_for(verts.index_range(), 4096, [&](IndexRange range) {
1843 for (const int i : range) {
1844 select_vert.span[i] = verts[i].flag_legacy & SELECT;
1845 }
1846 });
1847 select_vert.finish();
1848 }
1849
1850 if (mesh->medge) {
1851 const Span<MEdge> edges(mesh->medge, mesh->edges_num);
1852 if (std::any_of(edges.begin(), edges.end(), [](const MEdge &edge) {
1853 return edge.flag_legacy & SELECT;
1854 }))
1855 {
1856 SpanAttributeWriter<bool> select_edge = attributes.lookup_or_add_for_write_only_span<bool>(
1857 ".select_edge", AttrDomain::Edge);
1858 threading::parallel_for(edges.index_range(), 4096, [&](IndexRange range) {
1859 for (const int i : range) {
1860 select_edge.span[i] = edges[i].flag_legacy & SELECT;
1861 }
1862 });
1863 select_edge.finish();
1864 }
1865 }
1866
1867 const Span<MPoly> polys(
1868 static_cast<const MPoly *>(CustomData_get_layer(&mesh->face_data, CD_MPOLY)),
1869 mesh->faces_num);
1870 if (std::any_of(polys.begin(), polys.end(), [](const MPoly &poly) {
1871 return poly.flag_legacy & ME_FACE_SEL;
1872 }))
1873 {
1874 SpanAttributeWriter<bool> select_poly = attributes.lookup_or_add_for_write_only_span<bool>(
1875 ".select_poly", AttrDomain::Face);
1876 threading::parallel_for(polys.index_range(), 4096, [&](IndexRange range) {
1877 for (const int i : range) {
1878 select_poly.span[i] = polys[i].flag_legacy & ME_FACE_SEL;
1879 }
1880 });
1881 select_poly.finish();
1882 }
1883}
1884
1887/* -------------------------------------------------------------------- */
1892{
1893 using namespace blender;
1894 using namespace blender::bke;
1895 const MVert *mvert = static_cast<const MVert *>(
1896 CustomData_get_layer(&mesh->vert_data, CD_MVERT));
1897 if (!mvert || CustomData_has_layer_named(&mesh->vert_data, CD_PROP_FLOAT3, "position")) {
1898 return;
1899 }
1900
1901 const Span<MVert> verts(mvert, mesh->verts_num);
1902 MutableSpan<float3> positions(
1903 static_cast<float3 *>(CustomData_add_layer_named(
1904 &mesh->vert_data, CD_PROP_FLOAT3, CD_CONSTRUCT, mesh->verts_num, "position")),
1905 mesh->verts_num);
1906 threading::parallel_for(verts.index_range(), 2048, [&](IndexRange range) {
1907 for (const int i : range) {
1908 positions[i] = verts[i].co_legacy;
1909 }
1910 });
1911
1912 CustomData_free_layers(&mesh->vert_data, CD_MVERT, mesh->verts_num);
1913 mesh->mvert = nullptr;
1914}
1915
1918/* -------------------------------------------------------------------- */
1923{
1924 using namespace blender;
1925 using namespace blender::bke;
1926 const MEdge *medge = static_cast<const MEdge *>(
1927 CustomData_get_layer(&mesh->edge_data, CD_MEDGE));
1928 if (!medge || CustomData_has_layer_named(&mesh->edge_data, CD_PROP_INT32_2D, ".edge_verts")) {
1929 return;
1930 }
1931
1932 const Span<MEdge> legacy_edges(medge, mesh->edges_num);
1933 MutableSpan<int2> edges(
1934 static_cast<int2 *>(CustomData_add_layer_named(
1935 &mesh->edge_data, CD_PROP_INT32_2D, CD_CONSTRUCT, mesh->edges_num, ".edge_verts")),
1936 mesh->edges_num);
1937 threading::parallel_for(legacy_edges.index_range(), 2048, [&](IndexRange range) {
1938 for (const int i : range) {
1939 edges[i] = int2(legacy_edges[i].v1, legacy_edges[i].v2);
1940 }
1941 });
1942
1943 CustomData_free_layers(&mesh->edge_data, CD_MEDGE, mesh->edges_num);
1944 mesh->medge = nullptr;
1945}
1946
1949/* -------------------------------------------------------------------- */
1954{
1955 using namespace blender;
1956 /* It's not clear whether the active/render status was stored in the dedicated flags or in the
1957 * generic CustomData layer indices, so convert from both, preferring the explicit flags. */
1958
1959 auto active_from_flags = [&](const CustomData &data) {
1960 if (!mesh->active_color_attribute) {
1961 for (const int i : IndexRange(data.totlayer)) {
1962 if (data.layers[i].flag & CD_FLAG_COLOR_ACTIVE) {
1963 mesh->active_color_attribute = BLI_strdup(data.layers[i].name);
1964 }
1965 }
1966 }
1967 };
1968 auto active_from_indices = [&](const CustomData &data) {
1969 if (!mesh->active_color_attribute) {
1971 if (i != -1) {
1972 mesh->active_color_attribute = BLI_strdup(data.layers[i].name);
1973 }
1974 }
1975 if (!mesh->active_color_attribute) {
1977 if (i != -1) {
1978 mesh->active_color_attribute = BLI_strdup(data.layers[i].name);
1979 }
1980 }
1981 };
1982 auto default_from_flags = [&](const CustomData &data) {
1983 if (!mesh->default_color_attribute) {
1984 for (const int i : IndexRange(data.totlayer)) {
1985 if (data.layers[i].flag & CD_FLAG_COLOR_RENDER) {
1986 mesh->default_color_attribute = BLI_strdup(data.layers[i].name);
1987 }
1988 }
1989 }
1990 };
1991 auto default_from_indices = [&](const CustomData &data) {
1992 if (!mesh->default_color_attribute) {
1994 if (i != -1) {
1995 mesh->default_color_attribute = BLI_strdup(data.layers[i].name);
1996 }
1997 }
1998 if (!mesh->default_color_attribute) {
2000 if (i != -1) {
2001 mesh->default_color_attribute = BLI_strdup(data.layers[i].name);
2002 }
2003 }
2004 };
2005
2006 active_from_flags(mesh->vert_data);
2007 active_from_flags(mesh->corner_data);
2008 active_from_indices(mesh->vert_data);
2009 active_from_indices(mesh->corner_data);
2010
2011 default_from_flags(mesh->vert_data);
2012 default_from_flags(mesh->corner_data);
2013 default_from_indices(mesh->vert_data);
2014 default_from_indices(mesh->corner_data);
2015}
2016
2019/* -------------------------------------------------------------------- */
2024{
2025 using namespace blender;
2026 if (CustomData_has_layer_named(&mesh->corner_data, CD_PROP_INT32, ".corner_vert") &&
2027 CustomData_has_layer_named(&mesh->corner_data, CD_PROP_INT32, ".corner_edge"))
2028 {
2029 return;
2030 }
2031 const Span<MLoop> loops(
2032 static_cast<const MLoop *>(CustomData_get_layer(&mesh->corner_data, CD_MLOOP)),
2033 mesh->corners_num);
2034 MutableSpan<int> corner_verts(
2035 static_cast<int *>(CustomData_add_layer_named(
2036 &mesh->corner_data, CD_PROP_INT32, CD_CONSTRUCT, mesh->corners_num, ".corner_vert")),
2037 mesh->corners_num);
2038 MutableSpan<int> corner_edges(
2039 static_cast<int *>(CustomData_add_layer_named(
2040 &mesh->corner_data, CD_PROP_INT32, CD_CONSTRUCT, mesh->corners_num, ".corner_edge")),
2041 mesh->corners_num);
2042 threading::parallel_for(loops.index_range(), 2048, [&](IndexRange range) {
2043 for (const int i : range) {
2044 corner_verts[i] = loops[i].v;
2045 corner_edges[i] = loops[i].e;
2046 }
2047 });
2048
2049 CustomData_free_layers(&mesh->corner_data, CD_MLOOP, mesh->corners_num);
2050}
2051
2054/* -------------------------------------------------------------------- */
2058static bool poly_loops_orders_match(const Span<MPoly> polys)
2059{
2060 for (const int i : polys.index_range().drop_back(1)) {
2061 if (polys[i].loopstart > polys[i + 1].loopstart) {
2062 return false;
2063 }
2064 }
2065 return true;
2066}
2067
2069{
2070 using namespace blender;
2071 if (mesh->face_offset_indices) {
2072 return;
2073 }
2074 const Span<MPoly> polys(
2075 static_cast<const MPoly *>(CustomData_get_layer(&mesh->face_data, CD_MPOLY)),
2076 mesh->faces_num);
2077
2079 MutableSpan<int> offsets = mesh->face_offsets_for_write();
2080
2081 if (poly_loops_orders_match(polys)) {
2082 for (const int i : polys.index_range()) {
2083 offsets[i] = polys[i].loopstart;
2084 }
2085 }
2086 else {
2087 /* Reorder mesh polygons to match the order of their loops. */
2088 Array<int> orig_indices(polys.size());
2089 array_utils::fill_index_range<int>(orig_indices);
2090 std::stable_sort(orig_indices.begin(), orig_indices.end(), [polys](const int a, const int b) {
2091 return polys[a].loopstart < polys[b].loopstart;
2092 });
2093 CustomData old_poly_data = mesh->face_data;
2094 CustomData_reset(&mesh->face_data);
2096 &old_poly_data, &mesh->face_data, CD_MASK_MESH.pmask, CD_CONSTRUCT, mesh->faces_num);
2097
2098 int offset = 0;
2099 for (const int i : orig_indices.index_range()) {
2100 offsets[i] = offset;
2101 offset += polys[orig_indices[i]].totloop;
2102 }
2103
2104 threading::parallel_for(orig_indices.index_range(), 1024, [&](const IndexRange range) {
2105 for (const int i : range) {
2106 CustomData_copy_data(&old_poly_data, &mesh->face_data, orig_indices[i], i, 1);
2107 }
2108 });
2109
2110 CustomData_free(&old_poly_data, mesh->faces_num);
2111 }
2112
2113 CustomData_free_layers(&mesh->face_data, CD_MPOLY, mesh->faces_num);
2114}
2115
2118/* -------------------------------------------------------------------- */
2122namespace blender::bke {
2123
2124static bNodeTree *add_auto_smooth_node_tree(Main &bmain, Library *owner_library)
2125{
2127 &bmain, owner_library, DATA_("Auto Smooth"), "GeometryNodeTree");
2128 if (!group->geometry_node_asset_traits) {
2129 group->geometry_node_asset_traits = MEM_cnew<GeometryNodeAssetTraits>(__func__);
2130 }
2131 group->geometry_node_asset_traits->flag |= GEO_NODE_ASSET_MODIFIER;
2132
2133 group->tree_interface.add_socket(
2134 DATA_("Geometry"), "", "NodeSocketGeometry", NODE_INTERFACE_SOCKET_OUTPUT, nullptr);
2135 group->tree_interface.add_socket(
2136 DATA_("Geometry"), "", "NodeSocketGeometry", NODE_INTERFACE_SOCKET_INPUT, nullptr);
2137 bNodeTreeInterfaceSocket *angle_io_socket = group->tree_interface.add_socket(
2138 DATA_("Angle"), "", "NodeSocketFloat", NODE_INTERFACE_SOCKET_INPUT, nullptr);
2139 auto &angle_data = *static_cast<bNodeSocketValueFloat *>(angle_io_socket->socket_data);
2140 angle_data.min = 0.0f;
2141 angle_data.max = DEG2RADF(180.0f);
2142 angle_data.subtype = PROP_ANGLE;
2143
2144 bNode *group_output = node_add_node(nullptr, group, "NodeGroupOutput");
2145 group_output->locx = 480.0f;
2146 group_output->locy = -100.0f;
2147 bNode *group_input_angle = node_add_node(nullptr, group, "NodeGroupInput");
2148 group_input_angle->locx = -420.0f;
2149 group_input_angle->locy = -300.0f;
2150 LISTBASE_FOREACH (bNodeSocket *, socket, &group_input_angle->outputs) {
2151 if (!STREQ(socket->identifier, "Socket_2")) {
2152 socket->flag |= SOCK_HIDDEN;
2153 }
2154 }
2155 bNode *group_input_mesh = node_add_node(nullptr, group, "NodeGroupInput");
2156 group_input_mesh->locx = -60.0f;
2157 group_input_mesh->locy = -100.0f;
2158 LISTBASE_FOREACH (bNodeSocket *, socket, &group_input_mesh->outputs) {
2159 if (!STREQ(socket->identifier, "Socket_1")) {
2160 socket->flag |= SOCK_HIDDEN;
2161 }
2162 }
2163 bNode *shade_smooth_edge = node_add_node(nullptr, group, "GeometryNodeSetShadeSmooth");
2164 shade_smooth_edge->custom1 = int16_t(bke::AttrDomain::Edge);
2165 shade_smooth_edge->locx = 120.0f;
2166 shade_smooth_edge->locy = -100.0f;
2167 bNode *shade_smooth_face = node_add_node(nullptr, group, "GeometryNodeSetShadeSmooth");
2168 shade_smooth_face->custom1 = int16_t(bke::AttrDomain::Face);
2169 shade_smooth_face->locx = 300.0f;
2170 shade_smooth_face->locy = -100.0f;
2171 bNode *edge_angle = node_add_node(nullptr, group, "GeometryNodeInputMeshEdgeAngle");
2172 edge_angle->locx = -420.0f;
2173 edge_angle->locy = -220.0f;
2174 bNode *edge_smooth = node_add_node(nullptr, group, "GeometryNodeInputEdgeSmooth");
2175 edge_smooth->locx = -60.0f;
2176 edge_smooth->locy = -160.0f;
2177 bNode *face_smooth = node_add_node(nullptr, group, "GeometryNodeInputShadeSmooth");
2178 face_smooth->locx = -240.0f;
2179 face_smooth->locy = -340.0f;
2180 bNode *boolean_and = node_add_node(nullptr, group, "FunctionNodeBooleanMath");
2181 boolean_and->custom1 = NODE_BOOLEAN_MATH_AND;
2182 boolean_and->locx = -60.0f;
2183 boolean_and->locy = -220.0f;
2184 bNode *less_than_or_equal = node_add_node(nullptr, group, "FunctionNodeCompare");
2185 static_cast<NodeFunctionCompare *>(less_than_or_equal->storage)->operation =
2187 less_than_or_equal->locx = -240.0f;
2188 less_than_or_equal->locy = -180.0f;
2189
2190 node_add_link(group,
2191 edge_angle,
2192 node_find_socket(edge_angle, SOCK_OUT, "Unsigned Angle"),
2193 less_than_or_equal,
2194 node_find_socket(less_than_or_equal, SOCK_IN, "A"));
2195 node_add_link(group,
2196 shade_smooth_face,
2197 node_find_socket(shade_smooth_face, SOCK_OUT, "Geometry"),
2198 group_output,
2199 node_find_socket(group_output, SOCK_IN, "Socket_0"));
2200 node_add_link(group,
2201 group_input_angle,
2202 node_find_socket(group_input_angle, SOCK_OUT, "Socket_2"),
2203 less_than_or_equal,
2204 node_find_socket(less_than_or_equal, SOCK_IN, "B"));
2205 node_add_link(group,
2206 less_than_or_equal,
2207 node_find_socket(less_than_or_equal, SOCK_OUT, "Result"),
2208 boolean_and,
2209 node_find_socket(boolean_and, SOCK_IN, "Boolean"));
2210 node_add_link(group,
2211 face_smooth,
2212 node_find_socket(face_smooth, SOCK_OUT, "Smooth"),
2213 boolean_and,
2214 node_find_socket(boolean_and, SOCK_IN, "Boolean_001"));
2215 node_add_link(group,
2216 group_input_mesh,
2217 node_find_socket(group_input_mesh, SOCK_OUT, "Socket_1"),
2218 shade_smooth_edge,
2219 node_find_socket(shade_smooth_edge, SOCK_IN, "Geometry"));
2220 node_add_link(group,
2221 edge_smooth,
2222 node_find_socket(edge_smooth, SOCK_OUT, "Smooth"),
2223 shade_smooth_edge,
2224 node_find_socket(shade_smooth_edge, SOCK_IN, "Selection"));
2225 node_add_link(group,
2226 shade_smooth_edge,
2227 node_find_socket(shade_smooth_edge, SOCK_OUT, "Geometry"),
2228 shade_smooth_face,
2229 node_find_socket(shade_smooth_face, SOCK_IN, "Geometry"));
2230 node_add_link(group,
2231 boolean_and,
2232 node_find_socket(boolean_and, SOCK_OUT, "Boolean"),
2233 shade_smooth_edge,
2234 node_find_socket(shade_smooth_edge, SOCK_IN, "Shade Smooth"));
2235
2236 LISTBASE_FOREACH (bNode *, node, &group->nodes) {
2237 node_set_selected(node, false);
2238 }
2239
2240 BKE_ntree_update_main_tree(&bmain, group, nullptr);
2241
2242 return group;
2243}
2244
2246{
2248 for (const bNode *node : nodes) {
2249 LISTBASE_FOREACH (const bNodeSocket *, socket, &node->inputs) {
2250 result.add_new(socket);
2251 }
2252 LISTBASE_FOREACH (const bNodeSocket *, socket, &node->outputs) {
2253 result.add_new(socket);
2254 }
2255 }
2256 return result;
2257}
2258
2259/* Checks if the node group is the same as the one generated by #create_auto_smooth_modifier. */
2260static bool is_auto_smooth_node_tree(const bNodeTree &group)
2261{
2262 if (group.type != NTREE_GEOMETRY) {
2263 return false;
2264 }
2265 const Span<const bNode *> nodes = group.all_nodes();
2266 if (nodes.size() != 10) {
2267 return false;
2268 }
2269 if (!group.geometry_node_asset_traits) {
2270 return false;
2271 }
2272 if (group.geometry_node_asset_traits->flag != GEO_NODE_ASSET_MODIFIER) {
2273 return false;
2274 }
2275 const std::array<StringRef, 10> idnames({"NodeGroupOutput",
2276 "NodeGroupInput",
2277 "NodeGroupInput",
2278 "GeometryNodeSetShadeSmooth",
2279 "GeometryNodeSetShadeSmooth",
2280 "GeometryNodeInputMeshEdgeAngle",
2281 "GeometryNodeInputEdgeSmooth",
2282 "GeometryNodeInputShadeSmooth",
2283 "FunctionNodeBooleanMath",
2284 "FunctionNodeCompare"});
2285 for (const int i : nodes.index_range()) {
2286 if (nodes[i]->idname != idnames[i]) {
2287 return false;
2288 }
2289 }
2290 if (nodes[3]->custom1 != int16_t(bke::AttrDomain::Edge)) {
2291 return false;
2292 }
2293 if (static_cast<bNodeSocket *>(nodes[4]->inputs.last)
2294 ->default_value_typed<bNodeSocketValueBoolean>()
2295 ->value != 1)
2296 {
2297 return false;
2298 }
2299 if (nodes[4]->custom1 != int16_t(bke::AttrDomain::Face)) {
2300 return false;
2301 }
2302 if (nodes[8]->custom1 != NODE_BOOLEAN_MATH_AND) {
2303 return false;
2304 }
2305 if (static_cast<NodeFunctionCompare *>(nodes[9]->storage)->operation != NODE_COMPARE_LESS_EQUAL)
2306 {
2307 return false;
2308 }
2309 if (BLI_listbase_count(&group.links) != 9) {
2310 return false;
2311 }
2312
2313 const std::array<int, 9> link_from_socket_indices({16, 15, 3, 36, 19, 5, 18, 11, 22});
2314 const std::array<int, 9> link_to_socket_indices({23, 0, 24, 20, 21, 8, 9, 12, 10});
2315 const VectorSet<const bNodeSocket *> socket_indices = build_socket_indices(nodes);
2316 int i;
2317 LISTBASE_FOREACH_INDEX (const bNodeLink *, link, &group.links, i) {
2318 if (socket_indices.index_of(link->fromsock) != link_from_socket_indices[i]) {
2319 return false;
2320 }
2321 if (socket_indices.index_of(link->tosock) != link_to_socket_indices[i]) {
2322 return false;
2323 }
2324 }
2325
2326 return true;
2327}
2328
2330 Object &object,
2331 const FunctionRef<bNodeTree *(Library *owner_library)> get_node_group,
2332 const float angle)
2333{
2334 auto *md = reinterpret_cast<NodesModifierData *>(BKE_modifier_new(eModifierType_Nodes));
2335 STRNCPY(md->modifier.name, DATA_("Auto Smooth"));
2336 BKE_modifier_unique_name(&object.modifiers, &md->modifier);
2337 md->node_group = get_node_group(object.id.lib);
2338 id_us_plus(&md->node_group->id);
2339
2340 md->settings.properties = idprop::create_group("Nodes Modifier Settings").release();
2341 IDProperty *angle_prop = idprop::create("Socket_2", angle).release();
2342 auto *ui_data = reinterpret_cast<IDPropertyUIDataFloat *>(IDP_ui_data_ensure(angle_prop));
2343 ui_data->base.rna_subtype = PROP_ANGLE;
2344 ui_data->soft_min = 0.0f;
2345 ui_data->soft_max = DEG2RADF(180.0f);
2346 IDP_AddToGroup(md->settings.properties, angle_prop);
2347 IDP_AddToGroup(md->settings.properties, idprop::create("Socket_2_use_attribute", 0).release());
2348 IDP_AddToGroup(md->settings.properties, idprop::create("Socket_2_attribute_name", "").release());
2349
2350 BKE_modifiers_persistent_uid_init(object, md->modifier);
2351 return &md->modifier;
2352}
2353
2354} // namespace blender::bke
2355
2357{
2358 using namespace blender;
2359 using namespace blender::bke;
2360
2361 /* Add the node group lazily and share it among all objects in the same library. */
2362 Map<Library *, bNodeTree *> group_by_library;
2363 const auto add_node_group = [&](Library *owner_library) {
2364 if (bNodeTree **group = group_by_library.lookup_ptr(owner_library)) {
2365 /* Node tree has already been found/created for this versioning call. */
2366 return *group;
2367 }
2368 /* Try to find an existing group added by previous versioning to avoid adding duplicates. */
2369 LISTBASE_FOREACH (bNodeTree *, existing_group, &bmain.nodetrees) {
2370 if (existing_group->id.lib != owner_library) {
2371 continue;
2372 }
2373 if (is_auto_smooth_node_tree(*existing_group)) {
2374 group_by_library.add_new(owner_library, existing_group);
2375 return existing_group;
2376 }
2377 }
2378 bNodeTree *new_group = add_auto_smooth_node_tree(bmain, owner_library);
2379 /* Remove the default user. The count is tracked manually when assigning to modifiers. */
2380 id_us_min(&new_group->id);
2381 group_by_library.add_new(owner_library, new_group);
2382 return new_group;
2383 };
2384
2385 LISTBASE_FOREACH (Object *, object, &bmain.objects) {
2386 if (object->type != OB_MESH) {
2387 continue;
2388 }
2389 Mesh *mesh = static_cast<Mesh *>(object->data);
2390 const float angle = mesh->smoothresh_legacy;
2391 if (!(mesh->flag & ME_AUTOSMOOTH_LEGACY)) {
2392 continue;
2393 }
2394
2395 /* Auto-smooth disabled sharp edge tagging when the evaluated mesh had custom normals.
2396 * When the original mesh has custom normals, that's a good sign the evaluated mesh will
2397 * have custom normals as well. */
2398 bool has_custom_normals = CustomData_has_layer(&mesh->corner_data, CD_CUSTOMLOOPNORMAL);
2399 if (has_custom_normals) {
2400 continue;
2401 }
2402
2403 /* The "Weighted Normal" modifier has a "Keep Sharp" option that used to recalculate the sharp
2404 * edge tags based on the mesh's smoothing angle. To keep the same behavior, a new modifier has
2405 * to be added before that modifier when the option is on. */
2406 LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
2408 has_custom_normals = true;
2409 }
2410 if (md->type == eModifierType_Bevel) {
2411 BevelModifierData *bmd = reinterpret_cast<BevelModifierData *>(md);
2412 if (bmd->flags & MOD_BEVEL_HARDEN_NORMALS) {
2413 has_custom_normals = true;
2414 }
2415 }
2416 if (md->type == eModifierType_WeightedNormal) {
2417 WeightedNormalModifierData *nmd = reinterpret_cast<WeightedNormalModifierData *>(md);
2418 if ((nmd->flag & MOD_WEIGHTEDNORMAL_KEEP_SHARP) != 0) {
2419 ModifierData *new_md = create_auto_smooth_modifier(*object, add_node_group, angle);
2420 BLI_insertlinkbefore(&object->modifiers, object->modifiers.last, new_md);
2421 }
2422 }
2423 if (md->type == eModifierType_Nodes) {
2424 NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
2425 if (nmd->node_group && is_auto_smooth_node_tree(*nmd->node_group)) {
2426 /* This object has already been processed by versioning. If the mesh is linked from
2427 * another file its auto-smooth flag may not be cleared, so this check is necessary to
2428 * avoid adding a duplicate modifier. */
2429 has_custom_normals = true;
2430 break;
2431 }
2432 }
2433 }
2434
2435 /* Some modifiers always generate custom normals which disabled sharp edge tagging, making
2436 * adding a modifier at the end unnecessary. Conceptually this is similar to checking if the
2437 * evaluated mesh had custom normals. */
2438 if (has_custom_normals) {
2439 continue;
2440 }
2441
2442 ModifierData *last_md = static_cast<ModifierData *>(object->modifiers.last);
2443 ModifierData *new_md = create_auto_smooth_modifier(*object, add_node_group, angle);
2444 if (last_md && last_md->type == eModifierType_Subsurf && has_custom_normals &&
2445 (reinterpret_cast<SubsurfModifierData *>(last_md)->flags &
2447 {
2448 /* Add the auto smooth node group before the last subdivision surface modifier if possible.
2449 * Subdivision surface modifiers have special handling for interpolating custom normals. */
2450 BLI_insertlinkbefore(&object->modifiers, object->modifiers.last, new_md);
2451 }
2452 else {
2453 BLI_addtail(&object->modifiers, new_md);
2454 }
2455 }
2456
2457 LISTBASE_FOREACH (Mesh *, mesh, &bmain.meshes) {
2458 mesh->flag &= ~ME_AUTOSMOOTH_LEGACY;
2459 }
2460}
2461
2462namespace blender::bke {
2463
2465{
2466 bool changed = false;
2467 for (CustomDataLayer &layer : vert_layers) {
2468 if (StringRef(layer.name) == ".sculpt_mask") {
2469 layer.type = CD_PAINT_MASK;
2470 layer.name[0] = '\0';
2471 changed = true;
2472 break;
2473 }
2474 }
2475 if (!changed) {
2476 return;
2477 }
2478 /* #CustomData expects the layers to be sorted in increasing order based on type. */
2479 std::stable_sort(
2480 vert_layers.begin(),
2481 vert_layers.end(),
2482 [](const CustomDataLayer &a, const CustomDataLayer &b) { return a.type < b.type; });
2483}
2484
2486{
2487 if (mesh.attributes().contains(".sculpt_mask")) {
2488 return;
2489 }
2490 void *data = nullptr;
2491 const ImplicitSharingInfo *sharing_info = nullptr;
2492 for (const int i : IndexRange(mesh.vert_data.totlayer)) {
2493 CustomDataLayer &layer = mesh.vert_data.layers[i];
2494 if (layer.type == CD_PAINT_MASK) {
2495 data = layer.data;
2496 sharing_info = layer.sharing_info;
2497 layer.data = nullptr;
2498 layer.sharing_info = nullptr;
2499 CustomData_free_layer(&mesh.vert_data, CD_PAINT_MASK, mesh.verts_num, i);
2500 break;
2501 }
2502 }
2503 if (data != nullptr) {
2505 &mesh.vert_data, CD_PROP_FLOAT, data, mesh.verts_num, ".sculpt_mask", sharing_info);
2506 }
2507 if (sharing_info != nullptr) {
2508 sharing_info->remove_user_and_delete_if_last();
2509 }
2510}
2511
2512//
2513} // namespace blender::bke
2514
2518{
2519 const int nulegacy_faces = mesh->totface_legacy;
2521 eh.reserve(nulegacy_faces);
2522 MFace *legacy_faces = (MFace *)CustomData_get_layer_for_write(
2523 &mesh->fdata_legacy, CD_MFACE, mesh->totface_legacy);
2524
2525 MFace *mf = legacy_faces;
2526 for (int i = 0; i < nulegacy_faces; i++, mf++) {
2527 eh.add({mf->v1, mf->v2});
2528 eh.add({mf->v2, mf->v3});
2529
2530 if (mf->v4) {
2531 eh.add({mf->v3, mf->v4});
2532 eh.add({mf->v4, mf->v1});
2533 }
2534 else {
2535 eh.add({mf->v3, mf->v1});
2536 }
2537 }
2538
2539 const int numEdges = eh.size();
2540
2541 /* write new edges into a temporary CustomData */
2542 CustomData edgeData;
2543 CustomData_reset(&edgeData);
2544 CustomData_add_layer_named(&edgeData, CD_PROP_INT32_2D, CD_CONSTRUCT, numEdges, ".edge_verts");
2545 CustomData_add_layer(&edgeData, CD_ORIGINDEX, CD_SET_DEFAULT, numEdges);
2546
2548 &edgeData, CD_PROP_INT32_2D, ".edge_verts", mesh->edges_num);
2549 int *index = (int *)CustomData_get_layer_for_write(&edgeData, CD_ORIGINDEX, mesh->edges_num);
2550
2551 memset(index, ORIGINDEX_NONE, sizeof(int) * numEdges);
2552 MutableSpan(ege, numEdges).copy_from(eh.as_span().cast<blender::int2>());
2553
2554 /* free old CustomData and assign new one */
2555 CustomData_free(&mesh->edge_data, mesh->edges_num);
2556 mesh->edge_data = edgeData;
2557 mesh->edges_num = numEdges;
2558}
std::string BKE_attribute_calc_unique_name(const AttributeOwner &owner, const blender::StringRef name)
Definition attribute.cc:359
const char * BKE_uv_map_pin_name_get(const char *uv_map_name, char *buffer)
const char * BKE_uv_map_vert_select_name_get(const char *uv_map_name, char *buffer)
const char * BKE_uv_map_edge_select_name_get(const char *uv_map_name, char *buffer)
CustomData interface, see also DNA_customdata_types.h.
void * CustomData_get_n_for_write(CustomData *data, eCustomDataType type, int index, int n, int totelem)
void * CustomData_get_layer_named_for_write(CustomData *data, eCustomDataType type, blender::StringRef name, int totelem)
int CustomData_get_clone_layer(const CustomData *data, eCustomDataType type)
const void * CustomData_get_layer_n(const CustomData *data, eCustomDataType type, int n)
int CustomData_get_named_layer(const CustomData *data, eCustomDataType type, blender::StringRef name)
bool CustomData_free_layer_named(CustomData *data, blender::StringRef name, const int totelem)
void CustomData_set_layer_render_index(CustomData *data, eCustomDataType type, int n)
const void * CustomData_get_layer(const CustomData *data, eCustomDataType type)
@ CD_SET_DEFAULT
@ CD_CONSTRUCT
void CustomData_external_read(CustomData *data, ID *id, eCustomDataMask mask, int totelem)
const void * CustomData_get_layer_named(const CustomData *data, eCustomDataType type, blender::StringRef name)
void * CustomData_add_layer_named(CustomData *data, eCustomDataType type, eCDAllocType alloctype, int totelem, blender::StringRef name)
void CustomData_set_layer_render(CustomData *data, eCustomDataType type, int n)
int CustomData_get_stencil_layer(const CustomData *data, eCustomDataType type)
void CustomData_reset(CustomData *data)
int CustomData_get_named_layer_index(const CustomData *data, eCustomDataType type, blender::StringRef name)
bool CustomData_has_layer_named(const CustomData *data, eCustomDataType type, blender::StringRef name)
const char * CustomData_get_layer_name(const CustomData *data, eCustomDataType type, int n)
bool CustomData_free_layer(CustomData *data, eCustomDataType type, int totelem, int index)
void CustomData_free(CustomData *data, int totelem)
void CustomData_set_layer_clone(CustomData *data, eCustomDataType type, int n)
void CustomData_free_layers(CustomData *data, eCustomDataType type, int totelem)
void CustomData_external_add(CustomData *data, ID *id, eCustomDataType type, int totelem, const char *filepath)
void CustomData_set_layer_active_index(CustomData *data, eCustomDataType type, int n)
#define ORIGINDEX_NONE
const char * CustomData_get_render_layer_name(const CustomData *data, eCustomDataType type)
void CustomData_init_layout_from(const CustomData *source, CustomData *dest, eCustomDataMask mask, eCDAllocType alloctype, int totelem)
int CustomData_get_active_layer(const CustomData *data, eCustomDataType type)
void * CustomData_get_layer_for_write(CustomData *data, eCustomDataType type, int totelem)
void CustomData_free_elem(CustomData *data, int index, int count)
const void * CustomData_add_layer_with_data(CustomData *data, eCustomDataType type, void *layer_data, int totelem, const blender::ImplicitSharingInfo *sharing_info)
void CustomData_copy_data(const CustomData *source, CustomData *dest, int source_index, int dest_index, int count)
bool CustomData_has_layer(const CustomData *data, eCustomDataType type)
void * CustomData_add_layer(CustomData *data, eCustomDataType type, eCDAllocType alloctype, int totelem)
int CustomData_get_active_layer_index(const CustomData *data, eCustomDataType type)
const char * CustomData_get_active_layer_name(const CustomData *data, eCustomDataType type)
int CustomData_get_render_layer(const CustomData *data, eCustomDataType type)
const void * CustomData_add_layer_named_with_data(CustomData *data, eCustomDataType type, void *layer_data, int totelem, blender::StringRef name, const blender::ImplicitSharingInfo *sharing_info)
int CustomData_number_of_layers(const CustomData *data, eCustomDataType type)
bool CustomData_external_test(CustomData *data, eCustomDataType type)
int CustomData_get_render_layer_index(const CustomData *data, eCustomDataType type)
void CustomData_set_layer_active(CustomData *data, eCustomDataType type, int n)
void * CustomData_get_layer_n_for_write(CustomData *data, eCustomDataType type, int n, int totelem)
const CustomData_MeshMasks CD_MASK_MESH
void * CustomData_get_for_write(CustomData *data, int index, eCustomDataType type, int totelem)
void CustomData_set_layer_stencil(CustomData *data, eCustomDataType type, int n)
void CustomData_swap_corners(CustomData *data, int index, const int *corner_indices)
@ G_DEBUG
bool IDP_AddToGroup(IDProperty *group, IDProperty *prop) ATTR_NONNULL()
Definition idprop.cc:722
IDPropertyUIData * IDP_ui_data_ensure(IDProperty *prop)
Definition idprop.cc:1739
void id_us_plus(ID *id)
Definition lib_id.cc:351
void id_us_min(ID *id)
Definition lib_id.cc:359
void BKE_mesh_tessface_clear(Mesh *mesh)
void BKE_mesh_face_offsets_ensure_alloc(Mesh *mesh)
void BKE_modifiers_persistent_uid_init(const Object &object, ModifierData &md)
void BKE_modifier_unique_name(ListBase *modifiers, ModifierData *md)
ModifierData * BKE_modifier_new(int type)
int multires_mdisp_corners(const MDisps *s)
Definition multires.cc:1417
void BKE_ntree_update_main_tree(Main *bmain, bNodeTree *ntree, NodeTreeUpdateExtraParams *params)
#define BLI_assert(a)
Definition BLI_assert.h:50
#define LISTBASE_FOREACH(type, var, list)
#define LISTBASE_FOREACH_INDEX(type, var, list, index_var)
void void BLI_freelistN(struct ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:496
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:110
void BLI_insertlinkbefore(struct ListBase *listbase, void *vnextlink, void *vnewlink) ATTR_NONNULL(1)
Definition listbase.cc:370
int BLI_listbase_count(const struct ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
#define M_LN2
void axis_dominant_v3_to_m3_negate(float r_mat[3][3], const float normal[3])
MINLINE int poly_to_tri_count(int poly_count, int corner_count)
void mul_v2_m3v3(float r[2], const float M[3][3], const float a[3])
#define DEG2RADF(_deg)
MINLINE void copy_v4_v4(float r[4], const float a[4])
MINLINE void add_newell_cross_v3_v3v3(float n[3], const float v_prev[3], const float v_curr[3])
MINLINE void copy_v2_v2(float r[2], const float a[2])
MINLINE void normal_float_to_short_v3(short out[3], const float in[3])
MINLINE void normal_short_to_float_v3(float out[3], const short in[3])
MINLINE void zero_v3(float r[3])
MINLINE float normalize_v3(float n[3])
void * BLI_memarena_alloc(struct MemArena *ma, size_t size) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC ATTR_ALLOC_SIZE(2)
void BLI_memarena_free(struct MemArena *ma) ATTR_NONNULL(1)
struct MemArena * BLI_memarena_new(size_t bufsize, const char *name) ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL ATTR_NONNULL(2) ATTR_MALLOC
#define BLI_MEMARENA_STD_BUFSIZE
void void BLI_memarena_clear(MemArena *ma) ATTR_NONNULL(1)
void BLI_polyfill_calc_arena(const float(*coords)[2], unsigned int coords_num, int coords_sign, unsigned int(*r_tris)[3], struct MemArena *arena)
char * BLI_strdup(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC
Definition string.c:40
#define STRNCPY(dst, src)
Definition BLI_string.h:593
unsigned int uint
#define UNLIKELY(x)
#define ELEM(...)
#define STREQ(a, b)
#define LIKELY(x)
#define DATA_(msgid)
#define MAX_CUSTOMDATA_LAYER_NAME
@ CD_PROP_BYTE_COLOR
@ CD_PROP_FLOAT
@ CD_CUSTOMLOOPNORMAL
@ CD_TESSLOOPNORMAL
@ CD_PROP_FLOAT3
@ CD_PROP_INT32_2D
@ CD_PROP_COLOR
@ CD_PROP_INT32
@ CD_PROP_FLOAT2
@ CD_ORIGSPACE_MLOOP
#define CD_MASK_MDISPS
@ ME_AUTOSMOOTH_LEGACY
struct MCol MCol
@ MOD_BEVEL_HARDEN_NORMALS
@ eModifierType_Subsurf
@ eModifierType_NormalEdit
@ eModifierType_Nodes
@ eModifierType_Bevel
@ eModifierType_WeightedNormal
@ eSubsurfModifierFlag_UseCustomNormals
@ MOD_WEIGHTEDNORMAL_KEEP_SHARP
@ NTREE_GEOMETRY
@ GEO_NODE_ASSET_MODIFIER
@ SOCK_OUT
@ SOCK_IN
@ SOCK_HIDDEN
@ NODE_BOOLEAN_MATH_AND
@ NODE_COMPARE_LESS_EQUAL
Object is a sort of wrapper for general info.
@ OB_MESH
Read Guarded memory(de)allocation.
#define MEM_reallocN(vmemh, len)
@ PROP_ANGLE
Definition RNA_types.hh:155
ATTR_WARN_UNUSED_RESULT const BMVert * v2
void init()
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
static AttributeOwner from_id(ID *id)
Definition attribute.cc:43
Span< T > as_span() const
Definition BLI_array.hh:232
const T * end() const
Definition BLI_array.hh:314
IndexRange index_range() const
Definition BLI_array.hh:349
const T * begin() const
Definition BLI_array.hh:310
constexpr IndexRange drop_back(int64_t n) const
bool add(const Key &key, const Value &value)
Definition BLI_map.hh:271
void reserve(int64_t n)
Definition BLI_map.hh:979
void add(const Key &key, const Value &value)
constexpr T * end() const
Definition BLI_span.hh:549
constexpr T * begin() const
Definition BLI_span.hh:545
constexpr void copy_from(Span< T > values) const
Definition BLI_span.hh:726
constexpr int64_t size() const
Definition BLI_span.hh:253
constexpr const T * end() const
Definition BLI_span.hh:225
constexpr IndexRange index_range() const
Definition BLI_span.hh:402
constexpr const T * begin() const
Definition BLI_span.hh:221
void reserve(const int64_t n)
bool add(const Key &key)
void add_new(const Key &key)
Span< Key > as_span() const
int64_t size() const
local_group_size(16, 16) .push_constant(Type b
#define printf
#define logf(x)
#define sqrtf(x)
draw_view in_light_buf[] float
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
static float verts[][3]
IndexRange range
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void *(* MEM_malloc_arrayN)(size_t len, size_t size, const char *str)
Definition mallocn.cc:45
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
#define G(x, y, z)
void BKE_mesh_legacy_convert_uvs_to_generic(Mesh *mesh)
void BKE_mesh_legacy_edge_crease_to_layers(Mesh *mesh)
void BKE_mesh_legacy_face_set_to_generic(Mesh *mesh)
void BKE_mesh_legacy_sharp_faces_from_flags(Mesh *mesh)
void BKE_mesh_legacy_convert_edges_to_generic(Mesh *mesh)
void BKE_mesh_legacy_bevel_weight_to_generic(Mesh *mesh)
void BKE_mesh_legacy_face_map_to_generic(Main *bmain)
static void bm_corners_to_loops_ex(ID *id, CustomData *fdata_legacy, const int totface, CustomData *ldata, MFace *mface, int totloop, int findex, int loopstart, int numTex, int numCol)
static void update_active_fdata_layers(Mesh &mesh, CustomData *fdata_legacy, CustomData *ldata)
#define ML(v1, v2)
void BKE_mesh_strip_loose_faces(Mesh *mesh)
void BKE_mesh_convert_mfaces_to_mpolys(Mesh *mesh)
static void replace_custom_data_layer_with_named(CustomData &custom_data, const eCustomDataType old_type, const eCustomDataType new_type, const int elems_num, const char *new_name)
void BKE_mesh_tessface_calc(Mesh *mesh)
static int mesh_tessface_calc(Mesh &mesh, CustomData *fdata_legacy, CustomData *ldata, CustomData *pdata, float(*positions)[3], int totface, int totloop, int faces_num)
static int vergedgesort(const void *v1, const void *v2)
void BKE_mesh_legacy_convert_loops_to_corners(Mesh *mesh)
static void mesh_ensure_tessellation_customdata(Mesh *mesh)
static void mesh_calc_edges_mdata(const MVert *, const MFace *allface, MLoop *allloop, const MPoly *allpoly, int, int totface, int, int faces_num, MEdge **r_medge, int *r_totedge)
static bool check_matching_legacy_layer_counts(CustomData *fdata_legacy, CustomData *ldata, bool fallback)
#define LAYER_CMP(l_a, t_a, l_b, t_b)
void BKE_mesh_calc_edges_tessface(Mesh *mesh)
static bool poly_loops_orders_match(const Span< MPoly > polys)
#define MESH_MLOOPCOL_FROM_MCOL(_mloopcol, _mcol)
#define TESSFACE_IS_QUAD
#define ML_TO_MF_QUAD()
void BKE_mesh_legacy_convert_verts_to_positions(Mesh *mesh)
void BKE_mesh_legacy_convert_flags_to_hide_layers(Mesh *mesh)
void BKE_main_mesh_legacy_convert_auto_smooth(Main &bmain)
#define MESH_MLOOPCOL_TO_MCOL(_mloopcol, _mcol)
void BKE_mesh_legacy_bevel_weight_to_layers(Mesh *mesh)
void BKE_mesh_legacy_crease_to_generic(Mesh *mesh)
void BKE_mesh_calc_edges_legacy(Mesh *mesh)
static void add_mface_layers(Mesh &mesh, CustomData *fdata_legacy, CustomData *ldata, int total)
void BKE_mesh_legacy_convert_flags_to_selection_layers(Mesh *mesh)
static void CustomData_to_bmeshpoly(CustomData *fdata_legacy, CustomData *ldata, int totloop)
static void CustomData_bmesh_do_versions_update_active_layers(CustomData *fdata_legacy, CustomData *corner_data)
void BKE_mesh_legacy_convert_polys_to_offsets(Mesh *mesh)
#define ML_TO_MF(i1, i2, i3)
static void move_face_map_data_to_attributes(Mesh *mesh)
static void mesh_loops_to_tessdata(CustomData *fdata_legacy, CustomData *corner_data, MFace *mface, const int *polyindices, uint(*loopindices)[4], const int num_faces)
int BKE_mesh_mface_index_validate(MFace *mface, CustomData *fdata_legacy, int mfindex, int nr)
void BKE_mesh_legacy_uv_seam_from_flags(Mesh *mesh)
void BKE_mesh_legacy_convert_mpoly_to_material_indices(Mesh *mesh)
void BKE_mesh_do_versions_convert_mfaces_to_mpolys(Mesh *mesh)
static void to_edgesort(EdgeSort *ed, uint v1, uint v2, char is_loose, short is_draw)
void BKE_mesh_tessface_ensure(Mesh *mesh)
void BKE_mesh_legacy_sharp_edges_from_flags(Mesh *mesh)
static void convert_mfaces_to_mpolys(ID *id, CustomData *fdata_legacy, CustomData *ldata, CustomData *pdata, int totedge_i, int totface_i, int totloop_i, int faces_num_i, blender::int2 *edges, MFace *mface, int *r_totloop, int *r_faces_num)
void BKE_mesh_legacy_attribute_flags_to_strings(Mesh *mesh)
void BKE_mesh_do_versions_cd_flag_init(Mesh *mesh)
std::unique_ptr< IDProperty, IDPropertyDeleter > create(StringRefNull prop_name, int32_t value, eIDPropertyFlag flags={})
Allocate a new IDProperty of type IDP_INT, set its name and value.
std::unique_ptr< IDProperty, IDPropertyDeleter > create_group(StringRefNull prop_name, eIDPropertyFlag flags={})
Allocate a new IDProperty of type IDP_GROUP.
static bNodeTree * add_auto_smooth_node_tree(Main &bmain, Library *owner_library)
void mesh_sculpt_mask_to_generic(Mesh &mesh)
bNodeTree * node_tree_add_in_lib(Main *bmain, Library *owner_library, const char *name, const char *idname)
Definition node.cc:3231
bNodeLink * node_add_link(bNodeTree *ntree, bNode *fromnode, bNodeSocket *fromsock, bNode *tonode, bNodeSocket *tosock)
Definition node.cc:2912
static bool is_auto_smooth_node_tree(const bNodeTree &group)
bNode * node_add_node(const bContext *C, bNodeTree *ntree, const char *idname)
Definition node.cc:2617
void mesh_sculpt_mask_to_legacy(MutableSpan< CustomDataLayer > vert_layers)
bool node_set_selected(bNode *node, bool select)
Definition node.cc:3863
static VectorSet< const bNodeSocket * > build_socket_indices(const Span< const bNode * > nodes)
static ModifierData * create_auto_smooth_modifier(Object &object, const FunctionRef< bNodeTree *(Library *owner_library)> get_node_group, const float angle)
bNodeSocket * node_find_socket(bNode *node, eNodeSocketInOut in_out, StringRef identifier)
Definition node.cc:1829
#define hash
Definition noise.c:154
signed short int16_t
Definition stdint.h:76
unsigned int uint32_t
Definition stdint.h:80
CustomDataLayer * layers
CustomDataExternal * external
IDPropertyUIData base
Definition DNA_ID.h:109
Definition DNA_ID.h:413
float(* disps)[3]
unsigned int v2
unsigned int v1
unsigned int v4
unsigned int v3
float uv[4][2]
ListBase meshes
Definition BKE_main.hh:213
ListBase nodetrees
Definition BKE_main.hh:234
ListBase objects
Definition BKE_main.hh:212
struct bNodeTree * node_group
int16_t custom1
float locy
float locx
void * storage
ListBase outputs
float max
static DynamicLibrary lib